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
  ...
This commit is contained in:
Junio C Hamano 2016-09-19 13:47:17 -07:00
commit 81358dc238
9 changed files with 5202 additions and 4880 deletions

View file

@ -696,6 +696,7 @@ LIB_OBJS += abspath.o
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

4988
apply.c Normal file

File diff suppressed because it is too large Load diff

137
apply.h Normal file
View file

@ -0,0 +1,137 @@
#ifndef APPLY_H
#define APPLY_H
enum apply_ws_error_action {
nowarn_ws_error,
warn_on_ws_error,
die_on_ws_error,
correct_ws_error
};
enum apply_ws_ignore {
ignore_ws_none,
ignore_ws_change
};
enum apply_verbosity {
verbosity_silent = -1,
verbosity_normal = 0,
verbosity_verbose = 1
};
/*
* We need to keep track of how symlinks in the preimage are
* manipulated by the patches. A patch to add a/b/c where a/b
* is a symlink should not be allowed to affect the directory
* the symlink points at, but if the same patch removes a/b,
* it is perfectly fine, as the patch removes a/b to make room
* to create a directory a/b so that a/b/c can be created.
*
* See also "struct string_list symlink_changes" in "struct
* apply_state".
*/
#define APPLY_SYMLINK_GOES_AWAY 01
#define APPLY_SYMLINK_IN_RESULT 02
struct apply_state {
const char *prefix;
int prefix_length;
/* These are lock_file related */
struct lock_file *lock_file;
int newfd;
/* These control what gets looked at and modified */
int apply; /* this is not a dry-run */
int cached; /* apply to the index only */
int check; /* preimage must match working tree, don't actually apply */
int check_index; /* preimage must match the indexed version */
int update_index; /* check_index && apply */
/* These control cosmetic aspect of the output */
int diffstat; /* just show a diffstat, and don't actually apply */
int numstat; /* just show a numeric diffstat, and don't actually apply */
int summary; /* just report creation, deletion, etc, and don't actually apply */
/* These boolean parameters control how the apply is done */
int allow_overlap;
int apply_in_reverse;
int apply_with_reject;
int no_add;
int threeway;
int unidiff_zero;
int unsafe_paths;
/* Other non boolean parameters */
const char *index_file;
enum apply_verbosity apply_verbosity;
const char *fake_ancestor;
const char *patch_input_file;
int line_termination;
struct strbuf root;
int p_value;
int p_value_known;
unsigned int p_context;
/* Exclude and include path parameters */
struct string_list limit_by_name;
int has_include;
/* Various "current state" */
int linenr; /* current line number */
struct string_list symlink_changes; /* we have to track symlinks */
/*
* For "diff-stat" like behaviour, we keep track of the biggest change
* we've seen, and the longest filename. That allows us to do simple
* scaling.
*/
int max_change;
int max_len;
/*
* Records filenames that have been touched, in order to handle
* the case where more than one patches touch the same file.
*/
struct string_list fn_table;
/*
* This is to save reporting routines before using
* set_error_routine() or set_warn_routine() to install muting
* routines when in verbosity_silent mode.
*/
void (*saved_error_routine)(const char *err, va_list params);
void (*saved_warn_routine)(const char *warn, va_list params);
/* These control whitespace errors */
enum apply_ws_error_action ws_error_action;
enum apply_ws_ignore ws_ignore_action;
const char *whitespace_option;
int whitespace_error;
int squelch_whitespace_errors;
int applied_after_fixing_ws;
};
extern int apply_parse_options(int argc, const char **argv,
struct apply_state *state,
int *force_apply, int *options,
const char * const *apply_usage);
extern int init_apply_state(struct apply_state *state,
const char *prefix,
struct lock_file *lock_file);
extern void clear_apply_state(struct apply_state *state);
extern int check_apply_state(struct apply_state *state, int force_apply);
/*
* Some aspects of the apply behavior are controlled by the following
* bits in the "options" parameter passed to apply_all_patches().
*/
#define APPLY_OPT_INACCURATE_EOF (1<<0) /* accept inaccurate eof */
#define APPLY_OPT_RECOUNT (1<<1) /* accept inaccurate line count */
extern int apply_all_patches(struct apply_state *state,
int argc,
const char **argv,
int options);
#endif

