git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Neil Horman <nhorman@tuxdriver.com>
To: git@vger.kernel.org
Cc: "Zbigniew Jędrzejewski-Szmek" <zbyszek@in.waw.pl>,
	"Clemens Buchacher" <drizzd@aon.at>,
	"Phil Hord" <phil.hord@gmail.com>,
	"Junio C Hamano" <gitster@pobox.com>,
	"Neil Horman" <nhorman@tuxdriver.com>
Subject: [PATCH v8 2/4] git-cherry-pick: Add keep-redundant-commits option
Date: Fri, 20 Apr 2012 10:36:15 -0400	[thread overview]
Message-ID: <1334932577-31232-3-git-send-email-nhorman@tuxdriver.com> (raw)
In-Reply-To: <1334932577-31232-1-git-send-email-nhorman@tuxdriver.com>

The git-cherry-pick --allow-empty command by default only preserves empty
commits that were originally empty, i.e only those commits for which
<commit>^{tree} and <commit>^^{tree} are equal.  By default commits which are
non-empty, but were made empty by the inclusion of a prior commit on the current
history are filtered out.  This option allows us to override that behavior and
include redundant commits as empty commits in the change history.

Note that this patch changes the default behavior of git cherry-pick slightly.
Prior to this patch all commits in a cherry-pick sequence were applied and git
commit was run.  The implication here was that, if a commit was redundant, and
the commit did not trigger the fast forward logic, the git commit operation, and
therefore the git cherry-pick operation would fail, displaying the cherry pick
advice (i.e. run git commit --allow-empty).  With this patch however, such
redundant commits are automatically skipped without stopping, unless
--keep-redundant-commits is specified, in which case, they are automatically
applied as empty commits.

Signed-off-by: Neil Horman <nhorman@tuxdriver.com>
---
 Documentation/git-cherry-pick.txt |   12 ++++-
 builtin/revert.c                  |    8 +++-
 sequencer.c                       |   94 ++++++++++++++++++++++++++++++++----
 sequencer.h                       |    1 +
 4 files changed, 102 insertions(+), 13 deletions(-)

diff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt
index 730237a..0c004e9 100644
--- a/Documentation/git-cherry-pick.txt
+++ b/Documentation/git-cherry-pick.txt
@@ -110,7 +110,17 @@ effect to your index in a row.
 	behavior, allowing empty commits to be preserved automatically
 	in a cherry-pick. Note that when "--ff" is in effect, empty
 	commits that meet the "fast-forward" requirement will be kept
-	even without this option.
+	even without this option.  Note also, that use of this option only
+	keeps commits that were initially empty (i.e. the commit recorded the
+	same tree as its parent).  Commits which are made empty due to a
+	previous commit are ignored.  To force the inclusion of those commits
+	use `--keep-redundant-commits`.
+
+--keep-redundant-commits::
+	If a commit being cherry picked duplicates a commit already in the
+	current history, it will result in an empty changeset.  By default these
+	redundant commits are ignored.  This option overrides that behavior and
+	creates an empty commit object.  Implies `--allow-empty`.
 
 --strategy=<strategy>::
 	Use the given merge strategy.  Should only be used once.
diff --git a/builtin/revert.c b/builtin/revert.c
index 06b00e6..f135502 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -115,13 +115,15 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
 		OPT_END(),
 		OPT_END(),
 		OPT_END(),
+		OPT_END(),
 	};
 
 	if (opts->action == REPLAY_PICK) {
 		struct option cp_extra[] = {
 			OPT_BOOLEAN('x', NULL, &opts->record_origin, "append commit name"),
 			OPT_BOOLEAN(0, "ff", &opts->allow_ff, "allow fast-forward"),
-			OPT_BOOLEAN(0, "allow-empty", &opts->allow_empty, "preserve empty commits"),
+			OPT_BOOLEAN(0, "allow-empty", &opts->allow_empty, "preserve initially empty commits"),
+			OPT_BOOLEAN(0, "keep-redundant-commits", &opts->keep_redundant_commits, "keep redundant, empty commits"),
 			OPT_END(),
 		};
 		if (parse_options_concat(options, ARRAY_SIZE(options), cp_extra))
@@ -139,6 +141,10 @@ static void parse_args(int argc, const char **argv, struct replay_opts *opts)
 				"--abort", rollback,
 				NULL);
 
