MarkdownとBullet Journal

いわゆるプログラマーのつぶやき

【Git】rm:ファイルを削除 or 管理から外す

git rm コマンド

git追跡fileの削除はシェルのrmコマンドではなく、git rmコマンドを使う。

最初に現在の編集状況を解説し、次に丸数字の順に説明する。

f:id:ProgrammingForEver:20210908132056p:plain

想定する現在の編集状況(ある開発中の状態)

  • 社内業務用のシェルプログラムのプロジェクトとする
  • 10回前のcommitでfileはa.shとb.shがあり、b.sh内の変数名はhoge
  • 1回前のcommitでb.shの変数名をfugaに修正
  • 最新commitでc.sh fileを追加
  • 編集を再開しb.shの変数名をpiyoに変えてstaging(index)した。d.sh fileも追加&staging
  • さらにb.shの変数名をfugafugaに変えて、e.sh fileを追加した(e.shはまだgitで追跡されていない)
  • この状態を基本として、git rmコマンドを実行する
  • 変化が発生した部分を朱書きで表示

① git rm file名

gitが追跡しているfileを削除する。staging、作業ディレクトリーからファイルを削除する。削除した行為をgitが認識しているので次回のgit commit実行時にfile削除をリポジトリに記録出来る。シェルのrmコマンドを使うとstagingには削除したfileが残ったままとなるためgit rmを使用すること

② git rm file名(内容変更時)

gitの追跡対象fileで、内容が変更されたfileをgit rmで削除しようとするとエラーになる。これは変更内容が保存されていないfileの損失を防ぐためのもの

③ git rm -f file名(内容変更時)

しかし上記の様な場合でも「-f」オプションを付けると、file内容が変更されていても削除する(変更内容を失う)

④ git rm --cached file名

対象fileをgitの追跡対象から外すコマンド。stagingからは削除されるが、作業ディレクトリ内にはgitの追跡対象外fileとして残る。残したfileは .gitignoreに追記しないと常にstaging候補のfileとして表示される点に注意

(このd.shはcommitで保存されていないため①の様にgit rm d.shで削除しようとするとエラーになる)

git addとgit resetとgit rmの関係

git rmコマンドの話の延長として、git addコマンド、git resetコマンドの機能と比較しながら着目してみる。

  • git addは、fileをgit追跡対象fileにして、そのfileをstagingに上げる
  • git resetは、git追跡対象fileをstagingから下ろす
  • git rmは、git追跡対象fileを追跡対象から外す(or 削除)

つまり新規fileに対してgit addを行うと、①fileをgit追跡対象にして②stagingの2つの動作を行う。これは③git resetのunstagingと④git rmのgit追跡対象から外す( or 削除する)という二つのコマンドの逆動作を一気に行っている事になる。となると、git rmの機能だけの逆動作、つまり新規fileをgit追跡対象fileにするだけのコマンドが有っても良い理屈になり、実際に存在する。

git add -N コマンド

それがgit add -N file名コマンドで、対象fileをgit追跡対象にするがstagingはしない動作になる。コマンド例を示す。

  • a.shファイルは、git add a.shでgit追跡対象fileにしてstagingに上げる
  • b.shファイルは、git add -N b.shでgit追跡対象fileにするがstagingしない
  • c.shファイルは、何もしない(git追跡対象外のまま)
$ touch a.sh b.sh c.sh         # - a.sh , b.sh , c.sh を作成
$ git add a.sh                 # - a.shをstaging
$ git add -N b.sh              # -b.shを add -Nオプションで実行

$ git status                   # - 現在の状態を見る
On branch main
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
    new file:   a.sh           # - a.shはstagingされている

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
    new file:   b.sh           # - b.shは非stagingだがgit追跡対象になった

Untracked files:
  (use "git add <file>..." to include in what will be committed)
    c.sh                       # - c.shはgit追跡対象外のまま

$ git rm -f a.sh               # - git追跡対象fileなのでgit rm -f で強制削除出来る 
rm 'a.sh'
$ git rm -f b.sh               # - git追跡対象fileなのでgit rm -f で強制削除出来る
rm 'b.sh'
$ git rm -f c.sh               # - git追跡対象外fileなのでgit rm -f では削除出来ない
fatal: pathspec 'c.sh' did not match any files
$ 

上記の様にgit statusを実行すると、git add -Nを用いたb.shファイルには Changes not staged for commit: のメッセージが現れる。そしてgit追跡対象fileになっているのでgit rm -fの強制コマンドで削除出来る(修正保存されていないのでgit rmコマンドはエラーになる)。

fileの動き

作業ディレクトリに新規file追加してからの各コマンドによる動きを表にした。

f:id:ProgrammingForEver:20210913090355j:plain

説明は下記の別記事を参照して欲しい。

programmingforever.hatenablog.com

どう使うのか

このgit add -Nコマンドを使えば、stagingはしたくないが、新規作成したfileをgitの配下に置いて差分をgit diffでいつでも見られる運用が実現する。例えばgit add -N .と打つと、作業ディレクトリ内の全fileをすべてgitの追跡対象に出来るので、差分を確認しながら編集を行い、整ったものからstaging出来る。