Fix mail importer into shape again
[trackgit.git] / notes.py
blob3d5755c4f8ef7367143bed883042d844e212d104
1 import os
2 import sys
3 import time
4 import re
5 from collections import defaultdict
6 from sqlalchemy.orm import join
8 import db
9 from git import git
11 NOTES_INDEX = '.git/git-notes-index'
12 notes_env = {
13 'GIT_INDEX_FILE': NOTES_INDEX,
15 NOTES_REF = 'refs/heads/mailnotes'
17 _eol_space_re = re.compile('\s+$', re.MULTILINE)
18 def write_notes(commit_sha1, notes):
19 notes = _eol_space_re.sub('', notes)
20 notes = notes.strip('\n')
21 blob_sha1 = git('hash-object', '-w', '--stdin', input=notes)[0]
22 git('update-index', '--add', '--cacheinfo', '0644', blob_sha1, commit_sha1, env=notes_env)
24 def finalize_notes():
25 previous, ret = git('rev-parse', NOTES_REF)
26 if ret != 0:
27 args = []
28 previous_arg = []
29 else:
30 args = ['-p', previous.strip()]
31 previous_arg = [previous.strip()]
32 tree_sha1 = git('write-tree', env=notes_env)[0].strip()
33 head_sha1 = git('commit-tree', tree_sha1, *args,
34 input='Mass annotation by notes.py')[0].strip()
35 git('update-ref', '-m', 'Mass annotation by notes.py',
36 NOTES_REF, head_sha1, *previous_arg)
38 def split_and_tab(buf):
39 ret = []
40 for line in str(buf).splitlines():
41 ret.append('\t%s\n' % line)
42 return ret
44 def compute_notes(session, commit, mail):
45 notes = []
46 notes.append('Mail-From: %s\n' % mail.author)
47 notes.append('Message-Id: %s\n' % mail.message_id)
48 notes.append('Posted-Date: %s\n'
49 % time.strftime("%c", time.localtime(mail.post_date)))
50 if mail.in_reply_to:
51 notes.append('In-Reply-To: %s\n' % mail.in_reply_to)
52 if mail.references:
53 notes.append('References:\n')
54 for r in mail.references:
55 notes.append('\t%s\n' % r.message_id)
56 if len(mail.patch)>0 and mail.patch[0].extra_notes:
57 notes.append('Extra-notes:\n')
58 notes.extend(split_and_tab(mail.patch[0].extra_notes))
59 return notes
61 _merge_re = re.compile("^([a-f0-9]{40}) Merge branch '([^']+)'")
62 def _redo_pu(session, notes):
63 pu_ref = git('rev-parse', 'origin/pu')[0].strip()
64 pu_topic = session.query(db.Topic).filter(db.Topic.name == 'pu').first()
65 if pu_topic:
66 notes[pu_ref].append('Pu-Overview:\n')
67 notes[pu_ref].extend(split_and_tab(pu_topic.cooking_notes))
68 notes[pu_ref].append('\n')
69 for line in git('log', '--first-parent', '--pretty=tformat:%H %s',
70 'origin/master..origin/pu', ret_pipe=True):
71 m = _merge_re.match(line)
72 if not m:
73 continue
74 sha1 = m.group(1)
75 branch = m.group(2)
76 t = session.query(db.Topic).filter(db.Topic.name == branch).first()
77 if t and t.cooking_notes:
78 notes[sha1].append('Pu-Topic:\n')
79 notes[sha1].extend(split_and_tab(t.cooking_notes))
80 notes[sha1].append('\n')
82 def _redo_patches(session, notes):
83 count = 0
84 for cmt, mail in (session.query(db.Commit, db.Mail)
85 .select_from(join(db.Mail, db.Commit,
86 db.Mail.patch_id==db.Commit.patch_id))
87 .filter(db.Commit.upstream==True)
88 .filter(db.Mail.has_patch==True)):
89 nts = compute_notes(session, cmt, mail)
90 if not nts:
91 continue
92 if notes[cmt.sha1]:
93 notes[cmt.sha1].append('\n')
94 notes[cmt.sha1].extend(nts)
95 sys.stdout.write('\r%6d' % count)
96 sys.stdout.flush()
97 count = count + 1
98 sys.stdout.write('\n')
100 def _redo_all():
101 session = db.Session()
102 count = 0
103 notes = defaultdict(list)
104 _redo_patches(session, notes)
105 _redo_pu(session, notes)
106 os.unlink(NOTES_INDEX)
107 count = 0
108 for cmt, nts in notes.iteritems():
109 sys.stdout.write('\r%6d' % count)
110 sys.stdout.flush()
111 write_notes(cmt, ''.join(nts))
112 count = count + 1
113 sys.stdout.write('\n')
114 finalize_notes()
116 if __name__ == '__main__':
117 if len(sys.argv) > 1:
118 session = db.Session()
119 cmt = session.query(db.Commit).filter(db.Commit.sha1==sys.argv[1]).one()
120 print compute_notes(session, cmt)
121 else:
122 _redo_all()