All of lore.kernel.org
 help / color / mirror / Atom feed
From: Junio C Hamano <junkio@cox.net>
To: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Alex Bennee <kernel-hacker@bennee.com>, git@vger.kernel.org
Subject: [PATCH 1/2] git-log --cherry-pick
Date: Mon, 09 Apr 2007 04:07:46 -0700	[thread overview]
Message-ID: <7virc524cd.fsf_-_@assigned-by-dhcp.cox.net> (raw)
In-Reply-To: <7vircbwfym.fsf@assigned-by-dhcp.cox.net> (Junio C. Hamano's message of "Wed, 04 Apr 2007 22:25:05 -0700")

This is meant to be a saner replacement for "git-cherry".

When used with "A...B", this filters out commits whose patch
text has the same patch-id as a commit on the other side.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---

  Junio C Hamano <junkio@cox.net> writes:

  > Funny.
  >
  > Last night I was thinking about git-cherry, as it is one of the
  > few commands that have "funny parameter semantics that do not
  > mesh well with git-log family" (others are format-patch and
  > rebase).
  >
  > I think we should be able to use --left-right and ... operator
  > to express what the above cherry does with something like:
  >
  >     $ git log --left-right --ignore-common-patch cvs-upstream...my-branch
  >
  > The --ignore-common-patch option does not exist yet, but the
  > basic code to implement it should already be accessible from the
  > log family, as that is what format-patch needs to do.

 revision.c |  141 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 revision.h |    1 +
 2 files changed, 142 insertions(+), 0 deletions(-)

diff --git a/revision.c b/revision.c
index 486393c..0903f19 100644
--- a/revision.c
+++ b/revision.c
@@ -422,6 +422,139 @@ static void add_parents_to_list(struct rev_info *revs, struct commit *commit, st
 	}
 }
 
+/*
+ * This needs to be moved from builtin-log -- its get_patch_ids() implementation
+ * is horrible -- it pollutes the object array with non objects!
+ */
+static int get_patch_id(struct commit *commit, struct diff_options *options,
+		unsigned char *sha1)
+{
+	if (commit->parents)
+		diff_tree_sha1(commit->parents->item->object.sha1,
+		               commit->object.sha1, "", options);
+	else
+		diff_root_tree_sha1(commit->object.sha1, "", options);
+	diffcore_std(options);
+	return diff_flush_patch_id(options, sha1);
+}
+
+struct patch_id_ent {
+	unsigned char patch_id[20];
+	char seen;
+};
+
+static int compare_patch_id(const void *a_, const void *b_)
+{
+	struct patch_id_ent *a = *((struct patch_id_ent **)a_);
+	struct patch_id_ent *b = *((struct patch_id_ent **)b_);
+	return hashcmp(a->patch_id, b->patch_id);
+}
+
+static void cherry_pick_list(struct commit_list *list)
+{
+	struct commit_list *p;
+	int left_count = 0, right_count = 0, nr;
+	struct patch_id_ent *patches, **table;
+	int left_first, table_size;
+	struct diff_options opts;
+
+	/* First count the commits on the left and on the right */
+	for (p = list; p; p = p->next) {
+		struct commit *commit = p->item;
+		unsigned flags = commit->object.flags;
+		if (flags & BOUNDARY)
+			;
+		else if (flags & SYMMETRIC_LEFT)
+			left_count++;
+		else
+			right_count++;
+	}
+
+	left_first = left_count < right_count;
+	table_size = left_first ? left_count : right_count;
+
+	/* Allocate a look-up table to help matching up */
+	patches = xcalloc(table_size, sizeof(struct patch_id_ent));
+	table = xcalloc(table_size, sizeof(struct patch_id_ent *));
+	nr = 0;
+
+	diff_setup(&opts);
+	opts.recursive = 1;
+	if (diff_setup_done(&opts) < 0)
+		die("diff_setup_done failed");
+
+	/* Compute patch-ids for one side */
+	for (p = list; p; p = p->next) {
+		struct commit *commit = p->item;
+		unsigned flags = commit->object.flags;
+
+		if (flags & BOUNDARY)
+			continue;
+		/*
+		 * If we have fewer left, left_first is set and we omit
+		 * commits on the right branch in this loop.  If we have
+		 * fewer right, we skip the left ones.
+		 */
+		if (left_first != !!(flags & SYMMETRIC_LEFT))
+			continue;
+		if (get_patch_id(commit, &opts, patches[nr].patch_id))
+			continue;
+		/*
+		 * FIXME: this does not really work if the side
+		 * we are dealing with have two commits with the same
+		 * patch id, as we end up having two entries in the
+		 * patch table.
+		 */
+		table[nr] = &(patches[nr]);
+		commit->util = table[nr];
+		nr++;
+	}
+	qsort(table, nr, sizeof(table[0]), compare_patch_id);
+
+	/* Check the other side */
+	for (p = list; p; p = p->next) {
+		struct commit *commit = p->item;
+		unsigned flags = commit->object.flags;
+		struct patch_id_ent ent, *entp = &ent, **found;
+
+		if (flags & BOUNDARY)
+			continue;
+		/*
+		 * If we have fewer left, left_first is set and we omit
+		 * commits on the left branch in this loop.
+		 */
+		if (left_first == !!(flags & SYMMETRIC_LEFT))
+			continue;
+		if (get_patch_id(commit, &opts, ent.patch_id))
+			continue;
+		/*
+		 * Have we seen the same patch id?
+		 */
+		found = bsearch(&entp, table, nr, sizeof(table[0]),
+				compare_patch_id);
+		if (!found)
+			continue;
+		(*found)->seen = 1;
+		commit->object.flags |= SHOWN; /* exclude this from the output set */
+	}
+
+	/* Now check the original side for seen ones */
+	for (p = list; p; p = p->next) {
+		struct commit *commit = p->item;
+		struct patch_id_ent *ent;
+
+		ent = commit->util;
+		if (!ent)
+			continue;
+		if (ent->seen)
+			commit->object.flags |= SHOWN;
+		commit->util = NULL;
+	}
+
+	free(table);
+	free(patches);
+}
+
 static void limit_list(struct rev_info *revs)
 {
 	struct commit_list *list = revs->commits;
@@ -449,6 +582,9 @@ static void limit_list(struct rev_info *revs)
 			continue;
 		p = &commit_list_insert(commit, p)->next;
 	}
