Thread (6 messages) flat view 6 messages, 3 authors, 2016-06-15

Re: [RFC/PATCH] git put: an alternative to add/reset/checkout

From: Nguyen Thai Ngoc Duy <hidden>
Date: 2016-06-15 22:52:51

(Bringing up an old thread)

On Wed, Jun 8, 2011 at 3:06 AM, Jeff King [off-list ref] wrote:
quoted hunk ↗ jump to 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
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] <from> <to> [--] <file...>
+
+Move contents from one place to another, where <from> and <to> 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 <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
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help