Reset/revert a whole branch to another branches state?
Solution 1
One way to achieve this is through git reset
. While on branch B
execute
git reset --hard A
Thereafter, branch B
points to the head-commit of A
. The --hard
option resets the index and working tree so that all tracked files are reset to the version in branch A
. The old HEAD commit-ID of A
is stored in .git/ORIG_HEAD
in order to allow undoing the change.
Alternatively - while not on branch B
- you can delete branch B
and re-created it like this:
git branch -d B # delete branch B
git branch B A # re-create branch B and let it point to the commit of branch A
Other than the first suggestion, this will leave the index and working tree untouched.
Solution 2
If you want your branch B
to look exactly like branch A
. You could just do a reset --hard
git checkout branch-B
git reset --hard branch-A
Be careful you will lose commits in this case. Your branch-B will look exactly like branch-A, whatever commits were made to branch-B, that were not present in branch-A, will be lost. Also if branch-B is shared with other people, its not recommended to perform this operation.
In that case you could try reverting the commits you don't want in branch-B
git revert <sha-of-"some other commit">
git revert <sha-of-"merge of other stuff from branch C (into branch B)">
The second commit looks like a merge commit so you might have to pass the parent as well.
git revert <sha-of-"merge of other stuff from branch C (into branch B)"> -m1
Solution 3
For completion, let's add this very simple way to achieve it :
git branch -f branchB branchA
It takes advantage of the fact that branches in git are mere pointers. This command just replaces the ref to the tip commit of one branch with another. No need to go into complex structure changes to build something you already have.
(See the doc)
Solution 4
I realize (very late, I admit) that OP in fact never asked to delete all of B's history but rather changes, so my first answer, like most others above, is indeed achieving the expected working tree but unfortunately it's at the expense of branch B
's history, which is lost.
So let's suggest here the plumbing way to both preserve full history and achieve the exact tree you wanted to obtain, with git commit-tree
(see doc)
# to be executed from branch B
git reset --hard $(git commit-tree -m "Reset to A" -p $(git rev-parse --abbrev-ref HEAD) $(git rev-parse A)^{tree})
Explanation
The git commit-tree
command broken down :
git commit-tree -m <message> -p <parent> <tree>
-
<tree>
needs here to be branchA
's tree, we'll get it with$(git rev-parse A)^{tree}
.
-
<parent>
must point toB
's tip :$(git rev-parse --abbrev-ref HEAD)
- then the above two parameters plus the message are used by the command to produce a new commit,
- and finally
git reset --hard
sets current branch (B
) on the fresh new commit returned bygit commit-tree
.
Looks convoluted at first, but a great tool.
Solution 5
As others have shown it is true that a git reset --hard
will make branch B look exactly like branch A. However, this will delete B's history. A different approach, which avoids this problem, is to create and apply a patch file:
git checkout A
git diff B > /tmp/AtoB.patch # Generate changes needed to make B match the current branch A
git checkout B
git apply /tmp/AtoB.patch # Update files to match the state of A
git add -A # Track any deleted or newly created files
git commit -a -m "Make B match A" # Commit the changes
Now we're not "rewriting history" so there will be no controversy when you push to origin. This approach has the benefit that the syncing is a discrete commit in B's history that could be reverted at any time. NOTE however that the commit history from branch A is lost in the translation.
BTW: If you get an error about binary files, add the --binary flag to your diff command like this
git diff --binary B > /tmp/AtoB.patch
daniel451
Updated on October 23, 2021Comments
-
daniel451 over 2 years
I have a branch A and a branch B (and some other branches).
Lets say A's commit history looks like:
- commit 5
- commit 4
- commit 3
- ...
And B's commit history:
- some other commit
- commit 4
- merge of other stuff from branch C (into branch B)
- commit 3
- ...
Basically what I want is to "delete" all changes made by the commits some other commit and merge of other stuff from branch C to branch B.
I want the working tree of branch B to be exactly the same like branch A's working tree.
How do I achieve this?