Code

Merge branch 'bc/attr-ignore-case'
authorJunio C Hamano <gitster@pobox.com>
Tue, 18 Oct 2011 04:37:13 +0000 (21:37 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 18 Oct 2011 04:37:14 +0000 (21:37 -0700)
* bc/attr-ignore-case:
  attr.c: respect core.ignorecase when matching attribute patterns
  attr: read core.attributesfile from git_default_core_config
  builtin/mv.c: plug miniscule memory leak
  cleanup: use internal memory allocation wrapper functions everywhere
  attr.c: avoid inappropriate access to strbuf "buf" member

Conflicts:
transport-helper.c

1  2 
builtin/check-attr.c
cache.h
config.c
environment.c
t/t0003-attributes.sh

diff --combined builtin/check-attr.c
index ded0d836d39d101f7879fecb11e7057a006c1b17,abb11650fdd936d01210552b68739f2bff82ed06..44c421eb0fe9cd8947d6666d15d790bc241ee7f3
@@@ -5,7 -5,6 +5,7 @@@
  #include "parse-options.h"
  
  static int all_attrs;
 +static int cached_attrs;
  static int stdin_paths;
  static const char * const check_attr_usage[] = {
  "git check-attr [-a | --all | attr...] [--] pathname...",
@@@ -17,7 -16,6 +17,7 @@@ static int null_term_line
  
  static const struct option check_attr_options[] = {
        OPT_BOOLEAN('a', "all", &all_attrs, "report all attributes set on file"),
 +      OPT_BOOLEAN(0,  "cached", &cached_attrs, "use .gitattributes only from the index"),
        OPT_BOOLEAN(0 , "stdin", &stdin_paths, "read file names from stdin"),
        OPT_BOOLEAN('z', NULL, &null_term_line,
                "input paths are terminated by a null character"),
@@@ -94,6 -92,8 +94,8 @@@ int cmd_check_attr(int argc, const cha
        struct git_attr_check *check;
        int cnt, i, doubledash, filei;
  
+       git_config(git_default_config, NULL);
        argc = parse_options(argc, argv, prefix, check_attr_options,
                             check_attr_usage, PARSE_OPT_KEEP_DASHDASH);
  
                die("invalid cache");
        }
  
 +      if (cached_attrs)
 +              git_attr_set_direction(GIT_ATTR_INDEX, NULL);
 +
        doubledash = -1;
        for (i = 0; doubledash < 0 && i < argc; i++) {
                if (!strcmp(argv[i], "--"))
diff --combined cache.h
index 85c099655eb3486a7f2b65bc31742d4e9e0780d5,8d95fb25a2505a408aea4beba43409facb578213..be07ec70863cae3f6e1c9d5dbc6dc555e444e30a
+++ b/cache.h
@@@ -168,7 -168,6 +168,7 @@@ struct cache_entry 
        unsigned int ce_flags;
        unsigned char sha1[20];
        struct cache_entry *next;
 +      struct cache_entry *dir_next;
        char name[FLEX_ARRAY]; /* more */
  };
  
@@@ -440,12 -439,12 +440,12 @@@ extern const char *get_git_namespace(vo
  extern const char *strip_namespace(const char *namespaced_ref);
  extern const char *get_git_work_tree(void);
  extern const char *read_gitfile(const char *path);
 +extern const char *resolve_gitdir(const char *suspect);
  extern void set_git_work_tree(const char *tree);
  
  #define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
  
  extern const char **get_pathspec(const char *prefix, const char **pathspec);
 -extern const char *pathspec_prefix(const char *prefix, const char **pathspec);
  extern void setup_work_tree(void);
  extern const char *setup_git_directory_gently(int *);
  extern const char *setup_git_directory(void);
@@@ -590,6 -589,7 +590,7 @@@ extern int warn_ambiguous_refs
  extern int shared_repository;
  extern const char *apply_default_whitespace;
  extern const char *apply_default_ignorewhitespace;
+ extern const char *git_attributes_file;
  extern int zlib_compression_level;
  extern int core_compression_level;
  extern int core_compression_seen;
@@@ -820,51 -820,10 +821,51 @@@ static inline int get_sha1_with_context
  {
        return get_sha1_with_context_1(str, sha1, orc, 0, NULL);
  }
 +
 +/*
 + * Try to read a SHA1 in hexadecimal format from the 40 characters
 + * starting at hex.  Write the 20-byte result to sha1 in binary form.
 + * Return 0 on success.  Reading stops if a NUL is encountered in the
 + * input, so it is safe to pass this function an arbitrary
 + * null-terminated string.
 + */
  extern int get_sha1_hex(const char *hex, unsigned char *sha1);
 +
  extern char *sha1_to_hex(const unsigned char *sha1);  /* static buffer result! */
  extern int read_ref(const char *filename, unsigned char *sha1);
 -extern const char *resolve_ref(const char *path, unsigned char *sha1, int, int *);
 +
 +/*
 + * Resolve a reference, recursively following symbolic refererences.
 + *
 + * Store the referred-to object's name in sha1 and return the name of
 + * the non-symbolic reference that ultimately pointed at it.  The
 + * return value, if not NULL, is a pointer into either a static buffer
 + * or the input ref.
 + *
 + * If the reference cannot be resolved to an object, the behavior
 + * depends on the "reading" argument:
 + *
 + * - If reading is set, return NULL.
 + *
 + * - If reading is not set, clear sha1 and return the name of the last
 + *   reference name in the chain, which will either be a non-symbolic
 + *   reference or an undefined reference.  If this is a prelude to
 + *   "writing" to the ref, the return value is the name of the ref
 + *   that will actually be created or changed.
 + *
 + * If flag is non-NULL, set the value that it points to the
 + * combination of REF_ISPACKED (if the reference was found among the
 + * packed references) and REF_ISSYMREF (if the initial reference was a
 + * symbolic reference).
 + *
 + * If ref is not a properly-formatted, normalized reference, return
 + * NULL.  If more than MAXDEPTH recursive symbolic lookups are needed,
 + * give up and return NULL.
 + *
 + * errno is sometimes set on errors, but not always.
 + */
 +extern const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *flag);
 +
  extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref);
  extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);
  extern int interpret_branch_name(const char *str, struct strbuf *);
@@@ -1118,11 -1077,9 +1119,11 @@@ extern int git_config_bool(const char *
  extern int git_config_maybe_bool(const char *, const char *);
  extern int git_config_string(const char **, const char *, const char *);
  extern int git_config_pathname(const char **, const char *, const char *);
 +extern int git_config_set_in_file(const char *, const char *, const char *);
  extern int git_config_set(const char *, const char *);
  extern int git_config_parse_key(const char *, char **, int *);
  extern int git_config_set_multivar(const char *, const char *, const char *, int);
 +extern int git_config_set_multivar_in_file(const char *, const char *, const char *, const char *, int);
  extern int git_config_rename_section(const char *, const char *);
  extern const char *git_etc_gitconfig(void);
  extern int check_repository_format_version(const char *var, const char *value, void *cb);
diff --combined config.c
index a9e23594bd18b1cde49b55a40b2af8e2f3b74439,d3bcaa023d1f238d8fa9b2c416ae7448eff36d17..edf9914df6a1a789780c98d53b7b779908bb9141
+++ b/config.c
@@@ -491,6 -491,9 +491,9 @@@ static int git_default_core_config(cons
                return 0;
        }
  
+       if (!strcmp(var, "core.attributesfile"))
+               return git_config_pathname(&git_attributes_file, var, value);
        if (!strcmp(var, "core.bare")) {
                is_bare_repository_cfg = git_config_bool(var, value);
                return 0;
@@@ -1095,12 -1098,6 +1098,12 @@@ contline
        return offset;
  }
  
 +int git_config_set_in_file(const char *config_filename,
 +                      const char *key, const char *value)
 +{
 +      return git_config_set_multivar_in_file(config_filename, key, value, NULL, 0);
 +}
 +
  int git_config_set(const char *key, const char *value)
  {
        return git_config_set_multivar(key, value, NULL, 0);
@@@ -1198,14 -1195,19 +1201,14 @@@ out_free_ret_1
   * - the config file is removed and the lock file rename()d to it.
   *
   */
 -int git_config_set_multivar(const char *key, const char *value,
 -      const char *value_regex, int multi_replace)
 +int git_config_set_multivar_in_file(const char *config_filename,
 +                              const char *key, const char *value,
 +                              const char *value_regex, int multi_replace)
  {
        int fd = -1, in_fd;
        int ret;
 -      char *config_filename;
        struct lock_file *lock = NULL;
  
 -      if (config_exclusive_filename)
 -              config_filename = xstrdup(config_exclusive_filename);
 -      else
 -              config_filename = git_pathdup("config");
 -
        /* parse-key returns negative; flip the sign to feed exit(3) */
        ret = 0 - git_config_parse_key(key, &store.key, &store.baselen);
        if (ret)
  out_free:
        if (lock)
                rollback_lock_file(lock);
 -      free(config_filename);
        return ret;
  
  write_err_out:
  
  }
  
 +int git_config_set_multivar(const char *key, const char *value,
 +                      const char *value_regex, int multi_replace)
 +{
 +      const char *config_filename;
 +      char *buf = NULL;
 +      int ret;
 +
 +      if (config_exclusive_filename)
 +              config_filename = config_exclusive_filename;
 +      else
 +              config_filename = buf = git_pathdup("config");
 +
 +      ret = git_config_set_multivar_in_file(config_filename, key, value,
 +                                      value_regex, multi_replace);
 +      free(buf);
 +      return ret;
 +}
 +
  static int section_name_match (const char *buf, const char *name)
  {
        int i = 0, j = 0, dot = 0;
diff --combined environment.c
index 8174b703c4a6dd62b01dabb34acc2a6492b43ba2,d60b73f6a5a88632a2a36140bf719484adec9f39..0bee6a7a88299f8c89eedeee25cece1d3cdafef0
@@@ -29,6 -29,7 +29,7 @@@ const char *git_log_output_encoding
  int shared_repository = PERM_UMASK;
  const char *apply_default_whitespace;
  const char *apply_default_ignorewhitespace;
+ const char *git_attributes_file;
  int zlib_compression_level = Z_BEST_SPEED;
  int core_compression_level;
  int core_compression_seen;
@@@ -106,7 -107,7 +107,7 @@@ static char *expand_namespace(const cha
                if (strcmp((*c)->buf, "/") != 0)
                        strbuf_addf(&buf, "refs/namespaces/%s", (*c)->buf);
        strbuf_list_free(components);
 -      if (check_ref_format(buf.buf) != CHECK_REF_FORMAT_OK)
 +      if (check_refname_format(buf.buf, 0))
                die("bad git namespace path \"%s\"", raw_namespace);
        strbuf_addch(&buf, '/');
        return strbuf_detach(&buf, NULL);
diff --combined t/t0003-attributes.sh
index 46b0736b351d4676bdab16ec69ad1dfe42f3a900,6946c4b82f9291f6618d49de2e3892a2c203d337..dbb2623d930e433111f7e125749f5f1071e9ab3c
@@@ -5,16 -5,20 +5,16 @@@ test_description=gitattribute
  . ./test-lib.sh
  
  attr_check () {
 -
 -      path="$1"
 -      expect="$2"
 +      path="$1" expect="$2"
  
-       git check-attr test -- "$path" >actual 2>err &&
+       git $3 check-attr test -- "$path" >actual 2>err &&
        echo "$path: test: $2" >expect &&
        test_cmp expect actual &&
        test_line_count = 0 err
 -
  }
  
  
  test_expect_success 'setup' '
 -
        mkdir -p a/b/d a/c b &&
        (
                echo "[attr]notest !test"
@@@ -23,6 -27,7 +23,7 @@@
                echo "onoff test -test"
                echo "offon -test test"
                echo "no notest"
+               echo "A/e/F test=A/e/F"
        ) >.gitattributes &&
        (
                echo "g test=a/g" &&
        (
                echo "global test=global"
        ) >"$HOME"/global-gitattributes &&
 -      cat <<EOF >expect-all
 -f: test: f
 -a/f: test: f
 -a/c/f: test: f
 -a/g: test: a/g
 -a/b/g: test: a/b/g
 -b/g: test: unspecified
 -a/b/h: test: a/b/h
 -a/b/d/g: test: a/b/d/*
 -onoff: test: unset
 -offon: test: set
 -no: notest: set
 -no: test: unspecified
 -a/b/d/no: notest: set
 -a/b/d/no: test: a/b/d/*
 -a/b/d/yes: notest: set
 -a/b/d/yes: test: unspecified
 -EOF
 -
 +      cat <<-EOF >expect-all
 +      f: test: f
 +      a/f: test: f
 +      a/c/f: test: f
 +      a/g: test: a/g
 +      a/b/g: test: a/b/g
 +      b/g: test: unspecified
 +      a/b/h: test: a/b/h
 +      a/b/d/g: test: a/b/d/*
 +      onoff: test: unset
 +      offon: test: set
 +      no: notest: set
 +      no: test: unspecified
 +      a/b/d/no: notest: set
 +      a/b/d/no: test: a/b/d/*
 +      a/b/d/yes: notest: set
 +      a/b/d/yes: test: unspecified
 +      EOF
  '
  
  test_expect_success 'command line checks' '
 -
        test_must_fail git check-attr &&
        test_must_fail git check-attr -- &&
        test_must_fail git check-attr test &&
        echo "f" | test_must_fail git check-attr --stdin -- f &&
        echo "f" | test_must_fail git check-attr --stdin test -- f &&
        test_must_fail git check-attr "" -- f
 -
  '
  
  test_expect_success 'attribute test' '
 -
        attr_check f f &&
        attr_check a/f f &&
        attr_check a/c/f f &&
        attr_check no unspecified &&
        attr_check a/b/d/no "a/b/d/*" &&
        attr_check a/b/d/yes unspecified
 -
  '
  
+ test_expect_success 'attribute matching is case sensitive when core.ignorecase=0' '
+       test_must_fail attr_check F f "-c core.ignorecase=0" &&
+       test_must_fail attr_check a/F f "-c core.ignorecase=0" &&
+       test_must_fail attr_check a/c/F f "-c core.ignorecase=0" &&
+       test_must_fail attr_check a/G a/g "-c core.ignorecase=0" &&
+       test_must_fail attr_check a/B/g a/b/g "-c core.ignorecase=0" &&
+       test_must_fail attr_check a/b/G a/b/g "-c core.ignorecase=0" &&
+       test_must_fail attr_check a/b/H a/b/h "-c core.ignorecase=0" &&
+       test_must_fail attr_check a/b/D/g "a/b/d/*" "-c core.ignorecase=0" &&
+       test_must_fail attr_check oNoFf unset "-c core.ignorecase=0" &&
+       test_must_fail attr_check oFfOn set "-c core.ignorecase=0" &&
+       attr_check NO unspecified "-c core.ignorecase=0" &&
+       test_must_fail attr_check a/b/D/NO "a/b/d/*" "-c core.ignorecase=0" &&
+       attr_check a/b/d/YES a/b/d/* "-c core.ignorecase=0" &&
+       test_must_fail attr_check a/E/f "A/e/F" "-c core.ignorecase=0"
+ '
+ test_expect_success 'attribute matching is case insensitive when core.ignorecase=1' '
+       attr_check F f "-c core.ignorecase=1" &&
+       attr_check a/F f "-c core.ignorecase=1" &&
+       attr_check a/c/F f "-c core.ignorecase=1" &&
+       attr_check a/G a/g "-c core.ignorecase=1" &&
+       attr_check a/B/g a/b/g "-c core.ignorecase=1" &&
+       attr_check a/b/G a/b/g "-c core.ignorecase=1" &&
+       attr_check a/b/H a/b/h "-c core.ignorecase=1" &&
+       attr_check a/b/D/g "a/b/d/*" "-c core.ignorecase=1" &&
+       attr_check oNoFf unset "-c core.ignorecase=1" &&
+       attr_check oFfOn set "-c core.ignorecase=1" &&
+       attr_check NO unspecified "-c core.ignorecase=1" &&
+       attr_check a/b/D/NO "a/b/d/*" "-c core.ignorecase=1" &&
+       attr_check a/b/d/YES unspecified "-c core.ignorecase=1" &&
+       attr_check a/E/f "A/e/F" "-c core.ignorecase=1"
+ '
+ test_expect_success 'check whether FS is case-insensitive' '
+       mkdir junk &&
+       echo good >junk/CamelCase &&
+       echo bad >junk/camelcase &&
+       if test "$(cat junk/CamelCase)" != good
+       then
+               test_set_prereq CASE_INSENSITIVE_FS
+       fi
+ '
+ test_expect_success CASE_INSENSITIVE_FS 'additional case insensitivity tests' '
+       test_must_fail attr_check a/B/D/g "a/b/d/*" "-c core.ignorecase=0" &&
+       test_must_fail attr_check A/B/D/NO "a/b/d/*" "-c core.ignorecase=0" &&
+       attr_check A/b/h a/b/h "-c core.ignorecase=1" &&
+       attr_check a/B/D/g "a/b/d/*" "-c core.ignorecase=1" &&
+       attr_check A/B/D/NO "a/b/d/*" "-c core.ignorecase=1"
+ '
  test_expect_success 'unnormalized paths' '
 -
        attr_check ./f f &&
        attr_check ./a/g a/g &&
        attr_check a/./g a/g &&
        attr_check a/c/../b/g a/b/g
 -
  '
  
  test_expect_success 'relative paths' '
 -
        (cd a && attr_check ../f f) &&
        (cd a && attr_check f f) &&
        (cd a && attr_check i a/i) &&
        (cd b && attr_check ../a/f f) &&
        (cd b && attr_check ../a/g a/g) &&
        (cd b && attr_check ../a/b/g a/b/g)
 -
  '
  
  test_expect_success 'core.attributesfile' '
        attr_check global global &&
        git config core.attributesfile "~/global-gitattributes" &&
        attr_check global global &&
 -      echo "global test=precedence" >> .gitattributes &&
 +      echo "global test=precedence" >>.gitattributes &&
        attr_check global precedence
  '
  
  test_expect_success 'attribute test: read paths from stdin' '
 -
 -      grep -v notest < expect-all > expect &&
 -      sed -e "s/:.*//" < expect | git check-attr --stdin test > actual &&
 +      grep -v notest <expect-all >expect &&
 +      sed -e "s/:.*//" <expect | git check-attr --stdin test >actual &&
        test_cmp expect actual
  '
  
  test_expect_success 'attribute test: --all option' '
 +      grep -v unspecified <expect-all | sort >specified-all &&
 +      sed -e "s/:.*//" <expect-all | uniq >stdin-all &&
 +      git check-attr --stdin --all <stdin-all | sort >actual &&
 +      test_cmp specified-all actual
 +'
  
 -      grep -v unspecified < expect-all | sort > expect &&
 -      sed -e "s/:.*//" < expect-all | uniq |
 -              git check-attr --stdin --all | sort > actual &&
 -      test_cmp expect actual
 +test_expect_success 'attribute test: --cached option' '
 +      : >empty &&
 +      git check-attr --cached --stdin --all <stdin-all | sort >actual &&
 +      test_cmp empty actual &&
 +      git add .gitattributes a/.gitattributes a/b/.gitattributes &&
 +      git check-attr --cached --stdin --all <stdin-all | sort >actual &&
 +      test_cmp specified-all actual
  '
  
  test_expect_success 'root subdir attribute test' '
 -
        attr_check a/i a/i &&
        attr_check subdir/a/i unspecified
 -
  '
  
  test_expect_success 'setup bare' '
 -
        git clone --bare . bare.git &&
        cd bare.git
 -
  '
  
  test_expect_success 'bare repository: check that .gitattribute is ignored' '
 -
        (
                echo "f test=f"
                echo "a/i test=a/i"
        attr_check a/c/f unspecified &&
        attr_check a/i unspecified &&
        attr_check subdir/a/i unspecified
 +'
  
 +test_expect_success 'bare repository: check that --cached honors index' '
 +      GIT_INDEX_FILE=../.git/index \
 +      git check-attr --cached --stdin --all <../stdin-all |
 +      sort >actual &&
 +      test_cmp ../specified-all actual
  '
  
  test_expect_success 'bare repository: test info/attributes' '
 -
        (
                echo "f test=f"
                echo "a/i test=a/i"
        attr_check a/c/f f &&
        attr_check a/i a/i &&
        attr_check subdir/a/i unspecified
 -
  '
  
  test_done