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
Prev Previous commit
Next Next commit
add short flag
  • Loading branch information
SandrineP committed Jun 24, 2025
commit 9874773196c4810484afdbc51864722122c84c7e
1 change: 1 addition & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ int main(int argc, char** argv)

// Sub commands
init_subcommand init(lg2_obj, app);
status_subcommand status(lg2_obj, app);

app.parse(argc, argv);

Expand Down
1 change: 1 addition & 0 deletions src/subcommand/meson.build
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
subcommand_files = files([
'init_subcommand.cpp',
'status_subcommand.cpp',
])
170 changes: 170 additions & 0 deletions src/subcommand/status_subcommand.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
#include <iostream>
#include <ostream>
#include <string>

#include <git2.h>

#include "status_subcommand.hpp"
#include "../wrapper/status_wrapper.hpp"
#include "git2/diff.h"
Comment thread
ianthomas23 marked this conversation as resolved.
Outdated

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");
// Displays paths that have differences between the index file and the current HEAD commit,
// paths that have differences between the working tree and the index file, and paths in the
// working tree that are not tracked by Git (and are not ignored by gitignore[5]).
// The first are what you would commit by running git commit;
// the second and third are what you could commit by running git add before running git commit.

sub->add_flag("-s,--short", short_flag, "Give the output in the short-format.");
sub->add_flag("--long", long_flag, "Give the output in the long-format. This is the default.");
// sub->add_flag("--porcelain[=<version>]", 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->callback([this]() { this->run(); });
};

std::string untracked_header = "Untracked files:\n";
Comment thread
ianthomas23 marked this conversation as resolved.
Outdated
// "Untracked files:\n (use \"git add <file>...\" to include in what will be committed)";
std::string tobecommited_header = "Changes to be committed:\n";
// "Changes to be committed:\n (use \"git reset HEAD <file>...\" to unstage)";
std::string ignored_header = "Ignored files:\n";
// "Ignored files:\n (use \"git add -f <file>...\" to include in what will be committed)"
std::string notstagged_header = "Changes not staged for commit:\n";
// "Changes not staged for commit:\n (use \"git add%s <file>...\" to update what will be committed)\n (use \"git checkout -- <file>...\" to discard changes in working directory)"
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<git_status_t, status_messages> status_msg_map = //TODO : check spaces in short_mod
{
{ GIT_STATUS_CURRENT, {"", ""} },
{ GIT_STATUS_INDEX_NEW, {"A ", "\t new file:"} },
{ GIT_STATUS_INDEX_MODIFIED, {"M ", "\t modified:"} },
{ GIT_STATUS_INDEX_DELETED, {"D ", "\t deleted:"} },
{ GIT_STATUS_INDEX_RENAMED, {"R ", "\t renamed:"} },
{ GIT_STATUS_INDEX_TYPECHANGE, {"T ", "\t typechange:"} },
{ GIT_STATUS_WT_NEW, {"?? ", ""} },
{ GIT_STATUS_WT_MODIFIED, {" M" , "\t modified:"} },
{ GIT_STATUS_WT_DELETED, {" D ", "\t deleted:"} },
{ GIT_STATUS_WT_TYPECHANGE, {" T ", "\t typechange:"} },
{ GIT_STATUS_WT_RENAMED, {" R ", "\t renamed:"} },
{ GIT_STATUS_WT_UNREADABLE, {"", ""} },
{ GIT_STATUS_IGNORED, {"!! ", ""} },
{ GIT_STATUS_CONFLICTED, {"", ""} },
};

void print_entries(git_status_t status, status_list_wrapper& sl, bool head_selector, size_t output_format) // TODO: add different mods
{
const auto& entry_list = sl.get_entry_list(status);
if (!entry_list.empty())
{
for (auto* entry : entry_list)
{
if ((output_format <= 1))
Comment thread
ianthomas23 marked this conversation as resolved.
Outdated
{
std::cout << status_msg_map.at(status).long_mod << "\t";
}
else if (output_format == 2)
{
std::cout << status_msg_map.at(status).short_mod;
}

git_diff_delta* diff_delta;
if (head_selector)
{
diff_delta = entry->head_to_index;
}
else
{
diff_delta = entry->index_to_workdir;
}
const char* old_path = diff_delta->old_file.path;
const char* new_path = diff_delta->new_file.path;
if (old_path && new_path && std::strcmp(old_path, new_path))
{
std::cout << old_path << " -> " << new_path << std::endl;
}
else
{
if (old_path)
{
std::cout << old_path << std::endl;
}
else
{
std::cout << new_path << std::endl;
}
}
}
}
else
{}
}

