git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
* RFC: new git-splice subcommand for non-interactive branch splicing
@ 2016-05-27 14:08 Adam Spiers
  2016-05-27 15:27 ` Johannes Schindelin
  2016-05-30  0:34 ` RFC: new git-transplant subcommand for non-interactively moving commits between branches Adam Spiers
  0 siblings, 2 replies; 6+ messages in thread
From: Adam Spiers @ 2016-05-27 14:08 UTC (permalink / raw)
  To: git mailing list

Hi all,

I finally got around to implementing a new git subcommand which I've
wanted for quite a while.  I've called it git-splice.

Description
-----------

git-splice(1) non-interactively splices the current branch by removing
a range of commits from within it and/or cherry-picking a range of
commits into it.  It's essentially just a glorified wrapper around
cherry-pick and rebase -i.

Usage
-----

Examples:

    # Remove commit A from the current branch
    git splice A^!

    # Remove commits A..B from the current branch
    git splice A..B

    # Remove commits A..B from the current branch, and cherry-pick
    # commits C..D at the same point
    git splice A..B C..D

    # Cherry-pick commits C..D, splicing them in just after commit A
    git splice A C..D

    # Remove first commit mentioning 'foo', and insert all commits
    # in the 'elsewhere' branch which mention 'bar'
    git splice --grep=foo -n1 HEAD -- --grep=bar HEAD..elsewhere

    # Abort a splice which failed during cherry-pick or rebase
    git splice --abort

    # Resume a splice after manually fixing conflicts caused by
    # cherry-pick or rebase
    git splice --continue

N.B. Obviously this command rewrites history!  As with git rebase,
you should be aware of all the implications of history rewriting
before using it.

Code
----

Currently this is in alpha state:

  https://github.com/git/git/compare/master...aspiers:splice

and I reserve the right to rewrite the history of that branch in the
near future ;-)

I realise that the code does not yet conform to the coding standards
of the git project.  For example, it relies on non-POSIX bash
features, like arrays.  I would be happy to fix this if there is a
chance git-splice might be accepted for inclusion within the git
distribution.  (Presumably contrib/ is another possibility.)
Also, I haven't yet written a proper man page for it.

Motivation
----------

I wrote git-splice as the next step in the journey towards being able
to implement a tool which automatically (or at least
semi-automatically) splits a linear sequence of commits into a commit
graph where ancestry exactly mirrors commit "dependency".[0]  In other
words, in this commit graph, a commit B would have commit A as an
ancestor if and *only* if commit B cannot cleanly apply without A
already being present in the branch.  As a corollary, if commit F
depends on D and E, but D and E are mutually independent, F would
need to depend on a merge commit which contains D and E.

Such a tool could be useful for a few reasons.  Firstly, large patch
series are much harder to review than single commits or small patch
series, but typical development workflows often lead to large patch
series.

For example, if I work privately on a new feature for some hours /
days / weeks, I will typically amass a bunch of commits which are not
all directly related to the new feature: there are often refactorings,
fixes for bugs discovered during development of the new feature, etc.

I doubt I'm the only git user not disciplined enough to maintain neat
branch organization for the whole of a long period of hacking!
i.e. religiously maintaining one branch per bugfix, one branch per
refactoring, and one branch for the new feature.[1]  Typically, tidying
up the branches comes a bit later, when I want to start feeding stuff
upstream for review.

Therefore being able to reduce the effort involved with breaking a
large patch series into smaller related chunks seems potentially very
useful.

As well as making reviews smaller easier, this allows both the reviews
and any corresponding CI to proceed in a more parallelized fashion.

Some review systems can implicitly discourage reviews of large patch
series, by treating each commit as a review in its own right and/or not
providing sophisticated support for patch series.  Gerrit is one
example; gitlab and GitHub are counter-examples.

I'm sure there are other use cases which I didn't think of yet.

Next steps, and the future
--------------------------

Obviously, I'd welcome thoughts on whether it would make sense to
include this in the git distribution.

In the longer term however, I'd like to write two more subcommands:

  - git-transplant(1) which wraps around git-splice(1) and enables
    easy non-interactive transplanting of a range of commits from
    one branch to another.  This should be pretty straightforward
    to implement.

  - git-explode(1) which wraps around git-transplant(1) and
    git-deps(1), and automatically breaks a linear sequence of commits
    into multiple smaller sequences, forming a commit graph where
    ancestry mirrors commit dependency, as mentioned above.  I expect
    this to be more difficult, and would probably write it in Python.

    Ideally, this tool would also be able to integrate with other
    workflow management tools[1] in order to effectively create /
    manage topic branches and track dependencies between them.

Eventually, the utopia I'm dreaming about would become a reality and
look something like this:

    git checkout -b new-feature

    while in_long_frenzied_period_of_hacking; do
        # don't worry too much about branch maintenance here, just hack
        git add ...
        git commit ...
    done

    # Break lots of commits from new-feature into new topic branches:
    git explode

    # List topic branches
    git work list

    # Manually complete tidy-up of those branches
    git push ...
    git send-email ...


