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:
commit
12210859da
@ -93,6 +93,14 @@ OPTIONS
|
||||
--max-input-size=<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
|
||||
-----
|
||||
|
||||
|
@ -9,7 +9,7 @@ git-show-index - Show packed archive index
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'git show-index'
|
||||
'git show-index' [--object-format=<hash-algorithm>]
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
---
|
||||
Part of the linkgit:git[1] suite
|
||||
|
@ -238,6 +238,9 @@ the remote repository.
|
||||
`--signed-tags=verbatim` to linkgit:git-fast-export[1]. In the
|
||||
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
|
||||
@ -257,12 +260,14 @@ Support for this command is mandatory.
|
||||
'list'::
|
||||
Lists the refs, one per line, in the format "<value> <name>
|
||||
[<attr> ...]". The value may be a hex sha1 hash, "@<dest>" for
|
||||
a symref, or "?" to indicate that the helper could not get the
|
||||
value of the ref. A space-separated list of attributes follows
|
||||
the name; unrecognized attributes are ignored. The list ends
|
||||
with a blank line.
|
||||
a symref, ":<keyword> <value>" for a key-value pair, or
|
||||
"?" to indicate that the helper could not get the value of the
|
||||
ref. A space-separated list of attributes follows the name;
|
||||
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 KEYWORDS for a list of currently defined keywords.
|
||||
+
|
||||
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
|
||||
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
|
||||
-------
|
||||
|
||||
@ -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
|
||||
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
|
||||
--------
|
||||
linkgit:git-remote[1]
|
||||
|
@ -176,6 +176,21 @@ agent strings are purely informative for statistics and debugging
|
||||
purposes, and MUST NOT be used to programmatically assume the presence
|
||||
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
|
||||
------
|
||||
|
||||
|
@ -483,3 +483,12 @@ included in a request. This is done by sending each option as a
|
||||
a request.
|
||||
|
||||
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.
|
||||
|
@ -1220,6 +1220,15 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
|
||||
refs = transport_get_remote_refs(transport, &ref_prefixes);
|
||||
|
||||
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);
|
||||
/*
|
||||
* transport_get_remote_refs() may return refs with null sha-1
|
||||
|
@ -1555,13 +1555,9 @@ static void read_v2_anomalous_offsets(struct packed_git *p,
|
||||
{
|
||||
const uint32_t *idx1, *idx2;
|
||||
uint32_t i;
|
||||
const uint32_t hashwords = the_hash_algo->rawsz / sizeof(uint32_t);
|
||||
|
||||
/* The address of the 4-byte offset table */
|
||||
idx1 = (((const uint32_t *)p->index_data)
|
||||
+ 2 /* 8-byte header */
|
||||
+ 256 /* fan out */
|
||||
+ hashwords * p->num_objects /* object ID table */
|
||||
idx1 = (((const uint32_t *)((const uint8_t *)p->index_data + p->crc_offset))
|
||||
+ 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 foreign_nr = 1; /* zero is a "good" value, assume bad */
|
||||
int report_end_of_input = 0;
|
||||
int hash_algo = 0;
|
||||
|
||||
/*
|
||||
* 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);
|
||||
} else if (skip_prefix(arg, "--max-input-size=", &arg)) {
|
||||
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
|
||||
usage(index_pack_usage);
|
||||
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"));
|
||||
if (from_stdin && !startup_info->have_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)
|
||||
index_name = derive_filename(pack_name, "idx", &index_name_buf);
|
||||
|
||||
|
@ -118,6 +118,10 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
|
||||
transport->server_options = &server_options;
|
||||
|
||||
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)) {
|
||||
UNLEAK(sorting);
|
||||
return 1;
|
||||
|
@ -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);
|
||||
if (advertise_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());
|
||||
packet_write_fmt(1, "%s %s%c%s\n",
|
||||
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);
|
||||
if (linelen < reader->pktlen) {
|
||||
const char *feature_list = reader->line + linelen + 1;
|
||||
const char *hash = NULL;
|
||||
int len = 0;
|
||||
if (parse_feature_request(feature_list, "report-status"))
|
||||
report_status = 1;
|
||||
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
|
||||
&& parse_feature_request(feature_list, "push-options"))
|
||||
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")) {
|
||||
|
@ -1,9 +1,12 @@
|
||||
#include "builtin.h"
|
||||
#include "cache.h"
|
||||
#include "pack.h"
|
||||
#include "parse-options.h"
|
||||
|
||||
static const char show_index_usage[] =
|
||||
"git show-index";
|
||||
static const char *const show_index_usage[] = {
|
||||
"git show-index [--object-format=<hash-algorithm>]",
|
||||
NULL
|
||||
};
|
||||
|
||||
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 int version;
|
||||
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)
|
||||
die("unable to read header");
|
||||
if (top_index[0] == htonl(PACK_IDX_SIGNATURE)) {
|
||||
|
22
bundle.c
22
bundle.c
@ -23,6 +23,17 @@ static void add_to_ref_list(const struct object_id *oid, const char *name,
|
||||
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,
|
||||
const char *report_path)
|
||||
{
|
||||
@ -52,12 +63,21 @@ static int parse_bundle_header(int fd, struct bundle_header *header,
|
||||
}
|
||||
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.
|
||||
* Prerequisites have object name that is optionally
|
||||
* 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)) ||
|
||||
(!is_prereq && !*p)) {
|
||||
if (report_path)
|
||||
|
1
bundle.h
1
bundle.h
@ -15,6 +15,7 @@ struct ref_list {
|
||||
struct bundle_header {
|
||||
struct ref_list prerequisites;
|
||||
struct ref_list references;
|
||||
const struct git_hash_algo *hash_algo;
|
||||
};
|
||||
|
||||
int is_bundle(const char *path, int quiet);
|
||||
|
138
connect.c
138
connect.c
@ -18,7 +18,7 @@
|
||||
|
||||
static char *server_capabilities_v1;
|
||||
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)
|
||||
{
|
||||
@ -83,6 +83,21 @@ int server_supports_v2(const char *c, int die_on_error)
|
||||
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 die_on_error)
|
||||
{
|
||||
@ -181,17 +196,16 @@ reject:
|
||||
static void annotate_refs_with_symref_info(struct ref *ref)
|
||||
{
|
||||
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;
|
||||
const char *val;
|
||||
|
||||
val = parse_feature_value(feature_list, "symref", &len);
|
||||
val = next_server_feature_value("symref", &len, &offset);
|
||||
if (!val)
|
||||
break;
|
||||
parse_one_symref_info(&symref, val, len);
|
||||
feature_list = val + 1;
|
||||
}
|
||||
string_list_sort(&symref);
|
||||
|
||||
@ -205,21 +219,36 @@ static void annotate_refs_with_symref_info(struct ref *ref)
|
||||
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);
|
||||
if (nul_location == *len)
|
||||
if (nul_location == *linelen)
|
||||
return;
|
||||
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;
|
||||
const char *name;
|
||||
|
||||
if (parse_oid_hex(line, &oid, &name))
|
||||
if (parse_oid_hex_algop(line, &oid, &name, reader->hash_algo))
|
||||
return 0;
|
||||
if (*name != ' ')
|
||||
return 0;
|
||||
@ -235,13 +264,15 @@ static void check_no_capabilities(const char *line, int len)
|
||||
line + strlen(line));
|
||||
}
|
||||
|
||||
static int process_ref(const char *line, int len, struct ref ***list,
|
||||
unsigned int flags, struct oid_array *extra_have)
|
||||
static int process_ref(const struct packet_reader *reader, int len,
|
||||
struct ref ***list, unsigned int flags,
|
||||
struct oid_array *extra_have)
|
||||
{
|
||||
const char *line = reader->line;
|
||||
struct object_id old_oid;
|
||||
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;
|
||||
if (*name != ' ')
|
||||
return 0;
|
||||
@ -261,16 +292,17 @@ static int process_ref(const char *line, int len, struct ref ***list,
|
||||
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)
|
||||
{
|
||||
const char *line = reader->line;
|
||||
const char *arg;
|
||||
struct object_id old_oid;
|
||||
|
||||
if (!skip_prefix(line, "shallow ", &arg))
|
||||
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);
|
||||
if (!shallow_points)
|
||||
die(_("repository on the other end cannot be shallow"));
|
||||
@ -317,20 +349,20 @@ struct ref **get_remote_heads(struct packet_reader *reader,
|
||||
|
||||
switch (state) {
|
||||
case EXPECTING_FIRST_REF:
|
||||
process_capabilities(reader->line, &len);
|
||||
if (process_dummy_ref(reader->line)) {
|
||||
process_capabilities(reader, &len);
|
||||
if (process_dummy_ref(reader)) {
|
||||
state = EXPECTING_SHALLOW;
|
||||
break;
|
||||
}
|
||||
state = EXPECTING_REF;
|
||||
/* fallthrough */
|
||||
case EXPECTING_REF:
|
||||
if (process_ref(reader->line, len, &list, flags, extra_have))
|
||||
if (process_ref(reader, len, &list, flags, extra_have))
|
||||
break;
|
||||
state = EXPECTING_SHALLOW;
|
||||
/* fallthrough */
|
||||
case EXPECTING_SHALLOW:
|
||||
if (process_shallow(reader->line, len, shallow_points))
|
||||
if (process_shallow(reader, len, shallow_points))
|
||||
break;
|
||||
die(_("protocol error: unexpected '%s'"), reader->line);
|
||||
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 */
|
||||
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 i = 0;
|
||||
@ -352,6 +384,7 @@ static int process_ref_v2(const char *line, struct ref ***list)
|
||||
struct ref *ref;
|
||||
struct string_list line_sections = STRING_LIST_INIT_DUP;
|
||||
const char *end;
|
||||
const char *line = reader->line;
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
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) {
|
||||
ret = 0;
|
||||
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);
|
||||
|
||||
oidcpy(&ref->old_oid, &old_oid);
|
||||
memcpy(ref->old_oid.hash, old_oid.hash, reader->hash_algo->rawsz);
|
||||
**list = ref;
|
||||
*list = &ref->next;
|
||||
|
||||
@ -385,7 +418,8 @@ static int process_ref_v2(const char *line, struct ref ***list)
|
||||
struct object_id peeled_oid;
|
||||
char *peeled_name;
|
||||
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;
|
||||
goto out;
|
||||
}
|
||||
@ -393,7 +427,8 @@ static int process_ref_v2(const char *line, struct ref ***list)
|
||||
peeled_name = xstrfmt("%s^{}", ref->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->next;
|
||||
|
||||
@ -423,6 +458,7 @@ struct ref **get_remote_refs(int fd_out, struct packet_reader *reader,
|
||||
int stateless_rpc)
|
||||
{
|
||||
int i;
|
||||
const char *hash_name;
|
||||
*list = NULL;
|
||||
|
||||
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))
|
||||
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 &&
|
||||
server_supports_v2("server-option", 1))
|
||||
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 */
|
||||
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);
|
||||
}
|
||||
|
||||
@ -463,7 +509,7 @@ struct ref **get_remote_refs(int fd_out, struct packet_reader *reader,
|
||||
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;
|
||||
|
||||
@ -471,6 +517,8 @@ static const char *parse_feature_value(const char *feature_list, const char *fea
|
||||
return NULL;
|
||||
|
||||
len = strlen(feature);
|
||||
if (offset)
|
||||
feature_list += *offset;
|
||||
while (*feature_list) {
|
||||
const char *found = strstr(feature_list, feature);
|
||||
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") */
|
||||
else if (*value == '=') {
|
||||
int end;
|
||||
|
||||
value++;
|
||||
end = strcspn(value, " \t\n");
|
||||
if (lenp)
|
||||
*lenp = strcspn(value, " \t\n");
|
||||
*lenp = end;
|
||||
if (offset)
|
||||
*offset = value + end - feature_list;
|
||||
return value;
|
||||
}
|
||||
/*
|
||||
@ -500,14 +553,41 @@ static const char *parse_feature_value(const char *feature_list, const char *fea
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
|
@ -18,7 +18,10 @@ int url_is_local_not_ssh(const char *url);
|
||||
struct packet_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_feature_v2(const char *c, const char **v);
|
||||
int server_supports_feature(const char *c, const char *feature,
|
||||
int die_on_error);
|
||||
|
||||
|
14
fetch-pack.c
14
fetch-pack.c
@ -1050,6 +1050,8 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
|
||||
print_verbose(args, _("Server supports %s"), "deepen-relative");
|
||||
else if (args->deepen_relative)
|
||||
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) {
|
||||
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 ret = 0;
|
||||
const char *hash_name;
|
||||
struct strbuf req_buf = STRBUF_INIT;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
if (args->use_thin_pack)
|
||||
packet_buf_write(&req_buf, "thin-pack");
|
||||
|
@ -868,6 +868,12 @@ char *xgetcwd(void);
|
||||
FILE *fopen_for_writing(const char *path);
|
||||
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
|
||||
* that ptr is used twice, so don't pass e.g. ptr++.
|
||||
|
2
git.c
2
git.c
@ -574,7 +574,7 @@ static struct cmd_struct commands[] = {
|
||||
{ "shortlog", cmd_shortlog, RUN_SETUP_GENTLY | USE_PAGER },
|
||||
{ "show", cmd_show, 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 },
|
||||
{ "sparse-checkout", cmd_sparse_checkout, RUN_SETUP | NEED_WORK_TREE },
|
||||
{ "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE },
|
||||
|
@ -70,6 +70,7 @@ struct packed_git {
|
||||
size_t index_size;
|
||||
uint32_t num_objects;
|
||||
uint32_t num_bad_objects;
|
||||
uint32_t crc_offset;
|
||||
unsigned char *bad_object_sha1;
|
||||
int index_version;
|
||||
time_t mtime;
|
||||
|
@ -178,6 +178,7 @@ int load_idx(const char *path, const unsigned int hashsz, void *idx_map,
|
||||
*/
|
||||
(sizeof(off_t) <= 4))
|
||||
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;
|
||||
|
@ -490,6 +490,7 @@ void packet_reader_init(struct packet_reader *reader, int fd,
|
||||
reader->buffer_size = sizeof(packet_buffer);
|
||||
reader->options = options;
|
||||
reader->me = "git";
|
||||
reader->hash_algo = &hash_algos[GIT_HASH_SHA1];
|
||||
}
|
||||
|
||||
enum packet_read_status packet_reader_read(struct packet_reader *reader)
|
||||
|
@ -177,6 +177,9 @@ struct packet_reader {
|
||||
|
||||
unsigned use_sideband : 1;
|
||||
const char *me;
|
||||
|
||||
/* hash algorithm in use */
|
||||
const struct git_hash_algo *hash_algo;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -41,7 +41,9 @@ struct options {
|
||||
deepen_relative : 1,
|
||||
from_promisor : 1,
|
||||
no_dependents : 1,
|
||||
atomic : 1;
|
||||
atomic : 1,
|
||||
object_format : 1;
|
||||
const struct git_hash_algo *hash_algo;
|
||||
};
|
||||
static struct options options;
|
||||
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")) {
|
||||
options.filter = xstrdup(value);
|
||||
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 {
|
||||
return 1 /* unsupported */;
|
||||
}
|
||||
@ -231,6 +243,7 @@ static struct ref *parse_git_refs(struct discovery *heads, int for_push)
|
||||
case protocol_v0:
|
||||
get_remote_heads(&reader, &list, for_push ? REF_NORMAL : 0,
|
||||
NULL, &heads->shallow);
|
||||
options.hash_algo = reader.hash_algo;
|
||||
break;
|
||||
case protocol_unknown_version:
|
||||
BUG("unknown protocol version");
|
||||
@ -239,6 +252,19 @@ static struct ref *parse_git_refs(struct discovery *heads, int for_push)
|
||||
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)
|
||||
{
|
||||
char *data, *start, *mid;
|
||||
@ -249,6 +275,12 @@ static struct ref *parse_info_refs(struct discovery *heads)
|
||||
struct ref *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;
|
||||
start = NULL;
|
||||
mid = data;
|
||||
@ -259,13 +291,13 @@ static struct ref *parse_info_refs(struct discovery *heads)
|
||||
if (data[i] == '\t')
|
||||
mid = &data[i];
|
||||
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?"),
|
||||
transport_anonymize_url(url.buf));
|
||||
data[i] = 0;
|
||||
ref_name = mid + 1;
|
||||
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)
|
||||
refs = ref;
|
||||
if (last_ref)
|
||||
@ -509,11 +541,16 @@ static struct ref *get_refs(int for_push)
|
||||
static void output_refs(struct ref *refs)
|
||||
{
|
||||
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) {
|
||||
if (posn->symref)
|
||||
printf("@%s %s\n", posn->symref, posn->name);
|
||||
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");
|
||||
fflush(stdout);
|
||||
@ -1499,6 +1536,7 @@ int cmd_main(int argc, const char **argv)
|
||||
printf("option\n");
|
||||
printf("push\n");
|
||||
printf("check-connectivity\n");
|
||||
printf("object-format\n");
|
||||
printf("\n");
|
||||
fflush(stdout);
|
||||
} else if (skip_prefix(buf.buf, "stateless-connect ", &arg)) {
|
||||
|
@ -363,6 +363,7 @@ int send_pack(struct send_pack_args *args,
|
||||
int atomic_supported = 0;
|
||||
int use_push_options = 0;
|
||||
int push_options_supported = 0;
|
||||
int object_format_supported = 0;
|
||||
unsigned cmds_sent = 0;
|
||||
int ret;
|
||||
struct async demux;
|
||||
@ -389,6 +390,9 @@ int send_pack(struct send_pack_args *args,
|
||||
if (server_supports("push-options"))
|
||||
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) {
|
||||
int 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");
|
||||
if (use_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)
|
||||
strbuf_addf(&cap_buf, " agent=%s", git_user_agent_sanitized());
|
||||
|
||||
|
27
serve.c
27
serve.c
@ -22,6 +22,14 @@ static int agent_advertise(struct repository *r,
|
||||
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 {
|
||||
/*
|
||||
* 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 },
|
||||
{ "fetch", upload_pack_advertise, upload_pack_v2 },
|
||||
{ "server-option", always_advertise, NULL },
|
||||
{ "object-format", object_format_advertise, NULL },
|
||||
};
|
||||
|
||||
static void advertise_capabilities(void)
|
||||
@ -153,6 +162,22 @@ int has_capability(const struct argv_array *keys, const char *capability,
|
||||
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 {
|
||||
PROCESS_REQUEST_KEYS,
|
||||
PROCESS_REQUEST_DONE,
|
||||
@ -225,6 +250,8 @@ static int process_request(void)
|
||||
if (!command)
|
||||
die("no command requested");
|
||||
|
||||
check_algorithm(the_repository, &keys);
|
||||
|
||||
command->command(the_repository, &keys, &reader);
|
||||
|
||||
argv_array_clear(&keys);
|
||||
|
1
setup.c
1
setup.c
@ -1308,6 +1308,7 @@ void check_repository_format(struct repository_format *fmt)
|
||||
fmt = &repo_fmt;
|
||||
check_repository_format_gently(get_git_dir(), fmt, NULL);
|
||||
startup_info->have_repository = 1;
|
||||
repo_set_hash_algo(the_repository, fmt->hash_algo);
|
||||
clear_repository_format(&repo_fmt);
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,9 @@ int cmd__oid_array(int argc, const char **argv)
|
||||
{
|
||||
struct oid_array array = OID_ARRAY_INIT;
|
||||
struct strbuf line = STRBUF_INIT;
|
||||
int nongit_ok;
|
||||
|
||||
setup_git_directory_gently(&nongit_ok);
|
||||
|
||||
while (strbuf_getline(&line, stdin) != EOF) {
|
||||
const char *arg;
|
||||
|
@ -12,6 +12,7 @@ file_size () {
|
||||
}
|
||||
|
||||
test_expect_success setup '
|
||||
test_oid_init &&
|
||||
# clone does not allow us to pass core.bigfilethreshold to
|
||||
# new repos, so set core.bigfilethreshold globally
|
||||
git config --global core.bigfilethreshold 200k &&
|
||||
@ -64,7 +65,7 @@ test_expect_success 'add a large file or two' '
|
||||
test $count = 1 &&
|
||||
cnt=$(git show-index <"$idx" | wc -l) &&
|
||||
test $cnt = 2 &&
|
||||
for l in .git/objects/??/??????????????????????????????????????
|
||||
for l in .git/objects/$OIDPATH_REGEX
|
||||
do
|
||||
test_path_is_file "$l" || continue
|
||||
bad=t
|
||||
@ -177,7 +178,8 @@ test_expect_success 'git-show a large file' '
|
||||
|
||||
test_expect_success 'index-pack' '
|
||||
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' '
|
||||
|
@ -8,6 +8,10 @@ test_description='Test repository version check'
|
||||
. ./test-lib.sh
|
||||
|
||||
test_expect_success 'setup' '
|
||||
test_oid_cache <<-\EOF &&
|
||||
version sha1:0
|
||||
version sha256:1
|
||||
EOF
|
||||
cat >test.patch <<-\EOF &&
|
||||
diff --git a/test.txt b/test.txt
|
||||
new file mode 100644
|
||||
@ -23,7 +27,7 @@ test_expect_success 'setup' '
|
||||
'
|
||||
|
||||
test_expect_success 'gitdir selection on normal repos' '
|
||||
echo 0 >expect &&
|
||||
echo $(test_oid version) >expect &&
|
||||
git config core.repositoryformatversion >actual &&
|
||||
git -C test config core.repositoryformatversion >actual2 &&
|
||||
test_cmp expect actual &&
|
||||
|
@ -402,7 +402,7 @@ EOF
|
||||
|
||||
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 q2 q
|
||||
'
|
||||
|
@ -12,7 +12,8 @@ TRASH=$(pwd)
|
||||
|
||||
test_expect_success \
|
||||
'setup' \
|
||||
'rm -f .git/index* &&
|
||||
'test_oid_init &&
|
||||
rm -f .git/index* &&
|
||||
perl -e "print \"a\" x 4096;" > a &&
|
||||
perl -e "print \"b\" x 4096;" > b &&
|
||||
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' '
|
||||
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_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_expect_success 'index-pack --strict <pack> works in non-repo' '
|
||||
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
|
||||
'
|
||||
|
||||
|
@ -7,65 +7,65 @@ test_description='pack index with 64-bit offsets and object CRC'
|
||||
. ./test-lib.sh
|
||||
|
||||
test_expect_success 'setup' '
|
||||
test_oid_init &&
|
||||
rawsz=$(test_oid rawsz) &&
|
||||
rm -rf .git &&
|
||||
git init &&
|
||||
git config pack.threads 1 &&
|
||||
i=1 &&
|
||||
while test $i -le 100
|
||||
do
|
||||
iii=$(printf '%03i' $i)
|
||||
test-tool genrandom "bar" 200 > wide_delta_$iii &&
|
||||
test-tool genrandom "baz $iii" 50 >> wide_delta_$iii &&
|
||||
test-tool genrandom "foo"$i 100 > deep_delta_$iii &&
|
||||
test-tool genrandom "foo"$(expr $i + 1) 100 >> deep_delta_$iii &&
|
||||
test-tool genrandom "foo"$(expr $i + 2) 100 >> deep_delta_$iii &&
|
||||
echo $iii >file_$iii &&
|
||||
test-tool genrandom "$iii" 8192 >>file_$iii &&
|
||||
git update-index --add file_$iii deep_delta_$iii wide_delta_$iii &&
|
||||
i=$(expr $i + 1) || return 1
|
||||
done &&
|
||||
{ echo 101 && test-tool genrandom 100 8192; } >file_101 &&
|
||||
git update-index --add file_101 &&
|
||||
tree=$(git write-tree) &&
|
||||
commit=$(git commit-tree $tree </dev/null) && {
|
||||
echo $tree &&
|
||||
git ls-tree $tree | sed -e "s/.* \\([0-9a-f]*\\) .*/\\1/"
|
||||
} >obj-list &&
|
||||
git update-ref HEAD $commit
|
||||
test_oid_init &&
|
||||
rawsz=$(test_oid rawsz) &&
|
||||
rm -rf .git &&
|
||||
git init &&
|
||||
git config pack.threads 1 &&
|
||||
i=1 &&
|
||||
while test $i -le 100
|
||||
do
|
||||
iii=$(printf '%03i' $i)
|
||||
test-tool genrandom "bar" 200 > wide_delta_$iii &&
|
||||
test-tool genrandom "baz $iii" 50 >> wide_delta_$iii &&
|
||||
test-tool genrandom "foo"$i 100 > deep_delta_$iii &&
|
||||
test-tool genrandom "foo"$(expr $i + 1) 100 >> deep_delta_$iii &&
|
||||
test-tool genrandom "foo"$(expr $i + 2) 100 >> deep_delta_$iii &&
|
||||
echo $iii >file_$iii &&
|
||||
test-tool genrandom "$iii" 8192 >>file_$iii &&
|
||||
git update-index --add file_$iii deep_delta_$iii wide_delta_$iii &&
|
||||
i=$(expr $i + 1) || return 1
|
||||
done &&
|
||||
{ echo 101 && test-tool genrandom 100 8192; } >file_101 &&
|
||||
git update-index --add file_101 &&
|
||||
tree=$(git write-tree) &&
|
||||
commit=$(git commit-tree $tree </dev/null) && {
|
||||
echo $tree &&
|
||||
git ls-tree $tree | sed -e "s/.* \\([0-9a-f]*\\) .*/\\1/"
|
||||
} >obj-list &&
|
||||
git update-ref HEAD $commit
|
||||
'
|
||||
|
||||
test_expect_success \
|
||||
'pack-objects with index version 1' \
|
||||
'pack1=$(git pack-objects --index-version=1 test-1 <obj-list) &&
|
||||
git verify-pack -v "test-1-${pack1}.pack"'
|
||||
test_expect_success 'pack-objects with index version 1' '
|
||||
pack1=$(git pack-objects --index-version=1 test-1 <obj-list) &&
|
||||
git verify-pack -v "test-1-${pack1}.pack"
|
||||
'
|
||||
|
||||
test_expect_success \
|
||||
'pack-objects with index version 2' \
|
||||
'pack2=$(git pack-objects --index-version=2 test-2 <obj-list) &&
|
||||
git verify-pack -v "test-2-${pack2}.pack"'
|
||||
test_expect_success 'pack-objects with index version 2' '
|
||||
pack2=$(git pack-objects --index-version=2 test-2 <obj-list) &&
|
||||
git verify-pack -v "test-2-${pack2}.pack"
|
||||
'
|
||||
|
||||
test_expect_success \
|
||||
'both packs should be identical' \
|
||||
'cmp "test-1-${pack1}.pack" "test-2-${pack2}.pack"'
|
||||
test_expect_success 'both packs should be identical' '
|
||||
cmp "test-1-${pack1}.pack" "test-2-${pack2}.pack"
|
||||
'
|
||||
|
||||
test_expect_success \
|
||||
'index v1 and index v2 should be different' \
|
||||
'! cmp "test-1-${pack1}.idx" "test-2-${pack2}.idx"'
|
||||
test_expect_success 'index v1 and index v2 should be different' '
|
||||
! cmp "test-1-${pack1}.idx" "test-2-${pack2}.idx"
|
||||
'
|
||||
|
||||
test_expect_success \
|
||||
'index-pack with index version 1' \
|
||||
'git index-pack --index-version=1 -o 1.idx "test-1-${pack1}.pack"'
|
||||
test_expect_success 'index-pack with index version 1' '
|
||||
git index-pack --index-version=1 -o 1.idx "test-1-${pack1}.pack"
|
||||
'
|
||||
|
||||
test_expect_success \
|
||||
'index-pack with index version 2' \
|
||||
'git index-pack --index-version=2 -o 2.idx "test-1-${pack1}.pack"'
|
||||
test_expect_success 'index-pack with index version 2' '
|
||||
git index-pack --index-version=2 -o 2.idx "test-1-${pack1}.pack"
|
||||
'
|
||||
|
||||
test_expect_success \
|
||||
'index-pack results should match pack-objects ones' \
|
||||
'cmp "test-1-${pack1}.idx" "1.idx" &&
|
||||
cmp "test-2-${pack2}.idx" "2.idx"'
|
||||
test_expect_success 'index-pack results should match pack-objects ones' '
|
||||
cmp "test-1-${pack1}.idx" "1.idx" &&
|
||||
cmp "test-2-${pack2}.idx" "2.idx"
|
||||
'
|
||||
|
||||
test_expect_success 'index-pack --verify on index version 1' '
|
||||
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"
|
||||
'
|
||||
|
||||
test_expect_success \
|
||||
'pack-objects --index-version=2, is not accepted' \
|
||||
'test_must_fail git pack-objects --index-version=2, test-3 <obj-list'
|
||||
test_expect_success 'pack-objects --index-version=2, is not accepted' '
|
||||
test_must_fail git pack-objects --index-version=2, test-3 <obj-list
|
||||
'
|
||||
|
||||
test_expect_success \
|
||||
'index v2: force some 64-bit offsets with pack-objects' \
|
||||
'pack3=$(git pack-objects --index-version=2,0x40000 test-3 <obj-list)'
|
||||
test_expect_success 'index v2: force some 64-bit offsets with pack-objects' '
|
||||
pack3=$(git pack-objects --index-version=2,0x40000 test-3 <obj-list)
|
||||
'
|
||||
|
||||
if msg=$(git verify-pack -v "test-3-${pack3}.pack" 2>&1) ||
|
||||
! (echo "$msg" | grep "pack too large .* off_t")
|
||||
@ -91,21 +91,21 @@ else
|
||||
say "# skipping tests concerning 64-bit offsets"
|
||||
fi
|
||||
|
||||
test_expect_success OFF64_T \
|
||||
'index v2: verify a pack with some 64-bit offsets' \
|
||||
'git verify-pack -v "test-3-${pack3}.pack"'
|
||||
test_expect_success OFF64_T 'index v2: verify a pack with some 64-bit offsets' '
|
||||
git verify-pack -v "test-3-${pack3}.pack"
|
||||
'
|
||||
|
||||
test_expect_success OFF64_T \
|
||||
'64-bit offsets: should be different from previous index v2 results' \
|
||||
'! cmp "test-2-${pack2}.idx" "test-3-${pack3}.idx"'
|
||||
test_expect_success OFF64_T '64-bit offsets: should be different from previous index v2 results' '
|
||||
! cmp "test-2-${pack2}.idx" "test-3-${pack3}.idx"
|
||||
'
|
||||
|
||||
test_expect_success OFF64_T \
|
||||
'index v2: force some 64-bit offsets with index-pack' \
|
||||
'git index-pack --index-version=2,0x40000 -o 3.idx "test-1-${pack1}.pack"'
|
||||
test_expect_success OFF64_T 'index v2: force some 64-bit offsets with index-pack' '
|
||||
git index-pack --index-version=2,0x40000 -o 3.idx "test-1-${pack1}.pack"
|
||||
'
|
||||
|
||||
test_expect_success OFF64_T \
|
||||
'64-bit offsets: index-pack result should match pack-objects one' \
|
||||
'cmp "test-3-${pack3}.idx" "3.idx"'
|
||||
test_expect_success OFF64_T '64-bit offsets: index-pack result should match pack-objects one' '
|
||||
cmp "test-3-${pack3}.idx" "3.idx"
|
||||
'
|
||||
|
||||
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
|
||||
@ -120,135 +120,143 @@ test_expect_success OFF64_T 'index-pack --verify on 64-bit offset v2' '
|
||||
# returns the object number for given object in given pack index
|
||||
index_obj_nr()
|
||||
{
|
||||
idx_file=$1
|
||||
object_sha1=$2
|
||||
nr=0
|
||||
git show-index < $idx_file |
|
||||
while read offs sha1 extra
|
||||
do
|
||||
nr=$(($nr + 1))
|
||||
test "$sha1" = "$object_sha1" || continue
|
||||
echo "$(($nr - 1))"
|
||||
break
|
||||
done
|
||||
idx_file=$1
|
||||
object_sha1=$2
|
||||
nr=0
|
||||
git show-index < $idx_file |
|
||||
while read offs sha1 extra
|
||||
do
|
||||
nr=$(($nr + 1))
|
||||
test "$sha1" = "$object_sha1" || continue
|
||||
echo "$(($nr - 1))"
|
||||
break
|
||||
done
|
||||
}
|
||||
|
||||
# returns the pack offset for given object as found in given pack index
|
||||
index_obj_offset()
|
||||
{
|
||||
idx_file=$1
|
||||
object_sha1=$2
|
||||
git show-index < $idx_file | grep $object_sha1 |
|
||||
( read offs extra && echo "$offs" )
|
||||
idx_file=$1
|
||||
object_sha1=$2
|
||||
git show-index < $idx_file | grep $object_sha1 |
|
||||
( read offs extra && echo "$offs" )
|
||||
}
|
||||
|
||||
test_expect_success \
|
||||
'[index v1] 1) stream pack to repository' \
|
||||
'git index-pack --index-version=1 --stdin < "test-1-${pack1}.pack" &&
|
||||
git prune-packed &&
|
||||
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}.idx" ".git/objects/pack/pack-${pack1}.idx"'
|
||||
test_expect_success '[index v1] 1) stream pack to repository' '
|
||||
git index-pack --index-version=1 --stdin < "test-1-${pack1}.pack" &&
|
||||
git prune-packed &&
|
||||
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}.idx" ".git/objects/pack/pack-${pack1}.idx"
|
||||
'
|
||||
|
||||
test_expect_success \
|
||||
'[index v1] 2) create a stealth corruption in a delta base reference' \
|
||||
'# 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
|
||||
sha1_101=$(git hash-object file_101) &&
|
||||
sha1_099=$(git hash-object file_099) &&
|
||||
offs_101=$(index_obj_offset 1.idx $sha1_101) &&
|
||||
nr_099=$(index_obj_nr 1.idx $sha1_099) &&
|
||||
chmod +w ".git/objects/pack/pack-${pack1}.pack" &&
|
||||
recordsz=$((rawsz + 4)) &&
|
||||
dd of=".git/objects/pack/pack-${pack1}.pack" seek=$(($offs_101 + 1)) \
|
||||
if=".git/objects/pack/pack-${pack1}.idx" \
|
||||
skip=$((4 + 256 * 4 + $nr_099 * recordsz)) \
|
||||
bs=1 count=$rawsz conv=notrunc &&
|
||||
git cat-file blob $sha1_101 > file_101_foo1'
|
||||
'[index v1] 2) create a stealth corruption in a delta base reference' '
|
||||
# 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
|
||||
sha1_101=$(git hash-object file_101) &&
|
||||
sha1_099=$(git hash-object file_099) &&
|
||||
offs_101=$(index_obj_offset 1.idx $sha1_101) &&
|
||||
nr_099=$(index_obj_nr 1.idx $sha1_099) &&
|
||||
chmod +w ".git/objects/pack/pack-${pack1}.pack" &&
|
||||
recordsz=$((rawsz + 4)) &&
|
||||
dd of=".git/objects/pack/pack-${pack1}.pack" seek=$(($offs_101 + 1)) \
|
||||
if=".git/objects/pack/pack-${pack1}.idx" \
|
||||
skip=$((4 + 256 * 4 + $nr_099 * recordsz)) \
|
||||
bs=1 count=$rawsz conv=notrunc &&
|
||||
git cat-file blob $sha1_101 > file_101_foo1
|
||||
'
|
||||
|
||||
test_expect_success \
|
||||
'[index v1] 3) corrupted delta happily returned wrong data' \
|
||||
'test -f file_101_foo1 && ! cmp file_101 file_101_foo1'
|
||||
'[index v1] 3) corrupted delta happily returned wrong data' '
|
||||
test -f file_101_foo1 && ! cmp file_101 file_101_foo1
|
||||
'
|
||||
|
||||
test_expect_success \
|
||||
'[index v1] 4) confirm that the pack is actually corrupted' \
|
||||
'test_must_fail git fsck --full $commit'
|
||||
'[index v1] 4) confirm that the pack is actually corrupted' '
|
||||
test_must_fail git fsck --full $commit
|
||||
'
|
||||
|
||||
test_expect_success \
|
||||
'[index v1] 5) pack-objects happily reuses corrupted data' \
|
||||
'pack4=$(git pack-objects test-4 <obj-list) &&
|
||||
test -f "test-4-${pack4}.pack"'
|
||||
'[index v1] 5) pack-objects happily reuses corrupted data' '
|
||||
pack4=$(git pack-objects test-4 <obj-list) &&
|
||||
test -f "test-4-${pack4}.pack"
|
||||
'
|
||||
|
||||
test_expect_success '[index v1] 6) newly created pack is BAD !' '
|
||||
test_must_fail git verify-pack -v "test-4-${pack4}.pack"
|
||||
'
|
||||
|
||||
test_expect_success '[index v2] 1) stream pack to repository' '
|
||||
rm -f .git/objects/pack/* &&
|
||||
git index-pack --index-version=2 --stdin < "test-1-${pack1}.pack" &&
|
||||
git prune-packed &&
|
||||
git count-objects | ( read nr rest && test "$nr" -eq 1 ) &&
|
||||
cmp "test-1-${pack1}.pack" ".git/objects/pack/pack-${pack1}.pack" &&
|
||||
cmp "test-2-${pack1}.idx" ".git/objects/pack/pack-${pack1}.idx"
|
||||
'
|
||||
|
||||
test_expect_success \
|
||||
'[index v1] 6) newly created pack is BAD !' \
|
||||
'test_must_fail git verify-pack -v "test-4-${pack4}.pack"'
|
||||
'[index v2] 2) create a stealth corruption in a delta base reference' '
|
||||
# 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
|
||||
sha1_101=$(git hash-object file_101) &&
|
||||
sha1_099=$(git hash-object file_099) &&
|
||||
offs_101=$(index_obj_offset 1.idx $sha1_101) &&
|
||||
nr_099=$(index_obj_nr 1.idx $sha1_099) &&
|
||||
chmod +w ".git/objects/pack/pack-${pack1}.pack" &&
|
||||
dd of=".git/objects/pack/pack-${pack1}.pack" seek=$(($offs_101 + 1)) \
|
||||
if=".git/objects/pack/pack-${pack1}.idx" \
|
||||
skip=$((8 + 256 * 4 + $nr_099 * rawsz)) \
|
||||
bs=1 count=$rawsz conv=notrunc &&
|
||||
git cat-file blob $sha1_101 > file_101_foo2
|
||||
'
|
||||
|
||||
test_expect_success \
|
||||
'[index v2] 1) stream pack to repository' \
|
||||
'rm -f .git/objects/pack/* &&
|
||||
git index-pack --index-version=2 --stdin < "test-1-${pack1}.pack" &&
|
||||
git prune-packed &&
|
||||
git count-objects | ( read nr rest && test "$nr" -eq 1 ) &&
|
||||
cmp "test-1-${pack1}.pack" ".git/objects/pack/pack-${pack1}.pack" &&
|
||||
cmp "test-2-${pack1}.idx" ".git/objects/pack/pack-${pack1}.idx"'
|
||||
'[index v2] 3) corrupted delta happily returned wrong data' '
|
||||
test -f file_101_foo2 && ! cmp file_101 file_101_foo2
|
||||
'
|
||||
|
||||
test_expect_success \
|
||||
'[index v2] 2) create a stealth corruption in a delta base reference' \
|
||||
'# 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
|
||||
sha1_101=$(git hash-object file_101) &&
|
||||
sha1_099=$(git hash-object file_099) &&
|
||||
offs_101=$(index_obj_offset 1.idx $sha1_101) &&
|
||||
nr_099=$(index_obj_nr 1.idx $sha1_099) &&
|
||||
chmod +w ".git/objects/pack/pack-${pack1}.pack" &&
|
||||
dd of=".git/objects/pack/pack-${pack1}.pack" seek=$(($offs_101 + 1)) \
|
||||
if=".git/objects/pack/pack-${pack1}.idx" \
|
||||
skip=$((8 + 256 * 4 + $nr_099 * rawsz)) \
|
||||
bs=1 count=$rawsz conv=notrunc &&
|
||||
git cat-file blob $sha1_101 > file_101_foo2'
|
||||
'[index v2] 4) confirm that the pack is actually corrupted' '
|
||||
test_must_fail git fsck --full $commit
|
||||
'
|
||||
|
||||
test_expect_success \
|
||||
'[index v2] 3) corrupted delta happily returned wrong data' \
|
||||
'test -f file_101_foo2 && ! cmp file_101 file_101_foo2'
|
||||
'[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 --no-reuse-object test-6 <obj-list
|
||||
'
|
||||
|
||||
test_expect_success \
|
||||
'[index v2] 4) confirm that the pack is actually corrupted' \
|
||||
'test_must_fail git fsck --full $commit'
|
||||
|
||||
test_expect_success \
|
||||
'[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 --no-reuse-object test-6 <obj-list'
|
||||
|
||||
test_expect_success \
|
||||
'[index v2] 6) verify-pack detects CRC mismatch' \
|
||||
'rm -f .git/objects/pack/* &&
|
||||
git index-pack --index-version=2 --stdin < "test-1-${pack1}.pack" &&
|
||||
git verify-pack ".git/objects/pack/pack-${pack1}.pack" &&
|
||||
obj=$(git hash-object file_001) &&
|
||||
nr=$(index_obj_nr ".git/objects/pack/pack-${pack1}.idx" $obj) &&
|
||||
chmod +w ".git/objects/pack/pack-${pack1}.idx" &&
|
||||
printf xxxx | dd of=".git/objects/pack/pack-${pack1}.idx" conv=notrunc \
|
||||
bs=1 count=4 seek=$((8 + 256 * 4 + $(wc -l <obj-list) * rawsz + $nr * 4)) &&
|
||||
( while read obj
|
||||
do git cat-file -p $obj >/dev/null || exit 1
|
||||
done <obj-list ) &&
|
||||
test_must_fail git verify-pack ".git/objects/pack/pack-${pack1}.pack"
|
||||
'[index v2] 6) verify-pack detects CRC mismatch' '
|
||||
rm -f .git/objects/pack/* &&
|
||||
git index-pack --index-version=2 --stdin < "test-1-${pack1}.pack" &&
|
||||
git verify-pack ".git/objects/pack/pack-${pack1}.pack" &&
|
||||
obj=$(git hash-object file_001) &&
|
||||
nr=$(index_obj_nr ".git/objects/pack/pack-${pack1}.idx" $obj) &&
|
||||
chmod +w ".git/objects/pack/pack-${pack1}.idx" &&
|
||||
printf xxxx | dd of=".git/objects/pack/pack-${pack1}.idx" conv=notrunc \
|
||||
bs=1 count=4 seek=$((8 + 256 * 4 + $(wc -l <obj-list) * rawsz + $nr * 4)) &&
|
||||
( while read obj
|
||||
do git cat-file -p $obj >/dev/null || exit 1
|
||||
done <obj-list ) &&
|
||||
test_must_fail git verify-pack ".git/objects/pack/pack-${pack1}.pack"
|
||||
'
|
||||
|
||||
test_expect_success 'running index-pack in the object store' '
|
||||
rm -f .git/objects/pack/* &&
|
||||
cp test-1-${pack1}.pack .git/objects/pack/pack-${pack1}.pack &&
|
||||
(
|
||||
cd .git/objects/pack &&
|
||||
git index-pack pack-${pack1}.pack
|
||||
) &&
|
||||
test -f .git/objects/pack/pack-${pack1}.idx
|
||||
rm -f .git/objects/pack/* &&
|
||||
cp test-1-${pack1}.pack .git/objects/pack/pack-${pack1}.pack &&
|
||||
(
|
||||
cd .git/objects/pack &&
|
||||
git index-pack pack-${pack1}.pack
|
||||
) &&
|
||||
test -f .git/objects/pack/pack-${pack1}.idx
|
||||
'
|
||||
|
||||
test_expect_success 'index-pack --strict warns upon missing tagger in tag' '
|
||||
sha=$(git rev-parse HEAD) &&
|
||||
cat >wrong-tag <<EOF &&
|
||||
sha=$(git rev-parse HEAD) &&
|
||||
cat >wrong-tag <<EOF &&
|
||||
object $sha
|
||||
type commit
|
||||
tag guten tag
|
||||
@ -256,18 +264,18 @@ tag guten tag
|
||||
This is an invalid tag.
|
||||
EOF
|
||||
|
||||
tag=$(git hash-object -t tag -w --stdin <wrong-tag) &&
|
||||
pack1=$(echo $tag $sha | git pack-objects tag-test) &&
|
||||
echo remove tag object &&
|
||||
thirtyeight=${tag#??} &&
|
||||
rm -f .git/objects/${tag%$thirtyeight}/$thirtyeight &&
|
||||
git index-pack --strict tag-test-${pack1}.pack 2>err &&
|
||||
grep "^warning:.* expected .tagger. line" err
|
||||
tag=$(git hash-object -t tag -w --stdin <wrong-tag) &&
|
||||
pack1=$(echo $tag $sha | git pack-objects tag-test) &&
|
||||
echo remove tag object &&
|
||||
thirtyeight=${tag#??} &&
|
||||
rm -f .git/objects/${tag%$thirtyeight}/$thirtyeight &&
|
||||
git index-pack --strict tag-test-${pack1}.pack 2>err &&
|
||||
grep "^warning:.* expected .tagger. line" err
|
||||
'
|
||||
|
||||
test_expect_success 'index-pack --fsck-objects also warns upon missing tagger in tag' '
|
||||
git index-pack --fsck-objects tag-test-${pack1}.pack 2>err &&
|
||||
grep "^warning:.* expected .tagger. line" err
|
||||
git index-pack --fsck-objects tag-test-${pack1}.pack 2>err &&
|
||||
grep "^warning:.* expected .tagger. line" err
|
||||
'
|
||||
|
||||
test_done
|
||||
|
@ -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
|
||||
0012command=fetch
|
||||
$(echo "object-format=$(test_oid algo)" | packetize)
|
||||
00010013deepen-since 1
|
||||
0032want $(git rev-parse other)
|
||||
0032have $(git rev-parse master)
|
||||
$(echo "want $(git rev-parse other)" | packetize)
|
||||
$(echo "have $(git rev-parse master)" | packetize)
|
||||
0000
|
||||
EOF
|
||||
)
|
||||
|
@ -50,6 +50,24 @@ test_expect_success 'create password-protected repository' '
|
||||
"$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
|
||||
|
||||
test_expect_success 'cloning password-protected repository can fail' '
|
||||
|
@ -46,6 +46,7 @@ ssize_b100dots() {
|
||||
}
|
||||
|
||||
test_expect_success 'setup' '
|
||||
test_oid_init &&
|
||||
HTTP_CONTENT_ENCODING="identity" &&
|
||||
export HTTP_CONTENT_ENCODING &&
|
||||
git config http.receivepack true &&
|
||||
@ -62,8 +63,8 @@ test_expect_success 'setup' '
|
||||
test_copy_bytes 10 <fetch_body >fetch_body.trunc &&
|
||||
hash_next=$(git commit-tree -p HEAD -m next HEAD^{tree}) &&
|
||||
{
|
||||
printf "%s %s refs/heads/newbranch\\0report-status\\n" \
|
||||
"$ZERO_OID" "$hash_next" | packetize &&
|
||||
printf "%s %s refs/heads/newbranch\\0report-status object-format=%s\\n" \
|
||||
"$ZERO_OID" "$hash_next" "$(test_oid algo)" | packetize &&
|
||||
printf 0000 &&
|
||||
echo "$hash_next" | git pack-objects --stdout
|
||||
} >push_body &&
|
||||
|
@ -5,12 +5,17 @@ test_description='test protocol v2 server commands'
|
||||
. ./test-lib.sh
|
||||
|
||||
test_expect_success 'test capability advertisement' '
|
||||
test_oid_cache <<-EOF &&
|
||||
wrong_algo sha1:sha256
|
||||
wrong_algo sha256:sha1
|
||||
EOF
|
||||
cat >expect <<-EOF &&
|
||||
version 2
|
||||
agent=git/$(git version | cut -d" " -f3)
|
||||
ls-refs
|
||||
fetch=shallow
|
||||
server-option
|
||||
object-format=$(test_oid algo)
|
||||
0000
|
||||
EOF
|
||||
|
||||
@ -45,6 +50,7 @@ test_expect_success 'request invalid capability' '
|
||||
test_expect_success 'request with no command' '
|
||||
test-tool pkt-line pack >in <<-EOF &&
|
||||
agent=git/test
|
||||
object-format=$(test_oid algo)
|
||||
0000
|
||||
EOF
|
||||
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-tool pkt-line pack >in <<-EOF &&
|
||||
command=foo
|
||||
object-format=$(test_oid algo)
|
||||
agent=git/test
|
||||
0000
|
||||
EOF
|
||||
@ -61,6 +68,17 @@ test_expect_success 'request invalid command' '
|
||||
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_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-tool pkt-line pack >in <<-EOF &&
|
||||
command=ls-refs
|
||||
object-format=$(test_oid algo)
|
||||
0000
|
||||
EOF
|
||||
|
||||
@ -96,6 +115,7 @@ test_expect_success 'basics of ls-refs' '
|
||||
test_expect_success 'basic ref-prefixes' '
|
||||
test-tool pkt-line pack >in <<-EOF &&
|
||||
command=ls-refs
|
||||
object-format=$(test_oid algo)
|
||||
0001
|
||||
ref-prefix refs/heads/master
|
||||
ref-prefix refs/tags/one
|
||||
@ -116,6 +136,7 @@ test_expect_success 'basic ref-prefixes' '
|
||||
test_expect_success 'refs/heads prefix' '
|
||||
test-tool pkt-line pack >in <<-EOF &&
|
||||
command=ls-refs
|
||||
object-format=$(test_oid algo)
|
||||
0001
|
||||
ref-prefix refs/heads/
|
||||
0000
|
||||
@ -136,6 +157,7 @@ test_expect_success 'refs/heads prefix' '
|
||||
test_expect_success 'peel parameter' '
|
||||
test-tool pkt-line pack >in <<-EOF &&
|
||||
command=ls-refs
|
||||
object-format=$(test_oid algo)
|
||||
0001
|
||||
peel
|
||||
ref-prefix refs/tags/
|
||||
@ -157,6 +179,7 @@ test_expect_success 'peel parameter' '
|
||||
test_expect_success 'symrefs parameter' '
|
||||
test-tool pkt-line pack >in <<-EOF &&
|
||||
command=ls-refs
|
||||
object-format=$(test_oid algo)
|
||||
0001
|
||||
symrefs
|
||||
ref-prefix refs/heads/
|
||||
@ -178,6 +201,7 @@ test_expect_success 'symrefs parameter' '
|
||||
test_expect_success 'sending server-options' '
|
||||
test-tool pkt-line pack >in <<-EOF &&
|
||||
command=ls-refs
|
||||
object-format=$(test_oid algo)
|
||||
server-option=hello
|
||||
server-option=world
|
||||
0001
|
||||
@ -200,6 +224,7 @@ test_expect_success 'unexpected lines are not allowed in fetch request' '
|
||||
|
||||
test-tool pkt-line pack >in <<-EOF &&
|
||||
command=fetch
|
||||
object-format=$(test_oid algo)
|
||||
0001
|
||||
this-is-not-a-command
|
||||
0000
|
||||
|
@ -13,6 +13,7 @@ start_git_daemon --export-all --enable=receive-pack
|
||||
daemon_parent=$GIT_DAEMON_DOCUMENT_ROOT_PATH/parent
|
||||
|
||||
test_expect_success 'create repo to be served by git-daemon' '
|
||||
test_oid_init &&
|
||||
git init "$daemon_parent" &&
|
||||
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.
|
||||
test-tool pkt-line pack >in <<-EOF &&
|
||||
command=fetch
|
||||
object-format=$(test_oid algo)
|
||||
0001
|
||||
want $(git -C server rev-parse master)
|
||||
filter blob:none
|
||||
|
@ -27,6 +27,15 @@ check_output () {
|
||||
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)
|
||||
# \ /
|
||||
# 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-tool pkt-line pack >in <<-EOF &&
|
||||
command=fetch
|
||||
$(write_command fetch)
|
||||
0001
|
||||
no-progress
|
||||
want-ref refs/heads/non-existent
|
||||
@ -86,7 +95,7 @@ test_expect_success 'basic want-ref' '
|
||||
|
||||
oid=$(git rev-parse a) &&
|
||||
test-tool pkt-line pack >in <<-EOF &&
|
||||
command=fetch
|
||||
$(write_command fetch)
|
||||
0001
|
||||
no-progress
|
||||
want-ref refs/heads/master
|
||||
@ -110,7 +119,7 @@ test_expect_success 'multiple want-ref lines' '
|
||||
|
||||
oid=$(git rev-parse b) &&
|
||||
test-tool pkt-line pack >in <<-EOF &&
|
||||
command=fetch
|
||||
$(write_command fetch)
|
||||
0001
|
||||
no-progress
|
||||
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 &&
|
||||
|
||||
test-tool pkt-line pack >in <<-EOF &&
|
||||
command=fetch
|
||||
$(write_command fetch)
|
||||
0001
|
||||
no-progress
|
||||
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) &&
|
||||
test-tool pkt-line pack >in <<-EOF &&
|
||||
command=fetch
|
||||
$(write_command fetch)
|
||||
0001
|
||||
no-progress
|
||||
want-ref refs/heads/o/foo
|
||||
|
@ -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' '
|
||||
{
|
||||
packetize command=ls-refs &&
|
||||
packetize "object-format=$(test_oid algo)" &&
|
||||
printf 0001 &&
|
||||
# protocol expects 0000 flush here
|
||||
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' '
|
||||
{
|
||||
packetize command=fetch &&
|
||||
packetize "object-format=$(test_oid algo)" &&
|
||||
printf 0001 &&
|
||||
# protocol expects 0000 flush here
|
||||
printf 0001
|
||||
|
@ -52,9 +52,11 @@ do
|
||||
test -n "$GIT_REMOTE_TESTGIT_SIGNED_TAGS" && echo "signed-tags"
|
||||
test -n "$GIT_REMOTE_TESTGIT_NO_PRIVATE_UPDATE" && echo "no-private-update"
|
||||
echo 'option'
|
||||
echo 'object-format'
|
||||
echo
|
||||
;;
|
||||
list)
|
||||
echo ":object-format $(git rev-parse --show-object-format=storage)"
|
||||
git for-each-ref --format='? %(refname)' 'refs/heads/' 'refs/tags/'
|
||||
head=$(git symbolic-ref HEAD)
|
||||
echo "@$head HEAD"
|
||||
@ -139,6 +141,10 @@ do
|
||||
test $val = "true" && force="true" || force=
|
||||
echo "ok"
|
||||
;;
|
||||
object-format)
|
||||
test $val = "true" && object_format="true" || object_format=
|
||||
echo "ok"
|
||||
;;
|
||||
*)
|
||||
echo "unsupported"
|
||||
;;
|
||||
|
@ -1414,6 +1414,7 @@ test_oid_init
|
||||
|
||||
ZERO_OID=$(test_oid zero)
|
||||
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_BLOB=$(test_oid empty_blob)
|
||||
_z40=$ZERO_OID
|
||||
|
@ -32,7 +32,8 @@ struct helper_data {
|
||||
signed_tags : 1,
|
||||
check_connectivity : 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
|
||||
@ -207,6 +208,8 @@ static struct child_process *get_helper(struct transport *transport)
|
||||
data->import_marks = xstrdup(arg);
|
||||
} else if (starts_with(capname, "no-private-update")) {
|
||||
data->no_private_update = 1;
|
||||
} else if (starts_with(capname, "object-format")) {
|
||||
data->object_format = 1;
|
||||
} else if (mandatory) {
|
||||
die(_("unknown mandatory capability %s; this remote "
|
||||
"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;
|
||||
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)
|
||||
write_str_in_full(helper->in, "list for-push\n");
|
||||
else
|
||||
@ -1116,6 +1125,17 @@ static struct ref *get_refs_list_using_list(struct transport *transport,
|
||||
|
||||
if (!*buf.buf)
|
||||
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, ' ');
|
||||
if (!eov)
|
||||
@ -1128,7 +1148,7 @@ static struct ref *get_refs_list_using_list(struct transport *transport,
|
||||
if (buf.buf[0] == '@')
|
||||
(*tail)->symref = xstrdup(buf.buf + 1);
|
||||
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 (has_attribute(eon + 1, "unchanged")) {
|
||||
(*tail)->status |= REF_STATUS_UPTODATE;
|
||||
|
18
transport.c
18
transport.c
@ -143,6 +143,9 @@ static struct ref *get_refs_from_bundle(struct transport *transport,
|
||||
data->fd = read_bundle_header(transport->url, &data->header);
|
||||
if (data->fd < 0)
|
||||
die(_("could not read bundle '%s'"), transport->url);
|
||||
|
||||
transport->hash_algo = data->header.hash_algo;
|
||||
|
||||
for (i = 0; i < data->header.references.nr; i++) {
|
||||
struct ref_list_entry *e = data->header.references.list + i;
|
||||
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)
|
||||
{
|
||||
struct bundle_transport_data *data = transport->data;
|
||||
int ret;
|
||||
|
||||
if (!data->get_refs_from_bundle_called)
|
||||
get_refs_from_bundle(transport, 0, NULL);
|
||||
return unbundle(the_repository, &data->header, data->fd,
|
||||
transport->progress ? BUNDLE_VERBOSE : 0);
|
||||
ret = unbundle(the_repository, &data->header, data->fd,
|
||||
transport->progress ? BUNDLE_VERBOSE : 0);
|
||||
transport->hash_algo = data->header.hash_algo;
|
||||
return ret;
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
data->got_remote_heads = 1;
|
||||
transport->hash_algo = reader.hash_algo;
|
||||
|
||||
if (reader.line_peeked)
|
||||
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->hash_algo = &hash_algos[GIT_HASH_SHA1];
|
||||
|
||||
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,
|
||||
const char *name, const char *value)
|
||||
{
|
||||
|
@ -115,6 +115,8 @@ struct transport {
|
||||
struct git_transport_options *smart_options;
|
||||
|
||||
enum transport_family family;
|
||||
|
||||
const struct git_hash_algo *hash_algo;
|
||||
};
|
||||
|
||||
#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 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);
|
||||
void transport_unlock_pack(struct transport *transport);
|
||||
int transport_disconnect(struct transport *transport);
|
||||
|
@ -1132,7 +1132,7 @@ static int send_ref(const char *refname, const struct object_id *oid,
|
||||
struct strbuf symref_info = STRBUF_INIT;
|
||||
|
||||
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,
|
||||
0, capabilities,
|
||||
(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" : "",
|
||||
symref_info.buf,
|
||||
data->allow_filter ? " filter" : "",
|
||||
the_hash_algo->name,
|
||||
git_user_agent_sanitized());
|
||||
strbuf_release(&symref_info);
|
||||
} else {
|
||||
|
@ -105,6 +105,14 @@ char *xstrndup(const char *str, size_t 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 *ret;
|
||||
|
Loading…
Reference in New Issue
Block a user