git

Форк
0
/
diff-tree.c 
247 строк · 6.6 Кб
1
#include "builtin.h"
2
#include "config.h"
3
#include "diff.h"
4
#include "commit.h"
5
#include "gettext.h"
6
#include "hex.h"
7
#include "log-tree.h"
8
#include "read-cache-ll.h"
9
#include "repository.h"
10
#include "revision.h"
11
#include "tmp-objdir.h"
12
#include "tree.h"
13

14
static struct rev_info log_tree_opt;
15

16
static int diff_tree_commit_oid(const struct object_id *oid)
17
{
18
	struct commit *commit = lookup_commit_reference(the_repository, oid);
19
	if (!commit)
20
		return -1;
21
	return log_tree_commit(&log_tree_opt, commit);
22
}
23

24
/* Diff one or more commits. */
25
static int stdin_diff_commit(struct commit *commit, const char *p)
26
{
27
	struct object_id oid;
28
	struct commit_list **pptr = NULL;
29

30
	/* Graft the fake parents locally to the commit */
31
	while (isspace(*p++) && !parse_oid_hex(p, &oid, &p)) {
32
		struct commit *parent = lookup_commit(the_repository, &oid);
33
		if (!pptr) {
34
			/* Free the real parent list */
35
			free_commit_list(commit->parents);
36
			commit->parents = NULL;
37
			pptr = &(commit->parents);
38
		}
39
		if (parent) {
40
			pptr = &commit_list_insert(parent, pptr)->next;
41
		}
42
	}
43
	return log_tree_commit(&log_tree_opt, commit);
44
}
45

46
/* Diff two trees. */
47
static int stdin_diff_trees(struct tree *tree1, const char *p)
48
{
49
	struct object_id oid;
50
	struct tree *tree2;
51
	if (!isspace(*p++) || parse_oid_hex(p, &oid, &p) || *p)
52
		return error("Need exactly two trees, separated by a space");
53
	tree2 = lookup_tree(the_repository, &oid);
54
	if (!tree2 || parse_tree(tree2))
55
		return -1;
56
	printf("%s %s\n", oid_to_hex(&tree1->object.oid),
57
			  oid_to_hex(&tree2->object.oid));
58
	diff_tree_oid(&tree1->object.oid, &tree2->object.oid,
59
		      "", &log_tree_opt.diffopt);
60
	log_tree_diff_flush(&log_tree_opt);
61
	return 0;
62
}
63

64
static int diff_tree_stdin(char *line)
65
{
66
	int len = strlen(line);
67
	struct object_id oid;
68
	struct object *obj;
69
	const char *p;
70

71
	if (!len || line[len-1] != '\n')
72
		return -1;
73
	line[len-1] = 0;
74
	if (parse_oid_hex(line, &oid, &p))
75
		return -1;
76
	obj = parse_object(the_repository, &oid);
77
	if (!obj)
78
		return -1;
79
	if (obj->type == OBJ_COMMIT)
80
		return stdin_diff_commit((struct commit *)obj, p);
81
	if (obj->type == OBJ_TREE)
82
		return stdin_diff_trees((struct tree *)obj, p);
83
	error("Object %s is a %s, not a commit or tree",
84
	      oid_to_hex(&oid), type_name(obj->type));
85
	return -1;
86
}
87

88
static const char diff_tree_usage[] =
89
"git diff-tree [--stdin] [-m] [-s] [-v] [--no-commit-id] [--pretty]\n"
90
"              [-t] [-r] [-c | --cc] [--combined-all-paths] [--root] [--merge-base]\n"
91
"              [<common-diff-options>] <tree-ish> [<tree-ish>] [<path>...]\n"
92
"\n"
93
"  -r            diff recursively\n"
94
"  -c            show combined diff for merge commits\n"
95
"  --cc          show combined diff for merge commits removing uninteresting hunks\n"
96
"  --combined-all-paths\n"
97
"                show name of file in all parents for combined diffs\n"
98
"  --root        include the initial commit as diff against /dev/null\n"
99
COMMON_DIFF_OPTIONS_HELP;
100

101
static void diff_tree_tweak_rev(struct rev_info *rev)
102
{
103
	if (!rev->diffopt.output_format) {
104
		if (rev->dense_combined_merges)
105
			rev->diffopt.output_format = DIFF_FORMAT_PATCH;
106
		else
107
			rev->diffopt.output_format = DIFF_FORMAT_RAW;
108
	}
109
}
110

111
int cmd_diff_tree(int argc, const char **argv, const char *prefix)
112
{
113
	char line[1000];
114
	struct object *tree1, *tree2;
115
	static struct rev_info *opt = &log_tree_opt;
116
	struct setup_revision_opt s_r_opt;
117
	struct userformat_want w;
118
	int read_stdin = 0;
119
	int merge_base = 0;
120

121
	if (argc == 2 && !strcmp(argv[1], "-h"))
122
		usage(diff_tree_usage);
123

124
	git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
125

126
	prepare_repo_settings(the_repository);
127
	the_repository->settings.command_requires_full_index = 0;
128

129
	repo_init_revisions(the_repository, opt, prefix);
130
	if (repo_read_index(the_repository) < 0)
131
		die(_("index file corrupt"));
132
	opt->abbrev = 0;
133
	opt->diff = 1;
134
	opt->disable_stdin = 1;
135
	memset(&s_r_opt, 0, sizeof(s_r_opt));
136
	s_r_opt.tweak = diff_tree_tweak_rev;
137

138
	prefix = precompose_argv_prefix(argc, argv, prefix);
139
	argc = setup_revisions(argc, argv, opt, &s_r_opt);
140

141
	memset(&w, 0, sizeof(w));
142
	userformat_find_requirements(NULL, &w);
143

144
	if (!opt->show_notes_given && w.notes)
145
		opt->show_notes = 1;
146
	if (opt->show_notes)
147
		load_display_notes(&opt->notes_opt);
148

149
	while (--argc > 0) {
150
		const char *arg = *++argv;
151

152
		if (!strcmp(arg, "--stdin")) {
153
			read_stdin = 1;
154
			continue;
155
		}
156
		if (!strcmp(arg, "--merge-base")) {
157
			merge_base = 1;
158
			continue;
159
		}
160
		usage(diff_tree_usage);
161
	}
162

163
	if (read_stdin && merge_base)
164
		die(_("options '%s' and '%s' cannot be used together"), "--stdin", "--merge-base");
165
	if (merge_base && opt->pending.nr != 2)
166
		die(_("--merge-base only works with two commits"));
167

168
	opt->diffopt.rotate_to_strict = 1;
169

170
	if (opt->remerge_diff) {
171
		opt->remerge_objdir = tmp_objdir_create("remerge-diff");
172
		if (!opt->remerge_objdir)
173
			die(_("unable to create temporary object directory"));
174
		tmp_objdir_replace_primary_odb(opt->remerge_objdir, 1);
175
	}
176

177
	/*
178
	 * NOTE!  We expect "a..b" to expand to "^a b" but it is
179
	 * perfectly valid for revision range parser to yield "b ^a",
180
	 * which means the same thing. If we get the latter, i.e. the
181
	 * second one is marked UNINTERESTING, we recover the original
182
	 * order the user gave, i.e. "a..b", by swapping the trees.
183
	 */
184
	switch (opt->pending.nr) {
185
	case 0:
186
		if (!read_stdin)
187
			usage(diff_tree_usage);
188
		break;
189
	case 1:
190
		tree1 = opt->pending.objects[0].item;
191
		diff_tree_commit_oid(&tree1->oid);
192
		break;
193
	case 2:
194
		tree1 = opt->pending.objects[0].item;
195
		tree2 = opt->pending.objects[1].item;
196
		if (merge_base) {
197
			struct object_id oid;
198

199
			diff_get_merge_base(opt, &oid);
200
			tree1 = lookup_object(the_repository, &oid);
201
		} else if (tree2->flags & UNINTERESTING) {
202
			SWAP(tree2, tree1);
203
		}
204
		diff_tree_oid(&tree1->oid, &tree2->oid, "", &opt->diffopt);
205
		log_tree_diff_flush(opt);
206
		break;
207
	}
208

209
	if (read_stdin) {
210
		int saved_nrl = 0;
211
		int saved_dcctc = 0;
212

213
		opt->diffopt.rotate_to_strict = 0;
214
		opt->diffopt.no_free = 1;
215
		if (opt->diffopt.detect_rename) {
216
			if (the_repository->index->cache)
217
				repo_read_index(the_repository);
218
			opt->diffopt.setup |= DIFF_SETUP_USE_SIZE_CACHE;
219
		}
220
		while (fgets(line, sizeof(line), stdin)) {
221
			struct object_id oid;
222

223
			if (get_oid_hex(line, &oid)) {
224
				fputs(line, stdout);
225
				fflush(stdout);
226
			}
227
			else {
228
				diff_tree_stdin(line);
229
				if (saved_nrl < opt->diffopt.needed_rename_limit)
230
					saved_nrl = opt->diffopt.needed_rename_limit;
231
				if (opt->diffopt.degraded_cc_to_c)
232
					saved_dcctc = 1;
233
			}
234
		}
235
		opt->diffopt.degraded_cc_to_c = saved_dcctc;
236
		opt->diffopt.needed_rename_limit = saved_nrl;
237
		opt->diffopt.no_free = 0;
238
		diff_free(&opt->diffopt);
239
	}
240

241
	if (opt->remerge_diff) {
242
		tmp_objdir_destroy(opt->remerge_objdir);
243
		opt->remerge_objdir = NULL;
244
	}
245

246
	return diff_result_code(&opt->diffopt);
247
}
248

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.