Feedback on any of this is very welcome!

Thanks,
Adam


[0] https://github.com/aspiers/git-deps/#user-content-use-case-2-splitting-a-patch-series

    This type of dependency could be described as textual or syntactic or
    lexical, and is automatically detected by git-deps:

        https://github.com/aspiers/git-deps/

    which I wrote a couple of years ago and previously announced on this list:

        http://thread.gmane.org/gmane.comp.version-control.git/262000/focus=262606

    Of course, this is a somewhat naive approach in that it has no
    awareness of semantic dependencies, e.g. commit A changing file X
    in a way which only makes sense if commit B changing file Y is
    already present.  However in my experience it's still a useful
    start in the right direction, saving a lot of time by detecting
    the "obvious" dependencies, and often revealing dependencies which
    I would have otherwise missed.

[1] There are tools which can help with this, e.g. topgit, git-flow,
    and gitwork, which IMHO is particularly interesting.

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: RFC: new git-splice subcommand for non-interactive branch splicing
  2016-05-27 14:08 RFC: new git-splice subcommand for non-interactive branch splicing Adam Spiers
@ 2016-05-27 15:27 ` Johannes Schindelin
  2016-05-27 16:36   ` Adam Spiers
  2016-05-30  0:34 ` RFC: new git-transplant subcommand for non-interactively moving commits between branches Adam Spiers
  1 sibling, 1 reply; 6+ messages in thread
From: Johannes Schindelin @ 2016-05-27 15:27 UTC (permalink / raw)
  To: Adam Spiers; +Cc: git mailing list

Hi Adam,

On Fri, 27 May 2016, Adam Spiers wrote:

> Description
> -----------
> 
> git-splice(1) non-interactively splices the current branch by removing
> a range of commits from within it and/or cherry-picking a range of
> commits into it.  It's essentially just a glorified wrapper around
> cherry-pick and rebase -i.

It sounds as if you could accomplish the same with

	git checkout -b former-commits <split>
	git checkout -b latter-commits <base>
	git cherry-pick <split>..HEAD@{2}

> Next steps, and the future
> --------------------------
> 
> Obviously, I'd welcome thoughts on whether it would make sense to
> include this in the git distribution.

Far be I from discouraging you to work on these scripts, but I think that
a really good place for such subcommands is a separate repository, as you
have it already. There are already some rarely used subcommands in
libexec/git-core/ cluttering up the space and I would be reluctant to add
even more subcommands to the default Git installation delivered to every
user.

You can *always* just extend the PATH so that git-splice can be found;
Then `git splice ...` will do exactly what you want. That is e.g. how
git-flow works. (Of course I hope that you will maintain your scripts
much, much better than git-flow, i.e. not abandon all users).

> In the longer term however, I'd like to write two more subcommands:
> 
>   - git-transplant(1) which wraps around git-splice(1) and enables
>     easy non-interactive transplanting of a range of commits from
>     one branch to another.  This should be pretty straightforward
>     to implement.

This is just cherry-pick with a range...

>   - git-explode(1) which wraps around git-transplant(1) and
>     git-deps(1), and automatically breaks a linear sequence of commits
>     into multiple smaller sequences, forming a commit graph where
>     ancestry mirrors commit dependency, as mentioned above.  I expect
>     this to be more difficult, and would probably write it in Python.

You mean something like Darcs on top of Git. Essentially, you want to end
up with an octopus merge of branches whose commits would conflict if
exchanged.

I implemented the logic for this in a shell script somewhere, so it is not
*all* that hard (Python not required). But I ended up never quite using it
because it turns out that in practice, the commit "dependency" (as defined
by the commit diffs) does not really reflect the true dependency.

For example, in my work to move large parts of rebase -i into a builtin, I
have an entire series of commits that do nothing else but prepare the
sequencer for rebase -i's functionality. Most of these commits touch
completely separate parts of the code, so they would make independent
branches in your git-explode command. Yet, that would destroy the story
that the patch series tells, as the natural flow would get lost.

Another major complication is that sometimes the "dependency as per the
diff" is totally bogus. Take for example Git for Windows' patches on top
of Git: there are a couple "sub branches" that add global variables to
environment.c. By the logic of the overlapping (or touching) hunks, these
sub branches should build on top of each other, right? But they are
logically completely independent.

So I think that this is a nice exercise, but in practice it will require a
human to determine which commits really depend on each other.

> Eventually, the utopia I'm dreaming about would become a reality and
> look something like this:
> 
>     git checkout -b new-feature
> 
>     while in_long_frenzied_period_of_hacking; do
>         # don't worry too much about branch maintenance here, just hack
>         git add ...
>         git commit ...
>     done
> 
>     # Break lots of commits from new-feature into new topic branches:
>     git explode
> 
>     # List topic branches
>     git work list

You would render me *really* impressed if you could come up with an
automated way to determine logical dependencies between patches.

Ciao,
Johannes

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: RFC: new git-splice subcommand for non-interactive branch splicing
  2016-05-27 15:27 ` Johannes Schindelin
