一覧SE-04-1 · Gitの共同作業

Gitの変更取得とマージ

取得した参照と作業ブランチの違いを確認し、分岐前の値と双方の変更を根拠に競合を解消します。

説明

共有先の変更を取り込む作業は、取得と統合に分けられます。fetchは共有先のコミットと参照を取得し、mergeは現在のブランチへ履歴を統合します。取得しただけで作業ファイルが新しくなるとは限りません。

小さな設定リポジトリの複製を使います。originは親ディレクトリの共有元を指し、共有元では設定値が10から20へ進んでいます。まず早送りで取り込み、その後、二つのブランチが同じ行を変更して競合する状態を作り、内容を確認して解消します。

取得した参照と作業ブランチの違いを確認し、分岐前の値と双方の変更を根拠に競合を解消します。

1取得前は古い参照が残る

mainとorigin/mainのIDを確認します。origin/mainは前回取得した共有先の状態を表す、手元の参照です。

git rev-parse --short main手元のブランチのID git rev-parse --short origin/main前回取得した共有先の参照 cat settings.ini

二つのIDは同じで、作業ファイルは10です。共有元が更新されても、fetchするまでは手元のorigin/mainは自動で変わりません。

2fetchで参照だけを更新

共有元から取得した後、二つのIDと作業ファイルを再び確認します。-qは転送の進捗表示を省く指定です。

git fetch -q origin共有元のコミットと参照を取得する git rev-parse --short main git rev-parse --short origin/main cat settings.ini作業ファイルがまだ10であることを確認する

origin/mainだけが先へ進み、mainと作業ファイルの10は変わりません。取得と統合が別の操作であることが分かります。

3分岐していなければ早送りできる

差分を確認してから、fast-forwardだけを許可して統合します。

git diff main..origin/main取得済み内容との差を確認する -- settings.ini git merge --ff-only origin/main早送りできる場合だけ統合する cat settings.ini

設定は20になります。mainを取得済みコミットへ進めるだけで統合できたため、二つの親を持つ新しいマージコミットは作られていません。

4同じ行を二方向で変更

featureでは30、mainでは25へ変更します。どちらも20のコミットから分岐した履歴です。

git switch -q -c feature新しい作業ブランチを作る printf 'timeout=30\n' > settings.ini git add settings.ini git commit -q -m 'Feature timeout 30' git switch -q mainmain側へ戻って別の変更を作る printf 'timeout=25\n' > settings.ini git add settings.ini git commit -q -m 'Main timeout 25' git log --graph分岐した履歴を図式で表示する --oneline --all --decorate

mainとfeatureが別々のコミットを指し、その親は共通です。今度は片方へ参照を進めるだけでは、双方の変更を残す統合になりません。

5共通祖先からの差と両端の差

三点表記は共通祖先からfeatureまでの変更、二点表記は現在のmainとfeatureの内容差を示します。

git diff main...feature共通祖先からfeatureへの差分 -- settings.ini git diff main..featuremainとfeatureの両端の差分 -- settings.ini

最初は20→30、次は25→30です。ブランチで加えた変更をレビューする際は、比較の起点が共通祖先なのか現在の相手側なのかを確認します。

6同じ行の変更が競合する

main上でfeatureをマージします。両側が同じ設定行を別の値へ変更したため、自動では決まりません。

git merge featurefeatureを現在のmainへ統合する

CONFLICTが表示され、マージは途中で止まります。これだけでは統合済みになりません。内容を確認し、解消した結果をステージへ登録する必要があります。

7基準・main・featureの値を確認

作業ファイルの印と、indexに保持された3種類の内容を読みます。stage 1が共通祖先、2が現在側、3が取り込む側です。

git status --short cat settings.ini git show :1:settings.ini共通祖先の値 git show :2:settings.ini現在のmain側の値 git show :3:settings.ini取り込むfeature側の値

UUは両側で変更され未解消の状態です。基準20、main25、feature30が表示されます。競合マーカーのHEAD側がmain、feature側が取り込む側です。

8両ブランチの変更を調整して競合を解消する

この例では、待ち時間を30にそろえるという判断で解消します。競合マーカーを残さず、設定1行に書き換えます。

printf 'timeout=30\n'決定した設定で競合箇所を書き換える > settings.ini git add settings.ini解消済みの内容をステージへ登録する git diff --cached -- settings.ini git status --short

ステージには30だけが入り、mainの25に対する差分が出ます。git addは、競合を解消した内容を採用する操作も兼ねています。

9二つの親を持つ統合コミット

解消結果を記録し、親と内容を確認します。

git commit -m解消した統合結果を記録する "Merge feature with timeout 30" git log -1 --format="%h parents=%p %s" git show HEAD^1:settings.ini統合前のmainの内容 git show HEAD^2:settings.ini取り込んだfeatureの内容 cat settings.ini

parentsに二つのIDがあります。第1親はmainの25、第2親はfeatureの30で、統合結果も30です。片方の履歴を消すのではなく、双方を親として新しい結果を記録しました。

まとめ

fetchは共有先の状態を手元へ取得し、mergeは現在のブランチへ統合します。

分岐がなければ早送りで参照を進められます。分岐後の同じ行の変更は競合する場合があります。

競合では共通祖先と双方の変更を読み、採用する内容を決めてからステージとコミットへ記録します。

操作履歴と解説を振り返れます。

Git / シェル

1
git rev-parse --short main git rev-parse --short origin/main cat settings.ini
feb47f5
feb47f5
timeout=10
2
git fetch -q origin git rev-parse --short main git rev-parse --short origin/main cat settings.ini
feb47f5
888c1e3
timeout=10
3
git diff main..origin/main -- settings.ini git merge --ff-only origin/main cat settings.ini
diff --git a/settings.ini b/settings.ini
index bdb130a..806265d 100644
--- a/settings.ini
+++ b/settings.ini
@@ -1 +1 @@
-timeout=10
+timeout=20
Updating feb47f5..888c1e3
Fast-forward
 settings.ini | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
timeout=20
4
git switch -q -c feature printf 'timeout=30\n' > settings.ini git add settings.ini git commit -q -m 'Feature timeout 30' git switch -q main printf 'timeout=25\n' > settings.ini git add settings.ini git commit -q -m 'Main timeout 25' git log --graph --oneline --all --decorate
* 8af4f91 (feature) Feature timeout 30
| * a5394aa (HEAD -> main) Main timeout 25
|/  
* 888c1e3 (origin/main, origin/HEAD) Set shared timeout to 20
* feb47f5 Initial files
5
git diff main...feature -- settings.ini git diff main..feature -- settings.ini
diff --git a/settings.ini b/settings.ini
index 806265d..9ff9b17 100644
--- a/settings.ini
+++ b/settings.ini
@@ -1 +1 @@
-timeout=20
+timeout=30
diff --git a/settings.ini b/settings.ini
index 91545da..9ff9b17 100644
--- a/settings.ini
+++ b/settings.ini
@@ -1 +1 @@
-timeout=25
+timeout=30
6
git merge feature
Auto-merging settings.ini
CONFLICT (content): Merge conflict in settings.ini
Automatic merge failed; fix conflicts and then commit the result.
7
git status --short cat settings.ini git show :1:settings.ini git show :2:settings.ini git show :3:settings.ini
UU settings.ini
<<<<<<< HEAD
timeout=25
=======
timeout=30
>>>>>>> feature
timeout=20
timeout=25
timeout=30
8
printf 'timeout=30\n' > settings.ini git add settings.ini git diff --cached -- settings.ini git status --short
diff --git a/settings.ini b/settings.ini
index 91545da..9ff9b17 100644
--- a/settings.ini
+++ b/settings.ini
@@ -1 +1 @@
-timeout=25
+timeout=30
M  settings.ini
9
git commit -m "Merge feature with timeout 30" git log -1 --format="%h parents=%p %s" git show HEAD^1:settings.ini git show HEAD^2:settings.ini cat settings.ini
[main 98be103] Merge feature with timeout 30
98be103 parents=a5394aa 8af4f91 Merge feature with timeout 30
timeout=25
timeout=30
timeout=30

演習完了