Re: [PATCH stable] tests: use an absolute path to get around '..' being invalid on a dead CWD
> On Jan 19, 2017, at 4:44 PM, Sean Farleywrote: > > I guess so ... crazy freebsd. I had another thought on the way home: this might only be a problem in a jail - it’s not the first bit of strange behavior we’ve caught with hg when running in my buildbot-in-a-jail. In any event, I think this fixes the last test failure on the BSD builder. ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: [PATCH STABLE] pager: wrap _runcommand() no matter if stdout is redirected
On Thu, Jan 19, 2017 at 11:39:24PM +0900, Yuya Nishihara wrote: > # HG changeset patch > # User Yuya Nishihara> # Date 1484834492 -32400 > # Thu Jan 19 23:01:32 2017 +0900 > # Branch stable > # Node ID f3ca8b7e0e2df7507661adf5957c51e39bc6b5b1 > # Parent 262c2be8ea5a4f36fb7348cb5a940cf78f2dffa9 > pager: wrap _runcommand() no matter if stdout is redirected Queued for stable. Thanks! > > We've made chg utilize the common code path implemented in pager.py (by > 815e1cefd082 and 493935e0327a), but the chg server does not always start > with a tty. Because of this, uisetup() of the pager extension could be > skipped on the chg server. > > Kudos given to Sean Farley for dogfooding new chg and spotting this problem. > > diff --git a/hgext/pager.py b/hgext/pager.py > --- a/hgext/pager.py > +++ b/hgext/pager.py > @@ -115,9 +115,6 @@ def _runpager(ui, p): > pager.wait() > > def uisetup(ui): > -if '--debugger' in sys.argv or not ui.formatted(): > -return > - > class pagerui(ui.__class__): > def _runpager(self, pagercmd): > _runpager(self, pagercmd) > @@ -130,7 +127,7 @@ def uisetup(ui): > always = util.parsebool(options['pager']) > auto = options['pager'] == 'auto' > > -if not p: > +if not p or '--debugger' in sys.argv or not ui.formatted(): > pass > elif always: > usepager = True > diff --git a/tests/test-chg.t b/tests/test-chg.t > --- a/tests/test-chg.t > +++ b/tests/test-chg.t > @@ -32,6 +32,38 @@ long socket path > >$ cd .. > > +pager > +- > + > + $ cat >> fakepager.py < + > import sys > + > for line in sys.stdin: > + > sys.stdout.write('paged! %r\n' % line) > + > EOF > + > +enable pager extension globally, but spawns the master server with no tty: > + > + $ chg init pager > + $ cd pager > + $ cat >> $HGRCPATH < + > [extensions] > + > pager = > + > [pager] > + > pager = python $TESTTMP/fakepager.py > + > EOF > + $ chg version > /dev/null > + $ touch foo > + $ chg ci -qAm foo > + > +pager should be enabled if the attached client has a tty: > + > + $ chg log -l1 -q --config ui.formatted=True > + paged! '0:1f7b0de80e11\n' > + $ chg log -l1 -q --config ui.formatted=False > + 0:1f7b0de80e11 > + > + $ cd .. > + > server lifecycle > > > diff --git a/tests/test-pager.t b/tests/test-pager.t > --- a/tests/test-pager.t > +++ b/tests/test-pager.t > @@ -152,6 +152,11 @@ doesn't result in history being paged. >summary: modify a 9 > > > +Pager should not start if stdout is not a tty. > + > + $ hg log -l1 -q --config ui.formatted=False > + 10:46106edeeb38 > + > Pager with color enabled allows colors to come through by default, > even though stdout is no longer a tty. >$ cat >> $HGRCPATH < ___ > Mercurial-devel mailing list > Mercurial-devel@mercurial-scm.org > 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 shelve-ext] shelve: make unshelve not crash when there are missing files (issue4176)
On Thu, Jan 19, 2017 at 09:51:24AM -0800, Kostia Balytskyi wrote: > # HG changeset patch > # User Kostia Balytskyi> # Date 1484848120 28800 > # Thu Jan 19 09:48:40 2017 -0800 > # Node ID 2a1e998e369d2c5dc828ba805beceb15459746cd > # Parent 9f264adbe75bfae8551dc0e6e0fce8d43fc7b43a > shelve: make unshelve not crash when there are missing files (issue4176) Queued for stable, thanks. > > This patch makes it possible to unshelve while having missing files > in your repo as long as shelved changes don't touch those missing files. > It also makes error message better otherwise. > > diff --git a/hgext/shelve.py b/hgext/shelve.py > --- a/hgext/shelve.py > +++ b/hgext/shelve.py > @@ -650,7 +650,7 @@ def _commitworkingcopychanges(ui, repo, > # contains unknown files that are part of the pending change > s = repo.status() > addedbefore = frozenset(s.added) > -if not (s.modified or s.added or s.removed or s.deleted): > +if not (s.modified or s.added or s.removed): > return tmpwctx, addedbefore > ui.status(_("temporarily committing pending changes " > "(restore with 'hg unshelve --abort')\n")) > @@ -729,6 +729,17 @@ def _finishunshelve(repo, oldtiprev, tr) > repo.unfiltered().changelog.strip(oldtiprev, tr) > _aborttransaction(repo) > > +def _checkunshelveuntrackedproblems(ui, repo, shelvectx): > +"""Check potential problems which may result from working > +copy having untracked changes.""" > +wcdeleted = set(repo.status().deleted) > +shelvetouched = set(shelvectx.files()) > +intersection = wcdeleted.intersection(shelvetouched) > +if intersection: > +m = _("shelved change touches missing files") > +hint = _("run hg status to see which files are missing") > +raise error.Abort(m, hint=hint) > + > @command('unshelve', > [('a', 'abort', None, > _('abort an incomplete unshelve operation')), > @@ -857,7 +868,7 @@ def _dounshelve(ui, repo, *shelved, **op > tmpwctx) > > repo, shelvectx = _unshelverestorecommit(ui, repo, basename, > oldquiet) > - > +_checkunshelveuntrackedproblems(ui, repo, shelvectx) > branchtorestore = '' > if shelvectx.branch() != shelvectx.p1().branch(): > branchtorestore = shelvectx.branch() > diff --git a/tests/test-shelve.t b/tests/test-shelve.t > --- a/tests/test-shelve.t > +++ b/tests/test-shelve.t > @@ -1710,3 +1710,30 @@ Unshelve respects --keep even if user in >$ hg shelve --list >default (*s ago)changes to: 1 (glob) >$ cd .. > + > +Unshelving when there are deleted files does not crash (issue4176) > + $ hg init unshelve-deleted-file && cd unshelve-deleted-file > + $ echo a > a && echo b > b && hg ci -Am ab > + adding a > + adding b > + $ echo aa > a && hg shelve > + shelved as default > + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved > + $ rm b > + $ hg st > + ! b > + $ hg unshelve > + unshelving change 'default' > + $ hg shelve > + shelved as default > + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved > + $ rm a && echo b > b > + $ hg st > + ! a > + $ hg unshelve > + unshelving change 'default' > + abort: shelved change touches missing files > + (run hg status to see which files are missing) > + [255] > + $ hg st > + ! a > ___ > Mercurial-devel mailing list > Mercurial-devel@mercurial-scm.org > 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 stable] tests: use an absolute path to get around '..' being invalid on a dead CWD
Augie Facklerwrites: > # HG changeset patch > # User Augie Fackler > # Date 1484861029 18000 > # Thu Jan 19 16:23:49 2017 -0500 > # Branch stable > # Node ID 34923f3f218573ec57575f4ae59cb4198cc6a313 > # Parent b3d2e8cce78c04dbf6f20f0846b6ea8b622983c4 > tests: use an absolute path to get around '..' being invalid on a dead CWD > > Only FreeBSD seems to be this picky. Note that this explicit > absolute-path `cd` exposes a defect in the test, in that we end up > still inside the cwd-vanish repository, but that's not a regression in > this change. Since we're in a code freeze, I'm doing the smallest > thing possible to try and fix bugs on FreeBSD, rather than cleaning up > the entire problem. I'll follow up with a more complete fix after the > freeze. I guess so ... crazy freebsd. ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: [PATCH v9 RFC] scmutil: add a simple key-value file helper
Kostia Balytskyiwrites: > # HG changeset patch > # User Kostia Balytskyi > # Date 1484824655 28800 > # Thu Jan 19 03:17:35 2017 -0800 > # Node ID 19a449c91ef14e691cf1347748473e0094fedc86 > # Parent 9f264adbe75bfae8551dc0e6e0fce8d43fc7b43a > scmutil: add a simple key-value file helper > > The purpose of the added class is to serve purposes like save files of shelve > or state files of shelve, rebase and histedit. Keys of these files can be > alphanumeric and start with letters, while values must not contain newlines. > Keys which start with an uppercase letter are required, while other keys > are optional. > > In light of Mercurial's reluctancy to use Python's json module, this tries > to provide a reasonable alternative for a non-nested named data. > Comparing to current approach of storing state in plain text files, where > semantic meaning of lines of text is only determined by their oreder, > simple key-value file allows for reordering lines and thus helps handle > optional values. > > Initial use-case I see for this is obs-shelve's shelve files. Later we > can possibly migrate state files to this approach. > > The test is in a new file beause I did not figure out where to put it > within existing test suite. If you give me a better idea, I will gladly > follow it. Let's hold off until the freeze is over. ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: [PATCH 1 of 5] pager: stdout is line buffered by default
On Thu, Jan 19, 2017 at 11:02 AM, Simon Farnsworthwrote: > # HG changeset patch > # User Simon Farnsworth > # Date 1484835774 28800 > # Thu Jan 19 06:22:54 2017 -0800 > # Node ID 76123ae2e0ccaa58db3d4fc26b75b7251e13ad16 > # Parent 036c37bd3ec189480647ff568cee9e0b43a5bc81 > pager: stdout is line buffered by default We're in a code freeze and accept patches for the stable branch only, so please resend this after the freeze is over (~ Feb 3). ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: [PATCH 4 of 5] ui: log time spent blocked on the user
Simon Farnsworthwrites: > # HG changeset patch > # User Simon Farnsworth > # Date 1484842917 28800 > # Thu Jan 19 08:21:57 2017 -0800 > # Node ID 93221dde2fad942c4f920dab1f346da71ac8033d > # Parent e8cd90ea5d3eee923304c64a19c3be9bce50451c > ui: log time spent blocked on the user > > We use a wrapper around Mercurial at Facebook that logs key statistics (like > elpased time) to our standard performance tooling. > > This is less useful than it could be, because we currently can't tell when a > command is slow because we need to fix Mercurial versus when a command is > slow because the user isn't interacting quickly. > > Teach Mercurial to log the time it spends blocked, so that our tooling can > pick it up and submit it with the elapsed time - we can then do the math in > our tooling to see if Mercurial is slow, or if the user simply failed to > interact. Augie cut a 4.1-rc yesterday so that means we're in a freeze until 4.1 is out. Though, this series looks good :-) Please resend after Feb. ~1. ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 1 of 5] pager: stdout is line buffered by default
# HG changeset patch # User Simon Farnsworth# Date 1484835774 28800 # Thu Jan 19 06:22:54 2017 -0800 # Node ID 76123ae2e0ccaa58db3d4fc26b75b7251e13ad16 # Parent 036c37bd3ec189480647ff568cee9e0b43a5bc81 pager: stdout is line buffered by default pager only starts when ui.formatted() is true. In normal operation, when ui.formatted() is true, stdout is line buffered anyway. The code here doesn't actually do what the commit message in changeset 62c5e937 claims it will do; sys.stdout is not yet pager.stdin when we reopen, so we affect the original sys.stdout's buffering mode. It's the dup2 that puts pager.stdin's fd into sys.stdout's fd, and that doesn't affect buffering. diff --git a/hgext/pager.py b/hgext/pager.py --- a/hgext/pager.py +++ b/hgext/pager.py @@ -87,14 +87,10 @@ close_fds=util.closefds, stdin=subprocess.PIPE, stdout=util.stdout, stderr=util.stderr) -# back up original file objects and descriptors -olduifout = ui.fout -oldstdout = util.stdout +# back up original file descriptors stdoutfd = os.dup(util.stdout.fileno()) stderrfd = os.dup(util.stderr.fileno()) -# create new line-buffered stdout so that output can show up immediately -ui.fout = util.stdout = newstdout = os.fdopen(util.stdout.fileno(), 'wb', 1) os.dup2(pager.stdin.fileno(), util.stdout.fileno()) if ui._isatty(util.stderr): os.dup2(pager.stdin.fileno(), util.stderr.fileno()) @@ -103,15 +99,12 @@ def killpager(): if util.safehasattr(signal, "SIGINT"): signal.signal(signal.SIGINT, signal.SIG_IGN) -pager.stdin.close() -ui.fout = olduifout -util.stdout = oldstdout -# close new stdout while it's associated with pager; otherwise stdout -# fd would be closed when newstdout is deleted -newstdout.close() -# restore original fds: stdout is open again +# restore original fds os.dup2(stdoutfd, util.stdout.fileno()) os.dup2(stderrfd, util.stderr.fileno()) +# close pager's stdin so that it knows we're not going to send any more +# data +pager.stdin.close() pager.wait() def uisetup(ui): ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 5 of 5] crecord: log blocked time waiting for curses input
# HG changeset patch # User Simon Farnsworth# Date 1484850980 28800 # Thu Jan 19 10:36:20 2017 -0800 # Node ID 9684b31c29f3f17096ead595fef50b687d518b1c # Parent 93221dde2fad942c4f920dab1f346da71ac8033d crecord: log blocked time waiting for curses input We want to know when we're blocked on user input - log it. diff --git a/mercurial/crecord.py b/mercurial/crecord.py --- a/mercurial/crecord.py +++ b/mercurial/crecord.py @@ -514,6 +514,7 @@ self.ui = ui self.opts = {} +self.elapsedms = 0 self.errorstr = None # list of all chunks @@ -1374,29 +1375,40 @@ except curses.error: pass helpwin.refresh() +if self.ui.logblockedtime: +helpwin = util.elapsedtimewrapper(helpwin) try: helpwin.getkey() except curses.error: pass +finally: +if self.ui.logblockedtime: +self.elapsedms += helpwin.elapsedms def confirmationwindow(self, windowtext): "display an informational window, then wait for and return a keypress." confirmwin = curses.newwin(self.yscreensize, 0, 0, 0) +if self.ui.logblockedtime: +confirmwin = util.elapsedtimewrapper(confirmwin) try: -lines = windowtext.split("\n") -for line in lines: -self.printstring(confirmwin, line, pairname="selected") -except curses.error: -pass -self.stdscr.refresh() -confirmwin.refresh() -try: -response = chr(self.stdscr.getch()) -except ValueError: -response = None +try: +lines = windowtext.split("\n") +for line in lines: +self.printstring(confirmwin, line, pairname="selected") +except curses.error: +pass +self.stdscr.refresh() +confirmwin.refresh() +try: +response = chr(self.stdscr.getch()) +except ValueError: +response = None -return response +return response +finally: +if self.ui.logblockedtime: +self.elapsedms += confirmwin.elapsedms def reviewcommit(self): """ask for 'y' to be pressed to confirm selected. return True if @@ -1612,6 +1624,8 @@ origsigwinchhandler = signal.signal(signal.SIGWINCH, self.sigwinchhandler) self.stdscr = stdscr +if self.ui.logblockedtime: +self.stdscr = util.elapsedtimewrapper(self.stdscr) # error during initialization, cannot be printed in the curses # interface, it should be printed by the calling code self.initerr = None @@ -1630,6 +1644,8 @@ self.initcolorpair(curses.COLOR_WHITE, curses.COLOR_BLUE, name="legend") # newwin([height, width,] begin_y, begin_x) self.statuswin = curses.newwin(self.numstatuslines, 0, 0, 0) +if self.ui.logblockedtime: +self.statuswin = util.elapsedtimewrapper(self.statuswin) self.statuswin.keypad(1) # interpret arrow-key, etc. esc sequences # figure out how much space to allocate for the chunk-pad which is @@ -1650,15 +1666,23 @@ self.selecteditemendline = self.getnumlinesdisplayed( self.currentselecteditem, recursechildren=False) -while True: -self.updatescreen() -try: -keypressed = self.statuswin.getkey() -if self.errorstr is not None: -self.errorstr = None -continue -except curses.error: -keypressed = "foobar" -if self.handlekeypressed(keypressed): -break +try: +while True: +self.updatescreen() +try: +keypressed = self.statuswin.getkey() +if self.errorstr is not None: +self.errorstr = None +continue +except curses.error: +keypressed = "foobar" +if self.handlekeypressed(keypressed): +break +finally: +if self.ui.logblockedtime: +self.elapsedms += self.stdscr.elapsedms +self.elapsedms += self.statuswin.elapsedms +self.ui.log('uiblocked', +'crecord blocked for %0.1f ms', self.elapsed_ms, +crecord_blocked=self.elapsedms) signal.signal(signal.SIGWINCH, origsigwinchhandler) ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[Bug 5469] New: Add arguments to narrow the results of hg resolve --list
https://bz.mercurial-scm.org/show_bug.cgi?id=5469 Bug ID: 5469 Summary: Add arguments to narrow the results of hg resolve --list Product: Mercurial Version: stable branch Hardware: PC OS: Mac OS Status: UNCONFIRMED Severity: feature Priority: wish Component: Mercurial Assignee: bugzi...@mercurial-scm.org Reporter: joshg...@alum.mit.edu CC: mercurial-de...@selenic.com Currently the only way I know to get the list of resolved or unresolved files during a merge is to use grep and sed on the output of "hg resolve --list". Could we add arguments that list only the resolved or only the unresolved files? It might make sense for either option to imply --no-status. -- You are receiving this mail because: You are on the CC list for the bug. ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: [PATCH] ui: rename tmpdir parameter to more specific repopath
Yuya Nishiharawrites: > On Wed, 18 Jan 2017 18:38:54 -0800, Sean Farley wrote: >> # HG changeset patch >> # User Sean Farley >> # Date 1484792751 28800 >> # Wed Jan 18 18:25:51 2017 -0800 >> # Branch stable >> # Node ID 41d220e2bed95664c335f6a7ef70b8ce06dca86c >> # Parent 94af7d0c812fe7d3a5651191685ca43e1a331814 >> ui: rename tmpdir parameter to more specific repopath >> >> This was requested by Augie and I agree that repopath is more >> descriptive. >> >> diff --git a/hgext/histedit.py b/hgext/histedit.py >> index ae860d8..3e11fff 100644 >> --- a/hgext/histedit.py >> +++ b/hgext/histedit.py >> @@ -1333,11 +1333,11 @@ def ruleeditor(repo, ui, actions, editco >> >> rules = '\n'.join([act.torule() for act in actions]) >> rules += '\n\n' >> rules += editcomment >> rules = ui.edit(rules, ui.username(), {'prefix': 'histedit'}, >> -tmpdir=repo.path) >> +repopath=repo.path) > > repo.path could be set to ui at localrepository.__init__(), if we _always_ > want to switch the tempdir. Just an idea, which is obviously out of stable > scope. That's not a bad idea! I'll think about that after the freeze. ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
mercurial@30844: 2 new changesets (2 on stable)
2 new changesets (2 on stable) in mercurial: https://www.mercurial-scm.org/repo/hg/rev/2fb3ae89e4e1 changeset: 30843:2fb3ae89e4e1 branch: stable user:Augie Facklerdate:Wed Jan 18 23:34:35 2017 -0500 summary: contrib: fix check-commit to not reject commits from `hg sign` and `hg tag` https://www.mercurial-scm.org/repo/hg/rev/b3d2e8cce78c changeset: 30844:b3d2e8cce78c branch: stable bookmark:@ tag: tip user:Augie Fackler date:Wed Jan 18 23:43:41 2017 -0500 summary: tests: work around FreeBSD's unzip having slightly different output -- Repository URL: https://www.mercurial-scm.org/repo/hg ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 02 of 10 shelve-ext v2] shelve: rename stripnodes to nodestoprune
# HG changeset patch # User Kostia Balytskyi# Date 1484740179 28800 # Wed Jan 18 03:49:39 2017 -0800 # Node ID 155f97b77a4866075fa709daa3bef6dac9108e81 # Parent d904df83e9ead56f65104e10d765c0157d647401 shelve: rename stripnodes to nodestoprune Since we are introducing obs-based shelve, we are no longer stripping temporary nodes, we are obsoleting them. Therefore it looks like stipnodes would be a misleading name, while prune has a connotaion of "strip but with obsolescense", so nodestoprune seems like a good rename. diff --git a/hgext/shelve.py b/hgext/shelve.py --- a/hgext/shelve.py +++ b/hgext/shelve.py @@ -186,7 +186,7 @@ class shelvedstate(object): wctx = nodemod.bin(fp.readline().strip()) pendingctx = nodemod.bin(fp.readline().strip()) parents = [nodemod.bin(h) for h in fp.readline().split()] -stripnodes = [nodemod.bin(h) for h in fp.readline().split()] +nodestoprune = [nodemod.bin(h) for h in fp.readline().split()] branchtorestore = fp.readline().strip() keep = fp.readline().strip() == cls._keep except (ValueError, TypeError) as err: @@ -200,7 +200,7 @@ class shelvedstate(object): obj.wctx = repo[wctx] obj.pendingctx = repo[pendingctx] obj.parents = parents -obj.stripnodes = stripnodes +obj.nodestoprune = nodestoprune obj.branchtorestore = branchtorestore obj.keep = keep except error.RepoLookupError as err: @@ -209,7 +209,7 @@ class shelvedstate(object): return obj @classmethod -def save(cls, repo, name, originalwctx, pendingctx, stripnodes, +def save(cls, repo, name, originalwctx, pendingctx, nodestoprune, branchtorestore, keep=False): fp = repo.vfs(cls._filename, 'wb') fp.write('%i\n' % cls._version) @@ -219,7 +219,7 @@ class shelvedstate(object): fp.write('%s\n' % ' '.join([nodemod.hex(p) for p in repo.dirstate.parents()])) fp.write('%s\n' % - ' '.join([nodemod.hex(n) for n in stripnodes])) + ' '.join([nodemod.hex(n) for n in nodestoprune])) fp.write('%s\n' % branchtorestore) fp.write('%s\n' % (cls._keep if keep else cls._nokeep)) fp.close() @@ -570,7 +570,7 @@ def unshelveabort(ui, repo, state, opts) raise mergefiles(ui, repo, state.wctx, state.pendingctx) -repair.strip(ui, repo, state.stripnodes, backup=False, +repair.strip(ui, repo, state.nodestoprune, backup=False, topic='shelve') finally: shelvedstate.clear(repo) @@ -643,12 +643,12 @@ def unshelvecontinue(ui, repo, state, op shelvectx = state.pendingctx else: # only strip the shelvectx if the rebase produced it -state.stripnodes.append(shelvectx.node()) +state.nodestoprune.append(shelvectx.node()) mergefiles(ui, repo, state.wctx, shelvectx) restorebranch(ui, repo, state.branchtorestore) -repair.strip(ui, repo, state.stripnodes, backup=False, topic='shelve') +repair.strip(ui, repo, state.nodestoprune, backup=False, topic='shelve') shelvedstate.clear(repo) unshelvecleanup(ui, repo, state.name, opts) ui.status(_("unshelve of '%s' complete\n") % state.name) @@ -700,9 +700,9 @@ def _rebaserestoredcommit(ui, repo, opts except error.InterventionRequired: tr.close() -stripnodes = [repo.changelog.node(rev) - for rev in xrange(oldtiprev, len(repo))] -shelvedstate.save(repo, basename, pctx, tmpwctx, stripnodes, +nodestoprune = [repo.changelog.node(rev) +for rev in xrange(oldtiprev, len(repo))] +shelvedstate.save(repo, basename, pctx, tmpwctx, nodestoprune, branchtorestore, opts.get('keep')) util.rename(repo.join('rebasestate'), ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 03 of 10 shelve-ext v2] shelve: move node-pruning functionality to be member of shelvedstate
# HG changeset patch # User Kostia Balytskyi# Date 1484740179 28800 # Wed Jan 18 03:49:39 2017 -0800 # Node ID 249273f6db8bf0fdfbbf36bcbd9e3553e5715212 # Parent 155f97b77a4866075fa709daa3bef6dac9108e81 shelve: move node-pruning functionality to be member of shelvedstate Node-pruning can be node stripping or marker creation, depending on whether shelve is traditional or obs-based. Thus it makes sense to move it to a separate function. Also, since we already have shelvedstate object and this functionality operates on that object, it makes sense to make it a method. Having shelvedstate object contain repo and ui as members allows for calling 'state.prunenodes()' instead of 'state.prunenodes(repo, ui)' which is better IMO. diff --git a/hgext/shelve.py b/hgext/shelve.py --- a/hgext/shelve.py +++ b/hgext/shelve.py @@ -173,8 +173,12 @@ class shelvedstate(object): _keep = 'keep' _nokeep = 'nokeep' +def __init__(self, ui, repo): +self.ui = ui +self.repo = repo + @classmethod -def load(cls, repo): +def load(cls, ui, repo): fp = repo.vfs(cls._filename) try: version = int(fp.readline().strip()) @@ -195,7 +199,7 @@ class shelvedstate(object): fp.close() try: -obj = cls() +obj = cls(ui, repo) obj.name = name obj.wctx = repo[wctx] obj.pendingctx = repo[pendingctx] @@ -228,6 +232,11 @@ class shelvedstate(object): def clear(cls, repo): util.unlinkpath(repo.join(cls._filename), ignoremissing=True) +def prunenodes(self): +"""Cleanup temporary nodes from the repo""" +repair.strip(self.ui, self.repo, self.nodestoprune, backup=False, + topic='shelve') + def cleanupoldbackups(repo): vfs = scmutil.vfs(repo.join(backupdir)) maxbackups = repo.ui.configint('shelve', 'maxbackups', 10) @@ -570,8 +579,7 @@ def unshelveabort(ui, repo, state, opts) raise mergefiles(ui, repo, state.wctx, state.pendingctx) -repair.strip(ui, repo, state.nodestoprune, backup=False, - topic='shelve') +state.prunenodes() finally: shelvedstate.clear(repo) ui.warn(_("unshelve of '%s' aborted\n") % state.name) @@ -648,7 +656,7 @@ def unshelvecontinue(ui, repo, state, op mergefiles(ui, repo, state.wctx, shelvectx) restorebranch(ui, repo, state.branchtorestore) -repair.strip(ui, repo, state.nodestoprune, backup=False, topic='shelve') +state.prunenodes() shelvedstate.clear(repo) unshelvecleanup(ui, repo, state.name, opts) ui.status(_("unshelve of '%s' complete\n") % state.name) @@ -804,7 +812,7 @@ def _dounshelve(ui, repo, *shelved, **op ui.warn(_('tool option will be ignored\n')) try: -state = shelvedstate.load(repo) +state = shelvedstate.load(ui, repo) if opts.get('keep') is None: opts['keep'] = state.keep except IOError as err: ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 06 of 10 shelve-ext v2] shelve: add shelve type saving and loading
# HG changeset patch # User Kostia Balytskyi# Date 1484740179 28800 # Wed Jan 18 03:49:39 2017 -0800 # Node ID 149fc6d767ce3502528b43b0e209eb411dd6e842 # Parent 44425c4e86b2589184e23bed798999c15788b54b shelve: add shelve type saving and loading We need shelve type to be stored in .hg/shelvedstate in order to be able to run abort or continue action properly. If the shelve is obsbased, those actions should create markes, if it is traditional, the actions should strip commits. diff --git a/hgext/shelve.py b/hgext/shelve.py --- a/hgext/shelve.py +++ b/hgext/shelve.py @@ -185,6 +185,8 @@ class shelvedstate(object): _filename = 'shelvedstate' _keep = 'keep' _nokeep = 'nokeep' +_obsbased = 'obsbased' +_traditional = 'traditional' def __init__(self, ui, repo): self.ui = ui @@ -206,6 +208,7 @@ class shelvedstate(object): nodestoprune = [nodemod.bin(h) for h in fp.readline().split()] branchtorestore = fp.readline().strip() keep = fp.readline().strip() == cls._keep +obsshelve = fp.readline().strip() == cls._obsbased except (ValueError, TypeError) as err: raise error.CorruptedState(str(err)) finally: @@ -220,6 +223,7 @@ class shelvedstate(object): obj.nodestoprune = nodestoprune obj.branchtorestore = branchtorestore obj.keep = keep +obj.obsshelve = obsshelve except error.RepoLookupError as err: raise error.CorruptedState(str(err)) @@ -227,7 +231,7 @@ class shelvedstate(object): @classmethod def save(cls, repo, name, originalwctx, pendingctx, nodestoprune, - branchtorestore, keep=False): + branchtorestore, keep=False, obsshelve=False): fp = repo.vfs(cls._filename, 'wb') fp.write('%i\n' % cls._version) fp.write('%s\n' % name) @@ -239,6 +243,7 @@ class shelvedstate(object): ' '.join([nodemod.hex(n) for n in nodestoprune])) fp.write('%s\n' % branchtorestore) fp.write('%s\n' % (cls._keep if keep else cls._nokeep)) +fp.write('%s\n' % (cls._obsbased if obsshelve else cls._traditional)) fp.close() @classmethod ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 05 of 10 shelve-ext v2] shelve: add obs-based shelve functionality
# HG changeset patch # User Kostia Balytskyi# Date 1484740179 28800 # Wed Jan 18 03:49:39 2017 -0800 # Node ID 44425c4e86b2589184e23bed798999c15788b54b # Parent ceb709491816f84789b77a18bfcab15eaa9860fe shelve: add obs-based shelve functionality Obsolescense-based shelve works in a following way: 1. In order to shelve some changes, it creates a commit, records its node into a .oshelve file and prunes created commit. 2. In order to finish a shelve operation, transaction is just closed and not aborted. diff --git a/hgext/shelve.py b/hgext/shelve.py --- a/hgext/shelve.py +++ b/hgext/shelve.py @@ -373,10 +373,15 @@ def _nothingtoshelvemessaging(ui, repo, else: ui.status(_("nothing changed\n")) -def _shelvecreatedcommit(repo, node, name): -bases = list(mutableancestors(repo[node])) -shelvedfile(repo, name, 'hg').writebundle(bases, node) -cmdutil.export(repo, [node], +def _shelvecreatedcommit(ui, repo, node, name, tr): +if isobsshelve(repo, ui): +shelvedfile(repo, name, 'oshelve').writeobsshelveinfo({ +'node': nodemod.hex(node) +}) +else: +bases = list(mutableancestors(repo[node])) +shelvedfile(repo, name, 'hg').writebundle(bases, node) +cmdutil.export(repo.unfiltered(), [node], fp=shelvedfile(repo, name, patchextension).opener('wb'), opts=mdiff.diffopts(git=True)) @@ -387,8 +392,13 @@ def _includeunknownfiles(repo, pats, opt extra['shelve_unknown'] = '\0'.join(s.unknown) repo[None].add(s.unknown) -def _finishshelve(repo): -_aborttransaction(repo) +def _finishshelve(ui, repo, tr, node): +if isobsshelve(repo, ui): +obsolete.createmarkers(repo, [(repo.unfiltered()[node], ())]) +tr.close() +tr.release() +else: +_aborttransaction(repo) def _docreatecmd(ui, repo, pats, opts): wctx = repo[None] @@ -410,9 +420,12 @@ def _docreatecmd(ui, repo, pats, opts): try: lock = repo.lock() -# use an uncommitted transaction to generate the bundle to avoid -# pull races. ensure we don't print the abort message to stderr. -tr = repo.transaction('commit', report=lambda x: None) +# depending on whether shelve is traditional or +# obsolescense-based, we either abort or commit this +# transaction in the end. If we abort it, we don't +# want to print anything to stderr +report = None if isobsshelve(repo, ui) else (lambda x: None) +tr = repo.transaction('commit', report=report) interactive = opts.get('interactive', False) includeunknown = (opts.get('unknown', False) and @@ -438,16 +451,19 @@ def _docreatecmd(ui, repo, pats, opts): _nothingtoshelvemessaging(ui, repo, pats, opts) return 1 -_shelvecreatedcommit(repo, node, name) +_shelvecreatedcommit(ui, repo, node, name, tr) if ui.formatted(): desc = util.ellipsis(desc, ui.termwidth()) ui.status(_('shelved as %s\n') % name) -hg.update(repo, parent.node()) +# current wc parent may be already obsolete becuase +# it might have been created previously and shelve just +# reuses it +hg.update(repo.unfiltered(), parent.node()) if origbranch != repo['.'].branch() and not _isbareshelve(pats, opts): repo.dirstate.setbranch(origbranch) -_finishshelve(repo) +_finishshelve(ui, repo, tr, node) finally: lockmod.release(tr, lock) ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 09 of 10 shelve-ext v2] shelve: add logic to preserve active bookmarks
# HG changeset patch # User Kostia Balytskyi# Date 1484740179 28800 # Wed Jan 18 03:49:39 2017 -0800 # Node ID 088c9191d662d5c0003310119c51540926a815f7 # Parent 94a237a046059ef246aacb2c3ad809c9f0bdbe70 shelve: add logic to preserve active bookmarks This adds an explicit active-bookmark-handling logic to *both* traditional and obs-based shelve. Although it is possible to only add it to obs-based, I think it would be ugly and I see no harm in explicitly handling bookmarks in addition to reliance on trasnactions. diff --git a/hgext/shelve.py b/hgext/shelve.py --- a/hgext/shelve.py +++ b/hgext/shelve.py @@ -29,6 +29,7 @@ import time from mercurial.i18n import _ from mercurial import ( +bookmarks, bundle2, bundlerepo, changegroup, @@ -188,6 +189,8 @@ class shelvedstate(object): _nokeep = 'nokeep' _obsbased = 'obsbased' _traditional = 'traditional' +# colon is essential to differentiate from a real bookmark name +_noactivebook = ':no-active-bookmark' def __init__(self, ui, repo): self.ui = ui @@ -210,6 +213,7 @@ class shelvedstate(object): branchtorestore = fp.readline().strip() keep = fp.readline().strip() == cls._keep obsshelve = fp.readline().strip() == cls._obsbased +activebook = fp.readline().strip() except (ValueError, TypeError) as err: raise error.CorruptedState(str(err)) finally: @@ -225,6 +229,9 @@ class shelvedstate(object): obj.branchtorestore = branchtorestore obj.keep = keep obj.obsshelve = obsshelve +obj.activebookmark = '' +if activebook != cls._noactivebook: +obj.activebookmark = activebook except error.RepoLookupError as err: raise error.CorruptedState(str(err)) @@ -232,7 +239,7 @@ class shelvedstate(object): @classmethod def save(cls, repo, name, originalwctx, pendingctx, nodestoprune, - branchtorestore, keep=False, obsshelve=False): + branchtorestore, keep=False, obsshelve=False, activebook=''): fp = repo.vfs(cls._filename, 'wb') fp.write('%i\n' % cls._version) fp.write('%s\n' % name) @@ -245,6 +252,7 @@ class shelvedstate(object): fp.write('%s\n' % branchtorestore) fp.write('%s\n' % (cls._keep if keep else cls._nokeep)) fp.write('%s\n' % (cls._obsbased if obsshelve else cls._traditional)) +fp.write('%s\n' % (activebook or cls._noactivebook)) fp.close() @classmethod @@ -283,6 +291,16 @@ def cleanupoldbackups(repo): if err.errno != errno.ENOENT: raise +def _backupactivebookmark(repo): +activebookmark = repo._activebookmark +if activebookmark: +bookmarks.deactivate(repo) +return activebookmark + +def _restoreactivebookmark(repo, mark): +if mark: +bookmarks.activate(repo, mark) + def _aborttransaction(repo): '''Abort current transaction for shelve/unshelve, but keep dirstate ''' @@ -402,7 +420,9 @@ def _includeunknownfiles(repo, pats, opt extra['shelve_unknown'] = '\0'.join(s.unknown) repo[None].add(s.unknown) -def _finishshelve(ui, repo, tr, node): +def _finishshelve(ui, repo, tr, node, activebookmark): +if activebookmark: +bookmarks.activate(repo, activebookmark) if isobsshelve(repo, ui): obsolete.createmarkers(repo, [(repo.unfiltered()[node], ())]) tr.close() @@ -426,7 +446,7 @@ def _docreatecmd(ui, repo, pats, opts): if not opts.get('message'): opts['message'] = desc -lock = tr = None +lock = tr = activebookmark = None try: lock = repo.lock() @@ -442,6 +462,7 @@ def _docreatecmd(ui, repo, pats, opts): not opts.get('addremove', False)) name = getshelvename(repo, parent, opts) +activebookmark = _backupactivebookmark(repo) extra = {} if includeunknown: _includeunknownfiles(repo, pats, opts, extra) @@ -456,7 +477,8 @@ def _docreatecmd(ui, repo, pats, opts): node = cmdutil.commit(ui, repo, commitfunc, pats, opts) else: node = cmdutil.dorecord(ui, repo, commitfunc, None, -False, cmdutil.recordfilter, *pats, **opts) +False, cmdutil.recordfilter, *pats, +**opts) if not node: _nothingtoshelvemessaging(ui, repo, pats, opts) return 1 @@ -473,8 +495,9 @@ def _docreatecmd(ui, repo, pats, opts): if origbranch != repo['.'].branch() and not _isbareshelve(pats, opts): repo.dirstate.setbranch(origbranch) -_finishshelve(ui, repo, tr, node) +_finishshelve(ui, repo, tr, node, activebookmark) finally: +_restoreactivebookmark(repo, activebookmark)
[PATCH 08 of 10 shelve-ext v2] shelve: add obs-based unshelve functionality
# HG changeset patch # User Kostia Balytskyi# Date 1484835394 28800 # Thu Jan 19 06:16:34 2017 -0800 # Node ID 94a237a046059ef246aacb2c3ad809c9f0bdbe70 # Parent abdf9565fdce15604ea4abf013cb7c98a11f70ca shelve: add obs-based unshelve functionality Obsolescense-based unshelve works as follows: 1. Instead of stripping temporary nodes, markers are created to obsolete them. 2. Restoring commit is just finding it in an unfiltered repo. 3. '--keep' is only passed to rebase on traditional unshelves (and thus traditional rebases), becuase we want markers to be created fro obsolete-based rebases. 4. 'hg unshelve' uses unfiltered repo to perform rebases because we want rebase to be able to create markers between original and new commits. 'rebaseskipobsolete' is disabled to make rebase not skip the commit altogether. I have a test for the case when shelve node has been stripped before unshelve call, that test is together with ~30 commits I was talking about in patch 1. diff --git a/hgext/shelve.py b/hgext/shelve.py --- a/hgext/shelve.py +++ b/hgext/shelve.py @@ -25,6 +25,7 @@ from __future__ import absolute_import import collections import errno import itertools +import time from mercurial.i18n import _ from mercurial import ( @@ -252,8 +253,13 @@ class shelvedstate(object): def prunenodes(self): """Cleanup temporary nodes from the repo""" -repair.strip(self.ui, self.repo, self.nodestoprune, backup=False, - topic='shelve') +if self.obsshelve: +unfi = self.repo.unfiltered() +relations = [(unfi[n], ()) for n in self.nodestoprune] +obsolete.createmarkers(self.repo, relations) +else: +repair.strip(self.ui, self.repo, self.nodestoprune, backup=False, + topic='shelve') def cleanupoldbackups(repo): vfs = scmutil.vfs(repo.join(backupdir)) @@ -666,9 +672,14 @@ def unshelvecontinue(ui, repo, state, op util.rename(repo.join('unshelverebasestate'), repo.join('rebasestate')) try: -rebase.rebase(ui, repo, **{ -'continue' : True -}) +# if shelve is obs-based, we want rebase to be able +# to create markers to already-obsoleted commits +_repo = repo.unfiltered() if state.obsshelve else repo +with ui.configoverride({('experimental', 'rebaseskipobsolete'): +'off'}, 'unshelve'): +rebase.rebase(ui, _repo, **{ +'continue' : True, +}) except Exception: util.rename(repo.join('rebasestate'), repo.join('unshelverebasestate')) @@ -708,30 +719,58 @@ def _commitworkingcopychanges(ui, repo, with ui.configoverride({('ui', 'quiet'): True}): node = cmdutil.commit(ui, repo, commitfunc, [], tempopts) tmpwctx = repo[node] +ui.debug("temporary working copy commit: %s:%s\n" % + (tmpwctx.rev(), nodemod.short(node))) return tmpwctx, addedbefore -def _unshelverestorecommit(ui, repo, basename): +def _unshelverestorecommit(ui, repo, basename, obsshelve): """Recreate commit in the repository during the unshelve""" with ui.configoverride({('ui', 'quiet'): True}): -shelvedfile(repo, basename, 'hg').applybundle() -shelvectx = repo['tip'] +if obsshelve: +md = shelvedfile(repo, basename, 'oshelve').readobsshelveinfo() +shelvenode = nodemod.bin(md['node']) +repo = repo.unfiltered() +try: +shelvectx = repo[shelvenode] +except error.RepoLookupError: +m = _("shelved node %s not found in repo") +raise error.Abort(m % md['node']) +else: +shelvedfile(repo, basename, 'hg').applybundle() +shelvectx = repo['tip'] return repo, shelvectx def _rebaserestoredcommit(ui, repo, opts, tr, oldtiprev, basename, pctx, - tmpwctx, shelvectx, branchtorestore): + tmpwctx, shelvectx, branchtorestore, obsshelve): """Rebase restored commit from its original location to a destination""" # If the shelve is not immediately on top of the commit # we'll be merging with, rebase it to be on top. if tmpwctx.node() == shelvectx.parents()[0].node(): +# shelvectx is immediately on top of the tmpwctx return shelvectx +# we need a new commit extra every time we perform a rebase to ensure +# that "nothing to rebase" does not happen with obs-based shelve +# "nothing to rebase" means that tip does not point to a "successor" +# commit after a rebase and we have no way to learn which commit +# should be a "shelvectx". this is a dirty hack until we implement +# some way to learn the results of rebase operation, other than +# text output and
[PATCH 10 of 10 shelve-ext v2] shelve: disable inhibit for shelving period
# HG changeset patch # User Kostia Balytskyi# Date 1484838335 28800 # Thu Jan 19 07:05:35 2017 -0800 # Node ID 6a7e7659886d35dfdc4d7d5efb3ef8479ae35367 # Parent 088c9191d662d5c0003310119c51540926a815f7 shelve: disable inhibit for shelving period While shelving, we're creating a new commit and updating to it. If inhibit is enabled, it will try to add this commit to an inhibition set, which is not necessary (we're creating a marker right after we perform this operation). Thus, disabling inhibit speeds things up a bit. diff --git a/hgext/shelve.py b/hgext/shelve.py --- a/hgext/shelve.py +++ b/hgext/shelve.py @@ -447,8 +447,12 @@ def _docreatecmd(ui, repo, pats, opts): opts['message'] = desc lock = tr = activebookmark = None +_obsinhibit = _notset = object() try: lock = repo.lock() +if util.safehasattr(repo, '_obsinhibit'): +_obsinhibit = getattr(repo, '_obsinhibit') +del repo._obsinhibit # depending on whether shelve is traditional or # obsolescense-based, we either abort or commit this @@ -498,6 +502,8 @@ def _docreatecmd(ui, repo, pats, opts): _finishshelve(ui, repo, tr, node, activebookmark) finally: _restoreactivebookmark(repo, activebookmark) +if _obsinhibit is not _notset: +repo._obsinhibit = _obsinhibit lockmod.release(tr, lock) def _isbareshelve(pats, opts): ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 07 of 10 shelve-ext v2] shelve: migrate config overrides to ui.configoverride
# HG changeset patch # User Kostia Balytskyi# Date 1484740179 28800 # Wed Jan 18 03:49:39 2017 -0800 # Node ID abdf9565fdce15604ea4abf013cb7c98a11f70ca # Parent 149fc6d767ce3502528b43b0e209eb411dd6e842 shelve: migrate config overrides to ui.configoverride This patch also makes ui.quiet manipulations much more explicit and readable, addressing previous Yuya's concerns. diff --git a/hgext/shelve.py b/hgext/shelve.py --- a/hgext/shelve.py +++ b/hgext/shelve.py @@ -349,17 +349,16 @@ def getcommitfunc(extra, interactive, ed hasmq = util.safehasattr(repo, 'mq') if hasmq: saved, repo.mq.checkapplied = repo.mq.checkapplied, False -backup = repo.ui.backupconfig('phases', 'new-commit') try: -repo.ui.setconfig('phases', 'new-commit', phases.secret) -editor_ = False -if editor: -editor_ = cmdutil.getcommiteditor(editform='shelve.shelve', - **opts) -return repo.commit(message, shelveuser, opts.get('date'), match, - editor=editor_, extra=extra) +overrides = {('phases', 'new-commit'): phases.secret} +with repo.ui.configoverride(overrides): +editor_ = False +if editor: +editor_ = cmdutil.getcommiteditor(editform='shelve.shelve', + **opts) +return repo.commit(message, shelveuser, opts.get('date'), + match, editor=editor_, extra=extra) finally: -repo.ui.restoreconfig(backup) if hasmq: repo.mq.checkapplied = saved @@ -621,9 +620,7 @@ def unshelveabort(ui, repo, state, opts) def mergefiles(ui, repo, wctx, shelvectx): """updates to wctx and merges the changes from shelvectx into the dirstate.""" -oldquiet = ui.quiet -try: -ui.quiet = True +with ui.configoverride({('ui', 'quiet'): True}): hg.update(repo, wctx.node()) files = [] files.extend(shelvectx.files()) @@ -638,8 +635,6 @@ def mergefiles(ui, repo, wctx, shelvectx *pathtofiles(repo, files), **{'no_backup': True}) ui.popbuffer() -finally: -ui.quiet = oldquiet def restorebranch(ui, repo, branchtorestore): if branchtorestore and branchtorestore != repo.dirstate.branch(): @@ -710,17 +705,16 @@ def _commitworkingcopychanges(ui, repo, tempopts = {} tempopts['message'] = "pending changes temporary commit" tempopts['date'] = opts.get('date') -ui.quiet = True -node = cmdutil.commit(ui, repo, commitfunc, [], tempopts) +with ui.configoverride({('ui', 'quiet'): True}): +node = cmdutil.commit(ui, repo, commitfunc, [], tempopts) tmpwctx = repo[node] return tmpwctx, addedbefore -def _unshelverestorecommit(ui, repo, basename, oldquiet): +def _unshelverestorecommit(ui, repo, basename): """Recreate commit in the repository during the unshelve""" -ui.quiet = True -shelvedfile(repo, basename, 'hg').applybundle() -shelvectx = repo['tip'] -ui.quiet = oldquiet +with ui.configoverride({('ui', 'quiet'): True}): +shelvedfile(repo, basename, 'hg').applybundle() +shelvectx = repo['tip'] return repo, shelvectx def _rebaserestoredcommit(ui, repo, opts, tr, oldtiprev, basename, pctx, @@ -886,13 +880,9 @@ def _dounshelve(ui, repo, *shelved, **op if not shelvedfile(repo, basename, patchextension).exists(): raise error.Abort(_("shelved change '%s' not found") % basename) -oldquiet = ui.quiet lock = tr = None -forcemerge = ui.backupconfig('ui', 'forcemerge') try: -ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'unshelve') lock = repo.lock() - tr = repo.transaction('unshelve', report=lambda x: None) oldtiprev = len(repo) @@ -907,16 +897,18 @@ def _dounshelve(ui, repo, *shelved, **op tmpwctx, addedbefore = _commitworkingcopychanges(ui, repo, opts, tmpwctx) -repo, shelvectx = _unshelverestorecommit(ui, repo, basename, oldquiet) +repo, shelvectx = _unshelverestorecommit(ui, repo, basename) branchtorestore = '' if shelvectx.branch() != shelvectx.p1().branch(): branchtorestore = shelvectx.branch() -shelvectx = _rebaserestoredcommit(ui, repo, opts, tr, oldtiprev, - basename, pctx, tmpwctx, shelvectx, - branchtorestore) -mergefiles(ui, repo, pctx, shelvectx) +with ui.configoverride({('ui', 'forcemerge'): opts.get('tool', '')}, + 'unshelve'): +shelvectx = _rebaserestoredcommit(ui, repo, opts, tr, oldtiprev,
[PATCH 04 of 10 shelve-ext v2] shelve: add a function to check whether obs-based shelve is enabled
# HG changeset patch # User Kostia Balytskyi# Date 1484740179 28800 # Wed Jan 18 03:49:39 2017 -0800 # Node ID ceb709491816f84789b77a18bfcab15eaa9860fe # Parent 249273f6db8bf0fdfbbf36bcbd9e3553e5715212 shelve: add a function to check whether obs-based shelve is enabled A central place to check whether code should use traditional or obsolescense-based shelve behavior. diff --git a/hgext/shelve.py b/hgext/shelve.py --- a/hgext/shelve.py +++ b/hgext/shelve.py @@ -40,6 +40,7 @@ from mercurial import ( mdiff, merge, node as nodemod, +obsolete, patch, phases, repair, @@ -70,6 +71,18 @@ patchextension = 'patch' # generic user for all shelve operations shelveuser = 'shelve@localhost' +def isobsshelve(repo, ui): +"""Check whether obsolescense-based shelve is enabled""" +obsshelve = ui.configbool('experimental', 'obsshelve') +if not obsshelve: +return False +if not obsolete.isenabled(repo, obsolete.createmarkersopt): +w = _('ignoring experimental.obsshelve because createmarkers option ' + 'is disabled') +ui.warn(w) +return False +return True + class obsshelvefile(scmutil.simplekeyvaluefile): KEYS = [('node', True)] ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 01 of 10 shelve-ext v2] shelve: add an ability to write key-val data to a new type of shelve files
# HG changeset patch # User Kostia Balytskyi# Date 1484835841 28800 # Thu Jan 19 06:24:01 2017 -0800 # Node ID d904df83e9ead56f65104e10d765c0157d647401 # Parent 19a449c91ef14e691cf1347748473e0094fedc86 shelve: add an ability to write key-val data to a new type of shelve files Obsolescense-based shelve only needs metadata stored in .hg/shelved and if feels that this metadata should be stored in a simplekeyvaluefile format for potential extensibility purposes. I want to avoid storing it in an unstructured text file where order of lines determines their semantical meanings (as now happens in .hg/shelvedstate. .hg/rebasestate and I suspect other state files as well). Not included in this series, I have ~30 commits, doubling test-shelve.t in size and testing almost every tested shelve usecase for obs-shelve. Here's the series for the curious now: http://pastebin.com/tGJKx0vM I would like to send it to the mailing list and get accepted as well, but: 1. it's big, so should I send like 6 patches a time or so? 2. instead of having a commit per test case, it more like a commit per some amount of copy-pasted code. I tried to keep it meaningful and named commits somewhat properly, but it is far from this list standards IMO. Any advice on how to get it in without turning it into a 100 commits and spending many days writing descriptions? 3. it makes test-shelve.t run for twice as long (and it is already a slow test). Newest test-shelve.r runs for ~1 minute. diff --git a/hgext/shelve.py b/hgext/shelve.py --- a/hgext/shelve.py +++ b/hgext/shelve.py @@ -62,7 +62,7 @@ testedwith = 'ships-with-hg-core' backupdir = 'shelve-backup' shelvedir = 'shelved' -shelvefileextensions = ['hg', 'patch'] +shelvefileextensions = ['hg', 'patch', 'oshelve'] # universal extension is present in all types of shelves patchextension = 'patch' @@ -70,6 +70,9 @@ patchextension = 'patch' # generic user for all shelve operations shelveuser = 'shelve@localhost' +class obsshelvefile(scmutil.simplekeyvaluefile): +KEYS = [('node', True)] + class shelvedfile(object): """Helper for the file storing a single shelve @@ -153,6 +156,12 @@ class shelvedfile(object): bundle2.writebundle(self.ui, cg, self.fname, btype, self.vfs, compression=compression) +def writeobsshelveinfo(self, info): +obsshelvefile(self.vfs, self.fname).write(info) + +def readobsshelveinfo(self): +return obsshelvefile(self.vfs, self.fname).read() + class shelvedstate(object): """Handle persistence during unshelving operations. ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: [PATCH 2 of 2 V2] templater: add '{envvars}' to access environment variables
On Wed, 18 Jan 2017 21:39:47 -0500, Matt Harbison wrote: > On Wed, 18 Jan 2017 08:44:55 -0500, Yuya Nishiharawrote: > > > On Tue, 17 Jan 2017 23:50:47 -0500, Matt Harbison wrote: > >> # HG changeset patch > >> # User Matt Harbison > >> # Date 1484712774 18000 > >> # Tue Jan 17 23:12:54 2017 -0500 > >> # Node ID fce42f9dba7bab71463c0bcec44e6d87fdf275b2 > >> # Parent 5a03e25ec0c0417e915b2014995bd83443ef97ec > >> templater: add '{envvars}' to access environment variables > >> > >> Since the option for ui.exportableenviron is experimental, so is this > >> template > >> until the underlying API is sorted out. > >> > >> diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py > >> --- a/mercurial/cmdutil.py > >> +++ b/mercurial/cmdutil.py > >> @@ -1448,7 +1448,8 @@ > >> 'parent': '{rev}:{node|formatnode} ', > >> 'manifest': '{rev}:{node|formatnode}', > >> 'file_copy': '{name} ({source})', > >> -'extra': '{key}={value|stringescape}' > >> +'extra': '{key}={value|stringescape}', > >> +'envvar': '{key}={value|stringescape}' > > > > Dropped '|stringescape' since {envvar} should be readable text in > > general. > > The only reason I thought it was necessary is because MSYS is apparently > putting a couple '\n' in $PS1. > > $ ../hg log -r . -T "{get(envvars, 'PS1')}" --config > "experimental.exportableenviron=*" > \[\033]0;$MSYSTEM:\w\007 > \033[32m\]\u@\h \[\033[33m\w\033[0m\] > > vs > > $ echo $PS1 > \[\033]0;$MSYSTEM:\w\007 \033[32m\]\u@\h \[\033[33m\w\033[0m\] $ > > Maybe that's too much of a corner case to care. Dropping the escaping > also prevents the Windows paths from showing as 'C:\\Windows\\..', so > maybe that's a good thing. Ah, thanks for the clarification. I think the Windows paths is more important, so let's go without stringescape. ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: [PATCH 1 of 2 V2] ui: introduce an experimental dict of exportable environment variables
On Wed, 18 Jan 2017 21:32:34 -0500, Matt Harbison wrote: > On Wed, 18 Jan 2017 08:52:43 -0500, Yuya Nishiharawrote: > > On Tue, 17 Jan 2017 23:50:46 -0500, Matt Harbison wrote: > >> # HG changeset patch > >> # User Matt Harbison > >> # Date 1484712312 18000 > >> # Tue Jan 17 23:05:12 2017 -0500 > >> # Node ID 5a03e25ec0c0417e915b2014995bd83443ef97ec > >> # Parent 923336cf8b8afdb41746ecef8a39d773bd5538bf > >> ui: introduce an experimental dict of exportable environment variables > > > > This looks good as an experimental implementation, so queued, thanks. > > I found a few minor problems, which can be fixed later. > > > >> Care needs to be taken to prevent leaking potentially sensitive > >> environment > >> variables through hgweb, if template support for environment variables > >> is to be > >> introduced. There are a few ideas about the API for preventing > >> accidental > >> leaking [1]. Option 3 seems best from the POV of not needing to > >> configure > >> anything in the normal case. I couldn't figure out how to do that, so > >> guard it > >> with an experimental option for now. > >> > >> [1] > >> https://www.mercurial-scm.org/pipermail/mercurial-devel/2017-January/092383.html > > > > In addition to hgweb, we'll probably need to consider the case where hg > > command is executed behind a third-party web application. A web frontend > > may > > pass a revset provided by user for example, which seems a valid use case. > > OK. But this is well beyond anything I've done or understand, so I'll > need pointers if there's anything specific to this case. Sorry for vague comment. I was thinking about which command options should be considered safe for malicious input. For instance, a) a web application may accept ?rev=%REV% and execute "hg log -r'%REV%'", which seems fine as long as '%REV%' is shell-escaped appropriately. b) but if we have 'search_in_template(pattern, template)' revset and '{envvars}' template, (a) no longer be true. So I saw (b) had the same story for 'shell(command)' revset/fileset proposed before. We shouldn't allow shell injection in revset/fileset. Instead, we could define an external revset/fileset command in config file. Then, this question came up: c) how about -T? is a template fragment considered unsafe? > > And we'll need to build the dict by fixconfig(), not by __init__(). > > Is there a way to know if ui is being used by hgweb or a web app? I'd > prefer this --config setting go away, at least in the default case. Maybe > it's useful if someone wants to opt in some vars for hgweb, for some > reason. If hgweb is only the case, maybe we can overwrite ui._exportableenviron by hgweb. ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH STABLE] pager: wrap _runcommand() no matter if stdout is redirected
# HG changeset patch # User Yuya Nishihara# Date 1484834492 -32400 # Thu Jan 19 23:01:32 2017 +0900 # Branch stable # Node ID f3ca8b7e0e2df7507661adf5957c51e39bc6b5b1 # Parent 262c2be8ea5a4f36fb7348cb5a940cf78f2dffa9 pager: wrap _runcommand() no matter if stdout is redirected We've made chg utilize the common code path implemented in pager.py (by 815e1cefd082 and 493935e0327a), but the chg server does not always start with a tty. Because of this, uisetup() of the pager extension could be skipped on the chg server. Kudos given to Sean Farley for dogfooding new chg and spotting this problem. diff --git a/hgext/pager.py b/hgext/pager.py --- a/hgext/pager.py +++ b/hgext/pager.py @@ -115,9 +115,6 @@ def _runpager(ui, p): pager.wait() def uisetup(ui): -if '--debugger' in sys.argv or not ui.formatted(): -return - class pagerui(ui.__class__): def _runpager(self, pagercmd): _runpager(self, pagercmd) @@ -130,7 +127,7 @@ def uisetup(ui): always = util.parsebool(options['pager']) auto = options['pager'] == 'auto' -if not p: +if not p or '--debugger' in sys.argv or not ui.formatted(): pass elif always: usepager = True diff --git a/tests/test-chg.t b/tests/test-chg.t --- a/tests/test-chg.t +++ b/tests/test-chg.t @@ -32,6 +32,38 @@ long socket path $ cd .. +pager +- + + $ cat >> fakepager.py < import sys + > for line in sys.stdin: + > sys.stdout.write('paged! %r\n' % line) + > EOF + +enable pager extension globally, but spawns the master server with no tty: + + $ chg init pager + $ cd pager + $ cat >> $HGRCPATH < [extensions] + > pager = + > [pager] + > pager = python $TESTTMP/fakepager.py + > EOF + $ chg version > /dev/null + $ touch foo + $ chg ci -qAm foo + +pager should be enabled if the attached client has a tty: + + $ chg log -l1 -q --config ui.formatted=True + paged! '0:1f7b0de80e11\n' + $ chg log -l1 -q --config ui.formatted=False + 0:1f7b0de80e11 + + $ cd .. + server lifecycle diff --git a/tests/test-pager.t b/tests/test-pager.t --- a/tests/test-pager.t +++ b/tests/test-pager.t @@ -152,6 +152,11 @@ doesn't result in history being paged. summary: modify a 9 +Pager should not start if stdout is not a tty. + + $ hg log -l1 -q --config ui.formatted=False + 10:46106edeeb38 + Pager with color enabled allows colors to come through by default, even though stdout is no longer a tty. $ cat >> $HGRCPATH
Re: [PATCH] ui: rename tmpdir parameter to more specific repopath
On Wed, 18 Jan 2017 18:38:54 -0800, Sean Farley wrote: > # HG changeset patch > # User Sean Farley> # Date 1484792751 28800 > # Wed Jan 18 18:25:51 2017 -0800 > # Branch stable > # Node ID 41d220e2bed95664c335f6a7ef70b8ce06dca86c > # Parent 94af7d0c812fe7d3a5651191685ca43e1a331814 > ui: rename tmpdir parameter to more specific repopath > > This was requested by Augie and I agree that repopath is more > descriptive. > > diff --git a/hgext/histedit.py b/hgext/histedit.py > index ae860d8..3e11fff 100644 > --- a/hgext/histedit.py > +++ b/hgext/histedit.py > @@ -1333,11 +1333,11 @@ def ruleeditor(repo, ui, actions, editco > > rules = '\n'.join([act.torule() for act in actions]) > rules += '\n\n' > rules += editcomment > rules = ui.edit(rules, ui.username(), {'prefix': 'histedit'}, > -tmpdir=repo.path) > +repopath=repo.path) repo.path could be set to ui at localrepository.__init__(), if we _always_ want to switch the tempdir. Just an idea, which is obviously out of stable scope. ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: [PATCH STABLE] statprof: require input file
On Wed, 18 Jan 2017 23:11:44 -0800, Gregory Szorc wrote: > # HG changeset patch > # User Gregory Szorc> # Date 1484808307 28800 > # Wed Jan 18 22:45:07 2017 -0800 > # Branch stable > # Node ID c148ef85245edba14eff9219e0404fc832c8305c > # Parent 94af7d0c812fe7d3a5651191685ca43e1a331814 > statprof: require input file Queued this. Thanks for taking care of it. ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH v9 RFC] scmutil: add a simple key-value file helper
# HG changeset patch # User Kostia Balytskyi# Date 1484824655 28800 # Thu Jan 19 03:17:35 2017 -0800 # Node ID 19a449c91ef14e691cf1347748473e0094fedc86 # Parent 9f264adbe75bfae8551dc0e6e0fce8d43fc7b43a scmutil: add a simple key-value file helper The purpose of the added class is to serve purposes like save files of shelve or state files of shelve, rebase and histedit. Keys of these files can be alphanumeric and start with letters, while values must not contain newlines. Keys which start with an uppercase letter are required, while other keys are optional. In light of Mercurial's reluctancy to use Python's json module, this tries to provide a reasonable alternative for a non-nested named data. Comparing to current approach of storing state in plain text files, where semantic meaning of lines of text is only determined by their oreder, simple key-value file allows for reordering lines and thus helps handle optional values. Initial use-case I see for this is obs-shelve's shelve files. Later we can possibly migrate state files to this approach. The test is in a new file beause I did not figure out where to put it within existing test suite. If you give me a better idea, I will gladly follow it. diff --git a/mercurial/error.py b/mercurial/error.py --- a/mercurial/error.py +++ b/mercurial/error.py @@ -246,3 +246,6 @@ class UnsupportedBundleSpecification(Exc class CorruptedState(Exception): """error raised when a command is not able to read its state from file""" + +class MissingRequiredKey(Exception): +"""error raised when simple key-value file misses a required key""" diff --git a/mercurial/scmutil.py b/mercurial/scmutil.py --- a/mercurial/scmutil.py +++ b/mercurial/scmutil.py @@ -1571,3 +1571,51 @@ class checkambigatclosing(closewrapbase) def close(self): self._origfh.close() self._checkambig() + +class simplekeyvaluefile(object): +"""A simple file with key=value lines + +Keys must be alphanumerics and start with a letter, values must not +contain '\n' characters""" + +# if KEYS is non-empty, read values are validated against it: +# each key is a tuple (keyname, required) +KEYS = [] + +def __init__(self, vfs, path): +self.vfs = vfs +self.path = path + +def validate(self, d): +for key, req in self.KEYS: +if req and key not in d: +e = "missing a required key: '%s'" % key +raise error.MissingRequiredKey(e) + +def read(self): +lines = self.vfs.readlines(self.path) +try: +d = dict(line[:-1].split('=', 1) for line in lines if line) +except ValueError as e: +raise error.CorruptedState(str(e)) +self.validate(d) +return d + +def write(self, data): +"""Write key=>value mapping to a file +data is a dict. Keys must be alphanumerical and start with a letter. +Values must not contain newline characters.""" +lines = [] +for k, v in data.items(): +if not k[0].isalpha(): +e = "keys must start with a letter in a key-value file" +raise error.ProgrammingError(e) +if not k.isalnum(): +e = "invalid key name in a simple key-value file" +raise error.ProgrammingError(e) +if '\n' in v: +e = "invalid value in a simple key-value file" +raise error.ProgrammingError(e) +lines.append("%s=%s\n" % (k, v)) +with self.vfs(self.path, mode='wb', atomictemp=True) as fp: +fp.write(''.join(lines)) diff --git a/tests/test-simplekeyvaluefile.py b/tests/test-simplekeyvaluefile.py new file mode 100644 --- /dev/null +++ b/tests/test-simplekeyvaluefile.py @@ -0,0 +1,87 @@ +from __future__ import absolute_import + +import unittest +import silenttestrunner + +from mercurial import ( +error, +scmutil, +) + +contents = {} + +class fileobj(object): +def __init__(self, name): +self.name = name + +def __enter__(self): +return self + +def __exit__(self, *args, **kwargs): +pass + +def write(self, text): +contents[self.name] = text + +def read(self): +return contents[self.name] + +class mockvfs(object): +def read(self, path): +return fileobj(path).read() + +def readlines(self, path): +return fileobj(path).read().split('\n') + +def __call__(self, path, mode, atomictemp): +return fileobj(path) + +class testsimplekeyvaluefile(unittest.TestCase): +def setUp(self): +self.vfs = mockvfs() + +def testbasicwriting(self): +d = {'key1': 'value1', 'Key2': 'value2'} +scmutil.simplekeyvaluefile(self.vfs, 'kvfile').write(d) +self.assertEqual(sorted(self.vfs.read('kvfile').split('\n')), + ['', 'Key2=value2', 'key1=value1']) + +def testinvalidkeys(self): +d = {'0key1':