Skip to content

Commit b9ac6c5

Browse files
committed
Merge branch 'cc/multi-promisor'
Teach the lazy clone machinery that there can be more than one promisor remote and consult them in order when downloading missing objects on demand. * cc/multi-promisor: Move core_partial_clone_filter_default to promisor-remote.c Move repository_format_partial_clone to promisor-remote.c Remove fetch-object.{c,h} in favor of promisor-remote.{c,h} remote: add promisor and partial clone config to the doc partial-clone: add multiple remotes in the doc t0410: test fetching from many promisor remotes builtin/fetch: remove unique promisor remote limitation promisor-remote: parse remote.*.partialclonefilter Use promisor_remote_get_direct() and has_promisor_remote() promisor-remote: use repository_format_partial_clone promisor-remote: add promisor_remote_reinit() promisor-remote: implement promisor_remote_get_direct() Add initial support for many promisor remotes fetch-object: make functions return an error code t0410: remove pipes after git commands
2 parents de67293 + 4ca9474 commit b9ac6c5

27 files changed

+523
-172
lines changed

Documentation/config/remote.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,11 @@ remote.<name>.pruneTags::
7676
+
7777
See also `remote.<name>.prune` and the PRUNING section of
7878
linkgit:git-fetch[1].
79+
80+
remote.<name>.promisor::
81+
When set to true, this remote will be used to fetch promisor
82+
objects.
83+
84+
remote.<name>.partialclonefilter::
85+
The filter that will be applied when fetching from this
86+
promisor remote.

Documentation/technical/partial-clone.txt

Lines changed: 84 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,20 @@ advance* during clone and fetch operations and thereby reduce download
3030
times and disk usage. Missing objects can later be "demand fetched"
3131
if/when needed.
3232

33+
A remote that can later provide the missing objects is called a
34+
promisor remote, as it promises to send the objects when
35+
requested. Initialy Git supported only one promisor remote, the origin
36+
remote from which the user cloned and that was configured in the
37+
"extensions.partialClone" config option. Later support for more than
38+
one promisor remote has been implemented.
39+
3340
Use of partial clone requires that the user be online and the origin
34-
remote be available for on-demand fetching of missing objects. This may
35-
or may not be problematic for the user. For example, if the user can
36-
stay within the pre-selected subset of the source tree, they may not
37-
encounter any missing objects. Alternatively, the user could try to
38-
pre-fetch various objects if they know that they are going offline.
41+
remote or other promisor remotes be available for on-demand fetching
42+
of missing objects. This may or may not be problematic for the user.
43+
For example, if the user can stay within the pre-selected subset of
44+
the source tree, they may not encounter any missing objects.
45+
Alternatively, the user could try to pre-fetch various objects if they
46+
know that they are going offline.
3947

4048

4149
Non-Goals
@@ -100,18 +108,18 @@ or commits that reference missing trees.
100108
Handling Missing Objects
101109
------------------------
102110

103-
- An object may be missing due to a partial clone or fetch, or missing due
104-
to repository corruption. To differentiate these cases, the local
105-
repository specially indicates such filtered packfiles obtained from the
106-
promisor remote as "promisor packfiles".
111+
- An object may be missing due to a partial clone or fetch, or missing
112+
due to repository corruption. To differentiate these cases, the
113+
local repository specially indicates such filtered packfiles
114+
obtained from promisor remotes as "promisor packfiles".
107115
+
108116
These promisor packfiles consist of a "<name>.promisor" file with
109117
arbitrary contents (like the "<name>.keep" files), in addition to
110118
their "<name>.pack" and "<name>.idx" files.
111119

112120
- The local repository considers a "promisor object" to be an object that
113-
it knows (to the best of its ability) that the promisor remote has promised
114-
that it has, either because the local repository has that object in one of
121+
it knows (to the best of its ability) that promisor remotes have promised
122+
that they have, either because the local repository has that object in one of
115123
its promisor packfiles, or because another promisor object refers to it.
116124
+
117125
When Git encounters a missing object, Git can see if it is a promisor object
@@ -123,12 +131,12 @@ expensive-to-modify list of missing objects.[a]
123131
- Since almost all Git code currently expects any referenced object to be
124132
present locally and because we do not want to force every command to do
125133
a dry-run first, a fallback mechanism is added to allow Git to attempt
126-
to dynamically fetch missing objects from the promisor remote.
134+
to dynamically fetch missing objects from promisor remotes.
127135
+
128136
When the normal object lookup fails to find an object, Git invokes
129-
fetch-object to try to get the object from the server and then retry
130-
the object lookup. This allows objects to be "faulted in" without
131-
complicated prediction algorithms.
137+
promisor_remote_get_direct() to try to get the object from a promisor
138+
remote and then retry the object lookup. This allows objects to be
139+
"faulted in" without complicated prediction algorithms.
132140
+
133141
For efficiency reasons, no check as to whether the missing object is
134142
actually a promisor object is performed.
@@ -157,8 +165,7 @@ and prefetch those objects in bulk.
157165
+
158166
We are not happy with this global variable and would like to remove it,
159167
but that requires significant refactoring of the object code to pass an
160-
additional flag. We hope that concurrent efforts to add an ODB API can
161-
encompass this.
168+
additional flag.
162169

