キーワード検索50の記事がヒットしました。

鳥に生まれることができなかった人へ

git logのオプション(後編)

今回もgit logのオプションについての紹介ですが、前編中編とは少し趣旨が違います。

これまでは、いわば、ある一つのブランチの中で完結するようなオプションを紹介しました。 複数のブランチが切られている時(というかそれが当たり前ですが)、git logと打ってどの範囲のログが出力されるか、正確に分かっていますか? 「あのブランチだけのログが見たいのに、何故か違うブランチのログも出力される。。。」といったことはありませんか?「雰囲気でHEAD~とか打ってるけど詳しい意味は分かってない。。。」といったことはありませんか? たまーに出てくるチルダ(~)やキャレット(^)ですが、どのような意味を持っているか分かっていますか?

私もわかっていなかったので改めて確認したいと思います。

2つのブランチを対象にログを出力する

まずは、ダブルドット構文(..)とトリプルドット構文(...)の使用方法を説明します。

これらの構文は2つの引数(便宜上、引数と呼びます)をとります。ブランチA..ブランチBといった形ですね。

..は、どちらか一方からのみ辿れるコミットを出力します。結果は引数の渡し順によって変わります。...は、どちらか一方から辿れるコミットを出力します。こちらは引数の渡し順によって変わりません。

また、どちらも、2つのブランチ間で重複しているコミットを除く動きをします。


前提として、リポジトリは以下の状態であるとします。

image01

masterdevelopfixという3つのブランチがあります。

青い丸の中の数字はコミットのハッシュIDです。8developfixをマージしたマージコミットです。現在HEADdevelopを指しています。

このリポジトリを再現するには、以下のスクリプトを実行してください。

スクリプトを見る
git init

# masterブランチで作業
git commit -m "1" --allow-empty
git commit -m "2" --allow-empty
git commit -m "3" --allow-empty

# developブランチを作成
git checkout -b develop HEAD~~

# developブランチで作業
git commit -m "4" --allow-empty
git commit -m "5" --allow-empty

# fixブランチを作成
git checkout -b fix HEAD~~

# fixブランチで作業
git commit -m "6" --allow-empty
git commit -m "7" --allow-empty

# developブランチに移動
git checkout develop

# fixブランチをマージ
git merge --no-ff fix -m "8"

まずは、git log ブランチ名といった風に、単体でブランチ名を入力した場合の出力を確認しておきます。

git log master

masterである3から矢印で辿れる、3,2,1が対象です。

image02

git log develop(HEAD)

developである8から辿れる、8,7,6,5,4,1が対象です。

image03

現在HEADdevelopを指していることから、git log HEADとしても同じです。

git log fix

fixである7から辿れる、7,6,1が対象です。

image04

git log develop..master

では、..の動作を確認します。

言語化するとしたら「developになくて、masterにだけあるもの」 です 。3,2が出力されます。

masterから3,2,1が辿れますが、1developからも辿れるので対象外です。

image05

masterから辿れる3,2,1から、developからも辿れて重複している1が除かれているのが分かると思います。冒頭で「2つのブランチ間で重複しているコミットを除く動きをします。」と説明したのはこのことです。

git log master..develop

上記の逆です。読み方は「masterになくて、developにだけあるもの」です。

developから8,7,6,5,4,1が辿れますが、1masterからも辿ることができ、重複しているので対象外です。

image06

git log master..fix

読み方は「masterになくて、fixにだけあるもの」です。

fixから7,6,1が辿れますが、1masterからも辿れるので対象外です。

image07

git log fix..develop

読み方は「fixになくて、developにだけあるもの」です。

developから8,7,6,5,4,1が辿れますが、7,6,1はfixからも辿れることができ、重複しているので対象外です。

image08

git log develop..fix

読み方は「developになくて、fixにだけあるもの」です。

fixから7,6,1が辿れますが、これらは全てdevelopからも辿れることができ、重複しているのでコミットは出力されません。

image09

リモートリポジトリとの差分を確認できる

これまで示した例のように、ローカルでブランチ間の差分を見るのにも使用できますが、リモートリポジトリー(GitHubなど)との差分を見るのにこのコマンドは役に立ちます。

例えばgit log origin/featureAAA..featureAAAとすると、リモートになくてローカルにあるコミットだけを出力できます。つまり、どのコミットをpushすべきかという事が分かります。

git log master…develop (git log develop…master)

ここからは...の使用方法です。

...は、どちらか一方から辿れるものを出力します。どちらからも辿れるもの(つまり重複しているもの)は対象外です。

上記のコマンドでいうと、読み方は「masterdevelopのどちらか一方にあるもの」です。1masterからもdevelopからも辿ることができ、重複しているので対象外です。

image010

なお、...を使用する場合、どのような順番でブランチを指定しても結果は同じです。

git log develop…fix (git log fix…develop)

読み方は「developfixのどちらか一方にあるもの」です。7,6,1developからもfixからも辿れることができ、重複しているので対象外です。

チルダとキャッレット

チルダ~とキャレット^はgitの中でも理解しにくいものの一つです。私も何となく分からないので何となく放置していました。

あと、ググラビリティも低いですね。

~で親のコミットを表す

チルダを付与することで、ひとつ親のコミットを表すことができます。この場合、git log develop~git log 5と同義と言え、5,4,1が出力されます。

image12

同様に、~~~~~といった風に~を連ねればその分だけ親をさかのぼることができます。また、~~~2~~~~3と表すこともできます。

image13

^で親を選択する

キャレット^複数の親がある場合(マージコミットなど)に有効です。git log develop^とすることでひとつめの親である5を指すことになります。同様に、develop^2とすると、ふたつ目の親である7を指すことになります。

image14

git log develop^2とすると、2つ目の親である7を指すことになり、7,6,1が出力されます。

しかし、develop^^develop^2は同義ではありません。develop^^は、develop^^という風に「一つ目の親の一つ目の親」という意味になります。

develop^2^は「2つめの親の一つ目の親」であり、develop^2~と同義です。

image15

複雑ではないコミット履歴をこうやって図示しているから分かりやすいものの、これをコマンドラインでやろうと思うと直感的に理解しにくいと思います。この辺りはGUIツールの出番ですね。^~git log以外にも色んなところで出てくるので、これらの持つ意味さえ覚えておけばOKです。

--notでブランチを除外する

--not ブランチ名とすることで、そのブランチから辿れるコミットを除くことができます。

.....と同じオプションにも見えますが、これらは引数を2つしかとることができません。しかし--notなら、例えば、git log master develop --not fixとすることで、「masterdevelopから辿れるコミットから、fixからも辿れるコミットは除外する」といった風に、3つ以上のブランチ名を記述することができます。

image16

また、--not ブランチA ブランチBといった風に、2つ以上のブランチを除外することもできます。下図はgit log develop --not master fixのイメージです。

image17

これもややこしいですね。頭の中でこういったイメージを持ちながらコミットを辿るのは限界があります。GUIツールを頼りましょう。

また、ブランチ名の前にキャレット^をつけることで、--notと同じく、ブランチを除外することができます。git log develop ^master ^fixといった具合です。このように、^はブランチ名の前につけるか後ろに付けるかで全く意味が変わってきます。


参考

[git] チルダ(~)とキャレット(^)の違い | Tech控え帳

Git の HEAD^ と HEAD~ の違い - yu8mada

git の歴史の辿り方 · けんごのお屋敷

[Git] blameコマンドで特定の行がいつ変更されたのか調べて、バグの混入を見つける - YoheiM .NET