Mercurial > hg
changeset 2952:6ba3409f9725
merge.
author | Vadim Gelfer <vadim.gelfer@gmail.com> |
---|---|
date | Fri, 18 Aug 2006 17:02:38 -0700 |
parents | 5ddf7d305a27 (current diff) f7bed40e259a (diff) |
children | 51ba31494c69 |
files | mercurial/patch.py |
diffstat | 13 files changed, 417 insertions(+), 201 deletions(-) [+] |
line wrap: on
line diff
--- a/CONTRIBUTORS Thu Aug 17 21:13:35 2006 +0300 +++ b/CONTRIBUTORS Fri Aug 18 17:02:38 2006 -0700 @@ -4,6 +4,7 @@ Muli Ben-Yehuda <mulix at mulix.org> Mikael Berthe <mikael at lilotux.net> Benoit Boissinot <bboissin at gmail.com> +Brendan Cully <brendan at kublai.com> Vincent Danjean <vdanjean.ml at free.fr> Jake Edge <jake at edge2.net> Michael Fetterman <michael.fetterman at intel.com>
--- a/hgext/mq.py Thu Aug 17 21:13:35 2006 +0300 +++ b/hgext/mq.py Fri Aug 18 17:02:38 2006 -0700 @@ -252,6 +252,9 @@ for line in file(pf): line = line.rstrip() + if line.startswith('diff --git'): + diffstart = 2 + break if diffstart: if line.startswith('+++ '): diffstart = 2 @@ -298,8 +301,10 @@ return (message, comments, user, date, diffstart > 1) def printdiff(self, repo, node1, node2=None, files=None, - fp=None, changes=None, opts=None): - patch.diff(repo, node1, node2, files, + fp=None, changes=None, opts={}): + fns, matchfn, anypats = cmdutil.matchpats(repo, files, opts) + + patch.diff(repo, node1, node2, fns, match=matchfn, fp=fp, changes=changes, opts=self.diffopts()) def mergeone(self, repo, mergeq, head, patch, rev, wlock): @@ -408,7 +413,7 @@ self.ui.warn("patch failed, unable to continue (try -v)\n") return (False, [], False) - return (True, files.keys(), fuzz) + return (True, files, fuzz) def apply(self, repo, series, list=False, update_status=True, strict=False, patchdir=None, merge=None, wlock=None): @@ -421,42 +426,37 @@ lock = repo.lock() tr = repo.transaction() n = None - for patch in series: - pushable, reason = self.pushable(patch) + for patchname in series: + pushable, reason = self.pushable(patchname) if not pushable: - self.explain_pushable(patch, all_patches=True) + self.explain_pushable(patchname, all_patches=True) continue - self.ui.warn("applying %s\n" % patch) - pf = os.path.join(patchdir, patch) + self.ui.warn("applying %s\n" % patchname) + pf = os.path.join(patchdir, patchname) try: - message, comments, user, date, patchfound = self.readheaders(patch) + message, comments, user, date, patchfound = self.readheaders(patchname) except: - self.ui.warn("Unable to read %s\n" % pf) + self.ui.warn("Unable to read %s\n" % patchname) err = 1 break if not message: - message = "imported patch %s\n" % patch + message = "imported patch %s\n" % patchname else: if list: - message.append("\nimported patch %s" % patch) + message.append("\nimported patch %s" % patchname) message = '\n'.join(message) (patcherr, files, fuzz) = self.patch(repo, pf) patcherr = not patcherr - if merge and len(files) > 0: + if merge and files: # Mark as merged and update dirstate parent info - repo.dirstate.update(repo.dirstate.filterfiles(files), 'm') + repo.dirstate.update(repo.dirstate.filterfiles(files.keys()), 'm') p1, p2 = repo.dirstate.parents() repo.dirstate.setparents(p1, merge) - if len(files) > 0: - cwd = repo.getcwd() - cfiles = files - if cwd: - cfiles = [util.pathto(cwd, f) for f in files] - cmdutil.addremove(repo, cfiles, wlock=wlock) + files = patch.updatedir(self.ui, repo, files, wlock=wlock) n = repo.commit(files, message, user, date, force=1, lock=lock, wlock=wlock) @@ -464,11 +464,11 @@ raise util.Abort(_("repo commit failed")) if update_status: - self.applied.append(statusentry(revlog.hex(n), patch)) + self.applied.append(statusentry(revlog.hex(n), patchname)) if patcherr: if not patchfound: - self.ui.warn("patch %s is empty\n" % patch) + self.ui.warn("patch %s is empty\n" % patchname) err = 0 else: self.ui.warn("patch failed, rejects left in working dir\n") @@ -904,15 +904,15 @@ else: self.ui.write("Patch queue now empty\n") - def diff(self, repo, files): + def diff(self, repo, pats, opts): top = self.check_toppatch(repo) if not top: self.ui.write("No patches applied\n") return qp = self.qparents(repo, top) - self.printdiff(repo, qp, files=files) + self.printdiff(repo, qp, files=pats, opts=opts) - def refresh(self, repo, msg='', short=False): + def refresh(self, repo, pats=None, **opts): if len(self.applied) == 0: self.ui.write("No patches applied\n") return @@ -925,7 +925,7 @@ message, comments, user, date, patchfound = self.readheaders(patch) patchf = self.opener(patch, "w") - msg = msg.rstrip() + msg = opts.get('msg', '').rstrip() if msg: if comments: # Remove existing message. @@ -939,6 +939,7 @@ comments = "\n".join(comments) + '\n\n' patchf.write(comments) + fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts) tip = repo.changelog.tip() if top == tip: # if the top of our patch queue is also the tip, there is an @@ -956,7 +957,7 @@ # caching against the next repo.status call # mm, aa, dd, aa2, uu = repo.status(patchparent, tip)[:5] - if short: + if opts.get('short'): filelist = mm + aa + dd else: filelist = None @@ -992,16 +993,27 @@ m = list(util.unique(mm)) r = list(util.unique(dd)) a = list(util.unique(aa)) - filelist = list(util.unique(m + r + a)) + filelist = filter(matchfn, util.unique(m + r + a)) self.printdiff(repo, patchparent, files=filelist, changes=(m, a, r, [], u), fp=patchf) patchf.close() changes = repo.changelog.read(tip) repo.dirstate.setparents(*cparents) + copies = [(f, repo.dirstate.copied(f)) for f in a] repo.dirstate.update(a, 'a') + for dst, src in copies: + repo.dirstate.copy(src, dst) repo.dirstate.update(r, 'r') + # if the patch excludes a modified file, mark that file with mtime=0 + # so status can see it. + mm = [] + for i in range(len(m)-1, -1, -1): + if not matchfn(m[i]): + mm.append(m[i]) + del m[i] repo.dirstate.update(m, 'n') + repo.dirstate.update(mm, 'n', st_mtime=0) repo.dirstate.forget(forget) if not msg: @@ -1216,7 +1228,7 @@ if not self.ui.verbose: p = pname else: - p = str(self.series.index(pname)) + " " + p + p = str(self.series.index(pname)) + " " + pname return p def top(self, repo): @@ -1411,17 +1423,24 @@ changes unless -f is specified, in which case the patch will be initialised with them. - -m or -l set the patch header as well as the commit message. - If neither is specified, the patch header is empty and the + -e, -m or -l set the patch header as well as the commit message. + If none is specified, the patch header is empty and the commit message is 'New patch: PATCH'""" q = repo.mq message = commands.logmessage(opts) + if opts['edit']: + message = ui.edit(message, ui.username()) q.new(repo, patch, msg=message, force=opts['force']) q.save_dirty() return 0 -def refresh(ui, repo, **opts): - """update the current patch""" +def refresh(ui, repo, *pats, **opts): + """update the current patch + + If any file patterns are provided, the refreshed patch will contain only + the modifications that match those patterns; the remaining modifications + will remain in the working directory. + """ q = repo.mq message = commands.logmessage(opts) if opts['edit']: @@ -1430,14 +1449,13 @@ patch = q.applied[-1].name (message, comment, user, date, hasdiff) = q.readheaders(patch) message = ui.edit('\n'.join(message), user or ui.username()) - q.refresh(repo, msg=message, short=opts['short']) + q.refresh(repo, pats, msg=message, **opts) q.save_dirty() return 0 -def diff(ui, repo, *files, **opts): +def diff(ui, repo, *pats, **opts): """diff of the current patch""" - # deep in the dirstate code, the walkhelper method wants a list, not a tuple - repo.mq.diff(repo, list(files)) + repo.mq.diff(repo, pats, opts) return 0 def fold(ui, repo, *files, **opts): @@ -1469,20 +1487,21 @@ patches = [] messages = [] for f in files: - patch = q.lookup(f) - if patch in patches or patch == parent: - ui.warn(_('Skipping already folded patch %s') % patch) - if q.isapplied(patch): - raise util.Abort(_('qfold cannot fold already applied patch %s') % patch) - patches.append(patch) + p = q.lookup(f) + if p in patches or p == parent: + ui.warn(_('Skipping already folded patch %s') % p) + if q.isapplied(p): + raise util.Abort(_('qfold cannot fold already applied patch %s') % p) + patches.append(p) - for patch in patches: + for p in patches: if not message: - messages.append(q.readheaders(patch)[0]) - pf = q.join(patch) + messages.append(q.readheaders(p)[0]) + pf = q.join(p) (patchsuccess, files, fuzz) = q.patch(repo, pf) if not patchsuccess: - raise util.Abort(_('Error folding patch %s') % patch) + raise util.Abort(_('Error folding patch %s') % p) + patch.updatedir(ui, repo, files) if not message: message, comments, user = q.readheaders(parent)[0:3] @@ -1495,29 +1514,26 @@ message = ui.edit(message, user or ui.username()) q.refresh(repo, msg=message) - - for patch in patches: - q.delete(repo, patch, keep=opts['keep']) - + q.delete(repo, patches, keep=opts['keep']) q.save_dirty() def guard(ui, repo, *args, **opts): '''set or print guards for a patch - guards control whether a patch can be pushed. a patch with no - guards is aways pushed. a patch with posative guard ("+foo") is - pushed only if qselect command enables guard "foo". a patch with - nagative guard ("-foo") is never pushed if qselect command enables - guard "foo". + Guards control whether a patch can be pushed. A patch with no + guards is always pushed. A patch with a positive guard ("+foo") is + pushed only if the qselect command has activated it. A patch with + a negative guard ("-foo") is never pushed if the qselect command + has activated it. - with no arguments, default is to print current active guards. - with arguments, set active guards for patch. + With no arguments, print the currently active guards. + With arguments, set guards for the named patch. - to set nagative guard "-foo" on topmost patch ("--" is needed so - hg will not interpret "-foo" as argument): + To set a negative guard "-foo" on topmost patch ("--" is needed so + hg will not interpret "-foo" as an option): hg qguard -- -foo - to set guards on other patch: + To set guards on another patch: hg qguard other.patch +2.6.17 -stable ''' def status(idx): @@ -1723,32 +1739,34 @@ def select(ui, repo, *args, **opts): '''set or print guarded patches to push - use qguard command to set or print guards on patch. then use - qselect to tell mq which guards to use. example: + Use the qguard command to set or print guards on patch, then use + qselect to tell mq which guards to use. A patch will be pushed if it + has no guards or any positive guards match the currently selected guard, + but will not be pushed if any negative guards match the current guard. + For example: - qguard foo.patch -stable (nagative guard) - qguard bar.patch +stable (posative guard) + qguard foo.patch -stable (negative guard) + qguard bar.patch +stable (positive guard) qselect stable - this sets "stable" guard. mq will skip foo.patch (because it has - nagative match) but push bar.patch (because it has posative - match). patch is pushed if any posative guards match and no - nagative guards match. + This activates the "stable" guard. mq will skip foo.patch (because + it has a negative match) but push bar.patch (because it + has a positive match). - with no arguments, default is to print current active guards. - with arguments, set active guards as given. + With no arguments, prints the currently active guards. + With one argument, sets the active guard. - use -n/--none to deactivate guards (no other arguments needed). - when no guards active, patches with posative guards are skipped, - patches with nagative guards are pushed. + Use -n/--none to deactivate guards (no other arguments needed). + When no guards are active, patches with positive guards are skipped + and patches with negative guards are pushed. - qselect can change guards of applied patches. it does not pop - guarded patches by default. use --pop to pop back to last applied - patch that is not guarded. use --reapply (implies --pop) to push - back to current patch afterwards, but skip guarded patches. + qselect can change the guards on applied patches. It does not pop + guarded patches by default. Use --pop to pop back to the last applied + patch that is not guarded. Use --reapply (which implies --pop) to push + back to the current patch afterwards, but skip guarded patches. - use -s/--series to print list of all guards in series file (no - other arguments needed). use -v for more information.''' + Use -s/--series to print a list of all guards in the series file (no + other arguments needed). Use -v for more information.''' q = repo.mq guards = q.active() @@ -1885,7 +1903,10 @@ (commit, commands.table["^commit|ci"][1], 'hg qcommit [OPTION]... [FILE]...'), - "^qdiff": (diff, [], 'hg qdiff [FILE]...'), + "^qdiff": (diff, + [('I', 'include', [], _('include names matching the given patterns')), + ('X', 'exclude', [], _('exclude names matching the given patterns'))], + 'hg qdiff [-I] [-X] [FILE]...'), "qdelete|qremove|qrm": (delete, [('k', 'keep', None, _('keep patch file'))], @@ -1914,10 +1935,11 @@ 'hg qinit [-c]'), "qnew": (new, - [('m', 'message', '', _('use <text> as commit message')), + [('e', 'edit', None, _('edit commit message')), + ('m', 'message', '', _('use <text> as commit message')), ('l', 'logfile', '', _('read the commit message from <file>')), ('f', 'force', None, _('import uncommitted changes into patch'))], - 'hg qnew [-m TEXT] [-l FILE] [-f] PATCH'), + 'hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH'), "qnext": (next, [], 'hg qnext'), "qprev": (prev, [], 'hg qprev'), "^qpop": @@ -1939,8 +1961,10 @@ [('e', 'edit', None, _('edit commit message')), ('m', 'message', '', _('change commit message with <text>')), ('l', 'logfile', '', _('change commit message with <file> content')), - ('s', 'short', None, 'short refresh')], - 'hg qrefresh [-e] [-m TEXT] [-l FILE] [-s]'), + ('s', 'short', None, 'short refresh'), + ('I', 'include', [], _('include names matching the given patterns')), + ('X', 'exclude', [], _('exclude names matching the given patterns'))], + 'hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] FILES...'), 'qrename|qmv': (rename, [], 'hg qrename PATCH1 [PATCH2]'), "qrestore":
--- a/mercurial/commands.py Thu Aug 17 21:13:35 2006 +0300 +++ b/mercurial/commands.py Fri Aug 18 17:02:38 2006 -0700 @@ -1526,7 +1526,6 @@ if st == 'window': incrementing = rev matches.clear() - copies.clear() elif st == 'add': change = repo.changelog.read(repo.lookup(str(rev))) mf = repo.manifest.read(change[0]) @@ -1535,20 +1534,19 @@ if fn in skip: continue fstate.setdefault(fn, {}) - copies.setdefault(rev, {}) try: grepbody(fn, rev, getfile(fn).read(mf[fn])) if follow: copied = getfile(fn).renamed(mf[fn]) if copied: - copies[rev][fn] = copied[0] + copies.setdefault(rev, {})[fn] = copied[0] except KeyError: pass elif st == 'iter': states = matches[rev].items() states.sort() for fn, m in states: - copy = copies[rev].get(fn) + copy = copies.get(rev, {}).get(fn) if fn in skip: if copy: skip[copy] = True @@ -1571,7 +1569,7 @@ for fn, state in fstate: if fn in skip: continue - if fn not in copies[prev[fn]]: + if fn not in copies.get(prev[fn], {}): display(fn, rev, {}, state) return (count == 0 and 1) or 0 @@ -1683,44 +1681,7 @@ ui.debug(_('message:\n%s\n') % message) files, fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root) - removes = [] - if len(files) > 0: - cfiles = files.keys() - copies = [] - copts = {'after': False, 'force': False} - cwd = repo.getcwd() - if cwd: - cfiles = [util.pathto(cwd, f) for f in files.keys()] - for f in files: - ctype, gp = files[f] - if ctype == 'RENAME': - copies.append((gp.oldpath, gp.path, gp.copymod)) - removes.append(gp.oldpath) - elif ctype == 'COPY': - copies.append((gp.oldpath, gp.path, gp.copymod)) - elif ctype == 'DELETE': - removes.append(gp.path) - for src, dst, after in copies: - absdst = os.path.join(repo.root, dst) - if not after and os.path.exists(absdst): - raise util.Abort(_('patch creates existing file %s') % dst) - if cwd: - src, dst = [util.pathto(cwd, f) for f in (src, dst)] - copts['after'] = after - errs, copied = docopy(ui, repo, (src, dst), copts, wlock=wlock) - if errs: - raise util.Abort(errs) - if removes: - repo.remove(removes, True, wlock=wlock) - for f in files: - ctype, gp = files[f] - if gp and gp.mode: - x = gp.mode & 0100 != 0 - dst = os.path.join(repo.root, gp.path) - util.set_exec(dst, x) - cmdutil.addremove(repo, cfiles, wlock=wlock) - files = files.keys() - files.extend([r for r in removes if r not in files]) + files = patch.updatedir(ui, repo, files, wlock=wlock) repo.commit(files, message, user, date, wlock=wlock, lock=lock) finally: os.unlink(tmpname) @@ -3281,18 +3242,11 @@ return sys.modules[v] raise KeyError(name) -def dispatch(args): - for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM': - num = getattr(signal, name, None) - if num: signal.signal(num, catchterm) - - try: - u = ui.ui(traceback='--traceback' in sys.argv[1:]) - except util.Abort, inst: - sys.stderr.write(_("abort: %s\n") % inst) - return -1 - - for ext_name, load_from_name in u.extensions(): +def load_extensions(ui): + added = [] + for ext_name, load_from_name in ui.extensions(): + if ext_name in external: + continue try: if load_from_name: # the module will be loaded in sys.modules @@ -3312,23 +3266,36 @@ except ImportError: mod = importh(ext_name) external[ext_name] = mod.__name__ + added.append((mod, ext_name)) except (util.SignalInterrupt, KeyboardInterrupt): raise except Exception, inst: - u.warn(_("*** failed to import extension %s: %s\n") % (ext_name, inst)) - if u.print_exc(): + ui.warn(_("*** failed to import extension %s: %s\n") % + (ext_name, inst)) + if ui.print_exc(): return 1 - for name in external.itervalues(): - mod = sys.modules[name] + for mod, name in added: uisetup = getattr(mod, 'uisetup', None) if uisetup: - uisetup(u) + uisetup(ui) cmdtable = getattr(mod, 'cmdtable', {}) for t in cmdtable: if t in table: - u.warn(_("module %s overrides %s\n") % (name, t)) + ui.warn(_("module %s overrides %s\n") % (name, t)) table.update(cmdtable) + +def dispatch(args): + for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM': + num = getattr(signal, name, None) + if num: signal.signal(num, catchterm) + + try: + u = ui.ui(traceback='--traceback' in sys.argv[1:], + readhooks=[load_extensions]) + except util.Abort, inst: + sys.stderr.write(_("abort: %s\n") % inst) + return -1 try: cmd, func, args, options, cmdoptions = parse(u, args)
--- a/mercurial/filelog.py Thu Aug 17 21:13:35 2006 +0300 +++ b/mercurial/filelog.py Fri Aug 18 17:02:38 2006 -0700 @@ -96,31 +96,59 @@ return child # find all ancestors - needed = {node:1} - visit = [node] + needed = {(self, node):1} + files = [self] + visit = [(self, node)] while visit: - n = visit.pop(0) - for p in self.parents(n): - if p not in needed: - needed[p] = 1 - visit.append(p) + f, n = visit.pop(0) + rn = f.renamed(n) + if rn: + f, n = rn + f = filelog(self.opener, f, self.defversion) + files.insert(0, f) + if (f, n) not in needed: + needed[(f, n)] = 1 + else: + needed[(f, n)] += 1 + for p in f.parents(n): + if p == nullid: + continue + if (f, p) not in needed: + needed[(f, p)] = 1 + visit.append((f, p)) else: # count how many times we'll use this - needed[p] += 1 + needed[(f, p)] += 1 - # sort by revision which is a topological order - visit = [ (self.rev(n), n) for n in needed.keys() ] - visit.sort() + # sort by revision (per file) which is a topological order + visit = [] + for f in files: + fn = [(f.rev(n[1]), f, n[1]) for n in needed.keys() if n[0] == f] + fn.sort() + visit.extend(fn) hist = {} - for r,n in visit: - curr = decorate(self.read(n), self.linkrev(n)) - for p in self.parents(n): + for i in range(len(visit)): + r, f, n = visit[i] + curr = decorate(f.read(n), f.linkrev(n)) + if r == -1: + continue + parents = f.parents(n) + # follow parents across renames + if r < 1 and i > 0: + j = i + while j > 0 and visit[j][1] == f: + j -= 1 + parents = (visit[j][2],) + f = visit[j][1] + else: + parents = f.parents(n) + for p in parents: if p != nullid: curr = pair(hist[p], curr) # trim the history of unneeded revs - needed[p] -= 1 - if not needed[p]: + needed[(f, p)] -= 1 + if not needed[(f, p)]: del hist[p] hist[n] = curr
--- a/mercurial/patch.py Thu Aug 17 21:13:35 2006 +0300 +++ b/mercurial/patch.py Fri Aug 18 17:02:38 2006 -0700 @@ -11,6 +11,28 @@ demandload(globals(), "cmdutil mdiff util") demandload(globals(), "cStringIO email.Parser errno os re shutil sys tempfile") +# helper functions + +def copyfile(src, dst, basedir=None): + if not basedir: + basedir = os.getcwd() + + abssrc, absdst = [os.path.join(basedir, n) for n in (src, dst)] + if os.path.exists(absdst): + raise util.Abort(_("cannot create %s: destination already exists") % + dst) + + targetdir = os.path.dirname(absdst) + if not os.path.isdir(targetdir): + os.makedirs(targetdir) + try: + shutil.copyfile(abssrc, absdst) + shutil.copymode(abssrc, absdst) + except shutil.Error, inst: + raise util.Abort(str(inst)) + +# public functions + def extract(ui, fileobj): '''extract patch from data read from fileobj. @@ -174,21 +196,7 @@ if not p.copymod: continue - if os.path.exists(p.path): - raise util.Abort(_("cannot create %s: destination already exists") % - p.path) - - (src, dst) = [os.path.join(os.getcwd(), n) - for n in (p.oldpath, p.path)] - - targetdir = os.path.dirname(dst) - if not os.path.isdir(targetdir): - os.makedirs(targetdir) - try: - shutil.copyfile(src, dst) - shutil.copymode(src, dst) - except shutil.Error, inst: - raise util.Abort(str(inst)) + copyfile(p.oldpath, p.path) # rewrite patch hunk while pfline < p.lineno: @@ -281,6 +289,45 @@ ignoreblanklines=(opts.get('ignore_blank_lines') or ui.configbool('diff', 'ignoreblanklines', None))) +def updatedir(ui, repo, patches, wlock=None): + '''Update dirstate after patch application according to metadata''' + if not patches: + return + copies = [] + removes = [] + cfiles = patches.keys() + copts = {'after': False, 'force': False} + cwd = repo.getcwd() + if cwd: + cfiles = [util.pathto(cwd, f) for f in patches.keys()] + for f in patches: + ctype, gp = patches[f] + if ctype == 'RENAME': + copies.append((gp.oldpath, gp.path, gp.copymod)) + removes.append(gp.oldpath) + elif ctype == 'COPY': + copies.append((gp.oldpath, gp.path, gp.copymod)) + elif ctype == 'DELETE': + removes.append(gp.path) + for src, dst, after in copies: + if not after: + copyfile(src, dst, repo.root) + repo.copy(src, dst, wlock=wlock) + if removes: + repo.remove(removes, True, wlock=wlock) + for f in patches: + ctype, gp = patches[f] + if gp and gp.mode: + x = gp.mode & 0100 != 0 + dst = os.path.join(repo.root, gp.path) + util.set_exec(dst, x) + cmdutil.addremove(repo, cfiles, wlock=wlock) + files = patches.keys() + files.extend([r for r in removes if r not in files]) + files.sort() + + return files + def diff(repo, node1=None, node2=None, files=None, match=util.always, fp=None, changes=None, opts=None): '''print diff of changes to files between two nodes, or node and @@ -296,10 +343,27 @@ if not node1: node1 = repo.dirstate.parents()[0] + + clcache = {} + def getchangelog(n): + if n not in clcache: + clcache[n] = repo.changelog.read(n) + return clcache[n] + mcache = {} + def getmanifest(n): + if n not in mcache: + mcache[n] = repo.manifest.read(n) + return mcache[n] + fcache = {} + def getfile(f): + if f not in fcache: + fcache[f] = repo.file(f) + return fcache[f] + # reading the data for node1 early allows it to play nicely # with repo.status and the revlog cache. - change = repo.changelog.read(node1) - mmap = repo.manifest.read(change[0]) + change = getchangelog(node1) + mmap = getmanifest(change[0]) date1 = util.datestr(change[2]) if not changes: @@ -320,17 +384,32 @@ if not modified and not added and not removed: return + def renamedbetween(f, n1, n2): + r1, r2 = map(repo.changelog.rev, (n1, n2)) + src = None + while r2 > r1: + cl = getchangelog(n2)[0] + m = getmanifest(cl) + try: + src = getfile(f).renamed(m[f]) + except KeyError: + return None + if src: + f = src[0] + n2 = repo.changelog.parents(n2)[0] + r2 = repo.changelog.rev(n2) + return src + if node2: - change = repo.changelog.read(node2) - mmap2 = repo.manifest.read(change[0]) + change = getchangelog(node2) + mmap2 = getmanifest(change[0]) _date2 = util.datestr(change[2]) def date2(f): return _date2 def read(f): - return repo.file(f).read(mmap2[f]) + return getfile(f).read(mmap2[f]) def renamed(f): - src = repo.file(f).renamed(mmap2[f]) - return src and src[0] or None + return renamedbetween(f, node1, node2) else: tz = util.makedate()[1] _date2 = util.datestr() @@ -343,7 +422,18 @@ def read(f): return repo.wread(f) def renamed(f): - return repo.dirstate.copies.get(f) + src = repo.dirstate.copies.get(f) + parent = repo.dirstate.parents()[0] + if src: + f = src[0] + of = renamedbetween(f, node1, parent) + if of: + return of + elif src: + cl = getchangelog(parent)[0] + return (src, getmanifest(cl)[src]) + else: + return None if repo.ui.quiet: r = None @@ -357,7 +447,7 @@ src = renamed(f) if src: copied[f] = src - srcs = [x[1] for x in copied.items()] + srcs = [x[1][0] for x in copied.items()] all = modified + added + removed all.sort() @@ -366,7 +456,7 @@ tn = None dodiff = True if f in mmap: - to = repo.file(f).read(mmap[f]) + to = getfile(f).read(mmap[f]) if f not in removed: tn = read(f) if opts.git: @@ -385,13 +475,13 @@ else: mode = gitmode(util.is_exec(repo.wjoin(f), None)) if f in copied: - a = copied[f] + a, arev = copied[f] omode = gitmode(mmap.execf(a)) addmodehdr(header, omode, mode) op = a in removed and 'rename' or 'copy' header.append('%s from %s\n' % (op, a)) header.append('%s to %s\n' % (op, f)) - to = repo.file(a).read(mmap[a]) + to = getfile(a).read(arev) else: header.append('new file mode %s\n' % mode) elif f in removed:
--- a/mercurial/ui.py Thu Aug 17 21:13:35 2006 +0300 +++ b/mercurial/ui.py Fri Aug 18 17:02:38 2006 -0700 @@ -12,11 +12,13 @@ class ui(object): def __init__(self, verbose=False, debug=False, quiet=False, - interactive=True, traceback=False, parentui=None): + interactive=True, traceback=False, parentui=None, + readhooks=[]): self.overlay = {} if parentui is None: # this is the parent of all ui children self.parentui = None + self.readhooks = list(readhooks) self.cdata = ConfigParser.SafeConfigParser() self.readconfig(util.rcpath()) @@ -34,6 +36,7 @@ else: # parentui may point to an ui object which is already a child self.parentui = parentui.parentui or parentui + self.readhooks = list(parentui.readhooks or readhooks) parent_cdata = self.parentui.cdata self.cdata = ConfigParser.SafeConfigParser(parent_cdata.defaults()) # make interpolation work @@ -78,6 +81,8 @@ for name, path in self.configitems("paths"): if path and "://" not in path and not os.path.isabs(path): self.cdata.set("paths", name, os.path.join(root, path)) + for hook in self.readhooks: + hook(self) def setconfig(self, section, name, val): self.overlay[(section, name)] = val
--- a/tests/README Thu Aug 17 21:13:35 2006 +0300 +++ b/tests/README Fri Aug 18 17:02:38 2006 -0700 @@ -28,6 +28,6 @@ - diff will show the current time - use hg diff | sed "s/\(\(---\|+++\) [a-zA-Z0-9_/.-]*\).*/\1/" to strip - dates - + use hg diff | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \ + -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/" + to strip dates
--- a/tests/test-bisect Thu Aug 17 21:13:35 2006 +0300 +++ b/tests/test-bisect Fri Aug 18 17:02:38 2006 -0700 @@ -17,7 +17,7 @@ test $count -eq 0 && hg add hg ci -m "msg $count" -d "$count 0" echo % committed changeset $count - count=$(( $count + 1 )) + count=`expr $count + 1` done echo % log
--- a/tests/test-extdiff Thu Aug 17 21:13:35 2006 +0300 +++ b/tests/test-extdiff Fri Aug 18 17:02:38 2006 -0700 @@ -8,7 +8,11 @@ cd a echo a > a hg add -hg extdiff -o -Nr +diff -N /dev/null /dev/null 2> /dev/null +if [ $? -ne 0 ]; then + opt="-p gdiff" +fi +hg extdiff -o -Nr $opt echo "[extdiff]" >> $HGTMP/.hgrc echo "cmd.falabala=echo" >> $HGTMP/.hgrc
--- a/tests/test-git-export Thu Aug 17 21:13:35 2006 +0300 +++ b/tests/test-git-export Fri Aug 18 17:02:38 2006 -0700 @@ -8,22 +8,26 @@ echo new > new hg ci -Amnew -d '0 0' echo '% new file' -hg diff --git -r 0 | sed "s/\(\(---\|+++\) [a-zA-Z0-9_/.-]*\).*/\1/" +hg diff --git -r 0 | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \ + -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/" hg cp new copy hg ci -mcopy -d '0 0' echo '% copy' -hg diff --git -r 1:tip | sed "s/\(\(---\|+++\) [a-zA-Z0-9_/.-]*\).*/\1/" +hg diff --git -r 1:tip | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \ + -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/" hg mv copy rename hg ci -mrename -d '0 0' echo '% rename' -hg diff --git -r 2:tip | sed "s/\(\(---\|+++\) [a-zA-Z0-9_/.-]*\).*/\1/" +hg diff --git -r 2:tip | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \ + -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/" hg rm rename hg ci -mdelete -d '0 0' echo '% delete' -hg diff --git -r 3:tip | sed "s/\(\(---\|+++\) [a-zA-Z0-9_/.-]*\).*/\1/" +hg diff --git -r 3:tip | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \ + -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/" cat > src <<EOF 1 @@ -36,11 +40,13 @@ chmod +x src hg ci -munexec -d '0 0' echo '% chmod 644' -hg diff --git -r 5:tip | sed "s/\(\(---\|+++\) [a-zA-Z0-9_/.-]*\).*/\1/" +hg diff --git -r 5:tip | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \ + -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/" hg mv src dst chmod -x dst echo a >> dst hg ci -mrenamemod -d '0 0' echo '% rename+mod+chmod' -hg diff --git -r 6:tip | sed "s/\(\(---\|+++\) [a-zA-Z0-9_/.-]*\).*/\1/" +hg diff --git -r 6:tip | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \ + -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-issue322 Fri Aug 18 17:02:38 2006 -0700 @@ -0,0 +1,45 @@ +#!/bin/sh +# http://www.selenic.com/mercurial/bts/issue322 + +echo % file replaced with directory + +hg init a +cd a +echo a > a +hg commit -Ama +rm a +mkdir a +echo a > a/a + +echo % should fail - would corrupt dirstate +hg add a/a + +echo % should fail - if add succeeded, would corrupt manifest +hg commit -mb + +echo % should fail if commit succeeded - manifest is corrupt +hg verify + +cd .. +echo % should succeed, but manifest is corrupt +hg --debug --traceback clone a b + +echo % directory replaced with file + +hg init c +cd c +mkdir a +echo a > a/a +hg commit -Ama + +rm -rf a +echo a > a + +echo % should fail - would corrupt dirstate +hg add a + +echo % should fail - if add succeeded, would corrupt manifest +hg commit -mb a + +echo % should fail if commit succeeded - manifest is corrupt +hg verify
--- a/tests/test-mq Thu Aug 17 21:13:35 2006 +0300 +++ b/tests/test-mq Fri Aug 18 17:02:38 2006 -0700 @@ -126,3 +126,30 @@ hg ci -Ama hg strip tip 2>&1 | sed 's/\(saving bundle to \).*/\1/' hg unbundle .hg/strip-backup/* + +cat >>$HGTMP/.hgrc <<EOF +[diff] +git = True +EOF +cd .. +hg init git +cd git +hg qinit + +hg qnew -m'new file' new +echo foo > new +chmod +x new +hg add new +hg qrefresh +sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \ + -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/" .hg/patches/new + +hg qnew -m'copy file' copy +hg cp new copy +hg qrefresh +sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \ + -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/" .hg/patches/copy + +hg qpop +hg qpush +hg qdiff
--- a/tests/test-mq.out Thu Aug 17 21:13:35 2006 +0300 +++ b/tests/test-mq.out Fri Aug 18 17:02:38 2006 -0700 @@ -127,3 +127,22 @@ adding file changes added 1 changesets with 1 changes to 1 files (run 'hg update' to get a working copy) +new file + +diff --git a/new b/new +new file mode 100755 +--- /dev/null ++++ b/new +@@ -0,0 +1,1 @@ ++foo +copy file + +diff --git a/new b/copy +copy from new +copy to copy +Now at: new +applying copy +Now at: copy +diff --git a/new b/copy +copy from new +copy to copy