# Re: [PATCH] git-explain

21 messages from 2006-12-03 to 2006-12-06. Participants: Nicolas Pitre, Junio C Hamano, Carl Worth, Linus Torvalds, Johannes Schindelin, Alan Chandler, Jakub Narebski, Martin Langhoff.
Thread: https://gitlist.dev/t/43187

## Alan Chandler, 2006-12-03 17:01

Subject: Some advanced index playing
Message-ID: <200612031701.15594.alan@chandlerfamily.org.uk>
URL: https://gitlist.dev/e/200612031701.15594.alan%40chandlerfamily.org.uk

```
With all the discussion about the index file in the last few days I would have 
thought that this issue would have come up.  But I don't think it has.

I have been editing a set of files to make a commit, and after editing each 
one had done a git update-index.

At this point I am just about to commit when I realise that one of the files 
has changes in it that really ought to be a separate commit. 

So effectively, I want to do one of three things

a) git-commit <that-file>

Except I can't because there is a safety valve that prevents this and there is 
no force option.

b) Revert the index entry for that file back to the previous HEAD commit 
point, whilst leaving the edits in the working tree, so that I can then 
commit without that one file.

I can't find a command to do that.  The nearest seems to be 
git-update-index --remove, but the manual says that it will not do anything 
if the file still exists.

c) Revert the entire index back to the state it was at the last commit so I 
can selectively add back in the files that I have editted.

The command to do that seems to be

git-read-tree HEAD 

I tried this, and it did indeed seem to exactly this - not quite what I 
wanted, but actually a reasonable compromise.

However, it took me a long time scanning possible commands before I found it 
so I thought I might add some text to one of the tutorials

Any ideas of where?

What happened to the text written here

http://marc.theaimsgroup.com/?l=git&m=116406699903565&w=2

I thought this might be a place to put something like this, but having just 
updated my version of git from source, it doesn't seem to have been put in to 
git anywhere yet.



-- 
Alan Chandler

```

## Linus Torvalds, 2006-12-03 18:24

Subject: Re: Some advanced index playing
Message-ID: <Pine.LNX.4.64.0612031008360.3476@woody.osdl.org>
URL: https://gitlist.dev/e/Pine.LNX.4.64.0612031008360.3476%40woody.osdl.org
In-Reply-To: <200612031701.15594.alan@chandlerfamily.org.uk>

```


On Sun, 3 Dec 2006, Alan Chandler wrote:
>
> With all the discussion about the index file in the last few days I would have 
> thought that this issue would have come up.  But I don't think it has.
> 
> I have been editing a set of files to make a commit, and after editing each 
> one had done a git update-index.
> 
> At this point I am just about to commit when I realise that one of the files 
> has changes in it that really ought to be a separate commit. 
> 
> So effectively, I want to do one of three things
> 
> a) git-commit <that-file>
> 
> Except I can't because there is a safety valve that prevents this and there is 
> no force option.

I think that is actually a misfeature. 

This _should_ just work. It's the easy and logical way to do it, and it's 
the one that matches all the other behaviours of "git commit" these days.

The reason for the safety valve is actually not really "safety" any more, 
it's purely "historical behaviour". Ie the sanity check is not there 
because you would be doing anything unsafe, but simply because the 
behaviour in this area _changed_, so the semantics are different from what 
they were originally.

But those "original" semantics are now so old and so uninteresting that 
the safety feature has gone from being a safety feature to just being 
annoying, and hindering you from doing what you want to do.

Side note: you -can- do what you want to do, but it's insanely stupid. 
Here's what you'd do:

	git ls-tree HEAD -- that-file | git update-index --index-info
	git commit that-file

but there is no way in hell I will claim that this is a _good_ thing.

[ That said, the whole

	git ls-tree <treeish> -- <file-list> | git update-index --index-info

  is a useful pattern to know. You can basically insert _any_ part of old 
  historical state into the index with this, which can be useful if you 
  want to play games without changing the _other_ parts of the index. ]

So anyway, I would suggest that we just get rid of that partial commit 
"safety check" in "git commit" for now. It still makes sense for when 
you're in the middle of a _merge_, but the "verify that index matches" is 
not worth it.

Or at _least_ there should be a flag to force it.

Junio?


```

## Jakub Narebski, 2006-12-03 18:31

Subject: Re: Some advanced index playing
Message-ID: <ekv56m$vd0$1@sea.gmane.org>
URL: https://gitlist.dev/e/ekv56m%24vd0%241%40sea.gmane.org
In-Reply-To: <200612031701.15594.alan@chandlerfamily.org.uk>

```
Alan Chandler wrote:

> With all the discussion about the index file in the last few days I would have 
> thought that this issue would have come up.  But I don't think it has.
> 
> I have been editing a set of files to make a commit, and after editing each 
> one had done a git update-index.
> 
> At this point I am just about to commit when I realise that one of the files 
> has changes in it that really ought to be a separate commit. 
> 
> So effectively, I want to do one of three things
> 
> a) git-commit <that-file>
> 
> Except I can't because there is a safety valve that prevents this and there is 
> no force option.

I do wonder what this safety valve is for, and why it acts also if
index version is equal to working directory version...

> b) Revert the index entry for that file back to the previous HEAD commit 
> point, whilst leaving the edits in the working tree, so that I can then 
> commit without that one file.
> 
> I can't find a command to do that.  The nearest seems to be 
> git-update-index --remove, but the manual says that it will not do anything 
> if the file still exists.

There is "git update-index --force-remove <that-file>". Currently there
is no "git reset -- <that-file>", but you can revert index entry to the
one in HEAD using
  $ git ls-tree HEAD -- <that-file> | git update-index --index-info

And you can always try "git-rm + git-add".
 
> c) Revert the entire index back to the state it was at the last commit so I 
> can selectively add back in the files that I have editted.
> 
> The command to do that seems to be
> 
> git-read-tree HEAD 

No, the command for that is just "git reset" (which means 
"git reset --mixed HEAD"), and touches HEAD and index but not working
directory


> What happened to the text written here
> 
>   http://marc.theaimsgroup.com/?l=git&m=116406699903565&w=2
    "[DRAFT 2] Branching and merging with git"

> I thought this might be a place to put something like this, but having just 
> updated my version of git from source, it doesn't seem to have been put in to 
> git anywhere yet.

linux@horizon.com, perhaps you should send this text in the form of patch
creating Documentation/tutorial-3.txt file?
-- 
Jakub Narebski
Warsaw, Poland
ShadeHawk on #git


```

## Linus Torvalds, 2006-12-03 18:34

Subject: Re: Some advanced index playing
Message-ID: <Pine.LNX.4.64.0612031024480.3476@woody.osdl.org>
URL: https://gitlist.dev/e/Pine.LNX.4.64.0612031024480.3476%40woody.osdl.org
In-Reply-To: <200612031701.15594.alan@chandlerfamily.org.uk>

```


On Sun, 3 Dec 2006, Alan Chandler wrote:
> 
> c) Revert the entire index back to the state it was at the last commit so I 
> can selectively add back in the files that I have editted.
> 
> The command to do that seems to be
> 
> git-read-tree HEAD 

A side note on this..

It definitely works, but it's not really the right thing to do for a few 
reasons:

 - it isn't even what you wanted. You didn't want to reset _all_ the index 
   values, you only really wanted to reset a few of them. So as mentioned 
   in the previous email, the command sequence you'd wanted for that 
   operation is

	git ls-tree <tree> -- <path pattern list> |
		git update-index --index-info

But, that said, if you actually want to reset the whole index, 
"git-read-tree HEAD" works, but is not what you should do:

 - you really want to keep the index "stat()" cache valid, and 
   git-read-tree will throw that all out. So you would need to do a

	git update-index --refresh

   after you've reset the index ("git status" will do it for you, and if 
   you don't do either "git status" or the above,  

 - instead of re-reading the index 100% and then having to refresh it back 
   to mostly the same stat() into it already had, you can _merge_ the old 
   index with the information in HEAD, by using

	git read-tree -m HEAD

   which basically does a merge from the old index and the HEAD tree.

 - However, that actually fails if the old index wasn't just dirty, but 
   had unmerged paths etc, because then a "merge" would throw away that 
   unmerged information. So what you _really_ want to do is

	git read-tree --reset HEAD

   which (as the flag implies) will _reset_ the index to the tree in HEAD, 
   and this will do exactly what you were looking for: keep the "stat()" 
   information alone, but reset the actual index contents.

 - HOWEVER. This is exactly what "git reset" does.

So in short, you should just have done "git reset", and you'd have reset 
your index back to the state of your last commit.

So "git reset" is generally your friend whenever something goes wrong. If 
you also want to reset your checked-out files (which you did NOT want to 
do in this case, of course), you would have added the "--hard" flag to git 
reset.

And that (finally) concludes this particularly boring "git usage 101" 
session.


```

## Junio C Hamano, 2006-12-03 19:36

Subject: Re: Some advanced index playing
Message-ID: <7vy7powz3k.fsf@assigned-by-dhcp.cox.net>
URL: https://gitlist.dev/e/7vy7powz3k.fsf%40assigned-by-dhcp.cox.net
In-Reply-To: <Pine.LNX.4.64.0612031008360.3476@woody.osdl.org>

```
Linus Torvalds <torvalds@osdl.org> writes:

> I think that is actually a misfeature. 
>
> This _should_ just work. It's the easy and logical way to do it, and it's 
> the one that matches all the other behaviours of "git commit" these days.
>
> The reason for the safety valve is actually not really "safety" any more, 
> it's purely "historical behaviour". Ie the sanity check is not there 
> because you would be doing anything unsafe, but simply because the 
> behaviour in this area _changed_, so the semantics are different from what 
> they were originally.
> ...
> Or at _least_ there should be a flag to force it.
>
> Junio?

I agree that if this sequence:

	$ edit foo
        $ git update-index foo
        $ edit foo
        $ git commit foo
        
is what the user actually gives from the command line, after
this commit is made, the user would want to _lose_ the state of
foo at the update-index after this commit is made, 100% of the
time (not "most likely", nor "usually", but "always").  So I am
very in favor of removing that check.

I am a bit worried if the reason behind this safety valve might
have been something else, and we describe the reason as "we
would lose data with this sequence otherwise" only to illustrate
what's happening behind the scene in technical terms.

In other words, while I think no user would ever want to keep
the state of foo at update-index after the above exact sequence
as the end-user action, I am worried if a usage sequence that
involve a group of operations encapsulated in a larger command
(a synthetic command that touches index and working files
without making the user painfully aware of the index -- likes of
git-mv, git-rm, ...) might have been the true motivation of the
safety valve.

I need to be reminded by somebody who went back to the list
discussion around the time we introduced --only, and made sure
that the "you would lose the snapshot you staged in the index if
we allowed it" literally meant only that and nothing else; not
some other common sequence that had the above command sequence
inside, and keeping the state of 'foo' at update-index time made
sense for that usage pattern, although I do not think of
anything offhand...


```

## Alan Chandler, 2006-12-03 20:11

Subject: Re: Some advanced index playing
Message-ID: <200612032011.25922.alan@chandlerfamily.org.uk>
URL: https://gitlist.dev/e/200612032011.25922.alan%40chandlerfamily.org.uk
In-Reply-To: <Pine.LNX.4.64.0612031008360.3476@woody.osdl.org>

```
On Sunday 03 December 2006 18:24, Linus Torvalds wrote:
> On Sun, 3 Dec 2006, Alan Chandler wrote:
> > With all the discussion about the index file in the last few days I would
> > have thought that this issue would have come up.  But I don't think it
> > has.
> >
> > I have been editing a set of files to make a commit, and after editing
> > each one had done a git update-index.
> >
> > At this point I am just about to commit when I realise that one of the
> > files has changes in it that really ought to be a separate commit.
> >
> > So effectively, I want to do one of three things
> >
> > a) git-commit <that-file>
> >
> > Except I can't because there is a safety valve that prevents this and
> > there is no force option.
...
>
> 	git ls-tree HEAD -- that-file | git update-index --index-info
> 	git commit that-file

I don't quite understand this - maybe it should be

git ls-tree HEAD -- that-file | git update-index --index-info
git commit
git commit -a

either I want to ONLY commit that file at the working tree state (and index 
before these commands), or I want to commit ALL except this file (so I can 
later come and commit just that file)

so having reset the index to the state of HEAD for "that-file" then the commit 
should make a commit with all the other changes in the index (but NOT 
that-file) and then the git commit -a picks up "that-file"

-- 
Alan Chandler

```

## Jakub Narebski, 2006-12-03 20:19

Subject: Re: Some advanced index playing
Message-ID: <ekvbg3$jjd$1@sea.gmane.org>
URL: https://gitlist.dev/e/ekvbg3%24jjd%241%40sea.gmane.org
In-Reply-To: <200612032011.25922.alan@chandlerfamily.org.uk>

```
Alan Chandler wrote:

> On Sunday 03 December 2006 18:24, Linus Torvalds wrote:

> ...
>>
>>      git ls-tree HEAD -- that-file | git update-index --index-info
>>      git commit that-file
> 
> I don't quite understand this - maybe it should be
> 
> git ls-tree HEAD -- that-file | git update-index --index-info
> git commit
> git commit -a
> 
> either I want to ONLY commit that file at the working tree state (and index 
> before these commands), or I want to commit ALL except this file (so I can 
> later come and commit just that file)
> 
> so having reset the index to the state of HEAD for "that-file" then the commit 
> should make a commit with all the other changes in the index (but NOT 
> that-file) and then the git commit -a picks up "that-file"

$ git ls-tree HEAD -- that-file | git update-index --index-info
$ git commit that-file
$ git commit

resets index state of 'that-file' to the HEAD version, so the safety valve
is bypassed and "git commit that-file" commit _working directory_ version
only (because --only is default for "git commit <path>"). Then you commit
the rest of files.


While

$ git ls-tree HEAD -- that-file | git update-index --index-info
$ git commit
$ git commit -a

first commits rest of files, then all files to their working directory
version.

P.S. Could you wrap lines a little earlier (76 columns perhaps)? Thanks
in advance
-- 
Jakub Narebski
Warsaw, Poland
ShadeHawk on #git


```

## Junio C Hamano, 2006-12-03 20:26

Subject: Re: Some advanced index playing
Message-ID: <7v1wngwws6.fsf@assigned-by-dhcp.cox.net>
URL: https://gitlist.dev/e/7v1wngwws6.fsf%40assigned-by-dhcp.cox.net
In-Reply-To: <Pine.LNX.4.64.0612031024480.3476@woody.osdl.org>

