mirror of
https://github.com/git/git
synced 2024-08-26 19:19:18 +00:00
![Junio C Hamano](/assets/img/avatar_default.png)
Fetching from a shallow-cloned repository used to be forbidden, primarily because the codepaths involved were not carefully vetted and we did not bother supporting such usage. This attempts to allow object transfer out of a shallow-cloned repository in a controlled way (i.e. the receiver become a shallow repository with truncated history). * nd/shallow-clone: (31 commits) t5537: fix incorrect expectation in test case 10 shallow: remove unused code send-pack.c: mark a file-local function static git-clone.txt: remove shallow clone limitations prune: clean .git/shallow after pruning objects clone: use git protocol for cloning shallow repo locally send-pack: support pushing from a shallow clone via http receive-pack: support pushing to a shallow clone via http smart-http: support shallow fetch/clone remote-curl: pass ref SHA-1 to fetch-pack as well send-pack: support pushing to a shallow clone receive-pack: allow pushes that update .git/shallow connected.c: add new variant that runs with --shallow-file add GIT_SHALLOW_FILE to propagate --shallow-file to subprocesses receive/send-pack: support pushing from a shallow clone receive-pack: reorder some code in unpack() fetch: add --update-shallow to accept refs that update .git/shallow upload-pack: make sure deepening preserves shallow roots fetch: support fetching from a shallow repository clone: support remote shallow repository ...
219 lines
5.1 KiB
C
219 lines
5.1 KiB
C
#include "builtin.h"
|
|
#include "pkt-line.h"
|
|
#include "fetch-pack.h"
|
|
#include "remote.h"
|
|
#include "connect.h"
|
|
#include "sha1-array.h"
|
|
|
|
static const char fetch_pack_usage[] =
|
|
"git fetch-pack [--all] [--stdin] [--quiet|-q] [--keep|-k] [--thin] "
|
|
"[--include-tag] [--upload-pack=<git-upload-pack>] [--depth=<n>] "
|
|
"[--no-progress] [--diag-url] [-v] [<host>:]<directory> [<refs>...]";
|
|
|
|
static void add_sought_entry_mem(struct ref ***sought, int *nr, int *alloc,
|
|
const char *name, int namelen)
|
|
{
|
|
struct ref *ref = xcalloc(1, sizeof(*ref) + namelen + 1);
|
|
unsigned char sha1[20];
|
|
|
|
if (namelen > 41 && name[40] == ' ' && !get_sha1_hex(name, sha1)) {
|
|
hashcpy(ref->old_sha1, sha1);
|
|
name += 41;
|
|
namelen -= 41;
|
|
}
|
|
|
|
memcpy(ref->name, name, namelen);
|
|
ref->name[namelen] = '\0';
|
|
(*nr)++;
|
|
ALLOC_GROW(*sought, *nr, *alloc);
|
|
(*sought)[*nr - 1] = ref;
|
|
}
|
|
|
|
static void add_sought_entry(struct ref ***sought, int *nr, int *alloc,
|
|
const char *string)
|
|
{
|
|
add_sought_entry_mem(sought, nr, alloc, string, strlen(string));
|
|
}
|
|
|
|
int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
|
|
{
|
|
int i, ret;
|
|
struct ref *ref = NULL;
|
|
const char *dest = NULL;
|
|
struct ref **sought = NULL;
|
|
int nr_sought = 0, alloc_sought = 0;
|
|
int fd[2];
|
|
char *pack_lockfile = NULL;
|
|
char **pack_lockfile_ptr = NULL;
|
|
struct child_process *conn;
|
|
struct fetch_pack_args args;
|
|
struct sha1_array shallow = SHA1_ARRAY_INIT;
|
|
|
|
packet_trace_identity("fetch-pack");
|
|
|
|
memset(&args, 0, sizeof(args));
|
|
args.uploadpack = "git-upload-pack";
|
|
|
|
for (i = 1; i < argc && *argv[i] == '-'; i++) {
|
|
const char *arg = argv[i];
|
|
|
|
if (starts_with(arg, "--upload-pack=")) {
|
|
args.uploadpack = arg + 14;
|
|
continue;
|
|
}
|
|
if (starts_with(arg, "--exec=")) {
|
|
args.uploadpack = arg + 7;
|
|
continue;
|
|
}
|
|
if (!strcmp("--quiet", arg) || !strcmp("-q", arg)) {
|
|
args.quiet = 1;
|
|
continue;
|
|
}
|
|
if (!strcmp("--keep", arg) || !strcmp("-k", arg)) {
|
|
args.lock_pack = args.keep_pack;
|
|
args.keep_pack = 1;
|
|
continue;
|
|
}
|
|
if (!strcmp("--thin", arg)) {
|
|
args.use_thin_pack = 1;
|
|
continue;
|
|
}
|
|
if (!strcmp("--include-tag", arg)) {
|
|
args.include_tag = 1;
|
|
continue;
|
|
}
|
|
if (!strcmp("--all", arg)) {
|
|
args.fetch_all = 1;
|
|
continue;
|
|
}
|
|
if (!strcmp("--stdin", arg)) {
|
|
args.stdin_refs = 1;
|
|
continue;
|
|
}
|
|
if (!strcmp("--diag-url", arg)) {
|
|
args.diag_url = 1;
|
|
continue;
|
|
}
|
|
if (!strcmp("-v", arg)) {
|
|
args.verbose = 1;
|
|
continue;
|
|
}
|
|
if (starts_with(arg, "--depth=")) {
|
|
args.depth = strtol(arg + 8, NULL, 0);
|
|
continue;
|
|
}
|
|
if (!strcmp("--no-progress", arg)) {
|
|
args.no_progress = 1;
|
|
continue;
|
|
}
|
|
if (!strcmp("--stateless-rpc", arg)) {
|
|
args.stateless_rpc = 1;
|
|
continue;
|
|
}
|
|
if (!strcmp("--lock-pack", arg)) {
|
|
args.lock_pack = 1;
|
|
pack_lockfile_ptr = &pack_lockfile;
|
|
continue;
|
|
}
|
|
if (!strcmp("--check-self-contained-and-connected", arg)) {
|
|
args.check_self_contained_and_connected = 1;
|
|
continue;
|
|
}
|
|
if (!strcmp("--cloning", arg)) {
|
|
args.cloning = 1;
|
|
continue;
|
|
}
|
|
if (!strcmp("--update-shallow", arg)) {
|
|
args.update_shallow = 1;
|
|
continue;
|
|
}
|
|
usage(fetch_pack_usage);
|
|
}
|
|
|
|
if (i < argc)
|
|
dest = argv[i++];
|
|
else
|
|
usage(fetch_pack_usage);
|
|
|
|
/*
|
|
* Copy refs from cmdline to growable list, then append any
|
|
* refs from the standard input:
|
|
*/
|
|
for (; i < argc; i++)
|
|
add_sought_entry(&sought, &nr_sought, &alloc_sought, argv[i]);
|
|
if (args.stdin_refs) {
|
|
if (args.stateless_rpc) {
|
|
/* in stateless RPC mode we use pkt-line to read
|
|
* from stdin, until we get a flush packet
|
|
*/
|
|
for (;;) {
|
|
char *line = packet_read_line(0, NULL);
|
|
if (!line)
|
|
break;
|
|
add_sought_entry(&sought, &nr_sought, &alloc_sought, line);
|
|
}
|
|
}
|
|
else {
|
|
/* read from stdin one ref per line, until EOF */
|
|
struct strbuf line = STRBUF_INIT;
|
|
while (strbuf_getline(&line, stdin, '\n') != EOF)
|
|
add_sought_entry(&sought, &nr_sought, &alloc_sought, line.buf);
|
|
strbuf_release(&line);
|
|
}
|
|
}
|
|
|
|
if (args.stateless_rpc) {
|
|
conn = NULL;
|
|
fd[0] = 0;
|
|
fd[1] = 1;
|
|
} else {
|
|
int flags = args.verbose ? CONNECT_VERBOSE : 0;
|
|
if (args.diag_url)
|
|
flags |= CONNECT_DIAG_URL;
|
|
conn = git_connect(fd, dest, args.uploadpack,
|
|
flags);
|
|
if (!conn)
|
|
return args.diag_url ? 0 : 1;
|
|
}
|
|
get_remote_heads(fd[0], NULL, 0, &ref, 0, NULL, &shallow);
|
|
|
|
ref = fetch_pack(&args, fd, conn, ref, dest, sought, nr_sought,
|
|
&shallow, pack_lockfile_ptr);
|
|
if (pack_lockfile) {
|
|
printf("lock %s\n", pack_lockfile);
|
|
fflush(stdout);
|
|
}
|
|
if (args.check_self_contained_and_connected &&
|
|
args.self_contained_and_connected) {
|
|
printf("connectivity-ok\n");
|
|
fflush(stdout);
|
|
}
|
|
close(fd[0]);
|
|
close(fd[1]);
|
|
if (finish_connect(conn))
|
|
return 1;
|
|
|
|
ret = !ref;
|
|
|
|
/*
|
|
* If the heads to pull were given, we should have consumed
|
|
* all of them by matching the remote. Otherwise, 'git fetch
|
|
* remote no-such-ref' would silently succeed without issuing
|
|
* an error.
|
|
*/
|
|
for (i = 0; i < nr_sought; i++) {
|
|
if (!sought[i] || sought[i]->matched)
|
|
continue;
|
|
error("no such remote ref %s", sought[i]->name);
|
|
ret = 1;
|
|
}
|
|
|
|
while (ref) {
|
|
printf("%s %s\n",
|
|
sha1_to_hex(ref->old_sha1), ref->name);
|
|
ref = ref->next;
|
|
}
|
|
|
|
return ret;
|
|
}
|