Is there any way to get an immutable file ID for a file in repository?
I need an identifier which will survive a file rename. So if there was file Test01.txt and it was renamed to Test02.txt (using TortoiseHG rename menu item or the hg rename command). I want to have some ID which will correspond to Test01.txt at revision 1 and Test02.txt at revision 2.
Mercurial does not give any ID to files. This is different from some other systems, such as Bazaar, where each file (and directory) has a unique ID that follows the file throughout it's life time.
The structure in a Mercurial repository is as follows:
each entry in the changelog has a single pointer to
an entry in the manifest, which has a pointer per file to
an entry in the file's filelog
So if you add Test01.txt in revision 0, then you'll have a chain like this
changelog#0 -> manifest#0 -> Test01.txt#0
If you now rename and make a new commit, you will create a new changelog and manifest entry, and create a new filelog for Test02.txt:
changelog#1 -> manifest#1 -> Test02.txt#0
The new Test02.txt filelog entry will reference the Test01.txt entry. This is how Mercurial can keep track of renames:
$ hg debugdata Test02.txt 0
copy: Test01.txt
copyrev: 0936f74a58571dd87ad343cc3d6ae8434ad86fc4
test01
The best "file ID" you can talk about is therefore the ID of the first entry in the original file log. You can dig it out with hg debugindex:
$ hg debugindex Test01.txt
rev offset length base linkrev nodeid p1 p2
0 0 8 0 0 0936f74a5857 000000000000 000000000000
The "nodeid" column gives you the IDs for the revlog entries in the filelog for Test01.txt. Here we see that the first revision of the file has ID 0936f74a5857. This is just a short, 12 character prefix of the full 40 character SHA-1 hash. If you need the full hash, then read on...
The "linkrev" tells you that this version of the file is referenced by changeset 0. You can lookup the data in that changelog entry with hg debugdata -c 0, but for our purposes the normal hg log command also has the information:
$ hg log -r 0 --debug
changeset: 0:8e62ecaada0e5ba9efec234d0d9a66583347becf
phase: draft
parent: -1:0000000000000000000000000000000000000000
parent: -1:0000000000000000000000000000000000000000
manifest: 0:0537c846cd545da8f826b9d94fdb2fdae457bd07
user: Martin Geisler <mg#aragost.com>
date: Thu Feb 02 09:00:18 2012 +0100
files+: Test01.txt
extra: branch=default
description:
01
We're interested in the manifest ID. You can now look up the data in the correct manifest entry with:
$ hg debugdata -m 0537c846cd545da8f826b9d94fdb2fdae457bd07
Test01.txt0936f74a58571dd87ad343cc3d6ae8434ad86fc4
There is really a NUL byte between the file name and the filelog ID, but it's not visible in your terminal. You now have the full filelog ID for the first revision of the Test01.txt file.
You also need to go from Test02.txt to Test01.txt. You can use hg log --follow and hg debugrename for this: use hg log to get the revisions concerning the file, and use hg debugrename to see what the file was renamed from in each step.
Related
I am using hg convert to convert a Git repo into Mercurial on a Mac running macOS Mojave.
I've looked at similar questions How to use hg convert properly and hg convert not actually converting? but they haven't provided a definitive answer.
The command I've entered is:
hg convert ./my-src-dir/ my-dest-dir
The output is as follows:
initializing destination my-dest-dir repository
scanning source...
sorting...
converting...
6918 commit comment.
6917 commit comment.
6916 commit comment.
6915 commit comment.
6914 commit comment.
6913 commit comment.
This continues until the most recent commit...
1 commit comment.
0 commit comment.
updating bookmarks
I'm new to Mercurial. I can't see any errors but the destination directory only contains a .hg folder.
The output of hg sum is:
parent: -1:000000000000 (no revision checked out)
branch: default
commit: (clean)
update: 6919 new changesets (update)
phases: 6919 draft
Am I using this command correctly? I'm expecting to see all my src files in the new folder.
The key is:
parent: -1:000000000000 (no revision checked out)
The conversion presumably did work but you haven't yet updated your working folder to any particular changeset. So it appears to be empty.
Not knowing what (if any) branches, bookmarks, etc. are in the repo I can't say exactly what you should update to. But let's say you just want the last revision in there, you could do:
$ cd my-dest-dir
$ hg up tip
to get to whatever it thinks the last one is.
I am at revision 56, hash 6af16aa3edf8. Next revision will be 57, with hash ???. Is there a way to know the hash of revision 57? I need it in a pre-commit hook.
WHY THO?
I developed a script, called via pre-commit hook, that update some version files. That way, the compiled executables can give all info about the revision they are build from. I'm adding the revision number of the current commit in my version file, simply retrieved with "parent revision number + 1". Since the revision number is not reliable when collaborating with other people on the same repository, I prefer to add the hash, too. Don't know how to retrieve it...
No, you cannot predict the next hash even when you know its changeset completely. The commit time also plays a role there:
~/hg-test $ hg ci -m "b in foo"
~/hg-test $ hg id
d65d61e6898a tip
~/hg-test $ hg rollback
~/hg-test $ hg ci -m "b in foo"
~/hg-test $ hg id
c7f5ff744e43 tip
https://www.mercurial-scm.org/wiki/Nodeid
I suggest to solve your problem such:
In your build tools, query whether the project is built from a repository. If so: retrieve the repository information. E.g.
ver = $(hg log -r. -T"{node|short} from {date|isodate}")
will give you
c7f5ff744e43 from 2017-07-26 14:05 +0200
Generate the version file from that information in your build chain on the fly
For distribution purposes, generate and amend this file to the package, so that the build process, when it finds it is not started from a repository checkout, still has a version file it can make use of.
A number of changes have been made to a file in a branch. Some of the changes were for a feature that now needs to be implemented on the main branch.
How can I create a patch for each revision of the file so that I can choose which changes I wish to apply to my main branch? Ideally I would like each patch to contain the description of the revision too for reference purposes.
You can get a log for just one file by quoting that file in the log command:
hg log FILENAME
and you can get the diff for a certain revision and filename by giving both as argument to log, asking it to give you the patch (I've configured my hg to use the git-style patch format by default - maybe that's needed here, too):
hg log --patch --rev XXX FILENAME
if you want a patch for each revision FILENAME was changed, you could try in bash something like
for rev in $(hg log -T"{rev}\n" FILENAME); do hg log -p -r${rev} FILENAME > FILENAME.${rev}.patch; done
which yields you the typical patches, including the commit message used:
$ cat FILENAME.15.patch
Änderung: 15:441bead3e0b3
Vorgänger: 7:36479da8f266
Nutzer: planetmaker <email#example.org>
Datum: Tue Feb 10 22:58:24 2015 +0100
Zusammenfassung: More boo
diff --git a/FILENAME b/FILENAME
--- a/FILENAME
+++ b/FILENAME
## -1,1 +1,2 ##
foo is boo!
+Even moar booo!
Mercurial tracks the contents of a file throughout renames (hg mv $OLD $NEW), so that hg annotate $NEW also shows up the line-wise changes formerly made to $OLD with their original identification. That works fine.
BUT there seems no straightforward way to find out the name of the $OLD file, to which some given line has belonged within the ancestry of $NEW. hg annot $NEW -r$REV only works down to the rename changeset.
Of course the information is somehow accessible, e. g. by crawling through hg log (without --follow) and identifying the renames with some hg log -r$RENAMEREV -g -p (or by clicking through hg serve's web interface).
But this “workflow” is not only annoying and error-prone, but [most importantly] it isn't non-interactive/scriptable.
My question: Is there a way to get/construct either
some list of the file name history of $NEW (best with respective revision ranges), or
the name of the file in which line $LINE was commited (some kind of filename option for hg annot)?
Ideas in either the hg CLI or Python/hglib appreciated.
Either include the {file_copies} keyword in your hg log template:
$ hg init demo
$ cd demo
$ touch a
$ hg ci -Am 'file a'
adding a
$ hg mv a b
$ hg ci -Am 'moved to file b'
$ hg log -r . -T"{file_copies}\n"
b (a)
The built-in template status will include file copy info when you set the --copies flag:
$ hg log -r 1 -Tstatus --copies
changeset: 1:b37952faaddc
tag: tip
user: Martijn Pieters <mjpieters#fb.com>
date: Sun Jul 31 16:07:04 2016 +0100
summary: moved to file b
files:
A b
a
R a
So file b was taken from a.
See hg help template for more things you can include in log output.
Given a file name, how can I query Mercurial for the revision of the change set which introduced this file?
You want to work with revsets!
hg log -r "adds(filename.ext)"
The description from the help:
"adds(pattern)":
Changesets that add a file matching pattern.
The pattern without explicit kind like "glob:" is expected to be relative to the current directory and match against a file or a directory.
This should work:
hg log -r : -l 1 filepath
hg log -r : prints the history in reverse order, the -l 1 switch limits the changes displayed to the first one.