Merge branch 'jk/pathspec-literal'
authorJunio C Hamano <gitster@pobox.com>
Sun, 6 Jan 2013 07:42:07 +0000 (23:42 -0800)
committerJunio C Hamano <gitster@pobox.com>
Sun, 6 Jan 2013 07:42:07 +0000 (23:42 -0800)
Allow scripts to feed literal paths to commands that take
pathspecs, by disabling wildcard globbing.

* jk/pathspec-literal:
add global --literal-pathspecs option

Conflicts:
dir.c

1  2 
Documentation/git.txt
cache.h
dir.c
diff --combined Documentation/git.txt
index 7a3f03b5ce28eca6b8803a842d44465a2104144e,8d869db68281883161d6694ff87b04952cd403e1..05c0b942be6db6e969641a3f3b06cb3bc4b745a7
@@@ -43,15 -43,9 +43,15 @@@ unreleased) version of git, that is ava
  branch of the `git.git` repository.
  Documentation for older releases are available here:
  
 -* link:v1.8.0.2/git.html[documentation for release 1.8.0.2]
 +* link:v1.8.1/git.html[documentation for release 1.8.1]
  
  * release notes for
 +  link:RelNotes/1.8.1.txt[1.8.1].
 +
 +* link:v1.8.0.3/git.html[documentation for release 1.8.0.3]
 +
 +* release notes for
 +  link:RelNotes/1.8.0.3.txt[1.8.0.3],
    link:RelNotes/1.8.0.2.txt[1.8.0.2],
    link:RelNotes/1.8.0.1.txt[1.8.0.1],
    link:RelNotes/1.8.0.txt[1.8.0].
