Remove all local bookmarks and commits from mercurial repository - mercurial

I'm looking for the simplest way strip all local commits (i.e. not committed in the remote repo) and remove all local bookmarks from a mercurial repository. I know I can run something like hg bo | xargs hg strip && hg bo | xargs hg bo -d (obviously with stripping out the commit hashes) but I've had trouble trying things like this when I have stacked commits.
Is there an easy (if a little heavy handed, maybe) way to blow away all local changes to your copy of the repository, leaving you with one that mirrors the remote repository exactly? Thanks!

In order to strip subtree, you can strip only oldest ancestor by hand
The strip command removes the specified changesets and all their descendants.
In order to get roots of not-published changesets, you can use revsets and templating. If you have single remote repo (with default alias) it will be just
hg log -r "roots(outgoing())" -T "{node|short}\n"
for many remotes revset can be modified to
roots(outgoing(PATH1) & outgoing(PATH2) ... & outgoing(PATHN))
or (with some assumtions: a) Mercurial have support for phases b) secret phase not used c) remote repo is ordinary "publishing repo")
roots(draft())
(not pushed changesets are in draft phase by default)

Related

How to remove files from bitbucket repository? [duplicate]

I copied a large amount of data from my labs file server to my laptop, then accidentally committed it to the Mercurial repository I'm using to back up my thesis.
Now I have 200+ MB of data I don't need taking up space on my hard disk, even after I've deleted the files. This isn't really a problem, but the bitbucket repository I sync to only gives me 1 GB of space, which I will need for my data.
Stupidly I then deleted the files and committed again, so I can't just use rollback as described in https://stackoverflow.com/a/3290523/961959 without creating a new repository and having to reset up everything with bitbucket.
Is there a way I can fix this, so I don't waste a quarter of my repository space? Can I rollback the last 2 commits I did?
You can:
Use hg strip to remove the changeset where you've added the files and all of its
descendants (note that this will obliterate them completely, so only do it if these files are the only thing committed during this time).
Import those changesets to MQ and edit them.
Use hg convert from Mercurial to Mercurial with --filemap option.
Clone your repository up to faulty changeset and push from this one instead, as described in FAQ.
I'm going to describe what I would do if I wanted to roll back two most recent commits.
I assume that you haven't pushed to Bitbucked yet. I also assume you have these changesets on top of your DAG:
o C: Deleted files FILE1 and FILE2
|
o B: Some nice changes; also added large files FILE1 and FILE2
|
o A: Some good changeset
Here, C should be removed altogether, B should be edited (FILE1 and FILE2 additions should be rolled back), and A and below should be left as they are.
Caveat: this only works if B and C were not pushed onto Bitbucked (or any other public repo) yet.
You'll need to enable the MQ extension to do this. To do this, add these lines to the .hg/hgrc file in your repo:
[extensions]
mq=
Steps
First, I strip C:
$ hg strip C
Now, C is obliterated, and the parent is B. I import B as a Mercurial Queues patch to edit it:
$ hg qimport -r B -n B.patch
Now I have one patch on top our queue, which was created from B. I remove the big files, and refresh the patch:
$ hg forget FILE1 FILE2
$ hg qrefresh
Now B.patch no longer includes the big files. However, they are still on the disk, albeit not controlled bu Mercurial. I now finish my work with MQ:
$ hg qfinish -a
Here's what I have at the moment:
o B1: Some nice changes, no big files here
|
o A: Some good changeset
In case the changes are already pushed to BitBucket, it does offer an option to strip changesets from the server. After performing the strip locally, you should access the url:
https://bitbucket.org/<user>/<repo>/admin/strip
It'll offer an option to strip all changes following a specific commit.
NOTE: There used to be a link to reach that URL in the repo config menu. Now the only way to access it seems to be typing it directly.

Safe way to purge history - Mercurial

