Git: Git Branching & Merging – A Complete Tutorials

PRACTICAL TECHNICAL HANDBOOK / 19 SEPTEMBER 2026

Git Branching & Merging

Branch types, integration choices, conflicts and safe recovery

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.

Navigate the handbook

01. The mental model and terminology

02. Branch types and branching workflows

03. Everyday commands and remote branches

04. Choose an outcome and prepare the lab

05. Merge scenario atlas: S01-S15

06. Conflict resolution playbook

07. Collaboration, pull requests and releases

08. Recovery and troubleshooting

Appendix A. Copyable Mermaid diagram source

Appendix B. Official references and verification

The five choices to recognize immediately

Desired outcomeReach for
Advance a target that has not divergedgit merge --ff-only topic
Keep both histories and record integrationgit merge --no-ff topic
Deliver one commit per short-lived topicgit merge --squash topic, then commit
Replay a private topic into a clean seriesgit rebase main, then fast-forward
Move one independent fix to another linegit cherry-pick -x FIX_SHA

Reading key. 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.

01. The mental model

A commit records a snapshot and its parent commit(s). A branch 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]

flowchart LR
  A["A"] --> B["B<br/>main"] --> C["C<br/>feature/login"]

Here, main points to B and feature/login points to C. With HEAD 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]

TermMeaning that matters in practice
HEADYour current position. Usually it refers to the checked-out local branch; in detached HEAD it names a commit directly.
Working treeThe files you are editing. Uncommitted edits are not permanently attached to a branch.
Index / staging areaThe proposed next snapshot. git add updates it; git commit records it.
Reference / ref / tipA ref names an object; a branch tip is the commit currently named by that branch.
Ancestor / descendantA commit is an ancestor when you can reach it by walking parent links from another commit.
Merge baseA best common ancestor used for comparison. Complex histories can have more than one.
Source / target branchSource supplies changes; target receives them. git merge topic modifies the currently checked-out target.
Merge commit / first parentA merge commit has multiple parents. The first is normally the previous tip of the branch receiving the merge.
DAG / treeThe history is a directed acyclic graph. A tree describes a snapshot of paths and content; identical trees need not imply identical history.
Tag / releaseA tag identifies a chosen revision. Publishing or deploying a release is a separate workflow, not something a branch name proves.

Terminology and ancestry: [1, 2, 11, 12].

edit files -> git add -> index -> git commit -> local branch
local branch -> git push -> branch on the remote

The rule to remember. Switch to the destination before merging. git switch main followed by git merge topic means topic into main. Reversing the switch reverses the integration direction.

02. Branch types: mechanism versus convention

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]

Form or stateWhat it isImportant distinction
Local branchA writable local ref such as main or feature/login.Committing advances the checked-out branch.
Branch on a remoteA branch stored in another repository, commonly a server.Push updates it subject to permissions and policy.
Remote-tracking refYour local last-observed view, such as origin/main.Fetch refreshes it; it is not a live view of the server.
Tracking local branchA local branch with an upstream association.The association informs status and pull; it is not automatic synchronization.
Unborn / orphan branchAn unborn branch has no commit yet. An orphan start creates a new root history.After its first commit it is a normal branch with independent ancestry. See S11.
Protected branchA hosting-service rule applied to selected branches.Review, checks and push restrictions are server policy, not a special Git branch object.
Detached HEADHEAD names a commit rather than a local branch.Not a branch type. Name new work with git switch -c rescue/work before leaving it.

Reference mechanisms: [6, 9, 10]. Server protection: [20].

Read the branch state

git branch                 # Local branches; * is current
git branch -r              # Remote-tracking refs
git branch -a              # Both views
git branch -vv             # Upstream and ahead/behind summary
git remote -v              # Remote names and URLs

Create a tracking branch or set its upstream

These are separate recipes. The remote branch must already exist for the first one.

git fetch origin
git switch --track -c feature/login origin/feature/login

# Existing local branch: change its upstream association.
git branch --set-upstream-to=origin/feature/login feature/login

Three meanings people confuse. origin is a conventional remote name. An upstream branch is a configured tracking relationship. A remote literally named upstream is merely another remote, often the original project in a fork workflow. [6, 9]

A fork is another repository, not another branch. A worktree is another working directory attached to the same repository; it is useful for keeping two branches checked out separately. [19, 22]

Branch roles used by teams

Choose roles because the delivery process needs them, not because Git requires them. main, master and trunk can all be names for the principal line. The lifecycle below is a recommended naming convention, not a built-in taxonomy.

Role / exampleStart from -> integrate intoBenefit and cost
Main / trunkPrincipal integration line; protect it.One authoritative integration point; requires a clear quality gate.
Develop / integrationUsually main initially; receives features in Gitflow.Separates next-release work; adds another permanent line to maintain.
Feature / topicCurrent main, or develop in Gitflow -> that base.Isolates one change; long-lived topics accumulate integration risk.
Bugfix / fixThe affected active line -> the same line.Focused repair; choosing the wrong base can import unrelated code.
HotfixExact deployed tag / supported release -> affected release and active development.Fast, isolated production repair; every affected line still needs the fix.
ReleaseApproved integration point -> release and necessary back-merges.Allows stabilization; needs a freeze policy and fix propagation.
Maintenance / supportReleased tag -> supported version line.Supports older versions; each maintained line adds backport and testing cost.
Experiment / spikeAny explicit base -> merge only if adopted.Safe exploration; abandoned experiments must not become hidden dependencies.
Chore / docs / refactor / testThe active integration line -> that line.Useful intent labels; they are still ordinary topic branches.
Environment branchA team-defined line such as staging.Can support a deliberate promotion model; risks drift and environment-only fixes.

Creation recipes

Run these independently. main must exist; v1.2.0 must be a real released tag. In Gitflow, use develop rather than main where the team requires it. [6, 10, 24]

git switch -c develop main
git switch -c feature/login main
git switch -c bugfix/login-error main
git switch -c hotfix/1.2.1 v1.2.0
git switch -c release/1.3 main
git switch -c support/1.2 v1.2.0
git switch -c experiment/cache main
git switch -c chore/dependencies main
git switch -c docs/install main
git switch -c refactor/auth main
git switch -c test/login main
git switch -c staging main

Choose a branching workflow

A branching workflow decides which lines exist and how long they live. A merge method decides how one integration changes history. Choose them separately; Gitflow does not require every merge to use the same flag.

WorkflowStructure and good fitStrengthCost / risk
Trunk-basedOne principal line; very short-lived changes, often with feature flags. Useful for frequent delivery.Small integration gaps and rapid feedback.Requires strong checks, small changes and discipline around incomplete work.
GitHub flowShort-lived branch -> review / checks -> main. Useful for simple continuous-delivery teams.Easy to explain and automate.Supported old releases need an additional explicit policy.
Gitflowmain + develop + feature, release and hotfix branches. Useful for planned, versioned releases.Clear release-stabilization structure.More synchronization paths; often excessive for continuously deployed services.
Release / maintenance linesmain plus selected supported version branches. Useful when multiple versions remain active.Isolates version-specific fixes.Backport selection and per-version testing are ongoing work.

Workflow definitions and trade-offs: [22, 23, 24]. The Gitflow author also recommends simpler workflows for continuously delivered web applications. [24]

A practical default policy

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.

Branch names are not deployment evidence

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.

Keep the model small

QuestionDecision to record
Where does new work start?main, develop, or an explicitly supported release line.
What is the review unit?One logical commit, one topic branch, or one pull request.
Who may rewrite a topic?Its owner, with explicit coordination if it is already shared.
How do fixes propagate?Merge the shared history or cherry-pick an independent fix; test each target.
When is a branch deleted?After integration is verified and no other work depends on it.

03. Everyday branching and remote commands

Start one piece of work

Recipe for an existing clone with origin and main. Start clean; do not run this over unrelated uncommitted work.

git status --short
git fetch --prune origin
git switch main
git merge --ff-only origin/main
git switch -c feature/login
# Edit the intended files, then stage only those files.
git add -- path/to/changed-file
git diff --cached
git commit -m "feat: add login flow"
git push -u origin feature/login

Replace path/to/changed-file with a real changed path. -u 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]

ActionWhat changesWhat does not happen automatically
fetchDownloads objects and refreshes applicable remote-tracking refs.Does not integrate those changes into your current local branch.
pullFetches, then integrates into the current branch using the chosen mode.Does not choose the correct branch or policy for your team.
pushRequests remote ref updates using your local commits.Does not first fetch and merge someone else’s changes.

Use explicit pull modes rather than relying on machine-specific defaults. [9, 15]

git pull --ff-only     # Refuse if the local branch diverged.
git pull --rebase      # Replay local work; changes history.
git pull --no-rebase   # Integrate by merge; FF if permitted.

Rename and clean up deliberately

# Rename an existing local branch; coordinate shared names.
git branch -m old-name new-name
git push -u origin new-name

# After integration has been verified, leave the topic first.
git switch main
git branch -d feature/login
git push origin --delete feature/login
git fetch --prune origin

Deletion is two separate actions. git branch -d is local; git push origin --delete changes the server. Fetch pruning removes stale remote-tracking refs, not your local branches. After a squash, verify delivery and dependencies before deliberately using -D; do not force deletion simply to silence the safety check. [6, 9]

04. Inspect history and choose the outcome

Read the graph, not just the file diff

git log --oneline --graph --decorate --all
git log main..topic                 # Commits only on topic
git log --left-right --oneline main...topic
git diff main topic                # Compare the two tip trees
git diff main...topic              # Merge base -> topic tree
git diff --cached --check          # Staged whitespace/marker checks

For log, two dots select commits reachable from the right but not the left; three dots select the symmetric difference. For diff, three dots compare the merge base with the right-hand tip. Do not assume log and diff interpret the notation identically. [12, 16]

MethodOriginal source commitsNew integration commitBest reason to choose it
Fast-forwardKept unchangedNoneAdvance an already-compatible target.
Normal / –no-ff mergeKept unchangedA multi-parent merge, unless no-op / permitted FFPreserve shared history and integration ancestry.
Squash mergeRemain on source; not linked as merged parentsOne ordinary commit after you commitMake one topic the delivery unit.
Rebase + FFReplayed commits get new IDs when rewrittenNo merge commitPublish a clean, private patch series.
Cherry-pickSource stays; selected patch is copiedUsually one ordinary commit per pickMove a particular independent fix.

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]

Methods are not algorithms

Algorithm / optionMeaning
ortCurrent default for a two-head merge; handles common-base comparison and rename detection.
recursiveHistorical implementation; since Git 2.50 it is an alias for ort. Older installations differ.
resolveOlder two-head strategy without rename handling; rarely needed for routine work.
octopusCombines multiple topic heads; refuses complex manual conflict resolution.
ours / subtreeSpecial history-preserving or layout-aware strategies. See S13 / S14.
-s versus -X-s selects a strategy; -X supplies an option to it. Squash, –ff and –no-ff are not strategy names.

Algorithm details: [8].

Prepare the disposable learning lab

Run the helper below once in Bash or Zsh: macOS, Linux, or Git Bash on Windows. Every lab MODE 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.

lab() {
  case "$1" in
    base|ff|split|conflict) ;;
    *) echo 'Use: lab base|ff|split|conflict'; return 1 ;;
  esac
  cd "$(mktemp -d "${TMPDIR:-/tmp}/git-lab.XXXXXX")" || return
  git init -q -b main
  git config user.name 'Git Learner'
  git config user.email 'learner@example.invalid'
  git config commit.gpgsign false
  git config merge.ff true
  git config rerere.enabled false
  printf 'color=blue\n' > app.txt
  git add app.txt; git commit -qm A
  git branch base
  [ "$1" = base ] && return 0
  git switch -q -c topic
  if [ "$1" = conflict ]; then
    printf 'color=green\n' > app.txt
  else
    printf 'feature\n' > feature.txt
  fi
  git add .; git commit -qm B
  printf 'tests\n' > test.txt
  git add test.txt; git commit -qm C
  git switch -q main
  if [ "$1" != ff ]; then
    if [ "$1" = conflict ]; then
      printf 'color=red\n' > app.txt
    else
      printf 'guide\n' > guide.txt
    fi
    git add .; git commit -qm D
  fi
  printf 'Disposable lab: %s\n' "$PWD"
}
ModeStarting history
lab baseOnly A on main. A helper branch named base stays at A.
lab ffmain at A; topic adds B and C.
lab splitmain adds D; topic adds B and C in separate files.
lab conflictmain changes app.txt to red; topic changes it to green, then adds test.txt.

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.

05. Merge scenario atlas

S01 | Fast-forward: move the branch pointer

EVERYDAY

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.

flowchart LR
subgraph Before
  bA["A<br/>main"] --> bB["B"] --> bC["C<br/>topic"]
end
subgraph After
  aA["A"] --> aB["B"] --> aC["C<br/>main + topic"]
end

Older commits point toward newer commits. Mermaid source: S01

lab ff
git switch main
git merge --ff-only topic
git log --oneline --graph --decorate --all

Result. main and topic now point to C. B and C keep their identities; no merge commit exists. Only main moved during the merge.

ProsCons
Small, linear history; original commits remain intact; refusal protects a fast-forward-only policy.No explicit integration boundary. It cannot combine two diverged tips.

Watch out. Fast-forward does not mean one commit or squashed commits. Every commit already on topic remains in history.

git rev-parse main topic

Expected: Both lines print the same commit ID.

Reference: [3, 4]

S02 | Force a merge commit with –no-ff

EVERYDAY

Use this when the history is fast-forwardable, but the team wants an explicit record that a topic was integrated.

flowchart LR
subgraph Before
  bA["A<br/>main"] --> bB["B"] --> bC["C<br/>topic"]
end
subgraph After
  aA["A"] --> aB["B"] --> aC["C<br/>topic"] --> aM["M<br/>main"]
  aA --> aM
end

Older commits point toward newer commits. Mermaid source: S02

lab ff
git switch main
git merge --no-ff topic -m "M: integrate topic"
git show -s --format='%h %p %s' HEAD

Result. M has two parents: A first, C second. The source branch stays at C. The snapshot includes the topic changes.

ProsCons
Preserves original commits and a visible feature boundary. First-parent logs give a concise integration timeline.Adds a commit even when pointer movement would suffice; heavy use can clutter a small repository.

Watch out. –no-ff does not manufacture a merge when the input is already an ancestor of HEAD. That case is still a no-op.

git log --first-parent --oneline main

Expected: M appears as the integration step on main.

Reference: [3, 4]

S03 | Three-way merge: both branches advanced

EVERYDAY

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.

flowchart LR
subgraph Before
  bA["A"] --> bD["D<br/>main"]
  bA --> bB["B"] --> bC["C<br/>topic"]
end
subgraph After
  aA["A"] --> aD["D"] --> aM["M<br/>main"]
  aA --> aB["B"] --> aC["C<br/>topic"] --> aM
end

Older commits point toward newer commits. Mermaid source: S03

lab split
git switch main
git merge topic -m "M: combine both histories"
git show -s --format='%h %p %s' HEAD

Result. M joins D and C. Both histories remain reachable. This lab merges cleanly because the sides add different files.

ProsCons
Keeps shared history stable and preserves both lines of development; appropriate for collaboratively edited branches.Creates a non-linear history; unrelated-looking edits can still interact badly at runtime.

Watch out. A three-way merge is not a three-parent merge. It compares a base and two tips, usually producing a two-parent commit.

git diff HEAD^1 HEAD --stat

Expected: The diff shows what this integration added relative to the previous main tip.

Reference: [3, 4, 12]

S04 | Three-way merge with a conflict

EVERYDAY

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.

flowchart LR
subgraph Before
  bA["A"] --> bD["D<br/>main"]
  bA --> bB["B"] --> bC["C<br/>topic"]
end
subgraph After
  aA["A"] --> aD["D"] --> aM["M<br/>main"]
  aA --> aB["B"] --> aC["C<br/>topic"] --> aM
end

Older commits point toward newer commits. Mermaid source: S04

lab conflict
git merge topic
# Expected conflict: inspect before deciding.
git status --short
cat app.txt
# Lab decision: the agreed final value is purple.
printf 'color=purple\n' > app.txt
git add app.txt
git diff --cached --check
git commit -m "M: resolve color deliberately"

Result. 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.

ProsCons
Preserves both histories and makes the integration decision reviewable.Requires judgment and regression tests; choosing a side blindly can discard valid behavior.

Watch out. git add marks a path resolved; it does not prove the result is correct. To abandon an ordinary in-progress merge, use git merge –abort.

git status --short
git show HEAD:app.txt

Expected: The status is clean and the file contains color=purple.

Reference: [4, 13]

S05A | Already up to date: nothing to merge

GUARDRAIL

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.

