Re: [RFC/PATCH 1/2] reset: learn to reset to tree

2012-12-05 Thread Martin von Zweigbergk
On Tue, Dec 4, 2012 at 9:46 PM, Junio C Hamano gits...@pobox.com wrote:
 Martin von Zweigbergk martinv...@gmail.com writes:

 More importantly, when is it desirable not to delete deleted entries?

 When I am trying to check out contents of Documentation/ directory
 as of an older edition because we made mistakes updating the files
 in recent versions, with git checkout v1.9.0 Documentation/, for
 example.  Perhaps somebody had this bright idea of reformatting our
 docs with = Newer Style = section headers, replacing the underline
 style, and we found our toolchain depend on the underline style, or
 something.  The new files in the same directory added since v1.9.0
 may share the same mistake as the files whose recent such changes I
 am nuking with this operation, but that does not mean I want to
 retype the contents of them from scratch; I'd rather keep them
 around so that I can fix them up by hand.

I think I follow, but why, then, would you not have the save problem
with hunks *within* files that have been added in the new version? Or
is the only change to Documentation/ since the broken commit that a
new file has been added? That seems like a rather narrow use case in
that case? git checkout -p seems more generally useful (whether that
command deleted deleted files or not). It feels like I'm missing
something...

 I would have to say that it is more common; I do not recall a time I
 wanted to replace everything in a directory (and only there without
 touching other parts of the tree) with an old version, removing new
 ones.

It has happened a few times for me. I think this usually happens when
I realize that I had a better solution for some subsystem (under some
path) in some other branch (perhaps from a previous attempt at the
same problem) or an in older commit. Knowing that git checkout $rev
$path doesn't do what I expect and that git reset --hard $rev $path
is not allowed, I think I would usually do git reset $rev $path 
git checkout $path.
--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [RFC/PATCH 1/2] reset: learn to reset to tree

2012-12-04 Thread Martin von Zweigbergk
On Sat, Dec 1, 2012 at 1:24 AM, Junio C Hamano gits...@pobox.com wrote:
 Martin von Zweigbergk martinv...@gmail.com writes:

 On Thu, Nov 29, 2012 at 2:00 PM, Martin von Zweigbergk
 martinv...@gmail.com wrote:
 Slightly off topic, but another difference (or somehow another aspect
 of the same difference?) that has tripped me up a few times is that
 git checkout $rev . only affects added and modified files...

 checkout $commit pathspec has always been about ...

I suppose the has always been is meant to say that it's hard to
change at this point, not that it's more intuitive the way it works..?

 ...checking out the
 contents stored in the paths that match the pathspec from the named
 commit to the index and also o the working tree.

I think I have always thought that git checkout $commit $pathspec
would replace the section(s) of the tree defined by $pathspec. (I'm
using tree in the more general sense here, as I'm understood the
index is not stored as a tree.)

 When pathspec is dir/, it does not match the directory whose name
 is dir.  The pathspec matches the paths that store blobs under
 that directory.

Ah, right. Unlike git reset dir/, IIUC.

More importantly, when is it desirable not to delete deleted entries?
I find it much easier to imagine uses a git checkout $commit
$pathspec that does delete deleted entries. It seems like this must
have been discussed in depth before, so feel free to point me to an
old thread.

If it doesn't seem too strange to you and others if I make git reset
--hard [$commit] $pathspec work just like had expected git checkout
$commit $pathspec, I might look into that when I get some time.

 ...The please
 remove everything in dir/ part is not the job of checkout; of
 course, you can do it as a separate step (e.g. rm -fr dir/).

rm -rf dir/ would of course delete everything in there, including
e.g. build artifacts
--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [RFC/PATCH 1/2] reset: learn to reset to tree

2012-12-04 Thread Junio C Hamano
Martin von Zweigbergk martinv...@gmail.com writes:

 More importantly, when is it desirable not to delete deleted entries?

When I am trying to check out contents of Documentation/ directory
as of an older edition because we made mistakes updating the files
in recent versions, with git checkout v1.9.0 Documentation/, for
example.  Perhaps somebody had this bright idea of reformatting our
docs with = Newer Style = section headers, replacing the underline
style, and we found our toolchain depend on the underline style, or
something.  The new files in the same directory added since v1.9.0
may share the same mistake as the files whose recent such changes I
am nuking with this operation, but that does not mean I want to
retype the contents of them from scratch; I'd rather keep them
around so that I can fix them up by hand.

I would have to say that it is more common; I do not recall a time I
wanted to replace everything in a directory (and only there without
touching other parts of the tree) with an old version, removing new
ones.  git checkout [$commit] $paths is still an operation to help
me build new history forward starting from HEAD, and is not about
start building on top of the old $commit.  Losing the work I've done
to the files that did not exist in $commit:$paths is almost always
*not* what I would expect to happen with the command.



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


Re: [RFC/PATCH 1/2] reset: learn to reset to tree

2012-12-01 Thread Junio C Hamano
Martin von Zweigbergk martinv...@gmail.com writes:

 On Thu, Nov 29, 2012 at 2:00 PM, Martin von Zweigbergk
 martinv...@gmail.com wrote:
 Slightly off topic, but another difference (or somehow another aspect
 of the same difference?) that has tripped me up a few times is that
 git checkout $rev . only affects added and modified files...

checkout $commit pathspec has always been about checking out the
contents stored in the paths that match the pathspec from the named
commit to the index and also o the working tree.  checkout
pathspec is similar, but the stuff comes out of the index.

When pathspec is dir/, it does not match the directory whose name
is dir.  The pathspec matches the paths that store blobs under
that directory.

In other words, checkout dir/ (or checkout HEAD~4 dir/) has never
been about please remove everything in dir/, and then check out
everything in dir/ from the index (or from HEAD~4).  The please
remove everything in dir/ part is not the job of checkout; of
course, you can do it as a separate step (e.g. rm -fr dir/).


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


Re: [RFC/PATCH 1/2] reset: learn to reset to tree

2012-11-30 Thread Martin von Zweigbergk
On Thu, Nov 29, 2012 at 2:00 PM, Martin von Zweigbergk
martinv...@gmail.com wrote:
 Slightly off topic, but another difference (or somehow another aspect
 of the same difference?) that has tripped me up a few times is that
 git checkout $rev . only affects added and modified files (in $rev
 compared to HEAD), but git reset $rev . would also delete deleted
 files from the index.

Actually, what is the reasoning behind this difference? It almost
seems like a bug. I think I have just thought it was too obvious to be
a bug before, but thinking more about it, I can't see any reason why
git checkout $rev should delete files, but git checkout $rev .
shouldn't. I hope I'm just hallucinating or missing something. Can
someone shed some light on this?
--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[RFC/PATCH 1/2] reset: learn to reset to tree

2012-11-29 Thread Martin von Zweigbergk
In cases where HEAD is not supposed to be updated, there is no reason
that git reset should require a commit, a tree should be enough. So
make git reset $rev^{tree} work just like git reset $rev, except
that the former will not update HEAD (since there is no commit to
point it to).

Disallow --soft with trees, since that is about updating only HEAD.

By not updating HEAD, git reset $rev^{tree} behaves quite like git
reset $rev .. One might therefore consider requiring a path when
using reset with a tree to make that similarity more obvious. However,
a future commit will make git reset work on an unborn branch by
interpreting it as git reset $empty_tree and it would seem
unintuitive to the user to say git reset . on an unborn
branch. Requiring a path would also make git reset --hard $tree
disallowed.

This commit effectively undoes some of commit 13243c2 (reset: the
command takes committish, 2012-07-03). The command line argument is
now required to be an unambiguous treeish.

---

My implementation of lookup_commit_or_tree looks a little clunky. I'm
not very familiar with the API. Suggestions welcome.

Why is the HEAD is now at ... message printed only for --hard reset?
After all, HEAD is updated for all types of reset not involving paths.

 builtin/reset.c | 67 +
 t/t1512-rev-parse-disambiguation.sh |  4 ---
 t/t7102-reset.sh| 26 ++
 3 files changed, 65 insertions(+), 32 deletions(-)

diff --git a/builtin/reset.c b/builtin/reset.c
index 915cc9f..cec9874 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -225,6 +225,21 @@ static void die_if_unmerged_cache(int reset_type)
 
 }
 
+static struct object *lookup_commit_or_tree(const char *rev) {
+   unsigned char sha1[20];
+   struct commit *commit;
+   struct tree *tree;
+   if (get_sha1_treeish(rev, sha1))
+   die(_(Failed to resolve '%s' as a valid ref.), rev);
+   commit = lookup_commit_reference_gently(sha1, 1);
+   if (commit)
+   return (struct object *) commit;
+   tree = parse_tree_indirect(sha1);
+   if (tree)
+   return (struct object *) tree;
+   die(_(Could not parse object '%s'.), rev);
+}
+
 int cmd_reset(int argc, const char **argv, const char *prefix)
 {
int i = 0, reset_type = NONE, update_ref_status = 0, quiet = 0;
@@ -232,7 +247,8 @@ int cmd_reset(int argc, const char **argv, const char 
*prefix)
const char *rev = HEAD;
unsigned char sha1[20], *orig = NULL, sha1_orig[20],
*old_orig = NULL, sha1_old_orig[20];
-   struct commit *commit;
+   struct object *object;
+   struct commit *commit = NULL;
struct strbuf msg = STRBUF_INIT;
const struct option options[] = {
OPT__QUIET(quiet, N_(be quiet, only report errors)),
@@ -276,7 +292,7 @@ int cmd_reset(int argc, const char **argv, const char 
*prefix)
 * Otherwise, argv[i] could be either rev or paths and
 * has to be unambiguous.
 */
-   else if (!get_sha1_committish(argv[i], sha1)) {
+   else if (!get_sha1_treeish(argv[i], sha1)) {
/*
 * Ok, argv[i] looks like a rev; it should not
 * be a filename.
@@ -289,19 +305,12 @@ int cmd_reset(int argc, const char **argv, const char 
*prefix)
}
}
 
-   if (get_sha1_committish(rev, sha1))
-   die(_(Failed to resolve '%s' as a valid ref.), rev);
-
-   /*
-* NOTE: As git reset $treeish -- $path should be usable on
-* any tree-ish, this is not strictly correct. We are not
-* moving the HEAD to any commit; we are merely resetting the
-* entries in the index to that of a treeish.
-*/
-   commit = lookup_commit_reference(sha1);
-   if (!commit)
-   die(_(Could not parse object '%s'.), rev);
-   hashcpy(sha1, commit-object.sha1);
+   object = lookup_commit_or_tree(rev);
+   if (object-type == OBJ_COMMIT)
+   commit = (struct commit*) object;
+   else if (reset_type == SOFT)
+   die(_(--soft requires a commit, which '%s' is not), rev);
+   hashcpy(sha1, object-sha1);
 
if (patch_mode) {
if (reset_type != NONE)
@@ -347,23 +356,25 @@ int cmd_reset(int argc, const char **argv, const char 
*prefix)
die(_(Could not reset index file to revision '%s'.), 
rev);
}
 
-   /* Any resets update HEAD to the head being switched to,
-* saving the previous head in ORIG_HEAD before. */
-   if (!get_sha1(ORIG_HEAD, sha1_old_orig))
-   old_orig = sha1_old_orig;
-   if (!get_sha1(HEAD, sha1_orig)) {
-   orig = sha1_orig;
-   set_reflog_message(msg, updating ORIG_HEAD, NULL);
-   update_ref(msg.buf, ORIG_HEAD, orig, 

Re: [RFC/PATCH 1/2] reset: learn to reset to tree

2012-11-29 Thread Junio C Hamano
Martin von Zweigbergk martinv...@gmail.com writes:

 In cases where HEAD is not supposed to be updated, there is no reason
 that git reset should require a commit, a tree should be enough. So
 make git reset $rev^{tree} work just like git reset $rev, except
 that the former will not update HEAD (since there is no commit to
 point it to).

That is a horrible design I have to nack, unless you require
pathspec.  You cannot tell what git reset $sha1 would do without
checking the type of the object $sha1 refers to.  If you do this
only when pathspec is present, then the design is very reasonable.

 Disallow --soft with trees, since that is about updating only HEAD.

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


Re: [RFC/PATCH 1/2] reset: learn to reset to tree

2012-11-29 Thread Martin von Zweigbergk
On Thu, Nov 29, 2012 at 10:47 AM, Junio C Hamano gits...@pobox.com wrote:
 Martin von Zweigbergk martinv...@gmail.com writes:

 In cases where HEAD is not supposed to be updated, there is no reason
 that git reset should require a commit, a tree should be enough. So
 make git reset $rev^{tree} work just like git reset $rev, except
 that the former will not update HEAD (since there is no commit to
 point it to).

 That is a horrible design I have to nack, unless you require
 pathspec.  You cannot tell what git reset $sha1 would do without
 checking the type of the object $sha1 refers to.  If you do this
 only when pathspec is present, then the design is very reasonable.

Very good point. Thanks! I now see that git checkout also requires a
path when given a tree.

So then git reset on an unborn branch would imply git reset
$empty_tree -- . instead. And git reset --hard $tree would not be
allowed. And the intersection of these -- git reset --hard on and
unborn branch -- would also not work. Would the correct fix be to
first make git reset --hard -- $path work (*sigh*)? I have never
understood why that doesn't (shouldn't) work.
--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Re: [RFC/PATCH 1/2] reset: learn to reset to tree

2012-11-29 Thread Junio C Hamano
Junio C Hamano gits...@pobox.com writes:

 Martin von Zweigbergk martinv...@gmail.com writes:

 In cases where HEAD is not supposed to be updated, there is no reason
 that git reset should require a commit, a tree should be enough. So
 make git reset $rev^{tree} work just like git reset $rev, except
 that the former will not update HEAD (since there is no commit to
 point it to).

 That is a horrible design I have to nack, unless you require
 pathspec.  You cannot tell what git reset $sha1 would do without
 checking the type of the object $sha1 refers to.  If you do this
 only when pathspec is present, then the design is very reasonable.

The above applies to an _arbitrary_ $sha1.

Allowing reset $tree -- $pathspec is a very good addition in the
same sense that git checkout $tree -- $pathspec is useful.  These
two commands, reset and checkout, share that the source we grab
the blobs out of only need to be a tree and does not have to be a
commit, and the only difference between them is where the blobs we
grabbed out of that tree go, either only to the index or to both the
index and the working tree.

But I do not think it is connected, at least at the level the end
users perceive, to the issue of reset issued while on an unborn
branch.

If you limit the scope of the behaviour change exposed to the end
users so that you would make

$ git reset [HEAD]

act as a short-hand for

$ rm -f $GIT_DIR/index

when HEAD points at an unborn branch, and similarly make

$ git reset --hard [HEAD]

act as a short-hand for

$ rm -f $GIT_DIR/index
$ git clean -f -d

in such a case, I do not think it is unreasonable at all.

In such a case,

$ git reset --soft [HEAD]

would become just a no-op.  Earlier you were on an unborn branch,
and after reset --soft, nothing changes.

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


Re: [RFC/PATCH 1/2] reset: learn to reset to tree

2012-11-29 Thread Junio C Hamano
Martin von Zweigbergk martinv...@gmail.com writes:

 Would the correct fix be to
 first make git reset --hard -- $path work (*sigh*)? I have never
 understood why that doesn't (shouldn't) work.

What does it even mean, even when you are on an existing commit, to
hard reset partially?

Perhaps you looking for git checkout $tree -- $path?

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


Re: [RFC/PATCH 1/2] reset: learn to reset to tree

2012-11-29 Thread Martin von Zweigbergk
On Thu, Nov 29, 2012 at 11:13 AM, Junio C Hamano gits...@pobox.com wrote:
 [...]These
 two commands, reset and checkout, share that the source we grab
 the blobs out of only need to be a tree and does not have to be a
 commit, and the only difference between them is where the blobs we
 grabbed out of that tree go, either only to the index or to both the
 index and the working tree.

Slightly off topic, but another difference (or somehow another aspect
of the same difference?) that has tripped me up a few times is that
git checkout $rev . only affects added and modified files (in $rev
compared to HEAD), but git reset $rev . would also delete deleted
files from the index. I suppose this is also a partial answer to your
question in another message:

 What does it even mean, even when you are on an existing commit, to
 hard reset partially?

 Perhaps you looking for git checkout $tree -- $path?

A more direct answer would be that I would expect git reset --hard
$rev -- . to behave like git reset --hard $rev, except that it
wouldn't update HEAD. It seems to me that that would be similar to how
git reset $rev -- . behaves like git reset $rev, except that it
doesn't update HEAD. But reset and checkout with and without paths
still confuse me after years of using git, so I wouldn't be surprised
if I'm not making any sense.
--
To unsubscribe from this list: send the line unsubscribe git in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html