From: Jeff King <hidden> Date: 2016-06-15 22:51:25
This is an idea I've had for a while, but I was inspired to push it
forward a bit by Mike's command-line thread.
One problem I have seen people complain about is how "reset" and
"checkout" are somewhat overloaded to pull content into and out of the
index (since they also do things like moving HEAD or switching
branches). Those commands took the approach of saying "reset is about
changing the HEAD and possibly the index; therefore "reset -- file"
should be about pulling things from a commit into the index". And that
is certainly one way to think about it.
But another way to think about it is that commits, the index, and the
working tree are all "locations" with content. And one common operation
you may want to do is to move content from one spot to another, either
whole, by file, or by diff hunks. To a new user, knowing that "add" is
the command for moving content from thet working tree to the index does
not help them know which command to use to do the opposite content
movement.
So the "reset -- <file>" command is easily discoverable if you come at
it one way (I already know what reset does, but I want to reset just
some specific file), but not another way (I know how to move content one
way, but not the other way).
My idea is therefore to have a single command for moving content from
one location to another. You specify a source and a destination and get
a uniform interface for moving content.
A proof-of-concept patch is below. Be aware that is meant to be
illustrative and is not well tested. Also, it is a minimal presentation
of the concept. Other "locations" may also be meaningful. I'll include
some ideas below the patch.
---
Makefile | 1 +
git-put.sh | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 71 insertions(+), 0 deletions(-)
create mode 100644 git-put.sh
@@ -0,0 +1,70 @@+#!/bin/sh++SUBDIRECTORY_OK=Yes+OPTIONS_KEEPDASHASH=Yes+OPTIONS_SPEC="\+gitput[options]<from><to>[--]<file...>++Movecontentsfromoneplacetoanother,where<from>and<to>areoneof:+1.Acommit(e.g.,master,HEAD~10,v1.7.5)+2.ThespecialtokenINDEXtoindicategit'sindex.+3.ThespecialtokenWORKTREEtoindicatetheworkingdirectory.++Options:+--+pdon'tmovewholefiles;usethepatchinterface+"+.git-sh-setup++patch=+whiletest$#!=0;do+case"$1"in+-p)patch=--patch;;+--)shift;break;;+*)usage;;+esac+shift+done+test$#-lt2&&usage++from=$1;shift+to=$1;shift+test"$1"="--"&&shift++type_of(){+case"$1"in+INDEX)echoindex;;+WORKTREE)echoworktree;;+*)echocommit;;+esac+}++# Checkout contents to worktree without munging the index in+# between.+worktree_checkout(){+old=$GIT_INDEX_FILE+test-z"$old"&&old=$(gitrev-parse--git-dir)/index+new=$(gitrev-parse--git-dir)/put-index.tmp+cp"$old""$new"&&+GIT_INDEX_FILE=$newgitcheckout"$@"+status=$?+rm-f"$new"+exit$status+}++case"$(type_of"$from"),$(type_of"$to")"in+*,commit)+die"You can't modify an existing commit.";;+index,index)+die"You can't move content from the index on top of itself.";;+worktree,index)+execgitadd$patch--"$@";;+commit,index)+execgitreset$patch"$from"--"$@";;+index,worktree)+execgitcheckout$patch--"$@";;+worktree,worktree)+die"You can't move content in the worktree on top of itself.";;+commit,worktree)+worktree_checkout$patch"$from"--"$@";;+esac
As you can see, this handles only three typoes of locations: the
worktree, the index, and an arbitrary commit (really a tree-ish). Some
other types I've thought of are:
- stashes; you can already use stashes a source with "stash@{0}". They
could also be a destination, chaining to "git stash".
- branches as destinations; obviously we can't change an existing
commit, but what about something like:
git put WORKTREE BRANCH:foo
to optionally create a new branch "refs/heads/foo" based on the
current HEAD, push changes into a temporary index that matches its
tip, and then making a new commit based on top.
This would serve a similar purpose to stashes, except that they
would be named and could operate as full branches. I would find it
useful for picking apart a mass of worktree changes into discrete
commits.
- allow multiple destinations, like
# equivalent to "git checkout --"
git put HEAD INDEX,WORKTREE
- blobs as locations. We could allow something like:
git put v1.7.5:Makefile WORKTREE:Makefile
which would be equivalent to
git put v1.7.5 WORKTREE -- Makefile
but sometimes matches the user's mental model better. It also allows
pulling blobs from index stages, like:
# Resolve in favor of "ours"
git put :2:Makefile INDEX,WORKTREE
- subtrees as locations. This allows a form of renaming between old
versions.
git put gitgui-0.10.0: WORKTREE:git-gui
I hope it's obvious from what I wrote above and from the implementation,
but this would not be _replacing_ other commands, but would just be
another way of looking at them. By having more than one way to do the
same thing, it helps people discover the way that fits their mental
model most appropriately. Of course, it may also just introduce insane
confusion.
Let the flaming begin.
-Peff
On Wed, Jun 8, 2011 at 3:06 AM, Jeff King [off-list ref] wrote:
But another way to think about it is that commits, the index, and the
working tree are all "locations" with content. And one common operation
you may want to do is to move content from one spot to another, either
whole, by file, or by diff hunks.
Also "git put --pretend" or "git put --dry-run" may show the diff of
content movement, without actual moving. "--dry-run --patch"
combination probably does not make sense though.
As you can see, this handles only three typoes of locations: the
worktree, the index, and an arbitrary commit (really a tree-ish). Some
other types I've thought of are:
....
I find it intuitive (given a source tree and destination one(s), you
can copy content by paths or even by hunks), until you give it more
powers (creating new branch or commit, move subdirs...). The original
git-put idea could reduce a lot of confusion for new users. Your extra
types seem uncommon to me (or can be well covered with current
commands without much confusion). For advanced use cases, maybe the
current command set can be enhanced with new options.
--
Duy
(Bringing up an old thread)
On Wed, Jun 8, 2011 at 3:06 AM, Jeff King [off-list ref] wrote:
quoted hunk
...
But another way to think about it is that commits, the index, and the
working tree are all "locations" with content. And one common operation
you may want to do is to move content from one spot to another, either
whole, by file, or by diff hunks. To a new user, knowing that "add" is
the command for moving content from thet working tree to the index does
not help them know which command to use to do the opposite content
movement.
...
My idea is therefore to have a single command for moving content from
one location to another. You specify a source and a destination and get
a uniform interface for moving content.
A proof-of-concept patch is below. Be aware that is meant to be
illustrative and is not well tested. Also, it is a minimal presentation
of the concept. Other "locations" may also be meaningful. I'll include
some ideas below the patch.
---
Makefile | 1 +
git-put.sh | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 71 insertions(+), 0 deletions(-)
create mode 100644 git-put.sh
@@ -0,0 +1,70 @@+#!/bin/sh++SUBDIRECTORY_OK=Yes+OPTIONS_KEEPDASHASH=Yes+OPTIONS_SPEC="\+gitput[options]<from><to>[--]<file...>++Movecontentsfromoneplacetoanother,where<from>and<to>areoneof:+1.Acommit(e.g.,master,HEAD~10,v1.7.5)+2.ThespecialtokenINDEXtoindicategit'sindex.+3.ThespecialtokenWORKTREEtoindicatetheworkingdirectory.++Options:+--+pdon'tmovewholefiles;usethepatchinterface+"+.git-sh-setup++patch=+whiletest$#!=0;do+case"$1"in+-p)patch=--patch;;+--)shift;break;;+*)usage;;+esac+shift+done+test$#-lt2&&usage++from=$1;shift+to=$1;shift+test"$1"="--"&&shift++type_of(){+case"$1"in+INDEX)echoindex;;+WORKTREE)echoworktree;;+*)echocommit;;+esac+}++# Checkout contents to worktree without munging the index in+# between.+worktree_checkout(){+old=$GIT_INDEX_FILE+test-z"$old"&&old=$(gitrev-parse--git-dir)/index+new=$(gitrev-parse--git-dir)/put-index.tmp+cp"$old""$new"&&+GIT_INDEX_FILE=$newgitcheckout"$@"+status=$?+rm-f"$new"+exit$status+}++case"$(type_of"$from"),$(type_of"$to")"in+*,commit)+die"You can't modify an existing commit.";;+index,index)+die"You can't move content from the index on top of itself.";;+worktree,index)+execgitadd$patch--"$@";;+commit,index)+execgitreset$patch"$from"--"$@";;+index,worktree)+execgitcheckout$patch--"$@";;+worktree,worktree)+die"You can't move content in the worktree on top of itself.";;+commit,worktree)+worktree_checkout$patch"$from"--"$@";;+esac
As you can see, this handles only three typoes of locations: the
worktree, the index, and an arbitrary commit (really a tree-ish).
Last time we were stuck at the magic keywords INDEX and WORKTREE. What
if we sort of follow scp naming convention:
- Normal paths are working tree's paths
- Paths with a colon in it are in "remote" locations (index or a
tree). The part before colon specifies the location.
We could have:
git put <src> [<src>...] <dst>
git put <src> [<src>...] <dst> -- <pathspec>
Where <src> and <dst> could be
- <tree-ish> <colon> [<pathspec>]
- [0-3] <colon> [<pathspec>]
- <pathspec> (or plain path)
In the first form, pathspec could be specified in <src>. If <dst> is
worktree, then "." would be enough (or path to repo's root to be more
strict). In the second form, no pathspec can be part of <src> nor
<dst> because they're at the end already.
With this syntax we could have:
git put 0:path/to/file.c . (or git put 0: path/to/file.c)
-> copy file.c from index to worktree (at the same path "path/to/file.c")
git put path/to/file 0:
-> copy file to index
git put HEAD: . -- path/
-> checkout everything in path/ from HEAD
I'm not sure how mutiple <src> should work, but there may be a use case for it.
Some other types I've thought of are:
...
- branches as destinations; obviously we can't change an existing
commit, but what about something like:
git put WORKTREE BRANCH:foo
to optionally create a new branch "refs/heads/foo" based on the
current HEAD, push changes into a temporary index that matches its
tip, and then making a new commit based on top.
This would serve a similar purpose to stashes, except that they
would be named and could operate as full branches. I would find it
useful for picking apart a mass of worktree changes into discrete
commits.
- allow multiple destinations, like
# equivalent to "git checkout --"
git put HEAD INDEX,WORKTREE
These obviously do not work with the syntax I propose.
--
Duy
From: Michael Nahas <hidden> Date: 2016-06-15 22:52:51
On Mon, Jan 23, 2012 at 5:32 AM, Nguyen Thai Ngoc Duy [off-list ref] wrote:
(Bringing up an old thread)
"...thank you so much for bringing up such a painful subject. While
you're at it, why don't you give me a nice paper cut and pour lemon
juice on it."
"git put" is "git cp". It copies from one filesystem (or a snapshot
of a filesystem) to another filesystem.
Without multiple working directories, a modifiable "stash", or a
(useful) name for the filesystem referred to as
"index"/"cache"/"staging area", there is only one filesystem that the
command can write to: the (singular) working directory.
So, "git put <src filesystem> -- <path>" is fine. It will copy from
the path in the src filesystem to the path in the current working
directory. I don't think the command "put" is a great name for that.
Since we already have some strange double-usage commands like "git
checkout --" and "git reset --", perhaps this should be "git
cherry-pick --".
<rant>
But for my money, "git cp" is clearer and I'd love to get rid of the
user-confusing double-usage commands. I'd replace "git checkout --"
with "git cp NEXT WTREE -- <path>" and replace "git reset --" with
"git cp HEAD NEXT --" where NEXT is the filesystem represented by the
"index"/"cache"/"staging area" and WTREE is an alias for the working
directory.
</rant>
Still, good luck. It's a useful addition even if it is "git cherry-pick --".
Mike
On Wed, Jun 8, 2011 at 3:06 AM, Jeff King [off-list ref] wrote:
quoted
...
But another way to think about it is that commits, the index, and the
working tree are all "locations" with content. And one common operation
you may want to do is to move content from one spot to another, either
whole, by file, or by diff hunks. To a new user, knowing that "add" is
the command for moving content from thet working tree to the index does
not help them know which command to use to do the opposite content
movement.
...
My idea is therefore to have a single command for moving content from
one location to another. You specify a source and a destination and get
a uniform interface for moving content.
A proof-of-concept patch is below. Be aware that is meant to be
illustrative and is not well tested. Also, it is a minimal presentation
of the concept. Other "locations" may also be meaningful. I'll include
some ideas below the patch.
---
Makefile | 1 +
git-put.sh | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 71 insertions(+), 0 deletions(-)
create mode 100644 git-put.sh
@@ -0,0 +1,70 @@+#!/bin/sh++SUBDIRECTORY_OK=Yes+OPTIONS_KEEPDASHASH=Yes+OPTIONS_SPEC="\+gitput[options]<from><to>[--]<file...>++Movecontentsfromoneplacetoanother,where<from>and<to>areoneof:+1.Acommit(e.g.,master,HEAD~10,v1.7.5)+2.ThespecialtokenINDEXtoindicategit'sindex.+3.ThespecialtokenWORKTREEtoindicatetheworkingdirectory.++Options:+--+pdon'tmovewholefiles;usethepatchinterface+"+.git-sh-setup++patch=+whiletest$#!=0;do+case"$1"in+-p)patch=--patch;;+--)shift;break;;+*)usage;;+esac+shift+done+test$#-lt2&&usage++from=$1;shift+to=$1;shift+test"$1"="--"&&shift++type_of(){+case"$1"in+INDEX)echoindex;;+WORKTREE)echoworktree;;+*)echocommit;;+esac+}++# Checkout contents to worktree without munging the index in+# between.+worktree_checkout(){+old=$GIT_INDEX_FILE+test-z"$old"&&old=$(gitrev-parse--git-dir)/index+new=$(gitrev-parse--git-dir)/put-index.tmp+cp"$old""$new"&&+GIT_INDEX_FILE=$newgitcheckout"$@"+status=$?+rm-f"$new"+exit$status+}++case"$(type_of"$from"),$(type_of"$to")"in+*,commit)+die"You can't modify an existing commit.";;+index,index)+die"You can't move content from the index on top of itself.";;+worktree,index)+execgitadd$patch--"$@";;+commit,index)+execgitreset$patch"$from"--"$@";;+index,worktree)+execgitcheckout$patch--"$@";;+worktree,worktree)+die"You can't move content in the worktree on top of itself.";;+commit,worktree)+worktree_checkout$patch"$from"--"$@";;+esac
As you can see, this handles only three typoes of locations: the
worktree, the index, and an arbitrary commit (really a tree-ish).
Last time we were stuck at the magic keywords INDEX and WORKTREE. What
if we sort of follow scp naming convention:
- Normal paths are working tree's paths
- Paths with a colon in it are in "remote" locations (index or a
tree). The part before colon specifies the location.
We could have:
git put <src> [<src>...] <dst>
git put <src> [<src>...] <dst> -- <pathspec>
Where <src> and <dst> could be
- <tree-ish> <colon> [<pathspec>]
- [0-3] <colon> [<pathspec>]
- <pathspec> (or plain path)
In the first form, pathspec could be specified in <src>. If <dst> is
worktree, then "." would be enough (or path to repo's root to be more
strict). In the second form, no pathspec can be part of <src> nor
<dst> because they're at the end already.
With this syntax we could have:
git put 0:path/to/file.c . (or git put 0: path/to/file.c)
-> copy file.c from index to worktree (at the same path "path/to/file.c")
git put path/to/file 0:
-> copy file to index
git put HEAD: . -- path/
-> checkout everything in path/ from HEAD
I'm not sure how mutiple <src> should work, but there may be a use case for it.
quoted
Some other types I've thought of are:
...
- branches as destinations; obviously we can't change an existing
commit, but what about something like:
git put WORKTREE BRANCH:foo
to optionally create a new branch "refs/heads/foo" based on the
current HEAD, push changes into a temporary index that matches its
tip, and then making a new commit based on top.
This would serve a similar purpose to stashes, except that they
would be named and could operate as full branches. I would find it
useful for picking apart a mass of worktree changes into discrete
commits.
- allow multiple destinations, like
# equivalent to "git checkout --"
git put HEAD INDEX,WORKTREE
These obviously do not work with the syntax I propose.
--
Duy
On Mon, Jan 23, 2012 at 8:53 PM, Michael Nahas [off-list ref] wrote:
"git put" is "git cp". It copies from one filesystem (or a snapshot
of a filesystem) to another filesystem.
Exactly.
Without multiple working directories, a modifiable "stash", or a
(useful) name for the filesystem referred to as
"index"/"cache"/"staging area", there is only one filesystem that the
command can write to: the (singular) working directory.
No there are two writable "filesystems": working directory and
"index/cache/staging area"
So, "git put <src filesystem> -- <path>" is fine. It will copy from
the path in the src filesystem to the path in the current working
directory. I don't think the command "put" is a great name for that.
Since we already have some strange double-usage commands like "git
checkout --" and "git reset --", perhaps this should be "git
cherry-pick --".
The "-- <path>" thing may save you a few keystrokes when you want to
copy from more than one path(spec). The two below commands are
equivalent
git put HEAD:a/ HEAD/b/ HEAD/c/ .
git put HEAD: . -- a/ b/ c/
But of course if you just need to copy from one pathspec to another
place, "--" syntax is redundant.
<rant>
But for my money, "git cp" is clearer and I'd love to get rid of the
user-confusing double-usage commands. I'd replace "git checkout --"
with "git cp NEXT WTREE -- <path>" and replace "git reset --" with
"git cp HEAD NEXT --" where NEXT is the filesystem represented by the
"index"/"cache"/"staging area" and WTREE is an alias for the working
directory.
</rant>
I thought of "cp" (naturally, I was driven by "scp" syntax as I said)
and maybe if we think this through, we may be able to enhance cp to
support "remote locations" (and --patch option). So "put" vs "cp" is
not important to me now. What I'd like to hear is whether the syntax
makes sense.
My "hidden" plan if this works out would be to deprecate (or
discourage) everything in git-checkout except branch switching. I
don't have anything against git-reset. It's a kind of dangerous
command from the start (while git-checkout is more user friendly) and
can stay that way. The new "git <cp, put or whatever name>" should
fill 90% the needs for git-reset.
--
Duy
From: Michael Nahas <hidden> Date: 2016-06-15 22:52:51
Hi Duy,
I've contributed no code to git. I've come up with plenty of ideas,
which seem to have gotten little traction.
Your ideas are similar to mine (and others), but the last attempt to
get them into git did not accomplish anything. I don't know how much
work you have done on git, but before participating with git again, I
suggest you look at why the last attempt failed and we ask an
experienced person how things work.
It obviously isn't the design-first-then-find-a-willing-programmer of
the project I ran. I don't know if it's the IETF's "running code and
a general consensus". The only thing I've found is that people did
not want to discuss theory. (I believe the feeling is that theory is
only worthy of DARCS.) I also got the feeling that improving the user
interface (e.g., replacing "git checkout --" and "git reset --") was
not a priority.
So, please plan out a strategy before recruiting me to help push this
idea forward.
Mike
On Mon, Jan 23, 2012 at 9:35 AM, Nguyen Thai Ngoc Duy [off-list ref] wrote:
On Mon, Jan 23, 2012 at 8:53 PM, Michael Nahas [off-list ref] wrote:
quoted
"git put" is "git cp". It copies from one filesystem (or a snapshot
of a filesystem) to another filesystem.
Exactly.
quoted
Without multiple working directories, a modifiable "stash", or a
(useful) name for the filesystem referred to as
"index"/"cache"/"staging area", there is only one filesystem that the
command can write to: the (singular) working directory.
No there are two writable "filesystems": working directory and
"index/cache/staging area"
quoted
So, "git put <src filesystem> -- <path>" is fine. It will copy from
the path in the src filesystem to the path in the current working
directory. I don't think the command "put" is a great name for that.
Since we already have some strange double-usage commands like "git
checkout --" and "git reset --", perhaps this should be "git
cherry-pick --".
The "-- <path>" thing may save you a few keystrokes when you want to
copy from more than one path(spec). The two below commands are
equivalent
git put HEAD:a/ HEAD/b/ HEAD/c/ .
git put HEAD: . -- a/ b/ c/
But of course if you just need to copy from one pathspec to another
place, "--" syntax is redundant.
quoted
<rant>
But for my money, "git cp" is clearer and I'd love to get rid of the
user-confusing double-usage commands. I'd replace "git checkout --"
with "git cp NEXT WTREE -- <path>" and replace "git reset --" with
"git cp HEAD NEXT --" where NEXT is the filesystem represented by the
"index"/"cache"/"staging area" and WTREE is an alias for the working
directory.
</rant>
I thought of "cp" (naturally, I was driven by "scp" syntax as I said)
and maybe if we think this through, we may be able to enhance cp to
support "remote locations" (and --patch option). So "put" vs "cp" is
not important to me now. What I'd like to hear is whether the syntax
makes sense.
My "hidden" plan if this works out would be to deprecate (or
discourage) everything in git-checkout except branch switching. I
don't have anything against git-reset. It's a kind of dangerous
command from the start (while git-checkout is more user friendly) and
can stay that way. The new "git <cp, put or whatever name>" should
fill 90% the needs for git-reset.
--
Duy