Skip to content

Commit 42329c3

Browse files
committed
Add --deepen flag to the fetch command
1 parent 2f4d8e6 commit 42329c3

File tree

7 files changed

+146
-5
lines changed

7 files changed

+146
-5
lines changed

src/subcommand/fetch_subcommand.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ fetch_subcommand::fetch_subcommand(const libgit2_object&, CLI::App& app)
1414
sub->add_option("<remote>", m_remote_name, "The remote to fetch from")
1515
->default_val("origin");
1616
sub->add_option("--depth", m_depth, "deepen or shorten history of shallow clone");
17-
// sub->add_option("--deepen", m_deepen, "deepen history of shallow clone");
17+
sub->add_option("--deepen", m_deepen, "deepen history of shallow clone");
1818
// sub->add_option("--shallow-since", m_shallow_since, "<time>\ndeepen history of shallow repository based on time.");
1919
// sub->add_option("--shallow-exclude", m_shallow_exclude, "<ref>\ndeepen history of shallow clone, excluding ref");
2020
sub->add_flag("--unshallow", m_unshallow, "convert to a complete repository");
@@ -45,6 +45,11 @@ void fetch_subcommand::run()
4545
{
4646
fetch_opts.depth = GIT_FETCH_DEPTH_UNSHALLOW;
4747
}
48+
else if (m_deepen > 0)
49+
{
50+
size_t shallow_size = repo.shallow_depth_from_head();
51+
fetch_opts.depth = shallow_size + m_deepen;
52+
}
4853
else
4954
{
5055
fetch_opts.depth = m_depth;

src/subcommand/fetch_subcommand.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class fetch_subcommand
1616

1717
std::string m_remote_name;
1818
size_t m_depth = 0;
19-
// size_t m_deepen = 0;
19+
size_t m_deepen = 0;
2020
// std::string m_shallow_since;
2121
// std::string m_shallow_exclude;
2222
bool m_unshallow = false;

src/wrapper/commit_wrapper.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,18 @@ std::string commit_wrapper::commit_oid_tostr() const
2626
char buf[GIT_OID_SHA1_HEXSIZE + 1];
2727
return git_oid_tostr(buf, sizeof(buf), &this->oid());
2828
}
29+
30+
commit_list_wrapper commit_wrapper::get_parents_list() const
31+
{
32+
size_t parent_count = git_commit_parentcount(*this);
33+
std::vector<commit_wrapper> parents_list;
34+
parents_list.reserve(parent_count);
35+
36+
for (size_t i=0; parent_count; ++i)
37+
{
38+
git_commit* parent;
39+
git_commit_parent(&parent, *this, i);
40+
parents_list.push_back(std::move(commit_wrapper(parent)));
41+
}
42+
return commit_list_wrapper(std::move(parents_list));
43+
}

src/wrapper/commit_wrapper.hpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
#pragma once
22

33
#include <git2.h>
4-
#include <vector>
54
#include <string>
65

76
#include "../wrapper/wrapper_base.hpp"
87

8+
class commit_wrapper;
9+
using commit_list_wrapper = list_wrapper<commit_wrapper>;
10+
911
class commit_wrapper : public wrapper_base<git_commit>
1012
{
1113
public:
@@ -22,12 +24,12 @@ class commit_wrapper : public wrapper_base<git_commit>
2224
const git_oid& oid() const;
2325
std::string commit_oid_tostr() const;
2426

27+
commit_list_wrapper get_parents_list() const;
28+
2529
private:
2630

2731
commit_wrapper(git_commit* commit);
2832

2933
friend class repository_wrapper;
3034
friend class reference_wrapper;
3135
};
32-
33-
using commit_list_wrapper = list_wrapper<commit_wrapper>;

src/wrapper/repository_wrapper.cpp

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
1+
#include <algorithm>
2+
#include <fstream>
13
#include <iostream>
24

5+
#include "../utils/git_exception.hpp"
6+
#include "../wrapper/index_wrapper.hpp"
7+
#include "../wrapper/object_wrapper.hpp"
8+
#include "../wrapper/commit_wrapper.hpp"
9+
#include "../wrapper/remote_wrapper.hpp"
310
#include "../wrapper/repository_wrapper.hpp"
411

512
repository_wrapper::~repository_wrapper()
@@ -29,6 +36,11 @@ repository_wrapper repository_wrapper::clone(std::string_view url, std::string_v
2936
return rw;
3037
}
3138

