Merge branch 'da/git-prefix-everywhere' into next
authorJunio C Hamano <gitster@pobox.com>
Thu, 30 Jun 2011 00:09:27 +0000 (17:09 -0700)
committerJunio C Hamano <gitster@pobox.com>
Thu, 30 Jun 2011 00:09:27 +0000 (17:09 -0700)
* da/git-prefix-everywhere:
t/t7503-pre-commit-hook.sh: Add GIT_PREFIX tests
git-mergetool--lib: Make vimdiff retain the current directory
git: Remove handling for GIT_PREFIX
setup: Provide GIT_PREFIX to built-ins

1  2 
git-mergetool--lib.sh
git.c
setup.c
t/t1020-subdirectory.sh
diff --combined git-mergetool--lib.sh
index 4db9212331259664732f031438b7b87b7a10244f,f5a100a567298bdcd76a6a4efadf12bf40343398..91f90acfba2a710644f4a076d27d50e0fbb33c2d
@@@ -86,6 -86,11 +86,11 @@@ get_merge_tool_cmd () 
  }
  
  run_merge_tool () {
+       # If GIT_PREFIX is empty then we cannot use it in tools
+       # that expect to be able to chdir() to its value.
+       GIT_PREFIX=${GIT_PREFIX:-.}
+       export GIT_PREFIX
        merge_tool_path="$(get_merge_tool_path "$1")" || exit
        base_present="$2"
        status=0
                        check_unchanged
                else
                        "$merge_tool_path" -R -f -d -c "wincmd l" \
+                               -c 'cd $GIT_PREFIX' \
                                "$LOCAL" "$REMOTE"
                fi
                ;;
                        check_unchanged
                else
                        "$merge_tool_path" -R -f -d -c "wincmd l" \
+                               -c 'cd $GIT_PREFIX' \
                                "$LOCAL" "$REMOTE"
                fi
                ;;
                ;;
        p4merge)
                if merge_mode; then
 -                  touch "$BACKUP"
 -                      if $base_present; then
 -                              "$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" "$MERGED"
 -                      else
 -                              "$merge_tool_path" "$LOCAL" "$LOCAL" "$REMOTE" "$MERGED"
 -                      fi
 +                      touch "$BACKUP"
 +                      $base_present || >"$BASE"
 +                      "$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" "$MERGED"
                        check_unchanged
                else
                        "$merge_tool_path" "$LOCAL" "$REMOTE"
diff --combined git.c
index 89721d420a09bfc8eb6a2d4010f86f5685cfd255,ef598c3e7053b8dd2859f4d582ce2917a804fe42..8828c18d6cce99b8becfbb510fcc9b2b752598ca
--- 1/git.c
--- 2/git.c
+++ b/git.c
@@@ -6,7 -6,7 +6,7 @@@
  #include "run-command.h"
  
  const char git_usage_string[] =
 -      "git [--version] [--exec-path[=<path>]] [--html-path]\n"
 +      "git [--version] [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]\n"
        "           [-p|--paginate|--no-pager] [--no-replace-objects]\n"
        "           [--bare] [--git-dir=<path>] [--work-tree=<path>]\n"
        "           [-c name=value] [--help]\n"
