git

Форк
0
/
reflog.c 
444 строки · 11.4 Кб
1
#define USE_THE_REPOSITORY_VARIABLE
2

3
#include "git-compat-util.h"
4
#include "gettext.h"
5
#include "object-store-ll.h"
6
#include "reflog.h"
7
#include "refs.h"
8
#include "revision.h"
9
#include "tree.h"
10
#include "tree-walk.h"
11

12
/* Remember to update object flag allocation in object.h */
13
#define INCOMPLETE	(1u<<10)
14
#define STUDYING	(1u<<11)
15
#define REACHABLE	(1u<<12)
16

17
static int tree_is_complete(const struct object_id *oid)
18
{
19
	struct tree_desc desc;
20
	struct name_entry entry;
21
	int complete;
22
	struct tree *tree;
23

24
	tree = lookup_tree(the_repository, oid);
25
	if (!tree)
26
		return 0;
27
	if (tree->object.flags & SEEN)
28
		return 1;
29
	if (tree->object.flags & INCOMPLETE)
30
		return 0;
31

32
	if (!tree->buffer) {
33
		enum object_type type;
34
		unsigned long size;
35
		void *data = repo_read_object_file(the_repository, oid, &type,
36
						   &size);
37
		if (!data) {
38
			tree->object.flags |= INCOMPLETE;
39
			return 0;
40
		}
41
		tree->buffer = data;
42
		tree->size = size;
43
	}
44
	init_tree_desc(&desc, &tree->object.oid, tree->buffer, tree->size);
45
	complete = 1;
46
	while (tree_entry(&desc, &entry)) {
47
		if (!repo_has_object_file(the_repository, &entry.oid) ||
48
		    (S_ISDIR(entry.mode) && !tree_is_complete(&entry.oid))) {
49
			tree->object.flags |= INCOMPLETE;
50
			complete = 0;
51
		}
52
	}
53
	free_tree_buffer(tree);
54

55
	if (complete)
56
		tree->object.flags |= SEEN;
57
	return complete;
58
}
59

60
static int commit_is_complete(struct commit *commit)
61
{
62
	struct object_array study;
63
	struct object_array found;
64
	int is_incomplete = 0;
65
	int i;
66

67
	/* early return */
68
	if (commit->object.flags & SEEN)
69
		return 1;
70
	if (commit->object.flags & INCOMPLETE)
71
		return 0;
72
	/*
73
	 * Find all commits that are reachable and are not marked as
74
	 * SEEN.  Then make sure the trees and blobs contained are
75
	 * complete.  After that, mark these commits also as SEEN.
76
	 * If some of the objects that are needed to complete this
77
	 * commit are missing, mark this commit as INCOMPLETE.
78
	 */
79
	memset(&study, 0, sizeof(study));
80
	memset(&found, 0, sizeof(found));
81
	add_object_array(&commit->object, NULL, &study);
82
	add_object_array(&commit->object, NULL, &found);
83
	commit->object.flags |= STUDYING;
84
	while (study.nr) {
85
		struct commit *c;
86
		struct commit_list *parent;
87

88
		c = (struct commit *)object_array_pop(&study);
89
		if (!c->object.parsed && !parse_object(the_repository, &c->object.oid))
90
			c->object.flags |= INCOMPLETE;
91

92
		if (c->object.flags & INCOMPLETE) {
93
			is_incomplete = 1;
94
			break;
95
		}
96
		else if (c->object.flags & SEEN)
97
			continue;
98
		for (parent = c->parents; parent; parent = parent->next) {
99
			struct commit *p = parent->item;
100
			if (p->object.flags & STUDYING)
101
				continue;
102
			p->object.flags |= STUDYING;
103
			add_object_array(&p->object, NULL, &study);
104
			add_object_array(&p->object, NULL, &found);
105
		}
106
	}
107
	if (!is_incomplete) {
108
		/*
109
		 * make sure all commits in "found" array have all the
110
		 * necessary objects.
111
		 */
112
		for (i = 0; i < found.nr; i++) {
113
			struct commit *c =
114
				(struct commit *)found.objects[i].item;
115
			if (!tree_is_complete(get_commit_tree_oid(c))) {
116
				is_incomplete = 1;
117
				c->object.flags |= INCOMPLETE;
118
			}
119
		}
120
		if (!is_incomplete) {
121
			/* mark all found commits as complete, iow SEEN */
122
			for (i = 0; i < found.nr; i++)
123
				found.objects[i].item->flags |= SEEN;
124
		}
125
	}
126
	/* clear flags from the objects we traversed */
127
	for (i = 0; i < found.nr; i++)
128
		found.objects[i].item->flags &= ~STUDYING;
129
	if (is_incomplete)
130
		commit->object.flags |= INCOMPLETE;
131
	else {
132
		/*
133
		 * If we come here, we have (1) traversed the ancestry chain
134
		 * from the "commit" until we reach SEEN commits (which are
135
		 * known to be complete), and (2) made sure that the commits
136
		 * encountered during the above traversal refer to trees that
137
		 * are complete.  Which means that we know *all* the commits
138
		 * we have seen during this process are complete.
139
		 */
140
		for (i = 0; i < found.nr; i++)
141
			found.objects[i].item->flags |= SEEN;
142
	}
143
	/* free object arrays */
144
	object_array_clear(&study);
145
	object_array_clear(&found);
146
	return !is_incomplete;
147
}
148

