I have to rewrite very old commits in the git repository and not sure if it's even possible. By rewriting I mean squashing and rewording some commits. It's also worth mentioning that there are lots of merge commits in the repository and most of them should stay.
So let's say my repo is A <- B <- C <- D <- E <- ... X <- Y <- Z
and I have to squash A
and B
and reword C
and D
.
Well, I created a new branch tmp
and have done the following things:
tmp
branch references to the D
commit.tmp
branch I have smth like this - A' <- C' <- D'
. By the way, it's actually fine that rebase ignores all the merge commits before D
, but merge commits after D
must stay.And now I want to merge somehow my tmp
branch and master
branch to get something like this: A' <- C' <- D' <- E <- ... <- X <- Y <- Z
with all merge commits starting from E
. For me, it looks like changing a parent for commit E
, but not sure if it's possible to do this. There is probably some other ways to achieve what I need.
UPDATE
I tried to make a git replace D D'
on the master
branch and after this git filter-branch -- tmp
and it almost worked. Unfortunately, when I executed replace some commands after D
moved to the bottom and the history looked like A <- E <- F <- A' <- C' <- D' <- G <- ... <- X <- Y <- Z
and it was very strange to me, but when I tried git replace C D'
everything worked fine except duplicated D
and D'
commits. Not sure if this is a good approach, but look like the only one in my case.
I'm sorry if it's confusing, I'll try to clarify if something is unclear.
You can use git filter-branch
with parent-filter
to "re-parent" E
. The docs, including examples to do just the sort of thing you want to do, are here:
https://git-scm.com/docs/git-filter-branch
As always, the commit E
itself cannot be changed; really you are creating E'
which has parent of D'
, and so on down the tree until you get X'
, Y'
, Z'
. However, because you're accepting the commits' content "as is" rather than re-applying patches the way rebase does, this works fine for any commit graph (whereas it's not very hard to create a repo where rebase
will do the wrong thing with merge commits, even when told to preserve merges).
(The reason you normally rebase instead of reparenting like this, is that you generally can't assume the new base has the same content as the old base. But in this case it sounds like you can; only the commit graph leading up to D'
differs from D
, not the content.)
The important thing to remember is that you are rewriting history, which will break all existing clones of the repo. For something this sweeping, the safest thing is to: