The eighteenth batch
[alt-git.git] / builtin / patch-id.c
blobd790ae6354b415a1101b8b1de3d3fe5d7bbf27c5
1 #include "builtin.h"
2 #include "config.h"
3 #include "diff.h"
4 #include "gettext.h"
5 #include "hash.h"
6 #include "hex.h"
7 #include "parse-options.h"
8 #include "setup.h"
10 static void flush_current_id(int patchlen, struct object_id *id, struct object_id *result)
12 if (patchlen)
13 printf("%s %s\n", oid_to_hex(result), oid_to_hex(id));
16 static int remove_space(char *line)
18 char *src = line;
19 char *dst = line;
20 unsigned char c;
22 while ((c = *src++) != '\0') {
23 if (!isspace(c))
24 *dst++ = c;
26 return dst - line;
29 static int scan_hunk_header(const char *p, int *p_before, int *p_after)
31 static const char digits[] = "0123456789";
32 const char *q, *r;
33 int n;
35 q = p + 4;
36 n = strspn(q, digits);
37 if (q[n] == ',') {
38 q += n + 1;
39 *p_before = atoi(q);
40 n = strspn(q, digits);
41 } else {
42 *p_before = 1;
45 if (n == 0 || q[n] != ' ' || q[n+1] != '+')
46 return 0;
48 r = q + n + 2;
49 n = strspn(r, digits);
50 if (r[n] == ',') {
51 r += n + 1;
52 *p_after = atoi(r);
53 n = strspn(r, digits);
54 } else {
55 *p_after = 1;
57 if (n == 0)
58 return 0;
60 return 1;
63 static int get_one_patchid(struct object_id *next_oid, struct object_id *result,
64 struct strbuf *line_buf, int stable, int verbatim)
66 int patchlen = 0, found_next = 0;
67 int before = -1, after = -1;
68 int diff_is_binary = 0;
69 char pre_oid_str[GIT_MAX_HEXSZ + 1], post_oid_str[GIT_MAX_HEXSZ + 1];
70 git_hash_ctx ctx;
72 the_hash_algo->init_fn(&ctx);
73 oidclr(result, the_repository->hash_algo);
75 while (strbuf_getwholeline(line_buf, stdin, '\n') != EOF) {
76 char *line = line_buf->buf;
77 const char *p = line;
78 int len;
80 /* Possibly skip over the prefix added by "log" or "format-patch" */
81 if (!skip_prefix(line, "commit ", &p) &&
82 !skip_prefix(line, "From ", &p) &&
83 starts_with(line, "\\ ") && 12 < strlen(line)) {
84 if (verbatim)
85 the_hash_algo->update_fn(&ctx, line, strlen(line));
86 continue;
89 if (!get_oid_hex(p, next_oid)) {
90 found_next = 1;
91 break;
94 /* Ignore commit comments */
95 if (!patchlen && !starts_with(line, "diff "))
96 continue;
98 /* Parsing diff header? */
99 if (before == -1) {
100 if (starts_with(line, "GIT binary patch") ||
101 starts_with(line, "Binary files")) {
102 diff_is_binary = 1;
103 before = 0;
104 the_hash_algo->update_fn(&ctx, pre_oid_str,
105 strlen(pre_oid_str));
106 the_hash_algo->update_fn(&ctx, post_oid_str,
107 strlen(post_oid_str));
108 if (stable)
109 flush_one_hunk(result, &ctx);
110 continue;
111 } else if (skip_prefix(line, "index ", &p)) {
112 char *oid1_end = strstr(line, "..");
113 char *oid2_end = NULL;
114 if (oid1_end)
115 oid2_end = strstr(oid1_end, " ");
116 if (!oid2_end)
117 oid2_end = line + strlen(line) - 1;
118 if (oid1_end != NULL && oid2_end != NULL) {
119 *oid1_end = *oid2_end = '\0';
120 strlcpy(pre_oid_str, p, GIT_MAX_HEXSZ + 1);
121 strlcpy(post_oid_str, oid1_end + 2, GIT_MAX_HEXSZ + 1);
123 continue;
124 } else if (starts_with(line, "--- "))
125 before = after = 1;
126 else if (!isalpha(line[0]))
127 break;
130 if (diff_is_binary) {
131 if (starts_with(line, "diff ")) {
132 diff_is_binary = 0;
133 before = -1;
135 continue;
138 /* Looking for a valid hunk header? */
139 if (before == 0 && after == 0) {
140 if (starts_with(line, "@@ -")) {
141 /* Parse next hunk, but ignore line numbers. */
142 scan_hunk_header(line, &before, &after);
143 continue;
146 /* Split at the end of the patch. */
147 if (!starts_with(line, "diff "))
148 break;
150 /* Else we're parsing another header. */
151 if (stable)
152 flush_one_hunk(result, &ctx);
153 before = after = -1;
156 /* If we get here, we're inside a hunk. */
157 if (line[0] == '-' || line[0] == ' ')
158 before--;
159 if (line[0] == '+' || line[0] == ' ')
160 after--;
162 /* Add line to hash algo (possibly removing whitespace) */
163 len = verbatim ? strlen(line) : remove_space(line);
164 patchlen += len;
165 the_hash_algo->update_fn(&ctx, line, len);
168 if (!found_next)
169 oidclr(next_oid, the_repository->hash_algo);
171 flush_one_hunk(result, &ctx);
173 return patchlen;
176 static void generate_id_list(int stable, int verbatim)
178 struct object_id oid, n, result;
179 int patchlen;
180 struct strbuf line_buf = STRBUF_INIT;
182 oidclr(&oid, the_repository->hash_algo);
183 while (!feof(stdin)) {
184 patchlen = get_one_patchid(&n, &result, &line_buf, stable, verbatim);
185 flush_current_id(patchlen, &oid, &result);
186 oidcpy(&oid, &n);
188 strbuf_release(&line_buf);
191 static const char *const patch_id_usage[] = {
192 N_("git patch-id [--stable | --unstable | --verbatim]"), NULL
195 struct patch_id_opts {
196 int stable;
197 int verbatim;
200 static int git_patch_id_config(const char *var, const char *value,
201 const struct config_context *ctx, void *cb)
203 struct patch_id_opts *opts = cb;
205 if (!strcmp(var, "patchid.stable")) {
206 opts->stable = git_config_bool(var, value);
207 return 0;
209 if (!strcmp(var, "patchid.verbatim")) {
210 opts->verbatim = git_config_bool(var, value);
211 return 0;
214 return git_default_config(var, value, ctx, cb);
217 int cmd_patch_id(int argc, const char **argv, const char *prefix)
219 /* if nothing is set, default to unstable */
220 struct patch_id_opts config = {0, 0};
221 int opts = 0;
222 struct option builtin_patch_id_options[] = {
223 OPT_CMDMODE(0, "unstable", &opts,
224 N_("use the unstable patch-id algorithm"), 1),
225 OPT_CMDMODE(0, "stable", &opts,
226 N_("use the stable patch-id algorithm"), 2),
227 OPT_CMDMODE(0, "verbatim", &opts,
228 N_("don't strip whitespace from the patch"), 3),
229 OPT_END()
232 git_config(git_patch_id_config, &config);
234 /* verbatim implies stable */
235 if (config.verbatim)
236 config.stable = 1;
238 argc = parse_options(argc, argv, prefix, builtin_patch_id_options,
239 patch_id_usage, 0);
242 * We rely on `the_hash_algo` to compute patch IDs. This is dubious as
243 * it means that the hash algorithm now depends on the object hash of
244 * the repository, even though git-patch-id(1) clearly defines that
245 * patch IDs always use SHA1.
247 * NEEDSWORK: This hack should be removed in favor of converting
248 * the code that computes patch IDs to always use SHA1.
250 if (!the_hash_algo)
251 repo_set_hash_algo(the_repository, GIT_HASH_SHA1);
253 generate_id_list(opts ? opts > 1 : config.stable,
254 opts ? opts == 3 : config.verbatim);
255 return 0;