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