Merge branch 'maint'
authorJunio C Hamano <gitster@pobox.com>
Mon, 27 Feb 2012 01:13:07 +0000 (17:13 -0800)
committerJunio C Hamano <gitster@pobox.com>
Mon, 27 Feb 2012 01:39:04 +0000 (17:39 -0800)
* maint:
Document accumulated fixes since 1.7.9.2
Git 1.7.8.5
grep -P: Fix matching ^ and $
am: don't infloop for an empty input file
rebase -m: only call "notes copy" when rewritten exists and is non-empty
git-p4: remove bash-ism in t9800
git-p4: remove bash-ism in t9809
git-p4: fix submit regression with clientSpec and subdir clone
git-p4: set useClientSpec variable on initial clone
Makefile: add thread-utils.h to LIB_H

Conflicts:
RelNotes
t/t9809-git-p4-client-view.sh

1  2 
Documentation/RelNotes/1.7.10.txt
Documentation/git-p4.txt
Makefile
contrib/fast-import/git-p4
git-am.sh
t/t4150-am.sh
t/t9809-git-p4-client-view.sh
index 0add762b0a03ebfdf0718cab071bd44c0e8b30cc,0000000000000000000000000000000000000000..364e16d0d5a40bae90cc97e0d6c610d3c3221f22
mode 100644,000000..100644
--- /dev/null
@@@ -1,109 -1,0 +1,100 @@@
-  * The config.mak.autogen generated by optional autoconf support tried
-    to link the binary with -lintl even when libintl.h is missing from
-    the system.
-    (merge a8356d4 js/configure-libintl later to maint).
-  * "git add --refresh <pathspec>" used to warn about unmerged paths
-    outside the given pathspec.
-    (merge 3d1f148 jc/add-refresh-unmerged later to maint).
 +Git v1.7.10 Release Notes
 +=========================
 +
 +Updates since v1.7.9
 +--------------------
 +
 +UI, Workflows & Features
 +
 + * Improved handling of views, labels and branches in git-p4 (in contrib).
 +
 + * "git-p4" (in contrib) suffered from unnecessary merge conflicts when
 +   p4 expanded the embedded $RCS$-like keywords; it can be now told to
 +   unexpand them.
 +
 + * Some "git-svn" updates.
 +
 + * "vcs-svn"/"svn-fe" learned to read dumps with svn-deltas and
 +   support incremental imports.
 +
 + * The configuration mechanism learned an "include" facility; an
 +   assignment to the include.path pseudo-variable causes the named
 +   file to be included in-place when Git looks up configuration
 +   variables.
 +
 + * "git am" learned to pass "-b" option to underlying "git mailinfo", so
 +   that bracketed string other than "PATCH" at the beginning can be kept.
 +
 + * "git clone" learned "--single-branch" option to limit cloning to a
 +   single branch (surprise!).
 +
 + * "git clone" learned to detach the HEAD in the resulting repository
 +   when the source repository's HEAD does not point to a branch.
 +
 + * When showing a patch while ignoring whitespace changes, the context
 +   lines are taken from the postimage, in order to make it easier to
 +   view the output.
 +
 + * "diff-highlight" filter (in contrib/) was updated to produce more
 +   aesthetically pleasing output.
 +
 + * "git tag --list" can be given "--points-at <object>" to limit its
 +   output to those that point at the given object.
 +
 + * "git merge" in an interactive session learned to spawn the editor
 +   by default to let the user edit the auto-generated merge message,
 +   to encourage people to explain their merges better. Legacy scripts
 +   can export MERGE_AUTOEDIT=no to retain the historical behaviour.
 +   Both "git merge" and "git pull" can be given --no-edit from the
 +   command line to accept the auto-generated merge message.
 +
 + * "gitweb" allows intermediate entries in the directory hierarchy
 +   that leads to a projects to be clicked, which in turn shows the
 +   list of projects inside that directory.
 +
 +Performance
 +
 + * During "git upload-pack" in respose to "git fetch", unnecessary calls
 +   to parse_object() have been eliminated, to help performance in
 +   repositories with excessive number of refs.
 +
 +Internal Implementation (please report possible regressions)
 +
 + * Recursive call chains in "git index-pack" to deal with long delta
 +   chains have been flattened, to reduce the stack footprint.
 +
 + * Use of add_extra_ref() API is now gone, to make it possible to
 +   cleanly restructure the overall refs API.
 +
 + * The command line parser of "git pack-objects" now uses parse-options
 +   API.
 +
 + * The test suite supports the new "test_pause" helper function.
 +
 + * Parallel to the test suite, there is a beginning of performance
 +   benchmarking framework.
 +
 + * t/Makefile is adjusted to prevent newer versions of GNU make from
 +   running tests in seemingly random order.
 +
 +Also contains minor documentation updates and code clean-ups.
 +
 +
 +Fixes since v1.7.9
 +------------------
 +
 +Unless otherwise noted, all the fixes since v1.7.9 in the maintenance
 +releases are contained in this release (see release notes to them for
 +details).
 +
 + * "gitweb" used to drop warnings in the log file when "heads" view is
 +   accessed in a repository whose HEAD does not point at a valid
 +   branch.
 +
 +---
 +exec >/var/tmp/1
 +O=v1.7.9.2-261-gd065f68
 +echo O=$(git describe)
 +git log --first-parent --oneline ^maint $O..
 +echo
 +git shortlog --no-merges ^maint $O..
diff --combined Documentation/git-p4.txt
index 3fecefaea258a9f0a72651d59a554ee5b99b6382,ed827902fc540d098fc548e5bb68ab3ab95ffa25..b7c7929716adbad2e27f2d38b83a3c8f74604a59
@@@ -303,9 -303,13 +303,13 @@@ CLIENT SPE
  -----------
  The p4 client specification is maintained with the 'p4 client' command
  and contains among other fields, a View that specifies how the depot
- is mapped into the client repository.  Git-p4 can consult the client
- spec when given the '--use-client-spec' option or useClientSpec
- variable.
+ is mapped into the client repository.  The 'clone' and 'sync' commands
+ can consult the client spec when given the '--use-client-spec' option or
+ when the useClientSpec variable is true.  After 'git p4 clone', the
+ useClientSpec variable is automatically set in the repository
+ configuration file.  This allows future 'git p4 submit' commands to
+ work properly; the submit command looks only at the variable and does
+ not have a command-line option.
  
  The full syntax for a p4 view is documented in 'p4 help views'.  Git-p4
  knows only a subset of the view syntax.  It understands multi-line
@@@ -314,11 -318,6 +318,11 @@@ around whitespace.  Of the possible wil
  '...', and only when it is at the end of the path.  Git-p4 will complain
  if it encounters an unhandled wildcard.
  
 +Bugs in the implementation of overlap mappings exist.  If multiple depot
 +paths map through overlays to the same location in the repository,
 +git-p4 can choose the wrong one.  This is hard to solve without
 +dedicating a client spec just for git-p4.
 +
  The name of the client can be given to git-p4 in multiple ways.  The
  variable 'git-p4.client' takes precedence if it exists.  Otherwise,
  normal p4 mechanisms of determining the client are used:  environment
@@@ -483,11 -482,6 +487,11 @@@ git-p4.skipUserNameCheck:
        user map, 'git p4' exits.  This option can be used to force
        submission regardless.
  
 +git-p4.attemptRCSCleanup:
 +    If enabled, 'git p4 submit' will attempt to cleanup RCS keywords
 +    ($Header$, etc). These would otherwise cause merge conflicts and prevent
 +    the submit going ahead. This option should be considered experimental at
 +    present.
  
  IMPLEMENTATION DETAILS
  ----------------------
diff --combined Makefile
index 1fb170531708cc8dc0f34be4492b8bdf9e52b564,e4f8e0ef08b9f3f4eb5035f20421ee035127fdf2..cf2c40b44f8383d002235400660d76ef7f6de33c
+++ b/Makefile
@@@ -47,9 -47,6 +47,9 @@@ all:
  # A translated Git requires GNU libintl or another gettext implementation,
  # plus libintl-perl at runtime.
  #
 +# Define USE_GETTEXT_SCHEME and set it to 'fallthrough', if you don't trust
 +# the installed gettext translation of the shell scripts output.
 +#
  # Define HAVE_LIBCHARSET_H if you haven't set NO_GETTEXT and you can't
  # trust the langinfo.h's nl_langinfo(CODESET) function to return the
  # current character set. GNU and Solaris have a nl_langinfo(CODESET),
