fox
fox

Reputation: 16506

Rolling back remote git repo but maintaining local changes

A collaborator was supposed to push to a separate branch but instead made about 60 commits to the master of a repo. He's now made a hash of that branch. I need to:

  1. revert his changes
  2. ideally, push the changes that he made to a separate branch
  3. commit and push the local changes that I have made on my master branch

How do I do this in git / sourcetree without losing the local updates that I have?

Upvotes: 0

Views: 54

Answers (4)

tiagodws
tiagodws

Reputation: 1395

If I understood your problem correctly, you can do the following:

To keep your current not commited changes (if any):

  • Stash them to use them later:
git stash

To save his changes:

  • Create a new local branch with his changes:
git checkout -b BRANCH_NAME
  • (Optional) Push his changes to github:
git push origin BRANCH_NAME


Now his changes are safe on a new remote branch.


To revert the changes on master:

  • Go back to the master branch:
git checkout master
  • Reset master to the commit before his changes:
git reset --hard COMMIT_HASH
  • Push the updated branch:
git push -f origin master


You may need to check if the denyNonFastforwards flag is set to true on your .git/config file (you can change it to false and undo it after).


To get back your previously stashed changes:

  • Apply the last stash:
git stash apply

That's it!

Upvotes: 0

Rob
Rob

Reputation: 27357

This is relatively straight-forward.

  1. Create a temporary branch pointing to what master should be. For example, if you've got the most update-to-date version of master except for the bad commits, you'd run:

    git branch tmpMaster origin/master
    ## Or
    git branch tmpMaster sha_of_good_master
    
  2. Fetch the changes they've made to master

    git fetch --all
    
  3. Push their changes to a new branch

    git push origin origin/master:newBranch
    
  4. Force push what should be master to master.

    git push --force origin tmpMaster:master
    

Upvotes: 1

torek
torek

Reputation: 488213

If you can log into the server directly, you can fix it all there with relatively little fuss, but you have to know something about Git's innards.

If not, your best bet is to use a temporary repository (i.e., a fresh git clone) or temporary work-tree (i.e., git worktree add, assuming your Git is at least version 2.6 or so) on a machine you control that has push access. This does not require any special Git expertise at all, though if you are working with git worktree add you will need to not be on your master branch (i.e., if you're working within your own branch, you are fine here).

To use a fresh but temporary clone, run git clone as usual (making a new clone in a new directory). Then chdir (cd) into the new clone as usual. You should be on master; if not, run git checkout master. Your master will match origin/master since this is a new clone.

To use a temporary work-tree, run git add worktree ../fix master from the top level of your regular work-tree. Chdir into the new work-tree, which is now on your own master branch. run git fetch origin && git reset --hard origin/master to update your master to match origin/master.

Now that your master is synchronized with the other guy's master that should have been some other branch, create the branch he should have created: git checkout -b feature or whatever. Then push the new branch to the server as usual.

Next, set master up as it should be. Here you have two choices: you can roll all of his changes back with git reset --hard, and this is fine as long as you can get yourself, him, and anyone else who obtained his changes to likewise roll back. (This rolling back is all automatic for you if you are using your own repository with a temporary work-tree.) Or, you can make a new commit on master that uses the tree he should have left in place. Either way you must identify the commit to restore.

To do the former, run git reset --hard <commit-hash>. To do the latter, run git rm -rf . && git checkout <commit-hash> -- . && git commit (note that extra dots here). The former rewinds master, removing commits; the latter makes a new commit that has the same tree as the earlier commit (that's why we first remove everything, then check out everything from the earlier commit).

Now, for the latter case, you can just git push origin master as usual. For the former (git reset) case you must git push --force origin master.

(The danger with the --force method is that other users who are pushing to master may be making changes while you are doing all this; you will lose their changes as well. But since things are in a bad state now, you should make sure no one else is making these changes anyway.)

You are now done, or mostly done: you can remove your clone or your temporary work-tree. If you used a clone and used the "reset master" method, you may also need to reset your own repository's master as well, if you picked up the other guy's commits and incorporated them into your own master in this clone (the one that was not a temporary clone). If you merely added a new commit to fix his mistake, you are fine all the way around.

Upvotes: 0

Ben
Ben

Reputation: 1367

I don't know the equivalent steps to do this in sourcetree, but here is how it would be done from a terminal. Hopefully you can replicate these steps in sourcetree.

To do these tasks, you will make use of git reset --hard as well as git push -f. You will need to ensure that your remote allows non-fast forward pushes (The setting that prevents non-fast forward pushes is in your remote's .git/config file under [receive] > denyNonFastforwards = true. Just set it to 'false' temporarily while you fix things)

  1. clone the remote
    • git clone <url> <fix-the-repo>
  2. Armed with the SHA of what master is supposed to be:
    • cd <fix-the-repo>
  3. Create the feature branch your collaborator should have pushed to
    • git checkout master
    • git branch feature (master and feature now point to the same commit-id)
  4. Reset master to what it is supposed to be
    • git reset --hard <SHA>
  5. Push the updated branch pointers to the remote
    • git push -f origin master
    • git push origin feature
  6. Go to your local repo
    • cd <local-repo>
  7. Fetch the remote to ensure everything is fine and dandy
    • git fetch origin (you should see a message about a forced update of the master branch)
    • git log --oneline --decorate --graph --all
  8. Push your local changes
    • git push origin master

Whew!

Upvotes: 1

Related Questions