| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -48,6 +48,8 @@ set(GIT2CPP_SRC | |||
| 48 | 48 | ${GIT2CPP_SOURCE_DIR}/subcommand/checkout_subcommand.hpp | |
| 49 | 49 | ${GIT2CPP_SOURCE_DIR}/subcommand/clone_subcommand.cpp | |
| 50 | 50 | ${GIT2CPP_SOURCE_DIR}/subcommand/clone_subcommand.hpp | |
| 51 | + ${GIT2CPP_SOURCE_DIR}/subcommand/diff_subcommand.cpp | ||
| 52 | + ${GIT2CPP_SOURCE_DIR}/subcommand/diff_subcommand.hpp | ||
| 51 | 53 | ${GIT2CPP_SOURCE_DIR}/subcommand/commit_subcommand.cpp | |
| 52 | 54 | ${GIT2CPP_SOURCE_DIR}/subcommand/commit_subcommand.hpp | |
| 53 | 55 | ${GIT2CPP_SOURCE_DIR}/subcommand/config_subcommand.cpp | |
@@ -96,10 +98,16 @@ set(GIT2CPP_SRC | |||
| 96 | 98 | ${GIT2CPP_SOURCE_DIR}/wrapper/commit_wrapper.hpp | |
| 97 | 99 | ${GIT2CPP_SOURCE_DIR}/wrapper/config_wrapper.cpp | |
| 98 | 100 | ${GIT2CPP_SOURCE_DIR}/wrapper/config_wrapper.hpp | |
| 101 | + ${GIT2CPP_SOURCE_DIR}/wrapper/diff_wrapper.cpp | ||
| 102 | + ${GIT2CPP_SOURCE_DIR}/wrapper/diff_wrapper.hpp | ||
| 103 | + ${GIT2CPP_SOURCE_DIR}/wrapper/diffstats_wrapper.cpp | ||
| 104 | + ${GIT2CPP_SOURCE_DIR}/wrapper/diffstats_wrapper.hpp | ||
| 99 | 105 | ${GIT2CPP_SOURCE_DIR}/wrapper/index_wrapper.cpp | |
| 100 | 106 | ${GIT2CPP_SOURCE_DIR}/wrapper/index_wrapper.hpp | |
| 101 | 107 | ${GIT2CPP_SOURCE_DIR}/wrapper/object_wrapper.cpp | |
| 102 | 108 | ${GIT2CPP_SOURCE_DIR}/wrapper/object_wrapper.hpp | |
| 109 | + ${GIT2CPP_SOURCE_DIR}/wrapper/patch_wrapper.cpp | ||
| 110 | + ${GIT2CPP_SOURCE_DIR}/wrapper/patch_wrapper.hpp | ||
| 103 | 111 | ${GIT2CPP_SOURCE_DIR}/wrapper/rebase_wrapper.cpp | |
| 104 | 112 | ${GIT2CPP_SOURCE_DIR}/wrapper/rebase_wrapper.hpp | |
| 105 | 113 | ${GIT2CPP_SOURCE_DIR}/wrapper/refs_wrapper.cpp | |
@@ -114,6 +122,8 @@ set(GIT2CPP_SRC | |||
| 114 | 122 | ${GIT2CPP_SOURCE_DIR}/wrapper/signature_wrapper.hpp | |
| 115 | 123 | ${GIT2CPP_SOURCE_DIR}/wrapper/status_wrapper.cpp | |
| 116 | 124 | ${GIT2CPP_SOURCE_DIR}/wrapper/status_wrapper.hpp | |
| 125 | + ${GIT2CPP_SOURCE_DIR}/wrapper/tree_wrapper.cpp | ||
| 126 | + ${GIT2CPP_SOURCE_DIR}/wrapper/tree_wrapper.hpp | ||
| 117 | 127 | ${GIT2CPP_SOURCE_DIR}/wrapper/wrapper_base.hpp | |
| 118 | 128 | ${GIT2CPP_SOURCE_DIR}/main.cpp | |
| 119 | 129 | ${GIT2CPP_SOURCE_DIR}/version.hpp | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -11,6 +11,7 @@ | |||
| 11 | 11 | #include "subcommand/clone_subcommand.hpp" | |
| 12 | 12 | #include "subcommand/commit_subcommand.hpp" | |
| 13 | 13 | #include "subcommand/config_subcommand.hpp" | |
| 14 | + #include "subcommand/diff_subcommand.hpp" | ||
| 14 | 15 | #include "subcommand/fetch_subcommand.hpp" | |
| 15 | 16 | #include "subcommand/init_subcommand.hpp" | |
| 16 | 17 | #include "subcommand/log_subcommand.hpp" | |
@@ -44,6 +45,7 @@ int main(int argc, char** argv) | |||
| 44 | 45 | clone_subcommand clone(lg2_obj, app); | |
| 45 | 46 | commit_subcommand commit(lg2_obj, app); | |
| 46 | 47 | config_subcommand config(lg2_obj, app); | |
| 48 | + diff_subcommand diff(lg2_obj, app); | ||
| 47 | 49 | fetch_subcommand fetch(lg2_obj, app); | |
| 48 | 50 | reset_subcommand reset(lg2_obj, app); | |
| 49 | 51 | log_subcommand log(lg2_obj, app); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -24,7 +24,7 @@ void checkout_subcommand::run() | |||
| 24 | 24 | ||
| 25 | 25 | if (repo.state() != GIT_REPOSITORY_STATE_NONE) | |
| 26 | 26 | { | |
| 27 | - throw std::runtime_error("Cannot checkout, repository is in unexpected state"); | ||
| 27 | + std::runtime_error("Cannot checkout, repository is in unexpected state"); | ||
| 28 | 28 | } | |
| 29 | 29 | ||
| 30 | 30 | git_checkout_options options; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,318 @@ | |||
| 1 | + #include <git2.h> | ||
| 2 | + #include <git2/buffer.h> | ||
| 3 | + #include <git2/diff.h> | ||
| 4 | + #include <optional> | ||
| 5 | + #include <termcolor/termcolor.hpp> | ||
| 6 | + | ||
| 7 | + #include "../utils/common.hpp" | ||
| 8 | + #include "../subcommand/diff_subcommand.hpp" | ||
| 9 | + #include "../wrapper/patch_wrapper.hpp" | ||
| 10 | + #include "../wrapper/repository_wrapper.hpp" | ||
| 11 | + | ||
| 12 | + diff_subcommand::diff_subcommand(const libgit2_object&, CLI::App& app) | ||
| 13 | + { | ||
| 14 | + auto* sub = app.add_subcommand("diff", "Show changes between commits, commit and working tree, etc"); | ||
| 15 | + | ||
| 16 | + sub->add_option("<files>", m_files, "tree-ish objects to compare"); | ||
| 17 | + | ||
| 18 | + sub->add_flag("--stat", m_stat_flag, "Generate a diffstat"); | ||
| 19 | + sub->add_flag("--shortstat", m_shortstat_flag, "Output only the last line of --stat"); | ||
| 20 | + sub->add_flag("--numstat", m_numstat_flag, "Machine-friendly --stat"); | ||
| 21 | + sub->add_flag("--summary", m_summary_flag, "Output a condensed summary"); | ||
| 22 | + sub->add_flag("--name-only", m_name_only_flag, "Show only names of changed files"); | ||
| 23 | + sub->add_flag("--name-status", m_name_status_flag, "Show names and status of changed files"); | ||
| 24 | + sub->add_flag("--raw", m_raw_flag, "Generate the diff in raw format"); | ||
| 25 | + | ||
| 26 | + sub->add_flag("--cached,--staged", m_cached_flag, "Compare staged changes to HEAD"); | ||
| 27 | + sub->add_flag("--no-index", m_no_index_flag, "Compare two files on filesystem"); | ||
| 28 | + | ||
| 29 | + sub->add_flag("-R", m_reverse_flag, "Swap two inputs"); | ||
| 30 | + sub->add_flag("-a,--text", m_text_flag, "Treat all files as text"); | ||
| 31 | + sub->add_flag("--ignore-space-at-eol", m_ignore_space_at_eol_flag, "Ignore changes in whitespace at EOL"); | ||
| 32 | + sub->add_flag("-b,--ignore-space-change", m_ignore_space_change_flag, "Ignore changes in amount of whitespace"); | ||
| 33 | + sub->add_flag("-w,--ignore-all-space", m_ignore_all_space_flag, "Ignore whitespace when comparing lines"); | ||
| 34 | + sub->add_flag("--patience", m_patience_flag, "Generate diff using patience algorithm"); | ||
| 35 | + sub->add_flag("--minimal", m_minimal_flag, "Spend extra time to find smallest diff"); | ||
| 36 | + | ||
| 37 | + // TODO: add the following flags after the "move" subcommand has been implemented (needed for the tests) | ||
| 38 | + // sub->add_option("-M,--find-renames", m_rename_threshold, "Detect renames") | ||
| 39 | + // ->expected(0,1) | ||
| 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 | + // ->each([this](const std::string&) { m_find_copies_flag = true; }); | ||
| 44 | + // sub->add_flag("--find-copies-harder", m_find_copies_harder_flag, "Detect copies from unmodified files"); | ||
| 45 | + // sub->add_flag("-B,--break-rewrites", m_break_rewrites_flag, "Detect file rewrites"); | ||
| 46 | + | ||
| 47 | + sub->add_option("-U,--unified", m_context_lines, "Lines of context"); | ||
| 48 | + sub->add_option("--inter-hunk-context", m_interhunk_lines, "Context between hunks"); | ||
| 49 | + sub->add_option("--abbrev", m_abbrev, "Abbreviation length for object names") | ||
| 50 | + ->expected(0,1); | ||
| 51 | + | ||
| 52 | + sub->add_flag("--color", m_colour_flag, "Show colored diff"); | ||
| 53 | + sub->add_flag("--no-color", m_no_colour_flag, "Turn off colored diff"); | ||
| 54 | + | ||
| 55 | + sub->callback([this]() { this->run(); }); | ||
| 56 | + } | ||
| 57 | + | ||
| 58 | + void diff_subcommand::print_stats(const diff_wrapper& diff, bool use_colour) | ||
| 59 | + { | ||
| 60 | + git_diff_stats_format_t format; | ||
| 61 | + if (m_stat_flag) | ||
| 62 | + { | ||
| 63 | + format = GIT_DIFF_STATS_FULL; | ||
| 64 | + } | ||
| 65 | + if (m_shortstat_flag) | ||
| 66 | + { | ||
| 67 | + format = GIT_DIFF_STATS_SHORT; | ||
| 68 | + } | ||
| 69 | + if (m_numstat_flag) | ||
| 70 | + { | ||
| 71 | + format = GIT_DIFF_STATS_NUMBER; | ||
| 72 | + } | ||
| 73 | + if (m_summary_flag) | ||
| 74 | + { | ||
| 75 | + format = GIT_DIFF_STATS_INCLUDE_SUMMARY; | ||
| 76 | + } | ||
| 77 | + | ||
| 78 | + auto stats = diff.get_stats(); | ||
| 79 | + auto buf = stats.to_buf(format, 80); | ||
| 80 | + | ||
| 81 | + if (use_colour && m_stat_flag) | ||
| 82 | + { | ||
| 83 | + // Add colors to + and - characters | ||
| 84 | + std::string output(buf.ptr); | ||
| 85 | + bool in_parentheses = false; | ||
| 86 | + for (char c : output) | ||
| 87 | + { | ||
| 88 | + if (c == '(') | ||
| 89 | + { | ||
| 90 | + in_parentheses = true; | ||
| 91 | + std::cout << c; | ||
| 92 | + } | ||
| 93 | + else if (c == ')') | ||
| 94 | + { | ||
| 95 | + in_parentheses = false; | ||
| 96 | + std::cout << c; | ||
| 97 | + } | ||
| 98 | + else if (c == '+' && !in_parentheses) | ||
| 99 | + { | ||
| 100 | + std::cout << termcolor::green << '+' << termcolor::reset; | ||
| 101 | + } | ||
| 102 | + else if (c == '-' && !in_parentheses) | ||
| 103 | + { | ||
| 104 | + std::cout << termcolor::red << '-' << termcolor::reset; | ||
| 105 | + } | ||
| 106 | + else | ||
| 107 | + { | ||
| 108 | + std::cout << c; | ||
| 109 | + } | ||
| 110 | + } | ||
| 111 | + } | ||
| 112 | + else | ||
| 113 | + { | ||
| 114 | + std::cout << buf.ptr; | ||
| 115 | + } | ||
| 116 | + | ||
| 117 | + git_buf_dispose(&buf); | ||
| 118 | + } | ||
| 119 | + | ||
| 120 | + static int colour_printer([[maybe_unused]] const git_diff_delta* delta, [[maybe_unused]] const git_diff_hunk* hunk, const git_diff_line* line, void* payload) | ||
| 121 | + { | ||
| 122 | + bool* use_colour = reinterpret_cast<bool*>(payload); | ||
| 123 | + | ||
| 124 | + // Only print origin for context/addition/deletion lines | ||
| 125 | + // For other line types, content already includes everything | ||
| 126 | + bool print_origin = (line->origin == GIT_DIFF_LINE_CONTEXT || | ||
| 127 | + line->origin == GIT_DIFF_LINE_ADDITION || | ||
| 128 | + line->origin == GIT_DIFF_LINE_DELETION); | ||
| 129 | + | ||
| 130 | + if (*use_colour) | ||
| 131 | + { | ||
| 132 | + switch (line->origin) { | ||
| 133 | + case GIT_DIFF_LINE_ADDITION: std::cout << termcolor::green; break; | ||
| 134 | + case GIT_DIFF_LINE_DELETION: std::cout << termcolor::red; break; | ||
| 135 | + case GIT_DIFF_LINE_ADD_EOFNL: std::cout << termcolor::green; break; | ||
| 136 | + case GIT_DIFF_LINE_DEL_EOFNL: std::cout << termcolor::red; break; | ||
| 137 | + case GIT_DIFF_LINE_FILE_HDR: std::cout << termcolor::bold; break; | ||
| 138 | + case GIT_DIFF_LINE_HUNK_HDR: std::cout << termcolor::cyan; break; | ||
| 139 | + default: break; | ||
| 140 | + } | ||
| 141 | + } | ||
| 142 | + | ||
| 143 | + if (print_origin) | ||
| 144 | + { | ||
| 145 | + std::cout << line->origin; | ||
| 146 | + } | ||
| 147 | + | ||
| 148 | + std::cout << std::string_view(line->content, line->content_len); | ||
| 149 | + | ||
| 150 | + if (use_colour) | ||
| 151 | + { | ||
| 152 | + std::cout << termcolor::reset; | ||
| 153 | + } | ||
| 154 | + | ||
| 155 | + return 0; | ||
| 156 | + } | ||
| 157 | + | ||
| 158 | + void diff_subcommand::print_diff(diff_wrapper& diff, bool use_colour) | ||
| 159 | + { | ||
| 160 | + if (m_stat_flag || m_shortstat_flag || m_numstat_flag || m_summary_flag) | ||
| 161 | + { | ||
| 162 | + print_stats(diff, use_colour); | ||
| 163 | + return; | ||
| 164 | + } | ||
| 165 | + | ||
| 166 | + // TODO: add the following flags after the "move" subcommand has been implemented (needed for the tests) | ||
| 167 | + // if (m_find_renames_flag || m_find_copies_flag || m_find_copies_harder_flag || m_break_rewrites_flag) | ||
| 168 | + // { | ||
| 169 | + // git_diff_find_options find_opts; | ||
| 170 | + // git_diff_find_options_init(&find_opts, GIT_DIFF_FIND_OPTIONS_VERSION); | ||
| 171 | + | ||
| 172 | + // if (m_find_renames_flag) | ||
| 173 | + // { | ||
| 174 | + // find_opts.flags |= GIT_DIFF_FIND_RENAMES; | ||
| 175 | + // find_opts.rename_threshold = m_rename_threshold; | ||
| 176 | + // } | ||
| 177 | + // if (m_find_copies_flag) | ||
| 178 | + // { | ||
| 179 | + // find_opts.flags |= GIT_DIFF_FIND_COPIES; | ||
| 180 | + // find_opts.copy_threshold = m_copy_threshold; | ||
| 181 | + // } | ||
| 182 | + // if (m_find_copies_harder_flag) | ||
| 183 | + // { | ||
| 184 | + // find_opts.flags |= GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED; | ||
| 185 | + // } | ||
| 186 | + // if (m_break_rewrites_flag) | ||
| 187 | + // { | ||
| 188 | + // find_opts.flags |= GIT_DIFF_FIND_REWRITES; | ||
| 189 | + // } | ||
| 190 | + | ||
| 191 | + // diff.find_similar(&find_opts); | ||
| 192 | + // } | ||
| 193 | + | ||
| 194 | + git_diff_format_t format = GIT_DIFF_FORMAT_PATCH; | ||
| 195 | + if (m_name_only_flag) | ||
| 196 | + { | ||
| 197 | + format = GIT_DIFF_FORMAT_NAME_ONLY; | ||
| 198 | + } | ||
| 199 | + else if (m_name_status_flag) | ||
| 200 | + { | ||
| 201 | + format = GIT_DIFF_FORMAT_NAME_STATUS; | ||
| 202 | + } | ||
| 203 | + else if (m_raw_flag) | ||
| 204 | + { | ||
| 205 | + format = GIT_DIFF_FORMAT_RAW; | ||
| 206 | + } | ||
| 207 | + | ||
| 208 | + diff.print(format, colour_printer, &use_colour); | ||
| 209 | + } | ||
| 210 | + | ||
| 211 | + diff_wrapper compute_diff_no_index(std::vector<std::string> files, git_diff_options& diffopts) //std::pair<buf_wrapper, diff_wrapper> | ||
| 212 | + { | ||
| 213 | + if (files.size() != 2) | ||
| 214 | + { | ||
| 215 | + throw git_exception("two files should be provided as arguments", -1); //TODO: check error + code | ||
| 216 | + } | ||
| 217 | + | ||
| 218 | + git_diff_options_init(&diffopts, GIT_DIFF_OPTIONS_VERSION); | ||
| 219 | + | ||
| 220 | + std::string file1_str = read_file(files[0]); | ||
| 221 | + std::string file2_str = read_file(files[1]); | ||
| 222 | + | ||
| 223 | + if (file1_str.empty()) | ||
| 224 | + { | ||
| 225 | + throw git_exception("file " + files[0] + " cannot be read", -1); //TODO: check error + code | ||
| 226 | + } | ||
| 227 | + if (file2_str.empty()) | ||
| 228 | + { | ||
| 229 | + throw git_exception("file " + files[1] + " cannot be read", -1); //TODO: check error + code | ||
| 230 | + } | ||
| 231 | + | ||
| 232 | + auto patch = patch_wrapper::patch_from_files(files[0], file1_str, files[1], file2_str, &diffopts); | ||
| 233 | + auto buf = patch.to_buf(); | ||
| 234 | + auto diff = diff_wrapper::diff_from_buffer(buf); | ||
| 235 | + | ||
| 236 | + git_buf_dispose(&buf); | ||
| 237 | + | ||
| 238 | + return diff; | ||
| 239 | + } | ||
| 240 | + | ||
| 241 | + void diff_subcommand::run() | ||
| 242 | + { | ||
| 243 | + git_diff_options diffopts; | ||
| 244 | + git_diff_options_init(&diffopts, GIT_DIFF_OPTIONS_VERSION); | ||
| 245 | + | ||
| 246 | + bool use_colour = false; | ||
| 247 | + if (m_colour_flag) | ||
| 248 | + { | ||
| 249 | + use_colour = true; | ||
| 250 | + } | ||
| 251 | + if (m_no_colour_flag) | ||
| 252 | + { | ||
| 253 | + use_colour = false; | ||
| 254 | + } | ||
| 255 | + | ||
| 256 | + if (m_no_index_flag) | ||
| 257 | + { | ||
| 258 | + auto diff = compute_diff_no_index(m_files, diffopts); | ||
| 259 | + diff_subcommand::print_diff(diff, use_colour); | ||
| 260 | + } | ||
| 261 | + else | ||
| 262 | + { | ||
| 263 | + auto directory = get_current_git_path(); | ||
| 264 | + auto repo = repository_wrapper::open(directory); | ||
| 265 | + | ||
| 266 | + diffopts.context_lines = m_context_lines; | ||
| 267 | + diffopts.interhunk_lines = m_interhunk_lines; | ||
| 268 | + diffopts.id_abbrev = m_abbrev; | ||
| 269 | + | ||
| 270 | + if (m_reverse_flag) { diffopts.flags |= GIT_DIFF_REVERSE; } | ||
| 271 | + if (m_text_flag) { diffopts.flags |= GIT_DIFF_FORCE_TEXT; } | ||
| 272 | + if (m_ignore_space_at_eol_flag) { diffopts.flags |= GIT_DIFF_IGNORE_WHITESPACE_EOL; } | ||
| 273 | + if (m_ignore_space_change_flag) { diffopts.flags |= GIT_DIFF_IGNORE_WHITESPACE_CHANGE; } | ||
| 274 | + if (m_ignore_all_space_flag) { diffopts.flags |= GIT_DIFF_IGNORE_WHITESPACE; } | ||
| 275 | + if (m_untracked_flag) { diffopts.flags |= GIT_DIFF_INCLUDE_UNTRACKED; } | ||
| 276 | + if (m_patience_flag) { diffopts.flags |= GIT_DIFF_PATIENCE; } | ||
| 277 | + if (m_minimal_flag) { diffopts.flags |= GIT_DIFF_MINIMAL; } | ||
| 278 | + | ||
| 279 | + std::optional<tree_wrapper> tree1; | ||
| 280 | + std::optional<tree_wrapper> tree2; | ||
| 281 | + | ||
| 282 | + // TODO: throw error if m_files.size() > 2 | ||
| 283 | + if (m_files.size() >= 1) | ||
| 284 | + { | ||
| 285 | + tree1 = repo.treeish_to_tree(m_files[0]); | ||
| 286 | + } | ||
| 287 | + if (m_files.size() ==2) | ||
| 288 | + { | ||
| 289 | + tree2 = repo.treeish_to_tree(m_files[1]); | ||
| 290 | + } | ||
| 291 | + | ||
| 292 | + auto diff = [&repo, &tree1, &tree2, &diffopts, this]() | ||
| 293 | + { | ||
| 294 | + if (tree1.has_value() && tree2.has_value()) | ||
| 295 | + { | ||
| 296 | + return repo.diff_tree_to_tree(std::move(tree1.value()), std::move(tree2.value()), &diffopts); | ||
| 297 | + } | ||
| 298 | + else if (m_cached_flag) | ||
| 299 | + { | ||
| 300 | + if (m_cached_flag || !tree1) | ||
| 301 | + { | ||
| 302 | + tree1 = repo.treeish_to_tree("HEAD"); | ||
| 303 | + } | ||
| 304 | + return repo.diff_tree_to_index(std::move(tree1.value()), std::nullopt, &diffopts); | ||
| 305 | + } | ||
| 306 | + else if (tree1) | ||
| 307 | + { | ||
| 308 | + return repo.diff_tree_to_workdir_with_index(std::move(tree1.value()), &diffopts); | ||
| 309 | + } | ||
| 310 | + else | ||
| 311 | + { | ||
| 312 | + return repo.diff_index_to_workdir(std::nullopt, &diffopts); | ||
| 313 | + } | ||
| 314 | + }(); | ||
| 315 | + | ||
| 316 | + diff_subcommand::print_diff(diff, use_colour); | ||
| 317 | + } | ||
| 318 | + } | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments