changeset 11247:fd516380732a

Merge with i18n
author Matt Mackall <mpm@selenic.com>
date Mon, 31 May 2010 12:57:34 -0500
parents 258c98567aff (diff) d83ca89577da (current diff)
children 102d8eb5c890
files
diffstat 20 files changed, 374 insertions(+), 39 deletions(-) [+]
line wrap: on
line diff
--- a/doc/hgrc.5.txt	Tue May 25 13:24:49 2010 -0300
+++ b/doc/hgrc.5.txt	Mon May 31 12:57:34 2010 -0500
@@ -90,7 +90,8 @@
 ------
 
 A configuration file consists of sections, led by a ``[section]`` header
-and followed by ``name = value`` entries::
+and followed by ``name = value`` entries (sometimes called
+``configuration keys``)::
 
     [spam]
     eggs=ham
@@ -102,10 +103,55 @@
 removed from values. Empty lines are skipped. Lines beginning with
 ``#`` or ``;`` are ignored and may be used to provide comments.
 
+Configuration keys can be set multiple times, in which case mercurial
+will use the value that was configured last. As an example::
+
+    [spam]
+    eggs=large
+    ham=serrano
+    eggs=small
+
+This would set the configuration key named ``eggs`` to ``small``.
+
+It is also possible to define a section multiple times. A section can
+be redefined on the same and/or on different hgrc files. For example::
+
+    [foo]
+    eggs=large
+    ham=serrano
+    eggs=small
+
+    [bar]
+    eggs=ham
+    green=
+       eggs
+
+    [foo]
+    ham=prosciutto
+    eggs=medium
+    bread=toasted
+
+This would set the ``eggs``, ``ham``, and ``bread`` configuration keys
+of the ``foo`` section to ``medium``, ``prosciutto``, and ``toasted``,
+respectively. As you can see there only thing that matters is the last
+value that was set for each of the configuration keys.
+
+If a configuration key is set multiple times in different
+configuration files the final value will depend on the order in which
+the different configuration files are read, with settings from earlier
+paths overriding later ones as described on the ``Files`` section
+above.
+
 A line of the form ``%include file`` will include ``file`` into the
 current configuration file. The inclusion is recursive, which means
 that included files can include other files. Filenames are relative to
 the configuration file in which the ``%include`` directive is found.
+Environment variables and ``~user`` constructs are expanded in
+``file``. This lets you do something like::
+
+  %include ~/.hgrc.d/$HOST.rc
+
+to include a different configuration file on each computer you use.
 
 A line with ``%unset name`` will remove ``name`` from the current
 section, if it has been set previously.
@@ -853,7 +899,8 @@
     <fred@example.com>``. Default is ``$EMAIL`` or ``username@hostname``. If
     the username in hgrc is empty, it has to be specified manually or
     in a different hgrc file (e.g. ``$HOME/.hgrc``, if the admin set
-    ``username =``  in the system hgrc).
+    ``username =``  in the system hgrc). Environment variables in the
+    username are expanded.
 ``verbose``
     Increase the amount of output printed. True or False. Default is False.
 
--- a/hgext/mq.py	Tue May 25 13:24:49 2010 -0300
+++ b/hgext/mq.py	Mon May 31 12:57:34 2010 -0500
@@ -37,6 +37,9 @@
 preserving existing git patches upon qrefresh. If set to 'yes' or
 'no', mq will override the [diff] section and always generate git or
 regular patches, possibly losing data in the second case.
