git challenge に向けて - git 備忘録
ミクシィ主催の git challenge というイベントの第5回 に参加できるようになった.
git は普段から使っているが,基本的に Atlassian の SourceTree を使ってポチポチしてるだけであり,知識は必要に応じてググったものしかない. ということで,とても良い機会なので本を一冊読んで,一から丁寧に学んでみることにした.
今回読んだのは,「Gitによるバージョン管理」という書籍. 年末に大学の理工書店に行ったときに,あったものから適当に選んで買った. なんか初級者向けでは無いらしいので,面白いかなぁと.
git コマンド備忘録
書籍を読んで得た git コマンドに関する知見のメモ
「なんかこんな感じのことしてくれるコマンドなかったっけ??」を検索する用. 自分で書いておけば同じ「なんかこう」が出るでしょう,たぶん.
git init
リポジトリを作成してくれるコマンド.
Git のリポジトリは大きく分けて2つ
.git
がサブディレクトリに格納されているリポジトリ(non-bare)- それがむき出し(bare) の bare リポジトリ
GitHub なんかを使って普段使ってるのは前者の non-bare リポジトリ. bare リポジトリはリポジトリの更新情報だけを持つため,ワーキングディレクトリを持たない. ローカルでは普通,ワーキングディレクトリが要るので non-bare だが,リモートは bare (と non-bare)をあげるべきらしい(そうしないとワーキングツリーとインデックスの差異でうまく動作しないことがるそうな).
git-init
には --bare
というオプションを与えると bare リポジトリを作成できる.
また,--shared
オプションを付けることで,リポジトリの共有をするのに適した設定をしてくれる.
git config
リポジトリの設定を行ってくれるコマンド.
リポジトリの設定は .git/config
に,ログインユーザー全体の設定は ~/.gitconfig
に書かれている(前者が優先される). 直接書き換えることもできるが,このコマンドでも書き換え可能である.
ユーザー名やメールアドレスはもちろん,メッセージ編集用のエディタやテキストビューア,git コマンドのエイリアスも設定できる. git rebase -i
などで vi を使うのが嫌な emacs 派の人々はぜひ emacs に設定しなおしておこう!
git add
最も基本的なコマンドの一つ.
ファイルをインデックスに登録するコマンド. インデックスとは,コミットするファイルのキャッシュのようなモノ. つまり,コミットするファイルを指定するコマンドのようなモノ.
ちなみに,git では空のディレクトリを git-add
できない. どうしてもしたい場合によく使われるのは,中に何も書かれていない .gitignore
ファイルを入れておくことだそうな.
更にちなみに,SD1月号 にあった,「特定のファイルだけ git-add
するシェル芸」として
$ git ls-files -m | grep 'A' | xargs git add
というのがあった. git-ls-files
が便利で,-m
オプションで「変更が加えられたままコミットされていないファイル」のみを表示してくれたり,-o
オプションで「新規ファイル」だけを表示してくれたりする.
git diff
管理されてるファイルの差分を出力するコマンド.
ブランチ,インデックス,ワーキングツリーの差分を確認できる. 単純に git diff
とした場合はインデックスとワーキングツリーの差分を出力する.
git diff
の後にファイル名やブランチ名を指定することで,ファイル単位やブランチ単位で比較できる.
-M
オプションでリネームも確認できる.
git log
コミットログを出力する.
git shortlog
であれば要約したコミットログが出力される.
git log 1c26396e..d9e8f42
のようにコミットログのハッシュ値の範囲を指定することで,出力するコミットの範囲を指定できる. 1c26396e
では8桁で指定しているが,一意に定まれば何桁まででも省略してよい.
-p
オプションで差分も表示すし,-1
など数字を指定すると最新から n 回分を出力する. 出力を整形するオプションもある.
git status
ワーキングツリー,インデックス,リポジトリの状態を確認するコマンド. -s
オプションで短縮表示する.
git mv
git で管理されているファイルやディレクトリのリネームや移動を行うコマンド. 単なるファイルシステム上のコマンドを使わずに,git mv
を使えば変更や移動をインデックスに登録してくれる.
git rm
git で管理されてるファイルの削除を行うコマンド. git mv
同様,インデックスに削除したことが登録される.
削除したファイルを元に戻すには,git checkout HEAD hoge
みたいにすると良い.
git commit
インデックスに登録された変更をローカルリポジトリにコミットするコマンド.
コミットメッセージは慣例的に
- 1行目にはコミットが何をするものなのかを簡潔に書く
- 2行目は空行
- 3行目以降はより詳しく書く
git reset
特定のコミットまでコミットを取り消すコマンド.
(よくわからないので,検索した.)
なるほど
原則,git reset
は指定したコミットに HEAD を移動している(何も指定しなければ移動しない).
で,--soft
オプションを使うと,移動前との差分をインデックスに戻してくれる(そうすると,移動前と移動前の時点でのインデックスにあげてない変更の差分をワーキングツリーに残せる). --hard
オプションはその差分を全て破棄する. といった感じかなぁ.
ちなみに,前の HEAD は .git
の **ORIG_HEAD** にあるらしい.
(このあたりいじる問題が git challenge で出たら面白そう)
git clean
リポジトリで管理されていないファイルを一掃するコマンド.
(.gitignore で無視されてるファイルは削除できない.それを削除するためには -x
オプションを付ける.)
-n
オプションで削除されるファイルを確認できる. まぁどっちみち -f
オプションを付けないと削除はできない. ディレクトリも削除したい場合は -d
オプションを付ける.
ちなみに,git config
で -f
を付けなくても削除できるようになる.
git grep
git で管理しているファイルを grep するコマンド.
ブランチをまたいで検索してくれる(はず). git grep <pattern> <branch>
で特定のブランチ(コミットも?)検索できる. --cached
オプションでインデックスからのみ検索できる.
git show
ハッシュ値を指定してコミットの内容を出力する(何も指定しなければ HEAD).
git clone
リモートリポジトリをローカルリポジトリにコピーするコマンド.
git remote
リモートリポジトリの管理や更新を行うコマンド.
origin
がどこのリモートブランチなのかとかそんなところを管理する.
git merge
ブランチとブランチをマージするコマンド.
マージのされ方は大きく分けて2つある. マージ用のコミットができる場合とそうでない場合(Fast-Forward). 枝分かれしたコミットを持つブランチ同士をマージする場合(コンフリクトの可能性がある)は必ず前者になるが,そうでない場合は Fast-Forward マージができる.
--squash
オプションを指定するとマージするコミットを1つにまとめれる.
git push
リモートリポジトリのブランチにローカルリポジトリのブランチを反映させるコマンド.
だいたい git push origin master
って書くが,本来は git push origin master:master
と書く. 左がローカルで右がリモート .
git pull
ローカルリポジトリにリモートリポジトリのブランチを反映させるコマンド.
git branch
ローカルブランチを作成・削除・確認・トラッキングするコマンド.
git branch
はローカルブランチの確認. -r
オプションはトラッキングの確認(-a
で両方).
git branch <name>
でブランチの作成. 現在チェックアウトしているブランチ(コミット)以外を起点にしたい場合は <name>
の後に指定する.
-m
オプションでリネーム,-d
オプションで削除.
トラッキングの情報は .git/cinfig
にある. トラッキングはブランチ作成時に -t
オプションで指定できる.
git blame
ファイルの特定の行が最後に編集された履歴を追跡するコマンド.
(git challenge で使えそう...)
git blame test.sh
でファイルそのものを最後に変更したコミットを出力. git blame -L 3,7 test.sh
で3行目から7行目,git blame -L 3,+4 test.sh
で3行目から4行分を探索.
-C
や -M
でファイル間でコピーした個所や,ファイル内で移動した個所を特定できるが,英数字しかチェックしないみたい(今も可は分からん).
git checkout
ブランチのチェックアウトをするコマンド. チェックアウトとは特定のブランチ(コミット)のデータをインデックスやワーキングツリーに展開する事.
ファイルまで指定すれば,特定のファイルだけを持ってくることもできる. -b
オプションでブランチの作成とチェックアウトを同時に行える.
git tag
タグをつけるコマンド.
git tag
と何も指定しなければ一覧を出力する. オプションでメッセージを付けたり(-m
),署名を付けたり(-s
),削除したり(-d
)できる.
(リモートブランチ側のタグを削除するには git push
)
最も近いタグを表示したり,コミットがどのタグに含まれてるかは git describe
で行える.
git archive
指定したコミットのスナップショットを作成するコマンド.
git fetch
リモートリポジトリのデータを更新(取得)するコマンド.
git remote update
との違いは,それがリポジトリ単位で操作するコマンドに対し,git fetch
はブランチ単位で更新できること.
git stash
未コミットの状態を一時的に退避(保存)するコマンド.
git revert
コミットした内容を打ち消す 新たな コミットをするコマンド.
git rebase
敷居が高いがスゲー便利なコマンド.
使い方はなんとなくわかったけど,どういったものかをちゃんと知るために 「実用 Git」を参照した.
一連のコミットの基点を変更するコマンド.
部分木を切り貼りするためのコマンドという感じ.
git rebase <branch>
とすると,現在チェックアウトしてるブランチの基点を指定したブランチの先頭に移動させる. <branch>
の後にさらにブランチ名を指定すれば,チェックアウトしてるブランチではなく,指定したブランチの基点を変更する.
--onto
オプションを使うと全く別のブランチ(コミット)に移植できる(競合したらダメだけど).
競合したら --continue
(修正してコミット),--abort
(諦めて戻す),--skip
(無視して次のコミット) でなんとかする.
git rebase -i
-i
オプションを付けるとインタラクティブにリベースを行える.
実行すると,config で指定してあるエディタが起動して(デフォルトは vi),まずは各コミットをどのように変更するか(コミットメッセージを編集したり,コミットそのものを編集したり,前のコミットとくっ付けたり)を指定する(ちなみに上が古いコミット).
そしたら,一個一個指定した変更を行う.
ちなみに,git reset --hard @{1}
でリベースをもとに戻せる.
git format-patch
一連のコミットをパッチとして出力するコマンド.
(今も使われているのかはよくわからないけど)オープンソース系のプロジェクトはリポジトリへの書き込み権限などの関係からパッチをメールでやり取りして,取り込むという開発の仕方が多かった. ため,こういうコマンドがあるみたい.
git send-email
git format-patch
で作成したパッチをメールで送信するコマンド.
すごいピンポイント.
git am
メールで受信したパッチを取り込むコマンド.
すごいピンポイント.
git request-pull
git pull
を申請するため 情報を出力する コマンド.
プルしたリポジトリとか,コミットの情報とか,誰がしたかとかが出力される.
GitHub のプルリクエストってこれで動いてるんだぁ(確かめてないけど). GitHub とかが無い場合は,これをメールで送るそうな.
git cherry
パッチが取り込めるかをチェックするコマンド.
git cherry-pick
特定のコミットをブランチに取り込むコマンド.
git cherry
で確認して,良かったやつを git cherry-pick
で取り込むって感じ.
git bisect
バグが入り込んだ位置を特定するコマンド.
二分探索をするっぽいんだが,バグがあった・なかったを簡単に指定して,自動で半分のところにチェックアウトしてくれる.
全自動するコマンドもある git bisect run make
show-branch
ブランチの状態を表示するコマンド.
各ブランチの最新コミットや,どこから派生したかとか.
git submodule
他のリポジトリ(同じリポジトリの違うブランチでもいい)をモジュールとするコマンド.
git gc
リポジトリのオブジェクトを掃除するコマンド.
git rebase
なんかで到達できなくなったコミットオブジェクトを削除する. git は各コミットを差分ではなくスナップショットで保持してるので,コミットが多くなるとどうしても重くなる. そのとき用らしい.
基本的に各種コマンドが実行されるときに自動で実行されているらしい. どれぐらいの期間や,どのタイミングで削除するかは config で設定されているらしい.
git reflog
HEAD の時系列的な遷移を出力するコマンド.
git では HEAD の時系列的な遷移を参照ログとして残している(そのうち削除されちゃうけど). git reset
や git rebase
でもとのコミットが tree 上にはなくなったときに便利.
Git リポジトリの中身
git オブジェクトは全部で4種類ある.
- blob オブジェクト
- ファイルに対応するオブジェクト
- tree オブジェクト
- ワーキングツリーを構成するツリー(ディレクトリ)に対応するオブジェクト
- commit オブジェクト
- コミットの情報を保持するオブジェクト
- tag オブジェクト
- タグの情報を保持するオブジェクト
全てのオブジェクトには SHA-1ハッシュで生成された名前が付けられている. 名前が 00174b4bc3458089ed7c870d8774ca428b371836
だった場合,オブジェクトは .git/objects/00/174b4bc3458089ed7c870d8774ca428b371836
にある.
git cat-file
オブジェクトの中身は deflate圧縮(zlib圧縮の一つ)されており,各オブジェクトの種類によってフォーマットが異なる. しかし,git cat-file <object_name>
でその中身を読みやすいようにして出力してくれる.
-t
オプションでオブジェクトの種類(blob やら tree やら)を返し,-s
でオブジェクトのサイズを返し,-p
でデータを読みやすいようにして返し,-e
でそもそも存在するかどうかを返してくれる.
blob オブジェクト
ファイルサイズと実際のファイルのデータが圧縮されて保存されている.
git rev-parse :hoge
で hoge
ファイルの blob ファイル名を出力してくれる.
tree オブジェクト
各ファイルのパーミッション,オブジェクトの種類,ハッシュ値,実際のファイルやディレクトリの名前が圧縮されて保存されいる.
git rev-parse HEAD^{tree}
で HEAD の tree オブジェクト(ルートディレクトリ)のハッシュ値を返す.
commit オブジェクト
tree オブジェクト名(ルートディレクトリ, tree),親コミットの commit オブジェクト名(複数あるかも, parent),変更の作成者(author),commit オブジェクトの作成者(commiter),コミットメッセージが保存されている.
git rev-parse HEAD
で HEAD のハッシュ値を返す.
その他
~
と ^
特定のコミットから相対的に指定する方法には ~
と ^
がある.
HEAD~1
は深さを指定してる. 要するに何個前か.
HEAD^1
は幅を指定してる. マージとかして親が複数ある時に,どの親に戻るかを指定してる.