Re: [PATCH 07 of 10 shelve-ext v4] shelve: add shelve type saving and loading

2017-03-25 Thread Kostia Balytskyi
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

2017-03-25 Thread Jun Wu
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

2017-03-25 Thread Matt Harbison
# 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

2017-03-25 Thread Matt Harbison
# 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)"

2017-03-25 Thread Mike Hommey
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 Hommey  wrote:
> > > 
> > > > 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)"

2017-03-25 Thread Jun Wu
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 Hommey  wrote:
> > 
> > > 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)"

2017-03-25 Thread Mike Hommey
On Sat, Mar 25, 2017 at 10:34:02AM -0700, Gregory Szorc wrote:
> On Sat, Mar 25, 2017 at 4:19 AM, Mike Hommey  wrote:
> 
> > 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

2017-03-25 Thread Kostia Balytskyi
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

2017-03-25 Thread Kostia Balytskyi
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

2017-03-25 Thread Jun Wu
# 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

2017-03-25 Thread Jun Wu
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

2017-03-25 Thread Jun Wu
# 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)

2017-03-25 Thread Gregory Szorc
On Sat, Mar 25, 2017 at 12:34 PM, Gregory Szorc 
wrote:

> 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)

2017-03-25 Thread Gregory Szorc
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!


>
> 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

2017-03-25 Thread Jun Wu
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)"

2017-03-25 Thread Gregory Szorc
On Sat, Mar 25, 2017 at 4:19 AM, Mike Hommey  wrote:

> 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

2017-03-25 Thread Gregory Szorc
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)"

2017-03-25 Thread Gregory Szorc
On Sat, Mar 25, 2017 at 4:19 AM, Mike Hommey  wrote:

> 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

2017-03-25 Thread Denis Laxalde
# 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

2017-03-25 Thread Denis Laxalde
# 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

2017-03-25 Thread Denis Laxalde
# 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

2017-03-25 Thread Ryan McElroy



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

2017-03-25 Thread Ryan McElroy



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

2017-03-25 Thread Ryan McElroy



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 Szorc 
wrote:


# 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

2017-03-25 Thread Ryan McElroy



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)"

2017-03-25 Thread Mike Hommey
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

2017-03-25 Thread Yuya Nishihara
# 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

2017-03-25 Thread Yuya Nishihara
# 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

2017-03-25 Thread Yuya Nishihara
# 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

2017-03-25 Thread Yuya Nishihara
# 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

2017-03-25 Thread Yuya Nishihara
# 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)

2017-03-25 Thread Denis Laxalde
# 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()

2017-03-25 Thread Denis Laxalde
# 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

2017-03-25 Thread Denis Laxalde
# 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()

2017-03-25 Thread Denis Laxalde
# 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

2017-03-25 Thread Denis Laxalde
# 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

2017-03-25 Thread Denis Laxalde
# 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

2017-03-25 Thread Denis Laxalde
# 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

2017-03-25 Thread Simon Farnsworth

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 Wu  wrote:

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