The branch, master has been updated
       via  01742c07 Add --mkpath option. Fixes bugzilla bug 4621.
      from  e00662f2 Add packages to INSTALL.md; put INSTALL.md on ftp site

https://git.samba.org/?p=rsync.git;a=shortlog;h=master


- Log -----------------------------------------------------------------
commit 01742c07e6d7559d69e101a7c2e5380179c08555
Author: Wayne Davison <wa...@opencoder.net>
Date:   Thu Jul 23 20:46:51 2020 -0700

    Add --mkpath option. Fixes bugzilla bug 4621.

-----------------------------------------------------------------------

Summary of changes:
 NEWS.md               |  3 +++
 main.c                | 34 ++++++++++++++++++++++++++--------
 options.c             |  6 ++++++
 rsync.1.md            | 21 +++++++++++++++++++++
 testsuite/mkpath.test | 43 +++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 99 insertions(+), 8 deletions(-)
 create mode 100755 testsuite/mkpath.test


Changeset truncated at 500 lines:

diff --git a/NEWS.md b/NEWS.md
index a0ec31e0..27e8eef8 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -42,6 +42,9 @@
  - Added `--crtimes` (`-N`) option for preserving the file's create time (on
    an OS that supports that, such as macOS).
 
+ - Added `--mkpath` option to tell rsync that it should create a non-existing
+   path component of the destination arg.
+
  - Added the ability to specify "@netgroup" names to the `hosts allow` and
    `hosts deny` daemon parameters.  This is a finalized version of the
    netgroup-auth patch from the patches repo.
diff --git a/main.c b/main.c
index fcc0e65e..1130e24c 100644
--- a/main.c
+++ b/main.c
@@ -57,6 +57,7 @@ extern int copy_unsafe_links;
 extern int keep_dirlinks;
 extern int preserve_hard_links;
 extern int protocol_version;
+extern int mkpath_dest_arg;
 extern int file_total;
 extern int recurse;
 extern int xfer_dirs;
@@ -677,7 +678,7 @@ static pid_t do_cmd(char *cmd, char *machine, char *user, 
char **remote_argv, in
 static char *get_local_name(struct file_list *flist, char *dest_path)
 {
        STRUCT_STAT st;
-       int statret;
+       int statret, trailing_slash;
        char *cp;
 
        if (DEBUG_GTE(RECV, 1)) {
@@ -710,7 +711,26 @@ static char *get_local_name(struct file_list *flist, char 
*dest_path)
        }
 
        /* See what currently exists at the destination. */
-       if ((statret = do_stat(dest_path, &st)) == 0) {
+       statret = do_stat(dest_path, &st);
+       cp = strrchr(dest_path, '/');
+       trailing_slash = cp && !cp[1];
+
+       if (mkpath_dest_arg && statret < 0 && (cp || file_total > 1)) {
+               int ret = make_path(dest_path, file_total > 1 && 
!trailing_slash ? 0 : MKP_DROP_NAME);
+               if (ret < 0)
+                       goto mkdir_error;
+               if (INFO_GTE(NAME, 1)) {
+                       if (file_total == 1 || trailing_slash)
+                               *cp = '\0';
+                       rprintf(FINFO, "created %s %s\n", ret == 1 ? 
"directory" : "path", dest_path);
+                       if (file_total == 1 || trailing_slash)
+                               *cp = '/';
+               }
+               if (file_total > 1 || trailing_slash)
+                       statret = do_stat(dest_path, &st);
+       }
+
+       if (statret == 0) {
                /* If the destination is a dir, enter it and use mode 1. */
                if (S_ISDIR(st.st_mode)) {
                        if (!change_dir(dest_path, CD_NORMAL)) {
@@ -740,15 +760,12 @@ static char *get_local_name(struct file_list *flist, char 
*dest_path)
                exit_cleanup(RERR_FILESELECT);
        }
 
-       cp = strrchr(dest_path, '/');
-
        /* If we need a destination directory because the transfer is not
         * of a single non-directory or the user has requested one via a
         * destination path ending in a slash, create one and use mode 1. */
-       if (file_total > 1 || (cp && !cp[1])) {
-               /* Lop off the final slash (if any). */
-               if (cp && !cp[1])
-                       *cp = '\0';
+       if (file_total > 1 || trailing_slash) {
+               if (trailing_slash)
+                       *cp = '\0'; /* Lop off the final slash (if any). */
 
                if (statret == 0) {
                        rprintf(FERROR, "ERROR: destination path is not a 
directory\n");
@@ -756,6 +773,7 @@ static char *get_local_name(struct file_list *flist, char 
*dest_path)
                }
 
                if (do_mkdir(dest_path, ACCESSPERMS) != 0) {
+                   mkdir_error:
                        rsyserr(FERROR, errno, "mkdir %s failed",
                                full_fname(dest_path));
                        exit_cleanup(RERR_FILEIO);
diff --git a/options.c b/options.c
index 2ef2dbf2..d92a7665 100644
--- a/options.c
+++ b/options.c
@@ -104,6 +104,7 @@ int eol_nulls = 0;
 int protect_args = -1;
 int human_readable = 1;
 int recurse = 0;
+int mkpath_dest_arg = 0;
 int allow_inc_recurse = 1;
 int xfer_dirs = -1;
 int am_daemon = 0;
@@ -1017,6 +1018,8 @@ static struct poptOption long_options[] = {
   {"8-bit-output",    '8', POPT_ARG_VAL,    &allow_8bit_chars, 1, 0, 0 },
   {"no-8-bit-output",  0,  POPT_ARG_VAL,    &allow_8bit_chars, 0, 0, 0 },
   {"no-8",             0,  POPT_ARG_VAL,    &allow_8bit_chars, 0, 0, 0 },
+  {"mkpath",           0,  POPT_ARG_VAL,    &mkpath_dest_arg, 1, 0, 0 },
+  {"no-mkpath",        0,  POPT_ARG_VAL,    &mkpath_dest_arg, 0, 0, 0 },
   {"qsort",            0,  POPT_ARG_NONE,   &use_qsort, 0, 0, 0 },
   {"copy-as",          0,  POPT_ARG_STRING, &copy_as, 0, 0, 0 },
   {"address",          0,  POPT_ARG_STRING, &bind_address, 0, 0, 0 },
@@ -3115,6 +3118,9 @@ void server_options(char **args, int *argc_p)
        if (open_noatime && preserve_atimes <= 1)
                args[ac++] = "--open-noatime";
 
+       if (mkpath_dest_arg && am_sender)
+               args[ac++] = "--mkpath";
+
        if (ac > MAX_SERVER_ARGS) { /* Not possible... */
                rprintf(FERROR, "argc overflow in server_options().\n");
                exit_cleanup(RERR_MALLOC);
diff --git a/rsync.1.md b/rsync.1.md
index 4a2b5aeb..1aa8f5c4 100644
--- a/rsync.1.md
+++ b/rsync.1.md
@@ -351,6 +351,7 @@ detailed description below for a complete description.
 --append                 append data onto shorter files
 --append-verify          --append w/old data in file checksum
 --dirs, -d               transfer directories without recursing
+--mkpath                 create the destination's path component
 --links, -l              copy symlinks as symlinks
 --copy-links, -L         transform symlink into referent file/dir
 --copy-unsafe-links      only "unsafe" symlinks are transformed
@@ -977,6 +978,26 @@ your home directory (remove the '=' for that).
     `--old-d`) that tells rsync to use a hack of `-r --exclude='/*/*'` to get
     an older rsync to list a single directory without recursing.
 
+0.  `--mkpath`
+
+    Create a missing path component of the destination arg.  This allows rsync
+    to create multiple levels of missing destination dirs and to create a path
+    in which to put a single renamed file.  Keep in mind that you'll need to
+    supply a trailing slash if you want the entire destination path to be
+    treated as a directory when copying a single arg (making rsync behave the
+    same way that it would if the path component of the destination had already
+    existed).
+
+    For example, the following creates a copy of file foo as bar in the sub/dir
+    directory, creating dirs "sub" and "sub/dir" if either do not yet exist:
+
+    >     rsync -ai --mkpath foo sub/dir/bar
+
+    If you instead ran the following, it would have created file foo in the
+    sub/dir/bar directory:
+
+    >     rsync -ai --mkpath foo sub/dir/bar/
+
 0.  `--links`, `-l`
 
     When symlinks are encountered, recreate the symlink on the destination.
diff --git a/testsuite/mkpath.test b/testsuite/mkpath.test
new file mode 100755
index 00000000..6efb2105
--- /dev/null
+++ b/testsuite/mkpath.test
@@ -0,0 +1,43 @@
+#!/bin/sh
+
+. "$suitedir/rsync.fns"
+
+makepath "$fromdir"
+makepath "$todir"
+
+cp_p "$srcdir/rsync.h" "$fromdir/text"
+cp_p "$srcdir/configure.ac" "$fromdir/extra"
+
+cd "$tmpdir"
+
+deep_dir=to/foo/bar/baz/down/deep
+
+# Check that we can create several levels of dest dir
+$RSYNC -aiv --mkpath from/text $deep_dir/new
+test -f $deep_dir/new || test_fail "'new' file not found in $deep_dir dir"
+rm -rf to/foo
+
+$RSYNC -aiv --mkpath from/text $deep_dir/
+test -f $deep_dir/text || test_fail "'text' file not found in $deep_dir dir"
+rm $deep_dir/text
+
+# Make sure we can handle an existing path
+mkdir $deep_dir/new
+$RSYNC -aiv --mkpath from/text $deep_dir/new
+test -f $deep_dir/new/text || test_fail "'text' file not found in 
$deep_dir/new dir"
+rm -rf to/foo
+
+# Try the tests again with multiple source args
+$RSYNC -aiv --mkpath from/ $deep_dir
+test -f $deep_dir/extra || test_fail "'extra' file not found in $deep_dir dir"
+rm -rf to/foo
+
+$RSYNC -aiv --mkpath from/ $deep_dir/
+test -f $deep_dir/text || test_fail "'text' file not found in $deep_dir dir"
+
+# Make sure that we can handle no path
+$RSYNC -aiv --mkpath from/text to_text
+test -f to_text || test_fail "'to_text' file not found in current dir"
+
+# The script would have aborted on error, so getting here means we've won.
+exit 0


-- 
The rsync repository.

_______________________________________________
rsync-cvs mailing list
rsync-cvs@lists.samba.org
https://lists.samba.org/mailman/listinfo/rsync-cvs

Reply via email to