How to correctly close a feature branch in Mercurial? - merge

I've finished working on a feature branch feature-x. I want to merge results back to the default branch and close feature-x in order to get rid of it in the output of hg branches.
I came up with the following scenario, but it has some issues:
$ hg up default
$ hg merge feature-x
$ hg ci -m merge
$ hg up feature-x
$ hg ci -m 'Closed branch feature-x' --close-branch
So the feature-x branch (changests 40-41) is closed, but there is one new head, the closing branch changeset 44, that will be listed in hg heads every time:
$ hg log ...
o 44 Closed branch feature-x
|
| # 43 merge
|/|
| o 42 Changeset C
| |
o | 41 Changeset 2
| |
o | 40 Changeset 1
|/
o 39 Changeset B
|
o 38 Changeset A
|
Update: It appears that since version 1.5 Mercurial doesn't show heads of closed branches in the output of hg heads anymore.
Is it possible to close a merged branch without leaving one more head? Is there more correct way to close a feature branch?
Related questions:
Is there a downside to this Mercurial workflow: named branch "dead" head?

One way is to just leave merged feature branches open (and inactive):
$ hg up default
$ hg merge feature-x
$ hg ci -m merge
$ hg heads
(1 head)
$ hg branches
default 43:...
feature-x 41:...
(2 branches)
$ hg branches -a
default 43:...
(1 branch)
Another way is to close a feature branch before merging using an extra commit:
$ hg up feature-x
$ hg ci -m 'Closed branch feature-x' --close-branch
$ hg up default
$ hg merge feature-x
$ hg ci -m merge
$ hg heads
(1 head)
$ hg branches
default 43:...
(1 branch)
The first one is simpler, but it leaves an open branch. The second one leaves no open heads/branches, but it requires one more auxiliary commit. One may combine the last actual commit to the feature branch with this extra commit using --close-branch, but one should know in advance which commit will be the last one.
Update: Since Mercurial 1.5 you can close the branch at any time so it will not appear in both hg branches and hg heads anymore. The only thing that could possibly annoy you is that technically the revision graph will still have one more revision without childen.
Update 2: Since Mercurial 1.8 bookmarks have become a core feature of Mercurial. Bookmarks are more convenient for branching than named branches. See also this question:
Mercurial branching and bookmarks

imho there are two cases for branches that were forgot to close
Case 1:
branch was not merged into default
in this case I update to the branch and do another commit with --close-branch, unfortunatly this elects the branch to become the new tip and hence before pushing it to other clones I make sure that the real tip receives some more changes and others don't get confused about that strange tip.
hg up myBranch
hg commit --close-branch
Case 2:
branch was merged into default
This case is not that much different from case 1 and it can be solved by reproducing the steps for case 1 and two additional ones.
in this case I update to the branch changeset, do another commit with --close-branch and merge the new changeset that became the tip into default. the last operation creates a new tip that is in the default branch - HOORAY!
hg up myBranch
hg commit --close-branch
hg up default
hg merge myBranch
Hope this helps future readers.

EDIT ouch, too late... I know read your comment stating that you want to keep the feature-x changeset around, so the cloning approach here doesn't work.
I'll still let the answer here for it may help others.
If you want to completely get rid of "feature X", because, for example, it didn't work, you can clone. This is one of the method explained in the article and it does work, and it talks specifically about heads.
As far as I understand you have this and want to get rid of the "feature-x" head once and for all:
# changeset: 7:00a7f69c8335
|\ tag: tip
| | parent: 4:31b6f976956b
| | parent: 2:0a834fa43688
| | summary: merge
| |
| | o changeset: 5:013a3e954cfd
| |/ summary: Closed branch feature-x
| |
| o changeset: 4:31b6f976956b
| | summary: Changeset2
| |
| o changeset: 3:5cb34be9e777
| | parent: 1:1cc843e7f4b5
| | summary: Changeset 1
| |
o | changeset: 2:0a834fa43688
|/ summary: Changeset C
|
o changeset: 1:1cc843e7f4b5
| summary: Changeset B
|
o changeset: 0:a9afb25eaede
summary: Changeset A
So you do this:
hg clone . ../cleanedrepo --rev 7
And you'll have the following, and you'll see that feature-x is indeed gone:
# changeset: 5:00a7f69c8335
|\ tag: tip
| | parent: 4:31b6f976956b
| | parent: 2:0a834fa43688
| | summary: merge
| |
| o changeset: 4:31b6f976956b
| | summary: Changeset2
| |
| o changeset: 3:5cb34be9e777
| | parent: 1:1cc843e7f4b5
| | summary: Changeset 1
| |
o | changeset: 2:0a834fa43688
|/ summary: Changeset C
|
o changeset: 1:1cc843e7f4b5
| summary: Changeset B
|
o changeset: 0:a9afb25eaede
summary: Changeset A
I may have misunderstood what you wanted but please don't mod down, I took time reproducing your use case : )

