Skip to content

Commit 1a6fb5b

Browse files
authored
Implement stash show (#107)
1 parent aae4150 commit 1a6fb5b

File tree

10 files changed

+128
-28
lines changed

10 files changed

+128
-28
lines changed

src/subcommand/diff_subcommand.cpp

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,12 @@ diff_subcommand::diff_subcommand(const libgit2_object&, CLI::App& app)
5454
sub->callback([this]() { this->run(); });
5555
}
5656

57-
void diff_subcommand::print_stats(const diff_wrapper& diff, bool use_colour)
57+
void print_stats(const diff_wrapper& diff, bool use_colour, bool stat_flag, bool shortstat_flag, bool numstat_flag, bool summary_flag)
5858
{
5959
git_diff_stats_format_t format;
60-
if (m_stat_flag)
60+
if (stat_flag)
6161
{
62-
if (m_shortstat_flag || m_numstat_flag || m_summary_flag)
62+
if (shortstat_flag || numstat_flag || summary_flag)
6363
{
6464
throw git_exception("Only one of --stat, --shortstat, --numstat and --summary should be provided.", git2cpp_error_code::BAD_ARGUMENT);
6565
}
@@ -68,9 +68,9 @@ void diff_subcommand::print_stats(const diff_wrapper& diff, bool use_colour)
6868
format = GIT_DIFF_STATS_FULL;
6969
}
7070
}
71-
else if (m_shortstat_flag)
71+
else if (shortstat_flag)
7272
{
73-
if (m_numstat_flag || m_summary_flag)
73+
if (numstat_flag || summary_flag)
7474
{
7575
throw git_exception("Only one of --stat, --shortstat, --numstat and --summary should be provided.", git2cpp_error_code::BAD_ARGUMENT);
7676
}
@@ -79,9 +79,9 @@ void diff_subcommand::print_stats(const diff_wrapper& diff, bool use_colour)
7979
format = GIT_DIFF_STATS_SHORT;
8080
}
8181
}
82-
else if (m_numstat_flag)
82+
else if (numstat_flag)
8383
{
84-
if (m_summary_flag)
84+
if (summary_flag)
8585
{
8686
throw git_exception("Only one of --stat, --shortstat, --numstat and --summary should be provided.", git2cpp_error_code::BAD_ARGUMENT);
8787
}
@@ -90,15 +90,15 @@ void diff_subcommand::print_stats(const diff_wrapper& diff, bool use_colour)
9090
format = GIT_DIFF_STATS_NUMBER;
9191
}
9292
}
93-
else if (m_summary_flag)
93+
else if (summary_flag)
9494
{
9595
format = GIT_DIFF_STATS_INCLUDE_SUMMARY;
9696
}
9797

9898
auto stats = diff.get_stats();
9999
auto buf = stats.to_buf(format, 80);
100100

