Commit f17951d6 by Vicent Martí

Merge pull request #1431 from libgit2/autocrlf-fixes

Fix crlf handling, particularly when autocrlf=true
parents 13640d1b 3658e81e
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "common.h" #include "common.h"
#include "blob.h" #include "blob.h"
#include "filter.h" #include "filter.h"
#include "buf_text.h"
const void *git_blob_rawcontent(const git_blob *blob) const void *git_blob_rawcontent(const git_blob *blob)
{ {
...@@ -221,7 +222,9 @@ int git_blob_create_fromworkdir(git_oid *oid, git_repository *repo, const char * ...@@ -221,7 +222,9 @@ int git_blob_create_fromworkdir(git_oid *oid, git_repository *repo, const char *
return -1; return -1;
} }
error = blob_create_internal(oid, repo, git_buf_cstr(&full_path), git_buf_cstr(&full_path), true); error = blob_create_internal(
oid, repo, git_buf_cstr(&full_path),
git_buf_cstr(&full_path) + strlen(workdir), true);
git_buf_free(&full_path); git_buf_free(&full_path);
return error; return error;
...@@ -231,13 +234,21 @@ int git_blob_create_fromdisk(git_oid *oid, git_repository *repo, const char *pat ...@@ -231,13 +234,21 @@ int git_blob_create_fromdisk(git_oid *oid, git_repository *repo, const char *pat
{ {
int error; int error;
git_buf full_path = GIT_BUF_INIT; git_buf full_path = GIT_BUF_INIT;
const char *workdir, *hintpath;
if ((error = git_path_prettify(&full_path, path, NULL)) < 0) { if ((error = git_path_prettify(&full_path, path, NULL)) < 0) {
git_buf_free(&full_path); git_buf_free(&full_path);
return error; return error;
} }
error = blob_create_internal(oid, repo, git_buf_cstr(&full_path), git_buf_cstr(&full_path), true); hintpath = git_buf_cstr(&full_path);
workdir = git_repository_workdir(repo);
if (workdir && !git__prefixcmp(hintpath, workdir))
hintpath += strlen(workdir);
error = blob_create_internal(
oid, repo, git_buf_cstr(&full_path), hintpath, true);
git_buf_free(&full_path); git_buf_free(&full_path);
return error; return error;
......
...@@ -60,6 +60,83 @@ void git_buf_text_unescape(git_buf *buf) ...@@ -60,6 +60,83 @@ void git_buf_text_unescape(git_buf *buf)
buf->size = git__unescape(buf->ptr); buf->size = git__unescape(buf->ptr);
} }
int git_buf_text_crlf_to_lf(git_buf *tgt, const git_buf *src)
{
const char *scan = src->ptr;
const char *scan_end = src->ptr + src->size;
const char *next = memchr(scan, '\r', src->size);
char *out;
assert(tgt != src);
if (!next)
return GIT_ENOTFOUND;
/* reduce reallocs while in the loop */
if (git_buf_grow(tgt, src->size) < 0)
return -1;
out = tgt->ptr;
tgt->size = 0;
/* Find the next \r and copy whole chunk up to there to tgt */
for (; next; scan = next + 1, next = memchr(scan, '\r', scan_end - scan)) {
if (next > scan) {
size_t copylen = next - scan;
memcpy(out, scan, copylen);
out += copylen;
}
/* Do not drop \r unless it is followed by \n */
if (next[1] != '\n')
*out++ = '\r';
}
/* Copy remaining input into dest */
memcpy(out, scan, scan_end - scan + 1); /* +1 for NUL byte */
out += (scan_end - scan);
tgt->size = out - tgt->ptr;
return 0;
}
int git_buf_text_lf_to_crlf(git_buf *tgt, const git_buf *src)
{
const char *start = src->ptr;
const char *end = start + src->size;
const char *scan = start;
const char *next = memchr(scan, '\n', src->size);
assert(tgt != src);
if (!next)
return GIT_ENOTFOUND;
/* attempt to reduce reallocs while in the loop */
if (git_buf_grow(tgt, src->size + (src->size >> 4) + 1) < 0)
return -1;
tgt->size = 0;
for (; next; scan = next + 1, next = memchr(scan, '\n', end - scan)) {
size_t copylen = next - scan;
/* don't convert existing \r\n to \r\r\n */
size_t extralen = (next > start && next[-1] == '\r') ? 1 : 2;
size_t needsize = tgt->size + copylen + extralen + 1;
if (tgt->asize < needsize && git_buf_grow(tgt, needsize) < 0)
return -1;
if (next > scan) {
memcpy(tgt->ptr + tgt->size, scan, copylen);
tgt->size += copylen;
}
if (extralen == 2)
tgt->ptr[tgt->size++] = '\r';
tgt->ptr[tgt->size++] = '\n';
}
return git_buf_put(tgt, scan, end - scan);
}
int git_buf_text_common_prefix(git_buf *buf, const git_strarray *strings) int git_buf_text_common_prefix(git_buf *buf, const git_strarray *strings)
{ {
size_t i; size_t i;
......
...@@ -56,6 +56,20 @@ GIT_INLINE(int) git_buf_text_puts_escape_regex(git_buf *buf, const char *string) ...@@ -56,6 +56,20 @@ GIT_INLINE(int) git_buf_text_puts_escape_regex(git_buf *buf, const char *string)
extern void git_buf_text_unescape(git_buf *buf); extern void git_buf_text_unescape(git_buf *buf);
/** /**
* Replace all \r\n with \n (or do nothing if no \r\n are found)
*
* @return 0 on success, GIT_ENOTFOUND if no \r\n, -1 on memory error
*/
extern int git_buf_text_crlf_to_lf(git_buf *tgt, const git_buf *src);
/**
* Replace all \n with \r\n (or do nothing if no \n are found)
*
* @return 0 on success, GIT_ENOTFOUND if no \n, -1 on memory error
*/
extern int git_buf_text_lf_to_crlf(git_buf *tgt, const git_buf *src);
/**
* Fill buffer with the common prefix of a array of strings * Fill buffer with the common prefix of a array of strings
* *
* Buffer will be set to empty if there is no common prefix * Buffer will be set to empty if there is no common prefix
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include "blob.h" #include "blob.h"
#include "diff.h" #include "diff.h"
#include "pathspec.h" #include "pathspec.h"
#include "buf_text.h"
/* See docs/checkout-internals.md for more information */ /* See docs/checkout-internals.md for more information */
......
...@@ -9,9 +9,10 @@ ...@@ -9,9 +9,10 @@
#include "fileops.h" #include "fileops.h"
#include "hash.h" #include "hash.h"
#include "filter.h" #include "filter.h"
#include "buf_text.h"
#include "repository.h" #include "repository.h"
#include "git2/attr.h" #include "git2/attr.h"
#include "git2/blob.h"
struct crlf_attrs { struct crlf_attrs {
int crlf_action; int crlf_action;
...@@ -21,6 +22,8 @@ struct crlf_attrs { ...@@ -21,6 +22,8 @@ struct crlf_attrs {
struct crlf_filter { struct crlf_filter {
git_filter f; git_filter f;
struct crlf_attrs attrs; struct crlf_attrs attrs;
git_repository *repo;
char path[GIT_FLEX_ARRAY];
}; };
static int check_crlf(const char *value) static int check_crlf(const char *value)
...@@ -103,36 +106,46 @@ static int crlf_load_attributes(struct crlf_attrs *ca, git_repository *repo, con ...@@ -103,36 +106,46 @@ static int crlf_load_attributes(struct crlf_attrs *ca, git_repository *repo, con
return -1; return -1;
} }
static int drop_crlf(git_buf *dest, const git_buf *source) static int has_cr_in_index(git_filter *self)
{ {
const char *scan = source->ptr, *next; struct crlf_filter *filter = (struct crlf_filter *)self;
const char *scan_end = git_buf_cstr(source) + git_buf_len(source); git_index *index;
const git_index_entry *entry;
git_blob *blob;
const void *blobcontent;
git_off_t blobsize;
bool found_cr;
if (git_repository_index__weakptr(&index, filter->repo) < 0) {
giterr_clear();
return false;
}
/* Main scan loop. Find the next carriage return and copy the if (!(entry = git_index_get_bypath(index, filter->path, 0)) &&
* whole chunk up to that point to the destination buffer. !(entry = git_index_get_bypath(index, filter->path, 1)))
*/ return false;
while ((next = memchr(scan, '\r', scan_end - scan)) != NULL) {
/* copy input up to \r */
if (next > scan)
git_buf_put(dest, scan, next - scan);
/* Do not drop \r unless it is followed by \n */ if (!S_ISREG(entry->mode)) /* don't crlf filter non-blobs */
if (*(next + 1) != '\n') return true;
git_buf_putc(dest, '\r');
scan = next + 1; if (git_blob_lookup(&blob, filter->repo, &entry->oid) < 0)
} return false;
/* If there was no \r, then tell the library to skip this filter */ blobcontent = git_blob_rawcontent(blob);
if (scan == source->ptr) blobsize = git_blob_rawsize(blob);
return -1; if (!git__is_sizet(blobsize))
blobsize = (size_t)-1;
/* Copy remaining input into dest */ found_cr = (blobcontent != NULL &&
git_buf_put(dest, scan, scan_end - scan); blobsize > 0 &&
return 0; memchr(blobcontent, '\r', (size_t)blobsize) != NULL);
git_blob_free(blob);
return found_cr;
} }
static int crlf_apply_to_odb(git_filter *self, git_buf *dest, const git_buf *source) static int crlf_apply_to_odb(
git_filter *self, git_buf *dest, const git_buf *source)
{ {
struct crlf_filter *filter = (struct crlf_filter *)self; struct crlf_filter *filter = (struct crlf_filter *)self;
...@@ -162,40 +175,21 @@ static int crlf_apply_to_odb(git_filter *self, git_buf *dest, const git_buf *sou ...@@ -162,40 +175,21 @@ static int crlf_apply_to_odb(git_filter *self, git_buf *dest, const git_buf *sou
if (stats.cr != stats.crlf) if (stats.cr != stats.crlf)
return -1; return -1;
#if 0 if (filter->attrs.crlf_action == GIT_CRLF_GUESS) {
if (crlf_action == CRLF_GUESS) {
/* /*
* If the file in the index has any CR in it, do not convert. * If the file in the index has any CR in it, do not convert.
* This is the new safer autocrlf handling. * This is the new safer autocrlf handling.
*/ */
if (has_cr_in_index(path)) if (has_cr_in_index(self))
return 0; return -1;
} }
#endif
if (!stats.cr) if (!stats.cr)
return -1; return -1;
} }
/* Actually drop the carriage returns */ /* Actually drop the carriage returns */
return drop_crlf(dest, source); return git_buf_text_crlf_to_lf(dest, source);
}
static int convert_line_endings(git_buf *dest, const git_buf *source, const char *ending)
{
const char *scan = git_buf_cstr(source),
*next,
*scan_end = git_buf_cstr(source) + git_buf_len(source);
while ((next = memchr(scan, '\n', scan_end - scan)) != NULL) {
if (next > scan)
git_buf_put(dest, scan, next-scan);
git_buf_puts(dest, ending);
scan = next + 1;
}
git_buf_put(dest, scan, scan_end - scan);
return 0;
} }
static const char *line_ending(struct crlf_filter *filter) static const char *line_ending(struct crlf_filter *filter)
...@@ -238,26 +232,28 @@ line_ending_error: ...@@ -238,26 +232,28 @@ line_ending_error:
return NULL; return NULL;
} }
static int crlf_apply_to_workdir(git_filter *self, git_buf *dest, const git_buf *source) static int crlf_apply_to_workdir(
git_filter *self, git_buf *dest, const git_buf *source)
{ {
struct crlf_filter *filter = (struct crlf_filter *)self; struct crlf_filter *filter = (struct crlf_filter *)self;
const char *workdir_ending = NULL; const char *workdir_ending = NULL;
assert (self && dest && source); assert(self && dest && source);
/* Empty file? Nothing to do. */ /* Empty file? Nothing to do. */
if (git_buf_len(source) == 0) if (git_buf_len(source) == 0)
return 0; return -1;
/* Determine proper line ending */ /* Determine proper line ending */
workdir_ending = line_ending(filter); workdir_ending = line_ending(filter);
if (!workdir_ending) return -1; if (!workdir_ending)
return -1;
/* If the line ending is '\n', just copy the input */ if (!strcmp("\n", workdir_ending)) /* do nothing for \n ending */
if (!strcmp(workdir_ending, "\n")) return -1;
return git_buf_puts(dest, git_buf_cstr(source));
return convert_line_endings(dest, source, workdir_ending); /* for now, only lf->crlf conversion is supported here */
assert(!strcmp("\r\n", workdir_ending));
return git_buf_text_lf_to_crlf(dest, source);
} }
static int find_and_add_filter( static int find_and_add_filter(
...@@ -266,6 +262,7 @@ static int find_and_add_filter( ...@@ -266,6 +262,7 @@ static int find_and_add_filter(
{ {
struct crlf_attrs ca; struct crlf_attrs ca;
struct crlf_filter *filter; struct crlf_filter *filter;
size_t pathlen;
int error; int error;
/* Load gitattributes for the path */ /* Load gitattributes for the path */
...@@ -293,22 +290,27 @@ static int find_and_add_filter( ...@@ -293,22 +290,27 @@ static int find_and_add_filter(
/* If we're good, we create a new filter object and push it /* If we're good, we create a new filter object and push it
* into the filters array */ * into the filters array */
filter = git__malloc(sizeof(struct crlf_filter)); pathlen = strlen(path);
filter = git__malloc(sizeof(struct crlf_filter) + pathlen + 1);
GITERR_CHECK_ALLOC(filter); GITERR_CHECK_ALLOC(filter);
filter->f.apply = apply; filter->f.apply = apply;
filter->f.do_free = NULL; filter->f.do_free = NULL;
memcpy(&filter->attrs, &ca, sizeof(struct crlf_attrs)); memcpy(&filter->attrs, &ca, sizeof(struct crlf_attrs));
filter->repo = repo;
memcpy(filter->path, path, pathlen + 1);
return git_vector_insert(filters, filter); return git_vector_insert(filters, filter);
} }
int git_filter_add__crlf_to_odb(git_vector *filters, git_repository *repo, const char *path) int git_filter_add__crlf_to_odb(
git_vector *filters, git_repository *repo, const char *path)
{ {
return find_and_add_filter(filters, repo, path, &crlf_apply_to_odb); return find_and_add_filter(filters, repo, path, &crlf_apply_to_odb);
} }
int git_filter_add__crlf_to_workdir(git_vector *filters, git_repository *repo, const char *path) int git_filter_add__crlf_to_workdir(
git_vector *filters, git_repository *repo, const char *path)
{ {
return find_and_add_filter(filters, repo, path, &crlf_apply_to_workdir); return find_and_add_filter(filters, repo, path, &crlf_apply_to_workdir);
} }
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <ctype.h> #include <ctype.h>
#include "fileops.h" #include "fileops.h"
#include "filter.h" #include "filter.h"
#include "buf_text.h"
static int read_next_int(const char **str, int *value) static int read_next_int(const char **str, int *value)
{ {
......
...@@ -9,7 +9,6 @@ ...@@ -9,7 +9,6 @@
#include "common.h" #include "common.h"
#include "buffer.h" #include "buffer.h"
#include "buf_text.h"
#include "git2/odb.h" #include "git2/odb.h"
#include "git2/repository.h" #include "git2/repository.h"
......
...@@ -50,35 +50,44 @@ void reset_index_to_treeish(git_object *treeish) ...@@ -50,35 +50,44 @@ void reset_index_to_treeish(git_object *treeish)
git_index_free(index); git_index_free(index);
} }
static void test_file_contents_internal( static void check_file_contents_internal(
const char *path, const char *expectedcontents, bool strip_cr) const char *path,
const char *expected_content,
bool strip_cr,
const char *file,
int line,
const char *msg)
{ {
int fd; int fd;
char data[1024] = {0}; char data[1024] = {0};
git_buf buf = GIT_BUF_INIT; git_buf buf = GIT_BUF_INIT;
size_t expectedlen = strlen(expectedcontents); size_t expected_len = expected_content ? strlen(expected_content) : 0;
fd = p_open(path, O_RDONLY); fd = p_open(path, O_RDONLY);
cl_assert(fd >= 0); cl_assert(fd >= 0);
buf.ptr = data; buf.ptr = data;
buf.size = p_read(fd, buf.ptr, 1024); buf.size = p_read(fd, buf.ptr, sizeof(data));
cl_git_pass(p_close(fd)); cl_git_pass(p_close(fd));
if (strip_cr) if (strip_cr)
strip_cr_from_buf(&buf); strip_cr_from_buf(&buf);
cl_assert_equal_i((int)expectedlen, (int)buf.size); clar__assert_equal_i((int)expected_len, (int)buf.size, file, line, "strlen(expected_content) != strlen(actual_content)", 1);
cl_assert_equal_s(expectedcontents, buf.ptr); clar__assert_equal_s(expected_content, buf.ptr, file, line, msg, 1);
} }
void test_file_contents(const char *path, const char *expected) void check_file_contents_at_line(
const char *path, const char *expected,
const char *file, int line, const char *msg)
{ {
test_file_contents_internal(path, expected, false); check_file_contents_internal(path, expected, false, file, line, msg);
} }
void test_file_contents_nocr(const char *path, const char *expected) void check_file_contents_nocr_at_line(
const char *path, const char *expected,
const char *file, int line, const char *msg)
{ {
test_file_contents_internal(path, expected, true); check_file_contents_internal(path, expected, true, file, line, msg);
} }
...@@ -5,5 +5,17 @@ ...@@ -5,5 +5,17 @@
extern void strip_cr_from_buf(git_buf *buf); extern void strip_cr_from_buf(git_buf *buf);
extern void assert_on_branch(git_repository *repo, const char *branch); extern void assert_on_branch(git_repository *repo, const char *branch);
extern void reset_index_to_treeish(git_object *treeish); extern void reset_index_to_treeish(git_object *treeish);
extern void test_file_contents(const char *path, const char *expected);
extern void test_file_contents_nocr(const char *path, const char *expected); extern void check_file_contents_at_line(
const char *path, const char *expected,
const char *file, int line, const char *msg);
extern void check_file_contents_nocr_at_line(
const char *path, const char *expected,
const char *file, int line, const char *msg);
#define check_file_contents(PATH,EXP) \
check_file_contents_at_line(PATH,EXP,__FILE__,__LINE__,"String mismatch: " #EXP " != " #PATH)
#define check_file_contents_nocr(PATH,EXP) \
check_file_contents_nocr_at_line(PATH,EXP,__FILE__,__LINE__,"String mismatch: " #EXP " != " #PATH)
...@@ -11,6 +11,8 @@ ...@@ -11,6 +11,8 @@
#define MORE_LF_TEXT_RAW "lf\nlf\ncrlf\r\nlf\nlf\n" #define MORE_LF_TEXT_RAW "lf\nlf\ncrlf\r\nlf\nlf\n"
#define ALL_LF_TEXT_AS_CRLF "lf\r\nlf\r\nlf\r\nlf\r\nlf\r\n" #define ALL_LF_TEXT_AS_CRLF "lf\r\nlf\r\nlf\r\nlf\r\nlf\r\n"
#define MORE_CRLF_TEXT_AS_CRLF "crlf\r\ncrlf\r\nlf\r\ncrlf\r\ncrlf\r\n"
#define MORE_LF_TEXT_AS_CRLF "lf\r\nlf\r\ncrlf\r\nlf\r\nlf\r\n"
static git_repository *g_repo; static git_repository *g_repo;
...@@ -26,7 +28,6 @@ void test_checkout_crlf__cleanup(void) ...@@ -26,7 +28,6 @@ void test_checkout_crlf__cleanup(void)
void test_checkout_crlf__detect_crlf_autocrlf_false(void) void test_checkout_crlf__detect_crlf_autocrlf_false(void)
{ {
#ifdef GIT_WIN32
git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE; opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
...@@ -34,13 +35,12 @@ void test_checkout_crlf__detect_crlf_autocrlf_false(void) ...@@ -34,13 +35,12 @@ void test_checkout_crlf__detect_crlf_autocrlf_false(void)
git_checkout_head(g_repo, &opts); git_checkout_head(g_repo, &opts);
test_file_contents("./crlf/all-lf", ALL_LF_TEXT_RAW); check_file_contents("./crlf/all-lf", ALL_LF_TEXT_RAW);
#endif check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_RAW);
} }
void test_checkout_crlf__autocrlf_false_index_size_is_unfiltered_size(void) void test_checkout_crlf__autocrlf_false_index_size_is_unfiltered_size(void)
{ {
#ifdef GIT_WIN32
git_index *index; git_index *index;
const git_index_entry *entry; const git_index_entry *entry;
git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
...@@ -55,13 +55,14 @@ void test_checkout_crlf__autocrlf_false_index_size_is_unfiltered_size(void) ...@@ -55,13 +55,14 @@ void test_checkout_crlf__autocrlf_false_index_size_is_unfiltered_size(void)
cl_assert((entry = git_index_get_bypath(index, "all-lf", 0)) != NULL); cl_assert((entry = git_index_get_bypath(index, "all-lf", 0)) != NULL);
cl_assert(entry->file_size == strlen(ALL_LF_TEXT_RAW)); cl_assert(entry->file_size == strlen(ALL_LF_TEXT_RAW));
cl_assert((entry = git_index_get_bypath(index, "all-crlf", 0)) != NULL);
cl_assert(entry->file_size == strlen(ALL_CRLF_TEXT_RAW));
git_index_free(index); git_index_free(index);
#endif
} }
void test_checkout_crlf__detect_crlf_autocrlf_true(void) void test_checkout_crlf__detect_crlf_autocrlf_true(void)
{ {
#ifdef GIT_WIN32
git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE; opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
...@@ -69,13 +70,58 @@ void test_checkout_crlf__detect_crlf_autocrlf_true(void) ...@@ -69,13 +70,58 @@ void test_checkout_crlf__detect_crlf_autocrlf_true(void)
git_checkout_head(g_repo, &opts); git_checkout_head(g_repo, &opts);
test_file_contents("./crlf/all-lf", ALL_LF_TEXT_AS_CRLF); if (GIT_EOL_NATIVE == GIT_EOL_LF)
#endif check_file_contents("./crlf/all-lf", ALL_LF_TEXT_RAW);
else
check_file_contents("./crlf/all-lf", ALL_LF_TEXT_AS_CRLF);
check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_RAW);
}
void test_checkout_crlf__more_lf_autocrlf_true(void)
{
git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
cl_repo_set_bool(g_repo, "core.autocrlf", true);
git_checkout_head(g_repo, &opts);
if (GIT_EOL_NATIVE == GIT_EOL_LF)
check_file_contents("./crlf/more-lf", MORE_LF_TEXT_RAW);
else
check_file_contents("./crlf/more-lf", MORE_LF_TEXT_AS_CRLF);
}
void test_checkout_crlf__more_crlf_autocrlf_true(void)
{
git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
cl_repo_set_bool(g_repo, "core.autocrlf", true);
git_checkout_head(g_repo, &opts);
if (GIT_EOL_NATIVE == GIT_EOL_LF)
check_file_contents("./crlf/more-crlf", MORE_CRLF_TEXT_RAW);
else
check_file_contents("./crlf/more-crlf", MORE_CRLF_TEXT_AS_CRLF);
}
void test_checkout_crlf__all_crlf_autocrlf_true(void)
{
git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
cl_repo_set_bool(g_repo, "core.autocrlf", true);
git_checkout_head(g_repo, &opts);
check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_RAW);
} }
void test_checkout_crlf__autocrlf_true_index_size_is_filtered_size(void) void test_checkout_crlf__autocrlf_true_index_size_is_filtered_size(void)
{ {
#ifdef GIT_WIN32
git_index *index; git_index *index;
const git_index_entry *entry; const git_index_entry *entry;
git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
...@@ -88,8 +134,14 @@ void test_checkout_crlf__autocrlf_true_index_size_is_filtered_size(void) ...@@ -88,8 +134,14 @@ void test_checkout_crlf__autocrlf_true_index_size_is_filtered_size(void)
git_repository_index(&index, g_repo); git_repository_index(&index, g_repo);
cl_assert((entry = git_index_get_bypath(index, "all-lf", 0)) != NULL); cl_assert((entry = git_index_get_bypath(index, "all-lf", 0)) != NULL);
cl_assert(entry->file_size == strlen(ALL_LF_TEXT_AS_CRLF));
if (GIT_EOL_NATIVE == GIT_EOL_LF)
cl_assert_equal_sz(strlen(ALL_LF_TEXT_RAW), entry->file_size);
else
cl_assert_equal_sz(strlen(ALL_LF_TEXT_AS_CRLF), entry->file_size);
cl_assert((entry = git_index_get_bypath(index, "all-crlf", 0)) != NULL);
cl_assert_equal_sz(strlen(ALL_CRLF_TEXT_RAW), entry->file_size);
git_index_free(index); git_index_free(index);
#endif
} }
...@@ -48,9 +48,9 @@ void test_checkout_index__can_create_missing_files(void) ...@@ -48,9 +48,9 @@ void test_checkout_index__can_create_missing_files(void)
cl_git_pass(git_checkout_index(g_repo, NULL, &opts)); cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
test_file_contents("./testrepo/README", "hey there\n"); check_file_contents("./testrepo/README", "hey there\n");
test_file_contents("./testrepo/branch_file.txt", "hi\nbye!\n"); check_file_contents("./testrepo/branch_file.txt", "hi\nbye!\n");
test_file_contents("./testrepo/new.txt", "my new file\n"); check_file_contents("./testrepo/new.txt", "my new file\n");
} }
void test_checkout_index__can_remove_untracked_files(void) void test_checkout_index__can_remove_untracked_files(void)
...@@ -88,8 +88,8 @@ void test_checkout_index__honor_the_specified_pathspecs(void) ...@@ -88,8 +88,8 @@ void test_checkout_index__honor_the_specified_pathspecs(void)
cl_git_pass(git_checkout_index(g_repo, NULL, &opts)); cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
cl_assert_equal_i(false, git_path_isfile("./testrepo/README")); cl_assert_equal_i(false, git_path_isfile("./testrepo/README"));
test_file_contents("./testrepo/branch_file.txt", "hi\nbye!\n"); check_file_contents("./testrepo/branch_file.txt", "hi\nbye!\n");
test_file_contents("./testrepo/new.txt", "my new file\n"); check_file_contents("./testrepo/new.txt", "my new file\n");
} }
void test_checkout_index__honor_the_gitattributes_directives(void) void test_checkout_index__honor_the_gitattributes_directives(void)
...@@ -106,9 +106,9 @@ void test_checkout_index__honor_the_gitattributes_directives(void) ...@@ -106,9 +106,9 @@ void test_checkout_index__honor_the_gitattributes_directives(void)
cl_git_pass(git_checkout_index(g_repo, NULL, &opts)); cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
test_file_contents("./testrepo/README", "hey there\n"); check_file_contents("./testrepo/README", "hey there\n");
test_file_contents("./testrepo/new.txt", "my new file\n"); check_file_contents("./testrepo/new.txt", "my new file\n");
test_file_contents("./testrepo/branch_file.txt", "hi\r\nbye!\r\n"); check_file_contents("./testrepo/branch_file.txt", "hi\r\nbye!\r\n");
} }
void test_checkout_index__honor_coreautocrlf_setting_set_to_true(void) void test_checkout_index__honor_coreautocrlf_setting_set_to_true(void)
...@@ -124,7 +124,7 @@ void test_checkout_index__honor_coreautocrlf_setting_set_to_true(void) ...@@ -124,7 +124,7 @@ void test_checkout_index__honor_coreautocrlf_setting_set_to_true(void)
cl_git_pass(git_checkout_index(g_repo, NULL, &opts)); cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
test_file_contents("./testrepo/README", expected_readme_text); check_file_contents("./testrepo/README", expected_readme_text);
#endif #endif
} }
...@@ -139,7 +139,7 @@ void test_checkout_index__honor_coresymlinks_setting_set_to_true(void) ...@@ -139,7 +139,7 @@ void test_checkout_index__honor_coresymlinks_setting_set_to_true(void)
cl_git_pass(git_checkout_index(g_repo, NULL, &opts)); cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
#ifdef GIT_WIN32 #ifdef GIT_WIN32
test_file_contents("./testrepo/link_to_new.txt", "new.txt"); check_file_contents("./testrepo/link_to_new.txt", "new.txt");
#else #else
{ {
char link_data[1024]; char link_data[1024];
...@@ -149,7 +149,7 @@ void test_checkout_index__honor_coresymlinks_setting_set_to_true(void) ...@@ -149,7 +149,7 @@ void test_checkout_index__honor_coresymlinks_setting_set_to_true(void)
link_data[link_size] = '\0'; link_data[link_size] = '\0';
cl_assert_equal_i(link_size, strlen("new.txt")); cl_assert_equal_i(link_size, strlen("new.txt"));
cl_assert_equal_s(link_data, "new.txt"); cl_assert_equal_s(link_data, "new.txt");
test_file_contents("./testrepo/link_to_new.txt", "my new file\n"); check_file_contents("./testrepo/link_to_new.txt", "my new file\n");
} }
#endif #endif
} }
...@@ -164,7 +164,7 @@ void test_checkout_index__honor_coresymlinks_setting_set_to_false(void) ...@@ -164,7 +164,7 @@ void test_checkout_index__honor_coresymlinks_setting_set_to_false(void)
cl_git_pass(git_checkout_index(g_repo, NULL, &opts)); cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
test_file_contents("./testrepo/link_to_new.txt", "new.txt"); check_file_contents("./testrepo/link_to_new.txt", "new.txt");
} }
void test_checkout_index__donot_overwrite_modified_file_by_default(void) void test_checkout_index__donot_overwrite_modified_file_by_default(void)
...@@ -180,7 +180,7 @@ void test_checkout_index__donot_overwrite_modified_file_by_default(void) ...@@ -180,7 +180,7 @@ void test_checkout_index__donot_overwrite_modified_file_by_default(void)
cl_git_pass(git_checkout_index(g_repo, NULL, &opts)); cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
test_file_contents("./testrepo/new.txt", "This isn't what's stored!"); check_file_contents("./testrepo/new.txt", "This isn't what's stored!");
} }
void test_checkout_index__can_overwrite_modified_file(void) void test_checkout_index__can_overwrite_modified_file(void)
...@@ -193,7 +193,7 @@ void test_checkout_index__can_overwrite_modified_file(void) ...@@ -193,7 +193,7 @@ void test_checkout_index__can_overwrite_modified_file(void)
cl_git_pass(git_checkout_index(g_repo, NULL, &opts)); cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
test_file_contents("./testrepo/new.txt", "my new file\n"); check_file_contents("./testrepo/new.txt", "my new file\n");
} }
void test_checkout_index__options_disable_filters(void) void test_checkout_index__options_disable_filters(void)
...@@ -207,14 +207,14 @@ void test_checkout_index__options_disable_filters(void) ...@@ -207,14 +207,14 @@ void test_checkout_index__options_disable_filters(void)
cl_git_pass(git_checkout_index(g_repo, NULL, &opts)); cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
test_file_contents("./testrepo/new.txt", "my new file\r\n"); check_file_contents("./testrepo/new.txt", "my new file\r\n");
p_unlink("./testrepo/new.txt"); p_unlink("./testrepo/new.txt");
opts.disable_filters = true; opts.disable_filters = true;
cl_git_pass(git_checkout_index(g_repo, NULL, &opts)); cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
test_file_contents("./testrepo/new.txt", "my new file\n"); check_file_contents("./testrepo/new.txt", "my new file\n");
} }
void test_checkout_index__options_dir_modes(void) void test_checkout_index__options_dir_modes(void)
...@@ -274,7 +274,7 @@ void test_checkout_index__options_open_flags(void) ...@@ -274,7 +274,7 @@ void test_checkout_index__options_open_flags(void)
opts.checkout_strategy = GIT_CHECKOUT_FORCE; opts.checkout_strategy = GIT_CHECKOUT_FORCE;
cl_git_pass(git_checkout_index(g_repo, NULL, &opts)); cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
test_file_contents("./testrepo/new.txt", "hi\nmy new file\n"); check_file_contents("./testrepo/new.txt", "hi\nmy new file\n");
} }
struct notify_data { struct notify_data {
...@@ -469,9 +469,9 @@ void test_checkout_index__can_update_prefixed_files(void) ...@@ -469,9 +469,9 @@ void test_checkout_index__can_update_prefixed_files(void)
/* remove untracked will remove the .gitattributes file before the blobs /* remove untracked will remove the .gitattributes file before the blobs
* were created, so they will have had crlf filtering applied on Windows * were created, so they will have had crlf filtering applied on Windows
*/ */
test_file_contents_nocr("./testrepo/README", "hey there\n"); check_file_contents_nocr("./testrepo/README", "hey there\n");
test_file_contents_nocr("./testrepo/branch_file.txt", "hi\nbye!\n"); check_file_contents_nocr("./testrepo/branch_file.txt", "hi\nbye!\n");
test_file_contents_nocr("./testrepo/new.txt", "my new file\n"); check_file_contents_nocr("./testrepo/new.txt", "my new file\n");
cl_assert(!git_path_exists("testrepo/READ")); cl_assert(!git_path_exists("testrepo/READ"));
cl_assert(!git_path_exists("testrepo/README.after")); cl_assert(!git_path_exists("testrepo/README.after"));
...@@ -488,3 +488,20 @@ void test_checkout_index__can_checkout_a_newly_initialized_repository(void) ...@@ -488,3 +488,20 @@ void test_checkout_index__can_checkout_a_newly_initialized_repository(void)
cl_git_pass(git_checkout_index(g_repo, NULL, NULL)); cl_git_pass(git_checkout_index(g_repo, NULL, NULL));
} }
void test_checkout_index__issue_1397(void)
{
git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
test_checkout_index__cleanup();
g_repo = cl_git_sandbox_init("issue_1397");
cl_repo_set_bool(g_repo, "core.autocrlf", true);
opts.checkout_strategy = GIT_CHECKOUT_FORCE;
cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
check_file_contents("./issue_1397/crlf_file.txt", "first line\r\nsecond line\r\nboth with crlf");
}
...@@ -248,7 +248,7 @@ void test_checkout_tree__can_update_only(void) ...@@ -248,7 +248,7 @@ void test_checkout_tree__can_update_only(void)
cl_assert(!git_path_isdir("testrepo/a")); cl_assert(!git_path_isdir("testrepo/a"));
test_file_contents_nocr("testrepo/branch_file.txt", "hi\nbye!\n"); check_file_contents_nocr("testrepo/branch_file.txt", "hi\nbye!\n");
/* now checkout branch but with update only */ /* now checkout branch but with update only */
...@@ -269,7 +269,7 @@ void test_checkout_tree__can_update_only(void) ...@@ -269,7 +269,7 @@ void test_checkout_tree__can_update_only(void)
cl_assert(!git_path_isdir("testrepo/a")); cl_assert(!git_path_isdir("testrepo/a"));
/* but this file still should have been updated */ /* but this file still should have been updated */
test_file_contents_nocr("testrepo/branch_file.txt", "hi\n"); check_file_contents_nocr("testrepo/branch_file.txt", "hi\n");
git_object_free(obj); git_object_free(obj);
} }
...@@ -481,3 +481,26 @@ void test_checkout_tree__can_checkout_with_last_workdir_item_missing(void) ...@@ -481,3 +481,26 @@ void test_checkout_tree__can_checkout_with_last_workdir_item_missing(void)
git_commit_free(commit); git_commit_free(commit);
git_index_free(index); git_index_free(index);
} }
void test_checkout_tree__issue_1397(void)
{
git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
const char *partial_oid = "8a7ef04";
git_object *tree = NULL;
test_checkout_tree__cleanup(); /* cleanup default checkout */
g_repo = cl_git_sandbox_init("issue_1397");
cl_repo_set_bool(g_repo, "core.autocrlf", true);
cl_git_pass(git_revparse_single(&tree, g_repo, partial_oid));
opts.checkout_strategy = GIT_CHECKOUT_FORCE;
cl_git_pass(git_checkout_tree(g_repo, tree, &opts));
check_file_contents("./issue_1397/crlf_file.txt", "first line\r\nsecond line\r\nboth with crlf");
git_object_free(tree);
}
...@@ -904,3 +904,85 @@ void test_core_buffer__similarity_metric_whitespace(void) ...@@ -904,3 +904,85 @@ void test_core_buffer__similarity_metric_whitespace(void)
git_buf_free(&buf); git_buf_free(&buf);
} }
#define check_buf(expected,buf) do { \
cl_assert_equal_s(expected, buf.ptr); \
cl_assert_equal_sz(strlen(expected), buf.size); } while (0)
void test_core_buffer__lf_and_crlf_conversions(void)
{
git_buf src = GIT_BUF_INIT, tgt = GIT_BUF_INIT;
/* LF source */
git_buf_sets(&src, "lf\nlf\nlf\nlf\n");
cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
check_buf("lf\r\nlf\r\nlf\r\nlf\r\n", tgt);
cl_assert_equal_i(GIT_ENOTFOUND, git_buf_text_crlf_to_lf(&tgt, &src));
/* no conversion needed if all LFs already */
git_buf_sets(&src, "\nlf\nlf\nlf\nlf\nlf");
cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
check_buf("\r\nlf\r\nlf\r\nlf\r\nlf\r\nlf", tgt);
cl_assert_equal_i(GIT_ENOTFOUND, git_buf_text_crlf_to_lf(&tgt, &src));
/* no conversion needed if all LFs already */
/* CRLF source */
git_buf_sets(&src, "crlf\r\ncrlf\r\ncrlf\r\ncrlf\r\n");
cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
check_buf("crlf\r\ncrlf\r\ncrlf\r\ncrlf\r\n", tgt);
check_buf(src.ptr, tgt);
cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
check_buf("crlf\ncrlf\ncrlf\ncrlf\n", tgt);
git_buf_sets(&src, "\r\ncrlf\r\ncrlf\r\ncrlf\r\ncrlf\r\ncrlf");
cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
check_buf("\r\ncrlf\r\ncrlf\r\ncrlf\r\ncrlf\r\ncrlf", tgt);
check_buf(src.ptr, tgt);
cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
check_buf("\ncrlf\ncrlf\ncrlf\ncrlf\ncrlf", tgt);
/* CRLF in LF text */
git_buf_sets(&src, "\nlf\nlf\ncrlf\r\nlf\nlf\ncrlf\r\n");
cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
check_buf("\r\nlf\r\nlf\r\ncrlf\r\nlf\r\nlf\r\ncrlf\r\n", tgt);
cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
check_buf("\nlf\nlf\ncrlf\nlf\nlf\ncrlf\n", tgt);
/* LF in CRLF text */
git_buf_sets(&src, "\ncrlf\r\ncrlf\r\nlf\ncrlf\r\ncrlf");
cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
check_buf("\r\ncrlf\r\ncrlf\r\nlf\r\ncrlf\r\ncrlf", tgt);
cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
check_buf("\ncrlf\ncrlf\nlf\ncrlf\ncrlf", tgt);
/* bare CR test */
git_buf_sets(&src, "\rcrlf\r\nlf\nlf\ncr\rcrlf\r\nlf\ncr\r");
cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
check_buf("\rcrlf\r\nlf\r\nlf\r\ncr\rcrlf\r\nlf\r\ncr\r", tgt);
cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
check_buf("\rcrlf\nlf\nlf\ncr\rcrlf\nlf\ncr\r", tgt);
git_buf_sets(&src, "\rcr\r");
cl_assert_equal_i(GIT_ENOTFOUND, git_buf_text_lf_to_crlf(&tgt, &src));
cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
check_buf("\rcr\r", tgt);
git_buf_free(&src);
git_buf_free(&tgt);
}
...@@ -431,3 +431,26 @@ void test_diff_tree__regular_blob_mode_changed_to_executable_file(void) ...@@ -431,3 +431,26 @@ void test_diff_tree__regular_blob_mode_changed_to_executable_file(void)
cl_assert_equal_i(0, expect.file_status[GIT_DELTA_ADDED]); cl_assert_equal_i(0, expect.file_status[GIT_DELTA_ADDED]);
cl_assert_equal_i(0, expect.file_status[GIT_DELTA_TYPECHANGE]); cl_assert_equal_i(0, expect.file_status[GIT_DELTA_TYPECHANGE]);
} }
void test_diff_tree__issue_1397(void)
{
/* this test shows that it is not needed */
g_repo = cl_git_sandbox_init("issue_1397");
cl_repo_set_bool(g_repo, "core.autocrlf", true);
cl_assert((a = resolve_commit_oid_to_tree(g_repo, "8a7ef04")) != NULL);
cl_assert((b = resolve_commit_oid_to_tree(g_repo, "7f483a7")) != NULL);
cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, a, b, &opts));
cl_git_pass(git_diff_foreach(
diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &expect));
cl_assert_equal_i(1, expect.files);
cl_assert_equal_i(0, expect.file_status[GIT_DELTA_DELETED]);
cl_assert_equal_i(1, expect.file_status[GIT_DELTA_MODIFIED]);
cl_assert_equal_i(0, expect.file_status[GIT_DELTA_ADDED]);
cl_assert_equal_i(0, expect.file_status[GIT_DELTA_TYPECHANGE]);
}
...@@ -1084,3 +1084,99 @@ void test_diff_workdir__can_diff_empty_file(void) ...@@ -1084,3 +1084,99 @@ void test_diff_workdir__can_diff_empty_file(void)
git_diff_patch_free(patch); git_diff_patch_free(patch);
git_diff_list_free(diff); git_diff_list_free(diff);
} }
void test_diff_workdir__to_index_issue_1397(void)
{
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
git_diff_list *diff = NULL;
diff_expects exp;
g_repo = cl_git_sandbox_init("issue_1397");
cl_repo_set_bool(g_repo, "core.autocrlf", true);
opts.context_lines = 3;
opts.interhunk_lines = 1;
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
memset(&exp, 0, sizeof(exp));
cl_git_pass(git_diff_foreach(
diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
cl_assert_equal_i(0, exp.files);
cl_assert_equal_i(0, exp.hunks);
cl_assert_equal_i(0, exp.lines);
git_diff_list_free(diff);
diff = NULL;
cl_git_rewritefile("issue_1397/crlf_file.txt",
"first line\r\nsecond line modified\r\nboth with crlf");
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
memset(&exp, 0, sizeof(exp));
cl_git_pass(git_diff_foreach(
diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
cl_assert_equal_i(1, exp.files);
cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
cl_assert_equal_i(1, exp.hunks);
cl_assert_equal_i(5, exp.lines);
cl_assert_equal_i(3, exp.line_ctxt);
cl_assert_equal_i(1, exp.line_adds);
cl_assert_equal_i(1, exp.line_dels);
git_diff_list_free(diff);
}
void test_diff_workdir__to_tree_issue_1397(void)
{
const char *a_commit = "7f483a738"; /* the current HEAD */
git_tree *a;
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
git_diff_list *diff = NULL;
git_diff_list *diff2 = NULL;
diff_expects exp;
g_repo = cl_git_sandbox_init("issue_1397");
cl_repo_set_bool(g_repo, "core.autocrlf", true);
a = resolve_commit_oid_to_tree(g_repo, a_commit);
opts.context_lines = 3;
opts.interhunk_lines = 1;
cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, a, &opts));
memset(&exp, 0, sizeof(exp));
cl_git_pass(git_diff_foreach(
diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
cl_assert_equal_i(0, exp.files);
cl_assert_equal_i(0, exp.hunks);
cl_assert_equal_i(0, exp.lines);
git_diff_list_free(diff);
diff = NULL;
cl_git_pass(git_diff_tree_to_index(&diff, g_repo, a, NULL, &opts));
cl_git_pass(git_diff_index_to_workdir(&diff2, g_repo, NULL, &opts));
cl_git_pass(git_diff_merge(diff, diff2));
git_diff_list_free(diff2);
memset(&exp, 0, sizeof(exp));
cl_git_pass(git_diff_foreach(
diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
cl_assert_equal_i(0, exp.files);
cl_assert_equal_i(0, exp.hunks);
cl_assert_equal_i(0, exp.lines);
git_diff_list_free(diff);
git_tree_free(a);
}
...@@ -251,6 +251,48 @@ void test_index_tests__add(void) ...@@ -251,6 +251,48 @@ void test_index_tests__add(void)
git_repository_free(repo); git_repository_free(repo);
} }
static void cleanup_1397(void *opaque)
{
GIT_UNUSED(opaque);
cl_git_sandbox_cleanup();
}
void test_index_tests__add_issue_1397(void)
{
git_index *index;
git_repository *repo;
const git_index_entry *entry;
git_oid id1;
cl_set_cleanup(&cleanup_1397, NULL);
repo = cl_git_sandbox_init("issue_1397");
cl_repo_set_bool(repo, "core.autocrlf", true);
/* Ensure we're the only guy in the room */
cl_git_pass(git_repository_index(&index, repo));
/* Store the expected hash of the file/blob
* This has been generated by executing the following
* $ git hash-object crlf_file.txt
*/
cl_git_pass(git_oid_fromstr(&id1, "8312e0889a9cbab77c732b6bc39b51a683e3a318"));
/* Make sure the initial SHA-1 is correct */
cl_assert((entry = git_index_get_bypath(index, "crlf_file.txt", 0)) != NULL);
cl_assert_(git_oid_cmp(&id1, &entry->oid) == 0, "first oid check");
/* Update the index */
cl_git_pass(git_index_add_bypath(index, "crlf_file.txt"));
/* Check the new SHA-1 */
cl_assert((entry = git_index_get_bypath(index, "crlf_file.txt", 0)) != NULL);
cl_assert_(git_oid_cmp(&id1, &entry->oid) == 0, "second oid check");
git_index_free(index);
}
void test_index_tests__add_bypath_to_a_bare_repository_returns_EBAREPO(void) void test_index_tests__add_bypath_to_a_bare_repository_returns_EBAREPO(void)
{ {
git_repository *bare_repo; git_repository *bare_repo;
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
#include "posix.h" #include "posix.h"
#include "blob.h" #include "blob.h"
#include "filter.h" #include "filter.h"
#include "buf_text.h"
static git_repository *g_repo = NULL; static git_repository *g_repo = NULL;
#define NUM_TEST_OBJECTS 8 #define NUM_TEST_OBJECTS 8
......
[core]
bare = false
repositoryformatversion = 0
filemode = false
logallrefupdates = true
ignorecase = true
7f483a738f867e5b21c8f377d70311f011eb48b5
first line
second line
both with crlf
\ No newline at end of file
first line
second line with some change
both with crlf
\ No newline at end of file
...@@ -541,6 +541,18 @@ void test_status_worktree__line_endings_dont_count_as_changes_with_autocrlf(void ...@@ -541,6 +541,18 @@ void test_status_worktree__line_endings_dont_count_as_changes_with_autocrlf(void
cl_assert_equal_i(GIT_STATUS_CURRENT, status); cl_assert_equal_i(GIT_STATUS_CURRENT, status);
} }
void test_status_worktree__line_endings_dont_count_as_changes_with_autocrlf_issue_1397(void)
{
git_repository *repo = cl_git_sandbox_init("issue_1397");
unsigned int status;
cl_repo_set_bool(repo, "core.autocrlf", true);
cl_git_pass(git_status_file(&status, repo, "crlf_file.txt"));
cl_assert_equal_i(GIT_STATUS_CURRENT, status);
}
void test_status_worktree__conflicted_item(void) void test_status_worktree__conflicted_item(void)
{ {
git_repository *repo = cl_git_sandbox_init("status"); git_repository *repo = cl_git_sandbox_init("status");
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment