Re: [PATCH 07 of 10 shelve-ext v4] shelve: add shelve type saving and loading
On 21/03/2017 21:56, Augie Fackler wrote: > On Sat, Mar 11, 2017 at 01:00:26PM -0800, Kostia Balytskyi wrote: >> # HG changeset patch >> # User Kostia Balytskyi>> # Date 1489250294 28800 >> # Sat Mar 11 08:38:14 2017 -0800 >> # Node ID 5179d6be9d6a33071c8a18cc48166a3f1d5ceec5 >> # Parent 2dbff12d9b22281c8d84328704095d72b3151388 >> 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 >> @@ -183,6 +183,8 @@ class shelvedstate(object): >> _filename = 'shelvedstate' >> _keep = 'keep' >> _nokeep = 'nokeep' >> +_obsbased = 'obsbased' >> +_traditional = 'traditional' >> >> def __init__(self, ui, repo): >> self.ui = ui >> @@ -204,6 +206,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 > You should probably allow this line to not exist, and if it's missing > just presume "traditional" shelve. That way if a user gets upgraded > from hg 4.1 to 4.2 mid-unshelve, they won't be stuck. I think this is what I am doing here, is it not? If it does not exist, fp.readline().strip() will be empty string and therefore not equal to cls._obsbased. Activebookmark patch comes after this one so anyone who started their unshelve without this series, would have neither shelve type, nor active bookmark in their state file. Anyone, who started their unshelve with this series will have both. I do not see the scenario in which someone would be stuck. Please give me an example. > >> except (ValueError, TypeError) as err: >> raise error.CorruptedState(str(err)) >> finally: >> @@ -218,6 +221,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)) >> >> @@ -225,7 +229,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) >> @@ -237,6 +241,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 > ___ > 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 5 of 5] statfs: make getfstype() raise OSError
The series makes the CPython code cleaner. Marked as prereviewed. I was making "getfstype" a pure C function, so it's possible to be reused directly for non-cpython code (as a side effect, it does not have Python error handling). The new "describefstype" provides enough support if we want a pure C implementation later. There won't be much duplicated code. Thanks! Excerpts from Yuya Nishihara's message of 2017-03-25 19:38:51 +0900: > # HG changeset patch > # User Yuya Nishihara> # Date 1490430323 -32400 > # Sat Mar 25 17:25:23 2017 +0900 > # Node ID 05dc34258f021523bc1de5c403f671fa4d1b9905 > # Parent 69a0abd9c61f0dae12d60b4f6fd4ada8288bd451 > statfs: make getfstype() raise OSError > > It's better for getfstype() function to not suppress an error. Callers can > handle it as necessary. Now "hg debugfsinfo" will report OSError. > > diff --git a/mercurial/osutil.c b/mercurial/osutil.c > --- a/mercurial/osutil.c > +++ b/mercurial/osutil.c > @@ -1090,7 +1090,7 @@ static PyObject *getfstype(PyObject *sel > memset(, 0, sizeof(buf)); > r = statfs(path, ); > if (r != 0) > -Py_RETURN_NONE; > +return PyErr_SetFromErrno(PyExc_OSError); > return Py_BuildValue("s", describefstype()); > } > #endif /* defined(HAVE_LINUX_STATFS) || defined(HAVE_BSD_STATFS) */ > diff --git a/mercurial/util.py b/mercurial/util.py > --- a/mercurial/util.py > +++ b/mercurial/util.py > @@ -1089,7 +1089,10 @@ def copyfile(src, dest, hardlink=False, > if hardlink: > # Hardlinks are problematic on CIFS (issue4546), do not allow > hardlinks > # unless we are confident that dest is on a whitelisted filesystem. > -fstype = getfstype(os.path.dirname(dest)) > +try: > +fstype = getfstype(os.path.dirname(dest)) > +except OSError: > +fstype = None > if fstype not in _hardlinkfswhitelist: > hardlink = False > if hardlink: > @@ -1372,7 +1375,7 @@ def fspath(name, root): > def getfstype(dirpath): > '''Get the filesystem type name from a directory (best-effort) > > -Returns None if we are unsure, or errors like ENOENT, EPERM happen. > +Returns None if we are unsure. Raises OSError on ENOENT, EPERM, etc. > ''' > return getattr(osutil, 'getfstype', lambda x: None)(dirpath) > > diff --git a/tests/hghave.py b/tests/hghave.py > --- a/tests/hghave.py > +++ b/tests/hghave.py > @@ -349,7 +349,10 @@ def has_hardlink(): > @check("hardlink-whitelisted", "hardlinks on whitelisted filesystems") > def has_hardlink_whitelisted(): > from mercurial import util > -fstype = util.getfstype('.') > +try: > +fstype = util.getfstype('.') > +except OSError: > +return False > return fstype in util._hardlinkfswhitelist > > @check("rmcwd", "can remove current working directory") ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 3 of 4] ui: defer setting pager related properties until the pager has spawned
# HG changeset patch # User Matt Harbison# Date 1490490720 14400 # Sat Mar 25 21:12:00 2017 -0400 # Node ID 84bda5db69dbe3d550f45ccd6d6eda2aad760ee4 # Parent a263702b064a5a3ce1ca74b227e8e624e4b05874 ui: defer setting pager related properties until the pager has spawned When --pager=on is given, dispatch.py spawns a pager before setting up color. If the pager failed to launch, ui.pageractive was left set to True, so color configured itself based on 'color.pagermode'. A typical MSYS setting would be 'color.mode=auto, color.pagermode=ansi'. In the failure case, this would print a warning, disable the pager, and then print the raw ANSI codes to the terminal. Care needs to be taken, because it appears that leaving ui.pageractive=True was the only thing that prevented an attempt at running the pager again from inside the command. This results in a double warning message, so pager is simply disabled on failure. I don't understand what chg is doing with pager, or how it signals a failure to spawn the pager. But this is no worse that the previous behavior. The ui config settings didn't need to be moved to fix this, but it seemed like the right thing to do for consistency. diff --git a/mercurial/chgserver.py b/mercurial/chgserver.py --- a/mercurial/chgserver.py +++ b/mercurial/chgserver.py @@ -193,6 +193,7 @@ def _runpager(self, cmd): self._csystem(cmd, util.shellenviron(), type='pager', cmdtable={'attachio': attachio}) +return True return chgui(srcui) diff --git a/mercurial/ui.py b/mercurial/ui.py --- a/mercurial/ui.py +++ b/mercurial/ui.py @@ -850,15 +850,22 @@ self.debug('starting pager for command %r\n' % command) self.flush() -self.pageractive = True -# Preserve the formatted-ness of the UI. This is important -# because we mess with stdout, which might confuse -# auto-detection of things being formatted. -self.setconfig('ui', 'formatted', self.formatted(), 'pager') -self.setconfig('ui', 'interactive', False, 'pager') + +wasformatted = self.formatted() if util.safehasattr(signal, "SIGPIPE"): signal.signal(signal.SIGPIPE, _catchterm) -self._runpager(pagercmd) +if self._runpager(pagercmd): +self.pageractive = True +# Preserve the formatted-ness of the UI. This is important +# because we mess with stdout, which might confuse +# auto-detection of things being formatted. +self.setconfig('ui', 'formatted', wasformatted, 'pager') +self.setconfig('ui', 'interactive', False, 'pager') +else: +# If the pager can't be spawned in dispatch when --pager=on is +# given, don't try again when the command runs, to avoid a duplicate +# warning about a missing pager command. +self.disablepager() def _runpager(self, command): """Actually start the pager and set up file descriptors. @@ -868,7 +875,7 @@ """ if command == 'cat': # Save ourselves some work. -return +return False # If the command doesn't contain any of these characters, we # assume it's a binary and exec it directly. This means for # simple pager command configurations, we can degrade @@ -898,7 +905,7 @@ if e.errno == errno.ENOENT and not shell: self.warn(_("missing pager command '%s', skipping pager\n") % command) -return +return False raise # back up original file descriptors @@ -919,6 +926,8 @@ pager.stdin.close() pager.wait() +return True + def interface(self, feature): """what interface to use for interactive console features? ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 1 of 4] color: fix grammar in help text
# HG changeset patch # User Matt Harbison# Date 1490462963 14400 # Sat Mar 25 13:29:23 2017 -0400 # Node ID 22533c3af63d5a67d9210596eafd4b99ab9c7904 # Parent c60091fa1426892552dd6c0dd4b9c49e3c3da045 color: fix grammar in help text diff --git a/hgext/color.py b/hgext/color.py --- a/hgext/color.py +++ b/hgext/color.py @@ -7,7 +7,7 @@ '''enable Mercurial color mode (DEPRECATED) -This extensions enable Mercurial color mode. The feature is now directly +This extension enables Mercurial color mode. The feature is now directly available in Mercurial core. You can access it using:: [ui] ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: A possible explanation for random "stream ended unexpectedly (got m bytes, expected n)"
On Sat, Mar 25, 2017 at 06:24:26PM -0700, Jun Wu wrote: > May I ask what version of Python are you using? If it's < 2.7.4, the EINTR > issue is expected. 2.7.13. > > Excerpts from Mike Hommey's message of 2017-03-26 09:08:25 +0900: > > On Sat, Mar 25, 2017 at 10:34:02AM -0700, Gregory Szorc wrote: > > > On Sat, Mar 25, 2017 at 4:19 AM, Mike Hommeywrote: > > > > > > > Hi, > > > > > > > > I don't know about you, but occasionally, I've hit "stream ended > > > > unexpectedly (got m bytes, expected n)" errors that didn't make sense. > > > > Retrying would always work. > > > > > > > > Recently, I was trying to use signal.setitimer and a signal handler for > > > > some memory profiling on git-cinnabar, which uses mercurial as a > > > > library, and got "stream ended 4 unexpectedly (got m bytes, expected n)" > > > > *very* reproducibly. Like, with an interval timer firing every second, > > > > it took only a few seconds to hit the error during a clone. > > > > > > > > I'm pretty sure this can be reproduced with a similar setup in mercurial > > > > itself. > > > > > > > > Now, the reason this happens in this case is that, the code that fails > > > > does: > > > > > > > > def readexactly(stream, n): > > > > '''read n bytes from stream.read and abort if less was available''' > > > > s = stream.read(n) > > > > if len(s) < n: > > > > raise error.Abort(_("stream ended unexpectedly" > > > >" (got %d bytes, expected %d)") > > > > % (len(s), n)) > > > > return s > > > > > > > > ... and thanks to POSIX, interrupted reads can lead to short reads. So, > > > > you request n bytes, and get less, just because something interrupted > > > > the process. The problem then is that python doesn't let you know why > > > > you just got a short read, and you have to figure that out on your own. > > > > > > > > The same kind of problem is also true to some extent on writes. > > > > > > > > Now, the problem is that this piece of code is likely the most visible > > > > place where the issue exists, but there are many other places in the > > > > mercurial code base that are likely affected. > > > > > > > > And while the signal.setitimer case is a corner case (and I ended up > > > > using a separate thread to work around the problem ; my code wasn't > > > > interruption safe either anyways), I wonder if those random "stream > > > > ended unexpectedly (got m bytes, expected n)" errors I was getting under > > > > normal circumstances are not just a manifestation of the same underlying > > > > issue, which is that the code doesn't like interrupted reads. > > > > > > > > Disclaimer: I'm not going to work on fixing this issue, but I figured > > > > I'd let you know, in case someone wants to look into it more deeply. > > > > > > > > > > Thank you for writing this up. This "stream ended unexpectedly" has been a > > > thorn in my side for a while, as it comes up frequently in Mozilla's CI > > > with a frequency somewhere between 1 in 100-1000. Even retrying failed > > > operations multiple times isn't enough to overcome it > > > > > > I have long suspected interrupted system calls as a likely culprit. > > > However, when I initially investigated this a few months ago, I found that > > > Python's I/O APIs retry automatically for EINTR. See > > > https://hg.python.org/cpython/file/54c93e0fe79b/Lib/socket.py#l365 for > > > example. This /should/ make e.g. socket._fileobject.read() resilient > > > against signal interruption. (If Python's I/O APIs didn't do this, tons of > > > programs would break. Also, the semantics of .read() are such that it is > > > always supposed to retrieve all available bytes until EOF - at least for > > > some io ABCs. read1() exists to perform at most 1 system call.) > > > > Note that EINTR is not the only way read() can end from interruption: > > > >If a read() is interrupted by a signal before it reads any data, it > >shall return -1 with errno set to [EINTR]. > > > >If a read() is interrupted by a signal after it has successfully read > >some data, it shall return the number of bytes read. > > > >From http://pubs.opengroup.org/onlinepubs/9699919799/functions/read.html > > > > But that's POSIX, Windows is another story. Recv is different too. > > > > On Sat, Mar 25, 2017 at 12:00:42PM -0700, Gregory Szorc wrote: > > > Can you please provide more detailed steps to reproduce? > > > > > > I added the following code at the top of exchange.pull: > > > > > > def sighandler(sig, stack): > > > pass > > > > > > import signal > > > signal.signal(signal.SIGALRM, sighandler) > > > signal.setitimer(signal.ITIMER_REAL, 1.0, 1.0) > > > > > > However, I was unable to reproduce the "stream ended unexpectedly" failure > > > when cloning a Firefox repo from hg.mozilla.org. And I even tried with the > > > interval set to 1ms. > > > > So, I tried to reproduce again with my original
Re: A possible explanation for random "stream ended unexpectedly (got m bytes, expected n)"
May I ask what version of Python are you using? If it's < 2.7.4, the EINTR issue is expected. Excerpts from Mike Hommey's message of 2017-03-26 09:08:25 +0900: > On Sat, Mar 25, 2017 at 10:34:02AM -0700, Gregory Szorc wrote: > > On Sat, Mar 25, 2017 at 4:19 AM, Mike Hommeywrote: > > > > > Hi, > > > > > > I don't know about you, but occasionally, I've hit "stream ended > > > unexpectedly (got m bytes, expected n)" errors that didn't make sense. > > > Retrying would always work. > > > > > > Recently, I was trying to use signal.setitimer and a signal handler for > > > some memory profiling on git-cinnabar, which uses mercurial as a > > > library, and got "stream ended 4 unexpectedly (got m bytes, expected n)" > > > *very* reproducibly. Like, with an interval timer firing every second, > > > it took only a few seconds to hit the error during a clone. > > > > > > I'm pretty sure this can be reproduced with a similar setup in mercurial > > > itself. > > > > > > Now, the reason this happens in this case is that, the code that fails > > > does: > > > > > > def readexactly(stream, n): > > > '''read n bytes from stream.read and abort if less was available''' > > > s = stream.read(n) > > > if len(s) < n: > > > raise error.Abort(_("stream ended unexpectedly" > > >" (got %d bytes, expected %d)") > > > % (len(s), n)) > > > return s > > > > > > ... and thanks to POSIX, interrupted reads can lead to short reads. So, > > > you request n bytes, and get less, just because something interrupted > > > the process. The problem then is that python doesn't let you know why > > > you just got a short read, and you have to figure that out on your own. > > > > > > The same kind of problem is also true to some extent on writes. > > > > > > Now, the problem is that this piece of code is likely the most visible > > > place where the issue exists, but there are many other places in the > > > mercurial code base that are likely affected. > > > > > > And while the signal.setitimer case is a corner case (and I ended up > > > using a separate thread to work around the problem ; my code wasn't > > > interruption safe either anyways), I wonder if those random "stream > > > ended unexpectedly (got m bytes, expected n)" errors I was getting under > > > normal circumstances are not just a manifestation of the same underlying > > > issue, which is that the code doesn't like interrupted reads. > > > > > > Disclaimer: I'm not going to work on fixing this issue, but I figured > > > I'd let you know, in case someone wants to look into it more deeply. > > > > > > > Thank you for writing this up. This "stream ended unexpectedly" has been a > > thorn in my side for a while, as it comes up frequently in Mozilla's CI > > with a frequency somewhere between 1 in 100-1000. Even retrying failed > > operations multiple times isn't enough to overcome it > > > > I have long suspected interrupted system calls as a likely culprit. > > However, when I initially investigated this a few months ago, I found that > > Python's I/O APIs retry automatically for EINTR. See > > https://hg.python.org/cpython/file/54c93e0fe79b/Lib/socket.py#l365 for > > example. This /should/ make e.g. socket._fileobject.read() resilient > > against signal interruption. (If Python's I/O APIs didn't do this, tons of > > programs would break. Also, the semantics of .read() are such that it is > > always supposed to retrieve all available bytes until EOF - at least for > > some io ABCs. read1() exists to perform at most 1 system call.) > > Note that EINTR is not the only way read() can end from interruption: > >If a read() is interrupted by a signal before it reads any data, it >shall return -1 with errno set to [EINTR]. > >If a read() is interrupted by a signal after it has successfully read >some data, it shall return the number of bytes read. > >From http://pubs.opengroup.org/onlinepubs/9699919799/functions/read.html > > But that's POSIX, Windows is another story. Recv is different too. > > On Sat, Mar 25, 2017 at 12:00:42PM -0700, Gregory Szorc wrote: > > Can you please provide more detailed steps to reproduce? > > > > I added the following code at the top of exchange.pull: > > > > def sighandler(sig, stack): > > pass > > > > import signal > > signal.signal(signal.SIGALRM, sighandler) > > signal.setitimer(signal.ITIMER_REAL, 1.0, 1.0) > > > > However, I was unable to reproduce the "stream ended unexpectedly" failure > > when cloning a Firefox repo from hg.mozilla.org. And I even tried with the > > interval set to 1ms. > > So, I tried to reproduce again with my original testcase, and failed to. > In fact, instead I was getting urllib2.URLError: Interrupted system call> errors. Delaying the initial SIGALRM to go past > the start of the HTTP request didn't fail either. Even small intervals. > > Then I tried on another machine and it
Re: A possible explanation for random "stream ended unexpectedly (got m bytes, expected n)"
On Sat, Mar 25, 2017 at 10:34:02AM -0700, Gregory Szorc wrote: > On Sat, Mar 25, 2017 at 4:19 AM, Mike Hommeywrote: > > > Hi, > > > > I don't know about you, but occasionally, I've hit "stream ended > > unexpectedly (got m bytes, expected n)" errors that didn't make sense. > > Retrying would always work. > > > > Recently, I was trying to use signal.setitimer and a signal handler for > > some memory profiling on git-cinnabar, which uses mercurial as a > > library, and got "stream ended 4 unexpectedly (got m bytes, expected n)" > > *very* reproducibly. Like, with an interval timer firing every second, > > it took only a few seconds to hit the error during a clone. > > > > I'm pretty sure this can be reproduced with a similar setup in mercurial > > itself. > > > > Now, the reason this happens in this case is that, the code that fails > > does: > > > > def readexactly(stream, n): > > '''read n bytes from stream.read and abort if less was available''' > > s = stream.read(n) > > if len(s) < n: > > raise error.Abort(_("stream ended unexpectedly" > >" (got %d bytes, expected %d)") > > % (len(s), n)) > > return s > > > > ... and thanks to POSIX, interrupted reads can lead to short reads. So, > > you request n bytes, and get less, just because something interrupted > > the process. The problem then is that python doesn't let you know why > > you just got a short read, and you have to figure that out on your own. > > > > The same kind of problem is also true to some extent on writes. > > > > Now, the problem is that this piece of code is likely the most visible > > place where the issue exists, but there are many other places in the > > mercurial code base that are likely affected. > > > > And while the signal.setitimer case is a corner case (and I ended up > > using a separate thread to work around the problem ; my code wasn't > > interruption safe either anyways), I wonder if those random "stream > > ended unexpectedly (got m bytes, expected n)" errors I was getting under > > normal circumstances are not just a manifestation of the same underlying > > issue, which is that the code doesn't like interrupted reads. > > > > Disclaimer: I'm not going to work on fixing this issue, but I figured > > I'd let you know, in case someone wants to look into it more deeply. > > > > Thank you for writing this up. This "stream ended unexpectedly" has been a > thorn in my side for a while, as it comes up frequently in Mozilla's CI > with a frequency somewhere between 1 in 100-1000. Even retrying failed > operations multiple times isn't enough to overcome it > > I have long suspected interrupted system calls as a likely culprit. > However, when I initially investigated this a few months ago, I found that > Python's I/O APIs retry automatically for EINTR. See > https://hg.python.org/cpython/file/54c93e0fe79b/Lib/socket.py#l365 for > example. This /should/ make e.g. socket._fileobject.read() resilient > against signal interruption. (If Python's I/O APIs didn't do this, tons of > programs would break. Also, the semantics of .read() are such that it is > always supposed to retrieve all available bytes until EOF - at least for > some io ABCs. read1() exists to perform at most 1 system call.) Note that EINTR is not the only way read() can end from interruption: If a read() is interrupted by a signal before it reads any data, it shall return -1 with errno set to [EINTR]. If a read() is interrupted by a signal after it has successfully read some data, it shall return the number of bytes read. From http://pubs.opengroup.org/onlinepubs/9699919799/functions/read.html But that's POSIX, Windows is another story. Recv is different too. On Sat, Mar 25, 2017 at 12:00:42PM -0700, Gregory Szorc wrote: > Can you please provide more detailed steps to reproduce? > > I added the following code at the top of exchange.pull: > > def sighandler(sig, stack): > pass > > import signal > signal.signal(signal.SIGALRM, sighandler) > signal.setitimer(signal.ITIMER_REAL, 1.0, 1.0) > > However, I was unable to reproduce the "stream ended unexpectedly" failure > when cloning a Firefox repo from hg.mozilla.org. And I even tried with the > interval set to 1ms. So, I tried to reproduce again with my original testcase, and failed to. In fact, instead I was getting urllib2.URLError: errors. Delaying the initial SIGALRM to go past the start of the HTTP request didn't fail either. Even small intervals. Then I tried on another machine and it happened reliably... and I figured it was because I had a config difference between the two machines, where in the latter case git-cinnabar would not use mercurial to talk to the server (but it would still use it to read the bundle2, and fail in mercurial.changegroup.readexactly). The main difference is that in the latter case, the underlying "connection" is a pipe instead of a
Re: [PATCH 02 of 10 shelve-ext v4] shelve: add an ability to write key-val data to a new type of shelve files
On 21/03/2017 21:48, Augie Fackler wrote: > On Sat, Mar 11, 2017 at 01:00:21PM -0800, Kostia Balytskyi wrote: >> # HG changeset patch >> # User Kostia Balytskyi>> # Date 1489186989 28800 >> # Fri Mar 10 15:03:09 2017 -0800 >> # Node ID 13c8fb8e722fd0563a83e601bb784694535268f1 >> # Parent ca01391d61f5725c4fc79ccffe0c8e2d6dbb97f0 >> 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. > Ouch. It might make sense to have a test-shelve-obs.t that is the > parallel universe version, so they can run in parallel. Can do. Should I send 1000+ lines patch or should I send a series of ~30 patches? I can also try to split it into multiple series (which does not really make semantical sense to me, but might make reviewing easier for people). >> diff --git a/hgext/shelve.py b/hgext/shelve.py >> --- a/hgext/shelve.py >> +++ b/hgext/shelve.py >> @@ -63,7 +63,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' >> >> @@ -154,6 +154,12 @@ class shelvedfile(object): >> bundle2.writebundle(self.ui, cg, self.fname, btype, self.vfs, >> compression=compression) >> >> +def writeobsshelveinfo(self, info): >> +scmutil.simplekeyvaluefile(self.vfs, self.fname).write(info) >> + >> +def readobsshelveinfo(self): >> +return scmutil.simplekeyvaluefile(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 > ___ > 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 10 of 10 shelve-ext v4] shelve: add logic to preserve active bookmarks
On 21/03/2017 21:52, Augie Fackler wrote: > On Sat, Mar 11, 2017 at 01:00:29PM -0800, Kostia Balytskyi wrote: >> # HG changeset patch >> # User Kostia Balytskyi>> # Date 1489250294 28800 >> # Sat Mar 11 08:38:14 2017 -0800 >> # Node ID 1ea8a13f3618cb9a89bd46195f66b5943dab6f75 >> # Parent fb764ce7dd0b6b93cdb4449f5bb52164521b0aad >> shelve: add logic to preserve active bookmarks > I'm confused by this patch a bit. Is it only rquired for obs-based shelving? Depends on what you mean by "required". It is only strictly required for obs-shelve since traditional shelve did not move active bookmarks unexpectedly. The reason traditional shelve did not fail in this respect before is because of transaction abort logic. So it worked but was ugly. Once this patch is accepted, both traditional and obs-based shelve are going to use explicit bookmark handling. > > (Sorry about the slow response on this series. I've been low on review > time and the big series like this are falling to the bottom of the > priority queue.) Hopefully, the length is going to get smaller and smaller :) But I think the lesson here for me is to learn to recognize things which will be almost undoubtedly accepted, like refactorings and preparations and so forth. And send them in separate series. > >> 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, >> @@ -186,6 +187,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 >> @@ -208,6 +211,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: >> @@ -223,6 +227,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)) >> >> @@ -230,7 +237,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) >> @@ -243,6 +250,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 >> @@ -281,6 +289,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 >> ''' >> @@ -400,7 +418,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() >> @@ -424,7 +444,7 @@ def _docreatecmd(ui, repo, pats, opts): >> if not opts.get('message'): >>
[PATCH] hardlink: check directory's st_dev instead of files
# HG changeset patch # User Jun Wu# Date 1490475382 25200 # Sat Mar 25 13:56:22 2017 -0700 # Node ID b288a611eff957d3f03b06c419c16721f4fb5016 # Parent c60091fa1426892552dd6c0dd4b9c49e3c3da045 # Available At https://bitbucket.org/quark-zju/hg-draft # hg pull https://bitbucket.org/quark-zju/hg-draft -r b288a611eff9 hardlink: check directory's st_dev instead of files Previously, copyfiles will compare src's st_dev with dirname(dst)'s st_dev, to decide whether to enable hardlink or not. That could have issues on Linux's overlayfs, where stating directories could result in different st_dev from files, even if both the directories and the files exist in the overlay's upperdir. This patch fixes it by checking dirname(src) instead. That fixes test-hardlinks.t running on common Docker environment. The "copyfiles" seems to need some extra cleanups, for example, the "hardlink" check should only happen if it's copying a file, not a directory. That cleanup will be done later. diff --git a/mercurial/util.py b/mercurial/util.py --- a/mercurial/util.py +++ b/mercurial/util.py @@ -1125,5 +1125,5 @@ def copyfiles(src, dst, hardlink=None, p if hardlink is None: -hardlink = (os.stat(src).st_dev == +hardlink = (os.stat(os.path.dirname(src)).st_dev == os.stat(os.path.dirname(dst)).st_dev) if hardlink: ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: Hardlinks and Docker
Excerpts from Jun Wu's message of 2017-03-25 12:19:46 -0700: > Excerpts from Gregory Szorc's message of 2017-03-25 10:54:10 -0700: > > Jun, et al: > > > > The recent patches around hardlink detection are great! However, there are > > still some failures executing tests in certain Docker configurations, such > > as when using the overlay2 storage driver (overlayfs). > > > > --- /mnt/hg/tests/test-hardlinks.t > > +++ /mnt/hg/tests/test-hardlinks.t.err > > @@ -58,14 +58,14 @@ > > Create hardlinked clone r2: > > > >$ hg clone -U --debug r1 r2 --config progress.debug=true > > - linking: 1 > > - linking: 2 > > - linking: 3 > > - linking: 4 > > - linking: 5 > > - linking: 6 > > - linking: 7 > > - linked 7 files > > + copying: 1 > > + copying: 2 > > + copying: 3 > > + copying: 4 > > + copying: 5 > > + copying: 6 > > + copying: 7 > > + copied 7 files > > This is unrelated to the statfs change. The "linking" / "copying" is > outputed by "util.copyfiles", which remains unchanged before and after the > statfs change. > > use hardlink? | before statfs | after statfs >-- > util.copyfile | never | sometimes (*, changed) > util.copyfiles | sometimes (**) | sometimes (**, no change) > > (*) when dest is on a whitelisted filesystem - new code > (**) when src and dst has a same st_dev - old code > > util.copyfiles was trying to be smart, to use hardlink if src and dst are in > a same device: > > # util.copyfiles > if hardlink is None: > hardlink = (os.stat(src).st_dev == > os.stat(os.path.dirname(dst)).st_dev) > > Quick introduction to Linux's overlayfs: > > lowerdir: usually read-only > upperdir: read-write > overlay: check upperdir first, if not found, check lowerdir. > for read-only operation, it's like opening the file in > lowerdir directly. > for write operation, make sure materialize (copy) the file to > upperdir, if it's not already there, then open it in upperdir > directly. > > So the "st_dev" check may failin the overlay case. src and dst could have > different st_dev - one lower, and one upper. I believe that's the root cause > of the test failure. It seems the explanation is not what actually happened. stat could return differently for directories in overlays. So the direct fix is to stat the directory instead of the file. I will send it soon. > > Interestingly, even if both paths are not on a same device, the "link" > syscall will success. It does that by materializing the file you're linking > first. So on "overlayfs", hardlink works, but it *copies* the file if not > materialized. > > > Create non-hardlinked clone r3: > > > > > > "#require hardlink" is returning true. But hardlinks don't really work on > > overlayfs it appears. I see there is now a test-hardlinks-whitelisted.t, > > > > which is a copy of test-hardlinks.t but uses the new filesystem detection > > code for whitelisting hardlinks. test-hardlinks-whitelisted.t passes under > > Docker on overlayfs, which is terrific! But it begs the question: what's > > This seems a different topic. > > I think test-hardlinks-whitelisted.t should behavior correct (skipped) on > overlayfs. (Note: if the test was running under /tmp which may be "tmpfs", > things could be different because "tmpfs" is whitelisted). "getfstype" will > correctly detect overlayfs regardless whether the test directory is > materialized or not. And since overlayfs is not whitelisted, the test will > be skipped. > > It shows "(unknown)" for now though, I will submit a patch to let it show > "overlay". But that's just a display problem. > > > our strategy for making tests pass when run under "faulty" filesystems? I > > ask because `make docker-*` targets run the test harness and test failures > > abort that process. So it would be nice to not have test failures under > > Docker. > > > > (FWIW there are some permissions related failures running tests under > > Docker as well. I encourage others to run `make docker-ubuntu-xenial` and > > other docker-* targets to poke at things.) > > As I have explained above, the "statfs" code is solid and works with > overlay. While the old "st_dev" check is the cause - but I'd say it still > does the right thing, see below. > > A question is, do we want to change the "st_dev" check? No. Because it is > reasonable for overlay, too. Even if we know the "link" syscall can > success, but calling it may trigger a file copy, why use it? The current > "st_dev" check makes sure the hardlink operation *does not copy* the file, > and do work as expected if the overlay has materialized "src" and > "dirname(dst)". > > Then how to fix it? I think it's "test-hardlinks.t" who needs to be updated > to become aware of overlayfs. We can move the overlay sensitive part to a > different test file, guarded with a
[PATCH] statfs: detect more filesystems on Linux
# HG changeset patch # User Jun Wu# Date 1490471935 25200 # Sat Mar 25 12:58:55 2017 -0700 # Node ID 99337cfe021d5222a9420ae0d7e9b29c2d03573b # Parent c60091fa1426892552dd6c0dd4b9c49e3c3da045 # Available At https://bitbucket.org/quark-zju/hg-draft # hg pull https://bitbucket.org/quark-zju/hg-draft -r 99337cfe021d statfs: detect more filesystems on Linux Previously, the code only has what manpager says. In , there are more defined. This patch adds filesystems that appear in the current Arch Linux's /proc/filesystems (autofs, overlay, securityfs) and f2fs, which was seen in news. diff --git a/mercurial/osutil.c b/mercurial/osutil.c --- a/mercurial/osutil.c +++ b/mercurial/osutil.c @@ -816,4 +816,8 @@ const char *getfstype(const char *path, return "affs"; #endif +#ifdef AUTOFS_SUPER_MAGIC + if (pbuf->f_type == AUTOFS_SUPER_MAGIC) + return "autofs"; +#endif #ifdef BDEVFS_MAGIC if (pbuf->f_type == BDEVFS_MAGIC) @@ -896,4 +900,8 @@ const char *getfstype(const char *path, return "ext4"; #endif +#ifdef F2FS_SUPER_MAGIC + if (pbuf->f_type == F2FS_SUPER_MAGIC) + return "f2fs"; +#endif #ifdef FUSE_SUPER_MAGIC if (pbuf->f_type == FUSE_SUPER_MAGIC) @@ -976,4 +984,8 @@ const char *getfstype(const char *path, return "openprom"; #endif +#ifdef OVERLAYFS_SUPER_MAGIC + if (pbuf->f_type == OVERLAYFS_SUPER_MAGIC) + return "overlay"; +#endif #ifdef PIPEFS_MAGIC if (pbuf->f_type == PIPEFS_MAGIC) @@ -1008,4 +1020,8 @@ const char *getfstype(const char *path, return "romfs"; #endif +#ifdef SECURITYFS_MAGIC + if (pbuf->f_type == SECURITYFS_MAGIC) + return "securityfs"; +#endif #ifdef SELINUX_MAGIC if (pbuf->f_type == SELINUX_MAGIC) ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: [PATCH 7 of 7 V5] hgweb: expose a followlines UI in filerevision view (RFC)
On Sat, Mar 25, 2017 at 12:34 PM, Gregory Szorcwrote: > On Sat, Mar 25, 2017 at 2:21 AM, Denis Laxalde wrote: > >> # HG changeset patch >> # User Denis Laxalde >> # Date 1489594320 -3600 >> # Wed Mar 15 17:12:00 2017 +0100 >> # Node ID ec77aa4ff2993057b604bdffb449d2d179525a9f >> # Parent 2f75006a7f31c97d29fd6dd9b72fa7cc9e7ab840 >> # Available At https://hg.logilab.org/users/dlaxalde/hg >> # hg pull https://hg.logilab.org/users/dlaxalde/hg -r >> ec77aa4ff299 >> # EXP-Topic linerange-log/hgweb-filelog >> hgweb: expose a followlines UI in filerevision view (RFC) >> >> In filerevision view (/file//) we add some event listeners on >> mouse selection of elements in the block. >> Those listeners will capture the range of mouse-selected lines and, upon >> mouse >> release, a box inviting to follow the history of selected lines will show >> up. >> This action is advertised by a :after pseudo-element on file lines that >> shows >> up on hover and invite to "select a block of lines to follow its history". >> >> All JavaScript code lives in a new "linerangelog.js" file, sourced in >> filerevision template (only in "paper" style for now). >> >> This is proposal implementation, comments welcome on any aspects. >> > > This feature is frigging awesome!!! I can pretty much guarantee that some > engineers at Mozilla will go completely crazy for this feature. > > As I'm playing with it locally, I found a few paper cuts. > > First, on the initial revision introducing lines (last revision as > rendered in hgweb), the diff is often (always?) empty. I can also see empty > diffs in the intermediate changesets. For example, run this on > mercurial/exchange.py and ask it for the line range of the > _pushdiscovery(pushop) function. For this function, I get 2 empty diffs > (revisions 233623d58c9a and ddb56e7e1b92). Highlighting > _pushdiscoverychangeset() yields only an empty initial diff. > > Second, the highlighting UI is a bit confusing. It took me a few seconds > to realize I had to actually hold down the mouse button and highlight the > lines. I don't think users are accustomed to do this on text in web pages > unless they are copying text. It was definitely surprising to me that > highlighting lines of text resulted in a special pop-up appearing. I'm no > web design expert, but how about this design. > > As you mouseover lines, you see a graphic cursor somewhere on that line. > There is likely a label next to it saying "click anywhere to follow from > this line" or something like that. This is similar to the floating text > label you have now. When you mouse click, that floating cursor is dropped > and locked into place on that line. On subsequent mouseovers, another > cursor+label appears. The lines between the initial cursor and the current > mouse location are highlighted somehow (possibly adding a different > background color). The label says "click anywhere to follow lines XX to YY" > or something. When the user clicks to drop the 2nd cursor, either they're > taken directly to the filelog view or we get an inline box with links (like > the line number mouseovers on the annotate page). If the user accidentally > clicks to drop a cursor, they can clear the cursor be clicking the cursor > or an "x" next to it. > > Another minor nitpick with the highlighting UI is the "follow lines" box > may not appear next to the mouse cursor. I think we want it to appear near > the cursor so it is easier to find/use. > > The UX can obviously be improved as a follow-up. I don't want to detract > from getting the core of the feature landed. This is shaping up to be > pretty amazing! > There's also a performance issue on very large files (such as layout/base/nsCSSFrameConstructor.cpp from a Firefox repo) in Firefox. When you start highlighting, the browser slows down dramatically. This doesn't happen in Chrome. (But I can't get the pop-up box to render in Chrome.) This is definitely a regression from this patch. FWIW, introducing poorly-performing code in a web-based tool that Firefox developers use is a good way to make Firefox faster. I've seen web performance issues in hgweb and ReviewBoard result in performance improvements to Firefox. Funny how that works. > > >> >> diff --git a/contrib/wix/templates.wxs b/contrib/wix/templates.wxs >> --- a/contrib/wix/templates.wxs >> +++ b/contrib/wix/templates.wxs >> @@ -225,6 +225,7 @@ >> >> > /> >> >> +> /> >> >> >> >> diff --git a/mercurial/templates/paper/filerevision.tmpl >> b/mercurial/templates/paper/filerevision.tmpl >> --- a/mercurial/templates/paper/filerevision.tmpl >> +++ b/mercurial/templates/paper/filerevision.tmpl >> @@ -71,8 +71,11 @@ >> >> line wrap: > class="linewraplink" href="javascript:toggleLinewrap()">on >> line source >> -{text%fileline} >> +>
Re: [PATCH 7 of 7 V5] hgweb: expose a followlines UI in filerevision view (RFC)
On Sat, Mar 25, 2017 at 2:21 AM, Denis Laxaldewrote: > # HG changeset patch > # User Denis Laxalde > # Date 1489594320 -3600 > # Wed Mar 15 17:12:00 2017 +0100 > # Node ID ec77aa4ff2993057b604bdffb449d2d179525a9f > # Parent 2f75006a7f31c97d29fd6dd9b72fa7cc9e7ab840 > # Available At https://hg.logilab.org/users/dlaxalde/hg > # hg pull https://hg.logilab.org/users/dlaxalde/hg -r > ec77aa4ff299 > # EXP-Topic linerange-log/hgweb-filelog > hgweb: expose a followlines UI in filerevision view (RFC) > > In filerevision view (/file//) we add some event listeners on > mouse selection of elements in the block. > Those listeners will capture the range of mouse-selected lines and, upon > mouse > release, a box inviting to follow the history of selected lines will show > up. > This action is advertised by a :after pseudo-element on file lines that > shows > up on hover and invite to "select a block of lines to follow its history". > > All JavaScript code lives in a new "linerangelog.js" file, sourced in > filerevision template (only in "paper" style for now). > > This is proposal implementation, comments welcome on any aspects. > This feature is frigging awesome!!! I can pretty much guarantee that some engineers at Mozilla will go completely crazy for this feature. As I'm playing with it locally, I found a few paper cuts. First, on the initial revision introducing lines (last revision as rendered in hgweb), the diff is often (always?) empty. I can also see empty diffs in the intermediate changesets. For example, run this on mercurial/exchange.py and ask it for the line range of the _pushdiscovery(pushop) function. For this function, I get 2 empty diffs (revisions 233623d58c9a and ddb56e7e1b92). Highlighting _pushdiscoverychangeset() yields only an empty initial diff. Second, the highlighting UI is a bit confusing. It took me a few seconds to realize I had to actually hold down the mouse button and highlight the lines. I don't think users are accustomed to do this on text in web pages unless they are copying text. It was definitely surprising to me that highlighting lines of text resulted in a special pop-up appearing. I'm no web design expert, but how about this design. As you mouseover lines, you see a graphic cursor somewhere on that line. There is likely a label next to it saying "click anywhere to follow from this line" or something like that. This is similar to the floating text label you have now. When you mouse click, that floating cursor is dropped and locked into place on that line. On subsequent mouseovers, another cursor+label appears. The lines between the initial cursor and the current mouse location are highlighted somehow (possibly adding a different background color). The label says "click anywhere to follow lines XX to YY" or something. When the user clicks to drop the 2nd cursor, either they're taken directly to the filelog view or we get an inline box with links (like the line number mouseovers on the annotate page). If the user accidentally clicks to drop a cursor, they can clear the cursor be clicking the cursor or an "x" next to it. Another minor nitpick with the highlighting UI is the "follow lines" box may not appear next to the mouse cursor. I think we want it to appear near the cursor so it is easier to find/use. The UX can obviously be improved as a follow-up. I don't want to detract from getting the core of the feature landed. This is shaping up to be pretty amazing! > > diff --git a/contrib/wix/templates.wxs b/contrib/wix/templates.wxs > --- a/contrib/wix/templates.wxs > +++ b/contrib/wix/templates.wxs > @@ -225,6 +225,7 @@ > > > > + > > > > diff --git a/mercurial/templates/paper/filerevision.tmpl > b/mercurial/templates/paper/filerevision.tmpl > --- a/mercurial/templates/paper/filerevision.tmpl > +++ b/mercurial/templates/paper/filerevision.tmpl > @@ -71,8 +71,11 @@ > > line wrap: class="linewraplink" href="javascript:toggleLinewrap()">on > line source > -{text%fileline} > + data-logurl="{url|urlescape}log/{symrev}/{file|urlescape}" > >{text%fileline} > > + > + > + > > > > diff --git a/mercurial/templates/static/linerangelog.js > b/mercurial/templates/static/linerangelog.js > new file mode 100644 > --- /dev/null > +++ b/mercurial/templates/static/linerangelog.js > @@ -0,0 +1,83 @@ > +// Copyright 2017 Logilab SA > +// > +// This software may be used and distributed according to the terms > +// of the GNU General Public License, incorporated herein by reference. > + > +document.addEventListener('DOMContentLoaded', function() { > +// install mouse event listeners on > +var sourcelines = document.getElementsByClassName('sourcelines')[0]; > +if (typeof sourcelines === 'undefined') { > +return; > +} > +var targetUri = sourcelines.dataset.logurl; > +if (typeof
Re: Hardlinks and Docker
Excerpts from Gregory Szorc's message of 2017-03-25 10:54:10 -0700: > Jun, et al: > > The recent patches around hardlink detection are great! However, there are > still some failures executing tests in certain Docker configurations, such > as when using the overlay2 storage driver (overlayfs). > > --- /mnt/hg/tests/test-hardlinks.t > +++ /mnt/hg/tests/test-hardlinks.t.err > @@ -58,14 +58,14 @@ > Create hardlinked clone r2: > >$ hg clone -U --debug r1 r2 --config progress.debug=true > - linking: 1 > - linking: 2 > - linking: 3 > - linking: 4 > - linking: 5 > - linking: 6 > - linking: 7 > - linked 7 files > + copying: 1 > + copying: 2 > + copying: 3 > + copying: 4 > + copying: 5 > + copying: 6 > + copying: 7 > + copied 7 files This is unrelated to the statfs change. The "linking" / "copying" is outputed by "util.copyfiles", which remains unchanged before and after the statfs change. use hardlink? | before statfs | after statfs -- util.copyfile | never | sometimes (*, changed) util.copyfiles | sometimes (**) | sometimes (**, no change) (*) when dest is on a whitelisted filesystem - new code (**) when src and dst has a same st_dev - old code util.copyfiles was trying to be smart, to use hardlink if src and dst are in a same device: # util.copyfiles if hardlink is None: hardlink = (os.stat(src).st_dev == os.stat(os.path.dirname(dst)).st_dev) Quick introduction to Linux's overlayfs: lowerdir: usually read-only upperdir: read-write overlay: check upperdir first, if not found, check lowerdir. for read-only operation, it's like opening the file in lowerdir directly. for write operation, make sure materialize (copy) the file to upperdir, if it's not already there, then open it in upperdir directly. So the "st_dev" check may failin the overlay case. src and dst could have different st_dev - one lower, and one upper. I believe that's the root cause of the test failure. Interestingly, even if both paths are not on a same device, the "link" syscall will success. It does that by materializing the file you're linking first. So on "overlayfs", hardlink works, but it *copies* the file if not materialized. > Create non-hardlinked clone r3: > > > "#require hardlink" is returning true. But hardlinks don't really work on > overlayfs it appears. I see there is now a test-hardlinks-whitelisted.t, > > which is a copy of test-hardlinks.t but uses the new filesystem detection > code for whitelisting hardlinks. test-hardlinks-whitelisted.t passes under > Docker on overlayfs, which is terrific! But it begs the question: what's This seems a different topic. I think test-hardlinks-whitelisted.t should behavior correct (skipped) on overlayfs. (Note: if the test was running under /tmp which may be "tmpfs", things could be different because "tmpfs" is whitelisted). "getfstype" will correctly detect overlayfs regardless whether the test directory is materialized or not. And since overlayfs is not whitelisted, the test will be skipped. It shows "(unknown)" for now though, I will submit a patch to let it show "overlay". But that's just a display problem. > our strategy for making tests pass when run under "faulty" filesystems? I > ask because `make docker-*` targets run the test harness and test failures > abort that process. So it would be nice to not have test failures under > Docker. > > (FWIW there are some permissions related failures running tests under > Docker as well. I encourage others to run `make docker-ubuntu-xenial` and > other docker-* targets to poke at things.) As I have explained above, the "statfs" code is solid and works with overlay. While the old "st_dev" check is the cause - but I'd say it still does the right thing, see below. A question is, do we want to change the "st_dev" check? No. Because it is reasonable for overlay, too. Even if we know the "link" syscall can success, but calling it may trigger a file copy, why use it? The current "st_dev" check makes sure the hardlink operation *does not copy* the file, and do work as expected if the overlay has materialized "src" and "dirname(dst)". Then how to fix it? I think it's "test-hardlinks.t" who needs to be updated to become aware of overlayfs. We can move the overlay sensitive part to a different test file, guarded with a new hghave "overlay" so it gets skipped correctly. ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: A possible explanation for random "stream ended unexpectedly (got m bytes, expected n)"
On Sat, Mar 25, 2017 at 4:19 AM, Mike Hommeywrote: > Hi, > > I don't know about you, but occasionally, I've hit "stream ended > unexpectedly (got m bytes, expected n)" errors that didn't make sense. > Retrying would always work. > > Recently, I was trying to use signal.setitimer and a signal handler for > some memory profiling on git-cinnabar, which uses mercurial as a > library, and got "stream ended 4 unexpectedly (got m bytes, expected n)" > *very* reproducibly. Like, with an interval timer firing every second, > it took only a few seconds to hit the error during a clone. > Can you please provide more detailed steps to reproduce? I added the following code at the top of exchange.pull: def sighandler(sig, stack): pass import signal signal.signal(signal.SIGALRM, sighandler) signal.setitimer(signal.ITIMER_REAL, 1.0, 1.0) However, I was unable to reproduce the "stream ended unexpectedly" failure when cloning a Firefox repo from hg.mozilla.org. And I even tried with the interval set to 1ms. > > I'm pretty sure this can be reproduced with a similar setup in mercurial > itself. > > Now, the reason this happens in this case is that, the code that fails > does: > > def readexactly(stream, n): > '''read n bytes from stream.read and abort if less was available''' > s = stream.read(n) > if len(s) < n: > raise error.Abort(_("stream ended unexpectedly" >" (got %d bytes, expected %d)") > % (len(s), n)) > return s > > ... and thanks to POSIX, interrupted reads can lead to short reads. So, > you request n bytes, and get less, just because something interrupted > the process. The problem then is that python doesn't let you know why > you just got a short read, and you have to figure that out on your own. > > The same kind of problem is also true to some extent on writes. > > Now, the problem is that this piece of code is likely the most visible > place where the issue exists, but there are many other places in the > mercurial code base that are likely affected. > > And while the signal.setitimer case is a corner case (and I ended up > using a separate thread to work around the problem ; my code wasn't > interruption safe either anyways), I wonder if those random "stream > ended unexpectedly (got m bytes, expected n)" errors I was getting under > normal circumstances are not just a manifestation of the same underlying > issue, which is that the code doesn't like interrupted reads. > > Disclaimer: I'm not going to work on fixing this issue, but I figured > I'd let you know, in case someone wants to look into it more deeply. > > Cheers, > > Mike > ___ > 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
Hardlinks and Docker
Jun, et al: The recent patches around hardlink detection are great! However, there are still some failures executing tests in certain Docker configurations, such as when using the overlay2 storage driver (overlayfs). --- /mnt/hg/tests/test-hardlinks.t +++ /mnt/hg/tests/test-hardlinks.t.err @@ -58,14 +58,14 @@ Create hardlinked clone r2: $ hg clone -U --debug r1 r2 --config progress.debug=true - linking: 1 - linking: 2 - linking: 3 - linking: 4 - linking: 5 - linking: 6 - linking: 7 - linked 7 files + copying: 1 + copying: 2 + copying: 3 + copying: 4 + copying: 5 + copying: 6 + copying: 7 + copied 7 files Create non-hardlinked clone r3: "#require hardlink" is returning true. But hardlinks don't really work on overlayfs it appears. I see there is now a test-hardlinks-whitelisted.t, which is a copy of test-hardlinks.t but uses the new filesystem detection code for whitelisting hardlinks. test-hardlinks-whitelisted.t passes under Docker on overlayfs, which is terrific! But it begs the question: what's our strategy for making tests pass when run under "faulty" filesystems? I ask because `make docker-*` targets run the test harness and test failures abort that process. So it would be nice to not have test failures under Docker. (FWIW there are some permissions related failures running tests under Docker as well. I encourage others to run `make docker-ubuntu-xenial` and other docker-* targets to poke at things.) ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: A possible explanation for random "stream ended unexpectedly (got m bytes, expected n)"
On Sat, Mar 25, 2017 at 4:19 AM, Mike Hommeywrote: > Hi, > > I don't know about you, but occasionally, I've hit "stream ended > unexpectedly (got m bytes, expected n)" errors that didn't make sense. > Retrying would always work. > > Recently, I was trying to use signal.setitimer and a signal handler for > some memory profiling on git-cinnabar, which uses mercurial as a > library, and got "stream ended 4 unexpectedly (got m bytes, expected n)" > *very* reproducibly. Like, with an interval timer firing every second, > it took only a few seconds to hit the error during a clone. > > I'm pretty sure this can be reproduced with a similar setup in mercurial > itself. > > Now, the reason this happens in this case is that, the code that fails > does: > > def readexactly(stream, n): > '''read n bytes from stream.read and abort if less was available''' > s = stream.read(n) > if len(s) < n: > raise error.Abort(_("stream ended unexpectedly" >" (got %d bytes, expected %d)") > % (len(s), n)) > return s > > ... and thanks to POSIX, interrupted reads can lead to short reads. So, > you request n bytes, and get less, just because something interrupted > the process. The problem then is that python doesn't let you know why > you just got a short read, and you have to figure that out on your own. > > The same kind of problem is also true to some extent on writes. > > Now, the problem is that this piece of code is likely the most visible > place where the issue exists, but there are many other places in the > mercurial code base that are likely affected. > > And while the signal.setitimer case is a corner case (and I ended up > using a separate thread to work around the problem ; my code wasn't > interruption safe either anyways), I wonder if those random "stream > ended unexpectedly (got m bytes, expected n)" errors I was getting under > normal circumstances are not just a manifestation of the same underlying > issue, which is that the code doesn't like interrupted reads. > > Disclaimer: I'm not going to work on fixing this issue, but I figured > I'd let you know, in case someone wants to look into it more deeply. > Thank you for writing this up. This "stream ended unexpectedly" has been a thorn in my side for a while, as it comes up frequently in Mozilla's CI with a frequency somewhere between 1 in 100-1000. Even retrying failed operations multiple times isn't enough to overcome it I have long suspected interrupted system calls as a likely culprit. However, when I initially investigated this a few months ago, I found that Python's I/O APIs retry automatically for EINTR. See https://hg.python.org/cpython/file/54c93e0fe79b/Lib/socket.py#l365 for example. This /should/ make e.g. socket._fileobject.read() resilient against signal interruption. (If Python's I/O APIs didn't do this, tons of programs would break. Also, the semantics of .read() are such that it is always supposed to retrieve all available bytes until EOF - at least for some io ABCs. read1() exists to perform at most 1 system call.) But you provide compelling evidence that signal interruptions do in fact result in truncated reads. So something is obviously going wrong. The stream.read() in changegroup.readexactly() actually follows a pretty complicated path. Here is what happens when reading from an HTTP response: 1. stream is a mercurial.httppeer.readerproxy 2. The readerproxy is constructed from util._zlibengine.decompressorreader and a mercurial.keepalive.HTTPResponse instance 3. decompressorreader takes the file object interface from the HTTPResponse, constructs an util.filechunkiter, feeds that into a zlib.decompressobj and turns that into a util.chunkbuffer 4. When you stream.read(), it has to traverse through a util.chunkbuffer, a generator of zlib decompressed chunks, a util.filechunkiter, and HTTP chunked transfer decoding before it finally gets to a recv() in socket._fileobject.read(). Any of those things could have a bug where a signal interrupts things or a truncated read isn't retried. I suspect our bug is somewhere in this chain. I have some other wrinkles to add. Mozilla sees these stream failures much more frequently on Windows. They do happen on Linux and OS X in the wild. But they are >10x more frequent on Windows. I'm pretty sure we're running 2.7.12 on Windows (on my insistence to help isolate an old Python as the source of the bug). Mozilla also sees "unexpected negative part header" errors. e.g. https://bugzilla.mozilla.org/show_bug.cgi?id=148. The code in bundle2 that is emitting this error also uses changegroup.readexactly. I've long suspected the cause of this is a bit flip or reading a truncated or uninitialized 4 byte field. I would not at all be surprised if "stream ended unexpectedly" and "unexpected negative part header" share the same root cause. Anyway, since you can reproduce this pretty reliably with a constant barrage
[PATCH 3 of 4] templates: add "changeset.obsolete" label in command line style
# HG changeset patch # User Denis Laxalde# Date 1490434829 -3600 # Sat Mar 25 10:40:29 2017 +0100 # Node ID 94a70894c394268b845f427f7643738a70e336bb # Parent c4c825f1402861e4b988395ac3deebcc6b5292cf # Available At https://bitbucket.org/dlax/hg-work # hg pull https://bitbucket.org/dlax/hg-work -r 94a70894c394 # EXP-Topic obsolete-ui templates: add "changeset.obsolete" label in command line style Following respective change in cmdutil.changeset_printer. diff --git a/mercurial/templates/map-cmdline.default b/mercurial/templates/map-cmdline.default --- a/mercurial/templates/map-cmdline.default +++ b/mercurial/templates/map-cmdline.default @@ -29,7 +29,7 @@ lfile_copies_switch = '{if(file_copies_s # General templates _trouble_label = 'trouble.{trouble}' -_cset_labels = 'log.changeset changeset.{phase}{if(troubles, " changeset.troubled {troubles%_trouble_label}")}' +_cset_labels = 'log.changeset changeset.{phase}{if(obsolete, " changeset.obsolete")}{if(troubles, " changeset.troubled {troubles%_trouble_label}")}' cset = '{label("{_cset_labels}", "changeset: {rev}:{node|short}")}\n' diff --git a/tests/test-obsolete.t b/tests/test-obsolete.t --- a/tests/test-obsolete.t +++ b/tests/test-obsolete.t @@ -830,6 +830,13 @@ test the default cmdline template trouble: unstable, bumped summary: add babar + $ hg log -T default -r 'obsolete()' + changeset: 6:3de5eca88c00 + parent: 3:6f9641995072 + user:test + date:Thu Jan 01 00:00:00 1970 + + summary: add obsolete_e + test summary output ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 2 of 4] templatekw: add an "obsolete" keyword
# HG changeset patch # User Denis Laxalde# Date 1490434451 -3600 # Sat Mar 25 10:34:11 2017 +0100 # Node ID c4c825f1402861e4b988395ac3deebcc6b5292cf # Parent 130358da2eb894aa04e3bb731d0ccae84205c2ba # Available At https://bitbucket.org/dlax/hg-work # hg pull https://bitbucket.org/dlax/hg-work -r c4c825f14028 # EXP-Topic obsolete-ui templatekw: add an "obsolete" keyword This keyword is a Boolean that indicates if a changeset is obsolete. diff --git a/mercurial/templatekw.py b/mercurial/templatekw.py --- a/mercurial/templatekw.py +++ b/mercurial/templatekw.py @@ -514,6 +514,12 @@ def shownode(repo, ctx, templ, **args): """ return ctx.hex() +@templatekeyword('obsolete') +def showobsolete(repo, ctx, templ, **args): +"""Boolean. True if this changeset is obsolete. +""" +return ctx.obsolete() + @templatekeyword('p1rev') def showp1rev(repo, ctx, templ, **args): """Integer. The repository-local revision number of the changeset's diff --git a/tests/test-obsolete.t b/tests/test-obsolete.t --- a/tests/test-obsolete.t +++ b/tests/test-obsolete.t @@ -3,7 +3,7 @@ > # public changeset are not obsolete > publish=false > [ui] - > logtemplate="{rev}:{node|short} ({phase}{if(troubles, ' {troubles}')}) [{tags} {bookmarks}] {desc|firstline}\n" + > logtemplate="{rev}:{node|short} ({phase}{if(obsolete, ' *obsolete*')}{if(troubles, ' {troubles}')}) [{tags} {bookmarks}] {desc|firstline}\n" > EOF $ mkcommit() { >echo "$1" > "$1" @@ -155,9 +155,9 @@ check that heads does not report them 5:5601fb93a350 (draft) [tip ] add new_3_c $ hg heads --hidden 5:5601fb93a350 (draft) [tip ] add new_3_c - 4:ca819180edb9 (draft) [ ] add new_2_c - 3:cdbce2fbb163 (draft) [ ] add new_c - 2:245bde4270cd (draft) [ ] add original_c + 4:ca819180edb9 (draft *obsolete*) [ ] add new_2_c + 3:cdbce2fbb163 (draft *obsolete*) [ ] add new_c + 2:245bde4270cd (draft *obsolete*) [ ] add original_c check that summary does not report them @@ -392,11 +392,11 @@ clone support $ hg -R clone-dest log -G --hidden @ 6:6f9641995072 (draft) [tip ] add n3w_3_c | - | x 5:5601fb93a350 (draft) [ ] add new_3_c + | x 5:5601fb93a350 (draft *obsolete*) [ ] add new_3_c |/ - | x 4:ca819180edb9 (draft) [ ] add new_2_c + | x 4:ca819180edb9 (draft *obsolete*) [ ] add new_2_c |/ - | x 3:cdbce2fbb163 (draft) [ ] add new_c + | x 3:cdbce2fbb163 (draft *obsolete*) [ ] add new_c |/ | o 2:245bde4270cd (public) [ ] add original_c |/ @@ -475,7 +475,7 @@ detect outgoing obsolete and unstable $ hg debugobsolete | grep `getid original_d` 94b33453f93bdb8d457ef9b770851a618bf413e1 0 {6f96419950729f3671185b847352890f074f7557} (Thu Jan 01 00:00:00 1970 +) {'user': 'test'} $ hg log -r 'obsolete()' - 4:94b33453f93b (draft) [ ] add original_d + 4:94b33453f93b (draft *obsolete*) [ ] add original_d $ hg summary parent: 5:cda648ca50f5 tip (unstable) add original_e @@ -487,7 +487,7 @@ detect outgoing obsolete and unstable $ hg log -G -r '::unstable()' @ 5:cda648ca50f5 (draft unstable) [tip ] add original_e | - x 4:94b33453f93b (draft) [ ] add original_d + x 4:94b33453f93b (draft *obsolete*) [ ] add original_d | o 3:6f9641995072 (draft) [ ] add n3w_3_c | @@ -526,7 +526,7 @@ Don't try to push extinct changeset 1:7c3bad9141dc (public) [ ] add b 2:245bde4270cd (public) [ ] add original_c 3:6f9641995072 (draft) [ ] add n3w_3_c - 4:94b33453f93b (draft) [ ] add original_d + 4:94b33453f93b (draft *obsolete*) [ ] add original_d 5:cda648ca50f5 (draft unstable) [tip ] add original_e $ hg push ../tmpf -f # -f because be push unstable too pushing to ../tmpf @@ -550,7 +550,7 @@ Do not warn about new head when the new $ hg log -G @ 5:cda648ca50f5 (draft unstable) [tip ] add original_e | - x 4:94b33453f93b (draft) [ ] add original_d + x 4:94b33453f93b (draft *obsolete*) [ ] add original_d | o 3:6f9641995072 (draft) [ ] add n3w_3_c | @@ -588,9 +588,9 @@ Reminder of the repo situation $ hg log --hidden --graph @ 6:3de5eca88c00 (draft) [tip ] add obsolete_e | - | x 5:cda648ca50f5 (draft) [ ] add original_e + | x 5:cda648ca50f5 (draft *obsolete*) [ ] add original_e | | - | x 4:94b33453f93b (draft) [ ] add original_d + | x 4:94b33453f93b (draft *obsolete*) [ ] add original_d |/ o 3:6f9641995072 (draft) [ ] add n3w_3_c | @@ -811,6 +811,11 @@ Several troubles on the same changeset ( summary: add babar +test the "obsolete" templatekw + + $ hg log -r 'obsolete()' + 6:3de5eca88c00 (draft *obsolete*) [ ] add obsolete_e + test the "troubles" templatekw $ hg log -r 'bumped() and unstable()' @@ -927,7 +932,7 @@ Test that a local tag blocks a changeset $ hg log -G @ 3:323a9c3ddd91 (draft) [tip ] A | - | x 1:29f0c6921ddd (draft) [visible ] A + | x 1:29f0c6921ddd (draft *obsolete*) [visible ] A |/ o 0:d20a80d4def3 (draft)
[PATCH 4 of 4] summary: display obsolete state of parents
# HG changeset patch # User Denis Laxalde# Date 1490437808 -3600 # Sat Mar 25 11:30:08 2017 +0100 # Node ID af7190c42c1cd6ba16a9bd83ad54208d89f343fe # Parent 94a70894c394268b845f427f7643738a70e336bb # Available At https://bitbucket.org/dlax/hg-work # hg pull https://bitbucket.org/dlax/hg-work -r af7190c42c1c # EXP-Topic obsolete-ui summary: display obsolete state of parents Extend the "parent: " lines in summary to display "(obsolete)" when the parent is obsolete. diff --git a/mercurial/commands.py b/mercurial/commands.py --- a/mercurial/commands.py +++ b/mercurial/commands.py @@ -4826,6 +4826,8 @@ def summary(ui, repo, **opts): ui.write(_(' (empty repository)')) else: ui.write(_(' (no revision checked out)')) +if p.obsolete(): +ui.write(_(' (obsolete)')) if p.troubled(): ui.write(' (' + ', '.join(ui.label(trouble, 'trouble.%s' % trouble) diff --git a/tests/test-obsolete.t b/tests/test-obsolete.t --- a/tests/test-obsolete.t +++ b/tests/test-obsolete.t @@ -851,6 +851,17 @@ test summary output phases: 4 draft unstable: 2 changesets bumped: 1 changesets + $ hg up -r 'obsolete()' + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ hg summary + parent: 6:3de5eca88c00 (obsolete) + add obsolete_e + branch: default + commit: (clean) + update: 3 new changesets (update) + phases: 4 draft + unstable: 2 changesets + bumped: 1 changesets Test incoming/outcoming with changesets obsoleted remotely, known locally === ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: [PATCH 01 of 11 V2] scmutil: add a method to convert environment variables to config items
On 3/25/17 4:16 AM, Yuya Nishihara wrote: On Wed, 22 Mar 2017 08:52:32 -0700, Jun Wu wrote: Excerpts from Ryan McElroy's message of 2017-03-22 11:16:40 +: This function is fine, but scmutil feels like the wrong place for it. Why is it not in config.py? This same question applies to patches 2-5 as well. I seems like most of this should be living in config.py. We can move it over in a future series, I suppose? Two reasons: 1. The existing *path() functions are in scmposix, scmwindows and scmutil 2. If you look at config.py, it has zero special-case logic. i.e. it does not know config sections, it does not know config paths, etc. It only knows how to load configs and provides a class to access configs. I think config.py is better keeping "pure" by providing just the data strucutre without any knowledge about actual config sections etc. In the upcoming immutable config series, the config.py will have immutable structures. But things like ui.fixconfig, ui.readconfig, trust vs untrust etc will stay away from config.py Maybe we can introduce rcutil.py (or hgrcutil.py) and move scmutil.*rcpath(), ui.samplehgrcs, and ui.pathsuboption stuffs into it. +1, that's a good idea, I'll take a shot at this next week if nobody beats me to it. ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: [PATCH 2 of 2] rebase: don't require destination if commands.rebase.requiredest=False
On 3/25/17 4:52 AM, Yuya Nishihara wrote: On Fri, 24 Mar 2017 18:49:14 -0700, Jun Wu wrote: This looks good to me. Thanks for the fix! Excerpts from Martin von Zweigbergk's message of 2017-03-24 16:35:08 -0700: # HG changeset patch # User Martin von Zweigbergk# Date 1490397610 25200 # Fri Mar 24 16:20:10 2017 -0700 # Node ID ccac4af8923796bf53dba045d574dcfd68665fff # Parent d2674430b3d3eea1259e4fe2c1d7da539db9454f rebase: don't require destination if commands.rebase.requiredest=False Sure, queued, thanks. Argh, sorry for the facepalm failures on the original series. I'll do better next time! ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: [PATCH 2 of 2 V2] show: new extension for displaying various repository data
On 3/25/17 5:22 AM, Yuya Nishihara wrote: On Thu, 23 Mar 2017 11:38:45 -0400, Augie Fackler wrote: On Thu, Mar 23, 2017 at 12:16:16AM +0900, Yuya Nishihara wrote: On Tue, 21 Mar 2017 23:52:42 -0700, Gregory Szorc wrote: On Tue, Mar 21, 2017 at 11:49 PM, Gregory Szorcwrote: # HG changeset patch # User Gregory Szorc # Date 1490165337 25200 # Tue Mar 21 23:48:57 2017 -0700 # Node ID 80ca2bee4a06887f918e3328b3f005e4c1cb1ab1 # Parent ae796e23fd42b036352b298f570af8949c2db2d9 show: new extension for displaying various repository data Yuya, et al: Since the default output isn't covered by BC guarantees, we probably want HGPLAIN to be. I'm not sure what the preferred way to do that should be. Should I create a separate topic within the template for the plain view? No idea about BC guarantees vs HGPLAIN. HGPLAIN is to disable user configuration. If "hg show" is covered by e.g. compat version, we'll only need to set it to the lowest version if HGPLAIN set. My vision for `hg show` was that we document it as having explicitly unstable output and unsuitable for scripting. Perhaps in light of that, HGPLAIN=1 should cause `hg show` to say `abort: show is only for humans, you appear to be a robot` or similar. Makes some sense, but `hg show` would support -Tjson, which is for scripting. I basically agree, see my comment in my review: > We should consider aborting if HGPLAIN=1 and no template is specified, in fact! I think that gives us behavior everyone would be fairly happy with: abort if HGPLAIN is specified without a template, would would lead to unstable output. ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: [PATCH] findrenames: sort files not by object id but by path for stable result
On 3/23/17 2:00 PM, Yuya Nishihara wrote: On Wed, 22 Mar 2017 16:14:01 +, Ryan McElroy wrote: On 3/22/17 1:48 PM, Yuya Nishihara wrote: # HG changeset patch # User Yuya Nishihara# Date 1426413536 -32400 # Sun Mar 15 18:58:56 2015 +0900 # Node ID 425379e72e817b6c79e955173b015b9a0fe090e9 # Parent 102f291807c92864a2231e5e925d6cd64783bb59 findrenames: sort files not by object id but by path for stable result It seems the original implementation tried to sort added/removed files alphabetically, but actually it did sort fctx objects by address. This patch removes the use of set()s in order to preserve the order of added/removed files. addedfiles.remove() could be slightly slower than before, but it doesn't make much difference. ...except perhaps on a large number of files added/removed, right? Why not re-sort and move the possible O(n^2) to at worst O(n log n)? It doesn't seem like it would be too much more work to me. benchmark (on tmpfs): $ hg up -C 15afda349b11; hg purge --all; mv tests tests.new $ LANG=C hg --time addremove -n > /dev/null original: real 0.420 secs (user 0.390+0.000 sys 0.030+0.000) this patch: real 0.430 secs (user 0.390+0.000 sys 0.040+0.000) I think the algorithm scales O(n^2), so I'd be much more interested in a move of, say, 100k files than a move of 1 file that is dominated by startup time. This moves "tests" directory which contains ~600 files. I'll show the number of 50k files in V2, but this quadratic list.remove() is relatively cheap in "hg addremove". Ah, of course you're right there -- sorry I missed this (facepalm). Thanks for doing a larger move to make it so I don't worry too much about the scalability though, I appreciate that. ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
A possible explanation for random "stream ended unexpectedly (got m bytes, expected n)"
Hi, I don't know about you, but occasionally, I've hit "stream ended unexpectedly (got m bytes, expected n)" errors that didn't make sense. Retrying would always work. Recently, I was trying to use signal.setitimer and a signal handler for some memory profiling on git-cinnabar, which uses mercurial as a library, and got "stream ended 4 unexpectedly (got m bytes, expected n)" *very* reproducibly. Like, with an interval timer firing every second, it took only a few seconds to hit the error during a clone. I'm pretty sure this can be reproduced with a similar setup in mercurial itself. Now, the reason this happens in this case is that, the code that fails does: def readexactly(stream, n): '''read n bytes from stream.read and abort if less was available''' s = stream.read(n) if len(s) < n: raise error.Abort(_("stream ended unexpectedly" " (got %d bytes, expected %d)") % (len(s), n)) return s ... and thanks to POSIX, interrupted reads can lead to short reads. So, you request n bytes, and get less, just because something interrupted the process. The problem then is that python doesn't let you know why you just got a short read, and you have to figure that out on your own. The same kind of problem is also true to some extent on writes. Now, the problem is that this piece of code is likely the most visible place where the issue exists, but there are many other places in the mercurial code base that are likely affected. And while the signal.setitimer case is a corner case (and I ended up using a separate thread to work around the problem ; my code wasn't interruption safe either anyways), I wonder if those random "stream ended unexpectedly (got m bytes, expected n)" errors I was getting under normal circumstances are not just a manifestation of the same underlying issue, which is that the code doesn't like interrupted reads. Disclaimer: I'm not going to work on fixing this issue, but I figured I'd let you know, in case someone wants to look into it more deeply. Cheers, Mike ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 1 of 5] hghave: use util.getfstype
# HG changeset patch # User Yuya Nishihara# Date 1490433747 -32400 # Sat Mar 25 18:22:27 2017 +0900 # Node ID 32202fca1b0a8f3dd4debb9b35daff002c454f75 # Parent c60091fa1426892552dd6c0dd4b9c49e3c3da045 hghave: use util.getfstype diff --git a/tests/hghave.py b/tests/hghave.py --- a/tests/hghave.py +++ b/tests/hghave.py @@ -348,8 +348,8 @@ def has_hardlink(): @check("hardlink-whitelisted", "hardlinks on whitelisted filesystems") def has_hardlink_whitelisted(): -from mercurial import osutil, util -fstype = getattr(osutil, 'getfstype', lambda x: None)('.') +from mercurial import util +fstype = util.getfstype('.') return fstype in util._hardlinkfswhitelist @check("rmcwd", "can remove current working directory") ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 4 of 5] statfs: rename pygetfstype to getfstype
# HG changeset patch # User Yuya Nishihara# Date 1490430251 -32400 # Sat Mar 25 17:24:11 2017 +0900 # Node ID 69a0abd9c61f0dae12d60b4f6fd4ada8288bd451 # Parent d4f6c6d3f77d40eff1eb597642b10629d31ee1a7 statfs: rename pygetfstype to getfstype There's no ambiguity now. diff --git a/mercurial/osutil.c b/mercurial/osutil.c --- a/mercurial/osutil.c +++ b/mercurial/osutil.c @@ -1079,7 +1079,7 @@ static const char *describefstype(const #if defined(HAVE_BSD_STATFS) || defined(HAVE_LINUX_STATFS) /* given a directory path, return filesystem type name (best-effort) */ -static PyObject *pygetfstype(PyObject *self, PyObject *args) +static PyObject *getfstype(PyObject *self, PyObject *args) { const char *path = NULL; struct statfs buf; @@ -1272,7 +1272,7 @@ static PyMethodDef methods[] = { "set process title (best-effort)\n"}, #endif #if defined(HAVE_BSD_STATFS) || defined(HAVE_LINUX_STATFS) - {"getfstype", (PyCFunction)pygetfstype, METH_VARARGS, + {"getfstype", (PyCFunction)getfstype, METH_VARARGS, "get filesystem type (best-effort)\n"}, #endif #endif /* ndef _WIN32 */ ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 5 of 5] statfs: make getfstype() raise OSError
# HG changeset patch # User Yuya Nishihara# Date 1490430323 -32400 # Sat Mar 25 17:25:23 2017 +0900 # Node ID 05dc34258f021523bc1de5c403f671fa4d1b9905 # Parent 69a0abd9c61f0dae12d60b4f6fd4ada8288bd451 statfs: make getfstype() raise OSError It's better for getfstype() function to not suppress an error. Callers can handle it as necessary. Now "hg debugfsinfo" will report OSError. diff --git a/mercurial/osutil.c b/mercurial/osutil.c --- a/mercurial/osutil.c +++ b/mercurial/osutil.c @@ -1090,7 +1090,7 @@ static PyObject *getfstype(PyObject *sel memset(, 0, sizeof(buf)); r = statfs(path, ); if (r != 0) - Py_RETURN_NONE; + return PyErr_SetFromErrno(PyExc_OSError); return Py_BuildValue("s", describefstype()); } #endif /* defined(HAVE_LINUX_STATFS) || defined(HAVE_BSD_STATFS) */ diff --git a/mercurial/util.py b/mercurial/util.py --- a/mercurial/util.py +++ b/mercurial/util.py @@ -1089,7 +1089,10 @@ def copyfile(src, dest, hardlink=False, if hardlink: # Hardlinks are problematic on CIFS (issue4546), do not allow hardlinks # unless we are confident that dest is on a whitelisted filesystem. -fstype = getfstype(os.path.dirname(dest)) +try: +fstype = getfstype(os.path.dirname(dest)) +except OSError: +fstype = None if fstype not in _hardlinkfswhitelist: hardlink = False if hardlink: @@ -1372,7 +1375,7 @@ def fspath(name, root): def getfstype(dirpath): '''Get the filesystem type name from a directory (best-effort) -Returns None if we are unsure, or errors like ENOENT, EPERM happen. +Returns None if we are unsure. Raises OSError on ENOENT, EPERM, etc. ''' return getattr(osutil, 'getfstype', lambda x: None)(dirpath) diff --git a/tests/hghave.py b/tests/hghave.py --- a/tests/hghave.py +++ b/tests/hghave.py @@ -349,7 +349,10 @@ def has_hardlink(): @check("hardlink-whitelisted", "hardlinks on whitelisted filesystems") def has_hardlink_whitelisted(): from mercurial import util -fstype = util.getfstype('.') +try: +fstype = util.getfstype('.') +except OSError: +return False return fstype in util._hardlinkfswhitelist @check("rmcwd", "can remove current working directory") ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 3 of 5] statfs: refactor inner function as a mapping from statfs to string
# HG changeset patch # User Yuya Nishihara# Date 1490430201 -32400 # Sat Mar 25 17:23:21 2017 +0900 # Node ID d4f6c6d3f77d40eff1eb597642b10629d31ee1a7 # Parent ca3ea70b04791670fa70eabc2531629b4d82d7a2 statfs: refactor inner function as a mapping from statfs to string The major difference between BSD and Linux is how to get a fstype string. Let's split the longest part of getfstype() as a pure function. diff --git a/mercurial/osutil.c b/mercurial/osutil.c --- a/mercurial/osutil.c +++ b/mercurial/osutil.c @@ -794,18 +794,15 @@ static PyObject *setprocname(PyObject *s } #endif /* ndef SETPROCNAME_USE_NONE */ -#if defined(HAVE_BSD_STATFS) || defined(HAVE_LINUX_STATFS) -/* given a directory path and a zero-initialized statfs buffer, return - * filesystem type name (best-effort), or NULL. */ -const char *getfstype(const char *path, struct statfs *pbuf) { - int r; - r = statfs(path, pbuf); - if (r != 0) - return NULL; #if defined(HAVE_BSD_STATFS) +static const char *describefstype(const struct statfs *pbuf) +{ /* BSD or OSX provides a f_fstypename field */ return pbuf->f_fstypename; +} #elif defined(HAVE_LINUX_STATFS) +static const char *describefstype(const struct statfs *pbuf) +{ /* Begin of Linux filesystems */ #ifdef ADFS_SUPER_MAGIC if (pbuf->f_type == ADFS_SUPER_MAGIC) @@ -1076,19 +1073,25 @@ const char *getfstype(const char *path, return "xfs"; #endif /* End of Linux filesystems */ -#endif /* def HAVE_LINUX_STATFS */ return NULL; } +#endif /* def HAVE_LINUX_STATFS */ +#if defined(HAVE_BSD_STATFS) || defined(HAVE_LINUX_STATFS) +/* given a directory path, return filesystem type name (best-effort) */ static PyObject *pygetfstype(PyObject *self, PyObject *args) { const char *path = NULL; struct statfs buf; + int r; if (!PyArg_ParseTuple(args, "s", )) return NULL; memset(, 0, sizeof(buf)); - return Py_BuildValue("s", getfstype(path, )); + r = statfs(path, ); + if (r != 0) + Py_RETURN_NONE; + return Py_BuildValue("s", describefstype()); } #endif /* defined(HAVE_LINUX_STATFS) || defined(HAVE_BSD_STATFS) */ ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 2 of 5] statfs: simplify handling of return value
# HG changeset patch # User Yuya Nishihara# Date 1490429592 -32400 # Sat Mar 25 17:13:12 2017 +0900 # Node ID ca3ea70b04791670fa70eabc2531629b4d82d7a2 # Parent 32202fca1b0a8f3dd4debb9b35daff002c454f75 statfs: simplify handling of return value Py_BuildValue() can translate NULL pointer to None. diff --git a/mercurial/osutil.c b/mercurial/osutil.c --- a/mercurial/osutil.c +++ b/mercurial/osutil.c @@ -1088,12 +1088,7 @@ static PyObject *pygetfstype(PyObject *s return NULL; memset(, 0, sizeof(buf)); - const char *type = getfstype(path, ); - if (type == NULL) - Py_RETURN_NONE; - - PyObject *result = Py_BuildValue("s", type); - return result; + return Py_BuildValue("s", getfstype(path, )); } #endif /* defined(HAVE_LINUX_STATFS) || defined(HAVE_BSD_STATFS) */ ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 7 of 7 V5] hgweb: expose a followlines UI in filerevision view (RFC)
# HG changeset patch # User Denis Laxalde# Date 1489594320 -3600 # Wed Mar 15 17:12:00 2017 +0100 # Node ID ec77aa4ff2993057b604bdffb449d2d179525a9f # Parent 2f75006a7f31c97d29fd6dd9b72fa7cc9e7ab840 # Available At https://hg.logilab.org/users/dlaxalde/hg # hg pull https://hg.logilab.org/users/dlaxalde/hg -r ec77aa4ff299 # EXP-Topic linerange-log/hgweb-filelog hgweb: expose a followlines UI in filerevision view (RFC) In filerevision view (/file//) we add some event listeners on mouse selection of elements in the block. Those listeners will capture the range of mouse-selected lines and, upon mouse release, a box inviting to follow the history of selected lines will show up. This action is advertised by a :after pseudo-element on file lines that shows up on hover and invite to "select a block of lines to follow its history". All JavaScript code lives in a new "linerangelog.js" file, sourced in filerevision template (only in "paper" style for now). This is proposal implementation, comments welcome on any aspects. diff --git a/contrib/wix/templates.wxs b/contrib/wix/templates.wxs --- a/contrib/wix/templates.wxs +++ b/contrib/wix/templates.wxs @@ -225,6 +225,7 @@ + diff --git a/mercurial/templates/paper/filerevision.tmpl b/mercurial/templates/paper/filerevision.tmpl --- a/mercurial/templates/paper/filerevision.tmpl +++ b/mercurial/templates/paper/filerevision.tmpl @@ -71,8 +71,11 @@ line wrap: on line source -{text%fileline} +{text%fileline} + + + diff --git a/mercurial/templates/static/linerangelog.js b/mercurial/templates/static/linerangelog.js new file mode 100644 --- /dev/null +++ b/mercurial/templates/static/linerangelog.js @@ -0,0 +1,83 @@ +// Copyright 2017 Logilab SA +// +// This software may be used and distributed according to the terms +// of the GNU General Public License, incorporated herein by reference. + +document.addEventListener('DOMContentLoaded', function() { +// install mouse event listeners on +var sourcelines = document.getElementsByClassName('sourcelines')[0]; +if (typeof sourcelines === 'undefined') { +return; +} +var targetUri = sourcelines.dataset.logurl; +if (typeof targetUri === 'undefined') { +return; +} +// add event listener to the whole block to have +// it available in all children +sourcelines.addEventListener('mousedown', lineSelectStart); + +//** event handler for "mousedown" */ +function lineSelectStart(e) { +var startElement = e.target; +if (startElement.tagName !== 'SPAN') { +// we may be on a +return; +} +// retarget "mouseup" to sourcelines element so that if this event +// occurs outside the we don't miss it +sourcelines.setCapture(); +// drop any prior +var previousInvite = document.getElementById('followlines'); +if (previousInvite !== null) { +previousInvite.parentNode.removeChild(previousInvite); +} + +var startId = parseInt(startElement.id.slice(1)); + +//** event handler for "mouseup" */ +function lineSelectEnd(e) { +// remove "mouseup" listener +sourcelines.removeEventListener('mouseup', lineSelectEnd); + +var endElement = e.target; +if (endElement.tagName !== 'SPAN') { +// not on , probably outside , +// we cannot compute endId +return; +} + +// compute line range (startId, endId) and insert the +// "followlines" element +var endId = parseInt(endElement.id.slice(1)); +if (endId == startId) { +return; +} +var inviteElement = endElement; +if (endId < startId) { +var tmp = endId; +endId = startId; +startId = tmp; +inviteElement = startElement; +} +var div = followlinesBox(startId, endId); +inviteElement.appendChild(div); +} + +sourcelines.addEventListener('mouseup', lineSelectEnd); + +} + +//** return a with a link for followlines action */ +function followlinesBox(startId, endId) { +var div = document.createElement('div'); +div.id = 'followlines'; +var a = document.createElement('a'); +var url = targetUri + '?patch==' + startId + ':' + endId; +a.setAttribute('href', url); +a.textContent = 'follow lines ' + startId + ':' + endId; +div.appendChild(a); +return div; +} + +}, false); diff --git a/mercurial/templates/static/style-paper.css b/mercurial/templates/static/style-paper.css --- a/mercurial/templates/static/style-paper.css +++ b/mercurial/templates/static/style-paper.css @@
[PATCH 5 of 7 V5] hgweb: add a 'linerange' parameter to webutil.diffs()
# HG changeset patch # User Denis Laxalde# Date 1489414549 -3600 # Mon Mar 13 15:15:49 2017 +0100 # Node ID 5b6a965fd0e2a1d0607672c5c6de70856e15df9f # Parent 7f68dc6319094b00f78ede75a01af62801f6a765 # Available At https://hg.logilab.org/users/dlaxalde/hg # hg pull https://hg.logilab.org/users/dlaxalde/hg -r 5b6a965fd0e2 # EXP-Topic linerange-log/hgweb-filelog hgweb: add a 'linerange' parameter to webutil.diffs() This is used to filter out hunks based on their range (with respect to 'node2' for patch.diffhunks() call, i.e. 'ctx' for webutil.diffs()). This is the simplest way to filter diff hunks, here done on server side. Later on, it might be interesting to perform this filtering on client side and expose a "toggle" action to alternate between full and filtered diff. diff --git a/mercurial/hgweb/webutil.py b/mercurial/hgweb/webutil.py --- a/mercurial/hgweb/webutil.py +++ b/mercurial/hgweb/webutil.py @@ -434,7 +434,7 @@ def listfilediffs(tmpl, files, node, max if len(files) > max: yield tmpl('fileellipses') -def diffs(web, tmpl, ctx, basectx, files, style): +def diffs(web, tmpl, ctx, basectx, files, style, linerange=None): def prettyprintlines(lines, blockno): for lineno, l in enumerate(lines, 1): @@ -470,6 +470,11 @@ def diffs(web, tmpl, ctx, basectx, files header = header[1:] lines = [h + '\n' for h in header] for hunkrange, hunklines in hunks: +if linerange is not None and hunkrange is not None: +s1, l1, s2, l2 = hunkrange +lb, ub = linerange +if not (lb <= s2 < ub or lb < s2 + l2 <= ub): +continue lines.extend(hunklines) if lines: yield tmpl('diffblock', parity=next(parity), blockno=blockno, ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 4 of 7 V5] hgweb: handle a "linerange" request parameter in filelog command
# HG changeset patch # User Denis Laxalde# Date 1484844060 -3600 # Thu Jan 19 17:41:00 2017 +0100 # Node ID 7f68dc6319094b00f78ede75a01af62801f6a765 # Parent 137ff409f34ad82bc0840e4161de1b9f502811b7 # Available At https://hg.logilab.org/users/dlaxalde/hg # hg pull https://hg.logilab.org/users/dlaxalde/hg -r 7f68dc631909 # EXP-Topic linerange-log/hgweb-filelog hgweb: handle a "linerange" request parameter in filelog command We now handle a "linerange" URL query parameter to filter filelog using a logic similar to followlines() revset. The URL syntax is: log//?linerange=: As a result, filelog entries only consists of revision changing specified line range. The linerange information is propagated to "more"/"less" navigation links but not to numeric navigation links as this would apparently require a dedicated "revnav" class. Only update the "paper" template in this patch. diff --git a/mercurial/hgweb/webcommands.py b/mercurial/hgweb/webcommands.py --- a/mercurial/hgweb/webcommands.py +++ b/mercurial/hgweb/webcommands.py @@ -28,6 +28,7 @@ from .common import ( from .. import ( archival, +context, encoding, error, graphmod, @@ -968,6 +969,8 @@ def filelog(web, req, tmpl): except ValueError: pass +lrange = webutil.linerange(req) + lessvars = copy.copy(tmpl.defaults['sessionvars']) lessvars['revcount'] = max(revcount / 2, 1) morevars = copy.copy(tmpl.defaults['sessionvars']) @@ -996,24 +999,49 @@ def filelog(web, req, tmpl): path = fctx.path() return webutil.diffs(web, tmpl, ctx, basectx, [path], diffstyle) -for i in revs: -iterfctx = fctx.filectx(i) -diffs = None -if patch: -diffs = diff(iterfctx) -entries.append(dict( -parity=next(parity), -filerev=i, -file=f, -diff=diffs, -rename=webutil.renamelink(iterfctx), -**webutil.commonentry(repo, iterfctx))) -entries.reverse() +linerange = None +if lrange is not None: +linerange = webutil.formatlinerange(*lrange) +# deactivate numeric nav links when linerange is specified as this +# would required a dedicated "revnav" class +nav = None +ancestors = context.blockancestors(fctx, *lrange) +for i, (c, lr) in enumerate(ancestors, 1): +diffs = None +if patch: +diffs = diff(c) +# follow renames accross filtered (not in range) revisions +path = c.path() +entries.append(dict( +parity=next(parity), +filerev=c.rev(), +file=path, +diff=diffs, +linerange=webutil.formatlinerange(*lr), +**webutil.commonentry(repo, c))) +if i == revcount: +break +lessvars['linerange'] = webutil.formatlinerange(*lrange) +morevars['linerange'] = lessvars['linerange'] +else: +for i in revs: +iterfctx = fctx.filectx(i) +diffs = None +if patch: +diffs = diff(iterfctx) +entries.append(dict( +parity=next(parity), +filerev=i, +file=f, +diff=diffs, +rename=webutil.renamelink(iterfctx), +**webutil.commonentry(repo, iterfctx))) +entries.reverse() +revnav = webutil.filerevnav(web.repo, fctx.path()) +nav = revnav.gen(end - 1, revcount, count) latestentry = entries[:1] -revnav = webutil.filerevnav(web.repo, fctx.path()) -nav = revnav.gen(end - 1, revcount, count) return tmpl("filelog", file=f, nav=nav, @@ -1021,6 +1049,7 @@ def filelog(web, req, tmpl): entries=entries, patch=patch, latestentry=latestentry, +linerange=linerange, revcount=revcount, morevars=morevars, lessvars=lessvars, diff --git a/mercurial/hgweb/webutil.py b/mercurial/hgweb/webutil.py --- a/mercurial/hgweb/webutil.py +++ b/mercurial/hgweb/webutil.py @@ -18,6 +18,7 @@ from ..node import hex, nullid, short from .common import ( ErrorResponse, +HTTP_BAD_REQUEST, HTTP_NOT_FOUND, paritygen, ) @@ -317,6 +318,26 @@ def filectx(repo, req): return fctx +def linerange(req): +linerange = req.form.get('linerange') +if linerange is None: +return None +if len(linerange) > 1: +raise ErrorResponse(HTTP_BAD_REQUEST, +'redundant linerange parameter') +try: +fromline, toline = map(int, linerange[0].split(':', 1)) +except ValueError: +raise ErrorResponse(HTTP_BAD_REQUEST, +'invalid linerange parameter') +try: +return
[PATCH 1 of 7 V5] hgweb: handle "parity" internally in webutil.diffs()
# HG changeset patch # User Denis Laxalde# Date 1489398019 -3600 # Mon Mar 13 10:40:19 2017 +0100 # Node ID ffd1a15d456342caba744f5049b97862fcb697c7 # Parent 527a247f114f8af37326805fd6cce923f9ac6453 # Available At https://hg.logilab.org/users/dlaxalde/hg # hg pull https://hg.logilab.org/users/dlaxalde/hg -r ffd1a15d4563 # EXP-Topic linerange-log/hgweb-filelog hgweb: handle "parity" internally in webutil.diffs() There's apparently no reason to have the "parity" of diff blocks that webutil.diffs() generates coming from outside the function. So have it internally managed. We thus now pass a "web" object to webutil.diffs() to get access to both "repo" and "stripecount" attribute. diff --git a/mercurial/hgweb/webcommands.py b/mercurial/hgweb/webcommands.py --- a/mercurial/hgweb/webcommands.py +++ b/mercurial/hgweb/webcommands.py @@ -756,12 +756,11 @@ def filediff(web, req, tmpl): ctx = fctx.changectx() basectx = ctx.p1() -parity = paritygen(web.stripecount) style = web.config('web', 'style', 'paper') if 'style' in req.form: style = req.form['style'][0] -diffs = webutil.diffs(web.repo, tmpl, ctx, basectx, [path], parity, style) +diffs = webutil.diffs(web, tmpl, ctx, basectx, [path], style) if fctx is not None: rename = webutil.renamelink(fctx) ctx = fctx diff --git a/mercurial/hgweb/webutil.py b/mercurial/hgweb/webutil.py --- a/mercurial/hgweb/webutil.py +++ b/mercurial/hgweb/webutil.py @@ -388,8 +388,7 @@ def changesetentry(web, req, tmpl, ctx): if 'style' in req.form: style = req.form['style'][0] -parity = paritygen(web.stripecount) -diff = diffs(web.repo, tmpl, ctx, basectx, None, parity, style) +diff = diffs(web, tmpl, ctx, basectx, None, style) parity = paritygen(web.stripecount) diffstatsgen = diffstatgen(ctx, basectx) @@ -414,7 +413,7 @@ def listfilediffs(tmpl, files, node, max if len(files) > max: yield tmpl('fileellipses') -def diffs(repo, tmpl, ctx, basectx, files, parity, style): +def diffs(web, tmpl, ctx, basectx, files, style): def prettyprintlines(lines, blockno): for lineno, l in enumerate(lines, 1): @@ -433,6 +432,7 @@ def diffs(repo, tmpl, ctx, basectx, file lineid="l%s" % difflineno, linenumber="% 8s" % difflineno) +repo = web.repo if files: m = match.exact(repo.root, repo.getcwd(), files) else: @@ -441,6 +441,7 @@ def diffs(repo, tmpl, ctx, basectx, file diffopts = patch.diffopts(repo.ui, untrusted=True) node1 = basectx.node() node2 = ctx.node() +parity = paritygen(web.stripecount) diffhunks = patch.diffhunks(repo, node1, node2, m, opts=diffopts) for blockno, (header, hunks) in enumerate(diffhunks, 1): ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
[PATCH 6 of 7 V5] hgweb: filter diff hunks when 'linerange' and 'patch' are specified in filelog
# HG changeset patch # User Denis Laxalde# Date 1489414640 -3600 # Mon Mar 13 15:17:20 2017 +0100 # Node ID 2f75006a7f31c97d29fd6dd9b72fa7cc9e7ab840 # Parent 5b6a965fd0e2a1d0607672c5c6de70856e15df9f # Available At https://hg.logilab.org/users/dlaxalde/hg # hg pull https://hg.logilab.org/users/dlaxalde/hg -r 2f75006a7f31 # EXP-Topic linerange-log/hgweb-filelog hgweb: filter diff hunks when 'linerange' and 'patch' are specified in filelog diff --git a/mercurial/hgweb/webcommands.py b/mercurial/hgweb/webcommands.py --- a/mercurial/hgweb/webcommands.py +++ b/mercurial/hgweb/webcommands.py @@ -993,11 +993,12 @@ def filelog(web, req, tmpl): if 'style' in req.form: diffstyle = req.form['style'][0] -def diff(fctx): +def diff(fctx, linerange=None): ctx = fctx.changectx() basectx = ctx.p1() path = fctx.path() -return webutil.diffs(web, tmpl, ctx, basectx, [path], diffstyle) +return webutil.diffs(web, tmpl, ctx, basectx, [path], diffstyle, + linerange=linerange) linerange = None if lrange is not None: @@ -1009,7 +1010,7 @@ def filelog(web, req, tmpl): for i, (c, lr) in enumerate(ancestors, 1): diffs = None if patch: -diffs = diff(c) +diffs = diff(c, linerange=lr) # follow renames accross filtered (not in range) revisions path = c.path() entries.append(dict( diff --git a/tests/test-hgweb-filelog.t b/tests/test-hgweb-filelog.t --- a/tests/test-hgweb-filelog.t +++ b/tests/test-hgweb-filelog.t @@ -1159,6 +1159,328 @@ filelog with patch +filelog with 'linerange' and 'patch' + + $ cat c + b + c + $ cat < c + > b + > c+ + > + > a + > a + > + > d + > e + > f + > EOF + $ hg ci -m 'make c bigger and touch its beginning' c + $ cat < c + > b + > c+ + > + > a + > a + > + > d + > e+ + > f + > EOF + $ hg ci -m 'just touch end of c' c + $ cat < c + > b + > c++ + > + > a + > a + > + > d + > e+ + > f + > EOF + $ hg ci -m 'touch beginning of c' c + $ cat < c + > b- + > c++ + > + > a + > a + > + > d + > e+ + > f+ + > EOF + $ hg ci -m 'touching beginning and end of c' c + $ hg log -r 'followlines(c, 1:2, startrev=tip) and follow(c)' -p + changeset: 0:6563da9dcf87 + user:test + date:Thu Jan 01 00:00:00 1970 + + summary: b + + diff -r -r 6563da9dcf87 b + --- /dev/nullThu Jan 01 00:00:00 1970 + + +++ b/b Thu Jan 01 00:00:00 1970 + + @@ -0,0 +1,1 @@ + +b + + changeset: 7:46c1a66bd8fc + branch: a-branch + user:test + date:Thu Jan 01 00:00:00 1970 + + summary: change c + + diff -r c9637d3cc8ef -r 46c1a66bd8fc c + --- a/c Thu Jan 01 00:00:00 1970 + + +++ b/c Thu Jan 01 00:00:00 1970 + + @@ -1,1 +1,2 @@ + b + +c + + changeset: 8:c40702dbfc57 + branch: a-branch + user:test + date:Thu Jan 01 00:00:00 1970 + + summary: make c bigger and touch its beginning + + diff -r 46c1a66bd8fc -r c40702dbfc57 c + --- a/c Thu Jan 01 00:00:00 1970 + + +++ b/c Thu Jan 01 00:00:00 1970 + + @@ -1,2 +1,9 @@ + b + -c + +c+ + + + +a + +a + + + +d + +e + +f + + changeset: 10:f94018eca295 + branch: a-branch + user:test + date:Thu Jan 01 00:00:00 1970 + + summary: touch beginning of c + + diff -r 07faa31d6d1c -r f94018eca295 c + --- a/c Thu Jan 01 00:00:00 1970 + + +++ b/c Thu Jan 01 00:00:00 1970 + + @@ -1,5 +1,5 @@ + b + -c+ + +c++ + + a + a + + changeset: 11:ea4193bdd9bf + branch: a-branch + tag: tip + user:test + date:Thu Jan 01 00:00:00 1970 + + summary: touching beginning and end of c + + diff -r f94018eca295 -r ea4193bdd9bf c + --- a/c Thu Jan 01 00:00:00 1970 + + +++ b/c Thu Jan 01 00:00:00 1970 + + @@ -1,4 +1,4 @@ + -b + +b- + c++ + + a + @@ -6,4 +6,4 @@ + + d + e+ + -f + +f+ + + $ (get-with-headers.py localhost:$HGPORT 'log/tip/c?linerange=1:2=') + 200 Script output follows + + http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd;> + http://www.w3.org/1999/xhtml; xml:lang="en-US"> + + + + + + + test: c history + + + + + + + + + https://mercurial-scm.org/;> + + + + log + graph + tags + bookmarks + branches + + + changeset + browse + + + file + diff + comparison + annotate + file log + raw + + + help + + + + + + + + + + Mercurial + + log c @ 11:ea4193bdd9bf + a-branch tip +(following lines 1:2 back to filelog) + + + + + + Find changesets by keywords (author, files, the commit message), revision + number or hash, or revset expression. + + + + less + more + |
[PATCH 2 of 7 V5] hgweb: add a "patch" query parameter to filelog command
# HG changeset patch # User Denis Laxalde# Date 1489398073 -3600 # Mon Mar 13 10:41:13 2017 +0100 # Node ID a7b9bc1af8b81c332cf34960f581359e6942ca17 # Parent ffd1a15d456342caba744f5049b97862fcb697c7 # Available At https://hg.logilab.org/users/dlaxalde/hg # hg pull https://hg.logilab.org/users/dlaxalde/hg -r a7b9bc1af8b8 # EXP-Topic linerange-log/hgweb-filelog hgweb: add a "patch" query parameter to filelog command Add support for a "patch" query parameter in filelog web command similar to --patch option of `hg log` to display the diff of each changeset in the table of revisions. The diff text is displayed in a dedicated row of the table that follows the existing one for each entry and spans over all columns. Only update "paper" template in this patch. diff --git a/mercurial/hgweb/webcommands.py b/mercurial/hgweb/webcommands.py --- a/mercurial/hgweb/webcommands.py +++ b/mercurial/hgweb/webcommands.py @@ -973,6 +973,10 @@ def filelog(web, req, tmpl): morevars = copy.copy(tmpl.defaults['sessionvars']) morevars['revcount'] = revcount * 2 +patch = 'patch' in req.form +if patch: +lessvars['patch'] = morevars['patch'] = req.form['patch'][0] + count = fctx.filerev() + 1 start = max(0, count - revcount) # first rev on this page end = min(count, start + revcount) # last rev on this page @@ -981,12 +985,27 @@ def filelog(web, req, tmpl): repo = web.repo revs = fctx.filelog().revs(start, end - 1) entries = [] + +diffstyle = web.config('web', 'style', 'paper') +if 'style' in req.form: +diffstyle = req.form['style'][0] + +def diff(fctx): +ctx = fctx.changectx() +basectx = ctx.p1() +path = fctx.path() +return webutil.diffs(web, tmpl, ctx, basectx, [path], diffstyle) + for i in revs: iterfctx = fctx.filectx(i) +diffs = None +if patch: +diffs = diff(iterfctx) entries.append(dict( parity=next(parity), filerev=i, file=f, +diff=diffs, rename=webutil.renamelink(iterfctx), **webutil.commonentry(repo, iterfctx))) entries.reverse() @@ -1000,6 +1019,7 @@ def filelog(web, req, tmpl): nav=nav, symrev=webutil.symrevorshortnode(req, fctx), entries=entries, +patch=patch, latestentry=latestentry, revcount=revcount, morevars=morevars, diff --git a/mercurial/templates/paper/filelogentry.tmpl b/mercurial/templates/paper/filelogentry.tmpl --- a/mercurial/templates/paper/filelogentry.tmpl +++ b/mercurial/templates/paper/filelogentry.tmpl @@ -6,3 +6,4 @@ {inbranch%changelogbranchname}{branches%changelogbranchhead}{tags%changelogtag}{bookmarks%changelogtag}{rename%filelogrename} + {if(patch, '{diff}')} diff --git a/tests/test-hgweb-filelog.t b/tests/test-hgweb-filelog.t --- a/tests/test-hgweb-filelog.t +++ b/tests/test-hgweb-filelog.t @@ -221,6 +221,7 @@ tip - two revisions a-branch + Thu, 01 Jan 1970 00:00:00 + test @@ -229,6 +230,7 @@ tip - two revisions a-tag a-bookmark + @@ -340,6 +342,7 @@ second version - two revisions a-branch + Thu, 01 Jan 1970 00:00:00 + test @@ -348,6 +351,7 @@ second version - two revisions a-tag a-bookmark + @@ -459,6 +463,7 @@ first deleted - one revision a-tag a-bookmark + @@ -570,6 +575,7 @@ first version - one revision a-tag a-bookmark + @@ -762,6 +768,135 @@ should show base link, use spartan becau +filelog with patch + + $ (get-with-headers.py localhost:$HGPORT 'log/4/a?patch=1') + 200 Script output follows + + http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd;> + http://www.w3.org/1999/xhtml; xml:lang="en-US"> + + + + + + + test: a history + + + + + + + + + https://mercurial-scm.org/;> + + + + log + graph + tags + bookmarks + branches + + + changeset + browse + + + file + diff + comparison + annotate + file log + raw + + + help + + + + + + + + + + Mercurial + + log a @ 4:3f41bc784e7e + a-branch + + + + + + Find changesets by keywords (author, files, the commit message), revision + number or hash, or revset expression. + + + + less + more + | (0) tip + + + + +age +author +description + + + + +Thu, 01 Jan 1970 00:00:00 + +test + + second a + a-branch + + + + --- /dev/null Thu Jan 01 00:00:00 1970 + + +++ b/a Thu Jan 01 00:00:00 1970 + + @@ -0,0 +1,1 @@ + +b + +Thu, 01 Jan 1970 00:00:00 + +test + + first a + a-tag a-bookmark + + + + ---
[PATCH 3 of 7 V5] revset: factor out linerange processing into a utility function
# HG changeset patch # User Denis Laxalde# Date 1487957948 -3600 # Fri Feb 24 18:39:08 2017 +0100 # Node ID 137ff409f34ad82bc0840e4161de1b9f502811b7 # Parent a7b9bc1af8b81c332cf34960f581359e6942ca17 # Available At https://hg.logilab.org/users/dlaxalde/hg # hg pull https://hg.logilab.org/users/dlaxalde/hg -r 137ff409f34a # EXP-Topic linerange-log/hgweb-filelog revset: factor out linerange processing into a utility function Similar processing will be done in hgweb.webutil in forthcoming changeset. diff --git a/mercurial/revset.py b/mercurial/revset.py --- a/mercurial/revset.py +++ b/mercurial/revset.py @@ -945,11 +945,7 @@ def followlines(repo, subset, x): lr = getrange(args['lines'][0], _("followlines expects a line range")) fromline, toline = [getinteger(a, _("line range bounds must be integers")) for a in lr] -if toline - fromline < 0: -raise error.ParseError(_("line range must be positive")) -if fromline < 1: -raise error.ParseError(_("fromline must be strictly positive")) -fromline -= 1 +fromline, toline = util.processlinerange(fromline, toline) fctx = repo[rev].filectx(fname) revs = (c.rev() for c, _linerange diff --git a/mercurial/util.py b/mercurial/util.py --- a/mercurial/util.py +++ b/mercurial/util.py @@ -2119,6 +2119,27 @@ def unitcountfn(*unittable): return go +def processlinerange(fromline, toline): +"""Check that linerange : makes sense and return a +0-based range. + +>>> processlinerange(10, 20) +(9, 20) +>>> processlinerange(2, 1) +Traceback (most recent call last): +... +ParseError: line range must be positive +>>> processlinerange(0, 5) +Traceback (most recent call last): +... +ParseError: fromline must be strictly positive +""" +if toline - fromline < 0: +raise error.ParseError(_("line range must be positive")) +if fromline < 1: +raise error.ParseError(_("fromline must be strictly positive")) +return fromline - 1, toline + bytecount = unitcountfn( (100, 1 << 30, _('%.0f GB')), (10, 1 << 30, _('%.1f GB')), ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Re: [PATCH 4 of 4 V2] obsolete: allow cycles
On 23/03/2017 22:15, Jun Wu wrote: Excerpts from Augie Fackler's message of 2017-03-23 17:53:39 -0400: On Wed, Mar 22, 2017 at 1:42 AM, Jun Wuwrote: As for alternatives, a correct solution needs to refer to the marker it is replacing. Since cycles should be rare, can we record the full "replaced marker" data inline in the new marker? In local storage, that could be an If "markers being replaced" are explicitly recorded, you will miss remote markers that can be possibly replaced because you don't know them at the time appending a new local marker. So you end up with some "conflict resolution" logic during exchange. That is not very different from just using the offsets - since obsstore is append-only, new markers just "replace" old ones (I don't think there is an exception that the newly added marker is intended to be replaced by a previous one when working locally). It's simpler but has the same exchange headache. I wonder: could we get away from using dates by putting some kind of generation number in the marker? So if a marker would create a cycle, we increment its generation number relative to the previous highest value in the cycle. The problem is you don't know if a marker will create a cycle (or should invalidate another marker), because remote markers are unknown. If you do that during exchange, it makes exchange more complex. I think that is not very different from just using the offsets. Basically, if the new filed which is meant to be spread globally, date is probably the best option. If it is not meant to be spread globally, offset is already a decent choice, without format change. But offsets brings up the exchange headache. A thought experiment, to tease out concerns here; if we replaced the use of date with hashfunc(marker) (where hashfunc is a randomly chosen crypto hash), would people still have concerns? In other words, if we take the semantics of "date" away, and treat "date" as a random number that happens to always exist, do people's concerns still hold water? -- Simon Farnsworth ___ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel