Skip to content

Commit ffc8ede

Browse files
committed
Implemented git rm
1 parent a61ab26 commit ffc8ede

File tree

11 files changed

+476
-14
lines changed

11 files changed

+476
-14
lines changed

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ set(GIT2CPP_SRC
7676
${GIT2CPP_SOURCE_DIR}/subcommand/revlist_subcommand.hpp
7777
${GIT2CPP_SOURCE_DIR}/subcommand/revparse_subcommand.cpp
7878
${GIT2CPP_SOURCE_DIR}/subcommand/revparse_subcommand.hpp
79+
${GIT2CPP_SOURCE_DIR}/subcommand/rm_subcommand.cpp
80+
${GIT2CPP_SOURCE_DIR}/subcommand/rm_subcommand.hpp
7981
${GIT2CPP_SOURCE_DIR}/subcommand/stash_subcommand.cpp
8082
${GIT2CPP_SOURCE_DIR}/subcommand/stash_subcommand.hpp
8183
${GIT2CPP_SOURCE_DIR}/subcommand/status_subcommand.cpp

src/main.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "subcommand/status_subcommand.hpp"
2626
#include "subcommand/revparse_subcommand.hpp"
2727
#include "subcommand/revlist_subcommand.hpp"
28+
#include "subcommand/rm_subcommand.hpp"
2829

2930
int main(int argc, char** argv)
3031
{
@@ -55,8 +56,9 @@ int main(int argc, char** argv)
5556
push_subcommand push(lg2_obj, app);
5657
rebase_subcommand rebase(lg2_obj, app);
5758
remote_subcommand remote(lg2_obj, app);
58-
revparse_subcommand revparse(lg2_obj, app);
5959
revlist_subcommand revlist(lg2_obj, app);
60+
revparse_subcommand revparse(lg2_obj, app);
61+
rm_subcommand rm(lg2_obj, app);
6062
stash_subcommand stash(lg2_obj, app);
6163

6264
app.require_subcommand(/* min */ 0, /* max */ 1);

src/subcommand/rm_subcommand.cpp

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#include <filesystem>
2+
#include <ranges>
3+
#include "rm_subcommand.hpp"
4+
#include "../utils/common.hpp"
5+
#include "../utils/git_exception.hpp"
6+
#include "../wrapper/index_wrapper.hpp"
7+
#include "../wrapper/repository_wrapper.hpp"
8+
9+
namespace fs = std::filesystem;
10+
11+
rm_subcommand::rm_subcommand(const libgit2_object&, CLI::App& app)
12+
{
13+
auto* rm = app.add_subcommand("rm", "Remove files from the working tree and from the index");
14+
rm->add_option("<pathspec>", m_pathspec, "Files to remove");
15+
rm->add_flag("-r", m_recursive, "Allow recursive removal when a leading directory name is given");
16+
17+
rm->callback([this]() { this->run(); });
18+
}
19+
20+
void rm_subcommand::run()
21+
{
22+
auto directory = get_current_git_path();
23+
auto repo = repository_wrapper::open(directory);
24+
25+
index_wrapper index = repo.make_index();
26+
27+
std::vector<std::string> files;
28+
std::vector<std::string> directories;
29+
30+
std::ranges::for_each(m_pathspec, [&](const std::string& path)
31+
{
32+
if (!fs::exists(path))
33+
{
34+
std::string msg = "fatal: pathspec '" + path + "' did not math any file";
35+
throw git_exception(msg, 128);
36+
}
37+
if (fs::is_directory(path))
38+
{
39+
directories.push_back(path);
40+
}
41+
else
42+
{
43+
if (!repo.does_track(path))
44+
{
45+
std::string msg = "fatal: pathsspec '" + path + "'is not tracked";
46+
throw git_exception(msg, 128);
47+
}
48+
files.push_back(path);
49+
}
50+
});
51+
52+
if (!directories.empty() && !m_recursive)
53+
{
54+
std::string msg = "fatal: not removing '" + directories.front() + "' recursively without -r";
55+
throw git_exception(msg, 128);
56+
}
57+
58+
index.remove_entries(files);
59+
index.remove_directories(directories);
60+
index.write();
61+
62+
std::ranges::for_each(files, [](const std::string& path) { fs::remove(path); });
63+
std::ranges::for_each(directories, [](const std::string& path) { fs::remove_all(path); });
64+
}

src/subcommand/rm_subcommand.hpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#pragma once
2+
3+
#include <CLI/CLI.hpp>
4+
#include <string>
5+
#include <vector>
6+
7+
#include "../utils/common.hpp"
8+
9+
class rm_subcommand
10+
{
11+
public:
12+
13+
explicit rm_subcommand(const libgit2_object&, CLI::App& app);
14+
void run();
15+
16+
private:
17+
18+
std::vector<std::string> m_pathspec;
19+
bool m_recursive = false;
20+
};
21+

src/wrapper/index_wrapper.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include <git2/index.h>
2+
#include <algorithm>
23
#include <iostream>
34
#include <vector>
45

@@ -47,6 +48,20 @@ void index_wrapper::remove_entry(const std::string& path)
4748
throw_if_error(git_index_remove_bypath(*this, path.c_str()));
4849
}
4950

51+
void index_wrapper::remove_entries(std::vector<std::string> paths)
52+
{
53+
git_strarray_wrapper array{paths};
54+
throw_if_error(git_index_remove_all(*this, array, NULL, NULL));
55+
}
56+
57+
void index_wrapper::remove_directories(std::vector<std::string> entries)
58+
{
59+
std::for_each(entries.cbegin(), entries.cend(), [this](const std::string& path)
60+
{
61+
throw_if_error(git_index_remove_directory(*this, path.c_str(), 0));
62+
});
63+
}
64+
5065
void index_wrapper::write()
5166
{
5267
throw_if_error(git_index_write(*this));

src/wrapper/index_wrapper.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ class index_wrapper : public wrapper_base<git_index>
2828
void add_all();
2929

3030
void remove_entry(const std::string& path);
31+
void remove_entries(std::vector<std::string> paths);
32+
void remove_directories(std::vector<std::string> paths);
3133

3234
bool has_conflict() const;
3335
void output_conflicts();

src/wrapper/repository_wrapper.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,13 @@ revwalk_wrapper repository_wrapper::new_walker()
7070
return revwalk_wrapper(walker);
7171
}
7272

73+
bool repository_wrapper::does_track(std::string_view path) const
74+
{
75+
unsigned int flags;
76+
throw_if_error(git_status_file(&flags, *this, path.data()));
77+
return !(flags & GIT_STATUS_WT_NEW) && !(flags & GIT_STATUS_IGNORED);
78+
}
79+
7380
// Head
7481

7582
bool repository_wrapper::is_head_unborn() const

src/wrapper/repository_wrapper.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ class repository_wrapper : public wrapper_base<git_repository>
4343

4444
revwalk_wrapper new_walker();
4545

46+
bool does_track(std::string_view path) const;
47+
4648
// Head
4749
bool is_head_unborn() const;
4850
reference_wrapper head() const;

src/wrapper/status_wrapper.cpp

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -84,16 +84,3 @@ auto status_list_wrapper::get_entry_list(git_status_t status) const -> const sta
8484
}
8585
}
8686

87-
88-
89-
// std::ostream& operator<<(std::ostream& out, const status_list_wrapper& slw)
90-
// {
91-
// std::size_t status_list_size = git_status_list_entrycount(slw);
92-
// for (std::size_t i = 0; i < status_list_size; ++i)
93-
// {
94-
// std::cout << i << " ";
95-
// auto entry = git_status_byindex(slw, i);
96-
97-
// }
98-
// return out;
99-
// };

test/conftest_wasm.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ def pytest_ignore_collect(collection_path: pathlib.Path) -> bool:
3030
"test_reset.py",
3131
"test_revlist.py",
3232
"test_revparse.py",
33+
"test_rm.py",
3334
"test_stash.py",
3435
"test_status.py",
3536
]

0 commit comments

Comments
 (0)