Subject: Re: [PATCH v5 3/4] builtin/stash: provide a way to export stashes to a ref

From: Junio C Hamano <[email-masked]>
Date: Fri, 09 May 2025 12:31:11 -0700

"brian m. carlson" <[email-masked]> writes:

> +static int do_export_stash(struct repository *r,
> +			   const char *ref,
> +			   int argc,
> +			   const char **argv)
> +{
> +...
> +	if (argc) {
> +		/*
> +		 * Find each specified stash, and load data into the array.
> +		 */
> +		for (i = 0; i < argc; i++) {
> +			struct object_id oid;
> +			if (parse_revision(&revision, argv[i], 1) ||
> +			    get_oid_with_context(r, revision.buf,
> +						 GET_OID_QUIETLY | GET_OID_GENTLY,
> +						 &oid, &unused)) {
> +				res = error(_("unable to find stash entry %s"), argv[i]);
> +				goto out;
> +			}
> +			oid_array_append(&items, &oid);
> +		}

Grabbing individual reflog entry given on the command line looks
trivial, but ...

> +	} else {
> +		/*
> +		 * Walk the reflog, finding each stash entry, and load data into the
> +		 * array.
> +		 */
> +		for (i = 0;; i++) {
> +			char buf[32];
> +			struct object_id oid;
> +
> +			snprintf(buf, sizeof(buf), "%d", i);
> +			if (parse_revision(&revision, buf, 1) ||
> +			    get_oid_with_context(r, revision.buf,
> +						 GET_OID_QUIETLY | GET_OID_GENTLY,
> +						 &oid, &unused))
> +				break;
> +			oid_array_append(&items, &oid);
> +		}

... have you considered reusing reflog-walk.c:read_complete_reflog()
as a helper function?  

Doing so of would be more efficient than going from int to string
back to int to call read_ref_at() and iterate over the same reflog
entries with refs_for_each_reflog_ent().

> +	}
> +
> +	/*
> +	 * Now, create a set of commits identical to the regular stash commits,
> +	 * but where their first parents form a chain to our original empty
> +	 * base commit.
> +	 */
> +	for (i = items.nr - 1; i >= 0; i--) {
> +		struct commit_list *parents = NULL;
> +		struct commit_list **next = &parents;
> +		struct object_id out;
> +		const struct object_id *oid = items.oid + i;
> +
> +		next = commit_list_append(prev, next);
> +		next = commit_list_append(lookup_commit_reference(r, oid), next);

The individual-reflog-entry mode above was fairly strict in that a
list of reflog entries with even one unreadable commit caused the
whole command to fail, but reflog-walk mode assumed that a failure
to read an entry must always be due to reflog entries running out
due to the index incremented to a large enough number.  I suspect
get_oid_with_context() can give you oid obtained out of a reflog
entry without actually parsing the object or checking if it exists.

Should we be a bit more defensinve here in lookup_commit_reference()
call, which would silently throw a NULL back at us if the commit
cannot be found without complaining?