Differences between revisions 4 and 21 (spanning 17 versions)
Revision 4 as of 2008-03-24 14:13:05
Size: 2712
Editor: abuehl
Comment: potential closing branches feature
Revision 21 as of 2022-12-26 20:11:56
Size: 4557
Editor: gavenkoa
Comment: Info about topological heads.
Deletions are marked like this. Additions are marked like this.
Line 1: Line 1:
== Pruning dead branches == #pragma section-numbers 2
''Other Languages: [[ChinesePruningDeadBranches|Zh]]''
Line 3: Line 4:
If you've got a dead [:Branch:branch] you'd like to eliminate from the list of [:Head:heads], you can do a 'no-op merge' to remove it: = Pruning branches =
There are three standard ways of managing [[Branch|branches]] that you no longer plan on working with. Each approach has advantages and disadvantages and should be selected on what best fits how you work with your repository.

<<TableOfContents>>

== Closing branches ==

Since Mercurial 4.8 a branch closing is as simple as:

{{{
hg close-head BRANCH
}}}

In older versions of Mercurial a branch is closed by the commit so temporary switching of working directory is necessary:

{{{
hg up -C BRANCH
hg commit --close-branch -m 'Closing BRANCH, this approach never worked.'
hg up -C default
}}}
This creates a closing changeset which typically contains no modifications to tracked files. Closing changesets can be identified by close=1 in the changeset's extra field.

A closed branch is not considered active and will not be displayed by default. {{{hg branches}}} and {{{hg heads}}} don't show closed branches unless you run with the argument {{{--closed}}}:

{{{
% hg branches
default 12:d52ed2ac9127
% hg branches --closed
default 12:d52ed2ac9127
BRANCH 11:cd3e11a024bf (closed)
}}}

Still closed branches are topological heads, they are visible in `hg log -r 'heads(all())'`. To avoid topological heads see about history rewriting below.

== No-Op Merges ==
Another way is to do a "no-op merge" to remove the branch:
Line 11: Line 47:
=== Why this might be bad ===
The new merged head is based on the "eliminated" head, so it is not really eliminated. Furthermore, it is now incorporated into the main line, which actually may even be worse than simply letting that dead head alone.
Line 12: Line 50:
=== Why this might be bad === Consider you have a clone of a project like Mercurial – let's say a clone of the [[CrewRepository|crew repository]] – and you do make a change C1 based on some changeset P from the crew repo and propose that to the crew members for inclusion into the project.
Line 14: Line 52:
The new merged head is based on the "eliminated" head, so it is not really eliminated. Moreso, it is now incorporated into the main line, which actually may even be worser than simply letting that dead head alone. If your change C1 is rejected or rebased on inclusion, you now have a dead branch C1 in your repo. If you merge that in your repo, you just create yet another line of development that is divergent from crew. So, in this case, merging your dead head doesn't make anything better as any new change you commit to your merged head won't be accepted for inclusion into crew anymore, since that would mean pulling-in your "eliminated" head as well. Such a new change would be said to not be "clean".
Line 16: Line 54:
Consider you have a clone of a project like Mercurial &ndash; let's say a clone of the [:CrewRepository:crew repository] &ndash; and you do make a change C1 based on some changeset P from the crew repo and propose that to the crew members for inclusion into the project. == Using clone ==
The recommended procedure to really eliminate unwanted heads is to use {{{hg clone --rev}}}. First, you rename your current repo to a backup. Then you clone the backup back to the original name, but you specify {{{--rev X}}} where X is the parent of the first of the chain of wanted changesets. If your repository has other heads you need to preserve, specify them too, as additional {{{--rev Y}}} arguments. For example:
Line 18: Line 57:
If your change C1 is rejected or rebased on inclusion, you now have a dead branch C1 in your repo. If you merge that in your repo, you just create jet another line of devlopment that is divergent from crew. So, in this case, merging your dead head doesn't make anything better as any new change you commit to your merged head won't be accepted for inclusion into crew anymore, since that would mean pulling-in your "eliminated" head as well. Such a new change would be said to not be "clean". {{{
hg clone backup repo --rev X --rev Y
}}}
When you've cloned, verify that
Line 20: Line 62:
=== Using strip === {{{
hg incoming -R repo backup
}}}
really only shows the changesets you wanted to drop. If you discover changesets you do need, after all (for instance, another head you forgot to specify above), you can pull them over using
Line 22: Line 67:
Another option may be to strip your dead branch with {{{hg strip}}}, a command which is provided by the [:MqExtension]: {{{
hg pull -R repo backup --rev Y
}}}
Repeat until you're satisfied with the pruned repo.

Copy over all non-tracked files you might want to preserve. In particular, you might want to copy {{{.hg/hgrc}}} from the backup since your default path now points to the backup instead of the original clone source.