@@@ -381,11 -378,6 +381,11 @@@ BUILTIN_OBJS 
  BUILT_INS =
  COMPAT_CFLAGS =
  COMPAT_OBJS =
 +XDIFF_H =
 +XDIFF_OBJS =
 +VCSSVN_H =
 +VCSSVN_OBJS =
 +VCSSVN_TEST_OBJS =
  EXTRA_CPPFLAGS =
  LIB_H =
  LIB_OBJS =
@@@ -481,13 -473,16 +481,13 @@@ TEST_PROGRAMS_NEED_X += test-index-vers
  TEST_PROGRAMS_NEED_X += test-line-buffer
  TEST_PROGRAMS_NEED_X += test-match-trees
  TEST_PROGRAMS_NEED_X += test-mktemp
 -TEST_PROGRAMS_NEED_X += test-obj-pool
  TEST_PROGRAMS_NEED_X += test-parse-options
  TEST_PROGRAMS_NEED_X += test-path-utils
  TEST_PROGRAMS_NEED_X += test-run-command
  TEST_PROGRAMS_NEED_X += test-sha1
  TEST_PROGRAMS_NEED_X += test-sigchain
 -TEST_PROGRAMS_NEED_X += test-string-pool
  TEST_PROGRAMS_NEED_X += test-subprocess
  TEST_PROGRAMS_NEED_X += test-svn-fe
 -TEST_PROGRAMS_NEED_X += test-treap
  
  TEST_PROGRAMS = $(patsubst %,%$X,$(TEST_PROGRAMS_NEED_X))
  
@@@ -620,6 -615,7 +620,7 @@@ LIB_H += streaming.
  LIB_H += string-list.h
  LIB_H += submodule.h
  LIB_H += tag.h
+ LIB_H += thread-utils.h
  LIB_H += transport.h
  LIB_H += tree.h
  LIB_H += tree-walk.h
@@@ -1533,7 -1529,6 +1534,7 @@@ ifdef GETTEXT_POISO
  endif
  ifdef NO_GETTEXT
        BASIC_CFLAGS += -DNO_GETTEXT
 +      USE_GETTEXT_SCHEME ?= fallthrough
  endif
  ifdef NO_STRCASESTR
        COMPAT_CFLAGS += -DNO_STRCASESTR
@@@ -1931,7 -1926,6 +1932,7 @@@ sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|
      -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
      -e 's|@@LOCALEDIR@@|$(localedir_SQ)|g' \
      -e 's/@@NO_CURL@@/$(NO_CURL)/g' \
 +    -e 's/@@USE_GETTEXT_SCHEME@@/$(USE_GETTEXT_SCHEME)/g' \
      -e $(BROKEN_PATH_FIX) \
      $@.sh >$@+
  endef
@@@ -2033,24 -2027,12 +2034,24 @@@ GIT_OBJS := $(LIB_OBJS) $(BUILTIN_OBJS
  ifndef NO_CURL
        GIT_OBJS += http.o http-walker.o remote-curl.o
  endif
 -XDIFF_OBJS = xdiff/xdiffi.o xdiff/xprepare.o xdiff/xutils.o xdiff/xemit.o \
 -      xdiff/xmerge.o xdiff/xpatience.o xdiff/xhistogram.o
 -VCSSVN_OBJS = vcs-svn/string_pool.o vcs-svn/line_buffer.o \
 -      vcs-svn/repo_tree.o vcs-svn/fast_export.o vcs-svn/svndump.o
 -VCSSVN_TEST_OBJS = test-obj-pool.o test-string-pool.o \
 -      test-line-buffer.o test-treap.o
 +
 +XDIFF_OBJS += xdiff/xdiffi.o
 +XDIFF_OBJS += xdiff/xprepare.o
 +XDIFF_OBJS += xdiff/xutils.o
 +XDIFF_OBJS += xdiff/xemit.o
 +XDIFF_OBJS += xdiff/xmerge.o
 +XDIFF_OBJS += xdiff/xpatience.o
 +XDIFF_OBJS += xdiff/xhistogram.o
 +
 +VCSSVN_OBJS += vcs-svn/line_buffer.o
 +VCSSVN_OBJS += vcs-svn/sliding_window.o
 +VCSSVN_OBJS += vcs-svn/repo_tree.o
 +VCSSVN_OBJS += vcs-svn/fast_export.o
 +VCSSVN_OBJS += vcs-svn/svndiff.o
 +VCSSVN_OBJS += vcs-svn/svndump.o
 +
 +VCSSVN_TEST_OBJS += test-line-buffer.o
 +
  OBJECTS := $(GIT_OBJS) $(XDIFF_OBJS) $(VCSSVN_OBJS)
  
  dep_files := $(foreach f,$(OBJECTS),$(dir $f).depend/$(notdir $f).d)
@@@ -2169,25 -2151,16 +2170,25 @@@ connect.o transport.o url.o http-backen
  http-fetch.o http-walker.o remote-curl.o transport.o walker.o: walker.h
  http.o http-walker.o http-push.o http-fetch.o remote-curl.o: http.h url.h
  
 -xdiff-interface.o $(XDIFF_OBJS): \
 -      xdiff/xinclude.h xdiff/xmacros.h xdiff/xdiff.h xdiff/xtypes.h \
 -      xdiff/xutils.h xdiff/xprepare.h xdiff/xdiffi.h xdiff/xemit.h
 +XDIFF_H += xdiff/xinclude.h
 +XDIFF_H += xdiff/xmacros.h
 +XDIFF_H += xdiff/xdiff.h
 +XDIFF_H += xdiff/xtypes.h
 +XDIFF_H += xdiff/xutils.h
 +XDIFF_H += xdiff/xprepare.h
 +XDIFF_H += xdiff/xdiffi.h
 +XDIFF_H += xdiff/xemit.h
 +
 +xdiff-interface.o $(XDIFF_OBJS): $(XDIFF_H)
  
 -$(VCSSVN_OBJS) $(VCSSVN_TEST_OBJS): $(LIB_H) \
 -      vcs-svn/obj_pool.h vcs-svn/trp.h vcs-svn/string_pool.h \
 -      vcs-svn/line_buffer.h vcs-svn/repo_tree.h vcs-svn/fast_export.h \
 -      vcs-svn/svndump.h
 +VCSSVN_H += vcs-svn/line_buffer.h
 +VCSSVN_H += vcs-svn/sliding_window.h
 +VCSSVN_H += vcs-svn/repo_tree.h
 +VCSSVN_H += vcs-svn/fast_export.h
 +VCSSVN_H += vcs-svn/svndiff.h
 +VCSSVN_H += vcs-svn/svndump.h
  
 -test-svn-fe.o: vcs-svn/svndump.h
 +$(VCSSVN_OBJS) $(VCSSVN_TEST_OBJS): $(LIB_H) $(VCSSVN_H)
  endif
  
  exec_cmd.sp exec_cmd.s exec_cmd.o: EXTRA_CPPFLAGS = \
@@@ -2330,7 -2303,7 +2331,7 @@@ cscope
  ### Detect prefix changes
  TRACK_CFLAGS = $(CC):$(subst ','\'',$(ALL_CFLAGS)):\
               $(bindir_SQ):$(gitexecdir_SQ):$(template_dir_SQ):$(prefix_SQ):\
 -             $(localedir_SQ)
 +             $(localedir_SQ):$(USE_GETTEXT_SCHEME)
  
  GIT-CFLAGS: FORCE
        @FLAGS='$(TRACK_CFLAGS)'; \
@@@ -2361,10 -2334,6 +2362,10 @@@ GIT-BUILD-OPTIONS: FORC
        @echo USE_LIBPCRE=\''$(subst ','\'',$(subst ','\'',$(USE_LIBPCRE)))'\' >>$@
        @echo NO_PERL=\''$(subst ','\'',$(subst ','\'',$(NO_PERL)))'\' >>$@
        @echo NO_PYTHON=\''$(subst ','\'',$(subst ','\'',$(NO_PYTHON)))'\' >>$@
 +      @echo NO_UNIX_SOCKETS=\''$(subst ','\'',$(subst ','\'',$(NO_UNIX_SOCKETS)))'\' >>$@
 +ifdef GIT_TEST_OPTS
 +      @echo GIT_TEST_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_TEST_OPTS)))'\' >>$@
 +endif
  ifdef GIT_TEST_CMP
        @echo GIT_TEST_CMP=\''$(subst ','\'',$(subst ','\'',$(GIT_TEST_CMP)))'\' >>$@
  endif
