Re: [PATCH] py3: make keys of keyword arguments strings
Excerpts from Pulkit Goyal's message of 2016-12-07 10:43:24 +0530: > Also after this patch, `hg version` now runs on Python 3.5. Hurray! LGTM, having `hg version` running is really good news. Congrats! -- ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: [PATCH] push: add a message when pushing phases but not changes
Excerpts from Jeremy Wall (zaphar)'s message of 2016-12-02 13:12:20 -0600: > diff -r 9e29d4e4e08b -r 9cb1540e417d tests/test-phases-exchange.t > --- a/tests/test-phases-exchange.tTue Nov 29 04:11:05 2016 -0800 > +++ b/tests/test-phases-exchange.tWed Nov 30 15:55:42 2016 -0600 > @@ -384,7 +384,7 @@ >$ hg push ../alpha # from nu >pushing to ../alpha >searching for changes > - no changes found > + sending phase public for 145e75495359 >[1] I suppose now, that we are addmitting that there was something to push we should change the RC to 0. Question to others: would such change be considered a BC-breakage or a fix? -- ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: [PATCH 3 of 3] py3: make a bytes version of getopt.getopt()
The whole series looks good to me. Best, Mateusz -- ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 5 of 8 evolve-ext, V2] metaedit: use faster setparents instead of full update
# HG changeset patch # User Mateusz Kwapich <mitran...@fb.com> # Date 1479325623 0 # Wed Nov 16 19:47:03 2016 + # Branch stable # Node ID 651b6258e993f6d45e4ef7b324303201db5639b2 # Parent d4a8c386a14b3e455e60fffec7fb315f9629ff12 metaedit: use faster setparents instead of full update The working copy is not changing so there is no need to extra status call. This makes metaedit work on dirty wc. diff --git a/hgext/evolve.py b/hgext/evolve.py --- a/hgext/evolve.py +++ b/hgext/evolve.py @@ -3303,7 +3303,7 @@ def metaedit(ui, repo, *revs, **opts): if opts['fold']: ui.status('%i changesets folded\n' % len(revs)) if newp1 is not None: -hg.update(repo, newp1) +repo.setparents(newp1) finally: lockmod.release(lock, wlock) diff --git a/tests/test-evolve.t b/tests/test-evolve.t --- a/tests/test-evolve.t +++ b/tests/test-evolve.t @@ -1489,7 +1489,6 @@ check that metaedit respects allowunstab abort: cannot fold chain not ending with a head or with branching [255] $ hg metaedit --user foobar - 0 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg log --template '{rev}: {author}\n' -r '42:' --hidden 42: test 43: foobar @@ -1497,7 +1496,6 @@ check that metaedit respects allowunstab 43: foobar $ HGEDITOR="sed -i'' -e 's/safely/quickly/g'" hg metaedit '.^::.' - 0 files updated, 0 files merged, 0 files removed, 0 files unresolved $ HGEDITOR=cat hg metaedit '.^::.' --fold HG: This is a fold of 2 changesets. @@ -1519,7 +1517,6 @@ check that metaedit respects allowunstab HG: changed a HG: changed newfile 2 changesets folded - 0 files updated, 0 files merged, 0 files removed, 0 files unresolved $ glog -r . @ 45:ca7a9e928b25@default(draft) amended @@ -1553,7 +1550,6 @@ no new commit is created here because th TODO: don't create a new commit in this case $ hg metaedit --config defaults.metaedit= - 0 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg log -r '.^::.' --template '{rev}: {desc|firstline}\n' 36: add uu 46: amended @@ -1570,8 +1566,11 @@ TODO: don't create a new commit in this 47: foobar2 $ hg diff -r 45 -r 46 --hidden -'fold' one commit +'fold' one commit with dirty wc + $ echo x > newfile $ hg metaedit 39 --fold --user foobar3 1 changesets folded $ hg log -r 48 --template '{rev}: {author}\n' 48: foobar3 + $ hg st -amr + M newfile ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 6 of 8 evolve-ext, V2] metaedit: rewrite rewritemeta to reuse manifests
# HG changeset patch # User Mateusz Kwapich <mitran...@fb.com> # Date 1479405307 28800 # Thu Nov 17 09:55:07 2016 -0800 # Branch stable # Node ID 02a29df6827d1dae26b885c9c6c9d56be33ecd00 # Parent 651b6258e993f6d45e4ef7b324303201db5639b2 metaedit: rewrite rewritemeta to reuse manifests diff --git a/hgext/evolve.py b/hgext/evolve.py --- a/hgext/evolve.py +++ b/hgext/evolve.py @@ -908,11 +908,47 @@ def rewrite(repo, old, updates, head, ne lockmod.release(tr, lock, wlock) def metarewrite(repo, old, newbases, commitopts): -'''Like rewrite but affects only the changeset metadata.''' -# TODO: reuse the manifest for speed -newid, created = rewrite(repo, old, [old], old, newbases, - commitopts=commitopts) -return newid, created +"""Return (nodeid, created) where nodeid is the identifier of the +changeset generated by the rewrite process, and created is True if +nodeid was actually created. If created is False, nodeid +references a changeset existing before the rewrite call. +""" +wlock = lock = tr = None +try: +wlock = repo.wlock() +lock = repo.lock() +tr = repo.transaction('rewrite') +updatebookmarks = _bookmarksupdater(repo, old.node(), tr) + +message = cmdutil.logmessage(repo.ui, commitopts) +if not message: +message = old.description() + +user = commitopts.get('user') or old.user() +date = commitopts.get('date') or None # old.date() +extra = dict(commitopts.get('extra', old.extra())) +extra['branch'] = old.branch() + +new = context.memlightctx(repo, + old, + parents=newbases, + text=message, + user=user, + date=date, + extra=extra) + +if commitopts.get('edit'): +new._text = cmdutil.commitforceeditor(repo, new, []) +revcount = len(repo) +newid = repo.commitctx(new) +new = repo[newid] +created = len(repo) != revcount +updatebookmarks(newid) + +tr.close() +return newid, created +finally: +lockmod.release(tr, lock, wlock) class MergeFailure(error.Abort): pass ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 8 of 8 evolve-ext, V2] metaedit: make metaedit work nice when new unstable commits are disallowed
# HG changeset patch # User Mateusz Kwapich <mitran...@fb.com> # Date 1481041359 28800 # Tue Dec 06 08:22:39 2016 -0800 # Branch stable # Node ID 50ef3a1f97fb8faf1506d7751ba3f7ffe7e0e7db # Parent a7cc11231c424e435252e7388dd05b139e766af2 metaedit: make metaedit work nice when new unstable commits are disallowed Now, when metaedit is using a lightweight commits (reusing manifest) we can easily afford to actually rewrite all descendants if we attempt to metaedit a commit in the middle of a stack. diff --git a/hgext/evolve.py b/hgext/evolve.py --- a/hgext/evolve.py +++ b/hgext/evolve.py @@ -929,7 +929,7 @@ def metarewrite(repo, old, newbases, com extra = dict(commitopts.get('extra', old.extra())) extra['branch'] = old.branch() -new = context.memlightctx(repo, +new = context.metadataonlyctx(repo, old, parents=newbases, text=message, @@ -3310,9 +3310,14 @@ def metaedit(ui, repo, *revs, **opts): replacemap = {} # we need topological order allctx = sorted(allctx, key=lambda c: c.rev()) -for c in allctx: -if commitopts['edit']: -commitopts['message'] = \ +# all descendats that can be safely rewritten +newunstable = _disallowednewunstable(repo, revs) +newunstablectx = sorted([repo[r] for r in newunstable], + key=lambda c: c.rev()) + +def _rewritesingle(c, _commitopts): +if _commitopts.get('edit', False): +_commitopts['message'] = \ "HG: Commit message of changeset %s\n%s" %\ (str(c), c.description()) bases = [ @@ -3320,9 +3325,13 @@ def metaedit(ui, repo, *revs, **opts): replacemap.get(c.p2().node(), c.p2().node()), ] newid, created = metarewrite(repo, c, bases, - commitopts=commitopts) + commitopts=_commitopts) if created: replacemap[c.node()] = newid +for c in allctx: +_rewritesingle(c, commitopts) +for c in newunstablectx: +_rewritesingle(c, {}) if p1.node() in replacemap: newp1 = replacemap[p1.node()] diff --git a/tests/test-evolve.t b/tests/test-evolve.t --- a/tests/test-evolve.t +++ b/tests/test-evolve.t @@ -1574,3 +1574,54 @@ TODO: don't create a new commit in this 48: foobar3 $ hg st -amr M newfile + +metaedit a commit in the middle of the stack: + $ glog -r '(.^)::' + o 48:2603fdb715ea@default(draft) will cause conflict at evolve + | + | o 47:f72774e0c084@default(draft) amended + | | + x | 38:f8e30e9317aa@default(draft) will be evolved safely + | | + x | 37:36030b147271@default(draft) will be amended + |/ + @ 36:43c3f5ef149f@default(draft) add uu + | + o 35:7a555adf2b4a@default(draft) _pp + | + ~ + $ hg metaedit -m "add uu (with metaedit)" --config 'experimental.evolution=createmarkers, allnewcommands' + abort: cannot edit commit information in the middle of a stack + (f72774e0c084 will be affected) + [255] + $ glog -r '(.^)::' + o 48:2603fdb715ea@default(draft) will cause conflict at evolve + | + | o 47:f72774e0c084@default(draft) amended + | | + x | 38:f8e30e9317aa@default(draft) will be evolved safely + | | + x | 37:36030b147271@default(draft) will be amended + |/ + @ 36:43c3f5ef149f@default(draft) add uu + | + o 35:7a555adf2b4a@default(draft) _pp + | + ~ + $ hg metaedit -m "add uu (with metaedit)" + $ glog -r '(.^)::' + @ 49:d56769f6b2a5@default(draft) add uu (with metaedit) + | + | o 48:2603fdb715ea@default(draft) will cause conflict at evolve + | | + | | o 47:f72774e0c084@default(draft) amended + | | | + | x | 38:f8e30e9317aa@default(draft) will be evolved safely + | | | + | x | 37:36030b147271@default(draft) will be amended + | |/ + | x 36:43c3f5ef149f@default(draft) add uu + |/ + o 35:7a555adf2b4a@default(draft) _pp + | + ~ ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 7 of 8 evolve-ext, V2] evolve: make the disallowing new unstable more accurate
# HG changeset patch # User Mateusz Kwapich <mitran...@fb.com> # Date 1481040901 28800 # Tue Dec 06 08:15:01 2016 -0800 # Branch stable # Node ID a7cc11231c424e435252e7388dd05b139e766af2 # Parent 02a29df6827d1dae26b885c9c6c9d56be33ecd00 evolve: make the disallowing new unstable more accurate If the changesets are already unstable don't trigger disallownewunstable diff --git a/hgext/evolve.py b/hgext/evolve.py --- a/hgext/evolve.py +++ b/hgext/evolve.py @@ -3365,7 +3365,7 @@ def _disallowednewunstable(repo, revs): allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt) if allowunstable: return revset.baseset() -return repo.revs("(%ld::) - %ld", revs, revs) +return repo.revs("(%ld::) - %ld - unstable() - obsolete()", revs, revs) @eh.wrapcommand('graft') def graftwrapper(orig, ui, repo, *revs, **kwargs): ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 3 of 8 evolve-ext, V2] metaedit: extend the functionality to support editing multiple commits
# HG changeset patch # User Mateusz Kwapich <mitran...@fb.com> # Date 1481029159 28800 # Tue Dec 06 04:59:19 2016 -0800 # Branch stable # Node ID 2495e78f6db6bb8d885121c45c16b9ba9266d1a1 # Parent b8cbdf6356d770b70df92b13095c11438b8ad1a1 metaedit: extend the functionality to support editing multiple commits diff --git a/hgext/evolve.py b/hgext/evolve.py --- a/hgext/evolve.py +++ b/hgext/evolve.py @@ -3282,17 +3282,29 @@ def metaedit(ui, repo, *revs, **opts): else: ui.status(_("nothing changed\n")) else: -if commitopts['edit']: -commitopts['message'] = head.description() -newid, created = rewrite(repo, root, allctx, head, - [root.p1().node(), root.p2().node()], - commitopts=commitopts) -if created: -if p1.rev() in revs: -newp1 = newid -phases.retractboundary(repo, tr, targetphase, [newid]) -obsolete.createmarkers(repo, [(ctx, (repo[newid],)) - for ctx in allctx]) +replacemap = {} +# we need topological order +allctx = sorted(allctx, key=lambda c: c.rev()) +for c in allctx: +if commitopts['edit']: +commitopts['message'] = \ +"HG: Commit message of changeset %s\n%s" %\ +(str(c), c.description()) +bases = [ +replacemap.get(c.p1().node(), c.p1().node()), +replacemap.get(c.p2().node(), c.p2().node()), +] +newid, created = metarewrite(repo, c, bases, + commitopts=commitopts) +if created: +replacemap[c.node()] = newid + +if p1.node() in replacemap: +newp1 = replacemap[p1.node()] +if len(replacemap) > 0: +obsolete.createmarkers(repo, [(repo[old], (repo[new],)) +for old, new in replacemap.iteritems()]) +# TODO: set poroper phase boundaries else: ui.status(_("nothing changed\n")) tr.close() ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 1 of 8 evolve-ext, V2] metaedit: add a helper function for just metadata rewrites
# HG changeset patch # User Mateusz Kwapich <mitran...@fb.com> # Date 1481028829 28800 # Tue Dec 06 04:53:49 2016 -0800 # Branch stable # Node ID 78b75ed14103cee05ed13948025310919adde559 # Parent 727c7211c810d304ebf92b32db7ecf697ce46ac6 metaedit: add a helper function for just metadata rewrites It will be used by metaedit. diff --git a/hgext/evolve.py b/hgext/evolve.py --- a/hgext/evolve.py +++ b/hgext/evolve.py @@ -907,6 +907,13 @@ def rewrite(repo, old, updates, head, ne finally: lockmod.release(tr, lock, wlock) +def metarewrite(repo, old, newbases, commitopts): +'''Like rewrite but affects only the changeset metadata.''' +# TODO: reuse the manifest for speed +newid, created = rewrite(repo, old, [old], old, newbases, + commitopts=commitopts) +return newid, created + class MergeFailure(error.Abort): pass ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 4 of 8 evolve-ext, V2] metaedit: remove the code gating the new metaedit feature and test it
# HG changeset patch # User Mateusz Kwapich <mitran...@fb.com> # Date 1481038866 28800 # Tue Dec 06 07:41:06 2016 -0800 # Branch stable # Node ID d4a8c386a14b3e455e60fffec7fb315f9629ff12 # Parent 2495e78f6db6bb8d885121c45c16b9ba9266d1a1 metaedit: remove the code gating the new metaedit feature and test it diff --git a/hgext/evolve.py b/hgext/evolve.py --- a/hgext/evolve.py +++ b/hgext/evolve.py @@ -3220,17 +3220,6 @@ def metaedit(ui, repo, *revs, **opts): lock = repo.lock() revs = scmutil.revrange(repo, revs) -if not opts['fold'] and len(revs) > 1: -# TODO: handle multiple revisions. This is somewhat tricky because -# if we want to edit a series of commits: -# -# a b c -# -# we need to rewrite a first, then directly rewrite b on top of the -# new a, then rewrite c on top of the new b. So we need to handle -# revisions in topological order. -raise error.Abort(_('editing multiple revisions without --fold is ' -'not currently supported')) if opts['fold']: root, head = _foldcheck(repo, revs) diff --git a/tests/test-evolve.t b/tests/test-evolve.t --- a/tests/test-evolve.t +++ b/tests/test-evolve.t @@ -1496,10 +1496,8 @@ check that metaedit respects allowunstab $ hg log --template '{rev}: {author}\n' -r . 43: foobar -TODO: support this - $ hg metaedit '.^::.' - abort: editing multiple revisions without --fold is not currently supported - [255] + $ HGEDITOR="sed -i'' -e 's/safely/quickly/g'" hg metaedit '.^::.' + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved $ HGEDITOR=cat hg metaedit '.^::.' --fold HG: This is a fold of 2 changesets. @@ -1507,9 +1505,9 @@ TODO: support this amended - HG: Commit message of changeset 43. + HG: Commit message of changeset 44. - will be evolved safely + will be evolved quickly @@ -1524,16 +1522,17 @@ TODO: support this 0 files updated, 0 files merged, 0 files removed, 0 files unresolved $ glog -r . - @ 44:41bf1183869c@default(draft) amended + @ 45:ca7a9e928b25@default(draft) amended | ~ no new commit is created here because the date is the same $ HGEDITOR=cat hg metaedit + HG: Commit message of changeset ca7a9e928b25 amended - will be evolved safely + will be evolved quickly HG: Enter commit message. Lines beginning with 'HG:' are removed. @@ -1546,7 +1545,7 @@ no new commit is created here because th nothing changed $ glog -r '.^::.' - @ 44:41bf1183869c@default(draft) amended + @ 45:ca7a9e928b25@default(draft) amended | o 36:43c3f5ef149f@default(draft) add uu | @@ -1557,21 +1556,22 @@ TODO: don't create a new commit in this 0 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg log -r '.^::.' --template '{rev}: {desc|firstline}\n' 36: add uu - 45: amended + 46: amended $ hg up .^ 2 files updated, 0 files merged, 0 files removed, 0 files unresolved - $ hg metaedit --user foobar2 45 + $ hg metaedit --user foobar2 46 $ hg log --template '{rev}: {author}\n' -r '42:' --hidden 42: test 43: foobar - 44: test + 44: foobar 45: test - 46: foobar2 + 46: test + 47: foobar2 $ hg diff -r 45 -r 46 --hidden 'fold' one commit $ hg metaedit 39 --fold --user foobar3 1 changesets folded - $ hg log -r 47 --template '{rev}: {author}\n' - 47: foobar3 + $ hg log -r 48 --template '{rev}: {author}\n' + 48: foobar3 ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 2 of 8 evolve-ext, V2] metaedit: separate the fold and no-fold logic
# HG changeset patch # User Mateusz Kwapich <mitran...@fb.com> # Date 1481029031 28800 # Tue Dec 06 04:57:11 2016 -0800 # Branch stable # Node ID b8cbdf6356d770b70df92b13095c11438b8ad1a1 # Parent 78b75ed14103cee05ed13948025310919adde559 metaedit: separate the fold and no-fold logic As I want to implement the metaedit for multiple changesets I need to separate the logic doing a metaedit with fold from the logic that is doing metaedit without fold. This change intentionally copies code so it's easier to follow when its modified in the next changeset in that series. diff --git a/hgext/evolve.py b/hgext/evolve.py --- a/hgext/evolve.py +++ b/hgext/evolve.py @@ -3257,29 +3257,44 @@ def metaedit(ui, repo, *revs, **opts): if commitopts.get('message') or commitopts.get('logfile'): commitopts['edit'] = False else: -if opts['fold']: -msgs = ["HG: This is a fold of %d changesets." % len(allctx)] -msgs += ["HG: Commit message of changeset %s.\n\n%s\n" % - (c.rev(), c.description()) for c in allctx] -else: -msgs = [head.description()] -commitopts['message'] = "\n".join(msgs) commitopts['edit'] = True # TODO: if the author and message are the same, don't create a new # hash. Right now we create a new hash because the date can be # different. -newid, created = rewrite(repo, root, allctx, head, - [root.p1().node(), root.p2().node()], - commitopts=commitopts) -if created: -if p1.rev() in revs: -newp1 = newid -phases.retractboundary(repo, tr, targetphase, [newid]) -obsolete.createmarkers(repo, [(ctx, (repo[newid],)) - for ctx in allctx]) +if opts['fold']: +if commitopts['edit']: +msgs = ["HG: This is a fold of %d changesets." % +len(allctx)] +msgs += ["HG: Commit message of changeset %s.\n\n%s\n" % + (c.rev(), c.description()) for c in allctx] +commitopts['message'] = "\n".join(msgs) + +newid, created = rewrite(repo, root, allctx, head, + [root.p1().node(), root.p2().node()], + commitopts=commitopts) +if created: +if p1.rev() in revs: +newp1 = newid +phases.retractboundary(repo, tr, targetphase, [newid]) +obsolete.createmarkers(repo, [(ctx, (repo[newid],)) + for ctx in allctx]) +else: +ui.status(_("nothing changed\n")) else: -ui.status(_("nothing changed\n")) +if commitopts['edit']: +commitopts['message'] = head.description() +newid, created = rewrite(repo, root, allctx, head, + [root.p1().node(), root.p2().node()], + commitopts=commitopts) +if created: +if p1.rev() in revs: +newp1 = newid +phases.retractboundary(repo, tr, targetphase, [newid]) +obsolete.createmarkers(repo, [(ctx, (repo[newid],)) + for ctx in allctx]) +else: +ui.status(_("nothing changed\n")) tr.close() finally: tr.release() ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: [PATCH 2 of 4 evolve-ext] metaedit: extend the functionality to support editing multiple commits
Excerpts from Ryan McElroy's message of 2016-12-05 13:14:58 +: > > On 11/16/16 7:56 PM, Mateusz Kwapich wrote: > > # HG changeset patch > > # User Mateusz Kwapich <mitran...@fb.com> > > # Date 1479324135 0 > > # Wed Nov 16 19:22:15 2016 + > > # Branch stable > > # Node ID b2bde478bfebc390dba8f1ee314b7bdd062ab191 > > # Parent 744c6acd84edf73ffdf505b9673b0383db727a0e > > metaedit: extend the functionality to support editing multiple commits > > > > diff --git a/hgext/evolve.py b/hgext/evolve.py > > --- a/hgext/evolve.py > > +++ b/hgext/evolve.py > > @@ -3257,29 +3257,57 @@ def metaedit(ui, repo, *revs, **opts): > > if commitopts.get('message') or commitopts.get('logfile'): > > commitopts['edit'] = False > > else: > > -if opts['fold']: > > -msgs = ["HG: This is a fold of %d changesets." % > > len(allctx)] > > -msgs += ["HG: Commit message of changeset %s.\n\n%s\n" > > % > > - (c.rev(), c.description()) for c in allctx] > > -else: > > -msgs = [head.description()] > > -commitopts['message'] = "\n".join(msgs) > > Can you put this logic move into a separate commit? The idea that this > logic can move down the file without affecting behavior is non-obvious. I'll try to split it in the next version of this series. > > > commitopts['edit'] = True > > Is this line now duplicated above and below the else? Why not remove the > if/else entirely then? > > > > # TODO: if the author and message are the same, don't create > > a new > > # hash. Right now we create a new hash because the date can be > > # different. > > -newid, created = rewrite(repo, root, allctx, head, > > - [root.p1().node(), root.p2().node()], > > - commitopts=commitopts) > > -if created: > > -if p1.rev() in revs: > > -newp1 = newid > > -phases.retractboundary(repo, tr, targetphase, [newid]) > > -obsolete.createmarkers(repo, [(ctx, (repo[newid],)) > > - for ctx in allctx]) > > +if opts['fold']: > > +if commitopts['edit']: > > +msgs = ["HG: This is a fold of %d changesets." % > > +len(allctx)] > > +msgs += ["HG: Commit message of changeset %s.\n\n%s\n" > > % > > + (c.rev(), c.description()) for c in allctx] > > +commitopts['message'] = "\n".join(msgs) > > + > > +newid, created = rewrite(repo, root, allctx, head, > > + [root.p1().node(), > > root.p2().node()], > > + commitopts=commitopts) > > +if created: > > +if p1.rev() in revs: > > +newp1 = newid > > +phases.retractboundary(repo, tr, targetphase, [newid]) > > +obsolete.createmarkers(repo, [(ctx, (repo[newid],)) > > + for ctx in allctx]) > > +else: > > +ui.status(_("nothing changed\n")) > > else: > > -ui.status(_("nothing changed\n")) > > +replacemap = {} > > +# we need topological order > > +allctx = sorted(allctx, key=lambda c: c.rev()) > > +for c in allctx: > > +if commitopts['edit']: > > +commitopts['message'] = \ > > +"HG: Commit message of changeset %s\n%s" %\ > > +(str(c), c.description()) > > +bases = [ > > +replacemap.get(c.p1().node(), c.p1().node()), > > +replacemap.get(c.p2().node(), c.p2().node()), > > +] > > +newid, created = metarewrite(repo, c, bases, > > + commitopts=commitopts) > > +if created: > > +replacemap[c.n
Re: [PATCH 1 of 4 evolve-ext] metaedit: add a helper function for just metadata rewrites
Excerpts from Ryan McElroy's message of 2016-12-05 13:14:28 +: > > On 11/16/16 7:56 PM, Mateusz Kwapich wrote: > > # HG changeset patch > > # User Mateusz Kwapich <mitran...@fb.com> > > # Date 1479324110 0 > > # Wed Nov 16 19:21:50 2016 + > > # Branch stable > > # Node ID 744c6acd84edf73ffdf505b9673b0383db727a0e > > # Parent 727c7211c810d304ebf92b32db7ecf697ce46ac6 > > metaedit: add a helper function for just metadata rewrites > > > > It will be used by metaedit. > > > > diff --git a/hgext/evolve.py b/hgext/evolve.py > > --- a/hgext/evolve.py > > +++ b/hgext/evolve.py > > @@ -907,6 +907,13 @@ def rewrite(repo, old, updates, head, ne > > finally: > > lockmod.release(tr, lock, wlock) > > > > +def metarewrite(repo, old, newbases, commitopts): > > +'''Like rewrite but affects only the changeset metadata.''' > > +# TODO: reuse the manifest for speed > > Your series that Augie just pushed will allow this, right? Yes. > > > +newid, created = rewrite(repo, old, [old], old, newbases, > > + commitopts=commitopts) > > +return newid, created > > + > > class MergeFailure(error.Abort): > > pass > > > > https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel > -- ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: [PATCH] posix: give the cached symlink a real target
What Jun said. All the rest looks good to me. Excerpts from Martijn Pieters's message of 2016-11-30 16:51:41 +: > # HG changeset patch > # User Martijn Pieters> # Date 1480523976 0 > # Wed Nov 30 16:39:36 2016 + > # Node ID 4053f60f77657f8f54afc72d00bf629b75d0b4b9 > # Parent 9e29d4e4e08b5996adda49cdd0b497d89e2b16ee > posix: give the cached symlink a real target > > The NamedTemporaryFile file is cleared up so checklink ends up as a dangling > symlink, causing cp -r in tests to complain on both Solaris and OS X. Use > a permanent file instead when there is a .hg/cache directory. > > diff --git a/mercurial/posix.py b/mercurial/posix.py > --- a/mercurial/posix.py > +++ b/mercurial/posix.py > @@ -231,10 +231,18 @@ > cachedir = None > name = tempfile.mktemp(dir=checkdir, prefix='checklink-') > try: > -fd = tempfile.NamedTemporaryFile(dir=checkdir, > - prefix='hg-checklink-') > +fd = None > +if cachedir is None: > +fd = tempfile.NamedTemporaryFile(dir=checkdir, > + prefix='hg-checklink-') > +target = os.path.basename(fd.name) > +else: > +# create a fixed file to link to; doesn't matter if it > +# already exists. > +target = 'checklink-target' > +open(os.path.join(cachedir, target), 'w') > try: > -os.symlink(os.path.basename(fd.name), name) > +os.symlink(target, name) > if cachedir is None: > os.unlink(name) > else: > @@ -249,7 +257,8 @@ > continue > raise > finally: > -fd.close() > +if fd is not None: > +fd.close() > except AttributeError: > return False > except OSError as inst: > diff --git a/tests/test-clone.t b/tests/test-clone.t > --- a/tests/test-clone.t > +++ b/tests/test-clone.t > @@ -33,6 +33,7 @@ >branch2-served >checkisexec >checklink > + checklink-target >checknoexec >rbc-names-v1 >rbc-revs-v1 > @@ -50,6 +51,7 @@ >branch2-served >checkisexec >checklink > + checklink-target > >$ cat a >a > diff --git a/tests/test-hardlinks.t b/tests/test-hardlinks.t > --- a/tests/test-hardlinks.t > +++ b/tests/test-hardlinks.t > @@ -212,6 +212,8 @@ >2 r4/.hg/branch >2 r4/.hg/cache/branch2-served >2 r4/.hg/cache/checkisexec > + 3 r4/.hg/cache/checklink (?) > + ? r4/.hg/cache/checklink-target (glob) >2 r4/.hg/cache/checknoexec >2 r4/.hg/cache/rbc-names-v1 >2 r4/.hg/cache/rbc-revs-v1 > @@ -250,6 +252,7 @@ >1 r4/.hg/branch >2 r4/.hg/cache/branch2-served >2 r4/.hg/cache/checkisexec > + 2 r4/.hg/cache/checklink-target >2 r4/.hg/cache/checknoexec >2 r4/.hg/cache/rbc-names-v1 >2 r4/.hg/cache/rbc-revs-v1 > diff --git a/tests/test-tags.t b/tests/test-tags.t > --- a/tests/test-tags.t > +++ b/tests/test-tags.t > @@ -674,6 +674,7 @@ >branch2-served >checkisexec >checklink > + checklink-target >hgtagsfnodes1 >rbc-names-v1 >rbc-revs-v1 > @@ -700,6 +701,7 @@ >branch2-served >checkisexec >checklink > + checklink-target >hgtagsfnodes1 >rbc-names-v1 >rbc-revs-v1 -- ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: [PATCH] fsmonitor: be robust in the face of bad state
The logic looks correct. It definitely makes the fsmonitor more robust. Excerpts from Simon Farnsworth's message of 2016-11-25 07:30:53 -0800: > # HG changeset patch > # User Simon Farnsworth> # Date 1480087846 28800 > # Fri Nov 25 07:30:46 2016 -0800 > # Node ID 0ca34f1b83da754246ee33e01c4f7d6652061f5d > # Parent a3163433647108b7bec8fc45896db1c20b18ab21 > fsmonitor: be robust in the face of bad state > > fsmonitor could write out bad state if interrupted part way through, and > would then crash when it tried to read it back in. > > Make both sides of the operation more robust - reading state should fail > cleanly, and we can use atomictemp to write out cleanly as the file is > small. Between the two, we shouldn't crash with an IndexError any more. > > diff --git a/hgext/fsmonitor/state.py b/hgext/fsmonitor/state.py > --- a/hgext/fsmonitor/state.py > +++ b/hgext/fsmonitor/state.py > @@ -59,6 +59,12 @@ > state = file.read().split('\0') > # state = hostname\0clock\0ignorehash\0 + list of files, each > # followed by a \0 > +if len(state) < 3: > +self._ui.log( > +'fsmonitor', 'fsmonitor: state file truncated (expected ' > +'3 chunks, found %d), nuking state\n' % len(state)) > +self.invalidate() > +return None, None, None > diskhostname = state[0] > hostname = socket.gethostname() > if diskhostname != hostname: > @@ -85,12 +91,12 @@ > return > > try: > -file = self._opener('fsmonitor.state', 'wb') > +file = self._opener('fsmonitor.state', 'wb', atomictemp=True) > except (IOError, OSError): > self._ui.warn(_("warning: unable to write out fsmonitor > state\n")) > return > > -try: > +with file: > file.write(struct.pack(_versionformat, _version)) > file.write(socket.gethostname() + '\0') > file.write(clock + '\0') > @@ -98,8 +104,6 @@ > if notefiles: > file.write('\0'.join(notefiles)) > file.write('\0') > -finally: > -file.close() > > def invalidate(self): > try: -- ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: [PATCH] test-rebase-base: add test cases about multiple branches with merges
Please, forgive my email client (or my misuse of it). The proper contents of my last reply are following: LGTM Excerpts from Jun Wu's message of 2016-11-25 12:49:01 +: > # HG changeset patch > # User Jun Wu> # Date 1480077830 0 > # Fri Nov 25 12:43:50 2016 + > # Node ID c0a9c4c2c6ae2a779c060dc2424942099d7c984d > # Parent fd4175ec0f4e9bd68f4bfdcd601e11d77499d486 > # Available At https://bitbucket.org/quark-zju/hg-draft > # hg pull https://bitbucket.org/quark-zju/hg-draft -r > c0a9c4c2c6ae > test-rebase-base: add test cases about multiple branches with merges > > This helps clarify the current behavior. When a merge changeset is selected > in --base directly, only one path will be chosen. The behavior remains the > same before and after "rebase: calculate ancestors for --base separately > (issue5420)". > > diff --git a/tests/test-rebase-base.t b/tests/test-rebase-base.t > --- a/tests/test-rebase-base.t > +++ b/tests/test-rebase-base.t > @@ -93,2 +93,169 @@ Mixed rebasable and non-rebasable bases >nothing to rebase >[1] > + > + $ cd .. > + > +Multiple branches with merges: > + > + $ hg init b > + $ cd b > + > + $ hg debugdrawdag < + > h g > + > | /| > + > | f | > + > |/ / > + > | e > + > |/ d > + > | /| > + > | c | > + > |/ / > + > | b > + > |/ > + > a > + > EOS > + > + $ hg rebase -b b+f -d h > + rebasing 1:488e1b7e7341 "b" (b) > + rebasing 6:0c088b72e768 "d" (d) > + rebasing 4:0e9bbb7dd767 "f" (f) > + rebasing 7:9bdc802fd225 "g" (g tip) > + saved backup bundle to > $TESTTMP/b/.hg/strip-backup/0e9bbb7dd767-ff8b132b-backup.hg (glob) > + $ hg tglog > + o7: g > + |\ > + | o 6: f > + | | > + | | o5: d > + | | |\ > + | +---o 4: b > + | | | > + | o | 3: h > + | | | > + o | | 2: e > + |/ / > + | o 1: c > + |/ > + o 0: a > + > + $ cd .. > + > +Multiple branches with multiple merges: > + > + $ hg init c > + $ cd c > + > + $ hg debugdrawdag <<'EOS' > + > j i > + > | | > + > | h > + > |/| > + > | g | k > + > | /| |/ > + > | f | | > + > |/ / | > + > | e / > + > |/ d > + > | /| > + > | c | > + > |/ / > + > | b > + > |/ > + > a > + > EOS > + $ hg rebase -b b+f -d j > + rebasing 1:488e1b7e7341 "b" (b) > + rebasing 4:0e9bbb7dd767 "f" (f) > + rebasing 6:0c088b72e768 "d" (d) > + rebasing 9:91358dadd39b "k" (k) > + rebasing 7:9bdc802fd225 "g" (g) > + rebasing 8:91a34cc1f2a7 "h" (h) > + rebasing 10:f0d5af4cc88e "i" (i tip) > + saved backup bundle to > $TESTTMP/c/.hg/strip-backup/0e9bbb7dd767-6f921be4-backup.hg (glob) > + $ hg tglog > + o 10: i > + | > + o9: h > + |\ > + | o8: g > + | |\ > + +-o 7: k > + | | | > + o | |6: d > + |\ \ \ > + | | | o 5: f > + | | | | > + | o---+ 4: b > + | / / > + | | o 3: j > + | | | > + | o | 2: e > + | |/ > + o / 1: c > + |/ > + o 0: a > + > + > + $ cd .. > + > +Pick merge changesets in -b, only one of the two parents is selected: > + > + $ hg init d > + $ cd d > + > + $ hg debugdrawdag <<'EOS' > + > dest n > + > | |\ > + > m | g d > + > |\ | /| > + > | l | f | > + > \ \|/ / > + > k | e > + > i \|/ d > + > |\ | /| > + > | h | c | > + > \ \|/ / > + > j | b > + >\|/ > + > a > + > EOS > + > + $ hg rebase -b n+i -d dest > + rebasing 6:e22eece29d69 "h" (h) > + rebasing 12:edd90b885ce0 "i" (i) > + rebasing 1:488e1b7e7341 "b" (b) > + rebasing 10:0c088b72e768 "d" (d) > + rebasing 14:f188fc87af23 "n" (n tip) > + saved backup bundle to > $TESTTMP/d/.hg/strip-backup/e22eece29d69-0eeca84a-backup.hg (glob) > + $ hg tglog > + o14: n > + |\ > + | o13: d > + | |\ > + | | o 12: b > + | | | > + | | | o11: i > + | | | |\ > + | | +---o 10: h > + | | | | > + | | | | o9: m > + | | | | |\ > + o \ \ \ \ \8: g > + |\ \ \ \ \ \ > + | | | | | | o 7: l > + | | | | | | | > + | | | | | o | 6: k > + | | | | | |/ > + | | | | o / 5: j > + | | | | |/ > + | o-+ 4: f > + | / / / > + o-+ 3: e > + / / / > + | o / 2: dest > + | |/ > + o / 1: c > + |/ > + o 0: a > + > + $ cd .. -- ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: [PATCH] test-rebase-base: add test cases about multiple branches with merges
-- ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: [PATCH V2] evolve: improve error message if unstable changes are disallowed
LGTM, I suppose that timeless may also want to review it before it's queued. Excerpts from Pulkit Goyal's message of 2016-11-24 23:13:48 +0530: > # HG changeset patch > # User Pulkit Goyal <7895pul...@gmail.com> > # Date 1479915042 -19800 > # Wed Nov 23 21:00:42 2016 +0530 > # Node ID 920d5946d13339d9cf4828f678fb55063cd8 > # Parent cb2bac3253fbd52894ffcb4719a148fe6a3da38b > evolve: improve error message if unstable changes are disallowed > > I saw a question on stackoverflow why evolve reports something like cannot > fold chain not ending with head. Even I was confused the first time about the > behavior. The error message can be improved to avoid confusion to people who > are unaware about the config in future. > > diff -r cb2bac3253fb -r 920d5946d133 hgext/evolve.py > --- a/hgext/evolve.pyWed Nov 02 18:56:44 2016 +0100 > +++ b/hgext/evolve.pyWed Nov 23 21:00:42 2016 +0530 > @@ -2514,7 +2514,8 @@ > raise error.Abort('nothing to prune') > > if _disallowednewunstable(repo, revs): > -raise error.Abort(_("cannot prune in the middle of a stack")) > +raise error.Abort(_("cannot prune in the middle of a stack"), > +hint = _("new unstable changesets are not allowed")) > > # defines successors changesets > sucs = scmutil.revrange(repo, succs) > @@ -3234,8 +3235,9 @@ > newunstable = _disallowednewunstable(repo, revs) > if newunstable: > raise error.Abort( > -_('cannot edit commit information in the middle of a > stack'), > -hint=_('%s will be affected') % > repo[newunstable.first()]) > +_('cannot edit commit information in the middle of a '\ > +'stack'), hint=_('%s will become unstable and new > unstable'\ > +' changes are not allowed') % repo[newunstable.first()]) > root = head = repo[revs.first()] > > wctx = repo[None] > @@ -3299,7 +3301,8 @@ > head = repo[heads.first()] > if _disallowednewunstable(repo, revs): > raise error.Abort(_("cannot fold chain not ending with a head "\ > -"or with branching")) > +"or with branching"), hint = _("new unstable"\ > +" changesets are not allowed")) > return root, head > > def _disallowednewunstable(repo, revs): > diff -r cb2bac3253fb -r 920d5946d133 tests/test-evolve.t > --- a/tests/test-evolve.tWed Nov 02 18:56:44 2016 +0100 > +++ b/tests/test-evolve.tWed Nov 23 21:00:42 2016 +0530 > @@ -1301,9 +1301,11 @@ >created new head >$ hg prune '26 + 27' >abort: cannot prune in the middle of a stack > + (new unstable changesets are not allowed) >[255] >$ hg prune '19::28' >abort: cannot prune in the middle of a stack > + (new unstable changesets are not allowed) >[255] >$ hg prune '26::' >3 changesets pruned > @@ -1338,9 +1340,11 @@ > >$ hg fold --exact "19 + 18" >abort: cannot fold chain not ending with a head or with branching > + (new unstable changesets are not allowed) >[255] >$ hg fold --exact "18::29" >abort: cannot fold chain not ending with a head or with branching > + (new unstable changesets are not allowed) >[255] >$ hg fold --exact "19::" >2 changesets folded > @@ -1483,10 +1487,11 @@ > check that metaedit respects allowunstable >$ hg metaedit '.^' --config 'experimental.evolution=createmarkers, > allnewcommands' >abort: cannot edit commit information in the middle of a stack > - (c904da5245b0 will be affected) > + (c904da5245b0 will become unstable and new unstable changes are not > allowed) >[255] >$ hg metaedit '18::20' --fold --config > 'experimental.evolution=createmarkers, allnewcommands' >abort: cannot fold chain not ending with a head or with branching > + (new unstable changesets are not allowed) >[255] >$ hg metaedit --user foobar >0 files updated, 0 files merged, 0 files removed, 0 files unresolved -- ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: [PATCH 3 of 3 V3] memctx: allow the memlightctx thats reusing the manifest node
I like the relocate idea. It would be an useful abstractions - we have a lot of things that are rewriting one commit at a time. Excerpts from Jun Wu's message of 2016-11-22 22:18:59 +: > Excerpts from Augie Fackler's message of 2016-11-21 18:26:16 -0500: > > Also, could histedit be updated to use this for 'mess' actions > > perhaps? Probably not easy, but if it is I'd love to see an in-tree > > client of this class. Can you take a look? > > It does not work if "mess" happens after other csets creating new manifests. > > Instead of making it a special for histedit. I'm always more interested in > a general purpose lower-level function doing some kind of "rebase" in core, > that smartly deals with this case and handles bookmark and dirstate parent > movements (and obsmarker creation) automatically. > > There are currently multiple places reinventing the logic over and over: > - absorb / collate > - rebase > - histedit > - evolve > > And once we have this API in core, we can migrate the above commands to use > it. > > I'm thinking about something like: > > relocate(origctx, newparents, fileoverrides, metaoverrides, obsoleted, >transaction) > # origctx: context > # newparents: [node] > # fileoverrides: abstract dict, keys: path, value: (content, mode, renamed) > # metaoverrides: dict with defined set of keys. to override metadata like > #commit message etc. > # obsoleted: bool, could also be a non-bool to provide more metadata > > Then relocate will choose what *ctx to use internally. For absorb, > metaoverrides is empty, for metaedit, fileoverrides is empty. For rebase, > both are empty. For histedit, depends. > > Two things I'd like to make sure they are considered while developing the > API: > > 1. Giant files friendly - if we need to commit two giant files, we don't > need to keep both of them in memory. "fileoverrides" being an abstract > dict should solve this. > 2. Commit hooks friendly - if any commit hook is requiring a working copy, > fallback to the slow path that writs the disk. (this could be the most > complex part of the implementation, but I guess crecord-alike already > has similar things). We also need to have ways to mark a commit hook as > "do not require on-disk working copy" - could be a config option, or > checking some magic string in the hook itself. > > If we decide to go this way, I could help start some more formal discussion > (a plan page or so) - hopefully we can collect enough corner cases that > need support, have a final decision about the API soon. > > CC smf because it could be related to your memctx work. -- ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: [PATCH shelve-ext v2] shelve: make --keep option survive user intevention (issue5431)
LGTM -- ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: [PATCH shelve-ext] shelve: make --keep option survive user intevention (issue5431)
One less footgun in unshelve. Nice! Excerpts from Kostia Balytskyi's message of 2016-11-23 10:51:47 -0800: > diff --git a/hgext/shelve.py b/hgext/shelve.py > --- a/hgext/shelve.py > +++ b/hgext/shelve.py > @@ -782,6 +787,7 @@ def _dounshelve(ui, repo, *shelved, **op > > try: > state = shelvedstate.load(repo) > +opts['keep'] = opts.get('keep') or state.keep > except IOError as err: > if err.errno != errno.ENOENT: > raise Respecting the "hg unshelve --continue --keep" while ignoring the "hg unshelve --continue --no-keep" seems confusing to me. Either we should always respoect the options provided to continue or we should make them invalid. -- ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: [PATCH evolve-ext] evolve: improve error message if unstable changes are disallowed
Excerpts from Pulkit Goyal's message of 2016-11-23 21:06:29 +0530: > # HG changeset patch > # User Pulkit Goyal <7895pul...@gmail.com> > # Date 1479915042 -19800 > # Wed Nov 23 21:00:42 2016 +0530 > # Node ID 32083f1f0c67341e5b4c22e880b70ccc4e0fc088 > # Parent cb2bac3253fbd52894ffcb4719a148fe6a3da38b > evolve: improve error message if unstable changes are disallowed > > I saw a question on stackoverflow why evolve reports something like cannot > fold chain not ending with head. Even I was confused the first time about the > behavior. The error message can be improved to avoid confusion to people who > are unaware about the config in future. That sounds like a very useful information. It sucks that the errors have newlines in them. I think we can avoid it - see my inline comments. > > diff -r cb2bac3253fb -r 32083f1f0c67 hgext/evolve.py > --- a/hgext/evolve.pyWed Nov 02 18:56:44 2016 +0100 > +++ b/hgext/evolve.pyWed Nov 23 21:00:42 2016 +0530 > @@ -2514,7 +2514,8 @@ > raise error.Abort('nothing to prune') > > if _disallowednewunstable(repo, revs): > -raise error.Abort(_("cannot prune in the middle of a stack")) > +raise error.Abort(_("cannot prune in the middle of a stack\n"\ > +"new unstable changesets are not allowed")) This error doesn't have hint - maybe we could use it for that purpose. > > # defines successors changesets > sucs = scmutil.revrange(repo, succs) > @@ -3234,7 +3235,8 @@ > newunstable = _disallowednewunstable(repo, revs) > if newunstable: > raise error.Abort( > -_('cannot edit commit information in the middle of a > stack'), > +_('cannot edit commit information in the middle of a > stack\n'\ > +'new unstable changesets are not allowed'), > hint=_('%s will be affected') % > repo[newunstable.first()]) I would change the hint to say something along: '%s would become unstable but new unstable commits are not allowed' > root = head = repo[revs.first()] > > @@ -3299,7 +3301,8 @@ > head = repo[heads.first()] > if _disallowednewunstable(repo, revs): > raise error.Abort(_("cannot fold chain not ending with a head "\ > -"or with branching")) > +"or with branching\nnew unstable changesets are"\ > +" not allowed")) I would move this to hint param. > return root, head > > def _disallowednewunstable(repo, revs): > diff -r cb2bac3253fb -r 32083f1f0c67 tests/test-evolve.t > --- a/tests/test-evolve.tWed Nov 02 18:56:44 2016 +0100 > +++ b/tests/test-evolve.tWed Nov 23 21:00:42 2016 +0530 > @@ -1301,9 +1301,11 @@ >created new head >$ hg prune '26 + 27' >abort: cannot prune in the middle of a stack > + new unstable changesets are not allowed >[255] >$ hg prune '19::28' >abort: cannot prune in the middle of a stack > + new unstable changesets are not allowed >[255] >$ hg prune '26::' >3 changesets pruned > @@ -1338,9 +1340,11 @@ > >$ hg fold --exact "19 + 18" >abort: cannot fold chain not ending with a head or with branching > + new unstable changesets are not allowed >[255] >$ hg fold --exact "18::29" >abort: cannot fold chain not ending with a head or with branching > + new unstable changesets are not allowed >[255] >$ hg fold --exact "19::" >2 changesets folded > @@ -1483,10 +1487,12 @@ > check that metaedit respects allowunstable >$ hg metaedit '.^' --config 'experimental.evolution=createmarkers, > allnewcommands' >abort: cannot edit commit information in the middle of a stack > + new unstable changesets are not allowed >(c904da5245b0 will be affected) >[255] >$ hg metaedit '18::20' --fold --config > 'experimental.evolution=createmarkers, allnewcommands' >abort: cannot fold chain not ending with a head or with branching > + new unstable changesets are not allowed >[255] >$ hg metaedit --user foobar >0 files updated, 0 files merged, 0 files removed, 0 files unresolved -- ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: [PATCH 3 of 3 V3] memctx: allow the memlightctx thats reusing the manifest node
Excerpts from Augie Fackler's message of 2016-11-21 18:26:16 -0500: > On Mon, Nov 21, 2016 at 08:13:32AM -0800, Mateusz Kwapich wrote: > > # HG changeset patch > > # User Mateusz Kwapich <mitran...@fb.com> > > # Date 1479744581 28800 > > # Mon Nov 21 08:09:41 2016 -0800 > > # Node ID 4af70f21264ac8e52d9b218080bbc96ee5505606 > > # Parent 4a0824bead3ba5980bd8528937fba5f7bb31ba9f > > memctx: allow the memlightctx thats reusing the manifest node > > > > When we have a lot of files writing a new manifest revision can be > > expensive. > > This commit adds a possibility for memctx to reuse a manifest from a > > different > > commit. This can be beneficial for commands that are creating metadata > > changes > > without any actual files changed like "hg metaedit" in evolve extension. > > > > I will send the change for evolve that leverages this once this is accepted. > > > > diff --git a/mercurial/context.py b/mercurial/context.py > > --- a/mercurial/context.py > > +++ b/mercurial/context.py > > @@ -1975,3 +1975,101 @@ class memfilectx(committablefilectx): > > def write(self, data, flags): > > """wraps repo.wwrite""" > > self._data = data > > + > > +class memlightctx(committablectx): > > Could this instead be called "metadataonlyctx"? No need to do a > resend, if you're happy with my bikeshed color I can fix it in flight. > > Also, could histedit be updated to use this for 'mess' actions > perhaps? Probably not easy, but if it is I'd love to see an in-tree > client of this class. Can you take a look? sure, feel free to rename it. I can look into hooking this up into the histedit. It won't be helpful in general case of message editing but it can be used when the previous actions were all "pick" or "mess" and it will be much faster in that case. > > > +"""Like memctx but it's reusing the manifest of different commit. > > +Intended to be used by lightweight operations that are creating > > +metadata-only changes. > > + > > +Revision information is supplied at initialization time. 'repo' is the > > +current localrepo, 'ctx' is original revision which manifest we're > > reuisng > > +'parents' is a sequence of two parent revisions identifiers (pass None > > for > > +every missing parent), 'text' is the commit. > > + > > +user receives the committer name and defaults to current repository > > +username, date is the commit date in any format supported by > > +util.parsedate() and defaults to current date, extra is a dictionary of > > +metadata or is left empty. > > +""" > > +def __new__(cls, repo, path, *args, **kwargs): > > +return super(memlightctx, cls).__new__(cls, repo) > > + > > +def __init__(self, repo, originalctx, parents, text, user=None, > > date=None, > > + extra=None, editor=False): > > +super(memlightctx, self).__init__(repo, text, user, date, extra) > > +self._rev = None > > +self._node = None > > +self._originalctx = originalctx > > +self._manifestnode = originalctx.manifestnode() > > +parents = [(p or nullid) for p in parents] > > +p1, p2 = self._parents = [changectx(self._repo, p) for p in > > parents] > > + > > +# sanity check to ensure that the reused manifest parents are > > +# manifests of our commit parents > > +mp1, mp2 = self.manifestctx().parents > > +if p1 != nullid and p1.manifestctx().node() != mp1: > > +raise RuntimeError('can\'t reuse the manifest: ' > > + 'its p1 doesn\'t match the new ctx p1') > > +if p2 != nullid and p2.manifestctx().node() != mp2: > > +raise RuntimeError('can\'t reuse the manifest: ' > > + 'its p2 doesn\'t match the new ctx p2') > > + > > +self._files = originalctx.files() > > +self.substate = {} > > + > > +if extra: > > +self._extra = extra.copy() > > +else: > > +self._extra = {} > > + > > +if self._extra.get('branch', '') == '': > > +self._extra['branch'] = 'default' > > + > > +if editor: > > +self._text = editor(self._repo, self, []) > > +self._repo.savecommitmessage(self._text) > > + > > +def manifestnode(self): &g
[PATCH 2 of 3 V3] localrepo: make it possible to reuse manifest when commiting context
# HG changeset patch # User Mateusz Kwapich <mitran...@fb.com> # Date 1479409155 28800 # Thu Nov 17 10:59:15 2016 -0800 # Node ID 4a0824bead3ba5980bd8528937fba5f7bb31ba9f # Parent 7dfd4c184ee087f2c05e1bdae8a10ccefbff7a92 localrepo: make it possible to reuse manifest when commiting context This makes the commit function understand the context that's reusing manifest. diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py --- a/mercurial/localrepo.py +++ b/mercurial/localrepo.py @@ -1695,7 +1695,11 @@ class localrepository(object): tr = self.transaction("commit") trp = weakref.proxy(tr) -if ctx.files(): +if ctx.manifestnode(): +# reuse an existing manifest revision +mn = ctx.manifestnode() +files = ctx.files() +elif ctx.files(): m1ctx = p1.manifestctx() m2ctx = p2.manifestctx() mctx = m1ctx.copy() ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 1 of 3 V3] manifest: expose the parents() method
# HG changeset patch # User Mateusz Kwapich <mitran...@fb.com> # Date 1479409155 28800 # Thu Nov 17 10:59:15 2016 -0800 # Node ID 7dfd4c184ee087f2c05e1bdae8a10ccefbff7a92 # Parent 96f2f50d923f94c23999df198ff16409e7539af8 manifest: expose the parents() method diff --git a/mercurial/manifest.py b/mercurial/manifest.py --- a/mercurial/manifest.py +++ b/mercurial/manifest.py @@ -1381,6 +1381,10 @@ class manifestctx(object): memmf._manifestdict = self.read().copy() return memmf +@propertycache +def parents(self): +return self._revlog().parents(self._node) + def read(self): if not self._data: if self._node == revlog.nullid: @@ -1515,6 +1519,10 @@ class treemanifestctx(object): memmf._treemanifest = self.read().copy() return memmf +@propertycache +def parents(self): +return self._revlog().parents(self._node) + def readdelta(self, shallow=False): '''Returns a manifest containing just the entries that are present in this manifest, but not in its p1 manifest. This is efficient to read ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 3 of 3 V3] memctx: allow the memlightctx thats reusing the manifest node
# HG changeset patch # User Mateusz Kwapich <mitran...@fb.com> # Date 1479744581 28800 # Mon Nov 21 08:09:41 2016 -0800 # Node ID 4af70f21264ac8e52d9b218080bbc96ee5505606 # Parent 4a0824bead3ba5980bd8528937fba5f7bb31ba9f memctx: allow the memlightctx thats reusing the manifest node When we have a lot of files writing a new manifest revision can be expensive. This commit adds a possibility for memctx to reuse a manifest from a different commit. This can be beneficial for commands that are creating metadata changes without any actual files changed like "hg metaedit" in evolve extension. I will send the change for evolve that leverages this once this is accepted. diff --git a/mercurial/context.py b/mercurial/context.py --- a/mercurial/context.py +++ b/mercurial/context.py @@ -1975,3 +1975,101 @@ class memfilectx(committablefilectx): def write(self, data, flags): """wraps repo.wwrite""" self._data = data + +class memlightctx(committablectx): +"""Like memctx but it's reusing the manifest of different commit. +Intended to be used by lightweight operations that are creating +metadata-only changes. + +Revision information is supplied at initialization time. 'repo' is the +current localrepo, 'ctx' is original revision which manifest we're reuisng +'parents' is a sequence of two parent revisions identifiers (pass None for +every missing parent), 'text' is the commit. + +user receives the committer name and defaults to current repository +username, date is the commit date in any format supported by +util.parsedate() and defaults to current date, extra is a dictionary of +metadata or is left empty. +""" +def __new__(cls, repo, path, *args, **kwargs): +return super(memlightctx, cls).__new__(cls, repo) + +def __init__(self, repo, originalctx, parents, text, user=None, date=None, + extra=None, editor=False): +super(memlightctx, self).__init__(repo, text, user, date, extra) +self._rev = None +self._node = None +self._originalctx = originalctx +self._manifestnode = originalctx.manifestnode() +parents = [(p or nullid) for p in parents] +p1, p2 = self._parents = [changectx(self._repo, p) for p in parents] + +# sanity check to ensure that the reused manifest parents are +# manifests of our commit parents +mp1, mp2 = self.manifestctx().parents +if p1 != nullid and p1.manifestctx().node() != mp1: +raise RuntimeError('can\'t reuse the manifest: ' + 'its p1 doesn\'t match the new ctx p1') +if p2 != nullid and p2.manifestctx().node() != mp2: +raise RuntimeError('can\'t reuse the manifest: ' + 'its p2 doesn\'t match the new ctx p2') + +self._files = originalctx.files() +self.substate = {} + +if extra: +self._extra = extra.copy() +else: +self._extra = {} + +if self._extra.get('branch', '') == '': +self._extra['branch'] = 'default' + +if editor: +self._text = editor(self._repo, self, []) +self._repo.savecommitmessage(self._text) + +def manifestnode(self): +return self._manifestnode + +@propertycache +def _manifestctx(self): +return self._repo.manifestlog[self._manifestnode] + +def filectx(self, path, filelog=None): +return self._originalctx.filectx(path, filelog=filelog) + +def commit(self): +"""commit context to the repo""" +return self._repo.commitctx(self) + +@property +def _manifest(self): +return self._originalctx.manifest() + +@propertycache +def _status(self): +"""Calculate exact status from ``files`` specified in the ``origctx`` +and parents manifests. +""" +man1 = self.p1().manifest() +p2 = self._parents[1] +# "1 < len(self._parents)" can't be used for checking +# existence of the 2nd parent, because "memlightctx._parents" is +# explicitly initialized by the list, of which length is 2. +if p2.node() != nullid: +man2 = p2.manifest() +managing = lambda f: f in man1 or f in man2 +else: +managing = lambda f: f in man1 + +modified, added, removed = [], [], [] +for f in self._files: +if not managing(f): +added.append(f) +elif self[f]: +modified.append(f) +else: +removed.append(f) + +return scmutil.status(modified, added, removed, [], [], [], []) ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 3 of 3 V2] memctx: allow the memlightctx thats reusing the manifest node
# HG changeset patch # User Mateusz Kwapich <mitran...@fb.com> # Date 1479410909 28800 # Thu Nov 17 11:28:29 2016 -0800 # Node ID 0d41689f79cf22d8761dd6af9cb5da86008afe94 # Parent 4a0824bead3ba5980bd8528937fba5f7bb31ba9f memctx: allow the memlightctx thats reusing the manifest node When we have a lot of files writing a new manifest revision can be expensive. This commit adds a possibility for memctx to reuse a manifest from a different commit. This can be beneficial for commands that are creating metadata changes without any actual files changed like "hg metaedit" in evolve extension. I will send the change for evolve that leverages this once this is accepted. diff --git a/mercurial/context.py b/mercurial/context.py --- a/mercurial/context.py +++ b/mercurial/context.py @@ -1975,3 +1975,99 @@ class memfilectx(committablefilectx): def write(self, data, flags): """wraps repo.wwrite""" self._data = data + +class memlightctx(committablectx): +"""Like memctx but it's reusing the manifest of different commit. +Intended to be used by lightweight operations that are creating +metadata-only changes. + +Revision information is supplied at initialization time. 'repo' is the +current localrepo, 'ctx' is original revision which manifest we're reuisng +'parents' is a sequence of two parent revisions identifiers (pass None for +every missing parent), 'text' is the commit. + +user receives the committer name and defaults to current repository +username, date is the commit date in any format supported by +util.parsedate() and defaults to current date, extra is a dictionary of +metadata or is left empty. +""" +def __new__(cls, repo, path, *args, **kwargs): +return super(memlightctx, cls).__new__(cls, repo) + +def __init__(self, repo, originalctx, parents, text, user=None, date=None, + extra=None, editor=False): +super(memlightctx, self).__init__(repo, text, user, date, extra) +self._rev = None +self._node = None +self._originalctx = originalctx +self._manifestnode = originalctx.manifestnode() +parents = [(p or nullid) for p in parents] +p1, p2 = self._parents = [changectx(self._repo, p) for p in parents] + +# sanity check to ensure that the reused manifest parents are +# manifests of our commit parents +mp1, mp2 = self.manifestctx().parents +if p1 != nullid and p1.manifestctx().node() != mp1: +raise +if p2 != nullid and p2.manifestctx().node() != mp2: +raise + +self._files = originalctx.files() +self.substate = {} + +if extra: +self._extra = extra.copy() +else: +self._extra = {} + +if self._extra.get('branch', '') == '': +self._extra['branch'] = 'default' + +if editor: +self._text = editor(self._repo, self, []) +self._repo.savecommitmessage(self._text) + +def manifestnode(self): +return self._manifestnode + +@propertycache +def _manifestctx(self): +return self._repo.manifestlog[self._manifestnode] + +def filectx(self, path, filelog=None): +return self._originalctx.filectx(path, filelog=filelog) + +def commit(self): +"""commit context to the repo""" +return self._repo.commitctx(self) + +@property +def _manifest(self): +return self._originalctx.manifest() + +@propertycache +def _status(self): +"""Calculate exact status from ``files`` specified in the ``origctx`` +and parents manifests. +""" +man1 = self.p1().manifest() +p2 = self._parents[1] +# "1 < len(self._parents)" can't be used for checking +# existence of the 2nd parent, because "memlightctx._parents" is +# explicitly initialized by the list, of which length is 2. +if p2.node() != nullid: +man2 = p2.manifest() +managing = lambda f: f in man1 or f in man2 +else: +managing = lambda f: f in man1 + +modified, added, removed = [], [], [] +for f in self._files: +if not managing(f): +added.append(f) +elif self[f]: +modified.append(f) +else: +removed.append(f) + +return scmutil.status(modified, added, removed, [], [], [], []) ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 1 of 3 V2] manifest: expose the parents() method
# HG changeset patch # User Mateusz Kwapich <mitran...@fb.com> # Date 1479409155 28800 # Thu Nov 17 10:59:15 2016 -0800 # Node ID 7dfd4c184ee087f2c05e1bdae8a10ccefbff7a92 # Parent 96f2f50d923f94c23999df198ff16409e7539af8 manifest: expose the parents() method diff --git a/mercurial/manifest.py b/mercurial/manifest.py --- a/mercurial/manifest.py +++ b/mercurial/manifest.py @@ -1381,6 +1381,10 @@ class manifestctx(object): memmf._manifestdict = self.read().copy() return memmf +@propertycache +def parents(self): +return self._revlog().parents(self._node) + def read(self): if not self._data: if self._node == revlog.nullid: @@ -1515,6 +1519,10 @@ class treemanifestctx(object): memmf._treemanifest = self.read().copy() return memmf +@propertycache +def parents(self): +return self._revlog().parents(self._node) + def readdelta(self, shallow=False): '''Returns a manifest containing just the entries that are present in this manifest, but not in its p1 manifest. This is efficient to read ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: [PATCH] memctx: allow the memctx to reuse the manifest node
Hi, I’ll try write some tests for it – but it’s hard since we don’t have any feature that could directly benefit from it in core. Best, Mateusz On 11/16/16, 10:02 PM, "Jun Wu" <qu...@fb.com> wrote: I like the direction! I'm a bit concerned about how other existing "memctx" methods (ex. "_status", "__contains__") would work. I'd like to make sure they still have reasonable behavior with this change. See inline comments. Excerpts from Mateusz Kwapich's message of 2016-11-16 20:15:28 +: > # HG changeset patch > # User Mateusz Kwapich <mitran...@fb.com> > # Date 1479327311 0 > # Wed Nov 16 20:15:11 2016 + > # Node ID 0fd8175aa4e8a3a0cd6f637b34bfa25a103c454e > # Parent c27614f2dec1405db606d1ef871dfabf72cc0737 > memctx: allow the memctx to reuse the manifest node > > When we have a lot of files writing a new manifest revision can be expensive. > This commit adds a possibility for memctx to reuse a manifest from a different > commit. This can be beneficial for commands that are creating metadata changes > without any actual files changed like "hg metaedit" in evolve extension. > > I will send the change for evolve that leverages this once this is accepted. > > diff --git a/mercurial/context.py b/mercurial/context.py > --- a/mercurial/context.py > +++ b/mercurial/context.py > @@ -1160,6 +1160,7 @@ > changes=None): > self._repo = repo > self._rev = None > +self._manifestnode = None > self._node = None > self._text = text > if date: > @@ -1268,7 +1269,8 @@ > return None > > def manifestnode(self): > -return None > +return self._manifestnode > + > def user(self): > return self._user or self._repo.ui.username() > def date(self): > @@ -1833,11 +1835,12 @@ > # this field to determine what to do in filectxfn. > _returnnoneformissingfiles = True > > -def __init__(self, repo, parents, text, files, filectxfn, user=None, > - date=None, extra=None, editor=False): > +def __init__(self, repo, parents, text, files, filectxfn=None, user=None, > + date=None, extra=None, editor=False, manifestnode=None): IIUC, "manifestnode" conflicts with "files" / "filectxfn" here. If that's true, I think we want: - memctx constructor change: - "files" / "filectxfn" are optional, and conflict with "manifestnode" - probably worthwhile to update the class docstring - if "manifestnode" is provided: - change "memctx._manifest" to read "self._manifestnode" directly - change "memctx._files" to get the files changed lazily from manifests Then "memctx"'s other methods like "status", "filenode", "__contains__" would probably work as expected and feel consistent. If the new implementation diverges significantly, I think it may be a good idea to do it in a different class, like "memlightctx" which must reuse a manifest node. By providing the “files” we can commit the ctx without reading the manifest which gives us a serious perf boost if the manifest is large. > super(memctx, self).__init__(repo, text, user, date, extra) > self._rev = None > self._node = None > +self._manifestnode = manifestnode > parents = [(p or nullid) for p in parents] > p1, p2 = parents > self._parents = [changectx(self._repo, p) for p in (p1, p2)] > diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py > --- a/mercurial/localrepo.py > +++ b/mercurial/localrepo.py > @@ -1695,7 +1695,11 @@ > tr = self.transaction("commit") > trp = weakref.proxy(tr) > > -if ctx.files(): > +if ctx.manifestnode(): I think we want a sanity check here to prevent buggy linkrevs that can give us trouble in the future. If ctx.parents' manifestnodes are differnet from ctx.manifestnode's parents or ctx.manifestnode (for the empty commit case), abort. I’m fine with doing sanity checks. Thanks for suggestion. > +# reuse an existing manifest revision > +mn = ctx.manifestnode() > +files = ctx.files() > +elif ctx.files(): > m1ctx = p1.manifestctx() > m2ctx = p2.manifestctx() > mctx = m1ctx.copy() ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH] memctx: allow the memctx to reuse the manifest node
# HG changeset patch # User Mateusz Kwapich <mitran...@fb.com> # Date 1479327311 0 # Wed Nov 16 20:15:11 2016 + # Node ID 0fd8175aa4e8a3a0cd6f637b34bfa25a103c454e # Parent c27614f2dec1405db606d1ef871dfabf72cc0737 memctx: allow the memctx to reuse the manifest node When we have a lot of files writing a new manifest revision can be expensive. This commit adds a possibility for memctx to reuse a manifest from a different commit. This can be beneficial for commands that are creating metadata changes without any actual files changed like "hg metaedit" in evolve extension. I will send the change for evolve that leverages this once this is accepted. diff --git a/mercurial/context.py b/mercurial/context.py --- a/mercurial/context.py +++ b/mercurial/context.py @@ -1160,6 +1160,7 @@ changes=None): self._repo = repo self._rev = None +self._manifestnode = None self._node = None self._text = text if date: @@ -1268,7 +1269,8 @@ return None def manifestnode(self): -return None +return self._manifestnode + def user(self): return self._user or self._repo.ui.username() def date(self): @@ -1833,11 +1835,12 @@ # this field to determine what to do in filectxfn. _returnnoneformissingfiles = True -def __init__(self, repo, parents, text, files, filectxfn, user=None, - date=None, extra=None, editor=False): +def __init__(self, repo, parents, text, files, filectxfn=None, user=None, + date=None, extra=None, editor=False, manifestnode=None): super(memctx, self).__init__(repo, text, user, date, extra) self._rev = None self._node = None +self._manifestnode = manifestnode parents = [(p or nullid) for p in parents] p1, p2 = parents self._parents = [changectx(self._repo, p) for p in (p1, p2)] diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py --- a/mercurial/localrepo.py +++ b/mercurial/localrepo.py @@ -1695,7 +1695,11 @@ tr = self.transaction("commit") trp = weakref.proxy(tr) -if ctx.files(): +if ctx.manifestnode(): +# reuse an existing manifest revision +mn = ctx.manifestnode() +files = ctx.files() +elif ctx.files(): m1ctx = p1.manifestctx() m2ctx = p2.manifestctx() mctx = m1ctx.copy() ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 2 of 4 evolve-ext] metaedit: extend the functionality to support editing multiple commits
# HG changeset patch # User Mateusz Kwapich <mitran...@fb.com> # Date 1479324135 0 # Wed Nov 16 19:22:15 2016 + # Branch stable # Node ID b2bde478bfebc390dba8f1ee314b7bdd062ab191 # Parent 744c6acd84edf73ffdf505b9673b0383db727a0e metaedit: extend the functionality to support editing multiple commits diff --git a/hgext/evolve.py b/hgext/evolve.py --- a/hgext/evolve.py +++ b/hgext/evolve.py @@ -3257,29 +3257,57 @@ def metaedit(ui, repo, *revs, **opts): if commitopts.get('message') or commitopts.get('logfile'): commitopts['edit'] = False else: -if opts['fold']: -msgs = ["HG: This is a fold of %d changesets." % len(allctx)] -msgs += ["HG: Commit message of changeset %s.\n\n%s\n" % - (c.rev(), c.description()) for c in allctx] -else: -msgs = [head.description()] -commitopts['message'] = "\n".join(msgs) commitopts['edit'] = True # TODO: if the author and message are the same, don't create a new # hash. Right now we create a new hash because the date can be # different. -newid, created = rewrite(repo, root, allctx, head, - [root.p1().node(), root.p2().node()], - commitopts=commitopts) -if created: -if p1.rev() in revs: -newp1 = newid -phases.retractboundary(repo, tr, targetphase, [newid]) -obsolete.createmarkers(repo, [(ctx, (repo[newid],)) - for ctx in allctx]) +if opts['fold']: +if commitopts['edit']: +msgs = ["HG: This is a fold of %d changesets." % +len(allctx)] +msgs += ["HG: Commit message of changeset %s.\n\n%s\n" % + (c.rev(), c.description()) for c in allctx] +commitopts['message'] = "\n".join(msgs) + +newid, created = rewrite(repo, root, allctx, head, + [root.p1().node(), root.p2().node()], + commitopts=commitopts) +if created: +if p1.rev() in revs: +newp1 = newid +phases.retractboundary(repo, tr, targetphase, [newid]) +obsolete.createmarkers(repo, [(ctx, (repo[newid],)) + for ctx in allctx]) +else: +ui.status(_("nothing changed\n")) else: -ui.status(_("nothing changed\n")) +replacemap = {} +# we need topological order +allctx = sorted(allctx, key=lambda c: c.rev()) +for c in allctx: +if commitopts['edit']: +commitopts['message'] = \ +"HG: Commit message of changeset %s\n%s" %\ +(str(c), c.description()) +bases = [ +replacemap.get(c.p1().node(), c.p1().node()), +replacemap.get(c.p2().node(), c.p2().node()), +] +newid, created = metarewrite(repo, c, bases, + commitopts=commitopts) +if created: +replacemap[c.node()] = newid + +if p1.node() in replacemap: +newp1 = replacemap[p1.node()] +if len(replacemap) > 0: +obsolete.createmarkers(repo, [(repo[old], (repo[new],)) +for old, new in replacemap.iteritems()]) +# TODO: set poroper phase boundaries +else: +ui.status(_("nothing changed\n")) + tr.close() finally: tr.release() ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 4 of 4 evolve-ext] metaedit: use faster setparents instead of full update
# HG changeset patch # User Mateusz Kwapich <mitran...@fb.com> # Date 1479325623 0 # Wed Nov 16 19:47:03 2016 + # Branch stable # Node ID 539a0ff6a3d6664c2dd1fede40ba8e3e2efa9986 # Parent a10be4e9e682615db89200fbfb9583eaf5e05021 metaedit: use faster setparents instead of full update The working copy is not changing so there is no need to extra status call. This makes metaedit work on dirty wc. diff --git a/hgext/evolve.py b/hgext/evolve.py --- a/hgext/evolve.py +++ b/hgext/evolve.py @@ -3304,7 +3304,7 @@ def metaedit(ui, repo, *revs, **opts): if opts['fold']: ui.status('%i changesets folded\n' % len(revs)) if newp1 is not None: -hg.update(repo, newp1) +repo.setparents(newp1) finally: lockmod.release(lock, wlock) diff --git a/tests/test-evolve.t b/tests/test-evolve.t --- a/tests/test-evolve.t +++ b/tests/test-evolve.t @@ -1489,7 +1489,6 @@ check that metaedit respects allowunstab abort: cannot fold chain not ending with a head or with branching [255] $ hg metaedit --user foobar - 0 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg log --template '{rev}: {author}\n' -r '42:' --hidden 42: test 43: foobar @@ -1497,7 +1496,6 @@ check that metaedit respects allowunstab 43: foobar $ HGEDITOR="sed -i'' -e 's/safely/quickly/g'" hg metaedit '.^::.' - 0 files updated, 0 files merged, 0 files removed, 0 files unresolved $ HGEDITOR=cat hg metaedit '.^::.' --fold HG: This is a fold of 2 changesets. @@ -1519,7 +1517,6 @@ check that metaedit respects allowunstab HG: changed a HG: changed newfile 2 changesets folded - 0 files updated, 0 files merged, 0 files removed, 0 files unresolved $ glog -r . @ 45:ca7a9e928b25@default(draft) amended @@ -1553,7 +1550,6 @@ no new commit is created here because th TODO: don't create a new commit in this case $ hg metaedit --config defaults.metaedit= - 0 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg log -r '.^::.' --template '{rev}: {desc|firstline}\n' 36: add uu 46: amended @@ -1570,8 +1566,11 @@ TODO: don't create a new commit in this 47: foobar2 $ hg diff -r 45 -r 46 --hidden -'fold' one commit +'fold' one commit with dirty wc + $ echo x > newfile $ hg metaedit 39 --fold --user foobar3 1 changesets folded $ hg log -r 47 --template '{rev}: {author}\n' 47: foobar2 + $ hg st -amr + M newfile ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 3 of 4 evolve-ext] metaedit: remove the code gating the new metaedit feature and test it
# HG changeset patch # User Mateusz Kwapich <mitran...@fb.com> # Date 1479325155 0 # Wed Nov 16 19:39:15 2016 + # Branch stable # Node ID a10be4e9e682615db89200fbfb9583eaf5e05021 # Parent b2bde478bfebc390dba8f1ee314b7bdd062ab191 metaedit: remove the code gating the new metaedit feature and test it diff --git a/hgext/evolve.py b/hgext/evolve.py --- a/hgext/evolve.py +++ b/hgext/evolve.py @@ -3220,17 +3220,6 @@ def metaedit(ui, repo, *revs, **opts): lock = repo.lock() revs = scmutil.revrange(repo, revs) -if not opts['fold'] and len(revs) > 1: -# TODO: handle multiple revisions. This is somewhat tricky because -# if we want to edit a series of commits: -# -# a b c -# -# we need to rewrite a first, then directly rewrite b on top of the -# new a, then rewrite c on top of the new b. So we need to handle -# revisions in topological order. -raise error.Abort(_('editing multiple revisions without --fold is ' -'not currently supported')) if opts['fold']: root, head = _foldcheck(repo, revs) diff --git a/tests/test-evolve.t b/tests/test-evolve.t --- a/tests/test-evolve.t +++ b/tests/test-evolve.t @@ -1496,10 +1496,8 @@ check that metaedit respects allowunstab $ hg log --template '{rev}: {author}\n' -r . 43: foobar -TODO: support this - $ hg metaedit '.^::.' - abort: editing multiple revisions without --fold is not currently supported - [255] + $ HGEDITOR="sed -i'' -e 's/safely/quickly/g'" hg metaedit '.^::.' + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved $ HGEDITOR=cat hg metaedit '.^::.' --fold HG: This is a fold of 2 changesets. @@ -1507,9 +1505,9 @@ TODO: support this amended - HG: Commit message of changeset 43. + HG: Commit message of changeset 44. - will be evolved safely + will be evolved quickly @@ -1524,16 +1522,17 @@ TODO: support this 0 files updated, 0 files merged, 0 files removed, 0 files unresolved $ glog -r . - @ 44:41bf1183869c@default(draft) amended + @ 45:ca7a9e928b25@default(draft) amended | ~ no new commit is created here because the date is the same $ HGEDITOR=cat hg metaedit + HG: Commit message of changeset ca7a9e928b25 amended - will be evolved safely + will be evolved quickly HG: Enter commit message. Lines beginning with 'HG:' are removed. @@ -1546,7 +1545,7 @@ no new commit is created here because th nothing changed $ glog -r '.^::.' - @ 44:41bf1183869c@default(draft) amended + @ 45:ca7a9e928b25@default(draft) amended | o 36:43c3f5ef149f@default(draft) add uu | @@ -1557,21 +1556,22 @@ TODO: don't create a new commit in this 0 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg log -r '.^::.' --template '{rev}: {desc|firstline}\n' 36: add uu - 45: amended + 46: amended $ hg up .^ 2 files updated, 0 files merged, 0 files removed, 0 files unresolved - $ hg metaedit --user foobar2 45 + $ hg metaedit --user foobar2 46 $ hg log --template '{rev}: {author}\n' -r '42:' --hidden 42: test 43: foobar - 44: test + 44: foobar 45: test - 46: foobar2 + 46: test + 47: foobar2 $ hg diff -r 45 -r 46 --hidden 'fold' one commit $ hg metaedit 39 --fold --user foobar3 1 changesets folded $ hg log -r 47 --template '{rev}: {author}\n' - 47: foobar3 + 47: foobar2 ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 1 of 4 evolve-ext] metaedit: add a helper function for just metadata rewrites
# HG changeset patch # User Mateusz Kwapich <mitran...@fb.com> # Date 1479324110 0 # Wed Nov 16 19:21:50 2016 + # Branch stable # Node ID 744c6acd84edf73ffdf505b9673b0383db727a0e # Parent 727c7211c810d304ebf92b32db7ecf697ce46ac6 metaedit: add a helper function for just metadata rewrites It will be used by metaedit. diff --git a/hgext/evolve.py b/hgext/evolve.py --- a/hgext/evolve.py +++ b/hgext/evolve.py @@ -907,6 +907,13 @@ def rewrite(repo, old, updates, head, ne finally: lockmod.release(tr, lock, wlock) +def metarewrite(repo, old, newbases, commitopts): +'''Like rewrite but affects only the changeset metadata.''' +# TODO: reuse the manifest for speed +newid, created = rewrite(repo, old, [old], old, newbases, + commitopts=commitopts) +return newid, created + class MergeFailure(error.Abort): pass ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 2 of 2] py3: make encodefun in store.py compatible with py3k
# HG changeset patch # User Mateusz Kwapich <mitran...@fb.com> # Date 1475942045 25200 # Sat Oct 08 08:54:05 2016 -0700 # Node ID 086b25d1866e33fb7ebbe6c51522e6b573e281e2 # Parent 225efa4bf7f497e55f0ba57f64a33dce39eaeb29 py3: make encodefun in store.py compatible with py3k This ensures that the filename encoding functions always map bytestrings to bytestrings regardless of python version. diff --git a/mercurial/store.py b/mercurial/store.py --- a/mercurial/store.py +++ b/mercurial/store.py @@ -16,6 +16,7 @@ from .i18n import _ from . import ( error, parsers, +pycompat, scmutil, util, ) @@ -98,11 +99,20 @@ def _buildencodefun(): 'the\\x07quick\\xadshot' ''' e = '_' -cmap = dict([(chr(x), chr(x)) for x in xrange(127)]) +if pycompat.ispy3: +xchr = lambda x: bytes([x]) +asciistr = bytes(xrange(127)) +else: +xchr = chr +asciistr = map(chr, xrange(127)) +capitals = list(range(ord("A"), ord("Z") + 1)) + +cmap = {x:x for x in asciistr} for x in _reserved(): -cmap[chr(x)] = "~%02x" % x -for x in list(range(ord("A"), ord("Z") + 1)) + [ord(e)]: -cmap[chr(x)] = e + chr(x).lower() +cmap[xchr(x)] = "~%02x" % x +for x in capitals + [ord(e)]: +cmap[xchr(x)] = e + xchr(x).lower() + dmap = {} for k, v in cmap.iteritems(): dmap[v] = k diff --git a/tests/test-check-py3-compat.t b/tests/test-check-py3-compat.t --- a/tests/test-check-py3-compat.t +++ b/tests/test-check-py3-compat.t @@ -134,7 +134,6 @@ mercurial/sshserver.py: error importing: module 'mercurial.util' has no attribute 'stringio' (error at patch.py:*) mercurial/statichttprepo.py: error importing: module 'mercurial.util' has no attribute 'urlerr' (error at byterange.py:*) mercurial/store.py: error importing module: name 'xrange' is not defined (line *) - mercurial/streamclone.py: error importing: can't concat bytes to str (error at store.py:*) mercurial/subrepo.py: error importing: module 'mercurial.util' has no attribute 'stringio' (error at patch.py:*) mercurial/templatefilters.py: error importing: module 'mercurial.util' has no attribute 'stringio' (error at patch.py:*) mercurial/templatekw.py: error importing: module 'mercurial.util' has no attribute 'stringio' (error at patch.py:*) ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: [PATCH] dirstate: rebuild should update dirstate properly
Yeah, the V2 is good to go. On 9/29/16, 4:25 PM, "Yuya Nishihara" <you...@gmail.com on behalf of y...@tcha.org> wrote: On Tue, 27 Sep 2016 14:06:24 +, Mateusz Kwapich wrote: > On rebuild all the files are set to match the files in the revision but the revision doesn’t provide > us the correct mtimes to set. I’d argue it’s better to set the mtimes to value explicitly meaning > “unknown” than to a prepared arbitrary value. > Same for file mode: why do we even try to guess the file mode on disk? No idea why. I've checked b304c2496f52, which introduced the test, and a8f7791e3680, which introduced dirstate.rebuild(). What I can guess from them is we would set file modes because it was easy at that time. So I lean toward taking this patch. Is V2 the latest one? https://urldefense.proofpoint.com/v2/url?u=https-3A__patchwork.mercurial-2Dscm.org_patch_16496_=DQIDaQ=5VD0RTtNlTh3ycd41b3MUw=dK7q_6fOymlfdGMBe3wUaA=IsQ5Wde9tUWKzdvJxsr1Q4dsToyR34vkGXTiJ5Y6MAA=jPbJFKknCiMqPmtuWIpcn9-jehbUzpuFhTGdZQ6c33g= ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: [PATCH] dirstate: rebuild should update dirstate properly
Hey, On rebuild all the files are set to match the files in the revision but the revision doesn’t provide us the correct mtimes to set. I’d argue it’s better to set the mtimes to value explicitly meaning “unknown” than to a prepared arbitrary value. Same for file mode: why do we even try to guess the file mode on disk? Does anybody use “hg debugrebuilddirstate” as a method of dirstate population? I’ve only seen it used as a method of the whole dirstate invalidation. Best, Mateusz On 8/31/16, 2:55 PM, "Yuya Nishihara" <you...@gmail.com on behalf of y...@tcha.org> wrote: On Tue, 30 Aug 2016 21:34:01 +, Mateusz Kwapich wrote: > On 8/28/16, 3:01 PM, "Yuya Nishihara" <you...@gmail.com on behalf of y...@tcha.org> wrote: > > Updating dirstate by simply adding and dropping files from self._map doesn't > > keep the other maps updated (think: _dirs, _copymap, _foldmap, _nonormalset) > > thus introducing cache inconsistency. > > I think that's why rebuild() calls self.clear(). IIRC, I've pointed out that it > would be wrong to skip the whole clear() step when changedfiles is not None. > > Clear doesn’t invalidate all the caches – for example it doesn’t touch filefoldmap or > Dirfoldmap. Also: why would we clear those caches instead of updating them? Perhaps that's a bug introduced when file/dirfoldmap were added. > > --- a/tests/test-rebuildstate.t > > +++ b/tests/test-rebuildstate.t > > @@ -48,8 +48,8 @@ basic test for hg debugrebuildstate > > state dump after > > > >$ hg debugstate --nodates | sort > > - n 644 -1 set bar > > - n 644 -1 set foo > > + n 0 -1 unset bar > > + n 0 -1 unset foo > > This seems wrong. debugrebuilddirstate is documented as "dirstate will be > set to the files of the given revision. The actual working directory content > [...] is not considered." > > Yeah. It still doesn’t consider the working directory contents, still contains the same files > and all entries will still be checked at the next status call. Only things that are different in > this output are file permissions and mtime which are not stored in the commit itself. My concern is there might be a reason to fully populate a dirstate by "hg debugrebuilddirstate" because the result is explicitly tested by "hg debugstate". I hope my concern isn't a thing, but I'm not sure. ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH V2] dirstate: rebuild should update dirstate properly
# HG changeset patch # User Mateusz Kwapich <mitran...@fb.com> # Date 1472595388 25200 # Tue Aug 30 15:16:28 2016 -0700 # Node ID aff2b9911d78a3d427e3ba18a565e76215971948 # Parent 12f8bef59bfa2739d0c5d8425ab494fd2fe38a81 dirstate: rebuild should update dirstate properly Updating dirstate by simply adding and dropping files from self._map doesn't keep the other maps updated (think: _dirs, _copymap, _foldmap, _nonormalset) thus introducing cache inconsistency. This is also affecting the debugstate tests since now we don't even try to set correct mode and mtime for the files because they are marked dirty anyway and will be checked during next status call. diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -680,21 +680,15 @@ class dirstate(object): self.clear() self._lastnormaltime = lastnormaltime -for f in changedfiles: -mode = 0o666 -if f in allfiles and 'x' in allfiles.flags(f): -mode = 0o777 - -if f in allfiles: -self._map[f] = dirstatetuple('n', mode, -1, 0) -else: -self._map.pop(f, None) -if f in self._nonnormalset: -self._nonnormalset.remove(f) - if self._origpl is None: self._origpl = self._pl self._pl = (parent, nullid) +for f in changedfiles: +if f in allfiles: +self.normallookup(f) +else: +self.drop(f) + self._dirty = True def write(self, tr): diff --git a/tests/test-rebuildstate.t b/tests/test-rebuildstate.t --- a/tests/test-rebuildstate.t +++ b/tests/test-rebuildstate.t @@ -48,8 +48,8 @@ basic test for hg debugrebuildstate state dump after $ hg debugstate --nodates | sort - n 644 -1 set bar - n 644 -1 set foo + n 0 -1 unset bar + n 0 -1 unset foo $ hg debugadddrop --normal-lookup file1 file2 $ hg debugadddrop --drop bar @@ -57,7 +57,7 @@ state dump after $ hg debugstate --nodates n 0 -1 unset file1 n 0 -1 unset file2 - n 644 -1 set foo + n 0 -1 unset foo $ hg debugrebuildstate status @@ -115,7 +115,7 @@ dirstate $ hg debugrebuilddirstate --minimal $ hg debugdirstate --nodates r 0 0 * bar (glob) - n 644 -1 * foo (glob) + n 0 -1 * foo (glob) a 0 -1 * qux (glob) $ hg status -A A qux ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: [PATCH] localrepo: actually invalidate dirstate on invalidatedirstate()
So invalidate is by design not-always invalidating and it’s relying on filecache to actually invalidate dirstate when needed? Why do we even need the invalidate in this case if filecache can always properly invalidate dirstate? Best, Mateusz On 8/26/16, 5:02 PM, "Yuya Nishihara" <you...@gmail.com on behalf of y...@tcha.org> wrote: On Fri, 26 Aug 2016 08:31:09 -0700, Mateusz Kwapich wrote: > # HG changeset patch > # User Mateusz Kwapich <mitran...@fb.com> > # Date 1472225341 25200 > # Fri Aug 26 08:29:01 2016 -0700 > # Node ID 430e8c2cc229e0fbed231370c36cc2a215ecb30e > # Parent 318e2b600b80e4ed3c6f37df46ec7544f60d4c0b > localrepo: actually invalidate dirstate on invalidatedirstate() > > The old dirstate was still present in the filecache. It was invalidated only > because filecache did stat the file on the next access. This can lead to errors > in the case when file stat didn't change but the dirstate contents did change > (and we know about it and thats why we sometimes call dirstateinvalidate() to > refresh it). (+CC foozy since he's working on fixing timestamp issues) IIRC, that's by design. invalidate() just forces to compare timestamps to see if in-memory cache is valid. ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH] localrepo: actually invalidate dirstate on invalidatedirstate()
# HG changeset patch # User Mateusz Kwapich <mitran...@fb.com> # Date 1472225341 25200 # Fri Aug 26 08:29:01 2016 -0700 # Node ID 430e8c2cc229e0fbed231370c36cc2a215ecb30e # Parent 318e2b600b80e4ed3c6f37df46ec7544f60d4c0b localrepo: actually invalidate dirstate on invalidatedirstate() The old dirstate was still present in the filecache. It was invalidated only because filecache did stat the file on the next access. This can lead to errors in the case when file stat didn't change but the dirstate contents did change (and we know about it and thats why we sometimes call dirstateinvalidate() to refresh it). After this patch we will also invalidate the filecache on dirstateinvalidate. diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py --- a/mercurial/localrepo.py +++ b/mercurial/localrepo.py @@ -1243,6 +1243,7 @@ class localrepository(object): delattr(self.dirstate, k) except AttributeError: pass +self._filecache.pop('dirstate', None) delattr(self.unfiltered(), 'dirstate') def invalidate(self, clearfilecache=False): ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 1 of 2 V3] dirstate: add callback to notify extensions about wd parent change
# HG changeset patch # User Mateusz Kwapich <mitran...@fb.com> # Date 1470927641 25200 # Thu Aug 11 08:00:41 2016 -0700 # Node ID ab4af8f1ddcae13159edf3eeea9fa4358c93babf # Parent 5e2365698d448c2a1d75f6a58e11ec65f66a0266 dirstate: add callback to notify extensions about wd parent change The journal extension had to touch the dirstate internals to be notified about wd parent change. To make that detection cleaner and reusable let's move it core. Now the extension can register to be notified about parent changes. diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -101,6 +101,8 @@ class dirstate(object): self._parentwriters = 0 self._filename = 'dirstate' self._pendingfilename = '%s.pending' % self._filename +self._plchangecallbacks = {} +self._origpl = None # for consistent view between _pl() and _read() invocations self._pendingmode = None @@ -347,6 +349,8 @@ class dirstate(object): self._dirty = self._dirtypl = True oldp2 = self._pl[1] +if self._origpl is None: +self._origpl = self._pl self._pl = p1, p2 copies = {} if oldp2 != nullid and p2 == nullid: @@ -442,6 +446,7 @@ class dirstate(object): self._lastnormaltime = 0 self._dirty = False self._parentwriters = 0 +self._origpl = None def copy(self, source, dest): """Mark dest as a copy of source. Unmark dest if source is None.""" @@ -687,6 +692,8 @@ class dirstate(object): if f in self._nonnormalset: self._nonnormalset.remove(f) +if self._origpl is None: +self._origpl = self._pl self._pl = (parent, nullid) self._dirty = True @@ -721,7 +728,23 @@ class dirstate(object): st = self._opener(filename, "w", atomictemp=True, checkambig=True) self._writedirstate(st) +def addparentchangecallback(self, category, callback): +"""add a callback to be called when the wd parents are changed + +Callback will be called with the following arguments: +dirstate, (oldp1, oldp2), (newp1, newp2) + +Category is a unique identifier to allow overwriting an old callback +with a newer callback. +""" +self._plchangecallbacks[category] = callback + def _writedirstate(self, st): +# notify callbacks about parents change +if self._origpl is not None and self._origpl != self._pl: +for c, callback in sorted(self._plchangecallbacks.iteritems()): +callback(self, self._origpl, self._pl) +self._origpl = None # use the modification time of the newly created temporary file as the # filesystem's notion of 'now' now = util.fstat(st).st_mtime & _rangemask ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 2 of 2 V3] journal: use the dirstate parentchange callbacks
# HG changeset patch # User Mateusz Kwapich <mitran...@fb.com> # Date 1470759346 25200 # Tue Aug 09 09:15:46 2016 -0700 # Node ID cd05624a1a1c133a0086e85a142dc65641a4e208 # Parent ab4af8f1ddcae13159edf3eeea9fa4358c93babf journal: use the dirstate parentchange callbacks Instead of hacking into dirstate internals let's use the callbacks to be notified about wd parent change. diff --git a/hgext/journal.py b/hgext/journal.py --- a/hgext/journal.py +++ b/hgext/journal.py @@ -24,7 +24,6 @@ from mercurial import ( bookmarks, cmdutil, commands, -dirstate, dispatch, error, extensions, @@ -63,8 +62,6 @@ def extsetup(ui): extensions.wrapfunction(dispatch, 'runcommand', runcommand) extensions.wrapfunction(bookmarks.bmstore, '_write', recordbookmarks) extensions.wrapfunction( -dirstate.dirstate, '_writedirstate', recorddirstateparents) -extensions.wrapfunction( localrepo.localrepository.dirstate, 'func', wrapdirstate) extensions.wrapfunction(hg, 'postshare', wrappostshare) extensions.wrapfunction(hg, 'copystore', unsharejournal) @@ -84,34 +81,19 @@ def wrapdirstate(orig, repo): dirstate = orig(repo) if util.safehasattr(repo, 'journal'): dirstate.journalstorage = repo.journal +dirstate.addparentchangecallback('journal', recorddirstateparents) return dirstate -def recorddirstateparents(orig, dirstate, dirstatefp): +def recorddirstateparents(dirstate, old, new): """Records all dirstate parent changes in the journal.""" +old = list(old) +new = list(new) if util.safehasattr(dirstate, 'journalstorage'): -old = [node.nullid, node.nullid] -nodesize = len(node.nullid) -try: -# The only source for the old state is in the dirstate file still -# on disk; the in-memory dirstate object only contains the new -# state. dirstate._opendirstatefile() switches beteen .hg/dirstate -# and .hg/dirstate.pending depending on the transaction state. -with dirstate._opendirstatefile() as fp: -state = fp.read(2 * nodesize) -if len(state) == 2 * nodesize: -old = [state[:nodesize], state[nodesize:]] -except IOError: -pass - -new = dirstate.parents() -if old != new: -# only record two hashes if there was a merge -oldhashes = old[:1] if old[1] == node.nullid else old -newhashes = new[:1] if new[1] == node.nullid else new -dirstate.journalstorage.record( -wdirparenttype, '.', oldhashes, newhashes) - -return orig(dirstate, dirstatefp) +# only record two hashes if there was a merge +oldhashes = old[:1] if old[1] == node.nullid else old +newhashes = new[:1] if new[1] == node.nullid else new +dirstate.journalstorage.record( +wdirparenttype, '.', oldhashes, newhashes) # hooks to record bookmark changes (both local and remote) def recordbookmarks(orig, store, fp): ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 1 of 2 V2] dirstate: add callback to notify extensions about wd parent change
# HG changeset patch # User Mateusz Kwapich <mitran...@fb.com> # Date 1470759366 25200 # Tue Aug 09 09:16:06 2016 -0700 # Node ID 2abe7cd83b7db665c8a8869ecb3caa10eccd7cc3 # Parent 5e2365698d448c2a1d75f6a58e11ec65f66a0266 dirstate: add callback to notify extensions about wd parent change The journal extension had to touch the dirstate internals to be notified about wd parent change. To make that detection cleaner and reusable let's move it core. Now the extension can register to be notified about parent changes. diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -101,6 +101,8 @@ class dirstate(object): self._parentwriters = 0 self._filename = 'dirstate' self._pendingfilename = '%s.pending' % self._filename +self._plchangecallbacks = {} +self._origpl = None # for consistent view between _pl() and _read() invocations self._pendingmode = None @@ -347,6 +349,8 @@ class dirstate(object): self._dirty = self._dirtypl = True oldp2 = self._pl[1] +if self._origpl is None: +self._origpl = self._pl self._pl = p1, p2 copies = {} if oldp2 != nullid and p2 == nullid: @@ -442,6 +446,7 @@ class dirstate(object): self._lastnormaltime = 0 self._dirty = False self._parentwriters = 0 +self._origpl = None def copy(self, source, dest): """Mark dest as a copy of source. Unmark dest if source is None.""" @@ -721,7 +726,24 @@ class dirstate(object): st = self._opener(filename, "w", atomictemp=True, checkambig=True) self._writedirstate(st) +def addparentchangecallback(self, category, callback): +"""add a callback to be called when the wd parents are changed + +Callback will be called with the following arguments: +dirstate, (oldp1, oldp2), (newp1, newp2) + +Category is a unique identifier to allow overwriting an old callback +with a newer callback. +""" +self._plchangecallbacks[category] = callback + def _writedirstate(self, st): +# notify callbacks about parents change +if self._origpl is not None: +if self._origpl != self._pl: +for c, callback in sorted(self._plchangecallbacks.iteritems()): +callback(self, self._origpl, self._pl) +self._origpl = None # use the modification time of the newly created temporary file as the # filesystem's notion of 'now' now = util.fstat(st).st_mtime & _rangemask ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 2 of 2 V2] journal: use the dirstate parentchange callbacks
# HG changeset patch # User Mateusz Kwapich <mitran...@fb.com> # Date 1470759346 25200 # Tue Aug 09 09:15:46 2016 -0700 # Node ID 027a1548685a1e6815d20e39aa55ed8c2c68b5e3 # Parent 2abe7cd83b7db665c8a8869ecb3caa10eccd7cc3 journal: use the dirstate parentchange callbacks Instead of hacking into dirstate internals let's use the callbacks to be notified about wd parent change. diff --git a/hgext/journal.py b/hgext/journal.py --- a/hgext/journal.py +++ b/hgext/journal.py @@ -24,7 +24,6 @@ from mercurial import ( bookmarks, cmdutil, commands, -dirstate, dispatch, error, extensions, @@ -63,8 +62,6 @@ def extsetup(ui): extensions.wrapfunction(dispatch, 'runcommand', runcommand) extensions.wrapfunction(bookmarks.bmstore, '_write', recordbookmarks) extensions.wrapfunction( -dirstate.dirstate, '_writedirstate', recorddirstateparents) -extensions.wrapfunction( localrepo.localrepository.dirstate, 'func', wrapdirstate) extensions.wrapfunction(hg, 'postshare', wrappostshare) extensions.wrapfunction(hg, 'copystore', unsharejournal) @@ -84,34 +81,19 @@ def wrapdirstate(orig, repo): dirstate = orig(repo) if util.safehasattr(repo, 'journal'): dirstate.journalstorage = repo.journal +dirstate.addparentchangecallback('journal', recorddirstateparents) return dirstate -def recorddirstateparents(orig, dirstate, dirstatefp): +def recorddirstateparents(dirstate, old, new): """Records all dirstate parent changes in the journal.""" +old = list(old) +new = list(new) if util.safehasattr(dirstate, 'journalstorage'): -old = [node.nullid, node.nullid] -nodesize = len(node.nullid) -try: -# The only source for the old state is in the dirstate file still -# on disk; the in-memory dirstate object only contains the new -# state. dirstate._opendirstatefile() switches beteen .hg/dirstate -# and .hg/dirstate.pending depending on the transaction state. -with dirstate._opendirstatefile() as fp: -state = fp.read(2 * nodesize) -if len(state) == 2 * nodesize: -old = [state[:nodesize], state[nodesize:]] -except IOError: -pass - -new = dirstate.parents() -if old != new: -# only record two hashes if there was a merge -oldhashes = old[:1] if old[1] == node.nullid else old -newhashes = new[:1] if new[1] == node.nullid else new -dirstate.journalstorage.record( -wdirparenttype, '.', oldhashes, newhashes) - -return orig(dirstate, dirstatefp) +# only record two hashes if there was a merge +oldhashes = old[:1] if old[1] == node.nullid else old +newhashes = new[:1] if new[1] == node.nullid else new +dirstate.journalstorage.record( +wdirparenttype, '.', oldhashes, newhashes) # hooks to record bookmark changes (both local and remote) def recordbookmarks(orig, store, fp): ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 2 of 2] journal: use the dirstate parentchange callbacks
# HG changeset patch # User Mateusz Kwapich <mitran...@fb.com> # Date 1470681797 25200 # Mon Aug 08 11:43:17 2016 -0700 # Node ID cee42d2bce7f34cae842d0f868e829ff3c7ecd04 # Parent e7d2b86e2ee35fcb7b4e6ef6a4d0c6f696331f42 journal: use the dirstate parentchange callbacks Instead of hacking into dirstate internals let's use the callbacks to be notified about wd parent change. diff --git a/hgext/journal.py b/hgext/journal.py --- a/hgext/journal.py +++ b/hgext/journal.py @@ -24,7 +24,6 @@ from mercurial import ( bookmarks, cmdutil, commands, -dirstate, dispatch, error, extensions, @@ -63,8 +62,6 @@ def extsetup(ui): extensions.wrapfunction(dispatch, 'runcommand', runcommand) extensions.wrapfunction(bookmarks.bmstore, '_write', recordbookmarks) extensions.wrapfunction( -dirstate.dirstate, '_writedirstate', recorddirstateparents) -extensions.wrapfunction( localrepo.localrepository.dirstate, 'func', wrapdirstate) extensions.wrapfunction(hg, 'postshare', wrappostshare) extensions.wrapfunction(hg, 'copystore', unsharejournal) @@ -84,34 +81,19 @@ def wrapdirstate(orig, repo): dirstate = orig(repo) if util.safehasattr(repo, 'journal'): dirstate.journalstorage = repo.journal +dirstate.addparentchangecallback(recorddirstateparents) return dirstate -def recorddirstateparents(orig, dirstate, dirstatefp): +def recorddirstateparents(dirstate, old, new): """Records all dirstate parent changes in the journal.""" +old = list(old) +new = list(new) if util.safehasattr(dirstate, 'journalstorage'): -old = [node.nullid, node.nullid] -nodesize = len(node.nullid) -try: -# The only source for the old state is in the dirstate file still -# on disk; the in-memory dirstate object only contains the new -# state. dirstate._opendirstatefile() switches beteen .hg/dirstate -# and .hg/dirstate.pending depending on the transaction state. -with dirstate._opendirstatefile() as fp: -state = fp.read(2 * nodesize) -if len(state) == 2 * nodesize: -old = [state[:nodesize], state[nodesize:]] -except IOError: -pass - -new = dirstate.parents() -if old != new: -# only record two hashes if there was a merge -oldhashes = old[:1] if old[1] == node.nullid else old -newhashes = new[:1] if new[1] == node.nullid else new -dirstate.journalstorage.record( -wdirparenttype, '.', oldhashes, newhashes) - -return orig(dirstate, dirstatefp) +# only record two hashes if there was a merge +oldhashes = old[:1] if old[1] == node.nullid else old +newhashes = new[:1] if new[1] == node.nullid else new +dirstate.journalstorage.record( +wdirparenttype, '.', oldhashes, newhashes) # hooks to record bookmark changes (both local and remote) def recordbookmarks(orig, store, fp): ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 1 of 2] dirstate: add callback to notify extensions about wd parent change
# HG changeset patch # User Mateusz Kwapich <mitran...@fb.com> # Date 1470681797 25200 # Mon Aug 08 11:43:17 2016 -0700 # Node ID e7d2b86e2ee35fcb7b4e6ef6a4d0c6f696331f42 # Parent 5e2365698d448c2a1d75f6a58e11ec65f66a0266 dirstate: add callback to notify extensions about wd parent change The journal extension had to touch the dirstate internals to be notified about wd parent change. To make that detection cleaner and reusable let's move it core. Now the extension can register to be notified about parent changes. diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py --- a/mercurial/dirstate.py +++ b/mercurial/dirstate.py @@ -101,6 +101,8 @@ class dirstate(object): self._parentwriters = 0 self._filename = 'dirstate' self._pendingfilename = '%s.pending' % self._filename +self._parentchangecallbacks = [] +self._origpl = None # for consistent view between _pl() and _read() invocations self._pendingmode = None @@ -347,6 +349,8 @@ class dirstate(object): self._dirty = self._dirtypl = True oldp2 = self._pl[1] +if self._origpl is None: +self._origpl = self._pl self._pl = p1, p2 copies = {} if oldp2 != nullid and p2 == nullid: @@ -442,6 +446,7 @@ class dirstate(object): self._lastnormaltime = 0 self._dirty = False self._parentwriters = 0 +self._origpl = None def copy(self, source, dest): """Mark dest as a copy of source. Unmark dest if source is None.""" @@ -721,7 +726,16 @@ class dirstate(object): st = self._opener(filename, "w", atomictemp=True, checkambig=True) self._writedirstate(st) +def addparentchangecallback(self, callback): +self._parentchangecallbacks.append(callback) + def _writedirstate(self, st): +# notify callbacks about parents change +if self._origpl is not None: +if self._origpl != self._pl: +for callback in self._parentchangecallbacks: +callback(self, self._origpl, self._pl) +self._origpl = None # use the modification time of the newly created temporary file as the # filesystem's notion of 'now' now = util.fstat(st).st_mtime & _rangemask ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel