Merge branch 'lt/abbrev-auto'
authorJunio C Hamano <gitster@pobox.com>
Thu, 27 Oct 2016 21:58:47 +0000 (14:58 -0700)
committerJunio C Hamano <gitster@pobox.com>
Thu, 27 Oct 2016 21:58:47 +0000 (14:58 -0700)
Allow the default abbreviation length, which has historically been
7, to scale as the repository grows. The logic suggests to use 12
hexdigits for the Linux kernel, and 9 to 10 for Git itself.

* lt/abbrev-auto:
abbrev: auto size the default abbreviation
abbrev: prepare for new world order
abbrev: add FALLBACK_DEFAULT_ABBREV to prepare for auto sizing

1  2 
builtin/fetch.c
builtin/rev-parse.c
cache.h
diff.c
environment.c
sha1_name.c
transport.h
diff --combined builtin/fetch.c
index 74c0546362d9a6aab185f2a4327cd489a91a3f89,a9f12cc5cf76f3a0917b79a037fe47eb4f4b6858..b2e113d387af821355c66d32642351db6bf0ccd1
@@@ -17,6 -17,9 +17,9 @@@
  #include "argv-array.h"
  #include "utf8.h"
  
+ #define TRANSPORT_SUMMARY(x) \
+       (int)(TRANSPORT_SUMMARY_WIDTH + strlen(x) - gettext_width(x)), (x)
  static const char * const builtin_fetch_usage[] = {
        N_("git fetch [<options>] [<repository> [<refspec>...]]"),
        N_("git fetch [<options>] <group>"),
@@@ -35,15 -38,13 +38,15 @@@ static int fetch_prune_config = -1; /* 
  static int prune = -1; /* unspecified */
  #define PRUNE_BY_DEFAULT 0 /* do we prune by default? */
  
 -static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity;
 +static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity, deepen_relative;
  static int progress = -1, recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
 -static int tags = TAGS_DEFAULT, unshallow, update_shallow;
 +static int tags = TAGS_DEFAULT, unshallow, update_shallow, deepen;
  static int max_children = -1;
  static enum transport_family family;
  static const char *depth;
 +static const char *deepen_since;
  static const char *upload_pack;
 +static struct string_list deepen_not = STRING_LIST_INIT_NODUP;
  static struct strbuf default_rla = STRBUF_INIT;
  static struct transport *gtransport;
  static struct transport *gsecondary;
@@@ -119,12 -120,6 +122,12 @@@ static struct option builtin_fetch_opti
        OPT_BOOL(0, "progress", &progress, N_("force progress reporting")),
        OPT_STRING(0, "depth", &depth, N_("depth"),
                   N_("deepen history of shallow clone")),
 +      OPT_STRING(0, "shallow-since", &deepen_since, N_("time"),
 +                 N_("deepen history of shallow repository based on time")),
 +      OPT_STRING_LIST(0, "shallow-exclude", &deepen_not, N_("revision"),
 +                      N_("deepen history of shallow clone by excluding rev")),
 +      OPT_INTEGER(0, "deepen", &deepen_relative,
 +                  N_("deepen history of shallow clone")),
        { OPTION_SET_INT, 0, "unshallow", &unshallow, NULL,
                   N_("convert to a complete repository"),
                   PARSE_OPT_NONEG | PARSE_OPT_NOARG, NULL, 1 },
@@@ -241,10 -236,9 +244,10 @@@ static void find_non_local_tags(struct 
                 * as one to ignore by setting util to NULL.
                 */
                if (ends_with(ref->name, "^{}")) {
 -                      if (item && !has_object_file(&ref->old_oid) &&
 +                      if (item &&
 +                          !has_object_file_with_flags(&ref->old_oid, HAS_SHA1_QUICK) &&
                            !will_fetch(head, ref->old_oid.hash) &&
 -                          !has_sha1_file(item->util) &&
 +                          !has_sha1_file_with_flags(item->util, HAS_SHA1_QUICK) &&
                            !will_fetch(head, item->util))
                                item->util = NULL;
                        item = NULL;
                 * to check if it is a lightweight tag that we want to
                 * fetch.
                 */
 -              if (item && !has_sha1_file(item->util) &&
 +              if (item &&
 +                  !has_sha1_file_with_flags(item->util, HAS_SHA1_QUICK) &&
                    !will_fetch(head, item->util))
                        item->util = NULL;
  
         * We may have a final lightweight tag that needs to be
         * checked to see if it needs fetching.
         */
 -      if (item && !has_sha1_file(item->util) &&
 +      if (item &&
 +          !has_sha1_file_with_flags(item->util, HAS_SHA1_QUICK) &&
            !will_fetch(head, item->util))
                item->util = NULL;
  
@@@ -886,7 -878,7 +889,7 @@@ static int quickfetch(struct ref *ref_m
         * really need to perform.  Claiming failure now will ensure
         * we perform the network exchange to deepen our history.
         */
 -      if (depth)
 +      if (deepen)
                return -1;
        opt.quiet = 1;
        return check_connected(iterate_ref_map, &rm, &opt);
@@@ -994,7 -986,7 +997,7 @@@ static void set_option(struct transpor
                        name, transport->url);
  }
  
 -static struct transport *prepare_transport(struct remote *remote)
 +static struct transport *prepare_transport(struct remote *remote, int deepen)
  {
        struct transport *transport;
        transport = transport_get(remote, NULL);
                set_option(transport, TRANS_OPT_KEEP, "yes");
        if (depth)
                set_option(transport, TRANS_OPT_DEPTH, depth);
 +      if (deepen && deepen_since)
 +              set_option(transport, TRANS_OPT_DEEPEN_SINCE, deepen_since);
 +      if (deepen && deepen_not.nr)
 +              set_option(transport, TRANS_OPT_DEEPEN_NOT,
 +                         (const char *)&deepen_not);
 +      if (deepen_relative)
 +              set_option(transport, TRANS_OPT_DEEPEN_RELATIVE, "yes");
        if (update_shallow)
                set_option(transport, TRANS_OPT_UPDATE_SHALLOW, "yes");
        return transport;
  
  static void backfill_tags(struct transport *transport, struct ref *ref_map)
  {
 -      if (transport->cannot_reuse) {
 -              gsecondary = prepare_transport(transport->remote);
 +      int cannot_reuse;
 +
 +      /*
 +       * Once we have set TRANS_OPT_DEEPEN_SINCE, we can't unset it
 +       * when remote helper is used (setting it to an empty string
 +       * is not unsetting). We could extend the remote helper
 +       * protocol for that, but for now, just force a new connection
 +       * without deepen-since. Similar story for deepen-not.
 +       */
 +      cannot_reuse = transport->cannot_reuse ||
 +              deepen_since || deepen_not.nr;
 +      if (cannot_reuse) {
 +              gsecondary = prepare_transport(transport->remote, 0);
                transport = gsecondary;
        }
  
        transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL);
        transport_set_option(transport, TRANS_OPT_DEPTH, "0");
 +      transport_set_option(transport, TRANS_OPT_DEEPEN_RELATIVE, NULL);
        fetch_refs(transport, ref_map);
  
        if (gsecondary) {
@@@ -1249,7 -1222,7 +1252,7 @@@ static int fetch_one(struct remote *rem
                die(_("No remote repository specified.  Please, specify either a URL or a\n"
                    "remote name from which new revisions should be fetched."));
  
 -      gtransport = prepare_transport(remote);
 +      gtransport = prepare_transport(remote, 1);
  
        if (prune < 0) {
                /* no command line request */
@@@ -1309,13 -1282,6 +1312,13 @@@ int cmd_fetch(int argc, const char **ar
        argc = parse_options(argc, argv, prefix,
                             builtin_fetch_options, builtin_fetch_usage, 0);
  
 +      if (deepen_relative) {
 +              if (deepen_relative < 0)
 +                      die(_("Negative depth in --deepen is not supported"));
 +              if (depth)
 +                      die(_("--deepen and --depth are mutually exclusive"));
 +              depth = xstrfmt("%d", deepen_relative);
 +      }
        if (unshallow) {
                if (depth)
                        die(_("--depth and --unshallow cannot be used together"));
        /* no need to be strict, transport_set_option() will validate it again */
        if (depth && atoi(depth) < 1)
                die(_("depth %s is not a positive number"), depth);
 +      if (depth || deepen_since || deepen_not.nr)
 +              deepen = 1;
  
        if (recurse_submodules != RECURSE_SUBMODULES_OFF) {
                if (recurse_submodules_default) {
diff --combined builtin/rev-parse.c
index 4da1f1da25b48c1027a3d255f1f86bb260c1fd21,17cbfabdde3845099e0ffd303db5e4a730e5262b..cfb0f1510c59674abe68fb67ff45142b5c92ac89
@@@ -298,30 -298,14 +298,30 @@@ static int try_parent_shorthands(const 
        unsigned char sha1[20];
        struct commit *commit;
        struct commit_list *parents;
 -      int parents_only;
 -
 -      if ((dotdot = strstr(arg, "^!")))
 -              parents_only = 0;
 -      else if ((dotdot = strstr(arg, "^@")))
 -              parents_only = 1;
 -
 -      if (!dotdot || dotdot[2])
 +      int parent_number;
 +      int include_rev = 0;
 +      int include_parents = 0;
 +      int exclude_parent = 0;
 +
 +      if ((dotdot = strstr(arg, "^!"))) {
 +              include_rev = 1;
 +              if (dotdot[2])
 +                      return 0;
 +      } else if ((dotdot = strstr(arg, "^@"))) {
 +              include_parents = 1;
 +              if (dotdot[2])
 +                      return 0;
 +      } else if ((dotdot = strstr(arg, "^-"))) {
 +              include_rev = 1;
 +              exclude_parent = 1;
 +
 +              if (dotdot[2]) {
 +                      char *end;
 +                      exclude_parent = strtoul(dotdot + 2, &end, 10);
 +                      if (*end != '\0' || !exclude_parent)
 +                              return 0;
 +              }
 +      } else
                return 0;
  
        *dotdot = 0;
                return 0;
        }
  
 -      if (!parents_only)
 -              show_rev(NORMAL, sha1, arg);
        commit = lookup_commit_reference(sha1);
 -      for (parents = commit->parents; parents; parents = parents->next)
 -              show_rev(parents_only ? NORMAL : REVERSED,
 -                              parents->item->object.oid.hash, arg);
 +      if (exclude_parent &&
 +          exclude_parent > commit_list_count(commit->parents)) {
 +              *dotdot = '^';
 +              return 0;
 +      }
 +
 +      if (include_rev)
 +              show_rev(NORMAL, sha1, arg);
 +      for (parents = commit->parents, parent_number = 1;
 +           parents;
 +           parents = parents->next, parent_number++) {
 +              if (exclude_parent && parent_number != exclude_parent)
 +                      continue;
 +
 +              show_rev(include_parents ? NORMAL : REVERSED,
 +                       parents->item->object.oid.hash, arg);
 +      }
  
        *dotdot = '^';
        return 1;
@@@ -671,8 -643,9 +671,9 @@@ int cmd_rev_parse(int argc, const char 
                                filter &= ~(DO_FLAGS|DO_NOREV);
                                verify = 1;
                                abbrev = DEFAULT_ABBREV;
-                               if (arg[7] == '=')
-                                       abbrev = strtoul(arg + 8, NULL, 10);
+                               if (!arg[7])
+                                       continue;
+                               abbrev = strtoul(arg + 8, NULL, 10);
                                if (abbrev < MINIMUM_ABBREV)
                                        abbrev = MINIMUM_ABBREV;
                                else if (40 <= abbrev)
diff --combined cache.h
index f7ee4145634762a42663df3c69b095d64d919d01,0e2a0595e516445e833b24d8a2acd47dc07f5b49..446d4cb63258b7744e6729a83b980b72bf65c32e
+++ b/cache.h
@@@ -367,9 -367,8 +367,9 @@@ 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), 0)
 -#define add_file_to_cache(path, flags) add_file_to_index(&the_index, (path), (flags), 0)
 +#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 chmod_cache_entry(ce, flip) chmod_index_entry(&the_index, (ce), (flip))
  #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))
@@@ -409,7 -408,6 +409,7 @@@ static inline enum object_type object_t
  #define GIT_NAMESPACE_ENVIRONMENT "GIT_NAMESPACE"
  #define GIT_WORK_TREE_ENVIRONMENT "GIT_WORK_TREE"
  #define GIT_PREFIX_ENVIRONMENT "GIT_PREFIX"
 +#define GIT_SUPER_PREFIX_ENVIRONMENT "GIT_INTERNAL_SUPER_PREFIX"
  #define DEFAULT_GIT_DIR_ENVIRONMENT ".git"
  #define DB_ENVIRONMENT "GIT_OBJECT_DIRECTORY"
  #define INDEX_ENVIRONMENT "GIT_INDEX_FILE"
  #define GIT_GLOB_PATHSPECS_ENVIRONMENT "GIT_GLOB_PATHSPECS"
  #define GIT_NOGLOB_PATHSPECS_ENVIRONMENT "GIT_NOGLOB_PATHSPECS"
  #define GIT_ICASE_PATHSPECS_ENVIRONMENT "GIT_ICASE_PATHSPECS"
 +#define GIT_QUARANTINE_ENVIRONMENT "GIT_QUARANTINE_PATH"
  
  /*
   * This environment variable is expected to contain a boolean indicating
@@@ -477,7 -474,6 +477,7 @@@ extern int get_common_dir_noenv(struct 
  extern int get_common_dir(struct strbuf *sb, const char *gitdir);
  extern const char *get_git_namespace(void);
  extern const char *strip_namespace(const char *namespaced_ref);
 +extern const char *get_super_prefix(void);
  extern const char *get_git_work_tree(void);
  
  /*
@@@ -529,10 -525,9 +529,10 @@@ extern void verify_non_filename(const c
  extern int path_inside_repo(const char *prefix, const char *path);
  
  #define INIT_DB_QUIET 0x0001
 +#define INIT_DB_EXIST_OK 0x0002
  
 -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);
 +extern int init_db(const char *git_dir, const char *real_git_dir,
 +                 const char *template_dir, unsigned int flags);
  
  extern void sanitize_stdfds(void);
  extern int daemonize(void);
@@@ -592,10 -587,9 +592,10 @@@ 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, int force_mode);
 -extern int add_file_to_index(struct index_state *, const char *path, int flags, int force_mode);
 +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 struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, unsigned int refresh_options);
 +extern int chmod_index_entry(struct index_state *, struct cache_entry *ce, char flip);
  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);
  extern int index_name_is_other(const struct index_state *, const char *, int);
@@@ -1157,7 -1151,6 +1157,7 @@@ static inline int has_sha1_file(const u
  
  /* Same as the above, except for struct object_id. */
  extern int has_object_file(const struct object_id *oid);
 +extern int has_object_file_with_flags(const struct object_id *oid, int flags);
  
  /*
   * Return true iff an alternate object database has a loose object
@@@ -1190,6 -1183,9 +1190,9 @@@ static inline int hex2chr(const char *s
  #define MINIMUM_ABBREV minimum_abbrev
  #define DEFAULT_ABBREV default_abbrev
  
+ /* used when the code does not know or care what the default abbrev is */
+ #define FALLBACK_DEFAULT_ABBREV 7
  struct object_context {
        unsigned char tree[20];
        char path[PATH_MAX];
  #define GET_SHA1_TREEISH          020
  #define GET_SHA1_BLOB             040
  #define GET_SHA1_FOLLOW_SYMLINKS 0100
+ #define GET_SHA1_AUTOMATIC     0200
  #define GET_SHA1_ONLY_TO_DIE    04000
  
  #define GET_SHA1_DISAMBIGUATORS \
@@@ -1368,7 -1365,6 +1372,7 @@@ struct checkout 
                 not_new:1,
                 refresh_cache:1;
  };
 +#define CHECKOUT_INIT { NULL, "" }
  
  #define TEMPORARY_FILENAME_LENGTH 25
  extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath);
@@@ -1394,46 -1390,16 +1398,46 @@@ extern void remove_scheduled_dirs(void)
  
  extern struct alternate_object_database {
        struct alternate_object_database *next;
 -      char *name;
 -      char base[FLEX_ARRAY]; /* more */
 +
 +      /* see alt_scratch_buf() */
 +      struct strbuf scratch;
 +      size_t base_len;
 +
 +      char path[FLEX_ARRAY];
  } *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*);
  
 +/*
 + * Allocate a "struct alternate_object_database" but do _not_ actually
 + * add it to the list of alternates.
 + */
 +struct alternate_object_database *alloc_alt_odb(const char *dir);
 +
 +/*
 + * Add the directory to the on-disk alternates file; the new entry will also
 + * take effect in the current process.
 + */
 +extern void add_to_alternates_file(const char *dir);
 +
 +/*
 + * Add the directory to the in-memory list of alternates (along with any
 + * recursive alternates it points to), but do not modify the on-disk alternates
 + * file.
 + */
 +extern void add_to_alternates_memory(const char *dir);
 +
 +/*
 + * Returns a scratch strbuf pre-filled with the alternate object directory,
 + * including a trailing slash, which can be used to access paths in the
 + * alternate. Always use this over direct access to alt->scratch, as it
 + * cleans up any previous use of the scratch buffer.
 + */
 +extern struct strbuf *alt_scratch_buf(struct alternate_object_database *alt);
 +
  struct pack_window {
        struct pack_window *next;
        unsigned char *base;
@@@ -1636,15 -1602,7 +1640,15 @@@ struct object_info 
                } packed;
        } u;
  };
 +
 +/*
 + * Initializer for a "struct object_info" that wants no items. You may
 + * also memset() the memory to all-zeroes.
 + */
 +#define OBJECT_INFO_INIT {NULL}
 +
  extern int sha1_object_info_extended(const unsigned char *, struct object_info *, unsigned flags);
 +extern int packed_object_info(struct packed_git *pack, off_t offset, struct object_info *);
  
  /* Dumb servers support */
  extern int update_server_info(int);
@@@ -1911,7 -1869,7 +1915,7 @@@ 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 force_mode);
 +int add_files_to_cache(const char *prefix, const struct pathspec *pathspec, int flags);
  
  /* diff.c */
  extern int diff_auto_refresh_index;
diff --combined diff.c
index ae87888d1fe9500ed7a6d26fc2ec8031cfc27aee,cefc13eb8e811a5a5021fcd9573d5fd3c9a37bae..5a4e9b31efd5ba6ab43e715effe4d97b13ef2a07
--- 1/diff.c
--- 2/diff.c
+++ b/diff.c
@@@ -27,7 -27,6 +27,7 @@@
  #endif
  
  static int diff_detect_rename_default;
 +static int diff_indent_heuristic; /* experimental */
  static int diff_compaction_heuristic; /* experimental */
  static int diff_rename_limit_default = 400;
  static int diff_suppress_blank_empty;
@@@ -43,7 -42,6 +43,7 @@@ static int diff_stat_graph_width
  static int diff_dirstat_permille_default = 30;
  static struct diff_options default_diff_options;
  static long diff_algorithm;
 +static unsigned ws_error_highlight_default = WSEH_NEW;
  
  static char diff_colors[][COLOR_MAXLEN] = {
        GIT_COLOR_RESET,
        GIT_COLOR_NORMAL,       /* FUNCINFO */
  };
  
 +static NORETURN void die_want_option(const char *option_name)
 +{
 +      die(_("option '%s' requires a value"), option_name);
 +}
 +
  static int parse_diff_color_slot(const char *var)
  {
        if (!strcasecmp(var, "context") || !strcasecmp(var, "plain"))
@@@ -173,43 -166,6 +173,43 @@@ long parse_algorithm_value(const char *
        return -1;
  }
  
 +static int parse_one_token(const char **arg, const char *token)
 +{
 +      const char *rest;
 +      if (skip_prefix(*arg, token, &rest) && (!*rest || *rest == ',')) {
 +              *arg = rest;
 +              return 1;
 +      }
 +      return 0;
 +}
 +
 +static int parse_ws_error_highlight(const char *arg)
 +{
 +      const char *orig_arg = arg;
 +      unsigned val = 0;
 +
 +      while (*arg) {
 +              if (parse_one_token(&arg, "none"))
 +                      val = 0;
 +              else if (parse_one_token(&arg, "default"))
 +                      val = WSEH_NEW;
 +              else if (parse_one_token(&arg, "all"))
 +                      val = WSEH_NEW | WSEH_OLD | WSEH_CONTEXT;
 +              else if (parse_one_token(&arg, "new"))
 +                      val |= WSEH_NEW;
 +              else if (parse_one_token(&arg, "old"))
 +                      val |= WSEH_OLD;
 +              else if (parse_one_token(&arg, "context"))
 +                      val |= WSEH_CONTEXT;
 +              else {
 +                      return -1 - (int)(arg - orig_arg);
 +              }
 +              if (*arg)
 +                      arg++;
 +      }
 +      return val;
 +}
 +
  /*
   * These are to give UI layer defaults.
   * The core-level commands such as git-diff-files should
@@@ -221,21 -177,6 +221,21 @@@ void init_diff_ui_defaults(void
        diff_detect_rename_default = 1;
  }
  
 +int git_diff_heuristic_config(const char *var, const char *value, void *cb)
 +{
 +      if (!strcmp(var, "diff.indentheuristic")) {
 +              diff_indent_heuristic = git_config_bool(var, value);
 +              if (diff_indent_heuristic)
 +                      diff_compaction_heuristic = 0;
 +      }
 +      if (!strcmp(var, "diff.compactionheuristic")) {
 +              diff_compaction_heuristic = git_config_bool(var, value);
 +              if (diff_compaction_heuristic)
 +                      diff_indent_heuristic = 0;
 +      }
 +      return 0;
 +}
 +
  int git_diff_ui_config(const char *var, const char *value, void *cb)
  {
        if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) {
                diff_detect_rename_default = git_config_rename(var, value);
                return 0;
        }
 -      if (!strcmp(var, "diff.compactionheuristic")) {
 -              diff_compaction_heuristic = git_config_bool(var, value);
 -              return 0;
 -      }
        if (!strcmp(var, "diff.autorefreshindex")) {
                diff_auto_refresh_index = git_config_bool(var, value);
                return 0;
                return 0;
        }
  
 +      if (git_diff_heuristic_config(var, value, cb) < 0)
 +              return -1;
 +
 +      if (!strcmp(var, "diff.wserrorhighlight")) {
 +              int val = parse_ws_error_highlight(value);
 +              if (val < 0)
 +                      return -1;
 +              ws_error_highlight_default = val;
 +              return 0;
 +      }
 +
        if (git_color_config(var, value, cb) < 0)
                return -1;
  
@@@ -1018,8 -952,7 +1018,8 @@@ static int find_word_boundaries(mmfile_
  {
        if (word_regex && *begin < buffer->size) {
                regmatch_t match[1];
 -              if (!regexec(word_regex, buffer->ptr + *begin, 1, match, 0)) {
 +              if (!regexec_buf(word_regex, buffer->ptr + *begin,
 +                               buffer->size - *begin, 1, match, 0)) {
                        char *p = memchr(buffer->ptr + *begin + match[0].rm_so,
                                        '\n', match[0].rm_eo - match[0].rm_so);
                        *end = p ? p - buffer->ptr : match[0].rm_eo + *begin;
@@@ -2066,7 -1999,7 +2066,7 @@@ found_damage
                return;
  
        /* Show all directories with more than x% of the changes */
 -      qsort(dir.files, dir.nr, sizeof(dir.files[0]), dirstat_compare);
 +      QSORT(dir.files, dir.nr, dirstat_compare);
        gather_dirstat(options, &dir, changed, "", 0);
  }
  
@@@ -2110,7 -2043,7 +2110,7 @@@ static void show_dirstat_by_line(struc
                return;
  
        /* Show all directories with more than x% of the changes */
 -      qsort(dir.files, dir.nr, sizeof(dir.files[0]), dirstat_compare);
 +      QSORT(dir.files, dir.nr, dirstat_compare);
        gather_dirstat(options, &dir, changed, "", 0);
  }
  
@@@ -3156,7 -3089,7 +3156,7 @@@ static void fill_metainfo(struct strbu
                }
                strbuf_addf(msg, "%s%sindex %s..", line_prefix, set,
                            find_unique_abbrev(one->oid.hash, abbrev));
 -              strbuf_addstr(msg, find_unique_abbrev(two->oid.hash, abbrev));
 +              strbuf_add_unique_abbrev(msg, two->oid.hash, abbrev);
                if (one->mode == two->mode)
                        strbuf_addf(msg, " %06o", one->mode);
                strbuf_addf(msg, "%s\n", reset);
@@@ -3354,7 -3287,7 +3354,7 @@@ void diff_setup(struct diff_options *op
        options->rename_limit = -1;
        options->dirstat_permille = diff_dirstat_permille_default;
        options->context = diff_context_default;
 -      options->ws_error_highlight = WSEH_NEW;
 +      options->ws_error_highlight = ws_error_highlight_default;
        DIFF_OPT_SET(options, RENAME_EMPTY);
  
        /* pathchange left =NULL by default */
        options->use_color = diff_use_color_default;
        options->detect_rename = diff_detect_rename_default;
        options->xdl_opts |= diff_algorithm;
 -      if (diff_compaction_heuristic)
 +      if (diff_indent_heuristic)
 +              DIFF_XDL_SET(options, INDENT_HEURISTIC);
 +      else if (diff_compaction_heuristic)
                DIFF_XDL_SET(options, COMPACTION_HEURISTIC);
  
        options->orderfile = diff_order_file_cfg;
@@@ -3394,7 -3325,7 +3394,7 @@@ void diff_setup_done(struct diff_option
        if (options->output_format & DIFF_FORMAT_NO_OUTPUT)
                count++;
        if (count > 1)
 -              die("--name-only, --name-status, --check and -s are mutually exclusive");
 +              die(_("--name-only, --name-status, --check and -s are mutually exclusive"));
  
        /*
         * Most of the time we can say "there are changes"
                         */
                        read_cache();
        }
-       if (options->abbrev <= 0 || 40 < options->abbrev)
+       if (40 < options->abbrev)
                options->abbrev = 40; /* full */
  
        /*
@@@ -3590,7 -3521,7 +3590,7 @@@ static int stat_opt(struct diff_option
                        if (*arg == '=')
                                width = strtoul(arg + 1, &end, 10);
                        else if (!*arg && !av[1])
 -                              die("Option '--stat-width' requires a value");
 +                              die_want_option("--stat-width");
                        else if (!*arg) {
                                width = strtoul(av[1], &end, 10);
                                argcount = 2;
                        if (*arg == '=')
                                name_width = strtoul(arg + 1, &end, 10);
                        else if (!*arg && !av[1])
 -                              die("Option '--stat-name-width' requires a value");
 +                              die_want_option("--stat-name-width");
                        else if (!*arg) {
                                name_width = strtoul(av[1], &end, 10);
                                argcount = 2;
                        if (*arg == '=')
                                graph_width = strtoul(arg + 1, &end, 10);
                        else if (!*arg && !av[1])
 -                              die("Option '--stat-graph-width' requires a value");
 +                              die_want_option("--stat-graph-width");
                        else if (!*arg) {
                                graph_width = strtoul(av[1], &end, 10);
                                argcount = 2;
                        if (*arg == '=')
                                count = strtoul(arg + 1, &end, 10);
                        else if (!*arg && !av[1])
 -                              die("Option '--stat-count' requires a value");
 +                              die_want_option("--stat-count");
                        else if (!*arg) {
                                count = strtoul(av[1], &end, 10);
                                argcount = 2;
@@@ -3745,14 -3676,40 +3745,14 @@@ static void enable_patch_output(int *fm
        *fmt |= DIFF_FORMAT_PATCH;
  }
  
 -static int parse_one_token(const char **arg, const char *token)
 +static int parse_ws_error_highlight_opt(struct diff_options *opt, const char *arg)
  {
 -      const char *rest;
 -      if (skip_prefix(*arg, token, &rest) && (!*rest || *rest == ',')) {
 -              *arg = rest;
 -              return 1;
 -      }
 -      return 0;
 -}
 +      int val = parse_ws_error_highlight(arg);
  
 -static int parse_ws_error_highlight(struct diff_options *opt, const char *arg)
 -{
 -      const char *orig_arg = arg;
 -      unsigned val = 0;
 -      while (*arg) {
 -              if (parse_one_token(&arg, "none"))
 -                      val = 0;
 -              else if (parse_one_token(&arg, "default"))
 -                      val = WSEH_NEW;
 -              else if (parse_one_token(&arg, "all"))
 -                      val = WSEH_NEW | WSEH_OLD | WSEH_CONTEXT;
 -              else if (parse_one_token(&arg, "new"))
 -                      val |= WSEH_NEW;
 -              else if (parse_one_token(&arg, "old"))
 -                      val |= WSEH_OLD;
 -              else if (parse_one_token(&arg, "context"))
 -                      val |= WSEH_CONTEXT;
 -              else {
 -                      error("unknown value after ws-error-highlight=%.*s",
 -                            (int)(arg - orig_arg), orig_arg);
 -                      return 0;
 -              }
 -              if (*arg)
 -                      arg++;
 +      if (val < 0) {
 +              error("unknown value after ws-error-highlight=%.*s",
 +                    -1 - val, arg);
 +              return 0;
        }
        opt->ws_error_highlight = val;
        return 1;
@@@ -3861,15 -3818,9 +3861,15 @@@ int diff_opt_parse(struct diff_options 
                DIFF_XDL_SET(options, IGNORE_WHITESPACE_AT_EOL);
        else if (!strcmp(arg, "--ignore-blank-lines"))
                DIFF_XDL_SET(options, IGNORE_BLANK_LINES);
 -      else if (!strcmp(arg, "--compaction-heuristic"))
 +      else if (!strcmp(arg, "--indent-heuristic")) {
 +              DIFF_XDL_SET(options, INDENT_HEURISTIC);
 +              DIFF_XDL_CLR(options, COMPACTION_HEURISTIC);
 +      } else if (!strcmp(arg, "--no-indent-heuristic"))
 +              DIFF_XDL_CLR(options, INDENT_HEURISTIC);
 +      else if (!strcmp(arg, "--compaction-heuristic")) {
                DIFF_XDL_SET(options, COMPACTION_HEURISTIC);
 -      else if (!strcmp(arg, "--no-compaction-heuristic"))
 +              DIFF_XDL_CLR(options, INDENT_HEURISTIC);
 +      } else if (!strcmp(arg, "--no-compaction-heuristic"))
                DIFF_XDL_CLR(options, COMPACTION_HEURISTIC);
        else if (!strcmp(arg, "--patience"))
                options->xdl_opts = DIFF_WITH_ALG(options, PATIENCE_DIFF);
        else if (skip_prefix(arg, "--submodule=", &arg))
                return parse_submodule_opt(options, arg);
        else if (skip_prefix(arg, "--ws-error-highlight=", &arg))
 -              return parse_ws_error_highlight(options, arg);
 +              return parse_ws_error_highlight_opt(options, arg);
  
        /* misc options */
        else if (!strcmp(arg, "-z"))
@@@ -4157,8 -4108,7 +4157,8 @@@ void diff_free_filepair(struct diff_fil
        free(p);
  }
  
 -/* This is different from find_unique_abbrev() in that
 +/*
 + * This is different from find_unique_abbrev() in that
   * it stuffs the result with dots for alignment.
   */
  const char *diff_unique_abbrev(const unsigned char *sha1, int len)
  
        abbrev = find_unique_abbrev(sha1, len);
        abblen = strlen(abbrev);
 +
 +      /*
 +       * In well-behaved cases, where the abbbreviated result is the
 +       * same as the requested length, append three dots after the
 +       * abbreviation (hence the whole logic is limited to the case
 +       * where abblen < 37); when the actual abbreviated result is a
 +       * bit longer than the requested length, we reduce the number
 +       * of dots so that they match the well-behaved ones.  However,
 +       * if the actual abbreviation is longer than the requested
 +       * length by more than three, we give up on aligning, and add
 +       * three dots anyway, to indicate that the output is not the
 +       * full object name.  Yes, this may be suboptimal, but this
 +       * appears only in "diff --raw --abbrev" output and it is not
 +       * worth the effort to change it now.  Note that this would
 +       * likely to work fine when the automatic sizing of default
 +       * abbreviation length is used--we would be fed -1 in "len" in
 +       * that case, and will end up always appending three-dots, but
 +       * the automatic sizing is supposed to give abblen that ensures
 +       * uniqueness across all objects (statistically speaking).
 +       */
        if (abblen < 37) {
                static char hex[41];
                if (len < abblen && abblen <= len + 2)
@@@ -4680,25 -4610,25 +4680,25 @@@ static int is_summary_empty(const struc
  }
  
  static const char rename_limit_warning[] =
 -"inexact rename detection was skipped due to too many files.";
 +N_("inexact rename detection was skipped due to too many files.");
  
  static const char degrade_cc_to_c_warning[] =
 -"only found copies from modified paths due to too many files.";
 +N_("only found copies from modified paths due to too many files.");
  
  static const char rename_limit_advice[] =
 -"you may want to set your %s variable to at least "
 -"%d and retry the command.";
 +N_("you may want to set your %s variable to at least "
 +   "%d and retry the command.");
  
  void diff_warn_rename_limit(const char *varname, int needed, int degraded_cc)
  {
        if (degraded_cc)
 -              warning(degrade_cc_to_c_warning);
 +              warning(_(degrade_cc_to_c_warning));
        else if (needed)
 -              warning(rename_limit_warning);
 +              warning(_(rename_limit_warning));
        else
                return;
        if (0 < needed && needed < 32767)
 -              warning(rename_limit_advice, varname, needed);
 +              warning(_(rename_limit_advice), varname, needed);
  }
  
  void diff_flush(struct diff_options *options)
@@@ -4965,7 -4895,7 +4965,7 @@@ static int diffnamecmp(const void *a_, 
  void diffcore_fix_diff_index(struct diff_options *options)
  {
        struct diff_queue_struct *q = &diff_queued_diff;
 -      qsort(q->queue, q->nr, sizeof(q->queue[0]), diffnamecmp);
 +      QSORT(q->queue, q->nr, diffnamecmp);
  }
  
  void diffcore_std(struct diff_options *options)
diff --combined environment.c
index cdc097f80c4b876818fbe28dc1cc1a54cde68902,6f9d2905634359d2ebf5d8835e7d9f09920968e8..0935ec696e530e1b610c71eda0a0a7072d1d656f
@@@ -16,7 -16,7 +16,7 @@@ int trust_executable_bit = 1
  int trust_ctime = 1;
  int check_stat = 1;
  int has_symlinks = 1;
- int minimum_abbrev = 4, default_abbrev = 7;
+ int minimum_abbrev = 4, default_abbrev = -1;
  int ignore_case;
  int assume_unchanged;
  int prefer_symlink_refs;
@@@ -99,8 -99,6 +99,8 @@@ static char *work_tree
  static const char *namespace;
  static size_t namespace_len;
  
 +static const char *super_prefix;
 +
  static const char *git_dir, *git_common_dir;
  static char *git_object_dir, *git_index_file, *git_graft_file;
  int git_db_env, git_index_env, git_graft_env, git_common_dir_env;
@@@ -121,7 -119,6 +121,7 @@@ const char * const local_repo_env[] = 
        NO_REPLACE_OBJECTS_ENVIRONMENT,
        GIT_REPLACE_REF_BASE_ENVIRONMENT,
        GIT_PREFIX_ENVIRONMENT,
 +      GIT_SUPER_PREFIX_ENVIRONMENT,
        GIT_SHALLOW_FILE_ENVIRONMENT,
        GIT_COMMON_DIR_ENVIRONMENT,
        NULL
@@@ -231,16 -228,6 +231,16 @@@ const char *strip_namespace(const char 
        return namespaced_ref + namespace_len;
  }
  
 +const char *get_super_prefix(void)
 +{
 +      static int initialized;
 +      if (!initialized) {
 +              super_prefix = getenv(GIT_SUPER_PREFIX_ENVIRONMENT);
 +              initialized = 1;
 +      }
 +      return super_prefix;
 +}
 +
  static int git_work_tree_initialized;
  
  /*
diff --combined sha1_name.c
index 409283614679b625f1cb6d191f1a2a32423bbcda,beb7ab588b4da529080301576447762a17e4da11..84662a68041bed3f92b04a6c17d984104b3cf7b9
@@@ -15,6 -15,7 +15,7 @@@ typedef int (*disambiguate_hint_fn)(con
  
  struct disambiguate_state {
        int len; /* length of prefix in hex chars */
+       unsigned int nrobjects;
        char hex_pfx[GIT_SHA1_HEXSZ + 1];
        unsigned char bin_pfx[GIT_SHA1_RAWSZ];
  
@@@ -91,18 -92,25 +92,18 @@@ static void find_short_object_filename(
                 * alt->name/alt->base while iterating over the
                 * object databases including our own.
                 */
 -              const char *objdir = get_object_directory();
 -              size_t objdir_len = strlen(objdir);
 -              fakeent = xmalloc(st_add3(sizeof(*fakeent), objdir_len, 43));
 -              memcpy(fakeent->base, objdir, objdir_len);
 -              fakeent->name = fakeent->base + objdir_len + 1;
 -              fakeent->name[-1] = '/';
 +              fakeent = alloc_alt_odb(get_object_directory());
        }
        fakeent->next = alt_odb_list;
  
        xsnprintf(hex, sizeof(hex), "%.2s", ds->hex_pfx);
        for (alt = fakeent; alt && !ds->ambiguous; alt = alt->next) {
 +              struct strbuf *buf = alt_scratch_buf(alt);
                struct dirent *de;
                DIR *dir;
 -              /*
 -               * every alt_odb struct has 42 extra bytes after the base
 -               * for exactly this purpose
 -               */
 -              xsnprintf(alt->name, 42, "%.2s/", ds->hex_pfx);
 -              dir = opendir(alt->base);
 +
 +              strbuf_addf(buf, "%.2s/", ds->hex_pfx);
 +              dir = opendir(buf->buf);
                if (!dir)
                        continue;
  
  
                        if (strlen(de->d_name) != 38)
                                continue;
+                       /*
+                        * We only look at the one subdirectory, and we assume
+                        * each subdirectory is roughly similar, so each
+                        * object we find probably has 255 other objects in
+                        * the other fan-out directories.
+                        */
+                       ds->nrobjects += 256;
                        if (memcmp(de->d_name, ds->hex_pfx + 2, ds->len - 2))
                                continue;
                        memcpy(hex + 2, de->d_name, 38);
@@@ -144,6 -160,7 +153,7 @@@ static void unique_in_pack(struct packe
  
        open_pack_index(p);
        num = p->num_objects;
+       ds->nrobjects += num;
        last = num;
        while (first < last) {
                uint32_t mid = (first + last) / 2;
@@@ -373,6 -390,9 +383,9 @@@ static int show_ambiguous_object(const 
        return 0;
  }
  
+ /* start from our historical default before the automatic abbreviation */
+ static int default_automatic_abbrev = FALLBACK_DEFAULT_ABBREV;
  static int get_short_sha1(const char *name, int len, unsigned char *sha1,
                          unsigned flags)
  {
                for_each_abbrev(ds.hex_pfx, show_ambiguous_object, &ds);
        }
  
+       if (len < 16 && !status && (flags & GET_SHA1_AUTOMATIC)) {
+               unsigned int expect_collision = 1 << (len * 2);
+               if (ds.nrobjects > expect_collision) {
+                       default_automatic_abbrev = len+1;
+                       return SHORT_NAME_AMBIGUOUS;
+               }
+       }
        return status;
  }
  
@@@ -451,14 -479,19 +472,19 @@@ int for_each_abbrev(const char *prefix
  int find_unique_abbrev_r(char *hex, const unsigned char *sha1, int len)
  {
        int status, exists;
+       int flags = GET_SHA1_QUIETLY;
  
+       if (len < 0) {
+               flags |= GET_SHA1_AUTOMATIC;
+               len = default_automatic_abbrev;
+       }
        sha1_to_hex_r(hex, sha1);
        if (len == 40 || !len)
                return 40;
        exists = has_sha1_file(sha1);
        while (len < 40) {
                unsigned char sha1_ret[20];
-               status = get_short_sha1(hex, len, sha1_ret, GET_SHA1_QUIETLY);
+               status = get_short_sha1(hex, len, sha1_ret, flags);
                if (exists
                    ? !status
                    : status == SHORT_NAME_NOT_FOUND) {
diff --combined transport.h
index 68669f14d08f52d15f476fe97a813044a0d07dda,e783377e4004998e585182a015ba67c9db4b1575..5624c0218c5d8175c3187d1c89cc603cd3f1d2cf
@@@ -5,8 -5,6 +5,8 @@@
  #include "run-command.h"
  #include "remote.h"
  
 +struct string_list;
 +
  struct git_transport_options {
        unsigned thin : 1;
        unsigned keep : 1;
        unsigned check_self_contained_and_connected : 1;
        unsigned self_contained_and_connected : 1;
        unsigned update_shallow : 1;
 +      unsigned deepen_relative : 1;
        int depth;
 +      const char *deepen_since;
 +      const struct string_list *deepen_not;
        const char *uploadpack;
        const char *receivepack;
        struct push_cas_option *cas;
@@@ -147,8 -142,7 +147,7 @@@ struct transport 
  #define TRANSPORT_PUSH_ATOMIC 8192
  #define TRANSPORT_PUSH_OPTIONS 16384
  
- #define TRANSPORT_SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3)
- #define TRANSPORT_SUMMARY(x) (int)(TRANSPORT_SUMMARY_WIDTH + strlen(x) - gettext_width(x)), (x)
+ #define TRANSPORT_SUMMARY_WIDTH (2 * FALLBACK_DEFAULT_ABBREV + 3)
  
  /* Returns a transport suitable for the url */
  struct transport *transport_get(struct remote *, const char *);
@@@ -191,15 -185,6 +190,15 @@@ int transport_restrict_protocols(void)
  /* Limit the depth of the fetch if not null */
  #define TRANS_OPT_DEPTH "depth"
  
 +/* Limit the depth of the fetch based on time if not null */
 +#define TRANS_OPT_DEEPEN_SINCE "deepen-since"
 +
 +/* Limit the depth of the fetch based on revs if not null */
 +#define TRANS_OPT_DEEPEN_NOT "deepen-not"
 +
 +/* Limit the deepen of the fetch if not null */
 +#define TRANS_OPT_DEEPEN_RELATIVE "deepen-relative"
 +
  /* Aggressively fetch annotated tags if possible */
  #define TRANS_OPT_FOLLOWTAGS "followtags"