Merge branch 'jc/maint-config-exit-status'
authorJunio C Hamano <gitster@pobox.com>
Mon, 3 Sep 2012 22:53:06 +0000 (15:53 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 3 Sep 2012 22:53:07 +0000 (15:53 -0700)
The exit status code from "git config" was way overspecified while
being incorrect. Update the implementation to give the documented
status for a case that was documented, and introduce a new code for
"all other errors".

* jc/maint-config-exit-status:
config: "git config baa" should exit with status 1

1  2 
Documentation/git-config.txt
builtin/config.c
cache.h
index 490868eb23876b87afa3431eed0e44252f3b889c,529395fd1496312335c4bf4a6e1b484a60afd2f7..eaea0791657386469e448def614b949c0a1f45e9
@@@ -54,16 -54,16 +54,16 @@@ configuration file by default, and opti
  '--file <filename>' can be used to tell the command to write to
  that location (you can say '--local' but that is the default).
  
- This command will fail (with exit code ret) if:
+ This command will fail with non-zero status upon error.  Some exit
+ codes are:
  
  . The config file is invalid (ret=3),
  . can not write to the config file (ret=4),
  . no section or name was provided (ret=2),
  . the section or key is invalid (ret=1),
  . you try to unset an option which does not exist (ret=5),
- . you try to unset/set an option for which multiple lines match (ret=5),
- . you try to use an invalid regexp (ret=6), or
- . you use '--global' option without $HOME being properly set (ret=128).
+ . you try to unset/set an option for which multiple lines match (ret=5), or
+ . you try to use an invalid regexp (ret=6).
  
  On success, the command returns the exit code 0.
  
@@@ -97,11 -97,10 +97,11 @@@ OPTION
  
  --global::
        For writing options: write to global ~/.gitconfig file rather than
 -      the repository .git/config.
 +      the repository .git/config, write to $XDG_CONFIG_HOME/git/config file
 +      if this file exists and the ~/.gitconfig file doesn't.
  +
 -For reading options: read only from global ~/.gitconfig rather than
 -from all available files.
 +For reading options: read only from global ~/.gitconfig and from
 +$XDG_CONFIG_HOME/git/config rather than from all available files.
  +
  See also <<FILES>>.
  
@@@ -195,7 -194,7 +195,7 @@@ See also <<FILES>>
  FILES
  -----
  
 -If not set explicitly with '--file', there are three files where
 +If not set explicitly with '--file', there are four files where
  'git config' will search for configuration options:
  
  $GIT_DIR/config::
        User-specific configuration file. Also called "global"
        configuration file.
  
 +$XDG_CONFIG_HOME/git/config::
 +      Second user-specific configuration file. If $XDG_CONFIG_HOME is not set
 +      or empty, $HOME/.config/git/config will be used. Any single-valued
 +      variable set in this file will be overwritten by whatever is in
 +      ~/.gitconfig.  It is a good idea not to create this file if
 +      you sometimes use older versions of Git, as support for this
 +      file was added fairly recently.
 +
  $(prefix)/etc/gitconfig::
        System-wide configuration file.
  
@@@ -267,7 -258,7 +267,7 @@@ Given a .git/config like this
  
        ; Proxy settings
        [core]
 -              gitproxy="proxy-command" for kernel.org
 +              gitproxy=proxy-command for kernel.org
                gitproxy=default-proxy ; for all the rest
  
  you can set the filemode to true with
@@@ -342,7 -333,7 +342,7 @@@ To actually match only values with an e
  To add a new proxy, without altering any of the existing ones, use
  
  ------------
 -% git config core.gitproxy '"proxy-command" for example.com'
 +% git config --add core.gitproxy '"proxy-command" for example.com'
  ------------
  
  An example to use customized color from the configuration in your
diff --combined builtin/config.c
index 8cd08da99122bc79025d2a78204d316f1b7ba478,b44277c23e293f42186230cc7feb9099f2058b2b..ada6e1211462558f307dce6142b7b65d641c8e02
@@@ -160,8 -160,8 +160,8 @@@ static int show_config(const char *key_
  
  static int get_value(const char *key_, const char *regex_)
  {
-       int ret = -1;
+       int ret = CONFIG_GENERIC_ERROR;
 -      char *global = NULL, *repo_config = NULL;
 +      char *global = NULL, *xdg = NULL, *repo_config = NULL;
        const char *system_wide = NULL, *local;
        struct config_include_data inc = CONFIG_INCLUDE_INIT;
        config_fn_t fn;
  
        local = given_config_file;
        if (!local) {
 -              const char *home = getenv("HOME");
                local = repo_config = git_pathdup("config");
 -              if (home)
 -                      global = xstrdup(mkpath("%s/.gitconfig", home));
                if (git_config_system())
                        system_wide = git_etc_gitconfig();
 +              home_config_paths(&global, &xdg, "config");
        }
  
        if (use_key_regexp) {
                if (regcomp(key_regexp, key, REG_EXTENDED)) {
                        fprintf(stderr, "Invalid key pattern: %s\n", key_);
                        free(key);
+                       ret = CONFIG_INVALID_PATTERN;
                        goto free_strings;
                }
        } else {
-               if (git_config_parse_key(key_, &key, NULL))
+               if (git_config_parse_key(key_, &key, NULL)) {
+                       ret = CONFIG_INVALID_KEY;
                        goto free_strings;
+               }
        }
  
        if (regex_) {
                regexp = (regex_t*)xmalloc(sizeof(regex_t));
                if (regcomp(regexp, regex_, REG_EXTENDED)) {
                        fprintf(stderr, "Invalid pattern: %s\n", regex_);
+                       ret = CONFIG_INVALID_PATTERN;
                        goto free_strings;
                }
        }
  
        if (do_all && system_wide)
                git_config_from_file(fn, system_wide, data);
 +      if (do_all && xdg)
 +              git_config_from_file(fn, xdg, data);
        if (do_all && global)
                git_config_from_file(fn, global, data);
        if (do_all)
                git_config_from_file(fn, local, data);
        if (!do_all && !seen && global)
                git_config_from_file(fn, global, data);
 +      if (!do_all && !seen && xdg)
 +              git_config_from_file(fn, xdg, data);
        if (!do_all && !seen && system_wide)
                git_config_from_file(fn, system_wide, data);
  
  free_strings:
        free(repo_config);
        free(global);
 +      free(xdg);
        return ret;
  }
  
@@@ -382,25 -383,13 +386,25 @@@ int cmd_config(int argc, const char **a
        }
  
        if (use_global_config) {
 -              char *home = getenv("HOME");
 -              if (home) {
 -                      char *user_config = xstrdup(mkpath("%s/.gitconfig", home));
 -                      given_config_file = user_config;
 -              } else {
 +              char *user_config = NULL;
 +              char *xdg_config = NULL;
 +
 +              home_config_paths(&user_config, &xdg_config, "config");
 +
 +              if (!user_config)
 +                      /*
 +                       * It is unknown if HOME/.gitconfig exists, so
 +                       * we do not know if we should write to XDG
 +                       * location; error out even if XDG_CONFIG_HOME
 +                       * is set and points at a sane location.
 +                       */
                        die("$HOME not set");
 -              }
 +
 +              if (access(user_config, R_OK) &&
 +                  xdg_config && !access(xdg_config, R_OK))
 +                      given_config_file = xdg_config;
 +              else
 +                      given_config_file = user_config;
        }
        else if (use_system_config)
                given_config_file = git_etc_gitconfig();
diff --combined cache.h
index 95daa690aa897c47cde113c27c954ddb7ef0c103,96ba07320b0d7f80f9e397ed8ce83ce4e449b74e..eb4a0316d2d654160a51789dce5ad69fc9d1af45
+++ b/cache.h
@@@ -105,9 -105,6 +105,9 @@@ struct cache_header 
        unsigned int hdr_entries;
  };
  
 +#define INDEX_FORMAT_LB 2
 +#define INDEX_FORMAT_UB 4
 +
  /*
   * The "cache_time" is just the low 32 bits of the
   * time. It doesn't matter if it overflows - we only
@@@ -118,6 -115,48 +118,6 @@@ struct cache_time 
        unsigned int nsec;
  };
  
 -/*
 - * dev/ino/uid/gid/size are also just tracked to the low 32 bits
 - * Again - this is just a (very strong in practice) heuristic that
 - * the inode hasn't changed.
 - *
 - * We save the fields in big-endian order to allow using the
 - * index file over NFS transparently.
 - */
 -struct ondisk_cache_entry {
 -      struct cache_time ctime;
 -      struct cache_time mtime;
 -      unsigned int dev;
 -      unsigned int ino;
 -      unsigned int mode;
 -      unsigned int uid;
 -      unsigned int gid;
 -      unsigned int size;
 -      unsigned char sha1[20];
 -      unsigned short flags;
 -      char name[FLEX_ARRAY]; /* more */
 -};
 -
 -/*
 - * This struct is used when CE_EXTENDED bit is 1
 - * The struct must match ondisk_cache_entry exactly from
 - * ctime till flags
 - */
 -struct ondisk_cache_entry_extended {
 -      struct cache_time ctime;
 -      struct cache_time mtime;
 -      unsigned int dev;
 -      unsigned int ino;
 -      unsigned int mode;
 -      unsigned int uid;
 -      unsigned int gid;
 -      unsigned int size;
 -      unsigned char sha1[20];
 -      unsigned short flags;
 -      unsigned short flags2;
 -      char name[FLEX_ARRAY]; /* more */
 -};
 -
  struct cache_entry {
        struct cache_time ce_ctime;
        struct cache_time ce_mtime;
        unsigned int ce_gid;
        unsigned int ce_size;
        unsigned int ce_flags;
 +      unsigned int ce_namelen;
        unsigned char sha1[20];
        struct cache_entry *next;
        struct cache_entry *dir_next;
        char name[FLEX_ARRAY]; /* more */
  };
  
 -#define CE_NAMEMASK  (0x0fff)
  #define CE_STAGEMASK (0x3000)
  #define CE_EXTENDED  (0x4000)
  #define CE_VALID     (0x8000)
@@@ -198,13 -237,25 +198,13 @@@ static inline void copy_cache_entry(str
        dst->ce_flags = (dst->ce_flags & ~CE_STATE_MASK) | state;
  }
  
 -static inline unsigned create_ce_flags(size_t len, unsigned stage)
 -{
 -      if (len >= CE_NAMEMASK)
 -              len = CE_NAMEMASK;
 -      return (len | (stage << CE_STAGESHIFT));
 -}
 -
 -static inline size_t ce_namelen(const struct cache_entry *ce)
 +static inline unsigned create_ce_flags(unsigned stage)
  {
 -      size_t len = ce->ce_flags & CE_NAMEMASK;
 -      if (len < CE_NAMEMASK)
 -              return len;
 -      return strlen(ce->name + CE_NAMEMASK) + CE_NAMEMASK;
 +      return (stage << CE_STAGESHIFT);
  }
  
 +#define ce_namelen(ce) ((ce)->ce_namelen)
  #define ce_size(ce) cache_entry_size(ce_namelen(ce))
 -#define ondisk_ce_size(ce) (((ce)->ce_flags & CE_EXTENDED) ? \
 -                          ondisk_cache_entry_extended_size(ce_namelen(ce)) : \
 -                          ondisk_cache_entry_size(ce_namelen(ce)))
  #define ce_stage(ce) ((CE_STAGEMASK & (ce)->ce_flags) >> CE_STAGESHIFT)
  #define ce_uptodate(ce) ((ce)->ce_flags & CE_UPTODATE)
  #define ce_skip_worktree(ce) ((ce)->ce_flags & CE_SKIP_WORKTREE)
@@@ -255,11 -306,13 +255,11 @@@ static inline unsigned int canon_mode(u
        return S_IFGITLINK;
  }
  
 -#define flexible_size(STRUCT,len) ((offsetof(struct STRUCT,name) + (len) + 8) & ~7)
  #define cache_entry_size(len) (offsetof(struct cache_entry,name) + (len) + 1)
 -#define ondisk_cache_entry_size(len) flexible_size(ondisk_cache_entry,len)
 -#define ondisk_cache_entry_extended_size(len) flexible_size(ondisk_cache_entry_extended,len)
  
  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;
@@@ -400,11 -453,8 +400,11 @@@ extern const char *setup_git_directory(
  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);
 +extern void verify_filename(const char *prefix,
 +                          const char *name,
 +                          int diagnose_misspelt_rev);
  extern void verify_non_filename(const char *prefix, const char *name);
 +extern int path_inside_repo(const char *prefix, const char *path);
  
  #define INIT_DB_QUIET 0x0001
  
@@@ -442,7 -492,6 +442,7 @@@ extern int discard_index(struct index_s
  extern int unmerged_index(const struct index_state *);
  extern int verify_path(const char *path);
  extern struct cache_entry *index_name_exists(struct index_state *istate, const char *name, int namelen, int igncase);
 +extern int index_name_stage_pos(const struct index_state *, const char *name, int namelen, int stage);
  extern int index_name_pos(const struct index_state *, const char *name, int namelen);
  #define ADD_CACHE_OK_TO_ADD 1         /* Ok to add */
  #define ADD_CACHE_OK_TO_REPLACE 2     /* Ok to replace file/directory */
@@@ -555,7 -604,6 +555,7 @@@ extern int read_replace_refs
  extern int fsync_object_files;
  extern int core_preload_index;
  extern int core_apply_sparse_checkout;
 +extern int precomposed_unicode;
  
  enum branch_track {
        BRANCH_TRACK_UNSPECIFIED = -1,
@@@ -576,7 -624,6 +576,7 @@@ enum rebase_setup_type 
  enum push_default_type {
        PUSH_DEFAULT_NOTHING = 0,
        PUSH_DEFAULT_MATCHING,
 +      PUSH_DEFAULT_SIMPLE,
        PUSH_DEFAULT_UPSTREAM,
        PUSH_DEFAULT_CURRENT,
        PUSH_DEFAULT_UNSPECIFIED
@@@ -615,8 -662,6 +615,8 @@@ extern char *git_snpath(char *buf, size
        __attribute__((format (printf, 3, 4)));
  extern char *git_pathdup(const char *fmt, ...)
        __attribute__((format (printf, 1, 2)));
 +extern char *mkpathdup(const char *fmt, ...)
 +      __attribute__((format (printf, 1, 2)));
  
  /* Return a statically allocated filename matching the sha1 signature */
  extern char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
@@@ -706,7 -751,6 +706,7 @@@ int set_shared_perm(const char *path, i
  int safe_create_leading_directories(char *path);
  int safe_create_leading_directories_const(const char *path);
  int mkdir_in_gitdir(const char *path);
 +extern void home_config_paths(char **global, char **xdg, char *file);
  extern char *expand_user_path(const char *path);
  const char *enter_repo(const char *path, int strict);
  static inline int is_absolute_path(const char *path)
@@@ -782,25 -826,17 +782,25 @@@ struct object_context 
        unsigned mode;
  };
  
 +#define GET_SHA1_QUIETLY        01
 +#define GET_SHA1_COMMIT         02
 +#define GET_SHA1_COMMITTISH     04
 +#define GET_SHA1_TREE          010
 +#define GET_SHA1_TREEISH       020
 +#define GET_SHA1_BLOB        040
 +#define GET_SHA1_ONLY_TO_DIE 04000
 +
  extern int get_sha1(const char *str, unsigned char *sha1);
 -extern int get_sha1_with_mode_1(const char *str, unsigned char *sha1, unsigned *mode, int only_to_die, const char *prefix);
 -static inline int get_sha1_with_mode(const char *str, unsigned char *sha1, unsigned *mode)
 -{
 -      return get_sha1_with_mode_1(str, sha1, mode, 0, NULL);
 -}
 -extern int get_sha1_with_context_1(const char *name, unsigned char *sha1, struct object_context *orc, int only_to_die, const char *prefix);
 -static inline int get_sha1_with_context(const char *str, unsigned char *sha1, struct object_context *orc)
 -{
 -      return get_sha1_with_context_1(str, sha1, orc, 0, NULL);
 -}
 +extern int get_sha1_commit(const char *str, unsigned char *sha1);
 +extern int get_sha1_committish(const char *str, unsigned char *sha1);
 +extern int get_sha1_tree(const char *str, unsigned char *sha1);
 +extern int get_sha1_treeish(const char *str, unsigned char *sha1);
 +extern int get_sha1_blob(const char *str, unsigned char *sha1);
 +extern void maybe_die_on_misspelt_object_name(const char *name, const char *prefix);
 +extern int get_sha1_with_context(const char *str, unsigned flags, unsigned char *sha1, struct object_context *orc);
 +
 +typedef int each_abbrev_fn(const unsigned char *sha1, void *);
 +extern int for_each_abbrev(const char *prefix, each_abbrev_fn, void *);
  
  /*
   * Try to read a SHA1 in hexadecimal format from the 40 characters
@@@ -864,7 -900,6 +864,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 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,
                                        const char *required_type,
@@@ -885,8 -920,10 +885,8 @@@ enum date_mode 
  };
  
  const char *show_date(unsigned long time, int timezone, enum date_mode mode);
 -const char *show_date_relative(unsigned long time, int tz,
 -                             const struct timeval *now,
 -                             char *timebuf,
 -                             size_t timebuf_size);
 +void show_date_relative(unsigned long time, int tz, const struct timeval *now,
 +                      struct strbuf *timebuf);
  int parse_date(const char *date, char *buf, int bufsize);
  int parse_date_basic(const char *date, unsigned long *timestamp, int *offset);
  void datestamp(char *buf, int bufsize);
@@@ -895,19 -932,15 +895,19 @@@ unsigned long approxidate_careful(cons
  unsigned long approxidate_relative(const char *date, const struct timeval *now);
  enum date_mode parse_date_format(const char *format);
  
 -#define IDENT_WARN_ON_NO_NAME  1
 -#define IDENT_ERROR_ON_NO_NAME 2
 -#define IDENT_NO_DATE        4
 +#define IDENT_STRICT         1
 +#define IDENT_NO_DATE        2
 +#define IDENT_NO_NAME        4
  extern const char *git_author_info(int);
  extern const char *git_committer_info(int);
  extern const char *fmt_ident(const char *name, const char *email, const char *date_str, int);
  extern const char *fmt_name(const char *name, const char *email);
 +extern const char *ident_default_name(void);
 +extern const char *ident_default_email(void);
 +extern const char *ident_default_date(void);
  extern const char *git_editor(void);
  extern const char *git_pager(int stdout_is_tty);
 +extern int git_ident_config(const char *, const char *, void *);
  
  struct ident_split {
        const char *name_begin;
@@@ -1038,9 -1071,7 +1038,9 @@@ struct extra_have_objects 
  };
  extern struct ref **get_remote_heads(int in, struct ref **list, unsigned int flags, struct extra_have_objects *);
  extern int server_supports(const char *feature);
 -extern const char *parse_feature_request(const char *features, const char *feature);
 +extern int parse_feature_request(const char *features, const char *feature);
 +extern const char *server_feature_value(const char *feature, int *len_ret);
 +extern const char *parse_feature_value(const char *feature_list, const char *feature, int *len_ret);
  
  extern struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path);
  
@@@ -1110,6 -1141,7 +1110,7 @@@ extern int update_server_info(int)
  #define CONFIG_NO_WRITE 4
  #define CONFIG_NOTHING_SET 5
  #define CONFIG_INVALID_PATTERN 6
+ #define CONFIG_GENERIC_ERROR 7
  
  typedef int (*config_fn_t)(const char *, const char *, void *);
  extern int git_default_config(const char *, const char *, void *);
@@@ -1153,6 -1185,9 +1154,6 @@@ struct config_include_data 
  #define CONFIG_INCLUDE_INIT { 0 }
  extern int git_config_include(const char *name, const char *value, void *data);
  
 -#define MAX_GITNAME (1000)
 -extern char git_default_email[MAX_GITNAME];
 -extern char git_default_name[MAX_GITNAME];
  #define IDENT_NAME_GIVEN 01
  #define IDENT_MAIL_GIVEN 02
  #define IDENT_ALL_GIVEN (IDENT_NAME_GIVEN|IDENT_MAIL_GIVEN)