# HG changeset patch # User Franz Glasner # Date 1736167152 -3600 # Node ID bf74ce3c968d6cd4a4227a6efac11dc211187339 # Parent c7df81fb84b78dc9e95ca2a96f323800312b8c4a When computing digests use the order imposed by names alone. No different loops for dirs and nondirs. diff -r c7df81fb84b7 -r bf74ce3c968d cutils/treesum.py --- a/cutils/treesum.py Mon Jan 06 13:38:09 2025 +0100 +++ b/cutils/treesum.py Mon Jan 06 13:39:12 2025 +0100 @@ -307,47 +307,49 @@ outfp.flush() return - for top, dirs, nondirs in walk.walk( + for top, fsobjects in walk.walk( root, follow_symlinks=follow_directory_symlinks): dir_dgst = algorithm[0]() - for dn in dirs: - if dn.is_symlink and not follow_directory_symlinks: - linktgt = util.fsencode(os.readlink(dn.path)) - linkdgst = algorithm[0]() - linkdgst.update(b"%d:%s," % (len(linktgt), linktgt)) - dir_dgst.update(b"1:S,%d:%s," % (len(dn.fsname), dn.fsname)) - dir_dgst.update(linkdgst.digest()) - opath = "/".join(top) + "/" + dn.name if top else dn.name + for fso in fsobjects: + if fso.is_dir: + if fso.is_symlink and not follow_directory_symlinks: + linktgt = util.fsencode(os.readlink(fso.path)) + linkdgst = algorithm[0]() + linkdgst.update(b"%d:%s," % (len(linktgt), linktgt)) + dir_dgst.update(b"1:S,%d:%s," + % (len(fso.fsname), fso.fsname)) + dir_dgst.update(linkdgst.digest()) + opath = "/".join(top) + "/" + fso.name if top else fso.name + outfp.write( + format_bsd_line( + algorithm[1], + linkdgst.digest(), + "%s/./@" % (opath,), + use_base64)) + outfp.flush() + continue + # fetch from dir_digests + dgst = dir_digests[top + (fso.name,)] + dir_dgst.update(b"1:d,%d:%s," % (len(fso.fsname), fso.fsname)) + dir_dgst.update(dgst) + else: + dir_dgst.update(b"1:f,%d:%s," % (len(fso.fsname), fso.fsname)) + if with_metadata_mtime: + mtime = datetime.datetime.utcfromtimestamp( + int(fso.stat.st_mtime)) + mtime = mtime.isoformat("T") + "Z" + if not isinstance(mtime, bytes): + mtime = mtime.encode("ascii") + dir_dgst.update(b"5:mtime,%d:%s," % (len(mtime), mtime)) + dgst = digest.compute_digest_file( + algorithm[0], fso.path, use_mmap=use_mmap) + dir_dgst.update(dgst) + opath = "/".join(top) + "/" + fso.name if top else fso.name outfp.write( format_bsd_line( - algorithm[1], - linkdgst.digest(), - "%s/./@" % (opath,), - use_base64)) + algorithm[1], dgst, opath, use_base64)) outfp.flush() - continue - # fetch from dir_digests - dgst = dir_digests[top + (dn.name,)] - dir_dgst.update(b"1:d,%d:%s," % (len(dn.fsname), dn.fsname)) - dir_dgst.update(dgst) - for fn in nondirs: - dir_dgst.update(b"1:f,%d:%s," % (len(fn.fsname), fn.fsname)) - if with_metadata_mtime: - mtime = datetime.datetime.utcfromtimestamp( - int(fn.stat.st_mtime)) - mtime = mtime.isoformat("T") + "Z" - if not isinstance(mtime, bytes): - mtime = mtime.encode("ascii") - dir_dgst.update(b"5:mtime,%d:%s," % (len(mtime), mtime)) - dgst = digest.compute_digest_file( - algorithm[0], fn.path, use_mmap=use_mmap) - dir_dgst.update(dgst) - opath = "/".join(top) + "/" + fn.name if top else fn.name - outfp.write( - format_bsd_line( - algorithm[1], dgst, opath, use_base64)) - outfp.flush() opath = "/".join(top) + "/" if top else "" outfp.write(format_bsd_line( algorithm[1], dir_dgst.digest(), opath, use_base64)) diff -r c7df81fb84b7 -r bf74ce3c968d cutils/util/walk.py --- a/cutils/util/walk.py Mon Jan 06 13:38:09 2025 +0100 +++ b/cutils/util/walk.py Mon Jan 06 13:39:12 2025 +0100 @@ -130,11 +130,14 @@ - the `root` is never part of the returned data - the returned directory in "top" is not a string form but a list of individual path segments - - there is only one yields list + - there is only one yielded list * contains :class:`WalkDirEntry` * sorted by its fsname + The caller can easily get the old dirs and nondirs by filtering + the yielded list using "entry.is_dir". + - recurse into sub-directories first ("topdown=False") - sort consistently all yielded lists by the filesystem encoding