From: Michael Nahas Date: Mon, 23 Jan 2012 13:53:23 GMT Subject: Re: [RFC/PATCH] git put: an alternative to add/reset/checkout Message-ID: In-Reply-To: On Mon, Jan 23, 2012 at 5:32 AM, Nguyen Thai Ngoc Duy 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 -- " 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 --". 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 -- " 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. 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 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. 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 > > > > diff --git a/Makefile b/Makefile > > index e40ac0c..4564506 100644 > > --- a/Makefile > > +++ b/Makefile > > @@ -368,6 +368,7 @@ SCRIPT_SH += git-merge-one-file.sh > >  SCRIPT_SH += git-merge-resolve.sh > >  SCRIPT_SH += git-mergetool.sh > >  SCRIPT_SH += git-pull.sh > > +SCRIPT_SH += git-put.sh > >  SCRIPT_SH += git-quiltimport.sh > >  SCRIPT_SH += git-rebase.sh > >  SCRIPT_SH += git-repack.sh > > diff --git a/git-put.sh b/git-put.sh > > new file mode 100644 > > index 0000000..f673e14 > > --- /dev/null > > +++ b/git-put.sh > > @@ -0,0 +1,70 @@ > > +#!/bin/sh > > + > > +SUBDIRECTORY_OK=Yes > > +OPTIONS_KEEPDASHASH=Yes > > +OPTIONS_SPEC="\ > > +git put [options] [--] > > + > > +Move contents from one place to another, where and are one of: > > +  1. A commit (e.g., master, HEAD~10, v1.7.5) > > +  2. The special token INDEX to indicate git's index. > > +  3. The special token WORKTREE to indicate the working directory. > > + > > +Options: > > +-- > > +p            don't move whole files; use the patch interface > > +" > > +. git-sh-setup > > + > > +patch= > > +while test $# != 0; do > > +       case "$1" in > > +       -p) patch=--patch ;; > > +       --) shift; break ;; > > +       *) usage ;; > > +       esac > > +       shift > > +done > > +test $# -lt 2 && usage > > + > > +from=$1; shift > > +to=$1; shift > > +test "$1" = "--" && shift > > + > > +type_of() { > > +       case "$1" in > > +       INDEX) echo index ;; > > +       WORKTREE) echo worktree ;; > > +       *) echo commit ;; > > +       esac > > +} > > + > > +# Checkout contents to worktree without munging the index in > > +# between. > > +worktree_checkout() { > > +       old=$GIT_INDEX_FILE > > +       test -z "$old" && old=$(git rev-parse --git-dir)/index > > +       new=$(git rev-parse --git-dir)/put-index.tmp > > +       cp "$old" "$new" && > > +       GIT_INDEX_FILE=$new git checkout "$@" > > +       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) > > +       exec git add $patch -- "$@" ;; > > +commit,index) > > +       exec git reset $patch "$from" -- "$@" ;; > > +index,worktree) > > +       exec git checkout $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 [...] > git put [...] -- > > Where and could be > >  - [] >  - [0-3] [] >  - (or plain path) > > In the first form, pathspec could be specified in . If 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 nor > 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 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