```
Linus Torvalds <torvalds@osdl.org> writes:

> So "git reset" is generally your friend whenever something goes wrong. If 
> you also want to reset your checked-out files (which you did NOT want to 
> do in this case, of course), you would have added the "--hard" flag to git 
> reset.
>
> And that (finally) concludes this particularly boring "git usage 101" 
> session.
>
> 			Linus

One observation about git, made in a relatively distant past,
was "git is not a usable system yet; there is no 'git undo'".  I
think it was on the kernel list (I think it was from Alan who
seems to have lost his last name from his From: line lately, but
I may be mistaken).

It left a deep psychological trauma in me, not because it was
stated in a brutal way (it wasn't) but because I fully agreed
with that statement from the end user point of view, but I did
not see a good solution to the problem (and I from the beginning
kept saying "I do not do Porcelains" and kept calling what is
shipped with core "Porcelain-ish").

"git reset" is one part of "undo".  For example, undoing a
commit can be approximated with "reset HEAD^" or "reset --hard
HEAD^"; undoing a conflicted and unfinished merge can be
approximated with "reset HEAD" or "reset --hard HEAD".

But for one thing, these are only "approximations" (the working
tree files after these two forms of reset are different from the
state you had before running "commit" or "merge").  And for
another thing, "reset" is only one part of "undo".  "reset"
would not help "undo"-ing a botched "git bisect good", for
example; you need "git bisect reset".  Similarly, "git rebase"
in the middle can be undone with its own --abort option.  But
the user has to know about them.  Another twist is that once
completed, "rebase --abort" obviously would not mean "undo the
last rebase".

I think one cause of the "problem" (if not having a general
"undo" is a problem, and I think it is to some extent) is that
git Porcelain-ish commands try to stay stateless to allow mixing
and matching of different commands to leave the door open to the
end user to be flexible, but they go too far.  Some of the
commands do leave its state (e.g. MERGE_HEAD is a sign of a
merge in progress, and git-commit notices it), and some of the
commands know about state markers from the other commands
(e.g. "git reset" removes MERGE_HEAD).  However, I think we do
not do enough of inter-command coordination.  Although I haven't
checked, I would be very surprised if we already prevented "git
bisect" from running, while a merge is in progress, for example.

While I do not think we need to supply "git undo" for a command
that already ran successfully to its completion (e.g. I think
the answer to "how would I undo a commit I just made" can be
left as "use one of the reset, or --amend, depending on what you
want"), it might be a worthwhile thing to aim for to give a
unified "git oops" command that recovers from a "unusual" state
your git working tree may be left in.

For UI-usability minded people, it might be a good thing to
enumerate the possible 'states' a working tree can be in, draw a
state transition diagram among them, with possible and forbidden
transitions.  After that, we can annotate the allowed
transitions with "use this command to make this transtion
happen".

The following list may be a starter; it is not comprehensive,
and does not list the transitions, though.

 - the base state

 - bisect in progress

 - conflicted merge in progress (or --no-commit given)

 - immediately after "merge --squash" is run but not yet committed

 - conflicted git am/git applymbox in progress.

 - conflicted git rebase in progress (this actually has two
   separate states depending on --merge was used or not).

 - conflicted git revert/cherry-pick (I list this separately
   from 'merge in progress' because this is an example of
   command leaving too little state).

 - conflicted 'git checkout -m' (although this is almost the
   same as the base state, it has higher stages in the index).

```

## Alan Chandler, 2006-12-03 20:29

Subject: Re: Some advanced index playing
Message-ID: <200612032029.17150.alan@chandlerfamily.org.uk>
URL: https://gitlist.dev/e/200612032029.17150.alan%40chandlerfamily.org.uk
In-Reply-To: <ekvbg3$jjd$1@sea.gmane.org>

```
On Sunday 03 December 2006 20:19, Jakub Narebski wrote:

> P.S. Could you wrap lines a little earlier (76 columns perhaps)?
> Thanks in advance

Sorry wrap was set at 78 (kmail).  I've changed it to 72


-- 
Alan Chandler

```

## Alan Chandler, 2006-12-03 20:40

Subject: Re: Some advanced index playing
Message-ID: <200612032040.13100.alan@chandlerfamily.org.uk>
URL: https://gitlist.dev/e/200612032040.13100.alan%40chandlerfamily.org.uk
In-Reply-To: <Pine.LNX.4.64.0612031024480.3476@woody.osdl.org>

```
On Sunday 03 December 2006 18:34, Linus Torvalds wrote:
...
> So in short, you should just have done "git reset", and you'd have
> reset your index back to the state of your last commit.
>
> So "git reset" is generally your friend whenever something goes
> wrong. If you also want to reset your checked-out files (which you
> did NOT want to do in this case, of course), you would have added the
> "--hard" flag to git reset.

Doh [slaps head with wet blanket]

I was so worried about NOT changing my working tree - I totally 
overlooked the reset command.  I had never quite understood 
what --mixed really meant before.  But re-reading the man page now, its 
obvious.



-- 
Alan Chandler

```

## Linus Torvalds, 2006-12-03 20:40

Subject: Re: Some advanced index playing
Message-ID: <Pine.LNX.4.64.0612031236560.3476@woody.osdl.org>
URL: https://gitlist.dev/e/Pine.LNX.4.64.0612031236560.3476%40woody.osdl.org
In-Reply-To: <200612032011.25922.alan@chandlerfamily.org.uk>

