Reset/revert a whole branch to another branches state?

32,045

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>
  1. <tree> needs here to be branch A's tree, we'll get it with $(git rev-parse A)^{tree}.
  2. <parent> must point to B's tip :$(git rev-parse --abbrev-ref HEAD)
  3. then the above two parameters plus the message are used by the command to produce a new commit,
  4. and finally git reset --hard sets current branch (B) on the fresh new commit returned by git 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

Share:
32,045
daniel451
Author by

daniel451

Updated on October 23, 2021

Comments

  • daniel451
    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?