*/
#include "cache.h"
+#include "config.h"
#include "blob.h"
#include "delta.h"
#include "diff.h"
git_config(git_default_config, NULL);
}
-int parse_whitespace_option(struct apply_state *state, const char *option)
+static int parse_whitespace_option(struct apply_state *state, const char *option)
{
if (!option) {
state->ws_error_action = warn_on_ws_error;
return error(_("unrecognized whitespace option '%s'"), option);
}
-int parse_ignorewhitespace_option(struct apply_state *state,
- const char *option)
+static int parse_ignorewhitespace_option(struct apply_state *state,
+ const char *option)
{
if (!option || !strcmp(option, "no") ||
!strcmp(option, "false") || !strcmp(option, "never") ||
{
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->fn_table is cleared at the end of apply_patch() */
}
+static void mute_routine(const char *msg, va_list params)
+{
+ /* do nothing */
+}
+
int 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)
- return error("--reject and --3way cannot be used together.");
+ return error(_("--reject and --3way cannot be used together."));
if (state->cached && state->threeway)
- return error("--cached and --3way cannot be used together.");
+ return error(_("--cached and --3way cannot be used together."));
if (state->threeway) {
if (is_not_gitdir)
return error(_("--3way outside a repository"));
state->check_index = 1;
}
- if (state->apply_with_reject)
- state->apply = state->apply_verbosely = 1;
+ if (state->apply_with_reject) {
+ state->apply = 1;
+ if (state->apply_verbosity == verbosity_normal)
+ state->apply_verbosity = verbosity_verbose;
+ }
if (!force_apply && (state->diffstat || state->numstat || state->summary || state->check || state->fake_ancestor))
state->apply = 0;
if (state->check_index && is_not_gitdir)
if (!state->lock_file)
return error("BUG: state->lock_file should not be NULL");
+ if (state->apply_verbosity <= verbosity_silent) {
+ state->saved_error_routine = get_error_routine();
+ state->saved_warn_routine = get_warn_routine();
+ set_error_routine(mute_routine);
+ set_warn_routine(mute_routine);
+ }
+
return 0;
}
unsigned ws_rule;
int lines_added, lines_deleted;
int score;
+ int extension_linenr; /* first line specifying delete/new/rename/copy */
unsigned int is_toplevel_relative:1;
unsigned int inaccurate_eof:1;
unsigned int is_binary:1;
unsigned int recount:1;
unsigned int conflicted_threeway:1;
unsigned int direct_to_threeway:1;
+ unsigned int crlf_in_old:1;
struct fragment *fragments;
char *result;
size_t resultsize;
return find_name_common(state, line, def, p_value, line + len, 0);
}
-static int count_slashes(const char *cp)
-{
- int cnt = 0;
- char ch;
-
- while ((ch = *cp++))
- if (ch == '/')
- cnt++;
- return cnt;
-}
-
/*
* Given the string after "--- " or "+++ ", guess the appropriate
* p_value for the given patch.
* Does it begin with "a/$our-prefix" and such? Then this is
* very likely to apply to our directory.
*/
- if (!strncmp(name, state->prefix, state->prefix_length))
+ if (starts_with(name, state->prefix))
val = count_slashes(state->prefix);
else {
cp++;
- if (!strncmp(cp, state->prefix, state->prefix_length))
+ if (starts_with(cp, state->prefix))
val = count_slashes(state->prefix) + 1;
}
}
}
if (*name) {
- int len = strlen(*name);
char *another;
if (isnull)
return error(_("git apply: bad git-diff - expected /dev/null, got %s on line %d"),
*name, state->linenr);
another = find_name(state, line, NULL, state->p_value, TERM_TAB);
- if (!another || memcmp(another, *name, len + 1)) {
+ if (!another || strcmp(another, *name)) {
free(another);
return error((side == DIFF_NEW_NAME) ?
_("git apply: bad git-diff - inconsistent new filename on line %d") :
DIFF_NEW_NAME);
}
+static int parse_mode_line(const char *line, int linenr, unsigned int *mode)
+{
+ char *end;
+ *mode = strtoul(line, &end, 8);
+ if (end == line || !isspace(*end))
+ return error(_("invalid mode on line %d: %s"), linenr, line);
+ return 0;
+}
+
static int gitdiff_oldmode(struct apply_state *state,
const char *line,
struct patch *patch)
{
- patch->old_mode = strtoul(line, NULL, 8);
- return 0;
+ return parse_mode_line(line, state->linenr, &patch->old_mode);
}
static int gitdiff_newmode(struct apply_state *state,
const char *line,
struct patch *patch)
{
- patch->new_mode = strtoul(line, NULL, 8);
- return 0;
+ return parse_mode_line(line, state->linenr, &patch->new_mode);
}
static int gitdiff_delete(struct apply_state *state,
memcpy(patch->new_sha1_prefix, line, len);
patch->new_sha1_prefix[len] = 0;
if (*ptr == ' ')
- patch->old_mode = strtoul(ptr+1, NULL, 8);
+ return gitdiff_oldmode(state, ptr + 1, patch);
return 0;
}
}
}
+static int check_header_line(struct apply_state *state, struct patch *patch)
+{
+ int extensions = (patch->is_delete == 1) + (patch->is_new == 1) +
+ (patch->is_rename == 1) + (patch->is_copy == 1);
+ if (extensions > 1)
+ return error(_("inconsistent header lines %d and %d"),
+ patch->extension_linenr, state->linenr);
+ if (extensions && !patch->extension_linenr)
+ patch->extension_linenr = state->linenr;
+ return 0;
+}
+
/* Verify that we recognize the lines following a git header */
static int parse_git_header(struct apply_state *state,
const char *line,
res = p->fn(state, line + oplen, patch);
if (res < 0)
return -1;
+ if (check_header_line(state, patch))
+ return -1;
if (res > 0)
return offset;
break;
patch->old_name = xstrdup(patch->def_name);
patch->new_name = xstrdup(patch->def_name);
}
- if (!patch->is_delete && !patch->new_name) {
- error("git diff header lacks filename information "
- "(line %d)", state->linenr);
+ if ((!patch->new_name && !patch->is_delete) ||
+ (!patch->old_name && !patch->is_new)) {
+ error(_("git diff header lacks filename information "
+ "(line %d)"), state->linenr);
return -128;
}
patch->is_toplevel_relative = 1;
return;
err = whitespace_error_string(result);
- fprintf(stderr, "%s:%d: %s.\n%.*s\n",
- state->patch_input_file, linenr, err, len, line);
+ if (state->apply_verbosity > verbosity_silent)
+ fprintf(stderr, "%s:%d: %s.\n%.*s\n",
+ state->patch_input_file, linenr, err, len, line);
free(err);
}
}
/*
+ * Check if the patch has context lines with CRLF or
+ * the patch wants to remove lines with CRLF.
+ */
+static void check_old_for_crlf(struct patch *patch, const char *line, int len)
+{
+ if (len >= 2 && line[len-1] == '\n' && line[len-2] == '\r') {
+ patch->ws_rule |= WS_CR_AT_EOL;
+ patch->crlf_in_old = 1;
+ }
+}
+
+
+/*
* Parse a unified diff. Note that this really needs to parse each
* fragment separately, since the only way to know the difference
* between a "---" that is part of a patch, and a "---" that starts
if (!deleted && !added)
leading++;
trailing++;
+ check_old_for_crlf(patch, line, len);
if (!state->apply_in_reverse &&
state->ws_error_action == correct_ws_error)
check_whitespace(state, line, len, patch->ws_rule);
break;
case '-':
+ if (!state->apply_in_reverse)
+ check_old_for_crlf(patch, line, len);
if (state->apply_in_reverse &&
state->ws_error_action != nowarn_ws_error)
check_whitespace(state, line, len, patch->ws_rule);
trailing = 0;
break;
case '+':
+ if (state->apply_in_reverse)
+ check_old_for_crlf(patch, line, len);
if (!state->apply_in_reverse &&
state->ws_error_action != nowarn_ws_error)
check_whitespace(state, line, len, patch->ws_rule);
return error(_("new file %s depends on old contents"), patch->new_name);
if (0 < patch->is_delete && newlines)
return error(_("deleted file %s still has contents"), patch->old_name);
- if (!patch->is_delete && !newlines && context)
+ if (!patch->is_delete && !newlines && context && state->apply_verbosity > verbosity_silent)
fprintf_ln(stderr,
_("** warning: "
"file %s becomes empty but is not deleted"),
char *old_name = *name;
if (!old_name)
return;
- *name = xstrdup(prefix_filename(state->prefix, state->prefix_length, *name));
+ *name = prefix_filename(state->prefix, *name);
free(old_name);
}
int i;
/* Paths outside are not touched regardless of "--include" */
- if (0 < state->prefix_length) {
- int pathlen = strlen(pathname);
- if (pathlen <= state->prefix_length ||
- memcmp(state->prefix, pathname, state->prefix_length))
+ if (state->prefix && *state->prefix) {
+ const char *rest;
+ if (!skip_prefix(pathname, state->prefix, &rest) || !*rest)
return 0;
}
/* See if it matches any of exclude/include rule */
for (i = 0; i < state->limit_by_name.nr; i++) {
struct string_list_item *it = &state->limit_by_name.items[i];
- if (!wildmatch(it->string, pathname, 0, NULL))
+ if (!wildmatch(it->string, pathname, 0))
return (it->util != NULL);
}
return offset + hdrsize + patchsize;
}
-#define swap(a,b) myswap((a),(b),sizeof(a))
-
-#define myswap(a, b, size) do { \
- unsigned char mytmp[size]; \
- memcpy(mytmp, &a, size); \
- memcpy(&a, &b, size); \
- memcpy(&b, mytmp, size); \
-} while (0)
-
static void reverse_patches(struct patch *p)
{
for (; p; p = p->next) {
struct fragment *frag = p->fragments;
- swap(p->new_name, p->old_name);
- swap(p->new_mode, p->old_mode);
- swap(p->is_new, p->is_delete);
- swap(p->lines_added, p->lines_deleted);
- swap(p->old_sha1_prefix, p->new_sha1_prefix);
+ SWAP(p->new_name, p->old_name);
+ SWAP(p->new_mode, p->old_mode);
+ SWAP(p->is_new, p->is_delete);
+ SWAP(p->lines_added, p->lines_deleted);
+ SWAP(p->old_sha1_prefix, p->new_sha1_prefix);
for (; frag; frag = frag->next) {
- swap(frag->newpos, frag->oldpos);
- swap(frag->newlines, frag->oldlines);
+ SWAP(frag->newpos, frag->oldpos);
+ SWAP(frag->newlines, frag->oldlines);
}
}
}
add, pluses, del, minuses);
}
-static int read_old_data(struct stat *st, const char *path, struct strbuf *buf)
+static int read_old_data(struct stat *st, struct patch *patch,
+ const char *path, struct strbuf *buf)
{
+ enum safe_crlf safe_crlf = patch->crlf_in_old ?
+ SAFE_CRLF_KEEP_CRLF : SAFE_CRLF_RENORMALIZE;
switch (st->st_mode & S_IFMT) {
case S_IFLNK:
if (strbuf_readlink(buf, path, st->st_size) < 0)
case S_IFREG:
if (strbuf_read_file(buf, path, st->st_size) != st->st_size)
return error(_("unable to open or read %s"), path);
- convert_to_git(path, buf->buf, buf->len, buf, 0);
+ /*
+ * "git apply" without "--index/--cached" should never look
+ * at the index; the target file may not have been added to
+ * the index yet, and we may not even be in any Git repository.
+ * Pass NULL to convert_to_git() to stress this; the function
+ * should never look at the index when explicit crlf option
+ * is given.
+ */
+ convert_to_git(NULL, path, buf->buf, buf->len, buf, safe_crlf);
return 0;
default:
return -1;
img->line_allocated = img->line;
}
if (preimage_limit != postimage->nr)
- memmove(img->line + applied_pos + postimage->nr,
- img->line + applied_pos + preimage_limit,
- (img->nr - (applied_pos + preimage_limit)) *
- sizeof(*img->line));
- memcpy(img->line + applied_pos,
- postimage->line,
- postimage->nr * sizeof(*img->line));
+ MOVE_ARRAY(img->line + applied_pos + postimage->nr,
+ img->line + applied_pos + preimage_limit,
+ img->nr - (applied_pos + preimage_limit));
+ COPY_ARRAY(img->line + applied_pos, postimage->line, postimage->nr);
if (!state->allow_overlap)
for (i = 0; i < postimage->nr; i++)
img->line[applied_pos + i].flag |= LINE_PATCHED;
/* Ignore it, we already handled it */
break;
default:
- if (state->apply_verbosely)
+ if (state->apply_verbosity > verbosity_normal)
error(_("invalid start of line: '%c'"), first);
applied_pos = -1;
goto out;
state->apply = 0;
}
- if (state->apply_verbosely && applied_pos != pos) {
+ if (state->apply_verbosity > verbosity_normal && applied_pos != pos) {
int offset = applied_pos - pos;
if (state->apply_in_reverse)
offset = 0 - offset;
* Warn if it was necessary to reduce the number
* of context lines.
*/
- if ((leading != frag->leading) ||
- (trailing != frag->trailing))
+ if ((leading != frag->leading ||
+ trailing != frag->trailing) && state->apply_verbosity > verbosity_silent)
fprintf_ln(stderr, _("Context reduced to (%ld/%ld)"
" to apply fragment at %d"),
leading, trailing, applied_pos+1);
update_image(state, img, applied_pos, &preimage, &postimage);
} else {
- if (state->apply_verbosely)
+ if (state->apply_verbosity > verbosity_normal)
error(_("while searching for:\n%.*s"),
(int)(old - oldlines), oldlines);
}
/* Binary patch is irreversible without the optional second hunk */
if (state->apply_in_reverse) {
if (!fragment->next)
- return error("cannot reverse-apply a binary patch "
- "without the reverse hunk to '%s'",
+ return error(_("cannot reverse-apply a binary patch "
+ "without the reverse hunk to '%s'"),
patch->new_name
? patch->new_name : patch->old_name);
fragment = fragment->next;
struct patch *patch)
{
const char *name = patch->old_name ? patch->old_name : patch->new_name;
- unsigned char sha1[20];
+ struct object_id oid;
/*
* For safety, we require patch index line to contain
*/
if (strlen(patch->old_sha1_prefix) != 40 ||
strlen(patch->new_sha1_prefix) != 40 ||
- get_sha1_hex(patch->old_sha1_prefix, sha1) ||
- get_sha1_hex(patch->new_sha1_prefix, sha1))
- return error("cannot apply binary patch to '%s' "
- "without full index line", name);
+ get_oid_hex(patch->old_sha1_prefix, &oid) ||
+ get_oid_hex(patch->new_sha1_prefix, &oid))
+ return error(_("cannot apply binary patch to '%s' "
+ "without full index line"), name);
if (patch->old_name) {
/*
* See if the old one matches what the patch
* applies to.
*/
- hash_sha1_file(img->buf, img->len, blob_type, sha1);
- if (strcmp(sha1_to_hex(sha1), patch->old_sha1_prefix))
- return error("the patch applies to '%s' (%s), "
- "which does not match the "
- "current contents.",
- name, sha1_to_hex(sha1));
+ hash_sha1_file(img->buf, img->len, blob_type, oid.hash);
+ if (strcmp(oid_to_hex(&oid), patch->old_sha1_prefix))
+ return error(_("the patch applies to '%s' (%s), "
+ "which does not match the "
+ "current contents."),
+ name, oid_to_hex(&oid));
}
else {
/* Otherwise, the old one must be empty. */
if (img->len)
- return error("the patch applies to an empty "
- "'%s' but it is not empty", name);
+ return error(_("the patch applies to an empty "
+ "'%s' but it is not empty"), name);
}
- get_sha1_hex(patch->new_sha1_prefix, sha1);
- if (is_null_sha1(sha1)) {
+ get_oid_hex(patch->new_sha1_prefix, &oid);
+ if (is_null_oid(&oid)) {
clear_image(img);
return 0; /* deletion patch */
}
- if (has_sha1_file(sha1)) {
+ if (has_sha1_file(oid.hash)) {
/* We already have the postimage */
enum object_type type;
unsigned long size;
char *result;
- result = read_sha1_file(sha1, &type, &size);
+ result = read_sha1_file(oid.hash, &type, &size);
if (!result)
- return error("the necessary postimage %s for "
- "'%s' cannot be read",
+ return error(_("the necessary postimage %s for "
+ "'%s' cannot be read"),
patch->new_sha1_prefix, name);
clear_image(img);
img->buf = result;
name);
/* verify that the result matches */
- hash_sha1_file(img->buf, img->len, blob_type, sha1);
- if (strcmp(sha1_to_hex(sha1), patch->new_sha1_prefix))
+ hash_sha1_file(img->buf, img->len, blob_type, oid.hash);
+ if (strcmp(oid_to_hex(&oid), patch->new_sha1_prefix))
return error(_("binary patch to '%s' creates incorrect result (expecting %s, got %s)"),
- name, patch->new_sha1_prefix, sha1_to_hex(sha1));
+ name, patch->new_sha1_prefix, oid_to_hex(&oid));
}
return 0;
return 0;
}
-static int read_blob_object(struct strbuf *buf, const unsigned char *sha1, unsigned mode)
+static int read_blob_object(struct strbuf *buf, const struct object_id *oid, unsigned mode)
{
if (S_ISGITLINK(mode)) {
strbuf_grow(buf, 100);
- strbuf_addf(buf, "Subproject commit %s\n", sha1_to_hex(sha1));
+ strbuf_addf(buf, "Subproject commit %s\n", oid_to_hex(oid));
} else {
enum object_type type;
unsigned long sz;
char *result;
- result = read_sha1_file(sha1, &type, &sz);
+ result = read_sha1_file(oid->hash, &type, &sz);
if (!result)
return -1;
/* XXX read_sha1_file NUL-terminates */
{
if (!ce)
return 0;
- return read_blob_object(buf, ce->sha1, ce->ce_mode);
+ return read_blob_object(buf, &ce->oid, ce->ce_mode);
}
static struct patch *in_fn_table(struct apply_state *state, const char *name)
static int checkout_target(struct index_state *istate,
struct cache_entry *ce, struct stat *st)
{
- struct checkout costate;
+ struct checkout costate = CHECKOUT_INIT;
- memset(&costate, 0, sizeof(costate));
- costate.base_dir = "";
costate.refresh_cache = 1;
costate.istate = istate;
if (checkout_entry(ce, &costate, NULL) || lstat(ce->name, st))
struct strbuf *buf,
const struct cache_entry *ce,
struct stat *st,
+ struct patch *patch,
const char *name,
unsigned expected_mode)
{
} else if (has_symlink_leading_path(name, strlen(name))) {
return error(_("reading from '%s' beyond a symbolic link"), name);
} else {
- if (read_old_data(st, name, buf))
+ if (read_old_data(st, patch, name, buf))
return error(_("failed to read %s"), name);
}
}
/* We have a patched copy in memory; use that. */
strbuf_add(&buf, previous->result, previous->resultsize);
} else {
- status = load_patch_target(state, &buf, ce, st,
+ status = load_patch_target(state, &buf, ce, st, patch,
patch->old_name, patch->old_mode);
if (status < 0)
return status;
static int three_way_merge(struct image *image,
char *path,
- const unsigned char *base,
- const unsigned char *ours,
- const unsigned char *theirs)
+ const struct object_id *base,
+ const struct object_id *ours,
+ const struct object_id *theirs)
{
mmfile_t base_file, our_file, their_file;
mmbuffer_t result = { NULL };
ce = active_cache[pos];
if (lstat(name, &st)) {
if (errno != ENOENT)
- return error(_("%s: %s"), name, strerror(errno));
+ return error_errno("%s", name);
if (checkout_target(&the_index, ce, &st))
return -1;
}
if (verify_index_match(ce, &st))
return error(_("%s: does not match index"), name);
- status = load_patch_target(state, &buf, ce, &st, name, mode);
+ status = load_patch_target(state, &buf, ce, &st, patch, name, mode);
if (status < 0)
return status;
else if (status)
struct stat *st,
const struct cache_entry *ce)
{
- unsigned char pre_sha1[20], post_sha1[20], our_sha1[20];
+ struct object_id pre_oid, post_oid, our_oid;
struct strbuf buf = STRBUF_INIT;
size_t len;
int status;
/* Preimage the patch was prepared for */
if (patch->is_new)
- write_sha1_file("", 0, blob_type, pre_sha1);
- else if (get_sha1(patch->old_sha1_prefix, pre_sha1) ||
- read_blob_object(&buf, pre_sha1, patch->old_mode))
- return error("repository lacks the necessary blob to fall back on 3-way merge.");
+ write_sha1_file("", 0, blob_type, pre_oid.hash);
+ else if (get_sha1(patch->old_sha1_prefix, pre_oid.hash) ||
+ read_blob_object(&buf, &pre_oid, patch->old_mode))
+ return error(_("repository lacks the necessary blob to fall back on 3-way merge."));
- fprintf(stderr, "Falling back to three-way merge...\n");
+ if (state->apply_verbosity > verbosity_silent)
+ fprintf(stderr, _("Falling back to three-way merge...\n"));
img = strbuf_detach(&buf, &len);
prepare_image(&tmp_image, img, len, 1);
clear_image(&tmp_image);
return -1;
}
- /* post_sha1[] is theirs */
- write_sha1_file(tmp_image.buf, tmp_image.len, blob_type, post_sha1);
+ /* post_oid is theirs */
+ write_sha1_file(tmp_image.buf, tmp_image.len, blob_type, post_oid.hash);
clear_image(&tmp_image);
- /* our_sha1[] is ours */
+ /* our_oid is ours */
if (patch->is_new) {
if (load_current(state, &tmp_image, patch))
- return error("cannot read the current contents of '%s'",
+ return error(_("cannot read the current contents of '%s'"),
patch->new_name);
} else {
if (load_preimage(state, &tmp_image, patch, st, ce))
- return error("cannot read the current contents of '%s'",
+ return error(_("cannot read the current contents of '%s'"),
patch->old_name);
}
- write_sha1_file(tmp_image.buf, tmp_image.len, blob_type, our_sha1);
+ write_sha1_file(tmp_image.buf, tmp_image.len, blob_type, our_oid.hash);
clear_image(&tmp_image);
/* in-core three-way merge between post and our using pre as base */
status = three_way_merge(image, patch->new_name,
- pre_sha1, our_sha1, post_sha1);
+ &pre_oid, &our_oid, &post_oid);
if (status < 0) {
- fprintf(stderr, "Failed to fall back on three-way merge...\n");
+ if (state->apply_verbosity > verbosity_silent)
+ fprintf(stderr,
+ _("Failed to fall back on three-way merge...\n"));
return status;
}
if (patch->is_new)
oidclr(&patch->threeway_stage[0]);
else
- hashcpy(patch->threeway_stage[0].hash, pre_sha1);
- hashcpy(patch->threeway_stage[1].hash, our_sha1);
- hashcpy(patch->threeway_stage[2].hash, post_sha1);
- fprintf(stderr, "Applied patch to '%s' with conflicts.\n", patch->new_name);
+ oidcpy(&patch->threeway_stage[0], &pre_oid);
+ oidcpy(&patch->threeway_stage[1], &our_oid);
+ oidcpy(&patch->threeway_stage[2], &post_oid);
+ if (state->apply_verbosity > verbosity_silent)
+ fprintf(stderr,
+ _("Applied patch to '%s' with conflicts.\n"),
+ patch->new_name);
} else {
- fprintf(stderr, "Applied patch to '%s' cleanly.\n", patch->new_name);
+ if (state->apply_verbosity > verbosity_silent)
+ fprintf(stderr,
+ _("Applied patch to '%s' cleanly.\n"),
+ patch->new_name);
}
return 0;
}
} else if (!state->cached) {
stat_ret = lstat(old_name, st);
if (stat_ret && errno != ENOENT)
- return error(_("%s: %s"), old_name, strerror(errno));
+ return error_errno("%s", old_name);
}
if (state->check_index && !previous) {
} else if (stat_ret < 0) {
if (patch->is_new < 0)
goto is_new;
- return error(_("%s: %s"), old_name, strerror(errno));
+ return error_errno("%s", old_name);
}
if (!state->cached && !previous)
is_new:
patch->is_new = 1;
patch->is_delete = 0;
- free(patch->old_name);
- patch->old_name = NULL;
+ FREE_AND_NULL(patch->old_name);
return 0;
}
return 0;
return EXISTS_IN_WORKTREE;
- } else if ((errno != ENOENT) && (errno != ENOTDIR)) {
- return error("%s: %s", new_name, strerror(errno));
+ } else if (!is_missing_file_error(errno)) {
+ return error_errno("%s", new_name);
}
return 0;
}
prepare_fn_table(state, patch);
while (patch) {
int res;
- if (state->apply_verbosely)
+ if (state->apply_verbosity > verbosity_normal)
say_patch_name(stderr,
_("Checking patch %s..."), patch);
res = check_patch(state, patch);
return err;
}
-/* This function tries to read the sha1 from the current index */
-static int get_current_sha1(const char *path, unsigned char *sha1)
+static int read_apply_cache(struct apply_state *state)
+{
+ if (state->index_file)
+ return read_cache_from(state->index_file);
+ else
+ return read_cache();
+}
+
+/* This function tries to read the object name from the current index */
+static int get_current_oid(struct apply_state *state, const char *path,
+ struct object_id *oid)
{
int pos;
- if (read_cache() < 0)
+ if (read_apply_cache(state) < 0)
return -1;
pos = cache_name_pos(path, strlen(path));
if (pos < 0)
return -1;
- hashcpy(sha1, active_cache[pos]->sha1);
+ oidcpy(oid, &active_cache[pos]->oid);
return 0;
}
-static int preimage_sha1_in_gitlink_patch(struct patch *p, unsigned char sha1[20])
+static int preimage_oid_in_gitlink_patch(struct patch *p, struct object_id *oid)
{
/*
* A usable gitlink patch has only one fragment (hunk) that looks like:
(preimage = memchr(hunk->patch, '\n', hunk->size)) != NULL &&
starts_with(++preimage, heading) &&
/* does it record full SHA-1? */
- !get_sha1_hex(preimage + sizeof(heading) - 1, sha1) &&
- preimage[sizeof(heading) + 40 - 1] == '\n' &&
+ !get_oid_hex(preimage + sizeof(heading) - 1, oid) &&
+ preimage[sizeof(heading) + GIT_SHA1_HEXSZ - 1] == '\n' &&
/* does the abbreviated name on the index line agree with it? */
starts_with(preimage + sizeof(heading) - 1, p->old_sha1_prefix))
return 0; /* it all looks fine */
/* we may have full object name on the index line */
- return get_sha1_hex(p->old_sha1_prefix, sha1);
+ return get_oid_hex(p->old_sha1_prefix, oid);
}
/* Build an index that contains the just the files needed for a 3way merge */
-static int build_fake_ancestor(struct patch *list, const char *filename)
+static int build_fake_ancestor(struct apply_state *state, struct patch *list)
{
struct patch *patch;
struct index_state result = { NULL };
* worth showing the new sha1 prefix, but until then...
*/
for (patch = list; patch; patch = patch->next) {
- unsigned char sha1[20];
+ struct object_id oid;
struct cache_entry *ce;
const char *name;
continue;
if (S_ISGITLINK(patch->old_mode)) {
- if (!preimage_sha1_in_gitlink_patch(patch, sha1))
+ if (!preimage_oid_in_gitlink_patch(patch, &oid))
; /* ok, the textual part looks sane */
else
- return error("sha1 information is lacking or "
- "useless for submodule %s", name);
- } else if (!get_sha1_blob(patch->old_sha1_prefix, sha1)) {
+ return error(_("sha1 information is lacking or "
+ "useless for submodule %s"), name);
+ } else if (!get_sha1_blob(patch->old_sha1_prefix, oid.hash)) {
; /* ok */
} else if (!patch->lines_added && !patch->lines_deleted) {
/* mode-only change: update the current */
- if (get_current_sha1(patch->old_name, sha1))
- return error("mode change for %s, which is not "
- "in current HEAD", name);
+ if (get_current_oid(state, patch->old_name, &oid))
+ return error(_("mode change for %s, which is not "
+ "in current HEAD"), name);
} else
- return error("sha1 information is lacking or useless "
- "(%s).", name);
+ return error(_("sha1 information is lacking or useless "
+ "(%s)."), name);
- ce = make_cache_entry(patch->old_mode, sha1, name, 0, 0);
+ ce = make_cache_entry(patch->old_mode, oid.hash, name, 0, 0);
if (!ce)
return error(_("make_cache_entry failed for path '%s'"),
name);
if (add_index_entry(&result, ce, ADD_CACHE_OK_TO_ADD)) {
free(ce);
- return error("Could not add %s to temporary index",
+ return error(_("could not add %s to temporary index"),
name);
}
}
- hold_lock_file_for_update(&lock, filename, LOCK_DIE_ON_ERROR);
+ hold_lock_file_for_update(&lock, state->fake_ancestor, LOCK_DIE_ON_ERROR);
res = write_locked_index(&result, &lock, COMMIT_LOCK);
discard_index(&result);
if (res)
- return error("Could not write temporary index to %s", filename);
+ return error(_("could not write temporary index to %s"),
+ state->fake_ancestor);
return 0;
}
const char *s;
if (!skip_prefix(buf, "Subproject commit ", &s) ||
- get_sha1_hex(s, ce->sha1)) {
+ get_oid_hex(s, &ce->oid)) {
free(ce);
- return error(_("corrupt patch for submodule %s"), path);
+ return error(_("corrupt patch for submodule %s"), path);
}
} else {
if (!state->cached) {
if (lstat(path, &st) < 0) {
free(ce);
- return error(_("unable to stat newly "
- "created file '%s': %s"),
- path, strerror(errno));
+ return error_errno(_("unable to stat newly "
+ "created file '%s'"),
+ path);
}
fill_stat_cache_info(ce, &st);
}
- if (write_sha1_file(buf, size, blob_type, ce->sha1) < 0) {
+ if (write_sha1_file(buf, size, blob_type, ce->oid.hash) < 0) {
free(ce);
return error(_("unable to create backing store "
"for newly created file %s"), path);
ce->ce_mode = create_ce_mode(mode);
ce->ce_flags = create_ce_flags(stage);
ce->ce_namelen = namelen;
- hashcpy(ce->sha1, patch->threeway_stage[stage - 1].hash);
+ oidcpy(&ce->oid, &patch->threeway_stage[stage - 1]);
if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD) < 0) {
free(ce);
return error(_("unable to add cache entry for %s"),
}
if (!cnt) {
- if (state->apply_verbosely)
+ if (state->apply_verbosity > verbosity_normal)
say_patch_name(stderr,
_("Applied patch %s cleanly."), patch);
return 0;
"Applying patch %%s with %d rejects...",
cnt),
cnt);
- say_patch_name(stderr, sb.buf, patch);
+ if (state->apply_verbosity > verbosity_silent)
+ say_patch_name(stderr, sb.buf, patch);
strbuf_release(&sb);
cnt = strlen(patch->new_name);
rej = fopen(namebuf, "w");
if (!rej)
- return error(_("cannot open %s: %s"), namebuf, strerror(errno));
+ return error_errno(_("cannot open %s"), namebuf);
/* Normal git tools never deal with .rej, so do not pretend
* this is a git patch by saying --git or giving extended
frag;
cnt++, frag = frag->next) {
if (!frag->rejected) {
- fprintf_ln(stderr, _("Hunk #%d applied cleanly."), cnt);
+ if (state->apply_verbosity > verbosity_silent)
+ fprintf_ln(stderr, _("Hunk #%d applied cleanly."), cnt);
continue;
}
- fprintf_ln(stderr, _("Rejected hunk #%d."), cnt);
+ if (state->apply_verbosity > verbosity_silent)
+ fprintf_ln(stderr, _("Rejected hunk #%d."), cnt);
fprintf(rej, "%.*s", frag->size, frag->patch);
if (frag->patch[frag->size-1] != '\n')
fputc('\n', rej);
struct string_list_item *item;
string_list_sort(&cpath);
- for_each_string_list_item(item, &cpath)
- fprintf(stderr, "U %s\n", item->string);
+ if (state->apply_verbosity > verbosity_silent) {
+ for_each_string_list_item(item, &cpath)
+ fprintf(stderr, "U %s\n", item->string);
+ }
string_list_clear(&cpath, 0);
rerere(0);
listp = &patch->next;
}
else {
- if (state->apply_verbosely)
+ if (state->apply_verbosity > verbosity_normal)
say_patch_name(stderr, _("Skipped patch '%s'."), patch);
free_patch(patch);
skipped_patch++;
state->apply = 0;
state->update_index = state->check_index && state->apply;
- if (state->update_index && state->newfd < 0)
- state->newfd = hold_locked_index(state->lock_file, 1);
+ if (state->update_index && state->newfd < 0) {
+ if (state->index_file)
+ state->newfd = hold_lock_file_for_update(state->lock_file,
+ state->index_file,
+ LOCK_DIE_ON_ERROR);
+ else
+ state->newfd = hold_locked_index(state->lock_file, LOCK_DIE_ON_ERROR);
+ }
- if (state->check_index && read_cache() < 0) {
+ if (state->check_index && read_apply_cache(state) < 0) {
error(_("unable to read index file"));
res = -128;
goto end;
}
if (state->fake_ancestor &&
- build_fake_ancestor(list, state->fake_ancestor)) {
+ build_fake_ancestor(state, list)) {
res = -128;
goto end;
}
- if (state->diffstat)
+ if (state->diffstat && state->apply_verbosity > verbosity_silent)
stat_patch_list(state, list);
- if (state->numstat)
+ if (state->numstat && state->apply_verbosity > verbosity_silent)
numstat_patch_list(state, list);
- if (state->summary)
+ if (state->summary && state->apply_verbosity > verbosity_silent)
summary_patch_list(list);
end:
return res;
}
-int apply_option_parse_exclude(const struct option *opt,
- const char *arg, int unset)
+static int apply_option_parse_exclude(const struct option *opt,
+ const char *arg, int unset)
{
struct apply_state *state = opt->value;
add_name_limit(state, arg, 1);
return 0;
}
-int apply_option_parse_include(const struct option *opt,
- const char *arg, int unset)
+static int apply_option_parse_include(const struct option *opt,
+ const char *arg, int unset)
{
struct apply_state *state = opt->value;
add_name_limit(state, arg, 0);
return 0;
}
-int apply_option_parse_p(const struct option *opt,
- const char *arg,
- int unset)
+static int apply_option_parse_p(const struct option *opt,
+ const char *arg,
+ int unset)
{
struct apply_state *state = opt->value;
state->p_value = atoi(arg);
return 0;
}
-int apply_option_parse_space_change(const struct option *opt,
- const char *arg, int unset)
+static int apply_option_parse_space_change(const struct option *opt,
+ const char *arg, int unset)
{
struct apply_state *state = opt->value;
if (unset)
return 0;
}
-int apply_option_parse_whitespace(const struct option *opt,
- const char *arg, int unset)
+static int apply_option_parse_whitespace(const struct option *opt,
+ const char *arg, int unset)
{
struct apply_state *state = opt->value;
state->whitespace_option = arg;
return 0;
}
-int apply_option_parse_directory(const struct option *opt,
- const char *arg, int unset)
+static int apply_option_parse_directory(const struct option *opt,
+ const char *arg, int unset)
{
struct apply_state *state = opt->value;
strbuf_reset(&state->root);
for (i = 0; i < argc; i++) {
const char *arg = argv[i];
+ char *to_free = NULL;
int fd;
if (!strcmp(arg, "-")) {
errs |= res;
read_stdin = 0;
continue;
- } else if (0 < state->prefix_length)
- arg = prefix_filename(state->prefix,
- state->prefix_length,
- arg);
+ } else
+ arg = to_free = prefix_filename(state->prefix, arg);
fd = open(arg, O_RDONLY);
if (fd < 0) {
error(_("can't open patch '%s': %s"), arg, strerror(errno));
res = -128;
+ free(to_free);
goto end;
}
read_stdin = 0;
set_default_whitespace_mode(state);
res = apply_patch(state, fd, arg, options);
close(fd);
+ free(to_free);
if (res < 0)
goto end;
errs |= res;
goto end;
}
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");
+ warning(Q_("%d line applied after"
+ " fixing whitespace errors.",
+ "%d lines applied after"
+ " fixing whitespace errors.",
+ state->applied_after_fixing_ws),
+ state->applied_after_fixing_ws);
else if (state->whitespace_error)
warning(Q_("%d line adds whitespace errors.",
"%d lines add whitespace errors.",
state->newfd = -1;
}
- return !!errs;
+ res = !!errs;
end:
if (state->newfd >= 0) {
state->newfd = -1;
}
+ if (state->apply_verbosity <= verbosity_silent) {
+ set_error_routine(state->saved_error_routine);
+ set_warn_routine(state->saved_warn_routine);
+ }
+
+ if (res > -1)
+ return res;
return (res == -1 ? 1 : 128);
}
+
+int apply_parse_options(int argc, const char **argv,
+ struct apply_state *state,
+ int *force_apply, int *options,
+ const char * const *apply_usage)
+{
+ struct option builtin_apply_options[] = {
+ { OPTION_CALLBACK, 0, "exclude", state, N_("path"),
+ N_("don't apply changes matching the given path"),
+ 0, apply_option_parse_exclude },
+ { OPTION_CALLBACK, 0, "include", state, N_("path"),
+ N_("apply changes matching the given path"),
+ 0, apply_option_parse_include },
+ { OPTION_CALLBACK, 'p', NULL, state, N_("num"),
+ N_("remove <num> leading slashes from traditional diff paths"),
+ 0, apply_option_parse_p },
+ OPT_BOOL(0, "no-add", &state->no_add,
+ N_("ignore additions made by the patch")),
+ OPT_BOOL(0, "stat", &state->diffstat,
+ N_("instead of applying the patch, output diffstat for the input")),
+ OPT_NOOP_NOARG(0, "allow-binary-replacement"),
+ OPT_NOOP_NOARG(0, "binary"),
+ OPT_BOOL(0, "numstat", &state->numstat,
+ N_("show number of added and deleted lines in decimal notation")),
+ OPT_BOOL(0, "summary", &state->summary,
+ N_("instead of applying the patch, output a summary for the input")),
+ OPT_BOOL(0, "check", &state->check,
+ N_("instead of applying the patch, see if the patch is applicable")),
+ OPT_BOOL(0, "index", &state->check_index,
+ N_("make sure the patch is applicable to the current index")),
+ OPT_BOOL(0, "cached", &state->cached,
+ N_("apply a patch without touching the working tree")),
+ OPT_BOOL(0, "unsafe-paths", &state->unsafe_paths,
+ N_("accept a patch that touches outside the working area")),
+ OPT_BOOL(0, "apply", force_apply,
+ N_("also apply the patch (use with --stat/--summary/--check)")),
+ OPT_BOOL('3', "3way", &state->threeway,
+ N_( "attempt three-way merge if a patch does not apply")),
+ OPT_FILENAME(0, "build-fake-ancestor", &state->fake_ancestor,
+ N_("build a temporary index based on embedded index information")),
+ /* Think twice before adding "--nul" synonym to this */
+ OPT_SET_INT('z', NULL, &state->line_termination,
+ N_("paths are separated with NUL character"), '\0'),
+ OPT_INTEGER('C', NULL, &state->p_context,
+ N_("ensure at least <n> lines of context match")),
+ { OPTION_CALLBACK, 0, "whitespace", state, N_("action"),
+ N_("detect new or modified lines that have whitespace errors"),
+ 0, apply_option_parse_whitespace },
+ { OPTION_CALLBACK, 0, "ignore-space-change", state, NULL,
+ N_("ignore changes in whitespace when finding context"),
+ PARSE_OPT_NOARG, apply_option_parse_space_change },
+ { OPTION_CALLBACK, 0, "ignore-whitespace", state, NULL,
+ N_("ignore changes in whitespace when finding context"),
+ PARSE_OPT_NOARG, apply_option_parse_space_change },
+ OPT_BOOL('R', "reverse", &state->apply_in_reverse,
+ N_("apply the patch in reverse")),
+ OPT_BOOL(0, "unidiff-zero", &state->unidiff_zero,
+ N_("don't expect at least one line of context")),
+ OPT_BOOL(0, "reject", &state->apply_with_reject,
+ N_("leave the rejected hunks in corresponding *.rej files")),
+ OPT_BOOL(0, "allow-overlap", &state->allow_overlap,
+ N_("allow overlapping hunks")),
+ OPT__VERBOSE(&state->apply_verbosity, N_("be verbose")),
+ OPT_BIT(0, "inaccurate-eof", options,
+ N_("tolerate incorrectly detected missing new-line at the end of file"),
+ APPLY_OPT_INACCURATE_EOF),
+ OPT_BIT(0, "recount", options,
+ N_("do not trust the line counts in the hunk headers"),
+ APPLY_OPT_RECOUNT),
+ { OPTION_CALLBACK, 0, "directory", state, N_("root"),
+ N_("prepend <root> to all filenames"),
+ 0, apply_option_parse_directory },
+ OPT_END()
+ };
+
+ return parse_options(argc, argv, state->prefix, builtin_apply_options, apply_usage, 0);
+}