{"id":1165,"date":"2026-09-19T06:42:45","date_gmt":"2026-09-19T06:42:45","guid":{"rendered":"https:\/\/www.devopsschool.com\/tutorials\/?p=1165"},"modified":"2026-09-19T07:07:38","modified_gmt":"2026-09-19T07:07:38","slug":"git-git-branching-merging-a-complete-tutorials","status":"publish","type":"post","link":"https:\/\/www.devopsschool.com\/tutorials\/git-git-branching-merging-a-complete-tutorials\/","title":{"rendered":"Git: Git Branching &amp; Merging &#8211; A Complete Tutorials"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\"><em>PRACTICAL TECHNICAL HANDBOOK \/ 19 SEPTEMBER 2026<\/em><\/p>\n\n\n\n<h1 class=\"wp-block-heading\" id=\"top\">Git Branching &amp; Merging<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\"><em>Branch types, integration choices, conflicts and safe recovery<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Understand what Git changes, choose the right method, and verify the result. The scenario atlas pairs before-and-after graphs with runnable commands, trade-offs and the failure modes that matter.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Navigate the handbook<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"#mental\">01. The mental model and terminology<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"#branches\">02. Branch types and branching workflows<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"#commands\">03. Everyday commands and remote branches<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"#choice\">04. Choose an outcome and prepare the lab<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"#atlas\">05. Merge scenario atlas: S01-S15<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"#conflicts\">06. Conflict resolution playbook<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"#collaboration\">07. Collaboration, pull requests and releases<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"#recovery\">08. Recovery and troubleshooting<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"#appendix\">Appendix A. Copyable Mermaid diagram source<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"#sources\">Appendix B. Official references and verification<\/a><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The five choices to recognize immediately<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th><strong>Desired outcome<\/strong><\/th><th><strong>Reach for<\/strong><\/th><\/tr><\/thead><tbody><tr><td>Advance a target that has not diverged<\/td><td><code>git merge --ff-only topic<\/code><\/td><\/tr><tr><td>Keep both histories and record integration<\/td><td><code>git merge --no-ff topic<\/code><\/td><\/tr><tr><td>Deliver one commit per short-lived topic<\/td><td><code>git merge --squash topic<\/code>, then commit<\/td><\/tr><tr><td>Replay a private topic into a clean series<\/td><td><code>git rebase main<\/code>, then fast-forward<\/td><\/tr><tr><td>Move one independent fix to another line<\/td><td><code>git cherry-pick -x FIX_SHA<\/code><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Reading key.<\/strong> Commands in the scenario atlas are reproducible after loading the lab helper. Other command blocks are named recipes: adapt the stated branch, remote, file or commit names. Bracketed numbers point to references in Appendix B.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"mental\">01. The mental model<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A <strong>commit<\/strong> records a snapshot and its parent commit(s). A <strong>branch<\/strong> is a movable name for a commit, not another copy of the project. A commit can be reachable from many branches; it does not permanently belong to the branch on which you first created it. [1, 2]<\/p>\n\n\n\n<pre class=\"wp-block-code language-mermaid\" id=\"branch-pointer-diagram\"><code>flowchart LR\n  A[\"A\"] --&gt; B[\"B&lt;br\/&gt;main\"] --&gt; C[\"C&lt;br\/&gt;feature\/login\"]<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Here, <code>main<\/code> points to B and <code>feature\/login<\/code> points to C. With <code>HEAD<\/code> attached to feature\/login, a new commit advances feature\/login, not main. Diagram arrows show older-to-newer ancestry; actual commit objects refer back to their parents. [1]<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th><strong>Term<\/strong><\/th><th><strong>Meaning that matters in practice<\/strong><\/th><\/tr><\/thead><tbody><tr><td>HEAD<\/td><td>Your current position. Usually it refers to the checked-out local branch; in detached HEAD it names a commit directly.<\/td><\/tr><tr><td>Working tree<\/td><td>The files you are editing. Uncommitted edits are not permanently attached to a branch.<\/td><\/tr><tr><td>Index \/ staging area<\/td><td>The proposed next snapshot. <code>git add<\/code> updates it; <code>git commit<\/code> records it.<\/td><\/tr><tr><td>Reference \/ ref \/ tip<\/td><td>A ref names an object; a branch tip is the commit currently named by that branch.<\/td><\/tr><tr><td>Ancestor \/ descendant<\/td><td>A commit is an ancestor when you can reach it by walking parent links from another commit.<\/td><\/tr><tr><td>Merge base<\/td><td>A best common ancestor used for comparison. Complex histories can have more than one.<\/td><\/tr><tr><td>Source \/ target branch<\/td><td>Source supplies changes; target receives them. <code>git merge topic<\/code> modifies the currently checked-out target.<\/td><\/tr><tr><td>Merge commit \/ first parent<\/td><td>A merge commit has multiple parents. The first is normally the previous tip of the branch receiving the merge.<\/td><\/tr><tr><td>DAG \/ tree<\/td><td>The history is a directed acyclic graph. A tree describes a snapshot of paths and content; identical trees need not imply identical history.<\/td><\/tr><tr><td>Tag \/ release<\/td><td>A tag identifies a chosen revision. Publishing or deploying a release is a separate workflow, not something a branch name proves.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Terminology and ancestry: [1, 2, 11, 12].<\/p>\n\n\n\n<pre class=\"wp-block-code language-text\"><code>edit files -&gt; git add -&gt; index -&gt; git commit -&gt; local branch\nlocal branch -&gt; git push -&gt; branch on the remote<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The rule to remember.<\/strong> Switch to the <strong>destination<\/strong> before merging. <code>git switch main<\/code> followed by <code>git merge topic<\/code> means topic into main. Reversing the switch reverses the integration direction.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"branches\">02. Branch types: mechanism versus convention<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Git has reference mechanisms and branch states. Teams add names such as feature, release and hotfix as conventions. The name alone does not change Git behavior. [1, 2]<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th><strong>Form or state<\/strong><\/th><th><strong>What it is<\/strong><\/th><th><strong>Important distinction<\/strong><\/th><\/tr><\/thead><tbody><tr><td>Local branch<\/td><td>A writable local ref such as <code>main<\/code> or <code>feature\/login<\/code>.<\/td><td>Committing advances the checked-out branch.<\/td><\/tr><tr><td>Branch on a remote<\/td><td>A branch stored in another repository, commonly a server.<\/td><td>Push updates it subject to permissions and policy.<\/td><\/tr><tr><td>Remote-tracking ref<\/td><td>Your local last-observed view, such as <code>origin\/main<\/code>.<\/td><td>Fetch refreshes it; it is not a live view of the server.<\/td><\/tr><tr><td>Tracking local branch<\/td><td>A local branch with an upstream association.<\/td><td>The association informs status and pull; it is not automatic synchronization.<\/td><\/tr><tr><td>Unborn \/ orphan branch<\/td><td>An unborn branch has no commit yet. An orphan start creates a new root history.<\/td><td>After its first commit it is a normal branch with independent ancestry. See S11.<\/td><\/tr><tr><td>Protected branch<\/td><td>A hosting-service rule applied to selected branches.<\/td><td>Review, checks and push restrictions are server policy, not a special Git branch object.<\/td><\/tr><tr><td>Detached HEAD<\/td><td>HEAD names a commit rather than a local branch.<\/td><td>Not a branch type. Name new work with <code>git switch -c rescue\/work<\/code> before leaving it.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Reference mechanisms: [6, 9, 10]. Server protection: [20].<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Read the branch state<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>git branch                 # Local branches; * is current\ngit branch -r              # Remote-tracking refs\ngit branch -a              # Both views\ngit branch -vv             # Upstream and ahead\/behind summary\ngit remote -v              # Remote names and URLs<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Create a tracking branch or set its upstream<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">These are separate recipes. The remote branch must already exist for the first one.<\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>git fetch origin\ngit switch --track -c feature\/login origin\/feature\/login\n\n# Existing local branch: change its upstream association.\ngit branch --set-upstream-to=origin\/feature\/login feature\/login<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Three meanings people confuse.<\/strong> <code>origin<\/code> is a conventional remote name. An <strong>upstream branch<\/strong> is a configured tracking relationship. A remote literally named <code>upstream<\/code> is merely another remote, often the original project in a fork workflow. [6, 9]<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A <strong>fork<\/strong> is another repository, not another branch. A <strong>worktree<\/strong> is another working directory attached to the same repository; it is useful for keeping two branches checked out separately. [19, 22]<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Branch roles used by teams<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Choose roles because the delivery process needs them, not because Git requires them. <code>main<\/code>, <code>master<\/code> and <code>trunk<\/code> can all be names for the principal line. The lifecycle below is a recommended naming convention, not a built-in taxonomy.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th><strong>Role \/ example<\/strong><\/th><th><strong>Start from -&gt; integrate into<\/strong><\/th><th><strong>Benefit and cost<\/strong><\/th><\/tr><\/thead><tbody><tr><td>Main \/ trunk<\/td><td>Principal integration line; protect it.<\/td><td>One authoritative integration point; requires a clear quality gate.<\/td><\/tr><tr><td>Develop \/ integration<\/td><td>Usually main initially; receives features in Gitflow.<\/td><td>Separates next-release work; adds another permanent line to maintain.<\/td><\/tr><tr><td>Feature \/ topic<\/td><td>Current main, or develop in Gitflow -&gt; that base.<\/td><td>Isolates one change; long-lived topics accumulate integration risk.<\/td><\/tr><tr><td>Bugfix \/ fix<\/td><td>The affected active line -&gt; the same line.<\/td><td>Focused repair; choosing the wrong base can import unrelated code.<\/td><\/tr><tr><td>Hotfix<\/td><td>Exact deployed tag \/ supported release -&gt; affected release and active development.<\/td><td>Fast, isolated production repair; every affected line still needs the fix.<\/td><\/tr><tr><td>Release<\/td><td>Approved integration point -&gt; release and necessary back-merges.<\/td><td>Allows stabilization; needs a freeze policy and fix propagation.<\/td><\/tr><tr><td>Maintenance \/ support<\/td><td>Released tag -&gt; supported version line.<\/td><td>Supports older versions; each maintained line adds backport and testing cost.<\/td><\/tr><tr><td>Experiment \/ spike<\/td><td>Any explicit base -&gt; merge only if adopted.<\/td><td>Safe exploration; abandoned experiments must not become hidden dependencies.<\/td><\/tr><tr><td>Chore \/ docs \/ refactor \/ test<\/td><td>The active integration line -&gt; that line.<\/td><td>Useful intent labels; they are still ordinary topic branches.<\/td><\/tr><tr><td>Environment branch<\/td><td>A team-defined line such as staging.<\/td><td>Can support a deliberate promotion model; risks drift and environment-only fixes.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Creation recipes<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Run these independently. <code>main<\/code> must exist; <code>v1.2.0<\/code> must be a real released tag. In Gitflow, use develop rather than main where the team requires it. [6, 10, 24]<\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>git switch -c develop main\ngit switch -c feature\/login main\ngit switch -c bugfix\/login-error main\ngit switch -c hotfix\/1.2.1 v1.2.0\ngit switch -c release\/1.3 main\ngit switch -c support\/1.2 v1.2.0\ngit switch -c experiment\/cache main\ngit switch -c chore\/dependencies main\ngit switch -c docs\/install main\ngit switch -c refactor\/auth main\ngit switch -c test\/login main\ngit switch -c staging main<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Choose a branching workflow<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A <strong>branching workflow<\/strong> decides which lines exist and how long they live. A <strong>merge method<\/strong> decides how one integration changes history. Choose them separately; Gitflow does not require every merge to use the same flag.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th><strong>Workflow<\/strong><\/th><th><strong>Structure and good fit<\/strong><\/th><th><strong>Strength<\/strong><\/th><th><strong>Cost \/ risk<\/strong><\/th><\/tr><\/thead><tbody><tr><td>Trunk-based<\/td><td>One principal line; very short-lived changes, often with feature flags. Useful for frequent delivery.<\/td><td>Small integration gaps and rapid feedback.<\/td><td>Requires strong checks, small changes and discipline around incomplete work.<\/td><\/tr><tr><td>GitHub flow<\/td><td>Short-lived branch -&gt; review \/ checks -&gt; main. Useful for simple continuous-delivery teams.<\/td><td>Easy to explain and automate.<\/td><td>Supported old releases need an additional explicit policy.<\/td><\/tr><tr><td>Gitflow<\/td><td>main + develop + feature, release and hotfix branches. Useful for planned, versioned releases.<\/td><td>Clear release-stabilization structure.<\/td><td>More synchronization paths; often excessive for continuously deployed services.<\/td><\/tr><tr><td>Release \/ maintenance lines<\/td><td>main plus selected supported version branches. Useful when multiple versions remain active.<\/td><td>Isolates version-specific fixes.<\/td><td>Backport selection and per-version testing are ongoing work.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Workflow definitions and trade-offs: [22, 23, 24]. The Gitflow author also recommends simpler workflows for continuously delivered web applications. [24]<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>A practical default policy<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For a new web application, start with protected main and short-lived topics. Choose squash when a pull request is the delivery unit, or preserve well-structured commits through merges or rebase-and-fast-forward. Add release branches only when actual release support requires them. This is a starting recommendation, not a universal rule.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Branch names are not deployment evidence<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A branch called production can move without a deployment, and a deployment can use a tag or commit without moving a branch. Record the deployed commit and artifact identity. Where practical, promote the same built artifact through environments rather than rebuilding different code from environment branches.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Keep the model small<\/strong><\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th><strong>Question<\/strong><\/th><th><strong>Decision to record<\/strong><\/th><\/tr><\/thead><tbody><tr><td>Where does new work start?<\/td><td>main, develop, or an explicitly supported release line.<\/td><\/tr><tr><td>What is the review unit?<\/td><td>One logical commit, one topic branch, or one pull request.<\/td><\/tr><tr><td>Who may rewrite a topic?<\/td><td>Its owner, with explicit coordination if it is already shared.<\/td><\/tr><tr><td>How do fixes propagate?<\/td><td>Merge the shared history or cherry-pick an independent fix; test each target.<\/td><\/tr><tr><td>When is a branch deleted?<\/td><td>After integration is verified and no other work depends on it.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"commands\">03. Everyday branching and remote commands<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Start one piece of work<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Recipe for an existing clone with origin and main. Start clean; do not run this over unrelated uncommitted work.<\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>git status --short\ngit fetch --prune origin\ngit switch main\ngit merge --ff-only origin\/main\ngit switch -c feature\/login\n# Edit the intended files, then stage only those files.\ngit add -- path\/to\/changed-file\ngit diff --cached\ngit commit -m \"feat: add login flow\"\ngit push -u origin feature\/login<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Replace <code>path\/to\/changed-file<\/code> with a real changed path. <code>-u<\/code> sets the upstream after a successful push. In a protected repository, finish through a reviewed pull request rather than pushing directly to main. [6, 9, 20]<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th><strong>Action<\/strong><\/th><th><strong>What changes<\/strong><\/th><th><strong>What does not happen automatically<\/strong><\/th><\/tr><\/thead><tbody><tr><td>fetch<\/td><td>Downloads objects and refreshes applicable remote-tracking refs.<\/td><td>Does not integrate those changes into your current local branch.<\/td><\/tr><tr><td>pull<\/td><td>Fetches, then integrates into the current branch using the chosen mode.<\/td><td>Does not choose the correct branch or policy for your team.<\/td><\/tr><tr><td>push<\/td><td>Requests remote ref updates using your local commits.<\/td><td>Does not first fetch and merge someone else&#8217;s changes.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Use explicit pull modes rather than relying on machine-specific defaults. [9, 15]<\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>git pull --ff-only     # Refuse if the local branch diverged.\ngit pull --rebase      # Replay local work; changes history.\ngit pull --no-rebase   # Integrate by merge; FF if permitted.<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Rename and clean up deliberately<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code># Rename an existing local branch; coordinate shared names.\ngit branch -m old-name new-name\ngit push -u origin new-name\n\n# After integration has been verified, leave the topic first.\ngit switch main\ngit branch -d feature\/login\ngit push origin --delete feature\/login\ngit fetch --prune origin<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Deletion is two separate actions.<\/strong> <code>git branch -d<\/code> is local; <code>git push origin --delete<\/code> changes the server. Fetch pruning removes stale remote-tracking refs, not your local branches. After a squash, verify delivery and dependencies before deliberately using <code>-D<\/code>; do not force deletion simply to silence the safety check. [6, 9]<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"choice\">04. Inspect history and choose the outcome<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Read the graph, not just the file diff<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>git log --oneline --graph --decorate --all\ngit log main..topic                 # Commits only on topic\ngit log --left-right --oneline main...topic\ngit diff main topic                # Compare the two tip trees\ngit diff main...topic              # Merge base -&gt; topic tree\ngit diff --cached --check          # Staged whitespace\/marker checks<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">For <strong>log<\/strong>, two dots select commits reachable from the right but not the left; three dots select the symmetric difference. For <strong>diff<\/strong>, three dots compare the merge base with the right-hand tip. Do not assume log and diff interpret the notation identically. [12, 16]<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th><strong>Method<\/strong><\/th><th><strong>Original source commits<\/strong><\/th><th><strong>New integration commit<\/strong><\/th><th><strong>Best reason to choose it<\/strong><\/th><\/tr><\/thead><tbody><tr><td>Fast-forward<\/td><td>Kept unchanged<\/td><td>None<\/td><td>Advance an already-compatible target.<\/td><\/tr><tr><td>Normal \/ &#8211;no-ff merge<\/td><td>Kept unchanged<\/td><td>A multi-parent merge, unless no-op \/ permitted FF<\/td><td>Preserve shared history and integration ancestry.<\/td><\/tr><tr><td>Squash merge<\/td><td>Remain on source; not linked as merged parents<\/td><td>One ordinary commit after you commit<\/td><td>Make one topic the delivery unit.<\/td><\/tr><tr><td>Rebase + FF<\/td><td>Replayed commits get new IDs when rewritten<\/td><td>No merge commit<\/td><td>Publish a clean, private patch series.<\/td><\/tr><tr><td>Cherry-pick<\/td><td>Source stays; selected patch is copied<\/td><td>Usually one ordinary commit per pick<\/td><td>Move a particular independent fix.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">A fast-forward does not flatten merge commits that already exist in the source history. The table describes the integration step, not a guarantee that the entire graph is linear. [3-7]<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Methods are not algorithms<\/strong><\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th><strong>Algorithm \/ option<\/strong><\/th><th><strong>Meaning<\/strong><\/th><\/tr><\/thead><tbody><tr><td>ort<\/td><td>Current default for a two-head merge; handles common-base comparison and rename detection.<\/td><\/tr><tr><td>recursive<\/td><td>Historical implementation; since Git 2.50 it is an alias for ort. Older installations differ.<\/td><\/tr><tr><td>resolve<\/td><td>Older two-head strategy without rename handling; rarely needed for routine work.<\/td><\/tr><tr><td>octopus<\/td><td>Combines multiple topic heads; refuses complex manual conflict resolution.<\/td><\/tr><tr><td>ours \/ subtree<\/td><td>Special history-preserving or layout-aware strategies. See S13 \/ S14.<\/td><\/tr><tr><td>-s versus -X<\/td><td><code>-s<\/code> selects a strategy; <code>-X<\/code> supplies an option to it. Squash, &#8211;ff and &#8211;no-ff are not strategy names.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Algorithm details: [8].<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"lab\">Prepare the disposable learning lab<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Run the helper below once in <strong>Bash or Zsh<\/strong>: macOS, Linux, or Git Bash on Windows. Every <code>lab MODE<\/code> call creates and enters a new temporary repository. It never reuses your project directory. Git 2.34+ covers the commands used here; the examples were executed with Git 2.47.3.<\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>lab() {\n  case \"$1\" in\n    base|ff|split|conflict) ;;\n    *) echo 'Use: lab base|ff|split|conflict'; return 1 ;;\n  esac\n  cd \"$(mktemp -d \"${TMPDIR:-\/tmp}\/git-lab.XXXXXX\")\" || return\n  git init -q -b main\n  git config user.name 'Git Learner'\n  git config user.email 'learner@example.invalid'\n  git config commit.gpgsign false\n  git config merge.ff true\n  git config rerere.enabled false\n  printf 'color=blue\\n' &gt; app.txt\n  git add app.txt; git commit -qm A\n  git branch base\n  [ \"$1\" = base ] &amp;&amp; return 0\n  git switch -q -c topic\n  if [ \"$1\" = conflict ]; then\n    printf 'color=green\\n' &gt; app.txt\n  else\n    printf 'feature\\n' &gt; feature.txt\n  fi\n  git add .; git commit -qm B\n  printf 'tests\\n' &gt; test.txt\n  git add test.txt; git commit -qm C\n  git switch -q main\n  if [ \"$1\" != ff ]; then\n    if [ \"$1\" = conflict ]; then\n      printf 'color=red\\n' &gt; app.txt\n    else\n      printf 'guide\\n' &gt; guide.txt\n    fi\n    git add .; git commit -qm D\n  fi\n  printf 'Disposable lab: %s\\n' \"$PWD\"\n}<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th><strong>Mode<\/strong><\/th><th><strong>Starting history<\/strong><\/th><\/tr><\/thead><tbody><tr><td><code>lab base<\/code><\/td><td>Only A on main. A helper branch named base stays at A.<\/td><\/tr><tr><td><code>lab ff<\/code><\/td><td>main at A; topic adds B and C.<\/td><\/tr><tr><td><code>lab split<\/code><\/td><td>main adds D; topic adds B and C in separate files.<\/td><\/tr><tr><td><code>lab conflict<\/code><\/td><td>main changes app.txt to red; topic changes it to green, then adds test.txt.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">All graphs omit the helper base ref. Letters are commit-message labels, not literal hashes. The scenarios are independent: always run their first lab command. Expected conflicts and refusals are intentional; do not run those blocks inside a shell configured to exit on the first non-zero command.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"atlas\">05. Merge scenario atlas<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"S01\">S01 | Fast-forward: move the branch pointer<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><em>EVERYDAY<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Use this when main has not gained its own commits since topic branched off. The target can move from A to C without creating a new commit.<\/p>\n\n\n\n<pre class=\"wp-block-code language-mermaid\" id=\"mermaid_S01\"><code>flowchart LR\nsubgraph Before\n  bA[\"A&lt;br\/&gt;main\"] --&gt; bB[\"B\"] --&gt; bC[\"C&lt;br\/&gt;topic\"]\nend\nsubgraph After\n  aA[\"A\"] --&gt; aB[\"B\"] --&gt; aC[\"C&lt;br\/&gt;main + topic\"]\nend<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Older commits point toward newer commits. Mermaid source: <a href=\"#mermaid_S01\">S01<\/a><\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>lab ff\ngit switch main\ngit merge --ff-only topic\ngit log --oneline --graph --decorate --all<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Result.<\/strong> main and topic now point to C. B and C keep their identities; no merge commit exists. Only main moved during the merge.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th><strong>Pros<\/strong><\/th><th><strong>Cons<\/strong><\/th><\/tr><\/thead><tbody><tr><td>Small, linear history; original commits remain intact; refusal protects a fast-forward-only policy.<\/td><td>No explicit integration boundary. It cannot combine two diverged tips.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Watch out.<\/strong> Fast-forward does not mean one commit or squashed commits. Every commit already on topic remains in history.<\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>git rev-parse main topic<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Expected:<\/strong> Both lines print the same commit ID.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Reference: [3, 4]<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"S02\">S02 | Force a merge commit with &#8211;no-ff<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><em>EVERYDAY<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Use this when the history is fast-forwardable, but the team wants an explicit record that a topic was integrated.<\/p>\n\n\n\n<pre class=\"wp-block-code language-mermaid\" id=\"mermaid_S02\"><code>flowchart LR\nsubgraph Before\n  bA[\"A&lt;br\/&gt;main\"] --&gt; bB[\"B\"] --&gt; bC[\"C&lt;br\/&gt;topic\"]\nend\nsubgraph After\n  aA[\"A\"] --&gt; aB[\"B\"] --&gt; aC[\"C&lt;br\/&gt;topic\"] --&gt; aM[\"M&lt;br\/&gt;main\"]\n  aA --&gt; aM\nend<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Older commits point toward newer commits. Mermaid source: <a href=\"#mermaid_S02\">S02<\/a><\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>lab ff\ngit switch main\ngit merge --no-ff topic -m \"M: integrate topic\"\ngit show -s --format='%h %p %s' HEAD<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Result.<\/strong> M has two parents: A first, C second. The source branch stays at C. The snapshot includes the topic changes.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th><strong>Pros<\/strong><\/th><th><strong>Cons<\/strong><\/th><\/tr><\/thead><tbody><tr><td>Preserves original commits and a visible feature boundary. First-parent logs give a concise integration timeline.<\/td><td>Adds a commit even when pointer movement would suffice; heavy use can clutter a small repository.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Watch out.<\/strong> &#8211;no-ff does not manufacture a merge when the input is already an ancestor of HEAD. That case is still a no-op.<\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>git log --first-parent --oneline main<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Expected:<\/strong> M appears as the integration step on main.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Reference: [3, 4]<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"S03\">S03 | Three-way merge: both branches advanced<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><em>EVERYDAY<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">main contains D while topic contains B and C. A is their common ancestor. Git combines the changes from A to D with those from A to C.<\/p>\n\n\n\n<pre class=\"wp-block-code language-mermaid\" id=\"mermaid_S03\"><code>flowchart LR\nsubgraph Before\n  bA[\"A\"] --&gt; bD[\"D&lt;br\/&gt;main\"]\n  bA --&gt; bB[\"B\"] --&gt; bC[\"C&lt;br\/&gt;topic\"]\nend\nsubgraph After\n  aA[\"A\"] --&gt; aD[\"D\"] --&gt; aM[\"M&lt;br\/&gt;main\"]\n  aA --&gt; aB[\"B\"] --&gt; aC[\"C&lt;br\/&gt;topic\"] --&gt; aM\nend<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Older commits point toward newer commits. Mermaid source: <a href=\"#mermaid_S03\">S03<\/a><\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>lab split\ngit switch main\ngit merge topic -m \"M: combine both histories\"\ngit show -s --format='%h %p %s' HEAD<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Result.<\/strong> M joins D and C. Both histories remain reachable. This lab merges cleanly because the sides add different files.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th><strong>Pros<\/strong><\/th><th><strong>Cons<\/strong><\/th><\/tr><\/thead><tbody><tr><td>Keeps shared history stable and preserves both lines of development; appropriate for collaboratively edited branches.<\/td><td>Creates a non-linear history; unrelated-looking edits can still interact badly at runtime.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Watch out.<\/strong> A three-way merge is not a three-parent merge. It compares a base and two tips, usually producing a two-parent commit.<\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>git diff HEAD^1 HEAD --stat<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Expected:<\/strong> The diff shows what this integration added relative to the previous main tip.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Reference: [3, 4, 12]<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"S04\">S04 | Three-way merge with a conflict<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><em>EVERYDAY<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Both branches change the same setting differently: main wants red; topic wants green. Git stops for a human decision. The after graph exists only after resolution and commit.<\/p>\n\n\n\n<pre class=\"wp-block-code language-mermaid\" id=\"mermaid_S04\"><code>flowchart LR\nsubgraph Before\n  bA[\"A\"] --&gt; bD[\"D&lt;br\/&gt;main\"]\n  bA --&gt; bB[\"B\"] --&gt; bC[\"C&lt;br\/&gt;topic\"]\nend\nsubgraph After\n  aA[\"A\"] --&gt; aD[\"D\"] --&gt; aM[\"M&lt;br\/&gt;main\"]\n  aA --&gt; aB[\"B\"] --&gt; aC[\"C&lt;br\/&gt;topic\"] --&gt; aM\nend<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Older commits point toward newer commits. Mermaid source: <a href=\"#mermaid_S04\">S04<\/a><\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>lab conflict\ngit merge topic\n# Expected conflict: inspect before deciding.\ngit status --short\ncat app.txt\n# Lab decision: the agreed final value is purple.\nprintf 'color=purple\\n' &gt; app.txt\ngit add app.txt\ngit diff --cached --check\ngit commit -m \"M: resolve color deliberately\"<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Result.<\/strong> During the conflict, HEAD remains at D and no M exists. After committing, M has D and C as parents and contains the chosen resolution.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th><strong>Pros<\/strong><\/th><th><strong>Cons<\/strong><\/th><\/tr><\/thead><tbody><tr><td>Preserves both histories and makes the integration decision reviewable.<\/td><td>Requires judgment and regression tests; choosing a side blindly can discard valid behavior.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Watch out.<\/strong> git add marks a path resolved; it does not prove the result is correct. To abandon an ordinary in-progress merge, use git merge &#8211;abort.<\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>git status --short\ngit show HEAD:app.txt<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Expected:<\/strong> The status is clean and the file contains color=purple.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Reference: [4, 13]<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"S05A\">S05A | Already up to date: nothing to merge<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><em>GUARDRAIL<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Every commit reachable from topic is already reachable from main. Repeating the merge changes nothing; the tips need not be equal for this rule to hold.<\/p>\n\n\n\n<pre class=\"wp-block-code language-mermaid\" id=\"mermaid_S05A\"><code>flowchart LR\nsubgraph Before\n  bA[\"A\"] --&gt; bB[\"B\"] --&gt; bC[\"C&lt;br\/&gt;main + topic\"]\nend\nsubgraph After\n  aA[\"A\"] --&gt; aB[\"B\"] --&gt; aC[\"C&lt;br\/&gt;main + topic\"]\nend<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Older commits point toward newer commits. Mermaid source: <a href=\"#mermaid_S05A\">S05A<\/a><\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>lab ff\ngit merge --ff-only topic   # Prepare the BEFORE graph.\ngit merge topic             # Already up to date.<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Result.<\/strong> No commit is created and neither pointer moves.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Pros:<\/strong> Safe and idempotent. <strong>Cons:<\/strong> It checks ancestry, not whether a past change was later reverted.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Watch out.<\/strong> After a revert or an ours-strategy merge, Git can say this even though the files do not contain the topic behavior.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Reference: [4, 11]<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"S05B\">S05B | Fast-forward-only refuses divergence<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><em>GUARDRAIL<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The target and source each have unique commits. &#8211;ff-only refuses rather than choosing a merge or rebase for you.<\/p>\n\n\n\n<pre class=\"wp-block-code language-mermaid\" id=\"mermaid_S05B\"><code>flowchart LR\nsubgraph Before\n  bA[\"A\"] --&gt; bD[\"D&lt;br\/&gt;main\"]\n  bA --&gt; bB[\"B\"] --&gt; bC[\"C&lt;br\/&gt;topic\"]\nend\nsubgraph After\n  aA[\"A\"] --&gt; aD[\"D&lt;br\/&gt;main\"]\n  aA --&gt; aB[\"B\"] --&gt; aC[\"C&lt;br\/&gt;topic\"]\nend<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Older commits point toward newer commits. Mermaid source: <a href=\"#mermaid_S05B\">S05B<\/a><\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>lab split\ngit merge --ff-only topic   # Expected non-zero exit.\ngit status --short<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Result.<\/strong> The committed graph and branch pointers stay unchanged. This is a policy refusal, not a merge conflict.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Pros:<\/strong> Prevents accidental merge commits. <strong>Cons:<\/strong> You must deliberately select another integration method.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Watch out.<\/strong> Do not answer this error with reset &#8211;hard or force-push. Choose a normal merge or a permitted rebase after inspecting the graph.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Reference: [4]<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"S06\">S06 | Squash merge: one delivery commit<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><em>EVERYDAY<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Use this when a short-lived topic contains useful work but noisy intermediate commits. The destination receives one ordinary commit containing the combined result.<\/p>\n\n\n\n<pre class=\"wp-block-code language-mermaid\" id=\"mermaid_S06\"><code>flowchart LR\nsubgraph Before\n  bA[\"A\"] --&gt; bD[\"D&lt;br\/&gt;main\"]\n  bA --&gt; bB[\"B\"] --&gt; bC[\"C&lt;br\/&gt;topic\"]\nend\nsubgraph After\n  aA[\"A\"] --&gt; aD[\"D\"] --&gt; aS[\"S&lt;br\/&gt;main\"]\n  aA --&gt; aB[\"B\"] --&gt; aC[\"C&lt;br\/&gt;topic\"]\nend<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Older commits point toward newer commits. Mermaid source: <a href=\"#mermaid_S06\">S06<\/a><\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>lab split\ngit merge --squash topic\ngit diff --cached\ngit diff --cached --check\ngit commit -m \"S: deliver feature and tests\"<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Result.<\/strong> S has only D as its parent. There is deliberately no ancestry edge from C to S. B and C still exist on topic, but not as its merged ancestors.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th><strong>Pros<\/strong><\/th><th><strong>Cons<\/strong><\/th><\/tr><\/thead><tbody><tr><td>One reviewable delivery unit; concise main history; easy to revert the complete change as an ordinary commit.<\/td><td>Loses per-commit detail on main and does not record topic ancestry. Reusing the old topic can repeat changes or conflicts.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Watch out.<\/strong> &#8211;squash does not commit for you and does not set MERGE_HEAD. Use a fresh branch from updated main for the next task. git branch -d may refuse the old topic.<\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>git show -s --format=%p HEAD<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Expected:<\/strong> Exactly one parent ID is printed. Do not treat ancestry checks as squash-verification checks.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Reference: [4, 21]<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"S07\">S07 | Rebase, then fast-forward<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><em>EVERYDAY &#8211; REWRITES TOPIC<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Rebase replays the topic commits on top of current main; then a fast-forward integrates the replayed series. Rebase itself is not a merge.<\/p>\n\n\n\n<pre class=\"wp-block-code language-mermaid\" id=\"mermaid_S07\"><code>flowchart LR\nsubgraph Before\n  bA[\"A\"] --&gt; bD[\"D&lt;br\/&gt;main\"]\n  bA --&gt; bB[\"B\"] --&gt; bC[\"C&lt;br\/&gt;topic\"]\nend\nsubgraph After\n  aA[\"A\"] --&gt; aD[\"D\"] --&gt; aBP[\"B'\"] --&gt; aCP[\"C'&lt;br\/&gt;main + topic\"]\nend<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Older commits point toward newer commits. Mermaid source: <a href=\"#mermaid_S07\">S07<\/a><\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>lab split\ngit switch topic\ngit branch backup\/topic-before-rebase\ngit rebase main\ngit switch main\ngit merge --ff-only topic<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Result.<\/strong> D is followed by B&#8217; and C&#8217;. These replayed commits have new identities. The backup still retains B and C; it is omitted from the diagram.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th><strong>Pros<\/strong><\/th><th><strong>Cons<\/strong><\/th><\/tr><\/thead><tbody><tr><td>Linear main history while retaining separate logical commits; useful for a privately owned, well-organized topic.<\/td><td>Rewrites the topic; conflicts can recur per replayed commit; coordinating already-published history takes extra care.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Watch out.<\/strong> Do not rebase shared main or surprise collaborators on a shared topic. A no-op rebase need not recreate commits; the changed-parent example here does.<\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>git log --oneline --graph main<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Expected:<\/strong> The main line is A, D, B&#8217;, C&#8217; with no merge commit.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Reference: [5, 6]<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"S08\">S08 | Cherry-pick: bring across one change<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><em>SELECTIVE INTEGRATION<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Use this to backport a specific fix without importing its entire source branch. Here B adds feature.txt independently; C adds unrelated tests and is intentionally not selected.<\/p>\n\n\n\n<pre class=\"wp-block-code language-mermaid\" id=\"mermaid_S08\"><code>flowchart LR\nsubgraph Before\n  bA[\"A\"] --&gt; bD[\"D&lt;br\/&gt;main\"]\n  bA --&gt; bB[\"B\"] --&gt; bC[\"C&lt;br\/&gt;topic\"]\nend\nsubgraph After\n  aA[\"A\"] --&gt; aD[\"D\"] --&gt; aBP[\"B'&lt;br\/&gt;main\"]\n  aA --&gt; aB[\"B\"] --&gt; aC[\"C&lt;br\/&gt;topic\"]\nend<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Older commits point toward newer commits. Mermaid source: <a href=\"#mermaid_S08\">S08<\/a><\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>lab split\ngit switch main\ngit show topic~1 --stat\ngit cherry-pick -x topic~1<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Result.<\/strong> A new B&#8217; follows D. The source still points to C. There is no merge-parent edge between the two lines; cherry-pick is not a history merge.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th><strong>Pros<\/strong><\/th><th><strong>Cons<\/strong><\/th><\/tr><\/thead><tbody><tr><td>Precise selection; useful across supported release lines; -x records the source commit for traceability in the clean example.<\/td><td>Can omit dependencies or tests; creates parallel patch histories and extra maintenance work.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Watch out.<\/strong> Inspect prerequisites first. A fix may require earlier commits or a release-specific adaptation. -x does not make the source an ancestor.<\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>git show --stat HEAD\ngit log -1 --format=%B<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Expected:<\/strong> Only B&#8217;s selected change is applied and the message includes its source commit.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Reference: [7]<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"S09\">S09 | Merge main into a feature branch<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><em>EVERYDAY &#8211; SYNC DIRECTION<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Use this to update a shared topic without rewriting it. The target is topic, so the merge belongs on topic, not on main.<\/p>\n\n\n\n<pre class=\"wp-block-code language-mermaid\" id=\"mermaid_S09\"><code>flowchart LR\nsubgraph Before\n  bA[\"A\"] --&gt; bD[\"D&lt;br\/&gt;main\"]\n  bA --&gt; bB[\"B\"] --&gt; bC[\"C&lt;br\/&gt;topic\"]\nend\nsubgraph After\n  aA[\"A\"] --&gt; aD[\"D&lt;br\/&gt;main\"] --&gt; aM[\"M&lt;br\/&gt;topic\"]\n  aA --&gt; aB[\"B\"] --&gt; aC[\"C\"] --&gt; aM\nend<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Older commits point toward newer commits. Mermaid source: <a href=\"#mermaid_S09\">S09<\/a><\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>lab split\ngit switch topic\ngit merge main -m \"M: sync main into topic\"\ngit show -s --format='%h %p %s' HEAD<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Result.<\/strong> M has C as its first parent and D as its second. main remains at D. Integrating main into topic does not deliver topic into main.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th><strong>Pros<\/strong><\/th><th><strong>Cons<\/strong><\/th><\/tr><\/thead><tbody><tr><td>Safe for shared topic history; conflicts are addressed before final review; no forced update is needed.<\/td><td>Repeated syncs add merge commits to the feature history; frequent small integrations are easier to review.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Watch out.<\/strong> In a real clone, fetch and merge origin\/main when local main may be stale. Recheck the destination branch before every merge.<\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>git rev-parse main\ngit rev-parse topic^1 topic^2<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Expected:<\/strong> main still names D; the two parent lines name C and D, respectively.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Reference: [4, 9]<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"S10\">S10 | Octopus: merge several independent topics<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><em>SPECIALIZED<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">An octopus merge bundles multiple topic heads into one integration commit. Use it only for independent changes that merge without complex manual resolution.<\/p>\n\n\n\n<pre class=\"wp-block-code language-mermaid\" id=\"mermaid_S10\"><code>flowchart LR\nsubgraph Before\n  bA[\"A\"] --&gt; bD[\"D&lt;br\/&gt;main\"]\n  bA --&gt; bB[\"B&lt;br\/&gt;feature\/ui\"]\n  bA --&gt; bC[\"C&lt;br\/&gt;feature\/api\"]\nend\nsubgraph After\n  aA[\"A\"] --&gt; aD[\"D\"] --&gt; aM[\"M&lt;br\/&gt;main\"]\n  aA --&gt; aB[\"B&lt;br\/&gt;feature\/ui\"] --&gt; aM\n  aA --&gt; aC[\"C&lt;br\/&gt;feature\/api\"] --&gt; aM\nend<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Older commits point toward newer commits. Mermaid source: <a href=\"#mermaid_S10\">S10<\/a><\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>lab base\ngit switch -c feature\/ui\nprintf 'ui\\n' &gt; ui.txt\ngit add ui.txt; git commit -m B\ngit switch -c feature\/api base\nprintf 'api\\n' &gt; api.txt\ngit add api.txt; git commit -m C\ngit switch main\nprintf 'ops\\n' &gt; ops.txt\ngit add ops.txt; git commit -m D\ngit merge --no-ff feature\/ui feature\/api -m M<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Result.<\/strong> M has three parents in this example: D, B and C. Both feature branch pointers remain unchanged.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th><strong>Pros<\/strong><\/th><th><strong>Cons<\/strong><\/th><\/tr><\/thead><tbody><tr><td>One integration point for several independent topics.<\/td><td>Harder to review or partially revert. The octopus strategy refuses complex merges needing manual conflict resolution.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Watch out.<\/strong> Several regular two-branch merges are usually clearer for application teams. This is not the meaning of a three-way merge.<\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>git show -s --format=%p HEAD<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Expected:<\/strong> Three parent IDs are printed.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Reference: [8]<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"S11\">S11 | Merge histories with no common ancestor<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><em>SPECIALIZED<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Use this only for an intentional repository-history import. Git normally refuses histories that share no ancestor.<\/p>\n\n\n\n<pre class=\"wp-block-code language-mermaid\" id=\"mermaid_S11\"><code>flowchart LR\nsubgraph Before\n  bA[\"A\"] --&gt; bD[\"D&lt;br\/&gt;main\"]\n  bX[\"X&lt;br\/&gt;imported\"]\nend\nsubgraph After\n  aA[\"A\"] --&gt; aD[\"D\"] --&gt; aM[\"M&lt;br\/&gt;main\"]\n  aX[\"X&lt;br\/&gt;imported\"] --&gt; aM\nend<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Older commits point toward newer commits. Mermaid source: <a href=\"#mermaid_S11\">S11<\/a><\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>lab base\ngit switch --orphan imported\nprintf 'Imported project\\n' &gt; IMPORTED.md\ngit add IMPORTED.md; git commit -m X\ngit switch main\nprintf 'Local guide\\n' &gt; guide.txt\ngit add guide.txt; git commit -m D\ngit merge --allow-unrelated-histories --no-ff \\\n  imported -m \"M: import independent history\"<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Result.<\/strong> M connects the formerly separate roots. The example keeps both projects at the repository root because their file names do not collide.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th><strong>Pros<\/strong><\/th><th><strong>Cons<\/strong><\/th><\/tr><\/thead><tbody><tr><td>Preserves the imported history in one repository.<\/td><td>Can create add\/add conflicts or a confusing layout; licensing, provenance and sensitive-history review are separate requirements.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Watch out.<\/strong> Do not use this flag to bypass a wrong remote, wrong repository, or incomplete shallow fetch. An orphan branch is not automatically safe to merge.<\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>git rev-list --max-parents=0 main<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Expected:<\/strong> Two root commit IDs are reachable from main.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Reference: [4, 10]<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"S12\">S12 | Prefer a side for conflicting hunks: -X<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><em>SPECIALIZED &#8211; REVIEW CAREFULLY<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">-X ours or -X theirs changes how the normal merge resolves conflicting hunks. Non-conflicting changes from both sides still participate. Both choices produce the same ancestry graph below.<\/p>\n\n\n\n<pre class=\"wp-block-code language-mermaid\" id=\"mermaid_S12\"><code>flowchart LR\nsubgraph Before\n  bA[\"A\"] --&gt; bD[\"D&lt;br\/&gt;main\"]\n  bA --&gt; bB[\"B\"] --&gt; bC[\"C&lt;br\/&gt;topic\"]\nend\nsubgraph After\n  aA[\"A\"] --&gt; aD[\"D\"] --&gt; aM[\"M&lt;br\/&gt;main\"]\n  aA --&gt; aB[\"B\"] --&gt; aC[\"C&lt;br\/&gt;topic\"] --&gt; aM\nend<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Older commits point toward newer commits. Mermaid source: <a href=\"#mermaid_S12\">S12<\/a><\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>lab conflict\ngit merge --no-ff -s ort -X ours topic -m M\ncat app.txt        # color=red\ncat test.txt       # tests: incoming clean change kept\n\n# Alternative: repeat in a NEW lab, not the merged one.\nlab conflict\ngit merge --no-ff -s ort -X theirs topic -m M\ncat app.txt        # color=green\ncat test.txt       # tests: still present<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Result.<\/strong> With ours, M keeps red for the conflicting line. With theirs, M keeps green. Both retain test.txt from the incoming topic.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th><strong>Pros<\/strong><\/th><th><strong>Cons<\/strong><\/th><\/tr><\/thead><tbody><tr><td>Useful when a reviewed policy genuinely decides conflicts of this kind.<\/td><td>Can silently choose the wrong behavior. Some structural conflicts still require intervention.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Watch out.<\/strong> -X ours is not -s ours, and -s theirs is not a built-in strategy. During rebase, the usual intuitive meaning of ours\/theirs is reversed; see the conflict playbook.<\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>git show -s --format=%p HEAD<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Expected:<\/strong> The selected variant has a normal two-parent merge commit.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Reference: [8, 13]<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"S13\">S13 | Record ancestry but keep our whole tree<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><em>SPECIALIZED &#8211; HIGH RISK<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The ours strategy records the other history as merged while keeping the destination snapshot unchanged. It is a deliberate history-management operation, not normal conflict resolution.<\/p>\n\n\n\n<pre class=\"wp-block-code language-mermaid\" id=\"mermaid_S13\"><code>flowchart LR\nsubgraph Before\n  bA[\"A\"] --&gt; bD[\"D&lt;br\/&gt;main\"]\n  bA --&gt; bB[\"B\"] --&gt; bC[\"C&lt;br\/&gt;topic\"]\nend\nsubgraph After\n  aA[\"A\"] --&gt; aD[\"D\"] --&gt; aM[\"M&lt;br\/&gt;main\"]\n  aA --&gt; aB[\"B\"] --&gt; aC[\"C&lt;br\/&gt;topic\"] --&gt; aM\nend<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Older commits point toward newer commits. Mermaid source: <a href=\"#mermaid_S13\">S13<\/a><\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>lab split\ngit merge --no-ff -s ours topic \\\n  -m \"M: supersede topic without taking its files\"\ngit diff HEAD^1 HEAD\ngit merge-base --is-ancestor topic HEAD<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Result.<\/strong> M has two parents, but its tree equals D exactly. Neither feature.txt nor test.txt is taken from topic. Future merges consider C already integrated.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th><strong>Pros<\/strong><\/th><th><strong>Cons<\/strong><\/th><\/tr><\/thead><tbody><tr><td>Can retire or supersede a history deliberately while preserving the ancestry record.<\/td><td>Drops all incoming content, including non-conflicting work; may prevent later merges from introducing changes you actually wanted.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Watch out.<\/strong> An ancestry check returning success is not proof that the source behavior exists in the target. Never use -s ours as a convenient conflict shortcut.<\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>git diff --exit-code HEAD^1 HEAD<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Expected:<\/strong> Exit status 0 and no output: the destination tree did not change.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Reference: [8, 11]<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"S14\">S14 | Subtree-aware merge: align directory layouts<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><em>SPECIALIZED<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Use a subtree-aware merge when the incoming project root corresponds to a subdirectory in the destination. Here app.txt lives at the vendor root, but under vendor\/widget on main.<\/p>\n\n\n\n<pre class=\"wp-block-code language-mermaid\" id=\"mermaid_S14\"><code>flowchart LR\nsubgraph Before\n  bA[\"A\"] --&gt; bD[\"D&lt;br\/&gt;main\"]\n  bA --&gt; bV[\"V&lt;br\/&gt;vendor\"]\nend\nsubgraph After\n  aA[\"A\"] --&gt; aD[\"D\"] --&gt; aM[\"M&lt;br\/&gt;main\"]\n  aA --&gt; aV[\"V&lt;br\/&gt;vendor\"] --&gt; aM\nend<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Older commits point toward newer commits. Mermaid source: <a href=\"#mermaid_S14\">S14<\/a><\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>lab base\ngit switch -c vendor base\nprintf 'color=green\\n' &gt; app.txt\ngit commit -am \"V: update vendor file\"\ngit switch main\nmkdir -p vendor\/widget\ngit mv app.txt vendor\/widget\/app.txt\ngit commit -m \"D: place library in a subdirectory\"\ngit merge --no-ff -s ort -Xsubtree=vendor\/widget \\\n  vendor -m \"M: align vendor layout\"\ncat vendor\/widget\/app.txt<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Result.<\/strong> M keeps the destination layout and applies the vendor update under vendor\/widget. The file there contains color=green; no root app.txt is created.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th><strong>Pros<\/strong><\/th><th><strong>Cons<\/strong><\/th><\/tr><\/thead><tbody><tr><td>Accommodates intentional directory-layout differences while retaining history.<\/td><td>Requires correct prefix mapping and review of rename\/path behavior. A wrong mapping can produce an unwanted tree.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Watch out.<\/strong> -s subtree guesses alignment; -Xsubtree=PATH makes the mapping explicit with ort. Neither is the separate git subtree add\/pull workflow or a submodule.<\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>git ls-tree -r --name-only HEAD<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Expected:<\/strong> The file appears at vendor\/widget\/app.txt.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Reference: [8]<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"S15\">S15 | Interactive rebase: polish before integration<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><em>HISTORY EDITING &#8211; PRIVATE TOPIC<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Use interactive rebase to reorder, edit, combine or remove private topic commits intentionally. In this example, keep B as pick and change the line for C from pick to fixup in the editor, then save and close.<\/p>\n\n\n\n<pre class=\"wp-block-code language-mermaid\" id=\"mermaid_S15\"><code>flowchart LR\nsubgraph Before\n  bA[\"A\"] --&gt; bD[\"D&lt;br\/&gt;main\"]\n  bA --&gt; bB[\"B\"] --&gt; bC[\"C&lt;br\/&gt;topic\"]\nend\nsubgraph After\n  aA[\"A\"] --&gt; aD[\"D\"] --&gt; aS[\"S&lt;br\/&gt;main + topic\"]\nend<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Older commits point toward newer commits. Mermaid source: <a href=\"#mermaid_S15\">S15<\/a><\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>lab split\ngit switch topic\ngit branch backup\/topic-before-edit\ngit rebase -i main\n# Save the todo list: pick B, then fixup C.\ngit switch main\ngit merge --ff-only topic<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Result.<\/strong> S combines the changes from B and C on top of D and keeps the B message. Both main and topic move to this rewritten series. The backup still retains the old topic and is omitted above.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th><strong>Pros<\/strong><\/th><th><strong>Cons<\/strong><\/th><\/tr><\/thead><tbody><tr><td>Produces intentional, reviewable commits before publication; fixup removes correction noise without keeping every intermediate message.<\/td><td>Rewrites the topic. Dropping or reordering commits can remove behavior or violate dependencies; reviewers must inspect the rewritten result.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Watch out.<\/strong> This differs from git merge &#8211;squash: interactive rebase rewrites the source branch itself. Use squash instead of fixup in the todo list when you need to combine and edit commit messages.<\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>git log --oneline main\ngit show --stat HEAD<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Expected:<\/strong> The main line is A, D, S; S contains both feature.txt and test.txt.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Reference: [5, 29]<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"conflicts\">06. Conflict resolution: inspect, decide, finish<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A conflict means Git cannot safely choose a result mechanically. It does not mean either branch is wrong. First establish the intended behavior; then edit, stage, test and complete the correct operation. [13, 30]<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Use the base to understand both changes<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>git config merge.conflictStyle diff3\n# Set before starting the merge; diff3 includes the base text.<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code language-text\"><code>&lt;&lt;&lt;&lt;&lt;&lt;&lt; HEAD\ncolor=red\n||||||| base\ncolor=blue\n=======\ncolor=green\n&gt;&gt;&gt;&gt;&gt;&gt;&gt; topic<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">In an ordinary merge, the top section is your current target, the middle is the common base, and the bottom is the incoming source. The correct result may be either side or a new combined solution. Remove all markers after deciding. [4, 13]<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>A reliable resolution sequence<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>git status\ngit diff --name-only --diff-filter=U\ngit ls-files -u\ngit show :1:app.txt       # Base version, if present\ngit show :2:app.txt       # Ours in this ordinary merge\ngit show :3:app.txt       # Theirs in this ordinary merge\n# Edit app.txt to the intended final content.\ngit add -- app.txt\ngit diff --cached\ngit diff --cached --check\n# Run the relevant build and tests before completion.\ngit merge --continue<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Some conflict types do not have all three index stages. <code>git diff --cached --check<\/code> can catch whitespace errors and conflict markers; it cannot validate application behavior. [13, 16]<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th><strong>Operation in progress<\/strong><\/th><th><strong>Complete after resolving<\/strong><\/th><th><strong>Cancel<\/strong><\/th><\/tr><\/thead><tbody><tr><td>Ordinary merge<\/td><td><code>git merge --continue<\/code><\/td><td><code>git merge --abort<\/code><\/td><\/tr><tr><td>Rebase<\/td><td><code>git rebase --continue<\/code><\/td><td><code>git rebase --abort<\/code><\/td><\/tr><tr><td>Cherry-pick<\/td><td><code>git cherry-pick --continue<\/code><\/td><td><code>git cherry-pick --abort<\/code><\/td><\/tr><tr><td>Revert<\/td><td><code>git revert --continue<\/code><\/td><td><code>git revert --abort<\/code><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Do not skip a commit to escape a conflict.<\/strong> <code>git rebase --skip<\/code> and <code>git cherry-pick --skip<\/code> omit the current patch. Use them only after proving that omission is intentional. <code>--quit<\/code> is not equivalent to aborting and restoring the previous state. [5, 7, 14]<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Conflict types and side-selection traps<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th><strong>Conflict<\/strong><\/th><th><strong>What you must decide<\/strong><\/th><th><strong>Typical action after deciding<\/strong><\/th><\/tr><\/thead><tbody><tr><td>Content \/ same lines<\/td><td>Which behavior should survive, or how to combine it.<\/td><td>Edit the result, remove markers, then <code>git add -- file<\/code>.<\/td><\/tr><tr><td>Add \/ add<\/td><td>Two sides created the same path differently.<\/td><td>Combine or select the intended file, then stage it.<\/td><\/tr><tr><td>Modify \/ delete<\/td><td>One side deleted a path that the other changed.<\/td><td>Keep and stage it, or confirm deletion with <code>git rm -- file<\/code>.<\/td><\/tr><tr><td>Rename \/ rename or rename \/ delete<\/td><td>Which final path and content are intended.<\/td><td>Resolve paths, stage additions and removals, and inspect the full diff.<\/td><\/tr><tr><td>Binary file<\/td><td>Which complete version should be used.<\/td><td>Select or rebuild a valid file; do not expect line-by-line merging.<\/td><\/tr><tr><td>Generated file \/ lockfile<\/td><td>Which source inputs and tool version should define the output.<\/td><td>Resolve inputs, regenerate using project tooling, then test.<\/td><\/tr><tr><td>Submodule pointer<\/td><td>Which referenced submodule commit contains the intended work.<\/td><td>Resolve inside the submodule if needed, then stage its selected commit in the parent repository.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Conflict mechanics and content strategies: [8, 13, 30, 31].<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Ours and theirs depend on the operation<\/strong><\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th><strong>Context<\/strong><\/th><th><strong>Ours \/ stage 2<\/strong><\/th><th><strong>Theirs \/ stage 3<\/strong><\/th><\/tr><\/thead><tbody><tr><td>Merge topic into main<\/td><td>The checked-out main side.<\/td><td>The incoming topic side.<\/td><\/tr><tr><td>Rebase topic onto main<\/td><td>The so-far rebased result, starting at main.<\/td><td>The topic commit currently being replayed.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">During a conflict, the commands below choose a <strong>whole file<\/strong>, not just its conflicting hunks. They overwrite the working copy of that conflicted path. Pick one only after reviewing the correct side. [5, 13]<\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>git restore --ours -- app.txt     # Choose one, not both.\n# OR: git restore --theirs -- app.txt\ngit add -- app.txt<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Reuse a resolution, but still review it<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>git config rerere.enabled true\ngit config rerere.autoupdate false\ngit rerere diff<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Rerere records and reuses matching conflict resolutions. Leaving autoupdate false lets you inspect the reused working-tree result before staging it. It saves repeated editing, not the obligation to review and test. [25]<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Avoid preventable conflict noise.<\/strong> Keep formatting-only changes separate from behavior changes. Standardize line endings in <code>.gitattributes<\/code>; where branches genuinely use different normalization rules, a reviewed <code>-Xrenormalize<\/code> merge may help. Do not ignore whitespace blindly in whitespace-sensitive files. [8, 31]<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"collaboration\">07. Collaborating without losing someone else&#8217;s work<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Shared topic: preserve the history<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Recipe for a local feature\/login tracking a shared remote branch. Fetch first, inspect both sides, then merge the fetched remote tip. A push rejection means the remote may have moved again; inspect it rather than forcing over it. [9, 32]<\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>git switch feature\/login\ngit fetch origin\ngit log --left-right --oneline HEAD...origin\/feature\/login\ngit merge origin\/feature\/login\n# Review the combined result and run the relevant tests.\ngit push origin HEAD:feature\/login<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Private published topic: rewrite only with permission<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This recipe first incorporates the remote tip without discarding local commits, records the exact remote value, then rebases. Stop on any failed step. It assumes a clean tree, a topic you are allowed to rewrite, and an unused backup name. [5, 9]<\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>git switch feature\/login\ngit fetch origin\ngit merge --ff-only origin\/feature\/login\nexpected=$(git rev-parse origin\/feature\/login)\ngit branch backup\/feature-before-rebase\ngit rebase origin\/main\n# Inspect the rewritten diff and rerun the relevant tests.\ngit push \\\n  --force-with-lease=refs\/heads\/feature\/login:\"$expected\" \\\n  origin HEAD:refs\/heads\/feature\/login<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Why the explicit lease matters.<\/strong> The push succeeds only if the remote branch still matches the recorded commit. An IDE&#8217;s later fetch cannot silently change that saved value. A lease is a concurrency check, not proof that your rewritten content is correct. Never substitute an unrestricted force-push on a shared protected branch. [9]<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Inspect the ancestry relationship precisely<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>git merge-base --is-ancestor topic main  # Is topic included?\ngit merge-base --is-ancestor main topic  # Is FF possible?\ngit merge-base main topic               # Find a merge base<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">For <code>--is-ancestor<\/code>, exit 0 means yes, 1 means no, and another non-zero status signals an error. If no common base is visible, check for a shallow clone or wrong remote before concluding the histories are genuinely unrelated. [11]<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Work on a hotfix without disturbing your current checkout<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>git worktree add -b hotfix\/login ..\/project-hotfix main\n# Work in ..\/project-hotfix; the original working tree stays put.\n# After its work is committed and safe:\ngit worktree remove ..\/project-hotfix<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">A branch normally cannot be checked out in two linked worktrees at once. Removing a clean worktree is separate from deleting its branch. [19]<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Pull requests, release lines and the final quality gate<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">A <strong>pull request \/ merge request<\/strong> is a hosting-service review object, not a native Git commit type. Its final history depends on the repository&#8217;s enabled integration method. [20-22]<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th><strong>Review choice<\/strong><\/th><th><strong>Result to expect<\/strong><\/th><th><strong>Important caveat<\/strong><\/th><\/tr><\/thead><tbody><tr><td>Create a merge commit<\/td><td>Preserves source commits and adds a merge boundary.<\/td><td>A linear-history rule may disallow it.<\/td><\/tr><tr><td>Squash and merge<\/td><td>One destination commit for the reviewed change.<\/td><td>Do not continue stacking new work on the old squashed head without realigning it.<\/td><\/tr><tr><td>Rebase and merge<\/td><td>Separate rewritten commits on the destination without a merge commit.<\/td><td>GitHub always creates new commit IDs for this option; it is not identical to every local rebase case.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Hosting details above describe GitHub; other products can differ. [20, 21]<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Release and hotfix recipe: Gitflow-style mechanics<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Assume main is the stable released line at v1.3.0, develop holds the next release, and you have approval to integrate. These commands show the local mechanics; use equivalent reviewed pull requests when branch protections require them. [24]<\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>git switch -c hotfix\/1.3.1 v1.3.0\n# Implement, test and commit the focused repair HERE.\ngit switch main\ngit merge --no-ff hotfix\/1.3.1 -m \"Merge hotfix 1.3.1\"\n# Tag only the approved, tested release snapshot.\ngit tag -a v1.3.1 -m \"Release 1.3.1\"\ngit switch develop\ngit merge --no-ff hotfix\/1.3.1 -m \"Bring hotfix to develop\"\n# Push approved branch updates and the tag explicitly.\ngit push origin main develop\ngit push origin v1.3.1<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">With an active release branch, ensure the repair reaches it too. For an older supported line, cherry-pick the reviewed independent fix rather than importing an entire newer development line. Tags identify snapshots; do not move a published release tag to hide a correction. [7, 24, 28]<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>What must be true before integration is declared complete<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Confirm the correct target and reviewed diff, no unresolved paths, and a clean or deliberately understood working tree. Run the relevant build, unit, integration and user-interface\/end-to-end tests for the changed behavior. The required CI checks must pass on the intended integration result, not merely on an outdated topic commit. Record the chosen merge method and verify the resulting history.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>A clean merge is only a text\/history result.<\/strong> Two changes can merge without conflict and still break an API contract, database migration, permission check or user flow. Branch protection and tests address different risks; neither replaces review of the actual combined behavior.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"recovery\">08. Recover according to publication state<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">First run <code>git status<\/code>. Preserve uncommitted work before trying recovery commands. A backup branch protects committed history only; it does not preserve unstaged edits or untracked files. [17, 18, 26]<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th><strong>Situation<\/strong><\/th><th><strong>Preferred recovery<\/strong><\/th><th><strong>Why<\/strong><\/th><\/tr><\/thead><tbody><tr><td>Ordinary merge still in progress<\/td><td><code>git merge --abort<\/code><\/td><td>Cancels the in-progress merge; a clean starting tree makes recovery more predictable.<\/td><\/tr><tr><td>Unpublished completed merge \/ FF<\/td><td>Move the local target back to a verified pre-operation backup, using <code>reset --keep<\/code> where appropriate.<\/td><td>No collaborators depend on the discarded branch movement.<\/td><\/tr><tr><td>Published merge commit<\/td><td><code>git revert -m PARENT MERGE_SHA<\/code><\/td><td>Adds an explicit inverse commit instead of rewriting shared history.<\/td><\/tr><tr><td>Published squash commit<\/td><td><code>git revert SQUASH_SHA<\/code><\/td><td>A squash result is an ordinary one-parent commit.<\/td><\/tr><tr><td>Published fast-forward<\/td><td>Revert the selected ordinary commits in a reviewed, dependency-aware order.<\/td><td>There is no merge commit to revert as a single merge.<\/td><\/tr><tr><td>Lost tip \/ mistaken rebase<\/td><td>Inspect the reflog and create a rescue branch at the verified old commit.<\/td><td>Names the recoverable history before further edits.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Prepare before a risky local change<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>git status --short\ngit branch backup\/pre-integration\n# Only when there is work in progress to preserve:\ngit stash push -u -m \"before integration\"\ngit stash list<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Stash -u includes untracked files but not ignored files. Later, inspect the intended entry and use <code>git stash apply<\/code> before dropping it; conflicts can occur while applying it. [26]<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Find and name a lost commit<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>git reflog --date=iso\ngit show RECOVERED_SHA\ngit branch rescue\/lost-work RECOVERED_SHA<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Replace RECOVERED_SHA with the exact commit you inspected. Reflogs are local, expire, and are not a backup of every file edit. Do not assume that HEAD@{1} is always the state you need. [18]<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th><strong>Reset mode<\/strong><\/th><th><strong>Index and working-tree consequence<\/strong><\/th><\/tr><\/thead><tbody><tr><td><code>--soft COMMIT<\/code><\/td><td>Moves the branch; leaves index and working files as they are.<\/td><\/tr><tr><td><code>--mixed COMMIT<\/code><\/td><td>Moves the branch and resets the index; leaves working files.<\/td><\/tr><tr><td><code>--hard COMMIT<\/code><\/td><td>Overwrites tracked state and can overwrite\/remove obstructing untracked paths. Destructive.<\/td><\/tr><tr><td><code>--keep COMMIT<\/code><\/td><td>Updates the branch and relevant paths, but refuses when affected local changes would be overwritten.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Reset semantics: [17]. Do not use a hard reset as a routine response to a rejected push or confusing graph.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"R01\">R01 | Undo a published merge with revert<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><em>RECOVERY &#8211; PRESERVES HISTORY<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For a merge that others may already have fetched, create a compensating commit rather than rewriting the shared branch.<\/p>\n\n\n\n<pre class=\"wp-block-code language-mermaid\" id=\"mermaid_R01\"><code>flowchart LR\nsubgraph Before\n  bA[\"A\"] --&gt; bD[\"D\"] --&gt; bM[\"M&lt;br\/&gt;main\"]\n  bA --&gt; bB[\"B\"] --&gt; bC[\"C&lt;br\/&gt;topic\"] --&gt; bM\nend\nsubgraph After\n  aA[\"A\"] --&gt; aD[\"D\"] --&gt; aM[\"M\"] --&gt; aR[\"R&lt;br\/&gt;main\"]\n  aA --&gt; aB[\"B\"] --&gt; aC[\"C&lt;br\/&gt;topic\"] --&gt; aM\nend<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Older commits point toward newer commits. Mermaid source: <a href=\"#mermaid_R01\">R01<\/a><\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>lab split\ngit merge topic -m M       # Prepare the BEFORE graph.\ngit show -s --format='%h %p %s' HEAD\n# In THIS lab, parent 1 is previous main (D).\ngit revert -m 1 --no-edit HEAD\ngit diff HEAD~2 HEAD       # Empty in this simple lab.<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Result.<\/strong> R reverses the change from D to M. M and the topic ancestry remain in history. Here, with no intervening commits, R has the same tree as D.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th><strong>Pros<\/strong><\/th><th><strong>Cons<\/strong><\/th><\/tr><\/thead><tbody><tr><td>Safe for shared history; the rollback is explicit and reviewable.<\/td><td>Can conflict with later edits. Merging the same topic again does not automatically restore the reverted changes.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Watch out.<\/strong> -m 1 selects the parent to keep as the baseline; it is not a commit message. Inspect the parents, do not guess. Reintroduce deliberately with a revert of R or genuinely new changes.<\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>git merge-base --is-ancestor topic main<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Expected:<\/strong> Still exits 0: reverting content does not remove ancestry.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Reference: [14]<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Troubleshooting and the commands worth remembering<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th><strong>Symptom<\/strong><\/th><th><strong>What to check \/ do next<\/strong><\/th><\/tr><\/thead><tbody><tr><td>Push rejected: non-fast-forward<\/td><td>Fetch and compare local versus remote. Merge a shared line, or rebase only where rewriting is allowed. Do not force first.<\/td><\/tr><tr><td>Already up to date, but code is missing<\/td><td>Inspect earlier reverts, squash commits or an ours-strategy merge. Ancestry is not a content guarantee.<\/td><\/tr><tr><td>Fast-forward impossible<\/td><td>Inspect the unique commits on each side. Choose a deliberate merge or authorized rebase; see S05B.<\/td><\/tr><tr><td>Local changes would be overwritten<\/td><td>Commit a coherent change, stash it, or use another worktree. Do not discard edits just to switch branches.<\/td><\/tr><tr><td>Branch is not fully merged after squash<\/td><td>Expected when original source commits are not ancestors of the destination. Verify the delivered patch and dependent work before deletion.<\/td><\/tr><tr><td>No merge base \/ unrelated histories<\/td><td>Verify the remote and whether the clone is shallow. Fetch missing history before using the exceptional unrelated-histories flag.<\/td><\/tr><tr><td>Merge still seems unfinished<\/td><td>Use status and inspect unmerged paths. Stage the resolution and continue the operation that is actually active.<\/td><\/tr><tr><td>Rebase keeps stopping<\/td><td>It replays a series, so later commits may conflict separately. Resolve each; do not skip patches without understanding the loss.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Two easily missed command traps<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Review before creating a merge commit:<\/strong> &#8211;no-commit alone does not stop a fast-forward. Use &#8211;no-ff together with &#8211;no-commit when you need an inspectable, not-yet-committed merge result. [4]<\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>git merge --no-ff --no-commit topic\ngit diff --cached\n# Review and test, then git commit; or git merge --abort.<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Abandoning a squash attempt:<\/strong> squash does not set MERGE_HEAD, so merge &#8211;abort is not its general undo command. Only after a clean start, with no other edits to keep, the following discards its staged\/working-tree result. [4, 13]<\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>git restore --source=HEAD --staged --worktree -- .<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Danger: the restore command discards edits.<\/strong> It also removes paths added to the index by that squash if they do not exist in HEAD. Never run it over unrelated work you need to keep. Save that work separately first.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Daily inspection<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>git status --short\ngit branch -vv\ngit log --graph --oneline --decorate --all\ngit diff --stat main...topic\ngit diff --cached --check\ngit show -s --format=\"%h %p %s\" HEAD<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"appendix\">Appendix A. Copyable Mermaid source<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The complete BEFORE and AFTER Mermaid source appears with each corresponding scenario above. Use this index to jump directly to a diagram. Copy one complete Mermaid block into a Mermaid-capable editor. Labels name the branch pointers; arrows run from older to newer commits. [27]<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"#mermaid_S01\">S01 | Fast-forward: move the branch pointer<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"#mermaid_S02\">S02 | Force a merge commit with &#8211;no-ff<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"#mermaid_S03\">S03 | Three-way merge: both branches advanced<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"#mermaid_S04\">S04 | Three-way merge with a conflict<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"#mermaid_S05A\">S05A | Already up to date: nothing to merge<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"#mermaid_S05B\">S05B | Fast-forward-only refuses divergence<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"#mermaid_S06\">S06 | Squash merge: one delivery commit<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"#mermaid_S07\">S07 | Rebase, then fast-forward<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"#mermaid_S08\">S08 | Cherry-pick: bring across one change<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"#mermaid_S09\">S09 | Merge main into a feature branch<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"#mermaid_S10\">S10 | Octopus: merge several independent topics<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"#mermaid_S11\">S11 | Merge histories with no common ancestor<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"#mermaid_S12\">S12 | Prefer a side for conflicting hunks: -X<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"#mermaid_S13\">S13 | Record ancestry but keep our whole tree<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"#mermaid_S14\">S14 | Subtree-aware merge: align directory layouts<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"#mermaid_S15\">S15 | Interactive rebase: polish before integration<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"#mermaid_R01\">R01 | Undo a published merge with revert<\/a><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"sources\">Appendix B. Official references and verification<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Documentation checked on 19 September 2026. Git commands are supported by Git&#8217;s manuals and Pro Git; hosting-specific claims use GitHub&#8217;s documentation. Workflow guidance additionally uses DORA and the original Gitflow author. All titles below are clickable.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\" id=\"ref_1\"><strong>[1] Pro Git: Branches in a Nutshell<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/git-scm.com\/book\/en\/v2\/Git-Branching-Branches-in-a-Nutshell\">Branch pointers and HEAD<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\" id=\"ref_2\"><strong>[2] Git glossary<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/git-scm.com\/docs\/gitglossary\">Core terms<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\" id=\"ref_3\"><strong>[3] Pro Git: Basic Branching and Merging<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/git-scm.com\/book\/en\/v2\/Git-Branching-Basic-Branching-and-Merging\">Fast-forward and three-way examples<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\" id=\"ref_4\"><strong>[4] git merge<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/git-scm.com\/docs\/git-merge\">Modes, conflicts and merge state<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\" id=\"ref_5\"><strong>[5] git rebase<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/git-scm.com\/docs\/git-rebase\">Replay, interactive editing and conflict handling<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\" id=\"ref_6\"><strong>[6] git branch<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/git-scm.com\/docs\/git-branch\">Local refs, tracking and deletion<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\" id=\"ref_7\"><strong>[7] git cherry-pick<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/git-scm.com\/docs\/git-cherry-pick\">Selective replay and provenance<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\" id=\"ref_8\"><strong>[8] Merge strategies<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/git-scm.com\/docs\/merge-strategies\">ort, recursive, resolve, octopus, ours and subtree<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\" id=\"ref_9\"><strong>[9] Remote synchronization<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/git-scm.com\/docs\/git-fetch\">git fetch<\/a> | <a href=\"https:\/\/git-scm.com\/docs\/git-push\">git push and force-with-lease<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\" id=\"ref_10\"><strong>[10] git switch<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/git-scm.com\/docs\/git-switch\">Branch creation, detached HEAD and orphan starts<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\" id=\"ref_11\"><strong>[11] git merge-base<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/git-scm.com\/docs\/git-merge-base\">Common ancestors and ancestry predicates<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\" id=\"ref_12\"><strong>[12] Git revisions<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/git-scm.com\/docs\/gitrevisions\">Parent and range notation<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\" id=\"ref_13\"><strong>[13] git restore<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/git-scm.com\/docs\/git-restore\">Index stages, ours\/theirs and restoring paths<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\" id=\"ref_14\"><strong>[14] git revert<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/git-scm.com\/docs\/git-revert\">Mainline selection and reverted-merge behavior<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\" id=\"ref_15\"><strong>[15] git pull<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/git-scm.com\/docs\/git-pull\">Explicit integration modes<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\" id=\"ref_16\"><strong>[16] git diff<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/git-scm.com\/docs\/git-diff\">Tip comparison, three-dot comparison and checks<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\" id=\"ref_17\"><strong>[17] git reset<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/git-scm.com\/docs\/git-reset\">Reset modes and recovery consequences<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\" id=\"ref_18\"><strong>[18] git reflog<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/git-scm.com\/docs\/git-reflog\">Local reference history and expiration<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\" id=\"ref_19\"><strong>[19] git worktree<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/git-scm.com\/docs\/git-worktree\">Linked working trees<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\" id=\"ref_20\"><strong>[20] GitHub: Protected branches<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/docs.github.com\/en\/repositories\/configuring-branches-and-merges-in-your-repository\/managing-protected-branches\/about-protected-branches\">Review and status-check restrictions<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\" id=\"ref_21\"><strong>[21] GitHub: Pull request merges<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/docs.github.com\/en\/pull-requests\/reference\/pull-request-merges\">Merge, squash and rebase behavior<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\" id=\"ref_22\"><strong>[22] GitHub flow<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/docs.github.com\/en\/get-started\/using-github\/github-flow\">Branch, review and integration lifecycle<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\" id=\"ref_23\"><strong>[23] DORA: Trunk-based development<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/dora.dev\/capabilities\/trunk-based-development\/\">Short-lived work and continuous integration<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\" id=\"ref_24\"><strong>[24] Vincent Driessen: A successful Git branching model<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/nvie.com\/posts\/a-successful-git-branching-model\/\">Gitflow and its later applicability note<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\" id=\"ref_25\"><strong>[25] git rerere<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/git-scm.com\/docs\/git-rerere\">Recorded conflict resolution reuse<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\" id=\"ref_26\"><strong>[26] git stash<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/git-scm.com\/docs\/git-stash\">Saving and restoring work in progress<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\" id=\"ref_27\"><strong>[27] Mermaid: Flowcharts<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/mermaid.js.org\/syntax\/flowchart.html\">Nodes, links and subgraphs<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\" id=\"ref_28\"><strong>[28] git tag<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/git-scm.com\/docs\/git-tag\">Annotated tags and release references<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\" id=\"ref_29\"><strong>[29] Pro Git: Rebasing<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/git-scm.com\/book\/en\/v2\/Git-Branching-Rebasing\">Replayed commits and publication caution<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\" id=\"ref_30\"><strong>[30] Pro Git: Advanced Merging<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/git-scm.com\/book\/en\/v2\/Git-Tools-Advanced-Merging\">Conflict inspection and exceptional merges<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\" id=\"ref_31\"><strong>[31] Git attributes<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/git-scm.com\/docs\/gitattributes\">Normalization and content merge behavior<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\" id=\"ref_32\"><strong>[32] Pro Git: Remote Branches<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/git-scm.com\/book\/en\/v2\/Git-Branching-Remote-Branches\">Tracking refs and synchronization<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Executable verification<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The 17 scenario and recovery recipes were executed in isolated disposable repositories with <strong>Git 2.47.3<\/strong>. Checks covered branch tips, parent counts, intended refusals, resolved file contents, squash and cherry-pick ancestry, rewritten commits, three-parent octopus history, separate roots, both -X variants, unchanged ours-strategy trees, subtree paths, and revert behavior. Interactive rebase was checked with the specified pick\/fixup todo list.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The lab tests validate Git mechanics, not application correctness. Run project-specific tests and honor repository policy before integrating real work. Statements about newer Git behavior, such as the recursive alias beginning in 2.50, come from the current official manuals.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>One durable habit.<\/strong> Before every integration, know the target, the source, the intended history shape, and the recovery path. After it, verify both the graph and the behavior.<\/p>\n\n","protected":false},"excerpt":{"rendered":"<p>PRACTICAL TECHNICAL HANDBOOK \/ 19 SEPTEMBER 2026 Git Branching &amp; Merging Branch types, integration choices, conflicts and safe recovery Understand what Git changes, choose the right method,&#8230; <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-1165","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/posts\/1165","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/comments?post=1165"}],"version-history":[{"count":3,"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/posts\/1165\/revisions"}],"predecessor-version":[{"id":1186,"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/posts\/1165\/revisions\/1186"}],"wp:attachment":[{"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/media?parent=1165"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/categories?post=1165"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/tags?post=1165"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}