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 */
next prev 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$(echo .)net \
--cc=git@vger$(echo .)kernel.org \
--cc=kernel-hacker@bennee$(echo .)com \
--cc=torvalds@linux-foundation$(echo .)org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
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