Merge branch 'ds/partial-bundles'

Bundle file format gets extended to allow a partial bundle,
filtered by similar criteria you would give when making a
partial/lazy clone.

* ds/partial-bundles:
  clone: fail gracefully when cloning filtered bundle
  bundle: unbundle promisor packs
  bundle: create filtered bundles
  rev-list: move --filter parsing into revision.c
  bundle: parse filter capability
  list-objects: handle NULL function pointers
  MyFirstObjectWalk: update recommended usage
  list-objects: consolidate traverse_commit_list[_filtered]
  pack-bitmap: drop filter in prepare_bitmap_walk()
  pack-objects: use rev.filter when possible
  revision: put object filter into struct rev_info
  list-objects-filter-options: create copy helper
  index-pack: document and test the --promisor option
This commit is contained in:
Junio C Hamano
2022-03-21 15:14:24 -07:00
20 changed files with 317 additions and 133 deletions

View File

@ -11,7 +11,7 @@
#include "run-command.h"
#include "refs.h"
#include "strvec.h"
#include "list-objects-filter-options.h"
static const char v2_bundle_signature[] = "# v2 git bundle\n";
static const char v3_bundle_signature[] = "# v3 git bundle\n";
@ -33,6 +33,7 @@ void bundle_header_release(struct bundle_header *header)
{
string_list_clear(&header->prerequisites, 1);
string_list_clear(&header->references, 1);
list_objects_filter_release(&header->filter);
}
static int parse_capability(struct bundle_header *header, const char *capability)
@ -45,6 +46,10 @@ static int parse_capability(struct bundle_header *header, const char *capability
header->hash_algo = &hash_algos[algo];
return 0;
}
if (skip_prefix(capability, "filter=", &arg)) {
parse_list_objects_filter(&header->filter, arg);
return 0;
}
return error(_("unknown capability '%s'"), capability);
}
@ -220,6 +225,8 @@ int verify_bundle(struct repository *r,
req_nr = revs.pending.nr;
setup_revisions(2, argv, &revs, NULL);
list_objects_filter_copy(&revs.filter, &header->filter);
if (prepare_revision_walk(&revs))
die(_("revision walk setup failed"));
@ -259,6 +266,12 @@ int verify_bundle(struct repository *r,
r->nr),
(uintmax_t)r->nr);
list_refs(r, 0, NULL);
if (header->filter.choice) {
printf_ln("The bundle uses this filter: %s",
list_objects_filter_spec(&header->filter));
}
r = &header->prerequisites;
if (!r->nr) {
printf_ln(_("The bundle records a complete history."));
@ -319,6 +332,9 @@ static int write_pack_data(int bundle_fd, struct rev_info *revs, struct strvec *
"--stdout", "--thin", "--delta-base-offset",
NULL);
strvec_pushv(&pack_objects.args, pack_options->v);
if (revs->filter.choice)
strvec_pushf(&pack_objects.args, "--filter=%s",
list_objects_filter_spec(&revs->filter));
pack_objects.in = -1;
pack_objects.out = bundle_fd;
pack_objects.git_cmd = 1;
@ -486,10 +502,37 @@ int create_bundle(struct repository *r, const char *path,
int bundle_to_stdout;
int ref_count = 0;
struct rev_info revs, revs_copy;
int min_version = the_hash_algo == &hash_algos[GIT_HASH_SHA1] ? 2 : 3;
int min_version = 2;
struct bundle_prerequisites_info bpi;
int i;
/* init revs to list objects for pack-objects later */
save_commit_buffer = 0;
repo_init_revisions(r, &revs, NULL);
/*
* Pre-initialize the '--objects' flag so we can parse a
* --filter option successfully.
*/
revs.tree_objects = revs.blob_objects = 1;
argc = setup_revisions(argc, argv, &revs, NULL);
/*
* Reasons to require version 3:
*
* 1. @object-format is required because our hash algorithm is not
* SHA1.
* 2. @filter is required because we parsed an object filter.
*/
if (the_hash_algo != &hash_algos[GIT_HASH_SHA1] || revs.filter.choice)
min_version = 3;
if (argc > 1) {
error(_("unrecognized argument: %s"), argv[1]);
goto err;
}
bundle_to_stdout = !strcmp(path, "-");
if (bundle_to_stdout)
bundle_fd = 1;
@ -512,17 +555,14 @@ int create_bundle(struct repository *r, const char *path,
write_or_die(bundle_fd, capability, strlen(capability));
write_or_die(bundle_fd, the_hash_algo->name, strlen(the_hash_algo->name));
write_or_die(bundle_fd, "\n", 1);
}
/* init revs to list objects for pack-objects later */
save_commit_buffer = 0;
repo_init_revisions(r, &revs, NULL);
argc = setup_revisions(argc, argv, &revs, NULL);
if (argc > 1) {
error(_("unrecognized argument: %s"), argv[1]);
goto err;
if (revs.filter.choice) {
const char *value = expand_list_objects_filter_spec(&revs.filter);
capability = "@filter=";
write_or_die(bundle_fd, capability, strlen(capability));
write_or_die(bundle_fd, value, strlen(value));
write_or_die(bundle_fd, "\n", 1);
}
}
/* save revs.pending in revs_copy for later use */
@ -544,6 +584,12 @@ int create_bundle(struct repository *r, const char *path,
die("revision walk setup failed");
bpi.fd = bundle_fd;
bpi.pending = &revs_copy.pending;
/*
* Remove any object walking here. We only care about commits and
* tags here. The revs_copy has the right instances of these values.
*/
revs.blob_objects = revs.tree_objects = 0;
traverse_commit_list(&revs, write_bundle_prerequisites, NULL, &bpi);
object_array_remove_duplicates(&revs_copy.pending);
@ -574,6 +620,10 @@ int unbundle(struct repository *r, struct bundle_header *header,
struct child_process ip = CHILD_PROCESS_INIT;
strvec_pushl(&ip.args, "index-pack", "--fix-thin", "--stdin", NULL);
/* If there is a filter, then we need to create the promisor pack. */
if (header->filter.choice)
strvec_push(&ip.args, "--promisor=from-bundle");
if (extra_index_pack_args) {
strvec_pushv(&ip.args, extra_index_pack_args->v);
strvec_clear(extra_index_pack_args);