changeset 24027:f2a631c7387a

merge with stable
author Matt Mackall <mpm@selenic.com>
date Tue, 03 Feb 2015 19:10:03 -0600
parents 3daef83a1873 (diff) 942a5a34b2d0 (current diff)
children a78888d98606
files
diffstat 21 files changed, 518 insertions(+), 239 deletions(-) [+]
line wrap: on
line diff
--- a/hgext/histedit.py	Mon Feb 02 14:26:47 2015 -0500
+++ b/hgext/histedit.py	Tue Feb 03 19:10:03 2015 -0600
@@ -349,7 +349,7 @@
     repo, ctx = state.repo, state.parentctx
     oldctx = repo[ha]
     if oldctx.parents()[0] == ctx:
-        ui.debug('node %s unchanged\n' % ha)
+        ui.debug('node %s unchanged\n' % ha[:12])
         return oldctx, []
     hg.update(repo, ctx.node())
     stats = applychanges(ui, repo, oldctx, opts)
@@ -361,7 +361,7 @@
     n = commit(text=oldctx.description(), user=oldctx.user(),
                date=oldctx.date(), extra=oldctx.extra())
     if n is None:
-        ui.warn(_('%s: empty changeset\n') % node.hex(ha))
+        ui.warn(_('%s: empty changeset\n') % ha[:12])
         return ctx, []
     new = repo[n]
     return new, [(oldctx.node(), (n,))]
@@ -389,10 +389,10 @@
     if stats and stats[3] > 0:
         raise error.InterventionRequired(
             _('Fix up the change and run hg histedit --continue'))
-    n = repo.commit(text='fold-temp-revision %s' % ha, user=oldctx.user(),
+    n = repo.commit(text='fold-temp-revision %s' % ha[:12], user=oldctx.user(),
                     date=oldctx.date(), extra=oldctx.extra())
     if n is None:
-        ui.warn(_('%s: empty changeset') % node.hex(ha))
+        ui.warn(_('%s: empty changeset') % ha[:12])
         return ctx, []
     return finishfold(ui, repo, ctx, oldctx, n, opts, [])
 
@@ -579,6 +579,10 @@
                     _('only one repo argument allowed with --outgoing'))
         else:
             revs.extend(freeargs)
+            if len(revs) == 0:
+                histeditdefault = ui.config('histedit', 'defaultrev')
+                if histeditdefault:
+                    revs.append(histeditdefault)
             if len(revs) != 1:
                 raise util.Abort(
                     _('histedit requires exactly one ancestor revision'))
@@ -666,7 +670,7 @@
     while state.rules:
         state.write()
         action, ha = state.rules.pop(0)
-        ui.debug('histedit: processing %s %s\n' % (action, ha))
+        ui.debug('histedit: processing %s %s\n' % (action, ha[:12]))
         actfunc = actiontable[action]
         state.parentctx, replacement_ = actfunc(ui, state, ha, opts)
         state.replacements.extend(replacement_)
@@ -736,7 +740,7 @@
     if s.modified or s.added or s.removed or s.deleted:
         # prepare the message for the commit to comes
         if action in ('f', 'fold', 'r', 'roll'):
-            message = 'fold-temp-revision %s' % currentnode
+            message = 'fold-temp-revision %s' % currentnode[:12]
         else:
             message = ctx.description()
         editopt = action in ('e', 'edit', 'm', 'mess')
@@ -822,7 +826,7 @@
     or a rule on a changeset outside of the user-given range.
     """
     parsed = []
-    expected = set(str(c) for c in ctxs)
+    expected = set(c.hex() for c in ctxs)
     seen = set()
     for r in rules:
         if ' ' not in r:
@@ -830,22 +834,24 @@
         action, rest = r.split(' ', 1)
         ha = rest.strip().split(' ', 1)[0]
         try:
-            ha = str(repo[ha])  # ensure its a short hash
+            ha = repo[ha].hex()
         except error.RepoError:
-            raise util.Abort(_('unknown changeset %s listed') % ha)
+            raise util.Abort(_('unknown changeset %s listed') % ha[:12])
         if ha not in expected:
             raise util.Abort(
                 _('may not use changesets other than the ones listed'))
         if ha in seen:
-            raise util.Abort(_('duplicated command for changeset %s') % ha)
+            raise util.Abort(_('duplicated command for changeset %s') %
+                    ha[:12])
         seen.add(ha)
         if action not in actiontable:
             raise util.Abort(_('unknown action "%s"') % action)
         parsed.append([action, ha])
     missing = sorted(expected - seen)  # sort to stabilize output
     if missing:
-        raise util.Abort(_('missing rules for changeset %s') % missing[0],
-                         hint=_('do you want to use the drop action?'))
+        raise util.Abort(_('missing rules for changeset %s') %
+                missing[0][:12],
+                hint=_('do you want to use the drop action?'))
     return parsed
 
 def processreplacement(state):
--- a/hgext/largefiles/overrides.py	Mon Feb 02 14:26:47 2015 -0500
+++ b/hgext/largefiles/overrides.py	Tue Feb 03 19:10:03 2015 -0600
@@ -559,16 +559,6 @@
         # this isn't legal, let the original function deal with it
         return orig(ui, repo, pats, opts, rename)
 
-    def makestandin(relpath):
-        path = pathutil.canonpath(repo.root, repo.getcwd(), relpath)
-        return os.path.join(repo.wjoin(lfutil.standin(path)))
-
-    fullpats = scmutil.expandpats(pats)
-    dest = fullpats[-1]
-
-    if os.path.isdir(dest):
-        if not os.path.isdir(makestandin(dest)):
-            os.makedirs(makestandin(dest))
     # This could copy both lfiles and normal files in one command,
     # but we don't want to do that. First replace their matcher to
     # only match normal files and run it, then replace it to just
@@ -595,6 +585,17 @@
     except OSError:
         return result
 
+    def makestandin(relpath):
+        path = pathutil.canonpath(repo.root, repo.getcwd(), relpath)
+        return os.path.join(repo.wjoin(lfutil.standin(path)))
+
+    fullpats = scmutil.expandpats(pats)
+    dest = fullpats[-1]
+
+    if os.path.isdir(dest):
+        if not os.path.isdir(makestandin(dest)):
+            os.makedirs(makestandin(dest))
+
     try:
         try:
             # When we call orig below it creates the standins but we don't add
--- a/hgext/largefiles/reposetup.py	Mon Feb 02 14:26:47 2015 -0500
+++ b/hgext/largefiles/reposetup.py	Tue Feb 03 19:10:03 2015 -0600
@@ -326,10 +326,10 @@
                             actualfiles.append(lf)
                             if not matcheddir:
                                 # There may still be normal files in the dir, so
-                                # make sure a directory is in the list, which
-                                # forces status to walk and call the match
-                                # function on the matcher.  Windows does NOT
-                                # require this.
+                                # make sure _a_ directory is in the list, which
+                                # forces status/dirstate to walk all files and
+                                # call the match function on the matcher, even
+                                # on case sensitive filesytems.
                                 actualfiles.append('.')
                                 matcheddir = True
                 # Nothing in dir, so readd it
--- a/mercurial/bundle2.py	Mon Feb 02 14:26:47 2015 -0500
+++ b/mercurial/bundle2.py	Tue Feb 03 19:10:03 2015 -0600
@@ -145,6 +145,7 @@
 preserve.
 """
 
+import errno
 import sys
 import util
 import struct
@@ -484,6 +485,8 @@
 
     def __init__(self, fp):
         self._fp = fp
+        self._seekable = (util.safehasattr(fp, 'seek') and
+                          util.safehasattr(fp, 'tell'))
 
     def _unpack(self, format):
         """unpack this struct format from the stream"""
@@ -494,6 +497,29 @@
         """read exactly <size> bytes from the stream"""
         return changegroup.readexactly(self._fp, size)
 
+    def seek(self, offset, whence):
+        """move the underlying file pointer"""
+        if self._seekable:
+            return self._fp.seek(offset, whence)
+        else:
+            raise NotImplementedError(_('File pointer is not seekable'))
+
+    def tell(self):
+        """return the file offset, or None if file is not seekable"""
+        if self._seekable:
+            try:
+                return self._fp.tell()
+            except IOError, e:
+                if e.errno == errno.ESPIPE:
+                    self._seekable = False
+                else:
+                    raise
+        return None
+
+    def close(self):
+        """close underlying file"""
+        if util.safehasattr(self._fp, 'close'):
+            return self._fp.close()
 
 class unbundle20(unpackermixin):
     """interpret a bundle2 stream
--- a/mercurial/bundlerepo.py	Mon Feb 02 14:26:47 2015 -0500
+++ b/mercurial/bundlerepo.py	Tue Feb 03 19:10:03 2015 -0600
@@ -177,9 +177,6 @@
     def baserevision(self, nodeorrev):
         return filelog.filelog.revision(self, nodeorrev)
 
-    def _file(self, f):
-        self._repo.file(f)
-
 class bundlepeer(localrepo.localpeer):
     def canpush(self):
         return False
--- a/mercurial/copies.py	Mon Feb 02 14:26:47 2015 -0500
+++ b/mercurial/copies.py	Tue Feb 03 19:10:03 2015 -0600
@@ -144,6 +144,15 @@
             del c[k]
     return c
 
+def _computeforwardmissing(a, b):
+    """Computes which files are in b but not a.
+    This is its own function so extensions can easily wrap this call to see what
+    files _forwardcopies is about to process.
+    """
+    missing = set(b.manifest().iterkeys())
+    missing.difference_update(a.manifest().iterkeys())
+    return missing
+
 def _forwardcopies(a, b):
     '''find {dst@b: src@a} copy mapping where a is an ancestor of b'''
 
@@ -167,9 +176,7 @@
     # we currently don't try to find where old files went, too expensive
     # this means we can miss a case like 'hg rm b; hg cp a b'
     cm = {}
-    missing = set(b.manifest().iterkeys())
-    missing.difference_update(a.manifest().iterkeys())
-
+    missing = _computeforwardmissing(a, b)
     ancestrycontext = a._repo.changelog.ancestors([b.rev()], inclusive=True)
     for f in missing:
         fctx = b[f]
@@ -208,6 +215,22 @@
         return _backwardrenames(x, y)
     return _chain(x, y, _backwardrenames(x, a), _forwardcopies(a, y))
 
+def _computenonoverlap(repo, m1, m2, ma):
+    """Computes the files exclusive to m1 and m2.
+    This is its own function so extensions can easily wrap this call to see what
+    files mergecopies is about to process.
+    """
+    u1 = _nonoverlap(m1, m2, ma)
+    u2 = _nonoverlap(m2, m1, ma)
+
+    if u1:
+        repo.ui.debug("  unmatched files in local:\n   %s\n"
+                      % "\n   ".join(u1))
+    if u2:
+        repo.ui.debug("  unmatched files in other:\n   %s\n"
+                      % "\n   ".join(u2))
+    return u1, u2
+
 def mergecopies(repo, c1, c2, ca):
     """
     Find moves and copies between context c1 and c2 that are relevant
