Skip to content

Commit 1cd32b6

Browse files
authored
Add shallow clone (#68)
1 parent 28cb7a9 commit 1cd32b6

11 files changed

+139
-16
lines changed

src/subcommand/clone_subcommand.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ clone_subcommand::clone_subcommand(const libgit2_object&, CLI::App& app)
1111

1212
sub->add_option("<repository>", m_repository, "The (possibly remote) repository to clone from.")->required();
1313
sub->add_option("<directory>", m_directory, "The name of a new directory to clone into.");
14+
sub->add_option("--depth", m_depth, "Create a shallow clone of that depth.");
15+
// sub->add_option("--shallow-since", m_shallow_since, "<time>\ndeepen history of shallow repository based on time.");
16+
// sub->add_option("--shallow-exclude", m_shallow_exclude, "<ref>\ndeepen history of shallow clone, excluding ref");
1417
sub->add_flag("--bare", m_bare, "Create a bare Git repository.");
1518

1619
sub->callback([this]() { this->run(); });
@@ -28,8 +31,11 @@ void clone_subcommand::run()
2831
clone_opts.fetch_opts.callbacks.sideband_progress = sideband_progress;
2932
clone_opts.fetch_opts.callbacks.transfer_progress = fetch_progress;
3033
clone_opts.fetch_opts.callbacks.payload = &pd;
34+
clone_opts.fetch_opts.depth = m_depth;
3135
clone_opts.bare = m_bare ? 1 : 0;
3236

37+
38+
3339
std::string short_name = m_directory;
3440
if (m_directory.empty())
3541
{

src/subcommand/clone_subcommand.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,7 @@ class clone_subcommand
1616
std::string m_repository = {};
1717
std::string m_directory = {};
1818
bool m_bare = false;
19+
size_t m_depth = 0;
20+
// std::string m_shallow_since;
21+
// std::vector<std::string> m_shallow_exclude;
1922
};

src/subcommand/fetch_subcommand.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ fetch_subcommand::fetch_subcommand(const libgit2_object&, CLI::App& app)
1313

1414
sub->add_option("<remote>", m_remote_name, "The remote to fetch from")
1515
->default_val("origin");
16+
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");
18+
// sub->add_option("--shallow-since", m_shallow_since, "<time>\ndeepen history of shallow repository based on time.");
19+
// sub->add_option("--shallow-exclude", m_shallow_exclude, "<ref>\ndeepen history of shallow clone, excluding ref");
20+
sub->add_flag("--unshallow", m_unshallow, "convert to a complete repository");
21+
// sub->add_flag("--update-shallow", m_update_shallow, "accept refs that update .git/shallow");
1622

1723
sub->callback([this]() { this->run(); });
1824
}
@@ -33,6 +39,23 @@ void fetch_subcommand::run()
3339
fetch_opts.callbacks.payload = &pd;
3440
fetch_opts.callbacks.update_refs = update_refs;
3541

42+
if (repo.is_shallow())
43+
{
44+
if (m_unshallow)
45+
{
46+
fetch_opts.depth = GIT_FETCH_DEPTH_UNSHALLOW;
47+
}
48+
else
49+
{
50+
fetch_opts.depth = m_depth;
51+
}
52+
}
53+
else
54+
{
55+
fetch_opts.depth = 0;
56+
}
57+
58+
3659
cursor_hider ch;
3760

3861
// Perform the fetch

src/subcommand/fetch_subcommand.hpp

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

3+
#include <cstddef>
34
#include <string>
45

56
#include <CLI/CLI.hpp>
@@ -13,7 +14,11 @@ class fetch_subcommand
1314
explicit fetch_subcommand(const libgit2_object&, CLI::App& app);
1415
void run();
1516

16-
private:
17-
1817
std::string m_remote_name;
18+
size_t m_depth = 0;
19+
// size_t m_deepen = 0;
20+
// std::string m_shallow_since;
21+
// std::string m_shallow_exclude;
22+
bool m_unshallow = false;
23+
// bool m_update_shallow = false;
1924
};

src/subcommand/revparse_subcommand.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ revparse_subcommand::revparse_subcommand(const libgit2_object&, CLI::App& app)
88
auto* sub = app.add_subcommand("rev-parse", "Pick out and message parameters");
99

1010
sub->add_flag("--is-bare-repository", m_is_bare_repository_flag);
11+
sub->add_flag("--is-shallow-repository", m_is_shallow_repository_flag);
1112

1213
sub->callback([this]() { this->run(); });
1314
}
@@ -21,8 +22,12 @@ void revparse_subcommand::run()
2122
{
2223
std::cout << std::boolalpha << repo.is_bare() << std::endl;
2324
}
25+
else if (m_is_shallow_repository_flag)
26+
{
27+
std::cout << std::boolalpha << repo.is_shallow() << std::endl;
28+
}
2429
else
2530
{
26-
std::cout << "revparse only supports --is-bare-repository for now" << std::endl;
31+
std::cout << "revparse only supports --is-bare-repository and --is-shallow-repository for now" << std::endl;
2732
}
2833
}

src/subcommand/revparse_subcommand.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@ class revparse_subcommand
1414
private:
1515

1616
bool m_is_bare_repository_flag = false;
17+
bool m_is_shallow_repository_flag = false;
1718
};
18-

src/wrapper/repository_wrapper.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ bool repository_wrapper::is_bare() const
5151
return git_repository_is_bare(*this);
5252
}
5353

54+
bool repository_wrapper::is_shallow() const
55+
{
56+
return git_repository_is_shallow(*this);
57+
}
58+
5459
// Head
5560

