From 6b6dcfc297722f5a5324c4cb22f45adccd4c84ef Mon Sep 17 00:00:00 2001 From: Fredrik Kuivinen Date: Fri, 10 Mar 2006 10:21:37 +0100 Subject: Make it possible to not clobber object.util in sort_in_topological_order (take 2) Signed-off-by: Fredrik Kuivinen Signed-off-by: Junio C Hamano --- commit.c | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 06d543915..eb42d517a 100644 --- a/commit.c +++ b/commit.c @@ -569,10 +569,28 @@ int count_parents(struct commit * commit) return count; } +void topo_sort_default_setter(struct commit *c, void *data) +{ + c->object.util = data; +} + +void *topo_sort_default_getter(struct commit *c) +{ + return c->object.util; +} + /* * Performs an in-place topological sort on the list supplied. */ void sort_in_topological_order(struct commit_list ** list, int lifo) +{ + sort_in_topological_order_fn(list, lifo, topo_sort_default_setter, + topo_sort_default_getter); +} + +void sort_in_topological_order_fn(struct commit_list ** list, int lifo, + topo_sort_set_fn_t setter, + topo_sort_get_fn_t getter) { struct commit_list * next = *list; struct commit_list * work = NULL, **insert; @@ -596,7 +614,7 @@ void sort_in_topological_order(struct commit_list ** list, int lifo) next=*list; while (next) { next_nodes->list_item = next; - next->item->object.util = next_nodes; + setter(next->item, next_nodes); next_nodes++; next = next->next; } @@ -606,8 +624,8 @@ void sort_in_topological_order(struct commit_list ** list, int lifo) struct commit_list * parents = next->item->parents; while (parents) { struct commit * parent=parents->item; - struct sort_node * pn = (struct sort_node *)parent->object.util; - + struct sort_node * pn = (struct sort_node *) getter(parent); + if (pn) pn->indegree++; parents=parents->next; @@ -624,7 +642,7 @@ void sort_in_topological_order(struct commit_list ** list, int lifo) next=*list; insert = &work; while (next) { - struct sort_node * node = (struct sort_node *)next->item->object.util; + struct sort_node * node = (struct sort_node *) getter(next->item); if (node->indegree == 0) { insert = &commit_list_insert(next->item, insert)->next; @@ -637,15 +655,15 @@ void sort_in_topological_order(struct commit_list ** list, int lifo) sort_by_date(&work); while (work) { struct commit * work_item = pop_commit(&work); - struct sort_node * work_node = (struct sort_node *)work_item->object.util; + struct sort_node * work_node = (struct sort_node *) getter(work_item); struct commit_list * parents = work_item->parents; while (parents) { struct commit * parent=parents->item; - struct sort_node * pn = (struct sort_node *)parent->object.util; - + struct sort_node * pn = (struct sort_node *) getter(parent); + if (pn) { - /* + /* * parents are only enqueued for emission * when all their children have been emitted thereby * guaranteeing topological order. @@ -667,7 +685,7 @@ void sort_in_topological_order(struct commit_list ** list, int lifo) *pptr = work_node->list_item; pptr = &(*pptr)->next; *pptr = NULL; - work_item->object.util = NULL; + setter(work_item, NULL); } free(nodes); } -- cgit v1.2.1 From 90321c106ca6e36c0e884ca677c9a52dea47bdde Mon Sep 17 00:00:00 2001 From: Peter Eriksen Date: Mon, 3 Apr 2006 19:30:46 +0100 Subject: Replace xmalloc+memset(0) with xcalloc. Signed-off-by: Peter Eriksen Signed-off-by: Junio C Hamano --- commit.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index eb42d517a..d4976fb89 100644 --- a/commit.c +++ b/commit.c @@ -73,8 +73,7 @@ struct commit *lookup_commit(const unsigned char *sha1) { struct object *obj = lookup_object(sha1); if (!obj) { - struct commit *ret = xmalloc(sizeof(struct commit)); - memset(ret, 0, sizeof(struct commit)); + struct commit *ret = xcalloc(1, sizeof(struct commit)); created_object(sha1, &ret->object); ret->object.type = commit_type; return ret; -- cgit v1.2.1 From 5040f17eba15504bad66b14a645bddd9b015ebb7 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 6 Apr 2006 23:58:51 -0700 Subject: blame -S This adds the -S option to blame, which is needed by the CVS server emulation. Signed-off-by: Junio C Hamano --- commit.c | 134 +++++++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 79 insertions(+), 55 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index d4976fb89..d534c9ba5 100644 --- a/commit.c +++ b/commit.c @@ -101,11 +101,7 @@ static unsigned long parse_commit_date(const char *buf) return date; } -static struct commit_graft { - unsigned char sha1[20]; - int nr_parent; - unsigned char parent[0][20]; /* more */ -} **commit_graft; +static struct commit_graft **commit_graft; static int commit_graft_alloc, commit_graft_nr; static int commit_graft_pos(const unsigned char *sha1) @@ -127,70 +123,98 @@ static int commit_graft_pos(const unsigned char *sha1) return -lo - 1; } -static void prepare_commit_graft(void) +int register_commit_graft(struct commit_graft *graft, int ignore_dups) +{ + int pos = commit_graft_pos(graft->sha1); + + if (0 <= pos) { + if (ignore_dups) + free(graft); + else { + free(commit_graft[pos]); + commit_graft[pos] = graft; + } + return 1; + } + pos = -pos - 1; + if (commit_graft_alloc <= ++commit_graft_nr) { + commit_graft_alloc = alloc_nr(commit_graft_alloc); + commit_graft = xrealloc(commit_graft, + sizeof(*commit_graft) * + commit_graft_alloc); + } + if (pos < commit_graft_nr) + memmove(commit_graft + pos + 1, + commit_graft + pos, + (commit_graft_nr - pos - 1) * + sizeof(*commit_graft)); + commit_graft[pos] = graft; + return 0; +} + +struct commit_graft *read_graft_line(char *buf, int len) +{ + /* The format is just "Commit Parent1 Parent2 ...\n" */ + int i; + struct commit_graft *graft = NULL; + + if (buf[len-1] == '\n') + buf[--len] = 0; + if (buf[0] == '#') + return 0; + if ((len + 1) % 41) { + bad_graft_data: + error("bad graft data: %s", buf); + free(graft); + return NULL; + } + i = (len + 1) / 41 - 1; + graft = xmalloc(sizeof(*graft) + 20 * i); + graft->nr_parent = i; + if (get_sha1_hex(buf, graft->sha1)) + goto bad_graft_data; + for (i = 40; i < len; i += 41) { + if (buf[i] != ' ') + goto bad_graft_data; + if (get_sha1_hex(buf + i + 1, graft->parent[i/41])) + goto bad_graft_data; + } + return graft; +} + +int read_graft_file(const char *graft_file) { - char *graft_file = get_graft_file(); FILE *fp = fopen(graft_file, "r"); char buf[1024]; - if (!fp) { - commit_graft = (struct commit_graft **) "hack"; - return; - } + if (!fp) + return -1; while (fgets(buf, sizeof(buf), fp)) { /* The format is just "Commit Parent1 Parent2 ...\n" */ int len = strlen(buf); - int i; - struct commit_graft *graft = NULL; - - if (buf[len-1] == '\n') - buf[--len] = 0; - if (buf[0] == '#') - continue; - if ((len + 1) % 41) { - bad_graft_data: - error("bad graft data: %s", buf); - free(graft); - continue; - } - i = (len + 1) / 41 - 1; - graft = xmalloc(sizeof(*graft) + 20 * i); - graft->nr_parent = i; - if (get_sha1_hex(buf, graft->sha1)) - goto bad_graft_data; - for (i = 40; i < len; i += 41) { - if (buf[i] != ' ') - goto bad_graft_data; - if (get_sha1_hex(buf + i + 1, graft->parent[i/41])) - goto bad_graft_data; - } - i = commit_graft_pos(graft->sha1); - if (0 <= i) { + struct commit_graft *graft = read_graft_line(buf, len); + if (register_commit_graft(graft, 1)) error("duplicate graft data: %s", buf); - free(graft); - continue; - } - i = -i - 1; - if (commit_graft_alloc <= ++commit_graft_nr) { - commit_graft_alloc = alloc_nr(commit_graft_alloc); - commit_graft = xrealloc(commit_graft, - sizeof(*commit_graft) * - commit_graft_alloc); - } - if (i < commit_graft_nr) - memmove(commit_graft + i + 1, - commit_graft + i, - (commit_graft_nr - i - 1) * - sizeof(*commit_graft)); - commit_graft[i] = graft; } fclose(fp); + return 0; +} + +static void prepare_commit_graft(void) +{ + static int commit_graft_prepared; + char *graft_file; + + if (commit_graft_prepared) + return; + graft_file = get_graft_file(); + read_graft_file(graft_file); + commit_graft_prepared = 1; } static struct commit_graft *lookup_commit_graft(const unsigned char *sha1) { int pos; - if (!commit_graft) - prepare_commit_graft(); + prepare_commit_graft(); pos = commit_graft_pos(sha1); if (pos < 0) return NULL; -- cgit v1.2.1 From 684958ae6162c593bd90ba893271ffb2381c3c44 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 12 Apr 2006 11:31:23 -0700 Subject: When showing a commit message, do not lose an incomplete line. --- commit.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index d534c9ba5..c7bb8dba6 100644 --- a/commit.c +++ b/commit.c @@ -400,11 +400,11 @@ static int get_one_line(const char *msg, unsigned long len) while (len--) { char c = *msg++; + if (!c) + break; ret++; if (c == '\n') break; - if (!c) - return 0; } return ret; } -- cgit v1.2.1 From 40c2fe003ce2af3ec522f239be274fd1a27422f6 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 14 Apr 2006 21:20:51 -0700 Subject: Clean up trailing whitespace when pretty-printing commits Partly because we've messed up and now have some commits with trailing whitespace, but partly because this also just simplifies the code, let's remove trailing whitespace from the end when pretty-printing commits. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- commit.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index c7bb8dba6..ca2557450 100644 --- a/commit.c +++ b/commit.c @@ -557,16 +557,11 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit if (fmt == CMIT_FMT_ONELINE) break; } - if (fmt == CMIT_FMT_ONELINE) { - /* We do not want the terminating newline */ - if (buf[offset - 1] == '\n') - offset--; - } - else { - /* Make sure there is an EOLN */ - if (buf[offset - 1] != '\n') - buf[offset++] = '\n'; - } + while (offset && isspace(buf[offset-1])) + offset--; + /* Make sure there is an EOLN for the non-oneline case */ + if (fmt != CMIT_FMT_ONELINE) + buf[offset++] = '\n'; buf[offset] = '\0'; return offset; } -- cgit v1.2.1 From 5bc4ce589646faf72c7a77a5d32d9496ccc8d456 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 16 Apr 2006 14:24:56 -0700 Subject: reading $GIT_DIR/info/graft - skip comments correctly. Noticed by Yann Dirson. Signed-off-by: Junio C Hamano --- commit.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index ca2557450..05c4c923f 100644 --- a/commit.c +++ b/commit.c @@ -161,7 +161,7 @@ struct commit_graft *read_graft_line(char *buf, int len) if (buf[len-1] == '\n') buf[--len] = 0; if (buf[0] == '#') - return 0; + return NULL; if ((len + 1) % 41) { bad_graft_data: error("bad graft data: %s", buf); @@ -192,6 +192,8 @@ int read_graft_file(const char *graft_file) /* The format is just "Commit Parent1 Parent2 ...\n" */ int len = strlen(buf); struct commit_graft *graft = read_graft_line(buf, len); + if (!graft) + continue; if (register_commit_graft(graft, 1)) error("duplicate graft data: %s", buf); } -- cgit v1.2.1 From 360204c324ca9178e2bcb4d75f3986201f8ac7e1 Mon Sep 17 00:00:00 2001 From: Yann Dirson Date: Mon, 17 Apr 2006 13:41:49 +0200 Subject: Allow empty lines in info/grafts In addition to the existing comment support, that just allows the user to use a convention that works pretty much everywhere else. Signed-off-by: Yann Dirson Signed-off-by: Junio C Hamano --- commit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 05c4c923f..2717dd81c 100644 --- a/commit.c +++ b/commit.c @@ -160,7 +160,7 @@ struct commit_graft *read_graft_line(char *buf, int len) if (buf[len-1] == '\n') buf[--len] = 0; - if (buf[0] == '#') + if (buf[0] == '#' || buf[0] == '\0') return NULL; if ((len + 1) % 41) { bad_graft_data: -- cgit v1.2.1 From 3eefc189172b88dece6fb6d479b3ed13cc483dbc Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 18 Apr 2006 16:45:27 -0700 Subject: Tentative built-in format-patch. This only does --stdout right now. To write into separate files with pretty-printed filenames like the real thing does, it needs a bit mroe work. Signed-off-by: Junio C Hamano --- commit.c | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 2717dd81c..af747bde6 100644 --- a/commit.c +++ b/commit.c @@ -36,6 +36,8 @@ enum cmit_fmt get_commit_format(const char *arg) return CMIT_FMT_FULL; if (!strcmp(arg, "=fuller")) return CMIT_FMT_FULLER; + if (!strcmp(arg, "=email")) + return CMIT_FMT_EMAIL; if (!strcmp(arg, "=oneline")) return CMIT_FMT_ONELINE; die("invalid --pretty format"); @@ -428,6 +430,10 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const c time = strtoul(date, &date, 10); tz = strtol(date, NULL, 10); + if (fmt == CMIT_FMT_EMAIL) { + what = "From"; + filler = ""; + } ret = sprintf(buf, "%s: %.*s%.*s\n", what, (fmt == CMIT_FMT_FULLER) ? 4 : 0, filler, namelen, line); @@ -435,6 +441,9 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const c case CMIT_FMT_MEDIUM: ret += sprintf(buf + ret, "Date: %s\n", show_date(time, tz)); break; + case CMIT_FMT_EMAIL: + ret += sprintf(buf + ret, "Date: %s\n", show_date(time, tz)); + break; case CMIT_FMT_FULLER: ret += sprintf(buf + ret, "%sDate: %s\n", what, show_date(time, tz)); break; @@ -445,10 +454,12 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const c return ret; } -static int is_empty_line(const char *line, int len) +static int is_empty_line(const char *line, int *len_p) { + int len = *len_p; while (len && isspace(line[len-1])) len--; + *len_p = len; return !len; } @@ -457,7 +468,8 @@ static int add_merge_info(enum cmit_fmt fmt, char *buf, const struct commit *com struct commit_list *parent = commit->parents; int offset; - if ((fmt == CMIT_FMT_ONELINE) || !parent || !parent->next) + if ((fmt == CMIT_FMT_ONELINE) || (fmt == CMIT_FMT_EMAIL) || + !parent || !parent->next) return 0; offset = sprintf(buf, "Merge:"); @@ -480,9 +492,15 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit { int hdr = 1, body = 0; unsigned long offset = 0; - int indent = (fmt == CMIT_FMT_ONELINE) ? 0 : 4; + int indent = 4; int parents_shown = 0; const char *msg = commit->buffer; + const char *subject = NULL; + + if (fmt == CMIT_FMT_EMAIL) + subject = "Subject: "; + if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL) + indent = 0; for (;;) { const char *line = msg; @@ -506,7 +524,7 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit if (hdr) { if (linelen == 1) { hdr = 0; - if (fmt != CMIT_FMT_ONELINE) + if ((fmt != CMIT_FMT_ONELINE) && !subject) buf[offset++] = '\n'; continue; } @@ -544,20 +562,28 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit continue; } - if (is_empty_line(line, linelen)) { + if (is_empty_line(line, &linelen)) { if (!body) continue; + if (subject) + continue; if (fmt == CMIT_FMT_SHORT) break; } else { body = 1; } + if (subject) { + memcpy(buf + offset, subject, 9); + offset += 9; + } memset(buf + offset, ' ', indent); memcpy(buf + offset + indent, line, linelen); offset += linelen + indent; + buf[offset++] = '\n'; if (fmt == CMIT_FMT_ONELINE) break; + subject = NULL; } while (offset && isspace(buf[offset-1])) offset--; -- cgit v1.2.1 From 4c4b158b8f31a19f724a84b11fa538bfbf465d32 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 19 Apr 2006 15:16:08 -0700 Subject: Minor tweak on subject line in --pretty=email Signed-off-by: Junio C Hamano --- commit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index af747bde6..06e00987c 100644 --- a/commit.c +++ b/commit.c @@ -498,7 +498,7 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit const char *subject = NULL; if (fmt == CMIT_FMT_EMAIL) - subject = "Subject: "; + subject = "Subject: [PATCH] "; if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL) indent = 0; -- cgit v1.2.1 From 53f420ef00ca6cc3554084c4c9fb89f50c634f58 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 22 Apr 2006 03:06:13 -0700 Subject: git-fmt-patch: thinkofix to show [PATCH] properly. Updating "subject" variable without changing the hardcoded number of bytes to memcpy from it would not help much. Signed-off-by: Junio C Hamano --- commit.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 06e00987c..f4e4eea01 100644 --- a/commit.c +++ b/commit.c @@ -574,8 +574,9 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit } if (subject) { - memcpy(buf + offset, subject, 9); - offset += 9; + int slen = strlen(subject); + memcpy(buf + offset, subject, slen); + offset += slen; } memset(buf + offset, ' ', indent); memcpy(buf + offset + indent, line, linelen); -- cgit v1.2.1 From 2a3870432395e78c82a870bf1a177b8d5b8fbdf0 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 1 May 2006 01:44:33 -0700 Subject: Use RFC2822 dates from "git fmt-patch". Still Work-in-progress git fmt-patch (should it be known as format-patch-ng?) is matched with the fix made by Huw Davies in 262a6ef76a1dde97ab50d79fa5cd6d3f9f125765 commit to use RFC2822 date format. Signed-off-by: Junio C Hamano --- commit.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index f4e4eea01..42b44bba5 100644 --- a/commit.c +++ b/commit.c @@ -442,7 +442,8 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const c ret += sprintf(buf + ret, "Date: %s\n", show_date(time, tz)); break; case CMIT_FMT_EMAIL: - ret += sprintf(buf + ret, "Date: %s\n", show_date(time, tz)); + ret += sprintf(buf + ret, "Date: %s\n", + show_rfc2822_date(time, tz)); break; case CMIT_FMT_FULLER: ret += sprintf(buf + ret, "%sDate: %s\n", what, show_date(time, tz)); -- cgit v1.2.1 From 596524b33d50e47e2375cec9e00aff59f0e8278b Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Fri, 5 May 2006 04:30:52 +0200 Subject: Teach fmt-patch about --numbered Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- commit.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 42b44bba5..93b3903ea 100644 --- a/commit.c +++ b/commit.c @@ -489,17 +489,14 @@ static int add_merge_info(enum cmit_fmt fmt, char *buf, const struct commit *com return offset; } -unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, unsigned long len, char *buf, unsigned long space, int abbrev) +unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, unsigned long len, char *buf, unsigned long space, int abbrev, const char *subject) { int hdr = 1, body = 0; unsigned long offset = 0; int indent = 4; int parents_shown = 0; const char *msg = commit->buffer; - const char *subject = NULL; - if (fmt == CMIT_FMT_EMAIL) - subject = "Subject: [PATCH] "; if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL) indent = 0; -- cgit v1.2.1 From 6cdfd1797486138f50f1929c39e77a2cd1e3e033 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sun, 14 May 2006 17:20:46 -0700 Subject: commit: allow --pretty= args to be abbreviated Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano --- commit.c | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 2717dd81c..4a26070c1 100644 --- a/commit.c +++ b/commit.c @@ -22,23 +22,33 @@ struct sort_node const char *commit_type = "commit"; +struct cmt_fmt_map { + const char *n; + size_t cmp_len; + enum cmit_fmt v; +} cmt_fmts[] = { + { "raw", 1, CMIT_FMT_RAW }, + { "medium", 1, CMIT_FMT_MEDIUM }, + { "short", 1, CMIT_FMT_SHORT }, + { "full", 5, CMIT_FMT_FULL }, + { "fuller", 5, CMIT_FMT_FULLER }, + { "oneline", 1, CMIT_FMT_ONELINE }, +}; + enum cmit_fmt get_commit_format(const char *arg) { - if (!*arg) + int i; + + if (!arg || !*arg) return CMIT_FMT_DEFAULT; - if (!strcmp(arg, "=raw")) - return CMIT_FMT_RAW; - if (!strcmp(arg, "=medium")) - return CMIT_FMT_MEDIUM; - if (!strcmp(arg, "=short")) - return CMIT_FMT_SHORT; - if (!strcmp(arg, "=full")) - return CMIT_FMT_FULL; - if (!strcmp(arg, "=fuller")) - return CMIT_FMT_FULLER; - if (!strcmp(arg, "=oneline")) - return CMIT_FMT_ONELINE; - die("invalid --pretty format"); + if (*arg == '=') + arg++; + for (i = 0; i < ARRAY_SIZE(cmt_fmts); i++) { + if (!strncmp(arg, cmt_fmts[i].n, cmt_fmts[i].cmp_len)) + return cmt_fmts[i].v; + } + + die("invalid --pretty format: %s", arg); } static struct commit *check_commit(struct object *obj, -- cgit v1.2.1 From 698ce6f87e0d6db380f7306e190e8586da184577 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sat, 20 May 2006 15:40:29 +0200 Subject: fmt-patch: Support --attach This patch touches a couple of files, because it adds options to print a custom text just after the subject of a commit, and just after the diffstat. [jc: made "many dashes" used as the boundary leader into a single variable, to reduce the possibility of later tweaks to miscount the number of dashes to break it.] Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- commit.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 84558bac2..0b163d485 100644 --- a/commit.c +++ b/commit.c @@ -498,7 +498,7 @@ static int add_merge_info(enum cmit_fmt fmt, char *buf, const struct commit *com return offset; } -unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, unsigned long len, char *buf, unsigned long space, int abbrev, const char *subject) +unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, unsigned long len, char *buf, unsigned long space, int abbrev, const char *subject, const char *after_subject) { int hdr = 1, body = 0; unsigned long offset = 0; @@ -591,6 +591,14 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit buf[offset++] = '\n'; if (fmt == CMIT_FMT_ONELINE) break; + if (after_subject) { + int slen = strlen(after_subject); + if (slen > space - offset - 1) + slen = space - offset - 1; + memcpy(buf + offset, after_subject, slen); + offset += slen; + after_subject = NULL; + } subject = NULL; } while (offset && isspace(buf[offset-1])) -- cgit v1.2.1 From cdd406e389bd6e0b2ad9394340a366c0c5ae27fc Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 16 May 2006 02:29:42 -0700 Subject: CMIT_FMT_EMAIL: Q-encode Subject: and display-name part of From: fields. By convention, the commit message and the author/committer names in the commit objects are UTF-8 encoded. When formatting for e-mails, Q-encode them according to RFC 2047. While we are at it, generate the content-type and content-transfer-encoding headers as well. Signed-off-by: Junio C Hamano --- commit.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 72 insertions(+), 7 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 0b163d485..7597fba70 100644 --- a/commit.c +++ b/commit.c @@ -422,6 +422,46 @@ static int get_one_line(const char *msg, unsigned long len) return ret; } +static int is_rfc2047_special(char ch) +{ + return ((ch & 0x80) || (ch == '=') || (ch == '?') || (ch == '_')); +} + +static int add_rfc2047(char *buf, const char *line, int len) +{ + char *bp = buf; + int i, needquote; + static const char q_utf8[] = "=?utf-8?q?"; + + for (i = needquote = 0; !needquote && i < len; i++) { + unsigned ch = line[i]; + if (ch & 0x80) + needquote++; + if ((i + 1 < len) && + (ch == '=' && line[i+1] == '?')) + needquote++; + } + if (!needquote) + return sprintf(buf, "%.*s", len, line); + + memcpy(bp, q_utf8, sizeof(q_utf8)-1); + bp += sizeof(q_utf8)-1; + for (i = 0; i < len; i++) { + unsigned ch = line[i]; + if (is_rfc2047_special(ch)) { + sprintf(bp, "=%02X", ch); + bp += 3; + } + else if (ch == ' ') + *bp++ = '_'; + else + *bp++ = ch; + } + memcpy(bp, "?=", 2); + bp += 2; + return bp - buf; +} + static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const char *line) { char *date; @@ -440,12 +480,26 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const c tz = strtol(date, NULL, 10); if (fmt == CMIT_FMT_EMAIL) { - what = "From"; + char *name_tail = strchr(line, '<'); + int display_name_length; + if (!name_tail) + return 0; + while (line < name_tail && isspace(name_tail[-1])) + name_tail--; + display_name_length = name_tail - line; filler = ""; + strcpy(buf, "From: "); + ret = strlen(buf); + ret += add_rfc2047(buf + ret, line, display_name_length); + memcpy(buf + ret, name_tail, namelen - display_name_length); + ret += namelen - display_name_length; + buf[ret++] = '\n'; + } + else { + ret = sprintf(buf, "%s: %.*s%.*s\n", what, + (fmt == CMIT_FMT_FULLER) ? 4 : 0, + filler, namelen, line); } - ret = sprintf(buf, "%s: %.*s%.*s\n", what, - (fmt == CMIT_FMT_FULLER) ? 4 : 0, - filler, namelen, line); switch (fmt) { case CMIT_FMT_MEDIUM: ret += sprintf(buf + ret, "Date: %s\n", show_date(time, tz)); @@ -584,13 +638,24 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit int slen = strlen(subject); memcpy(buf + offset, subject, slen); offset += slen; + offset += add_rfc2047(buf + offset, line, linelen); + } + else { + memset(buf + offset, ' ', indent); + memcpy(buf + offset + indent, line, linelen); + offset += linelen + indent; } - memset(buf + offset, ' ', indent); - memcpy(buf + offset + indent, line, linelen); - offset += linelen + indent; buf[offset++] = '\n'; if (fmt == CMIT_FMT_ONELINE) break; + if (subject) { + static const char header[] = + "Content-Type: text/plain; charset=UTF-8\n" + "Content-Transfer-Encoding: 8bit\n"; + memcpy(buf + offset, header, sizeof(header)-1); + offset += sizeof(header)-1; + subject = NULL; + } if (after_subject) { int slen = strlen(after_subject); if (slen > space - offset - 1) -- cgit v1.2.1 From c831da664799eb4dbf082a843c34c66889548768 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 21 May 2006 23:55:00 -0700 Subject: builtin format-patch: squelch content-type for 7-bit ASCII When --attach is not used, usually we do not say Content-Type: and fluff, but if the commit message is not 7-bit ASCII, mark it as "text/plain; charset=UTF-8". This unclutters output somewhat. Signed-off-by: Junio C Hamano --- commit.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 7597fba70..94f470b75 100644 --- a/commit.c +++ b/commit.c @@ -559,10 +559,24 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit int indent = 4; int parents_shown = 0; const char *msg = commit->buffer; + int plain_non_ascii = 0; if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL) indent = 0; + /* After-subject is used to pass in Content-Type: multipart + * MIME header; in that case we do not have to do the + * plaintext content type even if the commit message has + * non 7-bit ASCII character. Otherwise, check if we need + * to say this is not a 7-bit ASCII. + */ + if (fmt == CMIT_FMT_EMAIL && !after_subject) { + int i; + for (i = 0; !plain_non_ascii && msg[i] && i < len; i++) + if (msg[i] & 0x80) + plain_non_ascii = 1; + } + for (;;) { const char *line = msg; int linelen = get_one_line(msg, len); @@ -648,13 +662,12 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit buf[offset++] = '\n'; if (fmt == CMIT_FMT_ONELINE) break; - if (subject) { + if (subject && plain_non_ascii) { static const char header[] = "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n"; memcpy(buf + offset, header, sizeof(header)-1); offset += sizeof(header)-1; - subject = NULL; } if (after_subject) { int slen = strlen(after_subject); -- cgit v1.2.1 From 885a86abe2e9f7b96a4e2012183c6751635840aa Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 14 Jun 2006 16:45:13 -0700 Subject: Shrink "struct object" a bit This shrinks "struct object" by a small amount, by getting rid of the "struct type *" pointer and replacing it with a 3-bit bitfield instead. In addition, we merge the bitfields and the "flags" field, which incidentally should also remove a useless 4-byte padding from the object when in 64-bit mode. Now, our "struct object" is still too damn large, but it's now less obviously bloated, and of the remaining fields, only the "util" (which is not used by most things) is clearly something that should be eventually discarded. This shrinks the "git-rev-list --all" memory use by about 2.5% on the kernel archive (and, perhaps more importantly, on the larger mozilla archive). That may not sound like much, but I suspect it's more on a 64-bit platform. There are other remaining inefficiencies (the parent lists, for example, probably have horrible malloc overhead), but this was pretty obvious. Most of the patch is just changing the comparison of the "type" pointer from one of the constant string pointers to the appropriate new TYPE_xxx small integer constant. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- commit.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 94f470b75..11fca559d 100644 --- a/commit.c +++ b/commit.c @@ -56,10 +56,10 @@ static struct commit *check_commit(struct object *obj, const unsigned char *sha1, int quiet) { - if (obj->type != commit_type) { + if (obj->type != TYPE_COMMIT) { if (!quiet) error("Object %s is a %s, not a commit", - sha1_to_hex(sha1), obj->type); + sha1_to_hex(sha1), typename(obj->type)); return NULL; } return (struct commit *) obj; @@ -86,11 +86,11 @@ struct commit *lookup_commit(const unsigned char *sha1) if (!obj) { struct commit *ret = xcalloc(1, sizeof(struct commit)); created_object(sha1, &ret->object); - ret->object.type = commit_type; + ret->object.type = TYPE_COMMIT; return ret; } if (!obj->type) - obj->type = commit_type; + obj->type = TYPE_COMMIT; return check_commit(obj, sha1, 0); } -- cgit v1.2.1 From d3ff6f55012c939740ce0982b24aeb6fba3c6e4f Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sat, 17 Jun 2006 18:26:18 -0700 Subject: Move "void *util" from "struct object" into "struct commit" Every single user actually wanted this only for commit objects, and we have no reason to waste space on it for other object types. So just move the structure member from the low-level "struct object" into the "struct commit". This leaves the commit object the same size, and removes one unnecessary pointer from all other object allocations. This shrinks memory usage (still at a fairly hefty half-gig, admittedly) of "git-rev-list --all --objects" on the mozilla repo by another 5% in my tests. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- commit.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 11fca559d..5914200a2 100644 --- a/commit.c +++ b/commit.c @@ -711,12 +711,12 @@ int count_parents(struct commit * commit) void topo_sort_default_setter(struct commit *c, void *data) { - c->object.util = data; + c->util = data; } void *topo_sort_default_getter(struct commit *c) { - return c->object.util; + return c->util; } /* -- cgit v1.2.1 From 855419f764a65e92f1d5dd1b3d50ee987db1d9de Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 19 Jun 2006 10:44:15 -0700 Subject: Add specialized object allocator This creates a simple specialized object allocator for basic objects. This avoids wasting space with malloc overhead (metadata and extra alignment), since the specialized allocator knows the alignment, and that objects, once allocated, are never freed. It also allows us to track some basic statistics about object allocations. For example, for the mozilla import, it shows object usage as follows: blobs: 627629 (14710 kB) trees: 1119035 (34969 kB) commits: 196423 (8440 kB) tags: 1336 (46 kB) and the simpler allocator shaves off about 2.5% off the memory footprint off a "git-rev-list --all --objects", and is a bit faster too. [ Side note: this concludes the series of "save memory in object storage". The thing is, there simply isn't much more to be saved on the objects. Doing "git-rev-list --all --objects" on the mozilla archive has a final total RSS of 131498 pages for me: that's about 513MB. Of that, the object overhead is now just 56MB, the rest is going somewhere else (put another way: the fact that this patch shaves off 2.5% of the total memory overhead, considering that objects are now not much more than 10% of the total shows how big the wasted space really was: this makes object allocations much more memory- and time-efficient). I haven't looked at where the rest is, but I suspect the bulk of it is just the pack-file loading. It may be that we should pack the tree objects separately from the blob objects: for git-rev-list --objects, we don't actually ever need to even look at the blobs, but since trees and blobs are interspersed in the pack-file, we end up not being dense in the tree accesses, so we end up looking at more pages than we strictly need to. So with a 535MB pack-file, it's entirely possible - even likely - that most of the remaining RSS is just the mmap of the pack-file itself. We don't need to map in _all_ of it, but we do end up mapping a fair amount. ] Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- commit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 5914200a2..0fa119828 100644 --- a/commit.c +++ b/commit.c @@ -84,7 +84,7 @@ struct commit *lookup_commit(const unsigned char *sha1) { struct object *obj = lookup_object(sha1); if (!obj) { - struct commit *ret = xcalloc(1, sizeof(struct commit)); + struct commit *ret = alloc_commit_node(); created_object(sha1, &ret->object); ret->object.type = TYPE_COMMIT; return ret; -- cgit v1.2.1 From 0da4677149b433e5424537c6acf45bd4d2f7a6df Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 19 Jun 2006 15:00:17 -0700 Subject: fix rfc2047 formatter. Running git-format-patch on patches from Lukas destroyed the From: line. This fixes it. Signed-off-by: Junio C Hamano --- commit.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 0fa119828..946615d2a 100644 --- a/commit.c +++ b/commit.c @@ -447,7 +447,7 @@ static int add_rfc2047(char *buf, const char *line, int len) memcpy(bp, q_utf8, sizeof(q_utf8)-1); bp += sizeof(q_utf8)-1; for (i = 0; i < len; i++) { - unsigned ch = line[i]; + unsigned ch = line[i] & 0xFF; if (is_rfc2047_special(ch)) { sprintf(bp, "=%02X", ch); bp += 3; @@ -571,10 +571,23 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit * to say this is not a 7-bit ASCII. */ if (fmt == CMIT_FMT_EMAIL && !after_subject) { - int i; - for (i = 0; !plain_non_ascii && msg[i] && i < len; i++) - if (msg[i] & 0x80) + int i, ch, in_body; + + for (in_body = i = 0; (ch = msg[i]) && i < len; i++) { + if (!in_body) { + /* author could be non 7-bit ASCII but + * the log may so; skip over the + * header part first. + */ + if (ch == '\n' && + i + 1 < len && msg[i+1] == '\n') + in_body = 1; + } + else if (ch & 0x80) { plain_non_ascii = 1; + break; + } + } } for (;;) { -- cgit v1.2.1 From 554fe20d805693d962bd41647c5dc075cf7f5261 Mon Sep 17 00:00:00 2001 From: Timo Hirvonen Date: Wed, 28 Jun 2006 12:04:39 +0300 Subject: Make some strings const Signed-off-by: Timo Hirvonen Signed-off-by: Junio C Hamano --- commit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 946615d2a..0327df123 100644 --- a/commit.c +++ b/commit.c @@ -543,7 +543,7 @@ static int add_merge_info(enum cmit_fmt fmt, char *buf, const struct commit *com const char *hex = abbrev ? find_unique_abbrev(p->object.sha1, abbrev) : sha1_to_hex(p->object.sha1); - char *dots = (abbrev && strlen(hex) != 40) ? "..." : ""; + const char *dots = (abbrev && strlen(hex) != 40) ? "..." : ""; parent = parent->next; offset += sprintf(buf + offset, " %s%s", hex, dots); -- cgit v1.2.1 From 3b44f15a35b3550f34a4f796706fceccdc90d506 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 28 Jun 2006 03:51:00 -0700 Subject: connect.c: check the commit buffer boundary while parsing. Signed-off-by: Junio C Hamano --- commit.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 0327df123..e51ffa1c6 100644 --- a/commit.c +++ b/commit.c @@ -236,6 +236,7 @@ static struct commit_graft *lookup_commit_graft(const unsigned char *sha1) int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size) { + char *tail = buffer; char *bufptr = buffer; unsigned char parent[20]; struct commit_list **pptr; @@ -245,9 +246,10 @@ int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size) if (item->object.parsed) return 0; item->object.parsed = 1; - if (memcmp(bufptr, "tree ", 5)) + tail += size; + if (tail <= bufptr + 5 || memcmp(bufptr, "tree ", 5)) return error("bogus commit object %s", sha1_to_hex(item->object.sha1)); - if (get_sha1_hex(bufptr + 5, parent) < 0) + if (tail <= bufptr + 45 || get_sha1_hex(bufptr + 5, parent) < 0) return error("bad tree pointer in commit %s", sha1_to_hex(item->object.sha1)); item->tree = lookup_tree(parent); @@ -257,10 +259,12 @@ int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size) pptr = &item->parents; graft = lookup_commit_graft(item->object.sha1); - while (!memcmp(bufptr, "parent ", 7)) { + while (bufptr + 48 < tail && !memcmp(bufptr, "parent ", 7)) { struct commit *new_parent; - if (get_sha1_hex(bufptr + 7, parent) || bufptr[47] != '\n') + if (tail <= bufptr + 48 || + get_sha1_hex(bufptr + 7, parent) || + bufptr[47] != '\n') return error("bad parents in commit %s", sha1_to_hex(item->object.sha1)); bufptr += 48; if (graft) -- cgit v1.2.1 From 7c6f8aaf6d795f0c9a75671acceb9754ea06fd81 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 29 Jun 2006 15:17:32 +0200 Subject: move get_merge_bases() to core lib. Signed-off-by: Junio C Hamano --- commit.c | 240 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 240 insertions(+) (limited to 'commit.c') diff --git a/commit.c b/commit.c index e51ffa1c6..0431027ef 100644 --- a/commit.c +++ b/commit.c @@ -846,3 +846,243 @@ void sort_in_topological_order_fn(struct commit_list ** list, int lifo, } free(nodes); } + +/* merge-rebase stuff */ + +#define PARENT1 1 +#define PARENT2 2 +#define UNINTERESTING 4 + +static struct commit *interesting(struct commit_list *list) +{ + while (list) { + struct commit *commit = list->item; + list = list->next; + if (commit->object.flags & UNINTERESTING) + continue; + return commit; + } + return NULL; +} + +/* + * A pathological example of how this thing works. + * + * Suppose we had this commit graph, where chronologically + * the timestamp on the commit are A <= B <= C <= D <= E <= F + * and we are trying to figure out the merge base for E and F + * commits. + * + * F + * / \ + * E A D + * \ / / + * B / + * \ / + * C + * + * First we push E and F to list to be processed. E gets bit 1 + * and F gets bit 2. The list becomes: + * + * list=F(2) E(1), result=empty + * + * Then we pop F, the newest commit, from the list. Its flag is 2. + * We scan its parents, mark them reachable from the side that F is + * reachable from, and push them to the list: + * + * list=E(1) D(2) A(2), result=empty + * + * Next pop E and do the same. + * + * list=D(2) B(1) A(2), result=empty + * + * Next pop D and do the same. + * + * list=C(2) B(1) A(2), result=empty + * + * Next pop C and do the same. + * + * list=B(1) A(2), result=empty + * + * Now it is B's turn. We mark its parent, C, reachable from B's side, + * and push it to the list: + * + * list=C(3) A(2), result=empty + * + * Now pop C and notice it has flags==3. It is placed on the result list, + * and the list now contains: + * + * list=A(2), result=C(3) + * + * We pop A and do the same. + * + * list=B(3), result=C(3) + * + * Next, we pop B and something very interesting happens. It has flags==3 + * so it is also placed on the result list, and its parents are marked + * uninteresting, retroactively, and placed back on the list: + * + * list=C(7), result=C(7) B(3) + * + * Now, list does not have any interesting commit. So we find the newest + * commit from the result list that is not marked uninteresting. Which is + * commit B. + * + * + * Another pathological example how this thing used to fail to mark an + * ancestor of a merge base as UNINTERESTING before we introduced the + * postprocessing phase (mark_reachable_commits). + * + * 2 + * H + * 1 / \ + * G A \ + * |\ / \ + * | B \ + * | \ \ + * \ C F + * \ \ / + * \ D / + * \ | / + * \| / + * E + * + * list A B C D E F G H + * G1 H2 - - - - - - 1 2 + * H2 E1 B1 - 1 - - 1 - 1 2 + * F2 E1 B1 A2 2 1 - - 1 2 1 2 + * E3 B1 A2 2 1 - - 3 2 1 2 + * B1 A2 2 1 - - 3 2 1 2 + * C1 A2 2 1 1 - 3 2 1 2 + * D1 A2 2 1 1 1 3 2 1 2 + * A2 2 1 1 1 3 2 1 2 + * B3 2 3 1 1 3 2 1 2 + * C7 2 3 7 1 3 2 1 2 + * + * At this point, unfortunately, everybody in the list is + * uninteresting, so we fail to complete the following two + * steps to fully marking uninteresting commits. + * + * D7 2 3 7 7 3 2 1 2 + * E7 2 3 7 7 7 2 1 2 + * + * and we ended up showing E as an interesting merge base. + * The postprocessing phase re-injects C and continues traversal + * to contaminate D and E. + */ + +static void mark_reachable_commits(struct commit_list *result, + struct commit_list *list) +{ + struct commit_list *tmp; + + /* + * Postprocess to fully contaminate the well. + */ + for (tmp = result; tmp; tmp = tmp->next) { + struct commit *c = tmp->item; + /* Reinject uninteresting ones to list, + * so we can scan their parents. + */ + if (c->object.flags & UNINTERESTING) + commit_list_insert(c, &list); + } + while (list) { + struct commit *c = list->item; + struct commit_list *parents; + + tmp = list; + list = list->next; + free(tmp); + + /* Anything taken out of the list is uninteresting, so + * mark all its parents uninteresting. We do not + * parse new ones (we already parsed all the relevant + * ones). + */ + parents = c->parents; + while (parents) { + struct commit *p = parents->item; + parents = parents->next; + if (!(p->object.flags & UNINTERESTING)) { + p->object.flags |= UNINTERESTING; + commit_list_insert(p, &list); + } + } + } +} + +struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2) +{ + struct commit_list *list = NULL; + struct commit_list *result = NULL; + struct commit_list *tmp = NULL; + + if (rev1 == rev2) + return commit_list_insert(rev1, &result); + + parse_commit(rev1); + parse_commit(rev2); + + rev1->object.flags |= PARENT1; + rev2->object.flags |= PARENT2; + insert_by_date(rev1, &list); + insert_by_date(rev2, &list); + + while (interesting(list)) { + struct commit *commit = list->item; + struct commit_list *parents; + int flags = commit->object.flags + & (PARENT1 | PARENT2 | UNINTERESTING); + + tmp = list; + list = list->next; + free(tmp); + if (flags == (PARENT1 | PARENT2)) { + insert_by_date(commit, &result); + + /* Mark parents of a found merge uninteresting */ + flags |= UNINTERESTING; + } + parents = commit->parents; + while (parents) { + struct commit *p = parents->item; + parents = parents->next; + if ((p->object.flags & flags) == flags) + continue; + parse_commit(p); + p->object.flags |= flags; + insert_by_date(p, &list); + } + } + + if (!result) + return NULL; + + if (result->next && list) + mark_reachable_commits(result, list); + + /* cull duplicates */ + for (tmp = result, list = NULL; tmp; ) { + struct commit *commit = tmp->item; + struct commit_list *next = tmp->next; + if (commit->object.flags & UNINTERESTING) { + if (list != NULL) + list->next = next; + free(tmp); + } else { + if (list == NULL) + result = tmp; + list = tmp; + commit->object.flags |= UNINTERESTING; + } + + tmp = next; + } + + /* reset flags */ + clear_commit_marks(rev1, PARENT1 | PARENT2 | UNINTERESTING); + clear_commit_marks(rev2, PARENT1 | PARENT2 | UNINTERESTING); + + return result; +} -- cgit v1.2.1 From 31609c17251f368584f7b94d44b06194112b4251 Mon Sep 17 00:00:00 2001 From: Rene Scharfe Date: Sun, 2 Jul 2006 01:29:26 +0200 Subject: Add get_merge_bases_clean() Add get_merge_bases_clean(), a wrapper for get_merge_bases() that cleans up after doing its work and make get_merge_bases() NOT clean up. Single-shot programs like git-merge-base can use the dirty and fast version. Also move the object flags used in get_merge_bases() out of the range defined in revision.h. This fixes the "66ae0c77...ced9456a 89719209...262a6ef7" test of the ... operator which is introduced with the next patch. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- commit.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 0431027ef..593414df3 100644 --- a/commit.c +++ b/commit.c @@ -849,9 +849,10 @@ void sort_in_topological_order_fn(struct commit_list ** list, int lifo, /* merge-rebase stuff */ -#define PARENT1 1 -#define PARENT2 2 -#define UNINTERESTING 4 +/* bits #0..7 in revision.h */ +#define PARENT1 (1u<< 8) +#define PARENT2 (1u<< 9) +#define UNINTERESTING (1u<<10) static struct commit *interesting(struct commit_list *list) { @@ -1080,9 +1081,20 @@ struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2) tmp = next; } - /* reset flags */ + return result; +} + +struct commit_list *get_merge_bases_clean(struct commit *rev1, + struct commit *rev2) +{ + unsigned int flags1 = rev1->object.flags; + unsigned int flags2 = rev2->object.flags; + struct commit_list *result = get_merge_bases(rev1, rev2); + clear_commit_marks(rev1, PARENT1 | PARENT2 | UNINTERESTING); clear_commit_marks(rev2, PARENT1 | PARENT2 | UNINTERESTING); + rev1->object.flags = flags1; + rev2->object.flags = flags2; return result; } -- cgit v1.2.1 From 31aea7ef77aff64a02afe1ea5f10375565911808 Mon Sep 17 00:00:00 2001 From: Rene Scharfe Date: Sun, 2 Jul 2006 01:29:58 +0200 Subject: Make clear_commit_marks() clean harder Don't care if objects have been parsed or not and don't stop when we reach a commit that is already clean -- its parents could be dirty. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- commit.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 593414df3..70a4effe5 100644 --- a/commit.c +++ b/commit.c @@ -397,13 +397,12 @@ void clear_commit_marks(struct commit *commit, unsigned int mark) { struct commit_list *parents; + if (!commit) + return; parents = commit->parents; commit->object.flags &= ~mark; while (parents) { - struct commit *parent = parents->item; - if (parent && parent->object.parsed && - (parent->object.flags & mark)) - clear_commit_marks(parent, mark); + clear_commit_marks(parents->item, mark); parents = parents->next; } } -- cgit v1.2.1 From c0fa8255c652e148f0910425d2cc2b8029065008 Mon Sep 17 00:00:00 2001 From: Rene Scharfe Date: Sun, 2 Jul 2006 11:49:38 +0200 Subject: Fold get_merge_bases_clean() into get_merge_bases() Change get_merge_bases() to be able to clean up after itself if needed by adding a cleanup parameter. We don't need to save the flags and restore them afterwards anymore; that was a leftover from before the flags were moved out of the range used in revision.c. clear_commit_marks() sets them to zero, which is enough. Signed-off-by: Rene Scharfe Acked-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- commit.c | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 70a4effe5..94c1d0ec5 100644 --- a/commit.c +++ b/commit.c @@ -1012,7 +1012,8 @@ static void mark_reachable_commits(struct commit_list *result, } } -struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2) +struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2, + int cleanup) { struct commit_list *list = NULL; struct commit_list *result = NULL; @@ -1080,20 +1081,10 @@ struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2) tmp = next; } - return result; -} - -struct commit_list *get_merge_bases_clean(struct commit *rev1, - struct commit *rev2) -{ - unsigned int flags1 = rev1->object.flags; - unsigned int flags2 = rev2->object.flags; - struct commit_list *result = get_merge_bases(rev1, rev2); - - clear_commit_marks(rev1, PARENT1 | PARENT2 | UNINTERESTING); - clear_commit_marks(rev2, PARENT1 | PARENT2 | UNINTERESTING); - rev1->object.flags = flags1; - rev2->object.flags = flags2; + if (cleanup) { + clear_commit_marks(rev1, PARENT1 | PARENT2 | UNINTERESTING); + clear_commit_marks(rev2, PARENT1 | PARENT2 | UNINTERESTING); + } return result; } -- cgit v1.2.1 From 542ccefe896a8960a6ce0b0ba4519537649ae29a Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 2 Jul 2006 11:34:17 -0700 Subject: commit.c: do not redefine UNINTERESTING bit. Signed-off-by: Junio C Hamano --- commit.c | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 94c1d0ec5..a608faf23 100644 --- a/commit.c +++ b/commit.c @@ -851,14 +851,14 @@ void sort_in_topological_order_fn(struct commit_list ** list, int lifo, /* bits #0..7 in revision.h */ #define PARENT1 (1u<< 8) #define PARENT2 (1u<< 9) -#define UNINTERESTING (1u<<10) +#define STALE (1u<<10) static struct commit *interesting(struct commit_list *list) { while (list) { struct commit *commit = list->item; list = list->next; - if (commit->object.flags & UNINTERESTING) + if (commit->object.flags & STALE) continue; return commit; } @@ -920,17 +920,17 @@ static struct commit *interesting(struct commit_list *list) * * Next, we pop B and something very interesting happens. It has flags==3 * so it is also placed on the result list, and its parents are marked - * uninteresting, retroactively, and placed back on the list: + * stale, retroactively, and placed back on the list: * * list=C(7), result=C(7) B(3) * * Now, list does not have any interesting commit. So we find the newest - * commit from the result list that is not marked uninteresting. Which is + * commit from the result list that is not marked stale. Which is * commit B. * * * Another pathological example how this thing used to fail to mark an - * ancestor of a merge base as UNINTERESTING before we introduced the + * ancestor of a merge base as STALE before we introduced the * postprocessing phase (mark_reachable_commits). * * 2 @@ -960,8 +960,8 @@ static struct commit *interesting(struct commit_list *list) * C7 2 3 7 1 3 2 1 2 * * At this point, unfortunately, everybody in the list is - * uninteresting, so we fail to complete the following two - * steps to fully marking uninteresting commits. + * stale, so we fail to complete the following two + * steps to fully marking stale commits. * * D7 2 3 7 7 3 2 1 2 * E7 2 3 7 7 7 2 1 2 @@ -981,10 +981,10 @@ static void mark_reachable_commits(struct commit_list *result, */ for (tmp = result; tmp; tmp = tmp->next) { struct commit *c = tmp->item; - /* Reinject uninteresting ones to list, + /* Reinject stale ones to list, * so we can scan their parents. */ - if (c->object.flags & UNINTERESTING) + if (c->object.flags & STALE) commit_list_insert(c, &list); } while (list) { @@ -995,8 +995,8 @@ static void mark_reachable_commits(struct commit_list *result, list = list->next; free(tmp); - /* Anything taken out of the list is uninteresting, so - * mark all its parents uninteresting. We do not + /* Anything taken out of the list is stale, so + * mark all its parents stale. We do not * parse new ones (we already parsed all the relevant * ones). */ @@ -1004,8 +1004,8 @@ static void mark_reachable_commits(struct commit_list *result, while (parents) { struct commit *p = parents->item; parents = parents->next; - if (!(p->object.flags & UNINTERESTING)) { - p->object.flags |= UNINTERESTING; + if (!(p->object.flags & STALE)) { + p->object.flags |= STALE; commit_list_insert(p, &list); } } @@ -1034,7 +1034,7 @@ struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2, struct commit *commit = list->item; struct commit_list *parents; int flags = commit->object.flags - & (PARENT1 | PARENT2 | UNINTERESTING); + & (PARENT1 | PARENT2 | STALE); tmp = list; list = list->next; @@ -1042,8 +1042,8 @@ struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2, if (flags == (PARENT1 | PARENT2)) { insert_by_date(commit, &result); - /* Mark parents of a found merge uninteresting */ - flags |= UNINTERESTING; + /* Mark parents of a found merge stale */ + flags |= STALE; } parents = commit->parents; while (parents) { @@ -1067,7 +1067,7 @@ struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2, for (tmp = result, list = NULL; tmp; ) { struct commit *commit = tmp->item; struct commit_list *next = tmp->next; - if (commit->object.flags & UNINTERESTING) { + if (commit->object.flags & STALE) { if (list != NULL) list->next = next; free(tmp); @@ -1075,15 +1075,15 @@ struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2, if (list == NULL) result = tmp; list = tmp; - commit->object.flags |= UNINTERESTING; + commit->object.flags |= STALE; } tmp = next; } if (cleanup) { - clear_commit_marks(rev1, PARENT1 | PARENT2 | UNINTERESTING); - clear_commit_marks(rev2, PARENT1 | PARENT2 | UNINTERESTING); + clear_commit_marks(rev1, PARENT1 | PARENT2 | STALE); + clear_commit_marks(rev2, PARENT1 | PARENT2 | STALE); } return result; -- cgit v1.2.1 From 2ef108013ec15e8d5feee6d5c42692ae956ee302 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 3 Jul 2006 03:02:27 -0700 Subject: get_merge_bases: clean up even when there is no common commit. Actually in this case we would have traversed a lot of commits, so cleaning things up is even more important. Signed-off-by: Junio C Hamano --- commit.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index a608faf23..12882fd49 100644 --- a/commit.c +++ b/commit.c @@ -1058,7 +1058,7 @@ struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2, } if (!result) - return NULL; + goto finish; if (result->next && list) mark_reachable_commits(result, list); @@ -1081,6 +1081,7 @@ struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2, tmp = next; } + finish: if (cleanup) { clear_commit_marks(rev1, PARENT1 | PARENT2 | STALE); clear_commit_marks(rev2, PARENT1 | PARENT2 | STALE); -- cgit v1.2.1 From 160b7983034cdd24ea1bf6ef7a2532a2296461c6 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 3 Jul 2006 03:05:20 -0700 Subject: revert clear-commit-marks for now. Earlier change broke "git describe A B" among other things. Revert it for now, and clean the commits smudged by get_merge_bases using clear_object_marks() function. For complex commit ancestry graph, this is way cheaper as well. Signed-off-by: Junio C Hamano --- commit.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 12882fd49..04390643e 100644 --- a/commit.c +++ b/commit.c @@ -397,12 +397,13 @@ void clear_commit_marks(struct commit *commit, unsigned int mark) { struct commit_list *parents; - if (!commit) - return; parents = commit->parents; commit->object.flags &= ~mark; while (parents) { - clear_commit_marks(parents->item, mark); + struct commit *parent = parents->item; + if (parent && parent->object.parsed && + (parent->object.flags & mark)) + clear_commit_marks(parent, mark); parents = parents->next; } } @@ -1082,10 +1083,8 @@ struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2, } finish: - if (cleanup) { - clear_commit_marks(rev1, PARENT1 | PARENT2 | STALE); - clear_commit_marks(rev2, PARENT1 | PARENT2 | STALE); - } + if (cleanup) + clear_object_marks(PARENT1 | PARENT2 | STALE); return result; } -- cgit v1.2.1 From 58ecf5c1cd9b2f712f85e23f065d3214cd867a71 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 4 Jul 2006 17:45:22 -0700 Subject: Re-fix clear_commit_marks(). Fix clear_commit_marks() enough to be usable in get_merge_bases(), and retire now unused clear_object_marks(). Signed-off-by: Junio C Hamano --- commit.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 04390643e..c6bf10d04 100644 --- a/commit.c +++ b/commit.c @@ -397,12 +397,13 @@ void clear_commit_marks(struct commit *commit, unsigned int mark) { struct commit_list *parents; - parents = commit->parents; commit->object.flags &= ~mark; + parents = commit->parents; while (parents) { struct commit *parent = parents->item; - if (parent && parent->object.parsed && - (parent->object.flags & mark)) + + /* Have we already cleared this? */ + if (mark & parent->object.flags) clear_commit_marks(parent, mark); parents = parents->next; } @@ -1083,8 +1084,10 @@ struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2, } finish: - if (cleanup) - clear_object_marks(PARENT1 | PARENT2 | STALE); + if (cleanup) { + clear_commit_marks(rev1, PARENT1 | PARENT2 | STALE); + clear_commit_marks(rev2, PARENT1 | PARENT2 | STALE); + } return result; } -- cgit v1.2.1 From f324943816789fa047441f66edbe11669dc8a9ab Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 4 Jul 2006 18:46:42 -0700 Subject: merge-base: update the clean-up postprocessing This removes the "contaminate the well even more" approach taken in the current merge-base postprosessing code. Instead, when there are more than one merge-base results, we compute the merge-base between them and see if one is a fast-forward of the other, in which case the ancestor is removed from the result. Signed-off-by: Junio C Hamano --- commit.c | 280 ++++++++++++++++++++------------------------------------------- 1 file changed, 90 insertions(+), 190 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index c6bf10d04..522a6f3ac 100644 --- a/commit.c +++ b/commit.c @@ -854,6 +854,7 @@ void sort_in_topological_order_fn(struct commit_list ** list, int lifo, #define PARENT1 (1u<< 8) #define PARENT2 (1u<< 9) #define STALE (1u<<10) +#define RESULT (1u<<11) static struct commit *interesting(struct commit_list *list) { @@ -867,183 +868,42 @@ static struct commit *interesting(struct commit_list *list) return NULL; } -/* - * A pathological example of how this thing works. - * - * Suppose we had this commit graph, where chronologically - * the timestamp on the commit are A <= B <= C <= D <= E <= F - * and we are trying to figure out the merge base for E and F - * commits. - * - * F - * / \ - * E A D - * \ / / - * B / - * \ / - * C - * - * First we push E and F to list to be processed. E gets bit 1 - * and F gets bit 2. The list becomes: - * - * list=F(2) E(1), result=empty - * - * Then we pop F, the newest commit, from the list. Its flag is 2. - * We scan its parents, mark them reachable from the side that F is - * reachable from, and push them to the list: - * - * list=E(1) D(2) A(2), result=empty - * - * Next pop E and do the same. - * - * list=D(2) B(1) A(2), result=empty - * - * Next pop D and do the same. - * - * list=C(2) B(1) A(2), result=empty - * - * Next pop C and do the same. - * - * list=B(1) A(2), result=empty - * - * Now it is B's turn. We mark its parent, C, reachable from B's side, - * and push it to the list: - * - * list=C(3) A(2), result=empty - * - * Now pop C and notice it has flags==3. It is placed on the result list, - * and the list now contains: - * - * list=A(2), result=C(3) - * - * We pop A and do the same. - * - * list=B(3), result=C(3) - * - * Next, we pop B and something very interesting happens. It has flags==3 - * so it is also placed on the result list, and its parents are marked - * stale, retroactively, and placed back on the list: - * - * list=C(7), result=C(7) B(3) - * - * Now, list does not have any interesting commit. So we find the newest - * commit from the result list that is not marked stale. Which is - * commit B. - * - * - * Another pathological example how this thing used to fail to mark an - * ancestor of a merge base as STALE before we introduced the - * postprocessing phase (mark_reachable_commits). - * - * 2 - * H - * 1 / \ - * G A \ - * |\ / \ - * | B \ - * | \ \ - * \ C F - * \ \ / - * \ D / - * \ | / - * \| / - * E - * - * list A B C D E F G H - * G1 H2 - - - - - - 1 2 - * H2 E1 B1 - 1 - - 1 - 1 2 - * F2 E1 B1 A2 2 1 - - 1 2 1 2 - * E3 B1 A2 2 1 - - 3 2 1 2 - * B1 A2 2 1 - - 3 2 1 2 - * C1 A2 2 1 1 - 3 2 1 2 - * D1 A2 2 1 1 1 3 2 1 2 - * A2 2 1 1 1 3 2 1 2 - * B3 2 3 1 1 3 2 1 2 - * C7 2 3 7 1 3 2 1 2 - * - * At this point, unfortunately, everybody in the list is - * stale, so we fail to complete the following two - * steps to fully marking stale commits. - * - * D7 2 3 7 7 3 2 1 2 - * E7 2 3 7 7 7 2 1 2 - * - * and we ended up showing E as an interesting merge base. - * The postprocessing phase re-injects C and continues traversal - * to contaminate D and E. - */ - -static void mark_reachable_commits(struct commit_list *result, - struct commit_list *list) -{ - struct commit_list *tmp; - - /* - * Postprocess to fully contaminate the well. - */ - for (tmp = result; tmp; tmp = tmp->next) { - struct commit *c = tmp->item; - /* Reinject stale ones to list, - * so we can scan their parents. - */ - if (c->object.flags & STALE) - commit_list_insert(c, &list); - } - while (list) { - struct commit *c = list->item; - struct commit_list *parents; - - tmp = list; - list = list->next; - free(tmp); - - /* Anything taken out of the list is stale, so - * mark all its parents stale. We do not - * parse new ones (we already parsed all the relevant - * ones). - */ - parents = c->parents; - while (parents) { - struct commit *p = parents->item; - parents = parents->next; - if (!(p->object.flags & STALE)) { - p->object.flags |= STALE; - commit_list_insert(p, &list); - } - } - } -} - -struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2, - int cleanup) +static struct commit_list *merge_bases(struct commit *one, struct commit *two) { struct commit_list *list = NULL; struct commit_list *result = NULL; - struct commit_list *tmp = NULL; - if (rev1 == rev2) - return commit_list_insert(rev1, &result); + if (one == two) + /* We do not mark this even with RESULT so we do not + * have to clean it up. + */ + return commit_list_insert(one, &result); - parse_commit(rev1); - parse_commit(rev2); + parse_commit(one); + parse_commit(two); - rev1->object.flags |= PARENT1; - rev2->object.flags |= PARENT2; - insert_by_date(rev1, &list); - insert_by_date(rev2, &list); + one->object.flags |= PARENT1; + two->object.flags |= PARENT2; + insert_by_date(one, &list); + insert_by_date(two, &list); while (interesting(list)) { - struct commit *commit = list->item; + struct commit *commit; struct commit_list *parents; - int flags = commit->object.flags - & (PARENT1 | PARENT2 | STALE); + struct commit_list *n; + int flags; - tmp = list; - list = list->next; - free(tmp); - if (flags == (PARENT1 | PARENT2)) { - insert_by_date(commit, &result); + commit = list->item; + n = list->next; + free(list); + list = n; + flags = commit->object.flags & (PARENT1 | PARENT2 | STALE); + if (flags == (PARENT1 | PARENT2)) { + if (!(commit->object.flags & RESULT)) { + commit->object.flags |= RESULT; + insert_by_date(commit, &result); + } /* Mark parents of a found merge stale */ flags |= STALE; } @@ -1059,35 +919,75 @@ struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2, } } - if (!result) - goto finish; - - if (result->next && list) - mark_reachable_commits(result, list); + /* Clean up the result to remove stale ones */ + list = result; result = NULL; + while (list) { + struct commit_list *n = list->next; + if (!(list->item->object.flags & STALE)) + insert_by_date(list->item, &result); + free(list); + list = n; + } + return result; +} - /* cull duplicates */ - for (tmp = result, list = NULL; tmp; ) { - struct commit *commit = tmp->item; - struct commit_list *next = tmp->next; - if (commit->object.flags & STALE) { - if (list != NULL) - list->next = next; - free(tmp); - } else { - if (list == NULL) - result = tmp; - list = tmp; - commit->object.flags |= STALE; +struct commit_list *get_merge_bases(struct commit *one, + struct commit *two, + int cleanup) +{ + const unsigned all_flags = (PARENT1 | PARENT2 | STALE | RESULT); + struct commit_list *list; + struct commit **rslt; + struct commit_list *result; + int cnt, i, j; + + result = merge_bases(one, two); + if (one == two) + return result; + if (!result || !result->next) { + if (cleanup) { + clear_commit_marks(one, all_flags); + clear_commit_marks(two, all_flags); } - - tmp = next; + return result; } - finish: - if (cleanup) { - clear_commit_marks(rev1, PARENT1 | PARENT2 | STALE); - clear_commit_marks(rev2, PARENT1 | PARENT2 | STALE); + /* There are more than one */ + cnt = 0; + list = result; + while (list) { + list = list->next; + cnt++; + } + rslt = xcalloc(cnt, sizeof(*rslt)); + for (list = result, i = 0; list; list = list->next) + rslt[i++] = list->item; + free_commit_list(result); + + clear_commit_marks(one, all_flags); + clear_commit_marks(two, all_flags); + for (i = 0; i < cnt - 1; i++) { + for (j = i+1; j < cnt; j++) { + if (!rslt[i] || !rslt[j]) + continue; + result = merge_bases(rslt[i], rslt[j]); + clear_commit_marks(rslt[i], all_flags); + clear_commit_marks(rslt[j], all_flags); + for (list = result; list; list = list->next) { + if (rslt[i] == list->item) + rslt[i] = NULL; + if (rslt[j] == list->item) + rslt[j] = NULL; + } + } } + /* Surviving ones in rslt[] are the independent results */ + result = NULL; + for (i = 0; i < cnt; i++) { + if (rslt[i]) + insert_by_date(rslt[i], &result); + } + free(rslt); return result; } -- cgit v1.2.1 From 1974632c664c2d573b36a00fa993c1c13dd8a967 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 11 Jul 2006 20:45:31 -0700 Subject: Remove TYPE_* constant macros and use object_type enums consistently. This updates the type-enumeration constants introduced to reduce the memory footprint of "struct object" to match the type bits already used in the packfile format, by removing the former (i.e. TYPE_* constant macros) and using the latter (i.e. enum object_type) throughout the code for consistency. Eventually we can stop passing around the "type strings" entirely, and this will help - no confusion about two different integer enumeration. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- commit.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 522a6f3ac..4c5aac876 100644 --- a/commit.c +++ b/commit.c @@ -56,7 +56,7 @@ static struct commit *check_commit(struct object *obj, const unsigned char *sha1, int quiet) { - if (obj->type != TYPE_COMMIT) { + if (obj->type != OBJ_COMMIT) { if (!quiet) error("Object %s is a %s, not a commit", sha1_to_hex(sha1), typename(obj->type)); @@ -86,11 +86,11 @@ struct commit *lookup_commit(const unsigned char *sha1) if (!obj) { struct commit *ret = alloc_commit_node(); created_object(sha1, &ret->object); - ret->object.type = TYPE_COMMIT; + ret->object.type = OBJ_COMMIT; return ret; } if (!obj->type) - obj->type = TYPE_COMMIT; + obj->type = OBJ_COMMIT; return check_commit(obj, sha1, 0); } -- cgit v1.2.1 From 19b3bd3e2d260323c5e727212a6dcd7fc0f7aa07 Mon Sep 17 00:00:00 2001 From: Robert Shearman Date: Thu, 13 Jul 2006 23:17:22 +0100 Subject: format-patch: Generate a newline between the subject header and the message body format-patch previously didn't generate a newline after a subject. This caused the diffstat to not be displayed in messages with only one line for the commit message. This patch fixes this by adding a newline after the headers if a body hasn't been added. Signed-off-by: Robert Shearman Signed-off-by: Junio C Hamano --- commit.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 522a6f3ac..6ac3bf755 100644 --- a/commit.c +++ b/commit.c @@ -655,6 +655,9 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit continue; } + if (!subject) + body = 1; + if (is_empty_line(line, &linelen)) { if (!body) continue; @@ -662,8 +665,6 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit continue; if (fmt == CMIT_FMT_SHORT) break; - } else { - body = 1; } if (subject) { @@ -702,6 +703,12 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit /* Make sure there is an EOLN for the non-oneline case */ if (fmt != CMIT_FMT_ONELINE) buf[offset++] = '\n'; + /* + * make sure there is another EOLN to separate the headers from whatever + * body the caller appends if we haven't already written a body + */ + if (fmt == CMIT_FMT_EMAIL && !body) + buf[offset++] = '\n'; buf[offset] = '\0'; return offset; } -- cgit v1.2.1 From 96f1e58f524fac8607cfc38896b365b6e8365b51 Mon Sep 17 00:00:00 2001 From: David Rientjes Date: Tue, 15 Aug 2006 10:23:48 -0700 Subject: remove unnecessary initializations [jc: I needed to hand merge the changes to the updated codebase, so the result needs to be checked.] Signed-off-by: David Rientjes Signed-off-by: Junio C Hamano --- commit.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 77f0ca175..972d1b70c 100644 --- a/commit.c +++ b/commit.c @@ -727,10 +727,10 @@ struct commit *pop_commit(struct commit_list **stack) int count_parents(struct commit * commit) { - int count = 0; + int count; struct commit_list * parents = commit->parents; - for (count=0;parents; parents=parents->next,count++) - ; + for (count = 0; parents; parents = parents->next,count++) + ; return count; } -- cgit v1.2.1 From a89fccd28197fa179828c8596791ff16e2268d20 Mon Sep 17 00:00:00 2001 From: David Rientjes Date: Thu, 17 Aug 2006 11:54:57 -0700 Subject: Do not use memcmp(sha1_1, sha1_2, 20) with hardcoded length. Introduces global inline: hashcmp(const unsigned char *sha1, const unsigned char *sha2) Uses memcmp for comparison and returns the result based on the length of the hash name (a future runtime decision). Acked-by: Alex Riesen Signed-off-by: David Rientjes Signed-off-by: Junio C Hamano --- commit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 972d1b70c..d17451fbf 100644 --- a/commit.c +++ b/commit.c @@ -123,7 +123,7 @@ static int commit_graft_pos(const unsigned char *sha1) while (lo < hi) { int mi = (lo + hi) / 2; struct commit_graft *graft = commit_graft[mi]; - int cmp = memcmp(sha1, graft->sha1, 20); + int cmp = hashcmp(sha1, graft->sha1); if (!cmp) return mi; if (cmp < 0) -- cgit v1.2.1 From 55c3eb434ab6d489c632263239be15a1054df7f2 Mon Sep 17 00:00:00 2001 From: Tilman Sauerbeck Date: Thu, 17 Aug 2006 20:44:16 +0200 Subject: Indentation fix. Signed-off-by: Tilman Sauerbeck Signed-off-by: Junio C Hamano --- commit.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index d17451fbf..00bc3de22 100644 --- a/commit.c +++ b/commit.c @@ -7,15 +7,15 @@ int save_commit_buffer = 1; struct sort_node { /* - * the number of children of the associated commit - * that also occur in the list being sorted. - */ + * the number of children of the associated commit + * that also occur in the list being sorted. + */ unsigned int indegree; /* - * reference to original list item that we will re-use - * on output. - */ + * reference to original list item that we will re-use + * on output. + */ struct commit_list * list_item; }; -- cgit v1.2.1 From 9a8e35e98793af086f05d1ca9643052df9b44a74 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sat, 26 Aug 2006 15:45:26 -0700 Subject: Relative timestamps in git log I noticed that I was looking at the kernel gitweb output at some point rather than just do "git log", simply because I liked seeing the simplified date-format, ie the "5 days ago" rather than a full date. This adds infrastructure to do that for "git log" too. It does NOT add the actual flag to enable it, though, so right now this patch is a no-op, but it should now be easy to add a command line flag (and possibly a config file option) to just turn on the "relative" date format. The exact cut-off points when it switches from days to weeks etc are totally arbitrary, but are picked somewhat to avoid the "1 weeks ago" thing (by making it show "10 days ago" rather than "1 week", or "70 minutes ago" rather than "1 hour ago"). [jc: with minor fix and tweak around "month" and "week" area.] Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- commit.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 00bc3de22..c3ff9b417 100644 --- a/commit.c +++ b/commit.c @@ -507,14 +507,14 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const c } switch (fmt) { case CMIT_FMT_MEDIUM: - ret += sprintf(buf + ret, "Date: %s\n", show_date(time, tz)); + ret += sprintf(buf + ret, "Date: %s\n", show_date(time, tz, 0)); break; case CMIT_FMT_EMAIL: ret += sprintf(buf + ret, "Date: %s\n", show_rfc2822_date(time, tz)); break; case CMIT_FMT_FULLER: - ret += sprintf(buf + ret, "%sDate: %s\n", what, show_date(time, tz)); + ret += sprintf(buf + ret, "%sDate: %s\n", what, show_date(time, tz, 0)); break; default: /* notin' */ -- cgit v1.2.1 From 3dfb9278dff6d81fcc062e9a56edab9ece38ea7d Mon Sep 17 00:00:00 2001 From: Jonas Fonseca Date: Mon, 28 Aug 2006 15:52:13 +0200 Subject: Add --relative-date option to the revision interface Exposes the infrastructure from 9a8e35e98793af086f05d1ca9643052df9b44a74. Signed-off-by: Jonas Fonseca Signed-off-by: Junio C Hamano --- commit.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index c3ff9b417..5b6e082c8 100644 --- a/commit.c +++ b/commit.c @@ -467,7 +467,8 @@ static int add_rfc2047(char *buf, const char *line, int len) return bp - buf; } -static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const char *line) +static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, + const char *line, int relative_date) { char *date; int namelen; @@ -507,14 +508,16 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const c } switch (fmt) { case CMIT_FMT_MEDIUM: - ret += sprintf(buf + ret, "Date: %s\n", show_date(time, tz, 0)); + ret += sprintf(buf + ret, "Date: %s\n", + show_date(time, tz, relative_date)); break; case CMIT_FMT_EMAIL: ret += sprintf(buf + ret, "Date: %s\n", show_rfc2822_date(time, tz)); break; case CMIT_FMT_FULLER: - ret += sprintf(buf + ret, "%sDate: %s\n", what, show_date(time, tz, 0)); + ret += sprintf(buf + ret, "%sDate: %s\n", what, + show_date(time, tz, relative_date)); break; default: /* notin' */ @@ -557,7 +560,10 @@ static int add_merge_info(enum cmit_fmt fmt, char *buf, const struct commit *com return offset; } -unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, unsigned long len, char *buf, unsigned long space, int abbrev, const char *subject, const char *after_subject) +unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, + unsigned long len, char *buf, unsigned long space, + int abbrev, const char *subject, + const char *after_subject, int relative_date) { int hdr = 1, body = 0; unsigned long offset = 0; @@ -646,12 +652,14 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit if (!memcmp(line, "author ", 7)) offset += add_user_info("Author", fmt, buf + offset, - line + 7); + line + 7, + relative_date); if (!memcmp(line, "committer ", 10) && (fmt == CMIT_FMT_FULL || fmt == CMIT_FMT_FULLER)) offset += add_user_info("Commit", fmt, buf + offset, - line + 10); + line + 10, + relative_date); continue; } -- cgit v1.2.1 From 14763e7bda06a502455ef4420067205797b9a907 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Wed, 11 Oct 2006 16:16:02 -0700 Subject: commit: fix a segfault when displaying a commit with unreachable parents I was running git show on various commits found by fsck-objects when I found this bug. Since find_unique_abbrev() cannot find an abbreviation for an object not in the database, it will return NULL, which is bad to run strlen() on. So instead, we'll just display the unabbreviated sha1 that we referenced in the commit. I'm not sure that this is the best 'fix' for it because the commit I was trying to show was broken, but I don't think a program should segfault even if the user tries to do something stupid. Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano --- commit.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 5b6e082c8..a6d543eee 100644 --- a/commit.c +++ b/commit.c @@ -548,10 +548,13 @@ static int add_merge_info(enum cmit_fmt fmt, char *buf, const struct commit *com while (parent) { struct commit *p = parent->item; - const char *hex = abbrev - ? find_unique_abbrev(p->object.sha1, abbrev) - : sha1_to_hex(p->object.sha1); - const char *dots = (abbrev && strlen(hex) != 40) ? "..." : ""; + const char *hex = NULL; + const char *dots; + if (abbrev) + hex = find_unique_abbrev(p->object.sha1, abbrev); + if (!hex) + hex = sha1_to_hex(p->object.sha1); + dots = (abbrev && strlen(hex) != 40) ? "..." : ""; parent = parent->next; offset += sprintf(buf + offset, " %s%s", hex, dots); -- cgit v1.2.1 From ed09aef06fda2ba06a7412e3fa43ab1c3449f723 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 30 Oct 2006 20:09:06 +0100 Subject: support fetching into a shallow repository A shallow commit is a commit which has parents, which in turn are "grafted away", i.e. the commit appears as if it were a root. Since these shallow commits should not be edited by the user, but only by core git, they are recorded in the file $GIT_DIR/shallow. A repository containing shallow commits is called shallow. The advantage of a shallow repository is that even if the upstream contains lots of history, your local (shallow) repository needs not occupy much disk space. The disadvantage is that you might miss a merge base when pulling some remote branch. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- commit.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'commit.c') diff --git a/commit.c b/commit.c index a6d543eee..bffa27886 100644 --- a/commit.c +++ b/commit.c @@ -1,6 +1,7 @@ #include "cache.h" #include "tag.h" #include "commit.h" +#include "pkt-line.h" int save_commit_buffer = 1; @@ -221,6 +222,8 @@ static void prepare_commit_graft(void) return; graft_file = get_graft_file(); read_graft_file(graft_file); + /* make sure shallows are read */ + is_repository_shallow(); commit_graft_prepared = 1; } @@ -234,6 +237,24 @@ static struct commit_graft *lookup_commit_graft(const unsigned char *sha1) return commit_graft[pos]; } +int write_shallow_commits(int fd, int use_pack_protocol) +{ + int i, count = 0; + for (i = 0; i < commit_graft_nr; i++) + if (commit_graft[i]->nr_parent < 0) { + const char *hex = + sha1_to_hex(commit_graft[i]->sha1); + count++; + if (use_pack_protocol) + packet_write(fd, "shallow %s", hex); + else { + write(fd, hex, 40); + write(fd, "\n", 1); + } + } + return count; +} + int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size) { char *tail = buffer; -- cgit v1.2.1 From f53514bc2d82f2f5cc7b447575e74aa266ed46f0 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 30 Oct 2006 20:09:53 +0100 Subject: allow deepening of a shallow repository Now, by saying "git fetch -depth " you can deepen a shallow repository. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- commit.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'commit.c') diff --git a/commit.c b/commit.c index bffa27886..d5103cd3c 100644 --- a/commit.c +++ b/commit.c @@ -255,6 +255,19 @@ int write_shallow_commits(int fd, int use_pack_protocol) return count; } +int unregister_shallow(const unsigned char *sha1) +{ + int pos = commit_graft_pos(sha1); + if (pos < 0) + return -1; + if (pos + 1 < commit_graft_nr) + memcpy(commit_graft + pos, commit_graft + pos + 1, + sizeof(struct commit_graft *) + * (commit_graft_nr - pos - 1)); + commit_graft_nr--; + return 0; +} + int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size) { char *tail = buffer; -- cgit v1.2.1 From 577ed5c20b4ca374626c104f90c88550cf415067 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 22 Oct 2006 17:32:47 -0700 Subject: rev-list --left-right The output from "symmetric diff", i.e. A...B, does not distinguish between commits that are reachable from A and the ones that are reachable from B. In this picture, such a symmetric diff includes commits marked with a and b. x---b---b branch B / \ / / . / / \ o---x---a---a branch A However, you cannot tell which ones are 'a' and which ones are 'b' from the output. Sometimes this is frustrating. This adds an output option, --left-right, to rev-list. rev-list --left-right A...B would show ones reachable from A prefixed with '<' and the ones reachable from B prefixed with '>'. When combined with --boundary, boundary commits (the ones marked with 'x' in the above picture) are shown with prefix '-', so you would see list that looks like this: git rev-list --left-right --boundary --pretty=oneline A...B >bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 3rd on b >bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 2nd on b --- commit.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index a6d543eee..289ef65eb 100644 --- a/commit.c +++ b/commit.c @@ -868,11 +868,11 @@ void sort_in_topological_order_fn(struct commit_list ** list, int lifo, /* merge-rebase stuff */ -/* bits #0..7 in revision.h */ -#define PARENT1 (1u<< 8) -#define PARENT2 (1u<< 9) -#define STALE (1u<<10) -#define RESULT (1u<<11) +/* bits #0..15 in revision.h */ +#define PARENT1 (1u<<16) +#define PARENT2 (1u<<17) +#define STALE (1u<<18) +#define RESULT (1u<<19) static struct commit *interesting(struct commit_list *list) { -- cgit v1.2.1 From 2ecd2bbcbe5335c1d9209b6ce28513e4e9d3491b Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 19 Dec 2006 00:14:04 -0800 Subject: Move in_merge_bases() to commit.c This reasonably useful function was hidden inside builtin-branch.c --- commit.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 289ef65eb..3167ce62a 100644 --- a/commit.c +++ b/commit.c @@ -1009,3 +1009,20 @@ struct commit_list *get_merge_bases(struct commit *one, free(rslt); return result; } + +int in_merge_bases(struct commit *rev1, struct commit *rev2) +{ + struct commit_list *bases, *b; + int ret = 0; + + bases = get_merge_bases(rev1, rev2, 1); + for (b = bases; b; b = b->next) { + if (!hashcmp(rev1->object.sha1, b->item->object.sha1)) { + ret = 1; + break; + } + } + + free_commit_list(bases); + return ret; +} -- cgit v1.2.1 From 52883fbd767f8a79a6f98a08907d0a9f6ba1ece1 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 25 Dec 2006 11:48:35 -0800 Subject: Teach log family --encoding Updated commit objects record the encoding used in their encoding header. This updates the log family to reencode it into the encoding specified in i18n.commitencoding (or the default, which is "utf-8") upon output. To force a specific encoding that is different, log family takes command line flag --encoding=; giving --encoding=none entirely disables the reencoding and lets you view log messges in their original encoding. Signed-off-by: Junio C Hamano --- commit.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 58 insertions(+), 3 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 289ef65eb..df4bc0775 100644 --- a/commit.c +++ b/commit.c @@ -1,6 +1,7 @@ #include "cache.h" #include "tag.h" #include "commit.h" +#include "utf8.h" int save_commit_buffer = 1; @@ -563,10 +564,53 @@ static int add_merge_info(enum cmit_fmt fmt, char *buf, const struct commit *com return offset; } -unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, - unsigned long len, char *buf, unsigned long space, +static char *get_header(const struct commit *commit, const char *key) +{ + int key_len = strlen(key); + const char *line = commit->buffer; + + for (;;) { + const char *eol = strchr(line, '\n'), *next; + + if (line == eol) + return NULL; + if (!eol) { + eol = line + strlen(line); + next = NULL; + } else + next = eol + 1; + if (!strncmp(line, key, key_len) && line[key_len] == ' ') { + int len = eol - line - key_len; + char *ret = xmalloc(len); + memcpy(ret, line + key_len + 1, len - 1); + ret[len - 1] = '\0'; + return ret; + } + line = next; + } +} + +static char *logmsg_reencode(const struct commit *commit) +{ + char *encoding = get_header(commit, "encoding"); + char *out; + + if (!encoding || !strcmp(encoding, git_commit_encoding)) + return NULL; + out = reencode_string(commit->buffer, git_commit_encoding, encoding); + free(encoding); + if (!out) + return NULL; + return out; +} + +unsigned long pretty_print_commit(enum cmit_fmt fmt, + const struct commit *commit, + unsigned long len, + char *buf, unsigned long space, int abbrev, const char *subject, - const char *after_subject, int relative_date) + const char *after_subject, + int relative_date) { int hdr = 1, body = 0; unsigned long offset = 0; @@ -574,6 +618,15 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit int parents_shown = 0; const char *msg = commit->buffer; int plain_non_ascii = 0; + char *reencoded = NULL; + + if (*git_commit_encoding) { + reencoded = logmsg_reencode(commit); + if (reencoded) { + msg = reencoded; + len = strlen(msg); + } + } if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL) indent = 0; @@ -721,6 +774,8 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit if (fmt == CMIT_FMT_EMAIL && !body) buf[offset++] = '\n'; buf[offset] = '\0'; + + free(reencoded); return offset; } -- cgit v1.2.1 From d2c11a38c476bdfa3dd2387a0d933b8c00e4dfe3 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 27 Dec 2006 16:41:33 -0800 Subject: UTF-8: introduce i18n.logoutputencoding. It is plausible for somebody to want to view the commit log in a different encoding from i18n.commitencoding -- the project's policy may be UTF-8 and the user may be using a commit message hook to run iconv to conform to that policy (and either not have i18n.commitencoding to default to UTF-8 or have it explicitly set to UTF-8). Even then, Latin-1 may be more convenient for the usual pager and the terminal the user uses. The new variable i18n.logoutputencoding is used in preference to i18n.commitencoding to decide what encoding to recode the log output in when git-log and friends formats the commit log message. Signed-off-by: Junio C Hamano --- commit.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index df4bc0775..6f2839a5c 100644 --- a/commit.c +++ b/commit.c @@ -592,12 +592,20 @@ static char *get_header(const struct commit *commit, const char *key) static char *logmsg_reencode(const struct commit *commit) { - char *encoding = get_header(commit, "encoding"); + char *encoding; char *out; + char *output_encoding = (git_log_output_encoding + ? git_log_output_encoding + : git_commit_encoding); - if (!encoding || !strcmp(encoding, git_commit_encoding)) + if (!output_encoding) return NULL; - out = reencode_string(commit->buffer, git_commit_encoding, encoding); + encoding = get_header(commit, "encoding"); + if (!encoding || !strcmp(encoding, output_encoding)) { + free(encoding); + return NULL; + } + out = reencode_string(commit->buffer, output_encoding, encoding); free(encoding); if (!out) return NULL; @@ -618,15 +626,10 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, int parents_shown = 0; const char *msg = commit->buffer; int plain_non_ascii = 0; - char *reencoded = NULL; + char *reencoded = logmsg_reencode(commit); - if (*git_commit_encoding) { - reencoded = logmsg_reencode(commit); - if (reencoded) { - msg = reencoded; - len = strlen(msg); - } - } + if (reencoded) + msg = reencoded; if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL) indent = 0; @@ -643,7 +646,7 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, for (in_body = i = 0; (ch = msg[i]) && i < len; i++) { if (!in_body) { /* author could be non 7-bit ASCII but - * the log may so; skip over the + * the log may be so; skip over the * header part first. */ if (ch == '\n' && -- cgit v1.2.1 From 4b46e22d48271d1a220133a925dc5009048eb577 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 30 Dec 2006 02:18:24 -0800 Subject: commit re-encoding: fix confusion between no and default conversion. Telling the git-log family not to do any character conversion is done with --encoding=none, which sets log_output_encoding to an empty string. However, logmsg_reencode() confused this with log_output_encoding and commit_encoding set to NULL. The latter means we should use the default encoding (i.e. utf-8). Signed-off-by: Junio C Hamano --- commit.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'commit.c') diff --git a/commit.c b/commit.c index eb06afbbe..e13b9cb6a 100644 --- a/commit.c +++ b/commit.c @@ -633,6 +633,8 @@ static char *logmsg_reencode(const struct commit *commit) : git_commit_encoding); if (!output_encoding) + output_encoding = "utf-8"; + else if (!*output_encoding) return NULL; encoding = get_header(commit, "encoding"); if (!encoding || !strcmp(encoding, output_encoding)) { -- cgit v1.2.1 From 53af9816bcb1d441fef76c3adaf0c4cb858768ac Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 30 Dec 2006 15:49:32 -0800 Subject: i18n: drop "encoding" header in the output after re-coding. After re-coding the commit message into the encoding the user specified (either with core.logoutputencidng or --encoding option), this drops the "encoding" header altogether. The output is after re-coding as the user asked (either with the config or --encoding= option), and the extra header becomes redundant information. Signed-off-by: Junio C Hamano --- commit.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) (limited to 'commit.c') diff --git a/commit.c b/commit.c index e13b9cb6a..afdf27eec 100644 --- a/commit.c +++ b/commit.c @@ -624,6 +624,48 @@ static char *get_header(const struct commit *commit, const char *key) } } +static char *replace_encoding_header(char *buf, char *encoding) +{ + char *encoding_header = strstr(buf, "\nencoding "); + char *end_of_encoding_header; + int encoding_header_pos; + int encoding_header_len; + int new_len; + int need_len; + int buflen = strlen(buf) + 1; + + if (!encoding_header) + return buf; /* should not happen but be defensive */ + encoding_header++; + end_of_encoding_header = strchr(encoding_header, '\n'); + if (!end_of_encoding_header) + return buf; /* should not happen but be defensive */ + end_of_encoding_header++; + + encoding_header_len = end_of_encoding_header - encoding_header; + encoding_header_pos = encoding_header - buf; + + if (is_encoding_utf8(encoding)) { + /* we have re-coded to UTF-8; drop the header */ + memmove(encoding_header, end_of_encoding_header, + buflen - (encoding_header_pos + encoding_header_len)); + return buf; + } + new_len = strlen(encoding); + need_len = new_len + strlen("encoding \n"); + if (encoding_header_len < need_len) { + buf = xrealloc(buf, buflen + (need_len - encoding_header_len)); + encoding_header = buf + encoding_header_pos; + end_of_encoding_header = encoding_header + encoding_header_len; + } + memmove(end_of_encoding_header + (need_len - encoding_header_len), + end_of_encoding_header, + buflen - (encoding_header_pos + encoding_header_len)); + memcpy(encoding_header + 9, encoding, strlen(encoding)); + encoding_header[9 + new_len] = '\n'; + return buf; +} + static char *logmsg_reencode(const struct commit *commit) { char *encoding; @@ -642,6 +684,9 @@ static char *logmsg_reencode(const struct commit *commit) return NULL; } out = reencode_string(commit->buffer, output_encoding, encoding); + if (out) + out = replace_encoding_header(out, output_encoding); + free(encoding); if (!out) return NULL; -- cgit v1.2.1 From e90068a9046ccaf0bed82fd180b4748edbd5659a Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 31 Dec 2006 18:18:23 -0800 Subject: i18n: do not leak 'encoding' header even when we cheat the conversion. We special case the case where encoding recorded in the commit and the output encoding are the same and do not call iconv(). But we should drop 'encoding' header for this case as well for consistency. Signed-off-by: Junio C Hamano --- commit.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index afdf27eec..544e42629 100644 --- a/commit.c +++ b/commit.c @@ -679,11 +679,13 @@ static char *logmsg_reencode(const struct commit *commit) else if (!*output_encoding) return NULL; encoding = get_header(commit, "encoding"); - if (!encoding || !strcmp(encoding, output_encoding)) { - free(encoding); + if (!encoding) return NULL; - } - out = reencode_string(commit->buffer, output_encoding, encoding); + if (!strcmp(encoding, output_encoding)) + out = strdup(commit->buffer); + else + out = reencode_string(commit->buffer, + output_encoding, encoding); if (out) out = replace_encoding_header(out, output_encoding); -- cgit v1.2.1 From f3a47405bb27846d62d20b056817f9c7d320e2db Mon Sep 17 00:00:00 2001 From: Lars Hjemli Date: Wed, 3 Jan 2007 14:34:13 +0100 Subject: Skip excessive blank lines before commit body This modifies pretty_print_commit() to make the output of git-rev-list and friends a bit more predictable. A commit body starting with blank lines might be unheard-of, but still possible to create using git-commit-tree (so is bound to appear somewhere, sometime). Signed-off-by: Lars Hjemli Signed-off-by: Junio C Hamano --- commit.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 544e42629..2a58175ac 100644 --- a/commit.c +++ b/commit.c @@ -703,7 +703,7 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const char *after_subject, int relative_date) { - int hdr = 1, body = 0; + int hdr = 1, body = 0, seen_title = 0; unsigned long offset = 0; int indent = 4; int parents_shown = 0; @@ -809,6 +809,8 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, body = 1; if (is_empty_line(line, &linelen)) { + if (!seen_title) + continue; if (!body) continue; if (subject) @@ -817,6 +819,7 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, break; } + seen_title = 1; if (subject) { int slen = strlen(subject); memcpy(buf + offset, subject, slen); -- cgit v1.2.1 From 93822c2239a336e5cb583549071c59202ef6c5b2 Mon Sep 17 00:00:00 2001 From: Andy Whitcroft Date: Mon, 8 Jan 2007 15:58:23 +0000 Subject: short i/o: fix calls to write to use xwrite or write_in_full We have a number of badly checked write() calls. Often we are expecting write() to write exactly the size we requested or fail, this fails to handle interrupts or short writes. Switch to using the new write_in_full(). Otherwise we at a minimum need to check for EINTR and EAGAIN, where this is appropriate use xwrite(). Note, the changes to config handling are much larger and handled in the next patch in the sequence. Signed-off-by: Andy Whitcroft Signed-off-by: Junio C Hamano --- commit.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 2a58175ac..9ce45cec2 100644 --- a/commit.c +++ b/commit.c @@ -249,8 +249,10 @@ int write_shallow_commits(int fd, int use_pack_protocol) if (use_pack_protocol) packet_write(fd, "shallow %s", hex); else { - write(fd, hex, 40); - write(fd, "\n", 1); + if (write_in_full(fd, hex, 40) != 40) + break; + if (write_in_full(fd, "\n", 1) != 1) + break; } } return count; -- cgit v1.2.1 From 1295228d1fdda17c2ef62e712649c962c3da5eb7 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 8 Jan 2007 23:10:49 -0800 Subject: merge-base: do not leak commit list Signed-off-by: Junio C Hamano --- commit.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 9ce45cec2..496d37aa0 100644 --- a/commit.c +++ b/commit.c @@ -1012,7 +1012,7 @@ void sort_in_topological_order_fn(struct commit_list ** list, int lifo, free(nodes); } -/* merge-rebase stuff */ +/* merge-base stuff */ /* bits #0..15 in revision.h */ #define PARENT1 (1u<<16) @@ -1020,6 +1020,8 @@ void sort_in_topological_order_fn(struct commit_list ** list, int lifo, #define STALE (1u<<18) #define RESULT (1u<<19) +static const unsigned all_flags = (PARENT1 | PARENT2 | STALE | RESULT); + static struct commit *interesting(struct commit_list *list) { while (list) { @@ -1084,6 +1086,7 @@ static struct commit_list *merge_bases(struct commit *one, struct commit *two) } /* Clean up the result to remove stale ones */ + free_commit_list(list); list = result; result = NULL; while (list) { struct commit_list *n = list->next; @@ -1099,7 +1102,6 @@ struct commit_list *get_merge_bases(struct commit *one, struct commit *two, int cleanup) { - const unsigned all_flags = (PARENT1 | PARENT2 | STALE | RESULT); struct commit_list *list; struct commit **rslt; struct commit_list *result; -- cgit v1.2.1 From 03840fc32d783be6750bf7e41a89687b8c3053eb Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 8 Jan 2007 23:22:31 -0800 Subject: Allow in_merge_bases() to take more than one reference commits. The internal function in_merge_bases(A, B) is used to make sure that commit A is an ancestor of commit B. This changes the signature of it to take an array of B's and updates its current callers. Signed-off-by: Junio C Hamano --- commit.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 496d37aa0..aa14c5adb 100644 --- a/commit.c +++ b/commit.c @@ -1158,14 +1158,17 @@ struct commit_list *get_merge_bases(struct commit *one, 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; } -- cgit v1.2.1 From f7e68b2967182f14547125d1369f37ad4d83187e Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 12 Jan 2007 17:32:38 -0800 Subject: Use log output encoding in --pretty=email headers. Private functions add_rfc2047() and pretty_print_commit() assumed they are only emitting UTF-8. Signed-off-by: Junio C Hamano --- commit.c | 82 +++++++++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 55 insertions(+), 27 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 496d37aa0..9b2b842e7 100644 --- a/commit.c +++ b/commit.c @@ -464,20 +464,29 @@ static int get_one_line(const char *msg, unsigned long len) return ret; } +/* High bit set, or ISO-2022-INT */ +static int non_ascii(int ch) +{ + ch = (ch & 0xff); + return ((ch & 0x80) || (ch == 0x1b)); +} + static int is_rfc2047_special(char ch) { - return ((ch & 0x80) || (ch == '=') || (ch == '?') || (ch == '_')); + return (non_ascii(ch) || (ch == '=') || (ch == '?') || (ch == '_')); } -static int add_rfc2047(char *buf, const char *line, int len) +static int add_rfc2047(char *buf, const char *line, int len, + const char *encoding) { char *bp = buf; int i, needquote; - static const char q_utf8[] = "=?utf-8?q?"; + char q_encoding[128]; + const char *q_encoding_fmt = "=?%s?q?"; for (i = needquote = 0; !needquote && i < len; i++) { - unsigned ch = line[i]; - if (ch & 0x80) + int ch = line[i]; + if (non_ascii(ch)) needquote++; if ((i + 1 < len) && (ch == '=' && line[i+1] == '?')) @@ -486,8 +495,11 @@ static int add_rfc2047(char *buf, const char *line, int len) if (!needquote) return sprintf(buf, "%.*s", len, line); - memcpy(bp, q_utf8, sizeof(q_utf8)-1); - bp += sizeof(q_utf8)-1; + i = snprintf(q_encoding, sizeof(q_encoding), q_encoding_fmt, encoding); + if (sizeof(q_encoding) < i) + die("Insanely long encoding name %s", encoding); + memcpy(bp, q_encoding, i); + bp += i; for (i = 0; i < len; i++) { unsigned ch = line[i] & 0xFF; if (is_rfc2047_special(ch)) { @@ -505,7 +517,8 @@ static int add_rfc2047(char *buf, const char *line, int len) } static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, - const char *line, int relative_date) + const char *line, int relative_date, + const char *encoding) { char *date; int namelen; @@ -533,7 +546,8 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, filler = ""; strcpy(buf, "From: "); ret = strlen(buf); - ret += add_rfc2047(buf + ret, line, display_name_length); + ret += add_rfc2047(buf + ret, line, display_name_length, + encoding); memcpy(buf + ret, name_tail, namelen - display_name_length); ret += namelen - display_name_length; buf[ret++] = '\n'; @@ -668,21 +682,18 @@ static char *replace_encoding_header(char *buf, char *encoding) return buf; } -static char *logmsg_reencode(const struct commit *commit) +static char *logmsg_reencode(const struct commit *commit, + char *output_encoding) { char *encoding; char *out; - char *output_encoding = (git_log_output_encoding - ? git_log_output_encoding - : git_commit_encoding); + char *utf8 = "utf-8"; - if (!output_encoding) - output_encoding = "utf-8"; - else if (!*output_encoding) + if (!*output_encoding) return NULL; encoding = get_header(commit, "encoding"); if (!encoding) - return NULL; + encoding = utf8; if (!strcmp(encoding, output_encoding)) out = strdup(commit->buffer); else @@ -691,7 +702,8 @@ static char *logmsg_reencode(const struct commit *commit) if (out) out = replace_encoding_header(out, output_encoding); - free(encoding); + if (encoding != utf8) + free(encoding); if (!out) return NULL; return out; @@ -711,8 +723,15 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, int parents_shown = 0; const char *msg = commit->buffer; int plain_non_ascii = 0; - char *reencoded = logmsg_reencode(commit); + char *reencoded; + char *encoding; + encoding = (git_log_output_encoding + ? git_log_output_encoding + : git_commit_encoding); + if (!encoding) + encoding = "utf-8"; + reencoded = logmsg_reencode(commit, encoding); if (reencoded) msg = reencoded; @@ -738,7 +757,7 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, i + 1 < len && msg[i+1] == '\n') in_body = 1; } - else if (ch & 0x80) { + else if (non_ascii(ch)) { plain_non_ascii = 1; break; } @@ -797,13 +816,15 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, offset += add_user_info("Author", fmt, buf + offset, line + 7, - relative_date); + relative_date, + encoding); if (!memcmp(line, "committer ", 10) && (fmt == CMIT_FMT_FULL || fmt == CMIT_FMT_FULLER)) offset += add_user_info("Commit", fmt, buf + offset, line + 10, - relative_date); + relative_date, + encoding); continue; } @@ -826,7 +847,8 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, int slen = strlen(subject); memcpy(buf + offset, subject, slen); offset += slen; - offset += add_rfc2047(buf + offset, line, linelen); + offset += add_rfc2047(buf + offset, line, linelen, + encoding); } else { memset(buf + offset, ' ', indent); @@ -837,11 +859,17 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, if (fmt == CMIT_FMT_ONELINE) break; if (subject && plain_non_ascii) { - static const char header[] = - "Content-Type: text/plain; charset=UTF-8\n" + int sz; + char header[512]; + const char *header_fmt = + "Content-Type: text/plain; charset=%s\n" "Content-Transfer-Encoding: 8bit\n"; - memcpy(buf + offset, header, sizeof(header)-1); - offset += sizeof(header)-1; + sz = snprintf(header, sizeof(header), header_fmt, + encoding); + if (sizeof(header) < sz) + die("Encoding name %s too long", encoding); + memcpy(buf + offset, header, sz); + offset += sz; } if (after_subject) { int slen = strlen(after_subject); -- cgit v1.2.1 From b6936205e73c058784288d21d1937e5bba26b91b Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Fri, 2 Feb 2007 05:10:25 -0800 Subject: Disallow invalid --pretty= abbreviations --pretty=o is a valid abbreviation, --pretty=omfg is not Noticed by: Nicolas Vilz Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano --- commit.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 9b2b842e7..3e8c87294 100644 --- a/commit.c +++ b/commit.c @@ -47,7 +47,8 @@ enum cmit_fmt get_commit_format(const char *arg) if (*arg == '=') arg++; for (i = 0; i < ARRAY_SIZE(cmt_fmts); i++) { - if (!strncmp(arg, cmt_fmts[i].n, cmt_fmts[i].cmp_len)) + if (!strncmp(arg, cmt_fmts[i].n, cmt_fmts[i].cmp_len) && + !strncmp(arg, cmt_fmts[i].n, strlen(arg))) return cmt_fmts[i].v; } -- cgit v1.2.1 From e52a5de45ab8b61bdddf48a466cb3388b38ad7a4 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Fri, 23 Feb 2007 01:35:03 +0100 Subject: pretty-formats: add 'format:' With this patch, $ git show -s \ --pretty=format:' Ze komit %h woss%n dunn buy ze great %an' shows something like Ze komit 04c5c88 woss dunn buy ze great Junio C Hamano The supported placeholders are: '%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': switch color to red '%Cgreen': switch color to green '%Cblue': switch color to blue '%Creset': reset color '%n': newline Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- commit.c | 195 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 195 insertions(+) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 3e8c87294..f78ce262f 100644 --- a/commit.c +++ b/commit.c @@ -3,6 +3,7 @@ #include "commit.h" #include "pkt-line.h" #include "utf8.h" +#include "interpolate.h" int save_commit_buffer = 1; @@ -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; @@ -46,6 +50,12 @@ enum cmit_fmt get_commit_format(const char *arg) 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))) @@ -710,6 +720,188 @@ static char *logmsg_reencode(const struct commit *commit, 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)); + 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); + 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); + + 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, eol - i); + 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, ""); + + 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, @@ -727,6 +919,9 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, char *reencoded; char *encoding; + if (fmt == CMIT_FMT_USERFORMAT) + return format_commit_message(commit, msg, buf, space); + encoding = (git_log_output_encoding ? git_log_output_encoding : git_commit_encoding); -- cgit v1.2.1 From 21666f1aae4e890d8f50924f9e80763b27e6a45d Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Mon, 26 Feb 2007 14:55:59 -0500 Subject: convert object type handling from a string to a number We currently have two parallel notation for dealing with object types in the code: a string and a numerical value. One of them is obviously redundent, and the most used one requires more stack space and a bunch of strcmp() all over the place. This is an initial step for the removal of the version using a char array found in object reading code paths. The patch is unfortunately large but there is no sane way to split it in smaller parts without breaking the system. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- commit.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 8d279b0b6..da515a497 100644 --- a/commit.c +++ b/commit.c @@ -342,18 +342,18 @@ int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size) 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)); -- cgit v1.2.1 From 3a55602eeca4ac8670e8698a7187e18b95683344 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 6 Mar 2007 20:44:17 -0500 Subject: General const correctness fixes We shouldn't attempt to assign constant strings into char*, as the string is not writable at runtime. Likewise we should always be treating unsigned values as unsigned values, not as signed values. Most of these are very straightforward. The only exception is the (unnecessary) xstrdup/free in builtin-branch.c for the detached head case. Since this is a user-level interactive type program and that particular code path is executed no more than once, I feel that the extra xstrdup call is well worth the easy elimination of this warning. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- commit.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 555252734..5b9234e12 100644 --- a/commit.c +++ b/commit.c @@ -651,7 +651,7 @@ static char *get_header(const struct commit *commit, const char *key) } } -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 *end_of_encoding_header; @@ -694,29 +694,26 @@ static char *replace_encoding_header(char *buf, char *encoding) } 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)) + use_encoding = encoding ? encoding : utf8; + if (!strcmp(use_encoding, output_encoding)) out = strdup(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; } @@ -917,7 +914,7 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, 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); -- cgit v1.2.1 From 567fb65e251d946cee2b73a9b188231652fe663c Mon Sep 17 00:00:00 2001 From: James Bowes Date: Mon, 19 Mar 2007 17:42:40 -0400 Subject: Replace remaining instances of strdup with xstrdup. Signed-off-by: James Bowes Signed-off-by: Junio C Hamano --- commit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 5b9234e12..718e56885 100644 --- a/commit.c +++ b/commit.c @@ -706,7 +706,7 @@ static char *logmsg_reencode(const struct commit *commit, encoding = get_header(commit, "encoding"); use_encoding = encoding ? encoding : utf8; if (!strcmp(use_encoding, output_encoding)) - out = strdup(commit->buffer); + out = xstrdup(commit->buffer); else out = reencode_string(commit->buffer, output_encoding, use_encoding); -- cgit v1.2.1 From 4621af371686f5c787c172c285562ad997281821 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Tue, 27 Mar 2007 19:26:28 -0400 Subject: --pretty=format: fix broken %ct and %at interpolation A pointer arithmetic error in fill_person caused random data from the commit object to be included with the timestamp, which looked something like: $ git-rev-list --pretty=format:%ct origin/next | head commit 98453bdb3db10db26099749bc4f2dc029bed9aa9 1174977948 -0700 Merge branch 'master' into next * master: Bisect: Use commit c0ce981f5ebfd02463ff697b2fca52c7a54b0625 1174889646 -0700 Signed-off-by: Jeff King Acked-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- commit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 718e56885..a92958cfa 100644 --- a/commit.c +++ b/commit.c @@ -760,7 +760,7 @@ static void fill_person(struct interp *table, const char *msg, int len) if (msg + start == ep) return; - table[5].value = xstrndup(msg + start, ep - msg + start); + table[5].value = xstrndup(msg + start, ep - (msg + start)); /* parse tz */ for (start = ep - msg + 1; start < len && isspace(msg[start]); start++) -- cgit v1.2.1 From 542e165cdc4fbebbfe7c8954ca1aa1d4162e38cb Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 28 Mar 2007 13:33:37 -0700 Subject: Fix "--pretty=format:" for parent related items. There are two breakages in the %P/%p interpolation. It appended an excess SP at the end of the list, and it gave uninitialized contents of a buffer on the stack for root commits. This fixes it, while updating the t6006 test which expected the wrong output. Signed-off-by: Junio C Hamano --- commit.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index a92958cfa..9ac533c85 100644 --- a/commit.c +++ b/commit.c @@ -849,19 +849,23 @@ static long format_commit_message(const struct commit *commit, 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 ", + i += snprintf(parents + i, sizeof(parents) - i - 1, " %s", sha1_to_hex(p->item->object.sha1)); - interp_set_entry(table, IPARENTS, parents); + 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 ", + 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); + interp_set_entry(table, IPARENTS_ABBREV, parents + 1); for (i = 0, state = HEADER; msg[i] && state < BODY; i++) { int eol; -- cgit v1.2.1 From 6a5ea2d023906a6e8feea42afa45536db6fd493f Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 28 Mar 2007 17:09:05 -0400 Subject: Fix "--pretty=format:" encoding item It printed the header "encoding " instead of just showing the encoding, as all other items do. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- commit.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 9ac533c85..a4f2e74c0 100644 --- a/commit.c +++ b/commit.c @@ -888,7 +888,8 @@ static long format_commit_message(const struct commit *commit, fill_person(table + ICOMMITTER_NAME, msg + i + 10, eol - i - 10); else if (!prefixcmp(msg + i, "encoding ")) - table[IENCODING].value = xstrndup(msg + i, eol - i); + table[IENCODING].value = + xstrndup(msg + i + 9, eol - i - 9); i = eol; } if (msg[i]) -- cgit v1.2.1 From d0e50cb4cb8e0cc8b445a9749547019efaa5703e Mon Sep 17 00:00:00 2001 From: Jeff King Date: Wed, 28 Mar 2007 17:52:09 -0400 Subject: commit: fix pretty-printing of messages with "\nencoding " The function replace_encoding_header is given the whole commit buffer, including the commit message. When looking for the encoding header, if none was found in the header, it would locate any line in the commit message matching "\nencoding " and remove it. Instead, we now make sure to search only to the end of the header. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- commit.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 3e8c87294..1fe23b6e3 100644 --- a/commit.c +++ b/commit.c @@ -644,6 +644,7 @@ static char *get_header(const struct commit *commit, const char *key) static char *replace_encoding_header(char *buf, 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; @@ -651,8 +652,10 @@ static char *replace_encoding_header(char *buf, char *encoding) 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) -- cgit v1.2.1 From 199c45bf2b168cb8b2231e45f35e5fd588c2fc19 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 9 Apr 2007 02:34:05 -0700 Subject: Add %m to '--pretty=format:' When used with '--boundary A...B', this shows the -/ marker you would get with --left-right option to 'git-log' family. When symmetric diff is not used, everybody is shown to be on the "right" branch. Signed-off-by: Junio C Hamano --- commit.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 754d1b8a0..952095faa 100644 --- a/commit.c +++ b/commit.c @@ -4,6 +4,8 @@ #include "pkt-line.h" #include "utf8.h" #include "interpolate.h" +#include "diff.h" +#include "revision.h" int save_commit_buffer = 1; @@ -808,7 +810,8 @@ static long format_commit_message(const struct commit *commit, { "%Cgreen" }, /* green */ { "%Cblue" }, /* blue */ { "%Creset" }, /* reset color */ - { "%n" } /* newline */ + { "%n" }, /* newline */ + { "%m" }, /* left/right/bottom */ }; enum interp_index { IHASH = 0, IHASH_ABBREV, @@ -824,14 +827,15 @@ static long format_commit_message(const struct commit *commit, ISUBJECT, IBODY, IRED, IGREEN, IBLUE, IRESET_COLOR, - INEWLINE + INEWLINE, + ILEFT_RIGHT, }; struct commit_list *p; char parents[1024]; int i; enum { HEADER, SUBJECT, BODY } state; - if (INEWLINE + 1 != ARRAY_SIZE(table)) + if (ILEFT_RIGHT + 1 != ARRAY_SIZE(table)) die("invalid interp table!"); /* these are independent of the commit */ @@ -852,6 +856,12 @@ static long format_commit_message(const struct commit *commit, interp_set_entry(table, ITREE_ABBREV, find_unique_abbrev(commit->tree->object.sha1, DEFAULT_ABBREV)); + interp_set_entry(table, ILEFT_RIGHT, + (commit->object.flags & BOUNDARY) + ? "-" + : (commit->object.flags & SYMMETRIC_LEFT) + ? "<" + : ">"); parents[1] = 0; for (i = 0, p = commit->parents; -- cgit v1.2.1 From 100c5f3b0b27ec6617de1a785c4ff481e92636c1 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 16 Apr 2007 22:11:43 -0700 Subject: Clean up object creation to use more common code This replaces the fairly odd "created_object()" function that did _most_ of the object setup with a more complete "create_object()" function that also has a more natural calling convention. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- commit.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 952095faa..10466c4ae 100644 --- a/commit.c +++ b/commit.c @@ -98,12 +98,8 @@ struct commit *lookup_commit_reference(const unsigned char *sha1) struct commit *lookup_commit(const unsigned char *sha1) { struct object *obj = lookup_object(sha1); - if (!obj) { - struct commit *ret = alloc_commit_node(); - created_object(sha1, &ret->object); - ret->object.type = OBJ_COMMIT; - return ret; - } + if (!obj) + return create_object(sha1, OBJ_COMMIT, alloc_commit_node()); if (!obj->type) obj->type = OBJ_COMMIT; return check_commit(obj, sha1, 0); -- cgit v1.2.1 From a7b02ccf9a682fa0c2b28df6ca20f9199cdca4de Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 24 Apr 2007 23:36:22 -0700 Subject: Add --date={local,relative,default} This adds --date={local,relative,default} option to log family of commands, to allow displaying timestamps in user's local timezone, relative time, or the default format. Existing --relative-date option is a synonym of --date=relative; we could probably deprecate it in the long run. Signed-off-by: Junio C Hamano --- commit.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 10466c4ae..f1ba972d9 100644 --- a/commit.c +++ b/commit.c @@ -526,7 +526,7 @@ static int add_rfc2047(char *buf, const char *line, int len, } static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, - const char *line, int relative_date, + const char *line, enum date_mode dmode, const char *encoding) { char *date; @@ -569,7 +569,7 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, switch (fmt) { case CMIT_FMT_MEDIUM: ret += sprintf(buf + ret, "Date: %s\n", - show_date(time, tz, relative_date)); + show_date(time, tz, dmode)); break; case CMIT_FMT_EMAIL: ret += sprintf(buf + ret, "Date: %s\n", @@ -577,7 +577,7 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, break; case CMIT_FMT_FULLER: ret += sprintf(buf + ret, "%sDate: %s\n", what, - show_date(time, tz, relative_date)); + show_date(time, tz, dmode)); break; default: /* notin' */ @@ -919,7 +919,7 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, char *buf, unsigned long space, int abbrev, const char *subject, const char *after_subject, - int relative_date) + enum date_mode dmode) { int hdr = 1, body = 0, seen_title = 0; unsigned long offset = 0; @@ -1023,14 +1023,14 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, offset += add_user_info("Author", fmt, buf + offset, line + 7, - relative_date, + dmode, encoding); if (!memcmp(line, "committer ", 10) && (fmt == CMIT_FMT_FULL || fmt == CMIT_FMT_FULLER)) offset += add_user_info("Commit", fmt, buf + offset, line + 10, - relative_date, + dmode, encoding); continue; } -- cgit v1.2.1 From 5094102e13640d7559a02fd7c77b44dc9254cbbb Mon Sep 17 00:00:00 2001 From: Daniel Barkalow Date: Wed, 2 May 2007 22:49:41 -0400 Subject: Make xstrndup common This also improves the implementation to match how strndup is specified (by GNU): if the length given is longer than the string, only the string's length is allocated and copied, but the string need not be null-terminated if it is at least as long as the given length. Signed-off-by: Daniel Barkalow Signed-off-by: Junio C Hamano --- commit.c | 8 -------- 1 file changed, 8 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 754d1b8a0..eb911f44d 100644 --- a/commit.c +++ b/commit.c @@ -720,14 +720,6 @@ static char *logmsg_reencode(const struct commit *commit, 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; -- cgit v1.2.1 From e102d4353d7cfd69a597cd976eabdcb74641be69 Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Fri, 4 May 2007 23:51:32 +0200 Subject: Small correction in reading of commit headers Check if a line of the header has enough characters to possibly contain the requested prefix. Signed-off-by: Alex Riesen Signed-off-by: Junio C Hamano --- commit.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index eb911f44d..7d78e786e 100644 --- a/commit.c +++ b/commit.c @@ -640,7 +640,9 @@ static char *get_header(const struct commit *commit, const char *key) next = NULL; } else next = eol + 1; - if (!strncmp(line, key, key_len) && line[key_len] == ' ') { + if (eol - line > key_len && + !strncmp(line, key, key_len) && + line[key_len] == ' ') { int len = eol - line - key_len; char *ret = xmalloc(len); memcpy(ret, line + key_len + 1, len - 1); -- cgit v1.2.1 From 0ab564be6e59c66c7aa4fc44997f3fc62ebcd0d9 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Tue, 15 May 2007 11:35:13 -0400 Subject: format-patch: add MIME-Version header when we add content-type. When we add Content-Type: header, we should also add MIME-Version: header as well. Signed-off-by: Junio C Hamano --- commit.c | 1 + 1 file changed, 1 insertion(+) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 7d78e786e..43b767ce5 100644 --- a/commit.c +++ b/commit.c @@ -1057,6 +1057,7 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, int sz; char header[512]; const char *header_fmt = + "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=%s\n" "Content-Transfer-Encoding: 8bit\n"; sz = snprintf(header, sizeof(header), header_fmt, -- cgit v1.2.1 From 996e2d6ea2626f55a59e70ac7305a02ce0171814 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20H=C3=B8gsberg?= Date: Fri, 1 Jun 2007 17:08:12 -0400 Subject: Use =20 when rfc2047 encoding spaces. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Encode ' ' using '=20' even though rfc2047 allows using '_' for readability. Unfortunately, many programs do not understand this and just leave the underscore in place. Using '=20' seems to work better. [jc: with adjustment to t3901] Signed-off-by: Kristian Høgsberg Signed-off-by: Junio C Hamano --- commit.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index bee066fa3..5632e3268 100644 --- a/commit.c +++ b/commit.c @@ -511,12 +511,16 @@ static int add_rfc2047(char *buf, const char *line, int len, bp += i; for (i = 0; i < len; i++) { unsigned ch = line[i] & 0xFF; - if (is_rfc2047_special(ch)) { + /* + * We encode ' ' using '=20' even though rfc2047 + * allows using '_' for readability. Unfortunately, + * many programs do not understand this and just + * leave the underscore in place. + */ + if (is_rfc2047_special(ch) || ch == ' ') { sprintf(bp, "=%02X", ch); bp += 3; } - else if (ch == ' ') - *bp++ = '_'; else *bp++ = ch; } -- cgit v1.2.1 From a6080a0a44d5ead84db3dabbbc80e82df838533d Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 7 Jun 2007 00:04:01 -0700 Subject: War on whitespace This uses "git-apply --whitespace=strip" to fix whitespace errors that have crept in to our source files over time. There are a few files that need to have trailing whitespaces (most notably, test vectors). The results still passes the test, and build result in Documentation/ area is unchanged. Signed-off-by: Junio C Hamano --- commit.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 5632e3268..57b69ca7f 100644 --- a/commit.c +++ b/commit.c @@ -148,7 +148,7 @@ static int commit_graft_pos(const unsigned char *sha1) int register_commit_graft(struct commit_graft *graft, int ignore_dups) { int pos = commit_graft_pos(graft->sha1); - + if (0 <= pos) { if (ignore_dups) free(graft); @@ -406,7 +406,7 @@ struct commit_list * insert_by_date(struct commit *item, struct commit_list **li return commit_list_insert(item, pp); } - + void sort_by_date(struct commit_list **list) { struct commit_list *ret = NULL; @@ -1160,7 +1160,7 @@ void sort_in_topological_order_fn(struct commit_list ** list, int lifo, next = next->next; count++; } - + if (!count) return; /* allocate an array to help sort the list */ @@ -1188,11 +1188,11 @@ void sort_in_topological_order_fn(struct commit_list ** list, int lifo, } next=next->next; } - /* + /* * find the tips * - * tips are nodes not reachable from any other node in the list - * + * tips are nodes not reachable from any other node in the list + * * the tips serve as a starting set for the work queue. */ next=*list; @@ -1220,7 +1220,7 @@ void sort_in_topological_order_fn(struct commit_list ** list, int lifo, if (pn) { /* - * parents are only enqueued for emission + * parents are only enqueued for emission * when all their children have been emitted thereby * guaranteeing topological order. */ -- cgit v1.2.1 From 16befb8b7fbfcc9b2d38931f4081669558300adf Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 8 Jun 2007 02:54:57 -0700 Subject: Even more missing static Signed-off-by: Junio C Hamano --- commit.c | 9 --------- 1 file changed, 9 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 57b69ca7f..4ca4d44ba 100644 --- a/commit.c +++ b/commit.c @@ -1116,15 +1116,6 @@ struct commit *pop_commit(struct commit_list **stack) return item; } -int count_parents(struct commit * commit) -{ - int count; - struct commit_list * parents = commit->parents; - for (count = 0; parents; parents = parents->next,count++) - ; - return count; -} - void topo_sort_default_setter(struct commit *c, void *data) { c->util = data; -- cgit v1.2.1 From 80583c0ef61cc966c7eee79cf3623a83197e19b8 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 11 Jun 2007 00:34:54 -0700 Subject: Lift 16kB limit of log message output Traditionally we had 16kB limit when formatting log messages for output, because it was easier to arrange for the caller to have a reasonably big buffer and pass it down without ever worrying about reallocating. This changes the calling convention of pretty_print_commit() to lift this limit. Instead of the buffer and remaining length, it now takes a pointer to the pointer that points at the allocated buffer, and another pointer to the location that stores the allocated length, and reallocates the buffer as necessary. To support the user format, the error return of interpolate() needed to be changed. It used to return a bool telling "Ok the result fits", or "Sorry, I had to truncate it". Now it returns 0 on success, and returns the size of the buffer it wants in order to fit the whole result. Signed-off-by: Junio C Hamano --- commit.c | 55 +++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 43 insertions(+), 12 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 4ca4d44ba..d43a68ecb 100644 --- a/commit.c +++ b/commit.c @@ -776,7 +776,7 @@ static void fill_person(struct interp *table, const char *msg, int len) } static long format_commit_message(const struct commit *commit, - const char *msg, char *buf, unsigned long space) + const char *msg, char **buf_p, unsigned long *space_p) { struct interp table[] = { { "%H" }, /* commit hash */ @@ -905,16 +905,27 @@ static long format_commit_message(const struct commit *commit, if (!table[i].value) interp_set_entry(table, i, ""); - interpolate(buf, space, user_format, table, ARRAY_SIZE(table)); + do { + char *buf = *buf_p; + unsigned long space = *space_p; + + space = interpolate(buf, space, user_format, + table, ARRAY_SIZE(table)); + if (!space) + break; + buf = xrealloc(buf, space); + *buf_p = buf; + *space_p = space; + } while (1); interp_clear_table(table, ARRAY_SIZE(table)); - return strlen(buf); + return strlen(*buf_p); } unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, unsigned long len, - char *buf, unsigned long space, + char **buf_p, unsigned long *space_p, int abbrev, const char *subject, const char *after_subject, enum date_mode dmode) @@ -927,9 +938,11 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, int plain_non_ascii = 0; char *reencoded; const char *encoding; + char *buf; + unsigned long space, slop; if (fmt == CMIT_FMT_USERFORMAT) - return format_commit_message(commit, msg, buf, space); + return format_commit_message(commit, msg, buf_p, space_p); encoding = (git_log_output_encoding ? git_log_output_encoding @@ -969,6 +982,26 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, } } + space = *space_p; + buf = *buf_p; + + /* + * We do not want to repeatedly realloc below, so + * preallocate with enough slop to hold MIME headers, + * "Subject: " prefix, etc. + */ + slop = 1000; + if (subject) + slop += strlen(subject); + if (after_subject) + slop += strlen(after_subject); + if (space < strlen(msg) + slop) { + space = strlen(msg) + slop; + buf = xrealloc(buf, space); + *space_p = space; + *buf_p = buf; + } + for (;;) { const char *line = msg; int linelen = get_one_line(msg, len); @@ -976,14 +1009,12 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, if (!linelen) break; - /* - * We want some slop for indentation and a possible - * final "...". Thus the "+ 20". - */ + /* 20 would cover indent and leave us some slop */ if (offset + linelen + 20 > space) { - memcpy(buf + offset, " ...\n", 8); - offset += 8; - break; + space = offset + linelen + 20; + buf = xrealloc(buf, space); + *buf_p = buf; + *space_p = space; } msg += linelen; -- cgit v1.2.1 From 4234a76167b12a7669dae0e6386c62e712b9dcf5 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 11 Jun 2007 22:10:55 -0700 Subject: Extend --pretty=oneline to cover the first paragraph, so that an ugly commit message like this can be handled sanely. Currently, --pretty=oneline and --pretty=email (hence format-patch) take and use only the first line of the commit log message. This changes them to: - Take the first paragraph, where the definition of the first paragraph is "skip all blank lines from the beginning, and then grab everything up to the next empty line". - Replace all line breaks with a whitespace. This change would not affect a well-behaved commit message that adheres to the convention of "single line summary, a blank line, and then body of message", as its first paragraph always consists of a single line. Commit messages from different culture, such as the ones imported from CVS/SVN, can however get chomped with the existing behaviour at the first linebreak in the middle of sentence right now, which would become much easier to see with this change. The Subject: and --pretty=oneline output would become very long and unsightly for non-conforming commits, but their messages are already ugly anyway, and thischange at least avoids the loss of information. The Subject: line from a multi-line paragraph is folded using RFC2822 line folding rules at the places where line breaks were in the original. Signed-off-by: Junio C Hamano --- commit.c | 388 +++++++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 254 insertions(+), 134 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index d43a68ecb..40c87a747 100644 --- a/commit.c +++ b/commit.c @@ -529,6 +529,14 @@ static int add_rfc2047(char *buf, const char *line, int len, return bp - buf; } +static unsigned long bound_rfc2047(unsigned long len, const char *encoding) +{ + /* upper bound of q encoded string of length 'len' */ + unsigned long elen = strlen(encoding); + + return len * 3 + elen + 100; +} + static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const char *line, enum date_mode dmode, const char *encoding) @@ -922,6 +930,216 @@ static long format_commit_message(const struct commit *commit, return strlen(*buf_p); } +static void pp_header(enum cmit_fmt fmt, + int abbrev, + enum date_mode dmode, + const char *encoding, + const struct commit *commit, + const char **msg_p, + unsigned long *len_p, + unsigned long *ofs_p, + char **buf_p, + unsigned long *space_p) +{ + int parents_shown = 0; + + for (;;) { + const char *line = *msg_p; + char *dst; + int linelen = get_one_line(*msg_p, *len_p); + unsigned long len; + + if (!linelen) + return; + *msg_p += linelen; + *len_p -= linelen; + + if (linelen == 1) + /* End of header */ + return; + + ALLOC_GROW(*buf_p, linelen + *ofs_p + 20, *space_p); + dst = *buf_p + *ofs_p; + + if (fmt == CMIT_FMT_RAW) { + memcpy(dst, line, linelen); + *ofs_p += linelen; + continue; + } + + if (!memcmp(line, "parent ", 7)) { + if (linelen != 48) + die("bad parent line in commit"); + continue; + } + + if (!parents_shown) { + struct commit_list *parent; + int num; + for (parent = commit->parents, num = 0; + parent; + parent = parent->next, num++) + ; + /* with enough slop */ + num = *ofs_p + num * 50 + 20; + ALLOC_GROW(*buf_p, num, *space_p); + dst = *buf_p + *ofs_p; + *ofs_p += add_merge_info(fmt, dst, commit, abbrev); + parents_shown = 1; + } + + /* + * MEDIUM == DEFAULT shows only author with dates. + * FULL shows both authors but not dates. + * FULLER shows both authors and dates. + */ + if (!memcmp(line, "author ", 7)) { + len = linelen; + if (fmt == CMIT_FMT_EMAIL) + len = bound_rfc2047(linelen, encoding); + ALLOC_GROW(*buf_p, *ofs_p + len, *space_p); + dst = *buf_p + *ofs_p; + *ofs_p += add_user_info("Author", fmt, dst, + line + 7, dmode, encoding); + } + + if (!memcmp(line, "committer ", 10) && + (fmt == CMIT_FMT_FULL || fmt == CMIT_FMT_FULLER)) { + len = linelen; + if (fmt == CMIT_FMT_EMAIL) + len = bound_rfc2047(linelen, encoding); + ALLOC_GROW(*buf_p, *ofs_p + len, *space_p); + dst = *buf_p + *ofs_p; + *ofs_p += add_user_info("Commit", fmt, dst, + line + 10, dmode, encoding); + } + } +} + +static void pp_title_line(enum cmit_fmt fmt, + const char **msg_p, + unsigned long *len_p, + unsigned long *ofs_p, + char **buf_p, + unsigned long *space_p, + int indent, + const char *subject, + const char *after_subject, + const char *encoding, + int plain_non_ascii) +{ + char *title; + unsigned long title_alloc, title_len; + unsigned long len; + + title_len = 0; + title_alloc = 80; + title = xmalloc(title_alloc); + for (;;) { + const char *line = *msg_p; + int linelen = get_one_line(line, *len_p); + *msg_p += linelen; + *len_p -= linelen; + + if (!linelen || is_empty_line(line, &linelen)) + break; + + if (title_alloc <= title_len + linelen + 2) { + title_alloc = title_len + linelen + 80; + title = xrealloc(title, title_alloc); + } + len = 0; + if (title_len) { + if (fmt == CMIT_FMT_EMAIL) { + len++; + title[title_len++] = '\n'; + } + len++; + title[title_len++] = ' '; + } + memcpy(title + title_len, line, linelen); + title_len += linelen; + } + + /* Enough slop for the MIME header and rfc2047 */ + len = bound_rfc2047(title_len, encoding)+ 1000; + if (subject) + len += strlen(subject); + if (after_subject) + len += strlen(after_subject); + if (encoding) + len += strlen(encoding); + ALLOC_GROW(*buf_p, title_len + *ofs_p + len, *space_p); + + if (subject) { + len = strlen(subject); + memcpy(*buf_p + *ofs_p, subject, len); + *ofs_p += len; + *ofs_p += add_rfc2047(*buf_p + *ofs_p, + title, title_len, encoding); + } else { + memcpy(*buf_p + *ofs_p, title, title_len); + *ofs_p += title_len; + } + (*buf_p)[(*ofs_p)++] = '\n'; + if (plain_non_ascii) { + const char *header_fmt = + "MIME-Version: 1.0\n" + "Content-Type: text/plain; charset=%s\n" + "Content-Transfer-Encoding: 8bit\n"; + *ofs_p += snprintf(*buf_p + *ofs_p, + *space_p - *ofs_p, + header_fmt, encoding); + } + if (after_subject) { + len = strlen(after_subject); + memcpy(*buf_p + *ofs_p, after_subject, len); + *ofs_p += len; + } + free(title); + if (fmt == CMIT_FMT_EMAIL) { + ALLOC_GROW(*buf_p, *ofs_p + 20, *space_p); + (*buf_p)[(*ofs_p)++] = '\n'; + } +} + +static void pp_remainder(enum cmit_fmt fmt, + const char **msg_p, + unsigned long *len_p, + unsigned long *ofs_p, + char **buf_p, + unsigned long *space_p, + int indent) +{ + int first = 1; + for (;;) { + const char *line = *msg_p; + int linelen = get_one_line(line, *len_p); + *msg_p += linelen; + *len_p -= linelen; + + if (!linelen) + break; + + if (is_empty_line(line, &linelen)) { + if (first) + continue; + if (fmt == CMIT_FMT_SHORT) + break; + } + first = 0; + + ALLOC_GROW(*buf_p, *ofs_p + linelen + indent + 20, *space_p); + if (indent) { + memset(*buf_p + *ofs_p, ' ', indent); + *ofs_p += indent; + } + memcpy(*buf_p + *ofs_p, line, linelen); + *ofs_p += linelen; + (*buf_p)[(*ofs_p)++] = '\n'; + } +} + unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, unsigned long len, @@ -930,16 +1148,14 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const char *after_subject, enum date_mode dmode) { - int hdr = 1, body = 0, seen_title = 0; unsigned long offset = 0; + unsigned long beginning_of_body; int indent = 4; - int parents_shown = 0; const char *msg = commit->buffer; int plain_non_ascii = 0; char *reencoded; const char *encoding; char *buf; - unsigned long space, slop; if (fmt == CMIT_FMT_USERFORMAT) return format_commit_message(commit, msg, buf_p, space_p); @@ -950,8 +1166,10 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, if (!encoding) encoding = "utf-8"; reencoded = logmsg_reencode(commit, encoding); - if (reencoded) + if (reencoded) { msg = reencoded; + len = strlen(reencoded); + } if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL) indent = 0; @@ -982,155 +1200,57 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, } } - space = *space_p; - buf = *buf_p; - - /* - * We do not want to repeatedly realloc below, so - * preallocate with enough slop to hold MIME headers, - * "Subject: " prefix, etc. - */ - slop = 1000; - if (subject) - slop += strlen(subject); - if (after_subject) - slop += strlen(after_subject); - if (space < strlen(msg) + slop) { - space = strlen(msg) + slop; - buf = xrealloc(buf, space); - *space_p = space; - *buf_p = buf; + pp_header(fmt, abbrev, dmode, encoding, + commit, &msg, &len, + &offset, buf_p, space_p); + if (fmt != CMIT_FMT_ONELINE && !subject) { + ALLOC_GROW(*buf_p, offset + 20, *space_p); + (*buf_p)[offset++] = '\n'; } + /* Skip excess blank lines at the beginning of body, if any... */ for (;;) { - const char *line = msg; int linelen = get_one_line(msg, len); - + int ll = linelen; if (!linelen) break; - - /* 20 would cover indent and leave us some slop */ - if (offset + linelen + 20 > space) { - space = offset + linelen + 20; - buf = xrealloc(buf, space); - *buf_p = buf; - *space_p = space; - } - + if (!is_empty_line(msg, &ll)) + break; msg += linelen; len -= linelen; - if (hdr) { - if (linelen == 1) { - hdr = 0; - if ((fmt != CMIT_FMT_ONELINE) && !subject) - buf[offset++] = '\n'; - continue; - } - if (fmt == CMIT_FMT_RAW) { - memcpy(buf + offset, line, linelen); - offset += linelen; - continue; - } - if (!memcmp(line, "parent ", 7)) { - if (linelen != 48) - die("bad parent line in commit"); - continue; - } - - if (!parents_shown) { - offset += add_merge_info(fmt, buf + offset, - commit, abbrev); - parents_shown = 1; - continue; - } - /* - * MEDIUM == DEFAULT shows only author with dates. - * FULL shows both authors but not dates. - * FULLER shows both authors and dates. - */ - if (!memcmp(line, "author ", 7)) - offset += add_user_info("Author", fmt, - buf + offset, - line + 7, - dmode, - encoding); - if (!memcmp(line, "committer ", 10) && - (fmt == CMIT_FMT_FULL || fmt == CMIT_FMT_FULLER)) - offset += add_user_info("Commit", fmt, - buf + offset, - line + 10, - dmode, - encoding); - continue; - } + } - if (!subject) - body = 1; + /* These formats treat the title line specially. */ + if (fmt == CMIT_FMT_ONELINE + || fmt == CMIT_FMT_EMAIL) + pp_title_line(fmt, &msg, &len, &offset, + buf_p, space_p, indent, + subject, after_subject, encoding, + plain_non_ascii); - if (is_empty_line(line, &linelen)) { - if (!seen_title) - continue; - if (!body) - continue; - if (subject) - continue; - if (fmt == CMIT_FMT_SHORT) - break; - } + beginning_of_body = offset; + if (fmt != CMIT_FMT_ONELINE) + pp_remainder(fmt, &msg, &len, &offset, + buf_p, space_p, indent); - seen_title = 1; - if (subject) { - int slen = strlen(subject); - memcpy(buf + offset, subject, slen); - offset += slen; - offset += add_rfc2047(buf + offset, line, linelen, - encoding); - } - else { - memset(buf + offset, ' ', indent); - memcpy(buf + offset + indent, line, linelen); - offset += linelen + indent; - } - buf[offset++] = '\n'; - if (fmt == CMIT_FMT_ONELINE) - break; - if (subject && plain_non_ascii) { - int sz; - char header[512]; - const char *header_fmt = - "MIME-Version: 1.0\n" - "Content-Type: text/plain; charset=%s\n" - "Content-Transfer-Encoding: 8bit\n"; - sz = snprintf(header, sizeof(header), header_fmt, - encoding); - if (sizeof(header) < sz) - die("Encoding name %s too long", encoding); - memcpy(buf + offset, header, sz); - offset += sz; - } - if (after_subject) { - int slen = strlen(after_subject); - if (slen > space - offset - 1) - slen = space - offset - 1; - memcpy(buf + offset, after_subject, slen); - offset += slen; - after_subject = NULL; - } - subject = NULL; - } - while (offset && isspace(buf[offset-1])) + while (offset && isspace((*buf_p)[offset-1])) offset--; + + ALLOC_GROW(*buf_p, offset + 20, *space_p); + buf = *buf_p; + /* Make sure there is an EOLN for the non-oneline case */ if (fmt != CMIT_FMT_ONELINE) buf[offset++] = '\n'; + /* - * make sure there is another EOLN to separate the headers from whatever - * body the caller appends if we haven't already written a body + * The caller may append additional body text in e-mail + * format. Make sure we did not strip the blank line + * between the header and the body. */ - if (fmt == CMIT_FMT_EMAIL && !body) + if (fmt == CMIT_FMT_EMAIL && offset <= beginning_of_body) buf[offset++] = '\n'; buf[offset] = '\0'; - free(reencoded); return offset; } -- cgit v1.2.1 From 4175e9e3a8734be1e96e385b0fa2428b86ed5809 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 13 Jun 2007 01:42:05 -0700 Subject: More static There still are quite a few symbols that ought to be static. Signed-off-by: Junio C Hamano --- commit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 4ca4d44ba..54abdd798 100644 --- a/commit.c +++ b/commit.c @@ -27,7 +27,7 @@ struct sort_node const char *commit_type = "commit"; -struct cmt_fmt_map { +static struct cmt_fmt_map { const char *n; size_t cmp_len; enum cmit_fmt v; -- cgit v1.2.1 From 4cd008a925f3c7c0064d86a4cb4b06278e79e9e7 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Fri, 15 Jun 2007 13:19:07 +0100 Subject: pp_header(): work around possible memory corruption add_user_info() possibly adds way more than just the commit header line. In fact, it sometimes needs so much more space that there is a buffer overrun, leading to an ugly crash. For example, the date is printed in its own line, and usually takes up more space than the equivalent Unix epoch. So, for good measure, add 80 characters (a full line) to the allocated space, in addition to the header line length. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- commit.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 40c87a747..dbb28b593 100644 --- a/commit.c +++ b/commit.c @@ -997,7 +997,7 @@ static void pp_header(enum cmit_fmt fmt, len = linelen; if (fmt == CMIT_FMT_EMAIL) len = bound_rfc2047(linelen, encoding); - ALLOC_GROW(*buf_p, *ofs_p + len, *space_p); + ALLOC_GROW(*buf_p, *ofs_p + len + 80, *space_p); dst = *buf_p + *ofs_p; *ofs_p += add_user_info("Author", fmt, dst, line + 7, dmode, encoding); @@ -1008,7 +1008,7 @@ static void pp_header(enum cmit_fmt fmt, len = linelen; if (fmt == CMIT_FMT_EMAIL) len = bound_rfc2047(linelen, encoding); - ALLOC_GROW(*buf_p, *ofs_p + len, *space_p); + ALLOC_GROW(*buf_p, *ofs_p + len + 80, *space_p); dst = *buf_p + *ofs_p; *ofs_p += add_user_info("Commit", fmt, dst, line + 10, dmode, encoding); -- cgit v1.2.1 From ee8f838e0346751b21250f8d107049829b855a98 Mon Sep 17 00:00:00 2001 From: Robin Rosenberg Date: Sat, 14 Jul 2007 01:00:42 +0200 Subject: Support output ISO 8601 format dates Support output of full ISO 8601 style dates in e.g. git log and other places that use interpolation for formatting. Signed-off-by: Robin Rosenberg Signed-off-by: Junio C Hamano --- commit.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 03436b1b0..d11941c7f 100644 --- a/commit.c +++ b/commit.c @@ -781,6 +781,7 @@ static void fill_person(struct interp *table, const char *msg, int len) 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)); + interp_set_entry(table, 6, show_date(date, tz, DATE_ISO8601)); } static long format_commit_message(const struct commit *commit, @@ -799,12 +800,14 @@ static long format_commit_message(const struct commit *commit, { "%aD" }, /* author date, RFC2822 style */ { "%ar" }, /* author date, relative */ { "%at" }, /* author date, UNIX timestamp */ + { "%ai" }, /* author date, ISO 8601 */ { "%cn" }, /* committer name */ { "%ce" }, /* committer email */ { "%cd" }, /* committer date */ { "%cD" }, /* committer date, RFC2822 style */ { "%cr" }, /* committer date, relative */ { "%ct" }, /* committer date, UNIX timestamp */ + { "%ci" }, /* committer date, ISO 8601 */ { "%e" }, /* encoding */ { "%s" }, /* subject */ { "%b" }, /* body */ @@ -821,10 +824,11 @@ static long format_commit_message(const struct commit *commit, IPARENTS, IPARENTS_ABBREV, IAUTHOR_NAME, IAUTHOR_EMAIL, IAUTHOR_DATE, IAUTHOR_DATE_RFC2822, IAUTHOR_DATE_RELATIVE, - IAUTHOR_TIMESTAMP, + IAUTHOR_TIMESTAMP, IAUTHOR_ISO8601, ICOMMITTER_NAME, ICOMMITTER_EMAIL, ICOMMITTER_DATE, ICOMMITTER_DATE_RFC2822, ICOMMITTER_DATE_RELATIVE, ICOMMITTER_TIMESTAMP, + ICOMMITTER_ISO8601, IENCODING, ISUBJECT, IBODY, -- cgit v1.2.1 From 73013afd14c14ba178ab2aaa458168ec1d5506de Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 13 Jul 2007 23:14:52 -0700 Subject: Make show_rfc2822_date() just another date output format. These days, show_date() takes a date_mode parameter to specify the output format, and a separate specialized function for dates in E-mails does not make much sense anymore. This retires show_rfc2822_date() function and make it just another date output format. Signed-off-by: Junio C Hamano --- commit.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index d11941c7f..4c5dfa9af 100644 --- a/commit.c +++ b/commit.c @@ -585,7 +585,7 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, break; case CMIT_FMT_EMAIL: ret += sprintf(buf + ret, "Date: %s\n", - show_rfc2822_date(time, tz)); + show_date(time, tz, DATE_RFC2822)); break; case CMIT_FMT_FULLER: ret += sprintf(buf + ret, "%sDate: %s\n", what, @@ -778,9 +778,9 @@ static void fill_person(struct interp *table, const char *msg, int len) 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)); + interp_set_entry(table, 2, show_date(date, tz, DATE_NORMAL)); + interp_set_entry(table, 3, show_date(date, tz, DATE_RFC2822)); + interp_set_entry(table, 4, show_date(date, tz, DATE_RELATIVE)); interp_set_entry(table, 6, show_date(date, tz, DATE_ISO8601)); } -- cgit v1.2.1 From c4640fe8d9e25fd3e206a39233c71a6dbb68917e Mon Sep 17 00:00:00 2001 From: Marco Costalba Date: Sun, 22 Jul 2007 10:23:05 +0200 Subject: Avoid to duplicate commit message when is not encoded When a commit message doesn't have encoding information and encoding output is utf-8 (default) then an useless xstrdup() of commit message is done. If we assume most of users live in an utf-8 world, this useless copy is the common case. Performance issue found with KCachegrind. Signed-off-by: Marco Costalba Signed-off-by: Junio C Hamano --- commit.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 4c5dfa9af..dc5a0643f 100644 --- a/commit.c +++ b/commit.c @@ -721,7 +721,10 @@ static char *logmsg_reencode(const struct commit *commit, encoding = get_header(commit, "encoding"); use_encoding = encoding ? encoding : utf8; if (!strcmp(use_encoding, output_encoding)) - out = xstrdup(commit->buffer); + if (encoding) /* we'll strip encoding header later */ + out = xstrdup(commit->buffer); + else + return NULL; /* nothing to do */ else out = reencode_string(commit->buffer, output_encoding, use_encoding); -- cgit v1.2.1 From 7b95089c0f59a25bb1c506b6962eb64412c585eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=1B=2Cbi=1B=28B=20Scharfe?= Date: Mon, 3 Sep 2007 20:06:36 +0200 Subject: Export format_commit_message() Drop the parameter "msg" of format_commit_message() (as it can be inferred from the parameter "commit"), add a parameter "template" in order to avoid accessing the static variable user_format directly and export the result. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- commit.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index dc5a0643f..99f65cee0 100644 --- a/commit.c +++ b/commit.c @@ -787,8 +787,8 @@ static void fill_person(struct interp *table, const char *msg, int len) interp_set_entry(table, 6, show_date(date, tz, DATE_ISO8601)); } -static long format_commit_message(const struct commit *commit, - const char *msg, char **buf_p, unsigned long *space_p) +long format_commit_message(const struct commit *commit, const void *format, + char **buf_p, unsigned long *space_p) { struct interp table[] = { { "%H" }, /* commit hash */ @@ -843,6 +843,7 @@ static long format_commit_message(const struct commit *commit, char parents[1024]; int i; enum { HEADER, SUBJECT, BODY } state; + const char *msg = commit->buffer; if (ILEFT_RIGHT + 1 != ARRAY_SIZE(table)) die("invalid interp table!"); @@ -924,7 +925,7 @@ static long format_commit_message(const struct commit *commit, char *buf = *buf_p; unsigned long space = *space_p; - space = interpolate(buf, space, user_format, + space = interpolate(buf, space, format, table, ARRAY_SIZE(table)); if (!space) break; @@ -1165,7 +1166,7 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, char *buf; if (fmt == CMIT_FMT_USERFORMAT) - return format_commit_message(commit, msg, buf_p, space_p); + return format_commit_message(commit, user_format, buf_p, space_p); encoding = (git_log_output_encoding ? git_log_output_encoding -- cgit v1.2.1 From 4acfd1b799acf43642a28a22cc794266c25129ef Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Mon, 10 Sep 2007 12:35:05 +0200 Subject: Change semantics of interpolate to work like snprintf. Also fix many off-by-ones and a useless memset. Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano --- commit.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 99f65cee0..25781cc83 100644 --- a/commit.c +++ b/commit.c @@ -923,15 +923,14 @@ long format_commit_message(const struct commit *commit, const void *format, do { char *buf = *buf_p; - unsigned long space = *space_p; + unsigned long len; - space = interpolate(buf, space, format, + len = interpolate(buf, *space_p, format, table, ARRAY_SIZE(table)); - if (!space) + if (len < *space_p) break; - buf = xrealloc(buf, space); + ALLOC_GROW(buf, len + 1, *space_p); *buf_p = buf; - *space_p = space; } while (1); interp_clear_table(table, ARRAY_SIZE(table)); -- cgit v1.2.1 From 674d1727305211f7ade4ade70440220f74f55162 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Mon, 10 Sep 2007 12:35:06 +0200 Subject: Rework pretty_print_commit to use strbufs instead of custom buffers. Also remove the "len" parameter, as: (1) it was used as a max boundary, and every caller used ~0u (2) we check for final NUL no matter what, so it doesn't help for speed. As a result most of the pp_* function takes 3 arguments less, and we need a lot less local variables, this makes the code way more readable, and easier to extend if needed. This patch also fixes some spacing and cosmetic issues. This patch also fixes (as a side effect) a memory leak intoruced in builtin-archive.c at commit df4a394f (fmt was xmalloc'ed and not free'd) Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano --- commit.c | 333 ++++++++++++++++++++++++--------------------------------------- 1 file changed, 124 insertions(+), 209 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 25781cc83..6602e2c72 100644 --- a/commit.c +++ b/commit.c @@ -458,11 +458,11 @@ void clear_commit_marks(struct commit *commit, unsigned int mark) /* * Generic support for pretty-printing the header */ -static int get_one_line(const char *msg, unsigned long len) +static int get_one_line(const char *msg) { int ret = 0; - while (len--) { + for (;;) { char c = *msg++; if (!c) break; @@ -485,31 +485,24 @@ static int is_rfc2047_special(char ch) return (non_ascii(ch) || (ch == '=') || (ch == '?') || (ch == '_')); } -static int add_rfc2047(char *buf, const char *line, int len, +static void add_rfc2047(struct strbuf *sb, const char *line, int len, const char *encoding) { - char *bp = buf; - int i, needquote; - char q_encoding[128]; - const char *q_encoding_fmt = "=?%s?q?"; + int i, last; - for (i = needquote = 0; !needquote && i < len; i++) { + for (i = 0; i < len; i++) { int ch = line[i]; if (non_ascii(ch)) - needquote++; - if ((i + 1 < len) && - (ch == '=' && line[i+1] == '?')) - needquote++; + goto needquote; + if ((i + 1 < len) && (ch == '=' && line[i+1] == '?')) + goto needquote; } - if (!needquote) - return sprintf(buf, "%.*s", len, line); - - i = snprintf(q_encoding, sizeof(q_encoding), q_encoding_fmt, encoding); - if (sizeof(q_encoding) < i) - die("Insanely long encoding name %s", encoding); - memcpy(bp, q_encoding, i); - bp += i; - for (i = 0; i < len; i++) { + strbuf_add(sb, line, len); + return; + +needquote: + strbuf_addf(sb, "=?%s?q?", encoding); + for (i = last = 0; i < len; i++) { unsigned ch = line[i] & 0xFF; /* * We encode ' ' using '=20' even though rfc2047 @@ -518,15 +511,13 @@ static int add_rfc2047(char *buf, const char *line, int len, * leave the underscore in place. */ if (is_rfc2047_special(ch) || ch == ' ') { - sprintf(bp, "=%02X", ch); - bp += 3; + strbuf_add(sb, line + last, i - last); + strbuf_addf(sb, "=%02X", ch); + last = i + 1; } - else - *bp++ = ch; } - memcpy(bp, "?=", 2); - bp += 2; - return bp - buf; + strbuf_add(sb, line + last, len - last); + strbuf_addstr(sb, "?="); } static unsigned long bound_rfc2047(unsigned long len, const char *encoding) @@ -537,21 +528,21 @@ static unsigned long bound_rfc2047(unsigned long len, const char *encoding) return len * 3 + elen + 100; } -static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, +static void add_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb, const char *line, enum date_mode dmode, const char *encoding) { char *date; int namelen; unsigned long time; - int tz, ret; + int tz; const char *filler = " "; if (fmt == CMIT_FMT_ONELINE) - return 0; + return; date = strchr(line, '>'); if (!date) - return 0; + return; namelen = ++date - line; time = strtoul(date, &date, 10); tz = strtol(date, NULL, 10); @@ -560,42 +551,35 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, char *name_tail = strchr(line, '<'); int display_name_length; if (!name_tail) - return 0; + return; while (line < name_tail && isspace(name_tail[-1])) name_tail--; display_name_length = name_tail - line; filler = ""; - strcpy(buf, "From: "); - ret = strlen(buf); - ret += add_rfc2047(buf + ret, line, display_name_length, - encoding); - memcpy(buf + ret, name_tail, namelen - display_name_length); - ret += namelen - display_name_length; - buf[ret++] = '\n'; + strbuf_addstr(sb, "From: "); + add_rfc2047(sb, line, display_name_length, encoding); + strbuf_add(sb, name_tail, namelen - display_name_length); + strbuf_addch(sb, '\n'); } else { - ret = sprintf(buf, "%s: %.*s%.*s\n", what, + strbuf_addf(sb, "%s: %.*s%.*s\n", what, (fmt == CMIT_FMT_FULLER) ? 4 : 0, filler, namelen, line); } switch (fmt) { case CMIT_FMT_MEDIUM: - ret += sprintf(buf + ret, "Date: %s\n", - show_date(time, tz, dmode)); + strbuf_addf(sb, "Date: %s\n", show_date(time, tz, dmode)); break; case CMIT_FMT_EMAIL: - ret += sprintf(buf + ret, "Date: %s\n", - show_date(time, tz, DATE_RFC2822)); + strbuf_addf(sb, "Date: %s\n", show_date(time, tz, DATE_RFC2822)); break; case CMIT_FMT_FULLER: - ret += sprintf(buf + ret, "%sDate: %s\n", what, - show_date(time, tz, dmode)); + strbuf_addf(sb, "%sDate: %s\n", what, show_date(time, tz, dmode)); break; default: /* notin' */ break; } - return ret; } static int is_empty_line(const char *line, int *len_p) @@ -607,16 +591,16 @@ static int is_empty_line(const char *line, int *len_p) return !len; } -static int add_merge_info(enum cmit_fmt fmt, char *buf, const struct commit *commit, int abbrev) +static void add_merge_info(enum cmit_fmt fmt, struct strbuf *sb, + const struct commit *commit, int abbrev) { struct commit_list *parent = commit->parents; - int offset; if ((fmt == CMIT_FMT_ONELINE) || (fmt == CMIT_FMT_EMAIL) || !parent || !parent->next) - return 0; + return; - offset = sprintf(buf, "Merge:"); + strbuf_addstr(sb, "Merge:"); while (parent) { struct commit *p = parent->item; @@ -629,10 +613,9 @@ static int add_merge_info(enum cmit_fmt fmt, char *buf, const struct commit *com dots = (abbrev && strlen(hex) != 40) ? "..." : ""; parent = parent->next; - offset += sprintf(buf + offset, " %s%s", hex, dots); + strbuf_addf(sb, " %s%s", hex, dots); } - buf[offset++] = '\n'; - return offset; + strbuf_addch(sb, '\n'); } static char *get_header(const struct commit *commit, const char *key) @@ -787,8 +770,8 @@ static void fill_person(struct interp *table, const char *msg, int len) interp_set_entry(table, 6, show_date(date, tz, DATE_ISO8601)); } -long format_commit_message(const struct commit *commit, const void *format, - char **buf_p, unsigned long *space_p) +void format_commit_message(const struct commit *commit, + const void *format, struct strbuf *sb) { struct interp table[] = { { "%H" }, /* commit hash */ @@ -841,6 +824,7 @@ long format_commit_message(const struct commit *commit, const void *format, }; struct commit_list *p; char parents[1024]; + unsigned long len; int i; enum { HEADER, SUBJECT, BODY } state; const char *msg = commit->buffer; @@ -921,20 +905,15 @@ long format_commit_message(const struct commit *commit, const void *format, if (!table[i].value) interp_set_entry(table, i, ""); - do { - char *buf = *buf_p; - unsigned long len; - - len = interpolate(buf, *space_p, format, - table, ARRAY_SIZE(table)); - if (len < *space_p) - break; - ALLOC_GROW(buf, len + 1, *space_p); - *buf_p = buf; - } while (1); + len = interpolate(sb->buf + sb->len, strbuf_avail(sb), + format, table, ARRAY_SIZE(table)); + if (len > strbuf_avail(sb)) { + strbuf_grow(sb, len); + interpolate(sb->buf + sb->len, strbuf_avail(sb) + 1, + format, table, ARRAY_SIZE(table)); + } + strbuf_setlen(sb, sb->len + len); interp_clear_table(table, ARRAY_SIZE(table)); - - return strlen(*buf_p); } static void pp_header(enum cmit_fmt fmt, @@ -943,34 +922,24 @@ static void pp_header(enum cmit_fmt fmt, const char *encoding, const struct commit *commit, const char **msg_p, - unsigned long *len_p, - unsigned long *ofs_p, - char **buf_p, - unsigned long *space_p) + struct strbuf *sb) { int parents_shown = 0; for (;;) { const char *line = *msg_p; - char *dst; - int linelen = get_one_line(*msg_p, *len_p); - unsigned long len; + int linelen = get_one_line(*msg_p); if (!linelen) return; *msg_p += linelen; - *len_p -= linelen; if (linelen == 1) /* End of header */ return; - ALLOC_GROW(*buf_p, linelen + *ofs_p + 20, *space_p); - dst = *buf_p + *ofs_p; - if (fmt == CMIT_FMT_RAW) { - memcpy(dst, line, linelen); - *ofs_p += linelen; + strbuf_add(sb, line, linelen); continue; } @@ -988,10 +957,8 @@ static void pp_header(enum cmit_fmt fmt, parent = parent->next, num++) ; /* with enough slop */ - num = *ofs_p + num * 50 + 20; - ALLOC_GROW(*buf_p, num, *space_p); - dst = *buf_p + *ofs_p; - *ofs_p += add_merge_info(fmt, dst, commit, abbrev); + strbuf_grow(sb, num * 50 + 20); + add_merge_info(fmt, sb, commit, abbrev); parents_shown = 1; } @@ -1001,129 +968,99 @@ static void pp_header(enum cmit_fmt fmt, * FULLER shows both authors and dates. */ if (!memcmp(line, "author ", 7)) { - len = linelen; + unsigned long len = linelen; if (fmt == CMIT_FMT_EMAIL) len = bound_rfc2047(linelen, encoding); - ALLOC_GROW(*buf_p, *ofs_p + len + 80, *space_p); - dst = *buf_p + *ofs_p; - *ofs_p += add_user_info("Author", fmt, dst, - line + 7, dmode, encoding); + strbuf_grow(sb, len + 80); + add_user_info("Author", fmt, sb, line + 7, dmode, encoding); } if (!memcmp(line, "committer ", 10) && (fmt == CMIT_FMT_FULL || fmt == CMIT_FMT_FULLER)) { - len = linelen; + unsigned long len = linelen; if (fmt == CMIT_FMT_EMAIL) len = bound_rfc2047(linelen, encoding); - ALLOC_GROW(*buf_p, *ofs_p + len + 80, *space_p); - dst = *buf_p + *ofs_p; - *ofs_p += add_user_info("Commit", fmt, dst, - line + 10, dmode, encoding); + strbuf_grow(sb, len + 80); + add_user_info("Commit", fmt, sb, line + 10, dmode, encoding); } } } static void pp_title_line(enum cmit_fmt fmt, const char **msg_p, - unsigned long *len_p, - unsigned long *ofs_p, - char **buf_p, - unsigned long *space_p, - int indent, + struct strbuf *sb, const char *subject, const char *after_subject, const char *encoding, int plain_non_ascii) { - char *title; - unsigned long title_alloc, title_len; + struct strbuf title; unsigned long len; - title_len = 0; - title_alloc = 80; - title = xmalloc(title_alloc); + strbuf_init(&title, 80); + for (;;) { const char *line = *msg_p; - int linelen = get_one_line(line, *len_p); - *msg_p += linelen; - *len_p -= linelen; + int linelen = get_one_line(line); + *msg_p += linelen; if (!linelen || is_empty_line(line, &linelen)) break; - if (title_alloc <= title_len + linelen + 2) { - title_alloc = title_len + linelen + 80; - title = xrealloc(title, title_alloc); - } - len = 0; - if (title_len) { + strbuf_grow(&title, linelen + 2); + if (title.len) { if (fmt == CMIT_FMT_EMAIL) { - len++; - title[title_len++] = '\n'; + strbuf_addch(&title, '\n'); } - len++; - title[title_len++] = ' '; + strbuf_addch(&title, ' '); } - memcpy(title + title_len, line, linelen); - title_len += linelen; + strbuf_add(&title, line, linelen); } /* Enough slop for the MIME header and rfc2047 */ - len = bound_rfc2047(title_len, encoding)+ 1000; + len = bound_rfc2047(title.len, encoding) + 1000; if (subject) len += strlen(subject); if (after_subject) len += strlen(after_subject); if (encoding) len += strlen(encoding); - ALLOC_GROW(*buf_p, title_len + *ofs_p + len, *space_p); + strbuf_grow(sb, title.len + len); if (subject) { - len = strlen(subject); - memcpy(*buf_p + *ofs_p, subject, len); - *ofs_p += len; - *ofs_p += add_rfc2047(*buf_p + *ofs_p, - title, title_len, encoding); + strbuf_addstr(sb, subject); + add_rfc2047(sb, title.buf, title.len, encoding); } else { - memcpy(*buf_p + *ofs_p, title, title_len); - *ofs_p += title_len; + strbuf_addbuf(sb, &title); } - (*buf_p)[(*ofs_p)++] = '\n'; + strbuf_addch(sb, '\n'); + if (plain_non_ascii) { const char *header_fmt = "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=%s\n" "Content-Transfer-Encoding: 8bit\n"; - *ofs_p += snprintf(*buf_p + *ofs_p, - *space_p - *ofs_p, - header_fmt, encoding); + strbuf_addf(sb, header_fmt, encoding); } if (after_subject) { - len = strlen(after_subject); - memcpy(*buf_p + *ofs_p, after_subject, len); - *ofs_p += len; + strbuf_addstr(sb, after_subject); } - free(title); if (fmt == CMIT_FMT_EMAIL) { - ALLOC_GROW(*buf_p, *ofs_p + 20, *space_p); - (*buf_p)[(*ofs_p)++] = '\n'; + strbuf_addch(sb, '\n'); } + strbuf_release(&title); } static void pp_remainder(enum cmit_fmt fmt, const char **msg_p, - unsigned long *len_p, - unsigned long *ofs_p, - char **buf_p, - unsigned long *space_p, + struct strbuf *sb, int indent) { int first = 1; for (;;) { const char *line = *msg_p; - int linelen = get_one_line(line, *len_p); + int linelen = get_one_line(line); *msg_p += linelen; - *len_p -= linelen; if (!linelen) break; @@ -1136,36 +1073,32 @@ static void pp_remainder(enum cmit_fmt fmt, } first = 0; - ALLOC_GROW(*buf_p, *ofs_p + linelen + indent + 20, *space_p); + strbuf_grow(sb, linelen + indent + 20); if (indent) { - memset(*buf_p + *ofs_p, ' ', indent); - *ofs_p += indent; + memset(sb->buf + sb->len, ' ', indent); + strbuf_setlen(sb, sb->len + indent); } - memcpy(*buf_p + *ofs_p, line, linelen); - *ofs_p += linelen; - (*buf_p)[(*ofs_p)++] = '\n'; + strbuf_add(sb, line, linelen); + strbuf_addch(sb, '\n'); } } -unsigned long pretty_print_commit(enum cmit_fmt fmt, - const struct commit *commit, - unsigned long len, - char **buf_p, unsigned long *space_p, - int abbrev, const char *subject, - const char *after_subject, +void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, + struct strbuf *sb, int abbrev, + const char *subject, const char *after_subject, enum date_mode dmode) { - unsigned long offset = 0; unsigned long beginning_of_body; int indent = 4; const char *msg = commit->buffer; int plain_non_ascii = 0; char *reencoded; const char *encoding; - char *buf; - if (fmt == CMIT_FMT_USERFORMAT) - return format_commit_message(commit, user_format, buf_p, space_p); + if (fmt == CMIT_FMT_USERFORMAT) { + format_commit_message(commit, user_format, sb); + return; + } encoding = (git_log_output_encoding ? git_log_output_encoding @@ -1175,7 +1108,6 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, reencoded = logmsg_reencode(commit, encoding); if (reencoded) { msg = reencoded; - len = strlen(reencoded); } if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL) @@ -1190,14 +1122,13 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, if (fmt == CMIT_FMT_EMAIL && !after_subject) { int i, ch, in_body; - for (in_body = i = 0; (ch = msg[i]) && i < len; i++) { + for (in_body = i = 0; (ch = msg[i]); i++) { if (!in_body) { /* author could be non 7-bit ASCII but * the log may be so; skip over the * header part first. */ - if (ch == '\n' && - i + 1 < len && msg[i+1] == '\n') + if (ch == '\n' && msg[i+1] == '\n') in_body = 1; } else if (non_ascii(ch)) { @@ -1207,59 +1138,44 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, } } - pp_header(fmt, abbrev, dmode, encoding, - commit, &msg, &len, - &offset, buf_p, space_p); + pp_header(fmt, abbrev, dmode, encoding, commit, &msg, sb); if (fmt != CMIT_FMT_ONELINE && !subject) { - ALLOC_GROW(*buf_p, offset + 20, *space_p); - (*buf_p)[offset++] = '\n'; + strbuf_addch(sb, '\n'); } /* Skip excess blank lines at the beginning of body, if any... */ for (;;) { - int linelen = get_one_line(msg, len); + int linelen = get_one_line(msg); int ll = linelen; if (!linelen) break; if (!is_empty_line(msg, &ll)) break; msg += linelen; - len -= linelen; } /* These formats treat the title line specially. */ - if (fmt == CMIT_FMT_ONELINE - || fmt == CMIT_FMT_EMAIL) - pp_title_line(fmt, &msg, &len, &offset, - buf_p, space_p, indent, - subject, after_subject, encoding, - plain_non_ascii); - - beginning_of_body = offset; - if (fmt != CMIT_FMT_ONELINE) - pp_remainder(fmt, &msg, &len, &offset, - buf_p, space_p, indent); - - while (offset && isspace((*buf_p)[offset-1])) - offset--; + if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL) + pp_title_line(fmt, &msg, sb, subject, + after_subject, encoding, plain_non_ascii); - ALLOC_GROW(*buf_p, offset + 20, *space_p); - buf = *buf_p; + beginning_of_body = sb->len; + if (fmt != CMIT_FMT_ONELINE) + pp_remainder(fmt, &msg, sb, indent); + strbuf_rtrim(sb); /* Make sure there is an EOLN for the non-oneline case */ if (fmt != CMIT_FMT_ONELINE) - buf[offset++] = '\n'; + strbuf_addch(sb, '\n'); /* * The caller may append additional body text in e-mail * format. Make sure we did not strip the blank line * between the header and the body. */ - if (fmt == CMIT_FMT_EMAIL && offset <= beginning_of_body) - buf[offset++] = '\n'; - buf[offset] = '\0'; + if (fmt == CMIT_FMT_EMAIL && sb->len <= beginning_of_body) + strbuf_addch(sb, '\n'); free(reencoded); - return offset; } struct commit *pop_commit(struct commit_list **stack) @@ -1338,12 +1254,12 @@ void sort_in_topological_order_fn(struct commit_list ** list, int lifo, next=next->next; } /* - * find the tips - * - * tips are nodes not reachable from any other node in the list - * - * the tips serve as a starting set for the work queue. - */ + * find the tips + * + * tips are nodes not reachable from any other node in the list + * + * the tips serve as a starting set for the work queue. + */ next=*list; insert = &work; while (next) { @@ -1370,9 +1286,9 @@ void sort_in_topological_order_fn(struct commit_list ** list, int lifo, if (pn) { /* * parents are only enqueued for emission - * when all their children have been emitted thereby - * guaranteeing topological order. - */ + * when all their children have been emitted thereby + * guaranteeing topological order. + */ pn->indegree--; if (!pn->indegree) { if (!lifo) @@ -1384,9 +1300,9 @@ void sort_in_topological_order_fn(struct commit_list ** list, int lifo, parents=parents->next; } /* - * work_item is a commit all of whose children - * have already been emitted. we can emit it now. - */ + * work_item is a commit all of whose children + * have already been emitted. we can emit it now. + */ *pptr = work_node->list_item; pptr = &(*pptr)->next; *pptr = NULL; @@ -1482,8 +1398,7 @@ static struct commit_list *merge_bases(struct commit *one, struct commit *two) } struct commit_list *get_merge_bases(struct commit *one, - struct commit *two, - int cleanup) + struct commit *two, int cleanup) { struct commit_list *list; struct commit **rslt; -- cgit v1.2.1 From a08f23ab3eab67aac7163a284038f45fa99ef66f Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Sat, 15 Sep 2007 23:50:12 +0200 Subject: Refactor replace_encoding_header. * Be more clever in how we search for "encoding ...\n": parse for real instead of the sloppy strstr's. * use strbuf_splice to do the substring replacements. Signed-off-by: Pierre Habouzit Acked-by: Linus Torvalds Signed-off-by: Junio C Hamano --- commit.c | 59 +++++++++++++++++++++++------------------------------------ 1 file changed, 23 insertions(+), 36 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 6602e2c72..13af93363 100644 --- a/commit.c +++ b/commit.c @@ -648,47 +648,34 @@ static char *get_header(const struct commit *commit, const char *key) 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 new_len; - int need_len; - int buflen = strlen(buf) + 1; - - 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) + struct strbuf tmp; + size_t start, len; + char *cp = buf; + + /* guess if there is an encoding header before a \n\n */ + while (strncmp(cp, "encoding ", strlen("encoding "))) { + cp = strchr(cp, '\n'); + if (!cp || *++cp == '\n') + return buf; + } + start = cp - buf; + cp = strchr(cp, '\n'); + if (!cp) return buf; /* should not happen but be defensive */ - end_of_encoding_header++; - - encoding_header_len = end_of_encoding_header - encoding_header; - encoding_header_pos = encoding_header - buf; + len = cp + 1 - (buf + start); + strbuf_init(&tmp, 0); + strbuf_attach(&tmp, buf, strlen(buf), strlen(buf) + 1); if (is_encoding_utf8(encoding)) { /* we have re-coded to UTF-8; drop the header */ - memmove(encoding_header, end_of_encoding_header, - buflen - (encoding_header_pos + encoding_header_len)); - return buf; - } - new_len = strlen(encoding); - need_len = new_len + strlen("encoding \n"); - if (encoding_header_len < need_len) { - buf = xrealloc(buf, buflen + (need_len - encoding_header_len)); - encoding_header = buf + encoding_header_pos; - end_of_encoding_header = encoding_header + encoding_header_len; + strbuf_splice(&tmp, start, len, NULL, 0); + } else { + /* just replaces XXXX in 'encoding XXXX\n' */ + strbuf_splice(&tmp, start + strlen("encoding "), + len - strlen("encoding \n"), + encoding, strlen(encoding)); } - memmove(end_of_encoding_header + (need_len - encoding_header_len), - end_of_encoding_header, - buflen - (encoding_header_pos + encoding_header_len)); - memcpy(encoding_header + 9, encoding, strlen(encoding)); - encoding_header[9 + new_len] = '\n'; - return buf; + return tmp.buf; } static char *logmsg_reencode(const struct commit *commit, -- cgit v1.2.1 From 8b6087fb25068d6af927f112a93dc056930f3108 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Sun, 16 Sep 2007 10:19:01 +0200 Subject: Remove preemptive allocations. Careful profiling shows that we spend more time guessing what pattern allocation will have, whereas we can delay it only at the point where add_rfc2047 will be used and don't allocate huge memory area for the many cases where it's not. Signed-off-by: Pierre Habouzit Acked-by: Linus Torvalds Signed-off-by: Junio C Hamano --- commit.c | 35 +++++------------------------------ 1 file changed, 5 insertions(+), 30 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 13af93363..85889f966 100644 --- a/commit.c +++ b/commit.c @@ -501,6 +501,7 @@ static void add_rfc2047(struct strbuf *sb, const char *line, int len, return; needquote: + strbuf_grow(sb, len * 3 + strlen(encoding) + 100); strbuf_addf(sb, "=?%s?q?", encoding); for (i = last = 0; i < len; i++) { unsigned ch = line[i] & 0xFF; @@ -520,14 +521,6 @@ needquote: strbuf_addstr(sb, "?="); } -static unsigned long bound_rfc2047(unsigned long len, const char *encoding) -{ - /* upper bound of q encoded string of length 'len' */ - unsigned long elen = strlen(encoding); - - return len * 3 + elen + 100; -} - static void add_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb, const char *line, enum date_mode dmode, const char *encoding) @@ -560,8 +553,7 @@ static void add_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb add_rfc2047(sb, line, display_name_length, encoding); strbuf_add(sb, name_tail, namelen - display_name_length); strbuf_addch(sb, '\n'); - } - else { + } else { strbuf_addf(sb, "%s: %.*s%.*s\n", what, (fmt == CMIT_FMT_FULLER) ? 4 : 0, filler, namelen, line); @@ -955,19 +947,12 @@ static void pp_header(enum cmit_fmt fmt, * FULLER shows both authors and dates. */ if (!memcmp(line, "author ", 7)) { - unsigned long len = linelen; - if (fmt == CMIT_FMT_EMAIL) - len = bound_rfc2047(linelen, encoding); - strbuf_grow(sb, len + 80); + strbuf_grow(sb, linelen + 80); add_user_info("Author", fmt, sb, line + 7, dmode, encoding); } - if (!memcmp(line, "committer ", 10) && (fmt == CMIT_FMT_FULL || fmt == CMIT_FMT_FULLER)) { - unsigned long len = linelen; - if (fmt == CMIT_FMT_EMAIL) - len = bound_rfc2047(linelen, encoding); - strbuf_grow(sb, len + 80); + strbuf_grow(sb, linelen + 80); add_user_info("Commit", fmt, sb, line + 10, dmode, encoding); } } @@ -982,7 +967,6 @@ static void pp_title_line(enum cmit_fmt fmt, int plain_non_ascii) { struct strbuf title; - unsigned long len; strbuf_init(&title, 80); @@ -1004,16 +988,7 @@ static void pp_title_line(enum cmit_fmt fmt, strbuf_add(&title, line, linelen); } - /* Enough slop for the MIME header and rfc2047 */ - len = bound_rfc2047(title.len, encoding) + 1000; - if (subject) - len += strlen(subject); - if (after_subject) - len += strlen(after_subject); - if (encoding) - len += strlen(encoding); - - strbuf_grow(sb, title.len + len); + strbuf_grow(sb, title.len + 1024); if (subject) { strbuf_addstr(sb, subject); add_rfc2047(sb, title.buf, title.len, encoding); -- cgit v1.2.1 From 182af8343c307436bb5364309aa6d4d46fa5911d Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Sun, 16 Sep 2007 00:32:36 +0200 Subject: Use xmemdupz() in many places. Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano --- commit.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 85889f966..f86fa776c 100644 --- a/commit.c +++ b/commit.c @@ -628,11 +628,7 @@ static char *get_header(const struct commit *commit, const char *key) if (eol - line > key_len && !strncmp(line, key, key_len) && line[key_len] == ' ') { - int len = eol - line - key_len; - char *ret = xmalloc(len); - memcpy(ret, line + key_len + 1, len - 1); - ret[len - 1] = '\0'; - return ret; + return xmemdupz(line + key_len + 1, eol - line - key_len - 1); } line = next; } @@ -709,7 +705,7 @@ static void fill_person(struct interp *table, const char *msg, int len) start = end + 1; while (end > 0 && isspace(msg[end - 1])) end--; - table[0].value = xstrndup(msg, end); + table[0].value = xmemdupz(msg, end); if (start >= len) return; @@ -721,7 +717,7 @@ static void fill_person(struct interp *table, const char *msg, int len) if (end >= len) return; - table[1].value = xstrndup(msg + start, end - start); + table[1].value = xmemdupz(msg + start, end - start); /* parse date */ for (start = end + 1; start < len && isspace(msg[start]); start++) @@ -732,7 +728,7 @@ static void fill_person(struct interp *table, const char *msg, int len) if (msg + start == ep) return; - table[5].value = xstrndup(msg + start, ep - (msg + start)); + table[5].value = xmemdupz(msg + start, ep - (msg + start)); /* parse tz */ for (start = ep - msg + 1; start < len && isspace(msg[start]); start++) @@ -859,7 +855,7 @@ void format_commit_message(const struct commit *commit, ; /* do nothing */ if (state == SUBJECT) { - table[ISUBJECT].value = xstrndup(msg + i, eol - i); + table[ISUBJECT].value = xmemdupz(msg + i, eol - i); i = eol; } if (i == eol) { @@ -875,7 +871,7 @@ void format_commit_message(const struct commit *commit, msg + i + 10, eol - i - 10); else if (!prefixcmp(msg + i, "encoding ")) table[IENCODING].value = - xstrndup(msg + i + 9, eol - i - 9); + xmemdupz(msg + i + 9, eol - i - 9); i = eol; } if (msg[i]) -- cgit v1.2.1 From c76689df6c64a1e987bd779bd71a2042b5131fb9 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Thu, 20 Sep 2007 00:42:12 +0200 Subject: strbuf API additions and enhancements. Add strbuf_remove, change strbuf_insert: As both are special cases of strbuf_splice, implement them as such. gcc is able to do the math and generate almost optimal code this way. Add strbuf_swap: Exchange the values of its arguments. Use it in fast-import.c Also fix spacing issues in strbuf.h Signed-off-by: Pierre Habouzit --- commit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index f86fa776c..55b08ec0b 100644 --- a/commit.c +++ b/commit.c @@ -656,7 +656,7 @@ static char *replace_encoding_header(char *buf, const char *encoding) strbuf_attach(&tmp, buf, strlen(buf), strlen(buf) + 1); if (is_encoding_utf8(encoding)) { /* we have re-coded to UTF-8; drop the header */ - strbuf_splice(&tmp, start, len, NULL, 0); + strbuf_remove(&tmp, start, len); } else { /* just replaces XXXX in 'encoding XXXX\n' */ strbuf_splice(&tmp, start + strlen("encoding "), -- cgit v1.2.1 From 55246aac6717e86c14f31391ac903ed810d1a9a0 Mon Sep 17 00:00:00 2001 From: Michal Vitecek Date: Tue, 25 Sep 2007 16:38:46 +0200 Subject: Don't use "" for placeholders and suppress printing of empty user formats. This changes the interporate() to replace entries with NULL values by the empty string, and uses it to interpolate missing fields in custom format output used in git-log and friends. It is most useful to avoid output from %b format for a commit log message that lack any body text. Signed-off-by: Junio C Hamano --- commit.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 99f65cee0..c9a18180b 100644 --- a/commit.c +++ b/commit.c @@ -917,9 +917,6 @@ long format_commit_message(const struct commit *commit, const void *format, } 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, ""); do { char *buf = *buf_p; -- cgit v1.2.1 From b315c5c08139c0d3c1e4867a305334e29da01d07 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Thu, 27 Sep 2007 12:58:23 +0200 Subject: strbuf change: be sure ->buf is never ever NULL. For that purpose, the ->buf is always initialized with a char * buf living in the strbuf module. It is made a char * so that we can sloppily accept things that perform: sb->buf[0] = '\0', and because you can't pass "" as an initializer for ->buf without making gcc unhappy for very good reasons. strbuf_init/_detach/_grow have been fixed to trust ->alloc and not ->buf anymore. as a consequence strbuf_detach is _mandatory_ to detach a buffer, copying ->buf isn't an option anymore, if ->buf is going to escape from the scope, and eventually be free'd. API changes: * strbuf_setlen now always works, so just make strbuf_reset a convenience macro. * strbuf_detatch takes a size_t* optional argument (meaning it can be NULL) to copy the buffer's len, as it was needed for this refactor to make the code more readable, and working like the callers. Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano --- commit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 55b08ec0b..62cc74d7a 100644 --- a/commit.c +++ b/commit.c @@ -663,7 +663,7 @@ static char *replace_encoding_header(char *buf, const char *encoding) len - strlen("encoding \n"), encoding, strlen(encoding)); } - return tmp.buf; + return strbuf_detach(&tmp, NULL); } static char *logmsg_reencode(const struct commit *commit, -- cgit v1.2.1 From 60fcc2e6ce255da1baa92905c10456981d260fa0 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 10 Oct 2007 23:14:35 +0100 Subject: clear_commit_marks(): avoid deep recursion Before this patch, clear_commit_marks() recursed for each parent. This could be potentially very expensive in terms of stack space. Probably the only reason that this did not lead to problems is the fact that we typically call clear_commit_marks() after marking a relatively small set of commits. Use (sort of) a tail recursion instead: first recurse on the parents other than the first one, and then continue the loop with the first parent. Noticed by Shawn Pearce. Signed-off-by: Johannes Schindelin Signed-off-by: Lars Hjemli Signed-off-by: Shawn O. Pearce --- commit.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index dc5a0643f..1fbdd2d51 100644 --- a/commit.c +++ b/commit.c @@ -441,17 +441,22 @@ struct commit *pop_most_recent_commit(struct commit_list **list, void clear_commit_marks(struct commit *commit, unsigned int mark) { - struct commit_list *parents; + while (commit) { + struct commit_list *parents; - commit->object.flags &= ~mark; - parents = commit->parents; - while (parents) { - struct commit *parent = parents->item; + if (!(mark & commit->object.flags)) + return; - /* Have we already cleared this? */ - if (mark & parent->object.flags) - clear_commit_marks(parent, mark); - parents = parents->next; + commit->object.flags &= ~mark; + + parents = commit->parents; + if (!parents) + return; + + while ((parents = parents->next)) + clear_commit_marks(parents->item, mark); + + commit = commit->parents->item; } } -- cgit v1.2.1 From 4593fb84051d39f65cec81958e91056986e4682f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 31 Oct 2007 14:55:17 -0700 Subject: format-patch -s: add MIME encoding header if signer's name requires so When the body of the commit log message contains a non-ASCII character, format-patch correctly emitted the encoding header to mark the resulting message as such. However, if the original message was fully ASCII, the command line switch "-s" was given to add a new sign-off, and the signer's name was not ASCII only, the resulting message would have contained non-ASCII character but was not marked as such. Signed-off-by: Junio C Hamano --- commit.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index ac24266e9..8262f6ac5 100644 --- a/commit.c +++ b/commit.c @@ -479,7 +479,7 @@ static int get_one_line(const char *msg) } /* High bit set, or ISO-2022-INT */ -static int non_ascii(int ch) +int non_ascii(int ch) { ch = (ch & 0xff); return ((ch & 0x80) || (ch == 0x1b)); @@ -1046,12 +1046,11 @@ static void pp_remainder(enum cmit_fmt fmt, void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, struct strbuf *sb, int abbrev, const char *subject, const char *after_subject, - enum date_mode dmode) + enum date_mode dmode, int plain_non_ascii) { unsigned long beginning_of_body; int indent = 4; const char *msg = commit->buffer; - int plain_non_ascii = 0; char *reencoded; const char *encoding; -- cgit v1.2.1 From 23c17d4a4a0e1fc9a5fa347f1fc6be3cf477e543 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 2 Nov 2007 13:32:58 -0700 Subject: Simplify topo-sort logic .. by not using quite so much indirection. This currently grows the "struct commit" a bit, which could be avoided by using a union for "util" and "indegree" (the topo-sort used to use "util" anyway, so you cannot use them together), but for now the goal of this was to simplify, not optimize. Signed-off-by: Linus Torvalds --- commit.c | 150 ++++++++++++++++++++++----------------------------------------- 1 file changed, 52 insertions(+), 98 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 8262f6ac5..ab4eb8bdd 100644 --- a/commit.c +++ b/commit.c @@ -9,22 +9,6 @@ int save_commit_buffer = 1; -struct sort_node -{ - /* - * the number of children of the associated commit - * that also occur in the list being sorted. - */ - unsigned int indegree; - - /* - * reference to original list item that we will re-use - * on output. - */ - struct commit_list * list_item; - -}; - const char *commit_type = "commit"; static struct cmt_fmt_map { @@ -1149,69 +1133,38 @@ struct commit *pop_commit(struct commit_list **stack) return item; } -void topo_sort_default_setter(struct commit *c, void *data) -{ - c->util = data; -} - -void *topo_sort_default_getter(struct commit *c) -{ - return c->util; -} - /* * Performs an in-place topological sort on the list supplied. */ void sort_in_topological_order(struct commit_list ** list, int lifo) { - sort_in_topological_order_fn(list, lifo, topo_sort_default_setter, - topo_sort_default_getter); -} - -void sort_in_topological_order_fn(struct commit_list ** list, int lifo, - topo_sort_set_fn_t setter, - topo_sort_get_fn_t getter) -{ - struct commit_list * next = *list; - struct commit_list * work = NULL, **insert; - struct commit_list ** pptr = list; - struct sort_node * nodes; - struct sort_node * next_nodes; - int count = 0; - - /* determine the size of the list */ - while (next) { - next = next->next; - count++; - } + struct commit_list *next, *orig = *list; + struct commit_list *work, **insert; + struct commit_list **pptr; - if (!count) + if (!orig) return; - /* allocate an array to help sort the list */ - nodes = xcalloc(count, sizeof(*nodes)); - /* link the list to the array */ - next_nodes = nodes; - next=*list; - while (next) { - next_nodes->list_item = next; - setter(next->item, next_nodes); - next_nodes++; - next = next->next; + *list = NULL; + + /* Mark them and clear the indegree */ + for (next = orig; next; next = next->next) { + struct commit *commit = next->item; + commit->object.flags |= TOPOSORT; + commit->indegree = 0; } + /* update the indegree */ - next=*list; - while (next) { + for (next = orig; next; next = next->next) { struct commit_list * parents = next->item->parents; while (parents) { - struct commit * parent=parents->item; - struct sort_node * pn = (struct sort_node *) getter(parent); + struct commit *parent = parents->item; - if (pn) - pn->indegree++; - parents=parents->next; + if (parent->object.flags & TOPOSORT) + parent->indegree++; + parents = parents->next; } - next=next->next; } + /* * find the tips * @@ -1219,55 +1172,56 @@ void sort_in_topological_order_fn(struct commit_list ** list, int lifo, * * the tips serve as a starting set for the work queue. */ - next=*list; + work = NULL; insert = &work; - while (next) { - struct sort_node * node = (struct sort_node *) getter(next->item); + for (next = orig; next; next = next->next) { + struct commit *commit = next->item; - if (node->indegree == 0) { - insert = &commit_list_insert(next->item, insert)->next; - } - next=next->next; + if (!commit->indegree) + insert = &commit_list_insert(commit, insert)->next; } /* process the list in topological order */ if (!lifo) sort_by_date(&work); + + pptr = list; + *list = NULL; while (work) { - struct commit * work_item = pop_commit(&work); - struct sort_node * work_node = (struct sort_node *) getter(work_item); - struct commit_list * parents = work_item->parents; + struct commit *commit; + struct commit_list *parents, *work_item; - while (parents) { - struct commit * parent=parents->item; - struct sort_node * pn = (struct sort_node *) getter(parent); - - if (pn) { - /* - * parents are only enqueued for emission - * when all their children have been emitted thereby - * guaranteeing topological order. - */ - pn->indegree--; - if (!pn->indegree) { - if (!lifo) - insert_by_date(parent, &work); - else - commit_list_insert(parent, &work); - } + work_item = work; + work = work_item->next; + work_item->next = NULL; + + commit = work_item->item; + for (parents = commit->parents; parents ; parents = parents->next) { + struct commit *parent=parents->item; + + if (!(parent->object.flags & TOPOSORT)) + continue; + + /* + * parents are only enqueued for emission + * when all their children have been emitted thereby + * guaranteeing topological order. + */ + if (!--parent->indegree) { + if (!lifo) + insert_by_date(parent, &work); + else + commit_list_insert(parent, &work); } - parents=parents->next; } /* * work_item is a commit all of whose children * have already been emitted. we can emit it now. */ - *pptr = work_node->list_item; - pptr = &(*pptr)->next; - *pptr = NULL; - setter(work_item, NULL); + commit->object.flags &= ~TOPOSORT; + *pptr = work_item; + pptr = &work_item->next; } - free(nodes); } /* merge-base stuff */ -- cgit v1.2.1 From 93fc05eb9ef505a05d9ce9415177697449afc8ad Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sun, 4 Nov 2007 19:15:06 +0000 Subject: Split off the pretty print stuff into its own file The file commit.c got quite large, but it does not have to be: the code concerning pretty printing is pretty well contained. In fact, this commit just splits it off into pretty.c, leaving commit.c with just 672 lines. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- commit.c | 718 --------------------------------------------------------------- 1 file changed, 718 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 8262f6ac5..b50926587 100644 --- a/commit.c +++ b/commit.c @@ -3,7 +3,6 @@ #include "commit.h" #include "pkt-line.h" #include "utf8.h" -#include "interpolate.h" #include "diff.h" #include "revision.h" @@ -27,46 +26,6 @@ struct sort_node const char *commit_type = "commit"; -static struct cmt_fmt_map { - const char *n; - size_t cmp_len; - enum cmit_fmt v; -} cmt_fmts[] = { - { "raw", 1, CMIT_FMT_RAW }, - { "medium", 1, CMIT_FMT_MEDIUM }, - { "short", 1, CMIT_FMT_SHORT }, - { "email", 1, CMIT_FMT_EMAIL }, - { "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; - - if (!arg || !*arg) - 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))) - return cmt_fmts[i].v; - } - - die("invalid --pretty format: %s", arg); -} - static struct commit *check_commit(struct object *obj, const unsigned char *sha1, int quiet) @@ -460,683 +419,6 @@ void clear_commit_marks(struct commit *commit, unsigned int mark) } } -/* - * Generic support for pretty-printing the header - */ -static int get_one_line(const char *msg) -{ - int ret = 0; - - for (;;) { - char c = *msg++; - if (!c) - break; - ret++; - if (c == '\n') - break; - } - return ret; -} - -/* High bit set, or ISO-2022-INT */ -int non_ascii(int ch) -{ - ch = (ch & 0xff); - return ((ch & 0x80) || (ch == 0x1b)); -} - -static int is_rfc2047_special(char ch) -{ - return (non_ascii(ch) || (ch == '=') || (ch == '?') || (ch == '_')); -} - -static void add_rfc2047(struct strbuf *sb, const char *line, int len, - const char *encoding) -{ - int i, last; - - for (i = 0; i < len; i++) { - int ch = line[i]; - if (non_ascii(ch)) - goto needquote; - if ((i + 1 < len) && (ch == '=' && line[i+1] == '?')) - goto needquote; - } - strbuf_add(sb, line, len); - return; - -needquote: - strbuf_grow(sb, len * 3 + strlen(encoding) + 100); - strbuf_addf(sb, "=?%s?q?", encoding); - for (i = last = 0; i < len; i++) { - unsigned ch = line[i] & 0xFF; - /* - * We encode ' ' using '=20' even though rfc2047 - * allows using '_' for readability. Unfortunately, - * many programs do not understand this and just - * leave the underscore in place. - */ - if (is_rfc2047_special(ch) || ch == ' ') { - strbuf_add(sb, line + last, i - last); - strbuf_addf(sb, "=%02X", ch); - last = i + 1; - } - } - strbuf_add(sb, line + last, len - last); - strbuf_addstr(sb, "?="); -} - -static void add_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb, - const char *line, enum date_mode dmode, - const char *encoding) -{ - char *date; - int namelen; - unsigned long time; - int tz; - const char *filler = " "; - - if (fmt == CMIT_FMT_ONELINE) - return; - date = strchr(line, '>'); - if (!date) - return; - namelen = ++date - line; - time = strtoul(date, &date, 10); - tz = strtol(date, NULL, 10); - - if (fmt == CMIT_FMT_EMAIL) { - char *name_tail = strchr(line, '<'); - int display_name_length; - if (!name_tail) - return; - while (line < name_tail && isspace(name_tail[-1])) - name_tail--; - display_name_length = name_tail - line; - filler = ""; - strbuf_addstr(sb, "From: "); - add_rfc2047(sb, line, display_name_length, encoding); - strbuf_add(sb, name_tail, namelen - display_name_length); - strbuf_addch(sb, '\n'); - } else { - strbuf_addf(sb, "%s: %.*s%.*s\n", what, - (fmt == CMIT_FMT_FULLER) ? 4 : 0, - filler, namelen, line); - } - switch (fmt) { - case CMIT_FMT_MEDIUM: - strbuf_addf(sb, "Date: %s\n", show_date(time, tz, dmode)); - break; - case CMIT_FMT_EMAIL: - strbuf_addf(sb, "Date: %s\n", show_date(time, tz, DATE_RFC2822)); - break; - case CMIT_FMT_FULLER: - strbuf_addf(sb, "%sDate: %s\n", what, show_date(time, tz, dmode)); - break; - default: - /* notin' */ - break; - } -} - -static int is_empty_line(const char *line, int *len_p) -{ - int len = *len_p; - while (len && isspace(line[len-1])) - len--; - *len_p = len; - return !len; -} - -static void add_merge_info(enum cmit_fmt fmt, struct strbuf *sb, - const struct commit *commit, int abbrev) -{ - struct commit_list *parent = commit->parents; - - if ((fmt == CMIT_FMT_ONELINE) || (fmt == CMIT_FMT_EMAIL) || - !parent || !parent->next) - return; - - strbuf_addstr(sb, "Merge:"); - - while (parent) { - struct commit *p = parent->item; - const char *hex = NULL; - const char *dots; - if (abbrev) - hex = find_unique_abbrev(p->object.sha1, abbrev); - if (!hex) - hex = sha1_to_hex(p->object.sha1); - dots = (abbrev && strlen(hex) != 40) ? "..." : ""; - parent = parent->next; - - strbuf_addf(sb, " %s%s", hex, dots); - } - strbuf_addch(sb, '\n'); -} - -static char *get_header(const struct commit *commit, const char *key) -{ - int key_len = strlen(key); - const char *line = commit->buffer; - - for (;;) { - const char *eol = strchr(line, '\n'), *next; - - if (line == eol) - return NULL; - if (!eol) { - eol = line + strlen(line); - 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; - } -} - -static char *replace_encoding_header(char *buf, const char *encoding) -{ - struct strbuf tmp; - size_t start, len; - char *cp = buf; - - /* guess if there is an encoding header before a \n\n */ - while (strncmp(cp, "encoding ", strlen("encoding "))) { - cp = strchr(cp, '\n'); - if (!cp || *++cp == '\n') - return buf; - } - start = cp - buf; - cp = strchr(cp, '\n'); - if (!cp) - return buf; /* should not happen but be defensive */ - len = cp + 1 - (buf + start); - - strbuf_init(&tmp, 0); - strbuf_attach(&tmp, buf, strlen(buf), strlen(buf) + 1); - if (is_encoding_utf8(encoding)) { - /* we have re-coded to UTF-8; drop the header */ - strbuf_remove(&tmp, start, len); - } else { - /* just replaces XXXX in 'encoding XXXX\n' */ - strbuf_splice(&tmp, start + strlen("encoding "), - len - strlen("encoding \n"), - encoding, strlen(encoding)); - } - return strbuf_detach(&tmp, NULL); -} - -static char *logmsg_reencode(const struct commit *commit, - const char *output_encoding) -{ - static const char *utf8 = "utf-8"; - const char *use_encoding; - char *encoding; - char *out; - - if (!*output_encoding) - return NULL; - encoding = get_header(commit, "encoding"); - use_encoding = encoding ? encoding : utf8; - if (!strcmp(use_encoding, output_encoding)) - if (encoding) /* we'll strip encoding header later */ - out = xstrdup(commit->buffer); - else - return NULL; /* nothing to do */ - else - out = reencode_string(commit->buffer, - output_encoding, use_encoding); - if (out) - out = replace_encoding_header(out, output_encoding); - - free(encoding); - return out; -} - -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 = xmemdupz(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 = xmemdupz(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 = xmemdupz(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, DATE_NORMAL)); - interp_set_entry(table, 3, show_date(date, tz, DATE_RFC2822)); - interp_set_entry(table, 4, show_date(date, tz, DATE_RELATIVE)); - interp_set_entry(table, 6, show_date(date, tz, DATE_ISO8601)); -} - -void format_commit_message(const struct commit *commit, - const void *format, struct strbuf *sb) -{ - 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 */ - { "%ai" }, /* author date, ISO 8601 */ - { "%cn" }, /* committer name */ - { "%ce" }, /* committer email */ - { "%cd" }, /* committer date */ - { "%cD" }, /* committer date, RFC2822 style */ - { "%cr" }, /* committer date, relative */ - { "%ct" }, /* committer date, UNIX timestamp */ - { "%ci" }, /* committer date, ISO 8601 */ - { "%e" }, /* encoding */ - { "%s" }, /* subject */ - { "%b" }, /* body */ - { "%Cred" }, /* red */ - { "%Cgreen" }, /* green */ - { "%Cblue" }, /* blue */ - { "%Creset" }, /* reset color */ - { "%n" }, /* newline */ - { "%m" }, /* left/right/bottom */ - }; - 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, IAUTHOR_ISO8601, - ICOMMITTER_NAME, ICOMMITTER_EMAIL, - ICOMMITTER_DATE, ICOMMITTER_DATE_RFC2822, - ICOMMITTER_DATE_RELATIVE, ICOMMITTER_TIMESTAMP, - ICOMMITTER_ISO8601, - IENCODING, - ISUBJECT, - IBODY, - IRED, IGREEN, IBLUE, IRESET_COLOR, - INEWLINE, - ILEFT_RIGHT, - }; - struct commit_list *p; - char parents[1024]; - unsigned long len; - int i; - enum { HEADER, SUBJECT, BODY } state; - const char *msg = commit->buffer; - - if (ILEFT_RIGHT + 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)); - interp_set_entry(table, ILEFT_RIGHT, - (commit->object.flags & BOUNDARY) - ? "-" - : (commit->object.flags & SYMMETRIC_LEFT) - ? "<" - : ">"); - - 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 = xmemdupz(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 = - xmemdupz(msg + i + 9, eol - i - 9); - i = eol; - } - if (msg[i]) - table[IBODY].value = xstrdup(msg + i); - - len = interpolate(sb->buf + sb->len, strbuf_avail(sb), - format, table, ARRAY_SIZE(table)); - if (len > strbuf_avail(sb)) { - strbuf_grow(sb, len); - interpolate(sb->buf + sb->len, strbuf_avail(sb) + 1, - format, table, ARRAY_SIZE(table)); - } - strbuf_setlen(sb, sb->len + len); - interp_clear_table(table, ARRAY_SIZE(table)); -} - -static void pp_header(enum cmit_fmt fmt, - int abbrev, - enum date_mode dmode, - const char *encoding, - const struct commit *commit, - const char **msg_p, - struct strbuf *sb) -{ - int parents_shown = 0; - - for (;;) { - const char *line = *msg_p; - int linelen = get_one_line(*msg_p); - - if (!linelen) - return; - *msg_p += linelen; - - if (linelen == 1) - /* End of header */ - return; - - if (fmt == CMIT_FMT_RAW) { - strbuf_add(sb, line, linelen); - continue; - } - - if (!memcmp(line, "parent ", 7)) { - if (linelen != 48) - die("bad parent line in commit"); - continue; - } - - if (!parents_shown) { - struct commit_list *parent; - int num; - for (parent = commit->parents, num = 0; - parent; - parent = parent->next, num++) - ; - /* with enough slop */ - strbuf_grow(sb, num * 50 + 20); - add_merge_info(fmt, sb, commit, abbrev); - parents_shown = 1; - } - - /* - * MEDIUM == DEFAULT shows only author with dates. - * FULL shows both authors but not dates. - * FULLER shows both authors and dates. - */ - if (!memcmp(line, "author ", 7)) { - strbuf_grow(sb, linelen + 80); - add_user_info("Author", fmt, sb, line + 7, dmode, encoding); - } - if (!memcmp(line, "committer ", 10) && - (fmt == CMIT_FMT_FULL || fmt == CMIT_FMT_FULLER)) { - strbuf_grow(sb, linelen + 80); - add_user_info("Commit", fmt, sb, line + 10, dmode, encoding); - } - } -} - -static void pp_title_line(enum cmit_fmt fmt, - const char **msg_p, - struct strbuf *sb, - const char *subject, - const char *after_subject, - const char *encoding, - int plain_non_ascii) -{ - struct strbuf title; - - strbuf_init(&title, 80); - - for (;;) { - const char *line = *msg_p; - int linelen = get_one_line(line); - - *msg_p += linelen; - if (!linelen || is_empty_line(line, &linelen)) - break; - - strbuf_grow(&title, linelen + 2); - if (title.len) { - if (fmt == CMIT_FMT_EMAIL) { - strbuf_addch(&title, '\n'); - } - strbuf_addch(&title, ' '); - } - strbuf_add(&title, line, linelen); - } - - strbuf_grow(sb, title.len + 1024); - if (subject) { - strbuf_addstr(sb, subject); - add_rfc2047(sb, title.buf, title.len, encoding); - } else { - strbuf_addbuf(sb, &title); - } - strbuf_addch(sb, '\n'); - - if (plain_non_ascii) { - const char *header_fmt = - "MIME-Version: 1.0\n" - "Content-Type: text/plain; charset=%s\n" - "Content-Transfer-Encoding: 8bit\n"; - strbuf_addf(sb, header_fmt, encoding); - } - if (after_subject) { - strbuf_addstr(sb, after_subject); - } - if (fmt == CMIT_FMT_EMAIL) { - strbuf_addch(sb, '\n'); - } - strbuf_release(&title); -} - -static void pp_remainder(enum cmit_fmt fmt, - const char **msg_p, - struct strbuf *sb, - int indent) -{ - int first = 1; - for (;;) { - const char *line = *msg_p; - int linelen = get_one_line(line); - *msg_p += linelen; - - if (!linelen) - break; - - if (is_empty_line(line, &linelen)) { - if (first) - continue; - if (fmt == CMIT_FMT_SHORT) - break; - } - first = 0; - - strbuf_grow(sb, linelen + indent + 20); - if (indent) { - memset(sb->buf + sb->len, ' ', indent); - strbuf_setlen(sb, sb->len + indent); - } - strbuf_add(sb, line, linelen); - strbuf_addch(sb, '\n'); - } -} - -void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, - struct strbuf *sb, int abbrev, - const char *subject, const char *after_subject, - enum date_mode dmode, int plain_non_ascii) -{ - unsigned long beginning_of_body; - int indent = 4; - const char *msg = commit->buffer; - char *reencoded; - const char *encoding; - - if (fmt == CMIT_FMT_USERFORMAT) { - format_commit_message(commit, user_format, sb); - return; - } - - encoding = (git_log_output_encoding - ? git_log_output_encoding - : git_commit_encoding); - if (!encoding) - encoding = "utf-8"; - reencoded = logmsg_reencode(commit, encoding); - if (reencoded) { - msg = reencoded; - } - - if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL) - indent = 0; - - /* After-subject is used to pass in Content-Type: multipart - * MIME header; in that case we do not have to do the - * plaintext content type even if the commit message has - * non 7-bit ASCII character. Otherwise, check if we need - * to say this is not a 7-bit ASCII. - */ - if (fmt == CMIT_FMT_EMAIL && !after_subject) { - int i, ch, in_body; - - for (in_body = i = 0; (ch = msg[i]); i++) { - if (!in_body) { - /* author could be non 7-bit ASCII but - * the log may be so; skip over the - * header part first. - */ - if (ch == '\n' && msg[i+1] == '\n') - in_body = 1; - } - else if (non_ascii(ch)) { - plain_non_ascii = 1; - break; - } - } - } - - pp_header(fmt, abbrev, dmode, encoding, commit, &msg, sb); - if (fmt != CMIT_FMT_ONELINE && !subject) { - strbuf_addch(sb, '\n'); - } - - /* Skip excess blank lines at the beginning of body, if any... */ - for (;;) { - int linelen = get_one_line(msg); - int ll = linelen; - if (!linelen) - break; - if (!is_empty_line(msg, &ll)) - break; - msg += linelen; - } - - /* These formats treat the title line specially. */ - if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL) - pp_title_line(fmt, &msg, sb, subject, - after_subject, encoding, plain_non_ascii); - - beginning_of_body = sb->len; - if (fmt != CMIT_FMT_ONELINE) - pp_remainder(fmt, &msg, sb, indent); - strbuf_rtrim(sb); - - /* Make sure there is an EOLN for the non-oneline case */ - if (fmt != CMIT_FMT_ONELINE) - strbuf_addch(sb, '\n'); - - /* - * The caller may append additional body text in e-mail - * format. Make sure we did not strip the blank line - * between the header and the body. - */ - if (fmt == CMIT_FMT_EMAIL && sb->len <= beginning_of_body) - strbuf_addch(sb, '\n'); - free(reencoded); -} - struct commit *pop_commit(struct commit_list **stack) { struct commit_list *top = *stack; -- cgit v1.2.1 From 0a61779994aa3de41d57bb85bd88a2f56c7ba7d8 Mon Sep 17 00:00:00 2001 From: Martin Koegler Date: Sat, 19 Jan 2008 18:35:23 +0100 Subject: parse_commit_buffer: tighten checks while parsing This tightens the parsing of a commit object in a couple of ways. - The "tree " header must end with a LF (earlier we did not check this condition). - Make sure parsing of timestamp on the "committer " header does not go beyond the buffer, even when (1) the "author " header does not end with a LF (this means that the commit object is malformed and lacks the committer information) or (2) the "committer " header does not have ">" that is the end of the e-mail address, or (3) the "committer " header does not end with a LF. We however still keep the existing behaviour to return a parsed commit object even when non-structural headers such as committer and author are malformed, so that tools that need to look at commits to clean up a history with such broken commits can still get at the structural data (i.e. the parents chain and the tree object). Signed-off-by: Martin Koegler Signed-off-by: Junio C Hamano --- commit.c | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index f074811ed..8b8fb04d1 100644 --- a/commit.c +++ b/commit.c @@ -48,19 +48,32 @@ struct commit *lookup_commit(const unsigned char *sha1) return check_commit(obj, sha1, 0); } -static unsigned long parse_commit_date(const char *buf) +static unsigned long parse_commit_date(const char *buf, const char *tail) { unsigned long date; + const char *dateptr; + if (buf + 6 >= tail) + return 0; if (memcmp(buf, "author", 6)) return 0; - while (*buf++ != '\n') + while (buf < tail && *buf++ != '\n') /* nada */; + if (buf + 9 >= tail) + return 0; if (memcmp(buf, "committer", 9)) return 0; - while (*buf++ != '>') + while (buf < tail && *buf++ != '>') /* nada */; - date = strtoul(buf, NULL, 10); + if (buf >= tail) + return 0; + dateptr = buf; + while (buf < tail && *buf++ != '\n') + /* nada */; + if (buf >= tail) + return 0; + /* dateptr < buf && buf[-1] == '\n', so strtoul will stop at buf-1 */ + date = strtoul(dateptr, NULL, 10); if (date == ULONG_MAX) date = 0; return date; @@ -236,9 +249,9 @@ int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size) return 0; item->object.parsed = 1; tail += size; - if (tail <= bufptr + 5 || memcmp(bufptr, "tree ", 5)) + if (tail <= bufptr + 46 || memcmp(bufptr, "tree ", 5) || bufptr[45] != '\n') return error("bogus commit object %s", sha1_to_hex(item->object.sha1)); - if (tail <= bufptr + 45 || get_sha1_hex(bufptr + 5, parent) < 0) + if (get_sha1_hex(bufptr + 5, parent) < 0) return error("bad tree pointer in commit %s", sha1_to_hex(item->object.sha1)); item->tree = lookup_tree(parent); @@ -275,7 +288,7 @@ int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size) n_refs++; } } - item->date = parse_commit_date(bufptr); + item->date = parse_commit_date(bufptr, tail); if (track_object_refs) { unsigned i = 0; -- cgit v1.2.1 From 172947e645a6c919efb78a246c919d0daaa674f0 Mon Sep 17 00:00:00 2001 From: Martin Koegler Date: Mon, 18 Feb 2008 21:47:57 +0100 Subject: check results of parse_commit in merge_bases An error is signaled by returning NULL. Signed-off-by: Martin Koegler Signed-off-by: Junio C Hamano --- commit.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 8b8fb04d1..70f12664c 100644 --- a/commit.c +++ b/commit.c @@ -552,8 +552,10 @@ static struct commit_list *merge_bases(struct commit *one, struct commit *two) */ return commit_list_insert(one, &result); - parse_commit(one); - parse_commit(two); + if (parse_commit(one)) + return NULL; + if (parse_commit(two)) + return NULL; one->object.flags |= PARENT1; two->object.flags |= PARENT2; @@ -586,7 +588,8 @@ static struct commit_list *merge_bases(struct commit *one, struct commit *two) parents = parents->next; if ((p->object.flags & flags) == flags) continue; - parse_commit(p); + if (parse_commit(p)) + return NULL; p->object.flags |= flags; insert_by_date(p, &list); } -- cgit v1.2.1 From 9786f68bfcc082778aee74159540e341bb239514 Mon Sep 17 00:00:00 2001 From: Martin Koegler Date: Mon, 18 Feb 2008 21:48:02 +0100 Subject: parse_commit: don't fail, if object is NULL Some codepaths (eg. builtin-rev-parse -> get_merge_bases -> parse_commit) can pass NULL. Signed-off-by: Martin Koegler Signed-off-by: Junio C Hamano --- commit.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 70f12664c..5d57450de 100644 --- a/commit.c +++ b/commit.c @@ -311,6 +311,8 @@ int parse_commit(struct commit *item) unsigned long size; int ret; + if (!item) + return -1; if (item->object.parsed) return 0; buffer = read_sha1_file(item->object.sha1, &type, &size); -- cgit v1.2.1 From dec38c81657f02624752a65c24d72613316713f5 Mon Sep 17 00:00:00 2001 From: Martin Koegler Date: Mon, 18 Feb 2008 21:48:03 +0100 Subject: check return value from parse_commit() in various functions Signed-off-by: Martin Koegler Signed-off-by: Junio C Hamano --- commit.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 5d57450de..22ce77686 100644 --- a/commit.c +++ b/commit.c @@ -387,8 +387,7 @@ struct commit *pop_most_recent_commit(struct commit_list **list, while (parents) { struct commit *commit = parents->item; - parse_commit(commit); - if (!(commit->object.flags & mark)) { + if (!parse_commit(commit) && !(commit->object.flags & mark)) { commit->object.flags |= mark; insert_by_date(commit, list); } -- cgit v1.2.1 From 7914053ba9901be1f1530f46e8e2e6ee6f4ae5b1 Mon Sep 17 00:00:00 2001 From: Martin Koegler Date: Mon, 25 Feb 2008 22:46:06 +0100 Subject: Remove unused object-ref code Signed-off-by: Martin Koegler Signed-off-by: Junio C Hamano --- commit.c | 11 ----------- 1 file changed, 11 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 22ce77686..6684c4e73 100644 --- a/commit.c +++ b/commit.c @@ -290,17 +290,6 @@ int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size) } item->date = parse_commit_date(bufptr, tail); - if (track_object_refs) { - unsigned i = 0; - struct commit_list *p; - struct object_refs *refs = alloc_object_refs(n_refs); - if (item->tree) - refs->ref[i++] = &item->tree->object; - for (p = item->parents; p; p = p->next) - refs->ref[i++] = &p->item->object; - set_object_refs(&item->object, refs); - } - return 0; } -- cgit v1.2.1 From 45163382437c3862d3beb88134b7a975a3a26443 Mon Sep 17 00:00:00 2001 From: Martin Koegler Date: Mon, 25 Feb 2008 22:46:07 +0100 Subject: builtin-fsck: reports missing parent commits parse_commit ignores parent commits with certain errors (eg. a non commit object is already loaded under the sha1 of the parent). To make fsck reports such errors, it has to compare the nummer of parent commits returned by parse commit with the number of parent commits in the object or in the graft/shallow file. Signed-off-by: Martin Koegler Signed-off-by: Junio C Hamano --- commit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 6684c4e73..94d5b3d26 100644 --- a/commit.c +++ b/commit.c @@ -193,7 +193,7 @@ static void prepare_commit_graft(void) commit_graft_prepared = 1; } -static struct commit_graft *lookup_commit_graft(const unsigned char *sha1) +struct commit_graft *lookup_commit_graft(const unsigned char *sha1) { int pos; prepare_commit_graft(); -- cgit v1.2.1 From 39468defd306b7fb8ca0d381a2d66216a8eb9095 Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Sat, 7 Jun 2008 22:38:37 +0200 Subject: Remove unused code in parse_commit_buffer() The n_refs variable is no longer really used in this function, so there is no reason to keep it. It was introduced in 27dedf0c and the code that really used it was removed in 7914053. Signed-off-by: Miklos Vajna Signed-off-by: Junio C Hamano --- commit.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 94d5b3d26..e2d8624d9 100644 --- a/commit.c +++ b/commit.c @@ -243,7 +243,6 @@ int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size) unsigned char parent[20]; struct commit_list **pptr; struct commit_graft *graft; - unsigned n_refs = 0; if (item->object.parsed) return 0; @@ -255,8 +254,6 @@ int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size) return error("bad tree pointer in commit %s", sha1_to_hex(item->object.sha1)); item->tree = lookup_tree(parent); - if (item->tree) - n_refs++; bufptr += 46; /* "tree " + "hex sha1" + "\n" */ pptr = &item->parents; @@ -272,10 +269,8 @@ int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size) if (graft) continue; new_parent = lookup_commit(parent); - if (new_parent) { + if (new_parent) pptr = &commit_list_insert(new_parent, pptr)->next; - n_refs++; - } } if (graft) { int i; @@ -285,7 +280,6 @@ int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size) if (!new_parent) continue; pptr = &commit_list_insert(new_parent, pptr)->next; - n_refs++; } } item->date = parse_commit_date(bufptr, tail); -- cgit v1.2.1 From 653194758ee338b7c87d011007c532ed5cf68d45 Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Fri, 27 Jun 2008 18:21:55 +0200 Subject: Move commit_list_count() to commit.c This function is useful outside builtin-merge-recursive, for example in builtin-merge. Signed-off-by: Miklos Vajna Signed-off-by: Junio C Hamano --- commit.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'commit.c') diff --git a/commit.c b/commit.c index e2d8624d9..bbf9c7541 100644 --- a/commit.c +++ b/commit.c @@ -325,6 +325,14 @@ struct commit_list *commit_list_insert(struct commit *item, struct commit_list * return new_list; } +unsigned commit_list_count(const struct commit_list *l) +{ + unsigned c = 0; + for (; l; l = l->next ) + c++; + return c; +} + void free_commit_list(struct commit_list *list) { while (list) { -- cgit v1.2.1 From 5240c9d75d8e1b747da427ba6320432d3201168a Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Fri, 27 Jun 2008 18:22:00 +0200 Subject: Introduce get_octopus_merge_bases() in commit.c This is like get_merge_bases() but it works for multiple heads, like show-branch --merge-base. Signed-off-by: Miklos Vajna Signed-off-by: Junio C Hamano --- commit.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'commit.c') diff --git a/commit.c b/commit.c index bbf9c7541..6052ca34c 100644 --- a/commit.c +++ b/commit.c @@ -600,6 +600,33 @@ static struct commit_list *merge_bases(struct commit *one, struct commit *two) return result; } +struct commit_list *get_octopus_merge_bases(struct commit_list *in) +{ + struct commit_list *i, *j, *k, *ret = NULL; + struct commit_list **pptr = &ret; + + for (i = in; i; i = i->next) { + if (!ret) + pptr = &commit_list_insert(i->item, pptr)->next; + else { + struct commit_list *new = NULL, *end = NULL; + + for (j = ret; j; j = j->next) { + struct commit_list *bases; + bases = get_merge_bases(i->item, j->item, 1); + if (!new) + new = bases; + else + end->next = bases; + for (k = bases; k; k = k->next) + end = k; + } + ret = new; + } + } + return ret; +} + struct commit_list *get_merge_bases(struct commit *one, struct commit *two, int cleanup) { -- cgit v1.2.1 From 6a938648e1454842157b84408acbb6471ec6745f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 27 Jun 2008 18:22:02 +0200 Subject: Introduce get_merge_bases_many() This introduces a new function get_merge_bases_many() which is a natural extension of two commit merge base computation. It is given one commit (one) and a set of other commits (twos), and computes the merge base of one and a merge across other commits. This is mostly useful to figure out the common ancestor when iterating over heads during an octopus merge. When making an octopus between commits A, B, C and D, we first merge tree of A and B, and then try to merge C with it. If we were making pairwise merge, we would be recording the tree resulting from the merge between A and B as a commit, say M, and then the next round we will be computing the merge base between M and C. o---C...* / . o---B...M / . o---o---A But during an octopus merge, we actually do not create a commit M. In order to figure out that the common ancestor to use for this merge, instead of computing the merge base between C and M, we can call merge_bases_many() with one set to C and twos containing A and B. Signed-off-by: Junio C Hamano --- commit.c | 56 ++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 38 insertions(+), 18 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 6052ca34c..cafed2692 100644 --- a/commit.c +++ b/commit.c @@ -533,26 +533,34 @@ static struct commit *interesting(struct commit_list *list) return NULL; } -static struct commit_list *merge_bases(struct commit *one, struct commit *two) +static struct commit_list *merge_bases_many(struct commit *one, int n, struct commit **twos) { struct commit_list *list = NULL; struct commit_list *result = NULL; + int i; - if (one == two) - /* We do not mark this even with RESULT so we do not - * have to clean it up. - */ - return commit_list_insert(one, &result); + for (i = 0; i < n; i++) { + if (one == twos[i]) + /* + * We do not mark this even with RESULT so we do not + * have to clean it up. + */ + return commit_list_insert(one, &result); + } if (parse_commit(one)) return NULL; - if (parse_commit(two)) - return NULL; + for (i = 0; i < n; i++) { + if (parse_commit(twos[i])) + return NULL; + } one->object.flags |= PARENT1; - two->object.flags |= PARENT2; insert_by_date(one, &list); - insert_by_date(two, &list); + for (i = 0; i < n; i++) { + twos[i]->object.flags |= PARENT2; + insert_by_date(twos[i], &list); + } while (interesting(list)) { struct commit *commit; @@ -627,21 +635,26 @@ struct commit_list *get_octopus_merge_bases(struct commit_list *in) return ret; } -struct commit_list *get_merge_bases(struct commit *one, - struct commit *two, int cleanup) +struct commit_list *get_merge_bases_many(struct commit *one, + int n, + struct commit **twos, + int cleanup) { struct commit_list *list; struct commit **rslt; struct commit_list *result; int cnt, i, j; - result = merge_bases(one, two); - if (one == two) - return result; + result = merge_bases_many(one, n, twos); + for (i = 0; i < n; i++) { + if (one == twos[i]) + return result; + } if (!result || !result->next) { if (cleanup) { clear_commit_marks(one, all_flags); - clear_commit_marks(two, all_flags); + for (i = 0; i < n; i++) + clear_commit_marks(twos[i], all_flags); } return result; } @@ -659,12 +672,13 @@ struct commit_list *get_merge_bases(struct commit *one, free_commit_list(result); clear_commit_marks(one, all_flags); - clear_commit_marks(two, all_flags); + for (i = 0; i < n; i++) + clear_commit_marks(twos[i], all_flags); for (i = 0; i < cnt - 1; i++) { for (j = i+1; j < cnt; j++) { if (!rslt[i] || !rslt[j]) continue; - result = merge_bases(rslt[i], rslt[j]); + result = merge_bases_many(rslt[i], 1, &rslt[j]); clear_commit_marks(rslt[i], all_flags); clear_commit_marks(rslt[j], all_flags); for (list = result; list; list = list->next) { @@ -686,6 +700,12 @@ struct commit_list *get_merge_bases(struct commit *one, return result; } +struct commit_list *get_merge_bases(struct commit *one, struct commit *two, + int cleanup) +{ + return get_merge_bases_many(one, 1, &two, cleanup); +} + int in_merge_bases(struct commit *commit, struct commit **reference, int num) { struct commit_list *bases, *b; -- cgit v1.2.1 From 98cf9c3bd7504d36e6049939bf9cc624f2cf5b9f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 27 Jun 2008 18:22:03 +0200 Subject: Introduce reduce_heads() The new function reduce_heads() is given a list of commits, and removes ones that can be reached from other commits in the list. It is useful for reducing the commits randomly thrown at the git-merge command and remove redundant commits that the user shouldn't have given to it. The implementation uses the get_merge_bases_many() introduced in the previous commit. If the merge base between one commit taken from the list and the remaining commits is the commit itself, that means the commit is reachable from some of the other commits. Signed-off-by: Junio C Hamano --- commit.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) (limited to 'commit.c') diff --git a/commit.c b/commit.c index cafed2692..d20b14ee3 100644 --- a/commit.c +++ b/commit.c @@ -725,3 +725,48 @@ int in_merge_bases(struct commit *commit, struct commit **reference, int num) free_commit_list(bases); return ret; } + +struct commit_list *reduce_heads(struct commit_list *heads) +{ + struct commit_list *p; + struct commit_list *result = NULL, **tail = &result; + struct commit **other; + size_t num_head, num_other; + + if (!heads) + return NULL; + + /* Avoid unnecessary reallocations */ + for (p = heads, num_head = 0; p; p = p->next) + num_head++; + other = xcalloc(sizeof(*other), num_head); + + /* For each commit, see if it can be reached by others */ + for (p = heads; p; p = p->next) { + struct commit_list *q, *base; + + num_other = 0; + for (q = heads; q; q = q->next) { + if (p == q) + continue; + other[num_other++] = q->item; + } + if (num_other) { + base = get_merge_bases_many(p->item, num_other, other, 1); + } else + base = NULL; + /* + * If p->item does not have anything common with other + * commits, there won't be any merge base. If it is + * reachable from some of the others, p->item will be + * the merge base. If its history is connected with + * others, but p->item is not reachable by others, we + * will get something other than p->item back. + */ + if (!base || (base->item != p->item)) + tail = &(commit_list_insert(p->item, tail)->next); + free_commit_list(base); + } + free(other); + return result; +} -- cgit v1.2.1 From 3d1dd4728b83e4c08d9fa7aaf2aa946e1012e061 Mon Sep 17 00:00:00 2001 From: Sverre Hvammen Johansen Date: Sun, 13 Jul 2008 08:13:55 +0000 Subject: reduce_heads(): thinkofix When comparing two commit objects for equality, it is sufficient to compare their in-core pointers because the object layer guarantees the uniqueness. However, comparing pointers to two "struct commit_list" instances that point at the same commit does not make any sense. Spotted by Sverre Hvammen Johansen who wrote an additional test to expose the problem, fixed by Miklos Vajna. Signed-off-by: Junio C Hamano --- commit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index d20b14ee3..03e73f323 100644 --- a/commit.c +++ b/commit.c @@ -747,7 +747,7 @@ struct commit_list *reduce_heads(struct commit_list *heads) num_other = 0; for (q = heads; q; q = q->next) { - if (p == q) + if (p->item == q->item) continue; other[num_other++] = q->item; } -- cgit v1.2.1 From 711f6b295cf463aae07eb76e009faed3d3699623 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 14 Jul 2008 00:09:41 -0700 Subject: reduce_heads(): protect from duplicate input Because we do not try computing merge base with itself for obvious reasons, the code was not prepared for an arguably insane case of the caller feeding the same commit twice to it. Noticed and test written by Sverre Hvammen Johansen Signed-off-by: Junio C Hamano --- commit.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 03e73f323..5148ec552 100644 --- a/commit.c +++ b/commit.c @@ -745,15 +745,22 @@ struct commit_list *reduce_heads(struct commit_list *heads) for (p = heads; p; p = p->next) { struct commit_list *q, *base; + /* Do we already have this in the result? */ + for (q = result; q; q = q->next) + if (p->item == q->item) + break; + if (q) + continue; + num_other = 0; for (q = heads; q; q = q->next) { if (p->item == q->item) continue; other[num_other++] = q->item; } - if (num_other) { + if (num_other) base = get_merge_bases_many(p->item, num_other, other, 1); - } else + else base = NULL; /* * If p->item does not have anything common with other -- cgit v1.2.1 From e358f3c31e1ae9f653e9b2a6be69f5df53b4ba7e Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 23 Jul 2008 01:51:36 +0100 Subject: sort_in_topological_order(): avoid setting a commit flag We used to set the TOPOSORT flag of commits during the topological sorting, but we can just as well use the member "indegree" for it: indegree is now incremented by 1 in the cases where the commit used to have the TOPOSORT flag. This is the same behavior as before, since indegree could not be non-zero when TOPOSORT was unset. Incidentally, this fixes the bug in show-branch where the 8th column was not shown: show-branch sorts the commits in topological order, assuming that all the commit flags are available for show-branch's private matters. But this was not true: TOPOSORT was identical to the flag corresponding to the 8th ref. So the flags for the 8th column were unset by the topological sorting. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- commit.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 5148ec552..dc0c5bfda 100644 --- a/commit.c +++ b/commit.c @@ -436,8 +436,7 @@ void sort_in_topological_order(struct commit_list ** list, int lifo) /* Mark them and clear the indegree */ for (next = orig; next; next = next->next) { struct commit *commit = next->item; - commit->object.flags |= TOPOSORT; - commit->indegree = 0; + commit->indegree = 1; } /* update the indegree */ @@ -446,7 +445,7 @@ void sort_in_topological_order(struct commit_list ** list, int lifo) while (parents) { struct commit *parent = parents->item; - if (parent->object.flags & TOPOSORT) + if (parent->indegree) parent->indegree++; parents = parents->next; } @@ -464,7 +463,7 @@ void sort_in_topological_order(struct commit_list ** list, int lifo) for (next = orig; next; next = next->next) { struct commit *commit = next->item; - if (!commit->indegree) + if (commit->indegree == 1) insert = &commit_list_insert(commit, insert)->next; } @@ -486,7 +485,7 @@ void sort_in_topological_order(struct commit_list ** list, int lifo) for (parents = commit->parents; parents ; parents = parents->next) { struct commit *parent=parents->item; - if (!(parent->object.flags & TOPOSORT)) + if (!parent->indegree) continue; /* @@ -494,7 +493,7 @@ void sort_in_topological_order(struct commit_list ** list, int lifo) * when all their children have been emitted thereby * guaranteeing topological order. */ - if (!--parent->indegree) { + if (--parent->indegree == 1) { if (!lifo) insert_by_date(parent, &work); else @@ -505,7 +504,7 @@ void sort_in_topological_order(struct commit_list ** list, int lifo) * work_item is a commit all of whose children * have already been emitted. we can emit it now. */ - commit->object.flags &= ~TOPOSORT; + commit->indegree = 0; *pptr = work_item; pptr = &work_item->next; } -- cgit v1.2.1 From c5ae6439d454288a809dd7c916e44ec4a5ae0b3b Mon Sep 17 00:00:00 2001 From: Nanako Shiraishi Date: Thu, 2 Oct 2008 19:14:30 +0900 Subject: commit.c: make read_graft_file() static This function is not called by any other file. Signed-off-by: Nanako Shiraishi Signed-off-by: Shawn O. Pearce --- commit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index dc0c5bfda..c99db162a 100644 --- a/commit.c +++ b/commit.c @@ -160,7 +160,7 @@ struct commit_graft *read_graft_line(char *buf, int len) return graft; } -int read_graft_file(const char *graft_file) +static int read_graft_file(const char *graft_file) { FILE *fp = fopen(graft_file, "r"); char buf[1024]; -- cgit v1.2.1 From 879ef2485d6ced20845ca626ecb45a9b65aa3a70 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sat, 20 Dec 2008 13:05:14 +0100 Subject: Introduce commit notes Commit notes are blobs which are shown together with the commit message. These blobs are taken from the notes ref, which you can configure by the config variable core.notesRef, which in turn can be overridden by the environment variable GIT_NOTES_REF. The notes ref is a branch which contains "files" whose names are the names of the corresponding commits (i.e. the SHA-1). The rationale for putting this information into a ref is this: we want to be able to fetch and possibly union-merge the notes, maybe even look at the date when a note was introduced, and we want to store them efficiently together with the other objects. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- commit.c | 1 + 1 file changed, 1 insertion(+) (limited to 'commit.c') diff --git a/commit.c b/commit.c index c99db162a..10e532afe 100644 --- a/commit.c +++ b/commit.c @@ -5,6 +5,7 @@ #include "utf8.h" #include "diff.h" #include "revision.h" +#include "notes.h" int save_commit_buffer = 1; -- cgit v1.2.1 From 7fcdb36e29f9a5e779bc9e44cd69f8f69fac9426 Mon Sep 17 00:00:00 2001 From: Jake Goulding Date: Mon, 26 Jan 2009 09:13:24 -0500 Subject: Make has_commit() non-static Move has_commit() from branch to a common location, in preparation for using it in "git-tag". Rename it to is_descendant_of() to make it more unique and descriptive. Signed-off-by: Jake Goulding Signed-off-by: Junio C Hamano --- commit.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'commit.c') diff --git a/commit.c b/commit.c index c99db162a..aa3b35b6a 100644 --- a/commit.c +++ b/commit.c @@ -705,6 +705,21 @@ struct commit_list *get_merge_bases(struct commit *one, struct commit *two, return get_merge_bases_many(one, 1, &two, cleanup); } +int is_descendant_of(struct commit *commit, struct commit_list *with_commit) +{ + if (!with_commit) + return 1; + while (with_commit) { + struct commit *other; + + other = with_commit->item; + with_commit = with_commit->next; + if (in_merge_bases(other, &commit, 1)) + return 1; + } + return 0; +} + int in_merge_bases(struct commit *commit, struct commit **reference, int num) { struct commit_list *bases, *b; -- cgit v1.2.1 From 954cfb5cfd17d57b9b31b19b73efe73199407e07 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 10 Feb 2009 21:31:33 -0800 Subject: Revert "Merge branch 'js/notes'" This reverts commit 7b75b331f6744fbf953fe8913703378ef86a2189, reversing changes made to 5d680a67d7909c89af96eba4a2d77abed606292b. --- commit.c | 1 - 1 file changed, 1 deletion(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index cf72143f5..aa3b35b6a 100644 --- a/commit.c +++ b/commit.c @@ -5,7 +5,6 @@ #include "utf8.h" #include "diff.h" #include "revision.h" -#include "notes.h" int save_commit_buffer = 1; -- cgit v1.2.1 From 836a3fd5b0c439642268fd1299cd16729f15e614 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sun, 17 May 2009 17:36:45 +0200 Subject: commit: add function to unparse a commit and its parents This patch adds the "unparse_commit" function that returns a commit into an unparsed state by freeing its data and resetting its fields to 0. Its parents are recursively unparsed too, because they might have been changed. But its tree is not unparsed as it should not have been modifed. Note that as the "flags" and "used" fields may be used even if the object is not parsed, we have to reset them anyway. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- commit.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'commit.c') diff --git a/commit.c b/commit.c index aa3b35b6a..8f6b703c5 100644 --- a/commit.c +++ b/commit.c @@ -316,6 +316,26 @@ int parse_commit(struct commit *item) return ret; } +static void unparse_commit_list(struct commit_list *list) +{ + for (; list; list = list->next) + unparse_commit(list->item); +} + +void unparse_commit(struct commit *item) +{ + item->object.flags = 0; + item->object.used = 0; + if (item->object.parsed) { + item->object.parsed = 0; + if (item->parents) { + unparse_commit_list(item->parents); + free_commit_list(item->parents); + item->parents = NULL; + } + } +} + struct commit_list *commit_list_insert(struct commit *item, struct commit_list **list_p) { struct commit_list *new_list = xmalloc(sizeof(struct commit_list)); -- cgit v1.2.1 From 7a8e3895f68e9ae4e44e521c78fc98768c2a88ec Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Wed, 27 May 2009 07:09:40 +0200 Subject: bisect: drop unparse_commit() and use clear_commit_marks() The goal of this patch series is to check if good revisions are ancestor of the bad revision without forking a process to launch "git rev-list $good ^$bad". This new version of this patch series does not use an "unparse_commit" function anymore, we use "clear_commit_marks" instead. Signed-off-by: Junio C Hamano --- commit.c | 20 -------------------- 1 file changed, 20 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 8f6b703c5..aa3b35b6a 100644 --- a/commit.c +++ b/commit.c @@ -316,26 +316,6 @@ int parse_commit(struct commit *item) return ret; } -static void unparse_commit_list(struct commit_list *list) -{ - for (; list; list = list->next) - unparse_commit(list->item); -} - -void unparse_commit(struct commit *item) -{ - item->object.flags = 0; - item->object.used = 0; - if (item->object.parsed) { - item->object.parsed = 0; - if (item->parents) { - unparse_commit_list(item->parents); - free_commit_list(item->parents); - item->parents = NULL; - } - } -} - struct commit_list *commit_list_insert(struct commit *item, struct commit_list **list_p) { struct commit_list *new_list = xmalloc(sizeof(struct commit_list)); -- cgit v1.2.1 From f290974390c9be76e9166df6f0ab5ad790b812ac Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Thu, 2 Jul 2009 21:34:54 -0700 Subject: Allow the Unix epoch to be a valid commit date It is common practice to use the Unix epoch as a fallback date when a suitable date is not available. This is true of git svn and possibly other importing tools that import non-git history into git. Instead of clobbering established strtoul() error reporting semantics with our own, preserve the strtoul() error value of ULONG_MAX for fsck.c to handle. Signed-off-by: Eric Wong Signed-off-by: Junio C Hamano --- commit.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index aa3b35b6a..a47fb4da2 100644 --- a/commit.c +++ b/commit.c @@ -50,7 +50,6 @@ struct commit *lookup_commit(const unsigned char *sha1) static unsigned long parse_commit_date(const char *buf, const char *tail) { - unsigned long date; const char *dateptr; if (buf + 6 >= tail) @@ -73,10 +72,7 @@ static unsigned long parse_commit_date(const char *buf, const char *tail) if (buf >= tail) return 0; /* dateptr < buf && buf[-1] == '\n', so strtoul will stop at buf-1 */ - date = strtoul(dateptr, NULL, 10); - if (date == ULONG_MAX) - date = 0; - return date; + return strtoul(dateptr, NULL, 10); } static struct commit_graft **commit_graft; -- cgit v1.2.1 From 7f3140cd23f126e578ccaaea8c2cebe36824a7ac Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 23 Jul 2009 17:33:49 +0200 Subject: git repack: keep commits hidden by a graft When you have grafts that pretend that a given commit has different parents than the ones recorded in the commit object, it is dangerous to let 'git repack' remove those hidden parents, as you can easily remove the graft and end up with a broken repository. So let's play it safe and keep those parent objects and everything that is reachable by them, in addition to the grafted parents. As this behavior can only be triggered by git pack-objects, and as that command handles duplicate parents gracefully, we do not bother to cull duplicated parents that may result by using both true and grafted parents. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- commit.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index aa3b35b6a..f69525a08 100644 --- a/commit.c +++ b/commit.c @@ -266,7 +266,11 @@ int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size) bufptr[47] != '\n') return error("bad parents in commit %s", sha1_to_hex(item->object.sha1)); bufptr += 48; - if (graft) + /* + * The clone is shallow if nr_parent < 0, and we must + * not traverse its real parents even when we unhide them. + */ + if (graft && (graft->nr_parent < 0 || grafts_replace_parents)) continue; new_parent = lookup_commit(parent); if (new_parent) -- cgit v1.2.1 From f203d6969b7cfbb56460d5bf8e99b0a7d1abe03b Mon Sep 17 00:00:00 2001 From: Brandon Casey Date: Thu, 27 Aug 2009 11:16:34 -0500 Subject: commit.c: rename variable named 'n' which masks previous declaration The variable named 'n' was initially declared to be of type int. The name 'n' was reused inside inner blocks as a different type. Rename the uses within inner blocks to avoid confusion and give them a slightly more descriptive name. Signed-off-by: Brandon Casey Signed-off-by: Junio C Hamano --- commit.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index e2bcbe814..a6c6f70a9 100644 --- a/commit.c +++ b/commit.c @@ -564,13 +564,13 @@ static struct commit_list *merge_bases_many(struct commit *one, int n, struct co while (interesting(list)) { struct commit *commit; struct commit_list *parents; - struct commit_list *n; + struct commit_list *next; int flags; commit = list->item; - n = list->next; + next = list->next; free(list); - list = n; + list = next; flags = commit->object.flags & (PARENT1 | PARENT2 | STALE); if (flags == (PARENT1 | PARENT2)) { @@ -598,11 +598,11 @@ static struct commit_list *merge_bases_many(struct commit *one, int n, struct co free_commit_list(list); list = result; result = NULL; while (list) { - struct commit_list *n = list->next; + struct commit_list *next = list->next; if (!(list->item->object.flags & STALE)) insert_by_date(list->item, &result); free(list); - list = n; + list = next; } return result; } -- cgit v1.2.1 From 2b7ca830c6a6e4f0e1722e24973a026b5b867227 Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Sat, 12 Sep 2009 10:54:32 +0200 Subject: use write_str_in_full helper to avoid literal string lengths In 2d14d65 (Use a clearer style to issue commands to remote helpers, 2009-09-03) I happened to notice two changes like this: - write_in_full(helper->in, "list\n", 5); + + strbuf_addstr(&buf, "list\n"); + write_in_full(helper->in, buf.buf, buf.len); + strbuf_reset(&buf); IMHO, it would be better to define a new function, static inline ssize_t write_str_in_full(int fd, const char *str) { return write_in_full(fd, str, strlen(str)); } and then use it like this: - strbuf_addstr(&buf, "list\n"); - write_in_full(helper->in, buf.buf, buf.len); - strbuf_reset(&buf); + write_str_in_full(helper->in, "list\n"); Thus not requiring the added allocation, and still avoiding the maintenance risk of literal string lengths. These days, compilers are good enough that strlen("literal") imposes no run-time cost. Transformed via this: perl -pi -e \ 's/write_in_full\((.*?), (".*?"), \d+\)/write_str_in_full($1, $2)/'\ $(git grep -l 'write_in_full.*"') Signed-off-by: Jim Meyering Signed-off-by: Junio C Hamano --- commit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index a6c6f70a9..fedbd5e52 100644 --- a/commit.c +++ b/commit.c @@ -212,7 +212,7 @@ int write_shallow_commits(int fd, int use_pack_protocol) else { if (write_in_full(fd, hex, 40) != 40) break; - if (write_in_full(fd, "\n", 1) != 1) + if (write_str_in_full(fd, "\n") != 1) break; } } -- cgit v1.2.1 From fe0a3cb23c7930be419937825591388465ceb47f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 14 Oct 2009 11:51:03 -0700 Subject: info/grafts: allow trailing whitespaces at the end of line When creating an info/grafts under windows, one typically gets a CRLF file. There is no good reason to forbid trailing CR at the end of the line (for that matter, any trailing whitespaces); the code allowed only LF simply because that was good enough for the platforms with LF line endings. Signed-off-by: Junio C Hamano --- commit.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index aa3b35b6a..eee5ab803 100644 --- a/commit.c +++ b/commit.c @@ -136,8 +136,8 @@ struct commit_graft *read_graft_line(char *buf, int len) int i; struct commit_graft *graft = NULL; - if (buf[len-1] == '\n') - buf[--len] = 0; + while (len && isspace(buf[len-1])) + buf[--len] = '\0'; if (buf[0] == '#' || buf[0] == '\0') return NULL; if ((len + 1) % 41) { -- cgit v1.2.1 From a97a74686d70a318cd802003498054cc1e8b0ae2 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Fri, 9 Oct 2009 12:21:57 +0200 Subject: Introduce commit notes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit notes are blobs which are shown together with the commit message. These blobs are taken from the notes ref, which you can configure by the config variable core.notesRef, which in turn can be overridden by the environment variable GIT_NOTES_REF. The notes ref is a branch which contains "files" whose names are the names of the corresponding commits (i.e. the SHA-1). The rationale for putting this information into a ref is this: we want to be able to fetch and possibly union-merge the notes, maybe even look at the date when a note was introduced, and we want to store them efficiently together with the other objects. This patch has been improved by the following contributions: - Thomas Rast: fix core.notesRef documentation - Tor Arne Vestbø: fix printing of multi-line notes - Alex Riesen: Using char array instead of char pointer costs less BSS - Johan Herland: Plug leak when msg is good, but msglen or type causes return Signed-off-by: Johannes Schindelin Signed-off-by: Thomas Rast Signed-off-by: Tor Arne Vestbø Signed-off-by: Johan Herland Signed-off-by: Junio C Hamano get_commit_notes(): Plug memory leak when 'if' triggers, but not because of read_sha1_file() failure --- commit.c | 1 + 1 file changed, 1 insertion(+) (limited to 'commit.c') diff --git a/commit.c b/commit.c index fedbd5e52..5ade8edf8 100644 --- a/commit.c +++ b/commit.c @@ -5,6 +5,7 @@ #include "utf8.h" #include "diff.h" #include "revision.h" +#include "notes.h" int save_commit_buffer = 1; -- cgit v1.2.1 From edace6f02eeae6f4a06ed1e4f6308703523d8535 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Fri, 30 Oct 2009 17:47:23 -0700 Subject: fetch-pack: Use a strbuf to compose the want list This change is being offered as a refactoring to make later commits in the smart HTTP series easier. By changing the enabled capabilities to be formatted in a strbuf it is easier to add a new capability to the set of supported capabilities. By formatting the want portion of the request into a strbuf and writing it as a whole block we can later decide to hold onto the req_buf (instead of releasing it) to recycle in stateless communications. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- commit.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index fedbd5e52..471efb056 100644 --- a/commit.c +++ b/commit.c @@ -199,7 +199,7 @@ struct commit_graft *lookup_commit_graft(const unsigned char *sha1) return commit_graft[pos]; } -int write_shallow_commits(int fd, int use_pack_protocol) +int write_shallow_commits(struct strbuf *out, int use_pack_protocol) { int i, count = 0; for (i = 0; i < commit_graft_nr; i++) @@ -208,12 +208,10 @@ int write_shallow_commits(int fd, int use_pack_protocol) sha1_to_hex(commit_graft[i]->sha1); count++; if (use_pack_protocol) - packet_write(fd, "shallow %s", hex); + packet_buf_write(out, "shallow %s", hex); else { - if (write_in_full(fd, hex, 40) != 40) - break; - if (write_str_in_full(fd, "\n") != 1) - break; + strbuf_addstr(out, hex); + strbuf_addch(out, '\n'); } } return count; -- cgit v1.2.1 From 65d41d48a4e4e795d64dd7842a44d693b741bf31 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Fri, 29 Jan 2010 05:28:44 -0500 Subject: fix memcpy of overlapping area Caught by valgrind in t5500, but it is pretty obvious from reading the code that this is shifting elements of an array to the left, which needs memmove. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- commit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 6393e1b36..a51d2cd1b 100644 --- a/commit.c +++ b/commit.c @@ -225,7 +225,7 @@ int unregister_shallow(const unsigned char *sha1) if (pos < 0) return -1; if (pos + 1 < commit_graft_nr) - memcpy(commit_graft + pos, commit_graft + pos + 1, + memmove(commit_graft + pos, commit_graft + pos + 1, sizeof(struct commit_graft *) * (commit_graft_nr - pos - 1)); commit_graft_nr--; -- cgit v1.2.1 From 40d52ff77b093fa48f58a168f4b0c4e65b862e56 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Thu, 1 Apr 2010 20:05:23 -0400 Subject: make commit_tree a library function Until now, this has been part of the commit-tree builtin. However, it is already used by other builtins (like commit, merge, and notes), and it would be useful to access it from library code. The check_valid helper has to come along, too, but is given a more library-ish name of "assert_sha1_type". Otherwise, the code is unchanged. There are still a few rough edges for a library function, like printing the utf8 warning to stderr, but we can address those if and when they come up as inappropriate. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- commit.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 731191e63..e9b075096 100644 --- a/commit.c +++ b/commit.c @@ -790,3 +790,58 @@ struct commit_list *reduce_heads(struct commit_list *heads) free(other); return result; } + +static const char commit_utf8_warn[] = +"Warning: commit message does not conform to UTF-8.\n" +"You may want to amend it after fixing the message, or set the config\n" +"variable i18n.commitencoding to the encoding your project uses.\n"; + +int commit_tree(const char *msg, unsigned char *tree, + struct commit_list *parents, unsigned char *ret, + const char *author) +{ + int result; + int encoding_is_utf8; + struct strbuf buffer; + + assert_sha1_type(tree, OBJ_TREE); + + /* Not having i18n.commitencoding is the same as having utf-8 */ + encoding_is_utf8 = is_encoding_utf8(git_commit_encoding); + + strbuf_init(&buffer, 8192); /* should avoid reallocs for the headers */ + strbuf_addf(&buffer, "tree %s\n", sha1_to_hex(tree)); + + /* + * NOTE! This ordering means that the same exact tree merged with a + * different order of parents will be a _different_ changeset even + * if everything else stays the same. + */ + while (parents) { + struct commit_list *next = parents->next; + strbuf_addf(&buffer, "parent %s\n", + sha1_to_hex(parents->item->object.sha1)); + free(parents); + parents = next; + } + + /* Person/date information */ + if (!author) + author = git_author_info(IDENT_ERROR_ON_NO_NAME); + strbuf_addf(&buffer, "author %s\n", author); + strbuf_addf(&buffer, "committer %s\n", git_committer_info(IDENT_ERROR_ON_NO_NAME)); + if (!encoding_is_utf8) + strbuf_addf(&buffer, "encoding %s\n", git_commit_encoding); + strbuf_addch(&buffer, '\n'); + + /* And add the comment */ + strbuf_addstr(&buffer, msg); + + /* And check the encoding */ + if (encoding_is_utf8 && !is_utf8(buffer.buf)) + fprintf(stderr, commit_utf8_warn); + + result = write_sha1_file(buffer.buf, buffer.len, commit_type, ret); + strbuf_release(&buffer); + return result; +} -- cgit v1.2.1 From 11af2aaed657d10dea083f5d5cb7f93bb96a7b70 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Thu, 22 Jul 2010 15:18:30 +0200 Subject: revert: refactor code to find commit subject in find_commit_subject() Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- commit.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 731191e63..fa1a5bac3 100644 --- a/commit.c +++ b/commit.c @@ -315,6 +315,25 @@ int parse_commit(struct commit *item) return ret; } +int find_commit_subject(const char *commit_buffer, const char **subject) +{ + const char *eol; + const char *p = commit_buffer; + + while (*p && (*p != '\n' || p[1] != '\n')) + p++; + if (*p) { + p += 2; + for (eol = p; *eol && *eol != '\n'; eol++) + ; /* do nothing */ + } else + eol = p; + + *subject = p; + + return eol - p; +} + struct commit_list *commit_list_insert(struct commit *item, struct commit_list **list_p) { struct commit_list *new_list = xmalloc(sizeof(struct commit_list)); -- cgit v1.2.1 From a6fa59924d154f2dcfc331357bf553e043aa0242 Mon Sep 17 00:00:00 2001 From: Pat Notz Date: Tue, 2 Nov 2010 13:59:07 -0600 Subject: commit: helper methods to reduce redundant blocks of code * builtin/commit.c: Replace block of code with a one-liner call to logmsg_reencode(). * commit.c: new function for looking up a comit by name * pretty.c: helper methods for getting output encodings Add helpers get_log_output_encoding() and get_commit_output_encoding() that eliminate some messy and duplicate if-blocks. Signed-off-by: Pat Notz Signed-off-by: Junio C Hamano --- commit.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 0094ec1c9..5ed9ccd72 100644 --- a/commit.c +++ b/commit.c @@ -49,6 +49,19 @@ struct commit *lookup_commit(const unsigned char *sha1) return check_commit(obj, sha1, 0); } +struct commit *lookup_commit_reference_by_name(const char *name) +{ + unsigned char sha1[20]; + struct commit *commit; + + if (get_sha1(name, sha1)) + return NULL; + commit = lookup_commit_reference(sha1); + if (!commit || parse_commit(commit)) + return NULL; + return commit; +} + static unsigned long parse_commit_date(const char *buf, const char *tail) { const char *dateptr; -- cgit v1.2.1 From 47e44ed1dc17d3a94ec4bf8dd29810ab7882041c Mon Sep 17 00:00:00 2001 From: Thiago Farina Date: Fri, 26 Nov 2010 23:58:14 -0200 Subject: commit: Add commit_list prefix in two function names. Add commit_list prefix to insert_by_date function and to sort_by_date, so it's clear that these functions refer to commit_list structure. Signed-off-by: Thiago Farina Signed-off-by: Junio C Hamano --- commit.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 0094ec1c9..554dcc3c7 100644 --- a/commit.c +++ b/commit.c @@ -360,7 +360,7 @@ void free_commit_list(struct commit_list *list) } } -struct commit_list * insert_by_date(struct commit *item, struct commit_list **list) +struct commit_list * commit_list_insert_by_date(struct commit *item, struct commit_list **list) { struct commit_list **pp = list; struct commit_list *p; @@ -374,11 +374,11 @@ struct commit_list * insert_by_date(struct commit *item, struct commit_list **li } -void sort_by_date(struct commit_list **list) +void commit_list_sort_by_date(struct commit_list **list) { struct commit_list *ret = NULL; while (*list) { - insert_by_date((*list)->item, &ret); + commit_list_insert_by_date((*list)->item, &ret); *list = (*list)->next; } *list = ret; @@ -398,7 +398,7 @@ struct commit *pop_most_recent_commit(struct commit_list **list, struct commit *commit = parents->item; if (!parse_commit(commit) && !(commit->object.flags & mark)) { commit->object.flags |= mark; - insert_by_date(commit, list); + commit_list_insert_by_date(commit, list); } parents = parents->next; } @@ -487,7 +487,7 @@ void sort_in_topological_order(struct commit_list ** list, int lifo) /* process the list in topological order */ if (!lifo) - sort_by_date(&work); + commit_list_sort_by_date(&work); pptr = list; *list = NULL; @@ -513,7 +513,7 @@ void sort_in_topological_order(struct commit_list ** list, int lifo) */ if (--parent->indegree == 1) { if (!lifo) - insert_by_date(parent, &work); + commit_list_insert_by_date(parent, &work); else commit_list_insert(parent, &work); } @@ -573,10 +573,10 @@ static struct commit_list *merge_bases_many(struct commit *one, int n, struct co } one->object.flags |= PARENT1; - insert_by_date(one, &list); + commit_list_insert_by_date(one, &list); for (i = 0; i < n; i++) { twos[i]->object.flags |= PARENT2; - insert_by_date(twos[i], &list); + commit_list_insert_by_date(twos[i], &list); } while (interesting(list)) { @@ -594,7 +594,7 @@ static struct commit_list *merge_bases_many(struct commit *one, int n, struct co if (flags == (PARENT1 | PARENT2)) { if (!(commit->object.flags & RESULT)) { commit->object.flags |= RESULT; - insert_by_date(commit, &result); + commit_list_insert_by_date(commit, &result); } /* Mark parents of a found merge stale */ flags |= STALE; @@ -608,7 +608,7 @@ static struct commit_list *merge_bases_many(struct commit *one, int n, struct co if (parse_commit(p)) return NULL; p->object.flags |= flags; - insert_by_date(p, &list); + commit_list_insert_by_date(p, &list); } } @@ -618,7 +618,7 @@ static struct commit_list *merge_bases_many(struct commit *one, int n, struct co while (list) { struct commit_list *next = list->next; if (!(list->item->object.flags & STALE)) - insert_by_date(list->item, &result); + commit_list_insert_by_date(list->item, &result); free(list); list = next; } @@ -711,7 +711,7 @@ struct commit_list *get_merge_bases_many(struct commit *one, result = NULL; for (i = 0; i < cnt; i++) { if (rslt[i]) - insert_by_date(rslt[i], &result); + commit_list_insert_by_date(rslt[i], &result); } free(rslt); return result; -- cgit v1.2.1 From df5d43be1f721b0ede37097b815463ceb43b0449 Mon Sep 17 00:00:00 2001 From: Ralf Thielow Date: Wed, 1 Dec 2010 20:15:59 +0100 Subject: commit.c: Remove backward goto in read_craft_line() Bad graft data is noticed in several places in read_graft_line(), and in each case we go back to the first site of detection. It in general is a better style to have an exception handling out of line from the main codepath and make error codepath jump forward. Signed-off-by: Ralf Thielow Reviewed-by: Jonathan Nieder Signed-off-by: Junio C Hamano --- commit.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 0094ec1c9..2d9265d9f 100644 --- a/commit.c +++ b/commit.c @@ -137,12 +137,8 @@ struct commit_graft *read_graft_line(char *buf, int len) buf[--len] = '\0'; if (buf[0] == '#' || buf[0] == '\0') return NULL; - if ((len + 1) % 41) { - bad_graft_data: - error("bad graft data: %s", buf); - free(graft); - return NULL; - } + if ((len + 1) % 41) + goto bad_graft_data; i = (len + 1) / 41 - 1; graft = xmalloc(sizeof(*graft) + 20 * i); graft->nr_parent = i; @@ -155,6 +151,11 @@ struct commit_graft *read_graft_line(char *buf, int len) goto bad_graft_data; } return graft; + +bad_graft_data: + error("bad graft data: %s", buf); + free(graft); + return NULL; } static int read_graft_file(const char *graft_file) -- cgit v1.2.1 From cf7b1cad0e8b024538de975dff5893262cec72d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sat, 5 Feb 2011 17:52:20 +0700 Subject: Add const to parse_{commit,tag}_buffer() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- commit.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 74d660188..ac337c7d7 100644 --- a/commit.c +++ b/commit.c @@ -245,10 +245,10 @@ int unregister_shallow(const unsigned char *sha1) return 0; } -int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size) +int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long size) { - char *tail = buffer; - char *bufptr = buffer; + const char *tail = buffer; + const char *bufptr = buffer; unsigned char parent[20]; struct commit_list **pptr; struct commit_graft *graft; -- cgit v1.2.1 From 09d46644b780ede1a6b757db3e1a1ae9c1128a13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Thu, 18 Aug 2011 19:29:35 +0700 Subject: Add for_each_commit_graft() to iterate all grafts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- commit.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'commit.c') diff --git a/commit.c b/commit.c index ac337c7d7..246cd18b0 100644 --- a/commit.c +++ b/commit.c @@ -214,6 +214,14 @@ struct commit_graft *lookup_commit_graft(const unsigned char *sha1) return commit_graft[pos]; } +int for_each_commit_graft(each_commit_graft_fn fn, void *cb_data) +{ + int i, ret; + for (i = ret = 0; i < commit_graft_nr && !ret; i++) + ret = fn(commit_graft[i], cb_data); + return ret; +} + int write_shallow_commits(struct strbuf *out, int use_pack_protocol) { int i, count = 0; -- cgit v1.2.1 From 294e15fc19b5ba370b703d003920d000a4f5547a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Thu, 18 Aug 2011 19:29:36 +0700 Subject: Move write_shallow_commits to fetch-pack.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This function produces network traffic and should be in fetch-pack. It has been in commit.c because it needs to iterate (private) graft list. It can now do so using for_each_commit_graft(). Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- commit.c | 18 ------------------ 1 file changed, 18 deletions(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index 246cd18b0..f271f590a 100644 --- a/commit.c +++ b/commit.c @@ -222,24 +222,6 @@ int for_each_commit_graft(each_commit_graft_fn fn, void *cb_data) return ret; } -int write_shallow_commits(struct strbuf *out, int use_pack_protocol) -{ - int i, count = 0; - for (i = 0; i < commit_graft_nr; i++) - if (commit_graft[i]->nr_parent < 0) { - const char *hex = - sha1_to_hex(commit_graft[i]->sha1); - count++; - if (use_pack_protocol) - packet_buf_write(out, "shallow %s", hex); - else { - strbuf_addstr(out, hex); - strbuf_addch(out, '\n'); - } - } - return count; -} - int unregister_shallow(const unsigned char *sha1) { int pos = commit_graft_pos(sha1); -- cgit v1.2.1 From cd2b8ae983a277fb3f3c2b2c6747b0a075af1421 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 25 Aug 2011 14:46:52 -0700 Subject: whitespace: have SP on both sides of an assignment "=" I've deliberately excluded the borrowed code in compat/nedmalloc directory. Signed-off-by: Junio C Hamano --- commit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'commit.c') diff --git a/commit.c b/commit.c index ac337c7d7..913dbabd1 100644 --- a/commit.c +++ b/commit.c @@ -515,7 +515,7 @@ void sort_in_topological_order(struct commit_list ** list, int lifo) commit = work_item->item; for (parents = commit->parents; parents ; parents = parents->next) { - struct commit *parent=parents->item; + struct commit *parent = parents->item; if (!parent->indegree) continue; -- cgit v1.2.1 From baf18fc261ca475343fe3cb9cd2c0dded4bc1bb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Sat, 17 Sep 2011 21:57:45 +1000 Subject: Accept tags in HEAD or MERGE_HEAD MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit HEAD and MERGE_HEAD (among other branch tips) should never hold a tag. That can only be caused by broken tools and is cumbersome to fix by an end user with: $ git update-ref HEAD $(git rev-parse HEAD^{commit}) which may look like a magic to a new person. Be easy, warn users (so broken tools can be fixed if they bother to report) and move on. Be robust, if the given SHA-1 cannot be resolved to a commit object, die (therefore return value is always valid). Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- commit.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'commit.c') diff --git a/commit.c b/commit.c index ac337c7d7..50fcf96c2 100644 --- a/commit.c +++ b/commit.c @@ -39,6 +39,18 @@ struct commit *lookup_commit_reference(const unsigned char *sha1) return lookup_commit_reference_gently(sha1, 0); } +struct commit *lookup_commit_or_die(const unsigned char *sha1, const char *ref_name) +{ + struct commit *c = lookup_commit_reference(sha1); + if (!c) + die(_("could not parse %s"), ref_name); + if (hashcmp(sha1, c->object.sha1)) { + warning(_("%s %s is not a commit!"), + ref_name, sha1_to_hex(sha1)); + } + return c; +} + struct commit *lookup_commit(const unsigned char *sha1) { struct object *obj = lookup_object(sha1); -- cgit v1.2.1