Commit 94f742ba by Carlos Martín Nieto

fileops: allow linking files when copying directory structures

When passed the LINK_FILES flag, the recursive copy will hardlink files
instead of copying them.
parent c1dbfcbb
...@@ -740,9 +740,11 @@ static int _cp_r_callback(void *ref, git_buf *from) ...@@ -740,9 +740,11 @@ static int _cp_r_callback(void *ref, git_buf *from)
return error; return error;
/* make symlink or regular file */ /* make symlink or regular file */
if (S_ISLNK(from_st.st_mode)) if (info->flags & GIT_CPDIR_LINK_FILES) {
error = p_link(from->ptr, info->to.ptr);
} else if (S_ISLNK(from_st.st_mode)) {
error = cp_link(from->ptr, info->to.ptr, (size_t)from_st.st_size); error = cp_link(from->ptr, info->to.ptr, (size_t)from_st.st_size);
else { } else {
mode_t usemode = from_st.st_mode; mode_t usemode = from_st.st_mode;
if ((info->flags & GIT_CPDIR_SIMPLE_TO_MODE) != 0) if ((info->flags & GIT_CPDIR_SIMPLE_TO_MODE) != 0)
......
...@@ -173,6 +173,7 @@ extern int git_futils_cp( ...@@ -173,6 +173,7 @@ extern int git_futils_cp(
* - GIT_CPDIR_SIMPLE_TO_MODE: default tries to replicate the mode of the * - GIT_CPDIR_SIMPLE_TO_MODE: default tries to replicate the mode of the
* source file to the target; with this flag, always use 0666 (or 0777 if * source file to the target; with this flag, always use 0666 (or 0777 if
* source has exec bits set) for target. * source has exec bits set) for target.
* - GIT_CPDIR_LINK_FILES will try to use hardlinks for the files
*/ */
typedef enum { typedef enum {
GIT_CPDIR_CREATE_EMPTY_DIRS = (1u << 0), GIT_CPDIR_CREATE_EMPTY_DIRS = (1u << 0),
...@@ -181,6 +182,7 @@ typedef enum { ...@@ -181,6 +182,7 @@ typedef enum {
GIT_CPDIR_OVERWRITE = (1u << 3), GIT_CPDIR_OVERWRITE = (1u << 3),
GIT_CPDIR_CHMOD_DIRS = (1u << 4), GIT_CPDIR_CHMOD_DIRS = (1u << 4),
GIT_CPDIR_SIMPLE_TO_MODE = (1u << 5), GIT_CPDIR_SIMPLE_TO_MODE = (1u << 5),
GIT_CPDIR_LINK_FILES = (1u << 6),
} git_futils_cpdir_flags; } git_futils_cpdir_flags;
/** /**
......
...@@ -45,6 +45,16 @@ void test_core_copy__file_in_dir(void) ...@@ -45,6 +45,16 @@ void test_core_copy__file_in_dir(void)
cl_assert(!git_path_isdir("an_dir")); cl_assert(!git_path_isdir("an_dir"));
} }
void assert_hard_link(const char *path)
{
/* we assert this by checking that there's more than one link to the file */
struct stat st;
cl_assert(git_path_isfile(path));
cl_git_pass(p_stat(path, &st));
cl_assert(st.st_nlink > 1);
}
void test_core_copy__tree(void) void test_core_copy__tree(void)
{ {
struct stat st; struct stat st;
...@@ -122,5 +132,21 @@ void test_core_copy__tree(void) ...@@ -122,5 +132,21 @@ void test_core_copy__tree(void)
cl_git_pass(git_futils_rmdir_r("t2", NULL, GIT_RMDIR_REMOVE_FILES)); cl_git_pass(git_futils_rmdir_r("t2", NULL, GIT_RMDIR_REMOVE_FILES));
cl_assert(!git_path_isdir("t2")); cl_assert(!git_path_isdir("t2"));
#ifndef GIT_WIN32
cl_git_pass(git_futils_cp_r("src", "t3", GIT_CPDIR_CREATE_EMPTY_DIRS | GIT_CPDIR_LINK_FILES, 0));
cl_assert(git_path_isdir("t3"));
cl_assert(git_path_isdir("t3"));
cl_assert(git_path_isdir("t3/b"));
cl_assert(git_path_isdir("t3/c"));
cl_assert(git_path_isdir("t3/c/d"));
cl_assert(git_path_isdir("t3/c/e"));
assert_hard_link("t3/f1");
assert_hard_link("t3/b/f2");
assert_hard_link("t3/c/f3");
assert_hard_link("t3/c/d/f4");
#endif
cl_git_pass(git_futils_rmdir_r("src", NULL, GIT_RMDIR_REMOVE_FILES)); cl_git_pass(git_futils_rmdir_r("src", NULL, GIT_RMDIR_REMOVE_FILES));
} }
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