Skip to content

Commit beea415

Browse files
pcloudsgitster
authored andcommitted
clone: support remote shallow repository
Cloning from a shallow repository does not follow the "8 steps for new .git/shallow" because if it does we need to get through step 6 for all refs. That means commit walking down to the bottom. Instead the rule to create .git/shallow is simpler and, more importantly, cheap: if a shallow commit is found in the pack, it's probably used (i.e. reachable from some refs), so we add it. Others are dropped. One may notice this method seems flawed by the word "probably". A shallow commit may not be reachable from any refs at all if it's attached to an object island (a group of objects that are not reachable by any refs). If that object island is not complete, a new fetch request may send more objects to connect it to some ref. At that time, because we incorrectly installed the shallow commit in this island, the user will not see anything after that commit (fsck is still ok). This is not desired. Given that object islands are rare (C Git never sends such islands for security reasons) and do not really harm the repository integrity, a tradeoff is made to surprise the user occasionally but work faster everyday. A new option --strict could be added later that follows exactly the 8 steps. "git prune" can also learn to remove dangling objects _and_ the shallow commits that are attached to them from .git/shallow. Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent f6486f0 commit beea415

6 files changed

Lines changed: 71 additions & 7 deletions

File tree

builtin/clone.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -889,6 +889,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
889889

890890
remote = remote_get(option_origin);
891891
transport = transport_get(remote, remote->url[0]);
892+
transport->cloning = 1;
892893

893894
if (!transport->get_refs_list || (!is_local && !transport->fetch))
894895
die(_("Don't know how to clone %s"), transport->url);

builtin/fetch-pack.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
153153
get_remote_heads(fd[0], NULL, 0, &ref, 0, NULL, NULL);
154154

155155
ref = fetch_pack(&args, fd, conn, ref, dest,
156-
sought, nr_sought, pack_lockfile_ptr);
156+
sought, nr_sought, NULL, pack_lockfile_ptr);
157157
if (pack_lockfile) {
158158
printf("lock %s\n", pack_lockfile);
159159
fflush(stdout);

fetch-pack.c

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "transport.h"
1414
#include "version.h"
1515
#include "prio-queue.h"
16+
#include "sha1-array.h"
1617

1718
static int transfer_unpack_limit = -1;
1819
static int fetch_unpack_limit = -1;
@@ -774,6 +775,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
774775
int fd[2],
775776
const struct ref *orig_ref,
776777
struct ref **sought, int nr_sought,
778+
struct shallow_info *si,
777779
char **pack_lockfile)
778780
{
779781
struct ref *ref = copy_ref_list(orig_ref);
@@ -852,6 +854,8 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
852854
if (args->depth > 0)
853855
setup_alternate_shallow(&shallow_lock, &alternate_shallow_file,
854856
NULL);
857+
else if (args->cloning && si->shallow && si->shallow->nr)
858+
alternate_shallow_file = setup_temporary_shallow(si->shallow);
855859
else
856860
alternate_shallow_file = NULL;
857861
if (get_pack(args, fd, pack_lockfile))
@@ -925,8 +929,11 @@ static int remove_duplicates_in_refs(struct ref **ref, int nr)
925929
return dst;
926930
}
927931

928-
static void update_shallow(struct fetch_pack_args *args)
932+
static void update_shallow(struct fetch_pack_args *args,
933+
struct shallow_info *si)
929934
{
935+
int i;
936+
930937
if (args->depth > 0 && alternate_shallow_file) {
931938
if (*alternate_shallow_file == '\0') { /* --unshallow */
932939
unlink_or_warn(git_path("shallow"));
@@ -935,16 +942,54 @@ static void update_shallow(struct fetch_pack_args *args)
935942
commit_lock_file(&shallow_lock);
936943
return;
937944
}
945+
946+
if (!si->shallow || !si->shallow->nr)
947+
return;
948+
949+
if (alternate_shallow_file) {
950+
/*
951+
* The temporary shallow file is only useful for
952+
* index-pack and unpack-objects because it may
953+
* contain more roots than we want. Delete it.
954+
*/
955+
if (*alternate_shallow_file)
956+
unlink(alternate_shallow_file);
957+
free((char *)alternate_shallow_file);
958+
}
959+
960+
if (args->cloning) {
961+
/*
962+
* remote is shallow, but this is a clone, there are
963+
* no objects in repo to worry about. Accept any
964+
* shallow points that exist in the pack (iow in repo
965+
* after get_pack() and reprepare_packed_git())
966+
*/
967+
struct sha1_array extra = SHA1_ARRAY_INIT;
968+
unsigned char (*sha1)[20] = si->shallow->sha1;
969+
for (i = 0; i < si->shallow->nr; i++)
970+
if (has_sha1_file(sha1[i]))
971+
sha1_array_append(&extra, sha1[i]);
972+
if (extra.nr) {
973+
setup_alternate_shallow(&shallow_lock,
974+
&alternate_shallow_file,
975+
&extra);
976+
commit_lock_file(&shallow_lock);
977+
}
978+
sha1_array_clear(&extra);
979+
return;
980+
}
938981
}
939982

940983
struct ref *fetch_pack(struct fetch_pack_args *args,
941984
int fd[], struct child_process *conn,
942985
const struct ref *ref,
943986
const char *dest,
944987
struct ref **sought, int nr_sought,
988+
struct sha1_array *shallow,
945989
char **pack_lockfile)
946990
{
947991
struct ref *ref_cpy;
992+
struct shallow_info si;
948993

949994
fetch_pack_setup();
950995
if (nr_sought)
@@ -954,8 +999,11 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
954999
packet_flush(fd[1]);
9551000
die("no matching remote head");
9561001
}
957-
ref_cpy = do_fetch_pack(args, fd, ref, sought, nr_sought, pack_lockfile);
958-
update_shallow(args);
1002+
prepare_shallow_info(&si, shallow);
1003+
ref_cpy = do_fetch_pack(args, fd, ref, sought, nr_sought,
1004+
&si, pack_lockfile);
9591005
reprepare_packed_git();
1006+
update_shallow(args, &si);
1007+
clear_shallow_info(&si);
9601008
return ref_cpy;
9611009
}

fetch-pack.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
#include "string-list.h"
55
#include "run-command.h"
66

7+
struct sha1_array;
8+
79
struct fetch_pack_args {
810
const char *uploadpack;
911
int unpacklimit;
@@ -20,6 +22,7 @@ struct fetch_pack_args {
2022
unsigned stateless_rpc:1;
2123
unsigned check_self_contained_and_connected:1;
2224
unsigned self_contained_and_connected:1;
25+
unsigned cloning:1;
2326
};
2427

2528
/*
@@ -33,6 +36,7 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
3336
const char *dest,
3437
struct ref **sought,
3538
int nr_sought,
39+
struct sha1_array *shallow,
3640
char **pack_lockfile);
3741

3842
#endif

transport.c

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,7 @@ struct git_transport_data {
456456
int fd[2];
457457
unsigned got_remote_heads : 1;
458458
struct sha1_array extra_have;
459+
struct sha1_array shallow;
459460
};
460461

461462
static int set_git_option(struct git_transport_options *opts,
@@ -512,7 +513,9 @@ static struct ref *get_refs_via_connect(struct transport *transport, int for_pus
512513

513514
connect_setup(transport, for_push, 0);
514515
get_remote_heads(data->fd[0], NULL, 0, &refs,
515-
for_push ? REF_NORMAL : 0, &data->extra_have, NULL);
516+
for_push ? REF_NORMAL : 0,
517+
&data->extra_have,
518+
transport->cloning ? &data->shallow : NULL);
516519
data->got_remote_heads = 1;
517520

518521
return refs;
@@ -539,17 +542,19 @@ static int fetch_refs_via_pack(struct transport *transport,
539542
args.depth = data->options.depth;
540543
args.check_self_contained_and_connected =
541544
data->options.check_self_contained_and_connected;
545+
args.cloning = transport->cloning;
542546

543547
if (!data->got_remote_heads) {
544548
connect_setup(transport, 0, 0);
545549
get_remote_heads(data->fd[0], NULL, 0, &refs_tmp, 0,
546-
NULL, NULL);
550+
NULL,
551+
transport->cloning ? &data->shallow : NULL);
547552
data->got_remote_heads = 1;
548553
}
549554

550555
refs = fetch_pack(&args, data->fd, data->conn,
551556
refs_tmp ? refs_tmp : transport->remote_refs,
552-
dest, to_fetch, nr_heads,
557+
dest, to_fetch, nr_heads, &data->shallow,
553558
&transport->pack_lockfile);
554559
close(data->fd[0]);
555560
close(data->fd[1]);

transport.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@ struct transport {
3535
*/
3636
unsigned cannot_reuse : 1;
3737

38+
/*
39+
* A hint from caller that it will be performing a clone, not
40+
* normal fetch. IOW the repository is guaranteed empty.
41+
*/
42+
unsigned cloning : 1;
43+
3844
/**
3945
* Returns 0 if successful, positive if the option is not
4046
* recognized or is inapplicable, and negative if the option

0 commit comments

Comments
 (0)