@@@ -428,6 -422,11 +428,11 @@@ help ...`
        Do not use replacement refs to replace git objects. See
        linkgit:git-replace[1] for more information.
  
+ --literal-pathspecs::
+       Treat pathspecs literally, rather than as glob patterns. This is
+       equivalent to setting the `GIT_LITERAL_PATHSPECS` environment
+       variable to `1`.
  
  GIT COMMANDS
  ------------
@@@ -796,6 -795,16 +801,16 @@@ for further details
        as a file path and will try to write the trace messages
        into it.
  
+ GIT_LITERAL_PATHSPECS::
+       Setting this variable to `1` will cause git to treat all
+       pathspecs literally, rather than as glob patterns. For example,
+       running `GIT_LITERAL_PATHSPECS=1 git log -- '*.c'` will search
+       for commits that touch the path `*.c`, not any paths that the
+       glob `*.c` matches. You might want this if you are feeding
+       literal paths to git (e.g., paths previously given to you by
+       `git ls-tree`, `--raw` diff output, etc).
  Discussion[[Discussion]]
  ------------------------
  
diff --combined cache.h
index e1df1e40dd64e990581c4554e8440bed44006e5d,95608d9dc5ef3af8f19a33155cc7f8b5d3928a56..c257953fa798110e0f4be9258f4f88f2d7952bdb
+++ b/cache.h
@@@ -362,6 -362,7 +362,7 @@@ static inline enum object_type object_t
  #define GIT_NOTES_DISPLAY_REF_ENVIRONMENT "GIT_NOTES_DISPLAY_REF"
  #define GIT_NOTES_REWRITE_REF_ENVIRONMENT "GIT_NOTES_REWRITE_REF"
  #define GIT_NOTES_REWRITE_MODE_ENVIRONMENT "GIT_NOTES_REWRITE_MODE"
+ #define GIT_LITERAL_PATHSPECS_ENVIRONMENT "GIT_LITERAL_PATHSPECS"
  
  /*
   * Repository-local GIT_* environment variables
@@@ -473,8 -474,6 +474,8 @@@ extern int index_name_is_other(const st
  extern int ie_match_stat(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);
  extern int ie_modified(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);
  
 +#define PATHSPEC_ONESTAR 1    /* the pathspec pattern sastisfies GFNM_ONESTAR */
 +
  struct pathspec {
        const char **raw; /* get_pathspec() result, not freed by free_pathspec() */
        int nr;
        struct pathspec_item {
                const char *match;
                int len;
 -              unsigned int use_wildcard:1;
 +              int nowildcard_len;
 +              int flags;
        } *items;
  };
  
@@@ -493,6 -491,8 +494,8 @@@ extern int init_pathspec(struct pathspe
  extern void free_pathspec(struct pathspec *);
  extern int ce_path_match(const struct cache_entry *ce, const struct pathspec *pathspec);
  
+ extern int limit_pathspec_to_literal(void);
  #define HASH_WRITE_OBJECT 1
  #define HASH_FORMAT_CHECK 2
  extern int index_fd(unsigned char *sha1, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
@@@ -717,11 -717,10 +720,11 @@@ static inline int is_absolute_path(cons
  }
  int is_directory(const char *);
  const char *real_path(const char *path);
 +const char *real_path_if_valid(const char *path);
  const char *absolute_path(const char *path);
  const char *relative_path(const char *abs, const char *base);
  int normalize_path_copy(char *dst, const char *src);
 -int longest_ancestor_length(const char *path, const char *prefix_list);
 +int longest_ancestor_length(const char *path, struct string_list *prefixes);
  char *strip_path_suffix(const char *path, const char *suffix);
  int daemon_avoid_alias(const char *path);
  int offset_1st_component(const char *path);
@@@ -1003,19 -1002,14 +1006,19 @@@ struct ref 
        unsigned char old_sha1[20];
        unsigned char new_sha1[20];
        char *symref;
 -      unsigned int force:1,
 +      unsigned int
 +              force:1,
 +              requires_force:1,
                merge:1,
                nonfastforward:1,
 +              not_forwardable:1,
 +              update:1,
                deletion:1;
        enum {
                REF_STATUS_NONE = 0,
                REF_STATUS_OK,
                REF_STATUS_REJECT_NONFASTFORWARD,
 +              REF_STATUS_REJECT_ALREADY_EXISTS,
                REF_STATUS_REJECT_NODELETE,
                REF_STATUS_UPTODATE,
                REF_STATUS_REMOTE_REJECT,
@@@ -1145,9 -1139,6 +1148,9 @@@ extern int check_repository_format_vers
  extern int git_env_bool(const char *, int);
  extern int git_config_system(void);
  extern int config_error_nonbool(const char *);
 +#ifdef __GNUC__
 +#define config_error_nonbool(s) (config_error_nonbool(s), -1)
 +#endif
  extern const char *get_log_output_encoding(void);
  extern const char *get_commit_output_encoding(void);
  
@@@ -1167,7 -1158,6 +1170,7 @@@ extern int author_ident_sufficiently_gi
  extern const char *git_commit_encoding;
  extern const char *git_log_output_encoding;
  extern const char *git_mailmap_file;
 +extern const char *git_mailmap_blob;
  
  /* IO helper functions */
  extern void maybe_flush_or_die(FILE *, const char *);
diff --combined dir.c
index 9afd388604c05b5c7f7c34cd19f1e6ff2a14fdce,03ff36bc6eb24d716fc3fa49ff745575fa79683b..095ea7ebabfa25f1ff1aba199c09daaac984dbaa
--- 1/dir.c
--- 2/dir.c
+++ b/dir.c
@@@ -34,32 -34,11 +34,33 @@@ int fnmatch_icase(const char *pattern, 
        return fnmatch(pattern, string, flags | (ignore_case ? FNM_CASEFOLD : 0));
  }
  
 +inline int git_fnmatch(const char *pattern, const char *string,
 +                     int flags, int prefix)
 +{
 +      int fnm_flags = 0;
 +      if (flags & GFNM_PATHNAME)
 +              fnm_flags |= FNM_PATHNAME;
 +      if (prefix > 0) {
 +              if (strncmp(pattern, string, prefix))
 +                      return FNM_NOMATCH;
 +              pattern += prefix;
 +              string += prefix;
 +      }
 +      if (flags & GFNM_ONESTAR) {
 +              int pattern_len = strlen(++pattern);
 +              int string_len = strlen(string);
 +              return string_len < pattern_len ||
 +                     strcmp(pattern,
 +                            string + string_len - pattern_len);
 +      }
 +      return fnmatch(pattern, string, fnm_flags);
 +}
 +
  static size_t common_prefix_len(const char **pathspec)
  {
        const char *n, *first;
        size_t max = 0;
+       int literal = limit_pathspec_to_literal();
  
        if (!pathspec)
                return max;
@@@ -69,7 -48,7 +70,7 @@@
                size_t i, len = 0;
                for (i = 0; first == n || i < max; i++) {
                        char c = n[i];
-                       if (!c || c != first[i] || is_glob_special(c))
+                       if (!c || c != first[i] || (!literal && is_glob_special(c)))
                                break;
                        if (c == '/')
                                len = i + 1;
@@@ -139,6 -118,7 +140,7 @@@ int within_depth(const char *name, int 
  static int match_one(const char *match, const char *name, int namelen)
  {
        int matchlen;
+       int literal = limit_pathspec_to_literal();
  
        /* If the match was just the prefix, we matched */
        if (!*match)
                for (;;) {
                        unsigned char c1 = tolower(*match);
                        unsigned char c2 = tolower(*name);
-                       if (c1 == '\0' || is_glob_special(c1))
+                       if (c1 == '\0' || (!literal && is_glob_special(c1)))
                                break;
                        if (c1 != c2)
                                return 0;
                for (;;) {
                        unsigned char c1 = *match;
                        unsigned char c2 = *name;
-                       if (c1 == '\0' || is_glob_special(c1))
+                       if (c1 == '\0' || (!literal && is_glob_special(c1)))
                                break;
                        if (c1 != c2)
                                return 0;
                }
        }
  
        /*
         * If we don't match the matchstring exactly,
         * we need to match by fnmatch
         */
        matchlen = strlen(match);
-       if (strncmp_icase(match, name, matchlen))
+       if (strncmp_icase(match, name, matchlen)) {
+               if (literal)
+                       return 0;
                return !fnmatch_icase(match, name, 0) ? MATCHED_FNMATCH : 0;
+       }
  
        if (namelen == matchlen)
                return MATCHED_EXACTLY;
@@@ -252,10 -234,7 +256,10 @@@ static int match_pathspec_item(const st
                        return MATCHED_RECURSIVELY;
        }
  
 -      if (item->use_wildcard && !fnmatch(match, name, 0))
 +      if (item->nowildcard_len < item->len &&
 +          !git_fnmatch(match, name,
 +                       item->flags & PATHSPEC_ONESTAR ? GFNM_ONESTAR : 0,
 +                       item->nowildcard_len - prefix))
                return MATCHED_FNMATCH;
  
        return 0;
@@@ -1454,14 -1433,10 +1458,18 @@@ int init_pathspec(struct pathspec *path
  
                item->match = path;
                item->len = strlen(path);
-               item->nowildcard_len = simple_length(path);
 -              item->use_wildcard = !limit_pathspec_to_literal() &&
 -                                   !no_wildcard(path);
 -              if (item->use_wildcard)
 -                      pathspec->has_wildcard = 1;
 +              item->flags = 0;
-               if (item->nowildcard_len < item->len) {
-                       pathspec->has_wildcard = 1;
-                       if (path[item->nowildcard_len] == '*' &&
-                           no_wildcard(path + item->nowildcard_len + 1))
-                               item->flags |= PATHSPEC_ONESTAR;
++              if (limit_pathspec_to_literal()) {
++                      item->nowildcard_len = item->len;
++              } else {
++                      item->nowildcard_len = simple_length(path);
++                      if (item->nowildcard_len < item->len) {
++                              pathspec->has_wildcard = 1;
++                              if (path[item->nowildcard_len] == '*' &&
++                                  no_wildcard(path + item->nowildcard_len + 1))
++                                      item->flags |= PATHSPEC_ONESTAR;
++                      }
 +              }
        }
  
        qsort(pathspec->items, pathspec->nr,
@@@ -1475,3 -1450,11 +1483,11 @@@ void free_pathspec(struct pathspec *pat
        free(pathspec->items);
        pathspec->items = NULL;
  }
+ int limit_pathspec_to_literal(void)
+ {
+       static int flag = -1;
+       if (flag < 0)
+               flag = git_env_bool(GIT_LITERAL_PATHSPECS_ENVIRONMENT, 0);
+       return flag;
+ }