-
Notifications
You must be signed in to change notification settings - Fork 25.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'ds/bundle-uri-clone' into seen
Implement "git clone --bundle-uri". source: <pull.1300.git.1658781277.gitgitgadget@gmail.com> * ds/bundle-uri-clone: clone: --bundle-uri cannot be combined with --depth bundle-uri: add support for http(s):// and file:// clone: add --bundle-uri option bundle-uri: create basic file-copy logic remote-curl: add 'get' capability
- Loading branch information
Showing
10 changed files
with
375 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
#include "cache.h" | ||
#include "bundle-uri.h" | ||
#include "bundle.h" | ||
#include "object-store.h" | ||
#include "refs.h" | ||
#include "run-command.h" | ||
|
||
static int find_temp_filename(struct strbuf *name) | ||
{ | ||
int fd; | ||
/* | ||
* Find a temporary filename that is available. This is briefly | ||
* racy, but unlikely to collide. | ||
*/ | ||
fd = odb_mkstemp(name, "bundles/tmp_uri_XXXXXX"); | ||
if (fd < 0) { | ||
warning(_("failed to create temporary file")); | ||
return -1; | ||
} | ||
|
||
close(fd); | ||
unlink(name->buf); | ||
return 0; | ||
} | ||
|
||
static int download_https_uri_to_file(const char *file, const char *uri) | ||
{ | ||
int result = 0; | ||
struct child_process cp = CHILD_PROCESS_INIT; | ||
FILE *child_in = NULL, *child_out = NULL; | ||
struct strbuf line = STRBUF_INIT; | ||
int found_get = 0; | ||
|
||
strvec_pushl(&cp.args, "git-remote-https", "origin", uri, NULL); | ||
cp.in = -1; | ||
cp.out = -1; | ||
|
||
if (start_command(&cp)) | ||
return 1; | ||
|
||
child_in = fdopen(cp.in, "w"); | ||
if (!child_in) { | ||
result = 1; | ||
goto cleanup; | ||
} | ||
|
||
child_out = fdopen(cp.out, "r"); | ||
if (!child_out) { | ||
result = 1; | ||
goto cleanup; | ||
} | ||
|
||
fprintf(child_in, "capabilities\n"); | ||
fflush(child_in); | ||
|
||
while (!strbuf_getline(&line, child_out)) { | ||
if (!line.len) | ||
break; | ||
if (!strcmp(line.buf, "get")) | ||
found_get = 1; | ||
} | ||
strbuf_release(&line); | ||
|
||
if (!found_get) { | ||
result = error(_("insufficient capabilities")); | ||
goto cleanup; | ||
} | ||
|
||
fprintf(child_in, "get %s %s\n\n", uri, file); | ||
|
||
cleanup: | ||
if (child_in) | ||
fclose(child_in); | ||
if (finish_command(&cp)) | ||
return 1; | ||
if (child_out) | ||
fclose(child_out); | ||
return result; | ||
} | ||
|
||
static int copy_uri_to_file(const char *filename, const char *uri) | ||
{ | ||
const char *out; | ||
|
||
if (skip_prefix(uri, "https:", &out) || | ||
skip_prefix(uri, "http:", &out)) | ||
return download_https_uri_to_file(filename, uri); | ||
|
||
if (!skip_prefix(uri, "file://", &out)) | ||
out = uri; | ||
|
||
/* Copy as a file */ | ||
return copy_file(filename, out, 0); | ||
} | ||
|
||
static int unbundle_from_file(struct repository *r, const char *file) | ||
{ | ||
int result = 0; | ||
int bundle_fd; | ||
struct bundle_header header = BUNDLE_HEADER_INIT; | ||
struct string_list_item *refname; | ||
struct strbuf bundle_ref = STRBUF_INIT; | ||
size_t bundle_prefix_len; | ||
|
||
if ((bundle_fd = read_bundle_header(file, &header)) < 0) | ||
return 1; | ||
|
||
if ((result = unbundle(r, &header, bundle_fd, NULL))) | ||
return 1; | ||
|
||
/* | ||
* Convert all refs/heads/ from the bundle into refs/bundles/ | ||
* in the local repository. | ||
*/ | ||
strbuf_addstr(&bundle_ref, "refs/bundles/"); | ||
bundle_prefix_len = bundle_ref.len; | ||
|
||
for_each_string_list_item(refname, &header.references) { | ||
struct object_id *oid = refname->util; | ||
struct object_id old_oid; | ||
const char *branch_name; | ||
int has_old; | ||
|
||
if (!skip_prefix(refname->string, "refs/heads/", &branch_name)) | ||
continue; | ||
|
||
strbuf_setlen(&bundle_ref, bundle_prefix_len); | ||
strbuf_addstr(&bundle_ref, branch_name); | ||
|
||
has_old = !read_ref(bundle_ref.buf, &old_oid); | ||
update_ref("fetched bundle", bundle_ref.buf, oid, | ||
has_old ? &old_oid : NULL, | ||
REF_SKIP_OID_VERIFICATION, | ||
UPDATE_REFS_MSG_ON_ERR); | ||
} | ||
|
||
bundle_header_release(&header); | ||
return result; | ||
} | ||
|
||
int fetch_bundle_uri(struct repository *r, const char *uri) | ||
{ | ||
int result = 0; | ||
struct strbuf filename = STRBUF_INIT; | ||
|
||
if ((result = find_temp_filename(&filename))) | ||
goto cleanup; | ||
|
||
if ((result = copy_uri_to_file(filename.buf, uri))) { | ||
warning(_("failed to download bundle from URI '%s'"), uri); | ||
goto cleanup; | ||
} | ||
|
||
if ((result = !is_bundle(filename.buf, 0))) { | ||
warning(_("file at URI '%s' is not a bundle"), uri); | ||
goto cleanup; | ||
} | ||
|
||
if ((result = unbundle_from_file(r, filename.buf))) { | ||
warning(_("failed to unbundle bundle from URI '%s'"), uri); | ||
goto cleanup; | ||
} | ||
|
||
cleanup: | ||
unlink(filename.buf); | ||
strbuf_release(&filename); | ||
return result; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
#ifndef BUNDLE_URI_H | ||
#define BUNDLE_URI_H | ||
|
||
struct repository; | ||
|
||
/** | ||
* Fetch data from the given 'uri' and unbundle the bundle data found | ||
* based on that information. | ||
* | ||
* Returns non-zero if no bundle information is found at the given 'uri'. | ||
*/ | ||
int fetch_bundle_uri(struct repository *r, const char *uri); | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
#!/bin/sh | ||
|
||
test_description='test downloading a file by URL' | ||
|
||
. ./test-lib.sh | ||
|
||
. "$TEST_DIRECTORY"/lib-httpd.sh | ||
start_httpd | ||
|
||
test_expect_success 'get by URL: 404' ' | ||
url="$HTTPD_URL/none.txt" && | ||
cat >input <<-EOF && | ||
capabilities | ||
get $url file1 | ||
EOF | ||
test_must_fail git remote-http $url <input 2>err && | ||
test_path_is_missing file1 && | ||
grep "failed to download file at URL" err && | ||
rm file1.temp | ||
' | ||
|
||
test_expect_success 'get by URL: 200' ' | ||
echo data >"$HTTPD_DOCUMENT_ROOT_PATH/exists.txt" && | ||
url="$HTTPD_URL/exists.txt" && | ||
cat >input <<-EOF && | ||
capabilities | ||
get $url file2 | ||
EOF | ||
GIT_TRACE2_PERF=1 git remote-http $url <input && | ||
test_cmp "$HTTPD_DOCUMENT_ROOT_PATH/exists.txt" file2 | ||
' | ||
|
||
test_done |
Oops, something went wrong.