2 * Copyright (C) 2005 Junio C Hamano
9 static int matches_pathspec(const char *name, const char **spec, int cnt)
12 int namelen = strlen(name);
13 for (i = 0; i < cnt; i++) {
14 int speclen = strlen(spec[i]);
15 if (! strncmp(spec[i], name, speclen) &&
17 (name[speclen] == 0 ||
18 name[speclen] == '/'))
24 static int detect_rename = 0;
27 * We do not detect circular renames. Just hold created and deleted
28 * entries and later attempt to match them up. If they do not match,
29 * then spit them out as deletes or creates as original.
32 static struct diff_spec_hold {
33 struct diff_spec_hold *next;
34 struct diff_spec_hold *matched;
35 struct diff_spec old, new;
37 } *createdfile, *deletedfile;
39 static void hold_spec(const char *path,
40 struct diff_spec *old, struct diff_spec *new)
42 struct diff_spec_hold **list, *elem;
43 list = (! old->file_valid) ? &createdfile : &deletedfile;
44 elem = xmalloc(sizeof(*elem) + strlen(path));
45 strcpy(elem->path, path);
53 #define MINIMUM_SCORE 7000
54 int estimate_similarity(struct diff_spec *one, struct diff_spec *two)
56 /* Return how similar they are, representing the score as an
57 * integer between 0 and 10000.
59 * This version is very dumb and detects exact matches only.
60 * Wnen Nico's delta stuff gets in, I'll use the delta
61 * algorithm to estimate the similarity score in core.
64 if (one->sha1_valid && two->sha1_valid &&
65 !memcmp(one->blob_sha1, two->blob_sha1, 20))
70 static void flush_renames(const char **spec, int cnt, int reverse)
72 struct diff_spec_hold *rename_src, *rename_dst, *elem;
73 struct diff_spec_hold *leftover = NULL;
74 int score, best_score;
77 rename_dst = createdfile;
78 createdfile = rename_dst->next;
79 best_score = MINIMUM_SCORE;
81 for (elem = deletedfile;
86 score = estimate_similarity(&elem->old,
88 if (best_score < score) {
94 rename_src->matched = rename_dst;
95 rename_dst->matched = rename_src;
98 matches_pathspec(rename_src->path, spec, cnt) ||
99 matches_pathspec(rename_dst->path, spec, cnt)) {
101 run_external_diff(rename_dst->path,
106 run_external_diff(rename_src->path,
113 rename_dst->next = leftover;
114 leftover = rename_dst;
118 /* unmatched deletes */
119 for (elem = deletedfile; elem; elem = elem->next) {
123 matches_pathspec(elem->path, spec, cnt)) {
125 run_external_diff(elem->path, NULL,
126 &elem->new, &elem->old);
128 run_external_diff(elem->path, NULL,
129 &elem->old, &elem->new);
133 /* unmatched creates */
134 for (elem = leftover; elem; elem = elem->next) {
136 matches_pathspec(elem->path, spec, cnt)) {
138 run_external_diff(elem->path, NULL,
139 &elem->new, &elem->old);
141 run_external_diff(elem->path, NULL,
142 &elem->old, &elem->new);
147 static int parse_oneside_change(const char *cp, struct diff_spec *one,
152 one->file_valid = one->sha1_valid = 1;
154 while ((ch = *cp) && '0' <= ch && ch <= '7') {
155 one->mode = (one->mode << 3) | (ch - '0');
159 if (strncmp(cp, "\tblob\t", 6))
162 if (get_sha1_hex(cp, one->blob_sha1))
171 static int parse_diff_raw_output(const char *buf,
172 const char **spec, int cnt, int reverse)
174 struct diff_spec old, new;
176 const char *cp = buf;
181 if (!cnt || matches_pathspec(cp + 1, spec, cnt))
182 diff_unmerge(cp + 1);
186 parse_oneside_change(cp, &new, path);
190 parse_oneside_change(cp, &old, path);
193 old.file_valid = old.sha1_valid =
194 new.file_valid = new.sha1_valid = 1;
195 old.mode = new.mode = 0;
196 while ((ch = *cp) && ('0' <= ch && ch <= '7')) {
197 old.mode = (old.mode << 3) | (ch - '0');
200 if (strncmp(cp, "->", 2))
203 while ((ch = *cp) && ('0' <= ch && ch <= '7')) {
204 new.mode = (new.mode << 3) | (ch - '0');
207 if (strncmp(cp, "\tblob\t", 6))
210 if (get_sha1_hex(cp, old.blob_sha1))
213 if (strncmp(cp, "->", 2))
216 if (get_sha1_hex(cp, new.blob_sha1))
227 if (detect_rename && old.file_valid != new.file_valid) {
229 hold_spec(path, &old, &new);
233 if (!cnt || matches_pathspec(path, spec, cnt)) {
235 run_external_diff(path, NULL, &new, &old);
237 run_external_diff(path, NULL, &old, &new);
242 static const char *diff_helper_usage =
243 "git-diff-helper [-r] [-R] [-z] paths...";
245 int main(int ac, const char **av) {
248 int line_termination = '\n';
252 while (1 < ac && av[1][0] == '-') {
255 else if (av[1][1] == 'z')
256 line_termination = 0;
257 else if (av[1][1] == 'r')
260 usage(diff_helper_usage);
263 /* the remaining parameters are paths patterns */
267 read_line(&sb, stdin, line_termination);
270 status = parse_diff_raw_output(sb.buf, av+1, ac-1, reverse);
272 flush_renames(av+1, ac-1, reverse);
273 printf("%s%c", sb.buf, line_termination);
277 flush_renames(av+1, ac-1, reverse);