Skip to content

Commit cccf74e

Browse files
pcloudsgitster
authored andcommitted
fetch, upload-pack: --deepen=N extends shallow boundary by N commits
In git-fetch, --depth argument is always relative with the latest remote refs. This makes it a bit difficult to cover this use case, where the user wants to make the shallow history, say 3 levels deeper. It would work if remote refs have not moved yet, but nobody can guarantee that, especially when that use case is performed a couple months after the last clone or "git fetch --depth". Also, modifying shallow boundary using --depth does not work well with clones created by --since or --not. This patch fixes that. A new argument --deepen=<N> will add <N> more (*) parent commits to the current history regardless of where remote refs are. Have/Want negotiation is still respected. So if remote refs move, the server will send two chunks: one between "have" and "want" and another to extend shallow history. In theory, the client could send no "want"s in order to get the second chunk only. But the protocol does not allow that. Either you send no want lines, which means ls-remote; or you have to send at least one want line that carries deep-relative to the server.. The main work was done by Dongcan Jiang. I fixed it up here and there. And of course all the bugs belong to me. (*) We could even support --deepen=<N> where <N> is negative. In that case we can cut some history from the shallow clone. This operation (and --depth=<shorter depth>) does not require interaction with remote side (and more complicated to implement as a result). Helped-by: Duy Nguyen <[email protected]> Helped-by: Eric Sunshine <[email protected]> Helped-by: Junio C Hamano <[email protected]> Signed-off-by: Dongcan Jiang <[email protected]> Signed-off-by: Nguyễn Thái Ngọc Duy <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 079aa97 commit cccf74e

15 files changed

+132
-6
lines changed

Documentation/fetch-options.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@
1414
linkgit:git-clone[1]), deepen or shorten the history to the specified
1515
number of commits. Tags for the deepened commits are not fetched.
1616

17+
--deepen=<depth>::
18+
Similar to --depth, except it specifies the number of commits
19+
from the current shallow boundary instead of from the tip of
20+
each remote branch history.
21+
1722
--shallow-since=<date>::
1823
Deepen or shorten the history of a shallow repository to
1924
include all reachable commits after <date>.

Documentation/git-fetch-pack.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,11 @@ be in a separate packet, and the list must end with a flush packet.
9696
exclude commits reachable from a specified remote branch or tag.
9797
This option can be specified multiple times.
9898

99+
--deepen-relative::
100+
Argument --depth specifies the number of commits from the
101+
current shallow boundary instead of from the tip of each
102+
remote branch history.
103+
99104
--no-progress::
100105
Do not show the progress.
101106

Documentation/gitremote-helpers.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,10 @@ set by Git if the remote helper has the 'option' capability.
422422
Deepens the history of a shallow repository excluding ref.
423423
Multiple options add up.
424424

425+
'option deepen-relative {'true'|'false'}::
426+
Deepens the history of a shallow repository relative to
427+
current boundary. Only valid when used with "option depth".
428+
425429
'option followtags' {'true'|'false'}::
426430
If enabled the helper should automatically fetch annotated
427431
tag objects if the object the tag points at was transferred

Documentation/technical/protocol-capabilities.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,13 @@ specific revision, instead of depth. Internally it's equivalent of
197197
doing "rev-list --not <rev>" on the server side. "deepen-not"
198198
cannot be used with "deepen", but can be used with "deepen-since".
199199

200+
deepen-relative
201+
---------------
202+
203+
If this capability is requested by the client, the semantics of
204+
"deepen" command is changed. The "depth" argument is the depth from
205+
the current shallow boundary, instead of the depth from remote refs.
206+
200207
no-progress
201208
-----------
202209

builtin/fetch-pack.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,10 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
113113
string_list_append(&deepen_not, arg);
114114
continue;
115115
}
116+
if (!strcmp(arg, "--deepen-relative")) {
117+
args.deepen_relative = 1;
118+
continue;
119+
}
116120
if (!strcmp("--no-progress", arg)) {
117121
args.no_progress = 1;
118122
continue;

builtin/fetch.c

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ static int fetch_prune_config = -1; /* unspecified */
3434
static int prune = -1; /* unspecified */
3535
#define PRUNE_BY_DEFAULT 0 /* do we prune by default? */
3636

37-
static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity;
37+
static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity, deepen_relative;
3838
static int progress = -1, recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
3939
static int tags = TAGS_DEFAULT, unshallow, update_shallow, deepen;
4040
static int max_children = 1;
@@ -121,6 +121,8 @@ static struct option builtin_fetch_options[] = {
121121
N_("deepen history of shallow repository based on time")),
122122
OPT_STRING_LIST(0, "shallow-exclude", &deepen_not, N_("revision"),
123123
N_("deepen history of shallow clone by excluding rev")),
124+
OPT_INTEGER(0, "deepen", &deepen_relative,
125+
N_("deepen history of shallow clone")),
124126
{ OPTION_SET_INT, 0, "unshallow", &unshallow, NULL,
125127
N_("convert to a complete repository"),
126128
PARSE_OPT_NONEG | PARSE_OPT_NOARG, NULL, 1 },
@@ -881,6 +883,8 @@ static struct transport *prepare_transport(struct remote *remote, int deepen)
881883
if (deepen && deepen_not.nr)
882884
set_option(transport, TRANS_OPT_DEEPEN_NOT,
883885
(const char *)&deepen_not);
886+
if (deepen_relative)
887+
set_option(transport, TRANS_OPT_DEEPEN_RELATIVE, "yes");
884888
if (update_shallow)
885889
set_option(transport, TRANS_OPT_UPDATE_SHALLOW, "yes");
886890
return transport;
@@ -906,6 +910,7 @@ static void backfill_tags(struct transport *transport, struct ref *ref_map)
906910

907911
transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL);
908912
transport_set_option(transport, TRANS_OPT_DEPTH, "0");
913+
transport_set_option(transport, TRANS_OPT_DEEPEN_RELATIVE, NULL);
909914
fetch_refs(transport, ref_map);
910915

911916
if (gsecondary) {
@@ -1177,6 +1182,13 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
11771182
argc = parse_options(argc, argv, prefix,
11781183
builtin_fetch_options, builtin_fetch_usage, 0);
11791184

1185+
if (deepen_relative) {
1186+
if (deepen_relative < 0)
1187+
die(_("Negative depth in --deepen is not supported"));
1188+
if (depth)
1189+
die(_("--deepen and --depth are mutually exclusive"));
1190+
depth = xstrfmt("%d", deepen_relative);
1191+
}
11801192
if (unshallow) {
11811193
if (depth)
11821194
die(_("--depth and --unshallow cannot be used together"));

fetch-pack.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,7 @@ static int find_common(struct fetch_pack_args *args,
324324
if (no_done) strbuf_addstr(&c, " no-done");
325325
if (use_sideband == 2) strbuf_addstr(&c, " side-band-64k");
326326
if (use_sideband == 1) strbuf_addstr(&c, " side-band");
327+
if (args->deepen_relative) strbuf_addstr(&c, " deepen-relative");
327328
if (args->use_thin_pack) strbuf_addstr(&c, " thin-pack");
328329
if (args->no_progress) strbuf_addstr(&c, " no-progress");
329330
if (args->include_tag) strbuf_addstr(&c, " include-tag");
@@ -883,6 +884,8 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
883884
deepen_not_ok = 1;
884885
else if (args->deepen_not)
885886
die(_("Server does not support --shallow-exclude"));
887+
if (!server_supports("deepen-relative") && args->deepen_relative)
888+
die(_("Server does not support --deepen"));
886889

887890
if (everything_local(args, &ref, sought, nr_sought)) {
888891
packet_flush(fd[1]);

fetch-pack.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ struct fetch_pack_args {
1212
int depth;
1313
const char *deepen_since;
1414
const struct string_list *deepen_not;
15+
unsigned deepen_relative:1;
1516
unsigned quiet:1;
1617
unsigned keep_pack:1;
1718
unsigned lock_pack:1;

remote-curl.c

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ struct options {
3030
dry_run : 1,
3131
thin : 1,
3232
/* One of the SEND_PACK_PUSH_CERT_* constants. */
33-
push_cert : 2;
33+
push_cert : 2,
34+
deepen_relative : 1;
3435
};
3536
static struct options options;
3637
static struct string_list cas_options = STRING_LIST_INIT_DUP;
@@ -70,6 +71,15 @@ static int set_option(const char *name, const char *value)
7071
string_list_append(&options.deepen_not, value);
7172
return 0;
7273
}
74+
else if (!strcmp(name, "deepen-relative")) {
75+
if (!strcmp(value, "true"))
76+
options.deepen_relative = 1;
77+
else if (!strcmp(value, "false"))
78+
options.deepen_relative = 0;
79+
else
80+
return -1;
81+
return 0;
82+
}
7383
else if (!strcmp(name, "followtags")) {
7484
if (!strcmp(value, "true"))
7585
options.followtags = 1;
@@ -761,6 +771,8 @@ static int fetch_git(struct discovery *heads,
761771
for (i = 0; i < options.deepen_not.nr; i++)
762772
argv_array_pushf(&args, "--shallow-exclude=%s",
763773
options.deepen_not.items[i].string);
774+
if (options.deepen_relative && options.depth)
775+
argv_array_push(&args, "--deepen-relative");
764776
argv_array_push(&args, url.buf);
765777

766778
for (i = 0; i < nr_heads; i++) {

t/t5500-fetch-pack.sh

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -682,4 +682,27 @@ test_expect_success 'fetch exclude tag one' '
682682
test_cmp expected actual
683683
'
684684

685+
test_expect_success 'fetching deepen' '
686+
test_create_repo shallow-deepen &&
687+
(
688+
cd shallow-deepen &&
689+
test_commit one &&
690+
test_commit two &&
691+
test_commit three &&
692+
git clone --depth 1 "file://$(pwd)/." deepen &&
693+
test_commit four &&
694+
git -C deepen log --pretty=tformat:%s master >actual &&
695+
echo three >expected &&
696+
test_cmp expected actual &&
697+
git -C deepen fetch --deepen=1 &&
698+
git -C deepen log --pretty=tformat:%s origin/master >actual &&
699+
cat >expected <<-\EOF &&
700+
four
701+
three
702+
two
703+
EOF
704+
test_cmp expected actual
705+
)
706+
'
707+
685708
test_done

t/t5539-fetch-http-shallow.sh

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,5 +120,31 @@ test_expect_success 'fetch exclude tag one' '
120120
test_cmp expected actual
121121
'
122122

123+
test_expect_success 'fetching deepen' '
124+
test_create_repo shallow-deepen &&
125+
(
126+
cd shallow-deepen &&
127+
test_commit one &&
128+
test_commit two &&
129+
test_commit three &&
130+
mv .git "$HTTPD_DOCUMENT_ROOT_PATH/shallow-deepen.git" &&
131+
git clone --depth 1 $HTTPD_URL/smart/shallow-deepen.git deepen &&
132+
mv "$HTTPD_DOCUMENT_ROOT_PATH/shallow-deepen.git" .git &&
133+
test_commit four &&
134+
git -C deepen log --pretty=tformat:%s master >actual &&
135+
echo three >expected &&
136+
test_cmp expected actual &&
137+
mv .git "$HTTPD_DOCUMENT_ROOT_PATH/shallow-deepen.git" &&
138+
git -C deepen fetch --deepen=1 &&
139+
git -C deepen log --pretty=tformat:%s origin/master >actual &&
140+
cat >expected <<-\EOF &&
141+
four
142+
three
143+
two
144+
EOF
145+
test_cmp expected actual
146+
)
147+
'
148+
123149
stop_httpd
124150
test_done

transport-helper.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,7 @@ static const char *boolean_options[] = {
258258
TRANS_OPT_THIN,
259259
TRANS_OPT_KEEP,
260260
TRANS_OPT_FOLLOWTAGS,
261+
TRANS_OPT_DEEPEN_RELATIVE
261262
};
262263

263264
static int strbuf_set_helper_option(struct helper_data *data,

transport.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,9 @@ static int set_git_option(struct git_transport_options *opts,
157157
} else if (!strcmp(name, TRANS_OPT_DEEPEN_NOT)) {
158158
opts->deepen_not = (const struct string_list *)value;
159159
return 0;
160+
} else if (!strcmp(name, TRANS_OPT_DEEPEN_RELATIVE)) {
161+
opts->deepen_relative = !!value;
162+
return 0;
160163
}
161164
return 1;
162165
}
@@ -213,6 +216,7 @@ static int fetch_refs_via_pack(struct transport *transport,
213216
args.depth = data->options.depth;
214217
args.deepen_since = data->options.deepen_since;
215218
args.deepen_not = data->options.deepen_not;
219+
args.deepen_relative = data->options.deepen_relative;
216220
args.check_self_contained_and_connected =
217221
data->options.check_self_contained_and_connected;
218222
args.cloning = transport->cloning;

transport.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ struct git_transport_options {
1414
unsigned check_self_contained_and_connected : 1;
1515
unsigned self_contained_and_connected : 1;
1616
unsigned update_shallow : 1;
17+
unsigned deepen_relative : 1;
1718
int depth;
1819
const char *deepen_since;
1920
const struct string_list *deepen_not;
@@ -181,6 +182,9 @@ int transport_restrict_protocols(void);
181182
/* Limit the depth of the fetch based on revs if not null */
182183
#define TRANS_OPT_DEEPEN_NOT "deepen-not"
183184

185+
/* Limit the deepen of the fetch if not null */
186+
#define TRANS_OPT_DEEPEN_RELATIVE "deepen-relative"
187+
184188
/* Aggressively fetch annotated tags if possible */
185189
#define TRANS_OPT_FOLLOWTAGS "followtags"
186190

upload-pack.c

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ static const char upload_pack_usage[] = "git upload-pack [--strict] [--timeout=<
3232

3333
static unsigned long oldest_have;
3434

35+
static int deepen_relative;
3536
static int multi_ack;
3637
static int no_done;
3738
static int use_thin_pack, use_ofs_delta, use_include_tag;
@@ -674,7 +675,8 @@ static void send_unshallow(const struct object_array *shallows)
674675
}
675676
}
676677

677-
static void deepen(int depth, const struct object_array *shallows)
678+
static void deepen(int depth, int deepen_relative,
679+
struct object_array *shallows)
678680
{
679681
if (depth == INFINITE_DEPTH && !is_repository_shallow()) {
680682
int i;
@@ -683,6 +685,17 @@ static void deepen(int depth, const struct object_array *shallows)
683685
struct object *object = shallows->objects[i].item;
684686
object->flags |= NOT_SHALLOW;
685687
}
688+
} else if (deepen_relative) {
689+
struct object_array reachable_shallows = OBJECT_ARRAY_INIT;
690+
struct commit_list *result;
691+
692+
get_reachable_list(shallows, &reachable_shallows);
693+
result = get_shallow_commits(&reachable_shallows,
694+
depth + 1,
695+
SHALLOW, NOT_SHALLOW);
696+
send_shallow(result);
697+
free_commit_list(result);
698+
object_array_clear(&reachable_shallows);
686699
} else {
687700
struct commit_list *result;
688701

@@ -779,6 +792,8 @@ static void receive_needs(void)
779792

780793
features = arg + 40;
781794

795+
if (parse_feature_request(features, "deepen-relative"))
796+
deepen_relative = 1;
782797
if (parse_feature_request(features, "multi_ack_detailed"))
783798
multi_ack = 2;
784799
else if (parse_feature_request(features, "multi_ack"))
@@ -828,7 +843,7 @@ static void receive_needs(void)
828843
if (depth > 0 && deepen_rev_list)
829844
die("git upload-pack: deepen and deepen-since (or deepen-not) cannot be used together");
830845
if (depth > 0)
831-
deepen(depth, &shallows);
846+
deepen(depth, deepen_relative, &shallows);
832847
else if (deepen_rev_list) {
833848
struct argv_array av = ARGV_ARRAY_INIT;
834849
int i;
@@ -899,8 +914,8 @@ static int send_ref(const char *refname, const struct object_id *oid,
899914
int flag, void *cb_data)
900915
{
901916
static const char *capabilities = "multi_ack thin-pack side-band"
902-
" side-band-64k ofs-delta shallow deepen-since deepen-not no-progress"
903-
" include-tag multi_ack_detailed";
917+
" side-band-64k ofs-delta shallow deepen-since deepen-not"
918+
" deepen-relative no-progress include-tag multi_ack_detailed";
904919
const char *refname_nons = strip_namespace(refname);
905920
struct object_id peeled;
906921

0 commit comments

Comments
 (0)