Integrating a finished feature
After review, run git merge --no-ff feature on main so the feature appears as a single merge commit with its own commits kept beneath it.
Git · Command
git merge brings the changes from one or more named commits, usually branch tips, into the current branch. Depending on the history, it either moves the branch pointer forward (a fast-forward) or records a new merge commit with several parents.
Last updated
Git finds the merge base, which is the point where the two histories split. If your branch tip is an ancestor of the target, Git simply moves HEAD forward to it. If not, the strategy (ort by default) runs a 3-way merge using the base and both tips, then commits the result with every merged tip as a parent. If some changes cannot be reconciled automatically, HEAD stays where it was, MERGE_HEAD records the other tip, and conflict markers are written into the files. You fix them, git add the files, and finish with git merge --continue or git commit. ORIG_HEAD is saved before the merge starts, so you can find your way back.
git merge [-n] [--stat] [--compact-summary] [--no-commit] [--squash] [--[no-]edit] [--no-verify] [-s <strategy>] [-X <strategy-option>] [-S[<keyid>]] [--[no-]allow-unrelated-histories] [--[no-]rerere-autoupdate] [-m <msg>] [-F <file>] [--into-name <branch>] [<commit>...]
git merge (--continue | --abort | --quit)| Name | Type | Description |
|---|---|---|
<commit>... | commit-ish | The commits to merge in, usually other branch heads. If you give more than one, you get an octopus merge. If you give none, Git merges the upstream that the current branch is configured to track. |
--ff / --no-ff / --ff-only | flag | Controls what happens when a fast-forward is possible: fast-forward (the default), always create a merge commit, or allow only a fast-forward and fail otherwise. |
--no-commit | flag | Does the merge but stops just before creating the merge commit, so you can inspect or adjust the result first. |
--squash | flag | Puts the merged result into the index and working tree but does not commit, move HEAD or record MERGE_HEAD. |
-s, --strategy=<strategy> | string | The merge strategy to use: ort, resolve, octopus, ours or subtree (recursive is now an alias for ort). |
-X, --strategy-option=<option> | string | An option passed to the strategy, e.g. ours, theirs, ignore-space-change, renormalize, find-renames or diff-algorithm. |
-m <msg> / -F <file> | string | The message for the merge commit, given inline or read from a file (only used if a merge commit is created). |
--allow-unrelated-histories | flag | Lets Git merge histories that have no common ancestor. |
--autostash | flag | Stashes local changes before the merge and reapplies them afterwards. |
--abort / --continue / --quit | flag | For a merge that has stopped: undo it and restore the pre-merge state, finish it after resolving conflicts, or forget it while leaving the index and working tree as they are. |
Exit status 0 on success. A non-zero status when a conflict stops the merge, or when --ff-only cannot fast-forward. If all named commits are already in HEAD's history, it prints "Already up to date."
#!/usr/bin/env bash
set -euo pipefail
cd "$(mktemp -d)" && git init -q -b main demo && cd demo
git config user.name dev && git config user.email dev@example.com
echo "greeting=hello" > app.conf
git add app.conf && git commit -qm "initial"
git switch -qc feature
echo "greeting=hi" > app.conf
git commit -qam "feature: change greeting"
git switch -q main
echo "greeting=hey" > app.conf
git commit -qam "main: change greeting"
# Try the merge; it stops on a conflict
if ! git merge --no-ff feature -m "Merge feature"; then
git status --short # shows: UU app.conf
git show :1:app.conf # common ancestor version
echo "greeting=hi" > app.conf # resolve by hand
git add app.conf
GIT_EDITOR=true git merge --continue
fi
git log --oneline --graph --allThe script builds two branches that edit the same line, merges them with --no-ff, looks at the base version from index stage 1, resolves the conflict by hand, and finishes with git merge --continue.
After review, run git merge --no-ff feature on main so the feature appears as a single merge commit with its own commits kept beneath it.
After git fetch, run git merge --ff-only origin/main to update a local branch that has no commits of its own. If it has diverged, the command fails instead of creating a merge commit.
Run git merge -s ours old-experiment to record the old branch as merged while keeping your tree exactly as it is.
Fetch the other project and run git merge --allow-unrelated-histories other/main to join two histories that share no ancestor.
If the current branch tip is an ancestor of the commit being merged, Git can fast-forward: it moves HEAD and the index to that commit without creating a new commit. Otherwise it does a true merge, which records a new commit whose parents are both tips. By default Git fast-forwards when it can. --no-ff forces a merge commit, and --ff-only refuses anything that is not a fast-forward. The merge.ff config setting chooses the default.
Follow-up: Why can merging an annotated tag create a merge commit even when a fast-forward is possible? Git always creates a merge commit for annotated tags so the tag message (and its signature check) goes into the commit. To avoid that, merge tag^0 or use --ff-only.
HEAD stays where it was, and MERGE_HEAD points to the other tip. Files that merged cleanly are updated. Conflicted files get <<<<<<< ======= >>>>>>> markers, and the index keeps stages 1, 2 and 3 for the base, ours and theirs. You can edit the files, git add them and finish with git merge --continue or git commit. You can run git merge --abort to restore the pre-merge state, or git merge --quit to stop tracking the merge while leaving the files as they are.
Follow-up: How can you see the original base text inside the conflict markers? Set merge.conflictStyle to diff3 or zdiff3; Git then adds a ||||||| section showing the common ancestor's text.
--squash prepares the same working tree and index a real merge would, but it does not commit, does not move HEAD and does not write MERGE_HEAD. When you commit afterwards, you get a normal single-parent commit. Git therefore does not record the branch as merged, and the branch's commits are not part of the new commit's ancestry. You cannot combine --squash with --commit.
Follow-up: Why might merging the same branch again after a squash cause conflicts? The squash commit is not linked to the branch's history, so the merge base is still the old split point and Git tries to apply those changes a second time.
git pullgit rebasegit merge-basegit mergetoolgit fmt-merge-msggit resetgit rereregit cherry-pick