Mercurialのローカルブランチのブランチ名を変更したり削除する


2011年 04月 02日

Mercurialはmercurialでpush済みの名前付きブランチをリネームする三つの方法の記事にあるように一度作成したものはすべて記録するという設計なので、確かにGitのように表題のAPIは提供されていません。

しかし、本質的には単純にファイルを書き換えているだけなので、pushしていないローカルの環境では、ブランチ名を変更したりブランチを削除することは簡単にできます。
MQ拡張によりパッチ化することでこれらを実現していきます。

ブランチを切ったけど名前間違っちゃった

$ hg branch cursed-branch # :p

よくあることです。
人間誰しもtypoします。落ち着きましょう。大丈夫です。Merucurialは寛大なので落ち着いて作業すれば問題ありません。hg branchのhelpを確認すれば何か手があるはずです。

$ hg help branch
hg branch [-fC] [NAME]
...
オプション:

 -f --force  同名既存ブランチが存在する場合でもブランチ作成を実施
 -C --clean  ブランチ名設定を解消し、 親リビジョンのブランチに戻る
    --mq     パッチ管理リポジトリへの操作

が、何度読み返してもブランチ名を変更するオプションは見当たりません。どうしたらいいのでしょうか?

.hg/をのぞいてみましょう。

$ ls .hg/
00changelog.i    branch           dirstate         last-message.txt requires         strip-backup     undo.branch      undo.dirstate
bookmarks        cache            hgrc             patches          store            undo.bookmarks   undo.desc
$ file .hg/branch
.hg/branch: ASCII text
$ cat .hg/branch
cursed-branch

テキストファイルである’branch’ファイルが怪しそうです。中を確認すると、’cursed-branch’という間違ったブランチ名が記述されていますね。
間違ったブランチ名を削除して正しいブランチ名に編集してみましょう。これでブランチ名を変更できます。

$ vim .hg/branch
$ cat .hg/branch
blessed-branch
$ hg branch
blessed-branch

また、作成した直後であればhg update defaultなどして他のブランチ移動すると作成したブランチはなくなるので、再度作成しなおすこともできます。

追記:

hg branch [name]は何度でも可能なので、上記のような手順は踏まなくてよいです。単純に正しい名前で再度branchコマンドを実行するだけでよくなります。

ブランチでコミットをいくつかしたけどブランチ名かえたくなった

ブランチを作った直後であれば、先程のようにファイルを書き換えるだけでよいですが、作ったブランチにコミットを築いてしまうとファイルを変更するだけではうまくいきません。
なぜならそれぞれのコミットにブランチの情報が含まれてしまっているからです。

では、どうやってブランチ名を変更すればいいのでしょうか?
変更したいブランチのコミットをパッチ化してbranchファイルを書きかえることで変更できます。
次のようなコミットを重ねていたとしましょう。

$ hg glog
@  チェンジセット:   4:9775fad84de8
|  ブランチ:         topic
|  タグ:             tip
|  親:               2:e938e86a55c4
|  ユーザ:           yoppi
|  日付:             Sat Mar 26 12:25:10 2011 +0900
|  要約:             added foo
|
| o  チェンジセット:   3:ae8709c095fd
| |  親:               1:a7338d78342d
| |  ユーザ:           yoppi
| |  日付:             Sat Mar 26 12:24:45 2011 +0900
| |  要約:             added hoge
| |
o |  チェンジセット:   2:e938e86a55c4
|/   ブランチ:         topic
|    ユーザ:           yoppi
|    日付:             Sat Mar 26 12:24:30 2011 +0900
|    要約:             added foo.txt
|
o  チェンジセット:   1:a7338d78342d
|  ユーザ:           yoppi
|  日付:             Sat Mar 26 12:23:55 2011 +0900
|  要約:             added hoge
|
o  チェンジセット:   0:1b3a9c75737d
   ユーザ:           yoppi
   日付:             Sat Mar 26 12:23:36 2011 +0900
   要約:             initial commit

リビジョン2でtopicブランチを切って機能を開発しています。
そしてリビジョン4のコミットの時点でこのブランチ名をfeatureにしたほうが良かったと思いブランチ名を変更しようとしましょう。
このtopicブランチのコミットをパッチ化します。

