2018年以降の記事はGitHub Pagesに移行しました

Git道場#1に参加しました と、pullとpull --rebase備忘録 #gitdojo

概要

対象者が

merge / rebaseにちょっと自信がない
conflictが怖い

まさにそんな状況だったので、打破するために参加しました。

心「Git総論、心構え」

Gitにはリモートリポジトリ、ローカルリポジトリがある
Gitは頑健である
Gitは時間的な変遷を管理する
  • 昨日のこの時間何してたかな?
  • testブランチを消してもコミットは残っている
  • 作業履歴が残っているのでreflogで参照できる
    • reflogで救済できる期間は90日以内、またはgit gc が実行されるまで
他(質疑応答の回答など)
  • ブランチを削除してもコミットは残っている。git branch -D
  • GitHub重い……
    • イントラにbareリポジトリ用意して毎朝GitHubから持ってくるなど

技「本日の課題、テクニックの解説」

課題
  • 今日はチームで一つのファイルを編集
  • pull, rebaseを怖がらずに
    • コンフリクトが起こった時の対処法を学ぶ
  • pullとの違いを理解する
    • ≒mergeとrebaseの違いでいいのかな?
ルール
  • 1-10までのNumbersファイルの後ろに記号を追加削除していく
  • コミットメッセージだけで語る
  • チーム間ではコミットメッセージのみを介して会話する
  • 「好きにやっていいので、ぐちゃぐちゃを楽しんでください」
  • 今回は@myfinderさん、@umiyoshさん、@nysalorさんと黙々と。

...みっちり数時間。

  • (グラフが)とても…カオスです
実習後
  • 再び講義。その後、同じNumbersファイルを(まっさらにしてから)やり直し
  • さっきはpullでリポジトリから取得していたと思うけど、今回は必ずpull --rebaseする事
    • コミットを直列にならべる事が目的
git pull --rebase
git add 
git rebase --continue
  • rebaseとは
    • (ざっくりと)コミットをかぶせる
      • リモートの最新コミットにローカルの共通コミットから先のコミットが被せられる(onto)
      • まっすぐになるので誰が何コミットしたかわかりやすい
      • コンフリクトした場合、無名ブランチがチェックアウトされた状態になる
  • 一方でmergeは
    • コミットを統合する(基本的にマージコミットが生まれる=見た感じ複雑)
      • リモートに新しいコミットがあると失敗する
      • 変更履歴が残るので、後で調査がしやすい
  • どちらがいいかは状況次第
    • 他人がコミットしている事を意識する
    • ローカルブランチでは作業履歴を残すためmergeの方がいいかも
アンチパターン
  • git add .
    • 全部追加するとかありえない
    • statusで確認してね
  • 無名ブランチでコミット
    • 困った場合は --abortで中断する
    • git rebase --skipでスキップする事ができる
  • git config user.nameとか設定して

という感じで13:00から18:00までadd,commit,push,pull(pull --rebase), コンフリクト解消を繰り返していました。個人的にはチューターの方にも丁寧に説明していただき、rebaseもかなり腹に落ちた感じで参加してよかった。後はこの感覚があるうちに本で復習してみる。

会場を提供して下さったフューチャーアーキテクトさんとGit道場師範の皆さんありがとうございました。門下生の皆さんお疲れ様でした。(終わった時にはほぼ全員ぐったりしていた)

pull、pull --rebaseまとめ

糞長いので先にまとめ。

pull

以下のふたつのリポジトリがあったとき

A
(FIRST)---(NUMBERADD)
          origin/master
B
(FIRST)---(hello world)---(hello git)---(bye svn)
origin/master

Bがpullするとこういう歴史が出来る。

          (NUMBERADD)----------------------------(merge)
         /                                       /
(FIRST)---(hello world)---(hello git)---(bye svn)
pull --rebase

同じく。

A
(FIRST)---(NUMBERADD)
          origin/master
B
(FIRST)---(hello world)---(hello git)---(bye svn)
origin/master

Bがpull --rebaseするとこういう歴史ができる。

          (NUMBERADD)---(hello world merge)
         /              no branch
(FIRST)---(hello world)---(hello git)---(bye svn)
          (NUMBERADD)---(hello world merge)---(hello git merge)
         /                                    no branch
(FIRST)---(hello world)---(hello git)---(bye svn)
          (NUMBERADD)---(hello world merge)---(hello git merge)---(bye svn merge)
         /                                                        no branch
(FIRST)---(hello world)---(hello git)---(bye svn)
          (NUMBERADD)---(hello world merge)---(hello git merge)---(bye svn merge)
         /                                                        master
(FIRST)--

整形して、

(FIRST)---(NUMBERADD)---(hello world merge)---(hello git merge)---(bye svn merge)
                                                                  master

以下に実際にやってみたログ。

pull

dojo1さんとdojo2さんでdojo.gitリポジトリに書き込んでいく。空のnumファイルをfirst commit後……。

dojo1

dojo1さんが準備。

$ cat num
 1
 2
 3
 4
 5
$ git coa -m "1から5までnumberふり"
[master 27734d8] 1から5までnumberふり
 1 files changed, 5 insertions(+), 0 deletions(-)

グラフはこんな感じ。

$ git g
* 27734d8 2012-04-22 kk_Ataka  (HEAD, master) 1から5までnumberふり
* 9f20959 2012-04-22 kk_Ataka  (origin/master) first commit
dojo2

dojo2さん。cloneしたときはこんなグラフ。

$ git g
* 9f20959 2012-04-22 kk_Ataka  (HEAD, origin/master, origin/HEAD) first commit

ここに27734d8をプルする前にローカルで色々育てる。

$ vim num
$ git coa -m "hello world追加"
$ vim num
$ git coa -m "hello git追加"
$ vim num
$ git coa -m "bye svn追加"

グラフはこう。

$ git g
* 817be9c 2012-04-22 kk_Ataka  (HEAD, master) bye svn追加
* 66d89ab 2012-04-22 kk_Ataka  hello git追加
* cb71f84 2012-04-22 kk_Ataka  hello world追加
* 9f20959 2012-04-22 kk_Ataka  (origin/master, origin/HEAD) first commit

この状態でプルすると…

$ git pull
remote: Counting objects: 5, done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From /home/kk_Ataka/github/dojo
   9f20959..27734d8  master     -> origin/master
Auto-merging num
CONFLICT (content): Merge conflict in num
Automatic merge failed; fix conflicts and then commit the result.
[22:54:45 kk_Ataka@www15203u (master *+|MERGING<>)~/github/dojo2]
$ git g
* 817be9c 2012-04-22 kk_Ataka  (HEAD, dojo2/master, master) bye svn追加
* 66d89ab 2012-04-22 kk_Ataka  hello git追加
* cb71f84 2012-04-22 kk_Ataka  hello world追加
| * 27734d8 2012-04-22 kk_Ataka  (origin/master, origin/HEAD) 1から5までnumberふり
|/
* 9f20959 2012-04-22 kk_Ataka  first commit

first commit以降のブランチがdojo1さんが編集したものと自分が育てたものに分岐。これを解消してやり、コミット。

 git coa -m "コンフリクトを解消し、メッセージにnumberをふった"
[master d21349d] コンフリクトを解消し、メッセージにnumberをふった
[22:57:18 kk_Ataka@www15203u (master>)~/github/dojo2]
$ git g
*   d21349d 2012-04-22 kk_Ataka  (HEAD, master) コンフリクトを解消し、メッセージにnumberをふ
|\
* | 817be9c 2012-04-22 kk_Ataka  (dojo2/master) bye svn追加
* | 66d89ab 2012-04-22 kk_Ataka  hello git追加
* | cb71f84 2012-04-22 kk_Ataka  hello world追加
| * 27734d8 2012-04-22 kk_Ataka  (origin/master, origin/HEAD) 1から5までnumberふり
|/
* 9f20959 2012-04-22 kk_Ataka  first commit
dojo1に戻り

dojo1に戻りプルして一行追加しコミット&プッシュ。

$ git pull
remote: Counting objects: 16, done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 12 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (12/12), done.
From /home/kk_Ataka/github/./dojo
   27734d8..d21349d  master     -> origin/master