View file

@ -28,6 +28,7 @@
#include "rerere.h"
#include "prompt.h"
#include "mailinfo.h"
#include "apply.h"
#include "string-list.h"
/**
@ -1509,40 +1510,60 @@ static int parse_mail_rebase(struct am_state *state, const char *mail)
*/
static int run_apply(const struct am_state *state, const char *index_file)
{
struct child_process cp = CHILD_PROCESS_INIT;
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;
cp.git_cmd = 1;
if (init_apply_state(&apply_state, NULL, &lock_file))
die("BUG: init_apply_state() failed");
if (index_file)
argv_array_pushf(&cp.env_array, "GIT_INDEX_FILE=%s", index_file);
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;
if (check_apply_state(&apply_state, force_apply))
die("BUG: check_apply_state() failed");
argv_array_push(&apply_paths, am_path(state, "patch"));
res = apply_all_patches(&apply_state, apply_paths.argc, apply_paths.argv, options);
argv_array_clear(&apply_paths);
argv_array_clear(&apply_opts);
clear_apply_state(&apply_state);
if (res)
return res;
if (index_file) {
/* Reload index as apply_all_patches() will have modified it. */
discard_cache();
read_cache_from(index_file);
}
argv_array_push(&cp.args, "apply");
argv_array_pushv(&cp.args, state->git_apply_opts.argv);
if (index_file)
argv_array_push(&cp.args, "--cached");
else
argv_array_push(&cp.args, "--index");
argv_array_push(&cp.args, am_path(state, "patch"));
if (run_command(&cp))
return -1;
/* Reload index as git-apply will have modified it. */
discard_cache();
read_cache_from(index_file ? index_file : get_index_file());
return 0;
}

File diff suppressed because it is too large Load diff

View file

@ -441,6 +441,9 @@ static inline int const_error(void)
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 *);

View file

@ -68,7 +68,7 @@ test_expect_success C_LOCALE_OUTPUT 'apply detecting corrupt patch correctly' '
sed -e "s/-CIT/xCIT/" <output >broken &&
test_must_fail git apply --stat --summary broken 2>detected &&
detected=$(cat detected) &&
detected=$(expr "$detected" : "fatal.*at line \\([0-9]*\\)\$") &&
detected=$(expr "$detected" : "error.*at line \\([0-9]*\\)\$") &&
detected=$(sed -ne "${detected}p" broken) &&
test "$detected" = xCIT
'
@ -77,7 +77,7 @@ test_expect_success C_LOCALE_OUTPUT 'apply detecting corrupt patch correctly' '
git diff --binary | sed -e "s/-CIT/xCIT/" >broken &&
test_must_fail git apply --stat --summary broken 2>detected &&
detected=$(cat detected) &&
detected=$(expr "$detected" : "fatal.*at line \\([0-9]*\\)\$") &&
detected=$(expr "$detected" : "error.*at line \\([0-9]*\\)\$") &&
detected=$(sed -ne "${detected}p" broken) &&
test "$detected" = xCIT
'

View file

@ -29,7 +29,7 @@ test_expect_success 'try to apply corrupted patch' '
'
test_expect_success 'compare diagnostic; ensure file is still here' '
echo "fatal: git diff header lacks filename information (line 4)" >expected &&
echo "error: git diff header lacks filename information (line 4)" >expected &&
test_path_is_file f &&
test_cmp expected actual
'

15
usage.c
View file

@ -70,6 +70,21 @@ void set_error_routine(void (*routine)(const char *err, va_list params))
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;