1
0
Fork 0
mirror of https://github.com/git/git.git synced 2024-05-12 02:26:28 +02:00
git/promisor-remote.c
Jonathan Tan ef830cc434 promisor-remote: teach lazy-fetch in any repo
This is one step towards supporting partial clone submodules.

Even after this patch, we will still lack partial clone submodules
support, primarily because a lot of Git code that accesses submodule
objects does so by adding their object stores as alternates, meaning
that any lazy fetches that would occur in the submodule would be done
based on the config of the superproject, not of the submodule. This also
prevents testing of the functionality in this patch by user-facing
commands. So for now, test this mechanism using a test helper.

Besides that, there is some code that uses the wrapper functions
like has_promisor_remote(). Those will need to be checked to see if they
could support the non-wrapper functions instead (and thus support any
repository, not just the_repository).

Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Reviewed-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-06-28 09:58:01 -07:00

266 lines
6.2 KiB
C

#include "cache.h"
#include "object-store.h"
#include "promisor-remote.h"
#include "config.h"
#include "transport.h"
#include "strvec.h"
struct promisor_remote_config {
struct promisor_remote *promisors;
struct promisor_remote **promisors_tail;
};
static int fetch_objects(struct repository *repo,
const char *remote_name,
const struct object_id *oids,
int oid_nr)
{
struct child_process child = CHILD_PROCESS_INIT;
int i;
FILE *child_in;
child.git_cmd = 1;
child.in = -1;
if (repo != the_repository)
prepare_other_repo_env(&child.env_array, repo->gitdir);
strvec_pushl(&child.args, "-c", "fetch.negotiationAlgorithm=noop",
"fetch", remote_name, "--no-tags",
"--no-write-fetch-head", "--recurse-submodules=no",
"--filter=blob:none", "--stdin", NULL);
if (start_command(&child))
die(_("promisor-remote: unable to fork off fetch subprocess"));
child_in = xfdopen(child.in, "w");
for (i = 0; i < oid_nr; i++) {
if (fputs(oid_to_hex(&oids[i]), child_in) < 0)
die_errno(_("promisor-remote: could not write to fetch subprocess"));
if (fputc('\n', child_in) < 0)
die_errno(_("promisor-remote: could not write to fetch subprocess"));
}
if (fclose(child_in) < 0)
die_errno(_("promisor-remote: could not close stdin to fetch subprocess"));
return finish_command(&child) ? -1 : 0;
}
static struct promisor_remote *promisor_remote_new(struct promisor_remote_config *config,
const char *remote_name)
{
struct promisor_remote *r;
if (*remote_name == '/') {
warning(_("promisor remote name cannot begin with '/': %s"),
remote_name);
return NULL;
}
FLEX_ALLOC_STR(r, name, remote_name);
*config->promisors_tail = r;
config->promisors_tail = &r->next;
return r;
}
static struct promisor_remote *promisor_remote_lookup(struct promisor_remote_config *config,
const char *remote_name,
struct promisor_remote **previous)
{
struct promisor_remote *r, *p;
for (p = NULL, r = config->promisors; r; p = r, r = r->next)
if (!strcmp(r->name, remote_name)) {
if (previous)
*previous = p;
return r;
}
return NULL;
}
static void promisor_remote_move_to_tail(struct promisor_remote_config *config,
struct promisor_remote *r,
struct promisor_remote *previous)
{
if (r->next == NULL)
return;
if (previous)
previous->next = r->next;
else
config->promisors = r->next ? r->next : r;
r->next = NULL;
*config->promisors_tail = r;
config->promisors_tail = &r->next;
}
static int promisor_remote_config(const char *var, const char *value, void *data)
{
struct promisor_remote_config *config = data;
const char *name;
size_t namelen;
const char *subkey;
if (parse_config_key(var, "remote", &name, &namelen, &subkey) < 0)
return 0;
if (!strcmp(subkey, "promisor")) {
char *remote_name;
if (!git_config_bool(var, value))
return 0;
remote_name = xmemdupz(name, namelen);
if (!promisor_remote_lookup(config, remote_name, NULL))
promisor_remote_new(config, remote_name);
free(remote_name);
return 0;
}
if (!strcmp(subkey, "partialclonefilter")) {
struct promisor_remote *r;
char *remote_name = xmemdupz(name, namelen);
r = promisor_remote_lookup(config, remote_name, NULL);
if (!r)
r = promisor_remote_new(config, remote_name);
free(remote_name);
if (!r)
return 0;
return git_config_string(&r->partial_clone_filter, var, value);
}
return 0;
}
static void promisor_remote_init(struct repository *r)
{
struct promisor_remote_config *config;
if (r->promisor_remote_config)
return;
config = r->promisor_remote_config =
xcalloc(sizeof(*r->promisor_remote_config), 1);
config->promisors_tail = &config->promisors;
repo_config(r, promisor_remote_config, config);
if (r->repository_format_partial_clone) {
struct promisor_remote *o, *previous;
o = promisor_remote_lookup(config,
r->repository_format_partial_clone,
&previous);
if (o)
promisor_remote_move_to_tail(config, o, previous);
else
promisor_remote_new(config, r->repository_format_partial_clone);
}
}
void promisor_remote_clear(struct promisor_remote_config *config)
{
while (config->promisors) {
struct promisor_remote *r = config->promisors;
config->promisors = config->promisors->next;
free(r);
}
config->promisors_tail = &config->promisors;
}
void repo_promisor_remote_reinit(struct repository *r)
{
promisor_remote_clear(r->promisor_remote_config);
FREE_AND_NULL(r->promisor_remote_config);
promisor_remote_init(r);
}
struct promisor_remote *repo_promisor_remote_find(struct repository *r,
const char *remote_name)
{
promisor_remote_init(r);
if (!remote_name)
return r->promisor_remote_config->promisors;
return promisor_remote_lookup(r->promisor_remote_config, remote_name, NULL);
}
int repo_has_promisor_remote(struct repository *r)
{
return !!repo_promisor_remote_find(r, NULL);
}
static int remove_fetched_oids(struct repository *repo,
struct object_id **oids,
int oid_nr, int to_free)
{
int i, remaining_nr = 0;
int *remaining = xcalloc(oid_nr, sizeof(*remaining));
struct object_id *old_oids = *oids;
struct object_id *new_oids;
for (i = 0; i < oid_nr; i++)
if (oid_object_info_extended(repo, &old_oids[i], NULL,
OBJECT_INFO_SKIP_FETCH_OBJECT)) {
remaining[i] = 1;
remaining_nr++;
}
if (remaining_nr) {
int j = 0;
CALLOC_ARRAY(new_oids, remaining_nr);
for (i = 0; i < oid_nr; i++)
if (remaining[i])
oidcpy(&new_oids[j++], &old_oids[i]);
*oids = new_oids;
if (to_free)
free(old_oids);
}
free(remaining);
return remaining_nr;
}
int promisor_remote_get_direct(struct repository *repo,
const struct object_id *oids,
int oid_nr)
{
struct promisor_remote *r;
struct object_id *remaining_oids = (struct object_id *)oids;
int remaining_nr = oid_nr;
int to_free = 0;
int res = -1;
if (oid_nr == 0)
return 0;
promisor_remote_init(repo);
for (r = repo->promisor_remote_config->promisors; r; r = r->next) {
if (fetch_objects(repo, r->name, remaining_oids, remaining_nr) < 0) {
if (remaining_nr == 1)
continue;
remaining_nr = remove_fetched_oids(repo, &remaining_oids,
remaining_nr, to_free);
if (remaining_nr) {
to_free = 1;
continue;
}
}
res = 0;
break;
}
if (to_free)
free(remaining_oids);
return res;
}