static int unidiff_zero;
static int p_value = 1;
+static int p_value_known;
static int check_index;
-static int write_index;
+static int update_index;
static int cached;
static int diffstat;
static int numstat;
} new_whitespace = warn_on_whitespace;
static int whitespace_error;
static int squelch_whitespace_errors = 5;
-static int applied_after_stripping;
+static int applied_after_fixing_ws;
static const char *patch_input_file;
static void parse_whitespace_option(const char *option)
unsigned long deflate_origlen;
int lines_added, lines_deleted;
int score;
+ unsigned int is_toplevel_relative:1;
unsigned int inaccurate_eof:1;
unsigned int is_binary:1;
unsigned int is_copy:1;
void *buffer = xmalloc(alloc);
for (;;) {
- int nr = alloc - size;
+ ssize_t nr = alloc - size;
if (nr < 1024) {
alloc += CHUNKSIZE;
buffer = xrealloc(buffer, alloc);
return 1;
}
-static char * find_name(const char *line, char *def, int p_value, int terminate)
+static char *find_name(const char *line, char *def, int p_value, int terminate)
{
int len;
const char *start = line;
return name;
}
+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.
+ */
+static int guess_p_value(const char *nameline)
+{
+ char *name, *cp;
+ int val = -1;
+
+ if (is_dev_null(nameline))
+ return -1;
+ name = find_name(nameline, NULL, 0, TERM_SPACE | TERM_TAB);
+ if (!name)
+ return -1;
+ cp = strchr(name, '/');
+ if (!cp)
+ val = 0;
+ else if (prefix) {
+ /*
+ * Does it begin with "a/$our-prefix" and such? Then this is
+ * very likely to apply to our directory.
+ */
+ if (!strncmp(name, prefix, prefix_length))
+ val = count_slashes(prefix);
+ else {
+ cp++;
+ if (!strncmp(cp, prefix, prefix_length))
+ val = count_slashes(prefix) + 1;
+ }
+ }
+ free(name);
+ return val;
+}
+
/*
* Get the name etc info from the --/+++ lines of a traditional patch header
*
- * NOTE! This hardcodes "-p1" behaviour in filename detection.
- *
* FIXME! The end-of-filename heuristics are kind of screwy. For existing
* files, we can happily check the index for a match, but for creating a
* new file we should try to match whatever "patch" does. I have no idea.
first += 4; /* skip "--- " */
second += 4; /* skip "+++ " */
+ if (!p_value_known) {
+ int p, q;
+ p = guess_p_value(first);
+ q = guess_p_value(second);
+ if (p < 0) p = q;
+ if (0 <= p && p == q) {
+ p_value = p;
+ p_value_known = 1;
+ }
+ }
if (is_dev_null(first)) {
patch->is_new = 1;
patch->is_delete = 0;
static char *gitdiff_verify_name(const char *line, int isnull, char *orig_name, const char *oldnew)
{
if (!orig_name && !isnull)
- return find_name(line, NULL, 1, TERM_TAB);
+ return find_name(line, NULL, p_value, TERM_TAB);
if (orig_name) {
int len;
len = strlen(name);
if (isnull)
die("git-apply: bad git-diff - expected /dev/null, got %s on line %d", name, linenr);
- another = find_name(line, NULL, 1, TERM_TAB);
+ another = find_name(line, NULL, p_value, TERM_TAB);
if (!another || memcmp(another, name, len))
die("git-apply: bad git-diff - inconsistent %s filename on line %d", oldnew, linenr);
free(another);
{
unsigned long offset, len;
+ patch->is_toplevel_relative = 0;
patch->is_rename = patch->is_copy = 0;
patch->is_new = patch->is_delete = -1;
patch->old_mode = patch->new_mode = 0;
die("git diff header lacks filename information (line %d)", linenr);
patch->old_name = patch->new_name = patch->def_name;
}
+ patch->is_toplevel_relative = 1;
*hdrsize = git_hdr_len;
return offset;
}
trailing++;
break;
case '-':
+ if (apply_in_reverse &&
+ new_whitespace != nowarn_whitespace)
+ check_whitespace(line, len);
deleted++;
oldlines--;
trailing = 0;
break;
case '+':
- if (new_whitespace != nowarn_whitespace)
+ if (!apply_in_reverse &&
+ new_whitespace != nowarn_whitespace)
check_whitespace(line, len);
added++;
newlines--;
*status_p = 0;
- if (!strncmp(buffer, "delta ", 6)) {
+ if (!prefixcmp(buffer, "delta ")) {
patch_method = BINARY_DELTA_DEFLATED;
origlen = strtoul(buffer + 6, NULL, 10);
}
- else if (!strncmp(buffer, "literal ", 8)) {
+ else if (!prefixcmp(buffer, "literal ")) {
patch_method = BINARY_LITERAL_DEFLATED;
origlen = strtoul(buffer + 8, NULL, 10);
}
free(qname);
}
-static int read_old_data(struct stat *st, const char *path, void *buf, unsigned long size)
+static int read_old_data(struct stat *st, const char *path, char **buf_p, unsigned long *alloc_p, unsigned long *size_p)
{
int fd;
unsigned long got;
+ unsigned long nsize;
+ char *nbuf;
+ unsigned long size = *size_p;
+ char *buf = *buf_p;
switch (st->st_mode & S_IFMT) {
case S_IFLNK:
- return readlink(path, buf, size);
+ return readlink(path, buf, size) != size;
case S_IFREG:
fd = open(path, O_RDONLY);
if (fd < 0)
return error("unable to open %s", path);
got = 0;
for (;;) {
- int ret = xread(fd, (char *) buf + got, size - got);
+ ssize_t ret = xread(fd, buf + got, size - got);
if (ret <= 0)
break;
got += ret;
}
close(fd);
- return got;
-
+ nsize = got;
+ nbuf = convert_to_git(path, buf, &nsize);
+ if (nbuf) {
+ free(buf);
+ *buf_p = nbuf;
+ *alloc_p = nsize;
+ *size_p = nsize;
+ }
+ return got != size;
default:
return -1;
}
}
/* Exact line number? */
- if (!memcmp(buf + start, fragment, fragsize))
+ if ((start + fragsize <= size) &&
+ !memcmp(buf + start, fragment, fragsize))
return start;
/*
buf = output;
if (need_fix_leading_space) {
+ int consecutive_spaces = 0;
/* between patch[1..last_tab_in_indent] strip the
* funny spaces, updating them to tab as needed.
*/
for (i = 1; i < last_tab_in_indent; i++, plen--) {
char ch = patch[i];
- if (ch != ' ')
+ if (ch != ' ') {
+ consecutive_spaces = 0;
*output++ = ch;
- else if ((i % 8) == 0)
- *output++ = '\t';
+ } else {
+ consecutive_spaces++;
+ if (consecutive_spaces == 8) {
+ *output++ = '\t';
+ consecutive_spaces = 0;
+ }
+ }
}
fixed = 1;
i = last_tab_in_indent;
if (add_nl_to_tail)
output[plen++] = '\n';
if (fixed)
- applied_after_stripping++;
+ applied_after_fixing_ws++;
return output + plen - buf;
}
char *new = xmalloc(size);
const char *oldlines, *newlines;
int oldsize = 0, newsize = 0;
+ int new_blank_lines_at_end = 0;
unsigned long leading, trailing;
int pos, lines;
char first;
int len = linelen(patch, size);
int plen;
+ int added_blank_line = 0;
if (!len)
break;
else if (first == '+')
first = '-';
}
+
switch (first) {
case '\n':
/* Newer GNU diff, empty context line */
break;
/* Fall-through for ' ' */
case '+':
- if (first != '+' || !no_add)
- newsize += apply_line(new + newsize, patch,
- plen);
+ if (first != '+' || !no_add) {
+ int added = apply_line(new + newsize, patch,
+ plen);
+ newsize += added;
+ if (first == '+' &&
+ added == 1 && new[newsize-1] == '\n')
+ added_blank_line = 1;
+ }
break;
case '@': case '\\':
/* Ignore it, we already handled it */
break;
default:
+ if (apply_verbosely)
+ error("invalid start of line: '%c'", first);
return -1;
}
+ if (added_blank_line)
+ new_blank_lines_at_end++;
+ else
+ new_blank_lines_at_end = 0;
patch += len;
size -= len;
}
if (match_beginning && offset)
offset = -1;
if (offset >= 0) {
- int diff = newsize - oldsize;
- unsigned long size = desc->size + diff;
- unsigned long alloc = desc->alloc;
+ int diff;
+ unsigned long size, alloc;
+
+ if (new_whitespace == strip_whitespace &&
+ (desc->size - oldsize - offset == 0)) /* end of file? */
+ newsize -= new_blank_lines_at_end;
+
+ diff = newsize - oldsize;
+ size = desc->size + diff;
+ alloc = desc->alloc;
/* Warn if it was necessary to reduce the number
* of context lines.
}
}
+ if (offset && apply_verbosely)
+ error("while searching for:\n%.*s", oldsize, oldlines);
+
free(old);
free(new);
return offset;
if (has_sha1_file(sha1)) {
/* We already have the postimage */
- char type[10];
+ enum object_type type;
unsigned long size;
free(desc->buffer);
- desc->buffer = read_sha1_file(sha1, type, &size);
+ desc->buffer = read_sha1_file(sha1, &type, &size);
if (!desc->buffer)
return error("the necessary postimage %s for "
"'%s' cannot be read",
return 0;
}
+static int read_file_or_gitlink(struct cache_entry *ce, char **buf_p,
+ unsigned long *size_p)
+{
+ if (!ce)
+ return 0;
+
+ if (S_ISGITLINK(ntohl(ce->ce_mode))) {
+ *buf_p = xmalloc(100);
+ *size_p = snprintf(*buf_p, 100,
+ "Subproject commit %s\n", sha1_to_hex(ce->sha1));
+ } else {
+ enum object_type type;
+ *buf_p = read_sha1_file(ce->sha1, &type, size_p);
+ if (!*buf_p)
+ return -1;
+ }
+ return 0;
+}
+
static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce)
{
char *buf;
alloc = 0;
buf = NULL;
if (cached) {
- if (ce) {
- char type[20];
- buf = read_sha1_file(ce->sha1, type, &size);
- if (!buf)
+ if (read_file_or_gitlink(ce, &buf, &size))
+ return error("read of %s failed", patch->old_name);
+ alloc = size;
+ } else if (patch->old_name) {
+ if (S_ISGITLINK(patch->old_mode)) {
+ if (ce)
+ read_file_or_gitlink(ce, &buf, &size);
+ else {
+ /*
+ * There is no way to apply subproject
+ * patch without looking at the index.
+ */
+ patch->fragments = NULL;
+ size = 0;
+ }
+ }
+ else {
+ size = xsize_t(st->st_size);
+ alloc = size + 8192;
+ buf = xmalloc(alloc);
+ if (read_old_data(st, patch->old_name,
+ &buf, &alloc, &size))
return error("read of %s failed",
patch->old_name);
- alloc = size;
}
}
- else if (patch->old_name) {
- size = st->st_size;
- alloc = size + 8192;
- buf = xmalloc(alloc);
- if (read_old_data(st, patch->old_name, buf, alloc) != size)
- return error("read of %s failed", patch->old_name);
- }
desc.size = size;
desc.alloc = alloc;
return 0;
}
+static int check_to_create_blob(const char *new_name, int ok_if_exists)
+{
+ struct stat nst;
+ if (!lstat(new_name, &nst)) {
+ if (S_ISDIR(nst.st_mode) || ok_if_exists)
+ return 0;
+ /*
+ * A leading component of new_name might be a symlink
+ * that is going to be removed with this patch, but
+ * still pointing at somewhere that has the path.
+ * In such a case, path "new_name" does not exist as
+ * far as git is concerned.
+ */
+ if (has_symlink_leading_path(new_name, NULL))
+ return 0;
+
+ return error("%s: already exists in working directory", new_name);
+ }
+ else if ((errno != ENOENT) && (errno != ENOTDIR))
+ return error("%s: %s", new_name, strerror(errno));
+ return 0;
+}
+
+static int verify_index_match(struct cache_entry *ce, struct stat *st)
+{
+ if (S_ISGITLINK(ntohl(ce->ce_mode))) {
+ if (!S_ISDIR(st->st_mode))
+ return -1;
+ return 0;
+ }
+ return ce_match_stat(ce, st, 1);
+}
+
static int check_patch(struct patch *patch, struct patch *prev_patch)
{
struct stat st;
int ok_if_exists;
patch->rejected = 1; /* we will drop this after we succeed */
+
+ /*
+ * Make sure that we do not have local modifications from the
+ * index when we are looking at the index. Also make sure
+ * we have the preimage file to be patched in the work tree,
+ * unless --cached, which tells git to apply only in the index.
+ */
if (old_name) {
- int changed = 0;
int stat_ret = 0;
unsigned st_mode = 0;
lstat(old_name, &st))
return -1;
}
- if (!cached)
- changed = ce_match_stat(ce, &st, 1);
- if (changed)
+ if (!cached && verify_index_match(ce, &st))
return error("%s: does not match index",
old_name);
if (cached)
st_mode = ntohl(ce->ce_mode);
- }
- else if (stat_ret < 0)
+ } else if (stat_ret < 0)
return error("%s: %s", old_name, strerror(errno));
if (!cached)
!ok_if_exists)
return error("%s: already exists in index", new_name);
if (!cached) {
- struct stat nst;
- if (!lstat(new_name, &nst)) {
- if (S_ISDIR(nst.st_mode) || ok_if_exists)
- ; /* ok */
- else
- return error("%s: already exists in working directory", new_name);
- }
- else if ((errno != ENOENT) && (errno != ENOTDIR))
- return error("%s: %s", new_name, strerror(errno));
+ int err = check_to_create_blob(new_name, ok_if_exists);
+ if (err)
+ return err;
}
if (!patch->new_mode) {
if (0 < patch->is_new)
return err;
}
+/* This function tries to read the sha1 from the current index */
+static int get_current_sha1(const char *path, unsigned char *sha1)
+{
+ int pos;
+
+ if (read_cache() < 0)
+ return -1;
+ pos = cache_name_pos(path, strlen(path));
+ if (pos < 0)
+ return -1;
+ hashcpy(sha1, active_cache[pos]->sha1);
+ return 0;
+}
+
static void show_index_list(struct patch *list)
{
struct patch *patch;
if (0 < patch->is_new)
sha1_ptr = null_sha1;
else if (get_sha1(patch->old_sha1_prefix, sha1))
- die("sha1 information is lacking or useless (%s).",
- name);
+ /* git diff has no index line for mode/type changes */
+ if (!patch->lines_added && !patch->lines_deleted) {
+ if (get_current_sha1(patch->new_name, sha1) ||
+ get_current_sha1(patch->old_name, sha1))
+ die("mode change for %s, which is not "
+ "in current HEAD", name);
+ sha1_ptr = sha1;
+ } else
+ die("sha1 information is lacking or useless "
+ "(%s).", name);
else
sha1_ptr = sha1;
}
}
-static void remove_file(struct patch *patch)
+static void remove_file(struct patch *patch, int rmdir_empty)
{
- if (write_index) {
+ if (update_index) {
if (remove_file_from_cache(patch->old_name) < 0)
die("unable to remove %s from index", patch->old_name);
cache_tree_invalidate_path(active_cache_tree, patch->old_name);
}
if (!cached) {
- if (!unlink(patch->old_name)) {
+ if (S_ISGITLINK(patch->old_mode)) {
+ if (rmdir(patch->old_name))
+ warning("unable to remove submodule %s",
+ patch->old_name);
+ } else if (!unlink(patch->old_name) && rmdir_empty) {
char *name = xstrdup(patch->old_name);
char *end = strrchr(name, '/');
while (end) {
int namelen = strlen(path);
unsigned ce_size = cache_entry_size(namelen);
- if (!write_index)
+ if (!update_index)
return;
ce = xcalloc(1, ce_size);
memcpy(ce->name, path, namelen);
ce->ce_mode = create_ce_mode(mode);
ce->ce_flags = htons(namelen);
- if (!cached) {
- if (lstat(path, &st) < 0)
- die("unable to stat newly created file %s", path);
- fill_stat_cache_info(ce, &st);
+ if (S_ISGITLINK(mode)) {
+ const char *s = buf;
+
+ if (get_sha1_hex(s + strlen("Subproject commit "), ce->sha1))
+ die("corrupt patch for subproject %s", path);
+ } else {
+ if (!cached) {
+ if (lstat(path, &st) < 0)
+ die("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)
+ die("unable to create backing store for newly created file %s", path);
}
- if (write_sha1_file(buf, size, blob_type, ce->sha1) < 0)
- die("unable to create backing store for newly created file %s", path);
if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD) < 0)
die("unable to add cache entry for %s", path);
}
static int try_create_file(const char *path, unsigned int mode, const char *buf, unsigned long size)
{
int fd;
+ char *nbuf;
- if (S_ISLNK(mode))
+ if (S_ISGITLINK(mode)) {
+ struct stat st;
+ if (!lstat(path, &st) && S_ISDIR(st.st_mode))
+ return 0;
+ return mkdir(path, 0777);
+ }
+
+ if (has_symlinks && S_ISLNK(mode))
/* Although buf:size is counted string, it also is NUL
* terminated.
*/
return symlink(buf, path);
+
fd = open(path, O_CREAT | O_EXCL | O_WRONLY, (mode & 0100) ? 0777 : 0666);
if (fd < 0)
return -1;
+
+ nbuf = convert_to_working_tree(path, buf, &size);
+ if (nbuf)
+ buf = nbuf;
+
while (size) {
int written = xwrite(fd, buf, size);
if (written < 0)
}
if (close(fd) < 0)
die("closing file %s: %s", path, strerror(errno));
+ if (nbuf)
+ free(nbuf);
return 0;
}
* used to be.
*/
struct stat st;
- errno = 0;
- if (!lstat(path, &st) && S_ISDIR(st.st_mode) && !rmdir(path))
+ if (!lstat(path, &st) && (!S_ISDIR(st.st_mode) || !rmdir(path)))
errno = EEXIST;
}
{
if (patch->is_delete > 0) {
if (phase == 0)
- remove_file(patch);
+ remove_file(patch, 1);
return;
}
if (patch->is_new > 0 || patch->is_copy) {
* thing: remove the old, write the new
*/
if (phase == 0)
- remove_file(patch);
+ remove_file(patch, patch->is_rename);
if (phase == 1)
create_file(patch);
}
return 1;
}
+static void prefix_one(char **name)
+{
+ char *old_name = *name;
+ if (!old_name)
+ return;
+ *name = xstrdup(prefix_filename(prefix, prefix_length, *name));
+ free(old_name);
+}
+
+static void prefix_patches(struct patch *p)
+{
+ if (!prefix || p->is_toplevel_relative)
+ return;
+ for ( ; p; p = p->next) {
+ if (p->new_name == p->old_name) {
+ char *prefixed = p->new_name;
+ prefix_one(&prefixed);
+ p->new_name = p->old_name = prefixed;
+ }
+ else {
+ prefix_one(&p->new_name);
+ prefix_one(&p->old_name);
+ }
+ }
+}
+
static int apply_patch(int fd, const char *filename, int inaccurate_eof)
{
unsigned long offset, size;
break;
if (apply_in_reverse)
reverse_patches(patch);
+ if (prefix)
+ prefix_patches(patch);
if (use_patch(patch)) {
patch_stats(patch);
*listp = patch;
listp = &patch->next;
- } else {
+ }
+ else {
/* perhaps free it a bit better? */
free(patch);
skipped_patch++;
if (whitespace_error && (new_whitespace == error_on_whitespace))
apply = 0;
- write_index = check_index && apply;
- if (write_index && newfd < 0)
- newfd = hold_lock_file_for_update(&lock_file,
- get_index_file(), 1);
+ update_index = check_index && apply;
+ if (update_index && newfd < 0)
+ newfd = hold_locked_index(&lock_file, 1);
+
if (check_index) {
if (read_cache() < 0)
die("unable to read index file");
int read_stdin = 1;
int inaccurate_eof = 0;
int errs = 0;
+ int is_not_gitdir = 0;
const char *whitespace_option = NULL;
+ prefix = setup_git_directory_gently(&is_not_gitdir);
+ prefix_length = prefix ? strlen(prefix) : 0;
+ git_config(git_apply_config);
+ if (apply_default_whitespace)
+ parse_whitespace_option(apply_default_whitespace);
+
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
char *end;
read_stdin = 0;
continue;
}
- if (!strncmp(arg, "--exclude=", 10)) {
+ if (!prefixcmp(arg, "--exclude=")) {
struct excludes *x = xmalloc(sizeof(*x));
x->path = arg + 10;
x->next = excludes;
excludes = x;
continue;
}
- if (!strncmp(arg, "-p", 2)) {
+ if (!prefixcmp(arg, "-p")) {
p_value = atoi(arg + 2);
+ p_value_known = 1;
continue;
}
if (!strcmp(arg, "--no-add")) {
continue;
}
if (!strcmp(arg, "--index")) {
+ if (is_not_gitdir)
+ die("--index outside a repository");
check_index = 1;
continue;
}
if (!strcmp(arg, "--cached")) {
+ if (is_not_gitdir)
+ die("--cached outside a repository");
check_index = 1;
cached = 1;
continue;
line_termination = 0;
continue;
}
- if (!strncmp(arg, "-C", 2)) {
+ if (!prefixcmp(arg, "-C")) {
p_context = strtoul(arg + 2, &end, 0);
if (*end != '\0')
die("unrecognized context count '%s'", arg + 2);
continue;
}
- if (!strncmp(arg, "--whitespace=", 13)) {
+ if (!prefixcmp(arg, "--whitespace=")) {
whitespace_option = arg + 13;
parse_whitespace_option(arg + 13);
continue;
apply = apply_with_reject = apply_verbosely = 1;
continue;
}
- if (!strcmp(arg, "--verbose")) {
+ if (!strcmp(arg, "-v") || !strcmp(arg, "--verbose")) {
apply_verbosely = 1;
continue;
}
inaccurate_eof = 1;
continue;
}
-
- if (check_index && prefix_length < 0) {
- prefix = setup_git_directory();
- prefix_length = prefix ? strlen(prefix) : 0;
- git_config(git_apply_config);
- if (!whitespace_option && apply_default_whitespace)
- parse_whitespace_option(apply_default_whitespace);
- }
if (0 < prefix_length)
arg = prefix_filename(prefix, prefix_length, arg);
squelched == 1 ? "" : "s");
}
if (new_whitespace == error_on_whitespace)
- die("%d line%s add%s trailing whitespaces.",
+ die("%d line%s add%s whitespace errors.",
whitespace_error,
whitespace_error == 1 ? "" : "s",
whitespace_error == 1 ? "s" : "");
- if (applied_after_stripping)
+ if (applied_after_fixing_ws)
fprintf(stderr, "warning: %d line%s applied after"
- " stripping trailing whitespaces.\n",
- applied_after_stripping,
- applied_after_stripping == 1 ? "" : "s");
+ " fixing whitespace errors.\n",
+ applied_after_fixing_ws,
+ applied_after_fixing_ws == 1 ? "" : "s");
else if (whitespace_error)
- fprintf(stderr, "warning: %d line%s add%s trailing"
- " whitespaces.\n",
+ fprintf(stderr, "warning: %d line%s add%s whitespace errors.\n",
whitespace_error,
whitespace_error == 1 ? "" : "s",
whitespace_error == 1 ? "s" : "");
}
- if (write_index) {
+ if (update_index) {
if (write_cache(newfd, active_cache, active_nr) ||
- close(newfd) || commit_lock_file(&lock_file))
+ close(newfd) || commit_locked_index(&lock_file))
die("Unable to write new index file");
}