📚

gitで○○する前のソースを取得する

2021/09/12に公開

gitを使って過去の作業ツリーを取得する方法を紹介します。

注意事項

以下の説明中には git checkout でブランチを離れる手順があります。ブランチを離れることに慣れていない方(普段 git switch を使っていて -d オプションを使ったことがない方など)は、お試しの際、ブランチを離れた時点で表示されるメッセージ[1]をよく読むことをおすすめします。

コミットする前

git commit でコミットする前のソースを取得するには

$ git checkout HEAD~

とします。HEAD~ は今居るブランチの最新コミット HEAD の親コミットを指します。

マージする前

git merge で作成されるマージコミットの場合には親コミットが複数あるのでその中から取得対象を選択します。例えば

$ git checkout HEAD^

(HEAD~ でも同じ)とすれば HEAD の1番目の親を選択して、マージ先ブランチのマージ前のソースを取得でき、

$ git checkout HEAD^2

のように2番目の親を選択すれば、マージ元ブランチの最終コミットのソースを取得できます。

もっと前のコミット

もっと前のコミットのソースを取得するには、~^ を複数組み合わせて辿る(例えば git checkout HEAD~~~)か、あるいは git loggit blame で対象のコミットハッシュを特定した上で

$ git checkout コミットハッシュ

とコミットを指定してソースを取得します。

リセットする前

git reset --hardHEAD を書き換えた直後に

$ git checkout HEAD@{1}

とすると reset 前のソースを取得できます。ただし git reset --hard で失われた未コミットの変更があったとしてもそれらは戻ってきません。

HEAD@{N} の形式でもっと前の作業ツリーの取得もできます。Nの値(N=0,1,2,...)ごとに何が取得されるかは git reflog で確認できます。

リベースする前

git rebase では reflog に複数のログが記録されるため、git reflog して

HEAD@{N}: rebase (start):

(Nは何らかの整数)を見つけて、

$ git checkout HEAD@{N+1}

(N+1には上で見つけた整数Nに1を足した結果を入力)すると rebase する前のソースを取得できます。

チェックアウトする前

$ git checkout -

git checkoutgit switch する前のブランチ/コミットのソースを取得できます。

$ git checkout @{-1}

としても同じです。ブランチをチェックアウトしていた場合はブランチをチェックアウトした状態になり、detached HEAD だった場合には detached HEAD 状態でコミットを取得します。

@{-N} の形式でもっと前のものも取得できます。Nの値(N=1,2,...)ごとに何が取得されるかは git reflog して checkout: moving from が見つかった順番で確認できます:

$ git reflog|sed -n "s/.* checkout: moving from \(.*\) to .*/\1/p"|awk '{ print "@{-" NR "} " $1 }'

まとめ

git checkout で過去の作業ツリーを取得する方法をいくつか紹介しました。
最後に紹介したように git checkout - すればチェックアウトする前の状態に戻って来れますし、チェックアウトで作業ツリーの変更が失われる可能性があれば

$ git checkout チェックアウト対象
error: Your local changes to the following files would be overwritten by checkout:
	変更されていたファイル
Please commit your changes or stash them before you switch branches.
Aborting

とエラーで止めてくれる[2]ので安心です。気軽に現在と過去を行ったり来たりしてみてください。

脚注
  1. advice.detachedHead がデフォルト値 false の場合に表示されるメッセージ ↩︎

  2. git stash で作業ツリーの変更を退避させた上でチェックアウトし直してください。 ↩︎

Discussion