From: "Ævar Arnfjörð Bjarmason" <avarab@gmail.com>
To: "brian m. carlson" <sandals@crustytoothpaste.net>
Cc: git@vger.kernel.org, Junio C Hamano <gitster@pobox.com>,
Phillip Wood <phillip.wood123@gmail.com>
Subject: Re: [PATCH v2 3/4] builtin/stash: provide a way to export stashes to a ref
Date: Thu, 31 Mar 2022 03:56:01 +0200 [thread overview]
Message-ID: <220331.86pmm2swm9.gmgdl@evledraar.gmail.com> (raw)
In-Reply-To: <20220329214941.2018609-4-sandals@crustytoothpaste.net>
On Tue, Mar 29 2022, brian m. carlson wrote:
> A common user problem is how to sync in-progress work to another
> machine. Users currently must use some sort of transfer of the working
> tree, which poses security risks and also necessarily causes the index
> to become dirty. The experience is suboptimal and frustrating for
> users.
>
> A reasonable idea is to use the stash for this purpose, but the stash is
> stored in the reflog, not in a ref, and as such it cannot be pushed or
> pulled. This also means that it cannot be saved into a bundle or
> preserved elsewhere, which is a problem when using throwaway development
> environments.
>
> Let's solve this problem by allowing the user to export the stash to a
> ref (or, to just write it into the repository and print the hash, à la
> git commit-tree). Introduce git stash export, which writes a chain of
> commits where the first parent is always a chain to the previous stash,
> or to a single, empty commit (for the final item) and the second is the
> stash commit normally written to the reflog.
>
> Iterate over each stash from topmost to bottomost, looking up the data
> for each one, and then create the chain from the single empty commit
> back up in reverse order. Generate a predictable empty commit so our
> behavior is reproducible. Create a useful commit message, preserving
> the author and committer information, to help users identify stash
> commits when viewing them as normal commits.
>
> If the user has specified specific stashes they'd like to export
> instead, use those instead of iterating over all of the stashes.
>
> As part of this, specifically request quiet behavior when looking up the
> OID for a revision because we will eventually hit a revision that
> doesn't exist and we don't want to die when that occurs.
>
> Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
> ---
> Documentation/git-stash.txt | 22 ++++-
> builtin/stash.c | 183 ++++++++++++++++++++++++++++++++++++
> 2 files changed, 204 insertions(+), 1 deletion(-)
>
> diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt
> index 6e15f47525..162110314e 100644
> --- a/Documentation/git-stash.txt
> +++ b/Documentation/git-stash.txt
> @@ -20,6 +20,7 @@ SYNOPSIS
> 'git stash' clear
> 'git stash' create [<message>]
> 'git stash' store [-m|--message <message>] [-q|--quiet] <commit>
> +'git stash' export ( --print | --to-ref <ref> ) [<stash>...]
>
> DESCRIPTION
> -----------
> @@ -151,6 +152,12 @@ store::
> reflog. This is intended to be useful for scripts. It is
> probably not the command you want to use; see "push" above.
>
> +export ( --print | --to-ref <ref> ) [<stash>...]::
> +
I think this extra \n here isn't needed.
> +static const char * const git_stash_export_usage[] = {
> + N_("git stash export (--print | --to-ref <ref>) [<stash>...]"),
> + NULL
> +};
> +
> +
Stray too-much-whitespace.
> static const char ref_stash[] = "refs/stash";
> static struct strbuf stash_index_path = STRBUF_INIT;
>
> @@ -1773,6 +1780,180 @@ static int save_stash(int argc, const char **argv, const char *prefix)
> return ret;
> }
>
> +static int write_commit_with_parents(struct object_id *out, const struct object_id *oid, struct commit_list *parents)
> +{
> + size_t author_len, committer_len;
> + struct commit *this;
> + const char *orig_author, *orig_committer;
> + char *author = NULL, *committer = NULL;
> + const char *buffer;
> + unsigned long bufsize;
> + const char *p;
> + struct strbuf msg = STRBUF_INIT;
> + int ret = 0;
With this...
> + this = lookup_commit_reference(the_repository, oid);
> + buffer = get_commit_buffer(this, &bufsize);
> + orig_author = find_commit_header(buffer, "author", &author_len);
> + orig_committer = find_commit_header(buffer, "committer", &committer_len);
> + p = memmem(buffer, bufsize, "\n\n", 2);
> +
> + if (!orig_author || !orig_committer || !p) {
> + error(_("cannot parse commit %s"), oid_to_hex(oid));
> + goto out;
..isn't this a logic error, shouldn't we return -1 here?
> + }
> + /* Jump to message. */
> + p += 2;
> + strbuf_addstr(&msg, "git stash: ");
> + strbuf_add(&msg, p, bufsize - (p - buffer));
> +
> + author = xmemdupz(orig_author, author_len);
> + committer = xmemdupz(orig_committer, committer_len);
> +
> + if (commit_tree_extended(msg.buf, msg.len,
> + the_hash_algo->empty_tree, parents,
> + out, author, committer,
> + NULL, NULL)) {
> + ret = -1;
> + error(_("could not write commit"));
better as "ret = error(..."?
> + goto out;
> + }
> +out:
> + strbuf_reset(&msg);
> + unuse_commit_buffer(this, buffer);
> + free(author);
> + free(committer);
> + return ret;
> +}
> +
> +static int do_export_stash(const char *ref, size_t argc, const char **argv)
> +{
> + struct object_id base;
> + struct object_context unused;
> + struct commit *prev;
> + struct object_id *items = NULL;
> + int nitems = 0, nalloc = 0;
Can nalloc be moved into the if=else scopes?
Also shouldn't these be size_t...?
> + int res = 0;
> + struct strbuf revision;
> + const char *author, *committer;
> +
> + /*
> + * This is an arbitrary, fixed date, specifically the one used by git
> + * format-patch. The goal is merely to produce reproducible output.
> + */
> + prepare_fallback_ident("git stash", "git@stash");
> + author = fmt_ident("git stash", "git@stash", WANT_BLANK_IDENT, "2001-09-17T00:00:00Z", 0);
> + committer = fmt_ident("git stash", "git@stash", WANT_BLANK_IDENT, "2001-09-17T00:00:00Z", 0);
> +
> + /* First, we create a single empty commit. */
> + if (commit_tree_extended(NULL, 0, the_hash_algo->empty_tree, NULL, &base, author, committer, NULL, NULL))
Some very long lines here.
> + return error(_("unable to write base commit"));
> +
> + prev = lookup_commit_reference(the_repository, &base);
> +
> + if (argc) {
> + /*
> + * Find each specified stash, and load data into the array.
> + */
> + for (int i = 0; i < argc; i++) {
...as this is size_t, not int.
> + ALLOC_GROW_BY(items, nitems, 1, nalloc);
> + if (parse_revision(&revision, argv[i], 0) ||
> + get_oid_with_context(the_repository, revision.buf,
> + GET_OID_QUIETLY | GET_OID_GENTLY,
> + &items[i], &unused)) {
> + error(_("unable to find stash entry %s"), argv[i]);
> + res = -1;
ditto "ret = error(..."
> + goto out;
> + }
> + }
> + } else {
> + /*
> + * Walk the reflog, finding each stash entry, and load data into the
> + * array.
> + */
> + for (int i = 0;; i++) {
Aside from the C99 dependency Junio mentioned, this should also be size_t.
> + /*
> + * 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 (int i = nitems - 1; i >= 0; i--) {
Did you spot my "count down" suggestion in
https://lore.kernel.org/git/220311.86bkydi65v.gmgdl@evledraar.gmail.com/
on the v1?
> + struct commit_list *parents = NULL;
> + struct commit_list **next = &parents;
> + struct object_id out;
> +
> + next = commit_list_append(prev, next);
> + next = commit_list_append(lookup_commit_reference(the_repository, &items[i]), next);
> + if (write_commit_with_parents(&out, &items[i], parents)) {
Here we returned -1 from this earlier, I think this would be more
straightforward as:
res = write_commit_with_parents(...)
if (res < 0)
goto out;
> + res = -1;
> + goto out;
So one doesn't have to wonder why we're ignoring the error value, and
using -1, but then treating all non-zero as errors.
> + }
> + prev = lookup_commit_reference(the_repository, &out);
> + }
> + if (ref)
> + update_ref(NULL, ref, &prev->object.oid, NULL, 0, UPDATE_REFS_DIE_ON_ERR);
> + else
> + puts(oid_to_hex(&prev->object.oid));
> +out:
> + free(items);
> +
> + return res;
> +}
> +
> +enum export_action {
> + ACTION_NONE,
> + ACTION_PRINT,
> + ACTION_TO_REF,
> +};
> +
> +static int export_stash(int argc, const char **argv, const char *prefix)
> +{
> + int ret = 0;
It looks like we don't need to initialize this.
> + const char *ref = NULL;
> + enum export_action action = ACTION_NONE;
> + struct option options[] = {
> + OPT_CMDMODE(0, "print", &action,
> + N_("print the object ID instead of writing it to a ref"),
> + ACTION_PRINT),
> + OPT_CMDMODE(0, "to-ref", &action,
> + N_("save the data to the given ref"),
> + ACTION_TO_REF),
> + OPT_END()
> + };
> +
> + argc = parse_options(argc, argv, prefix, options,
> + git_stash_export_usage,
> + PARSE_OPT_KEEP_DASHDASH);
> +
> + if (action == ACTION_NONE) {
> + return error(_("exactly one of --print and --to-ref is required"));
> + } else if (action == ACTION_TO_REF) {
> + if (!argc)
> + return error(_("--to-ref requires an argument"));
> + ref = argv[0];
> + argc--;
> + argv++;
> + }
> +
> +
nit: Too much whitespace
> + ret = do_export_stash(ref, argc, argv);
> + return ret;
Aside from the "ret" case above, maybe this would be better if the
"action" check became a swith, then the compiler would help check it
against the enum, and this wouldn't implicitly be both ACTION_PRINT and
ACTION_TO_REF, but could be done via a fall-through.
next prev parent reply other threads:[~2022-03-31 2:07 UTC|newest]
Thread overview: 76+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-03-10 17:32 [PATCH 0/6] Importing and exporting stashes to refs brian m. carlson
2022-03-10 17:32 ` [PATCH 1/6] builtin/stash: factor out generic function to look up stash info brian m. carlson
2022-03-10 17:32 ` [PATCH 2/6] builtin/stash: fill in all commit data brian m. carlson
2022-03-16 16:50 ` Junio C Hamano
2022-03-10 17:32 ` [PATCH 3/6] object-name: make get_oid quietly return an error brian m. carlson
2022-03-16 16:56 ` Junio C Hamano
2022-03-16 17:01 ` Drew Stolee
2022-03-16 21:40 ` brian m. carlson
2022-03-10 17:32 ` [PATCH 4/6] builtin/stash: provide a way to export stashes to a ref brian m. carlson
2022-03-11 2:08 ` Ævar Arnfjörð Bjarmason
2022-03-14 21:19 ` Phillip Wood
2022-03-15 10:50 ` Phillip Wood
2022-03-16 21:48 ` brian m. carlson
2022-03-18 13:34 ` C99 %zu support (on MSVC) (was: [PATCH 4/6] builtin/stash: provide a way to export stashes to a ref) Ævar Arnfjörð Bjarmason
2022-03-18 16:26 ` Phillip Wood
2022-03-24 14:02 ` [PATCH 4/6] builtin/stash: provide a way to export stashes to a ref Johannes Schindelin
2022-03-18 13:41 ` ssize_t portability (was: [PATCH 4/6] builtin/stash: provide a way to export stashes to a ref) Ævar Arnfjörð Bjarmason
2022-03-16 17:05 ` [PATCH 4/6] builtin/stash: provide a way to export stashes to a ref Junio C Hamano
2022-03-10 17:32 ` [PATCH 5/6] builtin/stash: provide a way to import stashes from " brian m. carlson
2022-03-16 17:26 ` Junio C Hamano
2022-03-16 21:50 ` brian m. carlson
2022-03-10 17:32 ` [PATCH 6/6] doc: add stash export and import to docs brian m. carlson
2022-03-16 17:34 ` Junio C Hamano
2022-03-16 21:44 ` Junio C Hamano
2022-03-10 19:14 ` [PATCH 0/6] Importing and exporting stashes to refs Junio C Hamano
2022-03-10 21:04 ` brian m. carlson
2022-03-10 21:38 ` Junio C Hamano
2022-03-10 22:42 ` brian m. carlson
2022-03-29 21:49 ` [PATCH v2 0/4] " brian m. carlson
2022-03-29 21:49 ` [PATCH v2 1/4] object-name: make get_oid quietly return an error brian m. carlson
2022-03-29 21:49 ` [PATCH v2 2/4] builtin/stash: factor out revision parsing into a function brian m. carlson
2022-03-29 21:49 ` [PATCH v2 3/4] builtin/stash: provide a way to export stashes to a ref brian m. carlson
2022-03-30 23:05 ` Junio C Hamano
2022-03-30 23:44 ` brian m. carlson
2022-03-31 1:56 ` Ævar Arnfjörð Bjarmason [this message]
2022-03-31 17:43 ` Junio C Hamano
2022-04-05 10:55 ` brian m. carlson
2022-04-06 9:05 ` Ævar Arnfjörð Bjarmason
2022-04-06 16:38 ` Junio C Hamano
2022-03-31 2:09 ` Ævar Arnfjörð Bjarmason
2022-04-05 10:22 ` brian m. carlson
2022-03-29 21:49 ` [PATCH v2 4/4] builtin/stash: provide a way to import stashes from " brian m. carlson
2022-03-31 1:48 ` [PATCH v2 0/4] Importing and exporting stashes to refs Junio C Hamano
2022-03-31 2:18 ` Ævar Arnfjörð Bjarmason
2022-04-03 18:22 ` [PATCH v3 " brian m. carlson
2022-04-03 18:22 ` [PATCH v3 1/4] object-name: make get_oid quietly return an error brian m. carlson
2022-04-03 18:22 ` [PATCH v3 2/4] builtin/stash: factor out revision parsing into a function brian m. carlson
2022-04-04 15:44 ` Phillip Wood
2022-04-03 18:22 ` [PATCH v3 3/4] builtin/stash: provide a way to export stashes to a ref brian m. carlson
2022-04-04 6:46 ` Ævar Arnfjörð Bjarmason
2022-04-03 18:22 ` [PATCH v3 4/4] builtin/stash: provide a way to import stashes from " brian m. carlson
2022-04-04 10:38 ` Ævar Arnfjörð Bjarmason
2022-04-05 10:03 ` brian m. carlson
2022-04-06 9:00 ` Ævar Arnfjörð Bjarmason
2022-04-04 0:05 ` [PATCH v3 0/4] Importing and exporting stashes to refs Junio C Hamano
2022-04-04 0:29 ` Junio C Hamano
2022-04-04 6:20 ` Ævar Arnfjörð Bjarmason
2022-04-05 9:15 ` brian m. carlson
2022-04-07 21:53 ` [PATCH v4 " brian m. carlson
2022-04-07 21:53 ` [PATCH v4 1/4] object-name: make get_oid quietly return an error brian m. carlson
2022-04-07 21:53 ` [PATCH v4 2/4] builtin/stash: factor out revision parsing into a function brian m. carlson
2022-04-07 21:53 ` [PATCH v4 3/4] builtin/stash: provide a way to export stashes to a ref brian m. carlson
2022-04-13 15:29 ` Ævar Arnfjörð Bjarmason
2022-04-13 15:36 ` Ævar Arnfjörð Bjarmason
2022-04-13 15:55 ` Ævar Arnfjörð Bjarmason
2022-04-07 21:53 ` [PATCH v4 4/4] builtin/stash: provide a way to import stashes from " brian m. carlson
2022-04-12 20:14 ` Jonathan Tan
2022-04-13 1:12 ` brian m. carlson
2022-04-13 17:34 ` Jonathan Tan
2022-04-13 18:25 ` Ævar Arnfjörð Bjarmason
2022-04-13 19:14 ` Jonathan Tan
2022-04-13 20:10 ` Junio C Hamano
2022-04-13 21:33 ` brian m. carlson
2022-04-13 21:43 ` Junio C Hamano
2022-04-13 18:33 ` Ævar Arnfjörð Bjarmason
2022-04-13 15:25 ` [PATCH v4 0/4] Importing and exporting stashes to refs Ævar Arnfjörð Bjarmason
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=220331.86pmm2swm9.gmgdl@evledraar.gmail.com \
--to=avarab@gmail.com \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=phillip.wood123@gmail.com \
--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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.