+	/* keep_if_made_empty implies allow_empty */
+	if (opts->keep_redundant_commits)
+		opts->allow_empty = 1;
+
 	/* Set the subcommand */
 	if (remove_state)
 		opts->subcommand = REPLAY_REMOVE_STATE;
diff --git a/sequencer.c b/sequencer.c
index 71929ba..4e3af82 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -13,6 +13,7 @@
 #include "rerere.h"
 #include "merge-recursive.h"
 #include "refs.h"
+#include "argv-array.h"
 
 #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
 
@@ -251,6 +252,30 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
 	return !clean;
 }
 
+static int is_index_unchanged(void)
+{
+	unsigned char head_sha1[20];
+	struct commit *head_commit;
+
+	if (!resolve_ref_unsafe("HEAD", head_sha1, 1, NULL))
+		return error(_("Could not resolve HEAD commit\n"));
+
+	head_commit = lookup_commit(head_sha1);
+	if (!head_commit || parse_commit(head_commit))
+		return error(_("could not parse commit %s\n"),
+			     sha1_to_hex(head_commit->object.sha1));
+
+	if (!active_cache_tree)
+		active_cache_tree = cache_tree();
+
+	if (!cache_tree_fully_valid(active_cache_tree))
+		if (cache_tree_update(active_cache_tree, active_cache,
+				  active_nr, 0))
+			return error(_("Unable to update cache tree\n"));
+
+	return !hashcmp(active_cache_tree->sha1, head_commit->tree->object.sha1);
+}
+
 /*
  * If we are cherry-pick, and if the merge did not result in
  * hand-editing, we will hit this commit and inherit the original
@@ -260,24 +285,46 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
  */
 static int run_git_commit(const char *defmsg, struct replay_opts *opts)
 {
-	/* 7 is max possible length of our args array including NULL */
-	const char *args[7];
-	int i = 0;
+	struct argv_array array;
+	int rc;
+
+	argv_array_init(&array);
+	argv_array_push(&array, "commit");
+	argv_array_push(&array, "-n");
 
-	args[i++] = "commit";
-	args[i++] = "-n";
 	if (opts->signoff)
-		args[i++] = "-s";
+		argv_array_push(&array, "-s");
 	if (!opts->edit) {
-		args[i++] = "-F";
-		args[i++] = defmsg;
+		argv_array_push(&array, "-F");
+		argv_array_push(&array, defmsg);
 	}
+
 	if (opts->allow_empty)
-		args[i++] = "--allow-empty";
+		argv_array_push(&array, "--allow-empty");
 
-	args[i] = NULL;
+	rc = run_command_v_opt(array.argv, RUN_GIT_CMD);
+	argv_array_clear(&array);
+	return rc;
+}
+
+static int is_original_commit_empty(struct commit *commit)
+{
+	const unsigned char *ptree_sha1;
+
+	if (parse_commit(commit))
+		return error(_("Could not parse commit %s\n"),
+			     sha1_to_hex(commit->object.sha1));
+	if (commit->parents) {
+		struct commit *parent = commit->parents->item;
+		if (parse_commit(parent))
+			return error(_("Could not parse parent commit %s\n"),
+				sha1_to_hex(parent->object.sha1));
+		ptree_sha1 = parent->tree->object.sha1;
+	} else {
+		ptree_sha1 = EMPTY_TREE_SHA1_BIN; /* commit is root */
+	}
 
-	return run_command_v_opt(args, RUN_GIT_CMD);
+	return !hashcmp(ptree_sha1, commit->tree->object.sha1);
 }
 
 static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
@@ -289,6 +336,8 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
 	char *defmsg = NULL;
 	struct strbuf msgbuf = STRBUF_INIT;
 	int res;
+	int empty_commit;
+	int index_unchanged;
 
 	if (opts->no_commit) {
 		/*
@@ -414,6 +463,10 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
 		free_commit_list(remotes);
 	}
 
+	empty_commit = is_original_commit_empty(commit);
+	if (empty_commit < 0)
+		return empty_commit;
+
 	/*
 	 * If the merge was clean or if it failed due to conflict, we write
 	 * CHERRY_PICK_HEAD for the subsequent invocation of commit to use.
@@ -434,6 +487,25 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
 		print_advice(res == 1, opts);
 		rerere(opts->allow_rerere_auto);
 	} else {
+		index_unchanged = is_index_unchanged();
+		/*
+		 * If index_unchanged is less than 0, that indicates we either
+		 * couldn't parse HEAD or the index, so error out here.
+		 */
+		if (index_unchanged < 0)
+			return index_unchanged;
+
+		if (!empty_commit && !opts->keep_redundant_commits && index_unchanged)
+			/*
+			 * The head tree and the index match
+			 * meaning the commit is empty.  Since it wasn't created
+			 * empty (based on the previous test), we can conclude
+			 * the commit has been made redundant.  Since we don't
+			 * want to keep redundant commits, we can just return
+			 * here, skipping this commit
+			 */
+			return 0;
+
 		if (!opts->no_commit)
 			res = run_git_commit(defmsg, opts);
 	}
