Gitに関する記事
ブランチなるものがどんなもんか試してみる。
#ブランチ関連のコマンド
-
git branch [-r|-a]
ブランチの一覧を表示する。*(アスタリスク)が付いているのが現在のブランチ。
オプションなし:ローカルブランチ一覧
-r:リモートブランチ一覧
-a:ローカル&リモートブランチ一覧 -
git branch <ブランチ>
ブランチを作成する。 -
git checkout <ブランチ>
ブランチを切り替える。 -
git checkout -b <ブランチ>
ブランチ作成してそのブランチに切り替える(=branch+checkout)。 -
git checkout -b <ブランチ> <リビジョン>
指定したリビジョンからブランチを作成してそのブランチに切り替える。
単純にリビジョンを指定してブランチを作る(ブランチの切り替えはしない)方法はないものかと調べたが、git branch
でそういうことをする方法は見つけられず、git checkout
で上記のようにするしかないようだ。 -
git branch -m <新しいブランチ名>
現在のブランチの名前を変更する。 -
git branch -d <ブランチ>
ブランチを削除する。 -
git branch -rd <リモート追跡ブランチ>
リモート追跡ブランチ(origin/hoge みたいな名前のやつ)を削除する。 -
git checkout -b <ブランチ> <リビジョン>
特定のリビジョンからブランチを作成してそのブランチに切り替える。 -
git merge [--no-ff] <ブランチ>
指定したブランチを現在のブランチに統合する。指定したブランチのコミットはそのまま残り、統合先のブランチに新しいコミットが作成される。
統合先のブランチで実行する。例えば、bugfixブランチをmasterブランチに統合する場合は、masterブランチでgit merge bugfix
を実行する。
オプション --no-ff については後で説明する。 -
git rebase <ブランチ>
指定したブランチに現在のブランチを統合する。
統合元のブランチで実行する。例えば、bugfixブランチをmasterブランチに統合する場合は、bugfixブランチでgit rebase master
を実行する。 -
git diff <ブランチ1> <ブランチ2>
ブランチ間の差分を表示する。 -
git log --graph --date-order --all --pretty=format:'%h %Cred%d %Cgreen%ad %Cblue%cn %Creset%s' --date=short
コミットグラフ(ブランチの状況)をわかりやすく表示する。
オプションが長いので以下のようにエイリアスを設定してgit graph
で使えるようにしておくとよい。 -
git config --global alias.graph "log --graph --date-order --all --pretty=format:'%h %Cred%d %Cgreen%ad %C(blue bold)%cn %Creset%s' --date=short"
#マージ
git merge
で他のブランチの変更内容を取り込むこと(マージ)ができる。
マージには fast-forwardマージと non fast-forwardマージ(3-way マージとも呼ばれる)の2種類がある。
git merge
コマンドが自動で判定してどちらかのマージを行うが、オプションで指定することもできる。
fast-forwardマージ
fast-forwardマージは新たなコミットを作らずにブランチが指すコミットを変更するだけのコミットである。
例で説明する。masterブランチのコミットBからtopicブランチを作成し、topicブランチで何回かコミットした。この段階で topicブランチを作ってからmasterブランチには1回もコミットしておらず、topicブランチはmasterブランチの履歴をすべて持っている。
上記の状態からtopicブランチをmasterブランチにマージするため masterブランチで git merge topic
を実行すると、masterブランチのHEADがDに移動される。これでマージ完了。これが fast-foward マージである。
先に述べたようにtopicブランチはmasterブランチの履歴をすべて持っているのでバージョン管理対象のファイルの更新は発生せず、また新たなコミットも作られない。masterブランチが指しているコミットが変更されるだけである。
non fast-forwardマージ
no fast-forwarcマージ(3-wayマージ)は新たなコミット「マージコミット」を作成するマージである。
例で説明する。fast-forwardマージの説明の例と同じくmasterブランチとtopicブランチがある。先の例と異なるのはmasterブランチの方でも何回かコミットしていることである。つまりtopicブランチがmasterブランチのすべての履歴を持っているわけではない。
この状態でmasterブランチにおいて git merge topic
を実行すると、masterブランチとtopicブランチを統合したマージコミットが作成され、それをmasterブランチが指すようになる。
--no-ff オプション
fast-forwardマージが実行されるような状態でもマージしたという履歴を意図的に残してマージしたい場合には git merge
の --no-ff オプションを使用する。git merge --no-ff <ブランチ名>
で必ず non fast-forwardマージが実行される。
#ブランチを使ってみる
##masterブランチ
git init
直後に git branch
としても何も表示されない。
y_ito@note MINGW64 /c/work/git_test
$ git init
Initialized empty Git repository in C:/work/git_test/.git/
y_ito@note MINGW64 /c/work/git_test (master)
$ git branch
y_ito@note MINGW64 /c/work/git_test (master)
$
というかブランチは単にコミットのどこかを指すポインタみたいなものらしいので、指し示すべきコミットがなければブランチも在りようがない。
コミットするとデフォルトのブランチである master が現れる。
y_ito@note MINGW64 /c/work/git_test (master)
$ git commit --allow-empty -m 'first commit'
[master (root-commit) 0b7383d] first commit
y_ito@note MINGW64 /c/work/git_test (master)
$ git log --oneline
0b7383d (HEAD -> master) first commit
y_ito@note MINGW64 /c/work/git_test (master)
$ git branch
* master
##ブランチの作成
ファイルを1個コミットする。
y_ito@note MINGW64 /c/work/git_test (master)
$ cat uchinaaguchi.txt
うちなーぐち,標準語
はいさい,やあ
y_ito@note MINGW64 /c/work/git_test (master)
$ git add .
y_ito@note MINGW64 /c/work/git_test (master)
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: uchinaaguchi.txt
y_ito@note MINGW64 /c/work/git_test (master)
$ git commit -m 'ファイルを新規作成した。'
[master 93ed5c0] ファイルを新規作成した。
1 file changed, 2 insertions(+)
create mode 100644 uchinaaguchi.txt
コミットログとブランチはこうなっている。masterブランチだけがある。
y_ito@note MINGW64 /c/work/git_test (master)
$ git log --name-status
commit 93ed5c0031f205b1e10ad58ed74175574c652be0 (HEAD -> master)
Author: y_ito <intointo@nifty.com>
Date: Tue May 15 21:34:24 2018 +0900
ファイルを新規作成した。
A uchinaaguchi.txt
commit 0b7383d2c2ecd652118d06aef56de1054c60ab19
Author: y_ito <intointo@nifty.com>
Date: Tue May 15 21:20:23 2018 +0900
first commit
y_ito@note MINGW64 /c/work/git_test (master)
$ git branch
* master
master以外のブランチを作る。
y_ito@note MINGW64 /c/work/git_test (master)
$ git branch branch-01
y_ito@note MINGW64 /c/work/git_test (master)
$ git branch branch-02
y_ito@note MINGW64 /c/work/git_test (master)
$ git branch
branch-01
branch-02
* master
##ブランチの切り替え
ブランチを切り替え、それぞれのブランチでファイルを編集する。
y_ito@note MINGW64 /c/work/git_test (master)
$ git checkout branch-01
Switched to branch 'branch-01'
y_ito@note MINGW64 /c/work/git_test (branch-01)
$ git branch
* branch-01
branch-02
master
<<<ここでファイルの編集>>>
y_ito@note MINGW64 /c/work/git_test (branch-01)
$ git diff
diff --git a/uchinaaguchi.txt b/uchinaaguchi.txt
index 125268c..f0a3e1b 100644
--- a/uchinaaguchi.txt
+++ b/uchinaaguchi.txt
@@ -1,2 +1,3 @@
うちなーぐち,標準語
はいさい,やあ
+ちゅーうがなびら,こんにちは
y_ito@note MINGW64 /c/work/git_test (branch-01)
$ git commit -a -m '「ちゅーうがなびら」を追加した。'
[branch-01 ba401ce] 「ちゅーうがなびら」を追加した。
1 file changed, 1 insertion(+)
y_ito@note MINGW64 /c/work/git_test (branch-01)
$ git log --oneline
ba401ce (HEAD -> branch-01) 「ちゅーうがなびら」を追加した。
93ed5c0 (master, branch-02) ファイルを新規作成した。
0b7383d first commit
y_ito@note MINGW64 /c/work/git_test (branch-01)
$ git checkout branch-02
Switched to branch 'branch-02'
y_ito@note MINGW64 /c/work/git_test (branch-02)
$ git branch
branch-01
* branch-02
master
<<<ここでファイルの編集>>>
y_ito@note MINGW64 /c/work/git_test (branch-02)
$ git diff
diff --git a/uchinaaguchi.txt b/uchinaaguchi.txt
index 125268c..04fdd9c 100644
--- a/uchinaaguchi.txt
+++ b/uchinaaguchi.txt
@@ -1,2 +1,3 @@
うちなーぐち,標準語
はいさい,やあ
+めんそーれ,いらっしゃいませ
y_ito@note MINGW64 /c/work/git_test (branch-02)
$ git commit -a -m '「めんそーれ」を追加した。'
[branch-02 b4eeaaf] 「めんそーれ」を追加した。
1 file changed, 1 insertion(+)
y_ito@note MINGW64 /c/work/git_test (branch-02)
$ git log --oneline
b4eeaaf (HEAD -> branch-02) 「めんそーれ」を追加した。
93ed5c0 (master) ファイルを新規作成した。
0b7383d first commit
それそれのブランチでファイルの内容が異なっている。
y_ito@note MINGW64 /c/work/git_test (branch-02)
$ git checkout master; cat uchinaaguchi.txt
Switched to branch 'master'
うちなーぐち,標準語
はいさい,やあ
y_ito@note MINGW64 /c/work/git_test (master)
$ git checkout branch-01; cat uchinaaguchi.txt
Switched to branch 'branch-01'
うちなーぐち,標準語
はいさい,やあ
ちゅーうがなびら,こんにちは
y_ito@note MINGW64 /c/work/git_test (branch-01)
$ git checkout branch-02; cat uchinaaguchi.txt
Switched to branch 'branch-02'
うちなーぐち,標準語
はいさい,やあ
めんそーれ,いらっしゃいませ
##マージする
mater で git merge
コマンドを使って branch-01 を master にマージする。
fast-forward マージが行われる。
y_ito@note MINGW64 /c/work/git_test (branch-02)
$ git checkout master
Switched to branch 'master'
y_ito@note MINGW64 /c/work/git_test (master)
$ git merge branch-01
Updating 93ed5c0..ba401ce
Fast-forward
uchinaaguchi.txt | 1 +
1 file changed, 1 insertion(+)
y_ito@note MINGW64 /c/work/git_test (master)
$ git log --oneline
ba401ce (HEAD -> master, branch-01) 「ちゅーうがなびら」を追加した。
93ed5c0 ファイルを新規作成した。
0b7383d first commit
##コンフリクトを解決してマージする
###git mergeのみでマージする
さらに branch-02 をマージしようとすると、コンフリクトする。
y_ito@note MINGW64 /c/work/git_test (master)
$ git merge branch-02
Auto-merging uchinaaguchi.txt
CONFLICT (content): Merge conflict in uchinaaguchi.txt
Automatic merge failed; fix conflicts and then commit the result.
TortoiseGit ではファイルのアイコンにエクスクラメーションマークが付く。
ファイルの中身はこうなっている。
うちなーぐち,標準語
はいさい,やあ
<<<<<<< HEAD
ちゅーうがなびら,こんにちは
=======
めんそーれ,いらっしゃいませ
>>>>>>> branch-02
各ブランチで編集した内容を両方残したいので、下記のように編集して保存する。
うちなーぐち,標準語
はいさい,やあ
ちゅーうがなびら,こんにちは
めんそーれ,いらっしゃいませ
編集したファイルをaddしてcommitする。このマージはnon fast-forward マージとなる。
y_ito@note MINGW64 /c/work/git_test (master|MERGING)
$ git diff
diff --cc uchinaaguchi.txt
index f0a3e1b,04fdd9c..0000000
--- a/uchinaaguchi.txt
+++ b/uchinaaguchi.txt
@@@ -1,3 -1,3 +1,4 @@@
うちなーぐち,標準語
はいさい,やあ
+ちゅーうがなびら,こんにちは
+ めんそーれ,いらっしゃいませ
y_ito@note MINGW64 /c/work/git_test (master|MERGING)
$ git add .
y_ito@note MINGW64 /c/work/git_test (master|MERGING)
$ git commit -m 'branch-02をマージした。'
[master 33a4d0c] branch-02をマージした。
y_ito@note MINGW64 /c/work/git_test (master)
$ git log --oneline
33a4d0c (HEAD -> master) branch-02をマージした。
b4eeaaf (branch-02) 「めんそーれ」を追加した。
ba401ce (branch-01) 「ちゅーうがなびら」を追加した。
93ed5c0 ファイルを新規作成した。
0b7383d first commit
git log
にオプションを付けて表示される履歴グラフは下記のようになる。2つのブランチで別々に編集され、それがマージされたことがグラフィカルに分かる。
y_ito@note MINGW64 /c/work/git_test (master)
$ git log --graph --oneline --decorate=full
* 33a4d0c (HEAD -> refs/heads/master) branch-02をマージした。
|\
| * b4eeaaf (refs/heads/branch-02) 「めんそーれ」を追加した。
* | ba401ce (refs/heads/branch-01) 「ちゅーうがなびら」を追加した。
|/
* 93ed5c0 ファイルを新規作成した。
* 0b7383d first commit
###git rebaseしてgit mergeする
master と branch-02 をマージするもう1つの方法に、rebaseしてからmergeするやり方がある。
まず、master を branch-02 とマージする前に戻す。
y_ito@note MINGW64 /c/work/git_test (master)
$ git reset --hard HEAD~
HEAD is now at ba401ce 「ちゅーうがなびら」を追加した。
y_ito@note MINGW64 /c/work/git_test (master)
$ git log --graph --oneline --decorate=full
* ba401ce (HEAD -> refs/heads/master, refs/heads/branch-01) 「ちゅーうがなびら」を追加した。
* 93ed5c0 ファイルを新規作成した。
* 0b7383d first commit
そして branch-02 を rebase する。
y_ito@note MINGW64 /c/work/git_test (master)
$ git checkout branch-02
Switched to branch 'branch-02'
y_ito@note MINGW64 /c/work/git_test (branch-02)
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: 「めんそーれ」を追加した。
error: Failed to merge in the changes.
Using index info to reconstruct a base tree...
M uchinaaguchi.txt
Falling back to patching base and 3-way merge...
Auto-merging uchinaaguchi.txt
CONFLICT (content): Merge conflict in uchinaaguchi.txt
Patch failed at 0001 「めんそーれ」を追加した。
The copy of the patch that failed is found in: .git/rebase-apply/patch
When you have resolved this problem, run "git rebase --continue".
If you prefer to skip this patch, run "git rebase --skip" instead.
To check out the original branch and stop rebasing, run "git rebase --abort".
すると、ファイルはこうなっている。
うちなーぐち,標準語
はいさい,やあ
<<<<<<< HEAD
ちゅーうがなびら,こんにちは
=======
めんそーれ,いらっしゃいませ
>>>>>>> 「めんそーれ」を追加した。
これを先の merge だけする場合と同じように編集して保存する。
うちなーぐち,標準語
はいさい,やあ
ちゅーうがなびら,こんにちは
めんそーれ,いらっしゃいませ
そして add して rebase --continue すると、branch-02 はrebaseされる。
#rebase って、base を re なのね。なんとなく判ったような気がする。
y_ito@note MINGW64 /c/work/git_test (branch-02|REBASE 1/1)
$ git diff
diff --cc uchinaaguchi.txt
index f0a3e1b,04fdd9c..0000000
--- a/uchinaaguchi.txt
+++ b/uchinaaguchi.txt
@@@ -1,3 -1,3 +1,4 @@@
うちなーぐち,標準語
はいさい,やあ
+ちゅーうがなびら,こんにちは
+ めんそーれ,いらっしゃいませ
y_ito@note MINGW64 /c/work/git_test (branch-02|REBASE 1/1)
$ git status
rebase in progress; onto ba401ce
You are currently rebasing branch 'branch-02' on 'ba401ce'.
(fix conflicts and then run "git rebase --continue")
(use "git rebase --skip" to skip this patch)
(use "git rebase --abort" to check out the original branch)
Unmerged paths:
(use "git reset HEAD <file>..." to unstage)
(use "git add <file>..." to mark resolution)
both modified: uchinaaguchi.txt
no changes added to commit (use "git add" and/or "git commit -a")
y_ito@note MINGW64 /c/work/git_test (branch-02|REBASE 1/1)
$ git add uchinaaguchi.txt
y_ito@note MINGW64 /c/work/git_test (branch-02|REBASE 1/1)
$ git status
rebase in progress; onto ba401ce
You are currently rebasing branch 'branch-02' on 'ba401ce'.
(all conflicts fixed: run "git rebase --continue")
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: uchinaaguchi.txt
y_ito@note MINGW64 /c/work/git_test (branch-02|REBASE 1/1)
$ git rebase --continue
Applying: 「めんそーれ」を追加した。
y_ito@note MINGW64 /c/work/git_test (branch-02)
$ git status
On branch branch-02
nothing to commit, working tree clean
y_ito@note MINGW64 /c/work/git_test (branch-02)
$ git log --oneline
c0e3b9e (HEAD -> branch-02) 「めんそーれ」を追加した。
ba401ce (master, branch-01) 「ちゅーうがなびら」を追加した。
93ed5c0 ファイルを新規作成した。
0b7383d first commit
あとは mater で branch-02 をマージする。このマージは fast-forwar マージとなる。
y_ito@note MINGW64 /c/work/git_test (branch-02)
$ git checkout master
Switched to branch 'master'
y_ito@note MINGW64 /c/work/git_test (master)
$ git merge branch-02
Updating ba401ce..c0e3b9e
Fast-forward
uchinaaguchi.txt | 1 +
1 file changed, 1 insertion(+)
先の rebase しないマージの場合とファイルの内容は同じだが、コミットグラフが異なる。
コミットの履歴が1本になっている。
y_ito@note MINGW64 /c/work/git_test (master)
$ git log --graph --oneline --decorate=full
* c0e3b9e (HEAD -> refs/heads/master, refs/heads/branch-02) 「めんそーれ」を追加した。
* ba401ce (refs/heads/branch-01) 「ちゅーうがなびら」を追加した。
* 93ed5c0 ファイルを新規作成した。
* 0b7383d first commit
###rebaseする/しないの判断
rebaseせずにマージした場合とrebaseしてマージした場合で以下の表のような違いがある。
マージコミットが | マージグラフは | |
---|---|---|
rebaseしなかった場合 | 作成される | 分岐あり |
rebaseした場合 | 作成されない | 分岐なしで1本線 |
「マージした」という履歴を残すことを重視する場合はrebaseしない、「履歴をきれいに保つ」ということを重視する場合はrebaseするという感じ。
GitHubなどを使って複数人で開発している場合は、「履歴をきれいに保つ」というのが重要視されていてpushする前にrebaseしましょうというのがルールになっているようだ。ただし、既にpushしたブランチをrebaseしてまたpushするのは禁止らしい。
##感想
例えば次期リリースの開発中に前のリリースの緊急対応が必要なバグが見つかった場合、ブランチを切れば時期リリース開発に影響なくバグフィックスができる。Gitの書籍や入門サイトのよくあげられている例だが、確かにそういう場面では役に立ちそう。
一人での開発でも、GitHubとかGitLabとか使ってなくても、Gitを使う意味は十分あるように思った。
#参考文献・参考ページ
##参考文献
WEB+DB PRESS Vol.90 特集1:もう困らない!Git実践活用
##参考ページ
チュートリアル1 ブランチを使ってみよう | サルでもわかるGit入門 〜バージョン管理を使いこなそう〜 | どこでもプロジェクト管理バックログ
初心者でもわかる!リベースの使い方を解説します | Git編:一歩踏み出すフロントエンド入門
git logにブランチ名を表示する git log --oneline --graph --decorate=fullのススメ - Qiita
Gitで特定のcommit idを指定してbranchを切ってcheckoutする方法 - Qiita
git graphを使ってターミナルで Git のツリー構造を表示する | ワードプレスのホームページ制作|新宿区のフリーランスWeb制作 ピクセルデザイン - Webサイトのディレクションからデザイン・コーディングまで
[Git] 使い分けできていますか?マージ(merge)&リベース(rebase)再入門 - The Powerful Code
シロク流、Gitの使い方 - Qiita
怖くないGit
【git】分かりやすく!mergeは「合流」、rebaseは「付け替え」! | NullNote meta : Description
Git 過去の特定のコミット位置からブランチを切りたい - かもメモ
gitのローカルのブランチ名を変更したい - Qiita