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
80 changes: 70 additions & 10 deletions src/subcommand/status_subcommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ status_subcommand::status_subcommand(const libgit2_object&, CLI::App& app)
const std::string untracked_header = "Untracked files:\n (use \"git add <file>...\" to include in what will be committed)\n";
const std::string tobecommited_header = "Changes to be committed:\n (use \"git reset HEAD <file>...\" to unstage)\n";
const std::string ignored_header = "Ignored files:\n (use \"git add -f <file>...\" to include in what will be committed)\n";
const 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)\n"
const std::string notstagged_header = "Changes not staged for commit:\n (use \"git add <file>...\" to update what will be committed)\n";
// TODO: add the following ot notstagged_header after "checkout <file>" is implemented: (use \"git checkout -- <file>...\" to discard changes in working directory)\n";
const std::string unmerged_header = "Unmerged paths:\n (use \"git add <file>...\" to mark resolution)\n";
// const std::string nothingtocommit_message = "No changes added to commit (use \"git add\" and/or \"git commit -a\")";
const std::string nothingtocommit_message = "no changes added to commit (use \"git add\" and/or \"git commit -a\")";
const std::string treeclean_message = "Nothing to commit, working tree clean";

enum class output_format
Expand Down Expand Up @@ -172,6 +172,7 @@ void status_run(status_subcommand_options options)
auto repo = repository_wrapper::open(directory);
auto sl = status_list_wrapper::status_list(repo);
auto branch_name = repo.head_short_name();
auto tracking_info = repo.get_tracking_info();

std::set<std::string> tracked_dir_set{};
std::set<std::string> untracked_dir_set{};
Expand All @@ -196,11 +197,44 @@ void status_run(status_subcommand_options options)
is_long = ((of == output_format::DEFAULT) || (of == output_format::LONG));
if (is_long)
{
std::cout << "On branch " << branch_name << "\n" << std::endl;
std::cout << "On branch " << branch_name << std::endl;

if (tracking_info.has_upstream)
{
if(tracking_info.ahead > 0 && tracking_info.behind == 0)
{
std::cout << "Your branch is ahead of '" << tracking_info.upstream_name << "' by "
<< tracking_info.ahead << " commit"
<< (tracking_info.ahead > 1 ? "s" : "") << "." << std::endl;
std::cout << " (use \"git push\" to publish your local commits)" << std::endl;
}
else if (tracking_info.ahead == 0 && tracking_info.behind > 0)
{
std::cout << "Your branch is behind '" << tracking_info.upstream_name << "' by "
<< tracking_info.behind << " commit"
<< (tracking_info.behind > 1 ? "s" : "") << "." << std::endl;
std::cout << " (use \"git pull\" to update your local branch)" << std::endl;
}
else if (tracking_info.ahead > 0 && tracking_info.behind > 0)
{
std::cout << "Your branch and '" << tracking_info.upstream_name
<< "' have diverged," << std::endl;
std::cout << "and have " << tracking_info.ahead << " and "
<< tracking_info.behind << " different commit"
<< ((tracking_info.ahead + tracking_info.behind) > 2 ? "s" : "")
<< " each, respectively." << std::endl;
std::cout << " (use \"git pull\" to merge the remote branch into yours)" << std::endl;
}
else // ahead == 0 && behind == 0
{
std::cout << "Your branch is up to date with '" << tracking_info.upstream_name << "'." << std::endl;
}
std::cout << std::endl;
}

if (repo.is_head_unborn())
{
std::cout << "No commits yet\n" << std::endl;
std::cout << "No commit yet\n" << std::endl;
}

if (sl.has_unmerged_header())
Expand All @@ -214,6 +248,30 @@ void status_run(status_subcommand_options options)
{
std::cout << "## " << branch_name << std::endl;
}

if (tracking_info.has_upstream)
{
std::cout << "..." << tracking_info.upstream_name;

if (tracking_info.ahead > 0 || tracking_info.behind > 0)
{
std::cout << " [";
if (tracking_info.ahead > 0)
{
std::cout << "ahead " << tracking_info.ahead;
}
if (tracking_info.behind > 0)
{
if (tracking_info.ahead > 0)
{
std::cout << ", ";
}
std::cout << "behind " << tracking_info.behind;
}
std::cout << "]";
}
std::cout << std::endl;
}
}

if (sl.has_tobecommited_header())
Expand Down Expand Up @@ -281,11 +339,13 @@ void status_run(status_subcommand_options options)
}