@@ -261,15 +284,7 @@
 
     repo.ui.debug("  searching for copies back to rev %d\n" % limit)
 
-    u1 = _nonoverlap(m1, m2, ma)
-    u2 = _nonoverlap(m2, m1, ma)
-
-    if u1:
-        repo.ui.debug("  unmatched files in local:\n   %s\n"
-                      % "\n   ".join(u1))
-    if u2:
-        repo.ui.debug("  unmatched files in other:\n   %s\n"
-                      % "\n   ".join(u2))
+    u1, u2 = _computenonoverlap(repo, m1, m2, ma)
 
     for f in u1:
         checkcopies(ctx, f, m1, m2, ca, limit, diverge, copy, fullcopy)
--- a/mercurial/filelog.py	Mon Feb 02 14:26:47 2015 -0500
+++ b/mercurial/filelog.py	Tue Feb 03 19:10:03 2015 -0600
@@ -104,9 +104,6 @@
                 raise error.CensoredNodeError(self.indexfile, node)
             raise
 
-    def _file(self, f):
-        return filelog(self.opener, f)
-
     def _iscensored(self, rev):
         """Check if a file revision is censored."""
         return self.flags(rev) & revlog.REVIDX_ISCENSORED
--- a/mercurial/help/subrepos.txt	Mon Feb 02 14:26:47 2015 -0500
+++ b/mercurial/help/subrepos.txt	Tue Feb 03 19:10:03 2015 -0600
@@ -91,7 +91,7 @@
     -S/--subrepos is specified.
 
 :cat: cat currently only handles exact file matches in subrepos.
-    Git and Subversion subrepositories are currently ignored.
+    Subversion subrepositories are currently ignored.
 
 :commit: commit creates a consistent snapshot of the state of the
     entire project and its subrepositories. If any subrepositories
--- a/mercurial/hgweb/webcommands.py	Mon Feb 02 14:26:47 2015 -0500
+++ b/mercurial/hgweb/webcommands.py	Tue Feb 03 19:10:03 2015 -0600
@@ -187,7 +187,7 @@
 
         mfunc = revset.match(web.repo.ui, revdef)
         try:
-            revs = mfunc(web.repo, revset.baseset(web.repo))
+            revs = mfunc(web.repo, revset.spanset(web.repo))
             return MODE_REVSET, revs
             # ParseError: wrongly placed tokens, wrongs arguments, etc
             # RepoLookupError: no such revision, e.g. in 'revision:'
