From cae291c57141a70db8324b1357d00b32a97b3c71 Mon Sep 17 00:00:00 2001 From: Felipe Contreras Date: Thu, 29 Aug 2013 22:37:35 -0500 Subject: [PATCH] revision: add --except option So that it's possible to remove certain refs from the list without removing the objects that are referenced by other refs. For example this repository: C (crap) B (test) A (HEAD, master) When using '--branches --except crap': B (test) A (HEAD, master) But when using '--branches --not crap' nothing will come out. Signed-off-by: Felipe Contreras --- Documentation/git-rev-parse.txt | 6 ++ contrib/completion/git-completion.bash | 2 +- revision.c | 54 +++++++++++++++++- revision.h | 3 +- t/t6112-rev-list-except.sh | 77 ++++++++++++++++++++++++++ 5 files changed, 139 insertions(+), 3 deletions(-) create mode 100755 t/t6112-rev-list-except.sh diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt index 0d2cdcde55..1617726c7d 100644 --- a/Documentation/git-rev-parse.txt +++ b/Documentation/git-rev-parse.txt @@ -140,6 +140,12 @@ can be used. abbreviate them to a shorter unique name. When no length is specified 7 is used. The minimum length is 4. +--except:: + Skip the following object names. For example: + '--branches --except master' will show all the branches, except master. + This differs from --not in that --except will still show the object, if + they are referenced by another object name. + --symbolic:: Usually the object names are output in SHA-1 form (with possible '{caret}' prefix); this option makes them output in a diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 9525343fcd..f955fcfabe 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1394,7 +1394,7 @@ _git_ls_tree () # Options that go well for log, shortlog and gitk __git_log_common_options=" - --not --all + --not --except --all --branches --tags --remotes --first-parent --merges --no-merges --max-count= diff --git a/revision.c b/revision.c index f40ccf1426..4b1423c44a 100644 --- a/revision.c +++ b/revision.c @@ -2051,6 +2051,9 @@ static int handle_revision_pseudo_opt(const char *submodule, handle_reflog(revs, *flags); } else if (!strcmp(arg, "--not")) { *flags ^= UNINTERESTING | BOTTOM; + *flags &= ~SKIP; + } else if (!strcmp(arg, "--except")) { + *flags |= SKIP; } else if (!strcmp(arg, "--no-walk")) { revs->no_walk = REVISION_WALK_NO_WALK_SORTED; } else if (starts_with(arg, "--no-walk=")) { @@ -2641,24 +2644,73 @@ void reset_revision_walk(void) clear_object_flags(SEEN | ADDED | SHOWN); } +static int refcmp(const char *a, const char *b) +{ + a = prettify_refname(a); + if (*a == '^') + a++; + b = prettify_refname(b); + if (*b == '^') + b++; + return strcmp(a, b); +} + +static int recalculate_flag(struct rev_info *revs, unsigned char *sha1, const char *name) +{ + int flags = 0; + int i; + for (i = 0; i < revs->cmdline.nr; i++) { + struct object *object; + struct rev_cmdline_entry *ce; + ce = &revs->cmdline.rev[i]; + object = ce->item; + while (object->type == OBJ_TAG) { + struct tag *tag = (struct tag *) object; + if (!tag->tagged) + continue; + object = parse_object(tag->tagged->sha1); + if (!object) + continue; + } + if (hashcmp(object->sha1, sha1)) + continue; + if (!strcmp(ce->name, name)) + continue; + flags |= ce->flags; + } + return flags; +} + int prepare_revision_walk(struct rev_info *revs) { int nr = revs->pending.nr; struct object_array_entry *e, *list; struct commit_list **next = &revs->commits; + int i; e = list = revs->pending.objects; revs->pending.nr = 0; revs->pending.alloc = 0; revs->pending.objects = NULL; while (--nr >= 0) { - struct commit *commit = handle_commit(revs, e->item, e->name); + struct commit *commit; + for (i = 0; i < revs->cmdline.nr; i++) { + struct rev_cmdline_entry *ce; + ce = &revs->cmdline.rev[i]; + if ((ce->flags & SKIP) && !refcmp(ce->name, e->name) && + ((ce->flags & UNINTERESTING) == (e->item->flags & UNINTERESTING))) { + e->item->flags = recalculate_flag(revs, e->item->sha1, ce->name); + goto next; + } + } + commit = handle_commit(revs, e->item, e->name); if (commit) { if (!(commit->object.flags & SEEN)) { commit->object.flags |= SEEN; next = commit_list_append(commit, next); } } +next: e++; } if (!revs->leak_pending) diff --git a/revision.h b/revision.h index 88967d6a24..c0fce4db73 100644 --- a/revision.h +++ b/revision.h @@ -18,7 +18,8 @@ #define SYMMETRIC_LEFT (1u<<8) #define PATCHSAME (1u<<9) #define BOTTOM (1u<<10) -#define ALL_REV_FLAGS ((1u<<11)-1) +#define SKIP (1u<<11) +#define ALL_REV_FLAGS ((1u<<12)-1) #define DECORATE_SHORT_REFS 1 #define DECORATE_FULL_REFS 2 diff --git a/t/t6112-rev-list-except.sh b/t/t6112-rev-list-except.sh new file mode 100755 index 0000000000..441e1da7af --- /dev/null +++ b/t/t6112-rev-list-except.sh @@ -0,0 +1,77 @@ +#!/bin/sh + +test_description='test for rev-list --except' + +. ./test-lib.sh + +test_expect_success 'setup' ' + + echo one > content && + git add content && + git commit -m one && + git checkout -b test master && + echo two > content && + git commit -a -m two && + git checkout -b merge master && + git merge test +' + +test_expect_success 'rev-list --except' ' + + git rev-list --topo-order --branches --except merge > actual && + git rev-list --topo-order test > expect && + test_cmp expect actual +' + +test_expect_success 'rev-list --except with extra' ' + + echo three > content && + git commit -a -m three && + git rev-list --topo-order --branches --except merge > actual && + git rev-list --topo-order test > expect && + test_cmp expect actual +' + +test_expect_success 'rev-list --except with full ref' ' + + git rev-list --topo-order --branches --except refs/heads/merge > actual && + git rev-list --topo-order test > expect && + test_cmp expect actual +' + +test_expect_success 'rev-list --except and --not' ' + + git rev-list --topo-order test --not master --except master > actual && + git rev-list --topo-order test > expect && + test_cmp expect actual +' + +test_expect_success 'rev-list --except and --not with proper flags' ' + + git checkout -b maint master && + git checkout -b next test && + echo four > content && + git commit -a -m four && + git rev-list --topo-order next --not master maint --except maint > actual && + git rev-list --topo-order next --not master > expect && + test_cmp expect actual +' + +test_expect_success 'rev-list --not ranges' ' + + git rev-list --topo-order test --not master --except master test > actual && + git rev-list --topo-order test > expect && + test_cmp expect actual +' + +test_expect_success 'rev-list multiple --not ranges' ' + + git checkout -b extra test && + echo five > content && + git commit -a -m five && + git rev-list --topo-order test --not master --except master test --not extra > actual && + git rev-list --topo-order test extra > expect && + test_cmp expect actual +' + +test_done -- 2.32.0.93.g670b81a890