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 --decoratemainと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.iniparentsに二つのIDがあります。第1親はmainの25、第2親はfeatureの30で、統合結果も30です。片方の履歴を消すのではなく、双方を親として新しい結果を記録しました。
まとめ
fetchは共有先の状態を手元へ取得し、mergeは現在のブランチへ統合します。
分岐がなければ早送りで参照を進められます。分岐後の同じ行の変更は競合する場合があります。
競合では共通祖先と双方の変更を読み、採用する内容を決めてからステージとコミットへ記録します。
操作履歴と解説を振り返れます。
Git / シェル
git rev-parse --short main
git rev-parse --short origin/main
cat settings.inifeb47f5 feb47f5 timeout=10
git fetch -q origin
git rev-parse --short main
git rev-parse --short origin/main
cat settings.inifeb47f5 888c1e3 timeout=10
git diff main..origin/main -- settings.ini
git merge --ff-only origin/main
cat settings.inidiff --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
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
git diff main...feature -- settings.ini
git diff main..feature -- settings.inidiff --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
git merge featureAuto-merging settings.ini CONFLICT (content): Merge conflict in settings.ini Automatic merge failed; fix conflicts and then commit the result.
git status --short
cat settings.ini
git show :1:settings.ini
git show :2:settings.ini
git show :3:settings.iniUU settings.ini <<<<<<< HEAD timeout=25 ======= timeout=30 >>>>>>> feature timeout=20 timeout=25 timeout=30
printf 'timeout=30\n' > settings.ini
git add settings.ini
git diff --cached -- settings.ini
git status --shortdiff --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
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
演習完了