Why I can switch to a branch after closing it? - mercurial

I discovered today that I can switch back to a branch even after I closed it. Why?
hg init abc
cd abc
echo 'readme1' > README1.txt
hg ci -Am "1st commit"
hg branch other-branch
echo 'readme2' > README2.txt
hg ci -Am "2nd commit"
hg update default
hg merge other-branch
hg ci -m "Merged other-branch into default"
hg update other-branch
hg ci -m "Closing other branch" --close-branch
hg update default
now I think I'm not supposed to do this
hg update other-branch
but it works ok
It confuses me and makes me feel somewhat uneasy.
UPDATE: sorry forgot to indicate that I'm using HG v.1.6

Like Amber said, when you close a branch, it just records that it is closed. As a consequence when you do "hg branches" you will just see "default", and not "other-branch".
However, as soon as you switch to this branch and commit something new, it automatically reopen it (and thus appears again in the list of "hg branches"). You can also re-close it when you are done.
I find this feature actually desirable: Imagine you have created a "stable" branch in order to stabilize some code for a delivery, only allowing bug fixes on this branch. Now after the delivery, you can close the stable branch and develop new features again on default, switching to the next iteration and preparing the next delivery (assuming you are using scrum for instance). Now when three days after, your delivery customer finds a problem and demand to receive a fixed delivery, not wanting to wait for the next one, then you can easily switch to the stable branch, reproduce the problem, fix it (reopening the branch), redeploy, and finally close the branch again. This seems like a plausible scenario and a good Mercurial behavior to me.
Just my 0.02€ :-)
Cheers,
Christophe.
= The illiterate of the 21st century will not be those who cannot read or write; they will be those who cannot learn, unlearn, and relearn. --Alvin Toffler =

Closing a branch mostly just makes it not show up in certain lists:
https://www.mercurial-scm.org/wiki/PruningDeadBranches#Closing_branches

Related

Mercurial workflow: why do I seem to commit everything twice?

