]> git.ipfire.org Git - thirdparty/git.git/blame - builtin/patch-id.c
cache.h: remove this no-longer-used header
[thirdparty/git.git] / builtin / patch-id.c
CommitLineData
c2e86add 1#include "builtin.h"
b2141fc1 2#include "config.h"
a8f6855f 3#include "diff.h"
f394e093 4#include "gettext.h"
41771fa4 5#include "hex.h"
2871f4d4 6#include "parse-options.h"
f9767222 7
1a876a69 8static void flush_current_id(int patchlen, struct object_id *id, struct object_id *result)
f9767222 9{
4507ecc7
RS
10 if (patchlen)
11 printf("%s %s\n", oid_to_hex(result), oid_to_hex(id));
f9767222
LT
12}
13
14static int remove_space(char *line)
15{
16 char *src = line;
17 char *dst = line;
18 unsigned char c;
19
20 while ((c = *src++) != '\0') {
21 if (!isspace(c))
22 *dst++ = c;
23 }
24 return dst - line;
25}
26
580fb25b
PB
27static int scan_hunk_header(const char *p, int *p_before, int *p_after)
28{
29 static const char digits[] = "0123456789";
30 const char *q, *r;
31 int n;
32
33 q = p + 4;
34 n = strspn(q, digits);
35 if (q[n] == ',') {
36 q += n + 1;
757e75c8 37 *p_before = atoi(q);
580fb25b 38 n = strspn(q, digits);
757e75c8
JZ
39 } else {
40 *p_before = 1;
580fb25b 41 }
757e75c8 42
580fb25b
PB
43 if (n == 0 || q[n] != ' ' || q[n+1] != '+')
44 return 0;
45
46 r = q + n + 2;
47 n = strspn(r, digits);
48 if (r[n] == ',') {
49 r += n + 1;
757e75c8 50 *p_after = atoi(r);
580fb25b 51 n = strspn(r, digits);
757e75c8
JZ
52 } else {
53 *p_after = 1;
580fb25b
PB
54 }
55 if (n == 0)
56 return 0;
57
580fb25b
PB
58 return 1;
59}
60
1a876a69 61static int get_one_patchid(struct object_id *next_oid, struct object_id *result,
2871f4d4 62 struct strbuf *line_buf, int stable, int verbatim)
f9767222 63{
9ae144fb 64 int patchlen = 0, found_next = 0;
580fb25b 65 int before = -1, after = -1;
0df19eb9
JZ
66 int diff_is_binary = 0;
67 char pre_oid_str[GIT_MAX_HEXSZ + 1], post_oid_str[GIT_MAX_HEXSZ + 1];
36261e42 68 git_hash_ctx ctx;
30e12b92 69
36261e42 70 the_hash_algo->init_fn(&ctx);
1a876a69 71 oidclr(result);
f9767222 72
b9ab810b
MS
73 while (strbuf_getwholeline(line_buf, stdin, '\n') != EOF) {
74 char *line = line_buf->buf;
2bb73ae8 75 const char *p = line;
f9767222
LT
76 int len;
77
0d32ae8d
JZ
78 /* Possibly skip over the prefix added by "log" or "format-patch" */
79 if (!skip_prefix(line, "commit ", &p) &&
2bb73ae8 80 !skip_prefix(line, "From ", &p) &&
2871f4d4
JZ
81 starts_with(line, "\\ ") && 12 < strlen(line)) {
82 if (verbatim)
83 the_hash_algo->update_fn(&ctx, line, strlen(line));
2485eab5 84 continue;
2871f4d4 85 }
f9767222 86
1a876a69 87 if (!get_oid_hex(p, next_oid)) {
9ae144fb
PB
88 found_next = 1;
89 break;
f9767222
LT
90 }
91
92 /* Ignore commit comments */
2bb73ae8 93 if (!patchlen && !starts_with(line, "diff "))
f9767222
LT
94 continue;
95
580fb25b
PB
96 /* Parsing diff header? */
97 if (before == -1) {
0df19eb9
JZ
98 if (starts_with(line, "GIT binary patch") ||
99 starts_with(line, "Binary files")) {
100 diff_is_binary = 1;
101 before = 0;
102 the_hash_algo->update_fn(&ctx, pre_oid_str,
103 strlen(pre_oid_str));
104 the_hash_algo->update_fn(&ctx, post_oid_str,
105 strlen(post_oid_str));
106 if (stable)
107 flush_one_hunk(result, &ctx);
580fb25b 108 continue;
0df19eb9
JZ
109 } else if (skip_prefix(line, "index ", &p)) {
110 char *oid1_end = strstr(line, "..");
111 char *oid2_end = NULL;
112 if (oid1_end)
113 oid2_end = strstr(oid1_end, " ");
114 if (!oid2_end)
115 oid2_end = line + strlen(line) - 1;
116 if (oid1_end != NULL && oid2_end != NULL) {
117 *oid1_end = *oid2_end = '\0';
118 strlcpy(pre_oid_str, p, GIT_MAX_HEXSZ + 1);
119 strlcpy(post_oid_str, oid1_end + 2, GIT_MAX_HEXSZ + 1);
120 }
121 continue;
122 } else if (starts_with(line, "--- "))
580fb25b
PB
123 before = after = 1;
124 else if (!isalpha(line[0]))
125 break;
126 }
9fabdedc 127
0df19eb9
JZ
128 if (diff_is_binary) {
129 if (starts_with(line, "diff ")) {
130 diff_is_binary = 0;
131 before = -1;
132 }
133 continue;
134 }
135
580fb25b
PB
136 /* Looking for a valid hunk header? */
137 if (before == 0 && after == 0) {
2bb73ae8 138 if (starts_with(line, "@@ -")) {
580fb25b
PB
139 /* Parse next hunk, but ignore line numbers. */
140 scan_hunk_header(line, &before, &after);
141 continue;
142 }
143
144 /* Split at the end of the patch. */
2bb73ae8 145 if (!starts_with(line, "diff "))
580fb25b
PB
146 break;
147
148 /* Else we're parsing another header. */
30e12b92
MT
149 if (stable)
150 flush_one_hunk(result, &ctx);
580fb25b
PB
151 before = after = -1;
152 }
153
154 /* If we get here, we're inside a hunk. */
155 if (line[0] == '-' || line[0] == ' ')
156 before--;
157 if (line[0] == '+' || line[0] == ' ')
158 after--;
f9767222 159
2871f4d4
JZ
160 /* Add line to hash algo (possibly removing whitespace) */
161 len = verbatim ? strlen(line) : remove_space(line);
f9767222 162 patchlen += len;
36261e42 163 the_hash_algo->update_fn(&ctx, line, len);
9ae144fb
PB
164 }
165
166 if (!found_next)
1a876a69 167 oidclr(next_oid);
9ae144fb 168
30e12b92
MT
169 flush_one_hunk(result, &ctx);
170
9ae144fb
PB
171 return patchlen;
172}
173
2871f4d4 174static void generate_id_list(int stable, int verbatim)
9ae144fb 175{
1a876a69 176 struct object_id oid, n, result;
9ae144fb 177 int patchlen;
b9ab810b 178 struct strbuf line_buf = STRBUF_INIT;
9ae144fb 179
1a876a69 180 oidclr(&oid);
9ae144fb 181 while (!feof(stdin)) {
2871f4d4 182 patchlen = get_one_patchid(&n, &result, &line_buf, stable, verbatim);
1a876a69 183 flush_current_id(patchlen, &oid, &result);
184 oidcpy(&oid, &n);
f9767222 185 }
b9ab810b 186 strbuf_release(&line_buf);
f9767222
LT
187}
188
2871f4d4
JZ
189static const char *const patch_id_usage[] = {
190 N_("git patch-id [--stable | --unstable | --verbatim]"), NULL
191};
192
193struct patch_id_opts {
194 int stable;
195 int verbatim;
196};
30e12b92
MT
197
198static int git_patch_id_config(const char *var, const char *value, void *cb)
199{
2871f4d4 200 struct patch_id_opts *opts = cb;
30e12b92
MT
201
202 if (!strcmp(var, "patchid.stable")) {
2871f4d4
JZ
203 opts->stable = git_config_bool(var, value);
204 return 0;
205 }
206 if (!strcmp(var, "patchid.verbatim")) {
207 opts->verbatim = git_config_bool(var, value);
30e12b92
MT
208 return 0;
209 }
210
211 return git_default_config(var, value, cb);
212}
f9767222 213
dedc0ec5 214int cmd_patch_id(int argc, const char **argv, const char *prefix)
f9767222 215{
2871f4d4
JZ
216 /* if nothing is set, default to unstable */
217 struct patch_id_opts config = {0, 0};
218 int opts = 0;
219 struct option builtin_patch_id_options[] = {
220 OPT_CMDMODE(0, "unstable", &opts,
221 N_("use the unstable patch-id algorithm"), 1),
222 OPT_CMDMODE(0, "stable", &opts,
223 N_("use the stable patch-id algorithm"), 2),
224 OPT_CMDMODE(0, "verbatim", &opts,
225 N_("don't strip whitespace from the patch"), 3),
226 OPT_END()
227 };
228
229 git_config(git_patch_id_config, &config);
230
231 /* verbatim implies stable */
232 if (config.verbatim)
233 config.stable = 1;
234
235 argc = parse_options(argc, argv, prefix, builtin_patch_id_options,
236 patch_id_usage, 0);
237
238 generate_id_list(opts ? opts > 1 : config.stable,
239 opts ? opts == 3 : config.verbatim);
f9767222 240 return 0;
a6080a0a 241}