diff options
Diffstat (limited to 'commit.c')
| -rw-r--r-- | commit.c | 324 | 
1 files changed, 200 insertions, 124 deletions
| @@ -55,12 +55,12 @@ struct commit *lookup_commit(const unsigned char *sha1)  struct commit *lookup_commit_reference_by_name(const char *name)  { -	unsigned char sha1[20]; +	struct object_id oid;  	struct commit *commit; -	if (get_sha1_committish(name, sha1)) +	if (get_sha1_committish(name, oid.hash))  		return NULL; -	commit = lookup_commit_reference(sha1); +	commit = lookup_commit_reference(oid.hash);  	if (parse_commit(commit))  		return NULL;  	return commit; @@ -99,7 +99,7 @@ static int commit_graft_alloc, commit_graft_nr;  static const unsigned char *commit_graft_sha1_access(size_t index, void *table)  {  	struct commit_graft **commit_graft_table = table; -	return commit_graft_table[index]->sha1; +	return commit_graft_table[index]->oid.hash;  }  static int commit_graft_pos(const unsigned char *sha1) @@ -110,7 +110,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); +	int pos = commit_graft_pos(graft->oid.hash);  	if (0 <= pos) {  		if (ignore_dups) @@ -138,22 +138,23 @@ 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; +	const int entry_size = GIT_SHA1_HEXSZ + 1;  	while (len && isspace(buf[len-1]))  		buf[--len] = '\0';  	if (buf[0] == '#' || buf[0] == '\0')  		return NULL; -	if ((len + 1) % 41) +	if ((len + 1) % entry_size)  		goto bad_graft_data; -	i = (len + 1) / 41 - 1; -	graft = xmalloc(sizeof(*graft) + 20 * i); +	i = (len + 1) / entry_size - 1; +	graft = xmalloc(sizeof(*graft) + GIT_SHA1_RAWSZ * i);  	graft->nr_parent = i; -	if (get_sha1_hex(buf, graft->sha1)) +	if (get_oid_hex(buf, &graft->oid))  		goto bad_graft_data; -	for (i = 40; i < len; i += 41) { +	for (i = GIT_SHA1_HEXSZ; i < len; i += entry_size) {  		if (buf[i] != ' ')  			goto bad_graft_data; -		if (get_sha1_hex(buf + i + 1, graft->parent[i/41])) +		if (get_sha1_hex(buf + i + 1, graft->parent[i/entry_size].hash))  			goto bad_graft_data;  	}  	return graft; @@ -302,39 +303,42 @@ int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long s  {  	const char *tail = buffer;  	const char *bufptr = buffer; -	unsigned char parent[20]; +	struct object_id parent;  	struct commit_list **pptr;  	struct commit_graft *graft; +	const int tree_entry_len = GIT_SHA1_HEXSZ + 5; +	const int parent_entry_len = GIT_SHA1_HEXSZ + 7;  	if (item->object.parsed)  		return 0;  	item->object.parsed = 1;  	tail += size; -	if (tail <= bufptr + 46 || memcmp(bufptr, "tree ", 5) || bufptr[45] != '\n') +	if (tail <= bufptr + tree_entry_len + 1 || memcmp(bufptr, "tree ", 5) || +			bufptr[tree_entry_len] != '\n')  		return error("bogus commit object %s", sha1_to_hex(item->object.sha1)); -	if (get_sha1_hex(bufptr + 5, parent) < 0) +	if (get_sha1_hex(bufptr + 5, parent.hash) < 0)  		return error("bad tree pointer in commit %s",  			     sha1_to_hex(item->object.sha1)); -	item->tree = lookup_tree(parent); -	bufptr += 46; /* "tree " + "hex sha1" + "\n" */ +	item->tree = lookup_tree(parent.hash); +	bufptr += tree_entry_len + 1; /* "tree " + "hex sha1" + "\n" */  	pptr = &item->parents;  	graft = lookup_commit_graft(item->object.sha1); -	while (bufptr + 48 < tail && !memcmp(bufptr, "parent ", 7)) { +	while (bufptr + parent_entry_len < tail && !memcmp(bufptr, "parent ", 7)) {  		struct commit *new_parent; -		if (tail <= bufptr + 48 || -		    get_sha1_hex(bufptr + 7, parent) || -		    bufptr[47] != '\n') +		if (tail <= bufptr + parent_entry_len + 1 || +		    get_sha1_hex(bufptr + 7, parent.hash) || +		    bufptr[parent_entry_len] != '\n')  			return error("bad parents in commit %s", sha1_to_hex(item->object.sha1)); -		bufptr += 48; +		bufptr += parent_entry_len + 1;  		/*  		 * 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); +		new_parent = lookup_commit(parent.hash);  		if (new_parent)  			pptr = &commit_list_insert(new_parent, pptr)->next;  	} @@ -342,7 +346,7 @@ int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long s  		int i;  		struct commit *new_parent;  		for (i = 0; i < graft->nr_parent; i++) { -			new_parent = lookup_commit(graft->parent[i]); +			new_parent = lookup_commit(graft->parent[i].hash);  			if (!new_parent)  				continue;  			pptr = &commit_list_insert(new_parent, pptr)->next; @@ -353,7 +357,7 @@ int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long s  	return 0;  } -int parse_commit(struct commit *item) +int parse_commit_gently(struct commit *item, int quiet_on_missing)  {  	enum object_type type;  	void *buffer; @@ -366,7 +370,8 @@ int parse_commit(struct commit *item)  		return 0;  	buffer = read_sha1_file(item->object.sha1, &type, &size);  	if (!buffer) -		return error("Could not read %s", +		return quiet_on_missing ? -1 : +			error("Could not read %s",  			     sha1_to_hex(item->object.sha1));  	if (type != OBJ_COMMIT) {  		free(buffer); @@ -430,12 +435,7 @@ struct commit_list *copy_commit_list(struct commit_list *list)  	struct commit_list *head = NULL;  	struct commit_list **pp = &head;  	while (list) { -		struct commit_list *new; -		new = xmalloc(sizeof(struct commit_list)); -		new->item = list->item; -		new->next = NULL; -		*pp = new; -		pp = &new->next; +		pp = commit_list_append(list->item, pp);  		list = list->next;  	}  	return head; @@ -589,26 +589,19 @@ define_commit_slab(author_date_slab, unsigned long);  static void record_author_date(struct author_date_slab *author_date,  			       struct commit *commit)  { -	const char *buf, *line_end, *ident_line;  	const char *buffer = get_commit_buffer(commit, NULL);  	struct ident_split ident; +	const char *ident_line; +	size_t ident_len;  	char *date_end;  	unsigned long date; -	for (buf = buffer; buf; buf = line_end + 1) { -		line_end = strchrnul(buf, '\n'); -		ident_line = skip_prefix(buf, "author "); -		if (!ident_line) { -			if (!line_end[0] || line_end[1] == '\n') -				return; /* end of header */ -			continue; -		} -		if (split_ident_line(&ident, -				     ident_line, line_end - ident_line) || -		    !ident.date_begin || !ident.date_end) -			goto fail_exit; /* malformed "author" line */ -		break; -	} +	ident_line = find_commit_header(buffer, "author", &ident_len); +	if (!ident_line) +		goto fail_exit; /* no author line */ +	if (split_ident_line(&ident, ident_line, ident_len) || +	    !ident.date_begin || !ident.date_end) +		goto fail_exit; /* malformed "author" line */  	date = strtoul(ident.date_begin, &date_end, 10);  	if (date_end != ident.date_end) @@ -770,45 +763,41 @@ void sort_in_topological_order(struct commit_list **list, enum rev_sort_order so  static const unsigned all_flags = (PARENT1 | PARENT2 | STALE | RESULT); -static struct commit *interesting(struct commit_list *list) +static int queue_has_nonstale(struct prio_queue *queue)  { -	while (list) { -		struct commit *commit = list->item; -		list = list->next; -		if (commit->object.flags & STALE) -			continue; -		return commit; +	int i; +	for (i = 0; i < queue->nr; i++) { +		struct commit *commit = queue->array[i].data; +		if (!(commit->object.flags & STALE)) +			return 1;  	} -	return NULL; +	return 0;  }  /* 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; +	struct prio_queue queue = { compare_commits_by_commit_date };  	struct commit_list *result = NULL;  	int i;  	one->object.flags |= PARENT1; -	commit_list_insert_by_date(one, &list); -	if (!n) -		return list; +	if (!n) { +		commit_list_append(one, &result); +		return result; +	} +	prio_queue_put(&queue, one); +  	for (i = 0; i < n; i++) {  		twos[i]->object.flags |= PARENT2; -		commit_list_insert_by_date(twos[i], &list); +		prio_queue_put(&queue, twos[i]);  	} -	while (interesting(list)) { -		struct commit *commit; +	while (queue_has_nonstale(&queue)) { +		struct commit *commit = prio_queue_get(&queue);  		struct commit_list *parents; -		struct commit_list *next;  		int flags; -		commit = list->item; -		next = list->next; -		free(list); -		list = next; -  		flags = commit->object.flags & (PARENT1 | PARENT2 | STALE);  		if (flags == (PARENT1 | PARENT2)) {  			if (!(commit->object.flags & RESULT)) { @@ -827,11 +816,11 @@ static struct commit_list *paint_down_to_common(struct commit *one, int n, struc  			if (parse_commit(p))  				return NULL;  			p->object.flags |= flags; -			commit_list_insert_by_date(p, &list); +			prio_queue_put(&queue, p);  		}  	} -	free_commit_list(list); +	clear_prio_queue(&queue);  	return result;  } @@ -883,7 +872,7 @@ struct commit_list *get_octopus_merge_bases(struct commit_list *in)  		for (j = ret; j; j = j->next) {  			struct commit_list *bases; -			bases = get_merge_bases(i->item, j->item, 1); +			bases = get_merge_bases(i->item, j->item);  			if (!new)  				new = bases;  			else @@ -952,10 +941,10 @@ static int remove_redundant(struct commit **array, int cnt)  	return filled;  } -struct commit_list *get_merge_bases_many(struct commit *one, -					 int n, -					 struct commit **twos, -					 int cleanup) +static struct commit_list *get_merge_bases_many_0(struct commit *one, +						  int n, +						  struct commit **twos, +						  int cleanup)  {  	struct commit_list *list;  	struct commit **rslt; @@ -976,12 +965,7 @@ struct commit_list *get_merge_bases_many(struct commit *one,  	}  	/* There are more than one */ -	cnt = 0; -	list = result; -	while (list) { -		list = list->next; -		cnt++; -	} +	cnt = commit_list_count(result);  	rslt = xcalloc(cnt, sizeof(*rslt));  	for (list = result, i = 0; list; list = list->next)  		rslt[i++] = list->item; @@ -998,10 +982,23 @@ struct commit_list *get_merge_bases_many(struct commit *one,  	return result;  } -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)  { -	return get_merge_bases_many(one, 1, &two, cleanup); +	return get_merge_bases_many_0(one, n, twos, 1); +} + +struct commit_list *get_merge_bases_many_dirty(struct commit *one, +					       int n, +					       struct commit **twos) +{ +	return get_merge_bases_many_0(one, n, twos, 0); +} + +struct commit_list *get_merge_bases(struct commit *one, struct commit *two) +{ +	return get_merge_bases_many_0(one, 1, &two, 1);  }  /* @@ -1161,6 +1158,40 @@ int parse_signed_commit(const struct commit *commit,  	return saw_signature;  } +int remove_signature(struct strbuf *buf) +{ +	const char *line = buf->buf; +	const char *tail = buf->buf + buf->len; +	int in_signature = 0; +	const char *sig_start = NULL; +	const char *sig_end = NULL; + +	while (line < tail) { +		const char *next = memchr(line, '\n', tail - line); +		next = next ? next + 1 : tail; + +		if (in_signature && line[0] == ' ') +			sig_end = next; +		else if (starts_with(line, gpg_sig_header) && +			 line[gpg_sig_header_len] == ' ') { +			sig_start = line; +			sig_end = next; +			in_signature = 1; +		} else { +			if (*line == '\n') +				/* dump the whole remainder of the buffer */ +				next = tail; +			in_signature = 0; +		} +		line = next; +	} + +	if (sig_start) +		strbuf_remove(buf, sig_start - buf->buf, sig_end - sig_start); + +	return sig_start != NULL; +} +  static void handle_signed_tag(struct commit *parent, struct commit_extra_header ***tail)  {  	struct merge_remote_desc *desc; @@ -1201,44 +1232,7 @@ free_return:  	free(buf);  } -static struct { -	char result; -	const char *check; -} sigcheck_gpg_status[] = { -	{ 'G', "\n[GNUPG:] GOODSIG " }, -	{ 'B', "\n[GNUPG:] BADSIG " }, -	{ 'U', "\n[GNUPG:] TRUST_NEVER" }, -	{ 'U', "\n[GNUPG:] TRUST_UNDEFINED" }, -}; - -static void parse_gpg_output(struct signature_check *sigc) -{ -	const char *buf = sigc->gpg_status; -	int i; - -	/* Iterate over all search strings */ -	for (i = 0; i < ARRAY_SIZE(sigcheck_gpg_status); i++) { -		const char *found, *next; - -		found = skip_prefix(buf, sigcheck_gpg_status[i].check + 1); -		if (!found) { -			found = strstr(buf, sigcheck_gpg_status[i].check); -			if (!found) -				continue; -			found += strlen(sigcheck_gpg_status[i].check); -		} -		sigc->result = sigcheck_gpg_status[i].result; -		/* The trust messages are not followed by key/signer information */ -		if (sigc->result != 'U') { -			sigc->key = xmemdupz(found, 16); -			found += 17; -			next = strchrnul(found, '\n'); -			sigc->signer = xmemdupz(found, next - found); -		} -	} -} - -void check_commit_signature(const struct commit* commit, struct signature_check *sigc) +void check_commit_signature(const struct commit *commit, struct signature_check *sigc)  {  	struct strbuf payload = STRBUF_INIT;  	struct strbuf signature = STRBUF_INIT; @@ -1255,6 +1249,7 @@ void check_commit_signature(const struct commit* commit, struct signature_check  				      &gpg_output, &gpg_status);  	if (status && !gpg_output.len)  		goto out; +	sigc->payload = strbuf_detach(&payload, NULL);  	sigc->gpg_output = strbuf_detach(&gpg_output, NULL);  	sigc->gpg_status = strbuf_detach(&gpg_status, NULL);  	parse_gpg_output(sigc); @@ -1299,6 +1294,19 @@ struct commit_extra_header *read_commit_extra_headers(struct commit *commit,  	return extra;  } +void for_each_mergetag(each_mergetag_fn fn, struct commit *commit, void *data) +{ +	struct commit_extra_header *extra, *to_free; + +	to_free = read_commit_extra_headers(commit, NULL); +	for (extra = to_free; extra; extra = extra->next) { +		if (strcmp(extra->key, "mergetag")) +			continue; /* not a merge tag */ +		fn(commit, extra, data); +	} +	free_commit_extra_headers(to_free); +} +  static inline int standard_header_field(const char *field, size_t len)  {  	return ((len == 4 && !memcmp(field, "tree ", 5)) || @@ -1577,10 +1585,10 @@ struct commit *get_merge_parent(const char *name)  {  	struct object *obj;  	struct commit *commit; -	unsigned char sha1[20]; -	if (get_sha1(name, sha1)) +	struct object_id oid; +	if (get_sha1(name, oid.hash))  		return NULL; -	obj = parse_object(sha1); +	obj = parse_object(oid.hash);  	commit = (struct commit *)peel_to_type(name, 0, obj, OBJ_COMMIT);  	if (commit && !commit->util) {  		struct merge_remote_desc *desc; @@ -1628,3 +1636,71 @@ void print_commit_list(struct commit_list *list,  		printf(format, sha1_to_hex(list->item->object.sha1));  	}  } + +const char *find_commit_header(const char *msg, const char *key, size_t *out_len) +{ +	int key_len = strlen(key); +	const char *line = msg; + +	while (line) { +		const char *eol = strchrnul(line, '\n'); + +		if (line == eol) +			return NULL; + +		if (eol - line > key_len && +		    !strncmp(line, key, key_len) && +		    line[key_len] == ' ') { +			*out_len = eol - line - key_len - 1; +			return line + key_len + 1; +		} +		line = *eol ? eol + 1 : NULL; +	} +	return NULL; +} + +/* + * Inspect sb and determine the true "end" of the log message, in + * order to find where to put a new Signed-off-by: line.  Ignored are + * trailing comment lines and blank lines, and also the traditional + * "Conflicts:" block that is not commented out, so that we can use + * "git commit -s --amend" on an existing commit that forgot to remove + * it. + * + * Returns the number of bytes from the tail to ignore, to be fed as + * the second parameter to append_signoff(). + */ +int ignore_non_trailer(struct strbuf *sb) +{ +	int boc = 0; +	int bol = 0; +	int in_old_conflicts_block = 0; + +	while (bol < sb->len) { +		char *next_line; + +		if (!(next_line = memchr(sb->buf + bol, '\n', sb->len - bol))) +			next_line = sb->buf + sb->len; +		else +			next_line++; + +		if (sb->buf[bol] == comment_line_char || sb->buf[bol] == '\n') { +			/* is this the first of the run of comments? */ +			if (!boc) +				boc = bol; +			/* otherwise, it is just continuing */ +		} else if (starts_with(sb->buf + bol, "Conflicts:\n")) { +			in_old_conflicts_block = 1; +			if (!boc) +				boc = bol; +		} else if (in_old_conflicts_block && sb->buf[bol] == '\t') { +			; /* a pathname in the conflicts block */ +		} else if (boc) { +			/* the previous was not trailing comment */ +			boc = 0; +			in_old_conflicts_block = 0; +		} +		bol = next_line - sb->buf; +	} +	return boc ? sb->len - boc : 0; +} | 
