From 8651c10f1ed5d42ef0ad6e9e9f654799b4ffb39c Mon Sep 17 00:00:00 2001 From: Ben Straub Date: Tue, 17 Jul 2012 19:57:37 -0700 Subject: [PATCH] Checkout: obey core.symlinks. --- src/checkout.c | 27 ++++++++++++++++++----- src/crlf.c | 2 +- src/fileops.c | 11 ++++++++++ src/fileops.h | 10 +++++++++ src/win32/posix_w32.c | 7 ++++-- tests-clar/checkout/checkout.c | 40 ++++++++++++++++++++++++++-------- 6 files changed, 79 insertions(+), 18 deletions(-) diff --git a/src/checkout.c b/src/checkout.c index 8ba3cf536..c4e75b67a 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -13,6 +13,7 @@ #include "git2/tree.h" #include "git2/commit.h" #include "git2/blob.h" +#include "git2/config.h" #include "common.h" #include "refs.h" @@ -29,22 +30,26 @@ typedef struct tree_walk_data git_indexer_stats *stats; git_repository *repo; git_odb *odb; + bool do_symlinks; } 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) { int retcode = GIT_ERROR; git_blob *blob; /* 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; if (!(retcode = git_blob__getbuf(&linktarget, blob))) { /* Create the link */ - retcode = p_symlink(git_buf_cstr(&linktarget), - git_buf_cstr(fnbuf)); + const char *new = git_buf_cstr(&linktarget), + *old = git_buf_cstr(fnbuf); + retcode = data->do_symlinks + ? p_symlink(new, old) + : git_futils_fake_symlink(new, old); } git_buf_free(&linktarget); git_blob_free(blob); @@ -77,7 +82,7 @@ static int blob_contents_to_file(git_repository *repo, git_buf *fnbuf, 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; 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 path, git_tree_entry_name(entry)); if (S_ISLNK(attr)) { - retcode = blob_contents_to_link(data->repo, &fnbuf, + retcode = blob_contents_to_link(data, &fnbuf, git_tree_entry_id(entry)); } else { retcode = blob_contents_to_file(data->repo, &fnbuf, @@ -125,6 +130,7 @@ int git_checkout_force(git_repository *repo, git_indexer_stats *stats) git_indexer_stats dummy_stats; git_tree *tree; tree_walk_data payload; + git_config *cfg; assert(repo); if (!stats) stats = &dummy_stats; @@ -134,6 +140,15 @@ int git_checkout_force(git_repository *repo, git_indexer_stats *stats) 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; payload.stats = stats; payload.repo = repo; diff --git a/src/crlf.c b/src/crlf.c index 888d86c36..f68938e61 100644 --- a/src/crlf.c +++ b/src/crlf.c @@ -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) { /* TODO */ - return 0; + return -1; } int git_filter_add__crlf_to_odb(git_vector *filters, git_repository *repo, const char *path) diff --git a/src/fileops.c b/src/fileops.c index 5849b79b2..bc58a0572 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -480,3 +480,14 @@ int git_futils_find_global_file(git_buf *path, const char *filename) return 0; #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; +} diff --git a/src/fileops.h b/src/fileops.h index b0c5779e5..594eacbd0 100644 --- a/src/fileops.h +++ b/src/fileops.h @@ -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); + +/** + * 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__ */ diff --git a/src/win32/posix_w32.c b/src/win32/posix_w32.c index c0d66c7ff..557760b94 100644 --- a/src/win32/posix_w32.c +++ b/src/win32/posix_w32.c @@ -7,6 +7,7 @@ #include "../posix.h" #include "path.h" #include "utf-conv.h" +#include "repository.h" #include #include #include @@ -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) { - /* TODO */ - return -1; + /* Real symlinks on NTFS require admin privileges. Until this changes, + * 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, ...) diff --git a/tests-clar/checkout/checkout.c b/tests-clar/checkout/checkout.c index 9ad41d032..e731ea7f5 100644 --- a/tests-clar/checkout/checkout.c +++ b/tests-clar/checkout/checkout.c @@ -12,7 +12,10 @@ static git_repository *g_repo; void test_checkout_checkout__initialize(void) { + const char *attributes = "*.txt text eol=cr\n"; + g_repo = cl_git_sandbox_init("testrepo"); + cl_git_mkfile("./testrepo/.gitattributes", attributes); } void test_checkout_checkout__cleanup(void) @@ -26,7 +29,7 @@ static void test_file_contents(const char *path, const char *expectedcontents) int fd; char buffer[1024] = {0}; 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_s(expectedcontents, buffer); cl_git_pass(p_close(fd)); @@ -67,15 +70,34 @@ void test_checkout_checkout__stats(void) /* TODO */ } -void test_checkout_checkout__links(void) +void test_checkout_checkout__symlinks(void) { - char link_data[1024]; - size_t link_size = 1024; + git_config *cfg; + 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)); - link_size = p_readlink("./testrepo/link_to_new.txt", link_data, link_size); - cl_assert_equal_i(link_size, strlen("new.txt")); - link_data[link_size] = '\0'; - cl_assert_equal_s(link_data, "new.txt"); - test_file_contents("./testrepo/link_to_new.txt", "my new file\n"); + +#ifdef GIT_WIN32 + test_file_contents("./testrepo/link_to_new.txt", "new.txt"); +#else + { + 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"); }