From: Johannes Schindelin <Johannes.Schindelin@gmx.de>
To: Junio C Hamano <gitster@pobox.com>
Cc: Johannes Schindelin via GitGitGadget <gitgitgadget@gmail.com>,
git@vger.kernel.org, Elijah Newren <newren@gmail.com>,
Orgad Shaneh <orgads@gmail.com>
Subject: Re: [PATCH 4/4] built-in rebase: call `git am` directly
Date: Fri, 18 Jan 2019 15:15:45 +0100 (STD) [thread overview]
Message-ID: <nycvar.QRO.7.76.6.1901181443350.41@tvgsbejvaqbjf.bet> (raw)
In-Reply-To: <xmqqwonkclpx.fsf@gitster-ct.c.googlers.com>
Hi Junio,
On Fri, 4 Jan 2019, Junio C Hamano wrote:
> "Johannes Schindelin via GitGitGadget" <gitgitgadget@gmail.com>
> writes:
>
> > +static int write_basic_state(struct rebase_options *opts)
> > +{
> > + write_file(state_dir_path("head-name", opts), "%s",
> > + opts->head_name ? opts->head_name : "detached HEAD");
> > + write_file(state_dir_path("onto", opts), "%s",
> > + opts->onto ? oid_to_hex(&opts->onto->object.oid) : "");
> > + write_file(state_dir_path("orig-head", opts), "%s",
> > + oid_to_hex(&opts->orig_head));
> > + write_file(state_dir_path("quiet", opts), "%s",
> > + opts->flags & REBASE_NO_QUIET ? "" : "t");
> > + if (opts->flags & REBASE_VERBOSE)
> > + write_file(state_dir_path("verbose", opts), "%s", "");
> > + if (opts->strategy)
> > + write_file(state_dir_path("strategy", opts), "%s",
> > + opts->strategy);
> > + if (opts->strategy_opts)
> > + write_file(state_dir_path("strategy_opts", opts), "%s",
> > + opts->strategy_opts);
> > + if (opts->allow_rerere_autoupdate >= 0)
> > + write_file(state_dir_path("allow_rerere_autoupdate", opts),
> > + "-%s-rerere-autoupdate",
> > + opts->allow_rerere_autoupdate ? "" : "-no");
>
> Inside rebase, allow-rerere-autoupdate can be -1 (unspecified), 0
> (declined) or 1 (requested), and this code is being consistent with
> that convention.
>
> The "--[no-]rerere-autoupdate" option that is parsed via
> OPT_RERERE_AUTOUPDATE (used in builtin/rebase--interactive.c among
> other built-in commands) on the other hand is tertially that uses 0
> (unspecified), 1 (requested) and 2 (declined). This might be a
> ticking timebomb to confuse us in the future that may be worth
> fixing but probably outside this series.
Good point. We use -1 for unspecified in so many places, I think
OPT_RERERE_AUTOUPDATE needs to be fixed. But yes, I'll leave this as
#leftoverbits here.
> > @@ -459,6 +490,30 @@ static int reset_head(struct object_id *oid, const char *action,
> > return ret;
> > }
> >
> > +static int move_to_original_branch(struct rebase_options *opts)
> > +{
> > + struct strbuf orig_head_reflog = STRBUF_INIT, head_reflog = STRBUF_INIT;
> > + int ret;
> > +
> > + if (!opts->head_name)
> > + return 0; /* nothing to move back to */
> > +
> > + if (!opts->onto)
> > + BUG("move_to_original_branch without onto");
>
> This check is absent in the scripted version, but from the message
> we generate here, it is clear that the caller must not call this
> when there is no "onto" commit. Good.
>
> > + strbuf_addf(&orig_head_reflog, "rebase finished: %s onto %s",
> > + opts->head_name, oid_to_hex(&opts->onto->object.oid));
> > + strbuf_addf(&head_reflog, "rebase finished: returning to %s",
> > + opts->head_name);
> > + ret = reset_head(NULL, "checkout", opts->head_name,
> > + RESET_HEAD_REFS_ONLY,
> > + orig_head_reflog.buf, head_reflog.buf);
>
> The *action given to reset_head() here is "checkout". Makes me
> wonder about two things:
>
> - The only real use of the parameter in the callee is to prepare
> the error and advice messages from the unpack_trees machinery,
> but because we are using it in REFS_ONLY mode, it does not
> matter. In fact it might even be misleading; perhaps pass NULL
> or something, so that a mistaken update to reset_head() later
> that lets REFS_ONLY request to go to unpack_trees machinery will
> catch it as a bug?
>
> - Another topic in flight wants to make sure that the post-checkout
> hook gets called when the RESET_HEAD_RUN_POST_CHECKOUT_HOOK flag
> is given by the caller, and IIRC, the use of the flag is strongly
> correlated to *action being "checkout". Do we want to pass
> REFS_ONLY and RUN_POST_CHECKOUT_HOOK flag for this call, or do we
> rather keep it silent? As the original scripted version did not
> use "checkout" here and never triggered post-checkout hook, I am
> inclined to say that we should not pass that other bit. That
> then leads me to suspect that we do not want *action to be
> "checkout" here.
The only thing for which that the `action` is used, though, is the call to
`setup_unpack_trees_porcelain()`, which does not accept a `NULL`. I guess
I could replace it by the empty string. Will do that.
>
> > + strbuf_release(&orig_head_reflog);
> > + strbuf_release(&head_reflog);
> > + return ret;
> > +}
>
> Unlike the scripted version, this does not die() upon failure, so
> the caller needs to be careful about the returned status.
Indeed. That function is only called from `run_am()`, and returns the
status in every instance. The caller of `run_am()`,
`run_specific_rebase()` also handles it correctly.
>
> > @@ -466,6 +521,129 @@ N_("Resolve all conflicts manually, mark them as resolved with\n"
> > "To abort and get back to the state before \"git rebase\", run "
> > "\"git rebase --abort\".");
> >
> > +static int run_am(struct rebase_options *opts)
> > +{
> > + struct child_process am = CHILD_PROCESS_INIT;
> > + struct child_process format_patch = CHILD_PROCESS_INIT;
> > + struct strbuf revisions = STRBUF_INIT;
> > + int status;
> > + char *rebased_patches;
> > +
> > + am.git_cmd = 1;
> > + argv_array_push(&am.args, "am");
> > +
> > + if (opts->action && !strcmp("continue", opts->action)) {
> > + argv_array_push(&am.args, "--resolved");
> > + argv_array_pushf(&am.args, "--resolvemsg=%s", resolvemsg);
> > + if (opts->gpg_sign_opt)
> > + argv_array_push(&am.args, opts->gpg_sign_opt);
> > + status = run_command(&am);
> > + if (status)
> > + return status;
> > +
> > + discard_cache();
> > + return move_to_original_branch(opts);
>
> It is curious why discard_cache() is placed exacly here, as if we
> want to preserve the contents of the in-core index when
> run_command() failed. But I do not think we care about the in-core
> index as the only thing that happen after "return status" is to
> return the control to run_specific_rebase(), let it jump to
> finished_rebase label to clean things up and rturn control to
> cmd_rebase() and exit based on the status value.
>
> It's not like move_to_original_branch() wants to call read_cache()
> and get the result from the "am" that run_command() executed,
> either.
>
> Puzzled. Care to explain a bit more in the in-code comment?
I think that this call is just a left-over from a previous version that
did not have the REFS_ONLY flag to pass to `move_to_original_branch()`
(and it caused havoc before that flag was passed). Let me double-check
whether the `discard_cache()` even makes sense any longer.
*clicketyclick* indeed that is the case. Will remove all three
`discard_cache()` calls.
>
> > + }
> > + if (opts->action && !strcmp("skip", opts->action)) {
> > + argv_array_push(&am.args, "--skip");
> > + argv_array_pushf(&am.args, "--resolvemsg=%s", resolvemsg);
> > + status = run_command(&am);
> > + if (status)
> > + return status;
> > +
> > + discard_cache();
> > + return move_to_original_branch(opts);
>
> Ditto.
>
> > + }
> > + if (opts->action && !strcmp("show-current-patch", opts->action)) {
> > + argv_array_push(&am.args, "--show-current-patch");
> > + return run_command(&am);
> > + }
>
> Up to this point, it is a faithful conversion of the first case/esac
> statement. Good.
>
> > + strbuf_addf(&revisions, "%s...%s",
> > + oid_to_hex(opts->root ?
> > + /* this is now equivalent to ! -z "$upstream" */
>
> Does "this" refer to the "opts->root being true" check?
>
> Because you are flipping the polarity of the test from scripted
> version, shouldn't the comment be updated to "-z $upstream"?
It did flip the polarity, you are right, this comment is incorrect. It is
even more incorrect, though, as it talks about a shell construct that is
no longer applicable. Will fix.
>
> > + &opts->onto->object.oid :
> > + &opts->upstream->object.oid),
> > + oid_to_hex(&opts->orig_head));
>
> > + rebased_patches = xstrdup(git_path("rebased-patches"));
> > + format_patch.out = open(rebased_patches,
> > + O_WRONLY | O_CREAT | O_TRUNC, 0666);
>
> Unlike scripted version, we do not remove a (possibly) existing file.
> We give CREAT in case there is no existing one, and TRUNC in case
> there is an existing one. Makes sense. A more faithful translation
> would have unlink(2)ed a (possibly) existing one, and then because
> we can afford to, passed O_EXCL to avoid stomping on somebody else
> racing with us, but I do not think it is worth it.
Okay.
> > + if (format_patch.out < 0) {
> > + status = error_errno(_("could not write '%s'"),
> > + rebased_patches);
>
> s/write '%s'/open '%s' for writing/? I dunno.
Yep, of course! Will fix.
> > + free(rebased_patches);
> > + argv_array_clear(&am.args);
> > + return status;
> > + }
> > +
> > + format_patch.git_cmd = 1;
> > + argv_array_pushl(&format_patch.args, "format-patch", "-k", "--stdout",
> > + "--full-index", "--cherry-pick", "--right-only",
> > + "--src-prefix=a/", "--dst-prefix=b/", "--no-renames",
> > + "--no-cover-letter", "--pretty=mboxrd", NULL);
> > + if (opts->git_format_patch_opt.len)
> > + argv_array_split(&format_patch.args,
> > + opts->git_format_patch_opt.buf);
> > + argv_array_push(&format_patch.args, revisions.buf);
> > + if (opts->restrict_revision)
> > + argv_array_pushf(&format_patch.args, "^%s",
> > + oid_to_hex(&opts->restrict_revision->object.oid));
>
> It is kinda surprising to see that we have learned quite a lot of
> fringe "configurations" we need to explicitly override like this.
>
> Looks like a quite faithful conversion, anyway.
>
> > + status = run_command(&format_patch);
> > + if (status) {
> > + unlink(rebased_patches);
> > + free(rebased_patches);
> > + argv_array_clear(&am.args);
> > +
> > + reset_head(&opts->orig_head, "checkout", opts->head_name, 0,
> > + "HEAD", NULL);
>
> This one may need to trigger post-checkout hook. The scripted
> version does two different things depending on the value of
> $head_name, but we can just use the same code without conditional?
Yes, because `opts->head_name` is `NULL` in one case, and not `NULL` in
the other, and the `reset_head()` function performs the desired operation
in each case.
> > + error(_("\ngit encountered an error while preparing the "
> > + "patches to replay\n"
> > + "these revisions:\n"
> > + "\n %s\n\n"
> > + "As a result, git cannot rebase them."),
> > + opts->revisions);
> > +
> > + strbuf_release(&revisions);
> > + return status;
> > + }
> > + strbuf_release(&revisions);
> > +
> > + am.in = open(rebased_patches, O_RDONLY);
> > + if (am.in < 0) {
> > + status = error_errno(_("could not read '%s'"),
> > + rebased_patches);
>
> s/write '%s'/open '%s' for reading/? I dunno.
Yep, will fix.
>
> > + free(rebased_patches);
> > + argv_array_clear(&am.args);
> > + return status;
> > + }
> > +
> > + argv_array_pushv(&am.args, opts->git_am_opts.argv);
> > + argv_array_push(&am.args, "--rebasing");
> > + argv_array_pushf(&am.args, "--resolvemsg=%s", resolvemsg);
> > + argv_array_push(&am.args, "--patch-format=mboxrd");
> > + if (opts->allow_rerere_autoupdate > 0)
> > + argv_array_push(&am.args, "--rerere-autoupdate");
> > + else if (opts->allow_rerere_autoupdate == 0)
> > + argv_array_push(&am.args, "--no-rerere-autoupdate");
> > + if (opts->gpg_sign_opt)
> > + argv_array_push(&am.args, opts->gpg_sign_opt);
> > + status = run_command(&am);
> > + unlink(rebased_patches);
> > + free(rebased_patches);
> > +
> > + if (!status) {
> > + discard_cache();
> > + return move_to_original_branch(opts);
> > + }
> > +
> > + if (is_directory(opts->state_dir))
> > + write_basic_state(opts);
> > +
> > + return status;
> > +}
> > +
> > static int run_specific_rebase(struct rebase_options *opts)
> > {
> > const char *argv[] = { NULL, NULL };
> > @@ -546,6 +724,11 @@ static int run_specific_rebase(struct rebase_options *opts)
> > goto finished_rebase;
> > }
> >
> > + if (opts->type == REBASE_AM) {
> > + status = run_am(opts);
> > + goto finished_rebase;
> > + }
> > +
> > add_var(&script_snippet, "GIT_DIR", absolute_path(get_git_dir()));
> > add_var(&script_snippet, "state_dir", opts->state_dir);
>
>
> Overall, this was quite a pleasant read and a well constructed
> series. Other than two minor points (i.e. interaction with the
> 'post-checkout hook' topic, and discard_cache() before calling
> move_to_original_branch) I did not quite understand, looks good to
> me.
>
> When merged to 'pu', I seem to be getting failure from t3425.5, .8
> and .11, by the way. I haven't dug into the actual breakages any
> further than that.
Sorry for the trouble, and for my silence (I was heads-down into the Azure
Pipelines support).
I did not see any breakage in `pu` lately, hopefully things resolved
themselves?
Ciao,
Dscho
next prev parent reply other threads:[~2019-01-18 14:16 UTC|newest]
Thread overview: 27+ messages / expand[flat|nested] mbox.gz Atom feed top
2018-12-21 13:17 [PATCH 0/4] Let the builtin rebase call the git am command directly Johannes Schindelin via GitGitGadget
2018-12-21 13:17 ` [PATCH 1/4] rebase: move `reset_head()` into a better spot Johannes Schindelin via GitGitGadget
2019-01-04 18:39 ` Junio C Hamano
2018-12-21 13:17 ` [PATCH 2/4] rebase: avoid double reflog entry when switching branches Johannes Schindelin via GitGitGadget
2019-01-04 18:38 ` Junio C Hamano
2019-01-18 14:18 ` Johannes Schindelin
2018-12-21 13:17 ` [PATCH 4/4] built-in rebase: call `git am` directly Johannes Schindelin via GitGitGadget
2019-01-04 18:38 ` Junio C Hamano
2019-01-18 14:15 ` Johannes Schindelin [this message]
2019-01-18 17:59 ` Junio C Hamano
2019-01-18 18:14 ` Johannes Schindelin
2019-01-04 23:19 ` Junio C Hamano
2019-01-07 17:19 ` Junio C Hamano
2018-12-21 13:17 ` [PATCH 3/4] rebase: teach `reset_head()` to optionally skip the worktree Johannes Schindelin via GitGitGadget
2019-01-04 18:38 ` Junio C Hamano
2019-01-04 18:40 ` Junio C Hamano
2019-01-18 14:18 ` Johannes Schindelin
2019-01-18 15:09 ` [PATCH v2 0/4] Let the builtin rebase call the git am command directly Johannes Schindelin via GitGitGadget
2019-01-18 15:09 ` [PATCH v2 1/4] rebase: move `reset_head()` into a better spot Johannes Schindelin via GitGitGadget
2019-01-18 15:09 ` [PATCH v2 2/4] rebase: avoid double reflog entry when switching branches Johannes Schindelin via GitGitGadget
2019-01-18 15:09 ` [PATCH v2 3/4] rebase: teach `reset_head()` to optionally skip the worktree Johannes Schindelin via GitGitGadget
2019-01-18 15:09 ` [PATCH v2 4/4] built-in rebase: call `git am` directly Johannes Schindelin via GitGitGadget
2019-01-26 21:13 ` Alban Gruin
2019-01-29 9:46 ` Johannes Schindelin
2019-01-18 18:06 ` [PATCH v2 0/4] Let the builtin rebase call the git am command directly Junio C Hamano
2019-01-18 20:13 ` Junio C Hamano
2019-01-18 20:31 ` Johannes Schindelin
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
List information: http://vger.kernel.org/majordomo-info.html
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=nycvar.QRO.7.76.6.1901181443350.41@tvgsbejvaqbjf.bet \
--to=johannes.schindelin@gmx.de \
--cc=git@vger.kernel.org \
--cc=gitgitgadget@gmail.com \
--cc=gitster@pobox.com \
--cc=newren@gmail.com \
--cc=orgads@gmail.com \
/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.
Code repositories for project(s) associated with this public inbox
https://80x24.org/mirrors/git.git
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 read-only IMAP folder(s) and NNTP newsgroup(s).