Merge branch 'nd/dwim-wildcards-as-pathspecs'
authorJunio C Hamano <gitster@pobox.com>
Tue, 19 May 2015 20:17:57 +0000 (13:17 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 19 May 2015 20:17:58 +0000 (13:17 -0700)
A heuristic to help the "git <cmd> <revs> <pathspec>" command line
convention to catch mistyped paths is to make sure all the non-rev
parameters in the later part of the command line are names of the
files in the working tree, but that means "git grep $str -- \*.c"
must always be disambiguated with "--", because nobody sane will
create a file whose name literally is asterisk-dot-see. Loosen the
heuristic to declare that with a wildcard string the user likely
meant to give us a pathspec.

* nd/dwim-wildcards-as-pathspecs:
pathspec: avoid the need of "--" when wildcard is used

1  2 
setup.c
diff --combined setup.c
index fb6186049d1d9af1e84b2468c069b346c5a24a3b,1055b8270811c9f55a441d4d7a7f5185dceec1d1..863ddfd938d29e58341bd2be5dd3b7ecd4db7df2
+++ b/setup.c
@@@ -140,7 -140,9 +140,9 @@@ int check_filename(const char *prefix, 
                if (arg[2] == '\0') /* ":/" is root dir, always exists */
                        return 1;
                name = arg + 2;
-       } else if (prefix)
+       } else if (!no_wildcard(arg))
+               return 1;
+       else if (prefix)
                name = prefix_filename(prefix, strlen(prefix), arg);
        else
                name = arg;
@@@ -224,36 -226,6 +226,36 @@@ void verify_non_filename(const char *pr
            "'git <command> [<revision>...] -- [<file>...]'", arg);
  }
  
 +int get_common_dir(struct strbuf *sb, const char *gitdir)
 +{
 +      struct strbuf data = STRBUF_INIT;
 +      struct strbuf path = STRBUF_INIT;
 +      const char *git_common_dir = getenv(GIT_COMMON_DIR_ENVIRONMENT);
 +      int ret = 0;
 +      if (git_common_dir) {
 +              strbuf_addstr(sb, git_common_dir);
 +              return 1;
 +      }
 +      strbuf_addf(&path, "%s/commondir", gitdir);
 +      if (file_exists(path.buf)) {
 +              if (strbuf_read_file(&data, path.buf, 0) <= 0)
 +                      die_errno(_("failed to read %s"), path.buf);
 +              while (data.len && (data.buf[data.len - 1] == '\n' ||
 +                                  data.buf[data.len - 1] == '\r'))
 +                      data.len--;
 +              data.buf[data.len] = '\0';
 +              strbuf_reset(&path);
 +              if (!is_absolute_path(data.buf))
 +                      strbuf_addf(&path, "%s/", gitdir);
 +              strbuf_addbuf(&path, &data);
 +              strbuf_addstr(sb, real_path(path.buf));
 +              ret = 1;
 +      } else
 +              strbuf_addstr(sb, gitdir);
 +      strbuf_release(&data);
 +      strbuf_release(&path);
 +      return ret;
 +}
  
  /*
   * Test if it looks like we're at a git directory.
   */
  int is_git_directory(const char *suspect)
  {
 -      char path[PATH_MAX];
 -      size_t len = strlen(suspect);
 +      struct strbuf path = STRBUF_INIT;
 +      int ret = 0;
 +      size_t len;
  
 -      if (PATH_MAX <= len + strlen("/objects"))
 -              die("Too long path: %.*s", 60, suspect);
 -      strcpy(path, suspect);
 +      /* Check worktree-related signatures */
 +      strbuf_addf(&path, "%s/HEAD", suspect);
 +      if (validate_headref(path.buf))
 +              goto done;
 +
 +      strbuf_reset(&path);
 +      get_common_dir(&path, suspect);
 +      len = path.len;
 +
 +      /* Check non-worktree-related signatures */
        if (getenv(DB_ENVIRONMENT)) {
                if (access(getenv(DB_ENVIRONMENT), X_OK))
 -                      return 0;
 +                      goto done;
        }
        else {
 -              strcpy(path + len, "/objects");
 -              if (access(path, X_OK))
 -                      return 0;
 +              strbuf_setlen(&path, len);
 +              strbuf_addstr(&path, "/objects");
 +              if (access(path.buf, X_OK))
 +                      goto done;
        }
  
 -      strcpy(path + len, "/refs");
 -      if (access(path, X_OK))
 -              return 0;
 -
 -      strcpy(path + len, "/HEAD");
 -      if (validate_headref(path))
 -              return 0;
 +      strbuf_setlen(&path, len);
 +      strbuf_addstr(&path, "/refs");
 +      if (access(path.buf, X_OK))
 +              goto done;
  
 -      return 1;
 +      ret = 1;
 +done:
 +      strbuf_release(&path);
 +      return ret;
  }
  
  int is_inside_git_dir(void)
