+### Database clearing from diff files.
+
+R_HUNK = RX.compile(r'^@@ -\d+,(\d+) \+\d+,(\d+) @@$')
+
+def clear_entry(db, lno, line):
+
+ good = True
+
+ if line.startswith('['):
+ pos = line.find(']')
+ if pos < 0:
+ moan("failed to parse file entry (type field; line %d)" % lno)
+ return False
+ ty = line[1:pos].strip()
+ rest = line[pos + 1:]
+ hash = None
+ else:
+ ff = line.split(None, 1)
+ if len(ff) != 2:
+ moan("failed to parse file entry (field split; line %d)" % lno)
+ return False
+ ty = 'regular-file'
+ hash, rest = ff
+
+ ff = rest.split(None, 5)
+ if len(ff) != 6:
+ moan("failed to parse file entry (field split; line %d)" % lno)
+ return False
+ ino, mode, uidgid, mtime, sz, name = ff
+
+ if ty != 'symbolic-link':
+ target = None
+ else:
+ nn = name.split(' -> ', 1)
+ if len(nn) != 2:
+ moan("failed to parse file entry (name split; line %d)" % lno)
+ return False
+ name, target = nn
+ target = target.decode('string_escape')
+ name = name.decode('string_escape')
+
+ try:
+ st = OS.lstat(name)
+ except OSError, e:
+ moan("failed to stat `%s': %s" % (name, e.strerror))
+ if e.errno != E.ENOENT: good = False
+ else:
+ print "Clear cache entry for `%s'" % name
+ db.forget(st.st_ino)
+
+ return good
+
+def clear_cache(db):
+
+ ## Work through the input diff file one line at a time.
+ diffstate = 'gap'
+ lno = 0
+ good = True
+ for line in stdin:
+ if line.endswith('\n'): line = line[:-1]
+ lno += 1
+
+ ## We're in a gap between hunks. Find a hunk header and extract the line
+ ## counts.
+ if diffstate == 'gap':
+ m = R_HUNK.match(line)
+ if m:
+ oldlines = int(m.group(1))
+ newlines = int(m.group(2))
+ diffstate = 'hunk'
+ hdrlno = lno
+
+ ## We're in a hunk. Keep track of whether we've reached the end, and
+ ## discard entries from the cache for mismatching lines.
+ elif diffstate == 'hunk':
+ if len(line) == 0:
+ moan("empty line in diff hunk (line %d)" % lno)
+ good = False
+ ty = line[0]
+ if ty == ' ':
+ oldlines -= 1; newlines -= 1
+ elif ty == '+':
+ newlines -= 1
+ if not clear_entry(db, lno, line[1:]): good = False
+ elif ty == '-':
+ oldlines -= 1
+ if not clear_entry(db, lno, line[1:]): good = False
+ else:
+ moan("incomprehensible line in diff hunk (line %d)" % lno)
+ good = false
+ if oldlines < 0 or newlines < 0:
+ moan("inconsistent lengths in diff hunk header (line %d)" % hdrlno)
+ good = False
+ if oldlines == newlines == 0:
+ diffstate = 'gap'
+
+ if diffstate == 'hunk':
+ moan("truncated diff hunk (started at line %d)" % hdrlno)
+ good = False
+
+ return good
+
+###--------------------------------------------------------------------------