# HG changeset patch # User Matt Mackall # Date 1336773971 -7200 # Node ID 5cf18921bb7b6cdb4cb299a8f26908a3a39b2996 # Parent 1435866c1937bf2de3d23594bd7aca991eb1967b# Parent e6dfbc5df76fe96f73ca204b1d10cd27a814e890 merge with stable diff -r 1435866c1937 -r 5cf18921bb7b hgext/largefiles/overrides.py --- 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, diff -r 1435866c1937 -r 5cf18921bb7b hgext/mq.py --- 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 diff -r 1435866c1937 -r 5cf18921bb7b hgext/pager.py --- 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) diff -r 1435866c1937 -r 5cf18921bb7b mercurial/cmdutil.py --- 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) diff -r 1435866c1937 -r 5cf18921bb7b mercurial/commands.py --- 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. """ diff -r 1435866c1937 -r 5cf18921bb7b mercurial/parsers.c --- 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; diff -r 1435866c1937 -r 5cf18921bb7b mercurial/revset.py --- 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)`` diff -r 1435866c1937 -r 5cf18921bb7b tests/test-check-code-hg.t --- 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: diff -r 1435866c1937 -r 5cf18921bb7b tests/test-commit-amend.t --- 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 diff -r 1435866c1937 -r 5cf18921bb7b tests/test-mq-qpush-fail.t --- 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] diff -r 1435866c1937 -r 5cf18921bb7b tests/test-mq.t --- 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 diff -r 1435866c1937 -r 5cf18921bb7b tests/test-revset.t --- 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