From: Junio C Hamano Date: Tue, 26 May 2015 20:49:24 +0000 (-0700) Subject: Merge branch 'jc/hash-object' into maint X-Git-Tag: v2.4.2~2 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/1e6c8babf86d8564104e159ce46df069c882a5af?hp=-c Merge branch 'jc/hash-object' into maint "hash-object --literally" introduced in v2.2 was not prepared to take a really long object type name. * jc/hash-object: write_sha1_file(): do not use a separate sha1[] array t1007: add hash-object --literally tests hash-object --literally: fix buffer overrun with extra-long object type git-hash-object.txt: document --literally option --- 1e6c8babf86d8564104e159ce46df069c882a5af diff --combined builtin/hash-object.c index 207b90c7b1,17e8bfdc44..07fef3cc6b --- a/builtin/hash-object.c +++ b/builtin/hash-object.c @@@ -22,10 -22,8 +22,8 @@@ static int hash_literally(unsigned cha if (strbuf_read(&buf, fd, 4096) < 0) ret = -1; - else if (flags & HASH_WRITE_OBJECT) - ret = write_sha1_file(buf.buf, buf.len, type, sha1); else - ret = hash_sha1_file(buf.buf, buf.len, type, sha1); + ret = hash_sha1_file_literally(buf.buf, buf.len, type, sha1, flags); strbuf_release(&buf); return ret; } @@@ -79,7 -77,7 +77,7 @@@ static void hash_stdin_paths(const cha int cmd_hash_object(int argc, const char **argv, const char *prefix) { static const char * const hash_object_usage[] = { - N_("git hash-object [-t ] [-w] [--path=|--no-filters] [--stdin] [--] ..."), + N_("git hash-object [-t ] [-w] [--path= | --no-filters] [--stdin] [--] ..."), N_("git hash-object --stdin-paths < "), NULL }; diff --combined cache.h index eabde3828c,e037cadf4c..e42be4b11b --- a/cache.h +++ b/cache.h @@@ -65,6 -65,13 +65,6 @@@ unsigned long git_deflate_bound(git_zst * * The value 0160000 is not normally a valid mode, and * also just happens to be S_IFDIR + S_IFLNK - * - * NOTE! We *really* shouldn't depend on the S_IFxxx macros - * always having the same values everywhere. We should use - * our internal git values for these things, and then we can - * translate that to the OS-specific value. It just so - * happens that everybody shares the same bit representation - * in the UNIX world (and apparently wider too..) */ #define S_IFGITLINK 0160000 #define S_ISGITLINK(m) (((m) & S_IFMT) == S_IFGITLINK) @@@ -475,7 -482,7 +475,7 @@@ extern int daemonize(void) alloc = (nr); \ else \ alloc = alloc_nr(alloc); \ - x = xrealloc((x), alloc * sizeof(*(x))); \ + REALLOC_ARRAY(x, alloc); \ } \ } while (0) @@@ -563,12 -570,30 +563,12 @@@ extern void fill_stat_cache_info(struc #define REFRESH_IN_PORCELAIN 0x0020 /* user friendly output, not "needs update" */ extern int refresh_index(struct index_state *, unsigned int flags, const struct pathspec *pathspec, char *seen, const char *header_msg); -struct lock_file { - struct lock_file *next; - int fd; - pid_t owner; - char on_list; - char filename[PATH_MAX]; -}; -#define LOCK_DIE_ON_ERROR 1 -#define LOCK_NODEREF 2 -extern int unable_to_lock_error(const char *path, int err); -extern void unable_to_lock_message(const char *path, int err, - struct strbuf *buf); -extern NORETURN void unable_to_lock_index_die(const char *path, int err); -extern int hold_lock_file_for_update(struct lock_file *, const char *path, int); -extern int hold_lock_file_for_append(struct lock_file *, const char *path, int); -extern int commit_lock_file(struct lock_file *); -extern int reopen_lock_file(struct lock_file *); extern void update_index_if_able(struct index_state *, struct lock_file *); extern int hold_locked_index(struct lock_file *, int); extern void set_alternate_index_output(const char *); -extern int close_lock_file(struct lock_file *); -extern void rollback_lock_file(struct lock_file *); -extern int delete_ref(const char *, const unsigned char *sha1, int delopt); + +extern int delete_ref(const char *, const unsigned char *sha1, unsigned int flags); /* Environment bits from configuration mechanism */ extern int trust_executable_bit; @@@ -610,16 -635,6 +610,16 @@@ extern int fsync_object_files extern int core_preload_index; extern int core_apply_sparse_checkout; extern int precomposed_unicode; +extern int protect_hfs; +extern int protect_ntfs; + +/* + * Include broken refs in all ref iterations, which will + * generally choke dangerous operations rather than letting + * them silently proceed without taking the broken ref into + * account. + */ +extern int ref_paranoia; /* * The character that begins a commented line in user-editable file @@@ -834,7 -849,6 +834,7 @@@ int normalize_path_copy(char *dst, cons int longest_ancestor_length(const char *path, struct string_list *prefixes); char *strip_path_suffix(const char *path, const char *suffix); int daemon_avoid_alias(const char *path); +extern int is_ntfs_dotgit(const char *name); /* object replacement */ #define LOOKUP_REPLACE_OBJECT 1 @@@ -874,6 -888,7 +874,7 @@@ static inline const unsigned char *look extern int sha1_object_info(const unsigned char *, unsigned long *); extern int hash_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *sha1); extern int write_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *return_sha1); + extern int hash_sha1_file_literally(const void *buf, unsigned long len, const char *type, unsigned char *sha1, unsigned flags); extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *); extern int force_object_loose(const unsigned char *sha1, time_t mtime); extern int git_open_noatime(const char *name); @@@ -954,8 -969,8 +955,8 @@@ extern int for_each_abbrev(const char * extern int get_sha1_hex(const char *hex, unsigned char *sha1); extern char *sha1_to_hex(const unsigned char *sha1); /* static buffer result! */ -extern int read_ref_full(const char *refname, unsigned char *sha1, - int reading, int *flags); +extern int read_ref_full(const char *refname, int resolve_flags, + unsigned char *sha1, int *flags); extern int read_ref(const char *refname, unsigned char *sha1); /* @@@ -967,49 -982,29 +968,49 @@@ * or the input ref. * * If the reference cannot be resolved to an object, the behavior - * depends on the "reading" argument: + * depends on the RESOLVE_REF_READING flag: * - * - If reading is set, return NULL. + * - If RESOLVE_REF_READING is set, return NULL. * - * - If reading is not set, clear sha1 and return the name of the last - * reference name in the chain, which will either be a non-symbolic + * - If RESOLVE_REF_READING is not set, clear sha1 and return the name of + * the last reference name in the chain, which will either be a non-symbolic * reference or an undefined reference. If this is a prelude to * "writing" to the ref, the return value is the name of the ref * that will actually be created or changed. * - * If flag is non-NULL, set the value that it points to the + * If the RESOLVE_REF_NO_RECURSE flag is passed, only resolves one + * level of symbolic reference. The value stored in sha1 for a symbolic + * reference will always be null_sha1 in this case, and the return + * value is the reference that the symref refers to directly. + * + * If flags is non-NULL, set the value that it points to the * combination of REF_ISPACKED (if the reference was found among the - * packed references) and REF_ISSYMREF (if the initial reference was a - * symbolic reference). + * packed references), REF_ISSYMREF (if the initial reference was a + * symbolic reference), REF_BAD_NAME (if the reference name is ill + * formed --- see RESOLVE_REF_ALLOW_BAD_NAME below), and REF_ISBROKEN + * (if the ref is malformed or has a bad name). See refs.h for more detail + * on each flag. * * If ref is not a properly-formatted, normalized reference, return * NULL. If more than MAXDEPTH recursive symbolic lookups are needed, * give up and return NULL. * - * errno is set to something meaningful on error. + * RESOLVE_REF_ALLOW_BAD_NAME allows resolving refs even when their + * name is invalid according to git-check-ref-format(1). If the name + * is bad then the value stored in sha1 will be null_sha1 and the two + * flags REF_ISBROKEN and REF_BAD_NAME will be set. + * + * Even with RESOLVE_REF_ALLOW_BAD_NAME, names that escape the refs/ + * directory and do not consist of all caps and underscores cannot be + * resolved. The function returns NULL for such ref names. + * Caps and underscores refers to the special refs, such as HEAD, + * FETCH_HEAD and friends, that all live outside of the refs/ directory. */ -extern const char *resolve_ref_unsafe(const char *ref, unsigned char *sha1, int reading, int *flag); -extern char *resolve_refdup(const char *ref, unsigned char *sha1, int reading, int *flag); +#define RESOLVE_REF_READING 0x01 +#define RESOLVE_REF_NO_RECURSE 0x02 +#define RESOLVE_REF_ALLOW_BAD_NAME 0x04 +extern const char *resolve_ref_unsafe(const char *ref, int resolve_flags, unsigned char *sha1, int *flags); +extern char *resolve_refdup(const char *ref, int resolve_flags, unsigned char *sha1, int *flags); extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref); extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref); @@@ -1045,7 -1040,6 +1046,7 @@@ enum date_mode DATE_SHORT, DATE_LOCAL, DATE_ISO8601, + DATE_ISO8601_STRICT, DATE_RFC2822, DATE_RAW }; @@@ -1053,10 -1047,10 +1054,10 @@@ const char *show_date(unsigned long time, int timezone, enum date_mode mode); void show_date_relative(unsigned long time, int tz, const struct timeval *now, struct strbuf *timebuf); -int parse_date(const char *date, char *buf, int bufsize); +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); -void datestamp(char *buf, int bufsize); +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); @@@ -1149,7 -1143,7 +1150,7 @@@ extern void prepare_alt_odb(void) extern void read_info_alternates(const char * relative_base, int depth); extern void add_to_alternates_file(const char *reference); typedef int alt_odb_fn(struct alternate_object_database *, void *); -extern void foreach_alt_odb(alt_odb_fn, void*); +extern int foreach_alt_odb(alt_odb_fn, void*); struct pack_window { struct pack_window *next; @@@ -1174,7 -1168,6 +1175,7 @@@ extern struct packed_git int pack_fd; unsigned pack_local:1, pack_keep:1, + freshened:1, do_not_close:1; unsigned char sha1[20]; /* something like ".git/objects/pack/xxxxx.pack" */ @@@ -1246,61 -1239,6 +1247,61 @@@ extern unsigned long unpack_object_head extern unsigned long get_size_from_delta(struct packed_git *, struct pack_window **, off_t); extern int unpack_object_header(struct packed_git *, struct pack_window **, off_t *, unsigned long *); +/* + * Iterate over the files in the loose-object parts of the object + * directory "path", triggering the following callbacks: + * + * - loose_object is called for each loose object we find. + * + * - loose_cruft is called for any files that do not appear to be + * loose objects. Note that we only look in the loose object + * directories "objects/[0-9a-f]{2}/", so we will not report + * "objects/foobar" as cruft. + * + * - loose_subdir is called for each top-level hashed subdirectory + * of the object directory (e.g., "$OBJDIR/f0"). It is called + * after the objects in the directory are processed. + * + * Any callback that is NULL will be ignored. Callbacks returning non-zero + * will end the iteration. + * + * In the "buf" variant, "path" is a strbuf which will also be used as a + * scratch buffer, but restored to its original contents before + * the function returns. + */ +typedef int each_loose_object_fn(const unsigned char *sha1, + const char *path, + void *data); +typedef int each_loose_cruft_fn(const char *basename, + const char *path, + void *data); +typedef int each_loose_subdir_fn(int nr, + const char *path, + void *data); +int for_each_loose_file_in_objdir(const char *path, + each_loose_object_fn obj_cb, + each_loose_cruft_fn cruft_cb, + each_loose_subdir_fn subdir_cb, + void *data); +int for_each_loose_file_in_objdir_buf(struct strbuf *path, + each_loose_object_fn obj_cb, + each_loose_cruft_fn cruft_cb, + each_loose_subdir_fn subdir_cb, + void *data); + +/* + * Iterate over loose and packed objects in both the local + * repository and any alternates repositories (unless the + * LOCAL_ONLY flag is set). + */ +#define FOR_EACH_OBJECT_LOCAL_ONLY 0x1 +typedef int each_packed_object_fn(const unsigned char *sha1, + struct packed_git *pack, + uint32_t pos, + void *data); +extern int for_each_loose_object(each_loose_object_fn, void *, unsigned flags); +extern int for_each_packed_object(each_packed_object_fn, void *, unsigned flags); + struct object_info { /* Request */ enum object_type *typep; @@@ -1347,8 -1285,6 +1348,8 @@@ extern int update_server_info(int) #define CONFIG_INVALID_PATTERN 6 #define CONFIG_GENERIC_ERROR 7 +#define CONFIG_REGEX_NONE ((void *)1) + struct git_config_source { unsigned int use_stdin:1; const char *file; @@@ -1386,7 -1322,6 +1387,7 @@@ extern int git_config_rename_section_in extern const char *git_etc_gitconfig(void); extern int check_repository_format_version(const char *var, const char *value, void *cb); extern int git_env_bool(const char *, int); +extern unsigned long git_env_ulong(const char *, unsigned long); extern int git_config_system(void); extern int config_error_nonbool(const char *); #if defined(__GNUC__) @@@ -1493,8 -1428,6 +1494,8 @@@ extern const char *git_mailmap_blob /* IO helper functions */ extern void maybe_flush_or_die(FILE *, const char *); +__attribute__((format (printf, 2, 3))) +extern void fprintf_or_die(FILE *, const char *fmt, ...); extern int copy_fd(int ifd, int ofd); extern int copy_file(const char *dst, const char *src, int mode); extern int copy_file_with_time(const char *dst, const char *src, int mode); @@@ -1518,7 -1451,7 +1519,7 @@@ extern const char *pager_program extern int pager_in_use(void); extern int pager_use_color; extern int term_columns(void); -extern int decimal_width(int); +extern int decimal_width(uintmax_t); extern int check_pager_config(const char *cmd); extern const char *editor_program; @@@ -1580,6 -1513,7 +1581,6 @@@ extern int ws_blank_line(const char *li #define ws_tab_width(rule) ((rule) & WS_TAB_WIDTH_MASK) /* ls-files */ -int report_path_error(const char *ps_matched, const struct pathspec *pathspec, const char *prefix); void overlay_tree_on_cache(const char *tree_name, const char *prefix); char *alias_lookup(const char *alias); diff --combined sha1_file.c index f860d67744,5d320753c3..47f56f2e89 --- a/sha1_file.c +++ b/sha1_file.c @@@ -8,7 -8,6 +8,7 @@@ */ #include "cache.h" #include "string-list.h" +#include "lockfile.h" #include "delta.h" #include "pack.h" #include "blob.h" @@@ -413,18 -412,14 +413,18 @@@ void add_to_alternates_file(const char link_alt_odb_entries(alt, strlen(alt), '\n', NULL, 0); } -void foreach_alt_odb(alt_odb_fn fn, void *cb) +int foreach_alt_odb(alt_odb_fn fn, void *cb) { struct alternate_object_database *ent; + int r = 0; prepare_alt_odb(); - for (ent = alt_odb_list; ent; ent = ent->next) - if (fn(ent, cb)) - return; + for (ent = alt_odb_list; ent; ent = ent->next) { + r = fn(ent, cb); + if (r) + break; + } + return r; } void prepare_alt_odb(void) @@@ -443,53 -438,27 +443,53 @@@ read_info_alternates(get_object_directory(), 0); } -static int has_loose_object_local(const unsigned char *sha1) +static int freshen_file(const char *fn) { - return !access(sha1_file_name(sha1), F_OK); + struct utimbuf t; + t.actime = t.modtime = time(NULL); + return !utime(fn, &t); } -int has_loose_object_nonlocal(const unsigned char *sha1) +static int check_and_freshen_file(const char *fn, int freshen) +{ + if (access(fn, F_OK)) + return 0; + if (freshen && freshen_file(fn)) + return 0; + return 1; +} + +static int check_and_freshen_local(const unsigned char *sha1, int freshen) +{ + return check_and_freshen_file(sha1_file_name(sha1), freshen); +} + +static int check_and_freshen_nonlocal(const unsigned char *sha1, int freshen) { struct alternate_object_database *alt; prepare_alt_odb(); for (alt = alt_odb_list; alt; alt = alt->next) { fill_sha1_path(alt->name, sha1); - if (!access(alt->base, F_OK)) + if (check_and_freshen_file(alt->base, freshen)) return 1; } return 0; } +static int check_and_freshen(const unsigned char *sha1, int freshen) +{ + return check_and_freshen_local(sha1, freshen) || + check_and_freshen_nonlocal(sha1, freshen); +} + +int has_loose_object_nonlocal(const unsigned char *sha1) +{ + return check_and_freshen_nonlocal(sha1, 0); +} + static int has_loose_object(const unsigned char *sha1) { - return has_loose_object_local(sha1) || - has_loose_object_nonlocal(sha1); + return check_and_freshen(sha1, 0); } static unsigned int pack_used_ctr; @@@ -694,26 -663,10 +694,26 @@@ void release_pack_memory(size_t need ; /* nothing */ } +static void mmap_limit_check(size_t length) +{ + static size_t limit = 0; + if (!limit) { + limit = git_env_ulong("GIT_MMAP_LIMIT", 0); + if (!limit) + limit = SIZE_MAX; + } + if (length > limit) + die("attempting to mmap %"PRIuMAX" over limit %"PRIuMAX, + (uintmax_t)length, (uintmax_t)limit); +} + void *xmmap(void *start, size_t length, int prot, int flags, int fd, off_t offset) { - void *ret = mmap(start, length, prot, flags, fd, offset); + void *ret; + + mmap_limit_check(length); + ret = mmap(start, length, prot, flags, fd, offset); if (ret == MAP_FAILED) { if (!length) return NULL; @@@ -1198,7 -1151,7 +1198,7 @@@ static void report_pack_garbage(struct if (!report_garbage) return; - sort_string_list(list); + string_list_sort(list); for (i = 0; i < list->nr; i++) { const char *path = list->items[i].string; @@@ -2943,6 -2896,7 +2943,6 @@@ static int write_loose_object(const uns } /* Set it up */ - memset(&stream, 0, sizeof(stream)); git_deflate_init(&stream, zlib_compression_level); stream.next_out = compressed; stream.avail_out = sizeof(compressed); @@@ -2991,27 -2945,8 +2991,26 @@@ return move_temp_to_file(tmp_file, filename); } +static int freshen_loose_object(const unsigned char *sha1) +{ + return check_and_freshen(sha1, 1); +} + +static int freshen_packed_object(const unsigned char *sha1) +{ + struct pack_entry e; + if (!find_pack_entry(sha1, &e)) + return 0; + if (e.p->freshened) + return 1; + if (!freshen_file(e.p->pack_name)) + return 0; + e.p->freshened = 1; + return 1; +} + - int write_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *returnsha1) + int write_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *sha1) { - unsigned char sha1[20]; char hdr[32]; int hdrlen; @@@ -3019,13 -2954,32 +3018,32 @@@ * it out into .git/objects/??/?{38} file. */ write_sha1_file_prepare(buf, len, type, sha1, hdr, &hdrlen); - if (returnsha1) - hashcpy(returnsha1, sha1); - if (has_sha1_file(sha1)) + if (freshen_packed_object(sha1) || freshen_loose_object(sha1)) return 0; return write_loose_object(sha1, hdr, hdrlen, buf, len, 0); } + int hash_sha1_file_literally(const void *buf, unsigned long len, const char *type, + unsigned char *sha1, unsigned flags) + { + char *header; + int hdrlen, status = 0; + + /* type string, SP, %lu of the length plus NUL must fit this */ + header = xmalloc(strlen(type) + 32); + write_sha1_file_prepare(buf, len, type, sha1, header, &hdrlen); + + if (!(flags & HASH_WRITE_OBJECT)) + goto cleanup; - if (has_sha1_file(sha1)) ++ if (freshen_packed_object(sha1) || freshen_loose_object(sha1)) + goto cleanup; + status = write_loose_object(sha1, header, hdrlen, buf, len, 0); + + cleanup: + free(header); + return status; + } + int force_object_loose(const unsigned char *sha1, time_t mtime) { void *buf; @@@ -3140,29 -3094,6 +3158,29 @@@ static int index_mem(unsigned char *sha return ret; } +static int index_stream_convert_blob(unsigned char *sha1, int fd, + const char *path, unsigned flags) +{ + int ret; + const int write_object = flags & HASH_WRITE_OBJECT; + struct strbuf sbuf = STRBUF_INIT; + + assert(path); + assert(would_convert_to_git_filter_fd(path)); + + convert_to_git_filter_fd(path, fd, &sbuf, + write_object ? safe_crlf : SAFE_CRLF_FALSE); + + if (write_object) + ret = write_sha1_file(sbuf.buf, sbuf.len, typename(OBJ_BLOB), + sha1); + else + ret = hash_sha1_file(sbuf.buf, sbuf.len, typename(OBJ_BLOB), + sha1); + strbuf_release(&sbuf); + return ret; +} + static int index_pipe(unsigned char *sha1, int fd, enum object_type type, const char *path, unsigned flags) { @@@ -3228,22 -3159,15 +3246,22 @@@ int index_fd(unsigned char *sha1, int f enum object_type type, const char *path, unsigned flags) { int ret; - size_t size = xsize_t(st->st_size); - if (!S_ISREG(st->st_mode)) + /* + * Call xsize_t() only when needed to avoid potentially unnecessary + * die() for large files. + */ + if (type == OBJ_BLOB && path && would_convert_to_git_filter_fd(path)) + ret = index_stream_convert_blob(sha1, fd, path, flags); + else if (!S_ISREG(st->st_mode)) ret = index_pipe(sha1, fd, type, path, flags); - else if (size <= big_file_threshold || type != OBJ_BLOB || - (path && would_convert_to_git(path, NULL, 0, 0))) - ret = index_core(sha1, fd, size, type, path, flags); + else if (st->st_size <= big_file_threshold || type != OBJ_BLOB || + (path && would_convert_to_git(path))) + ret = index_core(sha1, fd, xsize_t(st->st_size), type, path, + flags); else - ret = index_stream(sha1, fd, size, type, path, flags); + ret = index_stream(sha1, fd, xsize_t(st->st_size), type, path, + flags); close(fd); return ret; } @@@ -3308,172 -3232,3 +3326,172 @@@ void assert_sha1_type(const unsigned ch die("%s is not a valid '%s' object", sha1_to_hex(sha1), typename(expect)); } + +static int for_each_file_in_obj_subdir(int subdir_nr, + struct strbuf *path, + each_loose_object_fn obj_cb, + each_loose_cruft_fn cruft_cb, + each_loose_subdir_fn subdir_cb, + void *data) +{ + size_t baselen = path->len; + DIR *dir = opendir(path->buf); + struct dirent *de; + int r = 0; + + if (!dir) { + if (errno == ENOENT) + return 0; + return error("unable to open %s: %s", path->buf, strerror(errno)); + } + + while ((de = readdir(dir))) { + if (is_dot_or_dotdot(de->d_name)) + continue; + + strbuf_setlen(path, baselen); + strbuf_addf(path, "/%s", de->d_name); + + if (strlen(de->d_name) == 38) { + char hex[41]; + unsigned char sha1[20]; + + snprintf(hex, sizeof(hex), "%02x%s", + subdir_nr, de->d_name); + if (!get_sha1_hex(hex, sha1)) { + if (obj_cb) { + r = obj_cb(sha1, path->buf, data); + if (r) + break; + } + continue; + } + } + + if (cruft_cb) { + r = cruft_cb(de->d_name, path->buf, data); + if (r) + break; + } + } + strbuf_setlen(path, baselen); + + if (!r && subdir_cb) + r = subdir_cb(subdir_nr, path->buf, data); + + closedir(dir); + return r; +} + +int for_each_loose_file_in_objdir_buf(struct strbuf *path, + each_loose_object_fn obj_cb, + each_loose_cruft_fn cruft_cb, + each_loose_subdir_fn subdir_cb, + void *data) +{ + size_t baselen = path->len; + int r = 0; + int i; + + for (i = 0; i < 256; i++) { + strbuf_addf(path, "/%02x", i); + r = for_each_file_in_obj_subdir(i, path, obj_cb, cruft_cb, + subdir_cb, data); + strbuf_setlen(path, baselen); + if (r) + break; + } + + return r; +} + +int for_each_loose_file_in_objdir(const char *path, + each_loose_object_fn obj_cb, + each_loose_cruft_fn cruft_cb, + each_loose_subdir_fn subdir_cb, + void *data) +{ + struct strbuf buf = STRBUF_INIT; + int r; + + strbuf_addstr(&buf, path); + r = for_each_loose_file_in_objdir_buf(&buf, obj_cb, cruft_cb, + subdir_cb, data); + strbuf_release(&buf); + + return r; +} + +struct loose_alt_odb_data { + each_loose_object_fn *cb; + void *data; +}; + +static int loose_from_alt_odb(struct alternate_object_database *alt, + void *vdata) +{ + struct loose_alt_odb_data *data = vdata; + struct strbuf buf = STRBUF_INIT; + int r; + + /* copy base not including trailing '/' */ + strbuf_add(&buf, alt->base, alt->name - alt->base - 1); + r = for_each_loose_file_in_objdir_buf(&buf, + data->cb, NULL, NULL, + data->data); + strbuf_release(&buf); + return r; +} + +int for_each_loose_object(each_loose_object_fn cb, void *data, unsigned flags) +{ + struct loose_alt_odb_data alt; + int r; + + r = for_each_loose_file_in_objdir(get_object_directory(), + cb, NULL, NULL, data); + if (r) + return r; + + if (flags & FOR_EACH_OBJECT_LOCAL_ONLY) + return 0; + + alt.cb = cb; + alt.data = data; + return foreach_alt_odb(loose_from_alt_odb, &alt); +} + +static int for_each_object_in_pack(struct packed_git *p, each_packed_object_fn cb, void *data) +{ + uint32_t i; + int r = 0; + + for (i = 0; i < p->num_objects; i++) { + const unsigned char *sha1 = nth_packed_object_sha1(p, i); + + if (!sha1) + return error("unable to get sha1 of object %u in %s", + i, p->pack_name); + + r = cb(sha1, p, i, data); + if (r) + break; + } + return r; +} + +int for_each_packed_object(each_packed_object_fn cb, void *data, unsigned flags) +{ + struct packed_git *p; + int r = 0; + + prepare_packed_git(); + for (p = packed_git; p; p = p->next) { + if ((flags & FOR_EACH_OBJECT_LOCAL_ONLY) && !p->pack_local) + continue; + r = for_each_object_in_pack(p, cb, data); + if (r) + break; + } + return r; +} diff --combined t/t1007-hash-object.sh index ebb3a69c8c,7c3dcfb70c..7d2baa15bb --- a/t/t1007-hash-object.sh +++ b/t/t1007-hash-object.sh @@@ -201,12 -201,15 +201,23 @@@ test_expect_success 'corrupt tag' test_must_fail git hash-object -t tag --stdin