From: Junio C Hamano Date: Mon, 19 Sep 2016 20:47:17 +0000 (-0700) Subject: Merge branch 'cc/apply-am' X-Git-Tag: v2.11.0-rc0~144 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/81358dc238372793b1590efa149cc1581d1fbd98?ds=inline;hp=-c Merge branch 'cc/apply-am' "git am" has been taught to make an internal call to "git apply"'s innards without spawning the latter as a separate process. * cc/apply-am: (41 commits) builtin/am: use apply API in run_apply() apply: learn to use a different index file apply: pass apply state to build_fake_ancestor() apply: refactor `git apply` option parsing apply: change error_routine when silent usage: add get_error_routine() and get_warn_routine() usage: add set_warn_routine() apply: don't print on stdout in verbosity_silent mode apply: make it possible to silently apply apply: use error_errno() where possible apply: make some parsing functions static again apply: move libified code from builtin/apply.c to apply.{c,h} apply: rename and move opt constants to apply.h builtin/apply: rename option parsing functions builtin/apply: make create_one_file() return -1 on error builtin/apply: make try_create_file() return -1 on error builtin/apply: make write_out_results() return -1 on error builtin/apply: make write_out_one_result() return -1 on error builtin/apply: make create_file() return -1 on error builtin/apply: make add_index_file() return -1 on error ... --- 81358dc238372793b1590efa149cc1581d1fbd98 diff --combined Makefile index 7f184923dd,3230fd0f1a..df4f86bb30 --- a/Makefile +++ b/Makefile @@@ -296,11 -296,6 +296,11 @@@ all: # Define USE_NED_ALLOCATOR if you want to replace the platforms default # memory allocators with the nedmalloc allocator written by Niall Douglas. # +# Define OVERRIDE_STRDUP to override the libc version of strdup(3). +# This is necessary when using a custom allocator in order to avoid +# crashes due to allocation and free working on different 'heaps'. +# It's defined automatically if USE_NED_ALLOCATOR is set. +# # Define NO_REGEX if you have no or inferior regex support in your C library. # # Define HAVE_DEV_TTY if your system can open /dev/tty to interact with the @@@ -375,14 -370,6 +375,14 @@@ # Define HAVE_BSD_SYSCTL if your platform has a BSD-compatible sysctl function. # # Define HAVE_GETDELIM if your system has the getdelim() function. +# +# Define PAGER_ENV to a SP separated VAR=VAL pairs to define +# default environment variables to be passed when a pager is spawned, e.g. +# +# PAGER_ENV = LESS=FRX LV=-c +# +# to say "export LESS=FRX (and LV=-c) if the environment variable +# LESS (and LV) is not set, respectively". GIT-VERSION-FILE: FORCE @$(SHELL_PATH) ./GIT-VERSION-GEN @@@ -696,6 -683,7 +696,7 @@@ LIB_OBJS += abspath. LIB_OBJS += advice.o LIB_OBJS += alias.o LIB_OBJS += alloc.o + LIB_OBJS += apply.o LIB_OBJS += archive.o LIB_OBJS += archive-tar.o LIB_OBJS += archive-zip.o @@@ -768,7 -756,6 +769,7 @@@ LIB_OBJS += merge. LIB_OBJS += merge-blobs.o LIB_OBJS += merge-recursive.o LIB_OBJS += mergesort.o +LIB_OBJS += mru.o LIB_OBJS += name-hash.o LIB_OBJS += notes.o LIB_OBJS += notes-cache.o @@@ -1461,14 -1448,8 +1462,14 @@@ ifdef NATIVE_CRL endif ifdef USE_NED_ALLOCATOR - COMPAT_CFLAGS += -Icompat/nedmalloc - COMPAT_OBJS += compat/nedmalloc/nedmalloc.o + COMPAT_CFLAGS += -Icompat/nedmalloc + COMPAT_OBJS += compat/nedmalloc/nedmalloc.o + OVERRIDE_STRDUP = YesPlease +endif + +ifdef OVERRIDE_STRDUP + COMPAT_CFLAGS += -DOVERRIDE_STRDUP + COMPAT_OBJS += compat/strdup.o endif ifdef GIT_TEST_CMP_USE_COPIED_CONTEXT @@@ -1520,10 -1501,6 +1521,10 @@@ ifeq ($(PYTHON_PATH), NO_PYTHON = NoThanks endif +ifndef PAGER_ENV +PAGER_ENV = LESS=FRX LV=-c +endif + QUIET_SUBDIR0 = +$(MAKE) -C # space to separate -C and subdir QUIET_SUBDIR1 = @@@ -1653,11 -1630,6 +1654,11 @@@ ifdef DEFAULT_HELP_FORMA BASIC_CFLAGS += -DDEFAULT_HELP_FORMAT='"$(DEFAULT_HELP_FORMAT)"' endif +PAGER_ENV_SQ = $(subst ','\'',$(PAGER_ENV)) +PAGER_ENV_CQ = "$(subst ",\",$(subst \,\\,$(PAGER_ENV)))" +PAGER_ENV_CQ_SQ = $(subst ','\'',$(PAGER_ENV_CQ)) +BASIC_CFLAGS += -DPAGER_ENV='$(PAGER_ENV_CQ_SQ)' + ALL_CFLAGS += $(BASIC_CFLAGS) ALL_LDFLAGS += $(BASIC_LDFLAGS) @@@ -1782,7 -1754,7 +1783,7 @@@ common-cmds.h: $(wildcard Documentation SCRIPT_DEFINES = $(SHELL_PATH_SQ):$(DIFF_SQ):$(GIT_VERSION):\ $(localedir_SQ):$(NO_CURL):$(USE_GETTEXT_SCHEME):$(SANE_TOOL_PATH_SQ):\ - $(gitwebdir_SQ):$(PERL_PATH_SQ):$(SANE_TEXT_GREP) + $(gitwebdir_SQ):$(PERL_PATH_SQ):$(SANE_TEXT_GREP):$(PAGER_ENV) define cmd_munge_script $(RM) $@ $@+ && \ sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \ @@@ -1795,7 -1767,6 +1796,7 @@@ -e 's|@@GITWEBDIR@@|$(gitwebdir_SQ)|g' \ -e 's|@@PERL@@|$(PERL_PATH_SQ)|g' \ -e 's|@@SANE_TEXT_GREP@@|$(SANE_TEXT_GREP)|g' \ + -e 's|@@PAGER_ENV@@|$(PAGER_ENV_SQ)|g' \ $@.sh >$@+ endef @@@ -2040,7 -2011,7 +2041,7 @@@ endi ifdef USE_NED_ALLOCATOR compat/nedmalloc/nedmalloc.sp compat/nedmalloc/nedmalloc.o: EXTRA_CPPFLAGS = \ - -DNDEBUG -DOVERRIDE_STRDUP -DREPLACE_SYSTEM_ALLOCATOR + -DNDEBUG -DREPLACE_SYSTEM_ALLOCATOR compat/nedmalloc/nedmalloc.sp: SPARSE_FLAGS += -Wno-non-pointer-null endif @@@ -2203,7 -2174,6 +2204,7 @@@ GIT-BUILD-OPTIONS: FORC @echo NO_PERL=\''$(subst ','\'',$(subst ','\'',$(NO_PERL)))'\' >>$@+ @echo NO_PYTHON=\''$(subst ','\'',$(subst ','\'',$(NO_PYTHON)))'\' >>$@+ @echo NO_UNIX_SOCKETS=\''$(subst ','\'',$(subst ','\'',$(NO_UNIX_SOCKETS)))'\' >>$@+ + @echo PAGER_ENV=\''$(subst ','\'',$(subst ','\'',$(PAGER_ENV)))'\' >>$@+ ifdef TEST_OUTPUT_DIRECTORY @echo TEST_OUTPUT_DIRECTORY=\''$(subst ','\'',$(subst ','\'',$(TEST_OUTPUT_DIRECTORY)))'\' >>$@+ endif diff --combined builtin/am.c index 9daeb27225,1819f4d7bf..ddd23badae --- a/builtin/am.c +++ b/builtin/am.c @@@ -28,7 -28,7 +28,8 @@@ #include "rerere.h" #include "prompt.h" #include "mailinfo.h" + #include "apply.h" +#include "string-list.h" /** * Returns 1 if the file is empty or does not exist, 0 otherwise. @@@ -259,29 -259,38 +260,29 @@@ static int read_state_file(struct strbu } /** - * Reads a KEY=VALUE shell variable assignment from `fp`, returning the VALUE - * as a newly-allocated string. VALUE must be a quoted string, and the KEY must - * match `key`. Returns NULL on failure. - * - * This is used by read_author_script() to read the GIT_AUTHOR_* variables from - * the author-script. + * Take a series of KEY='VALUE' lines where VALUE part is + * sq-quoted, and append at the end of the string list */ -static char *read_shell_var(FILE *fp, const char *key) +static int parse_key_value_squoted(char *buf, struct string_list *list) { - struct strbuf sb = STRBUF_INIT; - const char *str; - - if (strbuf_getline_lf(&sb, fp)) - goto fail; - - if (!skip_prefix(sb.buf, key, &str)) - goto fail; - - if (!skip_prefix(str, "=", &str)) - goto fail; - - strbuf_remove(&sb, 0, str - sb.buf); - - str = sq_dequote(sb.buf); - if (!str) - goto fail; - - return strbuf_detach(&sb, NULL); - -fail: - strbuf_release(&sb); - return NULL; + while (*buf) { + struct string_list_item *item; + char *np; + char *cp = strchr(buf, '='); + if (!cp) + return -1; + np = strchrnul(cp, '\n'); + *cp++ = '\0'; + item = string_list_append(list, buf); + + buf = np + (*np == '\n'); + *np = '\0'; + cp = sq_dequote(cp); + if (!cp) + return -1; + item->util = xstrdup(cp); + } + return 0; } /** @@@ -303,39 -312,44 +304,39 @@@ static int read_author_script(struct am_state *state) { const char *filename = am_path(state, "author-script"); - FILE *fp; + struct strbuf buf = STRBUF_INIT; + struct string_list kv = STRING_LIST_INIT_DUP; + int retval = -1; /* assume failure */ + int fd; assert(!state->author_name); assert(!state->author_email); assert(!state->author_date); - fp = fopen(filename, "r"); - if (!fp) { + fd = open(filename, O_RDONLY); + if (fd < 0) { if (errno == ENOENT) return 0; die_errno(_("could not open '%s' for reading"), filename); } + strbuf_read(&buf, fd, 0); + close(fd); + if (parse_key_value_squoted(buf.buf, &kv)) + goto finish; - state->author_name = read_shell_var(fp, "GIT_AUTHOR_NAME"); - if (!state->author_name) { - fclose(fp); - return -1; - } - - state->author_email = read_shell_var(fp, "GIT_AUTHOR_EMAIL"); - if (!state->author_email) { - fclose(fp); - return -1; - } - - state->author_date = read_shell_var(fp, "GIT_AUTHOR_DATE"); - if (!state->author_date) { - fclose(fp); - return -1; - } - - if (fgetc(fp) != EOF) { - fclose(fp); - return -1; - } - - fclose(fp); - return 0; + if (kv.nr != 3 || + strcmp(kv.items[0].string, "GIT_AUTHOR_NAME") || + strcmp(kv.items[1].string, "GIT_AUTHOR_EMAIL") || + strcmp(kv.items[2].string, "GIT_AUTHOR_DATE")) + goto finish; + state->author_name = kv.items[0].util; + state->author_email = kv.items[1].util; + state->author_date = kv.items[2].util; + retval = 0; +finish: + string_list_clear(&kv, !!retval); + strbuf_release(&buf); + return retval; } /** @@@ -1509,39 -1523,59 +1510,59 @@@ static int parse_mail_rebase(struct am_ */ static int run_apply(const struct am_state *state, const char *index_file) { - struct child_process cp = CHILD_PROCESS_INIT; - - cp.git_cmd = 1; - - if (index_file) - argv_array_pushf(&cp.env_array, "GIT_INDEX_FILE=%s", index_file); + struct argv_array apply_paths = ARGV_ARRAY_INIT; + struct argv_array apply_opts = ARGV_ARRAY_INIT; + struct apply_state apply_state; + int res, opts_left; + static struct lock_file lock_file; + int force_apply = 0; + int options = 0; + + if (init_apply_state(&apply_state, NULL, &lock_file)) + die("BUG: init_apply_state() failed"); + + argv_array_push(&apply_opts, "apply"); + argv_array_pushv(&apply_opts, state->git_apply_opts.argv); + + opts_left = apply_parse_options(apply_opts.argc, apply_opts.argv, + &apply_state, &force_apply, &options, + NULL); + + if (opts_left != 0) + die("unknown option passed through to git apply"); + + if (index_file) { + apply_state.index_file = index_file; + apply_state.cached = 1; + } else + apply_state.check_index = 1; /* * If we are allowed to fall back on 3-way merge, don't give false * errors during the initial attempt. */ - if (state->threeway && !index_file) { - cp.no_stdout = 1; - cp.no_stderr = 1; - } + if (state->threeway && !index_file) + apply_state.apply_verbosity = verbosity_silent; - argv_array_push(&cp.args, "apply"); + if (check_apply_state(&apply_state, force_apply)) + die("BUG: check_apply_state() failed"); - argv_array_pushv(&cp.args, state->git_apply_opts.argv); + argv_array_push(&apply_paths, am_path(state, "patch")); - if (index_file) - argv_array_push(&cp.args, "--cached"); - else - argv_array_push(&cp.args, "--index"); + res = apply_all_patches(&apply_state, apply_paths.argc, apply_paths.argv, options); - argv_array_push(&cp.args, am_path(state, "patch")); + argv_array_clear(&apply_paths); + argv_array_clear(&apply_opts); + clear_apply_state(&apply_state); - if (run_command(&cp)) - return -1; + if (res) + return res; - /* Reload index as git-apply will have modified it. */ - discard_cache(); - read_cache_from(index_file ? index_file : get_index_file()); + if (index_file) { + /* Reload index as apply_all_patches() will have modified it. */ + discard_cache(); + read_cache_from(index_file); + } return 0; } @@@ -1565,19 -1599,48 +1586,19 @@@ static int build_fake_ancestor(const st return 0; } -/** - * Do the three-way merge using fake ancestor, their tree constructed - * from the fake ancestor and the postimage of the patch, and our - * state. - */ -static int run_fallback_merge_recursive(const struct am_state *state, - unsigned char *orig_tree, - unsigned char *our_tree, - unsigned char *their_tree) -{ - struct child_process cp = CHILD_PROCESS_INIT; - int status; - - cp.git_cmd = 1; - - argv_array_pushf(&cp.env_array, "GITHEAD_%s=%.*s", - sha1_to_hex(their_tree), linelen(state->msg), state->msg); - if (state->quiet) - argv_array_push(&cp.env_array, "GIT_MERGE_VERBOSITY=0"); - - argv_array_push(&cp.args, "merge-recursive"); - argv_array_push(&cp.args, sha1_to_hex(orig_tree)); - argv_array_push(&cp.args, "--"); - argv_array_push(&cp.args, sha1_to_hex(our_tree)); - argv_array_push(&cp.args, sha1_to_hex(their_tree)); - - status = run_command(&cp) ? (-1) : 0; - discard_cache(); - read_cache(); - return status; -} - /** * Attempt a threeway merge, using index_path as the temporary index. */ static int fall_back_threeway(const struct am_state *state, const char *index_path) { - unsigned char orig_tree[GIT_SHA1_RAWSZ], their_tree[GIT_SHA1_RAWSZ], - our_tree[GIT_SHA1_RAWSZ]; + struct object_id orig_tree, their_tree, our_tree; + const struct object_id *bases[1] = { &orig_tree }; + struct merge_options o; + struct commit *result; + char *their_tree_name; - if (get_sha1("HEAD", our_tree) < 0) - hashcpy(our_tree, EMPTY_TREE_SHA1_BIN); + if (get_oid("HEAD", &our_tree) < 0) + hashcpy(our_tree.hash, EMPTY_TREE_SHA1_BIN); if (build_fake_ancestor(state, index_path)) return error("could not build fake ancestor"); @@@ -1585,7 -1648,7 +1606,7 @@@ discard_cache(); read_cache_from(index_path); - if (write_index_as_tree(orig_tree, &the_index, index_path, 0, NULL)) + if (write_index_as_tree(orig_tree.hash, &the_index, index_path, 0, NULL)) return error(_("Repository lacks necessary blobs to fall back on 3-way merge.")); say(state, stdout, _("Using index info to reconstruct a base tree...")); @@@ -1601,7 -1664,7 +1622,7 @@@ init_revisions(&rev_info, NULL); rev_info.diffopt.output_format = DIFF_FORMAT_NAME_STATUS; diff_opt_parse(&rev_info.diffopt, &diff_filter_str, 1, rev_info.prefix); - add_pending_sha1(&rev_info, "HEAD", our_tree, 0); + add_pending_sha1(&rev_info, "HEAD", our_tree.hash, 0); diff_setup_done(&rev_info.diffopt); run_diff_index(&rev_info, 1); } @@@ -1610,7 -1673,7 +1631,7 @@@ return error(_("Did you hand edit your patch?\n" "It does not apply to blobs recorded in its index.")); - if (write_index_as_tree(their_tree, &the_index, index_path, 0, NULL)) + if (write_index_as_tree(their_tree.hash, &the_index, index_path, 0, NULL)) return error("could not write tree"); say(state, stdout, _("Falling back to patching base and 3-way merge...")); @@@ -1626,22 -1689,11 +1647,22 @@@ * changes. */ - if (run_fallback_merge_recursive(state, orig_tree, our_tree, their_tree)) { + init_merge_options(&o); + + o.branch1 = "HEAD"; + their_tree_name = xstrfmt("%.*s", linelen(state->msg), state->msg); + o.branch2 = their_tree_name; + + if (state->quiet) + o.verbosity = 0; + + if (merge_recursive_generic(&o, &our_tree, &their_tree, 1, bases, &result)) { rerere(state->allow_rerere_autoupdate); + free(their_tree_name); return error(_("Failed to merge in the changes.")); } + free(their_tree_name); return 0; } @@@ -1809,8 -1861,6 +1830,8 @@@ static void am_run(struct am_state *sta const char *mail = am_path(state, msgnum(state)); int apply_status; + reset_ident_date(); + if (!file_exists(mail)) goto next; @@@ -2209,7 -2259,7 +2230,7 @@@ int cmd_am(int argc, const char **argv int in_progress; const char * const usage[] = { - N_("git am [] [(|)...]"), + N_("git am [] [( | )...]"), N_("git am [] (--continue | --skip | --abort)"), NULL }; diff --combined git-compat-util.h index 68615b1a6a,de04df19a8..37cce0746f --- a/git-compat-util.h +++ b/git-compat-util.h @@@ -436,11 -436,13 +436,14 @@@ static inline int const_error(void return -1; } #define error(...) (error(__VA_ARGS__), const_error()) +#define error_errno(...) (error_errno(__VA_ARGS__), const_error()) #endif extern void set_die_routine(NORETURN_PTR void (*routine)(const char *err, va_list params)); extern void set_error_routine(void (*routine)(const char *err, va_list params)); + extern void (*get_error_routine(void))(const char *err, va_list params); + extern void set_warn_routine(void (*routine)(const char *warn, va_list params)); + extern void (*get_warn_routine(void))(const char *warn, va_list params); extern void set_die_is_recursing_routine(int (*routine)(void)); extern void set_error_handle(FILE *); @@@ -664,22 -666,10 +667,22 @@@ void *gitmemmem(const void *haystack, s const void *needle, size_t needlelen); #endif +#ifdef OVERRIDE_STRDUP +#ifdef strdup +#undef strdup +#endif +#define strdup gitstrdup +char *gitstrdup(const char *s); +#endif + #ifdef NO_GETPAGESIZE #define getpagesize() sysconf(_SC_PAGESIZE) #endif +#ifndef O_CLOEXEC +#define O_CLOEXEC 0 +#endif + #ifdef FREAD_READS_DIRECTORIES #ifdef fopen #undef fopen @@@ -828,7 -818,7 +831,7 @@@ extern FILE *fopen_for_writing(const ch * you can do: * * struct foo *f; - * FLEX_ALLOC_STR(f, name, src); + * FLEXPTR_ALLOC_STR(f, name, src); * * and "name" will point to a block of memory after the struct, which will be * freed along with the struct (but the pointer can be repointed anywhere). diff --combined usage.c index 0efa3faf60,2fd3045535..17f52c1b5c --- a/usage.c +++ b/usage.c @@@ -70,6 -70,21 +70,21 @@@ void set_error_routine(void (*routine)( error_routine = routine; } + void (*get_error_routine(void))(const char *err, va_list params) + { + return error_routine; + } + + void set_warn_routine(void (*routine)(const char *warn, va_list params)) + { + warn_routine = routine; + } + + void (*get_warn_routine(void))(const char *warn, va_list params) + { + return warn_routine; + } + void set_die_is_recursing_routine(int (*routine)(void)) { die_is_recursing = routine; @@@ -148,7 -163,6 +163,7 @@@ void NORETURN die_errno(const char *fmt va_end(params); } +#undef error_errno int error_errno(const char *fmt, ...) { char buf[1024];