It is strange, that no one yet has suggested the most robust way of closing a feature branches...
You can just combine merge commit with --close-branch flag (i.e. commit modified files and close the branch simultaneously):
hg up feature-x
hg merge default
hg ci -m "Merge feature-x and close branch" --close-branch
hg branch default -f
So, that is all. No one extra head on revgraph. No extra commit.

Related

create a commit of changes between two branches

I created branch br1 from default a few months back. I have been committing and pushing changes to br1 since then. Every now and then pulling in changes from default as follows:
hg up br1
hg merge default
// create a commit, push
I want to create a commit message of all the changes I made to br1 over the last few months. branch br1 and default are clean in my workspace, aka no uncommitted changes, no un-pushed commit. At this point, br1 is out of sync with default by 1 week. I did the following steps:
hg up default
hg merge br1
// At this point, "hg stat" shows files that I did not modify in br1. :(
// So, if I created a commit message at this point, it would be no good.
// I am not sure why these addition file modifications showed up.
I figured the issue might be because br1 is out of sync from default by a week. I performed the following set of steps in a clean workspace:
hg up br1
hg merge default
// created a commit -**ch1**, but did **NOT push**
hg up default
hg merge br1
// At this point, "hg stat" shows the same additional files as my pervious
// attempt. :(
Question:
- Does "hg merge" disregards commit that are not pushed?
- Do I need to push ch1 for these additional files to not to show up? Is this the reason for the additional files showing up when do a "hg merge br1"?
- Is there a way I can tell hg to take the ch1 into account when doing the merge from br1.
Thank you,
Ahmed.
well, since you didn't provide a working example, I'll have to build one from scratch, so forgive me for the bunch of command lines below
explanation at the end, because only makes sense if you follow the example steps
preparing the stage
# create a new dir an initialize the repository
mkdir hgtrial
cd hgtrial
hg init
# create an empty file and make first commit on default branch
echo . > dummy.txt
hg add dummy.txt
hg commit -m "1st commit"
# add info and make a second commmit also on default branch
echo abc >> dummy.txt
hg commit -m "2nd commit"
# create a new branch and make an empty commit on it
hg branch br1
hg commit -m "my new branch"
# work on br1
echo def >> dummy.txt
hg commit -m "def on br1"
# work on default (make br1 out of sync)
hg up default
echo ghi >> dummy.txt
hg commit -m "ghi on default"
echo jkl >> dummy.txt
hg commit -m "jkl on default"
# sync br1 by pulling default
hg up br1
hg merge default
# solve merge conflicts (abc def ghi jkl)
hg commit -m "br1 <- default"
# continue working on br1 (without touching dummy.txt)
echo 123 > another.txt
hg add another.txt
hg commit -m "another file"
echo 456 >> another.txt
hg commit -m "456"
# work on default (make br1 out of sync)
hg up default
echo yes-yes > one-more.txt
hg add one-more.txt
hg commit -m "yes-yes, on default"
echo no-no >> one-more.txt
hg commit -m "no-no, on default"
now the issue (notice we are standing on default branch)
# now the problem (scenario 1)
hg merge br1
hg stat
# dummy.txt is modified, trying to bring "def" from br1 into default
# abort
# checkout clean br1 (drop merge in progress)
hg up -C br1
hg merge default
hg commit -m "br1 <- default"
# the problem again (scenario 2)
hg up default
hg merge br1
hg stat
# dummy.txt is modified, trying to bring "def" from br1 into default
# SAME THING!
why?
because at some point "def" change was made in br1 and default never knew about it, so even when you haven't touch dummy.txt in a long while, you have synced default into br1, but no the other way around, therefore default has a lot to catch up with
EDIT: added screenshot with this scenario in TortoiseHg

