Merge branch 'ps/reflog-list' into HEAD
"git reflog" learned a "list" subcommand that enumerates known reflogs. * ps/reflog-list: builtin/reflog: introduce subcommand to list reflogs refs: stop resolving ref corresponding to reflogs refs: drop unused params from the reflog iterator callback refs: always treat iterators as ordered refs/files: sort merged worktree and common reflogs refs/files: sort reflogs returned by the reflog iterator dir-iterator: support iteration in sorted order dir-iterator: pass name to `prepare_next_entry_data()` directlymaint
						commit
						510a27e9e4
					
				|  | @ -10,6 +10,7 @@ SYNOPSIS | ||||||
| -------- | -------- | ||||||
| [verse] | [verse] | ||||||
| 'git reflog' [show] [<log-options>] [<ref>] | 'git reflog' [show] [<log-options>] [<ref>] | ||||||
|  | 'git reflog list' | ||||||
| '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 [--single-worktree] | <refs>...] | 	[--dry-run | -n] [--verbose] [--all [--single-worktree] | <refs>...] | ||||||
|  | @ -39,6 +40,8 @@ actions, and in addition the `HEAD` reflog records branch switching. | ||||||
| `git reflog show` is an alias for `git log -g --abbrev-commit | `git reflog show` is an alias for `git log -g --abbrev-commit | ||||||
| --pretty=oneline`; see linkgit:git-log[1] for more information. | --pretty=oneline`; see linkgit:git-log[1] for more information. | ||||||
|  |  | ||||||
|  | The "list" subcommand lists all refs which have a corresponding reflog. | ||||||
|  |  | ||||||
| The "expire" subcommand prunes older reflog entries. Entries older | The "expire" subcommand prunes older reflog entries. Entries older | ||||||
| than `expire` time, or entries older than `expire-unreachable` time | than `expire` time, or entries older than `expire-unreachable` time | ||||||
| and not reachable from the current tip, are removed from the reflog. | and not reachable from the current tip, are removed from the reflog. | ||||||
|  |  | ||||||
|  | @ -509,9 +509,7 @@ static int fsck_handle_reflog_ent(struct object_id *ooid, struct object_id *noid | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int fsck_handle_reflog(const char *logname, | static int fsck_handle_reflog(const char *logname, void *cb_data) | ||||||
| 			      const struct object_id *oid UNUSED, |  | ||||||
| 			      int flag UNUSED, void *cb_data) |  | ||||||
| { | { | ||||||
| 	struct strbuf refname = STRBUF_INIT; | 	struct strbuf refname = STRBUF_INIT; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @ -7,11 +7,15 @@ | ||||||
| #include "wildmatch.h" | #include "wildmatch.h" | ||||||
| #include "worktree.h" | #include "worktree.h" | ||||||
| #include "reflog.h" | #include "reflog.h" | ||||||
|  | #include "refs.h" | ||||||
| #include "parse-options.h" | #include "parse-options.h" | ||||||
|  |  | ||||||
| #define BUILTIN_REFLOG_SHOW_USAGE \ | #define BUILTIN_REFLOG_SHOW_USAGE \ | ||||||
| 	N_("git reflog [show] [<log-options>] [<ref>]") | 	N_("git reflog [show] [<log-options>] [<ref>]") | ||||||
|  |  | ||||||
|  | #define BUILTIN_REFLOG_LIST_USAGE \ | ||||||
|  | 	N_("git reflog list") | ||||||
|  |  | ||||||
| #define BUILTIN_REFLOG_EXPIRE_USAGE \ | #define BUILTIN_REFLOG_EXPIRE_USAGE \ | ||||||
| 	N_("git reflog expire [--expire=<time>] [--expire-unreachable=<time>]\n" \ | 	N_("git reflog expire [--expire=<time>] [--expire-unreachable=<time>]\n" \ | ||||||
| 	   "                  [--rewrite] [--updateref] [--stale-fix]\n" \ | 	   "                  [--rewrite] [--updateref] [--stale-fix]\n" \ | ||||||
|  | @ -29,6 +33,11 @@ static const char *const reflog_show_usage[] = { | ||||||
| 	NULL, | 	NULL, | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | static const char *const reflog_list_usage[] = { | ||||||
|  | 	BUILTIN_REFLOG_LIST_USAGE, | ||||||
|  | 	NULL, | ||||||
|  | }; | ||||||
|  |  | ||||||
| static const char *const reflog_expire_usage[] = { | static const char *const reflog_expire_usage[] = { | ||||||
| 	BUILTIN_REFLOG_EXPIRE_USAGE, | 	BUILTIN_REFLOG_EXPIRE_USAGE, | ||||||
| 	NULL | 	NULL | ||||||
|  | @ -46,6 +55,7 @@ static const char *const reflog_exists_usage[] = { | ||||||
|  |  | ||||||
| static const char *const reflog_usage[] = { | static const char *const reflog_usage[] = { | ||||||
| 	BUILTIN_REFLOG_SHOW_USAGE, | 	BUILTIN_REFLOG_SHOW_USAGE, | ||||||
|  | 	BUILTIN_REFLOG_LIST_USAGE, | ||||||
| 	BUILTIN_REFLOG_EXPIRE_USAGE, | 	BUILTIN_REFLOG_EXPIRE_USAGE, | ||||||
| 	BUILTIN_REFLOG_DELETE_USAGE, | 	BUILTIN_REFLOG_DELETE_USAGE, | ||||||
| 	BUILTIN_REFLOG_EXISTS_USAGE, | 	BUILTIN_REFLOG_EXISTS_USAGE, | ||||||
|  | @ -60,8 +70,7 @@ struct worktree_reflogs { | ||||||
| 	struct string_list reflogs; | 	struct string_list reflogs; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static int collect_reflog(const char *ref, const struct object_id *oid UNUSED, | static int collect_reflog(const char *ref, void *cb_data) | ||||||
| 			  int flags UNUSED, void *cb_data) |  | ||||||
| { | { | ||||||
| 	struct worktree_reflogs *cb = cb_data; | 	struct worktree_reflogs *cb = cb_data; | ||||||
| 	struct worktree *worktree = cb->worktree; | 	struct worktree *worktree = cb->worktree; | ||||||
|  | @ -238,6 +247,29 @@ static int cmd_reflog_show(int argc, const char **argv, const char *prefix) | ||||||
| 	return cmd_log_reflog(argc, argv, prefix); | 	return cmd_log_reflog(argc, argv, prefix); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static int show_reflog(const char *refname, void *cb_data UNUSED) | ||||||
|  | { | ||||||
|  | 	printf("%s\n", refname); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int cmd_reflog_list(int argc, const char **argv, const char *prefix) | ||||||
|  | { | ||||||
|  | 	struct option options[] = { | ||||||
|  | 		OPT_END() | ||||||
|  | 	}; | ||||||
|  | 	struct ref_store *ref_store; | ||||||
|  |  | ||||||
|  | 	argc = parse_options(argc, argv, prefix, options, reflog_list_usage, 0); | ||||||
|  | 	if (argc) | ||||||
|  | 		return error(_("%s does not accept arguments: '%s'"), | ||||||
|  | 			     "list", argv[0]); | ||||||
|  |  | ||||||
|  | 	ref_store = get_main_ref_store(the_repository); | ||||||
|  |  | ||||||
|  | 	return refs_for_each_reflog(ref_store, show_reflog, NULL); | ||||||
|  | } | ||||||
|  |  | ||||||
| static int cmd_reflog_expire(int argc, const char **argv, const char *prefix) | static int cmd_reflog_expire(int argc, const char **argv, const char *prefix) | ||||||
| { | { | ||||||
| 	struct cmd_reflog_expire_cb cmd = { 0 }; | 	struct cmd_reflog_expire_cb cmd = { 0 }; | ||||||
|  | @ -417,6 +449,7 @@ int cmd_reflog(int argc, const char **argv, const char *prefix) | ||||||
| 	parse_opt_subcommand_fn *fn = NULL; | 	parse_opt_subcommand_fn *fn = NULL; | ||||||
| 	struct option options[] = { | 	struct option options[] = { | ||||||
| 		OPT_SUBCOMMAND("show", &fn, cmd_reflog_show), | 		OPT_SUBCOMMAND("show", &fn, cmd_reflog_show), | ||||||
|  | 		OPT_SUBCOMMAND("list", &fn, cmd_reflog_list), | ||||||
| 		OPT_SUBCOMMAND("expire", &fn, cmd_reflog_expire), | 		OPT_SUBCOMMAND("expire", &fn, cmd_reflog_expire), | ||||||
| 		OPT_SUBCOMMAND("delete", &fn, cmd_reflog_delete), | 		OPT_SUBCOMMAND("delete", &fn, cmd_reflog_delete), | ||||||
| 		OPT_SUBCOMMAND("exists", &fn, cmd_reflog_exists), | 		OPT_SUBCOMMAND("exists", &fn, cmd_reflog_exists), | ||||||
|  |  | ||||||
							
								
								
									
										105
									
								
								dir-iterator.c
								
								
								
								
							
							
						
						
									
										105
									
								
								dir-iterator.c
								
								
								
								
							|  | @ -2,10 +2,19 @@ | ||||||
| #include "dir.h" | #include "dir.h" | ||||||
| #include "iterator.h" | #include "iterator.h" | ||||||
| #include "dir-iterator.h" | #include "dir-iterator.h" | ||||||
|  | #include "string-list.h" | ||||||
|  |  | ||||||
| struct dir_iterator_level { | struct dir_iterator_level { | ||||||
| 	DIR *dir; | 	DIR *dir; | ||||||
|  |  | ||||||
|  | 	/* | ||||||
|  | 	 * The directory entries of the current level. This list will only be | ||||||
|  | 	 * populated when the iterator is ordered. In that case, `dir` will be | ||||||
|  | 	 * set to `NULL`. | ||||||
|  | 	 */ | ||||||
|  | 	struct string_list entries; | ||||||
|  | 	size_t entries_idx; | ||||||
|  |  | ||||||
| 	/* | 	/* | ||||||
| 	 * The length of the directory part of path at this level | 	 * The length of the directory part of path at this level | ||||||
| 	 * (including a trailing '/'): | 	 * (including a trailing '/'): | ||||||
|  | @ -43,6 +52,31 @@ struct dir_iterator_int { | ||||||
| 	unsigned int flags; | 	unsigned int flags; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | static int next_directory_entry(DIR *dir, const char *path, | ||||||
|  | 				struct dirent **out) | ||||||
|  | { | ||||||
|  | 	struct dirent *de; | ||||||
|  |  | ||||||
|  | repeat: | ||||||
|  | 	errno = 0; | ||||||
|  | 	de = readdir(dir); | ||||||
|  | 	if (!de) { | ||||||
|  | 		if (errno) { | ||||||
|  | 			warning_errno("error reading directory '%s'", | ||||||
|  | 				      path); | ||||||
|  | 			return -1; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		return 1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (is_dot_or_dotdot(de->d_name)) | ||||||
|  | 		goto repeat; | ||||||
|  |  | ||||||
|  | 	*out = de; | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
| /* | /* | ||||||
|  * Push a level in the iter stack and initialize it with information from |  * Push a level in the iter stack and initialize it with information from | ||||||
|  * the directory pointed by iter->base->path. It is assumed that this |  * the directory pointed by iter->base->path. It is assumed that this | ||||||
|  | @ -72,6 +106,35 @@ static int push_level(struct dir_iterator_int *iter) | ||||||
| 		return -1; | 		return -1; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	string_list_init_dup(&level->entries); | ||||||
|  | 	level->entries_idx = 0; | ||||||
|  |  | ||||||
|  | 	/* | ||||||
|  | 	 * When the iterator is sorted we read and sort all directory entries | ||||||
|  | 	 * directly. | ||||||
|  | 	 */ | ||||||
|  | 	if (iter->flags & DIR_ITERATOR_SORTED) { | ||||||
|  | 		struct dirent *de; | ||||||
|  |  | ||||||
|  | 		while (1) { | ||||||
|  | 			int ret = next_directory_entry(level->dir, iter->base.path.buf, &de); | ||||||
|  | 			if (ret < 0) { | ||||||
|  | 				if (errno != ENOENT && | ||||||
|  | 				    iter->flags & DIR_ITERATOR_PEDANTIC) | ||||||
|  | 					return -1; | ||||||
|  | 				continue; | ||||||
|  | 			} else if (ret > 0) { | ||||||
|  | 				break; | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			string_list_append(&level->entries, de->d_name); | ||||||
|  | 		} | ||||||
|  | 		string_list_sort(&level->entries); | ||||||
|  |  | ||||||
|  | 		closedir(level->dir); | ||||||
|  | 		level->dir = NULL; | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -88,21 +151,22 @@ static int pop_level(struct dir_iterator_int *iter) | ||||||
| 		warning_errno("error closing directory '%s'", | 		warning_errno("error closing directory '%s'", | ||||||
| 			      iter->base.path.buf); | 			      iter->base.path.buf); | ||||||
| 	level->dir = NULL; | 	level->dir = NULL; | ||||||
|  | 	string_list_clear(&level->entries, 0); | ||||||
|  |  | ||||||
| 	return --iter->levels_nr; | 	return --iter->levels_nr; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* | /* | ||||||
|  * Populate iter->base with the necessary information on the next iteration |  * Populate iter->base with the necessary information on the next iteration | ||||||
|  * entry, represented by the given dirent de. Return 0 on success and -1 |  * entry, represented by the given name. Return 0 on success and -1 | ||||||
|  * otherwise, setting errno accordingly. |  * otherwise, setting errno accordingly. | ||||||
|  */ |  */ | ||||||
| static int prepare_next_entry_data(struct dir_iterator_int *iter, | static int prepare_next_entry_data(struct dir_iterator_int *iter, | ||||||
| 				   struct dirent *de) | 				   const char *name) | ||||||
| { | { | ||||||
| 	int err, saved_errno; | 	int err, saved_errno; | ||||||
|  |  | ||||||
| 	strbuf_addstr(&iter->base.path, de->d_name); | 	strbuf_addstr(&iter->base.path, name); | ||||||
| 	/* | 	/* | ||||||
| 	 * We have to reset these because the path strbuf might have | 	 * We have to reset these because the path strbuf might have | ||||||
| 	 * been realloc()ed at the previous strbuf_addstr(). | 	 * been realloc()ed at the previous strbuf_addstr(). | ||||||
|  | @ -139,27 +203,34 @@ int dir_iterator_advance(struct dir_iterator *dir_iterator) | ||||||
| 		struct dirent *de; | 		struct dirent *de; | ||||||
| 		struct dir_iterator_level *level = | 		struct dir_iterator_level *level = | ||||||
| 			&iter->levels[iter->levels_nr - 1]; | 			&iter->levels[iter->levels_nr - 1]; | ||||||
|  | 		const char *name; | ||||||
|  |  | ||||||
| 		strbuf_setlen(&iter->base.path, level->prefix_len); | 		strbuf_setlen(&iter->base.path, level->prefix_len); | ||||||
| 		errno = 0; |  | ||||||
| 		de = readdir(level->dir); |  | ||||||
|  |  | ||||||
| 		if (!de) { | 		if (level->dir) { | ||||||
| 			if (errno) { | 			int ret = next_directory_entry(level->dir, iter->base.path.buf, &de); | ||||||
| 				warning_errno("error reading directory '%s'", | 			if (ret < 0) { | ||||||
| 					      iter->base.path.buf); |  | ||||||
| 				if (iter->flags & DIR_ITERATOR_PEDANTIC) | 				if (iter->flags & DIR_ITERATOR_PEDANTIC) | ||||||
| 					goto error_out; | 					goto error_out; | ||||||
| 			} else if (pop_level(iter) == 0) { | 				continue; | ||||||
| 				return dir_iterator_abort(dir_iterator); | 			} else if (ret > 0) { | ||||||
|  | 				if (pop_level(iter) == 0) | ||||||
|  | 					return dir_iterator_abort(dir_iterator); | ||||||
|  | 				continue; | ||||||
| 			} | 			} | ||||||
| 			continue; |  | ||||||
|  | 			name = de->d_name; | ||||||
|  | 		} else { | ||||||
|  | 			if (level->entries_idx >= level->entries.nr) { | ||||||
|  | 				if (pop_level(iter) == 0) | ||||||
|  | 					return dir_iterator_abort(dir_iterator); | ||||||
|  | 				continue; | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			name = level->entries.items[level->entries_idx++].string; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if (is_dot_or_dotdot(de->d_name)) | 		if (prepare_next_entry_data(iter, name)) { | ||||||
| 			continue; |  | ||||||
|  |  | ||||||
| 		if (prepare_next_entry_data(iter, de)) { |  | ||||||
| 			if (errno != ENOENT && iter->flags & DIR_ITERATOR_PEDANTIC) | 			if (errno != ENOENT && iter->flags & DIR_ITERATOR_PEDANTIC) | ||||||
| 				goto error_out; | 				goto error_out; | ||||||
| 			continue; | 			continue; | ||||||
|  | @ -188,6 +259,8 @@ int dir_iterator_abort(struct dir_iterator *dir_iterator) | ||||||
| 			warning_errno("error closing directory '%s'", | 			warning_errno("error closing directory '%s'", | ||||||
| 				      iter->base.path.buf); | 				      iter->base.path.buf); | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | 		string_list_clear(&level->entries, 0); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	free(iter->levels); | 	free(iter->levels); | ||||||
|  |  | ||||||
|  | @ -54,8 +54,11 @@ | ||||||
|  *   and ITER_ERROR is returned immediately. In both cases, a meaningful |  *   and ITER_ERROR is returned immediately. In both cases, a meaningful | ||||||
|  *   warning is emitted. Note: ENOENT errors are always ignored so that |  *   warning is emitted. Note: ENOENT errors are always ignored so that | ||||||
|  *   the API users may remove files during iteration. |  *   the API users may remove files during iteration. | ||||||
|  |  * | ||||||
|  |  * - DIR_ITERATOR_SORTED: sort directory entries alphabetically. | ||||||
|  */ |  */ | ||||||
| #define DIR_ITERATOR_PEDANTIC (1 << 0) | #define DIR_ITERATOR_PEDANTIC (1 << 0) | ||||||
|  | #define DIR_ITERATOR_SORTED   (1 << 1) | ||||||
|  |  | ||||||
| struct dir_iterator { | struct dir_iterator { | ||||||
| 	/* The current path: */ | 	/* The current path: */ | ||||||
|  |  | ||||||
							
								
								
									
										27
									
								
								refs.c
								
								
								
								
							
							
						
						
									
										27
									
								
								refs.c
								
								
								
								
							|  | @ -1594,10 +1594,6 @@ struct ref_iterator *refs_ref_iterator_begin( | ||||||
| 	if (trim) | 	if (trim) | ||||||
| 		iter = prefix_ref_iterator_begin(iter, "", trim); | 		iter = prefix_ref_iterator_begin(iter, "", trim); | ||||||
|  |  | ||||||
| 	/* Sanity check for subclasses: */ |  | ||||||
| 	if (!iter->ordered) |  | ||||||
| 		BUG("reference iterator is not ordered"); |  | ||||||
|  |  | ||||||
| 	return iter; | 	return iter; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -2516,18 +2512,33 @@ cleanup: | ||||||
| 	return ret; | 	return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
| int refs_for_each_reflog(struct ref_store *refs, each_ref_fn fn, void *cb_data) | struct do_for_each_reflog_help { | ||||||
|  | 	each_reflog_fn *fn; | ||||||
|  | 	void *cb_data; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static int do_for_each_reflog_helper(struct repository *r UNUSED, | ||||||
|  | 				     const char *refname, | ||||||
|  | 				     const struct object_id *oid UNUSED, | ||||||
|  | 				     int flags, | ||||||
|  | 				     void *cb_data) | ||||||
|  | { | ||||||
|  | 	struct do_for_each_reflog_help *hp = cb_data; | ||||||
|  | 	return hp->fn(refname, hp->cb_data); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int refs_for_each_reflog(struct ref_store *refs, each_reflog_fn fn, void *cb_data) | ||||||
| { | { | ||||||
| 	struct ref_iterator *iter; | 	struct ref_iterator *iter; | ||||||
| 	struct do_for_each_ref_help hp = { fn, cb_data }; | 	struct do_for_each_reflog_help hp = { fn, cb_data }; | ||||||
|  |  | ||||||
| 	iter = refs->be->reflog_iterator_begin(refs); | 	iter = refs->be->reflog_iterator_begin(refs); | ||||||
|  |  | ||||||
| 	return do_for_each_repo_ref_iterator(the_repository, iter, | 	return do_for_each_repo_ref_iterator(the_repository, iter, | ||||||
| 					     do_for_each_ref_helper, &hp); | 					     do_for_each_reflog_helper, &hp); | ||||||
| } | } | ||||||
|  |  | ||||||
| int for_each_reflog(each_ref_fn fn, void *cb_data) | int for_each_reflog(each_reflog_fn fn, void *cb_data) | ||||||
| { | { | ||||||
| 	return refs_for_each_reflog(get_main_ref_store(the_repository), fn, cb_data); | 	return refs_for_each_reflog(get_main_ref_store(the_repository), fn, cb_data); | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										11
									
								
								refs.h
								
								
								
								
							
							
						
						
									
										11
									
								
								refs.h
								
								
								
								
							|  | @ -534,12 +534,19 @@ int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn, void *cb_dat | ||||||
| /* youngest entry first */ | /* youngest entry first */ | ||||||
| int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn, void *cb_data); | int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn, void *cb_data); | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * The signature for the callback function for the {refs_,}for_each_reflog() | ||||||
|  |  * functions below. The memory pointed to by the refname argument is only | ||||||
|  |  * guaranteed to be valid for the duration of a single callback invocation. | ||||||
|  |  */ | ||||||
|  | typedef int each_reflog_fn(const char *refname, void *cb_data); | ||||||
|  |  | ||||||
| /* | /* | ||||||
|  * Calls the specified function for each reflog file until it returns nonzero, |  * Calls the specified function for each reflog file until it returns nonzero, | ||||||
|  * and returns the value. Reflog file order is unspecified. |  * and returns the value. Reflog file order is unspecified. | ||||||
|  */ |  */ | ||||||
| int refs_for_each_reflog(struct ref_store *refs, each_ref_fn fn, void *cb_data); | int refs_for_each_reflog(struct ref_store *refs, each_reflog_fn fn, void *cb_data); | ||||||
| int for_each_reflog(each_ref_fn fn, void *cb_data); | int for_each_reflog(each_reflog_fn fn, void *cb_data); | ||||||
|  |  | ||||||
| #define REFNAME_ALLOW_ONELEVEL 1 | #define REFNAME_ALLOW_ONELEVEL 1 | ||||||
| #define REFNAME_REFSPEC_PATTERN 2 | #define REFNAME_REFSPEC_PATTERN 2 | ||||||
|  |  | ||||||
|  | @ -181,7 +181,6 @@ static int debug_ref_iterator_advance(struct ref_iterator *ref_iterator) | ||||||
| 		trace_printf_key(&trace_refs, "iterator_advance: %s (0)\n", | 		trace_printf_key(&trace_refs, "iterator_advance: %s (0)\n", | ||||||
| 			diter->iter->refname); | 			diter->iter->refname); | ||||||
|  |  | ||||||
| 	diter->base.ordered = diter->iter->ordered; |  | ||||||
| 	diter->base.refname = diter->iter->refname; | 	diter->base.refname = diter->iter->refname; | ||||||
| 	diter->base.oid = diter->iter->oid; | 	diter->base.oid = diter->iter->oid; | ||||||
| 	diter->base.flags = diter->iter->flags; | 	diter->base.flags = diter->iter->flags; | ||||||
|  | @ -222,7 +221,7 @@ debug_ref_iterator_begin(struct ref_store *ref_store, const char *prefix, | ||||||
| 		drefs->refs->be->iterator_begin(drefs->refs, prefix, | 		drefs->refs->be->iterator_begin(drefs->refs, prefix, | ||||||
| 						exclude_patterns, flags); | 						exclude_patterns, flags); | ||||||
| 	struct debug_ref_iterator *diter = xcalloc(1, sizeof(*diter)); | 	struct debug_ref_iterator *diter = xcalloc(1, sizeof(*diter)); | ||||||
| 	base_ref_iterator_init(&diter->base, &debug_ref_iterator_vtable, 1); | 	base_ref_iterator_init(&diter->base, &debug_ref_iterator_vtable); | ||||||
| 	diter->iter = res; | 	diter->iter = res; | ||||||
| 	trace_printf_key(&trace_refs, "ref_iterator_begin: \"%s\" (0x%x)\n", | 	trace_printf_key(&trace_refs, "ref_iterator_begin: \"%s\" (0x%x)\n", | ||||||
| 			 prefix, flags); | 			 prefix, flags); | ||||||
|  |  | ||||||
|  | @ -879,8 +879,7 @@ static struct ref_iterator *files_ref_iterator_begin( | ||||||
|  |  | ||||||
| 	CALLOC_ARRAY(iter, 1); | 	CALLOC_ARRAY(iter, 1); | ||||||
| 	ref_iterator = &iter->base; | 	ref_iterator = &iter->base; | ||||||
| 	base_ref_iterator_init(ref_iterator, &files_ref_iterator_vtable, | 	base_ref_iterator_init(ref_iterator, &files_ref_iterator_vtable); | ||||||
| 			       overlay_iter->ordered); |  | ||||||
| 	iter->iter0 = overlay_iter; | 	iter->iter0 = overlay_iter; | ||||||
| 	iter->repo = ref_store->repo; | 	iter->repo = ref_store->repo; | ||||||
| 	iter->flags = flags; | 	iter->flags = flags; | ||||||
|  | @ -2116,10 +2115,8 @@ static int files_for_each_reflog_ent(struct ref_store *ref_store, | ||||||
|  |  | ||||||
| struct files_reflog_iterator { | struct files_reflog_iterator { | ||||||
| 	struct ref_iterator base; | 	struct ref_iterator base; | ||||||
|  |  | ||||||
| 	struct ref_store *ref_store; | 	struct ref_store *ref_store; | ||||||
| 	struct dir_iterator *dir_iterator; | 	struct dir_iterator *dir_iterator; | ||||||
| 	struct object_id oid; |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static int files_reflog_iterator_advance(struct ref_iterator *ref_iterator) | static int files_reflog_iterator_advance(struct ref_iterator *ref_iterator) | ||||||
|  | @ -2130,25 +2127,13 @@ static int files_reflog_iterator_advance(struct ref_iterator *ref_iterator) | ||||||
| 	int ok; | 	int ok; | ||||||
|  |  | ||||||
| 	while ((ok = dir_iterator_advance(diter)) == ITER_OK) { | 	while ((ok = dir_iterator_advance(diter)) == ITER_OK) { | ||||||
| 		int flags; |  | ||||||
|  |  | ||||||
| 		if (!S_ISREG(diter->st.st_mode)) | 		if (!S_ISREG(diter->st.st_mode)) | ||||||
| 			continue; | 			continue; | ||||||
| 		if (diter->basename[0] == '.') | 		if (check_refname_format(diter->basename, | ||||||
|  | 					 REFNAME_ALLOW_ONELEVEL)) | ||||||
| 			continue; | 			continue; | ||||||
| 		if (ends_with(diter->basename, ".lock")) |  | ||||||
| 			continue; |  | ||||||
|  |  | ||||||
| 		if (!refs_resolve_ref_unsafe(iter->ref_store, |  | ||||||
| 					     diter->relative_path, 0, |  | ||||||
| 					     &iter->oid, &flags)) { |  | ||||||
| 			error("bad ref for %s", diter->path.buf); |  | ||||||
| 			continue; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		iter->base.refname = diter->relative_path; | 		iter->base.refname = diter->relative_path; | ||||||
| 		iter->base.oid = &iter->oid; |  | ||||||
| 		iter->base.flags = flags; |  | ||||||
| 		return ITER_OK; | 		return ITER_OK; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -2193,7 +2178,7 @@ static struct ref_iterator *reflog_iterator_begin(struct ref_store *ref_store, | ||||||
|  |  | ||||||
| 	strbuf_addf(&sb, "%s/logs", gitdir); | 	strbuf_addf(&sb, "%s/logs", gitdir); | ||||||
|  |  | ||||||
| 	diter = dir_iterator_begin(sb.buf, 0); | 	diter = dir_iterator_begin(sb.buf, DIR_ITERATOR_SORTED); | ||||||
| 	if (!diter) { | 	if (!diter) { | ||||||
| 		strbuf_release(&sb); | 		strbuf_release(&sb); | ||||||
| 		return empty_ref_iterator_begin(); | 		return empty_ref_iterator_begin(); | ||||||
|  | @ -2202,7 +2187,7 @@ static struct ref_iterator *reflog_iterator_begin(struct ref_store *ref_store, | ||||||
| 	CALLOC_ARRAY(iter, 1); | 	CALLOC_ARRAY(iter, 1); | ||||||
| 	ref_iterator = &iter->base; | 	ref_iterator = &iter->base; | ||||||
|  |  | ||||||
| 	base_ref_iterator_init(ref_iterator, &files_reflog_iterator_vtable, 0); | 	base_ref_iterator_init(ref_iterator, &files_reflog_iterator_vtable); | ||||||
| 	iter->dir_iterator = diter; | 	iter->dir_iterator = diter; | ||||||
| 	iter->ref_store = ref_store; | 	iter->ref_store = ref_store; | ||||||
| 	strbuf_release(&sb); | 	strbuf_release(&sb); | ||||||
|  | @ -2210,32 +2195,6 @@ static struct ref_iterator *reflog_iterator_begin(struct ref_store *ref_store, | ||||||
| 	return ref_iterator; | 	return ref_iterator; | ||||||
| } | } | ||||||
|  |  | ||||||
| static enum iterator_selection reflog_iterator_select( |  | ||||||
| 	struct ref_iterator *iter_worktree, |  | ||||||
| 	struct ref_iterator *iter_common, |  | ||||||
| 	void *cb_data UNUSED) |  | ||||||
| { |  | ||||||
| 	if (iter_worktree) { |  | ||||||
| 		/* |  | ||||||
| 		 * We're a bit loose here. We probably should ignore |  | ||||||
| 		 * common refs if they are accidentally added as |  | ||||||
| 		 * per-worktree refs. |  | ||||||
| 		 */ |  | ||||||
| 		return ITER_SELECT_0; |  | ||||||
| 	} else if (iter_common) { |  | ||||||
| 		if (parse_worktree_ref(iter_common->refname, NULL, NULL, |  | ||||||
| 				       NULL) == REF_WORKTREE_SHARED) |  | ||||||
| 			return ITER_SELECT_1; |  | ||||||
|  |  | ||||||
| 		/* |  | ||||||
| 		 * The main ref store may contain main worktree's |  | ||||||
| 		 * per-worktree refs, which should be ignored |  | ||||||
| 		 */ |  | ||||||
| 		return ITER_SKIP_1; |  | ||||||
| 	} else |  | ||||||
| 		return ITER_DONE; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_store) | static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_store) | ||||||
| { | { | ||||||
| 	struct files_ref_store *refs = | 	struct files_ref_store *refs = | ||||||
|  | @ -2246,9 +2205,9 @@ static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_st | ||||||
| 		return reflog_iterator_begin(ref_store, refs->gitcommondir); | 		return reflog_iterator_begin(ref_store, refs->gitcommondir); | ||||||
| 	} else { | 	} else { | ||||||
| 		return merge_ref_iterator_begin( | 		return merge_ref_iterator_begin( | ||||||
| 			0, reflog_iterator_begin(ref_store, refs->base.gitdir), | 			reflog_iterator_begin(ref_store, refs->base.gitdir), | ||||||
| 			reflog_iterator_begin(ref_store, refs->gitcommondir), | 			reflog_iterator_begin(ref_store, refs->gitcommondir), | ||||||
| 			reflog_iterator_select, refs); | 			ref_iterator_select, refs); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @ -25,11 +25,9 @@ int ref_iterator_abort(struct ref_iterator *ref_iterator) | ||||||
| } | } | ||||||
|  |  | ||||||
| void base_ref_iterator_init(struct ref_iterator *iter, | void base_ref_iterator_init(struct ref_iterator *iter, | ||||||
| 			    struct ref_iterator_vtable *vtable, | 			    struct ref_iterator_vtable *vtable) | ||||||
| 			    int ordered) |  | ||||||
| { | { | ||||||
| 	iter->vtable = vtable; | 	iter->vtable = vtable; | ||||||
| 	iter->ordered = !!ordered; |  | ||||||
| 	iter->refname = NULL; | 	iter->refname = NULL; | ||||||
| 	iter->oid = NULL; | 	iter->oid = NULL; | ||||||
| 	iter->flags = 0; | 	iter->flags = 0; | ||||||
|  | @ -74,7 +72,7 @@ struct ref_iterator *empty_ref_iterator_begin(void) | ||||||
| 	struct empty_ref_iterator *iter = xcalloc(1, sizeof(*iter)); | 	struct empty_ref_iterator *iter = xcalloc(1, sizeof(*iter)); | ||||||
| 	struct ref_iterator *ref_iterator = &iter->base; | 	struct ref_iterator *ref_iterator = &iter->base; | ||||||
|  |  | ||||||
| 	base_ref_iterator_init(ref_iterator, &empty_ref_iterator_vtable, 1); | 	base_ref_iterator_init(ref_iterator, &empty_ref_iterator_vtable); | ||||||
| 	return ref_iterator; | 	return ref_iterator; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -98,6 +96,49 @@ struct merge_ref_iterator { | ||||||
| 	struct ref_iterator **current; | 	struct ref_iterator **current; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | enum iterator_selection ref_iterator_select(struct ref_iterator *iter_worktree, | ||||||
|  | 					    struct ref_iterator *iter_common, | ||||||
|  | 					    void *cb_data UNUSED) | ||||||
|  | { | ||||||
|  | 	if (iter_worktree && !iter_common) { | ||||||
|  | 		/* | ||||||
|  | 		 * Return the worktree ref if there are no more common refs. | ||||||
|  | 		 */ | ||||||
|  | 		return ITER_SELECT_0; | ||||||
|  | 	} else if (iter_common) { | ||||||
|  | 		/* | ||||||
|  | 		 * In case we have pending worktree and common refs we need to | ||||||
|  | 		 * yield them based on their lexicographical order. Worktree | ||||||
|  | 		 * refs that have the same name as common refs shadow the | ||||||
|  | 		 * latter. | ||||||
|  | 		 */ | ||||||
|  | 		if (iter_worktree) { | ||||||
|  | 			int cmp = strcmp(iter_worktree->refname, | ||||||
|  | 					 iter_common->refname); | ||||||
|  | 			if (cmp < 0) | ||||||
|  | 				return ITER_SELECT_0; | ||||||
|  | 			else if (!cmp) | ||||||
|  | 				return ITER_SELECT_0_SKIP_1; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		 /* | ||||||
|  | 		  * We now know that the lexicographically-next ref is a common | ||||||
|  | 		  * ref. When the common ref is a shared one we return it. | ||||||
|  | 		  */ | ||||||
|  | 		if (parse_worktree_ref(iter_common->refname, NULL, NULL, | ||||||
|  | 				       NULL) == REF_WORKTREE_SHARED) | ||||||
|  | 			return ITER_SELECT_1; | ||||||
|  |  | ||||||
|  | 		/* | ||||||
|  | 		 * Otherwise, if the common ref is a per-worktree ref we skip | ||||||
|  | 		 * it because it would belong to the main worktree, not ours. | ||||||
|  | 		 */ | ||||||
|  | 		return ITER_SKIP_1; | ||||||
|  | 	} else { | ||||||
|  | 		return ITER_DONE; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| static int merge_ref_iterator_advance(struct ref_iterator *ref_iterator) | static int merge_ref_iterator_advance(struct ref_iterator *ref_iterator) | ||||||
| { | { | ||||||
| 	struct merge_ref_iterator *iter = | 	struct merge_ref_iterator *iter = | ||||||
|  | @ -207,7 +248,6 @@ static struct ref_iterator_vtable merge_ref_iterator_vtable = { | ||||||
| }; | }; | ||||||
|  |  | ||||||
| struct ref_iterator *merge_ref_iterator_begin( | struct ref_iterator *merge_ref_iterator_begin( | ||||||
| 		int ordered, |  | ||||||
| 		struct ref_iterator *iter0, struct ref_iterator *iter1, | 		struct ref_iterator *iter0, struct ref_iterator *iter1, | ||||||
| 		ref_iterator_select_fn *select, void *cb_data) | 		ref_iterator_select_fn *select, void *cb_data) | ||||||
| { | { | ||||||
|  | @ -222,7 +262,7 @@ struct ref_iterator *merge_ref_iterator_begin( | ||||||
| 	 * references through only if they exist in both iterators. | 	 * references through only if they exist in both iterators. | ||||||
| 	 */ | 	 */ | ||||||
|  |  | ||||||
| 	base_ref_iterator_init(ref_iterator, &merge_ref_iterator_vtable, ordered); | 	base_ref_iterator_init(ref_iterator, &merge_ref_iterator_vtable); | ||||||
| 	iter->iter0 = iter0; | 	iter->iter0 = iter0; | ||||||
| 	iter->iter1 = iter1; | 	iter->iter1 = iter1; | ||||||
| 	iter->select = select; | 	iter->select = select; | ||||||
|  | @ -271,12 +311,9 @@ struct ref_iterator *overlay_ref_iterator_begin( | ||||||
| 	} else if (is_empty_ref_iterator(back)) { | 	} else if (is_empty_ref_iterator(back)) { | ||||||
| 		ref_iterator_abort(back); | 		ref_iterator_abort(back); | ||||||
| 		return front; | 		return front; | ||||||
| 	} else if (!front->ordered || !back->ordered) { |  | ||||||
| 		BUG("overlay_ref_iterator requires ordered inputs"); |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return merge_ref_iterator_begin(1, front, back, | 	return merge_ref_iterator_begin(front, back, overlay_iterator_select, NULL); | ||||||
| 					overlay_iterator_select, NULL); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| struct prefix_ref_iterator { | struct prefix_ref_iterator { | ||||||
|  | @ -315,16 +352,12 @@ static int prefix_ref_iterator_advance(struct ref_iterator *ref_iterator) | ||||||
|  |  | ||||||
| 		if (cmp > 0) { | 		if (cmp > 0) { | ||||||
| 			/* | 			/* | ||||||
| 			 * If the source iterator is ordered, then we | 			 * As the source iterator is ordered, we | ||||||
| 			 * can stop the iteration as soon as we see a | 			 * can stop the iteration as soon as we see a | ||||||
| 			 * refname that comes after the prefix: | 			 * refname that comes after the prefix: | ||||||
| 			 */ | 			 */ | ||||||
| 			if (iter->iter0->ordered) { | 			ok = ref_iterator_abort(iter->iter0); | ||||||
| 				ok = ref_iterator_abort(iter->iter0); | 			break; | ||||||
| 				break; |  | ||||||
| 			} else { |  | ||||||
| 				continue; |  | ||||||
| 			} |  | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if (iter->trim) { | 		if (iter->trim) { | ||||||
|  | @ -396,7 +429,7 @@ struct ref_iterator *prefix_ref_iterator_begin(struct ref_iterator *iter0, | ||||||
| 	CALLOC_ARRAY(iter, 1); | 	CALLOC_ARRAY(iter, 1); | ||||||
| 	ref_iterator = &iter->base; | 	ref_iterator = &iter->base; | ||||||
|  |  | ||||||
| 	base_ref_iterator_init(ref_iterator, &prefix_ref_iterator_vtable, iter0->ordered); | 	base_ref_iterator_init(ref_iterator, &prefix_ref_iterator_vtable); | ||||||
|  |  | ||||||
| 	iter->iter0 = iter0; | 	iter->iter0 = iter0; | ||||||
| 	iter->prefix = xstrdup(prefix); | 	iter->prefix = xstrdup(prefix); | ||||||
|  |  | ||||||
|  | @ -1111,7 +1111,7 @@ static struct ref_iterator *packed_ref_iterator_begin( | ||||||
|  |  | ||||||
| 	CALLOC_ARRAY(iter, 1); | 	CALLOC_ARRAY(iter, 1); | ||||||
| 	ref_iterator = &iter->base; | 	ref_iterator = &iter->base; | ||||||
| 	base_ref_iterator_init(ref_iterator, &packed_ref_iterator_vtable, 1); | 	base_ref_iterator_init(ref_iterator, &packed_ref_iterator_vtable); | ||||||
|  |  | ||||||
| 	if (exclude_patterns) | 	if (exclude_patterns) | ||||||
| 		populate_excluded_jump_list(iter, snapshot, exclude_patterns); | 		populate_excluded_jump_list(iter, snapshot, exclude_patterns); | ||||||
|  |  | ||||||
|  | @ -486,7 +486,7 @@ struct ref_iterator *cache_ref_iterator_begin(struct ref_cache *cache, | ||||||
|  |  | ||||||
| 	CALLOC_ARRAY(iter, 1); | 	CALLOC_ARRAY(iter, 1); | ||||||
| 	ref_iterator = &iter->base; | 	ref_iterator = &iter->base; | ||||||
| 	base_ref_iterator_init(ref_iterator, &cache_ref_iterator_vtable, 1); | 	base_ref_iterator_init(ref_iterator, &cache_ref_iterator_vtable); | ||||||
| 	ALLOC_GROW(iter->levels, 10, iter->levels_alloc); | 	ALLOC_GROW(iter->levels, 10, iter->levels_alloc); | ||||||
|  |  | ||||||
| 	iter->levels_nr = 1; | 	iter->levels_nr = 1; | ||||||
|  |  | ||||||
|  | @ -312,13 +312,6 @@ enum do_for_each_ref_flags { | ||||||
|  */ |  */ | ||||||
| struct ref_iterator { | struct ref_iterator { | ||||||
| 	struct ref_iterator_vtable *vtable; | 	struct ref_iterator_vtable *vtable; | ||||||
|  |  | ||||||
| 	/* |  | ||||||
| 	 * Does this `ref_iterator` iterate over references in order |  | ||||||
| 	 * by refname? |  | ||||||
| 	 */ |  | ||||||
| 	unsigned int ordered : 1; |  | ||||||
|  |  | ||||||
| 	const char *refname; | 	const char *refname; | ||||||
| 	const struct object_id *oid; | 	const struct object_id *oid; | ||||||
| 	unsigned int flags; | 	unsigned int flags; | ||||||
|  | @ -386,15 +379,22 @@ typedef enum iterator_selection ref_iterator_select_fn( | ||||||
| 		struct ref_iterator *iter0, struct ref_iterator *iter1, | 		struct ref_iterator *iter0, struct ref_iterator *iter1, | ||||||
| 		void *cb_data); | 		void *cb_data); | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * An implementation of ref_iterator_select_fn that merges worktree and common | ||||||
|  |  * refs. Per-worktree refs from the common iterator are ignored, worktree refs | ||||||
|  |  * override common refs. Refs are selected lexicographically. | ||||||
|  |  */ | ||||||
|  | enum iterator_selection ref_iterator_select(struct ref_iterator *iter_worktree, | ||||||
|  | 					    struct ref_iterator *iter_common, | ||||||
|  | 					    void *cb_data); | ||||||
|  |  | ||||||
| /* | /* | ||||||
|  * Iterate over the entries from iter0 and iter1, with the values |  * Iterate over the entries from iter0 and iter1, with the values | ||||||
|  * interleaved as directed by the select function. The iterator takes |  * interleaved as directed by the select function. The iterator takes | ||||||
|  * ownership of iter0 and iter1 and frees them when the iteration is |  * ownership of iter0 and iter1 and frees them when the iteration is | ||||||
|  * over. A derived class should set `ordered` to 1 or 0 based on |  * over. | ||||||
|  * whether it generates its output in order by reference name. |  | ||||||
|  */ |  */ | ||||||
| struct ref_iterator *merge_ref_iterator_begin( | struct ref_iterator *merge_ref_iterator_begin( | ||||||
| 		int ordered, |  | ||||||
| 		struct ref_iterator *iter0, struct ref_iterator *iter1, | 		struct ref_iterator *iter0, struct ref_iterator *iter1, | ||||||
| 		ref_iterator_select_fn *select, void *cb_data); | 		ref_iterator_select_fn *select, void *cb_data); | ||||||
|  |  | ||||||
|  | @ -423,8 +423,6 @@ struct ref_iterator *overlay_ref_iterator_begin( | ||||||
|  * As an convenience to callers, if prefix is the empty string and |  * As an convenience to callers, if prefix is the empty string and | ||||||
|  * trim is zero, this function returns iter0 directly, without |  * trim is zero, this function returns iter0 directly, without | ||||||
|  * wrapping it. |  * wrapping it. | ||||||
|  * |  | ||||||
|  * The resulting ref_iterator is ordered if iter0 is. |  | ||||||
|  */ |  */ | ||||||
| struct ref_iterator *prefix_ref_iterator_begin(struct ref_iterator *iter0, | struct ref_iterator *prefix_ref_iterator_begin(struct ref_iterator *iter0, | ||||||
| 					       const char *prefix, | 					       const char *prefix, | ||||||
|  | @ -435,14 +433,11 @@ struct ref_iterator *prefix_ref_iterator_begin(struct ref_iterator *iter0, | ||||||
| /* | /* | ||||||
|  * Base class constructor for ref_iterators. Initialize the |  * Base class constructor for ref_iterators. Initialize the | ||||||
|  * ref_iterator part of iter, setting its vtable pointer as specified. |  * ref_iterator part of iter, setting its vtable pointer as specified. | ||||||
|  * `ordered` should be set to 1 if the iterator will iterate over |  | ||||||
|  * references in order by refname; otherwise it should be set to 0. |  | ||||||
|  * This is meant to be called only by the initializers of derived |  * This is meant to be called only by the initializers of derived | ||||||
|  * classes. |  * classes. | ||||||
|  */ |  */ | ||||||
| void base_ref_iterator_init(struct ref_iterator *iter, | void base_ref_iterator_init(struct ref_iterator *iter, | ||||||
| 			    struct ref_iterator_vtable *vtable, | 			    struct ref_iterator_vtable *vtable); | ||||||
| 			    int ordered); |  | ||||||
|  |  | ||||||
| /* | /* | ||||||
|  * Base class destructor for ref_iterators. Destroy the ref_iterator |  * Base class destructor for ref_iterators. Destroy the ref_iterator | ||||||
|  |  | ||||||
|  | @ -479,7 +479,7 @@ static struct reftable_ref_iterator *ref_iterator_for_stack(struct reftable_ref_ | ||||||
| 	int ret; | 	int ret; | ||||||
|  |  | ||||||
| 	iter = xcalloc(1, sizeof(*iter)); | 	iter = xcalloc(1, sizeof(*iter)); | ||||||
| 	base_ref_iterator_init(&iter->base, &reftable_ref_iterator_vtable, 1); | 	base_ref_iterator_init(&iter->base, &reftable_ref_iterator_vtable); | ||||||
| 	iter->prefix = prefix; | 	iter->prefix = prefix; | ||||||
| 	iter->base.oid = &iter->oid; | 	iter->base.oid = &iter->oid; | ||||||
| 	iter->flags = flags; | 	iter->flags = flags; | ||||||
|  | @ -504,49 +504,6 @@ done: | ||||||
| 	return iter; | 	return iter; | ||||||
| } | } | ||||||
|  |  | ||||||
| static enum iterator_selection iterator_select(struct ref_iterator *iter_worktree, |  | ||||||
| 					       struct ref_iterator *iter_common, |  | ||||||
| 					       void *cb_data UNUSED) |  | ||||||
| { |  | ||||||
| 	if (iter_worktree && !iter_common) { |  | ||||||
| 		/* |  | ||||||
| 		 * Return the worktree ref if there are no more common refs. |  | ||||||
| 		 */ |  | ||||||
| 		return ITER_SELECT_0; |  | ||||||
| 	} else if (iter_common) { |  | ||||||
| 		/* |  | ||||||
| 		 * In case we have pending worktree and common refs we need to |  | ||||||
| 		 * yield them based on their lexicographical order. Worktree |  | ||||||
| 		 * refs that have the same name as common refs shadow the |  | ||||||
| 		 * latter. |  | ||||||
| 		 */ |  | ||||||
| 		if (iter_worktree) { |  | ||||||
| 			int cmp = strcmp(iter_worktree->refname, |  | ||||||
| 					 iter_common->refname); |  | ||||||
| 			if (cmp < 0) |  | ||||||
| 				return ITER_SELECT_0; |  | ||||||
| 			else if (!cmp) |  | ||||||
| 				return ITER_SELECT_0_SKIP_1; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		 /* |  | ||||||
| 		  * We now know that the lexicographically-next ref is a common |  | ||||||
| 		  * ref. When the common ref is a shared one we return it. |  | ||||||
| 		  */ |  | ||||||
| 		if (parse_worktree_ref(iter_common->refname, NULL, NULL, |  | ||||||
| 				       NULL) == REF_WORKTREE_SHARED) |  | ||||||
| 			return ITER_SELECT_1; |  | ||||||
|  |  | ||||||
| 		/* |  | ||||||
| 		 * Otherwise, if the common ref is a per-worktree ref we skip |  | ||||||
| 		 * it because it would belong to the main worktree, not ours. |  | ||||||
| 		 */ |  | ||||||
| 		return ITER_SKIP_1; |  | ||||||
| 	} else { |  | ||||||
| 		return ITER_DONE; |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static struct ref_iterator *reftable_be_iterator_begin(struct ref_store *ref_store, | static struct ref_iterator *reftable_be_iterator_begin(struct ref_store *ref_store, | ||||||
| 						       const char *prefix, | 						       const char *prefix, | ||||||
| 						       const char **exclude_patterns, | 						       const char **exclude_patterns, | ||||||
|  | @ -575,8 +532,8 @@ static struct ref_iterator *reftable_be_iterator_begin(struct ref_store *ref_sto | ||||||
| 	 * single iterator. | 	 * single iterator. | ||||||
| 	 */ | 	 */ | ||||||
| 	worktree_iter = ref_iterator_for_stack(refs, refs->worktree_stack, prefix, flags); | 	worktree_iter = ref_iterator_for_stack(refs, refs->worktree_stack, prefix, flags); | ||||||
| 	return merge_ref_iterator_begin(1, &worktree_iter->base, &main_iter->base, | 	return merge_ref_iterator_begin(&worktree_iter->base, &main_iter->base, | ||||||
| 					iterator_select, NULL); | 					ref_iterator_select, NULL); | ||||||
| } | } | ||||||
|  |  | ||||||
| static int reftable_be_read_raw_ref(struct ref_store *ref_store, | static int reftable_be_read_raw_ref(struct ref_store *ref_store, | ||||||
|  | @ -1637,7 +1594,6 @@ struct reftable_reflog_iterator { | ||||||
| 	struct reftable_ref_store *refs; | 	struct reftable_ref_store *refs; | ||||||
| 	struct reftable_iterator iter; | 	struct reftable_iterator iter; | ||||||
| 	struct reftable_log_record log; | 	struct reftable_log_record log; | ||||||
| 	struct object_id oid; |  | ||||||
| 	char *last_name; | 	char *last_name; | ||||||
| 	int err; | 	int err; | ||||||
| }; | }; | ||||||
|  | @ -1648,8 +1604,6 @@ static int reftable_reflog_iterator_advance(struct ref_iterator *ref_iterator) | ||||||
| 		(struct reftable_reflog_iterator *)ref_iterator; | 		(struct reftable_reflog_iterator *)ref_iterator; | ||||||
|  |  | ||||||
| 	while (!iter->err) { | 	while (!iter->err) { | ||||||
| 		int flags; |  | ||||||
|  |  | ||||||
| 		iter->err = reftable_iterator_next_log(&iter->iter, &iter->log); | 		iter->err = reftable_iterator_next_log(&iter->iter, &iter->log); | ||||||
| 		if (iter->err) | 		if (iter->err) | ||||||
| 			break; | 			break; | ||||||
|  | @ -1662,17 +1616,13 @@ static int reftable_reflog_iterator_advance(struct ref_iterator *ref_iterator) | ||||||
| 		if (iter->last_name && !strcmp(iter->log.refname, iter->last_name)) | 		if (iter->last_name && !strcmp(iter->log.refname, iter->last_name)) | ||||||
| 			continue; | 			continue; | ||||||
|  |  | ||||||
| 		if (!refs_resolve_ref_unsafe(&iter->refs->base, iter->log.refname, | 		if (check_refname_format(iter->log.refname, | ||||||
| 					     0, &iter->oid, &flags)) { | 					 REFNAME_ALLOW_ONELEVEL)) | ||||||
| 			error(_("bad ref for %s"), iter->log.refname); |  | ||||||
| 			continue; | 			continue; | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		free(iter->last_name); | 		free(iter->last_name); | ||||||
| 		iter->last_name = xstrdup(iter->log.refname); | 		iter->last_name = xstrdup(iter->log.refname); | ||||||
| 		iter->base.refname = iter->log.refname; | 		iter->base.refname = iter->log.refname; | ||||||
| 		iter->base.oid = &iter->oid; |  | ||||||
| 		iter->base.flags = flags; |  | ||||||
|  |  | ||||||
| 		break; | 		break; | ||||||
| 	} | 	} | ||||||
|  | @ -1723,9 +1673,8 @@ static struct reftable_reflog_iterator *reflog_iterator_for_stack(struct reftabl | ||||||
| 	int ret; | 	int ret; | ||||||
|  |  | ||||||
| 	iter = xcalloc(1, sizeof(*iter)); | 	iter = xcalloc(1, sizeof(*iter)); | ||||||
| 	base_ref_iterator_init(&iter->base, &reftable_reflog_iterator_vtable, 1); | 	base_ref_iterator_init(&iter->base, &reftable_reflog_iterator_vtable); | ||||||
| 	iter->refs = refs; | 	iter->refs = refs; | ||||||
| 	iter->base.oid = &iter->oid; |  | ||||||
|  |  | ||||||
| 	ret = refs->err; | 	ret = refs->err; | ||||||
| 	if (ret) | 	if (ret) | ||||||
|  | @ -1758,8 +1707,8 @@ static struct ref_iterator *reftable_be_reflog_iterator_begin(struct ref_store * | ||||||
|  |  | ||||||
| 	worktree_iter = reflog_iterator_for_stack(refs, refs->worktree_stack); | 	worktree_iter = reflog_iterator_for_stack(refs, refs->worktree_stack); | ||||||
|  |  | ||||||
| 	return merge_ref_iterator_begin(1, &worktree_iter->base, &main_iter->base, | 	return merge_ref_iterator_begin(&worktree_iter->base, &main_iter->base, | ||||||
| 					iterator_select, NULL); | 					ref_iterator_select, NULL); | ||||||
| } | } | ||||||
|  |  | ||||||
| static int yield_log_record(struct reftable_log_record *log, | static int yield_log_record(struct reftable_log_record *log, | ||||||
|  |  | ||||||
|  | @ -1686,9 +1686,7 @@ static int handle_one_reflog_ent(struct object_id *ooid, struct object_id *noid, | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static int handle_one_reflog(const char *refname_in_wt, | static int handle_one_reflog(const char *refname_in_wt, void *cb_data) | ||||||
| 			     const struct object_id *oid UNUSED, |  | ||||||
| 			     int flag UNUSED, void *cb_data) |  | ||||||
| { | { | ||||||
| 	struct all_refs_cb *cb = cb_data; | 	struct all_refs_cb *cb = cb_data; | ||||||
| 	struct strbuf refname = STRBUF_INIT; | 	struct strbuf refname = STRBUF_INIT; | ||||||
|  |  | ||||||
|  | @ -221,15 +221,21 @@ static int cmd_verify_ref(struct ref_store *refs, const char **argv) | ||||||
| 	return ret; | 	return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static int each_reflog(const char *refname, void *cb_data UNUSED) | ||||||
|  | { | ||||||
|  | 	printf("%s\n", refname); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
| static int cmd_for_each_reflog(struct ref_store *refs, | static int cmd_for_each_reflog(struct ref_store *refs, | ||||||
| 			       const char **argv UNUSED) | 			       const char **argv UNUSED) | ||||||
| { | { | ||||||
| 	return refs_for_each_reflog(refs, each_ref, NULL); | 	return refs_for_each_reflog(refs, each_reflog, NULL); | ||||||
| } | } | ||||||
|  |  | ||||||
| static int each_reflog(struct object_id *old_oid, struct object_id *new_oid, | static int each_reflog_ent(struct object_id *old_oid, struct object_id *new_oid, | ||||||
| 		       const char *committer, timestamp_t timestamp, | 			   const char *committer, timestamp_t timestamp, | ||||||
| 		       int tz, const char *msg, void *cb_data UNUSED) | 			   int tz, const char *msg, void *cb_data UNUSED) | ||||||
| { | { | ||||||
| 	printf("%s %s %s %" PRItime " %+05d%s%s", oid_to_hex(old_oid), | 	printf("%s %s %s %" PRItime " %+05d%s%s", oid_to_hex(old_oid), | ||||||
| 	       oid_to_hex(new_oid), committer, timestamp, tz, | 	       oid_to_hex(new_oid), committer, timestamp, tz, | ||||||
|  | @ -241,14 +247,14 @@ static int cmd_for_each_reflog_ent(struct ref_store *refs, const char **argv) | ||||||
| { | { | ||||||
| 	const char *refname = notnull(*argv++, "refname"); | 	const char *refname = notnull(*argv++, "refname"); | ||||||
|  |  | ||||||
| 	return refs_for_each_reflog_ent(refs, refname, each_reflog, refs); | 	return refs_for_each_reflog_ent(refs, refname, each_reflog_ent, refs); | ||||||
| } | } | ||||||
|  |  | ||||||
| static int cmd_for_each_reflog_ent_reverse(struct ref_store *refs, const char **argv) | static int cmd_for_each_reflog_ent_reverse(struct ref_store *refs, const char **argv) | ||||||
| { | { | ||||||
| 	const char *refname = notnull(*argv++, "refname"); | 	const char *refname = notnull(*argv++, "refname"); | ||||||
|  |  | ||||||
| 	return refs_for_each_reflog_ent_reverse(refs, refname, each_reflog, refs); | 	return refs_for_each_reflog_ent_reverse(refs, refname, each_reflog_ent, refs); | ||||||
| } | } | ||||||
|  |  | ||||||
| static int cmd_reflog_exists(struct ref_store *refs, const char **argv) | static int cmd_reflog_exists(struct ref_store *refs, const char **argv) | ||||||
|  |  | ||||||
|  | @ -287,23 +287,23 @@ test_expect_success 'for_each_reflog()' ' | ||||||
| 	mkdir -p     .git/worktrees/wt/logs/refs/bisect && | 	mkdir -p     .git/worktrees/wt/logs/refs/bisect && | ||||||
| 	echo $ZERO_OID > .git/worktrees/wt/logs/refs/bisect/wt-random && | 	echo $ZERO_OID > .git/worktrees/wt/logs/refs/bisect/wt-random && | ||||||
|  |  | ||||||
| 	$RWT for-each-reflog | cut -d" " -f 2- | sort >actual && | 	$RWT for-each-reflog >actual && | ||||||
| 	cat >expected <<-\EOF && | 	cat >expected <<-\EOF && | ||||||
| 	HEAD 0x1 | 	HEAD | ||||||
| 	PSEUDO-WT 0x0 | 	PSEUDO-WT | ||||||
| 	refs/bisect/wt-random 0x0 | 	refs/bisect/wt-random | ||||||
| 	refs/heads/main 0x0 | 	refs/heads/main | ||||||
| 	refs/heads/wt-main 0x0 | 	refs/heads/wt-main | ||||||
| 	EOF | 	EOF | ||||||
| 	test_cmp expected actual && | 	test_cmp expected actual && | ||||||
|  |  | ||||||
| 	$RMAIN for-each-reflog | cut -d" " -f 2- | sort >actual && | 	$RMAIN for-each-reflog >actual && | ||||||
| 	cat >expected <<-\EOF && | 	cat >expected <<-\EOF && | ||||||
| 	HEAD 0x1 | 	HEAD | ||||||
| 	PSEUDO-MAIN 0x0 | 	PSEUDO-MAIN | ||||||
| 	refs/bisect/random 0x0 | 	refs/bisect/random | ||||||
| 	refs/heads/main 0x0 | 	refs/heads/main | ||||||
| 	refs/heads/wt-main 0x0 | 	refs/heads/wt-main | ||||||
| 	EOF | 	EOF | ||||||
| 	test_cmp expected actual | 	test_cmp expected actual | ||||||
| ' | ' | ||||||
|  |  | ||||||
|  | @ -68,11 +68,11 @@ test_expect_success 'verify_ref(new-main)' ' | ||||||
| ' | ' | ||||||
|  |  | ||||||
| test_expect_success 'for_each_reflog()' ' | test_expect_success 'for_each_reflog()' ' | ||||||
| 	$RUN for-each-reflog | sort -k2 | cut -d" " -f 2- >actual && | 	$RUN for-each-reflog >actual && | ||||||
| 	cat >expected <<-\EOF && | 	cat >expected <<-\EOF && | ||||||
| 	HEAD 0x1 | 	HEAD | ||||||
| 	refs/heads/main 0x0 | 	refs/heads/main | ||||||
| 	refs/heads/new-main 0x0 | 	refs/heads/new-main | ||||||
| 	EOF | 	EOF | ||||||
| 	test_cmp expected actual | 	test_cmp expected actual | ||||||
| ' | ' | ||||||
|  |  | ||||||
|  | @ -63,11 +63,11 @@ test_expect_success 'verify_ref(new-main)' ' | ||||||
| ' | ' | ||||||
|  |  | ||||||
| test_expect_success 'for_each_reflog()' ' | test_expect_success 'for_each_reflog()' ' | ||||||
| 	$RUN for-each-reflog | sort | cut -d" " -f 2- >actual && | 	$RUN for-each-reflog >actual && | ||||||
| 	cat >expected <<-\EOF && | 	cat >expected <<-\EOF && | ||||||
| 	HEAD 0x1 | 	HEAD | ||||||
| 	refs/heads/main 0x0 | 	refs/heads/main | ||||||
| 	refs/heads/new-main 0x0 | 	refs/heads/new-main | ||||||
| 	EOF | 	EOF | ||||||
| 	test_cmp expected actual | 	test_cmp expected actual | ||||||
| ' | ' | ||||||
|  |  | ||||||
|  | @ -436,4 +436,112 @@ test_expect_success 'empty reflog' ' | ||||||
| 	test_must_be_empty err | 	test_must_be_empty err | ||||||
| ' | ' | ||||||
|  |  | ||||||
|  | test_expect_success 'list reflogs' ' | ||||||
|  | 	test_when_finished "rm -rf repo" && | ||||||
|  | 	git init repo && | ||||||
|  | 	( | ||||||
|  | 		cd repo && | ||||||
|  | 		git reflog list >actual && | ||||||
|  | 		test_must_be_empty actual && | ||||||
|  |  | ||||||
|  | 		test_commit A && | ||||||
|  | 		cat >expect <<-EOF && | ||||||
|  | 		HEAD | ||||||
|  | 		refs/heads/main | ||||||
|  | 		EOF | ||||||
|  | 		git reflog list >actual && | ||||||
|  | 		test_cmp expect actual && | ||||||
|  |  | ||||||
|  | 		git branch b && | ||||||
|  | 		cat >expect <<-EOF && | ||||||
|  | 		HEAD | ||||||
|  | 		refs/heads/b | ||||||
|  | 		refs/heads/main | ||||||
|  | 		EOF | ||||||
|  | 		git reflog list >actual && | ||||||
|  | 		test_cmp expect actual | ||||||
|  | 	) | ||||||
|  | ' | ||||||
|  |  | ||||||
|  | test_expect_success 'list reflogs with worktree' ' | ||||||
|  | 	test_when_finished "rm -rf repo" && | ||||||
|  | 	git init repo && | ||||||
|  | 	( | ||||||
|  | 		cd repo && | ||||||
|  |  | ||||||
|  | 		test_commit A && | ||||||
|  | 		git worktree add wt && | ||||||
|  | 		git -c core.logAllRefUpdates=always \ | ||||||
|  | 			update-ref refs/worktree/main HEAD && | ||||||
|  | 		git -c core.logAllRefUpdates=always \ | ||||||
|  | 			update-ref refs/worktree/per-worktree HEAD && | ||||||
|  | 		git -c core.logAllRefUpdates=always -C wt \ | ||||||
|  | 			update-ref refs/worktree/per-worktree HEAD && | ||||||
|  | 		git -c core.logAllRefUpdates=always -C wt \ | ||||||
|  | 			update-ref refs/worktree/worktree HEAD && | ||||||
|  |  | ||||||
|  | 		cat >expect <<-EOF && | ||||||
|  | 		HEAD | ||||||
|  | 		refs/heads/main | ||||||
|  | 		refs/heads/wt | ||||||
|  | 		refs/worktree/main | ||||||
|  | 		refs/worktree/per-worktree | ||||||
|  | 		EOF | ||||||
|  | 		git reflog list >actual && | ||||||
|  | 		test_cmp expect actual && | ||||||
|  |  | ||||||
|  | 		cat >expect <<-EOF && | ||||||
|  | 		HEAD | ||||||
|  | 		refs/heads/main | ||||||
|  | 		refs/heads/wt | ||||||
|  | 		refs/worktree/per-worktree | ||||||
|  | 		refs/worktree/worktree | ||||||
|  | 		EOF | ||||||
|  | 		git -C wt reflog list >actual && | ||||||
|  | 		test_cmp expect actual | ||||||
|  | 	) | ||||||
|  | ' | ||||||
|  |  | ||||||
|  | test_expect_success 'reflog list returns error with additional args' ' | ||||||
|  | 	cat >expect <<-EOF && | ||||||
|  | 	error: list does not accept arguments: ${SQ}bogus${SQ} | ||||||
|  | 	EOF | ||||||
|  | 	test_must_fail git reflog list bogus 2>err && | ||||||
|  | 	test_cmp expect err | ||||||
|  | ' | ||||||
|  |  | ||||||
|  | test_expect_success 'reflog for symref with unborn target can be listed' ' | ||||||
|  | 	test_when_finished "rm -rf repo" && | ||||||
|  | 	git init repo && | ||||||
|  | 	( | ||||||
|  | 		cd repo && | ||||||
|  | 		test_commit A && | ||||||
|  | 		git symbolic-ref HEAD refs/heads/unborn && | ||||||
|  | 		cat >expect <<-EOF && | ||||||
|  | 		HEAD | ||||||
|  | 		refs/heads/main | ||||||
|  | 		EOF | ||||||
|  | 		git reflog list >actual && | ||||||
|  | 		test_cmp expect actual | ||||||
|  | 	) | ||||||
|  | ' | ||||||
|  |  | ||||||
|  | test_expect_success 'reflog with invalid object ID can be listed' ' | ||||||
|  | 	test_when_finished "rm -rf repo" && | ||||||
|  | 	git init repo && | ||||||
|  | 	( | ||||||
|  | 		cd repo && | ||||||
|  | 		test_commit A && | ||||||
|  | 		test-tool ref-store main update-ref msg refs/heads/missing \ | ||||||
|  | 			$(test_oid deadbeef) "$ZERO_OID" REF_SKIP_OID_VERIFICATION && | ||||||
|  | 		cat >expect <<-EOF && | ||||||
|  | 		HEAD | ||||||
|  | 		refs/heads/main | ||||||
|  | 		refs/heads/missing | ||||||
|  | 		EOF | ||||||
|  | 		git reflog list >actual && | ||||||
|  | 		test_cmp expect actual | ||||||
|  | 	) | ||||||
|  | ' | ||||||
|  |  | ||||||
| test_done | test_done | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	 Junio C Hamano
						Junio C Hamano