Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
125 changes: 82 additions & 43 deletions src/subcommand/diff_subcommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ diff_subcommand::diff_subcommand(const libgit2_object&, CLI::App& app)
{
auto* sub = app.add_subcommand("diff", "Show changes between commits, commit and working tree, etc");

sub->add_option("<files>", m_files, "tree-ish objects to compare");
sub->add_option("<files>", m_files, "tree-ish objects to compare")
->expected(0, 2);

sub->add_flag("--stat", m_stat_flag, "Generate a diffstat");
sub->add_flag("--shortstat", m_shortstat_flag, "Output only the last line of --stat");
Expand All @@ -33,15 +34,16 @@ diff_subcommand::diff_subcommand(const libgit2_object&, CLI::App& app)
sub->add_flag("--patience", m_patience_flag, "Generate diff using patience algorithm");
sub->add_flag("--minimal", m_minimal_flag, "Spend extra time to find smallest diff");

// TODO: add the following flags after the "move" subcommand has been implemented (needed for the tests)
// sub->add_option("-M,--find-renames", m_rename_threshold, "Detect renames")
// ->expected(0,1)
// ->each([this](const std::string&) { m_find_renames_flag = true; });
// sub->add_option("-C,--find-copies", m_copy_threshold, "Detect copies")
// ->expected(0,1)
// ->each([this](const std::string&) { m_find_copies_flag = true; });
// sub->add_flag("--find-copies-harder", m_find_copies_harder_flag, "Detect copies from unmodified files");
// sub->add_flag("-B,--break-rewrites", m_break_rewrites_flag, "Detect file rewrites");
sub->add_option("-M,--find-renames", m_rename_threshold, "Detect renames")
->expected(0,1)
->default_val(50)
->each([this](const std::string&) { m_find_renames_flag = true; });
sub->add_option("-C,--find-copies", m_copy_threshold, "Detect copies")
->expected(0,1)
->default_val(50)
->each([this](const std::string&) { m_find_copies_flag = true; });
sub->add_flag("--find-copies-harder", m_find_copies_harder_flag, "Detect copies from unmodified files");
sub->add_flag("-B,--break-rewrites", m_break_rewrites_flag, "Detect file rewrites");

sub->add_option("-U,--unified", m_context_lines, "Lines of context");
sub->add_option("--inter-hunk-context", m_interhunk_lines, "Context between hunks");
Expand Down Expand Up @@ -142,7 +144,6 @@ static int colour_printer([[maybe_unused]] const git_diff_delta* delta, [[maybe_
bool use_colour = *reinterpret_cast<bool*>(payload);

// Only print origin for context/addition/deletion lines
// For other line types, content already includes everything
bool print_origin = (line->origin == GIT_DIFF_LINE_CONTEXT ||
line->origin == GIT_DIFF_LINE_ADDITION ||
line->origin == GIT_DIFF_LINE_DELETION);
Expand Down Expand Up @@ -172,6 +173,39 @@ static int colour_printer([[maybe_unused]] const git_diff_delta* delta, [[maybe_
std::cout << termcolor::reset;
}

// Print copy/rename headers ONLY after the "diff --git" line
if (line->origin == GIT_DIFF_LINE_FILE_HDR)
{
if (delta->status == GIT_DELTA_COPIED)
{
if (use_colour)
{
std::cout << termcolor::bold;
}
std::cout << "similarity index " << delta->similarity << "%\n";
std::cout << "copy from " << delta->old_file.path << "\n";
std::cout << "copy to " << delta->new_file.path << "\n";
if (use_colour)
{
std::cout << termcolor::reset;
}
}
else if (delta->status == GIT_DELTA_RENAMED)
{
if (use_colour)
{
std::cout << termcolor::bold;
}
std::cout << "similarity index " << delta->similarity << "%\n";
std::cout << "rename from " << delta->old_file.path << "\n";
std::cout << "rename to " << delta->new_file.path << "\n";
if (use_colour)
{
std::cout << termcolor::reset;
}
}
}

return 0;
}

Expand All @@ -183,33 +217,30 @@ void diff_subcommand::print_diff(diff_wrapper& diff, bool use_colour)
return;
}

// TODO: add the following flags after the "move" subcommand has been implemented (needed for the tests)
// if (m_find_renames_flag || m_find_copies_flag || m_find_copies_harder_flag || m_break_rewrites_flag)
// {
// git_diff_find_options find_opts;
// git_diff_find_options_init(&find_opts, GIT_DIFF_FIND_OPTIONS_VERSION);

// if (m_find_renames_flag)
// {
// find_opts.flags |= GIT_DIFF_FIND_RENAMES;
// find_opts.rename_threshold = m_rename_threshold;
// }
// if (m_find_copies_flag)
// {
// find_opts.flags |= GIT_DIFF_FIND_COPIES;
// find_opts.copy_threshold = m_copy_threshold;
// }
// if (m_find_copies_harder_flag)
// {
// find_opts.flags |= GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED;
// }
// if (m_break_rewrites_flag)
// {
// find_opts.flags |= GIT_DIFF_FIND_REWRITES;
// }

// diff.find_similar(&find_opts);
// }
if (m_find_renames_flag || m_find_copies_flag || m_find_copies_harder_flag || m_break_rewrites_flag)
{
git_diff_find_options find_opts = GIT_DIFF_FIND_OPTIONS_INIT;

if (m_find_renames_flag || m_find_copies_flag)
{
find_opts.flags |= GIT_DIFF_FIND_RENAMES;
find_opts.rename_threshold = m_rename_threshold;
}
if (m_find_copies_flag)
{
find_opts.flags |= GIT_DIFF_FIND_COPIES;
find_opts.copy_threshold = m_copy_threshold;
}
if (m_find_copies_harder_flag)
{
find_opts.flags |= GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED;
}
if (m_break_rewrites_flag)
{
find_opts.flags |= GIT_DIFF_FIND_REWRITES;
}
diff.find_similar(&find_opts);
}

git_diff_format_t format = GIT_DIFF_FORMAT_PATCH;
if (m_name_only_flag)
Expand All @@ -228,7 +259,7 @@ void diff_subcommand::print_diff(diff_wrapper& diff, bool use_colour)
diff.print(format, colour_printer, &use_colour);
}

diff_wrapper compute_diff_no_index(std::vector<std::string> files, git_diff_options& diffopts) //std::pair<buf_wrapper, diff_wrapper>
diff_wrapper compute_diff_no_index(std::vector<std::string> files, git_diff_options& diffopts)
{
if (files.size() != 2)
{
Expand All @@ -242,11 +273,11 @@ diff_wrapper compute_diff_no_index(std::vector<std::string> files, git_diff_opti

if (file1_str.empty())
{
throw git_exception("Cannot read file: " + files[0], git2cpp_error_code::GENERIC_ERROR); //TODO: check error code with git
throw git_exception("Cannot read file: " + files[0], git2cpp_error_code::GENERIC_ERROR);
}
if (file2_str.empty())
{
throw git_exception("Cannot read file: " + files[1], git2cpp_error_code::GENERIC_ERROR); //TODO: check error code with git
throw git_exception("Cannot read file: " + files[1], git2cpp_error_code::GENERIC_ERROR);
}

auto patch = patch_wrapper::patch_from_files(files[0], file1_str, files[1], file2_str, &diffopts);
Expand Down Expand Up @@ -280,6 +311,11 @@ void diff_subcommand::run()
use_colour = true;
}

if (m_cached_flag && m_no_index_flag)
{
throw git_exception("--cached and --no-index are incompatible", git2cpp_error_code::BAD_ARGUMENT);
}

if (m_no_index_flag)
{
auto diff = compute_diff_no_index(m_files, diffopts);
Expand All @@ -302,11 +338,14 @@ void diff_subcommand::run()
if (m_untracked_flag) { diffopts.flags |= GIT_DIFF_INCLUDE_UNTRACKED; }
if (m_patience_flag) { diffopts.flags |= GIT_DIFF_PATIENCE; }
if (m_minimal_flag) { diffopts.flags |= GIT_DIFF_MINIMAL; }
if (m_find_copies_flag || m_find_copies_harder_flag || m_find_renames_flag)
{
diffopts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED;
}

std::optional<tree_wrapper> tree1;
std::optional<tree_wrapper> tree2;

// TODO: throw error if m_files.size() > 2
if (m_files.size() >= 1)
{
tree1 = repo.treeish_to_tree(m_files[0]);
Expand All @@ -324,7 +363,7 @@ void diff_subcommand::run()
}
else if (m_cached_flag)
{
if (m_cached_flag || !tree1)
if (!tree1)
{
tree1 = repo.treeish_to_tree("HEAD");
}
Expand Down
20 changes: 10 additions & 10 deletions src/subcommand/diff_subcommand.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,16 @@ class diff_subcommand
bool m_patience_flag = false;
bool m_minimal_flag = false;

// int m_rename_threshold = 50;
// bool m_find_renames_flag = false;
// int m_copy_threshold = 50;
// bool m_find_copies_flag = false;
// bool m_find_copies_harder_flag = false;
// bool m_break_rewrites_flag = false;

int m_context_lines = 3;
int m_interhunk_lines = 0;
int m_abbrev = 7;
uint16_t m_rename_threshold = 50;
bool m_find_renames_flag = false;
uint16_t m_copy_threshold = 50;
bool m_find_copies_flag = false;
bool m_find_copies_harder_flag = false;
bool m_break_rewrites_flag = false;

uint m_context_lines = 3;
uint m_interhunk_lines = 0;
uint m_abbrev = 7;

bool m_colour_flag = true;
bool m_no_colour_flag = false;
Expand Down
Loading
Loading