#include
#include
#include
#include
#include
#include
#include "status_subcommand.hpp"
#include "../wrapper/status_wrapper.hpp"
#include "../wrapper/refs_wrapper.hpp"
status_subcommand::status_subcommand(const libgit2_object&, CLI::App& app)
{
auto *sub = app.add_subcommand("status", "Show modified files in working directory, staged for your next commit");
sub->add_flag("-s,--short", m_short_flag, "Give the output in the short-format.");
sub->add_flag("--long", m_long_flag, "Give the output in the long-format. This is the default.");
// sub->add_flag("--porcelain[=]", porcelain, "Give the output in an easy-to-parse format for scripts.
// This is similar to the short output, but will remain stable across Git versions and regardless of user configuration.
// See below for details. The version parameter is used to specify the format version. This is optional and defaults
// to the original version v1 format.");
sub->add_flag("-b,--branch", m_branch_flag, "Show the branch and tracking info even in short-format.");
sub->callback([this]() { this->run(); });
};
const std::string untracked_header = "Untracked files:\n";
// "Untracked files:\n (use \"git add ...\" to include in what will be committed)";
const std::string tobecommited_header = "Changes to be committed:\n";
// "Changes to be committed:\n (use \"git reset HEAD ...\" to unstage)";
const std::string ignored_header = "Ignored files:\n";
// "Ignored files:\n (use \"git add -f ...\" to include in what will be committed)"
const std::string notstagged_header = "Changes not staged for commit:\n";
// "Changes not staged for commit:\n (use \"git add%s ...\" to update what will be committed)\n (use \"git checkout -- ...\" to discard changes in working directory)"
const std::string nothingtocommit_message = "No changes added to commit";
// "No changes added to commit (use \"git add\" and/or \"git commit -a\")"
struct status_messages
{
std::string short_mod;
std::string long_mod;
};
const std::map status_msg_map = //TODO : check spaces in short_mod
{
{ GIT_STATUS_CURRENT, {"", ""} },
{ GIT_STATUS_INDEX_NEW, {"A ", "\tnew file:"} },
{ GIT_STATUS_INDEX_MODIFIED, {"M ", "\tmodified:"} },
{ GIT_STATUS_INDEX_DELETED, {"D ", "\tdeleted:"} },
{ GIT_STATUS_INDEX_RENAMED, {"R ", "\trenamed:"} },
{ GIT_STATUS_INDEX_TYPECHANGE, {"T ", "\ttypechange:"} },
{ GIT_STATUS_WT_NEW, {"?? ", ""} },
{ GIT_STATUS_WT_MODIFIED, {" M " , "\tmodified:"} },
{ GIT_STATUS_WT_DELETED, {" D ", "\tdeleted:"} },
{ GIT_STATUS_WT_TYPECHANGE, {" T ", "\ttypechange:"} },
{ GIT_STATUS_WT_RENAMED, {" R ", "\trenamed:"} },
{ GIT_STATUS_WT_UNREADABLE, {"", ""} },
{ GIT_STATUS_IGNORED, {"!! ", ""} },
{ GIT_STATUS_CONFLICTED, {"", ""} },
};
enum class output_format
{
DEFAULT = 0,
LONG = 1,
SHORT = 2
};
struct print_entry
{
std::string status;
std::string item;
};
std::string get_print_status(git_status_t status, output_format of)
{
std::string entry_status;
if ((of == output_format::DEFAULT) || (of == output_format::LONG))
{
entry_status = status_msg_map.at(status).long_mod + " ";
}
else if (of == output_format::SHORT)
{
entry_status = status_msg_map.at(status).short_mod;
}
return entry_status;
}
void update_tracked_dir_set(const char* old_path, const char* new_path, std::set<:string>* tracked_dir_set = nullptr)
{
if (tracked_dir_set)
{
const size_t first_slash_idx = std::string_view(old_path).find('/');
if (std::string::npos != first_slash_idx)
{
auto directory = std::string_view(old_path).substr(0, first_slash_idx);
tracked_dir_set->insert(std::string(directory));
}
}
}
std::string get_print_item(const char* old_path, const char* new_path)
{
std::string entry_item;
if (old_path && new_path && std::strcmp(old_path, new_path))
{
entry_item = std::string(old_path) + " -> " + std::string(new_path);
}
else
{
entry_item = old_path ? old_path : new_path;
}
return entry_item;
}
std::vector get_entries_to_print(git_status_t status, status_list_wrapper& sl,
bool head_selector, output_format of, std::set<:string>* tracked_dir_set = nullptr)
{
std::vector entries_to_print{};
const auto& entry_list = sl.get_entry_list(status);
if (entry_list.empty())
{
return entries_to_print;
}
for (auto* entry : entry_list)
{
git_diff_delta* diff_delta = head_selector ? entry->head_to_index : entry->index_to_workdir;
const char* old_path = diff_delta->old_file.path;
const char* new_path = diff_delta->new_file.path;
update_tracked_dir_set(old_path, new_path, tracked_dir_set);
print_entry e = { get_print_status(status, of), get_print_item(old_path, new_path)};
entries_to_print.push_back(std::move(e));
}
return entries_to_print;
}
void print_entries(std::vector entries_to_print, bool is_long, stream_colour_fn colour)
{
for (auto e: entries_to_print)
{
if (is_long)
{
std::cout << colour << e.status << e.item << termcolor::reset << std::endl;
}
else
{
std::cout << colour << e.status << termcolor::reset << e.item << std::endl;
}
}
}
void print_not_tracked(const std::vector& entries_to_print, const std::set<:string>& tracked_dir_set,
std::set<:string>& untracked_dir_set, bool is_long, stream_colour_fn colour)
{
std::vector not_tracked_entries_to_print{};
for (auto e: entries_to_print)
{
const size_t first_slash_idx = e.item.find('/');
if (std::string::npos != first_slash_idx)
{
auto directory = "\t" + e.item.substr(0, first_slash_idx) + "/";
if (tracked_dir_set.contains(directory))
{
not_tracked_entries_to_print.push_back(e);
}
else
{
if (untracked_dir_set.contains(directory))
{}
else
{
not_tracked_entries_to_print.push_back({e.status, directory});
untracked_dir_set.insert(std::string(directory));
}
}
}
else
{
not_tracked_entries_to_print.push_back(e);
}
}
print_entries(not_tracked_entries_to_print, is_long, colour);
}
void status_subcommand::run()
{
auto directory = get_current_git_path();
auto repo = repository_wrapper::open(directory);
auto sl = status_list_wrapper::status_list(repo);
auto branch_name = repo.head().short_name();
std::set<:string> tracked_dir_set{};
std::set<:string> untracked_dir_set{};
std::vector<:string> untracked_to_print{};
std::vector<:string> ignored_to_print{};
output_format of = output_format::DEFAULT;
if (m_short_flag)
{
of = output_format::SHORT;
}
if (m_long_flag)
{
of = output_format::LONG;
}
// else if (porcelain_format)
// {
// output_format = 3;
// }
bool is_long;
is_long = ((of == output_format::DEFAULT) || (of == output_format::LONG));
if (is_long)
{
std::cout << "On branch " << branch_name << std::endl;
}
else
{
if (m_branch_flag)
{
std::cout << "## " << branch_name << std::endl;
}
}
if (sl.has_tobecommited_header())
{
stream_colour_fn colour = termcolor::green;
if (is_long)
{
std::cout << tobecommited_header;
}
print_entries(get_entries_to_print(GIT_STATUS_INDEX_NEW, sl, true, of, &tracked_dir_set), is_long, colour);
print_entries(get_entries_to_print(GIT_STATUS_INDEX_MODIFIED, sl, true, of, &tracked_dir_set), is_long, colour);
print_entries(get_entries_to_print(GIT_STATUS_INDEX_DELETED, sl, true, of, &tracked_dir_set), is_long, colour);
print_entries(get_entries_to_print(GIT_STATUS_INDEX_RENAMED, sl, true, of, &tracked_dir_set), is_long, colour);
print_entries(get_entries_to_print(GIT_STATUS_INDEX_TYPECHANGE, sl, true, of, &tracked_dir_set), is_long, colour);
if (is_long)
{
std::cout << std::endl;
}
}
if (sl.has_notstagged_header())
{
stream_colour_fn colour = termcolor::red;
if (is_long)
{
std::cout << notstagged_header;
}
print_entries(get_entries_to_print(GIT_STATUS_WT_MODIFIED, sl, false, of, &tracked_dir_set), is_long, colour);
print_entries(get_entries_to_print(GIT_STATUS_WT_DELETED, sl, false, of, &tracked_dir_set), is_long, colour);
print_entries(get_entries_to_print(GIT_STATUS_WT_TYPECHANGE, sl, false, of, &tracked_dir_set), is_long, colour);
print_entries(get_entries_to_print(GIT_STATUS_WT_RENAMED, sl, false, of, &tracked_dir_set), is_long, colour);
if (is_long)
{
std::cout << std::endl;
}
}
if (sl.has_untracked_header())
{
stream_colour_fn colour = termcolor::red;
if (is_long)
{
std::cout << untracked_header;
}
print_not_tracked(get_entries_to_print(GIT_STATUS_WT_NEW, sl, false, of), tracked_dir_set, untracked_dir_set, is_long, colour);
if (is_long)
{
std::cout << std::endl;
}
}
// if (sl.has_ignored_header())
// {
// stream_colour_fn colour = termcolor::red;
// if (is_long)
// {
// std::cout << ignored_header;
// }
// print_not_tracked(get_entries_to_print(GIT_STATUS_IGNORED, sl, false, of), tracked_dir_set, untracked_dir_set, is_long, colour);
// if (is_long)
// {
// std::cout << std::endl;
// }
// }
}