/* This is only called for regular files. We return -2 if we've finished
* handling the file, -1 if no dest-linking occurred, or a non-negative
- * value if we found an alternate basis file. */
+ * value if we found an alternate basis file. If we're called with the
+ * find_exact_for_existing flag, the destination file already exists, so
+ * we only try to find an exact alt-dest match. In this case, the returns
+ * can be -2 & -1 (both as above) as well as -3, which means that we
+ * removed the dest file but failed to create a hard link for it. */
static int try_dests_reg(struct file_struct *file, char *fname, int ndx,
- char *cmpbuf, stat_x *sxp, int itemizing,
- enum logcode code)
+ char *cmpbuf, stat_x *sxp, int find_exact_for_existing,
+ int itemizing, enum logcode code)
{
int best_match = -1;
int match_level = 0;
}
if (match_level == 3 && !copy_dest) {
+ if (find_exact_for_existing) {
+ if (do_unlink(fname) < 0 && errno != ENOENT)
+ return -1;
+ }
#ifdef SUPPORT_HARD_LINKS
if (link_dest) {
- if (!hard_link_one(file, fname, cmpbuf, 1))
+ if (!hard_link_one(file, fname, cmpbuf, 1)) {
+ if (find_exact_for_existing)
+ return -3;
goto try_a_copy;
+ }
if (preserve_hard_links && F_IS_HLINKED(file))
finish_hard_link(file, fname, ndx, &sxp->st, itemizing, code, j);
if (!maybe_ATTRS_REPORT && (INFO_GTE(NAME, 2) || stdout_format_has_i > 1)) {
}
} else
#endif
- if (itemizing)
- itemize(cmpbuf, file, ndx, 0, sxp, 0, 0, NULL);
+ {
+ if (itemizing)
+ itemize(cmpbuf, file, ndx, 0, sxp, 0, 0, NULL);
+ }
if (INFO_GTE(NAME, 2) && maybe_ATTRS_REPORT)
rprintf(FCLIENT, "%s is uptodate\n", fname);
return -2;
}
+ if (find_exact_for_existing)
+ return -1;
+
if (match_level >= 2) {
#ifdef SUPPORT_HARD_LINKS
try_a_copy: /* Copy the file locally. */
stat_errno = ENOENT;
}
- if (statret != 0 && basis_dir[0] != NULL) {
+ if (basis_dir[0] != NULL && (statret != 0 || !copy_dest)) {
int j = try_dests_reg(file, fname, ndx, fnamecmpbuf, &sx,
- itemizing, code);
+ statret == 0, itemizing, code);
if (j == -2) {
if (remove_source_files == 1)
goto return_with_success;
fnamecmp = fnamecmpbuf;
fnamecmp_type = j;
statret = 0;
+ } else if (j == -3) {
+ statret = -1;
+ stat_errno = ENOENT;
}
}
sender's file, the file will NOT be transferred to the destination
directory. This is useful for creating a sparse backup of just files that
have changed from an earlier backup.
+This option is typically used to copy into an empty (or newly created)
+directory.
Beginning in version 2.6.4, multiple bf(--compare-dest) directories may be
provided, which will cause rsync to search the list in the order specified
If em(DIR) is a relative path, it is relative to the destination directory.
See also bf(--copy-dest) and bf(--link-dest).
+NOTE: beginning with version 3.1.0, rsync will remove a file from a non-empty
+destination hierarchy if an exact match is found in one of the compare-dest
+hierarchies (making the end result more closely match a fresh copy).
+
dit(bf(--copy-dest=DIR)) This option behaves like bf(--compare-dest), but
rsync will also copy unchanged files found in em(DIR) to the destination
directory using a local copy.
selected to try to speed up the transfer.
This option works best when copying into an empty destination hierarchy, as
-rsync treats existing files as definitive (so it never looks in the link-dest
-dirs when a destination file already exists), and as malleable (so it might
-change the attributes of a destination file, which affects all the hard-linked
-versions).
+existing files may get their attributes tweaked, and that can affect alternate
+destination files via hard-links. Also, itemizing of changes can get a bit
+muddled. Note that prior to version 3.1.0, an alternate-directory exact match
+would never be found (nor linked into the destination) when a destination file
+already exists.
Note that if you combine this option with bf(--ignore-times), rsync will not
link any files together because it only links identical files together as a