概要
便利なのだけど、いざ使う時に忘れていることが多いので、ここにメモメモ。
git には、git worktree
という便利なサブコマンドがあります。
ソースコードを修正しながら対応するドキュメントをちょっと修正したりしたいときがあるけど、でもソースコードを修正するブランチとドキュメントを調整するブランチは別ってときがたまにあります。
ソースコードのコミットは、ブランチAで。
ドキュメントのコミットは、ブランチBでやりたいって感じですね。
また、新しい機能を作ってるんだけど、ブランチXのコードを参考にしながら作業したいときとか。
また、実務だと「前リリース時点から変更されたやつのdiff結果をWinMergeとかつかってレポートでほしい」とか。
そういうとき、最初素直に頭に浮かぶのは
- もう一個クローンして、そこを望みのブランチでcheckoutしておいて作業する
とかになると思います。私も昔そうでしたw
で、こういうときに git には git worktree
という機能があって、楽に複数ブランチで並行作業しやすいようになっています。
基本、利用するのは以下のコマンド
- git worktree add ディレクトリパス [-b] ブランチ名orコミットハッシュ
- git worktree remove ディレクトリパス
addすると指定のディレクトリパスに指定したブランチの内容がコピーされます。
このコピーされたディレクトリは、元のgit管理下のディレクトリに紐付いているので、コミットとかすると、ちゃんと元のリポジトリでも反映されるようになっています。
git worktree remove
すると、一時作業用に worktree を作っていた場所を消してくれます。そのworktreeで入れたコミットとかは、ちゃんと残ります。
試してみる
とりあえず、試してみます。
まず、作業場所作る
$ mkdir git-worktree-example $ cd git-worktree-example $ git init $ git switch -c master
適当にコミットを積む
$ cat - << EOF > main.go package main import "fmt" func main() { fmt.Println("hello go") } EOF $ git add main.go $ git commit -m "Add main.go"
ドキュメント作成用のブランチつくって、README.md用意
$ git switch -c document $ cat - << EOF > README.md # git worktree example EOF $ git add . $ git commit -m "Add README.md"
これで2つのブランチが現在存在することになりますね。
$ git switch master $ git branch document * master
ここから、コード作りながらドキュメントも並行して更新していこうとおもいます。
で、ここで git worktree
が使えます。ドキュメント用のブランチを別のツリーとして用意。
$ git worktree add ../document document Preparing worktree (checking out 'document') HEAD is now at c89a05f Add README.md
これで、../document
の場所に document ブランチが worktree としてコピーされました。
別のターミナル開いて、移動してみるとよくわかります。
$ cd .. $ ls document git-worktree-example $ cd document $ git branch * document + master
worktree 状態のものは、ブランチ表示のときに +
が表示されます。
$ git worktree list /home/gitpod/tmp/git-worktree-example 41cc600 [master] /home/gitpod/tmp/document c89a05f [document]
これで、2つのブランチを並行で使えます。
まず、masterブランチでコードを作る
$ git branch + document * master $ go mod init gitworktreeexample go: creating new go.mod: module gitworktreeexample $ git add . $ git commit -m "Add go.mod"
もう片方のターミナルに切り替えて、ドキュメント調整
$ git branch * document + master $ echo 'This is example that git worktree command' >> README.md $ git add . $ git commit -m "Update README.md"
これで両方のブランチでコミットが追加されています。worktree同士のコミットはちゃんとリンクされているので
master側でもdocument側でも比較すると差異が見れます。(普通にブランチにコミット追加しているのと同じです。)
# masterブランチ側 $ git log --no-merges document..master --oneline 4e888f4 (HEAD -> master) Add go.mod # document ブランチ側 $ git log --no-merges master..document --oneline bd131ee (HEAD -> document) Update README.md c89a05f Add README.md
てことで、documentブランチをmasterにマージ
$ git merge document Merge made by the 'recursive' strategy. README.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 README.md $ ls README.md go.mod main.go $ git log --oneline 0557454 (HEAD -> master) Merge branch 'document' bd131ee (document) Update README.md 4e888f4 Add go.mod c89a05f Add README.md 41cc600 Add main.go
オケですね。てことで、worktree としてコピーしていた document ブランチの場所はいらなくなったので後片付け。
$ git worktree remove document $ cd .. $ ls git-worktree-example
worktree としてコピーされていたディレクトリが消えてますね。
補足)worktreeが間違って削除されないようにする
worktree としてコピーした場所をgit操作間違って削除されないようにするには
以下のようにします。
$ git worktree lock document
これで、このworktreeの場所はロックされましたので、git worktree remove
してもエラーになります。
ロック解除する場合は以下のようにします。
$ git worktree unlock document
これで削除できるようになります。
過去の記事については、以下のページからご参照下さい。
- いろいろ備忘録日記まとめ
サンプルコードは、以下の場所で公開しています。
- いろいろ備忘録日記サンプルソース置き場