git

Форк
0
/
patch-id.c 
307 строк · 7.5 Кб
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"
9

10
static void flush_current_id(struct object_id *id, struct object_id *result)
11
{
12
	printf("%s %s\n", oid_to_hex(result), oid_to_hex(id));
13
}
14

15
static int remove_space(char *line)
16
{
17
	char *src = line;
18
	char *dst = line;
19
	unsigned char c;
20

21
	while ((c = *src++) != '\0') {
22
		if (!isspace(c))
23
			*dst++ = c;
24
	}
25
	return dst - line;
26
}
27

28
static int scan_hunk_header(const char *p, int *p_before, int *p_after)
29
{
30
	static const char digits[] = "0123456789";
31
	const char *q, *r;
32
	int n;
33

34
	q = p + 4;
35
	n = strspn(q, digits);
36
	if (q[n] == ',') {
37
		q += n + 1;
38
		*p_before = atoi(q);
39
		n = strspn(q, digits);
40
	} else {
41
		*p_before = 1;
42
	}
43

44
	if (n == 0 || q[n] != ' ' || q[n+1] != '+')
45
		return 0;
46

47
	r = q + n + 2;
48
	n = strspn(r, digits);
49
	if (r[n] == ',') {
50
		r += n + 1;
51
		*p_after = atoi(r);
52
		n = strspn(r, digits);
53
	} else {
54
		*p_after = 1;
55
	}
56
	if (n == 0)
57
		return 0;
58

59
	return 1;
60
}
61

62
/*
63
 * flag bits to control get_one_patchid()'s behaviour.
64
 *
65
 * STABLE/VERBATIM are given from the command line option as
66
 * --stable/--verbatim.  FIND_HEADER conveys the internal state
67
 * maintained by the caller to allow the function to avoid mistaking
68
 * lines of log message before seeing the "diff" part as the beginning
69
 * of the next patch.
70
 */
71
enum {
72
	GOPID_STABLE = (1<<0),		/* --stable */
73
	GOPID_VERBATIM = (1<<1),	/* --verbatim */
74
	GOPID_FIND_HEADER = (1<<2),	/* stop at the beginning of patch message */
75
};
76

77
static int get_one_patchid(struct object_id *next_oid, struct object_id *result,
78
			   struct strbuf *line_buf, unsigned flags)
79
{
80
	int stable = flags & GOPID_STABLE;
81
	int verbatim = flags & GOPID_VERBATIM;
82
	int find_header = flags & GOPID_FIND_HEADER;
83
	int patchlen = 0, found_next = 0;
84
	int before = -1, after = -1;
85
	int diff_is_binary = 0;
86
	char pre_oid_str[GIT_MAX_HEXSZ + 1], post_oid_str[GIT_MAX_HEXSZ + 1];
87
	git_hash_ctx ctx;
88

89
	the_hash_algo->init_fn(&ctx);
90
	oidclr(result, the_repository->hash_algo);
91

92
	while (strbuf_getwholeline(line_buf, stdin, '\n') != EOF) {
93
		char *line = line_buf->buf;
94
		const char *p = line;
95
		int len;
96

97
		/*
98
		 * The caller hasn't seen us find a patch header and
99
		 * return to it, or we have started processing patch
100
		 * and may encounter the beginning of the next patch.
101
		 */
102
		if (find_header) {
103
			/*
104
			 * If we see a line that begins with "<object name>",
105
			 * "commit <object name>" or "From <object name>", it is
106
			 * the beginning of a patch.  Return to the caller, as
107
			 * we are done with the one we have been processing.
108
			 */
109
			if (skip_prefix(line, "commit ", &p))
110
				;
111
			else if (skip_prefix(line, "From ", &p))
112
				;
113
			if (!get_oid_hex(p, next_oid)) {
114
				if (verbatim)
115
					the_hash_algo->update_fn(&ctx, line, strlen(line));
116
				found_next = 1;
117
				break;
118
			}
119
		}
120

121
		/* Ignore commit comments */
122
		if (!patchlen && !starts_with(line, "diff "))
123
			continue;
124

125
		/*
126
		 * We are past the commit log message.  Prepare to
127
		 * stop at the beginning of the next patch header.
128
		 */
129
		find_header = 1;
130

131
		/* Parsing diff header?  */
132
		if (before == -1) {
133
			if (starts_with(line, "GIT binary patch") ||
134
			    starts_with(line, "Binary files")) {
135
				diff_is_binary = 1;
136
				before = 0;
137
				the_hash_algo->update_fn(&ctx, pre_oid_str,
138
							 strlen(pre_oid_str));
139
				the_hash_algo->update_fn(&ctx, post_oid_str,
140
							 strlen(post_oid_str));
141
				if (stable)
142
					flush_one_hunk(result, &ctx);
143
				continue;
144
			} else if (skip_prefix(line, "index ", &p)) {
145
				char *oid1_end = strstr(line, "..");
146
				char *oid2_end = NULL;
147
				if (oid1_end)
148
					oid2_end = strstr(oid1_end, " ");
149
				if (!oid2_end)
150
					oid2_end = line + strlen(line) - 1;
151
				if (oid1_end != NULL && oid2_end != NULL) {
152
					*oid1_end = *oid2_end = '\0';
153
					strlcpy(pre_oid_str, p, GIT_MAX_HEXSZ + 1);
154
					strlcpy(post_oid_str, oid1_end + 2, GIT_MAX_HEXSZ + 1);
155
				}
156
				continue;
157
			} else if (starts_with(line, "--- "))
158
				before = after = 1;
159
			else if (!isalpha(line[0]))
160
				break;
161
		}
162

163
		/*
164
		 * A hunk about an incomplete line may have this
165
		 * marker at the end, which should just be ignored.
166
		 */
167
		if (starts_with(line, "\\ ") && 12 < strlen(line)) {
168
			if (verbatim)
169
				the_hash_algo->update_fn(&ctx, line, strlen(line));
170
			continue;
171
		}
172

173
		if (diff_is_binary) {
174
			if (starts_with(line, "diff ")) {
175
				diff_is_binary = 0;
176
				before = -1;
177
			}
178
			continue;
179
		}
180

181
		/* Looking for a valid hunk header?  */
182
		if (before == 0 && after == 0) {
183
			if (starts_with(line, "@@ -")) {
184
				/* Parse next hunk, but ignore line numbers.  */
185
				scan_hunk_header(line, &before, &after);
186
				continue;
187
			}
188

189
			/* Split at the end of the patch.  */
190
			if (!starts_with(line, "diff "))
191
				break;
192

193
			/* Else we're parsing another header.  */
194
			if (stable)
195
				flush_one_hunk(result, &ctx);
196
			before = after = -1;
197
		}
198

199
		/* If we get here, we're inside a hunk.  */
200
		if (line[0] == '-' || line[0] == ' ')
201
			before--;
202
		if (line[0] == '+' || line[0] == ' ')
203
			after--;
204

205
		/* Add line to hash algo (possibly removing whitespace) */
206
		len = verbatim ? strlen(line) : remove_space(line);
207
		patchlen += len;
208
		the_hash_algo->update_fn(&ctx, line, len);
209
	}
210

211
	if (!found_next)
212
		oidclr(next_oid, the_repository->hash_algo);
213

214
	flush_one_hunk(result, &ctx);
215

216
	return patchlen;
217
}
218

219
static void generate_id_list(unsigned flags)
220
{
221
	struct object_id oid, n, result;
222
	int patchlen;
223
	struct strbuf line_buf = STRBUF_INIT;
224

225
	oidclr(&oid, the_repository->hash_algo);
226
	flags |= GOPID_FIND_HEADER;
227
	while (!feof(stdin)) {
228
		patchlen = get_one_patchid(&n, &result, &line_buf, flags);
229
		if (patchlen)
230
			flush_current_id(&oid, &result);
231
		oidcpy(&oid, &n);
232
		flags &= ~GOPID_FIND_HEADER;
233
	}
234
	strbuf_release(&line_buf);
235
}
236

237
static const char *const patch_id_usage[] = {
238
	N_("git patch-id [--stable | --unstable | --verbatim]"), NULL
239
};
240

241
struct patch_id_opts {
242
	int stable;
243
	int verbatim;
244
};
245

246
static int git_patch_id_config(const char *var, const char *value,
247
			       const struct config_context *ctx, void *cb)
248
{
249
	struct patch_id_opts *opts = cb;
250

251
	if (!strcmp(var, "patchid.stable")) {
252
		opts->stable = git_config_bool(var, value);
253
		return 0;
254
	}
255
	if (!strcmp(var, "patchid.verbatim")) {
256
		opts->verbatim = git_config_bool(var, value);
257
		return 0;
258
	}
259

260
	return git_default_config(var, value, ctx, cb);
261
}
262

263
int cmd_patch_id(int argc, const char **argv, const char *prefix)
264
{
265
	/* if nothing is set, default to unstable */
266
	struct patch_id_opts config = {0, 0};
267
	int opts = 0;
268
	unsigned flags = 0;
269
	struct option builtin_patch_id_options[] = {
270
		OPT_CMDMODE(0, "unstable", &opts,
271
		    N_("use the unstable patch-id algorithm"), 1),
272
		OPT_CMDMODE(0, "stable", &opts,
273
		    N_("use the stable patch-id algorithm"), 2),
274
		OPT_CMDMODE(0, "verbatim", &opts,
275
			N_("don't strip whitespace from the patch"), 3),
276
		OPT_END()
277
	};
278

279
	git_config(git_patch_id_config, &config);
280

281
	/* verbatim implies stable */
282
	if (config.verbatim)
283
		config.stable = 1;
284

285
	argc = parse_options(argc, argv, prefix, builtin_patch_id_options,
286
			     patch_id_usage, 0);
287

288
	/*
289
	 * We rely on `the_hash_algo` to compute patch IDs. This is dubious as
290
	 * it means that the hash algorithm now depends on the object hash of
291
	 * the repository, even though git-patch-id(1) clearly defines that
292
	 * patch IDs always use SHA1.
293
	 *
294
	 * NEEDSWORK: This hack should be removed in favor of converting
295
	 * the code that computes patch IDs to always use SHA1.
296
	 */
297
	if (!the_hash_algo)
298
		repo_set_hash_algo(the_repository, GIT_HASH_SHA1);
299

300
	if (opts ? opts > 1 : config.stable)
301
		flags |= GOPID_STABLE;
302
	if (opts ? opts == 3 : config.verbatim)
303
		flags |= GOPID_VERBATIM;
304
	generate_id_list(flags);
305

306
	return 0;
307
}
308

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.