git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [SCRIPT/RFC 0/3] git-commit --onto-parent (three-way merge, no working tree file changes)

From
IDIgor Djordjevic <igor.d.djordjevic@gmail.com>
Date
Dec 4, 2017, 02:33 UTC
Message-ID
<7ae3ffd5-147d-55d2-9630-da12c429d631@gmail.com>
In-Reply-To
<33e97533-716b-e1cc-6aa0-bf8941225319@kdbg.org>
Hi Hannes,
On 01/12/2017 18:23, Johannes Sixt wrote:
Show 17 quoted lines
> 
> > To work with `--onto-parent` and be able to commit on top of any of
> > the topic branches, you would need a situation like this instead:
> >
> >   (1)  ...C      <- topic C
> >           |
> >      ...A |      <- topic A
> >          \|
> >        ...o I    <- integration
> >          /|
> >      ...B |      <- topic B
> >           |
> >        ...D      <- topic D
> 
> This is a very, VERY exotic workflow, I would say. How would you
> construct commit I when three or more topics have conflicts?
> merge-octopus does not support this use-case.

But I`m not interested in constructing commit I in the first place, only help working with it once it`s already there (which shouldn`t be too uncommon in case of unrelated, non-conflicting topics) - but I already agreed my example could have been a bit too esoteric, distracting attention from the important part :)

I`ll continue on this further below, where you commented on those more common scenarios.

Here, let`s try to look at the whole situation from a "patch queue" perspective instead, starting from something like this:

(5) ---O---I  <- integration <- HEAD

... and then making progress like this - first, commit A1 onto O, starting "topicA" branch at the same time, even, maybe using syntax like `git commit --onto-parent O -b topicA`:

(6) ---O---J  <- integration <- HEAD               [ J = I + A1 ]
        \ /
         A1   <- topicA
..., then commit B1 onto O to start "topicB" branch:
(7)      B1   <- topicB
        / \
    ---O---K  <- integration <- HEAD               [ K = J + B1 ]
        \ /
         A1   <- topicA
..., then add one more commit (patch) onto B1:
(8)      B1---B2   <- topicB
        /      \
    ---O--------L  <- integration <- HEAD          [ L = K + B2 ]
        \      /
         A1---/    <- topicA
..., and one more, B3:
(9)      B1---B2---B3   <- topicB
        /           \
    ---O-------------M  <- integration <- HEAD     [ M = L + B3 ]
        \           /
         A1--------/    <- topicA
We can also start a new topic branch (queue), commit C1:
(10)     B1---B2---B3   <- topicB
        /           \
    ---O-------------N  <- integration <- HEAD     [ N = M + C1 ]
       |\           /|
       | A1--------/ /  <- topicA
       \            /
        C1---------/    <- topicC
And lets make one more "topicA" related commit A2:
(11)     B1---B2---B3   <- topicB
        /           \
    ---O-------------P  <- integration <- HEAD     [ P = N + A2 ]
       |\           /|
       | A1---A2---/ /  <- topicA
       \            /
        C1---------/    <- topicC

Notice how HEAD never leaves "integration" branch, and underlying commit is recreated each time? It`s like a live branch we`re working on, but we`re not actually committing to.

No branch switching (and no working tree file changes caused by it), and we`re working on multiple topics/branches simultaneously, being able to instantly test their mutual interaction as we go, but also creating their separate (and "clean") histories at the same time.

I guess this would make most sense with simple topics we _could_ practically work on at the same time without making our life too complicated - what stands for "git add --patch", too, when working on multiple commits at the same time.

Once satisfied, of course each topic branch would need to be tested separately, and each commit, even - all the same as with "git add --patch" commits.

And "git add --patch" can still be used here, too, to distribute partial changes, currently existing together inside the working tree, to different topic branches, at the time of making the commit itself.

Does this approach make more sense in regards to "git commit --onto-parent" functionality I`m having in mind? Or I`m dreaming too much here...? :)

Show 23 quoted lines
> > Once there, starting from your initial position:
> >
> > >     ...A    ...C            <- topics A, C
> > >         \       \ E
> > >       ---o---o---o---o I    <- integration <- HEAD
> > >             /       /
> > >         ...B    ...D        <- topics B, D
> >
> > ... and doing something like `git commit --onto B --merge` would
> > yield:
> > 
> >     (3) ...A    ...C            <- topics A, C
> >           \       \ E
> >         ---o---o---o---o I'   <- integration
> >               /       /|
> >           ...B    ...D |      <- topic D
> >               \        |
> >                f-------'      <- topic B
> >
> > ... where (I' = I + f) is still true.
> 
> I am not used to this picture. I would not think that it is totally
> unacceptable, but it still has a hmm-factor.

Main idea is not to pile up uninteresting merge commits inside (throwaway) integration branch, needlessly complicating history, but pretend as we just made the integration merge, where fixup commit f was already existing in its topic branch prior the merge.

Show 15 quoted lines
> > If that`s preferred in some
> > cases, it could even look like this instead:
> >
> >   (4) ...A    ...C             <- topics A, C
> >           \       \ E  I
> >         ---o---o---o---o---F   <- integration
> >               /       /   /
> >           ...B    ...D   /     <- topic D
> >               \         /
> >                f-------'       <- topic B
> >
> > ... where F(4) = I'(3), so similar situation, just that we don`t
> > discard I but post F on top of it.
> 
> This is very acceptable.

And I can see logic behind this case, too (alongside that other one, where they can both be supported, for different scenarios).

Show 6 quoted lines
> Nevertheless, IMO, it is not the task of git-commit to re-compute a
> merge commit. It would be OK that it commits changes on top of a
> branch that is not checked out. Perhaps it would even be OK to remove
> the change from the current workspace (index and worktree), because
> it will return in the form of a merge later, but producing that merge
> is certainly not the task of git-commit.

Just to make sure we`re on the same page - you mean conceptually, or practically? Or both...? :)

Because practically, we don`t do any merge operations/computations (as can be seen inside the script), so we really don`t produce any merges :)

Having a merge commit being updated as an outcome is just a consequence of altering our history to pretend fixup commit already existed elsewhere, being brought into our current state from there - and that "current state" is updated accordingly to reflect the change we made in the history that led to it.

Let`s look at a different example, no merges involved:
(12) ---A---B---C---D  <- HEAD

Here, we can do `git commit --onto-parent C` to make the history look like this:

(13) ---A---B---C---E---D'  <- HEAD

..., where we practically inserted commit E between our HEAD and commit C, causing D to be updated to D' accordingly (rebased, practically).

Idea is the same with merges involved, updating HEAD commit to reflect altered history.

In situation (12), we could also do `git commit --onto-parent C --amend`, ending up with history like this instead:

(14) ---A---B---F---D'  <- HEAD

..., where F is amended C, E being added to it, instead of being a separate commit.

So there are quite some possibilities here.

But as said, I can understand a wish to have a commit elsewhere without "merging" it in, too (changes removed from current workspace), or "merging" it inside a separate commit (not updating existing HEAD one).

And saying that, making the merge commit at the same time could then be observed as no more than a shortcut, too, like being able to create a new branch on checkout using "-b" option.

So even conceptually, it would make sense to wish for something like this:

(15) git commit --onto B --merge

..., producing that graph (4) shown in the quote above, I would think?

But even in that case, do note no merge is really happening, we just alter history of our current state, where we already are.

Splitting changes out of HEAD to a commit directly preceding it, on a different branch, and still keeping those changes in HEAD, naturally causes our current HEAD state to have multiple parents, what is perceived as a merge commit, even without a merge operation leading to it.

Thus, `git commit` seems it should be up to the task, whichever approach user opts for :)

Regards, Buga
Previous: Johannes SixtNext: Johannes Sixt
Message 10 of 26 in “[SCRIPT/RFC 0/3] git-commit --onto-parent (three-way merge, no working tree file changes)”
  1. Igor DjordjevicNov 26, 2017
  2. 1/3 setup.shIgor Djordjevic, Nov 26, 2017
  3. 3/3 git-commit--onto-parent.shIgor Djordjevic, Nov 26, 2017
  4. 2/3 git-merge-one-file--cachedIgor Djordjevic, Nov 26, 2017
  5. Johannes SixtNov 27, 2017
  6. Igor DjordjevicNov 28, 2017
  7. Johannes SixtNov 29, 2017
  8. Igor DjordjevicNov 29, 2017
  9. Johannes SixtDec 1, 2017
  10. Igor DjordjevicDec 4, 2017
  11. Johannes SixtDec 6, 2017
  12. Junio C HamanoDec 6, 2017
  13. Igor DjordjevicDec 8, 2017
  14. Junio C HamanoDec 8, 2017
  15. Igor DjordjevicDec 8, 2017
  16. Alexei LozovskyDec 9, 2017
  17. Igor DjordjevicDec 9, 2017
  18. Phillip WoodDec 9, 2017
  19. Igor DjordjevicDec 10, 2017
  20. Phillip WoodDec 10, 2017
  21. Igor DjordjevicDec 10, 2017
  22. Alexei LozovskyDec 11, 2017
  23. Alexei LozovskyDec 11, 2017
  24. Phillip WoodDec 9, 2017
  25. Chris NerwertNov 30, 2017
  26. Igor DjordjevicDec 3, 2017

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.