# HG changeset patch # User Patrick Mezard # Date 2010-02-07 13:43:21 # Node ID f05e0d54f424a116098ac3a3b638c7fcd73b3118 # Parent d757bc0c78651de58c4c69904c4dc1ab681c44be # Parent b8801b58bbd8fe42571e52c0ba8646bb59077efb Merge with crew-stable diff --git a/hgext/mq.py b/hgext/mq.py --- a/hgext/mq.py +++ b/hgext/mq.py @@ -1208,12 +1208,14 @@ class queue(object): if newdate: newdate = '%d %d' % util.parsedate(newdate) wlock = repo.wlock() + try: self.check_toppatch(repo) (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name) top = bin(top) if repo.changelog.heads(top) != [top]: raise util.Abort(_("cannot refresh a revision with children")) + cparents = repo.changelog.parents(top) patchparent = self.qparents(repo, top) ph = patchheader(self.join(patchfn)) @@ -1232,173 +1234,148 @@ class queue(object): if comments: patchf.write(comments) - tip = repo.changelog.tip() - if top == tip: - # if the top of our patch queue is also the tip, there is an - # optimization here. We update the dirstate in place and strip - # off the tip commit. Then just commit the current directory - # tree. We can also send repo.commit the list of files - # changed to speed up the diff - # - # in short mode, we only diff the files included in the - # patch already plus specified files - # - # this should really read: - # mm, dd, aa, aa2 = repo.status(tip, patchparent)[:4] - # but we do it backwards to take advantage of manifest/chlog - # caching against the next repo.status call - # - mm, aa, dd, aa2 = repo.status(patchparent, tip)[:4] - changes = repo.changelog.read(tip) - man = repo.manifest.read(changes[0]) - aaa = aa[:] - matchfn = cmdutil.match(repo, pats, opts) - if opts.get('short'): - # if amending a patch, we start with existing - # files plus specified files - unfiltered - match = cmdutil.matchfiles(repo, mm + aa + dd + matchfn.files()) - # filter with inc/exl options - matchfn = cmdutil.match(repo, opts=opts) - else: - match = cmdutil.matchall(repo) - m, a, r, d = repo.status(match=match)[:4] + # update the dirstate in place, strip off the qtip commit + # and then commit. + # + # this should really read: + # mm, dd, aa, aa2 = repo.status(tip, patchparent)[:4] + # but we do it backwards to take advantage of manifest/chlog + # caching against the next repo.status call + mm, aa, dd, aa2 = repo.status(patchparent, top)[:4] + changes = repo.changelog.read(top) + man = repo.manifest.read(changes[0]) + aaa = aa[:] + matchfn = cmdutil.match(repo, pats, opts) + # in short mode, we only diff the files included in the + # patch already plus specified files + if opts.get('short'): + # if amending a patch, we start with existing + # files plus specified files - unfiltered + match = cmdutil.matchfiles(repo, mm + aa + dd + matchfn.files()) + # filter with inc/exl options + matchfn = cmdutil.match(repo, opts=opts) + else: + match = cmdutil.matchall(repo) + m, a, r, d = repo.status(match=match)[:4] - # we might end up with files that were added between - # tip and the dirstate parent, but then changed in the - # local dirstate. in this case, we want them to only - # show up in the added section - for x in m: - if x not in aa: - mm.append(x) - # we might end up with files added by the local dirstate that - # were deleted by the patch. In this case, they should only - # show up in the changed section. - for x in a: - if x in dd: - del dd[dd.index(x)] - mm.append(x) - else: - aa.append(x) - # make sure any files deleted in the local dirstate - # are not in the add or change column of the patch - forget = [] - for x in d + r: - if x in aa: - del aa[aa.index(x)] - forget.append(x) - continue - elif x in mm: - del mm[mm.index(x)] - dd.append(x) + # we might end up with files that were added between + # qtip and the dirstate parent, but then changed in the + # local dirstate. in this case, we want them to only + # show up in the added section + for x in m: + if x not in aa: + mm.append(x) + # we might end up with files added by the local dirstate that + # were deleted by the patch. In this case, they should only + # show up in the changed section. + for x in a: + if x in dd: + del dd[dd.index(x)] + mm.append(x) + else: + aa.append(x) + # make sure any files deleted in the local dirstate + # are not in the add or change column of the patch + forget = [] + for x in d + r: + if x in aa: + del aa[aa.index(x)] + forget.append(x) + continue + elif x in mm: + del mm[mm.index(x)] + dd.append(x) - m = list(set(mm)) - r = list(set(dd)) - a = list(set(aa)) - c = [filter(matchfn, l) for l in (m, a, r)] - match = cmdutil.matchfiles(repo, set(c[0] + c[1] + c[2])) - chunks = patch.diff(repo, patchparent, match=match, - changes=c, opts=diffopts) - for chunk in chunks: - patchf.write(chunk) + m = list(set(mm)) + r = list(set(dd)) + a = list(set(aa)) + c = [filter(matchfn, l) for l in (m, a, r)] + match = cmdutil.matchfiles(repo, set(c[0] + c[1] + c[2])) + chunks = patch.diff(repo, patchparent, match=match, + changes=c, opts=diffopts) + for chunk in chunks: + patchf.write(chunk) - try: - if diffopts.git or diffopts.upgrade: - copies = {} - for dst in a: - src = repo.dirstate.copied(dst) - # during qfold, the source file for copies may - # be removed. Treat this as a simple add. - if src is not None and src in repo.dirstate: - copies.setdefault(src, []).append(dst) - repo.dirstate.add(dst) - # remember the copies between patchparent and tip - for dst in aaa: - f = repo.file(dst) - src = f.renamed(man[dst]) - if src: - copies.setdefault(src[0], []).extend( - copies.get(dst, [])) - if dst in a: - copies[src[0]].append(dst) - # we can't copy a file created by the patch itself - if dst in copies: - del copies[dst] - for src, dsts in copies.iteritems(): - for dst in dsts: - repo.dirstate.copy(src, dst) - else: - for dst in a: - repo.dirstate.add(dst) - # Drop useless copy information - for f in list(repo.dirstate.copies()): - repo.dirstate.copy(None, f) - for f in r: - repo.dirstate.remove(f) - # if the patch excludes a modified file, mark that - # file with mtime=0 so status can see it. - mm = [] - for i in xrange(len(m)-1, -1, -1): - if not matchfn(m[i]): - mm.append(m[i]) - del m[i] - for f in m: - repo.dirstate.normal(f) - for f in mm: - repo.dirstate.normallookup(f) - for f in forget: - repo.dirstate.forget(f) + try: + if diffopts.git or diffopts.upgrade: + copies = {} + for dst in a: + src = repo.dirstate.copied(dst) + # during qfold, the source file for copies may + # be removed. Treat this as a simple add. + if src is not None and src in repo.dirstate: + copies.setdefault(src, []).append(dst) + repo.dirstate.add(dst) + # remember the copies between patchparent and qtip + for dst in aaa: + f = repo.file(dst) + src = f.renamed(man[dst]) + if src: + copies.setdefault(src[0], []).extend( + copies.get(dst, [])) + if dst in a: + copies[src[0]].append(dst) + # we can't copy a file created by the patch itself + if dst in copies: + del copies[dst] + for src, dsts in copies.iteritems(): + for dst in dsts: + repo.dirstate.copy(src, dst) + else: + for dst in a: + repo.dirstate.add(dst) + # Drop useless copy information + for f in list(repo.dirstate.copies()): + repo.dirstate.copy(None, f) + for f in r: + repo.dirstate.remove(f) + # if the patch excludes a modified file, mark that + # file with mtime=0 so status can see it. + mm = [] + for i in xrange(len(m)-1, -1, -1): + if not matchfn(m[i]): + mm.append(m[i]) + del m[i] + for f in m: + repo.dirstate.normal(f) + for f in mm: + repo.dirstate.normallookup(f) + for f in forget: + repo.dirstate.forget(f) - if not msg: - if not ph.message: - message = "[mq]: %s\n" % patchfn - else: - message = "\n".join(ph.message) + if not msg: + if not ph.message: + message = "[mq]: %s\n" % patchfn else: - message = msg - - user = ph.user or changes[1] + message = "\n".join(ph.message) + else: + message = msg - # assumes strip can roll itself back if interrupted - repo.dirstate.setparents(*cparents) - self.applied.pop() - self.applied_dirty = 1 - self.strip(repo, top, update=False, - backup='strip') - except: - repo.dirstate.invalidate() - raise + user = ph.user or changes[1] - try: - # might be nice to attempt to roll back strip after this - patchf.rename() - n = repo.commit(message, user, ph.date, match=match, - force=True) - self.applied.append(statusentry(hex(n), patchfn)) - except: - ctx = repo[cparents[0]] - repo.dirstate.rebuild(ctx.node(), ctx.manifest()) - self.save_dirty() - self.ui.warn(_('refresh interrupted while patch was popped! ' - '(revert --all, qpush to recover)\n')) - raise - else: - self.printdiff(repo, diffopts, patchparent, fp=patchf) + # assumes strip can roll itself back if interrupted + repo.dirstate.setparents(*cparents) + self.applied.pop() + self.applied_dirty = 1 + self.strip(repo, top, update=False, + backup='strip') + except: + repo.dirstate.invalidate() + raise + + try: + # might be nice to attempt to roll back strip after this patchf.rename() - added = repo.status()[1] - for a in added: - f = repo.wjoin(a) - try: - os.unlink(f) - except OSError, e: - if e.errno != errno.ENOENT: - raise - try: os.removedirs(os.path.dirname(f)) - except: pass - # forget the file copies in the dirstate - # push should readd the files later on - repo.dirstate.forget(a) - self.pop(repo, force=True) - self.push(repo, force=True) + n = repo.commit(message, user, ph.date, match=match, + force=True) + self.applied.append(statusentry(hex(n), patchfn)) + except: + ctx = repo[cparents[0]] + repo.dirstate.rebuild(ctx.node(), ctx.manifest()) + self.save_dirty() + self.ui.warn(_('refresh interrupted while patch was popped! ' + '(revert --all, qpush to recover)\n')) + raise finally: wlock.release() self.removeundo(repo) diff --git a/tests/test-mq-qrefresh b/tests/test-mq-qrefresh --- a/tests/test-mq-qrefresh +++ b/tests/test-mq-qrefresh @@ -155,6 +155,31 @@ hg qrefresh hg qdiff --nodates cd .. +echo '% issue2025: qrefresh does not honor filtering options when tip != qtip' +hg init repo-2025 +cd repo-2025 +echo a > a +echo b > b +hg ci -qAm addab +echo a >> a +echo b >> b +hg qnew -f patch +hg up -qC 0 +echo c > c +hg ci -qAm addc +hg up -qC 1 +echo '% refresh with tip != qtip' +hg --config diff.nodates=1 qrefresh -I b 2>&1 \ + | sed 's/saving bundle.*/saving bundle.../g' +echo '% status after refresh' +hg st +echo '% b after refresh' +cat b +echo '% patch file after refresh' +cat .hg/patches/patch +cd .. + + echo % issue1441 with git patches hg init repo-1441-git cd repo-1441-git diff --git a/tests/test-mq-qrefresh.out b/tests/test-mq-qrefresh.out --- a/tests/test-mq-qrefresh.out +++ b/tests/test-mq-qrefresh.out @@ -259,6 +259,26 @@ diff -r 000000000000 b +++ b/b @@ -0,0 +1,1 @@ +a +% issue2025: qrefresh does not honor filtering options when tip != qtip +% refresh with tip != qtip +saving bundle... +adding branch +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files +% status after refresh +M a +% b after refresh +b +b +% patch file after refresh +diff -r 1a60229be7ac b +--- a/b ++++ b/b +@@ -1,1 +1,2 @@ + b ++b % issue1441 with git patches diff --git a/b b/b new file mode 100644 diff --git a/tests/test-mq.out b/tests/test-mq.out --- a/tests/test-mq.out +++ b/tests/test-mq.out @@ -392,16 +392,11 @@ copy to copy 1 files updated, 0 files merged, 2 files removed, 0 files unresolved created new head 2 files updated, 0 files merged, 1 files removed, 0 files unresolved -popping bar adding branch adding changesets adding manifests adding file changes added 1 changesets with 1 changes to 1 files -patch queue now empty -(working directory not at a head) -applying bar -now at: bar diff --git a/bar b/bar new file mode 100644 --- /dev/null @@ -427,16 +422,11 @@ diff --git a/foo b/baz % test file move chains in the slow path 1 files updated, 0 files merged, 2 files removed, 0 files unresolved 2 files updated, 0 files merged, 1 files removed, 0 files unresolved -popping bar adding branch adding changesets adding manifests adding file changes added 1 changesets with 1 changes to 1 files -patch queue now empty -(working directory not at a head) -applying bar -now at: bar diff --git a/foo b/bleh rename from foo rename to bleh