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