31d51bb50e0aea32a5898e0876f660600fd1eb90
   1#include "cache.h"
   2#include "object-store.h"
   3#include "promisor-remote.h"
   4#include "config.h"
   5#include "transport.h"
   6
   7static char *repository_format_partial_clone;
   8
   9void set_repository_format_partial_clone(char *partial_clone)
  10{
  11        repository_format_partial_clone = xstrdup_or_null(partial_clone);
  12}
  13
  14static int fetch_refs(const char *remote_name, struct ref *ref)
  15{
  16        struct remote *remote;
  17        struct transport *transport;
  18        int original_fetch_if_missing = fetch_if_missing;
  19        int res;
  20
  21        fetch_if_missing = 0;
  22        remote = remote_get(remote_name);
  23        if (!remote->url[0])
  24                die(_("Remote with no URL"));
  25        transport = transport_get(remote, remote->url[0]);
  26
  27        transport_set_option(transport, TRANS_OPT_FROM_PROMISOR, "1");
  28        transport_set_option(transport, TRANS_OPT_NO_DEPENDENTS, "1");
  29        res = transport_fetch_refs(transport, ref);
  30        fetch_if_missing = original_fetch_if_missing;
  31
  32        return res;
  33}
  34
  35static int fetch_objects(const char *remote_name,
  36                         const struct object_id *oids,
  37                         int oid_nr)
  38{
  39        struct ref *ref = NULL;
  40        int i;
  41
  42        for (i = 0; i < oid_nr; i++) {
  43                struct ref *new_ref = alloc_ref(oid_to_hex(&oids[i]));
  44                oidcpy(&new_ref->old_oid, &oids[i]);
  45                new_ref->exact_oid = 1;
  46                new_ref->next = ref;
  47                ref = new_ref;
  48        }
  49        return fetch_refs(remote_name, ref);
  50}
  51
  52static struct promisor_remote *promisors;
  53static struct promisor_remote **promisors_tail = &promisors;
  54
  55static struct promisor_remote *promisor_remote_new(const char *remote_name)
  56{
  57        struct promisor_remote *r;
  58
  59        if (*remote_name == '/') {
  60                warning(_("promisor remote name cannot begin with '/': %s"),
  61                        remote_name);
  62                return NULL;
  63        }
  64
  65        FLEX_ALLOC_STR(r, name, remote_name);
  66
  67        *promisors_tail = r;
  68        promisors_tail = &r->next;
  69
  70        return r;
  71}
  72
  73static struct promisor_remote *promisor_remote_lookup(const char *remote_name,
  74                                                      struct promisor_remote **previous)
  75{
  76        struct promisor_remote *r, *p;
  77
  78        for (p = NULL, r = promisors; r; p = r, r = r->next)
  79                if (!strcmp(r->name, remote_name)) {
  80                        if (previous)
  81                                *previous = p;
  82                        return r;
  83                }
  84
  85        return NULL;
  86}
  87
  88static void promisor_remote_move_to_tail(struct promisor_remote *r,
  89                                         struct promisor_remote *previous)
  90{
  91        if (previous)
  92                previous->next = r->next;
  93        else
  94                promisors = r->next ? r->next : r;
  95        r->next = NULL;
  96        *promisors_tail = r;
  97        promisors_tail = &r->next;
  98}
  99
 100static int promisor_remote_config(const char *var, const char *value, void *data)
 101{
 102        const char *name;
 103        int namelen;
 104        const char *subkey;
 105
 106        if (parse_config_key(var, "remote", &name, &namelen, &subkey) < 0)
 107                return 0;
 108
 109        if (!strcmp(subkey, "promisor")) {
 110                char *remote_name;
 111
 112                if (!git_config_bool(var, value))
 113                        return 0;
 114
 115                remote_name = xmemdupz(name, namelen);
 116
 117                if (!promisor_remote_lookup(remote_name, NULL))
 118                        promisor_remote_new(remote_name);
 119
 120                free(remote_name);
 121                return 0;
 122        }
 123        if (!strcmp(subkey, "partialclonefilter")) {
 124                struct promisor_remote *r;
 125                char *remote_name = xmemdupz(name, namelen);
 126
 127                r = promisor_remote_lookup(remote_name, NULL);
 128                if (!r)
 129                        r = promisor_remote_new(remote_name);
 130
 131                free(remote_name);
 132
 133                if (!r)
 134                        return 0;
 135
 136                return git_config_string(&r->partial_clone_filter, var, value);
 137        }
 138
 139        return 0;
 140}
 141
 142static int initialized;
 143
 144static void promisor_remote_init(void)
 145{
 146        if (initialized)
 147                return;
 148        initialized = 1;
 149
 150        git_config(promisor_remote_config, NULL);
 151
 152        if (repository_format_partial_clone) {
 153                struct promisor_remote *o, *previous;
 154
 155                o = promisor_remote_lookup(repository_format_partial_clone,
 156                                           &previous);
 157                if (o)
 158                        promisor_remote_move_to_tail(o, previous);
 159                else
 160                        promisor_remote_new(repository_format_partial_clone);
 161        }
 162}
 163
 164static void promisor_remote_clear(void)
 165{
 166        while (promisors) {
 167                struct promisor_remote *r = promisors;
 168                promisors = promisors->next;
 169                free(r);
 170        }
 171
 172        promisors_tail = &promisors;
 173}
 174
 175void promisor_remote_reinit(void)
 176{
 177        initialized = 0;
 178        promisor_remote_clear();
 179        promisor_remote_init();
 180}
 181
 182struct promisor_remote *promisor_remote_find(const char *remote_name)
 183{
 184        promisor_remote_init();
 185
 186        if (!remote_name)
 187                return promisors;
 188
 189        return promisor_remote_lookup(remote_name, NULL);
 190}
 191
 192int has_promisor_remote(void)
 193{
 194        return !!promisor_remote_find(NULL);
 195}
 196
 197static int remove_fetched_oids(struct repository *repo,
 198                               struct object_id **oids,
 199                               int oid_nr, int to_free)
 200{
 201        int i, remaining_nr = 0;
 202        int *remaining = xcalloc(oid_nr, sizeof(*remaining));
 203        struct object_id *old_oids = *oids;
 204        struct object_id *new_oids;
 205
 206        for (i = 0; i < oid_nr; i++)
 207                if (oid_object_info_extended(repo, &old_oids[i], NULL,
 208                                             OBJECT_INFO_SKIP_FETCH_OBJECT)) {
 209                        remaining[i] = 1;
 210                        remaining_nr++;
 211                }
 212
 213        if (remaining_nr) {
 214                int j = 0;
 215                new_oids = xcalloc(remaining_nr, sizeof(*new_oids));
 216                for (i = 0; i < oid_nr; i++)
 217                        if (remaining[i])
 218                                oidcpy(&new_oids[j++], &old_oids[i]);
 219                *oids = new_oids;
 220                if (to_free)
 221                        free(old_oids);
 222        }
 223
 224        free(remaining);
 225
 226        return remaining_nr;
 227}
 228
 229int promisor_remote_get_direct(struct repository *repo,
 230                               const struct object_id *oids,
 231                               int oid_nr)
 232{
 233        struct promisor_remote *r;
 234        struct object_id *remaining_oids = (struct object_id *)oids;
 235        int remaining_nr = oid_nr;
 236        int to_free = 0;
 237        int res = -1;
 238
 239        promisor_remote_init();
 240
 241        for (r = promisors; r; r = r->next) {
 242                if (fetch_objects(r->name, remaining_oids, remaining_nr) < 0) {
 243                        if (remaining_nr == 1)
 244                                continue;
 245                        remaining_nr = remove_fetched_oids(repo, &remaining_oids,
 246                                                         remaining_nr, to_free);
 247                        if (remaining_nr) {
 248                                to_free = 1;
 249                                continue;
 250                        }
 251                }
 252                res = 0;
 253                break;
 254        }
 255
 256        if (to_free)
 257                free(remaining_oids);
 258
 259        return res;
 260}