Drimer
Drimer

Reputation: 115

How to clean up multiple git merges

I have merged branches several times without realizing that it worked the first time and thus messed up the git history. Now I would like to clean up the git history if possible. I would like the "Merge branch 'development' of https://... into development" to disappear or to reduced it to one entry in the git history. How could I do that?

Below is a screenshot of the history and a git log at the end of this post.

Edit: I am not sure, if I managed to express my desired result properly. My main concern is to clean up the history. The graphic below shows my current git history. I would like to squash the marked commits and merges together.

The merges and commits I would like to squash together.

|
:
|
*   commit 5c36203c6d995e11138ccdc79a9daa86cf05aeb7 (origin/development_initial, feature/project-ToolManager, development_initial)
|\  Merge: 8564fae 3dc0393
| | Author: Drimer
| | Date:   Fri Mar 4 12:48:17 2022 +0100
| |
| |     Merge branch 'development' of https://project into development
| |
| *   commit 3dc03939829e05b2431f8e3c6a353f2da308b2a1
| |\  Merge: bf2f283 f2e18a9
| | | Author: Drimer
| | | Date:   Fri Mar 4 11:27:27 2022 +0100
| | |
| | |     Merge branch 'development' of https://project into development
| | |
* | |   commit 8564fae1510d4dd1a318f5b223d822d2effe591e
|\ \ \  Merge: bf2f283 f2e18a9
| |/ /  Author: Drimer
|/| /   Date:   Fri Mar 4 11:27:27 2022 +0100
| |/
| |         Merge branch 'development' of https://project into development
| |
| *   commit f2e18a9fd2c7dd67626b205e7ad765066f3db1ca
| |\  Merge: 6b53b37 3a603f2
| | | Author: Drimer
| | | Date:   Thu Mar 3 16:03:44 2022 +0100
| | |
| | |     Merge branch 'development' of https://project into development
| | |
| | *   commit 3a603f2cad073dcfabf2b9c239b9eb82c238e8d2
| | |\  Merge: f49ff40 e3ccdc4
| | | | Author: Drimer
| | | | Date:   Tue Mar 1 15:15:58 2022 +0100
| | | |
| | | |     Merge branch 'development' of https://project into development
| | | |
| * | | commit 6b53b37fdd909be9ebc1b30abe315dabc2524336
| | |/  Author: Drimer
| |/|   Date:   Thu Mar 3 16:03:32 2022 +0100
| | |
| | |       Added a check ...
| | |
* | | commit bf2f2839f84ceb52fafe6de1463b106cdafb55fb
|\| | Merge: f49ff40 e3ccdc4
| |/  Author: Drimer
|/|   Date:   Tue Mar 1 15:15:58 2022 +0100
| |
| |       Merge branch 'development' of https://project into development
| |
| * commit e3ccdc485fd6364776c0f1a3d4ea2d4b54ea5f64
| | Author: Drimer
| | Date:   Tue Mar 1 14:48:24 2022 +0100
| |
| |     Load ...
| |
* | commit f49ff4065812c4a4f045c97c5817be326a4bbef6
|/  Author: Drimer
|   Date:   Tue Mar 1 14:48:24 2022 +0100
|
|       Load ...
|
*   commit c7424d0925d9b9ff5e821fe070b8dfd2bd89109e
|\  Merge: 2eb50d7 c71d5c0
| | Author: Drimer
| | Date:   Tue Mar 1 14:17:20 2022 +0100
| |
| |     Merge branch 'feature/project0003-resource-file' into development
| |
| |     # Conflicts:
:
|
*   commit 5c36203c6d995e11138ccdc79a9daa86cf05aeb7 (origin/development_initial, feature/project-ToolManager, development_initial)
|\  Merge: 8564fae 3dc0393
| | Author: Drimer
| | Date:   Fri Mar 4 12:48:17 2022 +0100
| |
| |     Merge branch 'development' of https://project into development
| |
| *   commit 3dc03939829e05b2431f8e3c6a353f2da308b2a1
| |\  Merge: bf2f283 f2e18a9
| | | Author: Drimer
| | | Date:   Fri Mar 4 11:27:27 2022 +0100
| | |
| | |     Merge branch 'development' of https://project into development
| | |
* | |   commit 8564fae1510d4dd1a318f5b223d822d2effe591e
|\ \ \  Merge: bf2f283 f2e18a9
| |/ /  Author: Drimer
|/| /   Date:   Fri Mar 4 11:27:27 2022 +0100
| |/
| |         Merge branch 'development' of https://project into development
| |
| *   commit f2e18a9fd2c7dd67626b205e7ad765066f3db1ca
| |\  Merge: 6b53b37 3a603f2
| | | Author: Drimer
| | | Date:   Thu Mar 3 16:03:44 2022 +0100
| | |
| | |     Merge branch 'development' of https://project into development
| | |
| | *   commit 3a603f2cad073dcfabf2b9c239b9eb82c238e8d2
| | |\  Merge: f49ff40 e3ccdc4
| | | | Author: Drimer
| | | | Date:   Tue Mar 1 15:15:58 2022 +0100
| | | |
| | | |     Merge branch 'development' of https://project into development
| | | |
| * | | commit 6b53b37fdd909be9ebc1b30abe315dabc2524336
| | |/  Author: Drimer
| |/|   Date:   Thu Mar 3 16:03:32 2022 +0100
| | |
| | |       Added a check
| | |
* | | commit bf2f2839f84ceb52fafe6de1463b106cdafb55fb
|\| | Merge: f49ff40 e3ccdc4
| |/  Author: Drimer
|/|   Date:   Tue Mar 1 15:15:58 2022 +0100
| |
| |       Merge branch 'development' of https://project into development
| |
| * commit e3ccdc485fd6364776c0f1a3d4ea2d4b54ea5f64
| | Author: Drimer
| | Date:   Tue Mar 1 14:48:24 2022 +0100
| |
| |     Load ...
| |
* | commit f49ff4065812c4a4f045c97c5817be326a4bbef6
|/  Author: Drimer
|   Date:   Tue Mar 1 14:48:24 2022 +0100
|
|       Load ...

