How can I insert or alter a mercurial revision - mercurial

How can I alter r0 so it looks like I added .hgignore when I created my repository or insert a commit before my current r0?
I just converted a huge SVN repo using hgsvn to mercurial. It took several hours and had to go through about a dozen branches to get the whole thing. My problem now is that .hgignore isn't committed, so when I hgimportsvn a branch, the .hgignore doesn't seem to come with it. I would like to insert that file as part of r0 or insert it before that (and shift everything by 1). I've also tried committing it at the end of my Mercurial trunk checkout, but it seems hgimportsvn always clones (branches?) from the same Mercurial revision my SVN branch was created from so .hgignore is lost again.

You probably need something like the ConvertExtension. Check out the --splicemap option.
To create a new history with a .hgignore file added as the first revision:
Create a new repository whose only revision is the .hgignore commit.
Create a splicemap file containing two 40-char hashes: rev 0 of your current database, and rev 0 of your new database.
Run hg convert <current_db_dir> <new_db_dir> --splicemap splice_filename
This adds each revision in the current database to the new database. The splicemap specifies editing of parents, so if revision 0 of the current database gets its parent set to revision 0 of the new database.
Below is a Windows batch file that creates a 3-revision database and a 1-revision database with an .hgignore file in it, the splices them together. The result should be what you are looking for. If you have a large original database it could take a while, since the entire history of the source database is re-written to the destination database.
#echo off
#REM Create a 3-revision database
hg init current
cd current
echo >file1
hg add
hg ci -m file1
echo >file2
hg add
hg ci -m file2
echo >file3
hg add
hg ci -m file3
#REM Add the first revision to the splice map
hg log -r 0 --template "{node} " > ..\map
#REM Display the result
hg log
cd ..
#REM Create a 1-revision database
hg init ignore
cd ignore
echo glob:*.txt>.hgignore
hg add
hg ci -m ignore
#REM Specify this node as the parent of the other
#REM database's first revision in the splice map
hg log -r 0 --template "{node}\n" >> ..\map
hg log
cd ..
#REM Here's the resulting splice map
type map
#REM Make a copy to store the result
hg clone ignore result
#REM Add revisions from "current" to "result" honoring
#REM the splice map
hg convert current result --splicemap map
#REM Display the result
cd result
hg log

