Merge branch 'ta/config-add-to-empty-or-true-fix' into maint
authorJunio C Hamano <gitster@pobox.com>
Tue, 30 Sep 2014 05:10:25 +0000 (22:10 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 30 Sep 2014 05:10:25 +0000 (22:10 -0700)
"git config --add section.var val" used to lose existing
section.var whose value was an empty string.

* ta/config-add-to-empty-or-true-fix:
config: avoid a funny sentinel value "a^"
make config --add behave correctly for empty and NULL values

1  2 
builtin/config.c
cache.h
config.c
diff --combined builtin/config.c
index fcd84747015a3d0a1da048612f5ac119a7e161d1,bf1aa6b2e4e61784f3d6af3bf1ba2eaafbfbca3e..7bba5163834c87272a85ba324821beb1c9bca2c1
@@@ -395,6 -395,19 +395,6 @@@ static int urlmatch_collect_fn(const ch
        return 0;
  }
  
 -static char *dup_downcase(const char *string)
 -{
 -      char *result;
 -      size_t len, i;
 -
 -      len = strlen(string);
 -      result = xmalloc(len + 1);
 -      for (i = 0; i < len; i++)
 -              result[i] = tolower(string[i]);
 -      result[i] = '\0';
 -      return result;
 -}
 -
  static int get_urlmatch(const char *var, const char *url)
  {
        char *section_tail;
        if (!url_normalize(url, &config.url))
                die("%s", config.url.err);
  
 -      config.section = dup_downcase(var);
 +      config.section = xstrdup_tolower(var);
        section_tail = strchr(config.section, '.');
        if (section_tail) {
                *section_tail = '\0';
@@@ -586,7 -599,8 +586,8 @@@ int cmd_config(int argc, const char **a
                check_argc(argc, 2, 2);
                value = normalize_value(argv[0], argv[1]);
                return git_config_set_multivar_in_file(given_config_source.file,
-                                                      argv[0], value, "^$", 0);
+                                                      argv[0], value,
+                                                      CONFIG_REGEX_NONE, 0);
        }
        else if (actions == ACTION_REPLACE_ALL) {
                check_write();
diff --combined cache.h
index fcb511db70f7703f2b29dbc89dcf703065c823fe,8356168bb5db9c5043ea48462386b1cf278e8d3d..dcf3a2afe9d782e4b2474d8834602f80610ba25f
+++ b/cache.h
@@@ -7,7 -7,6 +7,7 @@@
  #include "advice.h"
  #include "gettext.h"
  #include "convert.h"
 +#include "trace.h"
  
  #include SHA1_HEADER
  #ifndef git_SHA_CTX
@@@ -75,21 -74,6 +75,21 @@@ unsigned long git_deflate_bound(git_zst
  #define S_IFGITLINK   0160000
  #define S_ISGITLINK(m)        (((m) & S_IFMT) == S_IFGITLINK)
  
 +/*
 + * Some mode bits are also used internally for computations.
 + *
 + * They *must* not overlap with any valid modes, and they *must* not be emitted
 + * to outside world - i.e. appear on disk or network. In other words, it's just
 + * temporary fields, which we internally use, but they have to stay in-house.
 + *
 + * ( such approach is valid, as standard S_IF* fits into 16 bits, and in Git
 + *   codebase mode is `unsigned int` which is assumed to be at least 32 bits )
 + */
 +
 +/* used internally in tree-diff */
 +#define S_DIFFTREE_IFXMIN_NEQ 0x80000000
 +
 +
  /*
   * Intensive research over the course of many years has shown that
   * port 9418 is totally unused by anything else. Or
@@@ -151,7 -135,6 +151,7 @@@ struct cache_entry 
        unsigned int ce_mode;
        unsigned int ce_flags;
        unsigned int ce_namelen;
 +      unsigned int index;     /* for link extension */
        unsigned char sha1[20];
        char name[FLEX_ARRAY]; /* more */
  };
  #define CE_STAGESHIFT 12
  
  /*
 - * Range 0xFFFF0000 in ce_flags is divided into
 + * Range 0xFFFF0FFF in ce_flags is divided into
   * two parts: in-memory flags and on-disk ones.
   * Flags in CE_EXTENDED_FLAGS will get saved on-disk
   * if you want to save a new flag, add it in
  /* used to temporarily mark paths matched by pathspecs */
  #define CE_MATCHED           (1 << 26)
  
 +#define CE_UPDATE_IN_BASE    (1 << 27)
 +#define CE_STRIP_NAME        (1 << 28)
 +
  /*
   * Extended on-disk flags
   */
@@@ -288,22 -268,12 +288,22 @@@ static inline unsigned int canon_mode(u
  
  #define cache_entry_size(len) (offsetof(struct cache_entry,name) + (len) + 1)
  
 +#define SOMETHING_CHANGED     (1 << 0) /* unclassified changes go here */
 +#define CE_ENTRY_CHANGED      (1 << 1)
 +#define CE_ENTRY_REMOVED      (1 << 2)
 +#define CE_ENTRY_ADDED                (1 << 3)
 +#define RESOLVE_UNDO_CHANGED  (1 << 4)
 +#define CACHE_TREE_CHANGED    (1 << 5)
 +#define SPLIT_INDEX_ORDERED   (1 << 6)
 +
 +struct split_index;
  struct index_state {
        struct cache_entry **cache;
        unsigned int version;
        unsigned int cache_nr, cache_alloc, cache_changed;
        struct string_list *resolve_undo;
        struct cache_tree *cache_tree;
 +      struct split_index *split_index;
        struct cache_time timestamp;
        unsigned name_hash_initialized : 1,
                 initialized : 1;
@@@ -332,6 -302,7 +332,6 @@@ extern void free_name_hash(struct index
  #define read_cache_preload(pathspec) read_index_preload(&the_index, (pathspec))
  #define is_cache_unborn() is_index_unborn(&the_index)
  #define read_cache_unmerged() read_index_unmerged(&the_index)
 -#define write_cache(newfd, cache, entries) write_index(&the_index, (newfd))
  #define discard_cache() discard_index(&the_index)
  #define unmerged_cache() unmerged_index(&the_index)
  #define cache_name_pos(name, namelen) index_name_pos(&the_index,(name),(namelen))
@@@ -486,17 -457,12 +486,17 @@@ extern int daemonize(void)
        } while (0)
  
  /* Initialize and use the cache information */
 +struct lock_file;
  extern int read_index(struct index_state *);
  extern int read_index_preload(struct index_state *, const struct pathspec *pathspec);
 +extern int do_read_index(struct index_state *istate, const char *path,
 +                       int must_exist); /* for testting only! */
  extern int read_index_from(struct index_state *, const char *path);
  extern int is_index_unborn(struct index_state *);
  extern int read_index_unmerged(struct index_state *);
 -extern int write_index(struct index_state *, int newfd);
 +#define COMMIT_LOCK           (1 << 0)
 +#define CLOSE_LOCK            (1 << 1)
 +extern int write_locked_index(struct index_state *, struct lock_file *lock, unsigned flags);
  extern int discard_index(struct index_state *);
  extern int unmerged_index(const struct index_state *);
  extern int verify_path(const char *path);
@@@ -508,7 -474,6 +508,7 @@@ extern int index_name_pos(const struct 
  #define ADD_CACHE_SKIP_DFCHECK 4      /* Ok to skip DF conflict checks */
  #define ADD_CACHE_JUST_APPEND 8               /* Append only; tree.c::read_tree() */
  #define ADD_CACHE_NEW_ONLY 16         /* Do not replace existing ones */
 +#define ADD_CACHE_KEEP_CACHE_TREE 32  /* Do not invalidate cache-tree */
  extern int add_index_entry(struct index_state *, struct cache_entry *ce, int option);
  extern void rename_index_entry_at(struct index_state *, int pos, const char *new_name);
  extern int remove_index_entry_at(struct index_state *, int pos);
@@@ -579,8 -544,6 +579,8 @@@ struct lock_file 
  #define LOCK_DIE_ON_ERROR 1
  #define LOCK_NODEREF 2
  extern int unable_to_lock_error(const char *path, int err);
 +extern void unable_to_lock_message(const char *path, int err,
 +                                 struct strbuf *buf);
  extern NORETURN void unable_to_lock_index_die(const char *path, int err);
  extern int hold_lock_file_for_update(struct lock_file *, const char *path, int);
  extern int hold_lock_file_for_append(struct lock_file *, const char *path, int);
@@@ -588,6 -551,7 +588,6 @@@ extern int commit_lock_file(struct lock
  extern void update_index_if_able(struct index_state *, struct lock_file *);
  
  extern int hold_locked_index(struct lock_file *, int);
 -extern int commit_locked_index(struct lock_file *);
  extern void set_alternate_index_output(const char *);
  extern int close_lock_file(struct lock_file *);
  extern void rollback_lock_file(struct lock_file *);
@@@ -639,7 -603,6 +639,7 @@@ extern int precomposed_unicode
   * that is subject to stripspace.
   */
  extern char comment_line_char;
 +extern int auto_comment_line_char;
  
  enum branch_track {
        BRANCH_TRACK_UNSPECIFIED = -1,
@@@ -847,6 -810,7 +847,6 @@@ int normalize_path_copy(char *dst, cons
  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);
  
  /* object replacement */
  #define LOOKUP_REPLACE_OBJECT 1
@@@ -998,7 -962,7 +998,7 @@@ extern int read_ref(const char *refname
   * NULL.  If more than MAXDEPTH recursive symbolic lookups are needed,
   * give up and return NULL.
   *
 - * errno is sometimes set on errors, but not always.
 + * errno is set to something meaningful on error.
   */
  extern const char *resolve_ref_unsafe(const char *ref, unsigned char *sha1, int reading, int *flag);
  extern char *resolve_refdup(const char *ref, unsigned char *sha1, int reading, int *flag);
@@@ -1020,7 -984,7 +1020,7 @@@ extern int validate_headref(const char 
  
  extern int base_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);
  extern int df_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);
 -extern int cache_name_compare(const char *name1, int len1, const char *name2, int len2);
 +extern int name_compare(const char *name1, size_t len1, const char *name2, size_t len2);
  extern int cache_name_stage_compare(const char *name1, int len1, int stage1, const char *name2, int len2, int stage2);
  
  extern void *read_object_with_reference(const unsigned char *sha1,
@@@ -1082,13 -1046,6 +1082,13 @@@ struct ident_split 
   */
  extern int split_ident_line(struct ident_split *, const char *, int);
  
 +/*
 + * Like show_date, but pull the timestamp and tz parameters from
 + * the ident_split. It will also sanity-check the values and produce
 + * a well-known sentinel date if they appear bogus.
 + */
 +const char *show_ident_date(const struct ident_split *id, enum date_mode mode);
 +
  /*
   * Compare split idents for equality or strict ordering. Note that we
   * compare only the ident part of the line, ignoring any timestamp.
  extern int ident_cmp(const struct ident_split *, const struct ident_split *);
  
  struct checkout {
 +      struct index_state *istate;
        const char *base_dir;
        int base_dir_len;
        unsigned force:1,
  extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath);
  
  struct cache_def {
 -      char path[PATH_MAX + 1];
 -      int len;
 +      struct strbuf path;
        int flags;
        int track_flags;
        int prefix_len_stat_func;
  };
 +#define CACHE_DEF_INIT { STRBUF_INIT, 0, 0, 0 }
 +static inline void cache_def_clear(struct cache_def *cache)
 +{
 +      strbuf_release(&cache->path);
 +}
  
  extern int has_symlink_leading_path(const char *name, int len);
  extern int threaded_has_symlink_leading_path(struct cache_def *, const char *, int);
@@@ -1281,6 -1233,8 +1281,8 @@@ extern int update_server_info(int)
  #define CONFIG_INVALID_PATTERN 6
  #define CONFIG_GENERIC_ERROR 7
  
+ #define CONFIG_REGEX_NONE ((void *)1)
  struct git_config_source {
        unsigned int use_stdin:1;
        const char *file;
@@@ -1320,8 -1274,8 +1322,8 @@@ 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 *);
 -#if defined(__GNUC__) && ! defined(__clang__)
 -#define config_error_nonbool(s) (config_error_nonbool(s), -1)
 +#if defined(__GNUC__)
 +#define config_error_nonbool(s) (config_error_nonbool(s), const_error())
  #endif
  extern const char *get_log_output_encoding(void);
  extern const char *get_commit_output_encoding(void);
@@@ -1404,7 -1358,17 +1406,7 @@@ extern void *alloc_object_node(void)
  extern void alloc_report(void);
  extern unsigned int alloc_commit_index(void);
  
 -/* trace.c */
 -__attribute__((format (printf, 1, 2)))
 -extern void trace_printf(const char *format, ...);
 -__attribute__((format (printf, 2, 3)))
 -extern void trace_argv_printf(const char **argv, const char *format, ...);
 -extern void trace_repo_setup(const char *prefix);
 -extern int trace_want(const char *key);
 -__attribute__((format (printf, 2, 3)))
 -extern void trace_printf_key(const char *key, const char *fmt, ...);
 -extern void trace_strbuf(const char *key, const struct strbuf *buf);
 -
 +/* pkt-line.c */
  void packet_trace_identity(const char *prog);
  
  /* add */
diff --combined config.c
index 6cbf701a8f3e8837dac9c308cc19a1483114879d,2e709bf93cdfc4951c99812b69f4d08d8c0c1e93..9e42d3832bbcce428705ef616d5b2008c47ec91a
+++ b/config.c
@@@ -138,7 -138,8 +138,7 @@@ int git_config_include(const char *var
        if (ret < 0)
                return ret;
  
 -      type = skip_prefix(var, "include.");
 -      if (!type)
 +      if (!skip_prefix(var, "include.", &type))
                return ret;
  
        if (!strcmp(type, "path"))
        return ret;
  }
  
 -static void lowercase(char *p)
 -{
 -      for (; *p; p++)
 -              *p = tolower(*p);
 -}
 -
  void git_config_push_parameter(const char *text)
  {
        struct strbuf env = STRBUF_INIT;
  int git_config_parse_parameter(const char *text,
                               config_fn_t fn, void *data)
  {
 +      const char *value;
        struct strbuf **pair;
 +
        pair = strbuf_split_str(text, '=', 2);
        if (!pair[0])
                return error("bogus config parameter: %s", text);
 -      if (pair[0]->len && pair[0]->buf[pair[0]->len - 1] == '=')
 +
 +      if (pair[0]->len && pair[0]->buf[pair[0]->len - 1] == '=') {
                strbuf_setlen(pair[0], pair[0]->len - 1);
 +              value = pair[1] ? pair[1]->buf : "";
 +      } else {
 +              value = NULL;
 +      }
 +
        strbuf_trim(pair[0]);
        if (!pair[0]->len) {
                strbuf_list_free(pair);
                return error("bogus config parameter: %s", text);
        }
 -      lowercase(pair[0]->buf);
 -      if (fn(pair[0]->buf, pair[1] ? pair[1]->buf : NULL, data) < 0) {
 +      strbuf_tolower(pair[0]);
 +      if (fn(pair[0]->buf, value, data) < 0) {
                strbuf_list_free(pair);
                return -1;
        }
@@@ -825,16 -824,11 +825,16 @@@ static int git_default_core_config(cons
                return git_config_string(&editor_program, var, value);
  
        if (!strcmp(var, "core.commentchar")) {
 -              const char *comment;
 -              int ret = git_config_string(&comment, var, value);
 -              if (!ret)
 -                      comment_line_char = comment[0];
 -              return ret;
 +              if (!value)
 +                      return config_error_nonbool(var);
 +              else if (!strcasecmp(value, "auto"))
 +                      auto_comment_line_char = 1;
 +              else if (value[0] && !value[1]) {
 +                      comment_line_char = value[0];
 +                      auto_comment_line_char = 0;
 +              } else
 +                      return error("core.commentChar should only be one character");
 +              return 0;
        }
  
        if (!strcmp(var, "core.askpass"))
@@@ -1236,10 -1230,15 +1236,15 @@@ static struct 
  
  static int matches(const char *key, const char *value)
  {
-       return !strcmp(key, store.key) &&
-               (store.value_regex == NULL ||
-                (store.do_not_match ^
-                 !regexec(store.value_regex, value, 0, NULL, 0)));
+       if (strcmp(key, store.key))
+               return 0; /* not ours */
+       if (!store.value_regex)
+               return 1; /* always matches */
+       if (store.value_regex == CONFIG_REGEX_NONE)
+               return 0; /* never matches */
+       return store.do_not_match ^
+               (value && !regexec(store.value_regex, value, 0, NULL, 0));
  }
  
  static int store_aux(const char *key, const char *value, void *cb)
@@@ -1501,6 -1500,8 +1506,8 @@@ out_free_ret_1
  /*
   * If value==NULL, unset in (remove from) config,
   * if value_regex!=NULL, disregard key/value pairs where value does not match.
+  * if value_regex==CONFIG_REGEX_NONE, do not match any existing values
+  *     (only add a new one)
   * if multi_replace==0, nothing, or only one matching key/value is replaced,
   *     else all matching key/values (regardless how many) are removed,
   *     before the new pair is written.
@@@ -1584,6 -1585,8 +1591,8 @@@ int git_config_set_multivar_in_file(con
  
                if (value_regex == NULL)
                        store.value_regex = NULL;
+               else if (value_regex == CONFIG_REGEX_NONE)
+                       store.value_regex = CONFIG_REGEX_NONE;
                else {
                        if (value_regex[0] == '!') {
                                store.do_not_match = 1;
                if (git_config_from_file(store_aux, config_filename, NULL)) {
                        error("invalid config file %s", config_filename);
                        free(store.key);
-                       if (store.value_regex != NULL) {
+                       if (store.value_regex != NULL &&
+                           store.value_regex != CONFIG_REGEX_NONE) {
                                regfree(store.value_regex);
                                free(store.value_regex);
                        }
                }
  
                free(store.key);
-               if (store.value_regex != NULL) {
+               if (store.value_regex != NULL &&
+                   store.value_regex != CONFIG_REGEX_NONE) {
                        regfree(store.value_regex);
                        free(store.value_regex);
                }
                        MAP_PRIVATE, in_fd, 0);
                close(in_fd);
  
 +              if (chmod(lock->filename, st.st_mode & 07777) < 0) {
 +                      error("chmod on %s failed: %s",
 +                              lock->filename, strerror(errno));
 +                      ret = CONFIG_NO_WRITE;
 +                      goto out_free;
 +              }
 +
                if (store.seen == 0)
                        store.seen = 1;
  
@@@ -1797,7 -1795,6 +1808,7 @@@ int git_config_rename_section_in_file(c
        int out_fd;
        char buf[1024];
        FILE *config_file;
 +      struct stat st;
  
        if (new_name && !section_name_is_ok(new_name)) {
                ret = error("invalid section name: %s", new_name);
                goto unlock_and_out;
        }
  
 +      fstat(fileno(config_file), &st);
 +
 +      if (chmod(lock->filename, st.st_mode & 07777) < 0) {
 +              ret = error("chmod on %s failed: %s",
 +                              lock->filename, strerror(errno));
 +              goto out;
 +      }
 +
        while (fgets(buf, sizeof(buf), config_file)) {
                int i;
                int length;