Upvotes: 2

Views: 251

Answers (4)

LeGEC
LeGEC

Reputation: 52151

Here is one way to :

a. get back to just before ff5b6a3 LoadExtensio...
b. squash all commits in one
c. replay all commits starting from ff5b6a3 up to your current commit on top of the result


  1. run git rebase -i ff5b6a3^

an editor will open, listing all the commits that git rebase has identified as "commits to keep" :

pick ff5b6a3 LoadExtensio...
pick 4477db6 Refactoring ...
...
  1. insert a break instruction before the first line, then save and exit
break  # <- here
pick ff5b6a3 LoadExtensio...
pick 4477db6 Refactoring ...
...

git will display the message :

Stopped at 5c36203 (Merge branch 'development' of https://project into development)

and suspend the rebase, waiting for you to run git rebase --continue whenever you see fit.

-> you have now reach point a. above

  1. run :
git reset --soft c7424d0 # <- use '--soft' to keep the changes in the staging area

# inspect the state of your code :
git diff --cached    # <- use '--cached' to view the *staged* modifications
# or
git difftool -d --cached

# if this is what you expect, create a commit with that :
git commit

-> you have now reached point b. : you now have a single commit which "combines" all the commits highlighted in your question

  1. run
git rebase --continue

-> you have now reached point c., and are done.

Upvotes: 1

hakre
hakre

Reputation: 198214

checkout the files of the revision you want, reset the branch (not the checkout) to the base revision, fast-import the files on the branch and done.

Upvotes: -1

jthill
jthill

Reputation: 60547

If bf2f2839f worked i.e. has the content you want, and 5c36203c is the last redundant attempt,

git replace 5c36203c bf2f2839f
git filter-branch -- --all

and it's all over but the pushing and any needed downstream rewriting (of history you didn't rewrite for them, based on the history you did).


edit: @LeGEC pointed out that the merges aren't completely redundant, there's additional commits you might want in your record.

To linearize a stretch of history from base to tip, where tip is the final merge and base is the newest ancestor you want to preserve, you can

git checkout base
git rev-list --topo-order --no-merges --reverse base..tip \
| git cherry-pick --stdin
git replace --graft tip @

with the rev-list | cherry-pick being pretty much what rebase does. Then if that produces a history you like, filter-branch to bake in the rewired ancestry in the remaining commits.


Be very careful about how you pick your replacements here, you're deep into "measure twice, cut once" territory. Try this on a scratch clone and check the results,

git clone -s . `mktemp -d`
cd $_
git replace etc.
git filter-branch etc.

and then if you like the results you can push them back to your main local repo, if you don't you can just delete or abandon the wrecked local clone, no harm done.

See the docs for those commands, if you've got tags you'll want to say how to rewrite their names since published tags are normally not supposed ever to move, and perhaps you could add --oneline to your log command to lose all the not-needed-here details about each commit.

Upvotes: 1

cyan-kinesin
cyan-kinesin

Reputation: 704

Use rebase.

First at all, YOU CAN ALWAYS CANCEL WITH git rebase --abort command.

command like this

# first, count how many far from current index.
git log --oneline | grep -n "Merge branch"

# pick last number. if that is 5

git rebase -i HEAD~5

then you will see interactive editing view

pick abcdef12 my old merge commit <-- old
pick bacdef12 my old merge commit 2
pick cacdef12 my old merge commit 3
pick dacdef12 my old merge commit 4
pick ebcdef12 my current commit <--- current (git HEAD)

Change pick to fixup that you want to squash except first line. they would be squashed to oldest one.

flag as reword for editing commit message for squashing.

pick zbcdef12 more old commit but you don't want
pick ybcdef12 more old commit but you don't want 2
reword abcdef12 my old merge commit <--- they will be squashed this.
fixup bacdef12 my old merge commit 2
fixup cacdef12 my old merge commit 3
pick dacdef12 my old merge commit 4 which you don't want to merge
pick ebcdef12 my current commit <--- git HEAD

type :wq.

and then you will see like this.

my old merge commit

# Please enter the commit message blablablabla

Edit commit message like this: Squashed: Implement awesome function and linting blablabla

type :wq

and git will automatically squash commits bacdef12 and cacdef12 to abcdef12.

git log --oneline will show up like this:

ebcdef12 my current commit <--- current (git HEAD)
dacdef12 my old merge commit 4 which you don't want to merge
newhash Squashed: Implement awesome function and linting blablabla.

Upvotes: 0

Related Questions