[PATCH/RFCv4 1/2] git-rebase -i: add command "drop" to remove a commit

Subsystems: documentation, the rest

STALE3736d

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

[PATCH/RFCv4 1/2] git-rebase -i: add command "drop" to remove a commit

From: Galan Rémi <hidden>
Date: 2016-06-15 23:05:07

Instead of removing a line to remove the commit, you can use the
command "drop" (just like "pick" or "edit"). It has the same effect as
deleting the line (removing the commit) except that you keep a visual
trace of your actions, allowing a better control and reducing the
possibility of removing a commit by mistake.

Signed-off-by: Galan Rémi <redacted>
---
 Documentation/git-rebase.txt  |  3 +++
 git-rebase--interactive.sh    | 18 ++++++++++++++++++
 t/lib-rebase.sh               |  4 ++--
 t/t3404-rebase-interactive.sh | 10 ++++++++++
 4 files changed, 33 insertions(+), 2 deletions(-)
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 1d01baa..9cf3760 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -514,6 +514,9 @@ rebasing.
 If you just want to edit the commit message for a commit, replace the
 command "pick" with the command "reword".
 
+To drop a commit, replace the command "pick" with "drop", or just
+delete its line.
+
 If you want to fold two or more commits into one, replace the command
 "pick" for the second and subsequent commits with "squash" or "fixup".
 If the commits had different authors, the folded commit will be
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index dc3133f..869cc60 100644
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -152,6 +152,7 @@ Commands:
  s, squash = use commit, but meld into previous commit
  f, fixup = like "squash", but discard this commit's log message
  x, exec = run command (the rest of the line) using shell
+ d, drop = remove commit
 
 These lines can be re-ordered; they are executed from top to bottom.
 
@@ -508,6 +509,23 @@ do_next () {
 	"$comment_char"*|''|noop)
 		mark_action_done
 		;;
+	drop|d)
+		if test -z $sha1
+		then
+			warn "Missing SHA-1 in 'drop' command."
+			die "Please fix this using 'git rebase --edit-todo'."
+		fi
+
+		sha1_verif="$(git rev-parse --verify --quiet $sha1^{commit})"
+		if test -z $sha1_verif
+		then
+			warn "'$sha1' is not a SHA-1 or does not represent" \
+				"a commit in 'drop' command."
+			die "Please fix this using 'git rebase --edit-todo'."
+		fi
+
+		mark_action_done
+		;;
 	pick|p)
 		comment_for_reflog pick
 
diff --git a/t/lib-rebase.sh b/t/lib-rebase.sh
index 6bd2522..fdbc900 100644
--- a/t/lib-rebase.sh
+++ b/t/lib-rebase.sh
@@ -14,7 +14,7 @@
 #       specified line.
 #
 #   "<cmd> <lineno>" -- add a line with the specified command
-#       ("squash", "fixup", "edit", or "reword") and the SHA1 taken
+#       ("squash", "fixup", "edit", "reword" or "drop") and the SHA1 taken
 #       from the specified line.
 #
 #   "exec_cmd_with_args" -- add an "exec cmd with args" line.
@@ -46,7 +46,7 @@ set_fake_editor () {
 	action=pick
 	for line in $FAKE_LINES; do
 		case $line in
-		squash|fixup|edit|reword)
+		squash|fixup|edit|reword|drop)
 			action="$line";;
 		exec*)
 			echo "$line" | sed 's/_/ /g' >> "$1";;
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index ac429a0..8960083 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -1102,4 +1102,14 @@ test_expect_success 'rebase -i commits that overwrite untracked files (no ff)' '
 	test $(git cat-file commit HEAD | sed -ne \$p) = I
 '
 
+test_expect_success 'drop' '
+	test_when_finished "git checkout master" &&
+	git checkout -b dropBranchTest master &&
+	set_fake_editor &&
+	FAKE_LINES="1 drop 2 3 drop 4 5" git rebase -i --root &&
+	test E = $(git cat-file commit HEAD | sed -ne \$p) &&
+	test C = $(git cat-file commit HEAD^ | sed -ne \$p) &&
+	test A = $(git cat-file commit HEAD^^ | sed -ne \$p)
+'
+
 test_done
-- 
2.4.2.389.geaf7ccf

[PATCH/RFCv4 2/2] git rebase -i: warn about removed commits

From: Galan Rémi <hidden>
Date: 2016-06-15 23:05:07

Check if commits were removed (i.e. a line was deleted) and print
warnings or abort git rebase depending on the value of the
configuration variable rebase.missingCommits.

This patch gives the user the possibility to avoid silent loss of
information (losing a commit through deleting the line in this case)
if he wants to.

