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