diff --git a/sequencer.h b/sequencer.h
index e2cd725..aa5f17c 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -30,6 +30,7 @@ struct replay_opts {
 	int allow_ff;
 	int allow_rerere_auto;
 	int allow_empty;
+	int keep_redundant_commits;
 
 	int mainline;
 
-- 
1.7.7.6

  parent reply	other threads:[~2012-04-20 14:37 UTC|newest]

Thread overview: 121+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-03-30 19:48 [PATCH 0/4] Enhance git-rebases flexibiilty in handling empty commits Neil Horman
2012-03-30 19:48 ` [PATCH 1/4] git-cherry-pick: add keep-empty option Neil Horman
2012-03-30 20:34   ` Junio C Hamano
2012-03-30 21:15   ` Jeff King
2012-03-31 12:57   ` Neil Horman
2012-03-30 19:48 ` [PATCH 2/4] git-rebase: add keep_empty flag Neil Horman
2012-03-30 20:43   ` Junio C Hamano
2012-03-31 12:59     ` Neil Horman
2012-03-30 19:48 ` [PATCH 3/4] git-commit-am: Allow automatic rebasing to preserve empty commits Neil Horman
2012-03-30 20:46   ` Junio C Hamano
2012-03-31 13:01     ` Neil Horman
2012-03-30 20:47   ` Junio C Hamano
2012-03-31 13:03     ` Neil Horman
2012-03-30 19:48 ` [PATCH 4/4] git-commit-interactive: Allow " Neil Horman
2012-03-30 20:59   ` Junio C Hamano
2012-03-31 13:11     ` Neil Horman
2012-03-30 20:32 ` [PATCH 0/4] Enhance git-rebases flexibiilty in handling " Junio C Hamano
2012-04-05 19:39 ` [PATCH 0/5] Enhance git-rebases flexibiilty handling empty commits [v2] Neil Horman
2012-04-05 19:39   ` [PATCH 1/5] argv-array: Add argv_array_pop function [v2] Neil Horman
2012-04-05 20:12     ` Junio C Hamano
2012-04-05 23:24       ` Neil Horman
2012-04-06  0:12         ` Neil Horman
2012-04-06  0:19         ` Jeff King
2012-04-06  1:12           ` Neil Horman
2012-04-06  1:20         ` Junio C Hamano
2012-04-06  2:20           ` Jeff King
2012-04-06  4:19             ` Cc tags in the commit message (Re: [PATCH 1/5] argv-array: Add argv_array_pop function [v2]) Jonathan Nieder
2012-04-06  6:55             ` [PATCH 1/5] argv-array: Add argv_array_pop function [v2] Junio C Hamano
2012-04-06  7:33               ` Jeff King
2012-04-06 16:49                 ` Junio C Hamano
2012-04-06 18:02             ` Neil Horman
2012-04-05 19:39   ` [PATCH 2/5] git-cherry-pick: add allow-empty option [v2] Neil Horman
2012-04-05 19:39   ` [PATCH 3/5] git-cherry-pick: Add ignore-if-made-empty " Neil Horman
2012-04-05 21:01     ` Junio C Hamano
2012-04-05 23:45       ` Neil Horman
2012-04-06  1:20         ` Junio C Hamano
2012-04-06 18:06       ` Neil Horman
2012-04-06 18:30     ` Johannes Sixt
2012-04-05 19:39   ` [PATCH 4/5] git-cherry-pick: Add test to validate new options [v2] Neil Horman
2012-04-05 19:39   ` [PATCH 5/5] git-rebase: add keep_empty flag [v2] Neil Horman
2012-04-10 15:47 ` [PATCH v3 0/4] Enhance git-rebases flexibiilty in handling empty commits Neil Horman
2012-04-10 15:47   ` [PATCH v3 1/4] git-cherry-pick: add allow-empty option Neil Horman
2012-04-10 16:45     ` Junio C Hamano
2012-04-10 18:13       ` Neil Horman
2012-04-10 19:32         ` Junio C Hamano
2012-04-10 20:00           ` Neil Horman
2012-04-10 20:32             ` Junio C Hamano
2012-04-10 20:39               ` Neil Horman
2012-04-10 21:09                 ` Junio C Hamano
2012-04-11  0:44                   ` Neil Horman
2012-04-11 16:52                     ` Junio C Hamano
2012-04-11 18:29                       ` Neil Horman
2012-04-11 18:50                         ` Junio C Hamano
2012-04-11 18:56                           ` Neil Horman
2012-04-10 15:47   ` [PATCH v3 2/4] git-cherry-pick: Add keep-redundant-commits option Neil Horman
2012-04-10 17:04     ` Junio C Hamano
2012-04-10 18:25       ` Neil Horman
2012-04-10 15:47   ` [PATCH v3 3/4] git-cherry-pick: Add test to validate new options Neil Horman
2012-04-10 15:47   ` [PATCH v3 4/4] git-rebase: add keep_empty flag Neil Horman
2012-04-13 18:45 ` [PATCH v5 0/4]Enhance git-rebases flexibiilty in handling empty commits Neil Horman
2012-04-13 18:45   ` [PATCH v5 1/4] git-cherry-pick: add allow-empty option Neil Horman
2012-04-13 18:45   ` [PATCH v5 2/4] git-cherry-pick: Add keep-redundant-commits option Neil Horman
2012-04-15 10:42     ` Clemens Buchacher
2012-04-16 15:38       ` Neil Horman
2012-04-16 22:10         ` Clemens Buchacher
2012-04-17 10:43           ` Neil Horman
2012-04-17 15:42           ` Junio C Hamano
2012-04-17 21:37             ` Clemens Buchacher
2012-04-18 10:41               ` Neil Horman
2012-04-13 18:45   ` [PATCH v5 3/4] git-cherry-pick: Add test to validate new options Neil Horman
2012-04-15  9:39     ` Clemens Buchacher
2012-04-16 11:04       ` Neil Horman
2012-04-16 16:14       ` Neil Horman
2012-04-16 16:35         ` Junio C Hamano
2012-04-16 16:50           ` Neil Horman
2012-04-16 21:42             ` Clemens Buchacher
2012-04-17 10:56               ` Neil Horman
2012-04-17 21:38                 ` Clemens Buchacher
2012-04-18 10:48                   ` Neil Horman
2012-04-18 18:34                     ` Clemens Buchacher
2012-04-13 18:45   ` [PATCH v5 4/4] git-rebase: add keep_empty flag Neil Horman
2012-04-15  9:33     ` Clemens Buchacher
2012-04-16 16:46       ` Neil Horman
2012-04-17 18:20 ` [PATCH v6 0/4] Enhance git-rebases flexibiilty in handling empty commits Neil Horman
2012-04-17 18:20   ` [PATCH v6 1/4] git-cherry-pick: add allow-empty option Neil Horman
2012-04-17 18:20   ` [PATCH v6 2/4] git-cherry-pick: Add keep-redundant-commits option Neil Horman
2012-04-17 21:45     ` Clemens Buchacher
2012-04-18 10:49       ` Neil Horman
2012-04-17 18:20   ` [PATCH v6 3/4] git-cherry-pick: Add test to validate new options Neil Horman
2012-04-17 18:20   ` [PATCH v6 4/4] git-rebase: add keep_empty flag Neil Horman
2012-04-17 21:47     ` Clemens Buchacher
2012-04-18 10:50       ` Neil Horman
2012-04-18 22:58     ` Junio C Hamano
2012-04-19 13:08       ` Neil Horman
2012-04-19 17:53         ` Junio C Hamano
2012-04-18 19:17 ` [PATCH v7 0/4] Enhance git-rebases flexibiilty in handling empty commits Neil Horman
2012-04-18 19:17   ` [PATCH v7 1/4] git-cherry-pick: add allow-empty option Neil Horman
2012-04-18 19:17   ` [PATCH v7 2/4] git-cherry-pick: Add keep-redundant-commits option Neil Horman
2012-04-18 22:59     ` Junio C Hamano
2012-04-18 19:17   ` [PATCH v7 3/4] git-cherry-pick: Add test to validate new options Neil Horman
2012-04-18 19:18   ` [PATCH v7 4/4] git-rebase: add keep_empty flag Neil Horman
2012-04-19 10:02     ` Zbigniew Jędrzejewski-Szmek
2012-04-19 11:49       ` Thomas Rast
2012-04-19 12:19         ` Zbigniew Jędrzejewski-Szmek
2012-04-19 13:12           ` Neil Horman
2012-04-19 18:59         ` Junio C Hamano
2012-04-19 19:05           ` Junio C Hamano
2012-04-20 10:35             ` Neil Horman
2012-04-20 14:36 ` [PATCH v8 0/4] Enhance git-rebases flexibiilty in handling empty commits Neil Horman
2012-04-20 14:36   ` [PATCH v8 1/4] git-cherry-pick: add allow-empty option Neil Horman
2012-04-20 14:36   ` Neil Horman [this message]
2012-04-20 19:21     ` [PATCH v8 2/4] git-cherry-pick: Add keep-redundant-commits option Junio C Hamano
2012-04-20 19:56       ` Neil Horman
2012-04-20 14:36   ` [PATCH v8 3/4] git-cherry-pick: Add test to validate new options Neil Horman
2012-04-20 14:36   ` [PATCH v8 4/4] git-rebase: add keep_empty flag Neil Horman
2012-04-25  1:50     ` Junio C Hamano
2012-04-25 10:38       ` Neil Horman
2012-07-18  6:20     ` Martin von Zweigbergk
2012-07-18  7:10       ` Johannes Sixt
2012-07-18  7:16         ` Martin von Zweigbergk
2012-07-18 12:17         ` Neil Horman

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1334932577-31232-3-git-send-email-nhorman@tuxdriver.com \
    --to=nhorman@tuxdriver.com \
    --cc=drizzd@aon.at \
    --cc=git@vger.kernel.org \
    --cc=gitster@pobox.com \
    --cc=phil.hord@gmail.com \
    --cc=zbyszek@in.waw.pl \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).