Merge branch 'jk/setup-sequence-update'
authorJunio C Hamano <gitster@pobox.com>
Wed, 21 Sep 2016 22:15:23 +0000 (15:15 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 21 Sep 2016 22:15:24 +0000 (15:15 -0700)
There were numerous corner cases in which the configuration files
are read and used or not read at all depending on the directory a
Git command was run, leading to inconsistent behaviour. The code
to set-up repository access at the beginning of a Git process has
been updated to fix them.

* jk/setup-sequence-update:
t1007: factor out repeated setup
init: reset cached config when entering new repo
init: expand comments explaining config trickery
config: only read .git/config from configured repos
test-config: setup git directory
t1302: use "git -C"
pager: handle early config
pager: use callbacks instead of configset
pager: make pager_program a file-local static
pager: stop loading git_default_config()
pager: remove obsolete comment
diff: always try to set up the repository
diff: handle --no-index prefixes consistently
diff: skip implicit no-index check when given --no-index
patch-id: use RUN_SETUP_GENTLY
hash-object: always try to set up the git repository

1  2 
builtin/diff.c
builtin/init-db.c
cache.h
config.c
diff-no-index.c
environment.c
git.c
pager.c
t/helper/test-config.c
t/t4204-patch-id.sh
diff --combined builtin/diff.c
index b7a9405d9fbec46edf99637557d7499fb58c4d77,7be7806146b7e10be035ef6d3654bbd1c1c424a7..7f91f6d2267db962fb7c25e92983afd4e811d43e
@@@ -301,24 -301,24 +301,25 @@@ int cmd_diff(int argc, const char **arg
                        break;
        }
  
-       if (!no_index)
-               prefix = setup_git_directory_gently(&nongit);
-       /*
-        * Treat git diff with at least one path outside of the
-        * repo the same as if the command would have been executed
-        * outside of a git repository.  In this case it behaves
-        * the same way as "git diff --no-index <a> <b>", which acts
-        * as a colourful "diff" replacement.
-        */
-       if (nongit || ((argc == i + 2) &&
-                      (!path_inside_repo(prefix, argv[i]) ||
-                       !path_inside_repo(prefix, argv[i + 1]))))
-               no_index = DIFF_NO_INDEX_IMPLICIT;
+       prefix = setup_git_directory_gently(&nongit);
+       if (!no_index) {
+               /*
+                * Treat git diff with at least one path outside of the
+                * repo the same as if the command would have been executed
+                * outside of a git repository.  In this case it behaves
+                * the same way as "git diff --no-index <a> <b>", which acts
+                * as a colourful "diff" replacement.
+                */
+               if (nongit || ((argc == i + 2) &&
+                              (!path_inside_repo(prefix, argv[i]) ||
+                               !path_inside_repo(prefix, argv[i + 1]))))
+                       no_index = DIFF_NO_INDEX_IMPLICIT;
+       }
  
        if (!no_index)
                gitmodules_config();
 +      init_diff_ui_defaults();
        git_config(git_diff_ui_config, NULL);
        precompose_argv(argc, argv);
  
diff --combined builtin/init-db.c
index 80192f65e4cbf7144bbc7454a9fccf30a1707f9c,cc09fca81bb61dd6539c4a9e596699b7275b9a08..72e81447ae84c8e69799fff5a6fe7050241bea9b
@@@ -180,21 -180,36 +180,30 @@@ static int create_default_files(const c
        char junk[2];
        int reinit;
        int filemode;
 -
 -      /*
 -       * Create .git/refs/{heads,tags}
 -       */
 -      safe_create_dir(git_path_buf(&buf, "refs"), 1);
 -      safe_create_dir(git_path_buf(&buf, "refs/heads"), 1);
 -      safe_create_dir(git_path_buf(&buf, "refs/tags"), 1);
 +      struct strbuf err = STRBUF_INIT;
  
        /* Just look for `init.templatedir` */
        git_config(git_init_db_config, NULL);
  
-       /* First copy the templates -- we might have the default
+       /*
+        * First copy the templates -- we might have the default
         * config file there, in which case we would want to read
         * from it after installing.
+        *
+        * Before reading that config, we also need to clear out any cached
+        * values (since we've just potentially changed what's available on
+        * disk).
         */
        copy_templates(template_path);
+       git_config_clear();
+       reset_shared_repository();
        git_config(git_default_config, NULL);
-       is_bare_repository_cfg = init_is_bare_repository;
  
-       /* reading existing config may have overwrote it */
+       /*
+        * We must make sure command-line options continue to override any
+        * values we might have just re-read from the config.
+        */
+       is_bare_repository_cfg = init_is_bare_repository;
        if (init_shared_repository != -1)
                set_shared_repository(init_shared_repository);
  
         */
        if (get_shared_repository()) {
                adjust_shared_perm(get_git_dir());
 -              adjust_shared_perm(git_path_buf(&buf, "refs"));
 -              adjust_shared_perm(git_path_buf(&buf, "refs/heads"));
 -              adjust_shared_perm(git_path_buf(&buf, "refs/tags"));
        }
  
 +      /*
 +       * We need to create a "refs" dir in any case so that older
 +       * versions of git can tell that this is a repository.
 +       */
 +      safe_create_dir(git_path("refs"), 1);
 +      adjust_shared_perm(git_path("refs"));
 +
 +      if (refs_init_db(&err))
 +              die("failed to set up refs db: %s", err.buf);
 +
        /*
         * Create the default symlink from ".git/HEAD" to the "master"
         * branch, if it does not exist yet.
@@@ -398,16 -406,13 +407,16 @@@ int init_db(const char *template_dir, u
        if (!(flags & INIT_DB_QUIET)) {
                int len = strlen(git_dir);
  
 -              /* TRANSLATORS: The first '%s' is either "Reinitialized
 -                 existing" or "Initialized empty", the second " shared" or
 -                 "", and the last '%s%s' is the verbatim directory name. */
 -              printf(_("%s%s Git repository in %s%s\n"),
 -                     reinit ? _("Reinitialized existing") : _("Initialized empty"),
 -                     get_shared_repository() ? _(" shared") : "",
 -                     git_dir, len && git_dir[len-1] != '/' ? "/" : "");
 +              if (reinit)
 +                      printf(get_shared_repository()
 +                             ? _("Reinitialized existing shared Git repository in %s%s\n")
 +                             : _("Reinitialized existing Git repository in %s%s\n"),
 +                             git_dir, len && git_dir[len-1] != '/' ? "/" : "");
 +              else
 +                      printf(get_shared_repository()
 +                             ? _("Initialized empty shared Git repository in %s%s\n")
 +                             : _("Initialized empty Git repository in %s%s\n"),
 +                             git_dir, len && git_dir[len-1] != '/' ? "/" : "");
        }
  
        return 0;
diff --combined cache.h
index ee04430c93388f9a541f611404620fdf0547ec55,b2d77f3eb4daafe41749aa260927e32208e17871..d0494c85338b9063b3abe6e2081c921617ca0203
+++ b/cache.h
@@@ -173,7 -173,7 +173,7 @@@ struct cache_entry 
        unsigned int ce_flags;
        unsigned int ce_namelen;
        unsigned int index;     /* for link extension */
 -      unsigned char sha1[20];
 +      struct object_id oid;
        char name[FLEX_ARRAY]; /* more */
  };
  