+	if (revs->cherry_pick)
+		cherry_pick_list(newlist);
+
 	revs->commits = newlist;
 }
 
@@ -913,6 +1049,11 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
 				revs->left_right = 1;
 				continue;
 			}
+			if (!strcmp(arg, "--cherry-pick")) {
+				revs->cherry_pick = 1;
+				revs->left_right = 1;
+				continue;
+			}
 			if (!strcmp(arg, "--objects")) {
 				revs->tag_objects = 1;
 				revs->tree_objects = 1;
diff --git a/revision.h b/revision.h
index 55e6b53..b69624a 100644
--- a/revision.h
+++ b/revision.h
@@ -47,6 +47,7 @@ struct rev_info {
 			left_right:1,
 			parents:1,
 			reverse:1,
+			cherry_pick:1,
 			first_parent_only:1;
 
 	/* Diff flags */

  parent reply	other threads:[~2007-04-09 11:07 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2007-04-04 11:36 How can I easily verify my diffs are in parent branch? Alex Bennee
2007-04-04 12:28 ` Alex Riesen
2007-04-04 12:56 ` Andy Parkins
2007-04-04 15:12 ` Linus Torvalds
2007-04-05  5:25   ` Junio C Hamano
2007-04-05  9:16     ` David Kågedal
2007-04-05 10:24       ` Junio C Hamano
2007-04-05 14:53         ` [PATCH] Document --left-right option to rev-list Brian Gernhardt
2007-04-05 21:37           ` Junio C Hamano
2007-04-07 10:54           ` Alex Riesen
2007-04-09 11:07     ` Junio C Hamano [this message]
2007-04-10 22:39       ` [PATCH 1/4] Add %m to '--pretty=format:' Junio C Hamano
2007-04-10 22:39       ` [PATCH 2/4] Refactor patch-id filtering out of git-cherry and git-format-patch Junio C Hamano
2007-04-14  8:57         ` Johannes Schindelin
2007-04-10 22:40       ` [PATCH 3/4] git-log --cherry-pick A...B Junio C Hamano
2007-04-10 22:41       ` [PATCH 4/4] Documentation: --cherry-pick Junio C Hamano
2007-04-09 11:11     ` [PATCH 2/2] Add %m to '--pretty=format:' Junio C Hamano
2007-04-11 11:37   ` How can I easily verify my diffs are in parent branch? Alex Bennee
2007-04-11 16:00     ` Linus Torvalds

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=7virc524cd.fsf_-_@assigned-by-dhcp.cox.net \
    --to=junkio@cox.net \
    --cc=git@vger.kernel.org \
    --cc=kernel-hacker@bennee.com \
    --cc=torvalds@linux-foundation.org \
    /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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.