diff --git a/builtin-apply.c b/builtin-apply.c
index f94d0dbf488ff43ccc496939560c81daf05772cf..e71789803749b22a3ec041b2bce533957cea1c72 100644 (file)
--- a/builtin-apply.c
+++ b/builtin-apply.c
void *buffer = xmalloc(alloc);
for (;;) {
void *buffer = xmalloc(alloc);
for (;;) {
- int nr = alloc - size;
+ ssize_t nr = alloc - size;
if (nr < 1024) {
alloc += CHUNKSIZE;
buffer = xrealloc(buffer, alloc);
if (nr < 1024) {
alloc += CHUNKSIZE;
buffer = xrealloc(buffer, alloc);
@@ -1468,7 +1468,7 @@ static int read_old_data(struct stat *st, const char *path, char **buf_p, unsign
return error("unable to open %s", path);
got = 0;
for (;;) {
return error("unable to open %s", path);
got = 0;
for (;;) {
- int ret = xread(fd, buf + got, size - got);
+ ssize_t ret = xread(fd, buf + got, size - got);
if (ret <= 0)
break;
got += ret;
if (ret <= 0)
break;
got += ret;
@@ -1671,6 +1671,7 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i
char *new = xmalloc(size);
const char *oldlines, *newlines;
int oldsize = 0, newsize = 0;
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;
unsigned long leading, trailing;
int pos, lines;
@@ -1678,6 +1679,7 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i
char first;
int len = linelen(patch, size);
int plen;
char first;
int len = linelen(patch, size);
int plen;
+ int added_blank_line = 0;
if (!len)
break;
if (!len)
break;
@@ -1699,6 +1701,7 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i
else if (first == '+')
first = '-';
}
else if (first == '+')
first = '-';
}
+
switch (first) {
case '\n':
/* Newer GNU diff, empty context line */
switch (first) {
case '\n':
/* Newer GNU diff, empty context line */
@@ -1716,9 +1719,14 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i
break;
/* Fall-through for ' ' */
case '+':
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;
case '@': case '\\':
/* Ignore it, we already handled it */
@@ -1728,6 +1736,10 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i
error("invalid start of line: '%c'", first);
return -1;
}
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;
}
patch += len;
size -= len;
}
@@ -1770,9 +1782,16 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i
if (match_beginning && offset)
offset = -1;
if (offset >= 0) {
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.
/* Warn if it was necessary to reduce the number
* of context lines.
@@ -2009,6 +2028,29 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *
return 0;
}
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 check_patch(struct patch *patch, struct patch *prev_patch)
{
struct stat st;
static int check_patch(struct patch *patch, struct patch *prev_patch)
{
struct stat st;
!ok_if_exists)
return error("%s: already exists in index", new_name);
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)
}
if (!patch->new_mode) {
if (0 < patch->is_new)