特定時点の特定ファイルをエディタで開くgit-alias


#前書き
組込のC++プログラマ(2ねんせい)です。
プログラミングミリしら状態でうっかり仕事にしてしまったので、さっさと成長しないと食い殺される。助けて。
#本題
表題のaliasが見た限りだと無かったので作った。
笑えるほどに環境依存なので要改変。というかgitもbashも何ならWindowsもLinuxも何も分からん状態で書いている。
Windows10, Git for Windows, git version 2.17.0.windows.1 で確認。(~~そもそも環境の書き方がこれで十分なのかすら判らない~~)

`git show-file <ブランチ名orコミットハッシュ> <ファイルパス> &`で動作する。

```bash
# e.g. コミットハッシュが7a65b4cのとき
git show-file 7a65b4c file/path/my_file.cpp &
```
などと打つと、AppDataの奥底に`my_file-7a65b4c.cpp`というファイルが生成されてサクラエディタで表示される。なおコミット指定を`HEAD^^^^^`などとすると`my_file-HEAD^^^^^.cpp`というクソダサファイルを生成できて少し楽しい。
`&`はシェルスクリプトのバックグラウンドプロセス指定のために付けている。([参考](https://qiita.com/nyango/items/7b6b719f248b2ee8d379))
生成後のファイルは7日で削除される。
#alias

**一番下に少しマシになったバージョンがあるのでスクロールしてください。**

```bash:.gitconfig
[alias]
show-file = "!f(){ \
	if [ $# -eq 2 ];then \
		git clean-temp-file;\
		repo=${1};\
		repo_s=${repo##*/};\
		f_path=${2};\
		f_name=${f_path##*/};\
		f_row_name=${f_name%.*};\
		f_ext=${f_name##*.};\
		git show ${repo}:${f_path} > C:/Users/[user_name]/AppData/Local/Temp/git-show/${f_row_name}-${repo_s}.${f_ext};\
		if [ $? -eq 0 ];then \
			sakura C:/Users/[user_name]/AppData/Local/Temp/git-show/${f_row_name}-${repo_s}.${f_ext};\
		fi;\
	else \
		echo \"syntax error : git show-file <repo-name> <file-path>\";\
	fi;\
};f"
clean-temp-file = !find C:/Users/[user_name]/AppData/Local/Temp/git-show/* -mtime +7 -exec rm {} +
```

[user_name]は各自のユーザ名。
`AppData/Local/Temp/`以下の適当なフォルダに`git show`で[見つけたファイルを拡張子付きで出力](https://qiita.com/UmedaTakefumi/items/af2e38068616d982f489)して、適当なエディタ(今回は自前の環境で最も軽いサクラエディタ)で開いているだけ。PATHが通っているプログラムなら何でもいいと思う。`$EDITOR`指定でも行けるのかもしれない(要検証)。
変数`repo`と`f_path`をごちゃごちゃ弄ってるのは、複数のバージョンを同時に開きたい時のためのリネーム処理。用途によってはリネームしない方が適しているかもしれないし、その場合はaliasももっと短くなる。なおブランチ名の`/`を置き換える処理をしていないので、例えば`feature/some_branch`と`origin/feature/some_branch`はどちらも`my_file-some_branch.cpp`にリネームされてしまう。片手落ちも良いところである。(今ふと思ったが、`rev-parse`でハッシュ取得して使っても良いかもしれない)。
`clean-temp-file`は最終更新日から7日経過したファイルを削除するだけのコマンドなので普通にベタ書きしても多分動く。単にデバッグ用に書いたコードを流用しているだけ(手抜き)。
#不満点(要改善点)
- `git show`に失敗してもファイルが生成されてしまう上にbashが入力待ち状態で停止する(Ctrl+Cなどで脱出)
- `AppData`の絶対パスは何らかの方法で取得できそうな気がする
- `core.editor`を取得して使うようにすると(VSCodeとか使ってる人は)便利かも
- (このコマンドに限らず)bashから外部のツールを起動する際、末尾にいちいち`&`を付けないと並列起動できない......

#参考
- [gitで特定のコミット(リビジョン)からファイルを取り出す](https://qiita.com/UmedaTakefumi/items/af2e38068616d982f489)
- [gitのaliasコマンドに引数を渡す方法]
(https://qiita.com/yatemmma/items/22aa62e232776f4f330b)
- [シェルスクリプトで単純に並列実行・直列実行を行う](https://qiita.com/nyango/items/7b6b719f248b2ee8d379)

---
##以下日記
使ってみて思ったが、正直`git difftool --dir-diff`の下位互換みたいな雰囲気がある。特定ファイルを参照したいときは9割方「その時点で生じた差分を見たい」時だということを失念していた。まぁ変更履歴はどうでもいいから特定時点のファイル見たいとかいう要望には応えられるし……いやそれなら`git checkout`でも概ねいけるんだけど。

`AppData/Local/Temp/`以下に一時ファイルを生成するアイデアは、なんか`difftool`の挙動がそうなってるっぽかったので丸々パクった(免責事項)。

サクッと書いているがbashやgitのエスケープシーケンスの挙動が分からずにほぼ丸一日掛かっている。というか`\`で改行できることすら知らなかった。未だに詳細が分かっていない。

初心者なのでマサカリを沢山投げつけて頂ければ幸いです。頑張ります。

# 更新
ちょっとマシになった

```bash:.gitconfig
[alias]
    # 特定の時点(コミット/ブランチ等)の特定ファイルをエディタで開く.
    # 作業ディレクトリは(Windowsの場合)AppData/Local/Temp/git-show/ であり、1週間以上経過すると削除される.
    # git config core.editorに適当なエディタを設定していないと動作しないため注意.
    show-file = "!f(){ \
        if [ $# -eq 2 ];then \
            local repo=$(git rev-parse --short ${1});\
            if [ "${?}" -ne 0 ]; then \
                return 1;\
            fi;\
            git ls-files ${2} > /dev/null || return 1 ; \
            local f_path=${2};\
            local f_name=${f_path##*/};\
            local f_row_name=${f_name%.*};\
            local f_ext=${f_name##*.};\
            mkdir -p /tmp/git-show;\
            git show ${repo}:${f_path} > /tmp/git-show/${f_row_name}-${repo}.${f_ext};\
            if [ $? -eq 0 ];then \
                $(git config core.editor) /tmp/git-show/${f_row_name}-${repo}.${f_ext} &\
            fi;\
        else \
            echo \"syntax error : git show-file <repo-name> <file-path>\";\
        fi;\
        find /tmp/git-show/* -mtime +7 -exec rm {} + ;\
    };f"
```
まず、作業ディレクトリを`tmp`にした。`Git for Windows`のインストール時点で`tmp`が`AppData\Local\Temp`に設定されているらしく、これで正しく動く。当然Linuxでも動く……はず。
次に第一引数を`git rev-parse`に、第二引数を`git ls-files`に渡して入力値チェックを行った。
`rev-parse --short`は`HEAD`でも`ブランチ名`でも`任意桁のコミットハッシュ`でも全て`少ない桁のコミットハッシュ`に変換してくれるので、入力値チェックに使いやすい。こちらは変数に入れる都合上、`$?`の値を見て不正だったら`1`を返して終了するようにした。
第二引数の`ls-files`は単に実在するか否かのチェックだけだったので、標準出力は`dev/null`に投げ捨てた上で`||`で繋ぎ、失敗したら`1`を返して終了するようにした。
最後に起動するエディタを`git config core.editor`から引っ張ってくるようにした。設定していないと動かないためここはまだちょっと改良の余地がある。