/*
* apply.c
*
* Copyright (C) Linus Torvalds, 2005
*
* This applies patches on top of some (arbitrary) version of the SCM.
*
* NOTE! It does all its work in the index file, and only cares about
* the files in the working directory if you tell it to "merge" the
* patch apply.
*
* Even when merging it always takes the source from the index, and
* uses the working tree as a "branch" for a 3-way merge.
*/
#include <ctype.h>
#include "cache.h"
// We default to the merge behaviour, since that's what most people would
// expect.
//
// --check turns on checking that the working tree matches the
// files that are being modified, but doesn't apply the patch
// --stat does just a diffstat, and doesn't actually apply
// --show-files shows the directory changes
//
static int merge_patch = 1;
static int check_index = 0;
static int write_index = 0;
static int diffstat = 0;
static int check = 0;
static int apply = 1;
static int show_files = 0;
static const char apply_usage[] = "git-apply [--stat] [--check] [--show-files] <patch>";
/*
* 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 linenr = 1;
struct fragment {
unsigned long oldpos, oldlines;
unsigned long newpos, newlines;
const char *patch;
int size;
struct fragment *next;
};
struct patch {
char *new_name, *old_name, *def_name;
unsigned int old_mode, new_mode;
int is_rename, is_copy, is_new, is_delete;
int lines_added, lines_deleted;
struct fragment *fragments;
char *result;
unsigned long resultsize;
struct patch *next;
};
#define CHUNKSIZE (8192)
#define SLOP (16)
static void *read_patch_file(int fd, unsigned long *sizep)
{
unsigned long size = 0, alloc = CHUNKSIZE;
void *buffer = xmalloc(alloc);
for (;;) {
int nr = alloc - size;
if (nr < 1024) {
alloc += CHUNKSIZE;
buffer = xrealloc(buffer, alloc);
nr = alloc - size;
}
nr = read(fd, buffer + size, nr);
if (!nr)
break;
if (nr < 0) {
if (errno == EAGAIN)
continue;
die("git-apply: read returned %s", strerror(errno));
}
size += nr;
}
*sizep = size;
/*
* Make sure that we have some slop in the buffer
* so that we can do speculative "memcmp" etc, and
* see to it that it is NUL-filled.
*/
if (alloc < size + SLOP)
buffer = xrealloc(buffer, size + SLOP);
memset(buffer + size, 0, SLOP);
return buffer;
}
static unsigned long linelen(const char *buffer, unsigned long size)
{
unsigned long len = 0;
while (size--) {
len++;
if (*buffer++ == '\n')
break;
}
return len;
}
static int is_dev_null(const char *str)
{
return !memcmp("/dev/null", str, 9) && isspace(str[9]);
}
#define TERM_SPACE 1
#define TERM_TAB 2
static int name_terminate(const char *name, int namelen, int c, int terminate)
{
if (c == ' ' && !(terminate & TERM_SPACE))
return 0;
if (c == '\t' && !(terminate & TERM_TAB))
return
|