{"id":"l-git-2-2","courseId":"git-github","lang":"en","kind":"lesson","summary":"Merge a finished branch into main, predict whether Git will fast-forward or create a merge commit, and choose between the two on purpose with --no-ff and --ff-only.","html":"<p>You’ve got a finished <code dir=\"ltr\">footer</code> branch and a <code dir=\"ltr\">main</code> branch that doesn’t have it yet. Merging is how work on one line of history joins another. Git has two ways to do it, and which one happens isn’t random: you can predict it by looking at the graph before you type anything.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">The rule of direction</h2>\n<p>First, the rule that saves the most confusion: <strong>you merge another branch into the branch you’re on.</strong> <code dir=\"ltr\">git merge footer</code> means “bring <code dir=\"ltr\">footer</code>’s work into my current branch.” Only the current branch moves. The branch you named is left exactly where it was.</p>\n<p>So the routine is always: switch to the branch that should receive the work, then merge.</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"bash\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">Terminal</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> switch</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> main</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> merge</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> footer</span></span></code></pre></div>\n<h2 id=\"sec-2\" tabindex=\"-1\">Case 1: fast-forward</h2>\n<p>Since you made <code dir=\"ltr\">footer</code>, nobody committed on <code dir=\"ltr\">main</code>. Every commit on <code dir=\"ltr\">main</code> is already in <code dir=\"ltr\">footer</code>’s history. Git doesn’t need to combine anything; it slides <code dir=\"ltr\">main</code> forward to <code dir=\"ltr\">footer</code>’s latest commit:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"text\" data-index=\"1\"><div class=\"code-head\"><span class=\"code-title\">Text</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code>Updating c39be11..f1a3b07\nFast-forward\n index.html |  8 ++++++++\n styles.css | 12 ++++++++++++\n 2 files changed, 20 insertions(+)</code></pre></div>\n<p>That’s a <strong>fast-forward</strong>: no new commit, just a pointer moving along existing commits. History stays a straight line, as if you’d worked on <code dir=\"ltr\">main</code> all along.</p>\n<h2 id=\"sec-3\" tabindex=\"-1\">Case 2: a true merge</h2>\n<p>Now the more interesting case. While you built <code dir=\"ltr\">dark-mode</code>, you also fixed a typo directly on <code dir=\"ltr\">main</code>. Both branches have commits the other lacks; they’ve <strong>diverged</strong>. There’s no straight line to slide along.</p>\n<p>Git does a <strong>three-way merge</strong>. It finds the <strong>merge base</strong>, the most recent commit both branches share, and compares each branch tip with it. A change made on only one side is taken. Changes on both sides in different places are both taken. If both sides changed the same lines, Git stops and asks you; that’s a conflict, and it gets the whole next lesson.</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"bash\" data-index=\"2\"><div class=\"code-head\"><span class=\"code-title\">Terminal</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> switch</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> main</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> merge</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> dark-mode</span></span></code></pre></div>\n<p>Git opens your editor with the message <code dir=\"ltr\">Merge branch 'dark-mode'</code>. Save and close, and you’ll see:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"text\" data-index=\"3\"><div class=\"code-head\"><span class=\"code-title\">Text</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code>Merge made by the 'ort' strategy.\n styles.css | 24 ++++++++++++++++++++++++\n 1 file changed, 24 insertions(+)</code></pre></div>\n<p><code dir=\"ltr\">ort</code> is the name of Git’s default merge algorithm; you don’t need to choose it. The result is a <strong>merge commit</strong>: a commit with two parents, the previous <code dir=\"ltr\">main</code> tip and the <code dir=\"ltr\">dark-mode</code> tip. Its snapshot contains both lines of work.</p>\n<figure class=\"diagram\"><svg class=\"diagram-svg\" focusable=\"false\" viewBox=\"0 0 700 300\" role=\"img\" aria-labelledby=\"l-git-2-2-t1\"><defs><marker id=\"arrow-l-git-2-2-1\" viewBox=\"0 0 10 10\" refX=\"9\" refY=\"5\" markerWidth=\"7\" markerHeight=\"7\" orient=\"auto-start-reverse\"><path d=\"M0 0 L10 5 L0 10 z\" class=\"d-arrowhead\"/></marker></defs>\n  <title id=\"l-git-2-2-t1\">Top: main points at b2e0 and footer is two commits ahead on the same line, so merging slides main forward to f1a3 with no new commit. Bottom: main and dark-mode each have a new commit after b2e0, so merging creates merge commit m7c1 with two parents, and main moves to it.</title>\n  <text class=\"d-label-strong\" x=\"20\" y=\"28\">Fast-forward: main hasn't moved</text>\n  <circle class=\"d-box\" cx=\"60\" cy=\"80\" r=\"22\"/>\n  <text class=\"d-code\" x=\"60\" y=\"85\" text-anchor=\"middle\">b2e0</text>\n  <circle class=\"d-box\" cx=\"160\" cy=\"80\" r=\"22\"/>\n  <text class=\"d-code\" x=\"160\" y=\"85\" text-anchor=\"middle\">e9d2</text>\n  <circle class=\"d-box\" cx=\"260\" cy=\"80\" r=\"22\"/>\n  <text class=\"d-code\" x=\"260\" y=\"85\" text-anchor=\"middle\">f1a3</text>\n  <path class=\"d-line\" d=\"M138 80 L82 80\"/>\n  <path class=\"d-line\" d=\"M238 80 L182 80\"/>\n  <text class=\"d-code\" x=\"300\" y=\"70\">footer</text>\n  <text class=\"d-code\" x=\"300\" y=\"92\">main (after)</text>\n  <text class=\"d-label-muted\" x=\"40\" y=\"128\">main (before) was here, on b2e0</text>\n  <path class=\"d-arrow d-dashed\" d=\"M80 110 C140 125 220 125 250 106\" marker-end=\"url(#arrow-l-git-2-2-1)\"/>\n  <text class=\"d-label-strong\" x=\"20\" y=\"175\">Merge commit: both branches moved</text>\n  <circle class=\"d-box\" cx=\"60\" cy=\"235\" r=\"22\"/>\n  <text class=\"d-code\" x=\"60\" y=\"240\" text-anchor=\"middle\">b2e0</text>\n  <circle class=\"d-box\" cx=\"170\" cy=\"200\" r=\"22\"/>\n  <text class=\"d-code\" x=\"170\" y=\"205\" text-anchor=\"middle\">c39b</text>\n  <circle class=\"d-box\" cx=\"170\" cy=\"272\" r=\"22\"/>\n  <text class=\"d-code\" x=\"170\" y=\"277\" text-anchor=\"middle\">d4a7</text>\n  <circle class=\"d-box-primary\" cx=\"290\" cy=\"235\" r=\"22\"/>\n  <text class=\"d-code\" x=\"290\" y=\"240\" text-anchor=\"middle\">m7c1</text>\n  <path class=\"d-line\" d=\"M148 207 L80 228\"/>\n  <path class=\"d-line\" d=\"M148 265 L80 242\"/>\n  <path class=\"d-line\" d=\"M268 228 L192 207\"/>\n  <path class=\"d-line\" d=\"M268 242 L192 265\"/>\n  <text class=\"d-code\" x=\"325\" y=\"240\">main</text>\n  <text class=\"d-code\" x=\"200\" y=\"296\">dark-mode</text>\n  <text class=\"d-label-muted\" x=\"420\" y=\"225\">m7c1 has two parents:</text>\n  <text class=\"d-label-muted\" x=\"420\" y=\"248\">c39b (main) and d4a7</text>\n</svg><figcaption>Fast-forward versus merge commit</figcaption></figure>\n<h2 id=\"sec-4\" tabindex=\"-1\">Choosing on purpose</h2>\n<p>Both results contain the same final files. What differs is how history reads, and teams have opinions.</p>\n<ul>\n<li><strong>Fast-forward</strong> keeps history linear and quiet. Good for small changes, but after the fact you can’t see which commits belonged to which feature.</li>\n<li><strong>Merge commits</strong> keep each feature visibly grouped and record when it landed. The graph gets busier.</li>\n</ul>\n<p>Two flags let you pick instead of letting the graph decide:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"bash\" data-index=\"4\"><div class=\"code-head\"><span class=\"code-title\">Terminal</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> merge</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --no-ff</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> footer</span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">     # always create a merge commit, even if fast-forward is possible</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> merge</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --ff-only</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> footer</span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">   # fast-forward or refuse; never create a merge commit</span></span></code></pre></div>\n<p>My default for a solo portfolio: let Git fast-forward small branches, and don’t agonise over it. On a team, follow the team’s convention, which is usually set on GitHub rather than typed by hand. What matters far more than the policy is that each branch carries one coherent piece of work, so whichever shape history takes, every step in it makes sense.</p>\n<p><code dir=\"ltr\">--ff-only</code> is a good safety habit when you only expect to catch up, for example updating your local <code dir=\"ltr\">main</code>. If Git refuses, that’s news: something diverged that you didn’t know about. On GitHub you’ll meet the same choice as merge methods on a pull request in section 3.</p>\n<p>You can check the merge base yourself before merging, which tells you how far the branches have drifted:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"bash\" data-index=\"5\"><div class=\"code-head\"><span class=\"code-title\">Terminal</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> merge-base</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> main</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> dark-mode</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> log</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --oneline</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> main..dark-mode</span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">    # commits on dark-mode that main lacks</span></span>\n<span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> log</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --oneline</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> dark-mode..main</span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">    # and the reverse</span></span></code></pre></div>\n<p>If the second log is empty, <code dir=\"ltr\">main</code> hasn’t moved and the merge will fast-forward.</p>\n<aside class=\"callout callout-mistake\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\"/><path d=\"M12 9v4\"/><path d=\"M12 17h.01\"/></svg><span><span class=\"callout-kind\">Common mistake</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>Merging in the wrong direction</span></p>\n<p>You’re on <code dir=\"ltr\">dark-mode</code>, you run <code dir=\"ltr\">git merge main</code>, and wonder why <code dir=\"ltr\">main</code> never got the dark theme. The merge went into the branch you were on. Run <code dir=\"ltr\">git branch</code> (or check your prompt) before every merge, switch to the receiving branch, and merge from there. Merging <code dir=\"ltr\">main</code> into a feature branch is a real technique for catching up, so Git won’t stop you.</p>\n</aside>\n<h2 id=\"sec-5\" tabindex=\"-1\">After the merge</h2>\n<p>Once <code dir=\"ltr\">footer</code> is merged, the branch label has done its job. Delete it:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"bash\" data-index=\"6\"><div class=\"code-head\"><span class=\"code-title\">Terminal</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#953800;--shiki-dark:#FFA657\">git</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> branch</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> -d</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> footer</span></span></code></pre></div>\n<p><code dir=\"ltr\">-d</code> succeeds because the commits are reachable from <code dir=\"ltr\">main</code>. The commits themselves stay in history forever; only the sticky note goes. Clean up merged branches as you go, and <code dir=\"ltr\">git branch</code> stays a short list of work that’s actually in progress.</p>\n<p>So far every merge went smoothly because the two branches touched different lines. Next you’ll make them collide on purpose and resolve the conflict step by step.</p>\n","toc":[{"id":"sec-1","text":"The rule of direction","level":2},{"id":"sec-2","text":"Case 1: fast-forward","level":2},{"id":"sec-3","text":"Case 2: a true merge","level":2},{"id":"sec-4","text":"Choosing on purpose","level":2},{"id":"sec-5","text":"After the merge","level":2}],"takeaways":["You always merge another branch into the branch you’re on; only the current branch moves.","If the current branch hasn’t moved since the other branch started, Git fast-forwards the pointer and creates no new commit.","If both branches have new commits, Git does a three-way merge from their common ancestor and records a merge commit with two parents.","<code dir=\"ltr\">--no-ff</code> forces a merge commit to keep a feature grouped; <code dir=\"ltr\">--ff-only</code> refuses anything but a fast-forward."],"further":[{"title":"Basic Branching and Merging (Pro Git)","url":"https://git-scm.com/book/en/v2/Git-Branching-Basic-Branching-and-Merging"},{"title":"git merge reference","url":"https://git-scm.com/docs/git-merge"},{"title":"git merge-base reference","url":"https://git-scm.com/docs/git-merge-base"}],"quiz":[{"q":"<p>You’re on <code dir=\"ltr\">main</code>. Since you created <code dir=\"ltr\">footer</code> from it, <code dir=\"ltr\">main</code> has gained no commits and <code dir=\"ltr\">footer</code> has three. What does <code dir=\"ltr\">git merge footer</code> do?</p>","options":[{"text":"Moves <code dir=\"ltr\">main</code> forward to <code dir=\"ltr\">footer</code>’s latest commit without creating a new commit.","why":"Correct. <code dir=\"ltr\">main</code> is an ancestor of <code dir=\"ltr\">footer</code>, so Git can fast-forward the pointer. History stays a straight line."},{"text":"Creates a merge commit with two parents.","why":"That happens only when both branches have moved, or when you ask for it with <code dir=\"ltr\">--no-ff</code>."},{"text":"Copies the three commits onto <code dir=\"ltr\">main</code> with new IDs.","why":"That describes rebasing or cherry-picking. A merge reuses the existing commits."},{"text":"Moves <code dir=\"ltr\">footer</code> back to where <code dir=\"ltr\">main</code> is.","why":"A merge never moves the branch you name, only the branch you’re on."}],"answer":0},{"q":"<p>You meant to bring <code dir=\"ltr\">dark-mode</code> into <code dir=\"ltr\">main</code>, but you were on <code dir=\"ltr\">dark-mode</code> and ran <code dir=\"ltr\">git merge main</code>. What happened?</p>","options":[{"text":"Nothing; Git detects you meant the other direction and swaps them.","why":"Git does exactly what you asked. Merge direction is decided by which branch you’re on."},{"text":"The command failed because you can’t merge <code dir=\"ltr\">main</code> into a feature branch.","why":"You can, and teams do it to bring a long-running branch up to date. It’s legal, just not what you wanted here."},{"text":"<code dir=\"ltr\">main</code> now contains the dark mode work.","why":"<code dir=\"ltr\">main</code> didn’t move. Only the branch you’re on, <code dir=\"ltr\">dark-mode</code>, can change during a merge."},{"text":"<code dir=\"ltr\">dark-mode</code> gained <code dir=\"ltr\">main</code>’s new commits; <code dir=\"ltr\">main</code> is unchanged.","why":"Correct. Switch to <code dir=\"ltr\">main</code> with <code dir=\"ltr\">git switch main</code> and run <code dir=\"ltr\">git merge dark-mode</code> to merge in the direction you intended."}],"answer":3},{"q":"<p>Which commit does Git use as the starting point when it does a three-way merge of <code dir=\"ltr\">dark-mode</code> into <code dir=\"ltr\">main</code>?</p>","options":[{"text":"The first commit in the repository.","why":"The root commit is usually far too old. Git wants the most recent shared point, to compare only what each side changed."},{"text":"The most recent commit both branches share, their merge base.","why":"Correct. Git compares each branch tip with the merge base, takes changes made on either side, and flags places where both sides changed the same lines."},{"text":"Whichever branch tip has the older timestamp.","why":"Timestamps don’t decide anything in a merge. The graph does."}],"answer":1},{"q":"<p>Your team wants every feature to appear in <code dir=\"ltr\">main</code>’s history as one merge commit, even when a fast-forward would be possible. Which option gives that?</p>","options":[{"text":"<code dir=\"ltr\">git merge --ff-only feature</code>","why":"That’s the opposite policy. It allows only fast-forwards and refuses to create merge commits."},{"text":"<code dir=\"ltr\">git merge --squash feature</code>","why":"That stages the combined changes without committing or recording a second parent, so the branch link is lost."},{"text":"<code dir=\"ltr\">git merge --no-ff feature</code>","why":"Correct. It always records a merge commit, so the feature’s commits stay grouped on their own line in the graph."},{"text":"<code dir=\"ltr\">git merge --all feature</code>","why":"<code dir=\"ltr\">git merge</code> has no <code dir=\"ltr\">--all</code> option."}],"answer":2}],"exercise":{"kind":"choice","multiple":false,"options":["a","b","c","d"],"correct":["b"],"title":"Predict the merge","prompt":"<p>This is the portfolio right now. Arrows point from each commit to its parent.</p>\n<p>:::figure Before the merge\n<svg viewBox=\"0 0 640 220\" role=\"img\" aria-labelledby=\"ex1\"></p>\n  <title id=\"ex1\">Commit a1 then b2. From b2, main continues to c3, which main and HEAD point to. Also from b2, the projects-grid branch has d4 then e5, and projects-grid points to e5.</title>\n  <circle class=\"d-box\" cx=\"50\" cy=\"110\" r=\"24\"/>\n  <text class=\"d-code\" x=\"50\" y=\"115\" text-anchor=\"middle\">a1</text>\n  <circle class=\"d-box\" cx=\"160\" cy=\"110\" r=\"24\"/>\n  <text class=\"d-code\" x=\"160\" y=\"115\" text-anchor=\"middle\">b2</text>\n  <circle class=\"d-box\" cx=\"290\" cy=\"55\" r=\"24\"/>\n  <text class=\"d-code\" x=\"290\" y=\"60\" text-anchor=\"middle\">c3</text>\n  <circle class=\"d-box\" cx=\"290\" cy=\"165\" r=\"24\"/>\n  <text class=\"d-code\" x=\"290\" y=\"170\" text-anchor=\"middle\">d4</text>\n  <circle class=\"d-box\" cx=\"410\" cy=\"165\" r=\"24\"/>\n  <text class=\"d-code\" x=\"410\" y=\"170\" text-anchor=\"middle\">e5</text>\n  <path class=\"d-arrow\" d=\"M136 110 L78 110\" marker-end=\"url(#arrow)\"/>\n  <path class=\"d-arrow\" d=\"M267 64 L188 100\" marker-end=\"url(#arrow)\"/>\n  <path class=\"d-arrow\" d=\"M267 156 L188 120\" marker-end=\"url(#arrow)\"/>\n  <path class=\"d-arrow\" d=\"M386 165 L318 165\" marker-end=\"url(#arrow)\"/>\n  <rect class=\"d-box-accent\" x=\"340\" y=\"38\" width=\"90\" height=\"34\" rx=\"8\"/>\n  <text class=\"d-code\" x=\"385\" y=\"60\" text-anchor=\"middle\">main</text>\n  <rect class=\"d-box-warn\" x=\"450\" y=\"38\" width=\"90\" height=\"34\" rx=\"8\"/>\n  <text class=\"d-code\" x=\"495\" y=\"60\" text-anchor=\"middle\">HEAD</text>\n  <path class=\"d-line\" d=\"M450 55 L430 55\"/>\n  <path class=\"d-line\" d=\"M340 55 L318 55\"/>\n  <rect class=\"d-box-accent\" x=\"460\" y=\"148\" width=\"150\" height=\"34\" rx=\"8\"/>\n  <text class=\"d-code\" x=\"535\" y=\"170\" text-anchor=\"middle\">projects-grid</text>\n  <path class=\"d-line\" d=\"M460 165 L438 165\"/>\n</svg>\n:::\n<p>The two branches touched different files. You run <code dir=\"ltr\">git merge projects-grid</code>. What does the graph look like afterwards?</p>\n","hints":["Which branch is HEAD on? That’s the only branch that can move.","Has <code dir=\"ltr\">main</code> gained commits since <code dir=\"ltr\">projects-grid</code> split off at <code dir=\"ltr\">b2</code>?","When both sides have new commits, a fast-forward is impossible."],"explanation":"<p>HEAD is on <code dir=\"ltr\">main</code>, so <code dir=\"ltr\">main</code> is the branch that moves. Both branches have commits since their merge base <code dir=\"ltr\">b2</code> (<code dir=\"ltr\">c3</code> on <code dir=\"ltr\">main</code>; <code dir=\"ltr\">d4</code> and <code dir=\"ltr\">e5</code> on <code dir=\"ltr\">projects-grid</code>), so a fast-forward is impossible. Git does a three-way merge from <code dir=\"ltr\">b2</code> and records a new merge commit whose parents are <code dir=\"ltr\">c3</code> and <code dir=\"ltr\">e5</code>. <code dir=\"ltr\">main</code> moves to it; <code dir=\"ltr\">projects-grid</code> stays on <code dir=\"ltr\">e5</code> until you delete it.</p>\n","checkLabels":{},"optionText":{"a":{"text":"<code dir=\"ltr\">main</code> fast-forwards to <code dir=\"ltr\">e5</code>; no new commit is made.","why":"A fast-forward needs <code dir=\"ltr\">main</code> to be an ancestor of <code dir=\"ltr\">e5</code>. <code dir=\"ltr\">c3</code> isn’t in <code dir=\"ltr\">projects-grid</code>’s history, so Git can’t slide <code dir=\"ltr\">main</code> along a straight line."},"b":{"text":"A new merge commit with parents <code dir=\"ltr\">c3</code> and <code dir=\"ltr\">e5</code> appears; <code dir=\"ltr\">main</code> moves to it and <code dir=\"ltr\">projects-grid</code> stays on <code dir=\"ltr\">e5</code>.","why":"Correct. Both sides moved since <code dir=\"ltr\">b2</code>, so Git creates a two-parent commit on the current branch only."},"c":{"text":"A new merge commit appears and both <code dir=\"ltr\">main</code> and <code dir=\"ltr\">projects-grid</code> move to it.","why":"Only the branch you’re on moves during a merge. <code dir=\"ltr\">projects-grid</code> keeps pointing at <code dir=\"ltr\">e5</code>."},"d":{"text":"<code dir=\"ltr\">d4</code> and <code dir=\"ltr\">e5</code> are copied after <code dir=\"ltr\">c3</code> with new IDs, keeping history linear.","why":"That’s what a rebase does. A merge keeps the original commits and joins the two lines with a merge commit."}}},"words":722,"readMinutes":3}