182

My Git history looks like that :

Git history

I would like to squash the purple commits into a single one. I don't want to see them ever again in my commit log.

I've tried to do a git rebase -i 1, but even though 1 is on the blue branch (cf. picture), I still see every commit on my purple branch.

How can I completely remove the purple branch (from commit log) ?

3
  • 1
    Is your repo pushed anywhere that other people have pulled from it? Commented Jul 10, 2013 at 17:49
  • Both branches are purely local. Commented Jul 10, 2013 at 18:00
  • 1
    TIL doing git rebase -i <first commit sha> will get rid of all merge commits :) This may be probably true only for a repository with only merge commits where no conflicts were resolved Commented Sep 10, 2022 at 13:57

6 Answers 6

157

Do git rebase -i <sha before the branches diverged> this will allow you to remove the merge commit and the log will be one single line as you wanted. You can also delete any commits that you do not want any more. The reason that your rebase wasn't working was that you weren't going back far enough.

WARNING: You are rewriting history doing this. Doing this with changes that have been pushed to a remote repo will cause issues. I recommend only doing this with commits that are local.

Sign up to request clarification or add additional context in comments.

10 Comments

Even if I choose a sha before the branches have diverged, I see all the commits of the purple branch -_-
Yeah, then you delete them in the editor that comes up with git rebase and they will be removed.
Deleting the commits resulted in losing all the modification of the merge. But I did a soft reset and I've been able to have something quite like I wanted (order do not match initial expectations).
this answer is incomplete in detail, which branch do yu initiate the rebase from?
Fairly sure you do not want to 'remove' the commits. 'remove' is not an option in rebase, but delete is. if you delete a commit, you will loose the changes it introduced. You want to squash the commits.
|
153

Starting with the repo in the original state

Original repo history

To remove the merge commit and squash the branch into a single commit in the mainline

Squashed commits, no merge commit

Use these commands (replacing 5 and 1 with the SHAs of the corresponding commits):

git checkout 5 git reset --soft 1 git commit --amend -m '1 2 3 4 5' git rebase HEAD master 

To retain a merge commit but squash the branch commits into one:

Squashed commits, retained merge commit

Use these commands (replacing 5, 1 and C with the SHAs of the corresponding commits):

git checkout -b tempbranch 5 git reset --soft 1 git commit --amend -m '1 2 3 4 5' git checkout C git merge --no-ff tempbranch git rebase HEAD master 

To remove the merge commit and replace it with individual commits from the branch

Branch moved into mainline, no merge commit

Just do (replacing 5 with the SHA of the corresponding commit):

git rebase 5 master 

And finally, to remove the branch entirely

Branch removed entirely

Use this command (replacing C and D with the SHAs of the corresponding commits):

git rebase --onto C D~ master 

4 Comments

in git rebase 5 master case, why it is not "A B C 1 2 3 4 5 D ..." order?
That command takes the commits that master doesn't have in common with 5 and plops them on top of 5. The commit at C isn't part of the lineage of 5, it's the first to be moved on top of 5.
If you want to end up with "A B C 1 2 3 4 5 D ..." you can do: git rebase C 5; git rebase 5 master
Did exactly like this and worked even a thousand commits back!
57

To Just Remove a Merge Commit

If all you want to do is to remove a merge commit (2) so that it is like it never happened, the command is simply as follows

git rebase --onto <sha of 1> <sha of 2> <blue branch>

And now the purple branch isn't in the commit log of blue at all and you have two separate branches again. You can then squash the purple independently and do whatever other manipulations you want without the merge commit in the way.

4 Comments

Of all the answers, I find this both the most straightforward and the easiest to do. Thank you.
This is the first step to the question, and I agree the easiest that does exactly what was requested. The second step would be git merge --squash <purple branch name>
best answer, definitely. Thanks. ps: add --rebase-merges if you want to preserve some other merge commits along your blue branch (otherwise rebase will flatten those by default)
I made use of this to create my helper script here: stackoverflow.com/a/79786404/457268
27

There are two ways to tackle this based on what you want:

Solution 1: Remove purple commits, preserving history (incase you want to roll back)

git revert -m 1 <SHA of merge> 

-m 1 specifies which parent line to choose

Purple commits will still be there in history but since you have reverted, you will not see code from those commits.


Solution 2: Completely remove purple commits (disruptive change if repo is shared)

git rebase -i <SHA before branching out> 

and delete (remove lines) corresponding to purple commits.

This would be less tricky if commits were not made after merge. Additional commits increase the chance of conflicts during revert/rebase.

4 Comments

This does not sound right. Leaving aside the fact that it's hard to tell what the original poster wanted to do with the purple commits in the first place, if you revert the merge, that's fine, that will do a reverse patch of the changes from that commit and add it as another commit, canceling the purple commits out. It's like 1 - 1 = 0. But if you then rebase out the purple commits, you leave behind the reverted patch, unless you rebase that out too. If you don't, it's like applying -1 to your history, not 0, so you'll leave behind changes you don't want.
@Cupcake, provided were two separate answers. I have revised answer to be less confusing :P
AFAIK this is not what the OP is asking for. He wants to keep the changes, but remove the noise from his log. For that, he needs to squash the commits down, deleting will remove the changes.
True, looks like the original question has been rephrased. This answer is not for OPs problem.
12

For full control over the operation and to preserve any merge commits you want to keep along the way, the modern approach is to use

git rebase -i -r

From man git rebase of git version 2.44.2 about -r:

