Mercurial > hg > mercurial-crew-with-dirclash
view hgext/convert/hg.py @ 5483:0c43f87baba3 default tip
Fix file-changed-to-dir and dir-to-file commits (issue660).
Allow adding to dirstate files that clash with previously existing
but marked for removal. Protect from reintroducing clashes by revert.
This change doesn't address related issues with update. Current
workaround is to do "clean" update by manually removing conflicting
files/dirs from working directory.
author | Maxim Dounin <mdounin@mdounin.ru> |
---|---|
date | Sat, 27 Oct 2007 16:27:55 +0400 |
parents | 71e7c86adcb7 |
children |
line wrap: on
line source
# hg backend for convert extension # Note for hg->hg conversion: Old versions of Mercurial didn't trim # the whitespace from the ends of commit messages, but new versions # do. Changesets created by those older versions, then converted, may # thus have different hashes for changesets that are otherwise # identical. import os, time from mercurial.i18n import _ from mercurial.node import * from mercurial import hg, lock, revlog, util from common import NoRepo, commit, converter_source, converter_sink class mercurial_sink(converter_sink): def __init__(self, ui, path): converter_sink.__init__(self, ui, path) self.branchnames = ui.configbool('convert', 'hg.usebranchnames', True) self.clonebranches = ui.configbool('convert', 'hg.clonebranches', False) self.tagsbranch = ui.config('convert', 'hg.tagsbranch', 'default') self.lastbranch = None if os.path.isdir(path) and len(os.listdir(path)) > 0: try: self.repo = hg.repository(self.ui, path) ui.status(_('destination %s is a Mercurial repository\n') % path) except hg.RepoError, err: ui.print_exc() raise NoRepo(err.args[0]) else: try: ui.status(_('initializing destination %s repository\n') % path) self.repo = hg.repository(self.ui, path, create=True) self.created.append(path) except hg.RepoError, err: ui.print_exc() raise NoRepo("could not create hg repo %s as sink" % path) self.lock = None self.wlock = None self.filemapmode = False def before(self): self.wlock = self.repo.wlock() self.lock = self.repo.lock() self.repo.dirstate.clear() def after(self): self.repo.dirstate.invalidate() self.lock = None self.wlock = None def revmapfile(self): return os.path.join(self.path, ".hg", "shamap") def authorfile(self): return os.path.join(self.path, ".hg", "authormap") def getheads(self): h = self.repo.changelog.heads() return [ hex(x) for x in h ] def putfile(self, f, e, data): self.repo.wwrite(f, data, e) if f not in self.repo.dirstate: self.repo.dirstate.normallookup(f) def copyfile(self, source, dest): self.repo.copy(source, dest) def delfile(self, f): try: util.unlink(self.repo.wjoin(f)) #self.repo.remove([f]) except OSError: pass def setbranch(self, branch, pbranch, parents): if (not self.clonebranches) or (branch == self.lastbranch): return self.lastbranch = branch self.after() if not branch: branch = 'default' if not pbranch: pbranch = 'default' branchpath = os.path.join(self.path, branch) try: self.repo = hg.repository(self.ui, branchpath) except: if not parents: self.repo = hg.repository(self.ui, branchpath, create=True) else: self.ui.note(_('cloning branch %s to %s\n') % (pbranch, branch)) hg.clone(self.ui, os.path.join(self.path, pbranch), branchpath, rev=parents, update=False, stream=True) self.repo = hg.repository(self.ui, branchpath) self.before() def putcommit(self, files, parents, commit): seen = {} pl = [] for p in parents: if p not in seen: pl.append(p) seen[p] = 1 parents = pl nparents = len(parents) if self.filemapmode and nparents == 1: m1node = self.repo.changelog.read(bin(parents[0]))[0] parent = parents[0] if len(parents) < 2: parents.append("0" * 40) if len(parents) < 2: parents.append("0" * 40) p2 = parents.pop(0) text = commit.desc extra = commit.extra.copy() if self.branchnames and commit.branch: extra['branch'] = commit.branch if commit.rev: extra['convert_revision'] = commit.rev while parents: p1 = p2 p2 = parents.pop(0) a = self.repo.rawcommit(files, text, commit.author, commit.date, bin(p1), bin(p2), extra=extra) self.repo.dirstate.clear() text = "(octopus merge fixup)\n" p2 = hg.hex(self.repo.changelog.tip()) if self.filemapmode and nparents == 1: man = self.repo.manifest mnode = self.repo.changelog.read(bin(p2))[0] if not man.cmp(m1node, man.revision(mnode)): self.repo.rollback() self.repo.dirstate.clear() return parent return p2 def puttags(self, tags): try: old = self.repo.wfile(".hgtags").read() oldlines = old.splitlines(1) oldlines.sort() except: oldlines = [] k = tags.keys() k.sort() newlines = [] for tag in k: newlines.append("%s %s\n" % (tags[tag], tag)) newlines.sort() if newlines != oldlines: self.ui.status("updating tags\n") f = self.repo.wfile(".hgtags", "w") f.write("".join(newlines)) f.close() if not oldlines: self.repo.add([".hgtags"]) date = "%s 0" % int(time.mktime(time.gmtime())) extra = {} if self.tagsbranch != 'default': extra['branch'] = self.tagsbranch try: tagparent = self.repo.changectx(self.tagsbranch).node() except hg.RepoError, inst: tagparent = nullid self.repo.rawcommit([".hgtags"], "update tags", "convert-repo", date, tagparent, nullid) return hex(self.repo.changelog.tip()) def setfilemapmode(self, active): self.filemapmode = active class mercurial_source(converter_source): def __init__(self, ui, path, rev=None): converter_source.__init__(self, ui, path, rev) try: self.repo = hg.repository(self.ui, path) # try to provoke an exception if this isn't really a hg # repo, but some other bogus compatible-looking url self.repo.heads() except hg.RepoError: ui.print_exc() raise NoRepo("could not open hg repo %s as source" % path) self.lastrev = None self.lastctx = None self._changescache = None def changectx(self, rev): if self.lastrev != rev: self.lastctx = self.repo.changectx(rev) self.lastrev = rev return self.lastctx def getheads(self): if self.rev: return [hex(self.repo.changectx(self.rev).node())] else: return [hex(node) for node in self.repo.heads()] def getfile(self, name, rev): try: return self.changectx(rev).filectx(name).data() except revlog.LookupError, err: raise IOError(err) def getmode(self, name, rev): m = self.changectx(rev).manifest() return (m.execf(name) and 'x' or '') + (m.linkf(name) and 'l' or '') def getchanges(self, rev): ctx = self.changectx(rev) if self._changescache and self._changescache[0] == rev: m, a, r = self._changescache[1] else: m, a, r = self.repo.status(ctx.parents()[0].node(), ctx.node())[:3] changes = [(name, rev) for name in m + a + r] changes.sort() return (changes, self.getcopies(ctx, m + a)) def getcopies(self, ctx, files): copies = {} for name in files: try: copies[name] = ctx.filectx(name).renamed()[0] except TypeError: pass return copies def getcommit(self, rev): ctx = self.changectx(rev) parents = [hex(p.node()) for p in ctx.parents() if p.node() != nullid] return commit(author=ctx.user(), date=util.datestr(ctx.date()), desc=ctx.description(), parents=parents, branch=ctx.branch(), extra=ctx.extra()) def gettags(self): tags = [t for t in self.repo.tagslist() if t[0] != 'tip'] return dict([(name, hex(node)) for name, node in tags]) def getchangedfiles(self, rev, i): ctx = self.changectx(rev) i = i or 0 changes = self.repo.status(ctx.parents()[i].node(), ctx.node())[:3] if i == 0: self._changescache = (rev, changes) return changes[0] + changes[1] + changes[2]