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