@ 2016-05-27 16:36   ` Adam Spiers
  2016-05-28  7:06     ` Johannes Schindelin
  0 siblings, 1 reply; 6+ messages in thread
From: Adam Spiers @ 2016-05-27 16:36 UTC (permalink / raw)
  To: git mailing list

Hi Johannes,

Thanks for the quick reply!  Responses inline below:

On Fri, May 27, 2016 at 05:27:14PM +0200, Johannes Schindelin wrote:
> On Fri, 27 May 2016, Adam Spiers wrote:
> 
> > Description
> > -----------
> > 
> > git-splice(1) non-interactively splices the current branch by removing
> > a range of commits from within it and/or cherry-picking a range of
> > commits into it.  It's essentially just a glorified wrapper around
> > cherry-pick and rebase -i.
> 
> It sounds as if you could accomplish the same with
> 
>       git checkout -b former-commits <split>
>       git checkout -b latter-commits <base>
>       git cherry-pick <split>..HEAD@{2}

Not really - that is missing several features which git-splice
provides, e.g.

  - The ability to remove a non-consecutive list of commits
    from the branch.

  - The ability to insert commits at the same time as removing
    (granted, that's just in extra cherry-pick your method, but again
    that's another thing to orchestrate).

  - The ability to specify commits to remove / insert using
    arguments understood by git-rev-list.

  - The patch-id magic which is built into git-rebase.  This
    would kick in if any of the commits to insert are already
    in <split>..HEAD@{2} (using your reference terminology).

  - A single command to orchestrate the whole workflow, including
    cleanup, and --abort and --continue when manual conflict
    resolution is required.  This modularity should help a lot when
    building further tools which wrap around it in order to perform
    more complex tasks.

This last point is perhaps the most important.  Of course it's
possible to do this manually already.  But the whole point of
git-splice is to automate it in a convenient and reliable manner.

> > Next steps, and the future
> > --------------------------
> > 
> > Obviously, I'd welcome thoughts on whether it would make sense to
> > include this in the git distribution.
> 
> Far be I from discouraging you to work on these scripts, but I think that
> a really good place for such subcommands is a separate repository, as you
> have it already. There are already some rarely used subcommands in
> libexec/git-core/ cluttering up the space and I would be reluctant to add
> even more subcommands to the default Git installation delivered to every
> user.

Sure, I appreciate the difficulty in deciding where to draw the line.
My feeling is that rebase -i provides something tremendously
important, which the vast majority of users use on a regular basis,
but that git is currently missing a convenient way to
*non-interactively* perform the same magic which rebase -i
facilitates.  And removing / reordering commits is surely one of the
most common use cases of rebase -i, so I think a lot of people could
benefit from some porcelain to automate that and allow building
higher-level tools on top of it.

I suspect the most popular use-case in the short term would be the
infamous "oops, I only just noticed that I put that commit on the
wrong branch, and now there's already a whole bunch of other commits
on top of it".  I would expect that reducing this solution to a single
git-transplant(1) command would be pretty attractive for a lot of
people.  And of course GUIs / IDEs could incorporate it into their
more beautiful front-ends.  However, if it's not in git core, that's
unlikely to happen.

> You can *always* just extend the PATH so that git-splice can be found;
> Then `git splice ...` will do exactly what you want. That is e.g. how
> git-flow works.

Sure, I've been using that trick since at least 2009 ;-) [0]

> (Of course I hope that you will maintain your scripts
> much, much better than git-flow, i.e. not abandon all users).

I hope so too ;-)

> > In the longer term however, I'd like to write two more subcommands:
> > 
> >   - git-transplant(1) which wraps around git-splice(1) and enables
> >     easy non-interactive transplanting of a range of commits from
> >     one branch to another.  This should be pretty straightforward
> >     to implement.
> 
> This is just cherry-pick with a range...

No it's not:

  - git-transplant would be able to splice commits from one branch
    *into* (i.e. inside, *not* onto) another branch.

  - git-transplant would also take care of removing the commits from
    the source branch, but not before they were safely inside the
    destination branch.

  - git-transplant would orchestrate the whole workflow with a single
    command, complete with --abort and --continue.

> >   - git-explode(1) which wraps around git-transplant(1) and
> >     git-deps(1), and automatically breaks a linear sequence of commits
> >     into multiple smaller sequences, forming a commit graph where
> >     ancestry mirrors commit dependency, as mentioned above.  I expect
> >     this to be more difficult, and would probably write it in Python.
> 
> You mean something like Darcs on top of Git. Essentially, you want to end
> up with an octopus merge of branches whose commits would conflict if
> exchanged.

