./configure (optional if already run)
make
-based-on: fe2ef556d9ef11e5dd549e19a06a7a924f7ddfa1
+based-on: 6c8ca91c731b7bf2b081694bda85b7dadc2b7aff
+diff --git a/Makefile.in b/Makefile.in
+--- a/Makefile.in
++++ b/Makefile.in
+@@ -53,7 +53,7 @@ popt_OBJS=popt/findme.o popt/popt.o popt/poptconfig.o \
+ popt/popthelp.o popt/poptparse.o
+ OBJS=$(OBJS1) $(OBJS2) $(OBJS3) $(DAEMON_OBJ) $(LIBOBJ) @BUILD_ZLIB@ @BUILD_POPT@
+
+-TLS_OBJ = tls.o syscall.o util2.o t_stub.o lib/compat.o lib/snprintf.o lib/permstring.o lib/sysxattrs.o @BUILD_POPT@
++TLS_OBJ = tls.o syscall.o util1.o util2.o t_stub.o lib/compat.o lib/snprintf.o lib/permstring.o lib/sysxattrs.o lib/wildmatch.o @BUILD_POPT@
+
+ # Programs we must have to run the test cases
+ CHECK_PROGS = rsync$(EXEEXT) tls$(EXEEXT) getgroups$(EXEEXT) getfsdev$(EXEEXT) \
+@@ -170,7 +170,7 @@ getgroups$(EXEEXT): getgroups.o
+ getfsdev$(EXEEXT): getfsdev.o
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ getfsdev.o $(LIBS)
+
+-TRIMSLASH_OBJ = trimslash.o syscall.o util2.o t_stub.o lib/compat.o lib/snprintf.o
++TRIMSLASH_OBJ = trimslash.o syscall.o util1.o util2.o t_stub.o lib/compat.o lib/snprintf.o lib/wildmatch.o
+ trimslash$(EXEEXT): $(TRIMSLASH_OBJ)
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(TRIMSLASH_OBJ) $(LIBS)
+
diff --git a/generator.c b/generator.c
--- a/generator.c
+++ b/generator.c
-@@ -892,7 +892,7 @@ static int try_dests_reg(struct file_struct *file, char *fname, int ndx,
+@@ -962,7 +962,7 @@ static int try_dests_reg(struct file_struct *file, char *fname, int ndx,
best_match = j;
match_level = 2;
}
best_match = j;
match_level = 3;
break;
-@@ -932,7 +932,12 @@ static int try_dests_reg(struct file_struct *file, char *fname, int ndx,
- }
- } else
- #endif
-- {
-+ if (alt_dest_type == CLONE_DEST) {
-+ if (do_clone(cmpbuf, fname, file->mode) < 0) {
+@@ -988,9 +988,16 @@ static int try_dests_reg(struct file_struct *file, char *fname, int ndx,
+ goto got_nothing_for_ya;
+ }
+ #ifdef SUPPORT_HARD_LINKS
+- if (alt_dest_type == LINK_DEST) {
+- if (!hard_link_one(file, fname, cmpbuf, 1))
+- goto try_a_copy;
++ if (alt_dest_type == LINK_DEST || alt_dest_type == CLONE_DEST) {
++ if (alt_dest_type == LINK_DEST) {
++ if (!hard_link_one(file, fname, cmpbuf, 1))
++ goto try_a_copy;
++ } else if (do_clone(cmpbuf, fname, file->mode) == 0) {
++ finish_transfer(fname, fname, cmpbuf, NULL, file, 1, 0);
++ } else {
+ rsyserr(FERROR_XFER, errno, "failed to clone %s to %s", cmpbuf, fname);
+ exit_cleanup(RERR_UNSUPPORTED);
+ }
-+ } else {
- if (itemizing)
- itemize(cmpbuf, file, ndx, 0, sxp, 0, 0, NULL);
- }
-@@ -1088,7 +1093,7 @@ static int try_dests_non(struct file_struct *file, char *fname, int ndx,
+ if (atimes_ndx)
+ set_file_attrs(fname, file, sxp, NULL, 0);
+ if (preserve_hard_links && F_IS_HLINKED(file))
+@@ -1104,7 +1111,7 @@ static int try_dests_non(struct file_struct *file, char *fname, int ndx,
if (match_level == 3) {
#ifdef SUPPORT_HARD_LINKS
diff --git a/options.c b/options.c
--- a/options.c
+++ b/options.c
-@@ -783,7 +783,7 @@ enum {OPT_SERVER = 1000, OPT_DAEMON, OPT_SENDER, OPT_EXCLUDE, OPT_EXCLUDE_FROM,
+@@ -582,7 +582,7 @@ enum {OPT_SERVER = 1000, OPT_DAEMON, OPT_SENDER, OPT_EXCLUDE, OPT_EXCLUDE_FROM,
OPT_INCLUDE, OPT_INCLUDE_FROM, OPT_MODIFY_WINDOW, OPT_MIN_SIZE, OPT_CHMOD,
OPT_READ_BATCH, OPT_WRITE_BATCH, OPT_ONLY_WRITE_BATCH, OPT_MAX_SIZE,
- OPT_NO_D, OPT_APPEND, OPT_NO_ICONV, OPT_INFO, OPT_DEBUG,
-- OPT_USERMAP, OPT_GROUPMAP, OPT_CHOWN, OPT_BWLIMIT,
-+ OPT_USERMAP, OPT_GROUPMAP, OPT_CHOWN, OPT_BWLIMIT, OPT_CLONE_DEST,
- OPT_OLD_COMPRESS, OPT_NEW_COMPRESS, OPT_NO_COMPRESS,
+ OPT_NO_D, OPT_APPEND, OPT_NO_ICONV, OPT_INFO, OPT_DEBUG, OPT_BLOCK_SIZE,
+- OPT_USERMAP, OPT_GROUPMAP, OPT_CHOWN, OPT_BWLIMIT, OPT_STDERR,
++ OPT_USERMAP, OPT_GROUPMAP, OPT_CHOWN, OPT_BWLIMIT, OPT_STDERR, OPT_CLONE_DEST,
+ OPT_OLD_COMPRESS, OPT_NEW_COMPRESS, OPT_NO_COMPRESS, OPT_OLD_ARGS,
+ OPT_STOP_AFTER, OPT_STOP_AT,
OPT_REFUSED_BASE = 9000};
-
-@@ -938,6 +938,7 @@ static struct poptOption long_options[] = {
+@@ -743,6 +743,7 @@ static struct poptOption long_options[] = {
{"compare-dest", 0, POPT_ARG_STRING, 0, OPT_COMPARE_DEST, 0, 0 },
{"copy-dest", 0, POPT_ARG_STRING, 0, OPT_COPY_DEST, 0, 0 },
{"link-dest", 0, POPT_ARG_STRING, 0, OPT_LINK_DEST, 0, 0 },
{"fuzzy", 'y', POPT_ARG_NONE, 0, 'y', 0, 0 },
{"no-fuzzy", 0, POPT_ARG_VAL, &fuzzy_basis, 0, 0, 0 },
{"no-y", 0, POPT_ARG_VAL, &fuzzy_basis, 0, 0, 0 },
-@@ -1198,6 +1199,9 @@ static void set_refuse_options(void)
+@@ -1004,6 +1005,9 @@ static void set_refuse_options(void)
#ifndef SUPPORT_HARD_LINKS
parse_one_refuse_match(0, "link-dest", list_end);
#endif
+#ifndef FICLONE
+ parse_one_refuse_match(0, "clone-dest", list_end);
+#endif
- #ifndef ICONV_OPTION
- parse_one_refuse_match(0, "iconv", list_end);
+ #ifndef HAVE_MKTIME
+ parse_one_refuse_match(0, "stop-at", list_end);
#endif
-@@ -1363,6 +1367,8 @@ char *alt_dest_opt(int type)
+@@ -1333,6 +1337,8 @@ char *alt_dest_opt(int type)
return "--copy-dest";
case LINK_DEST:
return "--link-dest";
+ case CLONE_DEST:
+ return "--clone-dest";
default:
- assert(0);
+ NOISY_DEATH("Unknown alt_dest_opt type");
}
-@@ -1720,6 +1726,10 @@ int parse_arguments(int *argc_p, const char ***argv_p)
+@@ -1714,6 +1720,10 @@ int parse_arguments(int *argc_p, const char ***argv_p)
want_dest_type = LINK_DEST;
goto set_dest_dir;
diff --git a/rsync.1.md b/rsync.1.md
--- a/rsync.1.md
+++ b/rsync.1.md
-@@ -422,6 +422,7 @@ detailed description below for a complete description.
+@@ -510,6 +510,7 @@ has its own detailed description later in this manpage.
--compare-dest=DIR also compare destination files relative to DIR
--copy-dest=DIR ... and include copies of unchanged files
--link-dest=DIR hardlink to files in DIR when unchanged
--compress, -z compress file data during the transfer
--compress-choice=STR choose the compression algorithm (aka --zc)
--compress-level=NUM explicitly set compression level (aka --zl)
-@@ -2282,6 +2283,17 @@ your home directory (remove the '=' for that).
- specified (or implied by `-a`). You can work-around this bug by avoiding
- the `-o` option when sending to an old rsync.
+@@ -2720,6 +2721,18 @@ expand it.
+ this bug by avoiding the `-o` option (or using `--no-o`) when sending to an
+ old rsync.
+0. `--clone-dest=DIR`
+
-+ This option behaves like `--link-dest`, but unchanged files are reflinked
-+ from _DIR_ to the destination directory. The files do not need to match
-+ in attributes, as the data is cloned separately from the attributes.
++ This option behaves like [`--link-dest`](#opt), but unchanged files are
++ reflinked from _DIR_ to the destination directory. The files do not need
++ to match in attributes, as the data is cloned separately from the
++ attributes.
+
+ If _DIR_ is a relative path, it is relative to the destination directory.
-+ See also `--compare-dest` and `--copy-dest`.
++ See also [`--compare-dest`](#opt) and [`--copy-dest`](#opt).
+
+ All non-regular files are hard-linked (when possible).
+
diff --git a/rsync.h b/rsync.h
--- a/rsync.h
+++ b/rsync.h
-@@ -164,6 +164,11 @@
+@@ -175,6 +175,11 @@
#define COMPARE_DEST 1
#define COPY_DEST 2
#define LINK_DEST 3
diff --git a/syscall.c b/syscall.c
--- a/syscall.c
+++ b/syscall.c
-@@ -129,6 +129,54 @@ int do_link(const char *old_path, const char *new_path)
+@@ -146,6 +146,54 @@ int do_link(const char *old_path, const char *new_path)
}
#endif
int do_lchown(const char *path, uid_t owner, gid_t group)
{
if (dry_run) return 0;
+diff --git a/t_stub.c b/t_stub.c
+--- a/t_stub.c
++++ b/t_stub.c
+@@ -38,6 +38,7 @@ size_t max_alloc = 0; /* max_alloc is needed when combined with util2.o */
+ char *partial_dir;
+ char *module_dir;
+ filter_rule_list daemon_filter_list;
++short info_levels[COUNT_INFO], debug_levels[COUNT_DEBUG];
+
+ void rprintf(UNUSED(enum logcode code), const char *format, ...)
+ {
+diff --git a/t_unsafe.c b/t_unsafe.c
+--- a/t_unsafe.c
++++ b/t_unsafe.c
+@@ -28,7 +28,6 @@ int am_root = 0;
+ int am_sender = 1;
+ int read_only = 0;
+ int list_only = 0;
+-short info_levels[COUNT_INFO], debug_levels[COUNT_DEBUG];
+
+ int
+ main(int argc, char **argv)