correct_ws_error
};
+
+enum ws_ignore {
+ ignore_ws_none,
+ ignore_ws_change
+};
+
+/*
+ * 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 SYMLINK_GOES_AWAY 01
+#define 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 */
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;
+
/* These control whitespace errors */
enum ws_error_action ws_error_action;
+ enum ws_ignore ws_ignore_action;
const char *whitespace_option;
int whitespace_error;
int squelch_whitespace_errors;
int applied_after_fixing_ws;
};
-static int newfd = -1;
-
static const char * const apply_usage[] = {
N_("git apply [<options>] [<patch>...]"),
NULL
};
-
-static enum ws_ignore {
- ignore_ws_none,
- ignore_ws_change
-} ws_ignore_action = ignore_ws_none;
-
-
static void parse_whitespace_option(struct apply_state *state, const char *option)
{
if (!option) {
die(_("unrecognized whitespace option '%s'"), option);
}
-static void parse_ignorewhitespace_option(const char *option)
+static void parse_ignorewhitespace_option(struct apply_state *state,
+ const char *option)
{
if (!option || !strcmp(option, "no") ||
!strcmp(option, "false") || !strcmp(option, "never") ||
!strcmp(option, "none")) {
- ws_ignore_action = ignore_ws_none;
+ state->ws_ignore_action = ignore_ws_none;
return;
}
if (!strcmp(option, "change")) {
- ws_ignore_action = ignore_ws_change;
+ state->ws_ignore_action = ignore_ws_change;
return;
}
die(_("unrecognized whitespace ignore option '%s'"), option);
state->ws_error_action = (state->apply ? warn_on_ws_error : nowarn_ws_error);
}
-/*
- * 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.
- */
-static int max_change, max_len;
-
-/*
- * Various "current state", notably line numbers and what
- * file (and how) we're patching right now.. The "is_xxxx"
- * things are flags, where -1 means "don't know yet".
- */
-static int state_linenr = 1;
-
/*
* This represents one "hunk" from a patch, starting with
* "@@ -oldpos,oldlines +newpos,newlines @@" marker. The
struct line *line;
};
-/*
- * Records filenames that have been touched, in order to handle
- * the case where more than one patches touch the same file.
- */
-
-static struct string_list fn_table;
-
static uint32_t hash_line(const char *cp, size_t len)
{
size_t i;
#define TERM_SPACE 1
#define TERM_TAB 2
-static int name_terminate(const char *name, int namelen, int c, int terminate)
+static int name_terminate(int c, int terminate)
{
if (c == ' ' && !(terminate & TERM_SPACE))
return 0;
if (!end && isspace(c)) {
if (c == '\n')
break;
- if (name_terminate(start, line-start, c, terminate))
+ if (name_terminate(c, terminate))
break;
}
line++;
}
}
if (!name)
- die(_("unable to find filename in patch at line %d"), state_linenr);
+ die(_("unable to find filename in patch at line %d"), state->linenr);
}
static int gitdiff_hdrend(struct apply_state *state,
char *another;
if (isnull)
die(_("git apply: bad git-diff - expected /dev/null, got %s on line %d"),
- *name, state_linenr);
+ *name, state->linenr);
another = find_name(state, line, NULL, state->p_value, TERM_TAB);
if (!another || memcmp(another, *name, len + 1))
die((side == DIFF_NEW_NAME) ?
_("git apply: bad git-diff - inconsistent new filename on line %d") :
- _("git apply: bad git-diff - inconsistent old filename on line %d"), state_linenr);
+ _("git apply: bad git-diff - inconsistent old filename on line %d"), state->linenr);
free(another);
} else {
/* expect "/dev/null" */
if (memcmp("/dev/null", line, 9) || line[9] != '\n')
- die(_("git apply: bad git-diff - expected /dev/null on line %d"), state_linenr);
+ die(_("git apply: bad git-diff - expected /dev/null on line %d"), state->linenr);
}
}
line += len;
size -= len;
- state_linenr++;
- for (offset = len ; size > 0 ; offset += len, size -= len, line += len, state_linenr++) {
+ state->linenr++;
+ for (offset = len ; size > 0 ; offset += len, size -= len, line += len, state->linenr++) {
static const struct opentry {
const char *str;
int (*fn)(struct apply_state *, const char *, struct patch *);
patch->is_new = patch->is_delete = -1;
patch->old_mode = patch->new_mode = 0;
patch->old_name = patch->new_name = NULL;
- for (offset = 0; size > 0; offset += len, size -= len, line += len, state_linenr++) {
+ for (offset = 0; size > 0; offset += len, size -= len, line += len, state->linenr++) {
unsigned long nextlen;
len = linelen(line, size);
if (parse_fragment_header(line, len, &dummy) < 0)
continue;
die(_("patch fragment without header at line %d: %.*s"),
- state_linenr, (int)len-1, line);
+ state->linenr, (int)len-1, line);
}
if (size < len + 6)
"git diff header lacks filename information when removing "
"%d leading pathname components (line %d)",
state->p_value),
- state->p_value, state_linenr);
+ state->p_value, state->linenr);
patch->old_name = xstrdup(patch->def_name);
patch->new_name = xstrdup(patch->def_name);
}
if (!patch->is_delete && !patch->new_name)
die("git diff header lacks filename information "
- "(line %d)", state_linenr);
+ "(line %d)", state->linenr);
patch->is_toplevel_relative = 1;
*hdrsize = git_hdr_len;
return offset;
/* Ok, we'll consider it a patch */
parse_traditional_patch(state, line, line+len, patch);
*hdrsize = len + nextlen;
- state_linenr += 2;
+ state->linenr += 2;
return offset;
}
return -1;
{
unsigned result = ws_check(line + 1, len - 1, ws_rule);
- record_ws_error(state, result, line + 1, len - 2, state_linenr);
+ record_ws_error(state, result, line + 1, len - 2, state->linenr);
}
/*
/* Parse the thing.. */
line += len;
size -= len;
- state_linenr++;
+ state->linenr++;
added = deleted = 0;
for (offset = len;
0 < size;
- offset += len, size -= len, line += len, state_linenr++) {
+ offset += len, size -= len, line += len, state->linenr++) {
if (!oldlines && !newlines)
break;
len = linelen(line, size);
int len;
fragment = xcalloc(1, sizeof(*fragment));
- fragment->linenr = state_linenr;
+ fragment->linenr = state->linenr;
len = parse_fragment(state, line, size, patch, fragment);
if (len <= 0)
- die(_("corrupt patch at line %d"), state_linenr);
+ die(_("corrupt patch at line %d"), state->linenr);
fragment->patch = line;
fragment->size = len;
oldlines += fragment->oldlines;
* points at an allocated memory that the caller must free, so
* it is marked as "->free_patch = 1".
*/
-static struct fragment *parse_binary_hunk(char **buf_p,
+static struct fragment *parse_binary_hunk(struct apply_state *state,
+ char **buf_p,
unsigned long *sz_p,
int *status_p,
int *used_p)
else
return NULL;
- state_linenr++;
+ state->linenr++;
buffer += llen;
while (1) {
int byte_length, max_byte_length, newsize;
llen = linelen(buffer, size);
used += llen;
- state_linenr++;
+ state->linenr++;
if (llen == 1) {
/* consume the blank line */
buffer++;
free(data);
*status_p = -1;
error(_("corrupt binary patch at line %d: %.*s"),
- state_linenr-1, llen-1, buffer);
+ state->linenr-1, llen-1, buffer);
return NULL;
}
* -1 in case of error,
* the length of the parsed binary patch otherwise
*/
-static int parse_binary(char *buffer, unsigned long size, struct patch *patch)
+static int parse_binary(struct apply_state *state,
+ char *buffer,
+ unsigned long size,
+ struct patch *patch)
{
/*
* We have read "GIT binary patch\n"; what follows is a line
int status;
int used, used_1;
- forward = parse_binary_hunk(&buffer, &size, &status, &used);
+ forward = parse_binary_hunk(state, &buffer, &size, &status, &used);
if (!forward && !status)
/* there has to be one hunk (forward hunk) */
- return error(_("unrecognized binary patch at line %d"), state_linenr-1);
+ return error(_("unrecognized binary patch at line %d"), state->linenr-1);
if (status)
/* otherwise we already gave an error message */
return status;
- reverse = parse_binary_hunk(&buffer, &size, &status, &used_1);
+ reverse = parse_binary_hunk(state, &buffer, &size, &status, &used_1);
if (reverse)
used += used_1;
else if (status) {
if (llen == sizeof(git_binary) - 1 &&
!memcmp(git_binary, buffer + hd, llen)) {
int used;
- state_linenr++;
- used = parse_binary(buffer + hd + llen,
+ state->linenr++;
+ used = parse_binary(state, buffer + hd + llen,
size - hd - llen, patch);
if (used < 0)
return -1;
int len = strlen(binhdr[i]);
if (len < size - hd &&
!memcmp(binhdr[i], buffer + hd, len)) {
- state_linenr++;
+ state->linenr++;
patch->is_binary = 1;
patchsize = llen;
break;
*/
if ((state->apply || state->check) &&
(!patch->is_binary && !metadata_changes(patch)))
- die(_("patch with only garbage at line %d"), state_linenr);
+ die(_("patch with only garbage at line %d"), state->linenr);
}
return offset + hdrsize + patchsize;
static const char minuses[]=
"----------------------------------------------------------------------";
-static void show_stats(struct patch *patch)
+static void show_stats(struct apply_state *state, struct patch *patch)
{
struct strbuf qname = STRBUF_INIT;
char *cp = patch->new_name ? patch->new_name : patch->old_name;
/*
* "scale" the filename
*/
- max = max_len;
+ max = state->max_len;
if (max > 50)
max = 50;
/*
* scale the add/delete
*/
- max = max + max_change > 70 ? 70 - max : max_change;
+ max = max + state->max_change > 70 ? 70 - max : state->max_change;
add = patch->lines_added;
del = patch->lines_deleted;
- if (max_change > 0) {
- int total = ((add + del) * max + max_change / 2) / max_change;
- add = (add * max + max_change / 2) / max_change;
+ if (state->max_change > 0) {
+ int total = ((add + del) * max + state->max_change / 2) / state->max_change;
+ add = (add * max + state->max_change / 2) / state->max_change;
del = total - add;
}
printf("%5d %.*s%.*s\n", patch->lines_added + patch->lines_deleted,
* fuzzy matching. We collect all the line length information because
* we need it to adjust whitespace if we match.
*/
- if (ws_ignore_action == ignore_ws_change)
+ if (state->ws_ignore_action == ignore_ws_change)
return line_by_line_fuzzy_match(img, preimage, postimage,
try, try_lno, preimage_limit);
return read_blob_object(buf, ce->sha1, ce->ce_mode);
}
-static struct patch *in_fn_table(const char *name)
+static struct patch *in_fn_table(struct apply_state *state, const char *name)
{
struct string_list_item *item;
if (name == NULL)
return NULL;
- item = string_list_lookup(&fn_table, name);
+ item = string_list_lookup(&state->fn_table, name);
if (item != NULL)
return (struct patch *)item->util;
return patch == PATH_WAS_DELETED;
}
-static void add_to_fn_table(struct patch *patch)
+static void add_to_fn_table(struct apply_state *state, struct patch *patch)
{
struct string_list_item *item;
* file creations and copies
*/
if (patch->new_name != NULL) {
- item = string_list_insert(&fn_table, patch->new_name);
+ item = string_list_insert(&state->fn_table, patch->new_name);
item->util = patch;
}
* later chunks shouldn't patch old names
*/
if ((patch->new_name == NULL) || (patch->is_rename)) {
- item = string_list_insert(&fn_table, patch->old_name);
+ item = string_list_insert(&state->fn_table, patch->old_name);
item->util = PATH_WAS_DELETED;
}
}
-static void prepare_fn_table(struct patch *patch)
+static void prepare_fn_table(struct apply_state *state, struct patch *patch)
{
/*
* store information about incoming file deletion
while (patch) {
if ((patch->new_name == NULL) || (patch->is_rename)) {
struct string_list_item *item;
- item = string_list_insert(&fn_table, patch->old_name);
+ item = string_list_insert(&state->fn_table, patch->old_name);
item->util = PATH_TO_BE_DELETED;
}
patch = patch->next;
return 0;
}
-static struct patch *previous_patch(struct patch *patch, int *gone)
+static struct patch *previous_patch(struct apply_state *state,
+ struct patch *patch,
+ int *gone)
{
struct patch *previous;
if (patch->is_copy || patch->is_rename)
return NULL; /* "git" patches do not depend on the order */
- previous = in_fn_table(patch->old_name);
+ previous = in_fn_table(state, patch->old_name);
if (!previous)
return NULL;
{
if (state->cached || state->check_index) {
if (read_file_or_gitlink(ce, buf))
- return error(_("read of %s failed"), name);
+ return error(_("failed to read %s"), name);
} else if (name) {
if (S_ISGITLINK(expected_mode)) {
if (ce)
return error(_("reading from '%s' beyond a symbolic link"), name);
} else {
if (read_old_data(st, name, buf))
- return error(_("read of %s failed"), name);
+ return error(_("failed to read %s"), name);
}
}
return 0;
struct patch *previous;
int status;
- previous = previous_patch(patch, &status);
+ previous = previous_patch(state, patch, &status);
if (status)
return error(_("path %s has been renamed/deleted"),
patch->old_name);
free_fragment_list(patch->fragments);
patch->fragments = NULL;
} else if (status) {
- return error(_("read of %s failed"), patch->old_name);
+ return error(_("failed to read %s"), patch->old_name);
}
}
}
patch->result = image.buf;
patch->resultsize = image.len;
- add_to_fn_table(patch);
+ add_to_fn_table(state, patch);
free(image.line_allocated);
if (0 < patch->is_delete && patch->resultsize)
return 0;
assert(patch->is_new <= 0);
- previous = previous_patch(patch, &status);
+ previous = previous_patch(state, patch, &status);
if (status)
return error(_("path %s has been renamed/deleted"), old_name);
return 0;
}
-/*
- * 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.
- */
-static struct string_list symlink_changes;
-#define SYMLINK_GOES_AWAY 01
-#define SYMLINK_IN_RESULT 02
-
-static uintptr_t register_symlink_changes(const char *path, uintptr_t what)
+static uintptr_t register_symlink_changes(struct apply_state *state,
+ const char *path,
+ uintptr_t what)
{
struct string_list_item *ent;
- ent = string_list_lookup(&symlink_changes, path);
+ ent = string_list_lookup(&state->symlink_changes, path);
if (!ent) {
- ent = string_list_insert(&symlink_changes, path);
+ ent = string_list_insert(&state->symlink_changes, path);
ent->util = (void *)0;
}
ent->util = (void *)(what | ((uintptr_t)ent->util));
return (uintptr_t)ent->util;
}
-static uintptr_t check_symlink_changes(const char *path)
+static uintptr_t check_symlink_changes(struct apply_state *state, const char *path)
{
struct string_list_item *ent;
- ent = string_list_lookup(&symlink_changes, path);
+ ent = string_list_lookup(&state->symlink_changes, path);
if (!ent)
return 0;
return (uintptr_t)ent->util;
}
-static void prepare_symlink_changes(struct patch *patch)
+static void prepare_symlink_changes(struct apply_state *state, struct patch *patch)
{
for ( ; patch; patch = patch->next) {
if ((patch->old_name && S_ISLNK(patch->old_mode)) &&
(patch->is_rename || patch->is_delete))
/* the symlink at patch->old_name is removed */
- register_symlink_changes(patch->old_name, SYMLINK_GOES_AWAY);
+ register_symlink_changes(state, patch->old_name, SYMLINK_GOES_AWAY);
if (patch->new_name && S_ISLNK(patch->new_mode))
/* the symlink at patch->new_name is created or remains */
- register_symlink_changes(patch->new_name, SYMLINK_IN_RESULT);
+ register_symlink_changes(state, patch->new_name, SYMLINK_IN_RESULT);
}
}
if (!name->len)
break;
name->buf[name->len] = '\0';
- change = check_symlink_changes(name->buf);
+ change = check_symlink_changes(state, name->buf);
if (change & SYMLINK_IN_RESULT)
return 1;
if (change & SYMLINK_GOES_AWAY)
* B and rename from A to B is handled the same way by asking
* was_deleted().
*/
- if ((tpatch = in_fn_table(new_name)) &&
+ if ((tpatch = in_fn_table(state, new_name)) &&
(was_deleted(tpatch) || to_be_deleted(tpatch)))
ok_if_exists = 1;
else
{
int err = 0;
- prepare_symlink_changes(patch);
- prepare_fn_table(patch);
+ prepare_symlink_changes(state, patch);
+ prepare_fn_table(state, patch);
while (patch) {
if (state->apply_verbosely)
say_patch_name(stderr,
discard_index(&result);
}
-static void stat_patch_list(struct patch *patch)
+static void stat_patch_list(struct apply_state *state, struct patch *patch)
{
int files, adds, dels;
files++;
adds += patch->lines_added;
dels += patch->lines_deleted;
- show_stats(patch);
+ show_stats(state, patch);
}
print_stat_summary(stdout, files, adds, dels);
}
}
-static void patch_stats(struct patch *patch)
+static void patch_stats(struct apply_state *state, struct patch *patch)
{
int lines = patch->lines_added + patch->lines_deleted;
- if (lines > max_change)
- max_change = lines;
+ if (lines > state->max_change)
+ state->max_change = lines;
if (patch->old_name) {
int len = quote_c_style(patch->old_name, NULL, NULL, 0);
if (!len)
len = strlen(patch->old_name);
- if (len > max_len)
- max_len = len;
+ if (len > state->max_len)
+ state->max_len = len;
}
if (patch->new_name) {
int len = quote_c_style(patch->new_name, NULL, NULL, 0);
if (!len)
len = strlen(patch->new_name);
- if (len > max_len)
- max_len = len;
+ if (len > state->max_len)
+ state->max_len = len;
}
}
if (state->apply_in_reverse)
reverse_patches(patch);
if (use_patch(state, patch)) {
- patch_stats(patch);
+ patch_stats(state, patch);
*listp = patch;
listp = &patch->next;
}
state->apply = 0;
state->update_index = state->check_index && state->apply;
- if (state->update_index && newfd < 0)
- newfd = hold_locked_index(&lock_file, 1);
+ if (state->update_index && state->newfd < 0)
+ state->newfd = hold_locked_index(state->lock_file, 1);
if (state->check_index) {
if (read_cache() < 0)
build_fake_ancestor(list, state->fake_ancestor);
if (state->diffstat)
- stat_patch_list(list);
+ stat_patch_list(state, list);
if (state->numstat)
numstat_patch_list(state, list);
free_patch_list(list);
strbuf_release(&buf);
- string_list_clear(&fn_table, 0);
+ string_list_clear(&state->fn_table, 0);
return 0;
}
}
static int option_parse_space_change(const struct option *opt,
- const char *arg, int unset)
+ const char *arg, int unset)
{
+ struct apply_state *state = opt->value;
if (unset)
- ws_ignore_action = ignore_ws_none;
+ state->ws_ignore_action = ignore_ws_none;
else
- ws_ignore_action = ignore_ws_change;
+ state->ws_ignore_action = ignore_ws_change;
return 0;
}
return 0;
}
-static void init_apply_state(struct apply_state *state, const char *prefix)
+static void init_apply_state(struct apply_state *state,
+ const char *prefix,
+ struct lock_file *lock_file)
{
memset(state, 0, sizeof(*state));
state->prefix = prefix;
state->prefix_length = state->prefix ? strlen(state->prefix) : 0;
+ state->lock_file = lock_file;
+ state->newfd = -1;
state->apply = 1;
state->line_termination = '\n';
state->p_value = 1;
state->p_context = UINT_MAX;
state->squelch_whitespace_errors = 5;
state->ws_error_action = warn_on_ws_error;
+ state->ws_ignore_action = ignore_ws_none;
+ state->linenr = 1;
+ string_list_init(&state->fn_table, 0);
+ string_list_init(&state->limit_by_name, 0);
+ string_list_init(&state->symlink_changes, 0);
strbuf_init(&state->root, 0);
git_apply_config();
if (apply_default_whitespace)
parse_whitespace_option(state, apply_default_whitespace);
if (apply_default_ignorewhitespace)
- parse_ignorewhitespace_option(apply_default_ignorewhitespace);
+ parse_ignorewhitespace_option(state, apply_default_ignorewhitespace);
}
static void clear_apply_state(struct apply_state *state)
{
string_list_clear(&state->limit_by_name, 0);
+ string_list_clear(&state->symlink_changes, 0);
strbuf_release(&state->root);
+
+ /* &state->fn_table is cleared at the end of apply_patch() */
}
-int cmd_apply(int argc, const char **argv, const char *prefix)
+static void check_apply_state(struct apply_state *state, int force_apply)
+{
+ int is_not_gitdir = !startup_info->have_repository;
+
+ if (state->apply_with_reject && state->threeway)
+ die("--reject and --3way cannot be used together.");
+ if (state->cached && state->threeway)
+ die("--cached and --3way cannot be used together.");
+ if (state->threeway) {
+ if (is_not_gitdir)
+ die(_("--3way outside a repository"));
+ state->check_index = 1;
+ }
+ if (state->apply_with_reject)
+ state->apply = state->apply_verbosely = 1;
+ if (!force_apply && (state->diffstat || state->numstat || state->summary || state->check || state->fake_ancestor))
+ state->apply = 0;
+ if (state->check_index && is_not_gitdir)
+ die(_("--index outside a repository"));
+ if (state->cached) {
+ if (is_not_gitdir)
+ die(_("--cached outside a repository"));
+ state->check_index = 1;
+ }
+ if (state->check_index)
+ state->unsafe_paths = 0;
+ if (!state->lock_file)
+ die("BUG: state->lock_file should not be NULL");
+}
+
+static int apply_all_patches(struct apply_state *state,
+ int argc,
+ const char **argv,
+ int options)
{
int i;
int errs = 0;
- int is_not_gitdir = !startup_info->have_repository;
+ int read_stdin = 1;
+
+ for (i = 0; i < argc; i++) {
+ const char *arg = argv[i];
+ int fd;
+
+ if (!strcmp(arg, "-")) {
+ errs |= apply_patch(state, 0, "<stdin>", options);
+ read_stdin = 0;
+ continue;
+ } else if (0 < state->prefix_length)
+ arg = prefix_filename(state->prefix,
+ state->prefix_length,
+ arg);
+
+ fd = open(arg, O_RDONLY);
+ if (fd < 0)
+ die_errno(_("can't open patch '%s'"), arg);
+ read_stdin = 0;
+ set_default_whitespace_mode(state);
+ errs |= apply_patch(state, fd, arg, options);
+ close(fd);
+ }
+ set_default_whitespace_mode(state);
+ if (read_stdin)
+ errs |= apply_patch(state, 0, "<stdin>", options);
+
+ if (state->whitespace_error) {
+ if (state->squelch_whitespace_errors &&
+ state->squelch_whitespace_errors < state->whitespace_error) {
+ int squelched =
+ state->whitespace_error - state->squelch_whitespace_errors;
+ warning(Q_("squelched %d whitespace error",
+ "squelched %d whitespace errors",
+ squelched),
+ squelched);
+ }
+ if (state->ws_error_action == die_on_ws_error)
+ die(Q_("%d line adds whitespace errors.",
+ "%d lines add whitespace errors.",
+ state->whitespace_error),
+ state->whitespace_error);
+ if (state->applied_after_fixing_ws && state->apply)
+ warning("%d line%s applied after"
+ " fixing whitespace errors.",
+ state->applied_after_fixing_ws,
+ state->applied_after_fixing_ws == 1 ? "" : "s");
+ else if (state->whitespace_error)
+ warning(Q_("%d line adds whitespace errors.",
+ "%d lines add whitespace errors.",
+ state->whitespace_error),
+ state->whitespace_error);
+ }
+
+ if (state->update_index) {
+ if (write_locked_index(&the_index, state->lock_file, COMMIT_LOCK))
+ die(_("Unable to write new index file"));
+ state->newfd = -1;
+ }
+
+ return !!errs;
+}
+
+int cmd_apply(int argc, const char **argv, const char *prefix)
+{
int force_apply = 0;
int options = 0;
- int read_stdin = 1;
+ int ret;
struct apply_state state;
struct option builtin_apply_options[] = {
{ OPTION_CALLBACK, 0, "whitespace", &state, N_("action"),
N_("detect new or modified lines that have whitespace errors"),
0, option_parse_whitespace },
- { OPTION_CALLBACK, 0, "ignore-space-change", NULL, NULL,
+ { OPTION_CALLBACK, 0, "ignore-space-change", &state, NULL,
N_("ignore changes in whitespace when finding context"),
PARSE_OPT_NOARG, option_parse_space_change },
- { OPTION_CALLBACK, 0, "ignore-whitespace", NULL, NULL,
+ { OPTION_CALLBACK, 0, "ignore-whitespace", &state, NULL,
N_("ignore changes in whitespace when finding context"),
PARSE_OPT_NOARG, option_parse_space_change },
OPT_BOOL('R', "reverse", &state.apply_in_reverse,
OPT_END()
};
- init_apply_state(&state, prefix);
+ init_apply_state(&state, prefix, &lock_file);
argc = parse_options(argc, argv, state.prefix, builtin_apply_options,
apply_usage, 0);
- if (state.apply_with_reject && state.threeway)
- die("--reject and --3way cannot be used together.");
- if (state.cached && state.threeway)
- die("--cached and --3way cannot be used together.");
- if (state.threeway) {
- if (is_not_gitdir)
- die(_("--3way outside a repository"));
- state.check_index = 1;
- }
- if (state.apply_with_reject)
- state.apply = state.apply_verbosely = 1;
- if (!force_apply && (state.diffstat || state.numstat || state.summary || state.check || state.fake_ancestor))
- state.apply = 0;
- if (state.check_index && is_not_gitdir)
- die(_("--index outside a repository"));
- if (state.cached) {
- if (is_not_gitdir)
- die(_("--cached outside a repository"));
- state.check_index = 1;
- }
- if (state.check_index)
- state.unsafe_paths = 0;
+ check_apply_state(&state, force_apply);
- for (i = 0; i < argc; i++) {
- const char *arg = argv[i];
- int fd;
-
- if (!strcmp(arg, "-")) {
- errs |= apply_patch(&state, 0, "<stdin>", options);
- read_stdin = 0;
- continue;
- } else if (0 < state.prefix_length)
- arg = prefix_filename(state.prefix,
- state.prefix_length,
- arg);
-
- fd = open(arg, O_RDONLY);
- if (fd < 0)
- die_errno(_("can't open patch '%s'"), arg);
- read_stdin = 0;
- set_default_whitespace_mode(&state);
- errs |= apply_patch(&state, fd, arg, options);
- close(fd);
- }
- set_default_whitespace_mode(&state);
- if (read_stdin)
- errs |= apply_patch(&state, 0, "<stdin>", options);
- if (state.whitespace_error) {
- if (state.squelch_whitespace_errors &&
- state.squelch_whitespace_errors < state.whitespace_error) {
- int squelched =
- state.whitespace_error - state.squelch_whitespace_errors;
- warning(Q_("squelched %d whitespace error",
- "squelched %d whitespace errors",
- squelched),
- squelched);
- }
- if (state.ws_error_action == die_on_ws_error)
- die(Q_("%d line adds whitespace errors.",
- "%d lines add whitespace errors.",
- state.whitespace_error),
- state.whitespace_error);
- if (state.applied_after_fixing_ws && state.apply)
- warning("%d line%s applied after"
- " fixing whitespace errors.",
- state.applied_after_fixing_ws,
- state.applied_after_fixing_ws == 1 ? "" : "s");
- else if (state.whitespace_error)
- warning(Q_("%d line adds whitespace errors.",
- "%d lines add whitespace errors.",
- state.whitespace_error),
- state.whitespace_error);
- }
-
- if (state.update_index) {
- if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
- die(_("Unable to write new index file"));
- }
+ ret = apply_all_patches(&state, argc, argv, options);
clear_apply_state(&state);
- return !!errs;
+ return ret;
}