git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Junio C Hamano <gitster@pobox.com>
To: "brian m. carlson" <sandals@crustytoothpaste.net>
Cc: <git@vger.kernel.org>,  "D. Ben Knoble" <ben.knoble@gmail.com>
Subject: Re: [PATCH v6 5/5] builtin/stash: provide a way to import stashes from a ref
Date: Thu, 22 May 2025 14:09:23 -0700	[thread overview]
Message-ID: <xmqqcyc04akc.fsf@gitster.g> (raw)
In-Reply-To: <20250522185524.18398-7-sandals@crustytoothpaste.net> (brian m. carlson's message of "Thu, 22 May 2025 18:55:24 +0000")

"brian m. carlson" <sandals@crustytoothpaste.net> writes:

> We don't clear the stash first and instead add the specified stashes to
> the top of the stash.  This is because users may want to export just a
> few stashes, such as to share a small amount of work in progress with a
> colleague, and it would be undesirable for the receiving user to lose
> all of their data.  For users who do want to replace the stash, it's
> easy to do to: simply run "git stash clear" first.

Very sensible.

> +static int do_import_stash(struct repository *r, const char *rev)
> +{
> +	struct object_id chain;
> +	struct oid_array items = OID_ARRAY_INIT;
> +	int res = 0;
> +	ssize_t i;
> +	const char *buffer = NULL;
> +	unsigned long bufsize;
> +	struct commit *this = NULL;
> +	char *msg = NULL;
> +
> +	if (repo_get_oid(r, rev, &chain))
> +		return error(_("not a valid revision: %s"), rev);
> +
> +	this = lookup_commit_reference(r, &chain);
> +	if (!this)
> +		return error(_("not a commit: %s"), rev);
> +
> +	/*
> +	 * Walk the commit history, finding each stash entry, and load data into
> +	 * the array.
> +	 */
> +	for (;;) {
> +		const char *author, *committer;
> +		size_t author_len, committer_len;
> +		const char *p;
> +		const char *expected = "git stash <git@stash> 1000684800 +0000";

Makes me wonder if we can somehow share this with [4/5] where we
establish the author and committer ident (timestamp spelled in a
different form).  Three strings that has to stay in sync is not a
huge deal, though.

> +		const char *prefix = "git stash: ";
> +		struct commit *stash;
> +		struct tree *tree = repo_get_commit_tree(r, this);
> +
> +		if (!tree ||
> +		    !oideq(&tree->object.oid, r->hash_algo->empty_tree) ||
> +		    (this->parents &&
> +		     (!this->parents->next || this->parents->next->next))) {
> +			res = error(_("%s is not a valid exported stash commit"),
> +					oid_to_hex(&this->object.oid));
> +			goto out;
> +		}

OK.  "buffer" is initialized to NULL before entering this loop, and
cleared to NULL at the end of this loop when "this" is moved, so
jumping to "out:" would not confuse the call to unuse_buffer().

> +		buffer = repo_get_commit_buffer(r, this, &bufsize);

And "buffer" becomes non-NULL and holds data associated with "this";
any "goto out" in the error path would do the right thing.  Very
nicely designed loop.

> +		if (!this->parents) {
> +			/*
> +			 * We don't have any parents.  Make sure this is our
> +			 * root commit.
> +			 */
> +			author = find_commit_header(buffer, "author", &author_len);
> +			committer = find_commit_header(buffer, "committer", &committer_len);
> +
> +			if (!author || !committer) {
> +				error(_("cannot parse commit %s"), oid_to_hex(&this->object.oid));
> +				goto out;
> +			}
> +
> +			if (author_len != strlen(expected) ||
> +			    committer_len != strlen(expected) ||
> +			    memcmp(author, expected, author_len) ||
> +			    memcmp(committer, expected, committer_len)) {
> +				res = error(_("found root commit %s with invalid data"), oid_to_hex(&this->object.oid));
> +				goto out;
> +			}
> +			break;
> +		}

OK.

> +		p = strstr(buffer, "\n\n");
> +		if (!p) {
> +			res = error(_("cannot parse commit %s"), oid_to_hex(&this->object.oid));
> +			goto out;
> +		}
> +
> +		p += 2;
> +		if (((size_t)(bufsize - (p - buffer)) < strlen(prefix)) ||
> +		    memcmp(prefix, p, strlen(prefix))) {
> +			res = error(_("found stash commit %s with unexpected prefix"), oid_to_hex(&this->object.oid));
> +			goto out;
> +		}

I'd call that "without expected prefix" (the difference being "we
expected 'git stash:' prefix but you have 'git stosh:' prefix" vs
"your commit title is 'hello world'"), but OK.  The important thing
is that we are being careful not to get confused.

It is unfortunate historical practice to measure almost everything
in "unsigned long" and bufsize here is one of them, but do we need
that cast?  We know strlen(prefix) is a small known constant
integer, we know p is not smaller than buffer, we know (p-buffer) is
not larger than bufsize.

> +		stash = this->parents->next->item;
> +
> +		if (repo_parse_commit(r, this->parents->item) ||
> +		    repo_parse_commit(r, stash)) {
> +			res = error(_("cannot parse parents of commit: %s"),
> +					oid_to_hex(&this->object.oid));
> +			goto out;
> +		}
> +
> +		if (check_stash_topology(r, stash)) {
> +			res = error(_("%s does not look like a stash commit"),
> +					oid_to_hex(&stash->object.oid));
> +			goto out;
> +		}
> +
> +		repo_unuse_commit_buffer(r, this, buffer);
> +		buffer = NULL;
> +		oid_array_append(&items, &stash->object.oid);
> +		this = this->parents->item;
> +	}
> +
> +	/*
> +	 * Now, walk each entry, adding it to the stash as a normal stash
> +	 * commit.
> +	 */
> +	for (i = (ssize_t)items.nr - 1; i >= 0; i--) {
> +		const char *p;
> +		const struct object_id *oid = items.oid + i;
> +
> +		this = lookup_commit_reference(r, oid);
> +		buffer = repo_get_commit_buffer(r, this, &bufsize);
> +		if (!buffer) {
> +			res = error(_("cannot read commit buffer for %s"), oid_to_hex(oid));
> +			goto out;
> +		}
> +
> +		p = strstr(buffer, "\n\n");
> +		if (!p) {
> +			res = error(_("cannot parse commit %s"), oid_to_hex(oid));
> +			goto out;
> +		}
> +
> +		p += 2;
> +		msg = xmemdupz(p, bufsize - (p - buffer));
> +		repo_unuse_commit_buffer(r, this, buffer);
> +		buffer = NULL;
> +
> +		if (do_store_stash(oid, msg, 1)) {
> +			res = error(_("cannot save the stash for %s"), oid_to_hex(oid));
> +			goto out;
> +		}
> +		FREE_AND_NULL(msg);
> +	}
> +out:
> +	if (this && buffer)
> +		repo_unuse_commit_buffer(r, this, buffer);
> +	oid_array_clear(&items);
> +	free(msg);
> +
> +	return res;
> +}

OK.  Again, I suspect that commit_list may be a more natural thing
to use to accumulate the stash entries, but that is not a huge deal.

  reply	other threads:[~2025-05-22 21:09 UTC|newest]

Thread overview: 72+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-05-08 23:44 [PATCH v5 0/4] Importing and exporting stashes to refs brian m. carlson
2025-05-08 23:44 ` [PATCH v5 1/4] object-name: make get_oid quietly return an error brian m. carlson
2025-05-09  1:55   ` Junio C Hamano
2025-05-09 19:50     ` brian m. carlson
2025-05-08 23:44 ` [PATCH v5 2/4] builtin/stash: factor out revision parsing into a function brian m. carlson
2025-05-09 15:27   ` Junio C Hamano
2025-05-08 23:44 ` [PATCH v5 3/4] builtin/stash: provide a way to export stashes to a ref brian m. carlson
2025-05-09 16:38   ` Junio C Hamano
2025-05-09 19:31   ` Junio C Hamano
2025-05-10 21:24   ` Jeff King
2025-05-12  9:10   ` Phillip Wood
2025-05-12 15:53     ` Junio C Hamano
2025-05-08 23:44 ` [PATCH v5 4/4] builtin/stash: provide a way to import stashes from " brian m. carlson
2025-05-09 19:54   ` Junio C Hamano
2025-05-11 23:44     ` brian m. carlson
2025-05-10 17:21   ` Jeff King
2025-05-12 12:42     ` Junio C Hamano
2025-05-12 12:58       ` Jeff King
2025-05-12 16:05         ` Junio C Hamano
2025-05-12 21:19     ` brian m. carlson
2025-05-10 21:33   ` Jeff King
2025-05-12  9:10   ` Phillip Wood
2025-05-09  1:10 ` [PATCH v5 0/4] Importing and exporting stashes to refs Junio C Hamano
2025-05-09 20:16   ` brian m. carlson
2025-05-09 16:53 ` D. Ben Knoble
2025-05-09 20:15   ` brian m. carlson
2025-05-10 19:13     ` D. Ben Knoble
2025-05-22 18:55 ` [PATCH] Makefile: avoid constant rebuilds with compilation database brian m. carlson
2025-05-22 18:55   ` [PATCH v6 0/5] Importing and exporting stashes to refs brian m. carlson
2025-06-01 22:32     ` [PATCH v7 0/4] " brian m. carlson
2025-06-01 22:32       ` [PATCH v7 1/4] object-name: make get_oid quietly return an error brian m. carlson
2025-06-01 22:32       ` [PATCH v7 2/4] builtin/stash: factor out revision parsing into a function brian m. carlson
2025-06-01 22:32       ` [PATCH v7 3/4] builtin/stash: provide a way to export stashes to a ref brian m. carlson
2025-06-05  9:25         ` Phillip Wood
2025-06-11 11:31         ` Kristoffer Haugsbakk
2025-06-11 23:35           ` brian m. carlson
2025-06-01 22:32       ` [PATCH v7 4/4] builtin/stash: provide a way to import stashes from " brian m. carlson
2025-06-05  9:38       ` [PATCH v7 0/4] Importing and exporting stashes to refs Phillip Wood
2025-06-12  1:12       ` [PATCH v8 " brian m. carlson
2025-06-12  1:12         ` [PATCH v8 1/4] object-name: make get_oid quietly return an error brian m. carlson
2025-06-12  1:12         ` [PATCH v8 2/4] builtin/stash: factor out revision parsing into a function brian m. carlson
2025-06-12  1:12         ` [PATCH v8 3/4] builtin/stash: provide a way to export stashes to a ref brian m. carlson
2025-06-12  1:12         ` [PATCH v8 4/4] builtin/stash: provide a way to import stashes from " brian m. carlson
2025-06-25  8:40         ` [PATCH v8 0/4] Importing and exporting stashes to refs Phillip Wood
2025-06-25 16:30           ` Junio C Hamano
2025-05-22 18:55   ` [PATCH v6 1/5] object-name: make get_oid quietly return an error brian m. carlson
2025-05-22 19:27     ` Junio C Hamano
2025-05-22 18:55   ` [PATCH v6 2/5] reflog-walk: expose read_complete_reflog brian m. carlson
2025-05-22 21:53     ` Ramsay Jones
2025-05-23 23:22       ` brian m. carlson
2025-05-24  1:09         ` Ramsay Jones
2025-05-26 19:55           ` brian m. carlson
2025-05-29 16:01     ` Phillip Wood
2025-05-29 21:59       ` Junio C Hamano
2025-05-22 18:55   ` [PATCH v6 3/5] builtin/stash: factor out revision parsing into a function brian m. carlson
2025-05-22 20:34     ` Junio C Hamano
2025-05-23 23:25       ` brian m. carlson
2025-05-24  0:23         ` Junio C Hamano
2025-05-26 19:36           ` brian m. carlson
2025-05-22 18:55   ` [PATCH v6 4/5] builtin/stash: provide a way to export stashes to a ref brian m. carlson
2025-05-22 20:51     ` Junio C Hamano
2025-05-26 19:42       ` brian m. carlson
2025-05-29 16:01     ` Phillip Wood
2025-05-22 18:55   ` [PATCH v6 5/5] builtin/stash: provide a way to import stashes from " brian m. carlson
2025-05-22 21:09     ` Junio C Hamano [this message]
2025-05-26 20:03       ` brian m. carlson
2025-05-22 21:15     ` Junio C Hamano
2025-05-23 13:17       ` Phillip Wood
2025-05-22 19:00   ` [PATCH] Makefile: avoid constant rebuilds with compilation database brian m. carlson
2025-05-22 19:08     ` Junio C Hamano
2025-06-11 11:35 ` [PATCH v5 0/4] Importing and exporting stashes to refs Kristoffer Haugsbakk
2025-06-12  0:45   ` brian m. carlson

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=xmqqcyc04akc.fsf@gitster.g \
    --to=gitster@pobox.com \
    --cc=ben.knoble@gmail.com \
    --cc=git@vger.kernel.org \
    --cc=sandals@crustytoothpaste.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).