D616: context: add overlayworkingcontext and overlayworkingfilectx

2017-09-10 Thread phillco (Phil Cohen)
phillco updated this revision to Diff 1708.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D616?vs=1707&id=1708

REVISION DETAIL
  https://phab.mercurial-scm.org/D616

AFFECTED FILES
  mercurial/context.py

CHANGE DETAILS

diff --git a/mercurial/context.py b/mercurial/context.py
--- a/mercurial/context.py
+++ b/mercurial/context.py
@@ -1661,6 +1661,9 @@
   listsubrepos=listsubrepos, badfn=badfn,
   icasefs=icasefs)
 
+def flushall(self):
+pass # For overlayworkingfilectx compatibility.
+
 def _filtersuspectsymlink(self, files):
 if not files or self._repo.dirstate._checklink:
 return files
@@ -1974,6 +1977,198 @@
 def setflags(self, l, x):
 self._repo.wvfs.setflags(self._path, l, x)
 
+class overlayworkingctx(workingctx):
+"""Wraps another mutable context with a write-back cache that can be 
flushed
+at a later time.
+
+self._cache[path] maps to a dict with keys: {
+'exists': bool?
+'date': date?
+'data': str?
+'flags': str?
+}
+If `exists` is True, `flags` must be non-None and 'date' is non-None. If it
+is `False`, the file was deleted.
+"""
+
+def __init__(self, repo, wrappedctx):
+super(overlayworkingctx, self).__init__(repo)
+self._repo = repo
+self._wrappedctx = wrappedctx
+self._clean()
+
+def data(self, path):
+if self.isdirty(path):
+if self._cache[path]['exists']:
+if self._cache[path]['data']:
+return self._cache[path]['data']
+else:
+# Must fallback here, too, because we only set flags.
+return self._wrappedctx[path].data()
+else:
+raise ProgrammingError("No such file or directory: %s" %
+   self._path)
+else:
+return self._wrappedctx[path].data()
+
+def filedate(self, path):
+if self.isdirty(path):
+return self._cache[path]['date']
+else:
+return self._wrappedctx[path].date()
+
+def flags(self, path):
+if self.isdirty(path):
+if self._cache[path]['exists']:
+return self._cache[path]['flags']
+else:
+raise ProgrammingError("No such file or directory: %s" %
+   self._path)
+else:
+return self._wrappedctx[path].flags()
+
+def write(self, path, data, flags=None):
+if data is None:
+raise error.ProgrammingError("data must be non-None")
+self._markdirty(path)
+self._cache[path]['exists'] = True
+self._cache[path]['data'] = data
+self._cache[path]['date'] = util.makedate()
+if flags is not None:
+self._cache[path]['flags'] = flags
+
+def setflags(self, path, l, x):
+self._markdirty(path, exists=True, date=util.makedate(),
+flags=(l and 'l' or '') + (x and 'x' or ''))
+
+def remove(self, path):
+self._markdirty(path, exists=False)
+
+def exists(self, path):
+"""exists behaves like `lexists`, but needs to follow symlinks and
+return False if they are broken.
+"""
+if self.isdirty(path):
+# If this path exists and is a symlink, "follow" it by calling
+# exists on the destination path.
+if (self._cache[path]['exists'] and
+'l' in self._cache[path]['flags']):
+return self.exists(self._cache[path]['data'])
+else:
+return self._cache[path]['exists']
+return self._wrappedctx[path].exists()
+
+def lexists(self, path):
+"""lexists returns True if the path exists"""
+if self.isdirty(path):
+return self._cache[path]['exists']
+return self._wrappedctx[path].lexists()
+
+def size(self, path):
+if self.isdirty(path):
+if self._cache[path]['exists']:
+return len(self._cache[path]['data'])
+else:
+raise ProgrammingError("No such file or directory: %s" %
+   self._path)
+return self._wrappedctx[path].size()
+
+def flushall(self):
+for path in self._writeorder:
+entry = self._cache[path]
+if entry['exists']:
+self._wrappedctx[path].clearunknown()
+if entry['data'] is not None:
+if entry['flags'] is None:
+raise error.ProgrammingError('data set but not flags')
+self._wrappedctx[path].write(
+entry['data'],
+entry['flags'])
+else:
+self._wrappedctx[path].setflags(
+'l' in e

D616: context: add overlayworkingcontext and overlayworkingfilectx

2017-09-10 Thread phillco (Phil Cohen)
phillco updated this revision to Diff 1707.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D616?vs=1706&id=1707

REVISION DETAIL
  https://phab.mercurial-scm.org/D616

AFFECTED FILES
  mercurial/context.py

CHANGE DETAILS

diff --git a/mercurial/context.py b/mercurial/context.py
--- a/mercurial/context.py
+++ b/mercurial/context.py
@@ -1661,6 +1661,9 @@
   listsubrepos=listsubrepos, badfn=badfn,
   icasefs=icasefs)
 
+def flushall(self):
+pass # For overlayworkingfilectx compatibility.
+
 def _filtersuspectsymlink(self, files):
 if not files or self._repo.dirstate._checklink:
 return files
@@ -1974,6 +1977,198 @@
 def setflags(self, l, x):
 self._repo.wvfs.setflags(self._path, l, x)
 
+class overlayworkingctx(workingctx):
+"""Wraps another mutable context with a write-back cache that can be 
flushed
+at a later time.
+
+self._cache[path] maps to a dict with keys: {
+'exists': bool?
+'date': date?
+'data': str?
+'flags': str?
+}
+If `exists` is True, `flags` must be non-None and 'date' is non-None. If it
+is `False`, the file was deleted.
+"""
+
+def __init__(self, repo, wrappedctx):
+super(overlayworkingctx, self).__init__(repo)
+self._repo = repo
+self._wrappedctx = wrappedctx
+self._clean()
+
+def data(self, path):
+if self.isdirty(path):
+if self._cache[path]['exists']:
+if self._cache[path]['data']:
+return self._cache[path]['data']
+else:
+# Must fallback here, too, because we only set flags.
+return self._wrappedctx[path].data()
+else:
+raise ProgrammingError("No such file or directory: %s" %
+   self._path)
+else:
+return self._wrappedctx[path].data()
+
+def filedate(self, path):
+if self.isdirty(path):
+return self._cache[path]['date']
+else:
+return self._wrappedctx[path].date()
+
+def flags(self, path):
+if self.isdirty(path):
+if self._cache[path]['exists']:
+return self._cache[path]['flags']
+else:
+raise ProgrammingError("No such file or directory: %s" %
+   self._path)
+else:
+return self._wrappedctx[path].flags()
+
+def write(self, path, data, flags=None):
+if data is None:
+raise error.ProgrammingError("data must be non-None")
+self._markdirty(path)
+self._cache[path]['exists'] = True
+self._cache[path]['data'] = data
+self._cache[path]['date'] = util.makedate()
+if flags is not None:
+self._cache[path]['flags'] = flags
+
+def setflags(self, path, l, x):
+self._markdirty(path, exists=True, date=util.makedate(),
+flags=(l and 'l' or '') + (x and 'x' or ''))
+
+def remove(self, path):
+self._markdirty(path)
+
+def exists(self, path):
+"""exists behaves like `lexists`, but needs to follow symlinks and
+return False if they are broken.
+"""
+if self.isdirty(path):
+# If this path exists and is a symlink, "follow" it by calling
+# exists on the destination path.
+if (self._cache[path]['exists'] and
+'l' in self._cache[path]['flags']):
+return self.exists(self._cache[path]['data'])
+else:
+return self._cache[path]['exists']
+return self._wrappedctx[path].exists()
+
+def lexists(self, path):
+"""lexists returns True if the path exists"""
+if self.isdirty(path):
+return self._cache[path]['exists']
+return self._wrappedctx[path].lexists()
+
+def size(self, path):
+if self.isdirty(path):
+if self._cache[path]['exists']:
+return len(self._cache[path]['data'])
+else:
+raise ProgrammingError("No such file or directory: %s" %
+   self._path)
+return self._wrappedctx[path].size()
+
+def flushall(self):
+for path in self._writeorder:
+entry = self._cache[path]
+if entry['exists']:
+self._wrappedctx[path].clearunknown()
+if entry['data'] is not None:
+if entry['flags'] is None:
+raise error.ProgrammingError('data set but not flags')
+self._wrappedctx[path].write(
+entry['data'],
+entry['flags'])
+else:
+self._wrappedctx[path].setflags(
+'l' in entry['flags'],

D616: context: add overlayworkingcontext and overlayworkingfilectx

2017-09-10 Thread phillco (Phil Cohen)
phillco updated this revision to Diff 1706.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D616?vs=1705&id=1706

REVISION DETAIL
  https://phab.mercurial-scm.org/D616

AFFECTED FILES
  mercurial/context.py

CHANGE DETAILS

diff --git a/mercurial/context.py b/mercurial/context.py
--- a/mercurial/context.py
+++ b/mercurial/context.py
@@ -1661,6 +1661,9 @@
   listsubrepos=listsubrepos, badfn=badfn,
   icasefs=icasefs)
 
