瀏覽代碼

Implement --keep-tag-files to preserve directory roots/tag-files

We also add --keep-tag-files to keep in the archive the root directory and the
tag/exclusion file in the archive.

This is taken from a attic PR (and adapted for borg):
    commit f61e22cacc90e76e6c8f4b23677eee62c09e97ac
    Author: Yuri D'Elia <yuri.delia@eurac.edu>
    Date:   Mon Dec 15 12:27:43 2014 +0100
Thomas Waldmann 9 年之前
父節點
當前提交
7d178e09b0
共有 3 個文件被更改,包括 34 次插入9 次删除
  1. 14 5
      borg/archiver.py
  2. 6 4
      borg/helpers.py
  3. 14 0
      borg/testsuite/archiver.py

+ 14 - 5
borg/archiver.py

@@ -167,7 +167,8 @@ class Archiver:
             else:
                 restrict_dev = None
             self._process(archive, cache, args.excludes, args.exclude_caches, args.exclude_if_present,
-                          skip_inodes, path, restrict_dev, read_special=args.read_special, dry_run=dry_run)
+                          args.keep_tag_files, skip_inodes, path, restrict_dev,
+                          read_special=args.read_special, dry_run=dry_run)
         if not dry_run:
             archive.save(timestamp=args.timestamp)
             if args.progress:
@@ -183,7 +184,8 @@ class Archiver:
         return self.exit_code
 
     def _process(self, archive, cache, excludes, exclude_caches, exclude_if_present,
-                 skip_inodes, path, restrict_dev, read_special=False, dry_run=False):
+                 keep_tag_files, skip_inodes, path, restrict_dev,
+                 read_special=False, dry_run=False):
         if exclude_path(path, excludes):
             return
         try:
@@ -209,7 +211,11 @@ class Archiver:
                     status = 'E'
                     self.print_warning('%s: %s', path, e)
         elif stat.S_ISDIR(st.st_mode):
-            if dir_is_tagged(path, exclude_caches, exclude_if_present):
+            tag_path = dir_is_tagged(path, exclude_caches, exclude_if_present)
+            if tag_path:
+                if keep_tag_files:
+                    archive.process_dir(path, st)
+                    archive.process_file(tag_path, st, cache)
                 return
             if not dry_run:
                 status = archive.process_dir(path, st)
@@ -222,8 +228,8 @@ class Archiver:
                 for filename in sorted(entries):
                     entry_path = os.path.normpath(os.path.join(path, filename))
                     self._process(archive, cache, excludes, exclude_caches, exclude_if_present,
-                                  skip_inodes, entry_path, restrict_dev, read_special=read_special,
-                                  dry_run=dry_run)
+                                  keep_tag_files, skip_inodes, entry_path, restrict_dev,
+                                  read_special=read_special, dry_run=dry_run)
         elif stat.S_ISLNK(st.st_mode):
             if not dry_run:
                 status = archive.process_symlink(path, st)
@@ -788,6 +794,9 @@ class Archiver:
         subparser.add_argument('--exclude-if-present', dest='exclude_if_present',
                                metavar='FILENAME', action='append', type=str,
                                help='exclude directories that contain the specified file')
+        subparser.add_argument('--keep-tag-files', dest='keep_tag_files',
+                               action='store_true', default=False,
+                               help='keep tag files of excluded caches/directories')
         subparser.add_argument('-c', '--checkpoint-interval', dest='checkpoint_interval',
                                type=int, default=300, metavar='SECONDS',
                                help='write checkpoint every SECONDS seconds (Default: 300)')

+ 6 - 4
borg/helpers.py

@@ -450,16 +450,18 @@ def dir_is_cachedir(path):
 
 def dir_is_tagged(path, exclude_caches, exclude_if_present):
     """Determines whether the specified path is excluded by being a cache
-    directory or containing the user-specified tag file.
+    directory or containing the user-specified tag file. Returns the
+    path of the tag file (either CACHEDIR.TAG or the matching
+    user-specified file)
     """
     if exclude_caches and dir_is_cachedir(path):
-        return True
+        return os.path.join(path, 'CACHEDIR.TAG')
     if exclude_if_present is not None:
         for tag in exclude_if_present:
             tag_path = os.path.join(path, tag)
             if os.path.isfile(tag_path):
-                return True
-    return False
+                return tag_path
+    return None
 
 
 def format_time(t):

+ 14 - 0
borg/testsuite/archiver.py

@@ -510,6 +510,20 @@ class ArchiverTestCase(ArchiverTestCaseBase):
             self.cmd('extract', self.repository_location + '::test')
         self.assert_equal(sorted(os.listdir('output/input')), ['file1', 'tagged3'])
 
+    def test_exclude_keep_tagged(self):
+        self.cmd('init', self.repository_location)
+        self.create_regular_file('file1', size=1024 * 80)
+        self.create_regular_file('tagged1/.NOBACKUP')
+        self.create_regular_file('tagged1/file2', size=1024 * 80)
+        self.create_regular_file('tagged2/CACHEDIR.TAG', contents = b'Signature: 8a477f597d28d172789f06886806bc55 extra stuff')
+        self.create_regular_file('tagged2/file3', size=1024 * 80)
+        self.cmd('create', '--exclude-if-present', '.NOBACKUP', '--exclude-caches', '--keep-tag-files', self.repository_location + '::test', 'input')
+        with changedir('output'):
+            self.cmd('extract', self.repository_location + '::test')
+        self.assert_equal(sorted(os.listdir('output/input')), ['file1', 'tagged1', 'tagged2'])
+        self.assert_equal(sorted(os.listdir('output/input/tagged1')), ['.NOBACKUP'])
+        self.assert_equal(sorted(os.listdir('output/input/tagged2')), ['CACHEDIR.TAG'])
+
     def test_path_normalization(self):
         self.cmd('init', self.repository_location)
         self.create_regular_file('dir1/dir2/file', size=1024 * 80)