This adds the option --copy-dest, which works just like --link-dest except that identical files are copied into the destination instead of hard-linked. --- orig/generator.c 2005-03-09 23:46:28 +++ generator.c 2005-03-11 11:24:17 @@ -66,6 +66,7 @@ extern int always_checksum; extern char *partial_dir; extern char *basis_dir[]; extern int compare_dest; +extern int copy_dest; extern int link_dest; extern int whole_file; extern int local_server; @@ -873,6 +874,8 @@ static void recv_generator(char *fname, continue; best_match = i; match_level = 2; + if (copy_dest) + break; /* FALL THROUGH */ case 2: if (!unchanged_attrs(file, &st)) @@ -910,7 +913,20 @@ static void recv_generator(char *fname, match_level = 2; } #endif - if (compare_dest || (match_level && match_level < 3)) { + if (match_level == 2) { + /* Copy the file locally. */ + if (copy_file(fnamecmpbuf, fname, file->mode) < 0) { + if (verbose) { + rsyserr(FINFO, errno, + "copy_file %s => %s", + full_fname(fnamecmpbuf), + safe_fname(fname)); + } + match_level = 0; + statret = -1; + } else + set_perms(fname, file, NULL, 0); + } else if (compare_dest || match_level == 1) { fnamecmp = fnamecmpbuf; fnamecmp_type = i; } @@ -973,11 +989,9 @@ static void recv_generator(char *fname, return; } /* Only --compare-dest gets here. */ - if (unchanged_attrs(file, &st)) { - itemize(file, ndx, real_ret, &real_st, - ITEM_NO_DEST_AND_NO_UPDATE, 0, NULL); - return; - } + itemize(file, ndx, real_ret, &real_st, + ITEM_NO_DEST_AND_NO_UPDATE, 0, NULL); + return; } prepare_to_open: --- orig/options.c 2005-03-09 18:53:53 +++ options.c 2005-03-02 10:05:21 @@ -143,6 +143,7 @@ char *backup_dir = NULL; char backup_dir_buf[MAXPATHLEN]; int rsync_port = 0; int compare_dest = 0; +int copy_dest = 0; int link_dest = 0; int basis_dir_cnt = 0; char *dest_option = NULL; @@ -317,6 +318,7 @@ void usage(enum logcode F) rprintf(F," -T, --temp-dir=DIR create temporary files in directory DIR\n"); rprintf(F," -y, --fuzzy find similar file for basis if no dest file\n"); rprintf(F," --compare-dest=DIR also compare destination files relative to DIR\n"); + rprintf(F," --copy-dest=DIR ... and include copies of unchanged files\n"); rprintf(F," --link-dest=DIR hardlink to files in DIR when unchanged\n"); rprintf(F," -z, --compress compress file data during the transfer\n"); rprintf(F," -C, --cvs-exclude auto-ignore files the same way CVS does\n"); @@ -355,7 +357,7 @@ void usage(enum logcode F) } enum {OPT_VERSION = 1000, OPT_DAEMON, OPT_SENDER, OPT_EXCLUDE, OPT_EXCLUDE_FROM, - OPT_FILTER, OPT_COMPARE_DEST, OPT_LINK_DEST, + OPT_FILTER, OPT_COMPARE_DEST, OPT_COPY_DEST, OPT_LINK_DEST, OPT_INCLUDE, OPT_INCLUDE_FROM, OPT_MODIFY_WINDOW, OPT_READ_BATCH, OPT_WRITE_BATCH, OPT_TIMEOUT, OPT_MAX_SIZE, OPT_REFUSED_BASE = 9000}; @@ -424,6 +426,7 @@ static struct poptOption long_options[] {"timeout", 0, POPT_ARG_INT, &io_timeout, OPT_TIMEOUT, 0, 0 }, {"temp-dir", 'T', POPT_ARG_STRING, &tmpdir, 0, 0, 0 }, {"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, &fuzzy_basis, 0, 0, 0 }, /* TODO: Should this take an optional int giving the compression level? */ @@ -838,6 +841,11 @@ int parse_arguments(int *argc, const cha return 0; #endif + case OPT_COPY_DEST: + copy_dest = 1; + dest_option = "--copy-dest"; + goto set_dest_dir; + case OPT_COMPARE_DEST: compare_dest = 1; dest_option = "--compare-dest"; @@ -928,9 +936,9 @@ int parse_arguments(int *argc, const cha return 0; } - if (compare_dest + link_dest > 1) { + if (compare_dest + copy_dest + link_dest > 1) { snprintf(err_buf, sizeof err_buf, - "You may not mix --compare-dest and --link-dest.\n"); + "You may not mix --compare-dest, --copy-dest, and --link-dest.\n"); return 0; } --- orig/rsync.yo 2005-03-05 18:58:26 +++ rsync.yo 2005-03-03 02:19:19 @@ -353,6 +353,7 @@ to the detailed description below for a -T, --temp-dir=DIR create temporary files in directory DIR -y, --fuzzy find similar file for basis if no dest file --compare-dest=DIR also compare received files relative to DIR + --copy-dest=DIR ... and include copies of unchanged files --link-dest=DIR hardlink to files in DIR when unchanged -z, --compress compress file data during the transfer -C, --cvs-exclude auto-ignore files in the same way CVS does @@ -954,13 +955,30 @@ have changed from an earlier backup. 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 for an exact match. +If a match is found that differs only in attributes, a local copy is made +and the attributes updated. If a match is not found, a basis file from one of the em(DIR)s will be selected to try to speed up the transfer. If em(DIR) is a relative path, it is relative to the destination directory. -See also bf(--link-dest). +See also bf(--copy-dest) and bf(--link-dest). -dit(bf(--link-dest=DIR)) This option behaves like bf(--compare-dest), but +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. This is +useful for doing transfers to a new destination while leaving existing +files intact, and then doing a flash-cutover when all files have been +successfully transferred. + +Multiple bf(--copy-dest) directories may be provided, which will cause +rsync to search the list in the order specified for an unchanged file. +If a match is not found, a basis file from one of the em(DIR)s will be +selected to try to speed up the transfer. + +If em(DIR) is a relative path, it is relative to the destination directory. +See also bf(--compare-dest) and bf(--link-dest). + +dit(bf(--link-dest=DIR)) This option behaves like bf(--copy-dest), but unchanged files are hard linked from em(DIR) to the destination directory. The files must be identical in all preserved attributes (e.g. permissions, possibly ownership) in order for the files to be linked together. @@ -971,11 +989,13 @@ quote(tt( rsync -av --link-dest=$PWD/pr Beginning in version 2.6.4, multiple bf(--link-dest) directories may be provided, which will cause rsync to search the list in the order specified for an exact match. +If a match is found that differs only in attributes, a local copy is made +and the attributes updated. If a match is not found, a basis file from one of the em(DIR)s will be selected to try to speed up the transfer. If em(DIR) is a relative path, it is relative to the destination directory. -See also bf(--compare-dest). +See also bf(--compare-dest) and bf(--copy-dest). Note that rsync versions prior to 2.6.1 had a bug that could prevent bf(--link-dest) from working properly for a non-root user when bf(-o) was specified --- orig/testsuite/compare-dest.test 2005-02-26 19:49:59 +++ testsuite/compare-dest.test 2005-03-01 15:57:27 @@ -31,9 +31,9 @@ $RSYNC -av --exclude=/text --exclude=etc checkit "$RSYNC -avv --no-whole-file \ --compare-dest=\"$alt1dir\" --compare-dest=\"$alt2dir\" \ \"$fromdir/\" \"$todir/\"" "$chkdir" "$todir" -#checkit "$RSYNC -avv --no-whole-file \ -# --copy-dest=\"$alt1dir\" --copy-dest=\"$alt2dir\" \ -# \"$fromdir/\" \"$todir/\"" "$fromdir" "$todir" +checkit "$RSYNC -avv --no-whole-file \ + --copy-dest=\"$alt1dir\" --copy-dest=\"$alt2dir\" \ + \"$fromdir/\" \"$todir/\"" "$fromdir" "$todir" # The script would have aborted on error, so getting here means we've won. exit 0