Skip to content

Commit 889a273

Browse files
committed
revision: add --maximal option
When inspecting a range of commits from some set of starting references, it is sometimes useful to learn which commits are not reachable from any other commits in the selected range. One such application is in the creation of a sequence of bundles for the bundle URI feature. Creating a stack of bundles representing different slices of time includes defining which references to include. If all references are used, then this may be overwhelming or redundant. Instead, selecting commits that are maximal to the range could help defining a smaller reference set to use in the bundle header. Add a new '--maximal' option to restrict the output of a revision range to be only the commits that are not reachable from any other commit in the range, based on the reachability definition of the walk. This is accomplished by adding a new 28th bit flag, CHILD_VISITED, that is set as we walk. This does extend the bit range in object.h, but using an earlier bit may collide with another feature. The tests demonstrate the behavior of the feature with a positive-only range, ranges with negative references, and walk-modifying flags like --first-parent and --exclude-first-parent-only. Signed-off-by: Derrick Stolee <stolee@gmail.com>
1 parent b5c409c commit 889a273

File tree

5 files changed

+92
-5
lines changed

5 files changed

+92
-5
lines changed

Documentation/rev-list-options.adoc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,10 @@ The following options affect the way the simplification is performed:
444444
times; if so, a commit is included if it is any of the commits
445445
given or if it is an ancestor or descendant of one of them.
446446

447+
`--maximal`::
448+
Restrict the output commits to be those that are not reachable
449+
from any other commits in the revision range.
450+
447451
A more detailed explanation follows.
448452

449453
Suppose you specified `foo` as the _<paths>_. We shall call commits

object.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ void object_array_init(struct object_array *array);
6464

6565
/*
6666
* object flag allocation:
67-
* revision.h: 0---------10 15 23------27
67+
* revision.h: 0---------10 15 23--------28
6868
* fetch-pack.c: 01 67
6969
* negotiator/default.c: 2--5
7070
* walker.c: 0-2
@@ -86,7 +86,7 @@ void object_array_init(struct object_array *array);
8686
* builtin/unpack-objects.c: 2021
8787
* pack-bitmap.h: 2122
8888
*/
89-
#define FLAG_BITS 28
89+
#define FLAG_BITS 29
9090

9191
#define TYPE_BITS 3
9292

revision.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1150,7 +1150,8 @@ static int process_parents(struct rev_info *revs, struct commit *commit,
11501150
struct commit *p = parent->item;
11511151
parent = parent->next;
11521152
if (p)
1153-
p->object.flags |= UNINTERESTING;
1153+
p->object.flags |= UNINTERESTING |
1154+
CHILD_VISITED;
11541155
if (repo_parse_commit_gently(revs->repo, p, 1) < 0)
11551156
continue;
11561157
if (p->parents)
@@ -1204,7 +1205,7 @@ static int process_parents(struct rev_info *revs, struct commit *commit,
12041205
if (!*slot)
12051206
*slot = *revision_sources_at(revs->sources, commit);
12061207
}
1207-
p->object.flags |= pass_flags;
1208+
p->object.flags |= pass_flags | CHILD_VISITED;
12081209
if (!(p->object.flags & SEEN)) {
12091210
p->object.flags |= (SEEN | NOT_USER_GIVEN);
12101211
if (list)
@@ -2381,6 +2382,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
23812382
revs->first_parent_only = 1;
23822383
} else if (!strcmp(arg, "--exclude-first-parent-only")) {
23832384
revs->exclude_first_parent_only = 1;
2385+
} else if (!strcmp(arg, "--maximal")) {
2386+
revs->maximal = 1;
23842387
} else if (!strcmp(arg, "--ancestry-path")) {
23852388
revs->ancestry_path = 1;
23862389
revs->simplify_history = 0;
@@ -4125,6 +4128,8 @@ enum commit_action get_commit_action(struct rev_info *revs, struct commit *commi
41254128
{
41264129
if (commit->object.flags & SHOWN)
41274130
return commit_ignore;
4131+
if (revs->maximal && (commit->object.flags & CHILD_VISITED))
4132+
return commit_ignore;
41284133
if (revs->unpacked && has_object_pack(revs->repo, &commit->object.oid))
41294134
return commit_ignore;
41304135
if (revs->no_kept_objects) {

revision.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,9 @@
5252
#define NOT_USER_GIVEN (1u<<25)
5353
#define TRACK_LINEAR (1u<<26)
5454
#define ANCESTRY_PATH (1u<<27)
55-
#define ALL_REV_FLAGS (((1u<<11)-1) | NOT_USER_GIVEN | TRACK_LINEAR | PULL_MERGE)
55+
#define CHILD_VISITED (1u<<28)
56+
#define ALL_REV_FLAGS (((1u<<11)-1) | NOT_USER_GIVEN | TRACK_LINEAR \
57+
| PULL_MERGE | CHILD_VISITED)
5658

5759
#define DECORATE_SHORT_REFS 1
5860
#define DECORATE_FULL_REFS 2
@@ -198,6 +200,7 @@ struct rev_info {
198200
cherry_mark:1,
199201
bisect:1,
200202
ancestry_path:1,
203+
maximal:1,
201204

202205
/* True if --ancestry-path was specified without an
203206
* argument. The bottom revisions are implicitly

t/t6600-test-reach.sh

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -762,4 +762,79 @@ test_expect_success 'for-each-ref is-base: --sort' '
762762
--sort=refname --sort=-is-base:commit-2-3
763763
'
764764

765+
test_expect_success 'rev-list --maximal (all positive)' '
766+
# Only one maximal.
767+
cat >input <<-\EOF &&
768+
refs/heads/commit-1-1
769+
refs/heads/commit-4-2
770+
refs/heads/commit-4-4
771+
refs/heads/commit-8-4
772+
EOF
773+
774+
cat >expect <<-EOF &&
775+
$(git rev-parse refs/heads/commit-8-4)
776+
EOF
777+
run_all_modes git rev-list --maximal --stdin &&
778+
779+
# All maximal.
780+
cat >input <<-\EOF &&
781+
refs/heads/commit-5-2
782+
refs/heads/commit-4-3
783+
refs/heads/commit-3-4
784+
refs/heads/commit-2-5
785+
EOF
786+
787+
cat >expect <<-EOF &&
788+
$(git rev-parse refs/heads/commit-5-2)
789+
$(git rev-parse refs/heads/commit-4-3)
790+
$(git rev-parse refs/heads/commit-3-4)
791+
$(git rev-parse refs/heads/commit-2-5)
792+
EOF
793+
run_all_modes git rev-list --maximal --stdin &&
794+
795+
# Mix of both.
796+
cat >input <<-\EOF &&
797+
refs/heads/commit-5-2
798+
refs/heads/commit-3-2
799+
refs/heads/commit-2-5
800+
EOF
801+
802+
cat >expect <<-EOF &&
803+
$(git rev-parse refs/heads/commit-5-2)
804+
$(git rev-parse refs/heads/commit-2-5)
805+
EOF
806+
run_all_modes git rev-list --maximal --stdin
807+
'
808+
809+
test_expect_success 'rev-list --maximal (range)' '
810+
cat >input <<-\EOF &&
811+
refs/heads/commit-1-1
812+
refs/heads/commit-2-5
813+
refs/heads/commit-6-4
814+
^refs/heads/commit-4-5
815+
EOF
816+
817+
cat >expect <<-EOF &&
818+
$(git rev-parse refs/heads/commit-6-4)
819+
EOF
820+
run_all_modes git rev-list --maximal --stdin &&
821+
822+
# first-parent changes reachability: the first parent
823+
# reduces the second coordinate to 1 before reducing the
824+
# first coordinate.
825+
cat >input <<-\EOF &&
826+
refs/heads/commit-1-1
827+
refs/heads/commit-2-5
828+
refs/heads/commit-6-4
829+
^refs/heads/commit-4-5
830+
EOF
831+
832+
cat >expect <<-EOF &&
833+
$(git rev-parse refs/heads/commit-6-4)
834+
$(git rev-parse refs/heads/commit-2-5)
835+
EOF
836+
run_all_modes git rev-list --maximal --stdin \
837+
--first-parent --exclude-first-parent-only
838+
'
839+
765840
test_done

0 commit comments

Comments
 (0)