##// END OF EJS Templates
templatefuncs: add mailmap template function...
templatefuncs: add mailmap template function This commit adds a template function to support the .mailmap file in Mercurial repositories. The .mailmap file comes from git, and can be used to map new emails and names for old commits. The general use case is that someone may change their name or author commits under different emails and aliases, which would make these commits appear as though they came from different persons. The file allows you to specify the correct name that should be used in place of the author field specified in the commit. The mailmap file has 4 possible formats used to map old "commit" names to new "proper" names: 1. <proper@email.com> <commit@email.com> 2. Proper Name <commit@email.com> 3. Proper Name <proper@email.com> <commit@email.com> 4. Proper Name <proper@email.com> Commit Name <commit@email.com> Essentially there is a commit email present in each mailmap entry, that maps to either an updated name, email, or both. The final possible format allows commits authored by a person who used both an old name and an old email to map to a new name and email. To parse the file, we split by spaces and build a name out of every element that does not start with "<". Once we find an element that does start with "<" we concatenate all the name elements that preceded and add that as a parsed name. We then add the email as the first parsed email. We repeat the process until the end of the line, or a comment is found. We will be left with all parsed names in a list, and all parsed emails in a list, with the 0 index being the proper values and the 1 index being the commit values (if they were specified in the entry). The commit values are added as the keys to a dict, and with the proper fields as the values. The mapname function takes the mapping object and the commit author field and attempts to look for a corresponding entry. To do so we try (commit name, commit email) first, and if no results are returned then (None, commit email) is also looked up. This is due to format 4 from above, where someone may have a mailmap entry with both name and email, and if they don't it is possible they have an entry that uses only the commit email. Differential Revision: https://phab.mercurial-scm.org/D2904

File last commit:

r35583:07769a04 default
r37227:2a2ce93e default
Show More
filelog.py
139 lines | 4.3 KiB | text/x-python | PythonLexer
mpm@selenic.com
Break apart hg.py...
r1089 # filelog.py - file history class for mercurial
#
Thomas Arendsen Hein
Updated copyright notices and add "and others" to "hg version"
r4635 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
mpm@selenic.com
Break apart hg.py...
r1089 #
Martin Geisler
updated license to be explicit about GPL version 2
r8225 # This software may be used and distributed according to the terms of the
Matt Mackall
Update license to GPLv2+
r10263 # GNU General Public License version 2 or any later version.
mpm@selenic.com
Break apart hg.py...
r1089
Gregory Szorc
filelog: use absolute_import
r25948 from __future__ import absolute_import
import re
import struct
from . import (
error,
mdiff,
revlog,
)
mpm@selenic.com
Break apart hg.py...
r1089
Sune Foldager
filelog: extract metadata parsing and packing...
r14074 _mdre = re.compile('\1\n')
Mike Edgar
filelog: make parsemeta a public module function, to be used by censor module
r22421 def parsemeta(text):
Jun Wu
filelog: fix parsemeta docstring...
r32124 """return (metadatadict, metadatasize)"""
Sune Foldager
filelog: extract metadata parsing and packing...
r14074 # text can be buffer, so we can't use .startswith or .index
if text[:2] != '\1\n':
Mike Edgar
filelog: parsemeta stops returning unused key list...
r22422 return None, None
Sune Foldager
filelog: extract metadata parsing and packing...
r14074 s = _mdre.search(text, 2).start()
mtext = text[2:s]
meta = {}
for l in mtext.splitlines():
Matt Mackall
filelog: move metadata parsing to a helper function
r13240 k, v = l.split(": ", 1)
Sune Foldager
filelog: extract metadata parsing and packing...
r14074 meta[k] = v
Mike Edgar
filelog: parsemeta stops returning unused key list...
r22422 return meta, (s + 2)
Sune Foldager
filelog: extract metadata parsing and packing...
r14074
Mike Edgar
filelog: make packmeta a public module function, to be used by censor
r22420 def packmeta(meta, text):
Augie Fackler
python3: replace sorted(<dict>.iterkeys()) with sorted(<dict>)
r34025 keys = sorted(meta)
Mike Edgar
filelog: make packmeta a public module function, to be used by censor
r22420 metatext = "".join("%s: %s\n" % (k, meta[k]) for k in keys)
return "\1\n%s\1\n%s" % (metatext, text)
Matt Mackall
filelog: move metadata parsing to a helper function
r13240
Mike Edgar
filelog: raise CensoredNodeError when hash checks fail with censor metadata...
r22596 def _censoredtext(text):
m, offs = parsemeta(text)
Mike Edgar
filelog: allow censored files to contain padding data...
r24117 return m and "censored" in m
Mike Edgar
filelog: raise CensoredNodeError when hash checks fail with censor metadata...
r22596
Matt Mackall
revlog: kill from-style imports...
r7634 class filelog(revlog.revlog):
Matt Mackall
revlog: simplify revlog version handling...
r4258 def __init__(self, opener, path):
Durham Goode
filelog: use super() for calling base functions...
r19148 super(filelog, self).__init__(opener,
Benoit Boissinot
filelog encoding: move the encoding/decoding into store...
r8531 "/".join(("data", path + ".i")))
Matt Harbison
filelog: add the ability to report the user facing name...
r35583 # full name of the user visible file, relative to the repository root
self.filename = path
mpm@selenic.com
Break apart hg.py...
r1089
def read(self, node):
t = self.revision(node)
if not t.startswith('\1\n'):
return t
Benoit Boissinot
use __contains__, index or split instead of str.find...
r2579 s = t.index('\1\n', 2)
Matt Mackall
many, many trivial check-code fixups
r10282 return t[s + 2:]
mpm@selenic.com
Break apart hg.py...
r1089
def add(self, text, meta, transaction, link, p1=None, p2=None):
if meta or text.startswith('\1\n'):
Mike Edgar
filelog: make packmeta a public module function, to be used by censor
r22420 text = packmeta(meta, text)
mpm@selenic.com
Break apart hg.py...
r1089 return self.addrevision(text, transaction, link, p1, p2)
mpm@selenic.com
Add some rename debugging support
r1116 def renamed(self, node):
Matt Mackall
revlog: kill from-style imports...
r7634 if self.parents(node)[0] != revlog.nullid:
mpm@selenic.com
Add some rename debugging support
r1116 return False
Matt Mackall
filelog: move metadata parsing to a helper function
r13240 t = self.revision(node)
Mike Edgar
filelog: make parsemeta a public module function, to be used by censor module
r22421 m = parsemeta(t)[0]
Christian Ebert
Prefer i in d over d.has_key(i)
r5915 if m and "copy" in m:
Matt Mackall
revlog: kill from-style imports...
r7634 return (m["copy"], revlog.bin(m["copyrev"]))
mpm@selenic.com
Add some rename debugging support
r1116 return False
Matt Mackall
merge: use file size stored in revlog index...
r2898 def size(self, rev):
"""return the size of a given revision"""
# for revisions with renames, we have to go the slow way
node = self.node(rev)
if self.renamed(node):
return len(self.read(node))
Mike Edgar
revlog: add "iscensored()" to revlog public API...
r24118 if self.iscensored(rev):
Mike Edgar
filelog: censored files compare against empty data, have 0 size...
r22597 return 0
Matt Mackall
merge: use file size stored in revlog index...
r2898
Nicolas Dumazet
filelog: test behaviour for data starting with "\1\n"...
r11540 # XXX if self.read(node).startswith("\1\n"), this returns (size+4)
Durham Goode
filelog: use super() for calling base functions...
r19148 return super(filelog, self).size(rev)
Matt Mackall
merge: use file size stored in revlog index...
r2898
Matt Mackall
filelog: add hash-based comparisons...
r2887 def cmp(self, node, text):
Nicolas Dumazet
cmp: document the fact that we return True if content is different...
r11539 """compare text with a given file revision
returns True if text is different than what is stored.
"""
Matt Mackall
filelog: add hash-based comparisons...
r2887
Nicolas Dumazet
filelog: cmp: don't read data if hashes are identical (issue2273)...
r11541 t = text
if text.startswith('\1\n'):
t = '\1\n\1\n' + text
Durham Goode
filelog: use super() for calling base functions...
r19148 samehashes = not super(filelog, self).cmp(node, t)
Nicolas Dumazet
filelog: cmp: don't read data if hashes are identical (issue2273)...
r11541 if samehashes:
return False
Mike Edgar
filelog: censored files compare against empty data, have 0 size...
r22597 # censored files compare against the empty file
Mike Edgar
revlog: add "iscensored()" to revlog public API...
r24118 if self.iscensored(self.rev(node)):
Mike Edgar
filelog: censored files compare against empty data, have 0 size...
r22597 return text != ''
Nicolas Dumazet
filelog: cmp: don't read data if hashes are identical (issue2273)...
r11541 # renaming a file produces a different hash, even if the data
# remains unchanged. Check if it's the case (slow):
if self.renamed(node):
Matt Mackall
filelog: add hash-based comparisons...
r2887 t2 = self.read(node)
Matt Mackall
filelog.cmp: return 0 for equality...
r2895 return t2 != text
Matt Mackall
filelog: add hash-based comparisons...
r2887
Nicolas Dumazet
filelog: cmp: don't read data if hashes are identical (issue2273)...
r11541 return True
Sune Foldager
filelog: add file function to open other filelogs
r14287
Remi Chaintron
revlog: merge hash checking subfunctions...
r30584 def checkhash(self, text, node, p1=None, p2=None, rev=None):
Mike Edgar
filelog: raise CensoredNodeError when hash checks fail with censor metadata...
r22596 try:
Remi Chaintron
revlog: merge hash checking subfunctions...
r30584 super(filelog, self).checkhash(text, node, p1=p1, p2=p2, rev=rev)
Mike Edgar
filelog: raise CensoredNodeError when hash checks fail with censor metadata...
r22596 except error.RevlogError:
if _censoredtext(text):
Mike Edgar
changegroup: emit full-replacement deltas if either revision is censored...
r24190 raise error.CensoredNodeError(self.indexfile, node, text)
Mike Edgar
filelog: raise CensoredNodeError when hash checks fail with censor metadata...
r22596 raise
Mike Edgar
revlog: add "iscensored()" to revlog public API...
r24118 def iscensored(self, rev):
Mike Edgar
filelog: censored files compare against empty data, have 0 size...
r22597 """Check if a file revision is censored."""
Mike Edgar
filelog: use censored revlog flag bit to quickly check if a node is censored
r23858 return self.flags(rev) & revlog.REVIDX_ISCENSORED
Mike Edgar
revlog: addgroup checks if incoming deltas add censored revs, sets flag bit...
r24255
def _peek_iscensored(self, baserev, delta, flush):
"""Quickly check if a delta produces a censored revision."""
# Fragile heuristic: unless new file meta keys are added alphabetically
# preceding "censored", all censored revisions are prefixed by
# "\1\ncensored:". A delta producing such a censored revision must be a
# full-replacement delta, so we inspect the first and only patch in the
# delta for this prefix.
hlen = struct.calcsize(">lll")
if len(delta) <= hlen:
return False
oldlen = self.rawsize(baserev)
newlen = len(delta) - hlen
if delta[:hlen] != mdiff.replacediffheader(oldlen, newlen):
return False
add = "\1\ncensored:"
addlen = len(add)
return newlen >= addlen and delta[hlen:hlen + addlen] == add