// TODO: check if this message should be displayed even if there are untracked files
if (!(sl.has_tobecommited_header() | sl.has_notstagged_header() | sl.has_unmerged_header() | sl.has_untracked_header()))
if (is_long & (!(sl.has_tobecommited_header() | sl.has_notstagged_header() | sl.has_unmerged_header() | sl.has_untracked_header())))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
if (is_long & (!(sl.has_tobecommited_header() | sl.has_notstagged_header() | sl.has_unmerged_header() | sl.has_untracked_header())))
if (is_long & !(sl.has_tobecommited_header() | sl.has_notstagged_header() | sl.has_unmerged_header() | sl.has_untracked_header()))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We should use boolean operators (&&, ||) instead of bitwise operators here (&, |) toi avoid unecessary conversion to integers.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I published the review after the suggestion was applied, thus the outdated tag, but the comment is still relevant.

{
if (is_long)
{
std::cout << treeclean_message << std::endl;
}
std::cout << treeclean_message << std::endl;
}

if (is_long & !sl.has_tobecommited_header() & (sl.has_notstagged_header() | sl.has_untracked_header()))
{
std::cout << nothingtocommit_message << std::endl;
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The function is quite long, it would make it easier to read if it was split into logical smaller functions.

}
48 changes: 47 additions & 1 deletion src/wrapper/repository_wrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,53 @@ branch_iterator repository_wrapper::iterate_branches(git_branch_t type) const
return branch_iterator(iter);
}

std::optional<reference_wrapper> repository_wrapper::upstream() const
{
git_reference* ref;
reference_wrapper head = this->head();
int error = git_branch_upstream(&ref, head);
if (error == 0)
{
return reference_wrapper(ref);
}
else
{
return std::nullopt;
}
}

branch_tracking_info repository_wrapper::get_tracking_info() const
{
branch_tracking_info info;
info.has_upstream = false;
info.ahead = 0;
info.behind = 0;
info.upstream_name = "";

if (this->is_head_unborn())
{
return info;
}

reference_wrapper head = this->head();
std::optional<reference_wrapper> upstream = this->upstream();

if (upstream)
{
info.has_upstream = true;
info.upstream_name = upstream.value().short_name();

auto local_oid = head.target();
auto upstream_oid = upstream.value().target();

if (local_oid && upstream_oid)
{
git_graph_ahead_behind(&info.ahead, &info.behind, *this, local_oid, upstream_oid);
}
}
return info;
}

// Commits

commit_wrapper repository_wrapper::find_commit(std::string_view ref_name) const
Expand Down Expand Up @@ -458,7 +505,6 @@ config_wrapper repository_wrapper::get_config()
return config_wrapper(cfg);
}


// Diff

diff_wrapper repository_wrapper::diff_tree_to_index(tree_wrapper old_tree, std::optional<index_wrapper> index, git_diff_options* diffopts)
Expand Down
12 changes: 10 additions & 2 deletions src/wrapper/repository_wrapper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@
#include "../wrapper/tree_wrapper.hpp"
#include "../wrapper/wrapper_base.hpp"

struct branch_tracking_info
{
bool has_upstream;
std::string upstream_name;
size_t ahead;
size_t behind;
};

class repository_wrapper : public wrapper_base<git_repository>
{
public:
Expand Down Expand Up @@ -62,10 +70,10 @@ class repository_wrapper : public wrapper_base<git_repository>
branch_wrapper create_branch(std::string_view name, bool force);
branch_wrapper create_branch(std::string_view name, const commit_wrapper& commit, bool force);
branch_wrapper create_branch(std::string_view name, const annotated_commit_wrapper& commit, bool force);

branch_wrapper find_branch(std::string_view name) const;

branch_iterator iterate_branches(git_branch_t type) const;
std::optional<reference_wrapper> upstream() const;
branch_tracking_info get_tracking_info() const;

// Commits
commit_wrapper find_commit(std::string_view ref_name = "HEAD") const;
Expand Down
11 changes: 9 additions & 2 deletions src/wrapper/status_wrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,16 @@ status_list_wrapper::~status_list_wrapper()

status_list_wrapper status_list_wrapper::status_list(const repository_wrapper& rw)
{
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX |
GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR |
GIT_STATUS_OPT_SORT_CASE_SENSITIVELY;
opts.rename_threshold = 50;

status_list_wrapper res;
throw_if_error(git_status_list_new(&(res.p_resource), rw, nullptr));
throw_if_error(git_status_list_new(&(res.p_resource), rw, &opts));

std::size_t status_list_size = git_status_list_entrycount(res.p_resource);
for (std::size_t i = 0; i < status_list_size; ++i)
Expand Down Expand Up @@ -83,4 +91,3 @@ auto status_list_wrapper::get_entry_list(git_status_t status) const -> const sta
return m_empty;
}
}

Loading