$ hg qimport -r "branch(topic)"
$ hg qseries
2.diff
4.diff

この時点では、最後のコミットであるリビジョン4のパッチが適用されている状態です。
topicブランチの一番元になるコミットに移動しましょう。

$ hg qgoto 2
4.diff の適用解除
適用中の最上位パッチは 2.diff です
$ hg glog
o  チェンジセット:   3:ae8709c095fd
|  タグ:             tip
|  親:               1:a7338d78342d
|  ユーザ:           yoppi
|  日付:             Sat Mar 26 12:24:45 2011 +0900
|  要約:             added hoge
|
| @  チェンジセット:   2:e938e86a55c4
|/   ブランチ:         topic
|    タグ:             2.diff
|    タグ:             qbase
|    タグ:             qtip
|    ユーザ:           yoppi
|    日付:             Sat Mar 26 12:24:30 2011 +0900
|    要約:             added foo.txt
|
o  チェンジセット:   1:a7338d78342d
|  タグ:             qparent
|  ユーザ:           yoppi
|  日付:             Sat Mar 26 12:23:55 2011 +0900
|  要約:             added hoge
|
o  チェンジセット:   0:1b3a9c75737d
   ユーザ:           yoppi
   日付:             Sat Mar 26 12:23:36 2011 +0900
   要約:             initial commit

これで、branchを切った直後と同じ状態です。正確にはパッチが一つ適用されて状態ですが。
つまり、.hg/branchを編集すればブランチ名を変更できるわけです。
ただし、変更しただけでは有効になりません。パッチをrefreshすることで初めて変更が反映されます。

$ hg qrefresh
$ hg glog
@  チェンジセット:   3:16e6dd2ef5bb
|  ブランチ:         feature
|  タグ:             2.diff
|  タグ:             qbase
|  タグ:             qtip
|  タグ:             tip
|  親:               1:a7338d78342d
|  ユーザ:           yoppi
|  日付:             Sat Mar 26 12:24:30 2011 +0900
|  要約:             added foo.txt
|
| o  チェンジセット:   2:ae8709c095fd
|/   ユーザ:           yoppi
|    日付:             Sat Mar 26 12:24:45 2011 +0900
|    要約:             added hoge
|
o  チェンジセット:   1:a7338d78342d
|  タグ:             qparent
|  ユーザ:           yoppi
|  日付:             Sat Mar 26 12:23:55 2011 +0900
|  要約:             added hoge
|
o  チェンジセット:   0:1b3a9c75737d
   ユーザ:           yoppi
   日付:             Sat Mar 26 12:23:36 2011 +0900
   要約:             initial commit

これでブランチ名をtopicからfeatureへ変更できました。あとは、残りのパッチも適用してパッチの編集を終了しましょう。

$ hg qpush 2.diff
$ hg glog
@  チェンジセット:   4:999a9a5dcf51
|  ブランチ:         feature
|  タグ:             4.diff
|  タグ:             qtip
|  タグ:             tip
|  ユーザ:           yoppi
|  日付:             Sat Mar 26 12:25:10 2011 +0900
|  要約:             added foo
|
o  チェンジセット:   3:16e6dd2ef5bb
|  ブランチ:         feature
|  タグ:             2.diff
|  タグ:             qbase
|  親:               1:a7338d78342d
|  ユーザ:           yoppi
|  日付:             Sat Mar 26 12:24:30 2011 +0900
|  要約:             added foo.txt
|
| o  チェンジセット:   2:ae8709c095fd
|/   ユーザ:           yoppi
|    日付:             Sat Mar 26 12:24:45 2011 +0900
|    要約:             added hoge
|
o  チェンジセット:   1:a7338d78342d
|  タグ:             qparent
|  ユーザ:           yoppi
|  日付:             Sat Mar 26 12:23:55 2011 +0900
|  要約:             added hoge
|
o  チェンジセット:   0:1b3a9c75737d
   ユーザ:           yoppi
   日付:             Sat Mar 26 12:23:36 2011 +0900
   要約:             initial commit
