Commit 8651c10f by Ben Straub

Checkout: obey core.symlinks.

parent 3e026f1b
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "git2/tree.h" #include "git2/tree.h"
#include "git2/commit.h" #include "git2/commit.h"
#include "git2/blob.h" #include "git2/blob.h"
#include "git2/config.h"
#include "common.h" #include "common.h"
#include "refs.h" #include "refs.h"
...@@ -29,22 +30,26 @@ typedef struct tree_walk_data ...@@ -29,22 +30,26 @@ typedef struct tree_walk_data
git_indexer_stats *stats; git_indexer_stats *stats;
git_repository *repo; git_repository *repo;
git_odb *odb; git_odb *odb;
bool do_symlinks;
} tree_walk_data; } tree_walk_data;
static int blob_contents_to_link(git_repository *repo, git_buf *fnbuf, static int blob_contents_to_link(tree_walk_data *data, git_buf *fnbuf,
const git_oid *id) const git_oid *id)
{ {
int retcode = GIT_ERROR; int retcode = GIT_ERROR;
git_blob *blob; git_blob *blob;
/* Get the link target */ /* Get the link target */
if (!(retcode = git_blob_lookup(&blob, repo, id))) { if (!(retcode = git_blob_lookup(&blob, data->repo, id))) {
git_buf linktarget = GIT_BUF_INIT; git_buf linktarget = GIT_BUF_INIT;
if (!(retcode = git_blob__getbuf(&linktarget, blob))) { if (!(retcode = git_blob__getbuf(&linktarget, blob))) {
/* Create the link */ /* Create the link */
retcode = p_symlink(git_buf_cstr(&linktarget), const char *new = git_buf_cstr(&linktarget),
git_buf_cstr(fnbuf)); *old = git_buf_cstr(fnbuf);
retcode = data->do_symlinks
? p_symlink(new, old)
: git_futils_fake_symlink(new, old);
} }
git_buf_free(&linktarget); git_buf_free(&linktarget);
git_blob_free(blob); git_blob_free(blob);
...@@ -77,7 +82,7 @@ static int blob_contents_to_file(git_repository *repo, git_buf *fnbuf, ...@@ -77,7 +82,7 @@ static int blob_contents_to_file(git_repository *repo, git_buf *fnbuf,
return retcode; return retcode;
} }
static int checkout_walker(const char *path, git_tree_entry *entry, void *payload) static int checkout_walker(const char *path, const git_tree_entry *entry, void *payload)
{ {
int retcode = 0; int retcode = 0;
tree_walk_data *data = (tree_walk_data*)payload; tree_walk_data *data = (tree_walk_data*)payload;
...@@ -99,7 +104,7 @@ static int checkout_walker(const char *path, git_tree_entry *entry, void *payloa ...@@ -99,7 +104,7 @@ static int checkout_walker(const char *path, git_tree_entry *entry, void *payloa
path, path,
git_tree_entry_name(entry)); git_tree_entry_name(entry));
if (S_ISLNK(attr)) { if (S_ISLNK(attr)) {
retcode = blob_contents_to_link(data->repo, &fnbuf, retcode = blob_contents_to_link(data, &fnbuf,
git_tree_entry_id(entry)); git_tree_entry_id(entry));
} else { } else {
retcode = blob_contents_to_file(data->repo, &fnbuf, retcode = blob_contents_to_file(data->repo, &fnbuf,
...@@ -125,6 +130,7 @@ int git_checkout_force(git_repository *repo, git_indexer_stats *stats) ...@@ -125,6 +130,7 @@ int git_checkout_force(git_repository *repo, git_indexer_stats *stats)
git_indexer_stats dummy_stats; git_indexer_stats dummy_stats;
git_tree *tree; git_tree *tree;
tree_walk_data payload; tree_walk_data payload;
git_config *cfg;
assert(repo); assert(repo);
if (!stats) stats = &dummy_stats; if (!stats) stats = &dummy_stats;
...@@ -134,6 +140,15 @@ int git_checkout_force(git_repository *repo, git_indexer_stats *stats) ...@@ -134,6 +140,15 @@ int git_checkout_force(git_repository *repo, git_indexer_stats *stats)
return GIT_ERROR; return GIT_ERROR;
} }
/* Determine if symlinks should be handled */
if (!git_repository_config(&cfg, repo)) {
int temp = true;
if (!git_config_get_bool(&temp, cfg, "core.symlinks")) {
payload.do_symlinks = !!temp;
}
git_config_free(cfg);
}
stats->total = stats->processed = 0; stats->total = stats->processed = 0;
payload.stats = stats; payload.stats = stats;
payload.repo = repo; payload.repo = repo;
......
...@@ -230,7 +230,7 @@ static int find_and_add_filter(git_vector *filters, git_repository *repo, const ...@@ -230,7 +230,7 @@ static int find_and_add_filter(git_vector *filters, git_repository *repo, const
static int crlf_apply_to_workdir(git_filter *self, git_buf *dest, const git_buf *source) static int crlf_apply_to_workdir(git_filter *self, git_buf *dest, const git_buf *source)
{ {
/* TODO */ /* TODO */
return 0; return -1;
} }
int git_filter_add__crlf_to_odb(git_vector *filters, git_repository *repo, const char *path) int git_filter_add__crlf_to_odb(git_vector *filters, git_repository *repo, const char *path)
......
...@@ -480,3 +480,14 @@ int git_futils_find_global_file(git_buf *path, const char *filename) ...@@ -480,3 +480,14 @@ int git_futils_find_global_file(git_buf *path, const char *filename)
return 0; return 0;
#endif #endif
} }
int git_futils_fake_symlink(const char *old, const char *new)
{
int retcode = GIT_ERROR;
int fd = git_futils_creat_withpath(new, 0755, 0644);
if (fd >= 0) {
retcode = p_write(fd, old, strlen(old));
p_close(fd);
}
return retcode;
}
...@@ -179,4 +179,14 @@ extern int git_futils_find_global_file(git_buf *path, const char *filename); ...@@ -179,4 +179,14 @@ extern int git_futils_find_global_file(git_buf *path, const char *filename);
*/ */
extern int git_futils_find_system_file(git_buf *path, const char *filename); extern int git_futils_find_system_file(git_buf *path, const char *filename);
/**
* Create a "fake" symlink (text file containing the target path).
*
* @param new symlink file to be created
* @param old original symlink target
* @return 0 on success, -1 on error
*/
extern int git_futils_fake_symlink(const char *new, const char *old);
#endif /* INCLUDE_fileops_h__ */ #endif /* INCLUDE_fileops_h__ */
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "../posix.h" #include "../posix.h"
#include "path.h" #include "path.h"
#include "utf-conv.h" #include "utf-conv.h"
#include "repository.h"
#include <errno.h> #include <errno.h>
#include <io.h> #include <io.h>
#include <fcntl.h> #include <fcntl.h>
...@@ -219,8 +220,10 @@ int p_readlink(const char *link, char *target, size_t target_len) ...@@ -219,8 +220,10 @@ int p_readlink(const char *link, char *target, size_t target_len)
int p_symlink(const char *old, const char *new) int p_symlink(const char *old, const char *new)
{ {
/* TODO */ /* Real symlinks on NTFS require admin privileges. Until this changes,
return -1; * libgit2 just creates a text file with the link target in the contents.
*/
return git_futils_fake_symlink(old, new);
} }
int p_open(const char *path, int flags, ...) int p_open(const char *path, int flags, ...)
......
...@@ -12,7 +12,10 @@ static git_repository *g_repo; ...@@ -12,7 +12,10 @@ static git_repository *g_repo;
void test_checkout_checkout__initialize(void) void test_checkout_checkout__initialize(void)
{ {
const char *attributes = "*.txt text eol=cr\n";
g_repo = cl_git_sandbox_init("testrepo"); g_repo = cl_git_sandbox_init("testrepo");
cl_git_mkfile("./testrepo/.gitattributes", attributes);
} }
void test_checkout_checkout__cleanup(void) void test_checkout_checkout__cleanup(void)
...@@ -26,7 +29,7 @@ static void test_file_contents(const char *path, const char *expectedcontents) ...@@ -26,7 +29,7 @@ static void test_file_contents(const char *path, const char *expectedcontents)
int fd; int fd;
char buffer[1024] = {0}; char buffer[1024] = {0};
fd = p_open(path, O_RDONLY); fd = p_open(path, O_RDONLY);
cl_assert(fd); cl_assert(fd >= 0);
cl_assert_equal_i(p_read(fd, buffer, 1024), strlen(expectedcontents)); cl_assert_equal_i(p_read(fd, buffer, 1024), strlen(expectedcontents));
cl_assert_equal_s(expectedcontents, buffer); cl_assert_equal_s(expectedcontents, buffer);
cl_git_pass(p_close(fd)); cl_git_pass(p_close(fd));
...@@ -67,15 +70,34 @@ void test_checkout_checkout__stats(void) ...@@ -67,15 +70,34 @@ void test_checkout_checkout__stats(void)
/* TODO */ /* TODO */
} }
void test_checkout_checkout__links(void) void test_checkout_checkout__symlinks(void)
{ {
char link_data[1024]; git_config *cfg;
size_t link_size = 1024;
cl_git_pass(git_repository_config(&cfg, g_repo));
/* First try with symlinks forced on */
cl_git_pass(git_config_set_bool(cfg, "core.symlinks", true));
cl_git_pass(git_checkout_force(g_repo, NULL)); cl_git_pass(git_checkout_force(g_repo, NULL));
link_size = p_readlink("./testrepo/link_to_new.txt", link_data, link_size);
cl_assert_equal_i(link_size, strlen("new.txt")); #ifdef GIT_WIN32
link_data[link_size] = '\0'; test_file_contents("./testrepo/link_to_new.txt", "new.txt");
cl_assert_equal_s(link_data, "new.txt"); #else
test_file_contents("./testrepo/link_to_new.txt", "my new file\n"); {
char link_data[1024];
size_t link_size = 1024;
link_size = p_readlink("./testrepo/link_to_new.txt", link_data, link_size);
link_data[link_size] = '\0';
cl_assert_equal_i(link_size, strlen("new.txt"));
cl_assert_equal_s(link_data, "new.txt");
test_file_contents("./testrepo/link_to_new.txt", "my new file\n");
}
#endif
/* Now with symlinks forced off */
cl_git_pass(git_config_set_bool(cfg, "core.symlinks", false));
cl_git_pass(git_checkout_force(g_repo, NULL));
test_file_contents("./testrepo/link_to_new.txt", "new.txt");
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment