##// END OF EJS Templates
keyword: mimic cmdutil.bail_if_changed even more...
Christian Ebert -
r6672:6004eedb default
parent child Browse files
Show More
@@ -1,563 +1,565 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, revlog
81 from mercurial import commands, cmdutil, context, dispatch, filelog, revlog
82 from mercurial import patch, localrepo, templater, templatefilters, util
82 from mercurial import patch, localrepo, templater, templatefilters, util
83 from mercurial.hgweb import webcommands
83 from mercurial.hgweb import webcommands
84 from mercurial.node import nullid, hex
84 from mercurial.node import nullid, hex
85 from mercurial.i18n import _
85 from mercurial.i18n import _
86 import re, shutil, tempfile, time
86 import re, shutil, tempfile, time
87
87
88 commands.optionalrepo += ' kwdemo'
88 commands.optionalrepo += ' kwdemo'
89
89
90 # hg commands that do not act on keywords
90 # hg commands that do not act on keywords
91 nokwcommands = ('add addremove annotate bundle copy export grep incoming init'
91 nokwcommands = ('add addremove annotate bundle copy export grep incoming init'
92 ' log outgoing push rename rollback tip'
92 ' log outgoing push rename rollback tip'
93 ' convert email glog')
93 ' convert email glog')
94
94
95 # hg commands that trigger expansion only when writing to working dir,
95 # hg commands that trigger expansion only when writing to working dir,
96 # not when reading filelog, and unexpand when reading from working dir
96 # not when reading filelog, and unexpand when reading from working dir
97 restricted = 'record qfold qimport qnew qpush qrefresh qrecord'
97 restricted = 'record qfold qimport qnew qpush qrefresh qrecord'
98
98
99 def utcdate(date):
99 def utcdate(date):
100 '''Returns hgdate in cvs-like UTC format.'''
100 '''Returns hgdate in cvs-like UTC format.'''
101 return time.strftime('%Y/%m/%d %H:%M:%S', time.gmtime(date[0]))
101 return time.strftime('%Y/%m/%d %H:%M:%S', time.gmtime(date[0]))
102
102
103 # make keyword tools accessible
103 # make keyword tools accessible
104 kwtools = {'templater': None, 'hgcmd': '', 'inc': [], 'exc': ['.hg*']}
104 kwtools = {'templater': None, 'hgcmd': '', 'inc': [], 'exc': ['.hg*']}
105
105
106
106
107 class kwtemplater(object):
107 class kwtemplater(object):
108 '''
108 '''
109 Sets up keyword templates, corresponding keyword regex, and
109 Sets up keyword templates, corresponding keyword regex, and
110 provides keyword substitution functions.
110 provides keyword substitution functions.
111 '''
111 '''
112 templates = {
112 templates = {
113 'Revision': '{node|short}',
113 'Revision': '{node|short}',
114 'Author': '{author|user}',
114 'Author': '{author|user}',
115 'Date': '{date|utcdate}',
115 'Date': '{date|utcdate}',
116 'RCSFile': '{file|basename},v',
116 'RCSFile': '{file|basename},v',
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.matcher = util.matcher(repo.root,
125 self.matcher = util.matcher(repo.root,
126 inc=kwtools['inc'], exc=kwtools['exc'])[1]
126 inc=kwtools['inc'], exc=kwtools['exc'])[1]
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 kwmaps = [(k, templater.parsestring(v, False))
131 kwmaps = [(k, templater.parsestring(v, False))
132 for (k, v) in kwmaps]
132 for (k, v) in kwmaps]
133 self.templates = dict(kwmaps)
133 self.templates = dict(kwmaps)
134 escaped = map(re.escape, self.templates.keys())
134 escaped = map(re.escape, self.templates.keys())
135 kwpat = r'\$(%s)(: [^$\n\r]*? )??\$' % '|'.join(escaped)
135 kwpat = r'\$(%s)(: [^$\n\r]*? )??\$' % '|'.join(escaped)
136 self.re_kw = re.compile(kwpat)
136 self.re_kw = re.compile(kwpat)
137
137
138 templatefilters.filters['utcdate'] = utcdate
138 templatefilters.filters['utcdate'] = utcdate
139 self.ct = cmdutil.changeset_templater(self.ui, self.repo,
139 self.ct = cmdutil.changeset_templater(self.ui, self.repo,
140 False, '', False)
140 False, '', False)
141
141
142 def getnode(self, path, fnode):
142 def getnode(self, path, fnode):
143 '''Derives changenode from file path and filenode.'''
143 '''Derives changenode from file path and filenode.'''
144 # used by kwfilelog.read and kwexpand
144 # used by kwfilelog.read and kwexpand
145 c = context.filectx(self.repo, path, fileid=fnode)
145 c = context.filectx(self.repo, path, fileid=fnode)
146 return c.node()
146 return c.node()
147
147
148 def substitute(self, data, path, node, subfunc):
148 def substitute(self, data, path, node, subfunc):
149 '''Replaces keywords in data with expanded template.'''
149 '''Replaces keywords in data with expanded template.'''
150 def kwsub(mobj):
150 def kwsub(mobj):
151 kw = mobj.group(1)
151 kw = mobj.group(1)
152 self.ct.use_template(self.templates[kw])
152 self.ct.use_template(self.templates[kw])
153 self.ui.pushbuffer()
153 self.ui.pushbuffer()
154 self.ct.show(changenode=node, root=self.repo.root, file=path)
154 self.ct.show(changenode=node, root=self.repo.root, file=path)
155 ekw = templatefilters.firstline(self.ui.popbuffer())
155 ekw = templatefilters.firstline(self.ui.popbuffer())
156 return '$%s: %s $' % (kw, ekw)
156 return '$%s: %s $' % (kw, ekw)
157 return subfunc(kwsub, data)
157 return subfunc(kwsub, data)
158
158
159 def expand(self, path, node, data):
159 def expand(self, path, node, data):
160 '''Returns data with keywords expanded.'''
160 '''Returns data with keywords expanded.'''
161 if not self.restrict and self.matcher(path) and not util.binary(data):
161 if not self.restrict and self.matcher(path) and not util.binary(data):
162 changenode = self.getnode(path, node)
162 changenode = self.getnode(path, node)
163 return self.substitute(data, path, changenode, self.re_kw.sub)
163 return self.substitute(data, path, changenode, self.re_kw.sub)
164 return data
164 return data
165
165
166 def iskwfile(self, path, islink):
166 def iskwfile(self, path, islink):
167 '''Returns true if path matches [keyword] pattern
167 '''Returns true if path matches [keyword] pattern
168 and is not a symbolic link.
168 and is not a symbolic link.
169 Caveat: localrepository._link fails on Windows.'''
169 Caveat: localrepository._link fails on Windows.'''
170 return self.matcher(path) and not islink(path)
170 return self.matcher(path) and not islink(path)
171
171
172 def overwrite(self, node, expand, files):
172 def overwrite(self, node, expand, files):
173 '''Overwrites selected files expanding/shrinking keywords.'''
173 '''Overwrites selected files expanding/shrinking keywords.'''
174 ctx = self.repo.changectx(node)
174 ctx = self.repo.changectx(node)
175 mf = ctx.manifest()
175 mf = ctx.manifest()
176 if node is not None: # commit
176 if node is not None: # commit
177 files = [f for f in ctx.files() if f in mf]
177 files = [f for f in ctx.files() if f in mf]
178 notify = self.ui.debug
178 notify = self.ui.debug
179 else: # kwexpand/kwshrink
179 else: # kwexpand/kwshrink
180 notify = self.ui.note
180 notify = self.ui.note
181 candidates = [f for f in files if self.iskwfile(f, mf.linkf)]
181 candidates = [f for f in files if self.iskwfile(f, mf.linkf)]
182 if candidates:
182 if candidates:
183 self.restrict = True # do not expand when reading
183 self.restrict = True # do not expand when reading
184 candidates.sort()
184 candidates.sort()
185 action = expand and 'expanding' or 'shrinking'
185 action = expand and 'expanding' or 'shrinking'
186 for f in candidates:
186 for f in candidates:
187 fp = self.repo.file(f)
187 fp = self.repo.file(f)
188 data = fp.read(mf[f])
188 data = fp.read(mf[f])
189 if util.binary(data):
189 if util.binary(data):
190 continue
190 continue
191 if expand:
191 if expand:
192 changenode = node or self.getnode(f, mf[f])
192 changenode = node or self.getnode(f, mf[f])
193 data, found = self.substitute(data, f, changenode,
193 data, found = self.substitute(data, f, changenode,
194 self.re_kw.subn)
194 self.re_kw.subn)
195 else:
195 else:
196 found = self.re_kw.search(data)
196 found = self.re_kw.search(data)
197 if found:
197 if found:
198 notify(_('overwriting %s %s keywords\n') % (f, action))
198 notify(_('overwriting %s %s keywords\n') % (f, action))
199 self.repo.wwrite(f, data, mf.flags(f))
199 self.repo.wwrite(f, data, mf.flags(f))
200 self.repo.dirstate.normal(f)
200 self.repo.dirstate.normal(f)
201 self.restrict = False
201 self.restrict = False
202
202
203 def shrinktext(self, text):
203 def shrinktext(self, text):
204 '''Unconditionally removes all keyword substitutions from text.'''
204 '''Unconditionally removes all keyword substitutions from text.'''
205 return self.re_kw.sub(r'$\1$', text)
205 return self.re_kw.sub(r'$\1$', text)
206
206
207 def shrink(self, fname, text):
207 def shrink(self, fname, text):
208 '''Returns text with all keyword substitutions removed.'''
208 '''Returns text with all keyword substitutions removed.'''
209 if self.matcher(fname) and not util.binary(text):
209 if self.matcher(fname) and not util.binary(text):
210 return self.shrinktext(text)
210 return self.shrinktext(text)
211 return text
211 return text
212
212
213 def shrinklines(self, fname, lines):
213 def shrinklines(self, fname, lines):
214 '''Returns lines with keyword substitutions removed.'''
214 '''Returns lines with keyword substitutions removed.'''
215 if self.matcher(fname):
215 if self.matcher(fname):
216 text = ''.join(lines)
216 text = ''.join(lines)
217 if not util.binary(text):
217 if not util.binary(text):
218 return self.shrinktext(text).splitlines(True)
218 return self.shrinktext(text).splitlines(True)
219 return lines
219 return lines
220
220
221 def wread(self, fname, data):
221 def wread(self, fname, data):
222 '''If in restricted mode returns data read from wdir with
222 '''If in restricted mode returns data read from wdir with
223 keyword substitutions removed.'''
223 keyword substitutions removed.'''
224 return self.restrict and self.shrink(fname, data) or data
224 return self.restrict and self.shrink(fname, data) or data
225
225
226 class kwfilelog(filelog.filelog):
226 class kwfilelog(filelog.filelog):
227 '''
227 '''
228 Subclass of filelog to hook into its read, add, cmp methods.
228 Subclass of filelog to hook into its read, add, cmp methods.
229 Keywords are "stored" unexpanded, and processed on reading.
229 Keywords are "stored" unexpanded, and processed on reading.
230 '''
230 '''
231 def __init__(self, opener, kwt, path):
231 def __init__(self, opener, kwt, path):
232 super(kwfilelog, self).__init__(opener, path)
232 super(kwfilelog, self).__init__(opener, path)
233 self.kwt = kwt
233 self.kwt = kwt
234 self.path = path
234 self.path = path
235
235
236 def read(self, node):
236 def read(self, node):
237 '''Expands keywords when reading filelog.'''
237 '''Expands keywords when reading filelog.'''
238 data = super(kwfilelog, self).read(node)
238 data = super(kwfilelog, self).read(node)
239 return self.kwt.expand(self.path, node, data)
239 return self.kwt.expand(self.path, node, data)
240
240
241 def add(self, text, meta, tr, link, p1=None, p2=None):
241 def add(self, text, meta, tr, link, p1=None, p2=None):
242 '''Removes keyword substitutions when adding to filelog.'''
242 '''Removes keyword substitutions when adding to filelog.'''
243 text = self.kwt.shrink(self.path, text)
243 text = self.kwt.shrink(self.path, text)
244 return super(kwfilelog, self).add(text, meta, tr, link, p1, p2)
244 return super(kwfilelog, self).add(text, meta, tr, link, p1, p2)
245
245
246 def cmp(self, node, text):
246 def cmp(self, node, text):
247 '''Removes keyword substitutions for comparison.'''
247 '''Removes keyword substitutions for comparison.'''
248 text = self.kwt.shrink(self.path, text)
248 text = self.kwt.shrink(self.path, text)
249 if self.renamed(node):
249 if self.renamed(node):
250 t2 = super(kwfilelog, self).read(node)
250 t2 = super(kwfilelog, self).read(node)
251 return t2 != text
251 return t2 != text
252 return revlog.revlog.cmp(self, node, text)
252 return revlog.revlog.cmp(self, node, text)
253
253
254 def _status(ui, repo, kwt, *pats, **opts):
254 def _status(ui, repo, kwt, *pats, **opts):
255 '''Bails out if [keyword] configuration is not active.
255 '''Bails out if [keyword] configuration is not active.
256 Returns status of working directory.'''
256 Returns status of working directory.'''
257 if kwt:
257 if kwt:
258 matcher = cmdutil.match(repo, pats, opts)
258 matcher = cmdutil.match(repo, pats, opts)
259 return repo.status(match=matcher, list_clean=True)
259 return repo.status(match=matcher, list_clean=True)
260 if ui.configitems('keyword'):
260 if ui.configitems('keyword'):
261 raise util.Abort(_('[keyword] patterns cannot match'))
261 raise util.Abort(_('[keyword] patterns cannot match'))
262 raise util.Abort(_('no [keyword] patterns configured'))
262 raise util.Abort(_('no [keyword] patterns configured'))
263
263
264 def _kwfwrite(ui, repo, expand, *pats, **opts):
264 def _kwfwrite(ui, repo, expand, *pats, **opts):
265 '''Selects files and passes them to kwtemplater.overwrite.'''
265 '''Selects files and passes them to kwtemplater.overwrite.'''
266 if repo.dirstate.parents()[1] != nullid:
267 raise util.Abort(_('outstanding uncommitted merge'))
266 kwt = kwtools['templater']
268 kwt = kwtools['templater']
267 status = _status(ui, repo, kwt, *pats, **opts)
269 status = _status(ui, repo, kwt, *pats, **opts)
268 modified, added, removed, deleted, unknown, ignored, clean = status
270 modified, added, removed, deleted, unknown, ignored, clean = status
269 if modified or added or removed or deleted:
271 if modified or added or removed or deleted:
270 raise util.Abort(_('outstanding uncommitted changes in given files'))
272 raise util.Abort(_('outstanding uncommitted changes'))
271 wlock = lock = None
273 wlock = lock = None
272 try:
274 try:
273 wlock = repo.wlock()
275 wlock = repo.wlock()
274 lock = repo.lock()
276 lock = repo.lock()
275 kwt.overwrite(None, expand, clean)
277 kwt.overwrite(None, expand, clean)
276 finally:
278 finally:
277 del wlock, lock
279 del wlock, lock
278
280
279
281
280 def demo(ui, repo, *args, **opts):
282 def demo(ui, repo, *args, **opts):
281 '''print [keywordmaps] configuration and an expansion example
283 '''print [keywordmaps] configuration and an expansion example
282
284
283 Show current, custom, or default keyword template maps
285 Show current, custom, or default keyword template maps
284 and their expansion.
286 and their expansion.
285
287
286 Extend current configuration by specifying maps as arguments
288 Extend current configuration by specifying maps as arguments
287 and optionally by reading from an additional hgrc file.
289 and optionally by reading from an additional hgrc file.
288
290
289 Override current keyword template maps with "default" option.
291 Override current keyword template maps with "default" option.
290 '''
292 '''
291 def demostatus(stat):
293 def demostatus(stat):
292 ui.status(_('\n\t%s\n') % stat)
294 ui.status(_('\n\t%s\n') % stat)
293
295
294 def demoitems(section, items):
296 def demoitems(section, items):
295 ui.write('[%s]\n' % section)
297 ui.write('[%s]\n' % section)
296 for k, v in items:
298 for k, v in items:
297 ui.write('%s = %s\n' % (k, v))
299 ui.write('%s = %s\n' % (k, v))
298
300
299 msg = 'hg keyword config and expansion example'
301 msg = 'hg keyword config and expansion example'
300 kwstatus = 'current'
302 kwstatus = 'current'
301 fn = 'demo.txt'
303 fn = 'demo.txt'
302 branchname = 'demobranch'
304 branchname = 'demobranch'
303 tmpdir = tempfile.mkdtemp('', 'kwdemo.')
305 tmpdir = tempfile.mkdtemp('', 'kwdemo.')
304 ui.note(_('creating temporary repo at %s\n') % tmpdir)
306 ui.note(_('creating temporary repo at %s\n') % tmpdir)
305 repo = localrepo.localrepository(ui, tmpdir, True)
307 repo = localrepo.localrepository(ui, tmpdir, True)
306 ui.setconfig('keyword', fn, '')
308 ui.setconfig('keyword', fn, '')
307 if args or opts.get('rcfile'):
309 if args or opts.get('rcfile'):
308 kwstatus = 'custom'
310 kwstatus = 'custom'
309 if opts.get('rcfile'):
311 if opts.get('rcfile'):
310 ui.readconfig(opts.get('rcfile'))
312 ui.readconfig(opts.get('rcfile'))
311 if opts.get('default'):
313 if opts.get('default'):
312 kwstatus = 'default'
314 kwstatus = 'default'
313 kwmaps = kwtemplater.templates
315 kwmaps = kwtemplater.templates
314 if ui.configitems('keywordmaps'):
316 if ui.configitems('keywordmaps'):
315 # override maps from optional rcfile
317 # override maps from optional rcfile
316 for k, v in kwmaps.iteritems():
318 for k, v in kwmaps.iteritems():
317 ui.setconfig('keywordmaps', k, v)
319 ui.setconfig('keywordmaps', k, v)
318 elif args:
320 elif args:
319 # simulate hgrc parsing
321 # simulate hgrc parsing
320 rcmaps = ['[keywordmaps]\n'] + [a + '\n' for a in args]
322 rcmaps = ['[keywordmaps]\n'] + [a + '\n' for a in args]
321 fp = repo.opener('hgrc', 'w')
323 fp = repo.opener('hgrc', 'w')
322 fp.writelines(rcmaps)
324 fp.writelines(rcmaps)
323 fp.close()
325 fp.close()
324 ui.readconfig(repo.join('hgrc'))
326 ui.readconfig(repo.join('hgrc'))
325 if not opts.get('default'):
327 if not opts.get('default'):
326 kwmaps = dict(ui.configitems('keywordmaps')) or kwtemplater.templates
328 kwmaps = dict(ui.configitems('keywordmaps')) or kwtemplater.templates
327 uisetup(ui)
329 uisetup(ui)
328 reposetup(ui, repo)
330 reposetup(ui, repo)
329 for k, v in ui.configitems('extensions'):
331 for k, v in ui.configitems('extensions'):
330 if k.endswith('keyword'):
332 if k.endswith('keyword'):
331 extension = '%s = %s' % (k, v)
333 extension = '%s = %s' % (k, v)
332 break
334 break
333 demostatus('config using %s keyword template maps' % kwstatus)
335 demostatus('config using %s keyword template maps' % kwstatus)
334 ui.write('[extensions]\n%s\n' % extension)
336 ui.write('[extensions]\n%s\n' % extension)
335 demoitems('keyword', ui.configitems('keyword'))
337 demoitems('keyword', ui.configitems('keyword'))
336 demoitems('keywordmaps', kwmaps.iteritems())
338 demoitems('keywordmaps', kwmaps.iteritems())
337 keywords = '$' + '$\n$'.join(kwmaps.keys()) + '$\n'
339 keywords = '$' + '$\n$'.join(kwmaps.keys()) + '$\n'
338 repo.wopener(fn, 'w').write(keywords)
340 repo.wopener(fn, 'w').write(keywords)
339 repo.add([fn])
341 repo.add([fn])
340 path = repo.wjoin(fn)
342 path = repo.wjoin(fn)
341 ui.note(_('\n%s keywords written to %s:\n') % (kwstatus, path))
343 ui.note(_('\n%s keywords written to %s:\n') % (kwstatus, path))
342 ui.note(keywords)
344 ui.note(keywords)
343 ui.note('\nhg -R "%s" branch "%s"\n' % (tmpdir, branchname))
345 ui.note('\nhg -R "%s" branch "%s"\n' % (tmpdir, branchname))
344 # silence branch command if not verbose
346 # silence branch command if not verbose
345 quiet = ui.quiet
347 quiet = ui.quiet
346 ui.quiet = not ui.verbose
348 ui.quiet = not ui.verbose
347 commands.branch(ui, repo, branchname)
349 commands.branch(ui, repo, branchname)
348 ui.quiet = quiet
350 ui.quiet = quiet
349 for name, cmd in ui.configitems('hooks'):
351 for name, cmd in ui.configitems('hooks'):
350 if name.split('.', 1)[0].find('commit') > -1:
352 if name.split('.', 1)[0].find('commit') > -1:
351 repo.ui.setconfig('hooks', name, '')
353 repo.ui.setconfig('hooks', name, '')
352 ui.note(_('unhooked all commit hooks\n'))
354 ui.note(_('unhooked all commit hooks\n'))
353 ui.note('hg -R "%s" ci -m "%s"\n' % (tmpdir, msg))
355 ui.note('hg -R "%s" ci -m "%s"\n' % (tmpdir, msg))
354 repo.commit(text=msg)
356 repo.commit(text=msg)
355 format = ui.verbose and ' in %s' % path or ''
357 format = ui.verbose and ' in %s' % path or ''
356 demostatus('%s keywords expanded%s' % (kwstatus, format))
358 demostatus('%s keywords expanded%s' % (kwstatus, format))
357 ui.write(repo.wread(fn))
359 ui.write(repo.wread(fn))
358 ui.debug(_('\nremoving temporary repo %s\n') % tmpdir)
360 ui.debug(_('\nremoving temporary repo %s\n') % tmpdir)
359 shutil.rmtree(tmpdir, ignore_errors=True)
361 shutil.rmtree(tmpdir, ignore_errors=True)
360
362
361 def expand(ui, repo, *pats, **opts):
363 def expand(ui, repo, *pats, **opts):
362 '''expand keywords in working directory
364 '''expand keywords in working directory
363
365
364 Run after (re)enabling keyword expansion.
366 Run after (re)enabling keyword expansion.
365
367
366 kwexpand refuses to run if given files contain local changes.
368 kwexpand refuses to run if given files contain local changes.
367 '''
369 '''
368 # 3rd argument sets expansion to True
370 # 3rd argument sets expansion to True
369 _kwfwrite(ui, repo, True, *pats, **opts)
371 _kwfwrite(ui, repo, True, *pats, **opts)
370
372
371 def files(ui, repo, *pats, **opts):
373 def files(ui, repo, *pats, **opts):
372 '''print files currently configured for keyword expansion
374 '''print files currently configured for keyword expansion
373
375
374 Crosscheck which files in working directory are potential targets for
376 Crosscheck which files in working directory are potential targets for
375 keyword expansion.
377 keyword expansion.
376 That is, files matched by [keyword] config patterns but not symlinks.
378 That is, files matched by [keyword] config patterns but not symlinks.
377 '''
379 '''
378 kwt = kwtools['templater']
380 kwt = kwtools['templater']
379 status = _status(ui, repo, kwt, *pats, **opts)
381 status = _status(ui, repo, kwt, *pats, **opts)
380 modified, added, removed, deleted, unknown, ignored, clean = status
382 modified, added, removed, deleted, unknown, ignored, clean = status
381 files = modified + added + clean
383 files = modified + added + clean
382 if opts.get('untracked'):
384 if opts.get('untracked'):
383 files += unknown
385 files += unknown
384 files.sort()
386 files.sort()
385 wctx = repo.workingctx()
387 wctx = repo.workingctx()
386 islink = lambda p: 'l' in wctx.fileflags(p)
388 islink = lambda p: 'l' in wctx.fileflags(p)
387 kwfiles = [f for f in files if kwt.iskwfile(f, islink)]
389 kwfiles = [f for f in files if kwt.iskwfile(f, islink)]
388 cwd = pats and repo.getcwd() or ''
390 cwd = pats and repo.getcwd() or ''
389 kwfstats = not opts.get('ignore') and (('K', kwfiles),) or ()
391 kwfstats = not opts.get('ignore') and (('K', kwfiles),) or ()
390 if opts.get('all') or opts.get('ignore'):
392 if opts.get('all') or opts.get('ignore'):
391 kwfstats += (('I', [f for f in files if f not in kwfiles]),)
393 kwfstats += (('I', [f for f in files if f not in kwfiles]),)
392 for char, filenames in kwfstats:
394 for char, filenames in kwfstats:
393 format = (opts.get('all') or ui.verbose) and '%s %%s\n' % char or '%s\n'
395 format = (opts.get('all') or ui.verbose) and '%s %%s\n' % char or '%s\n'
394 for f in filenames:
396 for f in filenames:
395 ui.write(format % repo.pathto(f, cwd))
397 ui.write(format % repo.pathto(f, cwd))
396
398
397 def shrink(ui, repo, *pats, **opts):
399 def shrink(ui, repo, *pats, **opts):
398 '''revert expanded keywords in working directory
400 '''revert expanded keywords in working directory
399
401
400 Run before changing/disabling active keywords
402 Run before changing/disabling active keywords
401 or if you experience problems with "hg import" or "hg merge".
403 or if you experience problems with "hg import" or "hg merge".
402
404
403 kwshrink refuses to run if given files contain local changes.
405 kwshrink refuses to run if given files contain local changes.
404 '''
406 '''
405 # 3rd argument sets expansion to False
407 # 3rd argument sets expansion to False
406 _kwfwrite(ui, repo, False, *pats, **opts)
408 _kwfwrite(ui, repo, False, *pats, **opts)
407
409
408
410
409 def uisetup(ui):
411 def uisetup(ui):
410 '''Collects [keyword] config in kwtools.
412 '''Collects [keyword] config in kwtools.
411 Monkeypatches dispatch._parse if needed.'''
413 Monkeypatches dispatch._parse if needed.'''
412
414
413 for pat, opt in ui.configitems('keyword'):
415 for pat, opt in ui.configitems('keyword'):
414 if opt != 'ignore':
416 if opt != 'ignore':
415 kwtools['inc'].append(pat)
417 kwtools['inc'].append(pat)
416 else:
418 else:
417 kwtools['exc'].append(pat)
419 kwtools['exc'].append(pat)
418
420
419 if kwtools['inc']:
421 if kwtools['inc']:
420 def kwdispatch_parse(ui, args):
422 def kwdispatch_parse(ui, args):
421 '''Monkeypatch dispatch._parse to obtain running hg command.'''
423 '''Monkeypatch dispatch._parse to obtain running hg command.'''
422 cmd, func, args, options, cmdoptions = dispatch_parse(ui, args)
424 cmd, func, args, options, cmdoptions = dispatch_parse(ui, args)
423 kwtools['hgcmd'] = cmd
425 kwtools['hgcmd'] = cmd
424 return cmd, func, args, options, cmdoptions
426 return cmd, func, args, options, cmdoptions
425
427
426 dispatch_parse = dispatch._parse
428 dispatch_parse = dispatch._parse
427 dispatch._parse = kwdispatch_parse
429 dispatch._parse = kwdispatch_parse
428
430
429 def reposetup(ui, repo):
431 def reposetup(ui, repo):
430 '''Sets up repo as kwrepo for keyword substitution.
432 '''Sets up repo as kwrepo for keyword substitution.
431 Overrides file method to return kwfilelog instead of filelog
433 Overrides file method to return kwfilelog instead of filelog
432 if file matches user configuration.
434 if file matches user configuration.
433 Wraps commit to overwrite configured files with updated
435 Wraps commit to overwrite configured files with updated
434 keyword substitutions.
436 keyword substitutions.
435 Monkeypatches patch and webcommands.'''
437 Monkeypatches patch and webcommands.'''
436
438
437 try:
439 try:
438 if (not repo.local() or not kwtools['inc']
440 if (not repo.local() or not kwtools['inc']
439 or kwtools['hgcmd'] in nokwcommands.split()
441 or kwtools['hgcmd'] in nokwcommands.split()
440 or '.hg' in util.splitpath(repo.root)
442 or '.hg' in util.splitpath(repo.root)
441 or repo._url.startswith('bundle:')):
443 or repo._url.startswith('bundle:')):
442 return
444 return
443 except AttributeError:
445 except AttributeError:
444 pass
446 pass
445
447
446 kwtools['templater'] = kwt = kwtemplater(ui, repo)
448 kwtools['templater'] = kwt = kwtemplater(ui, repo)
447
449
448 class kwrepo(repo.__class__):
450 class kwrepo(repo.__class__):
449 def file(self, f):
451 def file(self, f):
450 if f[0] == '/':
452 if f[0] == '/':
451 f = f[1:]
453 f = f[1:]
452 return kwfilelog(self.sopener, kwt, f)
454 return kwfilelog(self.sopener, kwt, f)
453
455
454 def wread(self, filename):
456 def wread(self, filename):
455 data = super(kwrepo, self).wread(filename)
457 data = super(kwrepo, self).wread(filename)
456 return kwt.wread(filename, data)
458 return kwt.wread(filename, data)
457
459
458 def commit(self, files=None, text='', user=None, date=None,
460 def commit(self, files=None, text='', user=None, date=None,
459 match=None, force=False, force_editor=False,
461 match=None, force=False, force_editor=False,
460 p1=None, p2=None, extra={}, empty_ok=False):
462 p1=None, p2=None, extra={}, empty_ok=False):
461 wlock = lock = None
463 wlock = lock = None
462 _p1 = _p2 = None
464 _p1 = _p2 = None
463 try:
465 try:
464 wlock = self.wlock()
466 wlock = self.wlock()
465 lock = self.lock()
467 lock = self.lock()
466 # store and postpone commit hooks
468 # store and postpone commit hooks
467 commithooks = {}
469 commithooks = {}
468 for name, cmd in ui.configitems('hooks'):
470 for name, cmd in ui.configitems('hooks'):
469 if name.split('.', 1)[0] == 'commit':
471 if name.split('.', 1)[0] == 'commit':
470 commithooks[name] = cmd
472 commithooks[name] = cmd
471 ui.setconfig('hooks', name, None)
473 ui.setconfig('hooks', name, None)
472 if commithooks:
474 if commithooks:
473 # store parents for commit hook environment
475 # store parents for commit hook environment
474 if p1 is None:
476 if p1 is None:
475 _p1, _p2 = repo.dirstate.parents()
477 _p1, _p2 = repo.dirstate.parents()
476 else:
478 else:
477 _p1, _p2 = p1, p2 or nullid
479 _p1, _p2 = p1, p2 or nullid
478 _p1 = hex(_p1)
480 _p1 = hex(_p1)
479 if _p2 == nullid:
481 if _p2 == nullid:
480 _p2 = ''
482 _p2 = ''
481 else:
483 else:
482 _p2 = hex(_p2)
484 _p2 = hex(_p2)
483
485
484 n = super(kwrepo, self).commit(files, text, user, date, match,
486 n = super(kwrepo, self).commit(files, text, user, date, match,
485 force, force_editor, p1, p2,
487 force, force_editor, p1, p2,
486 extra, empty_ok)
488 extra, empty_ok)
487
489
488 # restore commit hooks
490 # restore commit hooks
489 for name, cmd in commithooks.iteritems():
491 for name, cmd in commithooks.iteritems():
490 ui.setconfig('hooks', name, cmd)
492 ui.setconfig('hooks', name, cmd)
491 if n is not None:
493 if n is not None:
492 kwt.overwrite(n, True, None)
494 kwt.overwrite(n, True, None)
493 repo.hook('commit', node=n, parent1=_p1, parent2=_p2)
495 repo.hook('commit', node=n, parent1=_p1, parent2=_p2)
494 return n
496 return n
495 finally:
497 finally:
496 del wlock, lock
498 del wlock, lock
497
499
498 # monkeypatches
500 # monkeypatches
499 def kwpatchfile_init(self, ui, fname, missing=False):
501 def kwpatchfile_init(self, ui, fname, missing=False):
500 '''Monkeypatch/wrap patch.patchfile.__init__ to avoid
502 '''Monkeypatch/wrap patch.patchfile.__init__ to avoid
501 rejects or conflicts due to expanded keywords in working dir.'''
503 rejects or conflicts due to expanded keywords in working dir.'''
502 patchfile_init(self, ui, fname, missing)
504 patchfile_init(self, ui, fname, missing)
503 # shrink keywords read from working dir
505 # shrink keywords read from working dir
504 self.lines = kwt.shrinklines(self.fname, self.lines)
506 self.lines = kwt.shrinklines(self.fname, self.lines)
505
507
506 def kw_diff(repo, node1=None, node2=None, match=None,
508 def kw_diff(repo, node1=None, node2=None, match=None,
507 fp=None, changes=None, opts=None):
509 fp=None, changes=None, opts=None):
508 '''Monkeypatch patch.diff to avoid expansion except when
510 '''Monkeypatch patch.diff to avoid expansion except when
509 comparing against working dir.'''
511 comparing against working dir.'''
510 if node2 is not None:
512 if node2 is not None:
511 kwt.matcher = util.never
513 kwt.matcher = util.never
512 elif node1 is not None and node1 != repo.changectx().node():
514 elif node1 is not None and node1 != repo.changectx().node():
513 kwt.restrict = True
515 kwt.restrict = True
514 patch_diff(repo, node1, node2, match, fp, changes, opts)
516 patch_diff(repo, node1, node2, match, fp, changes, opts)
515
517
516 def kwweb_annotate(web, req, tmpl):
518 def kwweb_annotate(web, req, tmpl):
517 '''Wraps webcommands.annotate turning off keyword expansion.'''
519 '''Wraps webcommands.annotate turning off keyword expansion.'''
518 kwt.matcher = util.never
520 kwt.matcher = util.never
519 return webcommands_annotate(web, req, tmpl)
521 return webcommands_annotate(web, req, tmpl)
520
522
521 def kwweb_changeset(web, req, tmpl):
523 def kwweb_changeset(web, req, tmpl):
522 '''Wraps webcommands.changeset turning off keyword expansion.'''
524 '''Wraps webcommands.changeset turning off keyword expansion.'''
523 kwt.matcher = util.never
525 kwt.matcher = util.never
524 return webcommands_changeset(web, req, tmpl)
526 return webcommands_changeset(web, req, tmpl)
525
527
526 def kwweb_filediff(web, req, tmpl):
528 def kwweb_filediff(web, req, tmpl):
527 '''Wraps webcommands.filediff turning off keyword expansion.'''
529 '''Wraps webcommands.filediff turning off keyword expansion.'''
528 kwt.matcher = util.never
530 kwt.matcher = util.never
529 return webcommands_filediff(web, req, tmpl)
531 return webcommands_filediff(web, req, tmpl)
530
532
531 repo.__class__ = kwrepo
533 repo.__class__ = kwrepo
532
534
533 patchfile_init = patch.patchfile.__init__
535 patchfile_init = patch.patchfile.__init__
534 patch_diff = patch.diff
536 patch_diff = patch.diff
535 webcommands_annotate = webcommands.annotate
537 webcommands_annotate = webcommands.annotate
536 webcommands_changeset = webcommands.changeset
538 webcommands_changeset = webcommands.changeset
537 webcommands_filediff = webcommands.filediff
539 webcommands_filediff = webcommands.filediff
538
540
539 patch.patchfile.__init__ = kwpatchfile_init
541 patch.patchfile.__init__ = kwpatchfile_init
540 patch.diff = kw_diff
542 patch.diff = kw_diff
541 webcommands.annotate = kwweb_annotate
543 webcommands.annotate = kwweb_annotate
542 webcommands.changeset = webcommands.rev = kwweb_changeset
544 webcommands.changeset = webcommands.rev = kwweb_changeset
543 webcommands.filediff = webcommands.diff = kwweb_filediff
545 webcommands.filediff = webcommands.diff = kwweb_filediff
544
546
545
547
546 cmdtable = {
548 cmdtable = {
547 'kwdemo':
549 'kwdemo':
548 (demo,
550 (demo,
549 [('d', 'default', None, _('show default keyword template maps')),
551 [('d', 'default', None, _('show default keyword template maps')),
550 ('f', 'rcfile', [], _('read maps from rcfile'))],
552 ('f', 'rcfile', [], _('read maps from rcfile'))],
551 _('hg kwdemo [-d] [-f RCFILE] [TEMPLATEMAP]...')),
553 _('hg kwdemo [-d] [-f RCFILE] [TEMPLATEMAP]...')),
552 'kwexpand': (expand, commands.walkopts,
554 'kwexpand': (expand, commands.walkopts,
553 _('hg kwexpand [OPTION]... [FILE]...')),
555 _('hg kwexpand [OPTION]... [FILE]...')),
554 'kwfiles':
556 'kwfiles':
555 (files,
557 (files,
556 [('a', 'all', None, _('show keyword status flags of all files')),
558 [('a', 'all', None, _('show keyword status flags of all files')),
557 ('i', 'ignore', None, _('show files excluded from expansion')),
559 ('i', 'ignore', None, _('show files excluded from expansion')),
558 ('u', 'untracked', None, _('additionally show untracked files')),
560 ('u', 'untracked', None, _('additionally show untracked files')),
559 ] + commands.walkopts,
561 ] + commands.walkopts,
560 _('hg kwfiles [OPTION]... [FILE]...')),
562 _('hg kwfiles [OPTION]... [FILE]...')),
561 'kwshrink': (shrink, commands.walkopts,
563 'kwshrink': (shrink, commands.walkopts,
562 _('hg kwshrink [OPTION]... [FILE]...')),
564 _('hg kwshrink [OPTION]... [FILE]...')),
563 }
565 }
@@ -1,436 +1,436 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 pulling from test-keyword.hg
87 pulling from test-keyword.hg
88 requesting all changes
88 requesting all changes
89 adding changesets
89 adding changesets
90 adding manifests
90 adding manifests
91 adding file changes
91 adding file changes
92 added 1 changesets with 1 changes to 1 files
92 added 1 changesets with 1 changes to 1 files
93 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
93 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
94 % cat
94 % cat
95 expand $Id$
95 expand $Id$
96 do not process $Id:
96 do not process $Id:
97 xxx $
97 xxx $
98 ignore $Id$
98 ignore $Id$
99 % addremove
99 % addremove
100 adding a
100 adding a
101 adding b
101 adding b
102 % status
102 % status
103 A a
103 A a
104 A b
104 A b
105 % default keyword expansion including commit hook
105 % default keyword expansion including commit hook
106 % interrupted commit should not change state or run commit hook
106 % interrupted commit should not change state or run commit hook
107 a
107 a
108 b
108 b
109 transaction abort!
109 transaction abort!
110 rollback completed
110 rollback completed
111 abort: empty commit message
111 abort: empty commit message
112 % status
112 % status
113 A a
113 A a
114 A b
114 A b
115 % commit
115 % commit
116 a
116 a
117 b
117 b
118 overwriting a expanding keywords
118 overwriting a expanding keywords
119 running hook commit.test: cp a hooktest
119 running hook commit.test: cp a hooktest
120 % status
120 % status
121 ? hooktest
121 ? hooktest
122 % identify
122 % identify
123 ef63ca68695b
123 ef63ca68695b
124 % cat
124 % cat
125 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
125 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
126 do not process $Id:
126 do not process $Id:
127 xxx $
127 xxx $
128 ignore $Id$
128 ignore $Id$
129 % hg cat
129 % hg cat
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 ignore $Id$
133 ignore $Id$
134 a
134 a
135 % diff a hooktest
135 % diff a hooktest
136 % removing commit hook from config
136 % removing commit hook from config
137 % bundle
137 % bundle
138 2 changesets found
138 2 changesets found
139 % notify on pull to check whether keywords stay as is in email
139 % notify on pull to check whether keywords stay as is in email
140 % ie. if patch.diff wrapper acts as it should
140 % ie. if patch.diff wrapper acts as it should
141 % pull from bundle
141 % pull from bundle
142 pulling from ../kw.hg
142 pulling from ../kw.hg
143 requesting all changes
143 requesting all changes
144 adding changesets
144 adding changesets
145 adding manifests
145 adding manifests
146 adding file changes
146 adding file changes
147 added 2 changesets with 3 changes to 3 files
147 added 2 changesets with 3 changes to 3 files
148
148
149 diff -r 000000000000 -r a2392c293916 sym
149 diff -r 000000000000 -r a2392c293916 sym
150 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
150 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
151 +++ b/sym Sat Feb 09 20:25:47 2008 +0100
151 +++ b/sym Sat Feb 09 20:25:47 2008 +0100
152 @@ -0,0 +1,1 @@
152 @@ -0,0 +1,1 @@
153 +a
153 +a
154 \ No newline at end of file
154 \ No newline at end of file
155
155
156 diff -r a2392c293916 -r ef63ca68695b a
156 diff -r a2392c293916 -r ef63ca68695b a
157 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
157 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
158 +++ b/a Thu Jan 01 00:00:00 1970 +0000
158 +++ b/a Thu Jan 01 00:00:00 1970 +0000
159 @@ -0,0 +1,3 @@
159 @@ -0,0 +1,3 @@
160 +expand $Id$
160 +expand $Id$
161 +do not process $Id:
161 +do not process $Id:
162 +xxx $
162 +xxx $
163 diff -r a2392c293916 -r ef63ca68695b b
163 diff -r a2392c293916 -r ef63ca68695b b
164 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
164 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
165 +++ b/b Thu Jan 01 00:00:00 1970 +0000
165 +++ b/b Thu Jan 01 00:00:00 1970 +0000
166 @@ -0,0 +1,1 @@
166 @@ -0,0 +1,1 @@
167 +ignore $Id$
167 +ignore $Id$
168 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
168 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
169 % remove notify config
169 % remove notify config
170 % touch
170 % touch
171 % status
171 % status
172 % update
172 % update
173 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
173 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
174 % cat
174 % cat
175 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
175 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
176 do not process $Id:
176 do not process $Id:
177 xxx $
177 xxx $
178 ignore $Id$
178 ignore $Id$
179 % check whether expansion is filewise
179 % check whether expansion is filewise
180 % commit c
180 % commit c
181 adding c
181 adding c
182 % force expansion
182 % force expansion
183 overwriting a expanding keywords
183 overwriting a expanding keywords
184 overwriting c expanding keywords
184 overwriting c expanding keywords
185 % compare changenodes in a c
185 % compare changenodes in a c
186 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
186 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
187 do not process $Id:
187 do not process $Id:
188 xxx $
188 xxx $
189 $Id: c,v 40a904bbbe4c 1970/01/01 00:00:01 user $
189 $Id: c,v 40a904bbbe4c 1970/01/01 00:00:01 user $
190 tests for different changenodes
190 tests for different changenodes
191 % qinit -c
191 % qinit -c
192 % qimport
192 % qimport
193 % qcommit
193 % qcommit
194 % keywords should not be expanded in patch
194 % keywords should not be expanded in patch
195 # HG changeset patch
195 # HG changeset patch
196 # User User Name <user@example.com>
196 # User User Name <user@example.com>
197 # Date 1 0
197 # Date 1 0
198 # Node ID 40a904bbbe4cd4ab0a1f28411e35db26341a40ad
198 # Node ID 40a904bbbe4cd4ab0a1f28411e35db26341a40ad
199 # Parent ef63ca68695bc9495032c6fda1350c71e6d256e9
199 # Parent ef63ca68695bc9495032c6fda1350c71e6d256e9
200 cndiff
200 cndiff
201
201
202 diff -r ef63ca68695b -r 40a904bbbe4c c
202 diff -r ef63ca68695b -r 40a904bbbe4c c
203 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
203 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
204 +++ b/c Thu Jan 01 00:00:01 1970 +0000
204 +++ b/c Thu Jan 01 00:00:01 1970 +0000
205 @@ -0,0 +1,2 @@
205 @@ -0,0 +1,2 @@
206 +$Id$
206 +$Id$
207 +tests for different changenodes
207 +tests for different changenodes
208 % qpop
208 % qpop
209 Patch queue now empty
209 Patch queue now empty
210 % qgoto - should imply qpush
210 % qgoto - should imply qpush
211 applying mqtest.diff
211 applying mqtest.diff
212 Now at: mqtest.diff
212 Now at: mqtest.diff
213 % cat
213 % cat
214 $Id: c,v 40a904bbbe4c 1970/01/01 00:00:01 user $
214 $Id: c,v 40a904bbbe4c 1970/01/01 00:00:01 user $
215 tests for different changenodes
215 tests for different changenodes
216 % qpop and move on
216 % qpop and move on
217 Patch queue now empty
217 Patch queue now empty
218 % copy
218 % copy
219 % kwfiles added
219 % kwfiles added
220 a
220 a
221 c
221 c
222 % commit
222 % commit
223 c
223 c
224 c: copy a:0045e12f6c5791aac80ca6cbfd97709a88307292
224 c: copy a:0045e12f6c5791aac80ca6cbfd97709a88307292
225 overwriting c expanding keywords
225 overwriting c expanding keywords
226 % cat a c
226 % cat a c
227 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
227 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
228 do not process $Id:
228 do not process $Id:
229 xxx $
229 xxx $
230 expand $Id: c,v e22d299ac0c2 1970/01/01 00:00:01 user $
230 expand $Id: c,v e22d299ac0c2 1970/01/01 00:00:01 user $
231 do not process $Id:
231 do not process $Id:
232 xxx $
232 xxx $
233 % touch copied c
233 % touch copied c
234 % status
234 % status
235 % kwfiles
235 % kwfiles
236 a
236 a
237 c
237 c
238 % diff --rev
238 % diff --rev
239 diff -r ef63ca68695b c
239 diff -r ef63ca68695b c
240 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
240 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
241 @@ -0,0 +1,3 @@
241 @@ -0,0 +1,3 @@
242 +expand $Id$
242 +expand $Id$
243 +do not process $Id:
243 +do not process $Id:
244 +xxx $
244 +xxx $
245 % rollback
245 % rollback
246 rolling back last transaction
246 rolling back last transaction
247 % status
247 % status
248 A c
248 A c
249 % update -C
249 % update -C
250 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
250 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
251 % custom keyword expansion
251 % custom keyword expansion
252 % try with kwdemo
252 % try with kwdemo
253 [extensions]
253 [extensions]
254 hgext.keyword =
254 hgext.keyword =
255 [keyword]
255 [keyword]
256 * =
256 * =
257 b = ignore
257 b = ignore
258 demo.txt =
258 demo.txt =
259 [keywordmaps]
259 [keywordmaps]
260 Xinfo = {author}: {desc}
260 Xinfo = {author}: {desc}
261 $Xinfo: test: hg keyword config and expansion example $
261 $Xinfo: test: hg keyword config and expansion example $
262 % cat
262 % cat
263 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
263 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
264 do not process $Id:
264 do not process $Id:
265 xxx $
265 xxx $
266 ignore $Id$
266 ignore $Id$
267 % hg cat
267 % hg cat
268 expand $Id: a ef63ca68695b Thu, 01 Jan 1970 00:00:00 +0000 user $
268 expand $Id: a ef63ca68695b Thu, 01 Jan 1970 00:00:00 +0000 user $
269 do not process $Id:
269 do not process $Id:
270 xxx $
270 xxx $
271 ignore $Id$
271 ignore $Id$
272 a
272 a
273 % interrupted commit should not change state
273 % interrupted commit should not change state
274 transaction abort!
274 transaction abort!
275 rollback completed
275 rollback completed
276 abort: empty commit message
276 abort: empty commit message
277 % status
277 % status
278 M a
278 M a
279 ? log
279 ? log
280 % commit
280 % commit
281 a
281 a
282 overwriting a expanding keywords
282 overwriting a expanding keywords
283 % status
283 % status
284 % cat
284 % cat
285 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
285 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
286 do not process $Id:
286 do not process $Id:
287 xxx $
287 xxx $
288 $Xinfo: User Name <user@example.com>: firstline $
288 $Xinfo: User Name <user@example.com>: firstline $
289 ignore $Id$
289 ignore $Id$
290 % hg cat
290 % hg cat
291 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
291 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
292 do not process $Id:
292 do not process $Id:
293 xxx $
293 xxx $
294 $Xinfo: User Name <user@example.com>: firstline $
294 $Xinfo: User Name <user@example.com>: firstline $
295 ignore $Id$
295 ignore $Id$
296 a
296 a
297 % annotate
297 % annotate
298 1: expand $Id$
298 1: expand $Id$
299 1: do not process $Id:
299 1: do not process $Id:
300 1: xxx $
300 1: xxx $
301 2: $Xinfo$
301 2: $Xinfo$
302 % remove
302 % remove
303 % status
303 % status
304 % rollback
304 % rollback
305 rolling back last transaction
305 rolling back last transaction
306 % status
306 % status
307 R a
307 R a
308 % revert a
308 % revert a
309 % cat a
309 % cat a
310 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
310 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
311 do not process $Id:
311 do not process $Id:
312 xxx $
312 xxx $
313 $Xinfo: User Name <user@example.com>: firstline $
313 $Xinfo: User Name <user@example.com>: firstline $
314 % clone to test incoming
314 % clone to test incoming
315 requesting all changes
315 requesting all changes
316 adding changesets
316 adding changesets
317 adding manifests
317 adding manifests
318 adding file changes
318 adding file changes
319 added 2 changesets with 3 changes to 3 files
319 added 2 changesets with 3 changes to 3 files
320 updating working directory
320 updating working directory
321 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
321 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
322 % incoming
322 % incoming
323 comparing with test-keyword/Test
323 comparing with test-keyword/Test
324 searching for changes
324 searching for changes
325 changeset: 2:bb948857c743
325 changeset: 2:bb948857c743
326 tag: tip
326 tag: tip
327 user: User Name <user@example.com>
327 user: User Name <user@example.com>
328 date: Thu Jan 01 00:00:02 1970 +0000
328 date: Thu Jan 01 00:00:02 1970 +0000
329 summary: firstline
329 summary: firstline
330
330
331 % commit rejecttest
331 % commit rejecttest
332 a
332 a
333 overwriting a expanding keywords
333 overwriting a expanding keywords
334 % export
334 % export
335 % import
335 % import
336 applying ../rejecttest.diff
336 applying ../rejecttest.diff
337 % cat
337 % cat
338 expand $Id: a 4e0994474d25 Thu, 01 Jan 1970 00:00:03 +0000 user $ rejecttest
338 expand $Id: a 4e0994474d25 Thu, 01 Jan 1970 00:00:03 +0000 user $ rejecttest
339 do not process $Id: rejecttest
339 do not process $Id: rejecttest
340 xxx $
340 xxx $
341 $Xinfo: User Name <user@example.com>: rejects? $
341 $Xinfo: User Name <user@example.com>: rejects? $
342 ignore $Id$
342 ignore $Id$
343
343
344 % rollback
344 % rollback
345 rolling back last transaction
345 rolling back last transaction
346 % clean update
346 % clean update
347 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
347 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
348 % kwexpand/kwshrink on selected files
348 % kwexpand/kwshrink on selected files
349 % copy a x/a
349 % copy a x/a
350 % kwexpand a
350 % kwexpand a
351 overwriting a expanding keywords
351 overwriting a expanding keywords
352 % kwexpand x/a should abort
352 % kwexpand x/a should abort
353 abort: outstanding uncommitted changes in given files
353 abort: outstanding uncommitted changes
354 x/a
354 x/a
355 x/a: copy a:779c764182ce5d43e2b1eb66ce06d7b47bfe342e
355 x/a: copy a:779c764182ce5d43e2b1eb66ce06d7b47bfe342e
356 overwriting x/a expanding keywords
356 overwriting x/a expanding keywords
357 % cat a
357 % cat a
358 expand $Id: x/a cfa68229c116 Thu, 01 Jan 1970 00:00:03 +0000 user $
358 expand $Id: x/a cfa68229c116 Thu, 01 Jan 1970 00:00:03 +0000 user $
359 do not process $Id:
359 do not process $Id:
360 xxx $
360 xxx $
361 $Xinfo: User Name <user@example.com>: xa $
361 $Xinfo: User Name <user@example.com>: xa $
362 % kwshrink a inside directory x
362 % kwshrink a inside directory x
363 overwriting x/a shrinking keywords
363 overwriting x/a shrinking keywords
364 % cat a
364 % cat a
365 expand $Id$
365 expand $Id$
366 do not process $Id:
366 do not process $Id:
367 xxx $
367 xxx $
368 $Xinfo$
368 $Xinfo$
369 % kwexpand nonexistent
369 % kwexpand nonexistent
370 nonexistent:
370 nonexistent:
371 % switch off expansion
371 % switch off expansion
372 % kwshrink with unknown file u
372 % kwshrink with unknown file u
373 overwriting a shrinking keywords
373 overwriting a shrinking keywords
374 overwriting x/a shrinking keywords
374 overwriting x/a shrinking keywords
375 % cat
375 % cat
376 expand $Id$
376 expand $Id$
377 do not process $Id:
377 do not process $Id:
378 xxx $
378 xxx $
379 $Xinfo$
379 $Xinfo$
380 ignore $Id$
380 ignore $Id$
381 % hg cat
381 % hg cat
382 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
382 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
383 do not process $Id:
383 do not process $Id:
384 xxx $
384 xxx $
385 $Xinfo: User Name <user@example.com>: firstline $
385 $Xinfo: User Name <user@example.com>: firstline $
386 ignore $Id$
386 ignore $Id$
387 a
387 a
388 % cat
388 % cat
389 expand $Id$
389 expand $Id$
390 do not process $Id:
390 do not process $Id:
391 xxx $
391 xxx $
392 $Xinfo$
392 $Xinfo$
393 ignore $Id$
393 ignore $Id$
394 % hg cat
394 % hg cat
395 expand $Id$
395 expand $Id$
396 do not process $Id:
396 do not process $Id:
397 xxx $
397 xxx $
398 $Xinfo$
398 $Xinfo$
399 ignore $Id$
399 ignore $Id$
400 a
400 a
401 % hg serve
401 % hg serve
402 % hgweb changeset
402 % hgweb changeset
403 200 Script output follows
403 200 Script output follows
404
404
405
405
406 # HG changeset patch
406 # HG changeset patch
407 # User User Name <user@example.com>
407 # User User Name <user@example.com>
408 # Date 3 0
408 # Date 3 0
409 # Node ID cfa68229c1167443337266ebac453c73b1d5d16e
409 # Node ID cfa68229c1167443337266ebac453c73b1d5d16e
410 # Parent bb948857c743469b22bbf51f7ec8112279ca5d83
410 # Parent bb948857c743469b22bbf51f7ec8112279ca5d83
411 xa
411 xa
412
412
413 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
413 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
414 +++ b/x/a Thu Jan 01 00:00:03 1970 +0000
414 +++ b/x/a Thu Jan 01 00:00:03 1970 +0000
415 @@ -0,0 +1,4 @@
415 @@ -0,0 +1,4 @@
416 +expand $Id$
416 +expand $Id$
417 +do not process $Id:
417 +do not process $Id:
418 +xxx $
418 +xxx $
419 +$Xinfo$
419 +$Xinfo$
420
420
421 % hgweb filediff
421 % hgweb filediff
422 200 Script output follows
422 200 Script output follows
423
423
424
424
425 --- a/a Thu Jan 01 00:00:00 1970 +0000
425 --- a/a Thu Jan 01 00:00:00 1970 +0000
426 +++ b/a Thu Jan 01 00:00:02 1970 +0000
426 +++ b/a Thu Jan 01 00:00:02 1970 +0000
427 @@ -1,3 +1,4 @@
427 @@ -1,3 +1,4 @@
428 expand $Id$
428 expand $Id$
429 do not process $Id:
429 do not process $Id:
430 xxx $
430 xxx $
431 +$Xinfo$
431 +$Xinfo$
432
432
433
433
434
434
435
435
436 % errors encountered
436 % errors encountered
General Comments 0
You need to be logged in to leave comments. Login now