+
+You will by default be managing a patch queue named "patches". You can
+create other, independent patch queues with the :hg:`qqueue` command.
 '''
 
 from mercurial.i18n import _
@@ -234,7 +237,12 @@
 class queue(object):
     def __init__(self, ui, path, patchdir=None):
         self.basepath = path
-        self.path = patchdir or os.path.join(path, "patches")
+        try:
+            fh = open(os.path.join(path, '.queue'))
+            curpath = os.path.join(path, fh.read().rstrip())
+        except IOError:
+            curpath = os.path.join(path, 'patches')
+        self.path = patchdir or curpath
         self.opener = util.opener(self.path)
         self.ui = ui
         self.applied_dirty = 0
@@ -608,8 +616,7 @@
                     repo.dirstate.invalidate()
                 raise
         finally:
-            del tr
-            release(lock, wlock)
+            release(tr, lock, wlock)
             self.removeundo(repo)
 
     def _apply(self, repo, series, list=False, update_status=True,
@@ -741,7 +748,6 @@
             raise util.Abort(_('qdelete requires at least one revision or '
                                'patch name'))
 
-        realpatches = []
         for patch in patches:
             patch = self.lookup(patch, strict=True)
             info = self.isapplied(patch)
@@ -749,8 +755,8 @@
                 raise util.Abort(_("cannot delete applied patch %s") % patch)
             if patch not in self.series:
                 raise util.Abort(_("patch %s not in series file") % patch)
-            realpatches.append(patch)
 
+        patches = list(patches)
         numrevs = 0
         if opts.get('rev'):
             if not self.applied:
@@ -759,10 +765,10 @@
             if len(revs) > 1 and revs[0] > revs[1]:
                 revs.reverse()
             revpatches = self._revpatches(repo, revs)
-            realpatches += revpatches
+            patches += revpatches
             numrevs = len(revpatches)
 
-        self._cleanup(realpatches, numrevs, opts.get('keep'))
+        self._cleanup(patches, numrevs, opts.get('keep'))
 
     def check_toppatch(self, repo):
         if self.applied:
@@ -2534,6 +2540,107 @@
     q.save_dirty()
     return 0
 
+def qqueue(ui, repo, name=None, **opts):
+    '''manage multiple patch queues
+
+    Supports switching between different patch queues, as well as creating
+    new patch queues and deleting existing ones.
+
+    Omitting a queue name or specifying -l/--list will show you the registered
+    queues - by default the "normal" patches queue is registered. The currently
+    active queue will be marked with "(active)".
+
+    To create a new queue, use -c/--create. The queue is automatically made
+    active, except in the case where there are applied patches from the
+    currently active queue in the repository. Then the queue will only be
+    created and switching will fail.
+
+    To delete an existing queue, use --delete. You cannot delete the currently
+    active queue.
+    '''
+
+    q = repo.mq
+
+    _defaultqueue = 'patches'
+    _allqueues = '.queues'
+    _activequeue = '.queue'
+
+    def _getcurrent():
+        return os.path.basename(q.path)
+
+    def _noqueues():
+        try:
+            fh = repo.opener(_allqueues, 'r')
+            fh.close()
+        except IOError:
+            return True
+
+        return False
+
+    def _getqueues():
+        current = _getcurrent()
+
+        try:
+            fh = repo.opener(_allqueues, 'r')
+            queues = [queue.strip() for queue in fh if queue.strip()]
+            if current not in queues:
+                queues.append(current)
+        except IOError:
+            queues = [_defaultqueue]
+
+        return sorted(queues)
+
+    def _setactive(name):
+        if q.applied:
+            raise util.Abort(_('patches applied - cannot set new queue active'))
+
+        fh = repo.opener(_activequeue, 'w')
+        fh.write(name)
+        fh.close()
+
+    def _addqueue(name):
+        fh = repo.opener(_allqueues, 'a')
+        fh.write('%s\n' % (name,))
+        fh.close()
+
+    if not name or opts.get('list'):
+        current = _getcurrent()
+        for queue in _getqueues():
+            ui.write('%s' % (queue,))
+            if queue == current:
+                ui.write(_(' (active)\n'))
+            else:
+                ui.write('\n')
+        return
+
+    existing = _getqueues()
+
+    if name not in existing and opts.get('delete'):
+        raise util.Abort(_('cannot delete queue that does not exist'))
+    elif name not in existing and not opts.get('create'):
+        raise util.Abort(_('use --create to create a new queue'))
+
+    if opts.get('create'):
+        if _noqueues():
+            _addqueue(_defaultqueue)
+        _addqueue(name)
+        _setactive(name)
+    elif opts.get('delete'):
+        current = _getcurrent()
+
+        if name == current:
+            raise util.Abort(_('cannot delete currently active queue'))
+
+        fh = repo.opener('.queues.new', 'w')
+        for queue in existing:
+            if queue == name:
+                continue
+            fh.write('%s\n' % (queue,))
+        fh.close()
+        util.rename(repo.join('.queues.new'), repo.join(_allqueues))
+    else:
+        _setactive(name)
+
 def reposetup(ui, repo):
     class mqrepo(repo.__class__):
         @util.propertycache
@@ -2840,6 +2947,14 @@
         (finish,
          [('a', 'applied', None, _('finish all applied changesets'))],
          _('hg qfinish [-a] [REV]...')),
+    'qqueue':
+        (qqueue,
+         [
+             ('l', 'list', False, _('list all available queues')),
+             ('c', 'create', False, _('create new queue')),
+             ('', 'delete', False, _('delete reference to queue')),
+         ],
+         _('[OPTION] [QUEUE]')),
 }
 
 colortable = {'qguard.negative': 'red',
--- a/hgext/record.py	Tue May 25 13:24:49 2010 -0300
+++ b/hgext/record.py	Mon May 31 12:57:34 2010 -0500
@@ -296,9 +296,9 @@
             ui.write("\n")
             if r == 7: # ?
                 doc = gettext(record.__doc__)
-                c = doc.find(_('y - record this change'))
+                c = doc.find('::') + 2
                 for l in doc[c:].splitlines():
-                    if l:
+                    if l.startswith('      '):
                         ui.write(l.strip(), '\n')
                 continue
             elif r == 0: # yes
@@ -380,7 +380,9 @@
       a - record all changes to all remaining files
       q - quit, recording no changes
 
-      ? - display help'''
+      ? - display help
+
+    This command is not available when committing a merge.'''
 
     dorecord(ui, repo, commands.commit, *pats, **opts)
 
@@ -419,10 +421,15 @@
         After the actual job is done by non-interactive command, working dir
         state is restored to original.
 
-        In the end we'll record intresting changes, and everything else will be
+        In the end we'll record interesting changes, and everything else will be
         left in place, so the user can continue his work.
         """
 
+        merge = len(repo[None].parents()) > 1
+        if merge:
+            raise util.Abort(_('cannot partially commit a merge '
+                               '(use hg commit instead)'))
+
         changes = repo.status(match=match)[:3]
         diffopts = mdiff.diffopts(git=True, nodates=True)
         chunks = patch.diff(repo, changes=changes, opts=diffopts)
--- a/mercurial/cmdutil.py	Tue May 25 13:24:49 2010 -0300
+++ b/mercurial/cmdutil.py	Mon May 31 12:57:34 2010 -0500
@@ -8,7 +8,7 @@
 from node import hex, nullid, nullrev, short
 from i18n import _
 import os, sys, errno, re, glob, tempfile
-import mdiff, bdiff, util, templater, patch, error, encoding, templatekw
+import util, templater, patch, error, encoding, templatekw
 import match as _match
 import similar
 
@@ -351,12 +351,13 @@
 
     def walkpat(pat):
         srcs = []
+        badstates = after and '?' or '?r'
         m = match(repo, [pat], opts, globbed=True)
         for abs in repo.walk(m):
             state = repo.dirstate[abs]
             rel = m.rel(abs)
             exact = m.exact(abs)
-            if state in '?r':
+            if state in badstates:
                 if exact and state == '?':
                     ui.warn(_('%s: not copying - file is not managed\n') % rel)
                 if exact and state == 'r':
--- a/mercurial/commands.py	Tue May 25 13:24:49 2010 -0300
+++ b/mercurial/commands.py	Mon May 31 12:57:34 2010 -0500
@@ -2530,19 +2530,26 @@
 def push(ui, repo, dest=None, **opts):
     """push changes to the specified destination
 
-    Push changes from the local repository to the specified destination.
-
-    This is the symmetrical operation for pull. It moves changes from
-    the current repository to a different one. If the destination is
-    local this is identical to a pull in that directory from the
-    current one.
-
-    By default, push will refuse to run if it detects the result would
-    increase the number of remote heads. This generally indicates the
-    user forgot to pull and merge before pushing.
-
-    If -r/--rev is used, the named revision and all its ancestors will
-    be pushed to the remote repository.
+    Push changesets from the local repository to the specified
+    destination.
+
+    This operation is symmetrical to pull: it is identical to a pull
+    in the destination repository from the current one.
+
+    By default, push will not allow creation of new heads at the
+    destination, since multiple heads would make it unclear which head
+    to use. In this situation, it is recommended to pull and merge
+    before pushing.
+
+    Use --new-branch if you want to allow push to create a new named
+    branch that is not present at the destination. This allows you to
+    only create a new branch without forcing other changes.
+
+    Use -f/--force to override the default behavior and push all
+    changesets on all branches.
+
+    If -r/--rev is used, the specified revision and all its ancestors
+    will be pushed to the remote repository.
 
     Please see :hg:`help urls` for important details about ``ssh://``
     URLs. If DESTINATION is omitted, a default path will be used.
--- a/mercurial/config.py	Tue May 25 13:24:49 2010 -0300
+++ b/mercurial/config.py	Mon May 31 12:57:34 2010 -0500
@@ -6,7 +6,7 @@
 # GNU General Public License version 2 or any later version.
 
 from i18n import _
-import error
+import error, util
 import re, os
 
 class sortdict(dict):
@@ -96,7 +96,7 @@
                 cont = False
             m = includere.match(l)
             if m:
-                inc = m.group(1)
+                inc = util.expandpath(m.group(1))
                 base = os.path.dirname(src)
                 inc = os.path.normpath(os.path.join(base, inc))
                 if include:
--- a/mercurial/hg.py	Tue May 25 13:24:49 2010 -0300
+++ b/mercurial/hg.py	Mon May 31 12:57:34 2010 -0500
@@ -277,6 +277,7 @@
                                      % dest)
                 raise
 
+            hardlink = None
             for f in src_repo.store.copylist():
                 src = os.path.join(src_repo.sharedpath, f)
                 dst = os.path.join(dest_path, f)
@@ -287,7 +288,7 @@
                     if dst.endswith('data'):
                         # lock to avoid premature writing to the target
                         dest_lock = lock.lock(os.path.join(dstbase, "lock"))
-                    util.copyfiles(src, dst)
+                    hardlink = util.copyfiles(src, dst, hardlink)
 
             # we need to re-init the repo after manually copying the data
             # into it
--- a/mercurial/localrepo.py	Tue May 25 13:24:49 2010 -0300
+++ b/mercurial/localrepo.py	Mon May 31 12:57:34 2010 -0500
@@ -971,7 +971,8 @@
                 self.branchtags()
             return n
         finally:
-            del tr
+            if tr:
+                tr.release()
             lock.release()
 
     def destroyed(self):
@@ -1041,7 +1042,9 @@
             match.bad = bad
 
         if working: # we need to scan the working dir
-            subrepos = ctx1.substate.keys()
+            subrepos = []
+            if '.hgsub' in self.dirstate:
+                subrepos = ctx1.substate.keys()
             s = self.dirstate.status(match, subrepos, listignored,
                                      listclean, listunknown)
             cmp, modified, added, removed, deleted, unknown, ignored, clean = s
@@ -2192,7 +2195,7 @@
 
             tr.close()
         finally:
-            del tr
+            tr.release()
 
         if changesets > 0:
             # forcefully update the on-disk branch cache
--- a/mercurial/transaction.py	Tue May 25 13:24:49 2010 -0300
+++ b/mercurial/transaction.py	Mon May 31 12:57:34 2010 -0500
@@ -43,6 +43,7 @@
 class transaction(object):
     def __init__(self, report, opener, journal, after=None, createmode=None):
         self.count = 1
+        self.usages = 1
         self.report = report
         self.opener = opener
         self.after = after
@@ -108,8 +109,16 @@
     @active
     def nest(self):
         self.count += 1
+        self.usages += 1
         return self
 
+    def release(self):
+        if self.count > 0:
+            self.usages -= 1
+        # of the transaction scopes are left without being closed, fail
+        if self.count > 0 and self.usages == 0:
+            self._abort()
+
     def running(self):
         return self.count > 0
 
@@ -136,6 +145,7 @@
 
     def _abort(self):
         self.count = 0
+        self.usages = 0
         self.file.close()
 
         try:
--- a/mercurial/ui.py	Tue May 25 13:24:49 2010 -0300
+++ b/mercurial/ui.py	Mon May 31 12:57:34 2010 -0500
@@ -266,6 +266,8 @@
         user = os.environ.get("HGUSER")
         if user is None:
             user = self.config("ui", "username")
+            if user is not None:
+                user = os.path.expandvars(user)
         if user is None:
             user = os.environ.get("EMAIL")
         if user is None and self.configbool("ui", "askusername"):
--- a/mercurial/util.py	Tue May 25 13:24:49 2010 -0300
+++ b/mercurial/util.py	Mon May 31 12:57:34 2010 -0500
@@ -458,7 +458,7 @@
         for name, kind in osutil.listdir(src):
             srcname = os.path.join(src, name)
             dstname = os.path.join(dst, name)
-            copyfiles(srcname, dstname, hardlink)
+            hardlink = copyfiles(srcname, dstname, hardlink)
     else:
         if hardlink:
             try:
@@ -469,6 +469,8 @@
         else:
             shutil.copy(src, dst)
 
+    return hardlink
+
 class path_auditor(object):
     '''ensure that a filesystem path contains no banned components.
     the following properties of a path are checked:
--- a/tests/test-hgrc	Tue May 25 13:24:49 2010 -0300
+++ b/tests/test-hgrc	Mon May 31 12:57:34 2010 -0500
@@ -23,8 +23,33 @@
     > $HGRCPATH
 hg showconfig foo
 
-echo '%include /no-such-file' > $HGRCPATH
+FAKEPATH=/path/to/nowhere
+export FAKEPATH
+echo '%include $FAKEPATH/no-such-file' > $HGRCPATH
 hg version 2>&1 | sed -e "s|$HGRCPATH|\$HGRCPATH|"
+unset FAKEPATH
+
+echo "% username expansion"
+olduser=$HGUSER
+unset HGUSER
+
+FAKEUSER='John Doe'
+export FAKEUSER
+echo '[ui]' > $HGRCPATH
+echo 'username = $FAKEUSER' >> $HGRCPATH
+
+hg init usertest
+cd usertest
+touch bar
+hg commit --addremove --quiet -m "added bar"
+hg log --template "{author}\n"
+cd ..
+
+hg showconfig | sed -e "s:$p:...:"
+
+unset FAKEUSER
+HGUSER=$olduser
+export HGUSER
 
 # HGPLAIN
 cd ..
--- a/tests/test-hgrc.out	Tue May 25 13:24:49 2010 -0300
+++ b/tests/test-hgrc.out	Mon May 31 12:57:34 2010 -0500
@@ -9,7 +9,10 @@
 hg: config error at $HGRCPATH:2: '  x = y'
 foo.bar=a\nb\nc\nde\nfg
 foo.baz=bif cb