--- a/mercurial/obsolete.py	Mon Feb 02 14:26:47 2015 -0500
+++ b/mercurial/obsolete.py	Tue Feb 03 19:10:03 2015 -0600
@@ -68,7 +68,7 @@
 
 """
 import struct
-import util, base85, node
+import util, base85, node, parsers
 import phases
 from i18n import _
 
@@ -146,7 +146,7 @@
 _fm0fsize = _calcsize(_fm0fixed)
 _fm0fnodesize = _calcsize(_fm0node)
 
-def _fm0readmarkers(data, off=0):
+def _fm0readmarkers(data, off):
     # Loop on markers
     l = len(data)
     while off + _fm0fsize <= l:
@@ -285,7 +285,7 @@
 _fm1metapair = 'BB'
 _fm1metapairsize = _calcsize('BB')
 
-def _fm1readmarkers(data, off=0):
+def _fm1purereadmarkers(data, off):
     # make some global constants local for performance
     noneflag = _fm1parentnone
     sha2flag = usingsha256
@@ -301,6 +301,7 @@
     # Loop on markers
     stop = len(data) - _fm1fsize
     ufixed = util.unpacker(_fm1fixed)
+
     while off <= stop:
         # read fixed part
         o1 = off + fsize
@@ -395,6 +396,13 @@
         data.append(value)
     return ''.join(data)
 
+def _fm1readmarkers(data, off):
+    native = getattr(parsers, 'fm1readmarkers', None)
+    if not native:
+        return _fm1purereadmarkers(data, off)
+    stop = len(data) - _fm1fsize
+    return native(data, off, stop)
+
 # mapping to read/write various marker formats
 # <version> -> (decoder, encoder)
 formats = {_fm0version: (_fm0readmarkers, _fm0encodeonemarker),
--- a/mercurial/parsers.c	Mon Feb 02 14:26:47 2015 -0500
+++ b/mercurial/parsers.c	Tue Feb 03 19:10:03 2015 -0600
@@ -1676,108 +1676,6 @@
 }
 
 /*
- * Given a (possibly overlapping) set of revs, return the greatest
- * common ancestors: those with the longest path to the root.
- */
-static PyObject *index_ancestors(indexObject *self, PyObject *args)
-{
-	PyObject *ret = NULL, *gca = NULL;
-	Py_ssize_t argcount, i, len;
-	bitmask repeat = 0;
-	int revcount = 0;
-	int *revs;
-
-	argcount = PySequence_Length(args);
-	revs = malloc(argcount * sizeof(*revs));
-	if (argcount > 0 && revs == NULL)
-		return PyErr_NoMemory();
-	len = index_length(self) - 1;
-
-	for (i = 0; i < argcount; i++) {
-		static const int capacity = 24;
-		PyObject *obj = PySequence_GetItem(args, i);
-		bitmask x;
-		long val;
-
-		if (!PyInt_Check(obj)) {
-			PyErr_SetString(PyExc_TypeError,
-					"arguments must all be ints");
-			Py_DECREF(obj);
-			goto bail;
-		}
-		val = PyInt_AsLong(obj);
-		Py_DECREF(obj);
-		if (val == -1) {
-			ret = PyList_New(0);
-			goto done;
-		}
-		if (val < 0 || val >= len) {
-			PyErr_SetString(PyExc_IndexError,
-					"index out of range");
-			goto bail;
-		}
-		/* this cheesy bloom filter lets us avoid some more
-		 * expensive duplicate checks in the common set-is-disjoint
-		 * case */
-		x = 1ull << (val & 0x3f);
-		if (repeat & x) {
-			int k;
-			for (k = 0; k < revcount; k++) {
-				if (val == revs[k])
-					goto duplicate;
-			}
-		}
-		else repeat |= x;
-		if (revcount >= capacity) {
-			PyErr_Format(PyExc_OverflowError,
-				     "bitset size (%d) > capacity (%d)",
-				     revcount, capacity);
-			goto bail;
-		}
-		revs[revcount++] = (int)val;
-	duplicate:;
-	}
-
-	if (revcount == 0) {
-		ret = PyList_New(0);
-		goto done;
-	}
-	if (revcount == 1) {
-		PyObject *obj;
-		ret = PyList_New(1);
-		if (ret == NULL)
-			goto bail;
-		obj = PyInt_FromLong(revs[0]);
-		if (obj == NULL)
-			goto bail;
-		PyList_SET_ITEM(ret, 0, obj);
-		goto done;
-	}
-
-	gca = find_gca_candidates(self, revs, revcount);
-	if (gca == NULL)
-		goto bail;
-
-	if (PyList_GET_SIZE(gca) <= 1) {
-		ret = gca;
-		Py_INCREF(gca);
-	}
-	else ret = find_deepest(self, gca);
-
-done:
-	free(revs);
-	Py_XDECREF(gca);
-
-	return ret;
-
-bail:
-	free(revs);
-	Py_XDECREF(gca);
-	Py_XDECREF(ret);
-	return NULL;
-}
-
-/*
  * Given a (possibly overlapping) set of revs, return all the
  * common ancestors heads: heads(::args[0] and ::a[1] and ...)
  */
@@ -1871,6 +1769,24 @@
 }
 
 /*
+ * Given a (possibly overlapping) set of revs, return the greatest
+ * common ancestors: those with the longest path to the root.
+ */
+static PyObject *index_ancestors(indexObject *self, PyObject *args)
+{
+	PyObject *gca = index_commonancestorsheads(self, args);
+	if (gca == NULL)
+		return NULL;
+
+	if (PyList_GET_SIZE(gca) <= 1) {
+		Py_INCREF(gca);
+		return gca;
+	}
+
+	return find_deepest(self, gca);
+}
+
+/*
  * Invalidate any trie entries introduced by added revs.
  */
 static void nt_invalidate_added(indexObject *self, Py_ssize_t start)
@@ -2230,6 +2146,154 @@
 	return NULL;
 }
 
+#define BUMPED_FIX 1
+#define USING_SHA_256 2
+
+static PyObject *readshas(
+	const char *source, unsigned char num, Py_ssize_t hashwidth)
+{
+	int i;
+	PyObject *list = PyTuple_New(num);
+	if (list == NULL) {
+		return NULL;
+	}
+	for (i = 0; i < num; i++) {
+		PyObject *hash = PyString_FromStringAndSize(source, hashwidth);
+		if (hash == NULL) {
+			Py_DECREF(list);
+			return NULL;
+		}
+		PyTuple_SetItem(list, i, hash);
+		source += hashwidth;
+	}
+	return list;
+}
+
+static PyObject *fm1readmarker(const char *data, uint32_t *msize)
+{
+	const char *meta;
+
+	double mtime;
+	int16_t tz;
+	uint16_t flags;
+	unsigned char nsuccs, nparents, nmetadata;
+	Py_ssize_t hashwidth = 20;
+
+	PyObject *prec = NULL, *parents = NULL, *succs = NULL;
+	PyObject *metadata = NULL, *ret = NULL;
+	int i;
+
+	*msize = getbe32(data);
+	data += 4;
+	mtime = getbefloat64(data);
+	data += 8;
+	tz = getbeint16(data);
+	data += 2;
+	flags = getbeuint16(data);
+	data += 2;
+
+	if (flags & USING_SHA_256) {
+		hashwidth = 32;
+	}
+
+	nsuccs = (unsigned char)(*data++);
+	nparents = (unsigned char)(*data++);
+	nmetadata = (unsigned char)(*data++);
+
+	prec = PyString_FromStringAndSize(data, hashwidth);
+	data += hashwidth;
+	if (prec == NULL) {
+		goto bail;
+	}
+
+	succs = readshas(data, nsuccs, hashwidth);
+	if (succs == NULL) {
+		goto bail;
+	}
+	data += nsuccs * hashwidth;
+
+	if (nparents == 1 || nparents == 2) {
+		parents = readshas(data, nparents, hashwidth);
+		if (parents == NULL) {
+			goto bail;
+		}
+		data += nparents * hashwidth;
+	} else {
+		parents = Py_None;
+	}
+
+	meta = data + (2 * nmetadata);
+	metadata = PyTuple_New(nmetadata);
+	if (metadata == NULL) {
+		goto bail;
+	}
+	for (i = 0; i < nmetadata; i++) {
+		PyObject *tmp, *left = NULL, *right = NULL;
+		Py_ssize_t metasize = (unsigned char)(*data++);
+		left = PyString_FromStringAndSize(meta, metasize);
+		meta += metasize;
+		metasize = (unsigned char)(*data++);
+		right = PyString_FromStringAndSize(meta, metasize);
+		meta += metasize;
+		if (!left || !right) {
+			Py_XDECREF(left);
+			Py_XDECREF(right);
+			goto bail;
+		}
+		tmp = PyTuple_Pack(2, left, right);
+		Py_DECREF(left);
+		Py_DECREF(right);
+		if (!tmp) {
+			goto bail;
+		}
+		PyTuple_SetItem(metadata, i, tmp);
+	}
+	ret = Py_BuildValue("(OOHO(di)O)", prec, succs, flags,
+			    metadata, mtime, (int)tz * 60, parents);
+bail:
+	Py_XDECREF(prec);
+	Py_XDECREF(succs);
+	Py_XDECREF(metadata);
+	if (parents != Py_None)
+		Py_XDECREF(parents);
+	return ret;
+}
+
+
+static PyObject *fm1readmarkers(PyObject *self, PyObject *args) {
+	const char *data;
+	Py_ssize_t datalen, offset, stop;
+	PyObject *markers = NULL;
+
+	if (!PyArg_ParseTuple(args, "s#nn", &data, &datalen, &offset, &stop)) {
+		return NULL;
+	}
+	data += offset;
+	markers = PyList_New(0);
+	if (!markers) {
+		return NULL;
+	}
+	while (offset < stop) {
+		uint32_t msize;
+		int error;
+		PyObject *record = fm1readmarker(data, &msize);
+		if (!record) {
+			goto bail;
+		}
+		error = PyList_Append(markers, record);
+		Py_DECREF(record);
+		if (error) {
+			goto bail;
+		}
+		data += msize;
+		offset += msize;
+	}
+	return markers;
+bail:
+	Py_DECREF(markers);
+	return NULL;
+}
+
 static char parsers_doc[] = "Efficient content parsing.";
 
 PyObject *encodedir(PyObject *self, PyObject *args);
@@ -2245,6 +2309,8 @@
 	{"encodedir", encodedir, METH_VARARGS, "encodedir a path\n"},
 	{"pathencode", pathencode, METH_VARARGS, "fncache-encode a path\n"},
 	{"lowerencode", lowerencode, METH_VARARGS, "lower-encode a path\n"},
+	{"fm1readmarkers", fm1readmarkers, METH_VARARGS,
+			"parse v1 obsolete markers\n"},
 	{NULL, NULL}
 };
 
--- a/mercurial/patch.py	Mon Feb 02 14:26:47 2015 -0500
+++ b/mercurial/patch.py	Tue Feb 03 19:10:03 2015 -0600
@@ -1739,17 +1739,6 @@
 def trydiff(repo, revs, ctx1, ctx2, modified, added, removed,
             copy, getfilectx, opts, losedatafn, prefix):
 
-    def join(f):
-        return posixpath.join(prefix, f)
-
-    def addmodehdr(header, omode, nmode):
-        if omode != nmode:
-            header.append('old mode %s\n' % omode)
-            header.append('new mode %s\n' % nmode)
-
-    def addindexmeta(meta, oindex, nindex):
-        meta.append('index %s..%s\n' % (oindex, nindex))
-
     def gitindex(text):
         if not text:
             text = ""
@@ -1764,18 +1753,9 @@
         aprefix = 'a/'
         bprefix = 'b/'
 
-    def diffline(a, b, revs):
-        if opts.git:
-            line = 'diff --git %s%s %s%s\n' % (aprefix, a, bprefix, b)
-        elif not repo.ui.quiet:
-            if revs:
-                revinfo = ' '.join(["-r %s" % rev for rev in revs])
-                line = 'diff %s %s\n' % (revinfo, a)
-            else:
-                line = 'diff %s\n' % a
-        else:
-            line = ''
-        return line
+    def diffline(f, revs):
+        revinfo = ' '.join(["-r %s" % rev for rev in revs])
+        return 'diff %s %s' % (revinfo, f)
 
     date1 = util.datestr(ctx1.date())
     date2 = util.datestr(ctx2.date())
@@ -1785,99 +1765,105 @@
 
     copyto = dict([(v, k) for k, v in copy.items()])
 
-    if opts.git:
-        revs = None
-
-    modifiedset, addedset, removedset = set(modified), set(added), set(removed)
-    # Fix up modified and added, since merged-in additions appear as
+    addedset, removedset = set(added), set(removed)
+    # Fix up  added, since merged-in additions appear as
     # modifications during merges
-    for f in modifiedset.copy():
+    for f in modified:
         if f not in ctx1:
             addedset.add(f)
-            modifiedset.remove(f)
     for f in sorted(modified + added + removed):
-        to = None
-        tn = None
+        flag1 = None
+        flag2 = None
+        content1 = None
+        content2 = None
         binarydiff = False
-        header = []
+        op = None
         if f not in addedset:
-            to = getfilectx(f, ctx1).data()
+            content1 = getfilectx(f, ctx1).data()
         if f not in removedset:
-            tn = getfilectx(f, ctx2).data()
-        a, b = f, f
+            content2 = getfilectx(f, ctx2).data()
+        f1, f2 = f, f
         if opts.git or losedatafn:
             if f in addedset:
-                mode = gitmode[ctx2.flags(f)]
-                if f in copy or f in copyto:
+                flag2 = ctx2.flags(f)
+                if f in copy:
                     if opts.git:
-                        if f in copy:
-                            a = copy[f]
-                        else:
-                            a = copyto[f]
-                        omode = gitmode[ctx1.flags(a)]
-                        addmodehdr(header, omode, mode)
-                        if a in removedset and a not in gone:
+                        f1 = copy[f]
+                        flag1 = ctx1.flags(f1)
+                        if f1 in removedset and f1 not in gone:
                             op = 'rename'
-                            gone.add(a)
+                            gone.add(f1)
                         else:
                             op = 'copy'
-                        header.append('%s from %s\n' % (op, join(a)))
-                        header.append('%s to %s\n' % (op, join(f)))
-                        to = getfilectx(a, ctx1).data()
+                        content1 = getfilectx(f1, ctx1).data()
                     else:
                         losedatafn(f)
                 else:
-                    if opts.git:
-                        header.append('new file mode %s\n' % mode)
-                    elif ctx2.flags(f):
+                    if not opts.git and flag2:
                         losedatafn(f)
-                if util.binary(to) or util.binary(tn):
+                if util.binary(content1) or util.binary(content2):
                     if opts.git:
                         binarydiff = True
                     else:
                         losedatafn(f)
-                if not opts.git and not tn:
+                if not opts.git and not content2:
                     # regular diffs cannot represent new empty file
                     losedatafn(f)
             elif f in removedset:
                 if opts.git:
                     # have we already reported a copy above?
-                    if ((f in copy and copy[f] in addedset
-                         and copyto[copy[f]] == f) or
-                        (f in copyto and copyto[f] in addedset
-                         and copy[copyto[f]] == f)):
+                    if (f in copyto and copyto[f] in addedset
+                        and copy[copyto[f]] == f):
                         continue
                     else:
-                        header.append('deleted file mode %s\n' %
-                                      gitmode[ctx1.flags(f)])
-                        if util.binary(to):
+                        flag1 = ctx1.flags(f)
+                        if util.binary(content1):
                             binarydiff = True
-                elif not to or util.binary(to):
+                elif not content1 or util.binary(content1):
                     # regular diffs cannot represent empty file deletion
                     losedatafn(f)
             else:
-                oflag = ctx1.flags(f)
-                nflag = ctx2.flags(f)
-                binary = util.binary(to) or util.binary(tn)
+                flag1 = ctx1.flags(f)
+                flag2 = ctx2.flags(f)
+                binary = util.binary(content1) or util.binary(content2)
                 if opts.git:
-                    addmodehdr(header, gitmode[oflag], gitmode[nflag])
                     if binary:
                         binarydiff = True
-                elif binary or nflag != oflag:
+                elif binary or flag2 != flag1:
                     losedatafn(f)
 
-        if opts.git or revs:
-            header.insert(0, diffline(join(a), join(b), revs))
+        path1 = posixpath.join(prefix, f1)
+        path2 = posixpath.join(prefix, f2)
+        header = []
+        if opts.git:
+            header.append('diff --git %s%s %s%s' %
+                          (aprefix, path1, bprefix, path2))
+            if content1 is None: # added
+                header.append('new file mode %s' % gitmode[flag2])
+            elif content2 is None: # removed
+                header.append('deleted file mode %s' % gitmode[flag1])
+            else:  # modified/copied/renamed
+                mode1, mode2 = gitmode[flag1], gitmode[flag2]
+                if mode1 != mode2:
+                    header.append('old mode %s' % mode1)
+                    header.append('new mode %s' % mode2)
+                if op is not None:
+                    header.append('%s from %s' % (op, path1))
+                    header.append('%s to %s' % (op, path2))
+        elif revs and not repo.ui.quiet:
+            header.append(diffline(path1, revs))
+
         if binarydiff and not opts.nobinary:
-            text = mdiff.b85diff(to, tn)
+            text = mdiff.b85diff(content1, content2)
             if text and opts.git:
-                addindexmeta(header, gitindex(to), gitindex(tn))
+                header.append('index %s..%s' %
+                              (gitindex(content1), gitindex(content2)))
         else:
-            text = mdiff.unidiff(to, date1,
-                                 tn, date2,
-                                 join(a), join(b), opts=opts)
+            text = mdiff.unidiff(content1, date1,
+                                 content2, date2,
+                                 path1, path2, opts=opts)
         if header and (text or len(header) > 1):
-            yield ''.join(header)
+            yield '\n'.join(header) + '\n'
         if text:
             yield text
 
--- a/mercurial/revset.py	Mon Feb 02 14:26:47 2015 -0500
+++ b/mercurial/revset.py	Tue Feb 03 19:10:03 2015 -0600
@@ -2242,6 +2242,71 @@
     except error.ParseError, inst:
         return (decl, None, None, parseerrordetail(inst))
 
+def _parsealiasdefn(defn, args):
+    """Parse alias definition ``defn``
+
+    This function also replaces alias argument references in the
+    specified definition by ``_aliasarg(ARGNAME)``.
+
+    ``args`` is a list of alias argument names, or None if the alias
+    is declared as a symbol.
+
+    This returns "tree" as parsing result.
+
+    >>> args = ['$1', '$2', 'foo']
+    >>> print prettyformat(_parsealiasdefn('$1 or foo', args))
+    (or
+      (func
+        ('symbol', '_aliasarg')
+        ('string', '$1'))
+      (func
+        ('symbol', '_aliasarg')
+        ('string', 'foo')))
+    >>> try:
+    ...     _parsealiasdefn('$1 or $bar', args)
+    ... except error.ParseError, inst:
+    ...     print parseerrordetail(inst)
+    at 6: '$' not for alias arguments
+    >>> args = ['$1', '$10', 'foo']
+    >>> print prettyformat(_parsealiasdefn('$10 or foobar', args))
+    (or
+      (func
+        ('symbol', '_aliasarg')
+        ('string', '$10'))
+      ('symbol', 'foobar'))
+    >>> print prettyformat(_parsealiasdefn('"$1" or "foo"', args))
+    (or
+      ('string', '$1')
+      ('string', 'foo'))
+    """
+    def tokenizedefn(program, lookup=None):
+        if args:
+            argset = set(args)
+        else:
+            argset = set()
+
+        for t, value, pos in _tokenizealias(program, lookup=lookup):
+            if t == 'symbol':
+                if value in argset:
+                    # emulate tokenization of "_aliasarg('ARGNAME')":
+                    # "_aliasarg()" is an unknown symbol only used separate
+                    # alias argument placeholders from regular strings.
+                    yield ('symbol', '_aliasarg', pos)
+                    yield ('(', None, pos)
+                    yield ('string', value, pos)
+                    yield (')', None, pos)
+                    continue
+                elif value.startswith('$'):
+                    raise error.ParseError(_("'$' not for alias arguments"),
+                                           pos)
+            yield (t, value, pos)
+
+    p = parser.parser(tokenizedefn, elements)
+    tree, pos = p.parse(defn)
+    if pos != len(defn):
+        raise error.ParseError(_('invalid token'), pos)
+    return tree
+
 class revsetalias(object):
     # whether own `error` information is already shown or not.
     # this avoids showing same warning multiple times at each `findaliases`.
@@ -2259,16 +2324,8 @@
                            ' "%s": %s') % (self.name, self.error)
             return
 
-        if self.args:
-            for arg in self.args:
-                # _aliasarg() is an unknown symbol only used separate
-                # alias argument placeholders from regular strings.
-                value = value.replace(arg, '_aliasarg(%r)' % (arg,))
-
         try:
-            self.replacement, pos = parse(value)
-            if pos != len(value):
-                raise error.ParseError(_('invalid token'), pos)
+            self.replacement = _parsealiasdefn(value, self.args)
             # Check for placeholder injection
             _checkaliasarg(self.replacement, self.args)
         except error.ParseError, inst:
--- a/mercurial/subrepo.py	Mon Feb 02 14:26:47 2015 -0500
+++ b/mercurial/subrepo.py	Tue Feb 03 19:10:03 2015 -0600
@@ -1577,6 +1577,25 @@
 
 
     @annotatesubrepoerror
+    def cat(self, match, prefix, **opts):
+        rev = self._state[1]
+        if match.anypats():
+            return 1 #No support for include/exclude yet
+
+        if not match.files():
+            return 1
+
+        for f in match.files():
+            output = self._gitcommand(["show", "%s:%s" % (rev, f)])
+            fp = cmdutil.makefileobj(self._subparent, opts.get('output'),
+                                     self._ctx.node(),
+                                     pathname=os.path.join(prefix, f))
+            fp.write(output)
+            fp.close()
+        return 0
+
+
+    @annotatesubrepoerror
     def status(self, rev2, **opts):
         rev1 = self._state[1]
         if self._gitmissing() or not rev1:
--- a/mercurial/unionrepo.py	Mon Feb 02 14:26:47 2015 -0500
+++ b/mercurial/unionrepo.py	Tue Feb 03 19:10:03 2015 -0600
@@ -160,9 +160,6 @@
     def baserevdiff(self, rev1, rev2):
         return filelog.filelog.revdiff(self, rev1, rev2)
 
-    def _file(self, f):
-        self._repo.file(f)
-
 class unionpeer(localrepo.localpeer):
     def canpush(self):
         return False
--- a/mercurial/util.h	Mon Feb 02 14:26:47 2015 -0500
+++ b/mercurial/util.h	Tue Feb 03 19:10:03 2015 -0600
@@ -172,6 +172,22 @@
 		(d[3]));
 }
 
+static inline int16_t getbeint16(const char *c)
+{
+	const unsigned char *d = (const unsigned char *)c;
+
+	return ((d[0] << 8) |
+		(d[1]));
+}
+
+static inline uint16_t getbeuint16(const char *c)
+{
+	const unsigned char *d = (const unsigned char *)c;
+
+	return ((d[0] << 8) |
+		(d[1]));
+}
+
 static inline void putbe32(uint32_t x, char *c)
 {
 	c[0] = (x >> 24) & 0xff;
@@ -180,4 +196,17 @@
 	c[3] = (x) & 0xff;
 }
 
+static inline double getbefloat64(const char *c)
+{
+	const unsigned char *d = (const unsigned char *)c;
+	double ret;
+	int i;
+	uint64_t t = 0;
+	for (i = 0; i < 8; i++) {
+		t = (t<<8) + d[i];
+	}
+	memcpy(&ret, &t, sizeof(t));
+	return ret;
+}
+
 #endif /* _HG_UTIL_H_ */
--- a/tests/test-histedit-arguments.t	Mon Feb 02 14:26:47 2015 -0500
+++ b/tests/test-histedit-arguments.t	Tue Feb 03 19:10:03 2015 -0600
@@ -103,6 +103,15 @@
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg up --quiet
 
+Test config specified default
+-----------------------------
+
+  $ HGEDITOR=cat hg histedit --config "histedit.defaultrev=only(.) - ::eb57da33312f" --commands - << EOF
+  > pick c8e68270e35a 3 four
+  > pick 08d98a8350f3 4 five
+  > EOF
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
 Run on a revision not descendants of the initial parent
 --------------------------------------------------------------------
 
--- a/tests/test-histedit-drop.t	Mon Feb 02 14:26:47 2015 -0500
+++ b/tests/test-histedit-drop.t	Tue Feb 03 19:10:03 2015 -0600
@@ -96,7 +96,6 @@
 Check histedit_source
 
   $ hg log --debug --rev f518305ce889
-  invalid branchheads cache (visible): tip differs
   changeset:   4:f518305ce889c07cb5bd05522176d75590ef3324
   tag:         tip
   phase:       draft
--- a/tests/test-rename.t	Mon Feb 02 14:26:47 2015 -0500
+++ b/tests/test-rename.t	Tue Feb 03 19:10:03 2015 -0600
@@ -620,10 +620,16 @@
   $ hg rename d1/d11/a1 .hg
   abort: path contains illegal component: .hg/a1 (glob)
   [255]
+  $ hg --config extensions.largefiles= rename d1/d11/a1 .hg
+  abort: path contains illegal component: .hg/a1 (glob)
+  [255]
   $ hg status -C
   $ hg rename d1/d11/a1 ..
   abort: ../a1 not under root '$TESTTMP' (glob)
   [255]
+  $ hg --config extensions.largefiles= rename d1/d11/a1 ..
+  abort: ../a1 not under root '$TESTTMP' (glob)
+  [255]
   $ hg status -C
 
   $ mv d1/d11/a1 .hg
--- a/tests/test-revset.t	Mon Feb 02 14:26:47 2015 -0500
+++ b/tests/test-revset.t	Tue Feb 03 19:10:03 2015 -0600
@@ -1030,6 +1030,19 @@
   ('symbol', 'tip')
   warning: failed to parse the declaration of revset alias "bad name": at 4: invalid token
   9
+  $ echo 'strictreplacing($1, $10) = $10 or desc("$1")' >> .hg/hgrc
+  $ try 'strictreplacing("foo", tip)'
+  (func
+    ('symbol', 'strictreplacing')
+    (list
+      ('string', 'foo')
+      ('symbol', 'tip')))
+  (or
+    ('symbol', 'tip')
+    (func
+      ('symbol', 'desc')
+      ('string', '$1')))
+  9
 
   $ try 'd(2:5)'
   (func
--- a/tests/test-subrepo-git.t	Mon Feb 02 14:26:47 2015 -0500
+++ b/tests/test-subrepo-git.t	Tue Feb 03 19:10:03 2015 -0600
@@ -802,4 +802,52 @@
   $ hg status --subrepos
   ? s/barfoo
 
+show file at specific revision
+  $ cat > s/foobar << EOF
+  > woop    woop
+  > fooo bar
+  > EOF
+  $ hg commit --subrepos -m "updated foobar"
+  committing subrepository s
+  $ cat > s/foobar << EOF
+  > current foobar
+  > (should not be visible using hg cat)
+  > EOF
+
+  $ hg cat -r . s/foobar
+  woop    woop
+  fooo bar (no-eol)
+  $ hg cat -r "parents(.)" s/foobar > catparents
+
+  $ mkdir -p tmp/s
+
+  $ hg cat -r "parents(.)" --output tmp/%% s/foobar
+  $ diff tmp/% catparents
+
+  $ hg cat -r "parents(.)" --output tmp/%s s/foobar
+  $ diff tmp/foobar catparents
+
+  $ hg cat -r "parents(.)" --output tmp/%d/otherfoobar s/foobar
+  $ diff tmp/s/otherfoobar catparents
+
+  $ hg cat -r "parents(.)" --output tmp/%p s/foobar
+  $ diff tmp/s/foobar catparents
+
+  $ hg cat -r "parents(.)" --output tmp/%H s/foobar
+  $ diff tmp/255ee8cf690ec86e99b1e80147ea93ece117cd9d catparents
+
+  $ hg cat -r "parents(.)" --output tmp/%R s/foobar
+  $ diff tmp/10 catparents
+
+  $ hg cat -r "parents(.)" --output tmp/%h s/foobar
+  $ diff tmp/255ee8cf690e catparents
+
+  $ rm tmp/10
+  $ hg cat -r "parents(.)" --output tmp/%r s/foobar
+  $ diff tmp/10 catparents
+
+  $ mkdir tmp/tc
+  $ hg cat -r "parents(.)" --output tmp/%b/foobar s/foobar
+  $ diff tmp/tc/foobar catparents
+
   $ cd ..