Why doesn't git diff work between two dates?

I checked out a project from an internal GitLab server using Eclipse, then I pulled all the changes. When I view the history from Eclipse. (Team > show in history), it displays the full history of the project.
Now I go to the relevant project from the terminal.
/home/workspace/ProjectX/
I am trying to get the differences between 2 dates with the following command:
git diff --name-only master#{2015-10-10}..master#{2015-11-10} > /home/results/ProjectX/Changes.txt
It wont display any result for that. It displays:
warning: Log for 'master' only goes back to Tue, 10 Nov 2015.
How can I get all the differences in that date range?
In addition to that, how does Eclipse request its history from the remote server. If we can run the same command from the terminal, that should work.
Git parses dates like master#{2015-10-10} using your reflog, which doesn't appear to go back as far as you're searching. But, you can find commits for that date range anyway with rev-list:
git rev-list --since='2015-10-10' --until='2015-11-10' master
You want the files changes between the most recent and the oldest commit in that list, which we can get using head and tail. I'd like to use -n1 and --reverse, but --reverse applies after -n, so we can't.
first=$(git rev-list --since='2015-10-10' --until='2015-11-10' master | tail -1)
last=$(git rev-list --since='2015-10-10' --until='2015-11-10' master | head -1)
git diff --name-only $first..$last
Setting variables and duplicating the rev-list feels clumsy, but the pipe-y version I can come up with is sort of worse. It picks the first and last commits, converts the newline to a space using tr, replaces the new space with .. using sed, then passes the pair off to git diff.
git rev-list --since='2015-10-10' --until='2015-11-10' master | \
(head -n1 && tail -n1) | \
tr '\n' ' ' | \
sed 's/ /../' | \
xargs git diff --name-only

Mercurial show diff against 2 parents or base during merge

Our teem recently faced with merge that removes one leaf of merge and we "lost" changes (as if you perform hg merge --tool internal:local).
This happen because we don't experienced with hg merge command.
hg diff shown only one difference, but not other.
BASE --- HEAD1 --- MERGE
\---- HEAD2 --/
Suppose in HEAD1 I merge HEAD2 but has not yet commit changes.
HEAD2 diff against MERGE I see by hg diff. It is -r BASE:HEAD2 patch.
How can I see diff between current local merge state with HEAD1 as if we merge from HEAD2
How can I see diff between current local merge state with BASE?
Thanks #Vince for suggestion. I reread hg help diff and hg help revset and get that I want.
Assume that you at MERGE before commit and perform merge from HEAD1.
To compare diff against HEAD1 use one of:
hg diff
hg diff -r .
hg diff -r HEAD1
Check:
hg log -r .
To compare diff against HEAD1 use one of:
hg diff -r HEAD2
If you have only 2 heads in current branch last expression can be written without HEAD2 name:
hg diff -r 'branch(.) & head() - .'
Check:
hg log -r 'branch(.) & head() - .'
To compare against BASE:
hg log -r 'ancestor(HEAD1, HEAD2)'
If you have only 2 heads in current branch last expression can be written without HEAD1/HEAD2 names::
hg diff -r 'ancestor(branch(.) & head())'
Check:
hg log -r 'ancestor(branch(.) & head())'
I wander if there are any shortcut for second parent of current merge. For first - just dot sign...
UPDATE Hm... p1() and p2() are awesome! I rewrote my examples in way that they have no concrete names HEAD1/HEAD2:
hg diff -r 'p1()'
hg diff -r 'p2()'
hg diff -r 'ancestor(p1(), p2())'

What does hg copy do?