I cloned a project to my local directory and made a lot of changes. My current goal is to push my changed code to a new branch in the remote repository. And eventually this new branch will be merged back to default.
My problem is, because of some stupid effort in the past a few weeks to try to recover some missing files, I end up with a few branch names that I don't want being shown in public.
Here's what I have:
$hg branches
dev-v02 197:xxxxx
dev2 194:xxxxx
dev 183:xxxxx
qa 189:xxxxx
$hg branch
dev-v02
My question is, if I push my current branch dev-v02 to the remote repository by "hg push --new-branch", and this branch later get merged back to default, will the unwanted branches show up in history of default? And if so, is there a safe way to purge them?
I do NOT want to discard my changes. I just don't want the unwanted branches showing up in "hg branches" or "hg his" commands by whoever later clones the project from the remote repository. I searched online and found "hg strip" but I couldn't tell from the article if it would also remove the changes I've made. Thanks.
Edit: I just cloned my local repository by "hg clone -r 197 original-dir dest-dir" as suggested by both kevin and chessbot and now hg branches shows:
dev-02 192:xxxxx
qa 187:xxxxx (inactive)
I guess "qa" remains because I had pushed it to the remote as a QA branch and closed it later, and I just have to live with that. I will push from this new directory from now on. Thanks guys for your help.
Try hg push --new-branch -b dev-v02 to specify that you're pushing only that branch.
(See: https://www.mercurial-scm.org/repo/hg/help/push)
Another thing you could do: Clone the repository locally on your machine, strip out the branches you don't want, and then push that clone to the server. Then you retain your history locally without pushing it to everyone else.
It depends.
Branches are permanently associated with a commit. The branch is part of the commit, and contributes to the hash. Changing the branch of a commit in the past would alter all commit hashes from that point forward. This is quite different from Git, where a branch is little more than an ephemeral pointer to a HEAD. Such pointers are implemented in Mercurial as bookmarks.
If the unwanted branches appear on commits which are (ancestors of) commits you want to publish, there is very little you can do, short of recreating the history with all-new hashes. This could (for instance) be done with hg export and hg import, along with local cloning and (probably) a certain amount of shell scripting. More efficiently, you could use the convert extension to automate the process. Since this changes commit hashes, it will likely cause serious problems if any of the commits have already been distributed publicly.
If you have no interest in sharing the offending commits, you can simply not publish them. This can be done with selective pushing. However, since you'll always have to manually exclude those commits every time you push, it's probably safer to clone and strip (or clone selectively with the -r flag). You can then push from your partial clone with impunity. Assuming you have a sufficiently recent version of Mercurial, you can also force the commits into the secret phase, so that they will not be pushed:
hg phase -fs revisions
You don't want to use hg strip, because it permanently removes the commits from the history (see Editing History in the Mercurial wiki)
If I were you, I would close the branches instead:
hg up -C badbranch
hg commit --close-branch -m 'close badbranch, this approach never worked'
hg up -C default
(source: Pruning branches in the Mercurial wiki)
After closing a branch, hg branches doesn't show it anymore.
If you do want to see closed branches, use the -c parameter:
hg branches -c
Disadvantage:
hg his still shows closed branches.
You could use the -b parameter though, to show only the default branch:
hg his -b default

Add a parent to the original changeset in Mercurial

I have a project with 24 months of source control history in a Mercurial repository.
I've recently found some old tarballs of the project that predate source control, and i think they would be useful to import into the repository as "pre-historic" changesets.
Can i somehow add a parent to my initial commit?
Alternatively, is it possible to re-play my entire repository history on top of the tarballs, preserving all metadata (timestamps etc)?
Is it possible to have the new parent commits use the timestamps of these old tarballs?
You can use the convert extension to build a new repository where the tarballs are imported as revisions before your current root revision.
First, you import the tarballs based on the null revision:
$ hg update null
$ tar -xvzf backup-2010.tar.gz
$ hg addremove
$ hg commit -m 'Version from 2010'
$ rm -r *
$ tar -xvzf backup-2011.tar.gz
$ hg addremove
$ hg commit -m 'Version from 2011'
I'm using addremove above to give Mercurial a chance to detect renames between each tarball (look at the --similarity flag to fine-tune this and use hg rename --after by hand to help Mercurial further). Also, I remove all the files in the working copy before importing a new tarball: that way the next commit will contain exactly the snapshot present in the tarball you unpack.
After you've imported all the tarballs like above, you have a parallel history in your repository:
[c1] --- [c2] --- [c3] ... [cN]
[t1] --- [t2] --- [tM]
Your old commits are c1 to cN and the commits from the tarballs are t1 to tM. At the moment they share no history — it's as if you used hg pull -f to pull an unrelated repository into the current one.
The convert extension can now be used to do a Mercurial to Mercurial conversion where you rewrite the parent revision of c1 to be tM. Use the --splicemap flag for this. It needs a file with
<full changeset hash for c1> <full changeset hash for tM>
Use hg log --template '{node} ' -r c1 -r tM > splicemap to generate such a file. Then run
$ hg convert --splicemap splicemap . spliced
to generate a new repository spliced with the combined history. The repository is new, so you need to get everybody to re-clone it.
This technique is similar to using hg rebase as suggested by Kindread. The difference is that convert wont try to merge anything: it simply rewrites the parent pointer in c1 to be tM. Since there is no merging involved, this cannot fails with weird merge conflicts.
You should look at using rebase. This can allow you to make the changes the 2nd changeset on your repo ( you have to rebase from the 1st ).
https://www.mercurial-scm.org/wiki/RebaseExtension
However, note that if there are other clones of this repo existing ( such as for fellow developers, or on a repo server ), you will have issues with them pulling the revised repo. You will probably have to co-ordinate with the owners of those clone's to get all work into a single clone, rebase that clone, and then have everyone re-clone from the revised clone. You will also have to change the phase the of the changesets.
https://www.mercurial-scm.org/wiki/Phases
Honestly though, I would just add them to your 'modern-day' repo, I don't think making them pre-historic would give you any notable advantage over adding them to the top.

Accidentally committed a large amount of raw data in Mercurial, how do I keep it from overloading my bitbucket repository?

I copied a large amount of data from my labs file server to my laptop, then accidentally committed it to the Mercurial repository I'm using to back up my thesis.
Now I have 200+ MB of data I don't need taking up space on my hard disk, even after I've deleted the files. This isn't really a problem, but the bitbucket repository I sync to only gives me 1 GB of space, which I will need for my data.
Stupidly I then deleted the files and committed again, so I can't just use rollback as described in https://stackoverflow.com/a/3290523/961959 without creating a new repository and having to reset up everything with bitbucket.
Is there a way I can fix this, so I don't waste a quarter of my repository space? Can I rollback the last 2 commits I did?
You can:
Use hg strip to remove the changeset where you've added the files and all of its
descendants (note that this will obliterate them completely, so only do it if these files are the only thing committed during this time).
Import those changesets to MQ and edit them.
Use hg convert from Mercurial to Mercurial with --filemap option.
Clone your repository up to faulty changeset and push from this one instead, as described in FAQ.
I'm going to describe what I would do if I wanted to roll back two most recent commits.
I assume that you haven't pushed to Bitbucked yet. I also assume you have these changesets on top of your DAG:
o C: Deleted files FILE1 and FILE2
|
o B: Some nice changes; also added large files FILE1 and FILE2
|
o A: Some good changeset
Here, C should be removed altogether, B should be edited (FILE1 and FILE2 additions should be rolled back), and A and below should be left as they are.
Caveat: this only works if B and C were not pushed onto Bitbucked (or any other public repo) yet.
You'll need to enable the MQ extension to do this. To do this, add these lines to the .hg/hgrc file in your repo:
[extensions]
mq=
Steps
First, I strip C:
$ hg strip C
Now, C is obliterated, and the parent is B. I import B as a Mercurial Queues patch to edit it:
$ hg qimport -r B -n B.patch
Now I have one patch on top our queue, which was created from B. I remove the big files, and refresh the patch:
$ hg forget FILE1 FILE2
$ hg qrefresh
Now B.patch no longer includes the big files. However, they are still on the disk, albeit not controlled bu Mercurial. I now finish my work with MQ:
$ hg qfinish -a
Here's what I have at the moment:
o B1: Some nice changes, no big files here
|
o A: Some good changeset
In case the changes are already pushed to BitBucket, it does offer an option to strip changesets from the server. After performing the strip locally, you should access the url:
https://bitbucket.org/<user>/<repo>/admin/strip
It'll offer an option to strip all changes following a specific commit.
NOTE: There used to be a link to reach that URL in the repo config menu. Now the only way to access it seems to be typing it directly.

Can changes made by strip be reflected in the public repo?

Let's say I have this:
hg clone public_repo my_repo
touch a b c d
hg add .
hg commit -m a a
hg commit -m b b
hg commit -m c c
hg commit -m d d
hg push
# let's say revX is the revision that added a
hg strip revX
In my repository's history, the commits are gone. However, if I try to do a push after the strip, it tells me no changes found. Can the strip be applied to the public repo?
EDIT: the question is just hypothetical :). I am not in this situation, I just thought it would be interesting to find out how this works.
You need to run hg strip there. If it's bitbucket repo, you can do it from the admin panel. If not, SSH.
This is not possible. You have to strip the public repo as well.
hg strip just takes away changesets. It does not perform an "adding" operation which could be pushed.
In the exact example you provide, you could just hg rollback on the public repo.
Let me pretend you're asking a slightly different question: I've stripped part of my repository history, how do I propagate this change to other dev's repositories?
Mercurial offers no way of propagating stripping in the same way it propagates adding changesets. So you have to come up with your own way.
Bitbucket, for example, has an updates list (on the repo summary page, if my memory serves me well). If you do a strip, Bitbucket displays there something like this:
Changeset 12345abcdef was stripped from the repository, please run hg strip 12345abcdef locally.
When we had to propagate stripping of part of an old branch in our shop, here's what we did:
Stripped the changesets on the server.
Created a batch file named strip.bat on the default branch of the repo containing the command we'd run on the server, i.e. hg strip 1234567890.
Told everybody that whenever they cannot push because “push creates new remote heads” for no apparent reason, this means they should run strip.bat.
In case we ever need to strip something again, we just add another hg strip in the batch file.
P.S. Yes it's better not to strip, e.g. use backout to commit inverse changesets. Sometimes that's not an option: if you mistakenly merged branch a into branch b when you wanted the merge b into a, you're trapped. Even if you backout the merge changeset on feature, the merged changesets from a are already marked as merged, and you'll have a hard time doing another merge of these branches.
The correct and safe way to remove unwanted changes that made it to the wild is to hg backout them.
Sometimes it is not possible to strip or backout a change, for example if it was a merge revision and you don't have admin access to the server. One method that does work is to branch the last good revision and then close the bad head.
hg up badrev^ (Go to the revision before the bad one)
touch dummy && hg add dummy && hg ci -m 'dummy' && hg rm dummy && hg ci --amend -m 'Fix accidental merge' (Create a second head with no changes from the good revision)
hg up badrev (Go to the bad revision)
hg ci --close-branch -m 'Close bad head' (Close the bad head)
hg push