@@ -12,7 +12,8 @@ diff_subcommand::diff_subcommand(const libgit2_object&, CLI::App& app)
1212{
1313 auto * sub = app.add_subcommand (" diff" , " Show changes between commits, commit and working tree, etc" );
1414
15- sub->add_option (" <files>" , m_files, " tree-ish objects to compare" );
15+ sub->add_option (" <files>" , m_files, " tree-ish objects to compare" )
16+ ->expected (0 , 2 );
1617
1718 sub->add_flag (" --stat" , m_stat_flag, " Generate a diffstat" );
1819 sub->add_flag (" --shortstat" , m_shortstat_flag, " Output only the last line of --stat" );
@@ -33,15 +34,16 @@ diff_subcommand::diff_subcommand(const libgit2_object&, CLI::App& app)
3334 sub->add_flag (" --patience" , m_patience_flag, " Generate diff using patience algorithm" );
3435 sub->add_flag (" --minimal" , m_minimal_flag, " Spend extra time to find smallest diff" );
3536
36- // TODO: add the following flags after the "move" subcommand has been implemented (needed for the tests)
37- // sub->add_option("-M,--find-renames", m_rename_threshold, "Detect renames")
38- // ->expected(0,1)
39- // ->each([this](const std::string&) { m_find_renames_flag = true; });
40- // sub->add_option("-C,--find-copies", m_copy_threshold, "Detect copies")
41- // ->expected(0,1)
42- // ->each([this](const std::string&) { m_find_copies_flag = true; });
43- // sub->add_flag("--find-copies-harder", m_find_copies_harder_flag, "Detect copies from unmodified files");
44- // sub->add_flag("-B,--break-rewrites", m_break_rewrites_flag, "Detect file rewrites");
37+ sub->add_option (" -M,--find-renames" , m_rename_threshold, " Detect renames" )
38+ ->expected (0 ,1 )
39+ ->default_val (50 )
40+ ->each ([this ](const std::string&) { m_find_renames_flag = true ; });
41+ sub->add_option (" -C,--find-copies" , m_copy_threshold, " Detect copies" )
42+ ->expected (0 ,1 )
43+ ->default_val (50 )
44+ ->each ([this ](const std::string&) { m_find_copies_flag = true ; });
45+ sub->add_flag (" --find-copies-harder" , m_find_copies_harder_flag, " Detect copies from unmodified files" );
46+ sub->add_flag (" -B,--break-rewrites" , m_break_rewrites_flag, " Detect file rewrites" );
4547
4648 sub->add_option (" -U,--unified" , m_context_lines, " Lines of context" );
4749 sub->add_option (" --inter-hunk-context" , m_interhunk_lines, " Context between hunks" );
@@ -142,7 +144,6 @@ static int colour_printer([[maybe_unused]] const git_diff_delta* delta, [[maybe_
142144 bool use_colour = *reinterpret_cast <bool *>(payload);
143145
144146 // Only print origin for context/addition/deletion lines
145- // For other line types, content already includes everything
146147 bool print_origin = (line->origin == GIT_DIFF_LINE_CONTEXT ||
147148 line->origin == GIT_DIFF_LINE_ADDITION ||
148149 line->origin == GIT_DIFF_LINE_DELETION);
@@ -172,6 +173,31 @@ static int colour_printer([[maybe_unused]] const git_diff_delta* delta, [[maybe_
172173 std::cout << termcolor::reset;
173174 }
174175
176+ // Print copy/rename headers ONLY after the "diff --git" line
177+ if (line->origin == GIT_DIFF_LINE_FILE_HDR)
178+ {
179+ if (delta->status == GIT_DELTA_COPIED)
180+ {
181+ if (use_colour)
182+ std::cout << termcolor::bold;
183+ std::cout << " similarity index " << delta->similarity << " %\n " ;
184+ std::cout << " copy from " << delta->old_file .path << " \n " ;
185+ std::cout << " copy to " << delta->new_file .path << " \n " ;
186+ if (use_colour)
187+ std::cout << termcolor::reset;
188+ }
189+ else if (delta->status == GIT_DELTA_RENAMED)
190+ {
191+ if (use_colour)
192+ std::cout << termcolor::bold;
193+ std::cout << " similarity index " << delta->similarity << " %\n " ;
194+ std::cout << " rename from " << delta->old_file .path << " \n " ;
195+ std::cout << " rename to " << delta->new_file .path << " \n " ;
196+ if (use_colour)
197+ std::cout << termcolor::reset;
198+ }
199+ }
200+
175201 return 0 ;
176202}
177203
@@ -183,33 +209,30 @@ void diff_subcommand::print_diff(diff_wrapper& diff, bool use_colour)
183209 return ;
184210 }
185211
186- // TODO: add the following flags after the "move" subcommand has been implemented (needed for the tests)
187- // if (m_find_renames_flag || m_find_copies_flag || m_find_copies_harder_flag || m_break_rewrites_flag)
188- // {
189- // git_diff_find_options find_opts;
190- // git_diff_find_options_init(&find_opts, GIT_DIFF_FIND_OPTIONS_VERSION);
191-
192- // if (m_find_renames_flag)
193- // {
194- // find_opts.flags |= GIT_DIFF_FIND_RENAMES;
195- // find_opts.rename_threshold = m_rename_threshold;
196- // }
197- // if (m_find_copies_flag)
198- // {
199- // find_opts.flags |= GIT_DIFF_FIND_COPIES;
200- // find_opts.copy_threshold = m_copy_threshold;
201- // }
202- // if (m_find_copies_harder_flag)
203- // {
204- // find_opts.flags |= GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED;
205- // }
206- // if (m_break_rewrites_flag)
207- // {
208- // find_opts.flags |= GIT_DIFF_FIND_REWRITES;
209- // }
210-
211- // diff.find_similar(&find_opts);
212- // }
212+ if (m_find_renames_flag || m_find_copies_flag || m_find_copies_harder_flag || m_break_rewrites_flag)
213+ {
214+ git_diff_find_options find_opts = GIT_DIFF_FIND_OPTIONS_INIT;
215+
216+ if (m_find_renames_flag || m_find_copies_flag)
217+ {
218+ find_opts.flags |= GIT_DIFF_FIND_RENAMES;
219+ find_opts.rename_threshold = (uint16_t )m_rename_threshold;
220+ }
221+ if (m_find_copies_flag)
222+ {
223+ find_opts.flags |= GIT_DIFF_FIND_COPIES;
224+ find_opts.copy_threshold = (uint16_t )m_copy_threshold;
225+ }
226+ if (m_find_copies_harder_flag)
227+ {
228+ find_opts.flags |= GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED;
229+ }
230+ if (m_break_rewrites_flag)
231+ {
232+ find_opts.flags |= GIT_DIFF_FIND_REWRITES;
233+ }
234+ diff.find_similar (&find_opts);
235+ }
213236
214237 git_diff_format_t format = GIT_DIFF_FORMAT_PATCH;
215238 if (m_name_only_flag)
@@ -228,7 +251,7 @@ void diff_subcommand::print_diff(diff_wrapper& diff, bool use_colour)
228251 diff.print (format, colour_printer, &use_colour);
229252}
230253
231- diff_wrapper compute_diff_no_index (std::vector<std::string> files, git_diff_options& diffopts) // std::pair<buf_wrapper, diff_wrapper>
254+ diff_wrapper compute_diff_no_index (std::vector<std::string> files, git_diff_options& diffopts)
232255{
233256 if (files.size () != 2 )
234257 {
@@ -242,11 +265,11 @@ diff_wrapper compute_diff_no_index(std::vector<std::string> files, git_diff_opti
242265
243266 if (file1_str.empty ())
244267 {
245- throw git_exception (" Cannot read file: " + files[0 ], git2cpp_error_code::GENERIC_ERROR); // TODO: check error code with git
268+ throw git_exception (" Cannot read file: " + files[0 ], git2cpp_error_code::GENERIC_ERROR);
246269 }
247270 if (file2_str.empty ())
248271 {
249- throw git_exception (" Cannot read file: " + files[1 ], git2cpp_error_code::GENERIC_ERROR); // TODO: check error code with git
272+ throw git_exception (" Cannot read file: " + files[1 ], git2cpp_error_code::GENERIC_ERROR);
250273 }
251274
252275 auto patch = patch_wrapper::patch_from_files (files[0 ], file1_str, files[1 ], file2_str, &diffopts);
@@ -263,6 +286,10 @@ void diff_subcommand::run()
263286 git_diff_options diffopts;
264287 git_diff_options_init (&diffopts, GIT_DIFF_OPTIONS_VERSION);
265288
289+ std::cerr << " DEBUG cached=" << m_cached_flag
290+ << " no_index=" << m_no_index_flag
291+ << " files=" << m_files.size () << " \n " ;
292+
266293 bool use_colour = false ;
267294 if (m_no_colour_flag)
268295 {
@@ -280,6 +307,11 @@ void diff_subcommand::run()
280307 use_colour = true ;
281308 }
282309
310+ if (m_cached_flag && m_no_index_flag)
311+ {
312+ throw git_exception (" --cached and --no-index are incompatible" , git2cpp_error_code::BAD_ARGUMENT);
313+ }
314+
283315 if (m_no_index_flag)
284316 {
285317 auto diff = compute_diff_no_index (m_files, diffopts);
@@ -302,11 +334,14 @@ void diff_subcommand::run()
302334 if (m_untracked_flag) { diffopts.flags |= GIT_DIFF_INCLUDE_UNTRACKED; }
303335 if (m_patience_flag) { diffopts.flags |= GIT_DIFF_PATIENCE; }
304336 if (m_minimal_flag) { diffopts.flags |= GIT_DIFF_MINIMAL; }
337+ if (m_find_copies_flag || m_find_copies_harder_flag || m_find_renames_flag)
338+ {
339+ diffopts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED;
340+ }
305341
306342 std::optional<tree_wrapper> tree1;
307343 std::optional<tree_wrapper> tree2;
308344
309- // TODO: throw error if m_files.size() > 2
310345 if (m_files.size () >= 1 )
311346 {
312347 tree1 = repo.treeish_to_tree (m_files[0 ]);
@@ -324,7 +359,7 @@ void diff_subcommand::run()
324359 }
325360 else if (m_cached_flag)
326361 {
327- if (m_cached_flag || !tree1)
362+ if (!tree1)
328363 {
329364 tree1 = repo.treeish_to_tree (" HEAD" );
330365 }
0 commit comments