1. Stefan Saasen
  2. git

Commits

Junio C Hamano  committed 45fa760 Merge

Merge branch 'jc/blame'

* jc/blame:
blame -S <ancestry-file>
Match ofs/cnt types in diff interface.
blame: use built-in xdiff
combine-diff: move the code to parse hunk-header into common library.
combine-diff: refactor built-in xdiff interface.
combine-diff: use built-in xdiff.

  • Participants
  • Parent commits d69dc37, 5040f17
  • Branches master

Comments (0)

Files changed (7)

File Makefile

View file
 	quote.o read-cache.o refs.o run-command.o \
 	server-info.o setup.o sha1_file.o sha1_name.o strbuf.o \
 	tag.o tree.o usage.o config.o environment.o ctype.o copy.o \
-	fetch-clone.o revision.o pager.o tree-walk.o \
+	fetch-clone.o revision.o pager.o tree-walk.o xdiff-interface.o \
 	$(DIFF_OBJS)
 
 GITLIBS = $(LIB_FILE) $(XDIFF_LIB)

File blame.c

View file
 #include "diff.h"
 #include "diffcore.h"
 #include "revision.h"
+#include "xdiff-interface.h"
 
 #define DEBUG 0
 
 static int num_commits = 0;
 static int patch_time = 0;
 
