Reputation: 16506
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:
master
branchHow do I do this in git / sourcetree without losing the local updates that I have?
Upvotes: 0
Views: 54
Reputation: 1395
If I understood your problem correctly, you can do the following:
git stash
git checkout -b BRANCH_NAME
git push origin BRANCH_NAME
Now his changes are safe on a new remote branch.
master
branch:git checkout master
git reset --hard COMMIT_HASH
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).
git stash apply
That's it!
Upvotes: 0
Reputation: 27357
This is relatively straight-forward.
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
Fetch the changes they've made to master
git fetch --all
Push their changes to a new branch
git push origin origin/master:newBranch
Force push what should be master to master.
git push --force origin tmpMaster:master
Upvotes: 1
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
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)
git clone <url> <fix-the-repo>
cd <fix-the-repo>
git checkout master
git branch feature
(master and feature now point to the same commit-id)git reset --hard <SHA>
git push -f origin master
git push origin feature
cd <local-repo>
git fetch origin
(you should see a message about a forced update of the master branch)git log --oneline --decorate --graph --all
git push origin master
Whew!
Upvotes: 1