git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* git-rm isn't the inverse action of git-add
@ 2007-07-02 18:09 Christian Jaeger
  2007-07-02 19:42 ` Yann Dirson
  0 siblings, 1 reply; 37+ messages in thread
From: Christian Jaeger @ 2007-07-02 18:09 UTC (permalink / raw)
  To: git

Hello

I'm coming from cogito. There you can run:

  cg-add $file ; cg-rm $file

and everything is as before; it adds the file to the directory
index/cache, and just removes it again from the latter.

Whereas with git,

  git-add $file; git-rm $file

is giving the error

  error: '..file..' has changes staged in the index (hint: try -f)

And sure enough, git rm -f $file will remove the file from the index,
but also unlink it from the directory. (Ok, I did remember that cogito's
-f option is unlinking the file, so I was cautious and didn't try it on
an important file, but still...)

Turns out that

  git rm  -f --cached $file

will do the same action as cg-rm $file.

Why so complicated? Why not just make git-rm without options behave like
cg-rm? (Or at the very least, I'd change the hint to say "try -f --cached".)

Christian.

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

* Re: git-rm isn't the inverse action of git-add
  2007-07-02 18:09 git-rm isn't the inverse action of git-add Christian Jaeger
@ 2007-07-02 19:42 ` Yann Dirson
  2007-07-02 20:23   ` Christian Jaeger
  0 siblings, 1 reply; 37+ messages in thread
From: Yann Dirson @ 2007-07-02 19:42 UTC (permalink / raw)
  To: Christian Jaeger; +Cc: git

On Mon, Jul 02, 2007 at 08:09:37PM +0200, Christian Jaeger wrote:
> Why so complicated? Why not just make git-rm without options behave like
> cg-rm? (Or at the very least, I'd change the hint to say "try -f --cached".)

It is probably a matter of taste.  Personally, I am really upset by
this behaviour that cvs, cogito, stgit and others share, which forces
me to issue 2 commands to really delete a file from version control
and from the filesystem.

Do you really need to undo an add more often than you need to remove a
file from version-control ?  It may be worth, however, to make things
easier.  Maybe "git add --undo foo" would be a solution ?  Not sure
we'd want to add --undo to many git commands, though.  Opinions ?

Best regards,
-- 
Yann

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

* Re: git-rm isn't the inverse action of git-add
  2007-07-02 19:42 ` Yann Dirson
@ 2007-07-02 20:23   ` Christian Jaeger
  2007-07-02 20:40     ` Yann Dirson
  2007-07-11 12:20     ` Jakub Narebski
  0 siblings, 2 replies; 37+ messages in thread
From: Christian Jaeger @ 2007-07-02 20:23 UTC (permalink / raw)
  To: Yann Dirson; +Cc: git

Yann Dirson wrote:
> On Mon, Jul 02, 2007 at 08:09:37PM +0200, Christian Jaeger wrote:
>   
>> Why so complicated? Why not just make git-rm without options behave like
>> cg-rm? (Or at the very least, I'd change the hint to say "try -f --cached".)
>>     
>
> It is probably a matter of taste.  Personally, I am really upset by
> this behaviour that cvs, cogito, stgit and others share, which forces
> me to issue 2 commands to really delete a file from version control
> and from the filesystem.
>   

It doesn't force you to issue 2 commands: the -f option to cg-rm unlinks
the file for you. So you only have to type an additional "-f".

Yes it's probably (partly) a matter of taste: in my bash startup files I
have mv aliased to mv -i and rm to rm -i, so that it asks me whether I'm
sure to delete or overwrite a file. If I know in advance that I'm sure,
I just type "rm -f $file", which then expands to "rm -i -f $file" where
the -f overrides the -i. cg-rm -f just fits very well into this scheme
(the only difference being that "cg-rm $file" doesn't explicitely ask me
whether I also want the file to be unlinked). (BTW note that usually for
removing a file I use a "trash" (or shorter alias "tra") command, which
moves it to a trash can instead of deleting; so I use "tra $file" by
default, and only for big files or when I'm sure I immediately want to
delete them, I use rm, and then if the paths are clear I add the -f
flag, if not (like globbing involved), I don't add the -f and thus am
asked for confirmation.)

If I could alias the git-rm command so that the default action is the
reverse of git-add and adding an -f flag removes it from disk, that
would be fine for me.

> Do you really need to undo an add more often than you need to remove a
> file from version-control ?  It may be worth, however, to make things
> easier.  Maybe "git add --undo foo" would be a solution ?

This doesn't sound very intuitive to me (and I couldn't fix it with an
alias).

I don't per se require undo actions. I just don't understand why git-rm
refuses to remove the file from the index, even if I didn't commit it.
The index is just an intermediate record of the changes in my
understandings, and the rm action would also be intermediate until it's
being committed. And a non-committed action being deleted shouldn't need
a special confirmation from me, especially not one which is consisting
of a combination of two flags (of which one is a destructive one).

Christian.

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

* Re: git-rm isn't the inverse action of git-add
  2007-07-02 20:23   ` Christian Jaeger
@ 2007-07-02 20:40     ` Yann Dirson
  2007-07-02 20:54       ` Matthieu Moy
  2007-07-02 21:20       ` git-rm isn't the inverse action of git-add Christian Jaeger
  2007-07-11 12:20     ` Jakub Narebski
  1 sibling, 2 replies; 37+ messages in thread
From: Yann Dirson @ 2007-07-02 20:40 UTC (permalink / raw)
  To: Christian Jaeger; +Cc: git

On Mon, Jul 02, 2007 at 10:23:00PM +0200, Christian Jaeger wrote:
> I don't per se require undo actions. I just don't understand why git-rm
> refuses to remove the file from the index, even if I didn't commit it.

I'd say it does so, so you won't loose any uncommitted changes without
knowing it - and "git add -f" is available when you have checked that
you indeed want to discard that data.

> The index is just an intermediate record of the changes in my
> understandings, and the rm action would also be intermediate until it's
> being committed. And a non-committed action being deleted shouldn't need
> a special confirmation from me, especially not one which is consisting
> of a combination of two flags (of which one is a destructive one).

It already works as such: it will warn you if you have already staged
the file in the index, but it has not been committed, in which case
the data would be lost as well:

$ echo foo > bar
/tmp/test$ git rm bar
fatal: pathspec 'bar' did not match any files
/tmp/test$ git add bar
/tmp/test$ git rm bar
error: 'bar' has changes staged in the index (hint: try -f)

That is, "git rm" will only ever remove the file without asking, when
it is safe do so, in that you can retrieve your file from history.  Or
do you think of another way, in which more safety would be needed ?

Best regards,
-- 
Yann

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

* Re: git-rm isn't the inverse action of git-add
  2007-07-02 20:40     ` Yann Dirson
@ 2007-07-02 20:54       ` Matthieu Moy
  2007-07-02 21:05         ` Johannes Schindelin
  2007-07-02 21:20       ` git-rm isn't the inverse action of git-add Christian Jaeger
  1 sibling, 1 reply; 37+ messages in thread
From: Matthieu Moy @ 2007-07-02 20:54 UTC (permalink / raw)
  To: Yann Dirson; +Cc: Christian Jaeger, git

Yann Dirson <ydirson@altern.org> writes:

> That is, "git rm" will only ever remove the file without asking, when
> it is safe do so, in that you can retrieve your file from history.  Or
> do you think of another way, in which more safety would be needed ?

Defaulting to --cached would be an obvious way to avoid data-loss.
_At least_, mentionning --cached in the error message in case of
staged changes would be a considerable step forward.

At the moment, the non-expert user will have difficulties to unversion
the file without deleting it. I just see it as

$ git rm foo
error: 'foo' has changes staged in the index
(hint: to hang yourself, try -f)
$ _

-- 
Matthieu

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

* Re: git-rm isn't the inverse action of git-add
  2007-07-02 20:54       ` Matthieu Moy
@ 2007-07-02 21:05         ` Johannes Schindelin
  2007-07-03 10:37           ` Matthieu Moy
  0 siblings, 1 reply; 37+ messages in thread
From: Johannes Schindelin @ 2007-07-02 21:05 UTC (permalink / raw)
  To: Matthieu Moy; +Cc: Yann Dirson, Christian Jaeger, git

Hi,

On Mon, 2 Jul 2007, Matthieu Moy wrote:

> Defaulting to --cached would be an obvious way to avoid data-loss. _At 
> least_, mentionning --cached in the error message in case of staged 
> changes would be a considerable step forward.
> 
> At the moment, the non-expert user will have difficulties to unversion 
> the file without deleting it. I just see it as
> 
> $ git rm foo
> error: 'foo' has changes staged in the index
> (hint: to hang yourself, try -f)
> $ _

What's so wrong with our man pages? You know, there have been man hours 
invested in them, and they are exclusively meant for consumption by people 
who do not know about the usage of the commands...

Hth,
Dscho

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

* Re: git-rm isn't the inverse action of git-add
  2007-07-02 20:40     ` Yann Dirson
  2007-07-02 20:54       ` Matthieu Moy
@ 2007-07-02 21:20       ` Christian Jaeger
  2007-07-03  4:12         ` Jeff King
  1 sibling, 1 reply; 37+ messages in thread
From: Christian Jaeger @ 2007-07-02 21:20 UTC (permalink / raw)
  To: Yann Dirson; +Cc: git

Yann Dirson wrote:
> On Mon, Jul 02, 2007 at 10:23:00PM +0200, Christian Jaeger wrote:
>   
>> I don't per se require undo actions. I just don't understand why git-rm
>> refuses to remove the file from the index, even if I didn't commit it.
>>     
>
> I'd say it does so, so you won't loose any uncommitted changes without
> knowing it - and "git add -f" is available when you have checked that
> you indeed want to discard that data.
>   

I'm really realising that

git-rm $file # where $file *has* been committed previously

does remove *and* unlink the file. (cg-rm does unlink only with the -f
flag, as said.)

So there's no -f flag in normal git-rm usage. It thus has a different
meaning, namely "force the operation pair of removing from index and
unlinking", not "force this operation also onto the checked out files"
as is the case with cogito.

So I now understand better why they invented the -f flag to git-rm for
the case you're mentioning above and why the hint doesn't warn about
it's danger, since git-rm is always dangerous. (Ok, as is "rm" without
the "-i"; I just found it normal that cogito behaved like my "-i" setup.)

Regarding the issue of "lost files" because they have been created,
added, and removed again before committing: as far as I remember this
has never happened to me with cogito. I commit often, so if I add a file
or a few, in most cases I commit just this fact (that they have been
added), before doing more fancy stuff. I'm maybe used to thinking in
database terms, work that isn't committed is lost. So if I create a file
and add it, in my brain the "attention, uncommitted work" flag is on,
and it usually doesn't happen that I later erroneously think the work
has been committed when in fact it isn't. (I can always check with a
quick cg-status (which shows the files as "A", which makes them stand
out better than in the git-status output)).

Just before writing this mail I had a case where I wanted to remove a
file from versioning control, but keep it on disk (I used git-rm and
that's how I learned that it really also unlinks the local file without
asking(*)). Note that this has not been an undo action; the file has
been committed previously.

(* thanks to git-reset I could get it back of course)

>
> That is, "git rm" will only ever remove the file without asking, when
> it is safe do so, in that you can retrieve your file from history. 

(Well it's not safe if you want to remove the file *from the index* and
naively mis-use the -f flag as suggested by the hint.)

>  Or
> do you think of another way, in which more safety would be needed ?
>   

I think we have just two different points in our view where we think
safety matters.

Regarding the man pages: yes the git-rm man page is fine, and it's nice
to see the manuals are improving. As noted I came from cogito, and
didn't expect git to behave so different with the same named (but
different purpose) options, so I didn't read the man pages (I've been in
irc and asked there, where someone suggested to bring this to the list;
I'm too tired today to think further about it and will try to read more
docs and hope I'll get to understand the git philosophies more).

Christian.

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

* Re: git-rm isn't the inverse action of git-add
  2007-07-02 21:20       ` git-rm isn't the inverse action of git-add Christian Jaeger
@ 2007-07-03  4:12         ` Jeff King
  2007-07-03  4:47           ` Junio C Hamano
  0 siblings, 1 reply; 37+ messages in thread
From: Jeff King @ 2007-07-03  4:12 UTC (permalink / raw)
  To: Christian Jaeger; +Cc: Yann Dirson, Johannes Schindelin, git

On Mon, Jul 02, 2007 at 11:20:59PM +0200, Christian Jaeger wrote:

> So there's no -f flag in normal git-rm usage. It thus has a different
> meaning, namely "force the operation pair of removing from index and
> unlinking", not "force this operation also onto the checked out files"
> as is the case with cogito.

Yes, git-rm is used in several situations, and the idea is that it
should behave safely in all situations; that is, without -f you can't
delete any data that can't be recovered from your history (but maybe
that means we shouldn't suggest -f in so cavalier a fashion).

Each file has three "states" that we care about: in the HEAD (H), in the
index (I), and in the working tree (W). Let's say you call 'git-rm'.
Here's a table of possibilities (A is some content "A", B is some
content not equal to "A", and N is "non-existant"):

H I W | ok? | why?
---------------------------------------------------
* N * | no  | file is not tracked
N A N | ?   | currently ok, but 'A' recoverable only through fsck
N A A | no  | 'A' recoverable only through fsck
N A B | no  | local modification 'B' would be lost
A A N | yes | 'A' recoverable through history
A A A | yes | 'A' recoverable through history
A A B | no  | local modification 'B' would be lost
A B N | ?   | currently ok, but 'B' recoverable only through fsck
A B A | no  | 'B' recoverable only through fsck
A B B | no  | 'B' recoverable only through fsck
B * * |     | equivalent to H=A

With --cached on, it is a little different:

H I W | ok? | why?
---------------------------------------------------
* N * | no  | file is not tracked
N A N |  ?  | currently ok, but 'A' recoverable only through fsck
N A A |  ?  | currently not ok, but 'A' still available in W
N A B | no  | 'A' recoverable only through fsck
A A N | yes | 'A' recoverable through history
A A A | yes | 'A' recoverable through history or working tree
A A B |  ?  | currently not ok, but 'A' still available in H
A B N |  ?  | currently ok, but 'B' recoverable only through fsck
A B A | no  | 'B' recoverable only through fsck
A B B |  ?  | currently not ok, but 'B' still available in W
B * * |     | equivalent to H=A

So it looks like our safety valve is a bit overbearing in a few
situations, and still misses some situations where data has to be pulled
out of the database with git-fsck.

I think if we actually spell out these possible states in the code, we
can get more accurate behavior, but also more accurate error messages. I
will try to work up a patch.

-Peff

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

* Re: git-rm isn't the inverse action of git-add
  2007-07-03  4:12         ` Jeff King
@ 2007-07-03  4:47           ` Junio C Hamano
  2007-07-03  4:59             ` Jeff King
  0 siblings, 1 reply; 37+ messages in thread
From: Junio C Hamano @ 2007-07-03  4:47 UTC (permalink / raw)
  To: Jeff King; +Cc: Christian Jaeger, Yann Dirson, Johannes Schindelin, git

Jeff King <peff@peff.net> writes:

> H I W | ok? | why?
> ---------------------------------------------------
> N A N | ?   | currently ok, but 'A' recoverable only through fsck
> A B N | ?   | currently ok, but 'B' recoverable only through fsck

These were explicitly done per request from git-rm users (myself
not one of them) who wanted to:

	rm the-file
        git rm the-file

sequence not to barf.  I suspect they were from CVS background
who are used to the SCM that complains if you still have the
file in the working tree when you say "scm rm".

I would not mind requiring -f for these cases.

> With --cached on, it is a little different:
>
> H I W | ok? | why?
> ---------------------------------------------------
> N A N |  ?  | currently ok, but 'A' recoverable only through fsck
> N A A |  ?  | currently not ok, but 'A' still available in W
> A A B |  ?  | currently not ok, but 'A' still available in H
> A B N |  ?  | currently ok, but 'B' recoverable only through fsck
> A B B |  ?  | currently not ok, but 'B' still available in W

I personally do not think we would need any safety check for
"git rm --cached", as it does not touch the working tree.  If
one cares about the differences among three states, one would
not issue "rm --cached" anyway.  The only reason "rm --cached"
is used is because one _knows_ that any blob should not exist at
that path in the index.

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

* Re: git-rm isn't the inverse action of git-add
  2007-07-03  4:47           ` Junio C Hamano
@ 2007-07-03  4:59             ` Jeff King
  2007-07-03  5:09               ` Junio C Hamano
  0 siblings, 1 reply; 37+ messages in thread
From: Jeff King @ 2007-07-03  4:59 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Christian Jaeger, Yann Dirson, Johannes Schindelin, git

On Mon, Jul 02, 2007 at 09:47:33PM -0700, Junio C Hamano wrote:

> These were explicitly done per request from git-rm users (myself
> not one of them) who wanted to:
> 
> 	rm the-file
>         git rm the-file

Ah, makes sense (if such a thing can be said about CVS behavior).

> > H I W | ok? | why?
> > ---------------------------------------------------
> > N A N |  ?  | currently ok, but 'A' recoverable only through fsck
> > N A A |  ?  | currently not ok, but 'A' still available in W
> > A A B |  ?  | currently not ok, but 'A' still available in H
> > A B N |  ?  | currently ok, but 'B' recoverable only through fsck
> > A B B |  ?  | currently not ok, but 'B' still available in W
> 
> I personally do not think we would need any safety check for
> "git rm --cached", as it does not touch the working tree.  If

It depends on how we want to define "lost" data. In many cases, we are
protecting against losing content that will still be available until the
next git-prune. Should our safety valve protect against that case, or
should it not? We are totally inconsistent.

The main one for --cached, of course, is when that content exists _only_
in the index, but no longer in the working tree (!A A N or !A A B).  You
really should be using regular git-rm (in the first case, since you are
saying "I don't want this file anymore") or git-add (throw out the old
data, use my new version).

OTOH, clearly git-add can "lose" data in this way as well, since a
"modify, git-add, modify, git-add" will "lose" any reference to the
index state after the first add. So maybe that is not worth worrying
about at all (in which case our safety valve is too strict in many
places).

We could also issue a warning when "losing" reference to data that is in
the object db, which would include the sha1; in that case, an immediate
"oops" could be rectified with git-show.

> one cares about the differences among three states, one would
> not issue "rm --cached" anyway.  The only reason "rm --cached"
> is used is because one _knows_ that any blob should not exist at
> that path in the index.

How about:

  git-add foo
  echo changes >>foo
  # oops, I don't want to commit foo just yet
  git-rm --cached foo

but in that case, maybe the user doesn't actually _care_ about that
intermediate state of 'foo'.

-Peff

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

* Re: git-rm isn't the inverse action of git-add
  2007-07-03  4:59             ` Jeff King
@ 2007-07-03  5:09               ` Junio C Hamano
  2007-07-03  5:12                 ` Jeff King
  0 siblings, 1 reply; 37+ messages in thread
From: Junio C Hamano @ 2007-07-03  5:09 UTC (permalink / raw)
  To: Jeff King; +Cc: Christian Jaeger, Yann Dirson, Johannes Schindelin, git

Jeff King <peff@peff.net> writes:

> OTOH, clearly git-add can "lose" data in this way as well, since a
> "modify, git-add, modify, git-add" will "lose" any reference to the
> index state after the first add. So maybe that is not worth worrying
> about at all (in which case our safety valve is too strict in many
> places).

Exactly.  And not considering that lossage helps us keep our
sanity.  I think "git rm --cached" falls into the same
category.  If the user wants to discard what is in the index
without losing a copy in the working tree, I think we should let
him do without fuss.

>   git-add foo
>   echo changes >>foo
>   # oops, I don't want to commit foo just yet
>   git-rm --cached foo
>
> but in that case, maybe the user doesn't actually _care_ about that
> intermediate state of 'foo'.

Yes, that is (at least, "used to be") exactly the use case "rm
--cached" is supposed to help.  Added something prematurely to
the index, not ready to commit that part of the changes yet.
Of course you could do partial commits with "add --interactive"
these days, so there is not as much need for this as it used to
be anymore.

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

* Re: git-rm isn't the inverse action of git-add
  2007-07-03  5:09               ` Junio C Hamano
@ 2007-07-03  5:12                 ` Jeff King
  2007-07-03  6:26                   ` Junio C Hamano
  0 siblings, 1 reply; 37+ messages in thread
From: Jeff King @ 2007-07-03  5:12 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Christian Jaeger, Yann Dirson, Johannes Schindelin, git

On Mon, Jul 02, 2007 at 10:09:35PM -0700, Junio C Hamano wrote:

> Exactly.  And not considering that lossage helps us keep our
> sanity.  I think "git rm --cached" falls into the same
> category.  If the user wants to discard what is in the index
> without losing a copy in the working tree, I think we should let
> him do without fuss.

OK. So should we _remove_ the safety valve in all cases where we're just
losing stuff that's in the index? It is, after all, recoverable. Should
there be a warning (I suspect it would get annoying very quickly)?

I think this would help by making the use of '-f' more rare, which is
the thing that can _really_ screw you, since it turns off the safety
valve even for things that aren't recoverable.

-Peff

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

* Re: git-rm isn't the inverse action of git-add
  2007-07-03  5:12                 ` Jeff King
@ 2007-07-03  6:26                   ` Junio C Hamano
  0 siblings, 0 replies; 37+ messages in thread
From: Junio C Hamano @ 2007-07-03  6:26 UTC (permalink / raw)
  To: Jeff King; +Cc: Christian Jaeger, Yann Dirson, Johannes Schindelin, git

Jeff King <peff@peff.net> writes:

> On Mon, Jul 02, 2007 at 10:09:35PM -0700, Junio C Hamano wrote:
>
>> Exactly.  And not considering that lossage helps us keep our
>> sanity.  I think "git rm --cached" falls into the same
>> category.  If the user wants to discard what is in the index
>> without losing a copy in the working tree, I think we should let
>> him do without fuss.
>
> OK. So should we _remove_ the safety valve in all cases where we're just
> losing stuff that's in the index? It is, after all, recoverable. Should
> there be a warning (I suspect it would get annoying very quickly)?

I personally do not think we would need any safety check for
"git rm --cached", as it does not touch the working tree.  For
non-cached case I think the current behaviour is fine.

But I should warn you that I rarely use "git rm" myself.

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

* Re: git-rm isn't the inverse action of git-add
  2007-07-02 21:05         ` Johannes Schindelin
@ 2007-07-03 10:37           ` Matthieu Moy
  2007-07-03 12:09             ` Johannes Schindelin
  0 siblings, 1 reply; 37+ messages in thread
From: Matthieu Moy @ 2007-07-03 10:37 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: Yann Dirson, Christian Jaeger, git

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

> What's so wrong with our man pages? You know, there have been man hours 
> invested in them, and they are exclusively meant for consumption by people 
> who do not know about the usage of the commands...

What's wrong is just that I shouldn't have to read a man page to avoid
data-loss. I should have to read them to do non-trivial things, for
sure.

Useability is not just about documenting surprising behaviors, it's
really about avoiding them.

-- 
Matthieu

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

* Re: git-rm isn't the inverse action of git-add
  2007-07-03 10:37           ` Matthieu Moy
@ 2007-07-03 12:09             ` Johannes Schindelin
  2007-07-03 13:40               ` Matthieu Moy
  0 siblings, 1 reply; 37+ messages in thread
From: Johannes Schindelin @ 2007-07-03 12:09 UTC (permalink / raw)
  To: Matthieu Moy; +Cc: Yann Dirson, Christian Jaeger, git

Hi,

On Tue, 3 Jul 2007, Matthieu Moy wrote:

> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> 
> > What's so wrong with our man pages? You know, there have been man 
> > hours invested in them, and they are exclusively meant for consumption 
> > by people who do not know about the usage of the commands...
> 
> What's wrong is just that I shouldn't have to read a man page to avoid
> data-loss.

Okay, Mr Moy. How did you learn that "rm" leads to data-loss? Because it 
does. Hmm. How did you expect then, that git-rm does _not_ lead to data 
loss? If in doubt, you _have_ to read the manual. Especially if the tool 
is powerful.

Ciao,
Dscho

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

* Re: git-rm isn't the inverse action of git-add
  2007-07-03 12:09             ` Johannes Schindelin
@ 2007-07-03 13:40               ` Matthieu Moy
  2007-07-03 14:21                 ` Johannes Schindelin
  2007-07-04 20:08                 ` Jan Hudec
  0 siblings, 2 replies; 37+ messages in thread
From: Matthieu Moy @ 2007-07-03 13:40 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: Yann Dirson, Christian Jaeger, git

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

> Hi,
>
> On Tue, 3 Jul 2007, Matthieu Moy wrote:
>
>> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
>> 
>> > What's so wrong with our man pages? You know, there have been man 
>> > hours invested in them, and they are exclusively meant for consumption 
>> > by people who do not know about the usage of the commands...
>> 
>> What's wrong is just that I shouldn't have to read a man page to avoid
>> data-loss.
>
> Okay, Mr Moy.

Glad to be called by my name. Is it a tradition here, or a way to make
fun of me?

> How did you learn that "rm" leads to data-loss? Because it does.

It obviously does, and I can't imagine any other behavior than
deleting the file for a command like "rm".

> Hmm. How did you expect then, that git-rm does _not_ lead to data
> loss? 

Because there are tons of possible behaviors for "$VCS rm", and I'd
expect it to be safe even if VCS=git, since it is with all the other
VCS I know.

What's wrong with the behavior of "hg rm"?
What's wrong with the behavior of "svn rm"?
What's wrong with the behavior of "bzr rm"?
(no, I won't do it with CVS ;-) )

None of these commands have the problem that git-rm has.

-- 
Matthieu

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

* Re: git-rm isn't the inverse action of git-add
  2007-07-03 13:40               ` Matthieu Moy
@ 2007-07-03 14:21                 ` Johannes Schindelin
  2007-07-04 20:08                 ` Jan Hudec
  1 sibling, 0 replies; 37+ messages in thread
From: Johannes Schindelin @ 2007-07-03 14:21 UTC (permalink / raw)
  To: Matthieu Moy; +Cc: Yann Dirson, Christian Jaeger, git

Hi,

On Tue, 3 Jul 2007, Matthieu Moy wrote:

> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> 
> > On Tue, 3 Jul 2007, Matthieu Moy wrote:
> >
> >> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> >> 
> >> > What's so wrong with our man pages? You know, there have been man 
> >> > hours invested in them, and they are exclusively meant for 
> >> > consumption by people who do not know about the usage of the 
> >> > commands...
> >> 
> >> What's wrong is just that I shouldn't have to read a man page to 
> >> avoid data-loss.
> >
> > Okay, Mr Moy.
> 
> Glad to be called by my name. Is it a tradition here, or a way to make 
> fun of me?

I tried to be funny, by introducing some diversity...

> > How did you learn that "rm" leads to data-loss? Because it does.
> 
> It obviously does, and I can't imagine any other behavior than deleting 
> the file for a command like "rm".
> 
> > Hmm. How did you expect then, that git-rm does _not_ lead to data
> > loss? 
> 
> Because there are tons of possible behaviors for "$VCS rm", and I'd 
> expect it to be safe even if VCS=git, since it is with all the other VCS 
> I know.

Which proves exactly my point. There are a ton of interpretations that 
make sense. So I would always look into the man page.

> What's wrong with the behavior of "hg rm"?
> What's wrong with the behavior of "svn rm"?
> What's wrong with the behavior of "bzr rm"?
> (no, I won't do it with CVS ;-) )
> 
> None of these commands have the problem that git-rm has.

Guess what. I do not know how they operate! I have no idea what the 
behaviour of the commands you mentioned is. So before I would answer (if 
they were not rethoric questions), I would actually really read the man 
page to know what they are supposed to do.

Ciao,
Dscho

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

* Re: git-rm isn't the inverse action of git-add
  2007-07-03 13:40               ` Matthieu Moy
  2007-07-03 14:21                 ` Johannes Schindelin
@ 2007-07-04 20:08                 ` Jan Hudec
  2007-07-05 13:44                   ` Matthieu Moy
  1 sibling, 1 reply; 37+ messages in thread
From: Jan Hudec @ 2007-07-04 20:08 UTC (permalink / raw)
  To: Johannes Schindelin, Yann Dirson, Christian Jaeger, git

[-- Attachment #1: Type: text/plain, Size: 1228 bytes --]

On Tue, Jul 03, 2007 at 15:40:12 +0200, Matthieu Moy wrote:
> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> > Hmm. How did you expect then, that git-rm does _not_ lead to data
> > loss? 
> 
> Because there are tons of possible behaviors for "$VCS rm", and I'd
> expect it to be safe even if VCS=git, since it is with all the other
> VCS I know.
> 
> What's wrong with the behavior of "hg rm"?
> What's wrong with the behavior of "svn rm"?
> What's wrong with the behavior of "bzr rm"?
> (no, I won't do it with CVS ;-) )
> 
> None of these commands have the problem that git-rm has.

Hm. They all behave roughly the same: They unversion the file and unlink it,
unless it is modified, in which case they unversion it and leave it alone.

Now git has the extra complexity that index contains also content of the
file. But the behaviour can be easily adapted like this (HEAD = version in
HEAD, index = version in index, tree = version in tree):
 - if (HEAD == index && index == version) unversion and unlink
 - else if (HEAD == index || index == version) unversion
 - else print message and do nothing

Would you consider that a sane behaviour?

-- 
						 Jan 'Bulb' Hudec <bulb@ucw.cz>

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

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

* Re: git-rm isn't the inverse action of git-add
  2007-07-04 20:08                 ` Jan Hudec
@ 2007-07-05 13:44                   ` Matthieu Moy
  2007-07-05 14:00                     ` David Kastrup
  2007-07-08 17:36                     ` [RFC][PATCH] " Matthieu Moy
  0 siblings, 2 replies; 37+ messages in thread
From: Matthieu Moy @ 2007-07-05 13:44 UTC (permalink / raw)
  To: Jan Hudec; +Cc: Johannes Schindelin, Yann Dirson, Christian Jaeger, git

Jan Hudec <bulb@ucw.cz> writes:

>> What's wrong with the behavior of "hg rm"?
>> What's wrong with the behavior of "svn rm"?
>> What's wrong with the behavior of "bzr rm"?
>> (no, I won't do it with CVS ;-) )
>> 
>> None of these commands have the problem that git-rm has.
>
> Hm. They all behave roughly the same: They unversion the file and unlink it,
> unless it is modified, in which case they unversion it and leave it
> alone.

Yes. Roughly, they'll ask you a --force flag whenever you'd risk
data-loss. bzr gives you the choice between --force and --keep (that
would be --cached in git) if the file doesn't match HEAD.

> Now git has the extra complexity that index contains also content of the
> file. But the behaviour can be easily adapted like this (HEAD = version in
> HEAD, index = version in index, tree = version in tree):
                                  ^^^^- I suppose you meant "version"
                                        here since you don't use
                                        "tree" after.

>  - if (HEAD == index && index == version) unversion and unlink

Just to be more precise:

   - if (HEAD == index && index == version) unversion and
       * if (--cached is not given) unlink
       * else do nothing

>  - else if (HEAD == index || index == version) unversion
>  - else print message and do nothing
>
> Would you consider that a sane behaviour?

To me, that's a sane behavior.

It makes a few senarios easy and safe, like this:

  $ git add <whatever>
  # Ooops, no, I didn't want to version this one :-(
  $ git rm some-file
  # Cool, I just cancelled my mistake without loosing anything ;-)
  
One benefit is: you don't have to use "-f" for a non-dangerous
senario. That seems stupid, but for the plain "rm" command, the "-rf"
is hardcoded in the fingers of many unix users, and I know several
people having lost data by typing it a bit too mechanically (with a
typo behind, like forgetting the "*" in "*~" ;-).

I'll try writting patch for that if people agree that this is saner
that the current behavior.

-- 
Matthieu

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

* Re: git-rm isn't the inverse action of git-add
  2007-07-05 13:44                   ` Matthieu Moy
@ 2007-07-05 14:00                     ` David Kastrup
  2007-07-08 17:36                     ` [RFC][PATCH] " Matthieu Moy
  1 sibling, 0 replies; 37+ messages in thread
From: David Kastrup @ 2007-07-05 14:00 UTC (permalink / raw)
  To: git

Matthieu Moy <Matthieu.Moy@imag.fr> writes:

> One benefit is: you don't have to use "-f" for a non-dangerous
> senario. That seems stupid, but for the plain "rm" command, the
> "-rf" is hardcoded in the fingers of many unix users, and I know
> several people having lost data by typing it a bit too mechanically
> (with a typo behind, like forgetting the "*" in "*~" ;-).

Just a few days ago, I used rm -rf * in a temporary directory.  I
would now advise people against doing that without an absolute path.
The problem was that at some later point of time, some history
search/key fsckup popped that line back into the shell and executed
it.

At that time, in my home directory.  This was definitely annoying,
even though the files and directories .* (and thus most configuration
data) were spared.

> I'll try writting patch for that if people agree that this is saner
> that the current behavior.

Sounds like it.

-- 
David Kastrup

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

* [RFC][PATCH] Re: git-rm isn't the inverse action of git-add
  2007-07-05 13:44                   ` Matthieu Moy
  2007-07-05 14:00                     ` David Kastrup
@ 2007-07-08 17:36                     ` Matthieu Moy
  2007-07-08 18:10                       ` Johannes Schindelin
  1 sibling, 1 reply; 37+ messages in thread
From: Matthieu Moy @ 2007-07-08 17:36 UTC (permalink / raw)
  To: git; +Cc: Jan Hudec, Johannes Schindelin, Yann Dirson, Christian Jaeger

Matthieu Moy <Matthieu.Moy@imag.fr> writes:

>>  - if (HEAD == index && index == version) unversion and unlink
>
> Just to be more precise:
>
>    - if (HEAD == index && index == version) unversion and
>        * if (--cached is not given) unlink
>        * else do nothing
>
>>  - else if (HEAD == index || index == version) unversion
>>  - else print message and do nothing
>>
>> Would you consider that a sane behaviour?

[...]

> I'll try writting patch for that if people agree that this is saner
> that the current behavior.

Here's a first attempt (I'm still not familiar with the git codebase,
so the patch is probably not so good).

