はじめに
git mvは「ファイルを移動(リネーム)するコマンド」ですが、
実際には特別な「rename」情報を記録しているわけではありません。
Gitの設計では「rename」は存在せず、
「削除 + 追加」 として履歴に残り、「これはrenameだな」と検出される仕組みです。
この記事では、ファイルの状態ごとにgit mvがどう挙動するかをパターン化して整理します。
基本挙動
git mv は、内部的に以下の処理をまとめて行っています。
mv a.txt b.txt # ファイル名変更
git rm --cached a.txt # インデックスから a.txt を削除
git add b.txt # インデックスに b.txt を追加
つまり 「単なるショートカット」 に過ぎません。
このためファイルの状態によって結果が変わります。
パターン一覧
① コミット済みファイルをリネーム
git mv a.txt b.txt
- a.txt は削除
- b.txt が新規ファイルとして追加
git status:
renamed: a.txt -> b.txt
※ただし内部的には削除+追加。
コミットすると履歴上は「削除と追加」になるが、git statusで確認するとリネームして判定される。
「1.」のより詳細な説明
コミット済みファイルをリネームする場合の挙動をより詳しくみていきます
1. コミット済みのファイルを確認
$ ls gitmv/
test01
gitmv/配下にtest01というファイルがあります。
こちらのファイルは既にコミット済みです。
2. ファイル名を変更
$ git mv gitmv/test01 gitmv/test02
3. ファイル名変更後の状態を確認
$ git status
On branch main
Your branch is up to date with 'origin/main'.
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
renamed: gitmv/test01 -> gitmv/test02
renamed: gitmv/test01 -> gitmv/test02と記載ある通りリネームされたと判定されています。
4. git mvを利用せずに同じ状態にする
- 通常の
mvコマンドでファイル名を変更する
$ mv gitmv/test01 gitmv/test02
- 現在のファイルの状態を確認する
$ git status
On branch main
Your branch is up to date with 'origin/main'.
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
deleted: gitmv/test01
Untracked files:
(use "git add <file>..." to include in what will be committed)
gitmv/test02
no changes added to commit (use "git add" and/or "git commit -a")
gitmv/test01が削除され、まだステージングに追加されていないことが分かります。
またgitmv/test02が新規ファイルとして追加され、まだ追跡されていない状態です。
- ファイル削除情報インデックスに追加する
$ git rm --cached gitmv/test01
rm 'gitmv/test01'
これで削除情報がインデックスに追加され、次回コミットでtest01は完全に削除されます。
※ インデックスの追加は'git add'でも可能だが、'git rm --cached'がやりたいことの文脈に一致するため利用しています。
- 現在のファイルの状態を確認する
$ git status -uall
On branch main
Your branch is up to date with 'origin/main'.
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
deleted: gitmv/test01
Untracked files:
(use "git add <file>..." to include in what will be committed)
gitmv/test02
gitmv/test01の削除がステージングに追加されました。
-
gitmv/test02の新規追加をステージングに追加する
$ git add gitmv/test02
- 現在のファイルの状態を確認する
$ git status
On branch main
Your branch is up to date with 'origin/main'.
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
renamed: gitmv/test01 -> gitmv/test02
gitmv/test01が削除、gitmv/test02が新規追加されたことにより
gitmv/test01はgitmv/test02にリネームされたと判定されています。
つまり以下の二つは同じことを実行していると言えます。
$ git mv gitmv/test01 gitmv/test02
$ mv gitmv/test01 gitmv/test02
$ git rm --cached gitmv/test01
$ git add gitmv/test02
② 新規ファイル(git add 済み)をリネーム
echo "foo" > a.txt
git add a.txt
git mv a.txt b.txt
- インデックス上で「a.txt の新規追加」が消える
- 「b.txt の新規追加」として置き換わる
git status:
new file: b.txt
👉 rename としては記録されず、単純に「b.txt の追加」となる。
「2.」のより詳細な説明
ステージング済み新規ファイルをリネームする場合の挙動をより詳しくみていきます
1. ステージング済み新規ファイルを確認
$ git status
On branch main
Your branch is up to date with 'origin/main'.
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: gitmv/test01
2. ファイル名を変更
$ git mv gitmv/test01 gitmv/test03
3. ファイル名変更後の状態を確認
$ git status
On branch main
Your branch is up to date with 'origin/main'.
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: gitmv/test03
リネームではなく、gitmv/test03の新規追加と認識される。
4. git mvを利用せずに同じ状態にする
- 通常の
mvコマンドでファイル名を変更する
$ mv gitmv/test01 gitmv/test03
- 現在のファイルの状態を確認する
$ git status
On branch main
Your branch is up to date with 'origin/main'.
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: gitmv/test01
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
deleted: gitmv/test01
Untracked files:
(use "git add <file>..." to include in what will be committed)
gitmv/test03
3つの情報が分かります
・gitmv/test01が新規ファイルとしてステージングエリアに追加されている
・gitmv/test01が削除ファイルとして未ステージングとなっている
・gitmv/test03が新規ファイルとして未追跡ファイルとなっている
- 変更を全てステージングに追加する
$ git add -A
- 現在のファイルの状態を確認する
$ git status
On branch main
Your branch is up to date with 'origin/main'.
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: gitmv/test03
git mvした結果と同じになりました。
つまり以下の2つは同じ挙動と言えます
$ git mv gitmv/test01 gitmv/test03
$ mv gitmv/test01 gitmv/test03
$ git add -A
③ 未追跡(untracked)ファイルをリネーム
echo "foo" > a.txt
git mv a.txt b.txt
fatal: not under version control, source=...
👉 エラー。
未追跡ファイルは Git 管理外なので git mv は使えない。
単純に OS コマンドの mv を使う必要がある。
④ ステージング済み+修正を加えたファイルをリネーム
echo "foo" > a.txt
git add a.txt
echo "bar" >> a.txt # さらに修正
git mv a.txt b.txt
インデックスに「b.txt の新規ファイル(内容=foo)」が登録される
ワーキングツリーには「b.txt(内容=foo+bar)」が残る
👉 「ステージング内容」と「作業ツリー内容」が分離する。
コミット時に混乱しやすいので注意。
「4.」のより詳細な説明
1. ステージング済み+修正を加えたファイルを確認
$ git status
On branch main
Your branch is up to date with 'origin/main'.
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: gitmv/test01
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)
modified: gitmv/test01
2. ファイル名を変更
$ git mv gitmv/test01 gitmv/test02
3. ファイル名変更後の状態を確認
$ git status
On branch main
Your branch is up to date with 'origin/main'.
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
deleted: gitmv/test01
new file: gitmv/test02
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)
modified: gitmv/test02
-
gitmv/test01の削除ファイルがステージングに追加されている -
gitmv/test02の変更前ファイルがステージングに追加されている -
gitmv/test01の変更後ファイルが未ステージングとなっている
git add .することでgitmv/test02の変更分も含めて新規ファイルとしてステージングに追加される
4. git mvを利用せずに同じ状態にする
- 通常の
mvコマンドでファイル名を変更する
$ mv gitmv/test01 gitmv/test02
- 現在のファイルの状態を確認する
$ git status
On branch main
Your branch is up to date with 'origin/main'.
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: gitmv/test01
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
deleted: gitmv/test01
Untracked files:
(use "git add <file>..." to include in what will be committed)
gitmv/test02
・gitmv/test01への修正がステージングに追加されている
・gitmv/test01の削除ファイルが未ステージングとなっている
・gitmv/test02が未追跡ファイルとなっている
- 変更を全てステージングに追加する
$ git add .
- 現在のファイルの状態を確認する
$ git status
On branch main
Your branch is up to date with 'origin/main'.
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
deleted: gitmv/test01
new file: gitmv/test02
以上の結果から以下の2つは同じ挙動と言えます
$ git mv gitmv/test01 gitmv/test02
$ git add .
$ mv gitmv/test01 gitmv/test02
$ git add .
まとめ
git mvは複数のコマンドを実行しているため理解しづらく、かつファイルのステータスによっても挙動が微妙にことなるため今回はまとめてみました。
参考になると幸いです。