Merge branch 'jk/maint-config-alias-fix' into maint
authorJunio C Hamano <gitster@pobox.com>
Wed, 1 Jun 2011 21:05:22 +0000 (14:05 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 1 Jun 2011 21:05:22 +0000 (14:05 -0700)
* jk/maint-config-alias-fix:
handle_options(): do not miscount how many arguments were used
config: always parse GIT_CONFIG_PARAMETERS during git_config
git_config: don't peek at global config_parameters
config: make environment parsing routines static

1  2 
cache.h
config.c
git.c
t/t1300-repo-config.sh
diff --combined cache.h
index dd34fede9d2c7b91766c59c26b631e476e7bd937,45bb36d17f384e6d1ec2233fbd8a25c49851ea3f..ce73e1f09d7fc4f4196e32c2cfdd16e343ad9653
+++ b/cache.h
@@@ -5,7 -5,6 +5,7 @@@
  #include "strbuf.h"
  #include "hash.h"
  #include "advice.h"
 +#include "gettext.h"
  
  #include SHA1_HEADER
  #ifndef git_SHA_CTX
@@@ -171,26 -170,26 +171,26 @@@ struct cache_entry 
   *
   * In-memory only flags
   */
 -#define CE_UPDATE    (0x10000)
 -#define CE_REMOVE    (0x20000)
 -#define CE_UPTODATE  (0x40000)
 -#define CE_ADDED     (0x80000)
 +#define CE_UPDATE            (1 << 16)
 +#define CE_REMOVE            (1 << 17)
 +#define CE_UPTODATE          (1 << 18)
 +#define CE_ADDED             (1 << 19)
  
 -#define CE_HASHED    (0x100000)
 -#define CE_UNHASHED  (0x200000)
 -#define CE_CONFLICTED (0x800000)
 +#define CE_HASHED            (1 << 20)
 +#define CE_UNHASHED          (1 << 21)
 +#define CE_WT_REMOVE         (1 << 22) /* remove in work directory */
 +#define CE_CONFLICTED        (1 << 23)
  
 -#define CE_WT_REMOVE (0x400000) /* remove in work directory */
 -
 -#define CE_UNPACKED  (0x1000000)
 +#define CE_UNPACKED          (1 << 24)
 +#define CE_NEW_SKIP_WORKTREE (1 << 25)
  
  /*
   * Extended on-disk flags
   */
 -#define CE_INTENT_TO_ADD 0x20000000
 -#define CE_SKIP_WORKTREE 0x40000000
 +#define CE_INTENT_TO_ADD     (1 << 29)
 +#define CE_SKIP_WORKTREE     (1 << 30)
  /* CE_EXTENDED2 is for future extension */
 -#define CE_EXTENDED2 0x80000000
 +#define CE_EXTENDED2         (1 << 31)
  
  #define CE_EXTENDED_FLAGS (CE_INTENT_TO_ADD | CE_SKIP_WORKTREE)
  
@@@ -429,7 -428,7 +429,7 @@@ extern const char **get_pathspec(const 
  extern void setup_work_tree(void);
  extern const char *setup_git_directory_gently(int *);
  extern const char *setup_git_directory(void);
 -extern const char *prefix_path(const char *prefix, int len, const char *path);
 +extern char *prefix_path(const char *prefix, int len, const char *path);
  extern const char *prefix_filename(const char *prefix, int len, const char *path);
  extern int check_filename(const char *prefix, const char *name);
  extern void verify_filename(const char *prefix, const char *name);
@@@ -437,7 -436,6 +437,7 @@@ extern void verify_non_filename(const c
  
  #define INIT_DB_QUIET 0x0001
  
 +extern int set_git_dir_init(const char *git_dir, const char *real_git_dir, int);
  extern int init_db(const char *template_dir, unsigned int flags);
  
  #define alloc_nr(x) (((x)+16)*3/2)
@@@ -502,23 -500,8 +502,23 @@@ 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);
  
 -extern int ce_path_match(const struct cache_entry *ce, const char **pathspec);
 -extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path);
 +struct pathspec {
 +      const char **raw; /* get_pathspec() result, not freed by free_pathspec() */
 +      int nr;
 +      unsigned int has_wildcard:1;
 +      unsigned int recursive:1;
 +      int max_depth;
 +      struct pathspec_item {
 +              const char *match;
 +              int len;
 +              unsigned int has_wildcard:1;
 +      } *items;
 +};
 +
 +extern int init_pathspec(struct pathspec *, const char **);
 +extern void free_pathspec(struct pathspec *);
 +extern int ce_path_match(const struct cache_entry *ce, const struct pathspec *pathspec);
 +extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path, int format_check);
  extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object);
  extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
  
  #define REFRESH_IGNORE_MISSING        0x0008  /* ignore non-existent */
  #define REFRESH_IGNORE_SUBMODULES     0x0010  /* ignore submodules */
  #define REFRESH_IN_PORCELAIN  0x0020  /* user friendly output, not "needs update" */
 -extern int refresh_index(struct index_state *, unsigned int flags, const char **pathspec, char *seen, char *header_msg);
 +extern int refresh_index(struct index_state *, unsigned int flags, const char **pathspec, char *seen, const char *header_msg);
  
  struct lock_file {
        struct lock_file *next;
@@@ -544,7 -527,6 +544,7 @@@ extern NORETURN void unable_to_lock_ind
  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);
  extern int commit_lock_file(struct lock_file *);
 +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 *);
@@@ -558,7 -540,6 +558,7 @@@ extern int trust_executable_bit
  extern int trust_ctime;
  extern int quote_path_fully;
  extern int has_symlinks;
 +extern int minimum_abbrev, default_abbrev;
  extern int ignore_case;
  extern int assume_unchanged;
  extern int prefer_symlink_refs;
@@@ -573,7 -554,6 +573,7 @@@ extern int core_compression_seen
  extern size_t packed_git_window_size;
  extern size_t packed_git_limit;
  extern size_t delta_base_cache_limit;
 +extern unsigned long big_file_threshold;
  extern int read_replace_refs;
  extern int fsync_object_files;
  extern int core_preload_index;
@@@ -590,7 -570,7 +590,7 @@@ extern enum safe_crlf safe_crlf
  enum auto_crlf {
        AUTO_CRLF_FALSE = 0,
        AUTO_CRLF_TRUE = 1,
 -      AUTO_CRLF_INPUT = -1,
 +      AUTO_CRLF_INPUT = -1
  };
  
  extern enum auto_crlf auto_crlf;
@@@ -627,7 -607,7 +627,7 @@@ enum rebase_setup_type 
  enum push_default_type {
        PUSH_DEFAULT_NOTHING = 0,
        PUSH_DEFAULT_MATCHING,
 -      PUSH_DEFAULT_TRACKING,
 +      PUSH_DEFAULT_UPSTREAM,
        PUSH_DEFAULT_CURRENT
  };
  
