Code

Merge branch 'maint' to synchronize with 1.5.0.6
authorJunio C Hamano <junkio@cox.net>
Wed, 28 Mar 2007 22:39:57 +0000 (15:39 -0700)
committerJunio C Hamano <junkio@cox.net>
Wed, 28 Mar 2007 22:39:57 +0000 (15:39 -0700)
1  2 
commit.c
git-commit.sh

diff --combined commit.c
index a4f2e74c0b9b17a467fd9c4d3f5a2d275d5ea038,1fe23b6e3a9d4bf46948be2b19a96049d944bb1a..754d1b8a0b8282fd3d1d6bd8f6ccb21b407504a5
+++ b/commit.c
@@@ -3,7 -3,6 +3,7 @@@
  #include "commit.h"
  #include "pkt-line.h"
  #include "utf8.h"
 +#include "interpolate.h"
  
  int save_commit_buffer = 1;
  
@@@ -37,11 -36,8 +37,11 @@@ struct cmt_fmt_map 
        { "full",       5,      CMIT_FMT_FULL },
        { "fuller",     5,      CMIT_FMT_FULLER },
        { "oneline",    1,      CMIT_FMT_ONELINE },
 +      { "format:",    7,      CMIT_FMT_USERFORMAT},
  };
  
 +static char *user_format;
 +
  enum cmit_fmt get_commit_format(const char *arg)
  {
        int i;
                return CMIT_FMT_DEFAULT;
        if (*arg == '=')
                arg++;
 +      if (!prefixcmp(arg, "format:")) {
 +              if (user_format)
 +                      free(user_format);
 +              user_format = xstrdup(arg + 7);
 +              return CMIT_FMT_USERFORMAT;
 +      }
        for (i = 0; i < ARRAY_SIZE(cmt_fmts); i++) {
                if (!strncmp(arg, cmt_fmts[i].n, cmt_fmts[i].cmp_len) &&
                    !strncmp(arg, cmt_fmts[i].n, strlen(arg)))
@@@ -352,18 -342,18 +352,18 @@@ int parse_commit_buffer(struct commit *
  
  int parse_commit(struct commit *item)
  {
 -      char type[20];
 +      enum object_type type;
        void *buffer;
        unsigned long size;
        int ret;
  
        if (item->object.parsed)
                return 0;
 -      buffer = read_sha1_file(item->object.sha1, type, &size);
 +      buffer = read_sha1_file(item->object.sha1, &type, &size);
        if (!buffer)
                return error("Could not read %s",
                             sha1_to_hex(item->object.sha1));
 -      if (strcmp(type, commit_type)) {
 +      if (type != OBJ_COMMIT) {
                free(buffer);
                return error("Object %s not a commit",
                             sha1_to_hex(item->object.sha1));
@@@ -651,9 -641,10 +651,10 @@@ static char *get_header(const struct co
        }
  }
  
 -static char *replace_encoding_header(char *buf, char *encoding)
 +static char *replace_encoding_header(char *buf, const char *encoding)
  {
        char *encoding_header = strstr(buf, "\nencoding ");
+       char *header_end = strstr(buf, "\n\n");
        char *end_of_encoding_header;
        int encoding_header_pos;
        int encoding_header_len;
        int need_len;
        int buflen = strlen(buf) + 1;
  
-       if (!encoding_header)
-               return buf; /* should not happen but be defensive */
+       if (!header_end)
+               header_end = buf + buflen;
+       if (!encoding_header || encoding_header >= header_end)
+               return buf;
        encoding_header++;
        end_of_encoding_header = strchr(encoding_header, '\n');
        if (!end_of_encoding_header)
  }
  
  static char *logmsg_reencode(const struct commit *commit,
 -                           char *output_encoding)
 +                           const char *output_encoding)
  {
 +      static const char *utf8 = "utf-8";
 +      const char *use_encoding;
        char *encoding;
        char *out;
 -      char *utf8 = "utf-8";
  
        if (!*output_encoding)
                return NULL;
        encoding = get_header(commit, "encoding");
 -      if (!encoding)
 -              encoding = utf8;
 -      if (!strcmp(encoding, output_encoding))
 -              out = strdup(commit->buffer);
 +      use_encoding = encoding ? encoding : utf8;
 +      if (!strcmp(use_encoding, output_encoding))
 +              out = xstrdup(commit->buffer);
        else
                out = reencode_string(commit->buffer,
 -                                    output_encoding, encoding);
 +                                    output_encoding, use_encoding);
        if (out)
                out = replace_encoding_header(out, output_encoding);
  
 -      if (encoding != utf8)
 -              free(encoding);
 -      if (!out)
 -              return NULL;
 +      free(encoding);
        return out;
  }
  
 +static char *xstrndup(const char *text, int len)
 +{
 +      char *result = xmalloc(len + 1);
 +      memcpy(result, text, len);
 +      result[len] = '\0';
 +      return result;
 +}
 +
 +static void fill_person(struct interp *table, const char *msg, int len)
 +{
 +      int start, end, tz = 0;
 +      unsigned long date;
 +      char *ep;
 +
 +      /* parse name */
 +      for (end = 0; end < len && msg[end] != '<'; end++)
 +              ; /* do nothing */
 +      start = end + 1;
 +      while (end > 0 && isspace(msg[end - 1]))
 +              end--;
 +      table[0].value = xstrndup(msg, end);
 +
 +      if (start >= len)
 +              return;
 +
 +      /* parse email */
 +      for (end = start + 1; end < len && msg[end] != '>'; end++)
 +              ; /* do nothing */
 +
 +      if (end >= len)
 +              return;
 +
 +      table[1].value = xstrndup(msg + start, end - start);
 +
 +      /* parse date */
 +      for (start = end + 1; start < len && isspace(msg[start]); start++)
 +              ; /* do nothing */
 +      if (start >= len)
 +              return;
 +      date = strtoul(msg + start, &ep, 10);
 +      if (msg + start == ep)
 +              return;
 +
 +      table[5].value = xstrndup(msg + start, ep - (msg + start));
 +
 +      /* parse tz */
 +      for (start = ep - msg + 1; start < len && isspace(msg[start]); start++)
 +              ; /* do nothing */
 +      if (start + 1 < len) {
 +              tz = strtoul(msg + start + 1, NULL, 10);
 +              if (msg[start] == '-')
 +                      tz = -tz;
 +      }
 +
 +      interp_set_entry(table, 2, show_date(date, tz, 0));
 +      interp_set_entry(table, 3, show_rfc2822_date(date, tz));
 +      interp_set_entry(table, 4, show_date(date, tz, 1));
 +}
 +
 +static long format_commit_message(const struct commit *commit,
 +              const char *msg, char *buf, unsigned long space)
 +{
 +      struct interp table[] = {
 +              { "%H" },       /* commit hash */
 +              { "%h" },       /* abbreviated commit hash */
 +              { "%T" },       /* tree hash */
 +              { "%t" },       /* abbreviated tree hash */
 +              { "%P" },       /* parent hashes */
 +              { "%p" },       /* abbreviated parent hashes */
 +              { "%an" },      /* author name */
 +              { "%ae" },      /* author email */
 +              { "%ad" },      /* author date */
 +              { "%aD" },      /* author date, RFC2822 style */
 +              { "%ar" },      /* author date, relative */
 +              { "%at" },      /* author date, UNIX timestamp */
 +              { "%cn" },      /* committer name */
 +              { "%ce" },      /* committer email */
 +              { "%cd" },      /* committer date */
 +              { "%cD" },      /* committer date, RFC2822 style */
 +              { "%cr" },      /* committer date, relative */
 +              { "%ct" },      /* committer date, UNIX timestamp */
 +              { "%e" },       /* encoding */
 +              { "%s" },       /* subject */
 +              { "%b" },       /* body */
 +              { "%Cred" },    /* red */
 +              { "%Cgreen" },  /* green */
 +              { "%Cblue" },   /* blue */
 +              { "%Creset" },  /* reset color */
 +              { "%n" }        /* newline */
 +      };
 +      enum interp_index {
 +              IHASH = 0, IHASH_ABBREV,
 +              ITREE, ITREE_ABBREV,
 +              IPARENTS, IPARENTS_ABBREV,
 +              IAUTHOR_NAME, IAUTHOR_EMAIL,
 +              IAUTHOR_DATE, IAUTHOR_DATE_RFC2822, IAUTHOR_DATE_RELATIVE,
 +              IAUTHOR_TIMESTAMP,
 +              ICOMMITTER_NAME, ICOMMITTER_EMAIL,
 +              ICOMMITTER_DATE, ICOMMITTER_DATE_RFC2822,
 +              ICOMMITTER_DATE_RELATIVE, ICOMMITTER_TIMESTAMP,
 +              IENCODING,
 +              ISUBJECT,
 +              IBODY,
 +              IRED, IGREEN, IBLUE, IRESET_COLOR,
 +              INEWLINE
 +      };
 +      struct commit_list *p;
 +      char parents[1024];
 +      int i;
 +      enum { HEADER, SUBJECT, BODY } state;
 +
 +      if (INEWLINE + 1 != ARRAY_SIZE(table))
 +              die("invalid interp table!");
 +
 +      /* these are independent of the commit */
 +      interp_set_entry(table, IRED, "\033[31m");
 +      interp_set_entry(table, IGREEN, "\033[32m");
 +      interp_set_entry(table, IBLUE, "\033[34m");
 +      interp_set_entry(table, IRESET_COLOR, "\033[m");
 +      interp_set_entry(table, INEWLINE, "\n");
 +
 +      /* these depend on the commit */
 +      if (!commit->object.parsed)
 +              parse_object(commit->object.sha1);
 +      interp_set_entry(table, IHASH, sha1_to_hex(commit->object.sha1));
 +      interp_set_entry(table, IHASH_ABBREV,
 +                      find_unique_abbrev(commit->object.sha1,
 +                              DEFAULT_ABBREV));
 +      interp_set_entry(table, ITREE, sha1_to_hex(commit->tree->object.sha1));
 +      interp_set_entry(table, ITREE_ABBREV,
 +                      find_unique_abbrev(commit->tree->object.sha1,
 +                              DEFAULT_ABBREV));
 +
 +      parents[1] = 0;
 +      for (i = 0, p = commit->parents;
 +                      p && i < sizeof(parents) - 1;
 +                      p = p->next)
 +              i += snprintf(parents + i, sizeof(parents) - i - 1, " %s",
 +                      sha1_to_hex(p->item->object.sha1));
 +      interp_set_entry(table, IPARENTS, parents + 1);
 +
 +      parents[1] = 0;
 +      for (i = 0, p = commit->parents;
 +                      p && i < sizeof(parents) - 1;
 +                      p = p->next)
 +              i += snprintf(parents + i, sizeof(parents) - i - 1, " %s",
 +                      find_unique_abbrev(p->item->object.sha1,
 +                              DEFAULT_ABBREV));
 +      interp_set_entry(table, IPARENTS_ABBREV, parents + 1);
 +
 +      for (i = 0, state = HEADER; msg[i] && state < BODY; i++) {
 +              int eol;
 +              for (eol = i; msg[eol] && msg[eol] != '\n'; eol++)
 +                      ; /* do nothing */
 +
 +              if (state == SUBJECT) {
 +                      table[ISUBJECT].value = xstrndup(msg + i, eol - i);
 +                      i = eol;
 +              }
 +              if (i == eol) {
 +                      state++;
 +                      /* strip empty lines */
 +                      while (msg[eol + 1] == '\n')
 +                              eol++;
 +              } else if (!prefixcmp(msg + i, "author "))
 +                      fill_person(table + IAUTHOR_NAME,
 +                                      msg + i + 7, eol - i - 7);
 +              else if (!prefixcmp(msg + i, "committer "))
 +                      fill_person(table + ICOMMITTER_NAME,
 +                                      msg + i + 10, eol - i - 10);
 +              else if (!prefixcmp(msg + i, "encoding "))
 +                      table[IENCODING].value =
 +                              xstrndup(msg + i + 9, eol - i - 9);
 +              i = eol;
 +      }
 +      if (msg[i])
 +              table[IBODY].value = xstrdup(msg + i);
 +      for (i = 0; i < ARRAY_SIZE(table); i++)
 +              if (!table[i].value)
 +                      interp_set_entry(table, i, "<unknown>");
 +
 +      interpolate(buf, space, user_format, table, ARRAY_SIZE(table));
 +      interp_clear_table(table, ARRAY_SIZE(table));
 +
 +      return strlen(buf);
 +}
 +
  unsigned long pretty_print_commit(enum cmit_fmt fmt,
                                  const struct commit *commit,
                                  unsigned long len,
        const char *msg = commit->buffer;
        int plain_non_ascii = 0;
        char *reencoded;
 -      char *encoding;
 +      const char *encoding;
 +
 +      if (fmt == CMIT_FMT_USERFORMAT)
 +              return format_commit_message(commit, msg, buf, space);
  
        encoding = (git_log_output_encoding
                    ? git_log_output_encoding
@@@ -1384,17 -1190,14 +1387,17 @@@ struct commit_list *get_merge_bases(str
        return result;
  }
  
 -int in_merge_bases(struct commit *rev1, struct commit *rev2)
 +int in_merge_bases(struct commit *commit, struct commit **reference, int num)
  {
        struct commit_list *bases, *b;
        int ret = 0;
  
 -      bases = get_merge_bases(rev1, rev2, 1);
 +      if (num == 1)
 +              bases = get_merge_bases(commit, *reference, 1);
 +      else
 +              die("not yet");
        for (b = bases; b; b = b->next) {
 -              if (!hashcmp(rev1->object.sha1, b->item->object.sha1)) {
 +              if (!hashcmp(commit->object.sha1, b->item->object.sha1)) {
                        ret = 1;
                        break;
                }
diff --combined git-commit.sh
index 3656d607d5b91d8d125b5a72b6a2a00aee512618,fdaedc0e2e6bcfd33816e3c165fbd5ae48e27eee..292cf967e3cbc77e7b49eb7252a756194e917418
@@@ -3,7 -3,7 +3,7 @@@
  # Copyright (c) 2005 Linus Torvalds
  # Copyright (c) 2006 Junio C Hamano
  
 -USAGE='[-a] [-s] [-v] [--no-verify] [-m <message> | -F <logfile> | (-C|-c) <commit> | --amend] [-u] [-e] [--author <author>] [[-i | -o] <path>...]'
 +USAGE='[-a | --interactive] [-s] [-v] [--no-verify] [-m <message> | -F <logfile> | (-C|-c) <commit> | --amend] [-u] [-e] [--author <author>] [[-i | -o] <path>...]'
  SUBDIRECTORY_OK=Yes
  . git-sh-setup
  require_work_tree
@@@ -13,10 -13,10 +13,10 @@@ git-rev-parse --verify HEAD >/dev/null 
  case "$0" in
  *status)
        status_only=t
 -      unmerged_ok_if_status=--unmerged ;;
 +      ;;
  *commit)
        status_only=
 -      unmerged_ok_if_status= ;;
 +      ;;
  esac
  
  refuse_partial () {
@@@ -71,7 -71,6 +71,7 @@@ trap 
  
  all=
  also=
 +interactive=
  only=
  logfile=
  use_commit=
                also=t
                shift
                ;;
 +      --int|--inte|--inter|--intera|--interac|--interact|--interacti|\
 +      --interactiv|--interactive)
 +              interactive=t
 +              shift
 +              ;;
        -o|--o|--on|--onl|--only)
                only=t
                shift
@@@ -310,14 -304,12 +310,14 @@@ case "$#,$also,$only,$amend" i
        ;;
  esac
  unset only
 -case "$all,$also,$#" in
 -t,t,*)
 -      die "Cannot use -a and -i at the same time." ;;
 +case "$all,$interactive,$also,$#" in
 +*t,*t,*)
 +      die "Cannot use -a, --interactive or -i at the same time." ;;
  t,,[1-9]*)
        die "Paths with -a does not make sense." ;;
 -,t,0)
 +,t,[1-9]*)
 +      die "Paths with --interactive does not make sense." ;;
 +,,t,0)
        die "No paths with -i does not make sense." ;;
  esac
  
