diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py --- a/mercurial/localrepo.py +++ b/mercurial/localrepo.py @@ -166,37 +166,44 @@ class localrepository(object): return s = l.split(" ", 1) if len(s) != 2: - self.ui.warn(_("%s: ignoring invalid tag\n") % context) + self.ui.warn(_("%s: cannot parse entry\n") % context) return node, key = s + key = key.strip() try: bin_n = bin(node) except TypeError: - self.ui.warn(_("%s: ignoring invalid tag\n") % context) + self.ui.warn(_("%s: node '%s' is not well formed\n") % + (context, node)) return if bin_n not in self.changelog.nodemap: - self.ui.warn(_("%s: ignoring invalid tag\n") % context) + self.ui.warn(_("%s: tag '%s' refers to unknown node\n") % + (context, key)) return - self.tagscache[key.strip()] = bin_n + self.tagscache[key] = bin_n - # read each head of the tags file, ending with the tip + # read the tags file from each head, ending with the tip, # and add each tag found to the map, with "newer" ones # taking precedence + heads = self.heads() + heads.reverse() fl = self.file(".hgtags") - h = fl.heads() - h.reverse() - for r in h: + for node in heads: + change = self.changelog.read(node) + rev = self.changelog.rev(node) + fn, ff = self.manifest.find(change[0], '.hgtags') + if fn is None: continue count = 0 - for l in fl.read(r).splitlines(): + for l in fl.read(fn).splitlines(): count += 1 - parsetag(l, ".hgtags:%d" % count) - + parsetag(l, _(".hgtags (rev %d:%s), line %d") % + (rev, short(node), count)) try: f = self.opener("localtags") count = 0 for l in f: count += 1 - parsetag(l, "localtags:%d" % count) + parsetag(l, _("localtags, line %d") % count) except IOError: pass diff --git a/mercurial/manifest.py b/mercurial/manifest.py --- a/mercurial/manifest.py +++ b/mercurial/manifest.py @@ -43,48 +43,61 @@ class manifest(revlog): def diff(self, a, b): return mdiff.textdiff(str(a), str(b)) + def _search(self, m, s, lo=0, hi=None): + '''return a tuple (start, end) that says where to find s within m. + + If the string is found m[start:end] are the line containing + that string. If start == end the string was not found and + they indicate the proper sorted insertion point. This was + taken from bisect_left, and modified to find line start/end as + it goes along. + + m should be a buffer or a string + s is a string''' + def advance(i, c): + while i < lenm and m[i] != c: + i += 1 + return i + lenm = len(m) + if not hi: + hi = lenm + while lo < hi: + mid = (lo + hi) // 2 + start = mid + while start > 0 and m[start-1] != '\n': + start -= 1 + end = advance(start, '\0') + if m[start:end] < s: + # we know that after the null there are 40 bytes of sha1 + # this translates to the bisect lo = mid + 1 + lo = advance(end + 40, '\n') + 1 + else: + # this translates to the bisect hi = mid + hi = start + end = advance(lo, '\0') + found = m[lo:end] + if cmp(s, found) == 0: + # we know that after the null there are 40 bytes of sha1 + end = advance(end + 40, '\n') + return (lo, end+1) + else: + return (lo, lo) + + def find(self, node, f): + '''look up entry for a single file efficiently. + return (node, flag) pair if found, (None, None) if not.''' + if self.mapcache and node == self.mapcache[0]: + return self.mapcache[1].get(f), self.mapcache[2].get(f) + text = self.revision(node) + start, end = self._search(text, f) + if start == end: + return None, None + l = text[start:end] + f, n = l.split('\0') + return bin(n[:40]), n[40:-1] == 'x' + def add(self, map, flags, transaction, link, p1=None, p2=None, changed=None): - - # returns a tuple (start, end). If the string is found - # m[start:end] are the line containing that string. If start == end - # the string was not found and they indicate the proper sorted - # insertion point. This was taken from bisect_left, and modified - # to find line start/end as it goes along. - # - # m should be a buffer or a string - # s is a string - # - def manifestsearch(m, s, lo=0, hi=None): - def advance(i, c): - while i < lenm and m[i] != c: - i += 1 - return i - lenm = len(m) - if not hi: - hi = lenm - while lo < hi: - mid = (lo + hi) // 2 - start = mid - while start > 0 and m[start-1] != '\n': - start -= 1 - end = advance(start, '\0') - if m[start:end] < s: - # we know that after the null there are 40 bytes of sha1 - # this translates to the bisect lo = mid + 1 - lo = advance(end + 40, '\n') + 1 - else: - # this translates to the bisect hi = mid - hi = start - end = advance(lo, '\0') - found = m[lo:end] - if cmp(s, found) == 0: - # we know that after the null there are 40 bytes of sha1 - end = advance(end + 40, '\n') - return (lo, end+1) - else: - return (lo, lo) - # apply the changes collected during the bisect loop to our addlist # return a delta suitable for addrevision def addlistdelta(addlist, x): @@ -137,7 +150,7 @@ class manifest(revlog): for w in work: f = w[0] # bs will either be the index of the item or the insert point - start, end = manifestsearch(addbuf, f, start) + start, end = self._search(addbuf, f, start) if w[1] == 0: l = "%s\000%s%s\n" % (f, hex(map[f]), flags[f] and "x" or '') diff --git a/tests/test-tags b/tests/test-tags --- a/tests/test-tags +++ b/tests/test-tags @@ -32,12 +32,31 @@ hg id hg status hg commit -m "merge" -d "1000000 0" + +# create fake head, make sure tag not visible afterwards +cp .hgtags tags +hg tag -d "1000000 0" last +hg rm .hgtags +hg commit -m "remove" -d "1000000 0" + +mv tags .hgtags +hg add .hgtags +hg commit -m "readd" -d "1000000 0" + +hg tags + # invalid tags echo "spam" >> .hgtags echo >> .hgtags echo "foo bar" >> .hgtags echo "$T invalid" | sed "s/..../a5a5/" >> .hg/localtags hg commit -m "tags" -d "1000000 0" + +# report tag parse error on other head +hg up 3 +echo 'x y' >> .hgtags +hg commit -m "head" -d "1000000 0" + hg tags hg tip diff --git a/tests/test-tags.out b/tests/test-tags.out --- a/tests/test-tags.out +++ b/tests/test-tags.out @@ -16,17 +16,26 @@ 1 files updated, 0 files merged, 0 files (branch merge, don't forget to commit) 8216907a933d+8a3ca90d111d+ tip M .hgtags -.hgtags:2: ignoring invalid tag -.hgtags:4: ignoring invalid tag -localtags:1: ignoring invalid tag -tip 4:fd868a874787a7b5af31e1675666ce691c803035 +tip 6:c6af9d771a81bb9c7f267ec03491224a9f8ba1cd first 0:0acdaf8983679e0aac16e811534eb49d7ee1f2b4 -changeset: 4:fd868a874787 -.hgtags:2: ignoring invalid tag -.hgtags:4: ignoring invalid tag -localtags:1: ignoring invalid tag +.hgtags (rev 7:39bba1bbbc4c), line 2: cannot parse entry +.hgtags (rev 7:39bba1bbbc4c), line 4: node 'foo' is not well formed +localtags, line 1: tag 'invalid' refers to unknown node +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +.hgtags (rev 7:39bba1bbbc4c), line 2: cannot parse entry +.hgtags (rev 7:39bba1bbbc4c), line 4: node 'foo' is not well formed +.hgtags (rev 8:4ca6f1b1a68c), line 2: node 'x' is not well formed +localtags, line 1: tag 'invalid' refers to unknown node +tip 8:4ca6f1b1a68c77be687a03aaeb1614671ba59b20 +first 0:0acdaf8983679e0aac16e811534eb49d7ee1f2b4 +changeset: 8:4ca6f1b1a68c +.hgtags (rev 7:39bba1bbbc4c), line 2: cannot parse entry +.hgtags (rev 7:39bba1bbbc4c), line 4: node 'foo' is not well formed +.hgtags (rev 8:4ca6f1b1a68c), line 2: node 'x' is not well formed +localtags, line 1: tag 'invalid' refers to unknown node tag: tip +parent: 3:b2ef3841386b user: test date: Mon Jan 12 13:46:40 1970 +0000 -summary: tags +summary: head