[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/QuantStack/git2cpp/0.0.12/src/subcommand/status_subcommand.cpp [Back]  [Original]

#include "status_subcommand.hpp"

#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 

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_options.m_short_flag, "Give the output in the short-format.");
    sub->add_flag("--long", m_options.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_options.m_branch_flag, "Show the branch and tracking info even in short-format.");

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

namespace
{
    const std::string
        untracked_header = "Untracked files:\n  (use \"git add ...\" to include in what will be committed)\n";
    const std::string tobecommited_header = "Changes to be committed:\n  (use \"git reset HEAD ...\" to unstage)\n";
    const std::string
        ignored_header = "Ignored files:\n  (use \"git add -f ...\" to include in what will be committed)\n";
    const std::string
        notstagged_header = "Changes not staged for commit:\n  (use \"git add ...\" to update what will be committed)\n";
    // TODO: add the following ot notstagged_header after "checkout " is implemented: (use \"git
    // checkout -- ...\" to discard changes in working directory)\n";
    const std::string unmerged_header = "Unmerged paths:\n  (use \"git add ...\" 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 treeclean_message = "Nothing to commit, working tree clean";

    enum class output_format
    {
        DEFAULT = 0,
        LONG = 1,
        SHORT = 2
    };

    struct print_entry
    {
        std::string status;
        std::string item;
    };

    struct combined_entry
    {
        git_status_t index_status = GIT_STATUS_CURRENT;
        git_status_t workdir_status = GIT_STATUS_CURRENT;
        std::string item;
    };

    std::string get_print_status(git_status_t status, bool is_long)
    {
        std::string entry_status;
        if (is_long)
        {
            entry_status = get_status_msg(status).long_mod;
        }
        else
        {
            entry_status = get_status_msg(status).short_mod;
        }
        return entry_status;
    }

    char short_char_from_status(git_status_t status)
    {
        switch (status)
        {
            case GIT_STATUS_INDEX_NEW:
            case GIT_STATUS_WT_NEW:
                return 'A';
            case GIT_STATUS_INDEX_MODIFIED:
            case GIT_STATUS_WT_MODIFIED:
                return 'M';
            case GIT_STATUS_INDEX_DELETED:
            case GIT_STATUS_WT_DELETED:
                return 'D';
            case GIT_STATUS_INDEX_RENAMED:
            case GIT_STATUS_WT_RENAMED:
                return 'R';
            case GIT_STATUS_INDEX_TYPECHANGE:
            case GIT_STATUS_WT_TYPECHANGE:
                return 'T';
            default:
                return ' ';
        }
    }

    void update_tracked_dir_set(const char* path, std::set* tracked_dir_set = nullptr)
    {
        if (tracked_dir_set)
        {
            const size_t first_slash_idx = std::string_view(path).find('/');
            if (std::string::npos != first_slash_idx)
            {
                auto directory = std::string_view(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::unordered_map
    build_combined_status_map(status_list_wrapper& sl, std::set& tracked_dir_set)
    {
        std::unordered_map combined;

        auto update_status_map =
            [&sl, &tracked_dir_set, &combined](const git_status_t(&status_array)[5], bool index)
        {
            for (git_status_t status : status_array)
            {
                const auto& list = sl.get_entry_list(status);
                for (auto* entry : list)
                {
                    git_diff_delta* dd = index ? entry->head_to_index : entry->index_to_workdir;
                    const char* old_path = dd->old_file.path;
                    const char* new_path = dd->new_file.path;
                    update_tracked_dir_set(old_path, &tracked_dir_set);
                    std::string item = get_print_item(old_path, new_path);
                    auto& ce = combined[item];
                    ce.item = item;
                    if (index)
                    {
                        ce.index_status = status;
                    }
                    else
                    {
                        ce.workdir_status = status;
                    }
                }
            }
        };

        const git_status_t index_statuses[] = {
            GIT_STATUS_INDEX_NEW,
            GIT_STATUS_INDEX_MODIFIED,
            GIT_STATUS_INDEX_DELETED,
            GIT_STATUS_INDEX_RENAMED,
            GIT_STATUS_INDEX_TYPECHANGE
        };
        update_status_map(index_statuses, true);

        const git_status_t worktree_statuses[] = {
            GIT_STATUS_WT_NEW,
            GIT_STATUS_WT_MODIFIED,
            GIT_STATUS_WT_DELETED,
            GIT_STATUS_WT_TYPECHANGE,
            GIT_STATUS_WT_RENAMED
        };
        update_status_map(worktree_statuses, false);

        return combined;
    }

    void
    print_combined_short(const std::unordered_map& entries_map, bool is_coloured)
    {
        std::vector keys;
        keys.reserve(entries_map.size());
        for (const auto& kv : entries_map)
        {
            keys.push_back(kv.first);
        }
        std::sort(keys.begin(), keys.end());

        struct normal_row
        {
            char idx;
            char wt;
            std::string item;
        };

        std::vector normal_rows;
        std::vector untracked_items;
        std::vector ignored_items;

        for (const auto& k : keys)
        {
            const auto& ce = entries_map.at(k);

            // Collect special cases to print last (untracked, ignored) only when not staged.
            if ((ce.workdir_status & GIT_STATUS_WT_NEW) && ce.index_status == 0)
            {
                untracked_items.push_back(ce.item);
                continue;
            }
            if ((ce.workdir_status & GIT_STATUS_IGNORED || ce.index_status & GIT_STATUS_IGNORED)
                && ce.index_status == 0)
            {
                ignored_items.push_back(ce.item);
                continue;
            }

            // Regular two-column entry (may include index or worktree or both)
            char idx = short_char_from_status(ce.index_status);
            char wt = short_char_from_status(ce.workdir_status);
            normal_rows.push_back({idx, wt, ce.item});
        }

        for (const auto& r : normal_rows)
        {
            if (is_coloured)
            {
                std::cout 

Web Proxy Viewer  |  New URL  |  Original Page