git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] rebase -i: add exec command to launch a shell command
@ 2010-08-05 13:00 Matthieu Moy
  2010-08-05 13:31 ` Ævar Arnfjörð Bjarmason
  0 siblings, 1 reply; 20+ messages in thread
From: Matthieu Moy @ 2010-08-05 13:00 UTC (permalink / raw)
  To: git, gitster; +Cc: Matthieu Moy

The typical usage pattern would be to run a test (or simply a compilation
command) at given points in history.

The shell command is ran (from the worktree root), and the rebase is
stopped when the command fails, to give the user an opportunity to fix
the problem before continuing with "git rebase --continue".

Signed-off-by: Matthieu Moy <Matthieu.Moy@imag.fr>
---

So, back to the "run from tree root", but that't now properly
documented and tested.

One notable difference with my first version is that the command is
ran in a subshell, defaulting to $SHELL (typically for users like me
with $SHELL=zsh who may want to take advantage of their shell's
advanced features)

 Documentation/git-rebase.txt  |   24 +++++++++++++++++++
 git-rebase--interactive.sh    |   20 ++++++++++++++++
 t/lib-rebase.sh               |    2 +
 t/t3404-rebase-interactive.sh |   50 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 96 insertions(+), 0 deletions(-)

diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index be23ad2..4bd4b66 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -459,6 +459,30 @@ sure that the current HEAD is "B", and call
 $ git rebase -i -p --onto Q O
 -----------------------------
 
+Reordering and editing commits usually creates untested intermediate
+steps.  You may want to check that your history editing did not break
+anything by running a test, or at least recompiling at intermediate
+points in history by using the "exec" command (shortcut "x").  You may
+do so by creating a todo list like this one:
+
+-------------------------------------------
+pick deadbee Implement feature XXX
+fixup f1a5c00 Fix to feature XXX
+exec make
+pick c0ffeee The oneline of the next commit
+edit deadbab The oneline of the commit after
+exec cd subdir; make test
+...
+-------------------------------------------
+
+The interactive rebase will stop when a command fails (i.e. exits with
+non-0 status) to give you an opportunity to fix the problem. You can
+continue with `git rebase --continue`.
+
+The "exec" command launches the command in a shell (the one specified
+in `$SHELL`, or the default shell if `$SHELL` is not set), so you can
+use usual shell commands like "cd". The command is run from the
+root of the working tree.
 
 SPLITTING COMMITS
 -----------------
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index b94c2a0..33d3087 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -537,6 +537,25 @@ do_next () {
 		esac
 		record_in_rewritten $sha1
 		;;
+	x|"exec")
+		read -r command rest < "$TODO"
+		mark_action_done
+		printf 'Executing: %s\n' "$rest"
+		# "exec" command doesn't take a sha1 in the todo-list.
+		# => can't just use $sha1 here.
+		git rev-parse --verify HEAD > "$DOTEST"/stopped-sha
+		${SHELL:-@SHELL_PATH@} -c "$rest" # Actual execution
+		status=$?
+		if test "$status" -ne 0
+		then
+			warn "Execution failed: $rest"
+			warn "You can fix the problem, and then run"
+			warn
+			warn "	git rebase --continue"
+			warn
+			exit "$status"
+		fi
+		;;
 	*)
 		warn "Unknown command: $command $sha1 $rest"
 		if git rev-parse --verify -q "$sha1" >/dev/null
@@ -957,6 +976,7 @@ first and then run 'git rebase --continue' again."
 #  e, edit = use commit, but stop for amending
 #  s, squash = use commit, but meld into previous commit
 #  f, fixup = like "squash", but discard this commit's log message
+#  x <cmd>, exec <cmd> = Run a shell command <cmd>, and stop if it fails
 #
 # If you remove a line here THAT COMMIT WILL BE LOST.
 # However, if you remove everything, the rebase will be aborted.
diff --git a/t/lib-rebase.sh b/t/lib-rebase.sh
index 6aefe27..6ccf797 100644
--- a/t/lib-rebase.sh
+++ b/t/lib-rebase.sh
@@ -47,6 +47,8 @@ for line in $FAKE_LINES; do
 	case $line in
 	squash|fixup|edit|reword)
 		action="$line";;
+	exec*)
+		echo "$line" | sed 's/_/ /g' >> "$1";;
 	"#")
 		echo '# comment' >> "$1";;
 	">")
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index 9f03ce6..3b07850 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -64,6 +64,56 @@ test_expect_success 'setup' '
 	done
 '
 
+# debugging-friendly alternatives to "test -f" and "test ! -f"
+file_must_exist () {
+    if ! [ -f "$1" ]; then
+	echo "file $1 not created."
+	false
+    fi
+}
+
+file_must_not_exist () {
+    if [ -f "$1" ]; then
+	echo "file $1 created while it shouldn't have. $2"
+	false
+    fi
+}
+
+test_expect_success 'rebase -i with the exec command' '
+	git checkout master &&
+	FAKE_LINES="1 exec_touch_touch-one 2 exec_touch_touch-two exec_false exec_touch_touch-three 3 4
+		exec_touch_\"touch-file__name_with_spaces\";_touch_touch-after-semicolon 5" \
+		test_must_fail git rebase -i A &&
+	file_must_exist touch-one &&
+	file_must_exist touch-two &&
+	file_must_not_exist touch-three "(Should have stopped before)" &&
+	test $(git rev-parse C) = $(git rev-parse HEAD) || {
+		echo "Stopped at wrong revision:"
+		echo "($(git describe --tags HEAD) instead of C)"
+		false
+	} &&
+	git rebase --continue &&
+	file_must_exist touch-three &&
+	file_must_exist "touch-file  name with spaces" &&
+	file_must_exist touch-after-semicolon &&
+	test $(git rev-parse master) = $(git rev-parse HEAD) || {
+		echo "Stopped at wrong revision:"
+		echo "($(git describe --tags HEAD) instead of master)"
+		false
+	} &&
+	rm -f touch-*
+'
+
+test_expect_success 'rebase -i with the exec command runs from tree root' '
+	git checkout master &&
+	mkdir subdir && cd subdir &&
+	FAKE_LINES="1 exec_touch_touch-subdir" \
+		git rebase -i HEAD^ &&
+	cd .. &&
+	file_must_exist touch-subdir &&
+	rm -fr subdir
+'
+
 test_expect_success 'no changes are a nop' '
 	git checkout branch2 &&
 	git rebase -i F &&
-- 
1.7.2.1.30.g18195

^ permalink raw reply related	[flat|nested] 20+ messages in thread
* Re: [RFC/PATCH] rebase -i: add run command to launch a shell command
@ 2010-08-02 10:02 Matthieu Moy
  2010-08-02 10:03 ` [PATCH] rebase -i: add exec " Matthieu Moy
  0 siblings, 1 reply; 20+ messages in thread
