##// END OF EJS Templates
Merge with stable
Martin Geisler -
r9944:15a1fe1f merge default
parent child Browse files
Show More
@@ -1,271 +1,279 b''
1 1 # extdiff.py - external diff program support for mercurial
2 2 #
3 3 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2, incorporated herein by reference.
7 7
8 8 '''command to allow external programs to compare revisions
9 9
10 10 The extdiff Mercurial extension allows you to use external programs
11 11 to compare revisions, or revision with working directory. The external
12 12 diff programs are called with a configurable set of options and two
13 13 non-option arguments: paths to directories containing snapshots of
14 14 files to compare.
15 15
16 16 The extdiff extension also allows to configure new diff commands, so
17 17 you do not need to type "hg extdiff -p kdiff3" always. ::
18 18
19 19 [extdiff]
20 20 # add new command that runs GNU diff(1) in 'context diff' mode
21 21 cdiff = gdiff -Nprc5
22 22 ## or the old way:
23 23 #cmd.cdiff = gdiff
24 24 #opts.cdiff = -Nprc5
25 25
26 26 # add new command called vdiff, runs kdiff3
27 27 vdiff = kdiff3
28 28
29 29 # add new command called meld, runs meld (no need to name twice)
30 30 meld =
31 31
32 32 # add new command called vimdiff, runs gvimdiff with DirDiff plugin
33 33 # (see http://www.vim.org/scripts/script.php?script_id=102) Non
34 34 # English user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
35 35 # your .vimrc
36 36 vimdiff = gvim -f '+next' '+execute "DirDiff" argv(0) argv(1)'
37 37
38 38 You can use -I/-X and list of file or directory names like normal "hg
39 39 diff" command. The extdiff extension makes snapshots of only needed
40 40 files, so running the external diff program will actually be pretty
41 41 fast (at least faster than having to compare the entire tree).
42 42 '''
43 43
44 44 from mercurial.i18n import _
45 45 from mercurial.node import short, nullid
46 from mercurial import cmdutil, util, commands
46 from mercurial import cmdutil, util, commands, encoding
47 47 import os, shlex, shutil, tempfile, re
48 48
49 49 def snapshot(ui, repo, files, node, tmproot):
50 50 '''snapshot files as of some revision
51 51 if not using snapshot, -I/-X does not work and recursive diff
52 52 in tools like kdiff3 and meld displays too many files.'''
53 53 dirname = os.path.basename(repo.root)
54 54 if dirname == "":
55 55 dirname = "root"
56 56 if node is not None:
57 57 dirname = '%s.%s' % (dirname, short(node))
58 58 base = os.path.join(tmproot, dirname)
59 59 os.mkdir(base)
60 60 if node is not None:
61 61 ui.note(_('making snapshot of %d files from rev %s\n') %
62 62 (len(files), short(node)))
63 63 else:
64 64 ui.note(_('making snapshot of %d files from working directory\n') %
65 65 (len(files)))
66 66 wopener = util.opener(base)
67 67 fns_and_mtime = []
68 68 ctx = repo[node]
69 69 for fn in files:
70 70 wfn = util.pconvert(fn)
71 71 if not wfn in ctx:
72 72 # File doesn't exist; could be a bogus modify
73 73 continue
74 74 ui.note(' %s\n' % wfn)
75 75 dest = os.path.join(base, wfn)
76 76 fctx = ctx[wfn]
77 77 data = repo.wwritedata(wfn, fctx.data())
78 78 if 'l' in fctx.flags():
79 79 wopener.symlink(data, wfn)
80 80 else:
81 81 wopener(wfn, 'w').write(data)
82 82 if 'x' in fctx.flags():
83 83 util.set_flags(dest, False, True)
84 84 if node is None:
85 85 fns_and_mtime.append((dest, repo.wjoin(fn), os.path.getmtime(dest)))
86 86 return dirname, fns_and_mtime
87 87
88 88 def dodiff(ui, repo, diffcmd, diffopts, pats, opts):
89 89 '''Do the actuall diff:
90 90
91 91 - copy to a temp structure if diffing 2 internal revisions
92 92 - copy to a temp structure if diffing working revision with
93 93 another one and more than 1 file is changed
94 94 - just invoke the diff for a single file in the working dir
95 95 '''
96 96
97 97 revs = opts.get('rev')
98 98 change = opts.get('change')
99 99 args = ' '.join(diffopts)
100 100 do3way = '$parent2' in args
101 101
102 102 if revs and change:
103 103 msg = _('cannot specify --rev and --change at the same time')
104 104 raise util.Abort(msg)
105 105 elif change:
106 106 node2 = repo.lookup(change)
107 107 node1a, node1b = repo.changelog.parents(node2)
108 108 else:
109 109 node1a, node2 = cmdutil.revpair(repo, revs)
110 110 if not revs:
111 111 node1b = repo.dirstate.parents()[1]
112 112 else:
113 113 node1b = nullid
114 114
115 115 # Disable 3-way merge if there is only one parent
116 116 if do3way:
117 117 if node1b == nullid:
118 118 do3way = False
119 119
120 120 matcher = cmdutil.match(repo, pats, opts)
121 121 mod_a, add_a, rem_a = map(set, repo.status(node1a, node2, matcher)[:3])
122 122 if do3way:
123 123 mod_b, add_b, rem_b = map(set, repo.status(node1b, node2, matcher)[:3])
124 124 else:
125 125 mod_b, add_b, rem_b = set(), set(), set()
126 126 modadd = mod_a | add_a | mod_b | add_b
127 127 common = modadd | rem_a | rem_b
128 128 if not common:
129 129 return 0
130 130
131 131 tmproot = tempfile.mkdtemp(prefix='extdiff.')
132 132 try:
133 133 # Always make a copy of node1a (and node1b, if applicable)
134 134 dir1a_files = mod_a | rem_a | ((mod_b | add_b) - add_a)
135 135 dir1a = snapshot(ui, repo, dir1a_files, node1a, tmproot)[0]
136 136 if do3way:
137 137 dir1b_files = mod_b | rem_b | ((mod_a | add_a) - add_b)
138 138 dir1b = snapshot(ui, repo, dir1b_files, node1b, tmproot)[0]
139 139 else:
140 140 dir1b = None
141 141
142 142 fns_and_mtime = []
143 143
144 144 # If node2 in not the wc or there is >1 change, copy it
145 145 dir2root = ''
146 146 if node2:
147 147 dir2 = snapshot(ui, repo, modadd, node2, tmproot)[0]
148 148 elif len(common) > 1:
149 149 #we only actually need to get the files to copy back to the working
150 150 #dir in this case (because the other cases are: diffing 2 revisions
151 151 #or single file -- in which case the file is already directly passed
152 152 #to the diff tool).
153 153 dir2, fns_and_mtime = snapshot(ui, repo, modadd, None, tmproot)
154 154 else:
155 155 # This lets the diff tool open the changed file directly
156 156 dir2 = ''
157 157 dir2root = repo.root
158 158
159 159 # If only one change, diff the files instead of the directories
160 160 # Handle bogus modifies correctly by checking if the files exist
161 161 if len(common) == 1:
162 162 common_file = util.localpath(common.pop())
163 163 dir1a = os.path.join(dir1a, common_file)
164 164 if not os.path.isfile(os.path.join(tmproot, dir1a)):
165 165 dir1a = os.devnull
166 166 if do3way:
167 167 dir1b = os.path.join(dir1b, common_file)
168 168 if not os.path.isfile(os.path.join(tmproot, dir1b)):
169 169 dir1b = os.devnull
170 170 dir2 = os.path.join(dir2root, dir2, common_file)
171 171
172 172 # Function to quote file/dir names in the argument string
173 173 # When not operating in 3-way mode, an empty string is returned for parent2
174 174 replace = dict(parent=dir1a, parent1=dir1a, parent2=dir1b, child=dir2)
175 175 def quote(match):
176 176 key = match.group()[1:]
177 177 if not do3way and key == 'parent2':
178 178 return ''
179 179 return util.shellquote(replace[key])
180 180
181 181 # Match parent2 first, so 'parent1?' will match both parent1 and parent
182 182 regex = '\$(parent2|parent1?|child)'
183 183 if not do3way and not re.search(regex, args):
184 184 args += ' $parent1 $child'
185 185 args = re.sub(regex, quote, args)
186 186 cmdline = util.shellquote(diffcmd) + ' ' + args
187 187
188 188 ui.debug('running %r in %s\n' % (cmdline, tmproot))
189 189 util.system(cmdline, cwd=tmproot)
190 190
191 191 for copy_fn, working_fn, mtime in fns_and_mtime:
192 192 if os.path.getmtime(copy_fn) != mtime:
193 193 ui.debug('file changed while diffing. '
194 194 'Overwriting: %s (src: %s)\n' % (working_fn, copy_fn))
195 195 util.copyfile(copy_fn, working_fn)
196 196
197 197 return 1
198 198 finally:
199 199 ui.note(_('cleaning up temp directory\n'))
200 200 shutil.rmtree(tmproot)
201 201
202 202 def extdiff(ui, repo, *pats, **opts):
203 203 '''use external program to diff repository (or selected files)
204 204
205 205 Show differences between revisions for the specified files, using
206 206 an external program. The default program used is diff, with
207 207 default options "-Npru".
208 208
209 209 To select a different program, use the -p/--program option. The
210 210 program will be passed the names of two directories to compare. To
211 211 pass additional options to the program, use -o/--option. These
212 212 will be passed before the names of the directories to compare.
213 213
214 214 When two revision arguments are given, then changes are shown
215 215 between those revisions. If only one revision is specified then
216 216 that revision is compared to the working directory, and, when no
217 217 revisions are specified, the working directory files are compared
218 218 to its parent.'''
219 219 program = opts.get('program')
220 220 option = opts.get('option')
221 221 if not program:
222 222 program = 'diff'
223 223 option = option or ['-Npru']
224 224 return dodiff(ui, repo, program, option, pats, opts)
225 225
226 226 cmdtable = {
227 227 "extdiff":
228 228 (extdiff,
229 229 [('p', 'program', '', _('comparison program to run')),
230 230 ('o', 'option', [], _('pass option to comparison program')),
231 231 ('r', 'rev', [], _('revision')),
232 232 ('c', 'change', '', _('change made by revision')),
233 233 ] + commands.walkopts,
234 234 _('hg extdiff [OPT]... [FILE]...')),
235 235 }
236 236
237 237 def uisetup(ui):
238 238 for cmd, path in ui.configitems('extdiff'):
239 239 if cmd.startswith('cmd.'):
240 240 cmd = cmd[4:]
241 241 if not path: path = cmd
242 242 diffopts = ui.config('extdiff', 'opts.' + cmd, '')
243 243 diffopts = diffopts and [diffopts] or []
244 244 elif cmd.startswith('opts.'):
245 245 continue
246 246 else:
247 247 # command = path opts
248 248 if path:
249 249 diffopts = shlex.split(path)
250 250 path = diffopts.pop(0)
251 251 else:
252 252 path, diffopts = cmd, []
253 253 def save(cmd, path, diffopts):
254 254 '''use closure to save diff command to use'''
255 255 def mydiff(ui, repo, *pats, **opts):
256 256 return dodiff(ui, repo, path, diffopts, pats, opts)
257 mydiff.__doc__ = _('''\
257 doc = _('''\
258 258 use %(path)s to diff repository (or selected files)
259 259
260 260 Show differences between revisions for the specified files, using the
261 261 %(path)s program.
262 262
263 263 When two revision arguments are given, then changes are shown between
264 264 those revisions. If only one revision is specified then that revision is
265 265 compared to the working directory, and, when no revisions are specified,
266 266 the working directory files are compared to its parent.\
267 267 ''') % dict(path=util.uirepr(path))
268
269 # We must translate the docstring right away since it is
270 # used as a format string. The string will unfortunately
271 # be translated again in commands.helpcmd and this will
272 # fail when the docstring contains non-ASCII characters.
273 # Decoding the string to a Unicode string here (using the
274 # right encoding) prevents that.
275 mydiff.__doc__ = doc.decode(encoding.encoding)
268 276 return mydiff
269 277 cmdtable[cmd] = (save(cmd, path, diffopts),
270 278 cmdtable['extdiff'][1][1:],
271 279 _('hg %s [OPTION]... [FILE]...') % cmd)
@@ -1,562 +1,563 b''
1 1 # keyword.py - $Keyword$ expansion for Mercurial
2 2 #
3 3 # Copyright 2007-2009 Christian Ebert <blacktrash@gmx.net>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2, incorporated herein by reference.
7 7 #
8 8 # $Id$
9 9 #
10 10 # Keyword expansion hack against the grain of a DSCM
11 11 #
12 12 # There are many good reasons why this is not needed in a distributed
13 13 # SCM, still it may be useful in very small projects based on single
14 14 # files (like LaTeX packages), that are mostly addressed to an
15 15 # audience not running a version control system.
16 16 #
17 17 # For in-depth discussion refer to
18 18 # <http://mercurial.selenic.com/wiki/KeywordPlan>.
19 19 #
20 20 # Keyword expansion is based on Mercurial's changeset template mappings.
21 21 #
22 22 # Binary files are not touched.
23 23 #
24 24 # Files to act upon/ignore are specified in the [keyword] section.
25 25 # Customized keyword template mappings in the [keywordmaps] section.
26 26 #
27 27 # Run "hg help keyword" and "hg kwdemo" to get info on configuration.
28 28
29 29 '''expand keywords in tracked files
30 30
31 31 This extension expands RCS/CVS-like or self-customized $Keywords$ in
32 32 tracked text files selected by your configuration.
33 33
34 34 Keywords are only expanded in local repositories and not stored in the
35 35 change history. The mechanism can be regarded as a convenience for the
36 36 current user or for archive distribution.
37 37
38 38 Configuration is done in the [keyword] and [keywordmaps] sections of
39 39 hgrc files.
40 40
41 41 Example::
42 42
43 43 [keyword]
44 44 # expand keywords in every python file except those matching "x*"
45 45 **.py =
46 46 x* = ignore
47 47
48 48 NOTE: the more specific you are in your filename patterns the less you
49 49 lose speed in huge repositories.
50 50
51 51 For [keywordmaps] template mapping and expansion demonstration and
52 52 control run "hg kwdemo". See "hg help templates" for a list of
53 53 available templates and filters.
54 54
55 55 An additional date template filter {date|utcdate} is provided. It
56 56 returns a date like "2006/09/18 15:13:13".
57 57
58 58 The default template mappings (view with "hg kwdemo -d") can be
59 59 replaced with customized keywords and templates. Again, run "hg
60 60 kwdemo" to control the results of your config changes.
61 61
62 62 Before changing/disabling active keywords, run "hg kwshrink" to avoid
63 63 the risk of inadvertently storing expanded keywords in the change
64 64 history.
65 65
66 66 To force expansion after enabling it, or a configuration change, run
67 67 "hg kwexpand".
68 68
69 69 Also, when committing with the record extension or using mq's qrecord,
70 70 be aware that keywords cannot be updated. Again, run "hg kwexpand" on
71 71 the files in question to update keyword expansions after all changes
72 72 have been checked in.
73 73
74 74 Expansions spanning more than one line and incremental expansions,
75 75 like CVS' $Log$, are not supported. A keyword template map "Log =
76 76 {desc}" expands to the first line of the changeset description.
77 77 '''
78 78
79 79 from mercurial import commands, cmdutil, dispatch, filelog, revlog, extensions
80 80 from mercurial import patch, localrepo, templater, templatefilters, util, match
81 81 from mercurial.hgweb import webcommands
82 82 from mercurial.lock import release
83 83 from mercurial.node import nullid
84 84 from mercurial.i18n import _
85 85 import re, shutil, tempfile
86 86
87 87 commands.optionalrepo += ' kwdemo'
88 88
89 89 # hg commands that do not act on keywords
90 90 nokwcommands = ('add addremove annotate bundle copy export grep incoming init'
91 91 ' log outgoing push rename rollback tip verify'
92 92 ' convert email glog')
93 93
94 94 # hg commands that trigger expansion only when writing to working dir,
95 95 # not when reading filelog, and unexpand when reading from working dir
96 96 restricted = 'merge record resolve qfold qimport qnew qpush qrefresh qrecord'
97 97
98 98 # provide cvs-like UTC date filter
99 99 utcdate = lambda x: util.datestr(x, '%Y/%m/%d %H:%M:%S')
100 100
101 101 # make keyword tools accessible
102 102 kwtools = {'templater': None, 'hgcmd': '', 'inc': [], 'exc': ['.hg*']}
103 103
104 104
105 105 class kwtemplater(object):
106 106 '''
107 107 Sets up keyword templates, corresponding keyword regex, and
108 108 provides keyword substitution functions.
109 109 '''
110 110 templates = {
111 111 'Revision': '{node|short}',
112 112 'Author': '{author|user}',
113 113 'Date': '{date|utcdate}',
114 'RCSFile': '{file|basename},v',
114 'RCSfile': '{file|basename},v',
115 'RCSFile': '{file|basename},v', # kept only for backwards compatibility
115 116 'Source': '{root}/{file},v',
116 117 'Id': '{file|basename},v {node|short} {date|utcdate} {author|user}',
117 118 'Header': '{root}/{file},v {node|short} {date|utcdate} {author|user}',
118 119 }
119 120
120 121 def __init__(self, ui, repo):
121 122 self.ui = ui
122 123 self.repo = repo
123 124 self.match = match.match(repo.root, '', [],
124 125 kwtools['inc'], kwtools['exc'])
125 126 self.restrict = kwtools['hgcmd'] in restricted.split()
126 127
127 128 kwmaps = self.ui.configitems('keywordmaps')
128 129 if kwmaps: # override default templates
129 130 self.templates = dict((k, templater.parsestring(v, False))
130 131 for k, v in kwmaps)
131 132 escaped = map(re.escape, self.templates.keys())
132 133 kwpat = r'\$(%s)(: [^$\n\r]*? )??\$' % '|'.join(escaped)
133 134 self.re_kw = re.compile(kwpat)
134 135
135 136 templatefilters.filters['utcdate'] = utcdate
136 137 self.ct = cmdutil.changeset_templater(self.ui, self.repo,
137 138 False, None, '', False)
138 139
139 140 def substitute(self, data, path, ctx, subfunc):
140 141 '''Replaces keywords in data with expanded template.'''
141 142 def kwsub(mobj):
142 143 kw = mobj.group(1)
143 144 self.ct.use_template(self.templates[kw])
144 145 self.ui.pushbuffer()
145 146 self.ct.show(ctx, root=self.repo.root, file=path)
146 147 ekw = templatefilters.firstline(self.ui.popbuffer())
147 148 return '$%s: %s $' % (kw, ekw)
148 149 return subfunc(kwsub, data)
149 150
150 151 def expand(self, path, node, data):
151 152 '''Returns data with keywords expanded.'''
152 153 if not self.restrict and self.match(path) and not util.binary(data):
153 154 ctx = self.repo.filectx(path, fileid=node).changectx()
154 155 return self.substitute(data, path, ctx, self.re_kw.sub)
155 156 return data
156 157
157 158 def iskwfile(self, path, flagfunc):
158 159 '''Returns true if path matches [keyword] pattern
159 160 and is not a symbolic link.
160 161 Caveat: localrepository._link fails on Windows.'''
161 162 return self.match(path) and not 'l' in flagfunc(path)
162 163
163 164 def overwrite(self, node, expand, files):
164 165 '''Overwrites selected files expanding/shrinking keywords.'''
165 166 ctx = self.repo[node]
166 167 mf = ctx.manifest()
167 168 if node is not None: # commit
168 169 files = [f for f in ctx.files() if f in mf]
169 170 notify = self.ui.debug
170 171 else: # kwexpand/kwshrink
171 172 notify = self.ui.note
172 173 candidates = [f for f in files if self.iskwfile(f, ctx.flags)]
173 174 if candidates:
174 175 self.restrict = True # do not expand when reading
175 176 msg = (expand and _('overwriting %s expanding keywords\n')
176 177 or _('overwriting %s shrinking keywords\n'))
177 178 for f in candidates:
178 179 fp = self.repo.file(f)
179 180 data = fp.read(mf[f])
180 181 if util.binary(data):
181 182 continue
182 183 if expand:
183 184 if node is None:
184 185 ctx = self.repo.filectx(f, fileid=mf[f]).changectx()
185 186 data, found = self.substitute(data, f, ctx,
186 187 self.re_kw.subn)
187 188 else:
188 189 found = self.re_kw.search(data)
189 190 if found:
190 191 notify(msg % f)
191 192 self.repo.wwrite(f, data, mf.flags(f))
192 193 if node is None:
193 194 self.repo.dirstate.normal(f)
194 195 self.restrict = False
195 196
196 197 def shrinktext(self, text):
197 198 '''Unconditionally removes all keyword substitutions from text.'''
198 199 return self.re_kw.sub(r'$\1$', text)
199 200
200 201 def shrink(self, fname, text):
201 202 '''Returns text with all keyword substitutions removed.'''
202 203 if self.match(fname) and not util.binary(text):
203 204 return self.shrinktext(text)
204 205 return text
205 206
206 207 def shrinklines(self, fname, lines):
207 208 '''Returns lines with keyword substitutions removed.'''
208 209 if self.match(fname):
209 210 text = ''.join(lines)
210 211 if not util.binary(text):
211 212 return self.shrinktext(text).splitlines(True)
212 213 return lines
213 214
214 215 def wread(self, fname, data):
215 216 '''If in restricted mode returns data read from wdir with
216 217 keyword substitutions removed.'''
217 218 return self.restrict and self.shrink(fname, data) or data
218 219
219 220 class kwfilelog(filelog.filelog):
220 221 '''
221 222 Subclass of filelog to hook into its read, add, cmp methods.
222 223 Keywords are "stored" unexpanded, and processed on reading.
223 224 '''
224 225 def __init__(self, opener, kwt, path):
225 226 super(kwfilelog, self).__init__(opener, path)
226 227 self.kwt = kwt
227 228 self.path = path
228 229
229 230 def read(self, node):
230 231 '''Expands keywords when reading filelog.'''
231 232 data = super(kwfilelog, self).read(node)
232 233 return self.kwt.expand(self.path, node, data)
233 234
234 235 def add(self, text, meta, tr, link, p1=None, p2=None):
235 236 '''Removes keyword substitutions when adding to filelog.'''
236 237 text = self.kwt.shrink(self.path, text)
237 238 return super(kwfilelog, self).add(text, meta, tr, link, p1, p2)
238 239
239 240 def cmp(self, node, text):
240 241 '''Removes keyword substitutions for comparison.'''
241 242 text = self.kwt.shrink(self.path, text)
242 243 if self.renamed(node):
243 244 t2 = super(kwfilelog, self).read(node)
244 245 return t2 != text
245 246 return revlog.revlog.cmp(self, node, text)
246 247
247 248 def _status(ui, repo, kwt, *pats, **opts):
248 249 '''Bails out if [keyword] configuration is not active.
249 250 Returns status of working directory.'''
250 251 if kwt:
251 252 unknown = (opts.get('unknown') or opts.get('all')
252 253 or opts.get('untracked'))
253 254 return repo.status(match=cmdutil.match(repo, pats, opts), clean=True,
254 255 unknown=unknown)
255 256 if ui.configitems('keyword'):
256 257 raise util.Abort(_('[keyword] patterns cannot match'))
257 258 raise util.Abort(_('no [keyword] patterns configured'))
258 259
259 260 def _kwfwrite(ui, repo, expand, *pats, **opts):
260 261 '''Selects files and passes them to kwtemplater.overwrite.'''
261 262 if repo.dirstate.parents()[1] != nullid:
262 263 raise util.Abort(_('outstanding uncommitted merge'))
263 264 kwt = kwtools['templater']
264 265 status = _status(ui, repo, kwt, *pats, **opts)
265 266 modified, added, removed, deleted = status[:4]
266 267 if modified or added or removed or deleted:
267 268 raise util.Abort(_('outstanding uncommitted changes'))
268 269 wlock = lock = None
269 270 try:
270 271 wlock = repo.wlock()
271 272 lock = repo.lock()
272 273 kwt.overwrite(None, expand, status[6])
273 274 finally:
274 275 release(lock, wlock)
275 276
276 277 def demo(ui, repo, *args, **opts):
277 278 '''print [keywordmaps] configuration and an expansion example
278 279
279 280 Show current, custom, or default keyword template maps and their
280 281 expansions.
281 282
282 283 Extend the current configuration by specifying maps as arguments
283 284 and using -f/--rcfile to source an external hgrc file.
284 285
285 286 Use -d/--default to disable current configuration.
286 287
287 288 See "hg help templates" for information on templates and filters.
288 289 '''
289 290 def demoitems(section, items):
290 291 ui.write('[%s]\n' % section)
291 for k, v in items:
292 for k, v in sorted(items):
292 293 ui.write('%s = %s\n' % (k, v))
293 294
294 295 msg = 'hg keyword config and expansion example'
295 296 fn = 'demo.txt'
296 297 branchname = 'demobranch'
297 298 tmpdir = tempfile.mkdtemp('', 'kwdemo.')
298 299 ui.note(_('creating temporary repository at %s\n') % tmpdir)
299 300 repo = localrepo.localrepository(ui, tmpdir, True)
300 301 ui.setconfig('keyword', fn, '')
301 302
302 303 uikwmaps = ui.configitems('keywordmaps')
303 304 if args or opts.get('rcfile'):
304 305 ui.status(_('\n\tconfiguration using custom keyword template maps\n'))
305 306 if uikwmaps:
306 307 ui.status(_('\textending current template maps\n'))
307 308 if opts.get('default') or not uikwmaps:
308 309 ui.status(_('\toverriding default template maps\n'))
309 310 if opts.get('rcfile'):
310 311 ui.readconfig(opts.get('rcfile'))
311 312 if args:
312 313 # simulate hgrc parsing
313 314 rcmaps = ['[keywordmaps]\n'] + [a + '\n' for a in args]
314 315 fp = repo.opener('hgrc', 'w')
315 316 fp.writelines(rcmaps)
316 317 fp.close()
317 318 ui.readconfig(repo.join('hgrc'))
318 319 kwmaps = dict(ui.configitems('keywordmaps'))
319 320 elif opts.get('default'):
320 321 ui.status(_('\n\tconfiguration using default keyword template maps\n'))
321 322 kwmaps = kwtemplater.templates
322 323 if uikwmaps:
323 324 ui.status(_('\tdisabling current template maps\n'))
324 325 for k, v in kwmaps.iteritems():
325 326 ui.setconfig('keywordmaps', k, v)
326 327 else:
327 328 ui.status(_('\n\tconfiguration using current keyword template maps\n'))
328 329 kwmaps = dict(uikwmaps) or kwtemplater.templates
329 330
330 331 uisetup(ui)
331 332 reposetup(ui, repo)
332 333 for k, v in ui.configitems('extensions'):
333 334 if k.endswith('keyword'):
334 335 extension = '%s = %s' % (k, v)
335 336 break
336 337 ui.write('[extensions]\n%s\n' % extension)
337 338 demoitems('keyword', ui.configitems('keyword'))
338 339 demoitems('keywordmaps', kwmaps.iteritems())
339 keywords = '$' + '$\n$'.join(kwmaps.keys()) + '$\n'
340 keywords = '$' + '$\n$'.join(sorted(kwmaps.keys())) + '$\n'
340 341 repo.wopener(fn, 'w').write(keywords)
341 342 repo.add([fn])
342 343 path = repo.wjoin(fn)
343 344 ui.note(_('\nkeywords written to %s:\n') % path)
344 345 ui.note(keywords)
345 346 ui.note('\nhg -R "%s" branch "%s"\n' % (tmpdir, branchname))
346 347 # silence branch command if not verbose
347 348 quiet = ui.quiet
348 349 ui.quiet = not ui.verbose
349 350 commands.branch(ui, repo, branchname)
350 351 ui.quiet = quiet
351 352 for name, cmd in ui.configitems('hooks'):
352 353 if name.split('.', 1)[0].find('commit') > -1:
353 354 repo.ui.setconfig('hooks', name, '')
354 355 ui.note(_('unhooked all commit hooks\n'))
355 356 ui.note('hg -R "%s" ci -m "%s"\n' % (tmpdir, msg))
356 357 repo.commit(text=msg)
357 358 ui.status(_('\n\tkeywords expanded\n'))
358 359 ui.write(repo.wread(fn))
359 360 ui.debug('\nremoving temporary repository %s\n' % tmpdir)
360 361 shutil.rmtree(tmpdir, ignore_errors=True)
361 362
362 363 def expand(ui, repo, *pats, **opts):
363 364 '''expand keywords in the working directory
364 365
365 366 Run after (re)enabling keyword expansion.
366 367
367 368 kwexpand refuses to run if given files contain local changes.
368 369 '''
369 370 # 3rd argument sets expansion to True
370 371 _kwfwrite(ui, repo, True, *pats, **opts)
371 372
372 373 def files(ui, repo, *pats, **opts):
373 374 '''show files configured for keyword expansion
374 375
375 376 List which files in the working directory are matched by the
376 377 [keyword] configuration patterns.
377 378
378 379 Useful to prevent inadvertent keyword expansion and to speed up
379 380 execution by including only files that are actual candidates for
380 381 expansion.
381 382
382 383 See "hg help keyword" on how to construct patterns both for
383 384 inclusion and exclusion of files.
384 385
385 386 With -A/--all and -v/--verbose the codes used to show the status
386 387 of files are::
387 388
388 389 K = keyword expansion candidate
389 390 k = keyword expansion candidate (not tracked)
390 391 I = ignored
391 392 i = ignored (not tracked)
392 393 '''
393 394 kwt = kwtools['templater']
394 395 status = _status(ui, repo, kwt, *pats, **opts)
395 396 cwd = pats and repo.getcwd() or ''
396 397 modified, added, removed, deleted, unknown, ignored, clean = status
397 398 files = []
398 399 if not (opts.get('unknown') or opts.get('untracked')) or opts.get('all'):
399 400 files = sorted(modified + added + clean)
400 401 wctx = repo[None]
401 402 kwfiles = [f for f in files if kwt.iskwfile(f, wctx.flags)]
402 403 kwunknown = [f for f in unknown if kwt.iskwfile(f, wctx.flags)]
403 404 if not opts.get('ignore') or opts.get('all'):
404 405 showfiles = kwfiles, kwunknown
405 406 else:
406 407 showfiles = [], []
407 408 if opts.get('all') or opts.get('ignore'):
408 409 showfiles += ([f for f in files if f not in kwfiles],
409 410 [f for f in unknown if f not in kwunknown])
410 411 for char, filenames in zip('KkIi', showfiles):
411 412 fmt = (opts.get('all') or ui.verbose) and '%s %%s\n' % char or '%s\n'
412 413 for f in filenames:
413 414 ui.write(fmt % repo.pathto(f, cwd))
414 415
415 416 def shrink(ui, repo, *pats, **opts):
416 417 '''revert expanded keywords in the working directory
417 418
418 419 Run before changing/disabling active keywords or if you experience
419 420 problems with "hg import" or "hg merge".
420 421
421 422 kwshrink refuses to run if given files contain local changes.
422 423 '''
423 424 # 3rd argument sets expansion to False
424 425 _kwfwrite(ui, repo, False, *pats, **opts)
425 426
426 427
427 428 def uisetup(ui):
428 429 '''Collects [keyword] config in kwtools.
429 430 Monkeypatches dispatch._parse if needed.'''
430 431
431 432 for pat, opt in ui.configitems('keyword'):
432 433 if opt != 'ignore':
433 434 kwtools['inc'].append(pat)
434 435 else:
435 436 kwtools['exc'].append(pat)
436 437
437 438 if kwtools['inc']:
438 439 def kwdispatch_parse(orig, ui, args):
439 440 '''Monkeypatch dispatch._parse to obtain running hg command.'''
440 441 cmd, func, args, options, cmdoptions = orig(ui, args)
441 442 kwtools['hgcmd'] = cmd
442 443 return cmd, func, args, options, cmdoptions
443 444
444 445 extensions.wrapfunction(dispatch, '_parse', kwdispatch_parse)
445 446
446 447 def reposetup(ui, repo):
447 448 '''Sets up repo as kwrepo for keyword substitution.
448 449 Overrides file method to return kwfilelog instead of filelog
449 450 if file matches user configuration.
450 451 Wraps commit to overwrite configured files with updated
451 452 keyword substitutions.
452 453 Monkeypatches patch and webcommands.'''
453 454
454 455 try:
455 456 if (not repo.local() or not kwtools['inc']
456 457 or kwtools['hgcmd'] in nokwcommands.split()
457 458 or '.hg' in util.splitpath(repo.root)
458 459 or repo._url.startswith('bundle:')):
459 460 return
460 461 except AttributeError:
461 462 pass
462 463
463 464 kwtools['templater'] = kwt = kwtemplater(ui, repo)
464 465
465 466 class kwrepo(repo.__class__):
466 467 def file(self, f):
467 468 if f[0] == '/':
468 469 f = f[1:]
469 470 return kwfilelog(self.sopener, kwt, f)
470 471
471 472 def wread(self, filename):
472 473 data = super(kwrepo, self).wread(filename)
473 474 return kwt.wread(filename, data)
474 475
475 476 def commit(self, *args, **opts):
476 477 # use custom commitctx for user commands
477 478 # other extensions can still wrap repo.commitctx directly
478 479 self.commitctx = self.kwcommitctx
479 480 try:
480 481 return super(kwrepo, self).commit(*args, **opts)
481 482 finally:
482 483 del self.commitctx
483 484
484 485 def kwcommitctx(self, ctx, error=False):
485 486 wlock = lock = None
486 487 try:
487 488 wlock = self.wlock()
488 489 lock = self.lock()
489 490 # store and postpone commit hooks
490 491 commithooks = {}
491 492 for name, cmd in ui.configitems('hooks'):
492 493 if name.split('.', 1)[0] == 'commit':
493 494 commithooks[name] = cmd
494 495 ui.setconfig('hooks', name, None)
495 496 if commithooks:
496 497 # store parents for commit hooks
497 498 p1, p2 = ctx.p1(), ctx.p2()
498 499 xp1, xp2 = p1.hex(), p2 and p2.hex() or ''
499 500
500 501 n = super(kwrepo, self).commitctx(ctx, error)
501 502
502 503 kwt.overwrite(n, True, None)
503 504 if commithooks:
504 505 for name, cmd in commithooks.iteritems():
505 506 ui.setconfig('hooks', name, cmd)
506 507 self.hook('commit', node=n, parent1=xp1, parent2=xp2)
507 508 return n
508 509 finally:
509 510 release(lock, wlock)
510 511
511 512 # monkeypatches
512 513 def kwpatchfile_init(orig, self, ui, fname, opener,
513 514 missing=False, eol=None):
514 515 '''Monkeypatch/wrap patch.patchfile.__init__ to avoid
515 516 rejects or conflicts due to expanded keywords in working dir.'''
516 517 orig(self, ui, fname, opener, missing, eol)
517 518 # shrink keywords read from working dir
518 519 self.lines = kwt.shrinklines(self.fname, self.lines)
519 520
520 521 def kw_diff(orig, repo, node1=None, node2=None, match=None, changes=None,
521 522 opts=None):
522 523 '''Monkeypatch patch.diff to avoid expansion except when
523 524 comparing against working dir.'''
524 525 if node2 is not None:
525 526 kwt.match = util.never
526 527 elif node1 is not None and node1 != repo['.'].node():
527 528 kwt.restrict = True
528 529 return orig(repo, node1, node2, match, changes, opts)
529 530
530 531 def kwweb_skip(orig, web, req, tmpl):
531 532 '''Wraps webcommands.x turning off keyword expansion.'''
532 533 kwt.match = util.never
533 534 return orig(web, req, tmpl)
534 535
535 536 repo.__class__ = kwrepo
536 537
537 538 extensions.wrapfunction(patch.patchfile, '__init__', kwpatchfile_init)
538 539 extensions.wrapfunction(patch, 'diff', kw_diff)
539 540 for c in 'annotate changeset rev filediff diff'.split():
540 541 extensions.wrapfunction(webcommands, c, kwweb_skip)
541 542
542 543 cmdtable = {
543 544 'kwdemo':
544 545 (demo,
545 546 [('d', 'default', None, _('show default keyword template maps')),
546 547 ('f', 'rcfile', '', _('read maps from rcfile'))],
547 548 _('hg kwdemo [-d] [-f RCFILE] [TEMPLATEMAP]...')),
548 549 'kwexpand': (expand, commands.walkopts,
549 550 _('hg kwexpand [OPTION]... [FILE]...')),
550 551 'kwfiles':
551 552 (files,
552 553 [('A', 'all', None, _('show keyword status flags of all files')),
553 554 ('i', 'ignore', None, _('show files excluded from expansion')),
554 555 ('u', 'unknown', None, _('only show unknown (not tracked) files')),
555 556 ('a', 'all', None,
556 557 _('show keyword status flags of all files (DEPRECATED)')),
557 558 ('u', 'untracked', None, _('only show untracked files (DEPRECATED)')),
558 559 ] + commands.walkopts,
559 560 _('hg kwfiles [OPTION]... [FILE]...')),
560 561 'kwshrink': (shrink, commands.walkopts,
561 562 _('hg kwshrink [OPTION]... [FILE]...')),
562 563 }
@@ -1,451 +1,453 b''
1 1 % hg kwdemo
2 2 [extensions]
3 3 hgext.keyword =
4 4 [keyword]
5 5 demo.txt =
6 6 [keywordmaps]
7 RCSFile = {file|basename},v
8 7 Author = {author|user}
8 Date = {date|utcdate}
9 9 Header = {root}/{file},v {node|short} {date|utcdate} {author|user}
10 Source = {root}/{file},v
11 Date = {date|utcdate}
12 10 Id = {file|basename},v {node|short} {date|utcdate} {author|user}
11 RCSFile = {file|basename},v
12 RCSfile = {file|basename},v
13 13 Revision = {node|short}
14 $RCSFile: demo.txt,v $
14 Source = {root}/{file},v
15 15 $Author: test $
16 $Date: 2000/00/00 00:00:00 $
16 17 $Header: /TMP/demo.txt,v xxxxxxxxxxxx 2000/00/00 00:00:00 test $
18 $Id: demo.txt,v xxxxxxxxxxxx 2000/00/00 00:00:00 test $
19 $RCSFile: demo.txt,v $
20 $RCSfile: demo.txt,v $
21 $Revision: xxxxxxxxxxxx $
17 22 $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 23 [extensions]
22 24 hgext.keyword =
23 25 [keyword]
24 26 demo.txt =
25 27 [keywordmaps]
26 28 Branch = {branches}
27 29 $Branch: demobranch $
28 30 % kwshrink should exit silently in empty/invalid repo
29 31 pulling from test-keyword.hg
30 32 requesting all changes
31 33 adding changesets
32 34 adding manifests
33 35 adding file changes
34 36 added 1 changesets with 1 changes to 1 files
35 37 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
36 38 % cat
37 39 expand $Id$
38 40 do not process $Id:
39 41 xxx $
40 42 ignore $Id$
41 43 % no kwfiles
42 44 % untracked candidates
43 45 k a
44 46 % addremove
45 47 adding a
46 48 adding b
47 49 % status
48 50 A a
49 51 A b
50 52 % default keyword expansion including commit hook
51 53 % interrupted commit should not change state or run commit hook
52 54 abort: empty commit message
53 55 % status
54 56 A a
55 57 A b
56 58 % commit
57 59 a
58 60 b
59 61 overwriting a expanding keywords
60 62 running hook commit.test: cp a hooktest
61 63 committed changeset 1:ef63ca68695bc9495032c6fda1350c71e6d256e9
62 64 % status
63 65 ? hooktest
64 66 % identify
65 67 ef63ca68695b
66 68 % cat
67 69 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
68 70 do not process $Id:
69 71 xxx $
70 72 ignore $Id$
71 73 % hg cat
72 74 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
73 75 do not process $Id:
74 76 xxx $
75 77 ignore $Id$
76 78 a
77 79 % diff a hooktest
78 80 % removing commit hook from config
79 81 % bundle
80 82 2 changesets found
81 83 % notify on pull to check whether keywords stay as is in email
82 84 % ie. if patch.diff wrapper acts as it should
83 85 % pull from bundle
84 86 pulling from ../kw.hg
85 87 requesting all changes
86 88 adding changesets
87 89 adding manifests
88 90 adding file changes
89 91 added 2 changesets with 3 changes to 3 files
90 92
91 93 diff -r 000000000000 -r a2392c293916 sym
92 94 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
93 95 +++ b/sym Sat Feb 09 20:25:47 2008 +0100
94 96 @@ -0,0 +1,1 @@
95 97 +a
96 98 \ No newline at end of file
97 99
98 100 diff -r a2392c293916 -r ef63ca68695b a
99 101 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
100 102 +++ b/a Thu Jan 01 00:00:00 1970 +0000
101 103 @@ -0,0 +1,3 @@
102 104 +expand $Id$
103 105 +do not process $Id:
104 106 +xxx $
105 107 diff -r a2392c293916 -r ef63ca68695b b
106 108 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
107 109 +++ b/b Thu Jan 01 00:00:00 1970 +0000
108 110 @@ -0,0 +1,1 @@
109 111 +ignore $Id$
110 112 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
111 113 % remove notify config
112 114 % touch
113 115 % status
114 116 % update
115 117 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
116 118 % cat
117 119 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
118 120 do not process $Id:
119 121 xxx $
120 122 ignore $Id$
121 123 % check whether expansion is filewise
122 124 % commit c
123 125 adding c
124 126 % force expansion
125 127 overwriting a expanding keywords
126 128 overwriting c expanding keywords
127 129 % compare changenodes in a c
128 130 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
129 131 do not process $Id:
130 132 xxx $
131 133 $Id: c,v 40a904bbbe4c 1970/01/01 00:00:01 user $
132 134 tests for different changenodes
133 135 % qinit -c
134 136 % qimport
135 137 % qcommit
136 138 % keywords should not be expanded in patch
137 139 # HG changeset patch
138 140 # User User Name <user@example.com>
139 141 # Date 1 0
140 142 # Node ID 40a904bbbe4cd4ab0a1f28411e35db26341a40ad
141 143 # Parent ef63ca68695bc9495032c6fda1350c71e6d256e9
142 144 cndiff
143 145
144 146 diff -r ef63ca68695b -r 40a904bbbe4c c
145 147 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
146 148 +++ b/c Thu Jan 01 00:00:01 1970 +0000
147 149 @@ -0,0 +1,2 @@
148 150 +$Id$
149 151 +tests for different changenodes
150 152 % qpop
151 153 popping mqtest.diff
152 154 patch queue now empty
153 155 % qgoto - should imply qpush
154 156 applying mqtest.diff
155 157 now at: mqtest.diff
156 158 % cat
157 159 $Id: c,v 40a904bbbe4c 1970/01/01 00:00:01 user $
158 160 tests for different changenodes
159 161 % qpop and move on
160 162 popping mqtest.diff
161 163 patch queue now empty
162 164 % copy
163 165 % kwfiles added
164 166 a
165 167 c
166 168 % commit
167 169 c
168 170 c: copy a:0045e12f6c5791aac80ca6cbfd97709a88307292
169 171 overwriting c expanding keywords
170 172 committed changeset 2:e22d299ac0c2bd8897b3df5114374b9e4d4ca62f
171 173 % cat a c
172 174 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
173 175 do not process $Id:
174 176 xxx $
175 177 expand $Id: c,v e22d299ac0c2 1970/01/01 00:00:01 user $
176 178 do not process $Id:
177 179 xxx $
178 180 % touch copied c
179 181 % status
180 182 % kwfiles
181 183 a
182 184 c
183 185 % ignored files
184 186 I b
185 187 I sym
186 188 % all files
187 189 K a
188 190 K c
189 191 I b
190 192 I sym
191 193 % diff --rev
192 194 diff -r ef63ca68695b c
193 195 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
194 196 @@ -0,0 +1,3 @@
195 197 +expand $Id$
196 198 +do not process $Id:
197 199 +xxx $
198 200 % rollback
199 201 rolling back last transaction
200 202 % status
201 203 A c
202 204 % update -C
203 205 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
204 206 % custom keyword expansion
205 207 % try with kwdemo
206 208 [extensions]
207 209 hgext.keyword =
208 210 [keyword]
209 211 * =
210 212 b = ignore
211 213 demo.txt =
212 214 [keywordmaps]
213 215 Xinfo = {author}: {desc}
214 216 $Xinfo: test: hg keyword config and expansion example $
215 217 % cat
216 218 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
217 219 do not process $Id:
218 220 xxx $
219 221 ignore $Id$
220 222 % hg cat
221 223 expand $Id: a ef63ca68695b Thu, 01 Jan 1970 00:00:00 +0000 user $
222 224 do not process $Id:
223 225 xxx $
224 226 ignore $Id$
225 227 a
226 228 % interrupted commit should not change state
227 229 abort: empty commit message
228 230 % status
229 231 M a
230 232 ? c
231 233 ? log
232 234 % commit
233 235 a
234 236 overwriting a expanding keywords
235 237 committed changeset 2:bb948857c743469b22bbf51f7ec8112279ca5d83
236 238 % status
237 239 ? c
238 240 % verify
239 241 checking changesets
240 242 checking manifests
241 243 crosschecking files in changesets and manifests
242 244 checking files
243 245 3 files, 3 changesets, 4 total revisions
244 246 % cat
245 247 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
246 248 do not process $Id:
247 249 xxx $
248 250 $Xinfo: User Name <user@example.com>: firstline $
249 251 ignore $Id$
250 252 % hg cat
251 253 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
252 254 do not process $Id:
253 255 xxx $
254 256 $Xinfo: User Name <user@example.com>: firstline $
255 257 ignore $Id$
256 258 a
257 259 % annotate
258 260 1: expand $Id$
259 261 1: do not process $Id:
260 262 1: xxx $
261 263 2: $Xinfo$
262 264 % remove
263 265 committed changeset 3:d14c712653769de926994cf7fbb06c8fbd68f012
264 266 % status
265 267 ? c
266 268 % rollback
267 269 rolling back last transaction
268 270 % status
269 271 R a
270 272 ? c
271 273 % revert a
272 274 % cat a
273 275 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
274 276 do not process $Id:
275 277 xxx $
276 278 $Xinfo: User Name <user@example.com>: firstline $
277 279 % clone to test incoming
278 280 requesting all changes
279 281 adding changesets
280 282 adding manifests
281 283 adding file changes
282 284 added 2 changesets with 3 changes to 3 files
283 285 updating to branch default
284 286 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
285 287 % incoming
286 288 comparing with test-keyword/Test
287 289 searching for changes
288 290 changeset: 2:bb948857c743
289 291 tag: tip
290 292 user: User Name <user@example.com>
291 293 date: Thu Jan 01 00:00:02 1970 +0000
292 294 summary: firstline
293 295
294 296 % commit rejecttest
295 297 a
296 298 overwriting a expanding keywords
297 299 committed changeset 2:85e279d709ffc28c9fdd1b868570985fc3d87082
298 300 % export
299 301 % import
300 302 applying ../rejecttest.diff
301 303 % cat
302 304 expand $Id: a 4e0994474d25 Thu, 01 Jan 1970 00:00:03 +0000 user $ rejecttest
303 305 do not process $Id: rejecttest
304 306 xxx $
305 307 $Xinfo: User Name <user@example.com>: rejects? $
306 308 ignore $Id$
307 309
308 310 % rollback
309 311 rolling back last transaction
310 312 % clean update
311 313 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
312 314 % kwexpand/kwshrink on selected files
313 315 % copy a x/a
314 316 % kwexpand a
315 317 overwriting a expanding keywords
316 318 % kwexpand x/a should abort
317 319 abort: outstanding uncommitted changes
318 320 x/a
319 321 x/a: copy a:779c764182ce5d43e2b1eb66ce06d7b47bfe342e
320 322 overwriting x/a expanding keywords
321 323 committed changeset 3:cfa68229c1167443337266ebac453c73b1d5d16e
322 324 % cat a
323 325 expand $Id: x/a cfa68229c116 Thu, 01 Jan 1970 00:00:03 +0000 user $
324 326 do not process $Id:
325 327 xxx $
326 328 $Xinfo: User Name <user@example.com>: xa $
327 329 % kwshrink a inside directory x
328 330 overwriting x/a shrinking keywords
329 331 % cat a
330 332 expand $Id$
331 333 do not process $Id:
332 334 xxx $
333 335 $Xinfo$
334 336 % kwexpand nonexistent
335 337 nonexistent:
336 338 % hg serve
337 339 % expansion
338 340 % hgweb file
339 341 200 Script output follows
340 342
341 343 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
342 344 do not process $Id:
343 345 xxx $
344 346 $Xinfo: User Name <user@example.com>: firstline $
345 347 % no expansion
346 348 % hgweb annotate
347 349 200 Script output follows
348 350
349 351
350 352 user@1: expand $Id$
351 353 user@1: do not process $Id:
352 354 user@1: xxx $
353 355 user@2: $Xinfo$
354 356
355 357
356 358
357 359
358 360 % hgweb changeset
359 361 200 Script output follows
360 362
361 363
362 364 # HG changeset patch
363 365 # User User Name <user@example.com>
364 366 # Date 3 0
365 367 # Node ID cfa68229c1167443337266ebac453c73b1d5d16e
366 368 # Parent bb948857c743469b22bbf51f7ec8112279ca5d83
367 369 xa
368 370
369 371 diff -r bb948857c743 -r cfa68229c116 x/a
370 372 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
371 373 +++ b/x/a Thu Jan 01 00:00:03 1970 +0000
372 374 @@ -0,0 +1,4 @@
373 375 +expand $Id$
374 376 +do not process $Id:
375 377 +xxx $
376 378 +$Xinfo$
377 379
378 380 % hgweb filediff
379 381 200 Script output follows
380 382
381 383
382 384 diff -r ef63ca68695b -r bb948857c743 a
383 385 --- a/a Thu Jan 01 00:00:00 1970 +0000
384 386 +++ b/a Thu Jan 01 00:00:02 1970 +0000
385 387 @@ -1,3 +1,4 @@
386 388 expand $Id$
387 389 do not process $Id:
388 390 xxx $
389 391 +$Xinfo$
390 392
391 393
392 394
393 395
394 396 % errors encountered
395 397 % merge/resolve
396 398 % simplemerge
397 399 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
398 400 created new head
399 401 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
400 402 (branch merge, don't forget to commit)
401 403 $Id: m 8731e1dadc99 Thu, 01 Jan 1970 00:00:00 +0000 test $
402 404 foo
403 405 % conflict
404 406 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
405 407 created new head
406 408 merging m
407 409 warning: conflicts during merge.
408 410 merging m failed!
409 411 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
410 412 use 'hg resolve' to retry unresolved file merges or 'hg update -C' to abandon
411 413 % keyword stays outside conflict zone
412 414 $Id$
413 415 <<<<<<< local
414 416 bar
415 417 =======
416 418 foo
417 419 >>>>>>> other
418 420 % resolve to local
419 421 $Id: m 43dfd2854b5b Thu, 01 Jan 1970 00:00:00 +0000 test $
420 422 bar
421 423 % switch off expansion
422 424 % kwshrink with unknown file u
423 425 overwriting a shrinking keywords
424 426 overwriting m shrinking keywords
425 427 overwriting x/a shrinking keywords
426 428 % cat
427 429 expand $Id$
428 430 do not process $Id:
429 431 xxx $
430 432 $Xinfo$
431 433 ignore $Id$
432 434 % hg cat
433 435 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
434 436 do not process $Id:
435 437 xxx $
436 438 $Xinfo: User Name <user@example.com>: firstline $
437 439 ignore $Id$
438 440 a
439 441 % cat
440 442 expand $Id$
441 443 do not process $Id:
442 444 xxx $
443 445 $Xinfo$
444 446 ignore $Id$
445 447 % hg cat
446 448 expand $Id$
447 449 do not process $Id:
448 450 xxx $
449 451 $Xinfo$
450 452 ignore $Id$
451 453 a
General Comments 0
You need to be logged in to leave comments. Login now