Maybe you could commit .hgignore and then rebase the commit to the beginning of history (see https://www.mercurial-scm.org/wiki/RebaseProject).

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: roll back an "hg commit --amend".

I accidentally did a "hg commit --amend" instead of just a commit. How can I roll back the commit to before the amend?
You can use hg reflog (from the journal extension) and hg reset <hash>.
hg reflog -v
should give something like:
<old-hash> -> <new-hash> <user> <timestamp> commit --amend <some-path>
if that is the amend you want to revert, just use:
hg reset <old-hash>
The commit will be reverted to what is previously was and the changes that were amended should now be uncommitted changes (check using hg status and hg diff).
If your version of Mercurial is new enough, I believe you should be able to use the hg unamend command from the uncommit extension that ships with Mercurial. This may require that obsolescence markers are enabled, I'm not sure.
Enable the uncommit extension, add this to your ~/.hgrc:
[extensions]
uncommit =
Actually run the unamend:
hg unamend
Find the latest saved backup in .hg/strip-backup directory
hg unbundle .hg/strip-backup/<latest backup>
Now you should have two heads - one with the amended commit, other one with two commits (first one - old commit before amending, second one caled: "temporary amend commit for (old commit hash)".
if you have histedit extension, you can do hg histedit on it in order to change it (e.g. select edit in order to achieve a state just before the commit, i.e. when you can see all changes using hg diff).
Don't forget to strip the old head.
It's 2022, and my attempts to use hg unamend have not worked. histedit is too clunky for my purposes, but the solution proposed by mariu52 elsewhere on this page can easily be adapted to work without histedit. It relies on the -k option of the strip subcommand.
In a nutshell:
Find the latest saved backup in the .hg/strip-backup/ directory
Run hg unbundle .hg/strip-backup/<latest backup> where <latest backup> signifies the full filename.
Run hg heads and note the rev number corresponding to the amendment.
Let's call this $AMENDREV; this is the rev number we will strip in the next step.
Run hg strip -k --rev $AMENDREV
Using the -k option in the strip command is critical.
WARNING: this procedure will in effect erase the memory of any add or remove commands
that were pending when the amend command was executed.
For example, in the transcript below, the hg add file2 command is effectively
forgotten after the strip command is executed.
For clarity, here's a transcript based on the above recipe.
$ mkdir tmp ; cd tmp
$ ls
$ echo 1 > file1
$ echo 2 > file2
$ hg init
$ ls
file1 file2
$ hg add file1
$ hg commit -m 'one file'
$ hg add file2
$ hg amend -m 'amendment'
saved backup bundle to /tmp/tmp/.hg/strip-backup/d332ee829c21-5a5f23b0-amend.hg
$ hg unbundle -u .hg/strip-backup/d332ee829c21-5a5f23b0-amend.hg
adding changesets
adding manifests
adding file changes
added 1 changesets with 0 changes to 1 files (+1 heads)
new changesets d332ee829c21 (1 drafts)
0 files updated, 0 files merged, 0 files removed, 0 files unresolved
updated to "1a445f3252eb: amendment"
1 other heads for branch "default"
$ hg heads
1[tip]:-1 d332ee829c21 2022-11-09 01:55 -0500 peak
one file
0 1a445f3252eb 2022-11-09 01:55 -0500 peak
amendment
$ ls
file1 file2
$ hg strip -k -r 0
saved backup bundle to /tmp/tmp/.hg/strip-backup/1a445f3252eb-bfaab5ec-backup.hg
$ ls
file1 file2
$ hg list
r0: peak tip 2022-11-09 01:55 -0500
one file
file1
$
hg unamend part of Mercurial 4.5 (2018-02-01).
NOTE: This answer is now deprecated. See the answer from #Sorina Sandu instead.
See hg help commit, where it says:
The --amend flag can be used to amend the parent of the working
directory with a new commit that contains the changes in the parent in
addition to those currently reported by "hg status", if there are any.
The old commit is stored in a backup bundle in ".hg/strip-backup" (see
"hg help bundle" and "hg help unbundle" on how to restore it).

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

How to diff a single directory in 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.

Mercurial Subrepos, how to control which changeset I want to use for a subrepo?

I am reading up on subrepos, and have been running some tests locally, seems to work OK so far, but I have one question.
How do I specify/control which changeset I want to use for a particular subrepo?
For instance, let's say I have the following two projects:
class library application
o fourth commit o second commit, added a feature
| |
o third commit o initial commit
|
| o second commit
|/
o initial commit
Now, I want the class library as a subrepo of my application, but due to the immaturity of the longest branch (the one ending up as fourth commit), I want to temporarily use the "second commit" tip.
How do I go about configuring that, assuming it is even possible?
Here's a batch file that sets up the above two repos + adds the library as a subrepo.
If you run the batch file, it will output:
[C:\Temp] :test
...
v4
As you can see from that last line there, it verifies the contents of the file in the class library, which is "v4" from the fourth commit. I'd like it to be "v2", and persist as "v2" until I'm ready to pull down a newer version from the class library repository.
Can anyone tell me if it is possible to do what I want, and if so, what I need to do in order to lock my subrepo to the right changeset?
Batch-file:
#echo off
if exist app rd /s /q app
if exist lib rd /s /q lib
if exist app-clone rd /s /q app-clone
rem == app ==
hg init app
cd app
echo program>main.txt
hg add main.txt
hg commit -m "initial commit"
echo program+feature1>main.txt
hg commit -m "second commit, added a feature"
cd ..
rem == lib ==
hg init lib
cd lib
echo v1>lib.txt
hg add lib.txt
hg commit -m "initial commit"
echo v2>lib.txt
hg commit -m "second commit"
hg update 0
echo v3>lib.txt
hg commit -m "third commit"
echo v4>lib.txt
hg commit -m "fourth commit"
cd ..
rem == subrepos ==
cd app
hg clone ..\lib lib
echo lib = ..\lib >.hgsub
hg add .hgsub
hg commit -m "added subrepo"
cd ..
rem == clone ==
hg clone app app-clone
type app-clone\lib\lib.txt
Edit: Ok, I got my answer, thanks #VonC, I added the following section to my batch-file, above the rem == clone == line, and re-executed it, and now it locks the subrepo to the correct changeset.
rem == lock ==
cd app\lib
hg update 1
cd ..
hg commit -m "lock to second commit"
cd ..
Not tested, but you should be able to go within your subrepo, update its content to the right commit (hg update), go back up one level (in the main project) and commit.
That should update the .hgsubstate with the right commit.
(extreme workaround, update that .hgsubstate yourself, but that is not recommended.)
The all idea of hg subrepos (or Git submodules) is to allow a dependency management by referencing a fixed id for a given sub-repo. If no id is given when creating the subrepo, then the latest id is selected (v4 in your case), but you can checkout whatever id you need.
Actually, this thread even complains that:
Right now, commit recursively tries to commit subrepositories before committing the current repository.
That allows you to:
record some changes in the sub-repo.
update the .hgsubstate of the main project with the new state (id) of the subrepo.
Your sub-repo revision won't be advanced without you explicitly choosing to do so, so all you have to do is set it up as you'd like initially. When initially creating the sub repo, just use a '-r' argument to clone only up to the changeset you'd like:
rem == subrepos ==
cd app
hg clone -r CHANGESETYOUWANT ..\lib lib
echo lib = ..\lib >.hgsub
hg add .hgsub
hg commit -m "added subrepo"
cd ..
note the modification on line three.