@@@ -343,28 -306,9 +345,28 @@@ void setup_work_tree(void
        initialized = 1;
  }
  
 +static int check_repo_format(const char *var, const char *value, void *cb)
 +{
 +      if (strcmp(var, "core.repositoryformatversion") == 0)
 +              repository_format_version = git_config_int(var, value);
 +      else if (strcmp(var, "core.sharedrepository") == 0)
 +              shared_repository = git_config_perm(var, value);
 +      return 0;
 +}
 +
  static int check_repository_format_gently(const char *gitdir, int *nongit_ok)
  {
 -      char repo_config[PATH_MAX+1];
 +      struct strbuf sb = STRBUF_INIT;
 +      const char *repo_config;
 +      config_fn_t fn;
 +      int ret = 0;
 +
 +      if (get_common_dir(&sb, gitdir))
 +              fn = check_repo_format;
 +      else
 +              fn = check_repository_format_version;
 +      strbuf_addstr(&sb, "/config");
 +      repo_config = sb.buf;
  
        /*
         * git_config() can't be used here because it calls git_pathdup()
         * Use a gentler version of git_config() to check if this repo
         * is a good one.
         */
 -      snprintf(repo_config, PATH_MAX, "%s/config", gitdir);
 -      git_config_early(check_repository_format_version, NULL, repo_config);
 +      git_config_early(fn, NULL, repo_config);
        if (GIT_REPO_VERSION < repository_format_version) {
                if (!nongit_ok)
                        die ("Expected git repo version <= %d, found %d",
                        GIT_REPO_VERSION, repository_format_version);
                warning("Please upgrade Git");
                *nongit_ok = -1;
 -              return -1;
 +              ret = -1;
        }
 -      return 0;
 +      strbuf_release(&sb);
 +      return ret;
 +}
 +
 +static void update_linked_gitdir(const char *gitfile, const char *gitdir)
 +{
 +      struct strbuf path = STRBUF_INIT;
 +      struct stat st;
 +
 +      strbuf_addf(&path, "%s/gitfile", gitdir);
 +      if (stat(path.buf, &st) || st.st_mtime + 24 * 3600 < time(NULL))
 +              write_file(path.buf, 0, "%s\n", gitfile);
 +      strbuf_release(&path);
  }
  
  /*
@@@ -449,8 -382,6 +451,8 @@@ const char *read_gitfile(const char *pa
  
        if (!is_git_directory(dir))
                die("Not a git repository: %s", dir);
 +
 +      update_linked_gitdir(path, dir);
        path = real_path(dir);
  
        free(buf);
@@@ -870,10 -801,11 +872,10 @@@ int git_config_perm(const char *var, co
  
  int check_repository_format_version(const char *var, const char *value, void *cb)
  {
 -      if (strcmp(var, "core.repositoryformatversion") == 0)
 -              repository_format_version = git_config_int(var, value);
 -      else if (strcmp(var, "core.sharedrepository") == 0)
 -              shared_repository = git_config_perm(var, value);
 -      else if (strcmp(var, "core.bare") == 0) {
 +      int ret = check_repo_format(var, value, cb);
 +      if (ret)
 +              return ret;
 +      if (strcmp(var, "core.bare") == 0) {
                is_bare_repository_cfg = git_config_bool(var, value);
                if (is_bare_repository_cfg == 1)
                        inside_work_tree = -1;