+def flushall(self):
+pass # For overlayworkingfilectx compatibility.
+
 def _filtersuspectsymlink(self, files):
 if not files or self._repo.dirstate._checklink:
 return files
@@ -1974,6 +1977,194 @@
 def setflags(self, l, x):
 self._repo.wvfs.setflags(self._path, l, x)
 
+class overlayworkingctx(workingctx):
+"""Wraps another mutable context with a write-back cache that can be 
flushed
+at a later time.
+
+self._cache[path] maps to a dict with keys: {
+'exists': bool?
+'date': date?
+'data': str?
+'flags': str?
+}
+If `exists` is True, `flags` must be non-None and 'date' is non-None. If it
+is `False`, the file was deleted.
+"""
+
+def __init__(self, repo, wrappedctx):
+super(overlayworkingctx, self).__init__(repo)
+self._repo = repo
+self._wrappedctx = wrappedctx
+self._clean()
+
+def data(self, path):
+if self.isdirty(path):
+if self._cache[path]['exists']:
+if self._cache[path]['data']:
+return self._cache[path]['data']
+else:
+# Must fallback here, too, because we only set flags.
+return self._wrappedctx[path].data()
+else:
+raise ProgrammingError("No such file or directory: %s" %
+   self._path)
+else:
+return self._wrappedctx[path].data()
+
+def filedate(self, path):
+if self.isdirty(path):
+return self._cache[path]['date']
+else:
+return self._wrappedctx[path].date()
+
+def flags(self, path):
+if self.isdirty(path):
+if self._cache[path]['exists']:
+return self._cache[path]['flags']
+else:
+raise ProgrammingError("No such file or directory: %s" %
+   self._path)
+else:
+return self._wrappedctx[path].flags()
+
+def write(self, path, data, flags=None):
+if data is None:
+raise error.ProgrammingError("data must be non-None")
+self._markdirty(path)
+self._cache[path]['exists'] = True
+self._cache[path]['data'] = data
+self._cache[path]['date'] = util.makedate()
+if flags is not None:
+self._cache[path]['flags'] = flags
+
+def setflags(self, path, l, x):
+self._markdirty(path, exists=True, date=util.makedate(),
+flags=(l and 'l' or '') + (x and 'x' or ''))
+
+def remove(self, path):
+self._markdirty(path)
+
+def exists(self, path):
+"""exists behaves like `lexists`, but needs to follow symlinks and
+return False if they are broken.
+"""
+if self.isdirty(path):
+# If this path exists and is a symlink, "follow" it by calling
+# exists on the destination path.
+if (self._cache[path]['exists'] and
+'l' in self._cache[path]['flags']):
+return self.exists(self._cache[path]['data'])
+else:
+return self._cache[path]['exists']
+return self._wrappedctx[path].exists()
+
+def lexists(self, path):
+"""lexists returns True if the path exists"""
+if self.isdirty(path):
+return self._cache[path]['exists']
+return self._wrappedctx[path].lexists()
+
+def size(self, path):
+if self.isdirty(path):
+if self._cache[path]['exists']:
+return len(self._cache[path]['data'])
+else:
+raise ProgrammingError("No such file or directory: %s" %
+   self._path)
+return self._wrappedctx[path].size()
+
+def flushall(self):
+for path in self._writeorder:
+entry = self._cache[path]
+if entry['exists']:
+self._wrappedctx[path].clearunknown()
+if entry['data'] is not None:
+if entry['flags'] is None:
+raise error.ProgrammingError('data set but not flags')
+self._wrappedctx[path].write(
+entry['data'],
+entry['flags'])
+else:
+self._wrappedctx[path].setflags(
+'l' in entry['flags'],

D616: context: add overlayworkingcontext and overlayworkingfilectx

2017-09-10 Thread phillco (Phil Cohen)
phillco updated this revision to Diff 1705.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D616?vs=1699&id=1705

REVISION DETAIL
  https://phab.mercurial-scm.org/D616

AFFECTED FILES
  mercurial/context.py

CHANGE DETAILS

diff --git a/mercurial/context.py b/mercurial/context.py
--- a/mercurial/context.py
+++ b/mercurial/context.py
@@ -1661,6 +1661,9 @@
   listsubrepos=listsubrepos, badfn=badfn,
   icasefs=icasefs)
 
+def flushall(self):
+pass # For overlayworkingfilectx compatibility.
+
 def _filtersuspectsymlink(self, files):
 if not files or self._repo.dirstate._checklink:
 return files
@@ -1974,6 +1977,198 @@
 def setflags(self, l, x):
 self._repo.wvfs.setflags(self._path, l, x)
 
