git-apply: first cut at actually checking fragment data

Right now it requires that the fragment offsets be exact,
and it doesn't actually apply the fragment yet, but it
does find where it goes and verify the data.

Next step: actually applying the fragment changes.
This commit is contained in:
Linus Torvalds 2005-06-05 11:03:13 -07:00
parent 477606f57d
commit 3cca928d4a

197
apply.c
View File

@ -25,6 +25,7 @@
// --show-files shows the directory changes // --show-files shows the directory changes
// //
static int merge_patch = 1; static int merge_patch = 1;
static int check_index = 0;
static int diffstat = 0; static int diffstat = 0;
static int check = 0; static int check = 0;
static int apply = 1; static int apply = 1;
@ -59,6 +60,8 @@ struct patch {
int is_rename, is_copy, is_new, is_delete; int is_rename, is_copy, is_new, is_delete;
int lines_added, lines_deleted; int lines_added, lines_deleted;
struct fragment *fragments; struct fragment *fragments;
const char *result;
unsigned long resultsize;
struct patch *next; struct patch *next;
}; };
@ -100,7 +103,7 @@ static void *read_patch_file(int fd, unsigned long *sizep)
return buffer; return buffer;
} }
static unsigned long linelen(char *buffer, unsigned long size) static unsigned long linelen(const char *buffer, unsigned long size)
{ {
unsigned long len = 0; unsigned long len = 0;
while (size--) { while (size--) {
@ -644,6 +647,8 @@ static int parse_fragment(char *line, unsigned long size, struct patch *patch, s
break; break;
/* We allow "\ No newline at end of file" */ /* We allow "\ No newline at end of file" */
case '\\': case '\\':
if (len < 12 || memcmp(line, "\\ No newline", 12))
return -1;
break; break;
} }
} }
@ -734,6 +739,154 @@ static void show_stats(struct patch *patch)
add, pluses, del, minuses); add, pluses, del, minuses);
} }
static int read_old_data(struct stat *st, const char *path, void *buf, unsigned long size)
{
int fd;
unsigned long got;
switch (st->st_mode & S_IFMT) {
case S_IFLNK:
return readlink(path, buf, size);
case S_IFREG:
fd = open(path, O_RDONLY);
if (fd < 0)
return error("unable to open %s", path);
got = 0;
for (;;) {
int ret = read(fd, buf + got, size - got);
if (ret < 0) {
if (errno == EAGAIN)
continue;
break;
}
if (!ret)
break;
got += ret;
}
close(fd);
return got;
default:
return -1;
}
}
static int find_offset(const char *buf, unsigned long size, const char *fragment, unsigned long fragsize, int line)
{
unsigned long start;
if (fragsize > size)
return -1;
start = 0;
if (line > 1) {
line--;
unsigned long offset = 0;
while (start + offset <= size) {
if (buf[offset++] == '\n') {
start = offset;
if (!--line)
break;
}
}
}
/* Exact line number? */
if (!memcmp(buf + start, fragment, fragsize))
return start;
/*
* We should start searching forward and backward.
*/
return -1;
}
static int apply_one_fragment(char *buf, unsigned long *sizep, unsigned long *bufsizep, struct fragment *frag)
{
const char *patch = frag->patch;
int offset, size = frag->size;
char *old = xmalloc(size);
char *new = xmalloc(size);
int oldsize = 0, newsize = 0;
while (size > 0) {
int len = linelen(patch, size);
int plen;
if (!len)
break;
/*
* "plen" is how much of the line we should use for
* the actual patch data. Normally we just remove the
* first character on the line, but if the line is
* followed by "\ No newline", then we also remove the
* last one (which is the newline, of course).
*/
plen = len-1;
if (len > size && patch[len] == '\\')
plen--;
switch (*patch) {
case ' ':
case '-':
memcpy(old + oldsize, patch + 1, plen);
oldsize += plen;
if (*patch == '-')
break;
/* Fall-through for ' ' */
case '+':
memcpy(new + newsize, patch + 1, plen);
newsize += plen;
break;
case '@': case '\\':
/* Ignore it, we already handled it */
break;
default:
return -1;
}
patch += len;
size -= len;
}
offset = find_offset(buf, *sizep, old, oldsize, frag->newpos);
if (offset >= 0) {
printf("found at offset %d\n", offset);
offset = 0;
}
free(old);
free(new);
return offset;
}
static int apply_fragments(char *buf, unsigned long *sizep, unsigned long *bufsizep, struct patch *patch)
{
struct fragment *frag = patch->fragments;
while (frag) {
if (apply_one_fragment(buf, sizep, bufsizep, frag) < 0)
return error("patch failed: %s:%d", patch->old_name, frag->oldpos);
frag = frag->next;
}
}
static int apply_data(struct patch *patch, struct stat *st)
{
unsigned long size, bufsize;
void *buf;
if (!patch->old_name || !patch->fragments)
return 0;
size = st->st_size;
bufsize = size + 16;
buf = xmalloc(bufsize);
if (read_old_data(st, patch->old_name, buf, bufsize) != size)
return error("read of %s failed", patch->old_name);
if (apply_fragments(buf, &size, &bufsize, patch) < 0)
return -1;
return 0;
}
static int check_patch(struct patch *patch) static int check_patch(struct patch *patch)
{ {
struct stat st; struct stat st;
@ -741,30 +894,50 @@ static int check_patch(struct patch *patch)
const char *new_name = patch->new_name; const char *new_name = patch->new_name;
if (old_name) { if (old_name) {
int pos = cache_name_pos(old_name, strlen(old_name));
int changed; int changed;
if (pos < 0)
return error("%s: does not exist in index", old_name);
if (patch->is_new < 0)
patch->is_new = 0;
if (lstat(old_name, &st) < 0) if (lstat(old_name, &st) < 0)
return error("%s: %s\n", strerror(errno)); return error("%s: %s\n", strerror(errno));
changed = ce_match_stat(active_cache[pos], &st); if (check_index) {
if (changed) int pos = cache_name_pos(old_name, strlen(old_name));
return error("%s: does not match index", old_name); if (pos < 0)
return error("%s: does not exist in index", old_name);
changed = ce_match_stat(active_cache[pos], &st);
if (changed)
return error("%s: does not match index", old_name);
}
if (patch->is_new < 0)
patch->is_new = 0;
if (!patch->old_mode) if (!patch->old_mode)
patch->old_mode = st.st_mode; patch->old_mode = st.st_mode;
if ((st.st_mode ^ patch->old_mode) & S_IFMT)
return error("%s: wrong type", old_name);
if (st.st_mode != patch->old_mode)
fprintf(stderr, "warning: %s has type %o, expected %o\n",
old_name, st.st_mode, patch->old_mode);
} }
if (new_name && (patch->is_new | patch->is_rename | patch->is_copy)) { if (new_name && (patch->is_new | patch->is_rename | patch->is_copy)) {
if (cache_name_pos(new_name, strlen(new_name)) >= 0) if (check_index && cache_name_pos(new_name, strlen(new_name)) >= 0)
return error("%s: already exists in index", new_name); return error("%s: already exists in index", new_name);
if (!lstat(new_name, &st)) if (!lstat(new_name, &st))
return error("%s: already exists in working directory", new_name); return error("%s: already exists in working directory", new_name);
if (errno != ENOENT) if (errno != ENOENT)
return error("%s: %s", new_name, strerror(errno)); return error("%s: %s", new_name, strerror(errno));
} }
if (new_name && old_name) {
int same = !strcmp(old_name, new_name);
if (!patch->new_mode)
patch->new_mode = patch->old_mode;
if ((patch->old_mode ^ patch->new_mode) & S_IFMT)
return error("new mode (%o) of %s does not match old mode (%o)%s%s",
patch->new_mode, new_name, patch->old_mode,
same ? "" : " of ", same ? "" : old_name);
}
if (apply_data(patch, &st) < 0)
return error("%s: patch does not apply", old_name);
return 0; return 0;
} }
@ -907,6 +1080,10 @@ int main(int argc, char **argv)
check = 1; check = 1;
continue; continue;
} }
if (!strcmp(arg, "--index")) {
check_index = 1;
continue;
}
if (!strcmp(arg, "--show-files")) { if (!strcmp(arg, "--show-files")) {
show_files = 1; show_files = 1;
continue; continue;