flowchart LR
subgraph Before
  bA["A"] --> bB["B"] --> bC["C<br/>main + topic"]
end
subgraph After
  aA["A"] --> aB["B"] --> aC["C<br/>main + topic"]
end

Older commits point toward newer commits. Mermaid source: S05A

lab ff
git merge --ff-only topic   # Prepare the BEFORE graph.
git merge topic             # Already up to date.

Result. No commit is created and neither pointer moves.

Pros: Safe and idempotent. Cons: It checks ancestry, not whether a past change was later reverted.

Watch out. After a revert or an ours-strategy merge, Git can say this even though the files do not contain the topic behavior.

Reference: [4, 11]

S05B | Fast-forward-only refuses divergence

GUARDRAIL

The target and source each have unique commits. –ff-only refuses rather than choosing a merge or rebase for you.

flowchart LR
subgraph Before
  bA["A"] --> bD["D<br/>main"]
  bA --> bB["B"] --> bC["C<br/>topic"]
end
subgraph After
  aA["A"] --> aD["D<br/>main"]
  aA --> aB["B"] --> aC["C<br/>topic"]
end

Older commits point toward newer commits. Mermaid source: S05B

lab split
git merge --ff-only topic   # Expected non-zero exit.
git status --short

Result. The committed graph and branch pointers stay unchanged. This is a policy refusal, not a merge conflict.

Pros: Prevents accidental merge commits. Cons: You must deliberately select another integration method.

Watch out. Do not answer this error with reset –hard or force-push. Choose a normal merge or a permitted rebase after inspecting the graph.

Reference: [4]

S06 | Squash merge: one delivery commit

EVERYDAY

Use this when a short-lived topic contains useful work but noisy intermediate commits. The destination receives one ordinary commit containing the combined result.

flowchart LR
subgraph Before
  bA["A"] --> bD["D<br/>main"]
  bA --> bB["B"] --> bC["C<br/>topic"]
end
subgraph After
  aA["A"] --> aD["D"] --> aS["S<br/>main"]
  aA --> aB["B"] --> aC["C<br/>topic"]
end

Older commits point toward newer commits. Mermaid source: S06

lab split
git merge --squash topic
git diff --cached
git diff --cached --check
git commit -m "S: deliver feature and tests"

Result. 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.

ProsCons
One reviewable delivery unit; concise main history; easy to revert the complete change as an ordinary commit.Loses per-commit detail on main and does not record topic ancestry. Reusing the old topic can repeat changes or conflicts.

Watch out. –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.

git show -s --format=%p HEAD

Expected: Exactly one parent ID is printed. Do not treat ancestry checks as squash-verification checks.

Reference: [4, 21]

S07 | Rebase, then fast-forward

EVERYDAY – REWRITES TOPIC

Rebase replays the topic commits on top of current main; then a fast-forward integrates the replayed series. Rebase itself is not a merge.

flowchart LR
subgraph Before
  bA["A"] --> bD["D<br/>main"]
  bA --> bB["B"] --> bC["C<br/>topic"]
end
subgraph After
  aA["A"] --> aD["D"] --> aBP["B'"] --> aCP["C'<br/>main + topic"]
end

Older commits point toward newer commits. Mermaid source: S07

lab split
git switch topic
git branch backup/topic-before-rebase
git rebase main
git switch main
git merge --ff-only topic

Result. D is followed by B’ and C’. These replayed commits have new identities. The backup still retains B and C; it is omitted from the diagram.

ProsCons
Linear main history while retaining separate logical commits; useful for a privately owned, well-organized topic.Rewrites the topic; conflicts can recur per replayed commit; coordinating already-published history takes extra care.

Watch out. 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.

git log --oneline --graph main

Expected: The main line is A, D, B’, C’ with no merge commit.

Reference: [5, 6]

S08 | Cherry-pick: bring across one change

SELECTIVE INTEGRATION

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.

flowchart LR
subgraph Before
  bA["A"] --> bD["D<br/>main"]
  bA --> bB["B"] --> bC["C<br/>topic"]
end
subgraph After
  aA["A"] --> aD["D"] --> aBP["B'<br/>main"]
  aA --> aB["B"] --> aC["C<br/>topic"]
end

Older commits point toward newer commits. Mermaid source: S08

lab split
git switch main
git show topic~1 --stat
git cherry-pick -x topic~1

Result. A new B’ 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.

ProsCons
Precise selection; useful across supported release lines; -x records the source commit for traceability in the clean example.Can omit dependencies or tests; creates parallel patch histories and extra maintenance work.

Watch out. Inspect prerequisites first. A fix may require earlier commits or a release-specific adaptation. -x does not make the source an ancestor.

git show --stat HEAD
git log -1 --format=%B

Expected: Only B’s selected change is applied and the message includes its source commit.

Reference: [7]

S09 | Merge main into a feature branch

EVERYDAY – SYNC DIRECTION

Use this to update a shared topic without rewriting it. The target is topic, so the merge belongs on topic, not on main.

flowchart LR
subgraph Before
  bA["A"] --> bD["D<br/>main"]
  bA --> bB["B"] --> bC["C<br/>topic"]
end
subgraph After
  aA["A"] --> aD["D<br/>main"] --> aM["M<br/>topic"]
  aA --> aB["B"] --> aC["C"] --> aM
end

Older commits point toward newer commits. Mermaid source: S09

lab split
git switch topic
git merge main -m "M: sync main into topic"
git show -s --format='%h %p %s' HEAD

Result. 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.

ProsCons
Safe for shared topic history; conflicts are addressed before final review; no forced update is needed.Repeated syncs add merge commits to the feature history; frequent small integrations are easier to review.

Watch out. In a real clone, fetch and merge origin/main when local main may be stale. Recheck the destination branch before every merge.