149
static int keep_entry(struct commit **it, struct object_id *oid)
150
{
151
	struct commit *commit;
152

153
	if (is_null_oid(oid))
154
		return 1;
155
	commit = lookup_commit_reference_gently(the_repository, oid, 1);
156
	if (!commit)
157
		return 0;
158

159
	/*
160
	 * Make sure everything in this commit exists.
161
	 *
162
	 * We have walked all the objects reachable from the refs
163
	 * and cache earlier.  The commits reachable by this commit
164
	 * must meet SEEN commits -- and then we should mark them as
165
	 * SEEN as well.
166
	 */
167
	if (!commit_is_complete(commit))
168
		return 0;
169
	*it = commit;
170
	return 1;
171
}
172

173
/*
174
 * Starting from commits in the cb->mark_list, mark commits that are
175
 * reachable from them.  Stop the traversal at commits older than
176
 * the expire_limit and queue them back, so that the caller can call
177
 * us again to restart the traversal with longer expire_limit.
178
 */
179
static void mark_reachable(struct expire_reflog_policy_cb *cb)
180
{
181
	struct commit_list *pending;
182
	timestamp_t expire_limit = cb->mark_limit;
183
	struct commit_list *leftover = NULL;
184

185
	for (pending = cb->mark_list; pending; pending = pending->next)
186
		pending->item->object.flags &= ~REACHABLE;
187

188
	pending = cb->mark_list;
189
	while (pending) {
190
		struct commit_list *parent;
191
		struct commit *commit = pop_commit(&pending);
192
		if (commit->object.flags & REACHABLE)
193
			continue;
194
		if (repo_parse_commit(the_repository, commit))
195
			continue;
196
		commit->object.flags |= REACHABLE;
197
		if (commit->date < expire_limit) {
198
			commit_list_insert(commit, &leftover);
199
			continue;
200
		}
201
		parent = commit->parents;
202
		while (parent) {
203
			commit = parent->item;
204
			parent = parent->next;
205
			if (commit->object.flags & REACHABLE)
206
				continue;
207
			commit_list_insert(commit, &pending);
208
		}
209
	}
210
	cb->mark_list = leftover;
211
}
212

213
static int unreachable(struct expire_reflog_policy_cb *cb, struct commit *commit, struct object_id *oid)
214
{
215
	/*
216
	 * We may or may not have the commit yet - if not, look it
217
	 * up using the supplied sha1.
218
	 */
219
	if (!commit) {
220
		if (is_null_oid(oid))
221
			return 0;
222

223
		commit = lookup_commit_reference_gently(the_repository, oid,
224
							1);
225

226
		/* Not a commit -- keep it */
227
		if (!commit)
228
			return 0;
229
	}
230

231
	/* Reachable from the current ref?  Don't prune. */
232
	if (commit->object.flags & REACHABLE)
233
		return 0;
234

235
	if (cb->mark_list && cb->mark_limit) {
236
		cb->mark_limit = 0; /* dig down to the root */
237
		mark_reachable(cb);
238
	}
239

240
	return !(commit->object.flags & REACHABLE);
241
}
242

