Git fork
at reftables-rust 207 lines 5.4 kB view raw
1#include "builtin.h" 2#include "config.h" 3#include "diff.h" 4#include "diffcore.h" 5#include "gettext.h" 6#include "hash.h" 7#include "hex.h" 8#include "object.h" 9#include "parse-options.h" 10#include "revision.h" 11#include "strbuf.h" 12 13static unsigned parse_mode_or_die(const char *mode, const char **end) 14{ 15 uint16_t ret; 16 17 *end = parse_mode(mode, &ret); 18 if (!*end) 19 die(_("unable to parse mode: %s"), mode); 20 return ret; 21} 22 23static void parse_oid_or_die(const char *hex, struct object_id *oid, 24 const char **end, const struct git_hash_algo *algop) 25{ 26 if (parse_oid_hex_algop(hex, oid, end, algop) || *(*end)++ != ' ') 27 die(_("unable to parse object id: %s"), hex); 28} 29 30int cmd_diff_pairs(int argc, const char **argv, const char *prefix, 31 struct repository *repo) 32{ 33 struct strbuf path_dst = STRBUF_INIT; 34 struct strbuf path = STRBUF_INIT; 35 struct strbuf meta = STRBUF_INIT; 36 struct option *parseopts; 37 struct rev_info revs; 38 int line_term = '\0'; 39 int ret; 40 41 const char * const builtin_diff_pairs_usage[] = { 42 N_("git diff-pairs -z [<diff-options>]"), 43 NULL 44 }; 45 struct option builtin_diff_pairs_options[] = { 46 OPT_END() 47 }; 48 49 repo_init_revisions(repo, &revs, prefix); 50 51 /* 52 * Diff options are usually parsed implicitly as part of 53 * setup_revisions(). Explicitly handle parsing to ensure options are 54 * printed in the usage message. 55 */ 56 parseopts = add_diff_options(builtin_diff_pairs_options, &revs.diffopt); 57 show_usage_with_options_if_asked(argc, argv, builtin_diff_pairs_usage, parseopts); 58 59 repo_config(repo, git_diff_basic_config, NULL); 60 revs.diffopt.no_free = 1; 61 revs.disable_stdin = 1; 62 revs.abbrev = 0; 63 revs.diff = 1; 64 65 argc = parse_options(argc, argv, prefix, parseopts, builtin_diff_pairs_usage, 66 PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_DASHDASH); 67 68 if (setup_revisions(argc, argv, &revs, NULL) > 1) 69 usagef(_("unrecognized argument: %s"), argv[0]); 70 71 /* 72 * With the -z option, both command input and raw output are 73 * NUL-delimited (this mode does not affect patch output). At present 74 * only NUL-delimited raw diff formatted input is supported. 75 */ 76 if (revs.diffopt.line_termination) 77 usage(_("working without -z is not supported")); 78 79 if (revs.prune_data.nr) 80 usage(_("pathspec arguments not supported")); 81 82 if (revs.pending.nr || revs.max_count != -1 || 83 revs.min_age != (timestamp_t)-1 || 84 revs.max_age != (timestamp_t)-1) 85 usage(_("revision arguments not allowed")); 86 87 if (!revs.diffopt.output_format) 88 revs.diffopt.output_format = DIFF_FORMAT_PATCH; 89 90 /* 91 * If rename detection is not requested, use rename information from the 92 * raw diff formatted input. Setting skip_resolving_statuses ensures 93 * diffcore_std() does not mess with rename information already present 94 * in queued filepairs. 95 */ 96 if (!revs.diffopt.detect_rename) 97 revs.diffopt.skip_resolving_statuses = 1; 98 99 while (1) { 100 struct object_id oid_a, oid_b; 101 struct diff_filepair *pair; 102 unsigned mode_a, mode_b; 103 const char *p; 104 char status; 105 106 if (strbuf_getwholeline(&meta, stdin, line_term) == EOF) 107 break; 108 109 p = meta.buf; 110 if (!*p) { 111 diffcore_std(&revs.diffopt); 112 diff_flush(&revs.diffopt); 113 /* 114 * When the diff queue is explicitly flushed, append a 115 * NUL byte to separate batches of diffs. 116 */ 117 fputc('\0', revs.diffopt.file); 118 fflush(revs.diffopt.file); 119 continue; 120 } 121 122 if (*p != ':') 123 die(_("invalid raw diff input")); 124 p++; 125 126 mode_a = parse_mode_or_die(p, &p); 127 mode_b = parse_mode_or_die(p, &p); 128 129 if (S_ISDIR(mode_a) || S_ISDIR(mode_b)) 130 die(_("tree objects not supported")); 131 132 parse_oid_or_die(p, &oid_a, &p, repo->hash_algo); 133 parse_oid_or_die(p, &oid_b, &p, repo->hash_algo); 134 135 status = *p++; 136 137 if (strbuf_getwholeline(&path, stdin, line_term) == EOF) 138 die(_("got EOF while reading path")); 139 140 switch (status) { 141 case DIFF_STATUS_ADDED: 142 pair = diff_queue_addremove(&diff_queued_diff, 143 &revs.diffopt, '+', mode_b, 144 &oid_b, 1, path.buf, 0); 145 if (pair) 146 pair->status = status; 147 break; 148 149 case DIFF_STATUS_DELETED: 150 pair = diff_queue_addremove(&diff_queued_diff, 151 &revs.diffopt, '-', mode_a, 152 &oid_a, 1, path.buf, 0); 153 if (pair) 154 pair->status = status; 155 break; 156 157 case DIFF_STATUS_TYPE_CHANGED: 158 case DIFF_STATUS_MODIFIED: 159 pair = diff_queue_change(&diff_queued_diff, &revs.diffopt, 160 mode_a, mode_b, &oid_a, &oid_b, 161 1, 1, path.buf, 0, 0); 162 if (pair) 163 pair->status = status; 164 break; 165 166 case DIFF_STATUS_RENAMED: 167 case DIFF_STATUS_COPIED: { 168 struct diff_filespec *a, *b; 169 unsigned int score; 170 171 if (strbuf_getwholeline(&path_dst, stdin, line_term) == EOF) 172 die(_("got EOF while reading destination path")); 173 174 a = alloc_filespec(path.buf); 175 b = alloc_filespec(path_dst.buf); 176 fill_filespec(a, &oid_a, 1, mode_a); 177 fill_filespec(b, &oid_b, 1, mode_b); 178 179 pair = diff_queue(&diff_queued_diff, a, b); 180 181 if (strtoul_ui(p, 10, &score)) 182 die(_("unable to parse rename/copy score: %s"), p); 183 184 pair->score = score * MAX_SCORE / 100; 185 pair->status = status; 186 pair->renamed_pair = 1; 187 } 188 break; 189 190 default: 191 die(_("unknown diff status: %c"), status); 192 } 193 } 194 195 revs.diffopt.no_free = 0; 196 diffcore_std(&revs.diffopt); 197 diff_flush(&revs.diffopt); 198 ret = diff_result_code(&revs); 199 200 strbuf_release(&path_dst); 201 strbuf_release(&path); 202 strbuf_release(&meta); 203 release_revisions(&revs); 204 FREE_AND_NULL(parseopts); 205 206 return ret; 207}