 6932c78cb4
			
		
	
	6932c78cb4
	
	
	
		
			
			Marco says it breaks qgit.  This makes the flags a bit more
orthogonal.
  $ git-diff-tree -r --abbrev ca18
    No output from this command because you asked to skip merge by
    not having -m there.
  $ git-diff-tree -r -m --abbrev ca18
  ca182053c7
  :100644 100644 538d21d... 59042d1... M	Makefile
  :100644 100644 410b758... 6c47c3a... M	entry.c
  ca182053c7
  :100644 100644 30479b4... 59042d1... M	Makefile
    The same "independent sets of diff" as before without -c.
  $ git-diff-tree -r -m -c --abbrev ca18
  ca182053c7
  ::100644 100644 100644 538d21d... 30479b4... 59042d1... MM	Makefile
    Combined.
  $ git-diff-tree -r -c --abbrev ca18
  ca182053c7
  ::100644 100644 100644 538d21d... 30479b4... 59042d1... MM	Makefile
    Asking for combined without -m does not make sense, so -c
    implies -m.
We need to supply -c as default to whatchanged, which is a
one-liner.
Signed-off-by: Junio C Hamano <junkio@cox.net>
		
	
		
			
				
	
	
		
			329 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			329 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| #include "cache.h"
 | |
| #include "diff.h"
 | |
| #include "commit.h"
 | |
| 
 | |
| static int show_root_diff = 0;
 | |
| static int no_commit_id = 0;
 | |
| static int verbose_header = 0;
 | |
| static int ignore_merges = 1;
 | |
| static int combine_merges = 0;
 | |
| static int dense_combined_merges = 0;
 | |
| static int read_stdin = 0;
 | |
| static int always_show_header = 0;
 | |
| 
 | |
| static const char *header = NULL;
 | |
| static const char *header_prefix = "";
 | |
| static enum cmit_fmt commit_format = CMIT_FMT_RAW;
 | |
| 
 | |
| static struct diff_options diff_options;
 | |
| 
 | |
| static int call_diff_flush(void)
 | |
| {
 | |
| 	diffcore_std(&diff_options);
 | |
| 	if (diff_queue_is_empty()) {
 | |
| 		int saved_fmt = diff_options.output_format;
 | |
| 		diff_options.output_format = DIFF_FORMAT_NO_OUTPUT;
 | |
| 		diff_flush(&diff_options);
 | |
| 		diff_options.output_format = saved_fmt;
 | |
| 		return 0;
 | |
| 	}
 | |
| 	if (header) {
 | |
| 		if (!no_commit_id)
 | |
| 			printf("%s%c", header, diff_options.line_termination);
 | |
| 		header = NULL;
 | |
| 	}
 | |
| 	diff_flush(&diff_options);
 | |
| 	return 1;
 | |
| }
 | |
| 
 | |
| static int diff_tree_sha1_top(const unsigned char *old,
 | |
| 			      const unsigned char *new, const char *base)
 | |
| {
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = diff_tree_sha1(old, new, base, &diff_options);
 | |
| 	call_diff_flush();
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int diff_root_tree(const unsigned char *new, const char *base)
 | |
| {
 | |
| 	int retval;
 | |
| 	void *tree;
 | |
| 	struct tree_desc empty, real;
 | |
| 
 | |
| 	tree = read_object_with_reference(new, "tree", &real.size, NULL);
 | |
| 	if (!tree)
 | |
| 		die("unable to read root tree (%s)", sha1_to_hex(new));
 | |
| 	real.buf = tree;
 | |
| 
 | |
| 	empty.buf = "";
 | |
| 	empty.size = 0;
 | |
| 	retval = diff_tree(&empty, &real, base, &diff_options);
 | |
| 	free(tree);
 | |
| 	call_diff_flush();
 | |
| 	return retval;
 | |
| }
 | |
| 
 | |
| static const char *generate_header(const unsigned char *commit_sha1,
 | |
| 				   const unsigned char *parent_sha1,
 | |
| 				   const struct commit *commit)
 | |
| {
 | |
| 	static char this_header[16384];
 | |
| 	int offset;
 | |
| 	unsigned long len;
 | |
| 	int abbrev = diff_options.abbrev;
 | |
| 	const char *msg = commit->buffer;
 | |
| 
 | |
| 	if (!verbose_header)
 | |
| 		return sha1_to_hex(commit_sha1);
 | |
| 
 | |
| 	len = strlen(msg);
 | |
| 
 | |
| 	offset = sprintf(this_header, "%s%s ",
 | |
| 			 header_prefix,
 | |
| 			 diff_unique_abbrev(commit_sha1, abbrev));
 | |
| 	if (commit_sha1 != parent_sha1)
 | |
| 		offset += sprintf(this_header + offset, "(from %s)\n",
 | |
| 				  parent_sha1
 | |
| 				  ? diff_unique_abbrev(parent_sha1, abbrev)
 | |
| 				  : "root");
 | |
| 	else
 | |
| 		offset += sprintf(this_header + offset, "(from parents)\n");
 | |
| 	offset += pretty_print_commit(commit_format, commit, len,
 | |
| 				      this_header + offset,
 | |
| 				      sizeof(this_header) - offset, abbrev);
 | |
| 	if (always_show_header) {
 | |
| 		puts(this_header);
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	return this_header;
 | |
| }
 | |
| 
 | |
| static int diff_tree_commit(struct commit *commit)
 | |
| {
 | |
| 	struct commit_list *parents;
 | |
| 	unsigned const char *sha1 = commit->object.sha1;
 | |
| 
 | |
| 	/* Root commit? */
 | |
| 	if (show_root_diff && !commit->parents) {
 | |
| 		header = generate_header(sha1, NULL, commit);
 | |
| 		diff_root_tree(sha1, "");
 | |
| 	}
 | |
| 
 | |
| 	/* More than one parent? */
 | |
| 	if (commit->parents && commit->parents->next) {
 | |
| 		if (ignore_merges)
 | |
| 			return 0;
 | |
| 		else if (combine_merges) {
 | |
| 			header = generate_header(sha1, sha1, commit);
 | |
| 			header = diff_tree_combined_merge(sha1, header,
 | |
| 							dense_combined_merges,
 | |
| 							&diff_options);
 | |
| 			if (!header && verbose_header)
 | |
| 				header_prefix = "\ndiff-tree ";
 | |
| 			return 0;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	for (parents = commit->parents; parents; parents = parents->next) {
 | |
| 		struct commit *parent = parents->item;
 | |
| 		header = generate_header(sha1, parent->object.sha1, commit);
 | |
| 		diff_tree_sha1_top(parent->object.sha1, sha1, "");
 | |
| 		if (!header && verbose_header) {
 | |
| 			header_prefix = "\ndiff-tree ";
 | |
| 			/*
 | |
| 			 * Don't print multiple merge entries if we
 | |
| 			 * don't print the diffs.
 | |
| 			 */
 | |
| 		}
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int diff_tree_commit_sha1(const unsigned char *sha1)
 | |
| {
 | |
| 	struct commit *commit = lookup_commit_reference(sha1);
 | |
| 	if (!commit)
 | |
| 		return -1;
 | |
| 	return diff_tree_commit(commit);
 | |
| }
 | |
| 
 | |
| static int diff_tree_stdin(char *line)
 | |
| {
 | |
| 	int len = strlen(line);
 | |
| 	unsigned char sha1[20];
 | |
| 	struct commit *commit;
 | |
| 
 | |
| 	if (!len || line[len-1] != '\n')
 | |
| 		return -1;
 | |
| 	line[len-1] = 0;
 | |
| 	if (get_sha1_hex(line, sha1))
 | |
| 		return -1;
 | |
| 	commit = lookup_commit(sha1);
 | |
| 	if (!commit || parse_commit(commit))
 | |
| 		return -1;
 | |
| 	if (isspace(line[40]) && !get_sha1_hex(line+41, sha1)) {
 | |
| 		/* Graft the fake parents locally to the commit */
 | |
| 		int pos = 41;
 | |
| 		struct commit_list **pptr, *parents;
 | |
| 
 | |
| 		/* Free the real parent list */
 | |
| 		for (parents = commit->parents; parents; ) {
 | |
| 			struct commit_list *tmp = parents->next;
 | |
| 			free(parents);
 | |
| 			parents = tmp;
 | |
| 		}
 | |
| 		commit->parents = NULL;
 | |
| 		pptr = &(commit->parents);
 | |
| 		while (line[pos] && !get_sha1_hex(line + pos, sha1)) {
 | |
| 			struct commit *parent = lookup_commit(sha1);
 | |
| 			if (parent) {
 | |
| 				pptr = &commit_list_insert(parent, pptr)->next;
 | |
| 			}
 | |
| 			pos += 41;
 | |
| 		}
 | |
| 	}
 | |
| 	return diff_tree_commit(commit);
 | |
| }
 | |
| 
 | |
| static const char diff_tree_usage[] =
 | |
| "git-diff-tree [--stdin] [-m] [-c] [--cc] [-s] [-v] [--pretty] [-t] [-r] [--root] "
 | |
| "[<common diff options>] <tree-ish> [<tree-ish>] [<path>...]\n"
 | |
| "  -r            diff recursively\n"
 | |
| "  --root        include the initial commit as diff against /dev/null\n"
 | |
| COMMON_DIFF_OPTIONS_HELP;
 | |
| 
 | |
| int main(int argc, const char **argv)
 | |
| {
 | |
| 	int nr_sha1;
 | |
| 	char line[1000];
 | |
| 	unsigned char sha1[2][20];
 | |
| 	const char *prefix = setup_git_directory();
 | |
| 
 | |
| 	git_config(git_diff_config);
 | |
| 	nr_sha1 = 0;
 | |
| 	diff_setup(&diff_options);
 | |
| 
 | |
| 	for (;;) {
 | |
| 		int diff_opt_cnt;
 | |
| 		const char *arg;
 | |
| 
 | |
| 		argv++;
 | |
| 		argc--;
 | |
| 		arg = *argv;
 | |
| 		if (!arg)
 | |
| 			break;
 | |
| 
 | |
| 		if (*arg != '-') {
 | |
| 			if (nr_sha1 < 2 && !get_sha1(arg, sha1[nr_sha1])) {
 | |
| 				nr_sha1++;
 | |
| 				continue;
 | |
| 			}
 | |
| 			break;
 | |
| 		}
 | |
| 
 | |
| 		diff_opt_cnt = diff_opt_parse(&diff_options, argv, argc);
 | |
| 		if (diff_opt_cnt < 0)
 | |
| 			usage(diff_tree_usage);
 | |
| 		else if (diff_opt_cnt) {
 | |
| 			argv += diff_opt_cnt - 1;
 | |
| 			argc -= diff_opt_cnt - 1;
 | |
| 			continue;
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 		if (!strcmp(arg, "--")) {
 | |
| 			argv++;
 | |
| 			argc--;
 | |
| 			break;
 | |
| 		}
 | |
| 		if (!strcmp(arg, "-r")) {
 | |
| 			diff_options.recursive = 1;
 | |
| 			continue;
 | |
| 		}
 | |
| 		if (!strcmp(arg, "-t")) {
 | |
| 			diff_options.recursive = 1;
 | |
| 			diff_options.tree_in_recursive = 1;
 | |
| 			continue;
 | |
| 		}
 | |
| 		if (!strcmp(arg, "-m")) {
 | |
| 			ignore_merges = 0;
 | |
| 			continue;
 | |
| 		}
 | |
| 		if (!strcmp(arg, "-c")) {
 | |
| 			combine_merges = 1;
 | |
| 			continue;
 | |
| 		}
 | |
| 		if (!strcmp(arg, "--cc")) {
 | |
| 			dense_combined_merges = combine_merges = 1;
 | |
| 			continue;
 | |
| 		}
 | |
| 		if (!strcmp(arg, "-v")) {
 | |
| 			verbose_header = 1;
 | |
| 			header_prefix = "diff-tree ";
 | |
| 			continue;
 | |
| 		}
 | |
| 		if (!strncmp(arg, "--pretty", 8)) {
 | |
| 			verbose_header = 1;
 | |
| 			header_prefix = "diff-tree ";
 | |
| 			commit_format = get_commit_format(arg+8);
 | |
| 			continue;
 | |
| 		}
 | |
| 		if (!strcmp(arg, "--stdin")) {
 | |
| 			read_stdin = 1;
 | |
| 			continue;
 | |
| 		}
 | |
| 		if (!strcmp(arg, "--root")) {
 | |
| 			show_root_diff = 1;
 | |
| 			continue;
 | |
| 		}
 | |
| 		if (!strcmp(arg, "--no-commit-id")) {
 | |
| 			no_commit_id = 1;
 | |
| 			continue;
 | |
| 		}
 | |
| 		if (!strcmp(arg, "--always")) {
 | |
| 			always_show_header = 1;
 | |
| 			continue;
 | |
| 		}
 | |
| 		usage(diff_tree_usage);
 | |
| 	}
 | |
| 
 | |
| 	if (combine_merges)
 | |
| 		ignore_merges = 0;
 | |
| 
 | |
| 	/* We can only do dense combined merges with diff output */
 | |
| 	if (dense_combined_merges)
 | |
| 		diff_options.output_format = DIFF_FORMAT_PATCH;
 | |
| 
 | |
| 	if (diff_options.output_format == DIFF_FORMAT_PATCH)
 | |
| 		diff_options.recursive = 1;
 | |
| 
 | |
| 	diff_tree_setup_paths(get_pathspec(prefix, argv));
 | |
| 	diff_setup_done(&diff_options);
 | |
| 
 | |
| 	switch (nr_sha1) {
 | |
| 	case 0:
 | |
| 		if (!read_stdin)
 | |
| 			usage(diff_tree_usage);
 | |
| 		break;
 | |
| 	case 1:
 | |
| 		diff_tree_commit_sha1(sha1[0]);
 | |
| 		break;
 | |
| 	case 2:
 | |
| 		diff_tree_sha1_top(sha1[0], sha1[1], "");
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	if (!read_stdin)
 | |
| 		return 0;
 | |
| 
 | |
| 	if (diff_options.detect_rename)
 | |
| 		diff_options.setup |= (DIFF_SETUP_USE_SIZE_CACHE |
 | |
| 				       DIFF_SETUP_USE_CACHE);
 | |
| 	while (fgets(line, sizeof(line), stdin))
 | |
| 		diff_tree_stdin(line);
 | |
| 
 | |
| 	return 0;
 | |
| }
 |