Merge branch 'js/config-cb'
authorJunio C Hamano <gitster@pobox.com>
Sun, 25 May 2008 21:25:02 +0000 (14:25 -0700)
committerJunio C Hamano <gitster@pobox.com>
Sun, 25 May 2008 21:25:02 +0000 (14:25 -0700)
* js/config-cb:
Provide git_config with a callback-data parameter

Conflicts:

builtin-add.c
builtin-cat-file.c

25 files changed:
1  2 
builtin-add.c
builtin-apply.c
builtin-cat-file.c
builtin-checkout.c
builtin-clone.c
builtin-commit.c
builtin-gc.c
builtin-init-db.c
builtin-log.c
builtin-mailinfo.c
builtin-merge-recursive.c
builtin-mv.c
builtin-pack-objects.c
builtin-rev-list.c
builtin-rev-parse.c
builtin-update-index.c
cache.h
config.c
diff.c
diff.h
fast-import.c
hash-object.c
remote.c
wt-status.c
wt-status.h
diff --combined builtin-add.c
index 6e4e645cb7ba1624825fac9ff3f1d9bce5e93689,15def7dada1b7266d0d6fe44da3f9c722f402463..1da22eec915539f06a4dfc1e4bb1d18482de0ede
@@@ -79,18 -79,12 +79,18 @@@ static void fill_directory(struct dir_s
                prune_directory(dir, pathspec, baselen);
  }
  
 +struct update_callback_data
 +{
 +      int flags;
 +      int add_errors;
 +};
 +
  static void update_callback(struct diff_queue_struct *q,
                            struct diff_options *opt, void *cbdata)
  {
 -      int i, verbose;
 +      int i;
 +      struct update_callback_data *data = cbdata;
  
 -      verbose = *((int *)cbdata);
        for (i = 0; i < q->nr; i++) {
                struct diff_filepair *p = q->queue[i];
                const char *path = p->one->path;
                case DIFF_STATUS_UNMERGED:
                case DIFF_STATUS_MODIFIED:
                case DIFF_STATUS_TYPE_CHANGED:
 -                      add_file_to_cache(path, verbose);
 +                      if (add_file_to_cache(path, data->flags)) {
 +                              if (!(data->flags & ADD_CACHE_IGNORE_ERRORS))
 +                                      die("updating files failed");
 +                              data->add_errors++;
 +                      }
                        break;
                case DIFF_STATUS_DELETED:
 -                      remove_file_from_cache(path);
 -                      if (verbose)
 +                      if (!(data->flags & ADD_CACHE_PRETEND))
 +                              remove_file_from_cache(path);
 +                      if (data->flags & (ADD_CACHE_PRETEND|ADD_CACHE_VERBOSE))
                                printf("remove '%s'\n", path);
                        break;
                }
        }
  }
  
 -void add_files_to_cache(int verbose, const char *prefix, const char **pathspec)
 +int add_files_to_cache(const char *prefix, const char **pathspec, int flags)
  {
 +      struct update_callback_data data;
        struct rev_info rev;
        init_revisions(&rev, prefix);
        setup_revisions(0, NULL, &rev, NULL);
        rev.prune_data = pathspec;
        rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
        rev.diffopt.format_callback = update_callback;
 -      rev.diffopt.format_callback_data = &verbose;
 +      data.flags = flags;
 +      data.add_errors = 0;
 +      rev.diffopt.format_callback_data = &data;
        run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
 +      return !!data.add_errors;
  }
  
  static void refresh(int verbose, const char **pathspec)
@@@ -192,7 -177,6 +192,7 @@@ static const char ignore_error[] 
  "The following paths are ignored by one of your .gitignore files:\n";
  
  static int verbose = 0, show_only = 0, ignored_too = 0, refresh_only = 0;
 +static int ignore_add_errors;
  
  static struct option builtin_add_options[] = {
        OPT__DRY_RUN(&show_only),
        OPT_BOOLEAN('f', NULL, &ignored_too, "allow adding otherwise ignored files"),
        OPT_BOOLEAN('u', NULL, &take_worktree_changes, "update tracked files"),
        OPT_BOOLEAN( 0 , "refresh", &refresh_only, "don't add, only refresh the index"),
 +      OPT_BOOLEAN( 0 , "ignore-errors", &ignore_add_errors, "just skip files which cannot be added because of errors"),
        OPT_END(),
  };
  
- static int add_config(const char *var, const char *value)
++static int add_config(const char *var, const char *value, void *cb)
 +{
 +      if (!strcasecmp(var, "add.ignore-errors")) {
 +              ignore_add_errors = git_config_bool(var, value);
 +              return 0;
 +      }
-       return git_default_config(var, value);
++      return git_default_config(var, value, cb);
 +}
 +
  int cmd_add(int argc, const char **argv, const char *prefix)
  {
 +      int exit_status = 0;
        int i, newfd;
        const char **pathspec;
        struct dir_struct dir;
 +      int flags;
  
        argc = parse_options(argc, argv, builtin_add_options,
                          builtin_add_usage, 0);
        if (add_interactive)
                exit(interactive_add(argc, argv, prefix));
  
-       git_config(add_config);
 -      git_config(git_default_config, NULL);
++      git_config(add_config, NULL);
  
        newfd = hold_locked_index(&lock_file, 1);
  
 +      flags = ((verbose ? ADD_CACHE_VERBOSE : 0) |
 +               (show_only ? ADD_CACHE_PRETEND : 0) |
 +               (ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0));
 +
        if (take_worktree_changes) {
                const char **pathspec;
                if (read_cache() < 0)
                        die("index file corrupt");
                pathspec = get_pathspec(prefix, argv);
 -              add_files_to_cache(verbose, prefix, pathspec);
 +              exit_status = add_files_to_cache(prefix, pathspec, flags);
                goto finish;
        }
  
  
        fill_directory(&dir, pathspec, ignored_too);
  
 -      if (show_only) {
 -              const char *sep = "", *eof = "";
 -              for (i = 0; i < dir.nr; i++) {
 -                      printf("%s%s", sep, dir.entries[i]->name);
 -                      sep = " ";
 -                      eof = "\n";
 -              }
 -              fputs(eof, stdout);
 -              return 0;
 -      }
 -
        if (read_cache() < 0)
                die("index file corrupt");
  
        }
  
        for (i = 0; i < dir.nr; i++)
 -              add_file_to_cache(dir.entries[i]->name, verbose);
 +              if (add_file_to_cache(dir.entries[i]->name, flags)) {
 +                      if (!ignore_add_errors)
 +                              die("adding files failed");
 +                      exit_status = 1;
 +              }
  
   finish:
        if (active_cache_changed) {
                        die("Unable to write new index file");
        }
  
 -      return 0;
 +      return exit_status;
  }
diff --combined builtin-apply.c
index 1540f28ab4acc7794796389237bcfeee1075b932,bbdf08a10a35b9c6263cb1e899381d40d7b51e57..c4978893122bbcfd80201fe937eb8433b29e1aa0
@@@ -418,7 -418,7 +418,7 @@@ static int guess_p_value(const char *na
  }
  
  /*
 - * Get the name etc info from the --/+++ lines of a traditional patch header
 + * Get the name etc info from the ---/+++ lines of a traditional patch header
   *
   * FIXME! The end-of-filename heuristics are kind of screwy. For existing
   * files, we can happily check the index for a match, but for creating a
@@@ -1143,6 -1143,21 +1143,6 @@@ static int parse_single_patch(char *lin
        if (patch->is_delete < 0 &&
            (newlines || (patch->fragments && patch->fragments->next)))
                patch->is_delete = 0;
 -      if (!unidiff_zero || context) {
 -              /* If the user says the patch is not generated with
 -               * --unified=0, or if we have seen context lines,
 -               * then not having oldlines means the patch is creation,
 -               * and not having newlines means the patch is deletion.
 -               */
 -              if (patch->is_new < 0 && !oldlines) {
 -                      patch->is_new = 1;
 -                      patch->old_name = NULL;
 -              }
 -              if (patch->is_delete < 0 && !newlines) {
 -                      patch->is_delete = 1;
 -                      patch->new_name = NULL;
 -              }
 -      }
  
        if (0 < patch->is_new && oldlines)
                die("new file %s depends on old contents", patch->new_name);