+class overlayworkingctx(workingctx):
+"""Wraps another mutable context with a write-back cache that can be 
flushed
+at a later time.
+
+self._cache[path] maps to a dict with keys: {
+'exists': bool?
+'date': date?
+'data': str?
+'flags': str?
+}
+If `exists` is True, `flags` must be non-None and 'date' is non-None. If it
+is `False`, the file was deleted.
+"""
+
+def __init__(self, repo, wrappedctx):
+super(overlayworkingctx, self).__init__(repo)
+self._repo = repo
+self._wrappedctx = wrappedctx
+self._clean()
+
+def data(self, path):
+if self.isdirty(path):
+if self._cache[path]['exists']:
+if self._cache[path]['data']:
+return self._cache[path]['data']
+else:
+# Must fallback here, too, because we only set flags.
+return self._wrappedctx[path].data()
+else:
+raise ProgrammingError("No such file or directory: %s" %
+   self._path)
+else:
+return self._wrappedctx[path].data()
+
+def filedate(self, path):
+if self.isdirty(path):
+return self._cache[path]['date']
+else:
+return self._wrappedctx[path].date()
+
+def flags(self, path):
+if self.isdirty(path):
+if self._cache[path]['exists']:
+return self._cache[path]['flags']
+else:
+raise ProgrammingError("No such file or directory: %s" %
+   self._path)
+else:
+return self._wrappedctx[path].flags()
+
+def write(self, path, data, flags=None):
+if data is None:
+raise error.ProgrammingError("data must be non-None")
+self._markdirty(path)
+self._cache[path]['exists'] = True
+self._cache[path]['data'] = data
+self._cache[path]['date'] = util.makedate()
+if flags is not None:
+self._cache[path]['flags'] = flags
+
+def setflags(self, path, l, x):
+self._markdirty(path, exists=True, date=util.makedate(),
+flags=(l and 'l' or '') + (x and 'x' or ''))
+
+def remove(self, path):
+self._markdirty(path)
+
+def exists(self, path):
+"""exists behaves like `lexists`, but needs to follow symlinks and
+return False if they are broken.
+"""
+if self.isdirty(path):
+# If this path exists and is a symlink, "follow" it by calling
+# exists on the destination path.
+if (self._cache[path]['exists'] and
+'l' in self._cache[path]['flags']):
+return self.exists(self._cache[path]['data'])
+else:
+return self._cache[path]['exists']
+return self._wrappedctx[path].exists()
+
+def lexists(self, path):
+"""lexists returns True if the path exists"""
+if self.isdirty(path):
+return self._cache[path]['exists']
+return self._wrappedctx[path].lexists()
+
+def size(self, path):
+if self.isdirty(path):
+if self._cache[path]['exists']:
+return len(self._cache[path]['data'])
+else:
+raise ProgrammingError("No such file or directory: %s" %
+   self._path)
+return self._wrappedctx[path].size()
+
+def flushall(self):
+for path in self._writeorder:
+entry = self._cache[path]
+if entry['exists']:
+self._wrappedctx[path].clearunknown()
+if entry['data'] is not None:
+if entry['flags'] is None:
+raise error.ProgrammingError('data set but not flags')
+self._wrappedctx[path].write(
+entry['data'],
+entry['flags'])
+else:
+self._wrappedctx[path].setflags(
+'l' in entry['flags'],

D674: filemerge: use arbitraryfilectx for backup files

2017-09-10 Thread phillco (Phil Cohen)
phillco created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D674

AFFECTED FILES
  mercurial/context.py
  mercurial/filemerge.py
  tests/test-dirstate-race.t

CHANGE DETAILS

diff --git a/tests/test-dirstate-race.t b/tests/test-dirstate-race.t
--- a/tests/test-dirstate-race.t
+++ b/tests/test-dirstate-race.t
@@ -80,9 +80,9 @@
 
   $ hg status --config extensions.dirstaterace=$TESTTMP/dirstaterace.py
   M d
-  M e
   ! b
   ! dir1/c
+  ! e
   $ hg debugdirstate
   n 644  2 * a (glob)
   n   0 -1 unset   b
diff --git a/mercurial/filemerge.py b/mercurial/filemerge.py
--- a/mercurial/filemerge.py
+++ b/mercurial/filemerge.py
@@ -7,7 +7,6 @@
 
 from __future__ import absolute_import
 
-import filecmp
 import os
 import re
 import tempfile
@@ -226,9 +225,9 @@
 return '\n'
 return None # unknown
 
-def _matcheol(file, origfile):
+def _matcheol(file, back):
 "Convert EOL markers in a file to match origfile"
-tostyle = _eoltype(util.readfile(origfile))
+tostyle = _eoltype(back.data()) # No repo.wread filters?
 if tostyle:
 data = util.readfile(file)
 style = _eoltype(data)
@@ -468,6 +467,12 @@
 a = _workingpath(repo, fcd)
 fd = fcd.path()
 
+# Run ``flushall()`` to make any missing folders the following wwrite
+# calls might be depending on.
+from . import context
+if isinstance(fcd, context.overlayworkingfilectx):
+fcd.ctx().flushall()
+
 util.writefile(a + ".local", fcd.decodeddata())
 repo.wwrite(fd + ".other", fco.data(), fco.flags())
 repo.wwrite(fd + ".base", fca.data(), fca.flags())
@@ -505,7 +510,9 @@
 
 args = _toolstr(ui, tool, "args", '$local $base $other')
 if "$output" in args:
-out, a = a, back # read input from backup, write to original
+# read input from backup, write to original
+out = a
+a = repo.wvfs.join(back.path())
 replace = {'local': a, 'base': b, 'other': c, 'output': out}
 args = util.interpolate(r'\$', replace, args,
 lambda s: util.shellquote(util.localpath(s)))
@@ -588,24 +595,39 @@
 def _restorebackup(fcd, back):
 # TODO: Add a workingfilectx.write(otherfilectx) path so we can use
 # util.copy here instead.
-fcd.write(util.readfile(back), fcd.flags())
+fcd.write(back.data(), fcd.flags())
 
 def _makebackup(repo, ui, fcd, premerge):
-"""Makes a backup of the local `fcd` file prior to merging.
+"""Makes and returns a filectx-like object for ``fcd``'s backup file.
 
 In addition to preserving the user's pre-existing modifications to `fcd`
 (if any), the backup is used to undo certain premerges, confirm whether a
 merge changed anything, and determine what line endings the new file should
 have.
 """
 if fcd.isabsent():
 return None
+from . import context
+back = scmutil.origpath(ui, repo, repo.wjoin(fcd.path()))
 
-a = _workingpath(repo, fcd)
-back = scmutil.origpath(ui, repo, a)
-if premerge:
-util.copyfile(a, back)
-return back
+inworkingdir = (back.startswith(repo.wvfs.base) and not
+back.startswith(repo.vfs.base))
+
+if isinstance(fcd, context.overlayworkingfilectx) and inworkingdir:
+# If the backup file is to be in the working directory, and we're
+# merging in-memory, we must redirect the backup to the memory context
+# so we don't disturb the working directory.
+relpath = back[len(repo.wvfs.base) + 1:]
+fcd.ctx()[relpath].write(fcd.data(), fcd.flags())
+return fcd.ctx()[relpath]
+else:
+# Otherwise, write to wherever the user specified the backups should 
go.
+#
+# A arbitraryfilectx is returned, so we can run the same functions on
+# the backup context regardless of where it lives.
+if premerge:
+util.copyfile(_workingpath(repo, fcd), back)
+return context.arbitraryfilectx(back)
 
 def _maketempfiles(repo, fco, fca):
 """Writes out `fco` and `fca` as temporary files, so an external merge
@@ -719,7 +741,7 @@
 return True, r, deleted
 finally:
 if not r and back is not None:
-util.unlink(back)
+back.remove()
 
 def _check(repo, r, ui, tool, fcd, files):
 fd = fcd.path()
@@ -741,7 +763,7 @@
 if not r and not checked and (_toolbool(ui, tool, "checkchanged") or
   'changed' in
   _toollist(ui, tool, "check")):
-if back is not None and filecmp.cmp(_workingpath(repo, fcd), back):
+if back is not None and not fcd.cmp(back):
 if ui.promptchoice(_(" output file %s appears unchanged\n"
  "was merge successful (yn)?"
 

D673: merge: flush any deferred writes just before recordupdates()

2017-09-10 Thread phillco (Phil Cohen)
phillco created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  ``recordupdates`` calls into the dirstate which requires the files to be
  there, so this is the last possible moment we can flush anything.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D673

AFFECTED FILES
  mercurial/merge.py

CHANGE DETAILS

diff --git a/mercurial/merge.py b/mercurial/merge.py
--- a/mercurial/merge.py
+++ b/mercurial/merge.py
@@ -1699,6 +1699,7 @@
 repo.vfs.write('updatestate', p2.hex())
 
 stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels)
+wc.flushall()
 
 if not partial:
 with repo.dirstate.parentchange():



To: phillco, #hg-reviewers
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D672: merge: add `ondisk` to merge.update and pass it in largefiles

2017-09-10 Thread phillco (Phil Cohen)
phillco created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  If set to false, parts of the merge might be performed in memory.
  
  Largefiles isn't a good candidate for in-memory merge (it uses a custom
  dirstate, matcher, and the files might not fit in memory) so have it always
  run an old-style merge.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D672

AFFECTED FILES
  hgext/largefiles/overrides.py
  mercurial/merge.py

CHANGE DETAILS

diff --git a/mercurial/merge.py b/mercurial/merge.py
--- a/mercurial/merge.py
+++ b/mercurial/merge.py
@@ -1467,7 +1467,7 @@
 
 def update(repo, node, branchmerge, force, ancestor=None,
mergeancestor=False, labels=None, matcher=None, mergeforce=False,
-   updatecheck=None):
+   updatecheck=None, ondisk=True):
 """
 Perform a merge between the working directory and the given node
 
@@ -1516,6 +1516,8 @@
 3 = abort: uncommitted changes (checked in commands.py)
 
 Return the same tuple as applyupdates().
+
+This function might run parts of the merge in memory if ``ondisk=False``.
 """
 # Avoid cycle.
 from . import sparse
diff --git a/hgext/largefiles/overrides.py b/hgext/largefiles/overrides.py
--- a/hgext/largefiles/overrides.py
+++ b/hgext/largefiles/overrides.py
@@ -1432,7 +1432,9 @@
 lfdirstate.write()
 
 oldstandins = lfutil.getstandinsstate(repo)
-
+# largefiles is not a good candidate for in-memory merge (large files,
+# custom dirstate, matcher usage) so always force an on-disk merge.
+kwargs["ondisk"] = True
 result = orig(repo, node, branchmerge, force, *args, **kwargs)
 
 newstandins = lfutil.getstandinsstate(repo)



To: phillco, #hg-reviewers
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D628: merge: flush any deferred writes before, and after, running any workers

2017-09-10 Thread phillco (Phil Cohen)
phillco updated this revision to Diff 1701.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D628?vs=1672&id=1701

REVISION DETAIL
  https://phab.mercurial-scm.org/D628

AFFECTED FILES
  mercurial/merge.py

CHANGE DETAILS

diff --git a/mercurial/merge.py b/mercurial/merge.py
--- a/mercurial/merge.py
+++ b/mercurial/merge.py
@@ -,6 +,10 @@
 
 _warnifmissingcwd(repo, cwd)
 
+# It's necessary to flush here in case we're inside a worker fork and will
+# quit after this function.
+wctx.flushall()
+
 def batchget(repo, mctx, wctx, actions):
 """apply gets to the working directory
 
@@ -1146,6 +1150,10 @@
 if i > 0:
 yield i, f
 
+# It's necessary to flush here in case we're inside a worker fork and will
+# quit after this function.
+wctx.flushall()
+
 def applyupdates(repo, actions, wctx, mctx, overwrite, labels=None):
 """apply the merge action list to the working directory
 
@@ -1214,6 +1222,10 @@
 progress(_updating, z, item=item, total=numupdates, unit=_files)
 removed = len(actions['r'])
 
+# We should flush before forking into worker processes, since those workers
+# flush when they complete, and we don't want to duplicate work.
+wctx.flushall()
+
 # get in parallel
 prog = worker.worker(repo.ui, 0.001, batchget, (repo, mctx, wctx),
  actions['g'])



To: phillco, #hg-reviewers
Cc: quark, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D628: merge: flush any deferred writes before, and after, running any workers

2017-09-10 Thread phillco (Phil Cohen)
phillco added a comment.


  (Done on both)

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D628

To: phillco, #hg-reviewers
Cc: quark, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D627: filemerge: flush if using deferred writes when running a merge tool

2017-09-10 Thread phillco (Phil Cohen)
phillco updated this revision to Diff 1700.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D627?vs=1671&id=1700

REVISION DETAIL
  https://phab.mercurial-scm.org/D627

AFFECTED FILES
  mercurial/filemerge.py

CHANGE DETAILS

diff --git a/mercurial/filemerge.py b/mercurial/filemerge.py
--- a/mercurial/filemerge.py
+++ b/mercurial/filemerge.py
@@ -666,6 +666,11 @@
 onfailure = _("merging %s failed!\n")
 precheck = None
 
