From: Junio C Hamano Date: Tue, 16 May 2017 02:51:59 +0000 (+0900) Subject: Merge branch 'js/larger-timestamps' X-Git-Tag: v2.14.0-rc0~183 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/b15667bbdc5ab7732caac977068f5d1cf083115e?ds=inline;hp=-c Merge branch 'js/larger-timestamps' Some platforms have ulong that is smaller than time_t, and our historical use of ulong for timestamp would mean they cannot represent some timestamp that the platform allows. Invent a separate and dedicated timestamp_t (so that we can distingiuish timestamps and a vanilla ulongs, which along is already a good move), and then declare uintmax_t is the type to be used as the timestamp_t. * js/larger-timestamps: archive-tar: fix a sparse 'constant too large' warning use uintmax_t for timestamps date.c: abort if the system time cannot handle one of our timestamps timestamp_t: a new data type for timestamps PRItime: introduce a new "printf format" for timestamps parse_timestamp(): specify explicitly where we parse timestamps t0006 & t5000: skip "far in the future" test when time_t is too limited t0006 & t5000: prepare for 64-bit timestamps ref-filter: avoid using `unsigned long` for catch-all data type --- b15667bbdc5ab7732caac977068f5d1cf083115e diff --combined archive-zip.c index e81c5ac15a,68df3d6440..27563e9e26 --- a/archive-zip.c +++ b/archive-zip.c @@@ -11,14 -11,16 +11,14 @@@ static int zip_date; static int zip_time; -static unsigned char *zip_dir; -static unsigned int zip_dir_size; +/* We only care about the "buf" part here. */ +static struct strbuf zip_dir; -static unsigned int zip_offset; -static unsigned int zip_dir_offset; +static uintmax_t zip_offset; static uint64_t zip_dir_entries; static unsigned int max_creator_version; -#define ZIP_DIRECTORY_MIN_SIZE (1024 * 1024) #define ZIP_STREAM (1 << 3) #define ZIP_UTF8 (1 << 11) @@@ -45,11 -47,24 +45,11 @@@ struct zip_data_desc unsigned char _end[1]; }; -struct zip_dir_header { +struct zip64_data_desc { unsigned char magic[4]; - unsigned char creator_version[2]; - unsigned char version[2]; - unsigned char flags[2]; - unsigned char compression_method[2]; - unsigned char mtime[2]; - unsigned char mdate[2]; unsigned char crc32[4]; - unsigned char compressed_size[4]; - unsigned char size[4]; - unsigned char filename_length[2]; - unsigned char extra_length[2]; - unsigned char comment_length[2]; - unsigned char disk[2]; - unsigned char attr1[2]; - unsigned char attr2[4]; - unsigned char offset[4]; + unsigned char compressed_size[8]; + unsigned char size[8]; unsigned char _end[1]; }; @@@ -73,14 -88,6 +73,14 @@@ struct zip_extra_mtime unsigned char _end[1]; }; +struct zip64_extra { + unsigned char magic[2]; + unsigned char extra_size[2]; + unsigned char size[8]; + unsigned char compressed_size[8]; + unsigned char _end[1]; +}; + struct zip64_dir_trailer { unsigned char magic[4]; unsigned char record_size[8]; @@@ -110,15 -117,11 +110,15 @@@ struct zip64_dir_trailer_locator */ #define ZIP_LOCAL_HEADER_SIZE offsetof(struct zip_local_header, _end) #define ZIP_DATA_DESC_SIZE offsetof(struct zip_data_desc, _end) +#define ZIP64_DATA_DESC_SIZE offsetof(struct zip64_data_desc, _end) #define ZIP_DIR_HEADER_SIZE offsetof(struct zip_dir_header, _end) #define ZIP_DIR_TRAILER_SIZE offsetof(struct zip_dir_trailer, _end) #define ZIP_EXTRA_MTIME_SIZE offsetof(struct zip_extra_mtime, _end) #define ZIP_EXTRA_MTIME_PAYLOAD_SIZE \ (ZIP_EXTRA_MTIME_SIZE - offsetof(struct zip_extra_mtime, flags)) +#define ZIP64_EXTRA_SIZE offsetof(struct zip64_extra, _end) +#define ZIP64_EXTRA_PAYLOAD_SIZE \ + (ZIP64_EXTRA_SIZE - offsetof(struct zip64_extra, size)) #define ZIP64_DIR_TRAILER_SIZE offsetof(struct zip64_dir_trailer, _end) #define ZIP64_DIR_TRAILER_RECORD_SIZE \ (ZIP64_DIR_TRAILER_SIZE - \ @@@ -165,26 -168,6 +165,26 @@@ static void copy_le16_clamp(unsigned ch copy_le16(dest, clamp_max(n, 0xffff, clamped)); } +static void copy_le32_clamp(unsigned char *dest, uint64_t n, int *clamped) +{ + copy_le32(dest, clamp_max(n, 0xffffffff, clamped)); +} + +static int strbuf_add_le(struct strbuf *sb, size_t size, uintmax_t n) +{ + while (size-- > 0) { + strbuf_addch(sb, n & 0xff); + n >>= 8; + } + return -!!n; +} + +static uint32_t clamp32(uintmax_t n) +{ + const uintmax_t max = 0xffffffff; + return (n < max) ? n : max; +} + static void *zlib_deflate_raw(void *data, unsigned long size, int compression_level, unsigned long *compressed_size) @@@ -222,23 -205,23 +222,23 @@@ static void write_zip_data_desc(unsigne unsigned long compressed_size, unsigned long crc) { - struct zip_data_desc trailer; - - copy_le32(trailer.magic, 0x08074b50); - copy_le32(trailer.crc32, crc); - copy_le32(trailer.compressed_size, compressed_size); - copy_le32(trailer.size, size); - write_or_die(1, &trailer, ZIP_DATA_DESC_SIZE); -} - -static void set_zip_dir_data_desc(struct zip_dir_header *header, - unsigned long size, - unsigned long compressed_size, - unsigned long crc) -{ - copy_le32(header->crc32, crc); - copy_le32(header->compressed_size, compressed_size); - copy_le32(header->size, size); + if (size >= 0xffffffff || compressed_size >= 0xffffffff) { + struct zip64_data_desc trailer; + copy_le32(trailer.magic, 0x08074b50); + copy_le32(trailer.crc32, crc); + copy_le64(trailer.compressed_size, compressed_size); + copy_le64(trailer.size, size); + write_or_die(1, &trailer, ZIP64_DATA_DESC_SIZE); + zip_offset += ZIP64_DATA_DESC_SIZE; + } else { + struct zip_data_desc trailer; + copy_le32(trailer.magic, 0x08074b50); + copy_le32(trailer.crc32, crc); + copy_le32(trailer.compressed_size, compressed_size); + copy_le32(trailer.size, size); + write_or_die(1, &trailer, ZIP_DATA_DESC_SIZE); + zip_offset += ZIP_DATA_DESC_SIZE; + } } static void set_zip_header_data_desc(struct zip_local_header *header, @@@ -280,14 -263,12 +280,14 @@@ static int write_zip_entry(struct archi unsigned int mode) { struct zip_local_header header; - struct zip_dir_header dirent; + uintmax_t offset = zip_offset; struct zip_extra_mtime extra; + struct zip64_extra extra64; + size_t header_extra_size = ZIP_EXTRA_MTIME_SIZE; + int need_zip64_extra = 0; unsigned long attr2; unsigned long compressed_size; unsigned long crc; - unsigned long direntsize; int method; unsigned char *out; void *deflated = NULL; @@@ -298,9 -279,6 +298,9 @@@ int is_binary = -1; const char *path_without_prefix = path + args->baselen; unsigned int creator_version = 0; + unsigned int version_needed = 10; + size_t zip_dir_extra_size = ZIP_EXTRA_MTIME_SIZE; + size_t zip64_dir_extra_payload_size = 0; crc = crc32(0, NULL, 0); @@@ -378,43 -356,43 +378,43 @@@ extra.flags[0] = 1; /* just mtime */ copy_le32(extra.mtime, args->time); - /* make sure we have enough free space in the dictionary */ - direntsize = ZIP_DIR_HEADER_SIZE + pathlen + ZIP_EXTRA_MTIME_SIZE; - while (zip_dir_size < zip_dir_offset + direntsize) { - zip_dir_size += ZIP_DIRECTORY_MIN_SIZE; - zip_dir = xrealloc(zip_dir, zip_dir_size); - } + if (size > 0xffffffff || compressed_size > 0xffffffff) + need_zip64_extra = 1; + if (stream && size > 0x7fffffff) + need_zip64_extra = 1; - copy_le32(dirent.magic, 0x02014b50); - copy_le16(dirent.creator_version, creator_version); - copy_le16(dirent.version, 10); - copy_le16(dirent.flags, flags); - copy_le16(dirent.compression_method, method); - copy_le16(dirent.mtime, zip_time); - copy_le16(dirent.mdate, zip_date); - set_zip_dir_data_desc(&dirent, size, compressed_size, crc); - copy_le16(dirent.filename_length, pathlen); - copy_le16(dirent.extra_length, ZIP_EXTRA_MTIME_SIZE); - copy_le16(dirent.comment_length, 0); - copy_le16(dirent.disk, 0); - copy_le32(dirent.attr2, attr2); - copy_le32(dirent.offset, zip_offset); + if (need_zip64_extra) + version_needed = 45; copy_le32(header.magic, 0x04034b50); - copy_le16(header.version, 10); + copy_le16(header.version, version_needed); copy_le16(header.flags, flags); copy_le16(header.compression_method, method); copy_le16(header.mtime, zip_time); copy_le16(header.mdate, zip_date); - set_zip_header_data_desc(&header, size, compressed_size, crc); + if (need_zip64_extra) { + set_zip_header_data_desc(&header, 0xffffffff, 0xffffffff, crc); + header_extra_size += ZIP64_EXTRA_SIZE; + } else { + set_zip_header_data_desc(&header, size, compressed_size, crc); + } copy_le16(header.filename_length, pathlen); - copy_le16(header.extra_length, ZIP_EXTRA_MTIME_SIZE); + copy_le16(header.extra_length, header_extra_size); write_or_die(1, &header, ZIP_LOCAL_HEADER_SIZE); zip_offset += ZIP_LOCAL_HEADER_SIZE; write_or_die(1, path, pathlen); zip_offset += pathlen; write_or_die(1, &extra, ZIP_EXTRA_MTIME_SIZE); zip_offset += ZIP_EXTRA_MTIME_SIZE; + if (need_zip64_extra) { + copy_le16(extra64.magic, 0x0001); + copy_le16(extra64.extra_size, ZIP64_EXTRA_PAYLOAD_SIZE); + copy_le64(extra64.size, size); + copy_le64(extra64.compressed_size, compressed_size); + write_or_die(1, &extra64, ZIP64_EXTRA_SIZE); + zip_offset += ZIP64_EXTRA_SIZE; + } + if (stream && method == 0) { unsigned char buf[STREAM_BUFFER_SIZE]; ssize_t readlen; @@@ -437,6 -415,9 +437,6 @@@ zip_offset += compressed_size; write_zip_data_desc(size, compressed_size, crc); - zip_offset += ZIP_DATA_DESC_SIZE; - - set_zip_dir_data_desc(&dirent, size, compressed_size, crc); } else if (stream && method == 8) { unsigned char buf[STREAM_BUFFER_SIZE]; ssize_t readlen; @@@ -492,6 -473,9 +492,6 @@@ zip_offset += compressed_size; write_zip_data_desc(size, compressed_size, crc); - zip_offset += ZIP_DATA_DESC_SIZE; - - set_zip_dir_data_desc(&dirent, size, compressed_size, crc); } else if (compressed_size > 0) { write_or_die(1, out, compressed_size); zip_offset += compressed_size; @@@ -500,46 -484,14 +500,46 @@@ free(deflated); free(buffer); - copy_le16(dirent.attr1, !is_binary); + if (compressed_size > 0xffffffff || size > 0xffffffff || + offset > 0xffffffff) { + if (compressed_size >= 0xffffffff) + zip64_dir_extra_payload_size += 8; + if (size >= 0xffffffff) + zip64_dir_extra_payload_size += 8; + if (offset >= 0xffffffff) + zip64_dir_extra_payload_size += 8; + zip_dir_extra_size += 2 + 2 + zip64_dir_extra_payload_size; + } - memcpy(zip_dir + zip_dir_offset, &dirent, ZIP_DIR_HEADER_SIZE); - zip_dir_offset += ZIP_DIR_HEADER_SIZE; - memcpy(zip_dir + zip_dir_offset, path, pathlen); - zip_dir_offset += pathlen; - memcpy(zip_dir + zip_dir_offset, &extra, ZIP_EXTRA_MTIME_SIZE); - zip_dir_offset += ZIP_EXTRA_MTIME_SIZE; + strbuf_add_le(&zip_dir, 4, 0x02014b50); /* magic */ + strbuf_add_le(&zip_dir, 2, creator_version); + strbuf_add_le(&zip_dir, 2, version_needed); + strbuf_add_le(&zip_dir, 2, flags); + strbuf_add_le(&zip_dir, 2, method); + strbuf_add_le(&zip_dir, 2, zip_time); + strbuf_add_le(&zip_dir, 2, zip_date); + strbuf_add_le(&zip_dir, 4, crc); + strbuf_add_le(&zip_dir, 4, clamp32(compressed_size)); + strbuf_add_le(&zip_dir, 4, clamp32(size)); + strbuf_add_le(&zip_dir, 2, pathlen); + strbuf_add_le(&zip_dir, 2, zip_dir_extra_size); + strbuf_add_le(&zip_dir, 2, 0); /* comment length */ + strbuf_add_le(&zip_dir, 2, 0); /* disk */ + strbuf_add_le(&zip_dir, 2, !is_binary); + strbuf_add_le(&zip_dir, 4, attr2); + strbuf_add_le(&zip_dir, 4, clamp32(offset)); + strbuf_add(&zip_dir, path, pathlen); + strbuf_add(&zip_dir, &extra, ZIP_EXTRA_MTIME_SIZE); + if (zip64_dir_extra_payload_size) { + strbuf_add_le(&zip_dir, 2, 0x0001); /* magic */ + strbuf_add_le(&zip_dir, 2, zip64_dir_extra_payload_size); + if (size >= 0xffffffff) + strbuf_add_le(&zip_dir, 8, size); + if (compressed_size >= 0xffffffff) + strbuf_add_le(&zip_dir, 8, compressed_size); + if (offset >= 0xffffffff) + strbuf_add_le(&zip_dir, 8, offset); + } zip_dir_entries++; return 0; @@@ -558,12 -510,12 +558,12 @@@ static void write_zip64_trailer(void copy_le32(trailer64.directory_start_disk, 0); copy_le64(trailer64.entries_on_this_disk, zip_dir_entries); copy_le64(trailer64.entries, zip_dir_entries); - copy_le64(trailer64.size, zip_dir_offset); + copy_le64(trailer64.size, zip_dir.len); copy_le64(trailer64.offset, zip_offset); copy_le32(locator64.magic, 0x07064b50); copy_le32(locator64.disk, 0); - copy_le64(locator64.offset, zip_offset + zip_dir_offset); + copy_le64(locator64.offset, zip_offset + zip_dir.len); copy_le32(locator64.number_of_disks, 1); write_or_die(1, &trailer64, ZIP64_DIR_TRAILER_SIZE); @@@ -581,11 -533,11 +581,11 @@@ static void write_zip_trailer(const uns copy_le16_clamp(trailer.entries_on_this_disk, zip_dir_entries, &clamped); copy_le16_clamp(trailer.entries, zip_dir_entries, &clamped); - copy_le32(trailer.size, zip_dir_offset); - copy_le32(trailer.offset, zip_offset); + copy_le32(trailer.size, zip_dir.len); + copy_le32_clamp(trailer.offset, zip_offset, &clamped); copy_le16(trailer.comment_length, sha1 ? GIT_SHA1_HEXSZ : 0); - write_or_die(1, zip_dir, zip_dir_offset); + write_or_die(1, zip_dir.buf, zip_dir.len); if (clamped) write_zip64_trailer(); write_or_die(1, &trailer, ZIP_DIR_TRAILER_SIZE); @@@ -593,9 -545,17 +593,17 @@@ write_or_die(1, sha1_to_hex(sha1), GIT_SHA1_HEXSZ); } - static void dos_time(time_t *time, int *dos_date, int *dos_time) + static void dos_time(timestamp_t *timestamp, int *dos_date, int *dos_time) { - struct tm *t = localtime(time); + time_t time; + struct tm *t; + + if (date_overflows(*timestamp)) + die("timestamp too large for this system: %"PRItime, + *timestamp); + time = (time_t)*timestamp; + t = localtime(&time); + *timestamp = time; *dos_date = t->tm_mday + (t->tm_mon + 1) * 32 + (t->tm_year + 1900 - 1980) * 512; @@@ -616,13 -576,14 +624,13 @@@ static int write_zip_archive(const stru dos_time(&args->time, &zip_date, &zip_time); - zip_dir = xmalloc(ZIP_DIRECTORY_MIN_SIZE); - zip_dir_size = ZIP_DIRECTORY_MIN_SIZE; + strbuf_init(&zip_dir, 0); err = write_archive_entries(args, write_zip_entry); if (!err) write_zip_trailer(args->commit_sha1); - free(zip_dir); + strbuf_release(&zip_dir); return err; } diff --combined builtin/am.c index 17c80329c2,89914ed875..a63935cc83 --- a/builtin/am.c +++ b/builtin/am.c @@@ -134,15 -134,17 +134,15 @@@ struct am_state }; /** - * Initializes am_state with the default values. The state directory is set to - * dir. + * Initializes am_state with the default values. */ -static void am_state_init(struct am_state *state, const char *dir) +static void am_state_init(struct am_state *state) { int gpgsign; memset(state, 0, sizeof(*state)); - assert(dir); - state->dir = xstrdup(dir); + state->dir = git_pathdup("rebase-apply"); state->prec = 4; @@@ -760,18 -762,14 +760,18 @@@ static int split_mail_conv(mail_conv_f mail = mkpath("%s/%0*d", state->dir, state->prec, i + 1); out = fopen(mail, "w"); - if (!out) + if (!out) { + if (in != stdin) + fclose(in); return error_errno(_("could not open '%s' for writing"), mail); + } ret = fn(out, in, keep_cr); fclose(out); - fclose(in); + if (in != stdin) + fclose(in); if (ret) return error(_("could not parse patch '%s'"), *paths); @@@ -879,12 -877,12 +879,12 @@@ static int hg_patch_to_mail(FILE *out, if (skip_prefix(sb.buf, "# User ", &str)) fprintf(out, "From: %s\n", str); else if (skip_prefix(sb.buf, "# Date ", &str)) { - unsigned long timestamp; + timestamp_t timestamp; long tz, tz2; char *end; errno = 0; - timestamp = strtoul(str, &end, 10); + timestamp = parse_timestamp(str, &end, 10); if (errno) return error(_("invalid timestamp")); @@@ -1183,39 -1181,42 +1183,39 @@@ static void NORETURN die_user_resolve(c exit(128); } -static void am_signoff(struct strbuf *sb) +/** + * Appends signoff to the "msg" field of the am_state. + */ +static void am_append_signoff(struct am_state *state) { char *cp; struct strbuf mine = STRBUF_INIT; + struct strbuf sb = STRBUF_INIT; + + strbuf_attach(&sb, state->msg, state->msg_len, state->msg_len); - /* Does it end with our own sign-off? */ + /* our sign-off */ strbuf_addf(&mine, "\n%s%s\n", sign_off_header, fmt_name(getenv("GIT_COMMITTER_NAME"), getenv("GIT_COMMITTER_EMAIL"))); - if (mine.len < sb->len && - !strcmp(mine.buf, sb->buf + sb->len - mine.len)) + + /* Does sb end with it already? */ + if (mine.len < sb.len && + !strcmp(mine.buf, sb.buf + sb.len - mine.len)) goto exit; /* no need to duplicate */ /* Does it have any Signed-off-by: in the text */ - for (cp = sb->buf; + for (cp = sb.buf; cp && *cp && (cp = strstr(cp, sign_off_header)) != NULL; cp = strchr(cp, '\n')) { - if (sb->buf == cp || cp[-1] == '\n') + if (sb.buf == cp || cp[-1] == '\n') break; } - strbuf_addstr(sb, mine.buf + !!cp); + strbuf_addstr(&sb, mine.buf + !!cp); exit: strbuf_release(&mine); -} - -/** - * Appends signoff to the "msg" field of the am_state. - */ -static void am_append_signoff(struct am_state *state) -{ - struct strbuf sb = STRBUF_INIT; - - strbuf_attach(&sb, state->msg, state->msg_len, state->msg_len); - am_signoff(&sb); state->msg = strbuf_detach(&sb, &state->msg_len); } @@@ -1320,6 -1321,9 +1320,6 @@@ static int parse_mail(struct am_state * strbuf_addbuf(&msg, &mi.log_message); strbuf_stripspace(&msg, 0); - if (state->signoff) - am_signoff(&msg); - assert(!state->author_name); state->author_name = strbuf_detach(&author_name, NULL); @@@ -1372,33 -1376,40 +1372,33 @@@ static int get_mail_commit_oid(struct o */ static void get_commit_info(struct am_state *state, struct commit *commit) { - const char *buffer, *ident_line, *author_date, *msg; + const char *buffer, *ident_line, *msg; size_t ident_len; - struct ident_split ident_split; - struct strbuf sb = STRBUF_INIT; + struct ident_split id; buffer = logmsg_reencode(commit, NULL, get_commit_output_encoding()); ident_line = find_commit_header(buffer, "author", &ident_len); - if (split_ident_line(&ident_split, ident_line, ident_len) < 0) { - strbuf_add(&sb, ident_line, ident_len); - die(_("invalid ident line: %s"), sb.buf); - } + if (split_ident_line(&id, ident_line, ident_len) < 0) + die(_("invalid ident line: %.*s"), (int)ident_len, ident_line); assert(!state->author_name); - if (ident_split.name_begin) { - strbuf_add(&sb, ident_split.name_begin, - ident_split.name_end - ident_split.name_begin); - state->author_name = strbuf_detach(&sb, NULL); - } else + if (id.name_begin) + state->author_name = + xmemdupz(id.name_begin, id.name_end - id.name_begin); + else state->author_name = xstrdup(""); assert(!state->author_email); - if (ident_split.mail_begin) { - strbuf_add(&sb, ident_split.mail_begin, - ident_split.mail_end - ident_split.mail_begin); - state->author_email = strbuf_detach(&sb, NULL); - } else + if (id.mail_begin) + state->author_email = + xmemdupz(id.mail_begin, id.mail_end - id.mail_begin); + else state->author_email = xstrdup(""); - author_date = show_ident_date(&ident_split, DATE_MODE(NORMAL)); - strbuf_addstr(&sb, author_date); assert(!state->author_date); - state->author_date = strbuf_detach(&sb, NULL); + state->author_date = xstrdup(show_ident_date(&id, DATE_MODE(NORMAL))); assert(!state->msg); msg = strstr(buffer, "\n\n"); @@@ -1406,7 -1417,6 +1406,7 @@@ die(_("unable to parse commit %s"), oid_to_hex(&commit->object.oid)); state->msg = xstrdup(msg + 2); state->msg_len = strlen(state->msg); + unuse_commit_buffer(commit, buffer); } /** @@@ -1838,9 -1848,6 +1838,9 @@@ static void am_run(struct am_state *sta if (skip) goto next; /* mail should be skipped */ + if (state->signoff) + am_append_signoff(state); + write_author_script(state); write_commit_msg(state); } @@@ -2315,7 -2322,7 +2315,7 @@@ int cmd_am(int argc, const char **argv git_config(git_am_config, NULL); - am_state_init(&state, git_path("rebase-apply")); + am_state_init(&state); in_progress = am_in_progress(&state); if (in_progress) diff --combined builtin/fsck.c index b5e13a4556,ea3a9f8a70..32a32e55c8 --- a/builtin/fsck.c +++ b/builtin/fsck.c @@@ -397,7 -397,7 +397,7 @@@ static int fsck_obj_buffer(const unsign static int default_refs; static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid, - unsigned long timestamp) + timestamp_t timestamp) { struct object *obj; @@@ -407,7 -407,7 +407,7 @@@ if (timestamp && name_objects) add_decoration(fsck_walk_options.object_names, obj, - xstrfmt("%s@{%ld}", refname, timestamp)); + xstrfmt("%s@{%"PRItime"}", refname, timestamp)); obj->used = 1; mark_object_reachable(obj); } else { @@@ -418,7 -418,7 +418,7 @@@ } static int fsck_handle_reflog_ent(struct object_id *ooid, struct object_id *noid, - const char *email, unsigned long timestamp, int tz, + const char *email, timestamp_t timestamp, int tz, const char *message, void *cb_data) { const char *refname = cb_data; @@@ -771,7 -771,6 +771,7 @@@ int cmd_fsck(int argc, const char **arg } if (keep_cache_objects) { + verify_index_checksum = 1; read_cache(); for (i = 0; i < active_nr; i++) { unsigned int mode; diff --combined builtin/gc.c index 91f7696a85,cb1e20aae4..f484eda43c --- a/builtin/gc.c +++ b/builtin/gc.c @@@ -33,7 -33,7 +33,7 @@@ static int aggressive_window = 250 static int gc_auto_threshold = 6700; static int gc_auto_pack_limit = 50; static int detach_auto = 1; - static unsigned long gc_log_expire_time; + static timestamp_t gc_log_expire_time; static const char *gc_log_expire = "1.day.ago"; static const char *prune_expire = "2.weeks.ago"; static const char *prune_worktrees_expire = "3.months.ago"; @@@ -232,7 -232,7 +232,7 @@@ static int need_to_gc(void static const char *lock_repo_for_gc(int force, pid_t* ret_pid) { static struct lock_file lock; - char my_host[128]; + char my_host[HOST_NAME_MAX + 1]; struct strbuf sb = STRBUF_INIT; struct stat st; uintmax_t pid; @@@ -244,19 -244,15 +244,19 @@@ /* already locked */ return NULL; - if (gethostname(my_host, sizeof(my_host))) + if (xgethostname(my_host, sizeof(my_host))) xsnprintf(my_host, sizeof(my_host), "unknown"); pidfile_path = git_pathdup("gc.pid"); fd = hold_lock_file_for_update(&lock, pidfile_path, LOCK_DIE_ON_ERROR); if (!force) { - static char locking_host[128]; + static char locking_host[HOST_NAME_MAX + 1]; + static char *scan_fmt; int should_exit; + + if (!scan_fmt) + scan_fmt = xstrfmt("%s %%%dc", "%"SCNuMAX, HOST_NAME_MAX); fp = fopen(pidfile_path, "r"); memset(locking_host, 0, sizeof(locking_host)); should_exit = @@@ -272,7 -268,7 +272,7 @@@ * running. */ time(NULL) - st.st_mtime <= 12 * 3600 && - fscanf(fp, "%"SCNuMAX" %127c", &pid, locking_host) == 2 && + fscanf(fp, scan_fmt, &pid, locking_host) == 2 && /* be gentle to concurrent "gc" on remote hosts */ (strcmp(locking_host, my_host) || !kill(pid, 0) || errno == EPERM); if (fp != NULL) diff --combined builtin/receive-pack.c index f96834f42c,b4e22f4449..0bb36d584d --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@@ -78,7 -78,7 +78,7 @@@ static const char *NONCE_OK = "OK" static const char *NONCE_SLOP = "SLOP"; static const char *nonce_status; static long nonce_stamp_slop; - static unsigned long nonce_stamp_slop_limit; + static timestamp_t nonce_stamp_slop_limit; static struct ref_transaction *transaction; static enum { @@@ -454,17 -454,17 +454,17 @@@ static void hmac_sha1(unsigned char *ou git_SHA1_Final(out, &ctx); } - static char *prepare_push_cert_nonce(const char *path, unsigned long stamp) + static char *prepare_push_cert_nonce(const char *path, timestamp_t stamp) { struct strbuf buf = STRBUF_INIT; unsigned char sha1[20]; - strbuf_addf(&buf, "%s:%lu", path, stamp); + strbuf_addf(&buf, "%s:%"PRItime, path, stamp); hmac_sha1(sha1, buf.buf, buf.len, cert_nonce_seed, strlen(cert_nonce_seed));; strbuf_release(&buf); /* RFC 2104 5. HMAC-SHA1-80 */ - strbuf_addf(&buf, "%lu-%.*s", stamp, 20, sha1_to_hex(sha1)); + strbuf_addf(&buf, "%"PRItime"-%.*s", stamp, 20, sha1_to_hex(sha1)); return strbuf_detach(&buf, NULL); } @@@ -496,7 -496,7 +496,7 @@@ static char *find_header(const char *ms static const char *check_nonce(const char *buf, size_t len) { char *nonce = find_header(buf, len, "nonce"); - unsigned long stamp, ostamp; + timestamp_t stamp, ostamp; char *bohmac, *expect = NULL; const char *retval = NONCE_BAD; @@@ -534,7 -534,7 +534,7 @@@ retval = NONCE_BAD; goto leave; } - stamp = strtoul(nonce, &bohmac, 10); + stamp = parse_timestamp(nonce, &bohmac, 10); if (bohmac == nonce || bohmac[0] != '-') { retval = NONCE_BAD; goto leave; @@@ -552,7 -552,7 +552,7 @@@ * would mean it was issued by another server with its clock * skewed in the future. */ - ostamp = strtoul(push_cert_nonce, NULL, 10); + ostamp = parse_timestamp(push_cert_nonce, NULL, 10); nonce_stamp_slop = (long)ostamp - (long)stamp; if (nonce_stamp_slop_limit && @@@ -772,6 -772,7 +772,6 @@@ static int run_update_hook(struct comma proc.stdout_to_stderr = 1; proc.err = use_sideband ? -1 : 0; proc.argv = argv; - proc.env = tmp_objdir_env(tmp_objdir); code = start_command(&proc); if (code) @@@ -1697,12 -1698,12 +1697,12 @@@ static const char *unpack(int err_fd, s if (status) return "unpack-objects abnormal exit"; } else { - char hostname[256]; + char hostname[HOST_NAME_MAX + 1]; argv_array_pushl(&child.args, "index-pack", "--stdin", NULL); push_header_arg(&child.args, &hdr); - if (gethostname(hostname, sizeof(hostname))) + if (xgethostname(hostname, sizeof(hostname))) xsnprintf(hostname, sizeof(hostname), "localhost"); argv_array_pushf(&child.args, "--keep=receive-pack %"PRIuMAX" on %s", diff --combined builtin/worktree.c index 1722a9bdc2,74f9b18d40..11f90d6e45 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@@ -24,14 -24,13 +24,14 @@@ struct add_opts int force; int detach; int checkout; + int keep_locked; const char *new_branch; int force_new_branch; }; static int show_only; static int verbose; - static unsigned long expire; + static timestamp_t expire; static int prune_worktree(const char *id, struct strbuf *reason) { @@@ -107,7 -106,8 +107,7 @@@ static void prune_worktrees(void printf("%s\n", reason.buf); if (show_only) continue; - strbuf_reset(&path); - strbuf_addstr(&path, git_path("worktrees/%s", d->d_name)); + git_path_buf(&path, "worktrees/%s", d->d_name); ret = remove_dir_recursively(&path, 0); if (ret < 0 && errno == ENOTDIR) ret = unlink(path.buf); @@@ -131,7 -131,7 +131,7 @@@ static int prune(int ac, const char **a OPT_END() }; - expire = ULONG_MAX; + expire = TIME_MAX; ac = parse_options(ac, av, prefix, options, worktree_usage, 0); if (ac) usage_with_options(worktree_usage, options); @@@ -215,7 -215,8 +215,7 @@@ static int add_worktree(const char *pat } name = worktree_basename(path, &len); - strbuf_addstr(&sb_repo, - git_path("worktrees/%.*s", (int)(path + len - name), name)); + git_path_buf(&sb_repo, "worktrees/%.*s", (int)(path + len - name), name); len = sb_repo.len; if (safe_create_leading_directories_const(sb_repo.buf)) die_errno(_("could not create leading directories of '%s'"), @@@ -241,10 -242,7 +241,10 @@@ * after the preparation is over. */ strbuf_addf(&sb, "%s/locked", sb_repo.buf); - write_file(sb.buf, "initializing"); + if (!opts->keep_locked) + write_file(sb.buf, "initializing"); + else + write_file(sb.buf, "added with --lock"); strbuf_addf(&sb_git, "%s/.git", path); if (safe_create_leading_directories_const(sb_git.buf)) @@@ -305,11 -303,9 +305,11 @@@ junk_git_dir = NULL; done: - strbuf_reset(&sb); - strbuf_addf(&sb, "%s/locked", sb_repo.buf); - unlink_or_warn(sb.buf); + if (ret || !opts->keep_locked) { + strbuf_reset(&sb); + strbuf_addf(&sb, "%s/locked", sb_repo.buf); + unlink_or_warn(sb.buf); + } argv_array_clear(&child_env); strbuf_release(&sb); strbuf_release(&symref); @@@ -332,7 -328,6 +332,7 @@@ static int add(int ac, const char **av N_("create or reset a branch")), OPT_BOOL(0, "detach", &opts.detach, N_("detach HEAD at named commit")), OPT_BOOL(0, "checkout", &opts.checkout, N_("populate the new working tree")), + OPT_BOOL(0, "lock", &opts.keep_locked, N_("keep the new working tree locked")), OPT_END() }; diff --combined cache.h index e1f0e182ad,f3a02993b2..188811920c --- a/cache.h +++ b/cache.h @@@ -599,7 -599,6 +599,7 @@@ extern int write_locked_index(struct in extern int discard_index(struct index_state *); extern int unmerged_index(const struct index_state *); extern int verify_path(const char *path); +extern int strcmp_offset(const char *s1, const char *s2, size_t *first_change); extern int index_dir_exists(struct index_state *istate, const char *name, int namelen); extern void adjust_dirname_case(struct index_state *istate, char *name); extern struct cache_entry *index_file_exists(struct index_state *istate, const char *name, int namelen, int igncase); @@@ -711,8 -710,6 +711,8 @@@ extern void update_index_if_able(struc extern int hold_locked_index(struct lock_file *, int); extern void set_alternate_index_output(const char *); +extern int verify_index_checksum; + /* Environment bits from configuration mechanism */ extern int trust_executable_bit; extern int trust_ctime; @@@ -1167,7 -1164,7 +1167,7 @@@ typedef int create_file_fn(const char * int raceproof_create_file(const char *path, create_file_fn fn, void *cb); int mkdir_in_gitdir(const char *path); -extern char *expand_user_path(const char *path); +extern char *expand_user_path(const char *path, int real_home); const char *enter_repo(const char *path, int strict); static inline int is_absolute_path(const char *path) { @@@ -1479,18 -1476,18 +1479,18 @@@ struct date_mode #define DATE_MODE(t) date_mode_from_type(DATE_##t) struct date_mode *date_mode_from_type(enum date_mode_type type); - const char *show_date(unsigned long time, int timezone, const struct date_mode *mode); - void show_date_relative(unsigned long time, int tz, const struct timeval *now, + const char *show_date(timestamp_t time, int timezone, const struct date_mode *mode); + void show_date_relative(timestamp_t time, int tz, const struct timeval *now, struct strbuf *timebuf); int parse_date(const char *date, struct strbuf *out); - int parse_date_basic(const char *date, unsigned long *timestamp, int *offset); - int parse_expiry_date(const char *date, unsigned long *timestamp); + int parse_date_basic(const char *date, timestamp_t *timestamp, int *offset); + int parse_expiry_date(const char *date, timestamp_t *timestamp); void datestamp(struct strbuf *out); #define approxidate(s) approxidate_careful((s), NULL) - unsigned long approxidate_careful(const char *, int *); - unsigned long approxidate_relative(const char *date, const struct timeval *now); + timestamp_t approxidate_careful(const char *, int *); + timestamp_t approxidate_relative(const char *date, const struct timeval *now); void parse_date_format(const char *format, struct date_mode *mode); - int date_overflows(unsigned long date); + int date_overflows(timestamp_t date); #define IDENT_STRICT 1 #define IDENT_NO_DATE 2 @@@ -1892,11 -1889,6 +1892,11 @@@ enum config_origin_type CONFIG_ORIGIN_CMDLINE }; +struct config_options { + unsigned int respect_includes : 1; + const char *git_dir; +}; + typedef int (*config_fn_t)(const char *, const char *, void *); extern int git_default_config(const char *, const char *, void *); extern int git_config_from_file(config_fn_t fn, const char *, void *); @@@ -1910,13 -1902,12 +1910,13 @@@ extern void read_early_config(config_fn extern void git_config(config_fn_t fn, void *); extern int git_config_with_options(config_fn_t fn, void *, struct git_config_source *config_source, - int respect_includes); + const struct config_options *opts); extern int git_parse_ulong(const char *, unsigned long *); extern int git_parse_maybe_bool(const char *); extern int git_config_int(const char *, const char *); extern int64_t git_config_int64(const char *, const char *); extern unsigned long git_config_ulong(const char *, const char *); +extern ssize_t git_config_ssize_t(const char *, const char *); extern int git_config_bool_or_int(const char *, const char *, int *); extern int git_config_bool(const char *, const char *); extern int git_config_maybe_bool(const char *, const char *); @@@ -1963,7 -1954,6 +1963,7 @@@ struct config_include_data int depth; config_fn_t fn; void *data; + const struct config_options *opts; }; #define CONFIG_INCLUDE_INIT { 0 } extern int git_config_include(const char *name, const char *value, void *data); diff --combined config.c index b4a3205da3,3247bfaa02..bb4d735701 --- a/config.c +++ b/config.c @@@ -135,7 -135,7 +135,7 @@@ static int handle_path_include(const ch if (!path) return config_error_nonbool("include.path"); - expanded = expand_user_path(path); + expanded = expand_user_path(path, 0); if (!expanded) return error("could not expand include path '%s'", path); path = expanded; @@@ -177,7 -177,7 +177,7 @@@ static int prepare_include_condition_pa char *expanded; int prefix = 0; - expanded = expand_user_path(pat->buf); + expanded = expand_user_path(pat->buf, 1); if (expanded) { strbuf_reset(pat); strbuf_addstr(pat, expanded); @@@ -191,7 -191,7 +191,7 @@@ return error(_("relative config include " "conditionals must come from files")); - strbuf_add_absolute_path(&path, cf->path); + strbuf_realpath(&path, cf->path, 1); slash = find_last_dir_sep(path.buf); if (!slash) die("BUG: how is this possible?"); @@@ -207,22 -207,13 +207,22 @@@ return prefix; } -static int include_by_gitdir(const char *cond, size_t cond_len, int icase) +static int include_by_gitdir(const struct config_options *opts, + const char *cond, size_t cond_len, int icase) { struct strbuf text = STRBUF_INIT; struct strbuf pattern = STRBUF_INIT; int ret = 0, prefix; + const char *git_dir; - strbuf_add_absolute_path(&text, get_git_dir()); + if (opts->git_dir) + git_dir = opts->git_dir; + else if (have_git_dir()) + git_dir = get_git_dir(); + else + goto done; + + strbuf_realpath(&text, git_dir, 1); strbuf_add(&pattern, cond, cond_len); prefix = prepare_include_condition_pattern(&pattern); @@@ -251,14 -242,13 +251,14 @@@ done return ret; } -static int include_condition_is_true(const char *cond, size_t cond_len) +static int include_condition_is_true(const struct config_options *opts, + const char *cond, size_t cond_len) { if (skip_prefix_mem(cond, cond_len, "gitdir:", &cond, &cond_len)) - return include_by_gitdir(cond, cond_len, 0); + return include_by_gitdir(opts, cond, cond_len, 0); else if (skip_prefix_mem(cond, cond_len, "gitdir/i:", &cond, &cond_len)) - return include_by_gitdir(cond, cond_len, 1); + return include_by_gitdir(opts, cond, cond_len, 1); /* unknown conditionals are always false */ return 0; @@@ -283,7 -273,7 +283,7 @@@ int git_config_include(const char *var ret = handle_path_include(value, inc); if (!parse_config_key(var, "includeif", &cond, &cond_len, &key) && - (cond && include_condition_is_true(cond, cond_len)) && + (cond && include_condition_is_true(inc->opts, cond, cond_len)) && !strcmp(key, "path")) ret = handle_path_include(value, inc); @@@ -844,15 -834,6 +844,15 @@@ int git_parse_ulong(const char *value, return 1; } +static int git_parse_ssize_t(const char *value, ssize_t *ret) +{ + intmax_t tmp; + if (!git_parse_signed(value, &tmp, maximum_signed_value_of_type(ssize_t))) + return 0; + *ret = tmp; + return 1; +} + NORETURN static void die_bad_number(const char *name, const char *value) { @@@ -911,14 -892,6 +911,14 @@@ unsigned long git_config_ulong(const ch return ret; } +ssize_t git_config_ssize_t(const char *name, const char *value) +{ + ssize_t ret; + if (!git_parse_ssize_t(value, &ret)) + die_bad_number(name, value); + return ret; +} + int git_parse_maybe_bool(const char *value) { if (!value) @@@ -975,7 -948,7 +975,7 @@@ int git_config_pathname(const char **de { if (!value) return config_error_nonbool(var); - *dest = expand_user_path(value); + *dest = expand_user_path(value, 0); if (!*dest) die(_("failed to expand user dir in: '%s'"), value); return 0; @@@ -1521,20 -1494,12 +1521,20 @@@ int git_config_system(void return !git_env_bool("GIT_CONFIG_NOSYSTEM", 0); } -static int do_git_config_sequence(config_fn_t fn, void *data) +static int do_git_config_sequence(const struct config_options *opts, + config_fn_t fn, void *data) { int ret = 0; char *xdg_config = xdg_config_home("config"); - char *user_config = expand_user_path("~/.gitconfig"); - char *repo_config = have_git_dir() ? git_pathdup("config") : NULL; + char *user_config = expand_user_path("~/.gitconfig", 0); + char *repo_config; + + if (opts->git_dir) + repo_config = mkpathdup("%s/config", opts->git_dir); + else if (have_git_dir()) + repo_config = git_pathdup("config"); + else + repo_config = NULL; current_parsing_scope = CONFIG_SCOPE_SYSTEM; if (git_config_system() && !access_or_die(git_etc_gitconfig(), R_OK, 0)) @@@ -1565,14 -1530,13 +1565,14 @@@ int git_config_with_options(config_fn_t fn, void *data, struct git_config_source *config_source, - int respect_includes) + const struct config_options *opts) { struct config_include_data inc = CONFIG_INCLUDE_INIT; - if (respect_includes) { + if (opts->respect_includes) { inc.fn = fn; inc.data = data; + inc.opts = opts; fn = git_config_include; data = &inc; } @@@ -1588,15 -1552,12 +1588,15 @@@ else if (config_source && config_source->blob) return git_config_from_blob_ref(fn, config_source->blob, data); - return do_git_config_sequence(fn, data); + return do_git_config_sequence(opts, fn, data); } static void git_config_raw(config_fn_t fn, void *data) { - if (git_config_with_options(fn, data, NULL, 1) < 0) + struct config_options opts = {0}; + + opts.respect_includes = 1; + if (git_config_with_options(fn, data, NULL, &opts) < 0) /* * git_config_with_options() normally returns only * zero, as most errors are fatal, and @@@ -1636,13 -1597,10 +1636,13 @@@ static void configset_iter(struct confi void read_early_config(config_fn_t cb, void *data) { + struct config_options opts = {0}; struct strbuf buf = STRBUF_INIT; - git_config_with_options(cb, data, NULL, 1); + opts.respect_includes = 1; + if (have_git_dir()) + opts.git_dir = get_git_dir(); /* * When setup_git_directory() was not yet asked to discover the * GIT_DIR, we ask discover_git_directory() to figure out whether there @@@ -1651,11 -1609,14 +1651,11 @@@ * notably, the current working directory is still the same after the * call). */ - if (!have_git_dir() && discover_git_directory(&buf)) { - struct git_config_source repo_config; + else if (discover_git_directory(&buf)) + opts.git_dir = buf.buf; + + git_config_with_options(cb, data, NULL, &opts); - memset(&repo_config, 0, sizeof(repo_config)); - strbuf_addstr(&buf, "/config"); - repo_config.file = buf.buf; - git_config_with_options(cb, data, &repo_config, 1); - } strbuf_release(&buf); } @@@ -1965,7 -1926,7 +1965,7 @@@ int git_config_get_expiry(const char *k if (ret) return ret; if (strcmp(*output, "now")) { - unsigned long now = approxidate("now"); + timestamp_t now = approxidate("now"); if (approxidate(*output) >= now) git_die_config(key, _("Invalid %s: '%s'"), key, *output); } diff --combined fetch-pack.c index afb8b05024,fbe52f0234..5f15dd2c39 --- a/fetch-pack.c +++ b/fetch-pack.c @@@ -276,8 -276,6 +276,8 @@@ static enum ack_type get_ack(int fd, un return ACK; } } + if (skip_prefix(line, "ERR ", &arg)) + die(_("remote error: %s"), arg); die(_("git fetch-pack: expected ACK/NAK, got '%s'"), line); } @@@ -394,8 -392,8 +394,8 @@@ static int find_common(struct fetch_pac if (args->depth > 0) packet_buf_write(&req_buf, "deepen %d", args->depth); if (args->deepen_since) { - unsigned long max_age = approxidate(args->deepen_since); - packet_buf_write(&req_buf, "deepen-since %lu", max_age); + timestamp_t max_age = approxidate(args->deepen_since); + packet_buf_write(&req_buf, "deepen-since %"PRItime, max_age); } if (args->deepen_not) { int i; @@@ -583,7 -581,7 +583,7 @@@ static int mark_complete_oid(const cha } static void mark_recent_complete_commits(struct fetch_pack_args *args, - unsigned long cutoff) + timestamp_t cutoff) { while (complete && cutoff <= complete->item->date) { print_verbose(args, _("Marking %s as complete"), @@@ -670,7 -668,7 +670,7 @@@ static int everything_local(struct fetc { struct ref *ref; int retval; - unsigned long cutoff = 0; + timestamp_t cutoff = 0; save_commit_buffer = 0; @@@ -804,8 -802,8 +804,8 @@@ static int get_pack(struct fetch_pack_a if (args->use_thin_pack) argv_array_push(&cmd.args, "--fix-thin"); if (args->lock_pack || unpack_limit) { - char hostname[256]; - if (gethostname(hostname, sizeof(hostname))) + char hostname[HOST_NAME_MAX + 1]; + if (xgethostname(hostname, sizeof(hostname))) xsnprintf(hostname, sizeof(hostname), "localhost"); argv_array_pushf(&cmd.args, "--keep=fetch-pack %"PRIuMAX " on %s", diff --combined git-compat-util.h index bb4537c0ba,f8349a03bd..ab7552a7ce --- a/git-compat-util.h +++ b/git-compat-util.h @@@ -319,6 -319,11 +319,11 @@@ extern char *gitdirname(char *) #define PRIo32 "o" #endif + typedef uintmax_t timestamp_t; + #define PRItime PRIuMAX + #define parse_timestamp strtoumax + #define TIME_MAX UINTMAX_MAX + #ifndef PATH_SEP #define PATH_SEP ':' #endif @@@ -616,7 -621,7 +621,7 @@@ extern int git_lstat(const char *, stru #endif #define DEFAULT_PACKED_GIT_LIMIT \ - ((1024L * 1024L) * (size_t)(sizeof(void*) >= 8 ? 8192 : 256)) + ((1024L * 1024L) * (size_t)(sizeof(void*) >= 8 ? (32 * 1024L * 1024L) : 256)) #ifdef NO_PREAD #define pread git_pread @@@ -884,12 -889,6 +889,12 @@@ static inline size_t xsize_t(off_t len __attribute__((format (printf, 3, 4))) extern int xsnprintf(char *dst, size_t max, const char *fmt, ...); +#ifndef HOST_NAME_MAX +#define HOST_NAME_MAX 256 +#endif + +extern int xgethostname(char *buf, size_t len); + /* in ctype.c, for kwset users */ extern const unsigned char tolower_trans_tbl[256]; diff --combined refs.c index fda4501181,6841bd9f71..26d40f9927 --- a/refs.c +++ b/refs.c @@@ -5,13 -5,11 +5,13 @@@ #include "cache.h" #include "hashmap.h" #include "lockfile.h" +#include "iterator.h" #include "refs.h" #include "refs/refs-internal.h" #include "object.h" #include "tag.h" #include "submodule.h" +#include "worktree.h" /* * List of all available backends @@@ -714,7 -712,7 +714,7 @@@ int is_branch(const char *refname struct read_ref_at_cb { const char *refname; - unsigned long at_time; + timestamp_t at_time; int cnt; int reccnt; unsigned char *sha1; @@@ -723,15 -721,15 +723,15 @@@ unsigned char osha1[20]; unsigned char nsha1[20]; int tz; - unsigned long date; + timestamp_t date; char **msg; - unsigned long *cutoff_time; + timestamp_t *cutoff_time; int *cutoff_tz; int *cutoff_cnt; }; static int read_ref_at_ent(struct object_id *ooid, struct object_id *noid, - const char *email, unsigned long timestamp, int tz, + const char *email, timestamp_t timestamp, int tz, const char *message, void *cb_data) { struct read_ref_at_cb *cb = cb_data; @@@ -778,7 -776,7 +778,7 @@@ } static int read_ref_at_ent_oldest(struct object_id *ooid, struct object_id *noid, - const char *email, unsigned long timestamp, + const char *email, timestamp_t timestamp, int tz, const char *message, void *cb_data) { struct read_ref_at_cb *cb = cb_data; @@@ -798,9 -796,9 +798,9 @@@ return 1; } - int read_ref_at(const char *refname, unsigned int flags, unsigned long at_time, int cnt, + int read_ref_at(const char *refname, unsigned int flags, timestamp_t at_time, int cnt, unsigned char *sha1, char **msg, - unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt) + timestamp_t *cutoff_time, int *cutoff_tz, int *cutoff_cnt) { struct read_ref_at_cb cb; @@@ -1240,18 -1238,6 +1240,18 @@@ int head_ref(each_ref_fn fn, void *cb_d return head_ref_submodule(NULL, fn, cb_data); } +struct ref_iterator *refs_ref_iterator_begin( + struct ref_store *refs, + const char *prefix, int trim, int flags) +{ + struct ref_iterator *iter; + + iter = refs->be->iterator_begin(refs, prefix, flags); + iter = prefix_ref_iterator_begin(iter, prefix, trim); + + return iter; +} + /* * Call fn for each reference in the specified submodule for which the * refname begins with prefix. If trim is non-zero, then trim that @@@ -1269,7 -1255,8 +1269,7 @@@ static int do_for_each_ref(struct ref_s if (!refs) return 0; - iter = refs->be->iterator_begin(refs, prefix, flags); - iter = prefix_ref_iterator_begin(iter, prefix, trim); + iter = refs_ref_iterator_begin(refs, prefix, trim, flags); return do_for_each_ref_iterator(iter, fn, cb_data); } @@@ -1347,13 -1334,6 +1347,13 @@@ int for_each_rawref(each_ref_fn fn, voi return refs_for_each_rawref(get_main_ref_store(), fn, cb_data); } +int refs_read_raw_ref(struct ref_store *ref_store, + const char *refname, unsigned char *sha1, + struct strbuf *referent, unsigned int *type) +{ + return ref_store->be->read_raw_ref(ref_store, refname, sha1, referent, type); +} + /* This function needs to return a meaningful errno on failure */ const char *refs_resolve_ref_unsafe(struct ref_store *refs, const char *refname, @@@ -1390,8 -1370,8 +1390,8 @@@ for (symref_count = 0; symref_count < SYMREF_MAXDEPTH; symref_count++) { unsigned int read_flags = 0; - if (refs->be->read_raw_ref(refs, refname, - sha1, &sb_refname, &read_flags)) { + if (refs_read_raw_ref(refs, refname, + sha1, &sb_refname, &read_flags)) { *flags |= read_flags; if (errno != ENOENT || (resolve_flags & RESOLVE_REF_READING)) return NULL; @@@ -1478,32 -1458,32 +1478,32 @@@ int resolve_gitlink_ref(const char *sub return 0; } -struct submodule_hash_entry +struct ref_store_hash_entry { struct hashmap_entry ent; /* must be the first member! */ struct ref_store *refs; - /* NUL-terminated name of submodule: */ - char submodule[FLEX_ARRAY]; + /* NUL-terminated identifier of the ref store: */ + char name[FLEX_ARRAY]; }; -static int submodule_hash_cmp(const void *entry, const void *entry_or_key, +static int ref_store_hash_cmp(const void *entry, const void *entry_or_key, const void *keydata) { - const struct submodule_hash_entry *e1 = entry, *e2 = entry_or_key; - const char *submodule = keydata ? keydata : e2->submodule; + const struct ref_store_hash_entry *e1 = entry, *e2 = entry_or_key; + const char *name = keydata ? keydata : e2->name; - return strcmp(e1->submodule, submodule); + return strcmp(e1->name, name); } -static struct submodule_hash_entry *alloc_submodule_hash_entry( - const char *submodule, struct ref_store *refs) +static struct ref_store_hash_entry *alloc_ref_store_hash_entry( + const char *name, struct ref_store *refs) { - struct submodule_hash_entry *entry; + struct ref_store_hash_entry *entry; - FLEX_ALLOC_STR(entry, submodule, submodule); - hashmap_entry_init(entry, strhash(submodule)); + FLEX_ALLOC_STR(entry, name, name); + hashmap_entry_init(entry, strhash(name)); entry->refs = refs; return entry; } @@@ -1514,23 -1494,20 +1514,23 @@@ static struct ref_store *main_ref_store /* A hashmap of ref_stores, stored by submodule name: */ static struct hashmap submodule_ref_stores; +/* A hashmap of ref_stores, stored by worktree id: */ +static struct hashmap worktree_ref_stores; + /* - * Return the ref_store instance for the specified submodule. If that - * ref_store hasn't been initialized yet, return NULL. + * Look up a ref store by name. If that ref_store hasn't been + * registered yet, return NULL. */ -static struct ref_store *lookup_submodule_ref_store(const char *submodule) +static struct ref_store *lookup_ref_store_map(struct hashmap *map, + const char *name) { - struct submodule_hash_entry *entry; + struct ref_store_hash_entry *entry; - if (!submodule_ref_stores.tablesize) + if (!map->tablesize) /* It's initialized on demand in register_ref_store(). */ return NULL; - entry = hashmap_get_from_hash(&submodule_ref_stores, - strhash(submodule), submodule); + entry = hashmap_get_from_hash(map, strhash(name), name); return entry ? entry->refs : NULL; } @@@ -1557,24 -1534,29 +1557,24 @@@ struct ref_store *get_main_ref_store(vo if (main_ref_store) return main_ref_store; - main_ref_store = ref_store_init(get_git_dir(), - (REF_STORE_READ | - REF_STORE_WRITE | - REF_STORE_ODB | - REF_STORE_MAIN)); + main_ref_store = ref_store_init(get_git_dir(), REF_STORE_ALL_CAPS); return main_ref_store; } /* - * Register the specified ref_store to be the one that should be used - * for submodule. It is a fatal error to call this function twice for - * the same submodule. + * Associate a ref store with a name. It is a fatal error to call this + * function twice for the same name. */ -static void register_submodule_ref_store(struct ref_store *refs, - const char *submodule) +static void register_ref_store_map(struct hashmap *map, + const char *type, + struct ref_store *refs, + const char *name) { - if (!submodule_ref_stores.tablesize) - hashmap_init(&submodule_ref_stores, submodule_hash_cmp, 0); + if (!map->tablesize) + hashmap_init(map, ref_store_hash_cmp, 0); - if (hashmap_put(&submodule_ref_stores, - alloc_submodule_hash_entry(submodule, refs))) - die("BUG: ref_store for submodule '%s' initialized twice", - submodule); + if (hashmap_put(map, alloc_ref_store_hash_entry(name, refs))) + die("BUG: %s ref_store '%s' initialized twice", type, name); } struct ref_store *get_submodule_ref_store(const char *submodule) @@@ -1591,7 -1573,7 +1591,7 @@@ return get_main_ref_store(); } - refs = lookup_submodule_ref_store(submodule); + refs = lookup_ref_store_map(&submodule_ref_stores, submodule); if (refs) return refs; @@@ -1610,39 -1592,12 +1610,39 @@@ /* assume that add_submodule_odb() has been called */ refs = ref_store_init(submodule_sb.buf, REF_STORE_READ | REF_STORE_ODB); - register_submodule_ref_store(refs, submodule); + register_ref_store_map(&submodule_ref_stores, "submodule", + refs, submodule); strbuf_release(&submodule_sb); return refs; } +struct ref_store *get_worktree_ref_store(const struct worktree *wt) +{ + struct ref_store *refs; + const char *id; + + if (wt->is_current) + return get_main_ref_store(); + + id = wt->id ? wt->id : "/"; + refs = lookup_ref_store_map(&worktree_ref_stores, id); + if (refs) + return refs; + + if (wt->id) + refs = ref_store_init(git_common_path("worktrees/%s", wt->id), + REF_STORE_ALL_CAPS); + else + refs = ref_store_init(get_git_common_dir(), + REF_STORE_ALL_CAPS); + + if (refs) + register_ref_store_map(&worktree_ref_stores, "worktree", + refs, id); + return refs; +} + void base_ref_store_init(struct ref_store *refs, const struct ref_storage_be *be) { @@@ -1688,102 -1643,16 +1688,102 @@@ int ref_transaction_commit(struct ref_t { struct ref_store *refs = transaction->ref_store; + if (getenv(GIT_QUARANTINE_ENVIRONMENT)) { + strbuf_addstr(err, + _("ref updates forbidden inside quarantine environment")); + return -1; + } + return refs->be->transaction_commit(refs, transaction, err); } int refs_verify_refname_available(struct ref_store *refs, const char *refname, - const struct string_list *extra, + const struct string_list *extras, const struct string_list *skip, struct strbuf *err) { - return refs->be->verify_refname_available(refs, refname, extra, skip, err); + const char *slash; + const char *extra_refname; + struct strbuf dirname = STRBUF_INIT; + struct strbuf referent = STRBUF_INIT; + struct object_id oid; + unsigned int type; + struct ref_iterator *iter; + int ok; + int ret = -1; + + /* + * For the sake of comments in this function, suppose that + * refname is "refs/foo/bar". + */ + + assert(err); + + strbuf_grow(&dirname, strlen(refname) + 1); + for (slash = strchr(refname, '/'); slash; slash = strchr(slash + 1, '/')) { + /* Expand dirname to the new prefix, not including the trailing slash: */ + strbuf_add(&dirname, refname + dirname.len, slash - refname - dirname.len); + + /* + * We are still at a leading dir of the refname (e.g., + * "refs/foo"; if there is a reference with that name, + * it is a conflict, *unless* it is in skip. + */ + if (skip && string_list_has_string(skip, dirname.buf)) + continue; + + if (!refs_read_raw_ref(refs, dirname.buf, oid.hash, &referent, &type)) { + strbuf_addf(err, "'%s' exists; cannot create '%s'", + dirname.buf, refname); + goto cleanup; + } + + if (extras && string_list_has_string(extras, dirname.buf)) { + strbuf_addf(err, "cannot process '%s' and '%s' at the same time", + refname, dirname.buf); + goto cleanup; + } + } + + /* + * We are at the leaf of our refname (e.g., "refs/foo/bar"). + * There is no point in searching for a reference with that + * name, because a refname isn't considered to conflict with + * itself. But we still need to check for references whose + * names are in the "refs/foo/bar/" namespace, because they + * *do* conflict. + */ + strbuf_addstr(&dirname, refname + dirname.len); + strbuf_addch(&dirname, '/'); + + iter = refs_ref_iterator_begin(refs, dirname.buf, 0, + DO_FOR_EACH_INCLUDE_BROKEN); + while ((ok = ref_iterator_advance(iter)) == ITER_OK) { + if (skip && + string_list_has_string(skip, iter->refname)) + continue; + + strbuf_addf(err, "'%s' exists; cannot create '%s'", + iter->refname, refname); + ref_iterator_abort(iter); + goto cleanup; + } + + if (ok != ITER_DONE) + die("BUG: error while iterating over references"); + + extra_refname = find_descendant_ref(dirname.buf, extras, skip); + if (extra_refname) + strbuf_addf(err, "cannot process '%s' and '%s' at the same time", + refname, extra_refname); + else + ret = 0; + +cleanup: + strbuf_release(&referent); + strbuf_release(&dirname); + return ret; } int refs_for_each_reflog(struct ref_store *refs, each_ref_fn fn, void *cb_data) diff --combined refs.h index aca805168a,2b80d37fd3..d18ef47128 --- a/refs.h +++ b/refs.h @@@ -5,7 -5,6 +5,7 @@@ struct object_id struct ref_store; struct strbuf; struct string_list; +struct worktree; /* * Resolve a reference, recursively following symbolic refererences. @@@ -98,7 -97,7 +98,7 @@@ int read_ref(const char *refname, unsig int refs_verify_refname_available(struct ref_store *refs, const char *refname, - const struct string_list *extra, + const struct string_list *extras, const struct string_list *skip, struct strbuf *err); @@@ -318,9 -317,9 +318,9 @@@ int safe_create_reflog(const char *refn /** Reads log for the value of ref during at_time. **/ int read_ref_at(const char *refname, unsigned int flags, - unsigned long at_time, int cnt, + timestamp_t at_time, int cnt, unsigned char *sha1, char **msg, - unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt); + timestamp_t *cutoff_time, int *cutoff_tz, int *cutoff_cnt); /** Check if a particular reflog exists */ int refs_reflog_exists(struct ref_store *refs, const char *refname); @@@ -357,7 -356,7 +357,7 @@@ int delete_reflog(const char *refname) /* iterate over reflog entries */ typedef int each_reflog_ent_fn( struct object_id *old_oid, struct object_id *new_oid, - const char *committer, unsigned long timestamp, + const char *committer, timestamp_t timestamp, int tz, const char *msg, void *cb_data); int refs_for_each_reflog_ent(struct ref_store *refs, const char *refname, @@@ -402,6 -401,16 +402,6 @@@ int refs_create_symref(struct ref_stor const char *target, const char *logmsg); int create_symref(const char *refname, const char *target, const char *logmsg); -/* - * Update HEAD of the specified gitdir. - * Similar to create_symref("relative-git-dir/HEAD", target, NULL), but - * this can update the main working tree's HEAD regardless of where - * $GIT_DIR points to. - * Return 0 if successful, non-zero otherwise. - * */ -int set_worktree_head_symref(const char *gitdir, const char *target, - const char *logmsg); - enum action_on_err { UPDATE_REFS_MSG_ON_ERR, UPDATE_REFS_DIE_ON_ERR, @@@ -607,7 -616,7 +607,7 @@@ typedef void reflog_expiry_prepare_fn(c typedef int reflog_expiry_should_prune_fn(unsigned char *osha1, unsigned char *nsha1, const char *email, - unsigned long timestamp, int tz, + timestamp_t timestamp, int tz, const char *message, void *cb_data); typedef void reflog_expiry_cleanup_fn(void *cb_data); @@@ -646,6 -655,5 +646,6 @@@ struct ref_store *get_main_ref_store(vo * submodule==NULL. */ struct ref_store *get_submodule_ref_store(const char *submodule); +struct ref_store *get_worktree_ref_store(const struct worktree *wt); #endif /* REFS_H */ diff --combined refs/files-backend.c index 9d08e84ded,6646dcfc83..4925e698d8 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@@ -1,7 -1,6 +1,7 @@@ #include "../cache.h" #include "../refs.h" #include "refs-internal.h" +#include "ref-cache.h" #include "../iterator.h" #include "../dir-iterator.h" #include "../lockfile.h" @@@ -14,6 -13,511 +14,6 @@@ struct ref_lock struct object_id old_oid; }; -struct ref_entry; - -/* - * Information used (along with the information in ref_entry) to - * describe a single cached reference. This data structure only - * occurs embedded in a union in struct ref_entry, and only when - * (ref_entry->flag & REF_DIR) is zero. - */ -struct ref_value { - /* - * The name of the object to which this reference resolves - * (which may be a tag object). If REF_ISBROKEN, this is - * null. If REF_ISSYMREF, then this is the name of the object - * referred to by the last reference in the symlink chain. - */ - struct object_id oid; - - /* - * If REF_KNOWS_PEELED, then this field holds the peeled value - * of this reference, or null if the reference is known not to - * be peelable. See the documentation for peel_ref() for an - * exact definition of "peelable". - */ - struct object_id peeled; -}; - -struct files_ref_store; - -/* - * Information used (along with the information in ref_entry) to - * describe a level in the hierarchy of references. This data - * structure only occurs embedded in a union in struct ref_entry, and - * only when (ref_entry.flag & REF_DIR) is set. In that case, - * (ref_entry.flag & REF_INCOMPLETE) determines whether the references - * in the directory have already been read: - * - * (ref_entry.flag & REF_INCOMPLETE) unset -- a directory of loose - * or packed references, already read. - * - * (ref_entry.flag & REF_INCOMPLETE) set -- a directory of loose - * references that hasn't been read yet (nor has any of its - * subdirectories). - * - * Entries within a directory are stored within a growable array of - * pointers to ref_entries (entries, nr, alloc). Entries 0 <= i < - * sorted are sorted by their component name in strcmp() order and the - * remaining entries are unsorted. - * - * Loose references are read lazily, one directory at a time. When a - * directory of loose references is read, then all of the references - * in that directory are stored, and REF_INCOMPLETE stubs are created - * for any subdirectories, but the subdirectories themselves are not - * read. The reading is triggered by get_ref_dir(). - */ -struct ref_dir { - int nr, alloc; - - /* - * Entries with index 0 <= i < sorted are sorted by name. New - * entries are appended to the list unsorted, and are sorted - * only when required; thus we avoid the need to sort the list - * after the addition of every reference. - */ - int sorted; - - /* A pointer to the files_ref_store that contains this ref_dir. */ - struct files_ref_store *ref_store; - - struct ref_entry **entries; -}; - -/* - * Bit values for ref_entry::flag. REF_ISSYMREF=0x01, - * REF_ISPACKED=0x02, REF_ISBROKEN=0x04 and REF_BAD_NAME=0x08 are - * public values; see refs.h. - */ - -/* - * The field ref_entry->u.value.peeled of this value entry contains - * the correct peeled value for the reference, which might be - * null_sha1 if the reference is not a tag or if it is broken. - */ -#define REF_KNOWS_PEELED 0x10 - -/* ref_entry represents a directory of references */ -#define REF_DIR 0x20 - -/* - * Entry has not yet been read from disk (used only for REF_DIR - * entries representing loose references) - */ -#define REF_INCOMPLETE 0x40 - -/* - * A ref_entry represents either a reference or a "subdirectory" of - * references. - * - * Each directory in the reference namespace is represented by a - * ref_entry with (flags & REF_DIR) set and containing a subdir member - * that holds the entries in that directory that have been read so - * far. If (flags & REF_INCOMPLETE) is set, then the directory and - * its subdirectories haven't been read yet. REF_INCOMPLETE is only - * used for loose reference directories. - * - * References are represented by a ref_entry with (flags & REF_DIR) - * unset and a value member that describes the reference's value. The - * flag member is at the ref_entry level, but it is also needed to - * interpret the contents of the value field (in other words, a - * ref_value object is not very much use without the enclosing - * ref_entry). - * - * Reference names cannot end with slash and directories' names are - * always stored with a trailing slash (except for the top-level - * directory, which is always denoted by ""). This has two nice - * consequences: (1) when the entries in each subdir are sorted - * lexicographically by name (as they usually are), the references in - * a whole tree can be generated in lexicographic order by traversing - * the tree in left-to-right, depth-first order; (2) the names of - * references and subdirectories cannot conflict, and therefore the - * presence of an empty subdirectory does not block the creation of a - * similarly-named reference. (The fact that reference names with the - * same leading components can conflict *with each other* is a - * separate issue that is regulated by verify_refname_available().) - * - * Please note that the name field contains the fully-qualified - * reference (or subdirectory) name. Space could be saved by only - * storing the relative names. But that would require the full names - * to be generated on the fly when iterating in do_for_each_ref(), and - * would break callback functions, who have always been able to assume - * that the name strings that they are passed will not be freed during - * the iteration. - */ -struct ref_entry { - unsigned char flag; /* ISSYMREF? ISPACKED? */ - union { - struct ref_value value; /* if not (flags&REF_DIR) */ - struct ref_dir subdir; /* if (flags&REF_DIR) */ - } u; - /* - * The full name of the reference (e.g., "refs/heads/master") - * or the full name of the directory with a trailing slash - * (e.g., "refs/heads/"): - */ - char name[FLEX_ARRAY]; -}; - -static void read_loose_refs(const char *dirname, struct ref_dir *dir); -static int search_ref_dir(struct ref_dir *dir, const char *refname, size_t len); -static struct ref_entry *create_dir_entry(struct files_ref_store *ref_store, - const char *dirname, size_t len, - int incomplete); -static void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *entry); -static int files_log_ref_write(struct files_ref_store *refs, - const char *refname, const unsigned char *old_sha1, - const unsigned char *new_sha1, const char *msg, - int flags, struct strbuf *err); - -static struct ref_dir *get_ref_dir(struct ref_entry *entry) -{ - struct ref_dir *dir; - assert(entry->flag & REF_DIR); - dir = &entry->u.subdir; - if (entry->flag & REF_INCOMPLETE) { - read_loose_refs(entry->name, dir); - - /* - * Manually add refs/bisect, which, being - * per-worktree, might not appear in the directory - * listing for refs/ in the main repo. - */ - if (!strcmp(entry->name, "refs/")) { - int pos = search_ref_dir(dir, "refs/bisect/", 12); - if (pos < 0) { - struct ref_entry *child_entry; - child_entry = create_dir_entry(dir->ref_store, - "refs/bisect/", - 12, 1); - add_entry_to_dir(dir, child_entry); - read_loose_refs("refs/bisect", - &child_entry->u.subdir); - } - } - entry->flag &= ~REF_INCOMPLETE; - } - return dir; -} - -static struct ref_entry *create_ref_entry(const char *refname, - const unsigned char *sha1, int flag, - int check_name) -{ - struct ref_entry *ref; - - if (check_name && - check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) - die("Reference has invalid format: '%s'", refname); - FLEX_ALLOC_STR(ref, name, refname); - hashcpy(ref->u.value.oid.hash, sha1); - oidclr(&ref->u.value.peeled); - ref->flag = flag; - return ref; -} - -static void clear_ref_dir(struct ref_dir *dir); - -static void free_ref_entry(struct ref_entry *entry) -{ - if (entry->flag & REF_DIR) { - /* - * Do not use get_ref_dir() here, as that might - * trigger the reading of loose refs. - */ - clear_ref_dir(&entry->u.subdir); - } - free(entry); -} - -/* - * Add a ref_entry to the end of dir (unsorted). Entry is always - * stored directly in dir; no recursion into subdirectories is - * done. - */ -static void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *entry) -{ - ALLOC_GROW(dir->entries, dir->nr + 1, dir->alloc); - dir->entries[dir->nr++] = entry; - /* optimize for the case that entries are added in order */ - if (dir->nr == 1 || - (dir->nr == dir->sorted + 1 && - strcmp(dir->entries[dir->nr - 2]->name, - dir->entries[dir->nr - 1]->name) < 0)) - dir->sorted = dir->nr; -} - -/* - * Clear and free all entries in dir, recursively. - */ -static void clear_ref_dir(struct ref_dir *dir) -{ - int i; - for (i = 0; i < dir->nr; i++) - free_ref_entry(dir->entries[i]); - free(dir->entries); - dir->sorted = dir->nr = dir->alloc = 0; - dir->entries = NULL; -} - -/* - * Create a struct ref_entry object for the specified dirname. - * dirname is the name of the directory with a trailing slash (e.g., - * "refs/heads/") or "" for the top-level directory. - */ -static struct ref_entry *create_dir_entry(struct files_ref_store *ref_store, - const char *dirname, size_t len, - int incomplete) -{ - struct ref_entry *direntry; - FLEX_ALLOC_MEM(direntry, name, dirname, len); - direntry->u.subdir.ref_store = ref_store; - direntry->flag = REF_DIR | (incomplete ? REF_INCOMPLETE : 0); - return direntry; -} - -static int ref_entry_cmp(const void *a, const void *b) -{ - struct ref_entry *one = *(struct ref_entry **)a; - struct ref_entry *two = *(struct ref_entry **)b; - return strcmp(one->name, two->name); -} - -static void sort_ref_dir(struct ref_dir *dir); - -struct string_slice { - size_t len; - const char *str; -}; - -static int ref_entry_cmp_sslice(const void *key_, const void *ent_) -{ - const struct string_slice *key = key_; - const struct ref_entry *ent = *(const struct ref_entry * const *)ent_; - int cmp = strncmp(key->str, ent->name, key->len); - if (cmp) - return cmp; - return '\0' - (unsigned char)ent->name[key->len]; -} - -/* - * Return the index of the entry with the given refname from the - * ref_dir (non-recursively), sorting dir if necessary. Return -1 if - * no such entry is found. dir must already be complete. - */ -static int search_ref_dir(struct ref_dir *dir, const char *refname, size_t len) -{ - struct ref_entry **r; - struct string_slice key; - - if (refname == NULL || !dir->nr) - return -1; - - sort_ref_dir(dir); - key.len = len; - key.str = refname; - r = bsearch(&key, dir->entries, dir->nr, sizeof(*dir->entries), - ref_entry_cmp_sslice); - - if (r == NULL) - return -1; - - return r - dir->entries; -} - -/* - * Search for a directory entry directly within dir (without - * recursing). Sort dir if necessary. subdirname must be a directory - * name (i.e., end in '/'). If mkdir is set, then create the - * directory if it is missing; otherwise, return NULL if the desired - * directory cannot be found. dir must already be complete. - */ -static struct ref_dir *search_for_subdir(struct ref_dir *dir, - const char *subdirname, size_t len, - int mkdir) -{ - int entry_index = search_ref_dir(dir, subdirname, len); - struct ref_entry *entry; - if (entry_index == -1) { - if (!mkdir) - return NULL; - /* - * Since dir is complete, the absence of a subdir - * means that the subdir really doesn't exist; - * therefore, create an empty record for it but mark - * the record complete. - */ - entry = create_dir_entry(dir->ref_store, subdirname, len, 0); - add_entry_to_dir(dir, entry); - } else { - entry = dir->entries[entry_index]; - } - return get_ref_dir(entry); -} - -/* - * If refname is a reference name, find the ref_dir within the dir - * tree that should hold refname. If refname is a directory name - * (i.e., ends in '/'), then return that ref_dir itself. dir must - * represent the top-level directory and must already be complete. - * Sort ref_dirs and recurse into subdirectories as necessary. If - * mkdir is set, then create any missing directories; otherwise, - * return NULL if the desired directory cannot be found. - */ -static struct ref_dir *find_containing_dir(struct ref_dir *dir, - const char *refname, int mkdir) -{ - const char *slash; - for (slash = strchr(refname, '/'); slash; slash = strchr(slash + 1, '/')) { - size_t dirnamelen = slash - refname + 1; - struct ref_dir *subdir; - subdir = search_for_subdir(dir, refname, dirnamelen, mkdir); - if (!subdir) { - dir = NULL; - break; - } - dir = subdir; - } - - return dir; -} - -/* - * Find the value entry with the given name in dir, sorting ref_dirs - * and recursing into subdirectories as necessary. If the name is not - * found or it corresponds to a directory entry, return NULL. - */ -static struct ref_entry *find_ref(struct ref_dir *dir, const char *refname) -{ - int entry_index; - struct ref_entry *entry; - dir = find_containing_dir(dir, refname, 0); - if (!dir) - return NULL; - entry_index = search_ref_dir(dir, refname, strlen(refname)); - if (entry_index == -1) - return NULL; - entry = dir->entries[entry_index]; - return (entry->flag & REF_DIR) ? NULL : entry; -} - -/* - * Remove the entry with the given name from dir, recursing into - * subdirectories as necessary. If refname is the name of a directory - * (i.e., ends with '/'), then remove the directory and its contents. - * If the removal was successful, return the number of entries - * remaining in the directory entry that contained the deleted entry. - * If the name was not found, return -1. Please note that this - * function only deletes the entry from the cache; it does not delete - * it from the filesystem or ensure that other cache entries (which - * might be symbolic references to the removed entry) are updated. - * Nor does it remove any containing dir entries that might be made - * empty by the removal. dir must represent the top-level directory - * and must already be complete. - */ -static int remove_entry(struct ref_dir *dir, const char *refname) -{ - int refname_len = strlen(refname); - int entry_index; - struct ref_entry *entry; - int is_dir = refname[refname_len - 1] == '/'; - if (is_dir) { - /* - * refname represents a reference directory. Remove - * the trailing slash; otherwise we will get the - * directory *representing* refname rather than the - * one *containing* it. - */ - char *dirname = xmemdupz(refname, refname_len - 1); - dir = find_containing_dir(dir, dirname, 0); - free(dirname); - } else { - dir = find_containing_dir(dir, refname, 0); - } - if (!dir) - return -1; - entry_index = search_ref_dir(dir, refname, refname_len); - if (entry_index == -1) - return -1; - entry = dir->entries[entry_index]; - - memmove(&dir->entries[entry_index], - &dir->entries[entry_index + 1], - (dir->nr - entry_index - 1) * sizeof(*dir->entries) - ); - dir->nr--; - if (dir->sorted > entry_index) - dir->sorted--; - free_ref_entry(entry); - return dir->nr; -} - -/* - * Add a ref_entry to the ref_dir (unsorted), recursing into - * subdirectories as necessary. dir must represent the top-level - * directory. Return 0 on success. - */ -static int add_ref(struct ref_dir *dir, struct ref_entry *ref) -{ - dir = find_containing_dir(dir, ref->name, 1); - if (!dir) - return -1; - add_entry_to_dir(dir, ref); - return 0; -} - -/* - * Emit a warning and return true iff ref1 and ref2 have the same name - * and the same sha1. Die if they have the same name but different - * sha1s. - */ -static int is_dup_ref(const struct ref_entry *ref1, const struct ref_entry *ref2) -{ - if (strcmp(ref1->name, ref2->name)) - return 0; - - /* Duplicate name; make sure that they don't conflict: */ - - if ((ref1->flag & REF_DIR) || (ref2->flag & REF_DIR)) - /* This is impossible by construction */ - die("Reference directory conflict: %s", ref1->name); - - if (oidcmp(&ref1->u.value.oid, &ref2->u.value.oid)) - die("Duplicated ref, and SHA1s don't match: %s", ref1->name); - - warning("Duplicated ref: %s", ref1->name); - return 1; -} - -/* - * Sort the entries in dir non-recursively (if they are not already - * sorted) and remove any duplicate entries. - */ -static void sort_ref_dir(struct ref_dir *dir) -{ - int i, j; - struct ref_entry *last = NULL; - - /* - * This check also prevents passing a zero-length array to qsort(), - * which is a problem on some platforms. - */ - if (dir->sorted == dir->nr) - return; - - QSORT(dir->entries, dir->nr, ref_entry_cmp); - - /* Remove any duplicates: */ - for (i = 0, j = 0; j < dir->nr; j++) { - struct ref_entry *entry = dir->entries[j]; - if (last && is_dup_ref(last, entry)) - free_ref_entry(entry); - else - last = dir->entries[i++] = entry; - } - dir->sorted = dir->nr = i; -} - /* * Return true if refname, which has the specified oid and flags, can * be resolved to an object in the database. If the referred-to object @@@ -32,8 -536,358 +32,8 @@@ static int ref_resolves_to_object(cons return 1; } -/* - * Return true if the reference described by entry can be resolved to - * an object in the database; otherwise, emit a warning and return - * false. - */ -static int entry_resolves_to_object(struct ref_entry *entry) -{ - return ref_resolves_to_object(entry->name, - &entry->u.value.oid, entry->flag); -} - -typedef int each_ref_entry_fn(struct ref_entry *entry, void *cb_data); - -/* - * Call fn for each reference in dir that has index in the range - * offset <= index < dir->nr. Recurse into subdirectories that are in - * that index range, sorting them before iterating. This function - * does not sort dir itself; it should be sorted beforehand. fn is - * called for all references, including broken ones. - */ -static int do_for_each_entry_in_dir(struct ref_dir *dir, int offset, - each_ref_entry_fn fn, void *cb_data) -{ - int i; - assert(dir->sorted == dir->nr); - for (i = offset; i < dir->nr; i++) { - struct ref_entry *entry = dir->entries[i]; - int retval; - if (entry->flag & REF_DIR) { - struct ref_dir *subdir = get_ref_dir(entry); - sort_ref_dir(subdir); - retval = do_for_each_entry_in_dir(subdir, 0, fn, cb_data); - } else { - retval = fn(entry, cb_data); - } - if (retval) - return retval; - } - return 0; -} - -/* - * Load all of the refs from the dir into our in-memory cache. The hard work - * of loading loose refs is done by get_ref_dir(), so we just need to recurse - * through all of the sub-directories. We do not even need to care about - * sorting, as traversal order does not matter to us. - */ -static void prime_ref_dir(struct ref_dir *dir) -{ - int i; - for (i = 0; i < dir->nr; i++) { - struct ref_entry *entry = dir->entries[i]; - if (entry->flag & REF_DIR) - prime_ref_dir(get_ref_dir(entry)); - } -} - -/* - * A level in the reference hierarchy that is currently being iterated - * through. - */ -struct cache_ref_iterator_level { - /* - * The ref_dir being iterated over at this level. The ref_dir - * is sorted before being stored here. - */ - struct ref_dir *dir; - - /* - * The index of the current entry within dir (which might - * itself be a directory). If index == -1, then the iteration - * hasn't yet begun. If index == dir->nr, then the iteration - * through this level is over. - */ - int index; -}; - -/* - * Represent an iteration through a ref_dir in the memory cache. The - * iteration recurses through subdirectories. - */ -struct cache_ref_iterator { - struct ref_iterator base; - - /* - * The number of levels currently on the stack. This is always - * at least 1, because when it becomes zero the iteration is - * ended and this struct is freed. - */ - size_t levels_nr; - - /* The number of levels that have been allocated on the stack */ - size_t levels_alloc; - - /* - * A stack of levels. levels[0] is the uppermost level that is - * being iterated over in this iteration. (This is not - * necessary the top level in the references hierarchy. If we - * are iterating through a subtree, then levels[0] will hold - * the ref_dir for that subtree, and subsequent levels will go - * on from there.) - */ - struct cache_ref_iterator_level *levels; -}; - -static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator) -{ - struct cache_ref_iterator *iter = - (struct cache_ref_iterator *)ref_iterator; - - while (1) { - struct cache_ref_iterator_level *level = - &iter->levels[iter->levels_nr - 1]; - struct ref_dir *dir = level->dir; - struct ref_entry *entry; - - if (level->index == -1) - sort_ref_dir(dir); - - if (++level->index == level->dir->nr) { - /* This level is exhausted; pop up a level */ - if (--iter->levels_nr == 0) - return ref_iterator_abort(ref_iterator); - - continue; - } - - entry = dir->entries[level->index]; - - if (entry->flag & REF_DIR) { - /* push down a level */ - ALLOC_GROW(iter->levels, iter->levels_nr + 1, - iter->levels_alloc); - - level = &iter->levels[iter->levels_nr++]; - level->dir = get_ref_dir(entry); - level->index = -1; - } else { - iter->base.refname = entry->name; - iter->base.oid = &entry->u.value.oid; - iter->base.flags = entry->flag; - return ITER_OK; - } - } -} - -static enum peel_status peel_entry(struct ref_entry *entry, int repeel); - -static int cache_ref_iterator_peel(struct ref_iterator *ref_iterator, - struct object_id *peeled) -{ - struct cache_ref_iterator *iter = - (struct cache_ref_iterator *)ref_iterator; - struct cache_ref_iterator_level *level; - struct ref_entry *entry; - - level = &iter->levels[iter->levels_nr - 1]; - - if (level->index == -1) - die("BUG: peel called before advance for cache iterator"); - - entry = level->dir->entries[level->index]; - - if (peel_entry(entry, 0)) - return -1; - oidcpy(peeled, &entry->u.value.peeled); - return 0; -} - -static int cache_ref_iterator_abort(struct ref_iterator *ref_iterator) -{ - struct cache_ref_iterator *iter = - (struct cache_ref_iterator *)ref_iterator; - - free(iter->levels); - base_ref_iterator_free(ref_iterator); - return ITER_DONE; -} - -static struct ref_iterator_vtable cache_ref_iterator_vtable = { - cache_ref_iterator_advance, - cache_ref_iterator_peel, - cache_ref_iterator_abort -}; - -static struct ref_iterator *cache_ref_iterator_begin(struct ref_dir *dir) -{ - struct cache_ref_iterator *iter; - struct ref_iterator *ref_iterator; - struct cache_ref_iterator_level *level; - - iter = xcalloc(1, sizeof(*iter)); - ref_iterator = &iter->base; - base_ref_iterator_init(ref_iterator, &cache_ref_iterator_vtable); - ALLOC_GROW(iter->levels, 10, iter->levels_alloc); - - iter->levels_nr = 1; - level = &iter->levels[0]; - level->index = -1; - level->dir = dir; - - return ref_iterator; -} - -struct nonmatching_ref_data { - const struct string_list *skip; - const char *conflicting_refname; -}; - -static int nonmatching_ref_fn(struct ref_entry *entry, void *vdata) -{ - struct nonmatching_ref_data *data = vdata; - - if (data->skip && string_list_has_string(data->skip, entry->name)) - return 0; - - data->conflicting_refname = entry->name; - return 1; -} - -/* - * Return 0 if a reference named refname could be created without - * conflicting with the name of an existing reference in dir. - * See verify_refname_available for more information. - */ -static int verify_refname_available_dir(const char *refname, - const struct string_list *extras, - const struct string_list *skip, - struct ref_dir *dir, - struct strbuf *err) -{ - const char *slash; - const char *extra_refname; - int pos; - struct strbuf dirname = STRBUF_INIT; - int ret = -1; - - /* - * For the sake of comments in this function, suppose that - * refname is "refs/foo/bar". - */ - - assert(err); - - strbuf_grow(&dirname, strlen(refname) + 1); - for (slash = strchr(refname, '/'); slash; slash = strchr(slash + 1, '/')) { - /* Expand dirname to the new prefix, not including the trailing slash: */ - strbuf_add(&dirname, refname + dirname.len, slash - refname - dirname.len); - - /* - * We are still at a leading dir of the refname (e.g., - * "refs/foo"; if there is a reference with that name, - * it is a conflict, *unless* it is in skip. - */ - if (dir) { - pos = search_ref_dir(dir, dirname.buf, dirname.len); - if (pos >= 0 && - (!skip || !string_list_has_string(skip, dirname.buf))) { - /* - * We found a reference whose name is - * a proper prefix of refname; e.g., - * "refs/foo", and is not in skip. - */ - strbuf_addf(err, "'%s' exists; cannot create '%s'", - dirname.buf, refname); - goto cleanup; - } - } - - if (extras && string_list_has_string(extras, dirname.buf) && - (!skip || !string_list_has_string(skip, dirname.buf))) { - strbuf_addf(err, "cannot process '%s' and '%s' at the same time", - refname, dirname.buf); - goto cleanup; - } - - /* - * Otherwise, we can try to continue our search with - * the next component. So try to look up the - * directory, e.g., "refs/foo/". If we come up empty, - * we know there is nothing under this whole prefix, - * but even in that case we still have to continue the - * search for conflicts with extras. - */ - strbuf_addch(&dirname, '/'); - if (dir) { - pos = search_ref_dir(dir, dirname.buf, dirname.len); - if (pos < 0) { - /* - * There was no directory "refs/foo/", - * so there is nothing under this - * whole prefix. So there is no need - * to continue looking for conflicting - * references. But we need to continue - * looking for conflicting extras. - */ - dir = NULL; - } else { - dir = get_ref_dir(dir->entries[pos]); - } - } - } - - /* - * We are at the leaf of our refname (e.g., "refs/foo/bar"). - * There is no point in searching for a reference with that - * name, because a refname isn't considered to conflict with - * itself. But we still need to check for references whose - * names are in the "refs/foo/bar/" namespace, because they - * *do* conflict. - */ - strbuf_addstr(&dirname, refname + dirname.len); - strbuf_addch(&dirname, '/'); - - if (dir) { - pos = search_ref_dir(dir, dirname.buf, dirname.len); - - if (pos >= 0) { - /* - * We found a directory named "$refname/" - * (e.g., "refs/foo/bar/"). It is a problem - * iff it contains any ref that is not in - * "skip". - */ - struct nonmatching_ref_data data; - - data.skip = skip; - data.conflicting_refname = NULL; - dir = get_ref_dir(dir->entries[pos]); - sort_ref_dir(dir); - if (do_for_each_entry_in_dir(dir, 0, nonmatching_ref_fn, &data)) { - strbuf_addf(err, "'%s' exists; cannot create '%s'", - data.conflicting_refname, refname); - goto cleanup; - } - } - } - - extra_refname = find_descendant_ref(dirname.buf, extras, skip); - if (extra_refname) - strbuf_addf(err, "cannot process '%s' and '%s' at the same time", - refname, extra_refname); - else - ret = 0; - -cleanup: - strbuf_release(&dirname); - return ret; -} - struct packed_ref_cache { - struct ref_entry *root; + struct ref_cache *cache; /* * Count of references to the data structure in this instance, @@@ -68,7 -922,7 +68,7 @@@ struct files_ref_store char *gitcommondir; char *packed_refs_path; - struct ref_entry *loose; + struct ref_cache *loose; struct packed_ref_cache *packed; }; @@@ -90,7 -944,7 +90,7 @@@ static void acquire_packed_ref_cache(st static int release_packed_ref_cache(struct packed_ref_cache *packed_refs) { if (!--packed_refs->referrers) { - free_ref_entry(packed_refs->root); + free_ref_cache(packed_refs->cache); stat_validity_clear(&packed_refs->validity); free(packed_refs); return 1; @@@ -114,7 -968,7 +114,7 @@@ static void clear_packed_ref_cache(stru static void clear_loose_ref_cache(struct files_ref_store *refs) { if (refs->loose) { - free_ref_entry(refs->loose); + free_ref_cache(refs->loose); refs->loose = NULL; } } @@@ -287,7 -1141,7 +287,7 @@@ static void read_packed_refs(FILE *f, s if (peeled == PEELED_FULLY || (peeled == PEELED_TAGS && starts_with(refname, "refs/tags/"))) last->flag |= REF_KNOWS_PEELED; - add_ref(dir, last); + add_ref_entry(dir, last); continue; } if (last && @@@ -375,12 -1229,11 +375,12 @@@ static struct packed_ref_cache *get_pac refs->packed = xcalloc(1, sizeof(*refs->packed)); acquire_packed_ref_cache(refs->packed); - refs->packed->root = create_dir_entry(refs, "", 0, 0); + refs->packed->cache = create_ref_cache(&refs->base, NULL); + refs->packed->cache->root->flag &= ~REF_INCOMPLETE; f = fopen(packed_refs_file, "r"); if (f) { stat_validity_update(&refs->packed->validity, fileno(f)); - read_packed_refs(f, get_ref_dir(refs->packed->root)); + read_packed_refs(f, get_ref_dir(refs->packed->cache->root)); fclose(f); } } @@@ -389,7 -1242,7 +389,7 @@@ static struct ref_dir *get_packed_ref_dir(struct packed_ref_cache *packed_ref_cache) { - return get_ref_dir(packed_ref_cache->root); + return get_ref_dir(packed_ref_cache->cache->root); } static struct ref_dir *get_packed_refs(struct files_ref_store *refs) @@@ -410,8 -1263,8 +410,8 @@@ static void add_packed_ref(struct files if (!packed_ref_cache->lock) die("internal error: packed refs not locked"); - add_ref(get_packed_ref_dir(packed_ref_cache), - create_ref_entry(refname, sha1, REF_ISPACKED, 1)); + add_ref_entry(get_packed_ref_dir(packed_ref_cache), + create_ref_entry(refname, sha1, REF_ISPACKED, 1)); } /* @@@ -419,11 -1272,9 +419,11 @@@ * (without recursing). dirname must end with '/'. dir must be the * directory entry corresponding to dirname. */ -static void read_loose_refs(const char *dirname, struct ref_dir *dir) +static void loose_fill_ref_dir(struct ref_store *ref_store, + struct ref_dir *dir, const char *dirname) { - struct files_ref_store *refs = dir->ref_store; + struct files_ref_store *refs = + files_downcast(ref_store, REF_STORE_READ, "fill_ref_dir"); DIR *d; struct dirent *de; int dirnamelen = strlen(dirname); @@@ -459,7 -1310,7 +459,7 @@@ } else if (S_ISDIR(st.st_mode)) { strbuf_addch(&refname, '/'); add_entry_to_dir(dir, - create_dir_entry(refs, refname.buf, + create_dir_entry(dir->cache, refname.buf, refname.len, 1)); } else { if (!refs_resolve_ref_unsafe(&refs->base, @@@ -496,24 -1347,9 +496,24 @@@ strbuf_release(&refname); strbuf_release(&path); closedir(d); + + /* + * Manually add refs/bisect, which, being per-worktree, might + * not appear in the directory listing for refs/ in the main + * repo. + */ + if (!strcmp(dirname, "refs/")) { + int pos = search_ref_dir(dir, "refs/bisect/", 12); + + if (pos < 0) { + struct ref_entry *child_entry = create_dir_entry( + dir->cache, "refs/bisect/", 12, 1); + add_entry_to_dir(dir, child_entry); + } + } } -static struct ref_dir *get_loose_refs(struct files_ref_store *refs) +static struct ref_cache *get_loose_ref_cache(struct files_ref_store *refs) { if (!refs->loose) { /* @@@ -521,19 -1357,14 +521,19 @@@ * are about to read the only subdirectory that can * hold references: */ - refs->loose = create_dir_entry(refs, "", 0, 0); + refs->loose = create_ref_cache(&refs->base, loose_fill_ref_dir); + + /* We're going to fill the top level ourselves: */ + refs->loose->root->flag &= ~REF_INCOMPLETE; + /* - * Create an incomplete entry for "refs/": + * Add an incomplete entry for "refs/" (to be filled + * lazily): */ - add_entry_to_dir(get_ref_dir(refs->loose), - create_dir_entry(refs, "refs/", 5, 1)); + add_entry_to_dir(get_ref_dir(refs->loose->root), + create_dir_entry(refs->loose, "refs/", 5, 1)); } - return get_ref_dir(refs->loose); + return refs->loose; } /* @@@ -543,7 -1374,7 +543,7 @@@ static struct ref_entry *get_packed_ref(struct files_ref_store *refs, const char *refname) { - return find_ref(get_packed_refs(refs), refname); + return find_ref_entry(get_packed_refs(refs), refname); } /* @@@ -733,7 -1564,7 +733,7 @@@ static void unlock_ref(struct ref_lock * * If the reference doesn't already exist, verify that refname doesn't * have a D/F conflict with any existing references. extras and skip - * are passed to verify_refname_available_dir() for this check. + * are passed to refs_verify_refname_available() for this check. * * If mustexist is not set and the reference is not found or is * broken, lock the reference anyway but clear sha1. @@@ -748,7 -1579,7 +748,7 @@@ * * but it includes a lot more code to * - Deal with possible races with other processes - * - Avoid calling verify_refname_available_dir() when it can be + * - Avoid calling refs_verify_refname_available() when it can be * avoided, namely if we were successfully able to read the ref * - Generate informative error messages in the case of failure */ @@@ -805,8 -1636,7 +805,8 @@@ retry } else { /* * The error message set by - * verify_refname_available_dir() is OK. + * refs_verify_refname_available() is + * OK. */ ret = TRANSACTION_NAME_CONFLICT; } @@@ -896,9 -1726,10 +896,9 @@@ goto error_return; } else if (remove_dir_recursively(&ref_file, REMOVE_DIR_EMPTY_ONLY)) { - if (verify_refname_available_dir( - refname, extras, skip, - get_loose_refs(refs), - err)) { + if (refs_verify_refname_available( + &refs->base, refname, + extras, skip, err)) { /* * The error message set by * verify_refname_available() is OK. @@@ -930,13 -1761,16 +930,13 @@@ /* * If the ref did not exist and we are creating it, - * make sure there is no existing packed ref whose - * name begins with our refname, nor a packed ref - * whose name is a proper prefix of our refname. + * make sure there is no existing ref that conflicts + * with refname: */ - if (verify_refname_available_dir( - refname, extras, skip, - get_packed_refs(refs), - err)) { + if (refs_verify_refname_available( + &refs->base, refname, + extras, skip, err)) goto error_return; - } } ret = 0; @@@ -951,6 -1785,41 +951,6 @@@ out return ret; } -/* - * Peel the entry (if possible) and return its new peel_status. If - * repeel is true, re-peel the entry even if there is an old peeled - * value that is already stored in it. - * - * It is OK to call this function with a packed reference entry that - * might be stale and might even refer to an object that has since - * been garbage-collected. In such a case, if the entry has - * REF_KNOWS_PEELED then leave the status unchanged and return - * PEEL_PEELED or PEEL_NON_TAG; otherwise, return PEEL_INVALID. - */ -static enum peel_status peel_entry(struct ref_entry *entry, int repeel) -{ - enum peel_status status; - - if (entry->flag & REF_KNOWS_PEELED) { - if (repeel) { - entry->flag &= ~REF_KNOWS_PEELED; - oidclr(&entry->u.value.peeled); - } else { - return is_null_oid(&entry->u.value.peeled) ? - PEEL_NON_TAG : PEEL_PEELED; - } - } - if (entry->flag & REF_ISBROKEN) - return PEEL_BROKEN; - if (entry->flag & REF_ISSYMREF) - return PEEL_IS_SYMREF; - - status = peel_object(entry->u.value.oid.hash, entry->u.value.peeled.hash); - if (status == PEEL_PEELED || status == PEEL_NON_TAG) - entry->flag |= REF_KNOWS_PEELED; - return status; -} - static int files_peel_ref(struct ref_store *ref_store, const char *refname, unsigned char *sha1) { @@@ -1066,6 -1935,7 +1066,6 @@@ static struct ref_iterator *files_ref_i const char *prefix, unsigned int flags) { struct files_ref_store *refs; - struct ref_dir *loose_dir, *packed_dir; struct ref_iterator *loose_iter, *packed_iter; struct files_ref_iterator *iter; struct ref_iterator *ref_iterator; @@@ -1089,24 -1959,41 +1089,24 @@@ * condition if loose refs are migrated to the packed-refs * file by a simultaneous process, but our in-memory view is * from before the migration. We ensure this as follows: - * First, we call prime_ref_dir(), which pre-reads the loose - * references for the subtree into the cache. (If they've - * already been read, that's OK; we only need to guarantee - * that they're read before the packed refs, not *how much* - * before.) After that, we call get_packed_ref_cache(), which - * internally checks whether the packed-ref cache is up to - * date with what is on disk, and re-reads it if not. + * First, we call start the loose refs iteration with its + * `prime_ref` argument set to true. This causes the loose + * references in the subtree to be pre-read into the cache. + * (If they've already been read, that's OK; we only need to + * guarantee that they're read before the packed refs, not + * *how much* before.) After that, we call + * get_packed_ref_cache(), which internally checks whether the + * packed-ref cache is up to date with what is on disk, and + * re-reads it if not. */ - loose_dir = get_loose_refs(refs); - - if (prefix && *prefix) - loose_dir = find_containing_dir(loose_dir, prefix, 0); - - if (loose_dir) { - prime_ref_dir(loose_dir); - loose_iter = cache_ref_iterator_begin(loose_dir); - } else { - /* There's nothing to iterate over. */ - loose_iter = empty_ref_iterator_begin(); - } + loose_iter = cache_ref_iterator_begin(get_loose_ref_cache(refs), + prefix, 1); iter->packed_ref_cache = get_packed_ref_cache(refs); acquire_packed_ref_cache(iter->packed_ref_cache); - packed_dir = get_packed_ref_dir(iter->packed_ref_cache); - - if (prefix && *prefix) - packed_dir = find_containing_dir(packed_dir, prefix, 0); - - if (packed_dir) { - packed_iter = cache_ref_iterator_begin(packed_dir); - } else { - /* There's nothing to iterate over. */ - packed_iter = empty_ref_iterator_begin(); - } + packed_iter = cache_ref_iterator_begin(iter->packed_ref_cache->cache, + prefix, 0); iter->iter0 = overlay_ref_iterator_begin(loose_iter, packed_iter); iter->flags = flags; @@@ -1209,9 -2096,9 +1209,9 @@@ static struct ref_lock *lock_ref_sha1_b */ if (remove_empty_directories(&ref_file)) { last_errno = errno; - if (!verify_refname_available_dir( - refname, extras, skip, - get_loose_refs(refs), err)) + if (!refs_verify_refname_available( + &refs->base, + refname, extras, skip, err)) strbuf_addf(err, "there are still refs under '%s'", refname); goto error_return; @@@ -1223,8 -2110,9 +1223,8 @@@ if (!resolved) { last_errno = errno; if (last_errno != ENOTDIR || - !verify_refname_available_dir( - refname, extras, skip, - get_loose_refs(refs), err)) + !refs_verify_refname_available(&refs->base, refname, + extras, skip, err)) strbuf_addf(err, "unable to resolve reference '%s': %s", refname, strerror(last_errno)); @@@ -1238,8 -2126,9 +1238,8 @@@ * our refname. */ if (is_null_oid(&lock->old_oid) && - verify_refname_available_dir(refname, extras, skip, - get_packed_refs(refs), - err)) { + refs_verify_refname_available(&refs->base, refname, + extras, skip, err)) { last_errno = ENOTDIR; goto error_return; } @@@ -1274,15 -2163,30 +1274,15 @@@ * Write an entry to the packed-refs file for the specified refname. * If peeled is non-NULL, write it as the entry's peeled value. */ -static void write_packed_entry(FILE *fh, char *refname, unsigned char *sha1, - unsigned char *peeled) +static void write_packed_entry(FILE *fh, const char *refname, + const unsigned char *sha1, + const unsigned char *peeled) { fprintf_or_die(fh, "%s %s\n", sha1_to_hex(sha1), refname); if (peeled) fprintf_or_die(fh, "^%s\n", sha1_to_hex(peeled)); } -/* - * An each_ref_entry_fn that writes the entry to a packed-refs file. - */ -static int write_packed_entry_fn(struct ref_entry *entry, void *cb_data) -{ - enum peel_status peel_status = peel_entry(entry, 0); - - if (peel_status != PEEL_PEELED && peel_status != PEEL_NON_TAG) - error("internal error: %s is not a valid packed reference!", - entry->name); - write_packed_entry(cb_data, entry->name, entry->u.value.oid.hash, - peel_status == PEEL_PEELED ? - entry->u.value.peeled.hash : NULL); - return 0; -} - /* * Lock the packed-refs file for writing. Flags is passed to * hold_lock_file_for_update(). Return 0 on success. On errors, set @@@ -1328,10 -2232,9 +1328,10 @@@ static int commit_packed_refs(struct fi { struct packed_ref_cache *packed_ref_cache = get_packed_ref_cache(refs); - int error = 0; + int ok, error = 0; int save_errno = 0; FILE *out; + struct ref_iterator *iter; files_assert_main_repository(refs, "commit_packed_refs"); @@@ -1343,18 -2246,8 +1343,18 @@@ die_errno("unable to fdopen packed-refs descriptor"); fprintf_or_die(out, "%s", PACKED_REFS_HEADER); - do_for_each_entry_in_dir(get_packed_ref_dir(packed_ref_cache), - 0, write_packed_entry_fn, out); + + iter = cache_ref_iterator_begin(packed_ref_cache->cache, NULL, 0); + while ((ok = ref_iterator_advance(iter)) == ITER_OK) { + struct object_id peeled; + int peel_error = ref_iterator_peel(iter, &peeled); + + write_packed_entry(out, iter->refname, iter->oid->hash, + peel_error ? NULL : peeled.hash); + } + + if (ok != ITER_DONE) + die("error while iterating over references"); if (commit_lock_file(packed_ref_cache->lock)) { save_errno = errno; @@@ -1392,6 -2285,65 +1392,6 @@@ struct ref_to_prune char name[FLEX_ARRAY]; }; -struct pack_refs_cb_data { - unsigned int flags; - struct ref_dir *packed_refs; - struct ref_to_prune *ref_to_prune; -}; - -/* - * An each_ref_entry_fn that is run over loose references only. If - * the loose reference can be packed, add an entry in the packed ref - * cache. If the reference should be pruned, also add it to - * ref_to_prune in the pack_refs_cb_data. - */ -static int pack_if_possible_fn(struct ref_entry *entry, void *cb_data) -{ - struct pack_refs_cb_data *cb = cb_data; - enum peel_status peel_status; - struct ref_entry *packed_entry; - int is_tag_ref = starts_with(entry->name, "refs/tags/"); - - /* Do not pack per-worktree refs: */ - if (ref_type(entry->name) != REF_TYPE_NORMAL) - return 0; - - /* ALWAYS pack tags */ - if (!(cb->flags & PACK_REFS_ALL) && !is_tag_ref) - return 0; - - /* Do not pack symbolic or broken refs: */ - if ((entry->flag & REF_ISSYMREF) || !entry_resolves_to_object(entry)) - return 0; - - /* Add a packed ref cache entry equivalent to the loose entry. */ - peel_status = peel_entry(entry, 1); - if (peel_status != PEEL_PEELED && peel_status != PEEL_NON_TAG) - die("internal error peeling reference %s (%s)", - entry->name, oid_to_hex(&entry->u.value.oid)); - packed_entry = find_ref(cb->packed_refs, entry->name); - if (packed_entry) { - /* Overwrite existing packed entry with info from loose entry */ - packed_entry->flag = REF_ISPACKED | REF_KNOWS_PEELED; - oidcpy(&packed_entry->u.value.oid, &entry->u.value.oid); - } else { - packed_entry = create_ref_entry(entry->name, entry->u.value.oid.hash, - REF_ISPACKED | REF_KNOWS_PEELED, 0); - add_ref(cb->packed_refs, packed_entry); - } - oidcpy(&packed_entry->u.value.peeled, &entry->u.value.peeled); - - /* Schedule the loose reference for pruning if requested. */ - if ((cb->flags & PACK_REFS_PRUNE)) { - struct ref_to_prune *n; - FLEX_ALLOC_STR(n, name, entry->name); - hashcpy(n->sha1, entry->u.value.oid.hash); - n->next = cb->ref_to_prune; - cb->ref_to_prune = n; - } - return 0; -} - enum { REMOVE_EMPTY_PARENTS_REF = 0x01, REMOVE_EMPTY_PARENTS_REFLOG = 0x02 @@@ -1481,73 -2433,21 +1481,73 @@@ static int files_pack_refs(struct ref_s struct files_ref_store *refs = files_downcast(ref_store, REF_STORE_WRITE | REF_STORE_ODB, "pack_refs"); - struct pack_refs_cb_data cbdata; - - memset(&cbdata, 0, sizeof(cbdata)); - cbdata.flags = flags; + struct ref_iterator *iter; + struct ref_dir *packed_refs; + int ok; + struct ref_to_prune *refs_to_prune = NULL; lock_packed_refs(refs, LOCK_DIE_ON_ERROR); - cbdata.packed_refs = get_packed_refs(refs); + packed_refs = get_packed_refs(refs); + + iter = cache_ref_iterator_begin(get_loose_ref_cache(refs), NULL, 0); + while ((ok = ref_iterator_advance(iter)) == ITER_OK) { + /* + * If the loose reference can be packed, add an entry + * in the packed ref cache. If the reference should be + * pruned, also add it to refs_to_prune. + */ + struct ref_entry *packed_entry; + int is_tag_ref = starts_with(iter->refname, "refs/tags/"); + + /* Do not pack per-worktree refs: */ + if (ref_type(iter->refname) != REF_TYPE_NORMAL) + continue; - do_for_each_entry_in_dir(get_loose_refs(refs), 0, - pack_if_possible_fn, &cbdata); + /* ALWAYS pack tags */ + if (!(flags & PACK_REFS_ALL) && !is_tag_ref) + continue; + + /* Do not pack symbolic or broken refs: */ + if (iter->flags & REF_ISSYMREF) + continue; + + if (!ref_resolves_to_object(iter->refname, iter->oid, iter->flags)) + continue; + + /* + * Create an entry in the packed-refs cache equivalent + * to the one from the loose ref cache, except that + * we don't copy the peeled status, because we want it + * to be re-peeled. + */ + packed_entry = find_ref_entry(packed_refs, iter->refname); + if (packed_entry) { + /* Overwrite existing packed entry with info from loose entry */ + packed_entry->flag = REF_ISPACKED; + oidcpy(&packed_entry->u.value.oid, iter->oid); + } else { + packed_entry = create_ref_entry(iter->refname, iter->oid->hash, + REF_ISPACKED, 0); + add_ref_entry(packed_refs, packed_entry); + } + oidclr(&packed_entry->u.value.peeled); + + /* Schedule the loose reference for pruning if requested. */ + if ((flags & PACK_REFS_PRUNE)) { + struct ref_to_prune *n; + FLEX_ALLOC_STR(n, name, iter->refname); + hashcpy(n->sha1, iter->oid->hash); + n->next = refs_to_prune; + refs_to_prune = n; + } + } + if (ok != ITER_DONE) + die("error while iterating over references"); if (commit_packed_refs(refs)) die_errno("unable to overwrite old ref-pack file"); - prune_refs(refs, cbdata.ref_to_prune); + prune_refs(refs, refs_to_prune); return 0; } @@@ -1588,7 -2488,7 +1588,7 @@@ static int repack_without_refs(struct f /* Remove refnames from the cache */ for_each_string_list_item(refname, refnames) - if (remove_entry(packed, refname->string) != -1) + if (remove_entry_from_dir(packed, refname->string) != -1) removed = 1; if (!removed) { /* @@@ -1708,6 -2608,26 +1708,6 @@@ static int rename_tmp_log(struct files_ return ret; } -static int files_verify_refname_available(struct ref_store *ref_store, - const char *newname, - const struct string_list *extras, - const struct string_list *skip, - struct strbuf *err) -{ - struct files_ref_store *refs = - files_downcast(ref_store, REF_STORE_READ, "verify_refname_available"); - struct ref_dir *packed_refs = get_packed_refs(refs); - struct ref_dir *loose_refs = get_loose_refs(refs); - - if (verify_refname_available_dir(newname, extras, skip, - packed_refs, err) || - verify_refname_available_dir(newname, extras, skip, - loose_refs, err)) - return -1; - - return 0; -} - static int write_ref_to_lockfile(struct ref_lock *lock, const unsigned char *sha1, struct strbuf *err); static int commit_ref_update(struct files_ref_store *refs, @@@ -2240,6 -3160,50 +2240,6 @@@ static int files_create_symref(struct r return ret; } -int set_worktree_head_symref(const char *gitdir, const char *target, const char *logmsg) -{ - /* - * FIXME: this obviously will not work well for future refs - * backends. This function needs to die. - */ - struct files_ref_store *refs = - files_downcast(get_main_ref_store(), - REF_STORE_WRITE, - "set_head_symref"); - - static struct lock_file head_lock; - struct ref_lock *lock; - struct strbuf head_path = STRBUF_INIT; - const char *head_rel; - int ret; - - strbuf_addf(&head_path, "%s/HEAD", absolute_path(gitdir)); - if (hold_lock_file_for_update(&head_lock, head_path.buf, - LOCK_NO_DEREF) < 0) { - struct strbuf err = STRBUF_INIT; - unable_to_lock_message(head_path.buf, errno, &err); - error("%s", err.buf); - strbuf_release(&err); - strbuf_release(&head_path); - return -1; - } - - /* head_rel will be "HEAD" for the main tree, "worktrees/wt/HEAD" for - linked trees */ - head_rel = remove_leading_path(head_path.buf, - absolute_path(get_git_common_dir())); - /* to make use of create_symref_locked(), initialize ref_lock */ - lock = xcalloc(1, sizeof(struct ref_lock)); - lock->lk = &head_lock; - lock->ref_name = xstrdup(head_rel); - - ret = create_symref_locked(refs, lock, head_rel, target, logmsg); - - unlock_ref(lock); /* will free lock */ - strbuf_release(&head_path); - return ret; -} - static int files_reflog_exists(struct ref_store *ref_store, const char *refname) { @@@ -2273,7 -3237,7 +2273,7 @@@ static int show_one_reflog_ent(struct s { struct object_id ooid, noid; char *email_end, *message; - unsigned long timestamp; + timestamp_t timestamp; int tz; const char *p = sb->buf; @@@ -2283,7 -3247,7 +2283,7 @@@ parse_oid_hex(p, &noid, &p) || *p++ != ' ' || !(email_end = strchr(p, '>')) || email_end[1] != ' ' || - !(timestamp = strtoul(email_end + 2, &message, 10)) || + !(timestamp = parse_timestamp(email_end + 2, &message, 10)) || !message || message[0] != ' ' || (message[1] != '+' && message[1] != '-') || !isdigit(message[2]) || !isdigit(message[3]) || @@@ -2330,8 -3294,8 +2330,8 @@@ static int files_for_each_reflog_ent_re /* Jump to the end */ if (fseek(logfp, 0, SEEK_END) < 0) - return error("cannot seek back reflog for %s: %s", - refname, strerror(errno)); + ret = error("cannot seek back reflog for %s: %s", + refname, strerror(errno)); pos = ftell(logfp); while (!ret && 0 < pos) { int cnt; @@@ -2341,17 -3305,13 +2341,17 @@@ /* Fill next block from the end */ cnt = (sizeof(buf) < pos) ? sizeof(buf) : pos; - if (fseek(logfp, pos - cnt, SEEK_SET)) - return error("cannot seek back reflog for %s: %s", - refname, strerror(errno)); + if (fseek(logfp, pos - cnt, SEEK_SET)) { + ret = error("cannot seek back reflog for %s: %s", + refname, strerror(errno)); + break; + } nread = fread(buf, cnt, 1, logfp); - if (nread != 1) - return error("cannot read %d bytes from reflog for %s: %s", - cnt, refname, strerror(errno)); + if (nread != 1) { + ret = error("cannot read %d bytes from reflog for %s: %s", + cnt, refname, strerror(errno)); + break; + } pos -= cnt; scanp = endp = buf + cnt; @@@ -3154,7 -4114,7 +3154,7 @@@ struct expire_reflog_cb }; static int expire_reflog_ent(struct object_id *ooid, struct object_id *noid, - const char *email, unsigned long timestamp, int tz, + const char *email, timestamp_t timestamp, int tz, const char *message, void *cb_data) { struct expire_reflog_cb *cb = cb_data; @@@ -3171,7 -4131,7 +3171,7 @@@ printf("prune %s", message); } else { if (cb->newlog) { - fprintf(cb->newlog, "%s %s %s %lu %+05d\t%s", + fprintf(cb->newlog, "%s %s %s %"PRItime" %+05d\t%s", oid_to_hex(ooid), oid_to_hex(noid), email, timestamp, tz, message); oidcpy(&cb->last_kept_oid, noid); @@@ -3330,6 -4290,7 +3330,6 @@@ struct ref_storage_be refs_be_files = files_ref_iterator_begin, files_read_raw_ref, - files_verify_refname_available, files_reflog_iterator_begin, files_for_each_reflog_ent, diff --combined t/helper/test-ref-store.c index 4a487c014e,9077ec2c33..fba85e7da5 --- a/t/helper/test-ref-store.c +++ b/t/helper/test-ref-store.c @@@ -1,6 -1,5 +1,6 @@@ #include "cache.h" #include "refs.h" +#include "worktree.h" static const char *notnull(const char *arg, const char *name) { @@@ -33,23 -32,6 +33,23 @@@ static const char **get_store(const cha strbuf_release(&sb); *refs = get_submodule_ref_store(gitdir); + } else if (skip_prefix(argv[0], "worktree:", &gitdir)) { + struct worktree **p, **worktrees = get_worktrees(0); + + for (p = worktrees; *p; p++) { + struct worktree *wt = *p; + + if (!wt->id) { + /* special case for main worktree */ + if (!strcmp(gitdir, "main")) + break; + } else if (!strcmp(gitdir, wt->id)) + break; + } + if (!*p) + die("no such worktree: %s", gitdir); + + *refs = get_worktree_ref_store(*p); } else die("unknown backend %s", argv[0]); @@@ -156,10 -138,10 +156,10 @@@ static int cmd_for_each_reflog(struct r } static int each_reflog(struct object_id *old_oid, struct object_id *new_oid, - const char *committer, unsigned long timestamp, + const char *committer, timestamp_t timestamp, int tz, const char *msg, void *cb_data) { - printf("%s %s %s %lu %d %s\n", + printf("%s %s %s %"PRItime" %d %s\n", oid_to_hex(old_oid), oid_to_hex(new_oid), committer, timestamp, tz, msg); return 0; diff --combined t/test-lib.sh index 014136fb06,8d25cb7c18..26b3edfb2e --- a/t/test-lib.sh +++ b/t/test-lib.sh @@@ -761,15 -761,10 +761,15 @@@ test_done () say "1..$test_count$skip_all" fi - test -d "$remove_trash" && - cd "$(dirname "$remove_trash")" && - rm -rf "$(basename "$remove_trash")" + if test -z "$debug" + then + test -d "$TRASH_DIRECTORY" || + error "Tests passed but trash directory already removed before test cleanup; aborting" + cd "$TRASH_DIRECTORY/.." && + rm -fr "$TRASH_DIRECTORY" || + error "Tests passed but test cleanup failed; aborting" + fi test_at_end_hook_ exit 0 ;; @@@ -924,6 -919,7 +924,6 @@@ case "$TRASH_DIRECTORY" i /*) ;; # absolute path is good *) TRASH_DIRECTORY="$TEST_OUTPUT_DIRECTORY/$TRASH_DIRECTORY" ;; esac -test ! -z "$debug" || remove_trash=$TRASH_DIRECTORY rm -fr "$TRASH_DIRECTORY" || { GIT_EXIT_OK=t echo >&5 "FATAL: Cannot prepare test area" @@@ -1168,3 -1164,6 +1168,6 @@@ build_option () test_lazy_prereq LONG_IS_64BIT ' test 8 -le "$(build_option sizeof-long)" ' + + test_lazy_prereq TIME_IS_64BIT 'test-date is64bit' + test_lazy_prereq TIME_T_IS_64BIT 'test-date time_t-is64bit' diff --combined wt-status.c index b7ade902fb,1b1d644b85..7daa5320ac --- a/wt-status.c +++ b/wt-status.c @@@ -1002,7 -1002,7 +1002,7 @@@ static void wt_longstatus_print_trackin color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "%c", comment_line_char); else - fputs("", s->fp); + fputs("\n", s->fp); } static int has_unmerged(struct wt_status *s) @@@ -1387,7 -1387,7 +1387,7 @@@ struct grab_1st_switch_cbdata }; static int grab_1st_switch(struct object_id *ooid, struct object_id *noid, - const char *email, unsigned long timestamp, int tz, + const char *email, timestamp_t timestamp, int tz, const char *message, void *cb_data) { struct grab_1st_switch_cbdata *cb = cb_data;