-hg: config error at $HGRCPATH:1: cannot include /no-such-file (No such file or directory)
+hg: config error at $HGRCPATH:1: cannot include /path/to/nowhere/no-such-file (No such file or directory)
+% username expansion
+John Doe
+ui.username=$FAKEUSER
 % customized hgrc
 read config from: .../.hgrc
 .../.hgrc:13: alias.log=log -g
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-mq-qqueue	Mon May 31 12:57:34 2010 -0500
@@ -0,0 +1,47 @@
+#!/bin/sh
+
+echo "[extensions]" >> $HGRCPATH
+echo "mq=" >> $HGRCPATH
+
+hg init foo
+cd foo
+echo a > a
+hg ci -qAm a
+
+echo %% default queue
+hg qqueue
+
+echo b > a
+hg qnew -fgDU somestuff
+
+echo %% applied patches in default queue
+hg qap
+
+echo %% try to change patch \(create succeeds, switch fails\)
+hg qqueue foo --create
+hg qqueue
+
+echo %% empty default queue
+hg qpop
+
+echo %% switch queue
+hg qqueue foo
+hg qqueue
+
+echo %% unapplied patches
+hg qun
+echo c > a
+hg qnew -fgDU otherstuff
+
+echo %% fail switching back
+hg qqueue patches
+
+echo %% fail deleting current
+hg qqueue foo --delete
+
+echo %% switch back and delete foo
+hg qpop -a
+hg qqueue patches
+hg qqueue foo --delete
+hg qqueue
+cd ..
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-mq-qqueue.out	Mon May 31 12:57:34 2010 -0500
@@ -0,0 +1,23 @@
+%% default queue
+patches (active)
+%% applied patches in default queue
+somestuff
+%% try to change patch (create succeeds, switch fails)
+abort: patches applied - cannot set new queue active
+foo
+patches (active)
+%% empty default queue
+popping somestuff
+patch queue now empty
+%% switch queue
+foo (active)
+patches
+%% unapplied patches
+%% fail switching back
+abort: patches applied - cannot set new queue active
+%% fail deleting current
+abort: cannot delete currently active queue
+%% switch back and delete foo
+popping otherstuff
+patch queue now empty
+patches (active)
--- a/tests/test-mq.out	Tue May 25 13:24:49 2010 -0300
+++ b/tests/test-mq.out	Mon May 31 12:57:34 2010 -0500
@@ -32,6 +32,9 @@
 will override the [diff] section and always generate git or regular patches,
 possibly losing data in the second case.
 
