This is a very naive attempt at teaching git cherry to restrict
attention to a given pathspec. Very much RFC, hence no documentation
update or test cases for now.

The first problem is the existing signature, with between 0 and 3
arguments. In order to preserve having defaults for limit, head,
upstream, I decided to make use of -- mandatory. The alternative seems
to be to only take a pathspec as arguments 4+, forcing the users to
write out the default values for head and/or limit (and how does one
actually write the default "no limit"?).

At first I just tried parse_pathspec directly to &revs.prune_data, but
that didn't seem to have any effect, and from staring at revision.c for
a while, it seems that one has to go through setup_revisions(). But that
fits well with insisting on the -- above, since we then have an argv[]
that ensures we never hit the handle_revision_arg() or
handle_revision_pseudo_opt() calls.

The motivation for this is that I'm in the process of switching a BSP
from a vendor kernel to one much closer to mainline's master branch. I
do need to apply/port some out-of-tree patches from the vendor kernel,
but it's much more managable if one can do a per-subsystem survey of
what might need porting. So I naively started by doing

  git cherry -v linus/master LSDK-17.12-V4.9 v4.9.62 -- drivers/mtd/spi-nor/

assuming that would Just Work. I was somewhat surprised that this didn't
give any output, but digging into the source revealed that (1) there
isn't actually any pathspec handling and (2) any argc other than 1..3 is
treated as 0 - something which is probably also worth fixing.

With this patch, the command above does give something reasonable, and
t3500-cherry.sh also seems to pass.

Signed-off-by: Rasmus Villemoes <r...@rasmusvillemoes.dk>
---
 builtin/log.c | 33 +++++++++++++++++++++++++++------
 1 file changed, 27 insertions(+), 6 deletions(-)

diff --git a/builtin/log.c b/builtin/log.c
index 46b4ca13e..2d4534b5c 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -1890,6 +1890,8 @@ int cmd_cherry(int argc, const char **argv, const char 
*prefix)
        const char *head = "HEAD";
        const char *limit = NULL;
        int verbose = 0, abbrev = 0;
+       int pathspec_argc = 0;
+       int i;
 
        struct option options[] = {
                OPT__ABBREV(&abbrev),
@@ -1897,17 +1899,27 @@ int cmd_cherry(int argc, const char **argv, const char 
*prefix)
                OPT_END()
        };
 
-       argc = parse_options(argc, argv, prefix, options, cherry_usage, 0);
+       argc = parse_options(argc, argv, prefix, options, cherry_usage,
+                            PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_UNKNOWN |
+                            PARSE_OPT_KEEP_DASHDASH);
+
+       for (i = 1; i < argc; ++i) {
+               if (!strcmp(argv[i], "--")) {
+                       pathspec_argc = 1 + argc - i;
+                       argc = i;
+                       break;
+               }
+       }
 
        switch (argc) {
+       case 4:
+               limit = argv[3];
+               /* FALLTHROUGH */
        case 3:
-               limit = argv[2];
+               head = argv[2];
                /* FALLTHROUGH */
        case 2:
-               head = argv[1];
-               /* FALLTHROUGH */
-       case 1:
-               upstream = argv[0];
+               upstream = argv[1];
                break;
        default:
                current_branch = branch_get(NULL);
@@ -1921,6 +1933,15 @@ int cmd_cherry(int argc, const char **argv, const char 
*prefix)
        }
 
        init_revisions(&revs, prefix);
+       if (pathspec_argc) {
+               /*
+                * hack: this reuses the element of argv before "--"
+                * as argv[0] - setup_revisions assumes argv[0] is
+                * present and ignores it, and starts its search for
+                * "--" at argv[1].
+                */
+               setup_revisions(pathspec_argc, &argv[argc-1], &revs, NULL);
+       }
        revs.max_parents = 1;
 
        if (add_pending_commit(head, &revs, 0))
-- 
2.15.1

Reply via email to