def _readtaghist(ui, repo, lines, fn, recode=None, calcnodelines=False): '''Read tag definitions from a file (or any source of lines). This function returns two sortdicts with similar information: - the first dict, bintaghist, contains the tag information as expected by the _readtags function, i.e. a mapping from tag name to (node, hist): - node is the node id from the last line read for that name, - hist is the list of node ids previously associated with it (in file order). All node ids are binary, not hex. - the second dict, hextaglines, is a mapping from tag name to a list of [hexnode, line number] pairs, ordered from the oldest to the newest node. When calcnodelines is False the hextaglines dict is not calculated (an empty dict is returned). This is done to improve this function's performance in cases where the line numbers are not needed. ''' bintaghist = util.sortdict() hextaglines = util.sortdict() count = 0 def warn(msg): ui.warn(_("%s, line %s: %s\n") % (fn, count, msg)) for nline, line in enumerate(lines): count += 1 if not line: continue try: (nodehex, name) = line.split(" ", 1) except ValueError: warn(_("cannot parse entry")) continue name = name.strip() if recode: name = recode(name) try: nodebin = bin(nodehex) except TypeError: warn(_("node '%s' is not well formed") % nodehex) continue # update filetags if calcnodelines: # map tag name to a list of line numbers if name not in hextaglines: hextaglines[name] = [] hextaglines[name].append([nodehex, nline]) continue # map tag name to (node, hist) if name not in bintaghist: bintaghist[name] = [] bintaghist[name].append(nodebin) return bintaghist, hextaglines
def merge(repo, fcd, fco, fca): ''' Merge the tags of two revisions, taking into account the base tags Try to minimize the diff between the merged tags and the first parent tags ''' ui = repo.ui # read the p1, p2 and base tags # only keep the line numbers for the p1 tags p1tags = readtagsformerge(ui, repo, fcd.data().splitlines(), fn="p1 tags", keeplinenums=True) p2tags = readtagsformerge(ui, repo, fco.data().splitlines(), fn="p2 tags", keeplinenums=False) basetags = readtagsformerge(ui, repo, fca.data().splitlines(), fn="base tags", keeplinenums=False) # recover the list of "lost tags" (i.e. those that were found on the base # revision but not on one of the revisions being merged) basetagset = set(basetags) for n, pntags in enumerate((p1tags, p2tags)): pntagset = set(pntags) pnlosttagset = basetagset - pntagset for t in pnlosttagset: pntags[t] = basetags[t] if pntags[t][-1][0] != hexnullid: pntags[t].append([hexnullid, None]) conflictedtags = [] # for reporting purposes mergedtags = util.sortdict(p1tags) # sortdict does not implement iteritems() for tname, p2nodes in p2tags.items(): if tname not in mergedtags: mergedtags[tname] = p2nodes continue p1nodes = mergedtags[tname] mergednodes = singletagmerge(p1nodes, p2nodes) if mergednodes is None: conflictedtags.append(tname) continue mergedtags[tname] = mergednodes if conflictedtags: numconflicts = len(conflictedtags) ui.warn( _('automatic .hgtags merge failed\n' 'the following %d tags are in conflict: %s\n') % (numconflicts, ', '.join(sorted(conflictedtags)))) return True, 1 writemergedtags(repo, mergedtags) ui.note(_('.hgtags merged successfully\n')) return False, 0
def update(self, src): for s, n in src._unset: if s in self and n in self._data[s]: del self._data[s][n] del self._source[(s, n)] for s in src: if s not in self: self._data[s] = util.sortdict() self._data[s].update(src._data[s]) self._source.update(src._source)
def showextras(**args): """:extras: List of dicts with key, value entries of the 'extras' field of this changeset.""" extras = args['ctx'].extra() extras = util.sortdict((k, extras[k]) for k in sorted(extras)) makemap = lambda k: {'key': k, 'value': extras[k]} c = [makemap(k) for k in extras] f = _showlist('extra', c, plural='extras', **args) return _hybrid(f, extras, makemap, lambda x: '%s=%s' % (x['key'], x['value']))
def showfilecopiesswitch(**args): """:file_copies_switch: List of strings. Like "file_copies" but displayed only if the --copied switch is set. """ copies = args['revcache'].get('copies') or [] copies = util.sortdict(copies) makemap = lambda k: {'name': k, 'source': copies[k]} c = [makemap(k) for k in copies] f = _showlist('file_copy', c, plural='file_copies', **args) return _hybrid(f, copies, makemap, lambda x: '%s (%s)' % (x['name'], x['source']))
def merge(repo, fcd, fco, fca): ''' Merge the tags of two revisions, taking into account the base tags Try to minimize the diff between the merged tags and the first parent tags ''' ui = repo.ui # read the p1, p2 and base tags # only keep the line numbers for the p1 tags p1tags = readtagsformerge( ui, repo, fcd.data().splitlines(), fn="p1 tags", keeplinenums=True) p2tags = readtagsformerge( ui, repo, fco.data().splitlines(), fn="p2 tags", keeplinenums=False) basetags = readtagsformerge( ui, repo, fca.data().splitlines(), fn="base tags", keeplinenums=False) # recover the list of "lost tags" (i.e. those that were found on the base # revision but not on one of the revisions being merged) basetagset = set(basetags) for n, pntags in enumerate((p1tags, p2tags)): pntagset = set(pntags) pnlosttagset = basetagset - pntagset for t in pnlosttagset: pntags[t] = basetags[t] if pntags[t][-1][0] != hexnullid: pntags[t].append([hexnullid, None]) conflictedtags = [] # for reporting purposes mergedtags = util.sortdict(p1tags) # sortdict does not implement iteritems() for tname, p2nodes in p2tags.items(): if tname not in mergedtags: mergedtags[tname] = p2nodes continue p1nodes = mergedtags[tname] mergednodes = singletagmerge(p1nodes, p2nodes) if mergednodes is None: conflictedtags.append(tname) continue mergedtags[tname] = mergednodes if conflictedtags: numconflicts = len(conflictedtags) ui.warn(_('automatic .hgtags merge failed\n' 'the following %d tags are in conflict: %s\n') % (numconflicts, ', '.join(sorted(conflictedtags)))) return True, 1 writemergedtags(repo, mergedtags) ui.note(_('.hgtags merged successfully\n')) return False, 0
def showfilecopies(**args): """:file_copies: List of strings. Files copied in this changeset with their sources. """ cache, ctx = args['cache'], args['ctx'] copies = args['revcache'].get('copies') if copies is None: if 'getrenamed' not in cache: cache['getrenamed'] = getrenamedfn(args['repo']) copies = [] getrenamed = cache['getrenamed'] for fn in ctx.files(): rename = getrenamed(fn, ctx.rev()) if rename: copies.append((fn, rename[0])) copies = util.sortdict(copies) makemap = lambda k: {'name': k, 'source': copies[k]} c = [makemap(k) for k in copies] f = _showlist('file_copy', c, plural='file_copies', **args) return _hybrid(f, copies, makemap, lambda x: '%s (%s)' % (x['name'], x['source']))
def parse(self, src, data, sections=None, remap=None, include=None): sectionre = util.re.compile(r'\[([^\[]+)\]') itemre = util.re.compile(r'([^=\s][^=]*?)\s*=\s*(.*\S|)') contre = util.re.compile(r'\s+(\S|\S.*\S)\s*$') emptyre = util.re.compile(r'(;|#|\s*$)') commentre = util.re.compile(r'(;|#)') unsetre = util.re.compile(r'%unset\s+(\S+)') includere = util.re.compile(r'%include\s+(\S|\S.*\S)\s*$') section = "" item = None line = 0 cont = False for l in data.splitlines(True): line += 1 if line == 1 and l.startswith('\xef\xbb\xbf'): # Someone set us up the BOM l = l[3:] if cont: if commentre.match(l): continue m = contre.match(l) if m: if sections and section not in sections: continue v = self.get(section, item) + "\n" + m.group(1) self.set(section, item, v, "%s:%d" % (src, line)) continue item = None cont = False m = includere.match(l) if m: inc = util.expandpath(m.group(1)) base = os.path.dirname(src) inc = os.path.normpath(os.path.join(base, inc)) if include: try: include(inc, remap=remap, sections=sections) except IOError, inst: if inst.errno != errno.ENOENT: raise error.ParseError( _("cannot include %s (%s)") % (inc, inst.strerror), "%s:%s" % (src, line)) continue if emptyre.match(l): continue m = sectionre.match(l) if m: section = m.group(1) if remap: section = remap.get(section, section) if section not in self: self._data[section] = util.sortdict() continue m = itemre.match(l) if m: item = m.group(1) cont = True if sections and section not in sections: continue self.set(section, item, m.group(2), "%s:%d" % (src, line)) continue m = unsetre.match(l) if m: name = m.group(1) if sections and section not in sections: continue if self.get(section, name) is not None: del self._data[section][name] self._unset.append((section, name)) continue raise error.ParseError(l.rstrip(), ("%s:%s" % (src, line)))
def set(self, section, item, value, source=""): if section not in self: self._data[section] = util.sortdict() self._data[section][item] = value if source: self._source[(section, item)] = source
def parse(self, src, data, sections=None, remap=None, include=None): sectionre = util.re.compile(r'\[([^\[]+)\]') itemre = util.re.compile(r'([^=\s][^=]*?)\s*=\s*(.*\S|)') contre = util.re.compile(r'\s+(\S|\S.*\S)\s*$') emptyre = util.re.compile(r'(;|#|\s*$)') commentre = util.re.compile(r'(;|#)') unsetre = util.re.compile(r'%unset\s+(\S+)') includere = util.re.compile(r'%include\s+(\S|\S.*\S)\s*$') section = "" item = None line = 0 cont = False for l in data.splitlines(True): line += 1 if line == 1 and l.startswith('\xef\xbb\xbf'): # Someone set us up the BOM l = l[3:] if cont: if commentre.match(l): continue m = contre.match(l) if m: if sections and section not in sections: continue v = self.get(section, item) + "\n" + m.group(1) self.set(section, item, v, "%s:%d" % (src, line)) continue item = None cont = False m = includere.match(l) if m and include: expanded = util.expandpath(m.group(1)) includepaths = [os.path.dirname(src)] + self._includepaths for base in includepaths: inc = os.path.normpath(os.path.join(base, expanded)) try: include(inc, remap=remap, sections=sections) break except IOError as inst: if inst.errno != errno.ENOENT: raise error.ParseError(_("cannot include %s (%s)") % (inc, inst.strerror), "%s:%s" % (src, line)) continue if emptyre.match(l): continue m = sectionre.match(l) if m: section = m.group(1) if remap: section = remap.get(section, section) if section not in self: self._data[section] = util.sortdict() continue m = itemre.match(l) if m: item = m.group(1) cont = True if sections and section not in sections: continue self.set(section, item, m.group(2), "%s:%d" % (src, line)) continue m = unsetre.match(l) if m: name = m.group(1) if sections and section not in sections: continue if self.get(section, name) is not None: del self._data[section][name] self._unset.append((section, name)) continue raise error.ParseError(l.rstrip(), ("%s:%s" % (src, line)))