Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

git-lfs support #10153

Open
wants to merge 44 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 40 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
8fb36a9
naive lfs support
b-camacho Mar 4, 2024
9ef1c28
parse out lfs url attr correctly
bcamacho2 Mar 11, 2024
f8bc96a
todo: actual smudge
bcamacho2 Mar 11, 2024
8c6641e
todo: fix git_attr_foreach
b-camacho Mar 12, 2024
cec370e
Merge branch 'master' into lfs
b-camacho Aug 6, 2024
787cc04
Merge branch 'master' into lfs
b-camacho Aug 6, 2024
ba417a2
wip
b-camacho Aug 26, 2024
87e0bc9
add libcurl to deps, builds now
b-camacho Oct 26, 2024
f4962fe
working smudge for GitSourceAccessor
b-camacho Nov 1, 2024
d2d6f20
Sink readFile impl for GitSourceAccessor
b-camacho Nov 5, 2024
75a1ba3
e2e test for gitlfs
b-camacho Nov 6, 2024
99705c6
Merge remote-tracking branch 'origin/master' into lfs
b-camacho Nov 6, 2024
ef6fa54
add libcurl to libfetchers deps
b-camacho Nov 6, 2024
4bdfeab
add lfs test, enable lfs on gitea in nixos test
b-camacho Nov 8, 2024
741a54d
tweak url parsing; add test case
b-camacho Nov 8, 2024
1939711
remove cruft
b-camacho Nov 8, 2024
b69fb15
better url handling; unit tests
b-camacho Nov 10, 2024
9a6388d
logs around getFingerprint
b-camacho Nov 12, 2024
0878e8f
use libgit2 pathspec matching instead of reimpl
b-camacho Nov 17, 2024
24453b7
pass path.rel instead of path.abs to gitattr matcher
b-camacho Nov 17, 2024
3252ca0
unit tests
b-camacho Nov 17, 2024
976941b
typo
b-camacho Nov 17, 2024
7bbc730
typo
b-camacho Nov 17, 2024
6d00439
Merge remote-tracking branch 'origin/master' into lfs
b-camacho Nov 17, 2024
b548e5c
typo
b-camacho Nov 18, 2024
f67b63f
remove debug prints
b-camacho Nov 18, 2024
b48dacd
Add docs
kip93 Nov 18, 2024
79d4106
Fix some errors, and add tests for them
kip93 Nov 20, 2024
70ffcc8
Fix format
kip93 Nov 20, 2024
93e63f7
FIx MacOS build
kip93 Nov 21, 2024
c9a8bd6
Work around https://github.com/libgit2/libgit2/issues/6946
kip93 Nov 25, 2024
d201b28
Pass lfs flag to submodules
kip93 Nov 25, 2024
38fb402
Fix lfs toURL missing argument
kip93 Nov 25, 2024
2bb2dc1
Fix lint
kip93 Nov 25, 2024
6778d24
Add (currently failing) test for flake inputs
kip93 Nov 25, 2024
85d6efb
trim storepath in test
b-camacho Dec 2, 2024
79a6438
fix sizeCallback
b-camacho Dec 2, 2024
169d62a
Merge branch 'master' into lfs
b-camacho Dec 2, 2024
9a7b14c
Fix flake path in test
kip93 Dec 4, 2024
65f78f7
Fix format
kip93 Dec 4, 2024
140b34b
Apply suggestions from code review
kip93 Dec 9, 2024
b1663fa
Re-introduce `git_attr_get_ext`
kip93 Dec 18, 2024
726f8fd
Rework tests
kip93 Dec 18, 2024
7756b22
Merge remote-tracking branch 'upstream/master' into lfs
kip93 Dec 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/libexpr/primops/fetchTree.cc
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,12 @@ static RegisterPrimOp primop_fetchTree({

Default: `false`

- `lfs` (Bool, optional)

Fetch any [Git LFS](https://git-lfs.com/) files.

Default: `false`

- `allRefs` (Bool, optional)

By default, this has no effect. This becomes relevant only once `shallow` cloning is disabled.
Expand Down Expand Up @@ -689,6 +695,13 @@ static RegisterPrimOp primop_fetchGit({

Make a shallow clone when fetching the Git tree.
When this is enabled, the options `ref` and `allRefs` have no effect anymore.

- `lfs` (default: `false`)

A boolean that when `true` specifies that [Git LFS] files should be fetched.

[Git LFS]: https://git-lfs.com/

- `allRefs`

Whether to fetch all references (eg. branches and tags) of the repository.
Expand Down
168 changes: 167 additions & 1 deletion src/libfetchers-tests/git-utils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <gtest/gtest.h>
#include "fs-sink.hh"
#include "serialise.hh"
#include "git-lfs-fetch.hh"

namespace nix {

Expand Down Expand Up @@ -78,7 +79,7 @@ TEST_F(GitUtilsTest, sink_basic)
// sink->createHardlink("foo-1.1/links/foo-2", CanonPath("foo-1.1/hello"));

auto result = repo->dereferenceSingletonDirectory(sink->flush());
auto accessor = repo->getAccessor(result, false);
auto accessor = repo->getAccessor(result, false, false);
auto entries = accessor->readDirectory(CanonPath::root);
ASSERT_EQ(entries.size(), 5);
ASSERT_EQ(accessor->readFile(CanonPath("hello")), "hello world");
Expand Down Expand Up @@ -109,4 +110,169 @@ TEST_F(GitUtilsTest, sink_hardlink)
}
};

namespace lfs {

TEST_F(GitUtilsTest, parseGitRemoteUrl)
{
{
GitUrl result = parseGitUrl("[email protected]:path/repo.git");
EXPECT_EQ(result.protocol, "ssh");
EXPECT_EQ(result.user, "git");
EXPECT_EQ(result.host, "example.com");
EXPECT_EQ(result.port, "");
EXPECT_EQ(result.path, "path/repo.git");
}

{
GitUrl result = parseGitUrl("example.com:/path/repo.git");
EXPECT_EQ(result.protocol, "ssh");
EXPECT_EQ(result.user, "");
EXPECT_EQ(result.host, "example.com");
EXPECT_EQ(result.port, "");
EXPECT_EQ(result.path, "/path/repo.git");
}

{
GitUrl result = parseGitUrl("example.com:path/repo.git");
EXPECT_EQ(result.protocol, "ssh");
EXPECT_EQ(result.user, "");
EXPECT_EQ(result.host, "example.com");
EXPECT_EQ(result.port, "");
EXPECT_EQ(result.path, "path/repo.git");
}

{
GitUrl result = parseGitUrl("https://example.com/path/repo.git");
EXPECT_EQ(result.protocol, "https");
EXPECT_EQ(result.user, "");
EXPECT_EQ(result.host, "example.com");
EXPECT_EQ(result.port, "");
EXPECT_EQ(result.path, "path/repo.git");
}

{
GitUrl result = parseGitUrl("ssh://[email protected]/path/repo.git");
EXPECT_EQ(result.protocol, "ssh");
EXPECT_EQ(result.user, "git");
EXPECT_EQ(result.host, "example.com");
EXPECT_EQ(result.port, "");
EXPECT_EQ(result.path, "path/repo.git");
}

{
GitUrl result = parseGitUrl("ssh://example/path/repo.git");
EXPECT_EQ(result.protocol, "ssh");
EXPECT_EQ(result.user, "");
EXPECT_EQ(result.host, "example");
EXPECT_EQ(result.port, "");
EXPECT_EQ(result.path, "path/repo.git");
}

{
GitUrl result = parseGitUrl("http://example.com:8080/path/repo.git");
EXPECT_EQ(result.protocol, "http");
EXPECT_EQ(result.user, "");
EXPECT_EQ(result.host, "example.com");
EXPECT_EQ(result.port, "8080");
EXPECT_EQ(result.path, "path/repo.git");
}

{
GitUrl result = parseGitUrl("invalid-url");
EXPECT_EQ(result.protocol, "");
EXPECT_EQ(result.user, "");
EXPECT_EQ(result.host, "");
EXPECT_EQ(result.port, "");
EXPECT_EQ(result.path, "");
}

{
GitUrl result = parseGitUrl("");
EXPECT_EQ(result.protocol, "");
EXPECT_EQ(result.user, "");
EXPECT_EQ(result.host, "");
EXPECT_EQ(result.port, "");
EXPECT_EQ(result.path, "");
}
}
TEST_F(GitUtilsTest, gitUrlToHttp)
{
{
const GitUrl url = parseGitUrl("[email protected]:user/repo.git");
EXPECT_EQ(url.toHttp(), "https://github.com/user/repo.git");
}
{
const GitUrl url = parseGitUrl("https://github.com/user/repo.git");
EXPECT_EQ(url.toHttp(), "https://github.com/user/repo.git");
}
{
const GitUrl url = parseGitUrl("http://github.com/user/repo.git");
EXPECT_EQ(url.toHttp(), "http://github.com/user/repo.git");
}
{
const GitUrl url = parseGitUrl("ssh://[email protected]:22/user/repo.git");
EXPECT_EQ(url.toHttp(), "https://github.com:22/user/repo.git");
}
{
const GitUrl url = parseGitUrl("invalid-url");
EXPECT_EQ(url.toHttp(), "");
}
}

TEST_F(GitUtilsTest, gitUrlToSsh)
{
{
const GitUrl url = parseGitUrl("https://example.com/user/repo.git");
const auto [host, path] = url.toSsh();
EXPECT_EQ(host, "example.com");
EXPECT_EQ(path, "user/repo.git");
}
{
const GitUrl url = parseGitUrl("[email protected]:user/repo.git");
const auto [host, path] = url.toSsh();
EXPECT_EQ(host, "[email protected]");
EXPECT_EQ(path, "user/repo.git");
}
}

class FetchAttributeTest : public ::testing::Test
{
protected:
void SetUp() override
{
// test literal (non-wildcard) matches too
std::string content1 = "litfile filter=lfs diff=lfs merge=lfs -text";
auto rules1 = parseGitAttrFile(content1);
fetch1.rules = std::move(rules1);

std::string content2 = "*.wildcard filter=lfs diff=lfs merge=lfs -text";
auto rules2 = parseGitAttrFile(content2);
fetch2.rules = std::move(rules2);
}

Fetch fetch1;
Fetch fetch2;
};

TEST_F(FetchAttributeTest, ExactMatch)
{
EXPECT_TRUE(fetch1.hasAttribute("litfile", "filter", "lfs"));
EXPECT_FALSE(fetch1.hasAttribute("other", "filter", "lfs"));
}

TEST_F(FetchAttributeTest, WildcardMatch)
{
EXPECT_TRUE(fetch2.hasAttribute("match.wildcard", "filter", "lfs"));
EXPECT_FALSE(fetch2.hasAttribute("nomatch.otherext", "filter", "lfs"));
EXPECT_FALSE(fetch2.hasAttribute("nomatch.wildcard.extra", "filter", "lfs"));
}

TEST_F(FetchAttributeTest, EmptyPath)
{
EXPECT_FALSE(fetch1.hasAttribute("", "filter", "lfs"));
EXPECT_FALSE(fetch2.hasAttribute("", "filter", "lfs"));
}

} // namespace lfs

} // namespace nix
Loading
Loading