@@@ -367,8 -367,8 +367,8 @@@ extern void free_name_hash(struct index
  #define rename_cache_entry_at(pos, new_name) rename_index_entry_at(&the_index, (pos), (new_name))
  #define remove_cache_entry_at(pos) remove_index_entry_at(&the_index, (pos))
  #define remove_file_from_cache(path) remove_file_from_index(&the_index, (path))
 -#define add_to_cache(path, st, flags) add_to_index(&the_index, (path), (st), (flags))
 -#define add_file_to_cache(path, flags) add_file_to_index(&the_index, (path), (flags))
 +#define add_to_cache(path, st, flags) add_to_index(&the_index, (path), (st), (flags), 0)
 +#define add_file_to_cache(path, flags) add_file_to_index(&the_index, (path), (flags), 0)
  #define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL, NULL)
  #define ce_match_stat(ce, st, options) ie_match_stat(&the_index, (ce), (st), (options))
  #define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options))
@@@ -453,6 -453,12 +453,12 @@@ static inline enum object_type object_t
   */
  extern const char * const local_repo_env[];
  
+ /*
+  * Returns true iff we have a configured git repository (either via
+  * setup_git_directory, or in the environment via $GIT_DIR).
+  */
+ int have_git_dir(void);
  extern int is_bare_repository_cfg;
  extern int is_bare_repository(void);
  extern int is_inside_git_dir(void);
@@@ -581,8 -587,8 +587,8 @@@ extern int remove_file_from_index(struc
  #define ADD_CACHE_IGNORE_ERRORS       4
  #define ADD_CACHE_IGNORE_REMOVAL 8
  #define ADD_CACHE_INTENT 16
 -extern int add_to_index(struct index_state *, const char *path, struct stat *, int flags);
 -extern int add_file_to_index(struct index_state *, const char *path, int flags);
 +extern int add_to_index(struct index_state *, const char *path, struct stat *, int flags, int force_mode);
 +extern int add_file_to_index(struct index_state *, const char *path, int flags, int force_mode);
  extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, unsigned int refresh_options);
  extern int ce_same_name(const struct cache_entry *a, const struct cache_entry *b);
  extern void set_object_name_for_intent_to_add_entry(struct cache_entry *ce);
@@@ -632,7 -638,6 +638,7 @@@ extern void fill_stat_cache_info(struc
  #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 struct pathspec *pathspec, char *seen, const char *header_msg);
 +extern struct cache_entry *refresh_cache_entry(struct cache_entry *, unsigned int);
  
  extern void update_index_if_able(struct index_state *, struct lock_file *);
  
@@@ -655,7 -660,6 +661,7 @@@ extern int warn_on_object_refname_ambig
  extern const char *apply_default_whitespace;
  extern const char *apply_default_ignorewhitespace;
  extern const char *git_attributes_file;
 +extern const char *git_hooks_path;
  extern int zlib_compression_level;
  extern int core_compression_level;
  extern int core_compression_seen;
@@@ -665,8 -669,15 +671,15 @@@ extern size_t delta_base_cache_limit
  extern unsigned long big_file_threshold;
  extern unsigned long pack_size_limit_cfg;
  
+ /*
+  * Accessors for the core.sharedrepository config which lazy-load the value
+  * from the config (if not already set). The "reset" function can be
+  * used to unset "set" or cached value, meaning that the value will be loaded
+  * fresh from the config file on the next call to get_shared_repository().
+  */
  void set_shared_repository(int value);
  int get_shared_repository(void);
+ void reset_shared_repository(void);
  
  /*
   * Do replace refs need to be checked this run?  This variable is
@@@ -809,18 -820,15 +822,18 @@@ extern void check_repository_format(voi
   */
  extern const char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
  extern const char *git_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
 +extern const char *git_common_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
  
  extern char *mksnpath(char *buf, size_t n, const char *fmt, ...)
        __attribute__((format (printf, 3, 4)));
  extern void strbuf_git_path(struct strbuf *sb, const char *fmt, ...)
        __attribute__((format (printf, 2, 3)));
 +extern void strbuf_git_common_path(struct strbuf *sb, const char *fmt, ...)
 +      __attribute__((format (printf, 2, 3)));
  extern char *git_path_buf(struct strbuf *buf, const char *fmt, ...)
        __attribute__((format (printf, 2, 3)));
 -extern void strbuf_git_path_submodule(struct strbuf *sb, const char *path,
 -                                    const char *fmt, ...)
 +extern int strbuf_git_path_submodule(struct strbuf *sb, const char *path,
 +                                   const char *fmt, ...)
        __attribute__((format (printf, 3, 4)));
  extern char *git_pathdup(const char *fmt, ...)
        __attribute__((format (printf, 1, 2)));
@@@ -953,39 -961,22 +966,39 @@@ static inline void oidclr(struct object
  #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)
 +extern const struct object_id empty_tree_oid;
 +#define EMPTY_TREE_SHA1_BIN (empty_tree_oid.hash)
  
  #define EMPTY_BLOB_SHA1_HEX \
        "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"
  #define EMPTY_BLOB_SHA1_BIN_LITERAL \
        "\xe6\x9d\xe2\x9b\xb2\xd1\xd6\x43\x4b\x8b" \
        "\x29\xae\x77\x5a\xd8\xc2\xe4\x8c\x53\x91"
 -#define EMPTY_BLOB_SHA1_BIN \
 -      ((const unsigned char *) EMPTY_BLOB_SHA1_BIN_LITERAL)
 +extern const struct object_id empty_blob_oid;
 +#define EMPTY_BLOB_SHA1_BIN (empty_blob_oid.hash)
 +
  
  static inline int is_empty_blob_sha1(const unsigned char *sha1)
  {
        return !hashcmp(sha1, EMPTY_BLOB_SHA1_BIN);
  }
  
 +static inline int is_empty_blob_oid(const struct object_id *oid)
 +{
 +      return !hashcmp(oid->hash, EMPTY_BLOB_SHA1_BIN);
 +}
 +
 +static inline int is_empty_tree_sha1(const unsigned char *sha1)
 +{
 +      return !hashcmp(sha1, EMPTY_TREE_SHA1_BIN);
 +}
 +
 +static inline int is_empty_tree_oid(const struct object_id *oid)
 +{
 +      return !hashcmp(oid->hash, EMPTY_TREE_SHA1_BIN);
 +}
 +
 +
  int git_mkstemp(char *path, size_t n, const char *template);
  
  /* set default permissions by passing mode arguments to open(2) */
@@@ -1021,11 -1012,6 +1034,11 @@@ int adjust_shared_perm(const char *path
   * directory while we were working.  To be robust against this kind of
   * race, callers might want to try invoking the function again when it
   * returns SCLD_VANISHED.
 + *
 + * safe_create_leading_directories() temporarily changes path while it
 + * is working but restores it before returning.
 + * safe_create_leading_directories_const() doesn't modify path, even
 + * temporarily.
   */
  enum scld_error {
        SCLD_OK = 0,
@@@ -1156,16 -1142,6 +1169,16 @@@ static inline unsigned int hexval(unsig
        return hexval_table[c];
  }
  
 +/*
 + * Convert two consecutive hexadecimal digits into a char.  Return a
 + * negative value on error.  Don't run over the end of short strings.
 + */
 +static inline int hex2chr(const char *s)
 +{
 +      int val = hexval(s[0]);
 +      return (val < 0) ? val : (val << 4) | hexval(s[1]);
 +}
 +
  /* Convert to/from hex/sha1 representation */
  #define MINIMUM_ABBREV minimum_abbrev
  #define DEFAULT_ABBREV default_abbrev
@@@ -1199,8 -1175,6 +1212,8 @@@ extern int get_sha1_blob(const char *st
  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);
  
 +extern int get_oid(const char *str, struct object_id *oid);
 +
  typedef int each_abbrev_fn(const unsigned char *sha1, void *);
  extern int for_each_abbrev(const char *prefix, each_abbrev_fn, void *);
  
@@@ -1226,12 -1200,11 +1239,12 @@@ extern int get_oid_hex(const char *hex
   *   printf("%s -> %s", sha1_to_hex(one), sha1_to_hex(two));
   */
  extern char *sha1_to_hex_r(char *out, const unsigned char *sha1);
 +extern char *oid_to_hex_r(char *out, const struct object_id *oid);
  extern char *sha1_to_hex(const unsigned char *sha1);  /* static buffer result! */
  extern char *oid_to_hex(const struct object_id *oid); /* same static buffer as sha1_to_hex */
  
  extern int interpret_branch_name(const char *str, int len, struct strbuf *);
 -extern int get_sha1_mb(const char *str, unsigned char *sha1);
 +extern int get_oid_mb(const char *str, struct object_id *oid);
  
  extern int validate_headref(const char *ref);
  
@@@ -1257,8 -1230,7 +1270,8 @@@ struct date_mode 
                DATE_ISO8601_STRICT,
                DATE_RFC2822,
                DATE_STRFTIME,
 -              DATE_RAW
 +              DATE_RAW,
 +              DATE_UNIX
        } type;
        const char *strftime_fmt;
        int local;
@@@ -1297,7 -1269,6 +1310,7 @@@ extern const char *ident_default_email(
  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 *);
 +extern void reset_ident_date(void);
  
  struct ident_split {
        const char *name_begin;
@@@ -1371,7 -1342,6 +1384,7 @@@ extern struct alternate_object_databas
  } *alt_odb_list;
  extern void prepare_alt_odb(void);
  extern void read_info_alternates(const char * relative_base, int depth);
 +extern char *compute_alternate_path(const char *path, struct strbuf *err);
  extern void add_to_alternates_file(const char *reference);
  typedef int alt_odb_fn(struct alternate_object_database *, void *);
  extern int foreach_alt_odb(alt_odb_fn, void*);
@@@ -1407,13 -1377,6 +1420,13 @@@ extern struct packed_git 
        char pack_name[FLEX_ARRAY]; /* more */
  } *packed_git;
  
 +/*
 + * A most-recently-used ordered version of the packed_git list, which can
 + * be iterated instead of packed_git (and marked via mru_mark).
 + */
 +struct mru;
 +extern struct mru *packed_git_mru;
 +
  struct pack_entry {
        off_t offset;
        unsigned char sha1[20];
@@@ -1453,6 -1416,7 +1466,6 @@@ extern unsigned char *use_pack(struct p
  extern void close_pack_windows(struct packed_git *);
  extern void close_all_packs(void);
  extern void unuse_pack(struct pack_window **);
 -extern void free_pack_by_name(const char *);
  extern void clear_delta_base_cache(void);
  extern struct packed_git *add_packed_git(const char *path, size_t path_len, int local);
  
@@@ -1551,7 -1515,7 +1564,7 @@@ struct object_info 
        /* Request */
        enum object_type *typep;
        unsigned long *sizep;
 -      unsigned long *disk_sizep;
 +      off_t *disk_sizep;
        unsigned char *delta_base_sha1;
        struct strbuf *typename;
  
@@@ -1602,18 -1566,10 +1615,18 @@@ struct git_config_source 
        const char *blob;
  };
  
 +enum config_origin_type {
 +      CONFIG_ORIGIN_BLOB,
 +      CONFIG_ORIGIN_FILE,
 +      CONFIG_ORIGIN_STDIN,
 +      CONFIG_ORIGIN_SUBMODULE_BLOB,
 +      CONFIG_ORIGIN_CMDLINE
 +};
 +
  typedef int (*config_fn_t)(const char *, const char *, void *);
  extern int git_default_config(const char *, const char *, void *);
  extern int git_config_from_file(config_fn_t fn, const char *, void *);
 -extern int git_config_from_mem(config_fn_t fn, const char *origin_type,
 +extern int git_config_from_mem(config_fn_t fn, const enum config_origin_type,
                                        const char *name, const char *buf, size_t len, void *data);
  extern void git_config_push_parameter(const char *text);
  extern int git_config_from_parameters(config_fn_t fn, void *data);
@@@ -1655,16 -1611,6 +1668,16 @@@ extern const char *get_log_output_encod
  extern const char *get_commit_output_encoding(void);
  
  extern int git_config_parse_parameter(const char *, config_fn_t fn, void *data);
 +
 +enum config_scope {
 +      CONFIG_SCOPE_UNKNOWN = 0,
 +      CONFIG_SCOPE_SYSTEM,
 +      CONFIG_SCOPE_GLOBAL,
 +      CONFIG_SCOPE_REPO,
 +      CONFIG_SCOPE_CMDLINE,
 +};
 +
 +extern enum config_scope current_config_scope(void);
  extern const char *current_config_origin_type(void);
  extern const char *current_config_name(void);
  
@@@ -1757,8 -1703,6 +1770,8 @@@ extern int ignore_untracked_cache_confi
  struct key_value_info {
        const char *filename;
        int linenr;
 +      enum config_origin_type origin_type;
 +      enum config_scope scope;
  };
  
  extern NORETURN void git_die_config(const char *key, const char *err, ...) __attribute__((format(printf, 2, 3)));
@@@ -1784,6 -1728,8 +1797,6 @@@ extern int copy_file(const char *dst, c
  extern int copy_file_with_time(const char *dst, const char *src, int mode);
  
  extern void write_or_die(int fd, const void *buf, size_t count);
 -extern int write_or_whine(int fd, const void *buf, size_t count, const char *msg);
 -extern int write_or_whine_pipe(int fd, const void *buf, size_t count, const char *msg);
  extern void fsync_or_die(int fd, const char *);
  
  extern ssize_t read_in_full(int fd, void *buf, size_t count);
@@@ -1795,25 -1741,11 +1808,24 @@@ static inline ssize_t write_str_in_full
        return write_in_full(fd, str, strlen(str));
  }
  
 -extern int write_file(const char *path, const char *fmt, ...);
 -extern int write_file_gently(const char *path, const char *fmt, ...);
 +/**
 + * Open (and truncate) the file at path, write the contents of buf to it,
 + * and close it. Dies if any errors are encountered.
 + */
 +extern void write_file_buf(const char *path, const char *buf, size_t len);
 +
 +/**
 + * Like write_file_buf(), but format the contents into a buffer first.
 + * Additionally, write_file() will append a newline if one is not already
 + * present, making it convenient to write text files:
 + *
 + *   write_file(path, "counter: %d", ctr);
 + */
 +__attribute__((format (printf, 2, 3)))
 +extern void write_file(const char *path, const char *fmt, ...);
  
  /* pager.c */
  extern void setup_pager(void);
- extern const char *pager_program;
  extern int pager_in_use(void);
  extern int pager_use_color;
  extern int term_columns(void);
@@@ -1846,14 -1778,14 +1858,14 @@@ void packet_trace_identity(const char *
   * return 0 if success, 1 - if addition of a file failed and
   * ADD_FILES_IGNORE_ERRORS was specified in flags
   */
 -int add_files_to_cache(const char *prefix, const struct pathspec *pathspec, int flags);
 +int add_files_to_cache(const char *prefix, const struct pathspec *pathspec, int flags, int force_mode);
  
  /* diff.c */
  extern int diff_auto_refresh_index;
  
  /* match-trees.c */
 -void shift_tree(const unsigned char *, const unsigned char *, unsigned char *, int);
 -void shift_tree_by(const unsigned char *, const unsigned char *, unsigned char *, const char *);
 +void shift_tree(const struct object_id *, const struct object_id *, struct object_id *, int);
 +void shift_tree_by(const struct object_id *, const struct object_id *, struct object_id *, const char *);
  
  /*
   * whitespace rules.
diff --combined config.c
index 0dfed682b86829fc06667d303c676e8abe6b3150,5e50fb1a31a5b4e0bb3c4cf517023c447c2f925f..1e4b6178f79587c9519ecbc9dd90c1e0b72af952
+++ b/config.c
@@@ -24,7 -24,7 +24,7 @@@ struct config_source 
                        size_t pos;
                } buf;
        } u;
 -      const char *origin_type;
 +      enum config_origin_type origin_type;
        const char *name;
        const char *path;
        int die_on_error;
        long (*do_ftell)(struct config_source *c);
  };
  
 +/*
 + * These variables record the "current" config source, which
 + * can be accessed by parsing callbacks.
 + *
 + * The "cf" variable will be non-NULL only when we are actually parsing a real
 + * config source (file, blob, cmdline, etc).
 + *
 + * The "current_config_kvi" variable will be non-NULL only when we are feeding
 + * cached config from a configset into a callback.
 + *
 + * They should generally never be non-NULL at the same time. If they are both
 + * NULL, then we aren't parsing anything (and depending on the function looking
 + * at the variables, it's either a bug for it to be called in the first place,
 + * or it's a function which can be reused for non-config purposes, and should
 + * fall back to some sane behavior).
 + */
  static struct config_source *cf;
 +static struct key_value_info *current_config_kvi;
 +
 +/*
 + * Similar to the variables above, this gives access to the "scope" of the
 + * current value (repo, global, etc). For cached values, it can be found via
 + * the current_config_kvi as above. During parsing, the current value can be
 + * found in this variable. It's not part of "cf" because it transcends a single
 + * file (i.e., a file included from .git/config is still in "repo" scope).
 + */
 +static enum config_scope current_parsing_scope;
  
  static int zlib_compression_seen;
  
@@@ -157,9 -131,7 +157,9 @@@ static int handle_path_include(const ch
        if (!access_or_die(path, R_OK, 0)) {
                if (++inc->depth > MAX_INCLUDE_DEPTH)
                        die(include_depth_advice, MAX_INCLUDE_DEPTH, path,
 -                          cf && cf->name ? cf->name : "the command line");
 +                          !cf ? "<unknown>" :
 +                          cf->name ? cf->name :
 +                          "the command line");
                ret = git_config_from_file(git_config_include, path, inc);
                inc->depth--;
        }
@@@ -190,7 -162,7 +190,7 @@@ void git_config_push_parameter(const ch
  {
        struct strbuf env = STRBUF_INIT;
        const char *old = getenv(CONFIG_DATA_ENVIRONMENT);
 -      if (old) {
 +      if (old && *old) {
                strbuf_addstr(&env, old);
                strbuf_addch(&env, ' ');
        }
@@@ -233,41 -205,32 +233,41 @@@ int git_config_parse_parameter(const ch
  int git_config_from_parameters(config_fn_t fn, void *data)
  {
        const char *env = getenv(CONFIG_DATA_ENVIRONMENT);
 +      int ret = 0;
        char *envw;
        const char **argv = NULL;
        int nr = 0, alloc = 0;
        int i;
 +      struct config_source source;
  
        if (!env)
                return 0;
 +
 +      memset(&source, 0, sizeof(source));
 +      source.prev = cf;
 +      source.origin_type = CONFIG_ORIGIN_CMDLINE;
 +      cf = &source;
 +
        /* sq_dequote will write over it */
        envw = xstrdup(env);
  
        if (sq_dequote_to_argv(envw, &argv, &nr, &alloc) < 0) {
 -              free(envw);
 -              return error("bogus format in " CONFIG_DATA_ENVIRONMENT);
 +              ret = error("bogus format in " CONFIG_DATA_ENVIRONMENT);
 +              goto out;
        }
  
        for (i = 0; i < nr; i++) {
                if (git_config_parse_parameter(argv[i], fn, data) < 0) {
 -                      free(argv);
 -                      free(envw);
 -                      return -1;
 +                      ret = -1;
 +                      goto out;
                }
        }
  
 +out:
        free(argv);
        free(envw);
 -      return nr > 0;
 +      cf = source.prev;
 +      return ret;
  }
  
  static int get_next_char(void)
@@@ -454,8 -417,6 +454,8 @@@ static int git_parse_source(config_fn_
        int comment = 0;
        int baselen = 0;
        struct strbuf *var = &cf->var;
 +      int error_return = 0;
 +      char *error_msg = NULL;
  
        /* U+FEFF Byte Order Mark in UTF8 */
        const char *bomptr = utf8_bom;
                if (get_value(fn, data, var) < 0)
                        break;
        }
 +
 +      switch (cf->origin_type) {
 +      case CONFIG_ORIGIN_BLOB:
 +              error_msg = xstrfmt(_("bad config line %d in blob %s"),
 +                                    cf->linenr, cf->name);
 +              break;
 +      case CONFIG_ORIGIN_FILE:
 +              error_msg = xstrfmt(_("bad config line %d in file %s"),
 +                                    cf->linenr, cf->name);
 +              break;
 +      case CONFIG_ORIGIN_STDIN:
 +              error_msg = xstrfmt(_("bad config line %d in standard input"),
 +                                    cf->linenr);
 +              break;
 +      case CONFIG_ORIGIN_SUBMODULE_BLOB:
 +              error_msg = xstrfmt(_("bad config line %d in submodule-blob %s"),
 +                                     cf->linenr, cf->name);
 +              break;
 +      case CONFIG_ORIGIN_CMDLINE:
 +              error_msg = xstrfmt(_("bad config line %d in command line %s"),
 +                                     cf->linenr, cf->name);
 +              break;
 +      default:
 +              error_msg = xstrfmt(_("bad config line %d in %s"),
 +                                    cf->linenr, cf->name);
 +      }
 +
        if (cf->die_on_error)
 -              die(_("bad config line %d in %s %s"), cf->linenr, cf->origin_type, cf->name);
 +              die("%s", error_msg);
        else
 -              return error(_("bad config line %d in %s %s"), cf->linenr, cf->origin_type, cf->name);
 +              error_return = error("%s", error_msg);
 +
 +      free(error_msg);
 +      return error_return;
  }
  
  static int parse_unit_factor(const char *end, uintmax_t *val)
@@@ -652,35 -583,16 +652,35 @@@ int git_parse_ulong(const char *value, 
  NORETURN
  static void die_bad_number(const char *name, const char *value)
  {
 -      const char *reason = errno == ERANGE ?
 -                           "out of range" :
 -                           "invalid unit";
 +      const char * error_type = (errno == ERANGE)? _("out of range"):_("invalid unit");
 +
        if (!value)
                value = "";
  
 -      if (cf && cf->origin_type && cf->name)
 -              die(_("bad numeric config value '%s' for '%s' in %s %s: %s"),
 -                  value, name, cf->origin_type, cf->name, reason);
 -      die(_("bad numeric config value '%s' for '%s': %s"), value, name, reason);
 +      if (!(cf && cf->name))
 +              die(_("bad numeric config value '%s' for '%s': %s"),
 +                  value, name, error_type);
 +
 +      switch (cf->origin_type) {
 +      case CONFIG_ORIGIN_BLOB:
 +              die(_("bad numeric config value '%s' for '%s' in blob %s: %s"),
 +                  value, name, cf->name, error_type);
 +      case CONFIG_ORIGIN_FILE:
 +              die(_("bad numeric config value '%s' for '%s' in file %s: %s"),
 +                  value, name, cf->name, error_type);
 +      case CONFIG_ORIGIN_STDIN:
 +              die(_("bad numeric config value '%s' for '%s' in standard input: %s"),
 +                  value, name, error_type);
 +      case CONFIG_ORIGIN_SUBMODULE_BLOB:
 +              die(_("bad numeric config value '%s' for '%s' in submodule-blob %s: %s"),
 +                  value, name, cf->name, error_type);
 +      case CONFIG_ORIGIN_CMDLINE:
 +              die(_("bad numeric config value '%s' for '%s' in command line %s: %s"),
 +                  value, name, cf->name, error_type);
 +      default:
 +              die(_("bad numeric config value '%s' for '%s' in %s: %s"),
 +                  value, name, cf->name, error_type);
 +      }
  }
  
  int git_config_int(const char *name, const char *value)
@@@ -805,9 -717,6 +805,9 @@@ static int git_default_core_config(cons
        if (!strcmp(var, "core.attributesfile"))
                return git_config_pathname(&git_attributes_file, var, value);
  
 +      if (!strcmp(var, "core.hookspath"))
 +              return git_config_pathname(&git_hooks_path, var, value);
 +
        if (!strcmp(var, "core.bare")) {
                is_bare_repository_cfg = git_config_bool(var, value);
                return 0;
                return 0;
        }
  
-       if (!strcmp(var, "core.pager"))
-               return git_config_string(&pager_program, var, value);
        if (!strcmp(var, "core.editor"))
                return git_config_string(&editor_program, var, value);
  
@@@ -1157,8 -1063,7 +1154,8 @@@ static int do_config_from(struct config
  }
  
  static int do_config_from_file(config_fn_t fn,
 -              const char *origin_type, const char *name, const char *path, FILE *f,
 +              const enum config_origin_type origin_type,
 +              const char *name, const char *path, FILE *f,
                void *data)
  {
        struct config_source top;
  
  static int git_config_from_stdin(config_fn_t fn, void *data)
  {
 -      return do_config_from_file(fn, "standard input", "", NULL, stdin, data);
 +      return do_config_from_file(fn, CONFIG_ORIGIN_STDIN, "", NULL, stdin, data);
  }
  
  int git_config_from_file(config_fn_t fn, const char *filename, void *data)
        f = fopen(filename, "r");
        if (f) {
                flockfile(f);
 -              ret = do_config_from_file(fn, "file", filename, filename, f, data);
 +              ret = do_config_from_file(fn, CONFIG_ORIGIN_FILE, filename, filename, f, data);
                funlockfile(f);
                fclose(f);
        }
        return ret;
  }
  
 -int git_config_from_mem(config_fn_t fn, const char *origin_type,
 +int git_config_from_mem(config_fn_t fn, const enum config_origin_type origin_type,
                        const char *name, const char *buf, size_t len, void *data)
  {
        struct config_source top;
@@@ -1232,7 -1137,7 +1229,7 @@@ static int git_config_from_blob_sha1(co
                return error("reference '%s' does not point to a blob", name);
        }
  
 -      ret = git_config_from_mem(fn, "blob", name, buf, size, data);
 +      ret = git_config_from_mem(fn, CONFIG_ORIGIN_BLOB, name, buf, size, data);
        free(buf);
  
        return ret;
@@@ -1286,36 -1191,47 +1283,36 @@@ int git_config_system(void
  
  static int do_git_config_sequence(config_fn_t fn, void *data)
  {
 -      int ret = 0, found = 0;
 +      int ret = 0;
        char *xdg_config = xdg_config_home("config");
        char *user_config = expand_user_path("~/.gitconfig");
-       char *repo_config = git_pathdup("config");
+       char *repo_config = have_git_dir() ? git_pathdup("config") : NULL;
  
 -      if (git_config_system() && !access_or_die(git_etc_gitconfig(), R_OK, 0)) {
 +      current_parsing_scope = CONFIG_SCOPE_SYSTEM;
 +      if (git_config_system() && !access_or_die(git_etc_gitconfig(), R_OK, 0))
                ret += git_config_from_file(fn, git_etc_gitconfig(),
                                            data);
 -              found += 1;
 -      }
  
 -      if (xdg_config && !access_or_die(xdg_config, R_OK, ACCESS_EACCES_OK)) {
 +      current_parsing_scope = CONFIG_SCOPE_GLOBAL;
 +      if (xdg_config && !access_or_die(xdg_config, R_OK, ACCESS_EACCES_OK))
                ret += git_config_from_file(fn, xdg_config, data);
 -              found += 1;
 -      }
  
 -      if (user_config && !access_or_die(user_config, R_OK, ACCESS_EACCES_OK)) {
 +      if (user_config && !access_or_die(user_config, R_OK, ACCESS_EACCES_OK))
                ret += git_config_from_file(fn, user_config, data);
 -              found += 1;
 -      }
  
 -      if (repo_config && !access_or_die(repo_config, R_OK, 0)) {
 +      current_parsing_scope = CONFIG_SCOPE_REPO;
 +      if (repo_config && !access_or_die(repo_config, R_OK, 0))
                ret += git_config_from_file(fn, repo_config, data);
 -              found += 1;
 -      }
  
 -      switch (git_config_from_parameters(fn, data)) {
 -      case -1: /* error */
 +      current_parsing_scope = CONFIG_SCOPE_CMDLINE;
 +      if (git_config_from_parameters(fn, data) < 0)
                die(_("unable to parse command-line config"));
 -              break;
 -      case 0: /* found nothing */
 -              break;
 -      default: /* found at least one item */
 -              found++;
 -              break;
 -      }
  
 +      current_parsing_scope = CONFIG_SCOPE_UNKNOWN;
        free(xdg_config);
        free(user_config);
        free(repo_config);
 -      return ret == 0 ? found : ret;
 +      return ret;
  }
  
  int git_config_with_options(config_fn_t fn, void *data,
@@@ -1350,7 -1266,7 +1347,7 @@@ static void git_config_raw(config_fn_t 
        if (git_config_with_options(fn, data, NULL, 1) < 0)
                /*
                 * git_config_with_options() normally returns only
 -               * positive values, as most errors are fatal, and
 +               * zero, as most errors are fatal, and
                 * non-fatal potential errors are guarded by "if"
                 * statements that are entered only when no error is
                 * possible.
                 * something went really wrong and we should stop
                 * immediately.
                 */
 -              die(_("unknown error occured while reading the configuration files"));
 +              die(_("unknown error occurred while reading the configuration files"));
  }
  
  static void configset_iter(struct config_set *cs, config_fn_t fn, void *data)
        struct string_list *values;
        struct config_set_element *entry;
        struct configset_list *list = &cs->list;
 -      struct key_value_info *kv_info;
  
        for (i = 0; i < list->nr; i++) {
                entry = list->items[i].e;
                value_index = list->items[i].value_index;
                values = &entry->value_list;
 -              if (fn(entry->key, values->items[value_index].string, data) < 0) {
 -                      kv_info = values->items[value_index].util;
 -                      git_die_config_linenr(entry->key, kv_info->filename, kv_info->linenr);
 -              }
 +
 +              current_config_kvi = values->items[value_index].util;
 +
 +              if (fn(entry->key, values->items[value_index].string, data) < 0)
 +                      git_die_config_linenr(entry->key,
 +                                            current_config_kvi->filename,
 +                                            current_config_kvi->linenr);
 +
 +              current_config_kvi = NULL;
        }
  }
  
@@@ -1438,19 -1350,14 +1435,19 @@@ static int configset_add_value(struct c
        l_item->e = e;
        l_item->value_index = e->value_list.nr - 1;
  
 -      if (cf) {
 +      if (!cf)
 +              die("BUG: configset_add_value has no source");
 +      if (cf->name) {
                kv_info->filename = strintern(cf->name);
                kv_info->linenr = cf->linenr;
 +              kv_info->origin_type = cf->origin_type;
        } else {
                /* for values read from `git_config_from_parameters()` */
                kv_info->filename = NULL;
                kv_info->linenr = -1;
 +              kv_info->origin_type = CONFIG_ORIGIN_CMDLINE;
        }
 +      kv_info->scope = current_parsing_scope;
        si->util = kv_info;
  
        return 0;
@@@ -2103,7 -2010,7 +2100,7 @@@ int git_config_set_multivar_in_file_gen
        lock = xcalloc(1, sizeof(struct lock_file));
        fd = hold_lock_file_for_update(lock, config_filename, 0);
        if (fd < 0) {
 -              error("could not lock config file %s: %s", config_filename, strerror(errno));
 +              error_errno("could not lock config file %s", config_filename);
                free(store.key);
                ret = CONFIG_NO_LOCK;
                goto out_free;
                free(store.key);
  
                if ( ENOENT != errno ) {
 -                      error("opening %s: %s", config_filename,
 -                            strerror(errno));
 +                      error_errno("opening %s", config_filename);
                        ret = CONFIG_INVALID_FILE; /* same as "invalid config file" */
                        goto out_free;
                }
                if (contents == MAP_FAILED) {
                        if (errno == ENODEV && S_ISDIR(st.st_mode))
                                errno = EISDIR;
 -                      error("unable to mmap '%s': %s",
 -                            config_filename, strerror(errno));
 +                      error_errno("unable to mmap '%s'", config_filename);
                        ret = CONFIG_INVALID_FILE;
                        contents = NULL;
                        goto out_free;
                in_fd = -1;
  
                if (chmod(get_lock_file_path(lock), st.st_mode & 07777) < 0) {
 -                      error("chmod on %s failed: %s",
 -                            get_lock_file_path(lock), strerror(errno));
 +                      error_errno("chmod on %s failed", get_lock_file_path(lock));
                        ret = CONFIG_NO_WRITE;
                        goto out_free;
                }
        }
  
        if (commit_lock_file(lock) < 0) {
 -              error("could not write config file %s: %s", config_filename,
 -                    strerror(errno));
 +              error_errno("could not write config file %s", config_filename);
                ret = CONFIG_NO_WRITE;
                lock = NULL;
                goto out_free;
@@@ -2417,8 -2328,8 +2414,8 @@@ int git_config_rename_section_in_file(c
        fstat(fileno(config_file), &st);
  
        if (chmod(get_lock_file_path(lock), st.st_mode & 07777) < 0) {
 -              ret = error("chmod on %s failed: %s",
 -                          get_lock_file_path(lock), strerror(errno));
 +              ret = error_errno("chmod on %s failed",
 +                                get_lock_file_path(lock));
                goto out;
        }
  
        fclose(config_file);
  unlock_and_out:
        if (commit_lock_file(lock) < 0)
 -              ret = error("could not write config file %s: %s",
 -                          config_filename, strerror(errno));
 +              ret = error_errno("could not write config file %s",
 +                                config_filename);
  out:
        free(filename_buf);
        return ret;
@@@ -2529,46 -2440,10 +2526,46 @@@ int parse_config_key(const char *var
  
  const char *current_config_origin_type(void)
  {
 -      return cf && cf->origin_type ? cf->origin_type : "command line";
 +      int type;
 +      if (current_config_kvi)
 +              type = current_config_kvi->origin_type;
 +      else if(cf)
 +              type = cf->origin_type;
 +      else
 +              die("BUG: current_config_origin_type called outside config callback");
 +
 +      switch (type) {
 +      case CONFIG_ORIGIN_BLOB:
 +              return "blob";
 +      case CONFIG_ORIGIN_FILE:
 +              return "file";
 +      case CONFIG_ORIGIN_STDIN:
 +              return "standard input";
 +      case CONFIG_ORIGIN_SUBMODULE_BLOB:
 +              return "submodule-blob";
 +      case CONFIG_ORIGIN_CMDLINE:
 +              return "command line";
 +      default:
 +              die("BUG: unknown config origin type");
 +      }
  }
  
  const char *current_config_name(void)
  {
 -      return cf && cf->name ? cf->name : "";
 +      const char *name;
 +      if (current_config_kvi)
 +              name = current_config_kvi->filename;
 +      else if (cf)
 +              name = cf->name;
 +      else
 +              die("BUG: current_config_name called outside config callback");
 +      return name ? name : "";
 +}
 +
 +enum config_scope current_config_scope(void)
 +{
 +      if (current_config_kvi)
 +              return current_config_kvi->scope;
 +      else
 +              return current_parsing_scope;
  }
diff --combined diff-no-index.c
index 1f8999b9cabf52c85b63f27178023ff53b7567c3,59237a626727864c786aca078564b632da08f294..f420786039d387d3a943d510c802ecd90c3ddf28
@@@ -65,7 -65,8 +65,7 @@@ static int populate_from_stdin(struct d
        size_t size = 0;
  
        if (strbuf_read(&buf, 0, 0) < 0)
 -              return error("error while reading from stdin %s",
 -                                   strerror(errno));
 +              return error_errno("error while reading from stdin");
  
        s->should_munmap = 0;
        s->data = strbuf_detach(&buf, &size);
@@@ -281,6 -282,9 +281,9 @@@ void diff_no_index(struct rev_info *rev
  
        DIFF_OPT_SET(&revs->diffopt, NO_INDEX);
  
+       DIFF_OPT_SET(&revs->diffopt, RELATIVE_NAME);
+       revs->diffopt.prefix = prefix;
        revs->max_count = -2;
        diff_setup_done(&revs->diffopt);
  
diff --combined environment.c
index ca72464a985021e58b898bb0b1f9af6c4a5282ac,abf3cb66e57ce5ea1d8c6b0378d9978de5d956c5..cd5aa57179240d615cb0628c9e24f44ac4b8c2d3
@@@ -31,7 -31,6 +31,7 @@@ const char *git_log_output_encoding
  const char *apply_default_whitespace;
  const char *apply_default_ignorewhitespace;
  const char *git_attributes_file;
 +const char *git_hooks_path;
  int zlib_compression_level = Z_BEST_SPEED;
  int core_compression_level;
  int core_compression_seen;
@@@ -40,7 -39,6 +40,6 @@@ size_t packed_git_window_size = DEFAULT
  size_t packed_git_limit = DEFAULT_PACKED_GIT_LIMIT;
  size_t delta_base_cache_limit = 96 * 1024 * 1024;
  unsigned long big_file_threshold = 512 * 1024 * 1024;
- const char *pager_program;
  int pager_use_color = 1;
  const char *editor_program;
  const char *askpass_program;
@@@ -196,6 -194,13 +195,13 @@@ int is_bare_repository(void
        return is_bare_repository_cfg && !get_git_work_tree();
  }
  
+ int have_git_dir(void)
+ {
+       return startup_info->have_repository
+               || git_dir
+               || getenv(GIT_DIR_ENVIRONMENT);
+ }
  const char *get_git_dir(void)
  {
        if (!git_dir)
@@@ -345,3 -350,8 +351,8 @@@ int get_shared_repository(void
        }
        return the_shared_repository;
  }
+ void reset_shared_repository(void)
+ {
+       need_shared_repository_from_config = 1;
+ }
diff --combined git.c
index 1c61151758531889e2a2e60e66799283240dc89a,03151f51e6e04f5c8f14d4ce86e573dca886bcab..ab5c99cf70c3b46fb70b0e6a6bf5520e392e099c
--- 1/git.c
--- 2/git.c
+++ b/git.c
@@@ -444,7 -444,7 +444,7 @@@ static struct cmd_struct commands[] = 
        { "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 },
+       { "patch-id", cmd_patch_id, RUN_SETUP_GENTLY },
        { "pickaxe", cmd_blame, RUN_SETUP },
        { "prune", cmd_prune, RUN_SETUP },
        { "prune-packed", cmd_prune_packed, RUN_SETUP },
@@@ -522,34 -522,21 +522,34 @@@ static void strip_extension(const char 
  
  static void handle_builtin(int argc, const char **argv)
  {
 +      struct argv_array args = ARGV_ARRAY_INIT;
        const char *cmd;
        struct cmd_struct *builtin;
  
        strip_extension(argv);
        cmd = argv[0];
  
 -      /* Turn "git cmd --help" into "git help cmd" */
 +      /* Turn "git cmd --help" into "git help --exclude-guides cmd" */
        if (argc > 1 && !strcmp(argv[1], "--help")) {
 +              int i;
 +
                argv[1] = argv[0];
                argv[0] = cmd = "help";
 +
 +              for (i = 0; i < argc; i++) {
 +                      argv_array_push(&args, argv[i]);
 +                      if (!i)
 +                              argv_array_push(&args, "--exclude-guides");
 +              }
 +
 +              argc++;
 +              argv = args.argv;
        }
  
        builtin = get_builtin(cmd);
        if (builtin)
                exit(run_builtin(builtin, argc, argv));
 +      argv_array_clear(&args);
  }
  
  static void execv_dashed_external(const char **argv)
@@@ -622,15 -609,48 +622,15 @@@ static int run_argv(int *argcp, const c
        return done_alias;
  }
  
 -/*
 - * Many parts of Git have subprograms communicate via pipe, expect the
 - * upstream of a pipe to die with SIGPIPE when the downstream of a
 - * pipe does not need to read all that is written.  Some third-party
 - * programs that ignore or block SIGPIPE for their own reason forget
 - * to restore SIGPIPE handling to the default before spawning Git and
 - * break this carefully orchestrated machinery.
 - *
 - * Restore the way SIGPIPE is handled to default, which is what we
 - * expect.
 - */
 -static void restore_sigpipe_to_default(void)
 -{
 -      sigset_t unblock;
 -
 -      sigemptyset(&unblock);
 -      sigaddset(&unblock, SIGPIPE);
 -      sigprocmask(SIG_UNBLOCK, &unblock, NULL);
 -      signal(SIGPIPE, SIG_DFL);
 -}
 -
 -int main(int argc, char **av)
 +int cmd_main(int argc, const char **argv)
  {
 -      const char **argv = (const char **) av;
        const char *cmd;
        int done_help = 0;
  
 -      cmd = git_extract_argv0_path(argv[0]);
 +      cmd = argv[0];
        if (!cmd)
                cmd = "git-help";
  
 -      /*
 -       * Always open file descriptors 0/1/2 to avoid clobbering files
 -       * in die().  It also avoids messing up when the pipes are dup'ed
 -       * onto stdin/stdout/stderr in the child processes we spawn.
 -       */
 -      sanitize_stdfds();
 -
 -      restore_sigpipe_to_default();
 -
 -      git_setup_gettext();
 -
        trace_command_performance(argv);
  
        /*
diff --combined pager.c
index 6470b8180df7e0de51a0d71ac47f8c078d226923,f97a8bdc90bf88a686c0e7ed84b4be1df52d7fdd..ae79643363091f4967097bdff6e009ea4c7df5e0
+++ b/pager.c
@@@ -6,12 -6,8 +6,8 @@@
  #define DEFAULT_PAGER "less"
  #endif
  
- /*
-  * This is split up from the rest of git so that we can do
-  * something different on Windows.
-  */
  static struct child_process pager_process = CHILD_PROCESS_INIT;
+ static const char *pager_program;
  
  static void wait_for_pager(int in_signal)
  {
@@@ -40,6 -36,44 +36,44 @@@ static void wait_for_pager_signal(int s
        raise(signo);
  }
  
+ static int core_pager_config(const char *var, const char *value, void *data)
+ {
+       if (!strcmp(var, "core.pager"))
+               return git_config_string(&pager_program, var, value);
+       return 0;
+ }
+ static void read_early_config(config_fn_t cb, void *data)
+ {
+       git_config_with_options(cb, data, NULL, 1);
+       /*
+        * Note that this is a really dirty hack that does the wrong thing in
+        * many cases. The crux of the problem is that we cannot run
+        * setup_git_directory() early on in git's setup, so we have no idea if
+        * we are in a repository or not, and therefore are not sure whether
+        * and how to read repository-local config.
+        *
+        * So if we _aren't_ in a repository (or we are but we would reject its
+        * core.repositoryformatversion), we'll read whatever is in .git/config
+        * blindly. Similarly, if we _are_ in a repository, but not at the
+        * root, we'll fail to find .git/config (because it's really
+        * ../.git/config, etc). See t7006 for a complete set of failures.
+        *
+        * However, we have historically provided this hack because it does
+        * work some of the time (namely when you are at the top-level of a
+        * valid repository), and would rarely make things worse (i.e., you do
+        * not generally have a .git/config file sitting around).
+        */
+       if (!startup_info->have_repository) {
+               struct git_config_source repo_config;
+               memset(&repo_config, 0, sizeof(repo_config));
+               repo_config.file = ".git/config";
+               git_config_with_options(cb, data, &repo_config, 1);
+       }
+ }
  const char *git_pager(int stdout_is_tty)
  {
        const char *pager;
@@@ -50,7 -84,7 +84,7 @@@
        pager = getenv("GIT_PAGER");
        if (!pager) {
                if (!pager_program)
-                       git_config(git_default_config, NULL);
+                       read_early_config(core_pager_config, NULL);
                pager = pager_program;
        }
        if (!pager)
        return pager;
  }
  
 +static void setup_pager_env(struct argv_array *env)
 +{
 +      const char **argv;
 +      int i;
 +      char *pager_env = xstrdup(PAGER_ENV);
 +      int n = split_cmdline(pager_env, &argv);
 +
 +      if (n < 0)
 +              die("malformed build-time PAGER_ENV: %s",
 +                      split_cmdline_strerror(n));
 +
 +      for (i = 0; i < n; i++) {
 +              char *cp = strchr(argv[i], '=');
 +
 +              if (!cp)
 +                      die("malformed build-time PAGER_ENV");
 +
 +              *cp = '\0';
 +              if (!getenv(argv[i])) {
 +                      *cp = '=';
 +                      argv_array_push(env, argv[i]);
 +              }
 +      }
 +      free(pager_env);
 +      free(argv);
 +}
 +
  void prepare_pager_args(struct child_process *pager_process, const char *pager)
  {
        argv_array_push(&pager_process->args, pager);
        pager_process->use_shell = 1;
 -      if (!getenv("LESS"))
 -              argv_array_push(&pager_process->env_array, "LESS=FRX");
 -      if (!getenv("LV"))
 -              argv_array_push(&pager_process->env_array, "LV=-c");
 +      setup_pager_env(&pager_process->env_array);
  }
  
  void setup_pager(void)
@@@ -180,23 -190,42 +214,42 @@@ int decimal_width(uintmax_t number
        return width;
  }
  
- /* returns 0 for "no pager", 1 for "use pager", and -1 for "not specified" */
- int check_pager_config(const char *cmd)
+ struct pager_command_config_data {
+       const char *cmd;
+       int want;
+       char *value;
+ };
+ static int pager_command_config(const char *var, const char *value, void *vdata)
  {
-       int want = -1;
-       struct strbuf key = STRBUF_INIT;
-       const char *value = NULL;
-       strbuf_addf(&key, "pager.%s", cmd);
-       if (git_config_key_is_valid(key.buf) &&
-           !git_config_get_value(key.buf, &value)) {
-               int b = git_config_maybe_bool(key.buf, value);
+       struct pager_command_config_data *data = vdata;
+       const char *cmd;
+       if (skip_prefix(var, "pager.", &cmd) && !strcmp(cmd, data->cmd)) {
+               int b = git_config_maybe_bool(var, value);
                if (b >= 0)
-                       want = b;
+                       data->want = b;
                else {
-                       want = 1;
-                       pager_program = xstrdup(value);
+                       data->want = 1;
+                       data->value = xstrdup(value);
                }
        }
-       strbuf_release(&key);
-       return want;
+       return 0;
+ }
+ /* returns 0 for "no pager", 1 for "use pager", and -1 for "not specified" */
+ int check_pager_config(const char *cmd)
+ {
+       struct pager_command_config_data data;
+       data.cmd = cmd;
+       data.want = -1;
+       data.value = NULL;
+       read_early_config(pager_command_config, &data);
+       if (data.value)
+               pager_program = data.value;
+       return data.want;
  }
diff --combined t/helper/test-config.c
index 3c6d08cd095152cab9abeb038c1b4f82440c55cd,0000000000000000000000000000000000000000..83a4f2ab86999876ecfb78c7e6dd108eb09b04ae
mode 100644,000000..100644
--- /dev/null
@@@ -1,188 -1,0 +1,191 @@@
 +#include "cache.h"
 +#include "string-list.h"
 +
 +/*
 + * This program exposes the C API of the configuration mechanism
 + * as a set of simple commands in order to facilitate testing.
 + *
 + * Reads stdin and prints result of command to stdout:
 + *
 + * get_value -> prints the value with highest priority for the entered key
 + *
 + * get_value_multi -> prints all values for the entered key in increasing order
 + *                 of priority
 + *
 + * get_int -> print integer value for the entered key or die
 + *
 + * get_bool -> print bool value for the entered key or die
 + *
 + * get_string -> print string value for the entered key or die
 + *
 + * configset_get_value -> returns value with the highest priority for the entered key
 + *                    from a config_set constructed from files entered as arguments.
 + *
 + * configset_get_value_multi -> returns value_list for the entered key sorted in
 + *                            ascending order of priority from a config_set
 + *                            constructed from files entered as arguments.
 + *
 + * iterate -> iterate over all values using git_config(), and print some
 + *            data for each
 + *
 + * Examples:
 + *
 + * To print the value with highest priority for key "foo.bAr Baz.rock":
 + *    test-config get_value "foo.bAr Baz.rock"
 + *
 + */
 +
 +static const char *scope_name(enum config_scope scope)
 +{
 +      switch (scope) {
 +      case CONFIG_SCOPE_SYSTEM:
 +              return "system";
 +      case CONFIG_SCOPE_GLOBAL:
 +              return "global";
 +      case CONFIG_SCOPE_REPO:
 +              return "repo";
 +      case CONFIG_SCOPE_CMDLINE:
 +              return "cmdline";
 +      default:
 +              return "unknown";
 +      }
 +}
 +static int iterate_cb(const char *var, const char *value, void *data)
 +{
 +      static int nr;
 +
 +      if (nr++)
 +              putchar('\n');
 +
 +      printf("key=%s\n", var);
 +      printf("value=%s\n", value ? value : "(null)");
 +      printf("origin=%s\n", current_config_origin_type());
 +      printf("name=%s\n", current_config_name());
 +      printf("scope=%s\n", scope_name(current_config_scope()));
 +
 +      return 0;
 +}
 +
 +int cmd_main(int argc, const char **argv)
 +{
 +      int i, val;
 +      const char *v;
 +      const struct string_list *strptr;
 +      struct config_set cs;
++
++      setup_git_directory();
++
 +      git_configset_init(&cs);
 +
 +      if (argc < 2) {
 +              fprintf(stderr, "Please, provide a command name on the command-line\n");
 +              goto exit1;
 +      } else if (argc == 3 && !strcmp(argv[1], "get_value")) {
 +              if (!git_config_get_value(argv[2], &v)) {
 +                      if (!v)
 +                              printf("(NULL)\n");
 +                      else
 +                              printf("%s\n", v);
 +                      goto exit0;
 +              } else {
 +                      printf("Value not found for \"%s\"\n", argv[2]);
 +                      goto exit1;
 +              }
 +      } else if (argc == 3 && !strcmp(argv[1], "get_value_multi")) {
 +              strptr = git_config_get_value_multi(argv[2]);
 +              if (strptr) {
 +                      for (i = 0; i < strptr->nr; i++) {
 +                              v = strptr->items[i].string;
 +                              if (!v)
 +                                      printf("(NULL)\n");
 +                              else
 +                                      printf("%s\n", v);
 +                      }
 +                      goto exit0;
 +              } else {
 +                      printf("Value not found for \"%s\"\n", argv[2]);
 +                      goto exit1;
 +              }
 +      } else if (argc == 3 && !strcmp(argv[1], "get_int")) {
 +              if (!git_config_get_int(argv[2], &val)) {
 +                      printf("%d\n", val);
 +                      goto exit0;
 +              } else {
 +                      printf("Value not found for \"%s\"\n", argv[2]);
 +                      goto exit1;
 +              }
 +      } else if (argc == 3 && !strcmp(argv[1], "get_bool")) {
 +              if (!git_config_get_bool(argv[2], &val)) {
 +                      printf("%d\n", val);
 +                      goto exit0;
 +              } else {
 +                      printf("Value not found for \"%s\"\n", argv[2]);
 +                      goto exit1;
 +              }
 +      } else if (argc == 3 && !strcmp(argv[1], "get_string")) {
 +              if (!git_config_get_string_const(argv[2], &v)) {
 +                      printf("%s\n", v);
 +                      goto exit0;
 +              } else {
 +                      printf("Value not found for \"%s\"\n", argv[2]);
 +                      goto exit1;
 +              }
 +      } else if (!strcmp(argv[1], "configset_get_value")) {
 +              for (i = 3; i < argc; i++) {
 +                      int err;
 +                      if ((err = git_configset_add_file(&cs, argv[i]))) {
 +                              fprintf(stderr, "Error (%d) reading configuration file %s.\n", err, argv[i]);
 +                              goto exit2;
 +                      }
 +              }
 +              if (!git_configset_get_value(&cs, argv[2], &v)) {
 +                      if (!v)
 +                              printf("(NULL)\n");
 +                      else
 +                              printf("%s\n", v);
 +                      goto exit0;
 +              } else {
 +                      printf("Value not found for \"%s\"\n", argv[2]);
 +                      goto exit1;
 +              }
 +      } else if (!strcmp(argv[1], "configset_get_value_multi")) {
 +              for (i = 3; i < argc; i++) {
 +                      int err;
 +                      if ((err = git_configset_add_file(&cs, argv[i]))) {
 +                              fprintf(stderr, "Error (%d) reading configuration file %s.\n", err, argv[i]);
 +                              goto exit2;
 +                      }
 +              }
 +              strptr = git_configset_get_value_multi(&cs, argv[2]);
 +              if (strptr) {
 +                      for (i = 0; i < strptr->nr; i++) {
 +                              v = strptr->items[i].string;
 +                              if (!v)
 +                                      printf("(NULL)\n");
 +                              else
 +                                      printf("%s\n", v);
 +                      }
 +                      goto exit0;
 +              } else {
 +                      printf("Value not found for \"%s\"\n", argv[2]);
 +                      goto exit1;
 +              }
 +      } else if (!strcmp(argv[1], "iterate")) {
 +              git_config(iterate_cb, NULL);
 +              goto exit0;
 +      }
 +
 +      die("%s: Please check the syntax and the function name", argv[0]);
 +
 +exit0:
 +      git_configset_clear(&cs);
 +      return 0;
 +
 +exit1:
 +      git_configset_clear(&cs);
 +      return 1;
 +
 +exit2:
 +      git_configset_clear(&cs);
 +      return 2;
 +}
diff --combined t/t4204-patch-id.sh
index 84a809690e786c1278d0885b4b441c6ce3bbe4f0,10b6ad02e2ee9016b2e678d8e2459e75f2d1e2f4..0288c17ec60b803d2815fb1b704c35f74e4a7753
@@@ -30,11 -30,11 +30,11 @@@ test_expect_success 'patch-id output i
  
  #calculate patch id. Make sure output is not empty.
  calc_patch_id () {
 -      name="$1"
 +      patch_name="$1"
        shift
        git patch-id "$@" |
 -      sed "s/ .*//" >patch-id_"$name" &&
 -      test_line_count -gt 0 patch-id_"$name"
 +      sed "s/ .*//" >patch-id_"$patch_name" &&
 +      test_line_count -gt 0 patch-id_"$patch_name"
  }
  
  get_top_diff () {
@@@ -143,6 -143,20 +143,20 @@@ test_expect_success 'patch-id supports 
        test_cmp patch-id_master patch-id_same
  '
  
+ test_expect_success 'patch-id respects config from subdir' '
+       test_config patchid.stable true &&
+       mkdir subdir &&
+       # copy these because test_patch_id() looks for them in
+       # the current directory
+       cp bar-then-foo foo-then-bar subdir &&
+       (
+               cd subdir &&
+               test_patch_id irrelevant patchid.stable=true
+       )
+ '
  cat >nonl <<\EOF
  diff --git i/a w/a
  index e69de29..2e65efe 100644