39+
std::string repository_wrapper::git_path() const
40+
{
41+
return git_repository_path(*this);
42+
}
43+
3244
git_repository_state_t repository_wrapper::state() const
3345
{
3446
return git_repository_state_t(git_repository_state(*this));
@@ -271,6 +283,86 @@ void repository_wrapper::reset(const object_wrapper& target, git_reset_t reset_t
271283
throw_if_error(git_reset(*this, target, reset_type, &checkout_options));
272284
}
273285

286+
size_t repository_wrapper::shallow_depth_from_head() const
287+
{
288+
size_t depth = 0;
289+
if (!this->is_shallow())
290+
{
291+
return depth;
292+
}
293+
294+
std::string git_path = this->git_path();
295+
std::string shallow_path = git_path + "shallow";
296+
297+
std::vector<git_oid> boundaries_list;
298+
std::ifstream f(shallow_path);
299+
std::string line;
300+
while (std::getline(f, line))
301+
{
302+
if (!line.empty())
303+
{
304+
git_oid commit_oid;
305+
git_oid_fromstrp(&commit_oid, line.c_str());
306+
boundaries_list.push_back(commit_oid);
307+
}
308+
}
309+
310+
if (boundaries_list.size()==0)
311+
{
312+
return depth;
313+
}
314+
315+
commit_wrapper head_commit = this->find_commit("HEAD");
316+
commit_list_wrapper commits_list = head_commit.get_parents_list();
317+
std::vector<size_t> depth_list(commits_list.size(), 0);
318+
std::vector<size_t> final_depths(boundaries_list.size(), 0);
319+
size_t has_parent = commits_list.size() > 0;
320+
while (has_parent)
321+
{
322+
has_parent = 0;
323+
std::vector<commit_wrapper> temp_commits_list;
324+
std::vector<size_t> temp_depth_list;
325+
commit_list_wrapper parent_list({});
326+
std::vector<size_t> has_parent_list;
327+
328+
for (size_t i = 0; commits_list.size(); i++)
329+
{
330+
const commit_wrapper& commit = commits_list[i];
331+
size_t depth = depth_list[i];
332+
const git_oid& oid = commit.oid();
333+
bool is_boundary = std::find_if(boundaries_list.cbegin(), boundaries_list.cend(), [oid](const git_oid& val) {return git_oid_equal(&oid, &val);}) != boundaries_list.cend();
334+
if (is_boundary)
335+
{
336+
final_depths.push_back(depth + 1);
337+
}
338+
else
339+
{
340+
parent_list = commit.get_parents_list();
341+
if (parent_list.size() > 0)
342+
{
343+
has_parent_list.push_back(1);
344+
for (size_t j = 0; parent_list.size(); j++)
345+
{
346+
const commit_wrapper& c = parent_list[j];
347+
temp_commits_list.push_back(std::move(const_cast<commit_wrapper&>(c)));
348+
temp_depth_list.push_back(depth + 1);
349+
}
350+
}
351+
else
352+
{
353+
has_parent_list.push_back(0);
354+
}
355+
}
356+
}
357+
depth_list = temp_depth_list;
358+
commits_list = commit_list_wrapper(std::move(temp_commits_list));
359+
has_parent = *std::max_element(has_parent_list.begin(), has_parent_list.end());
360+
}
361+
362+
depth = *std::max_element(final_depths.begin(), final_depths.end());
363+
return depth;
364+
}
365+
274366
// Trees
275367

276368
void repository_wrapper::checkout_tree(const object_wrapper& target, const git_checkout_options opts)

src/wrapper/repository_wrapper.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ class repository_wrapper : public wrapper_base<git_repository>
3131
static repository_wrapper open(std::string_view directory);
3232
static repository_wrapper clone(std::string_view url, std::string_view path, const git_clone_options& opts);
3333

34+
std::string git_path() const;
3435
git_repository_state_t state() const;
3536
void state_cleanup();
3637

@@ -81,6 +82,9 @@ class repository_wrapper : public wrapper_base<git_repository>
8182
void set_head_detached(const annotated_commit_wrapper& commit);
8283
void reset(const object_wrapper& target, git_reset_t reset_type, const git_checkout_options& checkout_options);
8384

85+
// TODO: check if it's the right place to put the following
86+
size_t shallow_depth_from_head() const;
87+
8488
// Trees
8589
void checkout_tree(const object_wrapper& target, const git_checkout_options opts);
8690

test/test_remote.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,29 @@ def test_unshallow(git2cpp_path, tmp_path, run_in_tmp_path):
365365
assert p_log_2.stdout.count("Author") > 1
366366

367367

368+
def test_fetch_deepen(git2cpp_path, tmp_path, run_in_tmp_path):
369+
url = "https://github.com/xtensor-stack/xtl.git"
370+
clone_cmd = [git2cpp_path, "clone", "--depth", "1", url]
371+
p_clone = subprocess.run(clone_cmd, capture_output=True, cwd=tmp_path, text=True)
372+
assert p_clone.returncode == 0
373+
assert (tmp_path / "xtl").exists()
374+
375+
xtl_path = tmp_path / "xtl"
376+
377+
cmd_log = [git2cpp_path, "log"]
378+
p_log = subprocess.run(cmd_log, capture_output=True, cwd=xtl_path, text=True)
379+
assert p_log.returncode == 0
380+
assert p_log.stdout.count("Author") == 1
381+
382+
deepen_cmd = [git2cpp_path, "fetch", "--deepen", "2"]
383+
p_deepen = subprocess.run(deepen_cmd, capture_output=True, cwd=xtl_path, text=True)
384+
assert p_deepen.returncode == 0
385+
386+
p_log_2 = subprocess.run(cmd_log, capture_output=True, cwd=xtl_path, text=True)
387+
assert p_log_2.returncode == 0
388+
assert p_log_2.stdout.count("Author") > 1
389+
390+
368391
def test_remote_in_cloned_repo(xtl_clone, git2cpp_path, tmp_path):
369392
"""Test that cloned repos have remotes configured."""
370393
assert (tmp_path / "xtl").exists()

0 commit comments

Comments
 (0)