Skip to content

Instantly share code, notes, and snippets.

@YorikSar
Created September 13, 2011 10:09
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save YorikSar/1213527 to your computer and use it in GitHub Desktop.
Save YorikSar/1213527 to your computer and use it in GitHub Desktop.
Time Machine diff
#!/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])
@mobibob
Copy link

mobibob commented May 29, 2012

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment