在执行git commit --amend之后再执行git push的情况下,如何解决“Your branch and 'origin/master' have diverged”这个问题?
我做了一些修改,导致产生了若干提交。然后我已经把它推送到了裸仓库。
edit 1
git commit -a
edit 2
git commit -a
git push
随后,我对已经推送到裸仓库的最近一次提交进行了修订。然后又做了一些改动并提交。
edit 3
git commit --amend
edit 4
git commit -a
edit 5
git commit -a
我知道不应该修改已经推送的提交,但我确实这么做了。
Git的状态现在显示分支已经分叉
git status
On branch master
Your branch and 'origin/master' have diverged,
and have 6 and 1 different commits each, respectively.
(use "git pull" if you want to integrate the remote branch with yours)
nothing to commit, working tree clean
我该如何解决这种情况?
解决方案
Fix is to either: be forced (to roll-back) or to force it.
- If, and only if you have not yet pruned the repo, (can be automatic by git's garbage collection, e.g.,
git gcand manual via agit prune, etc.), then you may be able to roll back the commit. Otherwise skip to option 2.
从提交历史中选择一个之前的点(例如,在查找已amend的提交时的线索:使用类似 git log --topo-order --graph --all --reflog 之类的更详细描述,找到最近的一个有效提交)
例如如果远端(origin/master)仍处于编辑的第3 次状态(+1差异),它可能看起来像这样(为便于说明而简化):
console
$ git log --topo-order --graph --all
# log abriviated for space
* commit 12345678 edit 5 (HEAD -> master)
|
| * commit 23456789 edit 4
|/
| * commit 56783948 other-work (origin/master, origin/HEAD)
| * commit 2345678A edit 3
|/
| * commit 2345678B edit 2
|/
| * commit 2345678C edit 1
|/
* commit root (probably need to add --boundary to see this kind of root)
你可以回滚到编辑的第3 次(你之前的版本):
bash
# from edit 5
# first backup your changes (-M to force move the local diverged copy to some other name can work too if you are on a different branch from master already)
git branch my-edit-5
# force off of the diverged branch
git checkout --force my-edit-5
# remove the local diverged branch (unless -M was used already)
git branch -D master
# now re-checkout "master" (by default this will select the origin/master and create a new ./master setup to track the origin master, otherwise you must manually do all that)
git checkout master || git checkout -B master origin/master
# check that it is rolled back
git status
# should say you are behind by 1 and can use git pull, so let's pull
git pull
# done: you are no longer diverged on your master branch
#
# OPTIONAL: now you can try to merge in from the other branch (locally, as it still probably tracks origin/master)
git merge --no-ff -- my-edit-5 master
# if all goes well it will merge as normal, otherwise
# don't bother merging
git merge --abort
2. Here is my recommendation from your specific example (adjust branch names as needed):
下面给出基于你这个 具体 示例的建议(根据需要调整分支名):
bash
#setup
git fetch --all --no-prune
git branch temp-name-that-is-unique-and-unused
git checkout --force temp-name-that-is-unique-and-unused
# backup old
git git branch -M master old-master-5
# throw out any untracked mess
git --unset-upstream old-master-5
# jump to upstream
git checkout --force -B master origin/master
# clean up
git branch -D temp-name-that-is-unique-and-unused
git fetch --all --no-prune
# check everything is ok
git log --branches --tags --topo-order --stat --notes --graph --show-signature --all --reflog
3. What if you need those changes on old-master-5?
(This being why you should not have used git commit --amend.)
警告:我通常会给我的git服务器设置保护(例如,即使GitHub的仓库也可以配置来防止这种情况,但默认并非如此),以阻止我接下来要解释的做法;这本来就非常危险。
There is a --force option to git push you can use to force your local copy to the server. But if you force it, there will be no undo afterwards.
有一个 --force 选项,可以用来强制把本地副本推送到服务器到 git push。但如果你强制执行,之后就没有撤销的机会。
You could also try using git push --atomic --force and the amended commit will be forced to the remote (and --atomic is critical to prevent causing harder to fix issues here).
你也可以尝试使用 git push --atomic --force,修改后的提交将被强制推送到远端(而 --atomic 对防止在这里引发更难以修复的问题至关重要)。