##// END OF EJS Templates
keyword: do not bother about detecting extension path in demo...
Christian Ebert -
r10714:9c0a1887 default
parent child Browse files
Show More
@@ -1,525 +1,521 b''
1 # keyword.py - $Keyword$ expansion for Mercurial
1 # keyword.py - $Keyword$ expansion for Mercurial
2 #
2 #
3 # Copyright 2007-2010 Christian Ebert <blacktrash@gmx.net>
3 # Copyright 2007-2010 Christian Ebert <blacktrash@gmx.net>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
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
14 # files (like LaTeX packages), that are mostly addressed to an
15 # audience not running a version control system.
15 # audience not running a version control system.
16 #
16 #
17 # For in-depth discussion refer to
17 # For in-depth discussion refer to
18 # <http://mercurial.selenic.com/wiki/KeywordPlan>.
18 # <http://mercurial.selenic.com/wiki/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 # Files to act upon/ignore are specified in the [keyword] section.
24 # Files to act upon/ignore are specified in the [keyword] section.
25 # Customized keyword template mappings in the [keywordmaps] section.
25 # Customized keyword template mappings in the [keywordmaps] section.
26 #
26 #
27 # Run "hg help keyword" and "hg kwdemo" to get info on configuration.
27 # Run "hg help keyword" and "hg kwdemo" to get info on configuration.
28
28
29 '''expand keywords in tracked files
29 '''expand keywords in tracked files
30
30
31 This extension expands RCS/CVS-like or self-customized $Keywords$ in
31 This extension expands RCS/CVS-like or self-customized $Keywords$ in
32 tracked text files selected by your configuration.
32 tracked text files selected by your configuration.
33
33
34 Keywords are only expanded in local repositories and not stored in the
34 Keywords are only expanded in local repositories and not stored in the
35 change history. The mechanism can be regarded as a convenience for the
35 change history. The mechanism can be regarded as a convenience for the
36 current user or for archive distribution.
36 current user or for archive distribution.
37
37
38 Configuration is done in the [keyword] and [keywordmaps] sections of
38 Configuration is done in the [keyword] and [keywordmaps] sections of
39 hgrc files.
39 hgrc files.
40
40
41 Example::
41 Example::
42
42
43 [keyword]
43 [keyword]
44 # expand keywords in every python file except those matching "x*"
44 # expand keywords in every python file except those matching "x*"
45 **.py =
45 **.py =
46 x* = ignore
46 x* = ignore
47
47
48 NOTE: the more specific you are in your filename patterns the less you
48 NOTE: the more specific you are in your filename patterns the less you
49 lose speed in huge repositories.
49 lose speed in huge repositories.
50
50
51 For [keywordmaps] template mapping and expansion demonstration and
51 For [keywordmaps] template mapping and expansion demonstration and
52 control run "hg kwdemo". See "hg help templates" for a list of
52 control run "hg kwdemo". See "hg help templates" for a list of
53 available templates and filters.
53 available templates and filters.
54
54
55 An additional date template filter {date|utcdate} is provided. It
55 An additional date template filter {date|utcdate} is provided. It
56 returns a date like "2006/09/18 15:13:13".
56 returns a date like "2006/09/18 15:13:13".
57
57
58 The default template mappings (view with "hg kwdemo -d") can be
58 The default template mappings (view with "hg kwdemo -d") can be
59 replaced with customized keywords and templates. Again, run "hg
59 replaced with customized keywords and templates. Again, run "hg
60 kwdemo" to control the results of your config changes.
60 kwdemo" to control the results of your config changes.
61
61
62 Before changing/disabling active keywords, run "hg kwshrink" to avoid
62 Before changing/disabling active keywords, run "hg kwshrink" to avoid
63 the risk of inadvertently storing expanded keywords in the change
63 the risk of inadvertently storing expanded keywords in the change
64 history.
64 history.
65
65
66 To force expansion after enabling it, or a configuration change, run
66 To force expansion after enabling it, or a configuration change, run
67 "hg kwexpand".
67 "hg kwexpand".
68
68
69 Also, when committing with the record extension or using mq's qrecord,
69 Also, when committing with the record extension or using mq's qrecord,
70 be aware that keywords cannot be updated. Again, run "hg kwexpand" on
70 be aware that keywords cannot be updated. Again, run "hg kwexpand" on
71 the files in question to update keyword expansions after all changes
71 the files in question to update keyword expansions after all changes
72 have been checked in.
72 have been checked in.
73
73
74 Expansions spanning more than one line and incremental expansions,
74 Expansions spanning more than one line and incremental expansions,
75 like CVS' $Log$, are not supported. A keyword template map "Log =
75 like CVS' $Log$, are not supported. A keyword template map "Log =
76 {desc}" expands to the first line of the changeset description.
76 {desc}" expands to the first line of the changeset description.
77 '''
77 '''
78
78
79 from mercurial import commands, cmdutil, dispatch, filelog, revlog, extensions
79 from mercurial import commands, cmdutil, dispatch, filelog, revlog, extensions
80 from mercurial import patch, localrepo, templater, templatefilters, util, match
80 from mercurial import patch, localrepo, templater, templatefilters, util, match
81 from mercurial.hgweb import webcommands
81 from mercurial.hgweb import webcommands
82 from mercurial.node import nullid
82 from mercurial.node import nullid
83 from mercurial.i18n import _
83 from mercurial.i18n import _
84 import re, shutil, tempfile
84 import re, shutil, tempfile
85
85
86 commands.optionalrepo += ' kwdemo'
86 commands.optionalrepo += ' kwdemo'
87
87
88 # hg commands that do not act on keywords
88 # hg commands that do not act on keywords
89 nokwcommands = ('add addremove annotate bundle copy export grep incoming init'
89 nokwcommands = ('add addremove annotate bundle copy export grep incoming init'
90 ' log outgoing push rename rollback tip verify'
90 ' log outgoing push rename rollback tip verify'
91 ' convert email glog')
91 ' convert email glog')
92
92
93 # hg commands that trigger expansion only when writing to working dir,
93 # hg commands that trigger expansion only when writing to working dir,
94 # not when reading filelog, and unexpand when reading from working dir
94 # not when reading filelog, and unexpand when reading from working dir
95 restricted = ('merge record resolve qfold qimport qnew qpush qrefresh qrecord'
95 restricted = ('merge record resolve qfold qimport qnew qpush qrefresh qrecord'
96 ' transplant')
96 ' transplant')
97
97
98 # provide cvs-like UTC date filter
98 # provide cvs-like UTC date filter
99 utcdate = lambda x: util.datestr((x[0], 0), '%Y/%m/%d %H:%M:%S')
99 utcdate = lambda x: util.datestr((x[0], 0), '%Y/%m/%d %H:%M:%S')
100
100
101 # make keyword tools accessible
101 # make keyword tools accessible
102 kwtools = {'templater': None, 'hgcmd': '', 'inc': [], 'exc': ['.hg*']}
102 kwtools = {'templater': None, 'hgcmd': '', 'inc': [], 'exc': ['.hg*']}
103
103
104
104
105 class kwtemplater(object):
105 class kwtemplater(object):
106 '''
106 '''
107 Sets up keyword templates, corresponding keyword regex, and
107 Sets up keyword templates, corresponding keyword regex, and
108 provides keyword substitution functions.
108 provides keyword substitution functions.
109 '''
109 '''
110 templates = {
110 templates = {
111 'Revision': '{node|short}',
111 'Revision': '{node|short}',
112 'Author': '{author|user}',
112 'Author': '{author|user}',
113 'Date': '{date|utcdate}',
113 'Date': '{date|utcdate}',
114 'RCSfile': '{file|basename},v',
114 'RCSfile': '{file|basename},v',
115 'RCSFile': '{file|basename},v', # kept for backwards compatibility
115 'RCSFile': '{file|basename},v', # kept for backwards compatibility
116 # with hg-keyword
116 # with hg-keyword
117 'Source': '{root}/{file},v',
117 'Source': '{root}/{file},v',
118 'Id': '{file|basename},v {node|short} {date|utcdate} {author|user}',
118 'Id': '{file|basename},v {node|short} {date|utcdate} {author|user}',
119 'Header': '{root}/{file},v {node|short} {date|utcdate} {author|user}',
119 'Header': '{root}/{file},v {node|short} {date|utcdate} {author|user}',
120 }
120 }
121
121
122 def __init__(self, ui, repo):
122 def __init__(self, ui, repo):
123 self.ui = ui
123 self.ui = ui
124 self.repo = repo
124 self.repo = repo
125 self.match = match.match(repo.root, '', [],
125 self.match = match.match(repo.root, '', [],
126 kwtools['inc'], kwtools['exc'])
126 kwtools['inc'], kwtools['exc'])
127 self.restrict = kwtools['hgcmd'] in restricted.split()
127 self.restrict = kwtools['hgcmd'] in restricted.split()
128
128
129 kwmaps = self.ui.configitems('keywordmaps')
129 kwmaps = self.ui.configitems('keywordmaps')
130 if kwmaps: # override default templates
130 if kwmaps: # override default templates
131 self.templates = dict((k, templater.parsestring(v, False))
131 self.templates = dict((k, templater.parsestring(v, False))
132 for k, v in kwmaps)
132 for k, v in kwmaps)
133 escaped = map(re.escape, self.templates.keys())
133 escaped = map(re.escape, self.templates.keys())
134 kwpat = r'\$(%s)(: [^$\n\r]*? )??\$' % '|'.join(escaped)
134 kwpat = r'\$(%s)(: [^$\n\r]*? )??\$' % '|'.join(escaped)
135 self.re_kw = re.compile(kwpat)
135 self.re_kw = re.compile(kwpat)
136
136
137 templatefilters.filters['utcdate'] = utcdate
137 templatefilters.filters['utcdate'] = utcdate
138 self.ct = cmdutil.changeset_templater(self.ui, self.repo,
138 self.ct = cmdutil.changeset_templater(self.ui, self.repo,
139 False, None, '', False)
139 False, None, '', False)
140
140
141 def substitute(self, data, path, ctx, subfunc):
141 def substitute(self, data, path, ctx, subfunc):
142 '''Replaces keywords in data with expanded template.'''
142 '''Replaces keywords in data with expanded template.'''
143 def kwsub(mobj):
143 def kwsub(mobj):
144 kw = mobj.group(1)
144 kw = mobj.group(1)
145 self.ct.use_template(self.templates[kw])
145 self.ct.use_template(self.templates[kw])
146 self.ui.pushbuffer()
146 self.ui.pushbuffer()
147 self.ct.show(ctx, root=self.repo.root, file=path)
147 self.ct.show(ctx, root=self.repo.root, file=path)
148 ekw = templatefilters.firstline(self.ui.popbuffer())
148 ekw = templatefilters.firstline(self.ui.popbuffer())
149 return '$%s: %s $' % (kw, ekw)
149 return '$%s: %s $' % (kw, ekw)
150 return subfunc(kwsub, data)
150 return subfunc(kwsub, data)
151
151
152 def expand(self, path, node, data):
152 def expand(self, path, node, data):
153 '''Returns data with keywords expanded.'''
153 '''Returns data with keywords expanded.'''
154 if not self.restrict and self.match(path) and not util.binary(data):
154 if not self.restrict and self.match(path) and not util.binary(data):
155 ctx = self.repo.filectx(path, fileid=node).changectx()
155 ctx = self.repo.filectx(path, fileid=node).changectx()
156 return self.substitute(data, path, ctx, self.re_kw.sub)
156 return self.substitute(data, path, ctx, self.re_kw.sub)
157 return data
157 return data
158
158
159 def iskwfile(self, path, flagfunc):
159 def iskwfile(self, path, flagfunc):
160 '''Returns true if path matches [keyword] pattern
160 '''Returns true if path matches [keyword] pattern
161 and is not a symbolic link.
161 and is not a symbolic link.
162 Caveat: localrepository._link fails on Windows.'''
162 Caveat: localrepository._link fails on Windows.'''
163 return self.match(path) and not 'l' in flagfunc(path)
163 return self.match(path) and not 'l' in flagfunc(path)
164
164
165 def overwrite(self, node, expand, candidates):
165 def overwrite(self, node, expand, candidates):
166 '''Overwrites selected files expanding/shrinking keywords.'''
166 '''Overwrites selected files expanding/shrinking keywords.'''
167 ctx = self.repo[node]
167 ctx = self.repo[node]
168 mf = ctx.manifest()
168 mf = ctx.manifest()
169 if node is not None: # commit
169 if node is not None: # commit
170 candidates = [f for f in ctx.files() if f in mf]
170 candidates = [f for f in ctx.files() if f in mf]
171 candidates = [f for f in candidates if self.iskwfile(f, ctx.flags)]
171 candidates = [f for f in candidates if self.iskwfile(f, ctx.flags)]
172 if candidates:
172 if candidates:
173 self.restrict = True # do not expand when reading
173 self.restrict = True # do not expand when reading
174 msg = (expand and _('overwriting %s expanding keywords\n')
174 msg = (expand and _('overwriting %s expanding keywords\n')
175 or _('overwriting %s shrinking keywords\n'))
175 or _('overwriting %s shrinking keywords\n'))
176 for f in candidates:
176 for f in candidates:
177 fp = self.repo.file(f)
177 fp = self.repo.file(f)
178 data = fp.read(mf[f])
178 data = fp.read(mf[f])
179 if util.binary(data):
179 if util.binary(data):
180 continue
180 continue
181 if expand:
181 if expand:
182 if node is None:
182 if node is None:
183 ctx = self.repo.filectx(f, fileid=mf[f]).changectx()
183 ctx = self.repo.filectx(f, fileid=mf[f]).changectx()
184 data, found = self.substitute(data, f, ctx,
184 data, found = self.substitute(data, f, ctx,
185 self.re_kw.subn)
185 self.re_kw.subn)
186 else:
186 else:
187 found = self.re_kw.search(data)
187 found = self.re_kw.search(data)
188 if found:
188 if found:
189 self.ui.note(msg % f)
189 self.ui.note(msg % f)
190 self.repo.wwrite(f, data, mf.flags(f))
190 self.repo.wwrite(f, data, mf.flags(f))
191 if node is None:
191 if node is None:
192 self.repo.dirstate.normal(f)
192 self.repo.dirstate.normal(f)
193 self.restrict = False
193 self.restrict = False
194
194
195 def shrinktext(self, text):
195 def shrinktext(self, text):
196 '''Unconditionally removes all keyword substitutions from text.'''
196 '''Unconditionally removes all keyword substitutions from text.'''
197 return self.re_kw.sub(r'$\1$', text)
197 return self.re_kw.sub(r'$\1$', text)
198
198
199 def shrink(self, fname, text):
199 def shrink(self, fname, text):
200 '''Returns text with all keyword substitutions removed.'''
200 '''Returns text with all keyword substitutions removed.'''
201 if self.match(fname) and not util.binary(text):
201 if self.match(fname) and not util.binary(text):
202 return self.shrinktext(text)
202 return self.shrinktext(text)
203 return text
203 return text
204
204
205 def shrinklines(self, fname, lines):
205 def shrinklines(self, fname, lines):
206 '''Returns lines with keyword substitutions removed.'''
206 '''Returns lines with keyword substitutions removed.'''
207 if self.match(fname):
207 if self.match(fname):
208 text = ''.join(lines)
208 text = ''.join(lines)
209 if not util.binary(text):
209 if not util.binary(text):
210 return self.shrinktext(text).splitlines(True)
210 return self.shrinktext(text).splitlines(True)
211 return lines
211 return lines
212
212
213 def wread(self, fname, data):
213 def wread(self, fname, data):
214 '''If in restricted mode returns data read from wdir with
214 '''If in restricted mode returns data read from wdir with
215 keyword substitutions removed.'''
215 keyword substitutions removed.'''
216 return self.restrict and self.shrink(fname, data) or data
216 return self.restrict and self.shrink(fname, data) or data
217
217
218 class kwfilelog(filelog.filelog):
218 class kwfilelog(filelog.filelog):
219 '''
219 '''
220 Subclass of filelog to hook into its read, add, cmp methods.
220 Subclass of filelog to hook into its read, add, cmp methods.
221 Keywords are "stored" unexpanded, and processed on reading.
221 Keywords are "stored" unexpanded, and processed on reading.
222 '''
222 '''
223 def __init__(self, opener, kwt, path):
223 def __init__(self, opener, kwt, path):
224 super(kwfilelog, self).__init__(opener, path)
224 super(kwfilelog, self).__init__(opener, path)
225 self.kwt = kwt
225 self.kwt = kwt
226 self.path = path
226 self.path = path
227
227
228 def read(self, node):
228 def read(self, node):
229 '''Expands keywords when reading filelog.'''
229 '''Expands keywords when reading filelog.'''
230 data = super(kwfilelog, self).read(node)
230 data = super(kwfilelog, self).read(node)
231 return self.kwt.expand(self.path, node, data)
231 return self.kwt.expand(self.path, node, data)
232
232
233 def add(self, text, meta, tr, link, p1=None, p2=None):
233 def add(self, text, meta, tr, link, p1=None, p2=None):
234 '''Removes keyword substitutions when adding to filelog.'''
234 '''Removes keyword substitutions when adding to filelog.'''
235 text = self.kwt.shrink(self.path, text)
235 text = self.kwt.shrink(self.path, text)
236 return super(kwfilelog, self).add(text, meta, tr, link, p1, p2)
236 return super(kwfilelog, self).add(text, meta, tr, link, p1, p2)
237
237
238 def cmp(self, node, text):
238 def cmp(self, node, text):
239 '''Removes keyword substitutions for comparison.'''
239 '''Removes keyword substitutions for comparison.'''
240 text = self.kwt.shrink(self.path, text)
240 text = self.kwt.shrink(self.path, text)
241 if self.renamed(node):
241 if self.renamed(node):
242 t2 = super(kwfilelog, self).read(node)
242 t2 = super(kwfilelog, self).read(node)
243 return t2 != text
243 return t2 != text
244 return revlog.revlog.cmp(self, node, text)
244 return revlog.revlog.cmp(self, node, text)
245
245
246 def _status(ui, repo, kwt, *pats, **opts):
246 def _status(ui, repo, kwt, *pats, **opts):
247 '''Bails out if [keyword] configuration is not active.
247 '''Bails out if [keyword] configuration is not active.
248 Returns status of working directory.'''
248 Returns status of working directory.'''
249 if kwt:
249 if kwt:
250 return repo.status(match=cmdutil.match(repo, pats, opts), clean=True,
250 return repo.status(match=cmdutil.match(repo, pats, opts), clean=True,
251 unknown=opts.get('unknown') or opts.get('all'))
251 unknown=opts.get('unknown') or opts.get('all'))
252 if ui.configitems('keyword'):
252 if ui.configitems('keyword'):
253 raise util.Abort(_('[keyword] patterns cannot match'))
253 raise util.Abort(_('[keyword] patterns cannot match'))
254 raise util.Abort(_('no [keyword] patterns configured'))
254 raise util.Abort(_('no [keyword] patterns configured'))
255
255
256 def _kwfwrite(ui, repo, expand, *pats, **opts):
256 def _kwfwrite(ui, repo, expand, *pats, **opts):
257 '''Selects files and passes them to kwtemplater.overwrite.'''
257 '''Selects files and passes them to kwtemplater.overwrite.'''
258 if repo.dirstate.parents()[1] != nullid:
258 if repo.dirstate.parents()[1] != nullid:
259 raise util.Abort(_('outstanding uncommitted merge'))
259 raise util.Abort(_('outstanding uncommitted merge'))
260 kwt = kwtools['templater']
260 kwt = kwtools['templater']
261 wlock = repo.wlock()
261 wlock = repo.wlock()
262 try:
262 try:
263 status = _status(ui, repo, kwt, *pats, **opts)
263 status = _status(ui, repo, kwt, *pats, **opts)
264 modified, added, removed, deleted, unknown, ignored, clean = status
264 modified, added, removed, deleted, unknown, ignored, clean = status
265 if modified or added or removed or deleted:
265 if modified or added or removed or deleted:
266 raise util.Abort(_('outstanding uncommitted changes'))
266 raise util.Abort(_('outstanding uncommitted changes'))
267 kwt.overwrite(None, expand, clean)
267 kwt.overwrite(None, expand, clean)
268 finally:
268 finally:
269 wlock.release()
269 wlock.release()
270
270
271 def demo(ui, repo, *args, **opts):
271 def demo(ui, repo, *args, **opts):
272 '''print [keywordmaps] configuration and an expansion example
272 '''print [keywordmaps] configuration and an expansion example
273
273
274 Show current, custom, or default keyword template maps and their
274 Show current, custom, or default keyword template maps and their
275 expansions.
275 expansions.
276
276
277 Extend the current configuration by specifying maps as arguments
277 Extend the current configuration by specifying maps as arguments
278 and using -f/--rcfile to source an external hgrc file.
278 and using -f/--rcfile to source an external hgrc file.
279
279
280 Use -d/--default to disable current configuration.
280 Use -d/--default to disable current configuration.
281
281
282 See "hg help templates" for information on templates and filters.
282 See "hg help templates" for information on templates and filters.
283 '''
283 '''
284 def demoitems(section, items):
284 def demoitems(section, items):
285 ui.write('[%s]\n' % section)
285 ui.write('[%s]\n' % section)
286 for k, v in sorted(items):
286 for k, v in sorted(items):
287 ui.write('%s = %s\n' % (k, v))
287 ui.write('%s = %s\n' % (k, v))
288
288
289 fn = 'demo.txt'
289 fn = 'demo.txt'
290 tmpdir = tempfile.mkdtemp('', 'kwdemo.')
290 tmpdir = tempfile.mkdtemp('', 'kwdemo.')
291 ui.note(_('creating temporary repository at %s\n') % tmpdir)
291 ui.note(_('creating temporary repository at %s\n') % tmpdir)
292 repo = localrepo.localrepository(ui, tmpdir, True)
292 repo = localrepo.localrepository(ui, tmpdir, True)
293 ui.setconfig('keyword', fn, '')
293 ui.setconfig('keyword', fn, '')
294
294
295 uikwmaps = ui.configitems('keywordmaps')
295 uikwmaps = ui.configitems('keywordmaps')
296 if args or opts.get('rcfile'):
296 if args or opts.get('rcfile'):
297 ui.status(_('\n\tconfiguration using custom keyword template maps\n'))
297 ui.status(_('\n\tconfiguration using custom keyword template maps\n'))
298 if uikwmaps:
298 if uikwmaps:
299 ui.status(_('\textending current template maps\n'))
299 ui.status(_('\textending current template maps\n'))
300 if opts.get('default') or not uikwmaps:
300 if opts.get('default') or not uikwmaps:
301 ui.status(_('\toverriding default template maps\n'))
301 ui.status(_('\toverriding default template maps\n'))
302 if opts.get('rcfile'):
302 if opts.get('rcfile'):
303 ui.readconfig(opts.get('rcfile'))
303 ui.readconfig(opts.get('rcfile'))
304 if args:
304 if args:
305 # simulate hgrc parsing
305 # simulate hgrc parsing
306 rcmaps = ['[keywordmaps]\n'] + [a + '\n' for a in args]
306 rcmaps = ['[keywordmaps]\n'] + [a + '\n' for a in args]
307 fp = repo.opener('hgrc', 'w')
307 fp = repo.opener('hgrc', 'w')
308 fp.writelines(rcmaps)
308 fp.writelines(rcmaps)
309 fp.close()
309 fp.close()
310 ui.readconfig(repo.join('hgrc'))
310 ui.readconfig(repo.join('hgrc'))
311 kwmaps = dict(ui.configitems('keywordmaps'))
311 kwmaps = dict(ui.configitems('keywordmaps'))
312 elif opts.get('default'):
312 elif opts.get('default'):
313 ui.status(_('\n\tconfiguration using default keyword template maps\n'))
313 ui.status(_('\n\tconfiguration using default keyword template maps\n'))
314 kwmaps = kwtemplater.templates
314 kwmaps = kwtemplater.templates
315 if uikwmaps:
315 if uikwmaps:
316 ui.status(_('\tdisabling current template maps\n'))
316 ui.status(_('\tdisabling current template maps\n'))
317 for k, v in kwmaps.iteritems():
317 for k, v in kwmaps.iteritems():
318 ui.setconfig('keywordmaps', k, v)
318 ui.setconfig('keywordmaps', k, v)
319 else:
319 else:
320 ui.status(_('\n\tconfiguration using current keyword template maps\n'))
320 ui.status(_('\n\tconfiguration using current keyword template maps\n'))
321 kwmaps = dict(uikwmaps) or kwtemplater.templates
321 kwmaps = dict(uikwmaps) or kwtemplater.templates
322
322
323 uisetup(ui)
323 uisetup(ui)
324 reposetup(ui, repo)
324 reposetup(ui, repo)
325 for k, v in ui.configitems('extensions'):
325 ui.write('[extensions]\nkeyword =\n')
326 if k.endswith('keyword'):
327 extension = '%s = %s' % (k, v)
328 break
329 ui.write('[extensions]\n%s\n' % extension)
330 demoitems('keyword', ui.configitems('keyword'))
326 demoitems('keyword', ui.configitems('keyword'))
331 demoitems('keywordmaps', kwmaps.iteritems())
327 demoitems('keywordmaps', kwmaps.iteritems())
332 keywords = '$' + '$\n$'.join(sorted(kwmaps.keys())) + '$\n'
328 keywords = '$' + '$\n$'.join(sorted(kwmaps.keys())) + '$\n'
333 repo.wopener(fn, 'w').write(keywords)
329 repo.wopener(fn, 'w').write(keywords)
334 repo.add([fn])
330 repo.add([fn])
335 ui.note(_('\nkeywords written to %s:\n') % fn)
331 ui.note(_('\nkeywords written to %s:\n') % fn)
336 ui.note(keywords)
332 ui.note(keywords)
337 repo.dirstate.setbranch('demobranch')
333 repo.dirstate.setbranch('demobranch')
338 for name, cmd in ui.configitems('hooks'):
334 for name, cmd in ui.configitems('hooks'):
339 if name.split('.', 1)[0].find('commit') > -1:
335 if name.split('.', 1)[0].find('commit') > -1:
340 repo.ui.setconfig('hooks', name, '')
336 repo.ui.setconfig('hooks', name, '')
341 msg = _('hg keyword configuration and expansion example')
337 msg = _('hg keyword configuration and expansion example')
342 ui.note("hg ci -m '%s'\n" % msg)
338 ui.note("hg ci -m '%s'\n" % msg)
343 repo.commit(text=msg)
339 repo.commit(text=msg)
344 ui.status(_('\n\tkeywords expanded\n'))
340 ui.status(_('\n\tkeywords expanded\n'))
345 ui.write(repo.wread(fn))
341 ui.write(repo.wread(fn))
346 shutil.rmtree(tmpdir, ignore_errors=True)
342 shutil.rmtree(tmpdir, ignore_errors=True)
347
343
348 def expand(ui, repo, *pats, **opts):
344 def expand(ui, repo, *pats, **opts):
349 '''expand keywords in the working directory
345 '''expand keywords in the working directory
350
346
351 Run after (re)enabling keyword expansion.
347 Run after (re)enabling keyword expansion.
352
348
353 kwexpand refuses to run if given files contain local changes.
349 kwexpand refuses to run if given files contain local changes.
354 '''
350 '''
355 # 3rd argument sets expansion to True
351 # 3rd argument sets expansion to True
356 _kwfwrite(ui, repo, True, *pats, **opts)
352 _kwfwrite(ui, repo, True, *pats, **opts)
357
353
358 def files(ui, repo, *pats, **opts):
354 def files(ui, repo, *pats, **opts):
359 '''show files configured for keyword expansion
355 '''show files configured for keyword expansion
360
356
361 List which files in the working directory are matched by the
357 List which files in the working directory are matched by the
362 [keyword] configuration patterns.
358 [keyword] configuration patterns.
363
359
364 Useful to prevent inadvertent keyword expansion and to speed up
360 Useful to prevent inadvertent keyword expansion and to speed up
365 execution by including only files that are actual candidates for
361 execution by including only files that are actual candidates for
366 expansion.
362 expansion.
367
363
368 See "hg help keyword" on how to construct patterns both for
364 See "hg help keyword" on how to construct patterns both for
369 inclusion and exclusion of files.
365 inclusion and exclusion of files.
370
366
371 With -A/--all and -v/--verbose the codes used to show the status
367 With -A/--all and -v/--verbose the codes used to show the status
372 of files are::
368 of files are::
373
369
374 K = keyword expansion candidate
370 K = keyword expansion candidate
375 k = keyword expansion candidate (not tracked)
371 k = keyword expansion candidate (not tracked)
376 I = ignored
372 I = ignored
377 i = ignored (not tracked)
373 i = ignored (not tracked)
378 '''
374 '''
379 kwt = kwtools['templater']
375 kwt = kwtools['templater']
380 status = _status(ui, repo, kwt, *pats, **opts)
376 status = _status(ui, repo, kwt, *pats, **opts)
381 cwd = pats and repo.getcwd() or ''
377 cwd = pats and repo.getcwd() or ''
382 modified, added, removed, deleted, unknown, ignored, clean = status
378 modified, added, removed, deleted, unknown, ignored, clean = status
383 files = []
379 files = []
384 if not opts.get('unknown') or opts.get('all'):
380 if not opts.get('unknown') or opts.get('all'):
385 files = sorted(modified + added + clean)
381 files = sorted(modified + added + clean)
386 wctx = repo[None]
382 wctx = repo[None]
387 kwfiles = [f for f in files if kwt.iskwfile(f, wctx.flags)]
383 kwfiles = [f for f in files if kwt.iskwfile(f, wctx.flags)]
388 kwunknown = [f for f in unknown if kwt.iskwfile(f, wctx.flags)]
384 kwunknown = [f for f in unknown if kwt.iskwfile(f, wctx.flags)]
389 if not opts.get('ignore') or opts.get('all'):
385 if not opts.get('ignore') or opts.get('all'):
390 showfiles = kwfiles, kwunknown
386 showfiles = kwfiles, kwunknown
391 else:
387 else:
392 showfiles = [], []
388 showfiles = [], []
393 if opts.get('all') or opts.get('ignore'):
389 if opts.get('all') or opts.get('ignore'):
394 showfiles += ([f for f in files if f not in kwfiles],
390 showfiles += ([f for f in files if f not in kwfiles],
395 [f for f in unknown if f not in kwunknown])
391 [f for f in unknown if f not in kwunknown])
396 for char, filenames in zip('KkIi', showfiles):
392 for char, filenames in zip('KkIi', showfiles):
397 fmt = (opts.get('all') or ui.verbose) and '%s %%s\n' % char or '%s\n'
393 fmt = (opts.get('all') or ui.verbose) and '%s %%s\n' % char or '%s\n'
398 for f in filenames:
394 for f in filenames:
399 ui.write(fmt % repo.pathto(f, cwd))
395 ui.write(fmt % repo.pathto(f, cwd))
400
396
401 def shrink(ui, repo, *pats, **opts):
397 def shrink(ui, repo, *pats, **opts):
402 '''revert expanded keywords in the working directory
398 '''revert expanded keywords in the working directory
403
399
404 Run before changing/disabling active keywords or if you experience
400 Run before changing/disabling active keywords or if you experience
405 problems with "hg import" or "hg merge".
401 problems with "hg import" or "hg merge".
406
402
407 kwshrink refuses to run if given files contain local changes.
403 kwshrink refuses to run if given files contain local changes.
408 '''
404 '''
409 # 3rd argument sets expansion to False
405 # 3rd argument sets expansion to False
410 _kwfwrite(ui, repo, False, *pats, **opts)
406 _kwfwrite(ui, repo, False, *pats, **opts)
411
407
412
408
413 def uisetup(ui):
409 def uisetup(ui):
414 '''Collects [keyword] config in kwtools.
410 '''Collects [keyword] config in kwtools.
415 Monkeypatches dispatch._parse if needed.'''
411 Monkeypatches dispatch._parse if needed.'''
416
412
417 for pat, opt in ui.configitems('keyword'):
413 for pat, opt in ui.configitems('keyword'):
418 if opt != 'ignore':
414 if opt != 'ignore':
419 kwtools['inc'].append(pat)
415 kwtools['inc'].append(pat)
420 else:
416 else:
421 kwtools['exc'].append(pat)
417 kwtools['exc'].append(pat)
422
418
423 if kwtools['inc']:
419 if kwtools['inc']:
424 def kwdispatch_parse(orig, ui, args):
420 def kwdispatch_parse(orig, ui, args):
425 '''Monkeypatch dispatch._parse to obtain running hg command.'''
421 '''Monkeypatch dispatch._parse to obtain running hg command.'''
426 cmd, func, args, options, cmdoptions = orig(ui, args)
422 cmd, func, args, options, cmdoptions = orig(ui, args)
427 kwtools['hgcmd'] = cmd
423 kwtools['hgcmd'] = cmd
428 return cmd, func, args, options, cmdoptions
424 return cmd, func, args, options, cmdoptions
429
425
430 extensions.wrapfunction(dispatch, '_parse', kwdispatch_parse)
426 extensions.wrapfunction(dispatch, '_parse', kwdispatch_parse)
431
427
432 def reposetup(ui, repo):
428 def reposetup(ui, repo):
433 '''Sets up repo as kwrepo for keyword substitution.
429 '''Sets up repo as kwrepo for keyword substitution.
434 Overrides file method to return kwfilelog instead of filelog
430 Overrides file method to return kwfilelog instead of filelog
435 if file matches user configuration.
431 if file matches user configuration.
436 Wraps commit to overwrite configured files with updated
432 Wraps commit to overwrite configured files with updated
437 keyword substitutions.
433 keyword substitutions.
438 Monkeypatches patch and webcommands.'''
434 Monkeypatches patch and webcommands.'''
439
435
440 try:
436 try:
441 if (not repo.local() or not kwtools['inc']
437 if (not repo.local() or not kwtools['inc']
442 or kwtools['hgcmd'] in nokwcommands.split()
438 or kwtools['hgcmd'] in nokwcommands.split()
443 or '.hg' in util.splitpath(repo.root)
439 or '.hg' in util.splitpath(repo.root)
444 or repo._url.startswith('bundle:')):
440 or repo._url.startswith('bundle:')):
445 return
441 return
446 except AttributeError:
442 except AttributeError:
447 pass
443 pass
448
444
449 kwtools['templater'] = kwt = kwtemplater(ui, repo)
445 kwtools['templater'] = kwt = kwtemplater(ui, repo)
450
446
451 class kwrepo(repo.__class__):
447 class kwrepo(repo.__class__):
452 def file(self, f):
448 def file(self, f):
453 if f[0] == '/':
449 if f[0] == '/':
454 f = f[1:]
450 f = f[1:]
455 return kwfilelog(self.sopener, kwt, f)
451 return kwfilelog(self.sopener, kwt, f)
456
452
457 def wread(self, filename):
453 def wread(self, filename):
458 data = super(kwrepo, self).wread(filename)
454 data = super(kwrepo, self).wread(filename)
459 return kwt.wread(filename, data)
455 return kwt.wread(filename, data)
460
456
461 def commit(self, *args, **opts):
457 def commit(self, *args, **opts):
462 # use custom commitctx for user commands
458 # use custom commitctx for user commands
463 # other extensions can still wrap repo.commitctx directly
459 # other extensions can still wrap repo.commitctx directly
464 self.commitctx = self.kwcommitctx
460 self.commitctx = self.kwcommitctx
465 try:
461 try:
466 return super(kwrepo, self).commit(*args, **opts)
462 return super(kwrepo, self).commit(*args, **opts)
467 finally:
463 finally:
468 del self.commitctx
464 del self.commitctx
469
465
470 def kwcommitctx(self, ctx, error=False):
466 def kwcommitctx(self, ctx, error=False):
471 n = super(kwrepo, self).commitctx(ctx, error)
467 n = super(kwrepo, self).commitctx(ctx, error)
472 # no lock needed, only called from repo.commit() which already locks
468 # no lock needed, only called from repo.commit() which already locks
473 kwt.overwrite(n, True, None)
469 kwt.overwrite(n, True, None)
474 return n
470 return n
475
471
476 # monkeypatches
472 # monkeypatches
477 def kwpatchfile_init(orig, self, ui, fname, opener,
473 def kwpatchfile_init(orig, self, ui, fname, opener,
478 missing=False, eol=None):
474 missing=False, eol=None):
479 '''Monkeypatch/wrap patch.patchfile.__init__ to avoid
475 '''Monkeypatch/wrap patch.patchfile.__init__ to avoid
480 rejects or conflicts due to expanded keywords in working dir.'''
476 rejects or conflicts due to expanded keywords in working dir.'''
481 orig(self, ui, fname, opener, missing, eol)
477 orig(self, ui, fname, opener, missing, eol)
482 # shrink keywords read from working dir
478 # shrink keywords read from working dir
483 self.lines = kwt.shrinklines(self.fname, self.lines)
479 self.lines = kwt.shrinklines(self.fname, self.lines)
484
480
485 def kw_diff(orig, repo, node1=None, node2=None, match=None, changes=None,
481 def kw_diff(orig, repo, node1=None, node2=None, match=None, changes=None,
486 opts=None):
482 opts=None):
487 '''Monkeypatch patch.diff to avoid expansion except when
483 '''Monkeypatch patch.diff to avoid expansion except when
488 comparing against working dir.'''
484 comparing against working dir.'''
489 if node2 is not None:
485 if node2 is not None:
490 kwt.match = util.never
486 kwt.match = util.never
491 elif node1 is not None and node1 != repo['.'].node():
487 elif node1 is not None and node1 != repo['.'].node():
492 kwt.restrict = True
488 kwt.restrict = True
493 return orig(repo, node1, node2, match, changes, opts)
489 return orig(repo, node1, node2, match, changes, opts)
494
490
495 def kwweb_skip(orig, web, req, tmpl):
491 def kwweb_skip(orig, web, req, tmpl):
496 '''Wraps webcommands.x turning off keyword expansion.'''
492 '''Wraps webcommands.x turning off keyword expansion.'''
497 kwt.match = util.never
493 kwt.match = util.never
498 return orig(web, req, tmpl)
494 return orig(web, req, tmpl)
499
495
500 repo.__class__ = kwrepo
496 repo.__class__ = kwrepo
501
497
502 extensions.wrapfunction(patch.patchfile, '__init__', kwpatchfile_init)
498 extensions.wrapfunction(patch.patchfile, '__init__', kwpatchfile_init)
503 if not kwt.restrict:
499 if not kwt.restrict:
504 extensions.wrapfunction(patch, 'diff', kw_diff)
500 extensions.wrapfunction(patch, 'diff', kw_diff)
505 for c in 'annotate changeset rev filediff diff'.split():
501 for c in 'annotate changeset rev filediff diff'.split():
506 extensions.wrapfunction(webcommands, c, kwweb_skip)
502 extensions.wrapfunction(webcommands, c, kwweb_skip)
507
503
508 cmdtable = {
504 cmdtable = {
509 'kwdemo':
505 'kwdemo':
510 (demo,
506 (demo,
511 [('d', 'default', None, _('show default keyword template maps')),
507 [('d', 'default', None, _('show default keyword template maps')),
512 ('f', 'rcfile', '', _('read maps from rcfile'))],
508 ('f', 'rcfile', '', _('read maps from rcfile'))],
513 _('hg kwdemo [-d] [-f RCFILE] [TEMPLATEMAP]...')),
509 _('hg kwdemo [-d] [-f RCFILE] [TEMPLATEMAP]...')),
514 'kwexpand': (expand, commands.walkopts,
510 'kwexpand': (expand, commands.walkopts,
515 _('hg kwexpand [OPTION]... [FILE]...')),
511 _('hg kwexpand [OPTION]... [FILE]...')),
516 'kwfiles':
512 'kwfiles':
517 (files,
513 (files,
518 [('A', 'all', None, _('show keyword status flags of all files')),
514 [('A', 'all', None, _('show keyword status flags of all files')),
519 ('i', 'ignore', None, _('show files excluded from expansion')),
515 ('i', 'ignore', None, _('show files excluded from expansion')),
520 ('u', 'unknown', None, _('only show unknown (not tracked) files')),
516 ('u', 'unknown', None, _('only show unknown (not tracked) files')),
521 ] + commands.walkopts,
517 ] + commands.walkopts,
522 _('hg kwfiles [OPTION]... [FILE]...')),
518 _('hg kwfiles [OPTION]... [FILE]...')),
523 'kwshrink': (shrink, commands.walkopts,
519 'kwshrink': (shrink, commands.walkopts,
524 _('hg kwshrink [OPTION]... [FILE]...')),
520 _('hg kwshrink [OPTION]... [FILE]...')),
525 }
521 }
@@ -1,480 +1,480 b''
1 % hg kwdemo
1 % hg kwdemo
2 [extensions]
2 [extensions]
3 keyword =
3 keyword =
4 [keyword]
4 [keyword]
5 demo.txt =
5 demo.txt =
6 [keywordmaps]
6 [keywordmaps]
7 Author = {author|user}
7 Author = {author|user}
8 Date = {date|utcdate}
8 Date = {date|utcdate}
9 Header = {root}/{file},v {node|short} {date|utcdate} {author|user}
9 Header = {root}/{file},v {node|short} {date|utcdate} {author|user}
10 Id = {file|basename},v {node|short} {date|utcdate} {author|user}
10 Id = {file|basename},v {node|short} {date|utcdate} {author|user}
11 RCSFile = {file|basename},v
11 RCSFile = {file|basename},v
12 RCSfile = {file|basename},v
12 RCSfile = {file|basename},v
13 Revision = {node|short}
13 Revision = {node|short}
14 Source = {root}/{file},v
14 Source = {root}/{file},v
15 $Author: test $
15 $Author: test $
16 $Date: 2000/00/00 00:00:00 $
16 $Date: 2000/00/00 00:00:00 $
17 $Header: /TMP/demo.txt,v xxxxxxxxxxxx 2000/00/00 00:00:00 test $
17 $Header: /TMP/demo.txt,v xxxxxxxxxxxx 2000/00/00 00:00:00 test $
18 $Id: demo.txt,v xxxxxxxxxxxx 2000/00/00 00:00:00 test $
18 $Id: demo.txt,v xxxxxxxxxxxx 2000/00/00 00:00:00 test $
19 $RCSFile: demo.txt,v $
19 $RCSFile: demo.txt,v $
20 $RCSfile: demo.txt,v $
20 $RCSfile: demo.txt,v $
21 $Revision: xxxxxxxxxxxx $
21 $Revision: xxxxxxxxxxxx $
22 $Source: /TMP/demo.txt,v $
22 $Source: /TMP/demo.txt,v $
23 [extensions]
23 [extensions]
24 keyword =
24 keyword =
25 [keyword]
25 [keyword]
26 demo.txt =
26 demo.txt =
27 [keywordmaps]
27 [keywordmaps]
28 Branch = {branches}
28 Branch = {branches}
29 $Branch: demobranch $
29 $Branch: demobranch $
30 % kwshrink should exit silently in empty/invalid repo
30 % kwshrink should exit silently in empty/invalid repo
31 pulling from test-keyword.hg
31 pulling from test-keyword.hg
32 requesting all changes
32 requesting all changes
33 adding changesets
33 adding changesets
34 adding manifests
34 adding manifests
35 adding file changes
35 adding file changes
36 added 1 changesets with 1 changes to 1 files
36 added 1 changesets with 1 changes to 1 files
37 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
37 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
38 % cat
38 % cat
39 expand $Id$
39 expand $Id$
40 do not process $Id:
40 do not process $Id:
41 xxx $
41 xxx $
42 ignore $Id$
42 ignore $Id$
43 % no kwfiles
43 % no kwfiles
44 % untracked candidates
44 % untracked candidates
45 k a
45 k a
46 % addremove
46 % addremove
47 adding a
47 adding a
48 adding b
48 adding b
49 % status
49 % status
50 A a
50 A a
51 A b
51 A b
52 % default keyword expansion including commit hook
52 % default keyword expansion including commit hook
53 % interrupted commit should not change state or run commit hook
53 % interrupted commit should not change state or run commit hook
54 abort: empty commit message
54 abort: empty commit message
55 % status
55 % status
56 A a
56 A a
57 A b
57 A b
58 % commit
58 % commit
59 a
59 a
60 b
60 b
61 overwriting a expanding keywords
61 overwriting a expanding keywords
62 running hook commit.test: cp a hooktest
62 running hook commit.test: cp a hooktest
63 committed changeset 1:ef63ca68695bc9495032c6fda1350c71e6d256e9
63 committed changeset 1:ef63ca68695bc9495032c6fda1350c71e6d256e9
64 % status
64 % status
65 ? hooktest
65 ? hooktest
66 % identify
66 % identify
67 ef63ca68695b
67 ef63ca68695b
68 % cat
68 % cat
69 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
69 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
70 do not process $Id:
70 do not process $Id:
71 xxx $
71 xxx $
72 ignore $Id$
72 ignore $Id$
73 % hg cat
73 % hg cat
74 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
74 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
75 do not process $Id:
75 do not process $Id:
76 xxx $
76 xxx $
77 ignore $Id$
77 ignore $Id$
78 a
78 a
79 % diff a hooktest
79 % diff a hooktest
80 % removing commit hook from config
80 % removing commit hook from config
81 % bundle
81 % bundle
82 2 changesets found
82 2 changesets found
83 % notify on pull to check whether keywords stay as is in email
83 % notify on pull to check whether keywords stay as is in email
84 % ie. if patch.diff wrapper acts as it should
84 % ie. if patch.diff wrapper acts as it should
85 % pull from bundle
85 % pull from bundle
86 pulling from ../kw.hg
86 pulling from ../kw.hg
87 requesting all changes
87 requesting all changes
88 adding changesets
88 adding changesets
89 adding manifests
89 adding manifests
90 adding file changes
90 adding file changes
91 added 2 changesets with 3 changes to 3 files
91 added 2 changesets with 3 changes to 3 files
92
92
93 diff -r 000000000000 -r a2392c293916 sym
93 diff -r 000000000000 -r a2392c293916 sym
94 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
94 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
95 +++ b/sym Sat Feb 09 20:25:47 2008 +0100
95 +++ b/sym Sat Feb 09 20:25:47 2008 +0100
96 @@ -0,0 +1,1 @@
96 @@ -0,0 +1,1 @@
97 +a
97 +a
98 \ No newline at end of file
98 \ No newline at end of file
99
99
100 diff -r a2392c293916 -r ef63ca68695b a
100 diff -r a2392c293916 -r ef63ca68695b a
101 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
101 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
102 +++ b/a Thu Jan 01 00:00:00 1970 +0000
102 +++ b/a Thu Jan 01 00:00:00 1970 +0000
103 @@ -0,0 +1,3 @@
103 @@ -0,0 +1,3 @@
104 +expand $Id$
104 +expand $Id$
105 +do not process $Id:
105 +do not process $Id:
106 +xxx $
106 +xxx $
107 diff -r a2392c293916 -r ef63ca68695b b
107 diff -r a2392c293916 -r ef63ca68695b b
108 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
108 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
109 +++ b/b Thu Jan 01 00:00:00 1970 +0000
109 +++ b/b Thu Jan 01 00:00:00 1970 +0000
110 @@ -0,0 +1,1 @@
110 @@ -0,0 +1,1 @@
111 +ignore $Id$
111 +ignore $Id$
112 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
112 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
113 % remove notify config
113 % remove notify config
114 % touch
114 % touch
115 % status
115 % status
116 % update
116 % update
117 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
117 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
118 % cat
118 % cat
119 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
119 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
120 do not process $Id:
120 do not process $Id:
121 xxx $
121 xxx $
122 ignore $Id$
122 ignore $Id$
123 % check whether expansion is filewise
123 % check whether expansion is filewise
124 % commit c
124 % commit c
125 adding c
125 adding c
126 % force expansion
126 % force expansion
127 overwriting a expanding keywords
127 overwriting a expanding keywords
128 overwriting c expanding keywords
128 overwriting c expanding keywords
129 % compare changenodes in a c
129 % compare changenodes in a c
130 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
130 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
131 do not process $Id:
131 do not process $Id:
132 xxx $
132 xxx $
133 $Id: c,v 40a904bbbe4c 1970/01/01 00:00:01 user $
133 $Id: c,v 40a904bbbe4c 1970/01/01 00:00:01 user $
134 tests for different changenodes
134 tests for different changenodes
135 % qinit -c
135 % qinit -c
136 % qimport
136 % qimport
137 % qcommit
137 % qcommit
138 % keywords should not be expanded in patch
138 % keywords should not be expanded in patch
139 # HG changeset patch
139 # HG changeset patch
140 # User User Name <user@example.com>
140 # User User Name <user@example.com>
141 # Date 1 0
141 # Date 1 0
142 # Node ID 40a904bbbe4cd4ab0a1f28411e35db26341a40ad
142 # Node ID 40a904bbbe4cd4ab0a1f28411e35db26341a40ad
143 # Parent ef63ca68695bc9495032c6fda1350c71e6d256e9
143 # Parent ef63ca68695bc9495032c6fda1350c71e6d256e9
144 cndiff
144 cndiff
145
145
146 diff -r ef63ca68695b -r 40a904bbbe4c c
146 diff -r ef63ca68695b -r 40a904bbbe4c c
147 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
147 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
148 +++ b/c Thu Jan 01 00:00:01 1970 +0000
148 +++ b/c Thu Jan 01 00:00:01 1970 +0000
149 @@ -0,0 +1,2 @@
149 @@ -0,0 +1,2 @@
150 +$Id$
150 +$Id$
151 +tests for different changenodes
151 +tests for different changenodes
152 % qpop
152 % qpop
153 popping mqtest.diff
153 popping mqtest.diff
154 patch queue now empty
154 patch queue now empty
155 % qgoto - should imply qpush
155 % qgoto - should imply qpush
156 applying mqtest.diff
156 applying mqtest.diff
157 now at: mqtest.diff
157 now at: mqtest.diff
158 % cat
158 % cat
159 $Id: c,v 40a904bbbe4c 1970/01/01 00:00:01 user $
159 $Id: c,v 40a904bbbe4c 1970/01/01 00:00:01 user $
160 tests for different changenodes
160 tests for different changenodes
161 % qpop and move on
161 % qpop and move on
162 popping mqtest.diff
162 popping mqtest.diff
163 patch queue now empty
163 patch queue now empty
164 % copy
164 % copy
165 % kwfiles added
165 % kwfiles added
166 a
166 a
167 c
167 c
168 % commit
168 % commit
169 c
169 c
170 c: copy a:0045e12f6c5791aac80ca6cbfd97709a88307292
170 c: copy a:0045e12f6c5791aac80ca6cbfd97709a88307292
171 overwriting c expanding keywords
171 overwriting c expanding keywords
172 committed changeset 2:25736cf2f5cbe41f6be4e6784ef6ecf9f3bbcc7d
172 committed changeset 2:25736cf2f5cbe41f6be4e6784ef6ecf9f3bbcc7d
173 % cat a c
173 % cat a c
174 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
174 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
175 do not process $Id:
175 do not process $Id:
176 xxx $
176 xxx $
177 expand $Id: c,v 25736cf2f5cb 1970/01/01 00:00:01 user $
177 expand $Id: c,v 25736cf2f5cb 1970/01/01 00:00:01 user $
178 do not process $Id:
178 do not process $Id:
179 xxx $
179 xxx $
180 % touch copied c
180 % touch copied c
181 % status
181 % status
182 % kwfiles
182 % kwfiles
183 a
183 a
184 c
184 c
185 % ignored files
185 % ignored files
186 I b
186 I b
187 I sym
187 I sym
188 % all files
188 % all files
189 K a
189 K a
190 K c
190 K c
191 I b
191 I b
192 I sym
192 I sym
193 % diff --rev
193 % diff --rev
194 diff -r ef63ca68695b c
194 diff -r ef63ca68695b c
195 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
195 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
196 @@ -0,0 +1,3 @@
196 @@ -0,0 +1,3 @@
197 +expand $Id$
197 +expand $Id$
198 +do not process $Id:
198 +do not process $Id:
199 +xxx $
199 +xxx $
200 % rollback
200 % rollback
201 rolling back last transaction
201 rolling back last transaction
202 % status
202 % status
203 A c
203 A c
204 % update -C
204 % update -C
205 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
205 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
206 % custom keyword expansion
206 % custom keyword expansion
207 % try with kwdemo
207 % try with kwdemo
208 [extensions]
208 [extensions]
209 keyword =
209 keyword =
210 [keyword]
210 [keyword]
211 * =
211 * =
212 b = ignore
212 b = ignore
213 demo.txt =
213 demo.txt =
214 [keywordmaps]
214 [keywordmaps]
215 Xinfo = {author}: {desc}
215 Xinfo = {author}: {desc}
216 $Xinfo: test: hg keyword configuration and expansion example $
216 $Xinfo: test: hg keyword configuration and expansion example $
217 % cat
217 % cat
218 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
218 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
219 do not process $Id:
219 do not process $Id:
220 xxx $
220 xxx $
221 ignore $Id$
221 ignore $Id$
222 % hg cat
222 % hg cat
223 expand $Id: a ef63ca68695b Thu, 01 Jan 1970 00:00:00 +0000 user $
223 expand $Id: a ef63ca68695b Thu, 01 Jan 1970 00:00:00 +0000 user $
224 do not process $Id:
224 do not process $Id:
225 xxx $
225 xxx $
226 ignore $Id$
226 ignore $Id$
227 a
227 a
228 % interrupted commit should not change state
228 % interrupted commit should not change state
229 abort: empty commit message
229 abort: empty commit message
230 % status
230 % status
231 M a
231 M a
232 ? c
232 ? c
233 ? log
233 ? log
234 % commit
234 % commit
235 a
235 a
236 overwriting a expanding keywords
236 overwriting a expanding keywords
237 committed changeset 2:bb948857c743469b22bbf51f7ec8112279ca5d83
237 committed changeset 2:bb948857c743469b22bbf51f7ec8112279ca5d83
238 % status
238 % status
239 ? c
239 ? c
240 % verify
240 % verify
241 checking changesets
241 checking changesets
242 checking manifests
242 checking manifests
243 crosschecking files in changesets and manifests
243 crosschecking files in changesets and manifests
244 checking files
244 checking files
245 3 files, 3 changesets, 4 total revisions
245 3 files, 3 changesets, 4 total revisions
246 % cat
246 % cat
247 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
247 expand $Id: a bb948857c743 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 % hg cat
252 % hg cat
253 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
253 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
254 do not process $Id:
254 do not process $Id:
255 xxx $
255 xxx $
256 $Xinfo: User Name <user@example.com>: firstline $
256 $Xinfo: User Name <user@example.com>: firstline $
257 ignore $Id$
257 ignore $Id$
258 a
258 a
259 % annotate
259 % annotate
260 1: expand $Id$
260 1: expand $Id$
261 1: do not process $Id:
261 1: do not process $Id:
262 1: xxx $
262 1: xxx $
263 2: $Xinfo$
263 2: $Xinfo$
264 % remove
264 % remove
265 committed changeset 3:d14c712653769de926994cf7fbb06c8fbd68f012
265 committed changeset 3:d14c712653769de926994cf7fbb06c8fbd68f012
266 % status
266 % status
267 ? c
267 ? c
268 % rollback
268 % rollback
269 rolling back last transaction
269 rolling back last transaction
270 % status
270 % status
271 R a
271 R a
272 ? c
272 ? c
273 % revert a
273 % revert a
274 % cat a
274 % cat a
275 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
275 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
276 do not process $Id:
276 do not process $Id:
277 xxx $
277 xxx $
278 $Xinfo: User Name <user@example.com>: firstline $
278 $Xinfo: User Name <user@example.com>: firstline $
279 % clone to test incoming
279 % clone to test incoming
280 requesting all changes
280 requesting all changes
281 adding changesets
281 adding changesets
282 adding manifests
282 adding manifests
283 adding file changes
283 adding file changes
284 added 2 changesets with 3 changes to 3 files
284 added 2 changesets with 3 changes to 3 files
285 updating to branch default
285 updating to branch default
286 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
286 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
287 % incoming
287 % incoming
288 comparing with test-keyword/Test
288 comparing with test-keyword/Test
289 searching for changes
289 searching for changes
290 changeset: 2:bb948857c743
290 changeset: 2:bb948857c743
291 tag: tip
291 tag: tip
292 user: User Name <user@example.com>
292 user: User Name <user@example.com>
293 date: Thu Jan 01 00:00:02 1970 +0000
293 date: Thu Jan 01 00:00:02 1970 +0000
294 summary: firstline
294 summary: firstline
295
295
296 % commit rejecttest
296 % commit rejecttest
297 a
297 a
298 overwriting a expanding keywords
298 overwriting a expanding keywords
299 committed changeset 2:85e279d709ffc28c9fdd1b868570985fc3d87082
299 committed changeset 2:85e279d709ffc28c9fdd1b868570985fc3d87082
300 % export
300 % export
301 % import
301 % import
302 applying ../rejecttest.diff
302 applying ../rejecttest.diff
303 % cat
303 % cat
304 expand $Id: a 4e0994474d25 Thu, 01 Jan 1970 00:00:03 +0000 user $ rejecttest
304 expand $Id: a 4e0994474d25 Thu, 01 Jan 1970 00:00:03 +0000 user $ rejecttest
305 do not process $Id: rejecttest
305 do not process $Id: rejecttest
306 xxx $
306 xxx $
307 $Xinfo: User Name <user@example.com>: rejects? $
307 $Xinfo: User Name <user@example.com>: rejects? $
308 ignore $Id$
308 ignore $Id$
309
309
310 % rollback
310 % rollback
311 rolling back last transaction
311 rolling back last transaction
312 % clean update
312 % clean update
313 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
313 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
314 % kwexpand/kwshrink on selected files
314 % kwexpand/kwshrink on selected files
315 % copy a x/a
315 % copy a x/a
316 % kwexpand a
316 % kwexpand a
317 overwriting a expanding keywords
317 overwriting a expanding keywords
318 % kwexpand x/a should abort
318 % kwexpand x/a should abort
319 abort: outstanding uncommitted changes
319 abort: outstanding uncommitted changes
320 x/a
320 x/a
321 x/a: copy a:779c764182ce5d43e2b1eb66ce06d7b47bfe342e
321 x/a: copy a:779c764182ce5d43e2b1eb66ce06d7b47bfe342e
322 overwriting x/a expanding keywords
322 overwriting x/a expanding keywords
323 committed changeset 3:b4560182a3f9a358179fd2d835c15e9da379c1e4
323 committed changeset 3:b4560182a3f9a358179fd2d835c15e9da379c1e4
324 % cat a
324 % cat a
325 expand $Id: x/a b4560182a3f9 Thu, 01 Jan 1970 00:00:03 +0000 user $
325 expand $Id: x/a b4560182a3f9 Thu, 01 Jan 1970 00:00:03 +0000 user $
326 do not process $Id:
326 do not process $Id:
327 xxx $
327 xxx $
328 $Xinfo: User Name <user@example.com>: xa $
328 $Xinfo: User Name <user@example.com>: xa $
329 % kwshrink a inside directory x
329 % kwshrink a inside directory x
330 overwriting x/a shrinking keywords
330 overwriting x/a shrinking keywords
331 % cat a
331 % cat a
332 expand $Id$
332 expand $Id$
333 do not process $Id:
333 do not process $Id:
334 xxx $
334 xxx $
335 $Xinfo$
335 $Xinfo$
336 % kwexpand nonexistent
336 % kwexpand nonexistent
337 nonexistent:
337 nonexistent:
338 % hg serve
338 % hg serve
339 % expansion
339 % expansion
340 % hgweb file
340 % hgweb file
341 200 Script output follows
341 200 Script output follows
342
342
343 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
343 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
344 do not process $Id:
344 do not process $Id:
345 xxx $
345 xxx $
346 $Xinfo: User Name <user@example.com>: firstline $
346 $Xinfo: User Name <user@example.com>: firstline $
347 % no expansion
347 % no expansion
348 % hgweb annotate
348 % hgweb annotate
349 200 Script output follows
349 200 Script output follows
350
350
351
351
352 user@1: expand $Id$
352 user@1: expand $Id$
353 user@1: do not process $Id:
353 user@1: do not process $Id:
354 user@1: xxx $
354 user@1: xxx $
355 user@2: $Xinfo$
355 user@2: $Xinfo$
356
356
357
357
358
358
359
359
360 % hgweb changeset
360 % hgweb changeset
361 200 Script output follows
361 200 Script output follows
362
362
363
363
364 # HG changeset patch
364 # HG changeset patch
365 # User User Name <user@example.com>
365 # User User Name <user@example.com>
366 # Date 3 0
366 # Date 3 0
367 # Node ID b4560182a3f9a358179fd2d835c15e9da379c1e4
367 # Node ID b4560182a3f9a358179fd2d835c15e9da379c1e4
368 # Parent bb948857c743469b22bbf51f7ec8112279ca5d83
368 # Parent bb948857c743469b22bbf51f7ec8112279ca5d83
369 xa
369 xa
370
370
371 diff -r bb948857c743 -r b4560182a3f9 x/a
371 diff -r bb948857c743 -r b4560182a3f9 x/a
372 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
372 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
373 +++ b/x/a Thu Jan 01 00:00:03 1970 +0000
373 +++ b/x/a Thu Jan 01 00:00:03 1970 +0000
374 @@ -0,0 +1,4 @@
374 @@ -0,0 +1,4 @@
375 +expand $Id$
375 +expand $Id$
376 +do not process $Id:
376 +do not process $Id:
377 +xxx $
377 +xxx $
378 +$Xinfo$
378 +$Xinfo$
379
379
380 % hgweb filediff
380 % hgweb filediff
381 200 Script output follows
381 200 Script output follows
382
382
383
383
384 diff -r ef63ca68695b -r bb948857c743 a
384 diff -r ef63ca68695b -r bb948857c743 a
385 --- a/a Thu Jan 01 00:00:00 1970 +0000
385 --- a/a Thu Jan 01 00:00:00 1970 +0000
386 +++ b/a Thu Jan 01 00:00:02 1970 +0000
386 +++ b/a Thu Jan 01 00:00:02 1970 +0000
387 @@ -1,3 +1,4 @@
387 @@ -1,3 +1,4 @@
388 expand $Id$
388 expand $Id$
389 do not process $Id:
389 do not process $Id:
390 xxx $
390 xxx $
391 +$Xinfo$
391 +$Xinfo$
392
392
393
393
394
394
395
395
396 % errors encountered
396 % errors encountered
397 % merge/resolve
397 % merge/resolve
398 % simplemerge
398 % simplemerge
399 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
399 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
400 created new head
400 created new head
401 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
401 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
402 (branch merge, don't forget to commit)
402 (branch merge, don't forget to commit)
403 $Id: m 27d48ee14f67 Thu, 01 Jan 1970 00:00:00 +0000 test $
403 $Id: m 27d48ee14f67 Thu, 01 Jan 1970 00:00:00 +0000 test $
404 foo
404 foo
405 % conflict
405 % conflict
406 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
406 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
407 created new head
407 created new head
408 merging m
408 merging m
409 warning: conflicts during merge.
409 warning: conflicts during merge.
410 merging m failed!
410 merging m failed!
411 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
411 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
412 use 'hg resolve' to retry unresolved file merges or 'hg update -C' to abandon
412 use 'hg resolve' to retry unresolved file merges or 'hg update -C' to abandon
413 % keyword stays outside conflict zone
413 % keyword stays outside conflict zone
414 $Id$
414 $Id$
415 <<<<<<< local
415 <<<<<<< local
416 bar
416 bar
417 =======
417 =======
418 foo
418 foo
419 >>>>>>> other
419 >>>>>>> other
420 % resolve to local
420 % resolve to local
421 $Id: m 41efa6d38e9b Thu, 01 Jan 1970 00:00:00 +0000 test $
421 $Id: m 41efa6d38e9b Thu, 01 Jan 1970 00:00:00 +0000 test $
422 bar
422 bar
423 % test restricted mode with transplant -b
423 % test restricted mode with transplant -b
424 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
424 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
425 marked working directory as branch foo
425 marked working directory as branch foo
426 created new head
426 created new head
427 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
427 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
428 applying 4aa30d025d50
428 applying 4aa30d025d50
429 4aa30d025d50 transplanted to 5a4da427c162
429 4aa30d025d50 transplanted to 5a4da427c162
430 % no expansion in changeset
430 % no expansion in changeset
431 changeset: 11:5a4da427c162
431 changeset: 11:5a4da427c162
432 tag: tip
432 tag: tip
433 parent: 9:41efa6d38e9b
433 parent: 9:41efa6d38e9b
434 user: test
434 user: test
435 date: Thu Jan 01 00:00:00 1970 +0000
435 date: Thu Jan 01 00:00:00 1970 +0000
436 summary: 9foobranch
436 summary: 9foobranch
437
437
438 diff -r 41efa6d38e9b -r 5a4da427c162 a
438 diff -r 41efa6d38e9b -r 5a4da427c162 a
439 --- a/a Thu Jan 01 00:00:00 1970 +0000
439 --- a/a Thu Jan 01 00:00:00 1970 +0000
440 +++ b/a Thu Jan 01 00:00:00 1970 +0000
440 +++ b/a Thu Jan 01 00:00:00 1970 +0000
441 @@ -1,3 +1,4 @@
441 @@ -1,3 +1,4 @@
442 +foobranch
442 +foobranch
443 expand $Id$
443 expand $Id$
444 do not process $Id:
444 do not process $Id:
445 xxx $
445 xxx $
446
446
447 % expansion in file
447 % expansion in file
448 foobranch
448 foobranch
449 expand $Id: a 5a4da427c162 Thu, 01 Jan 1970 00:00:00 +0000 test $
449 expand $Id: a 5a4da427c162 Thu, 01 Jan 1970 00:00:00 +0000 test $
450 % switch off expansion
450 % switch off expansion
451 % kwshrink with unknown file u
451 % kwshrink with unknown file u
452 overwriting a shrinking keywords
452 overwriting a shrinking keywords
453 overwriting m shrinking keywords
453 overwriting m shrinking keywords
454 overwriting x/a shrinking keywords
454 overwriting x/a shrinking keywords
455 % cat
455 % cat
456 expand $Id$
456 expand $Id$
457 do not process $Id:
457 do not process $Id:
458 xxx $
458 xxx $
459 $Xinfo$
459 $Xinfo$
460 ignore $Id$
460 ignore $Id$
461 % hg cat
461 % hg cat
462 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
462 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
463 do not process $Id:
463 do not process $Id:
464 xxx $
464 xxx $
465 $Xinfo: User Name <user@example.com>: firstline $
465 $Xinfo: User Name <user@example.com>: firstline $
466 ignore $Id$
466 ignore $Id$
467 a
467 a
468 % cat
468 % cat
469 expand $Id$
469 expand $Id$
470 do not process $Id:
470 do not process $Id:
471 xxx $
471 xxx $
472 $Xinfo$
472 $Xinfo$
473 ignore $Id$
473 ignore $Id$
474 % hg cat
474 % hg cat
475 expand $Id$
475 expand $Id$
476 do not process $Id:
476 do not process $Id:
477 xxx $
477 xxx $
478 $Xinfo$
478 $Xinfo$
479 ignore $Id$
479 ignore $Id$
480 a
480 a
General Comments 0
You need to be logged in to leave comments. Login now