--- a/hgext/largefiles/overrides.py Wed May 09 09:58:50 2012 +0200
+++ b/hgext/largefiles/overrides.py Sat May 12 00:06:11 2012 +0200
@@ -552,7 +552,8 @@
for lfile in modified:
lfutil.updatestandin(repo, lfutil.standin(lfile))
for lfile in missing:
- os.unlink(repo.wjoin(lfutil.standin(lfile)))
+ if (os.path.exists(repo.wjoin(lfutil.standin(lfile)))):
+ os.unlink(repo.wjoin(lfutil.standin(lfile)))
try:
ctx = repo[opts.get('rev')]
@@ -953,6 +954,8 @@
ui.status(_('largefiles: %d to upload\n') % len(toupload))
def overrideaddremove(orig, ui, repo, *pats, **opts):
+ if not lfutil.islfilesrepo(repo):
+ return orig(ui, repo, *pats, **opts)
# Get the list of missing largefiles so we can remove them
lfdirstate = lfutil.openlfdirstate(ui, repo)
s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [], False,
--- a/hgext/mq.py Wed May 09 09:58:50 2012 +0200
+++ b/hgext/mq.py Sat May 12 00:06:11 2012 +0200
@@ -554,6 +554,18 @@
except OSError, inst:
self.ui.warn(_('error removing undo: %s\n') % str(inst))
+ def backup(self, repo, files, copy=False):
+ # backup local changes in --force case
+ for f in sorted(files):
+ absf = repo.wjoin(f)
+ if os.path.lexists(absf):
+ self.ui.note(_('saving current version of %s as %s\n') %
+ (f, f + '.orig'))
+ if copy:
+ util.copyfile(absf, absf + '.orig')
+ else:
+ util.rename(absf, absf + '.orig')
+
def printdiff(self, repo, diffopts, node1, node2=None, files=None,
fp=None, changes=None, opts={}):
stat = opts.get('stat')
@@ -668,7 +680,8 @@
return (False, list(files), False)
def apply(self, repo, series, list=False, update_status=True,
- strict=False, patchdir=None, merge=None, all_files=None):
+ strict=False, patchdir=None, merge=None, all_files=None,
+ tobackup=None):
wlock = lock = tr = None
try:
wlock = repo.wlock()
@@ -676,7 +689,8 @@
tr = repo.transaction("qpush")
try:
ret = self._apply(repo, series, list, update_status,
- strict, patchdir, merge, all_files=all_files)
+ strict, patchdir, merge, all_files=all_files,
+ tobackup=tobackup)
tr.close()
self.savedirty()
return ret
@@ -693,9 +707,14 @@
self.removeundo(repo)
def _apply(self, repo, series, list=False, update_status=True,
- strict=False, patchdir=None, merge=None, all_files=None):
- '''returns (error, hash)
- error = 1 for unable to read, 2 for patch failed, 3 for patch fuzz'''
+ strict=False, patchdir=None, merge=None, all_files=None,
+ tobackup=None):
+ """returns (error, hash)
+
+ error = 1 for unable to read, 2 for patch failed, 3 for patch
+ fuzz. tobackup is None or a set of files to backup before they
+ are modified by a patch.
+ """
# TODO unify with commands.py
if not patchdir:
patchdir = self.path
@@ -727,6 +746,11 @@
message = '\n'.join(message)
if ph.haspatch:
+ if tobackup:
+ touched = patchmod.changedfiles(self.ui, repo, pf)
+ touched = set(touched) & tobackup
+ self.backup(repo, touched, copy=True)
+ tobackup = tobackup - touched
(patcherr, files, fuzz) = self.patch(repo, pf)
if all_files is not None:
all_files.update(files)
@@ -1133,7 +1157,7 @@
raise util.Abort(_("patch %s not in series") % patch)
def push(self, repo, patch=None, force=False, list=False,
- mergeq=None, all=False, move=False, exact=False):
+ mergeq=None, all=False, move=False, exact=False, nobackup=False):
diffopts = self.diffopts()
wlock = repo.wlock()
try:
@@ -1232,13 +1256,19 @@
else:
end = self.series.index(patch, start) + 1
+ tobackup = set()
+ if not nobackup and force:
+ m, a, r, d = self.checklocalchanges(repo, force=True)
+ tobackup.update(m + a)
+
s = self.series[start:end]
all_files = set()
try:
if mergeq:
ret = self.mergepatch(repo, mergeq, s, diffopts)
else:
- ret = self.apply(repo, s, list, all_files=all_files)
+ ret = self.apply(repo, s, list, all_files=all_files,
+ tobackup=tobackup)
except:
self.ui.warn(_('cleaning up working directory...'))
node = repo.dirstate.p1()
@@ -1268,7 +1298,8 @@
finally:
wlock.release()
- def pop(self, repo, patch=None, force=False, update=True, all=False):
+ def pop(self, repo, patch=None, force=False, update=True, all=False,
+ nobackup=False):
wlock = repo.wlock()
try:
if patch:
@@ -1313,8 +1344,11 @@
break
update = needupdate
- if not force and update:
- self.checklocalchanges(repo)
+ tobackup = set()
+ if update:
+ m, a, r, d = self.checklocalchanges(repo, force=force)
+ if not nobackup and force:
+ tobackup.update(m + a)
self.applieddirty = True
end = len(self.applied)
@@ -1344,6 +1378,10 @@
m, a, r, d = repo.status(qp, top)[:4]
if d:
raise util.Abort(_("deletions found between repo revs"))
+
+ # backup local changes in --force case
+ self.backup(repo, set(a + m + r) & tobackup)
+
for f in a:
try:
util.unlinkpath(repo.wjoin(f))
@@ -2460,7 +2498,8 @@
wlock.release()
@command("qgoto",
- [('f', 'force', None, _('overwrite any local changes'))],
+ [('f', 'force', None, _('overwrite any local changes')),
+ ('', 'no-backup', None, _('do not save backup copies of files'))],
_('hg qgoto [OPTION]... PATCH'))
def goto(ui, repo, patch, **opts):
'''push or pop patches until named patch is at top of stack
@@ -2468,10 +2507,11 @@
Returns 0 on success.'''
q = repo.mq
patch = q.lookup(patch)
+ nobackup = opts.get('no_backup')
if q.isapplied(patch):
- ret = q.pop(repo, patch, force=opts.get('force'))
+ ret = q.pop(repo, patch, force=opts.get('force'), nobackup=nobackup)
else:
- ret = q.push(repo, patch, force=opts.get('force'))
+ ret = q.push(repo, patch, force=opts.get('force'), nobackup=nobackup)
q.savedirty()
return ret
@@ -2598,7 +2638,9 @@
('m', 'merge', None, _('merge from another queue (DEPRECATED)')),
('n', 'name', '',
_('merge queue name (DEPRECATED)'), _('NAME')),
- ('', 'move', None, _('reorder patch series and apply only the patch'))],
+ ('', 'move', None,
+ _('reorder patch series and apply only the patch')),
+ ('', 'no-backup', None, _('do not save backup copies of files'))],
_('hg qpush [-f] [-l] [-a] [--move] [PATCH | INDEX]'))
def push(ui, repo, patch=None, **opts):
"""push the next patch onto the stack
@@ -2623,14 +2665,15 @@
ui.warn(_("merging with queue at: %s\n") % mergeq.path)
ret = q.push(repo, patch, force=opts.get('force'), list=opts.get('list'),
mergeq=mergeq, all=opts.get('all'), move=opts.get('move'),
- exact=opts.get('exact'))
+ exact=opts.get('exact'), nobackup=opts.get('no_backup'))
return ret
@command("^qpop",
[('a', 'all', None, _('pop all patches')),
('n', 'name', '',
_('queue name to pop (DEPRECATED)'), _('NAME')),
- ('f', 'force', None, _('forget any local changes to patched files'))],
+ ('f', 'force', None, _('forget any local changes to patched files')),
+ ('', 'no-backup', None, _('do not save backup copies of files'))],
_('hg qpop [-a] [-f] [PATCH | INDEX]'))
def pop(ui, repo, patch=None, **opts):
"""pop the current patch off the stack
@@ -2649,7 +2692,7 @@
else:
q = repo.mq
ret = q.pop(repo, patch, force=opts.get('force'), update=localupdate,
- all=opts.get('all'))
+ all=opts.get('all'), nobackup=opts.get('no_backup'))
q.savedirty()
return ret
--- a/hgext/pager.py Wed May 09 09:58:50 2012 +0200
+++ b/hgext/pager.py Sat May 12 00:06:11 2012 +0200
@@ -22,12 +22,6 @@
If no pager is set, the pager extensions uses the environment variable
$PAGER. If neither pager.pager, nor $PAGER is set, no pager is used.
-If you notice "BROKEN PIPE" error messages, you can disable them by
-setting::
-
- [pager]
- quiet = True
-
You can disable the pager for certain commands by adding them to the
pager.ignore list::
@@ -53,37 +47,27 @@
normal behavior.
'''
-import sys, os, signal, shlex, errno
+import atexit, sys, os, signal, subprocess
from mercurial import commands, dispatch, util, extensions
from mercurial.i18n import _
def _runpager(p):
- if not util.safehasattr(os, 'fork'):
- sys.stdout = util.popen(p, 'wb')
- if util.isatty(sys.stderr):
- sys.stderr = sys.stdout
- return
- fdin, fdout = os.pipe()
- pid = os.fork()
- if pid == 0:
- os.close(fdin)
- os.dup2(fdout, sys.stdout.fileno())
- if util.isatty(sys.stderr):
- os.dup2(fdout, sys.stderr.fileno())
- os.close(fdout)
- return
- os.dup2(fdin, sys.stdin.fileno())
- os.close(fdin)
- os.close(fdout)
- try:
- os.execvp('/bin/sh', ['/bin/sh', '-c', p])
- except OSError, e:
- if e.errno == errno.ENOENT:
- # no /bin/sh, try executing the pager directly
- args = shlex.split(p)
- os.execvp(args[0], args)
- else:
- raise
+ pager = subprocess.Popen(p, shell=True, bufsize=-1,
+ close_fds=util.closefds, stdin=subprocess.PIPE,
+ stdout=sys.stdout, stderr=sys.stderr)
+
+ stdout = os.dup(sys.stdout.fileno())
+ stderr = os.dup(sys.stderr.fileno())
+ os.dup2(pager.stdin.fileno(), sys.stdout.fileno())
+ if util.isatty(sys.stderr):
+ os.dup2(pager.stdin.fileno(), sys.stderr.fileno())
+
+ @atexit.register
+ def killpager():
+ pager.stdin.close()
+ os.dup2(stdout, sys.stdout.fileno())
+ os.dup2(stderr, sys.stderr.fileno())
+ pager.wait()
def uisetup(ui):
if ui.plain() or '--debugger' in sys.argv or not util.isatty(sys.stdout):
@@ -101,9 +85,11 @@
(cmd not in ui.configlist('pager', 'ignore') and not attend))):
ui.setconfig('ui', 'formatted', ui.formatted())
ui.setconfig('ui', 'interactive', False)
+ try:
+ signal.signal(signal.SIGPIPE, signal.SIG_DFL)
+ except ValueError:
+ pass
_runpager(p)
- if ui.configbool('pager', 'quiet'):
- signal.signal(signal.SIGPIPE, signal.SIG_DFL)
return orig(ui, options, cmd, cmdfunc)
extensions.wrapfunction(dispatch, '_runcommand', pagecmd)
--- a/mercurial/cmdutil.py Wed May 09 09:58:50 2012 +0200
+++ b/mercurial/cmdutil.py Sat May 12 00:06:11 2012 +0200
@@ -1311,6 +1311,12 @@
# |
# base o - parent of amending changeset
+ # Update extra dict from amended commit (e.g. to preserve graft source)
+ extra.update(old.extra())
+
+ # Also update it from the intermediate commit or from the wctx
+ extra.update(ctx.extra())
+
files = set(old.files())
# Second, we use either the commit we just did, or if there were no
@@ -1322,7 +1328,6 @@
user = ctx.user()
date = ctx.date()
message = ctx.description()
- extra = ctx.extra()
# Recompute copies (avoid recording a -> b -> a)
copied = copies.pathcopies(base, ctx)
--- a/mercurial/commands.py Wed May 09 09:58:50 2012 +0200
+++ b/mercurial/commands.py Sat May 12 00:06:11 2012 +0200
@@ -196,6 +196,8 @@
be identical) as its parameter. Detecting renamed files this way
can be expensive. After using this option, :hg:`status -C` can be
used to check which files were identified as moved or renamed.
+ If this option is not specified, only renames of identical files
+ are detected.
Returns 0 if all files are successfully added.
"""
--- a/mercurial/parsers.c Wed May 09 09:58:50 2012 +0200
+++ b/mercurial/parsers.c Sat May 12 00:06:11 2012 +0200
@@ -555,7 +555,7 @@
*/
static int nt_find(indexObject *self, const char *node, Py_ssize_t nodelen)
{
- int level, off;
+ int level, maxlevel, off;
if (nodelen == 20 && node[0] == '\0' && memcmp(node, nullid, 20) == 0)
return -1;
@@ -563,7 +563,9 @@
if (self->nt == NULL)
return -2;
- for (level = off = 0; level < nodelen; level++) {
+ maxlevel = nodelen > 20 ? 40 : ((int)nodelen * 2);
+
+ for (level = off = 0; level < maxlevel; level++) {
int k = nt_level(node, level);
nodetree *n = &self->nt[off];
int v = n->children[k];
@@ -606,7 +608,7 @@
int level = 0;
int off = 0;
- while (level < 20) {
+ while (level < 40) {
int k = nt_level(node, level);
nodetree *n;
int v;
--- a/mercurial/revset.py Wed May 09 09:58:50 2012 +0200
+++ b/mercurial/revset.py Sat May 12 00:06:11 2012 +0200
@@ -926,7 +926,7 @@
Special fields are ``summary`` and ``metadata``:
``summary`` matches the first line of the description.
- ``metatadata`` is equivalent to matching ``description user date``
+ ``metadata`` is equivalent to matching ``description user date``
(i.e. it matches the main metadata fields).
``metadata`` is the default field which is used when no fields are
@@ -996,7 +996,7 @@
# is only one field to match)
getinfo = lambda r: [f(r) for f in getfieldfuncs]
- matches = []
+ matches = set()
for rev in revs:
target = getinfo(rev)
for r in subset:
@@ -1006,10 +1006,8 @@
match = False
break
if match:
- matches.append(r)
- if len(revs) > 1:
- matches = sorted(set(matches))
- return matches
+ matches.add(r)
+ return [r for r in subset if r in matches]
def reverse(repo, subset, x):
"""``reverse(set)``
--- a/tests/test-check-code-hg.t Wed May 09 09:58:50 2012 +0200
+++ b/tests/test-check-code-hg.t Sat May 12 00:06:11 2012 +0200
@@ -220,9 +220,6 @@
> raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
warning: line over 80 characters
hgext/mq.py:0:
- > ('', 'move', None, _('reorder patch series and apply only the patch'))],
- warning: line over 80 characters
- hgext/mq.py:0:
> ('U', 'noupdate', None, _('do not update the new working directories')),
warning: line over 80 characters
hgext/mq.py:0:
--- a/tests/test-commit-amend.t Wed May 09 09:58:50 2012 +0200
+++ b/tests/test-commit-amend.t Sat May 12 00:06:11 2012 +0200
@@ -316,3 +316,37 @@
$ hg rollback
no rollback information available
[1]
+
+Preserve extra dict (issue3430):
+
+ $ hg branch a
+ marked working directory as branch a
+ (branches are permanent and global, did you want a bookmark?)
+ $ echo a >> a
+ $ hg ci -ma
+ $ hg ci --amend -m "a'"
+ saved backup bundle to $TESTTMP/.hg/strip-backup/167f8e3031df-amend-backup.hg
+ $ hg log -r . --template "{branch}\n"
+ a
+ $ hg ci --amend -m "a''"
+ saved backup bundle to $TESTTMP/.hg/strip-backup/ceac1a44c806-amend-backup.hg
+ $ hg log -r . --template "{branch}\n"
+ a
+
+Also preserve other entries in the dict that are in the old commit,
+first graft something so there's an additional entry:
+
+ $ hg up 0 -q
+ $ echo z > z
+ $ hg ci -Am 'fork'
+ adding z
+ created new head
+ $ hg up 11
+ 5 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ $ hg graft 12
+ grafting revision 12
+ $ hg ci --amend -m 'graft amend'
+ saved backup bundle to $TESTTMP/.hg/strip-backup/18a5124daf7a-amend-backup.hg
+ $ hg log -r . --debug | grep extra
+ extra: branch=a
+ extra: source=2647734878ef0236dda712fae9c1651cf694ea8a
--- a/tests/test-mq-qpush-fail.t Wed May 09 09:58:50 2012 +0200
+++ b/tests/test-mq-qpush-fail.t Sat May 12 00:06:11 2012 +0200
@@ -150,3 +150,134 @@
abort: cannot push to a previous patch: a
[255]
+test qpop --force and backup files
+
+ $ hg qpop -a
+ popping b
+ patch queue now empty
+ $ hg qq --create force
+ $ echo a > a
+ $ echo b > b
+ $ echo c > c
+ $ hg ci -Am add a b c
+ $ echo a >> a
+ $ hg rm b
+ $ hg rm c
+ $ hg qnew p1
+ $ echo a >> a
+ $ echo bb > b
+ $ hg add b
+ $ echo cc > c
+ $ hg add c
+ $ hg qpop --force --verbose
+ saving current version of a as a.orig
+ saving current version of b as b.orig
+ saving current version of c as c.orig
+ popping p1
+ patch queue now empty
+ $ hg st
+ ? a.orig
+ ? b.orig
+ ? c.orig
+ ? untracked-file
+ $ cat a.orig
+ a
+ a
+ a
+ $ cat b.orig
+ bb
+ $ cat c.orig
+ cc
+
+test qpop --force --no-backup
+
+ $ hg qpush
+ applying p1
+ now at: p1
+ $ rm a.orig
+ $ echo a >> a
+ $ hg qpop --force --no-backup --verbose
+ popping p1
+ patch queue now empty
+ $ test -f a.orig && echo 'error: backup with --no-backup'
+ [1]
+
+test qpush --force and backup files
+
+ $ echo a >> a
+ $ hg qnew p2
+ $ echo b >> b
+ $ echo d > d
+ $ echo e > e
+ $ hg add d e
+ $ hg rm c
+ $ hg qnew p3
+ $ hg qpop -a
+ popping p3
+ popping p2
+ patch queue now empty
+ $ echo a >> a
+ $ echo b1 >> b
+ $ echo d1 > d
+ $ hg add d
+ $ echo e1 > e
+ $ hg qpush -a --force --verbose
+ applying p2
+ saving current version of a as a.orig
+ patching file a
+ a
+ applying p3
+ saving current version of b as b.orig
+ saving current version of d as d.orig
+ patching file b
+ patching file c
+ patching file d
+ file d already exists
+ 1 out of 1 hunks FAILED -- saving rejects to file d.rej
+ patching file e
+ file e already exists
+ 1 out of 1 hunks FAILED -- saving rejects to file e.rej
+ patch failed to apply
+ b
+ patch failed, rejects left in working dir
+ errors during apply, please fix and refresh p3
+ [2]
+ $ cat a.orig
+ a
+ a
+ $ cat b.orig
+ b
+ b1
+ $ cat d.orig
+ d1
+
+test qpush --force --no-backup
+
+ $ hg revert -qa
+ $ hg qpop -a
+ popping p3
+ popping p2
+ patch queue now empty
+ $ echo a >> a
+ $ rm a.orig
+ $ hg qpush --force --no-backup --verbose
+ applying p2
+ patching file a
+ a
+ now at: p2
+ $ test -f a.orig && echo 'error: backup with --no-backup'
+ [1]
+
+test qgoto --force --no-backup
+
+ $ hg qpop
+ popping p2
+ patch queue now empty
+ $ echo a >> a
+ $ hg qgoto --force --no-backup p2 --verbose
+ applying p2
+ patching file a
+ a
+ now at: p2
+ $ test -f a.orig && echo 'error: backup with --no-backup'
+ [1]
--- a/tests/test-mq.t Wed May 09 09:58:50 2012 +0200
+++ b/tests/test-mq.t Sat May 12 00:06:11 2012 +0200
@@ -1356,11 +1356,15 @@
apply force, should discard changes in hello, but not bye
- $ hg qpush -f
+ $ hg qpush -f --verbose
applying empty
+ saving current version of hello.txt as hello.txt.orig
+ patching file hello.txt
+ hello.txt
now at: empty
$ hg st
M bye.txt
+ ? hello.txt.orig
$ hg diff --config diff.nodates=True
diff -r ba252371dbc1 bye.txt
--- a/bye.txt
--- a/tests/test-revset.t Wed May 09 09:58:50 2012 +0200
+++ b/tests/test-revset.t Sat May 12 00:06:11 2012 +0200
@@ -410,6 +410,10 @@
0
$ log '4::8 - 8'
4
+ $ log 'matching(1 or 2 or 3) and (2 or 3 or 1)'
+ 2
+ 3
+ 1
issue2437