Add the configuration variable rebase.missingCommitsCheck.
    - When unset or set to "ignore", no checking is done.
    - When set to "warn", the commits are checked, warnings are
      displayed but git rebase still proceeds.
    - When set to "error", the commits are checked, warnings are
      displayed and the rebase is aborted.

rebase.missingCommitsCheck defaults to "ignore".

Signed-off-by: Galan Rémi <redacted>
---
 Documentation/config.txt      | 10 ++++++
 Documentation/git-rebase.txt  |  6 ++++
 git-rebase--interactive.sh    | 82 +++++++++++++++++++++++++++++++++++++++++++
 t/t3404-rebase-interactive.sh | 63 +++++++++++++++++++++++++++++++++
 4 files changed, 161 insertions(+)
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 4d21ce1..b29cd8d 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -2160,6 +2160,16 @@ rebase.autoStash::
 	successful rebase might result in non-trivial conflicts.
 	Defaults to false.
 
+rebase.missingCommitsCheck::
+	If set to "warn", git rebase -i will print a warning if some
+	commits are removed (e.g. a line was deleted), however the
+	rebase will still proceed. If set to "error", it will print
+	the previous warning and abort the rebase. If set to
+	"ignore", no checking is done.
+	To drop a commit without warning or error, use the `drop`
+	command in the todo-list.
+	Defaults to "ignore".
+
 receive.advertiseAtomic::
 	By default, git-receive-pack will advertise the atomic push
 	capability to its clients. If you don't want to this capability
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 9cf3760..6d413a1 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -213,6 +213,12 @@ rebase.autoSquash::
 rebase.autoStash::
 	If set to true enable '--autostash' option by default.
 
+rebase.missingCommitsCheck::
+	If set to "warn" print warnings about removed commits in
+	interactive mode. If set to "error" print the warnings and
+	abort the rebase. If set to "ignore" no checking is
+	done. "ignore" by default.
+
 OPTIONS
 -------
 --onto <newbase>::
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 869cc60..26804dd 100644
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -851,6 +851,86 @@ add_exec_commands () {
 	mv "$1.new" "$1"
 }
 
+# Print the list of the SHA-1 of the commits
+# from a todo list in a file.
+# $1: todo-file, $2: outfile
+todo_list_to_sha_list () {
+	git stripspace --strip-comments <"$1" | while read -r command sha1 rest
+	do
+		case $command in
+		x|"exec")
+			;;
+		*)
+			printf "%s\n" "$sha1"
+			;;
+		esac
+	done >"$2"
+}
+
+# Use warn for each line of a file
+# $1: file
+warn_file () {
+	while read -r line
+	do
+		warn " - $line"
+	done <"$1"
+}
+
+# Check if the user dropped some commits by mistake
+# Behaviour determined by rebase.missingCommitsCheck.
+check_commits () {
+	checkLevel=$(git config --get rebase.missingCommitsCheck)
+	checkLevel=${checkLevel:-ignore}
+	# Don't be case sensitive
+	checkLevel=$(echo "$checkLevel" | tr 'A-Z' 'a-z')
+
+	case "$checkLevel" in
+	warn|error)
+		# Get the SHA-1 of the commits
+		todo_list_to_sha_list "$todo".backup "$todo".oldsha1
+		todo_list_to_sha_list "$todo" "$todo".newsha1
+
+		# Sort the SHA-1 and compare them
+		sort -u "$todo".oldsha1 >"$todo".oldsha1+
+		mv "$todo".oldsha1+ "$todo".oldsha1
+		sort -u "$todo".newsha1 >"$todo".newsha1+
+		mv "$todo".newsha1+ "$todo".newsha1
+		comm -2 -3 "$todo".oldsha1 "$todo".newsha1 >"$todo".miss
+
+		# Make the list user-friendly
+		opt="--no-walk=sorted --format=oneline --abbrev-commit --stdin"
+		git rev-list $opt <"$todo".miss >"$todo".miss+
+		mv "$todo".miss+ "$todo".miss
+
+		# Check missing commits
+		if test -s "$todo".miss
+		then
+			warn "Warning: some commits may have been dropped" \
+				"accidentally."
+			warn "Dropped commits (newer to older):"
+			warn_file "$todo".miss
+			warn ""
+			warn "To avoid this message, use \"drop\" to" \
+				"explicitly remove a commit."
+			warn "Use git --config rebase.missingCommitsCheck to change" \
+				"the level of warnings (ignore, warn, error)."
+			warn ""
+
+			if test "$checkLevel" = error
+			then
+				die_abort "Rebase aborted due to dropped commits."
+			fi
+		fi
+		;;
+	ignore)
+		;;
+	*)
+		warn "Unrecognized setting $checkLevel for option" \
+			"rebase.missingCommitsCheck."
+		;;
+	esac
+}
+
 # The whole contents of this file is run by dot-sourcing it from
 # inside a shell function.  It used to be that "return"s we see
 # below were not inside any function, and expected to return
