Merge branch 'bc/sha-256-part-1-of-4'

SHA-256 transition continues.

* bc/sha-256-part-1-of-4: (22 commits)
  fast-import: add options for rewriting submodules
  fast-import: add a generic function to iterate over marks
  fast-import: make find_marks work on any mark set
  fast-import: add helper function for inserting mark object entries
  fast-import: permit reading multiple marks files
  commit: use expected signature header for SHA-256
  worktree: allow repository version 1
  init-db: move writing repo version into a function
  builtin/init-db: add environment variable for new repo hash
  builtin/init-db: allow specifying hash algorithm on command line
  setup: allow check_repository_format to read repository format
  t/helper: make repository tests hash independent
  t/helper: initialize repository if necessary
  t/helper/test-dump-split-index: initialize git repository
  t6300: make hash algorithm independent
  t6300: abstract away SHA-1-specific constants
  t: use hash-specific lookup tables to define test constants
  repository: require a build flag to use SHA-256
  hex: add functions to parse hex object IDs in any algorithm
  hex: introduce parsing variants taking hash algorithms
  ...
This commit is contained in:
Junio C Hamano
2020-03-26 17:11:20 -07:00
28 changed files with 624 additions and 142 deletions

View File

@@ -122,6 +122,26 @@ Locations of Marks Files
Relative and non-relative marks may be combined by interweaving Relative and non-relative marks may be combined by interweaving
--(no-)-relative-marks with the --(import|export)-marks= options. --(no-)-relative-marks with the --(import|export)-marks= options.
Submodule Rewriting
~~~~~~~~~~~~~~~~~~~
--rewrite-submodules-from=<name>:<file>::
--rewrite-submodules-to=<name>:<file>::
Rewrite the object IDs for the submodule specified by <name> from the values
used in the from <file> to those used in the to <file>. The from marks should
have been created by `git fast-export`, and the to marks should have been
created by `git fast-import` when importing that same submodule.
+
<name> may be any arbitrary string not containing a colon character, but the
same value must be used with both options when specifying corresponding marks.
Multiple submodules may be specified with different values for <name>. It is an
error not to use these options in corresponding pairs.
+
These options are primarily useful when converting a repository from one hash
algorithm to another; without them, fast-import will fail if it encounters a
submodule because it has no way of writing the object ID into the new hash
algorithm.
Performance and Compression Tuning Performance and Compression Tuning
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@@ -10,7 +10,7 @@ SYNOPSIS
-------- --------
[verse] [verse]
'git init' [-q | --quiet] [--bare] [--template=<template_directory>] 'git init' [-q | --quiet] [--bare] [--template=<template_directory>]
[--separate-git-dir <git dir>] [--separate-git-dir <git dir>] [--object-format=<format]
[--shared[=<permissions>]] [directory] [--shared[=<permissions>]] [directory]
@@ -48,6 +48,11 @@ Only print error and warning messages; all other output will be suppressed.
Create a bare repository. If `GIT_DIR` environment is not set, it is set to the Create a bare repository. If `GIT_DIR` environment is not set, it is set to the
current working directory. current working directory.
--object-format=<format>::
Specify the given object format (hash algorithm) for the repository. The valid
values are 'sha1' and (if enabled) 'sha256'. 'sha1' is the default.
--template=<template_directory>:: --template=<template_directory>::
Specify the directory from which templates will be used. (See the "TEMPLATE Specify the directory from which templates will be used. (See the "TEMPLATE

View File

@@ -493,6 +493,12 @@ double-quotes and respecting backslash escapes. E.g., the value
details. This variable has lower precedence than other path details. This variable has lower precedence than other path
variables such as GIT_INDEX_FILE, GIT_OBJECT_DIRECTORY... variables such as GIT_INDEX_FILE, GIT_OBJECT_DIRECTORY...
`GIT_DEFAULT_HASH_ALGORITHM`::
If this variable is set, the default hash algorithm for new
repositories will be set to this value. This value is currently
ignored when cloning; the setting of the remote repository
is used instead. The default is "sha1".
Git Commits Git Commits
~~~~~~~~~~~ ~~~~~~~~~~~
`GIT_AUTHOR_NAME`:: `GIT_AUTHOR_NAME`::

View File

@@ -1106,7 +1106,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
} }
} }
init_db(git_dir, real_git_dir, option_template, INIT_DB_QUIET); init_db(git_dir, real_git_dir, option_template, GIT_HASH_UNKNOWN, INIT_DB_QUIET);
if (real_git_dir) if (real_git_dir)
git_dir = real_git_dir; git_dir = real_git_dir;

View File

@@ -1667,7 +1667,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
} }
if (amend) { if (amend) {
const char *exclude_gpgsig[2] = { "gpgsig", NULL }; const char *exclude_gpgsig[3] = { "gpgsig", "gpgsig-sha256", NULL };
extra = read_commit_extra_headers(current_head, exclude_gpgsig); extra = read_commit_extra_headers(current_head, exclude_gpgsig);
} else { } else {
struct commit_extra_header **tail = &extra; struct commit_extra_header **tail = &extra;

View File

@@ -20,6 +20,8 @@
#define TEST_FILEMODE 1 #define TEST_FILEMODE 1
#endif #endif
#define GIT_DEFAULT_HASH_ENVIRONMENT "GIT_DEFAULT_HASH"
static int init_is_bare_repository = 0; static int init_is_bare_repository = 0;
static int init_shared_repository = -1; static int init_shared_repository = -1;
static const char *init_db_template_dir; static const char *init_db_template_dir;
@@ -176,13 +178,36 @@ static int needs_work_tree_config(const char *git_dir, const char *work_tree)
return 1; return 1;
} }
void initialize_repository_version(int hash_algo)
{
char repo_version_string[10];
int repo_version = GIT_REPO_VERSION;
#ifndef ENABLE_SHA256
if (hash_algo != GIT_HASH_SHA1)
die(_("The hash algorithm %s is not supported in this build."), hash_algos[hash_algo].name);
#endif
if (hash_algo != GIT_HASH_SHA1)
repo_version = GIT_REPO_VERSION_READ;
/* This forces creation of new config file */
xsnprintf(repo_version_string, sizeof(repo_version_string),
"%d", repo_version);
git_config_set("core.repositoryformatversion", repo_version_string);
if (hash_algo != GIT_HASH_SHA1)
git_config_set("extensions.objectformat",
hash_algos[hash_algo].name);
}
static int create_default_files(const char *template_path, static int create_default_files(const char *template_path,
const char *original_git_dir) const char *original_git_dir,
const struct repository_format *fmt)
{ {
struct stat st1; struct stat st1;
struct strbuf buf = STRBUF_INIT; struct strbuf buf = STRBUF_INIT;
char *path; char *path;
char repo_version_string[10];
char junk[2]; char junk[2];
int reinit; int reinit;
int filemode; int filemode;
@@ -244,10 +269,7 @@ static int create_default_files(const char *template_path,
exit(1); exit(1);
} }
/* This forces creation of new config file */ initialize_repository_version(fmt->hash_algo);
xsnprintf(repo_version_string, sizeof(repo_version_string),
"%d", GIT_REPO_VERSION);
git_config_set("core.repositoryformatversion", repo_version_string);
/* Check filemode trustability */ /* Check filemode trustability */
path = git_path_buf(&buf, "config"); path = git_path_buf(&buf, "config");
@@ -340,12 +362,33 @@ static void separate_git_dir(const char *git_dir, const char *git_link)
write_file(git_link, "gitdir: %s", git_dir); write_file(git_link, "gitdir: %s", git_dir);
} }
static void validate_hash_algorithm(struct repository_format *repo_fmt, int hash)
{
const char *env = getenv(GIT_DEFAULT_HASH_ENVIRONMENT);
/*
* If we already have an initialized repo, don't allow the user to
* specify a different algorithm, as that could cause corruption.
* Otherwise, if the user has specified one on the command line, use it.
*/
if (repo_fmt->version >= 0 && hash != GIT_HASH_UNKNOWN && hash != repo_fmt->hash_algo)
die(_("attempt to reinitialize repository with different hash"));
else if (hash != GIT_HASH_UNKNOWN)
repo_fmt->hash_algo = hash;
else if (env) {
int env_algo = hash_algo_by_name(env);
if (env_algo == GIT_HASH_UNKNOWN)
die(_("unknown hash algorithm '%s'"), env);
repo_fmt->hash_algo = env_algo;
}
}
int init_db(const char *git_dir, const char *real_git_dir, int init_db(const char *git_dir, const char *real_git_dir,
const char *template_dir, unsigned int flags) const char *template_dir, int hash, unsigned int flags)
{ {
int reinit; int reinit;
int exist_ok = flags & INIT_DB_EXIST_OK; int exist_ok = flags & INIT_DB_EXIST_OK;
char *original_git_dir = real_pathdup(git_dir, 1); char *original_git_dir = real_pathdup(git_dir, 1);
struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
if (real_git_dir) { if (real_git_dir) {
struct stat st; struct stat st;
@@ -378,9 +421,11 @@ int init_db(const char *git_dir, const char *real_git_dir,
* config file, so this will not fail. What we are catching * config file, so this will not fail. What we are catching
* is an attempt to reinitialize new repository with an old tool. * is an attempt to reinitialize new repository with an old tool.
*/ */
check_repository_format(); check_repository_format(&repo_fmt);
reinit = create_default_files(template_dir, original_git_dir); validate_hash_algorithm(&repo_fmt, hash);
reinit = create_default_files(template_dir, original_git_dir, &repo_fmt);
create_object_directory(); create_object_directory();
@@ -482,6 +527,8 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
const char *work_tree; const char *work_tree;
const char *template_dir = NULL; const char *template_dir = NULL;
unsigned int flags = 0; unsigned int flags = 0;
const char *object_format = NULL;
int hash_algo = GIT_HASH_UNKNOWN;
const struct option init_db_options[] = { const struct option init_db_options[] = {
OPT_STRING(0, "template", &template_dir, N_("template-directory"), OPT_STRING(0, "template", &template_dir, N_("template-directory"),
N_("directory from which templates will be used")), N_("directory from which templates will be used")),
@@ -494,6 +541,8 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
OPT_BIT('q', "quiet", &flags, N_("be quiet"), INIT_DB_QUIET), OPT_BIT('q', "quiet", &flags, N_("be quiet"), INIT_DB_QUIET),
OPT_STRING(0, "separate-git-dir", &real_git_dir, N_("gitdir"), OPT_STRING(0, "separate-git-dir", &real_git_dir, N_("gitdir"),
N_("separate git dir from working tree")), N_("separate git dir from working tree")),
OPT_STRING(0, "object-format", &object_format, N_("hash"),
N_("specify the hash algorithm to use")),
OPT_END() OPT_END()
}; };
@@ -546,6 +595,12 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
free(cwd); free(cwd);
} }
if (object_format) {
hash_algo = hash_algo_by_name(object_format);
if (hash_algo == GIT_HASH_UNKNOWN)
die(_("unknown hash algorithm '%s'"), object_format);
}
if (init_shared_repository != -1) if (init_shared_repository != -1)
set_shared_repository(init_shared_repository); set_shared_repository(init_shared_repository);
@@ -597,5 +652,5 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
UNLEAK(work_tree); UNLEAK(work_tree);
flags |= INIT_DB_EXIST_OK; flags |= INIT_DB_EXIST_OK;
return init_db(git_dir, real_git_dir, template_dir, flags); return init_db(git_dir, real_git_dir, template_dir, hash_algo, flags);
} }

View File

@@ -880,7 +880,7 @@ static void write_reused_pack_one(size_t pos, struct hashfile *out,
len = encode_in_pack_object_header(header, sizeof(header), len = encode_in_pack_object_header(header, sizeof(header),
OBJ_REF_DELTA, size); OBJ_REF_DELTA, size);
hashwrite(out, header, len); hashwrite(out, header, len);
hashwrite(out, base_oid.hash, 20); hashwrite(out, base_oid.hash, the_hash_algo->rawsz);
copy_pack_data(out, reuse_packfile, w_curs, cur, next - cur); copy_pack_data(out, reuse_packfile, w_curs, cur, next - cur);
return; return;
} }

25
cache.h
View File

@@ -627,7 +627,9 @@ int path_inside_repo(const char *prefix, const char *path);
#define INIT_DB_EXIST_OK 0x0002 #define INIT_DB_EXIST_OK 0x0002
int init_db(const char *git_dir, const char *real_git_dir, int init_db(const char *git_dir, const char *real_git_dir,
const char *template_dir, unsigned int flags); const char *template_dir, int hash_algo,
unsigned int flags);
void initialize_repository_version(int hash_algo);
void sanitize_stdfds(void); void sanitize_stdfds(void);
int daemonize(void); int daemonize(void);
@@ -1086,8 +1088,10 @@ int verify_repository_format(const struct repository_format *format,
* and die if it is a version we don't understand. Generally one would * and die if it is a version we don't understand. Generally one would
* set_git_dir() before calling this, and use it only for "are we in a valid * set_git_dir() before calling this, and use it only for "are we in a valid
* repo?". * repo?".
*
* If successful and fmt is not NULL, fill fmt with data.
*/ */
void check_repository_format(void); void check_repository_format(struct repository_format *fmt);
#define MTIME_CHANGED 0x0001 #define MTIME_CHANGED 0x0001
#define CTIME_CHANGED 0x0002 #define CTIME_CHANGED 0x0002
@@ -1479,6 +1483,9 @@ int set_disambiguate_hint_config(const char *var, const char *value);
int get_sha1_hex(const char *hex, unsigned char *sha1); int get_sha1_hex(const char *hex, unsigned char *sha1);
int get_oid_hex(const char *hex, struct object_id *sha1); int get_oid_hex(const char *hex, struct object_id *sha1);
/* Like get_oid_hex, but for an arbitrary hash algorithm. */
int get_oid_hex_algop(const char *hex, struct object_id *oid, const struct git_hash_algo *algop);
/* /*
* Read `len` pairs of hexadecimal digits from `hex` and write the * Read `len` pairs of hexadecimal digits from `hex` and write the
* values to `binary` as `len` bytes. Return 0 on success, or -1 if * values to `binary` as `len` bytes. Return 0 on success, or -1 if
@@ -1514,6 +1521,20 @@ char *oid_to_hex(const struct object_id *oid); /* same static buffer */
*/ */
int parse_oid_hex(const char *hex, struct object_id *oid, const char **end); int parse_oid_hex(const char *hex, struct object_id *oid, const char **end);
/* Like parse_oid_hex, but for an arbitrary hash algorithm. */
int parse_oid_hex_algop(const char *hex, struct object_id *oid, const char **end,
const struct git_hash_algo *algo);
/*
* These functions work like get_oid_hex and parse_oid_hex, but they will parse
* a hex value for any algorithm. The algorithm is detected based on the length
* and the algorithm in use is returned. If this is not a hex object ID in any
* algorithm, returns GIT_HASH_UNKNOWN.
*/
int get_oid_hex_any(const char *hex, struct object_id *oid);
int parse_oid_hex_any(const char *hex, struct object_id *oid, const char **end);
/* /*
* This reads short-hand syntax that not only evaluates to a commit * This reads short-hand syntax that not only evaluates to a commit
* object name, but also can act as if the end user spelled the name * object name, but also can act as if the end user spelled the name

View File

@@ -961,14 +961,22 @@ cleanup_return:
return ret; return ret;
} }
static const char gpg_sig_header[] = "gpgsig"; /*
static const int gpg_sig_header_len = sizeof(gpg_sig_header) - 1; * Indexed by hash algorithm identifier.
*/
static const char *gpg_sig_headers[] = {
NULL,
"gpgsig",
"gpgsig-sha256",
};
static int do_sign_commit(struct strbuf *buf, const char *keyid) static int do_sign_commit(struct strbuf *buf, const char *keyid)
{ {
struct strbuf sig = STRBUF_INIT; struct strbuf sig = STRBUF_INIT;
int inspos, copypos; int inspos, copypos;
const char *eoh; const char *eoh;
const char *gpg_sig_header = gpg_sig_headers[hash_algo_by_ptr(the_hash_algo)];
int gpg_sig_header_len = strlen(gpg_sig_header);
/* find the end of the header */ /* find the end of the header */
eoh = strstr(buf->buf, "\n\n"); eoh = strstr(buf->buf, "\n\n");
@@ -1010,6 +1018,8 @@ int parse_signed_commit(const struct commit *commit,
const char *buffer = get_commit_buffer(commit, &size); const char *buffer = get_commit_buffer(commit, &size);
int in_signature, saw_signature = -1; int in_signature, saw_signature = -1;
const char *line, *tail; const char *line, *tail;
const char *gpg_sig_header = gpg_sig_headers[hash_algo_by_ptr(the_hash_algo)];
int gpg_sig_header_len = strlen(gpg_sig_header);
line = buffer; line = buffer;
tail = buffer + size; tail = buffer + size;
@@ -1056,11 +1066,17 @@ int remove_signature(struct strbuf *buf)
if (in_signature && line[0] == ' ') if (in_signature && line[0] == ' ')
sig_end = next; sig_end = next;
else if (starts_with(line, gpg_sig_header) && else if (starts_with(line, "gpgsig")) {
line[gpg_sig_header_len] == ' ') { int i;
sig_start = line; for (i = 1; i < GIT_HASH_NALGOS; i++) {
sig_end = next; const char *p;
in_signature = 1; if (skip_prefix(line, gpg_sig_headers[i], &p) &&
*p == ' ') {
sig_start = line;
sig_end = next;
in_signature = 1;
}
}
} else { } else {
if (*line == '\n') if (*line == '\n')
/* dump the whole remainder of the buffer */ /* dump the whole remainder of the buffer */

View File

@@ -16,6 +16,8 @@ DEVELOPER_CFLAGS += -Wstrict-prototypes
DEVELOPER_CFLAGS += -Wunused DEVELOPER_CFLAGS += -Wunused
DEVELOPER_CFLAGS += -Wvla DEVELOPER_CFLAGS += -Wvla
DEVELOPER_CFLAGS += -DENABLE_SHA256
ifndef COMPILER_FEATURES ifndef COMPILER_FEATURES
COMPILER_FEATURES := $(shell ./detect-compiler $(CC)) COMPILER_FEATURES := $(shell ./detect-compiler $(CC))
endif endif

View File

@@ -157,7 +157,7 @@ void hashfile_checkpoint(struct hashfile *f, struct hashfile_checkpoint *checkpo
{ {
hashflush(f); hashflush(f);
checkpoint->offset = f->total; checkpoint->offset = f->total;
checkpoint->ctx = f->ctx; the_hash_algo->clone_fn(&checkpoint->ctx, &f->ctx);
} }
int hashfile_truncate(struct hashfile *f, struct hashfile_checkpoint *checkpoint) int hashfile_truncate(struct hashfile *f, struct hashfile_checkpoint *checkpoint)

View File

@@ -18,6 +18,7 @@
#include "object-store.h" #include "object-store.h"
#include "mem-pool.h" #include "mem-pool.h"
#include "commit-reach.h" #include "commit-reach.h"
#include "khash.h"
#define PACK_ID_BITS 16 #define PACK_ID_BITS 16
#define MAX_PACK_ID ((1<<PACK_ID_BITS)-1) #define MAX_PACK_ID ((1<<PACK_ID_BITS)-1)
@@ -53,6 +54,7 @@ struct object_entry_pool {
struct mark_set { struct mark_set {
union { union {
struct object_id *oids[1024];
struct object_entry *marked[1024]; struct object_entry *marked[1024];
struct mark_set *sets[1024]; struct mark_set *sets[1024];
} data; } data;
@@ -131,6 +133,9 @@ struct recent_command {
char *buf; char *buf;
}; };
typedef void (*mark_set_inserter_t)(struct mark_set *s, struct object_id *oid, uintmax_t mark);
typedef void (*each_mark_fn_t)(uintmax_t mark, void *obj, void *cbp);
/* Configured limits on output */ /* Configured limits on output */
static unsigned long max_depth = 50; static unsigned long max_depth = 50;
static off_t max_packsize; static off_t max_packsize;
@@ -222,6 +227,11 @@ static int allow_unsafe_features;
/* Signal handling */ /* Signal handling */
static volatile sig_atomic_t checkpoint_requested; static volatile sig_atomic_t checkpoint_requested;
/* Submodule marks */
static struct string_list sub_marks_from = STRING_LIST_INIT_DUP;
static struct string_list sub_marks_to = STRING_LIST_INIT_DUP;
static kh_oid_map_t *sub_oid_map;
/* Where to write output of cat-blob commands */ /* Where to write output of cat-blob commands */
static int cat_blob_fd = STDOUT_FILENO; static int cat_blob_fd = STDOUT_FILENO;
@@ -230,6 +240,29 @@ static void parse_get_mark(const char *p);
static void parse_cat_blob(const char *p); static void parse_cat_blob(const char *p);
static void parse_ls(const char *p, struct branch *b); static void parse_ls(const char *p, struct branch *b);
static void for_each_mark(struct mark_set *m, uintmax_t base, each_mark_fn_t callback, void *p)
{
uintmax_t k;
if (m->shift) {
for (k = 0; k < 1024; k++) {
if (m->data.sets[k])
for_each_mark(m->data.sets[k], base + (k << m->shift), callback, p);
}
} else {
for (k = 0; k < 1024; k++) {
if (m->data.marked[k])
callback(base + k, m->data.marked[k], p);
}
}
}
static void dump_marks_fn(uintmax_t mark, void *object, void *cbp) {
struct object_entry *e = object;
FILE *f = cbp;
fprintf(f, ":%" PRIuMAX " %s\n", mark, oid_to_hex(&e->idx.oid));
}
static void write_branch_report(FILE *rpt, struct branch *b) static void write_branch_report(FILE *rpt, struct branch *b)
{ {
fprintf(rpt, "%s:\n", b->name); fprintf(rpt, "%s:\n", b->name);
@@ -258,8 +291,6 @@ static void write_branch_report(FILE *rpt, struct branch *b)
fputc('\n', rpt); fputc('\n', rpt);
} }
static void dump_marks_helper(FILE *, uintmax_t, struct mark_set *);
static void write_crash_report(const char *err) static void write_crash_report(const char *err)
{ {
char *loc = git_pathdup("fast_import_crash_%"PRIuMAX, (uintmax_t) getpid()); char *loc = git_pathdup("fast_import_crash_%"PRIuMAX, (uintmax_t) getpid());
@@ -338,7 +369,7 @@ static void write_crash_report(const char *err)
if (export_marks_file) if (export_marks_file)
fprintf(rpt, " exported to %s\n", export_marks_file); fprintf(rpt, " exported to %s\n", export_marks_file);
else else
dump_marks_helper(rpt, 0, marks); for_each_mark(marks, 0, dump_marks_fn, rpt);
fputc('\n', rpt); fputc('\n', rpt);
fputs("-------------------\n", rpt); fputs("-------------------\n", rpt);
@@ -493,9 +524,8 @@ static char *pool_strdup(const char *s)
return r; return r;
} }
static void insert_mark(uintmax_t idnum, struct object_entry *oe) static void insert_mark(struct mark_set *s, uintmax_t idnum, struct object_entry *oe)
{ {
struct mark_set *s = marks;
while ((idnum >> s->shift) >= 1024) { while ((idnum >> s->shift) >= 1024) {
s = mem_pool_calloc(&fi_mem_pool, 1, sizeof(struct mark_set)); s = mem_pool_calloc(&fi_mem_pool, 1, sizeof(struct mark_set));
s->shift = marks->shift + 10; s->shift = marks->shift + 10;
@@ -516,10 +546,9 @@ static void insert_mark(uintmax_t idnum, struct object_entry *oe)
s->data.marked[idnum] = oe; s->data.marked[idnum] = oe;
} }
static struct object_entry *find_mark(uintmax_t idnum) static void *find_mark(struct mark_set *s, uintmax_t idnum)
{ {
uintmax_t orig_idnum = idnum; uintmax_t orig_idnum = idnum;
struct mark_set *s = marks;
struct object_entry *oe = NULL; struct object_entry *oe = NULL;
if ((idnum >> s->shift) < 1024) { if ((idnum >> s->shift) < 1024) {
while (s && s->shift) { while (s && s->shift) {
@@ -919,7 +948,7 @@ static int store_object(
e = insert_object(&oid); e = insert_object(&oid);
if (mark) if (mark)
insert_mark(mark, e); insert_mark(marks, mark, e);
if (e->idx.offset) { if (e->idx.offset) {
duplicate_count_by_type[type]++; duplicate_count_by_type[type]++;
return 1; return 1;
@@ -1117,7 +1146,7 @@ static void stream_blob(uintmax_t len, struct object_id *oidout, uintmax_t mark)
e = insert_object(&oid); e = insert_object(&oid);
if (mark) if (mark)
insert_mark(mark, e); insert_mark(marks, mark, e);
if (e->idx.offset) { if (e->idx.offset) {
duplicate_count_by_type[OBJ_BLOB]++; duplicate_count_by_type[OBJ_BLOB]++;
@@ -1655,26 +1684,6 @@ static void dump_tags(void)
strbuf_release(&err); strbuf_release(&err);
} }
static void dump_marks_helper(FILE *f,
uintmax_t base,
struct mark_set *m)
{
uintmax_t k;
if (m->shift) {
for (k = 0; k < 1024; k++) {
if (m->data.sets[k])
dump_marks_helper(f, base + (k << m->shift),
m->data.sets[k]);
}
} else {
for (k = 0; k < 1024; k++) {
if (m->data.marked[k])
fprintf(f, ":%" PRIuMAX " %s\n", base + k,
oid_to_hex(&m->data.marked[k]->idx.oid));
}
}
}
static void dump_marks(void) static void dump_marks(void)
{ {
struct lock_file mark_lock = LOCK_INIT; struct lock_file mark_lock = LOCK_INIT;
@@ -1704,7 +1713,7 @@ static void dump_marks(void)
return; return;
} }
dump_marks_helper(f, 0, marks); for_each_mark(marks, 0, dump_marks_fn, f);
if (commit_lock_file(&mark_lock)) { if (commit_lock_file(&mark_lock)) {
failure |= error_errno("Unable to write file %s", failure |= error_errno("Unable to write file %s",
export_marks_file); export_marks_file);
@@ -1712,21 +1721,38 @@ static void dump_marks(void)
} }
} }
static void read_marks(void) static void insert_object_entry(struct mark_set *s, struct object_id *oid, uintmax_t mark)
{
struct object_entry *e;
e = find_object(oid);
if (!e) {
enum object_type type = oid_object_info(the_repository,
oid, NULL);
if (type < 0)
die("object not found: %s", oid_to_hex(oid));
e = insert_object(oid);
e->type = type;
e->pack_id = MAX_PACK_ID;
e->idx.offset = 1; /* just not zero! */
}
insert_mark(s, mark, e);
}
static void insert_oid_entry(struct mark_set *s, struct object_id *oid, uintmax_t mark)
{
insert_mark(s, mark, xmemdupz(oid, sizeof(*oid)));
}
static void read_mark_file(struct mark_set *s, FILE *f, mark_set_inserter_t inserter)
{ {
char line[512]; char line[512];
FILE *f = fopen(import_marks_file, "r");
if (f)
;
else if (import_marks_file_ignore_missing && errno == ENOENT)
goto done; /* Marks file does not exist */
else
die_errno("cannot read '%s'", import_marks_file);
while (fgets(line, sizeof(line), f)) { while (fgets(line, sizeof(line), f)) {
uintmax_t mark; uintmax_t mark;
char *end; char *end;
struct object_id oid; struct object_id oid;
struct object_entry *e;
/* Ensure SHA-1 objects are padded with zeros. */
memset(oid.hash, 0, sizeof(oid.hash));
end = strchr(line, '\n'); end = strchr(line, '\n');
if (line[0] != ':' || !end) if (line[0] != ':' || !end)
@@ -1734,21 +1760,23 @@ static void read_marks(void)
*end = 0; *end = 0;
mark = strtoumax(line + 1, &end, 10); mark = strtoumax(line + 1, &end, 10);
if (!mark || end == line + 1 if (!mark || end == line + 1
|| *end != ' ' || get_oid_hex(end + 1, &oid)) || *end != ' '
|| get_oid_hex_any(end + 1, &oid) == GIT_HASH_UNKNOWN)
die("corrupt mark line: %s", line); die("corrupt mark line: %s", line);
e = find_object(&oid); inserter(s, &oid, mark);
if (!e) {
enum object_type type = oid_object_info(the_repository,
&oid, NULL);
if (type < 0)
die("object not found: %s", oid_to_hex(&oid));
e = insert_object(&oid);
e->type = type;
e->pack_id = MAX_PACK_ID;
e->idx.offset = 1; /* just not zero! */
}
insert_mark(mark, e);
} }
}
static void read_marks(void)
{
FILE *f = fopen(import_marks_file, "r");
if (f)
;
else if (import_marks_file_ignore_missing && errno == ENOENT)
goto done; /* Marks file does not exist */
else
die_errno("cannot read '%s'", import_marks_file);
read_mark_file(marks, f, insert_object_entry);
fclose(f); fclose(f);
done: done:
import_marks_file_done = 1; import_marks_file_done = 1;
@@ -2134,6 +2162,30 @@ static uintmax_t change_note_fanout(struct tree_entry *root,
return do_change_note_fanout(root, root, hex_oid, 0, path, 0, fanout); return do_change_note_fanout(root, root, hex_oid, 0, path, 0, fanout);
} }
static int parse_mapped_oid_hex(const char *hex, struct object_id *oid, const char **end)
{
int algo;
khiter_t it;
/* Make SHA-1 object IDs have all-zero padding. */
memset(oid->hash, 0, sizeof(oid->hash));
algo = parse_oid_hex_any(hex, oid, end);
if (algo == GIT_HASH_UNKNOWN)
return -1;
it = kh_get_oid_map(sub_oid_map, *oid);
/* No such object? */
if (it == kh_end(sub_oid_map)) {
/* If we're using the same algorithm, pass it through. */
if (hash_algos[algo].format_id == the_hash_algo->format_id)
return 0;
return -1;
}
oidcpy(oid, kh_value(sub_oid_map, it));
return 0;
}
/* /*
* Given a pointer into a string, parse a mark reference: * Given a pointer into a string, parse a mark reference:
* *
@@ -2214,13 +2266,13 @@ static void file_change_m(const char *p, struct branch *b)
} }
if (*p == ':') { if (*p == ':') {
oe = find_mark(parse_mark_ref_space(&p)); oe = find_mark(marks, parse_mark_ref_space(&p));
oidcpy(&oid, &oe->idx.oid); oidcpy(&oid, &oe->idx.oid);
} else if (skip_prefix(p, "inline ", &p)) { } else if (skip_prefix(p, "inline ", &p)) {
inline_data = 1; inline_data = 1;
oe = NULL; /* not used with inline_data, but makes gcc happy */ oe = NULL; /* not used with inline_data, but makes gcc happy */
} else { } else {
if (parse_oid_hex(p, &oid, &p)) if (parse_mapped_oid_hex(p, &oid, &p))
die("Invalid dataref: %s", command_buf.buf); die("Invalid dataref: %s", command_buf.buf);
oe = find_object(&oid); oe = find_object(&oid);
if (*p++ != ' ') if (*p++ != ' ')
@@ -2388,13 +2440,13 @@ static void note_change_n(const char *p, struct branch *b, unsigned char *old_fa
/* Now parse the notemodify command. */ /* Now parse the notemodify command. */
/* <dataref> or 'inline' */ /* <dataref> or 'inline' */
if (*p == ':') { if (*p == ':') {
oe = find_mark(parse_mark_ref_space(&p)); oe = find_mark(marks, parse_mark_ref_space(&p));
oidcpy(&oid, &oe->idx.oid); oidcpy(&oid, &oe->idx.oid);
} else if (skip_prefix(p, "inline ", &p)) { } else if (skip_prefix(p, "inline ", &p)) {
inline_data = 1; inline_data = 1;
oe = NULL; /* not used with inline_data, but makes gcc happy */ oe = NULL; /* not used with inline_data, but makes gcc happy */
} else { } else {
if (parse_oid_hex(p, &oid, &p)) if (parse_mapped_oid_hex(p, &oid, &p))
die("Invalid dataref: %s", command_buf.buf); die("Invalid dataref: %s", command_buf.buf);
oe = find_object(&oid); oe = find_object(&oid);
if (*p++ != ' ') if (*p++ != ' ')
@@ -2409,7 +2461,7 @@ static void note_change_n(const char *p, struct branch *b, unsigned char *old_fa
oidcpy(&commit_oid, &s->oid); oidcpy(&commit_oid, &s->oid);
} else if (*p == ':') { } else if (*p == ':') {
uintmax_t commit_mark = parse_mark_ref_eol(p); uintmax_t commit_mark = parse_mark_ref_eol(p);
struct object_entry *commit_oe = find_mark(commit_mark); struct object_entry *commit_oe = find_mark(marks, commit_mark);
if (commit_oe->type != OBJ_COMMIT) if (commit_oe->type != OBJ_COMMIT)
die("Mark :%" PRIuMAX " not a commit", commit_mark); die("Mark :%" PRIuMAX " not a commit", commit_mark);
oidcpy(&commit_oid, &commit_oe->idx.oid); oidcpy(&commit_oid, &commit_oe->idx.oid);
@@ -2513,7 +2565,7 @@ static int parse_objectish(struct branch *b, const char *objectish)
oidcpy(&b->branch_tree.versions[1].oid, t); oidcpy(&b->branch_tree.versions[1].oid, t);
} else if (*objectish == ':') { } else if (*objectish == ':') {
uintmax_t idnum = parse_mark_ref_eol(objectish); uintmax_t idnum = parse_mark_ref_eol(objectish);
struct object_entry *oe = find_mark(idnum); struct object_entry *oe = find_mark(marks, idnum);
if (oe->type != OBJ_COMMIT) if (oe->type != OBJ_COMMIT)
die("Mark :%" PRIuMAX " not a commit", idnum); die("Mark :%" PRIuMAX " not a commit", idnum);
if (!oideq(&b->oid, &oe->idx.oid)) { if (!oideq(&b->oid, &oe->idx.oid)) {
@@ -2577,7 +2629,7 @@ static struct hash_list *parse_merge(unsigned int *count)
oidcpy(&n->oid, &s->oid); oidcpy(&n->oid, &s->oid);
else if (*from == ':') { else if (*from == ':') {
uintmax_t idnum = parse_mark_ref_eol(from); uintmax_t idnum = parse_mark_ref_eol(from);
struct object_entry *oe = find_mark(idnum); struct object_entry *oe = find_mark(marks, idnum);
if (oe->type != OBJ_COMMIT) if (oe->type != OBJ_COMMIT)
die("Mark :%" PRIuMAX " not a commit", idnum); die("Mark :%" PRIuMAX " not a commit", idnum);
oidcpy(&n->oid, &oe->idx.oid); oidcpy(&n->oid, &oe->idx.oid);
@@ -2751,7 +2803,7 @@ static void parse_new_tag(const char *arg)
} else if (*from == ':') { } else if (*from == ':') {
struct object_entry *oe; struct object_entry *oe;
from_mark = parse_mark_ref_eol(from); from_mark = parse_mark_ref_eol(from);
oe = find_mark(from_mark); oe = find_mark(marks, from_mark);
type = oe->type; type = oe->type;
oidcpy(&oid, &oe->idx.oid); oidcpy(&oid, &oe->idx.oid);
} else if (!get_oid(from, &oid)) { } else if (!get_oid(from, &oid)) {
@@ -2909,7 +2961,7 @@ static void parse_get_mark(const char *p)
if (*p != ':') if (*p != ':')
die("Not a mark: %s", p); die("Not a mark: %s", p);
oe = find_mark(parse_mark_ref_eol(p)); oe = find_mark(marks, parse_mark_ref_eol(p));
if (!oe) if (!oe)
die("Unknown mark: %s", command_buf.buf); die("Unknown mark: %s", command_buf.buf);
@@ -2924,12 +2976,12 @@ static void parse_cat_blob(const char *p)
/* cat-blob SP <object> LF */ /* cat-blob SP <object> LF */
if (*p == ':') { if (*p == ':') {
oe = find_mark(parse_mark_ref_eol(p)); oe = find_mark(marks, parse_mark_ref_eol(p));
if (!oe) if (!oe)
die("Unknown mark: %s", command_buf.buf); die("Unknown mark: %s", command_buf.buf);
oidcpy(&oid, &oe->idx.oid); oidcpy(&oid, &oe->idx.oid);
} else { } else {
if (parse_oid_hex(p, &oid, &p)) if (parse_mapped_oid_hex(p, &oid, &p))
die("Invalid dataref: %s", command_buf.buf); die("Invalid dataref: %s", command_buf.buf);
if (*p) if (*p)
die("Garbage after SHA1: %s", command_buf.buf); die("Garbage after SHA1: %s", command_buf.buf);
@@ -2993,18 +3045,54 @@ static struct object_entry *dereference(struct object_entry *oe,
return find_object(oid); return find_object(oid);
} }
static void insert_mapped_mark(uintmax_t mark, void *object, void *cbp)
{
struct object_id *fromoid = object;
struct object_id *tooid = find_mark(cbp, mark);
int ret;
khiter_t it;
it = kh_put_oid_map(sub_oid_map, *fromoid, &ret);
/* We've already seen this object. */
if (ret == 0)
return;
kh_value(sub_oid_map, it) = tooid;
}
static void build_mark_map_one(struct mark_set *from, struct mark_set *to)
{
for_each_mark(from, 0, insert_mapped_mark, to);
}
static void build_mark_map(struct string_list *from, struct string_list *to)
{
struct string_list_item *fromp, *top;
sub_oid_map = kh_init_oid_map();
for_each_string_list_item(fromp, from) {
top = string_list_lookup(to, fromp->string);
if (!fromp->util) {
die(_("Missing from marks for submodule '%s'"), fromp->string);
} else if (!top || !top->util) {
die(_("Missing to marks for submodule '%s'"), fromp->string);
}
build_mark_map_one(fromp->util, top->util);
}
}
static struct object_entry *parse_treeish_dataref(const char **p) static struct object_entry *parse_treeish_dataref(const char **p)
{ {
struct object_id oid; struct object_id oid;
struct object_entry *e; struct object_entry *e;
if (**p == ':') { /* <mark> */ if (**p == ':') { /* <mark> */
e = find_mark(parse_mark_ref_space(p)); e = find_mark(marks, parse_mark_ref_space(p));
if (!e) if (!e)
die("Unknown mark: %s", command_buf.buf); die("Unknown mark: %s", command_buf.buf);
oidcpy(&oid, &e->idx.oid); oidcpy(&oid, &e->idx.oid);
} else { /* <sha1> */ } else { /* <sha1> */
if (parse_oid_hex(*p, &oid, p)) if (parse_mapped_oid_hex(*p, &oid, p))
die("Invalid dataref: %s", command_buf.buf); die("Invalid dataref: %s", command_buf.buf);
e = find_object(&oid); e = find_object(&oid);
if (*(*p)++ != ' ') if (*(*p)++ != ' ')
@@ -3130,7 +3218,7 @@ static void parse_alias(void)
die(_("Expected 'to' command, got %s"), command_buf.buf); die(_("Expected 'to' command, got %s"), command_buf.buf);
e = find_object(&b.oid); e = find_object(&b.oid);
assert(e); assert(e);
insert_mark(next_mark, e); insert_mark(marks, next_mark, e);
} }
static char* make_fast_import_path(const char *path) static char* make_fast_import_path(const char *path)
@@ -3210,6 +3298,26 @@ static void option_export_pack_edges(const char *edges)
pack_edges = xfopen(edges, "a"); pack_edges = xfopen(edges, "a");
} }
static void option_rewrite_submodules(const char *arg, struct string_list *list)
{
struct mark_set *ms;
FILE *fp;
char *s = xstrdup(arg);
char *f = strchr(s, ':');
if (!f)
die(_("Expected format name:filename for submodule rewrite option"));
*f = '\0';
f++;
ms = xcalloc(1, sizeof(*ms));
string_list_insert(list, s)->util = ms;
fp = fopen(f, "r");
if (!fp)
die_errno("cannot read '%s'", f);
read_mark_file(ms, fp, insert_oid_entry);
fclose(fp);
}
static int parse_one_option(const char *option) static int parse_one_option(const char *option)
{ {
if (skip_prefix(option, "max-pack-size=", &option)) { if (skip_prefix(option, "max-pack-size=", &option)) {
@@ -3272,6 +3380,11 @@ static int parse_one_feature(const char *feature, int from_stream)
option_export_marks(arg); option_export_marks(arg);
} else if (!strcmp(feature, "alias")) { } else if (!strcmp(feature, "alias")) {
; /* Don't die - this feature is supported */ ; /* Don't die - this feature is supported */
} else if (skip_prefix(feature, "rewrite-submodules-to=", &arg)) {
option_rewrite_submodules(arg, &sub_marks_to);
} else if (skip_prefix(feature, "rewrite-submodules-from=", &arg)) {
option_rewrite_submodules(arg, &sub_marks_from);
} else if (skip_prefix(feature, "rewrite-submodules-from=", &arg)) {
} else if (!strcmp(feature, "get-mark")) { } else if (!strcmp(feature, "get-mark")) {
; /* Don't die - this feature is supported */ ; /* Don't die - this feature is supported */
} else if (!strcmp(feature, "cat-blob")) { } else if (!strcmp(feature, "cat-blob")) {
@@ -3377,6 +3490,7 @@ static void parse_argv(void)
seen_data_command = 1; seen_data_command = 1;
if (import_marks_file) if (import_marks_file)
read_marks(); read_marks();
build_mark_map(&sub_marks_from, &sub_marks_to);
} }
int cmd_main(int argc, const char **argv) int cmd_main(int argc, const char **argv)

21
hash.h
View File

@@ -16,6 +16,7 @@
#endif #endif
#if defined(SHA256_GCRYPT) #if defined(SHA256_GCRYPT)
#define SHA256_NEEDS_CLONE_HELPER
#include "sha256/gcrypt.h" #include "sha256/gcrypt.h"
#elif defined(SHA256_OPENSSL) #elif defined(SHA256_OPENSSL)
#include <openssl/sha.h> #include <openssl/sha.h>
@@ -54,12 +55,28 @@
#define git_SHA256_Update platform_SHA256_Update #define git_SHA256_Update platform_SHA256_Update
#define git_SHA256_Final platform_SHA256_Final #define git_SHA256_Final platform_SHA256_Final
#ifdef platform_SHA256_Clone
#define git_SHA256_Clone platform_SHA256_Clone
#endif
#ifdef SHA1_MAX_BLOCK_SIZE #ifdef SHA1_MAX_BLOCK_SIZE
#include "compat/sha1-chunked.h" #include "compat/sha1-chunked.h"
#undef git_SHA1_Update #undef git_SHA1_Update
#define git_SHA1_Update git_SHA1_Update_Chunked #define git_SHA1_Update git_SHA1_Update_Chunked
#endif #endif
static inline void git_SHA1_Clone(git_SHA_CTX *dst, const git_SHA_CTX *src)
{
memcpy(dst, src, sizeof(*dst));
}
#ifndef SHA256_NEEDS_CLONE_HELPER
static inline void git_SHA256_Clone(git_SHA256_CTX *dst, const git_SHA256_CTX *src)
{
memcpy(dst, src, sizeof(*dst));
}
#endif
/* /*
* Note that these constants are suitable for indexing the hash_algos array and * Note that these constants are suitable for indexing the hash_algos array and
* comparing against each other, but are otherwise arbitrary, so they should not * comparing against each other, but are otherwise arbitrary, so they should not
@@ -85,6 +102,7 @@ union git_hash_ctx {
typedef union git_hash_ctx git_hash_ctx; typedef union git_hash_ctx git_hash_ctx;
typedef void (*git_hash_init_fn)(git_hash_ctx *ctx); typedef void (*git_hash_init_fn)(git_hash_ctx *ctx);
typedef void (*git_hash_clone_fn)(git_hash_ctx *dst, const git_hash_ctx *src);
typedef void (*git_hash_update_fn)(git_hash_ctx *ctx, const void *in, size_t len); typedef void (*git_hash_update_fn)(git_hash_ctx *ctx, const void *in, size_t len);
typedef void (*git_hash_final_fn)(unsigned char *hash, git_hash_ctx *ctx); typedef void (*git_hash_final_fn)(unsigned char *hash, git_hash_ctx *ctx);
@@ -110,6 +128,9 @@ struct git_hash_algo {
/* The hash initialization function. */ /* The hash initialization function. */
git_hash_init_fn init_fn; git_hash_init_fn init_fn;
/* The hash context cloning function. */
git_hash_clone_fn clone_fn;
/* The hash update function. */ /* The hash update function. */
git_hash_update_fn update_fn; git_hash_update_fn update_fn;

57
hex.c
View File

@@ -47,30 +47,71 @@ int hex_to_bytes(unsigned char *binary, const char *hex, size_t len)
return 0; return 0;
} }
int get_sha1_hex(const char *hex, unsigned char *sha1) static int get_hash_hex_algop(const char *hex, unsigned char *hash,
const struct git_hash_algo *algop)
{ {
int i; int i;
for (i = 0; i < the_hash_algo->rawsz; i++) { for (i = 0; i < algop->rawsz; i++) {
int val = hex2chr(hex); int val = hex2chr(hex);
if (val < 0) if (val < 0)
return -1; return -1;
*sha1++ = val; *hash++ = val;
hex += 2; hex += 2;
} }
return 0; return 0;
} }
int get_sha1_hex(const char *hex, unsigned char *sha1)
{
return get_hash_hex_algop(hex, sha1, the_hash_algo);
}
int get_oid_hex_algop(const char *hex, struct object_id *oid,
const struct git_hash_algo *algop)
{
return get_hash_hex_algop(hex, oid->hash, algop);
}
/*
* NOTE: This function relies on hash algorithms being in order from shortest
* length to longest length.
*/
int get_oid_hex_any(const char *hex, struct object_id *oid)
{
int i;
for (i = GIT_HASH_NALGOS - 1; i > 0; i--) {
if (!get_hash_hex_algop(hex, oid->hash, &hash_algos[i]))
return i;
}
return GIT_HASH_UNKNOWN;
}
int get_oid_hex(const char *hex, struct object_id *oid) int get_oid_hex(const char *hex, struct object_id *oid)
{ {
return get_sha1_hex(hex, oid->hash); return get_oid_hex_algop(hex, oid, the_hash_algo);
}
int parse_oid_hex_algop(const char *hex, struct object_id *oid,
const char **end,
const struct git_hash_algo *algop)
{
int ret = get_hash_hex_algop(hex, oid->hash, algop);
if (!ret)
*end = hex + algop->hexsz;
return ret;
}
int parse_oid_hex_any(const char *hex, struct object_id *oid, const char **end)
{
int ret = get_oid_hex_any(hex, oid);
if (ret)
*end = hex + hash_algos[ret].hexsz;
return ret;
} }
int parse_oid_hex(const char *hex, struct object_id *oid, const char **end) int parse_oid_hex(const char *hex, struct object_id *oid, const char **end)
{ {
int ret = get_oid_hex(hex, oid); return parse_oid_hex_algop(hex, oid, end, the_hash_algo);
if (!ret)
*end = hex + the_hash_algo->hexsz;
return ret;
} }
char *hash_to_hex_algop_r(char *buffer, const unsigned char *hash, char *hash_to_hex_algop_r(char *buffer, const unsigned char *hash,

2
path.c
View File

@@ -851,7 +851,7 @@ const char *enter_repo(const char *path, int strict)
if (is_git_directory(".")) { if (is_git_directory(".")) {
set_git_dir(".", 0); set_git_dir(".", 0);
check_repository_format(); check_repository_format(NULL);
return path; return path;
} }

View File

@@ -89,6 +89,10 @@ void repo_set_gitdir(struct repository *repo,
void repo_set_hash_algo(struct repository *repo, int hash_algo) void repo_set_hash_algo(struct repository *repo, int hash_algo)
{ {
repo->hash_algo = &hash_algos[hash_algo]; repo->hash_algo = &hash_algos[hash_algo];
#ifndef ENABLE_SHA256
if (hash_algo != GIT_HASH_SHA1)
die(_("The hash algorithm %s is not supported in this build."), repo->hash_algo->name);
#endif
} }
/* /*

View File

@@ -1323,7 +1323,7 @@ static int try_to_commit(struct repository *r,
return -1; return -1;
if (flags & AMEND_MSG) { if (flags & AMEND_MSG) {
const char *exclude_gpgsig[] = { "gpgsig", NULL }; const char *exclude_gpgsig[] = { "gpgsig", "gpgsig-sha256", NULL };
const char *out_enc = get_commit_output_encoding(); const char *out_enc = get_commit_output_encoding();
const char *message = logmsg_reencode(current_head, NULL, const char *message = logmsg_reencode(current_head, NULL,
out_enc); out_enc);

View File

@@ -1266,10 +1266,12 @@ int git_config_perm(const char *var, const char *value)
return -(i & 0666); return -(i & 0666);
} }
void check_repository_format(void) void check_repository_format(struct repository_format *fmt)
{ {
struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT; struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
check_repository_format_gently(get_git_dir(), &repo_fmt, NULL); if (!fmt)
fmt = &repo_fmt;
check_repository_format_gently(get_git_dir(), fmt, NULL);
startup_info->have_repository = 1; startup_info->have_repository = 1;
clear_repository_format(&repo_fmt); clear_repository_format(&repo_fmt);
} }

View File

@@ -74,6 +74,11 @@ static void git_hash_sha1_init(git_hash_ctx *ctx)
git_SHA1_Init(&ctx->sha1); git_SHA1_Init(&ctx->sha1);
} }
static void git_hash_sha1_clone(git_hash_ctx *dst, const git_hash_ctx *src)
{
git_SHA1_Clone(&dst->sha1, &src->sha1);
}
static void git_hash_sha1_update(git_hash_ctx *ctx, const void *data, size_t len) static void git_hash_sha1_update(git_hash_ctx *ctx, const void *data, size_t len)
{ {
git_SHA1_Update(&ctx->sha1, data, len); git_SHA1_Update(&ctx->sha1, data, len);
@@ -90,6 +95,11 @@ static void git_hash_sha256_init(git_hash_ctx *ctx)
git_SHA256_Init(&ctx->sha256); git_SHA256_Init(&ctx->sha256);
} }
static void git_hash_sha256_clone(git_hash_ctx *dst, const git_hash_ctx *src)
{
git_SHA256_Clone(&dst->sha256, &src->sha256);
}
static void git_hash_sha256_update(git_hash_ctx *ctx, const void *data, size_t len) static void git_hash_sha256_update(git_hash_ctx *ctx, const void *data, size_t len)
{ {
git_SHA256_Update(&ctx->sha256, data, len); git_SHA256_Update(&ctx->sha256, data, len);
@@ -105,6 +115,11 @@ static void git_hash_unknown_init(git_hash_ctx *ctx)
BUG("trying to init unknown hash"); BUG("trying to init unknown hash");
} }
static void git_hash_unknown_clone(git_hash_ctx *dst, const git_hash_ctx *src)
{
BUG("trying to clone unknown hash");
}
static void git_hash_unknown_update(git_hash_ctx *ctx, const void *data, size_t len) static void git_hash_unknown_update(git_hash_ctx *ctx, const void *data, size_t len)
{ {
BUG("trying to update unknown hash"); BUG("trying to update unknown hash");
@@ -123,6 +138,7 @@ const struct git_hash_algo hash_algos[GIT_HASH_NALGOS] = {
0, 0,
0, 0,
git_hash_unknown_init, git_hash_unknown_init,
git_hash_unknown_clone,
git_hash_unknown_update, git_hash_unknown_update,
git_hash_unknown_final, git_hash_unknown_final,
NULL, NULL,
@@ -136,6 +152,7 @@ const struct git_hash_algo hash_algos[GIT_HASH_NALGOS] = {
GIT_SHA1_HEXSZ, GIT_SHA1_HEXSZ,
GIT_SHA1_BLKSZ, GIT_SHA1_BLKSZ,
git_hash_sha1_init, git_hash_sha1_init,
git_hash_sha1_clone,
git_hash_sha1_update, git_hash_sha1_update,
git_hash_sha1_final, git_hash_sha1_final,
&empty_tree_oid, &empty_tree_oid,
@@ -149,6 +166,7 @@ const struct git_hash_algo hash_algos[GIT_HASH_NALGOS] = {
GIT_SHA256_HEXSZ, GIT_SHA256_HEXSZ,
GIT_SHA256_BLKSZ, GIT_SHA256_BLKSZ,
git_hash_sha256_init, git_hash_sha256_init,
git_hash_sha256_clone,
git_hash_sha256_update, git_hash_sha256_update,
git_hash_sha256_final, git_hash_sha256_final,
&empty_tree_oid_sha256, &empty_tree_oid_sha256,

View File

@@ -22,8 +22,14 @@ inline void gcrypt_SHA256_Final(unsigned char *digest, gcrypt_SHA256_CTX *ctx)
memcpy(digest, gcry_md_read(*ctx, GCRY_MD_SHA256), SHA256_DIGEST_SIZE); memcpy(digest, gcry_md_read(*ctx, GCRY_MD_SHA256), SHA256_DIGEST_SIZE);
} }
inline void gcrypt_SHA256_Clone(gcrypt_SHA256_CTX *dst, const gcrypt_SHA256_CTX *src)
{
gcry_md_copy(dst, *src);
}
#define platform_SHA256_CTX gcrypt_SHA256_CTX #define platform_SHA256_CTX gcrypt_SHA256_CTX
#define platform_SHA256_Init gcrypt_SHA256_Init #define platform_SHA256_Init gcrypt_SHA256_Init
#define platform_SHA256_Clone gcrypt_SHA256_Clone
#define platform_SHA256_Update gcrypt_SHA256_Update #define platform_SHA256_Update gcrypt_SHA256_Update
#define platform_SHA256_Final gcrypt_SHA256_Final #define platform_SHA256_Final gcrypt_SHA256_Final

View File

@@ -13,6 +13,8 @@ int cmd__dump_split_index(int ac, const char **av)
struct split_index *si; struct split_index *si;
int i; int i;
setup_git_directory();
do_read_index(&the_index, av[1], 1); do_read_index(&the_index, av[1], 1);
printf("own %s\n", oid_to_hex(&the_index.oid)); printf("own %s\n", oid_to_hex(&the_index.oid));
si = the_index.split_index; si = the_index.split_index;

View File

@@ -19,12 +19,11 @@ static void test_parse_commit_in_graph(const char *gitdir, const char *worktree,
memset(the_repository, 0, sizeof(*the_repository)); memset(the_repository, 0, sizeof(*the_repository));
/* TODO: Needed for temporary hack in hashcmp, see 183a638b7da. */
repo_set_hash_algo(the_repository, GIT_HASH_SHA1);
if (repo_init(&r, gitdir, worktree)) if (repo_init(&r, gitdir, worktree))
die("Couldn't init repo"); die("Couldn't init repo");
repo_set_hash_algo(the_repository, hash_algo_by_ptr(r.hash_algo));
c = lookup_commit(&r, commit_oid); c = lookup_commit(&r, commit_oid);
if (!parse_commit_in_graph(&r, c)) if (!parse_commit_in_graph(&r, c))
@@ -50,12 +49,11 @@ static void test_get_commit_tree_in_graph(const char *gitdir,
memset(the_repository, 0, sizeof(*the_repository)); memset(the_repository, 0, sizeof(*the_repository));
/* TODO: Needed for temporary hack in hashcmp, see 183a638b7da. */
repo_set_hash_algo(the_repository, GIT_HASH_SHA1);
if (repo_init(&r, gitdir, worktree)) if (repo_init(&r, gitdir, worktree))
die("Couldn't init repo"); die("Couldn't init repo");
repo_set_hash_algo(the_repository, hash_algo_by_ptr(r.hash_algo));
c = lookup_commit(&r, commit_oid); c = lookup_commit(&r, commit_oid);
/* /*
@@ -75,6 +73,10 @@ static void test_get_commit_tree_in_graph(const char *gitdir,
int cmd__repository(int argc, const char **argv) int cmd__repository(int argc, const char **argv)
{ {
int nongit_ok = 0;
setup_git_directory_gently(&nongit_ok);
if (argc < 2) if (argc < 2)
die("must have at least 2 arguments"); die("must have at least 2 arguments");
if (!strcmp(argv[1], "parse_commit_in_graph")) { if (!strcmp(argv[1], "parse_commit_in_graph")) {

View File

@@ -133,6 +133,30 @@ test_expect_success 'other worktree HEAD link pointing at a funny place' '
test_i18ngrep "worktrees/other/HEAD points to something strange" out test_i18ngrep "worktrees/other/HEAD points to something strange" out
' '
test_expect_success 'commit with multiple signatures is okay' '
git cat-file commit HEAD >basis &&
cat >sigs <<-EOF &&
gpgsig -----BEGIN PGP SIGNATURE-----
VGhpcyBpcyBub3QgcmVhbGx5IGEgc2lnbmF0dXJlLg==
-----END PGP SIGNATURE-----
gpgsig-sha256 -----BEGIN PGP SIGNATURE-----
VGhpcyBpcyBub3QgcmVhbGx5IGEgc2lnbmF0dXJlLg==
-----END PGP SIGNATURE-----
EOF
sed -e "/^committer/q" basis >okay &&
cat sigs >>okay &&
echo >>okay &&
sed -e "1,/^$/d" basis >>okay &&
cat okay &&
new=$(git hash-object -t commit -w --stdin <okay) &&
test_when_finished "remove_object $new" &&
git update-ref refs/heads/bogus "$new" &&
test_when_finished "git update-ref -d refs/heads/bogus" &&
git fsck 2>out &&
cat out &&
! grep "commit $new" out
'
test_expect_success 'email without @ is okay' ' test_expect_success 'email without @ is okay' '
git cat-file commit HEAD >basis && git cat-file commit HEAD >basis &&
sed "s/@/AT/" basis >okay && sed "s/@/AT/" basis >okay &&

View File

@@ -20,6 +20,10 @@ setdate_and_increment () {
} }
test_expect_success setup ' test_expect_success setup '
test_oid_cache <<-EOF &&
disklen sha1:138
disklen sha256:154
EOF
setdate_and_increment && setdate_and_increment &&
echo "Using $datestamp" > one && echo "Using $datestamp" > one &&
git add one && git add one &&
@@ -50,6 +54,9 @@ test_atom() {
" "
} }
hexlen=$(test_oid hexsz)
disklen=$(test_oid disklen)
test_atom head refname refs/heads/master test_atom head refname refs/heads/master
test_atom head refname: refs/heads/master test_atom head refname: refs/heads/master
test_atom head refname:short master test_atom head refname:short master
@@ -82,9 +89,9 @@ test_atom head push:rstrip=-1 refs
test_atom head push:strip=1 remotes/myfork/master test_atom head push:strip=1 remotes/myfork/master
test_atom head push:strip=-1 master test_atom head push:strip=-1 master
test_atom head objecttype commit test_atom head objecttype commit
test_atom head objectsize 171 test_atom head objectsize $((131 + hexlen))
test_atom head objectsize:disk 138 test_atom head objectsize:disk $disklen
test_atom head deltabase 0000000000000000000000000000000000000000 test_atom head deltabase $ZERO_OID
test_atom head objectname $(git rev-parse refs/heads/master) test_atom head objectname $(git rev-parse refs/heads/master)
test_atom head objectname:short $(git rev-parse --short refs/heads/master) test_atom head objectname:short $(git rev-parse --short refs/heads/master)
test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/master) test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/master)
@@ -125,11 +132,11 @@ test_atom tag refname:short testtag
test_atom tag upstream '' test_atom tag upstream ''
test_atom tag push '' test_atom tag push ''
test_atom tag objecttype tag test_atom tag objecttype tag
test_atom tag objectsize 154 test_atom tag objectsize $((114 + hexlen))
test_atom tag objectsize:disk 138 test_atom tag objectsize:disk $disklen
test_atom tag '*objectsize:disk' 138 test_atom tag '*objectsize:disk' $disklen
test_atom tag deltabase 0000000000000000000000000000000000000000 test_atom tag deltabase $ZERO_OID
test_atom tag '*deltabase' 0000000000000000000000000000000000000000 test_atom tag '*deltabase' $ZERO_OID
test_atom tag objectname $(git rev-parse refs/tags/testtag) test_atom tag objectname $(git rev-parse refs/tags/testtag)
test_atom tag objectname:short $(git rev-parse --short refs/tags/testtag) test_atom tag objectname:short $(git rev-parse --short refs/tags/testtag)
test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/master) test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/master)
@@ -139,7 +146,7 @@ test_atom tag parent ''
test_atom tag numparent '' test_atom tag numparent ''
test_atom tag object $(git rev-parse refs/tags/testtag^0) test_atom tag object $(git rev-parse refs/tags/testtag^0)
test_atom tag type 'commit' test_atom tag type 'commit'
test_atom tag '*objectname' 'ea122842f48be4afb2d1fc6a4b96c05885ab7463' test_atom tag '*objectname' $(git rev-parse refs/tags/testtag^{})
test_atom tag '*objecttype' 'commit' test_atom tag '*objecttype' 'commit'
test_atom tag author '' test_atom tag author ''
test_atom tag authorname '' test_atom tag authorname ''
@@ -643,7 +650,7 @@ test_atom refs/tags/signed-long contents "subject line
body contents body contents
$sig" $sig"
cat >expected <<EOF sort >expected <<EOF
$(git rev-parse refs/tags/bogo) <committer@example.com> refs/tags/bogo $(git rev-parse refs/tags/bogo) <committer@example.com> refs/tags/bogo
$(git rev-parse refs/tags/master) <committer@example.com> refs/tags/master $(git rev-parse refs/tags/master) <committer@example.com> refs/tags/master
EOF EOF

View File

@@ -6,6 +6,11 @@ GNUPGHOME_NOT_USED=$GNUPGHOME
. "$TEST_DIRECTORY/lib-gpg.sh" . "$TEST_DIRECTORY/lib-gpg.sh"
test_expect_success GPG 'create signed commits' ' test_expect_success GPG 'create signed commits' '
test_oid_cache <<-\EOF &&
header sha1:gpgsig
header sha256:gpgsig-sha256
EOF
test_when_finished "test_unconfig commit.gpgsign" && test_when_finished "test_unconfig commit.gpgsign" &&
echo 1 >file && git add file && echo 1 >file && git add file &&
@@ -155,6 +160,11 @@ test_expect_success GPG 'verify signatures with --raw' '
) )
' '
test_expect_success GPG 'proper header is used for hash algorithm' '
git cat-file commit fourth-signed >output &&
grep "^$(test_oid header) -----BEGIN PGP SIGNATURE-----" output
'
test_expect_success GPG 'show signed commit with signature' ' test_expect_success GPG 'show signed commit with signature' '
git show -s initial >commit && git show -s initial >commit &&
git show -s --show-signature initial >show && git show -s --show-signature initial >show &&
@@ -162,7 +172,7 @@ test_expect_success GPG 'show signed commit with signature' '
git cat-file commit initial >cat && git cat-file commit initial >cat &&
grep -v -e "gpg: " -e "Warning: " show >show.commit && grep -v -e "gpg: " -e "Warning: " show >show.commit &&
grep -e "gpg: " -e "Warning: " show >show.gpg && grep -e "gpg: " -e "Warning: " show >show.gpg &&
grep -v "^ " cat | grep -v "^gpgsig " >cat.commit && grep -v "^ " cat | grep -v "^$(test_oid header) " >cat.commit &&
test_cmp show.commit commit && test_cmp show.commit commit &&
test_cmp show.gpg verify.2 && test_cmp show.gpg verify.2 &&
test_cmp cat.commit verify.1 test_cmp cat.commit verify.1
@@ -299,10 +309,10 @@ test_expect_success GPG 'check config gpg.format values' '
test_expect_success GPG 'detect fudged commit with double signature' ' test_expect_success GPG 'detect fudged commit with double signature' '
sed -e "/gpgsig/,/END PGP/d" forged1 >double-base && sed -e "/gpgsig/,/END PGP/d" forged1 >double-base &&
sed -n -e "/gpgsig/,/END PGP/p" forged1 | \ sed -n -e "/gpgsig/,/END PGP/p" forged1 | \
sed -e "s/^gpgsig//;s/^ //" | gpg --dearmor >double-sig1.sig && sed -e "s/^$(test_oid header)//;s/^ //" | gpg --dearmor >double-sig1.sig &&
gpg -o double-sig2.sig -u 29472784 --detach-sign double-base && gpg -o double-sig2.sig -u 29472784 --detach-sign double-base &&
cat double-sig1.sig double-sig2.sig | gpg --enarmor >double-combined.asc && cat double-sig1.sig double-sig2.sig | gpg --enarmor >double-combined.asc &&
sed -e "s/^\(-.*\)ARMORED FILE/\1SIGNATURE/;1s/^/gpgsig /;2,\$s/^/ /" \ sed -e "s/^\(-.*\)ARMORED FILE/\1SIGNATURE/;1s/^/$(test_oid header) /;2,\$s/^/ /" \
double-combined.asc > double-gpgsig && double-combined.asc > double-gpgsig &&
sed -e "/committer/r double-gpgsig" double-base >double-commit && sed -e "/committer/r double-gpgsig" double-base >double-commit &&
git hash-object -w -t commit double-commit >double-commit.commit && git hash-object -w -t commit double-commit >double-commit.commit &&

View File

@@ -3381,4 +3381,113 @@ test_expect_success 'X: handling encoding' '
git log -1 --format=%B encoding | grep $(printf "\317\200") git log -1 --format=%B encoding | grep $(printf "\317\200")
' '
###
### series Y (submodules and hash algorithms)
###
cat >Y-sub-input <<\Y_INPUT_END
blob
mark :1
data 4
foo
reset refs/heads/master
commit refs/heads/master
mark :2
author Full Name <user@company.tld> 1000000000 +0100
committer Full Name <user@company.tld> 1000000000 +0100
data 24
Test submodule commit 1
M 100644 :1 file
blob
mark :3
data 8
foo
bar
commit refs/heads/master
mark :4
author Full Name <user@company.tld> 1000000001 +0100
committer Full Name <user@company.tld> 1000000001 +0100
data 24
Test submodule commit 2
from :2
M 100644 :3 file
Y_INPUT_END
# Note that the submodule object IDs are intentionally not translated.
cat >Y-main-input <<\Y_INPUT_END
blob
mark :1
data 4
foo
reset refs/heads/master
commit refs/heads/master
mark :2
author Full Name <user@company.tld> 2000000000 +0100
committer Full Name <user@company.tld> 2000000000 +0100
data 14
Test commit 1
M 100644 :1 file
blob
mark :3
data 73
[submodule "sub1"]
path = sub1
url = https://void.example.com/main.git
commit refs/heads/master
mark :4
author Full Name <user@company.tld> 2000000001 +0100
committer Full Name <user@company.tld> 2000000001 +0100
data 14
Test commit 2
from :2
M 100644 :3 .gitmodules
M 160000 0712c5be7cf681388e355ef47525aaf23aee1a6d sub1
blob
mark :5
data 8
foo
bar
commit refs/heads/master
mark :6
author Full Name <user@company.tld> 2000000002 +0100
committer Full Name <user@company.tld> 2000000002 +0100
data 14
Test commit 3
from :4
M 100644 :5 file
M 160000 ff729f5e62f72c0c3978207d9a80e5f3a65f14d7 sub1
Y_INPUT_END
cat >Y-marks <<\Y_INPUT_END
:2 0712c5be7cf681388e355ef47525aaf23aee1a6d
:4 ff729f5e62f72c0c3978207d9a80e5f3a65f14d7
Y_INPUT_END
test_expect_success 'Y: setup' '
test_oid_cache <<-EOF
Ymaster sha1:9afed2f9161ddf416c0a1863b8b0725b00070504
Ymaster sha256:c0a1010da1df187b2e287654793df01b464bd6f8e3f17fc1481a7dadf84caee3
EOF
'
test_expect_success 'Y: rewrite submodules' '
git init main1 &&
(
cd main1 &&
git init sub2 &&
git -C sub2 fast-import --export-marks=../sub2-marks <../Y-sub-input &&
git fast-import --rewrite-submodules-from=sub:../Y-marks \
--rewrite-submodules-to=sub:sub2-marks <../Y-main-input &&
test "$(git rev-parse master)" = "$(test_oid Ymaster)"
)
'
test_done test_done

View File

@@ -494,21 +494,6 @@ case $(echo $GIT_TRACE |tr "[A-Z]" "[a-z]") in
;; ;;
esac esac
# Convenience
#
# A regexp to match 5, 35 and 40 hexdigits
_x05='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
_x35="$_x05$_x05$_x05$_x05$_x05$_x05$_x05"
_x40="$_x35$_x05"
# Zero SHA-1
_z40=0000000000000000000000000000000000000000
OID_REGEX="$_x40"
ZERO_OID=$_z40
EMPTY_TREE=4b825dc642cb6eb9a060e54bf8d69288fbee4904
EMPTY_BLOB=e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
# Line feed # Line feed
LF=' LF='
' '
@@ -1383,6 +1368,20 @@ then
fi fi
fi fi
# Convenience
# A regexp to match 5, 35 and 40 hexdigits
_x05='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
_x35="$_x05$_x05$_x05$_x05$_x05$_x05$_x05"
_x40="$_x35$_x05"
test_oid_init
ZERO_OID=$(test_oid zero)
OID_REGEX=$(echo $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
# Provide an implementation of the 'yes' utility; the upper bound # Provide an implementation of the 'yes' utility; the upper bound
# limit is there to help Windows that cannot stop this loop from # limit is there to help Windows that cannot stop this loop from
# wasting cycles when the downstream stops reading, so do not be # wasting cycles when the downstream stops reading, so do not be

View File

@@ -456,7 +456,7 @@ const struct worktree *find_shared_symref(const char *symref,
int submodule_uses_worktrees(const char *path) int submodule_uses_worktrees(const char *path)
{ {
char *submodule_gitdir; char *submodule_gitdir;
struct strbuf sb = STRBUF_INIT; struct strbuf sb = STRBUF_INIT, err = STRBUF_INIT;
DIR *dir; DIR *dir;
struct dirent *d; struct dirent *d;
int ret = 0; int ret = 0;
@@ -470,18 +470,16 @@ int submodule_uses_worktrees(const char *path)
get_common_dir_noenv(&sb, submodule_gitdir); get_common_dir_noenv(&sb, submodule_gitdir);
free(submodule_gitdir); free(submodule_gitdir);
/*
* The check below is only known to be good for repository format
* version 0 at the time of writing this code.
*/
strbuf_addstr(&sb, "/config"); strbuf_addstr(&sb, "/config");
read_repository_format(&format, sb.buf); read_repository_format(&format, sb.buf);
if (format.version != 0) { if (verify_repository_format(&format, &err)) {
strbuf_release(&err);
strbuf_release(&sb); strbuf_release(&sb);
clear_repository_format(&format); clear_repository_format(&format);
return 1; return 1;
} }
clear_repository_format(&format); clear_repository_format(&format);
strbuf_release(&err);
/* Replace config by worktrees. */ /* Replace config by worktrees. */
strbuf_setlen(&sb, sb.len - strlen("config")); strbuf_setlen(&sb, sb.len - strlen("config"));