Merge branch 'mg/rev-list-one-side-only'

* mg/rev-list-one-side-only:
  git-log: put space after commit mark
  t6007: test rev-list --cherry
  log --cherry: a synonym
  rev-list: documentation and test for --cherry-mark
  revision.c: introduce --cherry-mark
  rev-list/log: factor out revision mark generation
  rev-list: --left/right-only are mutually exclusive
  rev-list: documentation and test for --left/right-only
  t6007: Make sure we test --cherry-pick
  revlist.c: introduce --left/right-only for unsymmetric picking
This commit is contained in:
Junio C Hamano 2011-03-22 21:38:50 -07:00
commit aeb2aaa771
10 changed files with 230 additions and 69 deletions

View file

@ -31,6 +31,9 @@ SYNOPSIS
[ \--parents ] [ \--parents ]
[ \--timestamp ] [ \--timestamp ]
[ \--left-right ] [ \--left-right ]
[ \--left-only ]
[ \--right-only ]
[ \--cherry-mark ]
[ \--cherry-pick ] [ \--cherry-pick ]
[ \--encoding[=<encoding>] ] [ \--encoding[=<encoding>] ]
[ \--(author|committer|grep)=<pattern> ] [ \--(author|committer|grep)=<pattern> ]

View file

@ -151,6 +151,11 @@ ifdef::git-rev-list[]
to /dev/null as the output does not have to be formatted. to /dev/null as the output does not have to be formatted.
endif::git-rev-list[] endif::git-rev-list[]
--cherry-mark::
Like `--cherry-pick` (see below) but mark equivalent commits
with `=` rather than omitting them, and inequivalent ones with `+`.
--cherry-pick:: --cherry-pick::
Omit any commit that introduces the same change as Omit any commit that introduces the same change as
@ -165,6 +170,27 @@ from the other branch (for example, "3rd on b" may be cherry-picked
from branch A). With this option, such pairs of commits are from branch A). With this option, such pairs of commits are
excluded from the output. excluded from the output.
--left-only::
--right-only::
List only commits on the respective side of a symmetric range,
i.e. only those which would be marked `<` resp. `>` by
`--left-right`.
+
For example, `--cherry-pick --right-only A...B` omits those
commits from `B` which are in `A` or are patch-equivalent to a commit in
`A`. In other words, this lists the `{plus}` commits from `git cherry A B`.
More precisely, `--cherry-pick --right-only --no-merges` gives the exact
list.
--cherry::
A synonym for `--right-only --cherry-mark --no-merges`; useful to
limit the output to the commits on our side and mark those that
have been applied to the other side of a forked history with
`git log --cherry upstream...mybranch`, similar to
`git cherry upstream mybranch`.
-g:: -g::
--walk-reflogs:: --walk-reflogs::

View file

@ -64,18 +64,8 @@ static void show_commit(struct commit *commit, void *data)
if (info->header_prefix) if (info->header_prefix)
fputs(info->header_prefix, stdout); fputs(info->header_prefix, stdout);
if (!revs->graph) { if (!revs->graph)
if (commit->object.flags & BOUNDARY) fputs(get_revision_mark(revs, commit), stdout);
putchar('-');
else if (commit->object.flags & UNINTERESTING)
putchar('^');
else if (revs->left_right) {
if (commit->object.flags & SYMMETRIC_LEFT)
putchar('<');
else
putchar('>');
}
}
if (revs->abbrev_commit && revs->abbrev) if (revs->abbrev_commit && revs->abbrev)
fputs(find_unique_abbrev(commit->object.sha1, revs->abbrev), fputs(find_unique_abbrev(commit->object.sha1, revs->abbrev),
stdout); stdout);

View file

