Skip to content

Commit

Permalink
rev-list: support termination at promisor objects
Browse files Browse the repository at this point in the history
Teach rev-list to support termination of an object traversal at any
object from a promisor remote (whether one that the local repo also has,
or one that the local repo knows about because it has another promisor
object that references it).

This will be used subsequently in gc and in the connectivity check used
by fetch.

For efficiency, if an object is referenced by a promisor object, and is
in the local repo only as a non-promisor object, object traversal will
not stop there. This is to avoid building the list of promisor object
references.

(In list-objects.c, the case where obj is NULL in process_blob() and
process_tree() do not need to be changed because those happen only when
there is a conflict between the expected type and the existing object.
If the object doesn't exist, an object will be synthesized, which is
fine.)

Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
  • Loading branch information
jonathantanmy committed Sep 15, 2017
1 parent 57634d3 commit 2d7ae2b
Show file tree
Hide file tree
Showing 6 changed files with 165 additions and 5 deletions.
13 changes: 13 additions & 0 deletions builtin/rev-list.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "bisect.h"
#include "progress.h"
#include "reflog-walk.h"
#include "packfile.h"

static const char rev_list_usage[] =
"git rev-list [OPTION] <commit-id>... [ -- paths... ]\n"
Expand Down Expand Up @@ -287,6 +288,18 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
init_revisions(&revs, prefix);
revs.abbrev = DEFAULT_ABBREV;
revs.commit_format = CMIT_FMT_UNSPECIFIED;

/*
* Scan the argument list before invoking setup_revisions(), so that we
* know if fetch_if_missing needs to be set to 0.
*/
for (i = 1; i < argc; i++) {
if (!strcmp(argv[i], "--exclude-promisor-objects")) {
fetch_if_missing = 0;
break;
}
}

argc = setup_revisions(argc, argv, &revs, NULL);

memset(&info, 0, sizeof(info));
Expand Down
16 changes: 15 additions & 1 deletion list-objects.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "tree-walk.h"
#include "revision.h"
#include "list-objects.h"
#include "packfile.h"