$ hg qfinish -a
$ hg glog
@  チェンジセット:   4:999a9a5dcf51
|  ブランチ:         feature
|  タグ:             tip
|  ユーザ:           yoppi
|  日付:             Sat Mar 26 12:25:10 2011 +0900
|  要約:             added foo
|
o  チェンジセット:   3:16e6dd2ef5bb
|  ブランチ:         feature
|  親:               1:a7338d78342d
|  ユーザ:           yoppi
|  日付:             Sat Mar 26 12:24:30 2011 +0900
|  要約:             added foo.txt
|
| o  チェンジセット:   2:ae8709c095fd
|/   ユーザ:           yoppi
|    日付:             Sat Mar 26 12:24:45 2011 +0900
|    要約:             added hoge
|
o  チェンジセット:   1:a7338d78342d
|  ユーザ:           yoppi
|  日付:             Sat Mar 26 12:23:55 2011 +0900
|  要約:             added hoge
|
o  チェンジセット:   0:1b3a9c75737d
   ユーザ:           yoppi
   日付:             Sat Mar 26 12:23:36 2011 +0900
   要約:             initial commit

ブランチのルートのチェンジセットでブランチ名を変更することで、それ以降のチェンジセットも一度に変更されることになります。

そもそもこのブランチ削除したい

ブランチを切ったけどいらなくなってしまうことはしばしばあります。
削除せずに残しておくと、pushする時点で--new-branchを付けろと警告がでて、泣く泣く-bオプションでpushするブランチを指定するか、もしくは、closeしてpushするという方針しかとれないのでしょうか?

ローカルのブランチは削除できます。
ここでも、MQ拡張を使って削除したいブランチのコミットをパッチ化して実現します。

さきほどの’ブランチでコミットをいくつかしたけどブランチ名かえたくなった’で例としてとりあげているtopicブランチを削除したいとしましょう。
qimportでパッチ化したところまで手順は同じです。

$ hg glog
@  チェンジセット:   4:bd0a69adeed9
|  ブランチ:         topic
|  タグ:             tip
|  親:               2:8e372b859477
|  ユーザ:           yoppi
|  日付:             Sat Apr 02 21:55:25 2011 +0900
|  要約:             modified foo
|
| o  チェンジセット:   3:65d7581c136f
| |  親:               1:a65859ffe351
| |  ユーザ:           yoppi
| |  日付:             Sat Apr 02 21:55:06 2011 +0900
| |  要約:             modified hoge
| |
o |  チェンジセット:   2:8e372b859477
|/   ブランチ:         topic
|    ユーザ:           yoppi
|    日付:             Sat Apr 02 21:54:48 2011 +0900
|    要約:             added foo.txt
|
o  チェンジセット:   1:a65859ffe351
|  ユーザ:           yoppi
|  日付:             Sat Apr 02 21:54:19 2011 +0900
|  要約:             modified hoge
|
o  チェンジセット:   0:014088fbb922
   ユーザ:           yoppi
   日付:             Sat Apr 02 21:53:53 2011 +0900
   要約:             initial commit
$ hg qimport -r 2 -r 4

この時点では、作成したパッチが全てパッチキュー上に適用されています。
これらのパッチを全て取り除きましょう。

$ hg qpop -a
4.diff の適用解除
2.diff の適用解除
全てのパッチの適用が解除されました
$ hg glog
o  チェンジセット:   2:65d7581c136f
|  タグ:             tip
|  ユーザ:           yoppi
|  日付:             Sat Apr 02 21:55:06 2011 +0900
|  要約:             modified hoge
|
@  チェンジセット:   1:a65859ffe351
|  ユーザ:           yoppi
|  日付:             Sat Apr 02 21:54:19 2011 +0900
|  要約:             modified hoge
|
o  チェンジセット:   0:014088fbb922
   ユーザ:           yoppi
   日付:             Sat Apr 02 21:53:53 2011 +0900
   要約:             initial commit

歴史からコミットが失くなったと同時にブランチもなくなったことがわかります。
さて、最後にパッチが必要ならtransplantで他のブランチに移植したり、必要ないなら削除しましょう。

$ hg qseries | while read patch
do
  hg qdelete $patch
done

これで完全にブランチを削除したことになります。