@@@ -66,7 -66,7 +66,7 @@@ static void commit_pager_choice(void) 
  
  static int handle_options(const char ***argv, int *argc, int *envchanged)
  {
 -      int handled = 0;
 +      const char **orig_argv = *argv;
  
        while (*argc > 0) {
                const char *cmd = (*argv)[0];
                } else if (!strcmp(cmd, "--html-path")) {
                        puts(system_path(GIT_HTML_PATH));
                        exit(0);
 +              } else if (!strcmp(cmd, "--man-path")) {
 +                      puts(system_path(GIT_MAN_PATH));
 +                      exit(0);
 +              } else if (!strcmp(cmd, "--info-path")) {
 +                      puts(system_path(GIT_INFO_PATH));
 +                      exit(0);
                } else if (!strcmp(cmd, "-p") || !strcmp(cmd, "--paginate")) {
                        use_pager = 1;
                } else if (!strcmp(cmd, "--no-pager")) {
                                *envchanged = 1;
                        (*argv)++;
                        (*argc)--;
 -                      handled++;
                } else if (!prefixcmp(cmd, "--git-dir=")) {
                        setenv(GIT_DIR_ENVIRONMENT, cmd + 10, 1);
                        if (envchanged)
  
                (*argv)++;
                (*argc)--;
 -              handled++;
        }
 -      return handled;
 +      return (*argv) - orig_argv;
  }
  
  static int handle_alias(int *argcp, const char ***argv)
                if (alias_string[0] == '!') {
                        const char **alias_argv;
                        int argc = *argcp, i;
-                       struct strbuf sb = STRBUF_INIT;
-                       const char *env[2];
  
                        commit_pager_choice();
  
                                alias_argv[i] = (*argv)[i];
                        alias_argv[argc] = NULL;
  
-                       strbuf_addstr(&sb, "GIT_PREFIX=");
-                       if (subdir)
-                               strbuf_addstr(&sb, subdir);
-                       env[0] = sb.buf;
-                       env[1] = NULL;
-                       ret = run_command_v_opt_cd_env(alias_argv, RUN_USING_SHELL, NULL, env);
-                       strbuf_release(&sb);
+                       ret = run_command_v_opt(alias_argv, RUN_USING_SHELL);
                        if (ret >= 0)   /* normal exit */
                                exit(ret);
  
diff --combined setup.c
index ce87900ce3c68ace0f231827bdda0e9a65ef15b3,63f5368d901564010d0575485cd07e6b90bdf173..5ea5502e4881a806ebb3aa9fd38f146cf9f65cf9
+++ b/setup.c
@@@ -85,17 -85,8 +85,17 @@@ static void NORETURN die_verify_filenam
  {
        unsigned char sha1[20];
        unsigned mode;
 -      /* try a detailed diagnostic ... */
 -      get_sha1_with_mode_1(arg, sha1, &mode, 0, prefix);
 +
 +      /*
 +       * Saying "'(icase)foo' does not exist in the index" when the
 +       * user gave us ":(icase)foo" is just stupid.  A magic pathspec
 +       * begins with a colon and is followed by a non-alnum; do not
 +       * let get_sha1_with_mode_1(only_to_die=1) to even trigger.
 +       */
 +      if (!(arg[0] == ':' && !isalnum(arg[1])))
 +              /* try a detailed diagnostic ... */
 +              get_sha1_with_mode_1(arg, sha1, &mode, 1, prefix);
 +
        /* ... or fall back the most general message. */
        die("ambiguous argument '%s': unknown revision or path not in the working tree.\n"
            "Use '--' to separate paths from revisions", arg);
@@@ -135,105 -126,6 +135,105 @@@ void verify_non_filename(const char *pr
            "Use '--' to separate filenames from revisions", arg);
  }
  
 +/*
 + * Magic pathspec
 + *
 + * NEEDSWORK: These need to be moved to dir.h or even to a new
 + * pathspec.h when we restructure get_pathspec() users to use the
 + * "struct pathspec" interface.
 + *
 + * Possible future magic semantics include stuff like:
 + *
 + *    { PATHSPEC_NOGLOB, '!', "noglob" },
 + *    { PATHSPEC_ICASE, '\0', "icase" },
 + *    { PATHSPEC_RECURSIVE, '*', "recursive" },
 + *    { PATHSPEC_REGEXP, '\0', "regexp" },
 + *
 + */
 +#define PATHSPEC_FROMTOP    (1<<0)
 +
 +static struct pathspec_magic {
 +      unsigned bit;
 +      char mnemonic; /* this cannot be ':'! */
 +      const char *name;
 +} pathspec_magic[] = {
 +      { PATHSPEC_FROMTOP, '/', "top" },
 +};
 +
 +/*
 + * Take an element of a pathspec and check for magic signatures.
 + * Append the result to the prefix.
 + *
 + * For now, we only parse the syntax and throw out anything other than
 + * "top" magic.
 + *
 + * NEEDSWORK: This needs to be rewritten when we start migrating
 + * get_pathspec() users to use the "struct pathspec" interface.  For
 + * example, a pathspec element may be marked as case-insensitive, but
 + * the prefix part must always match literally, and a single stupid
 + * string cannot express such a case.
 + */
 +static const char *prefix_pathspec(const char *prefix, int prefixlen, const char *elt)
 +{
 +      unsigned magic = 0;
 +      const char *copyfrom = elt;
 +      int i;
 +
 +      if (elt[0] != ':') {
 +              ; /* nothing to do */
 +      } else if (elt[1] == '(') {
 +              /* longhand */
 +              const char *nextat;
 +              for (copyfrom = elt + 2;
 +                   *copyfrom && *copyfrom != ')';
 +                   copyfrom = nextat) {
 +                      size_t len = strcspn(copyfrom, ",)");
 +                      if (copyfrom[len] == ')')
 +                              nextat = copyfrom + len;
 +                      else
 +                              nextat = copyfrom + len + 1;
 +                      if (!len)
 +                              continue;
 +                      for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++)
 +                              if (strlen(pathspec_magic[i].name) == len &&
 +                                  !strncmp(pathspec_magic[i].name, copyfrom, len)) {
 +                                      magic |= pathspec_magic[i].bit;
 +                                      break;
 +                              }
 +                      if (ARRAY_SIZE(pathspec_magic) <= i)
 +                              die("Invalid pathspec magic '%.*s' in '%s'",
 +                                  (int) len, copyfrom, elt);
 +              }
 +              if (*copyfrom == ')')
 +                      copyfrom++;
 +      } else {
 +              /* shorthand */
 +              for (copyfrom = elt + 1;
 +                   *copyfrom && *copyfrom != ':';
 +                   copyfrom++) {
 +                      char ch = *copyfrom;
 +
 +                      if (!is_pathspec_magic(ch))
 +                              break;
 +                      for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++)
 +                              if (pathspec_magic[i].mnemonic == ch) {
 +                                      magic |= pathspec_magic[i].bit;
 +                                      break;
 +                              }
 +                      if (ARRAY_SIZE(pathspec_magic) <= i)
 +                              die("Unimplemented pathspec magic '%c' in '%s'",
 +                                  ch, elt);
 +              }
 +              if (*copyfrom == ':')
 +                      copyfrom++;
 +      }
 +
 +      if (magic & PATHSPEC_FROMTOP)
 +              return xstrdup(copyfrom);
 +      else
 +              return prefix_path(prefix, prefixlen, copyfrom);
 +}
 +
  const char **get_pathspec(const char *prefix, const char **pathspec)
  {
        const char *entry = *pathspec;
        dst = pathspec;
        prefixlen = prefix ? strlen(prefix) : 0;
        while (*src) {
 -              const char *p = prefix_path(prefix, prefixlen, *src);
 -              *(dst++) = p;
 +              *(dst++) = prefix_pathspec(prefix, prefixlen, *src);
                src++;
        }
        *dst = NULL;
@@@ -382,7 -275,7 +382,7 @@@ const char *read_gitfile_gently(const c
        const char *slash;
        struct stat st;
        int fd;
 -      size_t len;
 +      ssize_t len;
  
        if (stat(path, &st))
                return NULL;
@@@ -432,7 -325,6 +432,7 @@@ static const char *setup_explicit_git_d
        const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT);
        const char *worktree;
        char *gitfile;
 +      int offset;
  
        if (PATH_MAX - 40 < strlen(gitdirenv))
                die("'$%s' too big", GIT_DIR_ENVIRONMENT);
                return NULL;
        }
  
 -      if (!prefixcmp(cwd, worktree) &&
 -          cwd[strlen(worktree)] == '/') { /* cwd inside worktree */
 +      offset = dir_inside_of(cwd, worktree);
 +      if (offset >= 0) {      /* cwd inside worktree? */
                set_git_dir(real_path(gitdirenv));
                if (chdir(worktree))
                        die_errno("Could not chdir to '%s'", worktree);
                cwd[len++] = '/';
                cwd[len] = '\0';
                free(gitfile);
 -              return cwd + strlen(worktree) + 1;
 +              return cwd + offset;
        }
  
        /* cwd outside worktree */
@@@ -710,6 -602,11 +710,11 @@@ const char *setup_git_directory_gently(
        const char *prefix;
  
        prefix = setup_git_directory_gently_1(nongit_ok);
+       if (prefix)
+               setenv("GIT_PREFIX", prefix, 1);
+       else
+               setenv("GIT_PREFIX", "", 1);
        if (startup_info) {
                startup_info->have_repository = !nongit_ok || !*nongit_ok;
                startup_info->prefix = prefix;
diff --combined t/t1020-subdirectory.sh
index f6a44c9ee07329c8909662d2a807acf0dd6a8e43,3c7448026d452a9b7d0de621d251f73d0379fe8f..865b8ed26d577e154276887f88c8af9d13e62170
@@@ -7,7 -7,6 +7,7 @@@ test_description='Try various core-leve
  '
  
  . ./test-lib.sh
 +. "$TEST_DIRECTORY"/lib-read-tree.sh
  
  test_expect_success setup '
        long="a b c d e f g h i j k l m n o p q r s t u v w x y z" &&
@@@ -99,13 -98,13 +99,13 @@@ test_expect_success 'checkout-index' 
  test_expect_success 'read-tree' '
        rm -f one dir/two &&
        tree=`git write-tree` &&
 -      git read-tree --reset -u "$tree" &&
 +      read_tree_u_must_succeed --reset -u "$tree" &&
        cmp one original.one &&
        cmp dir/two original.two &&
        (
                cd dir &&
                rm -f two &&
 -              git read-tree --reset -u "$tree" &&
 +              read_tree_u_must_succeed --reset -u "$tree" &&
                cmp two ../original.two &&
                cmp ../one ../original.one
        )
@@@ -140,6 -139,22 +140,22 @@@ test_expect_success 'GIT_PREFIX for !al
        test_cmp expect actual
  '
  
+ test_expect_success 'GIT_PREFIX for built-ins' '
+       # Use GIT_EXTERNAL_DIFF to test that the "diff" built-in
+       # receives the GIT_PREFIX variable.
+       printf "dir/" >expect &&
+       printf "#!/bin/sh\n" >diff &&
+       printf "printf \"\$GIT_PREFIX\"" >>diff &&
+       chmod +x diff &&
+       (
+               cd dir &&
+               printf "change" >two &&
+               env GIT_EXTERNAL_DIFF=./diff git diff >../actual
+               git checkout -- two
+       ) &&
+       test_cmp expect actual
+ '
  test_expect_success 'no file/rev ambiguity check inside .git' '
        git commit -a -m 1 &&
        (