diffcore-pickaxe.c 6.81 KB
Newer Older
1 2
/*
 * Copyright (C) 2005 Junio C Hamano
3
 * Copyright (C) 2010 Google Inc.
4 5 6 7
 */
#include "cache.h"
#include "diff.h"
#include "diffcore.h"
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
#include "xdiff-interface.h"

struct diffgrep_cb {
	regex_t *regexp;
	int hit;
};

static void diffgrep_consume(void *priv, char *line, unsigned long len)
{
	struct diffgrep_cb *data = priv;
	regmatch_t regmatch;
	int hold;

	if (line[0] != '+' && line[0] != '-')
		return;
	if (data->hit)
		/*
		 * NEEDSWORK: we should have a way to terminate the
		 * caller early.
		 */
		return;
	/* Yuck -- line ought to be "const char *"! */
	hold = line[len];
	line[len] = '\0';
	data->hit = !regexec(data->regexp, line + 1, 1, &regmatch, 0);
	line[len] = hold;
}

static void fill_one(struct diff_filespec *one,
		     mmfile_t *mf, struct userdiff_driver **textconv)
{
	if (DIFF_FILE_VALID(one)) {
		*textconv = get_textconv(one);
		mf->size = fill_textconv(*textconv, one, &mf->ptr);
	} else {
		memset(mf, 0, sizeof(*mf));
	}
}

static int diff_grep(struct diff_filepair *p, regex_t *regexp, struct diff_options *o)
{
	regmatch_t regmatch;
	struct userdiff_driver *textconv_one = NULL;
	struct userdiff_driver *textconv_two = NULL;
	mmfile_t mf1, mf2;
	int hit;

	if (diff_unmodified_pair(p))
		return 0;

	fill_one(p->one, &mf1, &textconv_one);
	fill_one(p->two, &mf2, &textconv_two);

	if (!mf1.ptr) {
		if (!mf2.ptr)
			return 0; /* ignore unmerged */
		/* created "two" -- does it have what we are looking for? */
		hit = !regexec(regexp, p->two->data, 1, &regmatch, 0);
	} else if (!mf2.ptr) {
		/* removed "one" -- did it have what we are looking for? */
		hit = !regexec(regexp, p->one->data, 1, &regmatch, 0);
	} else {
		/*
		 * We have both sides; need to run textual diff and see if
		 * the pattern appears on added/deleted lines.
		 */
		struct diffgrep_cb ecbdata;
		xpparam_t xpp;
		xdemitconf_t xecfg;

		memset(&xpp, 0, sizeof(xpp));
		memset(&xecfg, 0, sizeof(xecfg));
		ecbdata.regexp = regexp;
		ecbdata.hit = 0;
		xecfg.ctxlen = o->context;
		xecfg.interhunkctxlen = o->interhunkcontext;
		xdi_diff_outf(&mf1, &mf2, diffgrep_consume, &ecbdata,
			      &xpp, &xecfg);
		hit = ecbdata.hit;
	}
	if (textconv_one)
		free(mf1.ptr);
	if (textconv_two)
		free(mf2.ptr);
	return hit;
}

static void diffcore_pickaxe_grep(struct diff_options *o)
{
	struct diff_queue_struct *q = &diff_queued_diff;
	int i, has_changes, err;
	regex_t regex;
	struct diff_queue_struct outq;
	outq.queue = NULL;
	outq.nr = outq.alloc = 0;

	err = regcomp(&regex, o->pickaxe, REG_EXTENDED | REG_NEWLINE);
	if (err) {
		char errbuf[1024];
		regerror(err, &regex, errbuf, 1024);
		regfree(&regex);
		die("invalid log-grep regex: %s", errbuf);
	}

	if (o->pickaxe_opts & DIFF_PICKAXE_ALL) {
		/* Showing the whole changeset if needle exists */
		for (i = has_changes = 0; !has_changes && i < q->nr; i++) {
			struct diff_filepair *p = q->queue[i];
			if (diff_grep(p, &regex, o))
				has_changes++;
		}
		if (has_changes)
			return; /* do not munge the queue */

		/*
		 * Otherwise we will clear the whole queue by copying
		 * the empty outq at the end of this function, but
		 * first clear the current entries in the queue.
		 */
		for (i = 0; i < q->nr; i++)
			diff_free_filepair(q->queue[i]);
	} else {
		/* Showing only the filepairs that has the needle */
		for (i = 0; i < q->nr; i++) {
			struct diff_filepair *p = q->queue[i];
			if (diff_grep(p, &regex, o))
				diff_q(&outq, p);
			else
				diff_free_filepair(p);
		}
	}

	regfree(&regex);

	free(q->queue);
	*q = outq;
	return;
}
146

147
static unsigned int contains(struct diff_filespec *one,
148 149
			     const char *needle, unsigned long len,
			     regex_t *regexp)
150
{
151
	unsigned int cnt;
152
	unsigned long sz;
153
	const char *data;
154
	if (diff_populate_filespec(one, 0))
155
		return 0;
156 157
	if (!len)
		return 0;
158

159 160
	sz = one->size;
	data = one->data;
161 162
	cnt = 0;

163 164 165 166
	if (regexp) {
		regmatch_t regmatch;
		int flags = 0;

167
		assert(data[sz] == '\0');
168 169
		while (*data && !regexec(regexp, data, 1, &regmatch, flags)) {
			flags |= REG_NOTBOL;
170 171 172
			data += regmatch.rm_eo;
			if (*data && regmatch.rm_so == regmatch.rm_eo)
				data++;
173 174
			cnt++;
		}
175 176

	} else { /* Classic exact string match */
177 178 179 180 181 182 183
		while (sz) {
			const char *found = memmem(data, sz, needle, len);
			if (!found)
				break;
			sz -= found - data + len;
			data = found + len;
			cnt++;
184
		}
185
	}
186
	diff_free_filespec_data(one);
187
	return cnt;
188 189
}

190
static void diffcore_pickaxe_count(struct diff_options *o)
191
{
192 193
	const char *needle = o->pickaxe;
	int opts = o->pickaxe_opts;
194
	struct diff_queue_struct *q = &diff_queued_diff;
195
	unsigned long len = strlen(needle);
196
	int i, has_changes;
197
	regex_t regex, *regexp = NULL;
198
	struct diff_queue_struct outq;
Bo Yang's avatar
Bo Yang committed
199
	DIFF_QUEUE_CLEAR(&outq);
200

201 202 203 204 205 206 207 208 209 210 211 212 213
	if (opts & DIFF_PICKAXE_REGEX) {
		int err;
		err = regcomp(&regex, needle, REG_EXTENDED | REG_NEWLINE);
		if (err) {
			/* The POSIX.2 people are surely sick */
			char errbuf[1024];
			regerror(err, &regex, errbuf, 1024);
			regfree(&regex);
			die("invalid pickaxe regex: %s", errbuf);
		}
		regexp = &regex;
	}

214 215 216 217 218 219 220 221
	if (opts & DIFF_PICKAXE_ALL) {
		/* Showing the whole changeset if needle exists */
		for (i = has_changes = 0; !has_changes && i < q->nr; i++) {
			struct diff_filepair *p = q->queue[i];
			if (!DIFF_FILE_VALID(p->one)) {
				if (!DIFF_FILE_VALID(p->two))
					continue; /* ignore unmerged */
				/* created */
222
				if (contains(p->two, needle, len, regexp))
223 224 225
					has_changes++;
			}
			else if (!DIFF_FILE_VALID(p->two)) {
226
				if (contains(p->one, needle, len, regexp))
227 228 229
					has_changes++;
			}
			else if (!diff_unmodified_pair(p) &&
230 231
				 contains(p->one, needle, len, regexp) !=
				 contains(p->two, needle, len, regexp))
232
				has_changes++;
233
		}
234 235 236 237 238 239 240 241 242 243 244
		if (has_changes)
			return; /* not munge the queue */

		/* otherwise we will clear the whole queue
		 * by copying the empty outq at the end of this
		 * function, but first clear the current entries
		 * in the queue.
		 */
		for (i = 0; i < q->nr; i++)
			diff_free_filepair(q->queue[i]);
	}
Junio C Hamano's avatar
Junio C Hamano committed
245
	else
246 247 248 249 250 251 252 253
		/* Showing only the filepairs that has the needle */
		for (i = 0; i < q->nr; i++) {
			struct diff_filepair *p = q->queue[i];
			has_changes = 0;
			if (!DIFF_FILE_VALID(p->one)) {
				if (!DIFF_FILE_VALID(p->two))
					; /* ignore unmerged */
				/* created */
254
				else if (contains(p->two, needle, len, regexp))
255 256 257
					has_changes = 1;
			}
			else if (!DIFF_FILE_VALID(p->two)) {
258
				if (contains(p->one, needle, len, regexp))
259 260 261
					has_changes = 1;
			}
			else if (!diff_unmodified_pair(p) &&
262 263
				 contains(p->one, needle, len, regexp) !=
				 contains(p->two, needle, len, regexp))
264 265 266
				has_changes = 1;

			if (has_changes)
Junio C Hamano's avatar
Junio C Hamano committed
267
				diff_q(&outq, p);
268 269
			else
				diff_free_filepair(p);
270
		}
271

272 273 274 275
	if (opts & DIFF_PICKAXE_REGEX) {
		regfree(&regex);
	}

276 277 278 279
	free(q->queue);
	*q = outq;
	return;
}
280 281 282 283 284 285 286 287 288

void diffcore_pickaxe(struct diff_options *o)
{
	/* Might want to warn when both S and G are on; I don't care... */
	if (o->pickaxe_opts & DIFF_PICKAXE_KIND_G)
		return diffcore_pickaxe_grep(o);
	else
		return diffcore_pickaxe_count(o);
}