0

I have history like that:

A <- B <- C <- D <- F

where F is newest.

B and C cancel each other out (really its 3 commits: merge feature to master, revert the merge commit, merge pr containing the reverting commit), so that:

git diff A D

shows no diff at all.

So it seems that applying D and F on top of A should be trivial. I want to get A <- D <- F (there's actually more commits after F).

  1. I've tried cherry-picking
git reset --hard A
git cherry-pick D F

and this is not trivial, as among D and F commits there's merge commits and git cherry-pick asks for additional info on which commit is to be treated as mainline.

  1. I've tried rebasing
git branch -c branch_A A
git branch -c branch_D D
git rebase --onto branch_A branch_B branch_C

which based on git rebase docs should do exactly what I want. Yet still im getting conflicts to fix.

How do I do that and why is it hard?

I simply want the worktree after every re-applied commit to be exactly the same as worktree in the master branch at the same commit.Worktree's start from the same state at commits A or D and I want for every commit applied to D to do exactly the same as it was applied to A.

thanks in advance

EDIT: let me ask here a different but connected question.

As really the problem I have is to merge a reverted commit again to the branch. This is impossible now as the commit already exists in the history, so the merge results in no conflicts but no change at all, for git this is merged already in the past.

How do I do this merge? Maybe I shouldn't try to remove the commits from main branch, but rather modify a feature branch so that its no longer recognizable as something that was already merged? Will this route be easier?

zaabson
  • 151
  • 12
  • 1
    `git replace --graft d a; git filter-branch` will do it, there's options for stuff like re-hanging tag labels and such so check the docs. – jthill Sep 27 '22 at 19:23
  • tried it and it didn't work, I believe because D is a merge commit ;/ and the second parent remained (?) EDIT: it should replace all parents according to docs, i dont know what happened . Nevertheless it changes all the commit hashes so I'm gonna try something else – zaabson Sep 28 '22 at 17:18
  • Well, then `git replace --edit` it and make the parents be what you want. Id's are going to change, that's Git's strongest guarantee, up there in "because math" territory: things with different histories or contents have different id's. – jthill Sep 28 '22 at 20:45

1 Answers1

1

It's hard with git rebase because there are merges.

It used to be impossible with git rebase, but now there's git rebase --rebase-merges.1 But rebase-merges doesn't copy the original merges; it re-performs the original merges. If there were any conflict resolutions, or (especially) any evil merges, you may run into various problem.

It's easier with filter-branch or filter-repo, as jthill notes in a comment, so that's probably the way to go. However, you can consider using git rebase --rebase-merges and either squashing together or simply deleting the offending commits. Given that one of the three commits in question is a merge commit, you literally can't squash it, so you will have to delete it. This affects the resulting topology so you might still have a lot of work to do.

Without seeing a more complete graph (e.g., from git log --decorate --oneline --graph) and where you want the various branch names and tags and other decorations to wind up, that's the most I can say for certain here.


1The old git rebase --preserve-merges had some of the right logic but could not handle general cases of editing the instruction sheet.

torek
  • 448,244
  • 59
  • 642
  • 775