删除所有本地变更集并恢复为树

我正在使用 Mercurial,我在本地遇到了一个可怕的麻烦,有三个头。我不能推,我只想删除所有的本地更改和提交,重新开始与完全干净的代码和一个干净的历史。

换句话说,我希望最终得到(a)与远程分支提示中存在的本地代码完全相同的代码,以及(b)没有任何本地提交的历史记录。

我知道 hg update -C会覆盖任何本地更改。但是如何删除本地提交呢?

说清楚,我没有兴趣保留我在当地完成的任何工作。我只是想用最简单的方式恢复到一个完全干净的本地结帐。

40188 次浏览

Ok. So just delete all the local stuff, hg init the new local repository and hg pull the latest tip you have. Don't forget to hg update after this.

Just delete everything you have on your local system and re-clone the remote repo.

You'll want to make a local clone where you preserve only the changesets that are also present in the remote repository. Use TortoiseHg, hg log or similar to figure out which of your revisions is that lastest revision you didn't make (the one before the mess started). Using hg outgoing can help here -- it will list all the changesets you made -- pick a revision number earlier than any of those.

If the target revision is called good and your clone is called foo, then do:

hg clone -r good foo foo-clean

This will be a fast, local operation -- there is no reason to download everything again. The foo-clean clone will only contain changesets up to revision good. You can now replace foo-clean/.hg/hgrc with foo/.hg/hgrc in order to preserve your repository-local settings such as the default push/pull path.

When you are satisfied that foo-clean has everything you need from foo, then simply delete foo and rename foo-clean to foo. Do a hg pull to get any new changesets from the remote repository into your clone and continue like normal.


If nobody has pushed new changesets to the remote repository, then it is very simple to determine which revision you want to use as good above: hg id default will tell you the ID of the tip in the remote repository.

When the simplest way (a new hg clone) isn't practical, I use hg strip:

% hg outgoing -l 1
% hg strip $rev # replace $rev with the revision number from outgoing

Repeat until hg outgoing stays quiet. Note that hg strip $rev obliterates $rev and all its descendants.

Note that you may have to first enable strip in your Mercurial settings.

PS: an even smarter approach is to use the revset language, and do:

% hg strip 'roots(outgoing())'

You may use

hg strip revision

to kill any revision and its subtree in your local repository.

https://www.mercurial-scm.org/wiki/Strip

But don't try to use it for anything that has been already pushed.

hg strip `hg out --template "{rev} {author}\n" | grep YOUR_AUTHOR_NAME | cut -d " " -f 1`

does the trick for me.

It strips all revisions that aren't pushed to the default repository which are created with your author name.

You can also use this style to make it not checking with the default repository but with another Repository

hg strip `hg out OTHER_REPO_ALIAS --template "{rev} {author}\n" | grep YOUR_AUTHOR_NAME | cut -d " " -f 1`

If you are using TortoiseHg, one simple way to get out of a (small) mess is to first update to the latest revision, then select your changesets and initiate "merge with local". When the merge dialogue appears, simply click the little '+' icon to reveal some extra options, one of which is "discard changesets from merge target (other) revision". Doing this will mean your changesets will still be in the repo and get pushed, but will have no effect, because they will be discarded in the merge. If you have a lot of changesets spanning many heads, you might not want to pollute the repo this way, but it's a simple fix and worth considering if the changesets you are discarding contain data that you may later want to reference.