Something like that, yes, but it's not as simple as a single octopus
merge.  It would support arbitrarily deep DAGs of topic branches.

> I implemented the logic for this in a shell script somewhere, so it is not
> *all* that hard (Python not required). But I ended up never quite using it
> because it turns out that in practice, the commit "dependency" (as defined
> by the commit diffs) does not really reflect the true dependency.
>
> For example,

[snipped examples]

Sure - I already covered this concern in footnote [0] of my previous
mail; maybe you missed that?  As I said there, in my experience, I
have found it very useful to be able to automatically detect textual
dependencies via git-deps, even though they do not represent the
entire set of dependencies.  I've even recorded a YouTube screencast
demonstrating one such use case[1].  So please don't let the question
for perfection become the enemy of the good ;-)

> So I think that this is a nice exercise, but in practice it will require a
> human to determine which commits really depend on each other.

Of course - this is exactly why I wrote "or at least
semi-automatically" in the first mail of this thread.  But even though
git-deps / git-explode can never automatically handle *all*
dependencies, they can handle enough dependencies to be significantly
useful.  I have concrete real-world experience of that.  In fact there
is one scenario I am working on right now which is current proof of
this (no coincidence, since that's what motivated me to take this next
step on this journey and write git-splice):

I have made a large bunch of small commits to a single text file
(design document).  Some are possibly contentious; some aren't.  So I
need to split them out into a series of smaller independent patch
series which I can submit to gerrit for review, thereby making life
easier for the reviewers and minimizing any bottlenecks where reviews
for one change are blocked because another change hasn't been reviewed
yet.  And in this case, because the changes are all applying to a
single file containing only natural language, git-deps correctly
determines *all* dependencies, not just textual ones.

> You would render me *really* impressed if you could come up with an
> automated way to determine logical dependencies between patches.

Hey, I would *really* impress myself if I could do that, too; after
all, that would be a pretty sophisticated form of artificial
intelligence :-)

Thanks again for the feedback!

Adam


[0] https://github.com/aspiers/git-config/commit/287685408326

    BTW there are currently 36 git-* scripts in that repo; you may or
    may not find it interesting to browse through them.