git rev-parse main
git rev-parse topic^1 topic^2

Expected: main still names D; the two parent lines name C and D, respectively.

Reference: [4, 9]

S10 | Octopus: merge several independent topics

SPECIALIZED

An octopus merge bundles multiple topic heads into one integration commit. Use it only for independent changes that merge without complex manual resolution.

flowchart LR
subgraph Before
  bA["A"] --> bD["D<br/>main"]
  bA --> bB["B<br/>feature/ui"]
  bA --> bC["C<br/>feature/api"]
end
subgraph After
  aA["A"] --> aD["D"] --> aM["M<br/>main"]
  aA --> aB["B<br/>feature/ui"] --> aM
  aA --> aC["C<br/>feature/api"] --> aM
end

Older commits point toward newer commits. Mermaid source: S10

lab base
git switch -c feature/ui
printf 'ui\n' > ui.txt
git add ui.txt; git commit -m B
git switch -c feature/api base
printf 'api\n' > api.txt
git add api.txt; git commit -m C
git switch main
printf 'ops\n' > ops.txt
git add ops.txt; git commit -m D
git merge --no-ff feature/ui feature/api -m M

Result. M has three parents in this example: D, B and C. Both feature branch pointers remain unchanged.

ProsCons
One integration point for several independent topics.Harder to review or partially revert. The octopus strategy refuses complex merges needing manual conflict resolution.

Watch out. Several regular two-branch merges are usually clearer for application teams. This is not the meaning of a three-way merge.

git show -s --format=%p HEAD

Expected: Three parent IDs are printed.

Reference: [8]

S11 | Merge histories with no common ancestor

SPECIALIZED

Use this only for an intentional repository-history import. Git normally refuses histories that share no ancestor.

flowchart LR
subgraph Before
  bA["A"] --> bD["D<br/>main"]
  bX["X<br/>imported"]
end
subgraph After
  aA["A"] --> aD["D"] --> aM["M<br/>main"]
  aX["X<br/>imported"] --> aM
end

Older commits point toward newer commits. Mermaid source: S11

lab base
git switch --orphan imported
printf 'Imported project\n' > IMPORTED.md
git add IMPORTED.md; git commit -m X
git switch main
printf 'Local guide\n' > guide.txt
git add guide.txt; git commit -m D
git merge --allow-unrelated-histories --no-ff \
  imported -m "M: import independent history"

Result. M connects the formerly separate roots. The example keeps both projects at the repository root because their file names do not collide.

ProsCons
Preserves the imported history in one repository.Can create add/add conflicts or a confusing layout; licensing, provenance and sensitive-history review are separate requirements.

Watch out. 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.

git rev-list --max-parents=0 main

Expected: Two root commit IDs are reachable from main.

Reference: [4, 10]

S12 | Prefer a side for conflicting hunks: -X

SPECIALIZED – REVIEW CAREFULLY

-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.

flowchart LR
subgraph Before
  bA["A"] --> bD["D<br/>main"]
  bA --> bB["B"] --> bC["C<br/>topic"]
end
subgraph After
  aA["A"] --> aD["D"] --> aM["M<br/>main"]
  aA --> aB["B"] --> aC["C<br/>topic"] --> aM
end

Older commits point toward newer commits. Mermaid source: S12

lab conflict
git merge --no-ff -s ort -X ours topic -m M
cat app.txt        # color=red
cat test.txt       # tests: incoming clean change kept

# Alternative: repeat in a NEW lab, not the merged one.
lab conflict
git merge --no-ff -s ort -X theirs topic -m M
cat app.txt        # color=green
cat test.txt       # tests: still present

Result. With ours, M keeps red for the conflicting line. With theirs, M keeps green. Both retain test.txt from the incoming topic.

ProsCons
Useful when a reviewed policy genuinely decides conflicts of this kind.Can silently choose the wrong behavior. Some structural conflicts still require intervention.

Watch out. -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.

git show -s --format=%p HEAD

Expected: The selected variant has a normal two-parent merge commit.

Reference: [8, 13]

S13 | Record ancestry but keep our whole tree

SPECIALIZED – HIGH RISK

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.

flowchart LR
subgraph Before
  bA["A"] --> bD["D<br/>main"]
  bA --> bB["B"] --> bC["C<br/>topic"]
end
subgraph After
  aA["A"] --> aD["D"] --> aM["M<br/>main"]
  aA --> aB["B"] --> aC["C<br/>topic"] --> aM
end

Older commits point toward newer commits. Mermaid source: S13

lab split
git merge --no-ff -s ours topic \
  -m "M: supersede topic without taking its files"
git diff HEAD^1 HEAD
git merge-base --is-ancestor topic HEAD

Result. 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.

ProsCons
Can retire or supersede a history deliberately while preserving the ancestry record.Drops all incoming content, including non-conflicting work; may prevent later merges from introducing changes you actually wanted.

Watch out. 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.

git diff --exit-code HEAD^1 HEAD

Expected: Exit status 0 and no output: the destination tree did not change.

Reference: [8, 11]

S14 | Subtree-aware merge: align directory layouts

SPECIALIZED

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.

flowchart LR
subgraph Before
  bA["A"] --> bD["D<br/>main"]
  bA --> bV["V<br/>vendor"]
end
subgraph After
  aA["A"] --> aD["D"] --> aM["M<br/>main"]
  aA --> aV["V<br/>vendor"] --> aM
end

Older commits point toward newer commits. Mermaid source: S14

lab base
git switch -c vendor base
printf 'color=green\n' > app.txt
git commit -am "V: update vendor file"
git switch main
mkdir -p vendor/widget
git mv app.txt vendor/widget/app.txt
git commit -m "D: place library in a subdirectory"
git merge --no-ff -s ort -Xsubtree=vendor/widget \
  vendor -m "M: align vendor layout"