@@@ -2252,11 -2267,16 +2252,11 @@@ static int verify_index_match(struct ca
        return ce_match_stat(ce, st, CE_MATCH_IGNORE_VALID);
  }
  
 -static int check_patch(struct patch *patch, struct patch *prev_patch)
 +static int check_preimage(struct patch *patch, struct cache_entry **ce, struct stat *st)
  {
 -      struct stat st;
        const char *old_name = patch->old_name;
 -      const char *new_name = patch->new_name;
 -      const char *name = old_name ? old_name : new_name;
 -      struct cache_entry *ce = NULL;
 -      int ok_if_exists;
 -
 -      patch->rejected = 1; /* we will drop this after we succeed */
 +      int stat_ret = 0;
 +      unsigned st_mode = 0;
  
        /*
         * Make sure that we do not have local modifications from the
         * we have the preimage file to be patched in the work tree,
         * unless --cached, which tells git to apply only in the index.
         */
 -      if (old_name) {
 -              int stat_ret = 0;
 -              unsigned st_mode = 0;
 -
 -              if (!cached)
 -                      stat_ret = lstat(old_name, &st);
 -              if (check_index) {
 -                      int pos = cache_name_pos(old_name, strlen(old_name));
 -                      if (pos < 0)
 -                              return error("%s: does not exist in index",
 -                                           old_name);
 -                      ce = active_cache[pos];
 -                      if (stat_ret < 0) {
 -                              struct checkout costate;
 -                              if (errno != ENOENT)
 -                                      return error("%s: %s", old_name,
 -                                                   strerror(errno));
 -                              /* checkout */
 -                              costate.base_dir = "";
 -                              costate.base_dir_len = 0;
 -                              costate.force = 0;
 -                              costate.quiet = 0;
 -                              costate.not_new = 0;
 -                              costate.refresh_cache = 1;
 -                              if (checkout_entry(ce,
 -                                                 &costate,
 -                                                 NULL) ||
 -                                  lstat(old_name, &st))
 -                                      return -1;
 -                      }
 -                      if (!cached && verify_index_match(ce, &st))
 -                              return error("%s: does not match index",
 -                                           old_name);
 -                      if (cached)
 -                              st_mode = ce->ce_mode;
 -              } else if (stat_ret < 0)
 -                      return error("%s: %s", old_name, strerror(errno));
 -
 -              if (!cached)
 -                      st_mode = ce_mode_from_stat(ce, st.st_mode);
 +      if (!old_name)
 +              return 0;
  
 +      assert(patch->is_new <= 0);
 +      if (!cached) {
 +              stat_ret = lstat(old_name, st);
 +              if (stat_ret && errno != ENOENT)
 +                      return error("%s: %s", old_name, strerror(errno));
 +      }
 +      if (check_index) {
 +              int pos = cache_name_pos(old_name, strlen(old_name));
 +              if (pos < 0) {
 +                      if (patch->is_new < 0)
 +                              goto is_new;
 +                      return error("%s: does not exist in index", old_name);
 +              }
 +              *ce = active_cache[pos];
 +              if (stat_ret < 0) {
 +                      struct checkout costate;
 +                      /* checkout */
 +                      costate.base_dir = "";
 +                      costate.base_dir_len = 0;
 +                      costate.force = 0;
 +                      costate.quiet = 0;
 +                      costate.not_new = 0;
 +                      costate.refresh_cache = 1;
 +                      if (checkout_entry(*ce, &costate, NULL) ||
 +                          lstat(old_name, st))
 +                              return -1;
 +              }
 +              if (!cached && verify_index_match(*ce, st))
 +                      return error("%s: does not match index", old_name);
 +              if (cached)
 +                      st_mode = (*ce)->ce_mode;
 +      } else if (stat_ret < 0) {
                if (patch->is_new < 0)
 -                      patch->is_new = 0;
 -              if (!patch->old_mode)
 -                      patch->old_mode = st_mode;
 -              if ((st_mode ^ patch->old_mode) & S_IFMT)
 -                      return error("%s: wrong type", old_name);
 -              if (st_mode != patch->old_mode)
 -                      fprintf(stderr, "warning: %s has type %o, expected %o\n",
 -                              old_name, st_mode, patch->old_mode);
 +                      goto is_new;
 +              return error("%s: %s", old_name, strerror(errno));
        }
  
 +      if (!cached)
 +              st_mode = ce_mode_from_stat(*ce, st->st_mode);
 +
 +      if (patch->is_new < 0)
 +              patch->is_new = 0;
 +      if (!patch->old_mode)
 +              patch->old_mode = st_mode;
 +      if ((st_mode ^ patch->old_mode) & S_IFMT)
 +              return error("%s: wrong type", old_name);
 +      if (st_mode != patch->old_mode)
 +              fprintf(stderr, "warning: %s has type %o, expected %o\n",
 +                      old_name, st_mode, patch->old_mode);
 +      return 0;
 +
 + is_new:
 +      patch->is_new = 1;
 +      patch->is_delete = 0;
 +      patch->old_name = NULL;
 +      return 0;
 +}
 +
 +static int check_patch(struct patch *patch, struct patch *prev_patch)
 +{
 +      struct stat st;
 +      const char *old_name = patch->old_name;
 +      const char *new_name = patch->new_name;
 +      const char *name = old_name ? old_name : new_name;
 +      struct cache_entry *ce = NULL;
 +      int ok_if_exists;
 +      int status;
 +
 +      patch->rejected = 1; /* we will drop this after we succeed */
 +
 +      status = check_preimage(patch, &ce, &st);
 +      if (status)
 +              return status;
 +      old_name = patch->old_name;
 +
        if (new_name && prev_patch && 0 < prev_patch->is_delete &&
            !strcmp(prev_patch->old_name, new_name))
                /*
@@@ -2985,11 -2979,11 +2985,11 @@@ static int apply_patch(int fd, const ch
        return 0;
  }
  
- static int git_apply_config(const char *var, const char *value)
+ static int git_apply_config(const char *var, const char *value, void *cb)
  {
        if (!strcmp(var, "apply.whitespace"))
                return git_config_string(&apply_default_whitespace, var, value);
-       return git_default_config(var, value);
+       return git_default_config(var, value, cb);
  }
  
  
@@@ -3005,7 -2999,7 +3005,7 @@@ int cmd_apply(int argc, const char **ar
  
        prefix = setup_git_directory_gently(&is_not_gitdir);
        prefix_length = prefix ? strlen(prefix) : 0;
-       git_config(git_apply_config);
+       git_config(git_apply_config, NULL);
        if (apply_default_whitespace)
                parse_whitespace_option(apply_default_whitespace);
  
diff --combined builtin-cat-file.c
index 5ef15a4fa9eac952db105376ec6a10b706b3cbb2,b488fad431d10a1e93f02cdcf075109430c96390..200345e7fbd54d86fb1e4db228244b7a7a221562
@@@ -8,10 -8,6 +8,10 @@@
  #include "tag.h"
  #include "tree.h"
  #include "builtin.h"
 +#include "parse-options.h"
 +
 +#define BATCH 1
 +#define BATCH_CHECK 2
  
  static void pprint_tag(const unsigned char *sha1, const char *buf, unsigned long size)
  {
                write_or_die(1, cp, endp - cp);
  }
  
 -int cmd_cat_file(int argc, const char **argv, const char *prefix)
 +static int cat_one_file(int opt, const char *exp_type, const char *obj_name)
  {
        unsigned char sha1[20];
        enum object_type type;
        void *buf;
        unsigned long size;
 -      int opt;
 -      const char *exp_type, *obj_name;
 -
 -      git_config(git_default_config, NULL);
 -      if (argc != 3)
 -              usage("git-cat-file [-t|-s|-e|-p|<type>] <sha1>");
 -      exp_type = argv[1];
 -      obj_name = argv[2];
  
        if (get_sha1(obj_name, sha1))
                die("Not a valid object name %s", obj_name);
  
 -      opt = 0;
 -      if ( exp_type[0] == '-' ) {
 -              opt = exp_type[1];
 -              if ( !opt || exp_type[2] )
 -                      opt = -1; /* Not a single character option */
 -      }
 -
        buf = NULL;
        switch (opt) {
        case 't':
        write_or_die(1, buf, size);
        return 0;
  }
-       git_config(git_default_config);
 +
 +static int batch_one_object(const char *obj_name, int print_contents)
 +{
 +      unsigned char sha1[20];
 +      enum object_type type;
 +      unsigned long size;
 +      void *contents = contents;
 +
 +      if (!obj_name)
 +         return 1;
 +
 +      if (get_sha1(obj_name, sha1)) {
 +              printf("%s missing\n", obj_name);
 +              return 0;
 +      }
 +
 +      if (print_contents == BATCH)
 +              contents = read_sha1_file(sha1, &type, &size);
 +      else
 +              type = sha1_object_info(sha1, &size);
 +
 +      if (type <= 0)
 +              return 1;
 +
 +      printf("%s %s %lu\n", sha1_to_hex(sha1), typename(type), size);
 +      fflush(stdout);
 +
 +      if (print_contents == BATCH) {
 +              write_or_die(1, contents, size);
 +              printf("\n");
 +              fflush(stdout);
 +      }
 +
 +      return 0;
 +}
 +
 +static int batch_objects(int print_contents)
 +{
 +      struct strbuf buf;
 +
 +      strbuf_init(&buf, 0);
 +      while (strbuf_getline(&buf, stdin, '\n') != EOF) {
 +              int error = batch_one_object(buf.buf, print_contents);
 +              if (error)
 +                      return error;
 +      }
 +
 +      return 0;
 +}
 +
 +static const char * const cat_file_usage[] = {
 +      "git-cat-file [-t|-s|-e|-p|<type>] <sha1>",
 +      "git-cat-file [--batch|--batch-check] < <list_of_sha1s>",
 +      NULL
 +};
 +
 +int cmd_cat_file(int argc, const char **argv, const char *prefix)
 +{
 +      int opt = 0, batch = 0;
 +      const char *exp_type = NULL, *obj_name = NULL;
 +
 +      const struct option options[] = {
 +              OPT_GROUP("<type> can be one of: blob, tree, commit, tag"),
 +              OPT_SET_INT('t', NULL, &opt, "show object type", 't'),
 +              OPT_SET_INT('s', NULL, &opt, "show object size", 's'),
 +              OPT_SET_INT('e', NULL, &opt,
 +                          "exit with zero when there's no error", 'e'),
 +              OPT_SET_INT('p', NULL, &opt, "pretty-print object's content", 'p'),
 +              OPT_SET_INT(0, "batch", &batch,
 +                          "show info and content of objects feeded on stdin", BATCH),
 +              OPT_SET_INT(0, "batch-check", &batch,
 +                          "show info about objects feeded on stdin",
 +                          BATCH_CHECK),
 +              OPT_END()
 +      };
 +
++      git_config(git_default_config, NULL);
 +
 +      if (argc != 3 && argc != 2)
 +              usage_with_options(cat_file_usage, options);
 +
 +      argc = parse_options(argc, argv, options, cat_file_usage, 0);
 +
 +      if (opt) {
 +              if (argc == 1)
 +                      obj_name = argv[0];
 +              else
 +                      usage_with_options(cat_file_usage, options);
 +      }
 +      if (!opt && !batch) {
 +              if (argc == 2) {
 +                      exp_type = argv[0];
 +                      obj_name = argv[1];
 +              } else
 +                      usage_with_options(cat_file_usage, options);
 +      }
 +      if (batch && (opt || argc)) {
 +              usage_with_options(cat_file_usage, options);
 +      }
 +
 +      if (batch)
 +              return batch_objects(batch);
 +
 +      return cat_one_file(opt, exp_type, obj_name);
 +}
diff --combined builtin-checkout.c
index 68fffd28cb636d115ed4491b55ce3b87d0b97109,c077134e62e92dc6eabb0bffc83974f5d79caa76..1ea017f5f70f2255fb937866e9913a06399ce517
@@@ -236,8 -236,6 +236,8 @@@ static int merge_working_tree(struct ch
                topts.src_index = &the_index;
                topts.dst_index = &the_index;
  
 +              topts.msgs.not_uptodate_file = "You have local changes to '%s'; cannot switch branches.";
 +
                refresh_cache(REFRESH_QUIET);
  
                if (unmerged_cache()) {
                         * entries in the index.
                         */
  
 -                      add_files_to_cache(0, NULL, NULL);
 +                      add_files_to_cache(NULL, NULL, 0);
                        work = write_tree_from_memory();
  
                        ret = reset_to_new(new->commit->tree, opts->quiet);
@@@ -516,7 -514,7 +516,7 @@@ int cmd_checkout(int argc, const char *
        memset(&opts, 0, sizeof(opts));
        memset(&new, 0, sizeof(new));
  
-       git_config(git_default_config);
+       git_config(git_default_config, NULL);
  
        opts.track = git_branch_track;
  
diff --combined builtin-clone.c
index 2a3f6732f2a233c17a0086628ff3c31319bfe8ac,0000000000000000000000000000000000000000..4740b130674c074180a190f4c700863ac992ef91
mode 100644,000000..100644
--- /dev/null
@@@ -1,550 -1,0 +1,550 @@@
-       git_config(git_default_config);
 +/*
 + * Builtin "git clone"
 + *
 + * Copyright (c) 2007 Kristian Høgsberg <krh@redhat.com>,
 + *             2008 Daniel Barkalow <barkalow@iabervon.org>
 + * Based on git-commit.sh by Junio C Hamano and Linus Torvalds
 + *
 + * Clone a repository into a different directory that does not yet exist.
 + */
 +
 +#include "cache.h"
 +#include "parse-options.h"
 +#include "fetch-pack.h"
 +#include "refs.h"
 +#include "tree.h"
 +#include "tree-walk.h"
 +#include "unpack-trees.h"
 +#include "transport.h"
 +#include "strbuf.h"
 +#include "dir.h"
 +
 +/*
 + * Overall FIXMEs:
 + *  - respect DB_ENVIRONMENT for .git/objects.
 + *
 + * Implementation notes:
 + *  - dropping use-separate-remote and no-separate-remote compatibility
 + *
 + */
 +static const char * const builtin_clone_usage[] = {
 +      "git-clone [options] [--] <repo> [<dir>]",
 +      NULL
 +};
 +
 +static int option_quiet, option_no_checkout, option_bare;
 +static int option_local, option_no_hardlinks, option_shared;
 +static char *option_template, *option_reference, *option_depth;
 +static char *option_origin = NULL;
 +static char *option_upload_pack = "git-upload-pack";
 +
 +static struct option builtin_clone_options[] = {
 +      OPT__QUIET(&option_quiet),
 +      OPT_BOOLEAN('n', "no-checkout", &option_no_checkout,
 +                  "don't create a checkout"),
 +      OPT_BOOLEAN(0, "bare", &option_bare, "create a bare repository"),
 +      OPT_BOOLEAN(0, "naked", &option_bare, "create a bare repository"),
 +      OPT_BOOLEAN('l', "local", &option_local,
 +                  "to clone from a local repository"),
 +      OPT_BOOLEAN(0, "no-hardlinks", &option_no_hardlinks,
 +                  "don't use local hardlinks, always copy"),
 +      OPT_BOOLEAN('s', "shared", &option_shared,
 +                  "setup as shared repository"),
 +      OPT_STRING(0, "template", &option_template, "path",
 +                 "path the template repository"),
 +      OPT_STRING(0, "reference", &option_reference, "repo",
 +                 "reference repository"),
 +      OPT_STRING('o', "origin", &option_origin, "branch",
 +                 "use <branch> instead or 'origin' to track upstream"),
 +      OPT_STRING('u', "upload-pack", &option_upload_pack, "path",
 +                 "path to git-upload-pack on the remote"),
 +      OPT_STRING(0, "depth", &option_depth, "depth",
 +                  "create a shallow clone of that depth"),
 +
 +      OPT_END()
 +};
 +
 +static char *get_repo_path(const char *repo, int *is_bundle)
 +{
 +      static char *suffix[] = { "/.git", ".git", "" };
 +      static char *bundle_suffix[] = { ".bundle", "" };
 +      struct stat st;
 +      int i;
 +
 +      for (i = 0; i < ARRAY_SIZE(suffix); i++) {
 +              const char *path;
 +              path = mkpath("%s%s", repo, suffix[i]);
 +              if (!stat(path, &st) && S_ISDIR(st.st_mode)) {
 +                      *is_bundle = 0;
 +                      return xstrdup(make_absolute_path(path));
 +              }
 +      }
 +
 +      for (i = 0; i < ARRAY_SIZE(bundle_suffix); i++) {
 +              const char *path;
 +              path = mkpath("%s%s", repo, bundle_suffix[i]);
 +              if (!stat(path, &st) && S_ISREG(st.st_mode)) {
 +                      *is_bundle = 1;
 +                      return xstrdup(make_absolute_path(path));
 +              }
 +      }
 +
 +      return NULL;
 +}
 +
 +static char *guess_dir_name(const char *repo, int is_bundle)
 +{
 +      const char *p, *start, *end, *limit;
 +      int after_slash_or_colon;
 +
 +      /* Guess dir name from repository: strip trailing '/',
 +       * strip trailing '[:/]*.{git,bundle}', strip leading '.*[/:]'. */
 +
 +      after_slash_or_colon = 1;
 +      limit = repo + strlen(repo);
 +      start = repo;
 +      end = limit;
 +      for (p = repo; p < limit; p++) {
 +              const char *prefix = is_bundle ? ".bundle" : ".git";
 +              if (!prefixcmp(p, prefix)) {
 +                      if (!after_slash_or_colon)
 +                              end = p;
 +                      p += strlen(prefix) - 1;
 +              } else if (!prefixcmp(p, ".bundle")) {
 +                      if (!after_slash_or_colon)
 +                              end = p;
 +                      p += 7;
 +              } else if (*p == '/' || *p == ':') {
 +                      if (end == limit)
 +                              end = p;
 +                      after_slash_or_colon = 1;
 +              } else if (after_slash_or_colon) {
 +                      start = p;
 +                      end = limit;
 +                      after_slash_or_colon = 0;
 +              }
 +      }
 +
 +      return xstrndup(start, end - start);
 +}
 +
 +static int is_directory(const char *path)
 +{
 +      struct stat buf;
 +
 +      return !stat(path, &buf) && S_ISDIR(buf.st_mode);
 +}
 +
 +static void setup_reference(const char *repo)
 +{
 +      const char *ref_git;
 +      char *ref_git_copy;
 +
 +      struct remote *remote;
 +      struct transport *transport;
 +      const struct ref *extra;
 +
 +      ref_git = make_absolute_path(option_reference);
 +
 +      if (is_directory(mkpath("%s/.git/objects", ref_git)))
 +              ref_git = mkpath("%s/.git", ref_git);
 +      else if (!is_directory(mkpath("%s/objects", ref_git)))
 +              die("reference repository '%s' is not a local directory.",
 +                  option_reference);
 +
 +      ref_git_copy = xstrdup(ref_git);
 +
 +      add_to_alternates_file(ref_git_copy);
 +
 +      remote = remote_get(ref_git_copy);
 +      transport = transport_get(remote, ref_git_copy);
 +      for (extra = transport_get_remote_refs(transport); extra;
 +           extra = extra->next)
 +              add_extra_ref(extra->name, extra->old_sha1, 0);
 +
 +      transport_disconnect(transport);
 +
 +      free(ref_git_copy);
 +}
 +
 +static void copy_or_link_directory(char *src, char *dest)
 +{
 +      struct dirent *de;
 +      struct stat buf;
 +      int src_len, dest_len;
 +      DIR *dir;
 +
 +      dir = opendir(src);
 +      if (!dir)
 +              die("failed to open %s\n", src);
 +
 +      if (mkdir(dest, 0777)) {
 +              if (errno != EEXIST)
 +                      die("failed to create directory %s\n", dest);
 +              else if (stat(dest, &buf))
 +                      die("failed to stat %s\n", dest);
 +              else if (!S_ISDIR(buf.st_mode))
 +                      die("%s exists and is not a directory\n", dest);
 +      }
 +
 +      src_len = strlen(src);
 +      src[src_len] = '/';
 +      dest_len = strlen(dest);
 +      dest[dest_len] = '/';
 +
 +      while ((de = readdir(dir)) != NULL) {
 +              strcpy(src + src_len + 1, de->d_name);
 +              strcpy(dest + dest_len + 1, de->d_name);
 +              if (stat(src, &buf)) {
 +                      warning ("failed to stat %s\n", src);
 +                      continue;
 +              }
 +              if (S_ISDIR(buf.st_mode)) {
 +                      if (de->d_name[0] != '.')
 +                              copy_or_link_directory(src, dest);
 +                      continue;
 +              }
 +
 +              if (unlink(dest) && errno != ENOENT)
 +                      die("failed to unlink %s\n", dest);
 +              if (!option_no_hardlinks) {
 +                      if (!link(src, dest))
 +                              continue;
 +                      if (option_local)
 +                              die("failed to create link %s\n", dest);
 +                      option_no_hardlinks = 1;
 +              }
 +              if (copy_file(dest, src, 0666))
 +                      die("failed to copy file to %s\n", dest);
 +      }
 +      closedir(dir);
 +}
 +
 +static const struct ref *clone_local(const char *src_repo,
 +                                   const char *dest_repo)
 +{
 +      const struct ref *ret;
 +      char src[PATH_MAX];
 +      char dest[PATH_MAX];
 +      struct remote *remote;
 +      struct transport *transport;
 +
 +      if (option_shared)
 +              add_to_alternates_file(src_repo);
 +      else {
 +              snprintf(src, PATH_MAX, "%s/objects", src_repo);
 +              snprintf(dest, PATH_MAX, "%s/objects", dest_repo);
 +              copy_or_link_directory(src, dest);
 +      }
 +
 +      remote = remote_get(src_repo);
 +      transport = transport_get(remote, src_repo);
 +      ret = transport_get_remote_refs(transport);
 +      transport_disconnect(transport);
 +      return ret;
 +}
 +
 +static const char *junk_work_tree;
 +static const char *junk_git_dir;
 +pid_t junk_pid;
 +
 +static void remove_junk(void)
 +{
 +      struct strbuf sb;
 +      if (getpid() != junk_pid)
 +              return;
 +      strbuf_init(&sb, 0);
 +      if (junk_git_dir) {
 +              strbuf_addstr(&sb, junk_git_dir);
 +              remove_dir_recursively(&sb, 0);
 +              strbuf_reset(&sb);
 +      }
 +      if (junk_work_tree) {
 +              strbuf_addstr(&sb, junk_work_tree);
 +              remove_dir_recursively(&sb, 0);
 +              strbuf_reset(&sb);
 +      }
 +}
 +
 +static void remove_junk_on_signal(int signo)
 +{
 +      remove_junk();
 +      signal(SIGINT, SIG_DFL);
 +      raise(signo);
 +}
 +
 +static const struct ref *locate_head(const struct ref *refs,
 +                                   const struct ref *mapped_refs,
 +                                   const struct ref **remote_head_p)
 +{
 +      const struct ref *remote_head = NULL;
 +      const struct ref *remote_master = NULL;
 +      const struct ref *r;
 +      for (r = refs; r; r = r->next)
 +              if (!strcmp(r->name, "HEAD"))
 +                      remote_head = r;
 +
 +      for (r = mapped_refs; r; r = r->next)
 +              if (!strcmp(r->name, "refs/heads/master"))
 +                      remote_master = r;
 +
 +      if (remote_head_p)
 +              *remote_head_p = remote_head;
 +
 +      /* If there's no HEAD value at all, never mind. */
 +      if (!remote_head)
 +              return NULL;
 +
 +      /* If refs/heads/master could be right, it is. */
 +      if (remote_master && !hashcmp(remote_master->old_sha1,
 +                                    remote_head->old_sha1))
 +              return remote_master;
 +
 +      /* Look for another ref that points there */
 +      for (r = mapped_refs; r; r = r->next)
 +              if (r != remote_head &&
 +                  !hashcmp(r->old_sha1, remote_head->old_sha1))
 +                      return r;
 +
 +      /* Nothing is the same */
 +      return NULL;
 +}
 +
 +static struct ref *write_remote_refs(const struct ref *refs,
 +              struct refspec *refspec, const char *reflog)
 +{
 +      struct ref *local_refs = NULL;
 +      struct ref **tail = &local_refs;
 +      struct ref *r;
 +
 +      get_fetch_map(refs, refspec, &tail, 0);
 +      get_fetch_map(refs, tag_refspec, &tail, 0);
 +
 +      for (r = local_refs; r; r = r->next)
 +              update_ref(reflog,
 +                         r->peer_ref->name, r->old_sha1, NULL, 0, DIE_ON_ERR);
 +      return local_refs;
 +}
 +
 +int cmd_clone(int argc, const char **argv, const char *prefix)
 +{
 +      int use_local_hardlinks = 1;
 +      int use_separate_remote = 1;
 +      int is_bundle = 0;
 +      struct stat buf;
 +      const char *repo_name, *repo, *work_tree, *git_dir;
 +      char *path, *dir;
 +      const struct ref *refs, *head_points_at, *remote_head, *mapped_refs;
 +      char branch_top[256], key[256], value[256];
 +      struct strbuf reflog_msg;
 +
 +      struct refspec refspec;
 +
 +      junk_pid = getpid();
 +
 +      argc = parse_options(argc, argv, builtin_clone_options,
 +                           builtin_clone_usage, 0);
 +
 +      if (argc == 0)
 +              die("You must specify a repository to clone.");
 +
 +      if (option_no_hardlinks)
 +              use_local_hardlinks = 0;
 +
 +      if (option_bare) {
 +              if (option_origin)
 +                      die("--bare and --origin %s options are incompatible.",
 +                          option_origin);
 +              option_no_checkout = 1;
 +              use_separate_remote = 0;
 +      }
 +
 +      if (!option_origin)
 +              option_origin = "origin";
 +
 +      repo_name = argv[0];
 +
 +      path = get_repo_path(repo_name, &is_bundle);
 +      if (path)
 +              repo = path;
 +      else if (!strchr(repo_name, ':'))
 +              repo = xstrdup(make_absolute_path(repo_name));
 +      else
 +              repo = repo_name;
 +
 +      if (argc == 2)
 +              dir = xstrdup(argv[1]);
 +      else
 +              dir = guess_dir_name(repo_name, is_bundle);
 +
 +      if (!stat(dir, &buf))
 +              die("destination directory '%s' already exists.", dir);
 +
 +      strbuf_init(&reflog_msg, 0);
 +      strbuf_addf(&reflog_msg, "clone: from %s", repo);
 +
 +      if (option_bare)
 +              work_tree = NULL;
 +      else {
 +              work_tree = getenv("GIT_WORK_TREE");
 +              if (work_tree && !stat(work_tree, &buf))
 +                      die("working tree '%s' already exists.", work_tree);
 +      }
 +
 +      if (option_bare || work_tree)
 +              git_dir = xstrdup(dir);
 +      else {
 +              work_tree = dir;
 +              git_dir = xstrdup(mkpath("%s/.git", dir));
 +      }
 +
 +      if (!option_bare) {
 +              junk_work_tree = work_tree;
 +              if (mkdir(work_tree, 0755))
 +                      die("could not create work tree dir '%s'.", work_tree);
 +              set_git_work_tree(work_tree);
 +      }
 +      junk_git_dir = git_dir;
 +      atexit(remove_junk);
 +      signal(SIGINT, remove_junk_on_signal);
 +
 +      setenv(CONFIG_ENVIRONMENT, xstrdup(mkpath("%s/config", git_dir)), 1);
 +
 +      set_git_dir(make_absolute_path(git_dir));
 +
 +      fprintf(stderr, "Initialize %s\n", git_dir);
 +      init_db(option_template, option_quiet ? INIT_DB_QUIET : 0);
 +
 +      if (option_reference)
 +              setup_reference(git_dir);
 +
++      git_config(git_default_config, NULL);
 +
 +      if (option_bare) {
 +              strcpy(branch_top, "refs/heads/");
 +
 +              git_config_set("core.bare", "true");
 +      } else {
 +              snprintf(branch_top, sizeof(branch_top),
 +                       "refs/remotes/%s/", option_origin);
 +
 +              /* Configure the remote */
 +              snprintf(key, sizeof(key), "remote.%s.url", option_origin);
 +              git_config_set(key, repo);
 +
 +              snprintf(key, sizeof(key), "remote.%s.fetch", option_origin);
 +              snprintf(value, sizeof(value),
 +                              "+refs/heads/*:%s*", branch_top);
 +              git_config_set_multivar(key, value, "^$", 0);
 +      }
 +
 +      refspec.force = 0;
 +      refspec.pattern = 1;
 +      refspec.src = "refs/heads/";
 +      refspec.dst = branch_top;
 +
 +      if (path && !is_bundle)
 +              refs = clone_local(path, git_dir);
 +      else {
 +              struct remote *remote = remote_get(argv[0]);
 +              struct transport *transport = transport_get(remote, argv[0]);
 +
 +              transport_set_option(transport, TRANS_OPT_KEEP, "yes");
 +
 +              if (option_depth)
 +                      transport_set_option(transport, TRANS_OPT_DEPTH,
 +                                           option_depth);
 +
 +              if (option_quiet)
 +                      transport->verbose = -1;
 +
 +              refs = transport_get_remote_refs(transport);
 +              transport_fetch_refs(transport, refs);
 +      }
 +
 +      clear_extra_refs();
 +
 +      mapped_refs = write_remote_refs(refs, &refspec, reflog_msg.buf);
 +
 +      head_points_at = locate_head(refs, mapped_refs, &remote_head);
 +
 +      if (head_points_at) {
 +              /* Local default branch link */
 +              create_symref("HEAD", head_points_at->name, NULL);
 +
 +              if (!option_bare) {
 +                      struct strbuf head_ref;
 +                      const char *head = head_points_at->name;
 +
 +                      if (!prefixcmp(head, "refs/heads/"))
 +                              head += 11;
 +
 +                      /* Set up the initial local branch */
 +
 +                      /* Local branch initial value */
 +                      update_ref(reflog_msg.buf, "HEAD",
 +                                 head_points_at->old_sha1,
 +                                 NULL, 0, DIE_ON_ERR);
 +
 +                      strbuf_init(&head_ref, 0);
 +                      strbuf_addstr(&head_ref, branch_top);
 +                      strbuf_addstr(&head_ref, "HEAD");
 +
 +                      /* Remote branch link */
 +                      create_symref(head_ref.buf,
 +                                    head_points_at->peer_ref->name,
 +                                    reflog_msg.buf);
 +
 +                      snprintf(key, sizeof(key), "branch.%s.remote", head);
 +                      git_config_set(key, option_origin);
 +                      snprintf(key, sizeof(key), "branch.%s.merge", head);
 +                      git_config_set(key, head_points_at->name);
 +              }
 +      } else if (remote_head) {
 +              /* Source had detached HEAD pointing somewhere. */
 +              if (!option_bare)
 +                      update_ref(reflog_msg.buf, "HEAD",
 +                                 remote_head->old_sha1,
 +                                 NULL, REF_NODEREF, DIE_ON_ERR);
 +      } else {
 +              /* Nothing to checkout out */
 +              if (!option_no_checkout)
 +                      warning("remote HEAD refers to nonexistent ref, "
 +                              "unable to checkout.\n");
 +              option_no_checkout = 1;
 +      }
 +
 +      if (!option_no_checkout) {
 +              struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
 +              struct unpack_trees_options opts;
 +              struct tree *tree;
 +              struct tree_desc t;
 +              int fd;
 +
 +              /* We need to be in the new work tree for the checkout */
 +              setup_work_tree();
 +
 +              fd = hold_locked_index(lock_file, 1);
 +
 +              memset(&opts, 0, sizeof opts);
 +              opts.update = 1;
 +              opts.merge = 1;
 +              opts.fn = oneway_merge;
 +              opts.verbose_update = !option_quiet;
 +              opts.src_index = &the_index;
 +              opts.dst_index = &the_index;
 +
 +              tree = parse_tree_indirect(remote_head->old_sha1);
 +              parse_tree(tree);
 +              init_tree_desc(&t, tree->buffer, tree->size);
 +              unpack_trees(1, &t, &opts);
 +
 +              if (write_cache(fd, active_cache, active_nr) ||
 +                  commit_locked_index(lock_file))
 +                      die("unable to write new index file");
 +      }
 +
 +      strbuf_release(&reflog_msg);
 +      junk_pid = 0;
 +      return 0;
 +}
diff --combined builtin-commit.c
index d75224381b7ad88d6bbb67db3a85bbb881cea941,9f0026ed00f4413df1ea6db472273ae03cb1ab91..07872c8ea71064c1bcf5813dd66b4a7e5feb9bec
@@@ -47,7 -47,6 +47,7 @@@ static enum 
  
  static char *logfile, *force_author, *template_file;
  static char *edit_message, *use_message;
 +static char *author_name, *author_email, *author_date;
  static int all, edit_flag, also, interactive, only, amend, signoff;
  static int quiet, verbose, untracked_files, no_verify, allow_empty;
  /*
@@@ -179,10 -178,9 +179,10 @@@ static void add_remove_files(struct pat
                struct stat st;
                struct path_list_item *p = &(list->items[i]);
  
 -              if (!lstat(p->path, &st))
 -                      add_to_cache(p->path, &st, 0);
 -              else
 +              if (!lstat(p->path, &st)) {
 +                      if (add_to_cache(p->path, &st, 0))
 +                              die("updating files failed");
 +              } else
                        remove_file_from_cache(p->path);
        }
  }
@@@ -247,7 -245,7 +247,7 @@@ static char *prepare_index(int argc, co
         */
        if (all || (also && pathspec && *pathspec)) {
                int fd = hold_locked_index(&index_lock, 1);
 -              add_files_to_cache(0, also ? prefix : NULL, pathspec);
 +              add_files_to_cache(also ? prefix : NULL, pathspec, 0);
                refresh_cache(REFRESH_QUIET);
                if (write_cache(fd, active_cache, active_nr) ||
                    close_lock_file(&index_lock))
@@@ -399,47 -397,6 +399,47 @@@ static int is_a_merge(const unsigned ch
  
  static const char sign_off_header[] = "Signed-off-by: ";
  
 +static void determine_author_info(void)
 +{
 +      char *name, *email, *date;
 +
 +      name = getenv("GIT_AUTHOR_NAME");
 +      email = getenv("GIT_AUTHOR_EMAIL");
 +      date = getenv("GIT_AUTHOR_DATE");
 +
 +      if (use_message) {
 +              const char *a, *lb, *rb, *eol;
 +
 +              a = strstr(use_message_buffer, "\nauthor ");
 +              if (!a)
 +                      die("invalid commit: %s", use_message);
 +
 +              lb = strstr(a + 8, " <");
 +              rb = strstr(a + 8, "> ");
 +              eol = strchr(a + 8, '\n');
 +              if (!lb || !rb || !eol)
 +                      die("invalid commit: %s", use_message);
 +
 +              name = xstrndup(a + 8, lb - (a + 8));
 +              email = xstrndup(lb + 2, rb - (lb + 2));
 +              date = xstrndup(rb + 2, eol - (rb + 2));
 +      }
 +
 +      if (force_author) {
 +              const char *lb = strstr(force_author, " <");
 +              const char *rb = strchr(force_author, '>');
 +
 +              if (!lb || !rb)
 +                      die("malformed --author parameter");
 +              name = xstrndup(force_author, lb - force_author);
 +              email = xstrndup(lb + 2, rb - (lb + 2));
 +      }
 +
 +      author_name = name;
 +      author_email = email;
 +      author_date = date;
 +}
 +
  static int prepare_to_commit(const char *index_file, const char *prefix)
  {
        struct stat statbuf;
        FILE *fp;
        const char *hook_arg1 = NULL;
        const char *hook_arg2 = NULL;
 +      int ident_shown = 0;
  
        if (!no_verify && run_hook(index_file, "pre-commit", NULL))
                return 0;
  
        strbuf_release(&sb);
  
 +      determine_author_info();
 +
 +      /* This checks if committer ident is explicitly given */
 +      git_committer_info(0);
        if (use_editor) {
 +              char *author_ident;
 +              const char *committer_ident;
 +
                if (in_merge)
                        fprintf(fp,
                                "#\n"
                if (only_include_assumed)
                        fprintf(fp, "# %s\n", only_include_assumed);
  
 +              author_ident = xstrdup(fmt_name(author_name, author_email));
 +              committer_ident = fmt_name(getenv("GIT_COMMITTER_NAME"),
 +                                         getenv("GIT_COMMITTER_EMAIL"));
 +              if (strcmp(author_ident, committer_ident))
 +                      fprintf(fp,
 +                              "%s"
 +                              "# Author:    %s\n",
 +                              ident_shown++ ? "" : "#\n",
 +                              author_ident);
 +              free(author_ident);
 +
 +              if (!user_ident_explicitly_given)
 +                      fprintf(fp,
 +                              "%s"
 +                              "# Committer: %s\n",
 +                              ident_shown++ ? "" : "#\n",
 +                              committer_ident);
 +
 +              if (ident_shown)
 +                      fprintf(fp, "#\n");
 +
                saved_color_setting = wt_status_use_color;
                wt_status_use_color = 0;
                commitable = run_status(fp, index_file, prefix, 1);
@@@ -696,6 -624,45 +696,6 @@@ static int message_is_empty(struct strb
        return 1;
  }
  
 -static void determine_author_info(struct strbuf *sb)
 -{
 -      char *name, *email, *date;
 -
 -      name = getenv("GIT_AUTHOR_NAME");
 -      email = getenv("GIT_AUTHOR_EMAIL");
 -      date = getenv("GIT_AUTHOR_DATE");
 -
 -      if (use_message) {
 -              const char *a, *lb, *rb, *eol;
 -
 -              a = strstr(use_message_buffer, "\nauthor ");
 -              if (!a)
 -                      die("invalid commit: %s", use_message);
 -
 -              lb = strstr(a + 8, " <");
 -              rb = strstr(a + 8, "> ");
 -              eol = strchr(a + 8, '\n');
 -              if (!lb || !rb || !eol)
 -                      die("invalid commit: %s", use_message);
 -
 -              name = xstrndup(a + 8, lb - (a + 8));
 -              email = xstrndup(lb + 2, rb - (lb + 2));
 -              date = xstrndup(rb + 2, eol - (rb + 2));
 -      }
 -
 -      if (force_author) {
 -              const char *lb = strstr(force_author, " <");
 -              const char *rb = strchr(force_author, '>');
 -
 -              if (!lb || !rb)
 -                      die("malformed --author parameter");
 -              name = xstrndup(force_author, lb - force_author);
 -              email = xstrndup(lb + 2, rb - (lb + 2));
 -      }
 -
 -      strbuf_addf(sb, "author %s\n", fmt_ident(name, email, date, IDENT_ERROR_ON_NO_NAME));
 -}
 -
  static int parse_and_validate_options(int argc, const char *argv[],
                                      const char * const usage[])
  {
@@@ -806,7 -773,7 +806,7 @@@ int cmd_status(int argc, const char **a
        const char *index_file;
        int commitable;
  
-       git_config(git_status_config);
+       git_config(git_status_config, NULL);
  
        if (wt_status_use_color == -1)
                wt_status_use_color = git_use_color_default;
@@@ -860,7 -827,7 +860,7 @@@ static void print_summary(const char *p
        }
  }
  
- int git_commit_config(const char *k, const char *v)
+ int git_commit_config(const char *k, const char *v, void *cb)
  {
        if (!strcmp(k, "commit.template")) {
                if (!v)
                return 0;
        }
  
-       return git_status_config(k, v);
+       return git_status_config(k, v, cb);
  }
  
  static const char commit_utf8_warn[] =
@@@ -897,7 -864,7 +897,7 @@@ int cmd_commit(int argc, const char **a
        unsigned char commit_sha1[20];
        struct ref_lock *ref_lock;
  
-       git_config(git_commit_config);
+       git_config(git_commit_config, NULL);
  
        argc = parse_and_validate_options(argc, argv, builtin_commit_usage);
  
                strbuf_addf(&sb, "parent %s\n", sha1_to_hex(head_sha1));
        }
  
 -      determine_author_info(&sb);
 +      strbuf_addf(&sb, "author %s\n",
 +                  fmt_ident(author_name, author_email, author_date, IDENT_ERROR_ON_NO_NAME));
        strbuf_addf(&sb, "committer %s\n", git_committer_info(IDENT_ERROR_ON_NO_NAME));
        if (!is_encoding_utf8(git_commit_encoding))
                strbuf_addf(&sb, "encoding %s\n", git_commit_encoding);
diff --combined builtin-gc.c
index 48f7d95f9784d27f023b22343929f5ad188caa4c,e1a329917a3844583e997480dd7408ce0aba00f9..f5625bb9fb090f5af4dfb782dc04005a4149a5c8
@@@ -35,7 -35,7 +35,7 @@@ static const char *argv_repack[MAX_ADD
  static const char *argv_prune[] = {"prune", "--expire", NULL, NULL};
  static const char *argv_rerere[] = {"rerere", "gc", NULL};
  
- static int gc_config(const char *var, const char *value)
+ static int gc_config(const char *var, const char *value, void *cb)
  {
        if (!strcmp(var, "gc.packrefs")) {
                if (value && !strcmp(value, "notbare"))
@@@ -67,7 -67,7 +67,7 @@@
                prune_expire = xstrdup(value);
                return 0;
        }
-       return git_default_config(var, value);
+       return git_default_config(var, value, cb);
  }
  
  static void append_option(const char **cmd, const char *opt, int max_length)
@@@ -219,14 -219,14 +219,14 @@@ int cmd_gc(int argc, const char **argv
        char buf[80];
  
        struct option builtin_gc_options[] = {
 -              OPT_BOOLEAN(0, "prune", &prune, "prune unreferenced objects"),
 +              OPT_BOOLEAN(0, "prune", &prune, "prune unreferenced objects (deprecated)"),
                OPT_BOOLEAN(0, "aggressive", &aggressive, "be more thorough (increased runtime)"),
                OPT_BOOLEAN(0, "auto", &auto_gc, "enable auto-gc mode"),
                OPT_BOOLEAN('q', "quiet", &quiet, "suppress progress reports"),
                OPT_END()
        };
  
-       git_config(gc_config);
+       git_config(gc_config, NULL);
  
        if (pack_refs < 0)
                pack_refs = !is_bare_repository();
                /*
                 * Auto-gc should be least intrusive as possible.
                 */
 -              prune = 0;
                if (!need_to_gc())
                        return 0;
                fprintf(stderr, "Auto packing your repository for optimum "
                        "performance. You may also\n"
                        "run \"git gc\" manually. See "
                        "\"git help gc\" for more information.\n");
 -      } else {
 -              /*
 -               * Use safer (for shared repos) "-A" option to
 -               * repack when not pruning. Auto-gc makes its
 -               * own decision.
 -               */
 -              if (prune)
 -                      append_option(argv_repack, "-a", MAX_ADD);
 -              else
 -                      append_option(argv_repack, "-A", MAX_ADD);
 -      }
 +      } else
 +              append_option(argv_repack, "-A", MAX_ADD);
  
        if (pack_refs && run_command_v_opt(argv_pack_refs, RUN_GIT_CMD))
                return error(FAILED_RUN, argv_pack_refs[0]);
diff --combined builtin-init-db.c
index 3968c9911ff23717a4505ba9a02af6e27bf0b4a0,f6aa3535299fdc56ff734c7476fcdfc7475a156d..d8bdf928b1e97864722352955a2e2e37b5c00806
@@@ -104,14 -104,12 +104,14 @@@ static void copy_templates_1(char *path
        }
  }
  
 -static void copy_templates(const char *git_dir, int len, const char *template_dir)
 +static void copy_templates(const char *template_dir)
  {
        char path[PATH_MAX];
        char template_path[PATH_MAX];
        int template_len;
        DIR *dir;
 +      const char *git_dir = get_git_dir();
 +      int len = strlen(git_dir);
  
        if (!template_dir)
                template_dir = getenv(TEMPLATE_DIR_ENVIRONMENT);
        strcpy(template_path + template_len, "config");
        repository_format_version = 0;
        git_config_from_file(check_repository_format_version,
-                            template_path);
+                            template_path, NULL);
        template_path[template_len] = 0;
  
        if (repository_format_version &&
        }
  
        memcpy(path, git_dir, len);
 +      if (len && path[len - 1] != '/')
 +              path[len++] = '/';
        path[len] = 0;
        copy_templates_1(path, len,
                         template_path, template_len,
        closedir(dir);
  }
  
 -static int create_default_files(const char *git_dir, const char *template_path)
 +static int create_default_files(const char *template_path)
  {
 +      const char *git_dir = get_git_dir();
        unsigned len = strlen(git_dir);
        static char path[PATH_MAX];
        struct stat st1;
        /*
         * Create .git/refs/{heads,tags}
         */
 -      strcpy(path + len, "refs");
 -      safe_create_dir(path, 1);
 -      strcpy(path + len, "refs/heads");
 -      safe_create_dir(path, 1);
 -      strcpy(path + len, "refs/tags");
 -      safe_create_dir(path, 1);
 +      safe_create_dir(git_path("refs"), 1);
 +      safe_create_dir(git_path("refs/heads"), 1);
 +      safe_create_dir(git_path("refs/tags"), 1);
  
        /* First copy the templates -- we might have the default
         * config file there, in which case we would want to read
         * from it after installing.
         */
 -      path[len] = 0;
 -      copy_templates(path, len, template_path);
 +      copy_templates(template_path);
  
-       git_config(git_default_config);
+       git_config(git_default_config, NULL);
  
        /*
         * We would have created the above under user's umask -- under
         * shared-repository settings, we would need to fix them up.
         */
        if (shared_repository) {
 -              path[len] = 0;
 -              adjust_shared_perm(path);
 -              strcpy(path + len, "refs");
 -              adjust_shared_perm(path);
 -              strcpy(path + len, "refs/heads");
 -              adjust_shared_perm(path);
 -              strcpy(path + len, "refs/tags");
 -              adjust_shared_perm(path);
 +              adjust_shared_perm(get_git_dir());
 +              adjust_shared_perm(git_path("refs"));
 +              adjust_shared_perm(git_path("refs/heads"));
 +              adjust_shared_perm(git_path("refs/tags"));
        }
  
        /*
                /* allow template config file to override the default */
                if (log_all_ref_updates == -1)
                    git_config_set("core.logallrefupdates", "true");
 -              if (work_tree != git_work_tree_cfg)
 +              if (prefixcmp(git_dir, work_tree) ||
 +                  strcmp(git_dir + strlen(work_tree), "/.git")) {
                        git_config_set("core.worktree", work_tree);
 +              }
        }
  
 -      /* Check if symlink is supported in the work tree */
        if (!reinit) {
 +              /* Check if symlink is supported in the work tree */
                path[len] = 0;
                strcpy(path + len, "tXXXXXX");
                if (!close(xmkstemp(path)) &&
                        unlink(path); /* good */
                else
                        git_config_set("core.symlinks", "false");
 +
 +              /* Check if the filesystem is case-insensitive */
 +              path[len] = 0;
 +              strcpy(path + len, "CoNfIg");
 +              if (!access(path, F_OK))
 +                      git_config_set("core.ignorecase", "true");
        }
  
        return reinit;
  }
  
 -static void guess_repository_type(const char *git_dir)
 +int init_db(const char *template_dir, unsigned int flags)
 +{
 +      const char *sha1_dir;
 +      char *path;
 +      int len, reinit;
 +
 +      safe_create_dir(get_git_dir(), 0);
 +
 +      /* Check to see if the repository version is right.
 +       * Note that a newly created repository does not have
 +       * config file, so this will not fail.  What we are catching
 +       * is an attempt to reinitialize new repository with an old tool.
 +       */
 +      check_repository_format();
 +
 +      reinit = create_default_files(template_dir);
 +
 +      sha1_dir = get_object_directory();
 +      len = strlen(sha1_dir);
 +      path = xmalloc(len + 40);
 +      memcpy(path, sha1_dir, len);
 +
 +      safe_create_dir(sha1_dir, 1);
 +      strcpy(path+len, "/pack");
 +      safe_create_dir(path, 1);
 +      strcpy(path+len, "/info");
 +      safe_create_dir(path, 1);
 +
 +      if (shared_repository) {
 +              char buf[10];
 +              /* We do not spell "group" and such, so that
 +               * the configuration can be read by older version
 +               * of git. Note, we use octal numbers for new share modes,
 +               * and compatibility values for PERM_GROUP and
 +               * PERM_EVERYBODY.
 +               */
 +              if (shared_repository == PERM_GROUP)
 +                      sprintf(buf, "%d", OLD_PERM_GROUP);
 +              else if (shared_repository == PERM_EVERYBODY)
 +                      sprintf(buf, "%d", OLD_PERM_EVERYBODY);
 +              else
 +                      sprintf(buf, "0%o", shared_repository);
 +              git_config_set("core.sharedrepository", buf);
 +              git_config_set("receive.denyNonFastforwards", "true");
 +      }
 +
 +      if (!(flags & INIT_DB_QUIET))
 +              printf("%s%s Git repository in %s/\n",
 +                     reinit ? "Reinitialized existing" : "Initialized empty",
 +                     shared_repository ? " shared" : "",
 +                     get_git_dir());
 +
 +      return 0;
 +}
 +
 +static int guess_repository_type(const char *git_dir)
  {
        char cwd[PATH_MAX];
        const char *slash;
  
 -      if (0 <= is_bare_repository_cfg)
 -              return;
 -      if (!git_dir)
 -              return;
 -
        /*
         * "GIT_DIR=. git init" is always bare.
         * "GIT_DIR=`pwd` git init" too.
         */
        if (!strcmp(".", git_dir))
 -              goto force_bare;
 +              return 1;
        if (!getcwd(cwd, sizeof(cwd)))
                die("cannot tell cwd");
        if (!strcmp(git_dir, cwd))
 -              goto force_bare;
 +              return 1;
        /*
         * "GIT_DIR=.git or GIT_DIR=something/.git is usually not.
         */
        if (!strcmp(git_dir, ".git"))
 -              return;
 +              return 0;
        slash = strrchr(git_dir, '/');
        if (slash && !strcmp(slash, "/.git"))
 -              return;
 +              return 0;
  
        /*
         * Otherwise it is often bare.  At this point
         * we are just guessing.
         */
 - force_bare:
 -      is_bare_repository_cfg = 1;
 -      return;
 +      return 1;
  }
  
  static const char init_db_usage[] =
  int cmd_init_db(int argc, const char **argv, const char *prefix)
  {
        const char *git_dir;
 -      const char *sha1_dir;
        const char *template_dir = NULL;
 -      char *path;
 -      int len, i, reinit;
 -      int quiet = 0;
 +      unsigned int flags = 0;
 +      int i;
  
        for (i = 1; i < argc; i++, argv++) {
                const char *arg = argv[1];
                else if (!prefixcmp(arg, "--shared="))
                        shared_repository = git_config_perm("arg", arg+9);
                else if (!strcmp(arg, "-q") || !strcmp(arg, "--quiet"))
 -                      quiet = 1;
 +                      flags |= INIT_DB_QUIET;
                else
                        usage(init_db_usage);
        }
                    GIT_WORK_TREE_ENVIRONMENT,
                    GIT_DIR_ENVIRONMENT);
  
 -      guess_repository_type(git_dir);
 -
 -      if (is_bare_repository_cfg <= 0) {
 -              git_work_tree_cfg = xcalloc(PATH_MAX, 1);
 -              if (!getcwd(git_work_tree_cfg, PATH_MAX))
 -                      die ("Cannot access current working directory.");
 -              if (access(get_git_work_tree(), X_OK))
 -                      die ("Cannot access work tree '%s'",
 -                           get_git_work_tree());
 -      }
 -
        /*
         * Set up the default .git directory contents
         */
 -      git_dir = getenv(GIT_DIR_ENVIRONMENT);
        if (!git_dir)
                git_dir = DEFAULT_GIT_DIR_ENVIRONMENT;
 -      safe_create_dir(git_dir, 0);
 -
 -      /* Check to see if the repository version is right.
 -       * Note that a newly created repository does not have
 -       * config file, so this will not fail.  What we are catching
 -       * is an attempt to reinitialize new repository with an old tool.
 -       */
 -      check_repository_format();
 -
 -      reinit = create_default_files(git_dir, template_dir);
  
 -      /*
 -       * And set up the object store.
 -       */
 -      sha1_dir = get_object_directory();
 -      len = strlen(sha1_dir);
 -      path = xmalloc(len + 40);
 -      memcpy(path, sha1_dir, len);
 -
 -      safe_create_dir(sha1_dir, 1);
 -      strcpy(path+len, "/pack");
 -      safe_create_dir(path, 1);
 -      strcpy(path+len, "/info");
 -      safe_create_dir(path, 1);
 -
 -      if (shared_repository) {
 -              char buf[10];
 -              /* We do not spell "group" and such, so that
 -               * the configuration can be read by older version
 -               * of git. Note, we use octal numbers for new share modes,
 -               * and compatibility values for PERM_GROUP and
 -               * PERM_EVERYBODY.
 -               */
 -              if (shared_repository == PERM_GROUP)
 -                      sprintf(buf, "%d", OLD_PERM_GROUP);
 -              else if (shared_repository == PERM_EVERYBODY)
 -                      sprintf(buf, "%d", OLD_PERM_EVERYBODY);
 -              else
 -                      sprintf(buf, "0%o", shared_repository);
 -              git_config_set("core.sharedrepository", buf);
 -              git_config_set("receive.denyNonFastforwards", "true");
 +      if (is_bare_repository_cfg < 0)
 +              is_bare_repository_cfg = guess_repository_type(git_dir);
 +
 +      if (!is_bare_repository_cfg) {
 +              if (git_dir) {
 +                      const char *git_dir_parent = strrchr(git_dir, '/');
 +                      if (git_dir_parent) {
 +                              char *rel = xstrndup(git_dir, git_dir_parent - git_dir);
 +                              git_work_tree_cfg = xstrdup(make_absolute_path(rel));
 +                              free(rel);
 +                      }
 +              }
 +              if (!git_work_tree_cfg) {
 +                      git_work_tree_cfg = xcalloc(PATH_MAX, 1);
 +                      if (!getcwd(git_work_tree_cfg, PATH_MAX))
 +                              die ("Cannot access current working directory.");
 +              }
 +              if (access(get_git_work_tree(), X_OK))
 +                      die ("Cannot access work tree '%s'",
 +                           get_git_work_tree());
        }
  
 -      if (!quiet)
 -              printf("%s%s Git repository in %s/\n",
 -                     reinit ? "Reinitialized existing" : "Initialized empty",
 -                     shared_repository ? " shared" : "",
 -                     git_dir);
 +      set_git_dir(make_absolute_path(git_dir));
  
 -      return 0;
 +      return init_db(template_dir, flags);
  }
diff --combined builtin-log.c
index 543855b7ad11bd0355d88bc1502030509875e0c7,addc7098cfd0c3ab0d0660fb6b14220f2ebf448f..9817d6fbeb69393ed321e71b6211a8630b982fab
@@@ -18,9 -18,6 +18,9 @@@
  #include "run-command.h"
  #include "shortlog.h"
  
 +/* Set a default date-time format for git log ("log.date" config variable) */
 +static const char *default_date_mode = NULL;
 +
  static int default_show_root = 1;
  static const char *fmt_patch_subject_prefix = "PATCH";
  static const char *fmt_pretty;
@@@ -64,12 -61,7 +64,12 @@@ static void cmd_log_init(int argc, cons
        DIFF_OPT_SET(&rev->diffopt, RECURSIVE);
        rev->show_root_diff = default_show_root;
        rev->subject_prefix = fmt_patch_subject_prefix;
 +
 +      if (default_date_mode)
 +              rev->date_mode = parse_date_format(default_date_mode);
 +
        argc = setup_revisions(argc, argv, rev, "HEAD");
 +
        if (rev->diffopt.pickaxe || rev->diffopt.filter)
                rev->always_show_header = 0;
        if (DIFF_OPT_TST(&rev->diffopt, FOLLOW_RENAMES)) {
@@@ -230,7 -222,7 +230,7 @@@ static int cmd_log_walk(struct rev_inf
        return 0;
  }
  
- static int git_log_config(const char *var, const char *value)
+ static int git_log_config(const char *var, const char *value, void *cb)
  {
        if (!strcmp(var, "format.pretty"))
                return git_config_string(&fmt_pretty, var, value);
                fmt_patch_subject_prefix = xstrdup(value);
                return 0;
        }
 +      if (!strcmp(var, "log.date"))
 +              return git_config_string(&default_date_mode, var, value);
        if (!strcmp(var, "log.showroot")) {
                default_show_root = git_config_bool(var, value);
                return 0;
        }
-       return git_diff_ui_config(var, value);
+       return git_diff_ui_config(var, value, cb);
  }
  
  int cmd_whatchanged(int argc, const char **argv, const char *prefix)
  {
        struct rev_info rev;
  
-       git_config(git_log_config);
+       git_config(git_log_config, NULL);
  
        if (diff_use_color_default == -1)
                diff_use_color_default = git_use_color_default;
@@@ -329,7 -319,7 +329,7 @@@ int cmd_show(int argc, const char **arg
        struct object_array_entry *objects;
        int i, count, ret = 0;
  
-       git_config(git_log_config);
+       git_config(git_log_config, NULL);
  
        if (diff_use_color_default == -1)
                diff_use_color_default = git_use_color_default;
@@@ -393,7 -383,7 +393,7 @@@ int cmd_log_reflog(int argc, const cha
  {
        struct rev_info rev;
  
-       git_config(git_log_config);
+       git_config(git_log_config, NULL);
  
        if (diff_use_color_default == -1)
                diff_use_color_default = git_use_color_default;
@@@ -426,7 -416,7 +426,7 @@@ int cmd_log(int argc, const char **argv
  {
        struct rev_info rev;
  
-       git_config(git_log_config);
+       git_config(git_log_config, NULL);
  
        if (diff_use_color_default == -1)
                diff_use_color_default = git_use_color_default;
@@@ -481,7 -471,7 +481,7 @@@ static void add_header(const char *valu
        extra_hdr[extra_hdr_nr++] = xstrndup(value, len);
  }
  
- static int git_format_config(const char *var, const char *value)
+ static int git_format_config(const char *var, const char *value, void *cb)
  {
        if (!strcmp(var, "format.headers")) {
                if (!value)
                fmt_patch_suffix = xstrdup(value);
                return 0;
        }
 +      if (!strcmp(var, "format.cc")) {
 +              if (!value)
 +                      return config_error_nonbool(var);
 +              ALLOC_GROW(extra_cc, extra_cc_nr + 1, extra_cc_alloc);
 +              extra_cc[extra_cc_nr++] = xstrdup(value);
 +              return 0;
 +      }
        if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) {
                return 0;
        }
                return 0;
        }
  
-       return git_log_config(var, value);
+       return git_log_config(var, value, cb);
  }
  
  
@@@ -781,7 -764,7 +781,7 @@@ int cmd_format_patch(int argc, const ch
        char *add_signoff = NULL;
        struct strbuf buf;
  
-       git_config(git_format_config);
+       git_config(git_format_config, NULL);
        init_revisions(&rev, prefix);
        rev.commit_format = CMIT_FMT_EMAIL;
        rev.verbose_header = 1;
diff --combined builtin-mailinfo.c
index e1e094f29ef3e7f7af3b0a20047b67a70c8c3e18,6e23ffd8af386257da8547627bb46b4f1b319e34..97c1ff97440ec107e336df774f86aadc28f26b89
@@@ -434,7 -434,6 +434,7 @@@ static int read_one_header_line(char *l
  
  static int decode_q_segment(char *in, char *ot, unsigned otsize, char *ep, int rfc2047)
  {
 +      char *otbegin = ot;
        char *otend = ot + otsize;
        int c;
        while ((c = *in++) != 0 && (in <= ep)) {
                *ot++ = c;
        }
        *ot = 0;
 -      return 0;
 +      return (ot - otbegin);
  }
  
  static int decode_b_segment(char *in, char *ot, unsigned otsize, char *ep)
  {
        /* Decode in..ep, possibly in-place to ot */
        int c, pos = 0, acc = 0;
 +      char *otbegin = ot;
        char *otend = ot + otsize;
  
        while ((c = *in++) != 0 && (in <= ep)) {
                }
        }
        *ot = 0;
 -      return 0;
 +      return (ot - otbegin);
  }
  
  /*
@@@ -625,24 -623,25 +625,24 @@@ static void decode_header(char *it, uns
                convert_to_utf8(it, itsize, "");
  }
  
 -static void decode_transfer_encoding(char *line, unsigned linesize)
 +static int decode_transfer_encoding(char *line, unsigned linesize, int inputlen)
  {
        char *ep;
  
        switch (transfer_encoding) {
        case TE_QP:
 -              ep = line + strlen(line);
 -              decode_q_segment(line, line, linesize, ep, 0);
 -              break;
 +              ep = line + inputlen;
 +              return decode_q_segment(line, line, linesize, ep, 0);
        case TE_BASE64:
 -              ep = line + strlen(line);
 -              decode_b_segment(line, line, linesize, ep);
 -              break;
 +              ep = line + inputlen;
 +              return decode_b_segment(line, line, linesize, ep);
        case TE_DONTCARE:
 -              break;
 +      default:
 +              return inputlen;
        }
  }
  
 -static int handle_filter(char *line, unsigned linesize);
 +static int handle_filter(char *line, unsigned linesize, int linelen);
  
  static int find_boundary(void)
  {
@@@ -670,7 -669,7 +670,7 @@@ again
                                        "can't recover\n");
                        exit(1);
                }
 -              handle_filter(newline, sizeof(newline));
 +              handle_filter(newline, sizeof(newline), strlen(newline));
  
                /* skip to the next boundary */
                if (!find_boundary())
@@@ -760,14 -759,14 +760,14 @@@ static int handle_commit_msg(char *line
        return 0;
  }
  
 -static int handle_patch(char *line)
 +static int handle_patch(char *line, int len)
  {
 -      fputs(line, patchfile);
 +      fwrite(line, 1, len, patchfile);
        patch_lines++;
        return 0;
  }
  
 -static int handle_filter(char *line, unsigned linesize)
 +static int handle_filter(char *line, unsigned linesize, int linelen)
  {
        static int filter = 0;
  
                        break;
                filter++;
        case 1:
 -              if (!handle_patch(line))
 +              if (!handle_patch(line, linelen))
                        break;
                filter++;
        default:
@@@ -795,7 -794,6 +795,7 @@@ static void handle_body(void
        int rc = 0;
        static char newline[2000];
        static char *np = newline;
 +      int len = strlen(line);
  
        /* Skip up to the first boundary */
        if (content_top->boundary) {
                /* process any boundary lines */
                if (content_top->boundary && is_multipart_boundary(line)) {
                        /* flush any leftover */
 -                      if ((transfer_encoding == TE_BASE64)  &&
 -                          (np != newline)) {
 -                              handle_filter(newline, sizeof(newline));
 -                      }
 +                      if (np != newline)
 +                              handle_filter(newline, sizeof(newline),
 +                                            np - newline);
                        if (!handle_boundary())
                                return;
                }
  
                /* Unwrap transfer encoding */
 -              decode_transfer_encoding(line, sizeof(line));
 +              len = decode_transfer_encoding(line, sizeof(line), len);
 +              if (len < 0) {
 +                      error("Malformed input line");
 +                      return;
 +              }
  
                switch (transfer_encoding) {
                case TE_BASE64:
  
                        /* binary data most likely doesn't have newlines */
                        if (message_type != TYPE_TEXT) {
 -                              rc = handle_filter(line, sizeof(newline));
 +                              rc = handle_filter(line, sizeof(line), len);
                                break;
                        }
  
 -                      /* this is a decoded line that may contain
 +                      /*
 +                       * This is a decoded line that may contain
                         * multiple new lines.  Pass only one chunk
                         * at a time to handle_filter()
                         */
 -
                        do {
 -                              while (*op != '\n' && *op != 0)
 +                              while (op < line + len && *op != '\n')
                                        *np++ = *op++;
                                *np = *op;
                                if (*np != 0) {
                                        /* should be sitting on a new line */
                                        *(++np) = 0;
                                        op++;
 -                                      rc = handle_filter(newline, sizeof(newline));
 +                                      rc = handle_filter(newline, sizeof(newline), np - newline);
                                        np = newline;
                                }
 -                      } while (*op != 0);
 -                      /* the partial chunk is saved in newline and
 -                       * will be appended by the next iteration of fgets
 +                      } while (op < line + len);
 +                      /*
 +                       * The partial chunk is saved in newline and will be
 +                       * appended by the next iteration of read_line_with_nul().
                         */
                        break;
                }
                default:
 -                      rc = handle_filter(line, sizeof(newline));
 +                      rc = handle_filter(line, sizeof(line), len);
                }
                if (rc)
                        /* nothing left to filter */
                        break;
 -      } while (fgets(line, sizeof(line), fin));
 +      } while ((len = read_line_with_nul(line, sizeof(line), fin)));
  
        return;
  }
@@@ -968,7 -962,7 +968,7 @@@ int cmd_mailinfo(int argc, const char *
        /* NEEDSWORK: might want to do the optional .git/ directory
         * discovery
         */
-       git_config(git_default_config);
+       git_config(git_default_config, NULL);
  
        def_charset = (git_commit_encoding ? git_commit_encoding : "utf-8");
        metainfo_charset = def_charset;
index 46e636fdcf1cb854a486e425df05a2485d18d5d7,e16f5e2a4e6a0acb3ea11414a92aed32716393c1..362c290028d68ebc2e589d424bc42cd5e8630a4e
@@@ -92,8 -92,7 +92,8 @@@ static struct path_list current_directo
  
  static int call_depth = 0;
  static int verbosity = 2;
 -static int rename_limit = -1;
 +static int diff_rename_limit = -1;
 +static int merge_rename_limit = -1;
  static int buffer_output = 1;
  static struct strbuf obuf = STRBUF_INIT;
  
@@@ -362,10 -361,7 +362,10 @@@ static struct path_list *get_renames(st
        diff_setup(&opts);
        DIFF_OPT_SET(&opts, RECURSIVE);
        opts.detect_rename = DIFF_DETECT_RENAME;
 -      opts.rename_limit = rename_limit;
 +      opts.rename_limit = merge_rename_limit >= 0 ? merge_rename_limit :
 +                          diff_rename_limit >= 0 ? diff_rename_limit :
 +                          500;
 +      opts.warn_on_too_large_rename = 1;
        opts.output_format = DIFF_FORMAT_NO_OUTPUT;
        if (diff_setup_done(&opts) < 0)
                die("diff setup failed");
@@@ -1340,21 -1336,17 +1340,21 @@@ static struct commit *get_ref(const cha
        return (struct commit *)object;
  }
  
- static int merge_config(const char *var, const char *value)
+ static int merge_config(const char *var, const char *value, void *cb)
  {
        if (!strcasecmp(var, "merge.verbosity")) {
                verbosity = git_config_int(var, value);
                return 0;
        }
        if (!strcasecmp(var, "diff.renamelimit")) {
 -              rename_limit = git_config_int(var, value);
 +              diff_rename_limit = git_config_int(var, value);
 +              return 0;
 +      }
 +      if (!strcasecmp(var, "merge.renamelimit")) {
 +              merge_rename_limit = git_config_int(var, value);
                return 0;
        }
-       return git_default_config(var, value);
+       return git_default_config(var, value, cb);
  }
  
  int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
                        subtree_merge = 1;
        }
  
-       git_config(merge_config);
+       git_config(merge_config, NULL);
        if (getenv("GIT_MERGE_VERBOSITY"))
                verbosity = strtol(getenv("GIT_MERGE_VERBOSITY"), NULL, 10);
  
diff --combined builtin-mv.c
index fb906b3fc7838dd81add9d87e5001d70e83e1e48,3edebef45f9220a7a76ad987debea73612350927..5530e11b89c2c05c95c8ab1e82999eec0fdcc4c5
@@@ -81,7 -81,7 +81,7 @@@ int cmd_mv(int argc, const char **argv
        struct path_list deleted = {NULL, 0, 0, 0};
        struct path_list changed = {NULL, 0, 0, 0};
  
-       git_config(git_default_config);
+       git_config(git_default_config, NULL);
  
        newfd = hold_locked_index(&lock_file, 1);
        if (read_cache() < 0)
  
                for (i = 0; i < added.nr; i++) {
                        const char *path = added.items[i].path;
 -                      add_file_to_cache(path, verbose);
 +                      if (add_file_to_cache(path, verbose ? ADD_CACHE_VERBOSE : 0))
 +                              die("updating index entries failed");
                }
  
                for (i = 0; i < deleted.nr; i++)
diff --combined builtin-pack-objects.c
index f43eb67016ee719d34d2e2f4ae8d77e1ec74b396,a8dc225a722c16d68b6d6e18b633a2aa60ff07dd..70d2f5d4161a0bb62f4734be97d0a8dd2a5098dc
@@@ -28,8 -28,7 +28,8 @@@ git-pack-objects [{ -q | --progress | -
        [--window=N] [--window-memory=N] [--depth=N] \n\
        [--no-reuse-delta] [--no-reuse-object] [--delta-base-offset] \n\
        [--threads=N] [--non-empty] [--revs [--unpacked | --all]*] [--reflog] \n\
 -      [--stdout | base-name] [--include-tag] [--keep-unreachable] \n\
 +      [--stdout | base-name] [--include-tag] \n\
 +      [--keep-unreachable | --unpack-unreachable] \n\
        [<ref-list | <object-list]";
  
  struct object_entry {
@@@ -44,7 -43,6 +44,7 @@@
                                             */
        void *delta_data;       /* cached delta (uncompressed) */
        unsigned long delta_size;       /* delta data size (uncompressed) */
 +      unsigned long z_delta_size;     /* delta data size (compressed) */
        unsigned int hash;      /* name hint hash */
        enum object_type type;
        enum object_type in_pack_type;  /* could be delta */
@@@ -67,8 -65,7 +67,8 @@@ static struct pack_idx_entry **written_
  static uint32_t nr_objects, nr_alloc, nr_result, nr_written;
  
  static int non_empty;
 -static int no_reuse_delta, no_reuse_object, keep_unreachable, include_tag;
 +static int reuse_delta = 1, reuse_object = 1;
 +static int keep_unreachable, unpack_unreachable, include_tag;
  static int local;
  static int incremental;
  static int allow_ofs_delta;
@@@ -105,53 -102,24 +105,53 @@@ static uint32_t written, written_delta
  static uint32_t reused, reused_delta;
  
  
 -static void *delta_against(void *buf, unsigned long size, struct object_entry *entry)
 +static void *get_delta(struct object_entry *entry)
  {
 -      unsigned long othersize, delta_size;
 +      unsigned long size, base_size, delta_size;
 +      void *buf, *base_buf, *delta_buf;
        enum object_type type;
 -      void *otherbuf = read_sha1_file(entry->delta->idx.sha1, &type, &othersize);
 -      void *delta_buf;
  
 -      if (!otherbuf)
 +      buf = read_sha1_file(entry->idx.sha1, &type, &size);
 +      if (!buf)
 +              die("unable to read %s", sha1_to_hex(entry->idx.sha1));
 +      base_buf = read_sha1_file(entry->delta->idx.sha1, &type, &base_size);
 +      if (!base_buf)
                die("unable to read %s", sha1_to_hex(entry->delta->idx.sha1));
 -        delta_buf = diff_delta(otherbuf, othersize,
 +      delta_buf = diff_delta(base_buf, base_size,
                               buf, size, &delta_size, 0);
 -        if (!delta_buf || delta_size != entry->delta_size)
 +      if (!delta_buf || delta_size != entry->delta_size)
                die("delta size changed");
 -        free(buf);
 -        free(otherbuf);
 +      free(buf);
 +      free(base_buf);
        return delta_buf;
  }
  
 +static unsigned long do_compress(void **pptr, unsigned long size)
 +{
 +      z_stream stream;
 +      void *in, *out;
 +      unsigned long maxsize;
 +
 +      memset(&stream, 0, sizeof(stream));
 +      deflateInit(&stream, pack_compression_level);
 +      maxsize = deflateBound(&stream, size);
 +
 +      in = *pptr;
 +      out = xmalloc(maxsize);
 +      *pptr = out;
 +
 +      stream.next_in = in;
 +      stream.avail_in = size;
 +      stream.next_out = out;
 +      stream.avail_out = maxsize;
 +      while (deflate(&stream, Z_FINISH) == Z_OK)
 +              ; /* nothing */
 +      deflateEnd(&stream);
 +
 +      free(in);
 +      return stream.total_out;
 +}
 +
  /*
   * The per-object header is a pretty dense thing, which is
   *  - first byte: low four bits are "size", then three bits of "type",
@@@ -254,42 -222,42 +254,42 @@@ static unsigned long write_object(struc
                                  struct object_entry *entry,
                                  off_t write_offset)
  {
 -      unsigned long size;
 -      enum object_type type;
 +      unsigned long size, limit, datalen;
        void *buf;
 -      unsigned char header[10];
 -      unsigned char dheader[10];
 +      unsigned char header[10], dheader[10];
        unsigned hdrlen;
 -      off_t datalen;
 -      enum object_type obj_type;
 -      int to_reuse = 0;
 -      /* write limit if limited packsize and not first object */
 -      unsigned long limit = pack_size_limit && nr_written ?
 -                              pack_size_limit - write_offset : 0;
 -                              /* no if no delta */
 -      int usable_delta =      !entry->delta ? 0 :
 -                              /* yes if unlimited packfile */
 -                              !pack_size_limit ? 1 :
 -                              /* no if base written to previous pack */
 -                              entry->delta->idx.offset == (off_t)-1 ? 0 :
 -                              /* otherwise double-check written to this
 -                               * pack,  like we do below
 -                               */
 -                              entry->delta->idx.offset ? 1 : 0;
 +      enum object_type type;
 +      int usable_delta, to_reuse;
  
        if (!pack_to_stdout)
                crc32_begin(f);
  
 -      obj_type = entry->type;
 -      if (no_reuse_object)
 +      type = entry->type;
 +
 +      /* write limit if limited packsize and not first object */
 +      limit = pack_size_limit && nr_written ?
 +                      pack_size_limit - write_offset : 0;
 +
 +      if (!entry->delta)
 +              usable_delta = 0;       /* no delta */
 +      else if (!pack_size_limit)
 +             usable_delta = 1;        /* unlimited packfile */
 +      else if (entry->delta->idx.offset == (off_t)-1)
 +              usable_delta = 0;       /* base was written to another pack */
 +      else if (entry->delta->idx.offset)
 +              usable_delta = 1;       /* base already exists in this pack */
 +      else
 +              usable_delta = 0;       /* base could end up in another pack */
 +
 +      if (!reuse_object)
                to_reuse = 0;   /* explicit */
        else if (!entry->in_pack)
                to_reuse = 0;   /* can't reuse what we don't have */
 -      else if (obj_type == OBJ_REF_DELTA || obj_type == OBJ_OFS_DELTA)
 +      else if (type == OBJ_REF_DELTA || type == OBJ_OFS_DELTA)
                                /* check_object() decided it for us ... */
                to_reuse = usable_delta;
                                /* ... but pack split may override that */
 -      else if (obj_type != entry->in_pack_type)
 +      else if (type != entry->in_pack_type)
                to_reuse = 0;   /* pack has delta which is unusable */
        else if (entry->delta)
                to_reuse = 0;   /* we want to pack afresh */
                                 */
  
        if (!to_reuse) {
 -              z_stream stream;
 -              unsigned long maxsize;
 -              void *out;
                if (!usable_delta) {
 -                      buf = read_sha1_file(entry->idx.sha1, &obj_type, &size);
 +                      buf = read_sha1_file(entry->idx.sha1, &type, &size);
                        if (!buf)
                                die("unable to read %s", sha1_to_hex(entry->idx.sha1));
 +                      /*
 +                       * make sure no cached delta data remains from a
 +                       * previous attempt before a pack split occured.
 +                       */
 +                      free(entry->delta_data);
 +                      entry->delta_data = NULL;
 +                      entry->z_delta_size = 0;
                } else if (entry->delta_data) {
                        size = entry->delta_size;
                        buf = entry->delta_data;
                        entry->delta_data = NULL;
 -                      obj_type = (allow_ofs_delta && entry->delta->idx.offset) ?
 +                      type = (allow_ofs_delta && entry->delta->idx.offset) ?
                                OBJ_OFS_DELTA : OBJ_REF_DELTA;
                } else {
 -                      buf = read_sha1_file(entry->idx.sha1, &type, &size);
 -                      if (!buf)
 -                              die("unable to read %s", sha1_to_hex(entry->idx.sha1));
 -                      buf = delta_against(buf, size, entry);
 +                      buf = get_delta(entry);
                        size = entry->delta_size;
 -                      obj_type = (allow_ofs_delta && entry->delta->idx.offset) ?
 +                      type = (allow_ofs_delta && entry->delta->idx.offset) ?
                                OBJ_OFS_DELTA : OBJ_REF_DELTA;
                }
 -              /* compress the data to store and put compressed length in datalen */
 -              memset(&stream, 0, sizeof(stream));
 -              deflateInit(&stream, pack_compression_level);
 -              maxsize = deflateBound(&stream, size);
 -              out = xmalloc(maxsize);
 -              /* Compress it */
 -              stream.next_in = buf;
 -              stream.avail_in = size;
 -              stream.next_out = out;
 -              stream.avail_out = maxsize;
 -              while (deflate(&stream, Z_FINISH) == Z_OK)
 -                      /* nothing */;
 -              deflateEnd(&stream);
 -              datalen = stream.total_out;
 +
 +              if (entry->z_delta_size)
 +                      datalen = entry->z_delta_size;
 +              else
 +                      datalen = do_compress(&buf, size);
  
                /*
                 * The object header is a byte of 'type' followed by zero or
                 * more bytes of length.
                 */
 -              hdrlen = encode_header(obj_type, size, header);
 +              hdrlen = encode_header(type, size, header);
  
 -              if (obj_type == OBJ_OFS_DELTA) {
 +              if (type == OBJ_OFS_DELTA) {
                        /*
                         * Deltas with relative base contain an additional
                         * encoding of the relative offset for the delta
                        while (ofs >>= 7)
                                dheader[--pos] = 128 | (--ofs & 127);
                        if (limit && hdrlen + sizeof(dheader) - pos + datalen + 20 >= limit) {
 -                              free(out);
                                free(buf);
                                return 0;
                        }
                        sha1write(f, header, hdrlen);
                        sha1write(f, dheader + pos, sizeof(dheader) - pos);
                        hdrlen += sizeof(dheader) - pos;
 -              } else if (obj_type == OBJ_REF_DELTA) {
 +              } else if (type == OBJ_REF_DELTA) {
                        /*
                         * Deltas with a base reference contain
                         * an additional 20 bytes for the base sha1.
                         */
                        if (limit && hdrlen + 20 + datalen + 20 >= limit) {
 -                              free(out);
                                free(buf);
                                return 0;
                        }
                        hdrlen += 20;
                } else {
                        if (limit && hdrlen + datalen + 20 >= limit) {
 -                              free(out);
                                free(buf);
                                return 0;
                        }
                        sha1write(f, header, hdrlen);
                }
 -              sha1write(f, out, datalen);
 -              free(out);
 +              sha1write(f, buf, datalen);
                free(buf);
        }
        else {
                off_t offset;
  
                if (entry->delta) {
 -                      obj_type = (allow_ofs_delta && entry->delta->idx.offset) ?
 +                      type = (allow_ofs_delta && entry->delta->idx.offset) ?
                                OBJ_OFS_DELTA : OBJ_REF_DELTA;
                        reused_delta++;
                }
 -              hdrlen = encode_header(obj_type, entry->size, header);
 +              hdrlen = encode_header(type, entry->size, header);
                offset = entry->in_pack_offset;
                revidx = find_pack_revindex(p, offset);
                datalen = revidx[1].offset - offset;
                        die("bad packed object CRC for %s", sha1_to_hex(entry->idx.sha1));
                offset += entry->in_pack_header_size;
                datalen -= entry->in_pack_header_size;
 -              if (obj_type == OBJ_OFS_DELTA) {
 +              if (type == OBJ_OFS_DELTA) {
                        off_t ofs = entry->idx.offset - entry->delta->idx.offset;
                        unsigned pos = sizeof(dheader) - 1;
                        dheader[pos] = ofs & 127;
                        sha1write(f, header, hdrlen);
                        sha1write(f, dheader + pos, sizeof(dheader) - pos);
                        hdrlen += sizeof(dheader) - pos;
 -              } else if (obj_type == OBJ_REF_DELTA) {
 +              } else if (type == OBJ_REF_DELTA) {
                        if (limit && hdrlen + 20 + datalen + 20 >= limit)
                                return 0;
                        sha1write(f, header, hdrlen);
@@@ -472,10 -452,11 +472,10 @@@ static void write_pack_file(void
        struct sha1file *f;
        off_t offset, offset_one, last_obj_offset = 0;
        struct pack_header hdr;
 -      int do_progress = progress >> pack_to_stdout;
        uint32_t nr_remaining = nr_result;
        time_t last_mtime = 0;
  
 -      if (do_progress)
 +      if (progress > pack_to_stdout)
                progress_state = start_progress("Writing objects", nr_result);
        written_list = xmalloc(nr_objects * sizeof(*written_list));
  
@@@ -1041,7 -1022,7 +1041,7 @@@ static void check_object(struct object_
                        unuse_pack(&w_curs);
                        return;
                case OBJ_REF_DELTA:
 -                      if (!no_reuse_delta && !entry->preferred_base)
 +                      if (reuse_delta && !entry->preferred_base)
                                base_ref = use_pack(p, &w_curs,
                                                entry->in_pack_offset + used, NULL);
                        entry->in_pack_header_size = used + 20;
                                die("delta base offset out of bound for %s",
                                    sha1_to_hex(entry->idx.sha1));
                        ofs = entry->in_pack_offset - ofs;
 -                      if (!no_reuse_delta && !entry->preferred_base) {
 +                      if (reuse_delta && !entry->preferred_base) {
                                struct revindex_entry *revidx;
                                revidx = find_pack_revindex(p, ofs);
                                base_ref = nth_packed_object_sha1(p, revidx->nr);
@@@ -1252,7 -1233,7 +1252,7 @@@ static int try_delta(struct unpacked *t
         * We do not bother to try a delta that we discarded
         * on an earlier try, but only when reusing delta data.
         */
 -      if (!no_reuse_delta && trg_entry->in_pack &&
 +      if (reuse_delta && trg_entry->in_pack &&
            trg_entry->in_pack == src_entry->in_pack &&
            trg_entry->in_pack_type != OBJ_REF_DELTA &&
            trg_entry->in_pack_type != OBJ_OFS_DELTA)
@@@ -1460,34 -1441,11 +1460,34 @@@ static void find_deltas(struct object_e
                                best_base = other_idx;
                }
  
 +              /*
 +               * If we decided to cache the delta data, then it is best
 +               * to compress it right away.  First because we have to do
 +               * it anyway, and doing it here while we're threaded will
 +               * save a lot of time in the non threaded write phase,
 +               * as well as allow for caching more deltas within
 +               * the same cache size limit.
 +               * ...
 +               * But only if not writing to stdout, since in that case
 +               * the network is most likely throttling writes anyway,
 +               * and therefore it is best to go to the write phase ASAP
 +               * instead, as we can afford spending more time compressing
 +               * between writes at that moment.
 +               */
 +              if (entry->delta_data && !pack_to_stdout) {
 +                      entry->z_delta_size = do_compress(&entry->delta_data,
 +                                                        entry->delta_size);
 +                      cache_lock();
 +                      delta_cache_size -= entry->delta_size;
 +                      delta_cache_size += entry->z_delta_size;
 +                      cache_unlock();
 +              }
 +
                /* if we made n a delta, and if n is already at max
                 * depth, leaving it in the window is pointless.  we
                 * should evict it first.
                 */
 -              if (entry->delta && depth <= n->depth)
 +              if (entry->delta && max_depth <= n->depth)
                        continue;
  
                /*
@@@ -1730,7 -1688,7 +1730,7 @@@ static void prepare_pack(int window, in
  
                if (entry->delta)
                        /* This happens if we decided to reuse existing
 -                       * delta from a pack.  "!no_reuse_delta &&" is implied.
 +                       * delta from a pack.  "reuse_delta &&" is implied.
                         */
                        continue;
  
        free(delta_list);
  }
  
- static int git_pack_config(const char *k, const char *v)
+ static int git_pack_config(const char *k, const char *v, void *cb)
  {
        if(!strcmp(k, "pack.window")) {
                window = git_config_int(k, v);
                pack_size_limit_cfg = git_config_ulong(k, v);
                return 0;
        }
-       return git_default_config(k, v);
+       return git_default_config(k, v, cb);
  }
  
  static void read_object_list_from_stdin(void)
@@@ -1947,32 -1905,6 +1947,32 @@@ static void add_objects_in_unpacked_pac
        free(in_pack.array);
  }
  
 +static void loosen_unused_packed_objects(struct rev_info *revs)
 +{
 +      struct packed_git *p;
 +      uint32_t i;
 +      const unsigned char *sha1;
 +
 +      for (p = packed_git; p; p = p->next) {
 +              for (i = 0; i < revs->num_ignore_packed; i++) {
 +                      if (matches_pack_name(p, revs->ignore_packed[i]))
 +                              break;
 +              }
 +              if (revs->num_ignore_packed <= i)
 +                      continue;
 +
 +              if (open_pack_index(p))
 +                      die("cannot open pack index");
 +
 +              for (i = 0; i < p->num_objects; i++) {
 +                      sha1 = nth_packed_object_sha1(p, i);
 +                      if (!locate_object_entry(sha1))
 +                              if (force_object_loose(sha1, p->mtime))
 +                                      die("unable to force loose object");
 +              }
 +      }
 +}
 +
  static void get_object_list(int ac, const char **av)
  {
        struct rev_info revs;
  
        if (keep_unreachable)
                add_objects_in_unpacked_packs(&revs);
 +      if (unpack_unreachable)
 +              loosen_unused_packed_objects(&revs);
  }
  
  static int adjust_perm(const char *path, mode_t mode)
@@@ -2033,7 -1963,7 +2033,7 @@@ int cmd_pack_objects(int argc, const ch
        rp_av[1] = "--objects"; /* --thin will make it --objects-edge */
        rp_ac = 2;
  
-       git_config(git_pack_config);
+       git_config(git_pack_config, NULL);
        if (!pack_compression_seen && core_compression_seen)
                pack_compression_level = core_compression_level;
  
                        continue;
                }
                if (!strcmp("--no-reuse-delta", arg)) {
 -                      no_reuse_delta = 1;
 +                      reuse_delta = 0;
                        continue;
                }
                if (!strcmp("--no-reuse-object", arg)) {
 -                      no_reuse_object = no_reuse_delta = 1;
 +                      reuse_object = reuse_delta = 0;
                        continue;
                }
                if (!strcmp("--delta-base-offset", arg)) {
                        keep_unreachable = 1;
                        continue;
                }
 +              if (!strcmp("--unpack-unreachable", arg)) {
 +                      unpack_unreachable = 1;
 +                      continue;
 +              }
                if (!strcmp("--include-tag", arg)) {
                        include_tag = 1;
                        continue;
        if (!pack_to_stdout && thin)
                die("--thin cannot be used to build an indexable pack.");
  
 +      if (keep_unreachable && unpack_unreachable)
 +              die("--keep-unreachable and --unpack-unreachable are incompatible.");
 +
  #ifdef THREADED_DELTA_SEARCH
        if (!delta_search_threads)      /* --threads=0 means autodetect */
                delta_search_threads = online_cpus();
diff --combined builtin-rev-list.c
index b474527402262fa821a53bc2cc0ca53b74014d8e,274299044ea9c55221298303e212580810ed2006..83a7b1349e06dbf1a355888272d9b13a7d4c22c4
@@@ -10,7 -10,6 +10,7 @@@
  #include "list-objects.h"
  #include "builtin.h"
  #include "log-tree.h"
 +#include "graph.h"
  
  /* bits #0-15 in revision.h */
  
@@@ -59,31 -58,26 +59,31 @@@ static const char *header_prefix
  static void finish_commit(struct commit *commit);
  static void show_commit(struct commit *commit)
  {
 +      graph_show_commit(revs.graph);
 +
        if (show_timestamp)
                printf("%lu ", commit->date);
        if (header_prefix)
                fputs(header_prefix, stdout);
 -      if (commit->object.flags & BOUNDARY)
 -              putchar('-');
 -      else if (commit->object.flags & UNINTERESTING)
 -              putchar('^');
 -      else if (revs.left_right) {
 -              if (commit->object.flags & SYMMETRIC_LEFT)
 -                      putchar('<');
 -              else
 -                      putchar('>');
 +
 +      if (!revs.graph) {
 +              if (commit->object.flags & BOUNDARY)
 +                      putchar('-');
 +              else if (commit->object.flags & UNINTERESTING)
 +                      putchar('^');
 +              else if (revs.left_right) {
 +                      if (commit->object.flags & SYMMETRIC_LEFT)
 +                              putchar('<');
 +                      else
 +                              putchar('>');
 +              }
        }
        if (revs.abbrev_commit && revs.abbrev)
                fputs(find_unique_abbrev(commit->object.sha1, revs.abbrev),
                      stdout);
        else
                fputs(sha1_to_hex(commit->object.sha1), stdout);
 -      if (revs.parents) {
 +      if (revs.print_parents) {
                struct commit_list *parents = commit->parents;
                while (parents) {
                        printf(" %s", sha1_to_hex(parents->item->object.sha1));
                pretty_print_commit(revs.commit_format, commit,
                                    &buf, revs.abbrev, NULL, NULL,
                                    revs.date_mode, 0);
 -              if (buf.len)
 -                      printf("%s%c", buf.buf, hdr_termination);
 +              if (revs.graph) {
 +                      if (buf.len) {
 +                              if (revs.commit_format != CMIT_FMT_ONELINE)
 +                                      graph_show_oneline(revs.graph);
 +
 +                              graph_show_commit_msg(revs.graph, &buf);
 +
 +                              /*
 +                               * Add a newline after the commit message.
 +                               *
 +                               * Usually, this newline produces a blank
 +                               * padding line between entries, in which case
 +                               * we need to add graph padding on this line.
 +                               *
 +                               * However, the commit message may not end in a
 +                               * newline.  In this case the newline simply
 +                               * ends the last line of the commit message,
 +                               * and we don't need any graph output.  (This
 +                               * always happens with CMIT_FMT_ONELINE, and it
 +                               * happens with CMIT_FMT_USERFORMAT when the
 +                               * format doesn't explicitly end in a newline.)
 +                               */
 +                              if (buf.len && buf.buf[buf.len - 1] == '\n')
 +                                      graph_show_padding(revs.graph);
 +                              putchar('\n');
 +                      } else {
 +                              /*
 +                               * If the message buffer is empty, just show
 +                               * the rest of the graph output for this
 +                               * commit.
 +                               */
 +                              if (graph_show_remainder(revs.graph))
 +                                      putchar('\n');
 +                      }
 +              } else {
 +                      if (buf.len)
 +                              printf("%s%c", buf.buf, hdr_termination);
 +              }
                strbuf_release(&buf);
 +      } else {
 +              if (graph_show_remainder(revs.graph))
 +                      putchar('\n');
        }
        maybe_flush_or_die(stdout, "stdout");
        finish_commit(commit);
@@@ -591,7 -546,7 +591,7 @@@ int cmd_rev_list(int argc, const char *
        int bisect_find_all = 0;
        int quiet = 0;
  
-       git_config(git_default_config);
+       git_config(git_default_config, NULL);
        init_revisions(&revs, prefix);
        revs.abbrev = 0;
        revs.commit_format = CMIT_FMT_UNSPECIFIED;
diff --combined builtin-rev-parse.c
index ab3e85054e174d2d328c625f1db744854b62f5f7,1d019b3c16a3ac5eae4c14921534f653fa9c71c1..a7860ed75ac4a7b1895bfe088e34a39b24448f57
@@@ -94,14 -94,6 +94,14 @@@ static void show(const char *arg
                puts(arg);
  }
  
 +/* Like show(), but with a negation prefix according to type */
 +static void show_with_type(int type, const char *arg)
 +{
 +      if (type != show_type)
 +              putchar('^');
 +      show(arg);
 +}
 +
  /* Output a revision, only if filter allows it */
  static void show_rev(int type, const unsigned char *sha1, const char *name)
  {
                return;
        def = NULL;
  
 -      if (type != show_type)
 -              putchar('^');
        if (symbolic && name) {
                if (symbolic == SHOW_SYMBOLIC_FULL) {
                        unsigned char discard[20];
                                 */
                                break;
                        case 1: /* happy */
 -                              show(full);
 +                              show_with_type(type, full);
                                break;
                        default: /* ambiguous */
                                error("refname '%s' is ambiguous", name);
                                break;
                        }
                } else {
 -                      show(name);
 +                      show_with_type(type, name);
                }
        }
        else if (abbrev)
 -              show(find_unique_abbrev(sha1, abbrev));
 +              show_with_type(type, find_unique_abbrev(sha1, abbrev));
        else
 -              show(sha1_to_hex(sha1));
 +              show_with_type(type, sha1_to_hex(sha1));
  }
  
  /* Output a flag, only if filter allows it. */
@@@ -387,7 -381,7 +387,7 @@@ int cmd_rev_parse(int argc, const char 
                return cmd_parseopt(argc - 1, argv + 1, prefix);
  
        prefix = setup_git_directory();
-       git_config(git_default_config);
+       git_config(git_default_config, NULL);
        for (i = 1; i < argc; i++) {
                const char *arg = argv[i];
  
diff --combined builtin-update-index.c
index d4c85c0cbc6fec3d526cc57d8b748b1f1a82b9a1,e1ca8deac2158f06208e69e2dc31fff48fac4f88..9e0d7ab11ee01c6a1def223820038a94113f348c
@@@ -567,7 -567,7 +567,7 @@@ int cmd_update_index(int argc, const ch
        int lock_error = 0;
        struct lock_file *lock_file;
  
-       git_config(git_default_config);
+       git_config(git_default_config, NULL);
  
        /* We can't free this memory, it becomes part of a linked list parsed atexit() */
        lock_file = xcalloc(1, sizeof(struct lock_file));
                                refresh_flags |= REFRESH_QUIET;
                                continue;
                        }
 +                      if (!strcmp(path, "--ignore-submodules")) {
 +                              refresh_flags |= REFRESH_IGNORE_SUBMODULES;
 +                              continue;
 +                      }
                        if (!strcmp(path, "--add")) {
                                allow_add = 1;
                                continue;
diff --combined cache.h
index 943bee9e7da6ce2b903ab9331b35430ecb09644e,ebe4031b65dc0e2ff9f9c05e9a423d70c9b17ba1..eab1a172fe4b2a9d735dc480879fd1034da7942a
+++ b/cache.h
@@@ -261,8 -261,8 +261,8 @@@ static inline void remove_name_hash(str
  #define add_cache_entry(ce, option) add_index_entry(&the_index, (ce), (option))
  #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, verbose) add_to_index(&the_index, (path), (st), (verbose))
 -#define add_file_to_cache(path, verbose) add_file_to_index(&the_index, (path), (verbose))
 +#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 refresh_cache(flags) refresh_index(&the_index, (flags), 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))
@@@ -317,7 -317,6 +317,7 @@@ extern char *get_graft_file(void)
  extern int set_git_dir(const char *path);
  extern const char *get_git_work_tree(void);
  extern const char *read_gitfile_gently(const char *path);
 +extern void set_git_work_tree(const char *tree);
  
  #define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
  
@@@ -330,10 -329,6 +330,10 @@@ extern const char *prefix_filename(cons
  extern void verify_filename(const char *prefix, const char *name);
  extern void verify_non_filename(const char *prefix, const char *name);
  
 +#define INIT_DB_QUIET 0x0001
 +
 +extern int init_db(const char *template_dir, unsigned int flags);
 +
  #define alloc_nr(x) (((x)+16)*3/2)
  
  /*
@@@ -371,11 -366,8 +371,11 @@@ extern int add_index_entry(struct index
  extern struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really);
  extern int remove_index_entry_at(struct index_state *, int pos);
  extern int remove_file_from_index(struct index_state *, const char *path);
 -extern int add_to_index(struct index_state *, const char *path, struct stat *, int verbose);
 -extern int add_file_to_index(struct index_state *, const char *path, int verbose);
 +#define ADD_CACHE_VERBOSE 1
 +#define ADD_CACHE_PRETEND 2
 +#define ADD_CACHE_IGNORE_ERRORS       4
 +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, int refresh);
  extern int ce_same_name(struct cache_entry *a, struct cache_entry *b);
  
@@@ -396,7 -388,6 +396,7 @@@ extern void fill_stat_cache_info(struc
  #define REFRESH_UNMERGED      0x0002  /* allow unmerged */
  #define REFRESH_QUIET         0x0004  /* be quiet about it */
  #define REFRESH_IGNORE_MISSING        0x0008  /* ignore non-existent */
 +#define REFRESH_IGNORE_SUBMODULES     0x0008  /* ignore submodules */
  extern int refresh_index(struct index_state *, unsigned int flags, const char **pathspec, char *seen);
  
  struct lock_file {
        char filename[PATH_MAX];
  };
  extern int hold_lock_file_for_update(struct lock_file *, const char *path, int);
 +extern int hold_lock_file_for_append(struct lock_file *, const char *path, int);
  extern int commit_lock_file(struct lock_file *);
  
  extern int hold_locked_index(struct lock_file *, int);
@@@ -531,7 -521,6 +531,7 @@@ extern void * read_sha1_file(const unsi
  extern int hash_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *sha1);
  extern int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *return_sha1);
  extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *);
 +extern int force_object_loose(const unsigned char *sha1, time_t mtime);
  
  extern int check_sha1_signature(const unsigned char *sha1, void *buf, unsigned long size, const char *type);
  
@@@ -625,7 -614,6 +625,7 @@@ extern struct alternate_object_databas
        char base[FLEX_ARRAY]; /* more */
  } *alt_odb_list;
  extern void prepare_alt_odb(void);
 +extern void add_to_alternates_file(const char *reference);
  
  struct pack_window {
        struct pack_window *next;
@@@ -723,10 -711,10 +723,10 @@@ extern int matches_pack_name(struct pac
  /* Dumb servers support */
  extern int update_server_info(int);
  
- typedef int (*config_fn_t)(const char *, const char *);
- extern int git_default_config(const char *, const char *);
- extern int git_config_from_file(config_fn_t fn, const char *);
- extern int git_config(config_fn_t fn);
+ 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(config_fn_t fn, void *);
  extern int git_parse_long(const char *, long *);
  extern int git_parse_ulong(const char *, unsigned long *);
  extern int git_config_int(const char *, const char *);
@@@ -738,7 -726,7 +738,7 @@@ extern int git_config_set(const char *
  extern int git_config_set_multivar(const char *, const char *, const char *, int);
  extern int git_config_rename_section(const char *, const char *);
  extern const char *git_etc_gitconfig(void);
- extern int check_repository_format_version(const char *var, const char *value);
+ extern int check_repository_format_version(const char *var, const char *value, void *cb);
  extern int git_env_bool(const char *, int);
  extern int git_config_system(void);
  extern int git_config_global(void);
@@@ -747,7 -735,6 +747,7 @@@ extern int config_error_nonbool(const c
  #define MAX_GITNAME (1000)
  extern char git_default_email[MAX_GITNAME];
  extern char git_default_name[MAX_GITNAME];
 +extern int user_ident_explicitly_given;
  
  extern const char *git_commit_encoding;
  extern const char *git_log_output_encoding;
@@@ -794,11 -781,7 +794,11 @@@ extern int convert_to_git(const char *p
  extern int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst);
  
  /* add */
 -void add_files_to_cache(int verbose, const char *prefix, const char **pathspec);
 +/*
 + * 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 char **pathspec, int flags);
  
  /* diff.c */
  extern int diff_auto_refresh_index;
diff --combined config.c
index 5195de10c9289a25053756ce799e654d5e01483e,5431e2dc01b01b60de6e1392e242c9b0e5d2a8cf..c2f2bbb000cd003002c1bd346e6be6c9d3ef5c2d
+++ b/config.c
@@@ -111,7 -111,7 +111,7 @@@ static inline int iskeychar(int c
        return isalnum(c) || c == '-';
  }
  
- static int get_value(config_fn_t fn, char *name, unsigned int len)
+ static int get_value(config_fn_t fn, void *data, char *name, unsigned int len)
  {
        int c;
        char *value;
                if (!value)
                        return -1;
        }
-       return fn(name, value);
+       return fn(name, value, data);
  }
  
  static int get_extended_base_var(char *name, int baselen, int c)
@@@ -197,7 -197,7 +197,7 @@@ static int get_base_var(char *name
        }
  }
  
- static int git_parse_file(config_fn_t fn)
+ static int git_parse_file(config_fn_t fn, void *data)
  {
        int comment = 0;
        int baselen = 0;
                if (!isalpha(c))
                        break;
                var[baselen] = tolower(c);
-               if (get_value(fn, var, baselen+1) < 0)
+               if (get_value(fn, data, var, baselen+1) < 0)
                        break;
        }
        die("bad config file line %d in %s", config_linenr, config_file_name);
@@@ -332,7 -332,7 +332,7 @@@ int git_config_string(const char **dest
        return 0;
  }
  
- int git_default_config(const char *var, const char *value)
+ int git_default_config(const char *var, const char *value, void *dummy)
  {
        /* This needs a better name */
        if (!strcmp(var, "core.filemode")) {
                if (!value)
                        return config_error_nonbool(var);
                strlcpy(git_default_name, value, sizeof(git_default_name));
 +              if (git_default_email[0])
 +                      user_ident_explicitly_given = 1;
                return 0;
        }
  
                if (!value)
                        return config_error_nonbool(var);
                strlcpy(git_default_email, value, sizeof(git_default_email));
 +              if (git_default_name[0])
 +                      user_ident_explicitly_given = 1;
                return 0;
        }
  
        return 0;
  }
  
- int git_config_from_file(config_fn_t fn, const char *filename)
+ int git_config_from_file(config_fn_t fn, const char *filename, void *data)
  {
        int ret;
        FILE *f = fopen(filename, "r");
                config_file_name = filename;
                config_linenr = 1;
                config_file_eof = 0;
-               ret = git_parse_file(fn);
+               ret = git_parse_file(fn, data);
                fclose(f);
                config_file_name = NULL;
        }
@@@ -565,7 -561,7 +565,7 @@@ int git_config_global(void
        return !git_env_bool("GIT_CONFIG_NOGLOBAL", 0);
  }
  
- int git_config(config_fn_t fn)
+ int git_config(config_fn_t fn, void *data)
  {
        int ret = 0;
        char *repo_config = NULL;
        filename = getenv(CONFIG_ENVIRONMENT);
        if (!filename) {
                if (git_config_system() && !access(git_etc_gitconfig(), R_OK))
-                       ret += git_config_from_file(fn, git_etc_gitconfig());
+                       ret += git_config_from_file(fn, git_etc_gitconfig(),
+                               data);
                home = getenv("HOME");
                filename = getenv(CONFIG_LOCAL_ENVIRONMENT);
                if (!filename)
        if (git_config_global() && home) {
                char *user_config = xstrdup(mkpath("%s/.gitconfig", home));
                if (!access(user_config, R_OK))
-                       ret = git_config_from_file(fn, user_config);
+                       ret = git_config_from_file(fn, user_config, data);
                free(user_config);
        }
  
-       ret += git_config_from_file(fn, filename);
+       ret += git_config_from_file(fn, filename, data);
        free(repo_config);
        return ret;
  }
@@@ -622,7 -619,7 +623,7 @@@ static int matches(const char* key, con
                  !regexec(store.value_regex, value, 0, NULL, 0)));
  }
  
- static int store_aux(const char* key, const char* value)
+ static int store_aux(const char* key, const char* value, void *cb)
  {
        const char *ep;
        size_t section_len;
@@@ -702,7 -699,7 +703,7 @@@ static int store_write_section(int fd, 
        if (dot) {
                strbuf_addf(&sb, "[%.*s \"", (int)(dot - key), key);
                for (i = dot - key + 1; i < store.baselen; i++) {
 -                      if (key[i] == '"')
 +                      if (key[i] == '"' || key[i] == '\\')
                                strbuf_addch(&sb, '\\');
                        strbuf_addch(&sb, key[i]);
                }
@@@ -951,7 -948,7 +952,7 @@@ int git_config_set_multivar(const char
                 * As a side effect, we make sure to transform only a valid
                 * existing config file.
                 */
-               if (git_config_from_file(store_aux, config_filename)) {
+               if (git_config_from_file(store_aux, config_filename, NULL)) {
                        error("invalid config file %s", config_filename);
                        free(store.key);
                        if (store.value_regex != NULL) {
diff --combined diff.c
index d57bc299ee7499dc8f448220e894cc6e6a1d8aca,1f46ff0f083fd307601b1e3e34e6626ec6801704..62fdc5492bbe5dacab6bc5b615a76ba5cd9de760
--- 1/diff.c
--- 2/diff.c
+++ b/diff.c
@@@ -19,7 -19,7 +19,7 @@@
  #endif
  
  static int diff_detect_rename_default;
 -static int diff_rename_limit_default = 100;
 +static int diff_rename_limit_default = 200;
  int diff_use_color_default = -1;
  static const char *external_diff_cmd_cfg;
  int diff_auto_refresh_index = 1;
@@@ -129,7 -129,7 +129,7 @@@ static int parse_funcname_pattern(cons
   * never be affected by the setting of diff.renames
   * the user happens to have in the configuration file.
   */
- int git_diff_ui_config(const char *var, const char *value)
+ int git_diff_ui_config(const char *var, const char *value, void *cb)
  {
        if (!strcmp(var, "diff.renamelimit")) {
                diff_rename_limit_default = git_config_int(var, value);
                        return parse_lldiff_command(var, ep, value);
        }
  
-       return git_diff_basic_config(var, value);
+       return git_diff_basic_config(var, value, cb);
  }
  
- int git_diff_basic_config(const char *var, const char *value)
+ int git_diff_basic_config(const char *var, const char *value, void *cb)
  {
        if (!prefixcmp(var, "diff.color.") || !prefixcmp(var, "color.diff.")) {
                int slot = parse_diff_color_slot(var, 11);
                }
        }
  
-       return git_color_default_config(var, value);
+       return git_color_default_config(var, value, cb);
  }
  
  static char *quote_two(const char *one, const char *two)
@@@ -2496,8 -2496,6 +2496,8 @@@ int diff_opt_parse(struct diff_options 
                DIFF_OPT_SET(options, ALLOW_EXTERNAL);
        else if (!strcmp(arg, "--no-ext-diff"))
                DIFF_OPT_CLR(options, ALLOW_EXTERNAL);
 +      else if (!strcmp(arg, "--ignore-submodules"))
 +              DIFF_OPT_SET(options, IGNORE_SUBMODULES);
  
        /* misc options */
        else if (!strcmp(arg, "-z"))
@@@ -3357,9 -3355,6 +3357,9 @@@ void diff_addremove(struct diff_option
        char concatpath[PATH_MAX];
        struct diff_filespec *one, *two;
  
 +      if (DIFF_OPT_TST(options, IGNORE_SUBMODULES) && S_ISGITLINK(mode))
 +              return;
 +
        /* This may look odd, but it is a preparation for
         * feeding "there are unchanged files which should
         * not produce diffs, but when you are doing copy
@@@ -3404,10 -3399,6 +3404,10 @@@ void diff_change(struct diff_options *o
        char concatpath[PATH_MAX];
        struct diff_filespec *one, *two;
  
 +      if (DIFF_OPT_TST(options, IGNORE_SUBMODULES) && S_ISGITLINK(old_mode)
 +                      && S_ISGITLINK(new_mode))
 +              return;
 +
        if (DIFF_OPT_TST(options, REVERSE_DIFF)) {
                unsigned tmp;
                const unsigned char *tmp_c;
diff --combined diff.h
index 1dfe1f98b1dbe66fd3c7c5cd5a999c28172cd8cc,1cf1eff485e58b3714e495888a3b26cf3b005049..b0b700a5f6ec71174c40285f376fc1b2bfb34e3a
--- 1/diff.h
--- 2/diff.h
+++ b/diff.h
@@@ -63,7 -63,6 +63,7 @@@ typedef void (*diff_format_fn_t)(struc
  #define DIFF_OPT_REVERSE_DIFF        (1 << 15)
  #define DIFF_OPT_CHECK_FAILED        (1 << 16)
  #define DIFF_OPT_RELATIVE_NAME       (1 << 17)
 +#define DIFF_OPT_IGNORE_SUBMODULES   (1 << 18)
  #define DIFF_OPT_TST(opts, flag)    ((opts)->flags & DIFF_OPT_##flag)
  #define DIFF_OPT_SET(opts, flag)    ((opts)->flags |= DIFF_OPT_##flag)
  #define DIFF_OPT_CLR(opts, flag)    ((opts)->flags &= ~DIFF_OPT_##flag)
@@@ -84,7 -83,6 +84,7 @@@ struct diff_options 
        int pickaxe_opts;
        int rename_score;
        int rename_limit;
 +      int warn_on_too_large_rename;
        int dirstat_percent;
        int setup;
        int abbrev;
@@@ -182,8 -180,8 +182,8 @@@ extern void diff_unmerge(struct diff_op
  #define DIFF_SETUP_USE_CACHE          2
  #define DIFF_SETUP_USE_SIZE_CACHE     4
  
- extern int git_diff_basic_config(const char *var, const char *value);
- extern int git_diff_ui_config(const char *var, const char *value);
+ extern int git_diff_basic_config(const char *var, const char *value, void *cb);
+ extern int git_diff_ui_config(const char *var, const char *value, void *cb);
  extern int diff_use_color_default;
  extern void diff_setup(struct diff_options *);
  extern int diff_opt_parse(struct diff_options *, const char **, int);
diff --combined fast-import.c
index caea684338031431dc3177716654a1a289faa9a3,36ec5b87f8878dacedc9fc3f4acc5b51644a4b71..93119bbd9484504d19f069a5c0038fb1e2f40a26
@@@ -1690,7 -1690,7 +1690,7 @@@ static void skip_optional_lf(void
                ungetc(term_char, stdin);
  }
  
 -static void cmd_mark(void)
 +static void parse_mark(void)
  {
        if (!prefixcmp(command_buf.buf, "mark :")) {
                next_mark = strtoumax(command_buf.buf + 6, NULL, 10);
                next_mark = 0;
  }
  
 -static void cmd_data(struct strbuf *sb)
 +static void parse_data(struct strbuf *sb)
  {
        strbuf_reset(sb);
  
@@@ -1798,13 -1798,13 +1798,13 @@@ static char *parse_ident(const char *bu
        return ident;
  }
  
 -static void cmd_new_blob(void)
 +static void parse_new_blob(void)
  {
        static struct strbuf buf = STRBUF_INIT;
  
        read_next_command();
 -      cmd_mark();
 -      cmd_data(&buf);
 +      parse_mark();
 +      parse_data(&buf);
        store_object(OBJ_BLOB, &buf, &last_blob, NULL, next_mark);
  }
  
@@@ -1908,7 -1908,7 +1908,7 @@@ static void file_change_m(struct branc
                        p = uq.buf;
                }
                read_next_command();
 -              cmd_data(&buf);
 +              parse_data(&buf);
                store_object(OBJ_BLOB, &buf, &last_blob, sha1, 0);
        } else if (oe) {
                if (oe->type != OBJ_BLOB)
@@@ -1995,7 -1995,7 +1995,7 @@@ static void file_change_deleteall(struc
        load_tree(&b->branch_tree);
  }
  
 -static void cmd_from_commit(struct branch *b, char *buf, unsigned long size)
 +static void parse_from_commit(struct branch *b, char *buf, unsigned long size)
  {
        if (!buf || size < 46)
                die("Not a valid commit: %s", sha1_to_hex(b->sha1));
                b->branch_tree.versions[1].sha1);
  }
  
 -static void cmd_from_existing(struct branch *b)
 +static void parse_from_existing(struct branch *b)
  {
        if (is_null_sha1(b->sha1)) {
                hashclr(b->branch_tree.versions[0].sha1);
  
                buf = read_object_with_reference(b->sha1,
                        commit_type, &size, b->sha1);
 -              cmd_from_commit(b, buf, size);
 +              parse_from_commit(b, buf, size);
                free(buf);
        }
  }
  
 -static int cmd_from(struct branch *b)
 +static int parse_from(struct branch *b)
  {
        const char *from;
        struct branch *s;
                if (oe->pack_id != MAX_PACK_ID) {
                        unsigned long size;
                        char *buf = gfi_unpack_entry(oe, &size);
 -                      cmd_from_commit(b, buf, size);
 +                      parse_from_commit(b, buf, size);
                        free(buf);
                } else
 -                      cmd_from_existing(b);
 +                      parse_from_existing(b);
        } else if (!get_sha1(from, b->sha1))
 -              cmd_from_existing(b);
 +              parse_from_existing(b);
        else
                die("Invalid ref name or SHA1 expression: %s", from);
  
        return 1;
  }
  
 -static struct hash_list *cmd_merge(unsigned int *count)
 +static struct hash_list *parse_merge(unsigned int *count)
  {
        struct hash_list *list = NULL, *n, *e = e;
        const char *from;
        return list;
  }
  
 -static void cmd_new_commit(void)
 +static void parse_new_commit(void)
  {
        static struct strbuf msg = STRBUF_INIT;
        struct branch *b;
                b = new_branch(sp);
  
        read_next_command();
 -      cmd_mark();
 +      parse_mark();
        if (!prefixcmp(command_buf.buf, "author ")) {
                author = parse_ident(command_buf.buf + 7);
                read_next_command();
        }
        if (!committer)
                die("Expected committer but didn't get one");
 -      cmd_data(&msg);
 +      parse_data(&msg);
        read_next_command();
 -      cmd_from(b);
 -      merge_list = cmd_merge(&merge_count);
 +      parse_from(b);
 +      merge_list = parse_merge(&merge_count);
  
        /* ensure the branch is active/loaded */
        if (!b->branch_tree.tree || !max_active_branches) {
        b->last_commit = object_count_by_type[OBJ_COMMIT];
  }
  
 -static void cmd_new_tag(void)
 +static void parse_new_tag(void)
  {
        static struct strbuf msg = STRBUF_INIT;
        char *sp;
  
        /* tag payload/message */
        read_next_command();
 -      cmd_data(&msg);
 +      parse_data(&msg);
  
        /* build the tag object */
        strbuf_reset(&new_data);
                t->pack_id = pack_id;
  }
  
 -static void cmd_reset_branch(void)
 +static void parse_reset_branch(void)
  {
        struct branch *b;
        char *sp;
        else
                b = new_branch(sp);
        read_next_command();
 -      cmd_from(b);
 +      parse_from(b);
        if (command_buf.len > 0)
                unread_command_buf = 1;
  }
  
 -static void cmd_checkpoint(void)
 +static void parse_checkpoint(void)
  {
        if (object_count) {
                cycle_packfile();
        skip_optional_lf();
  }
  
 -static void cmd_progress(void)
 +static void parse_progress(void)
  {
        fwrite(command_buf.buf, 1, command_buf.len, stdout);
        fputc('\n', stdout);
@@@ -2352,7 -2352,7 +2352,7 @@@ static void import_marks(const char *in
        fclose(f);
  }
  
- static int git_pack_config(const char *k, const char *v)
+ static int git_pack_config(const char *k, const char *v, void *cb)
  {
        if (!strcmp(k, "pack.depth")) {
                max_depth = git_config_int(k, v);
                pack_compression_seen = 1;
                return 0;
        }
-       return git_default_config(k, v);
+       return git_default_config(k, v, cb);
  }
  
  static const char fast_import_usage[] =
@@@ -2381,7 -2381,7 +2381,7 @@@ int main(int argc, const char **argv
        unsigned int i, show_stats = 1;
  
        setup_git_directory();
-       git_config(git_pack_config);
+       git_config(git_pack_config, NULL);
        if (!pack_compression_seen && core_compression_seen)
                pack_compression_level = core_compression_level;
  
        set_die_routine(die_nicely);
        while (read_next_command() != EOF) {
                if (!strcmp("blob", command_buf.buf))
 -                      cmd_new_blob();
 +                      parse_new_blob();
                else if (!prefixcmp(command_buf.buf, "commit "))
 -                      cmd_new_commit();
 +                      parse_new_commit();
                else if (!prefixcmp(command_buf.buf, "tag "))
 -                      cmd_new_tag();
 +                      parse_new_tag();
                else if (!prefixcmp(command_buf.buf, "reset "))
 -                      cmd_reset_branch();
 +                      parse_reset_branch();
                else if (!strcmp("checkpoint", command_buf.buf))
 -                      cmd_checkpoint();
 +                      parse_checkpoint();
                else if (!prefixcmp(command_buf.buf, "progress "))
 -                      cmd_progress();
 +                      parse_progress();
                else
                        die("Unsupported command: %s", command_buf.buf);
        }
diff --combined hash-object.c
index 0a7ac2fe2adf820a171898b9e51b245c063018cc,3d773900ff270114c21aa71f613f9fd69f79416a..48d522368454d2663d5cc075045d87f120058ced
@@@ -6,7 -6,6 +6,7 @@@
   */
  #include "cache.h"
  #include "blob.h"
 +#include "quote.h"
  
  static void hash_object(const char *path, enum object_type type, int write_object)
  {
@@@ -21,7 -20,6 +21,7 @@@
                    ? "Unable to add %s to database"
                    : "Unable to hash %s", path);
        printf("%s\n", sha1_to_hex(sha1));
 +      maybe_flush_or_die(stdout, "hash to stdout");
  }
  
  static void hash_stdin(const char *type, int write_object)
        printf("%s\n", sha1_to_hex(sha1));
  }
  
 +static void hash_stdin_paths(const char *type, int write_objects)
 +{
 +      struct strbuf buf, nbuf;
 +
 +      strbuf_init(&buf, 0);
 +      strbuf_init(&nbuf, 0);
 +      while (strbuf_getline(&buf, stdin, '\n') != EOF) {
 +              if (buf.buf[0] == '"') {
 +                      strbuf_reset(&nbuf);
 +                      if (unquote_c_style(&nbuf, buf.buf, NULL))
 +                              die("line is badly quoted");
 +                      strbuf_swap(&buf, &nbuf);
 +              }
 +              hash_object(buf.buf, type_from_string(type), write_objects);
 +      }
 +      strbuf_release(&buf);
 +      strbuf_release(&nbuf);
 +}
 +
  static const char hash_object_usage[] =
 -"git-hash-object [-t <type>] [-w] [--stdin] <file>...";
 +"git-hash-object [ [-t <type>] [-w] [--stdin] <file>... | --stdin-paths < <list-of-paths> ]";
  
  int main(int argc, char **argv)
  {
@@@ -63,9 -42,8 +63,9 @@@
        int prefix_length = -1;
        int no_more_flags = 0;
        int hashstdin = 0;
 +      int stdin_paths = 0;
  
-       git_config(git_default_config);
+       git_config(git_default_config, NULL);
  
        for (i = 1 ; i < argc; i++) {
                if (!no_more_flags && argv[i][0] == '-') {
                        }
                        else if (!strcmp(argv[i], "--help"))
                                usage(hash_object_usage);
 +                      else if (!strcmp(argv[i], "--stdin-paths")) {
 +                              if (hashstdin) {
 +                                      error("Can't use --stdin-paths with --stdin");
 +                                      usage(hash_object_usage);
 +                              }
 +                              stdin_paths = 1;
 +
 +                      }
                        else if (!strcmp(argv[i], "--stdin")) {
 +                              if (stdin_paths) {
 +                                      error("Can't use %s with --stdin-paths", argv[i]);
 +                                      usage(hash_object_usage);
 +                              }
                                if (hashstdin)
                                        die("Multiple --stdin arguments are not supported");
                                hashstdin = 1;
                else {
                        const char *arg = argv[i];
  
 +                      if (stdin_paths) {
 +                              error("Can't specify files (such as \"%s\") with --stdin-paths", arg);
 +                              usage(hash_object_usage);
 +                      }
 +
                        if (hashstdin) {
                                hash_stdin(type, write_object);
                                hashstdin = 0;
                        no_more_flags = 1;
                }
        }
 +
 +      if (stdin_paths)
 +              hash_stdin_paths(type, write_object);
 +
        if (hashstdin)
                hash_stdin(type, write_object);
        return 0;
diff --combined remote.c
index 75a12c0762160dd877d0b6f63f8ec47228b7fee4,dff6c5f686d51a5352989ac2c395f387c1903491..91e3b112bb5ffa151a8ede9391d3bd88ea4d3500
+++ b/remote.c
@@@ -2,16 -2,6 +2,16 @@@
  #include "remote.h"
  #include "refs.h"
  
 +static struct refspec s_tag_refspec = {
 +      0,
 +      1,
 +      0,
 +      "refs/tags/",
 +      "refs/tags/"
 +};
 +
 +const struct refspec *tag_refspec = &s_tag_refspec;
 +
  struct counted_string {
        size_t len;
        const char *s;
@@@ -298,7 -288,7 +298,7 @@@ static void read_branches_file(struct r
        remote->fetch_tags = 1; /* always auto-follow */
  }
  
- static int handle_config(const char *key, const char *value)
+ static int handle_config(const char *key, const char *value, void *cb)
  {
        const char *name;
        const char *subkey;
@@@ -420,7 -410,7 +420,7 @@@ static void read_config(void
                current_branch =
                        make_branch(head_ref + strlen("refs/heads/"), 0);
        }
-       git_config(handle_config);
+       git_config(handle_config, NULL);
        alias_all_urls();
  }
  
@@@ -444,16 -434,6 +444,16 @@@ static struct refspec *parse_refspec_in
                }
  
                rhs = strrchr(lhs, ':');
 +
 +              /*
 +               * Before going on, special case ":" (or "+:") as a refspec
 +               * for matching refs.
 +               */
 +              if (!fetch && rhs == lhs && rhs[1] == '\0') {
 +                      rs[i].matching = 1;
 +                      continue;
 +              }
 +
                if (rhs) {
                        rhs++;
                        rlen = strlen(rhs);
@@@ -875,7 -855,7 +875,7 @@@ static int match_explicit(struct ref *s
        const char *dst_value = rs->dst;
        char *dst_guess;
  
 -      if (rs->pattern)
 +      if (rs->pattern || rs->matching)
                return errs;
  
        matched_src = matched_dst = NULL;
@@@ -965,23 -945,13 +965,23 @@@ static const struct refspec *check_patt
                                                 const struct ref *src)
  {
        int i;
 +      int matching_refs = -1;
        for (i = 0; i < rs_nr; i++) {
 +              if (rs[i].matching &&
 +                  (matching_refs == -1 || rs[i].force)) {
 +                      matching_refs = i;
 +                      continue;
 +              }
 +
                if (rs[i].pattern &&
                    !prefixcmp(src->name, rs[i].src) &&
                    src->name[strlen(rs[i].src)] == '/')
                        return rs + i;
        }
 -      return NULL;
 +      if (matching_refs != -1)
 +              return rs + matching_refs;
 +      else
 +              return NULL;
  }
  
  /*
  int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
               int nr_refspec, const char **refspec, int flags)
  {
 -      struct refspec *rs =
 -              parse_push_refspec(nr_refspec, (const char **) refspec);
 +      struct refspec *rs;
        int send_all = flags & MATCH_REFS_ALL;
        int send_mirror = flags & MATCH_REFS_MIRROR;
 +      static const char *default_refspec[] = { ":", 0 };
  
 +      if (!nr_refspec) {
 +              nr_refspec = 1;
 +              refspec = default_refspec;
 +      }
 +      rs = parse_push_refspec(nr_refspec, (const char **) refspec);
        if (match_explicit_refs(src, dst, dst_tail, rs, nr_refspec))
                return -1;
  
                char *dst_name;
                if (src->peer_ref)
                        continue;
 -              if (nr_refspec) {
 -                      pat = check_pattern_match(rs, nr_refspec, src);
 -                      if (!pat)
 -                              continue;
 -              }
 -              else if (!send_mirror && prefixcmp(src->name, "refs/heads/"))
 +
 +              pat = check_pattern_match(rs, nr_refspec, src);
 +              if (!pat)
 +                      continue;
 +
 +              if (pat->matching) {
                        /*
                         * "matching refs"; traditionally we pushed everything
                         * including refs outside refs/heads/ hierarchy, but
                         * that does not make much sense these days.
                         */
 -                      continue;
 +                      if (!send_mirror && prefixcmp(src->name, "refs/heads/"))
 +                              continue;
 +                      dst_name = xstrdup(src->name);
  
 -              if (pat) {
 +              } else {
                        const char *dst_side = pat->dst ? pat->dst : pat->src;
                        dst_name = xmalloc(strlen(dst_side) +
                                           strlen(src->name) -
                                           strlen(pat->src) + 2);
                        strcpy(dst_name, dst_side);
                        strcat(dst_name, src->name + strlen(pat->src));
 -              } else
 -                      dst_name = xstrdup(src->name);
 +              }
                dst_peer = find_ref_by_name(dst, dst_name);
 -              if (dst_peer && dst_peer->peer_ref)
 -                      /* We're already sending something to this ref. */
 -                      goto free_name;
 +              if (dst_peer) {
 +                      if (dst_peer->peer_ref)
 +                              /* We're already sending something to this ref. */
 +                              goto free_name;
 +
 +              } else {
 +                      if (pat->matching && !(send_all || send_mirror))
 +                              /*
 +                               * Remote doesn't have it, and we have no
 +                               * explicit pattern, and we don't have
 +                               * --all nor --mirror.
 +                               */
 +                              goto free_name;
  
 -              if (!dst_peer && !nr_refspec && !(send_all || send_mirror))
 -                      /*
 -                       * Remote doesn't have it, and we have no
 -                       * explicit pattern, and we don't have
 -                       * --all nor --mirror.
 -                       */
 -                      goto free_name;
 -              if (!dst_peer) {
                        /* Create a new one and link it */
                        dst_peer = make_linked_ref(dst_name, dst_tail);
                        hashcpy(dst_peer->new_sha1, src->new_sha1);
                }
                dst_peer->peer_ref = src;
 -              if (pat)
 -                      dst_peer->force = pat->force;
 +              dst_peer->force = pat->force;
        free_name:
                free(dst_name);
        }
diff --combined wt-status.c
index 6e6516cd874a92376a672dcdc181aed6a3b5636c,c932b39d20407ff08f0bf52963a75dc717e74920..5b4d74c1f3f4c24aa23cf564a16592d188449da1
@@@ -18,7 -18,6 +18,7 @@@ static char wt_status_colors[][COLOR_MA
        "\033[32m", /* WT_STATUS_UPDATED: green */
        "\033[31m", /* WT_STATUS_CHANGED: red */
        "\033[31m", /* WT_STATUS_UNTRACKED: red */
 +      "\033[31m", /* WT_STATUS_NOBRANCH: red */
  };
  
  static const char use_add_msg[] =
@@@ -39,8 -38,6 +39,8 @@@ static int parse_status_slot(const cha
                return WT_STATUS_CHANGED;
        if (!strcasecmp(var+offset, "untracked"))
                return WT_STATUS_UNTRACKED;
 +      if (!strcasecmp(var+offset, "nobranch"))
 +              return WT_STATUS_NOBRANCH;
        die("bad config variable '%s'", var);
  }
  
@@@ -209,7 -206,7 +209,7 @@@ static void wt_status_print_updated(str
        rev.diffopt.format_callback = wt_status_print_updated_cb;
        rev.diffopt.format_callback_data = s;
        rev.diffopt.detect_rename = 1;
 -      rev.diffopt.rename_limit = 100;
 +      rev.diffopt.rename_limit = 200;
        rev.diffopt.break_opt = 0;
        run_diff_index(&rev, 1);
  }
@@@ -317,9 -314,8 +317,9 @@@ static void wt_status_print_verbose(str
  void wt_status_print(struct wt_status *s)
  {
        unsigned char sha1[20];
 -      s->is_initial = get_sha1(s->reference, sha1) ? 1 : 0;
 +      const char *branch_color = color(WT_STATUS_HEADER);
  
 +      s->is_initial = get_sha1(s->reference, sha1) ? 1 : 0;
        if (s->branch) {
                const char *on_what = "On branch ";
                const char *branch_name = s->branch;
                        branch_name += 11;
                else if (!strcmp(branch_name, "HEAD")) {
                        branch_name = "";
 +                      branch_color = color(WT_STATUS_NOBRANCH);
                        on_what = "Not currently on any branch.";
                }
 -              color_fprintf_ln(s->fp, color(WT_STATUS_HEADER),
 -                      "# %s%s", on_what, branch_name);
 +              color_fprintf(s->fp, color(WT_STATUS_HEADER), "# ");
 +              color_fprintf_ln(s->fp, branch_color, "%s%s", on_what, branch_name);
        }
  
        if (s->is_initial) {
        }
  }
  
- int git_status_config(const char *k, const char *v)
+ int git_status_config(const char *k, const char *v, void *cb)
  {
        if (!strcmp(k, "status.submodulesummary")) {
                int is_bool;
                wt_status_relative_paths = git_config_bool(k, v);
                return 0;
        }
-       return git_color_default_config(k, v);
+       return git_color_default_config(k, v, cb);
  }
diff --combined wt-status.h
index f0675fdff33420ba42f3a700501003a43afda376,f2c71302cb18e30e82059b787b4cff99080115b9..597c7ea988634409f920c86008f5ba70910722f9
@@@ -8,7 -8,6 +8,7 @@@ enum color_wt_status 
        WT_STATUS_UPDATED,
        WT_STATUS_CHANGED,
        WT_STATUS_UNTRACKED,
 +      WT_STATUS_NOBRANCH,
  };
  
  struct wt_status {
@@@ -28,7 -27,7 +28,7 @@@
        const char *prefix;
  };
  
- int git_status_config(const char *var, const char *value);
+ int git_status_config(const char *var, const char *value, void *cb);
  extern int wt_status_use_color;
  extern int wt_status_relative_paths;
  void wt_status_prepare(struct wt_status *s);