[1] https://github.com/aspiers/git-deps#use-case-1-porting-between-branches

    In this case, git-deps can serve as an early warning system to
    flag when a backporting task's cost:reward ratio would be too
    high to justify starting work on it.

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: RFC: new git-splice subcommand for non-interactive branch splicing
  2016-05-27 16:36   ` Adam Spiers
@ 2016-05-28  7:06     ` Johannes Schindelin
  2016-05-28 11:24       ` Adam Spiers
  0 siblings, 1 reply; 6+ messages in thread
From: Johannes Schindelin @ 2016-05-28  7:06 UTC (permalink / raw)
  To: Adam Spiers; +Cc: git mailing list

Hi Adam,

please reply-to-all on this list.

On Fri, 27 May 2016, Adam Spiers wrote:

> My feeling is that rebase -i provides something tremendously
> important, which the vast majority of users use on a regular basis,
> but that git is currently missing a convenient way to
> *non-interactively* perform the same magic which rebase -i
> facilitates.

Would it not make sense to enhance `rebase -i`, then?

I, for one, plan to port my Git garden shears (at least partially) once I
managed to get my rebase--helper work in. The shears script is kind of a
"--preseve-merges done right":

	https://github.com/git-for-windows/build-extra/blob/master/shears.sh

It allows you to (re-)create a branch structure like this:

	bud
	pick cafebabe XYZ
	pick 01234567 Another patch
	mark the-first-branch

	bud
	pick 98765432 This patch was unrelated
	mark the-second-branch

	bud
	merge the-first-branch
	merge the-second-branch

Of course, this is interactive. But quite frankly, you want to be able to
perform quite complicated stuff, and I think the command-line offers only
an inadequate interface for this.

> I suspect the most popular use-case in the short term would be the
> infamous "oops, I only just noticed that I put that commit on the
> wrong branch, and now there's already a whole bunch of other commits
> on top of it".

I have two workflows for that. The simpler one:

	git checkout other-branch
	git commit
	git checkout @{-1}

Sometimes I need to call `git stash -p` before, and `git stash apply`
after those calls.

The more complicated one comes in handy when a complete rebuild takes a
long time, and branch switching would trigger a rebuild:

	# Here, I stash what I *want* on the other branch
	git stash -p
	git worktree add throwaway other-branch
	cd throwaway
	git stash apply
	git commit

I did use the approach you proposed a couple of times: just commit in the
middle, and sort things out later. The problem: I frequently forgot, and
if I did not, reordering the commits resulted in stupid and avoidable
merge conflicts.

> > > In the longer term however, I'd like to write two more subcommands:
> > > 
> > >   - git-transplant(1) which wraps around git-splice(1) and enables
> > >     easy non-interactive transplanting of a range of commits from
> > >     one branch to another.  This should be pretty straightforward
> > >     to implement.
> > 
> > This is just cherry-pick with a range...
> 
> No it's not:
> 
>   - git-transplant would be able to splice commits from one branch
>     *into* (i.e. inside, *not* onto) another branch.

Okay, but in case of merge conflicts, you still have to switch to the
other branch, right?

>   - git-transplant would also take care of removing the commits from
>     the source branch, but not before they were safely inside the
>     destination branch.

That assumes a workflow where you develop on one big messy branch and
later sort it out into the appropriate, separate branches, right? I admit
that I used to do that, too, but ever since worktrees arrived, I do not do
that anymore: it resulted in too much clean-up work. Better to put the
commits into the correct branch right away. Of course, that is just *my*
preference.

>   - git-transplant would orchestrate the whole workflow with a single
>     command, complete with --abort and --continue.

cherry-pick also sports --abort and --continue.

> > >   - git-explode(1) which wraps around git-transplant(1) and
> > >     git-deps(1), and automatically breaks a linear sequence of commits
> > >     into multiple smaller sequences, forming a commit graph where
> > >     ancestry mirrors commit dependency, as mentioned above.  I expect
> > >     this to be more difficult, and would probably write it in Python.
> > 
> > You mean something like Darcs on top of Git. Essentially, you want to end
> > up with an octopus merge of branches whose commits would conflict if
> > exchanged.
> 
> Something like that, yes, but it's not as simple as a single octopus
> merge.  It would support arbitrarily deep DAGs of topic branches.

Yes, of course. Because

A - B - C - D

might need to resolve into

A - M1 - C - M3
  X        /
B - M2 - D

> > I implemented the logic for this in a shell script somewhere, so it is not
> > *all* that hard (Python not required). But I ended up never quite using it
> > because it turns out that in practice, the commit "dependency" (as defined
> > by the commit diffs) does not really reflect the true dependency.
> >
> > For example,
> 
> [snipped examples]
> 
> Sure - I already covered this concern in footnote [0] of my previous
> mail; maybe you missed that?

I think it deserves more prominent a place than a footnote.

> > So I think that this is a nice exercise, but in practice it will
> > require a human to determine which commits really depend on each
> > other.
> 
> Of course - this is exactly why I wrote "or at least
> semi-automatically" in the first mail of this thread.  But even though
> git-deps / git-explode can never automatically handle *all*
> dependencies, they can handle enough dependencies to be significantly
> useful.  I have concrete real-world experience of that.

I'd love to see those examples where it worked, because it sure did not
work for me (I wasted two weeks to implement that script that I never used
successfully).

> I have made a large bunch of small commits to a single text file
> (design document).  Some are possibly contentious; some aren't.

Ah. Well, as I said, I changed my workflow to use multiple worktrees with
multiple branches. The contentious changes would go into at least one
branch, more likely multiple. The uncontentious changes would go into
another.

And most likely at least some of those branches would cause merge
conflicts. However, they would do so only once, not multiple times during
the cleaning-up phase.

I did actually track the time at some stage to determine what is faster.
Sorting things into multiple branches won hands down (in my hands). And
no: I did not believe it would.

> So I need to split them out into a series of smaller independent patch
> series which I can submit to gerrit for review, thereby making life
> easier for the reviewers and minimizing any bottlenecks where reviews
> for one change are blocked because another change hasn't been reviewed
> yet.  And in this case, because the changes are all applying to a single
> file containing only natural language, git-deps correctly determines
> *all* dependencies, not just textual ones.

I do not buy that. When you introduce a section way down in the document
for which you have to introduce a new definition in one of the first
sections, logically those two changes belong to the same topic branch. Yet
git-deps would have no chance to determine that.

> > You would render me *really* impressed if you could come up with an
> > automated way to determine logical dependencies between patches.
> 
> Hey, I would *really* impress myself if I could do that, too; after
> all, that would be a pretty sophisticated form of artificial
> intelligence :-)

Yep, I will definitely follow your progress!

Ciao,
Johannes

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: RFC: new git-splice subcommand for non-interactive branch splicing
  2016-05-28  7:06     ` Johannes Schindelin
@ 2016-05-28 11:24       ` Adam Spiers
  0 siblings, 0 replies; 6+ messages in thread
From: Adam Spiers @ 2016-05-28 11:24 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: git mailing list, Jon Seymour

On Sat, May 28, 2016 at 09:06:59AM +0200, Johannes Schindelin wrote:
> Hi Adam,
> 
> please reply-to-all on this list.

Sorry, I forgot that was the policy here.  Every list and individual
has different preferences on whether to Cc: on list mail, so I find it
almost impossible to keep track of who prefers what :-/

> On Fri, 27 May 2016, Adam Spiers wrote:
> > My feeling is that rebase -i provides something tremendously
> > important, which the vast majority of users use on a regular basis,
> > but that git is currently missing a convenient way to
> > *non-interactively* perform the same magic which rebase -i
> > facilitates.
> 
> Would it not make sense to enhance `rebase -i`, then?

You mean enhance it to support non-interactive usage?  That wouldn't
make much sense to me, given that -i is short for --interactive.  Even
if we added a new non-interactive rebase mode which let you edit the
commits prior to rebasing them, I can't imagine how it would need to
be any different to how non-interactive rebase -i currently works,
i.e. setting GIT_SEQUENCE_EDITOR to a non-interactive command which
modifies the rebase todo file passed via $1.

Or maybe you are suggesting to enhance it to perform operations on the
rebase todo list, such as removing a commit from the todo list, or
moving a commit to a different position?  But that sounds like scope
creep to me; IMHO it would be cleaner for rebase -i to remain an
unopinionated platform for history editing, rather than to make
assumptions about common history editing workflows.  I think those
assumptions belong in higher-level porcelain tools.

Or if you have some other enhancement in mind, please share details!

> I, for one, plan to port my Git garden shears (at least partially) once I
> managed to get my rebase--helper work in. The shears script is kind of a
> "--preseve-merges done right":
> 
>     https://github.com/git-for-windows/build-extra/blob/master/shears.sh
> 
> It allows you to (re-)create a branch structure like this:
> 
>     bud
>     pick cafebabe XYZ
>     pick 01234567 Another patch
>     mark the-first-branch
> 
>     bud
>     pick 98765432 This patch was unrelated
>     mark the-second-branch
> 
>     bud
>     merge the-first-branch
>     merge the-second-branch
> 
> Of course, this is interactive.

Interesting approach; thanks for sharing.  At a first glance, this
does sound similar to what topgit and gitwork are trying to achieve.
I don't entirely understand it yet, however; it's hard to without
knowing more about the structure of Git for Windows' integration
branch and seeing a concrete example and/or comprehensive
documentation.  For example, it's not clear to me how
generate_script() works, or how it lets you modify just one of the
topic "sub-branches" and then automatically update all other
sub-branches which depend on it?

> But quite frankly, you want to be able to
> perform quite complicated stuff

Why do you say that?  Splice and transplant operations are
conceptually very straightforward, and not even particularly hard to
implement.  git-splice only took me a day or so, and I expect
git-transplant to be quicker.

The only complicated thing I want to implement is git-explode, but if
I have splice and transplant "primitives" available, then it should be
quite easy to implement git-explode as a series of splice/transplant
operations.

> and I think the command-line offers only an inadequate interface for
> this.

Please can you give an example where it would be inadequate?

> > I suspect the most popular use-case in the short term would be the
> > infamous "oops, I only just noticed that I put that commit on the
> > wrong branch, and now there's already a whole bunch of other commits
> > on top of it".
> 
> I have two workflows for that. The simpler one:
> 
>     git checkout other-branch
>     git commit
>     git checkout @{-1}
>
> after those calls.
> 
> The more complicated one comes in handy when a complete rebuild takes a
> long time, and branch switching would trigger a rebuild:
> 
>     # Here, I stash what I *want* on the other branch
>     git stash -p
>     git worktree add throwaway other-branch
>     cd throwaway
>     git stash apply
>     git commit

Neither of these workflows work with the scenario I described.  In my
scenario, the commit is already buried beneath other commits.

> I did use the approach you proposed a couple of times: just commit in the
> middle, and sort things out later. The problem: I frequently forgot, and
> if I did not, reordering the commits resulted in stupid and avoidable
> merge conflicts.

I think you are misunderstanding me - I am not proposing this
workflow; in fact that would be stupid because it's already in common
usage.  But I'm not even advocating it.  I'm saying:

  - I do it regularly by accident (since when I am in the hacking
    zone, I am usually focused on the code, not on branch maintenance).

  - When I eventually realise I've done it, I need to go back afterwards
    and clean up the mess by decomposing the linear series of commits
    into separate topic branches.

  - No really good higher-level topic branch maintenance tools exist yet.

  - This is a common problem which many git users suffer from, based
    on a) my experience collaborating with others, b) personal
    feedback I've received on this topic, and c) googling for things
    like "git move commit to another branch".

  - There needs to be an easier way to clean up the mess, which minimises
    the pain resulting from merge conflicts.

> > > > In the longer term however, I'd like to write two more subcommands:
> > > > 
> > > >   - git-transplant(1) which wraps around git-splice(1) and enables
> > > >     easy non-interactive transplanting of a range of commits from
> > > >     one branch to another.  This should be pretty straightforward
> > > >     to implement.
> > > 
> > > This is just cherry-pick with a range...
> > 
> > No it's not:
> > 
> >   - git-transplant would be able to splice commits from one branch
> >     *into* (i.e. inside, *not* onto) another branch.
> 
> Okay, but in case of merge conflicts, you still have to switch to the
> other branch, right?

Nope, if there are conflicts, you would either do

    git transplant --abort

leaving you where you started, or fix the conflicts and then do

    git transplant --continue

leaving you on the original branch, but with the transplanted commits
no longer in that branch and instead in the target branch.

> >   - git-transplant would also take care of removing the commits from
> >     the source branch, but not before they were safely inside the
> >     destination branch.
> 
> That assumes a workflow where you develop on one big messy branch and
> later sort it out into the appropriate, separate branches, right? I admit
> that I used to do that, too, but ever since worktrees arrived, I do not do
> that anymore: it resulted in too much clean-up work. Better to put the
> commits into the correct branch right away. Of course, that is just *my*
> preference.

It's *my* preference too, as I already stated above.  That does not
change the fact that even with the best intentions to avoid this
workflow, it will still happen, *especially* when there are still no
good higher-level tools which help avoid it.  BTW gitwork is the
closest I've seen to a tool which correctly addresses this:

    https://jonseymour.s3.amazonaws.com/git-work.html#_discussion

but AFAIK it hasn't been updated in 3 years.  Maybe Jon (cc'd) can
comment on whether that means it already worked perfectly 3 years ago ;-)

> >   - git-transplant would orchestrate the whole workflow with a single
> >     command, complete with --abort and --continue.
> 
> cherry-pick also sports --abort and --continue.

If you look at my implementation of git-splice you'll see that it uses
cherry-pick --abort and --continue.  So thanks but I'm already fully
aware of that :-)  However that misses the point.

Transplant needs to be a composite of multiple operations (splice into
the target branch followed by splice to remove from the source
branch).  Therefore given that either of these splice operations can
fail due to conflicts, the overall transplant needs to be orchestrated
as a single workflow which supports --abort and --continue.  For
example, if the second splice fails with conflicts, the user needs to
be able _with_a_single_command_ to roll back to the state before the
transplant started.  That means aborting the second splice, and
reverting the first.

If rollback is not as easy as a single command, the UX will create
fear of failure, which will discourage users from using the tool.

> > > >   - git-explode(1) which wraps around git-transplant(1) and
> > > >     git-deps(1), and automatically breaks a linear sequence of commits
> > > >     into multiple smaller sequences, forming a commit graph where
> > > >     ancestry mirrors commit dependency, as mentioned above.  I expect
> > > >     this to be more difficult, and would probably write it in Python.
> > > 
> > > You mean something like Darcs on top of Git. Essentially, you want to end
> > > up with an octopus merge of branches whose commits would conflict if
> > > exchanged.
> > 
> > Something like that, yes, but it's not as simple as a single octopus
> > merge.  It would support arbitrarily deep DAGs of topic branches.
> 
> Yes, of course. Because
> 
> A - B - C - D
> 
> might need to resolve into
> 
> A - M1 - C - M3
>   X        /
> B - M2 - D

Why are both M1 and M2 needed here?  Can't D be a child of M1?

    A---M1---C---M3
       /  \     /
    B-'    `-D-'

