Merge branch 'jk/commit-author-parsing'
authorJunio C Hamano <gitster@pobox.com>
Fri, 19 Sep 2014 18:38:33 +0000 (11:38 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 19 Sep 2014 18:38:33 +0000 (11:38 -0700)
Code clean-up.

* jk/commit-author-parsing:
determine_author_info(): copy getenv output
determine_author_info(): reuse parsing functions
date: use strbufs in date-formatting functions
record_author_date(): use find_commit_header()
record_author_date(): fix memory leak on malformed commit
commit: provide a function to find a header in a buffer

1  2 
builtin/commit.c
cache.h
commit.h
date.c
fast-import.c
ident.c
pretty.c
diff --combined builtin/commit.c
index 41f481bd030ba96f8883e64517eda95931344545,8c6372038bc9827e2d4e462228e79fa66e13c220..4a45bedc09d636c58a25099c4ee4b066d7a94284
@@@ -42,20 -42,7 +42,20 @@@ static const char * const builtin_statu
        NULL
  };
  
 -static const char implicit_ident_advice[] =
 +static const char implicit_ident_advice_noconfig[] =
 +N_("Your name and email address were configured automatically based\n"
 +"on your username and hostname. Please check that they are accurate.\n"
 +"You can suppress this message by setting them explicitly. Run the\n"
 +"following command and follow the instructions in your editor to edit\n"
 +"your configuration file:\n"
 +"\n"
 +"    git config --global --edit\n"
 +"\n"
 +"After doing this, you may fix the identity used for this commit with:\n"
 +"\n"
 +"    git commit --amend --reset-author\n");
 +
 +static const char implicit_ident_advice_config[] =
  N_("Your name and email address were configured automatically based\n"
  "on your username and hostname. Please check that they are accurate.\n"
  "You can suppress this message by setting them explicitly:\n"
@@@ -353,13 -340,6 +353,13 @@@ static char *prepare_index(int argc, co
  
                discard_cache();
                read_cache_from(index_lock.filename);
 +              if (update_main_cache_tree(WRITE_TREE_SILENT) == 0) {
 +                      if (reopen_lock_file(&index_lock) < 0)
 +                              die(_("unable to write index file"));
 +                      if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
 +                              die(_("unable to update temporary index"));
 +              } else
 +                      warning(_("Failed to update main cache tree"));
  
                commit_style = COMMIT_NORMAL;
                return index_lock.filename;
        if (!only && !pathspec.nr) {
                hold_locked_index(&index_lock, 1);
                refresh_cache_or_die(refresh_flags);
 -              if (active_cache_changed) {
 +              if (active_cache_changed
 +                  || !cache_tree_fully_valid(active_cache_tree)) {
                        update_main_cache_tree(WRITE_TREE_SILENT);
 +                      active_cache_changed = 1;
 +              }
 +              if (active_cache_changed) {
                        if (write_locked_index(&the_index, &index_lock,
                                               COMMIT_LOCK))
                                die(_("unable to write new_index file"));
        hold_locked_index(&index_lock, 1);
        add_remove_files(&partial);
        refresh_cache(REFRESH_QUIET);
 +      update_main_cache_tree(WRITE_TREE_SILENT);
        if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
                die(_("unable to write new_index file"));
  
@@@ -545,77 -520,80 +545,80 @@@ static int sane_ident_split(struct iden
        return 1;
  }
  
- static int parse_force_date(const char *in, char *out, int len)
+ static int parse_force_date(const char *in, struct strbuf *out)
  {
-       if (len < 1)
-               return -1;
-       *out++ = '@';
-       len--;
+       strbuf_addch(out, '@');
  
-       if (parse_date(in, out, len) < 0) {
+       if (parse_date(in, out) < 0) {
                int errors = 0;
                unsigned long t = approxidate_careful(in, &errors);
                if (errors)
                        return -1;
-               snprintf(out, len, "%lu", t);
+               strbuf_addf(out, "%lu", t);
        }
  
        return 0;
  }
  
+ static void set_ident_var(char **buf, char *val)
+ {
+       free(*buf);
+       *buf = val;
+ }
+ static char *envdup(const char *var)
+ {
+       const char *val = getenv(var);
+       return val ? xstrdup(val) : NULL;
+ }
  static void determine_author_info(struct strbuf *author_ident)
  {
        char *name, *email, *date;
        struct ident_split author;
-       char date_buf[64];
  
-       name = getenv("GIT_AUTHOR_NAME");
-       email = getenv("GIT_AUTHOR_EMAIL");
-       date = getenv("GIT_AUTHOR_DATE");
+       name = envdup("GIT_AUTHOR_NAME");
+       email = envdup("GIT_AUTHOR_EMAIL");
+       date = envdup("GIT_AUTHOR_DATE");
  
        if (author_message) {
-               const char *a, *lb, *rb, *eol;
+               struct ident_split ident;
                size_t len;
+               const char *a;
  
-               a = strstr(author_message_buffer, "\nauthor ");
+               a = find_commit_header(author_message_buffer, "author", &len);
                if (!a)
-                       die(_("invalid commit: %s"), author_message);
-               lb = strchrnul(a + strlen("\nauthor "), '<');
-               rb = strchrnul(lb, '>');
-               eol = strchrnul(rb, '\n');
-               if (!*lb || !*rb || !*eol)
-                       die(_("invalid commit: %s"), author_message);
-               if (lb == a + strlen("\nauthor "))
-                       /* \nauthor <foo@example.com> */
-                       name = xcalloc(1, 1);
-               else
-                       name = xmemdupz(a + strlen("\nauthor "),
-                                       (lb - strlen(" ") -
-                                        (a + strlen("\nauthor "))));
-               email = xmemdupz(lb + strlen("<"), rb - (lb + strlen("<")));
-               len = eol - (rb + strlen("> "));
-               date = xmalloc(len + 2);
-               *date = '@';
-               memcpy(date + 1, rb + strlen("> "), len);
-               date[len + 1] = '\0';
+                       die(_("commit '%s' lacks author header"), author_message);
+               if (split_ident_line(&ident, a, len) < 0)
+                       die(_("commit '%s' has malformed author line"), author_message);
+               set_ident_var(&name, xmemdupz(ident.name_begin, ident.name_end - ident.name_begin));
+               set_ident_var(&email, xmemdupz(ident.mail_begin, ident.mail_end - ident.mail_begin));
+               if (ident.date_begin) {
+                       struct strbuf date_buf = STRBUF_INIT;
+                       strbuf_addch(&date_buf, '@');
+                       strbuf_add(&date_buf, ident.date_begin, ident.date_end - ident.date_begin);
+                       strbuf_addch(&date_buf, ' ');
+                       strbuf_add(&date_buf, ident.tz_begin, ident.tz_end - ident.tz_begin);
+                       set_ident_var(&date, strbuf_detach(&date_buf, NULL));
+               }
        }
  
        if (force_author) {
-               const char *lb = strstr(force_author, " <");
-               const char *rb = strchr(force_author, '>');
+               struct ident_split ident;
  
-               if (!lb || !rb)
+               if (split_ident_line(&ident, force_author, strlen(force_author)) < 0)
                        die(_("malformed --author parameter"));
-               name = xstrndup(force_author, lb - force_author);
-               email = xstrndup(lb + 2, rb - (lb + 2));
+               set_ident_var(&name, xmemdupz(ident.name_begin, ident.name_end - ident.name_begin));
+               set_ident_var(&email, xmemdupz(ident.mail_begin, ident.mail_end - ident.mail_begin));
        }
  
        if (force_date) {
-               if (parse_force_date(force_date, date_buf, sizeof(date_buf)))
+               struct strbuf date_buf = STRBUF_INIT;
+               if (parse_force_date(force_date, &date_buf))
                        die(_("invalid date format: %s"), force_date);
-               date = date_buf;
+               set_ident_var(&date, strbuf_detach(&date_buf, NULL));
        }
  
        strbuf_addstr(author_ident, fmt_ident(name, email, date, IDENT_STRICT));
                export_one("GIT_AUTHOR_EMAIL", author.mail_begin, author.mail_end, 0);
                export_one("GIT_AUTHOR_DATE", author.date_begin, author.tz_end, '@');
        }
+       free(name);
+       free(email);
+       free(date);
  }
  
  static void split_ident_or_die(struct ident_split *id, const struct strbuf *buf)
@@@ -1073,8 -1055,7 +1080,8 @@@ static const char *find_author_by_nickn
        revs.mailmap = &mailmap;
        read_mailmap(revs.mailmap, NULL);
  
 -      prepare_revision_walk(&revs);
 +      if (prepare_revision_walk(&revs))
 +              die(_("revision walk setup failed"));
        commit = get_revision(&revs);
        if (commit) {
                struct pretty_print_context ctx = {0};
@@@ -1428,24 -1409,6 +1435,24 @@@ int cmd_status(int argc, const char **a
        return 0;
  }
  
 +static const char *implicit_ident_advice(void)
 +{
 +      char *user_config = NULL;
 +      char *xdg_config = NULL;
 +      int config_exists;
 +
 +      home_config_paths(&user_config, &xdg_config, "config");
 +      config_exists = file_exists(user_config) || file_exists(xdg_config);
 +      free(user_config);
 +      free(xdg_config);
 +
 +      if (config_exists)
 +              return _(implicit_ident_advice_config);
 +      else
 +              return _(implicit_ident_advice_noconfig);
 +
 +}
 +
  static void print_summary(const char *prefix, const unsigned char *sha1,
                          int initial_commit)
  {
                strbuf_addbuf_percentquote(&format, &committer_ident);
                if (advice_implicit_identity) {
                        strbuf_addch(&format, '\n');
 -                      strbuf_addstr(&format, _(implicit_ident_advice));
 +                      strbuf_addstr(&format, implicit_ident_advice());
                }
        }
        strbuf_release(&author_ident);
@@@ -1552,7 -1515,7 +1559,7 @@@ static int run_rewrite_hook(const unsig
  {
        /* oldsha1 SP newsha1 LF NUL */
        static char buf[2*40 + 3];
 -      struct child_process proc;
 +      struct child_process proc = CHILD_PROCESS_INIT;
        const char *argv[3];
        int code;
        size_t n;
        argv[1] = "amend";
        argv[2] = NULL;
  
 -      memset(&proc, 0, sizeof(proc));
        proc.argv = argv;
        proc.in = -1;
        proc.stdout_to_stderr = 1;
@@@ -1663,12 -1627,11 +1670,12 @@@ int cmd_commit(int argc, const char **a
        const char *index_file, *reflog_msg;
        char *nl;
        unsigned char sha1[20];
 -      struct ref_lock *ref_lock;
        struct commit_list *parents = NULL, **pptr = &parents;
        struct stat statbuf;
        struct commit *current_head = NULL;
        struct commit_extra_header *extra = NULL;
 +      struct ref_transaction *transaction;
 +      struct strbuf err = STRBUF_INIT;
  
        if (argc == 2 && !strcmp(argv[1], "-h"))
                usage_with_options(builtin_commit_usage, builtin_commit_options);
        strbuf_release(&author_ident);
        free_commit_extra_headers(extra);
  
 -      ref_lock = lock_any_ref_for_update("HEAD",
 -                                         !current_head
 -                                         ? NULL
 -                                         : current_head->object.sha1,
 -                                         0, NULL);
 -      if (!ref_lock) {
 -              rollback_index_files();
 -              die(_("cannot lock HEAD ref"));
 -      }
 -
        nl = strchr(sb.buf, '\n');
        if (nl)
                strbuf_setlen(&sb, nl + 1 - sb.buf);
        strbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg));
        strbuf_insert(&sb, strlen(reflog_msg), ": ", 2);
  
 -      if (write_ref_sha1(ref_lock, sha1, sb.buf) < 0) {
 +      transaction = ref_transaction_begin(&err);
 +      if (!transaction ||
 +          ref_transaction_update(transaction, "HEAD", sha1,
 +                                 current_head
 +                                 ? current_head->object.sha1 : NULL,
 +                                 0, !!current_head, &err) ||
 +          ref_transaction_commit(transaction, sb.buf, &err)) {
                rollback_index_files();
 -              die(_("cannot update HEAD ref"));
 +              die("%s", err.buf);
        }
 +      ref_transaction_free(transaction);
  
        unlink(git_path("CHERRY_PICK_HEAD"));
        unlink(git_path("REVERT_HEAD"));
        if (!quiet)
                print_summary(prefix, sha1, !current_head);
  
 +      strbuf_release(&err);
        return 0;
  }
diff --combined cache.h
index 823feb87b70b205b5de25dc10e84e97d88cb6c62,96ecc1f15881401fccc2a745e487b69e07b22d97..db4ccd1cf7575dfc31b6c7e6720f31529d9edc02
+++ b/cache.h
@@@ -8,7 -8,6 +8,7 @@@
  #include "gettext.h"
  #include "convert.h"
  #include "trace.h"
 +#include "string-list.h"
  
  #include SHA1_HEADER
  #ifndef git_SHA_CTX
@@@ -586,7 -585,6 +586,7 @@@ extern NORETURN void unable_to_lock_ind
  extern int hold_lock_file_for_update(struct lock_file *, const char *path, int);
  extern int hold_lock_file_for_append(struct lock_file *, const char *path, int);
  extern int commit_lock_file(struct lock_file *);
 +extern int reopen_lock_file(struct lock_file *);
  extern void update_index_if_able(struct index_state *, struct lock_file *);
  
  extern int hold_locked_index(struct lock_file *, int);
@@@ -1039,7 -1037,6 +1039,7 @@@ enum date_mode 
        DATE_SHORT,
        DATE_LOCAL,
        DATE_ISO8601,
 +      DATE_ISO8601_STRICT,
        DATE_RFC2822,
        DATE_RAW
  };
  const char *show_date(unsigned long time, int timezone, enum date_mode mode);
  void show_date_relative(unsigned long time, int tz, const struct timeval *now,
                        struct strbuf *timebuf);
- int parse_date(const char *date, char *buf, int bufsize);
+ int parse_date(const char *date, struct strbuf *out);
  int parse_date_basic(const char *date, unsigned long *timestamp, int *offset);
  int parse_expiry_date(const char *date, unsigned long *timestamp);
- void datestamp(char *buf, int bufsize);
+ void datestamp(struct strbuf *out);
  #define approxidate(s) approxidate_careful((s), NULL)
  unsigned long approxidate_careful(const char *, int *);
  unsigned long approxidate_relative(const char *date, const struct timeval *now);
@@@ -1064,7 -1061,6 +1064,7 @@@ extern const char *git_author_info(int)
  extern const char *git_committer_info(int);
  extern const char *fmt_ident(const char *name, const char *email, const char *date_str, int);
  extern const char *fmt_name(const char *name, const char *email);
 +extern const char *ident_default_name(void);
  extern const char *ident_default_email(void);
  extern const char *git_editor(void);
  extern const char *git_pager(int stdout_is_tty);
@@@ -1298,7 -1294,7 +1298,7 @@@ extern int git_config_from_buf(config_f
                               const char *buf, size_t len, void *data);
  extern void git_config_push_parameter(const char *text);
  extern int git_config_from_parameters(config_fn_t fn, void *data);
 -extern int git_config(config_fn_t fn, void *);
 +extern void git_config(config_fn_t fn, void *);
  extern int git_config_with_options(config_fn_t fn, void *,
                                   struct git_config_source *config_source,
                                   int respect_includes);
@@@ -1355,69 -1351,6 +1355,69 @@@ extern int parse_config_key(const char 
                            const char **subsection, int *subsection_len,
                            const char **key);
  
 +struct config_set_element {
 +      struct hashmap_entry ent;
 +      char *key;
 +      struct string_list value_list;
 +};
 +
 +struct configset_list_item {
 +      struct config_set_element *e;
 +      int value_index;
 +};
 +
 +/*
 + * the contents of the list are ordered according to their
 + * position in the config files and order of parsing the files.
 + * (i.e. key-value pair at the last position of .git/config will
 + * be at the last item of the list)
 + */
 +struct configset_list {
 +      struct configset_list_item *items;
 +      unsigned int nr, alloc;
 +};
 +
 +struct config_set {
 +      struct hashmap config_hash;
 +      int hash_initialized;
 +      struct configset_list list;
 +};
 +
 +extern void git_configset_init(struct config_set *cs);
 +extern int git_configset_add_file(struct config_set *cs, const char *filename);
 +extern int git_configset_get_value(struct config_set *cs, const char *key, const char **value);
 +extern const struct string_list *git_configset_get_value_multi(struct config_set *cs, const char *key);
 +extern void git_configset_clear(struct config_set *cs);
 +extern int git_configset_get_string_const(struct config_set *cs, const char *key, const char **dest);
 +extern int git_configset_get_string(struct config_set *cs, const char *key, char **dest);
 +extern int git_configset_get_int(struct config_set *cs, const char *key, int *dest);
 +extern int git_configset_get_ulong(struct config_set *cs, const char *key, unsigned long *dest);
 +extern int git_configset_get_bool(struct config_set *cs, const char *key, int *dest);
 +extern int git_configset_get_bool_or_int(struct config_set *cs, const char *key, int *is_bool, int *dest);
 +extern int git_configset_get_maybe_bool(struct config_set *cs, const char *key, int *dest);
 +extern int git_configset_get_pathname(struct config_set *cs, const char *key, const char **dest);
 +
 +extern int git_config_get_value(const char *key, const char **value);
 +extern const struct string_list *git_config_get_value_multi(const char *key);
 +extern void git_config_clear(void);
 +extern void git_config_iter(config_fn_t fn, void *data);
 +extern int git_config_get_string_const(const char *key, const char **dest);
 +extern int git_config_get_string(const char *key, char **dest);
 +extern int git_config_get_int(const char *key, int *dest);
 +extern int git_config_get_ulong(const char *key, unsigned long *dest);
 +extern int git_config_get_bool(const char *key, int *dest);
 +extern int git_config_get_bool_or_int(const char *key, int *is_bool, int *dest);
 +extern int git_config_get_maybe_bool(const char *key, int *dest);
 +extern int git_config_get_pathname(const char *key, const char **dest);
 +
 +struct key_value_info {
 +      const char *filename;
 +      int linenr;
 +};
 +
 +extern NORETURN void git_die_config(const char *key, const char *err, ...) __attribute__((format(printf, 2, 3)));
 +extern NORETURN void git_die_config_linenr(const char *key, const char *filename, int linenr);
 +
  extern int committer_ident_sufficiently_given(void);
  extern int author_ident_sufficiently_given(void);
  
diff --combined commit.h
index a401ddfbc4e5c2e5e4a9d7aeedebc34e6103c9fa,12f4fe10c19cf58961764d75f1adea29c37b1a87..470c74569fc64bc879955362b8996c6f4d616b31
+++ b/commit.h
@@@ -26,25 -26,13 +26,25 @@@ extern int save_commit_buffer
  extern const char *commit_type;
  
  /* While we can decorate any object with a name, it's only used for commits.. */
 -extern struct decoration name_decoration;
  struct name_decoration {
        struct name_decoration *next;
        int type;
 -      char name[1];
 +      char name[FLEX_ARRAY];
  };
  
 +enum decoration_type {
 +      DECORATION_NONE = 0,
 +      DECORATION_REF_LOCAL,
 +      DECORATION_REF_REMOTE,
 +      DECORATION_REF_TAG,
 +      DECORATION_REF_STASH,
 +      DECORATION_REF_HEAD,
 +      DECORATION_GRAFTED,
 +};
 +
 +void add_name_decoration(enum decoration_type type, const char *name, struct object *obj);
 +const struct name_decoration *get_name_decoration(const struct object *obj);
 +
  struct commit *lookup_commit(const unsigned char *sha1);
  struct commit *lookup_commit_reference(const unsigned char *sha1);
  struct commit *lookup_commit_reference_gently(const unsigned char *sha1,
@@@ -171,7 -159,6 +171,7 @@@ extern void get_commit_format(const cha
  extern const char *format_subject(struct strbuf *sb, const char *msg,
                                  const char *line_separator);
  extern void userformat_find_requirements(const char *fmt, struct userformat_want *w);
 +extern int commit_format_is_empty(enum cmit_fmt);
  extern void format_commit_message(const struct commit *commit,
                                  const char *format, struct strbuf *sb,
                                  const struct pretty_print_context *context);
@@@ -326,6 -313,17 +326,17 @@@ extern struct commit_extra_header *read
  
  extern void free_commit_extra_headers(struct commit_extra_header *extra);
  
+ /*
+  * Search the commit object contents given by "msg" for the header "key".
+  * Returns a pointer to the start of the header contents, or NULL. The length
+  * of the header, up to the first newline, is returned via out_len.
+  *
+  * Note that some headers (like mergetag) may be multi-line. It is the caller's
+  * responsibility to parse further in this case!
+  */
+ extern const char *find_commit_header(const char *msg, const char *key,
+                                     size_t *out_len);
  typedef void (*each_mergetag_fn)(struct commit *commit, struct commit_extra_header *extra,
                                 void *cb_data);
  
diff --combined date.c
index 5d73d9b7b223be54b8ad14c3c5a7eca1b501ec09,2c33468dfbf445017a1925065004646942a3cb75..59dfe579c6a0c65abe0a34052cdbf16643afbfb8
--- 1/date.c
--- 2/date.c
+++ b/date.c
@@@ -200,16 -200,7 +200,16 @@@ const char *show_date(unsigned long tim
                                tm->tm_mday,
                                tm->tm_hour, tm->tm_min, tm->tm_sec,
                                tz);
 -      else if (mode == DATE_RFC2822)
 +      else if (mode == DATE_ISO8601_STRICT) {
 +              char sign = (tz >= 0) ? '+' : '-';
 +              tz = abs(tz);
 +              strbuf_addf(&timebuf, "%04d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d",
 +                              tm->tm_year + 1900,
 +                              tm->tm_mon + 1,
 +                              tm->tm_mday,
 +                              tm->tm_hour, tm->tm_min, tm->tm_sec,
 +                              sign, tz / 100, tz % 100);
 +      } else if (mode == DATE_RFC2822)
                strbuf_addf(&timebuf, "%.3s, %d %.3s %d %02d:%02d:%02d %+05d",
                        weekday_names[tm->tm_wday], tm->tm_mday,
                        month_names[tm->tm_mon], tm->tm_year + 1900,
@@@ -614,7 -605,7 +614,7 @@@ static int match_tz(const char *date, i
        return end - date;
  }
  
- static int date_string(unsigned long date, int offset, char *buf, int len)
+ static void date_string(unsigned long date, int offset, struct strbuf *buf)
  {
        int sign = '+';
  
                offset = -offset;
                sign = '-';
        }
-       return snprintf(buf, len, "%lu %c%02d%02d", date, sign, offset / 60, offset % 60);
+       strbuf_addf(buf, "%lu %c%02d%02d", date, sign, offset / 60, offset % 60);
  }
  
  /*
@@@ -744,13 -735,14 +744,14 @@@ int parse_expiry_date(const char *date
        return errors;
  }
  
- int parse_date(const char *date, char *result, int maxlen)
+ int parse_date(const char *date, struct strbuf *result)
  {
        unsigned long timestamp;
        int offset;
        if (parse_date_basic(date, &timestamp, &offset))
                return -1;
-       return date_string(timestamp, offset, result, maxlen);
+       date_string(timestamp, offset, result);
+       return 0;
  }
  
  enum date_mode parse_date_format(const char *format)
        else if (!strcmp(format, "iso8601") ||
                 !strcmp(format, "iso"))
                return DATE_ISO8601;
 +      else if (!strcmp(format, "iso8601-strict") ||
 +               !strcmp(format, "iso-strict"))
 +              return DATE_ISO8601_STRICT;
        else if (!strcmp(format, "rfc2822") ||
                 !strcmp(format, "rfc"))
                return DATE_RFC2822;
                die("unknown date format %s", format);
  }
  
- void datestamp(char *buf, int bufsize)
+ void datestamp(struct strbuf *out)
  {
        time_t now;
        int offset;
        offset = tm_to_time_t(localtime(&now)) - now;
        offset /= 60;
  
-       date_string(now, offset, buf, bufsize);
+       date_string(now, offset, out);
  }
  
  /*
diff --combined fast-import.c
index c071253c90807b6fa98771e9fd046fed12bb7b4b,dc9f7a8ccbb303168d63c0401b8a5d61eab97e4b..9e6134d0346835173a937b2366f18ba013b38c0e
@@@ -946,12 -946,10 +946,12 @@@ static void unkeep_all_packs(void
  
  static void end_packfile(void)
  {
 -      struct packed_git *old_p = pack_data, *new_p;
 +      if (!pack_data)
 +              return;
  
        clear_delta_base_cache();
        if (object_count) {
 +              struct packed_git *new_p;
                unsigned char cur_pack_sha1[20];
                char *idx_name;
                int i;
                pack_id++;
        }
        else {
 -              close(old_p->pack_fd);
 -              unlink_or_warn(old_p->pack_name);
 +              close(pack_data->pack_fd);
 +              unlink_or_warn(pack_data->pack_name);
        }
 -      free(old_p);
 +      free(pack_data);
 +      pack_data = NULL;
  
        /* We can't carry a delta across packfiles. */
        strbuf_release(&last_blob.data);
@@@ -1682,9 -1679,8 +1682,9 @@@ found_entry
  static int update_branch(struct branch *b)
  {
        static const char *msg = "fast-import";
 -      struct ref_lock *lock;
 +      struct ref_transaction *transaction;
        unsigned char old_sha1[20];
 +      struct strbuf err = STRBUF_INIT;
  
        if (read_ref(b->name, old_sha1))
                hashclr(old_sha1);
                        delete_ref(b->name, old_sha1, 0);
                return 0;
        }
 -      lock = lock_any_ref_for_update(b->name, old_sha1, 0, NULL);
 -      if (!lock)
 -              return error("Unable to lock %s", b->name);
        if (!force_update && !is_null_sha1(old_sha1)) {
                struct commit *old_cmit, *new_cmit;
  
                old_cmit = lookup_commit_reference_gently(old_sha1, 0);
                new_cmit = lookup_commit_reference_gently(b->sha1, 0);
 -              if (!old_cmit || !new_cmit) {
 -                      unlock_ref(lock);
 +              if (!old_cmit || !new_cmit)
                        return error("Branch %s is missing commits.", b->name);
 -              }
  
                if (!in_merge_bases(old_cmit, new_cmit)) {
 -                      unlock_ref(lock);
                        warning("Not updating %s"
                                " (new tip %s does not contain %s)",
                                b->name, sha1_to_hex(b->sha1), sha1_to_hex(old_sha1));
                        return -1;
                }
        }
 -      if (write_ref_sha1(lock, b->sha1, msg) < 0)
 -              return error("Unable to update %s", b->name);
 +      transaction = ref_transaction_begin(&err);
 +      if (!transaction ||
 +          ref_transaction_update(transaction, b->name, b->sha1, old_sha1,
 +                                 0, 1, &err) ||
 +          ref_transaction_commit(transaction, msg, &err)) {
 +              ref_transaction_free(transaction);
 +              error("%s", err.buf);
 +              strbuf_release(&err);
 +              return -1;
 +      }
 +      ref_transaction_free(transaction);
 +      strbuf_release(&err);
        return 0;
  }
  
@@@ -1738,32 -1730,15 +1738,32 @@@ static void dump_tags(void
  {
        static const char *msg = "fast-import";
        struct tag *t;
 -      struct ref_lock *lock;
 -      char ref_name[PATH_MAX];
 +      struct strbuf ref_name = STRBUF_INIT;
 +      struct strbuf err = STRBUF_INIT;
 +      struct ref_transaction *transaction;
  
 +      transaction = ref_transaction_begin(&err);
 +      if (!transaction) {
 +              failure |= error("%s", err.buf);
 +              goto cleanup;
 +      }
        for (t = first_tag; t; t = t->next_tag) {
 -              sprintf(ref_name, "tags/%s", t->name);
 -              lock = lock_ref_sha1(ref_name, NULL);
 -              if (!lock || write_ref_sha1(lock, t->sha1, msg) < 0)
 -                      failure |= error("Unable to update %s", ref_name);
 +              strbuf_reset(&ref_name);
 +              strbuf_addf(&ref_name, "refs/tags/%s", t->name);
 +
 +              if (ref_transaction_update(transaction, ref_name.buf, t->sha1,
 +                                         NULL, 0, 0, &err)) {
 +                      failure |= error("%s", err.buf);
 +                      goto cleanup;
 +              }
        }
 +      if (ref_transaction_commit(transaction, msg, &err))
 +              failure |= error("%s", err.buf);
 +
 + cleanup:
 +      ref_transaction_free(transaction);
 +      strbuf_release(&ref_name);
 +      strbuf_release(&err);
  }
  
  static void dump_marks_helper(FILE *f,
@@@ -1996,7 -1971,7 +1996,7 @@@ static int parse_data(struct strbuf *sb
        return 1;
  }
  
- static int validate_raw_date(const char *src, char *result, int maxlen)
+ static int validate_raw_date(const char *src, struct strbuf *result)
  {
        const char *orig_src = src;
        char *endp;
                return -1;
  
        num = strtoul(src + 1, &endp, 10);
-       if (errno || endp == src + 1 || *endp || (endp - orig_src) >= maxlen ||
-           1400 < num)
+       if (errno || endp == src + 1 || *endp || 1400 < num)
                return -1;
  
-       strcpy(result, orig_src);
+       strbuf_addstr(result, orig_src);
        return 0;
  }
  
@@@ -2026,7 -2000,7 +2025,7 @@@ static char *parse_ident(const char *bu
  {
        const char *ltgt;
        size_t name_len;
-       char *ident;
+       struct strbuf ident = STRBUF_INIT;
  
        /* ensure there is a space delimiter even if there is no name */
        if (*buf == '<')
                die("Missing space after > in ident string: %s", buf);
        ltgt++;
        name_len = ltgt - buf;
-       ident = xmalloc(name_len + 24);
-       strncpy(ident, buf, name_len);
+       strbuf_add(&ident, buf, name_len);
  
        switch (whenspec) {
        case WHENSPEC_RAW:
-               if (validate_raw_date(ltgt, ident + name_len, 24) < 0)
+               if (validate_raw_date(ltgt, &ident) < 0)
                        die("Invalid raw date \"%s\" in ident: %s", ltgt, buf);
                break;
        case WHENSPEC_RFC2822:
-               if (parse_date(ltgt, ident + name_len, 24) < 0)
+               if (parse_date(ltgt, &ident) < 0)
                        die("Invalid rfc2822 date \"%s\" in ident: %s", ltgt, buf);
                break;
        case WHENSPEC_NOW:
                if (strcmp("now", ltgt))
                        die("Date in ident must be 'now': %s", buf);
-               datestamp(ident + name_len, 24);
+               datestamp(&ident);
                break;
        }
  
-       return ident;
+       return strbuf_detach(&ident, NULL);
  }
  
  static void parse_and_store_blob(
@@@ -3299,34 -3272,36 +3297,34 @@@ static void parse_option(const char *op
        die("This version of fast-import does not support option: %s", option);
  }
  
 -static int git_pack_config(const char *k, const char *v, void *cb)
 +static void git_pack_config(void)
  {
 -      if (!strcmp(k, "pack.depth")) {
 -              max_depth = git_config_int(k, v);
 +      int indexversion_value;
 +      unsigned long packsizelimit_value;
 +
 +      if (!git_config_get_ulong("pack.depth", &max_depth)) {
                if (max_depth > MAX_DEPTH)
                        max_depth = MAX_DEPTH;
 -              return 0;
        }
 -      if (!strcmp(k, "pack.compression")) {
 -              int level = git_config_int(k, v);
 -              if (level == -1)
 -                      level = Z_DEFAULT_COMPRESSION;
 -              else if (level < 0 || level > Z_BEST_COMPRESSION)
 -                      die("bad pack compression level %d", level);
 -              pack_compression_level = level;
 +      if (!git_config_get_int("pack.compression", &pack_compression_level)) {
 +              if (pack_compression_level == -1)
 +                      pack_compression_level = Z_DEFAULT_COMPRESSION;
 +              else if (pack_compression_level < 0 ||
 +                       pack_compression_level > Z_BEST_COMPRESSION)
 +                      git_die_config("pack.compression",
 +                                      "bad pack compression level %d", pack_compression_level);
                pack_compression_seen = 1;
 -              return 0;
        }
 -      if (!strcmp(k, "pack.indexversion")) {
 -              pack_idx_opts.version = git_config_int(k, v);
 +      if (!git_config_get_int("pack.indexversion", &indexversion_value)) {
 +              pack_idx_opts.version = indexversion_value;
                if (pack_idx_opts.version > 2)
 -                      die("bad pack.indexversion=%"PRIu32,
 -                          pack_idx_opts.version);
 -              return 0;
 -      }
 -      if (!strcmp(k, "pack.packsizelimit")) {
 -              max_packsize = git_config_ulong(k, v);
 -              return 0;
 +                      git_die_config("pack.indexversion",
 +                                      "bad pack.indexversion=%"PRIu32, pack_idx_opts.version);
        }
 -      return git_default_config(k, v, cb);
 +      if (!git_config_get_ulong("pack.packsizelimit", &packsizelimit_value))
 +              max_packsize = packsizelimit_value;
 +
 +      git_config(git_default_config, NULL);
  }
  
  static const char fast_import_usage[] =
@@@ -3379,7 -3354,7 +3377,7 @@@ int main(int argc, char **argv
  
        setup_git_directory();
        reset_pack_idx_option(&pack_idx_opts);
 -      git_config(git_pack_config, NULL);
 +      git_pack_config();
        if (!pack_compression_seen && core_compression_seen)
                pack_compression_level = core_compression_level;
  
diff --combined ident.c
index 77bc882e59c887e948a1fba4d99805f5dedfa7f7,9bcc4e11b859773c68784530febb529073fda41a..5ff1aadaaaa999df3bfecb07f84f259469b3a54d
+++ b/ident.c
@@@ -9,7 -9,7 +9,7 @@@
  
  static struct strbuf git_default_name = STRBUF_INIT;
  static struct strbuf git_default_email = STRBUF_INIT;
- static char git_default_date[50];
+ static struct strbuf git_default_date = STRBUF_INIT;
  
  #define IDENT_NAME_GIVEN 01
  #define IDENT_MAIL_GIVEN 02
@@@ -102,7 -102,7 +102,7 @@@ static void copy_email(const struct pas
        add_domainname(email);
  }
  
 -static const char *ident_default_name(void)
 +const char *ident_default_name(void)
  {
        if (!git_default_name.len) {
                copy_gecos(xgetpwuid_self(), &git_default_name);
@@@ -129,9 -129,9 +129,9 @@@ const char *ident_default_email(void
  
  static const char *ident_default_date(void)
  {
-       if (!git_default_date[0])
-               datestamp(git_default_date, sizeof(git_default_date));
-       return git_default_date;
+       if (!git_default_date.len)
+               datestamp(&git_default_date);
+       return git_default_date.buf;
  }
  
  static int crud(unsigned char c)
@@@ -292,7 -292,6 +292,6 @@@ const char *fmt_ident(const char *name
                      const char *date_str, int flag)
  {
        static struct strbuf ident = STRBUF_INIT;
-       char date[50];
        int strict = (flag & IDENT_STRICT);
        int want_date = !(flag & IDENT_NO_DATE);
        int want_name = !(flag & IDENT_NO_NAME);
                die("unable to auto-detect email address (got '%s')", email);
        }
  
-       if (want_date) {
-               if (date_str && date_str[0]) {
-                       if (parse_date(date_str, date, sizeof(date)) < 0)
-                               die("invalid date format: %s", date_str);
-               }
-               else
-                       strcpy(date, ident_default_date());
-       }
        strbuf_reset(&ident);
        if (want_name) {
                strbuf_addstr_without_crud(&ident, name);
                        strbuf_addch(&ident, '>');
        if (want_date) {
                strbuf_addch(&ident, ' ');
-               strbuf_addstr_without_crud(&ident, date);
+               if (date_str && date_str[0]) {
+                       if (parse_date(date_str, &ident) < 0)
+                               die("invalid date format: %s", date_str);
+               }
+               else
+                       strbuf_addstr(&ident, ident_default_date());
        }
        return ident.buf;
  }
  
diff --combined pretty.c
index 5f012a6b8ed6cdf345b350e3fa2f6b821ceebd7d,3a705576f3fd36f3b7b1bd2e82ee4460af637a38..63e03b67e932786e588c7742279d1c6d8ac9df0e
+++ b/pretty.c
@@@ -24,11 -24,6 +24,11 @@@ static size_t commit_formats_len
  static size_t commit_formats_alloc;
  static struct cmt_fmt_map *find_commit_format(const char *sought);
  
 +int commit_format_is_empty(enum cmit_fmt fmt)
 +{
 +      return fmt == CMIT_FMT_USERFORMAT && !*user_format;
 +}
 +
  static void save_user_format(struct rev_info *rev, const char *cp, int is_tformat)
  {
        free(user_format);
@@@ -70,9 -65,7 +70,9 @@@ static int git_pretty_formats_config(co
  
        commit_format->name = xstrdup(name);
        commit_format->format = CMIT_FMT_USERFORMAT;
 -      git_config_string(&fmt, var, value);
 +      if (git_config_string(&fmt, var, value))
 +              return -1;
 +
        if (starts_with(fmt, "format:") || starts_with(fmt, "tformat:")) {
                commit_format->is_tformat = fmt[0] == 't';
                fmt = strchr(fmt, ':') + 1;
@@@ -153,7 -146,7 +153,7 @@@ void get_commit_format(const char *arg
        struct cmt_fmt_map *commit_format;
  
        rev->use_terminator = 0;
 -      if (!arg || !*arg) {
 +      if (!arg) {
                rev->commit_format = CMIT_FMT_DEFAULT;
                return;
        }
                return;
        }
  
 -      if (strchr(arg, '%')) {
 +      if (!*arg || strchr(arg, '%')) {
                save_user_format(rev, arg, 1);
                return;
        }
@@@ -554,31 -547,11 +554,11 @@@ static void add_merge_info(const struc
        strbuf_addch(sb, '\n');
  }
  
- static char *get_header(const struct commit *commit, const char *msg,
-                       const char *key)
+ static char *get_header(const char *msg, const char *key)
  {
-       int key_len = strlen(key);
-       const char *line = msg;
-       while (line) {
-               const char *eol = strchrnul(line, '\n'), *next;
-               if (line == eol)
-                       return NULL;
-               if (!*eol) {
-                       warning("malformed commit (header is missing newline): %s",
-                               sha1_to_hex(commit->object.sha1));
-                       next = NULL;
-               } else
-                       next = eol + 1;
-               if (eol - line > key_len &&
-                   !strncmp(line, key, key_len) &&
-                   line[key_len] == ' ') {
-                       return xmemdupz(line + key_len + 1, eol - line - key_len - 1);
-               }
-               line = next;
-       }
-       return NULL;
+       size_t len;
+       const char *v = find_commit_header(msg, key, &len);
+       return v ? xmemdupz(v, len) : NULL;
  }
  
  static char *replace_encoding_header(char *buf, const char *encoding)
@@@ -624,11 -597,10 +604,10 @@@ const char *logmsg_reencode(const struc
  
        if (!output_encoding || !*output_encoding) {
                if (commit_encoding)
-                       *commit_encoding =
-                               get_header(commit, msg, "encoding");
+                       *commit_encoding = get_header(msg, "encoding");
                return msg;
        }
-       encoding = get_header(commit, msg, "encoding");
+       encoding = get_header(msg, "encoding");
        if (commit_encoding)
                *commit_encoding = encoding;
        use_encoding = encoding ? encoding : utf8;
@@@ -738,12 -710,9 +717,12 @@@ static size_t format_person_part(struc
        case 'r':       /* date, relative */
                strbuf_addstr(sb, show_ident_date(&s, DATE_RELATIVE));
                return placeholder_len;
 -      case 'i':       /* date, ISO 8601 */
 +      case 'i':       /* date, ISO 8601-like */
                strbuf_addstr(sb, show_ident_date(&s, DATE_ISO8601));
                return placeholder_len;
 +      case 'I':       /* date, ISO 8601 strict */
 +              strbuf_addstr(sb, show_ident_date(&s, DATE_ISO8601_STRICT));
 +              return placeholder_len;
        }
  
  skip: