始めに
何をどれだけ作るかは未定です。
まだGUIの話はしません。
動機
- I love git.
- 各種GUIクライアントたちは(機能とプラットフォームと好みで)一長一短
- 「ぼくのかんがえたさいきょうのGUIクライアント」を作れるようになりたい
行単位のstage/unstage
gitのGUIクライアントにとって最も大事な機能は
- 行単位でstageおよびunstageが気軽にできる
- wokrespaceとindexの状況(diff)が把握しやすい
だと私は考えています。そういうことにしましょう。
だいたいdiffを見ながらstage/unstageするので、後者は前者の必要条件ですね。
なお、ここで言うstageとはindexへの登録、unstageとはindexから解除のことです。
indexとはパスとデータベース上のファイル実体(blob)の対応表みたいなもので、これに登録されているものがcommitの対象となります。
コマンドラインの場合
GUIを作る前に、gitの勉強をしましょう。
コマンドラインでは行単位のstage/unstageをする際に、以下のコマンドを使います
git add -p
git reset -p
上記コマンドでインタラクティブモードに入り、対象となるhunkに対してe
を選んで編集することで、行単位でのstage/unstageが可能になります。
(hunkはdiffの断片で、1ファイル内の離れた場所にあるdiffは別のhunkとして切り出されます)
git add -e [ファイル名]
でもだいたい同じですが、reset
には-e
オプションがありません。
また、-p
オプション経由での編集時には、以下のようなコメントがついていて便利です。
# To remove '-' lines, make them ' ' lines (context).
# To remove '+' lines, delete them.
# Lines starting with # will be removed.
この編集用hunkは.git/addp-hunk-edit.diff
として一時ファイルが作成されます。
(git add -e
の場合は.git/ADD_EDIT.patch
で、
1ファイルについての1つ以上のhunkを含むpatch全体です)
ここで編集されたhunkをもとにpatchが作成されて、自動でapply --cached
されます。
git apply --cached <patch>
git apply -R --cached <patch>
このインタラクティブモードでは、
(おそらく)patchはファイルとして作成されずにapply
コマンドに渡されていますが、
git diff
およびgit diff --cached
の出力と同様の形式です。
この出力をapply
すればgit add
とgit reset
したのと同じことが起こります。
git add
は以下と同じ。
git diff | git apply --cached
git reset
は以下と同じ。
git diff --cached | git apply -R --cached
patchおよびdiffのフォーマット
詳細はこちら https://git-scm.com/docs/diff-format#_combined_diff_format
ヘッダー部分と、1つ以上のhunkの繰り返しからできています。
(複数ファイルが対象となっている場合は、それがさらに繰り返されます。)
- 1ファイルのdiff全体に対するヘッダー(4~5行)
- hunkのヘッダー(1行)
- hunkのボディ(1行~)
- hunkのヘッダー
- hunkのボディ
- 1ファイルのdiff全体に対するヘッダー
- hunkのヘッダー
- hunkのボディ
- hunkのヘッダー
- hunkのボディ
ヘッダー部分はこんな感じです。ファイルごとに存在します。
diff --git a/names.txt b/names.txt
index 61b59be..2d94d6d 100644
--- a/names.txt
+++ b/names.txt
hunkはこんな感じです。@@
から始まる行がhunk自身のヘッダーです。
@@ -1,2 +1 @@
-test.txt
-names.txt
\ No newline at end of file
インタラクティブモードではhunkのヘッダー部分は編集する必要がありませんが、apply --cached
される前に自動で再計算されてpatchに組み込まれているようです。
行単位のstage/unstageのためにやるべきこと5つ
- 元になるpatchとしてdiffを出力する
- stage/unstageしたい行を選ぶ
- hunkのボディを編集する
- hunkのヘッダーを再計算する
- 最終的なpatchをapplyする
GUIクライアントがGUIとして「見せる」のは1と2ですが、もちろん3~5も必要です。
- 3は、インタラクティブモードでのhunk編集でやっていることで、
+
や-
で始まる行を丸ごと消したり、スペース始まりにしたりします。 - 4は、hunk内の対応行の開始位置とオフセットを差分の適用前後それぞれについて表しています。
- 5は、applyするだけで、コマンドも分かっているので簡単なはずです。(でも、例えば、JGITは
git apply --cached
に相当するコマンドが無いように思います)
あとは作るだけ!
参考になるかもしれないコード
add/resetのインタラクティブモード
https://github.com/git/git/blob/master/git-add--interactive.perl
ただし、このコードが実際に使われているのか、よく分かっていません。
同内容のファイルは、windowsだと下記のような場所にあります。
C:\Program Files\Git\mingw64\libexec\git-core\git-add--interactive
git-gui.exeのStage Lines For Commit
https://github.com/git/git/blob/6867272d5b5615bd74ec97bf35b4c4a8d9fe3a51/git-gui/lib/diff.tcl#L643
tclのコードです。
テキストウィジェットの機能を駆使して文字列検索しているため、文法を知らずに読むのは無理でした。https://www.tcl.tk/man/tcl8.4/TkCmd/text.htm などを参照すれば読めると思います。