ソースを参照

add a --filter option replacing --changed/--unchanged

the problem here was that we do not just have changed and unchanged items,
but also a lot of items besides regular files which we just back up "as is" without
determining whether they are changed or not. thus, we can't support changed/unchanged
in a way users would expect them to work.

the A/M/U status only applies to the data content of regular files (compared to the index).
for all items, we ALWAYS save the metadata, there is no changed / not changed detection there.

thus, I replaced this with a --filter option where you can just specify which
status chars you want to see listed in the output.

E.g. --filter AM will only show regular files with A(dded) or M(odified) state, but nothing else.
Not giving --filter defaults to showing all items no matter what status they have.

Output is emitted via logger at info level, so it won't show up except if the logger is at that level.
Thomas Waldmann 9 年 前
コミット
21bd01ef16
3 ファイル変更19 行追加22 行削除
  1. 5 12
      borg/archiver.py
  2. 5 5
      borg/testsuite/archiver.py
  3. 9 5
      docs/usage.rst

+ 5 - 12
borg/archiver.py

@@ -74,12 +74,8 @@ class Archiver:
         logger.warning(msg)
         logger.warning(msg)
 
 
     def print_file_status(self, status, path):
     def print_file_status(self, status, path):
-        if status == 'U':
-            if self.unchanged:
-                print("%1s %s" % (status, remove_surrogates(path)), file=sys.stderr)
-        else:
-            if self.changed:
-                print("%1s %s" % (status, remove_surrogates(path)), file=sys.stderr)
+        if self.output_filter is None or status in self.output_filter:
+            logger.info("%1s %s", status, remove_surrogates(path))
 
 
     def do_serve(self, args):
     def do_serve(self, args):
         """Start in server mode. This command is usually not used manually.
         """Start in server mode. This command is usually not used manually.
@@ -128,6 +124,7 @@ class Archiver:
 
 
     def do_create(self, args):
     def do_create(self, args):
         """Create new archive"""
         """Create new archive"""
+        self.output_filter = args.output_filter
         dry_run = args.dry_run
         dry_run = args.dry_run
         t0 = datetime.now()
         t0 = datetime.now()
         if not dry_run:
         if not dry_run:
@@ -806,10 +803,8 @@ class Archiver:
                                help="""toggle progress display while creating the archive, showing Original,
                                help="""toggle progress display while creating the archive, showing Original,
                                Compressed and Deduplicated sizes, followed by the Number of files seen
                                Compressed and Deduplicated sizes, followed by the Number of files seen
                                and the path being processed, default: %(default)s""")
                                and the path being processed, default: %(default)s""")
-        subparser.add_argument('--changed', action='store_true', dest='changed', default=False,
-                               help="""display which files were added to the archive""")
-        subparser.add_argument('--unchanged', action='store_true', dest='unchanged', default=False,
-                               help="""display which files were *not* added to the archive""")
+        subparser.add_argument('--filter', dest='output_filter', metavar='STATUSCHARS',
+                               help='only display items with the given status characters')
         subparser.add_argument('-e', '--exclude', dest='excludes',
         subparser.add_argument('-e', '--exclude', dest='excludes',
                                type=ExcludePattern, action='append',
                                type=ExcludePattern, action='append',
                                metavar="PATTERN", help='exclude paths matching PATTERN')
                                metavar="PATTERN", help='exclude paths matching PATTERN')
@@ -1186,8 +1181,6 @@ class Archiver:
     def run(self, args):
     def run(self, args):
         os.umask(args.umask)  # early, before opening files
         os.umask(args.umask)  # early, before opening files
         self.lock_wait = args.lock_wait
         self.lock_wait = args.lock_wait
-        self.changed = getattr(args, 'changed', False)
-        self.unchanged = getattr(args, 'unchanged', False)
         RemoteRepository.remote_path = args.remote_path
         RemoteRepository.remote_path = args.remote_path
         RemoteRepository.umask = args.umask
         RemoteRepository.umask = args.umask
         setup_logging(level=args.log_level)  # do not use loggers before this!
         setup_logging(level=args.log_level)  # do not use loggers before this!

+ 5 - 5
borg/testsuite/archiver.py

@@ -713,11 +713,11 @@ class ArchiverTestCase(ArchiverTestCaseBase):
         os.utime('input/file1', (now - 5, now - 5)) # 5 seconds ago
         os.utime('input/file1', (now - 5, now - 5)) # 5 seconds ago
         self.create_regular_file('file2', size=1024 * 80)
         self.create_regular_file('file2', size=1024 * 80)
         self.cmd('init', self.repository_location)
         self.cmd('init', self.repository_location)
-        output = self.cmd('create', '--changed', '--unchanged', self.repository_location + '::test', 'input')
+        output = self.cmd('create', '-v', self.repository_location + '::test', 'input')
         self.assert_in("A input/file1", output)
         self.assert_in("A input/file1", output)
         self.assert_in("A input/file2", output)
         self.assert_in("A input/file2", output)
         # should find first file as unmodified
         # should find first file as unmodified
-        output = self.cmd('create', '--changed', '--unchanged', self.repository_location + '::test1', 'input')
+        output = self.cmd('create', '-v', self.repository_location + '::test1', 'input')
         self.assert_in("U input/file1", output)
         self.assert_in("U input/file1", output)
         # this is expected, although surprising, for why, see:
         # this is expected, although surprising, for why, see:
         # http://borgbackup.readthedocs.org/en/latest/faq.html#i-am-seeing-a-added-status-for-a-unchanged-file
         # http://borgbackup.readthedocs.org/en/latest/faq.html#i-am-seeing-a-added-status-for-a-unchanged-file
@@ -736,15 +736,15 @@ class ArchiverTestCase(ArchiverTestCaseBase):
         output = self.cmd('create', self.repository_location + '::test0', 'input')
         output = self.cmd('create', self.repository_location + '::test0', 'input')
         self.assert_not_in('file1', output)
         self.assert_not_in('file1', output)
         # should list the file as unchanged
         # should list the file as unchanged
-        output = self.cmd('create', '--unchanged', self.repository_location + '::test1', 'input')
+        output = self.cmd('create', '-v', '--filter=U', self.repository_location + '::test1', 'input')
         self.assert_in('file1', output)
         self.assert_in('file1', output)
         # should *not* list the file as changed
         # should *not* list the file as changed
-        output = self.cmd('create', '--changed', self.repository_location + '::test2', 'input')
+        output = self.cmd('create', '-v', '--filter=AM', self.repository_location + '::test2', 'input')
         self.assert_not_in('file1', output)
         self.assert_not_in('file1', output)
         # change the file
         # change the file
         self.create_regular_file('file1', size=1024 * 100)
         self.create_regular_file('file1', size=1024 * 100)
         # should list the file as changed
         # should list the file as changed
-        output = self.cmd('create', '--changed', self.repository_location + '::test3', 'input')
+        output = self.cmd('create', '-v', '--filter=AM', self.repository_location + '::test3', 'input')
         self.assert_in('file1', output)
         self.assert_in('file1', output)
 
 
     def test_cmdline_compatibility(self):
     def test_cmdline_compatibility(self):

+ 9 - 5
docs/usage.rst

@@ -429,10 +429,14 @@ Here are misc. notes about topics that are maybe not covered in enough detail in
 Item flags
 Item flags
 ~~~~~~~~~~
 ~~~~~~~~~~
 
 
-`borg create --changed` outputs a verbose list of all files, directories and other
-file system items it considered, with the exception of unchanged files
-(for this, also add `--unchanged`). For each item, it prefixes a single-letter
-flag that indicates type and/or status of the item.
+`borg create -v` outputs a verbose list of all files, directories and other
+file system items it considered (no matter whether they had content changes
+or not). For each item, it prefixes a single-letter flag that indicates type
+and/or status of the item.
+
+If you are interested only in a subset of that output, you can give e.g.
+`--filter=AME` and it will only show regular files with A, M or E status (see
+below).
 
 
 A uppercase character represents the status of a regular file relative to the
 A uppercase character represents the status of a regular file relative to the
 "files" cache (not relative to the repo - this is an issue if the files cache
 "files" cache (not relative to the repo - this is an issue if the files cache
@@ -441,7 +445,7 @@ chunks are stored. For 'U' all data chunks refer to already existing chunks.
 
 
 - 'A' = regular file, added (see also :ref:`a_status_oddity` in the FAQ)
 - 'A' = regular file, added (see also :ref:`a_status_oddity` in the FAQ)
 - 'M' = regular file, modified
 - 'M' = regular file, modified
-- 'U' = regular file, unchanged (only if `--unchanged` is specified)
+- 'U' = regular file, unchanged
 - 'E' = regular file, an error happened while accessing/reading *this* file
 - 'E' = regular file, an error happened while accessing/reading *this* file
 
 
 A lowercase character means a file type other than a regular file,
 A lowercase character means a file type other than a regular file,