Built-in cherry
authorRene Scharfe <rene.scharfe@lsrfire.ath.cx>
Mon, 23 Oct 2006 23:01:57 +0000 (01:01 +0200)
committerJunio C Hamano <junkio@cox.net>
Tue, 24 Oct 2006 03:25:32 +0000 (20:25 -0700)
This replaces the shell script git-cherry with a version written in C.

The behaviour of the new version differs from the original in two
points: it has no long help any more, and it is handling the (optional)
third parameter a bit differently.  Basically, it does the equivalent
of

   ours=`git-rev-list $ours ^$limit ^$upstream`

instead of

   ours=`git-rev-list $ours ^$limit`

Signed-off-by: Rene Scharfe <rene.scharfe@lsrfire.ath.cx>
Signed-off-by: Junio C Hamano <junkio@cox.net>
Makefile
builtin-log.c
builtin.h
git-cherry.sh [deleted file]
git.c

index 66c8b4b127cc5c0380ab7d8cb5f4d1dcb89b18a7..2b53747df13cf74e4fef3596ea1f42dba52c2e18 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -157,7 +157,7 @@ BASIC_LDFLAGS =
 
 SCRIPT_SH = \
        git-bisect.sh git-branch.sh git-checkout.sh \
-       git-cherry.sh git-clean.sh git-clone.sh git-commit.sh \
+       git-clean.sh git-clone.sh git-commit.sh \
        git-fetch.sh \
        git-ls-remote.sh \
        git-merge-one-file.sh git-parse-remote.sh \
@@ -210,7 +210,7 @@ PROGRAMS = \
 EXTRA_PROGRAMS =
 
 BUILT_INS = \
-       git-format-patch$X git-show$X git-whatchanged$X \
+       git-format-patch$X git-show$X git-whatchanged$X git-cherry$X \
        git-get-tar-commit-id$X \
        $(patsubst builtin-%.o,git-%$X,$(BUILTIN_OBJS))
 
index 9d1ceae44c6a449d2329e897b9705a5b457187f0..fc5e47606c65962a7a1c1ab37a77f21f185a3bd5 100644 (file)
@@ -437,3 +437,109 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        return 0;
 }
 
+static int add_pending_commit(const char *arg, struct rev_info *revs, int flags)
+{
+       unsigned char sha1[20];
+       if (get_sha1(arg, sha1) == 0) {
+               struct commit *commit = lookup_commit_reference(sha1);
+               if (commit) {
+                       commit->object.flags |= flags;
+                       add_pending_object(revs, &commit->object, arg);
+                       return 0;
+               }
+       }
+       return -1;
+}
+
+static const char cherry_usage[] =
+"git-cherry [-v] <upstream> [<head>] [<limit>]";
+int cmd_cherry(int argc, const char **argv, const char *prefix)
+{
+       struct rev_info revs;
+       struct diff_options patch_id_opts;
+       struct commit *commit;
+       struct commit_list *list = NULL;
+       const char *upstream;
+       const char *head = "HEAD";
+       const char *limit = NULL;
+       int verbose = 0;
+
+       if (argc > 1 && !strcmp(argv[1], "-v")) {
+               verbose = 1;
+               argc--;
+               argv++;
+       }
+
+       switch (argc) {
+       case 4:
+               limit = argv[3];
+               /* FALLTHROUGH */
+       case 3:
+               head = argv[2];
+               /* FALLTHROUGH */
+       case 2:
+               upstream = argv[1];
+               break;
+       default:
+               usage(cherry_usage);
+       }
+
+       init_revisions(&revs, prefix);
+       revs.diff = 1;
+       revs.combine_merges = 0;
+       revs.ignore_merges = 1;
+       revs.diffopt.recursive = 1;
+
+       if (add_pending_commit(head, &revs, 0))
+               die("Unknown commit %s", head);
+       if (add_pending_commit(upstream, &revs, UNINTERESTING))
+               die("Unknown commit %s", upstream);
+
+       /* Don't say anything if head and upstream are the same. */
+       if (revs.pending.nr == 2) {
+               struct object_array_entry *o = revs.pending.objects;
+               if (hashcmp(o[0].item->sha1, o[1].item->sha1) == 0)
+                       return 0;
+       }
+
+       get_patch_ids(&revs, &patch_id_opts, prefix);
+
+       if (limit && add_pending_commit(limit, &revs, UNINTERESTING))
+               die("Unknown commit %s", limit);
+
+       /* reverse the list of commits */
+       prepare_revision_walk(&revs);
+       while ((commit = get_revision(&revs)) != NULL) {
+               /* ignore merges */
+               if (commit->parents && commit->parents->next)
+                       continue;
+
+               commit_list_insert(commit, &list);
+       }
+
+       while (list) {
+               unsigned char sha1[20];
+               char sign = '+';
+
+               commit = list->item;
+               if (!get_patch_id(commit, &patch_id_opts, sha1) &&
+                   lookup_object(sha1))
+                       sign = '-';
+
+               if (verbose) {
+                       static char buf[16384];
+                       pretty_print_commit(CMIT_FMT_ONELINE, commit, ~0,
+                                           buf, sizeof(buf), 0, NULL, NULL, 0);
+                       printf("%c %s %s\n", sign,
+                              sha1_to_hex(commit->object.sha1), buf);
+               }
+               else {
+                       printf("%c %s\n", sign,
+                              sha1_to_hex(commit->object.sha1));
+               }
+
+               list = list->next;
+       }
+
+       return 0;
+}
index f71b9629b9c8671dd17296fabac7e1952b3fb9e9..285d98fd9fc877874c612e5d4407b1db5096867a 100644 (file)
--- a/builtin.h
+++ b/builtin.h
@@ -19,6 +19,7 @@ extern int cmd_archive(int argc, const char **argv, const char *prefix);
 extern int cmd_cat_file(int argc, const char **argv, const char *prefix);
 extern int cmd_checkout_index(int argc, const char **argv, const char *prefix);
 extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix);
