From 9841af55425893dbb6aa7e02da6609589dd04972 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Sun, 8 Mar 2015 19:18:21 +0100 Subject: [PATCH 1/2] 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. --- attic/archive.py | 16 +++++++++++++++- attic/archiver.py | 24 ++++++++++++++++++++---- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/attic/archive.py b/attic/archive.py index d78ce4b85..ed9561a4b 100644 --- a/attic/archive.py +++ b/attic/archive.py @@ -374,14 +374,22 @@ def process_dev(self, path, st): item = {b'path': make_path_safe(path), b'rdev': st.st_rdev} item.update(self.stat_attrs(st, path)) 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): source = os.readlink(path) item = {b'path': make_path_safe(path), b'source': source} item.update(self.stat_attrs(st, path)) self.add_item(item) + status = 's' # symlink + return status def process_file(self, path, st, cache): + status = None safe_path = make_path_safe(path) # Is it a hard link? if st.st_nlink > 1: @@ -390,7 +398,8 @@ def process_file(self, path, st, cache): item = self.stat_attrs(st, path) item.update({b'path': safe_path, b'source': source}) self.add_item(item) - return + status = 'h' # regular file, hardlink (to already seen inodes) + return status else: 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')) @@ -403,6 +412,9 @@ def process_file(self, path, st, cache): break else: 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 if chunks is None: with Archive._open_rb(path, st) as fd: @@ -410,10 +422,12 @@ def process_file(self, path, st, cache): for chunk in self.chunker.chunkify(fd): 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]) + status = status or 'M' # regular file, modified (if not 'A' already) item = {b'path': safe_path, b'chunks': chunks} item.update(self.stat_attrs(st, path)) self.stats.nfiles += 1 self.add_item(item) + return status @staticmethod def list_archives(repository, key, manifest, cache=None): diff --git a/attic/archiver.py b/attic/archiver.py index 47650c2d4..0bcfd89a1 100644 --- a/attic/archiver.py +++ b/attic/archiver.py @@ -157,16 +157,17 @@ def _process(self, archive, cache, excludes, exclude_caches, skip_inodes, path, # Ignore unix sockets if stat.S_ISSOCK(st.st_mode): return - self.print_verbose(remove_surrogates(path)) + status = None if stat.S_ISREG(st.st_mode): try: - archive.process_file(path, st, cache) + status = archive.process_file(path, st, cache) except IOError as e: self.print_error('%s: %s', path, e) elif stat.S_ISDIR(st.st_mode): if exclude_caches and is_cachedir(path): return archive.process_item(path, st) + status = 'd' # directory try: entries = os.listdir(path) except OSError as e: @@ -176,13 +177,28 @@ def _process(self, archive, cache, excludes, exclude_caches, skip_inodes, path, self._process(archive, cache, excludes, exclude_caches, skip_inodes, os.path.join(path, filename), restrict_dev) 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): archive.process_item(path, st) + status = 'f' # fifo 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: 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): """Extract archive contents""" From 6d67379c086efa50dc133c84f38c544f64d0e44a Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Sun, 22 Mar 2015 15:52:43 +0100 Subject: [PATCH 2/2] refactor indicator (status) and item processing process_item was used only for dirs and fifo, replaced it by process_dir and process_fifo, so the status can be generated there (as it is done for the other item types). --- attic/archive.py | 17 +++++++++++------ attic/archiver.py | 6 ++---- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/attic/archive.py b/attic/archive.py index ed9561a4b..2ba5a08f0 100644 --- a/attic/archive.py +++ b/attic/archive.py @@ -365,28 +365,33 @@ def stat_attrs(self, st, path): acl_get(path, item, st, self.numeric_owner) return item - def process_item(self, path, st): + def process_dir(self, path, st): item = {b'path': make_path_safe(path)} item.update(self.stat_attrs(st, path)) self.add_item(item) + return 'd' # directory + + def process_fifo(self, path, st): + item = {b'path': make_path_safe(path)} + item.update(self.stat_attrs(st, path)) + self.add_item(item) + return 'f' # fifo def process_dev(self, path, st): item = {b'path': make_path_safe(path), b'rdev': st.st_rdev} item.update(self.stat_attrs(st, path)) self.add_item(item) if stat.S_ISCHR(st.st_mode): - status = 'c' # char device + return 'c' # char device elif stat.S_ISBLK(st.st_mode): - status = 'b' # block device - return status + return 'b' # block device def process_symlink(self, path, st): source = os.readlink(path) item = {b'path': make_path_safe(path), b'source': source} item.update(self.stat_attrs(st, path)) self.add_item(item) - status = 's' # symlink - return status + return 's' # symlink def process_file(self, path, st, cache): status = None diff --git a/attic/archiver.py b/attic/archiver.py index 0bcfd89a1..f046a5439 100644 --- a/attic/archiver.py +++ b/attic/archiver.py @@ -166,8 +166,7 @@ def _process(self, archive, cache, excludes, exclude_caches, skip_inodes, path, elif stat.S_ISDIR(st.st_mode): if exclude_caches and is_cachedir(path): return - archive.process_item(path, st) - status = 'd' # directory + status = archive.process_dir(path, st) try: entries = os.listdir(path) except OSError as e: @@ -179,8 +178,7 @@ def _process(self, archive, cache, excludes, exclude_caches, skip_inodes, path, elif stat.S_ISLNK(st.st_mode): status = archive.process_symlink(path, st) elif stat.S_ISFIFO(st.st_mode): - archive.process_item(path, st) - status = 'f' # fifo + status = archive.process_fifo(path, st) elif stat.S_ISCHR(st.st_mode) or stat.S_ISBLK(st.st_mode): status = archive.process_dev(path, st) else: