Merge branch 'mk/grep-pcre'
authorJunio C Hamano <gitster@pobox.com>
Mon, 30 May 2011 07:00:07 +0000 (00:00 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 30 May 2011 07:00:07 +0000 (00:00 -0700)
* mk/grep-pcre:
git-grep: Fix problems with recently added tests
git-grep: Update tests (mainly for -P)
Makefile: Pass USE_LIBPCRE down in GIT-BUILD-OPTIONS
git-grep: update tests now regexp type is "last one wins"
git-grep: do not die upon -F/-P when grep.extendedRegexp is set.
git-grep: Bail out when -P is used with -F or -E
grep: Add basic tests
configure: Check for libpcre
git-grep: Learn PCRE
grep: Extract compile_regexp_failed() from compile_regexp()
grep: Fix a typo in a comment
grep: Put calls to fixmatch() and regmatch() into patmatch()
contrib/completion: --line-number to git grep
Documentation: Add --line-number to git-grep synopsis

1  2 
Makefile
builtin/grep.c
contrib/completion/git-completion.bash
t/README
t/test-lib.sh
diff --combined Makefile
index edd78419ff85e08b2e3f0e7c2b45654f631a9116,c449f5d6deae63e199575a525325a435c5a5ef4e..e40ac0c7f5ec2f304b88d92f47ff94272f5ce2a4
+++ b/Makefile
@@@ -24,6 -24,12 +24,12 @@@ all:
  # Define NO_OPENSSL environment variable if you do not have OpenSSL.
  # This also implies BLK_SHA1.
  #
+ # Define USE_LIBPCRE if you have and want to use libpcre. git-grep will be
+ # able to use Perl-compatible regular expressions.
+ #
+ # Define LIBPCREDIR=/foo/bar if your libpcre header and library files are in
+ # /foo/bar/include and /foo/bar/lib directories.
+ #
  # Define NO_CURL if you do not have libcurl installed.  git-http-pull and
  # git-http-push are not built, and you cannot use http:// and https://
  # transports.
@@@ -70,9 -76,6 +76,9 @@@
  # Define NO_FNMATCH_CASEFOLD if your fnmatch function doesn't have the
  # FNM_CASEFOLD GNU extension.
  #
 +# Define NO_GECOS_IN_PWENT if you don't have pw_gecos in struct passwd
 +# in the C library.
 +#
  # Define NO_LIBGEN_H if you don't have libgen.h.
  #
  # Define NEEDS_LIBGEN if your libgen needs -lgen when linking
@@@ -277,7 -280,8 +283,7 @@@ STRIP ?= stri
  #   mandir
  #   infodir
  #   htmldir
 -#   ETC_GITCONFIG (but not sysconfdir)
 -#   ETC_GITATTRIBUTES
 +#   sysconfdir
  # can be specified as a relative path some/where/else;
  # this is interpreted as relative to $(prefix) and "git" at
  # runtime figures out where they are based on the path to the executable.
@@@ -293,8 -297,15 +299,8 @@@ sharedir = $(prefix)/shar
  gitwebdir = $(sharedir)/gitweb
  template_dir = share/git-core/templates
  htmldir = share/doc/git-doc
 -ifeq ($(prefix),/usr)
 -sysconfdir = /etc
  ETC_GITCONFIG = $(sysconfdir)/gitconfig
  ETC_GITATTRIBUTES = $(sysconfdir)/gitattributes
 -else
 -sysconfdir = $(prefix)/etc
 -ETC_GITCONFIG = etc/gitconfig
 -ETC_GITATTRIBUTES = etc/gitattributes
 -endif
  lib = lib
  # DESTDIR=
  pathsep = :
@@@ -318,7 -329,9 +324,7 @@@ GCOV = gco
  
  export TCL_PATH TCLTK_PATH
  
 -# sparse is architecture-neutral, which means that we need to tell it
 -# explicitly what architecture to check for. Fix this up for yours..
 -SPARSE_FLAGS = -D__BIG_ENDIAN__ -D__powerpc__
 +SPARSE_FLAGS =
  
  
  
@@@ -363,6 -376,7 +369,6 @@@ SCRIPT_SH += git-merge-resolve.s
  SCRIPT_SH += git-mergetool.sh
  SCRIPT_SH += git-pull.sh
  SCRIPT_SH += git-quiltimport.sh
 -SCRIPT_SH += git-rebase--interactive.sh
  SCRIPT_SH += git-rebase.sh
  SCRIPT_SH += git-repack.sh
  SCRIPT_SH += git-request-pull.sh
@@@ -372,11 -386,7 +378,11 @@@ SCRIPT_SH += git-web--browse.s
  
  SCRIPT_LIB += git-mergetool--lib
  SCRIPT_LIB += git-parse-remote
 +SCRIPT_LIB += git-rebase--am
 +SCRIPT_LIB += git-rebase--interactive
 +SCRIPT_LIB += git-rebase--merge
  SCRIPT_LIB += git-sh-setup
 +SCRIPT_LIB += git-sh-i18n
  
  SCRIPT_PERL += git-add--interactive.perl
  SCRIPT_PERL += git-difftool.perl
@@@ -410,7 -420,6 +416,7 @@@ PROGRAM_OBJS += shell.
  PROGRAM_OBJS += show-index.o
  PROGRAM_OBJS += upload-pack.o
  PROGRAM_OBJS += http-backend.o
 +PROGRAM_OBJS += sh-i18n--envsubst.o
  
  PROGRAMS += $(patsubst %.o,git-%$X,$(PROGRAM_OBJS))
  
@@@ -420,10 -429,8 +426,10 @@@ TEST_PROGRAMS_NEED_X += test-dat
  TEST_PROGRAMS_NEED_X += test-delta
  TEST_PROGRAMS_NEED_X += test-dump-cache-tree
  TEST_PROGRAMS_NEED_X += test-genrandom
 +TEST_PROGRAMS_NEED_X += test-index-version
  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
@@@ -434,6 -441,8 +440,6 @@@ TEST_PROGRAMS_NEED_X += test-string-poo
  TEST_PROGRAMS_NEED_X += test-subprocess
  TEST_PROGRAMS_NEED_X += test-svn-fe
  TEST_PROGRAMS_NEED_X += test-treap
 -TEST_PROGRAMS_NEED_X += test-index-version
 -TEST_PROGRAMS_NEED_X += test-mktemp
  
  TEST_PROGRAMS = $(patsubst %,%$X,$(TEST_PROGRAMS_NEED_X))
  
@@@ -524,7 -533,6 +530,7 @@@ LIB_H += list-objects.
  LIB_H += ll-merge.h
  LIB_H += log-tree.h
  LIB_H += mailmap.h
 +LIB_H += merge-file.h
  LIB_H += merge-recursive.h
  LIB_H += notes.h
  LIB_H += notes-cache.h
@@@ -545,7 -553,6 +551,7 @@@ LIB_H += rerere.
  LIB_H += resolve-undo.h
  LIB_H += revision.h
  LIB_H += run-command.h
 +LIB_H += sha1-array.h
  LIB_H += sha1-lookup.h
  LIB_H += sideband.h
  LIB_H += sigchain.h
@@@ -648,7 -655,6 +654,7 @@@ LIB_OBJS += revision.
  LIB_OBJS += run-command.o
  LIB_OBJS += server-info.o
  LIB_OBJS += setup.o
 +LIB_OBJS += sha1-array.o
  LIB_OBJS += sha1-lookup.o
  LIB_OBJS += sha1_file.o
  LIB_OBJS += sha1_name.o
@@@ -924,7 -930,6 +930,7 @@@ ifeq ($(uname_O),Cygwin
        X = .exe
        COMPAT_OBJS += compat/cygwin.o
        UNRELIABLE_FSTAT = UnfortunatelyYes
 +      SPARSE_FLAGS = -isystem /usr/include/w32api -Wno-one-bit-signed-bitfield
  endif
  ifeq ($(uname_S),FreeBSD)
        NEEDS_LIBICONV = YesPlease
@@@ -1178,7 -1183,6 +1184,7 @@@ ifneq (,$(findstring MINGW,$(uname_S))
        EXTLIBS += -lws2_32
        PTHREAD_LIBS =
        X = .exe
 +      SPARSE_FLAGS = -Wno-one-bit-signed-bitfield
  ifneq (,$(wildcard ../THIS_IS_MSYSGIT))
        htmldir=doc/git/html/
        prefix =
@@@ -1194,14 -1198,6 +1200,14 @@@ endi
  -include config.mak.autogen
  -include config.mak
  
 +ifndef sysconfdir
 +ifeq ($(prefix),/usr)
 +sysconfdir = /etc
 +else
 +sysconfdir = etc
 +endif
 +endif
 +
  ifdef CHECK_HEADER_DEPENDENCIES
  COMPUTE_HEADER_DEPENDENCIES =
  USE_COMPUTED_HEADER_DEPENDENCIES =
@@@ -1258,6 -1254,15 +1264,15 @@@ ifdef NO_LIBGEN_
        COMPAT_OBJS += compat/basename.o
  endif
  
+ ifdef USE_LIBPCRE
+       BASIC_CFLAGS += -DUSE_LIBPCRE
+       ifdef LIBPCREDIR
+               BASIC_CFLAGS += -I$(LIBPCREDIR)/include
+               EXTLIBS += -L$(LIBPCREDIR)/$(lib) $(CC_LD_DYNPATH)$(LIBPCREDIR)/$(lib)
+       endif
+       EXTLIBS += -lpcre
+ endif
  ifdef NO_CURL
        BASIC_CFLAGS += -DNO_CURL
        REMOTE_CURL_PRIMARY =
@@@ -1590,7 -1595,6 +1605,7 @@@ ifndef 
        QUIET_LNCP     = @echo '   ' LN/CP $@;
        QUIET_XGETTEXT = @echo '   ' XGETTEXT $@;
        QUIET_GCOV     = @echo '   ' GCOV $@;
 +      QUIET_SP       = @echo '   ' SP $<;
        QUIET_SUBDIR0  = +@subdir=
        QUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \
                         $(MAKE) $(PRINT_DIR) -C $$subdir
@@@ -1686,19 -1690,17 +1701,19 @@@ strip: $(PROGRAMS) git$
        $(STRIP) $(STRIP_OPTS) $(PROGRAMS) git$X
  
  git.o: common-cmds.h
 -git.s git.o: EXTRA_CPPFLAGS = -DGIT_VERSION='"$(GIT_VERSION)"' \
 -      '-DGIT_HTML_PATH="$(htmldir_SQ)"'
 +git.sp git.s git.o: EXTRA_CPPFLAGS = -DGIT_VERSION='"$(GIT_VERSION)"' \
 +      '-DGIT_HTML_PATH="$(htmldir_SQ)"' \
 +      '-DGIT_MAN_PATH="$(mandir_SQ)"' \
 +      '-DGIT_INFO_PATH="$(infodir_SQ)"'
  
  git$X: git.o $(BUILTIN_OBJS) $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ git.o \
                $(BUILTIN_OBJS) $(ALL_LDFLAGS) $(LIBS)
  
 -help.o: common-cmds.h
 +help.sp help.o: common-cmds.h
  
 -builtin/help.o: common-cmds.h
 -builtin/help.s builtin/help.o: EXTRA_CPPFLAGS = \
 +builtin/help.sp builtin/help.o: common-cmds.h
 +builtin/help.sp builtin/help.s builtin/help.o: EXTRA_CPPFLAGS = \
        '-DGIT_HTML_PATH="$(htmldir_SQ)"' \
        '-DGIT_MAN_PATH="$(mandir_SQ)"' \
        '-DGIT_INFO_PATH="$(infodir_SQ)"'
@@@ -1760,7 -1762,33 +1775,7 @@@ $(patsubst %.perl,%,$(SCRIPT_PERL)): % 
  gitweb:
        $(QUIET_SUBDIR0)gitweb $(QUIET_SUBDIR1) all
  
 -ifdef JSMIN
 -GITWEB_PROGRAMS += gitweb/static/gitweb.min.js
 -GITWEB_JS = gitweb/static/gitweb.min.js
 -else
 -GITWEB_JS = gitweb/static/gitweb.js
 -endif
 -ifdef CSSMIN
 -GITWEB_PROGRAMS += gitweb/static/gitweb.min.css
 -GITWEB_CSS = gitweb/static/gitweb.min.css
 -else
 -GITWEB_CSS = gitweb/static/gitweb.css
 -endif
 -OTHER_PROGRAMS +=  gitweb/gitweb.cgi  $(GITWEB_PROGRAMS)
 -gitweb/gitweb.cgi: gitweb/gitweb.perl $(GITWEB_PROGRAMS)
 -      $(QUIET_SUBDIR0)gitweb $(QUIET_SUBDIR1) $(patsubst gitweb/%,%,$@)
 -
 -ifdef JSMIN
 -gitweb/static/gitweb.min.js: gitweb/static/gitweb.js
 -      $(QUIET_SUBDIR0)gitweb $(QUIET_SUBDIR1) $(patsubst gitweb/%,%,$@)
 -endif # JSMIN
 -ifdef CSSMIN
 -gitweb/static/gitweb.min.css: gitweb/static/gitweb.css
 -      $(QUIET_SUBDIR0)gitweb $(QUIET_SUBDIR1) $(patsubst gitweb/%,%,$@)
 -endif # CSSMIN
 -
 -
 -git-instaweb: git-instaweb.sh gitweb/gitweb.cgi gitweb/static/gitweb.css gitweb/static/gitweb.js
 +git-instaweb: git-instaweb.sh gitweb
        $(QUIET_GEN)$(RM) $@ $@+ && \
        sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
            -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
@@@ -1958,34 -1986,30 +1973,34 @@@ $(VCSSVN_OBJS) $(VCSSVN_TEST_OBJS): $(L
  test-svn-fe.o: vcs-svn/svndump.h
  endif
  
 -exec_cmd.s exec_cmd.o: EXTRA_CPPFLAGS = \
 +exec_cmd.sp exec_cmd.s exec_cmd.o: EXTRA_CPPFLAGS = \
        '-DGIT_EXEC_PATH="$(gitexecdir_SQ)"' \
        '-DBINDIR="$(bindir_relative_SQ)"' \
        '-DPREFIX="$(prefix_SQ)"'
  
 -builtin/init-db.s builtin/init-db.o: EXTRA_CPPFLAGS = \
 +builtin/init-db.sp builtin/init-db.s builtin/init-db.o: EXTRA_CPPFLAGS = \
        -DDEFAULT_GIT_TEMPLATE_DIR='"$(template_dir_SQ)"'
  
 -config.s config.o: EXTRA_CPPFLAGS = -DETC_GITCONFIG='"$(ETC_GITCONFIG_SQ)"'
 +config.sp config.s config.o: EXTRA_CPPFLAGS = \
 +      -DETC_GITCONFIG='"$(ETC_GITCONFIG_SQ)"'
  
 -attr.s attr.o: EXTRA_CPPFLAGS = -DETC_GITATTRIBUTES='"$(ETC_GITATTRIBUTES_SQ)"'
 +attr.sp attr.s attr.o: EXTRA_CPPFLAGS = \
 +      -DETC_GITATTRIBUTES='"$(ETC_GITATTRIBUTES_SQ)"'
  
 -http.s http.o: EXTRA_CPPFLAGS = -DGIT_HTTP_USER_AGENT='"git/$(GIT_VERSION)"'
 +http.sp http.s http.o: EXTRA_CPPFLAGS = \
 +      -DGIT_HTTP_USER_AGENT='"git/$(GIT_VERSION)"'
  
  ifdef NO_EXPAT
 -http-walker.s http-walker.o: EXTRA_CPPFLAGS = -DNO_EXPAT
 +http-walker.sp http-walker.s http-walker.o: EXTRA_CPPFLAGS = -DNO_EXPAT
  endif
  
  ifdef NO_REGEX
 -compat/regex/regex.o: EXTRA_CPPFLAGS = -DGAWK -DNO_MBSUPPORT
 +compat/regex/regex.sp compat/regex/regex.o: EXTRA_CPPFLAGS = \
 +      -DGAWK -DNO_MBSUPPORT
  endif
  
  ifdef USE_NED_ALLOCATOR
 -compat/nedmalloc/nedmalloc.o: EXTRA_CPPFLAGS = \
 +compat/nedmalloc/nedmalloc.sp compat/nedmalloc/nedmalloc.o: EXTRA_CPPFLAGS = \
        -DNDEBUG -DOVERRIDE_STRDUP -DREPLACE_SYSTEM_ALLOCATOR
  endif
  
@@@ -2044,14 -2068,10 +2059,14 @@@ XGETTEXT_FLAGS = 
        --from-code=UTF-8
  XGETTEXT_FLAGS_C = $(XGETTEXT_FLAGS) --language=C \
        --keyword=_ --keyword=N_ --keyword="Q_:1,2"
 +XGETTEXT_FLAGS_SH = $(XGETTEXT_FLAGS) --language=Shell
  LOCALIZED_C := $(C_OBJ:o=c)
 +LOCALIZED_SH := $(SCRIPT_SH)
  
  po/git.pot: $(LOCALIZED_C)
 -      $(QUIET_XGETTEXT)$(XGETTEXT) -o$@+ $(XGETTEXT_FLAGS_C) $(LOCALIZED_C) && \
 +      $(QUIET_XGETTEXT)$(XGETTEXT) -o$@+ $(XGETTEXT_FLAGS_C) $(LOCALIZED_C)
 +      $(QUIET_XGETTEXT)$(XGETTEXT) -o$@+ --join-existing $(XGETTEXT_FLAGS_SH) \
 +              $(LOCALIZED_SH)
        mv $@+ $@
  
  pot: po/git.pot
@@@ -2089,6 -2109,7 +2104,7 @@@ GIT-BUILD-OPTIONS: FORC
        @echo PYTHON_PATH=\''$(subst ','\'',$(PYTHON_PATH_SQ))'\' >>$@
        @echo TAR=\''$(subst ','\'',$(subst ','\'',$(TAR)))'\' >>$@
        @echo NO_CURL=\''$(subst ','\'',$(subst ','\'',$(NO_CURL)))'\' >>$@
+       @echo USE_LIBPCRE=\''$(subst ','\'',$(subst ','\'',$(USE_LIBPCRE)))'\' >>$@
        @echo NO_PERL=\''$(subst ','\'',$(subst ','\'',$(NO_PERL)))'\' >>$@
        @echo NO_PYTHON=\''$(subst ','\'',$(subst ','\'',$(NO_PYTHON)))'\' >>$@
  ifdef GIT_TEST_CMP
@@@ -2155,20 -2176,13 +2171,20 @@@ test-%$X: test-%.o $(GITLIBS
  check-sha1:: test-sha1$X
        ./test-sha1.sh
  
 +SP_OBJ = $(patsubst %.o,%.sp,$(C_OBJ))
 +
 +$(SP_OBJ): %.sp: %.c GIT-CFLAGS FORCE
 +      $(QUIET_SP)cgcc -no-compile $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) \
 +              $(SPARSE_FLAGS) $<
 +
 +.PHONY: sparse $(SP_OBJ)
 +sparse: $(SP_OBJ)
 +
  check: common-cmds.h
 -      if sparse; \
 +      @if sparse; \
        then \
 -              for i in $(patsubst %.o, %.c, $(GIT_OBJS)); \
 -              do \
 -                      sparse $(ALL_CFLAGS) $(SPARSE_FLAGS) $$i || exit; \
 -              done; \
 +              echo 2>&1 "Use 'make sparse' instead"; \
 +              $(MAKE) --no-print-directory sparse; \
        else \
                echo 2>&1 "Did you mean 'make test'?"; \
                exit 1; \
diff --combined builtin/grep.c
index 931eee0d750cd8662347e2a7c8e8a89d23228e17,298f763b702f80c4f8d9f37b3356d36b2b8da32f..871afaa3c76ea34b67671a6e4a46957a08a946e5
@@@ -533,18 -533,18 +533,18 @@@ static int grep_cache(struct grep_opt *
  static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
                     struct tree_desc *tree, struct strbuf *base, int tn_len)
  {
 -      int hit = 0, matched = 0;
 +      int hit = 0, match = 0;
        struct name_entry entry;
        int old_baselen = base->len;
  
        while (tree_entry(tree, &entry)) {
                int te_len = tree_entry_len(entry.path, entry.sha1);
  
 -              if (matched != 2) {
 -                      matched = tree_entry_interesting(&entry, base, tn_len, pathspec);
 -                      if (matched == -1)
 -                              break; /* no more matches */
 -                      if (!matched)
 +              if (match != 2) {
 +                      match = tree_entry_interesting(&entry, base, tn_len, pathspec);
 +                      if (match < 0)
 +                              break;
 +                      if (match == 0)
                                continue;
                }
  
@@@ -753,6 -753,15 +753,15 @@@ int cmd_grep(int argc, const char **arg
        int i;
        int dummy;
        int use_index = 1;
+       enum {
+               pattern_type_unspecified = 0,
+               pattern_type_bre,
+               pattern_type_ere,
+               pattern_type_fixed,
+               pattern_type_pcre,
+       };
+       int pattern_type = pattern_type_unspecified;
        struct option options[] = {
                OPT_BOOLEAN(0, "cached", &cached,
                        "search in index instead of in the work tree"),
                        "descend at most <depth> levels", PARSE_OPT_NONEG,
                        NULL, 1 },
                OPT_GROUP(""),
-               OPT_BIT('E', "extended-regexp", &opt.regflags,
-                       "use extended POSIX regular expressions", REG_EXTENDED),
-               OPT_NEGBIT('G', "basic-regexp", &opt.regflags,
-                       "use basic POSIX regular expressions (default)",
-                       REG_EXTENDED),
-               OPT_BOOLEAN('F', "fixed-strings", &opt.fixed,
-                       "interpret patterns as fixed strings"),
+               OPT_SET_INT('E', "extended-regexp", &pattern_type,
+                           "use extended POSIX regular expressions",
+                           pattern_type_ere),
+               OPT_SET_INT('G', "basic-regexp", &pattern_type,
+                           "use basic POSIX regular expressions (default)",
+                           pattern_type_bre),
+               OPT_SET_INT('F', "fixed-strings", &pattern_type,
+                           "interpret patterns as fixed strings",
+                           pattern_type_fixed),
+               OPT_SET_INT('P', "perl-regexp", &pattern_type,
+                           "use Perl-compatible regular expressions",
+                           pattern_type_pcre),
                OPT_GROUP(""),
                OPT_BOOLEAN('n', "line-number", &opt.linenum, "show line numbers"),
                OPT_NEGBIT('h', NULL, &opt.pathname, "don't show filenames", 1),
                             PARSE_OPT_KEEP_DASHDASH |
                             PARSE_OPT_STOP_AT_NON_OPTION |
                             PARSE_OPT_NO_INTERNAL_HELP);
+       switch (pattern_type) {
+       case pattern_type_fixed:
+               opt.fixed = 1;
+               opt.pcre = 0;
+               break;
+       case pattern_type_bre:
+               opt.fixed = 0;
+               opt.pcre = 0;
+               opt.regflags &= ~REG_EXTENDED;
+               break;
+       case pattern_type_ere:
+               opt.fixed = 0;
+               opt.pcre = 0;
+               opt.regflags |= REG_EXTENDED;
+               break;
+       case pattern_type_pcre:
+               opt.fixed = 0;
+               opt.pcre = 1;
+               break;
+       default:
+               break; /* nothing */
+       }
  
        if (use_index && !startup_info->have_repository)
                /* die the same way as if we did it at the beginning */
                die(_("no pattern given."));
        if (!opt.fixed && opt.ignore_case)
                opt.regflags |= REG_ICASE;
-       if ((opt.regflags != REG_NEWLINE) && opt.fixed)
-               die(_("cannot mix --fixed-strings and regexp"));
  
  #ifndef NO_PTHREADS
        if (online_cpus() == 1 || !grep_threads_ok(&opt))
                        verify_filename(prefix, argv[j]);
        }
  
 -      if (i < argc)
 -              paths = get_pathspec(prefix, argv + i);
 -      else if (prefix) {
 -              paths = xcalloc(2, sizeof(const char *));
 -              paths[0] = prefix;
 -              paths[1] = NULL;
 -      }
 +      paths = get_pathspec(prefix, argv + i);
        init_pathspec(&pathspec, paths);
        pathspec.max_depth = opt.max_depth;
        pathspec.recursive = 1;
index bb8d7d0878994eb3d53163c330de44d8c0d48b3f,2facd08d1639115188d8e0bcec35c47f4ac793c4..b36290fa6015d9b66dc6a5891ff12f85f279a1c3
@@@ -1,6 -1,6 +1,6 @@@
  #!bash
  #
 -# bash completion support for core Git.
 +# bash/zsh completion support for core Git.
  #
  # Copyright (C) 2006,2007 Shawn O. Pearce <spearce@spearce.org>
  # Conceptually based on gitcompletion (http://gitweb.hawaga.org.uk/).
  # To use these routines:
  #
  #    1) Copy this file to somewhere (e.g. ~/.git-completion.sh).
 -#    2) Added the following line to your .bashrc:
 -#        source ~/.git-completion.sh
 -#
 -#       Or, add the following lines to your .zshrc:
 -#        autoload bashcompinit
 -#        bashcompinit
 +#    2) Add the following line to your .bashrc/.zshrc:
  #        source ~/.git-completion.sh
  #
  #    3) Consider changing your PS1 to also show the current branch:
 -#        PS1='[\u@\h \W$(__git_ps1 " (%s)")]\$ '
 +#         Bash: PS1='[\u@\h \W$(__git_ps1 " (%s)")]\$ '
 +#         ZSH:  PS1='[%n@%m %c$(__git_ps1 " (%s)")]\$ '
  #
  #       The argument to __git_ps1 will be displayed only if you
  #       are currently in a git repository.  The %s token will be
  #       git@vger.kernel.org
  #
  
 +if [[ -n ${ZSH_VERSION-} ]]; then
 +      autoload -U +X bashcompinit && bashcompinit
 +fi
 +
  case "$COMP_WORDBREAKS" in
  *:*) : great ;;
  *)   COMP_WORDBREAKS="$COMP_WORDBREAKS:"
  # generates completion reply with compgen
  __gitcomp ()
  {
 -      local cur
 -      _get_comp_words_by_ref -n =: cur
 +      local cur_="$cur"
 +
        if [ $# -gt 2 ]; then
 -              cur="$3"
 +              cur_="$3"
        fi
 -      case "$cur" in
 +      case "$cur_" in
        --*=)
                COMPREPLY=()
                ;;
                local IFS=$'\n'
                COMPREPLY=($(compgen -P "${2-}" \
                        -W "$(__gitcomp_1 "${1-}" "${4-}")" \
 -                      -- "$cur"))
 +                      -- "$cur_"))
                ;;
        esac
  }
@@@ -551,7 -551,8 +551,7 @@@ __git_tags (
  __git_refs ()
  {
        local i is_hash=y dir="$(__gitdir "${1-}")" track="${2-}"
 -      local cur format refs
 -      _get_comp_words_by_ref -n =: cur
 +      local format refs
        if [ -d "$dir" ]; then
                case "$cur" in
                refs|refs/*)
@@@ -628,12 -629,12 +628,12 @@@ __git_refs_remotes (
  __git_remotes ()
  {
        local i ngoff IFS=$'\n' d="$(__gitdir)"
 -      shopt -q nullglob || ngoff=1
 -      shopt -s nullglob
 +      __git_shopt -q nullglob || ngoff=1
 +      __git_shopt -s nullglob
        for i in "$d/remotes"/*; do
                echo ${i#$d/remotes/}
        done
 -      [ "$ngoff" ] && shopt -u nullglob
 +      [ "$ngoff" ] && __git_shopt -u nullglob
        for i in $(git --git-dir="$d" config --get-regexp 'remote\..*\.url' 2>/dev/null); do
                i="${i#remote.}"
                echo "${i/.url*/}"
@@@ -665,18 -666,19 +665,18 @@@ __git_compute_merge_strategies (
  
  __git_complete_revlist_file ()
  {
 -      local pfx ls ref cur
 -      _get_comp_words_by_ref -n =: cur
 -      case "$cur" in
 +      local pfx ls ref cur_="$cur"
 +      case "$cur_" in
        *..?*:*)
                return
                ;;
        ?*:*)
 -              ref="${cur%%:*}"
 -              cur="${cur#*:}"
 -              case "$cur" in
 +              ref="${cur_%%:*}"
 +              cur_="${cur_#*:}"
 +              case "$cur_" in
                ?*/*)
 -                      pfx="${cur%/*}"
 -                      cur="${cur##*/}"
 +                      pfx="${cur_%/*}"
 +                      cur_="${cur_##*/}"
                        ls="$ref:$pfx"
                        pfx="$pfx/"
                        ;;
                                           s,$,/,
                                       }
                                       s/^.*    //')" \
 -                      -- "$cur"))
 +                      -- "$cur_"))
                ;;
        *...*)
 -              pfx="${cur%...*}..."
 -              cur="${cur#*...}"
 -              __gitcomp "$(__git_refs)" "$pfx" "$cur"
 +              pfx="${cur_%...*}..."
 +              cur_="${cur_#*...}"
 +              __gitcomp "$(__git_refs)" "$pfx" "$cur_"
                ;;
        *..*)
 -              pfx="${cur%..*}.."
 -              cur="${cur#*..}"
 -              __gitcomp "$(__git_refs)" "$pfx" "$cur"
 +              pfx="${cur_%..*}.."
 +              cur_="${cur_#*..}"
 +              __gitcomp "$(__git_refs)" "$pfx" "$cur_"
                ;;
        *)
                __gitcomp "$(__git_refs)"