+extern int cmd_cherry(int argc, const char **argv, const char *prefix);
 extern int cmd_commit_tree(int argc, const char **argv, const char *prefix);
 extern int cmd_count_objects(int argc, const char **argv, const char *prefix);
 extern int cmd_diff_files(int argc, const char **argv, const char *prefix);
diff --git a/git-cherry.sh b/git-cherry.sh
deleted file mode 100755 (executable)
index 8832573..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2005 Junio C Hamano.
-#
-
-USAGE='[-v] <upstream> [<head>] [<limit>]'
-LONG_USAGE='             __*__*__*__*__> <upstream>
-            /
-  fork-point
-            \__+__+__+__+__+__+__+__> <head>
-
-Each commit between the fork-point (or <limit> if given) and <head> is
-examined, and compared against the change each commit between the
-fork-point and <upstream> introduces.  If the change seems to be in
-the upstream, it is shown on the standard output with prefix "+".
-Otherwise it is shown with prefix "-".'
-. git-sh-setup
-
-case "$1" in -v) verbose=t; shift ;; esac 
-
-case "$#,$1" in
-1,*..*)
-    upstream=$(expr "z$1" : 'z\(.*\)\.\.') ours=$(expr "z$1" : '.*\.\.\(.*\)$')
-    set x "$upstream" "$ours"
-    shift ;;
-esac
-
-case "$#" in
-1) upstream=`git-rev-parse --verify "$1"` &&
-   ours=`git-rev-parse --verify HEAD` || exit
-   limit="$upstream"
-   ;;
-2) upstream=`git-rev-parse --verify "$1"` &&
-   ours=`git-rev-parse --verify "$2"` || exit
-   limit="$upstream"
-   ;;
-3) upstream=`git-rev-parse --verify "$1"` &&
-   ours=`git-rev-parse --verify "$2"` &&
-   limit=`git-rev-parse --verify "$3"` || exit
-   ;;
-*) usage ;;
-esac
-
-# Note that these list commits in reverse order;
-# not that the order in inup matters...
-inup=`git-rev-list ^$ours $upstream` &&
-ours=`git-rev-list $ours ^$limit` || exit
-
-tmp=.cherry-tmp$$
-patch=$tmp-patch
-mkdir $patch
-trap "rm -rf $tmp-*" 0 1 2 3 15
-
-for c in $inup
-do
-       git-diff-tree -p $c
-done | git-patch-id |
-while read id name
-do
-       echo $name >>$patch/$id
-done
-
-LF='
-'
-
-O=
-for c in $ours
-do
-       set x `git-diff-tree -p $c | git-patch-id`
-       if test "$2" != ""
-       then
-               if test -f "$patch/$2"
-               then
-                       sign=-
-               else
-                       sign=+
-               fi
-               case "$verbose" in
-               t)
-                       c=$(git-rev-list --pretty=oneline --max-count=1 $c)
-               esac
-               case "$O" in
-               '')     O="$sign $c" ;;
-               *)      O="$sign $c$LF$O" ;;
-               esac
-       fi
-done
-case "$O" in
-'') ;;
-*)  echo "$O" ;;
-esac
diff --git a/git.c b/git.c
index e089b53571cd8776ab208fe14b87af53b7057739..f8c991ff8c931cdbcdcc3f1b63ee7f5a44bbc537 100644 (file)
--- a/git.c
+++ b/git.c
@@ -224,6 +224,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
                { "cat-file", cmd_cat_file, RUN_SETUP },
                { "checkout-index", cmd_checkout_index, RUN_SETUP },
                { "check-ref-format", cmd_check_ref_format },
+               { "cherry", cmd_cherry, RUN_SETUP },
                { "commit-tree", cmd_commit_tree, RUN_SETUP },
                { "count-objects", cmd_count_objects, RUN_SETUP },
                { "diff", cmd_diff, RUN_SETUP | USE_PAGER },