Subject: Re: [PATCH v2 1/1] replay: make atomic ref updates the default behavior

From: Junio C Hamano <[email-masked]>
Date: Thu, 02 Oct 2025 11:27:25 -0700

Elijah Newren <[email-masked]> writes:

>   * it provided a natural low-level tool for the suite of hash-object,
> mktree, commit-tree, mktag, merge-tree, and update-ref, allowing users
> to have another building block for experimentation and making new
> tools.
>
> I was particularly focused on the last of those items for the intial
> version at the time, but it should be noted that all three of these
> are somewhat special cases, and the most common user desire is going
> to be replaying commits and updating the references at the end.

Yes.  We could even tweak the stream in the middle with "sed" or
"grep", I presume? ;-)

> Sure, but it's quite trivial to add, right -- as shown above with the
> extra "start", "prepare", "commit" directives?

Very true.

Completely a tangent but, isn't requiring "prepare" at this layer,
and possibly in the form of ref_transaction_prepare() at the C
layer, not so ergonomic API design?  Once you "start" a transaction
and threw a bunch of instruction, "commit" can notice that you are
in a transaction and should do whatever necessary (including
whatever "prepare" does).  I am not advocating to simplify the API
by making end-user/program facing "prepare" a no-op, but just
wondering why we decided to have "prepare" a so prominent API
element.

> ...
> Might I suggest a rewrite of the text of the commit message to this point?

I do think it makes more sense to the reader to know the reasoning
behind the _current_ design, and what its strengths are.

> =====
> The git replay command currently outputs update commands that can be
> piped to update-ref to achieve a rebase, e.g.
>
>   git replay --onto main topic1..topic2 | git update-ref --stdin
>
> This separation had advantages for three special cases:
>   * it made testing easy (when state isn't modified from one step to
> the next, you don't need to make temporary branches or have undo
> commands, or try to track the changes)
>   * it provided a natural can-it-rebase-cleanly (and what would it
> rebase to) capability without automatically updating refs, I guess
> kind of like a --dry-run
>   * it provided a natural low-level tool for the suite of hash-object,
> mktree, commit-tree, mktag, merge-tree, and update-ref, allowing users
> to have another building block for experimentation and making new
> tools.
>
> However, it should be noted that all three of these are somewhat
> special cases; users, whether on the client or server side, would
> almost certainly find it more ergonomical to simply have the updating
> of refs be the default.  Change the default behavior to update refs
> directly, and atomically (at least to the extent supported by the refs
> backend in use).
> ====

This reads very well.

> Why is --allow-partial helpful?  You discussed at length why you
> wanted atomic transactions, but you introduce this option with no
> rationale and instead just discuss that you implemented it and some
> design choices once you presuppose that someone wants to use it.
>
> Is there a usecase?  I asked for it last time, and suggested
> discarding the modes without one, but you only discarded one of the
> extras while leaving this one in.  I'd recommend discarding this one
> too and just having the two modes -- the output commands that get fed
> to update-ref, or the automatic transactional update of all or no
> refs.

I know there are people who like "best effort", but I too want to
learn a concrete use case where the "best effort" mode, which
updates only 3 refs among 30 that were to be updated, would give us
a better result than "all or none" transaction that fails.