+You will by default be managing a patch queue named "patches". You can create
+other, independent patch queues with the "hg qqueue" command.
+
 list of commands:
 
  qapplied     print the patches already applied
@@ -49,6 +52,7 @@
  qpop         pop the current patch off the stack
  qprev        print the name of the previous patch
  qpush        push the next patch onto the stack
+ qqueue       manage multiple patch queues
  qrefresh     update the current patch
  qrename      rename a patch
  qselect      set or print guarded patches to push
--- a/tests/test-record	Tue May 25 13:24:49 2010 -0300
+++ b/tests/test-record	Mon May 31 12:57:34 2010 -0500
@@ -301,9 +301,20 @@
 EOF
 echo; hg tip --config diff.git=True -p
 
+cd ..
+
+echo % abort early when a merge is in progress
+hg up 4
+touch iwillmergethat
+hg add iwillmergethat
+hg branch thatbranch
+hg ci -m'new head'
+hg up default
+hg merge thatbranch
+echo; hg record -m'will abort'
+hg up -C
 
 echo % with win32ext
-cd ..
 echo '[extensions]' >> .hg/hgrc
 echo 'win32text = ' >> .hg/hgrc
 echo '[decode]' >> .hg/hgrc
--- a/tests/test-record.out	Tue May 25 13:24:49 2010 -0300
+++ b/tests/test-record.out	Mon May 31 12:57:34 2010 -0500
@@ -24,6 +24,8 @@
 
       ? - display help
 