Note: currently, git-rm still shows those "rm '...'" messages on
stdout. AAUI, they were actually useful at a time when git-rm didn't
actually remove the files, and people actually ran the "rm" commands
after. They can probably be removed now, but that's another topic.


>From f4f4aa047b2b9050d968704d1f2db07b2a1a79cc Mon Sep 17 00:00:00 2001
From: Matthieu Moy <Matthieu.Moy@imag.fr>
Date: Sun, 8 Jul 2007 19:27:44 +0200
Subject: [PATCH] Make git-rm obey in more circumstances.

In the previous behavior of git-rm, git refused to do anything in case of
a difference between the file on disk, the index, and the HEAD. As a
result, the -f flag is forced even for simple senarios like:

$ git add foo
# oops, I didn't want to version it
$ git rm -f [--cached] foo
# foo is deleted on disk if --cached isn't provided.

This patch proposes a saner behavior. When there are no difference at all
between file, index and HEAD, the file is removed both from the index and
the tree, as before.

Otherwise, if the index matches either the file on disk or the HEAD, the
file is removed from the index, but the file is kept on disk, it may
contain important data.

Otherwise, that's an error, and git-rm aborts.

The above senario becomes

$ git add foo
$ git rm foo
# back to the initial state.

Signed-off-by: Matthieu Moy <Matthieu.Moy@imag.fr>
---
 Documentation/git-rm.txt |    9 ++++---
 builtin-rm.c             |   55 ++++++++++++++++++++++++++++++++++++---------
 2 files changed, 49 insertions(+), 15 deletions(-)

diff --git a/Documentation/git-rm.txt b/Documentation/git-rm.txt
index 78f45dc..180671c 100644
--- a/Documentation/git-rm.txt
+++ b/Documentation/git-rm.txt
@@ -11,10 +11,11 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
-Remove files from the working tree and from the index.  The
-files have to be identical to the tip of the branch, and no
-updates to its contents must have been placed in the staging
-area (aka index).
+Remove files from the working tree and from the index. The content
+placed in the staging area (aka index) must match either the content
+of the file on disk, or the tip of the branch. If it matches only one
+of them, the file is kept on disk for safety, but is still removed
+from the index.
 
 
 OPTIONS
diff --git a/builtin-rm.c b/builtin-rm.c
index 4a0bd93..d2a8998 100644
--- a/builtin-rm.c
+++ b/builtin-rm.c
@@ -12,18 +12,27 @@
 static const char builtin_rm_usage[] =
 "git-rm [-f] [-n] [-r] [--cached] [--ignore-unmatch] [--quiet] [--] <file>...";
 
+struct file_info {
+	const char *name;
+	int local_changes;
+	int staged_changes;
+};
+
 static struct {
 	int nr, alloc;
-	const char **name;
+	struct file_info * files;
 } list;
 
 static void add_list(const char *name)
 {
 	if (list.nr >= list.alloc) {
 		list.alloc = alloc_nr(list.alloc);
-		list.name = xrealloc(list.name, list.alloc * sizeof(const char *));
+		list.files = xrealloc(list.files, list.alloc * sizeof(const char *));
 	}
-	list.name[list.nr++] = name;
+	list.files[list.nr].name = name;
+	list.files[list.nr].local_changes  = 0;
+	list.files[list.nr].staged_changes = 0;
+	list.nr++;
 }
 
 static int remove_file(const char *name)
@@ -46,6 +55,26 @@ static int remove_file(const char *name)
 	return ret;
 }
 
+static int remove_file_maybe(const struct file_info fi, int quiet)
+{
+	const char *path = fi.name;
+	if (!fi.local_changes && !fi.staged_changes) {
+		/* The file matches either the index or the HEAD.
+		 * It's content exists somewhere else, it's safe to
+		 * delete it.
+		 */
+		return remove_file(path);
+	} else {
+		if (!quiet)
+			fprintf(stderr, 
+				"note: file '%s' not removed "
+				"(doesn't match %s).\n",
+				path,
+				fi.local_changes?"the index":"HEAD");
+		return 0;
+	}
+}
+
 static int check_local_mod(unsigned char *head)
 {
 	/* items in list are already sorted in the cache order,
@@ -62,7 +91,7 @@ static int check_local_mod(unsigned char *head)
 		struct stat st;
 		int pos;
 		struct cache_entry *ce;
-		const char *name = list.name[i];
+		const char *name = list.files[i].name;
 		unsigned char sha1[20];
 		unsigned mode;
 
@@ -87,13 +116,17 @@ static int check_local_mod(unsigned char *head)
 			continue;
 		}
 		if (ce_match_stat(ce, &st, 0))
-			errs = error("'%s' has local modifications "
-				     "(hint: try -f)", ce->name);
+			list.files[i].local_changes = 1;
+
 		if (no_head
 		     || get_tree_entry(head, name, sha1, &mode)
 		     || ce->ce_mode != create_ce_mode(mode)
 		     || hashcmp(ce->sha1, sha1))
-			errs = error("'%s' has changes staged in the index "
+			list.files[i].staged_changes = 1;
+
+		if (list.files[i].local_changes && 
+		    list.files[i].staged_changes)
+			errs = error("'%s' doesn't match neither HEAD nor the index "
 				     "(hint: try -f)", name);
 	}
 	return errs;
@@ -201,7 +234,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
 	 * the index unless all of them succeed.
 	 */
 	for (i = 0; i < list.nr; i++) {
-		const char *path = list.name[i];
+		const char *path = list.files[i].name;
 		if (!quiet)
 			printf("rm '%s'\n", path);
 
@@ -224,13 +257,13 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
 	if (!index_only) {
 		int removed = 0;
 		for (i = 0; i < list.nr; i++) {
-			const char *path = list.name[i];
-			if (!remove_file(path)) {
+			if (!remove_file_maybe(list.files[i], quiet)) {
 				removed = 1;
 				continue;
 			}
 			if (!removed)
-				die("git-rm: %s: %s", path, strerror(errno));
+				die("git-rm: %s: %s", 
+				    list.files[i].name, strerror(errno));
 		}
 	}
 
-- 
1.5.3.rc0.63.gc956-dirty



-- 
Matthieu

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

* Re: [RFC][PATCH] Re: git-rm isn't the inverse action of git-add
  2007-07-08 17:36                     ` [RFC][PATCH] " Matthieu Moy
@ 2007-07-08 18:10                       ` Johannes Schindelin
  2007-07-08 20:34                         ` Matthieu Moy
  0 siblings, 1 reply; 37+ messages in thread
From: Johannes Schindelin @ 2007-07-08 18:10 UTC (permalink / raw)
  To: Matthieu Moy; +Cc: git, Jan Hudec, Yann Dirson, Christian Jaeger

Hi,

On Sun, 8 Jul 2007, Matthieu Moy wrote:

> Subject: [PATCH] Make git-rm obey in more circumstances.

This is not really a good patch title.  Since it only obeys your 
particular understanding of what it should do.  You are changing 
semantics, and you should say so.

> In the previous behavior of git-rm, git refused to do anything in case 
> of a difference between the file on disk, the index, and the HEAD. As a 
> result, the -f flag is forced even for simple senarios like:
> 
> $ git add foo
> # oops, I didn't want to version it
> $ git rm -f [--cached] foo
> # foo is deleted on disk if --cached isn't provided.
> 
> This patch proposes a saner behavior. When there are no difference at 
> all between file, index and HEAD, the file is removed both from the 
> index and the tree, as before.
> 
> Otherwise, if the index matches either the file on disk or the HEAD, the 
> file is removed from the index, but the file is kept on disk, it may 
> contain important data.

However, if some of the files are of the first kind, and some are of the 
second kind, you happily apply with mixed strategies.  IMO that is wrong.

>  static struct {
>  	int nr, alloc;
> -	const char **name;
> +	struct file_info * files;
>  } list;
>  
>  static void add_list(const char *name)
>  {
>  	if (list.nr >= list.alloc) {
>  		list.alloc = alloc_nr(list.alloc);
> -		list.name = xrealloc(list.name, list.alloc * sizeof(const char *));
> +		list.files = xrealloc(list.files, list.alloc * sizeof(const char *));

This is wrong, too.  Yes, it works.  But it really should be 
"sizeof(struct file_info *)".  Remember, code is also documentation.

> +static int remove_file_maybe(const struct file_info fi, int quiet)
> +{
> +	const char *path = fi.name;
> +	if (!fi.local_changes && !fi.staged_changes) {
> +		/* The file matches either the index or the HEAD.
> +		 * It's content exists somewhere else, it's safe to
> +		 * delete it.
> +		 */
> +		return remove_file(path);
> +	} else {

Superfluous "{ .. }".

> +		if (!quiet)
> +			fprintf(stderr, 
> +				"note: file '%s' not removed "
> +				"(doesn't match %s).\n",
> +				path,
> +				fi.local_changes?"the index":"HEAD");
> +		return 0;
> +	}
> +}

I suspect that this case does never fail. 0 means success for 
remove_file().  Not good.  You should at least have a way to ensure that 
it removed the files from the working tree from a script.  Otherwise there 
is not much point in returning a value to begin with.

> @@ -224,13 +257,13 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
>  	if (!index_only) {
>  		int removed = 0;
>  		for (i = 0; i < list.nr; i++) {
> -			const char *path = list.name[i];
> -			if (!remove_file(path)) {
> +			if (!remove_file_maybe(list.files[i], quiet)) {
>  				removed = 1;
>  				continue;
>  			}
>  			if (!removed)
> -				die("git-rm: %s: %s", path, strerror(errno));
> +				die("git-rm: %s: %s", 
> +				    list.files[i].name, strerror(errno));
>  		}
>  	}

Style: the old code set and used "path" for readability.  You should do 
the same (with "file", probably).

Additionally, since this changes semantics, you better provide test cases 
to show what is expected to work, and _ensure_ that it actually works.

Ciao,
Dscho

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

* Re: [RFC][PATCH] Re: git-rm isn't the inverse action of git-add
  2007-07-08 18:10                       ` Johannes Schindelin
@ 2007-07-08 20:34                         ` Matthieu Moy
  2007-07-08 21:49                           ` Johannes Schindelin
  0 siblings, 1 reply; 37+ messages in thread
From: Matthieu Moy @ 2007-07-08 20:34 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: git, Jan Hudec, Yann Dirson, Christian Jaeger

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

>> This patch proposes a saner behavior. When there are no difference at 
>> all between file, index and HEAD, the file is removed both from the 
>> index and the tree, as before.
>> 
>> Otherwise, if the index matches either the file on disk or the HEAD, the 
>> file is removed from the index, but the file is kept on disk, it may 
>> contain important data.
>
> However, if some of the files are of the first kind, and some are of the 
> second kind, you happily apply with mixed strategies.  IMO that is wrong.

I'm not sure whether this is really wrong. The things git should
really care about are the index and the repository itself, and the
proposed behavior is consistant regarding that (either remove all
files from the index, or remove none).

I'm not opposed to your proposal, but I'd like to have other
opinion(s) on that before changing the code.

>>  static struct {
>>  	int nr, alloc;
>> -	const char **name;
>> +	struct file_info * files;
>>  } list;
>>  
>>  static void add_list(const char *name)
>>  {
>>  	if (list.nr >= list.alloc) {
>>  		list.alloc = alloc_nr(list.alloc);
>> -		list.name = xrealloc(list.name, list.alloc * sizeof(const char *));
>> +		list.files = xrealloc(list.files, list.alloc * sizeof(const char *));
>
> This is wrong, too.  Yes, it works.  But it really should be 
> "sizeof(struct file_info *)".  Remember, code is also documentation.

You don't need to argue, that was a typo. My code is definitely wrong,
but you're wrong too ;-). That's actually sizeof(struct file_info).

>> +		if (!quiet)
>> +			fprintf(stderr, 
>> +				"note: file '%s' not removed "
>> +				"(doesn't match %s).\n",
>> +				path,
>> +				fi.local_changes?"the index":"HEAD");
>> +		return 0;
>> +	}
>> +}
>
> I suspect that this case does never fail. 0 means success for 
> remove_file().  Not good.  You should at least have a way to ensure that 
> it removed the files from the working tree from a script.  Otherwise there 
> is not much point in returning a value to begin with.

I've changed it to have exit_status = 1 if git-rm aborted before
starting, and 2 if git-rm skiped some file removals (and of course, 0
if everything is done as expected).

>> @@ -224,13 +257,13 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
>>  	if (!index_only) {
>>  		int removed = 0;
>>  		for (i = 0; i < list.nr; i++) {
>> -			const char *path = list.name[i];
>> -			if (!remove_file(path)) {
>> +			if (!remove_file_maybe(list.files[i], quiet)) {
>>  				removed = 1;
>>  				continue;
>>  			}
>>  			if (!removed)
>> -				die("git-rm: %s: %s", path, strerror(errno));
>> +				die("git-rm: %s: %s", 
>> +				    list.files[i].name, strerror(errno));
>>  		}
>>  	}
>
> Style: the old code set and used "path" for readability.  You should do 
> the same (with "file", probably).

Done.

> Additionally, since this changes semantics, you better provide test cases 
> to show what is expected to work, and _ensure_ that it actually works.

Sure. I forgot to mention it in my message, but I wanted to have
feedback before getting into the testsuite stuff.

I'm posting the updated patch for info, but it should anyway not be
merged until

* We agree on the behavior when different files have different kinds
  of changes

* I add a testcase.



>From f39ae646049b95b055e34da378ea470ef3f3caef Mon Sep 17 00:00:00 2001
From: Matthieu Moy <Matthieu.Moy@imag.fr>
Date: Sun, 8 Jul 2007 19:27:44 +0200
Subject: [PATCH] Change the behavior of git-rm to let it obey in more circumstances without -f.

In the previous behavior of git-rm, git refused to do anything in case of
a difference between the file on disk, the index, and the HEAD. As a
result, the -f flag is forced even for simple senarios like:

$ git add foo
$ git rm -f [--cached] foo

This patch proposes a saner behavior. When there are no difference at all
between file, index and HEAD, the file is removed both from the index and
the tree, as before.

Otherwise, if the index matches either the file on disk or the HEAD, the
file is removed from the index, but the file is kept on disk, it may
contain important data.

Otherwise, that's an error, and git-rm aborts.

The above senario becomes

$ git add foo
$ git rm foo

Signed-off-by: Matthieu Moy <Matthieu.Moy@imag.fr>
---
 Documentation/git-rm.txt |    9 +++--
 builtin-rm.c             |   71 ++++++++++++++++++++++++++++++++++++++--------
 2 files changed, 64 insertions(+), 16 deletions(-)

diff --git a/Documentation/git-rm.txt b/Documentation/git-rm.txt
index 78f45dc..180671c 100644
--- a/Documentation/git-rm.txt
+++ b/Documentation/git-rm.txt
@@ -11,10 +11,11 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
-Remove files from the working tree and from the index.  The
-files have to be identical to the tip of the branch, and no
-updates to its contents must have been placed in the staging
-area (aka index).
+Remove files from the working tree and from the index. The content
+placed in the staging area (aka index) must match either the content
+of the file on disk, or the tip of the branch. If it matches only one
+of them, the file is kept on disk for safety, but is still removed
+from the index.
 
 
 OPTIONS
diff --git a/builtin-rm.c b/builtin-rm.c
index 4a0bd93..08af5de 100644
--- a/builtin-rm.c
+++ b/builtin-rm.c
@@ -12,20 +12,30 @@
 static const char builtin_rm_usage[] =
 "git-rm [-f] [-n] [-r] [--cached] [--ignore-unmatch] [--quiet] [--] <file>...";
 
+struct file_info {
+	const char *name;
+	int local_changes;
+	int staged_changes;
+};
+
 static struct {
 	int nr, alloc;
-	const char **name;
+	struct file_info *files;
 } list;
 
 static void add_list(const char *name)
 {
 	if (list.nr >= list.alloc) {
 		list.alloc = alloc_nr(list.alloc);
-		list.name = xrealloc(list.name, list.alloc * sizeof(const char *));
+		list.files = xrealloc(list.files, list.alloc * sizeof(struct file_info));
 	}
-	list.name[list.nr++] = name;
+	list.files[list.nr].name = name;
+	list.files[list.nr].local_changes  = 0;
+	list.files[list.nr].staged_changes = 0;
+	list.nr++;
 }
 
+/* Returns -1 on error, zero on success */
 static int remove_file(const char *name)
 {
 	int ret;
@@ -46,6 +56,30 @@ static int remove_file(const char *name)
 	return ret;
 }
 
+/* Returns 0 if the file was actually deleted, -1 if the file removal
+   was a failure, and 1 if remove_file wasn't actually called */
+static int remove_file_maybe(const struct file_info fi, int quiet)
+{
+	const char *path = fi.name;
+	if (!fi.local_changes && !fi.staged_changes)
+		/* The file matches either the index or the HEAD.
+		 * It's content exists somewhere else, it's safe to
+		 * delete it.
+		 */
+		return remove_file(path);
+	else {
+		if (!quiet)
+			fprintf(stderr, 
+				"note: file '%s' not removed "
+				"(%s).\n",
+				path,
+				fi.local_changes ? 
+				"the index doesn't match HEAD" :
+				"the file doesn't match the index");
+		return 1;
+	}
+}
+
 static int check_local_mod(unsigned char *head)
 {
 	/* items in list are already sorted in the cache order,
@@ -62,7 +96,7 @@ static int check_local_mod(unsigned char *head)
 		struct stat st;
 		int pos;
 		struct cache_entry *ce;
-		const char *name = list.name[i];
+		const char *name = list.files[i].name;
 		unsigned char sha1[20];
 		unsigned mode;
 
@@ -87,13 +121,17 @@ static int check_local_mod(unsigned char *head)
 			continue;
 		}
 		if (ce_match_stat(ce, &st, 0))
-			errs = error("'%s' has local modifications "
-				     "(hint: try -f)", ce->name);
+			list.files[i].local_changes = 1;
+
 		if (no_head
 		     || get_tree_entry(head, name, sha1, &mode)
 		     || ce->ce_mode != create_ce_mode(mode)
 		     || hashcmp(ce->sha1, sha1))
-			errs = error("'%s' has changes staged in the index "
+			list.files[i].staged_changes = 1;
+
+		if (list.files[i].local_changes && 
+		    list.files[i].staged_changes)
+			errs = error("'%s' doesn't match neither HEAD nor the index "
 				     "(hint: try -f)", name);
 	}
 	return errs;
@@ -108,6 +146,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
 	int ignore_unmatch = 0;
 	const char **pathspec;
 	char *seen;
+	int exit_status = 0;
 
 	git_config(git_default_config);
 
@@ -201,7 +240,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
 	 * the index unless all of them succeed.
 	 */
 	for (i = 0; i < list.nr; i++) {
-		const char *path = list.name[i];
+		const char *path = list.files[i].name;
 		if (!quiet)
 			printf("rm '%s'\n", path);
 
@@ -224,13 +263,21 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
 	if (!index_only) {
 		int removed = 0;
 		for (i = 0; i < list.nr; i++) {
-			const char *path = list.name[i];
-			if (!remove_file(path)) {
+			struct file_info file = list.files[i];
+			int status = remove_file_maybe(file, quiet);
+			if (status == 0) {
 				removed = 1;
 				continue;
+			} else if (status == 1) {
+				/* Let the user know from a script
+				 * that a file was not deleted on disk
+				 */
+				exit_status = 2;
+				continue;
 			}
 			if (!removed)
-				die("git-rm: %s: %s", path, strerror(errno));
+				die("git-rm: %s: %s", 
+				    file.name, strerror(errno));
 		}
 	}
 
@@ -240,5 +287,5 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
 			die("Unable to write new index file");
 	}
 
-	return 0;
+	return exit_status;
 }
-- 
1.5.3.rc0.64.gf4f4a-dirty



-- 
Matthieu

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

* Re: [RFC][PATCH] Re: git-rm isn't the inverse action of git-add
  2007-07-08 20:34                         ` Matthieu Moy
@ 2007-07-08 21:49                           ` Johannes Schindelin
  2007-07-09  9:45                             ` Matthieu Moy
  2007-07-13 17:36                             ` Matthieu Moy
  0 siblings, 2 replies; 37+ messages in thread
From: Johannes Schindelin @ 2007-07-08 21:49 UTC (permalink / raw)
  To: Matthieu Moy; +Cc: git, Jan Hudec, Yann Dirson, Christian Jaeger

Hi,

On Sun, 8 Jul 2007, Matthieu Moy wrote:

> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> 
> >> This patch proposes a saner behavior. When there are no difference at 
> >> all between file, index and HEAD, the file is removed both from the 
> >> index and the tree, as before.
> >> 
> >> Otherwise, if the index matches either the file on disk or the HEAD, 
> >> the file is removed from the index, but the file is kept on disk, it 
> >> may contain important data.
> >
> > However, if some of the files are of the first kind, and some are of 
> > the second kind, you happily apply with mixed strategies.  IMO that is 
> > wrong.
> 
> I'm not sure whether this is really wrong. The things git should
> really care about are the index and the repository itself, and the
> proposed behavior is consistant regarding that (either remove all
> files from the index, or remove none).

Well, I think it is wrong for the same reason as it is wrong to apply the 
changes to _any_ file when one would fail.  And since "git apply" shares 
my understanding, I think "git rm" should, too.

> >>  static struct {
> >>  	int nr, alloc;
> >> -	const char **name;
> >> +	struct file_info * files;
> >>  } list;
> >>  
> >>  static void add_list(const char *name)
> >>  {
> >>  	if (list.nr >= list.alloc) {
> >>  		list.alloc = alloc_nr(list.alloc);
> >> -		list.name = xrealloc(list.name, list.alloc * sizeof(const char *));
> >> +		list.files = xrealloc(list.files, list.alloc * sizeof(const char *));
> >
> > This is wrong, too.  Yes, it works.  But it really should be 
> > "sizeof(struct file_info *)".  Remember, code is also documentation.
> 
> You don't need to argue, that was a typo. My code is definitely wrong, 
> but you're wrong too ;-). That's actually sizeof(struct file_info).

Heh, right.

> >> +		if (!quiet)
> >> +			fprintf(stderr, 
> >> +				"note: file '%s' not removed "
> >> +				"(doesn't match %s).\n",
> >> +				path,
> >> +				fi.local_changes?"the index":"HEAD");
> >> +		return 0;
> >> +	}
> >> +}
> >
> > I suspect that this case does never fail. 0 means success for 
> > remove_file().  Not good.  You should at least have a way to ensure that 
> > it removed the files from the working tree from a script.  Otherwise there 
> > is not much point in returning a value to begin with.
> 
> I've changed it to have exit_status = 1 if git-rm aborted before
> starting, and 2 if git-rm skiped some file removals (and of course, 0
> if everything is done as expected).

Oh, so you do not take the return value of this function to determine if 
it has or has not done something with the files?  That's a bit confusing.

Besides, it would be all the more a reason for a test case, so that I can 
see that I am actually wrong.

> > Additionally, since this changes semantics, you better provide test 
> > cases to show what is expected to work, and _ensure_ that it actually 
> > works.
> 
> Sure. I forgot to mention it in my message, but I wanted to have 
> feedback before getting into the testsuite stuff.

I think it should be the other way.  If you change semantics with the 
patch, but another revision changes semantics _differently_, it is really 
easy to get lost.  In order to demonstrate what should be true, you have 
to provide examples.  And if you are already providing examples, just wrap 
them into

	test_description <description>
	. ./test-lib.sh

	...

	test_done

and prefix each test with "test_expect_success", and you're done.  It is 
really not something requiring a wizard.

> I'm posting the updated patch for info, but it should anyway not be
> merged until
> 
> * We agree on the behavior when different files have different kinds
>   of changes

I'd understand better what you wish to accomplish with the...

> * I add a testcase.

... testcase. So those are not two distinct points.

> >From f39ae646049b95b055e34da378ea470ef3f3caef Mon Sep 17 00:00:00 2001
> From: Matthieu Moy <Matthieu.Moy@imag.fr>
> Date: Sun, 8 Jul 2007 19:27:44 +0200
> Subject: [PATCH] Change the behavior of git-rm to let it obey in more circumstances without -f.

Please do not do this.

I meant to complain about your OP, but this time it is even worse.  The 
best way to guarantee that a patch gets lost in a thread is to move it _at 
the end_ of a reply.

Please follow the form that you change the subject, still reply, but but 
the quoted mail with your answers to that text between the "---" and the 
diffstat.

If that text is too long, you should use a separate email for the patch.

Ciao,
Dscho

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

* Re: [RFC][PATCH] Re: git-rm isn't the inverse action of git-add
  2007-07-08 21:49                           ` Johannes Schindelin
@ 2007-07-09  9:45                             ` Matthieu Moy
  2007-07-13 17:36                             ` Matthieu Moy
  1 sibling, 0 replies; 37+ messages in thread
From: Matthieu Moy @ 2007-07-09  9:45 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: git, Jan Hudec, Yann Dirson, Christian Jaeger

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

> Hi,
>
> On Sun, 8 Jul 2007, Matthieu Moy wrote:
>
>> I'm not sure whether this is really wrong. The things git should
>> really care about are the index and the repository itself, and the
>> proposed behavior is consistant regarding that (either remove all
>> files from the index, or remove none).
>
> Well, I think it is wrong for the same reason as it is wrong to apply the 
> changes to _any_ file when one would fail.  And since "git apply" shares 
> my understanding, I think "git rm" should, too.

OK, let's say I'm convinced ;-).

>> > I suspect that this case does never fail. 0 means success for 
>> > remove_file().  Not good.  You should at least have a way to ensure that 
>> > it removed the files from the working tree from a script.  Otherwise there 
>> > is not much point in returning a value to begin with.
>> 
>> I've changed it to have exit_status = 1 if git-rm aborted before
>> starting, and 2 if git-rm skiped some file removals (and of course, 0
>> if everything is done as expected).
>
> Oh, so you do not take the return value of this function to determine if 
> it has or has not done something with the files?  That's a bit confusing.

I did, but previously, I kept the code that "die()"s if the first call
to remove_file() "fails". In remove_file_maybe(), not removing a file
because it's not sure it's safe to delete it is not a failure, so I
had to put a "return 0;" here to avoid the fatal error. My first patch
had return status !=0 if we tried to remove the file, and it failed. I
changed that.

> I meant to complain about your OP, but this time it is even worse.  The 
> best way to guarantee that a patch gets lost in a thread is to move it _at 
> the end_ of a reply.

I had posted the patch for info, but I did expect this one to get
lost, since it's definitely not complete.

I'll post an updated patch with a testcase and an appropriate subject
line within a few days (I don't have time right now).

Thanks,

-- 
Matthieu

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

* Re: git-rm isn't the inverse action of git-add
  2007-07-02 20:23   ` Christian Jaeger
  2007-07-02 20:40     ` Yann Dirson
@ 2007-07-11 12:20     ` Jakub Narebski
  2007-07-11 18:56       ` Jan Hudec
  1 sibling, 1 reply; 37+ messages in thread
From: Jakub Narebski @ 2007-07-11 12:20 UTC (permalink / raw)
  To: git

Christian Jaeger wrote:

> I don't per se require undo actions. I just don't understand why git-rm
> refuses to remove the file from the index, even if I didn't commit it.
> The index is just an intermediate record of the changes in my
> understandings, and the rm action would also be intermediate until it's
> being committed. And a non-committed action being deleted shouldn't need
> a special confirmation from me, especially not one which is consisting
> of a combination of two flags (of which one is a destructive one).

Should git-rm refuse to remove index entry if it is different from working
directory version or not?

-- 
Jakub Narebski
Warsaw, Poland
ShadeHawk on #git

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

* Re: git-rm isn't the inverse action of git-add
  2007-07-11 12:20     ` Jakub Narebski
@ 2007-07-11 18:56       ` Jan Hudec
  2007-07-11 21:26         ` Junio C Hamano
  0 siblings, 1 reply; 37+ messages in thread
From: Jan Hudec @ 2007-07-11 18:56 UTC (permalink / raw)
  To: Jakub Narebski; +Cc: git

[-- Attachment #1: Type: text/plain, Size: 1245 bytes --]

On Wed, Jul 11, 2007 at 14:20:24 +0200, Jakub Narebski wrote:
> Christian Jaeger wrote:
> > I don't per se require undo actions. I just don't understand why git-rm
> > refuses to remove the file from the index, even if I didn't commit it.
> > The index is just an intermediate record of the changes in my
> > understandings, and the rm action would also be intermediate until it's
> > being committed. And a non-committed action being deleted shouldn't need
> > a special confirmation from me, especially not one which is consisting
> > of a combination of two flags (of which one is a destructive one).
> 
> Should git-rm refuse to remove index entry if it is different from working
> directory version or not?

IMHO it should refuse to remove index entry if it is different from both
working-tree version and versions in all parents.

If index matches any of that, but the working tree version does not match any
parent, the index entry should be removed (which currently isn't -- that's
the proposed change), but the file left in wokring tree. That would make
git-add + git-rm get you right back where you started, with nothing in index
and unversioned file in working tree.

-- 
						 Jan 'Bulb' Hudec <bulb@ucw.cz>

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]

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

* Re: git-rm isn't the inverse action of git-add
  2007-07-11 18:56       ` Jan Hudec
@ 2007-07-11 21:26         ` Junio C Hamano
  0 siblings, 0 replies; 37+ messages in thread
From: Junio C Hamano @ 2007-07-11 21:26 UTC (permalink / raw)
  To: Jan Hudec; +Cc: Jakub Narebski, git

Jan Hudec <bulb@ucw.cz> writes:

> If index matches any of that, but the working tree version does not match any
> parent, the index entry should be removed (which currently isn't -- that's
> the proposed change), but the file left in wokring tree. That would make
> git-add + git-rm get you right back where you started, with nothing in index
> and unversioned file in working tree.

Don't think of 'rm' as inverse of 'add'.  That would only
confuse you.

A natural inverse of 'add' is 'un-add', and that operation is
called 'rm --cached', because we use that to name the option to
invoke an "index-only" variant of a command when the command can
operate on index and working tree file (e.g. "diff --cached",
"apply --cached").

A life of a file that does _not_ make into a commit goes like
this:

	[1]$ edit a-new-file

This is 'create', not 'add'.  git is not involved in this step.

	[2]$ git add a-new-file

This is 'add'; place an existing file in the index.  When you do
not want it in the index, you 'un-add' it.

	[3]$ git rm --cached a-new-file

This removes the entry from the index, without touching the
working tree file.  If you do not want that file at all (as
opposed to, "I am making a series of partial commits, and the
addition of this path does not belong to the first commit of the
series, so I am unstaging"), this is followed by

	[4]$ rm -f a-new-file

Again, git is not involved in this step.

The thing is, people sometimes want to have steps 3 and 4
combined, and it meshes well with the users' expectation when
they see the word "rm".  Think of "git rm" without "--cached" as
a shorthand to do 3 and 4 in one go to meet that expectation.

Obviously, we cannot usefully combine steps 1 and 2.  We could
have "git add --create a-new-file" launch an editor to create a
new file, but that would not be very useful in practice.

The fact that steps 3 and 4 can be naturally combined, but steps
1 and 2 cannot be, makes "add" and "rm" not inverse of each
other.

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

* Re: [RFC][PATCH] Re: git-rm isn't the inverse action of git-add
  2007-07-08 21:49                           ` Johannes Schindelin
  2007-07-09  9:45                             ` Matthieu Moy
@ 2007-07-13 17:36                             ` Matthieu Moy
  2007-07-13 17:41                               ` [PATCH] More permissive "git-rm --cached" behavior without -f Matthieu Moy
  1 sibling, 1 reply; 37+ messages in thread
From: Matthieu Moy @ 2007-07-13 17:36 UTC (permalink / raw)
  To: Johannes Schindelin; +Cc: git, Jan Hudec, Yann Dirson, Christian Jaeger

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

>> > However, if some of the files are of the first kind, and some are of 
>> > the second kind, you happily apply with mixed strategies.  IMO that is 
>> > wrong.
>> 
>> I'm not sure whether this is really wrong. The things git should
>> really care about are the index and the repository itself, and the
>> proposed behavior is consistant regarding that (either remove all
>> files from the index, or remove none).
>
> Well, I think it is wrong for the same reason as it is wrong to apply the 
> changes to _any_ file when one would fail.  And since "git apply" shares 
> my understanding, I think "git rm" should, too.

OK, I've been thinking about it for some time (not having time to hack
can be good, it lets time for thinking instead ;-) ).

I'm actually still not convinced that my proposal was wrong, but I
think we disagree because we disagree on what is a "failure". I
consider leaving the file in the working tree to be just a safety
precaution, not a failure, and to me, it's OK to do that only for the
files that need it.

Fixing my patch by just "applying the same strategy to all files"
would be wrong: leaving _all_ the files on disk when just one has
local modifications is very misleading, and if the user notices it
after running the command, he or she does not always have an easy way
to get back to a clean situation (re-running the same command with -f
wouldn't work for example).

So, I went a shorter way from the current semantics:

* Allow --cached in more situations, so that -f is really needed in
  very particular situation (as I mentionned above, forcing -f too
  often means the -f gets hardcoded in the fingers, and makes it
  useless).

* Better error message, which points to --cached in addition to -f.

That's very close to what bzr does, BTW.

Drawback: it still doesn't solve the "rm isn't the inverse of add".

The patch is quite straightforward, and will be in a followup email.

-- 
Matthieu

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

* [PATCH] More permissive "git-rm --cached" behavior without -f.
  2007-07-13 17:36                             ` Matthieu Moy
@ 2007-07-13 17:41                               ` Matthieu Moy
  2007-07-13 17:57                                 ` Jeff King
                                                   ` (2 more replies)
  0 siblings, 3 replies; 37+ messages in thread
From: Matthieu Moy @ 2007-07-13 17:41 UTC (permalink / raw)
  To: git, Johannes Schindelin; +Cc: Matthieu Moy

In the previous behavior, "git-rm --cached" (without -f) had the same
restriction as "git-rm". This forced the user to use the -f flag in
situations which weren't actually dangerous, like:

$ git add foo           # oops, I didn't want this
$ git rm --cached foo   # back to initial situation

Previously, the index had to match the file *and* the HEAD. With
--cached, the index must now match the file *or* the HEAD. The behavior
without --cached is unchanged, but provides better error messages.

Signed-off-by: Matthieu Moy <Matthieu.Moy@imag.fr>
---
 Documentation/git-rm.txt |    3 ++-
 builtin-rm.c             |   32 ++++++++++++++++++++++++++------
 t/t3600-rm.sh            |   34 ++++++++++++++++++++++++++++++++++
 3 files changed, 62 insertions(+), 7 deletions(-)

diff --git a/Documentation/git-rm.txt b/Documentation/git-rm.txt
index 78f45dc..be61a82 100644
--- a/Documentation/git-rm.txt
+++ b/Documentation/git-rm.txt
@@ -14,7 +14,8 @@ DESCRIPTION
 Remove files from the working tree and from the index.  The
 files have to be identical to the tip of the branch, and no
 updates to its contents must have been placed in the staging
-area (aka index).
+area (aka index).  When --cached is given, the staged content has to
+match either the tip of the branch *or* the file on disk.
 
 
 OPTIONS
diff --git a/builtin-rm.c b/builtin-rm.c
index 4a0bd93..9a808c1 100644
--- a/builtin-rm.c
+++ b/builtin-rm.c
@@ -46,7 +46,7 @@ static int remove_file(const char *name)
 	return ret;
 }
 
-static int check_local_mod(unsigned char *head)
+static int check_local_mod(unsigned char *head, int index_only)
 {
 	/* items in list are already sorted in the cache order,
 	 * so we could do this a lot more efficiently by using
@@ -65,6 +65,8 @@ static int check_local_mod(unsigned char *head)
 		const char *name = list.name[i];
 		unsigned char sha1[20];
 		unsigned mode;
+		int local_changes = 0;
+		int staged_changes = 0;
 
 		pos = cache_name_pos(name, strlen(name));
 		if (pos < 0)
@@ -87,14 +89,32 @@ static int check_local_mod(unsigned char *head)
 			continue;
 		}
 		if (ce_match_stat(ce, &st, 0))
-			errs = error("'%s' has local modifications "
-				     "(hint: try -f)", ce->name);
+			local_changes = 1;
 		if (no_head
 		     || get_tree_entry(head, name, sha1, &mode)
 		     || ce->ce_mode != create_ce_mode(mode)
 		     || hashcmp(ce->sha1, sha1))
-			errs = error("'%s' has changes staged in the index "
-				     "(hint: try -f)", name);
+			staged_changes = 1;
+
+		if (local_changes && staged_changes)
+			errs = error("'%s' has staged content different "
+				     "from both the file and the HEAD\n"
+				     "(use -f to force removal)", name);
+		else if (!index_only) {
+			/* It's not dangerous to git-rm --cached a
+			 * file if the index matches the file or the
+			 * HEAD, since it means the deleted content is
+			 * still available somewhere.
+			 */
+			if (staged_changes)
+				errs = error("'%s' has changes staged in the index\n"
+					     "(use --cached to keep the file, "
+					     "or -f to force removal)", name);
+			if (local_changes)
+				errs = error("'%s' has local modifications\n"
+					     "(use --cached to keep the file, "
+					     "or -f to force removal)", name);
+		}
 	}
 	return errs;
 }
@@ -192,7 +212,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
 		unsigned char sha1[20];
 		if (get_sha1("HEAD", sha1))
 			hashclr(sha1);
-		if (check_local_mod(sha1))
+		if (check_local_mod(sha1, index_only))
 			exit(1);
 	}
 
diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh
index 13a461f..5c001aa 100755
--- a/t/t3600-rm.sh
+++ b/t/t3600-rm.sh
@@ -46,6 +46,40 @@ test_expect_success \
     'git rm --cached foo'
 
 test_expect_success \
+    'Test that git rm --cached foo succeeds if the index matches the file' \
+    'echo content > foo
+     git add foo
+     git rm --cached foo'
+
+test_expect_success \
+    'Test that git rm --cached foo succeeds if the index matches the file' \
+    'echo content > foo
+     git add foo
+     git commit -m foo
+     echo "other content" > foo
+     git rm --cached foo'
+
+test_expect_failure \
+    'Test that git rm --cached foo fails if the index matches neither the file nor HEAD' \
+    'echo content > foo
+     git add foo
+     git commit -m foo
+     echo "other content" > foo
+     git add foo
+     echo "yet another content" > foo
+     git rm --cached foo'
+
+test_expect_success \
+    'Test that git rm --cached -f foo works in case where --cached only did not' \
+    'echo content > foo
+     git add foo
+     git commit -m foo
+     echo "other content" > foo
+     git add foo
+     echo "yet another content" > foo
+     git rm --cached -f foo'
+
+test_expect_success \
     'Post-check that foo exists but is not in index after git rm foo' \
     '[ -f foo ] && ! git ls-files --error-unmatch foo'
 
-- 
1.5.3.rc1.4.gaf83-dirty

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

* Re: [PATCH] More permissive "git-rm --cached" behavior without -f.
  2007-07-13 17:41                               ` [PATCH] More permissive "git-rm --cached" behavior without -f Matthieu Moy
@ 2007-07-13 17:57                                 ` Jeff King
  2007-07-13 18:53                                   ` Matthieu Moy
  2007-07-14  0:44                                 ` Jakub Narebski
  2007-07-14  6:52                                 ` Junio C Hamano
  2 siblings, 1 reply; 37+ messages in thread
From: Jeff King @ 2007-07-13 17:57 UTC (permalink / raw)
  To: Matthieu Moy; +Cc: git, Johannes Schindelin

On Fri, Jul 13, 2007 at 07:41:38PM +0200, Matthieu Moy wrote:

> Previously, the index had to match the file *and* the HEAD. With
> --cached, the index must now match the file *or* the HEAD. The behavior
> without --cached is unchanged, but provides better error messages.

This does make more sense, but there are still some inconsistencies. Is
it OK to lose content that is only in the index, or not?

If it is OK, then --cached shouldn't need _any_ safety valve (and after
all, anything you remove in that manner is recoverable with git-fsck
until the next prune).

If it isn't OK, then you are not addressing the cases where git-rm
without --cached loses index content (that is different than HEAD and
the working tree).

-Peff

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

* Re: [PATCH] More permissive "git-rm --cached" behavior without -f.
  2007-07-13 17:57                                 ` Jeff King
@ 2007-07-13 18:53                                   ` Matthieu Moy
  2007-07-14  3:42                                     ` Jeff King
  0 siblings, 1 reply; 37+ messages in thread
From: Matthieu Moy @ 2007-07-13 18:53 UTC (permalink / raw)
  To: Jeff King; +Cc: git, Johannes Schindelin

Jeff King <peff@peff.net> writes:

> On Fri, Jul 13, 2007 at 07:41:38PM +0200, Matthieu Moy wrote:
>
>> Previously, the index had to match the file *and* the HEAD. With
>> --cached, the index must now match the file *or* the HEAD. The behavior
>> without --cached is unchanged, but provides better error messages.
>
> This does make more sense, but there are still some inconsistencies. Is
> it OK to lose content that is only in the index, or not?

I'd say it isn't OK. At least, that's what the previous git-rm
considered.

> If it is OK, then --cached shouldn't need _any_ safety valve (and after
> all, anything you remove in that manner is recoverable with git-fsck
> until the next prune).
>
> If it isn't OK, then you are not addressing the cases where git-rm
> without --cached loses index content (that is different than HEAD and
> the working tree).

Either I didn't understand your question, or the answer is "yes, I
do.". The behavior without --cached is not modified, except for the
error message, and the previous was to require -f whenever the index
doesn't match the head, *or* doesn't match the file. So, without
--cached, you need to have file=index=HEAD to be able to git-rm.

If I missunderstand you, please, provide a senario where my patch
doesn't do the expected.

-- 
Matthieu

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

* Re: [PATCH] More permissive "git-rm --cached" behavior without -f.
  2007-07-13 17:41                               ` [PATCH] More permissive "git-rm --cached" behavior without -f Matthieu Moy
  2007-07-13 17:57                                 ` Jeff King
@ 2007-07-14  0:44                                 ` Jakub Narebski
  2007-07-14  6:52                                 ` Junio C Hamano
  2 siblings, 0 replies; 37+ messages in thread
From: Jakub Narebski @ 2007-07-14  0:44 UTC (permalink / raw)
  To: git

Matthieu Moy wrote:

> In the previous behavior, "git-rm --cached" (without -f) had the same
> restriction as "git-rm". This forced the user to use the -f flag in
> situations which weren't actually dangerous, like:
> 
> $ git add foo           # oops, I didn't want this
> $ git rm --cached foo   # back to initial situation
> 
> Previously, the index had to match the file *and* the HEAD. With
> --cached, the index must now match the file *or* the HEAD. The behavior
> without --cached is unchanged, but provides better error messages.

Sensible.

There might be some discussion if what git-rm without --cached does
is right, but that is besides scope of this patch.
-- 
Jakub Narebski
Warsaw, Poland
ShadeHawk on #git

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

* Re: [PATCH] More permissive "git-rm --cached" behavior without -f.
  2007-07-13 18:53                                   ` Matthieu Moy
@ 2007-07-14  3:42                                     ` Jeff King
  0 siblings, 0 replies; 37+ messages in thread
From: Jeff King @ 2007-07-14  3:42 UTC (permalink / raw)
  To: git; +Cc: Johannes Schindelin

On Fri, Jul 13, 2007 at 08:53:38PM +0200, Matthieu Moy wrote:

> do.". The behavior without --cached is not modified, except for the
> error message, and the previous was to require -f whenever the index
> doesn't match the head, *or* doesn't match the file. So, without
> --cached, you need to have file=index=HEAD to be able to git-rm.
> 
> If I missunderstand you, please, provide a senario where my patch
> doesn't do the expected.

Right, my point was that there is a case where running without --cached
could lose content: when there is no working tree file. However,
thinking about it more, I recall that Junio made the point that allowing
that behavior means the CVS idiom of "rm file; git-rm file" will just
work.

Not that that was a problem you introduced; I merely wanted to push for
total consistency rather than just handling --cached. But I think the
non --cached behavior is actually right now, so let me retract my
complaint.