I'm trying to grasp the Mercurial basics so please bear with me. :) My current workflow is as follows:
do some work until I'm ready to commit or need the others' changes
pull
at this point I'd like to merge my work with the latest changesets and commit, however Mercurial insists on me committing before merging
so it goes like "commit, merge, commit" and I'm basically committing everything twice, writing the same notes in both changesets and pushing two changesets at a time
Is it intended to be so? Is it possible to have just one changeset coming from me with every merge? Is it indeed desirable?
I've read a lot of online manuals but still feel I do not have solid understanding of the process. All comments are welcome. Thanks!
EDIT: Turns out I didn't know that update could merge incoming changes with uncommitted edits.
Merging always creates a separate changeset in Mercurial.
Plus, merging is not possible as long as you have uncommitted stuff in your local repository.
So the solution is to commit first, and pull and merge afterwards.
This will always result in two changesets, not one.
(...because merging always creates a separate changeset)
But you don't commit the same stuff twice, and especially you shouldn't write the same commit message twice:
The first commit is what you actually changed ("fixed a bug in the foo bar").
The second commit is just the merge (TortoiseHG actually pre-populates the commit message with "Merge", 99% of the time I just leave it like that).
This workflow will prevent a merge in the history, but you still do a merge as noted below:
Do some work until you are ready to commit or need another's changes.
hg pull
hg update (Note: hg pull -u does this and the previous in one step.
During hg update, your uncommitted changes will be merged with the new tip of your current branch. You will still have to resolve any conflicts.
hg commit when ready.
I still recommend if you have extensive changes to commit first before pulling/merging because it is easier to start over by updating to that changeset if the merge goes badly.
Keeping the hg pull and hg update separate allows you to look at the incoming changesets and predict how the merge will go.
The reason is feels strange is that you delay your commit until you want to integrate with the others.
A big feature of distributed version control is that commits are local. Because they're local you should commit often — commit every time you have a small consistent chunk of work done. Your commits are not inflicted on others immediately so you wont interrupt them by making many small commits.
If you begin making more commits you'll see that your workflow becomes:
$ hg commit -m "Refactoring for Issue123"
$ hg commit -m "Basic functionality for Issue123"
$ hg commit -m "Fixed off-by-one error (Issue123)"
$ hg commit -m "Finished implementing Issue123"
$ hg commit -m "Added more tests for Issue123"
$ hg commit -m "Begin use new function from Issue123"
$ hg pull
$ hg merge
$ hg commit -m "Merge"
Here the ratio of merge commits to "real" commits is much more sensible.
Many people (myself included) like to use the rebase extension to avoid the merge completely. That extension linearizes the commits by faking the history so that it looks like you did your four commits after the changesets you pulled down with hg pull. The only change in workflow is that you hg rebase instead of hg merge above and then skip the final commit.

Easy way to tell what branch your working directory is in with Mercurial?

If I create a new branch using hg:
$ hg branch newbranch
And then look at branches:
$ hg branches
default 194:d9df55198e53
newbranch 193:a36a491b8507 (inactive)
newbranch is marked as (inactive), despite my working directly being currently mapped to it.
If I do a commit, default will then be flagged (inactive).
However, if I switch to default, merge, and commit, then switch back to newbranch, newbranch will say (inactive) again.
This is a bit of a pain, because I may do that on a friday night, and not come back to it until Monday, and not know what branch my working directory is actually pointed at.
So, is there a better way to tell, or should I always make it my workflow to specify the branch I want to work in before I start.
Though hg summary will tell you what branch you're on, so will hg branch without a branch name argument. Let's say I've never created a branch called mybranch before, but I want to start it:
> hg branch
default
> hg branch mybranch
marked working directory as branch mybranch
And now I've worked for a bit and want to commit, but forgot if I set the branch name for this next commit or not:
> hg branch
mybranch
Oh, I did.
Ok, I just discovered hg sum:
$ hg sum
parent: 195:d0a2617b4b51 tip
[Commit Message]
branch: newbranch
commit: (clean)
update: (current)
So I guess this is how you do it?

Abandoning changes without deleting from history

There is a commit that just didn't work, so I want to abandon it without deleting it from history.
I have updated from an earlier revision and committed, thus creating a new head.
I don't have branches, I don't want branches, I just want to simply go on with the new head exactly as it is, nothing fancy, no merge, no worries, just go on forgetting the previous one.
I can't seem to find how to do that, and I'm starting to believe it can't be done. All I find is stuff about branches, or stuff about merging.
Update your repository to the head with the revision that you want to forget about, then use hg commit --close-branch to mark that (anonymous) branch as closed. Then update to the head of the branch that you do want, and continue working.
You can still see the closed branch if you use the -c option to hg heads, but it won't show up by default and hg merge will know not try to merge with the closed head.
You will need to use hg push --force the first time you push this closed head to another repository since you are actually create additional heads in the remote repository when you push. So tell Mercurial that this is okay with --force. People who pull the closed head wont be bothered by any warnings.
I know you don't want to work with branches at this stage, but that's exactly what you've done. When you went back to an earlier version and committed something that worked you created a branch - an unnamed branch, but a branch all the same.
There's no problem with just carrying on just as you are and not worrying about having multiple heads, but if you want to tidy things up so you don't accidentally pick the wrong head one time then you can kill off the old branch.
There's a good section in the Mercurial documentation that takes you through a number of options around Pruning Dead Branches.
I think the best option for you is to mark the old branch as "closed". If your old head is revision "123" then:
hg update -r 123
hg commit --close-branch -m 'Closing old branch'
hg update -C default
First of all, type:
hg heads
Imagine, you have three heads listed:
changeset: 223:d1c3deae6297
user: Your name <your#email.com>
date: Mon Jun 09 02:24:23 2014 +0200
summary: commit description #3
changeset: 123:91c5402959z3
user: Your name <your#email.com>
date: Sat Dec 23 16:05:38 2013 +0200
summary: commit description #2
changeset: 59:81b9804156a8
user: Your name <your#email.com>
date: Sat Sep 14 13:14:40 2013 +0200
summary: commit description #1
Let's say, you want to keep the last head active (223) and close the rest.
You would then do as follows:
Close head #59
hg up -r 59
hg ci --close-branch -m "clean up heads; approach abandoned"
Close head #123
hg up -r 123
hg ci --close-branch -m "clean up heads; approach abandoned"
Commit the changes
hg push
Don't forget to switch to the right head at the end
hg up -r 223
And you're done.
You want to use hg backout. This removes the changes made by the changeset from any child changeset.
Check this out for a good explanation.
Mercurial Backout
An alternative to closing or stripping the unwanted branch would be to merge it in a way that totally discards its effects, but leaves it in history. This approach will allow those unwanted changes to propagate in a push - so only use this if that is the intended effect.
Let's say the changeset history looks like this:
1-2-3-4-5-6
\
7-8-*
and it is 5 and 6 which are no longer wanted.
You can do this:
hg up 8
hg merge -r 6 -t :local
hg commit ...
which will create this:
1-2-3-4-5-6
\ \
7-8-9-*
The update to 8 ensures you are working at the desired head in history, which you want to keep.
The -t :local instructs hg to use the merge "tool" called local which tells it to ignore changes from the other branch, i.e., the one NOT represented by the current working folder state. More info.
Thus the unwanted changes in 5 and 6 are preserved in history but do not affect anything more recent.
Both Niall's and Nick's answers are straight on. Because I find myself creating lots of dangling heads, I ended up writing an alias to close heads more easily. By adding this to your .hgrc:
[alias]
behead = !REV=$($HG id -i); $HG update $# -q && $HG ci --close-branch -m "Closing dead head" && $HG update $REV -q
(if you already have an [alias] section, you can append to it instead)
You can now close a head in one single-command (and without having to update to a different changeset manually) like this:
$ hg behead 123
Note: the alias takes advantage of the fact that Mercurial aliases can be shell commands. This means that this will probably only work on UNIX, not on Windows.
This is a use case for the Evolve extension. It's currently not bundled with Mercurial, so it is technically a third party extension. But it's being used quite heavily by a bunch of people, including Mercurial developers, is being very actively developed, and isn't going anywhere.
With the Evolve extension, you simply do
hg prune -r revname
and get on with your life. The cset will still be there, but obsoleted. It won't be visible unless you pass the --hidden option to Mercurial commands, and by default won't be pushed to remote repositories. Though I think you can force it if you really want to.
If the cset you are pruning has ancestors you want to keep, then you'll have to run hg evolve to rebase those changesets. hg evolve will do so automatically. Otherwise, you don't have to do anything.
You may clone your corrupted repo to a new one without cloning that unwanted head. Then remove old repository, move newly created clone to the original place and continue working with it. This will take some time, but you'll get a perfectly clean repository without a sign of that unwanted revision.
hg clone --rev myGoodResition myDirtyRepo myCleanRepo
I have run into this issue many times when I want to behead a head that was created in error. I always want to see it disappear off the face of the Earth.
On your local copy, get the latest and then:
Find the beginning of a head you want to strip (where a new neck starts to branch off), get the revision number
Strip it.
Source: TipsAndTricks.
Source: PruningDeadBranches#Using_strip.
hg --config extensions.hgext.mq= strip -n <rev>
Make a trivial file update (add a whitespace to a file), commit and push.
Your repo should now have the head stripped. The last step is important as stripping doesn't create any changes you can push to your central repository. Without the last step you only have stripped the head locally.

Closing Hg Branches

When using hg branch FeatureBranchName and publishing it to a central repo that is shared amongst developers, is there a way to eventually close the FeatureBranchName when its development has officially been merged with the default branch?
It would also be helpful if the FeatureBranchName was not visible when performing a hg branches command.
hg commit --close-branch
should be enough to mark a branch close. (see hg commit)
--close-branch
mark a branch as closed, hiding it from the branch list.
See also this thread:
My expectation is that I close a branch because this line of development has come to a dead end, and I don't want to be bothered with it any more.
Therefore, when a branch has been closed I shouldn't see it (in branches, heads, log, for instance) unless I explicitly ask to see closed branches.
I should note that I expect a closed branch to remain in the repository;
it may be useful in the future, and the commit --close-branch message
should at least explain why the branch was closed.
Pruning branches is another thing altogether.
Note: that "closing branch" business is one aspect seen as missing in Git, when compared to Mercurial:
Branches in git are, we’re always told, ephemeral things to be used and thrown away, and so far as I know git doesn’t have a way to indicate to your colleagues that you’re done with a branch;
the only way to do this is to delete it, or to hope they see the final merge commit and understand that the branch is closed to further development.
[In Mercurial] When you’re done with a branch, however, you cannot delete it from the repository; instead, you issue a commit which closes the branch, and Mercurial notes that the branch is closed. It’ll remain a permanent part of your repository history.
I wrote a simple script that completes the branch close, commands found at PruningDeadBranches.
## Script ##
#!/bin/bash
#script to close the not required branch in mercurial
hg up -C $1
if [ $? -eq 0 ]; then
echo "$1 is up"
else
echo "branch not found, please recheck your argument"
exit 1
fi
# if we are here then the branch is up, so we do the following
hg commit --close-branch -m 'this branch no longer required'
echo "$1 is closed"
hg up -C default
echo "default is up"
How to
Move to the local copy of the repository, and run this script by giving an argument. For example:
$./the_script_above.sh bad_branch_name_to_close
What does it do
This does the following:
If the branch exists, it updates to the given branch or else exists with
an error message.
It closes the branch.
Updates to the default branch.
Stops.

Trying to merge back from branch to main and close branch in Mercurial

I've got a named branch (same repository) that was created in order to to spike something. I've now decided that I want to move all the changesets created in the branch back into the main (default) and then close the branch.
I've tried a number of different things, including what was outlined in this post (How to repeatedly merge branches in Mercurial) but I just can't get it working :(
Can anyone provide any pointers?
Thanks.
Merge the feature branch into default
hg up default
hg merge feature-branch-name
hg ci -m 'merged feature-branch-name into default'
Close the branch you don't want to use anymore
hg up feature-branch-name
hg ci --close-branch -m 'close feature-branch-name branch'
hg up default
Note that the close command doesn't have any disruptive effects on the repository history
It flags your branch as closed so that it won't appear in hg branches and hg heads commands output
I've managed to solve my problem using the link I mentioned in my question. The steps described in the link had actually merged my changes across however I didn't realise as I was looking in the TortoiseHg UI and couldn't see the changes there. When I performed hg outgoing via the command line it appears that the merge had worked correctly.