2
0
Эх сурвалжийг харах

better attic create -v output

Added a indicator character to the left for (A)dded, (M)odified, (U)nchanged status
of regular files. Lowercase indicators are for special files.

You may or may not want to use grep to filter out U and d.
Thomas Waldmann 10 жил өмнө
parent
commit
9841af5542

+ 15 - 1
attic/archive.py

@@ -374,14 +374,22 @@ class Archive:
         item = {b'path': make_path_safe(path), b'rdev': st.st_rdev}
         item = {b'path': make_path_safe(path), b'rdev': st.st_rdev}
         item.update(self.stat_attrs(st, path))
         item.update(self.stat_attrs(st, path))
         self.add_item(item)
         self.add_item(item)
+        if stat.S_ISCHR(st.st_mode):
+            status = 'c'  # char device
+        elif stat.S_ISBLK(st.st_mode):
+            status = 'b'  # block device
+        return status
 
 
     def process_symlink(self, path, st):
     def process_symlink(self, path, st):
         source = os.readlink(path)
         source = os.readlink(path)
         item = {b'path': make_path_safe(path), b'source': source}
         item = {b'path': make_path_safe(path), b'source': source}
         item.update(self.stat_attrs(st, path))
         item.update(self.stat_attrs(st, path))
         self.add_item(item)
         self.add_item(item)
+        status = 's'  # symlink
+        return status
 
 
     def process_file(self, path, st, cache):
     def process_file(self, path, st, cache):
+        status = None
         safe_path = make_path_safe(path)
         safe_path = make_path_safe(path)
         # Is it a hard link?
         # Is it a hard link?
         if st.st_nlink > 1:
         if st.st_nlink > 1:
@@ -390,7 +398,8 @@ class Archive:
                 item = self.stat_attrs(st, path)
                 item = self.stat_attrs(st, path)
                 item.update({b'path': safe_path, b'source': source})
                 item.update({b'path': safe_path, b'source': source})
                 self.add_item(item)
                 self.add_item(item)
-                return
+                status = 'h'  # regular file, hardlink (to already seen inodes)
+                return status
             else:
             else:
                 self.hard_links[st.st_ino, st.st_dev] = safe_path
                 self.hard_links[st.st_ino, st.st_dev] = safe_path
         path_hash = self.key.id_hash(os.path.join(self.cwd, path).encode('utf-8', 'surrogateescape'))
         path_hash = self.key.id_hash(os.path.join(self.cwd, path).encode('utf-8', 'surrogateescape'))
@@ -403,6 +412,9 @@ class Archive:
                     break
                     break
             else:
             else:
                 chunks = [cache.chunk_incref(id_, self.stats) for id_ in ids]
                 chunks = [cache.chunk_incref(id_, self.stats) for id_ in ids]
+                status = 'U'  # regular file, unchanged
+        else:
+            status = 'A'  # regular file, added
         # Only chunkify the file if needed
         # Only chunkify the file if needed
         if chunks is None:
         if chunks is None:
             with Archive._open_rb(path, st) as fd:
             with Archive._open_rb(path, st) as fd:
@@ -410,10 +422,12 @@ class Archive:
                 for chunk in self.chunker.chunkify(fd):
                 for chunk in self.chunker.chunkify(fd):
                     chunks.append(cache.add_chunk(self.key.id_hash(chunk), chunk, self.stats))
                     chunks.append(cache.add_chunk(self.key.id_hash(chunk), chunk, self.stats))
             cache.memorize_file(path_hash, st, [c[0] for c in chunks])
             cache.memorize_file(path_hash, st, [c[0] for c in chunks])
+            status = status or 'M'  # regular file, modified (if not 'A' already)
         item = {b'path': safe_path, b'chunks': chunks}
         item = {b'path': safe_path, b'chunks': chunks}
         item.update(self.stat_attrs(st, path))
         item.update(self.stat_attrs(st, path))
         self.stats.nfiles += 1
         self.stats.nfiles += 1
         self.add_item(item)
         self.add_item(item)
+        return status
 
 
     @staticmethod
     @staticmethod
     def list_archives(repository, key, manifest, cache=None):
     def list_archives(repository, key, manifest, cache=None):

+ 20 - 4
attic/archiver.py

@@ -157,16 +157,17 @@ Type "Yes I am sure" if you understand this and want to continue.\n""")
         # Ignore unix sockets
         # Ignore unix sockets
         if stat.S_ISSOCK(st.st_mode):
         if stat.S_ISSOCK(st.st_mode):
             return
             return
-        self.print_verbose(remove_surrogates(path))
+        status = None
         if stat.S_ISREG(st.st_mode):
         if stat.S_ISREG(st.st_mode):
             try:
             try:
-                archive.process_file(path, st, cache)
+                status = archive.process_file(path, st, cache)
             except IOError as e:
             except IOError as e:
                 self.print_error('%s: %s', path, e)
                 self.print_error('%s: %s', path, e)
         elif stat.S_ISDIR(st.st_mode):
         elif stat.S_ISDIR(st.st_mode):
             if exclude_caches and is_cachedir(path):
             if exclude_caches and is_cachedir(path):
                 return
                 return
             archive.process_item(path, st)
             archive.process_item(path, st)
+            status = 'd'  # directory
             try:
             try:
                 entries = os.listdir(path)
                 entries = os.listdir(path)
             except OSError as e:
             except OSError as e:
@@ -176,13 +177,28 @@ Type "Yes I am sure" if you understand this and want to continue.\n""")
                     self._process(archive, cache, excludes, exclude_caches, skip_inodes,
                     self._process(archive, cache, excludes, exclude_caches, skip_inodes,
                                   os.path.join(path, filename), restrict_dev)
                                   os.path.join(path, filename), restrict_dev)
         elif stat.S_ISLNK(st.st_mode):
         elif stat.S_ISLNK(st.st_mode):
-            archive.process_symlink(path, st)
+            status = archive.process_symlink(path, st)
         elif stat.S_ISFIFO(st.st_mode):
         elif stat.S_ISFIFO(st.st_mode):
             archive.process_item(path, st)
             archive.process_item(path, st)
+            status = 'f'  # fifo
         elif stat.S_ISCHR(st.st_mode) or stat.S_ISBLK(st.st_mode):
         elif stat.S_ISCHR(st.st_mode) or stat.S_ISBLK(st.st_mode):
-            archive.process_dev(path, st)
+            status = archive.process_dev(path, st)
         else:
         else:
             self.print_error('Unknown file type: %s', path)
             self.print_error('Unknown file type: %s', path)
+            return
+        # Status output
+        # A lowercase character means a file type other than a regular file,
+        # attic usually just stores them. E.g. (d)irectory.
+        # Hardlinks to already seen content are indicated by (h).
+        # A uppercase character means a regular file that was (A)dded,
+        # (M)odified or was (U)nchanged.
+        # Note: A/M/U is relative to the "files" cache, not to the repo.
+        # This would be an issue if the files cache is not used.
+        if status is None:
+            status = '?'  # need to add a status code somewhere
+        # output ALL the stuff - it can be easily filtered using grep.
+        # even stuff considered unchanged might be interesting.
+        self.print_verbose("%1s %s", status, remove_surrogates(path))
 
 
     def do_extract(self, args):
     def do_extract(self, args):
         """Extract archive contents"""
         """Extract archive contents"""