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

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

Related

If you "hg clone" an earlier commit, and then commit, will you create a new branch?

I know that when you update to a previous revision, you create a new branch on your next commit. What happens when you clone an earlier revision? Will your next commit (or push) create a new branch?
The reason I ask is because our Tip is currently broken, so until it gets fixed, I was planning to do the following:
hg clone -r <prev_rev> <Tip>
# make changes
hg commit -m "my changes" -u me
# wait for Tip to get fixed
hg pull
hg merge
hg push
Will this work? Or will it end up creating a new branch or a dangling head? I would like to avoid doing either of these things.
Yes, that will create a branch, at least in the sense of having a "fork" in the history of the repository.
The branch will not be assigned a new branch name, but that in itself is not usually important in terms of understanding what the history will look like in a repository.
If your original repo was:
A-B-C-D
Then you do a hg clone -r C... and a new commit, you will end up with:
A-B-C-(D)
\
E
Now, since you have cloned only up to C, that specific local repository may not include D yet. But when you eventually synchronize it with another that will have to be dealt with. If you PULL, then you will get D in that clone. If you try to push HG will warn you about creating a new remote head for which the usual approach is to merge.
There is nothing wrong with this approach. If you need to work around an issue in tip for the time being, this is a reasonable way to do it. But you don't really need a new clone, you could just hg update C and work from there, which might be easier.

How to Switch the branch an commit with Tortoise Hg?