```


On Sun, 3 Dec 2006, Alan Chandler wrote:
> >
> > 	git ls-tree HEAD -- that-file | git update-index --index-info
> > 	git commit that-file
> 
> I don't quite understand this - maybe it should be
> 
> git ls-tree HEAD -- that-file | git update-index --index-info
> git commit
> git commit -a

Sure. It depends on which file you want to commit first.

If you want to commit "that-file" first, you do my sequence.

If you want to commit everything _but_ "that-file", you do the second 
sequence (which basically removes the changes to "that-file" from the 
index, then commits the index, and then with "git commit -a" commits the 
remaining dirty state, which is obviously those changes to "that-file" 
that you still had in the working tree).

> either I want to ONLY commit that file at the working tree state (and index 
> before these commands), or I want to commit ALL except this file (so I can 
> later come and commit just that file)

Right. If you do

	git ls-tree HEAD -- that-file | git update-index --index-info
	git commit that-file

you basically ONLY commit "that-file". You first reset it (in the index) 
to the old state, but that's just so that "git commit that-file" will now 
happily commit the current state (in the working tree) of "that-file".

So "git commit that-file" will basically _ignore_ your current index. 
Because you told "git commit" (by naming "that-file") that you _only_ 
wanted to commit "that-file". So whatever state you had in your current 
index doesn't matter at all - it will only look at the HEAD tree _and_ 
that single file that you specified.


```

## Junio C Hamano, 2006-12-04 10:41

Subject: Re: Some advanced index playing
Message-ID: <7vveksoscy.fsf@assigned-by-dhcp.cox.net>
URL: https://gitlist.dev/e/7vveksoscy.fsf%40assigned-by-dhcp.cox.net
In-Reply-To: <Pine.LNX.4.64.0612031008360.3476@woody.osdl.org>

```
Linus Torvalds <torvalds@osdl.org> writes:

> I think that is actually a misfeature. 
>
> This _should_ just work. It's the easy and logical way to do it, and it's 
> the one that matches all the other behaviours of "git commit" these days.
> ...
> So anyway, I would suggest that we just get rid of that partial commit 
> "safety check" in "git commit" for now. It still makes sense for when 
> you're in the middle of a _merge_, but the "verify that index matches" is 
> not worth it.

The codepath has a big "don't do this during a merge" check in
front.  I think this is a safe thing to do, so let's do this.

diff --git a/git-commit.sh b/git-commit.sh
index 81c3a0c..c829791 100755
--- a/git-commit.sh
+++ b/git-commit.sh
@@ -350,19 +350,9 @@ t,)
 			refuse_partial "Cannot do a partial commit during a merge."
 		fi
 		TMP_INDEX="$GIT_DIR/tmp-index$$"
-		if test -z "$initial_commit"
-		then
-			# make sure index is clean at the specified paths, or
-			# they are additions.
-			dirty_in_index=`git-diff-index --cached --name-status \
-				--diff-filter=DMTU HEAD -- "$@"`
-			test -z "$dirty_in_index" ||
-			refuse_partial "Different in index and the last commit:
-$dirty_in_index"
-		fi
 		commit_only=`git-ls-files --error-unmatch -- "$@"` || exit
 
-		# Build the temporary index and update the real index
+		# Build a temporary index and update the real index
 		# the same way.
 		if test -z "$initial_commit"
 		then

```

## Junio C Hamano, 2006-12-05 03:48

Subject: [PATCH] git-explain
Message-ID: <7vwt57j94c.fsf_-_@assigned-by-dhcp.cox.net>
URL: https://gitlist.dev/e/7vwt57j94c.fsf_-_%40assigned-by-dhcp.cox.net
In-Reply-To: <7v1wngwws6.fsf@assigned-by-dhcp.cox.net>

