34 ''' |
34 ''' |
35 |
35 |
36 from mercurial import context, util |
36 from mercurial import context, util |
37 import os.path, re, sys, time |
37 import os.path, re, sys, time |
38 |
38 |
39 |
|
40 re_kw = re.compile( |
39 re_kw = re.compile( |
41 r'\$(Id|Header|Author|Date|Revision|RCSFile|Source)[^$]*?\$') |
40 r'\$(Id|Header|Author|Date|Revision|RCSFile|Source)[^$]*?\$') |
42 |
41 |
43 |
42 def kwexpand(mobj, kwfctx): |
44 def kwexpand(matchobj, repo, path, changeid=None, fileid=None, filelog=None): |
|
45 '''Called by kwfilelog.read and pretxnkw. |
43 '''Called by kwfilelog.read and pretxnkw. |
46 Sets supported keywords as local variables and evaluates them to |
44 Expands keywords according to file context.''' |
47 their expansion if matchobj is equal to string representation.''' |
45 return '$%s: %s $' % (mobj.group(1), kwfctx.expand(mobj.group(1))) |
48 c = context.filectx(repo, path, |
|
49 changeid=changeid, fileid=fileid, filelog=filelog) |
|
50 def Revision(): |
|
51 return str(c.changectx()) |
|
52 def Author(): |
|
53 return util.shortuser(c.user()) |
|
54 def Date(): |
|
55 return time.strftime('%Y/%m/%d %H:%M:%S', time.gmtime(c.date()[0])) |
|
56 def RCSFile(): |
|
57 return os.path.basename(path)+',v' |
|
58 def Source(): |
|
59 return repo.wjoin(path)+',v' |
|
60 def Header(): |
|
61 return ' '.join([Source(), Revision(), Date(), Author()]) |
|
62 def Id(): |
|
63 return ' '.join([RCSFile(), Revision(), Date(), Author()]) |
|
64 return '$%s: %s $' % (matchobj.group(1), eval('%s()' % matchobj.group(1))) |
|
65 |
46 |
66 def kwfmatches(ui, repo, files): |
47 def kwfmatches(ui, repo, files): |
67 '''Selects candidates for keyword substitution |
48 '''Selects candidates for keyword substitution |
68 configured in keyword section in hgrc.''' |
49 configured in keyword section in hgrc.''' |
69 files = [f for f in files if not f.startswith('.hg')] |
50 files = [f for f in files if not f.startswith('.hg')] |
76 for mf in kwfmatchers: |
57 for mf in kwfmatchers: |
77 if mf(f): |
58 if mf(f): |
78 candidates.append(f) |
59 candidates.append(f) |
79 break |
60 break |
80 return candidates |
61 return candidates |
|
62 |
|
63 def utc(hgdate): |
|
64 '''Returns hgdate in cvs-like UTC format.''' |
|
65 return time.strftime('%Y/%m/%d %H:%M:%S', time.gmtime(hgdate[0])) |
|
66 |
|
67 |
|
68 class kwfilectx(context.filectx): |
|
69 ''' |
|
70 Provides keyword expansion functions based on file context. |
|
71 ''' |
|
72 def __init__(self, repo, path, changeid=None, fileid=None, filelog=None): |
|
73 context.filectx.__init__(self, repo, path, changeid, fileid, filelog) |
|
74 def Revision(self): |
|
75 return str(self.changectx()) |
|
76 def Author(self): |
|
77 return util.shortuser(self.user()) |
|
78 def Date(self): |
|
79 return utc(self.date()) |
|
80 def RCSFile(self): |
|
81 return os.path.basename(self._path)+',v' |
|
82 def Source(self): |
|
83 return self._repo.wjoin(self._path)+',v' |
|
84 def Header(self): |
|
85 return ' '.join( |
|
86 [self.Source(), self.Revision(), self.Date(), self.Author()]) |
|
87 def Id(self): |
|
88 return ' '.join( |
|
89 [self.RCSFile(), self.Revision(), self.Date(), self.Author()]) |
|
90 def expand(self, kw): |
|
91 '''Called from kwexpand, evaluates keyword.''' |
|
92 return eval('self.%s()' % kw) |
81 |
93 |
82 |
94 |
83 def reposetup(ui, repo): |
95 def reposetup(ui, repo): |
84 from mercurial import filelog, revlog |
96 from mercurial import filelog, revlog |
85 |
97 |
110 |
122 |
111 def read(self, node): |
123 def read(self, node): |
112 '''Substitutes keywords when reading filelog.''' |
124 '''Substitutes keywords when reading filelog.''' |
113 data = super(kwfilelog, self).read(node) |
125 data = super(kwfilelog, self).read(node) |
114 if self.iskwcandidate(data): |
126 if self.iskwcandidate(data): |
115 return re_kw.sub(lambda m: |
127 kwfctx = kwfilectx(self._repo, self._path, |
116 kwexpand(m, self._repo, self._path, |
128 fileid=node, filelog=self) |
117 fileid=node, filelog=self), data) |
129 return re_kw.sub(lambda m: kwexpand(m, kwfctx), data) |
118 return data |
130 return data |
119 |
131 |
120 def add(self, text, meta, tr, link, p1=None, p2=None): |
132 def add(self, text, meta, tr, link, p1=None, p2=None): |
121 '''Removes keyword substitutions when adding to filelog.''' |
133 '''Removes keyword substitutions when adding to filelog.''' |
122 if self.iskwcandidate(text): |
134 if self.iskwcandidate(text): |
168 modified, added = repo.status(files=files, match=match)[:2] |
180 modified, added = repo.status(files=files, match=match)[:2] |
169 |
181 |
170 for f in kwfmatches(ui, repo, modified+added): |
182 for f in kwfmatches(ui, repo, modified+added): |
171 data = repo.wfile(f).read() |
183 data = repo.wfile(f).read() |
172 if not util.binary(data): |
184 if not util.binary(data): |
173 data, kwct = re_kw.subn(lambda m: |
185 kwfctx = kwfilectx(repo, f, changeid=args['node']) |
174 kwexpand(m, repo, f, changeid=args['node']), data) |
186 data, kwct = re_kw.subn(lambda m: kwexpand(m, kwfctx), data) |
175 if kwct: |
187 if kwct: |
176 ui.debug(_('overwriting %s expanding keywords\n' % f)) |
188 ui.debug(_('overwriting %s expanding keywords\n' % f)) |
177 repo.wfile(f, 'w').write(data) |
189 repo.wfile(f, 'w').write(data) |