Fold out redundant divergent changes in Git

72 Views Asked by At

Given the initial state:

A - origin/branch-1
|
X   A - origin/branch-2
| /
M - master

where both A commits make the same edit and where it has no overlap with the X commit, what is the best way to get things to the following state?:

A - origin/branch-1 - origin/branch-2
|
X
|
M - master

Specifically, I want to NOT end up in the following state as (regardless of the physical actions taken) the logical actions are sequential and do not include a merge (i.e. the state where A didn't follow X was never valid in the first place and should be removed from the repository):

M - origin/branch-1 - origin/branch-2
|  \
A   |
|   |
X   A
| /
M - master
1

There are 1 best solutions below

3
On

A remote-tracking name like origin/branch-1 or origin/branch-2 represents your Git's memory of some other Git's branch name. That is, your Git called up their Git; their Git said my branch-2 holds hash ID _____ (fill in the blank with an actual hash ID) and your Git then grabbed that commit if necessary, so that you have it, and created or updated your origin/branch-2 to point to that commit.

Assuming that A and A are really A and A'—i.e., two different commits with different hash IDs but similar effects), your job is to convince the other Git repository—the one over on origin—to set its branch name branch2 to point to whichever of these two is the desired hash. You have that hash ID in your Git under the name origin/branch-1, so:

git push --force origin origin/branch-1:branch-2

will have your Git call up the Git at origin and tell it: I don't care which commit your branch2 identifies right now, make it point to hash ID _____ right now! (with the blank here filled in with the hash ID your Git has stored under the name origin/branch-1).

They may obey this command, or they may not. If they don't, you can't fix it from here like this: you will have to get someone who can issue such commands, to do that. That someone might be yourself but logged in to the server directly, or it might be someone with administrative permissions.1

Suppose they do obey. Their branch-2 currently points to some commit—maybe the one you think, maybe some other commit that's been added since then. Either way, though, they obey your forceful command, and now their branch-2 points to the other A. Your Git sees that they did obey, and updates your origin/branch-2, and you get what you wanted.

Be careful with force-push operations. Note that you can use --force-with-lease but to do so you'll need to create a local branch name on your end. The --force-with-lease operation needs to know what hash ID to hand to them, as it changes the command from I don't care, set to ___ to I think your branch is currently ___; if so, set it to ___; either way, tell me if I was right and if you obeyed. To make all this work, your Git you to have your own local name branch-2 that it can map back to your local origin/branch-2 so that it can get the hash IDs for both blanks.


1As a subterfuge, sometimes if you can't convince another Git to update a branch name, you can just tell that other Git to delete the branch name entirely. Having deleted it, you can then tell that other Git to create a whole new, totally different branch name that merely happens to have the exact same spelling. A lot of times, a Git that won't let you force-update a branch name, will let you delete and re-create it however you like.