💡

git add の対話モード

2022/07/01に公開

git add [ファイル名] あるいは git add . くらいしか今まで使ってきませんでした。しかしこの前ふと、「ファイルのこの部分だけはコミットしたいんだけどな〜」と思い調べてみたところ、git add には対話モードなる機能があることを知りました。お恥ずかしながら、実ははじめて知りました。使い方をまとめておきます。

https://git-scm.com/book/ja/v2/Git-のさまざまなツール-対話的なステージング

対話モードの始め方

ターミナル上で下記のように入力します。

# ショートハンド
git add -i
# すべて記述
git add --interactive

すると、対話モードが開始されます。対話モードでは、git statusと似た「どのファイルがステージに追加されているか・されていないか」といった情報と、この対話モードで実行できるコマンドの一覧が表示されています。

❯ git add -i
           staged     unstaged path
  1:    unchanged        +4/-0 Cargo.lock
  2:        +1/-0      nothing Cargo.toml

*** Commands ***
  1: status       2: update       3: revert       4: add untracked
  5: patch        6: diff         7: quit         8: help
  
What now>

status は git status とほぼ同等の目的を達成できますが、ステージした変更が左側、ステージしていない変更が右側に表示されます。直感的ですね。

update は、実行後ファイルを選択してステージに追加できます。git add をそのまま起動すると、個別のファイルを指定する際にはフルパスをコピー&ペーストする必要があるかと思いますが、update コマンドを利用すると番号の指定のみで済ませられ簡単です。

revert はファイルのステージを取り消します。git reset する際と同等の動きをします。全部を reset したいわけではない際に有効そうです。revert というとコミットの revert を思い浮かべてしまうのですが、そうではなく add という行為の revert (巻き戻し)のことのようです。

add untracked はまだ git でトラックされていないパスをステージに追加できます。新規ディレクトリやファイルを作成し、git add を実行すると untracked files というリストを見られるはずです。それをステージすることができます。

patch はファイルの一部をステージすることができる機能です。add コマンドには実は git add -p というパッチモードが存在するのですが、これを呼び出します。普通に add コマンドを実行しただけでは「ファイル単位での add」しか管理できませんが、p オプションはブロック単位(ハンクと呼ばれます)をサポートしています。

diff は git diff と同等です。これからコミットされる予定のものを確認するためのコマンドです。

対話モードのコマンドの使い方

対話モードのコマンドの使い方には流れがあります。次の通りです:

  1. 使いたいコマンドを選ぶ。
  2. 各コマンドの操作をする。
  3. 何も入力せず Enter を押す→対話モードのホームに戻る。
  4. 次の作業をする場合はこの手順1で使いたいコマンド選ぶ。終了したい場合はquitを選ぶ。

今回は update を使用する例を紹介します。例があまりよくなくて、もしかすると git add . で済ませる方が早いかもしれませんが、対話モードの体験の初歩として紹介しておきます。

update コマンドを使ってみる

実験するためのディレクトリを作成し、その中で Rust のプロジェクトを始めてみる例について考えてみます。

まず git-example という適当な名前で Rust のプロジェクトを作ってみます。cargo new コマンドで行えます。

cargo new git-example

cargo new はプロジェクトを開始するのに必要なものを一通り揃えた状態にしてくれるコマンドです。git も新規プロジェクト作成のタイミングで初期化されます。ディレクトリの中身を表示してみた結果は下記の通りです。

❯ ls --tree git-example
git-example
├── Cargo.toml
└── src
   └── main.rs

現状のステージの状況は次のようになっています。ファイル全体はまだ untracked な状態です。

❯ git status
On branch master

No commits yet

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        .gitignore
        Cargo.toml
        src/

git add -i を用いて対話モードを起動してみます。

❯ git add -i

*** Commands ***
  1: status       2: update       3: revert       4: add untracked
  5: patch        6: diff         7: quit         8: help
What now>

今回は untracked をまずステージするのが目的なので、「a」もしくは「4」を「What now>」に入力します。

❯ git add -i

*** Commands ***
  1: status       2: update       3: revert       4: add untracked
  5: patch        6: diff         7: quit         8: help
What now> a
  1: .gitignore
  2: Cargo.toml
  3: src/main.rs
Add untracked>>

今回は .gitignore の追加と Rust 関連のコミットは別のコミットメッセージにしたいものと仮定します。なので、今回は .gitignore からまずステージしていきます。

「Add untracked」にファイル名の先頭についている番号を入力することで、指定したファイルをステージすることができます。「1: .gitignore」を今回は選択したいので、「1」と入力します。入力後 Enter キーを押すと、.gitignore がステージされたことがわかります。

❯ git add -i

*** Commands ***
  1: status       2: update       3: revert       4: add untracked
  5: patch        6: diff         7: quit         8: help
What now> a
  1: .gitignore
  2: Cargo.toml
  3: src/main.rs
Add untracked>> 1
* 1: .gitignore
  2: Cargo.toml
  3: src/main.rs
Add untracked>> 

「Add untracked」に何も入力せず Enter を押すと、対話モードのホーム画面に戻ることができます。ホームに戻ったら「1」を入力して、追加された内容を確認してみます。.gitignore がステージされたのを確認できます。

Add untracked>> 
added 1 path

** Commands ***
  1: status       2: update       3: revert       4: add untracked
  5: patch        6: diff         7: quit         8: help
What now> 1
           staged     unstaged path
  1:        +1/-0      nothing .gitignore

最後に「q」を入力すると、対話モードを終了できます。その後、適当なコメントをつけて一度コミットを作成してください。

*** Commands ***
  1: status       2: update       3: revert       4: add untracked
  5: patch        6: diff         7: quit         8: help
What now> q
Bye.

コミット後、もう一度対話モードを起動して、今度は「Cargo.toml」などを追加してみるとよいかもしれません。

対話モードを使うメリット

コミットログは1つに対して1つの意味のまとまりがあると思います。たとえば、同一ファイル内にAという構造体の追加とBという関数の修正、さらに別ファイルでCという変数名の変更とその他多数の修正があったとして、A、B、Cではそれぞれ微妙に意味単位が異なるケースを考えてみます。修正を一通り行ってから気づいたものとしましょう。その際に、次のように別々にコミットログを用意したくなるケースがあるはずです。

  1. A という構造体を YYY という目的のために追加した。
  2. B という関数を XXX という目的のために追加した。
  3. 変数名を C に変更した。
    ...続く

GitHub が先日公開した記事でもやはり同様に、こうした意味単位で小さくコミットしていくことの重要性が語られています。「Make each commit both “small” and “atomic.”」という標語でそうした思想が語られています。

https://github.blog/2022-06-30-write-better-commits-build-better-projects/

修正量が膨大になると、さらにこの数は純増していきます。修正量が大きなパッチであればあるほど、作業を一度通しでやり切って整合性が取れた修正内容であることを確認してからコミットをしたくなるかもしれません。大きくなった差分をあらためて意味単位でまとめ直すのは結構大変です。通常のファイル単位での git addgit add -p を切り替えながら作業するのはまずまず煩雑です。

対話モードは、こうした意味単位のまとめ直しをしやすくするために便利なツールだなと思いました。通常のファイル単位でのステージと、patchを利用した部分ステージの両方をコマンド一つで一気に済ませられます。ここにメリットがあるのではないかと思います。

私も明日からの業務でいかせそうです。ぜひ活用してわかりやすいコミットログを作っていきましょう。

Discussion