Re: several quick questions

8 messages, 5 authors, 2016-06-15 · open the first message on its own page

Re: several quick questions

From: Junio C Hamano <hidden>
Date: 2016-06-15 22:42:19

Carl Worth [off-list ref] writes:
You've pointed out that branches are free in terms of what git has to
do. I'm saying that they're not free for the user who bears the cost
of inventing a name. And in the case of any commit-while-seeking, it's
at the time of the commit itself that the user has enough information
to invent a useful name, not prior to seeking, (when the user is still
trying to figure things out).
I think this is a very valid point and I am happy to accept a
workable proposal (does not have to be a working patch, but a
general semantics that covers most of if not all the corner
cases).

[PATCH] New git-seek command with documentation and test.

From: Carl Worth <hidden>
Date: 2016-06-15 22:42:20

Add git-seek which allows for temporary excursions through the
revision history. With "git seek <revision>" one gets a working tree
corresponding to <revision>. When done with the excursion "git seek"
returns back to the original branch from where the first seek began.

Signed-off-by: Carl Worth <redacted>

---

 git-seek could be used as a new basis for git-bisect. This patch does
 not do that, but even so, git-bisect and git-seek should play nicely
 with each other, (in the sense that either will refuse to do anything
 if .git/head-name already exists).

 On Tue, 14 Feb 2006 14:39:34 -0800, Junio C Hamano wrote:
 > Carl Worth [off-list ref] writes:
 > > [arguments in favor of a new git-seek] 
 > 
 > I think this is a very valid point and I am happy to accept a
 > workable proposal (does not have to be a working patch, but a
 > general semantics that covers most of if not all the corner
 > cases).
 
 I had planned to just let this drop as my original need was some
 historical exploration that I've already finished. But now I've found
 a common use case in my everyday workflow that could benefit from
 git-seek. Here it is:
 
 I receive a bug-fix patch that updates a test case to demonstrate the
 bug. I can apply both the fix and the test case and see it succeed.
 But what I really want to do is first commit the test case, see it
 fail, and only then commit the fix and see the test now succeed.  I'd
 also like the history to reflect that order. So what I do is:
 
 	$ git-am
 	$ git update-index test.c ; git commit -m "Update test"
 	$ git update-index buggy.c ; git commit -m "Fix bug"
 
 At that point, without git-seek I can get by with:
 
 	$ git checkout -b tmp HEAD^
 	$ make check # to see failure
 	$ git checkout <branch_I_was_on_to_begin_with>
 	$ git branch -d tmp # easy to forget, but breaks the next time otherwise
 	$ make check # to see success
 
 But what I'd really like to do, (and can with the attached patch), is:
 
 	$ git seek HEAD^
 	$ make check # to see failure
 	$ git seek
 	$ make check # to see success
 
 This avoids me having to: 1) invent a throwaway name, 2) remember the
 branch I started on, 3) remember to actually throwaway the temporary
 branch.

 I've documented git-seek quite carefully and added a test that tries
 to cover every documented failure mode.

 -Carl

 .gitignore                 |    1 
 Documentation/git-seek.txt |   44 +++++++++++++++++++++
 Makefile                   |    4 +-
 git-seek.sh                |   94 ++++++++++++++++++++++++++++++++++++++++++++
 t/t3800-seek.sh            |   82 ++++++++++++++++++++++++++++++++++++++
 5 files changed, 223 insertions(+), 2 deletions(-)
 create mode 100644 Documentation/git-seek.txt
 create mode 100644 git-seek.sh
 create mode 100755 t/t3800-seek.sh

2656ffb6e3fcbd9443c22b4675b13f23c031600e
diff --git a/.gitignore b/.gitignore
index 94f66d5..55484b0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -85,6 +85,7 @@ git-rev-list
 git-rev-parse
 git-revert
 git-rm
+git-seek
 git-send-email
 git-send-pack
 git-sh-setup