> > > I implemented the logic for this in a shell script somewhere, so it is not
> > > *all* that hard (Python not required). But I ended up never quite using it
> > > because it turns out that in practice, the commit "dependency" (as defined
> > > by the commit diffs) does not really reflect the true dependency.
> > >
> > > For example,
> > 
> > [snipped examples]
> > 
> > Sure - I already covered this concern in footnote [0] of my previous
> > mail; maybe you missed that?
> 
> I think it deserves more prominent a place than a footnote.

I originally had it inline ;-/ but then moved it to a footnote since
it was somewhat orthogonal to the main subject (git-splice) and I
thought it was at risk of diluting focus.  Sorry if that was the
wrong decision.

> > > So I think that this is a nice exercise, but in practice it will
> > > require a human to determine which commits really depend on each
> > > other.
> > 
> > Of course - this is exactly why I wrote "or at least
> > semi-automatically" in the first mail of this thread.  But even though
> > git-deps / git-explode can never automatically handle *all*
> > dependencies, they can handle enough dependencies to be significantly
> > useful.  I have concrete real-world experience of that.
> 
> I'd love to see those examples where it worked, because it sure did not
> work for me (I wasted two weeks to implement that script that I never used
> successfully).

Yeah - I was hoping to make another YouTube video demonstrating one of
them, but I've gone over my time budget simply by implementing
git-splice (and unit tests) and then discussing it here :-/

> > I have made a large bunch of small commits to a single text file
> > (design document).  Some are possibly contentious; some aren't.
> 
> Ah. Well, as I said, I changed my workflow to use multiple worktrees with
> multiple branches. The contentious changes would go into at least one
> branch, more likely multiple. The uncontentious changes would go into
> another.

Exactly, that's the ideal situation we're aiming for.  The question is
how to get there.  Doing this branch maintenance manually is currently
too inconvenient for most users.  My belief is that building primitive
operations such as splice and transplant will encourage the
development of higher-level tools which can further automate the
maintenance tasks.