static void process_blob(struct rev_info *revs,
struct blob *blob,
Expand All @@ -24,6 +25,13 @@ static void process_blob(struct rev_info *revs,
die("bad blob object");
if (obj->flags & (UNINTERESTING | SEEN))
return;
if (!has_object_file(&obj->oid)) {
if (revs->exclude_promisor_objects &&
is_promised(&obj->oid)) {
return;
}
/* error message will be reported later */
}
obj->flags |= SEEN;

pathlen = path->len;
Expand Down Expand Up @@ -77,16 +85,22 @@ static void process_tree(struct rev_info *revs,
enum interesting match = revs->diffopt.pathspec.nr == 0 ?
all_entries_interesting: entry_not_interesting;
int baselen = base->len;
int gently = revs->ignore_missing_links ||
revs->exclude_promisor_objects;

if (!revs->tree_objects)
return;
if (!obj)
die("bad tree object");
if (obj->flags & (UNINTERESTING | SEEN))
return;
if (parse_tree_gently(tree, revs->ignore_missing_links) < 0) {
if (parse_tree_gently(tree, gently) < 0) {
if (revs->ignore_missing_links)
return;
if (revs->exclude_promisor_objects &&
is_promised(&obj->oid)) {
return;
}
die("bad tree object %s", oid_to_hex(&obj->oid));
}

Expand Down
2 changes: 1 addition & 1 deletion object.c
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ struct object *parse_object(const struct object_id *oid)
if (obj && obj->parsed)
return obj;

if ((obj && obj->type == OBJ_BLOB) ||
if ((obj && obj->type == OBJ_BLOB && has_object_file(oid)) ||
(!obj && has_object_file(oid) &&
sha1_object_info(oid->hash, NULL) == OBJ_BLOB)) {
if (check_sha1_signature(repl, NULL, 0, NULL) < 0) {
Expand Down
33 changes: 31 additions & 2 deletions revision.c
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,8 @@ static struct object *get_reference(struct rev_info *revs, const char *name,
if (!object) {
if (revs->ignore_missing)
return object;
if (revs->exclude_promisor_objects && is_promised(oid))
return NULL;
die("bad object %s", name);
}
object->flags |= flags;
Expand Down Expand Up @@ -788,9 +790,17 @@ static int add_parents_to_list(struct rev_info *revs, struct commit *commit,

for (parent = commit->parents; parent; parent = parent->next) {
struct commit *p = parent->item;

if (parse_commit_gently(p, revs->ignore_missing_links) < 0)
int gently = revs->ignore_missing_links ||
revs->exclude_promisor_objects;
if (parse_commit_gently(p, gently) < 0) {
if (revs->exclude_promisor_objects &&
is_promised(&p->object.oid)) {
if (revs->first_parent_only)
break;
continue;
}
return -1;
}
if (revs->show_source && !p->util)
p->util = commit->util;
p->object.flags |= left_flag;
Expand Down Expand Up @@ -2043,6 +2053,10 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
revs->limited = 1;
} else if (!strcmp(arg, "--ignore-missing")) {
revs->ignore_missing = 1;
} else if (!strcmp(arg, "--exclude-promisor-objects")) {
if (fetch_if_missing)
die("BUG: exclude_promisor_objects can only be used when fetch_if_missing is 0");
revs->exclude_promisor_objects = 1;
} else {
int opts = diff_opt_parse(&revs->diffopt, argv, argc, revs->prefix);
if (!opts)
Expand Down Expand Up @@ -2763,6 +2777,16 @@ void reset_revision_walk(void)
clear_object_flags(SEEN | ADDED | SHOWN);
}

static int mark_uninteresting(const struct object_id *oid,
struct packed_git *pack,
uint32_t pos,
void *unused)
{
struct object *o = parse_object(oid);
o->flags |= UNINTERESTING | SEEN;
return 0;
}

int prepare_revision_walk(struct rev_info *revs)
{
int i;
Expand Down Expand Up @@ -2791,6 +2815,11 @@ int prepare_revision_walk(struct rev_info *revs)
(revs->limited && limiting_can_increase_treesame(revs)))
revs->treesame.name = "treesame";

if (revs->exclude_promisor_objects) {
for_each_packed_object(mark_uninteresting, NULL,
FOR_EACH_OBJECT_PROMISOR_ONLY);
}

if (revs->no_walk != REVISION_WALK_NO_WALK_UNSORTED)
commit_list_sort_by_date(&revs->commits);
if (revs->no_walk)
Expand Down
5 changes: 4 additions & 1 deletion revision.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,10 @@ struct rev_info {
bisect:1,
ancestry_path:1,
first_parent_only:1,
line_level_traverse:1;
line_level_traverse:1,

/* for internal use only */
exclude_promisor_objects:1;

/* Diff flags */
unsigned int diff:1,
Expand Down
101 changes: 101 additions & 0 deletions t/t0410-partial-clone.sh
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,107 @@ test_expect_success 'fetching of missing objects' '
git verify-pack --verbose "$IDX" | grep "$HASH"
'

test_expect_success 'rev-list stops traversal at missing and promised commit' '
rm -rf repo &&
test_create_repo repo &&
test_commit -C repo foo &&
test_commit -C repo bar &&
FOO=$(git -C repo rev-parse foo) &&
promise_and_delete "$FOO" &&
git -C repo config core.repositoryformatversion 1 &&
git -C repo config extensions.partialclone "arbitrary string" &&
git -C repo rev-list --exclude-promisor-objects --objects bar >out &&
grep $(git -C repo rev-parse bar) out &&
! grep $FOO out
'

test_expect_success 'rev-list stops traversal at missing and promised tree' '
rm -rf repo &&
test_create_repo repo &&
test_commit -C repo foo &&
mkdir repo/a_dir &&
echo something >repo/a_dir/something &&
git -C repo add a_dir/something &&
git -C repo commit -m bar &&
# foo^{tree} (tree referenced from commit)
TREE=$(git -C repo rev-parse foo^{tree}) &&
# a tree referenced by HEAD^{tree} (tree referenced from tree)
TREE2=$(git -C repo ls-tree HEAD^{tree} | grep " tree " | head -1 | cut -b13-52) &&
promise_and_delete "$TREE" &&
promise_and_delete "$TREE2" &&
git -C repo config core.repositoryformatversion 1 &&
git -C repo config extensions.partialclone "arbitrary string" &&
git -C repo rev-list --exclude-promisor-objects --objects HEAD >out &&
grep $(git -C repo rev-parse foo) out &&
! grep $TREE out &&
grep $(git -C repo rev-parse HEAD) out &&
! grep $TREE2 out
'

test_expect_success 'rev-list stops traversal at missing and promised blob' '
rm -rf repo &&
test_create_repo repo &&
echo something >repo/something &&
git -C repo add something &&
git -C repo commit -m foo &&
BLOB=$(git -C repo hash-object -w something) &&
promise_and_delete "$BLOB" &&
git -C repo config core.repositoryformatversion 1 &&
git -C repo config extensions.partialclone "arbitrary string" &&
git -C repo rev-list --exclude-promisor-objects --objects HEAD >out &&
grep $(git -C repo rev-parse HEAD) out &&
! grep $BLOB out
'

test_expect_success 'rev-list stops traversal at promisor commit, tree, and blob' '
rm -rf repo &&
test_create_repo repo &&
test_commit -C repo foo &&
test_commit -C repo bar &&
test_commit -C repo baz &&
COMMIT=$(git -C repo rev-parse foo) &&
TREE=$(git -C repo rev-parse bar^{tree}) &&
BLOB=$(git hash-object repo/baz.t) &&
printf "%s\n%s\n%s\n" $COMMIT $TREE $BLOB | pack_as_from_promisor &&
git -C repo config core.repositoryformatversion 1 &&
git -C repo config extensions.partialclone "arbitrary string" &&
git -C repo rev-list --exclude-promisor-objects --objects HEAD >out &&
! grep $COMMIT out &&
! grep $TREE out &&
! grep $BLOB out &&
grep $(git -C repo rev-parse bar) out # sanity check that some walking was done
'

test_expect_success 'rev-list accepts missing and promised objects on command line' '
rm -rf repo &&
test_create_repo repo &&
test_commit -C repo foo &&
test_commit -C repo bar &&
test_commit -C repo baz &&
COMMIT=$(git -C repo rev-parse foo) &&
TREE=$(git -C repo rev-parse bar^{tree}) &&
BLOB=$(git hash-object repo/baz.t) &&
promise_and_delete $COMMIT &&
promise_and_delete $TREE &&
promise_and_delete $BLOB &&
git -C repo config core.repositoryformatversion 1 &&
git -C repo config extensions.partialclone "arbitrary string" &&
git -C repo rev-list --exclude-promisor-objects --objects "$COMMIT" "$TREE" "$BLOB"
'

LIB_HTTPD_PORT=12345 # default port, 410, cannot be used as non-root
. "$TEST_DIRECTORY"/lib-httpd.sh
start_httpd
Expand Down

0 comments on commit 2d7ae2b

Please sign in to comment.