Merge branch 'jc/fetch-raw-sha1'
Allows requests to fetch objects at any tip of refs (including hidden ones). It seems that there may be use cases even outside Gerrit (e.g. $gmane/215701). * jc/fetch-raw-sha1: fetch: fetch objects by their exact SHA-1 object names upload-pack: optionally allow fetching from the tips of hidden refs fetch: use struct ref to represent refs to be fetched parse_fetch_refspec(): clarify the codeflow a bit
This commit is contained in:
commit
e4e1c54990
@ -2123,7 +2123,13 @@ uploadpack.hiderefs::
|
|||||||
are under the hierarchies listed on the value of this
|
are under the hierarchies listed on the value of this
|
||||||
variable is excluded, and is hidden from `git ls-remote`,
|
variable is excluded, and is hidden from `git ls-remote`,
|
||||||
`git fetch`, etc. An attempt to fetch a hidden ref by `git
|
`git fetch`, etc. An attempt to fetch a hidden ref by `git
|
||||||
fetch` will fail.
|
fetch` will fail. See also `uploadpack.allowtipsha1inwant`.
|
||||||
|
|
||||||
|
uploadpack.allowtipsha1inwant::
|
||||||
|
When `uploadpack.hiderefs` is in effect, allow `upload-pack`
|
||||||
|
to accept a fetch request that asks for an object at the tip
|
||||||
|
of a hidden ref (by default, such a request is rejected).
|
||||||
|
see also `uploadpack.hiderefs`.
|
||||||
|
|
||||||
url.<base>.insteadOf::
|
url.<base>.insteadOf::
|
||||||
Any URL that starts with this value will be rewritten to
|
Any URL that starts with this value will be rewritten to
|
||||||
|
@ -7,12 +7,31 @@ static const char fetch_pack_usage[] =
|
|||||||
"[--include-tag] [--upload-pack=<git-upload-pack>] [--depth=<n>] "
|
"[--include-tag] [--upload-pack=<git-upload-pack>] [--depth=<n>] "
|
||||||
"[--no-progress] [-v] [<host>:]<directory> [<refs>...]";
|
"[--no-progress] [-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);
|
||||||
|
|
||||||
|
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 cmd_fetch_pack(int argc, const char **argv, const char *prefix)
|
||||||
{
|
{
|
||||||
int i, ret;
|
int i, ret;
|
||||||
struct ref *ref = NULL;
|
struct ref *ref = NULL;
|
||||||
const char *dest = NULL;
|
const char *dest = NULL;
|
||||||
struct string_list sought = STRING_LIST_INIT_DUP;
|
struct ref **sought = NULL;
|
||||||
|
int nr_sought = 0, alloc_sought = 0;
|
||||||
int fd[2];
|
int fd[2];
|
||||||
char *pack_lockfile = NULL;
|
char *pack_lockfile = NULL;
|
||||||
char **pack_lockfile_ptr = NULL;
|
char **pack_lockfile_ptr = NULL;
|
||||||
@ -94,7 +113,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
|
|||||||
* refs from the standard input:
|
* refs from the standard input:
|
||||||
*/
|
*/
|
||||||
for (; i < argc; i++)
|
for (; i < argc; i++)
|
||||||
string_list_append(&sought, xstrdup(argv[i]));
|
add_sought_entry(&sought, &nr_sought, &alloc_sought, argv[i]);
|
||||||
if (args.stdin_refs) {
|
if (args.stdin_refs) {
|
||||||
if (args.stateless_rpc) {
|
if (args.stateless_rpc) {
|
||||||
/* in stateless RPC mode we use pkt-line to read
|
/* in stateless RPC mode we use pkt-line to read
|
||||||
@ -107,14 +126,14 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
|
|||||||
break;
|
break;
|
||||||
if (line[n-1] == '\n')
|
if (line[n-1] == '\n')
|
||||||
n--;
|
n--;
|
||||||
string_list_append(&sought, xmemdupz(line, n));
|
add_sought_entry_mem(&sought, &nr_sought, &alloc_sought, line, n);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
/* read from stdin one ref per line, until EOF */
|
/* read from stdin one ref per line, until EOF */
|
||||||
struct strbuf line = STRBUF_INIT;
|
struct strbuf line = STRBUF_INIT;
|
||||||
while (strbuf_getline(&line, stdin, '\n') != EOF)
|
while (strbuf_getline(&line, stdin, '\n') != EOF)
|
||||||
string_list_append(&sought, strbuf_detach(&line, NULL));
|
add_sought_entry(&sought, &nr_sought, &alloc_sought, line.buf);
|
||||||
strbuf_release(&line);
|
strbuf_release(&line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -131,7 +150,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
|
|||||||
get_remote_heads(fd[0], &ref, 0, NULL);
|
get_remote_heads(fd[0], &ref, 0, NULL);
|
||||||
|
|
||||||
ref = fetch_pack(&args, fd, conn, ref, dest,
|
ref = fetch_pack(&args, fd, conn, ref, dest,
|
||||||
&sought, pack_lockfile_ptr);
|
sought, nr_sought, pack_lockfile_ptr);
|
||||||
if (pack_lockfile) {
|
if (pack_lockfile) {
|
||||||
printf("lock %s\n", pack_lockfile);
|
printf("lock %s\n", pack_lockfile);
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
@ -141,7 +160,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
|
|||||||
if (finish_connect(conn))
|
if (finish_connect(conn))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
ret = !ref || sought.nr;
|
ret = !ref;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the heads to pull were given, we should have consumed
|
* If the heads to pull were given, we should have consumed
|
||||||
@ -149,8 +168,13 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
|
|||||||
* remote no-such-ref' would silently succeed without issuing
|
* remote no-such-ref' would silently succeed without issuing
|
||||||
* an error.
|
* an error.
|
||||||
*/
|
*/
|
||||||
for (i = 0; i < sought.nr; i++)
|
for (i = 0; i < nr_sought; i++) {
|
||||||
error("no such remote ref %s", sought.items[i].string);
|
if (!sought[i] || sought[i]->matched)
|
||||||
|
continue;
|
||||||
|
error("no such remote ref %s", sought[i]->name);
|
||||||
|
ret = 1;
|
||||||
|
}
|
||||||
|
|
||||||
while (ref) {
|
while (ref) {
|
||||||
printf("%s %s\n",
|
printf("%s %s\n",
|
||||||
sha1_to_hex(ref->old_sha1), ref->name);
|
sha1_to_hex(ref->old_sha1), ref->name);
|
||||||
|
3
cache.h
3
cache.h
@ -1017,7 +1017,8 @@ struct ref {
|
|||||||
force:1,
|
force:1,
|
||||||
forced_update:1,
|
forced_update:1,
|
||||||
merge:1,
|
merge:1,
|
||||||
deletion:1;
|
deletion:1,
|
||||||
|
matched:1;
|
||||||
enum {
|
enum {
|
||||||
REF_STATUS_NONE = 0,
|
REF_STATUS_NONE = 0,
|
||||||
REF_STATUS_OK,
|
REF_STATUS_OK,
|
||||||
|
101
fetch-pack.c
101
fetch-pack.c
@ -36,7 +36,7 @@ static int marked;
|
|||||||
#define MAX_IN_VAIN 256
|
#define MAX_IN_VAIN 256
|
||||||
|
|
||||||
static struct commit_list *rev_list;
|
static struct commit_list *rev_list;
|
||||||
static int non_common_revs, multi_ack, use_sideband;
|
static int non_common_revs, multi_ack, use_sideband, allow_tip_sha1_in_want;
|
||||||
|
|
||||||
static void rev_list_push(struct commit *commit, int mark)
|
static void rev_list_push(struct commit *commit, int mark)
|
||||||
{
|
{
|
||||||
@ -520,47 +520,37 @@ static void mark_recent_complete_commits(struct fetch_pack_args *args,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int non_matching_ref(struct string_list_item *item, void *unused)
|
|
||||||
{
|
|
||||||
if (item->util) {
|
|
||||||
item->util = NULL;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void filter_refs(struct fetch_pack_args *args,
|
static void filter_refs(struct fetch_pack_args *args,
|
||||||
struct ref **refs, struct string_list *sought)
|
struct ref **refs,
|
||||||
|
struct ref **sought, int nr_sought)
|
||||||
{
|
{
|
||||||
struct ref *newlist = NULL;
|
struct ref *newlist = NULL;
|
||||||
struct ref **newtail = &newlist;
|
struct ref **newtail = &newlist;
|
||||||
struct ref *ref, *next;
|
struct ref *ref, *next;
|
||||||
int sought_pos;
|
int i;
|
||||||
|
|
||||||
sought_pos = 0;
|
i = 0;
|
||||||
for (ref = *refs; ref; ref = next) {
|
for (ref = *refs; ref; ref = next) {
|
||||||
int keep = 0;
|
int keep = 0;
|
||||||
next = ref->next;
|
next = ref->next;
|
||||||
|
|
||||||
if (!memcmp(ref->name, "refs/", 5) &&
|
if (!memcmp(ref->name, "refs/", 5) &&
|
||||||
check_refname_format(ref->name + 5, 0))
|
check_refname_format(ref->name + 5, 0))
|
||||||
; /* trash */
|
; /* trash */
|
||||||
else {
|
else {
|
||||||
while (sought_pos < sought->nr) {
|
while (i < nr_sought) {
|
||||||
int cmp = strcmp(ref->name, sought->items[sought_pos].string);
|
int cmp = strcmp(ref->name, sought[i]->name);
|
||||||
if (cmp < 0)
|
if (cmp < 0)
|
||||||
break; /* definitely do not have it */
|
break; /* definitely do not have it */
|
||||||
else if (cmp == 0) {
|
else if (cmp == 0) {
|
||||||
keep = 1; /* definitely have it */
|
keep = 1; /* definitely have it */
|
||||||
sought->items[sought_pos++].util = "matched";
|
sought[i]->matched = 1;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
else
|
i++;
|
||||||
sought_pos++; /* might have it; keep looking */
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! keep && args->fetch_all &&
|
if (!keep && args->fetch_all &&
|
||||||
(!args->depth || prefixcmp(ref->name, "refs/tags/")))
|
(!args->depth || prefixcmp(ref->name, "refs/tags/")))
|
||||||
keep = 1;
|
keep = 1;
|
||||||
|
|
||||||
@ -573,7 +563,21 @@ static void filter_refs(struct fetch_pack_args *args,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
filter_string_list(sought, 0, non_matching_ref, NULL);
|
/* Append unmatched requests to the list */
|
||||||
|
if (allow_tip_sha1_in_want) {
|
||||||
|
for (i = 0; i < nr_sought; i++) {
|
||||||
|
ref = sought[i];
|
||||||
|
if (ref->matched)
|
||||||
|
continue;
|
||||||
|
if (get_sha1_hex(ref->name, ref->old_sha1))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ref->matched = 1;
|
||||||
|
*newtail = ref;
|
||||||
|
ref->next = NULL;
|
||||||
|
newtail = &ref->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
*refs = newlist;
|
*refs = newlist;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -583,7 +587,8 @@ static void mark_alternate_complete(const struct ref *ref, void *unused)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int everything_local(struct fetch_pack_args *args,
|
static int everything_local(struct fetch_pack_args *args,
|
||||||
struct ref **refs, struct string_list *sought)
|
struct ref **refs,
|
||||||
|
struct ref **sought, int nr_sought)
|
||||||
{
|
{
|
||||||
struct ref *ref;
|
struct ref *ref;
|
||||||
int retval;
|
int retval;
|
||||||
@ -637,7 +642,7 @@ static int everything_local(struct fetch_pack_args *args,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
filter_refs(args, refs, sought);
|
filter_refs(args, refs, sought, nr_sought);
|
||||||
|
|
||||||
for (retval = 1, ref = *refs; ref ; ref = ref->next) {
|
for (retval = 1, ref = *refs; ref ; ref = ref->next) {
|
||||||
const unsigned char *remote = ref->old_sha1;
|
const unsigned char *remote = ref->old_sha1;
|
||||||
@ -767,10 +772,17 @@ static int get_pack(struct fetch_pack_args *args,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int cmp_ref_by_name(const void *a_, const void *b_)
|
||||||
|
{
|
||||||
|
const struct ref *a = *((const struct ref **)a_);
|
||||||
|
const struct ref *b = *((const struct ref **)b_);
|
||||||
|
return strcmp(a->name, b->name);
|
||||||
|
}
|
||||||
|
|
||||||
static struct ref *do_fetch_pack(struct fetch_pack_args *args,
|
static struct ref *do_fetch_pack(struct fetch_pack_args *args,
|
||||||
int fd[2],
|
int fd[2],
|
||||||
const struct ref *orig_ref,
|
const struct ref *orig_ref,
|
||||||
struct string_list *sought,
|
struct ref **sought, int nr_sought,
|
||||||
char **pack_lockfile)
|
char **pack_lockfile)
|
||||||
{
|
{
|
||||||
struct ref *ref = copy_ref_list(orig_ref);
|
struct ref *ref = copy_ref_list(orig_ref);
|
||||||
@ -779,6 +791,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
|
|||||||
int agent_len;
|
int agent_len;
|
||||||
|
|
||||||
sort_ref_list(&ref, ref_compare_name);
|
sort_ref_list(&ref, ref_compare_name);
|
||||||
|
qsort(sought, nr_sought, sizeof(*sought), cmp_ref_by_name);
|
||||||
|
|
||||||
if (is_repository_shallow() && !server_supports("shallow"))
|
if (is_repository_shallow() && !server_supports("shallow"))
|
||||||
die("Server does not support shallow clients");
|
die("Server does not support shallow clients");
|
||||||
@ -808,6 +821,11 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
|
|||||||
fprintf(stderr, "Server supports side-band\n");
|
fprintf(stderr, "Server supports side-band\n");
|
||||||
use_sideband = 1;
|
use_sideband = 1;
|
||||||
}
|
}
|
||||||
|
if (server_supports("allow-tip-sha1-in-want")) {
|
||||||
|
if (args->verbose)
|
||||||
|
fprintf(stderr, "Server supports allow-tip-sha1-in-want\n");
|
||||||
|
allow_tip_sha1_in_want = 1;
|
||||||
|
}
|
||||||
if (!server_supports("thin-pack"))
|
if (!server_supports("thin-pack"))
|
||||||
args->use_thin_pack = 0;
|
args->use_thin_pack = 0;
|
||||||
if (!server_supports("no-progress"))
|
if (!server_supports("no-progress"))
|
||||||
@ -827,7 +845,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
|
|||||||
agent_len, agent_feature);
|
agent_len, agent_feature);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (everything_local(args, &ref, sought)) {
|
if (everything_local(args, &ref, sought, nr_sought)) {
|
||||||
packet_flush(fd[1]);
|
packet_flush(fd[1]);
|
||||||
goto all_done;
|
goto all_done;
|
||||||
}
|
}
|
||||||
@ -890,11 +908,32 @@ static void fetch_pack_setup(void)
|
|||||||
did_setup = 1;
|
did_setup = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int remove_duplicates_in_refs(struct ref **ref, int nr)
|
||||||
|
{
|
||||||
|
struct string_list names = STRING_LIST_INIT_NODUP;
|
||||||
|
int src, dst;
|
||||||
|
|
||||||
|
for (src = dst = 0; src < nr; src++) {
|
||||||
|
struct string_list_item *item;
|
||||||
|
item = string_list_insert(&names, ref[src]->name);
|
||||||
|
if (item->util)
|
||||||
|
continue; /* already have it */
|
||||||
|
item->util = ref[src];
|
||||||
|
if (src != dst)
|
||||||
|
ref[dst] = ref[src];
|
||||||
|
dst++;
|
||||||
|
}
|
||||||
|
for (src = dst; src < nr; src++)
|
||||||
|
ref[src] = NULL;
|
||||||
|
string_list_clear(&names, 0);
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
struct ref *fetch_pack(struct fetch_pack_args *args,
|
struct ref *fetch_pack(struct fetch_pack_args *args,
|
||||||
int fd[], struct child_process *conn,
|
int fd[], struct child_process *conn,
|
||||||
const struct ref *ref,
|
const struct ref *ref,
|
||||||
const char *dest,
|
const char *dest,
|
||||||
struct string_list *sought,
|
struct ref **sought, int nr_sought,
|
||||||
char **pack_lockfile)
|
char **pack_lockfile)
|
||||||
{
|
{
|
||||||
struct stat st;
|
struct stat st;
|
||||||
@ -906,16 +945,14 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
|
|||||||
st.st_mtime = 0;
|
st.st_mtime = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sought->nr) {
|
if (nr_sought)
|
||||||
sort_string_list(sought);
|
nr_sought = remove_duplicates_in_refs(sought, nr_sought);
|
||||||
string_list_remove_duplicates(sought, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ref) {
|
if (!ref) {
|
||||||
packet_flush(fd[1]);
|
packet_flush(fd[1]);
|
||||||
die("no matching remote head");
|
die("no matching remote head");
|
||||||
}
|
}
|
||||||
ref_cpy = do_fetch_pack(args, fd, ref, sought, pack_lockfile);
|
ref_cpy = do_fetch_pack(args, fd, ref, sought, nr_sought, pack_lockfile);
|
||||||
|
|
||||||
if (args->depth > 0) {
|
if (args->depth > 0) {
|
||||||
static struct lock_file lock;
|
static struct lock_file lock;
|
||||||
|
11
fetch-pack.h
11
fetch-pack.h
@ -20,17 +20,16 @@ struct fetch_pack_args {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* sought contains the full names of remote references that should be
|
* sought represents remote references that should be updated from.
|
||||||
* updated from. On return, the names that were found on the remote
|
* On return, the names that were found on the remote will have been
|
||||||
* will have been removed from the list. The util members of the
|
* marked as such.
|
||||||
* string_list_items are used internally; they must be NULL on entry
|
|
||||||
* (and will be NULL on exit).
|
|
||||||
*/
|
*/
|
||||||
struct ref *fetch_pack(struct fetch_pack_args *args,
|
struct ref *fetch_pack(struct fetch_pack_args *args,
|
||||||
int fd[], struct child_process *conn,
|
int fd[], struct child_process *conn,
|
||||||
const struct ref *ref,
|
const struct ref *ref,
|
||||||
const char *dest,
|
const char *dest,
|
||||||
struct string_list *sought,
|
struct ref **sought,
|
||||||
|
int nr_sought,
|
||||||
char **pack_lockfile);
|
char **pack_lockfile);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
41
remote.c
41
remote.c
@ -15,6 +15,7 @@ static struct refspec s_tag_refspec = {
|
|||||||
0,
|
0,
|
||||||
1,
|
1,
|
||||||
0,
|
0,
|
||||||
|
0,
|
||||||
"refs/tags/*",
|
"refs/tags/*",
|
||||||
"refs/tags/*"
|
"refs/tags/*"
|
||||||
};
|
};
|
||||||
@ -538,7 +539,7 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Before going on, special case ":" (or "+:") as a refspec
|
* Before going on, special case ":" (or "+:") as a refspec
|
||||||
* for matching refs.
|
* for pushing matching refs.
|
||||||
*/
|
*/
|
||||||
if (!fetch && rhs == lhs && rhs[1] == '\0') {
|
if (!fetch && rhs == lhs && rhs[1] == '\0') {
|
||||||
rs[i].matching = 1;
|
rs[i].matching = 1;
|
||||||
@ -565,26 +566,25 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp
|
|||||||
flags = REFNAME_ALLOW_ONELEVEL | (is_glob ? REFNAME_REFSPEC_PATTERN : 0);
|
flags = REFNAME_ALLOW_ONELEVEL | (is_glob ? REFNAME_REFSPEC_PATTERN : 0);
|
||||||
|
|
||||||
if (fetch) {
|
if (fetch) {
|
||||||
/*
|
unsigned char unused[40];
|
||||||
* LHS
|
|
||||||
* - empty is allowed; it means HEAD.
|
/* LHS */
|
||||||
* - otherwise it must be a valid looking ref.
|
|
||||||
*/
|
|
||||||
if (!*rs[i].src)
|
if (!*rs[i].src)
|
||||||
; /* empty is ok */
|
; /* empty is ok; it means "HEAD" */
|
||||||
else if (check_refname_format(rs[i].src, flags))
|
else if (llen == 40 && !get_sha1_hex(rs[i].src, unused))
|
||||||
|
rs[i].exact_sha1 = 1; /* ok */
|
||||||
|
else if (!check_refname_format(rs[i].src, flags))
|
||||||
|
; /* valid looking ref is ok */
|
||||||
|
else
|
||||||
goto invalid;
|
goto invalid;
|
||||||
/*
|
/* RHS */
|
||||||
* RHS
|
|
||||||
* - missing is ok, and is same as empty.
|
|
||||||
* - empty is ok; it means not to store.
|
|
||||||
* - otherwise it must be a valid looking ref.
|
|
||||||
*/
|
|
||||||
if (!rs[i].dst)
|
if (!rs[i].dst)
|
||||||
; /* ok */
|
; /* missing is ok; it is the same as empty */
|
||||||
else if (!*rs[i].dst)
|
else if (!*rs[i].dst)
|
||||||
; /* ok */
|
; /* empty is ok; it means "do not store" */
|
||||||
else if (check_refname_format(rs[i].dst, flags))
|
else if (!check_refname_format(rs[i].dst, flags))
|
||||||
|
; /* valid looking ref is ok */
|
||||||
|
else
|
||||||
goto invalid;
|
goto invalid;
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
@ -1466,7 +1466,12 @@ int get_fetch_map(const struct ref *remote_refs,
|
|||||||
} else {
|
} else {
|
||||||
const char *name = refspec->src[0] ? refspec->src : "HEAD";
|
const char *name = refspec->src[0] ? refspec->src : "HEAD";
|
||||||
|
|
||||||
ref_map = get_remote_ref(remote_refs, name);
|
if (refspec->exact_sha1) {
|
||||||
|
ref_map = alloc_ref(name);
|
||||||
|
get_sha1_hex(name, ref_map->old_sha1);
|
||||||
|
} else {
|
||||||
|
ref_map = get_remote_ref(remote_refs, name);
|
||||||
|
}
|
||||||
if (!missing_ok && !ref_map)
|
if (!missing_ok && !ref_map)
|
||||||
die("Couldn't find remote ref %s", name);
|
die("Couldn't find remote ref %s", name);
|
||||||
if (ref_map) {
|
if (ref_map) {
|
||||||
|
1
remote.h
1
remote.h
@ -62,6 +62,7 @@ struct refspec {
|
|||||||
unsigned force : 1;
|
unsigned force : 1;
|
||||||
unsigned pattern : 1;
|
unsigned pattern : 1;
|
||||||
unsigned matching : 1;
|
unsigned matching : 1;
|
||||||
|
unsigned exact_sha1 : 1;
|
||||||
|
|
||||||
char *src;
|
char *src;
|
||||||
char *dst;
|
char *dst;
|
||||||
|
@ -1043,4 +1043,38 @@ do
|
|||||||
'
|
'
|
||||||
done
|
done
|
||||||
|
|
||||||
|
test_expect_success 'fetch exact SHA1' '
|
||||||
|
mk_test heads/master hidden/one &&
|
||||||
|
git push testrepo master:refs/hidden/one &&
|
||||||
|
(
|
||||||
|
cd testrepo &&
|
||||||
|
git config transfer.hiderefs refs/hidden
|
||||||
|
) &&
|
||||||
|
check_push_result $the_commit hidden/one &&
|
||||||
|
|
||||||
|
mk_child child &&
|
||||||
|
(
|
||||||
|
cd child &&
|
||||||
|
|
||||||
|
# make sure $the_commit does not exist here
|
||||||
|
git repack -a -d &&
|
||||||
|
git prune &&
|
||||||
|
test_must_fail git cat-file -t $the_commit &&
|
||||||
|
|
||||||
|
# fetching the hidden object should fail by default
|
||||||
|
test_must_fail git fetch -v ../testrepo $the_commit:refs/heads/copy &&
|
||||||
|
test_must_fail git rev-parse --verify refs/heads/copy &&
|
||||||
|
|
||||||
|
# the server side can allow it to succeed
|
||||||
|
(
|
||||||
|
cd ../testrepo &&
|
||||||
|
git config uploadpack.allowtipsha1inwant true
|
||||||
|
) &&
|
||||||
|
|
||||||
|
git fetch -v ../testrepo $the_commit:refs/heads/copy &&
|
||||||
|
result=$(git rev-parse --verify refs/heads/copy) &&
|
||||||
|
test "$the_commit" = "$result"
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
@ -518,11 +518,9 @@ static int fetch_refs_via_pack(struct transport *transport,
|
|||||||
int nr_heads, struct ref **to_fetch)
|
int nr_heads, struct ref **to_fetch)
|
||||||
{
|
{
|
||||||
struct git_transport_data *data = transport->data;
|
struct git_transport_data *data = transport->data;
|
||||||
struct string_list sought = STRING_LIST_INIT_DUP;
|
|
||||||
const struct ref *refs;
|
const struct ref *refs;
|
||||||
char *dest = xstrdup(transport->url);
|
char *dest = xstrdup(transport->url);
|
||||||
struct fetch_pack_args args;
|
struct fetch_pack_args args;
|
||||||
int i;
|
|
||||||
struct ref *refs_tmp = NULL;
|
struct ref *refs_tmp = NULL;
|
||||||
|
|
||||||
memset(&args, 0, sizeof(args));
|
memset(&args, 0, sizeof(args));
|
||||||
@ -536,9 +534,6 @@ static int fetch_refs_via_pack(struct transport *transport,
|
|||||||
args.no_progress = !transport->progress;
|
args.no_progress = !transport->progress;
|
||||||
args.depth = data->options.depth;
|
args.depth = data->options.depth;
|
||||||
|
|
||||||
for (i = 0; i < nr_heads; i++)
|
|
||||||
string_list_append(&sought, to_fetch[i]->name);
|
|
||||||
|
|
||||||
if (!data->got_remote_heads) {
|
if (!data->got_remote_heads) {
|
||||||
connect_setup(transport, 0, 0);
|
connect_setup(transport, 0, 0);
|
||||||
get_remote_heads(data->fd[0], &refs_tmp, 0, NULL);
|
get_remote_heads(data->fd[0], &refs_tmp, 0, NULL);
|
||||||
@ -547,7 +542,8 @@ static int fetch_refs_via_pack(struct transport *transport,
|
|||||||
|
|
||||||
refs = fetch_pack(&args, data->fd, data->conn,
|
refs = fetch_pack(&args, data->fd, data->conn,
|
||||||
refs_tmp ? refs_tmp : transport->remote_refs,
|
refs_tmp ? refs_tmp : transport->remote_refs,
|
||||||
dest, &sought, &transport->pack_lockfile);
|
dest, to_fetch, nr_heads,
|
||||||
|
&transport->pack_lockfile);
|
||||||
close(data->fd[0]);
|
close(data->fd[0]);
|
||||||
close(data->fd[1]);
|
close(data->fd[1]);
|
||||||
if (finish_connect(data->conn))
|
if (finish_connect(data->conn))
|
||||||
@ -557,7 +553,6 @@ static int fetch_refs_via_pack(struct transport *transport,
|
|||||||
|
|
||||||
free_refs(refs_tmp);
|
free_refs(refs_tmp);
|
||||||
|
|
||||||
string_list_clear(&sought, 0);
|
|
||||||
free(dest);
|
free(dest);
|
||||||
return (refs ? 0 : -1);
|
return (refs ? 0 : -1);
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@ static const char upload_pack_usage[] = "git upload-pack [--strict] [--timeout=<
|
|||||||
#define SHALLOW (1u << 16)
|
#define SHALLOW (1u << 16)
|
||||||
#define NOT_SHALLOW (1u << 17)
|
#define NOT_SHALLOW (1u << 17)
|
||||||
#define CLIENT_SHALLOW (1u << 18)
|
#define CLIENT_SHALLOW (1u << 18)
|
||||||
|
#define HIDDEN_REF (1u << 19)
|
||||||
|
|
||||||
static unsigned long oldest_have;
|
static unsigned long oldest_have;
|
||||||
|
|
||||||
@ -33,6 +34,7 @@ static int multi_ack;
|
|||||||
static int no_done;
|
static int no_done;
|
||||||
static int use_thin_pack, use_ofs_delta, use_include_tag;
|
static int use_thin_pack, use_ofs_delta, use_include_tag;
|
||||||
static int no_progress, daemon_mode;
|
static int no_progress, daemon_mode;
|
||||||
|
static int allow_tip_sha1_in_want;
|
||||||
static int shallow_nr;
|
static int shallow_nr;
|
||||||
static struct object_array have_obj;
|
static struct object_array have_obj;
|
||||||
static struct object_array want_obj;
|
static struct object_array want_obj;
|
||||||
@ -487,6 +489,12 @@ static int get_common_commits(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int is_our_ref(struct object *o)
|
||||||
|
{
|
||||||
|
return o->flags &
|
||||||
|
((allow_tip_sha1_in_want ? HIDDEN_REF : 0) | OUR_REF);
|
||||||
|
}
|
||||||
|
|
||||||
static void check_non_tip(void)
|
static void check_non_tip(void)
|
||||||
{
|
{
|
||||||
static const char *argv[] = {
|
static const char *argv[] = {
|
||||||
@ -523,7 +531,7 @@ static void check_non_tip(void)
|
|||||||
o = get_indexed_object(--i);
|
o = get_indexed_object(--i);
|
||||||
if (!o)
|
if (!o)
|
||||||
continue;
|
continue;
|
||||||
if (!(o->flags & OUR_REF))
|
if (!is_our_ref(o))
|
||||||
continue;
|
continue;
|
||||||
memcpy(namebuf + 1, sha1_to_hex(o->sha1), 40);
|
memcpy(namebuf + 1, sha1_to_hex(o->sha1), 40);
|
||||||
if (write_in_full(cmd.in, namebuf, 42) < 0)
|
if (write_in_full(cmd.in, namebuf, 42) < 0)
|
||||||
@ -532,7 +540,7 @@ static void check_non_tip(void)
|
|||||||
namebuf[40] = '\n';
|
namebuf[40] = '\n';
|
||||||
for (i = 0; i < want_obj.nr; i++) {
|
for (i = 0; i < want_obj.nr; i++) {
|
||||||
o = want_obj.objects[i].item;
|
o = want_obj.objects[i].item;
|
||||||
if (o->flags & OUR_REF)
|
if (is_our_ref(o))
|
||||||
continue;
|
continue;
|
||||||
memcpy(namebuf, sha1_to_hex(o->sha1), 40);
|
memcpy(namebuf, sha1_to_hex(o->sha1), 40);
|
||||||
if (write_in_full(cmd.in, namebuf, 41) < 0)
|
if (write_in_full(cmd.in, namebuf, 41) < 0)
|
||||||
@ -566,7 +574,7 @@ error:
|
|||||||
/* Pick one of them (we know there at least is one) */
|
/* Pick one of them (we know there at least is one) */
|
||||||
for (i = 0; i < want_obj.nr; i++) {
|
for (i = 0; i < want_obj.nr; i++) {
|
||||||
o = want_obj.objects[i].item;
|
o = want_obj.objects[i].item;
|
||||||
if (!(o->flags & OUR_REF))
|
if (!is_our_ref(o))
|
||||||
die("git upload-pack: not our ref %s",
|
die("git upload-pack: not our ref %s",
|
||||||
sha1_to_hex(o->sha1));
|
sha1_to_hex(o->sha1));
|
||||||
}
|
}
|
||||||
@ -646,7 +654,7 @@ static void receive_needs(void)
|
|||||||
sha1_to_hex(sha1_buf));
|
sha1_to_hex(sha1_buf));
|
||||||
if (!(o->flags & WANTED)) {
|
if (!(o->flags & WANTED)) {
|
||||||
o->flags |= WANTED;
|
o->flags |= WANTED;
|
||||||
if (!(o->flags & OUR_REF))
|
if (!is_our_ref(o))
|
||||||
has_non_tip = 1;
|
has_non_tip = 1;
|
||||||
add_object_array(o, NULL, &want_obj);
|
add_object_array(o, NULL, &want_obj);
|
||||||
}
|
}
|
||||||
@ -732,8 +740,10 @@ static int mark_our_ref(const char *refname, const unsigned char *sha1, int flag
|
|||||||
{
|
{
|
||||||
struct object *o = lookup_unknown_object(sha1);
|
struct object *o = lookup_unknown_object(sha1);
|
||||||
|
|
||||||
if (ref_is_hidden(refname))
|
if (ref_is_hidden(refname)) {
|
||||||
|
o->flags |= HIDDEN_REF;
|
||||||
return 1;
|
return 1;
|
||||||
|
}
|
||||||
if (!o)
|
if (!o)
|
||||||
die("git upload-pack: cannot find object %s:", sha1_to_hex(sha1));
|
die("git upload-pack: cannot find object %s:", sha1_to_hex(sha1));
|
||||||
o->flags |= OUR_REF;
|
o->flags |= OUR_REF;
|
||||||
@ -752,9 +762,10 @@ static int send_ref(const char *refname, const unsigned char *sha1, int flag, vo
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (capabilities)
|
if (capabilities)
|
||||||
packet_write(1, "%s %s%c%s%s agent=%s\n",
|
packet_write(1, "%s %s%c%s%s%s agent=%s\n",
|
||||||
sha1_to_hex(sha1), refname_nons,
|
sha1_to_hex(sha1), refname_nons,
|
||||||
0, capabilities,
|
0, capabilities,
|
||||||
|
allow_tip_sha1_in_want ? " allow-tip-sha1-in-want" : "",
|
||||||
stateless_rpc ? " no-done" : "",
|
stateless_rpc ? " no-done" : "",
|
||||||
git_user_agent_sanitized());
|
git_user_agent_sanitized());
|
||||||
else
|
else
|
||||||
@ -788,6 +799,8 @@ static void upload_pack(void)
|
|||||||
|
|
||||||
static int upload_pack_config(const char *var, const char *value, void *unused)
|
static int upload_pack_config(const char *var, const char *value, void *unused)
|
||||||
{
|
{
|
||||||
|
if (!strcmp("uploadpack.allowtipsha1inwant", var))
|
||||||
|
allow_tip_sha1_in_want = git_config_bool(var, value);
|
||||||
return parse_hide_refs_config(var, value, "uploadpack");
|
return parse_hide_refs_config(var, value, "uploadpack");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user