From: Junio C Hamano Date: Wed, 4 May 2011 21:57:38 +0000 (-0700) Subject: Merge branch 'jc/pack-objects-bigfile' into maint X-Git-Tag: v1.7.5.1~6 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/f28d2e33c64819d0425c426cd2c4511634096a40?ds=inline;hp=-c Merge branch 'jc/pack-objects-bigfile' into maint * jc/pack-objects-bigfile: Teach core.bigfilethreashold to pack-objects --- f28d2e33c64819d0425c426cd2c4511634096a40 diff --combined Documentation/config.txt index 750c86d4f5,c5598f4af9..0906499e7d --- a/Documentation/config.txt +++ b/Documentation/config.txt @@@ -62,7 -62,7 +62,7 @@@ Internal whitespace within a variable v The values following the equals sign in variable assign are all either a string, an integer, or a boolean. Boolean values may be given as yes/no, -0/1, true/false or on/off. Case is not significant in boolean values, when +1/0, true/false or on/off. Case is not significant in boolean values, when converting value to the canonical form using '--bool' type specifier; 'git config' will ensure that the output is "true" or "false". @@@ -320,7 -320,7 +320,7 @@@ core.worktree: Set the path to the root of the working tree. This can be overridden by the GIT_WORK_TREE environment variable and the '--work-tree' command line option. - The value can an absolute path or relative to the path to + The value can be an absolute path or relative to the path to the .git directory, which is either specified by --git-dir or GIT_DIR, or automatically discovered. If --git-dir or GIT_DIR is specified but none of @@@ -376,6 -376,15 +376,6 @@@ core.warnAmbiguousRefs: If true, git will warn you if the ref name you passed it is ambiguous and might match multiple refs in the .git/refs/ tree. True by default. -core.abbrevguard:: - Even though git makes sure that it uses enough hexdigits to show - an abbreviated object name unambiguously, as more objects are - added to the repository over time, a short name that used to be - unique will stop being unique. Git uses this many extra hexdigits - that are more than necessary to make the object name currently - unique, in the hope that its output will stay unique a bit longer. - Defaults to 0. - core.compression:: An integer -1..9, indicating a default compression level. -1 is the zlib default. 0 means no compression, @@@ -442,8 -451,6 +442,6 @@@ for most projects as source code and ot be delta compressed, but larger binary media files won't be. + Common unit suffixes of 'k', 'm', or 'g' are supported. - + - Currently only linkgit:git-fast-import[1] honors this setting. core.excludesfile:: In addition to '.gitignore' (per-directory) and @@@ -558,12 -565,6 +556,12 @@@ core.sparseCheckout: Enable "sparse checkout" feature. See section "Sparse checkout" in linkgit:git-read-tree[1] for more information. +core.abbrev:: + Set the length object names are abbreviated to. If unspecified, + many commands abbreviate to 7 hexdigits, which may not be enough + for abbreviated object names to stay unique for sufficiently long + time. + add.ignore-errors:: add.ignoreErrors:: Tells 'git add' to continue adding files when some files cannot be @@@ -897,13 -898,9 +895,13 @@@ diff.wordRegex: characters are *ignorable* whitespace. fetch.recurseSubmodules:: - A boolean value which changes the behavior for fetch and pull, the - default is to not recursively fetch populated submodules unless - configured otherwise. + This option can be either set to a boolean value or to 'on-demand'. + Setting it to a boolean changes the behavior of fetch and pull to + unconditionally recurse into submodules when set to true or to not + recurse at all when set to false. When set to 'on-demand' (the default + value), fetch and pull will only recurse into a populated submodule + when its superproject retrieves a commit that updates the submodule's + reference. fetch.unpackLimit:: If the number of objects fetched over the git native @@@ -1102,12 -1099,6 +1100,12 @@@ All gitcvs variables except for 'gitcvs is one of "ext" and "pserver") to make them apply only for the given access method. +grep.lineNumber:: + If set to true, enable '-n' option by default. + +grep.extendedRegexp:: + If set to true, enable '--extended-regexp' option by default. + gui.commitmsgwidth:: Defines how wide the commit message window is in the linkgit:git-gui[1]. "75" is the default. @@@ -1598,8 -1589,7 +1596,8 @@@ push.default: * `matching` - push all matching branches. All branches having the same name in both ends are considered to be matching. This is the default. -* `tracking` - push the current branch to its upstream branch. +* `upstream` - push the current branch to its upstream branch. +* `tracking` - deprecated synonym for `upstream`. * `current` - push the current branch to a branch of the same name. rebase.stat:: @@@ -1827,7 -1817,7 +1825,7 @@@ submodule..update: linkgit:git-submodule[1] and linkgit:gitmodules[5] for details. submodule..fetchRecurseSubmodules:: - This option can be used to enable/disable recursive fetching of this + This option can be used to control recursive fetching of this submodule. It can be overridden by using the --[no-]recurse-submodules command line option to "git fetch" and "git pull". This setting will override that from in the linkgit:gitmodules[5] diff --combined cache.h index 2674f4cf5a,baec5edf03..28899b7b78 --- a/cache.h +++ b/cache.h @@@ -5,7 -5,6 +5,7 @@@ #include "strbuf.h" #include "hash.h" #include "advice.h" +#include "gettext.h" #include SHA1_HEADER #ifndef git_SHA_CTX @@@ -437,7 -436,6 +437,7 @@@ extern void verify_non_filename(const c #define INIT_DB_QUIET 0x0001 +extern int set_git_dir_init(const char *git_dir, const char *real_git_dir, int); extern int init_db(const char *template_dir, unsigned int flags); #define alloc_nr(x) (((x)+16)*3/2) @@@ -502,23 -500,8 +502,23 @@@ extern int index_name_is_other(const st extern int ie_match_stat(const struct index_state *, struct cache_entry *, struct stat *, unsigned int); extern int ie_modified(const struct index_state *, struct cache_entry *, struct stat *, unsigned int); -extern int ce_path_match(const struct cache_entry *ce, const char **pathspec); -extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path); +struct pathspec { + const char **raw; /* get_pathspec() result, not freed by free_pathspec() */ + int nr; + unsigned int has_wildcard:1; + unsigned int recursive:1; + int max_depth; + struct pathspec_item { + const char *match; + int len; + unsigned int has_wildcard:1; + } *items; +}; + +extern int init_pathspec(struct pathspec *, const char **); +extern void free_pathspec(struct pathspec *); +extern int ce_path_match(const struct cache_entry *ce, const struct pathspec *pathspec); +extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path, int format_check); extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object); extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st); @@@ -528,7 -511,7 +528,7 @@@ #define REFRESH_IGNORE_MISSING 0x0008 /* ignore non-existent */ #define REFRESH_IGNORE_SUBMODULES 0x0010 /* ignore submodules */ #define REFRESH_IN_PORCELAIN 0x0020 /* user friendly output, not "needs update" */ -extern int refresh_index(struct index_state *, unsigned int flags, const char **pathspec, char *seen, char *header_msg); +extern int refresh_index(struct index_state *, unsigned int flags, const char **pathspec, char *seen, const char *header_msg); struct lock_file { struct lock_file *next; @@@ -544,7 -527,6 +544,7 @@@ extern NORETURN void unable_to_lock_ind 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 void update_index_if_able(struct index_state *, struct lock_file *); extern int hold_locked_index(struct lock_file *, int); extern int commit_locked_index(struct lock_file *); @@@ -558,12 -540,12 +558,12 @@@ extern int trust_executable_bit extern int trust_ctime; extern int quote_path_fully; extern int has_symlinks; +extern int minimum_abbrev, default_abbrev; extern int ignore_case; extern int assume_unchanged; extern int prefer_symlink_refs; extern int log_all_ref_updates; extern int warn_ambiguous_refs; -extern int unique_abbrev_extra_length; extern int shared_repository; extern const char *apply_default_whitespace; extern const char *apply_default_ignorewhitespace; @@@ -573,6 -555,7 +573,7 @@@ extern int core_compression_seen extern size_t packed_git_window_size; extern size_t packed_git_limit; extern size_t delta_base_cache_limit; + extern unsigned long big_file_threshold; extern int read_replace_refs; extern int fsync_object_files; extern int core_preload_index; @@@ -589,7 -572,7 +590,7 @@@ extern enum safe_crlf safe_crlf enum auto_crlf { AUTO_CRLF_FALSE = 0, AUTO_CRLF_TRUE = 1, - AUTO_CRLF_INPUT = -1, + AUTO_CRLF_INPUT = -1 }; extern enum auto_crlf auto_crlf; @@@ -626,7 -609,7 +627,7 @@@ enum rebase_setup_type enum push_default_type { PUSH_DEFAULT_NOTHING = 0, PUSH_DEFAULT_MATCHING, - PUSH_DEFAULT_TRACKING, + PUSH_DEFAULT_UPSTREAM, PUSH_DEFAULT_CURRENT }; @@@ -694,11 -677,9 +695,11 @@@ static inline void hashclr(unsigned cha #define EMPTY_TREE_SHA1_HEX \ "4b825dc642cb6eb9a060e54bf8d69288fbee4904" -#define EMPTY_TREE_SHA1_BIN \ +#define EMPTY_TREE_SHA1_BIN_LITERAL \ "\x4b\x82\x5d\xc6\x42\xcb\x6e\xb9\xa0\x60" \ "\xe5\x4b\xf8\xd6\x92\x88\xfb\xee\x49\x04" +#define EMPTY_TREE_SHA1_BIN \ + ((const unsigned char *) EMPTY_TREE_SHA1_BIN_LITERAL) int git_mkstemp(char *path, size_t n, const char *template); @@@ -728,7 -709,6 +729,7 @@@ int set_shared_perm(const char *path, i #define adjust_shared_perm(path) set_shared_perm((path), 0) int safe_create_leading_directories(char *path); int safe_create_leading_directories_const(const char *path); +int mkdir_in_gitdir(const char *path); extern char *expand_user_path(const char *path); char *enter_repo(char *path, int strict); static inline int is_absolute_path(const char *path) @@@ -736,9 -716,9 +737,9 @@@ return path[0] == '/' || has_dos_drive_prefix(path); } int is_directory(const char *); -const char *make_absolute_path(const char *path); -const char *make_nonrelative_path(const char *path); -const char *make_relative_path(const char *abs, const char *base); +const char *real_path(const char *path); +const char *absolute_path(const char *path); +const char *relative_path(const char *abs, const char *base); int normalize_path_copy(char *dst, const char *src); int longest_ancestor_length(const char *path, const char *prefix_list); char *strip_path_suffix(const char *path, const char *suffix); @@@ -779,8 -759,8 +780,8 @@@ static inline unsigned int hexval(unsig } /* Convert to/from hex/sha1 representation */ -#define MINIMUM_ABBREV 4 -#define DEFAULT_ABBREV 7 +#define MINIMUM_ABBREV minimum_abbrev +#define DEFAULT_ABBREV default_abbrev struct object_context { unsigned char tree[20]; @@@ -918,8 -898,7 +919,8 @@@ extern struct packed_git time_t mtime; int pack_fd; unsigned pack_local:1, - pack_keep:1; + pack_keep:1, + do_not_close:1; unsigned char sha1[20]; /* something like ".git/objects/pack/xxxxx.pack" */ char pack_name[FLEX_ARRAY]; /* more */ @@@ -1019,13 -998,13 +1020,13 @@@ extern int git_config_maybe_bool(const extern int git_config_string(const char **, const char *, const char *); extern int git_config_pathname(const char **, const char *, const char *); extern int git_config_set(const char *, const char *); +extern int git_config_parse_key(const char *, char **, int *); extern int git_config_set_multivar(const char *, const char *, const char *, int); extern int git_config_rename_section(const char *, const char *); 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 int git_config_system(void); -extern int git_config_global(void); extern int config_error_nonbool(const char *); extern const char *get_log_output_encoding(void); extern const char *get_commit_output_encoding(void); @@@ -1087,14 -1066,9 +1088,14 @@@ extern void alloc_report(void) /* trace.c */ __attribute__((format (printf, 1, 2))) extern void trace_printf(const char *format, ...); +extern void trace_vprintf(const char *key, const char *format, va_list ap); __attribute__((format (printf, 2, 3))) extern void trace_argv_printf(const char **argv, const char *format, ...); extern void trace_repo_setup(const char *prefix); +extern int trace_want(const char *key); +extern void trace_strbuf(const char *key, const struct strbuf *buf); + +void packet_trace_identity(const char *prog); /* convert.c */ /* returns 1 if *dst was used */ diff --combined config.c index 0abcada938,85f956058c..d06fb19d51 --- a/config.c +++ b/config.c @@@ -20,7 -20,8 +20,7 @@@ static int zlib_compression_seen const char *config_exclusive_filename = NULL; -struct config_item -{ +struct config_item { struct config_item *next; char *name; char *value; @@@ -498,6 -499,13 +498,6 @@@ static int git_default_core_config(cons return 0; } - if (!strcmp(var, "core.abbrevguard")) { - unique_abbrev_extra_length = git_config_int(var, value); - if (unique_abbrev_extra_length < 0) - unique_abbrev_extra_length = 0; - return 0; - } - if (!strcmp(var, "core.bare")) { is_bare_repository_cfg = git_config_bool(var, value); return 0; @@@ -523,14 -531,6 +523,14 @@@ return 0; } + if (!strcmp(var, "core.abbrev")) { + int abbrev = git_config_int(var, value); + if (abbrev < minimum_abbrev || abbrev > 40) + return -1; + default_abbrev = abbrev; + return 0; + } + if (!strcmp(var, "core.loosecompression")) { int level = git_config_int(var, value); if (level == -1) @@@ -567,6 -567,12 +567,12 @@@ return 0; } + if (!strcmp(var, "core.bigfilethreshold")) { + long n = git_config_int(var, value); + big_file_threshold = 0 < n ? n : 0; + return 0; + } + if (!strcmp(var, "core.packedgitlimit")) { packed_git_limit = git_config_int(var, value); return 0; @@@ -737,10 -743,8 +743,10 @@@ static int git_default_push_config(cons push_default = PUSH_DEFAULT_NOTHING; else if (!strcmp(value, "matching")) push_default = PUSH_DEFAULT_MATCHING; - else if (!strcmp(value, "tracking")) - push_default = PUSH_DEFAULT_TRACKING; + else if (!strcmp(value, "upstream")) + push_default = PUSH_DEFAULT_UPSTREAM; + else if (!strcmp(value, "tracking")) /* deprecated */ + push_default = PUSH_DEFAULT_UPSTREAM; else if (!strcmp(value, "current")) push_default = PUSH_DEFAULT_CURRENT; else { @@@ -833,6 -837,11 +839,6 @@@ int git_config_system(void return !git_env_bool("GIT_CONFIG_NOSYSTEM", 0); } -int git_config_global(void) -{ - return !git_env_bool("GIT_CONFIG_NOGLOBAL", 0); -} - int git_config_from_parameters(config_fn_t fn, void *data) { static int loaded_environment; @@@ -864,7 -873,7 +870,7 @@@ int git_config_early(config_fn_t fn, vo } home = getenv("HOME"); - if (git_config_global() && home) { + if (home) { char *user_config = xstrdup(mkpath("%s/.gitconfig", home)); if (!access(user_config, R_OK)) { ret += git_config_from_file(fn, user_config, data); @@@ -1095,75 -1104,6 +1101,75 @@@ int git_config_set(const char *key, con return git_config_set_multivar(key, value, NULL, 0); } +/* + * Auxiliary function to sanity-check and split the key into the section + * identifier and variable name. + * + * Returns 0 on success, -1 when there is an invalid character in the key and + * -2 if there is no section name in the key. + * + * store_key - pointer to char* which will hold a copy of the key with + * lowercase section and variable name + * baselen - pointer to int which will hold the length of the + * section + subsection part, can be NULL + */ +int git_config_parse_key(const char *key, char **store_key, int *baselen_) +{ + int i, dot, baselen; + const char *last_dot = strrchr(key, '.'); + + /* + * Since "key" actually contains the section name and the real + * key name separated by a dot, we have to know where the dot is. + */ + + if (last_dot == NULL || last_dot == key) { + error("key does not contain a section: %s", key); + return -2; + } + + if (!last_dot[1]) { + error("key does not contain variable name: %s", key); + return -2; + } + + baselen = last_dot - key; + if (baselen_) + *baselen_ = baselen; + + /* + * Validate the key and while at it, lower case it for matching. + */ + *store_key = xmalloc(strlen(key) + 1); + + dot = 0; + for (i = 0; key[i]; i++) { + unsigned char c = key[i]; + if (c == '.') + dot = 1; + /* Leave the extended basename untouched.. */ + if (!dot || i > baselen) { + if (!iskeychar(c) || + (i == baselen + 1 && !isalpha(c))) { + error("invalid key: %s", key); + goto out_free_ret_1; + } + c = tolower(c); + } else if (c == '\n') { + error("invalid key (newline): %s", key); + goto out_free_ret_1; + } + (*store_key)[i] = c; + } + (*store_key)[i] = 0; + + return 0; + +out_free_ret_1: + free(*store_key); + return -1; +} + /* * If value==NULL, unset in (remove from) config, * if value_regex!=NULL, disregard key/value pairs where value does not match. @@@ -1190,23 -1130,59 +1196,23 @@@ int git_config_set_multivar(const char *key, const char *value, const char *value_regex, int multi_replace) { - int i, dot; int fd = -1, in_fd; int ret; char *config_filename; struct lock_file *lock = NULL; - const char *last_dot = strrchr(key, '.'); if (config_exclusive_filename) config_filename = xstrdup(config_exclusive_filename); else config_filename = git_pathdup("config"); - /* - * Since "key" actually contains the section name and the real - * key name separated by a dot, we have to know where the dot is. - */ - - if (last_dot == NULL) { - error("key does not contain a section: %s", key); - ret = 2; + /* parse-key returns negative; flip the sign to feed exit(3) */ + ret = 0 - git_config_parse_key(key, &store.key, &store.baselen); + if (ret) goto out_free; - } - store.baselen = last_dot - key; store.multi_replace = multi_replace; - /* - * Validate the key and while at it, lower case it for matching. - */ - store.key = xmalloc(strlen(key) + 1); - dot = 0; - for (i = 0; key[i]; i++) { - unsigned char c = key[i]; - if (c == '.') - dot = 1; - /* Leave the extended basename untouched.. */ - if (!dot || i > store.baselen) { - if (!iskeychar(c) || (i == store.baselen+1 && !isalpha(c))) { - error("invalid key: %s", key); - free(store.key); - ret = 1; - goto out_free; - } - c = tolower(c); - } else if (c == '\n') { - error("invalid key (newline): %s", key); - free(store.key); - ret = 1; - goto out_free; - } - store.key[i] = c; - } - store.key[i] = 0; /* * The lock serves a purpose in addition to locking: the new diff --combined environment.c index f4549d3f7b,8557e84ff6..40185bc854 --- a/environment.c +++ b/environment.c @@@ -15,13 -15,13 +15,13 @@@ int user_ident_explicitly_given int trust_executable_bit = 1; int trust_ctime = 1; int has_symlinks = 1; +int minimum_abbrev = 4, default_abbrev = 7; int ignore_case; int assume_unchanged; int prefer_symlink_refs; int is_bare_repository_cfg = -1; /* unspecified */ int log_all_ref_updates = -1; /* unspecified */ int warn_ambiguous_refs = 1; -int unique_abbrev_extra_length; int repository_format_version; const char *git_commit_encoding; const char *git_log_output_encoding; @@@ -35,6 -35,7 +35,7 @@@ int fsync_object_files size_t packed_git_window_size = DEFAULT_PACKED_GIT_WINDOW_SIZE; size_t packed_git_limit = DEFAULT_PACKED_GIT_LIMIT; size_t delta_base_cache_limit = 16 * 1024 * 1024; + unsigned long big_file_threshold = 512 * 1024 * 1024; const char *pager_program; int pager_use_color = 1; const char *editor_program; @@@ -140,7 -141,7 +141,7 @@@ static int git_work_tree_initialized void set_git_work_tree(const char *new_work_tree) { if (git_work_tree_initialized) { - new_work_tree = make_absolute_path(new_work_tree); + new_work_tree = real_path(new_work_tree); if (strcmp(new_work_tree, work_tree)) die("internal error: work tree has already been set\n" "Current worktree: %s\nNew worktree: %s", @@@ -148,7 -149,7 +149,7 @@@ return; } git_work_tree_initialized = 1; - work_tree = xstrdup(make_absolute_path(new_work_tree)); + work_tree = xstrdup(real_path(new_work_tree)); } const char *get_git_work_tree(void) diff --combined fast-import.c index 65d65bf8f9,1421dcd6eb..3e4e655bb9 --- a/fast-import.c +++ b/fast-import.c @@@ -24,12 -24,10 +24,12 @@@ Format of STDIN stream commit_msg ('from' sp committish lf)? ('merge' sp committish lf)* - file_change* + (file_change | ls)* lf?; commit_msg ::= data; + ls ::= 'ls' sp '"' quoted(path) '"' lf; + file_change ::= file_clr | file_del | file_rnm @@@ -134,7 -132,7 +134,7 @@@ ts ::= # time since the epoch in seconds, ascii base10 notation; tz ::= # GIT style timezone; - # note: comments and cat requests may appear anywhere + # note: comments, ls and cat requests may appear anywhere # in the input, except within a data command. Any form # of the data command always escapes the related input # from comment processing. @@@ -143,9 -141,7 +143,9 @@@ # must be the first character on that line (an lf # preceded it). # + cat_blob ::= 'cat-blob' sp (hexsha1 | idnum) lf; + ls_tree ::= 'ls' sp (hexsha1 | idnum) sp path_str lf; comment ::= '#' not_lf* lf; not_lf ::= # Any byte that is not ASCII newline (LF); @@@ -170,7 -166,8 +170,7 @@@ #define DEPTH_BITS 13 #define MAX_DEPTH ((1<pack_name, tmpfile); p->pack_fd = pack_fd; + p->do_not_close = 1; pack_file = sha1fd(pack_fd, p->pack_name); hdr.hdr_signature = htonl(PACK_SIGNATURE); @@@ -1788,11 -1794,7 +1787,11 @@@ static void read_marks(void { char line[512]; FILE *f = fopen(import_marks_file, "r"); - if (!f) + if (f) + ; + else if (import_marks_file_ignore_missing && errno == ENOENT) + return; /* Marks file does not exist */ + else die_errno("cannot read '%s'", import_marks_file); while (fgets(line, sizeof(line), f)) { uintmax_t mark; @@@ -2606,8 -2608,6 +2605,8 @@@ static void parse_new_commit(void note_change_n(b, prev_fanout); else if (!strcmp("deleteall", command_buf.buf)) file_change_deleteall(b); + else if (!prefixcmp(command_buf.buf, "ls ")) + parse_ls(b); else { unread_command_buf = 1; break; @@@ -2831,153 -2831,6 +2830,153 @@@ static void parse_cat_blob(void cat_blob(oe, sha1); } +static struct object_entry *dereference(struct object_entry *oe, + unsigned char sha1[20]) +{ + unsigned long size; + char *buf = NULL; + if (!oe) { + enum object_type type = sha1_object_info(sha1, NULL); + if (type < 0) + die("object not found: %s", sha1_to_hex(sha1)); + /* cache it! */ + oe = insert_object(sha1); + oe->type = type; + oe->pack_id = MAX_PACK_ID; + oe->idx.offset = 1; + } + switch (oe->type) { + case OBJ_TREE: /* easy case. */ + return oe; + case OBJ_COMMIT: + case OBJ_TAG: + break; + default: + die("Not a treeish: %s", command_buf.buf); + } + + if (oe->pack_id != MAX_PACK_ID) { /* in a pack being written */ + buf = gfi_unpack_entry(oe, &size); + } else { + enum object_type unused; + buf = read_sha1_file(sha1, &unused, &size); + } + if (!buf) + die("Can't load object %s", sha1_to_hex(sha1)); + + /* Peel one layer. */ + switch (oe->type) { + case OBJ_TAG: + if (size < 40 + strlen("object ") || + get_sha1_hex(buf + strlen("object "), sha1)) + die("Invalid SHA1 in tag: %s", command_buf.buf); + break; + case OBJ_COMMIT: + if (size < 40 + strlen("tree ") || + get_sha1_hex(buf + strlen("tree "), sha1)) + die("Invalid SHA1 in commit: %s", command_buf.buf); + } + + free(buf); + return find_object(sha1); +} + +static struct object_entry *parse_treeish_dataref(const char **p) +{ + unsigned char sha1[20]; + struct object_entry *e; + + if (**p == ':') { /* */ + char *endptr; + e = find_mark(strtoumax(*p + 1, &endptr, 10)); + if (endptr == *p + 1) + die("Invalid mark: %s", command_buf.buf); + if (!e) + die("Unknown mark: %s", command_buf.buf); + *p = endptr; + hashcpy(sha1, e->idx.sha1); + } else { /* */ + if (get_sha1_hex(*p, sha1)) + die("Invalid SHA1: %s", command_buf.buf); + e = find_object(sha1); + *p += 40; + } + + while (!e || e->type != OBJ_TREE) + e = dereference(e, sha1); + return e; +} + +static void print_ls(int mode, const unsigned char *sha1, const char *path) +{ + static struct strbuf line = STRBUF_INIT; + + /* See show_tree(). */ + const char *type = + S_ISGITLINK(mode) ? commit_type : + S_ISDIR(mode) ? tree_type : + blob_type; + + if (!mode) { + /* missing SP path LF */ + strbuf_reset(&line); + strbuf_addstr(&line, "missing "); + quote_c_style(path, &line, NULL, 0); + strbuf_addch(&line, '\n'); + } else { + /* mode SP type SP object_name TAB path LF */ + strbuf_reset(&line); + strbuf_addf(&line, "%06o %s %s\t", + mode, type, sha1_to_hex(sha1)); + quote_c_style(path, &line, NULL, 0); + strbuf_addch(&line, '\n'); + } + cat_blob_write(line.buf, line.len); +} + +static void parse_ls(struct branch *b) +{ + const char *p; + struct tree_entry *root = NULL; + struct tree_entry leaf = {NULL}; + + /* ls SP ( SP)? */ + p = command_buf.buf + strlen("ls "); + if (*p == '"') { + if (!b) + die("Not in a commit: %s", command_buf.buf); + root = &b->branch_tree; + } else { + struct object_entry *e = parse_treeish_dataref(&p); + root = new_tree_entry(); + hashcpy(root->versions[1].sha1, e->idx.sha1); + load_tree(root); + if (*p++ != ' ') + die("Missing space after tree-ish: %s", command_buf.buf); + } + if (*p == '"') { + static struct strbuf uq = STRBUF_INIT; + const char *endp; + strbuf_reset(&uq); + if (unquote_c_style(&uq, p, &endp)) + die("Invalid path: %s", command_buf.buf); + if (*endp) + die("Garbage after path in: %s", command_buf.buf); + p = uq.buf; + } + tree_content_get(root, p, &leaf); + /* + * A directory in preparation would have a sha1 of zero + * until it is saved. Save, for simplicity. + */ + if (S_ISDIR(leaf.versions[1].mode)) + store_tree(&leaf); + + print_ls(leaf.versions[1].mode, leaf.versions[1].sha1, p); + if (!b || root != &b->branch_tree) + release_tree_entry(root); +} + static void checkpoint(void) { checkpoint_requested = 0; @@@ -3013,8 -2866,7 +3012,8 @@@ static char* make_fast_import_path(cons return strbuf_detach(&abs_path, NULL); } -static void option_import_marks(const char *marks, int from_stream) +static void option_import_marks(const char *marks, + int from_stream, int ignore_missing) { if (import_marks_file) { if (from_stream) @@@ -3028,7 -2880,6 +3027,7 @@@ import_marks_file = make_fast_import_path(marks); safe_create_leading_directories_const(import_marks_file); import_marks_file_from_stream = from_stream; + import_marks_file_ignore_missing = ignore_missing; } static void option_date_format(const char *fmt) @@@ -3128,10 -2979,7 +3127,10 @@@ static int parse_one_feature(const cha if (!prefixcmp(feature, "date-format=")) { option_date_format(feature + 12); } else if (!prefixcmp(feature, "import-marks=")) { - option_import_marks(feature + 13, from_stream); + option_import_marks(feature + 13, from_stream, 0); + } else if (!prefixcmp(feature, "import-marks-if-exists=")) { + option_import_marks(feature + strlen("import-marks-if-exists="), + from_stream, 1); } else if (!prefixcmp(feature, "export-marks=")) { option_export_marks(feature + 13); } else if (!strcmp(feature, "cat-blob")) { @@@ -3142,8 -2990,6 +3141,8 @@@ relative_marks_paths = 0; } else if (!prefixcmp(feature, "force")) { force_update = 1; + } else if (!strcmp(feature, "notes") || !strcmp(feature, "ls")) { + ; /* do nothing; we have the feature */ } else { return 0; } @@@ -3206,10 -3052,6 +3205,6 @@@ static int git_pack_config(const char * max_packsize = git_config_ulong(k, v); return 0; } - if (!strcmp(k, "core.bigfilethreshold")) { - long n = git_config_int(k, v); - big_file_threshold = 0 < n ? n : 0; - } return git_default_config(k, v, cb); } @@@ -3283,8 -3125,6 +3278,8 @@@ int main(int argc, const char **argv while (read_next_command() != EOF) { if (!strcmp("blob", command_buf.buf)) parse_new_blob(); + else if (!prefixcmp(command_buf.buf, "ls ")) + parse_ls(NULL); else if (!prefixcmp(command_buf.buf, "commit ")) parse_new_commit(); else if (!prefixcmp(command_buf.buf, "tag "))