Gitはバージョン管理システムなのでソースコードなどで 古い記述を残さないで消してしまっても後から簡単に見ることが出来るので 余計な記述をどんどん消していく事ができます。
一方消したファイルも履歴に残っていますが、 いざ探そうと思うとちょっと面倒だったりするので とりあえず残しておこう、とか思う事もあるかもしれません。
そんなことをせずにレポジトリを綺麗にしておくためにも、 過去に削除したファイルを簡単に検索、復元させられる様にしておこう、 という話し。
過去に削除したファイルの検索
git log --diff-filter=D --summary
をすることで過去に削除したファイルを検索できます。
$ git log --diff-filter=D --summary
commit b318eb6b0875cffcc6e7c6c4990d5ef90177ad40
Author: rcmdnk <[email protected]>
Date: Thu Jun 12 18:03:06 2014 +0900
removed .mailto
delete mode 100644 .mailto
commit 7be0877bc3d8510fed4252e1dbc3d8074e2d4c11
Author: rcmdnk <[email protected]>
Date: Fri May 23 13:11:47 2014 +0900
removed unnecessary screen files
delete mode 100644 .screen/.hostForScreen
delete mode 100644 .screen/.hostForScreen-e
基本的にはlog
を見るわけですが、
--diff-filter=D
を使えば何かを削除したcommit
のみを取ってこれます。
さらに--summary
でrm
やadd
などをした時のファイル一覧が出るので
上みたいな感じに表示されて実際に削除されたファイルを見ることが出来ます。
過去に削除したファイルを復元する
git checkout <commit>^ -- <file_name>
これで削除したファイルを取ってこれます。
上の.mailto
を復元したいなら、
$ git checkout b318eb6b0875cffcc6e7c6c4990d5ef90177ad40^ -- .mailto
commit番号の後ろに^
を付けるのを忘れないように。
^
を付けると1つ前のcommitを意味する様になります。
b318...
は.mailto
を削除したcommitなのでそれより1つ前のファイルが有る所からでないと取ってこれません。
--
はそれ以降はオプション出ないことを知らせる引数で最後にファイル名を書きます。
.gitconfigにaliasを書いて簡単に検索、復元出来る様にする
コマンドを覚えて確認して復元して、ということでも良いですが、
Gitにはせっかくalias
機能があるのでそれを使います。
[alias]
deleted = log --diff-filter=D --summary
restore = "!f () {\
if [ \"$#\" -eq 0 ];then \
echo 'usage: git restore <file_name> [file_name_2 [file_name_3 [...]]]';\
return 1;\
fi;\
ret=0;\
for f in \"$@\";do \
if [ -a \"$(git rev-parse --show-toplevel)/$f\" ];then \
printf \"\\e[31m$f exists\\n\\e[m\";\
ret=2;\
continue;\
fi;\
c=$(git rev-list -n 1 HEAD -- \"$f\");\
if [ -n \"$c\" ];then \
git checkout \"$c\"^ -- \"$f\";\
echo \"$f\" has been restored;\
else \
printf \"\\e[31m$f is not in the log\\n\\e[m\";\
ret=2;\
fi;\
done;\
return $ret;\
};f"
こんな感じでdeleted
とrestore
という2つのエイリアスを~/.gitconfigの中に書いておきます。
エイリアスに関して詳しくは下のポストで。
deleted
の方は単に上で紹介したコマンドをそのままエイリアス化しただけです。
restore
の方はまずは
[ -a "$(git rev-parse --show-toplevel)/<file_name>" ]
でレポジトリにファイルが存在しているかどうかを確認しています。
git rev-parse --show-toplevel
は現在いるレポジトリのトップディレクトリ(ルートディレクトリ)の絶対パスを返します。
restore
ではレポジトリのどこに居ようがトップディレクトリからのパスでファイルを指定することにしています。
ファイルがある場合にはそれについてはスキップ。
その次は
git rev-list -n 1 HEAD -- <file_name>
によってそのファイルに関する最後のcommitを取ってきています。 これは削除されたファイルに関しては削除された時のcommit番号が返って来る事になります。
もし過去にもcommitしたことが無いファイルだとこれは空文字を返しますが、 終了ステータスは0で正常終了になるので 空文字出ないことを確認します。
空文字でなければ上で紹介したコマンドで復元。
複数のファイルを与えた場合でもそれぞれについて復元を試みます。
まとめ
Gitはサブコマンドが非常に多い上にそれらのオプションも多種多様なので とてもではないですが全ては覚えられません。
その中でも必要なものをエイリアス化しておけばコマンドを打つのも楽になりますし 簡単に覚えられます。
必要そうなものをとりあえずエイリアスにしておけば、 自分の~/.gitconfigを見ることでどういうコマンドがあったか、 ということを確認することも可能です。
これで心置きなく要らないファイルを消すことが出来ると思います。