@@@ -2373,18 -2342,7 +2374,18 @@@ ifdef GIT_TEST_CMP_USE_COPIED_CONTEX
  endif
        @echo NO_GETTEXT=\''$(subst ','\'',$(subst ','\'',$(NO_GETTEXT)))'\' >>$@
        @echo GETTEXT_POISON=\''$(subst ','\'',$(subst ','\'',$(GETTEXT_POISON)))'\' >>$@
 -      @echo NO_UNIX_SOCKETS=\''$(subst ','\'',$(subst ','\'',$(NO_UNIX_SOCKETS)))'\' >>$@
 +ifdef GIT_PERF_REPEAT_COUNT
 +      @echo GIT_PERF_REPEAT_COUNT=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_REPEAT_COUNT)))'\' >>$@
 +endif
 +ifdef GIT_PERF_REPO
 +      @echo GIT_PERF_REPO=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_REPO)))'\' >>$@
 +endif
 +ifdef GIT_PERF_LARGE_REPO
 +      @echo GIT_PERF_LARGE_REPO=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_LARGE_REPO)))'\' >>$@
 +endif
 +ifdef GIT_PERF_MAKE_OPTS
 +      @echo GIT_PERF_MAKE_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_MAKE_OPTS)))'\' >>$@
 +endif
  
  ### Detect Tck/Tk interpreter path changes
  ifndef NO_TCLTK
@@@ -2420,11 -2378,6 +2421,11 @@@ export NO_SVN_TEST
  test: all
        $(MAKE) -C t/ all
  
 +perf: all
 +      $(MAKE) -C t/perf/ all
 +
 +.PHONY: test perf
 +
  test-ctype$X: ctype.o
  
  test-date$X: date.o ctype.o
@@@ -2435,6 -2388,8 +2436,6 @@@ test-line-buffer$X: vcs-svn/lib.
  
  test-parse-options$X: parse-options.o parse-options-cb.o
  
 -test-string-pool$X: vcs-svn/lib.a
 -
  test-svn-fe$X: vcs-svn/lib.a
  
  .PRECIOUS: $(TEST_OBJS)
index 053955349a92207b9f67aec73aa54143c2204129,9ccc87b2055a8f90fbe1fcc80649f6a72bb09a5e..c5362c4c11d00e169781ff7da89c88a3051f6227
@@@ -10,7 -10,7 +10,7 @@@
  
  import optparse, sys, os, marshal, subprocess, shelve
  import tempfile, getopt, os.path, time, platform
 -import re
 +import re, shutil
  
  verbose = False
  
@@@ -38,7 -38,7 +38,7 @@@ def p4_build_cmd(cmd)
  
      host = gitConfig("git-p4.host")
      if len(host) > 0:
 -        real_cmd += ["-h", host]
 +        real_cmd += ["-H", host]
  
      client = gitConfig("git-p4.client")
      if len(client) > 0:
@@@ -186,47 -186,6 +186,47 @@@ def split_p4_type(p4type)
          mods = s[1]
      return (base, mods)
  
 +#
 +# return the raw p4 type of a file (text, text+ko, etc)
 +#
 +def p4_type(file):
 +    results = p4CmdList(["fstat", "-T", "headType", file])
 +    return results[0]['headType']
 +
 +#
 +# Given a type base and modifier, return a regexp matching
 +# the keywords that can be expanded in the file
 +#
 +def p4_keywords_regexp_for_type(base, type_mods):
 +    if base in ("text", "unicode", "binary"):
 +        kwords = None
 +        if "ko" in type_mods:
 +            kwords = 'Id|Header'
 +        elif "k" in type_mods:
 +            kwords = 'Id|Header|Author|Date|DateTime|Change|File|Revision'
 +        else:
 +            return None
 +        pattern = r"""
 +            \$              # Starts with a dollar, followed by...
 +            (%s)            # one of the keywords, followed by...
 +            (:[^$]+)?       # possibly an old expansion, followed by...
 +            \$              # another dollar
 +            """ % kwords
 +        return pattern
 +    else:
 +        return None
 +
 +#
 +# Given a file, return a regexp matching the possible
 +# RCS keywords that will be expanded, or None for files
 +# with kw expansion turned off.
 +#
 +def p4_keywords_regexp_for_file(file):
 +    if not os.path.exists(file):
 +        return None
 +    else:
 +        (type_base, type_mods) = split_p4_type(p4_type(file))
 +        return p4_keywords_regexp_for_type(type_base, type_mods)
  
  def setP4ExecBit(file, mode):
      # Reopens an already open file and changes the execute bit to match
@@@ -596,6 -555,46 +596,46 @@@ def p4PathStartsWith(path, prefix)
          return path.lower().startswith(prefix.lower())
      return path.startswith(prefix)
  
+ def getClientSpec():
+     """Look at the p4 client spec, create a View() object that contains
+        all the mappings, and return it."""
+     specList = p4CmdList("client -o")
+     if len(specList) != 1:
+         die('Output from "client -o" is %d lines, expecting 1' %
+             len(specList))
+     # dictionary of all client parameters
+     entry = specList[0]
+     # just the keys that start with "View"
+     view_keys = [ k for k in entry.keys() if k.startswith("View") ]
+     # hold this new View
+     view = View()
+     # append the lines, in order, to the view
+     for view_num in range(len(view_keys)):
+         k = "View%d" % view_num
+         if k not in view_keys:
+             die("Expected view key %s missing" % k)
+         view.append(entry[k])
+     return view
+ def getClientRoot():
+     """Grab the client directory."""
+     output = p4CmdList("client -o")
+     if len(output) != 1:
+         die('Output from "client -o" is %d lines, expecting 1' % len(output))
+     entry = output[0]
+     if "Root" not in entry:
+         die('Client has no "Root"')
+     return entry["Root"]
  class Command:
      def __init__(self):
          self.usage = "usage: %prog [options]"
  class P4UserMap:
      def __init__(self):
          self.userMapFromPerforceServer = False
 +        self.myP4UserId = None
 +
 +    def p4UserId(self):
 +        if self.myP4UserId:
 +            return self.myP4UserId
 +
 +        results = p4CmdList("user -o")
 +        for r in results:
 +            if r.has_key('User'):
 +                self.myP4UserId = r['User']
 +                return r['User']
 +        die("Could not find your p4 user id")
 +
 +    def p4UserIsMe(self, p4User):
 +        # return True if the given p4 user is actually me
 +        me = self.p4UserId()
 +        if not p4User or p4User != me:
 +            return False
 +        else:
 +            return True
  
      def getUserCacheFilename(self):
          home = os.environ.get("HOME", os.environ.get("USERPROFILE"))
@@@ -761,6 -740,7 +801,6 @@@ class P4Submit(Command, P4UserMap)
          self.verbose = False
          self.preserveUser = gitConfig("git-p4.preserveUser").lower() == "true"
          self.isWindows = (platform.system() == "Windows")
 -        self.myP4UserId = None
  
      def check(self):
          if len(p4CmdList("opened ...")) > 0:
  
          return result
  
 +    def patchRCSKeywords(self, file, pattern):
 +        # Attempt to zap the RCS keywords in a p4 controlled file matching the given pattern
 +        (handle, outFileName) = tempfile.mkstemp(dir='.')
 +        try:
 +            outFile = os.fdopen(handle, "w+")
 +            inFile = open(file, "r")
 +            regexp = re.compile(pattern, re.VERBOSE)
 +            for line in inFile.readlines():
 +                line = regexp.sub(r'$\1$', line)
 +                outFile.write(line)
 +            inFile.close()
 +            outFile.close()
 +            # Forcibly overwrite the original file
 +            os.unlink(file)
 +            shutil.move(outFileName, file)
 +        except:
 +            # cleanup our temporary file
 +            os.unlink(outFileName)
 +            print "Failed to strip RCS keywords in %s" % file
 +            raise
 +
 +        print "Patched up RCS keywords in %s" % file
 +
      def p4UserForCommit(self,id):
          # Return the tuple (perforce user,git email) for a given git commit id
          self.getUserMapFromPerforceServer()
      def canChangeChangelists(self):
          # check to see if we have p4 admin or super-user permissions, either of
          # which are required to modify changelists.
 -        results = p4CmdList("protects %s" % self.depotPath)
 +        results = p4CmdList(["protects", self.depotPath])
          for r in results:
              if r.has_key('perm'):
                  if r['perm'] == 'admin':
                      return 1
          return 0
  
 -    def p4UserId(self):
 -        if self.myP4UserId:
 -            return self.myP4UserId
 -
 -        results = p4CmdList("user -o")
 -        for r in results:
 -            if r.has_key('User'):
 -                self.myP4UserId = r['User']
 -                return r['User']
 -        die("Could not find your p4 user id")
 -
 -    def p4UserIsMe(self, p4User):
 -        # return True if the given p4 user is actually me
 -        me = self.p4UserId()
 -        if not p4User or p4User != me:
 -            return False
 -        else:
 -            return True
 -
      def prepareSubmitTemplate(self):
          # remove lines in the Files section that show changes to files outside the depot path we're committing into
          template = ""
          filesToDelete = set()
          editedFiles = set()
          filesToChangeExecBit = {}
 +
          for line in diff:
              diff = parseDiffTreeEntry(line)
              modifier = diff['status']
          patchcmd = diffcmd + " | git apply "
          tryPatchCmd = patchcmd + "--check -"
          applyPatchCmd = patchcmd + "--check --apply -"
 +        patch_succeeded = True
  
          if os.system(tryPatchCmd) != 0:
 +            fixed_rcs_keywords = False
 +            patch_succeeded = False
              print "Unfortunately applying the change failed!"
 +
 +            # Patch failed, maybe it's just RCS keyword woes. Look through
 +            # the patch to see if that's possible.
 +            if gitConfig("git-p4.attemptRCSCleanup","--bool") == "true":
 +                file = None
 +                pattern = None
 +                kwfiles = {}
 +                for file in editedFiles | filesToDelete:
 +                    # did this file's delta contain RCS keywords?
 +                    pattern = p4_keywords_regexp_for_file(file)
 +
 +                    if pattern:
 +                        # this file is a possibility...look for RCS keywords.
 +                        regexp = re.compile(pattern, re.VERBOSE)
 +                        for line in read_pipe_lines(["git", "diff", "%s^..%s" % (id, id), file]):
 +                            if regexp.search(line):
 +                                if verbose:
 +                                    print "got keyword match on %s in %s in %s" % (pattern, line, file)
 +                                kwfiles[file] = pattern
 +                                break
 +
 +                for file in kwfiles:
 +                    if verbose:
 +                        print "zapping %s with %s" % (line,pattern)
 +                    self.patchRCSKeywords(file, kwfiles[file])
 +                    fixed_rcs_keywords = True
 +
 +            if fixed_rcs_keywords:
 +                print "Retrying the patch with RCS keywords cleaned up"
 +                if os.system(tryPatchCmd) == 0:
 +                    patch_succeeded = True
 +
 +        if not patch_succeeded:
              print "What do you want to do?"
              response = "x"
              while response != "s" and response != "a" and response != "w":
              print "Internal error: cannot locate perforce depot path from existing branches"
              sys.exit(128)
  
-         self.clientPath = p4Where(self.depotPath)
+         self.useClientSpec = False
+         if gitConfig("git-p4.useclientspec", "--bool") == "true":
+             self.useClientSpec = True
+         if self.useClientSpec:
+             self.clientSpecDirs = getClientSpec()
  
-         if len(self.clientPath) == 0:
-             print "Error: Cannot locate perforce checkout of %s in client view" % self.depotPath
-             sys.exit(128)
+         if self.useClientSpec:
+             # all files are relative to the client spec
+             self.clientPath = getClientRoot()
+         else:
+             self.clientPath = p4Where(self.depotPath)
+         if self.clientPath == "":
+             die("Error: Cannot locate perforce checkout of %s in client view" % self.depotPath)
  
          print "Perforce checkout for depot path %s located at %s" % (self.depotPath, self.clientPath)
          self.oldWorkingDirectory = os.getcwd()
@@@ -1308,8 -1256,8 +1357,8 @@@ class View(object)
                  die("Can't handle * wildcards in view: %s" % self.path)
              triple_dot_index = self.path.find("...")
              if triple_dot_index >= 0:
 -                if not self.path.endswith("..."):
 -                    die("Can handle ... wildcard only at end of path: %s" %
 +                if triple_dot_index != len(self.path) - 3:
 +                    die("Can handle only single ... wildcard, at end: %s" %
                          self.path)
                  self.ends_triple_dot = True
  
              if self.exclude:
                  c = "-"
              return "View.Mapping: %s%s -> %s" % \
 -                   (c, self.depot_side, self.client_side)
 +                   (c, self.depot_side.path, self.client_side.path)
  
          def map_depot_to_client(self, depot_path):
              """Calculate the client path if using this mapping on the
              else:
                  # This mapping matched; no need to search any further.
                  # But, the mapping could be rejected if the client path
 -                # has already been claimed by an earlier mapping.
 +                # has already been claimed by an earlier mapping (i.e.
 +                # one later in the list, which we are walking backwards).
                  already_mapped_in_client = False
                  for f in paths_filled:
                      # this is View.Path.match
@@@ -1530,9 -1477,8 +1579,10 @@@ class P4Sync(Command, P4UserMap)
          self.p4BranchesInGit = []
          self.cloneExclude = []
          self.useClientSpec = False
+         self.useClientSpec_from_options = False
          self.clientSpecDirs = None
 +        self.tempBranches = []
 +        self.tempBranchLocation = "git-p4-tmp"
  
          if gitConfig("git-p4.syncFromOrigin") == "false":
              self.syncWithOrigin = False
                     .replace("%25", "%")
          return path
  
 +    # Force a checkpoint in fast-import and wait for it to finish
 +    def checkpoint(self):
 +        self.gitStream.write("checkpoint\n\n")
 +        self.gitStream.write("progress checkpoint\n\n")
 +        out = self.gitOutput.readline()
 +        if self.verbose:
 +            print "checkpoint finished: " + out
 +
      def extractFilesFromCommit(self, commit):
          self.cloneExclude = [re.sub(r"\.\.\.$", "", path)
                               for path in self.cloneExclude]
  
          # Note that we do not try to de-mangle keywords on utf16 files,
          # even though in theory somebody may want that.
 -        if type_base in ("text", "unicode", "binary"):
 -            if "ko" in type_mods:
 -                text = ''.join(contents)
 -                text = re.sub(r'\$(Id|Header):[^$]*\$', r'$\1$', text)
 -                contents = [ text ]
 -            elif "k" in type_mods:
 -                text = ''.join(contents)
 -                text = re.sub(r'\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$]*\$', r'$\1$', text)
 -                contents = [ text ]
 +        pattern = p4_keywords_regexp_for_type(type_base, type_mods)
 +        if pattern:
 +            regexp = re.compile(pattern, re.VERBOSE)
 +            text = ''.join(contents)
 +            text = regexp.sub(r'$\1$', text)
 +            contents = [ text ]
  
          self.gitStream.write("M %s inline %s\n" % (git_mode, relPath))
  
              if self.stream_file.has_key('depotFile'):
                  self.streamOneP4File(self.stream_file, self.stream_contents)
  
 +    def make_email(self, userid):
 +        if userid in self.users:
 +            return self.users[userid]
 +        else:
 +            return "%s <a@b>" % userid
 +
      def commit(self, details, files, branch, branchPrefixes, parent = ""):
          epoch = details["time"]
          author = details["user"]
          committer = ""
          if author not in self.users:
              self.getUserMapFromPerforceServer()
 -        if author in self.users:
 -            committer = "%s %s %s" % (self.users[author], epoch, self.tz)
 -        else:
 -            committer = "%s <a@b> %s %s" % (author, epoch, self.tz)
 +        committer = "%s %s %s" % (self.make_email(author), epoch, self.tz)
  
          self.gitStream.write("committer %s\n" % committer)
  
                      self.gitStream.write("from %s\n" % branch)
  
                      owner = labelDetails["Owner"]
 -                    tagger = ""
 -                    if author in self.users:
 -                        tagger = "%s %s %s" % (self.users[owner], epoch, self.tz)
 +
 +                    # Try to use the owner of the p4 label, or failing that,
 +                    # the current p4 user id.
 +                    if owner:
 +                        email = self.make_email(owner)
                      else:
 -                        tagger = "%s <a@b> %s %s" % (owner, epoch, self.tz)
 +                        email = self.make_email(self.p4UserId())
 +                    tagger = "%s %s %s" % (email, epoch, self.tz)
 +
                      self.gitStream.write("tagger %s\n" % tagger)
 -                    self.gitStream.write("data <<EOT\n")
 -                    self.gitStream.write(labelDetails["Description"])
 -                    self.gitStream.write("EOT\n\n")
 +
 +                    description = labelDetails["Description"]
 +                    self.gitStream.write("data %d\n" % len(description))
 +                    self.gitStream.write(description)
 +                    self.gitStream.write("\n")
  
                  else:
                      if not self.silent:
      def getLabels(self):
          self.labels = {}
  
 -        l = p4CmdList("labels %s..." % ' '.join (self.depotPaths))
 +        l = p4CmdList(["labels"] + ["%s..." % p for p in self.depotPaths])
          if len(l) > 0 and not self.silent:
              print "Finding files belonging to labels in %s" % `self.depotPaths`
  
              command = "branches"
  
          for info in p4CmdList(command):
 -            details = p4Cmd("branch -o %s" % info["branch"])
 +            details = p4Cmd(["branch", "-o", info["branch"]])
              viewIdx = 0
              while details.has_key("View%s" % viewIdx):
                  paths = details["View%s" % viewIdx].split(" ")
          sourceRef = self.gitRefForBranch(sourceBranch)
          #print "source " + sourceBranch
  
 -        branchParentChange = int(p4Cmd("changes -m 1 %s...@1,%s" % (sourceDepotPath, firstChange))["change"])
 +        branchParentChange = int(p4Cmd(["changes", "-m", "1", "%s...@1,%s" % (sourceDepotPath, firstChange)])["change"])
          #print "branch parent: %s" % branchParentChange
          gitParent = self.gitCommitByP4Change(sourceRef, branchParentChange)
          if len(gitParent) > 0:
          self.importChanges(changes)
          return True
  
 +    def searchParent(self, parent, branch, target):
 +        parentFound = False
 +        for blob in read_pipe_lines(["git", "rev-list", "--reverse", "--no-merges", parent]):
 +            blob = blob.strip()
 +            if len(read_pipe(["git", "diff-tree", blob, target])) == 0:
 +                parentFound = True
 +                if self.verbose:
 +                    print "Found parent of %s in commit %s" % (branch, blob)
 +                break
 +        if parentFound:
 +            return blob
 +        else:
 +            return None
 +
      def importChanges(self, changes):
          cnt = 1
          for change in changes:
 -            description = p4Cmd("describe %s" % change)
 +            description = p4Cmd(["describe", str(change)])
              self.updateOptionDict(description)
  
              if not self.silent:
                              parent = self.initialParents[branch]
                              del self.initialParents[branch]
  
 -                        self.commit(description, filesForCommit, branch, [branchPrefix], parent)
 +                        blob = None
 +                        if len(parent) > 0:
 +                            tempBranch = os.path.join(self.tempBranchLocation, "%d" % (change))
 +                            if self.verbose:
 +                                print "Creating temporary branch: " + tempBranch
 +                            self.commit(description, filesForCommit, tempBranch, [branchPrefix])
 +                            self.tempBranches.append(tempBranch)
 +                            self.checkpoint()
 +                            blob = self.searchParent(parent, branch, tempBranch)
 +                        if blob:
 +                            self.commit(description, filesForCommit, branch, [branchPrefix], blob)
 +                        else:
 +                            if self.verbose:
 +                                print "Parent of %s not found. Committing into head of %s" % (branch, parent)
 +                            self.commit(description, filesForCommit, branch, [branchPrefix], parent)
                  else:
                      files = self.extractFilesFromCommit(description)
                      self.commit(description, files, self.branch, self.depotPaths,
              print self.gitError.read()
  
  
-     def getClientSpec(self):
-         specList = p4CmdList("client -o")
-         if len(specList) != 1:
-             die('Output from "client -o" is %d lines, expecting 1' %
-                 len(specList))
-         # dictionary of all client parameters
-         entry = specList[0]
-         # just the keys that start with "View"
-         view_keys = [ k for k in entry.keys() if k.startswith("View") ]
-         # hold this new View
-         view = View()
-         # append the lines, in order, to the view
-         for view_num in range(len(view_keys)):
-             k = "View%d" % view_num
-             if k not in view_keys:
-                 die("Expected view key %s missing" % k)
-             view.append(entry[k])
-         self.clientSpecDirs = view
-         if self.verbose:
-             for i, m in enumerate(self.clientSpecDirs.mappings):
-                     print "clientSpecDirs %d: %s" % (i, str(m))
      def run(self, args):
          self.depotPaths = []
          self.changeRange = ""
              if not gitBranchExists(self.refPrefix + "HEAD") and self.importIntoRemotes and gitBranchExists(self.branch):
                  system("git symbolic-ref %sHEAD %s" % (self.refPrefix, self.branch))
  
-         if not self.useClientSpec:
+         # accept either the command-line option, or the configuration variable
+         if self.useClientSpec:
+             # will use this after clone to set the variable
+             self.useClientSpec_from_options = True
+         else:
              if gitConfig("git-p4.useclientspec", "--bool") == "true":
                  self.useClientSpec = True
          if self.useClientSpec:
-             self.getClientSpec()
+             self.clientSpecDirs = getClientSpec()
  
          # TODO: should always look at previous commits,
          # merge with previous imports, if possible.
          self.gitOutput.close()
          self.gitError.close()
  
 +        # Cleanup temporary branches created during import
 +        if self.tempBranches != []:
 +            for branch in self.tempBranches:
 +                read_pipe("git update-ref -d %s" % branch)
 +            os.rmdir(os.path.join(os.environ.get("GIT_DIR", ".git"), self.tempBranchLocation))
 +
          return True
  
  class P4Rebase(Command):
@@@ -2607,6 -2482,10 +2634,10 @@@ class P4Clone(P4Sync)
              else:
                  print "Could not detect main branch. No checkout/master branch created."
  
+         # auto-set this variable if invoked with --use-client-spec
+         if self.useClientSpec_from_options:
+             system("git config --bool git-p4.useclientspec true")
          return True
  
  class P4Branches(Command):
diff --combined git-am.sh
index 64d8e2a64ddd2e6162e7a8fd93ab4294de3f161e,f43a75b04bf692b9ff6acaae0f6bbe51369aa399..906f91f1884a2df1b55dd00a229899ec6a47bb6e
+++ b/git-am.sh
@@@ -15,7 -15,6 +15,7 @@@ q,quiet         be quie
  s,signoff       add a Signed-off-by line to the commit message
  u,utf8          recode into utf8 (default)
  k,keep          pass -k flag to git-mailinfo
 +keep-non-patch  pass -b flag to git-mailinfo
  keep-cr         pass --keep-cr flag to git-mailsplit for mbox format
  no-keep-cr      do not pass --keep-cr flag to git-mailsplit independent of am.keepcr
  c,scissors      strip everything before a scissors line
@@@ -202,7 -201,7 +202,7 @@@ check_patch_format () 
                l1=
                while test -z "$l1"
                do
-                       read l1
+                       read l1 || break
                done
                read l2
                read l3
@@@ -388,8 -387,6 +388,8 @@@ d
                utf8= ;;
        -k|--keep)
                keep=t ;;
 +      --keep-non-patch)
 +              keep=b ;;
        -c|--scissors)
                scissors=t ;;
        --no-scissors)
@@@ -568,25 -565,16 +568,25 @@@ case "$resolved" i
        fi
  esac
  
 +# Now, decide what command line options we will give to the git
 +# commands we invoke, based on the result of parsing command line
 +# options and previous invocation state stored in $dotest/ files.
 +
  if test "$(cat "$dotest/utf8")" = t
  then
        utf8=-u
  else
        utf8=-n
  fi
 -if test "$(cat "$dotest/keep")" = t
 -then
 -      keep=-k
 -fi
 +keep=$(cat "$dotest/keep")
 +case "$keep" in
 +t)
 +      keep=-k ;;
 +b)
 +      keep=-b ;;
 +*)
 +      keep= ;;
 +esac
  case "$(cat "$dotest/scissors")" in
  t)
        scissors=--scissors ;;
diff --combined t/t4150-am.sh
index f1b60b8560ce1b27497d9e0e08af007159e04d1d,00d669a3c9bbf3329cf800fc15147770a45e6a4b..6f77fffee60b5e37140dc8952c95144035ed457a
@@@ -237,7 -237,7 +237,7 @@@ test_expect_success 'am stays in branch
  
  test_expect_success 'am --signoff does not add Signed-off-by: line if already there' '
        git format-patch --stdout HEAD^ >patch3 &&
 -      sed -e "/^Subject/ s,\[PATCH,Re: Re: Re: & 1/5 v2," patch3 >patch4 &&
 +      sed -e "/^Subject/ s,\[PATCH,Re: Re: Re: & 1/5 v2] [foo," patch3 >patch4 &&
        rm -fr .git/rebase-apply &&
        git reset --hard &&
        git checkout HEAD^ &&
@@@ -259,17 -259,7 +259,17 @@@ test_expect_success 'am --keep really k
        git am --keep patch4 &&
        ! test -d .git/rebase-apply &&
        git cat-file commit HEAD >actual &&
 -      grep "Re: Re: Re: \[PATCH 1/5 v2\] third" actual
 +      grep "Re: Re: Re: \[PATCH 1/5 v2\] \[foo\] third" actual
 +'
 +
 +test_expect_success 'am --keep-non-patch really keeps the non-patch part' '
 +      rm -fr .git/rebase-apply &&
 +      git reset --hard &&
 +      git checkout HEAD^ &&
 +      git am --keep-non-patch patch4 &&
 +      ! test -d .git/rebase-apply &&
 +      git cat-file commit HEAD >actual &&
 +      grep "^\[foo\] third" actual
  '
  
  test_expect_success 'am -3 falls back to 3-way merge' '
@@@ -505,4 -495,14 +505,14 @@@ test_expect_success 'am -q is quiet' 
        ! test -s output.out
  '
  
+ test_expect_success 'am empty-file does not infloop' '
+       rm -fr .git/rebase-apply &&
+       git reset --hard &&
+       touch empty-file &&
+       test_tick &&
+       { git am empty-file > actual 2>&1 && false || :; } &&
+       echo Patch format detection failed. >expected &&
+       test_cmp expected actual
+ '
  test_done
index ae9145e307dc1a3bc9ce14ccfac9bb1392b7d4ee,b0c6d4391daa041cf379f402704fb0989849e727..773a516ff0f40d396cb04cc474c697617192ae71
@@@ -31,7 -31,7 +31,7 @@@ client_view() 
  #
  check_files_exist() {
        ok=0 &&
-       num=${#@} &&
+       num=$# &&
        for arg ; do
                test_path_is_file "$arg" &&
                ok=$(($ok + 1))
@@@ -71,20 -71,24 +71,24 @@@ git_verify() 
  #   - dir2
  #     - file21
  #     - file22
+ init_depot() {
+       for d in 1 2 ; do
+               mkdir -p dir$d &&
+               for f in 1 2 ; do
+                       echo dir$d/file$d$f >dir$d/file$d$f &&
+                       p4 add dir$d/file$d$f &&
+                       p4 submit -d "dir$d/file$d$f"
+               done
+       done &&
+       find . -type f ! -name files >files &&
+       check_files_exist dir1/file11 dir1/file12 \
+                         dir2/file21 dir2/file22
+ }
  test_expect_success 'init depot' '
        (
                cd "$cli" &&
-               for d in 1 2 ; do
-                       mkdir -p dir$d &&
-                       for f in 1 2 ; do
-                               echo dir$d/file$d$f >dir$d/file$d$f &&
-                               p4 add dir$d/file$d$f &&
-                               p4 submit -d "dir$d/file$d$f"
-                       done
-               done &&
-               find . -type f ! -name files >files &&
-               check_files_exist dir1/file11 dir1/file12 \
-                                 dir2/file21 dir2/file22
+               init_depot
        )
  '
  
@@@ -101,18 -105,12 +105,18 @@@ test_expect_success 'unsupported view w
        test_must_fail "$GITP4" clone --use-client-spec --dest="$git" //depot
  '
  
 -test_expect_success 'wildcard ... only supported at end of spec' '
 +test_expect_success 'wildcard ... only supported at end of spec 1' '
        client_view "//depot/.../file11 //client/.../file11" &&
        test_when_finished cleanup_git &&
        test_must_fail "$GITP4" clone --use-client-spec --dest="$git" //depot
  '
  
 +test_expect_success 'wildcard ... only supported at end of spec 2' '
 +      client_view "//depot/.../a/... //client/.../a/..." &&
 +      test_when_finished cleanup_git &&
 +      test_must_fail "$GITP4" clone --use-client-spec --dest="$git" //depot
 +'
 +
  test_expect_success 'basic map' '
        client_view "//depot/dir1/... //client/cli1/..." &&
        files="cli1/file11 cli1/file12" &&
@@@ -246,393 -244,139 +250,526 @@@ test_expect_success 'quotes on rhs only
        git_verify "cdir 1/file11" "cdir 1/file12"
  '
  
+ #
+ # Submit tests
+ #
+ # clone sets variable
+ test_expect_success 'clone --use-client-spec sets useClientSpec' '
+       client_view "//depot/... //client/..." &&
+       test_when_finished cleanup_git &&
+       "$GITP4" clone --use-client-spec --dest="$git" //depot &&
+       (
+               cd "$git" &&
+               git config --bool git-p4.useClientSpec >actual &&
+               echo true >true &&
+               test_cmp actual true
+       )
+ '
+ # clone just a subdir of the client spec
+ test_expect_success 'subdir clone' '
+       client_view "//depot/... //client/..." &&
+       files="dir1/file11 dir1/file12 dir2/file21 dir2/file22" &&
+       client_verify $files &&
+       test_when_finished cleanup_git &&
+       "$GITP4" clone --use-client-spec --dest="$git" //depot/dir1 &&
+       git_verify dir1/file11 dir1/file12
+ '
+ #
+ # submit back, see what happens:  five cases
+ #
+ test_expect_success 'subdir clone, submit modify' '
+       client_view "//depot/... //client/..." &&
+       test_when_finished cleanup_git &&
+       "$GITP4" clone --use-client-spec --dest="$git" //depot/dir1 &&
+       (
+               cd "$git" &&
+               git config git-p4.skipSubmitEdit true &&
+               echo line >>dir1/file12 &&
+               git add dir1/file12 &&
+               git commit -m dir1/file12 &&
+               "$GITP4" submit
+       ) &&
+       (
+               cd "$cli" &&
+               test_path_is_file dir1/file12 &&
+               test_line_count = 2 dir1/file12
+       )
+ '
+ test_expect_success 'subdir clone, submit add' '
+       client_view "//depot/... //client/..." &&
+       test_when_finished cleanup_git &&
+       "$GITP4" clone --use-client-spec --dest="$git" //depot/dir1 &&
+       (
+               cd "$git" &&
+               git config git-p4.skipSubmitEdit true &&
+               echo file13 >dir1/file13 &&
+               git add dir1/file13 &&
+               git commit -m dir1/file13 &&
+               "$GITP4" submit
+       ) &&
+       (
+               cd "$cli" &&
+               test_path_is_file dir1/file13
+       )
+ '
+ test_expect_success 'subdir clone, submit delete' '
+       client_view "//depot/... //client/..." &&
+       test_when_finished cleanup_git &&
+       "$GITP4" clone --use-client-spec --dest="$git" //depot/dir1 &&
+       (
+               cd "$git" &&
+               git config git-p4.skipSubmitEdit true &&
+               git rm dir1/file12 &&
+               git commit -m "delete dir1/file12" &&
+               "$GITP4" submit
+       ) &&
+       (
+               cd "$cli" &&
+               test_path_is_missing dir1/file12
+       )
+ '
+ test_expect_success 'subdir clone, submit copy' '
+       client_view "//depot/... //client/..." &&
+       test_when_finished cleanup_git &&
+       "$GITP4" clone --use-client-spec --dest="$git" //depot/dir1 &&
+       (
+               cd "$git" &&
+               git config git-p4.skipSubmitEdit true &&
+               git config git-p4.detectCopies true &&
+               cp dir1/file11 dir1/file11a &&
+               git add dir1/file11a &&
+               git commit -m "copy to dir1/file11a" &&
+               "$GITP4" submit
+       ) &&
+       (
+               cd "$cli" &&
+               test_path_is_file dir1/file11a
+       )
+ '
+ test_expect_success 'subdir clone, submit rename' '
+       client_view "//depot/... //client/..." &&
+       test_when_finished cleanup_git &&
+       "$GITP4" clone --use-client-spec --dest="$git" //depot/dir1 &&
+       (
+               cd "$git" &&
+               git config git-p4.skipSubmitEdit true &&
+               git config git-p4.detectRenames true &&
+               git mv dir1/file13 dir1/file13a &&
+               git commit -m "rename dir1/file13 to dir1/file13a" &&
+               "$GITP4" submit
+       ) &&
+       (
+               cd "$cli" &&
+               test_path_is_missing dir1/file13 &&
+               test_path_is_file dir1/file13a
+       )
+ '
+ test_expect_success 'reinit depot' '
+       (
+               cd "$cli" &&
+               p4 sync -f &&
+               rm files &&
+               p4 delete */* &&
+               p4 submit -d "delete all files" &&
+               init_depot
+       )
+ '
 +#
 +# What happens when two files of the same name are overlayed together?
 +# The last-listed file should take preference.
 +#
 +# //depot
 +#   - dir1
 +#     - file11
 +#     - file12
 +#     - filecollide
 +#   - dir2
 +#     - file21
 +#     - file22
 +#     - filecollide
 +#
 +test_expect_success 'overlay collision setup' '
 +      client_view "//depot/... //client/..." &&
 +      (
 +              cd "$cli" &&
 +              p4 sync &&
 +              echo dir1/filecollide >dir1/filecollide &&
 +              p4 add dir1/filecollide &&
 +              p4 submit -d dir1/filecollide &&
 +              echo dir2/filecollide >dir2/filecollide &&
 +              p4 add dir2/filecollide &&
 +              p4 submit -d dir2/filecollide
 +      )
 +'
 +
 +test_expect_success 'overlay collision 1 to 2' '
 +      client_view "//depot/dir1/... //client/..." \
 +                  "+//depot/dir2/... //client/..." &&
 +      files="file11 file12 file21 file22 filecollide" &&
 +      echo dir2/filecollide >actual &&
 +      client_verify $files &&
 +      test_cmp actual "$cli"/filecollide &&
 +      test_when_finished cleanup_git &&
 +      "$GITP4" clone --use-client-spec --dest="$git" //depot &&
 +      git_verify $files &&
 +      test_cmp actual "$git"/filecollide
 +'
 +
 +test_expect_failure 'overlay collision 2 to 1' '
 +      client_view "//depot/dir2/... //client/..." \
 +                  "+//depot/dir1/... //client/..." &&
 +      files="file11 file12 file21 file22 filecollide" &&
 +      echo dir1/filecollide >actual &&
 +      client_verify $files &&
 +      test_cmp actual "$cli"/filecollide &&
 +      test_when_finished cleanup_git &&
 +      "$GITP4" clone --use-client-spec --dest="$git" //depot &&
 +      git_verify $files &&
 +      test_cmp actual "$git"/filecollide
 +'
 +
 +test_expect_success 'overlay collision delete 2' '
 +      client_view "//depot/... //client/..." &&
 +      (
 +              cd "$cli" &&
 +              p4 sync &&
 +              p4 delete dir2/filecollide &&
 +              p4 submit -d "remove dir2/filecollide"
 +      )
 +'
 +
 +# no filecollide, got deleted with dir2
 +test_expect_failure 'overlay collision 1 to 2, but 2 deleted' '
 +      client_view "//depot/dir1/... //client/..." \
 +                  "+//depot/dir2/... //client/..." &&
 +      files="file11 file12 file21 file22" &&
 +      client_verify $files &&
 +      test_when_finished cleanup_git &&
 +      "$GITP4" clone --use-client-spec --dest="$git" //depot &&
 +      git_verify $files
 +'
 +
 +test_expect_success 'overlay collision update 1' '
 +      client_view "//depot/dir1/... //client/dir1/..." &&
 +      (
 +              cd "$cli" &&
 +              p4 sync &&
 +              p4 open dir1/filecollide &&
 +              echo dir1/filecollide update >dir1/filecollide &&
 +              p4 submit -d "update dir1/filecollide"
 +      )
 +'
 +
 +# still no filecollide, dir2 still wins with the deletion even though the
 +# change to dir1 is more recent
 +test_expect_failure 'overlay collision 1 to 2, but 2 deleted, then 1 updated' '
 +      client_view "//depot/dir1/... //client/..." \
 +                  "+//depot/dir2/... //client/..." &&
 +      files="file11 file12 file21 file22" &&
 +      client_verify $files &&
 +      test_when_finished cleanup_git &&
 +      "$GITP4" clone --use-client-spec --dest="$git" //depot &&
 +      git_verify $files
 +'
 +
 +test_expect_success 'overlay collision delete filecollides' '
 +      client_view "//depot/... //client/..." &&
 +      (
 +              cd "$cli" &&
 +              p4 sync &&
 +              p4 delete dir1/filecollide dir2/filecollide &&
 +              p4 submit -d "remove filecollides"
 +      )
 +'
 +
 +#
 +# Overlays as part of sync, rather than initial checkout:
 +#   1.  add a file in dir1
 +#   2.  sync to include it
 +#   3.  add same file in dir2
 +#   4.  sync, make sure content switches as dir2 has priority
 +#   5.  add another file in dir1
 +#   6.  sync
 +#   7.  add/delete same file in dir2
 +#   8.  sync, make sure it disappears, again dir2 wins
 +#   9.  cleanup
 +#
 +# //depot
 +#   - dir1
 +#     - file11
 +#     - file12
 +#     - colA
 +#     - colB
 +#   - dir2
 +#     - file21
 +#     - file22
 +#     - colA
 +#     - colB
 +#
 +test_expect_success 'overlay sync: add colA in dir1' '
 +      client_view "//depot/dir1/... //client/dir1/..." &&
 +      (
 +              cd "$cli" &&
 +              p4 sync &&
 +              echo dir1/colA >dir1/colA &&
 +              p4 add dir1/colA &&
 +              p4 submit -d dir1/colA
 +      )
 +'
 +
 +test_expect_success 'overlay sync: initial git checkout' '
 +      client_view "//depot/dir1/... //client/..." \
 +                  "+//depot/dir2/... //client/..." &&
 +      files="file11 file12 file21 file22 colA" &&
 +      echo dir1/colA >actual &&
 +      client_verify $files &&
 +      test_cmp actual "$cli"/colA &&
 +      "$GITP4" clone --use-client-spec --dest="$git" //depot &&
 +      git_verify $files &&
 +      test_cmp actual "$git"/colA
 +'
 +
 +test_expect_success 'overlay sync: add colA in dir2' '
 +      client_view "//depot/dir2/... //client/dir2/..." &&
 +      (
 +              cd "$cli" &&
 +              p4 sync &&
 +              echo dir2/colA >dir2/colA &&
 +              p4 add dir2/colA &&
 +              p4 submit -d dir2/colA
 +      )
 +'
 +
 +test_expect_success 'overlay sync: colA content switch' '
 +      client_view "//depot/dir1/... //client/..." \
 +                  "+//depot/dir2/... //client/..." &&
 +      files="file11 file12 file21 file22 colA" &&
 +      echo dir2/colA >actual &&
 +      client_verify $files &&
 +      test_cmp actual "$cli"/colA &&
 +      (
 +              cd "$git" &&
 +              "$GITP4" sync --use-client-spec &&
 +              git merge --ff-only p4/master
 +      ) &&
 +      git_verify $files &&
 +      test_cmp actual "$git"/colA
 +'
 +
 +test_expect_success 'overlay sync: add colB in dir1' '
 +      client_view "//depot/dir1/... //client/dir1/..." &&
 +      (
 +              cd "$cli" &&
 +              p4 sync &&
 +              echo dir1/colB >dir1/colB &&
 +              p4 add dir1/colB &&
 +              p4 submit -d dir1/colB
 +      )
 +'
 +
 +test_expect_success 'overlay sync: colB appears' '
 +      client_view "//depot/dir1/... //client/..." \
 +                  "+//depot/dir2/... //client/..." &&
 +      files="file11 file12 file21 file22 colA colB" &&
 +      echo dir1/colB >actual &&
 +      client_verify $files &&
 +      test_cmp actual "$cli"/colB &&
 +      (
 +              cd "$git" &&
 +              "$GITP4" sync --use-client-spec &&
 +              git merge --ff-only p4/master
 +      ) &&
 +      git_verify $files &&
 +      test_cmp actual "$git"/colB
 +'
 +
 +test_expect_success 'overlay sync: add/delete colB in dir2' '
 +      client_view "//depot/dir2/... //client/dir2/..." &&
 +      (
 +              cd "$cli" &&
 +              p4 sync &&
 +              echo dir2/colB >dir2/colB &&
 +              p4 add dir2/colB &&
 +              p4 submit -d dir2/colB &&
 +              p4 delete dir2/colB &&
 +              p4 submit -d "delete dir2/colB"
 +      )
 +'
 +
 +test_expect_success 'overlay sync: colB disappears' '
 +      client_view "//depot/dir1/... //client/..." \
 +                  "+//depot/dir2/... //client/..." &&
 +      files="file11 file12 file21 file22 colA" &&
 +      client_verify $files &&
 +      test_when_finished cleanup_git &&
 +      (
 +              cd "$git" &&
 +              "$GITP4" sync --use-client-spec &&
 +              git merge --ff-only p4/master
 +      ) &&
 +      git_verify $files
 +'
 +
 +test_expect_success 'overlay sync: cleanup' '
 +      client_view "//depot/... //client/..." &&
 +      (
 +              cd "$cli" &&
 +              p4 sync &&
 +              p4 delete dir1/colA dir2/colA dir1/colB &&
 +              p4 submit -d "remove overlay sync files"
 +      )
 +'
 +
 +#
 +# Overlay tests again, but swapped so dir1 has priority.
 +#   1.  add a file in dir1
 +#   2.  sync to include it
 +#   3.  add same file in dir2
 +#   4.  sync, make sure content does not switch
 +#   5.  add another file in dir1
 +#   6.  sync
 +#   7.  add/delete same file in dir2
 +#   8.  sync, make sure it is still there
 +#   9.  cleanup
 +#
 +# //depot
 +#   - dir1
 +#     - file11
 +#     - file12
 +#     - colA
 +#     - colB
 +#   - dir2
 +#     - file21
 +#     - file22
 +#     - colA
 +#     - colB
 +#
 +test_expect_success 'overlay sync swap: add colA in dir1' '
 +      client_view "//depot/dir1/... //client/dir1/..." &&
 +      (
 +              cd "$cli" &&
 +              p4 sync &&
 +              echo dir1/colA >dir1/colA &&
 +              p4 add dir1/colA &&
 +              p4 submit -d dir1/colA
 +      )
 +'
 +
 +test_expect_success 'overlay sync swap: initial git checkout' '
 +      client_view "//depot/dir2/... //client/..." \
 +                  "+//depot/dir1/... //client/..." &&
 +      files="file11 file12 file21 file22 colA" &&
 +      echo dir1/colA >actual &&
 +      client_verify $files &&
 +      test_cmp actual "$cli"/colA &&
 +      "$GITP4" clone --use-client-spec --dest="$git" //depot &&
 +      git_verify $files &&
 +      test_cmp actual "$git"/colA
 +'
 +
 +test_expect_success 'overlay sync swap: add colA in dir2' '
 +      client_view "//depot/dir2/... //client/dir2/..." &&
 +      (
 +              cd "$cli" &&
 +              p4 sync &&
 +              echo dir2/colA >dir2/colA &&
 +              p4 add dir2/colA &&
 +              p4 submit -d dir2/colA
 +      )
 +'
 +
 +test_expect_failure 'overlay sync swap: colA no content switch' '
 +      client_view "//depot/dir2/... //client/..." \
 +                  "+//depot/dir1/... //client/..." &&
 +      files="file11 file12 file21 file22 colA" &&
 +      echo dir1/colA >actual &&
 +      client_verify $files &&
 +      test_cmp actual "$cli"/colA &&
 +      (
 +              cd "$git" &&
 +              "$GITP4" sync --use-client-spec &&
 +              git merge --ff-only p4/master
 +      ) &&
 +      git_verify $files &&
 +      test_cmp actual "$git"/colA
 +'
 +
 +test_expect_success 'overlay sync swap: add colB in dir1' '
 +      client_view "//depot/dir1/... //client/dir1/..." &&
 +      (
 +              cd "$cli" &&
 +              p4 sync &&
 +              echo dir1/colB >dir1/colB &&
 +              p4 add dir1/colB &&
 +              p4 submit -d dir1/colB
 +      )
 +'
 +
 +test_expect_success 'overlay sync swap: colB appears' '
 +      client_view "//depot/dir2/... //client/..." \
 +                  "+//depot/dir1/... //client/..." &&
 +      files="file11 file12 file21 file22 colA colB" &&
 +      echo dir1/colB >actual &&
 +      client_verify $files &&
 +      test_cmp actual "$cli"/colB &&
 +      (
 +              cd "$git" &&
 +              "$GITP4" sync --use-client-spec &&
 +              git merge --ff-only p4/master
 +      ) &&
 +      git_verify $files &&
 +      test_cmp actual "$git"/colB
 +'
 +
 +test_expect_success 'overlay sync swap: add/delete colB in dir2' '
 +      client_view "//depot/dir2/... //client/dir2/..." &&
 +      (
 +              cd "$cli" &&
 +              p4 sync &&
 +              echo dir2/colB >dir2/colB &&
 +              p4 add dir2/colB &&
 +              p4 submit -d dir2/colB &&
 +              p4 delete dir2/colB &&
 +              p4 submit -d "delete dir2/colB"
 +      )
 +'
 +
 +test_expect_failure 'overlay sync swap: colB no change' '
 +      client_view "//depot/dir2/... //client/..." \
 +                  "+//depot/dir1/... //client/..." &&
 +      files="file11 file12 file21 file22 colA colB" &&
 +      echo dir1/colB >actual &&
 +      client_verify $files &&
 +      test_cmp actual "$cli"/colB &&
 +      test_when_finished cleanup_git &&
 +      (
 +              cd "$git" &&
 +              "$GITP4" sync --use-client-spec &&
 +              git merge --ff-only p4/master
 +      ) &&
 +      git_verify $files &&
 +      test_cmp actual "$cli"/colB
 +'
 +
 +test_expect_success 'overlay sync swap: cleanup' '
 +      client_view "//depot/... //client/..." &&
 +      (
 +              cd "$cli" &&
 +              p4 sync &&
 +              p4 delete dir1/colA dir2/colA dir1/colB &&
 +              p4 submit -d "remove overlay sync files"
 +      )
 +'
 +
  #
  # Rename directories to test quoting in depot-side mappings
  # //depot