+# If using deferred writes, must flush any deferred contents if running
+# an external merge tool since it has arbitrary access to the working
+# copy.
+wctx.flushall()
+
 toolconf = tool, toolpath, binary, symlink
 
 if mergetype == nomerge:



To: phillco, #hg-reviewers
Cc: quark, martinvonz, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D616: context: add overlayworkingcontext and overlayworkingfilectx

2017-09-10 Thread phillco (Phil Cohen)
phillco updated this revision to Diff 1699.
phillco marked 10 inline comments as done.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D616?vs=1592&id=1699

REVISION DETAIL
  https://phab.mercurial-scm.org/D616

AFFECTED FILES
  mercurial/context.py

CHANGE DETAILS

diff --git a/mercurial/context.py b/mercurial/context.py
--- a/mercurial/context.py
+++ b/mercurial/context.py
@@ -1661,6 +1661,9 @@
   listsubrepos=listsubrepos, badfn=badfn,
   icasefs=icasefs)
 
+def flushall(self):
+pass # For overlayworkingfilectx compatibility.
+
 def _filtersuspectsymlink(self, files):
 if not files or self._repo.dirstate._checklink:
 return files
@@ -1974,6 +1977,194 @@
 def setflags(self, l, x):
 self._repo.wvfs.setflags(self._path, l, x)
 
