diff --git a/hgext/convert/common.py b/hgext/convert/common.py --- a/hgext/convert/common.py +++ b/hgext/convert/common.py @@ -419,7 +419,7 @@ def parsesplicemap(path): fp = open(path, 'r') for i, line in enumerate(fp): try: - child, parents = line.splitlines()[0].rstrip().rsplit(' ', 1) + child, parents = line.splitlines()[0].rstrip().split(' ', 1) parents = parents.replace(',', ' ').split() except ValueError: raise util.Abort(_('syntax error in %s(%d): child parent1' diff --git a/hgext/largefiles/reposetup.py b/hgext/largefiles/reposetup.py --- a/hgext/largefiles/reposetup.py +++ b/hgext/largefiles/reposetup.py @@ -118,8 +118,10 @@ def reposetup(ui, repo): # handle it -- thus gaining a big performance boost. lfdirstate = lfutil.openlfdirstate(ui, self) if match.files() and not match.anypats(): - matchedfiles = [f for f in match.files() if f in lfdirstate] - if not matchedfiles: + for f in lfdirstate: + if match(f): + break + else: return super(lfiles_repo, self).status(node1, node2, match, listignored, listclean, listunknown, listsubrepos) diff --git a/hgext/mq.py b/hgext/mq.py --- a/hgext/mq.py +++ b/hgext/mq.py @@ -1796,6 +1796,7 @@ class queue(object): if (len(files) > 1 or len(rev) > 1) and patchname: raise util.Abort(_('option "-n" not valid when importing multiple ' 'patches')) + imported = [] if rev: # If mq patches are applied, we can only import revisions # that form a linear path to qbase. @@ -1848,6 +1849,7 @@ class queue(object): self.applied.insert(0, se) self.added.append(patchname) + imported.append(patchname) patchname = None if rev and repo.ui.configbool('mq', 'secret', False): # if we added anything with --rev, we must move the secret root @@ -1902,9 +1904,11 @@ class queue(object): self.seriesdirty = True self.ui.warn(_("adding %s to series file\n") % patchname) self.added.append(patchname) + imported.append(patchname) patchname = None self.removeundo(repo) + return imported @command("qdelete|qremove|qrm", [('k', 'keep', None, _('keep patch file')), @@ -2030,15 +2034,16 @@ def qimport(ui, repo, *filename, **opts) try: q = repo.mq try: - q.qimport(repo, filename, patchname=opts.get('name'), - existing=opts.get('existing'), force=opts.get('force'), - rev=opts.get('rev'), git=opts.get('git')) + imported = q.qimport( + repo, filename, patchname=opts.get('name'), + existing=opts.get('existing'), force=opts.get('force'), + rev=opts.get('rev'), git=opts.get('git')) finally: q.savedirty() - if opts.get('push') and not opts.get('rev'): - return q.push(repo, None) + if imported and opts.get('push') and not opts.get('rev'): + return q.push(repo, imported[-1]) finally: lock.release() return 0 diff --git a/mercurial/commandserver.py b/mercurial/commandserver.py --- a/mercurial/commandserver.py +++ b/mercurial/commandserver.py @@ -186,6 +186,7 @@ class server(object): self.repo.baseui = copiedui self.repo.ui = self.repo.dirstate._ui = self.repoui.copy() self.repo.invalidate() + self.repo.invalidatedirstate() req = dispatch.request(args[:], copiedui, self.repo, self.cin, self.cout, self.cerr) diff --git a/mercurial/context.py b/mercurial/context.py --- a/mercurial/context.py +++ b/mercurial/context.py @@ -902,11 +902,11 @@ class workingctx(changectx): try: rejected = [] for f in files: - if self._repo.dirstate[f] != 'a': - self._repo.dirstate.remove(f) - elif f not in self._repo.dirstate: + if f not in self._repo.dirstate: self._repo.ui.warn(_("%s not tracked!\n") % join(f)) rejected.append(f) + elif self._repo.dirstate[f] != 'a': + self._repo.dirstate.remove(f) else: self._repo.dirstate.drop(f) return rejected diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py --- a/mercurial/localrepo.py +++ b/mercurial/localrepo.py @@ -949,6 +949,7 @@ class localrepository(repo.repository): self.store.write() if self._dirtyphases: phases.writeroots(self) + self._dirtyphases = False for k, ce in self._filecache.items(): if k == 'dirstate': continue @@ -1323,6 +1324,9 @@ class localrepository(repo.repository): # tag cache retrieval" case to work. self.invalidatecaches() + # Discard all cache entries to force reloading everything. + self._filecache.clear() + def walk(self, match, node=None): ''' walk recursively through the directory tree or a given diff --git a/mercurial/patch.py b/mercurial/patch.py --- a/mercurial/patch.py +++ b/mercurial/patch.py @@ -475,9 +475,15 @@ class workingbackend(fsbackend): addremoved = set(self.changed) for src, dst in self.copied: scmutil.dirstatecopy(self.ui, self.repo, wctx, src, dst) - addremoved.discard(src) - if (not self.similarity) and self.removed: + if self.removed: wctx.forget(sorted(self.removed)) + for f in self.removed: + if f not in self.repo.dirstate: + # File was deleted and no longer belongs to the + # dirstate, it was probably marked added then + # deleted, and should not be considered by + # addremove(). + addremoved.discard(f) if addremoved: cwd = self.repo.getcwd() if cwd: @@ -722,21 +728,19 @@ class patchfile(object): h = h.getnormalized() # fast case first, no offsets, no fuzz - old = h.old() - start = h.starta + self.offset - # zero length hunk ranges already have their start decremented - if h.lena: - start -= 1 - orig_start = start + old, oldstart, new, newstart = h.fuzzit(0, False) + oldstart += self.offset + orig_start = oldstart # if there's skew we want to emit the "(offset %d lines)" even # when the hunk cleanly applies at start + skew, so skip the # fast case code - if self.skew == 0 and diffhelpers.testhunk(old, self.lines, start) == 0: + if (self.skew == 0 and + diffhelpers.testhunk(old, self.lines, oldstart) == 0): if self.remove: self.backend.unlink(self.fname) else: - self.lines[start : start + h.lena] = h.new() - self.offset += h.lenb - h.lena + self.lines[oldstart:oldstart + len(old)] = new + self.offset += len(new) - len(old) self.dirty = True return 0 @@ -744,23 +748,23 @@ class patchfile(object): self.hash = {} for x, s in enumerate(self.lines): self.hash.setdefault(s, []).append(x) - if h.hunk[-1][0] != ' ': - # if the hunk tried to put something at the bottom of the file - # override the start line and use eof here - search_start = len(self.lines) - else: - search_start = orig_start + self.skew for fuzzlen in xrange(3): for toponly in [True, False]: - old = h.old(fuzzlen, toponly) + old, oldstart, new, newstart = h.fuzzit(fuzzlen, toponly) + oldstart = oldstart + self.offset + self.skew + oldstart = min(oldstart, len(self.lines)) + if old: + cand = self.findlines(old[0][1:], oldstart) + else: + # Only adding lines with no or fuzzed context, just + # take the skew in account + cand = [oldstart] - cand = self.findlines(old[0][1:], search_start) for l in cand: - if diffhelpers.testhunk(old, self.lines, l) == 0: - newlines = h.new(fuzzlen, toponly) - self.lines[l : l + len(old)] = newlines - self.offset += len(newlines) - len(old) + if not old or diffhelpers.testhunk(old, self.lines, l) == 0: + self.lines[l : l + len(old)] = new + self.offset += len(new) - len(old) self.skew = l - orig_start self.dirty = True offset = l - orig_start - fuzzlen @@ -965,11 +969,11 @@ class hunk(object): def complete(self): return len(self.a) == self.lena and len(self.b) == self.lenb - def fuzzit(self, l, fuzz, toponly): + def _fuzzit(self, old, new, fuzz, toponly): # this removes context lines from the top and bottom of list 'l'. It # checks the hunk to make sure only context lines are removed, and then # returns a new shortened list of lines. - fuzz = min(fuzz, len(l)-1) + fuzz = min(fuzz, len(old)) if fuzz: top = 0 bot = 0 @@ -987,26 +991,21 @@ class hunk(object): else: break - # top and bot now count context in the hunk - # adjust them if either one is short - context = max(top, bot, 3) - if bot < context: - bot = max(0, fuzz - (context - bot)) - else: - bot = min(fuzz, bot) - if top < context: - top = max(0, fuzz - (context - top)) - else: - top = min(fuzz, top) + bot = min(fuzz, bot) + top = min(fuzz, top) + return old[top:len(old)-bot], new[top:len(new)-bot], top + return old, new, 0 - return l[top:len(l)-bot] - return l - - def old(self, fuzz=0, toponly=False): - return self.fuzzit(self.a, fuzz, toponly) - - def new(self, fuzz=0, toponly=False): - return self.fuzzit(self.b, fuzz, toponly) + def fuzzit(self, fuzz, toponly): + old, new, top = self._fuzzit(self.a, self.b, fuzz, toponly) + oldstart = self.starta + top + newstart = self.startb + top + # zero length hunk ranges already have their start decremented + if self.lena: + oldstart -= 1 + if self.lenb: + newstart -= 1 + return old, oldstart, new, newstart class binhunk(object): 'A binary patch file. Only understands literals so far.' diff --git a/mercurial/scmutil.py b/mercurial/scmutil.py --- a/mercurial/scmutil.py +++ b/mercurial/scmutil.py @@ -803,6 +803,10 @@ class filecache(object): return self def __get__(self, obj, type=None): + # do we need to check if the file changed? + if self.name in obj.__dict__: + return obj.__dict__[self.name] + entry = obj._filecache.get(self.name) if entry: @@ -818,5 +822,16 @@ class filecache(object): obj._filecache[self.name] = entry - setattr(obj, self.name, entry.obj) + obj.__dict__[self.name] = entry.obj return entry.obj + + def __set__(self, obj, value): + if self.name in obj._filecache: + obj._filecache[self.name].obj = value # update cached copy + obj.__dict__[self.name] = value # update copy returned by obj.x + + def __delete__(self, obj): + try: + del obj.__dict__[self.name] + except KeyError: + raise AttributeError, self.name diff --git a/mercurial/statichttprepo.py b/mercurial/statichttprepo.py --- a/mercurial/statichttprepo.py +++ b/mercurial/statichttprepo.py @@ -112,6 +112,7 @@ class statichttprepository(localrepo.loc self.spath = self.store.path self.sopener = self.store.opener self.sjoin = self.store.join + self._filecache = {} self.manifest = manifest.manifest(self.sopener) self.changelog = changelog.changelog(self.sopener) @@ -122,7 +123,6 @@ class statichttprepository(localrepo.loc self.encodepats = None self.decodepats = None self.capabilities.difference_update(["pushkey"]) - self._filecache = {} def url(self): return self._url diff --git a/tests/test-commandserver.py b/tests/test-commandserver.py --- a/tests/test-commandserver.py +++ b/tests/test-commandserver.py @@ -27,6 +27,7 @@ def readchannel(server): def runcommand(server, args, output=sys.stdout, error=sys.stderr, input=None): print ' runcommand', ' '.join(args) + sys.stdout.flush() server.stdin.write('runcommand\n') writeblock(server, '\0'.join(args)) @@ -56,6 +57,7 @@ def check(func, repopath=None): print print 'testing %s:' % func.__name__ print + sys.stdout.flush() server = connect(repopath) try: return func(server) @@ -163,8 +165,10 @@ def outsidechanges(server): f = open('a', 'ab') f.write('a\n') f.close() + runcommand(server, ['status']) os.system('hg ci -Am2') runcommand(server, ['tip']) + runcommand(server, ['status']) def bookmarks(server): readchannel(server) @@ -179,6 +183,13 @@ def bookmarks(server): os.system('hg upd bm1 -q') runcommand(server, ['bookmarks']) + runcommand(server, ['bookmarks', 'bm3']) + f = open('a', 'ab') + f.write('a\n') + f.close() + runcommand(server, ['commit', '-Amm']) + runcommand(server, ['bookmarks']) + def tagscache(server): readchannel(server) runcommand(server, ['id', '-t', '-r', '0']) @@ -191,6 +202,16 @@ def setphase(server): os.system('hg phase -r . -p') runcommand(server, ['phase', '-r', '.']) +def rollback(server): + readchannel(server) + runcommand(server, ['phase', '-r', '.', '-p']) + f = open('a', 'ab') + f.write('a\n') + f.close() + runcommand(server, ['commit', '-Am.']) + runcommand(server, ['rollback']) + runcommand(server, ['phase', '-r', '.']) + if __name__ == '__main__': os.system('hg init') @@ -210,3 +231,4 @@ if __name__ == '__main__': check(bookmarks) check(tagscache) check(setphase) + check(rollback) diff --git a/tests/test-commandserver.py.out b/tests/test-commandserver.py.out --- a/tests/test-commandserver.py.out +++ b/tests/test-commandserver.py.out @@ -4,10 +4,10 @@ testing hellomessage: o, 'capabilities: getencoding runcommand\nencoding: ***' runcommand id 000000000000 tip -abort: unknown command unknowncommand testing unknowncommand: +abort: unknown command unknowncommand testing checkruncommand: @@ -93,6 +93,8 @@ eff892de26ec tip testing outsidechanges: + runcommand status +M a runcommand tip changeset: 1:d3a0a68be6de tag: tip @@ -100,6 +102,7 @@ user: test date: Thu Jan 01 00:00:00 1970 +0000 summary: 2 + runcommand status testing bookmarks: @@ -111,6 +114,12 @@ no bookmarks set runcommand bookmarks * bm1 1:d3a0a68be6de bm2 1:d3a0a68be6de + runcommand bookmarks bm3 + runcommand commit -Amm + runcommand bookmarks + bm1 1:d3a0a68be6de + bm2 1:d3a0a68be6de + * bm3 2:aef17e88f5f0 testing tagscache: @@ -122,6 +131,17 @@ foo testing setphase: runcommand phase -r . -2: draft +3: draft runcommand phase -r . -2: public +3: public + +testing rollback: + + runcommand phase -r . -p +no phases changed + runcommand commit -Am. + runcommand rollback +repository tip rolled back to revision 3 (undo commit) +working directory now based on revision 3 + runcommand phase -r . +3: public diff --git a/tests/test-convert-splicemap.t b/tests/test-convert-splicemap.t --- a/tests/test-convert-splicemap.t +++ b/tests/test-convert-splicemap.t @@ -123,11 +123,11 @@ We want 2 to depend on 1 and 3. Since 3 the bug should be exhibited with all conversion orders. $ cat > ../splicemap < $(hg id -r 2 -i --debug) $(hg id -r 1 -i --debug),$(hg id -r 3 -i --debug) + > $(hg id -r 2 -i --debug) $(hg id -r 1 -i --debug), $(hg id -r 3 -i --debug) > EOF $ cd .. $ cat splicemap - 7c364e7fa7d70ae525610c016317ed717b519d97 717d54d67e6c31fd75ffef2ff3042bdd98418437,102a90ea7b4a3361e4082ed620918c261189a36a + 7c364e7fa7d70ae525610c016317ed717b519d97 717d54d67e6c31fd75ffef2ff3042bdd98418437, 102a90ea7b4a3361e4082ed620918c261189a36a Test regular conversion diff --git a/tests/test-import-bypass.t b/tests/test-import-bypass.t --- a/tests/test-import-bypass.t +++ b/tests/test-import-bypass.t @@ -111,7 +111,15 @@ Test unsupported combinations Test commit editor - $ hg diff -c 1 > ../test.diff + $ cat > ../test.diff < diff -r 07f494440405 -r 4e322f7ce8e3 a + > --- a/a Thu Jan 01 00:00:00 1970 +0000 + > +++ b/a Thu Jan 01 00:00:00 1970 +0000 + > @@ -1,1 +1,2 @@ + > -a + > +b + > +c + > EOF $ HGEDITOR=cat hg import --bypass ../test.diff applying ../test.diff @@ -138,7 +146,7 @@ Test patch.eol is handled $ hg --config patch.eol=auto import -d '0 0' -m 'test patch.eol' --bypass ../test.diff applying ../test.diff $ shortlog - o 3:d7805b4d2cb3 test 0 0 - default - test patch.eol + o 3:c606edafba99 test 0 0 - default - test patch.eol | @ 2:872023de769d test 0 0 - default - makeacrlf | diff --git a/tests/test-import-git.t b/tests/test-import-git.t --- a/tests/test-import-git.t +++ b/tests/test-import-git.t @@ -402,6 +402,23 @@ Renames and strip A b a R a + +Renames, similarity and git diff + + $ hg revert -aC + undeleting a + forgetting b + $ rm b + $ hg import --similarity 90 --no-commit - < diff --git a/a b/b + > rename from a + > rename to b + > EOF + applying patch from stdin + $ hg st --copies + A b + a + R a $ cd .. Pure copy with existing destination diff --git a/tests/test-import.t b/tests/test-import.t --- a/tests/test-import.t +++ b/tests/test-import.t @@ -445,7 +445,7 @@ Test fuzziness (ambiguous patch location $ hg import --no-commit -v fuzzy-tip.patch applying fuzzy-tip.patch patching file a - Hunk #1 succeeded at 1 with fuzz 2 (offset -2 lines). + Hunk #1 succeeded at 2 with fuzz 1 (offset 0 lines). applied to working directory $ hg revert -a reverting a @@ -462,7 +462,7 @@ test fuzziness with eol=auto $ hg --config patch.eol=auto import --no-commit -v fuzzy-tip.patch applying fuzzy-tip.patch patching file a - Hunk #1 succeeded at 1 with fuzz 2 (offset -2 lines). + Hunk #1 succeeded at 2 with fuzz 1 (offset 0 lines). applied to working directory $ cd .. @@ -663,7 +663,6 @@ test import with similarity and git and applying ../rename.diff patching file a patching file b - removing a adding b recording removal of a as rename to b (88% similar) applied to working directory @@ -679,7 +678,6 @@ test import with similarity and git and applying ../rename.diff patching file a patching file b - removing a adding b applied to working directory $ hg st -C @@ -998,3 +996,102 @@ import a unified diff with no lines of c c2 c3 c4 + +Test corner case involving fuzz and skew + + $ hg init morecornercases + $ cd morecornercases + + $ cat > 01-no-context-beginning-of-file.diff < diff --git a/a b/a + > --- a/a + > +++ b/a + > @@ -1,0 +1,1 @@ + > +line + > EOF + + $ cat > 02-no-context-middle-of-file.diff < diff --git a/a b/a + > --- a/a + > +++ b/a + > @@ -1,1 +1,1 @@ + > -2 + > +add some skew + > @@ -2,0 +2,1 @@ + > +line + > EOF + + $ cat > 03-no-context-end-of-file.diff < diff --git a/a b/a + > --- a/a + > +++ b/a + > @@ -10,0 +10,1 @@ + > +line + > EOF + + $ cat > 04-middle-of-file-completely-fuzzed.diff < diff --git a/a b/a + > --- a/a + > +++ b/a + > @@ -1,1 +1,1 @@ + > -2 + > +add some skew + > @@ -2,2 +2,3 @@ + > not matching, should fuzz + > ... a bit + > +line + > EOF + + $ cat > a < 1 + > 2 + > 3 + > 4 + > EOF + $ hg ci -Am adda a + $ for p in *.diff; do + > hg import -v --no-commit $p + > cat a + > hg revert -aqC a + > # patch -p1 < $p + > # cat a + > # hg revert -aC a + > done + applying 01-no-context-beginning-of-file.diff + patching file a + applied to working directory + 1 + line + 2 + 3 + 4 + applying 02-no-context-middle-of-file.diff + patching file a + Hunk #1 succeeded at 2 (offset 1 lines). + Hunk #2 succeeded at 4 (offset 1 lines). + applied to working directory + 1 + add some skew + 3 + line + 4 + applying 03-no-context-end-of-file.diff + patching file a + Hunk #1 succeeded at 5 (offset -6 lines). + applied to working directory + 1 + 2 + 3 + 4 + line + applying 04-middle-of-file-completely-fuzzed.diff + patching file a + Hunk #1 succeeded at 2 (offset 1 lines). + Hunk #2 succeeded at 5 with fuzz 2 (offset 1 lines). + applied to working directory + 1 + add some skew + 3 + 4 + line + diff --git a/tests/test-largefiles.t b/tests/test-largefiles.t --- a/tests/test-largefiles.t +++ b/tests/test-largefiles.t @@ -948,4 +948,50 @@ Symlink to a large largefile should beha $ test -L largelink $ cd .. +test for pattern matching on 'hg status': +to boost performance, largefiles checks whether specified patterns are +related to largefiles in working directory (NOT to STANDIN) or not. + $ hg init statusmatch + $ cd statusmatch + + $ mkdir -p a/b/c/d + $ echo normal > a/b/c/d/e.normal.txt + $ hg add a/b/c/d/e.normal.txt + $ echo large > a/b/c/d/e.large.txt + $ hg add --large a/b/c/d/e.large.txt + $ mkdir -p a/b/c/x + $ echo normal > a/b/c/x/y.normal.txt + $ hg add a/b/c/x/y.normal.txt + $ hg commit -m 'add files' + Invoking status precommit hook + A a/b/c/d/e.large.txt + A a/b/c/d/e.normal.txt + A a/b/c/x/y.normal.txt + +(1) no pattern: no performance boost + $ hg status -A + C a/b/c/d/e.large.txt + C a/b/c/d/e.normal.txt + C a/b/c/x/y.normal.txt + +(2) pattern not related to largefiles: performance boost + $ hg status -A a/b/c/x + C a/b/c/x/y.normal.txt + +(3) pattern related to largefiles: no performance boost + $ hg status -A a/b/c/d + C a/b/c/d/e.large.txt + C a/b/c/d/e.normal.txt + +(4) pattern related to STANDIN (not to largefiles): performance boost + $ hg status -A .hglf/a + C .hglf/a/b/c/d/e.large.txt + +(5) mixed case: no performance boost + $ hg status -A a/b/c/x a/b/c/d + C a/b/c/d/e.large.txt + C a/b/c/d/e.normal.txt + C a/b/c/x/y.normal.txt + + $ cd .. diff --git a/tests/test-mq-merge.t b/tests/test-mq-merge.t --- a/tests/test-mq-merge.t +++ b/tests/test-mq-merge.t @@ -125,12 +125,10 @@ Merge: merging with queue at: $TESTTMP/t2/.hg/refqueue (glob) applying patcha patching file a - Hunk #1 FAILED at 0 - 1 out of 1 hunks FAILED -- saving rejects to file a.rej - patch failed, unable to continue (try -v) - patch failed, rejects left in working dir + Hunk #1 succeeded at 2 with fuzz 1 (offset 0 lines). + fuzz found when applying patch, stopping patch didn't work out, merging patcha - 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + 1 files updated, 0 files merged, 1 files removed, 0 files unresolved 0 files updated, 2 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) applying patcha2 diff --git a/tests/test-mq-qimport.t b/tests/test-mq-qimport.t --- a/tests/test-mq-qimport.t +++ b/tests/test-mq-qimport.t @@ -153,21 +153,41 @@ qimport CRLF diff try to import --push - $ echo another >> b - $ hg diff > another.diff - $ hg up -C - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved - $ hg qimport --push another.diff - adding another.diff to series file - applying another.diff - now at: another.diff + $ cat > appendfoo.diff < append foo + > + > diff -r 07f494440405 -r 261500830e46 baz + > --- /dev/null Thu Jan 01 00:00:00 1970 +0000 + > +++ b/baz Thu Jan 01 00:00:00 1970 +0000 + > @@ -0,0 +1,1 @@ + > +foo + > EOF + + $ cat > appendbar.diff < append bar + > + > diff -r 07f494440405 -r 261500830e46 baz + > --- a/baz Thu Jan 01 00:00:00 1970 +0000 + > +++ b/baz Thu Jan 01 00:00:00 1970 +0000 + > @@ -1,1 +1,2 @@ + > foo + > +bar + > EOF + + $ hg qimport --push appendfoo.diff appendbar.diff + adding appendfoo.diff to series file + adding appendbar.diff to series file + applying appendfoo.diff + applying appendbar.diff + now at: appendbar.diff $ hg qfin -a patch b.diff finalized without changeset message - patch another.diff finalized without changeset message - $ hg qimport -rtip -P + $ hg qimport -r 'p1(.)::' -P $ hg qpop -a + popping 3.diff popping 2.diff patch queue now empty + $ hg qdel 3.diff $ hg qdel -k 2.diff qimport -e