Git fork
at reftables-rust 292 lines 7.3 kB view raw
1/* 2 * Copyright (C) 2005 Junio C Hamano 3 * Copyright (C) 2010 Google Inc. 4 */ 5 6#define DISABLE_SIGN_COMPARE_WARNINGS 7 8#include "git-compat-util.h" 9#include "diff.h" 10#include "diffcore.h" 11#include "xdiff-interface.h" 12#include "kwset.h" 13#include "oidset.h" 14#include "pretty.h" 15#include "quote.h" 16 17typedef int (*pickaxe_fn)(mmfile_t *one, mmfile_t *two, 18 struct diff_options *o, 19 regex_t *regexp, kwset_t kws); 20 21struct diffgrep_cb { 22 regex_t *regexp; 23 int hit; 24}; 25 26static int diffgrep_consume(void *priv, char *line, unsigned long len) 27{ 28 struct diffgrep_cb *data = priv; 29 regmatch_t regmatch; 30 31 if (line[0] != '+' && line[0] != '-') 32 return 0; 33 if (data->hit) 34 BUG("Already matched in diffgrep_consume! Broken xdiff_emit_line_fn?"); 35 if (!regexec_buf(data->regexp, line + 1, len - 1, 1, 36 &regmatch, 0)) { 37 data->hit = 1; 38 return 1; 39 } 40 return 0; 41} 42 43static int diff_grep(mmfile_t *one, mmfile_t *two, 44 struct diff_options *o, 45 regex_t *regexp, kwset_t kws UNUSED) 46{ 47 struct diffgrep_cb ecbdata; 48 xpparam_t xpp; 49 xdemitconf_t xecfg; 50 int ret; 51 52 /* 53 * We have both sides; need to run textual diff and see if 54 * the pattern appears on added/deleted lines. 55 */ 56 memset(&xpp, 0, sizeof(xpp)); 57 memset(&xecfg, 0, sizeof(xecfg)); 58 ecbdata.regexp = regexp; 59 ecbdata.hit = 0; 60 xecfg.flags = XDL_EMIT_NO_HUNK_HDR; 61 xecfg.ctxlen = o->context; 62 xecfg.interhunkctxlen = o->interhunkcontext; 63 64 /* 65 * An xdiff error might be our "data->hit" from above. See the 66 * comment for xdiff_emit_line_fn in xdiff-interface.h 67 */ 68 ret = xdi_diff_outf(one, two, NULL, diffgrep_consume, 69 &ecbdata, &xpp, &xecfg); 70 if (ecbdata.hit) 71 return 1; 72 if (ret) 73 return ret; 74 return 0; 75} 76 77static unsigned int contains(mmfile_t *mf, regex_t *regexp, kwset_t kws, 78 unsigned int limit) 79{ 80 unsigned int cnt = 0; 81 unsigned long sz = mf->size; 82 const char *data = mf->ptr; 83 84 if (regexp) { 85 regmatch_t regmatch; 86 int flags = 0; 87 88 while (sz && 89 !regexec_buf(regexp, data, sz, 1, &regmatch, flags)) { 90 flags |= REG_NOTBOL; 91 data += regmatch.rm_eo; 92 sz -= regmatch.rm_eo; 93 if (sz && regmatch.rm_so == regmatch.rm_eo) { 94 data++; 95 sz--; 96 } 97 cnt++; 98 99 if (limit && cnt == limit) 100 return cnt; 101 } 102 103 } else { /* Classic exact string match */ 104 while (sz) { 105 struct kwsmatch kwsm; 106 size_t offset = kwsexec(kws, data, sz, &kwsm); 107 if (offset == -1) 108 break; 109 sz -= offset + kwsm.size[0]; 110 data += offset + kwsm.size[0]; 111 cnt++; 112 113 if (limit && cnt == limit) 114 return cnt; 115 } 116 } 117 return cnt; 118} 119 120static int has_changes(mmfile_t *one, mmfile_t *two, 121 struct diff_options *o UNUSED, 122 regex_t *regexp, kwset_t kws) 123{ 124 unsigned int c1 = one ? contains(one, regexp, kws, 0) : 0; 125 unsigned int c2 = two ? contains(two, regexp, kws, c1 + 1) : 0; 126 return c1 != c2; 127} 128 129static int pickaxe_match(struct diff_filepair *p, struct diff_options *o, 130 regex_t *regexp, kwset_t kws, pickaxe_fn fn) 131{ 132 struct userdiff_driver *textconv_one = NULL; 133 struct userdiff_driver *textconv_two = NULL; 134 mmfile_t mf1, mf2; 135 int ret; 136 137 /* ignore unmerged */ 138 if (!DIFF_FILE_VALID(p->one) && !DIFF_FILE_VALID(p->two)) 139 return 0; 140 141 if (o->objfind) { 142 return (DIFF_FILE_VALID(p->one) && 143 oidset_contains(o->objfind, &p->one->oid)) || 144 (DIFF_FILE_VALID(p->two) && 145 oidset_contains(o->objfind, &p->two->oid)); 146 } 147 148 if (o->flags.allow_textconv) { 149 textconv_one = get_textconv(o->repo, p->one); 150 textconv_two = get_textconv(o->repo, p->two); 151 } 152 153 /* 154 * If we have an unmodified pair, we know that the count will be the 155 * same and don't even have to load the blobs. Unless textconv is in 156 * play, _and_ we are using two different textconv filters (e.g., 157 * because a pair is an exact rename with different textconv attributes 158 * for each side, which might generate different content). 159 */ 160 if (textconv_one == textconv_two && diff_unmodified_pair(p)) 161 return 0; 162 163 if ((o->pickaxe_opts & DIFF_PICKAXE_KIND_G) && 164 !o->flags.text && 165 ((!textconv_one && diff_filespec_is_binary(o->repo, p->one)) || 166 (!textconv_two && diff_filespec_is_binary(o->repo, p->two)))) 167 return 0; 168 169 mf1.size = fill_textconv(o->repo, textconv_one, p->one, &mf1.ptr); 170 mf2.size = fill_textconv(o->repo, textconv_two, p->two, &mf2.ptr); 171 172 ret = fn(&mf1, &mf2, o, regexp, kws); 173 174 if (textconv_one) 175 free(mf1.ptr); 176 if (textconv_two) 177 free(mf2.ptr); 178 diff_free_filespec_data(p->one); 179 diff_free_filespec_data(p->two); 180 181 return ret; 182} 183 184static void pickaxe(struct diff_queue_struct *q, struct diff_options *o, 185 regex_t *regexp, kwset_t kws, pickaxe_fn fn) 186{ 187 int i; 188 struct diff_queue_struct outq = DIFF_QUEUE_INIT; 189 190 if (o->pickaxe_opts & DIFF_PICKAXE_ALL) { 191 /* Showing the whole changeset if needle exists */ 192 for (i = 0; i < q->nr; i++) { 193 struct diff_filepair *p = q->queue[i]; 194 if (pickaxe_match(p, o, regexp, kws, fn)) 195 return; /* do not munge the queue */ 196 } 197 198 /* 199 * Otherwise we will clear the whole queue by copying 200 * the empty outq at the end of this function, but 201 * first clear the current entries in the queue. 202 */ 203 for (i = 0; i < q->nr; i++) 204 diff_free_filepair(q->queue[i]); 205 } else { 206 /* Showing only the filepairs that has the needle */ 207 for (i = 0; i < q->nr; i++) { 208 struct diff_filepair *p = q->queue[i]; 209 if (pickaxe_match(p, o, regexp, kws, fn)) 210 diff_q(&outq, p); 211 else 212 diff_free_filepair(p); 213 } 214 } 215 216 free(q->queue); 217 *q = outq; 218} 219 220static void regcomp_or_die(regex_t *regex, const char *needle, int cflags) 221{ 222 int err = regcomp(regex, needle, cflags); 223 if (err) { 224 /* The POSIX.2 people are surely sick */ 225 char errbuf[1024]; 226 regerror(err, regex, errbuf, 1024); 227 die("invalid regex: %s", errbuf); 228 } 229} 230 231void diffcore_pickaxe(struct diff_options *o) 232{ 233 const char *needle = o->pickaxe; 234 int opts = o->pickaxe_opts; 235 regex_t regex, *regexp = NULL; 236 kwset_t kws = NULL; 237 pickaxe_fn fn; 238 239 if (opts & ~DIFF_PICKAXE_KIND_OBJFIND && 240 (!needle || !*needle)) 241 BUG("should have needle under -G or -S"); 242 if (opts & (DIFF_PICKAXE_REGEX | DIFF_PICKAXE_KIND_G)) { 243 int cflags = REG_EXTENDED | REG_NEWLINE; 244 if (o->pickaxe_opts & DIFF_PICKAXE_IGNORE_CASE) 245 cflags |= REG_ICASE; 246 regcomp_or_die(&regex, needle, cflags); 247 regexp = &regex; 248 249 if (opts & DIFF_PICKAXE_KIND_G) 250 fn = diff_grep; 251 else if (opts & DIFF_PICKAXE_REGEX) 252 fn = has_changes; 253 else 254 /* 255 * We don't need to check the combination of 256 * -G and --pickaxe-regex, by the time we get 257 * here diff.c has already died if they're 258 * combined. See the usage tests in 259 * t4209-log-pickaxe.sh. 260 */ 261 BUG("unreachable"); 262 } else if (opts & DIFF_PICKAXE_KIND_S) { 263 if (o->pickaxe_opts & DIFF_PICKAXE_IGNORE_CASE && 264 has_non_ascii(needle)) { 265 struct strbuf sb = STRBUF_INIT; 266 int cflags = REG_NEWLINE | REG_ICASE; 267 268 basic_regex_quote_buf(&sb, needle); 269 regcomp_or_die(&regex, sb.buf, cflags); 270 strbuf_release(&sb); 271 regexp = &regex; 272 } else { 273 kws = kwsalloc(o->pickaxe_opts & DIFF_PICKAXE_IGNORE_CASE 274 ? tolower_trans_tbl : NULL); 275 kwsincr(kws, needle, strlen(needle)); 276 kwsprep(kws); 277 } 278 fn = has_changes; 279 } else if (opts & DIFF_PICKAXE_KIND_OBJFIND) { 280 fn = NULL; 281 } else { 282 BUG("unknown pickaxe_opts flag"); 283 } 284 285 pickaxe(&diff_queued_diff, o, regexp, kws, fn); 286 287 if (regexp) 288 regfree(regexp); 289 if (kws) 290 kwsfree(kws); 291 return; 292}