243
/*
244
 * Return true iff the specified reflog entry should be expired.
245
 */
246
int should_expire_reflog_ent(struct object_id *ooid, struct object_id *noid,
247
			     const char *email UNUSED,
248
			     timestamp_t timestamp, int tz UNUSED,
249
			     const char *message UNUSED, void *cb_data)
250
{
251
	struct expire_reflog_policy_cb *cb = cb_data;
252
	struct commit *old_commit, *new_commit;
253

254
	if (timestamp < cb->cmd.expire_total)
255
		return 1;
256

257
	old_commit = new_commit = NULL;
258
	if (cb->cmd.stalefix &&
259
	    (!keep_entry(&old_commit, ooid) || !keep_entry(&new_commit, noid)))
260
		return 1;
261

262
	if (timestamp < cb->cmd.expire_unreachable) {
263
		switch (cb->unreachable_expire_kind) {
264
		case UE_ALWAYS:
265
			return 1;
266
		case UE_NORMAL:
267
		case UE_HEAD:
268
			if (unreachable(cb, old_commit, ooid) || unreachable(cb, new_commit, noid))
269
				return 1;
270
			break;
271
		}
272
	}
273

274
	if (cb->cmd.recno && --(cb->cmd.recno) == 0)
275
		return 1;
276

277
	return 0;
278
}
279

280
int should_expire_reflog_ent_verbose(struct object_id *ooid,
281
				     struct object_id *noid,
282
				     const char *email,
283
				     timestamp_t timestamp, int tz,
284
				     const char *message, void *cb_data)
285
{
286
	struct expire_reflog_policy_cb *cb = cb_data;
287
	int expire;
288

289
	expire = should_expire_reflog_ent(ooid, noid, email, timestamp, tz,
290
					  message, cb);
291

292
	if (!expire)
293
		printf("keep %s", message);
294
	else if (cb->dry_run)
295
		printf("would prune %s", message);
296
	else
297
		printf("prune %s", message);
298

299
	return expire;
300
}
301

302
static int push_tip_to_list(const char *refname UNUSED,
303
			    const char *referent UNUSED,
304
			    const struct object_id *oid,
305
			    int flags, void *cb_data)
306
{
307
	struct commit_list **list = cb_data;
308
	struct commit *tip_commit;
309
	if (flags & REF_ISSYMREF)
310
		return 0;
311
	tip_commit = lookup_commit_reference_gently(the_repository, oid, 1);
312
	if (!tip_commit)
313
		return 0;
314
	commit_list_insert(tip_commit, list);
315
	return 0;
316
}
317

318
static int is_head(const char *refname)
319
{
320
	const char *stripped_refname;
321
	parse_worktree_ref(refname, NULL, NULL, &stripped_refname);
322
	return !strcmp(stripped_refname, "HEAD");
323
}
324

325
void reflog_expiry_prepare(const char *refname,
326
			   const struct object_id *oid,
327
			   void *cb_data)
328
{
329
	struct expire_reflog_policy_cb *cb = cb_data;
330
	struct commit_list *elem;
331
	struct commit *commit = NULL;
332

333
	if (!cb->cmd.expire_unreachable || is_head(refname)) {
334
		cb->unreachable_expire_kind = UE_HEAD;
335
	} else {
336
		commit = lookup_commit_reference_gently(the_repository,
337
							oid, 1);
338
		if (commit && is_null_oid(&commit->object.oid))
339
			commit = NULL;
340
		cb->unreachable_expire_kind = commit ? UE_NORMAL : UE_ALWAYS;
341
	}
342

343
	if (cb->cmd.expire_unreachable <= cb->cmd.expire_total)
344
		cb->unreachable_expire_kind = UE_ALWAYS;
345

346
	switch (cb->unreachable_expire_kind) {
347
	case UE_ALWAYS:
348
		return;
349
	case UE_HEAD:
350
		refs_for_each_ref(get_main_ref_store(the_repository),
351
				  push_tip_to_list, &cb->tips);
352
		for (elem = cb->tips; elem; elem = elem->next)
353
			commit_list_insert(elem->item, &cb->mark_list);
354
		break;
355
	case UE_NORMAL:
356
		commit_list_insert(commit, &cb->mark_list);
357
		/* For reflog_expiry_cleanup() below */
358
		cb->tip_commit = commit;
359
	}
360
	cb->mark_limit = cb->cmd.expire_total;
361
	mark_reachable(cb);
362
}
363

364
void reflog_expiry_cleanup(void *cb_data)
365
{
366
	struct expire_reflog_policy_cb *cb = cb_data;
367
	struct commit_list *elem;
368

369
	switch (cb->unreachable_expire_kind) {
370
	case UE_ALWAYS:
371
		return;
372
	case UE_HEAD:
373
		for (elem = cb->tips; elem; elem = elem->next)
374
			clear_commit_marks(elem->item, REACHABLE);
375
		free_commit_list(cb->tips);
376
		break;
377
	case UE_NORMAL:
378
		clear_commit_marks(cb->tip_commit, REACHABLE);
379
		break;
380
	}
381
	for (elem = cb->mark_list; elem; elem = elem->next)
382
		clear_commit_marks(elem->item, REACHABLE);
383
	free_commit_list(cb->mark_list);
384
}
385

386
int count_reflog_ent(struct object_id *ooid UNUSED,
387
		     struct object_id *noid UNUSED,
388
		     const char *email UNUSED,
389
		     timestamp_t timestamp, int tz UNUSED,
390
		     const char *message UNUSED, void *cb_data)
391
{
392
	struct cmd_reflog_expire_cb *cb = cb_data;
393
	if (!cb->expire_total || timestamp < cb->expire_total)
394
		cb->recno++;
395
	return 0;
396
}
397

398
int reflog_delete(const char *rev, enum expire_reflog_flags flags, int verbose)
399
{
400
	struct cmd_reflog_expire_cb cmd = { 0 };
401
	int status = 0;
402
	reflog_expiry_should_prune_fn *should_prune_fn = should_expire_reflog_ent;
403
	const char *spec = strstr(rev, "@{");
404
	char *ep, *ref;
405
	int recno;
406
	struct expire_reflog_policy_cb cb = {
407
		.dry_run = !!(flags & EXPIRE_REFLOGS_DRY_RUN),
408
	};
409

410
	if (verbose)
411
		should_prune_fn = should_expire_reflog_ent_verbose;
412

413
	if (!spec)
414
		return error(_("not a reflog: %s"), rev);
415

416
	if (!repo_dwim_log(the_repository, rev, spec - rev, NULL, &ref)) {
417
		status |= error(_("no reflog for '%s'"), rev);
418
		goto cleanup;
419
	}
420

421
	recno = strtoul(spec + 2, &ep, 10);
422
	if (*ep == '}') {
423
		cmd.recno = -recno;
424
		refs_for_each_reflog_ent(get_main_ref_store(the_repository),
425
					 ref, count_reflog_ent, &cmd);
426
	} else {
427
		cmd.expire_total = approxidate(spec + 2);
428
		refs_for_each_reflog_ent(get_main_ref_store(the_repository),
429
					 ref, count_reflog_ent, &cmd);
430
		cmd.expire_total = 0;
431
	}
432

433
	cb.cmd = cmd;
434
	status |= refs_reflog_expire(get_main_ref_store(the_repository), ref,
435
				     flags,
436
				     reflog_expiry_prepare,
437
				     should_prune_fn,
438
				     reflog_expiry_cleanup,
439
				     &cb);
440

441
 cleanup:
442
	free(ref);
443
	return status;
444
}
445

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

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

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

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