We recently did a hg copy of a directory in our repository. We thought it
does something like cp -a and hg add and maybe flag somehow that
this file has been copied from another file inside the repo (so hg
annotate shows the original committer). But it now seems that hg
copy does more or different stuff than that. I couldn't really find
much on how exactly copy works. So:
What exactly does hg copy do and what special treatment does this
cause in the future?
If it turns out to do "the wrong thing(tm)" for our case, how do I
unflag the file as beeing a copy of another file?
(This question was asked on the Mercurial mailinglist, you may want to follow the original thread too.)
What exactly does hg copy do and what special treatment does this
cause in the future?
It adds new files and marks them as copies of the old files. Because they are copies, a change made in the original file will be merged into copy. Time flows from left to right:
(init) --- (edit a.txt) ---- (a.txt edit is copied to b.txt)
\ /
(hg copy a.txt b.txt)
If it turns out to do 'the wrong thing(tm)' for our case, how do I
unflag the file as beeing a copy of another file?
This mechanism only kicks in when you merge. If b.txt is not present in the
common ancestor revision (init in the above graph), then Mercurial will
do a search backwards to see if b.txt is copied from somewhere else.
Let us continue the above graph in abbreviated form:
(i) -- (edit a) -- (a edit copied to b) -- (edit a) -- (merge)
\ / /
(copy a b) --/------- (edit b) ------------------/
The question is how the final merge is done. The common ancestor point
is now the copy a b node and here both a and b are present. This means
that there wont be any search for copies! So the second edit to a wont
be merged into b.
To double-check, I tried it out:
$ hg init
$ echo a > a
$ hg add a
$ hg commit -m init
$ hg copy a b
$ hg commit -m "copy a b"
This was the copy, b now contains a only.
$ hg update 0
0 files updated, 0 files merged, 1 files removed, 0 files unresolved
$ echo aa >> a
$ hg commit -m "edit a"
created a new head
$ hg merge
merging a and b to b
0 files updated, 1 files merged, 0 files removed, 0 files unresolved
(branch merge, don't forget to commit)
$ hg commit -m "a edit copied to b"
This was the first merge and the edit to a has been copied into b:
$ cat b
a
aa
We now make changes in parallel:
$ echo aaa >> a
$ hg commit -m "edit a again"
$ hg update 3
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ echo bbb >> b
$ hg commit -m "edit b"
created new head
$ hg merge
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
(branch merge, don't forget to commit)
There are no further copying done:
$ cat a
a
aa
aaa
$ cat b
a
aa
bbb
As for disabling this... you can't really explicitly disable the copy
detection. But as I hope to have illustrated above, it wont "bother" you
again after the first merge.
If the first merge is a problem, then you can use hg resolve --tool
internal:local to reset the files back to their state before you
started the merge. So with
$ hg resolve --tool internal:local b
we could have brought b back to just containing one line with a.
How do I unflag the file as being a copy of another file?
If you revert a hg copy, the copied-to file remains in your working directory afterwards, untracked. You just have to add it normally.
The copied-from file isn't affected at all.
% hg copy file new-file
% hg status -C
A new-file
__file
% hg revert new-file
% hg add new-file
% hg status -C
A new-file
Reference: Mercurial: The definitive guide

Bazaar: how to show history of changes to a file?

Hey I am using bazaar for my code, I wanna show the history of changes to a specific file, instead of showing changes to the whole bazaar repo. How could I do it?
bzr blame <filename> will show you which lines were introduced with which changes:
$ bzr blame Makefile
548 steve-b | #
| #
861 steve-b | OVERRIDE_TARBALL=yes
548 steve-b |
| include common/Make.rules
|
| DIRS=parser \
| profiles \
| utils \
| changehat/libapparmor \
| changehat/mod_apparmor \
| changehat/pam_apparmor \
| tests
If you just want the commit messages, bzr log <filename> will show you:
$ bzr log Makefile
------------------------------------------------------------
revno: 1828
tags: apparmor_2.7.0-beta2
committer: John Johansen <john.johansen#canonical.com>
branch nick: apparmor
timestamp: Thu 2011-09-15 13:28:01 -0700
message:
Remove extra space insert at from of ${TAG_VERSION} when doing the ~ to -
substitution.
Signed-off-by: John Johansen <john.johansen#canonical.com>
------------------------------------------------------------
revno: 1734
committer: Steve Beattie <sbeattie#ubuntu.com>
branch nick: apparmor
timestamp: Thu 2011-06-02 18:54:56 -0700
message:
This patch adjusts the tag make target to use a separate version with
'~' replaced by '-'. This is needed for mirroring to git as git can't
handle '~'s embedded in tag or branch names.
Tested by setting up a separate tag_version target like so:
tag_version:
echo ${TAG_VERSION}
...
bzr log is the key.
Purpose: Show historical log for a branch or subset of a branch.
Usage: bzr log [FILE...]
If you use a gui (TortoiseBzr of BzrExplorer), select the file and clic on log command.