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)
return error;
/* 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);
else {
} else {
mode_t usemode = from_st.st_mode;
if ((info->flags & GIT_CPDIR_SIMPLE_TO_MODE) != 0)
......
......@@ -173,6 +173,7 @@ extern int git_futils_cp(
* - 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 has exec bits set) for target.
* - GIT_CPDIR_LINK_FILES will try to use hardlinks for the files
*/
typedef enum {
GIT_CPDIR_CREATE_EMPTY_DIRS = (1u << 0),
......@@ -181,6 +182,7 @@ typedef enum {
GIT_CPDIR_OVERWRITE = (1u << 3),
GIT_CPDIR_CHMOD_DIRS = (1u << 4),
GIT_CPDIR_SIMPLE_TO_MODE = (1u << 5),
GIT_CPDIR_LINK_FILES = (1u << 6),
} git_futils_cpdir_flags;
/**
......
......@@ -45,6 +45,16 @@ void test_core_copy__file_in_dir(void)
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)
{
struct stat st;
......@@ -122,5 +132,21 @@ void test_core_copy__tree(void)
cl_git_pass(git_futils_rmdir_r("t2", NULL, GIT_RMDIR_REMOVE_FILES));
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));
}
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