@@@ -352,9 -344,6 +352,9 @@@ t,
        ) || exit
        ;;
  ,)
 +      if test "$interactive" = t; then
 +              git add --interactive || exit
 +      fi
        case "$#" in
        0)
                ;; # commit as-is
                if test -z "$initial_commit"
                then
                        cp "$THIS_INDEX" "$TMP_INDEX"
-                       GIT_INDEX_FILE="$TMP_INDEX" git-read-tree -m HEAD
+                       GIT_INDEX_FILE="$TMP_INDEX" git-read-tree -i -m HEAD
                else
                        rm -f "$TMP_INDEX"
                fi || exit
@@@ -404,17 -393,16 +404,17 @@@ els
        USE_INDEX="$THIS_INDEX"
  fi
  
 -GIT_INDEX_FILE="$USE_INDEX" \
 -      git-update-index -q $unmerged_ok_if_status --refresh || exit
 -
 -################################################################
 -# If the request is status, just show it and exit.
 -
 -case "$0" in
 -*status)
 +case "$status_only" in
 +t)
 +      # This will silently fail in a read-only repository, which is
 +      # what we want.
 +      GIT_INDEX_FILE="$USE_INDEX" git-update-index -q --unmerged --refresh
        run_status
        exit $?
 +      ;;
 +'')
 +      GIT_INDEX_FILE="$USE_INDEX" git-update-index -q --refresh || exit
 +      ;;
  esac
  
  ################################################################