> And most likely at least some of those branches would cause merge
> conflicts.

Small nitpick: technically, branches can't cause merge conflicts; only
operations such as cherry-pick / merge / rebase can cause them.

> However, they would do so only once, not multiple times during
> the cleaning-up phase.

Sorry, I'm not sure I understand this, because I'm not sure exactly
which operations you are referring to.

> I did actually track the time at some stage to determine what is faster.
> Sorting things into multiple branches won hands down (in my hands). And
> no: I did not believe it would.

It can certainly win, yes, and as I already said, this is my favoured
approach too, when I remember to do it.  But the win gets less likely
for each dependency which exists between branches, in the absence of
good branch management tools.

> > So I need to split them out into a series of smaller independent patch
> > series which I can submit to gerrit for review, thereby making life
> > easier for the reviewers and minimizing any bottlenecks where reviews
> > for one change are blocked because another change hasn't been reviewed
> > yet.  And in this case, because the changes are all applying to a single
> > file containing only natural language, git-deps correctly determines
> > *all* dependencies, not just textual ones.
> 
> I do not buy that. When you introduce a section way down in the document
> for which you have to introduce a new definition in one of the first
> sections, logically those two changes belong to the same topic branch. Yet
> git-deps would have no chance to determine that.

Sure, but none of the 24 commits I am referring to do that - a fact of
which I was entirely aware when I performed the analysis with
git-deps.  Sorry for omitting this from the above "because ..." clause.

But I would like to reiterate: just because it is known not to work in
all situations, it does *not* mean it can't be of use in any
situation.  Again, don't let the perfect become the enemy of the good.

Cheers,
Adam

^ permalink raw reply	[flat|nested] 6+ messages in thread

* RFC: new git-transplant subcommand for non-interactively moving commits between branches
  2016-05-27 14:08 RFC: new git-splice subcommand for non-interactive branch splicing Adam Spiers
  2016-05-27 15:27 ` Johannes Schindelin
@ 2016-05-30  0:34 ` Adam Spiers
  1 sibling, 0 replies; 6+ messages in thread
From: Adam Spiers @ 2016-05-30  0:34 UTC (permalink / raw)
  To: git mailing list

On Fri, May 27, 2016 at 03:08:11PM +0100, Adam Spiers wrote:
> Hi all,
>
> I finally got around to implementing a new git subcommand which I've
> wanted for quite a while.  I've called it git-splice.

[snipped]

> Next steps, and the future
> --------------------------
>
> Obviously, I'd welcome thoughts on whether it would make sense to
> include this in the git distribution.
>
> In the longer term however, I'd like to write two more subcommands:
>
>   - git-transplant(1) which wraps around git-splice(1) and enables
>     easy non-interactive transplanting of a range of commits from
>     one branch to another.  This should be pretty straightforward
>     to implement.

I've now written git-transplant too (and in the process of doing so,
made many more enhancements to git-splice).


Usage
-----

Examples:

    # Move commits A..B from the current branch onto branch X
    git transplant A..B X

    # Move commits A..B from the current branch into branch X after commit C
    git transplant --after=C A..B X

    # Create a new branch X starting at ref Y, then
    # move commits A..B from the current branch onto X
    git transplant --new-from=Y C A..B X

    # Abort a transplant which failed during cherry-pick or rebase
    git transplant --abort

    # Resume a transplant after manually fixing conflicts caused by
    # cherry-pick or rebase
    git transplant --continue

N.B. this command rewrites history, since after splicing the commits
into the target branch, it removes them from the current branch.  As
with git rebase, you should be aware of all the implications of
history rewriting before using it.


Motivation
----------

See the rest of this mail thread.


Code
----

> Currently this is in alpha state:
>
>   https://github.com/git/git/compare/master...aspiers:splice
>
> and I reserve the right to rewrite the history of that branch in the
> near future ;-)

This still holds for git-splice, but also now for git-transplant too:
the same branch now contains git-transplant and its test suite.


Next steps
----------

- Wait for feedback.  (Ideally I hope people will actually try both tools.)

- If there is any appetite for eventually moving this into the
  distribution, it will still need quite a few things cleaning up first,
  e.g. making the coding style consistent with git's, getting rid of
  bashisms ...

- (Eventually) write git-explode, as explained elsewhere in this thread.


Cheers!
Adam

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2016-05-30  0:34 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-05-27 14:08 RFC: new git-splice subcommand for non-interactive branch splicing Adam Spiers
2016-05-27 15:27 ` Johannes Schindelin
2016-05-27 16:36   ` Adam Spiers
2016-05-28  7:06     ` Johannes Schindelin
2016-05-28 11:24       ` Adam Spiers
2016-05-30  0:34 ` RFC: new git-transplant subcommand for non-interactively moving commits between branches Adam Spiers

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).