summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/blob.c15
-rw-r--r--src/buf_text.c77
-rw-r--r--src/buf_text.h14
-rw-r--r--src/checkout.c1
-rw-r--r--src/crlf.c118
-rw-r--r--src/diff_output.c1
-rw-r--r--src/filter.h1
7 files changed, 166 insertions, 61 deletions
diff --git a/src/blob.c b/src/blob.c
index bcb6ac96b..c0514fc13 100644
--- a/src/blob.c
+++ b/src/blob.c
@@ -12,6 +12,7 @@
#include "common.h"
#include "blob.h"
#include "filter.h"
+#include "buf_text.h"
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 *
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);
return error;
@@ -231,13 +234,21 @@ int git_blob_create_fromdisk(git_oid *oid, git_repository *repo, const char *pat
{
int error;
git_buf full_path = GIT_BUF_INIT;
+ const char *workdir, *hintpath;
if ((error = git_path_prettify(&full_path, path, NULL)) < 0) {
git_buf_free(&full_path);
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);
return error;
diff --git a/src/buf_text.c b/src/buf_text.c
index 3a8f442b4..443454b5f 100644
--- a/src/buf_text.c
+++ b/src/buf_text.c
@@ -60,6 +60,83 @@ void git_buf_text_unescape(git_buf *buf)
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)
{
size_t i;
diff --git a/src/buf_text.h b/src/buf_text.h
index 458ee33c9..58e4e26a7 100644
--- a/src/buf_text.h
+++ b/src/buf_text.h
@@ -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);
/**
+ * 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
*
* Buffer will be set to empty if there is no common prefix
diff --git a/src/checkout.c b/src/checkout.c
index e52649aec..5a2698e41 100644
--- a/src/checkout.c
+++ b/src/checkout.c
@@ -23,6 +23,7 @@
#include "blob.h"
#include "diff.h"
#include "pathspec.h"
+#include "buf_text.h"
/* See docs/checkout-internals.md for more information */
diff --git a/src/crlf.c b/src/crlf.c
index 060d39d37..81268da83 100644
--- a/src/crlf.c
+++ b/src/crlf.c
@@ -9,9 +9,10 @@
#include "fileops.h"
#include "hash.h"
#include "filter.h"
+#include "buf_text.h"
#include "repository.h"
-
#include "git2/attr.h"
+#include "git2/blob.h"
struct crlf_attrs {
int crlf_action;
@@ -21,6 +22,8 @@ struct crlf_attrs {
struct crlf_filter {
git_filter f;
struct crlf_attrs attrs;
+ git_repository *repo;
+ char path[GIT_FLEX_ARRAY];
};
static int check_crlf(const char *value)
@@ -103,36 +106,46 @@ static int crlf_load_attributes(struct crlf_attrs *ca, git_repository *repo, con
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;
- const char *scan_end = git_buf_cstr(source) + git_buf_len(source);
+ struct crlf_filter *filter = (struct crlf_filter *)self;
+ 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
- * whole chunk up to that point to the destination buffer.
- */
- while ((next = memchr(scan, '\r', scan_end - scan)) != NULL) {
- /* copy input up to \r */
- if (next > scan)
- git_buf_put(dest, scan, next - scan);
+ if (!(entry = git_index_get_bypath(index, filter->path, 0)) &&
+ !(entry = git_index_get_bypath(index, filter->path, 1)))
+ return false;
- /* Do not drop \r unless it is followed by \n */
- if (*(next + 1) != '\n')
- git_buf_putc(dest, '\r');
+ if (!S_ISREG(entry->mode)) /* don't crlf filter non-blobs */
+ return true;
- 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 */
- if (scan == source->ptr)
- return -1;
+ blobcontent = git_blob_rawcontent(blob);
+ blobsize = git_blob_rawsize(blob);
+ if (!git__is_sizet(blobsize))
+ blobsize = (size_t)-1;
+
+ found_cr = (blobcontent != NULL &&
+ blobsize > 0 &&
+ memchr(blobcontent, '\r', (size_t)blobsize) != NULL);
- /* Copy remaining input into dest */
- git_buf_put(dest, scan, scan_end - scan);
- return 0;
+ 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;
@@ -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)
return -1;
-#if 0
- if (crlf_action == CRLF_GUESS) {
+ if (filter->attrs.crlf_action == GIT_CRLF_GUESS) {
/*
* If the file in the index has any CR in it, do not convert.
* This is the new safer autocrlf handling.
*/
- if (has_cr_in_index(path))
- return 0;
+ if (has_cr_in_index(self))
+ return -1;
}
-#endif
if (!stats.cr)
return -1;
}
/* Actually drop the carriage returns */
- return drop_crlf(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;
+ return git_buf_text_crlf_to_lf(dest, source);
}
static const char *line_ending(struct crlf_filter *filter)
@@ -238,26 +232,28 @@ line_ending_error:
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;
const char *workdir_ending = NULL;
- assert (self && dest && source);
+ assert(self && dest && source);
/* Empty file? Nothing to do. */
if (git_buf_len(source) == 0)
- return 0;
+ return -1;
/* Determine proper line ending */
workdir_ending = line_ending(filter);
- if (!workdir_ending) return -1;
-
- /* If the line ending is '\n', just copy the input */
- if (!strcmp(workdir_ending, "\n"))
- return git_buf_puts(dest, git_buf_cstr(source));
+ if (!workdir_ending)
+ return -1;
+ if (!strcmp("\n", workdir_ending)) /* do nothing for \n ending */
+ return -1;
- 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(
@@ -266,6 +262,7 @@ static int find_and_add_filter(
{
struct crlf_attrs ca;
struct crlf_filter *filter;
+ size_t pathlen;
int error;
/* Load gitattributes for the path */
@@ -293,22 +290,27 @@ static int find_and_add_filter(
/* If we're good, we create a new filter object and push it
* 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);
filter->f.apply = apply;
filter->f.do_free = NULL;
memcpy(&filter->attrs, &ca, sizeof(struct crlf_attrs));
+ filter->repo = repo;
+ memcpy(filter->path, path, pathlen + 1);
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);
}
-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);
}
diff --git a/src/diff_output.c b/src/diff_output.c
index fba6129b7..e8dd5b317 100644
--- a/src/diff_output.c
+++ b/src/diff_output.c
@@ -12,6 +12,7 @@
#include <ctype.h>
#include "fileops.h"
#include "filter.h"
+#include "buf_text.h"
static int read_next_int(const char **str, int *value)
{
diff --git a/src/filter.h b/src/filter.h
index 0ca71656b..42a44ebdb 100644
--- a/src/filter.h
+++ b/src/filter.h
@@ -9,7 +9,6 @@
#include "common.h"
#include "buffer.h"
-#include "buf_text.h"
#include "git2/odb.h"
#include "git2/repository.h"