101-
if (use_colour && m_stat_flag)
101+
if (use_colour && stat_flag)
102102
{
103103
// Add colors to + and - characters
104104
std::string output(buf.ptr);
@@ -179,7 +179,7 @@ void diff_subcommand::print_diff(diff_wrapper& diff, bool use_colour)
179179
{
180180
if (m_stat_flag || m_shortstat_flag || m_numstat_flag || m_summary_flag)
181181
{
182-
print_stats(diff, use_colour);
182+
print_stats(diff, use_colour, m_stat_flag, m_shortstat_flag, m_numstat_flag, m_summary_flag);
183183
return;
184184
}
185185

@@ -320,19 +320,19 @@ void diff_subcommand::run()
320320
{
321321
if (tree1.has_value() && tree2.has_value())
322322
{
323-
return repo.diff_tree_to_tree(std::move(tree1.value()), std::move(tree2.value()), &diffopts);
323+
return repo.diff_tree_to_tree(tree1.value(), tree2.value(), &diffopts);
324324
}
325325
else if (m_cached_flag)
326326
{
327327
if (m_cached_flag || !tree1)
328328
{
329329
tree1 = repo.treeish_to_tree("HEAD");
330330
}
331-
return repo.diff_tree_to_index(std::move(tree1.value()), std::nullopt, &diffopts);
331+
return repo.diff_tree_to_index(tree1.value(), std::nullopt, &diffopts);
332332
}
333333
else if (tree1)
334334
{
335-
return repo.diff_tree_to_workdir_with_index(std::move(tree1.value()), &diffopts);
335+
return repo.diff_tree_to_workdir_with_index(tree1.value(), &diffopts);
336336
}
337337
else
338338
{

src/subcommand/diff_subcommand.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ class diff_subcommand
1111
public:
1212

1313
explicit diff_subcommand(const libgit2_object&, CLI::App& app);
14-
void print_stats(const diff_wrapper& diff, bool use_colour);
1514
void print_diff(diff_wrapper& diff, bool use_colour);
1615
void run();
1716

@@ -53,3 +52,5 @@ class diff_subcommand
5352
bool m_colour_flag = true;
5453
bool m_no_colour_flag = false;
5554
};
55+
56+
void print_stats(const diff_wrapper& diff, bool use_colour, bool stat_flag, bool shortstat_flag, bool numstat_flag, bool summary_flag);

src/subcommand/stash_subcommand.cpp

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55

66
#include <git2/remote.h>
77

8+
#include "../subcommand/diff_subcommand.hpp"
89
#include "../subcommand/stash_subcommand.hpp"
910
#include "../subcommand/status_subcommand.hpp"
10-
#include "../wrapper/repository_wrapper.hpp"
1111

1212
bool has_subcommand(CLI::App* cmd)
1313
{
14-
std::vector<std::string> subs = { "push", "pop", "list", "apply" };
14+
std::vector<std::string> subs = { "push", "pop", "list", "apply", "show" };
1515
return std::any_of(subs.begin(), subs.end(), [cmd](const std::string& s) { return cmd->got_subcommand(s); });
1616
}
1717

@@ -22,10 +22,15 @@ stash_subcommand::stash_subcommand(const libgit2_object&, CLI::App& app)
2222
auto* list = stash->add_subcommand("list", "");
2323
auto* pop = stash->add_subcommand("pop", "");
2424
auto* apply = stash->add_subcommand("apply", "");
25+
auto* show = stash->add_subcommand("show", "Show the changes recorded in the stash as a diff");
2526

2627
push->add_option("-m,--message", m_message, "");
2728
pop->add_option("--index", m_index, "");
2829
apply->add_option("--index", m_index, "");
30+
show->add_flag("--stat", m_stat_flag, "Generate a diffstat");
31+
show->add_flag("--shortstat", m_shortstat_flag, "Output only the last line of --stat");
32+
show->add_flag("--numstat", m_numstat_flag, "Machine-friendly --stat");
33+
show->add_flag("--summary", m_summary_flag, "Output a condensed summary");
2934

3035
stash->callback([this,stash]()
3136
{
@@ -38,6 +43,7 @@ stash_subcommand::stash_subcommand(const libgit2_object&, CLI::App& app)
3843
list->callback([this]() { this->run_list(); });
3944
pop->callback([this]() { this->run_pop(); });
4045
apply->callback([this]() { this->run_apply(); });
46+
show->callback([this]() { this->run_show(); });
4147
}
4248

4349
void stash_subcommand::run_push()
@@ -66,14 +72,20 @@ void stash_subcommand::run_list()
6672
throw_if_error(git_stash_foreach(repo, list_stash_cb, NULL));
6773
}
6874

75+
git_oid stash_subcommand::resolve_stash_commit(repository_wrapper& repo)
76+
{
77+
std::string stash_spec = "stash@{" + std::to_string(m_index) + "}";
78+
auto stash_obj = repo.revparse_single(stash_spec);
79+
git_oid stash_id = stash_obj->oid();
80+
return stash_id;
81+
}
82+
6983
void stash_subcommand::run_pop()
7084
{
7185
auto directory = get_current_git_path();
7286
auto repo = repository_wrapper::open(directory);
7387

74-
std::string stash_spec = "stash@{" + std::to_string(m_index) + "}";
75-
auto stash_obj = repo.revparse_single(stash_spec);
76-
git_oid stash_id = stash_obj->oid();
88+
git_oid stash_id = resolve_stash_commit(repo);
7789
char id_string[GIT_OID_HEXSZ + 1];
7890
git_oid_tostr(id_string, sizeof(id_string), &stash_id);
7991

@@ -90,3 +102,33 @@ void stash_subcommand::run_apply()
90102
throw_if_error(git_stash_apply(repo, m_index, NULL));
91103
status_run();
92104
}
105+
106+
void stash_subcommand::run_show()
107+
{
108+
auto directory = get_current_git_path();
109+
auto repo = repository_wrapper::open(directory);
110+
111+
git_oid stash_id = resolve_stash_commit(repo);
112+
commit_wrapper stash_commit = repo.find_commit(stash_id);
113+
114+
if (git_commit_parentcount(stash_commit) < 1)
115+
{
116+
throw std::runtime_error("stash show: stash commit has no parents");
117+
}
118+
119+
commit_wrapper parent_commit = stash_commit.get_parent(0);
120+
121+
tree_wrapper stash_tree = stash_commit.tree();
122+
tree_wrapper parent_tree = parent_commit.tree();
123+
124+
git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
125+
126+
diff_wrapper diff = repo.diff_tree_to_tree(parent_tree, stash_tree, &diff_opts);
127+
128+
bool use_colour = true;
129+
if (!m_shortstat_flag && !m_numstat_flag && !m_summary_flag)
130+
{
131+
m_stat_flag = true;
132+
}
133+
print_stats(diff, use_colour, m_stat_flag, m_shortstat_flag, m_numstat_flag, m_summary_flag);
134+
}

src/subcommand/stash_subcommand.hpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,28 @@
33
#include <CLI/CLI.hpp>
44

55
#include "../utils/common.hpp"
6+
#include "../wrapper/repository_wrapper.hpp"
67

78
class stash_subcommand
89
{
910
public:
1011

1112
explicit stash_subcommand(const libgit2_object&, CLI::App& app);
13+
git_oid resolve_stash_commit(repository_wrapper& repo);
1214
void run_push();
1315
void run_list();
1416
void run_pop();
1517
void run_apply();
18+
void run_show();
19+
20+
private:
1621

1722
std::vector<std::string> m_options;
1823
std::string m_message = "";
1924
size_t m_index = 0;
25+
26+
bool m_stat_flag = false;
27+
bool m_shortstat_flag = false;
28+
bool m_numstat_flag = false;
29+
bool m_summary_flag = false;
2030
};

src/wrapper/commit_wrapper.cpp

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
#include "../wrapper/commit_wrapper.hpp"
21
#include <git2/commit.h>
32

3+
#include "../utils/git_exception.hpp"
4+
#include "tree_wrapper.hpp"
5+
#include "../wrapper/commit_wrapper.hpp"
6+
47
commit_wrapper::commit_wrapper(git_commit* commit)
58
: base_type(commit)
69
{
@@ -38,6 +41,13 @@ std::string commit_wrapper::summary() const
3841
return git_commit_summary(*this);
3942
}
4043

44+
commit_wrapper commit_wrapper::get_parent(size_t i) const
45+
{
46+
git_commit* parent;
47+
throw_if_error(git_commit_parent(&parent, *this, i));
48+
return commit_wrapper(parent);
49+
}
50+
4151
commit_list_wrapper commit_wrapper::get_parents_list() const
4252
{
4353
size_t parent_count = git_commit_parentcount(*this);
@@ -52,3 +62,10 @@ commit_list_wrapper commit_wrapper::get_parents_list() const
5262
}
5363
return commit_list_wrapper(std::move(parents_list));
5464
}
65+
66+
tree_wrapper commit_wrapper::tree() const
67+
{
68+
git_tree* tree;
69+
throw_if_error(git_commit_tree(&tree, *this));
70+
return tree_wrapper(tree);
71+
}

src/wrapper/commit_wrapper.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <string>
55

66
#include "../wrapper/wrapper_base.hpp"
7+
#include "../wrapper/tree_wrapper.hpp"
78

89
class commit_wrapper;
910
using commit_list_wrapper = list_wrapper<commit_wrapper>;
@@ -27,8 +28,11 @@ class commit_wrapper : public wrapper_base<git_commit>
2728
std::string message() const;
2829
std::string summary() const;
2930

31+
commit_wrapper get_parent(size_t i) const;
3032
commit_list_wrapper get_parents_list() const;
3133

34+
tree_wrapper tree() const;
35+
3236
private:
3337

3438
commit_wrapper(git_commit* commit);

src/wrapper/repository_wrapper.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -507,7 +507,7 @@ config_wrapper repository_wrapper::get_config()
507507

508508
// Diff
509509

510-
diff_wrapper repository_wrapper::diff_tree_to_index(tree_wrapper old_tree, std::optional<index_wrapper> index, git_diff_options* diffopts)
510+
diff_wrapper repository_wrapper::diff_tree_to_index(const tree_wrapper& old_tree, std::optional<index_wrapper> index, git_diff_options* diffopts)
511511
{
512512
git_diff* diff;
513513
git_index* idx = nullptr;
@@ -519,21 +519,21 @@ diff_wrapper repository_wrapper::diff_tree_to_index(tree_wrapper old_tree, std::
519519
return diff_wrapper(diff);
520520
}
521521

522-
diff_wrapper repository_wrapper::diff_tree_to_tree(tree_wrapper old_tree, tree_wrapper new_tree, git_diff_options* diffopts)
522+
diff_wrapper repository_wrapper::diff_tree_to_tree(const tree_wrapper& old_tree, const tree_wrapper& new_tree, git_diff_options* diffopts)
523523
{
524524
git_diff* diff;
525525
throw_if_error(git_diff_tree_to_tree(&diff, *this, old_tree, new_tree, diffopts));
526526
return diff_wrapper(diff);
527527
}
528528

529-
diff_wrapper repository_wrapper::diff_tree_to_workdir(tree_wrapper old_tree, git_diff_options* diffopts)
529+
diff_wrapper repository_wrapper::diff_tree_to_workdir(const tree_wrapper& old_tree, git_diff_options* diffopts)
530530
{
531531
git_diff* diff;
532532
throw_if_error(git_diff_tree_to_workdir(&diff, *this, old_tree, diffopts));
533533
return diff_wrapper(diff);
534534
}
535535

536-
diff_wrapper repository_wrapper::diff_tree_to_workdir_with_index(tree_wrapper old_tree, git_diff_options* diffopts)
536+
diff_wrapper repository_wrapper::diff_tree_to_workdir_with_index(const tree_wrapper& old_tree, git_diff_options* diffopts)
537537
{
538538
git_diff* diff;
539539
throw_if_error(git_diff_tree_to_workdir_with_index(&diff, *this, old_tree, diffopts));

src/wrapper/repository_wrapper.hpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,10 +116,10 @@ class repository_wrapper : public wrapper_base<git_repository>
116116
config_wrapper get_config();
117117

118118
// Diff
119-
diff_wrapper diff_tree_to_index(tree_wrapper old_tree, std::optional<index_wrapper> index, git_diff_options* diffopts);
120-
diff_wrapper diff_tree_to_tree(tree_wrapper old_tree, tree_wrapper new_tree, git_diff_options* diffopts);
121-
diff_wrapper diff_tree_to_workdir(tree_wrapper old_tree, git_diff_options* diffopts);
122-
diff_wrapper diff_tree_to_workdir_with_index(tree_wrapper old_tree, git_diff_options* diffopts);
119+
diff_wrapper diff_tree_to_index(const tree_wrapper& old_tree, std::optional<index_wrapper> index, git_diff_options* diffopts);
120+
diff_wrapper diff_tree_to_tree(const tree_wrapper& old_tree, const tree_wrapper& new_tree, git_diff_options* diffopts);
121+
diff_wrapper diff_tree_to_workdir(const tree_wrapper& old_tree, git_diff_options* diffopts);
122+
diff_wrapper diff_tree_to_workdir_with_index(const tree_wrapper& old_tree, git_diff_options* diffopts);
123123
diff_wrapper diff_index_to_workdir(std::optional<index_wrapper> index, git_diff_options* diffopts);
124124

125125
//Tags

src/wrapper/tree_wrapper.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,6 @@ class tree_wrapper : public wrapper_base<git_tree>
1919

2020
tree_wrapper(git_tree* tree);
2121

22+
friend class commit_wrapper;
2223
friend class repository_wrapper;
2324
};

test/test_stash.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,3 +137,28 @@ def test_stash_apply(xtl_clone, commit_env_config, git2cpp_path, tmp_path, index
137137
assert "stash@{0}" in p_list.stdout
138138
if index_flag != "":
139139
assert "stash@{1}" in p_list.stdout
140+
141+
142+
def test_stash_show(xtl_clone, commit_env_config, git2cpp_path, tmp_path):
143+
assert (tmp_path / "xtl").exists()
144+
xtl_path = tmp_path / "xtl"
145+
146+
filename = "mook_show.txt"
147+
p = xtl_path / filename
148+
p.write_text("Hello")
149+
150+
cmd_add = [git2cpp_path, "add", filename]
151+
p_add = subprocess.run(cmd_add, cwd=xtl_path, text=True)
152+
assert p_add.returncode == 0
153+
154+
cmd_stash = [git2cpp_path, "stash"]
155+
p_stash = subprocess.run(cmd_stash, capture_output=True, cwd=xtl_path, text=True)
156+
assert p_stash.returncode == 0
157+
158+
cmd_show = [git2cpp_path, "stash", "show", "--stat"]
159+
p_show = subprocess.run(cmd_show, capture_output=True, cwd=xtl_path, text=True)
160+
assert p_show.returncode == 0
161+
162+
# A diffstat should mention the file and summary "file changed"
163+
assert filename in p_show.stdout
164+
assert "1 file changed" in p_show.stdout

0 commit comments

Comments
 (0)