@@@ -737,7 -739,9 +737,7 @@@ __git_complete_revlist (
  
  __git_complete_remote_or_refspec ()
  {
 -      local cur words cword
 -      _get_comp_words_by_ref -n =: cur words cword
 -      local cmd="${words[1]}"
 +      local cur_="$cur" cmd="${words[1]}"
        local i c=2 remote="" pfx="" lhs=1 no_complete_refspec=0
        while [ $c -lt $cword ]; do
                i="${words[c]}"
                return
        fi
        [ "$remote" = "." ] && remote=
 -      case "$cur" in
 +      case "$cur_" in
        *:*)
                case "$COMP_WORDBREAKS" in
                *:*) : great ;;
 -              *)   pfx="${cur%%:*}:" ;;
 +              *)   pfx="${cur_%%:*}:" ;;
                esac
 -              cur="${cur#*:}"
 +              cur_="${cur_#*:}"
                lhs=0
                ;;
        +*)
                pfx="+"
 -              cur="${cur#+}"
 +              cur_="${cur_#+}"
                ;;
        esac
        case "$cmd" in
        fetch)
                if [ $lhs = 1 ]; then
 -                      __gitcomp "$(__git_refs2 "$remote")" "$pfx" "$cur"
 +                      __gitcomp "$(__git_refs2 "$remote")" "$pfx" "$cur_"
                else
 -                      __gitcomp "$(__git_refs)" "$pfx" "$cur"
 +                      __gitcomp "$(__git_refs)" "$pfx" "$cur_"
                fi
                ;;
        pull)
                if [ $lhs = 1 ]; then
 -                      __gitcomp "$(__git_refs "$remote")" "$pfx" "$cur"
 +                      __gitcomp "$(__git_refs "$remote")" "$pfx" "$cur_"
                else
 -                      __gitcomp "$(__git_refs)" "$pfx" "$cur"
 +                      __gitcomp "$(__git_refs)" "$pfx" "$cur_"
                fi
                ;;
        push)
                if [ $lhs = 1 ]; then
 -                      __gitcomp "$(__git_refs)" "$pfx" "$cur"
 +                      __gitcomp "$(__git_refs)" "$pfx" "$cur_"
                else
 -                      __gitcomp "$(__git_refs "$remote")" "$pfx" "$cur"
 +                      __gitcomp "$(__git_refs "$remote")" "$pfx" "$cur_"
                fi
                ;;
        esac
  
  __git_complete_strategy ()
  {
 -      local cur prev
 -      _get_comp_words_by_ref -n =: cur prev
        __git_compute_merge_strategies
        case "$prev" in
        -s|--strategy)