@ -5734,7 +5734,7 @@ sub cmd_show_log {
my (@k, $c, $d, $stat); my (@k, $c, $d, $stat);
my $esc_color = qr/(?:\033\[(?:(?:\d+;)*\d*)?m)*/; my $esc_color = qr/(?:\033\[(?:(?:\d+;)*\d*)?m)*/;
while (<$log>) { while (<$log>) {
if (/^${esc_color}commit -?($::sha1_short)/o) { if (/^${esc_color}commit (- )?($::sha1_short)/o) {
my $cmt = $1; my $cmt = $1;
if ($c && cmt_showable($c) && $c->{r} != $r_last) { if ($c && cmt_showable($c) && $c->{r} != $r_last) {
$r_last = $c->{r}; $r_last = $c->{r};

17
graph.c
View file

@ -798,22 +798,9 @@ static void graph_output_commit_char(struct git_graph *graph, struct strbuf *sb)
} }
/* /*
* If revs->left_right is set, print '<' for commits that * get_revision_mark() handles all other cases without assert()
* come from the left side, and '>' for commits from the right
* side.
*/ */
if (graph->revs && graph->revs->left_right) { strbuf_addstr(sb, get_revision_mark(graph->revs, graph->commit));
if (graph->commit->object.flags & SYMMETRIC_LEFT)
strbuf_addch(sb, '<');
else
strbuf_addch(sb, '>');
return;
}
/*
* Print '*' in all other cases
*/
strbuf_addch(sb, '*');
} }
/* /*

View file

@ -380,18 +380,8 @@ void show_log(struct rev_info *opt)
if (!opt->verbose_header) { if (!opt->verbose_header) {
graph_show_commit(opt->graph); graph_show_commit(opt->graph);
if (!opt->graph) { if (!opt->graph)
if (commit->object.flags & BOUNDARY) put_revision_mark(opt, commit);
putchar('-');
else if (commit->object.flags & UNINTERESTING)
putchar('^');
else if (opt->left_right) {
if (commit->object.flags & SYMMETRIC_LEFT)
putchar('<');
else
putchar('>');
}
}
fputs(find_unique_abbrev(commit->object.sha1, abbrev_commit), stdout); fputs(find_unique_abbrev(commit->object.sha1, abbrev_commit), stdout);
if (opt->print_parents) if (opt->print_parents)
show_parents(commit, abbrev_commit); show_parents(commit, abbrev_commit);
@ -448,18 +438,8 @@ void show_log(struct rev_info *opt)
if (opt->commit_format != CMIT_FMT_ONELINE) if (opt->commit_format != CMIT_FMT_ONELINE)
fputs("commit ", stdout); fputs("commit ", stdout);
if (!opt->graph) { if (!opt->graph)
if (commit->object.flags & BOUNDARY) put_revision_mark(opt, commit);
putchar('-');
else if (commit->object.flags & UNINTERESTING)
putchar('^');
else if (opt->left_right) {
if (commit->object.flags & SYMMETRIC_LEFT)
putchar('<');
else
putchar('>');
}
}
fputs(find_unique_abbrev(commit->object.sha1, abbrev_commit), fputs(find_unique_abbrev(commit->object.sha1, abbrev_commit),
stdout); stdout);
if (opt->print_parents) if (opt->print_parents)

View file

@ -876,11 +876,7 @@ static size_t format_commit_one(struct strbuf *sb, const char *placeholder,
c->abbrev_parent_hashes.off; c->abbrev_parent_hashes.off;
return 1; return 1;
case 'm': /* left/right/bottom */ case 'm': /* left/right/bottom */
strbuf_addch(sb, (commit->object.flags & BOUNDARY) strbuf_addstr(sb, get_revision_mark(NULL, commit));
? '-'
: (commit->object.flags & SYMMETRIC_LEFT)
? '<'
: '>');
return 1; return 1;
case 'd': case 'd':
format_decoration(sb, commit); format_decoration(sb, commit);

View file

@ -535,6 +535,7 @@ static void cherry_pick_list(struct commit_list *list, struct rev_info *revs)
int left_count = 0, right_count = 0; int left_count = 0, right_count = 0;
int left_first; int left_first;
struct patch_ids ids; struct patch_ids ids;
unsigned cherry_flag;
/* First count the commits on the left and on the right */ /* First count the commits on the left and on the right */
for (p = list; p; p = p->next) { for (p = list; p; p = p->next) {
@ -572,6 +573,9 @@ static void cherry_pick_list(struct commit_list *list, struct rev_info *revs)
commit->util = add_commit_patch_id(commit, &ids); commit->util = add_commit_patch_id(commit, &ids);
} }
/* either cherry_mark or cherry_pick are true */
cherry_flag = revs->cherry_mark ? PATCHSAME : SHOWN;
/* Check the other side */ /* Check the other side */
for (p = list; p; p = p->next) { for (p = list; p; p = p->next) {
struct commit *commit = p->item; struct commit *commit = p->item;
@ -594,7 +598,7 @@ static void cherry_pick_list(struct commit_list *list, struct rev_info *revs)
if (!id) if (!id)
continue; continue;
id->seen = 1; id->seen = 1;
commit->object.flags |= SHOWN; commit->object.flags |= cherry_flag;
} }
/* Now check the original side for seen ones */ /* Now check the original side for seen ones */
@ -606,7 +610,7 @@ static void cherry_pick_list(struct commit_list *list, struct rev_info *revs)
if (!ent) if (!ent)
continue; continue;
if (ent->seen) if (ent->seen)
commit->object.flags |= SHOWN; commit->object.flags |= cherry_flag;
commit->util = NULL; commit->util = NULL;
} }
@ -729,6 +733,23 @@ static struct commit_list *collect_bottom_commits(struct commit_list *list)
return bottom; return bottom;
} }
/* Assumes either left_only or right_only is set */
static void limit_left_right(struct commit_list *list, struct rev_info *revs)
{
struct commit_list *p;
for (p = list; p; p = p->next) {
struct commit *commit = p->item;
if (revs->right_only) {
if (commit->object.flags & SYMMETRIC_LEFT)
commit->object.flags |= SHOWN;
} else /* revs->left_only is set */
if (!(commit->object.flags & SYMMETRIC_LEFT))
commit->object.flags |= SHOWN;
}
}
static int limit_list(struct rev_info *revs) static int limit_list(struct rev_info *revs)
{ {
int slop = SLOP; int slop = SLOP;
@ -781,9 +802,12 @@ static int limit_list(struct rev_info *revs)
show(revs, newlist); show(revs, newlist);
show_early_output = NULL; show_early_output = NULL;
} }
if (revs->cherry_pick) if (revs->cherry_pick || revs->cherry_mark)
cherry_pick_list(newlist, revs); cherry_pick_list(newlist, revs);
if (revs->left_only || revs->right_only)
limit_left_right(newlist, revs);
if (bottom) { if (bottom) {
limit_to_ancestry(bottom, newlist); limit_to_ancestry(bottom, newlist);
free_commit_list(bottom); free_commit_list(bottom);
@ -1260,9 +1284,32 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
revs->boundary = 1; revs->boundary = 1;
} else if (!strcmp(arg, "--left-right")) { } else if (!strcmp(arg, "--left-right")) {
revs->left_right = 1; revs->left_right = 1;
} else if (!strcmp(arg, "--left-only")) {
if (revs->right_only)
die("--left-only is incompatible with --right-only"
" or --cherry");
revs->left_only = 1;
} else if (!strcmp(arg, "--right-only")) {
if (revs->left_only)
die("--right-only is incompatible with --left-only");
revs->right_only = 1;
} else if (!strcmp(arg, "--cherry")) {
if (revs->left_only)
die("--cherry is incompatible with --left-only");
revs->cherry_mark = 1;
revs->right_only = 1;
revs->no_merges = 1;
revs->limited = 1;
} else if (!strcmp(arg, "--count")) { } else if (!strcmp(arg, "--count")) {
revs->count = 1; revs->count = 1;
} else if (!strcmp(arg, "--cherry-mark")) {
if (revs->cherry_pick)
die("--cherry-mark is incompatible with --cherry-pick");
revs->cherry_mark = 1;
revs->limited = 1; /* needs limit_list() */
} else if (!strcmp(arg, "--cherry-pick")) { } else if (!strcmp(arg, "--cherry-pick")) {
if (revs->cherry_mark)
die("--cherry-pick is incompatible with --cherry-mark");
revs->cherry_pick = 1; revs->cherry_pick = 1;
revs->limited = 1; revs->limited = 1;
} else if (!strcmp(arg, "--objects")) { } else if (!strcmp(arg, "--objects")) {
@ -2232,3 +2279,32 @@ struct commit *get_revision(struct rev_info *revs)
graph_update(revs->graph, c); graph_update(revs->graph, c);
return c; return c;
} }
char *get_revision_mark(const struct rev_info *revs, const struct commit *commit)
{
if (commit->object.flags & BOUNDARY)
return "-";
else if (commit->object.flags & UNINTERESTING)
return "^";
else if (commit->object.flags & PATCHSAME)
return "=";
else if (!revs || revs->left_right) {
if (commit->object.flags & SYMMETRIC_LEFT)
return "<";
else
return ">";
} else if (revs->graph)
return "*";
else if (revs->cherry_mark)
return "+";
return "";
}
void put_revision_mark(const struct rev_info *revs, const struct commit *commit)
{
char *mark = get_revision_mark(revs, commit);
if (!strlen(mark))
return;
fputs(mark, stdout);
putchar(' ');
}

View file

@ -14,7 +14,8 @@
#define CHILD_SHOWN (1u<<6) #define CHILD_SHOWN (1u<<6)
#define ADDED (1u<<7) /* Parents already parsed and added? */ #define ADDED (1u<<7) /* Parents already parsed and added? */
#define SYMMETRIC_LEFT (1u<<8) #define SYMMETRIC_LEFT (1u<<8)
#define ALL_REV_FLAGS ((1u<<9)-1) #define PATCHSAME (1u<<9)
#define ALL_REV_FLAGS ((1u<<10)-1)
#define DECORATE_SHORT_REFS 1 #define DECORATE_SHORT_REFS 1
#define DECORATE_FULL_REFS 2 #define DECORATE_FULL_REFS 2
@ -59,6 +60,8 @@ struct rev_info {
boundary:2, boundary:2,
count:1, count:1,
left_right:1, left_right:1,
left_only:1,
right_only:1,
rewrite_parents:1, rewrite_parents:1,
print_parents:1, print_parents:1,
show_source:1, show_source:1,
@ -66,6 +69,7 @@ struct rev_info {
reverse:1, reverse:1,
reverse_output_stage:1, reverse_output_stage:1,
cherry_pick:1, cherry_pick:1,
cherry_mark:1,
bisect:1, bisect:1,
ancestry_path:1, ancestry_path:1,
first_parent_only:1; first_parent_only:1;
@ -163,6 +167,8 @@ extern int handle_revision_arg(const char *arg, struct rev_info *revs,int flags,
extern int prepare_revision_walk(struct rev_info *revs); extern int prepare_revision_walk(struct rev_info *revs);
extern struct commit *get_revision(struct rev_info *revs); extern struct commit *get_revision(struct rev_info *revs);
extern char *get_revision_mark(const struct rev_info *revs, const struct commit *commit);
extern void put_revision_mark(const struct rev_info *revs, const struct commit *commit);
extern void mark_parents_uninteresting(struct commit *commit); extern void mark_parents_uninteresting(struct commit *commit);
extern void mark_tree_uninteresting(struct tree *tree); extern void mark_tree_uninteresting(struct tree *tree);

View file

@ -4,13 +4,14 @@ test_description='test git rev-list --cherry-pick -- file'
. ./test-lib.sh . ./test-lib.sh
# A---B # A---B---D---F
# \ # \
# \ # \
# C # C---E
# #
# B changes a file foo.c, adding a line of text. C changes foo.c as # B changes a file foo.c, adding a line of text. C changes foo.c as
# well as bar.c, but the change in foo.c was identical to change B. # well as bar.c, but the change in foo.c was identical to change B.
# D and C change bar in the same way, E and F differently.
test_expect_success setup ' test_expect_success setup '
echo Hallo > foo && echo Hallo > foo &&
@ -25,11 +26,26 @@ test_expect_success setup '
test_tick && test_tick &&
git commit -m "C" && git commit -m "C" &&
git tag C && git tag C &&
echo Dello > bar &&
git add bar &&
test_tick &&
git commit -m "E" &&
git tag E &&
git checkout master && git checkout master &&
git checkout branch foo && git checkout branch foo &&
test_tick && test_tick &&
git commit -m "B" && git commit -m "B" &&
git tag B git tag B &&
echo Cello > bar &&
git add bar &&
test_tick &&
git commit -m "D" &&
git tag D &&
echo Nello > bar &&
git add bar &&
test_tick &&
git commit -m "F" &&
git tag F
' '
cat >expect <<EOF cat >expect <<EOF
@ -53,8 +69,92 @@ test_expect_success '--cherry-pick foo comes up empty' '
test -z "$(git rev-list --left-right --cherry-pick B...C -- foo)" test -z "$(git rev-list --left-right --cherry-pick B...C -- foo)"
' '
cat >expect <<EOF
>tags/C
EOF
test_expect_success '--cherry-pick bar does not come up empty' ' test_expect_success '--cherry-pick bar does not come up empty' '
! test -z "$(git rev-list --left-right --cherry-pick B...C -- bar)" git rev-list --left-right --cherry-pick B...C -- bar > actual &&
git name-rev --stdin --name-only --refs="*tags/*" \
< actual > actual.named &&
test_cmp actual.named expect
'
test_expect_success 'bar does not come up empty' '
git rev-list --left-right B...C -- bar > actual &&
git name-rev --stdin --name-only --refs="*tags/*" \
< actual > actual.named &&
test_cmp actual.named expect
'
cat >expect <<EOF
<tags/F
>tags/E
EOF
test_expect_success '--cherry-pick bar does not come up empty (II)' '
git rev-list --left-right --cherry-pick F...E -- bar > actual &&
git name-rev --stdin --name-only --refs="*tags/*" \
< actual > actual.named &&
test_cmp actual.named expect
'
cat >expect <<EOF
+tags/F
=tags/D
+tags/E
=tags/C
EOF
test_expect_success '--cherry-mark' '
git rev-list --cherry-mark F...E -- bar > actual &&
git name-rev --stdin --name-only --refs="*tags/*" \
< actual > actual.named &&
test_cmp actual.named expect
'
cat >expect <<EOF
<tags/F
=tags/D
>tags/E
=tags/C
EOF
test_expect_success '--cherry-mark --left-right' '
git rev-list --cherry-mark --left-right F...E -- bar > actual &&
git name-rev --stdin --name-only --refs="*tags/*" \
< actual > actual.named &&
test_cmp actual.named expect
'
cat >expect <<EOF
tags/E
EOF
test_expect_success '--cherry-pick --right-only' '
git rev-list --cherry-pick --right-only F...E -- bar > actual &&
git name-rev --stdin --name-only --refs="*tags/*" \
< actual > actual.named &&
test_cmp actual.named expect
'
test_expect_success '--cherry-pick --left-only' '
git rev-list --cherry-pick --left-only E...F -- bar > actual &&
git name-rev --stdin --name-only --refs="*tags/*" \
< actual > actual.named &&
test_cmp actual.named expect
'
cat >expect <<EOF
+tags/E
=tags/C
EOF
test_expect_success '--cherry' '
git rev-list --cherry F...E -- bar > actual &&
git name-rev --stdin --name-only --refs="*tags/*" \
< actual > actual.named &&
test_cmp actual.named expect
' '
test_expect_success '--cherry-pick with independent, but identical branches' ' test_expect_success '--cherry-pick with independent, but identical branches' '
@ -75,11 +175,8 @@ cat >expect <<EOF
1 2 1 2
EOF EOF
# Insert an extra commit to break the symmetry
test_expect_success '--count --left-right' ' test_expect_success '--count --left-right' '
git checkout branch && git rev-list --count --left-right C...D > actual &&
test_commit D &&
git rev-list --count --left-right B...D > actual &&
test_cmp expect actual test_cmp expect actual
' '