comparison cutils/treesum.py @ 151:b26c4290e928

Implement "--mtime" for treesum to include a file's mtime in a directory digest. Pure file diests are not affected.
author Franz Glasner <fzglas.hg@dom66.de>
date Fri, 03 Jan 2025 23:33:37 +0100
parents f84cf853da22
children 46cb438fa520
comparison
equal deleted inserted replaced
150:f84cf853da22 151:b26c4290e928
75 "empty use it as the leading \"ROOT (<TAG>)\" output.") 75 "empty use it as the leading \"ROOT (<TAG>)\" output.")
76 gp.add_argument( 76 gp.add_argument(
77 "--mmap", action="store_true", dest="mmap", default=None, 77 "--mmap", action="store_true", dest="mmap", default=None,
78 help="Use mmap if available. Default is to determine " 78 help="Use mmap if available. Default is to determine "
79 "automatically from the filesize.") 79 "automatically from the filesize.")
80 gp.add_argument(
81 "--mtime", action="store_true", dest="metadata_mtime",
82 help="Consider the mtime of files (non-directories) when "
83 "generating digests for directories. Digests for files are "
84 "not affected.")
80 gp.add_argument( 85 gp.add_argument(
81 "--no-mmap", action="store_false", dest="mmap", default=None, 86 "--no-mmap", action="store_false", dest="mmap", default=None,
82 help="Dont use mmap.") 87 help="Dont use mmap.")
83 gp.add_argument( 88 gp.add_argument(
84 "--output", "-o", action="store", metavar="OUTPUT", 89 "--output", "-o", action="store", metavar="OUTPUT",
182 comment=[], 187 comment=[],
183 follow_directory_symlinks=False, 188 follow_directory_symlinks=False,
184 logical=None, 189 logical=None,
185 minimal=None, 190 minimal=None,
186 mmap=None, 191 mmap=None,
192 mtime=False,
187 output=None): 193 output=None):
188 opts = argparse.Namespace( 194 opts = argparse.Namespace(
189 directories=directories, 195 directories=directories,
190 algorithm=(util.algotag2algotype(algorithm), 196 algorithm=(util.algotag2algotype(algorithm),
191 algorithm), 197 algorithm),
194 comment=comment, 200 comment=comment,
195 follow_directory_symlinks=follow_directory_symlinks, 201 follow_directory_symlinks=follow_directory_symlinks,
196 logical=logical, 202 logical=logical,
197 minimal=minimal, 203 minimal=minimal,
198 mmap=mmap, 204 mmap=mmap,
205 metadata_mtime=mtime,
199 output=output) 206 output=output)
200 return opts 207 return opts
201 208
202 209
203 def treesum(opts): 210 def treesum(opts):
230 with out_cm as outfp: 237 with out_cm as outfp:
231 for d in opts.directories: 238 for d in opts.directories:
232 generate_treesum_for_directory( 239 generate_treesum_for_directory(
233 outfp, d, opts.algorithm, opts.mmap, opts.base64, opts.logical, 240 outfp, d, opts.algorithm, opts.mmap, opts.base64, opts.logical,
234 opts.follow_directory_symlinks, 241 opts.follow_directory_symlinks,
242 opts.metadata_mtime,
235 minimal=opts.minimal, 243 minimal=opts.minimal,
236 comment=opts.comment) 244 comment=opts.comment)
237 245
238 246
239 def generate_treesum_for_directory( 247 def generate_treesum_for_directory(
240 outfp, root, algorithm, use_mmap, use_base64, handle_root_logical, 248 outfp, root, algorithm, use_mmap, use_base64, handle_root_logical,
241 follow_directory_symlinks, minimal=None, comment=None): 249 follow_directory_symlinks, with_metadata_mtime,
250 minimal=None, comment=None):
242 """ 251 """
243 252
244 :param outfp: a *binary* file with a "write()" and a "flush()" method 253 :param outfp: a *binary* file with a "write()" and a "flush()" method
245 254
246 """ 255 """
247 outfp.write(format_bsd_line("VERSION", "1", None, False)) 256 outfp.write(format_bsd_line("VERSION", "1", None, False))
248 outfp.flush() 257 outfp.flush()
249 258
250 # Note given non-default flags that are relevant for directory traversal 259 # Note given non-default flags that are relevant for directory traversal
251 flags = [] 260 flags = []
261 if with_metadata_mtime:
262 flags.append("with-metadata-mtime")
252 if handle_root_logical: 263 if handle_root_logical:
253 flags.append("logical") 264 flags.append("logical")
254 if follow_directory_symlinks: 265 if follow_directory_symlinks:
255 flags.append("follow-directory-symlinks") 266 flags.append("follow-directory-symlinks")
256 if flags: 267 if flags:
319 dgst = dir_digests[top + (dn.name,)] 330 dgst = dir_digests[top + (dn.name,)]
320 dir_dgst.update(b"1:d,%d:%s," % (len(dn.fsname), dn.fsname)) 331 dir_dgst.update(b"1:d,%d:%s," % (len(dn.fsname), dn.fsname))
321 dir_dgst.update(dgst) 332 dir_dgst.update(dgst)
322 for fn in nondirs: 333 for fn in nondirs:
323 dir_dgst.update(b"1:f,%d:%s," % (len(fn.fsname), fn.fsname)) 334 dir_dgst.update(b"1:f,%d:%s," % (len(fn.fsname), fn.fsname))
335 if with_metadata_mtime:
336 mtime = datetime.datetime.utcfromtimestamp(
337 int(fn.stat.st_mtime))
338 mtime = mtime.isoformat("T") + "Z"
339 if not isinstance(mtime, bytes):
340 mtime = mtime.encode("ascii")
341 dir_dgst.update(b"5:mtime,%d:%s," % (len(mtime), mtime))
324 dgst = digest.compute_digest_file( 342 dgst = digest.compute_digest_file(
325 algorithm[0], fn.path, use_mmap=use_mmap) 343 algorithm[0], fn.path, use_mmap=use_mmap)
326 dir_dgst.update(dgst) 344 dir_dgst.update(dgst)
327 opath = "/".join(top) + "/" + fn.name if top else fn.name 345 opath = "/".join(top) + "/" + fn.name if top else fn.name
328 outfp.write( 346 outfp.write(