@@@ -985,7 -991,8 +985,7 @@@ __git_aliased_command (
  # __git_find_on_cmdline requires 1 argument
  __git_find_on_cmdline ()
  {
 -      local word subcommand c=1 words cword
 -      _get_comp_words_by_ref -n =: words cword
 +      local word subcommand c=1
        while [ $c -lt $cword ]; do
                word="${words[c]}"
                for subcommand in $1; do
  
  __git_has_doubledash ()
  {
 -      local c=1 words cword
 -      _get_comp_words_by_ref -n =: words cword
 +      local c=1
        while [ $c -lt $cword ]; do
                if [ "--" = "${words[c]}" ]; then
                        return 0
@@@ -1014,7 -1022,8 +1014,7 @@@ __git_whitespacelist="nowarn warn erro
  
  _git_am ()
  {
 -      local cur dir="$(__gitdir)"
 -      _get_comp_words_by_ref -n =: cur
 +      local dir="$(__gitdir)"
        if [ -d "$dir"/rebase-apply ]; then
                __gitcomp "--skip --continue --resolved --abort"
                return
  
  _git_apply ()
  {
 -      local cur
 -      _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --whitespace=*)
                __gitcomp "$__git_whitespacelist" "" "${cur##--whitespace=}"
@@@ -1060,6 -1071,8 +1060,6 @@@ _git_add (
  {
        __git_has_doubledash && return
  
 -      local cur
 -      _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --*)
                __gitcomp "
  
  _git_archive ()
  {
 -      local cur
 -      _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --format=*)
                __gitcomp "$(git archive --list)" "" "${cur##--format=}"
@@@ -1120,8 -1135,9 +1120,8 @@@ _git_bisect (
  
  _git_branch ()
  {
 -      local i c=1 only_local_ref="n" has_r="n" cur words cword
 +      local i c=1 only_local_ref="n" has_r="n"
  
 -      _get_comp_words_by_ref -n =: cur words cword
        while [ $c -lt $cword ]; do
                i="${words[c]}"
                case "$i" in
  
  _git_bundle ()
  {
 -      local words cword
 -      _get_comp_words_by_ref -n =: words cword
        local cmd="${words[2]}"
        case "$cword" in
        2)
@@@ -1173,6 -1191,8 +1173,6 @@@ _git_checkout (
  {
        __git_has_doubledash && return
  
 -      local cur
 -      _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --conflict=*)
                __gitcomp "diff3 merge" "" "${cur##--conflict=}"
@@@ -1202,6 -1222,8 +1202,6 @@@ _git_cherry (
  
  _git_cherry_pick ()
  {
 -      local cur
 -      _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --*)
                __gitcomp "--edit --no-commit"
@@@ -1216,6 -1238,8 +1216,6 @@@ _git_clean (
  {
        __git_has_doubledash && return
  
 -      local cur
 -      _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --*)
                __gitcomp "--dry-run --quiet"
  
  _git_clone ()
  {
 -      local cur
 -      _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --*)
                __gitcomp "
@@@ -1253,6 -1279,8 +1253,6 @@@ _git_commit (
  {
        __git_has_doubledash && return
  
 -      local cur
 -      _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --cleanup=*)
                __gitcomp "default strip verbatim whitespace
  
  _git_describe ()
  {
 -      local cur
 -      _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --*)
                __gitcomp "
@@@ -1318,6 -1348,8 +1318,6 @@@ _git_diff (
  {
        __git_has_doubledash && return
  
 -      local cur
 -      _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --*)
                __gitcomp "--cached --staged --pickaxe-all --pickaxe-regex
@@@ -1338,6 -1370,8 +1338,6 @@@ _git_difftool (
  {
        __git_has_doubledash && return
  
 -      local cur
 -      _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --tool=*)
                __gitcomp "$__git_mergetools_common kompare" "" "${cur##--tool=}"
@@@ -1362,6 -1396,8 +1362,6 @@@ __git_fetch_options=
  
  _git_fetch ()
  {
 -      local cur
 -      _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --*)
                __gitcomp "$__git_fetch_options"
  
  _git_format_patch ()
  {
 -      local cur
 -      _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --thread=*)
                __gitcomp "
  
  _git_fsck ()
  {
 -      local cur
 -      _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --*)
                __gitcomp "
  
  _git_gc ()
  {
 -      local cur
 -      _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --*)
                __gitcomp "--prune --aggressive"
@@@ -1436,13 -1478,16 +1436,14 @@@ _git_grep (
  {
        __git_has_doubledash && return
  
 -      local cur
 -      _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --*)
                __gitcomp "
                        --cached
                        --text --ignore-case --word-regexp --invert-match
-                       --full-name
+                       --full-name --line-number
                        --extended-regexp --basic-regexp --fixed-strings
+                       --perl-regexp
                        --files-with-matches --name-only
                        --files-without-match
                        --max-depth
  
  _git_help ()
  {
 -      local cur
 -      _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --*)
                __gitcomp "--all --info --man --web"
  
  _git_init ()
  {
 -      local cur
 -      _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --shared=*)
                __gitcomp "
@@@ -1494,6 -1543,8 +1495,6 @@@ _git_ls_files (
  {
        __git_has_doubledash && return
  
 -      local cur
 -      _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --*)
                __gitcomp "--cached --deleted --modified --others --ignored
@@@ -1534,7 -1585,7 +1535,7 @@@ __git_log_common_options=
  __git_log_gitk_options="
        --dense --sparse --full-history
        --simplify-merges --simplify-by-decoration
 -      --left-right
 +      --left-right --notes --no-notes
  "
  # Options that go well for log and shortlog (not gitk)
  __git_log_shortlog_options="
@@@ -1554,6 -1605,8 +1555,6 @@@ _git_log (
        if [ -f "$g/MERGE_HEAD" ]; then
                merge="--merge"
        fi
 -      local cur
 -      _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --pretty=*)
                __gitcomp "$__git_log_pretty_formats $(__git_pretty_aliases)
@@@ -1607,6 -1660,8 +1608,6 @@@ _git_merge (
  {
        __git_complete_strategy && return
  
 -      local cur
 -      _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --*)
                __gitcomp "$__git_merge_options"
  
  _git_mergetool ()
  {
 -      local cur
 -      _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --tool=*)
                __gitcomp "$__git_mergetools_common tortoisemerge" "" "${cur##--tool=}"
@@@ -1637,6 -1694,8 +1638,6 @@@ _git_merge_base (
  
  _git_mv ()
  {
 -      local cur
 -      _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --*)
                __gitcomp "--dry-run"
@@@ -1655,6 -1714,8 +1656,6 @@@ _git_notes (
  {
        local subcommands='add append copy edit list prune remove show'
        local subcommand="$(__git_find_on_cmdline "$subcommands")"
 -      local cur words cword
 -      _get_comp_words_by_ref -n =: cur words cword
  
        case "$subcommand,$cur" in
        ,--*)
@@@ -1704,6 -1765,8 +1705,6 @@@ _git_pull (
  {
        __git_complete_strategy && return
  
 -      local cur
 -      _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --*)
                __gitcomp "
  
  _git_push ()
  {
 -      local cur prev
 -      _get_comp_words_by_ref -n =: cur prev
        case "$prev" in
        --repo)
                __gitcomp "$(__git_remotes)"
  _git_rebase ()
  {
        local dir="$(__gitdir)"
 -      local cur
 -      _get_comp_words_by_ref -n =: cur
        if [ -d "$dir"/rebase-apply ] || [ -d "$dir"/rebase-merge ]; then
                __gitcomp "--continue --skip --abort"
                return
@@@ -1784,6 -1851,8 +1785,6 @@@ __git_send_email_suppresscc_options="au
  
  _git_send_email ()
  {
 -      local cur
 -      _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --confirm=*)
                __gitcomp "
@@@ -1825,6 -1894,8 +1826,6 @@@ _git_stage (
  
  __git_config_get_set_variables ()
  {
 -      local words cword
 -      _get_comp_words_by_ref -n =: words cword
        local prevword word config_file= c=$cword
        while [ $c -gt 1 ]; do
                word="${words[c]}"
  
  _git_config ()
  {
 -      local cur prev
 -      _get_comp_words_by_ref -n =: cur prev
        case "$prev" in
        branch.*.remote)
                __gitcomp "$(__git_remotes)"
                return
                ;;
        branch.*.*)
 -              local pfx="${cur%.*}."
 -              cur="${cur##*.}"
 -              __gitcomp "remote merge mergeoptions rebase" "$pfx" "$cur"
 +              local pfx="${cur%.*}." cur_="${cur##*.}"
 +              __gitcomp "remote merge mergeoptions rebase" "$pfx" "$cur_"
                return
                ;;
        branch.*)
 -              local pfx="${cur%.*}."
 -              cur="${cur#*.}"
 -              __gitcomp "$(__git_heads)" "$pfx" "$cur" "."
 +              local pfx="${cur%.*}." cur_="${cur#*.}"
 +              __gitcomp "$(__git_heads)" "$pfx" "$cur_" "."
                return
                ;;
        guitool.*.*)
 -              local pfx="${cur%.*}."
 -              cur="${cur##*.}"
 +              local pfx="${cur%.*}." cur_="${cur##*.}"
                __gitcomp "
                        argprompt cmd confirm needsfile noconsole norescan
                        prompt revprompt revunmerged title
 -                      " "$pfx" "$cur"
 +                      " "$pfx" "$cur_"
                return
                ;;
        difftool.*.*)
 -              local pfx="${cur%.*}."
 -              cur="${cur##*.}"
 -              __gitcomp "cmd path" "$pfx" "$cur"
 +              local pfx="${cur%.*}." cur_="${cur##*.}"
 +              __gitcomp "cmd path" "$pfx" "$cur_"
                return
                ;;
        man.*.*)
 -              local pfx="${cur%.*}."
 -              cur="${cur##*.}"
 -              __gitcomp "cmd path" "$pfx" "$cur"
 +              local pfx="${cur%.*}." cur_="${cur##*.}"
 +              __gitcomp "cmd path" "$pfx" "$cur_"
                return
                ;;
        mergetool.*.*)
 -              local pfx="${cur%.*}."
 -              cur="${cur##*.}"
 -              __gitcomp "cmd path trustExitCode" "$pfx" "$cur"
 +              local pfx="${cur%.*}." cur_="${cur##*.}"
 +              __gitcomp "cmd path trustExitCode" "$pfx" "$cur_"
                return
                ;;
        pager.*)
 -              local pfx="${cur%.*}."
 -              cur="${cur#*.}"
 +              local pfx="${cur%.*}." cur_="${cur#*.}"
                __git_compute_all_commands
 -              __gitcomp "$__git_all_commands" "$pfx" "$cur"
 +              __gitcomp "$__git_all_commands" "$pfx" "$cur_"
                return
                ;;
        remote.*.*)
 -              local pfx="${cur%.*}."
 -              cur="${cur##*.}"
 +              local pfx="${cur%.*}." cur_="${cur##*.}"
                __gitcomp "
                        url proxy fetch push mirror skipDefaultUpdate
                        receivepack uploadpack tagopt pushurl
 -                      " "$pfx" "$cur"
 +                      " "$pfx" "$cur_"
                return
                ;;
        remote.*)
 -              local pfx="${cur%.*}."
 -              cur="${cur#*.}"
 -              __gitcomp "$(__git_remotes)" "$pfx" "$cur" "."
 +              local pfx="${cur%.*}." cur_="${cur#*.}"
 +              __gitcomp "$(__git_remotes)" "$pfx" "$cur_" "."
                return
                ;;
        url.*.*)
 -              local pfx="${cur%.*}."
 -              cur="${cur##*.}"
 -              __gitcomp "insteadOf pushInsteadOf" "$pfx" "$cur"
 +              local pfx="${cur%.*}." cur_="${cur##*.}"
 +              __gitcomp "insteadOf pushInsteadOf" "$pfx" "$cur_"
                return
                ;;
        esac
@@@ -2314,6 -2397,8 +2315,6 @@@ _git_reset (
  {
        __git_has_doubledash && return
  
 -      local cur
 -      _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --*)
                __gitcomp "--merge --mixed --hard --soft --patch"
  
  _git_revert ()
  {
 -      local cur
 -      _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --*)
                __gitcomp "--edit --mainline --no-edit --no-commit --signoff"
@@@ -2338,6 -2425,8 +2339,6 @@@ _git_rm (
  {
        __git_has_doubledash && return
  
 -      local cur
 -      _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --*)
                __gitcomp "--cached --dry-run --ignore-unmatch --quiet"
@@@ -2351,6 -2440,8 +2352,6 @@@ _git_shortlog (
  {
        __git_has_doubledash && return
  
 -      local cur
 -      _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --*)
                __gitcomp "
@@@ -2368,6 -2459,8 +2369,6 @@@ _git_show (
  {
        __git_has_doubledash && return
  
 -      local cur
 -      _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --pretty=*)
                __gitcomp "$__git_log_pretty_formats $(__git_pretty_aliases)
  
  _git_show_branch ()
  {
 -      local cur
 -      _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --*)
                __gitcomp "
  
  _git_stash ()
  {
 -      local cur
 -      _get_comp_words_by_ref -n =: cur
        local save_opts='--keep-index --no-keep-index --quiet --patch'
        local subcommands='save list show apply clear drop pop create branch'
        local subcommand="$(__git_find_on_cmdline "$subcommands")"
@@@ -2451,6 -2548,8 +2452,6 @@@ _git_submodule (
  
        local subcommands="add status init update summary foreach sync"
        if [ -z "$(__git_find_on_cmdline "$subcommands")" ]; then
 -              local cur
 -              _get_comp_words_by_ref -n =: cur
                case "$cur" in
                --*)
                        __gitcomp "--quiet --cached"
@@@ -2494,6 -2593,8 +2495,6 @@@ _git_svn (
                        --edit --rmdir --find-copies-harder --copy-similarity=
                        "
  
 -              local cur
 -              _get_comp_words_by_ref -n =: cur
                case "$subcommand,$cur" in
                fetch,--*)
                        __gitcomp "--revision= --fetch-all $fc_opts"
  _git_tag ()
  {
        local i c=1 f=0
 -      local words cword prev
 -      _get_comp_words_by_ref -n =: words cword prev
        while [ $c -lt $cword ]; do
                i="${words[c]}"
                case "$i" in
@@@ -2608,14 -2711,10 +2609,14 @@@ _git (
        if [[ -n ${ZSH_VERSION-} ]]; then
                emulate -L bash
                setopt KSH_TYPESET
 +
 +              # workaround zsh's bug that leaves 'words' as a special
 +              # variable in versions < 4.3.12
 +              typeset -h words
        fi
  
 -      local cur words cword
 -      _get_comp_words_by_ref -n =: cur words cword
 +      local cur words cword prev
 +      _get_comp_words_by_ref -n =: cur words cword prev
        while [ $c -lt $cword ]; do
                i="${words[c]}"
                case "$i" in
@@@ -2663,22 -2762,17 +2664,22 @@@ _gitk (
        if [[ -n ${ZSH_VERSION-} ]]; then
                emulate -L bash
                setopt KSH_TYPESET
 +
 +              # workaround zsh's bug that leaves 'words' as a special
 +              # variable in versions < 4.3.12
 +              typeset -h words
        fi
  
 +      local cur words cword prev
 +      _get_comp_words_by_ref -n =: cur words cword prev
 +
        __git_has_doubledash && return
  
 -      local cur
        local g="$(__gitdir)"
        local merge=""
        if [ -f "$g/MERGE_HEAD" ]; then
                merge="--merge"
        fi
 -      _get_comp_words_by_ref -n =: cur
        case "$cur" in
        --*)
                __gitcomp "
@@@ -2707,7 -2801,7 +2708,7 @@@ complete -o bashdefault -o default -o n
  fi
  
  if [[ -n ${ZSH_VERSION-} ]]; then
 -      shopt () {
 +      __git_shopt () {
                local option
                if [ $# -ne 2 ]; then
                        echo "USAGE: $0 (-q|-s|-u) <option>" >&2
                        return 1
                esac
        }
 +else
 +      __git_shopt () {
 +              shopt "$@"
 +      }
  fi
diff --combined t/README
index cad36dd7502b81ef8e27476e9686562547b5a1c0,238729c5b7168919ab6f79cf0a0b11c4833ddeae..c85abaffb3b8c2142e87f6e0525fa67c6b62c1a1
+++ b/t/README
@@@ -379,7 -379,7 +379,7 @@@ library for your script to use
  
   - test_expect_success [<prereq>] <message> <script>
  
 -   Usually takes two strings as parameter, and evaluates the
 +   Usually takes two strings as parameters, and evaluates the
     <script>.  If it yields success, test is considered
     successful.  <message> should state what it is testing.
  
            'tree=$(git-write-tree)'
  
     If you supply three parameters the first will be taken to be a
 -   prerequisite, see the test_set_prereq and test_have_prereq
 +   prerequisite; see the test_set_prereq and test_have_prereq
     documentation below:
  
        test_expect_success TTY 'git --paginate rev-list uses a pager' \
     Merges the given rev using the given message.  Like test_commit,
     creates a tag and calls test_tick before committing.
  
 - - test_set_prereq SOME_PREREQ
 + - test_set_prereq <prereq>
  
     Set a test prerequisite to be used later with test_have_prereq. The
     test-lib will set some prerequisites for you, see the
     test_have_prereq directly, or the three argument invocation of
     test_expect_success and test_expect_failure.
  
 - - test_have_prereq SOME PREREQ
 + - test_have_prereq <prereq>
  
     Check if we have a prerequisite previously set with
     test_set_prereq. The most common use of this directly is to skip
  
     Check whether a file has the length it is expected to.
  
 - - test_path_is_file <file> [<diagnosis>]
 -   test_path_is_dir <dir> [<diagnosis>]
 + - test_path_is_file <path> [<diagnosis>]
 +   test_path_is_dir <path> [<diagnosis>]
     test_path_is_missing <path> [<diagnosis>]
  
 -   Check whether a file/directory exists or doesn't. <diagnosis> will
 -   be displayed if the test fails.
 +   Check if the named path is a file, if the named path is a
 +   directory, or if the named path does not exist, respectively,
 +   and fail otherwise, showing the <diagnosis> text.
  
   - test_when_finished <script>
  
@@@ -588,6 -587,11 +588,11 @@@ use these, and "test_set_prereq" for ho
     Test is not run by root user, and an attempt to write to an
     unwritable file is expected to fail correctly.
  
+  - LIBPCRE
+    Git was compiled with USE_LIBPCRE=YesPlease. Wrap any tests
+    that use git-grep --perl-regexp or git-grep -P in these.
  Tips for Writing Tests
  ----------------------
  
diff --combined t/test-lib.sh
index b12b993e9a9299ff865ef6f1b8770d503988d53b,d3ed59803f6e7c91a60cbabd38419ab0c8f8a797..64390d716d68b79b5a0915a707c95f5c2df39a46
@@@ -89,9 -89,6 +89,9 @@@ esa
  _x05='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
  _x40="$_x05$_x05$_x05$_x05$_x05$_x05$_x05$_x05"
  
 +# Zero SHA-1
 +_z40=0000000000000000000000000000000000000000
 +
  # Each test should start with something like this, after copyright notices:
  #
  # test_description='Description of this test...
@@@ -578,7 -575,7 +578,7 @@@ test_external () 
  test_external_without_stderr () {
        # The temporary file has no (and must have no) security
        # implications.
 -      tmp="$TMPDIR"; if [ -z "$tmp" ]; then tmp=/tmp; fi
 +      tmp=${TMPDIR:-/tmp}
        stderr="$tmp/git-external-stderr.$$.tmp"
        test_external "$@" 4> "$stderr"
        [ -f "$stderr" ] || error "Internal error: $stderr disappeared."
@@@ -731,11 -728,12 +731,11 @@@ test_expect_code () 
        exit_code=$?
        if test $exit_code = $want_code
        then
 -              echo >&2 "test_expect_code: command exited with $exit_code: $*"
                return 0
 -      else
 -              echo >&2 "test_expect_code: command exited with $exit_code, we wanted $want_code $*"
 -              return 1
        fi
 +
 +      echo >&2 "test_expect_code: command exited with $exit_code, we wanted $want_code $*"
 +      return 1
  }
  
  # test_cmp is a helper function to compare actual and expected output.
@@@ -803,14 -801,12 +803,14 @@@ test_done () 
                mkdir -p "$test_results_dir"
                test_results_path="$test_results_dir/${0%.sh}-$$.counts"
  
 -              echo "total $test_count" >> $test_results_path
 -              echo "success $test_success" >> $test_results_path
 -              echo "fixed $test_fixed" >> $test_results_path
 -              echo "broken $test_broken" >> $test_results_path
 -              echo "failed $test_failure" >> $test_results_path
 -              echo "" >> $test_results_path
 +              cat >>"$test_results_path" <<-EOF
 +              total $test_count
 +              success $test_success
 +              fixed $test_fixed
 +              broken $test_broken
 +              failed $test_failure
 +
 +              EOF
        fi
  
        if test "$test_fixed" != 0
@@@ -1071,6 -1067,7 +1071,7 @@@ esa
  
  test -z "$NO_PERL" && test_set_prereq PERL
  test -z "$NO_PYTHON" && test_set_prereq PYTHON
+ test -n "$USE_LIBPCRE" && test_set_prereq LIBPCRE
  
  # Can we rely on git's output in the C locale?
  if test -n "$GETTEXT_POISON"
        test_set_prereq C_LOCALE_OUTPUT
  fi
  
 +# Use this instead of test_cmp to compare files that contain expected and
 +# actual output from git commands that can be translated.  When running
 +# under GETTEXT_POISON this pretends that the command produced expected
 +# results.
 +test_i18ncmp () {
 +      test -n "$GETTEXT_POISON" || test_cmp "$@"
 +}
 +
 +# Use this instead of "grep expected-string actual" to see if the
 +# output from a git command that can be translated either contains an
 +# expected string, or does not contain an unwanted one.  When running
 +# under GETTEXT_POISON this pretends that the command produced expected
 +# results.
 +test_i18ngrep () {
 +      if test -n "$GETTEXT_POISON"
 +      then
 +          : # pretend success
 +      elif test "x!" = "x$1"
 +      then
 +              shift
 +              ! grep "$@"
 +      else
 +              grep "$@"
 +      fi
 +}
 +
  # test whether the filesystem supports symbolic links
  ln -s x y 2>/dev/null && test -h y 2>/dev/null && test_set_prereq SYMLINKS
  rm -f y