You can remove your backup repository now.

== Using strip ==
Another option may be to strip your dead branch with {{{hg strip}}}, a command which is provided by the StripExtension:
Line 34: Line 89:

However, note that this is a non-history-preserving transformation of your repository. If anyone else has already pulled your C1 and used that as a parent for more changes, you might get that back if you pull from that person (The "Genie is out of the Bottle" problem, see also [:EditingHistory]).

=== Closing branches ===

There was a proposal about a new "closing branches" feature for Mercurial on the mailing list. A variant was discussed to add a new flag to changesets, which &ndash; when set &ndash; indicates that a branch (or head) is to be considered "closed". This closing changeset would typically contain no modifications to tracked files.

Closing a branch with head H would then be done by committing a new changeset C having H as parent and setting that "closed" flag in C. Other hg commands would then respect that flag, for example {{{hg heads}}} might then return only heads taht are not closed.
However, note that this is a non-history-preserving transformation of your repository. If anyone else has already pulled your C1 and used that as a parent for more changes, you might get that back if you pull from that person (The "Genie is out of the Bottle" problem, see also EditingHistory).

Other Languages: Zh

Pruning branches

There are three standard ways of managing branches that you no longer plan on working with. Each approach has advantages and disadvantages and should be selected on what best fits how you work with your repository.

1. Closing branches

Since Mercurial 4.8 a branch closing is as simple as:

hg close-head BRANCH

In older versions of Mercurial a branch is closed by the commit so temporary switching of working directory is necessary:

hg up -C BRANCH
hg commit --close-branch -m 'Closing BRANCH, this approach never worked.'
hg up -C default

This creates a closing changeset which typically contains no modifications to tracked files. Closing changesets can be identified by close=1 in the changeset's extra field.

A closed branch is not considered active and will not be displayed by default. hg branches and hg heads don't show closed branches unless you run with the argument --closed:

% hg branches
default                       12:d52ed2ac9127
% hg branches --closed
default                       12:d52ed2ac9127
BRANCH                        11:cd3e11a024bf (closed)

Still closed branches are topological heads, they are visible in hg log -r 'heads(all())'. To avoid topological heads see about history rewriting below.

2. No-Op Merges

Another way is to do a "no-op merge" to remove the branch:

$ hg update -C tip # jump to one head
$ hg merge otherhead # merge in the other head
$ hg revert -a -r tip # undo all the changes from the merge
$ hg commit -m "eliminate other head" # create new tip identical to the old

2.1. Why this might be bad

The new merged head is based on the "eliminated" head, so it is not really eliminated. Furthermore, it is now incorporated into the main line, which actually may even be worse than simply letting that dead head alone.

Consider you have a clone of a project like Mercurial – let's say a clone of the crew repository – and you do make a change C1 based on some changeset P from the crew repo and propose that to the crew members for inclusion into the project.

If your change C1 is rejected or rebased on inclusion, you now have a dead branch C1 in your repo. If you merge that in your repo, you just create yet another line of development that is divergent from crew. So, in this case, merging your dead head doesn't make anything better as any new change you commit to your merged head won't be accepted for inclusion into crew anymore, since that would mean pulling-in your "eliminated" head as well. Such a new change would be said to not be "clean".

3. Using clone

The recommended procedure to really eliminate unwanted heads is to use hg clone --rev. First, you rename your current repo to a backup. Then you clone the backup back to the original name, but you specify --rev X where X is the parent of the first of the chain of wanted changesets. If your repository has other heads you need to preserve, specify them too, as additional --rev Y arguments. For example:

hg clone backup repo --rev X --rev Y

When you've cloned, verify that

hg incoming -R repo backup

really only shows the changesets you wanted to drop. If you discover changesets you do need, after all (for instance, another head you forgot to specify above), you can pull them over using

hg pull -R repo backup --rev Y

Repeat until you're satisfied with the pruned repo.

Copy over all non-tracked files you might want to preserve. In particular, you might want to copy .hg/hgrc from the backup since your default path now points to the backup instead of the original clone source.

You can remove your backup repository now.

4. Using strip

Another option may be to strip your dead branch with hg strip, a command which is provided by the StripExtension:

hg strip [-f] [-b] [-n] REV

strip a revision and all later revs on the same branch

options:

 -b --backup    bundle unrelated changesets
 -n --nobackup  no backups

However, note that this is a non-history-preserving transformation of your repository. If anyone else has already pulled your C1 and used that as a parent for more changes, you might get that back if you pull from that person (The "Genie is out of the Bottle" problem, see also EditingHistory).


CategoryTipsAndTricks

PruningDeadBranches (last edited 2022-12-26 20:11:56 by gavenkoa)