163170

164171
Fetching Missing Objects
@@ -182,21 +189,63 @@ has been updated to not use any object flags when the corresponding argument
182189
though they are not necessary.
183190

184191

192+
Using many promisor remotes
193+
---------------------------
194+
195+
Many promisor remotes can be configured and used.
196+
197+
This allows for example a user to have multiple geographically-close
198+
cache servers for fetching missing blobs while continuing to do
199+
filtered `git-fetch` commands from the central server.
200+
201+
When fetching objects, promisor remotes are tried one after the other
202+
until all the objects have been fetched.
203+
204+
Remotes that are considered "promisor" remotes are those specified by
205+
the following configuration variables:
206+
207+
- `extensions.partialClone = <name>`
208+
209+
- `remote.<name>.promisor = true`
210+
211+
- `remote.<name>.partialCloneFilter = ...`
212+
213+
Only one promisor remote can be configured using the
214+
`extensions.partialClone` config variable. This promisor remote will
215+
be the last one tried when fetching objects.
216+
217+
We decided to make it the last one we try, because it is likely that
218+
someone using many promisor remotes is doing so because the other
219+
promisor remotes are better for some reason (maybe they are closer or
220+
faster for some kind of objects) than the origin, and the origin is
221+
likely to be the remote specified by extensions.partialClone.
222+
223+
This justification is not very strong, but one choice had to be made,
224+
and anyway the long term plan should be to make the order somehow
225+
fully configurable.
226+
227+
For now though the other promisor remotes will be tried in the order
228+
they appear in the config file.
229+
185230
Current Limitations
186231
-------------------
187232

188-
- The remote used for a partial clone (or the first partial fetch
189-
following a regular clone) is marked as the "promisor remote".
233+
- It is not possible to specify the order in which the promisor
234+
remotes are tried in other ways than the order in which they appear
235+
in the config file.
190236
+
191-
We are currently limited to a single promisor remote and only that
192-
remote may be used for subsequent partial fetches.
237+
It is also not possible to specify an order to be used when fetching
238+
from one remote and a different order when fetching from another
239+
remote.
240+
241+
- It is not possible to push only specific objects to a promisor
242+
remote.
193243
+
194-
We accept this limitation because we believe initial users of this
195-
feature will be using it on repositories with a strong single central
196-
server.
244+
It is not possible to push at the same time to multiple promisor
245+
remote in a specific order.
197246

198-
- Dynamic object fetching will only ask the promisor remote for missing
199-
objects. We assume that the promisor remote has a complete view of the
247+
- Dynamic object fetching will only ask promisor remotes for missing
248+
objects. We assume that promisor remotes have a complete view of the
200249
repository and can satisfy all such requests.
201250

202251
- Repack essentially treats promisor and non-promisor packfiles as 2
@@ -218,15 +267,17 @@ server.
218267
Future Work
219268
-----------
220269

221-
- Allow more than one promisor remote and define a strategy for fetching
222-
missing objects from specific promisor remotes or of iterating over the
223-
set of promisor remotes until a missing object is found.
270+
- Improve the way to specify the order in which promisor remotes are
271+
tried.
224272
+
225-
A user might want to have multiple geographically-close cache servers
226-
for fetching missing blobs while continuing to do filtered `git-fetch`
227-
commands from the central server, for example.
273+
For example this could allow to specify explicitly something like:
274+
"When fetching from this remote, I want to use these promisor remotes
275+
in this order, though, when pushing or fetching to that remote, I want
276+
to use those promisor remotes in that order."
277+
278+
- Allow pushing to promisor remotes.
228279
+
229-
Or the user might want to work in a triangular work flow with multiple
280+
The user might want to work in a triangular work flow with multiple
230281
promisor remotes that each have an incomplete view of the repository.
231282