@@ -1096,6 +1176,8 @@ has_action "$todo" ||
 
 expand_todo_ids
 
+check_commits
+
 test -d "$rewritten" || test -n "$force_rebase" || skip_unnecessary_picks
 
 GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $onto_name"
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index 8960083..f369d2c 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -1112,4 +1112,67 @@ test_expect_success 'drop' '
 	test A = $(git cat-file commit HEAD^^ | sed -ne \$p)
 '
 
+cat >expect <<EOF
+Successfully rebased and updated refs/heads/tmp2.
+EOF
+
+test_expect_success 'rebase -i respects rebase.missingCommitsCheck=ignore' '
+	test_config rebase.missingCommitsCheck ignore &&
+	test_when_finished "git checkout master &&
+		git branch -D tmp2" &&
+	git checkout -b tmp2 master &&
+	set_fake_editor &&
+	FAKE_LINES="1 2 3 4" \
+		git rebase -i --root 2>warning &&
+	test D = $(git cat-file commit HEAD | sed -ne \$p) &&
+	test_cmp warning expect
+'
+
+cat >expect <<EOF
+Warning: some commits may have been dropped accidentally.
+Dropped commits (newer to older):
+ - $(git rev-list --pretty=oneline --abbrev-commit -1 master)
+
+To avoid this message, use "drop" to explicitly remove a commit.
+Use git --config rebase.missingCommitsCheck to change the level of warnings (ignore, warn, error).
+
+Successfully rebased and updated refs/heads/tmp2.
+EOF
+
+test_expect_success 'rebase -i respects rebase.missingCommitsCheck=warn' '
+	test_config rebase.missingCommitsCheck warn &&
+	test_when_finished "git checkout master &&
+		git branch -D tmp2" &&
+	git checkout -b tmp2 master &&
+	set_fake_editor &&
+	FAKE_LINES="1 2 3 4" \
+		git rebase -i --root 2>warning &&
+	test D = $(git cat-file commit HEAD | sed -ne \$p) &&
+	test_cmp warning expect
+'
+
+cat >expect <<EOF
+Warning: some commits may have been dropped accidentally.
+Dropped commits (newer to older):
+ - $(git rev-list --pretty=oneline --abbrev-commit -1 master)
+ - $(git rev-list --pretty=oneline --abbrev-commit -1 master~2)
+
+To avoid this message, use "drop" to explicitly remove a commit.
+Use git --config rebase.missingCommitsCheck to change the level of warnings (ignore, warn, error).
+
+Rebase aborted due to dropped commits.
+EOF
+
+test_expect_success 'rebase -i respects rebase.missingCommitsCheck=error' '
+	test_config rebase.missingCommitsCheck error &&
+	test_when_finished "git checkout master &&
+		git branch -D tmp2" &&
+	git checkout -b tmp2 master &&
+	set_fake_editor &&
+	test_must_fail env FAKE_LINES="1 2 4" \
+		git rebase -i --root 2>warning &&
+	test E = $(git cat-file commit HEAD | sed -ne \$p) &&
+	test_cmp warning expect
+'
+
 test_done
-- 
2.4.2.389.geaf7ccf

Re: [PATCH/RFCv4 2/2] git rebase -i: warn about removed commits

From: Remi Galan Alfonso <hidden>
Date: 2016-06-15 23:05:07

Galan Rémi [off-list ref] writes:
+                comm -2 -3 "$todo".oldsha1 "$todo".newsha1 >"$todo".miss
+
+                # Make the list user-friendly
+                opt="--no-walk=sorted --format=oneline --abbrev-commit --stdin"
+                git rev-list $opt <"$todo".miss >"$todo".miss+
+                mv "$todo".miss+ "$todo".miss
+
+                # Check missing commits
Found a bug here, got an error message from git rev-list if
"$todo".miss is empty.

Now it looks like:
		# Check missing commits
		if test -s "$todo".miss
		then
			# Make the list user-friendly
			opt="--no-walk=sorted --format=oneline --abbrev-commit --stdin"
			git rev-list $opt <"$todo".miss >"$todo".miss+
			mv "$todo".miss+ "$todo".miss

			warn "Warning: some commits may have been dropped" \
Thus the empty case is tested by the test -s of the warnings.

By the way, should I add --quiet to the options of the call to git
rev-list?

Rémi

Re: [PATCH/RFCv2 2/2] git rebase -i: warn about removed commits

From: Eric Sunshine <hidden>
Date: 2016-06-15 23:05:08

On Wednesday, June 3, 2015, Galan Rémi
[off-list ref] wrote:
Check if commits were removed (i.e. a line was deleted) and print
warnings or abort git rebase depending on the value of the
configuration variable rebase.missingCommits.
A few comments below in addition to those already made by Matthieu...
quoted hunk
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index 8960083..f369d2c 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -1112,4 +1112,67 @@ test_expect_success 'drop' '
        test A = $(git cat-file commit HEAD^^ | sed -ne \$p)
 '

