gitの追跡対象にしたままで大量のファイルを移動・名称変更する

nozzle

結論

git mvなんてやめてmvで全部移動させてよい。 git add -Aで変更されたファイルをstagedにすれば、ちゃんと名称変更扱いになる。 (ファイル削除と新規ファイル追加とはならない)

冗長な解説

git mvを使う (非推奨)

git mvとは

gitの追跡対象にしたままファイルの移動・名称変更ができるコマンド。

使い方

gitの追跡対象に”foo.txt”と”hoge.txt”があるとする。

> git ls-files
foo.txt
hoge.txt

今、“foo.txt”を”bar.txt”に名称を変更したい。 しかし、mv foo.txt bar.txtとすると、foo.txtの削除とbar.txtの新規追加となってしまう。 (これらをstagedにすると名称変更扱いになるが、[[#mvを使う (推奨) |後述]]する。)

> mv foo.txt bar.txt
> git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        deleted:    foo.txt

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        bar.txt

そこで、git mvを使うと追跡対象を保ったままファイル名を変更できる。

> mv foo.txt bar.txt
> git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        renamed:    foo.txt -> bar.txt

git mvの欠点

git mvではワイルドカードが使えない致命的な欠点がある。

> git ls-files
foo.txt
hoge.txt

このような状態でカレントディレクトリの全てのtxtファイルを”dir1”というディレクトリに移動させたい。 先述のとおりgit mv* (アスタリスク)が使えないので以下のコマンドは失敗する。

## 以下のコマンドは失敗する
> mkdir dir1
> git mv *.txt ./dir1/
fatal: bad source, source=*.txt, destination=dir1/*.txt

そのため、git mvで移動する場合、移動したいファイルの数だけコマンドを実行しなければならない。

> mkdir dir1
> git mv foo.txt ./dir1/foo.txt
> git mv hoge.txt ./dir1/hoge.txt

これは、移動したいファイルが大量にあるときに困る。 forループでスクリプトを書くこともできるが、面倒だ。

mvを使う (推奨)

mvでワイルドカードを使って移動する

> git ls-files
foo.txt
hoge.txt

先程と同じ状況でカレントディレクトリの全てのtxtファイルを”dir1”というディレクトリに移動させたい。 mv*を使って、複数のファイルを指定できるので以下のコマンドで移動できる。

> mkdir dir1
> mv *.txt dir1/
> ls dir1
foo.txt hoge.txt

この操作は、これらのファイルがunstagedな状態では、新規ファイルの追加と既存ファイルの削除として扱われてしまう。 そこでgit add -Aを実行して、変更があったファイルを全てstagedの状態にすると、きちんと名称変更として扱われる。

> git status
On branch master
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:    foo.txt
        deleted:    hoge.txt

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        dir1/
> git add -A
> git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        renamed:    foo.txt -> dir1/foo.txt
        renamed:    hoge.txt -> dir1/hoge.txt

参考:Git で管理しているファイルのリネームを git mv でなく mv してしまったときにどうなるのか調べてみた | Qiita 参考:Gitで追跡を維持してファイル名を変更する | nullpo.io