-r, --rebase-merges[=(rebase-cousins|no-rebase-cousins)], --no-rebase-merges By default, a rebase will simply drop merge commits from the todo list, and put the rebased commits into a single, linear branch. With --rebase-merges, the rebase will instead try to preserve the branching structure within the commits that are to be rebased, by recreating the merge commits. Any resolved merge conflicts or manual amendments in these merge commits will have to be resolved/re-applied manually. --no-rebase-merges can be used to countermand both the rebase.rebaseMerges config option and a previous --rebase-merges. When rebasing merges, there are two modes: rebase-cousins and no-rebase-cousins. If the mode is not specified, it defaults to no-rebase-cousins. In no-rebase-cousins mode, commits which do not have <upstream> as direct ancestor will keep their original branch point, i.e. commits that would be excluded by git-log(1)'s --ancestry-path option will keep their original ancestry by default. In rebase-cousins mode, such commits are instead rebased onto <upstream> (or <onto>, if specified). It is currently only possible to recreate the merge commits using the ort merge strategy; different merge strategies can be used only via explicit exec git merge -s <strategy> [...] commands. See also REBASING MERGES and INCOMPATIBLE OPTIONS below. 

3 Comments

good to know .. but this is the option I'd never use ... because personally I prefer minimum to no merge commits in my git history :)
I started to do this, but then I had no idea what I was doing, could you elaborate on this answer?
The command is still a git rebase -i at it's heart, so make sure you're comfortable with that first. Suggest you pick a simple merge commit to try this out first. Read what's in the todo textfile, try to trace and follow what git is going to do. Once you have a good idea, you can start experimenting by introducing new commits in the merge, move commits to before the merge, etc. If you need more specific help you'll need to provide more details :)
1

I wanted a simpler way of achieving Warren's rebase onto approach as I had a hard time making sense of the appropriate hashes and their meaning and I did not want to get into a detached head state (but point the result directly to the local! branch).

My use case often is to purge an unwanted merge from a current feature branch from history. So I want to be able to purge a merge commit by its id from the currently checked out branch with no further hashes as an input.

I wrote two helper scripts enabling me this workflow:

  1. Make sure you are in the right branch you wish to purge a merge from.

     $> git checkout branch-containing-unwanted-merge 
  2. Find the appropriate merge commit hash:

    $> ./list-merges.sh 3 c952c106b94e0353a6e8db93cb2fd6495404a07b 2025-10-08 my-unwanted-merge 305de5b3f0fdd02ba8d74520e957fd7db5336576 2025-10-06 some-other-merge 7b18a1eadda78d64b157a2dbd4696cf6be1bfec4 2025-10-02 yet-another-merge 

    The hash of the unwanted merge, in my case, is c952c106b94e0353a6e8db93cb2fd6495404a07b.

  3. Run the purge. The hash becomes the input of my purge command:

    $> ./purge-merge.sh c952c106b94e0353a6e8db93cb2fd6495404a07b Starting to purge merge from history (local only, no force pushing) Target Branch: branch-containing-unwanted-merge Merge to Undo: c952c106b94e0353a6e8db93cb2fd6495404a07b New Base (First Parent): f1e12273200300694d6fe63a9d57fa5c505d4927 History rewritten. The merge commit is gone. Local branch is now at the new tip. WARNING: To update the remote branch, run: git push --force-with-lease origin branch-containing-unwanted-merge 
  4. You may now force-push the local branch to the remote as recommended.


These are the scripts:

list-merges.sh

#!/usr/bin/env bash N=${1:-5} if ! git rev-parse --is-inside-work-tree > /dev/null 2>&1; then echo "Error: Not inside a Git repository." exit 1 fi git log --merges -n "$N" \ --pretty=format:$'%C(yellow)%H%C(reset)\t%C(cyan)%ad%C(reset)\t%s' \ --date=short --color=always \ | sed "s/\tMerge branch '\([^']*\)' into .*$/\t\1/" echo exit 0 
 $>\cat purge-merge.sh #!/usr/bin/env bash MERGE_SHA=$1 if [ -z "${MERGE_SHA}" ]; then echo "Error: Please provide the merge commit SHA." exit 1 fi if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then echo "Error: Not inside a Git repository." exit 1 fi if ! git diff-index --quiet HEAD --; then echo "Error: Working directory is not clean." echo "Please commit, stash, or reset your changes before running this command." exit 1 fi BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD) FIRST_PARENT=$(git rev-parse "${MERGE_SHA}^1") cat <<EOF Starting to purge merge from history (local only, no force pushing) Target Branch: ${BRANCH_NAME} Merge to Undo: ${MERGE_SHA} New Base (First Parent): ${FIRST_PARENT} EOF if git rebase --onto "${FIRST_PARENT}" "${MERGE_SHA}" HEAD; then git branch -f "${BRANCH_NAME}" HEAD git checkout "${BRANCH_NAME}" cat <<EOF_SUCCESS Merge Undo Successful History rewritten. The merge commit is gone. Local branch is now at the new tip. WARNING: To update the remote branch, run: git push --force-with-lease origin ${BRANCH_NAME} EOF_SUCCESS exit 0 else cat <<'EOF_FAILURE' Rebase Failed --- The rebase stopped due to conflicts or an error. Resolve conflicts, then run: git rebase --continue If you want to abort, run: git rebase --abort EOF_FAILURE exit 1 fi 

1 Comment

Created a snippet for the files as well: gist.github.com/k0pernikus/650b06750d6e4b9142c1d927bc82ddbe

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.