diff --git a/Documentation/git-seek.txt b/Documentation/git-seek.txt
new file mode 100644
index 0000000..cb5c13d
--- /dev/null
+++ b/Documentation/git-seek.txt
@@ -0,0 +1,44 @@
+git-bisect(1)
+=============
+
+NAME
+----
+git-seek - Provide a temporary excursion through the revision history.
+
+
+SYNOPSIS
+--------
+'git seek' [<revision>]
+
+DESCRIPTION
+-----------
+When given a <revision>, git-seek updates the files in the working
+tree to the state of the given revision. It will do this by performing
+a checkout of <revision> to a new branch named "seek", or by resetting
+the seek branch if it already exists.
+
+When run with with no <revision> argument, git-seek will return to the
+original branch from which the initial git-seek operation was
+performed, (this original branch name is saved in $GIT_DIR/head-name).
+
+git-seek refuses to do anything if the working tree or index are
+modified with respect to HEAD. If you want to carry modifications
+around, use git-checkout rather than git-seek.
+
+git-seek will also fail if GIT_DIR/head-name exists when a seek is not
+already in progress, or if a seek branch already exists that is not a
+subset of the current branch, (that is, if it has unmerged commits).
+
+Author
+------
+Written by Carl Worth <cworth@cworth.org>, based on git-bisect by
+Linus Torvalds <torvalds@osdl.org>
+
+Documentation
+-------------
+Documentation by Carl Worth and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the gitlink:git[7] suite
+
diff --git a/Makefile b/Makefile
index 8e6bbce..f3383d8 100644
--- a/Makefile
+++ b/Makefile
@@ -120,8 +120,8 @@ SCRIPT_SH = \
 	git-merge-one-file.sh git-parse-remote.sh \
 	git-prune.sh git-pull.sh git-push.sh git-rebase.sh \
 	git-repack.sh git-request-pull.sh git-reset.sh \
-	git-resolve.sh git-revert.sh git-rm.sh git-sh-setup.sh \
-	git-tag.sh git-verify-tag.sh git-whatchanged.sh \
+	git-resolve.sh git-revert.sh git-rm.sh git-seek.sh \
+	git-sh-setup.sh git-tag.sh git-verify-tag.sh git-whatchanged.sh \
 	git-applymbox.sh git-applypatch.sh git-am.sh \
 	git-merge.sh git-merge-stupid.sh git-merge-octopus.sh \
 	git-merge-resolve.sh git-merge-ours.sh git-grep.sh \
diff --git a/git-seek.sh b/git-seek.sh
new file mode 100644
index 0000000..26f0b76
--- /dev/null
+++ b/git-seek.sh
@@ -0,0 +1,94 @@
+#!/bin/sh
+
+USAGE='[<revision>]'
+LONG_USAGE='git-seek provides a temporary excursion through the revision history.
+
+When given a <revision>, git-seek updates the files in the working
+tree to the state of the given revision. It will do this by performing
+a checkout of <revision> to a new branch named "seek", or by resetting
+the seek branch if it already exists.
+
+When run with with no <revision> argument, git-seek will return to the
+original branch from which the initial git-seek operation was
+performed, (this original branch name is saved in $GIT_DIR/head-name).
+
+git-seek refuses to do anything if the working tree or index are
+modified with respect to HEAD. If you want to carry modifications
+around, use git-checkout rather than git-seek.
+
+git-seek will also fail if GIT_DIR/head-name exists when a seek is not
+already in progress, or if a seek branch already exists that is not a
+subset of the current branch, (that is, if it has unmerged commits).'
+
+. git-sh-setup
+
+# Does $GIT_DIR/head-name contain the given revision
+# We use git-rev-parse to correctly resolve any aliases through references.
+head_name_contains() {
+	old_head=$(git-rev-parse $(cat "$GIT_DIR/head-name"))
+	new_head=$(git-rev-parse "$1")
+	[ "$old_head" = "$new_head" ]
+}
+
+seek_to() {
+	target="$1"
+	head=$(GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD) ||
+	die "Bad HEAD - I need a symbolic ref"
+	case "$head" in
+	refs/heads/seek)
+		# An explicit seek to head-name is treated as a reset
+		if head_name_contains "$target"; then
+			seek_reset
+		else
+			git reset --hard $target
+		fi
+		;;
+	refs/heads/*)
+		[ -s "$GIT_DIR/head-name" ] && die "Will not seek: $GIT_DIR/head-name is already in use"
+		echo "$head" | sed 's#^refs/heads/##' >"$GIT_DIR/head-name"
+		if git-rev-parse --verify seek >&/dev/null ; then
+			git-branch -d seek || exit
+		fi
+		git checkout -b seek $target
+		;;
+	*)
+		die "Bad HEAD - strange symbolic ref"
+		;;
+	esac
+}
+
+seek_reset() {
+	if [ -s "$GIT_DIR/head-name" ]; then
+		source=$(cat "$GIT_DIR/head-name") || exit
+	else
+		echo >&2 "No seek is in progress: returning to master."
+		source 
+	fi
+	git checkout "$source" &&
+	(git branch -d seek || err=$? ; git checkout seek ; exit $err) &&
+	rm -f "$GIT_DIR/head-name"
+}
+
+head=$(git-rev-parse --verify HEAD) || die "You do not have a valid HEAD"
+
+files_dirty=$(git-diff-index --name-only $head) || exit
+index_dirty=$(git-diff-index --cached --name-only $head) || exit
+if [ "$files_dirty" -o "$index_dirty" ]; then
+	die "Will not seek from a dirty state:
+	${index_dirty:+(dirty in index: $index_dirty)} ${files_dirty:+(dirty in working tree: $files_dirty)}
+You may want to commit these changes first or perhaps use git-checkout
+-m instead of git-seek."
+fi
+
+case "$#" in
+0)
+	seek_reset
+	;;
+1)
+	seek_to "$1"
+	;;
+*)
+	usage 
+	;;
+esac
+
diff --git a/t/t3800-seek.sh b/t/t3800-seek.sh
new file mode 100755
index 0000000..e5d8f90
--- /dev/null
+++ b/t/t3800-seek.sh
@@ -0,0 +1,82 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Carl D. Worth
+#
+
+test_description='Test of git-seek and all documented failure modes.'
+
+. ./test-lib.sh
+
+echo "first" > file
+git-add file && git-commit -m "add first revision of file"
+echo "second" > file
+git-commit -a -m "commit second revision"
+git tag second
+echo "third" > file
+git-commit -a -m "commit third revision"
+
+verify_revision() {
+    contents=$(cat file) && [ "$contents" = "$1" ]
+}
+
+test_expect_success \
+    'Test of initial "git-seek <revision>"' \
+    'git-seek HEAD~2 && verify_revision first'
+
+test_expect_success \
+    'Test of "git-seek <revision>" during seek' \
+    'git-seek second && verify_revision second'
+
+test_expect_success \
+    'Test that "git-seek" returns to starting point and resets seek state' \
+    'git-seek && verify_revision third &&
+     [ ! -f .git/refs/seek ] &&
+     [ ! -f .git/head-name ]'
+
+test_expect_success \
+    'Test that "git-seek master" also resets seek state' \
+    'git seek HEAD^1 &&
+     git seek master && verify_revision third &&
+     [ ! -f .git/refs/seek ] &&
+     [ ! -f .git/head-name ]'
+
+test_expect_success \
+    'Test that "git-seek <revision>" which aliases to master also resets seek state' \
+    'source=$(git-rev-parse HEAD) &&
+     git seek HEAD^1 &&
+     git seek $source && verify_revision third &&
+     [ ! -f .git/refs/seek ] &&
+     [ ! -f .git/head-name ]'
+
+echo modified > file
+test_expect_failure \
+    'Test that git-seek fails with local file modification' \
+    'git-seek HEAD^'
+git-reset --hard master
+
+echo modified > file
+git-update-index file
+test_expect_failure \
+    'Test that git-seek fails with a modified index' \
+    'git-seek HEAD^'
+git-reset --hard master
+
+echo master > .git/head-name
+test_expect_failure \
+    'Test that git-seek fails when .git/head-name exists and not seeking' \
+    'git-seek HEAD^'
+rm .git/head-name
+
+git-seek HEAD^
+echo new > new; git-add new; git-commit -m "Commit new file to seek branch"
+test_expect_failure \
+    'Test that git-seek fails when there are unmerged commits on seek branch' \
+    'git-seek'
+
+git checkout master
+git-pull . seek >&/dev/null
+test_expect_success \
+    'Test that git-seek works again after merging in the seek branch' \
+    'git-seek'
+
+test_done
-- 
1.2.3.g207a-dirty



Re: [PATCH] New git-seek command with documentation and test.

From: J. Bruce Fields <hidden>
Date: 2016-06-15 22:42:20

On Thu, Feb 23, 2006 at 12:31:25PM -0800, Carl Worth wrote:
quoted hunk
--- /dev/null
+++ b/Documentation/git-seek.txt
@@ -0,0 +1,44 @@
+git-bisect(1)
+=============
Oops.
+When given a <revision>, git-seek updates the files in the working
+tree to the state of the given revision. It will do this by performing
+a checkout of <revision> to a new branch named "seek", or by resetting
+the seek branch if it already exists.
I wonder if its a good idea to silently reset a branch named with a
short common word?
+LONG_USAGE='git-seek provides a temporary excursion through the revision history.
+
+When given a <revision>, git-seek updates the files in the working
+tree to the state of the given revision. It will do this by performing
+a checkout of <revision> to a new branch named "seek", or by resetting
+the seek branch if it already exists.
These long usage texts with language duplicated from the man pages seem
like they'd be asking for bit-rot, when an update happens in one place
but not the other.  I dunno.

--b.

[PATCH] git-seek: Eliminate spurious warning. Fix errant reference to git-bisect in docs.

From: Carl Worth <hidden>
Date: 2016-06-15 22:42:20

This fixed a bug that would cause "git seek" to mistakenly try to
checkout the seek branch just after deleting it. Of course, that would
never work, but fixing the bug does squelch the annoying error caused
by the bug.

Also fix an errant title of "git-bisect" in the git-seek documentation.

---

On Thu, 23 Feb 2006 19:18:48 -0500, "J. Bruce Fields" wrote:
On Thu, Feb 23, 2006 at 12:31:25PM -0800, Carl Worth wrote:
quoted
+git-bisect(1)
+=============
Oops.
Thanks.
I wonder if its a good idea to silently reset a branch named with a
short common word?
It at least takes some care not to leave commits dangling when doing
this, (the seek branch must at least be a subset of the current
HEAD). I was pretty much following the lead of git-bisect here,
(though "bisect" is definitely a touch longer and less common than
"seek").

If it would be preferred to hide such "internal" branch names behind
some unlikely symbol or such, that would obviously be easy to do.

As is, the seek branch is at least documented, and rather well
advertised in operation, (for example, returning with "git seek"
reported "Deleted branch seek.").
These long usage texts with language duplicated from the man pages seem
like they'd be asking for bit-rot, when an update happens in one place
but not the other.  I dunno.
Yeah, I don't know. Again, I was just imitating things I'd seen
elsewhere.

 Documentation/git-seek.txt |    4 ++--
 git-seek.sh                |    2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

43f042982c26859b6b7f6055fc03dda8e89f4e70
diff --git a/Documentation/git-seek.txt b/Documentation/git-seek.txt
index cb5c13d..513dbc7 100644
--- a/Documentation/git-seek.txt
+++ b/Documentation/git-seek.txt
@@ -1,5 +1,5 @@
-git-bisect(1)
-=============
+git-seek(1)
+===========
 
 NAME
 ----
diff --git a/git-seek.sh b/git-seek.sh
index 26f0b76..921c014 100644
--- a/git-seek.sh
+++ b/git-seek.sh
@@ -65,7 +65,7 @@ seek_reset() {
 		source 
 	fi
 	git checkout "$source" &&
-	(git branch -d seek || err=$? ; git checkout seek ; exit $err) &&
+	(git branch -d seek || (err=$? ; git checkout seek ; exit $err)) &&
 	rm -f "$GIT_DIR/head-name"
 }
 
-- 
1.2.3.g2656-dirty

Re: [PATCH] New git-seek command with documentation and test.

From: Andreas Ericsson <hidden>
Date: 2016-06-15 22:42:20

Carl Worth wrote:
Add git-seek which allows for temporary excursions through the
revision history. With "git seek <revision>" one gets a working tree
corresponding to <revision>. When done with the excursion "git seek"
returns back to the original branch from where the first seek began.
I've said it before, and I'll say it again. This tool provides less 
flexibility and much less power than "git checkout -b branch 
<commit-ish>" (although it would be nice to have '-o' for 'overwrite 
existing branch' as an argument to git checkout)
Signed-off-by: Carl Worth <redacted>

---
 
 I had planned to just let this drop as my original need was some
 historical exploration that I've already finished. But now I've found
 a common use case in my everyday workflow that could benefit from
 git-seek. Here it is:
 
 I receive a bug-fix patch that updates a test case to demonstrate the
 bug. I can apply both the fix and the test case and see it succeed.
 But what I really want to do is first commit the test case, see it
 fail, and only then commit the fix and see the test now succeed.  I'd
 also like the history to reflect that order. So what I do is:
 
 	$ git-am
 	$ git update-index test.c ; git commit -m "Update test"
 	$ git update-index buggy.c ; git commit -m "Fix bug"
 
 At that point, without git-seek I can get by with:
 
 	$ git checkout -b tmp HEAD^
 	$ make check # to see failure
 	$ git checkout <branch_I_was_on_to_begin_with>
 	$ git branch -d tmp # easy to forget, but breaks the next time otherwise
 	$ make check # to see success
 
 But what I'd really like to do, (and can with the attached patch), is:
 
 	$ git seek HEAD^
 	$ make check # to see failure
 	$ git seek
 	$ make check # to see success
 
 This avoids me having to:
1) invent a throwaway name,
All programmers have at least five throwaway names that are only ever 
used as such (mine are, in order of precedence, foo, bar, tmp, fnurg, 
sdf and asd).
2) remember the branch I started on,
With topic branches, you need to pick more careful topic names. Without 
topic branches you're always on "master". Surely you know what the 
patches touch, so you know what branch they should be in.
3) remember to actually throwaway the temporary branch.
This isn't always a bad thing, since you after applying some patch or 
other decide you want to go back to this point in history, or want to 
keep the point so you can show the author some problem or other with the 
patch. With git-seek you'll then have to remember the hard-to-learn 
SHA1, or how far below HEAD or some other easily remembered point in 
history it is. In that case, you need to remember to add the 
branch/tag/whatever to where you seeked rather than just go on with the 
work. Removing a branch later is simple. Finding the right spot to 
create it later can be trouble-some.

If I had a vote, I'd say no to this patch, and to this tool entirely.

-- 
Andreas Ericsson                   andreas.ericsson@op5.se
OP5 AB                             www.op5.se
Tel: +46 8-230225                  Fax: +46 8-230231

Re: [PATCH] New git-seek command with documentation and test.

From: Carl Worth <hidden>
Date: 2016-06-15 22:42:20

On Fri, 24 Feb 2006 11:00:29 +0100, Andreas Ericsson wrote:
I've said it before, and I'll say it again. This tool provides less 
flexibility and much less power than "git checkout -b branch 
<commit-ish>"
Yes, that's by design. It's not intended to be a replacement for git
checkout -b. It's intended to be easier to use than that when its
purpose fits what you want to to.
quoted
1) invent a throwaway name,
All programmers have at least five throwaway names that are only ever 
used as such (mine are, in order of precedence, foo, bar, tmp, fnurg, 
sdf and asd).
Sure, and when I use "git checkout -b" I have to keep trying these
linearly until I found one that is available. That's what I've been
doing, and it's painful enough that I wrote this. (Though yes,
something like checkout -o would help here).
quoted
2) remember the branch I started on,
With topic branches, you need to pick more careful topic names. Without 
topic branches you're always on "master". Surely you know what the 
patches touch, so you know what branch they should be in.
I almost put "remember" in quotation marks. Obviously I know what I'm
working on. It's more a matter of just having to type the name, (I do
use very careful topic names so they tend to be longish). Having
tab-completion for git-checkout would help here.

So (1) and (2) have potential workarounds, but neither exists, and
even then they would still be harder to use than git-seek.
quoted
3) remember to actually throwaway the temporary branch.
This isn't always a bad thing, since you after applying some patch or 
other decide you want to go back to this point in history,
That assumes that I've made any change though. If you're going back in
the past to make changes, then "git checkout -b" is the right thing to
use. It's when you're not planning to make changes, but just exploring
the past that "git seek" is helpful.

So (3) is just extra pain when using git-seek for what its designed to
be good for, (exploring history when not planning on writing to it).

But note that the git-seek I've implemented *does* provide a writable
branch, so if you discover that you do want to commit something, then
that's always available. Linus gave compelling arguments for this.
               In that case, you need to remember to add the 
branch/tag/whatever to where you seeked rather than just go on with the 
work. Removing a branch later is simple. Finding the right spot to 
create it later can be trouble-some.
Yes. And that's why git-seek stops and warns you before it leaves
dangling commits by moving the branch. (Though it might make sense to
add a -f option to force it to seek regardless of the things it
currently balks at.)
If I had a vote, I'd say no to this patch, and to this tool entirely.
One argument in favor is that seeking already exists in git privately
within git-bisect. Exposing git-seek makes it easier to code new
operations along the lines of git-bisect. It's certainly consistent
with git's current implementation strategy to have the more primitive
pieces of complex operations exported and available.

-Carl

Re: [PATCH] New git-seek command with documentation and test.

From: Johannes Schindelin <hidden>
Date: 2016-06-15 22:42:20

Hi,

On Fri, 24 Feb 2006, Carl Worth wrote:
On Fri, 24 Feb 2006 11:00:29 +0100, Andreas Ericsson wrote:
quoted
I've said it before, and I'll say it again. This tool provides less 
flexibility and much less power than "git checkout -b branch 
<commit-ish>"
Yes, that's by design. It's not intended to be a replacement for git
checkout -b.
I do not really understand why.

git-seek shares so many characteristics with git-seek, you could make 
git-seek just another command line option to checkout (like "--temporary" 
and "--go-back").

Hth,
Dscho

Re: [PATCH] New git-seek command with documentation and test.

From: J. Bruce Fields <hidden>
Date: 2016-06-15 22:42:20

On Fri, Feb 24, 2006 at 10:48:46PM +0100, Johannes Schindelin wrote:
git-seek shares so many characteristics with git-seek, you could make 
git-seek just another command line option to checkout (like "--temporary" 
and "--go-back").
Well, as a user interface, git-seek seems a bit simpler (e.g., easier to
remember).--b.
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help