##// END OF EJS Templates
keyword: avoid extra diffs when not comparing against working dir...
Christian Ebert -
r5885:53be1575 default
parent child Browse files
Show More
@@ -1,511 +1,521 b''
1 # keyword.py - $Keyword$ expansion for Mercurial
1 # keyword.py - $Keyword$ expansion for Mercurial
2 #
2 #
3 # Copyright 2007, 2008 Christian Ebert <blacktrash@gmx.net>
3 # Copyright 2007, 2008 Christian Ebert <blacktrash@gmx.net>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7 #
7 #
8 # $Id$
8 # $Id$
9 #
9 #
10 # Keyword expansion hack against the grain of a DSCM
10 # Keyword expansion hack against the grain of a DSCM
11 #
11 #
12 # There are many good reasons why this is not needed in a distributed
12 # There are many good reasons why this is not needed in a distributed
13 # SCM, still it may be useful in very small projects based on single
13 # SCM, still it may be useful in very small projects based on single
14 # files (like LaTeX packages), that are mostly addressed to an audience
14 # files (like LaTeX packages), that are mostly addressed to an audience
15 # not running a version control system.
15 # not running a version control system.
16 #
16 #
17 # For in-depth discussion refer to
17 # For in-depth discussion refer to
18 # <http://www.selenic.com/mercurial/wiki/index.cgi/KeywordPlan>.
18 # <http://www.selenic.com/mercurial/wiki/index.cgi/KeywordPlan>.
19 #
19 #
20 # Keyword expansion is based on Mercurial's changeset template mappings.
20 # Keyword expansion is based on Mercurial's changeset template mappings.
21 #
21 #
22 # Binary files are not touched.
22 # Binary files are not touched.
23 #
23 #
24 # Setup in hgrc:
24 # Setup in hgrc:
25 #
25 #
26 # [extensions]
26 # [extensions]
27 # # enable extension
27 # # enable extension
28 # hgext.keyword =
28 # hgext.keyword =
29 #
29 #
30 # Files to act upon/ignore are specified in the [keyword] section.
30 # Files to act upon/ignore are specified in the [keyword] section.
31 # Customized keyword template mappings in the [keywordmaps] section.
31 # Customized keyword template mappings in the [keywordmaps] section.
32 #
32 #
33 # Run "hg help keyword" and "hg kwdemo" to get info on configuration.
33 # Run "hg help keyword" and "hg kwdemo" to get info on configuration.
34
34
35 '''keyword expansion in local repositories
35 '''keyword expansion in local repositories
36
36
37 This extension expands RCS/CVS-like or self-customized $Keywords$
37 This extension expands RCS/CVS-like or self-customized $Keywords$
38 in tracked text files selected by your configuration.
38 in tracked text files selected by your configuration.
39
39
40 Keywords are only expanded in local repositories and not stored in
40 Keywords are only expanded in local repositories and not stored in
41 the change history. The mechanism can be regarded as a convenience
41 the change history. The mechanism can be regarded as a convenience
42 for the current user or for archive distribution.
42 for the current user or for archive distribution.
43
43
44 Configuration is done in the [keyword] and [keywordmaps] sections
44 Configuration is done in the [keyword] and [keywordmaps] sections
45 of hgrc files.
45 of hgrc files.
46
46
47 Example:
47 Example:
48
48
49 [keyword]
49 [keyword]
50 # expand keywords in every python file except those matching "x*"
50 # expand keywords in every python file except those matching "x*"
51 **.py =
51 **.py =
52 x* = ignore
52 x* = ignore
53
53
54 Note: the more specific you are in your filename patterns
54 Note: the more specific you are in your filename patterns
55 the less you lose speed in huge repos.
55 the less you lose speed in huge repos.
56
56
57 For [keywordmaps] template mapping and expansion demonstration and
57 For [keywordmaps] template mapping and expansion demonstration and
58 control run "hg kwdemo".
58 control run "hg kwdemo".
59
59
60 An additional date template filter {date|utcdate} is provided.
60 An additional date template filter {date|utcdate} is provided.
61
61
62 The default template mappings (view with "hg kwdemo -d") can be replaced
62 The default template mappings (view with "hg kwdemo -d") can be replaced
63 with customized keywords and templates.
63 with customized keywords and templates.
64 Again, run "hg kwdemo" to control the results of your config changes.
64 Again, run "hg kwdemo" to control the results of your config changes.
65
65
66 Before changing/disabling active keywords, run "hg kwshrink" to avoid
66 Before changing/disabling active keywords, run "hg kwshrink" to avoid
67 the risk of inadvertedly storing expanded keywords in the change history.
67 the risk of inadvertedly storing expanded keywords in the change history.
68
68
69 To force expansion after enabling it, or a configuration change, run
69 To force expansion after enabling it, or a configuration change, run
70 "hg kwexpand".
70 "hg kwexpand".
71
71
72 Also, when committing with the record extension or using mq's qrecord, be aware
72 Also, when committing with the record extension or using mq's qrecord, be aware
73 that keywords cannot be updated. Again, run "hg kwexpand" on the files in
73 that keywords cannot be updated. Again, run "hg kwexpand" on the files in
74 question to update keyword expansions after all changes have been checked in.
74 question to update keyword expansions after all changes have been checked in.
75
75
76 Expansions spanning more than one line and incremental expansions,
76 Expansions spanning more than one line and incremental expansions,
77 like CVS' $Log$, are not supported. A keyword template map
77 like CVS' $Log$, are not supported. A keyword template map
78 "Log = {desc}" expands to the first line of the changeset description.
78 "Log = {desc}" expands to the first line of the changeset description.
79 '''
79 '''
80
80
81 from mercurial import commands, cmdutil, context, dispatch, filelog
81 from mercurial import commands, cmdutil, context, dispatch, filelog
82 from mercurial import patch, localrepo, revlog, templater, util
82 from mercurial import patch, localrepo, revlog, templater, util
83 from mercurial.node import *
83 from mercurial.node import *
84 from mercurial.i18n import _
84 from mercurial.i18n import _
85 import re, shutil, sys, tempfile, time
85 import re, shutil, sys, tempfile, time
86
86
87 commands.optionalrepo += ' kwdemo'
87 commands.optionalrepo += ' kwdemo'
88
88
89 def utcdate(date):
89 def utcdate(date):
90 '''Returns hgdate in cvs-like UTC format.'''
90 '''Returns hgdate in cvs-like UTC format.'''
91 return time.strftime('%Y/%m/%d %H:%M:%S', time.gmtime(date[0]))
91 return time.strftime('%Y/%m/%d %H:%M:%S', time.gmtime(date[0]))
92
92
93 def _kwrestrict(cmd):
93 def _kwrestrict(cmd):
94 '''Returns True if cmd should trigger restricted expansion.
94 '''Returns True if cmd should trigger restricted expansion.
95 Keywords will only expanded when writing to working dir.
95 Keywords will only expanded when writing to working dir.
96 Crucial for mq as expanded keywords should not make it into patches.'''
96 Crucial for mq as expanded keywords should not make it into patches.'''
97 return cmd in ('qimport', 'qnew', 'qpush', 'qrefresh', 'record', 'qrecord')
97 return cmd in ('diff1',
98 'qimport', 'qnew', 'qpush', 'qrefresh', 'record', 'qrecord')
98
99
99
100
100 _kwtemplater = None
101 _kwtemplater = None
101
102
102 class kwtemplater(object):
103 class kwtemplater(object):
103 '''
104 '''
104 Sets up keyword templates, corresponding keyword regex, and
105 Sets up keyword templates, corresponding keyword regex, and
105 provides keyword substitution functions.
106 provides keyword substitution functions.
106 '''
107 '''
107 templates = {
108 templates = {
108 'Revision': '{node|short}',
109 'Revision': '{node|short}',
109 'Author': '{author|user}',
110 'Author': '{author|user}',
110 'Date': '{date|utcdate}',
111 'Date': '{date|utcdate}',
111 'RCSFile': '{file|basename},v',
112 'RCSFile': '{file|basename},v',
112 'Source': '{root}/{file},v',
113 'Source': '{root}/{file},v',
113 'Id': '{file|basename},v {node|short} {date|utcdate} {author|user}',
114 'Id': '{file|basename},v {node|short} {date|utcdate} {author|user}',
114 'Header': '{root}/{file},v {node|short} {date|utcdate} {author|user}',
115 'Header': '{root}/{file},v {node|short} {date|utcdate} {author|user}',
115 }
116 }
116
117
117 def __init__(self, ui, repo, inc, exc, hgcmd):
118 def __init__(self, ui, repo, inc, exc, hgcmd):
118 self.ui = ui
119 self.ui = ui
119 self.repo = repo
120 self.repo = repo
120 self.matcher = util.matcher(repo.root, inc=inc, exc=exc)[1]
121 self.matcher = util.matcher(repo.root, inc=inc, exc=exc)[1]
121 self.hgcmd = hgcmd
122 self.hgcmd = hgcmd
122 self.commitnode = None
123 self.commitnode = None
123 self.path = ''
124 self.path = ''
124
125
125 kwmaps = self.ui.configitems('keywordmaps')
126 kwmaps = self.ui.configitems('keywordmaps')
126 if kwmaps: # override default templates
127 if kwmaps: # override default templates
127 kwmaps = [(k, templater.parsestring(v, quoted=False))
128 kwmaps = [(k, templater.parsestring(v, quoted=False))
128 for (k, v) in kwmaps]
129 for (k, v) in kwmaps]
129 self.templates = dict(kwmaps)
130 self.templates = dict(kwmaps)
130 escaped = map(re.escape, self.templates.keys())
131 escaped = map(re.escape, self.templates.keys())
131 kwpat = r'\$(%s)(: [^$\n\r]*? )??\$' % '|'.join(escaped)
132 kwpat = r'\$(%s)(: [^$\n\r]*? )??\$' % '|'.join(escaped)
132 self.re_kw = re.compile(kwpat)
133 self.re_kw = re.compile(kwpat)
133
134
134 templater.common_filters['utcdate'] = utcdate
135 templater.common_filters['utcdate'] = utcdate
135 self.ct = cmdutil.changeset_templater(self.ui, self.repo,
136 self.ct = cmdutil.changeset_templater(self.ui, self.repo,
136 False, '', False)
137 False, '', False)
137
138
138 def substitute(self, node, data, subfunc):
139 def substitute(self, node, data, subfunc):
139 '''Obtains file's changenode if commit node not given,
140 '''Obtains file's changenode if commit node not given,
140 and calls given substitution function.'''
141 and calls given substitution function.'''
141 if self.commitnode:
142 if self.commitnode:
142 fnode = self.commitnode
143 fnode = self.commitnode
143 else:
144 else:
144 c = context.filectx(self.repo, self.path, fileid=node)
145 c = context.filectx(self.repo, self.path, fileid=node)
145 fnode = c.node()
146 fnode = c.node()
146
147
147 def kwsub(mobj):
148 def kwsub(mobj):
148 '''Substitutes keyword using corresponding template.'''
149 '''Substitutes keyword using corresponding template.'''
149 kw = mobj.group(1)
150 kw = mobj.group(1)
150 self.ct.use_template(self.templates[kw])
151 self.ct.use_template(self.templates[kw])
151 self.ui.pushbuffer()
152 self.ui.pushbuffer()
152 self.ct.show(changenode=fnode, root=self.repo.root, file=self.path)
153 self.ct.show(changenode=fnode, root=self.repo.root, file=self.path)
153 return '$%s: %s $' % (kw, templater.firstline(self.ui.popbuffer()))
154 return '$%s: %s $' % (kw, templater.firstline(self.ui.popbuffer()))
154
155
155 return subfunc(kwsub, data)
156 return subfunc(kwsub, data)
156
157
157 def expand(self, node, data):
158 def expand(self, node, data):
158 '''Returns data with keywords expanded.'''
159 '''Returns data with keywords expanded.'''
159 if util.binary(data) or _kwrestrict(self.hgcmd):
160 if util.binary(data) or _kwrestrict(self.hgcmd):
160 return data
161 return data
161 return self.substitute(node, data, self.re_kw.sub)
162 return self.substitute(node, data, self.re_kw.sub)
162
163
163 def process(self, node, data, expand):
164 def process(self, node, data, expand):
164 '''Returns a tuple: data, count.
165 '''Returns a tuple: data, count.
165 Count is number of keywords/keyword substitutions,
166 Count is number of keywords/keyword substitutions,
166 telling caller whether to act on file containing data.'''
167 telling caller whether to act on file containing data.'''
167 if util.binary(data):
168 if util.binary(data):
168 return data, None
169 return data, None
169 if expand:
170 if expand:
170 return self.substitute(node, data, self.re_kw.subn)
171 return self.substitute(node, data, self.re_kw.subn)
171 return data, self.re_kw.search(data)
172 return data, self.re_kw.search(data)
172
173
173 def shrink(self, text):
174 def shrink(self, text):
174 '''Returns text with all keyword substitutions removed.'''
175 '''Returns text with all keyword substitutions removed.'''
175 if util.binary(text):
176 if util.binary(text):
176 return text
177 return text
177 return self.re_kw.sub(r'$\1$', text)
178 return self.re_kw.sub(r'$\1$', text)
178
179
179 class kwfilelog(filelog.filelog):
180 class kwfilelog(filelog.filelog):
180 '''
181 '''
181 Subclass of filelog to hook into its read, add, cmp methods.
182 Subclass of filelog to hook into its read, add, cmp methods.
182 Keywords are "stored" unexpanded, and processed on reading.
183 Keywords are "stored" unexpanded, and processed on reading.
183 '''
184 '''
184 def __init__(self, opener, path):
185 def __init__(self, opener, path):
185 super(kwfilelog, self).__init__(opener, path)
186 super(kwfilelog, self).__init__(opener, path)
186 _kwtemplater.path = path
187 _kwtemplater.path = path
187
188
188 def kwctread(self, node, expand):
189 def kwctread(self, node, expand):
189 '''Reads expanding and counting keywords, called from _overwrite.'''
190 '''Reads expanding and counting keywords, called from _overwrite.'''
190 data = super(kwfilelog, self).read(node)
191 data = super(kwfilelog, self).read(node)
191 return _kwtemplater.process(node, data, expand)
192 return _kwtemplater.process(node, data, expand)
192
193
193 def read(self, node):
194 def read(self, node):
194 '''Expands keywords when reading filelog.'''
195 '''Expands keywords when reading filelog.'''
195 data = super(kwfilelog, self).read(node)
196 data = super(kwfilelog, self).read(node)
196 return _kwtemplater.expand(node, data)
197 return _kwtemplater.expand(node, data)
197
198
198 def add(self, text, meta, tr, link, p1=None, p2=None):
199 def add(self, text, meta, tr, link, p1=None, p2=None):
199 '''Removes keyword substitutions when adding to filelog.'''
200 '''Removes keyword substitutions when adding to filelog.'''
200 text = _kwtemplater.shrink(text)
201 text = _kwtemplater.shrink(text)
201 return super(kwfilelog, self).add(text, meta, tr, link, p1=p1, p2=p2)
202 return super(kwfilelog, self).add(text, meta, tr, link, p1=p1, p2=p2)
202
203
203 def cmp(self, node, text):
204 def cmp(self, node, text):
204 '''Removes keyword substitutions for comparison.'''
205 '''Removes keyword substitutions for comparison.'''
205 text = _kwtemplater.shrink(text)
206 text = _kwtemplater.shrink(text)
206 if self.renamed(node):
207 if self.renamed(node):
207 t2 = super(kwfilelog, self).read(node)
208 t2 = super(kwfilelog, self).read(node)
208 return t2 != text
209 return t2 != text
209 return revlog.revlog.cmp(self, node, text)
210 return revlog.revlog.cmp(self, node, text)
210
211
211
212
212 # store original patch.patchfile.__init__
213 # store original patch.patchfile.__init__
213 _patchfile_init = patch.patchfile.__init__
214 _patchfile_init = patch.patchfile.__init__
214
215
215 def _kwpatchfile_init(self, ui, fname, missing=False):
216 def _kwpatchfile_init(self, ui, fname, missing=False):
216 '''Monkeypatch/wrap patch.patchfile.__init__ to avoid
217 '''Monkeypatch/wrap patch.patchfile.__init__ to avoid
217 rejects or conflicts due to expanded keywords in working dir.'''
218 rejects or conflicts due to expanded keywords in working dir.'''
218 _patchfile_init(self, ui, fname, missing=missing)
219 _patchfile_init(self, ui, fname, missing=missing)
219
220
220 if _kwtemplater.matcher(self.fname):
221 if _kwtemplater.matcher(self.fname):
221 # shrink keywords read from working dir
222 # shrink keywords read from working dir
222 kwshrunk = _kwtemplater.shrink(''.join(self.lines))
223 kwshrunk = _kwtemplater.shrink(''.join(self.lines))
223 self.lines = kwshrunk.splitlines(True)
224 self.lines = kwshrunk.splitlines(True)
224
225
225
226
226 def _iskwfile(f, link):
227 def _iskwfile(f, link):
227 return not link(f) and _kwtemplater.matcher(f)
228 return not link(f) and _kwtemplater.matcher(f)
228
229
229 def _status(ui, repo, *pats, **opts):
230 def _status(ui, repo, *pats, **opts):
230 '''Bails out if [keyword] configuration is not active.
231 '''Bails out if [keyword] configuration is not active.
231 Returns status of working directory.'''
232 Returns status of working directory.'''
232 if _kwtemplater:
233 if _kwtemplater:
233 files, match, anypats = cmdutil.matchpats(repo, pats, opts)
234 files, match, anypats = cmdutil.matchpats(repo, pats, opts)
234 return repo.status(files=files, match=match, list_clean=True)
235 return repo.status(files=files, match=match, list_clean=True)
235 if ui.configitems('keyword'):
236 if ui.configitems('keyword'):
236 raise util.Abort(_('[keyword] patterns cannot match'))
237 raise util.Abort(_('[keyword] patterns cannot match'))
237 raise util.Abort(_('no [keyword] patterns configured'))
238 raise util.Abort(_('no [keyword] patterns configured'))
238
239
239 def _overwrite(ui, repo, node=None, expand=True, files=None):
240 def _overwrite(ui, repo, node=None, expand=True, files=None):
240 '''Overwrites selected files expanding/shrinking keywords.'''
241 '''Overwrites selected files expanding/shrinking keywords.'''
241 ctx = repo.changectx(node)
242 ctx = repo.changectx(node)
242 mf = ctx.manifest()
243 mf = ctx.manifest()
243 if node is not None: # commit
244 if node is not None: # commit
244 _kwtemplater.commitnode = node
245 _kwtemplater.commitnode = node
245 files = [f for f in ctx.files() if mf.has_key(f)]
246 files = [f for f in ctx.files() if mf.has_key(f)]
246 notify = ui.debug
247 notify = ui.debug
247 else: # kwexpand/kwshrink
248 else: # kwexpand/kwshrink
248 notify = ui.note
249 notify = ui.note
249 candidates = [f for f in files if _iskwfile(f, mf.linkf)]
250 candidates = [f for f in files if _iskwfile(f, mf.linkf)]
250 if candidates:
251 if candidates:
251 candidates.sort()
252 candidates.sort()
252 action = expand and 'expanding' or 'shrinking'
253 action = expand and 'expanding' or 'shrinking'
253 for f in candidates:
254 for f in candidates:
254 fp = repo.file(f, kwmatch=True)
255 fp = repo.file(f, kwmatch=True)
255 data, kwfound = fp.kwctread(mf[f], expand)
256 data, kwfound = fp.kwctread(mf[f], expand)
256 if kwfound:
257 if kwfound:
257 notify(_('overwriting %s %s keywords\n') % (f, action))
258 notify(_('overwriting %s %s keywords\n') % (f, action))
258 repo.wwrite(f, data, mf.flags(f))
259 repo.wwrite(f, data, mf.flags(f))
259 repo.dirstate.normal(f)
260 repo.dirstate.normal(f)
260
261
261 def _kwfwrite(ui, repo, expand, *pats, **opts):
262 def _kwfwrite(ui, repo, expand, *pats, **opts):
262 '''Selects files and passes them to _overwrite.'''
263 '''Selects files and passes them to _overwrite.'''
263 status = _status(ui, repo, *pats, **opts)
264 status = _status(ui, repo, *pats, **opts)
264 modified, added, removed, deleted, unknown, ignored, clean = status
265 modified, added, removed, deleted, unknown, ignored, clean = status
265 if modified or added or removed or deleted:
266 if modified or added or removed or deleted:
266 raise util.Abort(_('outstanding uncommitted changes in given files'))
267 raise util.Abort(_('outstanding uncommitted changes in given files'))
267 wlock = lock = None
268 wlock = lock = None
268 try:
269 try:
269 wlock = repo.wlock()
270 wlock = repo.wlock()
270 lock = repo.lock()
271 lock = repo.lock()
271 _overwrite(ui, repo, expand=expand, files=clean)
272 _overwrite(ui, repo, expand=expand, files=clean)
272 finally:
273 finally:
273 del wlock, lock
274 del wlock, lock
274
275
275
276
276 def demo(ui, repo, *args, **opts):
277 def demo(ui, repo, *args, **opts):
277 '''print [keywordmaps] configuration and an expansion example
278 '''print [keywordmaps] configuration and an expansion example
278
279
279 Show current, custom, or default keyword template maps
280 Show current, custom, or default keyword template maps
280 and their expansion.
281 and their expansion.
281
282
282 Extend current configuration by specifying maps as arguments
283 Extend current configuration by specifying maps as arguments
283 and optionally by reading from an additional hgrc file.
284 and optionally by reading from an additional hgrc file.
284
285
285 Override current keyword template maps with "default" option.
286 Override current keyword template maps with "default" option.
286 '''
287 '''
287 def demostatus(stat):
288 def demostatus(stat):
288 ui.status(_('\n\t%s\n') % stat)
289 ui.status(_('\n\t%s\n') % stat)
289
290
290 def demoitems(section, items):
291 def demoitems(section, items):
291 ui.write('[%s]\n' % section)
292 ui.write('[%s]\n' % section)
292 for k, v in items:
293 for k, v in items:
293 ui.write('%s = %s\n' % (k, v))
294 ui.write('%s = %s\n' % (k, v))
294
295
295 msg = 'hg keyword config and expansion example'
296 msg = 'hg keyword config and expansion example'
296 kwstatus = 'current'
297 kwstatus = 'current'
297 fn = 'demo.txt'
298 fn = 'demo.txt'
298 branchname = 'demobranch'
299 branchname = 'demobranch'
299 tmpdir = tempfile.mkdtemp('', 'kwdemo.')
300 tmpdir = tempfile.mkdtemp('', 'kwdemo.')
300 ui.note(_('creating temporary repo at %s\n') % tmpdir)
301 ui.note(_('creating temporary repo at %s\n') % tmpdir)
301 repo = localrepo.localrepository(ui, path=tmpdir, create=True)
302 repo = localrepo.localrepository(ui, path=tmpdir, create=True)
302 ui.setconfig('keyword', fn, '')
303 ui.setconfig('keyword', fn, '')
303 if args or opts.get('rcfile'):
304 if args or opts.get('rcfile'):
304 kwstatus = 'custom'
305 kwstatus = 'custom'
305 if opts.get('rcfile'):
306 if opts.get('rcfile'):
306 ui.readconfig(opts.get('rcfile'))
307 ui.readconfig(opts.get('rcfile'))
307 if opts.get('default'):
308 if opts.get('default'):
308 kwstatus = 'default'
309 kwstatus = 'default'
309 kwmaps = kwtemplater.templates
310 kwmaps = kwtemplater.templates
310 if ui.configitems('keywordmaps'):
311 if ui.configitems('keywordmaps'):
311 # override maps from optional rcfile
312 # override maps from optional rcfile
312 for k, v in kwmaps.items():
313 for k, v in kwmaps.items():
313 ui.setconfig('keywordmaps', k, v)
314 ui.setconfig('keywordmaps', k, v)
314 elif args:
315 elif args:
315 # simulate hgrc parsing
316 # simulate hgrc parsing
316 rcmaps = ['[keywordmaps]\n'] + [a + '\n' for a in args]
317 rcmaps = ['[keywordmaps]\n'] + [a + '\n' for a in args]
317 fp = repo.opener('hgrc', 'w')
318 fp = repo.opener('hgrc', 'w')
318 fp.writelines(rcmaps)
319 fp.writelines(rcmaps)
319 fp.close()
320 fp.close()
320 ui.readconfig(repo.join('hgrc'))
321 ui.readconfig(repo.join('hgrc'))
321 if not opts.get('default'):
322 if not opts.get('default'):
322 kwmaps = dict(ui.configitems('keywordmaps')) or kwtemplater.templates
323 kwmaps = dict(ui.configitems('keywordmaps')) or kwtemplater.templates
323 reposetup(ui, repo)
324 reposetup(ui, repo)
324 for k, v in ui.configitems('extensions'):
325 for k, v in ui.configitems('extensions'):
325 if k.endswith('keyword'):
326 if k.endswith('keyword'):
326 extension = '%s = %s' % (k, v)
327 extension = '%s = %s' % (k, v)
327 break
328 break
328 demostatus('config using %s keyword template maps' % kwstatus)
329 demostatus('config using %s keyword template maps' % kwstatus)
329 ui.write('[extensions]\n%s\n' % extension)
330 ui.write('[extensions]\n%s\n' % extension)
330 demoitems('keyword', ui.configitems('keyword'))
331 demoitems('keyword', ui.configitems('keyword'))
331 demoitems('keywordmaps', kwmaps.items())
332 demoitems('keywordmaps', kwmaps.items())
332 keywords = '$' + '$\n$'.join(kwmaps.keys()) + '$\n'
333 keywords = '$' + '$\n$'.join(kwmaps.keys()) + '$\n'
333 repo.wopener(fn, 'w').write(keywords)
334 repo.wopener(fn, 'w').write(keywords)
334 repo.add([fn])
335 repo.add([fn])
335 path = repo.wjoin(fn)
336 path = repo.wjoin(fn)
336 ui.note(_('\n%s keywords written to %s:\n') % (kwstatus, path))
337 ui.note(_('\n%s keywords written to %s:\n') % (kwstatus, path))
337 ui.note(keywords)
338 ui.note(keywords)
338 ui.note('\nhg -R "%s" branch "%s"\n' % (tmpdir, branchname))
339 ui.note('\nhg -R "%s" branch "%s"\n' % (tmpdir, branchname))
339 # silence branch command if not verbose
340 # silence branch command if not verbose
340 quiet = ui.quiet
341 quiet = ui.quiet
341 ui.quiet = not ui.verbose
342 ui.quiet = not ui.verbose
342 commands.branch(ui, repo, branchname)
343 commands.branch(ui, repo, branchname)
343 ui.quiet = quiet
344 ui.quiet = quiet
344 for name, cmd in ui.configitems('hooks'):
345 for name, cmd in ui.configitems('hooks'):
345 if name.split('.', 1)[0].find('commit') > -1:
346 if name.split('.', 1)[0].find('commit') > -1:
346 repo.ui.setconfig('hooks', name, '')
347 repo.ui.setconfig('hooks', name, '')
347 ui.note(_('unhooked all commit hooks\n'))
348 ui.note(_('unhooked all commit hooks\n'))
348 ui.note('hg -R "%s" ci -m "%s"\n' % (tmpdir, msg))
349 ui.note('hg -R "%s" ci -m "%s"\n' % (tmpdir, msg))
349 repo.commit(text=msg)
350 repo.commit(text=msg)
350 format = ui.verbose and ' in %s' % path or ''
351 format = ui.verbose and ' in %s' % path or ''
351 demostatus('%s keywords expanded%s' % (kwstatus, format))
352 demostatus('%s keywords expanded%s' % (kwstatus, format))
352 ui.write(repo.wread(fn))
353 ui.write(repo.wread(fn))
353 ui.debug(_('\nremoving temporary repo %s\n') % tmpdir)
354 ui.debug(_('\nremoving temporary repo %s\n') % tmpdir)
354 shutil.rmtree(tmpdir, ignore_errors=True)
355 shutil.rmtree(tmpdir, ignore_errors=True)
355
356
356 def expand(ui, repo, *pats, **opts):
357 def expand(ui, repo, *pats, **opts):
357 '''expand keywords in working directory
358 '''expand keywords in working directory
358
359
359 Run after (re)enabling keyword expansion.
360 Run after (re)enabling keyword expansion.
360
361
361 kwexpand refuses to run if given files contain local changes.
362 kwexpand refuses to run if given files contain local changes.
362 '''
363 '''
363 # 3rd argument sets expansion to True
364 # 3rd argument sets expansion to True
364 _kwfwrite(ui, repo, True, *pats, **opts)
365 _kwfwrite(ui, repo, True, *pats, **opts)
365
366
366 def files(ui, repo, *pats, **opts):
367 def files(ui, repo, *pats, **opts):
367 '''print files currently configured for keyword expansion
368 '''print files currently configured for keyword expansion
368
369
369 Crosscheck which files in working directory are potential targets for
370 Crosscheck which files in working directory are potential targets for
370 keyword expansion.
371 keyword expansion.
371 That is, files matched by [keyword] config patterns but not symlinks.
372 That is, files matched by [keyword] config patterns but not symlinks.
372 '''
373 '''
373 status = _status(ui, repo, *pats, **opts)
374 status = _status(ui, repo, *pats, **opts)
374 modified, added, removed, deleted, unknown, ignored, clean = status
375 modified, added, removed, deleted, unknown, ignored, clean = status
375 files = modified + added + clean
376 files = modified + added + clean
376 if opts.get('untracked'):
377 if opts.get('untracked'):
377 files += unknown
378 files += unknown
378 files.sort()
379 files.sort()
379 kwfiles = [f for f in files if _iskwfile(f, repo._link)]
380 kwfiles = [f for f in files if _iskwfile(f, repo._link)]
380 cwd = pats and repo.getcwd() or ''
381 cwd = pats and repo.getcwd() or ''
381 kwfstats = not opts.get('ignore') and (('K', kwfiles),) or ()
382 kwfstats = not opts.get('ignore') and (('K', kwfiles),) or ()
382 if opts.get('all') or opts.get('ignore'):
383 if opts.get('all') or opts.get('ignore'):
383 kwfstats += (('I', [f for f in files if f not in kwfiles]),)
384 kwfstats += (('I', [f for f in files if f not in kwfiles]),)
384 for char, filenames in kwfstats:
385 for char, filenames in kwfstats:
385 format = (opts.get('all') or ui.verbose) and '%s %%s\n' % char or '%s\n'
386 format = (opts.get('all') or ui.verbose) and '%s %%s\n' % char or '%s\n'
386 for f in filenames:
387 for f in filenames:
387 ui.write(format % repo.pathto(f, cwd))
388 ui.write(format % repo.pathto(f, cwd))
388
389
389 def shrink(ui, repo, *pats, **opts):
390 def shrink(ui, repo, *pats, **opts):
390 '''revert expanded keywords in working directory
391 '''revert expanded keywords in working directory
391
392
392 Run before changing/disabling active keywords
393 Run before changing/disabling active keywords
393 or if you experience problems with "hg import" or "hg merge".
394 or if you experience problems with "hg import" or "hg merge".
394
395
395 kwshrink refuses to run if given files contain local changes.
396 kwshrink refuses to run if given files contain local changes.
396 '''
397 '''
397 # 3rd argument sets expansion to False
398 # 3rd argument sets expansion to False
398 _kwfwrite(ui, repo, False, *pats, **opts)
399 _kwfwrite(ui, repo, False, *pats, **opts)
399
400
400
401
401 def reposetup(ui, repo):
402 def reposetup(ui, repo):
402 '''Sets up repo as kwrepo for keyword substitution.
403 '''Sets up repo as kwrepo for keyword substitution.
403 Overrides file method to return kwfilelog instead of filelog
404 Overrides file method to return kwfilelog instead of filelog
404 if file matches user configuration.
405 if file matches user configuration.
405 Wraps commit to overwrite configured files with updated
406 Wraps commit to overwrite configured files with updated
406 keyword substitutions.
407 keyword substitutions.
407 This is done for local repos only, and only if there are
408 This is done for local repos only, and only if there are
408 files configured at all for keyword substitution.'''
409 files configured at all for keyword substitution.'''
409
410
410 if not repo.local():
411 if not repo.local():
411 return
412 return
412
413
413 nokwcommands = ('add', 'addremove', 'bundle', 'clone', 'copy',
414 nokwcommands = ('add', 'addremove', 'bundle', 'clone', 'copy',
414 'export', 'grep', 'identify', 'incoming', 'init',
415 'export', 'grep', 'identify', 'incoming', 'init',
415 'log', 'outgoing', 'push', 'remove', 'rename',
416 'log', 'outgoing', 'push', 'remove', 'rename',
416 'rollback', 'tip',
417 'rollback', 'tip',
417 'convert')
418 'convert')
418 hgcmd, func, args, opts, cmdopts = dispatch._parse(ui, sys.argv[1:])
419 hgcmd, func, args, opts, cmdopts = dispatch._parse(ui, sys.argv[1:])
419 if hgcmd in nokwcommands:
420 if hgcmd in nokwcommands:
420 return
421 return
421
422
423 if hgcmd == 'diff':
424 # only expand if comparing against working dir
425 node1, node2 = cmdutil.revpair(repo, cmdopts.get('rev'))
426 if node2 is not None:
427 return
428 # shrink if rev is not current node
429 if node1 is not None and node1 != repo.changectx().node():
430 hgcmd = 'diff1'
431
422 inc, exc = [], ['.hgtags']
432 inc, exc = [], ['.hgtags']
423 for pat, opt in ui.configitems('keyword'):
433 for pat, opt in ui.configitems('keyword'):
424 if opt != 'ignore':
434 if opt != 'ignore':
425 inc.append(pat)
435 inc.append(pat)
426 else:
436 else:
427 exc.append(pat)
437 exc.append(pat)
428 if not inc:
438 if not inc:
429 return
439 return
430
440
431 global _kwtemplater
441 global _kwtemplater
432 _kwtemplater = kwtemplater(ui, repo, inc, exc, hgcmd)
442 _kwtemplater = kwtemplater(ui, repo, inc, exc, hgcmd)
433
443
434 class kwrepo(repo.__class__):
444 class kwrepo(repo.__class__):
435 def file(self, f, kwmatch=False):
445 def file(self, f, kwmatch=False):
436 if f[0] == '/':
446 if f[0] == '/':
437 f = f[1:]
447 f = f[1:]
438 if kwmatch or _kwtemplater.matcher(f):
448 if kwmatch or _kwtemplater.matcher(f):
439 return kwfilelog(self.sopener, f)
449 return kwfilelog(self.sopener, f)
440 return filelog.filelog(self.sopener, f)
450 return filelog.filelog(self.sopener, f)
441
451
442 def wread(self, filename):
452 def wread(self, filename):
443 data = super(kwrepo, self).wread(filename)
453 data = super(kwrepo, self).wread(filename)
444 if _kwrestrict(hgcmd) and _kwtemplater.matcher(filename):
454 if _kwrestrict(hgcmd) and _kwtemplater.matcher(filename):
445 return _kwtemplater.shrink(data)
455 return _kwtemplater.shrink(data)
446 return data
456 return data
447
457
448 def commit(self, files=None, text='', user=None, date=None,
458 def commit(self, files=None, text='', user=None, date=None,
449 match=util.always, force=False, force_editor=False,
459 match=util.always, force=False, force_editor=False,
450 p1=None, p2=None, extra={}):
460 p1=None, p2=None, extra={}):
451 wlock = lock = None
461 wlock = lock = None
452 _p1 = _p2 = None
462 _p1 = _p2 = None
453 try:
463 try:
454 wlock = self.wlock()
464 wlock = self.wlock()
455 lock = self.lock()
465 lock = self.lock()
456 # store and postpone commit hooks
466 # store and postpone commit hooks
457 commithooks = []
467 commithooks = []
458 for name, cmd in ui.configitems('hooks'):
468 for name, cmd in ui.configitems('hooks'):
459 if name.split('.', 1)[0] == 'commit':
469 if name.split('.', 1)[0] == 'commit':
460 commithooks.append((name, cmd))
470 commithooks.append((name, cmd))
461 ui.setconfig('hooks', name, None)
471 ui.setconfig('hooks', name, None)
462 if commithooks:
472 if commithooks:
463 # store parents for commit hook environment
473 # store parents for commit hook environment
464 if p1 is None:
474 if p1 is None:
465 _p1, _p2 = repo.dirstate.parents()
475 _p1, _p2 = repo.dirstate.parents()
466 else:
476 else:
467 _p1, _p2 = p1, p2 or nullid
477 _p1, _p2 = p1, p2 or nullid
468 _p1 = hex(_p1)
478 _p1 = hex(_p1)
469 if _p2 == nullid:
479 if _p2 == nullid:
470 _p2 = ''
480 _p2 = ''
471 else:
481 else:
472 _p2 = hex(_p2)
482 _p2 = hex(_p2)
473
483
474 node = super(kwrepo,
484 node = super(kwrepo,
475 self).commit(files=files, text=text, user=user,
485 self).commit(files=files, text=text, user=user,
476 date=date, match=match, force=force,
486 date=date, match=match, force=force,
477 force_editor=force_editor,
487 force_editor=force_editor,
478 p1=p1, p2=p2, extra=extra)
488 p1=p1, p2=p2, extra=extra)
479
489
480 # restore commit hooks
490 # restore commit hooks
481 for name, cmd in commithooks:
491 for name, cmd in commithooks:
482 ui.setconfig('hooks', name, cmd)
492 ui.setconfig('hooks', name, cmd)
483 if node is not None:
493 if node is not None:
484 _overwrite(ui, self, node=node)
494 _overwrite(ui, self, node=node)
485 repo.hook('commit', node=node, parent1=_p1, parent2=_p2)
495 repo.hook('commit', node=node, parent1=_p1, parent2=_p2)
486 return node
496 return node
487 finally:
497 finally:
488 del wlock, lock
498 del wlock, lock
489
499
490 repo.__class__ = kwrepo
500 repo.__class__ = kwrepo
491 patch.patchfile.__init__ = _kwpatchfile_init
501 patch.patchfile.__init__ = _kwpatchfile_init
492
502
493
503
494 cmdtable = {
504 cmdtable = {
495 'kwdemo':
505 'kwdemo':
496 (demo,
506 (demo,
497 [('d', 'default', None, _('show default keyword template maps')),
507 [('d', 'default', None, _('show default keyword template maps')),
498 ('f', 'rcfile', [], _('read maps from rcfile'))],
508 ('f', 'rcfile', [], _('read maps from rcfile'))],
499 _('hg kwdemo [-d] [-f RCFILE] [TEMPLATEMAP]...')),
509 _('hg kwdemo [-d] [-f RCFILE] [TEMPLATEMAP]...')),
500 'kwexpand': (expand, commands.walkopts,
510 'kwexpand': (expand, commands.walkopts,
501 _('hg kwexpand [OPTION]... [FILE]...')),
511 _('hg kwexpand [OPTION]... [FILE]...')),
502 'kwfiles':
512 'kwfiles':
503 (files,
513 (files,
504 [('a', 'all', None, _('show keyword status flags of all files')),
514 [('a', 'all', None, _('show keyword status flags of all files')),
505 ('i', 'ignore', None, _('show files excluded from expansion')),
515 ('i', 'ignore', None, _('show files excluded from expansion')),
506 ('u', 'untracked', None, _('additionally show untracked files')),
516 ('u', 'untracked', None, _('additionally show untracked files')),
507 ] + commands.walkopts,
517 ] + commands.walkopts,
508 _('hg kwfiles [OPTION]... [FILE]...')),
518 _('hg kwfiles [OPTION]... [FILE]...')),
509 'kwshrink': (shrink, commands.walkopts,
519 'kwshrink': (shrink, commands.walkopts,
510 _('hg kwshrink [OPTION]... [FILE]...')),
520 _('hg kwshrink [OPTION]... [FILE]...')),
511 }
521 }
@@ -1,362 +1,362 b''
1 % help
1 % help
2 keyword extension - keyword expansion in local repositories
2 keyword extension - keyword expansion in local repositories
3
3
4 This extension expands RCS/CVS-like or self-customized $Keywords$
4 This extension expands RCS/CVS-like or self-customized $Keywords$
5 in tracked text files selected by your configuration.
5 in tracked text files selected by your configuration.
6
6
7 Keywords are only expanded in local repositories and not stored in
7 Keywords are only expanded in local repositories and not stored in
8 the change history. The mechanism can be regarded as a convenience
8 the change history. The mechanism can be regarded as a convenience
9 for the current user or for archive distribution.
9 for the current user or for archive distribution.
10
10
11 Configuration is done in the [keyword] and [keywordmaps] sections
11 Configuration is done in the [keyword] and [keywordmaps] sections
12 of hgrc files.
12 of hgrc files.
13
13
14 Example:
14 Example:
15
15
16 [keyword]
16 [keyword]
17 # expand keywords in every python file except those matching "x*"
17 # expand keywords in every python file except those matching "x*"
18 **.py =
18 **.py =
19 x* = ignore
19 x* = ignore
20
20
21 Note: the more specific you are in your filename patterns
21 Note: the more specific you are in your filename patterns
22 the less you lose speed in huge repos.
22 the less you lose speed in huge repos.
23
23
24 For [keywordmaps] template mapping and expansion demonstration and
24 For [keywordmaps] template mapping and expansion demonstration and
25 control run "hg kwdemo".
25 control run "hg kwdemo".
26
26
27 An additional date template filter {date|utcdate} is provided.
27 An additional date template filter {date|utcdate} is provided.
28
28
29 The default template mappings (view with "hg kwdemo -d") can be replaced
29 The default template mappings (view with "hg kwdemo -d") can be replaced
30 with customized keywords and templates.
30 with customized keywords and templates.
31 Again, run "hg kwdemo" to control the results of your config changes.
31 Again, run "hg kwdemo" to control the results of your config changes.
32
32
33 Before changing/disabling active keywords, run "hg kwshrink" to avoid
33 Before changing/disabling active keywords, run "hg kwshrink" to avoid
34 the risk of inadvertedly storing expanded keywords in the change history.
34 the risk of inadvertedly storing expanded keywords in the change history.
35
35
36 To force expansion after enabling it, or a configuration change, run
36 To force expansion after enabling it, or a configuration change, run
37 "hg kwexpand".
37 "hg kwexpand".
38
38
39 Also, when committing with the record extension or using mq's qrecord, be aware
39 Also, when committing with the record extension or using mq's qrecord, be aware
40 that keywords cannot be updated. Again, run "hg kwexpand" on the files in
40 that keywords cannot be updated. Again, run "hg kwexpand" on the files in
41 question to update keyword expansions after all changes have been checked in.
41 question to update keyword expansions after all changes have been checked in.
42
42
43 Expansions spanning more than one line and incremental expansions,
43 Expansions spanning more than one line and incremental expansions,
44 like CVS' $Log$, are not supported. A keyword template map
44 like CVS' $Log$, are not supported. A keyword template map
45 "Log = {desc}" expands to the first line of the changeset description.
45 "Log = {desc}" expands to the first line of the changeset description.
46
46
47 list of commands:
47 list of commands:
48
48
49 kwdemo print [keywordmaps] configuration and an expansion example
49 kwdemo print [keywordmaps] configuration and an expansion example
50 kwexpand expand keywords in working directory
50 kwexpand expand keywords in working directory
51 kwfiles print files currently configured for keyword expansion
51 kwfiles print files currently configured for keyword expansion
52 kwshrink revert expanded keywords in working directory
52 kwshrink revert expanded keywords in working directory
53
53
54 use "hg -v help keyword" to show aliases and global options
54 use "hg -v help keyword" to show aliases and global options
55 % hg kwdemo
55 % hg kwdemo
56 [extensions]
56 [extensions]
57 hgext.keyword =
57 hgext.keyword =
58 [keyword]
58 [keyword]
59 * =
59 * =
60 b = ignore
60 b = ignore
61 demo.txt =
61 demo.txt =
62 [keywordmaps]
62 [keywordmaps]
63 RCSFile = {file|basename},v
63 RCSFile = {file|basename},v
64 Author = {author|user}
64 Author = {author|user}
65 Header = {root}/{file},v {node|short} {date|utcdate} {author|user}
65 Header = {root}/{file},v {node|short} {date|utcdate} {author|user}
66 Source = {root}/{file},v
66 Source = {root}/{file},v
67 Date = {date|utcdate}
67 Date = {date|utcdate}
68 Id = {file|basename},v {node|short} {date|utcdate} {author|user}
68 Id = {file|basename},v {node|short} {date|utcdate} {author|user}
69 Revision = {node|short}
69 Revision = {node|short}
70 $RCSFile: demo.txt,v $
70 $RCSFile: demo.txt,v $
71 $Author: test $
71 $Author: test $
72 $Header: /TMP/demo.txt,v xxxxxxxxxxxx 2000/00/00 00:00:00 test $
72 $Header: /TMP/demo.txt,v xxxxxxxxxxxx 2000/00/00 00:00:00 test $
73 $Source: /TMP/demo.txt,v $
73 $Source: /TMP/demo.txt,v $
74 $Date: 2000/00/00 00:00:00 $
74 $Date: 2000/00/00 00:00:00 $
75 $Id: demo.txt,v xxxxxxxxxxxx 2000/00/00 00:00:00 test $
75 $Id: demo.txt,v xxxxxxxxxxxx 2000/00/00 00:00:00 test $
76 $Revision: xxxxxxxxxxxx $
76 $Revision: xxxxxxxxxxxx $
77 [extensions]
77 [extensions]
78 hgext.keyword =
78 hgext.keyword =
79 [keyword]
79 [keyword]
80 * =
80 * =
81 b = ignore
81 b = ignore
82 demo.txt =
82 demo.txt =
83 [keywordmaps]
83 [keywordmaps]
84 Branch = {branches}
84 Branch = {branches}
85 $Branch: demobranch $
85 $Branch: demobranch $
86 % kwshrink should exit silently in empty/invalid repo
86 % kwshrink should exit silently in empty/invalid repo
87 % cat
87 % cat
88 expand $Id$
88 expand $Id$
89 do not process $Id:
89 do not process $Id:
90 xxx $
90 xxx $
91 expand $Id$
91 expand $Id$
92 do not process $Id:
92 do not process $Id:
93 xxx $
93 xxx $
94 ignore $Id$
94 ignore $Id$
95 % addremove
95 % addremove
96 adding a
96 adding a
97 adding b
97 adding b
98 adding sym
98 adding sym
99 % status
99 % status
100 A a
100 A a
101 A b
101 A b
102 A sym
102 A sym
103 % default keyword expansion including commit hook
103 % default keyword expansion including commit hook
104 % interrupted commit should not change state or run commit hook
104 % interrupted commit should not change state or run commit hook
105 a
105 a
106 b
106 b
107 sym
107 sym
108 transaction abort!
108 transaction abort!
109 rollback completed
109 rollback completed
110 abort: empty commit message
110 abort: empty commit message
111 % status
111 % status
112 A a
112 A a
113 A b
113 A b
114 A sym
114 A sym
115 % commit
115 % commit
116 a
116 a
117 b
117 b
118 sym
118 sym
119 overwriting a expanding keywords
119 overwriting a expanding keywords
120 running hook commit.test: cp a hooktest
120 running hook commit.test: cp a hooktest
121 % status
121 % status
122 ? hooktest
122 ? hooktest
123 % identify
123 % identify
124 f782df5f9602
124 f782df5f9602
125 % cat
125 % cat
126 expand $Id: a,v f782df5f9602 1970/01/01 00:00:00 user $
126 expand $Id: a,v f782df5f9602 1970/01/01 00:00:00 user $
127 do not process $Id:
127 do not process $Id:
128 xxx $
128 xxx $
129 expand $Id: a,v f782df5f9602 1970/01/01 00:00:00 user $
129 expand $Id: a,v f782df5f9602 1970/01/01 00:00:00 user $
130 do not process $Id:
130 do not process $Id:
131 xxx $
131 xxx $
132 ignore $Id$
132 ignore $Id$
133 % hg cat
133 % hg cat
134 expand $Id: a,v f782df5f9602 1970/01/01 00:00:00 user $
134 expand $Id: a,v f782df5f9602 1970/01/01 00:00:00 user $
135 do not process $Id:
135 do not process $Id:
136 xxx $
136 xxx $
137 ignore $Id$
137 ignore $Id$
138 a
138 a
139 % diff a hooktest
139 % diff a hooktest
140 % removing commit hook from config
140 % removing commit hook from config
141 % touch
141 % touch
142 % status
142 % status
143 % update
143 % update
144 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
144 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
145 % cat
145 % cat
146 expand $Id: a,v f782df5f9602 1970/01/01 00:00:00 user $
146 expand $Id: a,v f782df5f9602 1970/01/01 00:00:00 user $
147 do not process $Id:
147 do not process $Id:
148 xxx $
148 xxx $
149 expand $Id: a,v f782df5f9602 1970/01/01 00:00:00 user $
149 expand $Id: a,v f782df5f9602 1970/01/01 00:00:00 user $
150 do not process $Id:
150 do not process $Id:
151 xxx $
151 xxx $
152 ignore $Id$
152 ignore $Id$
153 % check whether expansion is filewise
153 % check whether expansion is filewise
154 % commit c
154 % commit c
155 adding c
155 adding c
156 % force expansion
156 % force expansion
157 overwriting a expanding keywords
157 overwriting a expanding keywords
158 overwriting c expanding keywords
158 overwriting c expanding keywords
159 % compare changenodes in a c
159 % compare changenodes in a c
160 expand $Id: a,v f782df5f9602 1970/01/01 00:00:00 user $
160 expand $Id: a,v f782df5f9602 1970/01/01 00:00:00 user $
161 do not process $Id:
161 do not process $Id:
162 xxx $
162 xxx $
163 $Id: c,v ba4426d1938e 1970/01/01 00:00:01 user $
163 $Id: c,v ba4426d1938e 1970/01/01 00:00:01 user $
164 tests for different changenodes
164 tests for different changenodes
165 % rollback and remove c
165 % rollback and remove c
166 rolling back last transaction
166 rolling back last transaction
167 % copy
167 % copy
168 % kwfiles added
168 % kwfiles added
169 a
169 a
170 c
170 c
171 % commit
171 % commit
172 c
172 c
173 c: copy a:0045e12f6c5791aac80ca6cbfd97709a88307292
173 c: copy a:0045e12f6c5791aac80ca6cbfd97709a88307292
174 overwriting c expanding keywords
174 overwriting c expanding keywords
175 % cat a c
175 % cat a c
176 expand $Id: a,v f782df5f9602 1970/01/01 00:00:00 user $
176 expand $Id: a,v f782df5f9602 1970/01/01 00:00:00 user $
177 do not process $Id:
177 do not process $Id:
178 xxx $
178 xxx $
179 expand $Id: c,v 0ba462c0f077 1970/01/01 00:00:01 user $
179 expand $Id: c,v 0ba462c0f077 1970/01/01 00:00:01 user $
180 do not process $Id:
180 do not process $Id:
181 xxx $
181 xxx $
182 % touch copied c after 1 second
182 % touch copied c after 1 second
183 % status
183 % status
184 % kwfiles
184 % kwfiles
185 a
185 a
186 c
186 c
187 % diff --rev
187 % diff --rev
188 diff -r f782df5f9602 c
188 diff -r f782df5f9602 c
189 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
189 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
190 @@ -0,0 +1,3 @@
190 @@ -0,0 +1,3 @@
191 +expand $Id: c,v 0ba462c0f077 1970/01/01 00:00:01 user $
191 +expand $Id$
192 +do not process $Id:
192 +do not process $Id:
193 +xxx $
193 +xxx $
194 % rollback
194 % rollback
195 rolling back last transaction
195 rolling back last transaction
196 % status
196 % status
197 A c
197 A c
198 % update -C
198 % update -C
199 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
199 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
200 % custom keyword expansion
200 % custom keyword expansion
201 % try with kwdemo
201 % try with kwdemo
202 [extensions]
202 [extensions]
203 hgext.keyword =
203 hgext.keyword =
204 [keyword]
204 [keyword]
205 * =
205 * =
206 b = ignore
206 b = ignore
207 demo.txt =
207 demo.txt =
208 [keywordmaps]
208 [keywordmaps]
209 Xinfo = {author}: {desc}
209 Xinfo = {author}: {desc}
210 $Xinfo: test: hg keyword config and expansion example $
210 $Xinfo: test: hg keyword config and expansion example $
211 % cat
211 % cat
212 expand $Id: a,v f782df5f9602 1970/01/01 00:00:00 user $
212 expand $Id: a,v f782df5f9602 1970/01/01 00:00:00 user $
213 do not process $Id:
213 do not process $Id:
214 xxx $
214 xxx $
215 expand $Id: a,v f782df5f9602 1970/01/01 00:00:00 user $
215 expand $Id: a,v f782df5f9602 1970/01/01 00:00:00 user $
216 do not process $Id:
216 do not process $Id:
217 xxx $
217 xxx $
218 ignore $Id$
218 ignore $Id$
219 % hg cat
219 % hg cat
220 expand $Id: a f782df5f9602 Thu, 01 Jan 1970 00:00:00 +0000 user $
220 expand $Id: a f782df5f9602 Thu, 01 Jan 1970 00:00:00 +0000 user $
221 do not process $Id:
221 do not process $Id:
222 xxx $
222 xxx $
223 ignore $Id$
223 ignore $Id$
224 a
224 a
225 % interrupted commit should not change state
225 % interrupted commit should not change state
226 transaction abort!
226 transaction abort!
227 rollback completed
227 rollback completed
228 abort: empty commit message
228 abort: empty commit message
229 % status
229 % status
230 M a
230 M a
231 ? log
231 ? log
232 % commit
232 % commit
233 a
233 a
234 overwriting a expanding keywords
234 overwriting a expanding keywords
235 % status
235 % status
236 % cat
236 % cat
237 expand $Id: a 0729690beff6 Thu, 01 Jan 1970 00:00:02 +0000 user $
237 expand $Id: a 0729690beff6 Thu, 01 Jan 1970 00:00:02 +0000 user $
238 do not process $Id:
238 do not process $Id:
239 xxx $
239 xxx $
240 $Xinfo: User Name <user@example.com>: firstline $
240 $Xinfo: User Name <user@example.com>: firstline $
241 expand $Id: a 0729690beff6 Thu, 01 Jan 1970 00:00:02 +0000 user $
241 expand $Id: a 0729690beff6 Thu, 01 Jan 1970 00:00:02 +0000 user $
242 do not process $Id:
242 do not process $Id:
243 xxx $
243 xxx $
244 $Xinfo: User Name <user@example.com>: firstline $
244 $Xinfo: User Name <user@example.com>: firstline $
245 ignore $Id$
245 ignore $Id$
246 % hg cat
246 % hg cat
247 expand $Id: a 0729690beff6 Thu, 01 Jan 1970 00:00:02 +0000 user $
247 expand $Id: a 0729690beff6 Thu, 01 Jan 1970 00:00:02 +0000 user $
248 do not process $Id:
248 do not process $Id:
249 xxx $
249 xxx $
250 $Xinfo: User Name <user@example.com>: firstline $
250 $Xinfo: User Name <user@example.com>: firstline $
251 ignore $Id$
251 ignore $Id$
252 a
252 a
253 % remove
253 % remove
254 % status
254 % status
255 % rollback
255 % rollback
256 rolling back last transaction
256 rolling back last transaction
257 % status
257 % status
258 R a
258 R a
259 % revert a
259 % revert a
260 % cat a
260 % cat a
261 expand $Id: a 0729690beff6 Thu, 01 Jan 1970 00:00:02 +0000 user $
261 expand $Id: a 0729690beff6 Thu, 01 Jan 1970 00:00:02 +0000 user $
262 do not process $Id:
262 do not process $Id:
263 xxx $
263 xxx $
264 $Xinfo: User Name <user@example.com>: firstline $
264 $Xinfo: User Name <user@example.com>: firstline $
265 % clone to test incoming
265 % clone to test incoming
266 requesting all changes
266 requesting all changes
267 adding changesets
267 adding changesets
268 adding manifests
268 adding manifests
269 adding file changes
269 adding file changes
270 added 1 changesets with 3 changes to 3 files
270 added 1 changesets with 3 changes to 3 files
271 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
271 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
272 % incoming
272 % incoming
273 comparing with test-keyword/Test-a/../Test
273 comparing with test-keyword/Test-a/../Test
274 searching for changes
274 searching for changes
275 changeset: 1:0729690beff6
275 changeset: 1:0729690beff6
276 tag: tip
276 tag: tip
277 user: User Name <user@example.com>
277 user: User Name <user@example.com>
278 date: Thu Jan 01 00:00:02 1970 +0000
278 date: Thu Jan 01 00:00:02 1970 +0000
279 summary: firstline
279 summary: firstline
280
280
281 % commit rejecttest
281 % commit rejecttest
282 a
282 a
283 overwriting a expanding keywords
283 overwriting a expanding keywords
284 % export
284 % export
285 % import
285 % import
286 applying ../rejecttest.diff
286 applying ../rejecttest.diff
287 % cat
287 % cat
288 expand $Id: a 82983f13f138 Thu, 01 Jan 1970 00:00:03 +0000 user $ rejecttest
288 expand $Id: a 82983f13f138 Thu, 01 Jan 1970 00:00:03 +0000 user $ rejecttest
289 do not process $Id: rejecttest
289 do not process $Id: rejecttest
290 xxx $
290 xxx $
291 $Xinfo: User Name <user@example.com>: rejects? $
291 $Xinfo: User Name <user@example.com>: rejects? $
292 expand $Id: a 82983f13f138 Thu, 01 Jan 1970 00:00:03 +0000 user $ rejecttest
292 expand $Id: a 82983f13f138 Thu, 01 Jan 1970 00:00:03 +0000 user $ rejecttest
293 do not process $Id: rejecttest
293 do not process $Id: rejecttest
294 xxx $
294 xxx $
295 $Xinfo: User Name <user@example.com>: rejects? $
295 $Xinfo: User Name <user@example.com>: rejects? $
296 ignore $Id$
296 ignore $Id$
297
297
298 % rollback
298 % rollback
299 rolling back last transaction
299 rolling back last transaction
300 % clean update
300 % clean update
301 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
301 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
302 % kwexpand/kwshrink on selected files
302 % kwexpand/kwshrink on selected files
303 % copy a x/a
303 % copy a x/a
304 % kwexpand a
304 % kwexpand a
305 overwriting a expanding keywords
305 overwriting a expanding keywords
306 % kwexpand x/a should abort
306 % kwexpand x/a should abort
307 abort: outstanding uncommitted changes in given files
307 abort: outstanding uncommitted changes in given files
308 x/a
308 x/a
309 x/a: copy a:779c764182ce5d43e2b1eb66ce06d7b47bfe342e
309 x/a: copy a:779c764182ce5d43e2b1eb66ce06d7b47bfe342e
310 overwriting x/a expanding keywords
310 overwriting x/a expanding keywords
311 % cat a
311 % cat a
312 expand $Id: x/a f27c134d2d9b Thu, 01 Jan 1970 00:00:03 +0000 user $
312 expand $Id: x/a f27c134d2d9b Thu, 01 Jan 1970 00:00:03 +0000 user $
313 do not process $Id:
313 do not process $Id:
314 xxx $
314 xxx $
315 $Xinfo: User Name <user@example.com>: xa $
315 $Xinfo: User Name <user@example.com>: xa $
316 % kwshrink a inside directory x
316 % kwshrink a inside directory x
317 overwriting x/a shrinking keywords
317 overwriting x/a shrinking keywords
318 % cat a
318 % cat a
319 expand $Id$
319 expand $Id$
320 do not process $Id:
320 do not process $Id:
321 xxx $
321 xxx $
322 $Xinfo$
322 $Xinfo$
323 % kwexpand nonexistent
323 % kwexpand nonexistent
324 nonexistent: No such file or directory
324 nonexistent: No such file or directory
325 % switch off expansion
325 % switch off expansion
326 % kwshrink with unknown file u
326 % kwshrink with unknown file u
327 overwriting a shrinking keywords
327 overwriting a shrinking keywords
328 overwriting x/a shrinking keywords
328 overwriting x/a shrinking keywords
329 % cat
329 % cat
330 expand $Id$
330 expand $Id$
331 do not process $Id:
331 do not process $Id:
332 xxx $
332 xxx $
333 $Xinfo$
333 $Xinfo$
334 expand $Id$
334 expand $Id$
335 do not process $Id:
335 do not process $Id:
336 xxx $
336 xxx $
337 $Xinfo$
337 $Xinfo$
338 ignore $Id$
338 ignore $Id$
339 % hg cat
339 % hg cat
340 expand $Id: a 0729690beff6 Thu, 01 Jan 1970 00:00:02 +0000 user $
340 expand $Id: a 0729690beff6 Thu, 01 Jan 1970 00:00:02 +0000 user $
341 do not process $Id:
341 do not process $Id:
342 xxx $
342 xxx $
343 $Xinfo: User Name <user@example.com>: firstline $
343 $Xinfo: User Name <user@example.com>: firstline $
344 ignore $Id$
344 ignore $Id$
345 a
345 a
346 % cat
346 % cat
347 expand $Id$
347 expand $Id$
348 do not process $Id:
348 do not process $Id:
349 xxx $
349 xxx $
350 $Xinfo$
350 $Xinfo$
351 expand $Id$
351 expand $Id$
352 do not process $Id:
352 do not process $Id:
353 xxx $
353 xxx $
354 $Xinfo$
354 $Xinfo$
355 ignore $Id$
355 ignore $Id$
356 % hg cat
356 % hg cat
357 expand $Id$
357 expand $Id$
358 do not process $Id:
358 do not process $Id:
359 xxx $
359 xxx $
360 $Xinfo$
360 $Xinfo$
361 ignore $Id$
361 ignore $Id$
362 a
362 a
General Comments 0
You need to be logged in to leave comments. Login now