```
Junio C Hamano <junkio@cox.net> writes:

> Linus Torvalds <torvalds@osdl.org> writes:
>
>> So "git reset" is generally your friend whenever something goes wrong. If 
>> you also want to reset your checked-out files (which you did NOT want to 
>> do in this case, of course), you would have added the "--hard" flag to git 
>> reset.
>>
>> And that (finally) concludes this particularly boring "git usage 101" 
>> session.
>
> One observation about git, made in a relatively distant past,
> was "git is not a usable system yet; there is no 'git undo'".  I
> think it was on the kernel list (I think it was from Alan who
> seems to have lost his last name from his From: line lately, but
> I may be mistaken).
>
> It left a deep psychological trauma in me, not because it was
> stated in a brutal way (it wasn't) but because I fully agreed
> with that statement from the end user point of view, but I did
> not see a good solution to the problem (and I from the beginning
> kept saying "I do not do Porcelains" and kept calling what is
> shipped with core "Porcelain-ish").
> ...
> I think one cause of the "problem" (if not having a general
> "undo" is a problem, and I think it is to some extent) is that
> git Porcelain-ish commands try to stay stateless to allow mixing
> and matching of different commands to leave the door open to the
> end user to be flexible, but they go too far.

They go too far by doing too little.

And here is throwing an idea to remedy it; it is far from
complete but sending it out early before spending too much time
on pursuit of a wild goose.

-- >8 --
[PATCH] git-explain

This patch adds "git-explain" script that notices various clues
other commands can leave the working tree and repository in and
intended to guide the end user out of the confused mess.

This is only a demonstration-of-concept, as many commands do not
leave enough information for us to truly figure out what state
we are in nor how we got into the mess.  As a demonstration, it
makes "git merge" to leave a new "$GIT_DIR/FAILED_MERGE" file
when it gives up before touching the working tree.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-explain.sh |  172 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 git-merge.sh   |    3 +-
 git-reset.sh   |    3 +-
 3 files changed, 176 insertions(+), 2 deletions(-)

diff --git a/git-explain.sh b/git-explain.sh
new file mode 100755
index 0000000..07115ae
--- /dev/null
+++ b/git-explain.sh
@@ -0,0 +1,172 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Junio C Hamano
+#
+
+SUBDIRECTORY_OK=Yes
+. git-sh-setup
+
+TOP=`git-rev-parse --show-cdup`
+test -z "$TOP" && TOP=./
+
+say_with_indent() {
+	echo "$@" | sed -e 's/^/	/'
+}
+
+explain_merge_conflict () {
+	test -f $GIT_DIR/HEAD &&
+	test -f "$GIT_DIR/MERGE_HEAD" &&
+	test -f "$GIT_DIR/MERGE_MSG" &&
+	test "z`git ls-files -u`" != z || return 1
+
+	title=$(sed -e q "$GIT_DIR/MERGE_MSG")
+
+	conflicts=$(git ls-files -u |
+		sed -e 's/^[0-7]* [0-9a-f]* [0-3]	//' |
+		uniq)
+
+	git diff-index -r --name-only HEAD >"$tmp-1"
+	git diff-tree -r --name-only HEAD MERGE_HEAD >"$tmp-2"
+	locals=`comm -23 "$tmp-1" "$tmp-2"`
+
+	cat <<EOF
+You tried a merge whose title will be
+
+	$title
+
+This may have successfully merged some paths and they are already
+staged for the next commit, but the following paths could not be
+automatically merged:
+
+EOF
+
+	say_with_indent "$conflicts"
+
+	if test "z$locals" != z
+	then
+		cat <<\EOF
+
+Also you seem to have local modifications unrelated to this
+merge in the following paths:
+
+EOF
+
+		say_with_indent "$locals"
+	fi
+
+	cat <<\EOF
+
+There are two ways to proceed from here:
+
+(1) You can decide to abort this for now and get back to the
+    state before you started the merge.
+
+	$ git reset --hard
+
+    will revert your working tree files to the latest commit
+    before this merge.  Note that you would lose the local
+    modifications with this action, so you might want to
+    preserve them with this command before doing so:
+
+	$ git diff HEAD -- <locally changed files> >preserved-local-changes
+
+    Then you can re-apply the changes after running "git reset
+    --hard".
+
+(2) To resolve the conflicts and record this merge, you first
+    need to examine the conflicted paths listed above, and edit
+    them in your working tree.  After you are done, stage the
+    final version of these paths and make a commit, like this:
+
+	$ git add <locally changed files>
+	$ git commit
+
+    Do not use "git commit -a" unless you want the local
+    modifications that are not related to this merge committed
+    along with the changes this merge brings in.
+EOF
+}
+
+explain_merge_stop () {
+	test -f $GIT_DIR/HEAD &&
+	test -f $GIT_DIR/FAILED_MERGE &&
+	test "z`git rev-parse HEAD`" = "z`git rev-parse ORIG_HEAD`" &&
+	test "z`git ls-files -u`" = z || return 1
+
+	git diff-index -r --name-only HEAD >"$tmp-1"
+	git diff-tree -r --name-only HEAD FAILED_MERGE >"$tmp-2"
+	conflicts=`comm -12 "$tmp-1" "$tmp-2"`
+	locals=`comm -23 "$tmp-1" "$tmp-2"`
+
+	cat <<\EOF
+You tried a merge but it stopped without touching your working tree,
+because you had local changes in the following paths that may also
+be involved in the merge:
+
+EOF
+	say_with_indent "$conflicts"
+
+	if test "z$locals" != z
+	then
+		cat <<\EOF
+
+Also you seem to have local modifications unrelated to this
+merge in the following paths:
+
+EOF
+
+		say_with_indent "$locals"
+
+	fi
+
+	cat <<\EOF
+
+There are two ways to proceed from here:
+
+(1) You can decide to abort this merge for now.  You do not have
+    to do anything in this case because the merge hasn't touched
+    your working tree files yet.
+
+(2) You can first make the merge and re-apply your local changes
+    on top of the merge result.  To do this, first stash away
+    your local changes in a temporary commit, like this:
+
+	$ git commit -a -m 'WIP'
+	$ git tag WIP
+	$ git reset --hard HEAD^
+
+    and run the same "git merge" (or "git pull") again.  After
+    you finished the merge, you can review the local change with:
+
+	$ git show WIP
+
+    and re-apply them to your working tree as you see fit.
+EOF
+}
+
+explain_am () {
+	test -d $TOP/.dotest || return 1
+
+	cat <<EOF
+You tried to apply a series of `cat .dotest/last` patch(es) and
+are currently handling patch #`cat .dotest/next`.
+
+EOF
+
+}
+
+explain_base () {
+	# This comes at the end.
+	cat <<EOF
+Don't worry, you are not in the middle of anything complicated.
+EOF
+
+}
+
+tmp="$GIT_DIR/tmp"
+trap 'rm -f $tmp-*' 0
+
+explain_merge_conflict ||
+explain_merge_stop ||
+explain_am ||
+explain_base
\ No newline at end of file
diff --git a/git-merge.sh b/git-merge.sh
index 272f004..674abad 100755
--- a/git-merge.sh
+++ b/git-merge.sh
@@ -20,7 +20,7 @@ index_merge=t
 
 dropsave() {
 	rm -f -- "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/MERGE_MSG" \
-		 "$GIT_DIR/MERGE_SAVE" || exit 1
+		 "$GIT_DIR/MERGE_SAVE" "$GIT_DIR/FAILED_MERGE" || exit 1
 }
 
 savestate() {
@@ -400,6 +400,7 @@ case "$best_strategy" in
 '')
 	restorestate
 	echo >&2 "No merge strategy handled the merge."
+	echo "$@" >"$GIT_DIR/FAILED_MERGE"
 	exit 2
 	;;
 "$wt_strategy")
diff --git a/git-reset.sh b/git-reset.sh
index 3133b5b..045ee79 100755
--- a/git-reset.sh
+++ b/git-reset.sh
@@ -63,6 +63,7 @@ case "$reset_type" in
 	;;
 esac
 
-rm -f "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/rr-cache/MERGE_RR" "$GIT_DIR/SQUASH_MSG"
+rm -f "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/rr-cache/MERGE_RR" \
+	"$GIT_DIR/SQUASH_MSG" "$GIT_DIR/FAILED_MERGE"
 
 exit $update_ref_status
-- 
1.4.4.1.g15e3


```

## Jakub Narebski, 2006-12-05 10:43

Subject: Re: [PATCH] git-explain
Message-ID: <el3ibq$9kn$2@sea.gmane.org>
URL: https://gitlist.dev/e/el3ibq%249kn%242%40sea.gmane.org
In-Reply-To: <7vwt57j94c.fsf_-_@assigned-by-dhcp.cox.net>

```
Junio C Hamano wrote:

> [PATCH] git-explain
> 
> This patch adds "git-explain" script that notices various clues
> other commands can leave the working tree and repository in and
> intended to guide the end user out of the confused mess.

I like it, although I think that it explains a bit too little
about aborted git-rebase / git-am, and for example doesn't touch
git-bisect at all. Both as an end-user script (BTW. the name could
be changed using aliases), and as a policy for git commands.

-- 
Jakub Narebski
Warsaw, Poland
ShadeHawk on #git


```

## Martin Langhoff, 2006-12-05 23:00

Subject: Re: [PATCH] git-explain
Message-ID: <46a038f90612051500w44438f70y3c7d1c99998d16bb@mail.gmail.com>
URL: https://gitlist.dev/e/46a038f90612051500w44438f70y3c7d1c99998d16bb%40mail.gmail.com
In-Reply-To: <el3ibq$9kn$2@sea.gmane.org>

```
On 12/5/06, Jakub Narebski <jnareb@gmail.com> wrote:
> Junio C Hamano wrote:
>
> > [PATCH] git-explain
> >
> > This patch adds "git-explain" script that notices various clues
> > other commands can leave the working tree and repository in and
> > intended to guide the end user out of the confused mess.
>
> I like it, although I think that it explains a bit too little

I like what it does too... but why not as part of git-status?

cheers,



```

## Junio C Hamano, 2006-12-05 23:07

Subject: Re: [PATCH] git-explain
Message-ID: <7vslfudjsa.fsf@assigned-by-dhcp.cox.net>
URL: https://gitlist.dev/e/7vslfudjsa.fsf%40assigned-by-dhcp.cox.net
In-Reply-To: <46a038f90612051500w44438f70y3c7d1c99998d16bb@mail.gmail.com>

```
"Martin Langhoff" <martin.langhoff@gmail.com> writes:

> On 12/5/06, Jakub Narebski <jnareb@gmail.com> wrote:
>> Junio C Hamano wrote:
>>
>> > [PATCH] git-explain
>> >
>> > This patch adds "git-explain" script that notices various clues
>> > other commands can leave the working tree and repository in and
>> > intended to guide the end user out of the confused mess.
>>
>> I like it, although I think that it explains a bit too little
>
> I like what it does too... but why not as part of git-status?

The biggest reason was that it is a demonstration of concepts,
not replacement of anything.  Maybe "git status" can be replaced
with something like that if people worked on it enough.

The current use of "git status" inside "git commit" needs to be
revamped if we pursue this further, though.  Because one of the
points the "what state is this repository in" check "explain"
does is to define what operations are sensible in each state,
and most likely in many state it would not make _any_ sense to
run "git commit" (say, in the middle of "bisect").





```

## Johannes Schindelin, 2006-12-05 23:37

Subject: Re: [PATCH] git-explain
Message-ID: <Pine.LNX.4.63.0612060033540.28348@wbgn013.biozentrum.uni-wuerzburg.de>
URL: https://gitlist.dev/e/Pine.LNX.4.63.0612060033540.28348%40wbgn013.biozentrum.uni-wuerzburg.de
In-Reply-To: <7vslfudjsa.fsf@assigned-by-dhcp.cox.net>

