Merge branch 'bc/sha-256-part-2'

SHA-256 migration work continues.

* bc/sha-256-part-2: (44 commits)
  remote-testgit: adapt for object-format
  bundle: detect hash algorithm when reading refs
  t5300: pass --object-format to git index-pack
  t5704: send object-format capability with SHA-256
  t5703: use object-format serve option
  t5702: offer an object-format capability in the test
  t/helper: initialize the repository for test-sha1-array
  remote-curl: avoid truncating refs with ls-remote
  t1050: pass algorithm to index-pack when outside repo
  builtin/index-pack: add option to specify hash algorithm
  remote-curl: detect algorithm for dumb HTTP by size
  builtin/ls-remote: initialize repository based on fetch
  t5500: make hash independent
  serve: advertise object-format capability for protocol v2
  connect: parse v2 refs with correct hash algorithm
  connect: pass full packet reader when parsing v2 refs
  Documentation/technical: document object-format for protocol v2
  t1302: expect repo format version 1 for SHA-256
  builtin/show-index: provide options to determine hash algo
  t5302: modernize test formatting
  ...
This commit is contained in:
Junio C Hamano 2020-07-06 22:09:13 -07:00
commit 12210859da
45 changed files with 696 additions and 248 deletions

View File

@ -93,6 +93,14 @@ OPTIONS
--max-input-size=<size>:: --max-input-size=<size>::
Die, if the pack is larger than <size>. Die, if the pack is larger than <size>.
--object-format=<hash-algorithm>::
Specify the given object format (hash algorithm) for the pack. The valid
values are 'sha1' and (if enabled) 'sha256'. The default is the algorithm for
the current repository (set by `extensions.objectFormat`), or 'sha1' if no
value is set or outside a repository.
+
This option cannot be used with --stdin.
NOTES NOTES
----- -----

View File

@ -9,7 +9,7 @@ git-show-index - Show packed archive index
SYNOPSIS SYNOPSIS
-------- --------
[verse] [verse]
'git show-index' 'git show-index' [--object-format=<hash-algorithm>]
DESCRIPTION DESCRIPTION
@ -36,6 +36,15 @@ Note that you can get more information on a packfile by calling
linkgit:git-verify-pack[1]. However, as this command considers only the linkgit:git-verify-pack[1]. However, as this command considers only the
index file itself, it's both faster and more flexible. index file itself, it's both faster and more flexible.
OPTIONS
-------
--object-format=<hash-algorithm>::
Specify the given object format (hash algorithm) for the index file. The
valid values are 'sha1' and (if enabled) 'sha256'. The default is the
algorithm for the current repository (set by `extensions.objectFormat`), or
'sha1' if no value is set or outside a repository..
GIT GIT
--- ---
Part of the linkgit:git[1] suite Part of the linkgit:git[1] suite

View File

@ -238,6 +238,9 @@ the remote repository.
`--signed-tags=verbatim` to linkgit:git-fast-export[1]. In the `--signed-tags=verbatim` to linkgit:git-fast-export[1]. In the
absence of this capability, Git will use `--signed-tags=warn-strip`. absence of this capability, Git will use `--signed-tags=warn-strip`.
'object-format'::
This indicates that the helper is able to interact with the remote
side using an explicit hash algorithm extension.
COMMANDS COMMANDS
@ -257,12 +260,14 @@ Support for this command is mandatory.
'list':: 'list'::
Lists the refs, one per line, in the format "<value> <name> Lists the refs, one per line, in the format "<value> <name>
[<attr> ...]". The value may be a hex sha1 hash, "@<dest>" for [<attr> ...]". The value may be a hex sha1 hash, "@<dest>" for
a symref, or "?" to indicate that the helper could not get the a symref, ":<keyword> <value>" for a key-value pair, or
value of the ref. A space-separated list of attributes follows "?" to indicate that the helper could not get the value of the
the name; unrecognized attributes are ignored. The list ends ref. A space-separated list of attributes follows the name;
with a blank line. unrecognized attributes are ignored. The list ends with a
blank line.
+ +
See REF LIST ATTRIBUTES for a list of currently defined attributes. See REF LIST ATTRIBUTES for a list of currently defined attributes.
See REF LIST KEYWORDS for a list of currently defined keywords.
+ +
Supported if the helper has the "fetch" or "import" capability. Supported if the helper has the "fetch" or "import" capability.
@ -432,6 +437,18 @@ attributes are defined.
This ref is unchanged since the last import or fetch, although This ref is unchanged since the last import or fetch, although
the helper cannot necessarily determine what value that produced. the helper cannot necessarily determine what value that produced.
REF LIST KEYWORDS
-----------------
The 'list' command may produce a list of key-value pairs.
The following keys are defined.
'object-format'::
The refs are using the given hash algorithm. This keyword is only
used if the server and client both support the object-format
extension.
OPTIONS OPTIONS
------- -------
@ -516,6 +533,14 @@ set by Git if the remote helper has the 'option' capability.
transaction. If successful, all refs will be updated, or none will. If the transaction. If successful, all refs will be updated, or none will. If the
remote side does not support this capability, the push will fail. remote side does not support this capability, the push will fail.
'option object-format' {'true'|algorithm}::
If 'true', indicate that the caller wants hash algorithm information
to be passed back from the remote. This mode is used when fetching
refs.
+
If set to an algorithm, indicate that the caller wants to interact with
the remote side using that algorithm.
SEE ALSO SEE ALSO
-------- --------
linkgit:git-remote[1] linkgit:git-remote[1]

View File

@ -176,6 +176,21 @@ agent strings are purely informative for statistics and debugging
purposes, and MUST NOT be used to programmatically assume the presence purposes, and MUST NOT be used to programmatically assume the presence
or absence of particular features. or absence of particular features.
object-format
-------------
This capability, which takes a hash algorithm as an argument, indicates
that the server supports the given hash algorithms. It may be sent
multiple times; if so, the first one given is the one used in the ref
advertisement.
When provided by the client, this indicates that it intends to use the
given hash algorithm to communicate. The algorithm provided must be one
that the server supports.
If this capability is not provided, it is assumed that the only
supported algorithm is SHA-1.
symref symref
------ ------

View File

@ -483,3 +483,12 @@ included in a request. This is done by sending each option as a
a request. a request.
The provided options must not contain a NUL or LF character. The provided options must not contain a NUL or LF character.
object-format
~~~~~~~~~~~~~~~
The server can advertise the `object-format` capability with a value `X` (in the
form `object-format=X`) to notify the client that the server is able to deal
with objects using hash algorithm X. If not specified, the server is assumed to
only handle SHA-1. If the client would like to use a hash algorithm other than
SHA-1, it should specify its object-format string.

View File

@ -1220,6 +1220,15 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
refs = transport_get_remote_refs(transport, &ref_prefixes); refs = transport_get_remote_refs(transport, &ref_prefixes);
if (refs) { if (refs) {
int hash_algo = hash_algo_by_ptr(transport_get_hash_algo(transport));
/*
* Now that we know what algorithm the remote side is using,
* let's set ours to the same thing.
*/
initialize_repository_version(hash_algo);
repo_set_hash_algo(the_repository, hash_algo);
mapped_refs = wanted_peer_refs(refs, &remote->fetch); mapped_refs = wanted_peer_refs(refs, &remote->fetch);
/* /*
* transport_get_remote_refs() may return refs with null sha-1 * transport_get_remote_refs() may return refs with null sha-1

View File

@ -1555,13 +1555,9 @@ static void read_v2_anomalous_offsets(struct packed_git *p,
{ {
const uint32_t *idx1, *idx2; const uint32_t *idx1, *idx2;
uint32_t i; uint32_t i;
const uint32_t hashwords = the_hash_algo->rawsz / sizeof(uint32_t);
/* The address of the 4-byte offset table */ /* The address of the 4-byte offset table */
idx1 = (((const uint32_t *)p->index_data) idx1 = (((const uint32_t *)((const uint8_t *)p->index_data + p->crc_offset))
+ 2 /* 8-byte header */
+ 256 /* fan out */
+ hashwords * p->num_objects /* object ID table */
+ p->num_objects /* CRC32 table */ + p->num_objects /* CRC32 table */
); );
@ -1671,6 +1667,7 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
unsigned char pack_hash[GIT_MAX_RAWSZ]; unsigned char pack_hash[GIT_MAX_RAWSZ];
unsigned foreign_nr = 1; /* zero is a "good" value, assume bad */ unsigned foreign_nr = 1; /* zero is a "good" value, assume bad */
int report_end_of_input = 0; int report_end_of_input = 0;
int hash_algo = 0;
/* /*
* index-pack never needs to fetch missing objects except when * index-pack never needs to fetch missing objects except when
@ -1764,6 +1761,11 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
die(_("bad %s"), arg); die(_("bad %s"), arg);
} else if (skip_prefix(arg, "--max-input-size=", &arg)) { } else if (skip_prefix(arg, "--max-input-size=", &arg)) {
max_input_size = strtoumax(arg, NULL, 10); max_input_size = strtoumax(arg, NULL, 10);
} else if (skip_prefix(arg, "--object-format=", &arg)) {
hash_algo = hash_algo_by_name(arg);
if (hash_algo == GIT_HASH_UNKNOWN)
die(_("unknown hash algorithm '%s'"), arg);
repo_set_hash_algo(the_repository, hash_algo);
} else } else
usage(index_pack_usage); usage(index_pack_usage);
continue; continue;
@ -1780,6 +1782,8 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
die(_("--fix-thin cannot be used without --stdin")); die(_("--fix-thin cannot be used without --stdin"));
if (from_stdin && !startup_info->have_repository) if (from_stdin && !startup_info->have_repository)
die(_("--stdin requires a git repository")); die(_("--stdin requires a git repository"));
if (from_stdin && hash_algo)
die(_("--object-format cannot be used with --stdin"));
if (!index_name && pack_name) if (!index_name && pack_name)
index_name = derive_filename(pack_name, "idx", &index_name_buf); index_name = derive_filename(pack_name, "idx", &index_name_buf);

View File

@ -118,6 +118,10 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
transport->server_options = &server_options; transport->server_options = &server_options;
ref = transport_get_remote_refs(transport, &ref_prefixes); ref = transport_get_remote_refs(transport, &ref_prefixes);
if (ref) {
int hash_algo = hash_algo_by_ptr(transport_get_hash_algo(transport));
repo_set_hash_algo(the_repository, hash_algo);
}
if (transport_disconnect(transport)) { if (transport_disconnect(transport)) {
UNLEAK(sorting); UNLEAK(sorting);
return 1; return 1;

View File

@ -249,6 +249,7 @@ static void show_ref(const char *path, const struct object_id *oid)
strbuf_addf(&cap, " push-cert=%s", push_cert_nonce); strbuf_addf(&cap, " push-cert=%s", push_cert_nonce);
if (advertise_push_options) if (advertise_push_options)
strbuf_addstr(&cap, " push-options"); strbuf_addstr(&cap, " push-options");
strbuf_addf(&cap, " object-format=%s", the_hash_algo->name);
strbuf_addf(&cap, " agent=%s", git_user_agent_sanitized()); strbuf_addf(&cap, " agent=%s", git_user_agent_sanitized());
packet_write_fmt(1, "%s %s%c%s\n", packet_write_fmt(1, "%s %s%c%s\n",
oid_to_hex(oid), path, 0, cap.buf); oid_to_hex(oid), path, 0, cap.buf);
@ -1624,6 +1625,8 @@ static struct command *read_head_info(struct packet_reader *reader,
linelen = strlen(reader->line); linelen = strlen(reader->line);
if (linelen < reader->pktlen) { if (linelen < reader->pktlen) {
const char *feature_list = reader->line + linelen + 1; const char *feature_list = reader->line + linelen + 1;
const char *hash = NULL;
int len = 0;
if (parse_feature_request(feature_list, "report-status")) if (parse_feature_request(feature_list, "report-status"))
report_status = 1; report_status = 1;
if (parse_feature_request(feature_list, "side-band-64k")) if (parse_feature_request(feature_list, "side-band-64k"))
@ -1636,6 +1639,13 @@ static struct command *read_head_info(struct packet_reader *reader,
if (advertise_push_options if (advertise_push_options
&& parse_feature_request(feature_list, "push-options")) && parse_feature_request(feature_list, "push-options"))
use_push_options = 1; use_push_options = 1;
hash = parse_feature_value(feature_list, "object-format", &len, NULL);
if (!hash) {
hash = hash_algos[GIT_HASH_SHA1].name;
len = strlen(hash);
}
if (xstrncmpz(the_hash_algo->name, hash, len))
die("error: unsupported object format '%s'", hash);
} }
if (!strcmp(reader->line, "push-cert")) { if (!strcmp(reader->line, "push-cert")) {

View File

@ -1,9 +1,12 @@
#include "builtin.h" #include "builtin.h"
#include "cache.h" #include "cache.h"
#include "pack.h" #include "pack.h"
#include "parse-options.h"
static const char show_index_usage[] = static const char *const show_index_usage[] = {
"git show-index"; "git show-index [--object-format=<hash-algorithm>]",
NULL
};
int cmd_show_index(int argc, const char **argv, const char *prefix) int cmd_show_index(int argc, const char **argv, const char *prefix)
{ {
@ -11,10 +14,26 @@ int cmd_show_index(int argc, const char **argv, const char *prefix)
unsigned nr; unsigned nr;
unsigned int version; unsigned int version;
static unsigned int top_index[256]; static unsigned int top_index[256];
const unsigned hashsz = the_hash_algo->rawsz; unsigned hashsz;
const char *hash_name = NULL;
int hash_algo;
const struct option show_index_options[] = {
OPT_STRING(0, "object-format", &hash_name, N_("hash-algorithm"),
N_("specify the hash algorithm to use")),
OPT_END()
};
argc = parse_options(argc, argv, prefix, show_index_options, show_index_usage, 0);
if (hash_name) {
hash_algo = hash_algo_by_name(hash_name);
if (hash_algo == GIT_HASH_UNKNOWN)
die(_("Unknown hash algorithm"));
repo_set_hash_algo(the_repository, hash_algo);
}
hashsz = the_hash_algo->rawsz;
if (argc != 1)
usage(show_index_usage);
if (fread(top_index, 2 * 4, 1, stdin) != 1) if (fread(top_index, 2 * 4, 1, stdin) != 1)
die("unable to read header"); die("unable to read header");
if (top_index[0] == htonl(PACK_IDX_SIGNATURE)) { if (top_index[0] == htonl(PACK_IDX_SIGNATURE)) {

View File

@ -23,6 +23,17 @@ static void add_to_ref_list(const struct object_id *oid, const char *name,
list->nr++; list->nr++;
} }
static const struct git_hash_algo *detect_hash_algo(struct strbuf *buf)
{
size_t len = strcspn(buf->buf, " \n");
int algo;
algo = hash_algo_by_length(len / 2);
if (algo == GIT_HASH_UNKNOWN)
return NULL;
return &hash_algos[algo];
}
static int parse_bundle_header(int fd, struct bundle_header *header, static int parse_bundle_header(int fd, struct bundle_header *header,
const char *report_path) const char *report_path)
{ {
@ -52,12 +63,21 @@ static int parse_bundle_header(int fd, struct bundle_header *header,
} }
strbuf_rtrim(&buf); strbuf_rtrim(&buf);
if (!header->hash_algo) {
header->hash_algo = detect_hash_algo(&buf);
if (!header->hash_algo) {
error(_("unknown hash algorithm length"));
status = -1;
break;
}
}
/* /*
* Tip lines have object name, SP, and refname. * Tip lines have object name, SP, and refname.
* Prerequisites have object name that is optionally * Prerequisites have object name that is optionally
* followed by SP and subject line. * followed by SP and subject line.
*/ */
if (parse_oid_hex(buf.buf, &oid, &p) || if (parse_oid_hex_algop(buf.buf, &oid, &p, header->hash_algo) ||
(*p && !isspace(*p)) || (*p && !isspace(*p)) ||
(!is_prereq && !*p)) { (!is_prereq && !*p)) {
if (report_path) if (report_path)

View File

@ -15,6 +15,7 @@ struct ref_list {
struct bundle_header { struct bundle_header {
struct ref_list prerequisites; struct ref_list prerequisites;
struct ref_list references; struct ref_list references;
const struct git_hash_algo *hash_algo;
}; };
int is_bundle(const char *path, int quiet); int is_bundle(const char *path, int quiet);

138
connect.c
View File

@ -18,7 +18,7 @@
static char *server_capabilities_v1; static char *server_capabilities_v1;
static struct argv_array server_capabilities_v2 = ARGV_ARRAY_INIT; static struct argv_array server_capabilities_v2 = ARGV_ARRAY_INIT;
static const char *parse_feature_value(const char *, const char *, int *); static const char *next_server_feature_value(const char *feature, int *len, int *offset);
static int check_ref(const char *name, unsigned int flags) static int check_ref(const char *name, unsigned int flags)
{ {
@ -83,6 +83,21 @@ int server_supports_v2(const char *c, int die_on_error)
return 0; return 0;
} }
int server_feature_v2(const char *c, const char **v)
{
int i;
for (i = 0; i < server_capabilities_v2.argc; i++) {
const char *out;
if (skip_prefix(server_capabilities_v2.argv[i], c, &out) &&
(*out == '=')) {
*v = out + 1;
return 1;
}
}
return 0;
}
int server_supports_feature(const char *c, const char *feature, int server_supports_feature(const char *c, const char *feature,
int die_on_error) int die_on_error)
{ {
@ -181,17 +196,16 @@ reject:
static void annotate_refs_with_symref_info(struct ref *ref) static void annotate_refs_with_symref_info(struct ref *ref)
{ {
struct string_list symref = STRING_LIST_INIT_DUP; struct string_list symref = STRING_LIST_INIT_DUP;
const char *feature_list = server_capabilities_v1; int offset = 0;
while (feature_list) { while (1) {
int len; int len;
const char *val; const char *val;
val = parse_feature_value(feature_list, "symref", &len); val = next_server_feature_value("symref", &len, &offset);
if (!val) if (!val)
break; break;
parse_one_symref_info(&symref, val, len); parse_one_symref_info(&symref, val, len);
feature_list = val + 1;
} }
string_list_sort(&symref); string_list_sort(&symref);
@ -205,21 +219,36 @@ static void annotate_refs_with_symref_info(struct ref *ref)
string_list_clear(&symref, 0); string_list_clear(&symref, 0);
} }
static void process_capabilities(const char *line, int *len) static void process_capabilities(struct packet_reader *reader, int *linelen)
{ {
const char *feat_val;
int feat_len;
const char *line = reader->line;
int nul_location = strlen(line); int nul_location = strlen(line);
if (nul_location == *len) if (nul_location == *linelen)
return; return;
server_capabilities_v1 = xstrdup(line + nul_location + 1); server_capabilities_v1 = xstrdup(line + nul_location + 1);
*len = nul_location; *linelen = nul_location;
feat_val = server_feature_value("object-format", &feat_len);
if (feat_val) {
char *hash_name = xstrndup(feat_val, feat_len);
int hash_algo = hash_algo_by_name(hash_name);
if (hash_algo != GIT_HASH_UNKNOWN)
reader->hash_algo = &hash_algos[hash_algo];
free(hash_name);
} else {
reader->hash_algo = &hash_algos[GIT_HASH_SHA1];
}
} }
static int process_dummy_ref(const char *line) static int process_dummy_ref(const struct packet_reader *reader)
{ {
const char *line = reader->line;
struct object_id oid; struct object_id oid;
const char *name; const char *name;
if (parse_oid_hex(line, &oid, &name)) if (parse_oid_hex_algop(line, &oid, &name, reader->hash_algo))
return 0; return 0;
if (*name != ' ') if (*name != ' ')
return 0; return 0;
@ -235,13 +264,15 @@ static void check_no_capabilities(const char *line, int len)
line + strlen(line)); line + strlen(line));
} }
static int process_ref(const char *line, int len, struct ref ***list, static int process_ref(const struct packet_reader *reader, int len,
unsigned int flags, struct oid_array *extra_have) struct ref ***list, unsigned int flags,
struct oid_array *extra_have)
{ {
const char *line = reader->line;
struct object_id old_oid; struct object_id old_oid;
const char *name; const char *name;
if (parse_oid_hex(line, &old_oid, &name)) if (parse_oid_hex_algop(line, &old_oid, &name, reader->hash_algo))
return 0; return 0;
if (*name != ' ') if (*name != ' ')
return 0; return 0;
@ -261,16 +292,17 @@ static int process_ref(const char *line, int len, struct ref ***list,
return 1; return 1;
} }
static int process_shallow(const char *line, int len, static int process_shallow(const struct packet_reader *reader, int len,
struct oid_array *shallow_points) struct oid_array *shallow_points)
{ {
const char *line = reader->line;
const char *arg; const char *arg;
struct object_id old_oid; struct object_id old_oid;
if (!skip_prefix(line, "shallow ", &arg)) if (!skip_prefix(line, "shallow ", &arg))
return 0; return 0;
if (get_oid_hex(arg, &old_oid)) if (get_oid_hex_algop(arg, &old_oid, reader->hash_algo))
die(_("protocol error: expected shallow sha-1, got '%s'"), arg); die(_("protocol error: expected shallow sha-1, got '%s'"), arg);
if (!shallow_points) if (!shallow_points)
die(_("repository on the other end cannot be shallow")); die(_("repository on the other end cannot be shallow"));
@ -317,20 +349,20 @@ struct ref **get_remote_heads(struct packet_reader *reader,
switch (state) { switch (state) {
case EXPECTING_FIRST_REF: case EXPECTING_FIRST_REF:
process_capabilities(reader->line, &len); process_capabilities(reader, &len);
if (process_dummy_ref(reader->line)) { if (process_dummy_ref(reader)) {
state = EXPECTING_SHALLOW; state = EXPECTING_SHALLOW;
break; break;
} }
state = EXPECTING_REF; state = EXPECTING_REF;
/* fallthrough */ /* fallthrough */
case EXPECTING_REF: case EXPECTING_REF:
if (process_ref(reader->line, len, &list, flags, extra_have)) if (process_ref(reader, len, &list, flags, extra_have))
break; break;
state = EXPECTING_SHALLOW; state = EXPECTING_SHALLOW;
/* fallthrough */ /* fallthrough */
case EXPECTING_SHALLOW: case EXPECTING_SHALLOW:
if (process_shallow(reader->line, len, shallow_points)) if (process_shallow(reader, len, shallow_points))
break; break;
die(_("protocol error: unexpected '%s'"), reader->line); die(_("protocol error: unexpected '%s'"), reader->line);
case EXPECTING_DONE: case EXPECTING_DONE:
@ -344,7 +376,7 @@ struct ref **get_remote_heads(struct packet_reader *reader,
} }
/* Returns 1 when a valid ref has been added to `list`, 0 otherwise */ /* Returns 1 when a valid ref has been added to `list`, 0 otherwise */
static int process_ref_v2(const char *line, struct ref ***list) static int process_ref_v2(struct packet_reader *reader, struct ref ***list)
{ {
int ret = 1; int ret = 1;
int i = 0; int i = 0;
@ -352,6 +384,7 @@ static int process_ref_v2(const char *line, struct ref ***list)
struct ref *ref; struct ref *ref;
struct string_list line_sections = STRING_LIST_INIT_DUP; struct string_list line_sections = STRING_LIST_INIT_DUP;
const char *end; const char *end;
const char *line = reader->line;
/* /*
* Ref lines have a number of fields which are space deliminated. The * Ref lines have a number of fields which are space deliminated. The
@ -364,7 +397,7 @@ static int process_ref_v2(const char *line, struct ref ***list)
goto out; goto out;
} }
if (parse_oid_hex(line_sections.items[i++].string, &old_oid, &end) || if (parse_oid_hex_algop(line_sections.items[i++].string, &old_oid, &end, reader->hash_algo) ||
*end) { *end) {
ret = 0; ret = 0;
goto out; goto out;
@ -372,7 +405,7 @@ static int process_ref_v2(const char *line, struct ref ***list)
ref = alloc_ref(line_sections.items[i++].string); ref = alloc_ref(line_sections.items[i++].string);
oidcpy(&ref->old_oid, &old_oid); memcpy(ref->old_oid.hash, old_oid.hash, reader->hash_algo->rawsz);
**list = ref; **list = ref;
*list = &ref->next; *list = &ref->next;
@ -385,7 +418,8 @@ static int process_ref_v2(const char *line, struct ref ***list)
struct object_id peeled_oid; struct object_id peeled_oid;
char *peeled_name; char *peeled_name;
struct ref *peeled; struct ref *peeled;
if (parse_oid_hex(arg, &peeled_oid, &end) || *end) { if (parse_oid_hex_algop(arg, &peeled_oid, &end,
reader->hash_algo) || *end) {
ret = 0; ret = 0;
goto out; goto out;
} }
@ -393,7 +427,8 @@ static int process_ref_v2(const char *line, struct ref ***list)
peeled_name = xstrfmt("%s^{}", ref->name); peeled_name = xstrfmt("%s^{}", ref->name);
peeled = alloc_ref(peeled_name); peeled = alloc_ref(peeled_name);
oidcpy(&peeled->old_oid, &peeled_oid); memcpy(peeled->old_oid.hash, peeled_oid.hash,
reader->hash_algo->rawsz);
**list = peeled; **list = peeled;
*list = &peeled->next; *list = &peeled->next;
@ -423,6 +458,7 @@ struct ref **get_remote_refs(int fd_out, struct packet_reader *reader,
int stateless_rpc) int stateless_rpc)
{ {
int i; int i;
const char *hash_name;
*list = NULL; *list = NULL;
if (server_supports_v2("ls-refs", 1)) if (server_supports_v2("ls-refs", 1))
@ -431,6 +467,16 @@ struct ref **get_remote_refs(int fd_out, struct packet_reader *reader,
if (server_supports_v2("agent", 0)) if (server_supports_v2("agent", 0))
packet_write_fmt(fd_out, "agent=%s", git_user_agent_sanitized()); packet_write_fmt(fd_out, "agent=%s", git_user_agent_sanitized());
if (server_feature_v2("object-format", &hash_name)) {
int hash_algo = hash_algo_by_name(hash_name);
if (hash_algo == GIT_HASH_UNKNOWN)
die(_("unknown object format '%s' specified by server"), hash_name);
reader->hash_algo = &hash_algos[hash_algo];
packet_write_fmt(fd_out, "object-format=%s", reader->hash_algo->name);
} else {
reader->hash_algo = &hash_algos[GIT_HASH_SHA1];
}
if (server_options && server_options->nr && if (server_options && server_options->nr &&
server_supports_v2("server-option", 1)) server_supports_v2("server-option", 1))
for (i = 0; i < server_options->nr; i++) for (i = 0; i < server_options->nr; i++)
@ -450,7 +496,7 @@ struct ref **get_remote_refs(int fd_out, struct packet_reader *reader,
/* Process response from server */ /* Process response from server */
while (packet_reader_read(reader) == PACKET_READ_NORMAL) { while (packet_reader_read(reader) == PACKET_READ_NORMAL) {
if (!process_ref_v2(reader->line, &list)) if (!process_ref_v2(reader, &list))
die(_("invalid ls-refs response: %s"), reader->line); die(_("invalid ls-refs response: %s"), reader->line);
} }
@ -463,7 +509,7 @@ struct ref **get_remote_refs(int fd_out, struct packet_reader *reader,
return list; return list;
} }
static const char *parse_feature_value(const char *feature_list, const char *feature, int *lenp) const char *parse_feature_value(const char *feature_list, const char *feature, int *lenp, int *offset)
{ {
int len; int len;
@ -471,6 +517,8 @@ static const char *parse_feature_value(const char *feature_list, const char *fea
return NULL; return NULL;
len = strlen(feature); len = strlen(feature);
if (offset)
feature_list += *offset;
while (*feature_list) { while (*feature_list) {
const char *found = strstr(feature_list, feature); const char *found = strstr(feature_list, feature);
if (!found) if (!found)
@ -485,9 +533,14 @@ static const char *parse_feature_value(const char *feature_list, const char *fea
} }
/* feature with a value (e.g., "agent=git/1.2.3") */ /* feature with a value (e.g., "agent=git/1.2.3") */
else if (*value == '=') { else if (*value == '=') {
int end;
value++; value++;
end = strcspn(value, " \t\n");
if (lenp) if (lenp)
*lenp = strcspn(value, " \t\n"); *lenp = end;
if (offset)
*offset = value + end - feature_list;
return value; return value;
} }
/* /*
@ -500,14 +553,41 @@ static const char *parse_feature_value(const char *feature_list, const char *fea
return NULL; return NULL;
} }
int server_supports_hash(const char *desired, int *feature_supported)
{
int offset = 0;
int len;
const char *hash;
hash = next_server_feature_value("object-format", &len, &offset);
if (feature_supported)
*feature_supported = !!hash;
if (!hash) {
hash = hash_algos[GIT_HASH_SHA1].name;
len = strlen(hash);
}
while (hash) {
if (!xstrncmpz(desired, hash, len))
return 1;
hash = next_server_feature_value("object-format", &len, &offset);
}
return 0;
}
int parse_feature_request(const char *feature_list, const char *feature) int parse_feature_request(const char *feature_list, const char *feature)
{ {
return !!parse_feature_value(feature_list, feature, NULL); return !!parse_feature_value(feature_list, feature, NULL, NULL);
}
static const char *next_server_feature_value(const char *feature, int *len, int *offset)
{
return parse_feature_value(server_capabilities_v1, feature, len, offset);
} }
const char *server_feature_value(const char *feature, int *len) const char *server_feature_value(const char *feature, int *len)
{ {
return parse_feature_value(server_capabilities_v1, feature, len); return parse_feature_value(server_capabilities_v1, feature, len, NULL);
} }
int server_supports(const char *feature) int server_supports(const char *feature)

View File

@ -18,7 +18,10 @@ int url_is_local_not_ssh(const char *url);
struct packet_reader; struct packet_reader;
enum protocol_version discover_version(struct packet_reader *reader); enum protocol_version discover_version(struct packet_reader *reader);
int server_supports_hash(const char *desired, int *feature_supported);
const char *parse_feature_value(const char *feature_list, const char *feature, int *lenp, int *offset);
int server_supports_v2(const char *c, int die_on_error); int server_supports_v2(const char *c, int die_on_error);
int server_feature_v2(const char *c, const char **v);
int server_supports_feature(const char *c, const char *feature, int server_supports_feature(const char *c, const char *feature,
int die_on_error); int die_on_error);

View File

@ -1050,6 +1050,8 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
print_verbose(args, _("Server supports %s"), "deepen-relative"); print_verbose(args, _("Server supports %s"), "deepen-relative");
else if (args->deepen_relative) else if (args->deepen_relative)
die(_("Server does not support --deepen")); die(_("Server does not support --deepen"));
if (!server_supports_hash(the_hash_algo->name, NULL))
die(_("Server does not support this repository's object format"));
if (!args->no_dependents) { if (!args->no_dependents) {
mark_complete_and_common_ref(negotiator, args, &ref); mark_complete_and_common_ref(negotiator, args, &ref);
@ -1188,6 +1190,7 @@ static int send_fetch_request(struct fetch_negotiator *negotiator, int fd_out,
int sideband_all, int seen_ack) int sideband_all, int seen_ack)
{ {
int ret = 0; int ret = 0;
const char *hash_name;
struct strbuf req_buf = STRBUF_INIT; struct strbuf req_buf = STRBUF_INIT;
if (server_supports_v2("fetch", 1)) if (server_supports_v2("fetch", 1))
@ -1202,6 +1205,17 @@ static int send_fetch_request(struct fetch_negotiator *negotiator, int fd_out,
args->server_options->items[i].string); args->server_options->items[i].string);
} }
if (server_feature_v2("object-format", &hash_name)) {
int hash_algo = hash_algo_by_name(hash_name);
if (hash_algo_by_ptr(the_hash_algo) != hash_algo)
die(_("mismatched algorithms: client %s; server %s"),
the_hash_algo->name, hash_name);
packet_write_fmt(fd_out, "object-format=%s", the_hash_algo->name);
} else if (hash_algo_by_ptr(the_hash_algo) != GIT_HASH_SHA1) {
die(_("the server does not support algorithm '%s'"),
the_hash_algo->name);
}
packet_buf_delim(&req_buf); packet_buf_delim(&req_buf);
if (args->use_thin_pack) if (args->use_thin_pack)
packet_buf_write(&req_buf, "thin-pack"); packet_buf_write(&req_buf, "thin-pack");

View File

@ -868,6 +868,12 @@ char *xgetcwd(void);
FILE *fopen_for_writing(const char *path); FILE *fopen_for_writing(const char *path);
FILE *fopen_or_warn(const char *path, const char *mode); FILE *fopen_or_warn(const char *path, const char *mode);
/*
* Like strncmp, but only return zero if s is NUL-terminated and exactly len
* characters long. If it is not, consider it greater than t.
*/
int xstrncmpz(const char *s, const char *t, size_t len);
/* /*
* FREE_AND_NULL(ptr) is like free(ptr) followed by ptr = NULL. Note * FREE_AND_NULL(ptr) is like free(ptr) followed by ptr = NULL. Note
* that ptr is used twice, so don't pass e.g. ptr++. * that ptr is used twice, so don't pass e.g. ptr++.

2
git.c
View File

@ -574,7 +574,7 @@ static struct cmd_struct commands[] = {
{ "shortlog", cmd_shortlog, RUN_SETUP_GENTLY | USE_PAGER }, { "shortlog", cmd_shortlog, RUN_SETUP_GENTLY | USE_PAGER },
{ "show", cmd_show, RUN_SETUP }, { "show", cmd_show, RUN_SETUP },
{ "show-branch", cmd_show_branch, RUN_SETUP }, { "show-branch", cmd_show_branch, RUN_SETUP },
{ "show-index", cmd_show_index }, { "show-index", cmd_show_index, RUN_SETUP_GENTLY },
{ "show-ref", cmd_show_ref, RUN_SETUP }, { "show-ref", cmd_show_ref, RUN_SETUP },
{ "sparse-checkout", cmd_sparse_checkout, RUN_SETUP | NEED_WORK_TREE }, { "sparse-checkout", cmd_sparse_checkout, RUN_SETUP | NEED_WORK_TREE },
{ "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE }, { "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE },

View File

@ -70,6 +70,7 @@ struct packed_git {
size_t index_size; size_t index_size;
uint32_t num_objects; uint32_t num_objects;
uint32_t num_bad_objects; uint32_t num_bad_objects;
uint32_t crc_offset;
unsigned char *bad_object_sha1; unsigned char *bad_object_sha1;
int index_version; int index_version;
time_t mtime; time_t mtime;

View File

@ -178,6 +178,7 @@ int load_idx(const char *path, const unsigned int hashsz, void *idx_map,
*/ */
(sizeof(off_t) <= 4)) (sizeof(off_t) <= 4))
return error("pack too large for current definition of off_t in %s", path); return error("pack too large for current definition of off_t in %s", path);
p->crc_offset = 8 + 4 * 256 + nr * hashsz;
} }
p->index_version = version; p->index_version = version;

View File

@ -490,6 +490,7 @@ void packet_reader_init(struct packet_reader *reader, int fd,
reader->buffer_size = sizeof(packet_buffer); reader->buffer_size = sizeof(packet_buffer);
reader->options = options; reader->options = options;
reader->me = "git"; reader->me = "git";
reader->hash_algo = &hash_algos[GIT_HASH_SHA1];
} }
enum packet_read_status packet_reader_read(struct packet_reader *reader) enum packet_read_status packet_reader_read(struct packet_reader *reader)

View File

@ -177,6 +177,9 @@ struct packet_reader {
unsigned use_sideband : 1; unsigned use_sideband : 1;
const char *me; const char *me;
/* hash algorithm in use */
const struct git_hash_algo *hash_algo;
}; };
/* /*

View File

@ -41,7 +41,9 @@ struct options {
deepen_relative : 1, deepen_relative : 1,
from_promisor : 1, from_promisor : 1,
no_dependents : 1, no_dependents : 1,
atomic : 1; atomic : 1,
object_format : 1;
const struct git_hash_algo *hash_algo;
}; };
static struct options options; static struct options options;
static struct string_list cas_options = STRING_LIST_INIT_DUP; static struct string_list cas_options = STRING_LIST_INIT_DUP;
@ -190,6 +192,16 @@ static int set_option(const char *name, const char *value)
} else if (!strcmp(name, "filter")) { } else if (!strcmp(name, "filter")) {
options.filter = xstrdup(value); options.filter = xstrdup(value);
return 0; return 0;
} else if (!strcmp(name, "object-format")) {
int algo;
options.object_format = 1;
if (strcmp(value, "true")) {
algo = hash_algo_by_name(value);
if (algo == GIT_HASH_UNKNOWN)
die("unknown object format '%s'", value);
options.hash_algo = &hash_algos[algo];
}
return 0;
} else { } else {
return 1 /* unsupported */; return 1 /* unsupported */;
} }
@ -231,6 +243,7 @@ static struct ref *parse_git_refs(struct discovery *heads, int for_push)
case protocol_v0: case protocol_v0:
get_remote_heads(&reader, &list, for_push ? REF_NORMAL : 0, get_remote_heads(&reader, &list, for_push ? REF_NORMAL : 0,
NULL, &heads->shallow); NULL, &heads->shallow);
options.hash_algo = reader.hash_algo;
break; break;
case protocol_unknown_version: case protocol_unknown_version:
BUG("unknown protocol version"); BUG("unknown protocol version");
@ -239,6 +252,19 @@ static struct ref *parse_git_refs(struct discovery *heads, int for_push)
return list; return list;
} }
static const struct git_hash_algo *detect_hash_algo(struct discovery *heads)
{
const char *p = memchr(heads->buf, '\t', heads->len);
int algo;
if (!p)
return the_hash_algo;
algo = hash_algo_by_length((p - heads->buf) / 2);
if (algo == GIT_HASH_UNKNOWN)
return NULL;
return &hash_algos[algo];
}
static struct ref *parse_info_refs(struct discovery *heads) static struct ref *parse_info_refs(struct discovery *heads)
{ {
char *data, *start, *mid; char *data, *start, *mid;
@ -249,6 +275,12 @@ static struct ref *parse_info_refs(struct discovery *heads)
struct ref *ref = NULL; struct ref *ref = NULL;
struct ref *last_ref = NULL; struct ref *last_ref = NULL;
options.hash_algo = detect_hash_algo(heads);
if (!options.hash_algo)
die("%sinfo/refs not valid: could not determine hash algorithm; "
"is this a git repository?",
transport_anonymize_url(url.buf));
data = heads->buf; data = heads->buf;
start = NULL; start = NULL;
mid = data; mid = data;
@ -259,13 +291,13 @@ static struct ref *parse_info_refs(struct discovery *heads)
if (data[i] == '\t') if (data[i] == '\t')
mid = &data[i]; mid = &data[i];
if (data[i] == '\n') { if (data[i] == '\n') {
if (mid - start != the_hash_algo->hexsz) if (mid - start != options.hash_algo->hexsz)
die(_("%sinfo/refs not valid: is this a git repository?"), die(_("%sinfo/refs not valid: is this a git repository?"),
transport_anonymize_url(url.buf)); transport_anonymize_url(url.buf));
data[i] = 0; data[i] = 0;
ref_name = mid + 1; ref_name = mid + 1;
ref = alloc_ref(ref_name); ref = alloc_ref(ref_name);
get_oid_hex(start, &ref->old_oid); get_oid_hex_algop(start, &ref->old_oid, options.hash_algo);
if (!refs) if (!refs)
refs = ref; refs = ref;
if (last_ref) if (last_ref)
@ -509,11 +541,16 @@ static struct ref *get_refs(int for_push)
static void output_refs(struct ref *refs) static void output_refs(struct ref *refs)
{ {
struct ref *posn; struct ref *posn;
if (options.object_format && options.hash_algo) {
printf(":object-format %s\n", options.hash_algo->name);
}
for (posn = refs; posn; posn = posn->next) { for (posn = refs; posn; posn = posn->next) {
if (posn->symref) if (posn->symref)
printf("@%s %s\n", posn->symref, posn->name); printf("@%s %s\n", posn->symref, posn->name);
else else
printf("%s %s\n", oid_to_hex(&posn->old_oid), posn->name); printf("%s %s\n", hash_to_hex_algop(posn->old_oid.hash,
options.hash_algo),
posn->name);
} }
printf("\n"); printf("\n");
fflush(stdout); fflush(stdout);
@ -1499,6 +1536,7 @@ int cmd_main(int argc, const char **argv)
printf("option\n"); printf("option\n");
printf("push\n"); printf("push\n");
printf("check-connectivity\n"); printf("check-connectivity\n");
printf("object-format\n");
printf("\n"); printf("\n");
fflush(stdout); fflush(stdout);
} else if (skip_prefix(buf.buf, "stateless-connect ", &arg)) { } else if (skip_prefix(buf.buf, "stateless-connect ", &arg)) {

View File

@ -363,6 +363,7 @@ int send_pack(struct send_pack_args *args,
int atomic_supported = 0; int atomic_supported = 0;
int use_push_options = 0; int use_push_options = 0;
int push_options_supported = 0; int push_options_supported = 0;
int object_format_supported = 0;
unsigned cmds_sent = 0; unsigned cmds_sent = 0;
int ret; int ret;
struct async demux; struct async demux;
@ -389,6 +390,9 @@ int send_pack(struct send_pack_args *args,
if (server_supports("push-options")) if (server_supports("push-options"))
push_options_supported = 1; push_options_supported = 1;
if (!server_supports_hash(the_hash_algo->name, &object_format_supported))
die(_("the receiving end does not support this repository's hash algorithm"));
if (args->push_cert != SEND_PACK_PUSH_CERT_NEVER) { if (args->push_cert != SEND_PACK_PUSH_CERT_NEVER) {
int len; int len;
push_cert_nonce = server_feature_value("push-cert", &len); push_cert_nonce = server_feature_value("push-cert", &len);
@ -429,6 +433,8 @@ int send_pack(struct send_pack_args *args,
strbuf_addstr(&cap_buf, " atomic"); strbuf_addstr(&cap_buf, " atomic");
if (use_push_options) if (use_push_options)
strbuf_addstr(&cap_buf, " push-options"); strbuf_addstr(&cap_buf, " push-options");
if (object_format_supported)
strbuf_addf(&cap_buf, " object-format=%s", the_hash_algo->name);
if (agent_supported) if (agent_supported)
strbuf_addf(&cap_buf, " agent=%s", git_user_agent_sanitized()); strbuf_addf(&cap_buf, " agent=%s", git_user_agent_sanitized());

27
serve.c
View File

@ -22,6 +22,14 @@ static int agent_advertise(struct repository *r,
return 1; return 1;
} }
static int object_format_advertise(struct repository *r,
struct strbuf *value)
{
if (value)
strbuf_addstr(value, r->hash_algo->name);
return 1;
}
struct protocol_capability { struct protocol_capability {
/* /*
* The name of the capability. The server uses this name when * The name of the capability. The server uses this name when
@ -57,6 +65,7 @@ static struct protocol_capability capabilities[] = {
{ "ls-refs", always_advertise, ls_refs }, { "ls-refs", always_advertise, ls_refs },
{ "fetch", upload_pack_advertise, upload_pack_v2 }, { "fetch", upload_pack_advertise, upload_pack_v2 },
{ "server-option", always_advertise, NULL }, { "server-option", always_advertise, NULL },
{ "object-format", object_format_advertise, NULL },
}; };
static void advertise_capabilities(void) static void advertise_capabilities(void)
@ -153,6 +162,22 @@ int has_capability(const struct argv_array *keys, const char *capability,
return 0; return 0;
} }
static void check_algorithm(struct repository *r, struct argv_array *keys)
{
int client = GIT_HASH_SHA1, server = hash_algo_by_ptr(r->hash_algo);
const char *algo_name;
if (has_capability(keys, "object-format", &algo_name)) {
client = hash_algo_by_name(algo_name);
if (client == GIT_HASH_UNKNOWN)
die("unknown object format '%s'", algo_name);
}
if (client != server)
die("mismatched object format: server %s; client %s\n",
r->hash_algo->name, hash_algos[client].name);
}
enum request_state { enum request_state {
PROCESS_REQUEST_KEYS, PROCESS_REQUEST_KEYS,
PROCESS_REQUEST_DONE, PROCESS_REQUEST_DONE,
@ -225,6 +250,8 @@ static int process_request(void)
if (!command) if (!command)
die("no command requested"); die("no command requested");
check_algorithm(the_repository, &keys);
command->command(the_repository, &keys, &reader); command->command(the_repository, &keys, &reader);
argv_array_clear(&keys); argv_array_clear(&keys);

View File

@ -1308,6 +1308,7 @@ void check_repository_format(struct repository_format *fmt)
fmt = &repo_fmt; fmt = &repo_fmt;
check_repository_format_gently(get_git_dir(), fmt, NULL); check_repository_format_gently(get_git_dir(), fmt, NULL);
startup_info->have_repository = 1; startup_info->have_repository = 1;
repo_set_hash_algo(the_repository, fmt->hash_algo);
clear_repository_format(&repo_fmt); clear_repository_format(&repo_fmt);
} }

View File

@ -12,6 +12,9 @@ int cmd__oid_array(int argc, const char **argv)
{ {
struct oid_array array = OID_ARRAY_INIT; struct oid_array array = OID_ARRAY_INIT;
struct strbuf line = STRBUF_INIT; struct strbuf line = STRBUF_INIT;
int nongit_ok;
setup_git_directory_gently(&nongit_ok);
while (strbuf_getline(&line, stdin) != EOF) { while (strbuf_getline(&line, stdin) != EOF) {
const char *arg; const char *arg;

View File

@ -12,6 +12,7 @@ file_size () {
} }
test_expect_success setup ' test_expect_success setup '
test_oid_init &&
# clone does not allow us to pass core.bigfilethreshold to # clone does not allow us to pass core.bigfilethreshold to
# new repos, so set core.bigfilethreshold globally # new repos, so set core.bigfilethreshold globally
git config --global core.bigfilethreshold 200k && git config --global core.bigfilethreshold 200k &&
@ -64,7 +65,7 @@ test_expect_success 'add a large file or two' '
test $count = 1 && test $count = 1 &&
cnt=$(git show-index <"$idx" | wc -l) && cnt=$(git show-index <"$idx" | wc -l) &&
test $cnt = 2 && test $cnt = 2 &&
for l in .git/objects/??/?????????????????????????????????????? for l in .git/objects/$OIDPATH_REGEX
do do
test_path_is_file "$l" || continue test_path_is_file "$l" || continue
bad=t bad=t
@ -177,7 +178,8 @@ test_expect_success 'git-show a large file' '
test_expect_success 'index-pack' ' test_expect_success 'index-pack' '
git clone file://"$(pwd)"/.git foo && git clone file://"$(pwd)"/.git foo &&
GIT_DIR=non-existent git index-pack --strict --verify foo/.git/objects/pack/*.pack GIT_DIR=non-existent git index-pack --object-format=$(test_oid algo) \
--strict --verify foo/.git/objects/pack/*.pack
' '
test_expect_success 'repack' ' test_expect_success 'repack' '

View File

@ -8,6 +8,10 @@ test_description='Test repository version check'
. ./test-lib.sh . ./test-lib.sh
test_expect_success 'setup' ' test_expect_success 'setup' '
test_oid_cache <<-\EOF &&
version sha1:0
version sha256:1
EOF
cat >test.patch <<-\EOF && cat >test.patch <<-\EOF &&
diff --git a/test.txt b/test.txt diff --git a/test.txt b/test.txt
new file mode 100644 new file mode 100644
@ -23,7 +27,7 @@ test_expect_success 'setup' '
' '
test_expect_success 'gitdir selection on normal repos' ' test_expect_success 'gitdir selection on normal repos' '
echo 0 >expect && echo $(test_oid version) >expect &&
git config core.repositoryformatversion >actual && git config core.repositoryformatversion >actual &&
git -C test config core.repositoryformatversion >actual2 && git -C test config core.repositoryformatversion >actual2 &&
test_cmp expect actual && test_cmp expect actual &&

View File

@ -402,7 +402,7 @@ EOF
mv .git/config .git/config-saved mv .git/config .git/config-saved
test_expect_success 'git branch -m q q2 without config should succeed' ' test_expect_success SHA1 'git branch -m q q2 without config should succeed' '
git branch -m q q2 && git branch -m q q2 &&
git branch -m q2 q git branch -m q2 q
' '

View File

@ -12,7 +12,8 @@ TRASH=$(pwd)
test_expect_success \ test_expect_success \
'setup' \ 'setup' \
'rm -f .git/index* && 'test_oid_init &&
rm -f .git/index* &&
perl -e "print \"a\" x 4096;" > a && perl -e "print \"a\" x 4096;" > a &&
perl -e "print \"b\" x 4096;" > b && perl -e "print \"b\" x 4096;" > b &&
perl -e "print \"c\" x 4096;" > c && perl -e "print \"c\" x 4096;" > c &&
@ -412,18 +413,18 @@ test_expect_success 'set up pack for non-repo tests' '
' '
test_expect_success 'index-pack --stdin complains of non-repo' ' test_expect_success 'index-pack --stdin complains of non-repo' '
nongit test_must_fail git index-pack --stdin <foo.pack && nongit test_must_fail git index-pack --object-format=$(test_oid algo) --stdin <foo.pack &&
test_path_is_missing non-repo/.git test_path_is_missing non-repo/.git
' '
test_expect_success 'index-pack <pack> works in non-repo' ' test_expect_success 'index-pack <pack> works in non-repo' '
nongit git index-pack ../foo.pack && nongit git index-pack --object-format=$(test_oid algo) ../foo.pack &&
test_path_is_file foo.idx test_path_is_file foo.idx
' '
test_expect_success 'index-pack --strict <pack> works in non-repo' ' test_expect_success 'index-pack --strict <pack> works in non-repo' '
rm -f foo.idx && rm -f foo.idx &&
nongit git index-pack --strict ../foo.pack && nongit git index-pack --strict --object-format=$(test_oid algo) ../foo.pack &&
test_path_is_file foo.idx test_path_is_file foo.idx
' '

View File

@ -36,36 +36,36 @@ test_expect_success 'setup' '
git update-ref HEAD $commit git update-ref HEAD $commit
' '
test_expect_success \ test_expect_success 'pack-objects with index version 1' '
'pack-objects with index version 1' \ pack1=$(git pack-objects --index-version=1 test-1 <obj-list) &&
'pack1=$(git pack-objects --index-version=1 test-1 <obj-list) && git verify-pack -v "test-1-${pack1}.pack"
git verify-pack -v "test-1-${pack1}.pack"' '
test_expect_success \ test_expect_success 'pack-objects with index version 2' '
'pack-objects with index version 2' \ pack2=$(git pack-objects --index-version=2 test-2 <obj-list) &&
'pack2=$(git pack-objects --index-version=2 test-2 <obj-list) && git verify-pack -v "test-2-${pack2}.pack"
git verify-pack -v "test-2-${pack2}.pack"' '
test_expect_success \ test_expect_success 'both packs should be identical' '
'both packs should be identical' \ cmp "test-1-${pack1}.pack" "test-2-${pack2}.pack"
'cmp "test-1-${pack1}.pack" "test-2-${pack2}.pack"' '
test_expect_success \ test_expect_success 'index v1 and index v2 should be different' '
'index v1 and index v2 should be different' \ ! cmp "test-1-${pack1}.idx" "test-2-${pack2}.idx"
'! cmp "test-1-${pack1}.idx" "test-2-${pack2}.idx"' '
test_expect_success \ test_expect_success 'index-pack with index version 1' '
'index-pack with index version 1' \ git index-pack --index-version=1 -o 1.idx "test-1-${pack1}.pack"
'git index-pack --index-version=1 -o 1.idx "test-1-${pack1}.pack"' '
test_expect_success \ test_expect_success 'index-pack with index version 2' '
'index-pack with index version 2' \ git index-pack --index-version=2 -o 2.idx "test-1-${pack1}.pack"
'git index-pack --index-version=2 -o 2.idx "test-1-${pack1}.pack"' '
test_expect_success \ test_expect_success 'index-pack results should match pack-objects ones' '
'index-pack results should match pack-objects ones' \ cmp "test-1-${pack1}.idx" "1.idx" &&
'cmp "test-1-${pack1}.idx" "1.idx" && cmp "test-2-${pack2}.idx" "2.idx"
cmp "test-2-${pack2}.idx" "2.idx"' '
test_expect_success 'index-pack --verify on index version 1' ' test_expect_success 'index-pack --verify on index version 1' '
git index-pack --verify "test-1-${pack1}.pack" git index-pack --verify "test-1-${pack1}.pack"
@ -75,13 +75,13 @@ test_expect_success 'index-pack --verify on index version 2' '
git index-pack --verify "test-2-${pack2}.pack" git index-pack --verify "test-2-${pack2}.pack"
' '
test_expect_success \ test_expect_success 'pack-objects --index-version=2, is not accepted' '
'pack-objects --index-version=2, is not accepted' \ test_must_fail git pack-objects --index-version=2, test-3 <obj-list
'test_must_fail git pack-objects --index-version=2, test-3 <obj-list' '
test_expect_success \ test_expect_success 'index v2: force some 64-bit offsets with pack-objects' '
'index v2: force some 64-bit offsets with pack-objects' \ pack3=$(git pack-objects --index-version=2,0x40000 test-3 <obj-list)
'pack3=$(git pack-objects --index-version=2,0x40000 test-3 <obj-list)' '
if msg=$(git verify-pack -v "test-3-${pack3}.pack" 2>&1) || if msg=$(git verify-pack -v "test-3-${pack3}.pack" 2>&1) ||
! (echo "$msg" | grep "pack too large .* off_t") ! (echo "$msg" | grep "pack too large .* off_t")
@ -91,21 +91,21 @@ else
say "# skipping tests concerning 64-bit offsets" say "# skipping tests concerning 64-bit offsets"
fi fi
test_expect_success OFF64_T \ test_expect_success OFF64_T 'index v2: verify a pack with some 64-bit offsets' '
'index v2: verify a pack with some 64-bit offsets' \ git verify-pack -v "test-3-${pack3}.pack"
'git verify-pack -v "test-3-${pack3}.pack"' '
test_expect_success OFF64_T \ test_expect_success OFF64_T '64-bit offsets: should be different from previous index v2 results' '
'64-bit offsets: should be different from previous index v2 results' \ ! cmp "test-2-${pack2}.idx" "test-3-${pack3}.idx"
'! cmp "test-2-${pack2}.idx" "test-3-${pack3}.idx"' '
test_expect_success OFF64_T \ test_expect_success OFF64_T 'index v2: force some 64-bit offsets with index-pack' '
'index v2: force some 64-bit offsets with index-pack' \ git index-pack --index-version=2,0x40000 -o 3.idx "test-1-${pack1}.pack"
'git index-pack --index-version=2,0x40000 -o 3.idx "test-1-${pack1}.pack"' '
test_expect_success OFF64_T \ test_expect_success OFF64_T '64-bit offsets: index-pack result should match pack-objects one' '
'64-bit offsets: index-pack result should match pack-objects one' \ cmp "test-3-${pack3}.idx" "3.idx"
'cmp "test-3-${pack3}.idx" "3.idx"' '
test_expect_success OFF64_T 'index-pack --verify on 64-bit offset v2 (cheat)' ' test_expect_success OFF64_T 'index-pack --verify on 64-bit offset v2 (cheat)' '
# This cheats by knowing which lower offset should still be encoded # This cheats by knowing which lower offset should still be encoded
@ -142,17 +142,17 @@ index_obj_offset()
( read offs extra && echo "$offs" ) ( read offs extra && echo "$offs" )
} }
test_expect_success \ test_expect_success '[index v1] 1) stream pack to repository' '
'[index v1] 1) stream pack to repository' \ git index-pack --index-version=1 --stdin < "test-1-${pack1}.pack" &&
'git index-pack --index-version=1 --stdin < "test-1-${pack1}.pack" &&
git prune-packed && git prune-packed &&
git count-objects | ( read nr rest && test "$nr" -eq 1 ) && git count-objects | ( read nr rest && test "$nr" -eq 1 ) &&
cmp "test-1-${pack1}.pack" ".git/objects/pack/pack-${pack1}.pack" && cmp "test-1-${pack1}.pack" ".git/objects/pack/pack-${pack1}.pack" &&
cmp "test-1-${pack1}.idx" ".git/objects/pack/pack-${pack1}.idx"' cmp "test-1-${pack1}.idx" ".git/objects/pack/pack-${pack1}.idx"
'
test_expect_success \ test_expect_success \
'[index v1] 2) create a stealth corruption in a delta base reference' \ '[index v1] 2) create a stealth corruption in a delta base reference' '
'# This test assumes file_101 is a delta smaller than 16 bytes. # This test assumes file_101 is a delta smaller than 16 bytes.
# It should be against file_100 but we substitute its base for file_099 # It should be against file_100 but we substitute its base for file_099
sha1_101=$(git hash-object file_101) && sha1_101=$(git hash-object file_101) &&
sha1_099=$(git hash-object file_099) && sha1_099=$(git hash-object file_099) &&
@ -164,37 +164,41 @@ test_expect_success \
if=".git/objects/pack/pack-${pack1}.idx" \ if=".git/objects/pack/pack-${pack1}.idx" \
skip=$((4 + 256 * 4 + $nr_099 * recordsz)) \ skip=$((4 + 256 * 4 + $nr_099 * recordsz)) \
bs=1 count=$rawsz conv=notrunc && bs=1 count=$rawsz conv=notrunc &&
git cat-file blob $sha1_101 > file_101_foo1' git cat-file blob $sha1_101 > file_101_foo1
'
test_expect_success \ test_expect_success \
'[index v1] 3) corrupted delta happily returned wrong data' \ '[index v1] 3) corrupted delta happily returned wrong data' '
'test -f file_101_foo1 && ! cmp file_101 file_101_foo1' test -f file_101_foo1 && ! cmp file_101 file_101_foo1
'
test_expect_success \ test_expect_success \
'[index v1] 4) confirm that the pack is actually corrupted' \ '[index v1] 4) confirm that the pack is actually corrupted' '
'test_must_fail git fsck --full $commit' test_must_fail git fsck --full $commit
'
test_expect_success \ test_expect_success \
'[index v1] 5) pack-objects happily reuses corrupted data' \ '[index v1] 5) pack-objects happily reuses corrupted data' '
'pack4=$(git pack-objects test-4 <obj-list) && pack4=$(git pack-objects test-4 <obj-list) &&
test -f "test-4-${pack4}.pack"' test -f "test-4-${pack4}.pack"
'
test_expect_success \ test_expect_success '[index v1] 6) newly created pack is BAD !' '
'[index v1] 6) newly created pack is BAD !' \ test_must_fail git verify-pack -v "test-4-${pack4}.pack"
'test_must_fail git verify-pack -v "test-4-${pack4}.pack"' '
test_expect_success \ test_expect_success '[index v2] 1) stream pack to repository' '
'[index v2] 1) stream pack to repository' \ rm -f .git/objects/pack/* &&
'rm -f .git/objects/pack/* &&
git index-pack --index-version=2 --stdin < "test-1-${pack1}.pack" && git index-pack --index-version=2 --stdin < "test-1-${pack1}.pack" &&
git prune-packed && git prune-packed &&
git count-objects | ( read nr rest && test "$nr" -eq 1 ) && git count-objects | ( read nr rest && test "$nr" -eq 1 ) &&
cmp "test-1-${pack1}.pack" ".git/objects/pack/pack-${pack1}.pack" && cmp "test-1-${pack1}.pack" ".git/objects/pack/pack-${pack1}.pack" &&
cmp "test-2-${pack1}.idx" ".git/objects/pack/pack-${pack1}.idx"' cmp "test-2-${pack1}.idx" ".git/objects/pack/pack-${pack1}.idx"
'
test_expect_success \ test_expect_success \
'[index v2] 2) create a stealth corruption in a delta base reference' \ '[index v2] 2) create a stealth corruption in a delta base reference' '
'# This test assumes file_101 is a delta smaller than 16 bytes. # This test assumes file_101 is a delta smaller than 16 bytes.
# It should be against file_100 but we substitute its base for file_099 # It should be against file_100 but we substitute its base for file_099
sha1_101=$(git hash-object file_101) && sha1_101=$(git hash-object file_101) &&
sha1_099=$(git hash-object file_099) && sha1_099=$(git hash-object file_099) &&
@ -205,24 +209,28 @@ test_expect_success \
if=".git/objects/pack/pack-${pack1}.idx" \ if=".git/objects/pack/pack-${pack1}.idx" \
skip=$((8 + 256 * 4 + $nr_099 * rawsz)) \ skip=$((8 + 256 * 4 + $nr_099 * rawsz)) \
bs=1 count=$rawsz conv=notrunc && bs=1 count=$rawsz conv=notrunc &&
git cat-file blob $sha1_101 > file_101_foo2' git cat-file blob $sha1_101 > file_101_foo2
'
test_expect_success \ test_expect_success \
'[index v2] 3) corrupted delta happily returned wrong data' \ '[index v2] 3) corrupted delta happily returned wrong data' '
'test -f file_101_foo2 && ! cmp file_101 file_101_foo2' test -f file_101_foo2 && ! cmp file_101 file_101_foo2
'
test_expect_success \ test_expect_success \
'[index v2] 4) confirm that the pack is actually corrupted' \ '[index v2] 4) confirm that the pack is actually corrupted' '
'test_must_fail git fsck --full $commit' test_must_fail git fsck --full $commit
'
test_expect_success \ test_expect_success \
'[index v2] 5) pack-objects refuses to reuse corrupted data' \ '[index v2] 5) pack-objects refuses to reuse corrupted data' '
'test_must_fail git pack-objects test-5 <obj-list && test_must_fail git pack-objects test-5 <obj-list &&
test_must_fail git pack-objects --no-reuse-object test-6 <obj-list' test_must_fail git pack-objects --no-reuse-object test-6 <obj-list
'
test_expect_success \ test_expect_success \
'[index v2] 6) verify-pack detects CRC mismatch' \ '[index v2] 6) verify-pack detects CRC mismatch' '
'rm -f .git/objects/pack/* && rm -f .git/objects/pack/* &&
git index-pack --index-version=2 --stdin < "test-1-${pack1}.pack" && git index-pack --index-version=2 --stdin < "test-1-${pack1}.pack" &&
git verify-pack ".git/objects/pack/pack-${pack1}.pack" && git verify-pack ".git/objects/pack/pack-${pack1}.pack" &&
obj=$(git hash-object file_001) && obj=$(git hash-object file_001) &&

View File

@ -871,9 +871,10 @@ test_expect_success 'shallow since with commit graph and already-seen commit' '
GIT_PROTOCOL=version=2 git upload-pack . <<-EOF >/dev/null GIT_PROTOCOL=version=2 git upload-pack . <<-EOF >/dev/null
0012command=fetch 0012command=fetch
$(echo "object-format=$(test_oid algo)" | packetize)
00010013deepen-since 1 00010013deepen-since 1
0032want $(git rev-parse other) $(echo "want $(git rev-parse other)" | packetize)
0032have $(git rev-parse master) $(echo "have $(git rev-parse master)" | packetize)
0000 0000
EOF EOF
) )

View File

@ -50,6 +50,24 @@ test_expect_success 'create password-protected repository' '
"$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/repo.git" "$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/repo.git"
' '
test_expect_success 'create empty remote repository' '
git init --bare "$HTTPD_DOCUMENT_ROOT_PATH/empty.git" &&
(cd "$HTTPD_DOCUMENT_ROOT_PATH/empty.git" &&
mkdir -p hooks &&
write_script "hooks/post-update" <<-\EOF &&
exec git update-server-info
EOF
hooks/post-update
)
'
test_expect_success 'empty dumb HTTP repository has default hash algorithm' '
test_when_finished "rm -fr clone-empty" &&
git clone $HTTPD_URL/dumb/empty.git clone-empty &&
git -C clone-empty rev-parse --show-object-format >empty-format &&
test "$(cat empty-format)" = "$(test_oid algo)"
'
setup_askpass_helper setup_askpass_helper
test_expect_success 'cloning password-protected repository can fail' ' test_expect_success 'cloning password-protected repository can fail' '

View File

@ -46,6 +46,7 @@ ssize_b100dots() {
} }
test_expect_success 'setup' ' test_expect_success 'setup' '
test_oid_init &&
HTTP_CONTENT_ENCODING="identity" && HTTP_CONTENT_ENCODING="identity" &&
export HTTP_CONTENT_ENCODING && export HTTP_CONTENT_ENCODING &&
git config http.receivepack true && git config http.receivepack true &&
@ -62,8 +63,8 @@ test_expect_success 'setup' '
test_copy_bytes 10 <fetch_body >fetch_body.trunc && test_copy_bytes 10 <fetch_body >fetch_body.trunc &&
hash_next=$(git commit-tree -p HEAD -m next HEAD^{tree}) && hash_next=$(git commit-tree -p HEAD -m next HEAD^{tree}) &&
{ {
printf "%s %s refs/heads/newbranch\\0report-status\\n" \ printf "%s %s refs/heads/newbranch\\0report-status object-format=%s\\n" \
"$ZERO_OID" "$hash_next" | packetize && "$ZERO_OID" "$hash_next" "$(test_oid algo)" | packetize &&
printf 0000 && printf 0000 &&
echo "$hash_next" | git pack-objects --stdout echo "$hash_next" | git pack-objects --stdout
} >push_body && } >push_body &&

View File

@ -5,12 +5,17 @@ test_description='test protocol v2 server commands'
. ./test-lib.sh . ./test-lib.sh
test_expect_success 'test capability advertisement' ' test_expect_success 'test capability advertisement' '
test_oid_cache <<-EOF &&
wrong_algo sha1:sha256
wrong_algo sha256:sha1
EOF
cat >expect <<-EOF && cat >expect <<-EOF &&
version 2 version 2
agent=git/$(git version | cut -d" " -f3) agent=git/$(git version | cut -d" " -f3)
ls-refs ls-refs
fetch=shallow fetch=shallow
server-option server-option
object-format=$(test_oid algo)
0000 0000
EOF EOF
@ -45,6 +50,7 @@ test_expect_success 'request invalid capability' '
test_expect_success 'request with no command' ' test_expect_success 'request with no command' '
test-tool pkt-line pack >in <<-EOF && test-tool pkt-line pack >in <<-EOF &&
agent=git/test agent=git/test
object-format=$(test_oid algo)
0000 0000
EOF EOF
test_must_fail test-tool serve-v2 --stateless-rpc 2>err <in && test_must_fail test-tool serve-v2 --stateless-rpc 2>err <in &&
@ -54,6 +60,7 @@ test_expect_success 'request with no command' '
test_expect_success 'request invalid command' ' test_expect_success 'request invalid command' '
test-tool pkt-line pack >in <<-EOF && test-tool pkt-line pack >in <<-EOF &&
command=foo command=foo
object-format=$(test_oid algo)
agent=git/test agent=git/test
0000 0000
EOF EOF
@ -61,6 +68,17 @@ test_expect_success 'request invalid command' '
test_i18ngrep "invalid command" err test_i18ngrep "invalid command" err
' '
test_expect_success 'wrong object-format' '
test-tool pkt-line pack >in <<-EOF &&
command=fetch
agent=git/test
object-format=$(test_oid wrong_algo)
0000
EOF
test_must_fail test-tool serve-v2 --stateless-rpc 2>err <in &&
test_i18ngrep "mismatched object format" err
'
# Test the basics of ls-refs # Test the basics of ls-refs
# #
test_expect_success 'setup some refs and tags' ' test_expect_success 'setup some refs and tags' '
@ -74,6 +92,7 @@ test_expect_success 'setup some refs and tags' '
test_expect_success 'basics of ls-refs' ' test_expect_success 'basics of ls-refs' '
test-tool pkt-line pack >in <<-EOF && test-tool pkt-line pack >in <<-EOF &&
command=ls-refs command=ls-refs
object-format=$(test_oid algo)
0000 0000
EOF EOF
@ -96,6 +115,7 @@ test_expect_success 'basics of ls-refs' '
test_expect_success 'basic ref-prefixes' ' test_expect_success 'basic ref-prefixes' '
test-tool pkt-line pack >in <<-EOF && test-tool pkt-line pack >in <<-EOF &&
command=ls-refs command=ls-refs
object-format=$(test_oid algo)
0001 0001
ref-prefix refs/heads/master ref-prefix refs/heads/master
ref-prefix refs/tags/one ref-prefix refs/tags/one
@ -116,6 +136,7 @@ test_expect_success 'basic ref-prefixes' '
test_expect_success 'refs/heads prefix' ' test_expect_success 'refs/heads prefix' '
test-tool pkt-line pack >in <<-EOF && test-tool pkt-line pack >in <<-EOF &&
command=ls-refs command=ls-refs
object-format=$(test_oid algo)
0001 0001
ref-prefix refs/heads/ ref-prefix refs/heads/
0000 0000
@ -136,6 +157,7 @@ test_expect_success 'refs/heads prefix' '
test_expect_success 'peel parameter' ' test_expect_success 'peel parameter' '
test-tool pkt-line pack >in <<-EOF && test-tool pkt-line pack >in <<-EOF &&
command=ls-refs command=ls-refs
object-format=$(test_oid algo)
0001 0001
peel peel
ref-prefix refs/tags/ ref-prefix refs/tags/
@ -157,6 +179,7 @@ test_expect_success 'peel parameter' '
test_expect_success 'symrefs parameter' ' test_expect_success 'symrefs parameter' '
test-tool pkt-line pack >in <<-EOF && test-tool pkt-line pack >in <<-EOF &&
command=ls-refs command=ls-refs
object-format=$(test_oid algo)
0001 0001
symrefs symrefs
ref-prefix refs/heads/ ref-prefix refs/heads/
@ -178,6 +201,7 @@ test_expect_success 'symrefs parameter' '
test_expect_success 'sending server-options' ' test_expect_success 'sending server-options' '
test-tool pkt-line pack >in <<-EOF && test-tool pkt-line pack >in <<-EOF &&
command=ls-refs command=ls-refs
object-format=$(test_oid algo)
server-option=hello server-option=hello
server-option=world server-option=world
0001 0001
@ -200,6 +224,7 @@ test_expect_success 'unexpected lines are not allowed in fetch request' '
test-tool pkt-line pack >in <<-EOF && test-tool pkt-line pack >in <<-EOF &&
command=fetch command=fetch
object-format=$(test_oid algo)
0001 0001
this-is-not-a-command this-is-not-a-command
0000 0000

View File

@ -13,6 +13,7 @@ start_git_daemon --export-all --enable=receive-pack
daemon_parent=$GIT_DAEMON_DOCUMENT_ROOT_PATH/parent daemon_parent=$GIT_DAEMON_DOCUMENT_ROOT_PATH/parent
test_expect_success 'create repo to be served by git-daemon' ' test_expect_success 'create repo to be served by git-daemon' '
test_oid_init &&
git init "$daemon_parent" && git init "$daemon_parent" &&
test_commit -C "$daemon_parent" one test_commit -C "$daemon_parent" one
' '
@ -393,6 +394,7 @@ test_expect_success 'even with handcrafted request, filter does not work if not
# Custom request that tries to filter even though it is not advertised. # Custom request that tries to filter even though it is not advertised.
test-tool pkt-line pack >in <<-EOF && test-tool pkt-line pack >in <<-EOF &&
command=fetch command=fetch
object-format=$(test_oid algo)
0001 0001
want $(git -C server rev-parse master) want $(git -C server rev-parse master)
filter blob:none filter blob:none

View File

@ -27,6 +27,15 @@ check_output () {
test_cmp sorted_commits actual_commits test_cmp sorted_commits actual_commits
} }
write_command () {
echo "command=$1"
if test "$(test_oid algo)" != sha1
then
echo "object-format=$(test_oid algo)"
fi
}
# c(o/foo) d(o/bar) # c(o/foo) d(o/bar)
# \ / # \ /
# b e(baz) f(master) # b e(baz) f(master)
@ -65,7 +74,7 @@ test_expect_success 'config controls ref-in-want advertisement' '
test_expect_success 'invalid want-ref line' ' test_expect_success 'invalid want-ref line' '
test-tool pkt-line pack >in <<-EOF && test-tool pkt-line pack >in <<-EOF &&
command=fetch $(write_command fetch)
0001 0001
no-progress no-progress
want-ref refs/heads/non-existent want-ref refs/heads/non-existent
@ -86,7 +95,7 @@ test_expect_success 'basic want-ref' '
oid=$(git rev-parse a) && oid=$(git rev-parse a) &&
test-tool pkt-line pack >in <<-EOF && test-tool pkt-line pack >in <<-EOF &&
command=fetch $(write_command fetch)
0001 0001
no-progress no-progress
want-ref refs/heads/master want-ref refs/heads/master
@ -110,7 +119,7 @@ test_expect_success 'multiple want-ref lines' '
oid=$(git rev-parse b) && oid=$(git rev-parse b) &&
test-tool pkt-line pack >in <<-EOF && test-tool pkt-line pack >in <<-EOF &&
command=fetch $(write_command fetch)
0001 0001
no-progress no-progress
want-ref refs/heads/o/foo want-ref refs/heads/o/foo
@ -132,7 +141,7 @@ test_expect_success 'mix want and want-ref' '
git rev-parse e f >expected_commits && git rev-parse e f >expected_commits &&
test-tool pkt-line pack >in <<-EOF && test-tool pkt-line pack >in <<-EOF &&
command=fetch $(write_command fetch)
0001 0001
no-progress no-progress
want-ref refs/heads/master want-ref refs/heads/master
@ -155,7 +164,7 @@ test_expect_success 'want-ref with ref we already have commit for' '
oid=$(git rev-parse c) && oid=$(git rev-parse c) &&
test-tool pkt-line pack >in <<-EOF && test-tool pkt-line pack >in <<-EOF &&
command=fetch $(write_command fetch)
0001 0001
no-progress no-progress
want-ref refs/heads/o/foo want-ref refs/heads/o/foo

View File

@ -9,6 +9,7 @@ making sure that we do not segfault or otherwise behave badly.'
test_expect_success 'extra delim packet in v2 ls-refs args' ' test_expect_success 'extra delim packet in v2 ls-refs args' '
{ {
packetize command=ls-refs && packetize command=ls-refs &&
packetize "object-format=$(test_oid algo)" &&
printf 0001 && printf 0001 &&
# protocol expects 0000 flush here # protocol expects 0000 flush here
printf 0001 printf 0001
@ -21,6 +22,7 @@ test_expect_success 'extra delim packet in v2 ls-refs args' '
test_expect_success 'extra delim packet in v2 fetch args' ' test_expect_success 'extra delim packet in v2 fetch args' '
{ {
packetize command=fetch && packetize command=fetch &&
packetize "object-format=$(test_oid algo)" &&
printf 0001 && printf 0001 &&
# protocol expects 0000 flush here # protocol expects 0000 flush here
printf 0001 printf 0001

View File

@ -52,9 +52,11 @@ do
test -n "$GIT_REMOTE_TESTGIT_SIGNED_TAGS" && echo "signed-tags" test -n "$GIT_REMOTE_TESTGIT_SIGNED_TAGS" && echo "signed-tags"
test -n "$GIT_REMOTE_TESTGIT_NO_PRIVATE_UPDATE" && echo "no-private-update" test -n "$GIT_REMOTE_TESTGIT_NO_PRIVATE_UPDATE" && echo "no-private-update"
echo 'option' echo 'option'
echo 'object-format'
echo echo
;; ;;
list) list)
echo ":object-format $(git rev-parse --show-object-format=storage)"
git for-each-ref --format='? %(refname)' 'refs/heads/' 'refs/tags/' git for-each-ref --format='? %(refname)' 'refs/heads/' 'refs/tags/'
head=$(git symbolic-ref HEAD) head=$(git symbolic-ref HEAD)
echo "@$head HEAD" echo "@$head HEAD"
@ -139,6 +141,10 @@ do
test $val = "true" && force="true" || force= test $val = "true" && force="true" || force=
echo "ok" echo "ok"
;; ;;
object-format)
test $val = "true" && object_format="true" || object_format=
echo "ok"
;;
*) *)
echo "unsupported" echo "unsupported"
;; ;;

View File

@ -1414,6 +1414,7 @@ test_oid_init
ZERO_OID=$(test_oid zero) ZERO_OID=$(test_oid zero)
OID_REGEX=$(echo $ZERO_OID | sed -e 's/0/[0-9a-f]/g') OID_REGEX=$(echo $ZERO_OID | sed -e 's/0/[0-9a-f]/g')
OIDPATH_REGEX=$(test_oid_to_path $ZERO_OID | sed -e 's/0/[0-9a-f]/g')
EMPTY_TREE=$(test_oid empty_tree) EMPTY_TREE=$(test_oid empty_tree)
EMPTY_BLOB=$(test_oid empty_blob) EMPTY_BLOB=$(test_oid empty_blob)
_z40=$ZERO_OID _z40=$ZERO_OID

View File

@ -32,7 +32,8 @@ struct helper_data {
signed_tags : 1, signed_tags : 1,
check_connectivity : 1, check_connectivity : 1,
no_disconnect_req : 1, no_disconnect_req : 1,
no_private_update : 1; no_private_update : 1,
object_format : 1;
/* /*
* As an optimization, the transport code may invoke fetch before * As an optimization, the transport code may invoke fetch before
@ -207,6 +208,8 @@ static struct child_process *get_helper(struct transport *transport)
data->import_marks = xstrdup(arg); data->import_marks = xstrdup(arg);
} else if (starts_with(capname, "no-private-update")) { } else if (starts_with(capname, "no-private-update")) {
data->no_private_update = 1; data->no_private_update = 1;
} else if (starts_with(capname, "object-format")) {
data->object_format = 1;
} else if (mandatory) { } else if (mandatory) {
die(_("unknown mandatory capability %s; this remote " die(_("unknown mandatory capability %s; this remote "
"helper probably needs newer version of Git"), "helper probably needs newer version of Git"),
@ -1104,6 +1107,12 @@ static struct ref *get_refs_list_using_list(struct transport *transport,
data->get_refs_list_called = 1; data->get_refs_list_called = 1;
helper = get_helper(transport); helper = get_helper(transport);
if (data->object_format) {
write_str_in_full(helper->in, "option object-format\n");
if (recvline(data, &buf) || strcmp(buf.buf, "ok"))
exit(128);
}
if (data->push && for_push) if (data->push && for_push)
write_str_in_full(helper->in, "list for-push\n"); write_str_in_full(helper->in, "list for-push\n");
else else
@ -1116,6 +1125,17 @@ static struct ref *get_refs_list_using_list(struct transport *transport,
if (!*buf.buf) if (!*buf.buf)
break; break;
else if (buf.buf[0] == ':') {
const char *value;
if (skip_prefix(buf.buf, ":object-format ", &value)) {
int algo = hash_algo_by_name(value);
if (algo == GIT_HASH_UNKNOWN)
die(_("unsupported object format '%s'"),
value);
transport->hash_algo = &hash_algos[algo];
}
continue;
}
eov = strchr(buf.buf, ' '); eov = strchr(buf.buf, ' ');
if (!eov) if (!eov)
@ -1128,7 +1148,7 @@ static struct ref *get_refs_list_using_list(struct transport *transport,
if (buf.buf[0] == '@') if (buf.buf[0] == '@')
(*tail)->symref = xstrdup(buf.buf + 1); (*tail)->symref = xstrdup(buf.buf + 1);
else if (buf.buf[0] != '?') else if (buf.buf[0] != '?')
get_oid_hex(buf.buf, &(*tail)->old_oid); get_oid_hex_algop(buf.buf, &(*tail)->old_oid, transport->hash_algo);
if (eon) { if (eon) {
if (has_attribute(eon + 1, "unchanged")) { if (has_attribute(eon + 1, "unchanged")) {
(*tail)->status |= REF_STATUS_UPTODATE; (*tail)->status |= REF_STATUS_UPTODATE;

View File

@ -143,6 +143,9 @@ static struct ref *get_refs_from_bundle(struct transport *transport,
data->fd = read_bundle_header(transport->url, &data->header); data->fd = read_bundle_header(transport->url, &data->header);
if (data->fd < 0) if (data->fd < 0)
die(_("could not read bundle '%s'"), transport->url); die(_("could not read bundle '%s'"), transport->url);
transport->hash_algo = data->header.hash_algo;
for (i = 0; i < data->header.references.nr; i++) { for (i = 0; i < data->header.references.nr; i++) {
struct ref_list_entry *e = data->header.references.list + i; struct ref_list_entry *e = data->header.references.list + i;
struct ref *ref = alloc_ref(e->name); struct ref *ref = alloc_ref(e->name);
@ -157,11 +160,14 @@ static int fetch_refs_from_bundle(struct transport *transport,
int nr_heads, struct ref **to_fetch) int nr_heads, struct ref **to_fetch)
{ {
struct bundle_transport_data *data = transport->data; struct bundle_transport_data *data = transport->data;
int ret;
if (!data->get_refs_from_bundle_called) if (!data->get_refs_from_bundle_called)
get_refs_from_bundle(transport, 0, NULL); get_refs_from_bundle(transport, 0, NULL);
return unbundle(the_repository, &data->header, data->fd, ret = unbundle(the_repository, &data->header, data->fd,
transport->progress ? BUNDLE_VERBOSE : 0); transport->progress ? BUNDLE_VERBOSE : 0);
transport->hash_algo = data->header.hash_algo;
return ret;
} }
static int close_bundle(struct transport *transport) static int close_bundle(struct transport *transport)
@ -312,6 +318,7 @@ static struct ref *handshake(struct transport *transport, int for_push,
BUG("unknown protocol version"); BUG("unknown protocol version");
} }
data->got_remote_heads = 1; data->got_remote_heads = 1;
transport->hash_algo = reader.hash_algo;
if (reader.line_peeked) if (reader.line_peeked)
BUG("buffer must be empty at the end of handshake()"); BUG("buffer must be empty at the end of handshake()");
@ -989,9 +996,16 @@ struct transport *transport_get(struct remote *remote, const char *url)
ret->smart_options->receivepack = remote->receivepack; ret->smart_options->receivepack = remote->receivepack;
} }
ret->hash_algo = &hash_algos[GIT_HASH_SHA1];
return ret; return ret;
} }
const struct git_hash_algo *transport_get_hash_algo(struct transport *transport)
{
return transport->hash_algo;
}
int transport_set_option(struct transport *transport, int transport_set_option(struct transport *transport,
const char *name, const char *value) const char *name, const char *value)
{ {

View File

@ -115,6 +115,8 @@ struct transport {
struct git_transport_options *smart_options; struct git_transport_options *smart_options;
enum transport_family family; enum transport_family family;
const struct git_hash_algo *hash_algo;
}; };
#define TRANSPORT_PUSH_ALL (1<<0) #define TRANSPORT_PUSH_ALL (1<<0)
@ -243,6 +245,12 @@ int transport_push(struct repository *repo,
const struct ref *transport_get_remote_refs(struct transport *transport, const struct ref *transport_get_remote_refs(struct transport *transport,
const struct argv_array *ref_prefixes); const struct argv_array *ref_prefixes);
/*
* Fetch the hash algorithm used by a remote.
*
* This can only be called after fetching the remote refs.
*/
const struct git_hash_algo *transport_get_hash_algo(struct transport *transport);
int transport_fetch_refs(struct transport *transport, struct ref *refs); int transport_fetch_refs(struct transport *transport, struct ref *refs);
void transport_unlock_pack(struct transport *transport); void transport_unlock_pack(struct transport *transport);
int transport_disconnect(struct transport *transport); int transport_disconnect(struct transport *transport);

View File

@ -1132,7 +1132,7 @@ static int send_ref(const char *refname, const struct object_id *oid,
struct strbuf symref_info = STRBUF_INIT; struct strbuf symref_info = STRBUF_INIT;
format_symref_info(&symref_info, &data->symref); format_symref_info(&symref_info, &data->symref);
packet_write_fmt(1, "%s %s%c%s%s%s%s%s%s agent=%s\n", packet_write_fmt(1, "%s %s%c%s%s%s%s%s%s object-format=%s agent=%s\n",
oid_to_hex(oid), refname_nons, oid_to_hex(oid), refname_nons,
0, capabilities, 0, capabilities,
(data->allow_uor & ALLOW_TIP_SHA1) ? (data->allow_uor & ALLOW_TIP_SHA1) ?
@ -1142,6 +1142,7 @@ static int send_ref(const char *refname, const struct object_id *oid,
data->stateless_rpc ? " no-done" : "", data->stateless_rpc ? " no-done" : "",
symref_info.buf, symref_info.buf,
data->allow_filter ? " filter" : "", data->allow_filter ? " filter" : "",
the_hash_algo->name,
git_user_agent_sanitized()); git_user_agent_sanitized());
strbuf_release(&symref_info); strbuf_release(&symref_info);
} else { } else {

View File

@ -105,6 +105,14 @@ char *xstrndup(const char *str, size_t len)
return xmemdupz(str, p ? p - str : len); return xmemdupz(str, p ? p - str : len);
} }
int xstrncmpz(const char *s, const char *t, size_t len)
{
int res = strncmp(s, t, len);
if (res)
return res;
return s[len] == '\0' ? 0 : 1;
}
void *xrealloc(void *ptr, size_t size) void *xrealloc(void *ptr, size_t size)
{ {
void *ret; void *ret;