cat vendor/widget/app.txt

Result. 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.

ProsCons
Accommodates intentional directory-layout differences while retaining history.Requires correct prefix mapping and review of rename/path behavior. A wrong mapping can produce an unwanted tree.

Watch out. -s subtree guesses alignment; -Xsubtree=PATH makes the mapping explicit with ort. Neither is the separate git subtree add/pull workflow or a submodule.

git ls-tree -r --name-only HEAD

Expected: The file appears at vendor/widget/app.txt.

Reference: [8]

S15 | Interactive rebase: polish before integration

HISTORY EDITING – PRIVATE TOPIC

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.

flowchart LR
subgraph Before
  bA["A"] --> bD["D<br/>main"]
  bA --> bB["B"] --> bC["C<br/>topic"]
end
subgraph After
  aA["A"] --> aD["D"] --> aS["S<br/>main + topic"]
end

Older commits point toward newer commits. Mermaid source: S15

lab split
git switch topic
git branch backup/topic-before-edit
git rebase -i main
# Save the todo list: pick B, then fixup C.
git switch main
git merge --ff-only topic

Result. 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.

ProsCons
Produces intentional, reviewable commits before publication; fixup removes correction noise without keeping every intermediate message.Rewrites the topic. Dropping or reordering commits can remove behavior or violate dependencies; reviewers must inspect the rewritten result.

Watch out. This differs from git merge –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.

git log --oneline main
git show --stat HEAD

Expected: The main line is A, D, S; S contains both feature.txt and test.txt.

Reference: [5, 29]

06. Conflict resolution: inspect, decide, finish

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]

Use the base to understand both changes

git config merge.conflictStyle diff3
# Set before starting the merge; diff3 includes the base text.
<<<<<<< HEAD
color=red
||||||| base
color=blue
=======
color=green
>>>>>>> topic

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]

A reliable resolution sequence

git status
git diff --name-only --diff-filter=U
git ls-files -u
git show :1:app.txt       # Base version, if present
git show :2:app.txt       # Ours in this ordinary merge
git show :3:app.txt       # Theirs in this ordinary merge
# Edit app.txt to the intended final content.
git add -- app.txt
git diff --cached
git diff --cached --check
# Run the relevant build and tests before completion.
git merge --continue

Some conflict types do not have all three index stages. git diff --cached --check can catch whitespace errors and conflict markers; it cannot validate application behavior. [13, 16]

Operation in progressComplete after resolvingCancel
Ordinary mergegit merge --continuegit merge --abort
Rebasegit rebase --continuegit rebase --abort
Cherry-pickgit cherry-pick --continuegit cherry-pick --abort
Revertgit revert --continuegit revert --abort

Do not skip a commit to escape a conflict. git rebase --skip and git cherry-pick --skip omit the current patch. Use them only after proving that omission is intentional. --quit is not equivalent to aborting and restoring the previous state. [5, 7, 14]

Conflict types and side-selection traps

ConflictWhat you must decideTypical action after deciding
Content / same linesWhich behavior should survive, or how to combine it.Edit the result, remove markers, then git add -- file.
Add / addTwo sides created the same path differently.Combine or select the intended file, then stage it.
Modify / deleteOne side deleted a path that the other changed.Keep and stage it, or confirm deletion with git rm -- file.
Rename / rename or rename / deleteWhich final path and content are intended.Resolve paths, stage additions and removals, and inspect the full diff.
Binary fileWhich complete version should be used.Select or rebuild a valid file; do not expect line-by-line merging.
Generated file / lockfileWhich source inputs and tool version should define the output.Resolve inputs, regenerate using project tooling, then test.
Submodule pointerWhich referenced submodule commit contains the intended work.Resolve inside the submodule if needed, then stage its selected commit in the parent repository.

Conflict mechanics and content strategies: [8, 13, 30, 31].

Ours and theirs depend on the operation

ContextOurs / stage 2Theirs / stage 3
Merge topic into mainThe checked-out main side.The incoming topic side.
Rebase topic onto mainThe so-far rebased result, starting at main.The topic commit currently being replayed.

During a conflict, the commands below choose a whole file, not just its conflicting hunks. They overwrite the working copy of that conflicted path. Pick one only after reviewing the correct side. [5, 13]

git restore --ours -- app.txt     # Choose one, not both.
# OR: git restore --theirs -- app.txt
git add -- app.txt

Reuse a resolution, but still review it

git config rerere.enabled true
git config rerere.autoupdate false
git rerere diff

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]

Avoid preventable conflict noise. Keep formatting-only changes separate from behavior changes. Standardize line endings in .gitattributes; where branches genuinely use different normalization rules, a reviewed -Xrenormalize merge may help. Do not ignore whitespace blindly in whitespace-sensitive files. [8, 31]

07. Collaborating without losing someone else’s work

Shared topic: preserve the history

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]

git switch feature/login
git fetch origin
git log --left-right --oneline HEAD...origin/feature/login
git merge origin/feature/login
# Review the combined result and run the relevant tests.
git push origin HEAD:feature/login

Private published topic: rewrite only with permission

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]

git switch feature/login
git fetch origin
git merge --ff-only origin/feature/login
expected=$(git rev-parse origin/feature/login)
git branch backup/feature-before-rebase
git rebase origin/main
# Inspect the rewritten diff and rerun the relevant tests.
git push \
  --force-with-lease=refs/heads/feature/login:"$expected" \
  origin HEAD:refs/heads/feature/login

Why the explicit lease matters. The push succeeds only if the remote branch still matches the recorded commit. An IDE’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]

Inspect the ancestry relationship precisely