+class overlayworkingctx(workingctx):
+"""Wraps another mutable context with a write-back cache that can be 
flushed
+at a later time.
+
+self._cache[path] maps to a dict with keys: {
+'exists': bool?
+'date': date?
+'data': str?
+'flags': str?
+}
+If `exists` is True, `flags` must be non-None and 'date' is non-None. If it
+is `False`, the file was deleted.
+"""
+
+def __init__(self, repo, wrappedctx):
+super(overlayworkingctx, self).__init__(repo)
+self._repo = repo
+self._wrappedctx = wrappedctx
+self._clean()
+
+def data(self, path):
+if self.isdirty(path):
+if self._cache[path]['exists']:
+if self._cache[path]['data']:
+return self._cache[path]['data']
+else:
+# Must fallback here, too, because we only set flags.
+return self._wrappedctx[path].data()
+else:
+raise ProgrammingError("No such file or directory: %s" %
+   self._path)
+else:
+return self._wrappedctx[path].data()
+
+def filedate(self, path):
+if self.isdirty(path):
+return self._cache[path]['date']
+else:
+return self._wrappedctx[path].date()
+
+def flags(self, path):
+if self.isdirty(path):
+if self._cache[path]['exists']:
+return self._cache[path]['flags']
+else:
+raise ProgrammingError("No such file or directory: %s" %
+   self._path)
+else:
+return self._wrappedctx[path].flags()
+
+def write(self, path, data, flags=None):
+if data is None:
+raise error.ProgrammingError("data must be non-None")
+self._markdirty(path)
+self._cache[path]['exists'] = True
+self._cache[path]['data'] = data
+self._cache[path]['date'] = util.makedate()
+if flags is not None:
+self._cache[path]['flags'] = flags
+
+def setflags(self, path, l, x):
+self._markdirty(path, exists=True, date=util.makedate(),
+flags=(l and 'l' or '') + (x and 'x' or ''))
+
+def remove(self, path):
+self._markdirty(path)
+
+def exists(self, path):
+"""exists behaves like `lexists`, but needs to follow symlinks and
+return False if they are broken.
+"""
+if self.isdirty(path):
+# If this path exists and is a symlink, "follow" it by calling
+# exists on the destination path.
+if (self._cache[path]['exists'] and
+'l' in self._cache[path]['flags']):
+return self.exists(self._cache[path]['data'])
+else:
+return self._cache[path]['exists']
+return self._wrappedctx[path].exists()
+
+def lexists(self, path):
+"""lexists returns True if the path exists"""
+if self.isdirty(path):
+return self._cache[path]['exists']
+return self._wrappedctx[path].lexists()
+
+def size(self, path):
+if self.isdirty(path):
+if self._cache[path]['exists']:
+return len(self._cache[path]['data'])
+else:
+raise ProgrammingError("No such file or directory: %s" %
+   self._path)
+return self._wrappedctx[path].size()
+
+def flushall(self):
+for path in self._writeorder:
+entry = self._cache[path]
+if entry['exists']:
+self._wrappedctx[path].clearunknown()
+if entry['data'] is not None:
+if entry['flags'] is None:
+raise error.ProgrammingError('data set but not flags')
+self._wrappedctx[path].write(
+entry['data'],
+entry['flags'])
+else:
+self._wrappedctx[path].setflags(
+   

D670: changegroup: remove external uses of getbundler

2017-09-10 Thread durham (Durham Goode)
durham created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  Now that makestream and makechangegroup are the primary creation methods for
  changegroups, let's get rid of this rogue use of getbundler and getsubsetraw.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D670

AFFECTED FILES
  mercurial/exchange.py

CHANGE DETAILS

diff --git a/mercurial/exchange.py b/mercurial/exchange.py
--- a/mercurial/exchange.py
+++ b/mercurial/exchange.py
@@ -1595,8 +1595,8 @@
 raise ValueError(_('unsupported getbundle arguments: %s')
  % ', '.join(sorted(kwargs.keys(
 outgoing = _computeoutgoing(repo, heads, common)
-bundler = changegroup.getbundler('01', repo, bundlecaps)
-return changegroup.getsubsetraw(repo, outgoing, bundler, source)
+return changegroup.makestream(repo, outgoing, '01', source,
+  bundlecaps=bundlecaps)
 
 # bundle20 case
 b2caps = {}



To: durham, #hg-reviewers
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D668: changegroup: replace changegroup with makechangegroup

2017-09-10 Thread durham (Durham Goode)
durham created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  As part of reducing the number of changegroup creation APIs, let's replace the
  changegroup function with makechangegroup. This pushes the responsibility of
  creating the outgoing set to the caller, but that seems like a simple and
  reasonable concept for the caller to be aware of.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D668

AFFECTED FILES
  mercurial/changegroup.py
  mercurial/localrepo.py
  mercurial/wireproto.py

CHANGE DETAILS

diff --git a/mercurial/wireproto.py b/mercurial/wireproto.py
--- a/mercurial/wireproto.py
+++ b/mercurial/wireproto.py
@@ -795,7 +795,9 @@
 @wireprotocommand('changegroup', 'roots')
 def changegroup(repo, proto, roots):
 nodes = decodelist(roots)
-cg = changegroupmod.changegroup(repo, nodes, 'serve')
+outgoing = discovery.outgoing(repo, missingroots=nodes,
+  missingheads=repo.heads())
+cg = changegroupmod.makechangegroup(repo, outgoing, '01', 'serve')
 return streamres(reader=cg, v1compressible=True)
 
 @wireprotocommand('changegroupsubset', 'bases heads')
diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py
--- a/mercurial/localrepo.py
+++ b/mercurial/localrepo.py
@@ -287,7 +287,9 @@
 return self._repo.branches(nodes)
 
 def changegroup(self, basenodes, source):
-return changegroup.changegroup(self._repo, basenodes, source)
+outgoing = discovery.outgoing(self._repo, missingroots=basenodes,
+  missingheads=self._repo.heads())
+return changegroup.makechangegroup(self._repo, outgoing, '01', source)
 
 def changegroupsubset(self, bases, heads, source):
 outgoing = discovery.outgoing(self._repo, missingroots=bases,
diff --git a/mercurial/changegroup.py b/mercurial/changegroup.py
--- a/mercurial/changegroup.py
+++ b/mercurial/changegroup.py
@@ -21,7 +21,6 @@
 
 from . import (
 dagutil,
-discovery,
 error,
 mdiff,
 phases,
@@ -938,12 +937,6 @@
 return makechangegroup(repo, outgoing, version, source,
bundlecaps=bundlecaps)
 
-def changegroup(repo, basenodes, source):
-# to avoid a race we use changegroupsubset() (issue1320)
-outgoing = discovery.outgoing(repo, missingroots=basenodes,
-  missingheads=repo.heads())
-return makechangegroup(repo, outgoing, '01', source)
-
 def _addchangegroupfiles(repo, source, revmap, trp, expectedfiles, needfiles):
 revisions = 0
 files = 0



To: durham, #hg-reviewers
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D665: changegroup: replace changegroupsubset with makechangegroup

2017-09-10 Thread durham (Durham Goode)
durham created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  As part of getting rid of all the permutations of changegroup creation, let's
  remove changegroupsubset and call makechangegroup instead. This moves the
  responsibility of creating the outgoing set to the caller, but that seems 
like a
  relatively reasonable unit of functionality for the caller to have to care 
about
  (i.e. what commits should be bundled).

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D665

AFFECTED FILES
  hgext/shelve.py
  mercurial/changegroup.py
  mercurial/localrepo.py
  mercurial/wireproto.py

CHANGE DETAILS

diff --git a/mercurial/wireproto.py b/mercurial/wireproto.py
--- a/mercurial/wireproto.py
+++ b/mercurial/wireproto.py
@@ -21,6 +21,7 @@
 from . import (
 bundle2,
 changegroup as changegroupmod,
+discovery,
 encoding,
 error,
 exchange,
@@ -801,7 +802,9 @@
 def changegroupsubset(repo, proto, bases, heads):
 bases = decodelist(bases)
 heads = decodelist(heads)
-cg = changegroupmod.changegroupsubset(repo, bases, heads, 'serve')
+outgoing = discovery.outgoing(repo, missingroots=bases,
+  missingheads=heads)
+cg = changegroupmod.makechangegroup(repo, outgoing, '01', 'serve')
 return streamres(reader=cg, v1compressible=True)
 
 @wireprotocommand('debugwireargs', 'one two *')
diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py
--- a/mercurial/localrepo.py
+++ b/mercurial/localrepo.py
@@ -31,6 +31,7 @@
 context,
 dirstate,
 dirstateguard,
+discovery,
 encoding,
 error,
 exchange,
@@ -289,7 +290,9 @@
 return changegroup.changegroup(self._repo, basenodes, source)
 
 def changegroupsubset(self, bases, heads, source):
-return changegroup.changegroupsubset(self._repo, bases, heads, source)
+outgoing = discovery.outgoing(self._repo, missingroots=bases,
+  missingheads=heads)
+return changegroup.makechangegroup(self._repo, outgoing, '01', source)
 
 # End of baselegacywirecommands interface.
 
diff --git a/mercurial/changegroup.py b/mercurial/changegroup.py
--- a/mercurial/changegroup.py
+++ b/mercurial/changegroup.py
@@ -927,22 +927,6 @@
 _changegroupinfo(repo, csets, source)
 return bundler.generate(commonrevs, csets, fastpathlinkrev, source)
 
-def changegroupsubset(repo, roots, heads, source, version='01'):
-"""Compute a changegroup consisting of all the nodes that are
-descendants of any of the roots and ancestors of any of the heads.
-Return a chunkbuffer object whose read() method will return
-successive changegroup chunks.
-
-It is fairly complex as determining which filenodes and which
-manifest nodes need to be included for the changeset to be complete
-is non-trivial.
-
-Another wrinkle is doing the reverse, figuring out which changeset in
-the changegroup a particular filenode or manifestnode belongs to.
-"""
-outgoing = discovery.outgoing(repo, missingroots=roots, missingheads=heads)
-return makechangegroup(repo, outgoing, version, source)
-
 def getlocalchangegroupraw(repo, source, outgoing, bundlecaps=None,
version='01'):
 """Like getbundle, but taking a discovery.outgoing as an argument.
@@ -972,7 +956,9 @@
 
 def changegroup(repo, basenodes, source):
 # to avoid a race we use changegroupsubset() (issue1320)
-return changegroupsubset(repo, basenodes, repo.heads(), source)
+outgoing = discovery.outgoing(repo, missingroots=basenodes,
+  missingheads=repo.heads())
+return makechangegroup(repo, outgoing, '01', source)
 
 def _addchangegroupfiles(repo, source, revmap, trp, expectedfiles, needfiles):
 revisions = 0
diff --git a/hgext/shelve.py b/hgext/shelve.py
--- a/hgext/shelve.py
+++ b/hgext/shelve.py
@@ -33,6 +33,7 @@
 bundlerepo,
 changegroup,
 cmdutil,
+discovery,
 error,
 exchange,
 hg,
@@ -145,8 +146,11 @@
 btype = 'HG20'
 compression = 'BZ'
 
-cg = changegroup.changegroupsubset(self.repo, bases, [node], 'shelve',
-   version=cgversion)
+outgoing = discovery.outgoing(self.repo, missingroots=bases,
+  missingheads=[node])
+cg = changegroup.makechangegroup(self.repo, outgoing, cgversion,
+ 'shelve')
+
 bundle2.writebundle(self.ui, cg, self.fname, btype, self.vfs,
 compression=compression)
 



To: durham, #hg-reviewers
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D671: changegroup: rename getsubsetraw to makestream

2017-09-10 Thread durham (Durham Goode)
durham created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  Now that nothing uses getsubsetraw except makestream, let's move the
  functionality into the makestream. This removes the last remaining excess
  changegroup creation function, getsubsetraw.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D671

AFFECTED FILES
  mercurial/changegroup.py

CHANGE DETAILS

diff --git a/mercurial/changegroup.py b/mercurial/changegroup.py
--- a/mercurial/changegroup.py
+++ b/mercurial/changegroup.py
@@ -898,19 +898,17 @@
 for node in nodes:
 repo.ui.debug("%s\n" % hex(node))
 
-def makestream(repo, outgoing, version, source, fastpath=False,
-   bundlecaps=None):
-bundler = getbundler(version, repo, bundlecaps=bundlecaps)
-return getsubsetraw(repo, outgoing, bundler, source, fastpath=fastpath)
-
 def makechangegroup(repo, outgoing, version, source, fastpath=False,
 bundlecaps=None):
 cgstream = makestream(repo, outgoing, version, source,
   fastpath=fastpath, bundlecaps=bundlecaps)
 return getunbundler(version, util.chunkbuffer(cgstream), None,
 {'clcount': len(outgoing.missing) })
 
-def getsubsetraw(repo, outgoing, bundler, source, fastpath=False):
+def makestream(repo, outgoing, version, source, fastpath=False,
+   bundlecaps=None):
+bundler = getbundler(version, repo, bundlecaps=bundlecaps)
+
 repo = repo.unfiltered()
 commonrevs = outgoing.common
 csets = outgoing.missing



To: durham, #hg-reviewers
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D667: changegroup: delete getlocalchangegroup

2017-09-10 Thread durham (Durham Goode)
durham created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  As part of reducing the number of changegroup creation APIs, let's go ahead 
and
  delete this unused function. It appears to have been deprecated in the last
  release anyway.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D667

AFFECTED FILES
  mercurial/changegroup.py

CHANGE DETAILS

diff --git a/mercurial/changegroup.py b/mercurial/changegroup.py
--- a/mercurial/changegroup.py
+++ b/mercurial/changegroup.py
@@ -938,11 +938,6 @@
 return makechangegroup(repo, outgoing, version, source,
bundlecaps=bundlecaps)
 
-def getlocalchangegroup(repo, *args, **kwargs):
-repo.ui.deprecwarn('getlocalchangegroup is deprecated, use getchangegroup',
-   '4.3')
-return getchangegroup(repo, *args, **kwargs)
-
 def changegroup(repo, basenodes, source):
 # to avoid a race we use changegroupsubset() (issue1320)
 outgoing = discovery.outgoing(repo, missingroots=basenodes,



To: durham, #hg-reviewers
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D669: changegroup: replace getchangegroup with makechangegroup

2017-09-10 Thread durham (Durham Goode)
durham created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  As part of reducing the number of changegroup creation APIs, let's replace
  getchangegroup with calls to makechangegroup. This is mostly a drop in
  replacement, but it does change the version specifier to be required, so it's
  more obvious which callers are creating old version 1 changegroups still.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D669

AFFECTED FILES
  mercurial/bundle2.py
  mercurial/changegroup.py
  mercurial/exchange.py
  tests/test-bundle2-format.t
  tests/test-bundle2-multiple-changegroups.t
  tests/test-bundle2-remote-changegroup.t

CHANGE DETAILS

diff --git a/tests/test-bundle2-remote-changegroup.t 
b/tests/test-bundle2-remote-changegroup.t
--- a/tests/test-bundle2-remote-changegroup.t
+++ b/tests/test-bundle2-remote-changegroup.t
@@ -64,7 +64,8 @@
   > common.extend(repo.lookup(r) for r in repo.revs(_common))
   > heads = [repo.lookup(r) for r in repo.revs(heads)]
   > outgoing = discovery.outgoing(repo, common, heads)
-  > cg = changegroup.getchangegroup(repo, 'changegroup', outgoing)
+  > cg = changegroup.makechangegroup(repo, outgoing, '01',
+  >  'changegroup')
   > newpart('changegroup', cg.getchunks())
   > else:
   > raise Exception('unknown verb')
diff --git a/tests/test-bundle2-multiple-changegroups.t 
b/tests/test-bundle2-multiple-changegroups.t
--- a/tests/test-bundle2-multiple-changegroups.t
+++ b/tests/test-bundle2-multiple-changegroups.t
@@ -13,13 +13,13 @@
   > # in 'heads' as intermediate heads for the first changegroup.
   > intermediates = [repo[r].p1().node() for r in heads]
   > outgoing = discovery.outgoing(repo, common, intermediates)
-  > cg = changegroup.getchangegroup(repo, source, outgoing,
-  > bundlecaps=bundlecaps)
+  > cg = changegroup.makechangegroup(repo, outgoing, '01',
+  >  source, bundlecaps=bundlecaps)
   > bundler.newpart('output', data='changegroup1')
   > bundler.newpart('changegroup', data=cg.getchunks())
   > outgoing = discovery.outgoing(repo, common + intermediates, heads)
-  > cg = changegroup.getchangegroup(repo, source, outgoing,
-  > bundlecaps=bundlecaps)
+  > cg = changegroup.makechangegroup(repo, outgoing, '01',
+  >  source, bundlecaps=bundlecaps)
   > bundler.newpart('output', data='changegroup2')
   > bundler.newpart('changegroup', data=cg.getchunks())
   > 
diff --git a/tests/test-bundle2-format.t b/tests/test-bundle2-format.t
--- a/tests/test-bundle2-format.t
+++ b/tests/test-bundle2-format.t
@@ -114,7 +114,8 @@
   > headmissing = [c.node() for c in repo.set('heads(%ld)', revs)]
   > headcommon  = [c.node() for c in repo.set('parents(%ld) - 
%ld', revs, revs)]
   > outgoing = discovery.outgoing(repo, headcommon, headmissing)
-  > cg = changegroup.getchangegroup(repo, 'test:bundle2', 
outgoing, None)
+  > cg = changegroup.makechangegroup(repo, outgoing, '01',
+  >  'test:bundle2')
   > bundler.newpart('changegroup', data=cg.getchunks(),
   > mandatory=False)
   > 
diff --git a/mercurial/exchange.py b/mercurial/exchange.py
--- a/mercurial/exchange.py
+++ b/mercurial/exchange.py
@@ -965,8 +965,8 @@
 cg = changegroup.makechangegroup(pushop.repo, outgoing, '01', 'push',
 fastpath=True, bundlecaps=bundlecaps)
 else:
-cg = changegroup.getchangegroup(pushop.repo, 'push', outgoing,
-bundlecaps=bundlecaps)
+cg = changegroup.makechangegroup(pushop.repo, outgoing, '01',
+'push', bundlecaps=bundlecaps)
 
 # apply changegroup to remote
 # local repo finds heads on server, finds out what
diff --git a/mercurial/changegroup.py b/mercurial/changegroup.py
--- a/mercurial/changegroup.py
+++ b/mercurial/changegroup.py
@@ -926,17 +926,6 @@
 _changegroupinfo(repo, csets, source)
 return bundler.generate(commonrevs, csets, fastpathlinkrev, source)
 
-def getchangegroup(repo, source, outgoing, bundlecaps=None,
-   version='01'):
-"""Like getbundle, but taking a discovery.outgoing as an argument.
-
-This is only implemented for local repos and reuses potentially
-precomputed sets in outgoing."""
-if not outgoing.missing:
-return None
-return makechangegroup(repo, outgoing, version, source,
-   bundlecaps=bundlecaps)
-
 def _addchangegroupfiles(repo, source, revmap, trp, expectedfiles, needfiles):
 revisions = 0
 f

D664: changegroup: replace getsubset with makechangegroup

2017-09-10 Thread durham (Durham Goode)
durham created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  The current changegroup APIs are a bit of a mess. Currently you can use
  getsubsetraw, getsubset, changegroupsubset, getlocalchangegroupraw,
  getchangegroup, and getlocalchangroup to produce changegroups. This patch is 
the
  beginning of a refactor to boil all of that away to just makechangegroup and
  makestream.
  
  The first step adds the new functions and replaces getsubset function with 
them.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D664

AFFECTED FILES
  mercurial/changegroup.py
  mercurial/exchange.py

CHANGE DETAILS

diff --git a/mercurial/exchange.py b/mercurial/exchange.py
--- a/mercurial/exchange.py
+++ b/mercurial/exchange.py
@@ -963,12 +963,8 @@
 or pushop.repo.changelog.filteredrevs):
 # push everything,
 # use the fast path, no race possible on push
-bundler = changegroup.cg1packer(pushop.repo, bundlecaps)
-cg = changegroup.getsubset(pushop.repo,
-   outgoing,
-   bundler,
-   'push',
-   fastpath=True)
+cg = changegroup.makechangegroup(pushop.repo, outgoing, '01', 'push',
+fastpath=True, bundlecaps=bundlecaps)
 else:
 cg = changegroup.getchangegroup(pushop.repo, 'push', outgoing,
 bundlecaps=bundlecaps)
diff --git a/mercurial/changegroup.py b/mercurial/changegroup.py
--- a/mercurial/changegroup.py
+++ b/mercurial/changegroup.py
@@ -899,6 +899,18 @@
 for node in nodes:
 repo.ui.debug("%s\n" % hex(node))
 
+def makestream(repo, outgoing, version, source, fastpath=False,
+   bundlecaps=None):
+bundler = getbundler(version, repo, bundlecaps=bundlecaps)
+return getsubsetraw(repo, outgoing, bundler, source, fastpath=fastpath)
+
+def makechangegroup(repo, outgoing, version, source, fastpath=False,
+bundlecaps=None):
+cgstream = makestream(repo, outgoing, version, source,
+  fastpath=fastpath, bundlecaps=bundlecaps)
+return getunbundler(version, util.chunkbuffer(cgstream), None,
+{'clcount': len(outgoing.missing) })
+
 def getsubsetraw(repo, outgoing, bundler, source, fastpath=False):
 repo = repo.unfiltered()
 commonrevs = outgoing.common
@@ -915,11 +927,6 @@
 _changegroupinfo(repo, csets, source)
 return bundler.generate(commonrevs, csets, fastpathlinkrev, source)
 
-def getsubset(repo, outgoing, bundler, source, fastpath=False):
-gengroup = getsubsetraw(repo, outgoing, bundler, source, fastpath)
-return getunbundler(bundler.version, util.chunkbuffer(gengroup), None,
-{'clcount': len(outgoing.missing)})
-
 def changegroupsubset(repo, roots, heads, source, version='01'):
 """Compute a changegroup consisting of all the nodes that are
 descendants of any of the roots and ancestors of any of the heads.
@@ -934,8 +941,7 @@
 the changegroup a particular filenode or manifestnode belongs to.
 """
 outgoing = discovery.outgoing(repo, missingroots=roots, missingheads=heads)
-bundler = getbundler(version, repo)
-return getsubset(repo, outgoing, bundler, source)
+return makechangegroup(repo, outgoing, version, source)
 
 def getlocalchangegroupraw(repo, source, outgoing, bundlecaps=None,
version='01'):
@@ -956,8 +962,8 @@
 precomputed sets in outgoing."""
 if not outgoing.missing:
 return None
-bundler = getbundler(version, repo, bundlecaps)
-return getsubset(repo, outgoing, bundler, source)
+return makechangegroup(repo, outgoing, version, source,
+   bundlecaps=bundlecaps)
 
 def getlocalchangegroup(repo, *args, **kwargs):
 repo.ui.deprecwarn('getlocalchangegroup is deprecated, use getchangegroup',



To: durham, #hg-reviewers
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D666: changegroup: replace getlocalchangegroupraw with makestream

2017-09-10 Thread durham (Durham Goode)
durham created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  As part of reducing the number of changegroup creation apis, let's replace 
calls
  to getlocalchangegroupraw with calls to makestream. Aside from one case of
  checking if there are no outgoing commits and returning None, this is pretty
  much a drop in replacement.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D666

AFFECTED FILES
  mercurial/changegroup.py
  mercurial/exchange.py

CHANGE DETAILS

diff --git a/mercurial/exchange.py b/mercurial/exchange.py
--- a/mercurial/exchange.py
+++ b/mercurial/exchange.py
@@ -755,10 +755,9 @@
 if not cgversions:
 raise ValueError(_('no common changegroup version'))
 version = max(cgversions)
-cg = changegroup.getlocalchangegroupraw(pushop.repo, 'push',
-pushop.outgoing,
-version=version)
-cgpart = bundler.newpart('changegroup', data=cg)
+cgstream = changegroup.makestream(pushop.repo, pushop.outgoing, version,
+  'push')
+cgpart = bundler.newpart('changegroup', data=cgstream)
 if cgversions:
 cgpart.addparam('version', version)
 if 'treemanifest' in pushop.repo.requirements:
@@ -1621,7 +1620,7 @@
 def _getbundlechangegrouppart(bundler, repo, source, bundlecaps=None,
   b2caps=None, heads=None, common=None, **kwargs):
 """add a changegroup part to the requested bundle"""
-cg = None
+cgstream = None
 if kwargs.get('cg', True):
 # build changegroup bundle here.
 version = '01'
@@ -1633,12 +1632,11 @@
 raise ValueError(_('no common changegroup version'))
 version = max(cgversions)
 outgoing = _computeoutgoing(repo, heads, common)
-cg = changegroup.getlocalchangegroupraw(repo, source, outgoing,
-bundlecaps=bundlecaps,
-version=version)
+cgstream = changegroup.makestream(repo, outgoing, version, source,
+  bundlecaps=bundlecaps)
 
-if cg:
-part = bundler.newpart('changegroup', data=cg)
+if cgstream:
+part = bundler.newpart('changegroup', data=cgstream)
 if cgversions:
 part.addparam('version', version)
 part.addparam('nbchanges', str(len(outgoing.missing)), mandatory=False)
diff --git a/mercurial/changegroup.py b/mercurial/changegroup.py
--- a/mercurial/changegroup.py
+++ b/mercurial/changegroup.py
@@ -927,17 +927,6 @@
 _changegroupinfo(repo, csets, source)
 return bundler.generate(commonrevs, csets, fastpathlinkrev, source)
 
-def getlocalchangegroupraw(repo, source, outgoing, bundlecaps=None,
-   version='01'):
-"""Like getbundle, but taking a discovery.outgoing as an argument.
-
-This is only implemented for local repos and reuses potentially
-precomputed sets in outgoing. Returns a raw changegroup generator."""
-if not outgoing.missing:
-return None
-bundler = getbundler(version, repo, bundlecaps)
-return getsubsetraw(repo, outgoing, bundler, source)
-
 def getchangegroup(repo, source, outgoing, bundlecaps=None,
version='01'):
 """Like getbundle, but taking a discovery.outgoing as an argument.



To: durham, #hg-reviewers
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D663: dirstate: perform transactions with _map using single call, where possible

2017-09-10 Thread phillco (Phil Cohen)
phillco accepted this revision.
phillco added a comment.


  These look good to me.

INLINE COMMENTS

> dirstate.py:585-587
>  # if there is a merge going on and the file was either
>  # in state 'm' (-1) or coming from other parent (-2) before
>  # being removed, restore that state.

Possibly move this comment to just above line 590?

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D663

To: mbolin, #hg-reviewers, phillco
Cc: phillco, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


Re: [PATCH] extensions: fix wrapcommand/function of class instance

2017-09-10 Thread Jun Wu
LGTM. Thanks!

Excerpts from Yuya Nishihara's message of 2017-09-10 23:47:02 +0900:
> # HG changeset patch
> # User Yuya Nishihara 
> # Date 1505054234 -32400
> #  Sun Sep 10 23:37:14 2017 +0900
> # Node ID 283ecec363f4901ec8948dbdc7dba564296d8582
> # Parent  a763c891f36e55f4869f443c220227d1da747d18
> extensions: fix wrapcommand/function of class instance
> 
> 5361771f9714 changed _updatewrapper() to copy the __name__ attribute, but
> not all callable objects has __name__.
> 
> Spotted by loading mq with extdiff.
> 
> diff --git a/mercurial/extensions.py b/mercurial/extensions.py
> --- a/mercurial/extensions.py
> +++ b/mercurial/extensions.py
> @@ -333,7 +333,10 @@ def bind(func, *args):
>  
>  def _updatewrapper(wrap, origfn, unboundwrapper):
>  '''Copy and add some useful attributes to wrapper'''
> -wrap.__name__ = origfn.__name__
> +try:
> +wrap.__name__ = origfn.__name__
> +except AttributeError:
> +pass
>  wrap.__module__ = getattr(origfn, '__module__')
>  wrap.__doc__ = getattr(origfn, '__doc__')
>  wrap.__dict__.update(getattr(origfn, '__dict__', {}))
> diff --git a/tests/test-extensions-wrapfunction.py 
> b/tests/test-extensions-wrapfunction.py
> --- a/tests/test-extensions-wrapfunction.py
> +++ b/tests/test-extensions-wrapfunction.py
> @@ -54,3 +54,11 @@ with wrap1:
>  print('context manager', dummy.getstack())
>  print('context manager', dummy.getstack())
>  print('context manager', dummy.getstack())
> +
> +# Wrap callable object which has no __name__
> +class callableobj(object):
> +def __call__(self):
> +return ['orig']
> +dummy.cobj = callableobj()
> +extensions.wrapfunction(dummy, 'cobj', wrappers[0])
> +print('wrap callable object', dummy.cobj())
> diff --git a/tests/test-extensions-wrapfunction.py.out 
> b/tests/test-extensions-wrapfunction.py.out
> --- a/tests/test-extensions-wrapfunction.py.out
> +++ b/tests/test-extensions-wrapfunction.py.out
> @@ -18,3 +18,4 @@ context manager [0, 1, 'orig']
>  context manager [2, 0, 1, 'orig']
>  context manager [2, 1, 'orig']
>  context manager [2, 'orig']
> +wrap callable object [0, 'orig']
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


[PATCH 1 of 2 stable] mq: test coverage of how [diff] configuration influence can break mq patches

2017-09-10 Thread Mads Kiilerich
# HG changeset patch
# User Mads Kiilerich 
# Date 1505083342 -7200
#  Mon Sep 11 00:42:22 2017 +0200
# Branch stable
# Node ID e05e50fbdeaf7eb52a2936e1dfe98643d68c334e
# Parent  3c3066367d72344935aabf9606a5b40e9950b5e7
mq: test coverage of how [diff] configuration influence can break mq patches

diff --git a/tests/test-mq-git.t b/tests/test-mq-git.t
--- a/tests/test-mq-git.t
+++ b/tests/test-mq-git.t
@@ -208,5 +208,40 @@ git=no: regular patch after qrefresh wit
   @@ -0,0 +1,1 @@
   +a
 
+Test how [diff] configuration influence and cause invalid or lossy patches:
+
+  $ cat <> .hg/hgrc
+  > [mq]
+  > git = AUTO
+  > [diff]
+  > nobinary = True
+  > noprefix = True
+  > showfunc = True
+  > ignorews = True
+  > ignorewsamount = True
+  > ignoreblanklines = True
+  > unified = 1
+  > EOF
+
+  $ echo ' a' > a
+  $ hg qnew prepare -d '0 0'
+  $ echo '  a' > a
+  $ printf '\0' > b
+  $ echo >> c
+  $ hg qnew diff -d '0 0'
+
+  $ cat .hg/patches/prepare
+  # HG changeset patch
+  # Date 0 0
+  # Parent  cf0bfe72686a47d8d7d7b4529a3adb8b0b449a9f
+  
+  $ cat .hg/patches/diff
+  # HG changeset patch
+  # Date 0 0
+  # Parent  fb9c4422b0f37dd576522dd9a3f99b825c177efe
+  
+  diff --git b b
+  Binary file b has changed
+
   $ cd ..
 
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


[PATCH 2 of 2 stable] mq: create non-lossy patches, also with custom global diff configuration

2017-09-10 Thread Mads Kiilerich
# HG changeset patch
# User Mads Kiilerich 
# Date 1505083344 -7200
#  Mon Sep 11 00:42:24 2017 +0200
# Branch stable
# Node ID 987a85c42b08ab2a82cce39b004e00b708320d0e
# Parent  e05e50fbdeaf7eb52a2936e1dfe98643d68c334e
mq: create non-lossy patches, also with custom global diff configuration

Users with custom [diff] configuration most certainly didn't intend it to make
mq lose changes. It could:

 * git is handled perfectly fine.

 * nobinary could make mq leave some files out from the patches.

 * noprefix could make mq itself (and probably also other tools) fail to apply
   patches without the usual a/b prefix.

 * ignorews, ignorewsamount, or ignoreblanklines could create patches with
   missing whitespace that could fail to apply correctly.

Thus, when refreshing patches, use patch.difffeatureopts, optionally with git
as before, but without the config options for whitespace and format changing
that most likely will cause loss or problems.

(patch.diffopts is just patch.difffeatureopts with all options enabled and can
be replaced with that.)

diff --git a/hgext/mq.py b/hgext/mq.py
--- a/hgext/mq.py
+++ b/hgext/mq.py
@@ -503,8 +503,11 @@ class queue(object):
 self.guardsdirty = False
 self.activeguards = None
 
-def diffopts(self, opts=None, patchfn=None):
-diffopts = patchmod.diffopts(self.ui, opts)
+def diffopts(self, opts=None, patchfn=None, plain=False):
+"""Return diff options tweaked for this mq use, possibly upgrading to
+git format, and possibly plain and without lossy options."""
+diffopts = patchmod.difffeatureopts(self.ui, opts,
+git=True, whitespace=not plain, formatchanging=not plain)
 if self.gitmode == 'auto':
 diffopts.upgrade = True
 elif self.gitmode == 'keep':
@@ -1177,7 +1180,7 @@ class queue(object):
 date = opts.get('date')
 if date:
 date = util.parsedate(date)
-diffopts = self.diffopts({'git': opts.get('git')})
+diffopts = self.diffopts({'git': opts.get('git')}, plain=True)
 if opts.get('checkname', True):
 self.checkpatchname(patchfn)
 inclsubs = checksubstate(repo)
@@ -1642,7 +1645,8 @@ class queue(object):
 substatestate = repo.dirstate['.hgsubstate']
 
 ph = patchheader(self.join(patchfn), self.plainmode)
-diffopts = self.diffopts({'git': opts.get('git')}, patchfn)
+diffopts = self.diffopts({'git': opts.get('git')}, patchfn,
+ plain=True)
 if newuser:
 ph.setuser(newuser)
 if newdate:
diff --git a/tests/test-mq-git.t b/tests/test-mq-git.t
--- a/tests/test-mq-git.t
+++ b/tests/test-mq-git.t
@@ -235,13 +235,35 @@ Test how [diff] configuration influence 
   # Date 0 0
   # Parent  cf0bfe72686a47d8d7d7b4529a3adb8b0b449a9f
   
+  diff -r cf0bfe72686a -r fb9c4422b0f3 a
+  --- a/a
+  +++ b/a
+  @@ -1,1 +1,1 @@
+  -a
+  + a
   $ cat .hg/patches/diff
   # HG changeset patch
   # Date 0 0
   # Parent  fb9c4422b0f37dd576522dd9a3f99b825c177efe
   
-  diff --git b b
-  Binary file b has changed
+  diff --git a/a b/a
+  --- a/a
+  +++ b/a
+  @@ -1,1 +1,1 @@
+  - a
+  +  a
+  diff --git a/b b/b
+  index 
78981922613b2afb6025042ff6bd878ac1994e85..f76dd238ade08917e6712764a16a22005a50573d
+  GIT binary patch
+  literal 1
+  Ic${MZ000310RR91
+  
+  diff --git a/c b/c
+  --- a/c
+  +++ b/c
+  @@ -1,1 +1,2 @@
+   a
+  +
 
   $ cd ..
 
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D544: releasenotes: update docstrings with information on additional flags

2017-09-10 Thread rishabhmadan96 (Rishabh Madan)
rishabhmadan96 updated this revision to Diff 1690.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D544?vs=1368&id=1690

REVISION DETAIL
  https://phab.mercurial-scm.org/D544

AFFECTED FILES
  hgext/releasenotes.py

CHANGE DETAILS

diff --git a/hgext/releasenotes.py b/hgext/releasenotes.py
--- a/hgext/releasenotes.py
+++ b/hgext/releasenotes.py
@@ -550,6 +550,33 @@
 this command and changes should not be lost when running this command on
 that file. A particular use case for this is to tweak the wording of a
 release note after it has been added to the release notes file.
+
+The -c/--check option checks the commit message for invalid admonitions.
+In case of an invalid admonition, the flag returns the name of admonition
+along with the changeset ID. For example::
+
+   .. abcd::
+
+  First paragraph under this admonition
+
+For the above commit message, using `hg releasenotes -r . --check`
+returns: Invalid admonition 'abcd' present in changeset 3ea92981e103
+
+If an invalid admonition is similar to the available admonitions upto
+a certain threshold, the correct admonition will be suggested.
+For example::
+
+   .. fixes::
+
+  Fixes issue1234
+
+For the above commit message, using `hg releasenotes -r . --check`
+returns: Invalid admonition 'fixes' present in changeset 687be3ff87c6
+(did you mean fix?)
+
+The -l/--list option, presents the user with a list of existing available
+admonitions along with their title. This also includes the custom
+admonitions (if any).
 """
 sections = releasenotessections(ui, repo)
 if opts.get('list'):



To: rishabhmadan96, #hg-reviewers
Cc: dsp, durin42, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D544: releasenotes: update docstrings with information on additional flags

2017-09-10 Thread rishabhmadan96 (Rishabh Madan)
rishabhmadan96 added inline comments.

INLINE COMMENTS

> dsp wrote in releasenotes.py:554
> I think we usually don't address "you" directly. Maybe something like
> 
>   The -c/--check option checks the commit message for invalid admonitions. In 
> case of an invalid admonition, the flag returns the name of admonition along 
> with the changeset ID. For example::

I referred the patchbomb.py file while writing this part, and it made use of 
'you' and 'your'.
But I guess it's okay to have it without usage of 'you'. So I'll make the 
changes here.

> dsp wrote in releasenotes.py:563
> I think the colon at the end is invalid.

My bad! I'll correct this one.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D544

To: rishabhmadan96, #hg-reviewers
Cc: dsp, durin42, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


[PATCH evolve-ext STABLE] topic: check availability of obsutil.getmarkers() for portability

2017-09-10 Thread FUJIWARA Katsunori
# HG changeset patch
# User FUJIWARA Katsunori 
# Date 1505049726 -32400
#  Sun Sep 10 22:22:06 2017 +0900
# Branch stable
# Node ID 12654c26c4ed79e83661c92473d3f42bb4de5134
# Parent  0d16c89aa18584443d08fd07c587d4cb14677c4a
# Available At https://fo...@bitbucket.org/foozy/hgext-evolve
#  hg pull https://fo...@bitbucket.org/foozy/hgext-evolve -r 
12654c26c4ed
# EXP-Topic topic-hg-version-portability
topic: check availability of obsutil.getmarkers() for portability

Before this patch, topic extension causes unintentional failure with
Mercurial earlier than 4.3, because obsutil.getmarkers() has been
available since Mercurial 4.3 (tests for topic on mercurial-4.*
branches fail, too).

This breaks "minimumhgversion = '4.0'" declaration of topic extension.

This patch fixes this issue in a straightforward way for simplicity on
stable branch.

I'm planning to centralize such portability logic in topic extension
into topic/compat.py or so on default branch for efficiency at
runtime, like as evolve/compat.py.

diff --git a/hgext3rd/topic/__init__.py b/hgext3rd/topic/__init__.py
--- a/hgext3rd/topic/__init__.py
+++ b/hgext3rd/topic/__init__.py
@@ -69,7 +69,6 @@ from mercurial import (
 namespaces,
 node,
 obsolete,
-obsutil,
 patch,
 phases,
 registrar,
@@ -555,6 +554,16 @@ def _showlasttouched(repo, fm, opts):
 fm.plain('\n')
 fm.end()
 
+getmarkers = None
+try:
+from mercurial import obsutil
+getmarkers = getattr(obsutil, 'getmarkers', None)
+except ImportError:
+pass
+
+if getmarkers is None:
+getmarkers = obsolete.getmarkers
+
 def _getlasttouched(repo, topics):
 """
 Calculates the last time a topic was used. Returns a dictionary of seconds
@@ -576,7 +585,7 @@ def _getlasttouched(repo, topics):
 maxtime = rt
 # looking on the markers also to get more information and accurate
 # last touch time.
-obsmarkers = obsutil.getmarkers(repo, [repo[revs].node()])
+obsmarkers = getmarkers(repo, [repo[revs].node()])
 for marker in obsmarkers:
 rt = marker.date()
 if rt[0] > maxtime[0]:
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


[PATCH] extensions: fix wrapcommand/function of class instance

2017-09-10 Thread Yuya Nishihara
# HG changeset patch
# User Yuya Nishihara 
# Date 1505054234 -32400
#  Sun Sep 10 23:37:14 2017 +0900
# Node ID 283ecec363f4901ec8948dbdc7dba564296d8582
# Parent  a763c891f36e55f4869f443c220227d1da747d18
extensions: fix wrapcommand/function of class instance

5361771f9714 changed _updatewrapper() to copy the __name__ attribute, but
not all callable objects has __name__.

Spotted by loading mq with extdiff.

diff --git a/mercurial/extensions.py b/mercurial/extensions.py
--- a/mercurial/extensions.py
+++ b/mercurial/extensions.py
@@ -333,7 +333,10 @@ def bind(func, *args):
 
 def _updatewrapper(wrap, origfn, unboundwrapper):
 '''Copy and add some useful attributes to wrapper'''
-wrap.__name__ = origfn.__name__
+try:
+wrap.__name__ = origfn.__name__
+except AttributeError:
+pass
 wrap.__module__ = getattr(origfn, '__module__')
 wrap.__doc__ = getattr(origfn, '__doc__')
 wrap.__dict__.update(getattr(origfn, '__dict__', {}))
diff --git a/tests/test-extensions-wrapfunction.py 
b/tests/test-extensions-wrapfunction.py
--- a/tests/test-extensions-wrapfunction.py
+++ b/tests/test-extensions-wrapfunction.py
@@ -54,3 +54,11 @@ with wrap1:
 print('context manager', dummy.getstack())
 print('context manager', dummy.getstack())
 print('context manager', dummy.getstack())
+
+# Wrap callable object which has no __name__
+class callableobj(object):
+def __call__(self):
+return ['orig']
+dummy.cobj = callableobj()
+extensions.wrapfunction(dummy, 'cobj', wrappers[0])
+print('wrap callable object', dummy.cobj())
diff --git a/tests/test-extensions-wrapfunction.py.out 
b/tests/test-extensions-wrapfunction.py.out
--- a/tests/test-extensions-wrapfunction.py.out
+++ b/tests/test-extensions-wrapfunction.py.out
@@ -18,3 +18,4 @@ context manager [0, 1, 'orig']
 context manager [2, 0, 1, 'orig']
 context manager [2, 1, 'orig']
 context manager [2, 'orig']
+wrap callable object [0, 'orig']
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel