Re: [PATCH/RFC] commit: new option to abort -a something is already staged

2018-08-24 Thread Jacob Keller
On Fri, Aug 24, 2018 at 7:42 AM Duy Nguyen  wrote:
>
> On Fri, Aug 24, 2018 at 5:02 AM Jacob Keller  wrote:
> >
> > On Thu, Aug 23, 2018 at 9:28 AM Junio C Hamano  wrote:
> > > I think the above example forgets "-a" on the final "git commit"
> > > step.  With it added, I can understand the concern (and I am sure
> > > you would, too).
> > >
> > > The user is trying to add everything done in the working tree, and
> > > "commit -a" would catch all changes to paths that were already
> > > tracked, but a separate "add" is necessary for newly created paths.
> > > But adding a new path means the index no longer matches HEAD, and
> > > the "commit -a" at the final step sweeps the changes to already
> > > tracked paths---failing that because there "already is something
> > > staged" will break the workflow.
> >
> > Right. I think this would need to be able to understand the case of
> > "different only by new files".
>
> OK so the rules I'm going to try to implement is, if the  version in
> the index is not the same as one in HEAD or one in worktree, then "git
> commit -a" fails.
>
> The unwritten line is, if the path in question does not exist in HEAD,
> then the first condition "as one in HEAD" is dropped, naturally. This
> addresses the "git add new-file" problem, but if you have made more
> changes in new-file in worktree after "git add" and do "git commit -a"
> then you still get rejected because it fails the second condition.
>
> File removal should be considered as well. But I don't foresee any
> problem there. Resolving merges, replacing higher stage entries with
> stage 0 will be accepted at "git commit -a" as usual.
>
> Let me know if we should tweak these rules (and how).
> --
> Duy

This seems reasonable to me. It might trigger a few issues with people
doing "git add new_file ; $EDITOR new_file ; git commit -a" but... I
know I've accidentally done "git commit -a" and had problems.

What about "git commit "? That's another case where I've
accidentally lost some work for a similar reason.

Thanks,
Jake


Re: [PATCH/RFC] commit: new option to abort -a something is already staged

2018-08-24 Thread Duy Nguyen
On Fri, Aug 24, 2018 at 5:02 AM Jacob Keller  wrote:
>
> On Thu, Aug 23, 2018 at 9:28 AM Junio C Hamano  wrote:
> > I think the above example forgets "-a" on the final "git commit"
> > step.  With it added, I can understand the concern (and I am sure
> > you would, too).
> >
> > The user is trying to add everything done in the working tree, and
> > "commit -a" would catch all changes to paths that were already
> > tracked, but a separate "add" is necessary for newly created paths.
> > But adding a new path means the index no longer matches HEAD, and
> > the "commit -a" at the final step sweeps the changes to already
> > tracked paths---failing that because there "already is something
> > staged" will break the workflow.
>
> Right. I think this would need to be able to understand the case of
> "different only by new files".

OK so the rules I'm going to try to implement is, if the  version in
the index is not the same as one in HEAD or one in worktree, then "git
commit -a" fails.

The unwritten line is, if the path in question does not exist in HEAD,
then the first condition "as one in HEAD" is dropped, naturally. This
addresses the "git add new-file" problem, but if you have made more
changes in new-file in worktree after "git add" and do "git commit -a"
then you still get rejected because it fails the second condition.

File removal should be considered as well. But I don't foresee any
problem there. Resolving merges, replacing higher stage entries with
stage 0 will be accepted at "git commit -a" as usual.

Let me know if we should tweak these rules (and how).
-- 
Duy


Re: [PATCH/RFC] commit: new option to abort -a something is already staged

2018-08-23 Thread Jacob Keller
On Thu, Aug 23, 2018 at 9:28 AM Junio C Hamano  wrote:
> I think the above example forgets "-a" on the final "git commit"
> step.  With it added, I can understand the concern (and I am sure
> you would, too).
>
> The user is trying to add everything done in the working tree, and
> "commit -a" would catch all changes to paths that were already
> tracked, but a separate "add" is necessary for newly created paths.
> But adding a new path means the index no longer matches HEAD, and
> the "commit -a" at the final step sweeps the changes to already
> tracked paths---failing that because there "already is something
> staged" will break the workflow.

Right. I think this would need to be able to understand the case of
"different only by new files".

Thanks,
Jake


Re: [PATCH/RFC] commit: new option to abort -a something is already staged

2018-08-23 Thread Jacob Keller
On Mon, Aug 20, 2018 at 8:43 AM Nguyễn Thái Ngọc Duy  wrote:
> Instead, let's handle just this problem for now. This new option
> commit.preciousDirtyIndex, if set to false, will not allow `commit -a`
> to continue if the final index is different from the existing one. I
> don't think this can be achieved with hooks because the hooks don't
> know about "-a" or different commit modes.
>

Here you say setting it to "false" enables this check but...

> +commit.preciousDirtyIndex::
> +   If some changes are partially staged in the index (i.e.
> +   "git commit -a" and "git commit" commit different changes), reject
> +   "git commit -a".
> +

Here, sounds like it is enabled by setting it to true.

Thanks,
Jake


Re: [PATCH/RFC] commit: new option to abort -a something is already staged

2018-08-23 Thread Junio C Hamano
Duy Nguyen  writes:

> On Thu, Aug 23, 2018 at 4:15 AM Jonathan Nieder  wrote:
>> > The remaining question becomes scripts.  A script might do
>> >
>> >   ... modify old-file and create new-file ...
>> >   git add new-file
>> >   git commit -m "some great message"
>> >
>> > which would trip this error.  For that matter, humans might do that,
>> > too.  Could the check detect this case (where the only changes in the
>> > index are additions of new files) and treat it as non-destructive?
>>
>> (where the only changes in the index are additions of new files *that
>> match the worktree*)
>
> I don't think my change would affect this case. If "git commit" does
> not change the index by itself, there's no point in stopping it.

I think the above example forgets "-a" on the final "git commit"
step.  With it added, I can understand the concern (and I am sure
you would, too).

The user is trying to add everything done in the working tree, and
"commit -a" would catch all changes to paths that were already
tracked, but a separate "add" is necessary for newly created paths.
But adding a new path means the index no longer matches HEAD, and
the "commit -a" at the final step sweeps the changes to already
tracked paths---failing that because there "already is something
staged" will break the workflow.


Re: [PATCH/RFC] commit: new option to abort -a something is already staged

2018-08-23 Thread Duy Nguyen
On Thu, Aug 23, 2018 at 4:15 AM Jonathan Nieder  wrote:
> > The remaining question becomes scripts.  A script might do
> >
> >   ... modify old-file and create new-file ...
> >   git add new-file
> >   git commit -m "some great message"
> >
> > which would trip this error.  For that matter, humans might do that,
> > too.  Could the check detect this case (where the only changes in the
> > index are additions of new files) and treat it as non-destructive?
>
> (where the only changes in the index are additions of new files *that
> match the worktree*)

I don't think my change would affect this case. If "git commit" does
not change the index by itself, there's no point in stopping it.
-- 
Duy


Re: [PATCH/RFC] commit: new option to abort -a something is already staged

2018-08-22 Thread Jonathan Nieder
A few quick corrections:

Jonathan Nieder wrote:

> I'm starting to lean toward having this on unconditionally, with a
> message that points the user who really doesn't want to clobber their

... who really *does* want to clobber their index ...

> index toward "git add -u", as a good idea.  I think that for humans,
> that would be okay and that configuration doesn't really help much
> for this.
>
> The remaining question becomes scripts.  A script might do
>
>   ... modify old-file and create new-file ...
>   git add new-file
>   git commit -m "some great message"
>
> which would trip this error.  For that matter, humans might do that,
> too.  Could the check detect this case (where the only changes in the
> index are additions of new files) and treat it as non-destructive?

(where the only changes in the index are additions of new files *that
match the worktree*)

Sorry for the noise,
Jonathan


Re: [PATCH/RFC] commit: new option to abort -a something is already staged

2018-08-22 Thread Jonathan Nieder
Duy Nguyen wrote:
> On Mon, Aug 20, 2018 at 9:30 PM Jonathan Nieder  wrote:

>> I frequently use "git commit -a" this way intentionally, so I would be
>> unlikely to turn this config on.  That leads me to suspect it's not a
>> good candidate for configuration:
>>
>> - it's not configuration for the sake of a transition period, since some
>>   people would keep it on forever
>>
>> - it's not configuration based on different project needs, either
>>
>> So configuration doesn't feel like a good fit.
>
> I think it falls under personal preference (yes some people like me
> will keep it on forever in fear of losing staged changes).

Sorry for the lack of clarity.  I meant "some people would keep it off
forever".

>> That said, I lean toward your initial thought, that this is papering
>> over a missing undo feature.  Can you say more about how you'd imagine
>> undo working?
[...]
> [1] 
> https://public-inbox.org/git/1375597720-13236-1-git-send-email-pclo...@gmail.com/
> [2] 
> https://public-inbox.org/git/1375966270-10968-1-git-send-email-pclo...@gmail.com/