git merge-base --is-ancestor topic main  # Is topic included?
git merge-base --is-ancestor main topic  # Is FF possible?
git merge-base main topic               # Find a merge base

For --is-ancestor, 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]

Work on a hotfix without disturbing your current checkout

git worktree add -b hotfix/login ../project-hotfix main
# Work in ../project-hotfix; the original working tree stays put.
# After its work is committed and safe:
git worktree remove ../project-hotfix

A branch normally cannot be checked out in two linked worktrees at once. Removing a clean worktree is separate from deleting its branch. [19]

Pull requests, release lines and the final quality gate

A pull request / merge request is a hosting-service review object, not a native Git commit type. Its final history depends on the repository’s enabled integration method. [20-22]

Review choiceResult to expectImportant caveat
Create a merge commitPreserves source commits and adds a merge boundary.A linear-history rule may disallow it.
Squash and mergeOne destination commit for the reviewed change.Do not continue stacking new work on the old squashed head without realigning it.
Rebase and mergeSeparate rewritten commits on the destination without a merge commit.GitHub always creates new commit IDs for this option; it is not identical to every local rebase case.

Hosting details above describe GitHub; other products can differ. [20, 21]

Release and hotfix recipe: Gitflow-style mechanics

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]

git switch -c hotfix/1.3.1 v1.3.0
# Implement, test and commit the focused repair HERE.
git switch main
git merge --no-ff hotfix/1.3.1 -m "Merge hotfix 1.3.1"
# Tag only the approved, tested release snapshot.
git tag -a v1.3.1 -m "Release 1.3.1"
git switch develop
git merge --no-ff hotfix/1.3.1 -m "Bring hotfix to develop"
# Push approved branch updates and the tag explicitly.
git push origin main develop
git push origin v1.3.1

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]

What must be true before integration is declared complete

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.

A clean merge is only a text/history result. 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.

08. Recover according to publication state

First run git status. 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]

SituationPreferred recoveryWhy
Ordinary merge still in progressgit merge --abortCancels the in-progress merge; a clean starting tree makes recovery more predictable.
Unpublished completed merge / FFMove the local target back to a verified pre-operation backup, using reset --keep where appropriate.No collaborators depend on the discarded branch movement.
Published merge commitgit revert -m PARENT MERGE_SHAAdds an explicit inverse commit instead of rewriting shared history.
Published squash commitgit revert SQUASH_SHAA squash result is an ordinary one-parent commit.
Published fast-forwardRevert the selected ordinary commits in a reviewed, dependency-aware order.There is no merge commit to revert as a single merge.
Lost tip / mistaken rebaseInspect the reflog and create a rescue branch at the verified old commit.Names the recoverable history before further edits.

Prepare before a risky local change

git status --short
git branch backup/pre-integration
# Only when there is work in progress to preserve:
git stash push -u -m "before integration"
git stash list

Stash -u includes untracked files but not ignored files. Later, inspect the intended entry and use git stash apply before dropping it; conflicts can occur while applying it. [26]

Find and name a lost commit

git reflog --date=iso
git show RECOVERED_SHA
git branch rescue/lost-work RECOVERED_SHA

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]

Reset modeIndex and working-tree consequence
--soft COMMITMoves the branch; leaves index and working files as they are.
--mixed COMMITMoves the branch and resets the index; leaves working files.
--hard COMMITOverwrites tracked state and can overwrite/remove obstructing untracked paths. Destructive.
--keep COMMITUpdates the branch and relevant paths, but refuses when affected local changes would be overwritten.

Reset semantics: [17]. Do not use a hard reset as a routine response to a rejected push or confusing graph.

R01 | Undo a published merge with revert

RECOVERY – PRESERVES HISTORY

For a merge that others may already have fetched, create a compensating commit rather than rewriting the shared branch.

flowchart LR
subgraph Before
  bA["A"] --> bD["D"] --> bM["M<br/>main"]
  bA --> bB["B"] --> bC["C<br/>topic"] --> bM
end
subgraph After
  aA["A"] --> aD["D"] --> aM["M"] --> aR["R<br/>main"]
  aA --> aB["B"] --> aC["C<br/>topic"] --> aM
end

Older commits point toward newer commits. Mermaid source: R01

lab split
git merge topic -m M       # Prepare the BEFORE graph.
git show -s --format='%h %p %s' HEAD
# In THIS lab, parent 1 is previous main (D).
git revert -m 1 --no-edit HEAD
git diff HEAD~2 HEAD       # Empty in this simple lab.

Result. 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.

ProsCons
Safe for shared history; the rollback is explicit and reviewable.Can conflict with later edits. Merging the same topic again does not automatically restore the reverted changes.

Watch out. -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.

git merge-base --is-ancestor topic main

Expected: Still exits 0: reverting content does not remove ancestry.

Reference: [14]

Troubleshooting and the commands worth remembering

SymptomWhat to check / do next
Push rejected: non-fast-forwardFetch and compare local versus remote. Merge a shared line, or rebase only where rewriting is allowed. Do not force first.
Already up to date, but code is missingInspect earlier reverts, squash commits or an ours-strategy merge. Ancestry is not a content guarantee.
Fast-forward impossibleInspect the unique commits on each side. Choose a deliberate merge or authorized rebase; see S05B.
Local changes would be overwrittenCommit a coherent change, stash it, or use another worktree. Do not discard edits just to switch branches.
Branch is not fully merged after squashExpected when original source commits are not ancestors of the destination. Verify the delivered patch and dependent work before deletion.
No merge base / unrelated historiesVerify the remote and whether the clone is shallow. Fetch missing history before using the exceptional unrelated-histories flag.
Merge still seems unfinishedUse status and inspect unmerged paths. Stage the resolution and continue the operation that is actually active.
Rebase keeps stoppingIt replays a series, so later commits may conflict separately. Resolve each; do not skip patches without understanding the loss.