-#define TEMPFILE_PATH_LEN 60
-static struct patch *get_patch(struct commit *commit, struct commit *other)
-{
+struct blame_diff_state {
+	struct xdiff_emit_state xm;
 	struct patch *ret;
-	struct util_info *info_c = (struct util_info *)commit->object.util;
-	struct util_info *info_o = (struct util_info *)other->object.util;
-	char tmp_path1[TEMPFILE_PATH_LEN], tmp_path2[TEMPFILE_PATH_LEN];
-	char diff_cmd[TEMPFILE_PATH_LEN*2 + 20];
-	struct timeval tv_start, tv_end;
-	int fd;
-	FILE *fin;
-	char buf[1024];
-
-	ret = xmalloc(sizeof(struct patch));
-	ret->chunks = NULL;
-	ret->num = 0;
-
-	get_blob(commit);
-	get_blob(other);
+};
 
-	gettimeofday(&tv_start, NULL);
+static void process_u0_diff(void *state_, char *line, unsigned long len)
+{
+	struct blame_diff_state *state = state_;
+	struct chunk *chunk;
 
-	fd = git_mkstemp(tmp_path1, TEMPFILE_PATH_LEN, "git-blame-XXXXXX");
-	if (fd < 0)
-		die("unable to create temp-file: %s", strerror(errno));
+	if (len < 4 || line[0] != '@' || line[1] != '@')
+		return;
 
-	if (xwrite(fd, info_c->buf, info_c->size) != info_c->size)
-		die("write failed: %s", strerror(errno));
-	close(fd);
+	if (DEBUG)
+		printf("chunk line: %.*s", (int)len, line);
+	state->ret->num++;
+	state->ret->chunks = xrealloc(state->ret->chunks,
+				      sizeof(struct chunk) * state->ret->num);
+	chunk = &state->ret->chunks[state->ret->num - 1];
+
+	assert(!strncmp(line, "@@ -", 4));
+
+	if (parse_hunk_header(line, len,
+			      &chunk->off1, &chunk->len1,
+			      &chunk->off2, &chunk->len2)) {
+		state->ret->num--;
+		return;
+	}
 
-	fd = git_mkstemp(tmp_path2, TEMPFILE_PATH_LEN, "git-blame-XXXXXX");
-	if (fd < 0)
-		die("unable to create temp-file: %s", strerror(errno));
+	if (chunk->len1 == 0)
+		chunk->off1++;
+	if (chunk->len2 == 0)
+		chunk->off2++;
 
-	if (xwrite(fd, info_o->buf, info_o->size) != info_o->size)
-		die("write failed: %s", strerror(errno));
-	close(fd);
+	if (chunk->off1 > 0)
+		chunk->off1--;
+	if (chunk->off2 > 0)
+		chunk->off2--;
 
-	sprintf(diff_cmd, "diff -U 0 %s %s", tmp_path1, tmp_path2);
-	fin = popen(diff_cmd, "r");
-	if (!fin)
-		die("popen failed: %s", strerror(errno));
+	assert(chunk->off1 >= 0);
+	assert(chunk->off2 >= 0);
+}
 
-	while (fgets(buf, sizeof(buf), fin)) {
-		struct chunk *chunk;
-		char *start, *sp;
+static struct patch *get_patch(struct commit *commit, struct commit *other)
+{
+	struct blame_diff_state state;
+	xpparam_t xpp;
+	xdemitconf_t xecfg;
+	mmfile_t file_c, file_o;
+	xdemitcb_t ecb;
+	struct util_info *info_c = (struct util_info *)commit->object.util;
+	struct util_info *info_o = (struct util_info *)other->object.util;
+	struct timeval tv_start, tv_end;
 
-		if (buf[0] != '@' || buf[1] != '@')
-			continue;
+	get_blob(commit);
+	file_c.ptr = info_c->buf;
+	file_c.size = info_c->size;
 
-		if (DEBUG)
-			printf("chunk line: %s", buf);
-		ret->num++;
-		ret->chunks = xrealloc(ret->chunks,
-				       sizeof(struct chunk) * ret->num);
-		chunk = &ret->chunks[ret->num - 1];
-
-		assert(!strncmp(buf, "@@ -", 4));
-
-		start = buf + 4;
-		sp = index(start, ' ');
-		*sp = '\0';
-		if (index(start, ',')) {
-			int ret =
-			    sscanf(start, "%d,%d", &chunk->off1, &chunk->len1);
-			assert(ret == 2);
-		} else {
-			int ret = sscanf(start, "%d", &chunk->off1);
-			assert(ret == 1);
-			chunk->len1 = 1;
-		}
-		*sp = ' ';
-
-		start = sp + 1;
-		sp = index(start, ' ');
-		*sp = '\0';
-		if (index(start, ',')) {
-			int ret =
-			    sscanf(start, "%d,%d", &chunk->off2, &chunk->len2);
-			assert(ret == 2);
-		} else {
-			int ret = sscanf(start, "%d", &chunk->off2);
-			assert(ret == 1);
-			chunk->len2 = 1;
-		}
-		*sp = ' ';
+	get_blob(other);
+	file_o.ptr = info_o->buf;
+	file_o.size = info_o->size;
 
-		if (chunk->len1 == 0)
-			chunk->off1++;
-		if (chunk->len2 == 0)
-			chunk->off2++;
+	gettimeofday(&tv_start, NULL);
 
-		if (chunk->off1 > 0)
-			chunk->off1--;
-		if (chunk->off2 > 0)
-			chunk->off2--;
+	xpp.flags = XDF_NEED_MINIMAL;
+	xecfg.ctxlen = 0;
+	xecfg.flags = 0;
+	ecb.outf = xdiff_outf;
+	ecb.priv = &state;
+	memset(&state, 0, sizeof(state));
+	state.xm.consume = process_u0_diff;
+	state.ret = xmalloc(sizeof(struct patch));
+	state.ret->chunks = NULL;
+	state.ret->num = 0;
 
-		assert(chunk->off1 >= 0);
-		assert(chunk->off2 >= 0);
-	}
-	pclose(fin);
-	unlink(tmp_path1);
-	unlink(tmp_path2);
+	xdl_diff(&file_c, &file_o, &xpp, &xecfg, &ecb);
 
 	gettimeofday(&tv_end, NULL);
 	patch_time += 1000000 * (tv_end.tv_sec - tv_start.tv_sec) +
 		tv_end.tv_usec - tv_start.tv_usec;
 
 	num_get_patch++;
-	return ret;
+	return state.ret;
 }
 
 static void free_patch(struct patch *p)
 	static char author_buf[1024];
 
 	tmp = strstr(commit->buffer, "\nauthor ") + 8;
-	len = index(tmp, '\n') - tmp;
+	len = strchr(tmp, '\n') - tmp;
 	ret->author = author_buf;
 	memcpy(ret->author, tmp, len);
 
 	return util->topo_data;
 }
 
+static int read_ancestry(const char *graft_file,
+			 unsigned char **start_sha1)
+{
+	FILE *fp = fopen(graft_file, "r");
+	char buf[1024];
+	if (!fp)
+		return -1;
+	while (fgets(buf, sizeof(buf), fp)) {
+		/* The format is just "Commit Parent1 Parent2 ...\n" */
+		int len = strlen(buf);
+		struct commit_graft *graft = read_graft_line(buf, len);
+		register_commit_graft(graft, 0);
+		if (!*start_sha1)
+			*start_sha1 = graft->sha1;
+	}
+	fclose(fp);
+	return 0;
+}
+
 int main(int argc, const char **argv)
 {
 	int i;
 	struct commit *initial = NULL;
-	unsigned char sha1[20];
+	unsigned char sha1[20], *sha1_p = NULL;
 
 	const char *filename = NULL, *commit = NULL;
 	char filename_buf[256];
 				  !strcmp(argv[i], "--compability")) {
 				compability = 1;
 				continue;
+			} else if(!strcmp(argv[i], "-S")) {
+				if (i + 1 < argc &&
+				    !read_ancestry(argv[i + 1], &sha1_p)) {
+					compability = 1;
+					i++;
+					continue;
+				}
+				usage(blame_usage);
 			} else if(!strcmp(argv[i], "--")) {
 				options = 0;
 				continue;
 
 	if(!filename)
 		usage(blame_usage);
-	if(!commit)
+	if (commit && sha1_p)
+		usage(blame_usage);
+	else if(!commit)
 		commit = "HEAD";
 
 	if(prefix)
 		strcpy(filename_buf, filename);
 	filename = filename_buf;
 
-	if (get_sha1(commit, sha1))
-		die("get_sha1 failed, commit '%s' not found", commit);
-	start_commit = lookup_commit_reference(sha1);
+	if (!sha1_p) {
+		if (get_sha1(commit, sha1))
+			die("get_sha1 failed, commit '%s' not found", commit);
+		sha1_p = sha1;
+	}
+	start_commit = lookup_commit_reference(sha1_p);
 	get_util(start_commit)->pathname = filename;
 	if (fill_util_info(start_commit)) {
 		printf("%s not found in %s\n", filename, commit);
 			if(blame_contents[blame_len-1] != '\n')
 				putc('\n', stdout);
 		} else {
-			char* next_buf = index(buf, '\n') + 1;
+			char* next_buf = strchr(buf, '\n') + 1;
 			fwrite(buf, next_buf - buf, 1, stdout);
 			buf = next_buf;
 		}

File combine-diff.c

View file
 #include "diff.h"
 #include "diffcore.h"
 #include "quote.h"
+#include "xdiff-interface.h"
 
 static int uninteresting(struct diff_filepair *p)
 {
 	return blob;
 }
 
-#define TMPPATHLEN 50
-#define MAXLINELEN 10240
-
-static void write_to_temp_file(char *tmpfile, void *blob, unsigned long size)
-{
-	int fd = git_mkstemp(tmpfile, TMPPATHLEN, ".diff_XXXXXX");
-	if (fd < 0)
-		die("unable to create temp-file");
-	if (write(fd, blob, size) != size)
-		die("unable to write temp-file");
-	close(fd);
-}
-
-static void write_temp_blob(char *tmpfile, const unsigned char *sha1)
-{
-	unsigned long size;
-	void *blob;
-	blob = grab_blob(sha1, &size);
-	write_to_temp_file(tmpfile, blob, size);
-	free(blob);
-}
-
-static int parse_num(char **cp_p, unsigned int *num_p)
-{
-	char *cp = *cp_p;
-	unsigned int num = 0;
-	int read_some;
-
-	while ('0' <= *cp && *cp <= '9')
-		num = num * 10 + *cp++ - '0';
-	if (!(read_some = cp - *cp_p))
-		return -1;
-	*cp_p = cp;
-	*num_p = num;
-	return 0;
-}
-
-static int parse_hunk_header(char *line, int len,
-			     unsigned int *ob, unsigned int *on,
-			     unsigned int *nb, unsigned int *nn)
-{
-	char *cp;
-	cp = line + 4;
-	if (parse_num(&cp, ob)) {
-	bad_line:
-		return error("malformed diff output: %s", line);
-	}
-	if (*cp == ',') {
-		cp++;
-		if (parse_num(&cp, on))
-			goto bad_line;
-	}
-	else
-		*on = 1;
-	if (*cp++ != ' ' || *cp++ != '+')
-		goto bad_line;
-	if (parse_num(&cp, nb))
-		goto bad_line;
-	if (*cp == ',') {
-		cp++;
-		if (parse_num(&cp, nn))
-			goto bad_line;
-	}
-	else
-		*nn = 1;
-	return -!!memcmp(cp, " @@", 3);
-}
-
-static void append_lost(struct sline *sline, int n, const char *line)
+static void append_lost(struct sline *sline, int n, const char *line, int len)
 {
 	struct lline *lline;
-	int len = strlen(line);
 	unsigned long this_mask = (1UL<<n);
 	if (line[len-1] == '\n')
 		len--;
 	sline->lost_tail = &lline->next;
 }
 
-static void combine_diff(const unsigned char *parent, const char *ourtmp,
+struct combine_diff_state {
+	struct xdiff_emit_state xm;
+
+	unsigned int lno;
+	int ob, on, nb, nn;
+	unsigned long nmask;
+	int num_parent;
+	int n;
+	struct sline *sline;
+	struct sline *lost_bucket;
+};
+
+static void consume_line(void *state_, char *line, unsigned long len)
+{
+	struct combine_diff_state *state = state_;
+	if (5 < len && !memcmp("@@ -", line, 4)) {
+		if (parse_hunk_header(line, len,
+				      &state->ob, &state->on,
+				      &state->nb, &state->nn))
+			return;
+		state->lno = state->nb;
+		if (!state->nb)
+			/* @@ -1,2 +0,0 @@ to remove the
+			 * first two lines...
+			 */
+			state->nb = 1;
+		if (state->nn == 0)
+			/* @@ -X,Y +N,0 @@ removed Y lines
+			 * that would have come *after* line N
+			 * in the result.  Our lost buckets hang
+			 * to the line after the removed lines,
+			 */
+			state->lost_bucket = &state->sline[state->nb];
+		else
+			state->lost_bucket = &state->sline[state->nb-1];
+		if (!state->sline[state->nb-1].p_lno)
+			state->sline[state->nb-1].p_lno =
+				xcalloc(state->num_parent,
+					sizeof(unsigned long));
+		state->sline[state->nb-1].p_lno[state->n] = state->ob;
+		return;
+	}
+	if (!state->lost_bucket)
+		return; /* not in any hunk yet */
+	switch (line[0]) {
+	case '-':
+		append_lost(state->lost_bucket, state->n, line+1, len-1);
+		break;
+	case '+':
+		state->sline[state->lno-1].flag |= state->nmask;
+		state->lno++;
+		break;
+	}
+}
+
+static void combine_diff(const unsigned char *parent, mmfile_t *result_file,
 			 struct sline *sline, int cnt, int n, int num_parent)
 {
-	FILE *in;
-	char parent_tmp[TMPPATHLEN];
-	char cmd[TMPPATHLEN * 2 + 1024];
-	char line[MAXLINELEN];
-	unsigned int lno, ob, on, nb, nn, p_lno;
+	unsigned int p_lno, lno;
 	unsigned long nmask = (1UL << n);
-	struct sline *lost_bucket = NULL;
+	xpparam_t xpp;
+	xdemitconf_t xecfg;
+	mmfile_t parent_file;
+	xdemitcb_t ecb;
+	struct combine_diff_state state;
+	unsigned long sz;
 
 	if (!cnt)
 		return; /* result deleted */
 
-	write_temp_blob(parent_tmp, parent);
-	sprintf(cmd, "diff --unified=0 -La/x -Lb/x '%s' '%s'",
-		parent_tmp, ourtmp);
-	in = popen(cmd, "r");
-	if (!in)
-		die("cannot spawn %s", cmd);
-
-	lno = 1;
-	while (fgets(line, sizeof(line), in) != NULL) {
-		int len = strlen(line);
-		if (5 < len && !memcmp("@@ -", line, 4)) {
-			if (parse_hunk_header(line, len,
-					      &ob, &on, &nb, &nn))
-				break;
-			lno = nb;
-			if (!nb)
-				/* @@ -1,2 +0,0 @@ to remove the
-				 * first two lines...
-				 */
-				nb = 1;
-			if (nn == 0)
-				/* @@ -X,Y +N,0 @@ removed Y lines
-				 * that would have come *after* line N
-				 * in the result.  Our lost buckets hang
-				 * to the line after the removed lines,
-				 */
-				lost_bucket = &sline[nb];
-			else
-				lost_bucket = &sline[nb-1];
-			if (!sline[nb-1].p_lno)
-				sline[nb-1].p_lno =
-					xcalloc(num_parent,
-						sizeof(unsigned long));
-			sline[nb-1].p_lno[n] = ob;
-			continue;
-		}
-		if (!lost_bucket)
-			continue; /* not in any hunk yet */
-		switch (line[0]) {
-		case '-':
-			append_lost(lost_bucket, n, line+1);
-			break;
-		case '+':
-			sline[lno-1].flag |= nmask;
-			lno++;
-			break;
-		}
-	}
-	fclose(in);
-	unlink(parent_tmp);
+	parent_file.ptr = grab_blob(parent, &sz);
+	parent_file.size = sz;
+	xpp.flags = XDF_NEED_MINIMAL;
+	xecfg.ctxlen = 0;
+	xecfg.flags = 0;
+	ecb.outf = xdiff_outf;
+	ecb.priv = &state;
+	memset(&state, 0, sizeof(state));
+	state.xm.consume = consume_line;
+	state.nmask = nmask;
+	state.sline = sline;
+	state.lno = 1;
+	state.num_parent = num_parent;
+	state.n = n;
+
+	xdl_diff(&parent_file, result_file, &xpp, &xecfg, &ecb);
+	free(parent_file.ptr);
 
 	/* Assign line numbers for this parent.
 	 *
 			   int dense, const char *header,
 			   struct diff_options *opt)
 {
-	unsigned long size, cnt, lno;
+	unsigned long result_size, cnt, lno;
 	char *result, *cp, *ep;
 	struct sline *sline; /* survived lines */
 	int mode_differs = 0;
 	int i, show_hunks, shown_header = 0;
-	char ourtmp_buf[TMPPATHLEN];
-	char *ourtmp = ourtmp_buf;
 	int working_tree_file = !memcmp(elem->sha1, null_sha1, 20);
 	int abbrev = opt->full_index ? 40 : DEFAULT_ABBREV;
+	mmfile_t result_file;
 
 	/* Read the result of merge first */
-	if (!working_tree_file) {
-		result = grab_blob(elem->sha1, &size);
-		write_to_temp_file(ourtmp, result, size);
-	}
+	if (!working_tree_file)
+		result = grab_blob(elem->sha1, &result_size);
 	else {
 		/* Used by diff-tree to read from the working tree */
 		struct stat st;
 		int fd;
-		ourtmp = elem->path;
-		if (0 <= (fd = open(ourtmp, O_RDONLY)) &&
+		if (0 <= (fd = open(elem->path, O_RDONLY)) &&
 		    !fstat(fd, &st)) {
 			int len = st.st_size;
 			int cnt = 0;
 
 			elem->mode = canon_mode(st.st_mode);
-			size = len;
+			result_size = len;
 			result = xmalloc(len + 1);
 			while (cnt < len) {
 				int done = xread(fd, result+cnt, len-cnt);
 				if (done == 0)
 					break;
 				if (done < 0)
-					die("read error '%s'", ourtmp);
+					die("read error '%s'", elem->path);
 				cnt += done;
 			}
 			result[len] = 0;
 		}
 		else {
 			/* deleted file */
-			size = 0;
+			result_size = 0;
 			elem->mode = 0;
 			result = xmalloc(1);
 			result[0] = 0;
-			ourtmp = "/dev/null";
 		}
 		if (0 <= fd)
 			close(fd);
 	}
 
-	for (cnt = 0, cp = result; cp - result < size; cp++) {
+	for (cnt = 0, cp = result; cp - result < result_size; cp++) {
 		if (*cp == '\n')
 			cnt++;
 	}
-	if (size && result[size-1] != '\n')
+	if (result_size && result[result_size-1] != '\n')
 		cnt++; /* incomplete line */
 
 	sline = xcalloc(cnt+1, sizeof(*sline));
 		sline[lno].lost_tail = &sline[lno].lost_head;
 		sline[lno].flag = 0;
 	}
-	for (lno = 0, cp = result; cp - result < size; cp++) {
+	for (lno = 0, cp = result; cp - result < result_size; cp++) {
 		if (*cp == '\n') {
 			sline[lno].len = cp - sline[lno].bol;
 			lno++;
 				sline[lno].bol = cp + 1;
 		}
 	}
-	if (size && result[size-1] != '\n')
-		sline[cnt-1].len = size - (sline[cnt-1].bol - result);
+	if (result_size && result[result_size-1] != '\n')
+		sline[cnt-1].len = result_size - (sline[cnt-1].bol - result);
+
+	result_file.ptr = result;
+	result_file.size = result_size;
 
 	sline[0].p_lno = xcalloc((cnt+1) * num_parent, sizeof(unsigned long));
 	for (lno = 0; lno < cnt; lno++)
 			}
 		}
 		if (i <= j)
-			combine_diff(elem->parent[i].sha1, ourtmp, sline,
+			combine_diff(elem->parent[i].sha1, &result_file, sline,
 				     cnt, i, num_parent);
 		if (elem->parent[i].mode != elem->mode)
 			mode_differs = 1;
 		}
 		dump_sline(sline, cnt, num_parent);
 	}
-	if (ourtmp == ourtmp_buf)
-		unlink(ourtmp);
 	free(result);
 
 	for (i = 0; i < cnt; i++) {

File commit.c

View file
 	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)
 	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;

File commit.h

View file
 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_graft {
+	unsigned char sha1[20];
+	int nr_parent;
+	unsigned char parent[FLEX_ARRAY][20]; /* more */
+};
+
+struct commit_graft *read_graft_line(char *buf, int len);
+int register_commit_graft(struct commit_graft *, int);
+int read_graft_file(const char *graft_file);
+
 #endif /* COMMIT_H */

File xdiff-interface.c

View file
+#include "cache.h"
+#include "xdiff-interface.h"
+
+static int parse_num(char **cp_p, int *num_p)
+{
+	char *cp = *cp_p;
+	int num = 0;
+	int read_some;
+
+	while ('0' <= *cp && *cp <= '9')
+		num = num * 10 + *cp++ - '0';
+	if (!(read_some = cp - *cp_p))
+		return -1;
+	*cp_p = cp;
+	*num_p = num;
+	return 0;
+}
+
+int parse_hunk_header(char *line, int len,
+		      int *ob, int *on,
+		      int *nb, int *nn)
+{
+	char *cp;
+	cp = line + 4;
+	if (parse_num(&cp, ob)) {
+	bad_line:
+		return error("malformed diff output: %s", line);
+	}
+	if (*cp == ',') {
+		cp++;
+		if (parse_num(&cp, on))
+			goto bad_line;
+	}
+	else
+		*on = 1;
+	if (*cp++ != ' ' || *cp++ != '+')
+		goto bad_line;
+	if (parse_num(&cp, nb))
+		goto bad_line;
+	if (*cp == ',') {
+		cp++;
+		if (parse_num(&cp, nn))
+			goto bad_line;
+	}
+	else
+		*nn = 1;
+	return -!!memcmp(cp, " @@", 3);
+}
+
+static void consume_one(void *priv_, char *s, unsigned long size)
+{
+	struct xdiff_emit_state *priv = priv_;
+	char *ep;
+	while (size) {
+		unsigned long this_size;
+		ep = memchr(s, '\n', size);
+		this_size = (ep == NULL) ? size : (ep - s + 1);
+		priv->consume(priv, s, this_size);
+		size -= this_size;
+		s += this_size;
+	}
+}
+
+int xdiff_outf(void *priv_, mmbuffer_t *mb, int nbuf)
+{
+	struct xdiff_emit_state *priv = priv_;
+	int i;
+
+	for (i = 0; i < nbuf; i++) {
+		if (mb[i].ptr[mb[i].size-1] != '\n') {
+			/* Incomplete line */
+			priv->remainder = realloc(priv->remainder,
+						  priv->remainder_size +
+						  mb[i].size);
+			memcpy(priv->remainder + priv->remainder_size,
+			       mb[i].ptr, mb[i].size);
+			priv->remainder_size += mb[i].size;
+			continue;
+		}
+
+		/* we have a complete line */
+		if (!priv->remainder) {
+			consume_one(priv, mb[i].ptr, mb[i].size);
+			continue;
+		}
+		priv->remainder = realloc(priv->remainder,
+					  priv->remainder_size +
+					  mb[i].size);
+		memcpy(priv->remainder + priv->remainder_size,
+		       mb[i].ptr, mb[i].size);
+		consume_one(priv, priv->remainder,
+			    priv->remainder_size + mb[i].size);
+		free(priv->remainder);
+		priv->remainder = NULL;
+		priv->remainder_size = 0;
+	}
+	if (priv->remainder) {
+		consume_one(priv, priv->remainder, priv->remainder_size);
+		free(priv->remainder);
+		priv->remainder = NULL;
+		priv->remainder_size = 0;
+	}
+	return 0;
+}

File xdiff-interface.h

View file
+#ifndef XDIFF_INTERFACE_H
+#define XDIFF_INTERFACE_H
+
+#include "xdiff/xdiff.h"
+
+struct xdiff_emit_state;
+
+typedef void (*xdiff_emit_consume_fn)(void *, char *, unsigned long);
+
+struct xdiff_emit_state {
+	xdiff_emit_consume_fn consume;
+	char *remainder;
+	unsigned long remainder_size;
+};
+
+int xdiff_outf(void *priv_, mmbuffer_t *mb, int nbuf);
+int parse_hunk_header(char *line, int len,
+		      int *ob, int *on,
+		      int *nb, int *nn);
+
+#endif