gitのdetatched head運用でmainの誤commitにサヨナラ
そもそもブランチを置かなければ誤commitを防げる
git管理下のディレクトリにいるとき、誤ってmainブランチで作業してしまう事故を防ぎたいと思っていました。以下の記事を読んだことがあり、ローカルにmainを置かない運用があることは知っていましたが、なかなか踏み切れずにいました。
先日、筆者が使用しているGit支援ツールのghqにpartial clone機能が追加されました。これを機に、前述の運用方法に取り組んでみました。
しばらく使ってみて、いい感じにフローが固まってきたので本記事にまとめます。
使用しているエイリアス
git remote-head
remote HEADを取得します。ヘルパーとして定義しているだけで、直接実行することは基本的にありません。
remote-head = symbolic-ref refs/remotes/origin/HEAD
$ git remote-head
refs/remotes/origin/master
git default-branch
remote-headの出力から、デフォルトブランチを取得します。ヘルパーとして定義しているだけで、直接実行することは基本的にありません。
default-branch = !git remote-head | sed 's!.*/!!'
$ git remote-head
master
git refresh
remoteの最新の状態を取ってきます。これも直接実行することは基本的にありません。
refresh = fetch --all --prune
git dead
本記事のメインディッシュ。ローカルを最新のdetached headにするコマンドです。detached-headだと長いなと思ったのでdeadにしました。headを取ってくるのでbeheadにしようかと思いましたが言葉が強すぎるのでやめました。deadならまぁ良いかなと思いましたがペアプロのときにはビビられました。
dead = !git refresh --quiet && git switch -d $(git remote-head)
$ git dead
HEAD is now at 1234567 initial commit
git single
現在チェックアウトしているブランチ以外を削除します。使用注意。
single = !git branch | grep -v HEAD | xargs git branch --no-run-if-empty -d
git body
detached headに新しい体を生やすコマンドです。無引数でも実行できるように実行ファイルとして作りました。パスの通ったディレクトリに配置してください。
#!/usr/bin/env bash
git dead && git switch --create "${1:-"wip-$RANDOM"}"
引数を渡すと、一度deadしてから新しいブランチを作ります。
$ git body a
HEAD is now at 1234567 initial commit
Switched to a new branch 'a'
無引数で実行すると、wip-{ランダムな数値}
のブランチが作られます。
$ git body
HEAD is now at 1234567 initial commit
Switched to a new branch 'wip-1234'
fuse
デフォルトブランチの変更を取り込みます。たまに使います。
fuse = !git refresh --quiet && git merge $(git remote-head)
使用しているシェル関数
以下のclone
を定義して使っています。途中でcd
したいのでシェル関数として定義しています。
内部では以下を行います。
- 冒頭で述べた
ghq get
のpartial cloneを行う - そのディレクトリに移動する
- remote-headに移動し、既存のブランチを処理する
clone() {
has 'ghq' || return 1
ghq get --partial blobless "$1"
local target
target=$(echo "$1" | sed -r 's;https?://[^/]+|\.git$;;')
local dir
dir=$(ghq list | grep --color=never --fixed-strings "$target")
[ -n "$dir" ] && cd "$(ghq root)/$dir" || return
git dead
git single
}
作業フロー
まず、clone {repositoryのURL}
を実行してプロジェクトをローカルにクローンします。
これで、detached headのクローンとディレクトリ移動が自動で行われます。
$ clone https://github.com/vim-jp/ekiden
clone https://github.com/vim-jp/ekiden -> /Users/kawarimidoll/ghq/github.com/vim-jp/ekiden
git clone --recursive --filter=blob:none https://github.com/vim-jp/ekiden /Users/kawarimidoll/ghq/github.com/vim-jp/ekiden
Cloning into '/Users/kawarimidoll/ghq/github.com/vim-jp/ekiden'...
// 中略…
HEAD is now at 94a45b5 Update content.json by #1037
Deleted branch main (was 94a45b5).
作業を開始するときはgit body
でブランチを作り、commitを積んでいきます。
pushは以下の記事のものを使っています。
作業後はgit dead
でdetached headに戻ります。なお、git body
は内部でgit dead
を実行するので、すぐに別の作業に入る場合は直接git body
を実行します。
所感
最初はローカルにmainを置かないことにちょっと不安がありましたが、想像以上に簡単に移行できて普通に使うことができています。単に事故が減って快適になりました。
また、外部OSSの確認だけしたい場合なども、partial cloneにすればデータ量を削減できて良い感じです。
本記事を書いている途中で以下の記事が公開されてシンパシーを感じました。
Discussion