Updating 27734d8..d21349d
Fast-forward
 num |    6 +++---
 1 files changed, 3 insertions(+), 3 deletions(-)
$ git g
*   d21349d 2012-04-22 kk_Ataka  (HEAD, origin/master, master) コンフリクトを解消し、メッセ
|\
* | 817be9c 2012-04-22 kk_Ataka  bye svn追加
* | 66d89ab 2012-04-22 kk_Ataka  hello git追加
* | cb71f84 2012-04-22 kk_Ataka  hello world追加
| * 27734d8 2012-04-22 kk_Ataka  (dojo1/master) 1から5までnumberふり
|/
* 9f20959 2012-04-22 kk_Ataka  first commit
$ vim num
$ git coa -m "number6を追加"
[master eeeab6f] number6を追加
 1 files changed, 1 insertions(+), 0 deletions(-)
$ git push
Counting objects: 5, done.
Writing objects: 100% (3/3), 294 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
To /home/kk_Ataka/github/./dojo.git/
   d21349d..eeeab6f  master -> master
同じ頃dojo2では

その隙にdojo2は少しローカルで育て中。(ただし、今度はコンフリクトさせないように)

$ vi num
$ git coa -m "bye svnは削除"

そしてさっきのdojo1の更新をプル。

$ git pull
remote: Counting objects: 5, done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From /home/kk_Ataka/github/dojo
   d21349d..eeeab6f  master     -> origin/master
Auto-merging num
Merge made by recursive.
 num |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)
[23:06:42 kk_Ataka@www15203u (master>)~/github/dojo2]
$ git g
*   5ba127c 2012-04-22 kk_Ataka  (HEAD, master) Merge branch 'master' of /home/kk_Ataka/gith
|\
* | 252d4b5 2012-04-22 kk_Ataka  bye svnは削除
| * eeeab6f 2012-04-22 kk_Ataka  (origin/master, origin/HEAD) number6を追加
|/
*   d21349d 2012-04-22 kk_Ataka  コンフリクトを解消し、メッセージにnumberをふった
|\
* | 817be9c 2012-04-22 kk_Ataka  (dojo2/master) bye svn追加
* | 66d89ab 2012-04-22 kk_Ataka  hello git追加
* | cb71f84 2012-04-22 kk_Ataka  hello world追加
| * 27734d8 2012-04-22 kk_Ataka  1から5までnumberふり
|/
* 9f20959 2012-04-22 kk_Ataka  first commit

こうなる。なので基本的にプルはマージの履歴が残る。

pull --rebase

pull --rebaseではどうか。下記の状態まで戻る。

dojo1
$ git g
* 49622c6 2012-04-22 kk_Ataka  (HEAD, origin/master, master) 1から5までnumberふり
* ec12cb5 2012-04-22 kk_Ataka  first commit
dojo2
$ git  g
* 3a940bc 2012-04-22 kk_Ataka  (HEAD, master) bye svn追加
* 763cbaa 2012-04-22 kk_Ataka  hello git追加
* d6c6c98 2012-04-22 kk_Ataka  hello world追加
* ec12cb5 2012-04-22 kk_Ataka  (origin/master, origin/HEAD) first commit

ここでpull --rebaseを使う。

$ git pull --rebase
remote: Counting objects: 5, done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From /home/kk_Ataka/github/dojo
   ec12cb5..49622c6  master     -> origin/master
First, rewinding head to replay your work on top of it...
Applying: hello world追加
Using index info to reconstruct a base tree...
Falling back to patching base and 3-way merge...
Auto-merging num
CONFLICT (content): Merge conflict in num
Failed to merge in the changes.
Patch failed at 0001 hello world追加

When you have resolved this problem run "git rebase --continue".
If you would prefer to skip this patch, instead run "git rebase --skip".
To restore the original branch and stop rebasing run "git rebase --abort".

盛大にコンフリクトする。改めて見るとrebase --continue, --skip, --abortを使いなさいって書いてあるな。

で、今のrebaseが始まったとき、ブランチはどこにいるのかというと……。

