merge.
--- 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