Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Implemented git rm
  • Loading branch information
JohanMabille committed Feb 9, 2026
commit ffc8ede499bba93c63d4b0cf96d237dbc55be473
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ set(GIT2CPP_SRC
${GIT2CPP_SOURCE_DIR}/subcommand/revlist_subcommand.hpp
${GIT2CPP_SOURCE_DIR}/subcommand/revparse_subcommand.cpp
${GIT2CPP_SOURCE_DIR}/subcommand/revparse_subcommand.hpp
${GIT2CPP_SOURCE_DIR}/subcommand/rm_subcommand.cpp
${GIT2CPP_SOURCE_DIR}/subcommand/rm_subcommand.hpp
${GIT2CPP_SOURCE_DIR}/subcommand/stash_subcommand.cpp
${GIT2CPP_SOURCE_DIR}/subcommand/stash_subcommand.hpp
${GIT2CPP_SOURCE_DIR}/subcommand/status_subcommand.cpp
Expand Down
4 changes: 3 additions & 1 deletion src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "subcommand/status_subcommand.hpp"
#include "subcommand/revparse_subcommand.hpp"
#include "subcommand/revlist_subcommand.hpp"
#include "subcommand/rm_subcommand.hpp"

int main(int argc, char** argv)
{
Expand Down Expand Up @@ -55,8 +56,9 @@ int main(int argc, char** argv)
push_subcommand push(lg2_obj, app);
rebase_subcommand rebase(lg2_obj, app);
remote_subcommand remote(lg2_obj, app);
revparse_subcommand revparse(lg2_obj, app);
revlist_subcommand revlist(lg2_obj, app);
revparse_subcommand revparse(lg2_obj, app);
rm_subcommand rm(lg2_obj, app);
stash_subcommand stash(lg2_obj, app);

app.require_subcommand(/* min */ 0, /* max */ 1);
Expand Down
64 changes: 64 additions & 0 deletions src/subcommand/rm_subcommand.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#include <filesystem>
#include <ranges>
#include "rm_subcommand.hpp"
#include "../utils/common.hpp"
#include "../utils/git_exception.hpp"
#include "../wrapper/index_wrapper.hpp"
#include "../wrapper/repository_wrapper.hpp"

namespace fs = std::filesystem;

rm_subcommand::rm_subcommand(const libgit2_object&, CLI::App& app)
{
auto* rm = app.add_subcommand("rm", "Remove files from the working tree and from the index");
rm->add_option("<pathspec>", m_pathspec, "Files to remove");
rm->add_flag("-r", m_recursive, "Allow recursive removal when a leading directory name is given");

rm->callback([this]() { this->run(); });
}

void rm_subcommand::run()
{
auto directory = get_current_git_path();
auto repo = repository_wrapper::open(directory);

index_wrapper index = repo.make_index();

std::vector<std::string> files;
std::vector<std::string> directories;

std::ranges::for_each(m_pathspec, [&](const std::string& path)
{
if (!fs::exists(path))
{
std::string msg = "fatal: pathspec '" + path + "' did not math any file";
throw git_exception(msg, 128);
}
if (fs::is_directory(path))
{
directories.push_back(path);
}
else
{
if (!repo.does_track(path))
{
std::string msg = "fatal: pathsspec '" + path + "'is not tracked";
throw git_exception(msg, 128);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use the new git2cpp_error_code here.

}
files.push_back(path);
}
});

if (!directories.empty() && !m_recursive)
{
std::string msg = "fatal: not removing '" + directories.front() + "' recursively without -r";
throw git_exception(msg, 128);
}

index.remove_entries(files);
index.remove_directories(directories);
index.write();

std::ranges::for_each(files, [](const std::string& path) { fs::remove(path); });
std::ranges::for_each(directories, [](const std::string& path) { fs::remove_all(path); });
}
21 changes: 21 additions & 0 deletions src/subcommand/rm_subcommand.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#pragma once

#include <CLI/CLI.hpp>
#include <string>
#include <vector>

#include "../utils/common.hpp"

class rm_subcommand
{
public:

explicit rm_subcommand(const libgit2_object&, CLI::App& app);
void run();

private:

std::vector<std::string> m_pathspec;
bool m_recursive = false;
};

15 changes: 15 additions & 0 deletions src/wrapper/index_wrapper.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include <git2/index.h>
#include <algorithm>
#include <iostream>
#include <vector>

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

void index_wrapper::remove_entries(std::vector<std::string> paths)
{
git_strarray_wrapper array{paths};
throw_if_error(git_index_remove_all(*this, array, NULL, NULL));
}

void index_wrapper::remove_directories(std::vector<std::string> entries)
{
std::for_each(entries.cbegin(), entries.cend(), [this](const std::string& path)
{
throw_if_error(git_index_remove_directory(*this, path.c_str(), 0));
});
}

void index_wrapper::write()
{
throw_if_error(git_index_write(*this));
Expand Down
2 changes: 2 additions & 0 deletions src/wrapper/index_wrapper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ class index_wrapper : public wrapper_base<git_index>
void add_all();

void remove_entry(const std::string& path);
void remove_entries(std::vector<std::string> paths);
void remove_directories(std::vector<std::string> paths);

bool has_conflict() const;
void output_conflicts();
Expand Down
7 changes: 7 additions & 0 deletions src/wrapper/repository_wrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,13 @@ revwalk_wrapper repository_wrapper::new_walker()
return revwalk_wrapper(walker);
}

bool repository_wrapper::does_track(std::string_view path) const
{
unsigned int flags;
throw_if_error(git_status_file(&flags, *this, path.data()));
return !(flags & GIT_STATUS_WT_NEW) && !(flags & GIT_STATUS_IGNORED);
}

// Head

bool repository_wrapper::is_head_unborn() const
Expand Down
2 changes: 2 additions & 0 deletions src/wrapper/repository_wrapper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ class repository_wrapper : public wrapper_base<git_repository>

revwalk_wrapper new_walker();

bool does_track(std::string_view path) const;

// Head
bool is_head_unborn() const;
reference_wrapper head() const;
Expand Down
13 changes: 0 additions & 13 deletions src/wrapper/status_wrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,16 +84,3 @@ auto status_list_wrapper::get_entry_list(git_status_t status) const -> const sta
}
}



// std::ostream& operator<<(std::ostream& out, const status_list_wrapper& slw)
// {
// std::size_t status_list_size = git_status_list_entrycount(slw);
// for (std::size_t i = 0; i < status_list_size; ++i)
// {
// std::cout << i << " ";
// auto entry = git_status_byindex(slw, i);

// }
// return out;
// };
1 change: 1 addition & 0 deletions test/conftest_wasm.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ def pytest_ignore_collect(collection_path: pathlib.Path) -> bool:
"test_reset.py",
"test_revlist.py",
"test_revparse.py",
"test_rm.py",
"test_stash.py",
"test_status.py",
]
Expand Down
Loading