Merge branch 'sb/committer'
authorJunio C Hamano <gitster@pobox.com>
Wed, 14 May 2008 20:45:20 +0000 (13:45 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 14 May 2008 20:45:20 +0000 (13:45 -0700)
* sb/committer:
commit: Show committer if automatic
commit: Show author if different from committer
Preparation to call determine_author_info from prepare_to_commit

1  2 
builtin-commit.c
cache.h
config.c
environment.c
diff --combined builtin-commit.c
index a65c2b8c37c2f7785d76ec1f4e3692ee52dab8e9,a113eb0272438e60ca0af6dff441b9279cd2d133..0baec6db6ac76de81b93f4c990f8347814618be7
@@@ -47,6 -47,7 +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;
  /*
@@@ -101,7 -102,7 +102,7 @@@ static struct option builtin_commit_opt
        OPT_BOOLEAN('o', "only", &only, "commit only specified files"),
        OPT_BOOLEAN('n', "no-verify", &no_verify, "bypass pre-commit hook"),
        OPT_BOOLEAN(0, "amend", &amend, "amend previous commit"),
 -      OPT_BOOLEAN(0, "untracked-files", &untracked_files, "show all untracked files"),
 +      OPT_BOOLEAN('u', "untracked-files", &untracked_files, "show all untracked files"),
        OPT_BOOLEAN(0, "allow-empty", &allow_empty, "ok to record an empty change"),
        OPT_STRING(0, "cleanup", &cleanup_arg, "default", "how to strip spaces and #comments from message"),
  
@@@ -175,11 -176,9 +176,11 @@@ static void add_remove_files(struct pat
  {
        int i;
        for (i = 0; i < list->nr; i++) {
 +              struct stat st;
                struct path_list_item *p = &(list->items[i]);
 -              if (file_exists(p->path))
 -                      add_file_to_cache(p->path, 0);
 +
 +              if (!lstat(p->path, &st))
 +                      add_to_cache(p->path, &st, 0);
                else
                        remove_file_from_cache(p->path);
        }
@@@ -397,6 -396,47 +398,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);
@@@ -624,45 -693,6 +695,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[])
  {
@@@ -922,7 -952,8 +954,8 @@@ int cmd_commit(int argc, const char **a
                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 cache.h
index 9cee9a5f3fb2e51f617dcf9f92defcd596b97c10,327e5eacd3191c115497d03dba079bba058cf2d8..093f04cec0459e67c54f3fc5b536f59fb0f343dc
+++ b/cache.h
@@@ -133,7 -133,6 +133,7 @@@ struct cache_entry 
  #define CE_UPDATE    (0x10000)
  #define CE_REMOVE    (0x20000)
  #define CE_UPTODATE  (0x40000)
 +#define CE_ADDED     (0x80000)
  
  #define CE_HASHED    (0x100000)
  #define CE_UNHASHED  (0x200000)
@@@ -154,6 -153,20 +154,6 @@@ static inline void copy_cache_entry(str
        dst->ce_flags = (dst->ce_flags & ~CE_STATE_MASK) | state;
  }
  
 -/*
 - * We don't actually *remove* it, we can just mark it invalid so that
 - * we won't find it in lookups.
 - *
 - * Not only would we have to search the lists (simple enough), but
 - * we'd also have to rehash other hash buckets in case this makes the
 - * hash bucket empty (common). So it's much better to just mark
 - * it.
 - */
 -static inline void remove_index_entry(struct cache_entry *ce)
 -{
 -      ce->ce_flags |= CE_UNHASHED;
 -}
 -
  static inline unsigned create_ce_flags(size_t len, unsigned stage)
  {
        if (len >= CE_NAMEMASK)
@@@ -228,23 -241,6 +228,23 @@@ struct index_state 
  
  extern struct index_state the_index;
  
 +/* Name hashing */
 +extern void add_name_hash(struct index_state *istate, struct cache_entry *ce);
 +/*
 + * We don't actually *remove* it, we can just mark it invalid so that
 + * we won't find it in lookups.
 + *
 + * Not only would we have to search the lists (simple enough), but
 + * we'd also have to rehash other hash buckets in case this makes the
 + * hash bucket empty (common). So it's much better to just mark
 + * it.
 + */
 +static inline void remove_name_hash(struct cache_entry *ce)
 +{
 +      ce->ce_flags |= CE_UNHASHED;
 +}
 +
 +
  #ifndef NO_THE_INDEX_COMPATIBILITY_MACROS
  #define active_cache (the_index.cache)
  #define active_nr (the_index.cache_nr)
  #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 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))
 -#define cache_name_exists(name, namelen) index_name_exists(&the_index, (name), (namelen))
 +#define cache_name_exists(name, namelen, igncase) index_name_exists(&the_index, (name), (namelen), (igncase))
  #endif
  
  enum object_type {
@@@ -356,7 -351,7 +356,7 @@@ extern int write_index(const struct ind
  extern int discard_index(struct index_state *);
  extern int unmerged_index(const struct index_state *);
  extern int verify_path(const char *path);
 -extern int index_name_exists(struct index_state *istate, const char *name, int namelen);
 +extern struct cache_entry *index_name_exists(struct index_state *istate, const char *name, int namelen, int igncase);
  extern int index_name_pos(const struct index_state *, const char *name, int namelen);
  #define ADD_CACHE_OK_TO_ADD 1         /* Ok to add */
  #define ADD_CACHE_OK_TO_REPLACE 2     /* Ok to replace file/directory */
@@@ -366,7 -361,6 +366,7 @@@ 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);
  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);
@@@ -411,7 -405,6 +411,7 @@@ extern int delete_ref(const char *, con
  extern int trust_executable_bit;
  extern int quote_path_fully;
  extern int has_symlinks;
 +extern int ignore_case;
  extern int assume_unchanged;
  extern int prefer_symlink_refs;
  extern int log_all_ref_updates;
@@@ -441,15 -434,7 +441,15 @@@ enum branch_track 
        BRANCH_TRACK_EXPLICIT,
  };
  
 +enum rebase_setup_type {
 +      AUTOREBASE_NEVER = 0,
 +      AUTOREBASE_LOCAL,
 +      AUTOREBASE_REMOTE,
 +      AUTOREBASE_ALWAYS,
 +};
 +
  extern enum branch_track git_branch_track;
 +extern enum rebase_setup_type autorebase;
  
  #define GIT_REPO_VERSION 0
  extern int repository_format_version;
@@@ -606,7 -591,7 +606,7 @@@ struct checkout 
  };
  
  extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath);
 -extern int has_symlink_leading_path(const char *name, char *last_symlink);
 +extern int has_symlink_leading_path(int len, const char *name);
  
  extern struct alternate_object_database {
        struct alternate_object_database *next;
@@@ -650,7 -635,6 +650,7 @@@ struct ref 
        struct ref *next;
        unsigned char old_sha1[20];
        unsigned char new_sha1[20];
 +      char *symref;
        unsigned int force:1,
                merge:1,
                nonfastforward:1,
@@@ -735,6 -719,7 +735,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;
diff --combined config.c
index e42ada78f8b10e7fb5600a6c99f81f27accd42fa,3bc4c7ed59dfdfc8c15c0066dd0b9fed57bfda04..5195de10c9289a25053756ce799e654d5e01483e
+++ b/config.c
@@@ -350,11 -350,6 +350,11 @@@ int git_default_config(const char *var
                return 0;
        }
  
 +      if (!strcmp(var, "core.ignorecase")) {
 +              ignore_case = git_config_bool(var, value);
 +              return 0;
 +      }
 +
        if (!strcmp(var, "core.bare")) {
                is_bare_repository_cfg = git_config_bool(var, value);
                return 0;
                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;
        }
  
                git_branch_track = git_config_bool(var, value);
                return 0;
        }
 +      if (!strcmp(var, "branch.autosetuprebase")) {
 +              if (!value)
 +                      return config_error_nonbool(var);
 +              else if (!strcmp(value, "never"))
 +                      autorebase = AUTOREBASE_NEVER;
 +              else if (!strcmp(value, "local"))
 +                      autorebase = AUTOREBASE_LOCAL;
 +              else if (!strcmp(value, "remote"))
 +                      autorebase = AUTOREBASE_REMOTE;
 +              else if (!strcmp(value, "always"))
 +                      autorebase = AUTOREBASE_ALWAYS;
 +              else
 +                      return error("Malformed value for %s", var);
 +              return 0;
 +      }
  
        /* Add other config variables here and to Documentation/config.txt. */
        return 0;
@@@ -627,9 -611,11 +631,9 @@@ static int store_aux(const char* key, c
        case KEY_SEEN:
                if (matches(key, value)) {
                        if (store.seen == 1 && store.multi_replace == 0) {
 -                              fprintf(stderr,
 -                                      "Warning: %s has multiple values\n",
 -                                      key);
 +                              warning("%s has multiple values", key);
                        } else if (store.seen >= MAX_MATCHES) {
 -                              fprintf(stderr, "Too many matches\n");
 +                              error("too many matches for %s", key);
                                return 1;
                        }
  
        return 0;
  }
  
 -static int write_error(void)
 +static int write_error(const char *filename)
  {
 -      fprintf(stderr, "Failed to write new configuration file\n");
 +      error("failed to write new configuration file %s", filename);
  
        /* Same error code as "failed to rename". */
        return 4;
@@@ -698,7 -684,7 +702,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]);
                }
@@@ -840,7 -826,7 +844,7 @@@ int git_config_set_multivar(const char
         */
  
        if (last_dot == NULL) {
 -              fprintf(stderr, "key does not contain a section: %s\n", key);
 +              error("key does not contain a section: %s", key);
                ret = 2;
                goto out_free;
        }
                /* Leave the extended basename untouched.. */
                if (!dot || i > store.baselen) {
                        if (!iskeychar(c) || (i == store.baselen+1 && !isalpha(c))) {
 -                              fprintf(stderr, "invalid key: %s\n", key);
 +                              error("invalid key: %s", key);
                                free(store.key);
                                ret = 1;
                                goto out_free;
                        }
                        c = tolower(c);
                } else if (c == '\n') {
 -                      fprintf(stderr, "invalid key (newline): %s\n", key);
 +                      error("invalid key (newline): %s", key);
                        free(store.key);
                        ret = 1;
                        goto out_free;
        lock = xcalloc(sizeof(struct lock_file), 1);
        fd = hold_lock_file_for_update(lock, config_filename, 0);
        if (fd < 0) {
 -              fprintf(stderr, "could not lock config file\n");
 +              error("could not lock config file %s", config_filename);
                free(store.key);
                ret = -1;
                goto out_free;
                        store.value_regex = (regex_t*)xmalloc(sizeof(regex_t));
                        if (regcomp(store.value_regex, value_regex,
                                        REG_EXTENDED)) {
 -                              fprintf(stderr, "Invalid pattern: %s\n",
 -                                      value_regex);
 +                              error("invalid pattern: %s", value_regex);
                                free(store.value_regex);
                                ret = 6;
                                goto out_free;
                 * existing config file.
                 */
                if (git_config_from_file(store_aux, config_filename)) {
 -                      fprintf(stderr, "invalid config file\n");
 +                      error("invalid config file %s", config_filename);
                        free(store.key);
                        if (store.value_regex != NULL) {
                                regfree(store.value_regex);
        }
  
        if (commit_lock_file(lock) < 0) {
 -              fprintf(stderr, "Cannot commit config file!\n");
 +              error("could not commit config file %s", config_filename);
                ret = 4;
                goto out_free;
        }
@@@ -1048,7 -1035,7 +1052,7 @@@ out_free
        return ret;
  
  write_err_out:
 -      ret = write_error();
 +      ret = write_error(lock->filename);
        goto out_free;
  
  }
@@@ -1098,7 -1085,7 +1102,7 @@@ int git_config_rename_section(const cha
        config_filename = xstrdup(config_filename);
        out_fd = hold_lock_file_for_update(lock, config_filename, 0);
        if (out_fd < 0) {
 -              ret = error("Could not lock config file!");
 +              ret = error("could not lock config file %s", config_filename);
                goto out;
        }
  
                                }
                                store.baselen = strlen(new_name);
                                if (!store_write_section(out_fd, new_name)) {
 -                                      ret = write_error();
 +                                      ret = write_error(lock->filename);
                                        goto out;
                                }
                                continue;
                        continue;
                length = strlen(buf);
                if (write_in_full(out_fd, buf, length) != length) {
 -                      ret = write_error();
 +                      ret = write_error(lock->filename);
                        goto out;
                }
        }
        fclose(config_file);
   unlock_and_out:
        if (commit_lock_file(lock) < 0)
 -                      ret = error("Cannot commit config file!");
 +              ret = error("could not commit config file %s", config_filename);
   out:
        free(config_filename);
        return ret;
diff --combined environment.c
index 55c252706317818db64af8ff3fbfde6cd23e3488,1b80036ca508c1a6d052abd9ea42224b95fdf69e..4fcb471248fb5276a68b9f86a77a2f5cde7dd884
  
  char git_default_email[MAX_GITNAME];
  char git_default_name[MAX_GITNAME];
+ int user_ident_explicitly_given;
  int trust_executable_bit = 1;
  int quote_path_fully = 1;
  int has_symlinks = 1;
 +int ignore_case;
  int assume_unchanged;
  int prefer_symlink_refs;
  int is_bare_repository_cfg = -1; /* unspecified */
@@@ -39,7 -39,6 +40,7 @@@ int auto_crlf = 0;    /* 1: both ways, -1
  enum safe_crlf safe_crlf = SAFE_CRLF_WARN;
  unsigned whitespace_rule_cfg = WS_DEFAULT_RULE;
  enum branch_track git_branch_track = BRANCH_TRACK_REMOTE;
 +enum rebase_setup_type autorebase = AUTOREBASE_NEVER;
  
  /* This is set by setup_git_dir_gently() and/or git_default_config() */
  char *git_work_tree_cfg;