diff options
92 files changed, 1453 insertions, 1170 deletions
diff --git a/Documentation/RelNotes/2.2.0.txt b/Documentation/RelNotes/2.2.0.txt index f771854705..22b73618fc 100644 --- a/Documentation/RelNotes/2.2.0.txt +++ b/Documentation/RelNotes/2.2.0.txt @@ -25,6 +25,15 @@ UI, Workflows & Features Performance, Internal Implementation, etc. + * The API to manipulate the "refs" is currently undergoing a revamp + to make it more transactional, with the eventual goal to allow + all-or-none atomic updates and migrating the storage to something + other than the traditional filesystem based one (e.g. databases). + + * We no longer attempt to keep track of individual dependencies to + the header files in the build procedure, relying on automated + dependency generation support from modern compilers. + * In tests, we have been using NOT_{MINGW,CYGWIN} test prerequisites long before negated prerequisites e.g. !MINGW were invented. The former has been converted to the latter to avoid confusion. @@ -37,12 +46,25 @@ Performance, Internal Implementation, etc. to update the file again while still holding the lock, but the lockfile API lacked support for such an access pattern. + * The API to allocate the structure to keep track of commit + decoration has been updated to make it less cumbersome to use. + * An in-core caching layer to let us avoid reading the same - configuration files number of times has been added. + configuration files number of times has been added. A few commands + have been converted to use this subsystem. * Various code paths have been cleaned up and simplified by using "strbuf", "starts_with()", and "skip_prefix()" APIs more. + * A few codepaths that died when large blobs that would not fit in + core are involved in their operation have been taught to punt + instead, by e.g. marking too large a blob as not to be diffed. + + * A few more code paths in "commit" and "checkout" have been taught + to repopulate the cache-tree in the index, to help speed up later + "write-tree" (used in "commit") and "diff-index --cached" (used in + "status"). + Also contains various documentation updates and code clean-ups. @@ -89,3 +111,22 @@ notes for details). * Pack-protocol documentation had a minor typo. (merge 5d146f7 sp/pack-protocol-doc-on-shallow later to maint). + + * "git checkout -m" did not switch to another branch while carrying + the local changes forward when a path was deleted from the index. + (merge 6a143aa jn/unpack-trees-checkout-m-carry-deletion later to maint). + + * With sufficiently long refnames, "git fast-import" could have + overflown an on-stack buffer. + (merge c252785 jk/fast-import-fixes later to maint). + + * After "pack-refs --prune" packed refs at the top-level, it failed + to prune them. + (merge afd11d3 jk/prune-top-level-refs-after-packing later to maint). + + * Progress output from "git gc --auto" was visible in "git fetch -q". + (merge 6fceed3 nd/fetch-pass-quiet-to-gc-child-process later to maint). + + * We used to pass -1000 to poll(2), expecting it to also mean "no + timeout", which should be spelled as -1. + (merge 6c71f8b et/spell-poll-infinite-with-minus-one-only later to maint). diff --git a/Documentation/config.txt b/Documentation/config.txt index c55c22ab7b..3b5b24aeb7 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -499,7 +499,8 @@ core.bigFileThreshold:: Files larger than this size are stored deflated, without attempting delta compression. Storing large files without delta compression avoids excessive memory usage, at the - slight expense of increased disk usage. + slight expense of increased disk usage. Additionally files + larger than this size are always treated as binary. + Default is 512 MiB on all platforms. This should be reasonable for most projects as source code and other text files can still diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt index 643c1ba929..9b45bda748 100644 --- a/Documentation/gitattributes.txt +++ b/Documentation/gitattributes.txt @@ -440,8 +440,8 @@ Unspecified:: A path to which the `diff` attribute is unspecified first gets its contents inspected, and if it looks like - text, it is treated as text. Otherwise it would - generate `Binary files differ`. + text and is smaller than core.bigFileThreshold, it is treated + as text. Otherwise it would generate `Binary files differ`. String:: diff --git a/Documentation/technical/api-config.txt b/Documentation/technical/api-config.txt index 21f280ca6d..0d8b99b368 100644 --- a/Documentation/technical/api-config.txt +++ b/Documentation/technical/api-config.txt @@ -155,6 +155,19 @@ as well as retrieval for the queried variable, including: Similar to `git_config_get_string`, but expands `~` or `~user` into the user's home directory when found at the beginning of the path. +`git_die_config(const char *key, const char *err, ...)`:: + + First prints the error message specified by the caller in `err` and then + dies printing the line number and the file name of the highest priority + value for the configuration variable `key`. + +`void git_die_config_linenr(const char *key, const char *filename, int linenr)`:: + + Helper function which formats the die error message according to the + parameters entered. Used by `git_die_config()`. It can be used by callers + handling `git_config_get_value_multi()` to print the correct error message + for the desired value. + See test-config.c for usage examples. Value Parsing Helpers diff --git a/Documentation/technical/api-run-command.txt b/Documentation/technical/api-run-command.txt index 69510ae57a..842b8389eb 100644 --- a/Documentation/technical/api-run-command.txt +++ b/Documentation/technical/api-run-command.txt @@ -13,6 +13,10 @@ produces in the caller in order to process it. Functions --------- +`child_process_init` + + Initialize a struct child_process variable. + `start_command`:: Start a sub-process. Takes a pointer to a `struct child_process` @@ -96,8 +100,8 @@ command to run in a sub-process. The caller: -1. allocates and clears (memset(&chld, 0, sizeof(chld));) a - struct child_process variable; +1. allocates and clears (using child_process_init() or + CHILD_PROCESS_INIT) a struct child_process variable; 2. initializes the members; 3. calls start_command(); 4. processes the data; @@ -317,9 +317,6 @@ all:: # dependency rules. The default is "auto", which means to use computed header # dependencies if your compiler is detected to support it. # -# Define CHECK_HEADER_DEPENDENCIES to check for problems in the hard-coded -# dependency rules. -# # Define NATIVE_CRLF if your platform uses CRLF for line endings. # # Define XDL_FAST_HASH to use an alternative line-hashing method in @@ -432,7 +429,6 @@ XDIFF_OBJS = VCSSVN_OBJS = GENERATED_H = EXTRA_CPPFLAGS = -LIB_H = LIB_OBJS = PROGRAM_OBJS = PROGRAMS = @@ -632,131 +628,11 @@ VCSSVN_LIB = vcs-svn/lib.a GENERATED_H += common-cmds.h -LIB_H += advice.h -LIB_H += archive.h -LIB_H += argv-array.h -LIB_H += attr.h -LIB_H += bisect.h -LIB_H += blob.h -LIB_H += branch.h -LIB_H += builtin.h -LIB_H += bulk-checkin.h -LIB_H += bundle.h -LIB_H += cache-tree.h -LIB_H += cache.h -LIB_H += color.h -LIB_H += column.h -LIB_H += commit.h -LIB_H += compat/bswap.h -LIB_H += compat/mingw.h -LIB_H += compat/obstack.h -LIB_H += compat/poll/poll.h -LIB_H += compat/precompose_utf8.h -LIB_H += compat/terminal.h -LIB_H += compat/win32/dirent.h -LIB_H += compat/win32/pthread.h -LIB_H += compat/win32/syslog.h -LIB_H += connected.h -LIB_H += convert.h -LIB_H += credential.h -LIB_H += csum-file.h -LIB_H += decorate.h -LIB_H += delta.h -LIB_H += diff.h -LIB_H += diffcore.h -LIB_H += dir.h -LIB_H += exec_cmd.h -LIB_H += ewah/ewok.h -LIB_H += ewah/ewok_rlw.h -LIB_H += fetch-pack.h -LIB_H += fmt-merge-msg.h -LIB_H += fsck.h -LIB_H += gettext.h -LIB_H += git-compat-util.h -LIB_H += gpg-interface.h -LIB_H += graph.h -LIB_H += grep.h -LIB_H += hashmap.h -LIB_H += help.h -LIB_H += http.h -LIB_H += kwset.h -LIB_H += levenshtein.h -LIB_H += line-log.h -LIB_H += line-range.h -LIB_H += list-objects.h -LIB_H += ll-merge.h -LIB_H += log-tree.h -LIB_H += mailmap.h -LIB_H += merge-blobs.h -LIB_H += merge-recursive.h -LIB_H += mergesort.h -LIB_H += notes-cache.h -LIB_H += notes-merge.h -LIB_H += notes-utils.h -LIB_H += notes.h -LIB_H += object.h -LIB_H += pack-objects.h -LIB_H += pack-revindex.h -LIB_H += pack.h -LIB_H += pack-bitmap.h -LIB_H += parse-options.h -LIB_H += patch-ids.h -LIB_H += pathspec.h -LIB_H += pkt-line.h -LIB_H += prio-queue.h -LIB_H += progress.h -LIB_H += prompt.h -LIB_H += quote.h -LIB_H += reachable.h -LIB_H += reflog-walk.h -LIB_H += refs.h -LIB_H += remote.h -LIB_H += rerere.h -LIB_H += resolve-undo.h -LIB_H += revision.h -LIB_H += run-command.h -LIB_H += send-pack.h -LIB_H += sequencer.h -LIB_H += sha1-array.h -LIB_H += sha1-lookup.h -LIB_H += shortlog.h -LIB_H += sideband.h -LIB_H += sigchain.h -LIB_H += strbuf.h -LIB_H += streaming.h -LIB_H += string-list.h -LIB_H += submodule.h -LIB_H += tag.h -LIB_H += tar.h -LIB_H += thread-utils.h -LIB_H += transport.h -LIB_H += tree-walk.h -LIB_H += tree.h -LIB_H += unpack-trees.h -LIB_H += unicode_width.h -LIB_H += url.h -LIB_H += urlmatch.h -LIB_H += userdiff.h -LIB_H += utf8.h -LIB_H += varint.h -LIB_H += vcs-svn/fast_export.h -LIB_H += vcs-svn/line_buffer.h -LIB_H += vcs-svn/repo_tree.h -LIB_H += vcs-svn/sliding_window.h -LIB_H += vcs-svn/svndiff.h -LIB_H += vcs-svn/svndump.h -LIB_H += walker.h -LIB_H += wildmatch.h -LIB_H += wt-status.h -LIB_H += xdiff-interface.h -LIB_H += xdiff/xdiff.h -LIB_H += xdiff/xdiffi.h -LIB_H += xdiff/xemit.h -LIB_H += xdiff/xinclude.h -LIB_H += xdiff/xmacros.h -LIB_H += xdiff/xprepare.h -LIB_H += xdiff/xtypes.h -LIB_H += xdiff/xutils.h +LIB_H = $(shell $(FIND) . \ + -name .git -prune -o \ + -name t -prune -o \ + -name Documentation -prune -o \ + -name '*.h' -print) LIB_OBJS += abspath.o LIB_OBJS += advice.o @@ -1026,11 +902,6 @@ sysconfdir = etc endif endif -ifdef CHECK_HEADER_DEPENDENCIES -COMPUTE_HEADER_DEPENDENCIES = no -USE_COMPUTED_HEADER_DEPENDENCIES = -endif - ifndef COMPUTE_HEADER_DEPENDENCIES COMPUTE_HEADER_DEPENDENCIES = auto endif @@ -1382,7 +1253,6 @@ ifdef NO_INET_PTON endif ifndef NO_UNIX_SOCKETS LIB_OBJS += unix-socket.o - LIB_H += unix-socket.h PROGRAM_OBJS += credential-cache.o PROGRAM_OBJS += credential-cache--daemon.o endif @@ -1406,12 +1276,10 @@ endif ifdef BLK_SHA1 SHA1_HEADER = "block-sha1/sha1.h" LIB_OBJS += block-sha1/sha1.o - LIB_H += block-sha1/sha1.h else ifdef PPC_SHA1 SHA1_HEADER = "ppc/sha1.h" LIB_OBJS += ppc/sha1.o ppc/sha1ppc.o - LIB_H += ppc/sha1.h else ifdef APPLE_COMMON_CRYPTO COMPAT_CFLAGS += -DCOMMON_DIGEST_FOR_OPENSSL @@ -1938,29 +1806,13 @@ $(dep_dirs): missing_dep_dirs := $(filter-out $(wildcard $(dep_dirs)),$(dep_dirs)) dep_file = $(dir $@).depend/$(notdir $@).d dep_args = -MF $(dep_file) -MQ $@ -MMD -MP -ifdef CHECK_HEADER_DEPENDENCIES -$(error cannot compute header dependencies outside a normal build. \ -Please unset CHECK_HEADER_DEPENDENCIES and try again) -endif endif ifneq ($(COMPUTE_HEADER_DEPENDENCIES),yes) -ifndef CHECK_HEADER_DEPENDENCIES dep_dirs = missing_dep_dirs = dep_args = endif -endif - -ifdef CHECK_HEADER_DEPENDENCIES -ifndef PRINT_HEADER_DEPENDENCIES -missing_deps = $(filter-out $(notdir $^), \ - $(notdir $(shell $(MAKE) -s $@ \ - CHECK_HEADER_DEPENDENCIES=YesPlease \ - USE_COMPUTED_HEADER_DEPENDENCIES=YesPlease \ - PRINT_HEADER_DEPENDENCIES=YesPlease))) -endif -endif ASM_SRC := $(wildcard $(OBJECTS:o=S)) ASM_OBJ := $(ASM_SRC:S=o) @@ -1968,45 +1820,10 @@ C_OBJ := $(filter-out $(ASM_OBJ),$(OBJECTS)) .SUFFIXES: -ifdef PRINT_HEADER_DEPENDENCIES -$(C_OBJ): %.o: %.c FORCE - echo $^ -$(ASM_OBJ): %.o: %.S FORCE - echo $^ - -ifndef CHECK_HEADER_DEPENDENCIES -$(error cannot print header dependencies during a normal build. \ -Please set CHECK_HEADER_DEPENDENCIES and try again) -endif -endif - -ifndef PRINT_HEADER_DEPENDENCIES -ifdef CHECK_HEADER_DEPENDENCIES -$(C_OBJ): %.o: %.c $(dep_files) FORCE - @set -e; echo CHECK $@; \ - missing_deps="$(missing_deps)"; \ - if test "$$missing_deps"; \ - then \ - echo missing dependencies: $$missing_deps; \ - false; \ - fi -$(ASM_OBJ): %.o: %.S $(dep_files) FORCE - @set -e; echo CHECK $@; \ - missing_deps="$(missing_deps)"; \ - if test "$$missing_deps"; \ - then \ - echo missing dependencies: $$missing_deps; \ - false; \ - fi -endif -endif - -ifndef CHECK_HEADER_DEPENDENCIES $(C_OBJ): %.o: %.c GIT-CFLAGS $(missing_dep_dirs) $(QUIET_CC)$(CC) -o $*.o -c $(dep_args) $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) $< $(ASM_OBJ): %.o: %.S GIT-CFLAGS $(missing_dep_dirs) $(QUIET_CC)$(CC) -o $*.o -c $(dep_args) $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) $< -endif %.s: %.c GIT-CFLAGS FORCE $(QUIET_CC)$(CC) -o $@ -S $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) $< @@ -2133,9 +1950,9 @@ XGETTEXT_FLAGS_C = $(XGETTEXT_FLAGS) --language=C \ XGETTEXT_FLAGS_SH = $(XGETTEXT_FLAGS) --language=Shell \ --keyword=gettextln --keyword=eval_gettextln XGETTEXT_FLAGS_PERL = $(XGETTEXT_FLAGS) --keyword=__ --language=Perl -LOCALIZED_C := $(C_OBJ:o=c) $(LIB_H) $(GENERATED_H) -LOCALIZED_SH := $(SCRIPT_SH) -LOCALIZED_PERL := $(SCRIPT_PERL) +LOCALIZED_C = $(C_OBJ:o=c) $(LIB_H) $(GENERATED_H) +LOCALIZED_SH = $(SCRIPT_SH) +LOCALIZED_PERL = $(SCRIPT_PERL) ifdef XGETTEXT_INCLUDE_TESTS LOCALIZED_C += t/t0200/test.c @@ -2143,7 +1960,7 @@ LOCALIZED_SH += t/t0200/test.sh LOCALIZED_PERL += t/t0200/test.perl endif -po/git.pot: $(LOCALIZED_C) +po/git.pot: $(GENERATED_H) FORCE $(QUIET_XGETTEXT)$(XGETTEXT) -o$@+ $(XGETTEXT_FLAGS_C) $(LOCALIZED_C) $(QUIET_XGETTEXT)$(XGETTEXT) -o$@+ --join-existing $(XGETTEXT_FLAGS_SH) \ $(LOCALIZED_SH) @@ -86,8 +86,7 @@ int error_resolve_conflict(const char *me) * other commands doing a merge do. */ advise(_("Fix them up in the work tree, and then use 'git add/rm <file>'\n" - "as appropriate to mark resolution and make a commit, or use\n" - "'git commit -a'.")); + "as appropriate to mark resolution and make a commit.")); return -1; } @@ -1,26 +1,13 @@ #include "cache.h" -static const char *alias_key; -static char *alias_val; - -static int alias_lookup_cb(const char *k, const char *v, void *cb) -{ - const char *name; - if (skip_prefix(k, "alias.", &name) && !strcmp(name, alias_key)) { - if (!v) - return config_error_nonbool(k); - alias_val = xstrdup(v); - return 0; - } - return 0; -} - char *alias_lookup(const char *alias) { - alias_key = alias; - alias_val = NULL; - git_config(alias_lookup_cb, NULL); - return alias_val; + char *v = NULL; + struct strbuf key = STRBUF_INIT; + strbuf_addf(&key, "alias.%s", alias); + git_config_get_string(key.buf, &v); + strbuf_release(&key); + return v; } #define SPLIT_CMDLINE_BAD_ENDING 1 diff --git a/archive-tar.c b/archive-tar.c index 603650fa3c..df2f4c8a64 100644 --- a/archive-tar.c +++ b/archive-tar.c @@ -395,7 +395,7 @@ static int write_tar_filter_archive(const struct archiver *ar, struct archiver_args *args) { struct strbuf cmd = STRBUF_INIT; - struct child_process filter; + struct child_process filter = CHILD_PROCESS_INIT; const char *argv[2]; int r; @@ -406,7 +406,6 @@ static int write_tar_filter_archive(const struct archiver *ar, if (args->compression_level >= 0) strbuf_addf(&cmd, " -%d", args->compression_level); - memset(&filter, 0, sizeof(filter)); argv[0] = cmd.buf; argv[1] = NULL; filter.argv = argv; @@ -402,14 +402,6 @@ static int parse_archive_args(int argc, const char **argv, return argc; } -static int git_default_archive_config(const char *var, const char *value, - void *cb) -{ - if (!strcmp(var, "uploadarchive.allowunreachable")) - remote_allow_unreachable = git_config_bool(var, value); - return git_default_config(var, value, cb); -} - int write_archive(int argc, const char **argv, const char *prefix, int setup_prefix, const char *name_hint, int remote) { @@ -420,7 +412,9 @@ int write_archive(int argc, const char **argv, const char *prefix, if (setup_prefix && prefix == NULL) prefix = setup_git_directory_gently(&nongit); - git_config(git_default_archive_config, NULL); + git_config_get_bool("uploadarchive.allowunreachable", &remote_allow_unreachable); + git_config(git_default_config, NULL); + init_tar_archiver(); init_zip_archiver(); @@ -215,11 +215,12 @@ static struct commit_list *best_bisection_sorted(struct commit_list *list, int n } qsort(array, cnt, sizeof(*array), compare_commit_dist); for (p = list, i = 0; i < cnt; i++) { - struct name_decoration *r = xmalloc(sizeof(*r) + 100); + char buf[100]; /* enough for dist=%d */ struct object *obj = &(array[i].commit->object); - sprintf(r->name, "dist=%d", array[i].distance); - r->next = add_decoration(&name_decoration, obj, r); + snprintf(buf, sizeof(buf), "dist=%d", array[i].distance); + add_name_decoration(DECORATION_NONE, buf, obj); + p->item = array[i].commit; p = p->next; } @@ -140,33 +140,17 @@ static int setup_tracking(const char *new_ref, const char *orig_ref, return 0; } -struct branch_desc_cb { - const char *config_name; - const char *value; -}; - -static int read_branch_desc_cb(const char *var, const char *value, void *cb) -{ - struct branch_desc_cb *desc = cb; - if (strcmp(desc->config_name, var)) - return 0; - free((char *)desc->value); - return git_config_string(&desc->value, var, value); -} - int read_branch_desc(struct strbuf *buf, const char *branch_name) { - struct branch_desc_cb cb; + char *v = NULL; struct strbuf name = STRBUF_INIT; strbuf_addf(&name, "branch.%s.description", branch_name); - cb.config_name = name.buf; - cb.value = NULL; - if (git_config(read_branch_desc_cb, &cb) < 0) { + if (git_config_get_string(name.buf, &v)) { strbuf_release(&name); return -1; } - if (cb.value) - strbuf_addstr(buf, cb.value); + strbuf_addstr(buf, v); + free(v); strbuf_release(&name); return 0; } @@ -226,7 +210,6 @@ void create_branch(const char *head, int force, int reflog, int clobber_head, int quiet, enum branch_track track) { - struct ref_lock *lock = NULL; struct commit *commit; unsigned char sha1[20]; char *real_ref, msg[PATH_MAX + 20]; @@ -285,15 +268,6 @@ void create_branch(const char *head, die(_("Not a valid branch point: '%s'."), start_name); hashcpy(sha1, commit->object.sha1); - if (!dont_change_ref) { - lock = lock_any_ref_for_update(ref.buf, NULL, 0, NULL); - if (!lock) - die_errno(_("Failed to lock ref for update")); - } - - if (reflog) - log_all_ref_updates = 1; - if (forcing) snprintf(msg, sizeof msg, "branch: Reset to %s", start_name); @@ -301,13 +275,26 @@ void create_branch(const char *head, snprintf(msg, sizeof msg, "branch: Created from %s", start_name); + if (reflog) + log_all_ref_updates = 1; + + if (!dont_change_ref) { + struct ref_transaction *transaction; + struct strbuf err = STRBUF_INIT; + + transaction = ref_transaction_begin(&err); + if (!transaction || + ref_transaction_update(transaction, ref.buf, sha1, + null_sha1, 0, !forcing, &err) || + ref_transaction_commit(transaction, msg, &err)) + die("%s", err.buf); + ref_transaction_free(transaction); + strbuf_release(&err); + } + if (real_ref && track) setup_tracking(ref.buf + 11, real_ref, track, quiet); - if (!dont_change_ref) - if (write_ref_sha1(lock, sha1, msg) < 0) - die_errno(_("Failed to write ref")); - strbuf_release(&ref); free(real_ref); } diff --git a/builtin/add.c b/builtin/add.c index 4baf3a5635..352b85e8db 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -180,7 +180,7 @@ static int edit_patch(int argc, const char **argv, const char *prefix) char *file = git_pathdup("ADD_EDIT.patch"); const char *apply_argv[] = { "apply", "--recount", "--cached", NULL, NULL }; - struct child_process child; + struct child_process child = CHILD_PROCESS_INIT; struct rev_info rev; int out; struct stat st; @@ -214,7 +214,6 @@ static int edit_patch(int argc, const char **argv, const char *prefix) if (!st.st_size) die(_("Empty patch. Aborted.")); - memset(&child, 0, sizeof(child)); child.git_cmd = 1; child.argv = apply_argv; if (run_command(&child)) diff --git a/builtin/apply.c b/builtin/apply.c index 6b7c764918..f204cca5d2 100644 --- a/builtin/apply.c +++ b/builtin/apply.c @@ -4274,13 +4274,11 @@ static int apply_patch(int fd, const char *filename, int options) return 0; } -static int git_apply_config(const char *var, const char *value, void *cb) +static void git_apply_config(void) { - if (!strcmp(var, "apply.whitespace")) - return git_config_string(&apply_default_whitespace, var, value); - else if (!strcmp(var, "apply.ignorewhitespace")) - return git_config_string(&apply_default_ignorewhitespace, var, value); - return git_default_config(var, value, cb); + git_config_get_string_const("apply.whitespace", &apply_default_whitespace); + git_config_get_string_const("apply.ignorewhitespace", &apply_default_ignorewhitespace); + git_config(git_default_config, NULL); } static int option_parse_exclude(const struct option *opt, @@ -4428,7 +4426,7 @@ int cmd_apply(int argc, const char **argv, const char *prefix_) prefix = prefix_; prefix_length = prefix ? strlen(prefix) : 0; - git_config(git_apply_config, NULL); + git_apply_config(); if (apply_default_whitespace) parse_whitespace_option(apply_default_whitespace); if (apply_default_ignorewhitespace) diff --git a/builtin/checkout.c b/builtin/checkout.c index f71e74531d..8afdf2b5c4 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -552,6 +552,12 @@ static int merge_working_tree(const struct checkout_opts *opts, } } + if (!active_cache_tree) + active_cache_tree = cache_tree(); + + if (!cache_tree_fully_valid(active_cache_tree)) + cache_tree_update(&the_index, WRITE_TREE_SILENT | WRITE_TREE_REPAIR); + if (write_locked_index(&the_index, lock_file, COMMIT_LOCK)) die(_("unable to write new index file")); diff --git a/builtin/commit.c b/builtin/commit.c index 36e3a2eef2..41f481bd03 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -353,6 +353,13 @@ static char *prepare_index(int argc, const char **argv, const char *prefix, discard_cache(); read_cache_from(index_lock.filename); + if (update_main_cache_tree(WRITE_TREE_SILENT) == 0) { + if (reopen_lock_file(&index_lock) < 0) + die(_("unable to write index file")); + if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK)) + die(_("unable to update temporary index")); + } else + warning(_("Failed to update main cache tree")); commit_style = COMMIT_NORMAL; return index_lock.filename; @@ -393,8 +400,12 @@ static char *prepare_index(int argc, const char **argv, const char *prefix, if (!only && !pathspec.nr) { hold_locked_index(&index_lock, 1); refresh_cache_or_die(refresh_flags); - if (active_cache_changed) { + if (active_cache_changed + || !cache_tree_fully_valid(active_cache_tree)) { update_main_cache_tree(WRITE_TREE_SILENT); + active_cache_changed = 1; + } + if (active_cache_changed) { if (write_locked_index(&the_index, &index_lock, COMMIT_LOCK)) die(_("unable to write new_index file")); @@ -444,6 +455,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix, hold_locked_index(&index_lock, 1); add_remove_files(&partial); refresh_cache(REFRESH_QUIET); + update_main_cache_tree(WRITE_TREE_SILENT); if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK)) die(_("unable to write new_index file")); @@ -1540,7 +1552,7 @@ static int run_rewrite_hook(const unsigned char *oldsha1, { /* oldsha1 SP newsha1 LF NUL */ static char buf[2*40 + 3]; - struct child_process proc; + struct child_process proc = CHILD_PROCESS_INIT; const char *argv[3]; int code; size_t n; @@ -1552,7 +1564,6 @@ static int run_rewrite_hook(const unsigned char *oldsha1, argv[1] = "amend"; argv[2] = NULL; - memset(&proc, 0, sizeof(proc)); proc.argv = argv; proc.in = -1; proc.stdout_to_stderr = 1; @@ -1652,11 +1663,12 @@ int cmd_commit(int argc, const char **argv, const char *prefix) const char *index_file, *reflog_msg; char *nl; unsigned char sha1[20]; - struct ref_lock *ref_lock; struct commit_list *parents = NULL, **pptr = &parents; struct stat statbuf; struct commit *current_head = NULL; struct commit_extra_header *extra = NULL; + struct ref_transaction *transaction; + struct strbuf err = STRBUF_INIT; if (argc == 2 && !strcmp(argv[1], "-h")) usage_with_options(builtin_commit_usage, builtin_commit_options); @@ -1778,16 +1790,6 @@ int cmd_commit(int argc, const char **argv, const char *prefix) strbuf_release(&author_ident); free_commit_extra_headers(extra); - ref_lock = lock_any_ref_for_update("HEAD", - !current_head - ? NULL - : current_head->object.sha1, - 0, NULL); - if (!ref_lock) { - rollback_index_files(); - die(_("cannot lock HEAD ref")); - } - nl = strchr(sb.buf, '\n'); if (nl) strbuf_setlen(&sb, nl + 1 - sb.buf); @@ -1796,10 +1798,17 @@ int cmd_commit(int argc, const char **argv, const char *prefix) strbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg)); strbuf_insert(&sb, strlen(reflog_msg), ": ", 2); - if (write_ref_sha1(ref_lock, sha1, sb.buf) < 0) { + transaction = ref_transaction_begin(&err); + if (!transaction || + ref_transaction_update(transaction, "HEAD", sha1, + current_head + ? current_head->object.sha1 : NULL, + 0, !!current_head, &err) || + ref_transaction_commit(transaction, sb.buf, &err)) { rollback_index_files(); - die(_("cannot update HEAD ref")); + die("%s", err.buf); } + ref_transaction_free(transaction); unlink(git_path("CHERRY_PICK_HEAD")); unlink(git_path("REVERT_HEAD")); @@ -1828,5 +1837,6 @@ int cmd_commit(int argc, const char **argv, const char *prefix) if (!quiet) print_summary(prefix, sha1, !current_head); + strbuf_release(&err); return 0; } diff --git a/builtin/fetch.c b/builtin/fetch.c index e8d0cca3e4..159fb7e916 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -1110,9 +1110,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) struct string_list list = STRING_LIST_INIT_NODUP; struct remote *remote; int result = 0; - static const char *argv_gc_auto[] = { - "gc", "--auto", NULL, - }; + struct argv_array argv_gc_auto = ARGV_ARRAY_INIT; packet_trace_identity("fetch"); @@ -1198,7 +1196,11 @@ int cmd_fetch(int argc, const char **argv, const char *prefix) list.strdup_strings = 1; string_list_clear(&list, 0); - run_command_v_opt(argv_gc_auto, RUN_GIT_CMD); + argv_array_pushl(&argv_gc_auto, "gc", "--auto", NULL); + if (verbosity < 0) + argv_array_push(&argv_gc_auto, "--quiet"); + run_command_v_opt(argv_gc_auto.argv, RUN_GIT_CMD); + argv_array_clear(&argv_gc_auto); return result; } diff --git a/builtin/gc.c b/builtin/gc.c index 8d219d8c42..ced1456e1e 100644 --- a/builtin/gc.c +++ b/builtin/gc.c @@ -55,44 +55,33 @@ static void remove_pidfile_on_signal(int signo) raise(signo); } -static int gc_config(const char *var, const char *value, void *cb) +static void gc_config(void) { - if (!strcmp(var, "gc.packrefs")) { + const char *value; + + if (!git_config_get_value("gc.packrefs", &value)) { if (value && !strcmp(value, "notbare")) pack_refs = -1; else - pack_refs = git_config_bool(var, value); - return 0; - } - if (!strcmp(var, "gc.aggressivewindow")) { - aggressive_window = git_config_int(var, value); - return 0; - } - if (!strcmp(var, "gc.aggressivedepth")) { - aggressive_depth = git_config_int(var, value); - return 0; - } - if (!strcmp(var, "gc.auto")) { - gc_auto_threshold = git_config_int(var, value); - return 0; - } - if (!strcmp(var, "gc.autopacklimit")) { - gc_auto_pack_limit = git_config_int(var, value); - return 0; + pack_refs = git_config_bool("gc.packrefs", value); } - if (!strcmp(var, "gc.autodetach")) { - detach_auto = git_config_bool(var, value); - return 0; - } - if (!strcmp(var, "gc.pruneexpire")) { - if (value && strcmp(value, "now")) { + + git_config_get_int("gc.aggressivewindow", &aggressive_window); + git_config_get_int("gc.aggressivedepth", &aggressive_depth); + git_config_get_int("gc.auto", &gc_auto_threshold); + git_config_get_int("gc.autopacklimit", &gc_auto_pack_limit); + git_config_get_bool("gc.autodetach", &detach_auto); + + if (!git_config_get_string_const("gc.pruneexpire", &prune_expire)) { + if (strcmp(prune_expire, "now")) { unsigned long now = approxidate("now"); - if (approxidate(value) >= now) - return error(_("Invalid %s: '%s'"), var, value); + if (approxidate(prune_expire) >= now) { + git_die_config("gc.pruneexpire", _("Invalid gc.pruneexpire: '%s'"), + prune_expire); + } } - return git_config_string(&prune_expire, var, value); } - return git_default_config(var, value, cb); + git_config(git_default_config, NULL); } static int too_many_loose_objects(void) @@ -301,7 +290,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix) argv_array_pushl(&prune, "prune", "--expire", NULL ); argv_array_pushl(&rerere, "rerere", "gc", NULL); - git_config(gc_config, NULL); + gc_config(); if (pack_refs < 0) pack_refs = !is_bare_repository(); diff --git a/builtin/hash-object.c b/builtin/hash-object.c index d7fcf4c13c..6158363318 100644 --- a/builtin/hash-object.c +++ b/builtin/hash-object.c @@ -10,35 +10,55 @@ #include "parse-options.h" #include "exec_cmd.h" -static void hash_fd(int fd, const char *type, int write_object, const char *path) +/* + * This is to create corrupt objects for debugging and as such it + * needs to bypass the data conversion performed by, and the type + * limitation imposed by, index_fd() and its callees. + */ +static int hash_literally(unsigned char *sha1, int fd, const char *type, unsigned flags) +{ + struct strbuf buf = STRBUF_INIT; + int ret; + + 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); + strbuf_release(&buf); + return ret; +} + +static void hash_fd(int fd, const char *type, const char *path, unsigned flags, + int literally) { struct stat st; unsigned char sha1[20]; - unsigned flags = (HASH_FORMAT_CHECK | - (write_object ? HASH_WRITE_OBJECT : 0)); if (fstat(fd, &st) < 0 || - index_fd(sha1, fd, &st, type_from_string(type), path, flags)) - die(write_object + (literally + ? hash_literally(sha1, fd, type, flags) + : index_fd(sha1, fd, &st, type_from_string(type), path, flags))) + die((flags & HASH_WRITE_OBJECT) ? "Unable to add %s to database" : "Unable to hash %s", path); printf("%s\n", sha1_to_hex(sha1)); maybe_flush_or_die(stdout, "hash to stdout"); } -static void hash_object(const char *path, const char *type, int write_object, - const char *vpath) +static void hash_object(const char *path, const char *type, const char *vpath, + unsigned flags, int literally) { int fd; fd = open(path, O_RDONLY); if (fd < 0) die_errno("Cannot open '%s'", path); - hash_fd(fd, type, write_object, vpath); + hash_fd(fd, type, vpath, flags, literally); } -static int no_filters; - -static void hash_stdin_paths(const char *type, int write_objects) +static void hash_stdin_paths(const char *type, int no_filters, unsigned flags, + int literally) { struct strbuf buf = STRBUF_INIT, nbuf = STRBUF_INIT; @@ -49,47 +69,46 @@ static void hash_stdin_paths(const char *type, int write_objects) die("line is badly quoted"); strbuf_swap(&buf, &nbuf); } - hash_object(buf.buf, type, write_objects, - no_filters ? NULL : buf.buf); + hash_object(buf.buf, type, no_filters ? NULL : buf.buf, flags, + literally); } strbuf_release(&buf); strbuf_release(&nbuf); } -static const char * const hash_object_usage[] = { - N_("git hash-object [-t <type>] [-w] [--path=<file>|--no-filters] [--stdin] [--] <file>..."), - N_("git hash-object --stdin-paths < <list-of-paths>"), - NULL -}; - -static const char *type; -static int write_object; -static int hashstdin; -static int stdin_paths; -static const char *vpath; - -static const struct option hash_object_options[] = { - OPT_STRING('t', NULL, &type, N_("type"), N_("object type")), - OPT_BOOL('w', NULL, &write_object, N_("write the object into the object database")), - OPT_COUNTUP( 0 , "stdin", &hashstdin, N_("read the object from stdin")), - OPT_BOOL( 0 , "stdin-paths", &stdin_paths, N_("read file names from stdin")), - OPT_BOOL( 0 , "no-filters", &no_filters, N_("store file as is without filters")), - OPT_STRING( 0 , "path", &vpath, N_("file"), N_("process file as it were from this path")), - OPT_END() -}; - int cmd_hash_object(int argc, const char **argv, const char *prefix) { + static const char * const hash_object_usage[] = { + N_("git hash-object [-t <type>] [-w] [--path=<file>|--no-filters] [--stdin] [--] <file>..."), + N_("git hash-object --stdin-paths < <list-of-paths>"), + NULL + }; + const char *type = blob_type; + int hashstdin = 0; + int stdin_paths = 0; + int no_filters = 0; + int literally = 0; + unsigned flags = HASH_FORMAT_CHECK; + const char *vpath = NULL; + const struct option hash_object_options[] = { + OPT_STRING('t', NULL, &type, N_("type"), N_("object type")), + OPT_BIT('w', NULL, &flags, N_("write the object into the object database"), + HASH_WRITE_OBJECT), + OPT_COUNTUP( 0 , "stdin", &hashstdin, N_("read the object from stdin")), + OPT_BOOL( 0 , "stdin-paths", &stdin_paths, N_("read file names from stdin")), + OPT_BOOL( 0 , "no-filters", &no_filters, N_("store file as is without filters")), + OPT_BOOL( 0, "literally", &literally, N_("just hash any random garbage to create corrupt objects for debugging Git")), + OPT_STRING( 0 , "path", &vpath, N_("file"), N_("process file as it were from this path")), + OPT_END() + }; int i; int prefix_length = -1; const char *errstr = NULL; - type = blob_type; - argc = parse_options(argc, argv, NULL, hash_object_options, hash_object_usage, 0); - if (write_object) { + if (flags & HASH_WRITE_OBJECT) { prefix = setup_git_directory(); prefix_length = prefix ? strlen(prefix) : 0; if (vpath && prefix) @@ -119,19 +138,19 @@ int cmd_hash_object(int argc, const char **argv, const char *prefix) } if (hashstdin) - hash_fd(0, type, write_object, vpath); + hash_fd(0, type, vpath, flags, literally); for (i = 0 ; i < argc; i++) { const char *arg = argv[i]; if (0 <= prefix_length) arg = prefix_filename(prefix, prefix_length, arg); - hash_object(arg, type, write_object, - no_filters ? NULL : vpath ? vpath : arg); + hash_object(arg, type, no_filters ? NULL : vpath ? vpath : arg, + flags, literally); } if (stdin_paths) - hash_stdin_paths(type, write_object); + hash_stdin_paths(type, no_filters, flags, literally); return 0; } diff --git a/builtin/help.c b/builtin/help.c index 1fdefeb686..8343b4027d 100644 --- a/builtin/help.c +++ b/builtin/help.c @@ -79,12 +79,11 @@ static const char *get_man_viewer_info(const char *name) static int check_emacsclient_version(void) { struct strbuf buffer = STRBUF_INIT; - struct child_process ec_process; + struct child_process ec_process = CHILD_PROCESS_INIT; const char *argv_ec[] = { "emacsclient", "--version", NULL }; int version; /* emacsclient prints its version number on stderr */ - memset(&ec_process, 0, sizeof(ec_process)); ec_process.argv = argv_ec; ec_process.err = -1; ec_process.stdout_to_stderr = 1; diff --git a/builtin/merge.c b/builtin/merge.c index ce82eb297d..9da9e30d9b 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -237,11 +237,10 @@ static void drop_save(void) static int save_state(unsigned char *stash) { int len; - struct child_process cp; + struct child_process cp = CHILD_PROCESS_INIT; struct strbuf buffer = STRBUF_INIT; const char *argv[] = {"stash", "create", NULL}; - memset(&cp, 0, sizeof(cp)); cp.argv = argv; cp.out = -1; cp.git_cmd = 1; diff --git a/builtin/mv.c b/builtin/mv.c index 6ffe540c20..bf784cb943 100644 --- a/builtin/mv.c +++ b/builtin/mv.c @@ -61,6 +61,46 @@ static const char *add_slash(const char *path) static struct lock_file lock_file; #define SUBMODULE_WITH_GITDIR ((const char *)1) +static void prepare_move_submodule(const char *src, int first, + const char **submodule_gitfile) +{ + struct strbuf submodule_dotgit = STRBUF_INIT; + if (!S_ISGITLINK(active_cache[first]->ce_mode)) + die(_("Directory %s is in index and no submodule?"), src); + if (!is_staging_gitmodules_ok()) + die(_("Please stage your changes to .gitmodules or stash them to proceed")); + strbuf_addf(&submodule_dotgit, "%s/.git", src); + *submodule_gitfile = read_gitfile(submodule_dotgit.buf); + if (*submodule_gitfile) + *submodule_gitfile = xstrdup(*submodule_gitfile); + else + *submodule_gitfile = SUBMODULE_WITH_GITDIR; + strbuf_release(&submodule_dotgit); +} + +static int index_range_of_same_dir(const char *src, int length, + int *first_p, int *last_p) +{ + const char *src_w_slash = add_slash(src); + int first, last, len_w_slash = length + 1; + + first = cache_name_pos(src_w_slash, len_w_slash); + if (first >= 0) + die(_("%.*s is in index"), len_w_slash, src_w_slash); + + first = -1 - first; + for (last = first; last < active_nr; last++) { + const char *path = active_cache[last]->name; + if (strncmp(path, src_w_slash, len_w_slash)) + break; + } + if (src_w_slash != src) + free((char *)src_w_slash); + *first_p = first; + *last_p = last; + return last - first; +} + int cmd_mv(int argc, const char **argv, const char *prefix) { int i, gitmodules_modified = 0; @@ -108,7 +148,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix) destination = internal_copy_pathspec(dest_path[0], argv, argc, DUP_BASENAME); } else { if (argc != 1) - die("destination '%s' is not a directory", dest_path[0]); + die(_("destination '%s' is not a directory"), dest_path[0]); destination = dest_path; } @@ -131,75 +171,36 @@ int cmd_mv(int argc, const char **argv, const char *prefix) && lstat(dst, &st) == 0) bad = _("cannot move directory over file"); else if (src_is_dir) { - int first = cache_name_pos(src, length); - if (first >= 0) { - struct strbuf submodule_dotgit = STRBUF_INIT; - if (!S_ISGITLINK(active_cache[first]->ce_mode)) - die (_("Huh? Directory %s is in index and no submodule?"), src); - if (!is_staging_gitmodules_ok()) - die (_("Please, stage your changes to .gitmodules or stash them to proceed")); - strbuf_addf(&submodule_dotgit, "%s/.git", src); - submodule_gitfile[i] = read_gitfile(submodule_dotgit.buf); - if (submodule_gitfile[i]) - submodule_gitfile[i] = xstrdup(submodule_gitfile[i]); - else - submodule_gitfile[i] = SUBMODULE_WITH_GITDIR; - strbuf_release(&submodule_dotgit); - } else { - const char *src_w_slash = add_slash(src); - int last, len_w_slash = length + 1; + int first = cache_name_pos(src, length), last; - modes[i] = WORKING_DIRECTORY; + if (first >= 0) + prepare_move_submodule(src, first, + submodule_gitfile + i); + else if (index_range_of_same_dir(src, length, + &first, &last) < 1) + bad = _("source directory is empty"); + else { /* last - first >= 1 */ + int j, dst_len, n; - first = cache_name_pos(src_w_slash, len_w_slash); - if (first >= 0) - die (_("Huh? %.*s is in index?"), - len_w_slash, src_w_slash); - - first = -1 - first; - for (last = first; last < active_nr; last++) { - const char *path = active_cache[last]->name; - if (strncmp(path, src_w_slash, len_w_slash)) - break; - } - if (src_w_slash != src) - free((char *)src_w_slash); - - if (last - first < 1) - bad = _("source directory is empty"); - else { - int j, dst_len; - - if (last - first > 0) { - source = xrealloc(source, - (argc + last - first) - * sizeof(char *)); - destination = xrealloc(destination, - (argc + last - first) - * sizeof(char *)); - modes = xrealloc(modes, - (argc + last - first) - * sizeof(enum update_mode)); - submodule_gitfile = xrealloc(submodule_gitfile, - (argc + last - first) - * sizeof(char *)); - } + modes[i] = WORKING_DIRECTORY; + n = argc + last - first; + source = xrealloc(source, n * sizeof(char *)); + destination = xrealloc(destination, n * sizeof(char *)); + modes = xrealloc(modes, n * sizeof(enum update_mode)); + submodule_gitfile = xrealloc(submodule_gitfile, n * sizeof(char *)); - dst = add_slash(dst); - dst_len = strlen(dst); + dst = add_slash(dst); + dst_len = strlen(dst); - for (j = 0; j < last - first; j++) { - const char *path = - active_cache[first + j]->name; - source[argc + j] = path; - destination[argc + j] = - prefix_path(dst, dst_len, - path + length + 1); - modes[argc + j] = INDEX; - submodule_gitfile[argc + j] = NULL; - } - argc += last - first; + for (j = 0; j < last - first; j++) { + const char *path = active_cache[first + j]->name; + source[argc + j] = path; + destination[argc + j] = + prefix_path(dst, dst_len, path + length + 1); + modes[argc + j] = INDEX; + submodule_gitfile[argc + j] = NULL; } + argc += last - first; } } else if (cache_name_pos(src, length) < 0) bad = _("not under version control"); @@ -225,24 +226,22 @@ int cmd_mv(int argc, const char **argv, const char *prefix) else string_list_insert(&src_for_dst, dst); - if (bad) { - if (ignore_errors) { - if (--argc > 0) { - memmove(source + i, source + i + 1, - (argc - i) * sizeof(char *)); - memmove(destination + i, - destination + i + 1, - (argc - i) * sizeof(char *)); - memmove(modes + i, modes + i + 1, - (argc - i) * sizeof(enum update_mode)); - memmove(submodule_gitfile + i, - submodule_gitfile + i + 1, - (argc - i) * sizeof(char *)); - i--; - } - } else - die (_("%s, source=%s, destination=%s"), - bad, src, dst); + if (!bad) + continue; + if (!ignore_errors) + die(_("%s, source=%s, destination=%s"), + bad, src, dst); + if (--argc > 0) { + int n = argc - i; + memmove(source + i, source + i + 1, + n * sizeof(char *)); + memmove(destination + i, destination + i + 1, + n * sizeof(char *)); + memmove(modes + i, modes + i + 1, + n * sizeof(enum update_mode)); + memmove(submodule_gitfile + i, submodule_gitfile + i + 1, + n * sizeof(char *)); + i--; } } @@ -254,7 +253,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix) printf(_("Renaming %s to %s\n"), src, dst); if (!show_only && mode != INDEX) { if (rename(src, dst) < 0 && !ignore_errors) - die_errno (_("renaming '%s' failed"), src); + die_errno(_("renaming '%s' failed"), src); if (submodule_gitfile[i]) { if (submodule_gitfile[i] != SUBMODULE_WITH_GITDIR) connect_work_tree_and_git_dir(dst, submodule_gitfile[i]); @@ -275,10 +274,9 @@ int cmd_mv(int argc, const char **argv, const char *prefix) if (gitmodules_modified) stage_updated_gitmodules(); - if (active_cache_changed) { - if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK)) - die(_("Unable to write new index file")); - } + if (active_cache_changed && + write_locked_index(&the_index, &lock_file, COMMIT_LOCK)) + die(_("Unable to write new index file")); return 0; } diff --git a/builtin/notes.c b/builtin/notes.c index 820c34135c..c25a412510 100644 --- a/builtin/notes.c +++ b/builtin/notes.c @@ -122,12 +122,11 @@ static void write_commented_object(int fd, const unsigned char *object) { const char *show_args[5] = {"show", "--stat", "--no-notes", sha1_to_hex(object), NULL}; - struct child_process show; + struct child_process show = CHILD_PROCESS_INIT; struct strbuf buf = STRBUF_INIT; struct strbuf cbuf = STRBUF_INIT; /* Invoke "git show --stat --no-notes $object" */ - memset(&show, 0, sizeof(show)); show.argv = show_args; show.no_stdin = 1; show.out = -1; diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index f93ac454b4..afb8d99264 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -255,7 +255,7 @@ static int copy_to_sideband(int in, int out, void *arg) typedef int (*feed_fn)(void *, const char **, size_t *); static int run_and_feed_hook(const char *hook_name, feed_fn feed, void *feed_state) { - struct child_process proc; + struct child_process proc = CHILD_PROCESS_INIT; struct async muxer; const char *argv[2]; int code; @@ -266,7 +266,6 @@ static int run_and_feed_hook(const char *hook_name, feed_fn feed, void *feed_sta argv[1] = NULL; - memset(&proc, 0, sizeof(proc)); proc.argv = argv; proc.in = -1; proc.stdout_to_stderr = 1; @@ -350,7 +349,7 @@ static int run_receive_hook(struct command *commands, const char *hook_name, static int run_update_hook(struct command *cmd) { const char *argv[5]; - struct child_process proc; + struct child_process proc = CHILD_PROCESS_INIT; int code; argv[0] = find_hook("update"); @@ -362,7 +361,6 @@ static int run_update_hook(struct command *cmd) argv[3] = sha1_to_hex(cmd->new_sha1); argv[4] = NULL; - memset(&proc, 0, sizeof(proc)); proc.no_stdin = 1; proc.stdout_to_stderr = 1; proc.err = use_sideband ? -1 : 0; @@ -475,7 +473,6 @@ static const char *update(struct command *cmd, struct shallow_info *si) const char *namespaced_name; unsigned char *old_sha1 = cmd->old_sha1; unsigned char *new_sha1 = cmd->new_sha1; - struct ref_lock *lock; /* only refs/... are allowed */ if (!starts_with(name, "refs/") || check_refname_format(name + 5, 0)) { @@ -576,19 +573,27 @@ static const char *update(struct command *cmd, struct shallow_info *si) return NULL; /* good */ } else { + struct strbuf err = STRBUF_INIT; + struct ref_transaction *transaction; + if (shallow_update && si->shallow_ref[cmd->index] && update_shallow_ref(cmd, si)) return "shallow error"; - lock = lock_any_ref_for_update(namespaced_name, old_sha1, - 0, NULL); - if (!lock) { - rp_error("failed to lock %s", name); - return "failed to lock"; - } - if (write_ref_sha1(lock, new_sha1, "push")) { - return "failed to write"; /* error() already called */ + transaction = ref_transaction_begin(&err); + if (!transaction || + ref_transaction_update(transaction, namespaced_name, + new_sha1, old_sha1, 0, 1, &err) || + ref_transaction_commit(transaction, "push", &err)) { + ref_transaction_free(transaction); + + rp_error("%s", err.buf); + strbuf_release(&err); + return "failed to update ref"; } + + ref_transaction_free(transaction); + strbuf_release(&err); return NULL; /* good */ } } @@ -598,7 +603,7 @@ static void run_update_post_hook(struct command *commands) struct command *cmd; int argc; const char **argv; - struct child_process proc; + struct child_process proc = CHILD_PROCESS_INIT; char *hook; hook = find_hook("post-update"); @@ -621,7 +626,6 @@ static void run_update_post_hook(struct command *commands) } argv[argc] = NULL; - memset(&proc, 0, sizeof(proc)); proc.no_stdin = 1; proc.stdout_to_stderr = 1; proc.err = use_sideband ? -1 : 0; @@ -911,7 +915,7 @@ static const char *unpack(int err_fd, struct shallow_info *si) const char *hdr_err; int status; char hdr_arg[38]; - struct child_process child; + struct child_process child = CHILD_PROCESS_INIT; int fsck_objects = (receive_fsck_objects >= 0 ? receive_fsck_objects : transfer_fsck_objects >= 0 @@ -933,7 +937,6 @@ static const char *unpack(int err_fd, struct shallow_info *si) argv_array_pushl(&av, "--shallow-file", alt_shallow_file, NULL); } - memset(&child, 0, sizeof(child)); if (ntohl(hdr.hdr_entries) < unpack_limit) { argv_array_pushl(&av, "unpack-objects", hdr_arg, NULL); if (quiet) diff --git a/builtin/remote-ext.c b/builtin/remote-ext.c index 692c834d9d..d699d28e98 100644 --- a/builtin/remote-ext.c +++ b/builtin/remote-ext.c @@ -179,9 +179,8 @@ static void send_git_request(int stdin_fd, const char *serv, const char *repo, static int run_child(const char *arg, const char *service) { int r; - struct child_process child; + struct child_process child = CHILD_PROCESS_INIT; - memset(&child, 0, sizeof(child)); child.in = -1; child.out = -1; child.err = 0; diff --git a/builtin/repack.c b/builtin/repack.c index a77e743b94..fc088dbe6a 100644 --- a/builtin/repack.c +++ b/builtin/repack.c @@ -133,7 +133,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix) {".idx"}, {".bitmap", 1}, }; - struct child_process cmd; + struct child_process cmd = CHILD_PROCESS_INIT; struct string_list_item *item; struct argv_array cmd_args = ARGV_ARRAY_INIT; struct string_list names = STRING_LIST_INIT_DUP; @@ -250,7 +250,6 @@ int cmd_repack(int argc, const char **argv, const char *prefix) argv_array_push(&cmd_args, packtmp); - memset(&cmd, 0, sizeof(cmd)); cmd.argv = cmd_args.argv; cmd.git_cmd = 1; cmd.out = -1; diff --git a/builtin/replace.c b/builtin/replace.c index 294b61b97e..8020db8500 100644 --- a/builtin/replace.c +++ b/builtin/replace.c @@ -155,7 +155,8 @@ static int replace_object_sha1(const char *object_ref, unsigned char prev[20]; enum object_type obj_type, repl_type; char ref[PATH_MAX]; - struct ref_lock *lock; + struct ref_transaction *transaction; + struct strbuf err = STRBUF_INIT; obj_type = sha1_object_info(object, NULL); repl_type = sha1_object_info(repl, NULL); @@ -168,12 +169,13 @@ static int replace_object_sha1(const char *object_ref, check_ref_valid(object, prev, ref, sizeof(ref), force); - lock = lock_any_ref_for_update(ref, prev, 0, NULL); - if (!lock) - die("%s: cannot lock the ref", ref); - if (write_ref_sha1(lock, repl, NULL) < 0) - die("%s: cannot update the ref", ref); + transaction = ref_transaction_begin(&err); + if (!transaction || + ref_transaction_update(transaction, ref, repl, prev, 0, 1, &err) || + ref_transaction_commit(transaction, NULL, &err)) + die("%s", err.buf); + ref_transaction_free(transaction); return 0; } @@ -197,7 +199,7 @@ static int replace_object(const char *object_ref, const char *replace_ref, int f static void export_object(const unsigned char *sha1, enum object_type type, int raw, const char *filename) { - struct child_process cmd = { NULL }; + struct child_process cmd = CHILD_PROCESS_INIT; int fd; fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666); @@ -234,7 +236,7 @@ static void import_object(unsigned char *sha1, enum object_type type, if (!raw && type == OBJ_TREE) { const char *argv[] = { "mktree", NULL }; - struct child_process cmd = { argv }; + struct child_process cmd = CHILD_PROCESS_INIT; struct strbuf result = STRBUF_INIT; cmd.argv = argv; diff --git a/builtin/tag.c b/builtin/tag.c index 19eb747820..a81b9e4174 100644 --- a/builtin/tag.c +++ b/builtin/tag.c @@ -576,7 +576,6 @@ int cmd_tag(int argc, const char **argv, const char *prefix) struct strbuf ref = STRBUF_INIT; unsigned char object[20], prev[20]; const char *object_ref, *tag; - struct ref_lock *lock; struct create_tag_options opt; char *cleanup_arg = NULL; int annotate = 0, force = 0, lines = -1; @@ -584,6 +583,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix) const char *msgfile = NULL, *keyid = NULL; struct msg_arg msg = { 0, STRBUF_INIT }; struct commit_list *with_commit = NULL; + struct ref_transaction *transaction; + struct strbuf err = STRBUF_INIT; struct option options[] = { OPT_CMDMODE('l', "list", &cmdmode, N_("list tag names"), 'l'), { OPTION_INTEGER, 'n', NULL, &lines, N_("n"), @@ -729,14 +730,17 @@ int cmd_tag(int argc, const char **argv, const char *prefix) if (annotate) create_tag(object, tag, &buf, &opt, prev, object); - lock = lock_any_ref_for_update(ref.buf, prev, 0, NULL); - if (!lock) - die(_("%s: cannot lock the ref"), ref.buf); - if (write_ref_sha1(lock, object, NULL) < 0) - die(_("%s: cannot update the ref"), ref.buf); + transaction = ref_transaction_begin(&err); + if (!transaction || + ref_transaction_update(transaction, ref.buf, object, prev, + 0, 1, &err) || + ref_transaction_commit(transaction, NULL, &err)) + die("%s", err.buf); + ref_transaction_free(transaction); if (force && !is_null_sha1(prev) && hashcmp(prev, object)) printf(_("Updated tag '%s' (was %s)\n"), tag, find_unique_abbrev(prev, DEFAULT_ABBREV)); + strbuf_release(&err); strbuf_release(&buf); strbuf_release(&ref); return 0; diff --git a/builtin/update-ref.c b/builtin/update-ref.c index 3067b11310..54a48c0cfa 100644 --- a/builtin/update-ref.c +++ b/builtin/update-ref.c @@ -12,11 +12,8 @@ static const char * const git_update_ref_usage[] = { NULL }; -static struct ref_transaction *transaction; - static char line_termination = '\n'; static int update_flags; -static struct strbuf err = STRBUF_INIT; /* * Parse one whitespace- or NUL-terminated, possibly C-quoted argument @@ -177,8 +174,10 @@ static int parse_next_sha1(struct strbuf *input, const char **next, * depending on how line_termination is set. */ -static const char *parse_cmd_update(struct strbuf *input, const char *next) +static const char *parse_cmd_update(struct ref_transaction *transaction, + struct strbuf *input, const char *next) { + struct strbuf err = STRBUF_INIT; char *refname; unsigned char new_sha1[20]; unsigned char old_sha1[20]; @@ -204,12 +203,15 @@ static const char *parse_cmd_update(struct strbuf *input, const char *next) update_flags = 0; free(refname); + strbuf_release(&err); return next; } -static const char *parse_cmd_create(struct strbuf *input, const char *next) +static const char *parse_cmd_create(struct ref_transaction *transaction, + struct strbuf *input, const char *next) { + struct strbuf err = STRBUF_INIT; char *refname; unsigned char new_sha1[20]; @@ -226,16 +228,21 @@ static const char *parse_cmd_create(struct strbuf *input, const char *next) if (*next != line_termination) die("create %s: extra input: %s", refname, next); - ref_transaction_create(transaction, refname, new_sha1, update_flags); + if (ref_transaction_create(transaction, refname, new_sha1, + update_flags, &err)) + die("%s", err.buf); update_flags = 0; free(refname); + strbuf_release(&err); return next; } -static const char *parse_cmd_delete(struct strbuf *input, const char *next) +static const char *parse_cmd_delete(struct ref_transaction *transaction, + struct strbuf *input, const char *next) { + struct strbuf err = STRBUF_INIT; char *refname; unsigned char old_sha1[20]; int have_old; @@ -256,17 +263,21 @@ static const char *parse_cmd_delete(struct strbuf *input, const char *next) if (*next != line_termination) die("delete %s: extra input: %s", refname, next); - ref_transaction_delete(transaction, refname, old_sha1, - update_flags, have_old); + if (ref_transaction_delete(transaction, refname, old_sha1, + update_flags, have_old, &err)) + die("%s", err.buf); update_flags = 0; free(refname); + strbuf_release(&err); return next; } -static const char *parse_cmd_verify(struct strbuf *input, const char *next) +static const char *parse_cmd_verify(struct ref_transaction *transaction, + struct strbuf *input, const char *next) { + struct strbuf err = STRBUF_INIT; char *refname; unsigned char new_sha1[20]; unsigned char old_sha1[20]; @@ -294,6 +305,7 @@ static const char *parse_cmd_verify(struct strbuf *input, const char *next) update_flags = 0; free(refname); + strbuf_release(&err); return next; } @@ -307,7 +319,7 @@ static const char *parse_cmd_option(struct strbuf *input, const char *next) return next + 8; } -static void update_refs_stdin(void) +static void update_refs_stdin(struct ref_transaction *transaction) { struct strbuf input = STRBUF_INIT; const char *next; @@ -322,13 +334,13 @@ static void update_refs_stdin(void) else if (isspace(*next)) die("whitespace before command: %s", next); else if (starts_with(next, "update ")) - next = parse_cmd_update(&input, next + 7); + next = parse_cmd_update(transaction, &input, next + 7); else if (starts_with(next, "create ")) - next = parse_cmd_create(&input, next + 7); + next = parse_cmd_create(transaction, &input, next + 7); else if (starts_with(next, "delete ")) - next = parse_cmd_delete(&input, next + 7); + next = parse_cmd_delete(transaction, &input, next + 7); else if (starts_with(next, "verify ")) - next = parse_cmd_verify(&input, next + 7); + next = parse_cmd_verify(transaction, &input, next + 7); else if (starts_with(next, "option ")) next = parse_cmd_option(&input, next + 7); else @@ -362,15 +374,21 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix) die("Refusing to perform update with empty message."); if (read_stdin) { - transaction = ref_transaction_begin(); + struct strbuf err = STRBUF_INIT; + struct ref_transaction *transaction; + + transaction = ref_transaction_begin(&err); + if (!transaction) + die("%s", err.buf); if (delete || no_deref || argc > 0) usage_with_options(git_update_ref_usage, options); if (end_null) line_termination = '\0'; - update_refs_stdin(); + update_refs_stdin(transaction); if (ref_transaction_commit(transaction, msg, &err)) die("%s", err.buf); ref_transaction_free(transaction); + strbuf_release(&err); return 0; } diff --git a/builtin/verify-pack.c b/builtin/verify-pack.c index 972579f33c..7747537beb 100644 --- a/builtin/verify-pack.c +++ b/builtin/verify-pack.c @@ -8,7 +8,7 @@ static int verify_one_pack(const char *path, unsigned int flags) { - struct child_process index_pack; + struct child_process index_pack = CHILD_PROCESS_INIT; const char *argv[] = {"index-pack", NULL, NULL, NULL }; struct strbuf arg = STRBUF_INIT; int verbose = flags & VERIFY_PACK_VERBOSE; @@ -32,7 +32,6 @@ static int verify_one_pack(const char *path, unsigned int flags) strbuf_addstr(&arg, ".pack"); argv[2] = arg.buf; - memset(&index_pack, 0, sizeof(index_pack)); index_pack.argv = argv; index_pack.git_cmd = 1; @@ -240,7 +240,7 @@ int create_bundle(struct bundle_header *header, const char *path, int i, ref_count = 0; struct strbuf buf = STRBUF_INIT; struct rev_info revs; - struct child_process rls; + struct child_process rls = CHILD_PROCESS_INIT; FILE *rls_fout; bundle_to_stdout = !strcmp(path, "-"); @@ -258,7 +258,6 @@ int create_bundle(struct bundle_header *header, const char *path, init_revisions(&revs, NULL); /* write prerequisites */ - memset(&rls, 0, sizeof(rls)); argv_array_pushl(&rls.args, "rev-list", "--boundary", "--pretty=oneline", NULL); @@ -417,14 +416,13 @@ int unbundle(struct bundle_header *header, int bundle_fd, int flags) { const char *argv_index_pack[] = {"index-pack", "--fix-thin", "--stdin", NULL, NULL}; - struct child_process ip; + struct child_process ip = CHILD_PROCESS_INIT; if (flags & BUNDLE_VERBOSE) argv_index_pack[3] = "-v"; if (verify_bundle(header, 0)) return -1; - memset(&ip, 0, sizeof(ip)); ip.argv = argv_index_pack; ip.in = bundle_fd; ip.no_stdout = 1; diff --git a/cache-tree.c b/cache-tree.c index c53f7de2b1..75a54fdc72 100644 --- a/cache-tree.c +++ b/cache-tree.c @@ -246,9 +246,12 @@ static int update_one(struct cache_tree *it, struct strbuf buffer; int missing_ok = flags & WRITE_TREE_MISSING_OK; int dryrun = flags & WRITE_TREE_DRY_RUN; + int repair = flags & WRITE_TREE_REPAIR; int to_invalidate = 0; int i; + assert(!(dryrun && repair)); + *skip_count = 0; if (0 <= it->entry_count && has_sha1_file(it->sha1)) @@ -320,6 +323,7 @@ static int update_one(struct cache_tree *it, int pathlen, entlen; const unsigned char *sha1; unsigned mode; + int expected_missing = 0; path = ce->name; pathlen = ce_namelen(ce); @@ -336,8 +340,10 @@ static int update_one(struct cache_tree *it, i += sub->count; sha1 = sub->cache_tree->sha1; mode = S_IFDIR; - if (sub->cache_tree->entry_count < 0) + if (sub->cache_tree->entry_count < 0) { to_invalidate = 1; + expected_missing = 1; + } } else { sha1 = ce->sha1; @@ -347,6 +353,8 @@ static int update_one(struct cache_tree *it, } if (mode != S_IFGITLINK && !missing_ok && !has_sha1_file(sha1)) { strbuf_release(&buffer); + if (expected_missing) + return -1; return error("invalid object %06o %s for '%.*s'", mode, sha1_to_hex(sha1), entlen+baselen, path); } @@ -381,7 +389,14 @@ static int update_one(struct cache_tree *it, #endif } - if (dryrun) + if (repair) { + unsigned char sha1[20]; + hash_sha1_file(buffer.buf, buffer.len, tree_type, sha1); + if (has_sha1_file(sha1)) + hashcpy(it->sha1, sha1); + else + to_invalidate = 1; + } else if (dryrun) hash_sha1_file(buffer.buf, buffer.len, tree_type, it->sha1); else if (write_sha1_file(buffer.buf, buffer.len, tree_type, it->sha1)) { strbuf_release(&buffer); diff --git a/cache-tree.h b/cache-tree.h index b47ccec7f6..aa7b3e4a0a 100644 --- a/cache-tree.h +++ b/cache-tree.h @@ -39,6 +39,7 @@ int update_main_cache_tree(int); #define WRITE_TREE_IGNORE_CACHE_TREE 2 #define WRITE_TREE_DRY_RUN 4 #define WRITE_TREE_SILENT 8 +#define WRITE_TREE_REPAIR 16 /* error return codes */ #define WRITE_TREE_UNREADABLE_INDEX (-1) @@ -8,6 +8,7 @@ #include "gettext.h" #include "convert.h" #include "trace.h" +#include "string-list.h" #include SHA1_HEADER #ifndef git_SHA_CTX @@ -1296,7 +1297,7 @@ extern int git_config_from_buf(config_fn_t fn, const char *name, const char *buf, size_t len, void *data); extern void git_config_push_parameter(const char *text); extern int git_config_from_parameters(config_fn_t fn, void *data); -extern int git_config(config_fn_t fn, void *); +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); @@ -1353,9 +1354,32 @@ extern int parse_config_key(const char *var, const char **subsection, int *subsection_len, const char **key); +struct config_set_element { + struct hashmap_entry ent; + char *key; + struct string_list value_list; +}; + +struct configset_list_item { + struct config_set_element *e; + int value_index; +}; + +/* + * the contents of the list are ordered according to their + * position in the config files and order of parsing the files. + * (i.e. key-value pair at the last position of .git/config will + * be at the last item of the list) + */ +struct configset_list { + struct configset_list_item *items; + unsigned int nr, alloc; +}; + struct config_set { struct hashmap config_hash; int hash_initialized; + struct configset_list list; }; extern void git_configset_init(struct config_set *cs); @@ -1385,6 +1409,14 @@ extern int git_config_get_bool_or_int(const char *key, int *is_bool, int *dest); extern int git_config_get_maybe_bool(const char *key, int *dest); extern int git_config_get_pathname(const char *key, const char **dest); +struct key_value_info { + const char *filename; + int linenr; +}; + +extern NORETURN void git_die_config(const char *key, const char *err, ...) __attribute__((format(printf, 2, 3))); +extern NORETURN void git_die_config_linenr(const char *key, const char *filename, int linenr); + extern int committer_ident_sufficiently_given(void); extern int author_ident_sufficiently_given(void); @@ -367,7 +367,7 @@ int parseopt_column_callback(const struct option *opt, } static int fd_out = -1; -static struct child_process column_process; +static struct child_process column_process = CHILD_PROCESS_INIT; int run_column_filter(int colopts, const struct column_options *opts) { @@ -26,13 +26,25 @@ extern int save_commit_buffer; extern const char *commit_type; /* While we can decorate any object with a name, it's only used for commits.. */ -extern struct decoration name_decoration; struct name_decoration { struct name_decoration *next; int type; - char name[1]; + char name[FLEX_ARRAY]; }; +enum decoration_type { + DECORATION_NONE = 0, + DECORATION_REF_LOCAL, + DECORATION_REF_REMOTE, + DECORATION_REF_TAG, + DECORATION_REF_STASH, + DECORATION_REF_HEAD, + DECORATION_GRAFTED, +}; + +void add_name_decoration(enum decoration_type type, const char *name, struct object *obj); +const struct name_decoration *get_name_decoration(const struct object *obj); + struct commit *lookup_commit(const unsigned char *sha1); struct commit *lookup_commit_reference(const unsigned char *sha1); struct commit *lookup_commit_reference_gently(const unsigned char *sha1, @@ -35,12 +35,6 @@ struct config_source { long (*do_ftell)(struct config_source *c); }; -struct config_set_element { - struct hashmap_entry ent; - char *key; - struct string_list value_list; -}; - static struct config_source *cf; static int zlib_compression_seen; @@ -252,6 +246,7 @@ static int get_next_char(void) cf->linenr++; if (c == EOF) { cf->eof = 1; + cf->linenr++; c = '\n'; } return c; @@ -327,6 +322,7 @@ static int get_value(config_fn_t fn, void *data, struct strbuf *name) { int c; char *value; + int ret; /* Get the full name */ for (;;) { @@ -349,7 +345,15 @@ static int get_value(config_fn_t fn, void *data, struct strbuf *name) if (!value) return -1; } - return fn(name->buf, value, data); + /* + * We already consumed the \n, but we need linenr to point to + * the line we just parsed during the call to fn to get + * accurate line number in error messages. + */ + cf->linenr--; + ret = fn(name->buf, value, data); + cf->linenr++; + return ret; } static int get_extended_base_var(struct strbuf *name, int c) @@ -465,9 +469,9 @@ static int git_parse_source(config_fn_t fn, void *data) break; } if (cf->die_on_error) - die("bad config file line %d in %s", cf->linenr, cf->name); + die(_("bad config file line %d in %s"), cf->linenr, cf->name); else - return error("bad config file line %d in %s", cf->linenr, cf->name); + return error(_("bad config file line %d in %s"), cf->linenr, cf->name); } static int parse_unit_factor(const char *end, uintmax_t *val) @@ -583,9 +587,9 @@ static void die_bad_number(const char *name, const char *value) value = ""; if (cf && cf->name) - die("bad numeric config value '%s' for '%s' in %s: %s", + die(_("bad numeric config value '%s' for '%s' in %s: %s"), value, name, cf->name, reason); - die("bad numeric config value '%s' for '%s': %s", value, name, reason); + die(_("bad numeric config value '%s' for '%s': %s"), value, name, reason); } int git_config_int(const char *name, const char *value) @@ -670,7 +674,7 @@ int git_config_pathname(const char **dest, const char *var, const char *value) return config_error_nonbool(var); *dest = expand_user_path(value); if (!*dest) - die("Failed to expand user dir in: '%s'", value); + die(_("failed to expand user dir in: '%s'"), value); return 0; } @@ -748,7 +752,7 @@ static int git_default_core_config(const char *var, const char *value) if (level == -1) level = Z_DEFAULT_COMPRESSION; else if (level < 0 || level > Z_BEST_COMPRESSION) - die("bad zlib compression level %d", level); + die(_("bad zlib compression level %d"), level); zlib_compression_level = level; zlib_compression_seen = 1; return 0; @@ -759,7 +763,7 @@ static int git_default_core_config(const char *var, const char *value) if (level == -1) level = Z_DEFAULT_COMPRESSION; else if (level < 0 || level > Z_BEST_COMPRESSION) - die("bad zlib compression level %d", level); + die(_("bad zlib compression level %d"), level); core_compression_level = level; core_compression_seen = 1; if (!zlib_compression_seen) @@ -881,7 +885,7 @@ static int git_default_core_config(const char *var, const char *value) else if (!strcmp(value, "link")) object_creation_mode = OBJECT_CREATION_USES_HARDLINKS; else - die("Invalid mode for object creation: %s", value); + die(_("invalid mode for object creation: %s"), value); return 0; } @@ -1181,7 +1185,7 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config) switch (git_config_from_parameters(fn, data)) { case -1: /* error */ - die("unable to parse command-line config"); + die(_("unable to parse command-line config")); break; case 0: /* found nothing */ break; @@ -1228,9 +1232,48 @@ int git_config_with_options(config_fn_t fn, void *data, return ret; } -int git_config(config_fn_t fn, void *data) +static void git_config_raw(config_fn_t fn, void *data) +{ + if (git_config_with_options(fn, data, NULL, 1) < 0) + /* + * git_config_with_options() normally returns only + * positive values, as most errors are fatal, and + * non-fatal potential errors are guarded by "if" + * statements that are entered only when no error is + * possible. + * + * If we ever encounter a non-fatal error, it means + * something went really wrong and we should stop + * immediately. + */ + die(_("unknown error occured while reading the configuration files")); +} + +static void configset_iter(struct config_set *cs, config_fn_t fn, void *data) { - return git_config_with_options(fn, data, NULL, 1); + int i, value_index; + struct string_list *values; + struct config_set_element *entry; + struct configset_list *list = &cs->list; + struct key_value_info *kv_info; + + for (i = 0; i < list->nr; i++) { + entry = list->items[i].e; + value_index = list->items[i].value_index; + values = &entry->value_list; + if (fn(entry->key, values->items[value_index].string, data) < 0) { + kv_info = values->items[value_index].util; + git_die_config_linenr(entry->key, kv_info->filename, kv_info->linenr); + } + } +} + +static void git_config_check_init(void); + +void git_config(config_fn_t fn, void *data) +{ + git_config_check_init(); + configset_iter(&the_config_set, fn, data); } static struct config_set_element *configset_find_element(struct config_set *cs, const char *key) @@ -1258,6 +1301,10 @@ static struct config_set_element *configset_find_element(struct config_set *cs, static int configset_add_value(struct config_set *cs, const char *key, const char *value) { struct config_set_element *e; + struct string_list_item *si; + struct configset_list_item *l_item; + struct key_value_info *kv_info = xmalloc(sizeof(*kv_info)); + e = configset_find_element(cs, key); /* * Since the keys are being fed by git_config*() callback mechanism, they @@ -1270,7 +1317,22 @@ static int configset_add_value(struct config_set *cs, const char *key, const cha string_list_init(&e->value_list, 1); hashmap_add(&cs->config_hash, e); } - string_list_append_nodup(&e->value_list, value ? xstrdup(value) : NULL); + si = string_list_append_nodup(&e->value_list, value ? xstrdup(value) : NULL); + + ALLOC_GROW(cs->list.items, cs->list.nr + 1, cs->list.alloc); + l_item = &cs->list.items[cs->list.nr++]; + l_item->e = e; + l_item->value_index = e->value_list.nr - 1; + + if (cf) { + kv_info->filename = strintern(cf->name); + kv_info->linenr = cf->linenr; + } else { + /* for values read from `git_config_from_parameters()` */ + kv_info->filename = NULL; + kv_info->linenr = -1; + } + si->util = kv_info; return 0; } @@ -1285,6 +1347,9 @@ void git_configset_init(struct config_set *cs) { hashmap_init(&cs->config_hash, (hashmap_cmp_fn)config_set_element_cmp, 0); cs->hash_initialized = 1; + cs->list.nr = 0; + cs->list.alloc = 0; + cs->list.items = NULL; } void git_configset_clear(struct config_set *cs) @@ -1297,10 +1362,14 @@ void git_configset_clear(struct config_set *cs) hashmap_iter_init(&cs->config_hash, &iter); while ((entry = hashmap_iter_next(&iter))) { free(entry->key); - string_list_clear(&entry->value_list, 0); + string_list_clear(&entry->value_list, 1); } hashmap_free(&cs->config_hash, 1); cs->hash_initialized = 0; + free(cs->list.items); + cs->list.nr = 0; + cs->list.alloc = 0; + cs->list.items = NULL; } static int config_set_callback(const char *key, const char *value, void *cb) @@ -1419,7 +1488,7 @@ static void git_config_check_init(void) if (the_config_set.hash_initialized) return; git_configset_init(&the_config_set); - git_config(config_set_callback, &the_config_set); + git_config_raw(config_set_callback, &the_config_set); } void git_config_clear(void) @@ -1443,8 +1512,12 @@ const struct string_list *git_config_get_value_multi(const char *key) int git_config_get_string_const(const char *key, const char **dest) { + int ret; git_config_check_init(); - return git_configset_get_string_const(&the_config_set, key, dest); + ret = git_configset_get_string_const(&the_config_set, key, dest); + if (ret < 0) + git_die_config(key, NULL); + return ret; } int git_config_get_string(const char *key, char **dest) @@ -1485,8 +1558,39 @@ int git_config_get_maybe_bool(const char *key, int *dest) int git_config_get_pathname(const char *key, const char **dest) { + int ret; git_config_check_init(); - return git_configset_get_pathname(&the_config_set, key, dest); + ret = git_configset_get_pathname(&the_config_set, key, dest); + if (ret < 0) + git_die_config(key, NULL); + return ret; +} + +NORETURN +void git_die_config_linenr(const char *key, const char *filename, int linenr) +{ + if (!filename) + die(_("unable to parse '%s' from command-line config"), key); + else + die(_("bad config variable '%s' in file '%s' at line %d"), + key, filename, linenr); +} + +NORETURN __attribute__((format(printf, 2, 3))) +void git_die_config(const char *key, const char *err, ...) +{ + const struct string_list *values; + struct key_value_info *kv_info; + + if (err) { + va_list params; + va_start(params, err); + vreportf("error: ", err, params); + va_end(params); + } + values = git_config_get_value_multi(key); + kv_info = values->items[values->nr - 1].util; + git_die_config_linenr(key, kv_info->filename, kv_info->linenr); } /* @@ -1522,7 +1626,7 @@ static int store_aux(const char *key, const char *value, void *cb) case KEY_SEEN: if (matches(key, value)) { if (store.seen == 1 && store.multi_replace == 0) { - warning("%s has multiple values", key); + warning(_("%s has multiple values"), key); } ALLOC_GROW(store.offset, store.seen + 1, @@ -537,7 +537,8 @@ static struct child_process *git_proxy_connect(int fd[2], char *host) get_host_and_port(&host, &port); - proxy = xcalloc(1, sizeof(*proxy)); + proxy = xmalloc(sizeof(*proxy)); + child_process_init(proxy); argv_array_push(&proxy->args, git_proxy_command); argv_array_push(&proxy->args, host); argv_array_push(&proxy->args, port); @@ -639,7 +640,7 @@ static enum protocol parse_connect_url(const char *url_orig, char **ret_host, return protocol; } -static struct child_process no_fork; +static struct child_process no_fork = CHILD_PROCESS_INIT; /* * This returns a dummy child_process if the transport protocol does not @@ -694,7 +695,8 @@ struct child_process *git_connect(int fd[2], const char *url, target_host, 0); free(target_host); } else { - conn = xcalloc(1, sizeof(*conn)); + conn = xmalloc(sizeof(*conn)); + child_process_init(conn); strbuf_addstr(&cmd, prog); strbuf_addch(&cmd, ' '); diff --git a/connected.c b/connected.c index dae9c9972e..299c56090b 100644 --- a/connected.c +++ b/connected.c @@ -25,7 +25,7 @@ static int check_everything_connected_real(sha1_iterate_fn fn, struct transport *transport, const char *shallow_file) { - struct child_process rev_list; + struct child_process rev_list = CHILD_PROCESS_INIT; const char *argv[9]; char commit[41]; unsigned char sha1[20]; @@ -60,7 +60,6 @@ static int check_everything_connected_real(sha1_iterate_fn fn, argv[ac++] = "--quiet"; argv[ac] = NULL; - memset(&rev_list, 0, sizeof(rev_list)); rev_list.argv = argv; rev_list.git_cmd = 1; rev_list.in = -1; diff --git a/contrib/subtree/Makefile b/contrib/subtree/Makefile index d9a0ce2c63..c2bd703ee3 100644 --- a/contrib/subtree/Makefile +++ b/contrib/subtree/Makefile @@ -1,3 +1,6 @@ +# The default target of this Makefile is... +all:: + -include ../../config.mak.autogen -include ../../config.mak @@ -34,7 +37,7 @@ GIT_SUBTREE_XML := git-subtree.xml GIT_SUBTREE_TXT := git-subtree.txt GIT_SUBTREE_HTML := git-subtree.html -all: $(GIT_SUBTREE) +all:: $(GIT_SUBTREE) $(GIT_SUBTREE): $(GIT_SUBTREE_SH) sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' $< >$@ @@ -321,7 +321,7 @@ static int filter_buffer(int in, int out, void *data) /* * Spawn cmd and feed the buffer contents through its stdin. */ - struct child_process child_process; + struct child_process child_process = CHILD_PROCESS_INIT; struct filter_params *params = (struct filter_params *)data; int write_err, status; const char *argv[] = { NULL, NULL }; @@ -344,7 +344,6 @@ static int filter_buffer(int in, int out, void *data) argv[0] = cmd.buf; - memset(&child_process, 0, sizeof(child_process)); child_process.argv = argv; child_process.use_shell = 1; child_process.in = -1; diff --git a/credential-cache.c b/credential-cache.c index 9a03792c7d..8689a1519a 100644 --- a/credential-cache.c +++ b/credential-cache.c @@ -37,12 +37,11 @@ static int send_request(const char *socket, const struct strbuf *out) static void spawn_daemon(const char *socket) { - struct child_process daemon; + struct child_process daemon = CHILD_PROCESS_INIT; const char *argv[] = { NULL, NULL, NULL }; char buf[128]; int r; - memset(&daemon, 0, sizeof(daemon)); argv[0] = "git-credential-cache--daemon"; argv[1] = socket; daemon.argv = argv; diff --git a/credential.c b/credential.c index 4d79d320f8..1886ea50b3 100644 --- a/credential.c +++ b/credential.c @@ -205,11 +205,10 @@ static int run_credential_helper(struct credential *c, const char *cmd, int want_output) { - struct child_process helper; + struct child_process helper = CHILD_PROCESS_INIT; const char *argv[] = { NULL, NULL }; FILE *fp; - memset(&helper, 0, sizeof(helper)); argv[0] = cmd; helper.argv = argv; helper.use_shell = 1; @@ -230,23 +230,6 @@ struct daemon_service { int overridable; }; -static struct daemon_service *service_looking_at; -static int service_enabled; - -static int git_daemon_config(const char *var, const char *value, void *cb) -{ - const char *service; - - if (skip_prefix(var, "daemon.", &service) && - !strcmp(service, service_looking_at->config_name)) { - service_enabled = git_config_bool(var, value); - return 0; - } - - /* we are not interested in parsing any other configuration here */ - return 0; -} - static int daemon_error(const char *dir, const char *msg) { if (!informative_errors) @@ -259,7 +242,7 @@ static const char *access_hook; static int run_access_hook(struct daemon_service *service, const char *dir, const char *path) { - struct child_process child; + struct child_process child = CHILD_PROCESS_INIT; struct strbuf buf = STRBUF_INIT; const char *argv[8]; const char **arg = argv; @@ -277,7 +260,6 @@ static int run_access_hook(struct daemon_service *service, const char *dir, cons *arg = NULL; #undef STRARG - memset(&child, 0, sizeof(child)); child.use_shell = 1; child.argv = argv; child.no_stdin = 1; @@ -324,6 +306,7 @@ static int run_service(const char *dir, struct daemon_service *service) { const char *path; int enabled = service->enabled; + struct strbuf var = STRBUF_INIT; loginfo("Request %s for '%s'", service->name, dir); @@ -354,11 +337,9 @@ static int run_service(const char *dir, struct daemon_service *service) } if (service->overridable) { - service_looking_at = service; - service_enabled = -1; - git_config(git_daemon_config, NULL); - if (0 <= service_enabled) - enabled = service_enabled; + strbuf_addf(&var, "daemon.%s", service->config_name); + git_config_get_bool(var.buf, &enabled); + strbuf_release(&var); } if (!enabled) { logerror("'%s': service not enabled for '%s'", @@ -406,9 +387,8 @@ static void copy_to_log(int fd) static int run_service_command(const char **argv) { - struct child_process cld; + struct child_process cld = CHILD_PROCESS_INIT; - memset(&cld, 0, sizeof(cld)); cld.argv = argv; cld.git_cmd = 1; cld.err = -1; @@ -733,7 +713,7 @@ static void check_dead_children(void) static char **cld_argv; static void handle(int incoming, struct sockaddr *addr, socklen_t addrlen) { - struct child_process cld = { NULL }; + struct child_process cld = CHILD_PROCESS_INIT; char addrbuf[300] = "REMOTE_ADDR=", portbuf[300]; char *env[] = { addrbuf, portbuf, NULL }; @@ -376,7 +376,7 @@ static unsigned long diff_filespec_size(struct diff_filespec *one) { if (!DIFF_FILE_VALID(one)) return 0; - diff_populate_filespec(one, 1); + diff_populate_filespec(one, CHECK_SIZE_ONLY); return one->size; } @@ -1910,11 +1910,11 @@ static void show_dirstat(struct diff_options *options) diff_free_filespec_data(p->one); diff_free_filespec_data(p->two); } else if (DIFF_FILE_VALID(p->one)) { - diff_populate_filespec(p->one, 1); + diff_populate_filespec(p->one, CHECK_SIZE_ONLY); copied = added = 0; diff_free_filespec_data(p->one); } else if (DIFF_FILE_VALID(p->two)) { - diff_populate_filespec(p->two, 1); + diff_populate_filespec(p->two, CHECK_SIZE_ONLY); copied = 0; added = p->two->size; diff_free_filespec_data(p->two); @@ -2188,8 +2188,8 @@ int diff_filespec_is_binary(struct diff_filespec *one) one->is_binary = one->driver->binary; else { if (!one->data && DIFF_FILE_VALID(one)) - diff_populate_filespec(one, 0); - if (one->data) + diff_populate_filespec(one, CHECK_BINARY); + if (one->is_binary == -1 && one->data) one->is_binary = buffer_is_binary(one->data, one->size); if (one->is_binary == -1) @@ -2324,6 +2324,19 @@ static void builtin_diff(const char *name_a, } else if (!DIFF_OPT_TST(o, TEXT) && ( (!textconv_one && diff_filespec_is_binary(one)) || (!textconv_two && diff_filespec_is_binary(two)) )) { + if (!one->data && !two->data && + S_ISREG(one->mode) && S_ISREG(two->mode) && + !DIFF_OPT_TST(o, BINARY)) { + if (!hashcmp(one->sha1, two->sha1)) { + if (must_show_header) + fprintf(o->file, "%s", header.buf); + goto free_ab_and_return; + } + fprintf(o->file, "%s", header.buf); + fprintf(o->file, "%sBinary files %s and %s differ\n", + line_prefix, lbl[0], lbl[1]); + goto free_ab_and_return; + } if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0) die("unable to read files to diff"); /* Quite common confusing case */ @@ -2668,8 +2681,9 @@ static int diff_populate_gitlink(struct diff_filespec *s, int size_only) * grab the data for the blob (or file) for our own in-core comparison. * diff_filespec has data and size fields for this purpose. */ -int diff_populate_filespec(struct diff_filespec *s, int size_only) +int diff_populate_filespec(struct diff_filespec *s, unsigned int flags) { + int size_only = flags & CHECK_SIZE_ONLY; int err = 0; /* * demote FAIL to WARN to allow inspecting the situation @@ -2724,6 +2738,11 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only) } if (size_only) return 0; + if ((flags & CHECK_BINARY) && + s->size > big_file_threshold && s->is_binary == -1) { + s->is_binary = 1; + return 0; + } fd = open(s->path, O_RDONLY); if (fd < 0) goto err_empty; @@ -2745,16 +2764,21 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only) } else { enum object_type type; - if (size_only) { + if (size_only || (flags & CHECK_BINARY)) { type = sha1_object_info(s->sha1, &s->size); if (type < 0) die("unable to read %s", sha1_to_hex(s->sha1)); - } else { - s->data = read_sha1_file(s->sha1, &type, &s->size); - if (!s->data) - die("unable to read %s", sha1_to_hex(s->sha1)); - s->should_free = 1; + if (size_only) + return 0; + if (s->size > big_file_threshold && s->is_binary == -1) { + s->is_binary = 1; + return 0; + } } + s->data = read_sha1_file(s->sha1, &type, &s->size); + if (!s->data) + die("unable to read %s", sha1_to_hex(s->sha1)); + s->should_free = 1; } return 0; } @@ -4688,8 +4712,8 @@ static int diff_filespec_check_stat_unmatch(struct diff_filepair *p) !DIFF_FILE_VALID(p->two) || (p->one->sha1_valid && p->two->sha1_valid) || (p->one->mode != p->two->mode) || - diff_populate_filespec(p->one, 1) || - diff_populate_filespec(p->two, 1) || + diff_populate_filespec(p->one, CHECK_SIZE_ONLY) || + diff_populate_filespec(p->two, CHECK_SIZE_ONLY) || (p->one->size != p->two->size) || !diff_filespec_is_identical(p->one, p->two)) /* (2) */ p->skip_stat_unmatch_result = 1; @@ -4931,7 +4955,7 @@ static char *run_textconv(const char *pgm, struct diff_filespec *spec, struct diff_tempfile *temp; const char *argv[3]; const char **arg = argv; - struct child_process child; + struct child_process child = CHILD_PROCESS_INIT; struct strbuf buf = STRBUF_INIT; int err = 0; @@ -4940,7 +4964,6 @@ static char *run_textconv(const char *pgm, struct diff_filespec *spec, *arg++ = temp->name; *arg = NULL; - memset(&child, 0, sizeof(child)); child.use_shell = 1; child.argv = argv; child.out = -1; diff --git a/diffcore-rename.c b/diffcore-rename.c index 2e44a37459..4e132f1fdb 100644 --- a/diffcore-rename.c +++ b/diffcore-rename.c @@ -147,9 +147,11 @@ static int estimate_similarity(struct diff_filespec *src, * is a possible size - we really should have a flag to * say whether the size is valid or not!) */ - if (!src->cnt_data && diff_populate_filespec(src, 1)) + if (!src->cnt_data && + diff_populate_filespec(src, CHECK_SIZE_ONLY)) return 0; - if (!dst->cnt_data && diff_populate_filespec(dst, 1)) + if (!dst->cnt_data && + diff_populate_filespec(dst, CHECK_SIZE_ONLY)) return 0; max_size = ((src->size > dst->size) ? src->size : dst->size); diff --git a/diffcore.h b/diffcore.h index c876dac71a..33ea2de348 100644 --- a/diffcore.h +++ b/diffcore.h @@ -55,7 +55,9 @@ extern void free_filespec(struct diff_filespec *); extern void fill_filespec(struct diff_filespec *, const unsigned char *, int, unsigned short); -extern int diff_populate_filespec(struct diff_filespec *, int); +#define CHECK_SIZE_ONLY 1 +#define CHECK_BINARY 2 +extern int diff_populate_filespec(struct diff_filespec *, unsigned int); extern void diff_free_filespec_data(struct diff_filespec *); extern void diff_free_filespec_blob(struct diff_filespec *); extern int diff_filespec_is_binary(struct diff_filespec *); @@ -38,10 +38,9 @@ int launch_editor(const char *path, struct strbuf *buffer, const char *const *en if (strcmp(editor, ":")) { const char *args[] = { editor, real_path(path), NULL }; - struct child_process p; + struct child_process p = CHILD_PROCESS_INIT; int ret, sig; - memset(&p, 0, sizeof(p)); p.argv = args; p.env = env; p.use_shell = 1; diff --git a/fast-import.c b/fast-import.c index d73f58cbe3..c071253c90 100644 --- a/fast-import.c +++ b/fast-import.c @@ -946,10 +946,12 @@ static void unkeep_all_packs(void) static void end_packfile(void) { - struct packed_git *old_p = pack_data, *new_p; + if (!pack_data) + return; clear_delta_base_cache(); if (object_count) { + struct packed_git *new_p; unsigned char cur_pack_sha1[20]; char *idx_name; int i; @@ -991,10 +993,11 @@ static void end_packfile(void) pack_id++; } else { - close(old_p->pack_fd); - unlink_or_warn(old_p->pack_name); + close(pack_data->pack_fd); + unlink_or_warn(pack_data->pack_name); } - free(old_p); + free(pack_data); + pack_data = NULL; /* We can't carry a delta across packfiles. */ strbuf_release(&last_blob.data); @@ -1679,8 +1682,9 @@ found_entry: static int update_branch(struct branch *b) { static const char *msg = "fast-import"; - struct ref_lock *lock; + struct ref_transaction *transaction; unsigned char old_sha1[20]; + struct strbuf err = STRBUF_INIT; if (read_ref(b->name, old_sha1)) hashclr(old_sha1); @@ -1689,29 +1693,33 @@ static int update_branch(struct branch *b) delete_ref(b->name, old_sha1, 0); return 0; } - lock = lock_any_ref_for_update(b->name, old_sha1, 0, NULL); - if (!lock) - return error("Unable to lock %s", b->name); if (!force_update && !is_null_sha1(old_sha1)) { struct commit *old_cmit, *new_cmit; old_cmit = lookup_commit_reference_gently(old_sha1, 0); new_cmit = lookup_commit_reference_gently(b->sha1, 0); - if (!old_cmit || !new_cmit) { - unlock_ref(lock); + if (!old_cmit || !new_cmit) return error("Branch %s is missing commits.", b->name); - } if (!in_merge_bases(old_cmit, new_cmit)) { - unlock_ref(lock); warning("Not updating %s" " (new tip %s does not contain %s)", b->name, sha1_to_hex(b->sha1), sha1_to_hex(old_sha1)); return -1; } } - if (write_ref_sha1(lock, b->sha1, msg) < 0) - return error("Unable to update %s", b->name); + transaction = ref_transaction_begin(&err); + if (!transaction || + ref_transaction_update(transaction, b->name, b->sha1, old_sha1, + 0, 1, &err) || + ref_transaction_commit(transaction, msg, &err)) { + ref_transaction_free(transaction); + error("%s", err.buf); + strbuf_release(&err); + return -1; + } + ref_transaction_free(transaction); + strbuf_release(&err); return 0; } @@ -1730,15 +1738,32 @@ static void dump_tags(void) { static const char *msg = "fast-import"; struct tag *t; - struct ref_lock *lock; - char ref_name[PATH_MAX]; + struct strbuf ref_name = STRBUF_INIT; + struct strbuf err = STRBUF_INIT; + struct ref_transaction *transaction; + transaction = ref_transaction_begin(&err); + if (!transaction) { + failure |= error("%s", err.buf); + goto cleanup; + } for (t = first_tag; t; t = t->next_tag) { - sprintf(ref_name, "tags/%s", t->name); - lock = lock_ref_sha1(ref_name, NULL); - if (!lock || write_ref_sha1(lock, t->sha1, msg) < 0) - failure |= error("Unable to update %s", ref_name); + strbuf_reset(&ref_name); + strbuf_addf(&ref_name, "refs/tags/%s", t->name); + + if (ref_transaction_update(transaction, ref_name.buf, t->sha1, + NULL, 0, 0, &err)) { + failure |= error("%s", err.buf); + goto cleanup; + } } + if (ref_transaction_commit(transaction, msg, &err)) + failure |= error("%s", err.buf); + + cleanup: + ref_transaction_free(transaction); + strbuf_release(&ref_name); + strbuf_release(&err); } static void dump_marks_helper(FILE *f, @@ -3274,36 +3299,34 @@ static void parse_option(const char *option) die("This version of fast-import does not support option: %s", option); } -static int git_pack_config(const char *k, const char *v, void *cb) +static void git_pack_config(void) { - if (!strcmp(k, "pack.depth")) { - max_depth = git_config_int(k, v); + int indexversion_value; + unsigned long packsizelimit_value; + + if (!git_config_get_ulong("pack.depth", &max_depth)) { if (max_depth > MAX_DEPTH) max_depth = MAX_DEPTH; - return 0; } - if (!strcmp(k, "pack.compression")) { - int level = git_config_int(k, v); - if (level == -1) - level = Z_DEFAULT_COMPRESSION; - else if (level < 0 || level > Z_BEST_COMPRESSION) - die("bad pack compression level %d", level); - pack_compression_level = level; + if (!git_config_get_int("pack.compression", &pack_compression_level)) { + if (pack_compression_level == -1) + pack_compression_level = Z_DEFAULT_COMPRESSION; + else if (pack_compression_level < 0 || + pack_compression_level > Z_BEST_COMPRESSION) + git_die_config("pack.compression", + "bad pack compression level %d", pack_compression_level); pack_compression_seen = 1; - return 0; } - if (!strcmp(k, "pack.indexversion")) { - pack_idx_opts.version = git_config_int(k, v); + if (!git_config_get_int("pack.indexversion", &indexversion_value)) { + pack_idx_opts.version = indexversion_value; if (pack_idx_opts.version > 2) - die("bad pack.indexversion=%"PRIu32, - pack_idx_opts.version); - return 0; - } - if (!strcmp(k, "pack.packsizelimit")) { - max_packsize = git_config_ulong(k, v); - return 0; + git_die_config("pack.indexversion", + "bad pack.indexversion=%"PRIu32, pack_idx_opts.version); } - return git_default_config(k, v, cb); + if (!git_config_get_ulong("pack.packsizelimit", &packsizelimit_value)) + max_packsize = packsizelimit_value; + + git_config(git_default_config, NULL); } static const char fast_import_usage[] = @@ -3356,7 +3379,7 @@ int main(int argc, char **argv) setup_git_directory(); reset_pack_idx_option(&pack_idx_opts); - git_config(git_pack_config, NULL); + git_pack_config(); if (!pack_compression_seen && core_compression_seen) pack_compression_level = core_compression_level; diff --git a/fetch-pack.c b/fetch-pack.c index b8a58fa7a5..7487aa7306 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -666,7 +666,7 @@ static int get_pack(struct fetch_pack_args *args, char hdr_arg[256]; const char **av, *cmd_name; int do_keep = args->keep_pack; - struct child_process cmd; + struct child_process cmd = CHILD_PROCESS_INIT; int ret; memset(&demux, 0, sizeof(demux)); @@ -685,7 +685,6 @@ static int get_pack(struct fetch_pack_args *args, else demux.out = xd[0]; - memset(&cmd, 0, sizeof(cmd)); cmd.argv = argv; av = argv; *hdr_arg = 0; @@ -869,34 +868,15 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args, return ref; } -static int fetch_pack_config(const char *var, const char *value, void *cb) +static void fetch_pack_config(void) { - if (strcmp(var, "fetch.unpacklimit") == 0) { - fetch_unpack_limit = git_config_int(var, value); - return 0; - } - - if (strcmp(var, "transfer.unpacklimit") == 0) { - transfer_unpack_limit = git_config_int(var, value); - return 0; - } - - if (strcmp(var, "repack.usedeltabaseoffset") == 0) { - prefer_ofs_delta = git_config_bool(var, value); - return 0; - } - - if (!strcmp(var, "fetch.fsckobjects")) { - fetch_fsck_objects = git_config_bool(var, value); - return 0; - } - - if (!strcmp(var, "transfer.fsckobjects")) { - transfer_fsck_objects = git_config_bool(var, value); - return 0; - } + git_config_get_int("fetch.unpacklimit", &fetch_unpack_limit); + git_config_get_int("transfer.unpacklimit", &transfer_unpack_limit); + git_config_get_bool("repack.usedeltabaseoffset", &prefer_ofs_delta); + git_config_get_bool("fetch.fsckobjects", &fetch_fsck_objects); + git_config_get_bool("transfer.fsckobjects", &transfer_fsck_objects); - return git_default_config(var, value, cb); + git_config(git_default_config, NULL); } static void fetch_pack_setup(void) @@ -904,7 +884,7 @@ static void fetch_pack_setup(void) static int did_setup; if (did_setup) return; - git_config(fetch_pack_config, NULL); + fetch_pack_config(); if (0 <= transfer_unpack_limit) unpack_limit = transfer_unpack_limit; else if (0 <= fetch_unpack_limit) diff --git a/git-compat-util.h b/git-compat-util.h index d675c89603..4e7e3f8726 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -609,6 +609,7 @@ extern try_to_free_t set_try_to_free_routine(try_to_free_t); extern char *xstrdup(const char *str); extern void *xmalloc(size_t size); extern void *xmallocz(size_t size); +extern void *xmallocz_gently(size_t size); extern void *xmemdupz(const void *data, size_t len); extern char *xstrndup(const char *str, size_t len); extern void *xrealloc(void *ptr, size_t size); diff --git a/git-pull.sh b/git-pull.sh index 18a394fcc4..4d4fc77b05 100755 --- a/git-pull.sh +++ b/git-pull.sh @@ -20,7 +20,7 @@ die_conflict () { if [ $(git config --bool --get advice.resolveConflict || echo true) = "true" ]; then die "$(gettext "Pull is not possible because you have unmerged files. Please, fix them up in the work tree, and then use 'git add/rm <file>' -as appropriate to mark resolution, or use 'git commit -a'.")" +as appropriate to mark resolution and make a commit.")" else die "$(gettext "Pull is not possible because you have unmerged files.")" fi diff --git a/gpg-interface.c b/gpg-interface.c index ff07012726..1ef73fb7df 100644 --- a/gpg-interface.c +++ b/gpg-interface.c @@ -55,12 +55,11 @@ const char *get_signing_key(void) */ int sign_buffer(struct strbuf *buffer, struct strbuf *signature, const char *signing_key) { - struct child_process gpg; + struct child_process gpg = CHILD_PROCESS_INIT; const char *args[4]; ssize_t len; size_t i, j, bottom; - memset(&gpg, 0, sizeof(gpg)); gpg.argv = args; gpg.in = -1; gpg.out = -1; @@ -116,7 +115,7 @@ int verify_signed_buffer(const char *payload, size_t payload_size, const char *signature, size_t signature_size, struct strbuf *gpg_output, struct strbuf *gpg_status) { - struct child_process gpg; + struct child_process gpg = CHILD_PROCESS_INIT; const char *args_gpg[] = {NULL, "--status-fd=1", "--verify", "FILE", "-", NULL}; char path[PATH_MAX]; int fd, ret; @@ -133,7 +132,6 @@ int verify_signed_buffer(const char *payload, size_t payload_size, path, strerror(errno)); close(fd); - memset(&gpg, 0, sizeof(gpg)); gpg.argv = args_gpg; gpg.in = -1; gpg.out = -1; diff --git a/http-backend.c b/http-backend.c index 80790bbaef..404e682593 100644 --- a/http-backend.c +++ b/http-backend.c @@ -219,29 +219,22 @@ static void get_idx_file(char *name) send_local_file("application/x-git-packed-objects-toc", name); } -static int http_config(const char *var, const char *value, void *cb) +static void http_config(void) { - const char *p; + int i, value = 0; + struct strbuf var = STRBUF_INIT; - if (!strcmp(var, "http.getanyfile")) { - getanyfile = git_config_bool(var, value); - return 0; - } + git_config_get_bool("http.getanyfile", &getanyfile); - if (skip_prefix(var, "http.", &p)) { - int i; - - for (i = 0; i < ARRAY_SIZE(rpc_service); i++) { - struct rpc_service *svc = &rpc_service[i]; - if (!strcmp(p, svc->config_name)) { - svc->enabled = git_config_bool(var, value); - return 0; - } - } + for (i = 0; i < ARRAY_SIZE(rpc_service); i++) { + struct rpc_service *svc = &rpc_service[i]; + strbuf_addf(&var, "http.%s", svc->config_name); + if (!git_config_get_bool(var.buf, &value)) + svc->enabled = value; + strbuf_reset(&var); } - /* we are not interested in parsing any other configuration here */ - return 0; + strbuf_release(&var); } static struct rpc_service *select_service(const char *name) @@ -323,7 +316,7 @@ static void run_service(const char **argv) const char *host = getenv("REMOTE_ADDR"); struct argv_array env = ARGV_ARRAY_INIT; int gzipped_request = 0; - struct child_process cld; + struct child_process cld = CHILD_PROCESS_INIT; if (encoding && !strcmp(encoding, "gzip")) gzipped_request = 1; @@ -341,7 +334,6 @@ static void run_service(const char **argv) argv_array_pushf(&env, "GIT_COMMITTER_EMAIL=%s@http.%s", user, host); - memset(&cld, 0, sizeof(cld)); cld.argv = argv; cld.env = env.argv; if (gzipped_request) @@ -627,7 +619,7 @@ int main(int argc, char **argv) access("git-daemon-export-ok", F_OK) ) not_found("Repository not exported: '%s'", dir); - git_config(http_config, NULL); + http_config(); cmd->imp(cmd_arg); return 0; } @@ -300,6 +300,9 @@ static CURL *get_curl_handle(void) { CURL *result = curl_easy_init(); + if (!result) + die("curl_easy_init failed"); + if (!curl_ssl_verify) { curl_easy_setopt(result, CURLOPT_SSL_VERIFYPEER, 0); curl_easy_setopt(result, CURLOPT_SSL_VERIFYHOST, 0); @@ -399,7 +402,8 @@ void http_init(struct remote *remote, const char *url, int proactive_auth) git_config(urlmatch_config_entry, &config); free(normalized_url); - curl_global_init(CURL_GLOBAL_ALL); + if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) + die("curl_global_init failed"); http_proactive_auth = proactive_auth; @@ -417,10 +421,8 @@ void http_init(struct remote *remote, const char *url, int proactive_auth) } curlm = curl_multi_init(); - if (curlm == NULL) { - fprintf(stderr, "Error creating curl multi handle.\n"); - exit(1); - } + if (!curlm) + die("curl_multi_init failed"); #endif if (getenv("GIT_SSL_NO_VERIFY")) @@ -1332,7 +1334,7 @@ int finish_http_pack_request(struct http_pack_request *preq) struct packed_git **lst; struct packed_git *p = preq->target; char *tmp_idx; - struct child_process ip; + struct child_process ip = CHILD_PROCESS_INIT; const char *ip_argv[8]; close_pack_index(p); @@ -1355,7 +1357,6 @@ int finish_http_pack_request(struct http_pack_request *preq) ip_argv[3] = preq->tmpfile; ip_argv[4] = NULL; - memset(&ip, 0, sizeof(ip)); ip.argv = ip_argv; ip.git_cmd = 1; ip.no_stdin = 1; diff --git a/imap-send.c b/imap-send.c index 87f9bb18f6..f33e56dba1 100644 --- a/imap-send.c +++ b/imap-send.c @@ -69,6 +69,7 @@ struct imap_server_conf { char *tunnel; char *host; int port; + char *folder; char *user; char *pass; int use_ssl; @@ -82,6 +83,7 @@ static struct imap_server_conf server = { NULL, /* tunnel */ NULL, /* host */ 0, /* port */ + NULL, /* folder */ NULL, /* user */ NULL, /* pass */ 0, /* use_ssl */ @@ -523,7 +525,7 @@ static struct imap_cmd *issue_imap_cmd(struct imap_store *ctx, if (Verbose) { if (imap->num_in_progress) printf("(%d in progress) ", imap->num_in_progress); - if (memcmp(cmd->cmd, "LOGIN", 5)) + if (!starts_with(cmd->cmd, "LOGIN")) printf(">>> %s", buf); else printf(">>> %d LOGIN <user> <pass>\n", cmd->tag); @@ -791,7 +793,7 @@ static int get_cmd_result(struct imap_store *ctx, struct imap_cmd *tcmd) else /*if (!strcmp("BAD", arg))*/ resp = RESP_BAD; fprintf(stderr, "IMAP command '%s' returned response (%s) - %s\n", - memcmp(cmdp->cmd, "LOGIN", 5) ? + !starts_with(cmdp->cmd, "LOGIN") ? cmdp->cmd : "LOGIN <user> <pass>", arg, cmd ? cmd : ""); } @@ -924,17 +926,16 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, char *f /* open connection to IMAP server */ if (srvc->tunnel) { - const char *argv[] = { srvc->tunnel, NULL }; - struct child_process tunnel = {NULL}; + struct child_process tunnel = CHILD_PROCESS_INIT; imap_info("Starting tunnel '%s'... ", srvc->tunnel); - tunnel.argv = argv; + argv_array_push(&tunnel.args, srvc->tunnel); tunnel.use_shell = 1; tunnel.in = -1; tunnel.out = -1; if (start_command(&tunnel)) - die("cannot start proxy %s", argv[0]); + die("cannot start proxy %s", srvc->tunnel); imap->buf.sock.fd[0] = tunnel.out; imap->buf.sock.fd[1] = tunnel.in; @@ -1306,45 +1307,35 @@ static int split_msg(struct strbuf *all_msgs, struct strbuf *msg, int *ofs) return 1; } -static char *imap_folder; - -static int git_imap_config(const char *key, const char *val, void *cb) +static void git_imap_config(void) { - if (!skip_prefix(key, "imap.", &key)) - return 0; + const char *val = NULL; - /* check booleans first, and barf on others */ - if (!strcmp("sslverify", key)) - server.ssl_verify = git_config_bool(key, val); - else if (!strcmp("preformattedhtml", key)) - server.use_html = git_config_bool(key, val); - else if (!val) - return config_error_nonbool(key); - - if (!strcmp("folder", key)) { - imap_folder = xstrdup(val); - } else if (!strcmp("host", key)) { - if (starts_with(val, "imap:")) - val += 5; - else if (starts_with(val, "imaps:")) { - val += 6; - server.use_ssl = 1; + git_config_get_bool("imap.sslverify", &server.ssl_verify); + git_config_get_bool("imap.preformattedhtml", &server.use_html); + git_config_get_string("imap.folder", &server.folder); + + if (!git_config_get_value("imap.host", &val)) { + if (!val) { + git_die_config("imap.host", "Missing value for 'imap.host'"); + } else { + if (starts_with(val, "imap:")) + val += 5; + else if (starts_with(val, "imaps:")) { + val += 6; + server.use_ssl = 1; + } + if (starts_with(val, "//")) + val += 2; + server.host = xstrdup(val); } - if (starts_with(val, "//")) - val += 2; - server.host = xstrdup(val); - } else if (!strcmp("user", key)) - server.user = xstrdup(val); - else if (!strcmp("pass", key)) - server.pass = xstrdup(val); - else if (!strcmp("port", key)) - server.port = git_config_int(key, val); - else if (!strcmp("tunnel", key)) - server.tunnel = xstrdup(val); - else if (!strcmp("authmethod", key)) - server.auth_method = xstrdup(val); + } - return 0; + git_config_get_string("imap.user", &server.user); + git_config_get_string("imap.pass", &server.pass); + git_config_get_int("imap.port", &server.port); + git_config_get_string("imap.tunnel", &server.tunnel); + git_config_get_string("imap.authmethod", &server.auth_method); } int main(int argc, char **argv) @@ -1365,12 +1356,12 @@ int main(int argc, char **argv) usage(imap_send_usage); setup_git_directory_gently(&nongit_ok); - git_config(git_imap_config, NULL); + git_imap_config(); if (!server.port) server.port = server.use_ssl ? 993 : 143; - if (!imap_folder) { + if (!server.folder) { fprintf(stderr, "no imap store specified\n"); return 1; } @@ -1400,7 +1391,7 @@ int main(int argc, char **argv) } /* write it to the imap server */ - ctx = imap_open_store(&server, imap_folder); + ctx = imap_open_store(&server, server.folder); if (!ctx) { fprintf(stderr, "failed to open store\n"); return 1; diff --git a/ll-merge.c b/ll-merge.c index fb61ea66a1..8ea03e536a 100644 --- a/ll-merge.c +++ b/ll-merge.c @@ -225,11 +225,8 @@ static int read_merge_config(const char *var, const char *value, void *cb) const char *key, *name; int namelen; - if (!strcmp(var, "merge.default")) { - if (value) - default_ll_merge = xstrdup(value); - return 0; - } + if (!strcmp(var, "merge.default")) + return git_config_string(&default_ll_merge, var, value); /* * We are not interested in anything but "merge.<name>.variable"; @@ -254,12 +251,8 @@ static int read_merge_config(const char *var, const char *value, void *cb) ll_user_merge_tail = &(fn->next); } - if (!strcmp("name", key)) { - if (!value) - return error("%s: lacks value", var); - fn->description = xstrdup(value); - return 0; - } + if (!strcmp("name", key)) + return git_config_string(&fn->description, var, value); if (!strcmp("driver", key)) { if (!value) @@ -285,12 +278,8 @@ static int read_merge_config(const char *var, const char *value, void *cb) return 0; } - if (!strcmp("recursive", key)) { - if (!value) - return error("%s: lacks value", var); - fn->recursive = xstrdup(value); - return 0; - } + if (!strcmp("recursive", key)) + return git_config_string(&fn->recursive, var, value); return 0; } diff --git a/log-tree.c b/log-tree.c index 95e9b1da25..bcee7c5966 100644 --- a/log-tree.c +++ b/log-tree.c @@ -12,17 +12,7 @@ #include "sequencer.h" #include "line-log.h" -struct decoration name_decoration = { "object names" }; - -enum decoration_type { - DECORATION_NONE = 0, - DECORATION_REF_LOCAL, - DECORATION_REF_REMOTE, - DECORATION_REF_TAG, - DECORATION_REF_STASH, - DECORATION_REF_HEAD, - DECORATION_GRAFTED, -}; +static struct decoration name_decoration = { "object names" }; static char decoration_colors[][COLOR_MAXLEN] = { GIT_COLOR_RESET, @@ -84,15 +74,20 @@ int parse_decorate_color_config(const char *var, const int ofs, const char *valu #define decorate_get_color_opt(o, ix) \ decorate_get_color((o)->use_color, ix) -static void add_name_decoration(enum decoration_type type, const char *name, struct object *obj) +void add_name_decoration(enum decoration_type type, const char *name, struct object *obj) { int nlen = strlen(name); - struct name_decoration *res = xmalloc(sizeof(struct name_decoration) + nlen); + struct name_decoration *res = xmalloc(sizeof(*res) + nlen + 1); memcpy(res->name, name, nlen + 1); res->type = type; res->next = add_decoration(&name_decoration, obj, res); } +const struct name_decoration *get_name_decoration(const struct object *obj) +{ + return lookup_decoration(&name_decoration, obj); +} + static int add_ref_decoration(const char *refname, const unsigned char *sha1, int flags, void *cb_data) { struct object *obj; @@ -187,13 +182,13 @@ void format_decorations(struct strbuf *sb, int use_color) { const char *prefix; - struct name_decoration *decoration; + const struct name_decoration *decoration; const char *color_commit = diff_get_color(use_color, DIFF_COMMIT); const char *color_reset = decorate_get_color(use_color, DECORATION_NONE); - decoration = lookup_decoration(&name_decoration, &commit->object); + decoration = get_name_decoration(&commit->object); if (!decoration) return; prefix = " ("; diff --git a/merge-recursive.c b/merge-recursive.c index 1d332b8bbb..8ab944c44c 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -2026,22 +2026,12 @@ int merge_recursive_generic(struct merge_options *o, return clean ? 0 : 1; } -static int merge_recursive_config(const char *var, const char *value, void *cb) +static void merge_recursive_config(struct merge_options *o) { - struct merge_options *o = cb; - if (!strcmp(var, "merge.verbosity")) { - o->verbosity = git_config_int(var, value); - return 0; - } - if (!strcmp(var, "diff.renamelimit")) { - o->diff_rename_limit = git_config_int(var, value); - return 0; - } - if (!strcmp(var, "merge.renamelimit")) { - o->merge_rename_limit = git_config_int(var, value); - return 0; - } - return git_xmerge_config(var, value, cb); + git_config_get_int("merge.verbosity", &o->verbosity); + git_config_get_int("diff.renamelimit", &o->diff_rename_limit); + git_config_get_int("merge.renamelimit", &o->merge_rename_limit); + git_config(git_xmerge_config, NULL); } void init_merge_options(struct merge_options *o) @@ -2052,7 +2042,7 @@ void init_merge_options(struct merge_options *o) o->diff_rename_limit = -1; o->merge_rename_limit = -1; o->renormalize = 0; - git_config(merge_recursive_config, o); + merge_recursive_config(o); if (getenv("GIT_MERGE_VERBOSITY")) o->verbosity = strtol(getenv("GIT_MERGE_VERBOSITY"), NULL, 10); @@ -6,19 +6,13 @@ #define DEFAULT_PAGER "less" #endif -struct pager_config { - const char *cmd; - int want; - char *value; -}; - /* * This is split up from the rest of git so that we can do * something different on Windows. */ static const char *pager_argv[] = { NULL, NULL }; -static struct child_process pager_process; +static struct child_process pager_process = CHILD_PROCESS_INIT; static void wait_for_pager(void) { @@ -155,30 +149,22 @@ int decimal_width(int number) return width; } -static int pager_command_config(const char *var, const char *value, void *data) +/* returns 0 for "no pager", 1 for "use pager", and -1 for "not specified" */ +int check_pager_config(const char *cmd) { - struct pager_config *c = data; - if (starts_with(var, "pager.") && !strcmp(var + 6, c->cmd)) { - int b = git_config_maybe_bool(var, value); + int want = -1; + struct strbuf key = STRBUF_INIT; + const char *value = NULL; + strbuf_addf(&key, "pager.%s", cmd); + if (!git_config_get_value(key.buf, &value)) { + int b = git_config_maybe_bool(key.buf, value); if (b >= 0) - c->want = b; + want = b; else { - c->want = 1; - c->value = xstrdup(value); + want = 1; + pager_program = xstrdup(value); } } - return 0; -} - -/* returns 0 for "no pager", 1 for "use pager", and -1 for "not specified" */ -int check_pager_config(const char *cmd) -{ - struct pager_config c; - c.cmd = cmd; - c.want = -1; - c.value = NULL; - git_config(pager_command_config, &c); - if (c.value) - pager_program = c.value; - return c.want; + strbuf_release(&key); + return want; } @@ -6,7 +6,7 @@ static char *do_askpass(const char *cmd, const char *prompt) { - struct child_process pass; + struct child_process pass = CHILD_PROCESS_INIT; const char *args[3]; static struct strbuf buffer = STRBUF_INIT; int err = 0; @@ -15,7 +15,6 @@ static char *do_askpass(const char *cmd, const char *prompt) args[1] = prompt; args[2] = NULL; - memset(&pass, 0, sizeof(pass)); pass.argv = args; pass.out = -1; diff --git a/read-cache.c b/read-cache.c index 6f0057fe66..b5917e0c07 100644 --- a/read-cache.c +++ b/read-cache.c @@ -1246,24 +1246,16 @@ static struct cache_entry *refresh_cache_entry(struct cache_entry *ce, #define INDEX_FORMAT_DEFAULT 3 -static int index_format_config(const char *var, const char *value, void *cb) -{ - unsigned int *version = cb; - if (!strcmp(var, "index.version")) { - *version = git_config_int(var, value); - return 0; - } - return 1; -} - static unsigned int get_index_format_default(void) { char *envversion = getenv("GIT_INDEX_VERSION"); char *endp; + int value; unsigned int version = INDEX_FORMAT_DEFAULT; if (!envversion) { - git_config(index_format_config, &version); + if (!git_config_get_int("index.version", &value)) + version = value; if (version < INDEX_FORMAT_LB || INDEX_FORMAT_UB < version) { warning(_("index.version set, but the value is invalid.\n" "Using version %i"), INDEX_FORMAT_DEFAULT); @@ -25,6 +25,11 @@ static unsigned char refname_disposition[256] = { }; /* + * Used as a flag to ref_transaction_delete when a loose ref is being + * pruned. + */ +#define REF_ISPRUNING 0x0100 +/* * Try to read one refname component from the front of refname. * Return the length of the component found, or -1 if the component is * not legal. It is legal if it is something reasonable to have under @@ -2068,7 +2073,10 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log) return logs_found; } -/* This function should make sure errno is meaningful on error */ +/* + * Locks a "refs/" ref returning the lock on success and NULL on failure. + * On failure errno is set to something meaningful. + */ static struct ref_lock *lock_ref_sha1_basic(const char *refname, const unsigned char *old_sha1, int flags, int *type_p) @@ -2169,15 +2177,6 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname, return NULL; } -struct ref_lock *lock_ref_sha1(const char *refname, const unsigned char *old_sha1) -{ - char refpath[PATH_MAX]; - if (check_refname_format(refname, 0)) - return NULL; - strcpy(refpath, mkpath("refs/%s", refname)); - return lock_ref_sha1_basic(refpath, old_sha1, 0, NULL); -} - struct ref_lock *lock_any_ref_for_update(const char *refname, const unsigned char *old_sha1, int flags, int *type_p) @@ -2387,13 +2386,25 @@ static void try_remove_empty_parents(char *name) /* make sure nobody touched the ref, and unlink */ static void prune_ref(struct ref_to_prune *r) { - struct ref_lock *lock = lock_ref_sha1(r->name + 5, r->sha1); + struct ref_transaction *transaction; + struct strbuf err = STRBUF_INIT; - if (lock) { - unlink_or_warn(git_path("%s", r->name)); - unlock_ref(lock); - try_remove_empty_parents(r->name); + if (check_refname_format(r->name, 0)) + return; + + transaction = ref_transaction_begin(&err); + if (!transaction || + ref_transaction_delete(transaction, r->name, r->sha1, + REF_ISPRUNING, 1, &err) || + ref_transaction_commit(transaction, NULL, &err)) { + ref_transaction_free(transaction); + error("%s", err.buf); + strbuf_release(&err); + return; } + ref_transaction_free(transaction); + strbuf_release(&err); + try_remove_empty_parents(r->name); } static void prune_refs(struct ref_to_prune *r) @@ -2536,11 +2547,6 @@ int repack_without_refs(const char **refnames, int n, struct strbuf *err) return ret; } -static int repack_without_ref(const char *refname) -{ - return repack_without_refs(&refname, 1, NULL); -} - static int delete_ref_loose(struct ref_lock *lock, int flag) { if (!(flag & REF_ISPACKED) || flag & REF_ISSYMREF) { @@ -2558,24 +2564,22 @@ static int delete_ref_loose(struct ref_lock *lock, int flag) int delete_ref(const char *refname, const unsigned char *sha1, int delopt) { - struct ref_lock *lock; - int ret = 0, flag = 0; - - lock = lock_ref_sha1_basic(refname, sha1, delopt, &flag); - if (!lock) + struct ref_transaction *transaction; + struct strbuf err = STRBUF_INIT; + + transaction = ref_transaction_begin(&err); + if (!transaction || + ref_transaction_delete(transaction, refname, sha1, delopt, + sha1 && !is_null_sha1(sha1), &err) || + ref_transaction_commit(transaction, NULL, &err)) { + error("%s", err.buf); + ref_transaction_free(transaction); + strbuf_release(&err); return 1; - ret |= delete_ref_loose(lock, flag); - - /* removing the loose one could have resurrected an earlier - * packed one. Also, if it was not loose we need to repack - * without it. - */ - ret |= repack_without_ref(lock->ref_name); - - unlink_or_warn(git_path("logs/%s", lock->ref_name)); - clear_loose_ref_cache(&ref_cache); - unlock_ref(lock); - return ret; + } + ref_transaction_free(transaction); + strbuf_release(&err); + return 0; } /* @@ -3332,43 +3336,6 @@ int for_each_reflog(each_ref_fn fn, void *cb_data) return retval; } -static struct ref_lock *update_ref_lock(const char *refname, - const unsigned char *oldval, - int flags, int *type_p, - enum action_on_err onerr) -{ - struct ref_lock *lock; - lock = lock_any_ref_for_update(refname, oldval, flags, type_p); - if (!lock) { - const char *str = "Cannot lock the ref '%s'."; - switch (onerr) { - case UPDATE_REFS_MSG_ON_ERR: error(str, refname); break; - case UPDATE_REFS_DIE_ON_ERR: die(str, refname); break; - case UPDATE_REFS_QUIET_ON_ERR: break; - } - } - return lock; -} - -static int update_ref_write(const char *action, const char *refname, - const unsigned char *sha1, struct ref_lock *lock, - struct strbuf *err, enum action_on_err onerr) -{ - if (write_ref_sha1(lock, sha1, action) < 0) { - const char *str = "Cannot update the ref '%s'."; - if (err) - strbuf_addf(err, str, refname); - - switch (onerr) { - case UPDATE_REFS_MSG_ON_ERR: error(str, refname); break; - case UPDATE_REFS_DIE_ON_ERR: die(str, refname); break; - case UPDATE_REFS_QUIET_ON_ERR: break; - } - return 1; - } - return 0; -} - /** * Information needed for a single ref update. Set new_sha1 to the * new value or to zero to delete the ref. To check the old value @@ -3386,6 +3353,21 @@ struct ref_update { }; /* + * Transaction states. + * OPEN: The transaction is in a valid state and can accept new updates. + * An OPEN transaction can be committed. + * CLOSED: A closed transaction is no longer active and no other operations + * than free can be used on it in this state. + * A transaction can either become closed by successfully committing + * an active transaction or if there is a failure while building + * the transaction thus rendering it failed/inactive. + */ +enum ref_transaction_state { + REF_TRANSACTION_OPEN = 0, + REF_TRANSACTION_CLOSED = 1 +}; + +/* * Data structure for holding a reference transaction, which can * consist of checks and updates to multiple references, carried out * as atomically as possible. This structure is opaque to callers. @@ -3394,9 +3376,10 @@ struct ref_transaction { struct ref_update **updates; size_t alloc; size_t nr; + enum ref_transaction_state state; }; -struct ref_transaction *ref_transaction_begin(void) +struct ref_transaction *ref_transaction_begin(struct strbuf *err) { return xcalloc(1, sizeof(struct ref_transaction)); } @@ -3436,6 +3419,9 @@ int ref_transaction_update(struct ref_transaction *transaction, { struct ref_update *update; + if (transaction->state != REF_TRANSACTION_OPEN) + die("BUG: update called for transaction that is not open"); + if (have_old && !old_sha1) die("BUG: have_old is true but old_sha1 is NULL"); @@ -3448,44 +3434,84 @@ int ref_transaction_update(struct ref_transaction *transaction, return 0; } -void ref_transaction_create(struct ref_transaction *transaction, - const char *refname, - const unsigned char *new_sha1, - int flags) +int ref_transaction_create(struct ref_transaction *transaction, + const char *refname, + const unsigned char *new_sha1, + int flags, + struct strbuf *err) { - struct ref_update *update = add_update(transaction, refname); + struct ref_update *update; + + if (transaction->state != REF_TRANSACTION_OPEN) + die("BUG: create called for transaction that is not open"); + + if (!new_sha1 || is_null_sha1(new_sha1)) + die("BUG: create ref with null new_sha1"); + + update = add_update(transaction, refname); - assert(!is_null_sha1(new_sha1)); hashcpy(update->new_sha1, new_sha1); hashclr(update->old_sha1); update->flags = flags; update->have_old = 1; + return 0; } -void ref_transaction_delete(struct ref_transaction *transaction, - const char *refname, - const unsigned char *old_sha1, - int flags, int have_old) +int ref_transaction_delete(struct ref_transaction *transaction, + const char *refname, + const unsigned char *old_sha1, + int flags, int have_old, + struct strbuf *err) { - struct ref_update *update = add_update(transaction, refname); + struct ref_update *update; + if (transaction->state != REF_TRANSACTION_OPEN) + die("BUG: delete called for transaction that is not open"); + + if (have_old && !old_sha1) + die("BUG: have_old is true but old_sha1 is NULL"); + + update = add_update(transaction, refname); update->flags = flags; update->have_old = have_old; if (have_old) { assert(!is_null_sha1(old_sha1)); hashcpy(update->old_sha1, old_sha1); } + return 0; } int update_ref(const char *action, const char *refname, const unsigned char *sha1, const unsigned char *oldval, int flags, enum action_on_err onerr) { - struct ref_lock *lock; - lock = update_ref_lock(refname, oldval, flags, NULL, onerr); - if (!lock) + struct ref_transaction *t; + struct strbuf err = STRBUF_INIT; + + t = ref_transaction_begin(&err); + if (!t || + ref_transaction_update(t, refname, sha1, oldval, flags, + !!oldval, &err) || + ref_transaction_commit(t, action, &err)) { + const char *str = "update_ref failed for ref '%s': %s"; + + ref_transaction_free(t); + switch (onerr) { + case UPDATE_REFS_MSG_ON_ERR: + error(str, refname, err.buf); + break; + case UPDATE_REFS_DIE_ON_ERR: + die(str, refname, err.buf); + break; + case UPDATE_REFS_QUIET_ON_ERR: + break; + } + strbuf_release(&err); return 1; - return update_ref_write(action, refname, sha1, lock, NULL, onerr); + } + strbuf_release(&err); + ref_transaction_free(t); + return 0; } static int ref_update_compare(const void *r1, const void *r2) @@ -3519,8 +3545,13 @@ int ref_transaction_commit(struct ref_transaction *transaction, int n = transaction->nr; struct ref_update **updates = transaction->updates; - if (!n) + if (transaction->state != REF_TRANSACTION_OPEN) + die("BUG: commit called for transaction that is not open"); + + if (!n) { + transaction->state = REF_TRANSACTION_CLOSED; return 0; + } /* Allocate work space */ delnames = xmalloc(sizeof(*delnames) * n); @@ -3535,12 +3566,12 @@ int ref_transaction_commit(struct ref_transaction *transaction, for (i = 0; i < n; i++) { struct ref_update *update = updates[i]; - update->lock = update_ref_lock(update->refname, - (update->have_old ? - update->old_sha1 : NULL), - update->flags, - &update->type, - UPDATE_REFS_QUIET_ON_ERR); + update->lock = lock_any_ref_for_update(update->refname, + (update->have_old ? + update->old_sha1 : + NULL), + update->flags, + &update->type); if (!update->lock) { if (err) strbuf_addf(err, "Cannot lock the ref '%s'.", @@ -3555,14 +3586,15 @@ int ref_transaction_commit(struct ref_transaction *transaction, struct ref_update *update = updates[i]; if (!is_null_sha1(update->new_sha1)) { - ret = update_ref_write(msg, - update->refname, - update->new_sha1, - update->lock, err, - UPDATE_REFS_QUIET_ON_ERR); - update->lock = NULL; /* freed by update_ref_write */ - if (ret) + ret = write_ref_sha1(update->lock, update->new_sha1, + msg); + update->lock = NULL; /* freed by write_ref_sha1 */ + if (ret) { + if (err) + strbuf_addf(err, "Cannot update the ref '%s'.", + update->refname); goto cleanup; + } } } @@ -3571,8 +3603,9 @@ int ref_transaction_commit(struct ref_transaction *transaction, struct ref_update *update = updates[i]; if (update->lock) { - delnames[delnum++] = update->lock->ref_name; ret |= delete_ref_loose(update->lock, update->type); + if (!(update->flags & REF_ISPRUNING)) + delnames[delnum++] = update->lock->ref_name; } } @@ -3582,6 +3615,8 @@ int ref_transaction_commit(struct ref_transaction *transaction, clear_loose_ref_cache(&ref_cache); cleanup: + transaction->state = REF_TRANSACTION_CLOSED; + for (i = 0; i < n; i++) if (updates[i]->lock) unlock_ref(updates[i]->lock); @@ -10,6 +10,38 @@ struct ref_lock { int force_write; }; +/* + * A ref_transaction represents a collection of ref updates + * that should succeed or fail together. + * + * Calling sequence + * ---------------- + * - Allocate and initialize a `struct ref_transaction` by calling + * `ref_transaction_begin()`. + * + * - List intended ref updates by calling functions like + * `ref_transaction_update()` and `ref_transaction_create()`. + * + * - Call `ref_transaction_commit()` to execute the transaction. + * If this succeeds, the ref updates will have taken place and + * the transaction cannot be rolled back. + * + * - At any time call `ref_transaction_free()` to discard the + * transaction and free associated resources. In particular, + * this rolls back the transaction if it has not been + * successfully committed. + * + * Error handling + * -------------- + * + * On error, transaction functions append a message about what + * went wrong to the 'err' argument. The message mentions what + * ref was being updated (if any) when the error occurred so it + * can be passed to 'die' or 'error' as-is. + * + * The message is appended to err without first clearing err. + * err will not be '\n' terminated. + */ struct ref_transaction; /* @@ -141,14 +173,17 @@ extern int is_branch(const char *refname); extern int peel_ref(const char *refname, unsigned char *sha1); /* - * Locks a "refs/" ref returning the lock on success and NULL on failure. - * On failure errno is set to something meaningful. + * Flags controlling lock_any_ref_for_update(), ref_transaction_update(), + * ref_transaction_create(), etc. + * REF_NODEREF: act on the ref directly, instead of dereferencing + * symbolic references. + * + * Flags >= 0x100 are reserved for internal use. */ -extern struct ref_lock *lock_ref_sha1(const char *refname, const unsigned char *old_sha1); - -/** Locks any ref (for 'HEAD' type refs). */ #define REF_NODEREF 0x01 -/* errno is set to something meaningful on failure */ +/* + * This function sets errno to something meaningful on failure. + */ extern struct ref_lock *lock_any_ref_for_update(const char *refname, const unsigned char *old_sha1, int flags, int *type_p); @@ -232,7 +267,7 @@ enum action_on_err { * Begin a reference transaction. The reference transaction must * be freed by calling ref_transaction_free(). */ -struct ref_transaction *ref_transaction_begin(void); +struct ref_transaction *ref_transaction_begin(struct strbuf *err); /* * The following functions add a reference check or update to a @@ -250,7 +285,7 @@ struct ref_transaction *ref_transaction_begin(void); * it must not have existed beforehand. * Function returns 0 on success and non-zero on failure. A failure to update * means that the transaction as a whole has failed and will need to be - * rolled back. On failure the err buffer will be updated. + * rolled back. */ int ref_transaction_update(struct ref_transaction *transaction, const char *refname, @@ -264,28 +299,34 @@ int ref_transaction_update(struct ref_transaction *transaction, * that the reference should have after the update; it must not be the * null SHA-1. It is verified that the reference does not exist * already. + * Function returns 0 on success and non-zero on failure. A failure to create + * means that the transaction as a whole has failed and will need to be + * rolled back. */ -void ref_transaction_create(struct ref_transaction *transaction, - const char *refname, - const unsigned char *new_sha1, - int flags); +int ref_transaction_create(struct ref_transaction *transaction, + const char *refname, + const unsigned char *new_sha1, + int flags, + struct strbuf *err); /* * Add a reference deletion to transaction. If have_old is true, then * old_sha1 holds the value that the reference should have had before * the update (which must not be the null SHA-1). + * Function returns 0 on success and non-zero on failure. A failure to delete + * means that the transaction as a whole has failed and will need to be + * rolled back. */ -void ref_transaction_delete(struct ref_transaction *transaction, - const char *refname, - const unsigned char *old_sha1, - int flags, int have_old); +int ref_transaction_delete(struct ref_transaction *transaction, + const char *refname, + const unsigned char *old_sha1, + int flags, int have_old, + struct strbuf *err); /* * Commit all of the changes that have been queued in transaction, as * atomically as possible. Return a nonzero value if there is a * problem. - * If err is non-NULL we will add an error string to it to explain why - * the transaction failed. The string does not end in newline. */ int ref_transaction_commit(struct ref_transaction *transaction, const char *msg, struct strbuf *err); diff --git a/remote-curl.c b/remote-curl.c index 0fcf2ce5ff..017ddd9284 100644 --- a/remote-curl.c +++ b/remote-curl.c @@ -623,10 +623,9 @@ static int rpc_service(struct rpc_state *rpc, struct discovery *heads) const char *svc = rpc->service_name; struct strbuf buf = STRBUF_INIT; struct strbuf *preamble = rpc->stdin_preamble; - struct child_process client; + struct child_process client = CHILD_PROCESS_INIT; int err = 0; - memset(&client, 0, sizeof(client)); client.in = -1; client.out = -1; client.git_cmd = 1; diff --git a/remote-testsvn.c b/remote-testsvn.c index 686e07d317..48bf6eb93b 100644 --- a/remote-testsvn.c +++ b/remote-testsvn.c @@ -175,7 +175,7 @@ static int cmd_import(const char *line) char *note_msg; unsigned char head_sha1[20]; unsigned int startrev; - struct child_process svndump_proc; + struct child_process svndump_proc = CHILD_PROCESS_INIT; const char *command = "svnrdump"; if (read_ref(private_ref, head_sha1)) @@ -200,7 +200,6 @@ static int cmd_import(const char *line) if(dumpin_fd < 0) die_errno("Couldn't open svn dump file %s.", url); } else { - memset(&svndump_proc, 0, sizeof(struct child_process)); svndump_proc.out = -1; argv_array_push(&svndump_proc.args, command); argv_array_push(&svndump_proc.args, "dump"); @@ -573,15 +573,11 @@ static int do_plain_rerere(struct string_list *rr, int fd) return write_rr(rr, fd); } -static int git_rerere_config(const char *var, const char *value, void *cb) +static void git_rerere_config(void) { - if (!strcmp(var, "rerere.enabled")) - rerere_enabled = git_config_bool(var, value); - else if (!strcmp(var, "rerere.autoupdate")) - rerere_autoupdate = git_config_bool(var, value); - else - return git_default_config(var, value, cb); - return 0; + git_config_get_bool("rerere.enabled", &rerere_enabled); + git_config_get_bool("rerere.autoupdate", &rerere_autoupdate); + git_config(git_default_config, NULL); } static int is_rerere_enabled(void) @@ -606,7 +602,7 @@ int setup_rerere(struct string_list *merge_rr, int flags) { int fd; - git_config(git_rerere_config, NULL); + git_rerere_config(); if (!is_rerere_enabled()) return -1; @@ -699,24 +695,6 @@ static void unlink_rr_item(const char *name) rmdir(git_path("rr-cache/%s", name)); } -struct rerere_gc_config_cb { - int cutoff_noresolve; - int cutoff_resolve; -}; - -static int git_rerere_gc_config(const char *var, const char *value, void *cb) -{ - struct rerere_gc_config_cb *cf = cb; - - if (!strcmp(var, "gc.rerereresolved")) - cf->cutoff_resolve = git_config_int(var, value); - else if (!strcmp(var, "gc.rerereunresolved")) - cf->cutoff_noresolve = git_config_int(var, value); - else - return git_default_config(var, value, cb); - return 0; -} - void rerere_gc(struct string_list *rr) { struct string_list to_remove = STRING_LIST_INIT_DUP; @@ -724,9 +702,12 @@ void rerere_gc(struct string_list *rr) struct dirent *e; int i, cutoff; time_t now = time(NULL), then; - struct rerere_gc_config_cb cf = { 15, 60 }; + int cutoff_noresolve = 15; + int cutoff_resolve = 60; - git_config(git_rerere_gc_config, &cf); + git_config_get_int("gc.rerereresolved", &cutoff_resolve); + git_config_get_int("gc.rerereunresolved", &cutoff_noresolve); + git_config(git_default_config, NULL); dir = opendir(git_path("rr-cache")); if (!dir) die_errno("unable to open rr-cache directory"); @@ -736,12 +717,12 @@ void rerere_gc(struct string_list *rr) then = rerere_last_used_at(e->d_name); if (then) { - cutoff = cf.cutoff_resolve; + cutoff = cutoff_resolve; } else { then = rerere_created_at(e->d_name); if (!then) continue; - cutoff = cf.cutoff_noresolve; + cutoff = cutoff_noresolve; } if (then < now - cutoff * 86400) string_list_append(&to_remove, e->d_name); diff --git a/revision.c b/revision.c index 615535c984..0d3e4171ef 100644 --- a/revision.c +++ b/revision.c @@ -473,7 +473,7 @@ static int rev_compare_tree(struct rev_info *revs, * If we are simplifying by decoration, then the commit * is worth showing if it has a tag pointing at it. */ - if (lookup_decoration(&name_decoration, &commit->object)) + if (get_name_decoration(&commit->object)) return REV_TREE_DIFFERENT; /* * A commit that is not pointed by a tag is uninteresting diff --git a/run-command.c b/run-command.c index 35a3ebf07b..761f0fde40 100644 --- a/run-command.c +++ b/run-command.c @@ -8,6 +8,12 @@ # define SHELL_PATH "/bin/sh" #endif +void child_process_init(struct child_process *child) +{ + memset(child, 0, sizeof(*child)); + argv_array_init(&child->args); +} + struct child_to_clean { pid_t pid; struct child_to_clean *next; @@ -555,31 +561,21 @@ int run_command(struct child_process *cmd) return finish_command(cmd); } -static void prepare_run_command_v_opt(struct child_process *cmd, - const char **argv, - int opt) -{ - memset(cmd, 0, sizeof(*cmd)); - cmd->argv = argv; - cmd->no_stdin = opt & RUN_COMMAND_NO_STDIN ? 1 : 0; - cmd->git_cmd = opt & RUN_GIT_CMD ? 1 : 0; - cmd->stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0; - cmd->silent_exec_failure = opt & RUN_SILENT_EXEC_FAILURE ? 1 : 0; - cmd->use_shell = opt & RUN_USING_SHELL ? 1 : 0; - cmd->clean_on_exit = opt & RUN_CLEAN_ON_EXIT ? 1 : 0; -} - int run_command_v_opt(const char **argv, int opt) { - struct child_process cmd; - prepare_run_command_v_opt(&cmd, argv, opt); - return run_command(&cmd); + return run_command_v_opt_cd_env(argv, opt, NULL, NULL); } int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const char *const *env) { - struct child_process cmd; - prepare_run_command_v_opt(&cmd, argv, opt); + struct child_process cmd = CHILD_PROCESS_INIT; + cmd.argv = argv; + cmd.no_stdin = opt & RUN_COMMAND_NO_STDIN ? 1 : 0; + cmd.git_cmd = opt & RUN_GIT_CMD ? 1 : 0; + cmd.stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0; + cmd.silent_exec_failure = opt & RUN_SILENT_EXEC_FAILURE ? 1 : 0; + cmd.use_shell = opt & RUN_USING_SHELL ? 1 : 0; + cmd.clean_on_exit = opt & RUN_CLEAN_ON_EXIT ? 1 : 0; cmd.dir = dir; cmd.env = env; return run_command(&cmd); @@ -763,14 +759,13 @@ char *find_hook(const char *name) int run_hook_ve(const char *const *env, const char *name, va_list args) { - struct child_process hook; + struct child_process hook = CHILD_PROCESS_INIT; const char *p; p = find_hook(name); if (!p) return 0; - memset(&hook, 0, sizeof(hook)); argv_array_push(&hook.args, p); while ((p = va_arg(args, const char *))) argv_array_push(&hook.args, p); diff --git a/run-command.h b/run-command.h index ea73de309b..1b135d1c96 100644 --- a/run-command.h +++ b/run-command.h @@ -44,6 +44,9 @@ struct child_process { unsigned clean_on_exit:1; }; +#define CHILD_PROCESS_INIT { NULL, ARGV_ARRAY_INIT } +void child_process_init(struct child_process *); + int start_command(struct child_process *); int finish_command(struct child_process *); int run_command(struct child_process *); diff --git a/send-pack.c b/send-pack.c index 6129b0fd8e..8b4cbf049c 100644 --- a/send-pack.c +++ b/send-pack.c @@ -47,7 +47,7 @@ static int pack_objects(int fd, struct ref *refs, struct sha1_array *extra, stru NULL, NULL, }; - struct child_process po; + struct child_process po = CHILD_PROCESS_INIT; int i; i = 4; @@ -59,7 +59,6 @@ static int pack_objects(int fd, struct ref *refs, struct sha1_array *extra, stru argv[i++] = "-q"; if (args->progress) argv[i++] = "--progress"; - memset(&po, 0, sizeof(po)); po.argv = argv; po.in = -1; po.out = args->stateless_rpc ? -1 : fd; diff --git a/sequencer.c b/sequencer.c index 3c060e0547..5e8a207474 100644 --- a/sequencer.c +++ b/sequencer.c @@ -237,23 +237,33 @@ static int error_dirty_index(struct replay_opts *opts) static int fast_forward_to(const unsigned char *to, const unsigned char *from, int unborn, struct replay_opts *opts) { - struct ref_lock *ref_lock; + struct ref_transaction *transaction; struct strbuf sb = STRBUF_INIT; - int ret; + struct strbuf err = STRBUF_INIT; read_cache(); if (checkout_fast_forward(from, to, 1)) exit(128); /* the callee should have complained already */ - ref_lock = lock_any_ref_for_update("HEAD", unborn ? null_sha1 : from, - 0, NULL); - if (!ref_lock) - return error(_("Failed to lock HEAD during fast_forward_to")); strbuf_addf(&sb, "%s: fast-forward", action_name(opts)); - ret = write_ref_sha1(ref_lock, to, sb.buf); + + transaction = ref_transaction_begin(&err); + if (!transaction || + ref_transaction_update(transaction, "HEAD", + to, unborn ? null_sha1 : from, + 0, 1, &err) || + ref_transaction_commit(transaction, sb.buf, &err)) { + ref_transaction_free(transaction); + error("%s", err.buf); + strbuf_release(&sb); + strbuf_release(&err); + return -1; + } strbuf_release(&sb); - return ret; + strbuf_release(&err); + ref_transaction_free(transaction); + return 0; } static int do_recursive_merge(struct commit *base, struct commit *next, diff --git a/sha1_file.c b/sha1_file.c index 95afd20910..c08c0cbea8 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -1923,7 +1923,9 @@ static void *unpack_compressed_entry(struct packed_git *p, git_zstream stream; unsigned char *buffer, *in; - buffer = xmallocz(size); + buffer = xmallocz_gently(size); + if (!buffer) + return NULL; memset(&stream, 0, sizeof(stream)); stream.next_out = buffer; stream.avail_out = size + 1; diff --git a/sha1_name.c b/sha1_name.c index 63ee66fedd..7098b10e3d 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -839,7 +839,7 @@ static int handle_one_ref(const char *path, } if (object->type != OBJ_COMMIT) return 0; - commit_list_insert_by_date((struct commit *)object, list); + commit_list_insert((struct commit *)object, list); return 0; } @@ -1366,6 +1366,7 @@ static int get_sha1_with_context_1(const char *name, if (!only_to_die && namelen > 2 && name[1] == '/') { struct commit_list *list = NULL; for_each_ref(handle_one_ref, &list); + commit_list_sort_by_date(&list); return get_sha1_oneline(name + 2, sha1, list); } if (namelen < 3 || diff --git a/submodule.c b/submodule.c index c3a61e70f9..0690dc50d0 100644 --- a/submodule.c +++ b/submodule.c @@ -433,13 +433,12 @@ static int submodule_needs_pushing(const char *path, const unsigned char sha1[20 return 0; if (for_each_remote_ref_submodule(path, has_remote, NULL) > 0) { - struct child_process cp; + struct child_process cp = CHILD_PROCESS_INIT; const char *argv[] = {"rev-list", NULL, "--not", "--remotes", "-n", "1" , NULL}; struct strbuf buf = STRBUF_INIT; int needs_pushing = 0; argv[1] = sha1_to_hex(sha1); - memset(&cp, 0, sizeof(cp)); cp.argv = argv; cp.env = local_repo_env; cp.git_cmd = 1; @@ -524,10 +523,9 @@ static int push_submodule(const char *path) return 1; if (for_each_remote_ref_submodule(path, has_remote, NULL) > 0) { - struct child_process cp; + struct child_process cp = CHILD_PROCESS_INIT; const char *argv[] = {"push", NULL}; - memset(&cp, 0, sizeof(cp)); cp.argv = argv; cp.env = local_repo_env; cp.git_cmd = 1; @@ -569,12 +567,11 @@ static int is_submodule_commit_present(const char *path, unsigned char sha1[20]) if (!add_submodule_odb(path) && lookup_commit_reference(sha1)) { /* Even if the submodule is checked out and the commit is * present, make sure it is reachable from a ref. */ - struct child_process cp; + struct child_process cp = CHILD_PROCESS_INIT; const char *argv[] = {"rev-list", "-n", "1", NULL, "--not", "--all", NULL}; struct strbuf buf = STRBUF_INIT; argv[3] = sha1_to_hex(sha1); - memset(&cp, 0, sizeof(cp)); cp.argv = argv; cp.env = local_repo_env; cp.git_cmd = 1; @@ -695,7 +692,7 @@ int fetch_populated_submodules(const struct argv_array *options, int quiet) { int i, result = 0; - struct child_process cp; + struct child_process cp = CHILD_PROCESS_INIT; struct argv_array argv = ARGV_ARRAY_INIT; struct string_list_item *name_for_path; const char *work_tree = get_git_work_tree(); @@ -711,7 +708,6 @@ int fetch_populated_submodules(const struct argv_array *options, argv_array_push(&argv, "--recurse-submodules-default"); /* default value, "--submodule-prefix" and its value are added later */ - memset(&cp, 0, sizeof(cp)); cp.env = local_repo_env; cp.git_cmd = 1; cp.no_stdin = 1; @@ -794,7 +790,7 @@ out: unsigned is_submodule_modified(const char *path, int ignore_untracked) { ssize_t len; - struct child_process cp; + struct child_process cp = CHILD_PROCESS_INIT; const char *argv[] = { "status", "--porcelain", @@ -821,7 +817,6 @@ unsigned is_submodule_modified(const char *path, int ignore_untracked) if (ignore_untracked) argv[2] = "-uno"; - memset(&cp, 0, sizeof(cp)); cp.argv = argv; cp.env = local_repo_env; cp.git_cmd = 1; @@ -862,7 +857,7 @@ unsigned is_submodule_modified(const char *path, int ignore_untracked) int submodule_uses_gitfile(const char *path) { - struct child_process cp; + struct child_process cp = CHILD_PROCESS_INIT; const char *argv[] = { "submodule", "foreach", @@ -883,7 +878,6 @@ int submodule_uses_gitfile(const char *path) strbuf_release(&buf); /* Now test that all nested submodules use a gitfile too */ - memset(&cp, 0, sizeof(cp)); cp.argv = argv; cp.env = local_repo_env; cp.git_cmd = 1; @@ -901,7 +895,7 @@ int ok_to_remove_submodule(const char *path) { struct stat st; ssize_t len; - struct child_process cp; + struct child_process cp = CHILD_PROCESS_INIT; const char *argv[] = { "status", "--porcelain", @@ -918,7 +912,6 @@ int ok_to_remove_submodule(const char *path) if (!submodule_uses_gitfile(path)) return 0; - memset(&cp, 0, sizeof(cp)); cp.argv = argv; cp.env = local_repo_env; cp.git_cmd = 1; diff --git a/t/t0090-cache-tree.sh b/t/t0090-cache-tree.sh index 6c33e28ee8..f9648a8642 100755 --- a/t/t0090-cache-tree.sh +++ b/t/t0090-cache-tree.sh @@ -8,7 +8,7 @@ cache-tree extension. . ./test-lib.sh cmp_cache_tree () { - test-dump-cache-tree >actual && + test-dump-cache-tree | sed -e '/#(ref)/d' >actual && sed "s/$_x40/SHA/" <actual >filtered && test_cmp "$1" filtered } @@ -16,15 +16,40 @@ cmp_cache_tree () { # We don't bother with actually checking the SHA1: # test-dump-cache-tree already verifies that all existing data is # correct. -test_shallow_cache_tree () { - printf "SHA (%d entries, 0 subtrees)\n" $(git ls-files|wc -l) >expect && +generate_expected_cache_tree_rec () { + dir="$1${1:+/}" && + parent="$2" && + # ls-files might have foo/bar, foo/bar/baz, and foo/bar/quux + # We want to count only foo because it's the only direct child + subtrees=$(git ls-files|grep /|cut -d / -f 1|uniq) && + subtree_count=$(echo "$subtrees"|awk '$1 {++c} END {print c}') && + entries=$(git ls-files|wc -l) && + printf "SHA $dir (%d entries, %d subtrees)\n" "$entries" "$subtree_count" && + for subtree in $subtrees + do + cd "$subtree" + generate_expected_cache_tree_rec "$dir$subtree" "$dir" || return 1 + cd .. + done && + dir=$parent +} + +generate_expected_cache_tree () { + ( + generate_expected_cache_tree_rec + ) +} + +test_cache_tree () { + generate_expected_cache_tree >expect && cmp_cache_tree expect } test_invalid_cache_tree () { - echo "invalid (0 subtrees)" >expect && - printf "SHA #(ref) (%d entries, 0 subtrees)\n" $(git ls-files|wc -l) >>expect && - cmp_cache_tree expect + printf "invalid %s ()\n" "" "$@" >expect && + test-dump-cache-tree | + sed -n -e "s/[0-9]* subtrees//" -e '/#(ref)/d' -e '/^invalid /p' >actual && + test_cmp expect actual } test_no_cache_tree () { @@ -32,26 +57,59 @@ test_no_cache_tree () { cmp_cache_tree expect } -test_expect_failure 'initial commit has cache-tree' ' +test_expect_success 'initial commit has cache-tree' ' test_commit foo && - test_shallow_cache_tree + test_cache_tree ' test_expect_success 'read-tree HEAD establishes cache-tree' ' git read-tree HEAD && - test_shallow_cache_tree + test_cache_tree ' test_expect_success 'git-add invalidates cache-tree' ' test_when_finished "git reset --hard; git read-tree HEAD" && - echo "I changed this file" > foo && + echo "I changed this file" >foo && git add foo && test_invalid_cache_tree ' +test_expect_success 'git-add in subdir invalidates cache-tree' ' + test_when_finished "git reset --hard; git read-tree HEAD" && + mkdir dirx && + echo "I changed this file" >dirx/foo && + git add dirx/foo && + test_invalid_cache_tree +' + +cat >before <<\EOF +SHA (3 entries, 2 subtrees) +SHA dir1/ (1 entries, 0 subtrees) +SHA dir2/ (1 entries, 0 subtrees) +EOF + +cat >expect <<\EOF +invalid (2 subtrees) +invalid dir1/ (0 subtrees) +SHA dir2/ (1 entries, 0 subtrees) +EOF + +test_expect_success 'git-add in subdir does not invalidate sibling cache-tree' ' + git tag no-children && + test_when_finished "git reset --hard no-children; git read-tree HEAD" && + mkdir dir1 dir2 && + test_commit dir1/a && + test_commit dir2/b && + echo "I changed this file" >dir1/a && + cmp_cache_tree before && + echo "I changed this file" >dir1/a && + git add dir1/a && + cmp_cache_tree expect +' + test_expect_success 'update-index invalidates cache-tree' ' test_when_finished "git reset --hard; git read-tree HEAD" && - echo "I changed this file" > foo && + echo "I changed this file" >foo && git update-index --add foo && test_invalid_cache_tree ' @@ -59,7 +117,7 @@ test_expect_success 'update-index invalidates cache-tree' ' test_expect_success 'write-tree establishes cache-tree' ' test-scrap-cache-tree && git write-tree && - test_shallow_cache_tree + test_cache_tree ' test_expect_success 'test-scrap-cache-tree works' ' @@ -70,24 +128,94 @@ test_expect_success 'test-scrap-cache-tree works' ' test_expect_success 'second commit has cache-tree' ' test_commit bar && - test_shallow_cache_tree + test_cache_tree +' + +test_expect_success 'commit --interactive gives cache-tree on partial commit' ' + cat <<-\EOT >foo.c && + int foo() + { + return 42; + } + int bar() + { + return 42; + } + EOT + git add foo.c && + test_invalid_cache_tree && + git commit -m "add a file" && + test_cache_tree && + cat <<-\EOT >foo.c && + int foo() + { + return 43; + } + int bar() + { + return 44; + } + EOT + (echo p; echo 1; echo; echo s; echo n; echo y; echo q) | + git commit --interactive -m foo && + test_cache_tree +' + +test_expect_success 'commit in child dir has cache-tree' ' + mkdir dir && + >dir/child.t && + git add dir/child.t && + git commit -m dir/child.t && + test_cache_tree ' test_expect_success 'reset --hard gives cache-tree' ' test-scrap-cache-tree && git reset --hard && - test_shallow_cache_tree + test_cache_tree ' test_expect_success 'reset --hard without index gives cache-tree' ' rm -f .git/index && git reset --hard && - test_shallow_cache_tree + test_cache_tree ' -test_expect_failure 'checkout gives cache-tree' ' +test_expect_success 'checkout gives cache-tree' ' + git tag current && git checkout HEAD^ && - test_shallow_cache_tree + test_cache_tree +' + +test_expect_success 'checkout -b gives cache-tree' ' + git checkout current && + git checkout -b prev HEAD^ && + test_cache_tree +' + +test_expect_success 'checkout -B gives cache-tree' ' + git checkout current && + git checkout -B prev HEAD^ && + test_cache_tree +' + +test_expect_success 'partial commit gives cache-tree' ' + git checkout -b partial no-children && + test_commit one && + test_commit two && + echo "some change" >one.t && + git add one.t && + echo "some other change" >two.t && + git commit two.t -m partial && + test_cache_tree +' + +test_expect_success 'no phantom error when switching trees' ' + mkdir newdir && + >newdir/one && + git add newdir/one && + git checkout 2>errors && + ! test -s errors ' test_done diff --git a/t/t1050-large.sh b/t/t1050-large.sh index aea493646e..05a1e1d270 100755 --- a/t/t1050-large.sh +++ b/t/t1050-large.sh @@ -112,6 +112,20 @@ test_expect_success 'diff --raw' ' git diff --raw HEAD^ ' +test_expect_success 'diff --stat' ' + git diff --stat HEAD^ HEAD +' + +test_expect_success 'diff' ' + git diff HEAD^ HEAD >actual && + grep "Binary files.*differ" actual +' + +test_expect_success 'diff --cached' ' + git diff --cached HEAD^ >actual && + grep "Binary files.*differ" actual +' + test_expect_success 'hash-object' ' git hash-object large1 ' @@ -163,4 +177,10 @@ test_expect_success 'zip achiving, deflate' ' git archive --format=zip HEAD >/dev/null ' +test_expect_success 'fsck' ' + test_must_fail git fsck 2>err && + n=$(grep "error: attempting to allocate .* over limit" err | wc -l) && + test "$n" -gt 1 +' + test_done diff --git a/t/t1308-config-set.sh b/t/t1308-config-set.sh index 7fdf840b00..ea0bce2dc6 100755 --- a/t/t1308-config-set.sh +++ b/t/t1308-config-set.sh @@ -119,6 +119,16 @@ test_expect_success 'find integer value for a key' ' check_config get_int lamb.chop 65 ' +test_expect_success 'find string value for a key' ' + check_config get_string case.baz hask && + check_config expect_code 1 get_string case.ba "Value not found for \"case.ba\"" +' + +test_expect_success 'check line error when NULL string is queried' ' + test_expect_code 128 test-config get_string case.foo 2>result && + test_i18ngrep "fatal: .*case\.foo.*\.git/config.*line 7" result +' + test_expect_success 'find integer if value is non parse-able' ' check_config expect_code 128 get_int lamb.head ' @@ -197,4 +207,15 @@ test_expect_success 'proper error on error in custom config files' ' test_cmp expect actual ' +test_expect_success 'check line errors for malformed values' ' + mv .git/config .git/config.old && + test_when_finished "mv .git/config.old .git/config" && + cat >.git/config <<-\EOF && + [alias] + br + EOF + test_expect_code 128 git br 2>result && + test_i18ngrep "fatal: .*alias\.br.*\.git/config.*line 2" result +' + test_done diff --git a/t/t3210-pack-refs.sh b/t/t3210-pack-refs.sh index 1a2080e3dc..3a017bf437 100755 --- a/t/t3210-pack-refs.sh +++ b/t/t3210-pack-refs.sh @@ -151,4 +151,11 @@ test_expect_success 'delete ref while another dangling packed ref' ' test_cmp /dev/null result ' +test_expect_success 'pack ref directly below refs/' ' + git update-ref refs/top HEAD && + git pack-refs --all --prune && + grep refs/top .git/packed-refs && + test_path_is_missing .git/refs/top +' + test_done diff --git a/t/t4055-diff-context.sh b/t/t4055-diff-context.sh index cd0454356a..741e0803c1 100755 --- a/t/t4055-diff-context.sh +++ b/t/t4055-diff-context.sh @@ -79,7 +79,7 @@ test_expect_success 'non-integer config parsing' ' test_expect_success 'negative integer config parsing' ' git config diff.context -1 && test_must_fail git diff 2>output && - test_i18ngrep "bad config file" output + test_i18ngrep "bad config variable" output ' test_expect_success '-U0 is valid, so is diff.context=0' ' diff --git a/t/t7201-co.sh b/t/t7201-co.sh index 0c9ec0ad44..eae9e5a937 100755 --- a/t/t7201-co.sh +++ b/t/t7201-co.sh @@ -223,6 +223,23 @@ test_expect_success 'checkout --merge --conflict=diff3 <branch>' ' test_cmp two expect ' +test_expect_success 'switch to another branch while carrying a deletion' ' + + git checkout -f master && git reset --hard && git clean -f && + git rm two && + + test_must_fail git checkout simple 2>errs && + test_i18ngrep overwritten errs && + + git checkout --merge simple 2>errs && + test_i18ngrep ! overwritten errs && + git ls-files -u && + test_must_fail git cat-file -t :0:two && + test "$(git cat-file -t :1:two)" = blob && + test "$(git cat-file -t :2:two)" = blob && + test_must_fail git cat-file -t :3:two +' + test_expect_success 'checkout to detach HEAD (with advice declined)' ' git config advice.detachedHead false && diff --git a/test-config.c b/test-config.c index 9dd1b22630..6a77552210 100644 --- a/test-config.c +++ b/test-config.c @@ -16,6 +16,8 @@ * * get_bool -> print bool value for the entered key or die * + * get_string -> print string value for the entered key or die + * * configset_get_value -> returns value with the highest priority for the entered key * from a config_set constructed from files entered as arguments. * @@ -84,6 +86,14 @@ int main(int argc, char **argv) printf("Value not found for \"%s\"\n", argv[2]); goto exit1; } + } else if (argc == 3 && !strcmp(argv[1], "get_string")) { + if (!git_config_get_string_const(argv[2], &v)) { + printf("%s\n", v); + goto exit0; + } else { + printf("Value not found for \"%s\"\n", argv[2]); + goto exit1; + } } else if (!strcmp(argv[1], "configset_get_value")) { for (i = 3; i < argc; i++) { int err; diff --git a/test-dump-cache-tree.c b/test-dump-cache-tree.c index 330ba4f4dd..54c0872fcb 100644 --- a/test-dump-cache-tree.c +++ b/test-dump-cache-tree.c @@ -26,16 +26,16 @@ static int dump_cache_tree(struct cache_tree *it, return 0; if (it->entry_count < 0) { + /* invalid */ dump_one(it, pfx, ""); dump_one(ref, pfx, "#(ref) "); - if (it->subtree_nr != ref->subtree_nr) - errs = 1; } else { dump_one(it, pfx, ""); if (hashcmp(it->sha1, ref->sha1) || ref->entry_count != it->entry_count || ref->subtree_nr != it->subtree_nr) { + /* claims to be valid but is lying */ dump_one(ref, pfx, "#(ref) "); errs = 1; } diff --git a/test-run-command.c b/test-run-command.c index 37918e15f5..89c7de2c60 100644 --- a/test-run-command.c +++ b/test-run-command.c @@ -15,9 +15,7 @@ int main(int argc, char **argv) { - struct child_process proc; - - memset(&proc, 0, sizeof(proc)); + struct child_process proc = CHILD_PROCESS_INIT; if (argc < 3) return 1; diff --git a/test-subprocess.c b/test-subprocess.c index 93525eb7be..56881a0324 100644 --- a/test-subprocess.c +++ b/test-subprocess.c @@ -3,7 +3,7 @@ int main(int argc, char **argv) { - struct child_process cp; + struct child_process cp = CHILD_PROCESS_INIT; int nogit = 0; setup_git_directory_gently(&nogit); @@ -13,7 +13,6 @@ int main(int argc, char **argv) setup_work_tree(); argv++; } - memset(&cp, 0, sizeof(cp)); cp.git_cmd = 1; cp.argv = (const char **)argv + 1; return run_command(&cp); diff --git a/transport-helper.c b/transport-helper.c index 3d8fe7d801..080a7a6ae2 100644 --- a/transport-helper.c +++ b/transport-helper.c @@ -118,7 +118,8 @@ static struct child_process *get_helper(struct transport *transport) if (data->helper) return data->helper; - helper = xcalloc(1, sizeof(*helper)); + helper = xmalloc(sizeof(*helper)); + child_process_init(helper); helper->in = -1; helper->out = -1; helper->err = 0; @@ -395,7 +396,7 @@ static int get_importer(struct transport *transport, struct child_process *fasti struct child_process *helper = get_helper(transport); struct helper_data *data = transport->data; int cat_blob_fd, code; - memset(fastimport, 0, sizeof(*fastimport)); + child_process_init(fastimport); fastimport->in = helper->out; argv_array_push(&fastimport->args, "fast-import"); argv_array_push(&fastimport->args, debug ? "--stats" : "--quiet"); diff --git a/transport.c b/transport.c index 662421bb5e..7388bb87da 100644 --- a/transport.c +++ b/transport.c @@ -201,7 +201,7 @@ static struct ref *get_refs_via_rsync(struct transport *transport, int for_push) { struct strbuf buf = STRBUF_INIT, temp_dir = STRBUF_INIT; struct ref dummy = {NULL}, *tail = &dummy; - struct child_process rsync; + struct child_process rsync = CHILD_PROCESS_INIT; const char *args[5]; int temp_dir_len; @@ -218,7 +218,6 @@ static struct ref *get_refs_via_rsync(struct transport *transport, int for_push) strbuf_addstr(&buf, rsync_url(transport->url)); strbuf_addstr(&buf, "/refs"); - memset(&rsync, 0, sizeof(rsync)); rsync.argv = args; rsync.stdout_to_stderr = 1; args[0] = "rsync"; @@ -263,9 +262,8 @@ static struct ref *get_refs_via_rsync(struct transport *transport, int for_push) static int fetch_objs_via_rsync(struct transport *transport, int nr_objs, struct ref **to_fetch) { - struct child_process rsync; + struct child_process rsync = CHILD_PROCESS_INIT; - memset(&rsync, 0, sizeof(rsync)); rsync.stdout_to_stderr = 1; argv_array_push(&rsync.args, "rsync"); argv_array_push(&rsync.args, (transport->verbose > 1) ? "-rv" : "-r"); @@ -327,7 +325,7 @@ static int rsync_transport_push(struct transport *transport, { struct strbuf buf = STRBUF_INIT, temp_dir = STRBUF_INIT; int result = 0, i; - struct child_process rsync; + struct child_process rsync = CHILD_PROCESS_INIT; const char *args[10]; if (flags & TRANSPORT_PUSH_MIRROR) @@ -338,7 +336,6 @@ static int rsync_transport_push(struct transport *transport, strbuf_addstr(&buf, rsync_url(transport->url)); strbuf_addch(&buf, '/'); - memset(&rsync, 0, sizeof(rsync)); rsync.argv = args; rsync.stdout_to_stderr = 1; i = 0; @@ -1056,7 +1053,7 @@ static int run_pre_push_hook(struct transport *transport, { int ret = 0, x; struct ref *r; - struct child_process proc; + struct child_process proc = CHILD_PROCESS_INIT; struct strbuf buf; const char *argv[4]; @@ -1067,7 +1064,6 @@ static int run_pre_push_hook(struct transport *transport, argv[2] = transport->url; argv[3] = NULL; - memset(&proc, 0, sizeof(proc)); proc.argv = argv; proc.in = -1; diff --git a/unpack-trees.c b/unpack-trees.c index c6aa8fb993..629c658c46 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -1176,7 +1176,8 @@ return_failed: static int reject_merge(const struct cache_entry *ce, struct unpack_trees_options *o) { - return add_rejected_path(o, ERROR_WOULD_OVERWRITE, ce->name); + return o->gently ? -1 : + add_rejected_path(o, ERROR_WOULD_OVERWRITE, ce->name); } static int same(const struct cache_entry *a, const struct cache_entry *b) @@ -1631,7 +1632,7 @@ int threeway_merge(const struct cache_entry * const *stages, /* #14, #14ALT, #2ALT */ if (remote && !df_conflict_head && head_match && !remote_match) { if (index && !same(index, remote) && !same(index, head)) - return o->gently ? -1 : reject_merge(index, o); + return reject_merge(index, o); return merged_entry(remote, index, o); } /* @@ -1639,7 +1640,7 @@ int threeway_merge(const struct cache_entry * const *stages, * make sure that it matches head. */ if (index && !same(index, head)) - return o->gently ? -1 : reject_merge(index, o); + return reject_merge(index, o); if (head) { /* #5ALT, #15 */ @@ -1768,9 +1769,8 @@ int twoway_merge(const struct cache_entry * const *src, else return merged_entry(newtree, current, o); } - return o->gently ? -1 : reject_merge(current, o); - } - else if ((!oldtree && !newtree) || /* 4 and 5 */ + return reject_merge(current, o); + } else if ((!oldtree && !newtree) || /* 4 and 5 */ (!oldtree && newtree && same(current, newtree)) || /* 6 and 7 */ (oldtree && newtree && @@ -1779,26 +1779,15 @@ int twoway_merge(const struct cache_entry * const *src, !same(oldtree, newtree) && /* 18 and 19 */ same(current, newtree))) { return keep_entry(current, o); - } - else if (oldtree && !newtree && same(current, oldtree)) { + } else if (oldtree && !newtree && same(current, oldtree)) { /* 10 or 11 */ return deleted_entry(oldtree, current, o); - } - else if (oldtree && newtree && + } else if (oldtree && newtree && same(current, oldtree) && !same(current, newtree)) { /* 20 or 21 */ return merged_entry(newtree, current, o); - } - else { - /* all other failures */ - if (oldtree) - return o->gently ? -1 : reject_merge(oldtree, o); - if (current) - return o->gently ? -1 : reject_merge(current, o); - if (newtree) - return o->gently ? -1 : reject_merge(newtree, o); - return -1; - } + } else + return reject_merge(current, o); } else if (newtree) { if (oldtree && !o->initial_checkout) { diff --git a/upload-pack.c b/upload-pack.c index 01de944a0a..c789ec0050 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -80,7 +80,7 @@ static int write_one_shallow(const struct commit_graft *graft, void *cb_data) static void create_pack_file(void) { - struct child_process pack_objects; + struct child_process pack_objects = CHILD_PROCESS_INIT; char data[8193], progress[128]; char abort_msg[] = "aborting due to possible repository " "corruption on the remote side."; @@ -108,7 +108,6 @@ static void create_pack_file(void) argv[arg++] = "--include-tag"; argv[arg++] = NULL; - memset(&pack_objects, 0, sizeof(pack_objects)); pack_objects.in = -1; pack_objects.out = -1; pack_objects.err = -1; @@ -167,7 +166,9 @@ static void create_pack_file(void) if (!pollsize) break; - ret = poll(pfd, pollsize, 1000 * keepalive); + ret = poll(pfd, pollsize, + keepalive < 0 ? -1 : 1000 * keepalive); + if (ret < 0) { if (errno != EINTR) { error("poll failed, resuming: %s", @@ -448,7 +449,7 @@ static void check_non_tip(void) static const char *argv[] = { "rev-list", "--stdin", NULL, }; - static struct child_process cmd; + static struct child_process cmd = CHILD_PROCESS_INIT; struct object *o; char namebuf[42]; /* ^ + SHA-1 + LF */ int i; @@ -205,7 +205,7 @@ static int mark_complete(const char *path, const unsigned char *sha1, int flag, struct commit *commit = lookup_commit_reference_gently(sha1, 1); if (commit) { commit->object.flags |= COMPLETE; - commit_list_insert_by_date(commit, &complete); + commit_list_insert(commit, &complete); } return 0; } @@ -251,64 +251,76 @@ void walker_targets_free(int targets, char **target, const char **write_ref) int walker_fetch(struct walker *walker, int targets, char **target, const char **write_ref, const char *write_ref_log_details) { - struct ref_lock **lock = xcalloc(targets, sizeof(struct ref_lock *)); + struct strbuf refname = STRBUF_INIT; + struct strbuf err = STRBUF_INIT; + struct ref_transaction *transaction = NULL; unsigned char *sha1 = xmalloc(targets * 20); - const char *msg; - char *to_free = NULL; - int ret; - int i; + char *msg = NULL; + int i, ret = -1; save_commit_buffer = 0; - for (i = 0; i < targets; i++) { - if (!write_ref || !write_ref[i]) - continue; - - lock[i] = lock_ref_sha1(write_ref[i], NULL); - if (!lock[i]) { - error("Can't lock ref %s", write_ref[i]); - goto unlock_and_fail; + if (write_ref) { + transaction = ref_transaction_begin(&err); + if (!transaction) { + error("%s", err.buf); + goto done; } } - if (!walker->get_recover) + if (!walker->get_recover) { for_each_ref(mark_complete, NULL); + commit_list_sort_by_date(&complete); + } for (i = 0; i < targets; i++) { if (interpret_target(walker, target[i], &sha1[20 * i])) { error("Could not interpret response from server '%s' as something to pull", target[i]); - goto unlock_and_fail; + goto done; } if (process(walker, lookup_unknown_object(&sha1[20 * i]))) - goto unlock_and_fail; + goto done; } if (loop(walker)) - goto unlock_and_fail; - - if (write_ref_log_details) - msg = to_free = xstrfmt("fetch from %s", write_ref_log_details); - else - msg = "fetch (unknown)"; + goto done; + if (!write_ref) { + ret = 0; + goto done; + } + if (write_ref_log_details) { + msg = xstrfmt("fetch from %s", write_ref_log_details); + } else { + msg = NULL; + } for (i = 0; i < targets; i++) { - if (!write_ref || !write_ref[i]) + if (!write_ref[i]) continue; - ret = write_ref_sha1(lock[i], &sha1[20 * i], msg); - lock[i] = NULL; - if (ret) - goto unlock_and_fail; + strbuf_reset(&refname); + strbuf_addf(&refname, "refs/%s", write_ref[i]); + if (ref_transaction_update(transaction, refname.buf, + &sha1[20 * i], NULL, 0, 0, + &err)) { + error("%s", err.buf); + goto done; + } + } + if (ref_transaction_commit(transaction, + msg ? msg : "fetch (unknown)", + &err)) { + error("%s", err.buf); + goto done; } - free(to_free); - - return 0; -unlock_and_fail: - for (i = 0; i < targets; i++) - if (lock[i]) - unlock_ref(lock[i]); - free(to_free); + ret = 0; - return -1; +done: + ref_transaction_free(transaction); + free(msg); + free(sha1); + strbuf_release(&err); + strbuf_release(&refname); + return ret; } void walker_free(struct walker *walker) @@ -9,16 +9,23 @@ static void do_nothing(size_t size) static void (*try_to_free_routine)(size_t size) = do_nothing; -static void memory_limit_check(size_t size) +static int memory_limit_check(size_t size, int gentle) { static int limit = -1; if (limit == -1) { const char *env = getenv("GIT_ALLOC_LIMIT"); limit = env ? atoi(env) * 1024 : 0; } - if (limit && size > limit) - die("attempting to allocate %"PRIuMAX" over limit %d", - (intmax_t)size, limit); + if (limit && size > limit) { + if (gentle) { + error("attempting to allocate %"PRIuMAX" over limit %d", + (intmax_t)size, limit); + return -1; + } else + die("attempting to allocate %"PRIuMAX" over limit %d", + (intmax_t)size, limit); + } + return 0; } try_to_free_t set_try_to_free_routine(try_to_free_t routine) @@ -42,11 +49,12 @@ char *xstrdup(const char *str) return ret; } -void *xmalloc(size_t size) +static void *do_xmalloc(size_t size, int gentle) { void *ret; - memory_limit_check(size); + if (memory_limit_check(size, gentle)) + return NULL; ret = malloc(size); if (!ret && !size) ret = malloc(1); @@ -55,9 +63,16 @@ void *xmalloc(size_t size) ret = malloc(size); if (!ret && !size) ret = malloc(1); - if (!ret) - die("Out of memory, malloc failed (tried to allocate %lu bytes)", - (unsigned long)size); + if (!ret) { + if (!gentle) + die("Out of memory, malloc failed (tried to allocate %lu bytes)", + (unsigned long)size); + else { + error("Out of memory, malloc failed (tried to allocate %lu bytes)", + (unsigned long)size); + return NULL; + } + } } #ifdef XMALLOC_POISON memset(ret, 0xA5, size); @@ -65,16 +80,37 @@ void *xmalloc(size_t size) return ret; } -void *xmallocz(size_t size) +void *xmalloc(size_t size) +{ + return do_xmalloc(size, 0); +} + +static void *do_xmallocz(size_t size, int gentle) { void *ret; - if (unsigned_add_overflows(size, 1)) - die("Data too large to fit into virtual memory space."); - ret = xmalloc(size + 1); - ((char*)ret)[size] = 0; + if (unsigned_add_overflows(size, 1)) { + if (gentle) { + error("Data too large to fit into virtual memory space."); + return NULL; + } else + die("Data too large to fit into virtual memory space."); + } + ret = do_xmalloc(size + 1, gentle); + if (ret) + ((char*)ret)[size] = 0; return ret; } +void *xmallocz(size_t size) +{ + return do_xmallocz(size, 0); +} + +void *xmallocz_gently(size_t size) +{ + return do_xmallocz(size, 1); +} + /* * xmemdupz() allocates (len + 1) bytes of memory, duplicates "len" bytes of * "data" to the allocated memory, zero terminates the allocated memory, @@ -96,7 +132,7 @@ void *xrealloc(void *ptr, size_t size) { void *ret; - memory_limit_check(size); + memory_limit_check(size, 0); ret = realloc(ptr, size); if (!ret && !size) ret = realloc(ptr, 1); @@ -115,7 +151,7 @@ void *xcalloc(size_t nmemb, size_t size) { void *ret; - memory_limit_check(size * nmemb); + memory_limit_check(size * nmemb, 0); ret = calloc(nmemb, size); if (!ret && (!nmemb || !size)) ret = calloc(1, 1); diff --git a/wt-status.c b/wt-status.c index 27da5296be..1bf5d72545 100644 --- a/wt-status.c +++ b/wt-status.c @@ -725,7 +725,7 @@ static void wt_status_print_changed(struct wt_status *s) static void wt_status_print_submodule_summary(struct wt_status *s, int uncommitted) { - struct child_process sm_summary; + struct child_process sm_summary = CHILD_PROCESS_INIT; struct argv_array env = ARGV_ARRAY_INIT; struct argv_array argv = ARGV_ARRAY_INIT; struct strbuf cmd_stdout = STRBUF_INIT; @@ -744,7 +744,6 @@ static void wt_status_print_submodule_summary(struct wt_status *s, int uncommitt if (!uncommitted) argv_array_push(&argv, s->amend ? "HEAD^" : "HEAD"); - memset(&sm_summary, 0, sizeof(sm_summary)); sm_summary.argv = argv.argv; sm_summary.env = env.argv; sm_summary.git_cmd = 1; |
