diff --git a/Tests/Utilities/TestPatch.cpp b/Tests/Utilities/TestPatch.cpp index f194391c65..9679636725 100644 --- a/Tests/Utilities/TestPatch.cpp +++ b/Tests/Utilities/TestPatch.cpp @@ -200,3 +200,23 @@ TEST_CASE(two_patches_in_single_patch_file) EXPECT_FILE_EQ(ByteString::formatted("{}/first_file_to_add", s_test_dir), "Hello, friends!\n"); EXPECT_FILE_EQ(ByteString::formatted("{}/second_file_to_add", s_test_dir), "Hello, friends!\n"); } + +TEST_CASE(patch_adding_file_to_existing_file) +{ + PatchSetup setup; + + auto patch = R"( +--- /dev/null ++++ a +@@ -0,0 +1 @@ ++1 +)"sv; + + auto file = "a\n"sv; + auto input = MUST(Core::File::open(ByteString::formatted("{}/a", s_test_dir), Core::File::OpenMode::Write)); + MUST(input->write_until_depleted(file.bytes())); + + run_patch(ExpectSuccess::No, {}, patch); + + EXPECT_FILE_EQ(ByteString::formatted("{}/a", s_test_dir), "a\n"sv); +} diff --git a/Userland/Libraries/LibDiff/Applier.cpp b/Userland/Libraries/LibDiff/Applier.cpp index 77fe8bec79..05a3a68676 100644 --- a/Userland/Libraries/LibDiff/Applier.cpp +++ b/Userland/Libraries/LibDiff/Applier.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Shannon Booth + * Copyright (c) 2023-2024, Shannon Booth * * SPDX-License-Identifier: BSD-2-Clause */ @@ -35,9 +35,20 @@ static Optional locate_hunk(Vector const& content, Hunk co // Make a first best guess at where the from-file range is telling us where the hunk should be. size_t offset_guess = expected_line_number(hunk.location) - 1 + offset; - // If there's no lines surrounding this hunk - it will always succeed, so there is no point in checking any further. - if (hunk.location.old_range.number_of_lines == 0) + // If there's no lines surrounding this hunk - it will always succeed, + // so there is no point in checking any further. Note that this check is + // also what makes matching against an empty 'from file' work (with no lines), + // as in that case there is no content for us to even match against in the + // first place! + // + // However, we also should reject patches being added when the hunk is + // claiming the file is completely empty - but there are actually lines in + // that file. + if (hunk.location.old_range.number_of_lines == 0) { + if (hunk.location.old_range.start_line == 0 && !content.is_empty()) + return {}; return Location { offset_guess, 0, 0 }; + } size_t patch_prefix_context = 0; for (auto const& line : hunk.lines) {