#include "revision.h"
#include "notes.h"
#include "gpg-interface.h"
+#include "mergesort.h"
+
+static struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **);
int save_commit_buffer = 1;
unsigned char sha1[20];
struct commit *commit;
- if (get_sha1(name, sha1))
+ if (get_sha1_committish(name, sha1))
return NULL;
commit = lookup_commit_reference(sha1);
if (!commit || parse_commit(commit))
return commit_list_insert(item, pp);
}
+static int commit_list_compare_by_date(const void *a, const void *b)
+{
+ unsigned long a_date = ((const struct commit_list *)a)->item->date;
+ unsigned long b_date = ((const struct commit_list *)b)->item->date;
+ if (a_date < b_date)
+ return 1;
+ if (a_date > b_date)
+ return -1;
+ return 0;
+}
+
+static void *commit_list_get_next(const void *a)
+{
+ return ((const struct commit_list *)a)->next;
+}
+
+static void commit_list_set_next(void *a, void *next)
+{
+ ((struct commit_list *)a)->next = next;
+}
void commit_list_sort_by_date(struct commit_list **list)
{
- struct commit_list *ret = NULL;
- while (*list) {
- commit_list_insert_by_date((*list)->item, &ret);
- *list = (*list)->next;
- }
- *list = ret;
+ *list = llist_mergesort(*list, commit_list_get_next, commit_list_set_next,
+ commit_list_compare_by_date);
}
struct commit *pop_most_recent_commit(struct commit_list **list,
return ret;
}
-void clear_commit_marks(struct commit *commit, unsigned int mark)
+static void clear_commit_marks_1(struct commit_list **plist,
+ struct commit *commit, unsigned int mark)
{
while (commit) {
struct commit_list *parents;
return;
while ((parents = parents->next))
- clear_commit_marks(parents->item, mark);
+ commit_list_insert(parents->item, plist);
commit = commit->parents->item;
}
}
+void clear_commit_marks(struct commit *commit, unsigned int mark)
+{
+ struct commit_list *list = NULL;
+ commit_list_insert(commit, &list);
+ while (list)
+ clear_commit_marks_1(&list, pop_commit(&list), mark);
+}
+
void clear_commit_marks_for_object_array(struct object_array *a, unsigned mark)
{
struct object *object;
return NULL;
}
+/* all input commits in one and twos[] must have been parsed! */
static struct commit_list *paint_down_to_common(struct commit *one, int n, struct commit **twos)
{
struct commit_list *list = NULL;
one->object.flags |= PARENT1;
commit_list_insert_by_date(one, &list);
+ if (!n)
+ return list;
for (i = 0; i < n; i++) {
twos[i]->object.flags |= PARENT2;
commit_list_insert_by_date(twos[i], &list);
redundant = xcalloc(cnt, 1);
filled_index = xmalloc(sizeof(*filled_index) * (cnt - 1));
+ for (i = 0; i < cnt; i++)
+ parse_commit(array[i]);
for (i = 0; i < cnt; i++) {
struct commit_list *common;
{
struct commit_list *p;
struct commit_list *result = NULL, **tail = &result;
- struct commit **other;
- size_t num_head, num_other;
+ struct commit **array;
+ int num_head, i;
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;
-
- /* Do we already have this in the result? */
- for (q = result; q; q = q->next)
- if (p->item == q->item)
- break;
- if (q)
+ /* Uniquify */
+ for (p = heads; p; p = p->next)
+ p->item->object.flags &= ~STALE;
+ for (p = heads, num_head = 0; p; p = p->next) {
+ if (p->item->object.flags & STALE)
continue;
-
- num_other = 0;
- for (q = heads; q; q = q->next) {
- if (p->item == q->item)
- continue;
- other[num_other++] = q->item;
+ p->item->object.flags |= STALE;
+ num_head++;
+ }
+ array = xcalloc(sizeof(*array), num_head);
+ for (p = heads, i = 0; p; p = p->next) {
+ if (p->item->object.flags & STALE) {
+ array[i++] = p->item;
+ p->item->object.flags &= ~STALE;
}
- 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);
+ num_head = remove_redundant(array, num_head);
+ for (i = 0; i < num_head; i++)
+ tail = &commit_list_insert(array[i], tail)->next;
return result;
}
return 0;
}
-struct commit_extra_header *read_commit_extra_header_lines(const char *buffer, size_t size,
- const char **exclude)
+static struct commit_extra_header *read_commit_extra_header_lines(
+ const char *buffer, size_t size,
+ const char **exclude)
{
struct commit_extra_header *extra = NULL, **tail = &extra, *it = NULL;
const char *line, *next, *eof, *eob;
return result;
}
+static int find_invalid_utf8(const char *buf, int len)
+{
+ int offset = 0;
+
+ while (len) {
+ unsigned char c = *buf++;
+ int bytes, bad_offset;
+
+ len--;
+ offset++;
+
+ /* Simple US-ASCII? No worries. */
+ if (c < 0x80)
+ continue;
+
+ bad_offset = offset-1;
+
+ /*
+ * Count how many more high bits set: that's how
+ * many more bytes this sequence should have.
+ */
+ bytes = 0;
+ while (c & 0x40) {
+ c <<= 1;
+ bytes++;
+ }
+
+ /* Must be between 1 and 5 more bytes */
+ if (bytes < 1 || bytes > 5)
+ return bad_offset;
+
+ /* Do we *have* that many bytes? */
+ if (len < bytes)
+ return bad_offset;
+
+ offset += bytes;
+ len -= bytes;
+
+ /* And verify that they are good continuation bytes */
+ do {
+ if ((*buf++ & 0xc0) != 0x80)
+ return bad_offset;
+ } while (--bytes);
+
+ /* We could/should check the value and length here too */
+ }
+ return -1;
+}
+
+/*
+ * This verifies that the buffer is in proper utf8 format.
+ *
+ * If it isn't, it assumes any non-utf8 characters are Latin1,
+ * and does the conversion.
+ *
+ * Fixme: we should probably also disallow overlong forms and
+ * invalid characters. But we don't do that currently.
+ */
+static int verify_utf8(struct strbuf *buf)
+{
+ int ok = 1;
+ long pos = 0;
+
+ for (;;) {
+ int bad;
+ unsigned char c;
+ unsigned char replace[2];
+
+ bad = find_invalid_utf8(buf->buf + pos, buf->len - pos);
+ if (bad < 0)
+ return ok;
+ pos += bad;
+ ok = 0;
+ c = buf->buf[pos];
+ strbuf_remove(buf, pos, 1);
+
+ /* We know 'c' must be in the range 128-255 */
+ replace[0] = 0xc0 + (c >> 6);
+ replace[1] = 0x80 + (c & 0x3f);
+ strbuf_insert(buf, pos, replace, 2);
+ pos += 2;
+ }
+}
+
static const char commit_utf8_warn[] =
-"Warning: commit message does not conform to UTF-8.\n"
+"Warning: commit message did 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";
/* Person/date information */
if (!author)
- author = git_author_info(IDENT_ERROR_ON_NO_NAME);
+ author = git_author_info(IDENT_STRICT);
strbuf_addf(&buffer, "author %s\n", author);
- strbuf_addf(&buffer, "committer %s\n", git_committer_info(IDENT_ERROR_ON_NO_NAME));
+ strbuf_addf(&buffer, "committer %s\n", git_committer_info(IDENT_STRICT));
if (!encoding_is_utf8)
strbuf_addf(&buffer, "encoding %s\n", git_commit_encoding);
strbuf_addbuf(&buffer, msg);
/* And check the encoding */
- if (encoding_is_utf8 && !is_utf8(buffer.buf))
+ if (encoding_is_utf8 && !verify_utf8(&buffer))
fprintf(stderr, commit_utf8_warn);
if (sign_commit && do_sign_commit(&buffer, sign_commit))
}
return commit;
}
+
+/*
+ * Append a commit to the end of the commit_list.
+ *
+ * next starts by pointing to the variable that holds the head of an
+ * empty commit_list, and is updated to point to the "next" field of
+ * the last item on the list as new commits are appended.
+ *
+ * Usage example:
+ *
+ * struct commit_list *list;
+ * struct commit_list **next = &list;
+ *
+ * next = commit_list_append(c1, next);
+ * next = commit_list_append(c2, next);
+ * assert(commit_list_count(list) == 2);
+ * return list;
+ */
+struct commit_list **commit_list_append(struct commit *commit,
+ struct commit_list **next)
+{
+ struct commit_list *new = xmalloc(sizeof(struct commit_list));
+ new->item = commit;
+ *next = new;
+ new->next = NULL;
+ return &new->next;
+}