* [PATCH] add commit --interactive
@ 2007-03-01 10:56 Paolo Bonzini
2007-03-02 6:06 ` Junio C Hamano
0 siblings, 1 reply; 3+ messages in thread
From: Paolo Bonzini @ 2007-03-01 10:56 UTC (permalink / raw)
To: git
[-- Attachment #1: Type: text/plain, Size: 162 bytes --]
The --interactive option behaves like "git commit", except that
"git add --interactive" is executed before committing. It is
incompatible with -a and -i.
Paolo
[-- Attachment #2: git-commit-interactive.patch --]
[-- Type: text/plain, Size: 2823 bytes --]
* git-commit: add a --interactive option.
The --interactive option behaves like "git commit", except that
"git add --interactive" is executed before committing. It is
incompatible with -a and -i.
diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index 2187eee..3a4d456 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -8,8 +8,9 @@ git-commit - Record changes to the repository
SYNOPSIS
--------
[verse]
-'git-commit' [-a] [-s] [-v] [(-c | -C) <commit> | -F <file> | -m <msg> |
- --amend] [--no-verify] [-e] [--author <author>]
+'git-commit' [-a | --interactive] [-s] [-v]
+ [(-c | -C) <commit> | -F <file> | -m <msg> | --amend]
+ [--no-verify] [-e] [--author <author>]
[--] [[-i | -o ]<file>...]
DESCRIPTION
@@ -35,6 +36,10 @@ methods:
before, and to automatically "rm" files that have been
removed from the working tree, and perform the actual commit.
+5. by using the --interactive switch with the 'commit' command to invoke "add"
+ in interactive mode, which lets the user decide one by one which files
+ to add or remove.
+
The gitlink:git-status[1] command can be used to obtain a
summary of what is included by any of the above for the next
commit by giving the same set of parameters you would give to
diff --git a/git-commit.sh b/git-commit.sh
index be3677c..e8ec3b9 100755
--- a/git-commit.sh
+++ b/git-commit.sh
@@ -3,7 +3,7 @@
# Copyright (c) 2005 Linus Torvalds
# Copyright (c) 2006 Junio C Hamano
-USAGE='[-a] [-s] [-v] [--no-verify] [-m <message> | -F <logfile> | (-C|-c) <commit> | --amend] [-u] [-e] [--author <author>] [[-i | -o] <path>...]'
+USAGE='[-a | --interactive] [-s] [-v] [--no-verify] [-m <message> | -F <logfile> | (-C|-c) <commit> | --amend] [-u] [-e] [--author <author>] [[-i | -o] <path>...]'
SUBDIRECTORY_OK=Yes
. git-sh-setup
require_work_tree
@@ -71,6 +71,7 @@ trap '
all=
also=
+interactive=
only=
logfile=
use_commit=
@@ -131,6 +132,11 @@ do
also=t
shift
;;
+ --int|--inte|--inter|--intera|--interac|--interact|--interacti|\
+ --interactiv|--interactive)
+ interactive=t
+ shift
+ ;;
-o|--o|--on|--onl|--only)
only=t
shift
@@ -304,12 +310,14 @@ case "$#,$also,$only,$amend" in
;;
esac
unset only
-case "$all,$also,$#" in
-t,t,*)
- die "Cannot use -a and -i at the same time." ;;
+case "$all,$interactive,$also,$#" in
+*t,*t,*)
+ die "Cannot use -a, --interactive or -i at the same time." ;;
t,,[1-9]*)
die "Paths with -a does not make sense." ;;
-,t,0)
+,t,[1-9]*)
+ die "Paths with --interactive does not make sense." ;;
+,,t,0)
die "No paths with -i does not make sense." ;;
esac
@@ -344,6 +352,9 @@ t,)
) || exit
;;
,)
+ if test "$interactive" = t; then
+ git add --interactive || exit
+ fi
case "$#" in
0)
;; # commit as-is
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH] add commit --interactive
2007-03-01 10:56 [PATCH] add commit --interactive Paolo Bonzini
@ 2007-03-02 6:06 ` Junio C Hamano
2007-03-02 12:47 ` David Kågedal
0 siblings, 1 reply; 3+ messages in thread
From: Junio C Hamano @ 2007-03-02 6:06 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: git
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.
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH] add commit --interactive
2007-03-02 6:06 ` Junio C Hamano
@ 2007-03-02 12:47 ` David Kågedal
0 siblings, 0 replies; 3+ messages in thread
From: David Kågedal @ 2007-03-02 12:47 UTC (permalink / raw)
To: git
Junio C Hamano <junkio@cox.net> writes:
> 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
[...]
> * Switch to the commit you want to apply part of what was
[...]
> * Trickle some of the stashed changes in, perhaps interactively:
[...]
> * Review and test the change in the working tree. This does not
[...]
> * 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.
What I would want to do here is to simply restore the original "stash"
tree, so something like "git checkout stash -- ." would probably work.
The reason is that what I would consider a common scenario is that I
realize that some of the changes I made should be commited first, and
I extract them the way you describe, and maybe update them to erase
traces of things I don't want to commit, perhaps on the same source
lines.
But then I want to continue where I was, and reintroduce the things I
cleaned away in the previous step.
This is also much simpler to do. So let's make two scripts
"git-stash" and "git-unstash"
# git-stash:
git-commit -m "temporary stash"
git-tag stash
git-reset --hard HEAD^
# git-unstash:
git-checkout stash -- .
# or possibly:
#current=$(git-rev-parse HEAD)
#git-checkout stash
#git-reset --mixed $current
git-tag -d stash
Then I would do this:
[ ... hack hack ... ]
$ git stash
$ git pick stash
[ ... cleanup ... ]
$ git commit -a -m "first change"
$ git unstash
--
David Kågedal
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2007-03-02 12:53 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-03-01 10:56 [PATCH] add commit --interactive Paolo Bonzini
2007-03-02 6:06 ` Junio C Hamano
2007-03-02 12:47 ` David Kågedal
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 NNTP newsgroup(s).