232283
- Allow repack to work on promisor packfiles (while keeping them distinct

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -884,7 +884,6 @@ LIB_OBJS += ewah/ewah_io.o
884884
LIB_OBJS += ewah/ewah_rlw.o
885885
LIB_OBJS += exec-cmd.o
886886
LIB_OBJS += fetch-negotiator.o
887-
LIB_OBJS += fetch-object.o
888887
LIB_OBJS += fetch-pack.o
889888
LIB_OBJS += fsck.o
890889
LIB_OBJS += fsmonitor.o
@@ -948,6 +947,7 @@ LIB_OBJS += preload-index.o
948947
LIB_OBJS += pretty.o
949948
LIB_OBJS += prio-queue.o
950949
LIB_OBJS += progress.o
950+
LIB_OBJS += promisor-remote.o
951951
LIB_OBJS += prompt.o
952952
LIB_OBJS += protocol.o
953953
LIB_OBJS += quote.o

builtin/cat-file.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "sha1-array.h"
1616
#include "packfile.h"
1717
#include "object-store.h"
18+
#include "promisor-remote.h"
1819

1920
struct batch_options {
2021
int enabled;
@@ -524,8 +525,8 @@ static int batch_objects(struct batch_options *opt)
524525
if (opt->all_objects) {
525526
struct object_cb_data cb;
526527

527-
if (repository_format_partial_clone)
528-
warning("This repository has extensions.partialClone set. Some objects may not be loaded.");
528+
if (has_promisor_remote())
529+
warning("This repository uses promisor remotes. Some objects may not be loaded.");
529530

530531
cb.opt = opt;
531532
cb.expand = &data;

builtin/fetch.c

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "list-objects-filter-options.h"
2525
#include "commit-reach.h"
2626
#include "branch.h"
27+
#include "promisor-remote.h"
2728

2829
#define FORCED_UPDATES_DELAY_WARNING_IN_MS (10 * 1000)
2930

@@ -1559,37 +1560,27 @@ static inline void fetch_one_setup_partial(struct remote *remote)
15591560
* If no prior partial clone/fetch and the current fetch DID NOT
15601561
* request a partial-fetch, do a normal fetch.
15611562
*/
1562-
if (!repository_format_partial_clone && !filter_options.choice)
1563+
if (!has_promisor_remote() && !filter_options.choice)
15631564
return;
15641565

15651566
/*
1566-
* If this is the FIRST partial-fetch request, we enable partial
1567-
* on this repo and remember the given filter-spec as the default
1568-
* for subsequent fetches to this remote.
1567+
* If this is a partial-fetch request, we enable partial on
1568+
* this repo if not already enabled and remember the given
1569+
* filter-spec as the default for subsequent fetches to this
1570+
* remote.
15691571
*/
1570-
if (!repository_format_partial_clone && filter_options.choice) {
1572+
if (filter_options.choice) {
15711573
partial_clone_register(remote->name, &filter_options);
15721574
return;
15731575
}
15741576

1575-
/*
1576-
* We are currently limited to only ONE promisor remote and only
1577-
* allow partial-fetches from the promisor remote.
1578-
*/
1579-
if (strcmp(remote->name, repository_format_partial_clone)) {
1580-
if (filter_options.choice)
1581-
die(_("--filter can only be used with the remote "
1582-
"configured in extensions.partialClone"));
1583-
return;
1584-
}
1585-
15861577
/*
15871578
* Do a partial-fetch from the promisor remote using either the
15881579
* explicitly given filter-spec or inherit the filter-spec from
15891580
* the config.
15901581
*/
15911582
if (!filter_options.choice)
1592-
partial_clone_get_default_filter_spec(&filter_options);
1583+
partial_clone_get_default_filter_spec(&filter_options, remote->name);
15931584
return;
15941585
}
15951586

@@ -1710,7 +1701,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
17101701
if (depth || deepen_since || deepen_not.nr)
17111702
deepen = 1;
17121703

1713-
if (filter_options.choice && !repository_format_partial_clone)
1704+
if (filter_options.choice && !has_promisor_remote())
17141705
die("--filter can only be used when extensions.partialClone is set");
17151706

17161707
if (all) {
@@ -1744,7 +1735,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
17441735
}
17451736

17461737
if (remote) {
1747-
if (filter_options.choice || repository_format_partial_clone)
1738+
if (filter_options.choice || has_promisor_remote())
17481739
fetch_one_setup_partial(remote);
17491740
result = fetch_one(remote, argc, argv, prune_tags_ok);
17501741
} else {

builtin/gc.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "pack-objects.h"
2828
#include "blob.h"
2929
#include "tree.h"
30+
#include "promisor-remote.h"
3031

3132
#define FAILED_RUN "failed to run %s"
3233

@@ -659,7 +660,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
659660
argv_array_push(&prune, prune_expire);
660661
if (quiet)
661662
argv_array_push(&prune, "--no-progress");
662-
if (repository_format_partial_clone)
663+
if (has_promisor_remote())
663664
argv_array_push(&prune,
664665
"--exclude-promisor-objects");
665666
if (run_command_v_opt(prune.argv, RUN_GIT_CMD))

builtin/index-pack.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
#include "thread-utils.h"
1515
#include "packfile.h"
1616
#include "object-store.h"
17-
#include "fetch-object.h"
17+
#include "promisor-remote.h"
1818

1919
static const char index_pack_usage[] =
2020
"git index-pack [-v] [-o <index-file>] [--keep | --keep=<msg>] [--verify] [--strict] (<pack-file> | --stdin [--fix-thin] [<pack-file>])";
@@ -1352,7 +1352,7 @@ static void fix_unresolved_deltas(struct hashfile *f)
13521352
sorted_by_pos[i] = &ref_deltas[i];
13531353
QSORT(sorted_by_pos, nr_ref_deltas, delta_pos_compare);
13541354

1355-
if (repository_format_partial_clone) {
1355+
if (has_promisor_remote()) {
13561356
/*
13571357
* Prefetch the delta bases.
13581358
*/
@@ -1366,8 +1366,8 @@ static void fix_unresolved_deltas(struct hashfile *f)
13661366
oid_array_append(&to_fetch, &d->oid);
13671367
}
13681368
if (to_fetch.nr)
1369-
fetch_objects(repository_format_partial_clone,
1370-
to_fetch.oid, to_fetch.nr);
1369+
promisor_remote_get_direct(the_repository,
1370+
to_fetch.oid, to_fetch.nr);
13711371
oid_array_clear(&to_fetch);
13721372
}
13731373

builtin/repack.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "midx.h"
1212
#include "packfile.h"
1313
#include "object-store.h"
14+
#include "promisor-remote.h"
1415

1516
static int delta_base_offset = 1;
1617
static int pack_kept_objects = -1;
@@ -361,7 +362,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
361362
argv_array_push(&cmd.args, "--all");
362363
argv_array_push(&cmd.args, "--reflog");
363364
argv_array_push(&cmd.args, "--indexed-objects");
364-
if (repository_format_partial_clone)
365+
if (has_promisor_remote())
365366
argv_array_push(&cmd.args, "--exclude-promisor-objects");
366367
if (write_bitmaps > 0)
367368
argv_array_push(&cmd.args, "--write-bitmap-index");

cache-tree.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "cache-tree.h"
66
#include "object-store.h"
77
#include "replace-object.h"
8+
#include "promisor-remote.h"
89

910
#ifndef DEBUG_CACHE_TREE
1011
#define DEBUG_CACHE_TREE 0
@@ -357,7 +358,7 @@ static int update_one(struct cache_tree *it,
357358
}
358359

359360
ce_missing_ok = mode == S_IFGITLINK || missing_ok ||
360-
(repository_format_partial_clone &&
361+
(has_promisor_remote() &&
361362
ce_skip_worktree(ce));
362363
if (is_null_oid(oid) ||
363364
(!ce_missing_ok && !has_object_file(oid))) {

cache.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -937,8 +937,6 @@ extern int grafts_replace_parents;
937937
#define GIT_REPO_VERSION 0
938938
#define GIT_REPO_VERSION_READ 1
939939
extern int repository_format_precious_objects;
940-
extern char *repository_format_partial_clone;
941-
extern const char *core_partial_clone_filter_default;
942940
extern int repository_format_worktree_config;
943941

944942
/*

config.c

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1379,11 +1379,6 @@ static int git_default_core_config(const char *var, const char *value, void *cb)
13791379
return 0;
13801380
}
13811381

1382-
if (!strcmp(var, "core.partialclonefilter")) {
1383-
return git_config_string(&core_partial_clone_filter_default,
1384-
var, value);
1385-
}
1386-
13871382
if (!strcmp(var, "core.usereplacerefs")) {
13881383
read_replace_refs = git_config_bool(var, value);
13891384
return 0;

0 commit comments

Comments
 (0)