Hgに慣れてしまった人のためのGitメモ

基本的な開発スタイルはHgでもGitでもそう変わらないと思うので、考え方を完全に持ち込む、というかHgに慣れたんだけどみんなGitHubにいるからGitも使わざるを得ないとかそういう人(というか私)のための備忘録として。単にどのVCSがどう違うかは大体知っているんだけど、いざやってみようとするとコケまくる人たちのために。。。
ソースはgittutorial(7)

結論からいうとこの表を書きたかった。

やりたいこと mercurial git
レポジトリ作成 hg init (みんなでさわる場合)git init --bare --shared
レポジトリクローン hg clone git clone
変更内容のコミット hg ci git commit -a *1
変更状態 hg diff git diff
revert hg revert -all . git reset --hard
ブランチ作成 hg branch git branch
ブランチ一覧 hg branches git branch
ブランチ移動 hg up -C git checkout
マージ hg merge git merge
ブランチ削除 なんかあった気がする git branch -d *2
向こうのと同期 hg pull -u git pull -a

注意点…pull/pushはちょっと挙動そのものが違うようだ。
ホントはこの本をちゃんと読んだ方がよいのだが、今手元にないのでメモ。

入門Git

入門Git

ユーザ情報の登録

Hgだと~/.hgrcに

[ui]
username = UENISHI Kota

なんて書いたりしますが、gitでは

$ git config --global user.name "Your Name Comes Here"
$ git config --global user.email you@yourdomain.example.com

とやります。~/.gitconfigに

[user]
        name = UENISHI Kota
        emacsl = kuenishi+github@spam.com
        email = kuenishi+github@spam.com

なんて書くのでもいいみたいです。よく分からないけど不思議ですね。

新規プロジェクトのインポート

これは同じなので簡単。git initでOK。

ファイル追加と差分の反映

Hgだと一旦addしたファイルは勝手に管理してもらえるけど、gitだと変更するために差分をaddで登録しないといけない。Hgと同じやり方でコミットしたい場合は

 $ git commit -a

とやると hg ci と同じ意味になる。hg ci -Aとは意味が違うので注意。-mオプションは同じなのでエディタを見たくない場合は-mをつける。

ブランチ

ブランチ作成は同じ。ブランチ一覧はmercurialだと hg branches になるけど

  $ git branch

で十分。ブランチ削除のときは git branch -d branchnameで、ブランチ間を移動するときは、hgだと hg up -C branchnameなんだけど、

$ git checkout branchname

でできる。checkoutとか言葉が混乱してややこしいですね。

clone/push/pull

mercurialと同じ、などと思ってはいけない。そんなあなたは考えが甘い。mercurialと同じだと思って clone して、コードを修正して、 commit して、変更内容をpushしたいと考えるだろう。そして、必ず次のような警告が表示されることだろう。

shuna:hoge kuenishi$ git commit
[master a425236] refactor
1 files changed, 0 insertions(+), 5 deletions(-)
shuna:hoge kuenishi$ git push
Counting objects: 5, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 294 bytes, done.
Total 3 (delta 2), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
warning: updating the current branch
warning: Updating the currently checked out branch may cause confusion,
warning: as the index and work tree do not reflect changes that are in HEAD.
warning: As a result, you may see the changes you just pushed into it
warning: reverted when you run 'git diff' over there, and you may want
warning: to run 'git reset --hard' before starting to work to recover.
warning:
warning: You can set 'receive.denyCurrentBranch' configuration variable to
warning: 'refuse' in the remote repository to forbid pushing into its
warning: current branch.
warning: To allow pushing into the current branch, you can set it to 'ignore';
warning: but this is not recommended unless you arranged to update its work
warning: tree to match what you pushed in some other way.
warning:
warning: To squelch this message, you can set it to 'warn'.
warning:
warning: Note that the default will change in a future version of git
warning: to refuse updating the current branch unless you have the
warning: configuration variable set to either 'ignore' or 'warn'.
To /Users/kuenishi/Dropbox/dev/hoge
2038374..a425236 master -> master
shuna:hoge kuenishi$

インデックスとかwork treeとかがHEADにある変更を反映していないと。refuseとかwarnとかをグローバルに?設定するのはとても気持ち悪いので、もうちょっと調べてみた。
で、なんのこっちゃらと思ったら、push先がbareになっていないのが悪いらしい。

If you're creating a repo that people are going to want to push to, then you should create it using git init --bare (and git init --bare --shared if several user accounts need access to it), or git clone --bare if you're creating it by cloning an existing repo.

つまり、みんなでpushするレポジトリがあったら、そいつをbareなものにしておけば、bareなものに対するpushにはこの気持ち悪い警告は出ない。gitの方がmercurialよりも複雑だなぁという印象*3

また、書き上げた後に気づいたのだけども、gitとmercurialでpush/pullの挙動が違うところがもう一箇所ある。mercurialではpush/pullをすると、レポジトリの状態を全て同期してくれる。つまり、片方のchangesetが他方に全て渡されるのだ。なので当然、全てのブランチが同期できる。しかしgitでは、push/pullの際には、明示的または暗黙に特定のブランチが指定されることになってしまうらしく、何も指定しなければmasterらしい。他のブランチは何も同期されない。タグがどうなるかはよく分からない。
これは理由を考えたら当然かもしれなくて、Linux Kernelのようにブランチが雲霞の如く世界中に生まれてしまった場合に、push/pullで全部changesetを取り込んでいては、レポジトリ内のchangesetの増大が大きすぎるのだろう(と勝手に想像している)。

まとめ

やりたいこと mercurial git
レポジトリ作成 hg init (みんなでさわる場合)git init --bare --shared
レポジトリクローン hg clone git clone
変更内容のコミット hg ci git commit -a *4
変更状態 hg diff git diff
revert hg revert -all . git reset --hard
ブランチ作成 hg branch git branch
ブランチ一覧 hg branches git branch
ブランチ移動 hg up -C git checkout
マージ hg merge git merge
ブランチ削除 なんかあった気がする git branch -d *5
向こうのと同期 hg pull -u git pull -a

だいたいこんな感じかなぁ。あとは衝突状態の解決方法が残ってるか。。。間違いがあったらご指摘いただきたいです。

入門Mercurial Linux/Windows対応

入門Mercurial Linux/Windows対応

*1:-aをつけないときは登録済みのファイルでも事前にaddしておくこと

*2:-Dもいけるらしい

*3:この日記は主観と誤解にも

*4:-aをつけないときは登録済みのファイルでも事前にaddしておくこと

*5:-Dもいけるらしい