And assuming the "git-rm when no working tree file" current behavior is
OK, then I think your patch removes the last consistency problem that I
mentioned in my state table here:

  http://article.gmane.org/gmane.comp.version-control.git/51449

So in a round-about way, I totally approve of your patch. Sorry for the
confusion.

-Peff

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

* Re: [PATCH] More permissive "git-rm --cached" behavior without -f.
  2007-07-13 17:41                               ` [PATCH] More permissive "git-rm --cached" behavior without -f Matthieu Moy
  2007-07-13 17:57                                 ` Jeff King
  2007-07-14  0:44                                 ` Jakub Narebski
@ 2007-07-14  6:52                                 ` Junio C Hamano
  2007-07-14  7:16                                   ` Junio C Hamano
  2 siblings, 1 reply; 37+ messages in thread
From: Junio C Hamano @ 2007-07-14  6:52 UTC (permalink / raw)
  To: Matthieu Moy; +Cc: git, Johannes Schindelin

Although I would not be using it often myself, I think this
would make "git rm" more pleasant to use.

Thanks for the patch, and my thanks also go to people who
commented on the patch.

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

* Re: [PATCH] More permissive "git-rm --cached" behavior without -f.
  2007-07-14  6:52                                 ` Junio C Hamano
@ 2007-07-14  7:16                                   ` Junio C Hamano
  2007-07-14 10:14                                     ` Matthieu Moy
  0 siblings, 1 reply; 37+ messages in thread
From: Junio C Hamano @ 2007-07-14  7:16 UTC (permalink / raw)
  To: Matthieu Moy; +Cc: git, Johannes Schindelin

Junio C Hamano <gitster@pobox.com> writes:

> Although I would not be using it often myself, I think this
> would make "git rm" more pleasant to use.
>
> Thanks for the patch, and my thanks also go to people who
> commented on the patch.

Having said that, I think this comment is not quite right.

+		else if (!index_only) {
+			/* It's not dangerous to git-rm --cached a
+			 * file if the index matches the file or the
+			 * HEAD, since it means the deleted content is
+			 * still available somewhere.
+			 */

Personally I do not think "rm --cached" needs any such "safety",
even though I'll keep the check for now, primarily because
loosening the restriction later is always easier than adding new
restriction.  I really do not think this is about protecting the
user from "deleted content is not available anywhere else".

In this sequence:

	edit a-new-file
	git add a-new-file
        edit a-new-file
        git add a-new-file

we do not complain, even though we are *losing* the contents we
earlier staged.  If you replace the second "git add" with
"git-rm --cached", the sequence should work the same way.  In
either case, you are working towards your next commit, and most
likely are doing a partial commit (iow, your working tree does
not match any of the commit you create in the middle).  Earlier
you thought you would want one state of the file in the next
commit, but now you decided against putting that new file in the
first commit in the series.  You may make further updates to the
index and would make a commit, but after making the commit, your
working tree still has "a-new-file" and you can add the contents
from it for the later commit.

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

* Re: [PATCH] More permissive "git-rm --cached" behavior without -f.
  2007-07-14  7:16                                   ` Junio C Hamano
@ 2007-07-14 10:14                                     ` Matthieu Moy
  0 siblings, 0 replies; 37+ messages in thread
From: Matthieu Moy @ 2007-07-14 10:14 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Johannes Schindelin

Junio C Hamano <gitster@pobox.com> writes:

> Junio C Hamano <gitster@pobox.com> writes:
>
>> Although I would not be using it often myself, I think this
>> would make "git rm" more pleasant to use.
>>
>> Thanks for the patch, and my thanks also go to people who
>> commented on the patch.
>
> Having said that, I think this comment is not quite right.
>
> +		else if (!index_only) {
> +			/* It's not dangerous to git-rm --cached a
> +			 * file if the index matches the file or the
> +			 * HEAD, since it means the deleted content is
> +			 * still available somewhere.
> +			 */
>
> Personally I do not think "rm --cached" needs any such "safety",
> even though I'll keep the check for now, primarily because
> loosening the restriction later is always easier than adding new
> restriction.  I really do not think this is about protecting the
> user from "deleted content is not available anywhere else".

I agree that this is something you can argue about.

But in this case, the behavior without -f should be changed too. If
the file matches HEAD, then "git-rm file" should work, regardless of
the index then (but this situation is less frequent).

In any case, the situation where you might lose content in the index
by doing git-rm are rare: it means you started working on a file, did
"git-add" at least once, and edited the file again later, and then
decided you wanted to remove the file. So, requiring the -f flag in
those situation is not a real problem, even if the situation is
slightly-dangerous-but-not-quite-so.

I'm willing to work on another patch on top of this one if there's an
agreement on a better semantics. This one was about fixing something
which was IMHO wrong, but doesn't necessarily achieve perfection ;-).

-- 
Matthieu

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

end of thread, other threads:[~2007-07-14 10:14 UTC | newest]

Thread overview: 37+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-07-02 18:09 git-rm isn't the inverse action of git-add Christian Jaeger
2007-07-02 19:42 ` Yann Dirson
2007-07-02 20:23   ` Christian Jaeger
2007-07-02 20:40     ` Yann Dirson
2007-07-02 20:54       ` Matthieu Moy
2007-07-02 21:05         ` Johannes Schindelin
2007-07-03 10:37           ` Matthieu Moy
2007-07-03 12:09             ` Johannes Schindelin
2007-07-03 13:40               ` Matthieu Moy
2007-07-03 14:21                 ` Johannes Schindelin
2007-07-04 20:08                 ` Jan Hudec
2007-07-05 13:44                   ` Matthieu Moy
2007-07-05 14:00                     ` David Kastrup
2007-07-08 17:36                     ` [RFC][PATCH] " Matthieu Moy
2007-07-08 18:10                       ` Johannes Schindelin
2007-07-08 20:34                         ` Matthieu Moy
2007-07-08 21:49                           ` Johannes Schindelin
2007-07-09  9:45                             ` Matthieu Moy
2007-07-13 17:36                             ` Matthieu Moy
2007-07-13 17:41                               ` [PATCH] More permissive "git-rm --cached" behavior without -f Matthieu Moy
2007-07-13 17:57                                 ` Jeff King
2007-07-13 18:53                                   ` Matthieu Moy
2007-07-14  3:42                                     ` Jeff King
2007-07-14  0:44                                 ` Jakub Narebski
2007-07-14  6:52                                 ` Junio C Hamano
2007-07-14  7:16                                   ` Junio C Hamano
2007-07-14 10:14                                     ` Matthieu Moy
2007-07-02 21:20       ` git-rm isn't the inverse action of git-add Christian Jaeger
2007-07-03  4:12         ` Jeff King
2007-07-03  4:47           ` Junio C Hamano
2007-07-03  4:59             ` Jeff King
2007-07-03  5:09               ` Junio C Hamano
2007-07-03  5:12                 ` Jeff King
2007-07-03  6:26                   ` Junio C Hamano
2007-07-11 12:20     ` Jakub Narebski
2007-07-11 18:56       ` Jan Hudec
2007-07-11 21:26         ` Junio C Hamano

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