5661
bool repository_wrapper::is_head_unborn() const
@@ -326,13 +331,13 @@ std::vector<std::string> repository_wrapper::list_remotes() const
326331
{
327332
git_strarray remotes = {0};
328333
throw_if_error(git_remote_list(&remotes, *this));
329-
334+
330335
std::vector<std::string> result;
331336
for (size_t i = 0; i < remotes.count; ++i)
332337
{
333338
result.emplace_back(remotes.strings[i]);
334339
}
335-
340+
336341
git_strarray_dispose(&remotes);
337342
return result;
338343
}

src/wrapper/repository_wrapper.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class repository_wrapper : public wrapper_base<git_repository>
3434
void state_cleanup();
3535

3636
bool is_bare() const;
37+
bool is_shallow() const;
3738

3839
// Head
3940
bool is_head_unborn() const;

test/test_clone.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33

44
import pytest
55

6+
url = "https://github.com/xtensor-stack/xtl.git"
67

7-
def test_clone(git2cpp_path, tmp_path, run_in_tmp_path):
8-
url = "https://github.com/xtensor-stack/xtl.git"
98

9+
def test_clone(git2cpp_path, tmp_path, run_in_tmp_path):
1010
clone_cmd = [git2cpp_path, "clone", url]
1111
p_clone = subprocess.run(clone_cmd, capture_output=True, cwd=tmp_path, text=True)
1212
assert p_clone.returncode == 0
@@ -16,12 +16,24 @@ def test_clone(git2cpp_path, tmp_path, run_in_tmp_path):
1616

1717

1818
def test_clone_is_bare(git2cpp_path, tmp_path, run_in_tmp_path):
19-
url = "https://github.com/xtensor-stack/xtl.git"
20-
2119
clone_cmd = [git2cpp_path, "clone", "--bare", url]
2220
p_clone = subprocess.run(clone_cmd, capture_output=True, cwd=tmp_path, text=True)
2321
assert p_clone.returncode == 0
2422

2523
status_cmd = [git2cpp_path, "status"]
2624
p_status = subprocess.run(status_cmd, capture_output=True, cwd=tmp_path, text=True)
2725
assert p_status.returncode != 0
26+
27+
28+
def test_clone_shallow(git2cpp_path, tmp_path, run_in_tmp_path):
29+
clone_cmd = [git2cpp_path, "clone", "--depth", "1", url]
30+
p_clone = subprocess.run(clone_cmd, capture_output=True, cwd=tmp_path, text=True)
31+
assert p_clone.returncode == 0
32+
assert (tmp_path / "xtl").exists()
33+
34+
xtl_path = tmp_path / "xtl"
35+
36+
cmd_log = [git2cpp_path, "log"]
37+
p_log = subprocess.run(cmd_log, capture_output=True, cwd=xtl_path, text=True)
38+
assert p_log.returncode == 0
39+
assert p_log.stdout.count("Author") == 1

test/test_remote.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,54 @@ def test_fetch_default_origin(git2cpp_path, repo_with_remote):
317317
assert p.returncode in [0, 1]
318318

319319

320+
def test_fetch_depth(git2cpp_path, tmp_path, run_in_tmp_path):
321+
url = "https://github.com/xtensor-stack/xtl.git"
322+
clone_cmd = [git2cpp_path, "clone", "--depth", "1", url]
323+
p_clone = subprocess.run(clone_cmd, capture_output=True, cwd=tmp_path, text=True)
324+
assert p_clone.returncode == 0
325+
assert (tmp_path / "xtl").exists()
326+
327+
xtl_path = tmp_path / "xtl"
328+
329+
cmd_log = [git2cpp_path, "log"]
330+
p_log = subprocess.run(cmd_log, capture_output=True, cwd=xtl_path, text=True)
331+
assert p_log.returncode == 0
332+
assert p_log.stdout.count("Author") == 1
333+
334+
depth_cmd = [git2cpp_path, "fetch", "--depth", "3"]
335+
p_depth = subprocess.run(depth_cmd, capture_output=True, cwd=xtl_path, text=True)
336+
assert p_depth.returncode == 0
337+
338+
p_log_2 = subprocess.run(cmd_log, capture_output=True, cwd=xtl_path, text=True)
339+
assert p_log_2.returncode == 0
340+
assert p_log_2.stdout.count("Author") > 1
341+
342+
343+
def test_unshallow(git2cpp_path, tmp_path, run_in_tmp_path):
344+
url = "https://github.com/xtensor-stack/xtl.git"
345+
clone_cmd = [git2cpp_path, "clone", "--depth", "1", url]
346+
p_clone = subprocess.run(clone_cmd, capture_output=True, cwd=tmp_path, text=True)
347+
assert p_clone.returncode == 0
348+
assert (tmp_path / "xtl").exists()
349+
350+
xtl_path = tmp_path / "xtl"
351+
352+
cmd_log = [git2cpp_path, "log"]
353+
p_log = subprocess.run(cmd_log, capture_output=True, cwd=xtl_path, text=True)
354+
assert p_log.returncode == 0
355+
assert p_log.stdout.count("Author") == 1
356+
357+
unshallow_cmd = [git2cpp_path, "fetch", "--unshallow"]
358+
p_unshallow = subprocess.run(
359+
unshallow_cmd, capture_output=True, cwd=xtl_path, text=True
360+
)
361+
assert p_unshallow.returncode == 0
362+
363+
p_log_2 = subprocess.run(cmd_log, capture_output=True, cwd=xtl_path, text=True)
364+
assert p_log_2.returncode == 0
365+
assert p_log_2.stdout.count("Author") > 1
366+
367+
320368
def test_remote_in_cloned_repo(xtl_clone, git2cpp_path, tmp_path):
321369
"""Test that cloned repos have remotes configured."""
322370
assert (tmp_path / "xtl").exists()

0 commit comments

Comments
 (0)