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.
Related
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
Is there a way to edit a commit message in Mercurial on a commit after other commits have occured using TortoiseHg? I've read these posts:
How to edit incorrect commit message in Mercurial?
Mercurial: how to amend the last commit?
and have been able to update a "regular" commit message when it is the latest commit on a branch (using TortoiseHg). However, I haven't been able to figure out how to edit a commit message when other commits have occurred after the one I want to edit. It always edits the last commit.
Based on Ed Cottrell's comment, I did a test where I made two commits without pushing to the central repo, and I still have the same issue - only the last commit message can be edited.
EDIT: I should clarify that I am looking to update a changeset that has been pushed.
Histedit extension (bundled with TortoiseHG now) has a mess command for changing the commit message of historical changesets.
Unfortunately, this command is not supported by the TortoiseHG GUI so you need to run the command from command line.
As long as the change in question is local and hasn't been pushed anywhere, it is possible.
The commit message is used to compute the globally unique hash id that is used for all repositories to determine whether or not they already have a changeset. If you change the commit message, you change the unique hash id and every repo will see it as a "new" changeset. All other repositories that had the old changeset will try to get the new one and ask that you merge it with itself.... This is not a good thing, so the short answer to your question is "don't do it".
If you could definitively purge that change from all other repos, so that only the local copy is left you could essentially get to the "draft" state. Note that if any repo has the "old" changeset, it will be pushed to the central repo someday and cause the mess that we are trying to avoid.
If the changeset is still local (e.g. in draft status), you can use hg commit --amend if it is the parent of the working directory.
If there are changes after it, I would use mq and hg qimport all the changes down to and including the one where you want to edit the commit message. hg qpop -a and then hg qpush to get to the patch that represents the changeset you want to edit. Then hg qrefresh -e and make your changes. Then just hg qfin -a and you should be good to go.
The advice from Edward is good — if you've pushed your changes to another repository, you should consider them set in stone and not update the commit message or any other aspect of them.
However, we're working on changing this in Mercurial. There is an experimental extension that will allow you to do more extensive history editing and push those edits to other repositories. It is called the Evolve Extension and it enables some behavior that is partly in the core of Mercurial and partly outside core.
When using evolve, you can edit the second-to-last commit message like this
$ hg update .^
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ hg commit --amend -m 'new commit message'
1 new unstable changesets
$ hg stabilize
more:[5] old tip changeset
atop:[6] new commit message
The extension allows you to do this as long as the changesets are in the draft phase. To keep them in the draft phase after pushing them somewhere, the repository you push to need to be configured as a non-publishing repository. You can read more about this in the Changeset Evolution Documentation.
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.
I use TortoiseHg 1.1 and in our team we tend to create named branches for each case we work on then merge with default when we're done. Unfortunately most of the named branches don't get closed. This isn't a huge problem but it's annoying when you try and filter by the branch you are currently working on and there is a massive list of old branches to search through.
So is there an easy way in TortoiseHg or from the command line to close multiple branches quickly without having to manually commit and close on each branch?
Unfortunately no.
You need to close each branch separately with its own commit on that branch.
The best way to do that is to use the command line, you could even create a simple batch file to close a bunch of them:
for %%f in (branch1 branch2 branch4) do (
hg up "%%f"
if exitcode 1 goto quit
hg commit --close-branch -m "closing branch %%f"
if exitcode 1 goto quit
)
:quit
The workflow I use is to use the bookmarks extension to keep only local bookmarks for my lines of development, this way I use normal unnamed branches in Mercurial, but can still easily tell them apart locally. The commit messages are used to separate them later.
This way I don't have a bunch of named branches cluttering up my history, I don't have to remember to close them, and I only have to keep track of the branches I use locally.
See the following for all the possible options:
https://www.mercurial-scm.org/wiki/PruningDeadBranches
of which the closing branch option can be used.
hg up -C badbranch
hg commit --close-branch -m 'close badbranch, this approach never worked'
hg up -C default # switch back to "good" branch
Also, my understanding has been that it is preferable to clone for most work and use named branches only for few possible long lived trains of development.
Here is a powershell script that will close all active branches except the default.
Add your own filtering logic to exclude branches you don't want to close.
$branches = hg branches -a | sort
$exclude = "default"
foreach ($item in $branches)
{
$branch = $item.Split([string[]] " ", [StringSplitOptions]::RemoveEmptyEntries)[0]
if(!$exclude.Contains($branch))
{
hg update $branch
hg commit --close-branch -m "closing old branch"
hg status
Write-Host $branch
}
}
hg push
Okay so this is way late, but I set out to do this via bash and after a lot of pain I got a working solution. Take note that this does not do an hg push at the end, because I wanted to look over the results in tortoise before pushing.
#Protected branches to not close
develop='develop'
default='default'
IFS=$'\n'
#Get all branches
allBranches=$( hg branches | sort )
#Loop over and close old branches
for item in $allBranches
do
#Trim off everything but the branch name
branch=$( echo $item | sed -r 's/\s*[0-9]+:[0-9a-f]+(\s\(inactive\))?$//' )
if [ $branch != $develop ] && [ $branch != $default ]
then
hg update $branch
hg commit --close-branch -m "Closing old branch"
fi
done
After running the script I asked the team which branches they were actively working on and stripped those closure commits.
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