Skip to content

Commit

Permalink
fetch-pack: write fetched refs to .promisor
Browse files Browse the repository at this point in the history
The specification of promisor packfiles (in partial-clone.txt) states
that the .promisor files that accompany packfiles do not matter (just
like .keep files), so whenever a packfile is fetched from the promisor
remote, Git has been writing empty .promisor files. But these files
could contain more useful information.

So instead of writing empty files, write the refs fetched to these
files. This makes it easier to debug issues with partial clones, as we
can identify what refs (and their associated hashes) were fetched at the
time the packfile was downloaded, and if necessary, compare those hashes
against what the promisor remote reports now.

This is implemented by teaching fetch-pack to write its own non-empty
.promisor file whenever it knows the name of the pack's lockfile. This
covers the case wherein the user runs "git fetch" with an internal
protocol or HTTP protocol v2 (fetch_refs_via_pack() in transport.c sets
lock_pack) and with HTTP protocol v0/v1 (fetch_git() in remote-curl.c
passes "--lock-pack" to "fetch-pack").

Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Acked-by: Josh Steadmon <steadmon@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
  • Loading branch information
jonathantanmy authored and gitster committed Oct 16, 2019
1 parent 08da649 commit 5374a29
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 4 deletions.
7 changes: 7 additions & 0 deletions builtin/repack.c
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,13 @@ static void repack_promisor_objects(const struct pack_objects_args *args,
/*
* pack-objects creates the .pack and .idx files, but not the
* .promisor file. Create the .promisor file, which is empty.
*
* NEEDSWORK: fetch-pack sometimes generates non-empty
* .promisor files containing the ref names and associated
* hashes at the point of generation of the corresponding
* packfile, but this would not preserve their contents. Maybe
* concatenate the contents of all .promisor files instead of
* just creating a new empty file.
*/
promisor_name = mkpathdup("%s-%s.promisor", packtmp,
line.buf);
Expand Down
47 changes: 43 additions & 4 deletions fetch-pack.c
Original file line number Diff line number Diff line change
Expand Up @@ -754,8 +754,33 @@ static int sideband_demux(int in, int out, void *data)
return ret;
}

static void write_promisor_file(const char *keep_name,
struct ref **sought, int nr_sought)
{
struct strbuf promisor_name = STRBUF_INIT;
int suffix_stripped;
FILE *output;
int i;

strbuf_addstr(&promisor_name, keep_name);
suffix_stripped = strbuf_strip_suffix(&promisor_name, ".keep");
if (!suffix_stripped)
BUG("name of pack lockfile should end with .keep (was '%s')",
keep_name);
strbuf_addstr(&promisor_name, ".promisor");

output = xfopen(promisor_name.buf, "w");
for (i = 0; i < nr_sought; i++)
fprintf(output, "%s %s\n", oid_to_hex(&sought[i]->old_oid),
sought[i]->name);
fclose(output);

strbuf_release(&promisor_name);
}

static int get_pack(struct fetch_pack_args *args,
int xd[2], char **pack_lockfile)
int xd[2], char **pack_lockfile,
struct ref **sought, int nr_sought)
{
struct async demux;
int do_keep = args->keep_pack;
Expand Down Expand Up @@ -817,7 +842,13 @@ static int get_pack(struct fetch_pack_args *args,
}
if (args->check_self_contained_and_connected)
argv_array_push(&cmd.args, "--check-self-contained-and-connected");
if (args->from_promisor)
/*
* If we're obtaining the filename of a lockfile, we'll use
* that filename to write a .promisor file with more
* information below. If not, we need index-pack to do it for
* us.
*/
if (!(do_keep && pack_lockfile) && args->from_promisor)
argv_array_push(&cmd.args, "--promisor");
}
else {
Expand Down Expand Up @@ -871,6 +902,14 @@ static int get_pack(struct fetch_pack_args *args,
die(_("%s failed"), cmd_name);
if (use_sideband && finish_async(&demux))
die(_("error in sideband demultiplexer"));

/*
* Now that index-pack has succeeded, write the promisor file using the
* obtained .keep filename if necessary
*/
if (do_keep && pack_lockfile && args->from_promisor)
write_promisor_file(*pack_lockfile, sought, nr_sought);

return 0;
}

Expand Down Expand Up @@ -1006,7 +1045,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
alternate_shallow_file = setup_temporary_shallow(si->shallow);
else
alternate_shallow_file = NULL;
if (get_pack(args, fd, pack_lockfile))
if (get_pack(args, fd, pack_lockfile, sought, nr_sought))
die(_("git fetch-pack: fetch failed."));

all_done:
Expand Down Expand Up @@ -1453,7 +1492,7 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,

/* get the pack */
process_section_header(&reader, "packfile", 0);
if (get_pack(args, fd, pack_lockfile))
if (get_pack(args, fd, pack_lockfile, sought, nr_sought))
die(_("git fetch-pack: fetch failed."));

state = FETCH_DONE;
Expand Down
8 changes: 8 additions & 0 deletions t/t5616-partial-clone.sh
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,14 @@ test_expect_success 'do partial clone 1' '
test "$(git -C pc1 config --local remote.origin.partialclonefilter)" = "blob:none"
'

test_expect_success 'verify that .promisor file contains refs fetched' '
ls pc1/.git/objects/pack/pack-*.promisor >promisorlist &&
test_line_count = 1 promisorlist &&
git -C srv.bare rev-list HEAD >headhash &&
grep "$(cat headhash) HEAD" $(cat promisorlist) &&
grep "$(cat headhash) refs/heads/master" $(cat promisorlist)
'

# checkout master to force dynamic object fetch of blobs at HEAD.
test_expect_success 'verify checkout with dynamic object fetch' '
git -C pc1 rev-list --quiet --objects --missing=print HEAD >observed &&
Expand Down

0 comments on commit 5374a29

Please sign in to comment.