From: Matthieu Moy @ 2010-08-02 10:02 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason; +Cc: Marc Branchaud, git

Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:

> On Fri, Jul 30, 2010 at 15:24, Matthieu Moy
> <Matthieu.Moy@grenoble-inp.fr> wrote:
>> Marc Branchaud <marcnarc@xiplink.com> writes:
>>
>>>> The name of the command may be subject to discussions. I've chosen
>>>> "run", but maybe "shell" would be OK too. In both cases, it doesn't
>>>> allow the one-letter version since both "r" and "s" are already used.
>>>
>>> "exec" with one-letter "x"?
>>
>> Thanks, that sounds good, yes. Any other thought?
>
> I like "exec".

Yes, it's the best proposal IMHO. "x" is very often associated to
"execute", and I'd rather keep away from punctuation/shift combo.
There have been discussions in the past about giving "!" a semantics
(like "fixup!" for a variant of "fixup"). Let's keep this as an option
for the future by not using ! now.

> I think the docs need to elaborate on the environment the "exec"
> command gets executed in, what's its current working directory for
> instance? Wherever I happened to run git-rebase from? the project
> root?

That's a good question. My original patch was running the command from
the toplevel, which is the natural way to implement it. I've changed
my mind to execute the command from the place where "git rebase -i"
was started (which means this has to be memorized in a temporary file
to be persistant accross "git rebase --continue"). I think this makes
more sense for the user, and I've actually already been biten by the
old behavior, running "rebase -i" from a doc/ subdirectory, and
wondering why my "exec make" was rebuilding the code itself.

This comes with at least one drawback: if directory from which the
rebase was started didn't exist in the past, then we can't do a simple
"cd" to it. My implementation re-creates the directory temporarily, so
that the command can run, and cleans it up afterwards. The only really
problematic case is when the directory can not be created (like
directory/file conflict). It this case, the command is not ran, and
the script exits.

> your if ! eval .. error message also exits with 0, surely that should
> exit with 1?

I'm now exiting with the same status as the command itself in case of
failure.

New version follows. It should hopefully look more like a real patch
than an RFC now.

-- 
Matthieu Moy
http://www-verimag.imag.fr/~moy/

^ permalink raw reply	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2010-08-10 11:23 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-08-05 13:00 [PATCH] rebase -i: add exec command to launch a shell command Matthieu Moy
2010-08-05 13:31 ` Ævar Arnfjörð Bjarmason
2010-08-05 16:47   ` Matthieu Moy
2010-08-05 16:54     ` [PATCH 1/2] " Matthieu Moy
2010-08-05 16:54     ` [PATCH 2/2] test-lib: user-friendly alternatives to test [!] [-d|-f] Matthieu Moy
2010-08-05 17:03       ` [PATCH v2] " Matthieu Moy
2010-08-06 22:57         ` Jonathan Nieder
2010-08-07  0:21           ` Ævar Arnfjörð Bjarmason
2010-08-09 16:25       ` [PATCH 2/2] " Junio C Hamano
2010-08-10 11:00         ` [PATCH] test-lib: user-friendly alternatives to test [-d|-f|-e] Matthieu Moy
2010-08-10 11:11           ` Joshua Juran
2010-08-10 11:18             ` [PATCH v3] " Matthieu Moy
2010-08-05 18:24     ` [PATCH] rebase -i: add exec command to launch a shell command Erik Faye-Lund
2010-08-05 18:37     ` Jacob Helwig
2010-08-05 20:16       ` Junio C Hamano
  -- strict thread matches above, loose matches on Subject: below --
2010-08-02 10:02 [RFC/PATCH] rebase -i: add run " Matthieu Moy
2010-08-02 10:03 ` [PATCH] rebase -i: add exec " Matthieu Moy
2010-08-02 12:30   ` Jared Hance
2010-08-02 15:51   ` Ævar Arnfjörð Bjarmason
2010-08-06 21:07   ` Johannes Sixt
2010-08-07  8:48     ` Matthieu Moy

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).