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