| # Copyright 2025 The Go Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style |
| # license that can be found in the LICENSE file. |
| |
| # Mercurial extension to add a 'goreposum' command that |
| # computes a hash of a remote repo's tag state. |
| # Tag definitions can come from the .hgtags file stored in |
| # any head of any branch, and the server protocol does not |
| # expose the tags directly. However, the protocol does expose |
| # the hashes of all the branch heads, so we can use a hash of |
| # all those branch names and heads as a conservative snapshot |
| # of the entire remote repo state, and use that as the tag sum. |
| # Any change on the server then invalidates the tag sum, |
| # even if it didn't have anything to do with tags, but at least |
| # we will avoid re-cloning a server when there have been no |
| # changes at all. |
| # |
| # Separately, this extension also adds a 'golookup' command that |
| # returns the hash of a specific reference, like 'default' or a tag. |
| # And golookup of a hash confirms that it still exists on the server. |
| # We can use that to revalidate that specific versions still exist and |
| # have the same meaning they did the last time we checked. |
| # |
| # Usage: |
| # |
| # hg --config "extensions.goreposum=$GOROOT/lib/hg/goreposum.py" goreposum REPOURL |
| |
| import base64, hashlib, sys |
| from mercurial import registrar, ui, hg, node |
| from mercurial.i18n import _ |
| cmdtable = {} |
| command = registrar.command(cmdtable) |
| @command(b'goreposum', [], _('url'), norepo=True) |
| def goreposum(ui, url): |
| """ |
| goreposum computes a checksum of all the named state in the remote repo. |
| It hashes together all the branch names and hashes |
| and then all the bookmark names and hashes. |
| Tags are stored in .hgtags files in any of the branches, |
| so the branch metadata includes the tags as well. |
| """ |
| h = hashlib.sha256() |
| peer = hg.peer(ui, {}, url) |
| for name, revs in peer.branchmap().items(): |
| h.update(name) |
| for r in revs: |
| h.update(b' ') |
| h.update(r) |
| h.update(b'\n') |
| if (b'bookmarks' in peer.listkeys(b'namespaces')): |
| for name, rev in peer.listkeys(b'bookmarks').items(): |
| h.update(name) |
| h.update(b'=') |
| h.update(rev) |
| h.update(b'\n') |
| print('r1:'+base64.standard_b64encode(h.digest()).decode('utf-8')) |
| |
| @command(b'golookup', [], _('url rev'), norepo=True) |
| def golookup(ui, url, rev): |
| """ |
| golookup looks up a single identifier in the repo, |
| printing its hash. |
| """ |
| print(node.hex(hg.peer(ui, {}, url).lookup(rev)).decode('utf-8')) |