git prev & git next (git nextの改良)
はじめに
コードを読むために、前のコミットに戻ったり、先のコミットに進んだりすることがよくあります。移動を楽にするため今まではGit prev & nextのエイリアスを使っていました。git prev
で1つ前のコミットに戻り、git next
で1つ先のコミットに進みます。
git prev
は問題ありませんが、git next
はブランチがmaster(main)限定かつ、コミットヒストリが膨大なリポジトリだと遅くなります。本記事では任意のブランチに対してgit next
をできるようにし、最新のコミット付近で速くする方法について書きます。
結論
.gitconfig
に以下のエイリアスを追加します。git prev
は元記事と同じです。
[alias]
prev = checkout HEAD^
next = !sh -c 'git checkout \"$(git log --format=%H \"$(git name-rev --name-only HEAD | sed 's/~.*$//')\" | grep -B1 -m1 \"$(git rev-parse HEAD)\" | head -1)\"'
もしくは以下のコマンドで追加できます。
git config --global alias.prev 'checkout HEAD^'
git config --global alias.next $'!sh -c \'git checkout "$(git log --format=%H "$(git name-rev --name-only HEAD | sed \'s/~.*$//\')" | grep -B1 -m1 "$(git rev-parse HEAD)" | head -1)"\''
ただし、以下の注意があります。
- コミットヒストリは分岐するので
git prev
に対してgit next
は可逆ではありません。分岐地点でgit next
を使うと、git prev
を使う前のコミットに戻るとは限りません。 -
git prev
またはgit next
を使うと、detached HEADになります[1]。例えば、ブランチの先頭でgit prev
とgit next
をちょうど1回ずつ使ってブランチの先頭に戻ってきたとしても、detached HEADのままです。
詳細
コマンドの説明
はじめに述べた通り、コミットヒストリが膨大なとき元記事のgit next
は遅いです。これはgit log
に--reverse
オプションを付けているためです。
nextのエイリアスのスクリプト部分(!sh -c'...'
の中身)を抜き出し、整形して以下に示します。!sh
の!
は外部コマンドを呼び出すための記法です。
branch_name="$(git name-rev --name-only HEAD | sed "s/~.*$//")"
current_hash="$(git rev-parse HEAD)"
next_hash="$( \
git log --format=%H "$branch_name" \
| grep -m1 -B1 "$current_hash" \
| head -1 \
)"
git checkout "$next_hash"
-
git name-rev
を使って現在のコミット(HEADが指し示すコミット)のブランチ名を取得します[2]。detached HEADのとき、ブランチ名とブランチの先頭からの世代数が出力されるので(master~4など)、sed
で世代数を削ります。 -
git rev-parse
を使って現在のコミットのハッシュ値を取得し、現在のコミットから1つ先のコミットのハッシュ値を得ます。grep
の-m
オプションで最大マッチ行数を指定するとコミットヒストリが膨大なときに速くなります。 -
得たハッシュ値を使って1つ先のコミットにチェックアウトして終わりです。
変更点は以下の3点です。
-
git name-rev
でブランチ名を取得し、master(main)以外のブランチでコミットを移動できるようにしました。 -
git log
の--reverse
オプションを指定せず、最新のコミット付近での移動を速くしました。 -
grep
に-m
オプションを指定し、ハッシュ値の検索時間を短くしました。
計測
速くした、という記事なのでtovalds/linux(コミット数は約112万)で簡単な計測をしました。
git next |
元記事 | 本記事 |
---|---|---|
最新コミット付近 | 0.137 | 10.215 |
最古コミット付近 | 10.031 | 10.483 |
-
ブランチ名を取得する方法はいくつかありますが、detached HEADでブランチ名を取得できるコマンドは
git name-rev
のみでした。 ↩︎
Discussion