void status_subcommand::run()
{
auto directory = get_current_git_path();
auto bare = false;
auto repo = repository_wrapper::init(directory, bare);
auto sl = status_list_wrapper::status_list(repo);

// TODO: add branch info

size_t output_format = 0;
if (short_flag)
{
output_format = 2;
}
if (long_flag)
{
output_format = 1;
}
// else if (porcelain_format)
// {
// output_format = 3;
// }

if (sl.has_tobecommited_header())
{
std::cout << tobecommited_header << std::endl;
print_entries(GIT_STATUS_INDEX_NEW, sl, true, output_format);
print_entries(GIT_STATUS_INDEX_MODIFIED, sl, true, output_format);
print_entries(GIT_STATUS_INDEX_DELETED, sl, true, output_format);
print_entries(GIT_STATUS_INDEX_RENAMED, sl, true, output_format);
print_entries(GIT_STATUS_INDEX_TYPECHANGE, sl, true, output_format);
std::cout << std::endl;
}

if (sl.has_notstagged_header())
{
std::cout << notstagged_header << std::endl;
print_entries(GIT_STATUS_WT_MODIFIED, sl, false, output_format);
print_entries(GIT_STATUS_WT_DELETED, sl, false, output_format);
print_entries(GIT_STATUS_WT_TYPECHANGE, sl, false, output_format);
print_entries(GIT_STATUS_WT_RENAMED, sl, false, output_format);
std::cout << std::endl;
}

if (sl.has_untracked_header())
{
std::cout << untracked_header << std::endl;
print_entries(GIT_STATUS_WT_NEW, sl, false, output_format);
std::cout << std::endl;
}

if (sl.has_ignored_header())
{
std::cout << ignored_header << std::endl;
print_entries(GIT_STATUS_IGNORED, sl, false, output_format);
std::cout << std::endl;
}
}
17 changes: 17 additions & 0 deletions src/subcommand/status_subcommand.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#pragma once

#include <CLI/CLI.hpp>

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

class status_subcommand
{
public:

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

private:
bool short_flag = false;
bool long_flag = false;
};
1 change: 1 addition & 0 deletions src/wrapper/meson.build
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
wrapper_files = files([
'repository_wrapper.cpp',
'status_wrapper.cpp',
])
91 changes: 91 additions & 0 deletions src/wrapper/status_wrapper.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
#include "../utils/git_exception.hpp"
#include "../wrapper/status_wrapper.hpp"

status_list_wrapper::~status_list_wrapper()
{
git_status_list_free(p_ressource);
p_ressource = nullptr;
}

status_list_wrapper status_list_wrapper::status_list(const repository_wrapper& rw)
{
status_list_wrapper res;
throwIfError(git_status_list_new(&(res.p_ressource), rw, nullptr));

std::size_t status_list_size = git_status_list_entrycount(res.p_ressource);
for (std::size_t i = 0; i < status_list_size; ++i)
{
auto entry = git_status_byindex(res.p_ressource, i);
res.m_entries[entry->status].push_back(entry);
}

if (!res.get_entry_list(GIT_STATUS_INDEX_NEW).empty() || !res.get_entry_list(GIT_STATUS_INDEX_MODIFIED).empty() || !res.get_entry_list(GIT_STATUS_INDEX_DELETED).empty() || !res.get_entry_list(GIT_STATUS_INDEX_RENAMED).empty() || !res.get_entry_list(GIT_STATUS_INDEX_TYPECHANGE).empty())
{
res.m_tobecommited_header_flag = true;
}
if (!res.get_entry_list(GIT_STATUS_WT_NEW).empty())
{
res.m_untracked_header_flag = true;
}
if (!res.get_entry_list(GIT_STATUS_WT_MODIFIED).empty() || !res.get_entry_list(GIT_STATUS_WT_DELETED).empty() || !res.get_entry_list(GIT_STATUS_WT_TYPECHANGE).empty() || !res.get_entry_list(GIT_STATUS_WT_RENAMED).empty())
{
res.m_notstagged_header_flag = true;
}
if (!res.get_entry_list(GIT_STATUS_IGNORED).empty())
{
res.m_ignored_header_flag = true;
}
// if (!res.tobecommited_header_flag)
// {
// res.m_nothingtocommit_message_flag = true;
// }

return res;
}

bool status_list_wrapper::has_untracked_header() const
{
return m_untracked_header_flag;
}
bool status_list_wrapper::has_tobecommited_header() const
{
return m_tobecommited_header_flag;
}
bool status_list_wrapper::has_ignored_header() const
{
return m_ignored_header_flag;
}
bool status_list_wrapper::has_notstagged_header() const
{
return m_notstagged_header_flag;
}
bool status_list_wrapper::has_nothingtocommit_message() const
{
return m_nothingtocommit_message_flag;
}

auto status_list_wrapper::get_entry_list(git_status_t status) const -> const status_entry_list&
{
if (auto search = m_entries.find(status); search != m_entries.end())
{
return search->second;
}
else
{
return m_empty;
}
}



// 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;
// };
42 changes: 42 additions & 0 deletions src/wrapper/status_wrapper.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#pragma once

#include <map>
#include <vector>

#include <git2.h>

#include "../wrapper/repository_wrapper.hpp"

class status_list_wrapper : public wrapper_base<git_status_list>
{
public:
using status_entry_list = std::vector<const git_status_entry*>;

~status_list_wrapper();

status_list_wrapper(status_list_wrapper&&) = default;
status_list_wrapper& operator=(status_list_wrapper&&) = default;

static status_list_wrapper status_list(const repository_wrapper& wrapper);

const status_entry_list& get_entry_list(git_status_t status) const;

bool has_untracked_header() const;
bool has_tobecommited_header() const;
bool has_ignored_header() const;
bool has_notstagged_header() const;
bool has_nothingtocommit_message() const;

private:

status_list_wrapper() = default;

using status_entry_map = std::map<git_status_t, status_entry_list>;
status_entry_map m_entries;
status_entry_list m_empty = {};
bool m_untracked_header_flag = false;
bool m_tobecommited_header_flag = false;
bool m_ignored_header_flag = false;
bool m_notstagged_header_flag = false;
bool m_nothingtocommit_message_flag = false;
};