##// END OF EJS Templates
templater: don't overwrite the keyword mapping in runsymbol() (issue4362)...
Matt Harbison -
r23167:a3c2d921 stable
parent child Browse files
Show More
@@ -1,763 +1,761 b''
1 # templater.py - template expansion for output
1 # templater.py - template expansion for output
2 #
2 #
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from i18n import _
8 from i18n import _
9 import os, re
9 import os, re
10 import util, config, templatefilters, templatekw, parser, error
10 import util, config, templatefilters, templatekw, parser, error
11 import revset as revsetmod
11 import revset as revsetmod
12 import types
12 import types
13 import minirst
13 import minirst
14
14
15 # template parsing
15 # template parsing
16
16
17 elements = {
17 elements = {
18 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
18 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
19 ",": (2, None, ("list", 2)),
19 ",": (2, None, ("list", 2)),
20 "|": (5, None, ("|", 5)),
20 "|": (5, None, ("|", 5)),
21 "%": (6, None, ("%", 6)),
21 "%": (6, None, ("%", 6)),
22 ")": (0, None, None),
22 ")": (0, None, None),
23 "symbol": (0, ("symbol",), None),
23 "symbol": (0, ("symbol",), None),
24 "string": (0, ("string",), None),
24 "string": (0, ("string",), None),
25 "rawstring": (0, ("rawstring",), None),
25 "rawstring": (0, ("rawstring",), None),
26 "end": (0, None, None),
26 "end": (0, None, None),
27 }
27 }
28
28
29 def tokenizer(data):
29 def tokenizer(data):
30 program, start, end = data
30 program, start, end = data
31 pos = start
31 pos = start
32 while pos < end:
32 while pos < end:
33 c = program[pos]
33 c = program[pos]
34 if c.isspace(): # skip inter-token whitespace
34 if c.isspace(): # skip inter-token whitespace
35 pass
35 pass
36 elif c in "(,)%|": # handle simple operators
36 elif c in "(,)%|": # handle simple operators
37 yield (c, None, pos)
37 yield (c, None, pos)
38 elif (c in '"\'' or c == 'r' and
38 elif (c in '"\'' or c == 'r' and
39 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
39 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
40 if c == 'r':
40 if c == 'r':
41 pos += 1
41 pos += 1
42 c = program[pos]
42 c = program[pos]
43 decode = False
43 decode = False
44 else:
44 else:
45 decode = True
45 decode = True
46 pos += 1
46 pos += 1
47 s = pos
47 s = pos
48 while pos < end: # find closing quote
48 while pos < end: # find closing quote
49 d = program[pos]
49 d = program[pos]
50 if decode and d == '\\': # skip over escaped characters
50 if decode and d == '\\': # skip over escaped characters
51 pos += 2
51 pos += 2
52 continue
52 continue
53 if d == c:
53 if d == c:
54 if not decode:
54 if not decode:
55 yield ('rawstring', program[s:pos], s)
55 yield ('rawstring', program[s:pos], s)
56 break
56 break
57 yield ('string', program[s:pos], s)
57 yield ('string', program[s:pos], s)
58 break
58 break
59 pos += 1
59 pos += 1
60 else:
60 else:
61 raise error.ParseError(_("unterminated string"), s)
61 raise error.ParseError(_("unterminated string"), s)
62 elif c.isalnum() or c in '_':
62 elif c.isalnum() or c in '_':
63 s = pos
63 s = pos
64 pos += 1
64 pos += 1
65 while pos < end: # find end of symbol
65 while pos < end: # find end of symbol
66 d = program[pos]
66 d = program[pos]
67 if not (d.isalnum() or d == "_"):
67 if not (d.isalnum() or d == "_"):
68 break
68 break
69 pos += 1
69 pos += 1
70 sym = program[s:pos]
70 sym = program[s:pos]
71 yield ('symbol', sym, s)
71 yield ('symbol', sym, s)
72 pos -= 1
72 pos -= 1
73 elif c == '}':
73 elif c == '}':
74 pos += 1
74 pos += 1
75 break
75 break
76 else:
76 else:
77 raise error.ParseError(_("syntax error"), pos)
77 raise error.ParseError(_("syntax error"), pos)
78 pos += 1
78 pos += 1
79 yield ('end', None, pos)
79 yield ('end', None, pos)
80
80
81 def compiletemplate(tmpl, context, strtoken="string"):
81 def compiletemplate(tmpl, context, strtoken="string"):
82 parsed = []
82 parsed = []
83 pos, stop = 0, len(tmpl)
83 pos, stop = 0, len(tmpl)
84 p = parser.parser(tokenizer, elements)
84 p = parser.parser(tokenizer, elements)
85 while pos < stop:
85 while pos < stop:
86 n = tmpl.find('{', pos)
86 n = tmpl.find('{', pos)
87 if n < 0:
87 if n < 0:
88 parsed.append((strtoken, tmpl[pos:]))
88 parsed.append((strtoken, tmpl[pos:]))
89 break
89 break
90 if n > 0 and tmpl[n - 1] == '\\':
90 if n > 0 and tmpl[n - 1] == '\\':
91 # escaped
91 # escaped
92 parsed.append((strtoken, (tmpl[pos:n - 1] + "{")))
92 parsed.append((strtoken, (tmpl[pos:n - 1] + "{")))
93 pos = n + 1
93 pos = n + 1
94 continue
94 continue
95 if n > pos:
95 if n > pos:
96 parsed.append((strtoken, tmpl[pos:n]))
96 parsed.append((strtoken, tmpl[pos:n]))
97
97
98 pd = [tmpl, n + 1, stop]
98 pd = [tmpl, n + 1, stop]
99 parseres, pos = p.parse(pd)
99 parseres, pos = p.parse(pd)
100 parsed.append(parseres)
100 parsed.append(parseres)
101
101
102 return [compileexp(e, context) for e in parsed]
102 return [compileexp(e, context) for e in parsed]
103
103
104 def compileexp(exp, context):
104 def compileexp(exp, context):
105 t = exp[0]
105 t = exp[0]
106 if t in methods:
106 if t in methods:
107 return methods[t](exp, context)
107 return methods[t](exp, context)
108 raise error.ParseError(_("unknown method '%s'") % t)
108 raise error.ParseError(_("unknown method '%s'") % t)
109
109
110 # template evaluation
110 # template evaluation
111
111
112 def getsymbol(exp):
112 def getsymbol(exp):
113 if exp[0] == 'symbol':
113 if exp[0] == 'symbol':
114 return exp[1]
114 return exp[1]
115 raise error.ParseError(_("expected a symbol, got '%s'") % exp[0])
115 raise error.ParseError(_("expected a symbol, got '%s'") % exp[0])
116
116
117 def getlist(x):
117 def getlist(x):
118 if not x:
118 if not x:
119 return []
119 return []
120 if x[0] == 'list':
120 if x[0] == 'list':
121 return getlist(x[1]) + [x[2]]
121 return getlist(x[1]) + [x[2]]
122 return [x]
122 return [x]
123
123
124 def getfilter(exp, context):
124 def getfilter(exp, context):
125 f = getsymbol(exp)
125 f = getsymbol(exp)
126 if f not in context._filters:
126 if f not in context._filters:
127 raise error.ParseError(_("unknown function '%s'") % f)
127 raise error.ParseError(_("unknown function '%s'") % f)
128 return context._filters[f]
128 return context._filters[f]
129
129
130 def gettemplate(exp, context):
130 def gettemplate(exp, context):
131 if exp[0] == 'string' or exp[0] == 'rawstring':
131 if exp[0] == 'string' or exp[0] == 'rawstring':
132 return compiletemplate(exp[1], context, strtoken=exp[0])
132 return compiletemplate(exp[1], context, strtoken=exp[0])
133 if exp[0] == 'symbol':
133 if exp[0] == 'symbol':
134 return context._load(exp[1])
134 return context._load(exp[1])
135 raise error.ParseError(_("expected template specifier"))
135 raise error.ParseError(_("expected template specifier"))
136
136
137 def runstring(context, mapping, data):
137 def runstring(context, mapping, data):
138 return data.decode("string-escape")
138 return data.decode("string-escape")
139
139
140 def runrawstring(context, mapping, data):
140 def runrawstring(context, mapping, data):
141 return data
141 return data
142
142
143 def runsymbol(context, mapping, key):
143 def runsymbol(context, mapping, key):
144 v = mapping.get(key)
144 v = mapping.get(key)
145 if v is None:
145 if v is None:
146 v = context._defaults.get(key)
146 v = context._defaults.get(key)
147 if v is None:
147 if v is None:
148 try:
148 try:
149 v = context.process(key, mapping)
149 v = context.process(key, mapping)
150 except TemplateNotFound:
150 except TemplateNotFound:
151 v = ''
151 v = ''
152 if callable(v):
152 if callable(v):
153 return v(**mapping)
153 return v(**mapping)
154 if isinstance(v, types.GeneratorType):
154 if isinstance(v, types.GeneratorType):
155 v = list(v)
155 v = list(v)
156 mapping[key] = v
157 return v
158 return v
156 return v
159
157
160 def buildfilter(exp, context):
158 def buildfilter(exp, context):
161 func, data = compileexp(exp[1], context)
159 func, data = compileexp(exp[1], context)
162 filt = getfilter(exp[2], context)
160 filt = getfilter(exp[2], context)
163 return (runfilter, (func, data, filt))
161 return (runfilter, (func, data, filt))
164
162
165 def runfilter(context, mapping, data):
163 def runfilter(context, mapping, data):
166 func, data, filt = data
164 func, data, filt = data
167 try:
165 try:
168 return filt(func(context, mapping, data))
166 return filt(func(context, mapping, data))
169 except (ValueError, AttributeError, TypeError):
167 except (ValueError, AttributeError, TypeError):
170 if isinstance(data, tuple):
168 if isinstance(data, tuple):
171 dt = data[1]
169 dt = data[1]
172 else:
170 else:
173 dt = data
171 dt = data
174 raise util.Abort(_("template filter '%s' is not compatible with "
172 raise util.Abort(_("template filter '%s' is not compatible with "
175 "keyword '%s'") % (filt.func_name, dt))
173 "keyword '%s'") % (filt.func_name, dt))
176
174
177 def buildmap(exp, context):
175 def buildmap(exp, context):
178 func, data = compileexp(exp[1], context)
176 func, data = compileexp(exp[1], context)
179 ctmpl = gettemplate(exp[2], context)
177 ctmpl = gettemplate(exp[2], context)
180 return (runmap, (func, data, ctmpl))
178 return (runmap, (func, data, ctmpl))
181
179
182 def runtemplate(context, mapping, template):
180 def runtemplate(context, mapping, template):
183 for func, data in template:
181 for func, data in template:
184 yield func(context, mapping, data)
182 yield func(context, mapping, data)
185
183
186 def runmap(context, mapping, data):
184 def runmap(context, mapping, data):
187 func, data, ctmpl = data
185 func, data, ctmpl = data
188 d = func(context, mapping, data)
186 d = func(context, mapping, data)
189 if callable(d):
187 if callable(d):
190 d = d()
188 d = d()
191
189
192 lm = mapping.copy()
190 lm = mapping.copy()
193
191
194 for i in d:
192 for i in d:
195 if isinstance(i, dict):
193 if isinstance(i, dict):
196 lm.update(i)
194 lm.update(i)
197 lm['originalnode'] = mapping.get('node')
195 lm['originalnode'] = mapping.get('node')
198 yield runtemplate(context, lm, ctmpl)
196 yield runtemplate(context, lm, ctmpl)
199 else:
197 else:
200 # v is not an iterable of dicts, this happen when 'key'
198 # v is not an iterable of dicts, this happen when 'key'
201 # has been fully expanded already and format is useless.
199 # has been fully expanded already and format is useless.
202 # If so, return the expanded value.
200 # If so, return the expanded value.
203 yield i
201 yield i
204
202
205 def buildfunc(exp, context):
203 def buildfunc(exp, context):
206 n = getsymbol(exp[1])
204 n = getsymbol(exp[1])
207 args = [compileexp(x, context) for x in getlist(exp[2])]
205 args = [compileexp(x, context) for x in getlist(exp[2])]
208 if n in funcs:
206 if n in funcs:
209 f = funcs[n]
207 f = funcs[n]
210 return (f, args)
208 return (f, args)
211 if n in context._filters:
209 if n in context._filters:
212 if len(args) != 1:
210 if len(args) != 1:
213 raise error.ParseError(_("filter %s expects one argument") % n)
211 raise error.ParseError(_("filter %s expects one argument") % n)
214 f = context._filters[n]
212 f = context._filters[n]
215 return (runfilter, (args[0][0], args[0][1], f))
213 return (runfilter, (args[0][0], args[0][1], f))
216 raise error.ParseError(_("unknown function '%s'") % n)
214 raise error.ParseError(_("unknown function '%s'") % n)
217
215
218 def date(context, mapping, args):
216 def date(context, mapping, args):
219 if not (1 <= len(args) <= 2):
217 if not (1 <= len(args) <= 2):
220 # i18n: "date" is a keyword
218 # i18n: "date" is a keyword
221 raise error.ParseError(_("date expects one or two arguments"))
219 raise error.ParseError(_("date expects one or two arguments"))
222
220
223 date = args[0][0](context, mapping, args[0][1])
221 date = args[0][0](context, mapping, args[0][1])
224 if len(args) == 2:
222 if len(args) == 2:
225 fmt = stringify(args[1][0](context, mapping, args[1][1]))
223 fmt = stringify(args[1][0](context, mapping, args[1][1]))
226 return util.datestr(date, fmt)
224 return util.datestr(date, fmt)
227 return util.datestr(date)
225 return util.datestr(date)
228
226
229 def diff(context, mapping, args):
227 def diff(context, mapping, args):
230 if len(args) > 2:
228 if len(args) > 2:
231 # i18n: "diff" is a keyword
229 # i18n: "diff" is a keyword
232 raise error.ParseError(_("diff expects one, two or no arguments"))
230 raise error.ParseError(_("diff expects one, two or no arguments"))
233
231
234 def getpatterns(i):
232 def getpatterns(i):
235 if i < len(args):
233 if i < len(args):
236 s = args[i][1].strip()
234 s = args[i][1].strip()
237 if s:
235 if s:
238 return [s]
236 return [s]
239 return []
237 return []
240
238
241 ctx = mapping['ctx']
239 ctx = mapping['ctx']
242 chunks = ctx.diff(match=ctx.match([], getpatterns(0), getpatterns(1)))
240 chunks = ctx.diff(match=ctx.match([], getpatterns(0), getpatterns(1)))
243
241
244 return ''.join(chunks)
242 return ''.join(chunks)
245
243
246 def fill(context, mapping, args):
244 def fill(context, mapping, args):
247 if not (1 <= len(args) <= 4):
245 if not (1 <= len(args) <= 4):
248 # i18n: "fill" is a keyword
246 # i18n: "fill" is a keyword
249 raise error.ParseError(_("fill expects one to four arguments"))
247 raise error.ParseError(_("fill expects one to four arguments"))
250
248
251 text = stringify(args[0][0](context, mapping, args[0][1]))
249 text = stringify(args[0][0](context, mapping, args[0][1]))
252 width = 76
250 width = 76
253 initindent = ''
251 initindent = ''
254 hangindent = ''
252 hangindent = ''
255 if 2 <= len(args) <= 4:
253 if 2 <= len(args) <= 4:
256 try:
254 try:
257 width = int(stringify(args[1][0](context, mapping, args[1][1])))
255 width = int(stringify(args[1][0](context, mapping, args[1][1])))
258 except ValueError:
256 except ValueError:
259 # i18n: "fill" is a keyword
257 # i18n: "fill" is a keyword
260 raise error.ParseError(_("fill expects an integer width"))
258 raise error.ParseError(_("fill expects an integer width"))
261 try:
259 try:
262 initindent = stringify(_evalifliteral(args[2], context, mapping))
260 initindent = stringify(_evalifliteral(args[2], context, mapping))
263 hangindent = stringify(_evalifliteral(args[3], context, mapping))
261 hangindent = stringify(_evalifliteral(args[3], context, mapping))
264 except IndexError:
262 except IndexError:
265 pass
263 pass
266
264
267 return templatefilters.fill(text, width, initindent, hangindent)
265 return templatefilters.fill(text, width, initindent, hangindent)
268
266
269 def pad(context, mapping, args):
267 def pad(context, mapping, args):
270 """usage: pad(text, width, fillchar=' ', right=False)
268 """usage: pad(text, width, fillchar=' ', right=False)
271 """
269 """
272 if not (2 <= len(args) <= 4):
270 if not (2 <= len(args) <= 4):
273 # i18n: "pad" is a keyword
271 # i18n: "pad" is a keyword
274 raise error.ParseError(_("pad() expects two to four arguments"))
272 raise error.ParseError(_("pad() expects two to four arguments"))
275
273
276 width = int(args[1][1])
274 width = int(args[1][1])
277
275
278 text = stringify(args[0][0](context, mapping, args[0][1]))
276 text = stringify(args[0][0](context, mapping, args[0][1]))
279 if args[0][0] == runstring:
277 if args[0][0] == runstring:
280 text = stringify(runtemplate(context, mapping,
278 text = stringify(runtemplate(context, mapping,
281 compiletemplate(text, context)))
279 compiletemplate(text, context)))
282
280
283 right = False
281 right = False
284 fillchar = ' '
282 fillchar = ' '
285 if len(args) > 2:
283 if len(args) > 2:
286 fillchar = stringify(args[2][0](context, mapping, args[2][1]))
284 fillchar = stringify(args[2][0](context, mapping, args[2][1]))
287 if len(args) > 3:
285 if len(args) > 3:
288 right = util.parsebool(args[3][1])
286 right = util.parsebool(args[3][1])
289
287
290 if right:
288 if right:
291 return text.rjust(width, fillchar)
289 return text.rjust(width, fillchar)
292 else:
290 else:
293 return text.ljust(width, fillchar)
291 return text.ljust(width, fillchar)
294
292
295 def get(context, mapping, args):
293 def get(context, mapping, args):
296 if len(args) != 2:
294 if len(args) != 2:
297 # i18n: "get" is a keyword
295 # i18n: "get" is a keyword
298 raise error.ParseError(_("get() expects two arguments"))
296 raise error.ParseError(_("get() expects two arguments"))
299
297
300 dictarg = args[0][0](context, mapping, args[0][1])
298 dictarg = args[0][0](context, mapping, args[0][1])
301 if not util.safehasattr(dictarg, 'get'):
299 if not util.safehasattr(dictarg, 'get'):
302 # i18n: "get" is a keyword
300 # i18n: "get" is a keyword
303 raise error.ParseError(_("get() expects a dict as first argument"))
301 raise error.ParseError(_("get() expects a dict as first argument"))
304
302
305 key = args[1][0](context, mapping, args[1][1])
303 key = args[1][0](context, mapping, args[1][1])
306 yield dictarg.get(key)
304 yield dictarg.get(key)
307
305
308 def _evalifliteral(arg, context, mapping):
306 def _evalifliteral(arg, context, mapping):
309 t = stringify(arg[0](context, mapping, arg[1]))
307 t = stringify(arg[0](context, mapping, arg[1]))
310 if arg[0] == runstring or arg[0] == runrawstring:
308 if arg[0] == runstring or arg[0] == runrawstring:
311 yield runtemplate(context, mapping,
309 yield runtemplate(context, mapping,
312 compiletemplate(t, context, strtoken='rawstring'))
310 compiletemplate(t, context, strtoken='rawstring'))
313 else:
311 else:
314 yield t
312 yield t
315
313
316 def if_(context, mapping, args):
314 def if_(context, mapping, args):
317 if not (2 <= len(args) <= 3):
315 if not (2 <= len(args) <= 3):
318 # i18n: "if" is a keyword
316 # i18n: "if" is a keyword
319 raise error.ParseError(_("if expects two or three arguments"))
317 raise error.ParseError(_("if expects two or three arguments"))
320
318
321 test = stringify(args[0][0](context, mapping, args[0][1]))
319 test = stringify(args[0][0](context, mapping, args[0][1]))
322 if test:
320 if test:
323 yield _evalifliteral(args[1], context, mapping)
321 yield _evalifliteral(args[1], context, mapping)
324 elif len(args) == 3:
322 elif len(args) == 3:
325 yield _evalifliteral(args[2], context, mapping)
323 yield _evalifliteral(args[2], context, mapping)
326
324
327 def ifcontains(context, mapping, args):
325 def ifcontains(context, mapping, args):
328 if not (3 <= len(args) <= 4):
326 if not (3 <= len(args) <= 4):
329 # i18n: "ifcontains" is a keyword
327 # i18n: "ifcontains" is a keyword
330 raise error.ParseError(_("ifcontains expects three or four arguments"))
328 raise error.ParseError(_("ifcontains expects three or four arguments"))
331
329
332 item = stringify(args[0][0](context, mapping, args[0][1]))
330 item = stringify(args[0][0](context, mapping, args[0][1]))
333 items = args[1][0](context, mapping, args[1][1])
331 items = args[1][0](context, mapping, args[1][1])
334
332
335 # Iterating over items gives a formatted string, so we iterate
333 # Iterating over items gives a formatted string, so we iterate
336 # directly over the raw values.
334 # directly over the raw values.
337 if ((callable(items) and item in [i.values()[0] for i in items()]) or
335 if ((callable(items) and item in [i.values()[0] for i in items()]) or
338 (isinstance(items, str) and item in items)):
336 (isinstance(items, str) and item in items)):
339 yield _evalifliteral(args[2], context, mapping)
337 yield _evalifliteral(args[2], context, mapping)
340 elif len(args) == 4:
338 elif len(args) == 4:
341 yield _evalifliteral(args[3], context, mapping)
339 yield _evalifliteral(args[3], context, mapping)
342
340
343 def ifeq(context, mapping, args):
341 def ifeq(context, mapping, args):
344 if not (3 <= len(args) <= 4):
342 if not (3 <= len(args) <= 4):
345 # i18n: "ifeq" is a keyword
343 # i18n: "ifeq" is a keyword
346 raise error.ParseError(_("ifeq expects three or four arguments"))
344 raise error.ParseError(_("ifeq expects three or four arguments"))
347
345
348 test = stringify(args[0][0](context, mapping, args[0][1]))
346 test = stringify(args[0][0](context, mapping, args[0][1]))
349 match = stringify(args[1][0](context, mapping, args[1][1]))
347 match = stringify(args[1][0](context, mapping, args[1][1]))
350 if test == match:
348 if test == match:
351 yield _evalifliteral(args[2], context, mapping)
349 yield _evalifliteral(args[2], context, mapping)
352 elif len(args) == 4:
350 elif len(args) == 4:
353 yield _evalifliteral(args[3], context, mapping)
351 yield _evalifliteral(args[3], context, mapping)
354
352
355 def join(context, mapping, args):
353 def join(context, mapping, args):
356 if not (1 <= len(args) <= 2):
354 if not (1 <= len(args) <= 2):
357 # i18n: "join" is a keyword
355 # i18n: "join" is a keyword
358 raise error.ParseError(_("join expects one or two arguments"))
356 raise error.ParseError(_("join expects one or two arguments"))
359
357
360 joinset = args[0][0](context, mapping, args[0][1])
358 joinset = args[0][0](context, mapping, args[0][1])
361 if callable(joinset):
359 if callable(joinset):
362 jf = joinset.joinfmt
360 jf = joinset.joinfmt
363 joinset = [jf(x) for x in joinset()]
361 joinset = [jf(x) for x in joinset()]
364
362
365 joiner = " "
363 joiner = " "
366 if len(args) > 1:
364 if len(args) > 1:
367 joiner = stringify(args[1][0](context, mapping, args[1][1]))
365 joiner = stringify(args[1][0](context, mapping, args[1][1]))
368
366
369 first = True
367 first = True
370 for x in joinset:
368 for x in joinset:
371 if first:
369 if first:
372 first = False
370 first = False
373 else:
371 else:
374 yield joiner
372 yield joiner
375 yield x
373 yield x
376
374
377 def label(context, mapping, args):
375 def label(context, mapping, args):
378 if len(args) != 2:
376 if len(args) != 2:
379 # i18n: "label" is a keyword
377 # i18n: "label" is a keyword
380 raise error.ParseError(_("label expects two arguments"))
378 raise error.ParseError(_("label expects two arguments"))
381
379
382 # ignore args[0] (the label string) since this is supposed to be a a no-op
380 # ignore args[0] (the label string) since this is supposed to be a a no-op
383 yield _evalifliteral(args[1], context, mapping)
381 yield _evalifliteral(args[1], context, mapping)
384
382
385 def revset(context, mapping, args):
383 def revset(context, mapping, args):
386 """usage: revset(query[, formatargs...])
384 """usage: revset(query[, formatargs...])
387 """
385 """
388 if not len(args) > 0:
386 if not len(args) > 0:
389 # i18n: "revset" is a keyword
387 # i18n: "revset" is a keyword
390 raise error.ParseError(_("revset expects one or more arguments"))
388 raise error.ParseError(_("revset expects one or more arguments"))
391
389
392 raw = args[0][1]
390 raw = args[0][1]
393 ctx = mapping['ctx']
391 ctx = mapping['ctx']
394 repo = ctx._repo
392 repo = ctx._repo
395
393
396 def query(expr):
394 def query(expr):
397 m = revsetmod.match(repo.ui, expr)
395 m = revsetmod.match(repo.ui, expr)
398 return m(repo, revsetmod.spanset(repo))
396 return m(repo, revsetmod.spanset(repo))
399
397
400 if len(args) > 1:
398 if len(args) > 1:
401 formatargs = list([a[0](context, mapping, a[1]) for a in args[1:]])
399 formatargs = list([a[0](context, mapping, a[1]) for a in args[1:]])
402 revs = query(revsetmod.formatspec(raw, *formatargs))
400 revs = query(revsetmod.formatspec(raw, *formatargs))
403 revs = list([str(r) for r in revs])
401 revs = list([str(r) for r in revs])
404 else:
402 else:
405 revsetcache = mapping['cache'].setdefault("revsetcache", {})
403 revsetcache = mapping['cache'].setdefault("revsetcache", {})
406 if raw in revsetcache:
404 if raw in revsetcache:
407 revs = revsetcache[raw]
405 revs = revsetcache[raw]
408 else:
406 else:
409 revs = query(raw)
407 revs = query(raw)
410 revs = list([str(r) for r in revs])
408 revs = list([str(r) for r in revs])
411 revsetcache[raw] = revs
409 revsetcache[raw] = revs
412
410
413 return templatekw.showlist("revision", revs, **mapping)
411 return templatekw.showlist("revision", revs, **mapping)
414
412
415 def rstdoc(context, mapping, args):
413 def rstdoc(context, mapping, args):
416 if len(args) != 2:
414 if len(args) != 2:
417 # i18n: "rstdoc" is a keyword
415 # i18n: "rstdoc" is a keyword
418 raise error.ParseError(_("rstdoc expects two arguments"))
416 raise error.ParseError(_("rstdoc expects two arguments"))
419
417
420 text = stringify(args[0][0](context, mapping, args[0][1]))
418 text = stringify(args[0][0](context, mapping, args[0][1]))
421 style = stringify(args[1][0](context, mapping, args[1][1]))
419 style = stringify(args[1][0](context, mapping, args[1][1]))
422
420
423 return minirst.format(text, style=style, keep=['verbose'])
421 return minirst.format(text, style=style, keep=['verbose'])
424
422
425 def shortest(context, mapping, args):
423 def shortest(context, mapping, args):
426 """usage: shortest(node, minlength=4)
424 """usage: shortest(node, minlength=4)
427 """
425 """
428 if not (1 <= len(args) <= 2):
426 if not (1 <= len(args) <= 2):
429 # i18n: "shortest" is a keyword
427 # i18n: "shortest" is a keyword
430 raise error.ParseError(_("shortest() expects one or two arguments"))
428 raise error.ParseError(_("shortest() expects one or two arguments"))
431
429
432 node = stringify(args[0][0](context, mapping, args[0][1]))
430 node = stringify(args[0][0](context, mapping, args[0][1]))
433
431
434 minlength = 4
432 minlength = 4
435 if len(args) > 1:
433 if len(args) > 1:
436 minlength = int(args[1][1])
434 minlength = int(args[1][1])
437
435
438 cl = mapping['ctx']._repo.changelog
436 cl = mapping['ctx']._repo.changelog
439 def isvalid(test):
437 def isvalid(test):
440 try:
438 try:
441 try:
439 try:
442 cl.index.partialmatch(test)
440 cl.index.partialmatch(test)
443 except AttributeError:
441 except AttributeError:
444 # Pure mercurial doesn't support partialmatch on the index.
442 # Pure mercurial doesn't support partialmatch on the index.
445 # Fallback to the slow way.
443 # Fallback to the slow way.
446 if cl._partialmatch(test) is None:
444 if cl._partialmatch(test) is None:
447 return False
445 return False
448
446
449 try:
447 try:
450 i = int(test)
448 i = int(test)
451 # if we are a pure int, then starting with zero will not be
449 # if we are a pure int, then starting with zero will not be
452 # confused as a rev; or, obviously, if the int is larger than
450 # confused as a rev; or, obviously, if the int is larger than
453 # the value of the tip rev
451 # the value of the tip rev
454 if test[0] == '0' or i > len(cl):
452 if test[0] == '0' or i > len(cl):
455 return True
453 return True
456 return False
454 return False
457 except ValueError:
455 except ValueError:
458 return True
456 return True
459 except error.RevlogError:
457 except error.RevlogError:
460 return False
458 return False
461
459
462 shortest = node
460 shortest = node
463 startlength = max(6, minlength)
461 startlength = max(6, minlength)
464 length = startlength
462 length = startlength
465 while True:
463 while True:
466 test = node[:length]
464 test = node[:length]
467 if isvalid(test):
465 if isvalid(test):
468 shortest = test
466 shortest = test
469 if length == minlength or length > startlength:
467 if length == minlength or length > startlength:
470 return shortest
468 return shortest
471 length -= 1
469 length -= 1
472 else:
470 else:
473 length += 1
471 length += 1
474 if len(shortest) <= length:
472 if len(shortest) <= length:
475 return shortest
473 return shortest
476
474
477 def strip(context, mapping, args):
475 def strip(context, mapping, args):
478 if not (1 <= len(args) <= 2):
476 if not (1 <= len(args) <= 2):
479 # i18n: "strip" is a keyword
477 # i18n: "strip" is a keyword
480 raise error.ParseError(_("strip expects one or two arguments"))
478 raise error.ParseError(_("strip expects one or two arguments"))
481
479
482 text = stringify(args[0][0](context, mapping, args[0][1]))
480 text = stringify(args[0][0](context, mapping, args[0][1]))
483 if len(args) == 2:
481 if len(args) == 2:
484 chars = stringify(args[1][0](context, mapping, args[1][1]))
482 chars = stringify(args[1][0](context, mapping, args[1][1]))
485 return text.strip(chars)
483 return text.strip(chars)
486 return text.strip()
484 return text.strip()
487
485
488 def sub(context, mapping, args):
486 def sub(context, mapping, args):
489 if len(args) != 3:
487 if len(args) != 3:
490 # i18n: "sub" is a keyword
488 # i18n: "sub" is a keyword
491 raise error.ParseError(_("sub expects three arguments"))
489 raise error.ParseError(_("sub expects three arguments"))
492
490
493 pat = stringify(args[0][0](context, mapping, args[0][1]))
491 pat = stringify(args[0][0](context, mapping, args[0][1]))
494 rpl = stringify(args[1][0](context, mapping, args[1][1]))
492 rpl = stringify(args[1][0](context, mapping, args[1][1]))
495 src = stringify(_evalifliteral(args[2], context, mapping))
493 src = stringify(_evalifliteral(args[2], context, mapping))
496 yield re.sub(pat, rpl, src)
494 yield re.sub(pat, rpl, src)
497
495
498 def startswith(context, mapping, args):
496 def startswith(context, mapping, args):
499 if len(args) != 2:
497 if len(args) != 2:
500 # i18n: "startswith" is a keyword
498 # i18n: "startswith" is a keyword
501 raise error.ParseError(_("startswith expects two arguments"))
499 raise error.ParseError(_("startswith expects two arguments"))
502
500
503 patn = stringify(args[0][0](context, mapping, args[0][1]))
501 patn = stringify(args[0][0](context, mapping, args[0][1]))
504 text = stringify(args[1][0](context, mapping, args[1][1]))
502 text = stringify(args[1][0](context, mapping, args[1][1]))
505 if text.startswith(patn):
503 if text.startswith(patn):
506 return text
504 return text
507 return ''
505 return ''
508
506
509
507
510 def word(context, mapping, args):
508 def word(context, mapping, args):
511 """return nth word from a string"""
509 """return nth word from a string"""
512 if not (2 <= len(args) <= 3):
510 if not (2 <= len(args) <= 3):
513 # i18n: "word" is a keyword
511 # i18n: "word" is a keyword
514 raise error.ParseError(_("word expects two or three arguments, got %d")
512 raise error.ParseError(_("word expects two or three arguments, got %d")
515 % len(args))
513 % len(args))
516
514
517 num = int(stringify(args[0][0](context, mapping, args[0][1])))
515 num = int(stringify(args[0][0](context, mapping, args[0][1])))
518 text = stringify(args[1][0](context, mapping, args[1][1]))
516 text = stringify(args[1][0](context, mapping, args[1][1]))
519 if len(args) == 3:
517 if len(args) == 3:
520 splitter = stringify(args[2][0](context, mapping, args[2][1]))
518 splitter = stringify(args[2][0](context, mapping, args[2][1]))
521 else:
519 else:
522 splitter = None
520 splitter = None
523
521
524 tokens = text.split(splitter)
522 tokens = text.split(splitter)
525 if num >= len(tokens):
523 if num >= len(tokens):
526 return ''
524 return ''
527 else:
525 else:
528 return tokens[num]
526 return tokens[num]
529
527
530 methods = {
528 methods = {
531 "string": lambda e, c: (runstring, e[1]),
529 "string": lambda e, c: (runstring, e[1]),
532 "rawstring": lambda e, c: (runrawstring, e[1]),
530 "rawstring": lambda e, c: (runrawstring, e[1]),
533 "symbol": lambda e, c: (runsymbol, e[1]),
531 "symbol": lambda e, c: (runsymbol, e[1]),
534 "group": lambda e, c: compileexp(e[1], c),
532 "group": lambda e, c: compileexp(e[1], c),
535 # ".": buildmember,
533 # ".": buildmember,
536 "|": buildfilter,
534 "|": buildfilter,
537 "%": buildmap,
535 "%": buildmap,
538 "func": buildfunc,
536 "func": buildfunc,
539 }
537 }
540
538
541 funcs = {
539 funcs = {
542 "date": date,
540 "date": date,
543 "diff": diff,
541 "diff": diff,
544 "fill": fill,
542 "fill": fill,
545 "get": get,
543 "get": get,
546 "if": if_,
544 "if": if_,
547 "ifcontains": ifcontains,
545 "ifcontains": ifcontains,
548 "ifeq": ifeq,
546 "ifeq": ifeq,
549 "join": join,
547 "join": join,
550 "label": label,
548 "label": label,
551 "pad": pad,
549 "pad": pad,
552 "revset": revset,
550 "revset": revset,
553 "rstdoc": rstdoc,
551 "rstdoc": rstdoc,
554 "shortest": shortest,
552 "shortest": shortest,
555 "startswith": startswith,
553 "startswith": startswith,
556 "strip": strip,
554 "strip": strip,
557 "sub": sub,
555 "sub": sub,
558 "word": word,
556 "word": word,
559 }
557 }
560
558
561 # template engine
559 # template engine
562
560
563 stringify = templatefilters.stringify
561 stringify = templatefilters.stringify
564
562
565 def _flatten(thing):
563 def _flatten(thing):
566 '''yield a single stream from a possibly nested set of iterators'''
564 '''yield a single stream from a possibly nested set of iterators'''
567 if isinstance(thing, str):
565 if isinstance(thing, str):
568 yield thing
566 yield thing
569 elif not util.safehasattr(thing, '__iter__'):
567 elif not util.safehasattr(thing, '__iter__'):
570 if thing is not None:
568 if thing is not None:
571 yield str(thing)
569 yield str(thing)
572 else:
570 else:
573 for i in thing:
571 for i in thing:
574 if isinstance(i, str):
572 if isinstance(i, str):
575 yield i
573 yield i
576 elif not util.safehasattr(i, '__iter__'):
574 elif not util.safehasattr(i, '__iter__'):
577 if i is not None:
575 if i is not None:
578 yield str(i)
576 yield str(i)
579 elif i is not None:
577 elif i is not None:
580 for j in _flatten(i):
578 for j in _flatten(i):
581 yield j
579 yield j
582
580
583 def parsestring(s, quoted=True):
581 def parsestring(s, quoted=True):
584 '''parse a string using simple c-like syntax.
582 '''parse a string using simple c-like syntax.
585 string must be in quotes if quoted is True.'''
583 string must be in quotes if quoted is True.'''
586 if quoted:
584 if quoted:
587 if len(s) < 2 or s[0] != s[-1]:
585 if len(s) < 2 or s[0] != s[-1]:
588 raise SyntaxError(_('unmatched quotes'))
586 raise SyntaxError(_('unmatched quotes'))
589 return s[1:-1].decode('string_escape')
587 return s[1:-1].decode('string_escape')
590
588
591 return s.decode('string_escape')
589 return s.decode('string_escape')
592
590
593 class engine(object):
591 class engine(object):
594 '''template expansion engine.
592 '''template expansion engine.
595
593
596 template expansion works like this. a map file contains key=value
594 template expansion works like this. a map file contains key=value
597 pairs. if value is quoted, it is treated as string. otherwise, it
595 pairs. if value is quoted, it is treated as string. otherwise, it
598 is treated as name of template file.
596 is treated as name of template file.
599
597
600 templater is asked to expand a key in map. it looks up key, and
598 templater is asked to expand a key in map. it looks up key, and
601 looks for strings like this: {foo}. it expands {foo} by looking up
599 looks for strings like this: {foo}. it expands {foo} by looking up
602 foo in map, and substituting it. expansion is recursive: it stops
600 foo in map, and substituting it. expansion is recursive: it stops
603 when there is no more {foo} to replace.
601 when there is no more {foo} to replace.
604
602
605 expansion also allows formatting and filtering.
603 expansion also allows formatting and filtering.
606
604
607 format uses key to expand each item in list. syntax is
605 format uses key to expand each item in list. syntax is
608 {key%format}.
606 {key%format}.
609
607
610 filter uses function to transform value. syntax is
608 filter uses function to transform value. syntax is
611 {key|filter1|filter2|...}.'''
609 {key|filter1|filter2|...}.'''
612
610
613 def __init__(self, loader, filters={}, defaults={}):
611 def __init__(self, loader, filters={}, defaults={}):
614 self._loader = loader
612 self._loader = loader
615 self._filters = filters
613 self._filters = filters
616 self._defaults = defaults
614 self._defaults = defaults
617 self._cache = {}
615 self._cache = {}
618
616
619 def _load(self, t):
617 def _load(self, t):
620 '''load, parse, and cache a template'''
618 '''load, parse, and cache a template'''
621 if t not in self._cache:
619 if t not in self._cache:
622 self._cache[t] = compiletemplate(self._loader(t), self)
620 self._cache[t] = compiletemplate(self._loader(t), self)
623 return self._cache[t]
621 return self._cache[t]
624
622
625 def process(self, t, mapping):
623 def process(self, t, mapping):
626 '''Perform expansion. t is name of map element to expand.
624 '''Perform expansion. t is name of map element to expand.
627 mapping contains added elements for use during expansion. Is a
625 mapping contains added elements for use during expansion. Is a
628 generator.'''
626 generator.'''
629 return _flatten(runtemplate(self, mapping, self._load(t)))
627 return _flatten(runtemplate(self, mapping, self._load(t)))
630
628
631 engines = {'default': engine}
629 engines = {'default': engine}
632
630
633 def stylelist():
631 def stylelist():
634 paths = templatepaths()
632 paths = templatepaths()
635 if not paths:
633 if not paths:
636 return _('no templates found, try `hg debuginstall` for more info')
634 return _('no templates found, try `hg debuginstall` for more info')
637 dirlist = os.listdir(paths[0])
635 dirlist = os.listdir(paths[0])
638 stylelist = []
636 stylelist = []
639 for file in dirlist:
637 for file in dirlist:
640 split = file.split(".")
638 split = file.split(".")
641 if split[0] == "map-cmdline":
639 if split[0] == "map-cmdline":
642 stylelist.append(split[1])
640 stylelist.append(split[1])
643 return ", ".join(sorted(stylelist))
641 return ", ".join(sorted(stylelist))
644
642
645 class TemplateNotFound(util.Abort):
643 class TemplateNotFound(util.Abort):
646 pass
644 pass
647
645
648 class templater(object):
646 class templater(object):
649
647
650 def __init__(self, mapfile, filters={}, defaults={}, cache={},
648 def __init__(self, mapfile, filters={}, defaults={}, cache={},
651 minchunk=1024, maxchunk=65536):
649 minchunk=1024, maxchunk=65536):
652 '''set up template engine.
650 '''set up template engine.
653 mapfile is name of file to read map definitions from.
651 mapfile is name of file to read map definitions from.
654 filters is dict of functions. each transforms a value into another.
652 filters is dict of functions. each transforms a value into another.
655 defaults is dict of default map definitions.'''
653 defaults is dict of default map definitions.'''
656 self.mapfile = mapfile or 'template'
654 self.mapfile = mapfile or 'template'
657 self.cache = cache.copy()
655 self.cache = cache.copy()
658 self.map = {}
656 self.map = {}
659 self.base = (mapfile and os.path.dirname(mapfile)) or ''
657 self.base = (mapfile and os.path.dirname(mapfile)) or ''
660 self.filters = templatefilters.filters.copy()
658 self.filters = templatefilters.filters.copy()
661 self.filters.update(filters)
659 self.filters.update(filters)
662 self.defaults = defaults
660 self.defaults = defaults
663 self.minchunk, self.maxchunk = minchunk, maxchunk
661 self.minchunk, self.maxchunk = minchunk, maxchunk
664 self.ecache = {}
662 self.ecache = {}
665
663
666 if not mapfile:
664 if not mapfile:
667 return
665 return
668 if not os.path.exists(mapfile):
666 if not os.path.exists(mapfile):
669 raise util.Abort(_("style '%s' not found") % mapfile,
667 raise util.Abort(_("style '%s' not found") % mapfile,
670 hint=_("available styles: %s") % stylelist())
668 hint=_("available styles: %s") % stylelist())
671
669
672 conf = config.config()
670 conf = config.config()
673 conf.read(mapfile)
671 conf.read(mapfile)
674
672
675 for key, val in conf[''].items():
673 for key, val in conf[''].items():
676 if not val:
674 if not val:
677 raise SyntaxError(_('%s: missing value') % conf.source('', key))
675 raise SyntaxError(_('%s: missing value') % conf.source('', key))
678 if val[0] in "'\"":
676 if val[0] in "'\"":
679 try:
677 try:
680 self.cache[key] = parsestring(val)
678 self.cache[key] = parsestring(val)
681 except SyntaxError, inst:
679 except SyntaxError, inst:
682 raise SyntaxError('%s: %s' %
680 raise SyntaxError('%s: %s' %
683 (conf.source('', key), inst.args[0]))
681 (conf.source('', key), inst.args[0]))
684 else:
682 else:
685 val = 'default', val
683 val = 'default', val
686 if ':' in val[1]:
684 if ':' in val[1]:
687 val = val[1].split(':', 1)
685 val = val[1].split(':', 1)
688 self.map[key] = val[0], os.path.join(self.base, val[1])
686 self.map[key] = val[0], os.path.join(self.base, val[1])
689
687
690 def __contains__(self, key):
688 def __contains__(self, key):
691 return key in self.cache or key in self.map
689 return key in self.cache or key in self.map
692
690
693 def load(self, t):
691 def load(self, t):
694 '''Get the template for the given template name. Use a local cache.'''
692 '''Get the template for the given template name. Use a local cache.'''
695 if t not in self.cache:
693 if t not in self.cache:
696 try:
694 try:
697 self.cache[t] = util.readfile(self.map[t][1])
695 self.cache[t] = util.readfile(self.map[t][1])
698 except KeyError, inst:
696 except KeyError, inst:
699 raise TemplateNotFound(_('"%s" not in template map') %
697 raise TemplateNotFound(_('"%s" not in template map') %
700 inst.args[0])
698 inst.args[0])
701 except IOError, inst:
699 except IOError, inst:
702 raise IOError(inst.args[0], _('template file %s: %s') %
700 raise IOError(inst.args[0], _('template file %s: %s') %
703 (self.map[t][1], inst.args[1]))
701 (self.map[t][1], inst.args[1]))
704 return self.cache[t]
702 return self.cache[t]
705
703
706 def __call__(self, t, **mapping):
704 def __call__(self, t, **mapping):
707 ttype = t in self.map and self.map[t][0] or 'default'
705 ttype = t in self.map and self.map[t][0] or 'default'
708 if ttype not in self.ecache:
706 if ttype not in self.ecache:
709 self.ecache[ttype] = engines[ttype](self.load,
707 self.ecache[ttype] = engines[ttype](self.load,
710 self.filters, self.defaults)
708 self.filters, self.defaults)
711 proc = self.ecache[ttype]
709 proc = self.ecache[ttype]
712
710
713 stream = proc.process(t, mapping)
711 stream = proc.process(t, mapping)
714 if self.minchunk:
712 if self.minchunk:
715 stream = util.increasingchunks(stream, min=self.minchunk,
713 stream = util.increasingchunks(stream, min=self.minchunk,
716 max=self.maxchunk)
714 max=self.maxchunk)
717 return stream
715 return stream
718
716
719 def templatepaths():
717 def templatepaths():
720 '''return locations used for template files.'''
718 '''return locations used for template files.'''
721 pathsrel = ['templates']
719 pathsrel = ['templates']
722 paths = [os.path.normpath(os.path.join(util.datapath, f))
720 paths = [os.path.normpath(os.path.join(util.datapath, f))
723 for f in pathsrel]
721 for f in pathsrel]
724 return [p for p in paths if os.path.isdir(p)]
722 return [p for p in paths if os.path.isdir(p)]
725
723
726 def templatepath(name):
724 def templatepath(name):
727 '''return location of template file. returns None if not found.'''
725 '''return location of template file. returns None if not found.'''
728 for p in templatepaths():
726 for p in templatepaths():
729 f = os.path.join(p, name)
727 f = os.path.join(p, name)
730 if os.path.exists(f):
728 if os.path.exists(f):
731 return f
729 return f
732 return None
730 return None
733
731
734 def stylemap(styles, paths=None):
732 def stylemap(styles, paths=None):
735 """Return path to mapfile for a given style.
733 """Return path to mapfile for a given style.
736
734
737 Searches mapfile in the following locations:
735 Searches mapfile in the following locations:
738 1. templatepath/style/map
736 1. templatepath/style/map
739 2. templatepath/map-style
737 2. templatepath/map-style
740 3. templatepath/map
738 3. templatepath/map
741 """
739 """
742
740
743 if paths is None:
741 if paths is None:
744 paths = templatepaths()
742 paths = templatepaths()
745 elif isinstance(paths, str):
743 elif isinstance(paths, str):
746 paths = [paths]
744 paths = [paths]
747
745
748 if isinstance(styles, str):
746 if isinstance(styles, str):
749 styles = [styles]
747 styles = [styles]
750
748
751 for style in styles:
749 for style in styles:
752 if not style:
750 if not style:
753 continue
751 continue
754 locations = [os.path.join(style, 'map'), 'map-' + style]
752 locations = [os.path.join(style, 'map'), 'map-' + style]
755 locations.append('map')
753 locations.append('map')
756
754
757 for path in paths:
755 for path in paths:
758 for location in locations:
756 for location in locations:
759 mapfile = os.path.join(path, location)
757 mapfile = os.path.join(path, location)
760 if os.path.isfile(mapfile):
758 if os.path.isfile(mapfile):
761 return style, mapfile
759 return style, mapfile
762
760
763 raise RuntimeError("No hgweb templates found in %r" % paths)
761 raise RuntimeError("No hgweb templates found in %r" % paths)
@@ -1,150 +1,156 b''
1 $ cat >> $HGRCPATH <<EOF
1 $ cat >> $HGRCPATH <<EOF
2 > [extensions]
2 > [extensions]
3 > convert=
3 > convert=
4 > [convert]
4 > [convert]
5 > hg.saverev=False
5 > hg.saverev=False
6 > EOF
6 > EOF
7 $ hg init orig
7 $ hg init orig
8 $ cd orig
8 $ cd orig
9 $ echo foo > foo
9 $ echo foo > foo
10 $ echo bar > bar
10 $ echo bar > bar
11 $ hg ci -qAm 'add foo bar' -d '0 0'
11 $ hg ci -qAm 'add foo bar' -d '0 0'
12 $ echo >> foo
12 $ echo >> foo
13 $ hg ci -m 'change foo' -d '1 0'
13 $ hg ci -m 'change foo' -d '1 0'
14 $ hg up -qC 0
14 $ hg up -qC 0
15 $ hg copy --after --force foo bar
15 $ hg copy --after --force foo bar
16 $ hg copy foo baz
16 $ hg copy foo baz
17 $ hg ci -m 'make bar and baz copies of foo' -d '2 0'
17 $ hg ci -m 'make bar and baz copies of foo' -d '2 0'
18 created new head
18 created new head
19
20 Test that template can print all file copies (issue4362)
21 $ hg log -r . --template "{file_copies % ' File: {file_copy}\n'}"
22 File: bar (foo)
23 File: baz (foo)
24
19 $ hg bookmark premerge1
25 $ hg bookmark premerge1
20 $ hg merge -r 1
26 $ hg merge -r 1
21 merging baz and foo to baz
27 merging baz and foo to baz
22 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
28 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
23 (branch merge, don't forget to commit)
29 (branch merge, don't forget to commit)
24 $ hg ci -m 'merge local copy' -d '3 0'
30 $ hg ci -m 'merge local copy' -d '3 0'
25 $ hg up -C 1
31 $ hg up -C 1
26 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
32 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
27 (leaving bookmark premerge1)
33 (leaving bookmark premerge1)
28 $ hg bookmark premerge2
34 $ hg bookmark premerge2
29 $ hg merge 2
35 $ hg merge 2
30 merging foo and baz to baz
36 merging foo and baz to baz
31 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
37 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
32 (branch merge, don't forget to commit)
38 (branch merge, don't forget to commit)
33 $ hg ci -m 'merge remote copy' -d '4 0'
39 $ hg ci -m 'merge remote copy' -d '4 0'
34 created new head
40 created new head
35 #if execbit
41 #if execbit
36 $ chmod +x baz
42 $ chmod +x baz
37 #else
43 #else
38 $ echo some other change to make sure we get a rev 5 > baz
44 $ echo some other change to make sure we get a rev 5 > baz
39 #endif
45 #endif
40 $ hg ci -m 'mark baz executable' -d '5 0'
46 $ hg ci -m 'mark baz executable' -d '5 0'
41 $ cd ..
47 $ cd ..
42 $ hg convert --datesort orig new 2>&1 | grep -v 'subversion python bindings could not be loaded'
48 $ hg convert --datesort orig new 2>&1 | grep -v 'subversion python bindings could not be loaded'
43 initializing destination new repository
49 initializing destination new repository
44 scanning source...
50 scanning source...
45 sorting...
51 sorting...
46 converting...
52 converting...
47 5 add foo bar
53 5 add foo bar
48 4 change foo
54 4 change foo
49 3 make bar and baz copies of foo
55 3 make bar and baz copies of foo
50 2 merge local copy
56 2 merge local copy
51 1 merge remote copy
57 1 merge remote copy
52 0 mark baz executable
58 0 mark baz executable
53 updating bookmarks
59 updating bookmarks
54 $ cd new
60 $ cd new
55 $ hg out ../orig
61 $ hg out ../orig
56 comparing with ../orig
62 comparing with ../orig
57 searching for changes
63 searching for changes
58 no changes found
64 no changes found
59 [1]
65 [1]
60 #if execbit
66 #if execbit
61 $ hg bookmarks
67 $ hg bookmarks
62 premerge1 3:973ef48a98a4
68 premerge1 3:973ef48a98a4
63 premerge2 5:13d9b87cf8f8
69 premerge2 5:13d9b87cf8f8
64 #else
70 #else
65 Different hash because no x bit
71 Different hash because no x bit
66 $ hg bookmarks
72 $ hg bookmarks
67 premerge1 3:973ef48a98a4
73 premerge1 3:973ef48a98a4
68 premerge2 5:df0779bcf33c
74 premerge2 5:df0779bcf33c
69 #endif
75 #endif
70 $ cd ..
76 $ cd ..
71
77
72 check shamap LF and CRLF handling
78 check shamap LF and CRLF handling
73
79
74 $ cat > rewrite.py <<EOF
80 $ cat > rewrite.py <<EOF
75 > import sys
81 > import sys
76 > # Interlace LF and CRLF
82 > # Interlace LF and CRLF
77 > lines = [(l.rstrip() + ((i % 2) and '\n' or '\r\n'))
83 > lines = [(l.rstrip() + ((i % 2) and '\n' or '\r\n'))
78 > for i, l in enumerate(file(sys.argv[1]))]
84 > for i, l in enumerate(file(sys.argv[1]))]
79 > file(sys.argv[1], 'wb').write(''.join(lines))
85 > file(sys.argv[1], 'wb').write(''.join(lines))
80 > EOF
86 > EOF
81 $ python rewrite.py new/.hg/shamap
87 $ python rewrite.py new/.hg/shamap
82 $ cd orig
88 $ cd orig
83 $ hg up -qC 1
89 $ hg up -qC 1
84 $ echo foo >> foo
90 $ echo foo >> foo
85 $ hg ci -qm 'change foo again'
91 $ hg ci -qm 'change foo again'
86 $ hg up -qC 2
92 $ hg up -qC 2
87 $ echo foo >> foo
93 $ echo foo >> foo
88 $ hg ci -qm 'change foo again again'
94 $ hg ci -qm 'change foo again again'
89 $ cd ..
95 $ cd ..
90 $ hg convert --datesort orig new 2>&1 | grep -v 'subversion python bindings could not be loaded'
96 $ hg convert --datesort orig new 2>&1 | grep -v 'subversion python bindings could not be loaded'
91 scanning source...
97 scanning source...
92 sorting...
98 sorting...
93 converting...
99 converting...
94 1 change foo again again
100 1 change foo again again
95 0 change foo again
101 0 change foo again
96 updating bookmarks
102 updating bookmarks
97
103
98 init broken repository
104 init broken repository
99
105
100 $ hg init broken
106 $ hg init broken
101 $ cd broken
107 $ cd broken
102 $ echo a >> a
108 $ echo a >> a
103 $ echo b >> b
109 $ echo b >> b
104 $ hg ci -qAm init
110 $ hg ci -qAm init
105 $ echo a >> a
111 $ echo a >> a
106 $ echo b >> b
112 $ echo b >> b
107 $ hg copy b c
113 $ hg copy b c
108 $ hg ci -qAm changeall
114 $ hg ci -qAm changeall
109 $ hg up -qC 0
115 $ hg up -qC 0
110 $ echo bc >> b
116 $ echo bc >> b
111 $ hg ci -m changebagain
117 $ hg ci -m changebagain
112 created new head
118 created new head
113 $ HGMERGE=internal:local hg -q merge
119 $ HGMERGE=internal:local hg -q merge
114 $ hg ci -m merge
120 $ hg ci -m merge
115 $ hg mv b d
121 $ hg mv b d
116 $ hg ci -m moveb
122 $ hg ci -m moveb
117
123
118 break it
124 break it
119
125
120 $ rm .hg/store/data/b.*
126 $ rm .hg/store/data/b.*
121 $ cd ..
127 $ cd ..
122 $ hg --config convert.hg.ignoreerrors=True convert broken fixed
128 $ hg --config convert.hg.ignoreerrors=True convert broken fixed
123 initializing destination fixed repository
129 initializing destination fixed repository
124 scanning source...
130 scanning source...
125 sorting...
131 sorting...
126 converting...
132 converting...
127 4 init
133 4 init
128 ignoring: data/b.i@1e88685f5dde: no match found
134 ignoring: data/b.i@1e88685f5dde: no match found
129 3 changeall
135 3 changeall
130 2 changebagain
136 2 changebagain
131 1 merge
137 1 merge
132 0 moveb
138 0 moveb
133 $ hg -R fixed verify
139 $ hg -R fixed verify
134 checking changesets
140 checking changesets
135 checking manifests
141 checking manifests
136 crosschecking files in changesets and manifests
142 crosschecking files in changesets and manifests
137 checking files
143 checking files
138 3 files, 5 changesets, 5 total revisions
144 3 files, 5 changesets, 5 total revisions
139
145
140 manifest -r 0
146 manifest -r 0
141
147
142 $ hg -R fixed manifest -r 0
148 $ hg -R fixed manifest -r 0
143 a
149 a
144
150
145 manifest -r tip
151 manifest -r tip
146
152
147 $ hg -R fixed manifest -r tip
153 $ hg -R fixed manifest -r tip
148 a
154 a
149 c
155 c
150 d
156 d
@@ -1,691 +1,697 b''
1 Create a repo with some stuff in it:
1 Create a repo with some stuff in it:
2
2
3 $ hg init a
3 $ hg init a
4 $ cd a
4 $ cd a
5 $ echo a > a
5 $ echo a > a
6 $ echo a > d
6 $ echo a > d
7 $ echo a > e
7 $ echo a > e
8 $ hg ci -qAm0
8 $ hg ci -qAm0
9 $ echo b > a
9 $ echo b > a
10 $ hg ci -m1 -u bar
10 $ hg ci -m1 -u bar
11 $ hg mv a b
11 $ hg mv a b
12 $ hg ci -m2
12 $ hg ci -m2
13 $ hg cp b c
13 $ hg cp b c
14 $ hg ci -m3 -u baz
14 $ hg ci -m3 -u baz
15 $ echo b > d
15 $ echo b > d
16 $ echo f > e
16 $ echo f > e
17 $ hg ci -m4
17 $ hg ci -m4
18 $ hg up -q 3
18 $ hg up -q 3
19 $ echo b > e
19 $ echo b > e
20 $ hg branch -q stable
20 $ hg branch -q stable
21 $ hg ci -m5
21 $ hg ci -m5
22 $ hg merge -q default --tool internal:local
22 $ hg merge -q default --tool internal:local
23 $ hg branch -q default
23 $ hg branch -q default
24 $ hg ci -m6
24 $ hg ci -m6
25 $ hg phase --public 3
25 $ hg phase --public 3
26 $ hg phase --force --secret 6
26 $ hg phase --force --secret 6
27
27
28 $ hg log -G --template '{author}@{rev}.{phase}: {desc}\n'
28 $ hg log -G --template '{author}@{rev}.{phase}: {desc}\n'
29 @ test@6.secret: 6
29 @ test@6.secret: 6
30 |\
30 |\
31 | o test@5.draft: 5
31 | o test@5.draft: 5
32 | |
32 | |
33 o | test@4.draft: 4
33 o | test@4.draft: 4
34 |/
34 |/
35 o baz@3.public: 3
35 o baz@3.public: 3
36 |
36 |
37 o test@2.public: 2
37 o test@2.public: 2
38 |
38 |
39 o bar@1.public: 1
39 o bar@1.public: 1
40 |
40 |
41 o test@0.public: 0
41 o test@0.public: 0
42
42
43
43
44 Need to specify a rev:
44 Need to specify a rev:
45
45
46 $ hg graft
46 $ hg graft
47 abort: no revisions specified
47 abort: no revisions specified
48 [255]
48 [255]
49
49
50 Can't graft ancestor:
50 Can't graft ancestor:
51
51
52 $ hg graft 1 2
52 $ hg graft 1 2
53 skipping ancestor revision 1
53 skipping ancestor revision 1
54 skipping ancestor revision 2
54 skipping ancestor revision 2
55 [255]
55 [255]
56
56
57 Specify revisions with -r:
57 Specify revisions with -r:
58
58
59 $ hg graft -r 1 -r 2
59 $ hg graft -r 1 -r 2
60 skipping ancestor revision 1
60 skipping ancestor revision 1
61 skipping ancestor revision 2
61 skipping ancestor revision 2
62 [255]
62 [255]
63
63
64 $ hg graft -r 1 2
64 $ hg graft -r 1 2
65 skipping ancestor revision 2
65 skipping ancestor revision 2
66 skipping ancestor revision 1
66 skipping ancestor revision 1
67 [255]
67 [255]
68
68
69 Can't graft with dirty wd:
69 Can't graft with dirty wd:
70
70
71 $ hg up -q 0
71 $ hg up -q 0
72 $ echo foo > a
72 $ echo foo > a
73 $ hg graft 1
73 $ hg graft 1
74 abort: uncommitted changes
74 abort: uncommitted changes
75 [255]
75 [255]
76 $ hg revert a
76 $ hg revert a
77
77
78 Graft a rename:
78 Graft a rename:
79 (this also tests that editor is invoked if '--edit' is specified)
79 (this also tests that editor is invoked if '--edit' is specified)
80
80
81 $ hg status --rev "2^1" --rev 2
81 $ hg status --rev "2^1" --rev 2
82 A b
82 A b
83 R a
83 R a
84 $ HGEDITOR=cat hg graft 2 -u foo --edit
84 $ HGEDITOR=cat hg graft 2 -u foo --edit
85 grafting revision 2
85 grafting revision 2
86 merging a and b to b
86 merging a and b to b
87 2
87 2
88
88
89
89
90 HG: Enter commit message. Lines beginning with 'HG:' are removed.
90 HG: Enter commit message. Lines beginning with 'HG:' are removed.
91 HG: Leave message empty to abort commit.
91 HG: Leave message empty to abort commit.
92 HG: --
92 HG: --
93 HG: user: foo
93 HG: user: foo
94 HG: branch 'default'
94 HG: branch 'default'
95 HG: added b
95 HG: added b
96 HG: removed a
96 HG: removed a
97 $ hg export tip --git
97 $ hg export tip --git
98 # HG changeset patch
98 # HG changeset patch
99 # User foo
99 # User foo
100 # Date 0 0
100 # Date 0 0
101 # Thu Jan 01 00:00:00 1970 +0000
101 # Thu Jan 01 00:00:00 1970 +0000
102 # Node ID ef0ef43d49e79e81ddafdc7997401ba0041efc82
102 # Node ID ef0ef43d49e79e81ddafdc7997401ba0041efc82
103 # Parent 68795b066622ca79a25816a662041d8f78f3cd9e
103 # Parent 68795b066622ca79a25816a662041d8f78f3cd9e
104 2
104 2
105
105
106 diff --git a/a b/b
106 diff --git a/a b/b
107 rename from a
107 rename from a
108 rename to b
108 rename to b
109
109
110 Look for extra:source
110 Look for extra:source
111
111
112 $ hg log --debug -r tip
112 $ hg log --debug -r tip
113 changeset: 7:ef0ef43d49e79e81ddafdc7997401ba0041efc82
113 changeset: 7:ef0ef43d49e79e81ddafdc7997401ba0041efc82
114 tag: tip
114 tag: tip
115 phase: draft
115 phase: draft
116 parent: 0:68795b066622ca79a25816a662041d8f78f3cd9e
116 parent: 0:68795b066622ca79a25816a662041d8f78f3cd9e
117 parent: -1:0000000000000000000000000000000000000000
117 parent: -1:0000000000000000000000000000000000000000
118 manifest: 7:e59b6b228f9cbf9903d5e9abf996e083a1f533eb
118 manifest: 7:e59b6b228f9cbf9903d5e9abf996e083a1f533eb
119 user: foo
119 user: foo
120 date: Thu Jan 01 00:00:00 1970 +0000
120 date: Thu Jan 01 00:00:00 1970 +0000
121 files+: b
121 files+: b
122 files-: a
122 files-: a
123 extra: branch=default
123 extra: branch=default
124 extra: source=5c095ad7e90f871700f02dd1fa5012cb4498a2d4
124 extra: source=5c095ad7e90f871700f02dd1fa5012cb4498a2d4
125 description:
125 description:
126 2
126 2
127
127
128
128
129
129
130 Graft out of order, skipping a merge and a duplicate
130 Graft out of order, skipping a merge and a duplicate
131 (this also tests that editor is not invoked if '--edit' is not specified)
131 (this also tests that editor is not invoked if '--edit' is not specified)
132
132
133 $ hg graft 1 5 4 3 'merge()' 2 -n
133 $ hg graft 1 5 4 3 'merge()' 2 -n
134 skipping ungraftable merge revision 6
134 skipping ungraftable merge revision 6
135 skipping revision 2 (already grafted to 7)
135 skipping revision 2 (already grafted to 7)
136 grafting revision 1
136 grafting revision 1
137 grafting revision 5
137 grafting revision 5
138 grafting revision 4
138 grafting revision 4
139 grafting revision 3
139 grafting revision 3
140
140
141 $ HGEDITOR=cat hg graft 1 5 4 3 'merge()' 2 --debug
141 $ HGEDITOR=cat hg graft 1 5 4 3 'merge()' 2 --debug
142 skipping ungraftable merge revision 6
142 skipping ungraftable merge revision 6
143 scanning for duplicate grafts
143 scanning for duplicate grafts
144 skipping revision 2 (already grafted to 7)
144 skipping revision 2 (already grafted to 7)
145 grafting revision 1
145 grafting revision 1
146 searching for copies back to rev 1
146 searching for copies back to rev 1
147 unmatched files in local:
147 unmatched files in local:
148 b
148 b
149 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
149 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
150 src: 'a' -> dst: 'b' *
150 src: 'a' -> dst: 'b' *
151 checking for directory renames
151 checking for directory renames
152 resolving manifests
152 resolving manifests
153 branchmerge: True, force: True, partial: False
153 branchmerge: True, force: True, partial: False
154 ancestor: 68795b066622, local: ef0ef43d49e7+, remote: 5d205f8b35b6
154 ancestor: 68795b066622, local: ef0ef43d49e7+, remote: 5d205f8b35b6
155 preserving b for resolve of b
155 preserving b for resolve of b
156 b: local copied/moved from a -> m
156 b: local copied/moved from a -> m
157 updating: b 1/1 files (100.00%)
157 updating: b 1/1 files (100.00%)
158 picked tool 'internal:merge' for b (binary False symlink False)
158 picked tool 'internal:merge' for b (binary False symlink False)
159 merging b and a to b
159 merging b and a to b
160 my b@ef0ef43d49e7+ other a@5d205f8b35b6 ancestor a@68795b066622
160 my b@ef0ef43d49e7+ other a@5d205f8b35b6 ancestor a@68795b066622
161 premerge successful
161 premerge successful
162 b
162 b
163 grafting revision 5
163 grafting revision 5
164 searching for copies back to rev 1
164 searching for copies back to rev 1
165 resolving manifests
165 resolving manifests
166 branchmerge: True, force: True, partial: False
166 branchmerge: True, force: True, partial: False
167 ancestor: 4c60f11aa304, local: 6b9e5368ca4e+, remote: 97f8bfe72746
167 ancestor: 4c60f11aa304, local: 6b9e5368ca4e+, remote: 97f8bfe72746
168 e: remote is newer -> g
168 e: remote is newer -> g
169 getting e
169 getting e
170 updating: e 1/1 files (100.00%)
170 updating: e 1/1 files (100.00%)
171 b: keep -> k
171 b: keep -> k
172 e
172 e
173 grafting revision 4
173 grafting revision 4
174 searching for copies back to rev 1
174 searching for copies back to rev 1
175 resolving manifests
175 resolving manifests
176 branchmerge: True, force: True, partial: False
176 branchmerge: True, force: True, partial: False
177 ancestor: 4c60f11aa304, local: 1905859650ec+, remote: 9c233e8e184d
177 ancestor: 4c60f11aa304, local: 1905859650ec+, remote: 9c233e8e184d
178 preserving e for resolve of e
178 preserving e for resolve of e
179 d: remote is newer -> g
179 d: remote is newer -> g
180 getting d
180 getting d
181 updating: d 1/2 files (50.00%)
181 updating: d 1/2 files (50.00%)
182 b: keep -> k
182 b: keep -> k
183 e: versions differ -> m
183 e: versions differ -> m
184 updating: e 2/2 files (100.00%)
184 updating: e 2/2 files (100.00%)
185 picked tool 'internal:merge' for e (binary False symlink False)
185 picked tool 'internal:merge' for e (binary False symlink False)
186 merging e
186 merging e
187 my e@1905859650ec+ other e@9c233e8e184d ancestor e@68795b066622
187 my e@1905859650ec+ other e@9c233e8e184d ancestor e@68795b066622
188 warning: conflicts during merge.
188 warning: conflicts during merge.
189 merging e incomplete! (edit conflicts, then use 'hg resolve --mark')
189 merging e incomplete! (edit conflicts, then use 'hg resolve --mark')
190 abort: unresolved conflicts, can't continue
190 abort: unresolved conflicts, can't continue
191 (use hg resolve and hg graft --continue)
191 (use hg resolve and hg graft --continue)
192 [255]
192 [255]
193
193
194 Commit while interrupted should fail:
194 Commit while interrupted should fail:
195
195
196 $ hg ci -m 'commit interrupted graft'
196 $ hg ci -m 'commit interrupted graft'
197 abort: graft in progress
197 abort: graft in progress
198 (use 'hg graft --continue' or 'hg update' to abort)
198 (use 'hg graft --continue' or 'hg update' to abort)
199 [255]
199 [255]
200
200
201 Abort the graft and try committing:
201 Abort the graft and try committing:
202
202
203 $ hg up -C .
203 $ hg up -C .
204 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
204 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
205 $ echo c >> e
205 $ echo c >> e
206 $ hg ci -mtest
206 $ hg ci -mtest
207
207
208 $ hg strip . --config extensions.mq=
208 $ hg strip . --config extensions.mq=
209 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
209 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
210 saved backup bundle to $TESTTMP/a/.hg/strip-backup/*-backup.hg (glob)
210 saved backup bundle to $TESTTMP/a/.hg/strip-backup/*-backup.hg (glob)
211
211
212 Graft again:
212 Graft again:
213
213
214 $ hg graft 1 5 4 3 'merge()' 2
214 $ hg graft 1 5 4 3 'merge()' 2
215 skipping ungraftable merge revision 6
215 skipping ungraftable merge revision 6
216 skipping revision 2 (already grafted to 7)
216 skipping revision 2 (already grafted to 7)
217 skipping revision 1 (already grafted to 8)
217 skipping revision 1 (already grafted to 8)
218 skipping revision 5 (already grafted to 9)
218 skipping revision 5 (already grafted to 9)
219 grafting revision 4
219 grafting revision 4
220 merging e
220 merging e
221 warning: conflicts during merge.
221 warning: conflicts during merge.
222 merging e incomplete! (edit conflicts, then use 'hg resolve --mark')
222 merging e incomplete! (edit conflicts, then use 'hg resolve --mark')
223 abort: unresolved conflicts, can't continue
223 abort: unresolved conflicts, can't continue
224 (use hg resolve and hg graft --continue)
224 (use hg resolve and hg graft --continue)
225 [255]
225 [255]
226
226
227 Continue without resolve should fail:
227 Continue without resolve should fail:
228
228
229 $ hg graft -c
229 $ hg graft -c
230 grafting revision 4
230 grafting revision 4
231 abort: unresolved merge conflicts (see hg help resolve)
231 abort: unresolved merge conflicts (see hg help resolve)
232 [255]
232 [255]
233
233
234 Fix up:
234 Fix up:
235
235
236 $ echo b > e
236 $ echo b > e
237 $ hg resolve -m e
237 $ hg resolve -m e
238 (no more unresolved files)
238 (no more unresolved files)
239
239
240 Continue with a revision should fail:
240 Continue with a revision should fail:
241
241
242 $ hg graft -c 6
242 $ hg graft -c 6
243 abort: can't specify --continue and revisions
243 abort: can't specify --continue and revisions
244 [255]
244 [255]
245
245
246 $ hg graft -c -r 6
246 $ hg graft -c -r 6
247 abort: can't specify --continue and revisions
247 abort: can't specify --continue and revisions
248 [255]
248 [255]
249
249
250 Continue for real, clobber usernames
250 Continue for real, clobber usernames
251
251
252 $ hg graft -c -U
252 $ hg graft -c -U
253 grafting revision 4
253 grafting revision 4
254 grafting revision 3
254 grafting revision 3
255
255
256 Compare with original:
256 Compare with original:
257
257
258 $ hg diff -r 6
258 $ hg diff -r 6
259 $ hg status --rev 0:. -C
259 $ hg status --rev 0:. -C
260 M d
260 M d
261 M e
261 M e
262 A b
262 A b
263 a
263 a
264 A c
264 A c
265 a
265 a
266 R a
266 R a
267
267
268 View graph:
268 View graph:
269
269
270 $ hg log -G --template '{author}@{rev}.{phase}: {desc}\n'
270 $ hg log -G --template '{author}@{rev}.{phase}: {desc}\n'
271 @ test@11.draft: 3
271 @ test@11.draft: 3
272 |
272 |
273 o test@10.draft: 4
273 o test@10.draft: 4
274 |
274 |
275 o test@9.draft: 5
275 o test@9.draft: 5
276 |
276 |
277 o bar@8.draft: 1
277 o bar@8.draft: 1
278 |
278 |
279 o foo@7.draft: 2
279 o foo@7.draft: 2
280 |
280 |
281 | o test@6.secret: 6
281 | o test@6.secret: 6
282 | |\
282 | |\
283 | | o test@5.draft: 5
283 | | o test@5.draft: 5
284 | | |
284 | | |
285 | o | test@4.draft: 4
285 | o | test@4.draft: 4
286 | |/
286 | |/
287 | o baz@3.public: 3
287 | o baz@3.public: 3
288 | |
288 | |
289 | o test@2.public: 2
289 | o test@2.public: 2
290 | |
290 | |
291 | o bar@1.public: 1
291 | o bar@1.public: 1
292 |/
292 |/
293 o test@0.public: 0
293 o test@0.public: 0
294
294
295 Graft again onto another branch should preserve the original source
295 Graft again onto another branch should preserve the original source
296 $ hg up -q 0
296 $ hg up -q 0
297 $ echo 'g'>g
297 $ echo 'g'>g
298 $ hg add g
298 $ hg add g
299 $ hg ci -m 7
299 $ hg ci -m 7
300 created new head
300 created new head
301 $ hg graft 7
301 $ hg graft 7
302 grafting revision 7
302 grafting revision 7
303
303
304 $ hg log -r 7 --template '{rev}:{node}\n'
304 $ hg log -r 7 --template '{rev}:{node}\n'
305 7:ef0ef43d49e79e81ddafdc7997401ba0041efc82
305 7:ef0ef43d49e79e81ddafdc7997401ba0041efc82
306 $ hg log -r 2 --template '{rev}:{node}\n'
306 $ hg log -r 2 --template '{rev}:{node}\n'
307 2:5c095ad7e90f871700f02dd1fa5012cb4498a2d4
307 2:5c095ad7e90f871700f02dd1fa5012cb4498a2d4
308
308
309 $ hg log --debug -r tip
309 $ hg log --debug -r tip
310 changeset: 13:9db0f28fd3747e92c57d015f53b5593aeec53c2d
310 changeset: 13:9db0f28fd3747e92c57d015f53b5593aeec53c2d
311 tag: tip
311 tag: tip
312 phase: draft
312 phase: draft
313 parent: 12:b592ea63bb0c19a6c5c44685ee29a2284f9f1b8f
313 parent: 12:b592ea63bb0c19a6c5c44685ee29a2284f9f1b8f
314 parent: -1:0000000000000000000000000000000000000000
314 parent: -1:0000000000000000000000000000000000000000
315 manifest: 13:dc313617b8c32457c0d589e0dbbedfe71f3cd637
315 manifest: 13:dc313617b8c32457c0d589e0dbbedfe71f3cd637
316 user: foo
316 user: foo
317 date: Thu Jan 01 00:00:00 1970 +0000
317 date: Thu Jan 01 00:00:00 1970 +0000
318 files+: b
318 files+: b
319 files-: a
319 files-: a
320 extra: branch=default
320 extra: branch=default
321 extra: source=5c095ad7e90f871700f02dd1fa5012cb4498a2d4
321 extra: source=5c095ad7e90f871700f02dd1fa5012cb4498a2d4
322 description:
322 description:
323 2
323 2
324
324
325
325
326 Disallow grafting an already grafted cset onto its original branch
326 Disallow grafting an already grafted cset onto its original branch
327 $ hg up -q 6
327 $ hg up -q 6
328 $ hg graft 7
328 $ hg graft 7
329 skipping already grafted revision 7 (was grafted from 2)
329 skipping already grafted revision 7 (was grafted from 2)
330 [255]
330 [255]
331
331
332 Disallow grafting already grafted csets with the same origin onto each other
332 Disallow grafting already grafted csets with the same origin onto each other
333 $ hg up -q 13
333 $ hg up -q 13
334 $ hg graft 2
334 $ hg graft 2
335 skipping revision 2 (already grafted to 13)
335 skipping revision 2 (already grafted to 13)
336 [255]
336 [255]
337 $ hg graft 7
337 $ hg graft 7
338 skipping already grafted revision 7 (13 also has origin 2)
338 skipping already grafted revision 7 (13 also has origin 2)
339 [255]
339 [255]
340
340
341 $ hg up -q 7
341 $ hg up -q 7
342 $ hg graft 2
342 $ hg graft 2
343 skipping revision 2 (already grafted to 7)
343 skipping revision 2 (already grafted to 7)
344 [255]
344 [255]
345 $ hg graft tip
345 $ hg graft tip
346 skipping already grafted revision 13 (7 also has origin 2)
346 skipping already grafted revision 13 (7 also has origin 2)
347 [255]
347 [255]
348
348
349 Graft with --log
349 Graft with --log
350
350
351 $ hg up -Cq 1
351 $ hg up -Cq 1
352 $ hg graft 3 --log -u foo
352 $ hg graft 3 --log -u foo
353 grafting revision 3
353 grafting revision 3
354 warning: can't find ancestor for 'c' copied from 'b'!
354 warning: can't find ancestor for 'c' copied from 'b'!
355 $ hg log --template '{rev} {parents} {desc}\n' -r tip
355 $ hg log --template '{rev} {parents} {desc}\n' -r tip
356 14 1:5d205f8b35b6 3
356 14 1:5d205f8b35b6 3
357 (grafted from 4c60f11aa304a54ae1c199feb94e7fc771e51ed8)
357 (grafted from 4c60f11aa304a54ae1c199feb94e7fc771e51ed8)
358
358
359 Resolve conflicted graft
359 Resolve conflicted graft
360 $ hg up -q 0
360 $ hg up -q 0
361 $ echo b > a
361 $ echo b > a
362 $ hg ci -m 8
362 $ hg ci -m 8
363 created new head
363 created new head
364 $ echo a > a
364 $ echo a > a
365 $ hg ci -m 9
365 $ hg ci -m 9
366 $ hg graft 1 --tool internal:fail
366 $ hg graft 1 --tool internal:fail
367 grafting revision 1
367 grafting revision 1
368 abort: unresolved conflicts, can't continue
368 abort: unresolved conflicts, can't continue
369 (use hg resolve and hg graft --continue)
369 (use hg resolve and hg graft --continue)
370 [255]
370 [255]
371 $ hg resolve --all
371 $ hg resolve --all
372 merging a
372 merging a
373 (no more unresolved files)
373 (no more unresolved files)
374 $ hg graft -c
374 $ hg graft -c
375 grafting revision 1
375 grafting revision 1
376 $ hg export tip --git
376 $ hg export tip --git
377 # HG changeset patch
377 # HG changeset patch
378 # User bar
378 # User bar
379 # Date 0 0
379 # Date 0 0
380 # Thu Jan 01 00:00:00 1970 +0000
380 # Thu Jan 01 00:00:00 1970 +0000
381 # Node ID 64ecd9071ce83c6e62f538d8ce7709d53f32ebf7
381 # Node ID 64ecd9071ce83c6e62f538d8ce7709d53f32ebf7
382 # Parent 4bdb9a9d0b84ffee1d30f0dfc7744cade17aa19c
382 # Parent 4bdb9a9d0b84ffee1d30f0dfc7744cade17aa19c
383 1
383 1
384
384
385 diff --git a/a b/a
385 diff --git a/a b/a
386 --- a/a
386 --- a/a
387 +++ b/a
387 +++ b/a
388 @@ -1,1 +1,1 @@
388 @@ -1,1 +1,1 @@
389 -a
389 -a
390 +b
390 +b
391
391
392 Resolve conflicted graft with rename
392 Resolve conflicted graft with rename
393 $ echo c > a
393 $ echo c > a
394 $ hg ci -m 10
394 $ hg ci -m 10
395 $ hg graft 2 --tool internal:fail
395 $ hg graft 2 --tool internal:fail
396 grafting revision 2
396 grafting revision 2
397 abort: unresolved conflicts, can't continue
397 abort: unresolved conflicts, can't continue
398 (use hg resolve and hg graft --continue)
398 (use hg resolve and hg graft --continue)
399 [255]
399 [255]
400 $ hg resolve --all
400 $ hg resolve --all
401 merging a and b to b
401 merging a and b to b
402 (no more unresolved files)
402 (no more unresolved files)
403 $ hg graft -c
403 $ hg graft -c
404 grafting revision 2
404 grafting revision 2
405 $ hg export tip --git
405 $ hg export tip --git
406 # HG changeset patch
406 # HG changeset patch
407 # User test
407 # User test
408 # Date 0 0
408 # Date 0 0
409 # Thu Jan 01 00:00:00 1970 +0000
409 # Thu Jan 01 00:00:00 1970 +0000
410 # Node ID 2e80e1351d6ed50302fe1e05f8bd1d4d412b6e11
410 # Node ID 2e80e1351d6ed50302fe1e05f8bd1d4d412b6e11
411 # Parent e5a51ae854a8bbaaf25cc5c6a57ff46042dadbb4
411 # Parent e5a51ae854a8bbaaf25cc5c6a57ff46042dadbb4
412 2
412 2
413
413
414 diff --git a/a b/b
414 diff --git a/a b/b
415 rename from a
415 rename from a
416 rename to b
416 rename to b
417
417
418 Test simple origin(), with and without args
418 Test simple origin(), with and without args
419 $ hg log -r 'origin()'
419 $ hg log -r 'origin()'
420 changeset: 1:5d205f8b35b6
420 changeset: 1:5d205f8b35b6
421 user: bar
421 user: bar
422 date: Thu Jan 01 00:00:00 1970 +0000
422 date: Thu Jan 01 00:00:00 1970 +0000
423 summary: 1
423 summary: 1
424
424
425 changeset: 2:5c095ad7e90f
425 changeset: 2:5c095ad7e90f
426 user: test
426 user: test
427 date: Thu Jan 01 00:00:00 1970 +0000
427 date: Thu Jan 01 00:00:00 1970 +0000
428 summary: 2
428 summary: 2
429
429
430 changeset: 3:4c60f11aa304
430 changeset: 3:4c60f11aa304
431 user: baz
431 user: baz
432 date: Thu Jan 01 00:00:00 1970 +0000
432 date: Thu Jan 01 00:00:00 1970 +0000
433 summary: 3
433 summary: 3
434
434
435 changeset: 4:9c233e8e184d
435 changeset: 4:9c233e8e184d
436 user: test
436 user: test
437 date: Thu Jan 01 00:00:00 1970 +0000
437 date: Thu Jan 01 00:00:00 1970 +0000
438 summary: 4
438 summary: 4
439
439
440 changeset: 5:97f8bfe72746
440 changeset: 5:97f8bfe72746
441 branch: stable
441 branch: stable
442 parent: 3:4c60f11aa304
442 parent: 3:4c60f11aa304
443 user: test
443 user: test
444 date: Thu Jan 01 00:00:00 1970 +0000
444 date: Thu Jan 01 00:00:00 1970 +0000
445 summary: 5
445 summary: 5
446
446
447 $ hg log -r 'origin(7)'
447 $ hg log -r 'origin(7)'
448 changeset: 2:5c095ad7e90f
448 changeset: 2:5c095ad7e90f
449 user: test
449 user: test
450 date: Thu Jan 01 00:00:00 1970 +0000
450 date: Thu Jan 01 00:00:00 1970 +0000
451 summary: 2
451 summary: 2
452
452
453 Now transplant a graft to test following through copies
453 Now transplant a graft to test following through copies
454 $ hg up -q 0
454 $ hg up -q 0
455 $ hg branch -q dev
455 $ hg branch -q dev
456 $ hg ci -qm "dev branch"
456 $ hg ci -qm "dev branch"
457 $ hg --config extensions.transplant= transplant -q 7
457 $ hg --config extensions.transplant= transplant -q 7
458 $ hg log -r 'origin(.)'
458 $ hg log -r 'origin(.)'
459 changeset: 2:5c095ad7e90f
459 changeset: 2:5c095ad7e90f
460 user: test
460 user: test
461 date: Thu Jan 01 00:00:00 1970 +0000
461 date: Thu Jan 01 00:00:00 1970 +0000
462 summary: 2
462 summary: 2
463
463
464 Test that the graft and transplant markers in extra are converted, allowing
464 Test that the graft and transplant markers in extra are converted, allowing
465 origin() to still work. Note that these recheck the immediately preceeding two
465 origin() to still work. Note that these recheck the immediately preceeding two
466 tests.
466 tests.
467 $ hg --quiet --config extensions.convert= --config convert.hg.saverev=True convert . ../converted
467 $ hg --quiet --config extensions.convert= --config convert.hg.saverev=True convert . ../converted
468
468
469 The graft case
469 The graft case
470 $ hg -R ../converted log -r 7 --template "{rev}: {node}\n{join(extras, '\n')}\n"
470 $ hg -R ../converted log -r 7 --template "{rev}: {node}\n{join(extras, '\n')}\n"
471 7: 7ae846e9111fc8f57745634250c7b9ac0a60689b
471 7: 7ae846e9111fc8f57745634250c7b9ac0a60689b
472 branch=default
472 branch=default
473 convert_revision=ef0ef43d49e79e81ddafdc7997401ba0041efc82
473 convert_revision=ef0ef43d49e79e81ddafdc7997401ba0041efc82
474 source=e0213322b2c1a5d5d236c74e79666441bee67a7d
474 source=e0213322b2c1a5d5d236c74e79666441bee67a7d
475 $ hg -R ../converted log -r 'origin(7)'
475 $ hg -R ../converted log -r 'origin(7)'
476 changeset: 2:e0213322b2c1
476 changeset: 2:e0213322b2c1
477 user: test
477 user: test
478 date: Thu Jan 01 00:00:00 1970 +0000
478 date: Thu Jan 01 00:00:00 1970 +0000
479 summary: 2
479 summary: 2
480
480
481 Test that template correctly expands more than one 'extra' (issue4362)
482 $ hg -R ../converted log -r 7 --template "{extras % ' Extra: {extra}\n'}"
483 Extra: branch=default
484 Extra: convert_revision=ef0ef43d49e79e81ddafdc7997401ba0041efc82
485 Extra: source=e0213322b2c1a5d5d236c74e79666441bee67a7d
486
481 The transplant case
487 The transplant case
482 $ hg -R ../converted log -r tip --template "{rev}: {node}\n{join(extras, '\n')}\n"
488 $ hg -R ../converted log -r tip --template "{rev}: {node}\n{join(extras, '\n')}\n"
483 21: fbb6c5cc81002f2b4b49c9d731404688bcae5ade
489 21: fbb6c5cc81002f2b4b49c9d731404688bcae5ade
484 branch=dev
490 branch=dev
485 convert_revision=7e61b508e709a11d28194a5359bc3532d910af21
491 convert_revision=7e61b508e709a11d28194a5359bc3532d910af21
486 transplant_source=z\xe8F\xe9\x11\x1f\xc8\xf5wEcBP\xc7\xb9\xac (esc)
492 transplant_source=z\xe8F\xe9\x11\x1f\xc8\xf5wEcBP\xc7\xb9\xac (esc)
487 `h\x9b (esc)
493 `h\x9b (esc)
488 $ hg -R ../converted log -r 'origin(tip)'
494 $ hg -R ../converted log -r 'origin(tip)'
489 changeset: 2:e0213322b2c1
495 changeset: 2:e0213322b2c1
490 user: test
496 user: test
491 date: Thu Jan 01 00:00:00 1970 +0000
497 date: Thu Jan 01 00:00:00 1970 +0000
492 summary: 2
498 summary: 2
493
499
494
500
495 Test simple destination
501 Test simple destination
496 $ hg log -r 'destination()'
502 $ hg log -r 'destination()'
497 changeset: 7:ef0ef43d49e7
503 changeset: 7:ef0ef43d49e7
498 parent: 0:68795b066622
504 parent: 0:68795b066622
499 user: foo
505 user: foo
500 date: Thu Jan 01 00:00:00 1970 +0000
506 date: Thu Jan 01 00:00:00 1970 +0000
501 summary: 2
507 summary: 2
502
508
503 changeset: 8:6b9e5368ca4e
509 changeset: 8:6b9e5368ca4e
504 user: bar
510 user: bar
505 date: Thu Jan 01 00:00:00 1970 +0000
511 date: Thu Jan 01 00:00:00 1970 +0000
506 summary: 1
512 summary: 1
507
513
508 changeset: 9:1905859650ec
514 changeset: 9:1905859650ec
509 user: test
515 user: test
510 date: Thu Jan 01 00:00:00 1970 +0000
516 date: Thu Jan 01 00:00:00 1970 +0000
511 summary: 5
517 summary: 5
512
518
513 changeset: 10:52dc0b4c6907
519 changeset: 10:52dc0b4c6907
514 user: test
520 user: test
515 date: Thu Jan 01 00:00:00 1970 +0000
521 date: Thu Jan 01 00:00:00 1970 +0000
516 summary: 4
522 summary: 4
517
523
518 changeset: 11:882b35362a6b
524 changeset: 11:882b35362a6b
519 user: test
525 user: test
520 date: Thu Jan 01 00:00:00 1970 +0000
526 date: Thu Jan 01 00:00:00 1970 +0000
521 summary: 3
527 summary: 3
522
528
523 changeset: 13:9db0f28fd374
529 changeset: 13:9db0f28fd374
524 user: foo
530 user: foo
525 date: Thu Jan 01 00:00:00 1970 +0000
531 date: Thu Jan 01 00:00:00 1970 +0000
526 summary: 2
532 summary: 2
527
533
528 changeset: 14:f64defefacee
534 changeset: 14:f64defefacee
529 parent: 1:5d205f8b35b6
535 parent: 1:5d205f8b35b6
530 user: foo
536 user: foo
531 date: Thu Jan 01 00:00:00 1970 +0000
537 date: Thu Jan 01 00:00:00 1970 +0000
532 summary: 3
538 summary: 3
533
539
534 changeset: 17:64ecd9071ce8
540 changeset: 17:64ecd9071ce8
535 user: bar
541 user: bar
536 date: Thu Jan 01 00:00:00 1970 +0000
542 date: Thu Jan 01 00:00:00 1970 +0000
537 summary: 1
543 summary: 1
538
544
539 changeset: 19:2e80e1351d6e
545 changeset: 19:2e80e1351d6e
540 user: test
546 user: test
541 date: Thu Jan 01 00:00:00 1970 +0000
547 date: Thu Jan 01 00:00:00 1970 +0000
542 summary: 2
548 summary: 2
543
549
544 changeset: 21:7e61b508e709
550 changeset: 21:7e61b508e709
545 branch: dev
551 branch: dev
546 tag: tip
552 tag: tip
547 user: foo
553 user: foo
548 date: Thu Jan 01 00:00:00 1970 +0000
554 date: Thu Jan 01 00:00:00 1970 +0000
549 summary: 2
555 summary: 2
550
556
551 $ hg log -r 'destination(2)'
557 $ hg log -r 'destination(2)'
552 changeset: 7:ef0ef43d49e7
558 changeset: 7:ef0ef43d49e7
553 parent: 0:68795b066622
559 parent: 0:68795b066622
554 user: foo
560 user: foo
555 date: Thu Jan 01 00:00:00 1970 +0000
561 date: Thu Jan 01 00:00:00 1970 +0000
556 summary: 2
562 summary: 2
557
563
558 changeset: 13:9db0f28fd374
564 changeset: 13:9db0f28fd374
559 user: foo
565 user: foo
560 date: Thu Jan 01 00:00:00 1970 +0000
566 date: Thu Jan 01 00:00:00 1970 +0000
561 summary: 2
567 summary: 2
562
568
563 changeset: 19:2e80e1351d6e
569 changeset: 19:2e80e1351d6e
564 user: test
570 user: test
565 date: Thu Jan 01 00:00:00 1970 +0000
571 date: Thu Jan 01 00:00:00 1970 +0000
566 summary: 2
572 summary: 2
567
573
568 changeset: 21:7e61b508e709
574 changeset: 21:7e61b508e709
569 branch: dev
575 branch: dev
570 tag: tip
576 tag: tip
571 user: foo
577 user: foo
572 date: Thu Jan 01 00:00:00 1970 +0000
578 date: Thu Jan 01 00:00:00 1970 +0000
573 summary: 2
579 summary: 2
574
580
575 Transplants of grafts can find a destination...
581 Transplants of grafts can find a destination...
576 $ hg log -r 'destination(7)'
582 $ hg log -r 'destination(7)'
577 changeset: 21:7e61b508e709
583 changeset: 21:7e61b508e709
578 branch: dev
584 branch: dev
579 tag: tip
585 tag: tip
580 user: foo
586 user: foo
581 date: Thu Jan 01 00:00:00 1970 +0000
587 date: Thu Jan 01 00:00:00 1970 +0000
582 summary: 2
588 summary: 2
583
589
584 ... grafts of grafts unfortunately can't
590 ... grafts of grafts unfortunately can't
585 $ hg graft -q 13
591 $ hg graft -q 13
586 $ hg log -r 'destination(13)'
592 $ hg log -r 'destination(13)'
587 All copies of a cset
593 All copies of a cset
588 $ hg log -r 'origin(13) or destination(origin(13))'
594 $ hg log -r 'origin(13) or destination(origin(13))'
589 changeset: 2:5c095ad7e90f
595 changeset: 2:5c095ad7e90f
590 user: test
596 user: test
591 date: Thu Jan 01 00:00:00 1970 +0000
597 date: Thu Jan 01 00:00:00 1970 +0000
592 summary: 2
598 summary: 2
593
599
594 changeset: 7:ef0ef43d49e7
600 changeset: 7:ef0ef43d49e7
595 parent: 0:68795b066622
601 parent: 0:68795b066622
596 user: foo
602 user: foo
597 date: Thu Jan 01 00:00:00 1970 +0000
603 date: Thu Jan 01 00:00:00 1970 +0000
598 summary: 2
604 summary: 2
599
605
600 changeset: 13:9db0f28fd374
606 changeset: 13:9db0f28fd374
601 user: foo
607 user: foo
602 date: Thu Jan 01 00:00:00 1970 +0000
608 date: Thu Jan 01 00:00:00 1970 +0000
603 summary: 2
609 summary: 2
604
610
605 changeset: 19:2e80e1351d6e
611 changeset: 19:2e80e1351d6e
606 user: test
612 user: test
607 date: Thu Jan 01 00:00:00 1970 +0000
613 date: Thu Jan 01 00:00:00 1970 +0000
608 summary: 2
614 summary: 2
609
615
610 changeset: 21:7e61b508e709
616 changeset: 21:7e61b508e709
611 branch: dev
617 branch: dev
612 user: foo
618 user: foo
613 date: Thu Jan 01 00:00:00 1970 +0000
619 date: Thu Jan 01 00:00:00 1970 +0000
614 summary: 2
620 summary: 2
615
621
616 changeset: 22:1313d0a825e2
622 changeset: 22:1313d0a825e2
617 branch: dev
623 branch: dev
618 tag: tip
624 tag: tip
619 user: foo
625 user: foo
620 date: Thu Jan 01 00:00:00 1970 +0000
626 date: Thu Jan 01 00:00:00 1970 +0000
621 summary: 2
627 summary: 2
622
628
623
629
624 graft works on complex revset
630 graft works on complex revset
625
631
626 $ hg graft 'origin(13) or destination(origin(13))'
632 $ hg graft 'origin(13) or destination(origin(13))'
627 skipping ancestor revision 21
633 skipping ancestor revision 21
628 skipping ancestor revision 22
634 skipping ancestor revision 22
629 skipping revision 2 (already grafted to 22)
635 skipping revision 2 (already grafted to 22)
630 grafting revision 7
636 grafting revision 7
631 grafting revision 13
637 grafting revision 13
632 grafting revision 19
638 grafting revision 19
633 merging b
639 merging b
634
640
635 graft with --force (still doesn't graft merges)
641 graft with --force (still doesn't graft merges)
636
642
637 $ hg graft 19 0 6
643 $ hg graft 19 0 6
638 skipping ungraftable merge revision 6
644 skipping ungraftable merge revision 6
639 skipping ancestor revision 0
645 skipping ancestor revision 0
640 skipping already grafted revision 19 (22 also has origin 2)
646 skipping already grafted revision 19 (22 also has origin 2)
641 [255]
647 [255]
642 $ hg graft 19 0 6 --force
648 $ hg graft 19 0 6 --force
643 skipping ungraftable merge revision 6
649 skipping ungraftable merge revision 6
644 grafting revision 19
650 grafting revision 19
645 merging b
651 merging b
646 grafting revision 0
652 grafting revision 0
647
653
648 graft --force after backout
654 graft --force after backout
649
655
650 $ echo abc > a
656 $ echo abc > a
651 $ hg ci -m 28
657 $ hg ci -m 28
652 $ hg backout 28
658 $ hg backout 28
653 reverting a
659 reverting a
654 changeset 29:484c03b8dfa4 backs out changeset 28:6c56f0f7f033
660 changeset 29:484c03b8dfa4 backs out changeset 28:6c56f0f7f033
655 $ hg graft 28
661 $ hg graft 28
656 skipping ancestor revision 28
662 skipping ancestor revision 28
657 [255]
663 [255]
658 $ hg graft 28 --force
664 $ hg graft 28 --force
659 grafting revision 28
665 grafting revision 28
660 merging a
666 merging a
661 $ cat a
667 $ cat a
662 abc
668 abc
663
669
664 graft --continue after --force
670 graft --continue after --force
665
671
666 $ hg backout 30
672 $ hg backout 30
667 reverting a
673 reverting a
668 changeset 31:3b96c18b7a1b backs out changeset 30:8f539994be33
674 changeset 31:3b96c18b7a1b backs out changeset 30:8f539994be33
669 $ hg graft 28 --force --tool internal:fail
675 $ hg graft 28 --force --tool internal:fail
670 grafting revision 28
676 grafting revision 28
671 abort: unresolved conflicts, can't continue
677 abort: unresolved conflicts, can't continue
672 (use hg resolve and hg graft --continue)
678 (use hg resolve and hg graft --continue)
673 [255]
679 [255]
674 $ hg resolve --all
680 $ hg resolve --all
675 merging a
681 merging a
676 (no more unresolved files)
682 (no more unresolved files)
677 $ hg graft -c
683 $ hg graft -c
678 grafting revision 28
684 grafting revision 28
679 $ cat a
685 $ cat a
680 abc
686 abc
681
687
682 Continue testing same origin policy, using revision numbers from test above
688 Continue testing same origin policy, using revision numbers from test above
683 but do some destructive editing of the repo:
689 but do some destructive editing of the repo:
684
690
685 $ hg up -qC 7
691 $ hg up -qC 7
686 $ hg tag -l -r 13 tmp
692 $ hg tag -l -r 13 tmp
687 $ hg --config extensions.mq= strip 2
693 $ hg --config extensions.mq= strip 2
688 saved backup bundle to $TESTTMP/a/.hg/strip-backup/5c095ad7e90f-backup.hg (glob)
694 saved backup bundle to $TESTTMP/a/.hg/strip-backup/5c095ad7e90f-backup.hg (glob)
689 $ hg graft tmp
695 $ hg graft tmp
690 skipping already grafted revision 8 (2 also has unknown origin 5c095ad7e90f871700f02dd1fa5012cb4498a2d4)
696 skipping already grafted revision 8 (2 also has unknown origin 5c095ad7e90f871700f02dd1fa5012cb4498a2d4)
691 [255]
697 [255]
General Comments 0
You need to be logged in to leave comments. Login now