Two easily missed command traps

Review before creating a merge commit: –no-commit alone does not stop a fast-forward. Use –no-ff together with –no-commit when you need an inspectable, not-yet-committed merge result. [4]

git merge --no-ff --no-commit topic
git diff --cached
# Review and test, then git commit; or git merge --abort.

Abandoning a squash attempt: squash does not set MERGE_HEAD, so merge –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]

git restore --source=HEAD --staged --worktree -- .

Danger: the restore command discards edits. 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.

Daily inspection

git status --short
git branch -vv
git log --graph --oneline --decorate --all
git diff --stat main...topic
git diff --cached --check
git show -s --format="%h %p %s" HEAD

Appendix A. Copyable Mermaid source

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]

S01 | Fast-forward: move the branch pointer

S02 | Force a merge commit with –no-ff

S03 | Three-way merge: both branches advanced

S04 | Three-way merge with a conflict

S05A | Already up to date: nothing to merge

S05B | Fast-forward-only refuses divergence

S06 | Squash merge: one delivery commit

S07 | Rebase, then fast-forward

S08 | Cherry-pick: bring across one change

S09 | Merge main into a feature branch

S10 | Octopus: merge several independent topics

S11 | Merge histories with no common ancestor

S12 | Prefer a side for conflicting hunks: -X

S13 | Record ancestry but keep our whole tree

S14 | Subtree-aware merge: align directory layouts

S15 | Interactive rebase: polish before integration

R01 | Undo a published merge with revert

Appendix B. Official references and verification

Documentation checked on 19 September 2026. Git commands are supported by Git’s manuals and Pro Git; hosting-specific claims use GitHub’s documentation. Workflow guidance additionally uses DORA and the original Gitflow author. All titles below are clickable.

[1] Pro Git: Branches in a Nutshell

Branch pointers and HEAD

[2] Git glossary

Core terms

[3] Pro Git: Basic Branching and Merging

Fast-forward and three-way examples

[4] git merge

Modes, conflicts and merge state

[5] git rebase

Replay, interactive editing and conflict handling

[6] git branch

Local refs, tracking and deletion

[7] git cherry-pick

Selective replay and provenance

[8] Merge strategies

ort, recursive, resolve, octopus, ours and subtree

[9] Remote synchronization

git fetch | git push and force-with-lease

[10] git switch

Branch creation, detached HEAD and orphan starts

[11] git merge-base

Common ancestors and ancestry predicates

[12] Git revisions

Parent and range notation

[13] git restore

Index stages, ours/theirs and restoring paths

[14] git revert

Mainline selection and reverted-merge behavior

[15] git pull

Explicit integration modes

[16] git diff

Tip comparison, three-dot comparison and checks

[17] git reset

Reset modes and recovery consequences

[18] git reflog

Local reference history and expiration

[19] git worktree

Linked working trees

[20] GitHub: Protected branches

Review and status-check restrictions

[21] GitHub: Pull request merges

Merge, squash and rebase behavior

[22] GitHub flow

Branch, review and integration lifecycle

[23] DORA: Trunk-based development

Short-lived work and continuous integration

[24] Vincent Driessen: A successful Git branching model

Gitflow and its later applicability note

[25] git rerere

Recorded conflict resolution reuse

[26] git stash

Saving and restoring work in progress

[27] Mermaid: Flowcharts

Nodes, links and subgraphs

[28] git tag

Annotated tags and release references

[29] Pro Git: Rebasing

Replayed commits and publication caution

[30] Pro Git: Advanced Merging

Conflict inspection and exceptional merges

[31] Git attributes

Normalization and content merge behavior

[32] Pro Git: Remote Branches

Tracking refs and synchronization

Executable verification

The 17 scenario and recovery recipes were executed in isolated disposable repositories with Git 2.47.3. 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.

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.

One durable habit. 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.

Related Posts

AWS Elastic IP Cross-Account / Cross-Organization Transfer Runbook

Use case: AWS Organization A / Account A → AWS Organization B / Account BObjective: Retain the exact same public IPv4 address while changing AWS account ownership….

Read More

Terraform Developer Toolchain – Hands-On Lab Manual

DOWNLOAD – HERE Write -> Validate -> Test -> Lint -> Secure -> Document -> Cost -> Automate -> Govern Framework What | Why | When |…

Read More

Kafka Master Tutorials Series: 7 Topics, Partitions, Consumers, Consumer Groups & Lag

Developer Planning Guide for Correct Mapping, Scaling, Reliability and Performance Audience: Developers, students, freshers, architects, platform engineersTraining context: Confluent Kafka ClusterGoal: Remove confusion around how Kafka Topics, Partitions, Producers, Consumers,…

Read More

Kafka Master Tutorials Series: 6 – Kafka Consumer Deep Dive

Consumer Groups, Parallelism, Offsets, Rebalancing, Failover, Consumption Patterns and Production Tuning Audience: Students and freshers with no previous Kafka experienceGoal: Start with “What is a consumer?” and finish with…

Read More

Redis Tutorials: A Complete Fundamental Turorials

From Fundamentals to Production-Grade Caching, Sessions, Pub/Sub, Counters, Locks and Failure Handling 1. What is Redis? Redis is a high-performance, primarily in-memory data store. The easiest mental…

Read More

Kafka Master Tutorials Series: 5 – Deep Dive Into Kafka Producers

From send() to Broker ACK: Keys, Partitions, Batching, Retries, Reliability, Latency and Performance Tuning Audience: Students and freshers with no prior Kafka experienceGoal: Build from producer fundamentals to production-grade Kafka producer…

Read More