reflog expire: cover reflog from all worktrees
Reported-by: Jeff King <peff@peff.net> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>maint
							parent
							
								
									b29759d89a
								
							
						
					
					
						commit
						c9ef0d95eb
					
				|  | @ -20,7 +20,7 @@ depending on the subcommand: | ||||||
| 'git reflog' ['show'] [log-options] [<ref>] | 'git reflog' ['show'] [log-options] [<ref>] | ||||||
| 'git reflog expire' [--expire=<time>] [--expire-unreachable=<time>] | 'git reflog expire' [--expire=<time>] [--expire-unreachable=<time>] | ||||||
| 	[--rewrite] [--updateref] [--stale-fix] | 	[--rewrite] [--updateref] [--stale-fix] | ||||||
| 	[--dry-run | -n] [--verbose] [--all | <refs>...] | 	[--dry-run | -n] [--verbose] [--all [--single-worktree] | <refs>...] | ||||||
| 'git reflog delete' [--rewrite] [--updateref] | 'git reflog delete' [--rewrite] [--updateref] | ||||||
| 	[--dry-run | -n] [--verbose] ref@\{specifier\}... | 	[--dry-run | -n] [--verbose] ref@\{specifier\}... | ||||||
| 'git reflog exists' <ref> | 'git reflog exists' <ref> | ||||||
|  | @ -72,6 +72,11 @@ Options for `expire` | ||||||
| --all:: | --all:: | ||||||
| 	Process the reflogs of all references. | 	Process the reflogs of all references. | ||||||
|  |  | ||||||
|  | --single-worktree:: | ||||||
|  | 	By default when `--all` is specified, reflogs from all working | ||||||
|  | 	trees are processed. This option limits the processing to reflogs | ||||||
|  | 	from the current working tree only. | ||||||
|  |  | ||||||
| --expire=<time>:: | --expire=<time>:: | ||||||
| 	Prune entries older than the specified time. If this option is | 	Prune entries older than the specified time. If this option is | ||||||
| 	not specified, the expiration time is taken from the | 	not specified, the expiration time is taken from the | ||||||
|  |  | ||||||
|  | @ -10,6 +10,7 @@ | ||||||
| #include "diff.h" | #include "diff.h" | ||||||
| #include "revision.h" | #include "revision.h" | ||||||
| #include "reachable.h" | #include "reachable.h" | ||||||
|  | #include "worktree.h" | ||||||
|  |  | ||||||
| /* NEEDSWORK: switch to using parse_options */ | /* NEEDSWORK: switch to using parse_options */ | ||||||
| static const char reflog_expire_usage[] = | static const char reflog_expire_usage[] = | ||||||
|  | @ -52,6 +53,7 @@ struct collect_reflog_cb { | ||||||
| 	struct collected_reflog **e; | 	struct collected_reflog **e; | ||||||
| 	int alloc; | 	int alloc; | ||||||
| 	int nr; | 	int nr; | ||||||
|  | 	struct worktree *wt; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| /* Remember to update object flag allocation in object.h */ | /* Remember to update object flag allocation in object.h */ | ||||||
|  | @ -330,13 +332,27 @@ static int push_tip_to_list(const char *refname, const struct object_id *oid, | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static int is_head(const char *refname) | ||||||
|  | { | ||||||
|  | 	switch (ref_type(refname)) { | ||||||
|  | 	case REF_TYPE_OTHER_PSEUDOREF: | ||||||
|  | 	case REF_TYPE_MAIN_PSEUDOREF: | ||||||
|  | 		if (parse_worktree_ref(refname, NULL, NULL, &refname)) | ||||||
|  | 			BUG("not a worktree ref: %s", refname); | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		break; | ||||||
|  | 	} | ||||||
|  | 	return !strcmp(refname, "HEAD"); | ||||||
|  | } | ||||||
|  |  | ||||||
| static void reflog_expiry_prepare(const char *refname, | static void reflog_expiry_prepare(const char *refname, | ||||||
| 				  const struct object_id *oid, | 				  const struct object_id *oid, | ||||||
| 				  void *cb_data) | 				  void *cb_data) | ||||||
| { | { | ||||||
| 	struct expire_reflog_policy_cb *cb = cb_data; | 	struct expire_reflog_policy_cb *cb = cb_data; | ||||||
|  |  | ||||||
| 	if (!cb->cmd.expire_unreachable || !strcmp(refname, "HEAD")) { | 	if (!cb->cmd.expire_unreachable || is_head(refname)) { | ||||||
| 		cb->tip_commit = NULL; | 		cb->tip_commit = NULL; | ||||||
| 		cb->unreachable_expire_kind = UE_HEAD; | 		cb->unreachable_expire_kind = UE_HEAD; | ||||||
| 	} else { | 	} else { | ||||||
|  | @ -388,8 +404,19 @@ static int collect_reflog(const char *ref, const struct object_id *oid, int unus | ||||||
| { | { | ||||||
| 	struct collected_reflog *e; | 	struct collected_reflog *e; | ||||||
| 	struct collect_reflog_cb *cb = cb_data; | 	struct collect_reflog_cb *cb = cb_data; | ||||||
|  | 	struct strbuf newref = STRBUF_INIT; | ||||||
|  |  | ||||||
|  | 	/* | ||||||
|  | 	 * Avoid collecting the same shared ref multiple times because | ||||||
|  | 	 * they are available via all worktrees. | ||||||
|  | 	 */ | ||||||
|  | 	if (!cb->wt->is_current && ref_type(ref) == REF_TYPE_NORMAL) | ||||||
|  | 		return 0; | ||||||
|  |  | ||||||
|  | 	strbuf_worktree_ref(cb->wt, &newref, ref); | ||||||
|  | 	FLEX_ALLOC_STR(e, reflog, newref.buf); | ||||||
|  | 	strbuf_release(&newref); | ||||||
|  |  | ||||||
| 	FLEX_ALLOC_STR(e, reflog, ref); |  | ||||||
| 	oidcpy(&e->oid, oid); | 	oidcpy(&e->oid, oid); | ||||||
| 	ALLOC_GROW(cb->e, cb->nr + 1, cb->alloc); | 	ALLOC_GROW(cb->e, cb->nr + 1, cb->alloc); | ||||||
| 	cb->e[cb->nr++] = e; | 	cb->e[cb->nr++] = e; | ||||||
|  | @ -512,7 +539,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix) | ||||||
| { | { | ||||||
| 	struct expire_reflog_policy_cb cb; | 	struct expire_reflog_policy_cb cb; | ||||||
| 	timestamp_t now = time(NULL); | 	timestamp_t now = time(NULL); | ||||||
| 	int i, status, do_all; | 	int i, status, do_all, all_worktrees = 1; | ||||||
| 	int explicit_expiry = 0; | 	int explicit_expiry = 0; | ||||||
| 	unsigned int flags = 0; | 	unsigned int flags = 0; | ||||||
|  |  | ||||||
|  | @ -549,6 +576,8 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix) | ||||||
| 			flags |= EXPIRE_REFLOGS_UPDATE_REF; | 			flags |= EXPIRE_REFLOGS_UPDATE_REF; | ||||||
| 		else if (!strcmp(arg, "--all")) | 		else if (!strcmp(arg, "--all")) | ||||||
| 			do_all = 1; | 			do_all = 1; | ||||||
|  | 		else if (!strcmp(arg, "--single-worktree")) | ||||||
|  | 			all_worktrees = 0; | ||||||
| 		else if (!strcmp(arg, "--verbose")) | 		else if (!strcmp(arg, "--verbose")) | ||||||
| 			flags |= EXPIRE_REFLOGS_VERBOSE; | 			flags |= EXPIRE_REFLOGS_VERBOSE; | ||||||
| 		else if (!strcmp(arg, "--")) { | 		else if (!strcmp(arg, "--")) { | ||||||
|  | @ -577,10 +606,19 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix) | ||||||
|  |  | ||||||
| 	if (do_all) { | 	if (do_all) { | ||||||
| 		struct collect_reflog_cb collected; | 		struct collect_reflog_cb collected; | ||||||
|  | 		struct worktree **worktrees, **p; | ||||||
| 		int i; | 		int i; | ||||||
|  |  | ||||||
| 		memset(&collected, 0, sizeof(collected)); | 		memset(&collected, 0, sizeof(collected)); | ||||||
| 		for_each_reflog(collect_reflog, &collected); | 		worktrees = get_worktrees(0); | ||||||
|  | 		for (p = worktrees; *p; p++) { | ||||||
|  | 			if (!all_worktrees && !(*p)->is_current) | ||||||
|  | 				continue; | ||||||
|  | 			collected.wt = *p; | ||||||
|  | 			refs_for_each_reflog(get_worktree_ref_store(*p), | ||||||
|  | 					     collect_reflog, &collected); | ||||||
|  | 		} | ||||||
|  | 		free_worktrees(worktrees); | ||||||
| 		for (i = 0; i < collected.nr; i++) { | 		for (i = 0; i < collected.nr; i++) { | ||||||
| 			struct collected_reflog *e = collected.e[i]; | 			struct collected_reflog *e = collected.e[i]; | ||||||
| 			set_reflog_expiry_param(&cb.cmd, explicit_expiry, e->reflog); | 			set_reflog_expiry_param(&cb.cmd, explicit_expiry, e->reflog); | ||||||
|  |  | ||||||
|  | @ -368,4 +368,19 @@ test_expect_success 'continue walking past root commits' ' | ||||||
| 	) | 	) | ||||||
| ' | ' | ||||||
|  |  | ||||||
|  | test_expect_success 'expire with multiple worktrees' ' | ||||||
|  | 	git init main-wt && | ||||||
|  | 	( | ||||||
|  | 		cd main-wt && | ||||||
|  | 		test_tick && | ||||||
|  | 		test_commit foo && | ||||||
|  | 		git  worktree add link-wt && | ||||||
|  | 		test_tick && | ||||||
|  | 		test_commit -C link-wt foobar && | ||||||
|  | 		test_tick && | ||||||
|  | 		git reflog expire --verbose --all --expire=$test_tick && | ||||||
|  | 		test_must_be_empty .git/worktrees/link-wt/logs/HEAD | ||||||
|  | 	) | ||||||
|  | ' | ||||||
|  |  | ||||||
| test_done | test_done | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	 Nguyễn Thái Ngọc Duy
						Nguyễn Thái Ngọc Duy