+    This command is not available when committing a merge.
+
 options:
 
  -A --addremove     mark new/missing files as added/removed before committing
@@ -626,6 +628,15 @@
  b
 +c
 
+% abort early when a merge is in progress
+1 files updated, 0 files merged, 5 files removed, 0 files unresolved
+marked working directory as branch thatbranch
+5 files updated, 0 files merged, 2 files removed, 0 files unresolved
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+(branch merge, don't forget to commit)
+
+abort: cannot partially commit a merge (use hg commit instead)
+0 files updated, 0 files merged, 1 files removed, 0 files unresolved
 % with win32ext
 diff --git a/subdir/f1 b/subdir/f1
 1 hunks, 1 lines changed
@@ -637,8 +648,9 @@
 +d
 record this change to 'subdir/f1'? [Ynsfdaq?] 
 
-changeset:   25:5bacc1f6e9cf
+changeset:   26:5bacc1f6e9cf
 tag:         tip
+parent:      24:1460f6e47966
 user:        test
 date:        Thu Jan 01 00:00:23 1970 +0000
 summary:     w1
--- a/tests/test-rename	Tue May 25 13:24:49 2010 -0300
+++ b/tests/test-rename	Mon May 31 12:57:34 2010 -0500
@@ -23,6 +23,14 @@
 hg update -C
 rm d2/c
 
+echo '# rename --after a single file when src and tgt already tracked'
+mv d1/d11/a1 d2/c
+hg addrem
+hg rename --after d1/d11/a1 d2/c
+hg status -C
+hg update -C
+rm d2/c
+
 echo "# rename --after a single file to a nonexistant target filename"
 hg rename --after d1/a dummy
 
--- a/tests/test-rename.out	Tue May 25 13:24:49 2010 -0300
+++ b/tests/test-rename.out	Mon May 31 12:57:34 2010 -0500
@@ -8,6 +8,13 @@
   d1/d11/a1
 R d1/d11/a1
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+# rename --after a single file when src and tgt already tracked
+removing d1/d11/a1
+adding d2/c
+A d2/c
+  d1/d11/a1
+R d1/d11/a1
+1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 # rename --after a single file to a nonexistant target filename
 d1/a: not recording move - dummy does not exist
 # move a single file to an existing directory