{"thread":{"id":"35128","subject":"[PATCH v3] revision: add --except option","startedAt":"2013-10-12T07:08:10Z","lastAt":"2013-10-12T07:08:10Z","messageCount":1,"participants":["Felipe Contreras"],"isPatch":true,"patchVersion":3,"patchTotal":null},"messages":[{"id":"228860","messageId":"1381561690-20827-1-git-send-email-felipe.contreras@gmail.com","threadId":"35128","inReplyTo":null,"subject":"[PATCH v3] revision: add --except option","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-10-12T07:08:10Z","receivedAt":"2013-10-12T07:08:10Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"So that it's possible to remove certain refs from the list without\nremoving the objects that are referenced by other refs.\n\nFor example this repository:\n\n  C (crap)\n  B (test)\n  A (HEAD, master)\n\nWhen using '--branches --except crap':\n\n  B (test)\n  A (HEAD, master)\n\nBut when using '--branches --not crap' nothing will come out.\n\nSigned-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n Documentation/git-rev-parse.txt        |  6 +++\n contrib/completion/git-completion.bash |  2 +-\n revision.c                             | 54 +++++++++++++++++++++++-\n revision.h                             |  3 +-\n t/t6112-rev-list-except.sh             | 77 ++++++++++++++++++++++++++++++++++\n 5 files changed, 139 insertions(+), 3 deletions(-)\n create mode 100755 t/t6112-rev-list-except.sh\n\ndiff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt\nindex 2b126c0..fe5cc6b 100644\n--- a/Documentation/git-rev-parse.txt\n+++ b/Documentation/git-rev-parse.txt\n@@ -110,6 +110,12 @@ can be used.\n \tstrip '{caret}' prefix from the object names that already have\n \tone.\n \n+--except::\n+\tSkip the following object names. For example:\n+\t'--branches --except master' will show all the branches, except master.\n+\tThis differs from --not in that --except will still show the object, if\n+\tthey are referenced by another object name.\n+\n --symbolic::\n \tUsually the object names are output in SHA-1 form (with\n \tpossible '{caret}' prefix); this option makes them output in a\ndiff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash\nindex 5da920e..aed8c12 100644\n--- a/contrib/completion/git-completion.bash\n+++ b/contrib/completion/git-completion.bash\n@@ -1386,7 +1386,7 @@ _git_ls_tree ()\n \n # Options that go well for log, shortlog and gitk\n __git_log_common_options=\"\n-\t--not --all\n+\t--not --except --all\n \t--branches --tags --remotes\n \t--first-parent --merges --no-merges\n \t--max-count=\ndiff --git a/revision.c b/revision.c\nindex 84ccc05..c92f755 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -1984,6 +1984,9 @@ static int handle_revision_pseudo_opt(const char *submodule,\n \t\thandle_reflog(revs, *flags);\n \t} else if (!strcmp(arg, \"--not\")) {\n \t\t*flags ^= UNINTERESTING | BOTTOM;\n+\t\t*flags &= ~SKIP;\n+\t} else if (!strcmp(arg, \"--except\")) {\n+\t\t*flags |= SKIP;\n \t} else if (!strcmp(arg, \"--no-walk\")) {\n \t\trevs->no_walk = REVISION_WALK_NO_WALK_SORTED;\n \t} else if (!prefixcmp(arg, \"--no-walk=\")) {\n@@ -2573,24 +2576,73 @@ void reset_revision_walk(void)\n \tclear_object_flags(SEEN | ADDED | SHOWN);\n }\n \n+static int refcmp(const char *a, const char *b)\n+{\n+\ta = prettify_refname(a);\n+\tif (*a == '^')\n+\t\ta++;\n+\tb = prettify_refname(b);\n+\tif (*b == '^')\n+\t\tb++;\n+\treturn strcmp(a, b);\n+}\n+\n+static int recalculate_flag(struct rev_info *revs, unsigned char *sha1, const char *name)\n+{\n+\tint flags = 0;\n+\tint i;\n+\tfor (i = 0; i < revs->cmdline.nr; i++) {\n+\t\tstruct object *object;\n+\t\tstruct rev_cmdline_entry *ce;\n+\t\tce = &revs->cmdline.rev[i];\n+\t\tobject = ce->item;\n+\t\twhile (object->type == OBJ_TAG) {\n+\t\t\tstruct tag *tag = (struct tag *) object;\n+\t\t\tif (!tag->tagged)\n+\t\t\t\tcontinue;\n+\t\t\tobject = parse_object(tag->tagged->sha1);\n+\t\t\tif (!object)\n+\t\t\t\tcontinue;\n+\t\t}\n+\t\tif (hashcmp(object->sha1, sha1))\n+\t\t\tcontinue;\n+\t\tif (!strcmp(ce->name, name))\n+\t\t\tcontinue;\n+\t\tflags |= ce->flags;\n+\t}\n+\treturn flags;\n+}\n+\n int prepare_revision_walk(struct rev_info *revs)\n {\n \tint nr = revs->pending.nr;\n \tstruct object_array_entry *e, *list;\n \tstruct commit_list **next = &revs->commits;\n+\tint i;\n \n \te = list = revs->pending.objects;\n \trevs->pending.nr = 0;\n \trevs->pending.alloc = 0;\n \trevs->pending.objects = NULL;\n \twhile (--nr >= 0) {\n-\t\tstruct commit *commit = handle_commit(revs, e->item, e->name);\n+\t\tstruct commit *commit;\n+\t\tfor (i = 0; i < revs->cmdline.nr; i++) {\n+\t\t\tstruct rev_cmdline_entry *ce;\n+\t\t\tce = &revs->cmdline.rev[i];\n+\t\t\tif ((ce->flags & SKIP) && !refcmp(ce->name, e->name) &&\n+\t\t\t\t\t((ce->flags & UNINTERESTING) == (e->item->flags & UNINTERESTING))) {\n+\t\t\t\te->item->flags = recalculate_flag(revs, e->item->sha1, ce->name);\n+\t\t\t\tgoto next;\n+\t\t\t}\n+\t\t}\n+\t\tcommit = handle_commit(revs, e->item, e->name);\n \t\tif (commit) {\n \t\t\tif (!(commit->object.flags & SEEN)) {\n \t\t\t\tcommit->object.flags |= SEEN;\n \t\t\t\tnext = commit_list_append(commit, next);\n \t\t\t}\n \t\t}\n+next:\n \t\te++;\n \t}\n \tif (!revs->leak_pending)\ndiff --git a/revision.h b/revision.h\nindex 95859ba..89f5037 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -17,7 +17,8 @@\n #define SYMMETRIC_LEFT\t(1u<<8)\n #define PATCHSAME\t(1u<<9)\n #define BOTTOM\t\t(1u<<10)\n-#define ALL_REV_FLAGS\t((1u<<11)-1)\n+#define SKIP\t\t(1u<<11)\n+#define ALL_REV_FLAGS\t((1u<<12)-1)\n \n #define DECORATE_SHORT_REFS\t1\n #define DECORATE_FULL_REFS\t2\ndiff --git a/t/t6112-rev-list-except.sh b/t/t6112-rev-list-except.sh\nnew file mode 100755\nindex 0000000..441e1da\n--- /dev/null\n+++ b/t/t6112-rev-list-except.sh\n@@ -0,0 +1,77 @@\n+#!/bin/sh\n+\n+test_description='test for rev-list --except'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\n+\techo one > content &&\n+\tgit add content &&\n+\tgit commit -m one &&\n+\tgit checkout -b test master &&\n+\techo two > content &&\n+\tgit commit -a -m two &&\n+\tgit checkout -b merge master &&\n+\tgit merge test\n+'\n+\n+test_expect_success 'rev-list --except' '\n+\n+\tgit rev-list --topo-order --branches --except merge > actual &&\n+\tgit rev-list --topo-order test > expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'rev-list --except with extra' '\n+\n+\techo three > content &&\n+\tgit commit -a -m three &&\n+\tgit rev-list --topo-order --branches --except merge > actual &&\n+\tgit rev-list --topo-order test > expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'rev-list --except with full ref' '\n+\n+\tgit rev-list --topo-order --branches --except refs/heads/merge > actual &&\n+\tgit rev-list --topo-order test > expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'rev-list --except and --not' '\n+\n+\tgit rev-list --topo-order test --not master --except master > actual &&\n+\tgit rev-list --topo-order test > expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'rev-list --except and --not with proper flags' '\n+\n+\tgit checkout -b maint master &&\n+\tgit checkout -b next test &&\n+\techo four > content &&\n+\tgit commit -a -m four &&\n+\tgit rev-list --topo-order next --not master maint --except maint > actual &&\n+\tgit rev-list --topo-order next --not master > expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'rev-list --not ranges' '\n+\n+\tgit rev-list --topo-order test --not master --except master test > actual &&\n+\tgit rev-list --topo-order test > expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'rev-list multiple --not ranges' '\n+\n+\tgit checkout -b extra test &&\n+\techo five > content &&\n+\tgit commit -a -m five &&\n+\tgit rev-list --topo-order test --not master --except master test --not extra > actual &&\n+\tgit rev-list --topo-order test extra > expect &&\n+\ttest_cmp expect actual\n+'\n+\n+test_done\n-- \n1.8.4-fc\n"}]}