-- Matt McCutchen <hashproduct@gmail.com>
-based-on: 25082d1ef6712a15c52a1dacb36b7f0642c23ac8
+based-on: 2ac35b45071c7bfd8be6be41bfd45326f1f57bce
diff --git a/options.c b/options.c
--- a/options.c
+++ b/options.c
-@@ -33,6 +33,7 @@ extern filter_rule_list filter_list;
- extern filter_rule_list daemon_filter_list;
+@@ -35,6 +35,7 @@ extern filter_rule_list daemon_filter_list;
+ #define NOT_SPECIFIED (-42)
int make_backups = 0;
+int make_source_backups = 0;
/**
* If 1, send the whole file as literal data rather than trying to
-@@ -713,6 +714,7 @@ void usage(enum logcode F)
+@@ -730,6 +731,7 @@ void usage(enum logcode F)
rprintf(F," --existing skip creating new files on receiver\n");
rprintf(F," --ignore-existing skip updating files that already exist on receiver\n");
rprintf(F," --remove-source-files sender removes synchronized files (non-dirs)\n");
rprintf(F," --del an alias for --delete-during\n");
rprintf(F," --delete delete extraneous files from destination dirs\n");
rprintf(F," --delete-before receiver deletes before transfer, not during\n");
-@@ -967,6 +969,7 @@ static struct poptOption long_options[] = {
+@@ -994,6 +996,7 @@ static struct poptOption long_options[] = {
{"bwlimit", 0, POPT_ARG_STRING, &bwlimit_arg, OPT_BWLIMIT, 0, 0 },
{"no-bwlimit", 0, POPT_ARG_VAL, &bwlimit, 0, 0, 0 },
{"backup", 'b', POPT_ARG_VAL, &make_backups, 1, 0, 0 },
{"no-backup", 0, POPT_ARG_VAL, &make_backups, 0, 0, 0 },
{"backup-dir", 0, POPT_ARG_STRING, &backup_dir, 0, 0, 0 },
{"suffix", 0, POPT_ARG_STRING, &backup_suffix, 0, 0, 0 },
-@@ -2536,6 +2539,8 @@ void server_options(char **args, int *argc_p)
+@@ -2641,6 +2644,8 @@ void server_options(char **args, int *argc_p)
goto oom;
args[ac++] = arg;
}
diff --git a/rsync.yo b/rsync.yo
--- a/rsync.yo
+++ b/rsync.yo
-@@ -368,6 +368,7 @@ to the detailed description below for a complete description. verb(
+@@ -387,6 +387,7 @@ to the detailed description below for a complete description. verb(
--existing skip creating new files on receiver
--ignore-existing skip updating files that exist on receiver
--remove-source-files sender removes synchronized files (non-dir)
--del an alias for --delete-during
--delete delete extraneous files from dest dirs
--delete-before receiver deletes before xfer, not during
-@@ -1228,6 +1229,14 @@ dit(bf(--remove-source-files)) This tells rsync to remove from the sending
- side the files (meaning non-directories) that are a part of the transfer
- and have been successfully duplicated on the receiving side.
+@@ -1337,6 +1338,14 @@ bf(--exclude='*.new') for the rsync transfer).
+ Starting with 3.1.0, rsync will skip the sender-side removal (and output an
+ error) if the file's size or modify time has not stayed unchanged.
+dit(bf(--source-backup)) Makes the sender back up the source files it removes
+due to bf(--remove-source-files). This option is independent of
diff --git a/sender.c b/sender.c
--- a/sender.c
+++ b/sender.c
-@@ -39,6 +39,7 @@ extern int protocol_version;
+@@ -40,6 +40,7 @@ extern int protocol_version;
extern int remove_source_files;
extern int updating_basis_file;
extern int make_backups;
extern int inplace;
extern int batch_fd;
extern int write_batch;
-@@ -125,6 +126,7 @@ void successful_send(int ndx)
- char fname[MAXPATHLEN];
+@@ -128,6 +129,7 @@ void successful_send(int ndx)
struct file_struct *file;
struct file_list *flist;
+ STRUCT_STAT st;
+ int result;
if (!remove_source_files)
return;
-@@ -135,7 +137,11 @@ void successful_send(int ndx)
+@@ -152,7 +154,11 @@ void successful_send(int ndx)
return;
- f_name(file, fname);
+ }
-- if (do_unlink(fname) == 0) {
+- if (do_unlink(fname) < 0) {
+ if (make_source_backups)
+ result = !make_backup(fname, True);
+ else
+ result = do_unlink(fname);
-+ if (result == 0) {
- if (INFO_GTE(REMOVE, 1))
- rprintf(FINFO, "sender removed %s\n", fname);
- } else
++ if (result < 0) {
+ failed_op = "remove";
+ failed:
+ if (errno == ENOENT)