ブランチをディレクトリに割り当つつGit管理対象外ファイルも同期するgit worksyncを作った

カテゴリ: git

バージョン管理ソフトGitにはgit worktreeコマンドがあり、これを使うとディレクトリ単位で作業ブランチを切り替られます。

git switchコマンドでブランチを切り替えてしまうと、今いるディレクトリの内容が別のブランチの内容に切り替わります。これでは複数のブランチのソースコードを実行して結果を比較したい時などに不便です。ブランチごとにディレクトリが違えばそういった比較が簡単ですね。

ただし、git worktreeを使ってブランチを切り替えると、新しいディレクトリにはGitでバージョン管理したものしか存在しません。ということは、

  • Pythonのvenvで作った仮想環境がなくてパッケージを読み込めない
  • バージョン管理するにはデカ過ぎるdataディレクトリをコードが正しく参照できない

といった事態が発生します。困りますね。

そこでgit worksyncコマンドを作りました。従来のgit worktreeコマンドと比較してみましょう。

今のところはmacOS、Linuxで動作します。将来的にはGo言語あたりで書き直してWindowsにも対応したいなと思います。

レポジトリ初期化

まず、適当なディレクトリで

  1. レポジトリ初期化
  2. Python用仮想環境を生成。仮想環境はGit管理対象外にする。
  3. 仮想環境起動にtomlパッケージをインストールしバージョンを確認

という手順を踏んでみます。

# 1. レポジトリ初期化
mkdir -p repo
cd repo
git init --quiet

# 2. Python用仮想環境を生成。Git管理対象外にする。
python3 -m venv .venv
echo .venv > .gitignore
git add .gitignore
git commit --quiet -m "ignore .venv"

# 3. 仮想環境起動にtomlパッケージをインストールしバージョンを確認
source .venv/bin/activate
pip3 --quiet install "toml==0.10.2"
python -c "import toml; print('toml', toml.__version__)"
## toml 0.10.2

無事にtoml 0.10.2をインストールできました。

ブランチを別ディレクトリに割り当て

git worktree

次にgit worktreeコマンドを使って、

  • mainブランチから派生したworktree1ブランチを作成
  • worktree1ブランチを同名のディレクトリに展開

してみましょう。これにはgit worktree addコマンドを使い、引数には

  • ディレクトリのパス
  • ブランチ名

の順に指定します。順番を覚えられないという人は、worktreeという用語がGitでは作業ディレクトリのことを指すことを覚えておきましょう。コマンドが重視しているしているもの、すなわちディレクトリのパスが先です。

worktreeを追加したら、そのディレクトリの中身を見てみます。 lsコマンドに-Aオプションをつけて、.から始まるファイルやディレクトリも表示しましょう。

git branch worktree1 --quiet
git worktree add ../worktree1 worktree1 --quiet
ls -A ../worktree1
## .git
## .gitignore

.gitディレクトリと.gitignoerファイルが見えますね。しかし、.venvが足りません。これでは先程インストールしたtomlパッケージを利用できずに不便ですね。

git worksync

同じことをgit worksyncコマンドでやってみましょう。

インストール

インストールするには、私のレポジトリからgit-worksyncファイルをダウンロードして、パスの通っている場所に保存し、実行権限を付与します。

https://github.com/atusy/git-worksync

コマンドでやるなら↓のような感じ。 INSTALL_DIRのところは適当に書き換えてください。

INSTALL_DIR="/usr/local/bin"
sudo wget -q https://raw.githubusercontent.com/atusy/git-worksync/main/git-worksync \
  -O "${INSTALL_DIR}/git-worksync"
sudo chmod +x "${INSTALL_DIR}/git-worksync"

インストールに成功すると、git worksyncで コマンドのヘルプを表示できます。ファイル名はgit-worksyncだったのにgit worksyncでいいの?と思う方がいるかもしれませんが、 gitはサブコマンドを与えた時にハイフン区切りのコマンドを自動で探しにいってくれます。今回のように拡張コマンドを作成するときはgit-*という名前にすると良い、覚えておきましょう。

git worksync
## `git worktree add` with synchronizing untracked/ignored resources.
## Additional options are below.
## 
##   -i=, --ignored=     The applying method for the ignored (default: symbolic-link)
##                       See METHODS section for the details.
##   -u=, --untracked=   The applying method for the ignored (default: symbolic-link)
##                       See METHODS section for the details.
##   -s=, --symbolic=, --symbolic-link=<path>
##                       Create symbolic link of a path.
##                       Specify multiple times for the multiple paths.
## ...省略

ふむふむ、untracked(未追跡)やignored(無視)なファイルなども良い感じにworktree間で同期してくれるみたいですね。

実行

試してみましょう。 git worktree addコマンド由来の引数はすべてそのまま使えます。今度はworktree2ブランチをworktree2ディレクトリに展開します。で、展開した先のディレクトリを表示してみます。先のコードとほとんど変わりませんね。

cd ../repo
git branch worktree2 --quiet
git worksync ../worktree2 worktree2 --quiet
cd ../worktree2
ls -A
## .git
## .gitignore
## .venv

お、今度は.venvもいます。ということは仮想環境を起動すればtomlパッケージを読み込めるはず。

source .venv/bin/activate
python3 -c "import toml; print(toml.__version__)"
## 0.10.2

読み込めました!万歳!!

おわりに

是非、使ってみて、バグ報告や機能の要望、★付けなどしてください。

https://github.com/atusy/git-worksync

もう少しオプションも紹介しようかと思いましたが、ヘルプ見てください。同期方法を色々と調整できます。

丁度良い頃合いに夜半を過ぎたので、そろそろ寝るとします。この記事を書く間にバグに2つも気付けました。書いてよかったです。