From 269defdf30a4beb48f230e36f20d00ee40348ab6 Mon Sep 17 00:00:00 2001 From: Jake Goulding Date: Mon, 26 Jan 2009 09:13:23 -0500 Subject: Make opt_parse_with_commit() non-static Moving opt_parse_with_commit() from branch to a common location, in preparation for using it in tag. Rename it to match naming convention of other option parsing functions. Signed-off-by: Jake Goulding Signed-off-by: Junio C Hamano --- builtin-branch.c | 20 ++------------------ parse-options.c | 17 +++++++++++++++++ parse-options.h | 1 + 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/builtin-branch.c b/builtin-branch.c index 02fa38fd3..82d6fb27c 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -466,22 +466,6 @@ static void rename_branch(const char *oldname, const char *newname, int force) strbuf_release(&newsection); } -static int opt_parse_with_commit(const struct option *opt, const char *arg, int unset) -{ - unsigned char sha1[20]; - struct commit *commit; - - if (!arg) - return -1; - if (get_sha1(arg, sha1)) - die("malformed object name %s", arg); - commit = lookup_commit_reference(sha1); - if (!commit) - die("no such commit %s", arg); - commit_list_insert(commit, opt->value); - return 0; -} - static int opt_parse_merge_filter(const struct option *opt, const char *arg, int unset) { merge_filter = ((opt->long_name[0] == 'n') @@ -517,13 +501,13 @@ int cmd_branch(int argc, const char **argv, const char *prefix) OPTION_CALLBACK, 0, "contains", &with_commit, "commit", "print only branches that contain the commit", PARSE_OPT_LASTARG_DEFAULT, - opt_parse_with_commit, (intptr_t)"HEAD", + parse_opt_with_commit, (intptr_t)"HEAD", }, { OPTION_CALLBACK, 0, "with", &with_commit, "commit", "print only branches that contain the commit", PARSE_OPT_HIDDEN | PARSE_OPT_LASTARG_DEFAULT, - opt_parse_with_commit, (intptr_t) "HEAD", + parse_opt_with_commit, (intptr_t) "HEAD", }, OPT__ABBREV(&abbrev), diff --git a/parse-options.c b/parse-options.c index 9eb55cc8b..4c5d09dd2 100644 --- a/parse-options.c +++ b/parse-options.c @@ -1,6 +1,7 @@ #include "git-compat-util.h" #include "parse-options.h" #include "cache.h" +#include "commit.h" #define OPT_SHORT 1 #define OPT_UNSET 2 @@ -506,6 +507,22 @@ int parse_opt_verbosity_cb(const struct option *opt, const char *arg, return 0; } +int parse_opt_with_commit(const struct option *opt, const char *arg, int unset) +{ + unsigned char sha1[20]; + struct commit *commit; + + if (!arg) + return -1; + if (get_sha1(arg, sha1)) + return error("malformed object name %s", arg); + commit = lookup_commit_reference(sha1); + if (!commit) + return error("no such commit %s", arg); + commit_list_insert(commit, opt->value); + return 0; +} + /* * This should really be OPTION_FILENAME type as a part of * parse_options that take prefix to do this while parsing. diff --git a/parse-options.h b/parse-options.h index 034162ec6..912290549 100644 --- a/parse-options.h +++ b/parse-options.h @@ -151,6 +151,7 @@ extern int parse_options_end(struct parse_opt_ctx_t *ctx); extern int parse_opt_abbrev_cb(const struct option *, const char *, int); extern int parse_opt_approxidate_cb(const struct option *, const char *, int); extern int parse_opt_verbosity_cb(const struct option *, const char *, int); +extern int parse_opt_with_commit(const struct option *, const char *, int); #define OPT__VERBOSE(var) OPT_BOOLEAN('v', "verbose", (var), "be verbose") #define OPT__QUIET(var) OPT_BOOLEAN('q', "quiet", (var), "be quiet") -- cgit v1.2.1 From 7fcdb36e29f9a5e779bc9e44cd69f8f69fac9426 Mon Sep 17 00:00:00 2001 From: Jake Goulding Date: Mon, 26 Jan 2009 09:13:24 -0500 Subject: Make has_commit() non-static Move has_commit() from branch to a common location, in preparation for using it in "git-tag". Rename it to is_descendant_of() to make it more unique and descriptive. Signed-off-by: Jake Goulding Signed-off-by: Junio C Hamano --- builtin-branch.c | 20 +++----------------- commit.c | 15 +++++++++++++++ commit.h | 1 + 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/builtin-branch.c b/builtin-branch.c index 82d6fb27c..56a1971d6 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -193,21 +193,6 @@ struct ref_list { int kinds; }; -static int has_commit(struct commit *commit, struct commit_list *with_commit) -{ - if (!with_commit) - return 1; - while (with_commit) { - struct commit *other; - - other = with_commit->item; - with_commit = with_commit->next; - if (in_merge_bases(other, &commit, 1)) - return 1; - } - return 0; -} - static int append_ref(const char *refname, const unsigned char *sha1, int flags, void *cb_data) { struct ref_list *ref_list = (struct ref_list*)(cb_data); @@ -231,7 +216,7 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags, return error("branch '%s' does not point at a commit", refname); /* Filter with with_commit if specified */ - if (!has_commit(commit, ref_list->with_commit)) + if (!is_descendant_of(commit, ref_list->with_commit)) return 0; /* Don't add types the caller doesn't want */ @@ -401,7 +386,8 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev, str qsort(ref_list.list, ref_list.index, sizeof(struct ref_item), ref_cmp); detached = (detached && (kinds & REF_LOCAL_BRANCH)); - if (detached && head_commit && has_commit(head_commit, with_commit)) { + if (detached && head_commit && + is_descendant_of(head_commit, with_commit)) { struct ref_item item; item.name = xstrdup("(no branch)"); item.kind = REF_LOCAL_BRANCH; diff --git a/commit.c b/commit.c index c99db162a..aa3b35b6a 100644 --- a/commit.c +++ b/commit.c @@ -705,6 +705,21 @@ struct commit_list *get_merge_bases(struct commit *one, struct commit *two, return get_merge_bases_many(one, 1, &two, cleanup); } +int is_descendant_of(struct commit *commit, struct commit_list *with_commit) +{ + if (!with_commit) + return 1; + while (with_commit) { + struct commit *other; + + other = with_commit->item; + with_commit = with_commit->next; + if (in_merge_bases(other, &commit, 1)) + return 1; + } + return 0; +} + int in_merge_bases(struct commit *commit, struct commit **reference, int num) { struct commit_list *bases, *b; diff --git a/commit.h b/commit.h index 3a7b06a82..ba9f63813 100644 --- a/commit.h +++ b/commit.h @@ -133,6 +133,7 @@ extern int is_repository_shallow(void); extern struct commit_list *get_shallow_commits(struct object_array *heads, int depth, int shallow_flag, int not_shallow_flag); +int is_descendant_of(struct commit *, struct commit_list *); int in_merge_bases(struct commit *, struct commit **, int); extern int interactive_add(int argc, const char **argv, const char *prefix); -- cgit v1.2.1 From 32c35cfb1e9c8523b9d60e5095f1c49ebaef0279 Mon Sep 17 00:00:00 2001 From: Jake Goulding Date: Mon, 26 Jan 2009 09:13:25 -0500 Subject: git-tag: Add --contains option This functions similarly to "git branch --contains"; it will show all tags that contain the specified commit, by sharing the same logic. The patch also adds documentation and tests for the new option. Signed-off-by: Jake Goulding Signed-off-by: Junio C Hamano --- Documentation/git-tag.txt | 5 +- builtin-tag.c | 29 +++++++++++- t/t7004-tag.sh | 115 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 146 insertions(+), 3 deletions(-) diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt index e44f54302..533d18bbd 100644 --- a/Documentation/git-tag.txt +++ b/Documentation/git-tag.txt @@ -12,7 +12,7 @@ SYNOPSIS 'git tag' [-a | -s | -u ] [-f] [-m | -F ] [ | ] 'git tag' -d ... -'git tag' [-n[]] -l [] +'git tag' [-n[]] -l [--contains ] [] 'git tag' -v ... DESCRIPTION @@ -68,6 +68,9 @@ OPTIONS List tags with names that match the given pattern (or all if no pattern is given). Typing "git tag" without arguments, also lists all tags. +--contains :: + Only list tags which contain the specified commit. + -m :: Use the given tag message (instead of prompting). If multiple `-m` options are given, their values are diff --git a/builtin-tag.c b/builtin-tag.c index a39849989..01e73747d 100644 --- a/builtin-tag.c +++ b/builtin-tag.c @@ -26,6 +26,7 @@ static char signingkey[1000]; struct tag_filter { const char *pattern; int lines; + struct commit_list *with_commit; }; #define PGP_SIGNATURE "-----BEGIN PGP SIGNATURE-----" @@ -42,6 +43,16 @@ static int show_reference(const char *refname, const unsigned char *sha1, char *buf, *sp, *eol; size_t len; + if (filter->with_commit) { + struct commit *commit; + + commit = lookup_commit_reference_gently(sha1, 1); + if (!commit) + return 0; + if (!is_descendant_of(commit, filter->with_commit)) + return 0; + } + if (!filter->lines) { printf("%s\n", refname); return 0; @@ -79,7 +90,8 @@ static int show_reference(const char *refname, const unsigned char *sha1, return 0; } -static int list_tags(const char *pattern, int lines) +static int list_tags(const char *pattern, int lines, + struct commit_list *with_commit) { struct tag_filter filter; @@ -88,6 +100,7 @@ static int list_tags(const char *pattern, int lines) filter.pattern = pattern; filter.lines = lines; + filter.with_commit = with_commit; for_each_tag_ref(show_reference, (void *) &filter); @@ -360,6 +373,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix) list = 0, delete = 0, verify = 0; const char *msgfile = NULL, *keyid = NULL; struct msg_arg msg = { 0, STRBUF_INIT }; + struct commit_list *with_commit = NULL; struct option options[] = { OPT_BOOLEAN('l', NULL, &list, "list tag names"), { OPTION_INTEGER, 'n', NULL, &lines, NULL, @@ -378,6 +392,14 @@ int cmd_tag(int argc, const char **argv, const char *prefix) OPT_STRING('u', NULL, &keyid, "key-id", "use another key to sign the tag"), OPT_BOOLEAN('f', NULL, &force, "replace the tag if exists"), + + OPT_GROUP("Tag listing options"), + { + OPTION_CALLBACK, 0, "contains", &with_commit, "commit", + "print only tags that contain the commit", + PARSE_OPT_LASTARG_DEFAULT, + parse_opt_with_commit, (intptr_t)"HEAD", + }, OPT_END() }; @@ -402,9 +424,12 @@ int cmd_tag(int argc, const char **argv, const char *prefix) if (list + delete + verify > 1) usage_with_options(git_tag_usage, options); if (list) - return list_tags(argv[0], lines == -1 ? 0 : lines); + return list_tags(argv[0], lines == -1 ? 0 : lines, + with_commit); if (lines != -1) die("-n option is only allowed with -l."); + if (with_commit) + die("--contains option is only allowed with -l."); if (delete) return for_each_tag_name(argv, delete_tag); if (verify) diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh index f377fea4b..69501e271 100755 --- a/t/t7004-tag.sh +++ b/t/t7004-tag.sh @@ -1090,6 +1090,121 @@ test_expect_success 'filename for the message is relative to cwd' ' git cat-file tag tag-from-subdir-2 | grep "in sub directory" ' +# create a few more commits to test --contains + +hash1=$(git rev-parse HEAD) + +test_expect_success 'creating second commit and tag' ' + echo foo-2.0 >foo && + git add foo && + git commit -m second + git tag v2.0 +' + +hash2=$(git rev-parse HEAD) + +test_expect_success 'creating third commit without tag' ' + echo foo-dev >foo && + git add foo && + git commit -m third +' + +hash3=$(git rev-parse HEAD) + +# simple linear checks of --continue + +cat > expected <actual + test_cmp expected actual +" + +# other ways of specifying the commit +test_expect_success 'checking that first commit is in all tags (tag)' " + git tag -l --contains v1.0 v* >actual + test_cmp expected actual +" + +test_expect_success 'checking that first commit is in all tags (relative)' " + git tag -l --contains HEAD~2 v* >actual + test_cmp expected actual +" + +cat > expected <actual + test_cmp expected actual +" + + +cat > expected <actual + test_cmp expected actual +" + +# how about a simple merge? + +test_expect_success 'creating simple branch' ' + git branch stable v2.0 && + git checkout stable && + echo foo-3.0 > foo && + git commit foo -m fourth + git tag v3.0 +' + +hash4=$(git rev-parse HEAD) + +cat > expected <actual + test_cmp expected actual +" + +test_expect_success 'merging original branch into this branch' ' + git merge --strategy=ours master && + git tag v4.0 +' + +cat > expected <actual + test_cmp expected actual +" + +cat > expected <actual + test_cmp expected actual +" + # mixing modes and options: test_expect_success 'mixing incompatibles modes and options is forbidden' ' -- cgit v1.2.1