I want to use hg graft to copy a changeset from one branch to another.
However, a file that is modified in the changeset has been renamed (using hg rename) in the source branch -- before the changeset that I want to graft.
When I try I get:
scanning for duplicate grafts
grafting revision 3
searching for copies back to rev 2
unmatched files in local:
f1.txt
resolving manifests
overwrite: False, partial: False
ancestor: c06936303423, local: cfeaa3003c57+, remote: e3f2a201d1e2
remote changed f1a.txt which local deleted
use (c)hanged version or leave (d)eleted? c
Neither (c) nor (d) seem like the right option.
The file f1a.txt was renamed from f1.txt in the source branch. f1a.txt never even existed in the target branch.
Is this possible?
This is an excellent question. It looks like it is not possible. I replayed your scenario and also tried transplant and rebase -- with the same effect.
I guess the basic reason why this does not work is because a graft explicitly only picks the changes of one revision, i.e. past changes (including file renames) explicitly are not considered (update: could be a bug or missing feature as well, see #Soulman's answer).
You did not ask for, but here's a work around ..
Get a patch of the changeset you want to graft:
$ hg export --git -r <rev-to-graft> > my.patch
In this patch, replace f1a.txt with f1.txt, so you get:
...
diff --git a/f1.txt b/f1.txt
--- a/f1.txt
+++ b/f1.txt
## -1,1 +1,1 ##
...
Sed is your friend here: sed -i my.patch -e "s,\([ab]\)/f1a\.txt,\1/f1.txt,g"
Import the edited patch to the target revision:
$ hg up <target-rev>
$ hg import my.patch
I think this is simply a bug that will hopefully be fixed. There is a ticket: Graft fails when files have been moved in an ancestor revision.
A rename is a delete + add + a note saying to treat history of the old file as the history of the new file. When doing a graft (that is making a new changeset by duplicating the same changes without creating a relation as opposed to a merge), you have to incorporate the changes of the changeset some how. You can only graft an "add" operation in this case or decide not to add the file (leave it deleted). I am sorry to say, that it is fundamentally impossible to graft a rename in this case, because it is meaningless if the other branch did not have that file.
I am sorry, if I didn't understand correctly the situation you are in. If that is the case, then please provide an example. (Few commands to set up a dummy repo, which reproduces your case will do the trick).
Related
Is it possible to detect if a commit creates a new bookmark or branch via hooks in .hgrc?
I've tried to see if I can find out using hg log, but it just shows on what branch/bookmark the commit has been created: http://hgbook.red-bean.com/read/customizing-the-output-of-mercurial.html
There don't seem to be hooks for it: http://hgbook.red-bean.com/read/handling-repository-events-with-hooks.html
It would make sense I suppose that there isn't a hook for it, because it is also not possible to make a commit which is 'just' the creation of the branch indicating branches/bookmarks only exists when added to a specific commit.
I figured I could check hg branches and hg bookmarks before and after each commit and determine which are removed and added, but is there a cleaner way for detecting branch/bookmark adds/removes?
The pushkey and prepushkey hooks can detect the addition, deletion, and moves of bookmarks.
In hgrc:
[hooks]
prepushkey=echo %HG_NAMESPACE% %HG_KEY% %HG_OLD% %HG_NEW%\n >> out.txt
HG_NAMESPACE will contain "bookmark" and HG_KEY will contain the name of the bookmark.
HG_OLD will contain the hash of the commit the bookmark was before the operation. It won't be set if the bookmark is being created.
HG_NEW will contain the hash of the commit the bookmark will be after the operation. It won't be set if the bookmark is being deleted.
Bookmarks
Bookmarks-handling does not require commit
Bookmark can be created|modified for any changeset in history, not only for current
Bookmark(s) can appear as result of data-exchange (pull|push), not local user's actions
Only part of all possible cases can be covered by hook
Branches
Changing branch always reflected in commit
Branch in changeset may differ from parent's branch not only as result of hg branch (see "merge branches" and "mergesets") - and I haven't nice and easy and ready to use solution for this case
Common notes
You can use standard precommit hook (executed before commit and can enable|disable commit) for testing different conditions in commit or or pretxncommit
Mercurial template-engine has keywords for branch and bookmark for using in hg log -T
In pretxncommit hook commit already exist, but not finalized - it means you can manipulate with commit's data using -r tip and tip's parent in any Mercurial command
Dirty skeleton for hook's body
hg log -T "{KEYWORD}\n" -r "tip + tip^" | ....
where KEYWORD may be branch or bookmarks. Result of log is two-strings output, which can be identical of different (not sure for bookmark, TBT!!), which you have to compare (as you want and can)
PS: Idea inspired by EnsureCommitPolicy Wiki and Mercurial pre commit hook topic here
It seems that when using the git exteneded diff format, which is needed to properly support renames. mercurial doesn't add the revision ids in the command line it prints in the patch, for example:
diff --git a/test.txt b/test.txt
Instead of:
diff -r 86b54773cb1d -r 354c20900a62 test.txt
Is there a way to make it do add a revision? It's quite useful when looking at patches later...
EDIT: git adds a line like the following to handle this:
index e4ff69d..09eb727 100644
Why can't mercurial add a line like this too:
index 86b54773cb1d..354c20900a62 100644
since it doesn't track file permissions like git it can always just invent something using the umask or some default, but that's still better than nothing.
I think you're out of luck. That's called --git because it's supposed to mimic the diff format git creates and accepts, so it excludes any mercurial-isms.
You could probably use hg export --git to get a mercurial comment block at the top which will include the hashes.
What mercurial command can you use to see the changeset prior to changeset xyz?
If you do hg log -r :xyz you see all the changesets prior to (and including) xyz - listed in ascending order. But I'd like to easily see just the prior changeset.
Update: Really what I'm after is this: If I do an hg fetch, what command can I use to see the changeset PRIOR to the the changesets that were pulled in by the fetch?
hg log -r :xyz where xyz is the first changeset pulled in by the fetch works but it returns the entire list of prior changesets where I just want the most recent.
You can't do it. Once you've pulled (and fetch is just pull + either update or merge) there is no record of what you had before the pull and what you just got from the pull.
You should do hg incoming before you pull to see what you will get.
Also, stop using fetch. The acts of pulling and updating and merging are completely separate and doing them in a single commands provides inadequate error reporting and just confuses things. The command is disabled by default and there's talk of removing it entirely. Merging is coding, and it shouldn't be hidden.
Expanding to Show cases you can't cover
If before fetching your history is this:
[A]-[B]-[C]
and you (against all advice) fetch and get [D] you now have:
[A]-[B]-[C]-[D]
And you can see exactly what's new with:
hg diff -r tip-1
or with:
hg diff -r "parent(tip)"
But if starting again with A,B,C you fetch and get D,E yielding this:
[A]-[B]-[C]-[D]-[E]
there is no command you can run to see "what changed" without having previously written [C] down on a post it note.
If, on the other hand your repo started out looking like this:
[A]-[B]
\
-[C]
and after fetching you have this:
[A]-[B]-[D]
\
-[C]-[E]
there's no single command that will tell you "what changed". Similarly, if before pulling your repo looked like this:
[A]-[B]-[C]
and after fetching you got this:
[A]-[B]-[C]-[E]-[F]
\ /
-[D]-------/
where [F] is the new ill-advised auto-merge changeset fetch created then the command:
hg diff -r C
will tell you what's new, but there's no way to look up 'C' without having previously written it down.
I assume xyz is a changeset in hash form. Try:
hg log -r :xyz-1
That should work to list just the changeset prior to 1
I'm not sure what is meant by prior. Your question could mean you might just want the parents of the changeset. These you would get easily with
hg parent -r xyz
This does not need fancy new versions of mercurial.
hg parents [-r REV] [FILE]
show the parents of the working directory or revision
Print the working directory's parent revisions. If a revision is given via
-r/--rev, the parent of that revision will be printed. If a file argument
is given, the revision in which the file was last changed (before the
working directory revision or the argument to --rev if given) is printed.
options:
-r --rev show parents of the specified revision
--style display using template map file
--template display with template
--mq operate on patch repository
With the parentrevspec[1] extension installed, you can use the git-like syntax below. Depending on your shell, the quotes may not be necessary.
hg log -r 'xyz^'
https://www.mercurial-scm.org/wiki/ParentrevspecExtension
We're currently working on the new version (version 2.0) of an application.
We have a customer running version 1.0 of the app who found a bug. We updated to the tagged changeset for version 1.0 and located and fixed the bug.
We committed the change which created a new head in our source tree.
The question is how best to merge this? Ideally I would want to merge it into the changeset that followed version 1.0. I don't want to merge it into the tip because the code where the bug was found doesn't actually exist anymore.
I realise I perhaps should have created a separate branch for "v1.0 bug fix".
Thanks,
Ben
When moving a change within a repository using merge it's all about the most recent common ancestor of the place you have the change and the place you want it. If before making this fix your repo looked like this:
[a]-[b]-[c]-[d]
with the 1.0 tagged changeset being [b] then you now have this:
[a]-[b]-[c]-[d]
\
-[e]
where the fix is in [e]. If that's the case then you just need to do this:
hg update d
hg merge e
hg commit
Then you'll have this:
[a]-[b]-[c]-[d]-[f]
\ /
-[e]-----
If on the other hand before making the changes your repo looked like this:
[a]-[b]-[c]-[d]
\
-[e]-[f]
where the 1.0 rag pointed to [f] then you now have this:
[a]-[b]-[c]-[d]
\
-[e]-[f]-[g]
with the fix in [g]. If you want to move the changeset [g] into [d] without bringing [e] and [f] along with it, there's no good way to do it. The not-so-good way available to you (called cherrypicking) is to use the hg export and hg import commands.
No workflow requires cherry picking, but avoiding it requires a little forethought. In that second case you would avoid it by making the fix not on the 1.0 series (as a child of [f]) but instead as a child of the most recent common ancestor of the two places you want that change. Since you want that change in both [d] and [f] you look for their most recent common ancestor and see it's [b] and make the change as a child of that using these commands:
hg update b
..edit..
hg commit
leaving you with this graph:
[a]-[b]-[c]-[d]
\
\--[g]
\
-[e]-[f]
this fix, [g] is a new head, and you can merge it into both [d] (2.0) and [f] (1.0) without any cherry picking at all. The commands for that would be:
hg update d
hg merge g
hg commit
hg update f
hg merge g
hg commit
and the resulting graph would be:
[a]-[b]-[c]-[d]--[h]
\ /
\--[g]----
\ \
-[e]-[f]-[i]
where [h] is your new 2.0 with the fix, and [i] is your new 1.0 with the fix.
Summary: you can always avoid cherry picking with forethought, but it's not the end of the world if you didn't
It sounds like you have this:
[a]-[b]-[v1]-[c]-[d]-[v2]
\
-[fix]
and want to get rid of the extra head without merging any of the changes in [fix].
Option #1
The following commands will simple mark the branch closed, and it won't count as an extra head and be hidden from hg heads and hg branches commands:
hg update fix
hg commit --close-branch -m "closed branch"
Option #2
The following commands will "dummy merge" the extra head, throwing away any changes.
hg update v2
hg --config ui.merge=internal:fail merge
hg revert --all --rev .
hg commit -m "dummy merge"
the --config ui.merge=internal:fail flag prevents a merge tool from trying to merge any conflicts, but doesn't prevent files added to the other head from appearing, since there would be no merge conflicts with a newly added file. The revert will simply update all the files back to the first parent's state. You'll end up with:
[a]-[b]-[v1]-[c]-[d]-[v2]-[merge]
\ /
-[fix]-----------
but the content of [merge] will be the same as [v2].
Option #3
Yet another way to do a dummy merge:
hg update v2
hg debugsetparents v2 fix
hg commit -m "dummy merge"
This sets the working directory to v2, but then "fakes out" Mercurial that both v2 and fix are the parents and commits it as a merge.
Before, when I was using perforce, I could work on multiple bugs at once as long as the code did not affect the same files, by having multiple change sets open at once.
Changeset 1:
A.txt
B.txt
C.txt
Changeset 2:
D.txt
E.txt
F.txt
I could submit changeset 2 to the repository without submitting changeset 1 (because it's still in progress)
Is this possible with Mercurial? other than doing each file as a separate commit?
You can always just do: hg commit D.txt E.txt F.txt to commit just those files which will leave A.txt, B.txt, and C.txt uncommited. Using the -I option to commit lets you do those with patterns if they're, for example, in a common directory: hg commit -I 'dir1/**'
You can have two separate branches (working copies) and make one change in and the other in the other. That's one way.
Another is to use Mercurial Queues. You can use qpush --move to change the order of changesets if they have no dependencies on one another, so you can then use qfinish to 'commit' the first changeset that's ready.
You don't actually hold changesets "open" in Mercurial.
Either you've committed your changes, or you haven't.
You can, however, have multiple files with uncommitted changes, and only commit a few of them. It is even possible to commit parts of files, chunks, instead of the whole file.
If I was to do what you're asking I would simply make another clone locally from my first one, and work on the two different fixes in two different working folders. Then I have all the freedom I need to combine the two (push/pull locally), push to the server, etc.
In Mercurial, cloning is a cheap operation when done locally. Just clone the repository for each thing you are working on. Push back to the main repository as each thing is ready:
hg clone http://project project
hg clone project project-bugfix // uses hardlinks and is inexpensive.
hg clone project project-feature
<edit bugfix and feature as needed>
But remember that the default push location is the project cloned from, so you'll either need to edit .hg\hgrc's default push path for the "project" clones, or make sure to hg push http://project from "project-bugfix" and "project-feature" when complete.
Even if your modifications touches the same file you can select what to include (hunk by hunk). You just have to activate the record extension (it's installed by not activated by default).
To activate the record extension, put this in the [extensions] section of your .hgrc:
[extensions]
hgext.record=
Then to commit, replace hg commit by hg record. Let's assume your example change sets with a G.txt file that have changes for both change sets. Commit with:
hg record D.txt E.txt F.txt G.txt
Answer questions, for example:
2 hunks, 6 lines changed
examine changes to 'D.txt'? [Ynsfdaq?] f # f for full file (help with ?)
... skipped file E and F
6 hunks, 35 lines changed
examine changes to 'G.txt'? [Ynsfdaq?] y
## ... (patch hunk here)
record change 6/16 to 'G.txt'? [Ynsfdaq?] y # y to include the hunk, n to skip