$ git br
* (no branch) 49622c6 1から5までnumberふり
  master      3a940bc [ahead 3, behind 1] bye svn追加

ここ。無名ブランチ。この496...はどこかというと……。

$ git g
* 3a940bc 2012-04-22 kk_Ataka  (master) bye svn追加
* 763cbaa 2012-04-22 kk_Ataka  hello git追加
* d6c6c98 2012-04-22 kk_Ataka  hello world追加
| * 49622c6 2012-04-22 kk_Ataka  (HEAD, origin/master, origin/HEAD) 1から5までnumberふり
|/
* ec12cb5 2012-04-22 kk_Ataka  first commit

dojo1さんがプッシュしていた所。なにはともあれまず、リポジトリにプッシュされていたものを優先する。このdojo1さんの変更に対して、d6c6c98、763cbaa、3a940bcとひとつずつrebaseしていく。問題を解決したらrebase --continue。無名ブランチにいるのでコミットしちゃダメ。

$ vim num
競合解消…
$ git add num
$ git rebase --continue
Applying: hello world追加
Applying: hello git追加
Using index info to reconstruct a base tree...
Falling back to patching base and 3-way merge...
Auto-merging num
CONFLICT (content): Merge conflict in num
Failed to merge in the changes.
Patch failed at 0002 hello git追加

ひとつ解消したらふたつ目でまたコンフリクトした。今どこにいるかというと……。

$ git br
* (no branch) 781c723 hello world追加
  master      3a940bc [ahead 3, behind 1] bye svn追加
$ git g
* 781c723 2012-04-22 kk_Ataka  (HEAD) hello world追加
| * 3a940bc 2012-04-22 kk_Ataka  (master) bye svn追加
| * 763cbaa 2012-04-22 kk_Ataka  hello git追加
| * d6c6c98 2012-04-22 kk_Ataka  hello world追加
* | 49622c6 2012-04-22 kk_Ataka  (origin/master, origin/HEAD) 1から5までnumberふり
|/
* ec12cb5 2012-04-22 kk_Ataka  first commit

コンフリクトを解消しつつ無名ブランチで突き進んでいる。

$ vim num
競合解消…
$ git add num
$ git rebase --continue
Applying: hello git追加
Applying: bye svn追加
Using index info to reconstruct a base tree...
Falling back to patching base and 3-way merge...
Auto-merging num
CONFLICT (content): Merge conflict in num
Failed to merge in the changes.
Patch failed at 0003 bye svn追加

どんどん無名ブランチを進めていく。

$ git br
* (no branch) 8fb0d5e hello git追加
  master      3a940bc [ahead 3, behind 1] bye svn追加
$ git g
* 8fb0d5e 2012-04-22 kk_Ataka  (HEAD) hello git追加
* 781c723 2012-04-22 kk_Ataka  hello world追加
| * 3a940bc 2012-04-22 kk_Ataka  (master) bye svn追加
| * 763cbaa 2012-04-22 kk_Ataka  hello git追加
| * d6c6c98 2012-04-22 kk_Ataka  hello world追加
* | 49622c6 2012-04-22 kk_Ataka  (origin/master, origin/HEAD) 1から5までnumberふり
|/
* ec12cb5 2012-04-22 kk_Ataka  first commit

解消。

$ vim num
競合解消…
$ git add num
$ git rebase --continue
Applying: bye svn追加

これで全てのコミットの変更がdojo1さんの変更の上に適用された。で、今どこにいるかというと。

$ git br
* master 96897da [ahead 3] bye svn追加
$ git g
* 96897da 2012-04-22 kk_Ataka  (HEAD, master) bye svn追加
* 8fb0d5e 2012-04-22 kk_Ataka  hello git追加
* 781c723 2012-04-22 kk_Ataka  hello world追加
* 49622c6 2012-04-22 kk_Ataka  (origin/master, origin/HEAD) 1から5までnumberふり
* ec12cb5 2012-04-22 kk_Ataka  first commit

無名ブランチからmasterブランチに。自分で作っていたコミットはなくなりあたらしくdojo1さんが作ったコミットの先に移動している。(SHA1が違う)