@@@ -695,11 -675,9 +695,11 @@@ static inline void hashclr(unsigned cha
  
  #define EMPTY_TREE_SHA1_HEX \
        "4b825dc642cb6eb9a060e54bf8d69288fbee4904"
 -#define EMPTY_TREE_SHA1_BIN \
 +#define EMPTY_TREE_SHA1_BIN_LITERAL \
         "\x4b\x82\x5d\xc6\x42\xcb\x6e\xb9\xa0\x60" \
         "\xe5\x4b\xf8\xd6\x92\x88\xfb\xee\x49\x04"
 +#define EMPTY_TREE_SHA1_BIN \
 +       ((const unsigned char *) EMPTY_TREE_SHA1_BIN_LITERAL)
  
  int git_mkstemp(char *path, size_t n, const char *template);
  
@@@ -729,7 -707,6 +729,7 @@@ int set_shared_perm(const char *path, i
  #define adjust_shared_perm(path) set_shared_perm((path), 0)
  int safe_create_leading_directories(char *path);
  int safe_create_leading_directories_const(const char *path);
 +int mkdir_in_gitdir(const char *path);
  extern char *expand_user_path(const char *path);
  char *enter_repo(char *path, int strict);
  static inline int is_absolute_path(const char *path)
        return path[0] == '/' || has_dos_drive_prefix(path);
  }
  int is_directory(const char *);
 -const char *make_absolute_path(const char *path);
 -const char *make_nonrelative_path(const char *path);
 -const char *make_relative_path(const char *abs, const char *base);
 +const char *real_path(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);
  char *strip_path_suffix(const char *path, const char *suffix);
@@@ -780,8 -757,8 +780,8 @@@ static inline unsigned int hexval(unsig
  }
  
  /* Convert to/from hex/sha1 representation */
 -#define MINIMUM_ABBREV 4
 -#define DEFAULT_ABBREV 7
 +#define MINIMUM_ABBREV minimum_abbrev
 +#define DEFAULT_ABBREV default_abbrev
  
  struct object_context {
        unsigned char tree[20];
@@@ -882,7 -859,7 +882,7 @@@ struct cache_def 
  
  extern int has_symlink_leading_path(const char *name, int len);
  extern int threaded_has_symlink_leading_path(struct cache_def *, const char *, int);
 -extern int has_symlink_or_noent_leading_path(const char *name, int len);
 +extern int check_leading_path(const char *name, int len);
  extern int has_dirs_only_path(const char *name, int len, int prefix_len);
  extern void schedule_dir_for_removal(const char *name, int len);
  extern void remove_scheduled_dirs(void);
@@@ -919,8 -896,7 +919,8 @@@ extern struct packed_git 
        time_t mtime;
        int pack_fd;
        unsigned pack_local:1,
 -               pack_keep:1;
 +               pack_keep:1,
 +               do_not_close:1;
        unsigned char sha1[20];
        /* something like ".git/objects/pack/xxxxx.pack" */
        char pack_name[FLEX_ARRAY]; /* more */
@@@ -965,7 -941,6 +965,7 @@@ extern struct ref *find_ref_by_name(con
  extern char *git_getpass(const char *prompt);
  extern struct child_process *git_connect(int fd[2], const char *url, const char *prog, int flags);
  extern int finish_connect(struct child_process *conn);
 +extern int git_connection_is_socket(struct child_process *conn);
  extern int path_match(const char *path, int nr, char **match);
  struct extra_have_objects {
        int nr, alloc;
@@@ -1007,8 -982,6 +1007,6 @@@ typedef int (*config_fn_t)(const char *
  extern int git_default_config(const char *, const char *, void *);
  extern int git_config_from_file(config_fn_t fn, const char *, void *);
  extern void git_config_push_parameter(const char *text);
- extern int git_config_parse_parameter(const char *text);
- extern int git_config_parse_environment(void);
  extern int git_config_from_parameters(config_fn_t fn, void *data);
  extern int git_config(config_fn_t fn, void *);
  extern int git_config_early(config_fn_t fn, void *, const char *repo_config);
@@@ -1021,17 -994,14 +1019,17 @@@ extern int git_config_maybe_bool(const 
  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(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_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);
  extern int git_env_bool(const char *, int);
  extern int git_config_system(void);
 -extern int git_config_global(void);
  extern int config_error_nonbool(const char *);
 +extern const char *get_log_output_encoding(void);
 +extern const char *get_commit_output_encoding(void);
 +
  extern const char *config_exclusive_filename;
  
  #define MAX_GITNAME (1000)
@@@ -1089,14 -1059,9 +1087,14 @@@ extern void alloc_report(void)
  /* trace.c */
  __attribute__((format (printf, 1, 2)))
  extern void trace_printf(const char *format, ...);
 +extern void trace_vprintf(const char *key, const char *format, va_list ap);
  __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);
 +extern void trace_strbuf(const char *key, const struct strbuf *buf);
 +
 +void packet_trace_identity(const char *prog);
  
  /* convert.c */
  /* returns 1 if *dst was used */
@@@ -1122,17 -1087,15 +1120,17 @@@ void shift_tree_by(const unsigned char 
  /*
   * whitespace rules.
   * used by both diff and apply
 + * last two digits are tab width
   */
 -#define WS_BLANK_AT_EOL         01
 -#define WS_SPACE_BEFORE_TAB   02
 -#define WS_INDENT_WITH_NON_TAB        04
 -#define WS_CR_AT_EOL           010
 -#define WS_BLANK_AT_EOF        020
 -#define WS_TAB_IN_INDENT       040
 +#define WS_BLANK_AT_EOL         0100
 +#define WS_SPACE_BEFORE_TAB     0200
 +#define WS_INDENT_WITH_NON_TAB  0400
 +#define WS_CR_AT_EOL           01000
 +#define WS_BLANK_AT_EOF        02000
 +#define WS_TAB_IN_INDENT       04000
  #define WS_TRAILING_SPACE      (WS_BLANK_AT_EOL|WS_BLANK_AT_EOF)
 -#define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB)
 +#define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB|8)
 +#define WS_TAB_WIDTH_MASK        077
  extern unsigned whitespace_rule_cfg;
  extern unsigned whitespace_rule(const char *);
  extern unsigned parse_whitespace_rule(const char *);
@@@ -1141,7 -1104,6 +1139,7 @@@ extern void ws_check_emit(const char *l
  extern char *whitespace_error_string(unsigned ws);
  extern void ws_fix_copy(struct strbuf *, const char *, int, unsigned, int *);
  extern int ws_blank_line(const char *line, int len, unsigned ws_rule);
 +#define ws_tab_width(rule)     ((rule) & WS_TAB_WIDTH_MASK)
  
  /* ls-files */
  int report_path_error(const char *ps_matched, const char **pathspec, int prefix_offset);
@@@ -1155,7 -1117,6 +1153,7 @@@ const char *split_cmdline_strerror(int 
  /* git.c */
  struct startup_info {
        int have_repository;
 +      const char *prefix;
  };
  extern struct startup_info *startup_info;
  
diff --combined config.c
index d06fb19d511c29e92aa840c664618ca4a6f73fe6,8220c0cbf70bccf67659220908ba7046e13d50fe..61de4d6a178ec8c2886331daefead68e762362db
+++ b/config.c
@@@ -20,14 -20,6 +20,6 @@@ static int zlib_compression_seen
  
  const char *config_exclusive_filename = NULL;
  
- struct config_item {
-       struct config_item *next;
-       char *name;
-       char *value;
- };
- static struct config_item *config_parameters;
- static struct config_item **config_parameters_tail = &config_parameters;
  static void lowercase(char *p)
  {
        for (; *p; p++)
@@@ -47,9 -39,9 +39,9 @@@ void git_config_push_parameter(const ch
        strbuf_release(&env);
  }
  
- int git_config_parse_parameter(const char *text)
+ static int git_config_parse_parameter(const char *text,
+                                     config_fn_t fn, void *data)
  {
-       struct config_item *ct;
        struct strbuf tmp = STRBUF_INIT;
        struct strbuf **pair;
        strbuf_addstr(&tmp, text);
        strbuf_trim(pair[0]);
        if (!pair[0]->len) {
                strbuf_list_free(pair);
-               return -1;
+               return error("bogus config parameter: %s", text);
        }
-       ct = xcalloc(1, sizeof(struct config_item));
-       ct->name = strbuf_detach(pair[0], NULL);
-       if (pair[1]) {
-               strbuf_trim(pair[1]);
-               ct->value = strbuf_detach(pair[1], NULL);
+       lowercase(pair[0]->buf);
+       if (fn(pair[0]->buf, pair[1] ? pair[1]->buf : NULL, data) < 0) {
+               strbuf_list_free(pair);
+               return -1;
        }
        strbuf_list_free(pair);
-       lowercase(ct->name);
-       *config_parameters_tail = ct;
-       config_parameters_tail = &ct->next;
        return 0;
  }
  
- int git_config_parse_environment(void) {
+ int git_config_from_parameters(config_fn_t fn, void *data)
+ {
        const char *env = getenv(CONFIG_DATA_ENVIRONMENT);
        char *envw;
        const char **argv = NULL;
@@@ -92,8 -81,7 +81,7 @@@
        }
  
        for (i = 0; i < nr; i++) {
-               if (git_config_parse_parameter(argv[i]) < 0) {
-                       error("bogus config parameter: %s", argv[i]);
+               if (git_config_parse_parameter(argv[i], fn, data) < 0) {
                        free(argv);
                        free(envw);
                        return -1;
  
        free(argv);
        free(envw);
-       return 0;
+       return nr > 0;
  }
  
  static int get_next_char(void)
@@@ -409,7 -397,7 +397,7 @@@ unsigned long git_config_ulong(const ch
        return ret;
  }
  
 -int git_config_maybe_bool(const char *name, const char *value)
 +static int git_config_maybe_bool_text(const char *name, const char *value)
  {
        if (!value)
                return 1;
        return -1;
  }
  
 +int git_config_maybe_bool(const char *name, const char *value)
 +{
 +      long v = git_config_maybe_bool_text(name, value);
 +      if (0 <= v)
 +              return v;
 +      if (git_parse_long(value, &v))
 +              return !!v;
 +      return -1;
 +}
 +
  int git_config_bool_or_int(const char *name, const char *value, int *is_bool)
  {
 -      int v = git_config_maybe_bool(name, value);
 +      int v = git_config_maybe_bool_text(name, value);
        if (0 <= v) {
                *is_bool = 1;
                return v;
@@@ -523,14 -501,6 +511,14 @@@ static int git_default_core_config(cons
                return 0;
        }
  
 +      if (!strcmp(var, "core.abbrev")) {
 +              int abbrev = git_config_int(var, value);
 +              if (abbrev < minimum_abbrev || abbrev > 40)
 +                      return -1;
 +              default_abbrev = abbrev;
 +              return 0;
 +      }
 +
        if (!strcmp(var, "core.loosecompression")) {
                int level = git_config_int(var, value);
                if (level == -1)
                return 0;
        }
  
 +      if (!strcmp(var, "core.bigfilethreshold")) {
 +              long n = git_config_int(var, value);
 +              big_file_threshold = 0 < n ? n : 0;
 +              return 0;
 +      }
 +
        if (!strcmp(var, "core.packedgitlimit")) {
                packed_git_limit = git_config_int(var, value);
                return 0;
@@@ -743,10 -707,8 +731,10 @@@ static int git_default_push_config(cons
                        push_default = PUSH_DEFAULT_NOTHING;
                else if (!strcmp(value, "matching"))
                        push_default = PUSH_DEFAULT_MATCHING;
 -              else if (!strcmp(value, "tracking"))
 -                      push_default = PUSH_DEFAULT_TRACKING;
 +              else if (!strcmp(value, "upstream"))
 +                      push_default = PUSH_DEFAULT_UPSTREAM;
 +              else if (!strcmp(value, "tracking")) /* deprecated */
 +                      push_default = PUSH_DEFAULT_UPSTREAM;
                else if (!strcmp(value, "current"))
                        push_default = PUSH_DEFAULT_CURRENT;
                else {
@@@ -839,22 -801,11 +827,6 @@@ int git_config_system(void
        return !git_env_bool("GIT_CONFIG_NOSYSTEM", 0);
  }
  
- int git_config_from_parameters(config_fn_t fn, void *data)
 -int git_config_global(void)
--{
-       static int loaded_environment;
-       const struct config_item *ct;
-       if (!loaded_environment) {
-               if (git_config_parse_environment() < 0)
-                       return -1;
-               loaded_environment = 1;
-       }
-       for (ct = config_parameters; ct; ct = ct->next)
-               if (fn(ct->name, ct->value, data) < 0)
-                       return -1;
-       return 0;
 -      return !git_env_bool("GIT_CONFIG_NOGLOBAL", 0);
--}
--
  int git_config_early(config_fn_t fn, void *data, const char *repo_config)
  {
        int ret = 0, found = 0;
        }
  
        home = getenv("HOME");
 -      if (git_config_global() && home) {
 +      if (home) {
                char *user_config = xstrdup(mkpath("%s/.gitconfig", home));
                if (!access(user_config, R_OK)) {
                        ret += git_config_from_file(fn, user_config, data);
                found += 1;
        }
  
-       ret += git_config_from_parameters(fn, data);
-       if (config_parameters)
-               found += 1;
+       switch (git_config_from_parameters(fn, data)) {
+       case -1: /* error */
+               ret--;
+               break;
+       case 0: /* found nothing */
+               break;
+       default: /* found at least one item */
+               found++;
+               break;
+       }
  
 -      if (found == 0)
 -              return -1;
 -      return ret;
 +      return ret == 0 ? found : ret;
  }
  
  int git_config(config_fn_t fn, void *data)
@@@ -1101,75 -1061,6 +1080,75 @@@ int git_config_set(const char *key, con
        return git_config_set_multivar(key, value, NULL, 0);
  }
  
 +/*
 + * Auxiliary function to sanity-check and split the key into the section
 + * identifier and variable name.
 + *
 + * Returns 0 on success, -1 when there is an invalid character in the key and
 + * -2 if there is no section name in the key.
 + *
 + * store_key - pointer to char* which will hold a copy of the key with
 + *             lowercase section and variable name
 + * baselen - pointer to int which will hold the length of the
 + *           section + subsection part, can be NULL
 + */
 +int git_config_parse_key(const char *key, char **store_key, int *baselen_)
 +{
 +      int i, dot, baselen;
 +      const char *last_dot = strrchr(key, '.');
 +
 +      /*
 +       * Since "key" actually contains the section name and the real
 +       * key name separated by a dot, we have to know where the dot is.
 +       */
 +
 +      if (last_dot == NULL || last_dot == key) {
 +              error("key does not contain a section: %s", key);
 +              return -2;
 +      }
 +
 +      if (!last_dot[1]) {
 +              error("key does not contain variable name: %s", key);
 +              return -2;
 +      }
 +
 +      baselen = last_dot - key;
 +      if (baselen_)
 +              *baselen_ = baselen;
 +
 +      /*
 +       * Validate the key and while at it, lower case it for matching.
 +       */
 +      *store_key = xmalloc(strlen(key) + 1);
 +
 +      dot = 0;
 +      for (i = 0; key[i]; i++) {
 +              unsigned char c = key[i];
 +              if (c == '.')
 +                      dot = 1;
 +              /* Leave the extended basename untouched.. */
 +              if (!dot || i > baselen) {
 +                      if (!iskeychar(c) ||
 +                          (i == baselen + 1 && !isalpha(c))) {
 +                              error("invalid key: %s", key);
 +                              goto out_free_ret_1;
 +                      }
 +                      c = tolower(c);
 +              } else if (c == '\n') {
 +                      error("invalid key (newline): %s", key);
 +                      goto out_free_ret_1;
 +              }
 +              (*store_key)[i] = c;
 +      }
 +      (*store_key)[i] = 0;
 +
 +      return 0;
 +
 +out_free_ret_1:
 +      free(*store_key);
 +      return -1;
 +}
 +
  /*
   * If value==NULL, unset in (remove from) config,
   * if value_regex!=NULL, disregard key/value pairs where value does not match.
  int git_config_set_multivar(const char *key, const char *value,
        const char *value_regex, int multi_replace)
  {
 -      int i, dot;
        int fd = -1, in_fd;
        int ret;
        char *config_filename;
        struct lock_file *lock = NULL;
 -      const char *last_dot = strrchr(key, '.');
  
        if (config_exclusive_filename)
                config_filename = xstrdup(config_exclusive_filename);
        else
                config_filename = git_pathdup("config");
  
 -      /*
 -       * Since "key" actually contains the section name and the real
 -       * key name separated by a dot, we have to know where the dot is.
 -       */
 -
 -      if (last_dot == NULL) {
 -              error("key does not contain a section: %s", key);
 -              ret = 2;
 +      /* parse-key returns negative; flip the sign to feed exit(3) */
 +      ret = 0 - git_config_parse_key(key, &store.key, &store.baselen);
 +      if (ret)
                goto out_free;
 -      }
 -      store.baselen = last_dot - key;
  
        store.multi_replace = multi_replace;
  
 -      /*
 -       * Validate the key and while at it, lower case it for matching.
 -       */
 -      store.key = xmalloc(strlen(key) + 1);
 -      dot = 0;
 -      for (i = 0; key[i]; i++) {
 -              unsigned char c = key[i];
 -              if (c == '.')
 -                      dot = 1;
 -              /* Leave the extended basename untouched.. */
 -              if (!dot || i > store.baselen) {
 -                      if (!iskeychar(c) || (i == store.baselen+1 && !isalpha(c))) {
 -                              error("invalid key: %s", key);
 -                              free(store.key);
 -                              ret = 1;
 -                              goto out_free;
 -                      }
 -                      c = tolower(c);
 -              } else if (c == '\n') {
 -                      error("invalid key (newline): %s", key);
 -                      free(store.key);
 -                      ret = 1;
 -                      goto out_free;
 -              }
 -              store.key[i] = c;
 -      }
 -      store.key[i] = 0;
  
        /*
         * The lock serves a purpose in addition to locking: the new
diff --combined git.c
index ef598c3e7053b8dd2859f4d582ce2917a804fe42,6bea8eeaacb4858bcf6c93de4c609c5d9574b946..df4306d165927085c06083b999d6600fbfd6e18a
--- 1/git.c
--- 2/git.c
+++ b/git.c
@@@ -19,22 -19,14 +19,22 @@@ static struct startup_info git_startup_
  static int use_pager = -1;
  struct pager_config {
        const char *cmd;
 -      int val;
 +      int want;
 +      char *value;
  };
  
  static int pager_command_config(const char *var, const char *value, void *data)
  {
        struct pager_config *c = data;
 -      if (!prefixcmp(var, "pager.") && !strcmp(var + 6, c->cmd))
 -              c->val = git_config_bool(var, value);
 +      if (!prefixcmp(var, "pager.") && !strcmp(var + 6, c->cmd)) {
 +              int b = git_config_maybe_bool(var, value);
 +              if (b >= 0)
 +                      c->want = b;
 +              else {
 +                      c->want = 1;
 +                      c->value = xstrdup(value);
 +              }
 +      }
        return 0;
  }
  
@@@ -43,12 -35,9 +43,12 @@@ int check_pager_config(const char *cmd
  {
        struct pager_config c;
        c.cmd = cmd;
 -      c.val = -1;
 +      c.want = -1;
 +      c.value = NULL;
        git_config(pager_command_config, &c);
 -      return c.val;
 +      if (c.value)
 +              pager_program = c.value;
 +      return c.want;
  }
  
  static void commit_pager_choice(void) {
@@@ -66,7 -55,7 +66,7 @@@
  
  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];
                                *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)
        alias_string = alias_lookup(alias_command);
        if (alias_string) {
                if (alias_string[0] == '!') {
 +                      const char **alias_argv;
 +                      int argc = *argcp, i;
 +
                        commit_pager_choice();
 -                      if (*argcp > 1) {
 -                              struct strbuf buf;
 -
 -                              strbuf_init(&buf, PATH_MAX);
 -                              strbuf_addstr(&buf, alias_string);
 -                              sq_quote_argv(&buf, (*argv) + 1, PATH_MAX);
 -                              free(alias_string);
 -                              alias_string = buf.buf;
 -                      }
 -                      trace_printf("trace: alias to shell cmd: %s => %s\n",
 -                                   alias_command, alias_string + 1);
 -                      ret = system(alias_string + 1);
 -                      if (ret >= 0 && WIFEXITED(ret) &&
 -                          WEXITSTATUS(ret) != 127)
 -                              exit(WEXITSTATUS(ret));
 -                      die("Failed to run '%s' when expanding alias '%s'",
 -                          alias_string + 1, alias_command);
 +
 +                      /* build alias_argv */
 +                      alias_argv = xmalloc(sizeof(*alias_argv) * (argc + 1));
 +                      alias_argv[0] = alias_string + 1;
 +                      for (i = 1; i < argc; ++i)
 +                              alias_argv[i] = (*argv)[i];
 +                      alias_argv[argc] = NULL;
 +
 +                      ret = run_command_v_opt(alias_argv, RUN_USING_SHELL);
 +                      if (ret >= 0)   /* normal exit */
 +                              exit(ret);
 +
 +                      die_errno("While expanding alias '%s': '%s'",
 +                          alias_command, alias_string + 1);
                }
                count = split_cmdline(alias_string, &new_argv);
                if (count < 0)
@@@ -313,6 -300,7 +311,6 @@@ static void handle_internal_command(in
        const char *cmd = argv[0];
        static struct cmd_struct commands[] = {
                { "add", cmd_add, RUN_SETUP | NEED_WORK_TREE },
 -              { "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE },
                { "annotate", cmd_annotate, RUN_SETUP },
                { "apply", cmd_apply, RUN_SETUP_GENTLY },
                { "archive", cmd_archive },
                { "branch", cmd_branch, RUN_SETUP },
                { "bundle", cmd_bundle, RUN_SETUP_GENTLY },
                { "cat-file", cmd_cat_file, RUN_SETUP },
 +              { "check-attr", cmd_check_attr, RUN_SETUP },
 +              { "check-ref-format", cmd_check_ref_format },
                { "checkout", cmd_checkout, RUN_SETUP | NEED_WORK_TREE },
                { "checkout-index", cmd_checkout_index,
                        RUN_SETUP | NEED_WORK_TREE},
 -              { "check-ref-format", cmd_check_ref_format },
 -              { "check-attr", cmd_check_attr, RUN_SETUP },
                { "cherry", cmd_cherry, RUN_SETUP },
                { "cherry-pick", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE },
 -              { "clone", cmd_clone },
                { "clean", cmd_clean, RUN_SETUP | NEED_WORK_TREE },
 +              { "clone", cmd_clone },
                { "commit", cmd_commit, RUN_SETUP | NEED_WORK_TREE },
                { "commit-tree", cmd_commit_tree, RUN_SETUP },
                { "config", cmd_config, RUN_SETUP_GENTLY },
                { "init-db", cmd_init_db },
                { "log", cmd_log, RUN_SETUP },
                { "ls-files", cmd_ls_files, RUN_SETUP },
 -              { "ls-tree", cmd_ls_tree, RUN_SETUP },
                { "ls-remote", cmd_ls_remote, RUN_SETUP_GENTLY },
 +              { "ls-tree", cmd_ls_tree, RUN_SETUP },
                { "mailinfo", cmd_mailinfo },
                { "mailsplit", cmd_mailsplit },
                { "merge", cmd_merge, RUN_SETUP | NEED_WORK_TREE },
                { "notes", cmd_notes, RUN_SETUP },
                { "pack-objects", cmd_pack_objects, RUN_SETUP },
                { "pack-redundant", cmd_pack_redundant, RUN_SETUP },
 +              { "pack-refs", cmd_pack_refs, RUN_SETUP },
                { "patch-id", cmd_patch_id },
                { "peek-remote", cmd_ls_remote, RUN_SETUP_GENTLY },
                { "pickaxe", cmd_blame, RUN_SETUP },
                { "receive-pack", cmd_receive_pack },
                { "reflog", cmd_reflog, RUN_SETUP },
                { "remote", cmd_remote, RUN_SETUP },
 +              { "remote-ext", cmd_remote_ext },
 +              { "remote-fd", cmd_remote_fd },
                { "replace", cmd_replace, RUN_SETUP },
 -              { "repo-config", cmd_config, RUN_SETUP_GENTLY },
 +              { "repo-config", cmd_repo_config, RUN_SETUP_GENTLY },
                { "rerere", cmd_rerere, RUN_SETUP },
                { "reset", cmd_reset, RUN_SETUP },
                { "rev-list", cmd_rev_list, RUN_SETUP },
                { "rm", cmd_rm, RUN_SETUP },
                { "send-pack", cmd_send_pack, RUN_SETUP },
                { "shortlog", cmd_shortlog, RUN_SETUP_GENTLY | USE_PAGER },
 -              { "show-branch", cmd_show_branch, RUN_SETUP },
                { "show", cmd_show, RUN_SETUP },
 +              { "show-branch", cmd_show_branch, RUN_SETUP },
 +              { "show-ref", cmd_show_ref, RUN_SETUP },
 +              { "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE },
                { "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
                { "stripspace", cmd_stripspace },
                { "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
                { "update-server-info", cmd_update_server_info, RUN_SETUP },
                { "upload-archive", cmd_upload_archive },
                { "var", cmd_var, RUN_SETUP_GENTLY },
 +              { "verify-pack", cmd_verify_pack },
                { "verify-tag", cmd_verify_tag, RUN_SETUP },
                { "version", cmd_version },
                { "whatchanged", cmd_whatchanged, RUN_SETUP },
                { "write-tree", cmd_write_tree, RUN_SETUP },
 -              { "verify-pack", cmd_verify_pack },
 -              { "show-ref", cmd_show_ref, RUN_SETUP },
 -              { "pack-refs", cmd_pack_refs, RUN_SETUP },
        };
        int i;
        static const char ext[] = STRIP_EXTENSION;
diff --combined t/t1300-repo-config.sh
index 53fb8228cf18e2b58f3ea63e98a0220fa0fd39f2,de2a014d8fade18d8e8f666c502e584a57473d28..3db56267ee89e1b585a548f7bee13386e47395f4
@@@ -288,14 -288,6 +288,14 @@@ EO
  test_expect_success 'working --list' \
        'git config --list > output && cmp output expect'
  
 +cat > expect << EOF
 +EOF
 +
 +test_expect_success '--list without repo produces empty output' '
 +      git --git-dir=nonexistent config --list >output &&
 +      test_cmp expect output
 +'
 +
  cat > expect << EOF
  beta.noindent sillyValue
  nextsection.nonewline wow2 for me
@@@ -844,27 -836,6 +844,27 @@@ test_expect_success SYMLINKS 'symlinke
  
  '
  
 +test_expect_success 'nonexistent configuration' '
 +      (
 +              GIT_CONFIG=doesnotexist &&
 +              export GIT_CONFIG &&
 +              test_must_fail git config --list &&
 +              test_must_fail git config test.xyzzy
 +      )
 +'
 +
 +test_expect_success SYMLINKS 'symlink to nonexistent configuration' '
 +      ln -s doesnotexist linktonada &&
 +      ln -s linktonada linktolinktonada &&
 +      (
 +              GIT_CONFIG=linktonada &&
 +              export GIT_CONFIG &&
 +              test_must_fail git config --list &&
 +              GIT_CONFIG=linktolinktonada &&
 +              test_must_fail git config --list
 +      )
 +'
 +
  test_expect_success 'check split_cmdline return' "
        git config alias.split-cmdline-fix 'echo \"' &&
        test_must_fail git split-cmdline-fix &&
        "
  
  test_expect_success 'git -c "key=value" support' '
 -      test "z$(git -c name=value config name)" = zvalue &&
        test "z$(git -c core.name=value config core.name)" = zvalue &&
 -      test "z$(git -c CamelCase=value config camelcase)" = zvalue &&
 -      test "z$(git -c flag config --bool flag)" = ztrue &&
 -      test_must_fail git -c core.name=value config name
 +      test "z$(git -c foo.CamelCase=value config foo.camelcase)" = zvalue &&
 +      test "z$(git -c foo.flag config --bool foo.flag)" = ztrue &&
 +      test_must_fail git -c name=value config core.name
 +'
 +
 +test_expect_success 'key sanity-checking' '
 +      test_must_fail git config foo=bar &&
 +      test_must_fail git config foo=.bar &&
 +      test_must_fail git config foo.ba=r &&
 +      test_must_fail git config foo.1bar &&
 +      test_must_fail git config foo."ba
 +                              z".bar &&
 +      test_must_fail git config . false &&
 +      test_must_fail git config .foo false &&
 +      test_must_fail git config foo. false &&
 +      test_must_fail git config .foo. false &&
 +      git config foo.bar true &&
 +      git config foo."ba =z".bar false
  '
  
+ test_expect_success 'git -c works with aliases of builtins' '
+       git config alias.checkconfig "-c foo.check=bar config foo.check" &&
+       echo bar >expect &&
+       git checkconfig >actual &&
+       test_cmp expect actual
+ '
  test_done