+cat >expect <<EOF
+Successfully rebased and updated refs/heads/tmp2.
+EOF
+
+test_expect_success 'rebase -i respects rebase.missingCommitsCheck=ignore' '
+       test_config rebase.missingCommitsCheck ignore &&
+       test_when_finished "git checkout master &&
+               git branch -D tmp2" &&
Strange indentation.
+       git checkout -b tmp2 master &&
+       set_fake_editor &&
+       FAKE_LINES="1 2 3 4" \
+               git rebase -i --root 2>warning &&
The file containing the actual output is usually spelled "actual".
+       test D = $(git cat-file commit HEAD | sed -ne \$p) &&
+       test_cmp warning expect
The arguments to test_cmp are usually reversed so that 'expect' comes
before 'actual', which results in a more natural-feeling diff when
test_cmp detects that the files differ.

These comments apply to remaining new tests, as well.
+'
+
+cat >expect <<EOF
+Warning: some commits may have been dropped accidentally.
+Dropped commits (newer to older):
+ - $(git rev-list --pretty=oneline --abbrev-commit -1 master)
+
+To avoid this message, use "drop" to explicitly remove a commit.
+Use git --config rebase.missingCommitsCheck to change the level of warnings (ignore, warn, error).
+
+Successfully rebased and updated refs/heads/tmp2.
+EOF
+
+test_expect_success 'rebase -i respects rebase.missingCommitsCheck=warn' '
+       test_config rebase.missingCommitsCheck warn &&
+       test_when_finished "git checkout master &&
+               git branch -D tmp2" &&
+       git checkout -b tmp2 master &&
+       set_fake_editor &&
+       FAKE_LINES="1 2 3 4" \
+               git rebase -i --root 2>warning &&
+       test D = $(git cat-file commit HEAD | sed -ne \$p) &&
+       test_cmp warning expect
+'
+
+cat >expect <<EOF
+Warning: some commits may have been dropped accidentally.
+Dropped commits (newer to older):
+ - $(git rev-list --pretty=oneline --abbrev-commit -1 master)
+ - $(git rev-list --pretty=oneline --abbrev-commit -1 master~2)
+
+To avoid this message, use "drop" to explicitly remove a commit.
+Use git --config rebase.missingCommitsCheck to change the level of warnings (ignore, warn, error).
+
+Rebase aborted due to dropped commits.
+EOF
+
+test_expect_success 'rebase -i respects rebase.missingCommitsCheck=error' '
+       test_config rebase.missingCommitsCheck error &&
+       test_when_finished "git checkout master &&
+               git branch -D tmp2" &&
+       git checkout -b tmp2 master &&
+       set_fake_editor &&
+       test_must_fail env FAKE_LINES="1 2 4" \
+               git rebase -i --root 2>warning &&
+       test E = $(git cat-file commit HEAD | sed -ne \$p) &&
+       test_cmp warning expect
+'
+
 test_done
--
2.4.2.389.geaf7ccf

Re: [PATCH/RFCv2 2/2] git rebase -i: warn about removed commits

From: Remi Galan Alfonso <hidden>
Date: 2016-06-15 23:05:08

Eric Sunshine [off-list ref] writes:
quoted
+test_expect_success 'rebase -i respects rebase.missingCommitsCheck=ignore' '
+       test_config rebase.missingCommitsCheck ignore &&
+       test_when_finished "git checkout master &&
+               git branch -D tmp2" &&
Strange indentation.
Considering that 'git branch -D tmp2' is a part of test_when_finished,
I wasn't sure of how it was supposed to be indented, so I did it this
way to show that it was still within test_when_finished and not a
separate command.
	test_when_finished "git checkout master &&
	git branch -D tmp2" &&
Doesn't seem as clear, especially if you quickly read the lines.

For now, I have removed the tab.
Also the other points have been corrected.

Thank you,
Rémi

Re: [PATCH/RFCv2 2/2] git rebase -i: warn about removed commits

From: Remi Galan Alfonso <hidden>
Date: 2016-06-15 23:05:11

I'm going to try to change the die_abort in this patch by a die, so
that the user can use rebase --edit-todo afterward. This way, adding
the checking on the SHA-1 for the 'drop' command (discussed in 1/2)
(and also maybe the other commands requiring a correct SHA-1
corresponding to a commit) to the 2/2 part would make a bit more
sense. Though I still see some other issues with this, I agree that it
makes more sense in 2/2 rather than in 1/2 (some more checking in a
future patch would be a good idea).

(So far I've tried rather quickly, but it doesn't seem as easy as I
originally though, working on it though)

Rémi
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help