Thanks for the links!  That's very helpful.

I'm starting to lean toward having this on unconditionally, with a
message that points the user who really doesn't want to clobber their
index toward "git add -u", as a good idea.  I think that for humans,
that would be okay and that configuration doesn't really help much
for this.

The remaining question becomes scripts.  A script might do

... modify old-file and create new-file ...
git add new-file
git commit -m "some great message"

which would trip this error.  For that matter, humans might do that,
too.  Could the check detect this case (where the only changes in the
index are additions of new files) and treat it as non-destructive?

Thanks,
Jonathan


Re: [PATCH/RFC] commit: new option to abort -a something is already staged

2018-08-21 Thread Duy Nguyen
On Mon, Aug 20, 2018 at 9:30 PM Jonathan Nieder  wrote:
>
> Hi,
>
> Nguyễn Thái Ngọc Duy wrote:
>
> > So many times I have carefully cherry picked changes to the index with
> > `git add -p` then accidentally did `git commit -am ` (usually by
> > retrieving a command from history and pressing Enter too quickly)
> > which destroyed beautiful index.
> >
> > One way to deal with this is some form of `git undo` that allows me to
> > retrieve the old index.
>
> Yes, I would love such an undo feature!
>
> How would you imagine that this information would get stored?  We can
> start with adding that and a low-level (plumbing) interface to it, to
> avoid being blocked on getting the user-facing (porcelain) "git undo"
> interface right.  (Or we can go straight for the porcelain.  It's fine
> for it to start minimal and gain switches later.)

Yeah I'd love to see "git undo" too, but unfortunately I don't have a
clear model how it should operate. Which is why I still hesitate to
implement one. :P

All I have is something close to how undo is done in an editor, where
we could save a list of actions and corresponding undo ones, but
editors are wysiwyg and we can't just let the user undo, see the
result elsewhere and redo if they're not happy.

> > Instead, let's handle just this problem for now. This new option
> > commit.preciousDirtyIndex, if set to false, will not allow `commit -a`
> > to continue if the final index is different from the existing one. I
> > don't think this can be achieved with hooks because the hooks don't
> > know about "-a" or different commit modes.
>
> I frequently use "git commit -a" this way intentionally, so I would be
> unlikely to turn this config on.  That leads me to suspect it's not a
> good candidate for configuration:
>
> - it's not configuration for the sake of a transition period, since some
>   people would keep it on forever
>
> - it's not configuration based on different project needs, either
>
> So configuration doesn't feel like a good fit.

I think it falls under personal preference (yes some people like me
will keep it on forever in fear of losing staged changes).

> That said, I lean toward your initial thought, that this is papering
> over a missing undo feature.  Can you say more about how you'd imagine
> undo working?

There is not much to say. But at least to be able to undo changes in
the index, I made two attempts in the past [1] [2] (and forgot about
them until I got bitten by the lack of undo again this time). I guess
I could push for one of those approaches again because it will help me
and also lay the foundation for any git-undo in the future.

Once we have can restore index, undoing "git reset --hard" is also
possible (by hashing everything and putting them in a temporary index
first).

[1] 
https://public-inbox.org/git/1375597720-13236-1-git-send-email-pclo...@gmail.com/
[2] 
https://public-inbox.org/git/1375966270-10968-1-git-send-email-pclo...@gmail.com/
-- 
Duy


Re: [PATCH/RFC] commit: new option to abort -a something is already staged

2018-08-20 Thread Jonathan Nieder
Hi,

Nguyễn Thái Ngọc Duy wrote:

> So many times I have carefully cherry picked changes to the index with
> `git add -p` then accidentally did `git commit -am ` (usually by
> retrieving a command from history and pressing Enter too quickly)
> which destroyed beautiful index.
>
> One way to deal with this is some form of `git undo` that allows me to
> retrieve the old index.

Yes, I would love such an undo feature!

How would you imagine that this information would get stored?  We can
start with adding that and a low-level (plumbing) interface to it, to
avoid being blocked on getting the user-facing (porcelain) "git undo"
interface right.  (Or we can go straight for the porcelain.  It's fine
for it to start minimal and gain switches later.)

[...]
> Instead, let's handle just this problem for now. This new option
> commit.preciousDirtyIndex, if set to false, will not allow `commit -a`
> to continue if the final index is different from the existing one. I
> don't think this can be achieved with hooks because the hooks don't
> know about "-a" or different commit modes.

I frequently use "git commit -a" this way intentionally, so I would be
unlikely to turn this config on.  That leads me to suspect it's not a
good candidate for configuration:

- it's not configuration for the sake of a transition period, since some
  people would keep it on forever

- it's not configuration based on different project needs, either

So configuration doesn't feel like a good fit.

It might be that I can switch my muscle memory to "git add -u && git
commit", in which case this could work as a new default without
configuration (or with configuration for a transition period).  A
separate commandline option "git commit -a --no-clobber-index" might
make sense as well, since then I could pass --clobber-index to keep my
current workflow.  That would also follow the usual progression:
introduce commandline option first, then config, then change default.

That said, I lean toward your initial thought, that this is papering
over a missing undo feature.  Can you say more about how you'd imagine
undo working?

Thanks,
Jonathan


Re: [PATCH/RFC] commit: new option to abort -a something is already staged

2018-08-20 Thread Eric Sunshine
On Mon, Aug 20, 2018 at 11:41 AM Nguyễn Thái Ngọc Duy  wrote:
> One way to deal with this is some form of `git undo` that allows me to
> retrieve the old index. That's not a lot of work by itself. The problem
> is designing that `git undo` interface because there are more undo
> options that this.

s/that/than/


Re: [PATCH/RFC] commit: new option to abort -a something is already staged

2018-08-20 Thread Junio C Hamano
Nguyễn Thái Ngọc Duy   writes:

> +commit.preciousDirtyIndex::
> + If some changes are partially staged in the index (i.e.
> + "git commit -a" and "git commit" commit different changes), reject
> + "git commit -a".

Or perhaps better yet, go into yes/no interaction to confirm if you
have access to interactive terminal at fd #0/#1, perhaps?

> diff --git a/builtin/commit.c b/builtin/commit.c
> index 213fca2d8e..489e4b9f50 100644
> --- a/builtin/commit.c
> +++ b/builtin/commit.c
> @@ -98,6 +98,7 @@ static const char *author_message, *author_message_buffer;
>  static char *edit_message, *use_message;
>  static char *fixup_message, *squash_message;
>  static int all, also, interactive, patch_interactive, only, amend, signoff;
> +static int allow_dirty_index = 1;
>  static int edit_flag = -1; /* unspecified */
>  static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
>  static int config_commit_verbose = -1; /* unspecified */
> @@ -385,10 +386,24 @@ static const char *prepare_index(int argc, const char 
> **argv, const char *prefix
>* (B) on failure, rollback the real index.
>*/
>   if (all || (also && pathspec.nr)) {
> + int compare_oid = all && !allow_dirty_index;
> + struct object_id previous_oid;
> +
> + if (compare_oid) {
> + if (update_main_cache_tree(0) || !the_index.cache_tree)
> + die(_("error building trees"));

Hmph, when we conclude a conflicted merge with "commit -a", wouldn't
we fail to compute the "before" picture, with higher-stage entries
stil in the index?

> + if (the_index.cache_tree->entry_count >= 0)
> + oidcpy(_oid, 
> _index.cache_tree->oid);
> + else
> + oidclr(_oid);

The cache-tree covers no entry, meaning the index has no cache
entries?  Shouldn't we setting EMPTY_TREE_SHA1_BIN or something
instead?

> + }
>   hold_locked_index(_lock, LOCK_DIE_ON_ERROR);
>   add_files_to_cache(also ? prefix : NULL, , 0);
>   refresh_cache_or_die(refresh_flags);
>   update_main_cache_tree(WRITE_TREE_SILENT);
> + if (compare_oid && the_index.cache_tree &&
> + oidcmp(_oid, _index.cache_tree->oid))
> + die(_("staged content is different, aborting"));

I was hoping that it is an easy change to teach add_files_to_cache()
to report how many paths it actually has "added" (modifications and
removals are of course also counted), which allows us not to waste
computing a throw-away tree object once more.

There only are three existing callers to the function, and only one
of them rely on the current "non-zero is error, zero is good"
semantics, so updating that caller (and perhaps vetting the other
callers to see if they also _should_ pay attention to the return
value, at least to detect errors of not number of paths updated)
shouldn't be that much more effort, and would be a good update to
the API regardless of this new feature, I would think.

>   if (write_locked_index(_index, _lock, 0))
>   die(_("unable to write new_index file"));
>   commit_style = COMMIT_NORMAL;
> @@ -1413,6 +1428,10 @@ static int git_commit_config(const char *k, const char 
> *v, void *cb)
>   config_commit_verbose = git_config_bool_or_int(k, v, _bool);
>   return 0;
>   }
> + if (!strcmp(k, "commit.preciousdirtyindex")) {
> + allow_dirty_index = !git_config_bool(k, v);
> + return 0;
> + }
>  
>   status = git_gpg_config(k, v, NULL);
>   if (status)