How to diff a single directory in mercurial - mercurial

I have two local mercurial repos, let's call them product and skunkworks. Skunkworks is a clone of product and has a lot of recent commit activity against it. I need to be able to package up the diffs from one small part of the directory tree and pull those out as a patch file.
I've got the following layout on disk:
c:/dev/product
c:/dev/skunkworks
The area of diffs I want to package up is from c:/dev/skunkworks/buildstuff. This directory exists in the product repo. I don't want to expose everyone else to the changes in the rest of skunkworks just yet, but need to be able to share my new build goodness.
I've tried a number of different parameter settings and combinations of the 'hg diff' command but am obviously doing something wrong. Any pointers or suggestions would be greatly appreciated.
Thanks!

This can be done pretty easily, but first lemme suggest that the next time you're doing to independent bits of work (the build work and the rest of skunkworks) you do them in separate heads -- then you can push the buildwork directly to product using hg push w/o moving skunkworks over there too. That pattern would look like:
hg clone product skunkworks
cd skunkworks
... build hacking ...
hg commit
... build hacking ...
hg commit
hg update REVNUMBER # where revnumber is the last revision that exists in product
... skunkworks hacking ...
hg commit # at this point you'll see (+1 heads)
... more skunkworks hacking ...
hg commit
at that point your skunkworks clone will have two heads, one with build work and one with skunkworks. You can switch back and forth while working, alternating between build and sunkworks while keeping them separate within the same clone. If while working on skunkworks you want the build niceties too you can do:
hg update SKUNKWORKSHEAD
hg merge BUILDHEAD
that merges the buildwork into skunkworks, but the buildwork stuff still doesn't have any skunkworks as its parent, so at anytime you could do:
cd ../product
hg pull -r BUILDHEAD ../skunkworks
and you'd move only the build stuff into product without any of the other skunkworks changes.
This is the optimal way to do such a thing (technically called anonymous branches) because when you are ready to move skunkworks into product it'll know exactly where the build changes came from and how to correctly integrate them into skunkworks.
Whew, all that said what you want to do now is probably:
cd skunkworks
hg diff -I buildstuff/** -r LAST_REV_IN_PRODUCT -r tip > ../applyme.patch
cd ../product
hg import --no-commit ../applyme.patch
hg commit
The drawback there is that when you do merge skunkworks back into product all the build changes will already be there, which will make the merge less automatic that it might otherwise be.

I think all you want is to run in skunkworks hg diff buildstuff -r <rev> where <rev> is the last revision matching the product repo.
Below creates a number of files and directories representing the original repository:
C:\>hg init test
C:\>cd test
C:\test>echo >file1
C:\test>echo >file2
C:\test>md dir1
C:\test>md dir2
C:\test>echo >dir1\file3
C:\test>echo >dir1\file4
C:\test>echo >dir2\file5
C:\test>echo >dir2\file6
C:\test>hg ci -Am 1
adding dir1/file3
adding dir1/file4
adding dir2/file5
adding dir2/file6
adding file1
adding file2
We'll clone and change a file in each of the subdirectories:
C:\test>cd ..
C:\>hg clone test skunk
updating to branch default
6 files updated, 0 files merged, 0 files removed, 0 files unresolved
C:\>cd skunk
C:\skunk>echo >>dir1\file4
C:\skunk>echo >>dir2\file5
C:\skunk>hg st
M dir1\file4
M dir2\file5
C:\skunk>hg ci -m skunk
Now just display the diff for dir2 compared to the previous checkin:
C:\skunk>hg diff dir2 -r 0
diff --git a/dir2/file5 b/dir2/file5
--- a/dir2/file5
+++ b/dir2/file5
## -1,1 +1,2 ##
ECHO is on.
+ECHO is on.

Related

Add a parent to the original changeset in Mercurial

I have a project with 24 months of source control history in a Mercurial repository.
I've recently found some old tarballs of the project that predate source control, and i think they would be useful to import into the repository as "pre-historic" changesets.
Can i somehow add a parent to my initial commit?
Alternatively, is it possible to re-play my entire repository history on top of the tarballs, preserving all metadata (timestamps etc)?
Is it possible to have the new parent commits use the timestamps of these old tarballs?
You can use the convert extension to build a new repository where the tarballs are imported as revisions before your current root revision.
First, you import the tarballs based on the null revision:
$ hg update null
$ tar -xvzf backup-2010.tar.gz
$ hg addremove
$ hg commit -m 'Version from 2010'
$ rm -r *
$ tar -xvzf backup-2011.tar.gz
$ hg addremove
$ hg commit -m 'Version from 2011'
I'm using addremove above to give Mercurial a chance to detect renames between each tarball (look at the --similarity flag to fine-tune this and use hg rename --after by hand to help Mercurial further). Also, I remove all the files in the working copy before importing a new tarball: that way the next commit will contain exactly the snapshot present in the tarball you unpack.
After you've imported all the tarballs like above, you have a parallel history in your repository:
[c1] --- [c2] --- [c3] ... [cN]
[t1] --- [t2] --- [tM]
Your old commits are c1 to cN and the commits from the tarballs are t1 to tM. At the moment they share no history — it's as if you used hg pull -f to pull an unrelated repository into the current one.
The convert extension can now be used to do a Mercurial to Mercurial conversion where you rewrite the parent revision of c1 to be tM. Use the --splicemap flag for this. It needs a file with
<full changeset hash for c1> <full changeset hash for tM>
Use hg log --template '{node} ' -r c1 -r tM > splicemap to generate such a file. Then run
$ hg convert --splicemap splicemap . spliced
to generate a new repository spliced with the combined history. The repository is new, so you need to get everybody to re-clone it.
This technique is similar to using hg rebase as suggested by Kindread. The difference is that convert wont try to merge anything: it simply rewrites the parent pointer in c1 to be tM. Since there is no merging involved, this cannot fails with weird merge conflicts.
You should look at using rebase. This can allow you to make the changes the 2nd changeset on your repo ( you have to rebase from the 1st ).
https://www.mercurial-scm.org/wiki/RebaseExtension
However, note that if there are other clones of this repo existing ( such as for fellow developers, or on a repo server ), you will have issues with them pulling the revised repo. You will probably have to co-ordinate with the owners of those clone's to get all work into a single clone, rebase that clone, and then have everyone re-clone from the revised clone. You will also have to change the phase the of the changesets.
https://www.mercurial-scm.org/wiki/Phases
Honestly though, I would just add them to your 'modern-day' repo, I don't think making them pre-historic would give you any notable advantage over adding them to the top.

Mercurial Patch Creation and Usage

I have come across a problem that I "think" can only be resolved using patches.
I cloned a project from our main repository, made quite a few changes (updates, deletion of files & directory and additions) to it. These changes are not even committed. The problem is, project from the main repository has been deleted/removed and recreated as a new project (name is same, all the directory structures everything is same as before). I cloned that project again from the main repository and would like to transfer all my uncommitted changes to it.
I am still exploring the hg patch to resolve that. It would be helpful if someone could confirm that creating and adding a patch IS the right approach to this, any resources explaining the process would be of great help.
You're correct — a patch is what you need to transfer the information from one repository to another (unrelated) repository. This will work since the files are the same, as you note.
So, to transfer your uncommitted changes from your old clone, you do
$ hg diff -g > uncommited.patch
$ cd ../new
$ hg import --no-commit ../old/uncomitted.patch
That will restore the information saved in the patch. This includes information about files that are added or renamed in the old clone.
The following steps can be performed with a standard Mercurial install:
Commit the changes in your local repository. Note the revision number.
Use "hg export -r REV >patch.diff" to create a patch.
Clone the new repository.
Use "hg import patch.diff" to apply the patch to the new repository.
Example
C:\>hg init example
C:\>cd example
C:\example>echo >file1
C:\example>hg ci -Am file1
adding file1
C:\example>hg clone . ..\example2
updating to branch default
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
C:\example>rd /s/q .hg
C:\example>hg init
C:\example>hg ci -Am same-but-different
adding file1
At this point example and example2 have identical contents, but the repositories are unrelated to each other due to deleting and reinitializing the .hg folder.
Now make some changes and commit them in one of the repositories, then export them as a patch:
C:\example>echo >>file1
C:\example>echo >file2
C:\example>hg ci -Am changes
adding file2
C:\example>hg export -r 1 >patch.diff
Below shows that the other repository can't pull the changes, because of the reinitialization. It can, however, apply the patch successfully:
C:\example>cd ..\example2
C:\example2>hg pull
pulling from c:\example
searching for changes
abort: repository is unrelated
C:\example2>hg import ..\example\patch.diff
applying ..\example\patch.diff
I would first make copies of everything so you have a way of backtracking.
Then, in the working copy with the changes, I would first delete the .hg directory, then copy in the .hg directory from the new repo. This basically transfers all of the changed files into the new repo without the need to delete any files and directories.
You will still need to tell the repo about whether to remove any files marked as missing. You will also have to handle renames manually. If this is a small number of operations, it's easier than trying to use the patch method.
Once this is done, commit your changes and push, if necessary.
seems like what you want is patch queues. In that you have uncommitted changes, and you want to pull from the new repo before committing them....
$ hg qinit -c # initialize mq for your repo containing the uncommitted changes
$ hg qnew name_of_patch # create patch that contains your uncommitted changes
$ hg qpop # resets your working dir back to the parent changeset
no worries though, your changes are safe and sound in .hg/patches/name_of_patch to see for yourself.....
$ cat .hg/patches/name_of_patch
now pull in the new repo
$ hg pull -u http://location.of.new/repo # pull in changes from new repo update working dir
$ hg qpush # apply your uncommitted changes to new repo
If you are lucky you will have no merge conflicts and you can go ahead and commit the patch by....
$ hg qfinish -a # change all applied patches to changeset
And then if you want....
$ hg push http://location.of.new/repo
If the repos are unrelated, just init a patch repo on your new repo. and manually copy the patch in and add it to .hg/patches/series file.
assuming patch was created. clone new repo
$ hg clone http://location.of.new/repo ./new_repo
init patch repo
$ cd ./new_repo && hg qinit -c
copy patch
$ cp ../old_repo/.hg/patches/name_of_patch .hg/patches/
edit series file using an editor of some sort
$ your_favorite_editor .hg/patches/series
name_of_patch # <---put this in the series file
apply your patch to new repo
$ hg qpush
if no merge conflicts and you are convinced it works
$ hg qfinish -a
If the layout is the same, you can just copy all the files over (excluding .hg) and then use hg addrem.
Try to look into the MQ plugin, it does exactly this if I recall. I've never had a use for that though, so I can't say.
If the old repository was simply moved/cloned to a new URL then you could simply change the remote repository you talk to the new one.
If, however, it was recreated from the ground up (even with the same structure) then I don't believe Mercurial has any built-in functionality to help you here. Mercurial patches reference specific changesets which won't exist in your new repository.
You could use a merge tool to perform the diff and bring across any changes you made.
Edited To answer the question in the comment:
When you clone the repository you are taking a complete snapshot of the entire change history - along with the associated change-set IDs, etc.
Mercurial tracks changes by change-sets to the repository, rather than at the file level like Subversion.
If you clone, then you can easily push/merge into another repository that was also cloned from the same source.
If you recreated the repository then the change IDs won't match, and can't be merged in Hg.
The only option in this scenario would be to use a Merge tool which will let you see mismatches in files/folder structure.
Also: Worth pointing out http://hginit.com/ because it explains (indirectly) some of this.

Mercurial - see list of files that need to be manually merged?

Is there a Mercurial command you can use after an hg pull to see a list of all files that will be need to be manually merged (ie: that have conflicts) when doing an hg merge?
hg resolve --list
From the documentation:
Merges with unresolved conflicts are often the result of non-interactive merging using the internal:merge configuration setting, or a command-line merge tool like diff3. The resolve command is used to manage the files involved in a merge, after hg merge has been run, and before hg commit is run (i.e. the working directory must have two parents).
Edit 5 January 2012:
(I received an up vote for this answer today so I revisited it. I discovered that I misunderstood the question.)
The question is "I have performed a pull from a remote repository and have not yet performed a merge. Can I see what conflicts will be created upon performing the merge?"
My answer above is clearly wrong. After reading through the linked documentation, I do not think there is a built-in method for doing this. However, there is a way to do it without ruining your working source tree.
Let's assume you have cloned repository A from some remote source to repository B on your local system, i.e. hg clone http://hg.example.com/A B. After doing so, you make changes to your local repository, B, that involve at least one commit. In the meantime, changes have been made to repository A so that when you do a pull you get a message indicated new changesets have been added and heads have been created.
At this point, you can do hg heads to list the two changesets that will be involved in a merge. From this information, you can issue a status command to list the differences between the heads. Assuming the revision numbers in your repository B, according to the heads list, are "1" and "2", then you can do hg status --rev 1:2 to see a list of the changes.
Of course, this doesn't really tell you if conflicts will occur when you do a merge. Since there isn't a command that will show you this, you will have to "preview" the merge by cloning to a new repository and doing the merge there. So, hg clone B C && cd C && hg merge. If you are satisfied with the result of this merge you can do hg com -m 'Merging complete' && hg push && cd ../ && rm -rf C.
It's a bit of a process, but it keeps your current source tree clean if the merge turns out to be a disaster. You might also find this description of working with public repositories helpful.
Unless I'm misreading it myself, the answers above don't seem to address the question that I think is being asked: I have two branches in my repository that I'd like to merge, and I want to know what conflicts will come up (e.g., before stepping through the conflict resolutions one-by-one.)
To do this, I would merge with the :merge3 tool (which tries to merge automatically, but leaves conflicts unresolved) and then use hg resolve --list — or just look at the output of merge command — to see the conflicts.
hg merge <otherbranch> --tool :merge3
hg resolve -l
If you didn't actually want to merge in the end (if you just want to see what would conflict) you can run hg update -C afterwards to undo the merge.
If you do want to finish the merge, you can run hg resolve <filepath> for each file, or just hg resolve --all to step through all that remain with conflicts, before you hg commit the merge changeset.
You can use the --rev option of hg stat with a pair of revisions to see what file differences exist between the two. See below for a slightly verbose but detailed example:
First we start by making a new repository:
[gkeramidas /tmp]$ hg init foo
[gkeramidas /tmp]$ cd foo
Then add a single file called foo.txt to the new repository:
[gkeramidas /tmp/foo]$ echo foo > foo.txt
[gkeramidas /tmp/foo]$ hg commit -Am 'add foo'
adding foo.txt
[gkeramidas /tmp/foo]$ hg glog
# 0[tip] b7ac7bd864b7 2011-01-30 18:11 -0800 gkeramidas
add foo
Now add a second file, called bar.txt as revision 1:
[gkeramidas /tmp/foo]$ echo bar > bar.txt
[gkeramidas /tmp/foo]$ hg commit -Am 'add bar'
adding bar.txt
Go back to revision 0, and add a third file, on a different head. This is done to simulate a pull from someone else who had cloned the same repository at its starting revision:
[gkeramidas /tmp/foo]$ hg up -C 0
0 files updated, 0 files merged, 1 files removed, 0 files unresolved
[gkeramidas /tmp/foo]$ echo koko > koko.txt
[gkeramidas /tmp/foo]$ hg commit -Am 'add koko'
adding koko.txt
created new head
[gkeramidas /tmp/foo]$ hg glog
# 2[tip]:0 e5d80abdcb06 2011-01-30 18:12 -0800 gkeramidas
| add koko
|
| o 1 a2d0d0e66ce4 2011-01-30 18:12 -0800 gkeramidas
|/ add bar
|
o 0 b7ac7bd864b7 2011-01-30 18:11 -0800 gkeramidas
add foo
Now you can use hg stat to see what file differences exist between any pair of revisions, e.g. the changes from rev 0 to rev 1 added 'bar.txt' to the file list:
[gkeramidas /tmp/foo]$ hg stat --rev 0:1
A bar.txt
The changes from rev 0 to rev2 added 'koko.txt' to the file list:
[gkeramidas /tmp/foo]$ hg stat --rev 0:2
A koko.txt
But more interestingly, the changes from rev 1 to rev 2 involve two file manifest changes. (1) 'koko.txt' was added in rev 2, and (2) 'bar.txt' exists in rev 1 but is missing from rev 2, so it shows as a 'removed' file:
[gkeramidas /tmp/foo]$ hg stat --rev 1:2
A koko.txt
R bar.txt
I think hg status is what you are looking for.
You may want to read this chapter from Mercurial: The Definitive Guide
http://hgbook.red-bean.com/read/mercurial-in-daily-use.html

mercurial -- "merge" by abandoning one changeset

I have two heads, let's call them "A" (the good head) and "B" (the bad head). I want to merge them by taking everything from A and nothing from B. Basically, my merge of A and B is A.
When I try hg merge, it starts asking me about this file and that, and inevitably I get into trouble. I don't want any of that! How can I tell it to merge them and end up with A, preferably without any intermediate steps?
From the Mercurial tips at section 22. Keep "My" or "Their" files when doing a merge.
https://www.mercurial-scm.org/wiki/TipsAndTricks
Occasionally you want to merge two heads, but you want to throw away all changes from one of the heads, a so-called dummy merge. You can override the merge by using the ui.merge configuration entry:
$ hg --config ui.merge=internal:local merge #keep my files
$ hg --config ui.merge=internal:other merge #keep their files
Here local means parent of working directory, other is the head you want to merge with. This will leave out updates from the other head.
To merge X into the current revision without letting any of the changes from X come through, do:
hg --config ui.merge=internal:fail merge X
hg revert --all --rev .
The other approach is mentioned in : https://www.mercurial-scm.org/wiki/PruningDeadBranches
$ 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
One thing I came across and started using recently on some personal repos was just using the close-branch switch with commit. e.g.
$ hg update B
$ hg commit --close-branch -m "Abandoning branch"
In my reasoning, if you're blowing away one branch in favor of the other entirely, it's simply not a merge and it's silly to call it that. I'm relatively new to hg myself, and I seem to recall that --close-branch has not been around since the beginning and maybe that's why it doesn't have as much traction as the merging gyrations I usually see.

Can't branch a single file with Mercurial?

is this possible with Mercurial? and which Version Control system can do this besides Clearcase?
David is correct that you can't have a branch that exists on only a single file, but it's worth pointing out that people often have branches that alter only a single file. Since the branch metadata is stored in the changeset, and since the changeset contains only a delta (change), having a branch that alters only a single files is nearly instantanous to create, update, commit, and merge, plus it takes up almost no space on disk.
Resultingly, it's a very common way to handle per-customer configurations. Keep the tiny change for them in a branch, and merge from main, where development happened, into that branch, whenever you want to update their deployment.
How you could use MQ:
$ hg qnew -m "Changes for client0" client0
... change the file ...
$ hg qref # update the client0 patch with the changes
$ hg qpop # pop the changes off the queue stack
... develop like normal ...
... client0 asks for a build ...
$ hg qpu # apply client0's patch
$ make release
$ hg qpop
It would get a bit finicky if you've got to deal with a lot of clients… But it may be worth considering.
The other thing you could do, of course, is just commit a bunch of .diff files:
... make changes for client 0 ...
$ hg diff > client0.diff
$ hg revert --all
$ hg add client0.diff
$ hg ci -m "Adding client0 changes"
... develop ...
... client0 asks for a build ...
$ patch -p1 < client0.diff
$ make release
$ hg revert --all
No, it's not possible. A branch in Mercurial is a snapshot of the entire repository state.
You could do it with CVS, though, as CVS tracks changes on a per-file basis :)