Commit 3c5c9313 authored by Gaurav Agrawal's avatar Gaurav Agrawal 🌳 Committed by Alberto Fanjul
Browse files

flatpak: Patch libgit2 for diff files without newline at end

https://github.com/libgit2/libgit2/pull/5159
parent 79ff5406
Pipeline #93984 failed with stages
in 4 minutes and 20 seconds
From c8406a3b5aa83e2a4671ebca10ee6603a6245fb3 Mon Sep 17 00:00:00 2001
From: Patrick Steinhardt <ps@pks.im>
Date: Fri, 5 Jul 2019 11:06:33 +0200
Subject: [PATCH 1/2] patch_parse: handle missing newline indicator in old file
When either the old or new file contents have no newline at the end of
the file, then git-diff(1) will print out a "\ No newline at end of
file" indicator. While we do correctly handle this in the case where the
new file has this indcator, we fail to parse patches where the old file
is missing a newline at EOF.
Fix this bug by handling and missing newline indicators in the old file.
Add tests to verify that we can parse such files.
---
src/patch_parse.c | 13 ++++++++++++-
tests/patch/parse.c | 14 ++++++++++++++
tests/patch/patch_common.h | 10 ++++++++++
3 files changed, 36 insertions(+), 1 deletion(-)
diff --git a/src/patch_parse.c b/src/patch_parse.c
index b44d4f02f..9f5bef5b1 100644
--- a/src/patch_parse.c
+++ b/src/patch_parse.c
@@ -578,6 +578,16 @@ static int parse_hunk_body(
old_lineno = -1;
break;
+ case '\\':
+ /*
+ * If there are no oldlines left, then this is probably
+ * the "\ No newline at end of file" marker. Do not
+ * verify its format, as it may be localized.
+ */
+ if (!oldlines)
+ continue;
+ /* fall through */
+
default:
error = git_parse_err("invalid patch hunk at line %"PRIuZ, ctx->parse_ctx.line_num);
goto done;
@@ -606,7 +616,8 @@ static int parse_hunk_body(
goto done;
}
- /* Handle "\ No newline at end of file". Only expect the leading
+ /*
+ * Handle "\ No newline at end of file". Only expect the leading
* backslash, though, because the rest of the string could be
* localized. Because `diff` optimizes for the case where you
* want to apply the patch by hand.
diff --git a/tests/patch/parse.c b/tests/patch/parse.c
index a40ad7b23..8c29e311a 100644
--- a/tests/patch/parse.c
+++ b/tests/patch/parse.c
@@ -102,6 +102,20 @@ void test_patch_parse__invalid_patches_fails(void)
strlen(PATCH_CORRUPT_MISSING_HUNK_HEADER), NULL));
}
+void test_patch_parse__no_newline_at_end_of_new_file(void)
+{
+ git_patch *patch;
+ cl_git_pass(git_patch_from_buffer(&patch, PATCH_APPEND_NO_NL, strlen(PATCH_APPEND_NO_NL), NULL));
+ git_patch_free(patch);
+}
+
+void test_patch_parse__no_newline_at_end_of_old_file(void)
+{
+ git_patch *patch;
+ cl_git_pass(git_patch_from_buffer(&patch, PATCH_APPEND_NO_NL_IN_OLD_FILE, strlen(PATCH_APPEND_NO_NL_IN_OLD_FILE), NULL));
+ git_patch_free(patch);
+}
+
void test_patch_parse__files_with_whitespaces_succeeds(void)
{
git_patch *patch;
diff --git a/tests/patch/patch_common.h b/tests/patch/patch_common.h
index 291ece9eb..2db8d933f 100644
--- a/tests/patch/patch_common.h
+++ b/tests/patch/patch_common.h
@@ -681,6 +681,16 @@
"+added line with no nl\n" \
"\\ No newline at end of file\n"
+#define PATCH_APPEND_NO_NL_IN_OLD_FILE \
+ "diff --git a/file.txt b/file.txt\n" \
+ "index 9432026..83759c0 100644\n" \
+ "--- a/file.txt\n" \
+ "+++ b/file.txt\n" \
+ "@@ -1,1 +1,1 @@\n" \
+ "-foo\n" \
+ "\\ No newline at end of file\n" \
+ "+foo\n"
+
#define PATCH_NAME_WHITESPACE \
"diff --git a/file with spaces.txt b/file with spaces.txt\n" \
"index 9432026..83759c0 100644\n" \
--
2.22.0
From 329eedb84202f24c88707eb55f8cbc8afd9d6245 Mon Sep 17 00:00:00 2001
From: Erik Aigner <aigner.erik@gmail.com>
Date: Sat, 6 Jul 2019 23:54:45 +0200
Subject: [PATCH 2/2] ensure valid patch output
---
src/apply.c | 11 +++++++++++
src/diff.c | 25 ++++++++++++++-----------
src/patch_generate.c | 2 +-
src/patch_parse.c | 43 ++++++++++++++++++++++++++++++++-----------
tests/diff/patchid.c | 2 +-
tests/patch/parse.c | 24 +++++++++++++++---------
6 files changed, 74 insertions(+), 33 deletions(-)
diff --git a/src/apply.c b/src/apply.c
index f876e437e..075127208 100644
--- a/src/apply.c
+++ b/src/apply.c
@@ -199,6 +199,7 @@ static int apply_hunk(
for (i = 0; i < hunk->line_count; i++) {
size_t linenum = hunk->line_start + i;
+ git_diff_line *next;
git_diff_line *line = git_array_get(patch->lines, linenum);
if (!line) {
@@ -206,6 +207,16 @@ static int apply_hunk(
goto done;
}
+ /* lookahead for eofnl */
+ if (i < hunk->line_count - 1) {
+ next = git_array_get(patch->lines, linenum + 1);
+ if (next->origin == GIT_DIFF_LINE_CONTEXT_EOFNL ||
+ next->origin == GIT_DIFF_LINE_DEL_EOFNL ||
+ next->origin == GIT_DIFF_LINE_ADD_EOFNL) {
+ line->content_len -= 1;
+ }
+ }
+
if (line->origin == GIT_DIFF_LINE_CONTEXT ||
line->origin == GIT_DIFF_LINE_DELETION) {
if ((error = git_vector_insert(&preimage.lines, line)) < 0)
diff --git a/src/diff.c b/src/diff.c
index 6c2592137..7e7ce1524 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -474,17 +474,20 @@ static int line_cb(
GIT_UNUSED(hunk);
switch (line->origin) {
- case GIT_DIFF_LINE_ADDITION:
- git_buf_putc(&buf, '+');
- break;
- case GIT_DIFF_LINE_DELETION:
- git_buf_putc(&buf, '-');
- break;
- case GIT_DIFF_LINE_CONTEXT:
- break;
- default:
- git_error_set(GIT_ERROR_PATCH, "invalid line origin for patch");
- return -1;
+ case GIT_DIFF_LINE_ADDITION:
+ git_buf_putc(&buf, '+');
+ break;
+ case GIT_DIFF_LINE_DELETION:
+ git_buf_putc(&buf, '-');
+ break;
+ case GIT_DIFF_LINE_CONTEXT:
+ case GIT_DIFF_LINE_CONTEXT_EOFNL:
+ case GIT_DIFF_LINE_ADD_EOFNL:
+ case GIT_DIFF_LINE_DEL_EOFNL:
+ break;
+ default:
+ git_error_set(GIT_ERROR_PATCH, "invalid line origin for patch");
+ return -1;
}
git_buf_put(&buf, line->content, line->content_len);
diff --git a/src/patch_generate.c b/src/patch_generate.c
index 80033ee53..5023bfe28 100644
--- a/src/patch_generate.c
+++ b/src/patch_generate.c
@@ -836,7 +836,7 @@ static int patch_generated_line_cb(
{
git_patch_generated *patch = payload;
git_patch_hunk *hunk;
- git_diff_line *line;
+ git_diff_line *line;
GIT_UNUSED(delta);
GIT_UNUSED(hunk_);
diff --git a/src/patch_parse.c b/src/patch_parse.c
index 9f5bef5b1..d654610a9 100644
--- a/src/patch_parse.c
+++ b/src/patch_parse.c
@@ -524,6 +524,14 @@ fail:
return -1;
}
+static int eof_for_origin(int origin) {
+ if (origin == GIT_DIFF_LINE_ADDITION)
+ return GIT_DIFF_LINE_ADD_EOFNL;
+ if (origin == GIT_DIFF_LINE_DELETION)
+ return GIT_DIFF_LINE_DEL_EOFNL;
+ return GIT_DIFF_LINE_CONTEXT_EOFNL;
+}
+
static int parse_hunk_body(
git_patch_parsed *patch,
git_patch_hunk *hunk,
@@ -534,6 +542,7 @@ static int parse_hunk_body(
int oldlines = hunk->hunk.old_lines;
int newlines = hunk->hunk.new_lines;
+ int last_origin = 0;
for (;
ctx->parse_ctx.remain_len > 1 &&
@@ -579,14 +588,11 @@ static int parse_hunk_body(
break;
case '\\':
- /*
- * If there are no oldlines left, then this is probably
- * the "\ No newline at end of file" marker. Do not
- * verify its format, as it may be localized.
- */
- if (!oldlines)
- continue;
- /* fall through */
+ prefix = 0;
+ origin = eof_for_origin(last_origin);
+ old_lineno = -1;
+ new_lineno = -1;
+ break;
default:
error = git_parse_err("invalid patch hunk at line %"PRIuZ, ctx->parse_ctx.line_num);
@@ -607,6 +613,8 @@ static int parse_hunk_body(
line->new_lineno = new_lineno;
hunk->line_count++;
+
+ last_origin = origin;
}
if (oldlines || newlines) {
@@ -617,7 +625,7 @@ static int parse_hunk_body(
}
/*
- * Handle "\ No newline at end of file". Only expect the leading
+ * Handle "\ No newline at end of file". Only expect the leading
* backslash, though, because the rest of the string could be
* localized. Because `diff` optimizes for the case where you
* want to apply the patch by hand.
@@ -628,11 +636,24 @@ static int parse_hunk_body(
line = git_array_get(patch->base.lines, git_array_size(patch->base.lines) - 1);
if (line->content_len < 1) {
- error = git_parse_err("cannot trim trailing newline of empty line");
+ error = git_parse_err("last line has no trailing newline");
goto done;
}
- line->content_len--;
+ line = git_array_alloc(patch->base.lines);
+ GIT_ERROR_CHECK_ALLOC(line);
+
+ memset(line, 0x0, sizeof(git_diff_line));
+
+ line->content = ctx->parse_ctx.line;
+ line->content_len = ctx->parse_ctx.line_len;
+ line->content_offset = ctx->parse_ctx.content_len - ctx->parse_ctx.remain_len;
+ line->origin = eof_for_origin(last_origin);
+ line->num_lines = 1;
+ line->old_lineno = -1;
+ line->new_lineno = -1;
+
+ hunk->line_count++;
git_parse_advance_line(&ctx->parse_ctx);
}
diff --git a/tests/diff/patchid.c b/tests/diff/patchid.c
index 75a2aa814..7801f29a1 100644
--- a/tests/diff/patchid.c
+++ b/tests/diff/patchid.c
@@ -22,7 +22,7 @@ void test_diff_patchid__simple_commit(void)
void test_diff_patchid__filename_with_spaces(void)
{
- verify_patch_id(PATCH_APPEND_NO_NL, "f0ba05413beaef743b630e796153839462ee477a");
+ verify_patch_id(PATCH_APPEND_NO_NL, "cc0d67fc516f3378baa8a72b248da6e26c5dbf4a");
}
void test_diff_patchid__multiple_hunks(void)
diff --git a/tests/patch/parse.c b/tests/patch/parse.c
index 8c29e311a..8dc2082e0 100644
--- a/tests/patch/parse.c
+++ b/tests/patch/parse.c
@@ -27,6 +27,18 @@ static void ensure_patch_validity(git_patch *patch)
cl_assert_equal_i(0, delta->new_file.size);
}
+static void ensure_identical_patch_inout(const char *content) {
+ git_buf buf = GIT_BUF_INIT;
+ git_patch *patch;
+
+ cl_git_pass(git_patch_from_buffer(&patch, content, strlen(content), NULL));
+ cl_git_pass(git_patch_to_buf(&buf, patch));
+ cl_assert_equal_strn(git_buf_cstr(&buf), content, strlen(content));
+
+ git_patch_free(patch);
+ git_buf_dispose(&buf);
+}
+
void test_patch_parse__original_to_change_middle(void)
{
git_patch *patch;
@@ -104,21 +116,15 @@ void test_patch_parse__invalid_patches_fails(void)
void test_patch_parse__no_newline_at_end_of_new_file(void)
{
- git_patch *patch;
- cl_git_pass(git_patch_from_buffer(&patch, PATCH_APPEND_NO_NL, strlen(PATCH_APPEND_NO_NL), NULL));
- git_patch_free(patch);
+ ensure_identical_patch_inout(PATCH_APPEND_NO_NL);
}
void test_patch_parse__no_newline_at_end_of_old_file(void)
{
- git_patch *patch;
- cl_git_pass(git_patch_from_buffer(&patch, PATCH_APPEND_NO_NL_IN_OLD_FILE, strlen(PATCH_APPEND_NO_NL_IN_OLD_FILE), NULL));
- git_patch_free(patch);
+ ensure_identical_patch_inout(PATCH_APPEND_NO_NL_IN_OLD_FILE);
}
void test_patch_parse__files_with_whitespaces_succeeds(void)
{
- git_patch *patch;
- cl_git_pass(git_patch_from_buffer(&patch, PATCH_NAME_WHITESPACE, strlen(PATCH_NAME_WHITESPACE), NULL));
- git_patch_free(patch);
+ ensure_identical_patch_inout(PATCH_NAME_WHITESPACE);
}
--
2.22.0
......@@ -50,7 +50,15 @@
{
"type" : "git",
"url" : "https://github.com/libgit2/libgit2.git",
"branch" : "maint/v0.27"
"branch" : "master"
},
{
"type": "patch",
"path": "0001-patch_parse-handle-missing-newline-indicator-in-old-.patch"
},
{
"type": "patch",
"path": "0002-ensure-valid-patch-output.patch"
}
]
},
......
......@@ -157,13 +157,7 @@ public class Window : Gtk.ApplicationWindow {
//TODO: patches for files without newline ending should be fixed really
var old_text = old_sourceview.buffer.text;
if (!old_text.has_suffix("\n")) {
old_text = old_text.concat("\n");
}
var new_text = new_sourceview.buffer.text;
if (!new_text.has_suffix("\n")) {
new_text = new_text.concat("\n");
}
int old_linecount = old_sourceview.buffer.get_line_count ();
int new_linecount = new_sourceview.buffer.get_line_count ();
var diff = new Ggit.Diff.buffers(old_text.data, file1_path,
......
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