diff --git a/Tests/Utilities/TestPatch.cpp b/Tests/Utilities/TestPatch.cpp index 1b998b0295..fd1a051154 100644 --- a/Tests/Utilities/TestPatch.cpp +++ b/Tests/Utilities/TestPatch.cpp @@ -149,3 +149,19 @@ TEST_CASE(strip_path_partially) EXPECT_FILE_EQ(MUST(String::formatted("{}/to/basename", s_test_dir)), "Hello, friends!\n"); } + +TEST_CASE(add_file_from_scratch) +{ + PatchSetup setup; + + auto patch = R"( +--- /dev/null ++++ a/file_to_add +@@ -0,0 +1 @@ ++Hello, friends! +)"sv; + + run_patch({}, patch, "patching file file_to_add\n"sv); + + EXPECT_FILE_EQ(MUST(String::formatted("{}/file_to_add", s_test_dir)), "Hello, friends!\n"); +} diff --git a/Userland/Utilities/patch.cpp b/Userland/Utilities/patch.cpp index 17ef289ef8..0ce7c37557 100644 --- a/Userland/Utilities/patch.cpp +++ b/Userland/Utilities/patch.cpp @@ -12,10 +12,31 @@ #include #include +static bool is_adding_file(Diff::Patch const& patch) +{ + return patch.hunks[0].location.old_range.start_line == 0; +} + +static ErrorOr read_content(StringView path_of_file_to_patch, Diff::Patch const& patch) +{ + auto file_to_patch_or_error = Core::File::open(path_of_file_to_patch, Core::File::OpenMode::Read); + + // Trivial case - no error reading the file. + if (!file_to_patch_or_error.is_error()) + return TRY(file_to_patch_or_error.release_value()->read_until_eof()); + + auto const& error = file_to_patch_or_error.error(); + + // If the patch is adding a file then it is fine for opening the file to error out if it did not exist. + if (!is_adding_file(patch) || !error.is_errno() || error.code() != ENOENT) + return file_to_patch_or_error.release_error(); + + return ByteBuffer {}; +} + static ErrorOr do_patch(StringView path_of_file_to_patch, Diff::Patch const& patch) { - auto file_to_patch = TRY(Core::File::open(path_of_file_to_patch, Core::File::OpenMode::Read)); - auto content = TRY(file_to_patch->read_until_eof()); + ByteBuffer content = TRY(read_content(path_of_file_to_patch, patch)); auto lines = StringView(content).lines(); // Apply patch to a temporary file in case one or more of the hunks fails. @@ -54,7 +75,7 @@ ErrorOr serenity_main(Main::Arguments arguments) StringView to_patch; if (FileSystem::is_regular_file(patch.header.old_file_path)) { to_patch = patch.header.old_file_path; - } else if (FileSystem::is_regular_file(patch.header.new_file_path)) { + } else if (is_adding_file(patch) || FileSystem::is_regular_file(patch.header.new_file_path)) { to_patch = patch.header.new_file_path; } else { warnln("Unable to determine file to patch");