```
Hi,

On Tue, 5 Dec 2006, Junio C Hamano wrote:

> "Martin Langhoff" <martin.langhoff@gmail.com> writes:
> 
> > I like what it does too... but why not as part of git-status?
> 
> The biggest reason was that it is a demonstration of concepts,
> not replacement of anything.  Maybe "git status" can be replaced
> with something like that if people worked on it enough.

Okay, so what do people need? This is evidently a question for people who 
are not intimately familiar with the core of Git. So, where are you 
newbies when we need you? Speak up!

Surely, you hit a wall on the road, where you really wanted to ask "git 
wtf"?

> The current use of "git status" inside "git commit" needs to be revamped 
> if we pursue this further, though.  Because one of the points the "what 
> state is this repository in" check "explain" does is to define what 
> operations are sensible in each state, and most likely in many state it 
> would not make _any_ sense to run "git commit" (say, in the middle of 
> "bisect").

That is right, but it is only a matter of having a command line switch to 
suppress what you do not need for the commit message. After all, "status" 
really should tell you about the status of the working directory. The fact 
that we have the _same_ script for "commit" and "status" is no excuse!

Ciao,
Dscho

```

## Junio C Hamano, 2006-12-05 23:57

Subject: Re: [PATCH] git-explain
Message-ID: <7vu009dhgz.fsf@assigned-by-dhcp.cox.net>
URL: https://gitlist.dev/e/7vu009dhgz.fsf%40assigned-by-dhcp.cox.net
In-Reply-To: <Pine.LNX.4.63.0612060033540.28348@wbgn013.biozentrum.uni-wuerzburg.de>

```
Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

> On Tue, 5 Dec 2006, Junio C Hamano wrote:
>...
>> The current use of "git status" inside "git commit" needs to be revamped 
>> if we pursue this further, though.  Because one of the points the "what 
>> state is this repository in" check "explain" does is to define what 
>> operations are sensible in each state, and most likely in many state it 
>> would not make _any_ sense to run "git commit" (say, in the middle of 
>> "bisect").
>
> That is right, but it is only a matter of having a command line switch to 
> suppress what you do not need for the commit message. After all, "status" 
> really should tell you about the status of the working directory. The fact 
> that we have the _same_ script for "commit" and "status" is no excuse!

Sure.  The way I envisioned it was that we would have a moral
equivalent of current git-status output for explanation of the
base state.

I originally wanted to do "git oops" which would have been a
universal "undo" command, and "explain" was only the first
smaller step toward that goal.  But the more I think about it, I
feel that "undo" is less important.


```

## Carl Worth, 2006-12-06 00:07

Subject: Re: [PATCH] git-explain
Message-ID: <87bqmh28g7.wl%cworth@cworth.org>
URL: https://gitlist.dev/e/87bqmh28g7.wl%25cworth%40cworth.org
In-Reply-To: <7vu009dhgz.fsf@assigned-by-dhcp.cox.net>

```
On Tue, 05 Dec 2006 15:57:00 -0800, Junio C Hamano wrote:
> I originally wanted to do "git oops" which would have been a
> universal "undo" command, and "explain" was only the first
> smaller step toward that goal.  But the more I think about it, I
> feel that "undo" is less important.

I like the idea of "git status" explaining what operations are "in
progress" and what the user might want to do next to complete the
operation.

As for the desire for "undo", it probably makes sense to just keep
focusing on making "git reset" a nice, safe way to undo any sort of
"in progress" operation. The recent cleanup patches are in line with
this.

One thing that conflicts with that goal a bit is that git-reset is
also used to move the current branch arbitrarily in the commit DAG,
and also (with --hard) to discard local commits. Those are definitely
less "safe" operations.

-Carl

```

## Johannes Schindelin, 2006-12-06 00:27

Subject: Re: [PATCH] git-explain
Message-ID: <Pine.LNX.4.63.0612060125320.28348@wbgn013.biozentrum.uni-wuerzburg.de>
URL: https://gitlist.dev/e/Pine.LNX.4.63.0612060125320.28348%40wbgn013.biozentrum.uni-wuerzburg.de
In-Reply-To: <7vu009dhgz.fsf@assigned-by-dhcp.cox.net>

```
Hi,

On Tue, 5 Dec 2006, Junio C Hamano wrote:

> But the more I think about it, I feel that "undo" is less important.

Me, too. I was really astonished that people said -- after quite some 
explanations -- "wow, you mean I did not lose any work?"

That's the nice thing about git: it is really hard to lose any objects. 
Unless you explicitely issue a "git prune", you can resurrect whatever was 
your state.

In this spirit, maybe it is time to enable reflog per default?


Ciao,
Dscho

```

## Nicolas Pitre, 2006-12-06 01:50

Subject: Re: [PATCH] git-explain
Message-ID: <Pine.LNX.4.64.0612052050190.2630@xanadu.home>
URL: https://gitlist.dev/e/Pine.LNX.4.64.0612052050190.2630%40xanadu.home
In-Reply-To: <Pine.LNX.4.63.0612060125320.28348@wbgn013.biozentrum.uni-wuerzburg.de>

```
On Wed, 6 Dec 2006, Johannes Schindelin wrote:

> In this spirit, maybe it is time to enable reflog per default?

Definitely.



```