I have two branches named X and Y. X is previously created and y is the latest one. Now I am at branch 'Y' and I have modified some files. I need to commit the files which I have modified and I need to commit the changes to 'X' branch instead of 'Y' branch. How can I do this with Mercury Hg or Tortoise Hg? (How to Switch to 'x' branch and commit the changes?)
You could
Commit it to Y branch
Then transplant/grave it to X branch
Then strip changeset from Y branch
As result it's look like you commited it to X instead of Y
If you have not committed the changes then you should be able to do the following:
hg update X
hg commit
Unless you've changed some of the same files in the same places within the file, this should be sufficient. If you have, then your hg update X command should fail. You have a few options at this point.
You can use Mercurial Queues (mq).
You can put your current changes into a patch queue:
hg qnew stufftomove -e
You'll be presented with whatever editor you use to create a commit message. Go ahead and fill in your commit message with whatever you want it to be when it is ultimately committed. If you don't use -e, it will just create the patch queue with no commit message. Later you can make a commit message with hg qrefresh -e if you want.
After the patch queue is created, pop it off with:
hg qpop
Now move to the X branch:
hg update X
Now push the patch queue on. This will apply the patch file:
hg qpush
This can either go on cleanly, or if you did have merge conflicts, you'll get a message that .rej files have been created. Those show you the parts of the changes that could not be applied automatically.
If you have .rej files, manually apply the pieces that didn't work and then run
hg qrefresh
This will update the patch file with the new changes.
When everything looks like what you want it to, you can convert the patch queue to a real commit with
hg qfinish stufftomove
Your file changes should now be committed on the branch you want them to be on.
Alternatively, if you already committed the changes on the wrong branch, and have not pushed your changes (or had them pulled) to a remote repository, you can do this:
hg qimport -r <revision to import>
At this point, continue the above instructions starting with hg qpop.
If you can be more specific about the state of the files and branches, I can help more specifically. In any case, I hope this helps.
Side note: If you've got merge conflicts in the steps above and don't like dealing with .rej files, you can use hg rebase which will allow you to resolve the conflicts in your favorite merge tool (or whatever tool you've got configured). Hope this helps.
I use Shelving for such operation: https://tortoisehg.bitbucket.io/manual/2.9/shelve.html
While you're on 'Y' branch, put all your changes to shelf. Then switch to 'X' branch and restore changes from your shelf.

Mercurial: Switch working directory to branch without losing changes?

Let's say that I have a named branch 'B1' which I'm doing feature development on.
I am at a good stopping point before a demo though not done with the feature so I:
hg up default
hg merge B1
hg ci -m "merged in feature drop"
hg push
Now I continue working for a half an hour or so and go to commit only to realize that I forgot to update back to B1 and that my current working directory is on default - uhoh. In theory I should be able to just mark my working directory parent as the tip of B1 - is there an easy way to do this?
I could of course commit, update back to B1, and merge my changes back, but then there's an unstable changeset in default and this happens often enough to me that I would like a real solution.
Two ways. First, the obvious way:
hg diff > foo
hg up -C b1
hg import --no-commit foo
rm foo
Second, the magical way:
hg up -r 'ancestor(., b1)' # take working dir back to the fork point
hg up b1 # take it forward to the branch head
This way involves merges. Depending on how much your branches have diverged, this may be painless. Or it may be complicated, and you may make a mess of your changes that you haven't saved anywhere. Which is why even magicians like myself prefer to do it the first way.
I would use the shelve extension. I think it’s distributed along with TortoiseHg, you can also use it from the UI:
hg shelve --all
hg up B1
hg unshelve
Rebase extension allow you to change parent for any commit for wrongly commited changeset.
If you want just change branch for future commit - MQ (as mentioned) or Shelve
Typically for this sort of dynamic approach, I favor mercurial queues.
In your situation, what I would do would be to create a patch on default with the changes, pop the patch off, switch over to B1, and apply the patch.
It goes something like:
hg qnew OOPSPATCH
hg qrefresh
hg qpop
hg up B1
hg qpush
<hack hack>
hg qrefresh
hg qfinish
All you need is simple hg up -m B1
From hg up --help:
options:
…
-m --merge merge uncommitted changes
…

ignore non-existant files when merging in Mercurial

I am working with a repository with stable and experimental branches. Sometimes I add a file on the experimental branch that is not yet ready for the stable branch. When I merge, I want to merge the changes in the files that are common to both branches, but ignore the files that don't exist on one of the branches.
Here's a simple example:
hg init
hg branch stable
(create file A)
hg add A
hg commit -m "Added A"
hg branch exp
(create file B)
hg add B
hg commit -m "Added B, which is really experimental"
(modify file A)
hg commit -m "Some changes to A"
hg update stable
hg merge exp
However I change the merge tool configuration, Mercurial always seems to take B along with the merge. Since it doesn't exist on the stable branch, it's never a conflict.
I could do the following:
hg update stable
hg merge exp
hg commit -m "Merged"
hg revert -r 0 B
but that requires me to know which files need reverting.
Any thoughts on the simplest way to make the merge ignore files that don't exist on one branch, and preferably do it automatically?
You cannot ask Mercurial to do what you want.
You do not merge individual files, you merge the entire branch, which means that all the files that are part of the branch becomes part of the merge. This is how Mercurial is designed and how it operates.
Now, you could revert/forget/delete the files you don't want before you commit, but then you're just setting yourself up for disaster later.
I recommend you separate things you want to keep from things you don't know if you want to keep so that you can merge one branch and let the other be separate for now.

Mercurial Getting out of a bad merge

I just merged branch A into B, and for some reason the merge did not go well. I want to revert B back to where it was before the merge and try again like it never happened before. I was thinking of just doing
hg clone myrepo newrepo -r A -r 12345
where 12345 is the revision number before B's bad merge commit
I think this works, but I have a lot of other branches (most of which are closed using commit --close-branch) and this puts those branches back to an inactive state.
Is there a way to clone everything except revision 123456 or something? (where 123456 is the bad commit on B)
Assuming you have not pushed the merge changeset to any public location, the easiest solution is to use the hg strip command that comes with the Mercurial Queues (i.e. mq) extension.
From the wiki:
hg strip rev removes the rev revision
and all its descendants from a
repository. To remove an unwanted
branch, you would specify the first
revision specific to that branch. By
default, hg strip will place a backup
in the .hg/strip-backup/ directory. If
strip turned out to be a bad idea, you
can restore with hg unbundle
.hg/strip-backup/filename.
It might not be as nice as hg rollback, but usually what I do is update to head A, merge in previous head B, check that I got it right this time, and then dummy-merge away the bad merge.
I hope I'm understanding your situation correctly. If I am you should be able to update to the revision of B before the merge, give that revision a new branch name, merge A in to it, and continue on. You'll probably want to mark the original B branch as closed.
$ hg up 12345 #12345 is the revision of B prior to the merge
$ hg branch B-take2
$ hg merge A
$ hg commit -m 'merge A in to B-take2'
$ hg up B
$ hg commit --close-branch -m 'mark original B branch as closed'
Is it too late to use the hg rollback command? If so, try the hg backout command.