mirror of
https://github.com/SerenityOS/serenity
synced 2024-10-15 12:23:15 +00:00
patch+LibDiff: Implement 'strip' of filenames when parsing patch
Implement the patch '-p' / '--strip' option, which strips the given number of leading components from filenames parsed in the patch header. If not given this option defaults to the basename of that path.
This commit is contained in:
parent
87e2b8e343
commit
81df0278b1
|
@ -107,3 +107,45 @@ TEST_CASE(basic_addition_patch_from_empty_file)
|
|||
|
||||
EXPECT_FILE_EQ(MUST(String::formatted("{}/a", s_test_dir)), "1\n2\n3\n");
|
||||
}
|
||||
|
||||
TEST_CASE(strip_path_to_basename)
|
||||
{
|
||||
PatchSetup setup;
|
||||
|
||||
auto patch = R"(
|
||||
--- /dev/null
|
||||
+++ a/bunch/of/../folders/stripped/to/basename
|
||||
@@ -0,0 +1 @@
|
||||
+Hello, friends!
|
||||
)"sv;
|
||||
|
||||
auto file = ""sv;
|
||||
auto input = MUST(Core::File::open(MUST(String::formatted("{}/basename", s_test_dir)), Core::File::OpenMode::Write));
|
||||
MUST(input->write_until_depleted(file.bytes()));
|
||||
|
||||
run_patch({}, patch, "patching file basename\n"sv);
|
||||
|
||||
EXPECT_FILE_EQ(MUST(String::formatted("{}/basename", s_test_dir)), "Hello, friends!\n");
|
||||
}
|
||||
|
||||
TEST_CASE(strip_path_partially)
|
||||
{
|
||||
PatchSetup setup;
|
||||
|
||||
auto patch = R"(
|
||||
--- /dev/null
|
||||
+++ a/bunch/of/../folders/stripped/to/basename
|
||||
@@ -0,0 +1 @@
|
||||
+Hello, friends!
|
||||
)"sv;
|
||||
|
||||
MUST(Core::System::mkdir(MUST(String::formatted("{}/to", s_test_dir)), 0755));
|
||||
|
||||
auto file = ""sv;
|
||||
auto input = MUST(Core::File::open(MUST(String::formatted("{}/to/basename", s_test_dir)), Core::File::OpenMode::Write));
|
||||
MUST(input->write_until_depleted(file.bytes()));
|
||||
|
||||
run_patch({ "-p6" }, patch, "patching file to/basename\n"sv);
|
||||
|
||||
EXPECT_FILE_EQ(MUST(String::formatted("{}/to/basename", s_test_dir)), "Hello, friends!\n");
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include "Hunks.h"
|
||||
#include <AK/Debug.h>
|
||||
#include <AK/LexicalPath.h>
|
||||
|
||||
namespace Diff {
|
||||
|
||||
|
@ -57,19 +58,48 @@ bool Parser::consume_line_number(size_t& number)
|
|||
return true;
|
||||
}
|
||||
|
||||
ErrorOr<Header> Parser::parse_header()
|
||||
ErrorOr<String> Parser::parse_file_line(Optional<size_t> const& strip_count)
|
||||
{
|
||||
// FIXME: handle parsing timestamps as well.
|
||||
auto path = consume_line();
|
||||
|
||||
// No strip count given. Default to basename of file.
|
||||
if (!strip_count.has_value())
|
||||
return String::from_deprecated_string(LexicalPath::basename(path));
|
||||
|
||||
// NOTE: We cannot use LexicalPath::parts as we want to strip the non-canonicalized path.
|
||||
auto const& parts = path.split_view('/');
|
||||
|
||||
// More components to strip than the filename has. Just pretend it is missing.
|
||||
if (strip_count.value() >= parts.size())
|
||||
return String();
|
||||
|
||||
// Remove given number of leading components from the path.
|
||||
size_t components = parts.size() - strip_count.value();
|
||||
|
||||
StringBuilder stripped_path;
|
||||
for (size_t i = parts.size() - components; i < parts.size(); ++i) {
|
||||
TRY(stripped_path.try_append(parts[i]));
|
||||
if (i != parts.size() - 1)
|
||||
TRY(stripped_path.try_append("/"sv));
|
||||
}
|
||||
|
||||
return stripped_path.to_string();
|
||||
}
|
||||
|
||||
ErrorOr<Header> Parser::parse_header(Optional<size_t> const& strip_count)
|
||||
{
|
||||
Header header;
|
||||
|
||||
while (!is_eof()) {
|
||||
|
||||
if (consume_specific("+++ ")) {
|
||||
header.new_file_path = TRY(String::from_utf8(consume_line()));
|
||||
header.new_file_path = TRY(parse_file_line(strip_count));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (consume_specific("--- ")) {
|
||||
header.old_file_path = TRY(String::from_utf8(consume_line()));
|
||||
header.old_file_path = TRY(parse_file_line(strip_count));
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -82,9 +82,10 @@ public:
|
|||
|
||||
ErrorOr<Vector<Hunk>> parse_hunks();
|
||||
|
||||
ErrorOr<Header> parse_header();
|
||||
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);
|
||||
};
|
||||
|
|
|
@ -30,9 +30,11 @@ static ErrorOr<void> do_patch(StringView path_of_file_to_patch, Diff::Patch cons
|
|||
ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||
{
|
||||
StringView directory;
|
||||
Optional<size_t> strip_count;
|
||||
|
||||
Core::ArgsParser args_parser;
|
||||
args_parser.add_option(directory, "Change the working directory to <directory> before applying the patch file", "directory", 'd', "directory");
|
||||
args_parser.add_option(strip_count, "Strip given number of leading path components from file names (defaults as basename)", "strip", 'p', "count");
|
||||
args_parser.parse(arguments);
|
||||
|
||||
if (!directory.is_null())
|
||||
|
@ -45,7 +47,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
|||
// FIXME: Support multiple patches in the patch file.
|
||||
Diff::Parser parser(patch_content);
|
||||
Diff::Patch patch;
|
||||
patch.header = TRY(parser.parse_header());
|
||||
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.
|
||||
|
|
Loading…
Reference in a new issue