mirror of
https://github.com/git/git.git
synced 2024-11-24 10:26:17 +08:00
Merge branches 'lt/crlf' and 'jc/apply-config'
* lt/crlf: Teach core.autocrlf to 'git apply' t0020: add test for auto-crlf Make AutoCRLF ternary variable. Lazy man's auto-CRLF * jc/apply-config: t4119: test autocomputing -p<n> for traditional diff input. git-apply: guess correct -p<n> value for non-git patches. git-apply: notice "diff --git" patch again Fix botched "leak fix" t4119: add test for traditional patch and different p_value apply: fix memory leak in prefix_one() git-apply: require -p<n> when working in a subdirectory. git-apply: do not lose cwd when run from a subdirectory. Teach 'git apply' to look at $HOME/.gitconfig even outside of a repository Teach 'git apply' to look at $GIT_DIR/config
This commit is contained in:
commit
ef1a5c2fa8
3
Makefile
3
Makefile
@ -266,7 +266,8 @@ LIB_OBJS = \
|
||||
revision.o pager.o tree-walk.o xdiff-interface.o \
|
||||
write_or_die.o trace.o list-objects.o grep.o \
|
||||
alloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \
|
||||
color.o wt-status.o archive-zip.o archive-tar.o shallow.o utf8.o
|
||||
color.o wt-status.o archive-zip.o archive-tar.o shallow.o utf8.o \
|
||||
convert.o
|
||||
|
||||
BUILTIN_OBJS = \
|
||||
builtin-add.o \
|
||||
|
155
builtin-apply.c
155
builtin-apply.c
@ -28,6 +28,7 @@ static int newfd = -1;
|
||||
|
||||
static int unidiff_zero;
|
||||
static int p_value = 1;
|
||||
static int p_value_known;
|
||||
static int check_index;
|
||||
static int write_index;
|
||||
static int cached;
|
||||
@ -144,6 +145,7 @@ struct patch {
|
||||
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;
|
||||
@ -238,7 +240,7 @@ static int name_terminate(const char *name, int namelen, int c, int terminate)
|
||||
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;
|
||||
@ -311,11 +313,54 @@ static char * find_name(const char *line, char *def, int p_value, int terminate)
|
||||
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.
|
||||
@ -326,6 +371,16 @@ static void parse_traditional_patch(const char *first, const char *second, struc
|
||||
|
||||
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;
|
||||
@ -787,6 +842,7 @@ static int find_header(char *line, unsigned long size, int *hdrsize, struct patc
|
||||
{
|
||||
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;
|
||||
@ -831,6 +887,7 @@ static int find_header(char *line, unsigned long size, int *hdrsize, struct patc
|
||||
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;
|
||||
}
|
||||
@ -1393,28 +1450,39 @@ static void show_stats(struct patch *patch)
|
||||
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);
|
||||
int ret = xread(fd, buf + got, size - got);
|
||||
if (ret <= 0)
|
||||
break;
|
||||
got += ret;
|
||||
}
|
||||
close(fd);
|
||||
return got;
|
||||
|
||||
nsize = got;
|
||||
nbuf = buf;
|
||||
if (convert_to_git(path, &nbuf, &nsize)) {
|
||||
free(buf);
|
||||
*buf_p = nbuf;
|
||||
*alloc_p = nsize;
|
||||
*size_p = nsize;
|
||||
}
|
||||
return got != size;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
@ -1910,7 +1978,7 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *
|
||||
size = st->st_size;
|
||||
alloc = size + 8192;
|
||||
buf = xmalloc(alloc);
|
||||
if (read_old_data(st, patch->old_name, buf, alloc) != size)
|
||||
if (read_old_data(st, patch->old_name, &buf, &alloc, &size))
|
||||
return error("read of %s failed", patch->old_name);
|
||||
}
|
||||
|
||||
@ -2232,7 +2300,7 @@ static void patch_stats(struct patch *patch)
|
||||
}
|
||||
}
|
||||
|
||||
static void remove_file(struct patch *patch)
|
||||
static void remove_file(struct patch *patch, int rmdir_empty)
|
||||
{
|
||||
if (write_index) {
|
||||
if (remove_file_from_cache(patch->old_name) < 0)
|
||||
@ -2240,7 +2308,7 @@ static void remove_file(struct patch *patch)
|
||||
cache_tree_invalidate_path(active_cache_tree, patch->old_name);
|
||||
}
|
||||
if (!cached) {
|
||||
if (!unlink(patch->old_name)) {
|
||||
if (!unlink(patch->old_name) && rmdir_empty) {
|
||||
char *name = xstrdup(patch->old_name);
|
||||
char *end = strrchr(name, '/');
|
||||
while (end) {
|
||||
@ -2282,12 +2350,22 @@ static void add_index_file(const char *path, unsigned mode, void *buf, unsigned
|
||||
static int try_create_file(const char *path, unsigned int mode, const char *buf, unsigned long size)
|
||||
{
|
||||
int fd;
|
||||
char *nbuf;
|
||||
unsigned long nsize;
|
||||
|
||||
if (S_ISLNK(mode))
|
||||
/* Although buf:size is counted string, it also is NUL
|
||||
* terminated.
|
||||
*/
|
||||
return symlink(buf, path);
|
||||
nsize = size;
|
||||
nbuf = (char *) buf;
|
||||
if (convert_to_working_tree(path, &nbuf, &nsize)) {
|
||||
free((char *) buf);
|
||||
buf = nbuf;
|
||||
size = nsize;
|
||||
}
|
||||
|
||||
fd = open(path, O_CREAT | O_EXCL | O_WRONLY, (mode & 0100) ? 0777 : 0666);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
@ -2373,7 +2451,7 @@ static void write_out_one_result(struct patch *patch, int phase)
|
||||
{
|
||||
if (patch->is_delete > 0) {
|
||||
if (phase == 0)
|
||||
remove_file(patch);
|
||||
remove_file(patch, 1);
|
||||
return;
|
||||
}
|
||||
if (patch->is_new > 0 || patch->is_copy) {
|
||||
@ -2386,7 +2464,7 @@ static void write_out_one_result(struct patch *patch, int phase)
|
||||
* thing: remove the old, write the new
|
||||
*/
|
||||
if (phase == 0)
|
||||
remove_file(patch);
|
||||
remove_file(patch, 0);
|
||||
if (phase == 1)
|
||||
create_file(patch);
|
||||
}
|
||||
@ -2508,6 +2586,32 @@ static int use_patch(struct patch *p)
|
||||
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;
|
||||
@ -2530,11 +2634,14 @@ static int apply_patch(int fd, const char *filename, int inaccurate_eof)
|
||||
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++;
|
||||
@ -2595,9 +2702,16 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix)
|
||||
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;
|
||||
@ -2617,6 +2731,7 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix)
|
||||
}
|
||||
if (!prefixcmp(arg, "-p")) {
|
||||
p_value = atoi(arg + 2);
|
||||
p_value_known = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--no-add")) {
|
||||
@ -2648,10 +2763,14 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix)
|
||||
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;
|
||||
@ -2700,14 +2819,6 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix)
|
||||
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);
|
||||
|
||||
|
5
cache.h
5
cache.h
@ -211,6 +211,7 @@ extern const char *apply_default_whitespace;
|
||||
extern int zlib_compression_level;
|
||||
extern size_t packed_git_window_size;
|
||||
extern size_t packed_git_limit;
|
||||
extern int auto_crlf;
|
||||
|
||||
#define GIT_REPO_VERSION 0
|
||||
extern int repository_format_version;
|
||||
@ -478,4 +479,8 @@ extern int nfvasprintf(char **str, const char *fmt, va_list va);
|
||||
extern void trace_printf(const char *format, ...);
|
||||
extern void trace_argv_printf(const char **argv, int count, const char *format, ...);
|
||||
|
||||
/* convert.c */
|
||||
extern int convert_to_git(const char *path, char **bufp, unsigned long *sizep);
|
||||
extern int convert_to_working_tree(const char *path, char **bufp, unsigned long *sizep);
|
||||
|
||||
#endif /* CACHE_H */
|
||||
|
9
config.c
9
config.c
@ -326,6 +326,15 @@ int git_default_config(const char *var, const char *value)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strcmp(var, "core.autocrlf")) {
|
||||
if (value && !strcasecmp(value, "input")) {
|
||||
auto_crlf = -1;
|
||||
return 0;
|
||||
}
|
||||
auto_crlf = git_config_bool(var, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strcmp(var, "user.name")) {
|
||||
strlcpy(git_default_name, value, sizeof(git_default_name));
|
||||
return 0;
|
||||
|
186
convert.c
Normal file
186
convert.c
Normal file
@ -0,0 +1,186 @@
|
||||
#include "cache.h"
|
||||
/*
|
||||
* convert.c - convert a file when checking it out and checking it in.
|
||||
*
|
||||
* This should use the pathname to decide on whether it wants to do some
|
||||
* more interesting conversions (automatic gzip/unzip, general format
|
||||
* conversions etc etc), but by default it just does automatic CRLF<->LF
|
||||
* translation when the "auto_crlf" option is set.
|
||||
*/
|
||||
|
||||
struct text_stat {
|
||||
/* CR, LF and CRLF counts */
|
||||
unsigned cr, lf, crlf;
|
||||
|
||||
/* These are just approximations! */
|
||||
unsigned printable, nonprintable;
|
||||
};
|
||||
|
||||
static void gather_stats(const char *buf, unsigned long size, struct text_stat *stats)
|
||||
{
|
||||
unsigned long i;
|
||||
|
||||
memset(stats, 0, sizeof(*stats));
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
unsigned char c = buf[i];
|
||||
if (c == '\r') {
|
||||
stats->cr++;
|
||||
if (i+1 < size && buf[i+1] == '\n')
|
||||
stats->crlf++;
|
||||
continue;
|
||||
}
|
||||
if (c == '\n') {
|
||||
stats->lf++;
|
||||
continue;
|
||||
}
|
||||
if (c == 127)
|
||||
/* DEL */
|
||||
stats->nonprintable++;
|
||||
else if (c < 32) {
|
||||
switch (c) {
|
||||
/* BS, HT, ESC and FF */
|
||||
case '\b': case '\t': case '\033': case '\014':
|
||||
stats->printable++;
|
||||
break;
|
||||
default:
|
||||
stats->nonprintable++;
|
||||
}
|
||||
}
|
||||
else
|
||||
stats->printable++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The same heuristics as diff.c::mmfile_is_binary()
|
||||
*/
|
||||
static int is_binary(unsigned long size, struct text_stat *stats)
|
||||
{
|
||||
|
||||
if ((stats->printable >> 7) < stats->nonprintable)
|
||||
return 1;
|
||||
/*
|
||||
* Other heuristics? Average line length might be relevant,
|
||||
* as might LF vs CR vs CRLF counts..
|
||||
*
|
||||
* NOTE! It might be normal to have a low ratio of CRLF to LF
|
||||
* (somebody starts with a LF-only file and edits it with an editor
|
||||
* that adds CRLF only to lines that are added..). But do we
|
||||
* want to support CR-only? Probably not.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
int convert_to_git(const char *path, char **bufp, unsigned long *sizep)
|
||||
{
|
||||
char *buffer, *nbuf;
|
||||
unsigned long size, nsize;
|
||||
struct text_stat stats;
|
||||
|
||||
/*
|
||||
* FIXME! Other pluggable conversions should go here,
|
||||
* based on filename patterns. Right now we just do the
|
||||
* stupid auto-CRLF one.
|
||||
*/
|
||||
if (!auto_crlf)
|
||||
return 0;
|
||||
|
||||
size = *sizep;
|
||||
if (!size)
|
||||
return 0;
|
||||
buffer = *bufp;
|
||||
|
||||
gather_stats(buffer, size, &stats);
|
||||
|
||||
/* No CR? Nothing to convert, regardless. */
|
||||
if (!stats.cr)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* We're currently not going to even try to convert stuff
|
||||
* that has bare CR characters. Does anybody do that crazy
|
||||
* stuff?
|
||||
*/
|
||||
if (stats.cr != stats.crlf)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* And add some heuristics for binary vs text, of course...
|
||||
*/
|
||||
if (is_binary(size, &stats))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Ok, allocate a new buffer, fill it in, and return true
|
||||
* to let the caller know that we switched buffers on it.
|
||||
*/
|
||||
nsize = size - stats.crlf;
|
||||
nbuf = xmalloc(nsize);
|
||||
*bufp = nbuf;
|
||||
*sizep = nsize;
|
||||
do {
|
||||
unsigned char c = *buffer++;
|
||||
if (c != '\r')
|
||||
*nbuf++ = c;
|
||||
} while (--size);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int convert_to_working_tree(const char *path, char **bufp, unsigned long *sizep)
|
||||
{
|
||||
char *buffer, *nbuf;
|
||||
unsigned long size, nsize;
|
||||
struct text_stat stats;
|
||||
unsigned char last;
|
||||
|
||||
/*
|
||||
* FIXME! Other pluggable conversions should go here,
|
||||
* based on filename patterns. Right now we just do the
|
||||
* stupid auto-CRLF one.
|
||||
*/
|
||||
if (auto_crlf <= 0)
|
||||
return 0;
|
||||
|
||||
size = *sizep;
|
||||
if (!size)
|
||||
return 0;
|
||||
buffer = *bufp;
|
||||
|
||||
gather_stats(buffer, size, &stats);
|
||||
|
||||
/* No LF? Nothing to convert, regardless. */
|
||||
if (!stats.lf)
|
||||
return 0;
|
||||
|
||||
/* Was it already in CRLF format? */
|
||||
if (stats.lf == stats.crlf)
|
||||
return 0;
|
||||
|
||||
/* If we have any bare CR characters, we're not going to touch it */
|
||||
if (stats.cr != stats.crlf)
|
||||
return 0;
|
||||
|
||||
if (is_binary(size, &stats))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Ok, allocate a new buffer, fill it in, and return true
|
||||
* to let the caller know that we switched buffers on it.
|
||||
*/
|
||||
nsize = size + stats.lf - stats.crlf;
|
||||
nbuf = xmalloc(nsize);
|
||||
*bufp = nbuf;
|
||||
*sizep = nsize;
|
||||
last = 0;
|
||||
do {
|
||||
unsigned char c = *buffer++;
|
||||
if (c == '\n' && last != '\r')
|
||||
*nbuf++ = '\r';
|
||||
*nbuf++ = c;
|
||||
last = c;
|
||||
} while (--size);
|
||||
|
||||
return 1;
|
||||
}
|
17
diff.c
17
diff.c
@ -1378,6 +1378,9 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only)
|
||||
reuse_worktree_file(s->path, s->sha1, 0)) {
|
||||
struct stat st;
|
||||
int fd;
|
||||
char *buf;
|
||||
unsigned long size;
|
||||
|
||||
if (lstat(s->path, &st) < 0) {
|
||||
if (errno == ENOENT) {
|
||||
err_empty:
|
||||
@ -1410,7 +1413,19 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only)
|
||||
s->data = xmmap(NULL, s->size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
close(fd);
|
||||
s->should_munmap = 1;
|
||||
/* FIXME! CRLF -> LF conversion goes here, based on "s->path" */
|
||||
|
||||
/*
|
||||
* Convert from working tree format to canonical git format
|
||||
*/
|
||||
buf = s->data;
|
||||
size = s->size;
|
||||
if (convert_to_git(s->path, &buf, &size)) {
|
||||
munmap(s->data, s->size);
|
||||
s->should_munmap = 0;
|
||||
s->data = buf;
|
||||
s->size = size;
|
||||
s->should_free = 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
char type[20];
|
||||
|
16
entry.c
16
entry.c
@ -78,6 +78,9 @@ static int write_entry(struct cache_entry *ce, char *path, struct checkout *stat
|
||||
path, sha1_to_hex(ce->sha1));
|
||||
}
|
||||
switch (ntohl(ce->ce_mode) & S_IFMT) {
|
||||
char *buf;
|
||||
unsigned long nsize;
|
||||
|
||||
case S_IFREG:
|
||||
if (to_tempfile) {
|
||||
strcpy(path, ".merge_file_XXXXXX");
|
||||
@ -89,7 +92,18 @@ static int write_entry(struct cache_entry *ce, char *path, struct checkout *stat
|
||||
return error("git-checkout-index: unable to create file %s (%s)",
|
||||
path, strerror(errno));
|
||||
}
|
||||
/* FIXME: LF -> CRLF conversion goes here, based on "ce->name" */
|
||||
|
||||
/*
|
||||
* Convert from git internal format to working tree format
|
||||
*/
|
||||
buf = new;
|
||||
nsize = size;
|
||||
if (convert_to_working_tree(ce->name, &buf, &nsize)) {
|
||||
free(new);
|
||||
new = buf;
|
||||
size = nsize;
|
||||
}
|
||||
|
||||
wrote = write_in_full(fd, new, size);
|
||||
close(fd);
|
||||
free(new);
|
||||
|
@ -28,6 +28,7 @@ size_t packed_git_window_size = DEFAULT_PACKED_GIT_WINDOW_SIZE;
|
||||
size_t packed_git_limit = DEFAULT_PACKED_GIT_LIMIT;
|
||||
int pager_in_use;
|
||||
int pager_use_color = 1;
|
||||
int auto_crlf = 0; /* 1: both ways, -1: only when adding git objects */
|
||||
|
||||
static const char *git_dir;
|
||||
static char *git_object_dir, *git_index_file, *git_refs_dir, *git_graft_file;
|
||||
|
23
sha1_file.c
23
sha1_file.c
@ -2082,7 +2082,7 @@ int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, con
|
||||
{
|
||||
unsigned long size = st->st_size;
|
||||
void *buf;
|
||||
int ret;
|
||||
int ret, re_allocated = 0;
|
||||
|
||||
buf = "";
|
||||
if (size)
|
||||
@ -2091,11 +2091,30 @@ int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, con
|
||||
|
||||
if (!type)
|
||||
type = blob_type;
|
||||
/* FIXME: CRLF -> LF conversion here for blobs! We'll need the path! */
|
||||
|
||||
/*
|
||||
* Convert blobs to git internal format
|
||||
*/
|
||||
if (!strcmp(type, blob_type)) {
|
||||
unsigned long nsize = size;
|
||||
char *nbuf = buf;
|
||||
if (convert_to_git(NULL, &nbuf, &nsize)) {
|
||||
if (size)
|
||||
munmap(buf, size);
|
||||
size = nsize;
|
||||
buf = nbuf;
|
||||
re_allocated = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (write_object)
|
||||
ret = write_sha1_file(buf, size, type, sha1);
|
||||
else
|
||||
ret = hash_sha1_file(buf, size, type, sha1);
|
||||
if (re_allocated) {
|
||||
free(buf);
|
||||
return ret;
|
||||
}
|
||||
if (size)
|
||||
munmap(buf, size);
|
||||
return ret;
|
||||
|
217
t/t0020-crlf.sh
Executable file
217
t/t0020-crlf.sh
Executable file
@ -0,0 +1,217 @@
|
||||
#!/bin/sh
|
||||
|
||||
test_description='CRLF conversion'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
append_cr () {
|
||||
sed -e 's/$/Q/' | tr Q '\015'
|
||||
}
|
||||
|
||||
remove_cr () {
|
||||
tr '\015' Q <"$1" | grep Q >/dev/null &&
|
||||
tr '\015' Q <"$1" | sed -ne 's/Q$//p'
|
||||
}
|
||||
|
||||
test_expect_success setup '
|
||||
|
||||
git repo-config core.autocrlf false &&
|
||||
|
||||
for w in Hello world how are you; do echo $w; done >one &&
|
||||
mkdir dir &&
|
||||
for w in I am very very fine thank you; do echo $w; done >dir/two &&
|
||||
git add . &&
|
||||
|
||||
git commit -m initial &&
|
||||
|
||||
one=`git rev-parse HEAD:one` &&
|
||||
dir=`git rev-parse HEAD:dir` &&
|
||||
two=`git rev-parse HEAD:dir/two` &&
|
||||
|
||||
for w in Some extra lines here; do echo $w; done >>one &&
|
||||
git diff >patch.file &&
|
||||
patched=`git hash-object --stdin <one` &&
|
||||
git read-tree --reset -u HEAD &&
|
||||
|
||||
echo happy.
|
||||
'
|
||||
|
||||
test_expect_success 'update with autocrlf=input' '
|
||||
|
||||
rm -f tmp one dir/two &&
|
||||
git read-tree --reset -u HEAD &&
|
||||
git repo-config core.autocrlf input &&
|
||||
|
||||
for f in one dir/two
|
||||
do
|
||||
append_cr <$f >tmp && mv -f tmp $f &&
|
||||
git update-index -- $f || {
|
||||
echo Oops
|
||||
false
|
||||
break
|
||||
}
|
||||
done &&
|
||||
|
||||
differs=`git diff-index --cached HEAD` &&
|
||||
test -z "$differs" || {
|
||||
echo Oops "$differs"
|
||||
false
|
||||
}
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'update with autocrlf=true' '
|
||||
|
||||
rm -f tmp one dir/two &&
|
||||
git read-tree --reset -u HEAD &&
|
||||
git repo-config core.autocrlf true &&
|
||||
|
||||
for f in one dir/two
|
||||
do
|
||||
append_cr <$f >tmp && mv -f tmp $f &&
|
||||
git update-index -- $f || {
|
||||
echo "Oops $f"
|
||||
false
|
||||
break
|
||||
}
|
||||
done &&
|
||||
|
||||
differs=`git diff-index --cached HEAD` &&
|
||||
test -z "$differs" || {
|
||||
echo Oops "$differs"
|
||||
false
|
||||
}
|
||||
|
||||
'
|
||||
|
||||
test_expect_success 'checkout with autocrlf=true' '
|
||||
|
||||
rm -f tmp one dir/two &&
|
||||
git repo-config core.autocrlf true &&
|
||||
git read-tree --reset -u HEAD &&
|
||||
|
||||
for f in one dir/two
|
||||
do
|
||||
remove_cr "$f" >tmp && mv -f tmp $f &&
|
||||
git update-index -- $f || {
|
||||
echo "Eh? $f"
|
||||
false
|
||||
break
|
||||
}
|
||||
done &&
|
||||
test "$one" = `git hash-object --stdin <one` &&
|
||||
test "$two" = `git hash-object --stdin <dir/two` &&
|
||||
differs=`git diff-index --cached HEAD` &&
|
||||
test -z "$differs" || {
|
||||
echo Oops "$differs"
|
||||
false
|
||||
}
|
||||
'
|
||||
|
||||
test_expect_success 'checkout with autocrlf=input' '
|
||||
|
||||
rm -f tmp one dir/two &&
|
||||
git repo-config core.autocrlf input &&
|
||||
git read-tree --reset -u HEAD &&
|
||||
|
||||
for f in one dir/two
|
||||
do
|
||||
if remove_cr "$f" >/dev/null
|
||||
then
|
||||
echo "Eh? $f"
|
||||
false
|
||||
break
|
||||
else
|
||||
git update-index -- $f
|
||||
fi
|
||||
done &&
|
||||
test "$one" = `git hash-object --stdin <one` &&
|
||||
test "$two" = `git hash-object --stdin <dir/two` &&
|
||||
differs=`git diff-index --cached HEAD` &&
|
||||
test -z "$differs" || {
|
||||
echo Oops "$differs"
|
||||
false
|
||||
}
|
||||
'
|
||||
|
||||
test_expect_success 'apply patch (autocrlf=input)' '
|
||||
|
||||
rm -f tmp one dir/two &&
|
||||
git repo-config core.autocrlf input &&
|
||||
git read-tree --reset -u HEAD &&
|
||||
|
||||
git apply patch.file &&
|
||||
test "$patched" = "`git hash-object --stdin <one`" || {
|
||||
echo "Eh? apply without index"
|
||||
false
|
||||
}
|
||||
'
|
||||
|
||||
test_expect_success 'apply patch --cached (autocrlf=input)' '
|
||||
|
||||
rm -f tmp one dir/two &&
|
||||
git repo-config core.autocrlf input &&
|
||||
git read-tree --reset -u HEAD &&
|
||||
|
||||
git apply --cached patch.file &&
|
||||
test "$patched" = `git rev-parse :one` || {
|
||||
echo "Eh? apply with --cached"
|
||||
false
|
||||
}
|
||||
'
|
||||
|
||||
test_expect_success 'apply patch --index (autocrlf=input)' '
|
||||
|
||||
rm -f tmp one dir/two &&
|
||||
git repo-config core.autocrlf input &&
|
||||
git read-tree --reset -u HEAD &&
|
||||
|
||||
git apply --index patch.file &&
|
||||
test "$patched" = `git rev-parse :one` &&
|
||||
test "$patched" = `git hash-object --stdin <one` || {
|
||||
echo "Eh? apply with --index"
|
||||
false
|
||||
}
|
||||
'
|
||||
|
||||
test_expect_success 'apply patch (autocrlf=true)' '
|
||||
|
||||
rm -f tmp one dir/two &&
|
||||
git repo-config core.autocrlf true &&
|
||||
git read-tree --reset -u HEAD &&
|
||||
|
||||
git apply patch.file &&
|
||||
test "$patched" = "`remove_cr one | git hash-object --stdin`" || {
|
||||
echo "Eh? apply without index"
|
||||
false
|
||||
}
|
||||
'
|
||||
|
||||
test_expect_success 'apply patch --cached (autocrlf=true)' '
|
||||
|
||||
rm -f tmp one dir/two &&
|
||||
git repo-config core.autocrlf true &&
|
||||
git read-tree --reset -u HEAD &&
|
||||
|
||||
git apply --cached patch.file &&
|
||||
test "$patched" = `git rev-parse :one` || {
|
||||
echo "Eh? apply without index"
|
||||
false
|
||||
}
|
||||
'
|
||||
|
||||
test_expect_success 'apply patch --index (autocrlf=true)' '
|
||||
|
||||
rm -f tmp one dir/two &&
|
||||
git repo-config core.autocrlf true &&
|
||||
git read-tree --reset -u HEAD &&
|
||||
|
||||
git apply --index patch.file &&
|
||||
test "$patched" = `git rev-parse :one` &&
|
||||
test "$patched" = "`remove_cr one | git hash-object --stdin`" || {
|
||||
echo "Eh? apply with --index"
|
||||
false
|
||||
}
|
||||
'
|
||||
|
||||
test_done
|
162
t/t4119-apply-config.sh
Executable file
162
t/t4119-apply-config.sh
Executable file
@ -0,0 +1,162 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2007 Junio C Hamano
|
||||
#
|
||||
|
||||
test_description='git-apply --whitespace=strip and configuration file.
|
||||
|
||||
'
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
test_expect_success setup '
|
||||
mkdir sub &&
|
||||
echo A >sub/file1 &&
|
||||
cp sub/file1 saved &&
|
||||
git add sub/file1 &&
|
||||
echo "B " >sub/file1 &&
|
||||
git diff >patch.file
|
||||
'
|
||||
|
||||
# Also handcraft GNU diff output; note this has trailing whitespace.
|
||||
cat >gpatch.file <<\EOF &&
|
||||
--- file1 2007-02-21 01:04:24.000000000 -0800
|
||||
+++ file1+ 2007-02-21 01:07:44.000000000 -0800
|
||||
@@ -1 +1 @@
|
||||
-A
|
||||
+B
|
||||
EOF
|
||||
|
||||
sed -e 's|file1|sub/&|' gpatch.file >gpatch-sub.file &&
|
||||
sed -e '
|
||||
/^--- /s|file1|a/sub/&|
|
||||
/^+++ /s|file1|b/sub/&|
|
||||
' gpatch.file >gpatch-ab-sub.file &&
|
||||
|
||||
check_result () {
|
||||
if grep " " "$1"
|
||||
then
|
||||
echo "Eh?"
|
||||
false
|
||||
elif grep B "$1"
|
||||
then
|
||||
echo Happy
|
||||
else
|
||||
echo "Huh?"
|
||||
false
|
||||
fi
|
||||
}
|
||||
|
||||
test_expect_success 'apply --whitespace=strip' '
|
||||
|
||||
rm -f sub/file1 &&
|
||||
cp saved sub/file1 &&
|
||||
git update-index --refresh &&
|
||||
|
||||
git apply --whitespace=strip patch.file &&
|
||||
check_result sub/file1
|
||||
'
|
||||
|
||||
test_expect_success 'apply --whitespace=strip from config' '
|
||||
|
||||
rm -f sub/file1 &&
|
||||
cp saved sub/file1 &&
|
||||
git update-index --refresh &&
|
||||
|
||||
git config apply.whitespace strip &&
|
||||
git apply patch.file &&
|
||||
check_result sub/file1
|
||||
'
|
||||
|
||||
D=`pwd`
|
||||
|
||||
test_expect_success 'apply --whitespace=strip in subdir' '
|
||||
|
||||
cd "$D" &&
|
||||
git config --unset-all apply.whitespace
|
||||
rm -f sub/file1 &&
|
||||
cp saved sub/file1 &&
|
||||
git update-index --refresh &&
|
||||
|
||||
cd sub &&
|
||||
git apply --whitespace=strip ../patch.file &&
|
||||
check_result file1
|
||||
'
|
||||
|
||||
test_expect_success 'apply --whitespace=strip from config in subdir' '
|
||||
|
||||
cd "$D" &&
|
||||
git config apply.whitespace strip &&
|
||||
rm -f sub/file1 &&
|
||||
cp saved sub/file1 &&
|
||||
git update-index --refresh &&
|
||||
|
||||
cd sub &&
|
||||
git apply ../patch.file &&
|
||||
check_result file1
|
||||
'
|
||||
|
||||
test_expect_success 'same in subdir but with traditional patch input' '
|
||||
|
||||
cd "$D" &&
|
||||
git config apply.whitespace strip &&
|
||||
rm -f sub/file1 &&
|
||||
cp saved sub/file1 &&
|
||||
git update-index --refresh &&
|
||||
|
||||
cd sub &&
|
||||
git apply ../gpatch.file &&
|
||||
check_result file1
|
||||
'
|
||||
|
||||
test_expect_success 'same but with traditional patch input of depth 1' '
|
||||
|
||||
cd "$D" &&
|
||||
git config apply.whitespace strip &&
|
||||
rm -f sub/file1 &&
|
||||
cp saved sub/file1 &&
|
||||
git update-index --refresh &&
|
||||
|
||||
cd sub &&
|
||||
git apply ../gpatch-sub.file &&
|
||||
check_result file1
|
||||
'
|
||||
|
||||
test_expect_success 'same but with traditional patch input of depth 2' '
|
||||
|
||||
cd "$D" &&
|
||||
git config apply.whitespace strip &&
|
||||
rm -f sub/file1 &&
|
||||
cp saved sub/file1 &&
|
||||
git update-index --refresh &&
|
||||
|
||||
cd sub &&
|
||||
git apply ../gpatch-ab-sub.file &&
|
||||
check_result file1
|
||||
'
|
||||
|
||||
test_expect_success 'same but with traditional patch input of depth 1' '
|
||||
|
||||
cd "$D" &&
|
||||
git config apply.whitespace strip &&
|
||||
rm -f sub/file1 &&
|
||||
cp saved sub/file1 &&
|
||||
git update-index --refresh &&
|
||||
|
||||
git apply -p0 gpatch-sub.file &&
|
||||
check_result sub/file1
|
||||
'
|
||||
|
||||
test_expect_success 'same but with traditional patch input of depth 2' '
|
||||
|
||||
cd "$D" &&
|
||||
git config apply.whitespace strip &&
|
||||
rm -f sub/file1 &&
|
||||
cp saved sub/file1 &&
|
||||
git update-index --refresh &&
|
||||
|
||||
git apply gpatch-ab-sub.file &&
|
||||
check_result sub/file1
|
||||
'
|
||||
|
||||
test_done
|
Loading…
Reference in New Issue
Block a user