Mercurial repository identification
Asked Answered
D

2

5

I need to be able to uniquely identify a Mercurial repository and have that identifier placed in a file that is included when cloned. If I can put the identifier in a file in the .hg folder that is preferable to simply adding a normal file to the repo.

I understand that I can get a near certain identifier from the first changes that are committed. I know that the hgrc file cannot be used to store the identifier, because it is not cloned.

So, my question is: Is there another file in the .hg folder that is cloned that I can use to put the identifier? Thanks.

Duron answered 10/6, 2011 at 13:57 Comment(1)
I really have ask: What is the purpose of the identifier? How is supposed to be used?Helpful
H
9

From first read, it sounds like you want to be able to make sure that a clone of the repository is a clone of the correct repository and not some stand-in impostor. However, if the identification information you're thinking of using is cloned with everything else, then an impostor would still pass this test. You'd need to keep that identifier separate so that it can be compared against information in the clone.

Whether that is your purpose or not, any file in .hg that is cloned you may not want to edit. You'd have to add a file to be tracked in the other areas of the repo, outside of .hg. However, you don't really need an extra file at all, as the changeset hash is not just near certain, but very certain, so the information for handily identifying a repository is built-in to the repository itself.

On the commandline, you can get either the short or full versions of the very first changeset's hash identifier:

> hg id -i -r0
89abf5502e3c

> hg log -r0 --template "{node}"
89abf5502e3c5c65e532db04d8d87141f0ac8b73

If I am correct about your desire to compare 2 identifiers so that you or someone else knows a clone of the repository is a true clone and not a false clone, you would have the same changset id available separately so that someone can use one of the above commands to see the id of their clone and compare it to what you say it should be. This is much like how many websites with downloadable executable files show a hash identifier next to the download link so that you can hash the file yourself and compare the result to the hash on the website.

Edit regarding your comment that sheds light on the purpose of this:

Since you need to be able to read it from a file, there are a couple options:

Tracked file in repository root

There is one file you might consider, other than creating your own: .hgtags.

hg tag -r0 ident

...would tag the very first revision, allowing you to use ident as a reference to that changeset rather than -r0. Mercurial always uses tag information from the latest version of .hgtags, no matter what changeset the working directory is updated to, but that may not matter to your app. hg tag appends a line such as this to the .hgtags file, creating the file if it doesn't exist:

a247494248c4b96a571bbd12e90eade3bf559281 ident

This is most handy if you don't have a tags files yet in your repos, because it will be the first line in the file for easy finding. You might think could simply write this file yourself, but then you'd still have to call hg to get the changeset id and again at some point for adding it to tracking and then committing: hg tag does all that for you.

If there is already the possibility of a tags file to consider, that's ok, too, because they tend to be relatively short and you just need to look for the 1 line that ends with your chosen tag name. Mercurial is designed for append-only operations to .hgtags, but everything would still work fine if you inserted the line for this tag as the very first line if .hgtags already exists because: 1. The tag will never be moved or removed. 2. You'll be using a tag name not already used in the file.

Reading hg's guts

There are files that normally only Mercurial itself touches deeper in .hg that can be read to get the first changeset's hash. I looked into Mercurial's File Formats, Revlog, and RevlogNG, and at least for 2 of my own repos, .hg\store\00changelog.i contains the first changeset's hash at offset 0x20 (20 byte length). Probably, at least since Mercurial 0.9, it will be the same in all repos. RevlogNG also notes the first 4 bytes of that file will indicate Revlog version number and flags. While the changeset id is only 20 bytes long currently, the actual field for it is 32 bytes long, probably for future expansion to a longer hash.

Since this option requires no alteration of existing repositories and only involves reading the first 52-64 bytes of the main index, it's the one I'd probably go with. If I was catching this requirement in the early stages of the product before any repos it manages were out in the wild, I would lean toward the custom file approach because I would probably have my own metadata file created and added from the beginning of the repo.

Helpful answered 10/6, 2011 at 14:40 Comment(6)
If you want to ensure this is an exact clone, you ideally should compare all the changeset IDs (up until the tip in the parent repo, at least) — modifications to later changesets won't affect r0's ID.Radiosonde
@Cat Plus Plus: I know, but I'm not even sure the OP is after any particular level of security. I speculated. And if I wanted to make sure a repository was authoritative for cloning from, I'd put it on bitbucket, use a strong password changed frequently, and disallow public forks, etc.Helpful
Comparing only the heads uniquely identifies the repo, as every hash includes the hash of its ancestors.Fantasist
We want to be able to locate the 'matching' repo on a USB drive, where we can't predict the drive letter, or how many repos may be on it. The folder that contains the repo cannot be used to determine the correct repo, as it may not match that on the local computer. Our application is driving Mercurial for our users, and they don't really know our app uses it. It is costly to do these out of process calls, so we would like to minimize the number of times we do it, and store the hash. We can then have our app read the file to get the hash.Duron
@OP: I updated with some info you'll probably like. Sounds like an interesting project.Helpful
@Joel B Fant: Thanks. Looks like you gave me a couple ways to do what we want.Duron
S
0

error: repository is unrelated message come from mercurial/treediscovery.py:

base = list(base)
if base == [nullid]:
    if force:
        repo.ui.warn(_("warning: repository is unrelated\n"))
    else:
        raise util.Abort(_("repository is unrelated"))

base variable store last common parts of two repositories. By giving this idea of push/pull checks we may assume that repositories are related if they have common roots, so check hashes from command:

$ hg log -r "roots(all())"

For unknown to me reason hg log -r 0 always shown same root, but you may have situation that FIRST_REPO hold SECOND_REPO history, but obviously 0 revs of SECOND_REPO different from FIRST_REPO but Mercurial check is passed.

You may not trick roots checking by carefully crafting repositories because building two repositories looks like these (with common parts but different roots):

0 <--- SHA-256-XXX <--- SHA-256-YYY <--- SHA-256-ZZZ
0 <--- SHA-256-YYY <--- SHA-256-ZZZ

impossible because that mean you reverse SHA-256 as each subsequent hash depends on previous values.

Snowmobile answered 23/10, 2015 at 10:16 Comment(1)
See also #7313678Snowmobile

© 2022 - 2024 — McMap. All rights reserved.