Skip to content
10 changes: 10 additions & 0 deletions include/git2/repository.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "types.h"
#include "oid.h"
#include "buffer.h"
#include "oidarray.h"

/**
* @file git2/repository.h
Expand Down Expand Up @@ -745,6 +746,15 @@ GIT_EXTERN(const char *) git_repository_get_namespace(git_repository *repo);
*/
GIT_EXTERN(int) git_repository_is_shallow(git_repository *repo);

/**
* Determine the shallow roots of the repository
*
* @param out An array of shallow oids.
* @param repo The repository
* @return 0 on success, an error otherwise.
*/
GIT_EXTERN(int) git_repository_shallow_roots(git_oidarray *out, git_repository *repo);

/**
* Retrieve the configured identity to use for reflogs
*
Expand Down
66 changes: 64 additions & 2 deletions src/repository.c
Original file line number Diff line number Diff line change
Expand Up @@ -2477,6 +2477,70 @@ int git_repository_state_cleanup(git_repository *repo)
return git_repository__cleanup_files(repo, state_files, ARRAY_SIZE(state_files));
}

int git_repository__shallow_roots(git_array_oid_t *out, git_repository *repo)
{
git_buf path = GIT_BUF_INIT;
git_buf contents = GIT_BUF_INIT;
int error, updated, line_num = 1;
char *line;
char *buffer;

assert(out);

if ((error = git_buf_joinpath(&path, repo->path_repository, "shallow")) < 0)
return error;

error = git_futils_readbuffer_updated(&contents, git_buf_cstr(&path), &repo->shallow_checksum, &updated);
git_buf_free(&path);

if (error < 0)
return error;

if (!updated) {
goto unchanged;
}

git_array_clear(repo->shallow_oids);

buffer = contents.ptr;
while ((line = git__strsep(&buffer, "\n")) != NULL) {
git_oid *oid = git_array_alloc(repo->shallow_oids);

error = git_oid_fromstr(oid, line);
if (error < 0) {
giterr_set(GITERR_REPOSITORY, "Invalid OID at line %d", line_num);
git_array_clear(repo->shallow_oids);
return -1;
}
++line_num;
}

if (*buffer) {
giterr_set(GITERR_REPOSITORY, "No EOL at line %d", line_num);
git_array_clear(repo->shallow_oids);
return -1;
}

unchanged:
*out = repo->shallow_oids;

return 0;
}

int git_repository_shallow_roots(git_oidarray *out, git_repository *repo)
{
int ret;
git_array_oid_t array;

assert(out);

ret = git_repository__shallow_roots(&array, repo);

git_oidarray__from_array(out, &array);

return ret;
}

int git_repository_is_shallow(git_repository *repo)
{
git_buf path = GIT_BUF_INIT;
Expand All @@ -2494,8 +2558,6 @@ int git_repository_is_shallow(git_repository *repo)
return 0;
}

if (error < 0)
return error;
return st.st_size == 0 ? 0 : 1;
}

Expand Down
6 changes: 6 additions & 0 deletions src/repository.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "attrcache.h"
#include "submodule.h"
#include "diff_driver.h"
#include "oidarray.h"

#define DOT_GIT ".git"
#define GIT_DIR DOT_GIT "/"
Expand Down Expand Up @@ -140,6 +141,9 @@ struct git_repository {

unsigned int lru_counter;

git_oid shallow_checksum;
git_array_oid_t shallow_oids;

git_atomic attr_session_key;

git_cvar_value cvar_cache[GIT_CVAR_CACHE_MAX];
Expand Down Expand Up @@ -212,4 +216,6 @@ extern size_t git_repository__reserved_names_posix_len;
bool git_repository__reserved_names(
git_buf **out, size_t *outlen, git_repository *repo, bool include_ntfs);

int git_repository__shallow_roots(git_array_oid_t *out, git_repository *repo);

#endif
53 changes: 53 additions & 0 deletions tests/repo/shallow.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
#include "fileops.h"

static git_repository *g_repo;
static git_oid g_shallow_oid;

void test_repo_shallow__initialize(void)
{
cl_git_pass(git_oid_fromstr(&g_shallow_oid, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"));
}

void test_repo_shallow__cleanup(void)
Expand Down Expand Up @@ -37,3 +39,54 @@ void test_repo_shallow__clears_errors(void)
cl_assert_equal_i(0, git_repository_is_shallow(g_repo));
cl_assert_equal_p(NULL, giterr_last());
}

void test_repo_shallow__shallow_oids(void)
{
git_oidarray oids, oids2;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These variables need to be freed after we're done with them.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Grr 😆. Any chance I could graft leaks on clar's exit, so I get a nice reminder when that happens ?

g_repo = cl_git_sandbox_init("shallow.git");

cl_git_pass(git_repository_shallow_roots(&oids, g_repo));
cl_assert_equal_i(1, oids.count);
cl_assert_equal_oid(&g_shallow_oid, &oids.ids[0]);

cl_git_pass(git_repository_shallow_roots(&oids2, g_repo));
cl_assert_equal_p(oids.ids, oids2.ids);
}

void test_repo_shallow__cache_clearing(void)
{
git_oidarray oids, oids2;
git_oid tmp_oid;

git_oid_fromstr(&tmp_oid, "0000000000000000000000000000000000000000");
g_repo = cl_git_sandbox_init("shallow.git");

cl_git_pass(git_repository_shallow_roots(&oids, g_repo));
cl_assert_equal_i(1, oids.count);
cl_assert_equal_oid(&g_shallow_oid, &oids.ids[0]);

cl_git_mkfile("shallow.git/shallow",
"be3563ae3f795b2b4353bcce3a527ad0a4f7f644\n"
"0000000000000000000000000000000000000000\n"
);

cl_git_pass(git_repository_shallow_roots(&oids2, g_repo));
cl_assert_equal_i(2, oids2.count);
cl_assert_equal_oid(&g_shallow_oid, &oids2.ids[0]);
cl_assert_equal_oid(&tmp_oid, &oids2.ids[1]);
}

void test_repo_shallow__errors_on_borked(void)
{
git_oidarray oids;

g_repo = cl_git_sandbox_init("shallow.git");

cl_git_mkfile("shallow.git/shallow", "lolno");

cl_git_fail_with(git_repository_shallow_roots(&oids, g_repo), -1);

cl_git_mkfile("shallow.git/shallow", "lolno\n");

cl_git_fail_with(git_repository_shallow_roots(&oids, g_repo), -1);
}