mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-12-11 17:00:37 +00:00
LibDiff+patch: Support multiple patches in a single patch file
Multiple patches may be concatenated in the same patch file, such as git commits which are changing multiple files at the same time. To handle this, parse each patch in order in the patch file, and apply each patch sequentially. To determine whether we are at the end of a patch (and not just parsing another hunk) the parser will look for a leading '@@ ' after every hunk. If that is found, there is another hunk. Otherwise, we must be at the end of this patch.
This commit is contained in:
parent
ddbd77cca1
commit
dd373eacbc
Notes:
sideshowbarker
2024-07-17 01:13:25 +09:00
Author: https://github.com/shannonbooth Commit: https://github.com/SerenityOS/serenity/commit/dd373eacbc Pull-request: https://github.com/SerenityOS/serenity/pull/20259
4 changed files with 57 additions and 20 deletions
|
@ -165,3 +165,25 @@ TEST_CASE(add_file_from_scratch)
|
|||
|
||||
EXPECT_FILE_EQ(MUST(String::formatted("{}/file_to_add", s_test_dir)), "Hello, friends!\n");
|
||||
}
|
||||
|
||||
TEST_CASE(two_patches_in_single_patch_file)
|
||||
{
|
||||
PatchSetup setup;
|
||||
|
||||
auto patch = R"(
|
||||
--- /dev/null
|
||||
+++ a/first_file_to_add
|
||||
@@ -0,0 +1 @@
|
||||
+Hello, friends!
|
||||
--- /dev/null
|
||||
+++ a/second_file_to_add
|
||||
@@ -0,0 +1 @@
|
||||
+Hello, friends!
|
||||
)"sv;
|
||||
|
||||
run_patch({}, patch, "patching file first_file_to_add\n"
|
||||
"patching file second_file_to_add\n"sv);
|
||||
|
||||
EXPECT_FILE_EQ(MUST(String::formatted("{}/first_file_to_add", s_test_dir)), "Hello, friends!\n");
|
||||
EXPECT_FILE_EQ(MUST(String::formatted("{}/second_file_to_add", s_test_dir)), "Hello, friends!\n");
|
||||
}
|
||||
|
|
|
@ -87,6 +87,14 @@ ErrorOr<String> Parser::parse_file_line(Optional<size_t> const& strip_count)
|
|||
return stripped_path.to_string();
|
||||
}
|
||||
|
||||
ErrorOr<Patch> Parser::parse_patch(Optional<size_t> const& strip_count)
|
||||
{
|
||||
Patch patch;
|
||||
patch.header = TRY(parse_header(strip_count));
|
||||
patch.hunks = TRY(parse_hunks());
|
||||
return patch;
|
||||
}
|
||||
|
||||
ErrorOr<Header> Parser::parse_header(Optional<size_t> const& strip_count)
|
||||
{
|
||||
Header header;
|
||||
|
@ -111,20 +119,20 @@ ErrorOr<Header> Parser::parse_header(Optional<size_t> const& strip_count)
|
|||
consume_line();
|
||||
}
|
||||
|
||||
return Error::from_string_literal("Unable to find any patch");
|
||||
return header;
|
||||
}
|
||||
|
||||
ErrorOr<Vector<Hunk>> Parser::parse_hunks()
|
||||
{
|
||||
Vector<Hunk> hunks;
|
||||
|
||||
while (!is_eof()) {
|
||||
while (next_is("@@ ")) {
|
||||
// Try an locate a hunk location in this hunk. It may be prefixed with information.
|
||||
auto maybe_location = consume_unified_location();
|
||||
consume_line();
|
||||
|
||||
if (!maybe_location.has_value())
|
||||
continue;
|
||||
break;
|
||||
|
||||
Hunk hunk { *maybe_location, {} };
|
||||
|
||||
|
@ -178,6 +186,8 @@ ErrorOr<Vector<Hunk>> Parser::parse_hunks()
|
|||
ErrorOr<Vector<Hunk>> parse_hunks(StringView diff)
|
||||
{
|
||||
Parser lexer(diff);
|
||||
while (!lexer.next_is("@@ ") && !lexer.is_eof())
|
||||
lexer.consume_line();
|
||||
return lexer.parse_hunks();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -80,11 +80,13 @@ class Parser : public GenericLexer {
|
|||
public:
|
||||
using GenericLexer::GenericLexer;
|
||||
|
||||
ErrorOr<Patch> parse_patch(Optional<size_t> const& strip_count = {});
|
||||
|
||||
ErrorOr<Vector<Hunk>> parse_hunks();
|
||||
|
||||
private:
|
||||
ErrorOr<Header> parse_header(Optional<size_t> const& strip_count);
|
||||
|
||||
private:
|
||||
ErrorOr<String> parse_file_line(Optional<size_t> const& strip_count);
|
||||
Optional<HunkLocation> consume_unified_location();
|
||||
bool consume_line_number(size_t& number);
|
||||
|
|
|
@ -65,25 +65,28 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
|||
|
||||
auto patch_content = TRY(input->read_until_eof());
|
||||
|
||||
// FIXME: Support multiple patches in the patch file.
|
||||
Diff::Parser parser(patch_content);
|
||||
Diff::Patch patch;
|
||||
patch.header = TRY(parser.parse_header(strip_count));
|
||||
patch.hunks = TRY(parser.parse_hunks());
|
||||
|
||||
// FIXME: Support adding/removing a file, and asking for file to patch as fallback otherwise.
|
||||
StringView to_patch;
|
||||
if (FileSystem::is_regular_file(patch.header.old_file_path)) {
|
||||
to_patch = patch.header.old_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");
|
||||
return 1;
|
||||
while (!parser.is_eof()) {
|
||||
Diff::Patch patch = TRY(parser.parse_patch(strip_count));
|
||||
|
||||
if (patch.header.format == Diff::Format::Unknown)
|
||||
break;
|
||||
|
||||
// FIXME: Support adding/removing a file, and asking for file to patch as fallback otherwise.
|
||||
StringView to_patch;
|
||||
if (FileSystem::is_regular_file(patch.header.old_file_path)) {
|
||||
to_patch = patch.header.old_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");
|
||||
return 1;
|
||||
}
|
||||
|
||||
outln("patching file {}", to_patch);
|
||||
TRY(do_patch(to_patch, patch));
|
||||
}
|
||||
|
||||
outln("patching file {}", to_patch);
|
||||
TRY(do_patch(to_patch, patch));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue