Created
September 13, 2011 10:09
-
-
Save YorikSar/1213527 to your computer and use it in GitHub Desktop.
Time Machine diff
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
import sys, os | |
def safe_index(l, val): | |
try: | |
return l.index(val) | |
except ValueError: | |
return -1 | |
def onerror(err): | |
print >>sys.stderr, err | |
def lsamefile(f1, f2): | |
s1 = os.lstat(f1) | |
s2 = os.lstat(f2) | |
return os.path.samestat(s1, s2) | |
def diff_tm(old_dir, new_dir): | |
""" | |
Yields tuples like: | |
is_dir, event, path | |
Where: | |
is_dir is boolean showing wherther item is dir or file; | |
event is: | |
- '+' for new item, | |
- '-' for deleted item, | |
- '!' for changed item, | |
- ' ' for not changed item | |
path is relative path of item | |
""" | |
old_walk = os.walk(old_dir, onerror=onerror) | |
new_walk = os.walk(new_dir, onerror=onerror) | |
while True: | |
try: | |
old_cur, old_subdirs, old_files = old_walk.next() | |
new_cur, new_subdirs, new_files = new_walk.next() | |
except StopIteration: | |
break | |
subdirs = [] | |
for d in old_subdirs: | |
path1 = os.path.join(old_cur,d) | |
rel = os.path.relpath(path1,old_dir) | |
ind = safe_index(new_subdirs,d) | |
if ind == -1: | |
yield (True, '-', rel) | |
continue | |
path2 = os.path.join(new_cur,d) | |
if lsamefile(path1,path2): | |
yield (True, ' ', rel) | |
else: | |
yield (True, '!', rel) | |
subdirs.append(d) | |
del new_subdirs[ind] | |
for d in new_subdirs: | |
yield (True, '+', os.path.relpath(os.path.join(new_cur, d), new_dir)) | |
old_subdirs[:] = new_subdirs[:] = subdirs | |
for f in old_files: | |
path1 = os.path.join(old_cur, f) | |
rel = os.path.relpath(path1, old_dir) | |
ind = safe_index(new_files, f) | |
if ind == -1: | |
yield (False, '-', rel) | |
continue | |
path2 = os.path.join(new_cur,f) | |
if lsamefile(path1,path2): | |
yield (False, ' ', rel) | |
else: | |
yield (False, '!', rel) | |
del new_files[ind] | |
for f in new_files: | |
yield (False, '+', os.path.relpath(os.path.join(new_cur, f), new_dir)) | |
if __name__ == '__main__': | |
import optparse | |
parser = optparse.OptionParser( | |
usage="Usage: %prog old_dir new_dir", | |
description="Gathers difference between two directory trees by inode " | |
"numbers. By default prints only new and changed files. " | |
"All output paths is relative to old_dir/new_dir.") | |
parser.add_option("-f", "--files", action="store_true", | |
dest="show_files", default=True, help="Show files in output") | |
parser.add_option("-F", "--no-files", action="store_false", | |
dest="show_files", default=True, help="Don't show files in output") | |
parser.add_option("-d", "--dirs", action="store_true", | |
dest="show_dirs", default=False, help="Show dirs in output") | |
parser.add_option("-D", "--no-dirs", action="store_true", | |
dest="show_dirs", default=False, help="Don't show dirs in output") | |
parser.add_option("-s", action="store_true", | |
dest="names_only", default=True, help="Show only file/dir paths") | |
parser.add_option("-l", action="store_false", | |
dest="names_only", help="Show what happend with file/dir") | |
parser.add_option("-u", action="store_true", dest="show_unchanged", | |
default=False, help="Show unchanged items") | |
parser.add_option("-U", action="store_false", dest="show_unchanged", | |
help="Don't show unchanged items") | |
parser.add_option("-c", action="store_true", dest="show_changed", | |
default=True, help="Show changed items") | |
parser.add_option("-C", action="store_false", dest="show_changed", | |
help="Don't show changed items") | |
parser.add_option("-r", action="store_true", dest="show_removed", | |
default=False, help="Show removed items") | |
parser.add_option("-R", action="store_false", dest="show_removed", | |
help="Don't show removed items") | |
parser.add_option("-n", action="store_true", dest="show_new", | |
default=True, help="Show new items") | |
parser.add_option("-N", action="store_false", dest="show_new", | |
help="Don't show new items") | |
(options, args) = parser.parse_args() | |
if len(args) != 2: | |
parser.print_help() | |
sys.exit(1) | |
event_dict = {' ':'Unchanged', '!':'Changed', '-':'Removed', '+':'New'} | |
event_opts = {' ':options.show_unchanged, '!':options.show_changed, | |
'-':options.show_removed, '+':options.show_new} | |
for t in diff_tm(args[0], args[1]): | |
if (t[0] and options.show_dirs or not t[0] and options.show_files) \ | |
and event_opts[t[1]]: | |
if options.names_only: | |
print t[2] | |
else: | |
print "%s %s: %s" % (event_dict[t[1]], | |
"dir" if t[0] else "file", t[2]) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
From blog: http://procrastinatrix.yorik.name/2010/10/time-machine-backup-diffs.html
Run it like this:
cd /Volumes//Backups.backupdb//
diff_tm yyyy-MM-dd-HHmmss1 yyyy-MM-dd-HHmmss2
By default it shows only new or changed files, just everything that was sent to server during last backup. It runs very fast (around a second for not-so-far backups).
Hope, it will help someone. Testing, changes, comments appreciated.
PS: I've found that the fattest files in every backup is Chrome's history index. I think, I'll keep them in backup.