Re: [PATCH] add commit --interactive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



I am guilty of introducing "add --interactive" and am somewhat
regretting it.

Why?

Because it encourages a wrong workflow, but the word "wrong"
needs to be clarified.

The workflow assumed by "add --interactive" (hence by its
wrapper, "commit --interactive") goes like this:

 1. You have some large task.  You start hacking and keep hacking
    and hacking.

 2. Your working tree might still be a messy state, but there are
    some good bits in the mess that are worth committing on their
    own, excluding the other changes in the working tree.  This
    could happen for a number of reasons:

    - You might be actually done with the change and your working
      tree is perfect, but the change since the HEAD commit is
      too big to be a single logical change.  You want to split
      the changes into smaller steps.

    - You might be still in the middle of what you initially
      wanted to achieve, but at the same time you found
      something worth changing, which is independently useful
      regardless of what you are doing.  It may be a typo.  It
      may be a bug that might or might not affect what you are
      doing.  But as long as you found it and fixed it, you
      would want to make a separate commit.

 3. So you would fire up "add --interactive", and isolate the
    "good bits" by updating the index selectively to prepare for
    commit.  You would run "diff --cached" to review the change
    you are going to commit makes sense, and then finally make a
    commit.

I am not saying the first two steps are wrong.  Everybody does
that all the time.

What's wrong is the approach "add --interactive" takes in the
last step.  The isolation of "good bits" happens in the index,
not in the working tree --- which is largely because git is
designed to commit the state recorded in the index --- but that
encourages the user to commit something that has never been in
the working tree, so by definition what is committed in step 3
never could have been even tested.  And that is what I feel is
very wrong.  People in the past claimed that they later would go
back to each individual commit and test them, but that is simply
hard to believe, because it is tedious in practice.

We should be able to do better than that.  Instead of the step 3
in the above, we could have a way to quickly stash the working
tree state, reset the working tree to that of the last commit
(or whatever commit we want to build a new commit on top of),
and selectively apply parts of the the changes we stashed to the
working tree.  Then we can test the state in the working tree,
and run "commit -a" as usual.  What's still left in the stashed
state can incrementally be trickled in to make further commits.

Probably the operation would go like this.  I'll describe them
in terms of lower level operations, and leave the scripting to
others.

* Stashing the current state

        git commit -a -m 'stashed'
	git tag -f stash
        git reset --hard HEAD^

  After this point, "diff stash^ stash" is the changes yet
  to be applied.

* Switch to the commit you want to apply part of what was
  stashed to.  This part is optional if you are only building on
  top of the current HEAD, but if you have a small but urgent
  bugfix you might want to switch to another branch (say,
  'maint').  There is no magic command needed -- just run

	git checkout maint

  as usual, for example.

* Trickle some of the stashed changes in, perhaps interactively:

	git pick stash

  would probably give an interactive UI similar to "add -i" to
  let you pick and choose from "git show stash" (either per-hunk
  or per-path), but unlike "add -i", which applies the chosen
  change only to the index, it applies the change to the working
  tree and to the index.  As the last step of its operation,
  "git pick" would save the resulting index state as a tree:

	git tag -f pre-fixup `git-write-tree`

* Review and test the change in the working tree.  This does not
  need any special command.  You may also make fix-up in the
  working tree as usual.  Then you would make a commit, perhaps
  using the usual "git commit".

* Then this is the tricky and interesting part.  We need to
  subtract the change we already used to advance HEAD from
  "stashed changes".  I do not think we currently have a single
  command to do this step, but it would probably go like this.

  1. First, we build a tree that is stash^ plus the changes
     we committed right now.

	git read-tree -m -u stash^
	git merge-recursive HEAD^ -- stash^ HEAD
	NEW_TREE=`git write-tree`
        NEW_BASE=`echo stash | git commit-tree $NEW_TREE`

     This would make our working tree and the index match the
     state you would have had, had you started from the original
     state and made only the changes you have committed just
     now.  Note that the resulting tree contains the manual
     fix-ups you made since pre-fixup.

  2. The tree state you would eventually want to have used to be
     in stash but that lacks the manual fix-up you made, which
     is the difference between pre-fixup and HEAD.  So let's
     update the final state with this difference:

	git read-tree -m -u stash
        git merge-recursive pre-fixup -- stash HEAD
	NEW_TREE=`git write-tree`

  3. The strategy is to keep stash pointed at a commit that has
     the tree state of what you eventually would want to have
     (which is now in $NEW_TREE), and represent the changes yet
     to be applied as "diff stash^ stash".  So let's update the
     stash tag:

	NEW_STASH=`echo stash | git commit-tree $NEW_TREE -p $NEW_BASE`
	git tag -f stash $NEW_STASH

* You just finished one step, and still have remainder that can
  be seen from "git show stash".  Go back to "Switch to the
  commit" step (or "Trickle some of the stashed changes" step)
  above and repeat.  The output from "git show stash" will keep
  shrinking, and when it becomes empty you are done.


-
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]