##// END OF EJS Templates
import: avoid editor invocation when importing with "--exact" for exact-ness...
FUJIWARA Katsunori -
r22278:ffaaa80f default
parent child Browse files
Show More
@@ -1,2719 +1,2725 b''
1 # cmdutil.py - help for command processing in mercurial
1 # cmdutil.py - help for command processing in mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 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 node import hex, nullid, nullrev, short
8 from node import hex, nullid, nullrev, short
9 from i18n import _
9 from i18n import _
10 import os, sys, errno, re, tempfile
10 import os, sys, errno, re, tempfile
11 import util, scmutil, templater, patch, error, templatekw, revlog, copies
11 import util, scmutil, templater, patch, error, templatekw, revlog, copies
12 import match as matchmod
12 import match as matchmod
13 import context, repair, graphmod, revset, phases, obsolete, pathutil
13 import context, repair, graphmod, revset, phases, obsolete, pathutil
14 import changelog
14 import changelog
15 import bookmarks
15 import bookmarks
16 import lock as lockmod
16 import lock as lockmod
17
17
18 def parsealiases(cmd):
18 def parsealiases(cmd):
19 return cmd.lstrip("^").split("|")
19 return cmd.lstrip("^").split("|")
20
20
21 def findpossible(cmd, table, strict=False):
21 def findpossible(cmd, table, strict=False):
22 """
22 """
23 Return cmd -> (aliases, command table entry)
23 Return cmd -> (aliases, command table entry)
24 for each matching command.
24 for each matching command.
25 Return debug commands (or their aliases) only if no normal command matches.
25 Return debug commands (or their aliases) only if no normal command matches.
26 """
26 """
27 choice = {}
27 choice = {}
28 debugchoice = {}
28 debugchoice = {}
29
29
30 if cmd in table:
30 if cmd in table:
31 # short-circuit exact matches, "log" alias beats "^log|history"
31 # short-circuit exact matches, "log" alias beats "^log|history"
32 keys = [cmd]
32 keys = [cmd]
33 else:
33 else:
34 keys = table.keys()
34 keys = table.keys()
35
35
36 for e in keys:
36 for e in keys:
37 aliases = parsealiases(e)
37 aliases = parsealiases(e)
38 found = None
38 found = None
39 if cmd in aliases:
39 if cmd in aliases:
40 found = cmd
40 found = cmd
41 elif not strict:
41 elif not strict:
42 for a in aliases:
42 for a in aliases:
43 if a.startswith(cmd):
43 if a.startswith(cmd):
44 found = a
44 found = a
45 break
45 break
46 if found is not None:
46 if found is not None:
47 if aliases[0].startswith("debug") or found.startswith("debug"):
47 if aliases[0].startswith("debug") or found.startswith("debug"):
48 debugchoice[found] = (aliases, table[e])
48 debugchoice[found] = (aliases, table[e])
49 else:
49 else:
50 choice[found] = (aliases, table[e])
50 choice[found] = (aliases, table[e])
51
51
52 if not choice and debugchoice:
52 if not choice and debugchoice:
53 choice = debugchoice
53 choice = debugchoice
54
54
55 return choice
55 return choice
56
56
57 def findcmd(cmd, table, strict=True):
57 def findcmd(cmd, table, strict=True):
58 """Return (aliases, command table entry) for command string."""
58 """Return (aliases, command table entry) for command string."""
59 choice = findpossible(cmd, table, strict)
59 choice = findpossible(cmd, table, strict)
60
60
61 if cmd in choice:
61 if cmd in choice:
62 return choice[cmd]
62 return choice[cmd]
63
63
64 if len(choice) > 1:
64 if len(choice) > 1:
65 clist = choice.keys()
65 clist = choice.keys()
66 clist.sort()
66 clist.sort()
67 raise error.AmbiguousCommand(cmd, clist)
67 raise error.AmbiguousCommand(cmd, clist)
68
68
69 if choice:
69 if choice:
70 return choice.values()[0]
70 return choice.values()[0]
71
71
72 raise error.UnknownCommand(cmd)
72 raise error.UnknownCommand(cmd)
73
73
74 def findrepo(p):
74 def findrepo(p):
75 while not os.path.isdir(os.path.join(p, ".hg")):
75 while not os.path.isdir(os.path.join(p, ".hg")):
76 oldp, p = p, os.path.dirname(p)
76 oldp, p = p, os.path.dirname(p)
77 if p == oldp:
77 if p == oldp:
78 return None
78 return None
79
79
80 return p
80 return p
81
81
82 def bailifchanged(repo):
82 def bailifchanged(repo):
83 if repo.dirstate.p2() != nullid:
83 if repo.dirstate.p2() != nullid:
84 raise util.Abort(_('outstanding uncommitted merge'))
84 raise util.Abort(_('outstanding uncommitted merge'))
85 modified, added, removed, deleted = repo.status()[:4]
85 modified, added, removed, deleted = repo.status()[:4]
86 if modified or added or removed or deleted:
86 if modified or added or removed or deleted:
87 raise util.Abort(_('uncommitted changes'))
87 raise util.Abort(_('uncommitted changes'))
88 ctx = repo[None]
88 ctx = repo[None]
89 for s in sorted(ctx.substate):
89 for s in sorted(ctx.substate):
90 if ctx.sub(s).dirty():
90 if ctx.sub(s).dirty():
91 raise util.Abort(_("uncommitted changes in subrepo %s") % s)
91 raise util.Abort(_("uncommitted changes in subrepo %s") % s)
92
92
93 def logmessage(ui, opts):
93 def logmessage(ui, opts):
94 """ get the log message according to -m and -l option """
94 """ get the log message according to -m and -l option """
95 message = opts.get('message')
95 message = opts.get('message')
96 logfile = opts.get('logfile')
96 logfile = opts.get('logfile')
97
97
98 if message and logfile:
98 if message and logfile:
99 raise util.Abort(_('options --message and --logfile are mutually '
99 raise util.Abort(_('options --message and --logfile are mutually '
100 'exclusive'))
100 'exclusive'))
101 if not message and logfile:
101 if not message and logfile:
102 try:
102 try:
103 if logfile == '-':
103 if logfile == '-':
104 message = ui.fin.read()
104 message = ui.fin.read()
105 else:
105 else:
106 message = '\n'.join(util.readfile(logfile).splitlines())
106 message = '\n'.join(util.readfile(logfile).splitlines())
107 except IOError, inst:
107 except IOError, inst:
108 raise util.Abort(_("can't read commit message '%s': %s") %
108 raise util.Abort(_("can't read commit message '%s': %s") %
109 (logfile, inst.strerror))
109 (logfile, inst.strerror))
110 return message
110 return message
111
111
112 def mergeeditform(ctxorbool, baseform):
112 def mergeeditform(ctxorbool, baseform):
113 """build appropriate editform from ctxorbool and baseform
113 """build appropriate editform from ctxorbool and baseform
114
114
115 'cxtorbool' is one of a ctx to be committed, or a bool whether
115 'cxtorbool' is one of a ctx to be committed, or a bool whether
116 merging is committed.
116 merging is committed.
117
117
118 This returns editform 'baseform' with '.merge' if merging is
118 This returns editform 'baseform' with '.merge' if merging is
119 committed, or one with '.normal' suffix otherwise.
119 committed, or one with '.normal' suffix otherwise.
120 """
120 """
121 if isinstance(ctxorbool, bool):
121 if isinstance(ctxorbool, bool):
122 if ctxorbool:
122 if ctxorbool:
123 return baseform + ".merge"
123 return baseform + ".merge"
124 elif 1 < len(ctxorbool.parents()):
124 elif 1 < len(ctxorbool.parents()):
125 return baseform + ".merge"
125 return baseform + ".merge"
126
126
127 return baseform + ".normal"
127 return baseform + ".normal"
128
128
129 def getcommiteditor(edit=False, finishdesc=None, extramsg=None,
129 def getcommiteditor(edit=False, finishdesc=None, extramsg=None,
130 editform='', **opts):
130 editform='', **opts):
131 """get appropriate commit message editor according to '--edit' option
131 """get appropriate commit message editor according to '--edit' option
132
132
133 'finishdesc' is a function to be called with edited commit message
133 'finishdesc' is a function to be called with edited commit message
134 (= 'description' of the new changeset) just after editing, but
134 (= 'description' of the new changeset) just after editing, but
135 before checking empty-ness. It should return actual text to be
135 before checking empty-ness. It should return actual text to be
136 stored into history. This allows to change description before
136 stored into history. This allows to change description before
137 storing.
137 storing.
138
138
139 'extramsg' is a extra message to be shown in the editor instead of
139 'extramsg' is a extra message to be shown in the editor instead of
140 'Leave message empty to abort commit' line. 'HG: ' prefix and EOL
140 'Leave message empty to abort commit' line. 'HG: ' prefix and EOL
141 is automatically added.
141 is automatically added.
142
142
143 'editform' is a dot-separated list of names, to distinguish
143 'editform' is a dot-separated list of names, to distinguish
144 the purpose of commit text editing.
144 the purpose of commit text editing.
145
145
146 'getcommiteditor' returns 'commitforceeditor' regardless of
146 'getcommiteditor' returns 'commitforceeditor' regardless of
147 'edit', if one of 'finishdesc' or 'extramsg' is specified, because
147 'edit', if one of 'finishdesc' or 'extramsg' is specified, because
148 they are specific for usage in MQ.
148 they are specific for usage in MQ.
149 """
149 """
150 if edit or finishdesc or extramsg:
150 if edit or finishdesc or extramsg:
151 return lambda r, c, s: commitforceeditor(r, c, s,
151 return lambda r, c, s: commitforceeditor(r, c, s,
152 finishdesc=finishdesc,
152 finishdesc=finishdesc,
153 extramsg=extramsg,
153 extramsg=extramsg,
154 editform=editform)
154 editform=editform)
155 elif editform:
155 elif editform:
156 return lambda r, c, s: commiteditor(r, c, s, editform=editform)
156 return lambda r, c, s: commiteditor(r, c, s, editform=editform)
157 else:
157 else:
158 return commiteditor
158 return commiteditor
159
159
160 def loglimit(opts):
160 def loglimit(opts):
161 """get the log limit according to option -l/--limit"""
161 """get the log limit according to option -l/--limit"""
162 limit = opts.get('limit')
162 limit = opts.get('limit')
163 if limit:
163 if limit:
164 try:
164 try:
165 limit = int(limit)
165 limit = int(limit)
166 except ValueError:
166 except ValueError:
167 raise util.Abort(_('limit must be a positive integer'))
167 raise util.Abort(_('limit must be a positive integer'))
168 if limit <= 0:
168 if limit <= 0:
169 raise util.Abort(_('limit must be positive'))
169 raise util.Abort(_('limit must be positive'))
170 else:
170 else:
171 limit = None
171 limit = None
172 return limit
172 return limit
173
173
174 def makefilename(repo, pat, node, desc=None,
174 def makefilename(repo, pat, node, desc=None,
175 total=None, seqno=None, revwidth=None, pathname=None):
175 total=None, seqno=None, revwidth=None, pathname=None):
176 node_expander = {
176 node_expander = {
177 'H': lambda: hex(node),
177 'H': lambda: hex(node),
178 'R': lambda: str(repo.changelog.rev(node)),
178 'R': lambda: str(repo.changelog.rev(node)),
179 'h': lambda: short(node),
179 'h': lambda: short(node),
180 'm': lambda: re.sub('[^\w]', '_', str(desc))
180 'm': lambda: re.sub('[^\w]', '_', str(desc))
181 }
181 }
182 expander = {
182 expander = {
183 '%': lambda: '%',
183 '%': lambda: '%',
184 'b': lambda: os.path.basename(repo.root),
184 'b': lambda: os.path.basename(repo.root),
185 }
185 }
186
186
187 try:
187 try:
188 if node:
188 if node:
189 expander.update(node_expander)
189 expander.update(node_expander)
190 if node:
190 if node:
191 expander['r'] = (lambda:
191 expander['r'] = (lambda:
192 str(repo.changelog.rev(node)).zfill(revwidth or 0))
192 str(repo.changelog.rev(node)).zfill(revwidth or 0))
193 if total is not None:
193 if total is not None:
194 expander['N'] = lambda: str(total)
194 expander['N'] = lambda: str(total)
195 if seqno is not None:
195 if seqno is not None:
196 expander['n'] = lambda: str(seqno)
196 expander['n'] = lambda: str(seqno)
197 if total is not None and seqno is not None:
197 if total is not None and seqno is not None:
198 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
198 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
199 if pathname is not None:
199 if pathname is not None:
200 expander['s'] = lambda: os.path.basename(pathname)
200 expander['s'] = lambda: os.path.basename(pathname)
201 expander['d'] = lambda: os.path.dirname(pathname) or '.'
201 expander['d'] = lambda: os.path.dirname(pathname) or '.'
202 expander['p'] = lambda: pathname
202 expander['p'] = lambda: pathname
203
203
204 newname = []
204 newname = []
205 patlen = len(pat)
205 patlen = len(pat)
206 i = 0
206 i = 0
207 while i < patlen:
207 while i < patlen:
208 c = pat[i]
208 c = pat[i]
209 if c == '%':
209 if c == '%':
210 i += 1
210 i += 1
211 c = pat[i]
211 c = pat[i]
212 c = expander[c]()
212 c = expander[c]()
213 newname.append(c)
213 newname.append(c)
214 i += 1
214 i += 1
215 return ''.join(newname)
215 return ''.join(newname)
216 except KeyError, inst:
216 except KeyError, inst:
217 raise util.Abort(_("invalid format spec '%%%s' in output filename") %
217 raise util.Abort(_("invalid format spec '%%%s' in output filename") %
218 inst.args[0])
218 inst.args[0])
219
219
220 def makefileobj(repo, pat, node=None, desc=None, total=None,
220 def makefileobj(repo, pat, node=None, desc=None, total=None,
221 seqno=None, revwidth=None, mode='wb', modemap=None,
221 seqno=None, revwidth=None, mode='wb', modemap=None,
222 pathname=None):
222 pathname=None):
223
223
224 writable = mode not in ('r', 'rb')
224 writable = mode not in ('r', 'rb')
225
225
226 if not pat or pat == '-':
226 if not pat or pat == '-':
227 fp = writable and repo.ui.fout or repo.ui.fin
227 fp = writable and repo.ui.fout or repo.ui.fin
228 if util.safehasattr(fp, 'fileno'):
228 if util.safehasattr(fp, 'fileno'):
229 return os.fdopen(os.dup(fp.fileno()), mode)
229 return os.fdopen(os.dup(fp.fileno()), mode)
230 else:
230 else:
231 # if this fp can't be duped properly, return
231 # if this fp can't be duped properly, return
232 # a dummy object that can be closed
232 # a dummy object that can be closed
233 class wrappedfileobj(object):
233 class wrappedfileobj(object):
234 noop = lambda x: None
234 noop = lambda x: None
235 def __init__(self, f):
235 def __init__(self, f):
236 self.f = f
236 self.f = f
237 def __getattr__(self, attr):
237 def __getattr__(self, attr):
238 if attr == 'close':
238 if attr == 'close':
239 return self.noop
239 return self.noop
240 else:
240 else:
241 return getattr(self.f, attr)
241 return getattr(self.f, attr)
242
242
243 return wrappedfileobj(fp)
243 return wrappedfileobj(fp)
244 if util.safehasattr(pat, 'write') and writable:
244 if util.safehasattr(pat, 'write') and writable:
245 return pat
245 return pat
246 if util.safehasattr(pat, 'read') and 'r' in mode:
246 if util.safehasattr(pat, 'read') and 'r' in mode:
247 return pat
247 return pat
248 fn = makefilename(repo, pat, node, desc, total, seqno, revwidth, pathname)
248 fn = makefilename(repo, pat, node, desc, total, seqno, revwidth, pathname)
249 if modemap is not None:
249 if modemap is not None:
250 mode = modemap.get(fn, mode)
250 mode = modemap.get(fn, mode)
251 if mode == 'wb':
251 if mode == 'wb':
252 modemap[fn] = 'ab'
252 modemap[fn] = 'ab'
253 return open(fn, mode)
253 return open(fn, mode)
254
254
255 def openrevlog(repo, cmd, file_, opts):
255 def openrevlog(repo, cmd, file_, opts):
256 """opens the changelog, manifest, a filelog or a given revlog"""
256 """opens the changelog, manifest, a filelog or a given revlog"""
257 cl = opts['changelog']
257 cl = opts['changelog']
258 mf = opts['manifest']
258 mf = opts['manifest']
259 msg = None
259 msg = None
260 if cl and mf:
260 if cl and mf:
261 msg = _('cannot specify --changelog and --manifest at the same time')
261 msg = _('cannot specify --changelog and --manifest at the same time')
262 elif cl or mf:
262 elif cl or mf:
263 if file_:
263 if file_:
264 msg = _('cannot specify filename with --changelog or --manifest')
264 msg = _('cannot specify filename with --changelog or --manifest')
265 elif not repo:
265 elif not repo:
266 msg = _('cannot specify --changelog or --manifest '
266 msg = _('cannot specify --changelog or --manifest '
267 'without a repository')
267 'without a repository')
268 if msg:
268 if msg:
269 raise util.Abort(msg)
269 raise util.Abort(msg)
270
270
271 r = None
271 r = None
272 if repo:
272 if repo:
273 if cl:
273 if cl:
274 r = repo.unfiltered().changelog
274 r = repo.unfiltered().changelog
275 elif mf:
275 elif mf:
276 r = repo.manifest
276 r = repo.manifest
277 elif file_:
277 elif file_:
278 filelog = repo.file(file_)
278 filelog = repo.file(file_)
279 if len(filelog):
279 if len(filelog):
280 r = filelog
280 r = filelog
281 if not r:
281 if not r:
282 if not file_:
282 if not file_:
283 raise error.CommandError(cmd, _('invalid arguments'))
283 raise error.CommandError(cmd, _('invalid arguments'))
284 if not os.path.isfile(file_):
284 if not os.path.isfile(file_):
285 raise util.Abort(_("revlog '%s' not found") % file_)
285 raise util.Abort(_("revlog '%s' not found") % file_)
286 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False),
286 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False),
287 file_[:-2] + ".i")
287 file_[:-2] + ".i")
288 return r
288 return r
289
289
290 def copy(ui, repo, pats, opts, rename=False):
290 def copy(ui, repo, pats, opts, rename=False):
291 # called with the repo lock held
291 # called with the repo lock held
292 #
292 #
293 # hgsep => pathname that uses "/" to separate directories
293 # hgsep => pathname that uses "/" to separate directories
294 # ossep => pathname that uses os.sep to separate directories
294 # ossep => pathname that uses os.sep to separate directories
295 cwd = repo.getcwd()
295 cwd = repo.getcwd()
296 targets = {}
296 targets = {}
297 after = opts.get("after")
297 after = opts.get("after")
298 dryrun = opts.get("dry_run")
298 dryrun = opts.get("dry_run")
299 wctx = repo[None]
299 wctx = repo[None]
300
300
301 def walkpat(pat):
301 def walkpat(pat):
302 srcs = []
302 srcs = []
303 badstates = after and '?' or '?r'
303 badstates = after and '?' or '?r'
304 m = scmutil.match(repo[None], [pat], opts, globbed=True)
304 m = scmutil.match(repo[None], [pat], opts, globbed=True)
305 for abs in repo.walk(m):
305 for abs in repo.walk(m):
306 state = repo.dirstate[abs]
306 state = repo.dirstate[abs]
307 rel = m.rel(abs)
307 rel = m.rel(abs)
308 exact = m.exact(abs)
308 exact = m.exact(abs)
309 if state in badstates:
309 if state in badstates:
310 if exact and state == '?':
310 if exact and state == '?':
311 ui.warn(_('%s: not copying - file is not managed\n') % rel)
311 ui.warn(_('%s: not copying - file is not managed\n') % rel)
312 if exact and state == 'r':
312 if exact and state == 'r':
313 ui.warn(_('%s: not copying - file has been marked for'
313 ui.warn(_('%s: not copying - file has been marked for'
314 ' remove\n') % rel)
314 ' remove\n') % rel)
315 continue
315 continue
316 # abs: hgsep
316 # abs: hgsep
317 # rel: ossep
317 # rel: ossep
318 srcs.append((abs, rel, exact))
318 srcs.append((abs, rel, exact))
319 return srcs
319 return srcs
320
320
321 # abssrc: hgsep
321 # abssrc: hgsep
322 # relsrc: ossep
322 # relsrc: ossep
323 # otarget: ossep
323 # otarget: ossep
324 def copyfile(abssrc, relsrc, otarget, exact):
324 def copyfile(abssrc, relsrc, otarget, exact):
325 abstarget = pathutil.canonpath(repo.root, cwd, otarget)
325 abstarget = pathutil.canonpath(repo.root, cwd, otarget)
326 if '/' in abstarget:
326 if '/' in abstarget:
327 # We cannot normalize abstarget itself, this would prevent
327 # We cannot normalize abstarget itself, this would prevent
328 # case only renames, like a => A.
328 # case only renames, like a => A.
329 abspath, absname = abstarget.rsplit('/', 1)
329 abspath, absname = abstarget.rsplit('/', 1)
330 abstarget = repo.dirstate.normalize(abspath) + '/' + absname
330 abstarget = repo.dirstate.normalize(abspath) + '/' + absname
331 reltarget = repo.pathto(abstarget, cwd)
331 reltarget = repo.pathto(abstarget, cwd)
332 target = repo.wjoin(abstarget)
332 target = repo.wjoin(abstarget)
333 src = repo.wjoin(abssrc)
333 src = repo.wjoin(abssrc)
334 state = repo.dirstate[abstarget]
334 state = repo.dirstate[abstarget]
335
335
336 scmutil.checkportable(ui, abstarget)
336 scmutil.checkportable(ui, abstarget)
337
337
338 # check for collisions
338 # check for collisions
339 prevsrc = targets.get(abstarget)
339 prevsrc = targets.get(abstarget)
340 if prevsrc is not None:
340 if prevsrc is not None:
341 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
341 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
342 (reltarget, repo.pathto(abssrc, cwd),
342 (reltarget, repo.pathto(abssrc, cwd),
343 repo.pathto(prevsrc, cwd)))
343 repo.pathto(prevsrc, cwd)))
344 return
344 return
345
345
346 # check for overwrites
346 # check for overwrites
347 exists = os.path.lexists(target)
347 exists = os.path.lexists(target)
348 samefile = False
348 samefile = False
349 if exists and abssrc != abstarget:
349 if exists and abssrc != abstarget:
350 if (repo.dirstate.normalize(abssrc) ==
350 if (repo.dirstate.normalize(abssrc) ==
351 repo.dirstate.normalize(abstarget)):
351 repo.dirstate.normalize(abstarget)):
352 if not rename:
352 if not rename:
353 ui.warn(_("%s: can't copy - same file\n") % reltarget)
353 ui.warn(_("%s: can't copy - same file\n") % reltarget)
354 return
354 return
355 exists = False
355 exists = False
356 samefile = True
356 samefile = True
357
357
358 if not after and exists or after and state in 'mn':
358 if not after and exists or after and state in 'mn':
359 if not opts['force']:
359 if not opts['force']:
360 ui.warn(_('%s: not overwriting - file exists\n') %
360 ui.warn(_('%s: not overwriting - file exists\n') %
361 reltarget)
361 reltarget)
362 return
362 return
363
363
364 if after:
364 if after:
365 if not exists:
365 if not exists:
366 if rename:
366 if rename:
367 ui.warn(_('%s: not recording move - %s does not exist\n') %
367 ui.warn(_('%s: not recording move - %s does not exist\n') %
368 (relsrc, reltarget))
368 (relsrc, reltarget))
369 else:
369 else:
370 ui.warn(_('%s: not recording copy - %s does not exist\n') %
370 ui.warn(_('%s: not recording copy - %s does not exist\n') %
371 (relsrc, reltarget))
371 (relsrc, reltarget))
372 return
372 return
373 elif not dryrun:
373 elif not dryrun:
374 try:
374 try:
375 if exists:
375 if exists:
376 os.unlink(target)
376 os.unlink(target)
377 targetdir = os.path.dirname(target) or '.'
377 targetdir = os.path.dirname(target) or '.'
378 if not os.path.isdir(targetdir):
378 if not os.path.isdir(targetdir):
379 os.makedirs(targetdir)
379 os.makedirs(targetdir)
380 if samefile:
380 if samefile:
381 tmp = target + "~hgrename"
381 tmp = target + "~hgrename"
382 os.rename(src, tmp)
382 os.rename(src, tmp)
383 os.rename(tmp, target)
383 os.rename(tmp, target)
384 else:
384 else:
385 util.copyfile(src, target)
385 util.copyfile(src, target)
386 srcexists = True
386 srcexists = True
387 except IOError, inst:
387 except IOError, inst:
388 if inst.errno == errno.ENOENT:
388 if inst.errno == errno.ENOENT:
389 ui.warn(_('%s: deleted in working copy\n') % relsrc)
389 ui.warn(_('%s: deleted in working copy\n') % relsrc)
390 srcexists = False
390 srcexists = False
391 else:
391 else:
392 ui.warn(_('%s: cannot copy - %s\n') %
392 ui.warn(_('%s: cannot copy - %s\n') %
393 (relsrc, inst.strerror))
393 (relsrc, inst.strerror))
394 return True # report a failure
394 return True # report a failure
395
395
396 if ui.verbose or not exact:
396 if ui.verbose or not exact:
397 if rename:
397 if rename:
398 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
398 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
399 else:
399 else:
400 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
400 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
401
401
402 targets[abstarget] = abssrc
402 targets[abstarget] = abssrc
403
403
404 # fix up dirstate
404 # fix up dirstate
405 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
405 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
406 dryrun=dryrun, cwd=cwd)
406 dryrun=dryrun, cwd=cwd)
407 if rename and not dryrun:
407 if rename and not dryrun:
408 if not after and srcexists and not samefile:
408 if not after and srcexists and not samefile:
409 util.unlinkpath(repo.wjoin(abssrc))
409 util.unlinkpath(repo.wjoin(abssrc))
410 wctx.forget([abssrc])
410 wctx.forget([abssrc])
411
411
412 # pat: ossep
412 # pat: ossep
413 # dest ossep
413 # dest ossep
414 # srcs: list of (hgsep, hgsep, ossep, bool)
414 # srcs: list of (hgsep, hgsep, ossep, bool)
415 # return: function that takes hgsep and returns ossep
415 # return: function that takes hgsep and returns ossep
416 def targetpathfn(pat, dest, srcs):
416 def targetpathfn(pat, dest, srcs):
417 if os.path.isdir(pat):
417 if os.path.isdir(pat):
418 abspfx = pathutil.canonpath(repo.root, cwd, pat)
418 abspfx = pathutil.canonpath(repo.root, cwd, pat)
419 abspfx = util.localpath(abspfx)
419 abspfx = util.localpath(abspfx)
420 if destdirexists:
420 if destdirexists:
421 striplen = len(os.path.split(abspfx)[0])
421 striplen = len(os.path.split(abspfx)[0])
422 else:
422 else:
423 striplen = len(abspfx)
423 striplen = len(abspfx)
424 if striplen:
424 if striplen:
425 striplen += len(os.sep)
425 striplen += len(os.sep)
426 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
426 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
427 elif destdirexists:
427 elif destdirexists:
428 res = lambda p: os.path.join(dest,
428 res = lambda p: os.path.join(dest,
429 os.path.basename(util.localpath(p)))
429 os.path.basename(util.localpath(p)))
430 else:
430 else:
431 res = lambda p: dest
431 res = lambda p: dest
432 return res
432 return res
433
433
434 # pat: ossep
434 # pat: ossep
435 # dest ossep
435 # dest ossep
436 # srcs: list of (hgsep, hgsep, ossep, bool)
436 # srcs: list of (hgsep, hgsep, ossep, bool)
437 # return: function that takes hgsep and returns ossep
437 # return: function that takes hgsep and returns ossep
438 def targetpathafterfn(pat, dest, srcs):
438 def targetpathafterfn(pat, dest, srcs):
439 if matchmod.patkind(pat):
439 if matchmod.patkind(pat):
440 # a mercurial pattern
440 # a mercurial pattern
441 res = lambda p: os.path.join(dest,
441 res = lambda p: os.path.join(dest,
442 os.path.basename(util.localpath(p)))
442 os.path.basename(util.localpath(p)))
443 else:
443 else:
444 abspfx = pathutil.canonpath(repo.root, cwd, pat)
444 abspfx = pathutil.canonpath(repo.root, cwd, pat)
445 if len(abspfx) < len(srcs[0][0]):
445 if len(abspfx) < len(srcs[0][0]):
446 # A directory. Either the target path contains the last
446 # A directory. Either the target path contains the last
447 # component of the source path or it does not.
447 # component of the source path or it does not.
448 def evalpath(striplen):
448 def evalpath(striplen):
449 score = 0
449 score = 0
450 for s in srcs:
450 for s in srcs:
451 t = os.path.join(dest, util.localpath(s[0])[striplen:])
451 t = os.path.join(dest, util.localpath(s[0])[striplen:])
452 if os.path.lexists(t):
452 if os.path.lexists(t):
453 score += 1
453 score += 1
454 return score
454 return score
455
455
456 abspfx = util.localpath(abspfx)
456 abspfx = util.localpath(abspfx)
457 striplen = len(abspfx)
457 striplen = len(abspfx)
458 if striplen:
458 if striplen:
459 striplen += len(os.sep)
459 striplen += len(os.sep)
460 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
460 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
461 score = evalpath(striplen)
461 score = evalpath(striplen)
462 striplen1 = len(os.path.split(abspfx)[0])
462 striplen1 = len(os.path.split(abspfx)[0])
463 if striplen1:
463 if striplen1:
464 striplen1 += len(os.sep)
464 striplen1 += len(os.sep)
465 if evalpath(striplen1) > score:
465 if evalpath(striplen1) > score:
466 striplen = striplen1
466 striplen = striplen1
467 res = lambda p: os.path.join(dest,
467 res = lambda p: os.path.join(dest,
468 util.localpath(p)[striplen:])
468 util.localpath(p)[striplen:])
469 else:
469 else:
470 # a file
470 # a file
471 if destdirexists:
471 if destdirexists:
472 res = lambda p: os.path.join(dest,
472 res = lambda p: os.path.join(dest,
473 os.path.basename(util.localpath(p)))
473 os.path.basename(util.localpath(p)))
474 else:
474 else:
475 res = lambda p: dest
475 res = lambda p: dest
476 return res
476 return res
477
477
478
478
479 pats = scmutil.expandpats(pats)
479 pats = scmutil.expandpats(pats)
480 if not pats:
480 if not pats:
481 raise util.Abort(_('no source or destination specified'))
481 raise util.Abort(_('no source or destination specified'))
482 if len(pats) == 1:
482 if len(pats) == 1:
483 raise util.Abort(_('no destination specified'))
483 raise util.Abort(_('no destination specified'))
484 dest = pats.pop()
484 dest = pats.pop()
485 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
485 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
486 if not destdirexists:
486 if not destdirexists:
487 if len(pats) > 1 or matchmod.patkind(pats[0]):
487 if len(pats) > 1 or matchmod.patkind(pats[0]):
488 raise util.Abort(_('with multiple sources, destination must be an '
488 raise util.Abort(_('with multiple sources, destination must be an '
489 'existing directory'))
489 'existing directory'))
490 if util.endswithsep(dest):
490 if util.endswithsep(dest):
491 raise util.Abort(_('destination %s is not a directory') % dest)
491 raise util.Abort(_('destination %s is not a directory') % dest)
492
492
493 tfn = targetpathfn
493 tfn = targetpathfn
494 if after:
494 if after:
495 tfn = targetpathafterfn
495 tfn = targetpathafterfn
496 copylist = []
496 copylist = []
497 for pat in pats:
497 for pat in pats:
498 srcs = walkpat(pat)
498 srcs = walkpat(pat)
499 if not srcs:
499 if not srcs:
500 continue
500 continue
501 copylist.append((tfn(pat, dest, srcs), srcs))
501 copylist.append((tfn(pat, dest, srcs), srcs))
502 if not copylist:
502 if not copylist:
503 raise util.Abort(_('no files to copy'))
503 raise util.Abort(_('no files to copy'))
504
504
505 errors = 0
505 errors = 0
506 for targetpath, srcs in copylist:
506 for targetpath, srcs in copylist:
507 for abssrc, relsrc, exact in srcs:
507 for abssrc, relsrc, exact in srcs:
508 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
508 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
509 errors += 1
509 errors += 1
510
510
511 if errors:
511 if errors:
512 ui.warn(_('(consider using --after)\n'))
512 ui.warn(_('(consider using --after)\n'))
513
513
514 return errors != 0
514 return errors != 0
515
515
516 def service(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
516 def service(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
517 runargs=None, appendpid=False):
517 runargs=None, appendpid=False):
518 '''Run a command as a service.'''
518 '''Run a command as a service.'''
519
519
520 def writepid(pid):
520 def writepid(pid):
521 if opts['pid_file']:
521 if opts['pid_file']:
522 mode = appendpid and 'a' or 'w'
522 mode = appendpid and 'a' or 'w'
523 fp = open(opts['pid_file'], mode)
523 fp = open(opts['pid_file'], mode)
524 fp.write(str(pid) + '\n')
524 fp.write(str(pid) + '\n')
525 fp.close()
525 fp.close()
526
526
527 if opts['daemon'] and not opts['daemon_pipefds']:
527 if opts['daemon'] and not opts['daemon_pipefds']:
528 # Signal child process startup with file removal
528 # Signal child process startup with file removal
529 lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-')
529 lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-')
530 os.close(lockfd)
530 os.close(lockfd)
531 try:
531 try:
532 if not runargs:
532 if not runargs:
533 runargs = util.hgcmd() + sys.argv[1:]
533 runargs = util.hgcmd() + sys.argv[1:]
534 runargs.append('--daemon-pipefds=%s' % lockpath)
534 runargs.append('--daemon-pipefds=%s' % lockpath)
535 # Don't pass --cwd to the child process, because we've already
535 # Don't pass --cwd to the child process, because we've already
536 # changed directory.
536 # changed directory.
537 for i in xrange(1, len(runargs)):
537 for i in xrange(1, len(runargs)):
538 if runargs[i].startswith('--cwd='):
538 if runargs[i].startswith('--cwd='):
539 del runargs[i]
539 del runargs[i]
540 break
540 break
541 elif runargs[i].startswith('--cwd'):
541 elif runargs[i].startswith('--cwd'):
542 del runargs[i:i + 2]
542 del runargs[i:i + 2]
543 break
543 break
544 def condfn():
544 def condfn():
545 return not os.path.exists(lockpath)
545 return not os.path.exists(lockpath)
546 pid = util.rundetached(runargs, condfn)
546 pid = util.rundetached(runargs, condfn)
547 if pid < 0:
547 if pid < 0:
548 raise util.Abort(_('child process failed to start'))
548 raise util.Abort(_('child process failed to start'))
549 writepid(pid)
549 writepid(pid)
550 finally:
550 finally:
551 try:
551 try:
552 os.unlink(lockpath)
552 os.unlink(lockpath)
553 except OSError, e:
553 except OSError, e:
554 if e.errno != errno.ENOENT:
554 if e.errno != errno.ENOENT:
555 raise
555 raise
556 if parentfn:
556 if parentfn:
557 return parentfn(pid)
557 return parentfn(pid)
558 else:
558 else:
559 return
559 return
560
560
561 if initfn:
561 if initfn:
562 initfn()
562 initfn()
563
563
564 if not opts['daemon']:
564 if not opts['daemon']:
565 writepid(os.getpid())
565 writepid(os.getpid())
566
566
567 if opts['daemon_pipefds']:
567 if opts['daemon_pipefds']:
568 lockpath = opts['daemon_pipefds']
568 lockpath = opts['daemon_pipefds']
569 try:
569 try:
570 os.setsid()
570 os.setsid()
571 except AttributeError:
571 except AttributeError:
572 pass
572 pass
573 os.unlink(lockpath)
573 os.unlink(lockpath)
574 util.hidewindow()
574 util.hidewindow()
575 sys.stdout.flush()
575 sys.stdout.flush()
576 sys.stderr.flush()
576 sys.stderr.flush()
577
577
578 nullfd = os.open(os.devnull, os.O_RDWR)
578 nullfd = os.open(os.devnull, os.O_RDWR)
579 logfilefd = nullfd
579 logfilefd = nullfd
580 if logfile:
580 if logfile:
581 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
581 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
582 os.dup2(nullfd, 0)
582 os.dup2(nullfd, 0)
583 os.dup2(logfilefd, 1)
583 os.dup2(logfilefd, 1)
584 os.dup2(logfilefd, 2)
584 os.dup2(logfilefd, 2)
585 if nullfd not in (0, 1, 2):
585 if nullfd not in (0, 1, 2):
586 os.close(nullfd)
586 os.close(nullfd)
587 if logfile and logfilefd not in (0, 1, 2):
587 if logfile and logfilefd not in (0, 1, 2):
588 os.close(logfilefd)
588 os.close(logfilefd)
589
589
590 if runfn:
590 if runfn:
591 return runfn()
591 return runfn()
592
592
593 def tryimportone(ui, repo, hunk, parents, opts, msgs, updatefunc):
593 def tryimportone(ui, repo, hunk, parents, opts, msgs, updatefunc):
594 """Utility function used by commands.import to import a single patch
594 """Utility function used by commands.import to import a single patch
595
595
596 This function is explicitly defined here to help the evolve extension to
596 This function is explicitly defined here to help the evolve extension to
597 wrap this part of the import logic.
597 wrap this part of the import logic.
598
598
599 The API is currently a bit ugly because it a simple code translation from
599 The API is currently a bit ugly because it a simple code translation from
600 the import command. Feel free to make it better.
600 the import command. Feel free to make it better.
601
601
602 :hunk: a patch (as a binary string)
602 :hunk: a patch (as a binary string)
603 :parents: nodes that will be parent of the created commit
603 :parents: nodes that will be parent of the created commit
604 :opts: the full dict of option passed to the import command
604 :opts: the full dict of option passed to the import command
605 :msgs: list to save commit message to.
605 :msgs: list to save commit message to.
606 (used in case we need to save it when failing)
606 (used in case we need to save it when failing)
607 :updatefunc: a function that update a repo to a given node
607 :updatefunc: a function that update a repo to a given node
608 updatefunc(<repo>, <node>)
608 updatefunc(<repo>, <node>)
609 """
609 """
610 tmpname, message, user, date, branch, nodeid, p1, p2 = \
610 tmpname, message, user, date, branch, nodeid, p1, p2 = \
611 patch.extract(ui, hunk)
611 patch.extract(ui, hunk)
612
612
613 update = not opts.get('bypass')
613 update = not opts.get('bypass')
614 strip = opts["strip"]
614 strip = opts["strip"]
615 sim = float(opts.get('similarity') or 0)
615 sim = float(opts.get('similarity') or 0)
616 if not tmpname:
616 if not tmpname:
617 return (None, None, False)
617 return (None, None, False)
618 msg = _('applied to working directory')
618 msg = _('applied to working directory')
619
619
620 rejects = False
620 rejects = False
621
621
622 try:
622 try:
623 cmdline_message = logmessage(ui, opts)
623 cmdline_message = logmessage(ui, opts)
624 if cmdline_message:
624 if cmdline_message:
625 # pickup the cmdline msg
625 # pickup the cmdline msg
626 message = cmdline_message
626 message = cmdline_message
627 elif message:
627 elif message:
628 # pickup the patch msg
628 # pickup the patch msg
629 message = message.strip()
629 message = message.strip()
630 else:
630 else:
631 # launch the editor
631 # launch the editor
632 message = None
632 message = None
633 ui.debug('message:\n%s\n' % message)
633 ui.debug('message:\n%s\n' % message)
634
634
635 if len(parents) == 1:
635 if len(parents) == 1:
636 parents.append(repo[nullid])
636 parents.append(repo[nullid])
637 if opts.get('exact'):
637 if opts.get('exact'):
638 if not nodeid or not p1:
638 if not nodeid or not p1:
639 raise util.Abort(_('not a Mercurial patch'))
639 raise util.Abort(_('not a Mercurial patch'))
640 p1 = repo[p1]
640 p1 = repo[p1]
641 p2 = repo[p2 or nullid]
641 p2 = repo[p2 or nullid]
642 elif p2:
642 elif p2:
643 try:
643 try:
644 p1 = repo[p1]
644 p1 = repo[p1]
645 p2 = repo[p2]
645 p2 = repo[p2]
646 # Without any options, consider p2 only if the
646 # Without any options, consider p2 only if the
647 # patch is being applied on top of the recorded
647 # patch is being applied on top of the recorded
648 # first parent.
648 # first parent.
649 if p1 != parents[0]:
649 if p1 != parents[0]:
650 p1 = parents[0]
650 p1 = parents[0]
651 p2 = repo[nullid]
651 p2 = repo[nullid]
652 except error.RepoError:
652 except error.RepoError:
653 p1, p2 = parents
653 p1, p2 = parents
654 else:
654 else:
655 p1, p2 = parents
655 p1, p2 = parents
656
656
657 n = None
657 n = None
658 if update:
658 if update:
659 if p1 != parents[0]:
659 if p1 != parents[0]:
660 updatefunc(repo, p1.node())
660 updatefunc(repo, p1.node())
661 if p2 != parents[1]:
661 if p2 != parents[1]:
662 repo.setparents(p1.node(), p2.node())
662 repo.setparents(p1.node(), p2.node())
663
663
664 if opts.get('exact') or opts.get('import_branch'):
664 if opts.get('exact') or opts.get('import_branch'):
665 repo.dirstate.setbranch(branch or 'default')
665 repo.dirstate.setbranch(branch or 'default')
666
666
667 partial = opts.get('partial', False)
667 partial = opts.get('partial', False)
668 files = set()
668 files = set()
669 try:
669 try:
670 patch.patch(ui, repo, tmpname, strip=strip, files=files,
670 patch.patch(ui, repo, tmpname, strip=strip, files=files,
671 eolmode=None, similarity=sim / 100.0)
671 eolmode=None, similarity=sim / 100.0)
672 except patch.PatchError, e:
672 except patch.PatchError, e:
673 if not partial:
673 if not partial:
674 raise util.Abort(str(e))
674 raise util.Abort(str(e))
675 if partial:
675 if partial:
676 rejects = True
676 rejects = True
677
677
678 files = list(files)
678 files = list(files)
679 if opts.get('no_commit'):
679 if opts.get('no_commit'):
680 if message:
680 if message:
681 msgs.append(message)
681 msgs.append(message)
682 else:
682 else:
683 if opts.get('exact') or p2:
683 if opts.get('exact') or p2:
684 # If you got here, you either use --force and know what
684 # If you got here, you either use --force and know what
685 # you are doing or used --exact or a merge patch while
685 # you are doing or used --exact or a merge patch while
686 # being updated to its first parent.
686 # being updated to its first parent.
687 m = None
687 m = None
688 else:
688 else:
689 m = scmutil.matchfiles(repo, files or [])
689 m = scmutil.matchfiles(repo, files or [])
690 editform = mergeeditform(repo[None], 'import.normal')
690 editform = mergeeditform(repo[None], 'import.normal')
691 editor = getcommiteditor(editform=editform, **opts)
691 if opts.get('exact'):
692 editor = None
693 else:
694 editor = getcommiteditor(editform=editform, **opts)
692 n = repo.commit(message, opts.get('user') or user,
695 n = repo.commit(message, opts.get('user') or user,
693 opts.get('date') or date, match=m,
696 opts.get('date') or date, match=m,
694 editor=editor, force=partial)
697 editor=editor, force=partial)
695 else:
698 else:
696 if opts.get('exact') or opts.get('import_branch'):
699 if opts.get('exact') or opts.get('import_branch'):
697 branch = branch or 'default'
700 branch = branch or 'default'
698 else:
701 else:
699 branch = p1.branch()
702 branch = p1.branch()
700 store = patch.filestore()
703 store = patch.filestore()
701 try:
704 try:
702 files = set()
705 files = set()
703 try:
706 try:
704 patch.patchrepo(ui, repo, p1, store, tmpname, strip,
707 patch.patchrepo(ui, repo, p1, store, tmpname, strip,
705 files, eolmode=None)
708 files, eolmode=None)
706 except patch.PatchError, e:
709 except patch.PatchError, e:
707 raise util.Abort(str(e))
710 raise util.Abort(str(e))
708 editor = getcommiteditor(editform='import.bypass')
711 if opts.get('exact'):
712 editor = None
713 else:
714 editor = getcommiteditor(editform='import.bypass')
709 memctx = context.makememctx(repo, (p1.node(), p2.node()),
715 memctx = context.makememctx(repo, (p1.node(), p2.node()),
710 message,
716 message,
711 opts.get('user') or user,
717 opts.get('user') or user,
712 opts.get('date') or date,
718 opts.get('date') or date,
713 branch, files, store,
719 branch, files, store,
714 editor=editor)
720 editor=editor)
715 n = memctx.commit()
721 n = memctx.commit()
716 finally:
722 finally:
717 store.close()
723 store.close()
718 if opts.get('exact') and hex(n) != nodeid:
724 if opts.get('exact') and hex(n) != nodeid:
719 raise util.Abort(_('patch is damaged or loses information'))
725 raise util.Abort(_('patch is damaged or loses information'))
720 if n:
726 if n:
721 # i18n: refers to a short changeset id
727 # i18n: refers to a short changeset id
722 msg = _('created %s') % short(n)
728 msg = _('created %s') % short(n)
723 return (msg, n, rejects)
729 return (msg, n, rejects)
724 finally:
730 finally:
725 os.unlink(tmpname)
731 os.unlink(tmpname)
726
732
727 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
733 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
728 opts=None):
734 opts=None):
729 '''export changesets as hg patches.'''
735 '''export changesets as hg patches.'''
730
736
731 total = len(revs)
737 total = len(revs)
732 revwidth = max([len(str(rev)) for rev in revs])
738 revwidth = max([len(str(rev)) for rev in revs])
733 filemode = {}
739 filemode = {}
734
740
735 def single(rev, seqno, fp):
741 def single(rev, seqno, fp):
736 ctx = repo[rev]
742 ctx = repo[rev]
737 node = ctx.node()
743 node = ctx.node()
738 parents = [p.node() for p in ctx.parents() if p]
744 parents = [p.node() for p in ctx.parents() if p]
739 branch = ctx.branch()
745 branch = ctx.branch()
740 if switch_parent:
746 if switch_parent:
741 parents.reverse()
747 parents.reverse()
742 prev = (parents and parents[0]) or nullid
748 prev = (parents and parents[0]) or nullid
743
749
744 shouldclose = False
750 shouldclose = False
745 if not fp and len(template) > 0:
751 if not fp and len(template) > 0:
746 desc_lines = ctx.description().rstrip().split('\n')
752 desc_lines = ctx.description().rstrip().split('\n')
747 desc = desc_lines[0] #Commit always has a first line.
753 desc = desc_lines[0] #Commit always has a first line.
748 fp = makefileobj(repo, template, node, desc=desc, total=total,
754 fp = makefileobj(repo, template, node, desc=desc, total=total,
749 seqno=seqno, revwidth=revwidth, mode='wb',
755 seqno=seqno, revwidth=revwidth, mode='wb',
750 modemap=filemode)
756 modemap=filemode)
751 if fp != template:
757 if fp != template:
752 shouldclose = True
758 shouldclose = True
753 if fp and fp != sys.stdout and util.safehasattr(fp, 'name'):
759 if fp and fp != sys.stdout and util.safehasattr(fp, 'name'):
754 repo.ui.note("%s\n" % fp.name)
760 repo.ui.note("%s\n" % fp.name)
755
761
756 if not fp:
762 if not fp:
757 write = repo.ui.write
763 write = repo.ui.write
758 else:
764 else:
759 def write(s, **kw):
765 def write(s, **kw):
760 fp.write(s)
766 fp.write(s)
761
767
762
768
763 write("# HG changeset patch\n")
769 write("# HG changeset patch\n")
764 write("# User %s\n" % ctx.user())
770 write("# User %s\n" % ctx.user())
765 write("# Date %d %d\n" % ctx.date())
771 write("# Date %d %d\n" % ctx.date())
766 write("# %s\n" % util.datestr(ctx.date()))
772 write("# %s\n" % util.datestr(ctx.date()))
767 if branch and branch != 'default':
773 if branch and branch != 'default':
768 write("# Branch %s\n" % branch)
774 write("# Branch %s\n" % branch)
769 write("# Node ID %s\n" % hex(node))
775 write("# Node ID %s\n" % hex(node))
770 write("# Parent %s\n" % hex(prev))
776 write("# Parent %s\n" % hex(prev))
771 if len(parents) > 1:
777 if len(parents) > 1:
772 write("# Parent %s\n" % hex(parents[1]))
778 write("# Parent %s\n" % hex(parents[1]))
773 write(ctx.description().rstrip())
779 write(ctx.description().rstrip())
774 write("\n\n")
780 write("\n\n")
775
781
776 for chunk, label in patch.diffui(repo, prev, node, opts=opts):
782 for chunk, label in patch.diffui(repo, prev, node, opts=opts):
777 write(chunk, label=label)
783 write(chunk, label=label)
778
784
779 if shouldclose:
785 if shouldclose:
780 fp.close()
786 fp.close()
781
787
782 for seqno, rev in enumerate(revs):
788 for seqno, rev in enumerate(revs):
783 single(rev, seqno + 1, fp)
789 single(rev, seqno + 1, fp)
784
790
785 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
791 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
786 changes=None, stat=False, fp=None, prefix='',
792 changes=None, stat=False, fp=None, prefix='',
787 listsubrepos=False):
793 listsubrepos=False):
788 '''show diff or diffstat.'''
794 '''show diff or diffstat.'''
789 if fp is None:
795 if fp is None:
790 write = ui.write
796 write = ui.write
791 else:
797 else:
792 def write(s, **kw):
798 def write(s, **kw):
793 fp.write(s)
799 fp.write(s)
794
800
795 if stat:
801 if stat:
796 diffopts = diffopts.copy(context=0)
802 diffopts = diffopts.copy(context=0)
797 width = 80
803 width = 80
798 if not ui.plain():
804 if not ui.plain():
799 width = ui.termwidth()
805 width = ui.termwidth()
800 chunks = patch.diff(repo, node1, node2, match, changes, diffopts,
806 chunks = patch.diff(repo, node1, node2, match, changes, diffopts,
801 prefix=prefix)
807 prefix=prefix)
802 for chunk, label in patch.diffstatui(util.iterlines(chunks),
808 for chunk, label in patch.diffstatui(util.iterlines(chunks),
803 width=width,
809 width=width,
804 git=diffopts.git):
810 git=diffopts.git):
805 write(chunk, label=label)
811 write(chunk, label=label)
806 else:
812 else:
807 for chunk, label in patch.diffui(repo, node1, node2, match,
813 for chunk, label in patch.diffui(repo, node1, node2, match,
808 changes, diffopts, prefix=prefix):
814 changes, diffopts, prefix=prefix):
809 write(chunk, label=label)
815 write(chunk, label=label)
810
816
811 if listsubrepos:
817 if listsubrepos:
812 ctx1 = repo[node1]
818 ctx1 = repo[node1]
813 ctx2 = repo[node2]
819 ctx2 = repo[node2]
814 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
820 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
815 tempnode2 = node2
821 tempnode2 = node2
816 try:
822 try:
817 if node2 is not None:
823 if node2 is not None:
818 tempnode2 = ctx2.substate[subpath][1]
824 tempnode2 = ctx2.substate[subpath][1]
819 except KeyError:
825 except KeyError:
820 # A subrepo that existed in node1 was deleted between node1 and
826 # A subrepo that existed in node1 was deleted between node1 and
821 # node2 (inclusive). Thus, ctx2's substate won't contain that
827 # node2 (inclusive). Thus, ctx2's substate won't contain that
822 # subpath. The best we can do is to ignore it.
828 # subpath. The best we can do is to ignore it.
823 tempnode2 = None
829 tempnode2 = None
824 submatch = matchmod.narrowmatcher(subpath, match)
830 submatch = matchmod.narrowmatcher(subpath, match)
825 sub.diff(ui, diffopts, tempnode2, submatch, changes=changes,
831 sub.diff(ui, diffopts, tempnode2, submatch, changes=changes,
826 stat=stat, fp=fp, prefix=prefix)
832 stat=stat, fp=fp, prefix=prefix)
827
833
828 class changeset_printer(object):
834 class changeset_printer(object):
829 '''show changeset information when templating not requested.'''
835 '''show changeset information when templating not requested.'''
830
836
831 def __init__(self, ui, repo, patch, diffopts, buffered):
837 def __init__(self, ui, repo, patch, diffopts, buffered):
832 self.ui = ui
838 self.ui = ui
833 self.repo = repo
839 self.repo = repo
834 self.buffered = buffered
840 self.buffered = buffered
835 self.patch = patch
841 self.patch = patch
836 self.diffopts = diffopts
842 self.diffopts = diffopts
837 self.header = {}
843 self.header = {}
838 self.hunk = {}
844 self.hunk = {}
839 self.lastheader = None
845 self.lastheader = None
840 self.footer = None
846 self.footer = None
841
847
842 def flush(self, rev):
848 def flush(self, rev):
843 if rev in self.header:
849 if rev in self.header:
844 h = self.header[rev]
850 h = self.header[rev]
845 if h != self.lastheader:
851 if h != self.lastheader:
846 self.lastheader = h
852 self.lastheader = h
847 self.ui.write(h)
853 self.ui.write(h)
848 del self.header[rev]
854 del self.header[rev]
849 if rev in self.hunk:
855 if rev in self.hunk:
850 self.ui.write(self.hunk[rev])
856 self.ui.write(self.hunk[rev])
851 del self.hunk[rev]
857 del self.hunk[rev]
852 return 1
858 return 1
853 return 0
859 return 0
854
860
855 def close(self):
861 def close(self):
856 if self.footer:
862 if self.footer:
857 self.ui.write(self.footer)
863 self.ui.write(self.footer)
858
864
859 def show(self, ctx, copies=None, matchfn=None, **props):
865 def show(self, ctx, copies=None, matchfn=None, **props):
860 if self.buffered:
866 if self.buffered:
861 self.ui.pushbuffer()
867 self.ui.pushbuffer()
862 self._show(ctx, copies, matchfn, props)
868 self._show(ctx, copies, matchfn, props)
863 self.hunk[ctx.rev()] = self.ui.popbuffer(labeled=True)
869 self.hunk[ctx.rev()] = self.ui.popbuffer(labeled=True)
864 else:
870 else:
865 self._show(ctx, copies, matchfn, props)
871 self._show(ctx, copies, matchfn, props)
866
872
867 def _show(self, ctx, copies, matchfn, props):
873 def _show(self, ctx, copies, matchfn, props):
868 '''show a single changeset or file revision'''
874 '''show a single changeset or file revision'''
869 changenode = ctx.node()
875 changenode = ctx.node()
870 rev = ctx.rev()
876 rev = ctx.rev()
871
877
872 if self.ui.quiet:
878 if self.ui.quiet:
873 self.ui.write("%d:%s\n" % (rev, short(changenode)),
879 self.ui.write("%d:%s\n" % (rev, short(changenode)),
874 label='log.node')
880 label='log.node')
875 return
881 return
876
882
877 log = self.repo.changelog
883 log = self.repo.changelog
878 date = util.datestr(ctx.date())
884 date = util.datestr(ctx.date())
879
885
880 hexfunc = self.ui.debugflag and hex or short
886 hexfunc = self.ui.debugflag and hex or short
881
887
882 parents = [(p, hexfunc(log.node(p)))
888 parents = [(p, hexfunc(log.node(p)))
883 for p in self._meaningful_parentrevs(log, rev)]
889 for p in self._meaningful_parentrevs(log, rev)]
884
890
885 # i18n: column positioning for "hg log"
891 # i18n: column positioning for "hg log"
886 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)),
892 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)),
887 label='log.changeset changeset.%s' % ctx.phasestr())
893 label='log.changeset changeset.%s' % ctx.phasestr())
888
894
889 branch = ctx.branch()
895 branch = ctx.branch()
890 # don't show the default branch name
896 # don't show the default branch name
891 if branch != 'default':
897 if branch != 'default':
892 # i18n: column positioning for "hg log"
898 # i18n: column positioning for "hg log"
893 self.ui.write(_("branch: %s\n") % branch,
899 self.ui.write(_("branch: %s\n") % branch,
894 label='log.branch')
900 label='log.branch')
895 for bookmark in self.repo.nodebookmarks(changenode):
901 for bookmark in self.repo.nodebookmarks(changenode):
896 # i18n: column positioning for "hg log"
902 # i18n: column positioning for "hg log"
897 self.ui.write(_("bookmark: %s\n") % bookmark,
903 self.ui.write(_("bookmark: %s\n") % bookmark,
898 label='log.bookmark')
904 label='log.bookmark')
899 for tag in self.repo.nodetags(changenode):
905 for tag in self.repo.nodetags(changenode):
900 # i18n: column positioning for "hg log"
906 # i18n: column positioning for "hg log"
901 self.ui.write(_("tag: %s\n") % tag,
907 self.ui.write(_("tag: %s\n") % tag,
902 label='log.tag')
908 label='log.tag')
903 if self.ui.debugflag and ctx.phase():
909 if self.ui.debugflag and ctx.phase():
904 # i18n: column positioning for "hg log"
910 # i18n: column positioning for "hg log"
905 self.ui.write(_("phase: %s\n") % _(ctx.phasestr()),
911 self.ui.write(_("phase: %s\n") % _(ctx.phasestr()),
906 label='log.phase')
912 label='log.phase')
907 for parent in parents:
913 for parent in parents:
908 # i18n: column positioning for "hg log"
914 # i18n: column positioning for "hg log"
909 self.ui.write(_("parent: %d:%s\n") % parent,
915 self.ui.write(_("parent: %d:%s\n") % parent,
910 label='log.parent changeset.%s' % ctx.phasestr())
916 label='log.parent changeset.%s' % ctx.phasestr())
911
917
912 if self.ui.debugflag:
918 if self.ui.debugflag:
913 mnode = ctx.manifestnode()
919 mnode = ctx.manifestnode()
914 # i18n: column positioning for "hg log"
920 # i18n: column positioning for "hg log"
915 self.ui.write(_("manifest: %d:%s\n") %
921 self.ui.write(_("manifest: %d:%s\n") %
916 (self.repo.manifest.rev(mnode), hex(mnode)),
922 (self.repo.manifest.rev(mnode), hex(mnode)),
917 label='ui.debug log.manifest')
923 label='ui.debug log.manifest')
918 # i18n: column positioning for "hg log"
924 # i18n: column positioning for "hg log"
919 self.ui.write(_("user: %s\n") % ctx.user(),
925 self.ui.write(_("user: %s\n") % ctx.user(),
920 label='log.user')
926 label='log.user')
921 # i18n: column positioning for "hg log"
927 # i18n: column positioning for "hg log"
922 self.ui.write(_("date: %s\n") % date,
928 self.ui.write(_("date: %s\n") % date,
923 label='log.date')
929 label='log.date')
924
930
925 if self.ui.debugflag:
931 if self.ui.debugflag:
926 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
932 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
927 for key, value in zip([# i18n: column positioning for "hg log"
933 for key, value in zip([# i18n: column positioning for "hg log"
928 _("files:"),
934 _("files:"),
929 # i18n: column positioning for "hg log"
935 # i18n: column positioning for "hg log"
930 _("files+:"),
936 _("files+:"),
931 # i18n: column positioning for "hg log"
937 # i18n: column positioning for "hg log"
932 _("files-:")], files):
938 _("files-:")], files):
933 if value:
939 if value:
934 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
940 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
935 label='ui.debug log.files')
941 label='ui.debug log.files')
936 elif ctx.files() and self.ui.verbose:
942 elif ctx.files() and self.ui.verbose:
937 # i18n: column positioning for "hg log"
943 # i18n: column positioning for "hg log"
938 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
944 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
939 label='ui.note log.files')
945 label='ui.note log.files')
940 if copies and self.ui.verbose:
946 if copies and self.ui.verbose:
941 copies = ['%s (%s)' % c for c in copies]
947 copies = ['%s (%s)' % c for c in copies]
942 # i18n: column positioning for "hg log"
948 # i18n: column positioning for "hg log"
943 self.ui.write(_("copies: %s\n") % ' '.join(copies),
949 self.ui.write(_("copies: %s\n") % ' '.join(copies),
944 label='ui.note log.copies')
950 label='ui.note log.copies')
945
951
946 extra = ctx.extra()
952 extra = ctx.extra()
947 if extra and self.ui.debugflag:
953 if extra and self.ui.debugflag:
948 for key, value in sorted(extra.items()):
954 for key, value in sorted(extra.items()):
949 # i18n: column positioning for "hg log"
955 # i18n: column positioning for "hg log"
950 self.ui.write(_("extra: %s=%s\n")
956 self.ui.write(_("extra: %s=%s\n")
951 % (key, value.encode('string_escape')),
957 % (key, value.encode('string_escape')),
952 label='ui.debug log.extra')
958 label='ui.debug log.extra')
953
959
954 description = ctx.description().strip()
960 description = ctx.description().strip()
955 if description:
961 if description:
956 if self.ui.verbose:
962 if self.ui.verbose:
957 self.ui.write(_("description:\n"),
963 self.ui.write(_("description:\n"),
958 label='ui.note log.description')
964 label='ui.note log.description')
959 self.ui.write(description,
965 self.ui.write(description,
960 label='ui.note log.description')
966 label='ui.note log.description')
961 self.ui.write("\n\n")
967 self.ui.write("\n\n")
962 else:
968 else:
963 # i18n: column positioning for "hg log"
969 # i18n: column positioning for "hg log"
964 self.ui.write(_("summary: %s\n") %
970 self.ui.write(_("summary: %s\n") %
965 description.splitlines()[0],
971 description.splitlines()[0],
966 label='log.summary')
972 label='log.summary')
967 self.ui.write("\n")
973 self.ui.write("\n")
968
974
969 self.showpatch(changenode, matchfn)
975 self.showpatch(changenode, matchfn)
970
976
971 def showpatch(self, node, matchfn):
977 def showpatch(self, node, matchfn):
972 if not matchfn:
978 if not matchfn:
973 matchfn = self.patch
979 matchfn = self.patch
974 if matchfn:
980 if matchfn:
975 stat = self.diffopts.get('stat')
981 stat = self.diffopts.get('stat')
976 diff = self.diffopts.get('patch')
982 diff = self.diffopts.get('patch')
977 diffopts = patch.diffopts(self.ui, self.diffopts)
983 diffopts = patch.diffopts(self.ui, self.diffopts)
978 prev = self.repo.changelog.parents(node)[0]
984 prev = self.repo.changelog.parents(node)[0]
979 if stat:
985 if stat:
980 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
986 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
981 match=matchfn, stat=True)
987 match=matchfn, stat=True)
982 if diff:
988 if diff:
983 if stat:
989 if stat:
984 self.ui.write("\n")
990 self.ui.write("\n")
985 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
991 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
986 match=matchfn, stat=False)
992 match=matchfn, stat=False)
987 self.ui.write("\n")
993 self.ui.write("\n")
988
994
989 def _meaningful_parentrevs(self, log, rev):
995 def _meaningful_parentrevs(self, log, rev):
990 """Return list of meaningful (or all if debug) parentrevs for rev.
996 """Return list of meaningful (or all if debug) parentrevs for rev.
991
997
992 For merges (two non-nullrev revisions) both parents are meaningful.
998 For merges (two non-nullrev revisions) both parents are meaningful.
993 Otherwise the first parent revision is considered meaningful if it
999 Otherwise the first parent revision is considered meaningful if it
994 is not the preceding revision.
1000 is not the preceding revision.
995 """
1001 """
996 parents = log.parentrevs(rev)
1002 parents = log.parentrevs(rev)
997 if not self.ui.debugflag and parents[1] == nullrev:
1003 if not self.ui.debugflag and parents[1] == nullrev:
998 if parents[0] >= rev - 1:
1004 if parents[0] >= rev - 1:
999 parents = []
1005 parents = []
1000 else:
1006 else:
1001 parents = [parents[0]]
1007 parents = [parents[0]]
1002 return parents
1008 return parents
1003
1009
1004
1010
1005 class changeset_templater(changeset_printer):
1011 class changeset_templater(changeset_printer):
1006 '''format changeset information.'''
1012 '''format changeset information.'''
1007
1013
1008 def __init__(self, ui, repo, patch, diffopts, tmpl, mapfile, buffered):
1014 def __init__(self, ui, repo, patch, diffopts, tmpl, mapfile, buffered):
1009 changeset_printer.__init__(self, ui, repo, patch, diffopts, buffered)
1015 changeset_printer.__init__(self, ui, repo, patch, diffopts, buffered)
1010 formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12])
1016 formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12])
1011 defaulttempl = {
1017 defaulttempl = {
1012 'parent': '{rev}:{node|formatnode} ',
1018 'parent': '{rev}:{node|formatnode} ',
1013 'manifest': '{rev}:{node|formatnode}',
1019 'manifest': '{rev}:{node|formatnode}',
1014 'file_copy': '{name} ({source})',
1020 'file_copy': '{name} ({source})',
1015 'extra': '{key}={value|stringescape}'
1021 'extra': '{key}={value|stringescape}'
1016 }
1022 }
1017 # filecopy is preserved for compatibility reasons
1023 # filecopy is preserved for compatibility reasons
1018 defaulttempl['filecopy'] = defaulttempl['file_copy']
1024 defaulttempl['filecopy'] = defaulttempl['file_copy']
1019 self.t = templater.templater(mapfile, {'formatnode': formatnode},
1025 self.t = templater.templater(mapfile, {'formatnode': formatnode},
1020 cache=defaulttempl)
1026 cache=defaulttempl)
1021 if tmpl:
1027 if tmpl:
1022 self.t.cache['changeset'] = tmpl
1028 self.t.cache['changeset'] = tmpl
1023
1029
1024 self.cache = {}
1030 self.cache = {}
1025
1031
1026 def _meaningful_parentrevs(self, ctx):
1032 def _meaningful_parentrevs(self, ctx):
1027 """Return list of meaningful (or all if debug) parentrevs for rev.
1033 """Return list of meaningful (or all if debug) parentrevs for rev.
1028 """
1034 """
1029 parents = ctx.parents()
1035 parents = ctx.parents()
1030 if len(parents) > 1:
1036 if len(parents) > 1:
1031 return parents
1037 return parents
1032 if self.ui.debugflag:
1038 if self.ui.debugflag:
1033 return [parents[0], self.repo['null']]
1039 return [parents[0], self.repo['null']]
1034 if parents[0].rev() >= ctx.rev() - 1:
1040 if parents[0].rev() >= ctx.rev() - 1:
1035 return []
1041 return []
1036 return parents
1042 return parents
1037
1043
1038 def _show(self, ctx, copies, matchfn, props):
1044 def _show(self, ctx, copies, matchfn, props):
1039 '''show a single changeset or file revision'''
1045 '''show a single changeset or file revision'''
1040
1046
1041 showlist = templatekw.showlist
1047 showlist = templatekw.showlist
1042
1048
1043 # showparents() behaviour depends on ui trace level which
1049 # showparents() behaviour depends on ui trace level which
1044 # causes unexpected behaviours at templating level and makes
1050 # causes unexpected behaviours at templating level and makes
1045 # it harder to extract it in a standalone function. Its
1051 # it harder to extract it in a standalone function. Its
1046 # behaviour cannot be changed so leave it here for now.
1052 # behaviour cannot be changed so leave it here for now.
1047 def showparents(**args):
1053 def showparents(**args):
1048 ctx = args['ctx']
1054 ctx = args['ctx']
1049 parents = [[('rev', p.rev()), ('node', p.hex())]
1055 parents = [[('rev', p.rev()), ('node', p.hex())]
1050 for p in self._meaningful_parentrevs(ctx)]
1056 for p in self._meaningful_parentrevs(ctx)]
1051 return showlist('parent', parents, **args)
1057 return showlist('parent', parents, **args)
1052
1058
1053 props = props.copy()
1059 props = props.copy()
1054 props.update(templatekw.keywords)
1060 props.update(templatekw.keywords)
1055 props['parents'] = showparents
1061 props['parents'] = showparents
1056 props['templ'] = self.t
1062 props['templ'] = self.t
1057 props['ctx'] = ctx
1063 props['ctx'] = ctx
1058 props['repo'] = self.repo
1064 props['repo'] = self.repo
1059 props['revcache'] = {'copies': copies}
1065 props['revcache'] = {'copies': copies}
1060 props['cache'] = self.cache
1066 props['cache'] = self.cache
1061
1067
1062 # find correct templates for current mode
1068 # find correct templates for current mode
1063
1069
1064 tmplmodes = [
1070 tmplmodes = [
1065 (True, None),
1071 (True, None),
1066 (self.ui.verbose, 'verbose'),
1072 (self.ui.verbose, 'verbose'),
1067 (self.ui.quiet, 'quiet'),
1073 (self.ui.quiet, 'quiet'),
1068 (self.ui.debugflag, 'debug'),
1074 (self.ui.debugflag, 'debug'),
1069 ]
1075 ]
1070
1076
1071 types = {'header': '', 'footer':'', 'changeset': 'changeset'}
1077 types = {'header': '', 'footer':'', 'changeset': 'changeset'}
1072 for mode, postfix in tmplmodes:
1078 for mode, postfix in tmplmodes:
1073 for type in types:
1079 for type in types:
1074 cur = postfix and ('%s_%s' % (type, postfix)) or type
1080 cur = postfix and ('%s_%s' % (type, postfix)) or type
1075 if mode and cur in self.t:
1081 if mode and cur in self.t:
1076 types[type] = cur
1082 types[type] = cur
1077
1083
1078 try:
1084 try:
1079
1085
1080 # write header
1086 # write header
1081 if types['header']:
1087 if types['header']:
1082 h = templater.stringify(self.t(types['header'], **props))
1088 h = templater.stringify(self.t(types['header'], **props))
1083 if self.buffered:
1089 if self.buffered:
1084 self.header[ctx.rev()] = h
1090 self.header[ctx.rev()] = h
1085 else:
1091 else:
1086 if self.lastheader != h:
1092 if self.lastheader != h:
1087 self.lastheader = h
1093 self.lastheader = h
1088 self.ui.write(h)
1094 self.ui.write(h)
1089
1095
1090 # write changeset metadata, then patch if requested
1096 # write changeset metadata, then patch if requested
1091 key = types['changeset']
1097 key = types['changeset']
1092 self.ui.write(templater.stringify(self.t(key, **props)))
1098 self.ui.write(templater.stringify(self.t(key, **props)))
1093 self.showpatch(ctx.node(), matchfn)
1099 self.showpatch(ctx.node(), matchfn)
1094
1100
1095 if types['footer']:
1101 if types['footer']:
1096 if not self.footer:
1102 if not self.footer:
1097 self.footer = templater.stringify(self.t(types['footer'],
1103 self.footer = templater.stringify(self.t(types['footer'],
1098 **props))
1104 **props))
1099
1105
1100 except KeyError, inst:
1106 except KeyError, inst:
1101 msg = _("%s: no key named '%s'")
1107 msg = _("%s: no key named '%s'")
1102 raise util.Abort(msg % (self.t.mapfile, inst.args[0]))
1108 raise util.Abort(msg % (self.t.mapfile, inst.args[0]))
1103 except SyntaxError, inst:
1109 except SyntaxError, inst:
1104 raise util.Abort('%s: %s' % (self.t.mapfile, inst.args[0]))
1110 raise util.Abort('%s: %s' % (self.t.mapfile, inst.args[0]))
1105
1111
1106 def gettemplate(ui, tmpl, style):
1112 def gettemplate(ui, tmpl, style):
1107 """
1113 """
1108 Find the template matching the given template spec or style.
1114 Find the template matching the given template spec or style.
1109 """
1115 """
1110
1116
1111 # ui settings
1117 # ui settings
1112 if not tmpl and not style:
1118 if not tmpl and not style:
1113 tmpl = ui.config('ui', 'logtemplate')
1119 tmpl = ui.config('ui', 'logtemplate')
1114 if tmpl:
1120 if tmpl:
1115 try:
1121 try:
1116 tmpl = templater.parsestring(tmpl)
1122 tmpl = templater.parsestring(tmpl)
1117 except SyntaxError:
1123 except SyntaxError:
1118 tmpl = templater.parsestring(tmpl, quoted=False)
1124 tmpl = templater.parsestring(tmpl, quoted=False)
1119 return tmpl, None
1125 return tmpl, None
1120 else:
1126 else:
1121 style = util.expandpath(ui.config('ui', 'style', ''))
1127 style = util.expandpath(ui.config('ui', 'style', ''))
1122
1128
1123 if style:
1129 if style:
1124 mapfile = style
1130 mapfile = style
1125 if not os.path.split(mapfile)[0]:
1131 if not os.path.split(mapfile)[0]:
1126 mapname = (templater.templatepath('map-cmdline.' + mapfile)
1132 mapname = (templater.templatepath('map-cmdline.' + mapfile)
1127 or templater.templatepath(mapfile))
1133 or templater.templatepath(mapfile))
1128 if mapname:
1134 if mapname:
1129 mapfile = mapname
1135 mapfile = mapname
1130 return None, mapfile
1136 return None, mapfile
1131
1137
1132 if not tmpl:
1138 if not tmpl:
1133 return None, None
1139 return None, None
1134
1140
1135 # looks like a literal template?
1141 # looks like a literal template?
1136 if '{' in tmpl:
1142 if '{' in tmpl:
1137 return tmpl, None
1143 return tmpl, None
1138
1144
1139 # perhaps a stock style?
1145 # perhaps a stock style?
1140 if not os.path.split(tmpl)[0]:
1146 if not os.path.split(tmpl)[0]:
1141 mapname = (templater.templatepath('map-cmdline.' + tmpl)
1147 mapname = (templater.templatepath('map-cmdline.' + tmpl)
1142 or templater.templatepath(tmpl))
1148 or templater.templatepath(tmpl))
1143 if mapname and os.path.isfile(mapname):
1149 if mapname and os.path.isfile(mapname):
1144 return None, mapname
1150 return None, mapname
1145
1151
1146 # perhaps it's a reference to [templates]
1152 # perhaps it's a reference to [templates]
1147 t = ui.config('templates', tmpl)
1153 t = ui.config('templates', tmpl)
1148 if t:
1154 if t:
1149 try:
1155 try:
1150 tmpl = templater.parsestring(t)
1156 tmpl = templater.parsestring(t)
1151 except SyntaxError:
1157 except SyntaxError:
1152 tmpl = templater.parsestring(t, quoted=False)
1158 tmpl = templater.parsestring(t, quoted=False)
1153 return tmpl, None
1159 return tmpl, None
1154
1160
1155 if tmpl == 'list':
1161 if tmpl == 'list':
1156 ui.write(_("available styles: %s\n") % templater.stylelist())
1162 ui.write(_("available styles: %s\n") % templater.stylelist())
1157 raise util.Abort(_("specify a template"))
1163 raise util.Abort(_("specify a template"))
1158
1164
1159 # perhaps it's a path to a map or a template
1165 # perhaps it's a path to a map or a template
1160 if ('/' in tmpl or '\\' in tmpl) and os.path.isfile(tmpl):
1166 if ('/' in tmpl or '\\' in tmpl) and os.path.isfile(tmpl):
1161 # is it a mapfile for a style?
1167 # is it a mapfile for a style?
1162 if os.path.basename(tmpl).startswith("map-"):
1168 if os.path.basename(tmpl).startswith("map-"):
1163 return None, os.path.realpath(tmpl)
1169 return None, os.path.realpath(tmpl)
1164 tmpl = open(tmpl).read()
1170 tmpl = open(tmpl).read()
1165 return tmpl, None
1171 return tmpl, None
1166
1172
1167 # constant string?
1173 # constant string?
1168 return tmpl, None
1174 return tmpl, None
1169
1175
1170 def show_changeset(ui, repo, opts, buffered=False):
1176 def show_changeset(ui, repo, opts, buffered=False):
1171 """show one changeset using template or regular display.
1177 """show one changeset using template or regular display.
1172
1178
1173 Display format will be the first non-empty hit of:
1179 Display format will be the first non-empty hit of:
1174 1. option 'template'
1180 1. option 'template'
1175 2. option 'style'
1181 2. option 'style'
1176 3. [ui] setting 'logtemplate'
1182 3. [ui] setting 'logtemplate'
1177 4. [ui] setting 'style'
1183 4. [ui] setting 'style'
1178 If all of these values are either the unset or the empty string,
1184 If all of these values are either the unset or the empty string,
1179 regular display via changeset_printer() is done.
1185 regular display via changeset_printer() is done.
1180 """
1186 """
1181 # options
1187 # options
1182 patch = None
1188 patch = None
1183 if opts.get('patch') or opts.get('stat'):
1189 if opts.get('patch') or opts.get('stat'):
1184 patch = scmutil.matchall(repo)
1190 patch = scmutil.matchall(repo)
1185
1191
1186 tmpl, mapfile = gettemplate(ui, opts.get('template'), opts.get('style'))
1192 tmpl, mapfile = gettemplate(ui, opts.get('template'), opts.get('style'))
1187
1193
1188 if not tmpl and not mapfile:
1194 if not tmpl and not mapfile:
1189 return changeset_printer(ui, repo, patch, opts, buffered)
1195 return changeset_printer(ui, repo, patch, opts, buffered)
1190
1196
1191 try:
1197 try:
1192 t = changeset_templater(ui, repo, patch, opts, tmpl, mapfile, buffered)
1198 t = changeset_templater(ui, repo, patch, opts, tmpl, mapfile, buffered)
1193 except SyntaxError, inst:
1199 except SyntaxError, inst:
1194 raise util.Abort(inst.args[0])
1200 raise util.Abort(inst.args[0])
1195 return t
1201 return t
1196
1202
1197 def showmarker(ui, marker):
1203 def showmarker(ui, marker):
1198 """utility function to display obsolescence marker in a readable way
1204 """utility function to display obsolescence marker in a readable way
1199
1205
1200 To be used by debug function."""
1206 To be used by debug function."""
1201 ui.write(hex(marker.precnode()))
1207 ui.write(hex(marker.precnode()))
1202 for repl in marker.succnodes():
1208 for repl in marker.succnodes():
1203 ui.write(' ')
1209 ui.write(' ')
1204 ui.write(hex(repl))
1210 ui.write(hex(repl))
1205 ui.write(' %X ' % marker.flags())
1211 ui.write(' %X ' % marker.flags())
1206 parents = marker.parentnodes()
1212 parents = marker.parentnodes()
1207 if parents is not None:
1213 if parents is not None:
1208 ui.write('{%s} ' % ', '.join(hex(p) for p in parents))
1214 ui.write('{%s} ' % ', '.join(hex(p) for p in parents))
1209 ui.write('(%s) ' % util.datestr(marker.date()))
1215 ui.write('(%s) ' % util.datestr(marker.date()))
1210 ui.write('{%s}' % (', '.join('%r: %r' % t for t in
1216 ui.write('{%s}' % (', '.join('%r: %r' % t for t in
1211 sorted(marker.metadata().items())
1217 sorted(marker.metadata().items())
1212 if t[0] != 'date')))
1218 if t[0] != 'date')))
1213 ui.write('\n')
1219 ui.write('\n')
1214
1220
1215 def finddate(ui, repo, date):
1221 def finddate(ui, repo, date):
1216 """Find the tipmost changeset that matches the given date spec"""
1222 """Find the tipmost changeset that matches the given date spec"""
1217
1223
1218 df = util.matchdate(date)
1224 df = util.matchdate(date)
1219 m = scmutil.matchall(repo)
1225 m = scmutil.matchall(repo)
1220 results = {}
1226 results = {}
1221
1227
1222 def prep(ctx, fns):
1228 def prep(ctx, fns):
1223 d = ctx.date()
1229 d = ctx.date()
1224 if df(d[0]):
1230 if df(d[0]):
1225 results[ctx.rev()] = d
1231 results[ctx.rev()] = d
1226
1232
1227 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
1233 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
1228 rev = ctx.rev()
1234 rev = ctx.rev()
1229 if rev in results:
1235 if rev in results:
1230 ui.status(_("found revision %s from %s\n") %
1236 ui.status(_("found revision %s from %s\n") %
1231 (rev, util.datestr(results[rev])))
1237 (rev, util.datestr(results[rev])))
1232 return str(rev)
1238 return str(rev)
1233
1239
1234 raise util.Abort(_("revision matching date not found"))
1240 raise util.Abort(_("revision matching date not found"))
1235
1241
1236 def increasingwindows(windowsize=8, sizelimit=512):
1242 def increasingwindows(windowsize=8, sizelimit=512):
1237 while True:
1243 while True:
1238 yield windowsize
1244 yield windowsize
1239 if windowsize < sizelimit:
1245 if windowsize < sizelimit:
1240 windowsize *= 2
1246 windowsize *= 2
1241
1247
1242 class FileWalkError(Exception):
1248 class FileWalkError(Exception):
1243 pass
1249 pass
1244
1250
1245 def walkfilerevs(repo, match, follow, revs, fncache):
1251 def walkfilerevs(repo, match, follow, revs, fncache):
1246 '''Walks the file history for the matched files.
1252 '''Walks the file history for the matched files.
1247
1253
1248 Returns the changeset revs that are involved in the file history.
1254 Returns the changeset revs that are involved in the file history.
1249
1255
1250 Throws FileWalkError if the file history can't be walked using
1256 Throws FileWalkError if the file history can't be walked using
1251 filelogs alone.
1257 filelogs alone.
1252 '''
1258 '''
1253 wanted = set()
1259 wanted = set()
1254 copies = []
1260 copies = []
1255 minrev, maxrev = min(revs), max(revs)
1261 minrev, maxrev = min(revs), max(revs)
1256 def filerevgen(filelog, last):
1262 def filerevgen(filelog, last):
1257 """
1263 """
1258 Only files, no patterns. Check the history of each file.
1264 Only files, no patterns. Check the history of each file.
1259
1265
1260 Examines filelog entries within minrev, maxrev linkrev range
1266 Examines filelog entries within minrev, maxrev linkrev range
1261 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
1267 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
1262 tuples in backwards order
1268 tuples in backwards order
1263 """
1269 """
1264 cl_count = len(repo)
1270 cl_count = len(repo)
1265 revs = []
1271 revs = []
1266 for j in xrange(0, last + 1):
1272 for j in xrange(0, last + 1):
1267 linkrev = filelog.linkrev(j)
1273 linkrev = filelog.linkrev(j)
1268 if linkrev < minrev:
1274 if linkrev < minrev:
1269 continue
1275 continue
1270 # only yield rev for which we have the changelog, it can
1276 # only yield rev for which we have the changelog, it can
1271 # happen while doing "hg log" during a pull or commit
1277 # happen while doing "hg log" during a pull or commit
1272 if linkrev >= cl_count:
1278 if linkrev >= cl_count:
1273 break
1279 break
1274
1280
1275 parentlinkrevs = []
1281 parentlinkrevs = []
1276 for p in filelog.parentrevs(j):
1282 for p in filelog.parentrevs(j):
1277 if p != nullrev:
1283 if p != nullrev:
1278 parentlinkrevs.append(filelog.linkrev(p))
1284 parentlinkrevs.append(filelog.linkrev(p))
1279 n = filelog.node(j)
1285 n = filelog.node(j)
1280 revs.append((linkrev, parentlinkrevs,
1286 revs.append((linkrev, parentlinkrevs,
1281 follow and filelog.renamed(n)))
1287 follow and filelog.renamed(n)))
1282
1288
1283 return reversed(revs)
1289 return reversed(revs)
1284 def iterfiles():
1290 def iterfiles():
1285 pctx = repo['.']
1291 pctx = repo['.']
1286 for filename in match.files():
1292 for filename in match.files():
1287 if follow:
1293 if follow:
1288 if filename not in pctx:
1294 if filename not in pctx:
1289 raise util.Abort(_('cannot follow file not in parent '
1295 raise util.Abort(_('cannot follow file not in parent '
1290 'revision: "%s"') % filename)
1296 'revision: "%s"') % filename)
1291 yield filename, pctx[filename].filenode()
1297 yield filename, pctx[filename].filenode()
1292 else:
1298 else:
1293 yield filename, None
1299 yield filename, None
1294 for filename_node in copies:
1300 for filename_node in copies:
1295 yield filename_node
1301 yield filename_node
1296
1302
1297 for file_, node in iterfiles():
1303 for file_, node in iterfiles():
1298 filelog = repo.file(file_)
1304 filelog = repo.file(file_)
1299 if not len(filelog):
1305 if not len(filelog):
1300 if node is None:
1306 if node is None:
1301 # A zero count may be a directory or deleted file, so
1307 # A zero count may be a directory or deleted file, so
1302 # try to find matching entries on the slow path.
1308 # try to find matching entries on the slow path.
1303 if follow:
1309 if follow:
1304 raise util.Abort(
1310 raise util.Abort(
1305 _('cannot follow nonexistent file: "%s"') % file_)
1311 _('cannot follow nonexistent file: "%s"') % file_)
1306 raise FileWalkError("Cannot walk via filelog")
1312 raise FileWalkError("Cannot walk via filelog")
1307 else:
1313 else:
1308 continue
1314 continue
1309
1315
1310 if node is None:
1316 if node is None:
1311 last = len(filelog) - 1
1317 last = len(filelog) - 1
1312 else:
1318 else:
1313 last = filelog.rev(node)
1319 last = filelog.rev(node)
1314
1320
1315
1321
1316 # keep track of all ancestors of the file
1322 # keep track of all ancestors of the file
1317 ancestors = set([filelog.linkrev(last)])
1323 ancestors = set([filelog.linkrev(last)])
1318
1324
1319 # iterate from latest to oldest revision
1325 # iterate from latest to oldest revision
1320 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
1326 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
1321 if not follow:
1327 if not follow:
1322 if rev > maxrev:
1328 if rev > maxrev:
1323 continue
1329 continue
1324 else:
1330 else:
1325 # Note that last might not be the first interesting
1331 # Note that last might not be the first interesting
1326 # rev to us:
1332 # rev to us:
1327 # if the file has been changed after maxrev, we'll
1333 # if the file has been changed after maxrev, we'll
1328 # have linkrev(last) > maxrev, and we still need
1334 # have linkrev(last) > maxrev, and we still need
1329 # to explore the file graph
1335 # to explore the file graph
1330 if rev not in ancestors:
1336 if rev not in ancestors:
1331 continue
1337 continue
1332 # XXX insert 1327 fix here
1338 # XXX insert 1327 fix here
1333 if flparentlinkrevs:
1339 if flparentlinkrevs:
1334 ancestors.update(flparentlinkrevs)
1340 ancestors.update(flparentlinkrevs)
1335
1341
1336 fncache.setdefault(rev, []).append(file_)
1342 fncache.setdefault(rev, []).append(file_)
1337 wanted.add(rev)
1343 wanted.add(rev)
1338 if copied:
1344 if copied:
1339 copies.append(copied)
1345 copies.append(copied)
1340
1346
1341 return wanted
1347 return wanted
1342
1348
1343 def walkchangerevs(repo, match, opts, prepare):
1349 def walkchangerevs(repo, match, opts, prepare):
1344 '''Iterate over files and the revs in which they changed.
1350 '''Iterate over files and the revs in which they changed.
1345
1351
1346 Callers most commonly need to iterate backwards over the history
1352 Callers most commonly need to iterate backwards over the history
1347 in which they are interested. Doing so has awful (quadratic-looking)
1353 in which they are interested. Doing so has awful (quadratic-looking)
1348 performance, so we use iterators in a "windowed" way.
1354 performance, so we use iterators in a "windowed" way.
1349
1355
1350 We walk a window of revisions in the desired order. Within the
1356 We walk a window of revisions in the desired order. Within the
1351 window, we first walk forwards to gather data, then in the desired
1357 window, we first walk forwards to gather data, then in the desired
1352 order (usually backwards) to display it.
1358 order (usually backwards) to display it.
1353
1359
1354 This function returns an iterator yielding contexts. Before
1360 This function returns an iterator yielding contexts. Before
1355 yielding each context, the iterator will first call the prepare
1361 yielding each context, the iterator will first call the prepare
1356 function on each context in the window in forward order.'''
1362 function on each context in the window in forward order.'''
1357
1363
1358 follow = opts.get('follow') or opts.get('follow_first')
1364 follow = opts.get('follow') or opts.get('follow_first')
1359
1365
1360 if opts.get('rev'):
1366 if opts.get('rev'):
1361 revs = scmutil.revrange(repo, opts.get('rev'))
1367 revs = scmutil.revrange(repo, opts.get('rev'))
1362 elif follow:
1368 elif follow:
1363 revs = repo.revs('reverse(:.)')
1369 revs = repo.revs('reverse(:.)')
1364 else:
1370 else:
1365 revs = revset.spanset(repo)
1371 revs = revset.spanset(repo)
1366 revs.reverse()
1372 revs.reverse()
1367 if not revs:
1373 if not revs:
1368 return []
1374 return []
1369 wanted = set()
1375 wanted = set()
1370 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1376 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1371 fncache = {}
1377 fncache = {}
1372 change = repo.changectx
1378 change = repo.changectx
1373
1379
1374 # First step is to fill wanted, the set of revisions that we want to yield.
1380 # First step is to fill wanted, the set of revisions that we want to yield.
1375 # When it does not induce extra cost, we also fill fncache for revisions in
1381 # When it does not induce extra cost, we also fill fncache for revisions in
1376 # wanted: a cache of filenames that were changed (ctx.files()) and that
1382 # wanted: a cache of filenames that were changed (ctx.files()) and that
1377 # match the file filtering conditions.
1383 # match the file filtering conditions.
1378
1384
1379 if not slowpath and not match.files():
1385 if not slowpath and not match.files():
1380 # No files, no patterns. Display all revs.
1386 # No files, no patterns. Display all revs.
1381 wanted = revs
1387 wanted = revs
1382
1388
1383 if not slowpath and match.files():
1389 if not slowpath and match.files():
1384 # We only have to read through the filelog to find wanted revisions
1390 # We only have to read through the filelog to find wanted revisions
1385
1391
1386 try:
1392 try:
1387 wanted = walkfilerevs(repo, match, follow, revs, fncache)
1393 wanted = walkfilerevs(repo, match, follow, revs, fncache)
1388 except FileWalkError:
1394 except FileWalkError:
1389 slowpath = True
1395 slowpath = True
1390
1396
1391 # We decided to fall back to the slowpath because at least one
1397 # We decided to fall back to the slowpath because at least one
1392 # of the paths was not a file. Check to see if at least one of them
1398 # of the paths was not a file. Check to see if at least one of them
1393 # existed in history, otherwise simply return
1399 # existed in history, otherwise simply return
1394 for path in match.files():
1400 for path in match.files():
1395 if path == '.' or path in repo.store:
1401 if path == '.' or path in repo.store:
1396 break
1402 break
1397 else:
1403 else:
1398 return []
1404 return []
1399
1405
1400 if slowpath:
1406 if slowpath:
1401 # We have to read the changelog to match filenames against
1407 # We have to read the changelog to match filenames against
1402 # changed files
1408 # changed files
1403
1409
1404 if follow:
1410 if follow:
1405 raise util.Abort(_('can only follow copies/renames for explicit '
1411 raise util.Abort(_('can only follow copies/renames for explicit '
1406 'filenames'))
1412 'filenames'))
1407
1413
1408 # The slow path checks files modified in every changeset.
1414 # The slow path checks files modified in every changeset.
1409 # This is really slow on large repos, so compute the set lazily.
1415 # This is really slow on large repos, so compute the set lazily.
1410 class lazywantedset(object):
1416 class lazywantedset(object):
1411 def __init__(self):
1417 def __init__(self):
1412 self.set = set()
1418 self.set = set()
1413 self.revs = set(revs)
1419 self.revs = set(revs)
1414
1420
1415 # No need to worry about locality here because it will be accessed
1421 # No need to worry about locality here because it will be accessed
1416 # in the same order as the increasing window below.
1422 # in the same order as the increasing window below.
1417 def __contains__(self, value):
1423 def __contains__(self, value):
1418 if value in self.set:
1424 if value in self.set:
1419 return True
1425 return True
1420 elif not value in self.revs:
1426 elif not value in self.revs:
1421 return False
1427 return False
1422 else:
1428 else:
1423 self.revs.discard(value)
1429 self.revs.discard(value)
1424 ctx = change(value)
1430 ctx = change(value)
1425 matches = filter(match, ctx.files())
1431 matches = filter(match, ctx.files())
1426 if matches:
1432 if matches:
1427 fncache[value] = matches
1433 fncache[value] = matches
1428 self.set.add(value)
1434 self.set.add(value)
1429 return True
1435 return True
1430 return False
1436 return False
1431
1437
1432 def discard(self, value):
1438 def discard(self, value):
1433 self.revs.discard(value)
1439 self.revs.discard(value)
1434 self.set.discard(value)
1440 self.set.discard(value)
1435
1441
1436 wanted = lazywantedset()
1442 wanted = lazywantedset()
1437
1443
1438 class followfilter(object):
1444 class followfilter(object):
1439 def __init__(self, onlyfirst=False):
1445 def __init__(self, onlyfirst=False):
1440 self.startrev = nullrev
1446 self.startrev = nullrev
1441 self.roots = set()
1447 self.roots = set()
1442 self.onlyfirst = onlyfirst
1448 self.onlyfirst = onlyfirst
1443
1449
1444 def match(self, rev):
1450 def match(self, rev):
1445 def realparents(rev):
1451 def realparents(rev):
1446 if self.onlyfirst:
1452 if self.onlyfirst:
1447 return repo.changelog.parentrevs(rev)[0:1]
1453 return repo.changelog.parentrevs(rev)[0:1]
1448 else:
1454 else:
1449 return filter(lambda x: x != nullrev,
1455 return filter(lambda x: x != nullrev,
1450 repo.changelog.parentrevs(rev))
1456 repo.changelog.parentrevs(rev))
1451
1457
1452 if self.startrev == nullrev:
1458 if self.startrev == nullrev:
1453 self.startrev = rev
1459 self.startrev = rev
1454 return True
1460 return True
1455
1461
1456 if rev > self.startrev:
1462 if rev > self.startrev:
1457 # forward: all descendants
1463 # forward: all descendants
1458 if not self.roots:
1464 if not self.roots:
1459 self.roots.add(self.startrev)
1465 self.roots.add(self.startrev)
1460 for parent in realparents(rev):
1466 for parent in realparents(rev):
1461 if parent in self.roots:
1467 if parent in self.roots:
1462 self.roots.add(rev)
1468 self.roots.add(rev)
1463 return True
1469 return True
1464 else:
1470 else:
1465 # backwards: all parents
1471 # backwards: all parents
1466 if not self.roots:
1472 if not self.roots:
1467 self.roots.update(realparents(self.startrev))
1473 self.roots.update(realparents(self.startrev))
1468 if rev in self.roots:
1474 if rev in self.roots:
1469 self.roots.remove(rev)
1475 self.roots.remove(rev)
1470 self.roots.update(realparents(rev))
1476 self.roots.update(realparents(rev))
1471 return True
1477 return True
1472
1478
1473 return False
1479 return False
1474
1480
1475 # it might be worthwhile to do this in the iterator if the rev range
1481 # it might be worthwhile to do this in the iterator if the rev range
1476 # is descending and the prune args are all within that range
1482 # is descending and the prune args are all within that range
1477 for rev in opts.get('prune', ()):
1483 for rev in opts.get('prune', ()):
1478 rev = repo[rev].rev()
1484 rev = repo[rev].rev()
1479 ff = followfilter()
1485 ff = followfilter()
1480 stop = min(revs[0], revs[-1])
1486 stop = min(revs[0], revs[-1])
1481 for x in xrange(rev, stop - 1, -1):
1487 for x in xrange(rev, stop - 1, -1):
1482 if ff.match(x):
1488 if ff.match(x):
1483 wanted = wanted - [x]
1489 wanted = wanted - [x]
1484
1490
1485 # Now that wanted is correctly initialized, we can iterate over the
1491 # Now that wanted is correctly initialized, we can iterate over the
1486 # revision range, yielding only revisions in wanted.
1492 # revision range, yielding only revisions in wanted.
1487 def iterate():
1493 def iterate():
1488 if follow and not match.files():
1494 if follow and not match.files():
1489 ff = followfilter(onlyfirst=opts.get('follow_first'))
1495 ff = followfilter(onlyfirst=opts.get('follow_first'))
1490 def want(rev):
1496 def want(rev):
1491 return ff.match(rev) and rev in wanted
1497 return ff.match(rev) and rev in wanted
1492 else:
1498 else:
1493 def want(rev):
1499 def want(rev):
1494 return rev in wanted
1500 return rev in wanted
1495
1501
1496 it = iter(revs)
1502 it = iter(revs)
1497 stopiteration = False
1503 stopiteration = False
1498 for windowsize in increasingwindows():
1504 for windowsize in increasingwindows():
1499 nrevs = []
1505 nrevs = []
1500 for i in xrange(windowsize):
1506 for i in xrange(windowsize):
1501 try:
1507 try:
1502 rev = it.next()
1508 rev = it.next()
1503 if want(rev):
1509 if want(rev):
1504 nrevs.append(rev)
1510 nrevs.append(rev)
1505 except (StopIteration):
1511 except (StopIteration):
1506 stopiteration = True
1512 stopiteration = True
1507 break
1513 break
1508 for rev in sorted(nrevs):
1514 for rev in sorted(nrevs):
1509 fns = fncache.get(rev)
1515 fns = fncache.get(rev)
1510 ctx = change(rev)
1516 ctx = change(rev)
1511 if not fns:
1517 if not fns:
1512 def fns_generator():
1518 def fns_generator():
1513 for f in ctx.files():
1519 for f in ctx.files():
1514 if match(f):
1520 if match(f):
1515 yield f
1521 yield f
1516 fns = fns_generator()
1522 fns = fns_generator()
1517 prepare(ctx, fns)
1523 prepare(ctx, fns)
1518 for rev in nrevs:
1524 for rev in nrevs:
1519 yield change(rev)
1525 yield change(rev)
1520
1526
1521 if stopiteration:
1527 if stopiteration:
1522 break
1528 break
1523
1529
1524 return iterate()
1530 return iterate()
1525
1531
1526 def _makefollowlogfilematcher(repo, files, followfirst):
1532 def _makefollowlogfilematcher(repo, files, followfirst):
1527 # When displaying a revision with --patch --follow FILE, we have
1533 # When displaying a revision with --patch --follow FILE, we have
1528 # to know which file of the revision must be diffed. With
1534 # to know which file of the revision must be diffed. With
1529 # --follow, we want the names of the ancestors of FILE in the
1535 # --follow, we want the names of the ancestors of FILE in the
1530 # revision, stored in "fcache". "fcache" is populated by
1536 # revision, stored in "fcache". "fcache" is populated by
1531 # reproducing the graph traversal already done by --follow revset
1537 # reproducing the graph traversal already done by --follow revset
1532 # and relating linkrevs to file names (which is not "correct" but
1538 # and relating linkrevs to file names (which is not "correct" but
1533 # good enough).
1539 # good enough).
1534 fcache = {}
1540 fcache = {}
1535 fcacheready = [False]
1541 fcacheready = [False]
1536 pctx = repo['.']
1542 pctx = repo['.']
1537
1543
1538 def populate():
1544 def populate():
1539 for fn in files:
1545 for fn in files:
1540 for i in ((pctx[fn],), pctx[fn].ancestors(followfirst=followfirst)):
1546 for i in ((pctx[fn],), pctx[fn].ancestors(followfirst=followfirst)):
1541 for c in i:
1547 for c in i:
1542 fcache.setdefault(c.linkrev(), set()).add(c.path())
1548 fcache.setdefault(c.linkrev(), set()).add(c.path())
1543
1549
1544 def filematcher(rev):
1550 def filematcher(rev):
1545 if not fcacheready[0]:
1551 if not fcacheready[0]:
1546 # Lazy initialization
1552 # Lazy initialization
1547 fcacheready[0] = True
1553 fcacheready[0] = True
1548 populate()
1554 populate()
1549 return scmutil.matchfiles(repo, fcache.get(rev, []))
1555 return scmutil.matchfiles(repo, fcache.get(rev, []))
1550
1556
1551 return filematcher
1557 return filematcher
1552
1558
1553 def _makenofollowlogfilematcher(repo, pats, opts):
1559 def _makenofollowlogfilematcher(repo, pats, opts):
1554 '''hook for extensions to override the filematcher for non-follow cases'''
1560 '''hook for extensions to override the filematcher for non-follow cases'''
1555 return None
1561 return None
1556
1562
1557 def _makelogrevset(repo, pats, opts, revs):
1563 def _makelogrevset(repo, pats, opts, revs):
1558 """Return (expr, filematcher) where expr is a revset string built
1564 """Return (expr, filematcher) where expr is a revset string built
1559 from log options and file patterns or None. If --stat or --patch
1565 from log options and file patterns or None. If --stat or --patch
1560 are not passed filematcher is None. Otherwise it is a callable
1566 are not passed filematcher is None. Otherwise it is a callable
1561 taking a revision number and returning a match objects filtering
1567 taking a revision number and returning a match objects filtering
1562 the files to be detailed when displaying the revision.
1568 the files to be detailed when displaying the revision.
1563 """
1569 """
1564 opt2revset = {
1570 opt2revset = {
1565 'no_merges': ('not merge()', None),
1571 'no_merges': ('not merge()', None),
1566 'only_merges': ('merge()', None),
1572 'only_merges': ('merge()', None),
1567 '_ancestors': ('ancestors(%(val)s)', None),
1573 '_ancestors': ('ancestors(%(val)s)', None),
1568 '_fancestors': ('_firstancestors(%(val)s)', None),
1574 '_fancestors': ('_firstancestors(%(val)s)', None),
1569 '_descendants': ('descendants(%(val)s)', None),
1575 '_descendants': ('descendants(%(val)s)', None),
1570 '_fdescendants': ('_firstdescendants(%(val)s)', None),
1576 '_fdescendants': ('_firstdescendants(%(val)s)', None),
1571 '_matchfiles': ('_matchfiles(%(val)s)', None),
1577 '_matchfiles': ('_matchfiles(%(val)s)', None),
1572 'date': ('date(%(val)r)', None),
1578 'date': ('date(%(val)r)', None),
1573 'branch': ('branch(%(val)r)', ' or '),
1579 'branch': ('branch(%(val)r)', ' or '),
1574 '_patslog': ('filelog(%(val)r)', ' or '),
1580 '_patslog': ('filelog(%(val)r)', ' or '),
1575 '_patsfollow': ('follow(%(val)r)', ' or '),
1581 '_patsfollow': ('follow(%(val)r)', ' or '),
1576 '_patsfollowfirst': ('_followfirst(%(val)r)', ' or '),
1582 '_patsfollowfirst': ('_followfirst(%(val)r)', ' or '),
1577 'keyword': ('keyword(%(val)r)', ' or '),
1583 'keyword': ('keyword(%(val)r)', ' or '),
1578 'prune': ('not (%(val)r or ancestors(%(val)r))', ' and '),
1584 'prune': ('not (%(val)r or ancestors(%(val)r))', ' and '),
1579 'user': ('user(%(val)r)', ' or '),
1585 'user': ('user(%(val)r)', ' or '),
1580 }
1586 }
1581
1587
1582 opts = dict(opts)
1588 opts = dict(opts)
1583 # follow or not follow?
1589 # follow or not follow?
1584 follow = opts.get('follow') or opts.get('follow_first')
1590 follow = opts.get('follow') or opts.get('follow_first')
1585 followfirst = opts.get('follow_first') and 1 or 0
1591 followfirst = opts.get('follow_first') and 1 or 0
1586 # --follow with FILE behaviour depends on revs...
1592 # --follow with FILE behaviour depends on revs...
1587 it = iter(revs)
1593 it = iter(revs)
1588 startrev = it.next()
1594 startrev = it.next()
1589 try:
1595 try:
1590 followdescendants = startrev < it.next()
1596 followdescendants = startrev < it.next()
1591 except (StopIteration):
1597 except (StopIteration):
1592 followdescendants = False
1598 followdescendants = False
1593
1599
1594 # branch and only_branch are really aliases and must be handled at
1600 # branch and only_branch are really aliases and must be handled at
1595 # the same time
1601 # the same time
1596 opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
1602 opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
1597 opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']]
1603 opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']]
1598 # pats/include/exclude are passed to match.match() directly in
1604 # pats/include/exclude are passed to match.match() directly in
1599 # _matchfiles() revset but walkchangerevs() builds its matcher with
1605 # _matchfiles() revset but walkchangerevs() builds its matcher with
1600 # scmutil.match(). The difference is input pats are globbed on
1606 # scmutil.match(). The difference is input pats are globbed on
1601 # platforms without shell expansion (windows).
1607 # platforms without shell expansion (windows).
1602 pctx = repo[None]
1608 pctx = repo[None]
1603 match, pats = scmutil.matchandpats(pctx, pats, opts)
1609 match, pats = scmutil.matchandpats(pctx, pats, opts)
1604 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1610 slowpath = match.anypats() or (match.files() and opts.get('removed'))
1605 if not slowpath:
1611 if not slowpath:
1606 for f in match.files():
1612 for f in match.files():
1607 if follow and f not in pctx:
1613 if follow and f not in pctx:
1608 # If the file exists, it may be a directory, so let it
1614 # If the file exists, it may be a directory, so let it
1609 # take the slow path.
1615 # take the slow path.
1610 if os.path.exists(repo.wjoin(f)):
1616 if os.path.exists(repo.wjoin(f)):
1611 slowpath = True
1617 slowpath = True
1612 continue
1618 continue
1613 else:
1619 else:
1614 raise util.Abort(_('cannot follow file not in parent '
1620 raise util.Abort(_('cannot follow file not in parent '
1615 'revision: "%s"') % f)
1621 'revision: "%s"') % f)
1616 filelog = repo.file(f)
1622 filelog = repo.file(f)
1617 if not filelog:
1623 if not filelog:
1618 # A zero count may be a directory or deleted file, so
1624 # A zero count may be a directory or deleted file, so
1619 # try to find matching entries on the slow path.
1625 # try to find matching entries on the slow path.
1620 if follow:
1626 if follow:
1621 raise util.Abort(
1627 raise util.Abort(
1622 _('cannot follow nonexistent file: "%s"') % f)
1628 _('cannot follow nonexistent file: "%s"') % f)
1623 slowpath = True
1629 slowpath = True
1624
1630
1625 # We decided to fall back to the slowpath because at least one
1631 # We decided to fall back to the slowpath because at least one
1626 # of the paths was not a file. Check to see if at least one of them
1632 # of the paths was not a file. Check to see if at least one of them
1627 # existed in history - in that case, we'll continue down the
1633 # existed in history - in that case, we'll continue down the
1628 # slowpath; otherwise, we can turn off the slowpath
1634 # slowpath; otherwise, we can turn off the slowpath
1629 if slowpath:
1635 if slowpath:
1630 for path in match.files():
1636 for path in match.files():
1631 if path == '.' or path in repo.store:
1637 if path == '.' or path in repo.store:
1632 break
1638 break
1633 else:
1639 else:
1634 slowpath = False
1640 slowpath = False
1635
1641
1636 if slowpath:
1642 if slowpath:
1637 # See walkchangerevs() slow path.
1643 # See walkchangerevs() slow path.
1638 #
1644 #
1639 # pats/include/exclude cannot be represented as separate
1645 # pats/include/exclude cannot be represented as separate
1640 # revset expressions as their filtering logic applies at file
1646 # revset expressions as their filtering logic applies at file
1641 # level. For instance "-I a -X a" matches a revision touching
1647 # level. For instance "-I a -X a" matches a revision touching
1642 # "a" and "b" while "file(a) and not file(b)" does
1648 # "a" and "b" while "file(a) and not file(b)" does
1643 # not. Besides, filesets are evaluated against the working
1649 # not. Besides, filesets are evaluated against the working
1644 # directory.
1650 # directory.
1645 matchargs = ['r:', 'd:relpath']
1651 matchargs = ['r:', 'd:relpath']
1646 for p in pats:
1652 for p in pats:
1647 matchargs.append('p:' + p)
1653 matchargs.append('p:' + p)
1648 for p in opts.get('include', []):
1654 for p in opts.get('include', []):
1649 matchargs.append('i:' + p)
1655 matchargs.append('i:' + p)
1650 for p in opts.get('exclude', []):
1656 for p in opts.get('exclude', []):
1651 matchargs.append('x:' + p)
1657 matchargs.append('x:' + p)
1652 matchargs = ','.join(('%r' % p) for p in matchargs)
1658 matchargs = ','.join(('%r' % p) for p in matchargs)
1653 opts['_matchfiles'] = matchargs
1659 opts['_matchfiles'] = matchargs
1654 else:
1660 else:
1655 if follow:
1661 if follow:
1656 fpats = ('_patsfollow', '_patsfollowfirst')
1662 fpats = ('_patsfollow', '_patsfollowfirst')
1657 fnopats = (('_ancestors', '_fancestors'),
1663 fnopats = (('_ancestors', '_fancestors'),
1658 ('_descendants', '_fdescendants'))
1664 ('_descendants', '_fdescendants'))
1659 if pats:
1665 if pats:
1660 # follow() revset interprets its file argument as a
1666 # follow() revset interprets its file argument as a
1661 # manifest entry, so use match.files(), not pats.
1667 # manifest entry, so use match.files(), not pats.
1662 opts[fpats[followfirst]] = list(match.files())
1668 opts[fpats[followfirst]] = list(match.files())
1663 else:
1669 else:
1664 opts[fnopats[followdescendants][followfirst]] = str(startrev)
1670 opts[fnopats[followdescendants][followfirst]] = str(startrev)
1665 else:
1671 else:
1666 opts['_patslog'] = list(pats)
1672 opts['_patslog'] = list(pats)
1667
1673
1668 filematcher = None
1674 filematcher = None
1669 if opts.get('patch') or opts.get('stat'):
1675 if opts.get('patch') or opts.get('stat'):
1670 # When following files, track renames via a special matcher.
1676 # When following files, track renames via a special matcher.
1671 # If we're forced to take the slowpath it means we're following
1677 # If we're forced to take the slowpath it means we're following
1672 # at least one pattern/directory, so don't bother with rename tracking.
1678 # at least one pattern/directory, so don't bother with rename tracking.
1673 if follow and not match.always() and not slowpath:
1679 if follow and not match.always() and not slowpath:
1674 # _makelogfilematcher expects its files argument to be relative to
1680 # _makelogfilematcher expects its files argument to be relative to
1675 # the repo root, so use match.files(), not pats.
1681 # the repo root, so use match.files(), not pats.
1676 filematcher = _makefollowlogfilematcher(repo, match.files(),
1682 filematcher = _makefollowlogfilematcher(repo, match.files(),
1677 followfirst)
1683 followfirst)
1678 else:
1684 else:
1679 filematcher = _makenofollowlogfilematcher(repo, pats, opts)
1685 filematcher = _makenofollowlogfilematcher(repo, pats, opts)
1680 if filematcher is None:
1686 if filematcher is None:
1681 filematcher = lambda rev: match
1687 filematcher = lambda rev: match
1682
1688
1683 expr = []
1689 expr = []
1684 for op, val in opts.iteritems():
1690 for op, val in opts.iteritems():
1685 if not val:
1691 if not val:
1686 continue
1692 continue
1687 if op not in opt2revset:
1693 if op not in opt2revset:
1688 continue
1694 continue
1689 revop, andor = opt2revset[op]
1695 revop, andor = opt2revset[op]
1690 if '%(val)' not in revop:
1696 if '%(val)' not in revop:
1691 expr.append(revop)
1697 expr.append(revop)
1692 else:
1698 else:
1693 if not isinstance(val, list):
1699 if not isinstance(val, list):
1694 e = revop % {'val': val}
1700 e = revop % {'val': val}
1695 else:
1701 else:
1696 e = '(' + andor.join((revop % {'val': v}) for v in val) + ')'
1702 e = '(' + andor.join((revop % {'val': v}) for v in val) + ')'
1697 expr.append(e)
1703 expr.append(e)
1698
1704
1699 if expr:
1705 if expr:
1700 expr = '(' + ' and '.join(expr) + ')'
1706 expr = '(' + ' and '.join(expr) + ')'
1701 else:
1707 else:
1702 expr = None
1708 expr = None
1703 return expr, filematcher
1709 return expr, filematcher
1704
1710
1705 def getgraphlogrevs(repo, pats, opts):
1711 def getgraphlogrevs(repo, pats, opts):
1706 """Return (revs, expr, filematcher) where revs is an iterable of
1712 """Return (revs, expr, filematcher) where revs is an iterable of
1707 revision numbers, expr is a revset string built from log options
1713 revision numbers, expr is a revset string built from log options
1708 and file patterns or None, and used to filter 'revs'. If --stat or
1714 and file patterns or None, and used to filter 'revs'. If --stat or
1709 --patch are not passed filematcher is None. Otherwise it is a
1715 --patch are not passed filematcher is None. Otherwise it is a
1710 callable taking a revision number and returning a match objects
1716 callable taking a revision number and returning a match objects
1711 filtering the files to be detailed when displaying the revision.
1717 filtering the files to be detailed when displaying the revision.
1712 """
1718 """
1713 if not len(repo):
1719 if not len(repo):
1714 return [], None, None
1720 return [], None, None
1715 limit = loglimit(opts)
1721 limit = loglimit(opts)
1716 # Default --rev value depends on --follow but --follow behaviour
1722 # Default --rev value depends on --follow but --follow behaviour
1717 # depends on revisions resolved from --rev...
1723 # depends on revisions resolved from --rev...
1718 follow = opts.get('follow') or opts.get('follow_first')
1724 follow = opts.get('follow') or opts.get('follow_first')
1719 possiblyunsorted = False # whether revs might need sorting
1725 possiblyunsorted = False # whether revs might need sorting
1720 if opts.get('rev'):
1726 if opts.get('rev'):
1721 revs = scmutil.revrange(repo, opts['rev'])
1727 revs = scmutil.revrange(repo, opts['rev'])
1722 # Don't sort here because _makelogrevset might depend on the
1728 # Don't sort here because _makelogrevset might depend on the
1723 # order of revs
1729 # order of revs
1724 possiblyunsorted = True
1730 possiblyunsorted = True
1725 else:
1731 else:
1726 if follow and len(repo) > 0:
1732 if follow and len(repo) > 0:
1727 revs = repo.revs('reverse(:.)')
1733 revs = repo.revs('reverse(:.)')
1728 else:
1734 else:
1729 revs = revset.spanset(repo)
1735 revs = revset.spanset(repo)
1730 revs.reverse()
1736 revs.reverse()
1731 if not revs:
1737 if not revs:
1732 return revset.baseset(), None, None
1738 return revset.baseset(), None, None
1733 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
1739 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
1734 if possiblyunsorted:
1740 if possiblyunsorted:
1735 revs.sort(reverse=True)
1741 revs.sort(reverse=True)
1736 if expr:
1742 if expr:
1737 # Revset matchers often operate faster on revisions in changelog
1743 # Revset matchers often operate faster on revisions in changelog
1738 # order, because most filters deal with the changelog.
1744 # order, because most filters deal with the changelog.
1739 revs.reverse()
1745 revs.reverse()
1740 matcher = revset.match(repo.ui, expr)
1746 matcher = revset.match(repo.ui, expr)
1741 # Revset matches can reorder revisions. "A or B" typically returns
1747 # Revset matches can reorder revisions. "A or B" typically returns
1742 # returns the revision matching A then the revision matching B. Sort
1748 # returns the revision matching A then the revision matching B. Sort
1743 # again to fix that.
1749 # again to fix that.
1744 revs = matcher(repo, revs)
1750 revs = matcher(repo, revs)
1745 revs.sort(reverse=True)
1751 revs.sort(reverse=True)
1746 if limit is not None:
1752 if limit is not None:
1747 limitedrevs = revset.baseset()
1753 limitedrevs = revset.baseset()
1748 for idx, rev in enumerate(revs):
1754 for idx, rev in enumerate(revs):
1749 if idx >= limit:
1755 if idx >= limit:
1750 break
1756 break
1751 limitedrevs.append(rev)
1757 limitedrevs.append(rev)
1752 revs = limitedrevs
1758 revs = limitedrevs
1753
1759
1754 return revs, expr, filematcher
1760 return revs, expr, filematcher
1755
1761
1756 def getlogrevs(repo, pats, opts):
1762 def getlogrevs(repo, pats, opts):
1757 """Return (revs, expr, filematcher) where revs is an iterable of
1763 """Return (revs, expr, filematcher) where revs is an iterable of
1758 revision numbers, expr is a revset string built from log options
1764 revision numbers, expr is a revset string built from log options
1759 and file patterns or None, and used to filter 'revs'. If --stat or
1765 and file patterns or None, and used to filter 'revs'. If --stat or
1760 --patch are not passed filematcher is None. Otherwise it is a
1766 --patch are not passed filematcher is None. Otherwise it is a
1761 callable taking a revision number and returning a match objects
1767 callable taking a revision number and returning a match objects
1762 filtering the files to be detailed when displaying the revision.
1768 filtering the files to be detailed when displaying the revision.
1763 """
1769 """
1764 limit = loglimit(opts)
1770 limit = loglimit(opts)
1765 # Default --rev value depends on --follow but --follow behaviour
1771 # Default --rev value depends on --follow but --follow behaviour
1766 # depends on revisions resolved from --rev...
1772 # depends on revisions resolved from --rev...
1767 follow = opts.get('follow') or opts.get('follow_first')
1773 follow = opts.get('follow') or opts.get('follow_first')
1768 if opts.get('rev'):
1774 if opts.get('rev'):
1769 revs = scmutil.revrange(repo, opts['rev'])
1775 revs = scmutil.revrange(repo, opts['rev'])
1770 elif follow:
1776 elif follow:
1771 revs = repo.revs('reverse(:.)')
1777 revs = repo.revs('reverse(:.)')
1772 else:
1778 else:
1773 revs = revset.spanset(repo)
1779 revs = revset.spanset(repo)
1774 revs.reverse()
1780 revs.reverse()
1775 if not revs:
1781 if not revs:
1776 return revset.baseset([]), None, None
1782 return revset.baseset([]), None, None
1777 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
1783 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
1778 if expr:
1784 if expr:
1779 # Revset matchers often operate faster on revisions in changelog
1785 # Revset matchers often operate faster on revisions in changelog
1780 # order, because most filters deal with the changelog.
1786 # order, because most filters deal with the changelog.
1781 if not opts.get('rev'):
1787 if not opts.get('rev'):
1782 revs.reverse()
1788 revs.reverse()
1783 matcher = revset.match(repo.ui, expr)
1789 matcher = revset.match(repo.ui, expr)
1784 # Revset matches can reorder revisions. "A or B" typically returns
1790 # Revset matches can reorder revisions. "A or B" typically returns
1785 # returns the revision matching A then the revision matching B. Sort
1791 # returns the revision matching A then the revision matching B. Sort
1786 # again to fix that.
1792 # again to fix that.
1787 revs = matcher(repo, revs)
1793 revs = matcher(repo, revs)
1788 if not opts.get('rev'):
1794 if not opts.get('rev'):
1789 revs.sort(reverse=True)
1795 revs.sort(reverse=True)
1790 if limit is not None:
1796 if limit is not None:
1791 count = 0
1797 count = 0
1792 limitedrevs = revset.baseset([])
1798 limitedrevs = revset.baseset([])
1793 it = iter(revs)
1799 it = iter(revs)
1794 while count < limit:
1800 while count < limit:
1795 try:
1801 try:
1796 limitedrevs.append(it.next())
1802 limitedrevs.append(it.next())
1797 except (StopIteration):
1803 except (StopIteration):
1798 break
1804 break
1799 count += 1
1805 count += 1
1800 revs = limitedrevs
1806 revs = limitedrevs
1801
1807
1802 return revs, expr, filematcher
1808 return revs, expr, filematcher
1803
1809
1804 def displaygraph(ui, dag, displayer, showparents, edgefn, getrenamed=None,
1810 def displaygraph(ui, dag, displayer, showparents, edgefn, getrenamed=None,
1805 filematcher=None):
1811 filematcher=None):
1806 seen, state = [], graphmod.asciistate()
1812 seen, state = [], graphmod.asciistate()
1807 for rev, type, ctx, parents in dag:
1813 for rev, type, ctx, parents in dag:
1808 char = 'o'
1814 char = 'o'
1809 if ctx.node() in showparents:
1815 if ctx.node() in showparents:
1810 char = '@'
1816 char = '@'
1811 elif ctx.obsolete():
1817 elif ctx.obsolete():
1812 char = 'x'
1818 char = 'x'
1813 copies = None
1819 copies = None
1814 if getrenamed and ctx.rev():
1820 if getrenamed and ctx.rev():
1815 copies = []
1821 copies = []
1816 for fn in ctx.files():
1822 for fn in ctx.files():
1817 rename = getrenamed(fn, ctx.rev())
1823 rename = getrenamed(fn, ctx.rev())
1818 if rename:
1824 if rename:
1819 copies.append((fn, rename[0]))
1825 copies.append((fn, rename[0]))
1820 revmatchfn = None
1826 revmatchfn = None
1821 if filematcher is not None:
1827 if filematcher is not None:
1822 revmatchfn = filematcher(ctx.rev())
1828 revmatchfn = filematcher(ctx.rev())
1823 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
1829 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
1824 lines = displayer.hunk.pop(rev).split('\n')
1830 lines = displayer.hunk.pop(rev).split('\n')
1825 if not lines[-1]:
1831 if not lines[-1]:
1826 del lines[-1]
1832 del lines[-1]
1827 displayer.flush(rev)
1833 displayer.flush(rev)
1828 edges = edgefn(type, char, lines, seen, rev, parents)
1834 edges = edgefn(type, char, lines, seen, rev, parents)
1829 for type, char, lines, coldata in edges:
1835 for type, char, lines, coldata in edges:
1830 graphmod.ascii(ui, state, type, char, lines, coldata)
1836 graphmod.ascii(ui, state, type, char, lines, coldata)
1831 displayer.close()
1837 displayer.close()
1832
1838
1833 def graphlog(ui, repo, *pats, **opts):
1839 def graphlog(ui, repo, *pats, **opts):
1834 # Parameters are identical to log command ones
1840 # Parameters are identical to log command ones
1835 revs, expr, filematcher = getgraphlogrevs(repo, pats, opts)
1841 revs, expr, filematcher = getgraphlogrevs(repo, pats, opts)
1836 revdag = graphmod.dagwalker(repo, revs)
1842 revdag = graphmod.dagwalker(repo, revs)
1837
1843
1838 getrenamed = None
1844 getrenamed = None
1839 if opts.get('copies'):
1845 if opts.get('copies'):
1840 endrev = None
1846 endrev = None
1841 if opts.get('rev'):
1847 if opts.get('rev'):
1842 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
1848 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
1843 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
1849 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
1844 displayer = show_changeset(ui, repo, opts, buffered=True)
1850 displayer = show_changeset(ui, repo, opts, buffered=True)
1845 showparents = [ctx.node() for ctx in repo[None].parents()]
1851 showparents = [ctx.node() for ctx in repo[None].parents()]
1846 displaygraph(ui, revdag, displayer, showparents,
1852 displaygraph(ui, revdag, displayer, showparents,
1847 graphmod.asciiedges, getrenamed, filematcher)
1853 graphmod.asciiedges, getrenamed, filematcher)
1848
1854
1849 def checkunsupportedgraphflags(pats, opts):
1855 def checkunsupportedgraphflags(pats, opts):
1850 for op in ["newest_first"]:
1856 for op in ["newest_first"]:
1851 if op in opts and opts[op]:
1857 if op in opts and opts[op]:
1852 raise util.Abort(_("-G/--graph option is incompatible with --%s")
1858 raise util.Abort(_("-G/--graph option is incompatible with --%s")
1853 % op.replace("_", "-"))
1859 % op.replace("_", "-"))
1854
1860
1855 def graphrevs(repo, nodes, opts):
1861 def graphrevs(repo, nodes, opts):
1856 limit = loglimit(opts)
1862 limit = loglimit(opts)
1857 nodes.reverse()
1863 nodes.reverse()
1858 if limit is not None:
1864 if limit is not None:
1859 nodes = nodes[:limit]
1865 nodes = nodes[:limit]
1860 return graphmod.nodes(repo, nodes)
1866 return graphmod.nodes(repo, nodes)
1861
1867
1862 def add(ui, repo, match, dryrun, listsubrepos, prefix, explicitonly):
1868 def add(ui, repo, match, dryrun, listsubrepos, prefix, explicitonly):
1863 join = lambda f: os.path.join(prefix, f)
1869 join = lambda f: os.path.join(prefix, f)
1864 bad = []
1870 bad = []
1865 oldbad = match.bad
1871 oldbad = match.bad
1866 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
1872 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
1867 names = []
1873 names = []
1868 wctx = repo[None]
1874 wctx = repo[None]
1869 cca = None
1875 cca = None
1870 abort, warn = scmutil.checkportabilityalert(ui)
1876 abort, warn = scmutil.checkportabilityalert(ui)
1871 if abort or warn:
1877 if abort or warn:
1872 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
1878 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
1873 for f in repo.walk(match):
1879 for f in repo.walk(match):
1874 exact = match.exact(f)
1880 exact = match.exact(f)
1875 if exact or not explicitonly and f not in repo.dirstate:
1881 if exact or not explicitonly and f not in repo.dirstate:
1876 if cca:
1882 if cca:
1877 cca(f)
1883 cca(f)
1878 names.append(f)
1884 names.append(f)
1879 if ui.verbose or not exact:
1885 if ui.verbose or not exact:
1880 ui.status(_('adding %s\n') % match.rel(join(f)))
1886 ui.status(_('adding %s\n') % match.rel(join(f)))
1881
1887
1882 for subpath in sorted(wctx.substate):
1888 for subpath in sorted(wctx.substate):
1883 sub = wctx.sub(subpath)
1889 sub = wctx.sub(subpath)
1884 try:
1890 try:
1885 submatch = matchmod.narrowmatcher(subpath, match)
1891 submatch = matchmod.narrowmatcher(subpath, match)
1886 if listsubrepos:
1892 if listsubrepos:
1887 bad.extend(sub.add(ui, submatch, dryrun, listsubrepos, prefix,
1893 bad.extend(sub.add(ui, submatch, dryrun, listsubrepos, prefix,
1888 False))
1894 False))
1889 else:
1895 else:
1890 bad.extend(sub.add(ui, submatch, dryrun, listsubrepos, prefix,
1896 bad.extend(sub.add(ui, submatch, dryrun, listsubrepos, prefix,
1891 True))
1897 True))
1892 except error.LookupError:
1898 except error.LookupError:
1893 ui.status(_("skipping missing subrepository: %s\n")
1899 ui.status(_("skipping missing subrepository: %s\n")
1894 % join(subpath))
1900 % join(subpath))
1895
1901
1896 if not dryrun:
1902 if not dryrun:
1897 rejected = wctx.add(names, prefix)
1903 rejected = wctx.add(names, prefix)
1898 bad.extend(f for f in rejected if f in match.files())
1904 bad.extend(f for f in rejected if f in match.files())
1899 return bad
1905 return bad
1900
1906
1901 def forget(ui, repo, match, prefix, explicitonly):
1907 def forget(ui, repo, match, prefix, explicitonly):
1902 join = lambda f: os.path.join(prefix, f)
1908 join = lambda f: os.path.join(prefix, f)
1903 bad = []
1909 bad = []
1904 oldbad = match.bad
1910 oldbad = match.bad
1905 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
1911 match.bad = lambda x, y: bad.append(x) or oldbad(x, y)
1906 wctx = repo[None]
1912 wctx = repo[None]
1907 forgot = []
1913 forgot = []
1908 s = repo.status(match=match, clean=True)
1914 s = repo.status(match=match, clean=True)
1909 forget = sorted(s[0] + s[1] + s[3] + s[6])
1915 forget = sorted(s[0] + s[1] + s[3] + s[6])
1910 if explicitonly:
1916 if explicitonly:
1911 forget = [f for f in forget if match.exact(f)]
1917 forget = [f for f in forget if match.exact(f)]
1912
1918
1913 for subpath in sorted(wctx.substate):
1919 for subpath in sorted(wctx.substate):
1914 sub = wctx.sub(subpath)
1920 sub = wctx.sub(subpath)
1915 try:
1921 try:
1916 submatch = matchmod.narrowmatcher(subpath, match)
1922 submatch = matchmod.narrowmatcher(subpath, match)
1917 subbad, subforgot = sub.forget(ui, submatch, prefix)
1923 subbad, subforgot = sub.forget(ui, submatch, prefix)
1918 bad.extend([subpath + '/' + f for f in subbad])
1924 bad.extend([subpath + '/' + f for f in subbad])
1919 forgot.extend([subpath + '/' + f for f in subforgot])
1925 forgot.extend([subpath + '/' + f for f in subforgot])
1920 except error.LookupError:
1926 except error.LookupError:
1921 ui.status(_("skipping missing subrepository: %s\n")
1927 ui.status(_("skipping missing subrepository: %s\n")
1922 % join(subpath))
1928 % join(subpath))
1923
1929
1924 if not explicitonly:
1930 if not explicitonly:
1925 for f in match.files():
1931 for f in match.files():
1926 if f not in repo.dirstate and not os.path.isdir(match.rel(join(f))):
1932 if f not in repo.dirstate and not os.path.isdir(match.rel(join(f))):
1927 if f not in forgot:
1933 if f not in forgot:
1928 if os.path.exists(match.rel(join(f))):
1934 if os.path.exists(match.rel(join(f))):
1929 ui.warn(_('not removing %s: '
1935 ui.warn(_('not removing %s: '
1930 'file is already untracked\n')
1936 'file is already untracked\n')
1931 % match.rel(join(f)))
1937 % match.rel(join(f)))
1932 bad.append(f)
1938 bad.append(f)
1933
1939
1934 for f in forget:
1940 for f in forget:
1935 if ui.verbose or not match.exact(f):
1941 if ui.verbose or not match.exact(f):
1936 ui.status(_('removing %s\n') % match.rel(join(f)))
1942 ui.status(_('removing %s\n') % match.rel(join(f)))
1937
1943
1938 rejected = wctx.forget(forget, prefix)
1944 rejected = wctx.forget(forget, prefix)
1939 bad.extend(f for f in rejected if f in match.files())
1945 bad.extend(f for f in rejected if f in match.files())
1940 forgot.extend(forget)
1946 forgot.extend(forget)
1941 return bad, forgot
1947 return bad, forgot
1942
1948
1943 def cat(ui, repo, ctx, matcher, prefix, **opts):
1949 def cat(ui, repo, ctx, matcher, prefix, **opts):
1944 err = 1
1950 err = 1
1945
1951
1946 def write(path):
1952 def write(path):
1947 fp = makefileobj(repo, opts.get('output'), ctx.node(),
1953 fp = makefileobj(repo, opts.get('output'), ctx.node(),
1948 pathname=os.path.join(prefix, path))
1954 pathname=os.path.join(prefix, path))
1949 data = ctx[path].data()
1955 data = ctx[path].data()
1950 if opts.get('decode'):
1956 if opts.get('decode'):
1951 data = repo.wwritedata(path, data)
1957 data = repo.wwritedata(path, data)
1952 fp.write(data)
1958 fp.write(data)
1953 fp.close()
1959 fp.close()
1954
1960
1955 # Automation often uses hg cat on single files, so special case it
1961 # Automation often uses hg cat on single files, so special case it
1956 # for performance to avoid the cost of parsing the manifest.
1962 # for performance to avoid the cost of parsing the manifest.
1957 if len(matcher.files()) == 1 and not matcher.anypats():
1963 if len(matcher.files()) == 1 and not matcher.anypats():
1958 file = matcher.files()[0]
1964 file = matcher.files()[0]
1959 mf = repo.manifest
1965 mf = repo.manifest
1960 mfnode = ctx._changeset[0]
1966 mfnode = ctx._changeset[0]
1961 if mf.find(mfnode, file)[0]:
1967 if mf.find(mfnode, file)[0]:
1962 write(file)
1968 write(file)
1963 return 0
1969 return 0
1964
1970
1965 # Don't warn about "missing" files that are really in subrepos
1971 # Don't warn about "missing" files that are really in subrepos
1966 bad = matcher.bad
1972 bad = matcher.bad
1967
1973
1968 def badfn(path, msg):
1974 def badfn(path, msg):
1969 for subpath in ctx.substate:
1975 for subpath in ctx.substate:
1970 if path.startswith(subpath):
1976 if path.startswith(subpath):
1971 return
1977 return
1972 bad(path, msg)
1978 bad(path, msg)
1973
1979
1974 matcher.bad = badfn
1980 matcher.bad = badfn
1975
1981
1976 for abs in ctx.walk(matcher):
1982 for abs in ctx.walk(matcher):
1977 write(abs)
1983 write(abs)
1978 err = 0
1984 err = 0
1979
1985
1980 matcher.bad = bad
1986 matcher.bad = bad
1981
1987
1982 for subpath in sorted(ctx.substate):
1988 for subpath in sorted(ctx.substate):
1983 sub = ctx.sub(subpath)
1989 sub = ctx.sub(subpath)
1984 try:
1990 try:
1985 submatch = matchmod.narrowmatcher(subpath, matcher)
1991 submatch = matchmod.narrowmatcher(subpath, matcher)
1986
1992
1987 if not sub.cat(ui, submatch, os.path.join(prefix, sub._path),
1993 if not sub.cat(ui, submatch, os.path.join(prefix, sub._path),
1988 **opts):
1994 **opts):
1989 err = 0
1995 err = 0
1990 except error.RepoLookupError:
1996 except error.RepoLookupError:
1991 ui.status(_("skipping missing subrepository: %s\n")
1997 ui.status(_("skipping missing subrepository: %s\n")
1992 % os.path.join(prefix, subpath))
1998 % os.path.join(prefix, subpath))
1993
1999
1994 return err
2000 return err
1995
2001
1996 def duplicatecopies(repo, rev, fromrev, skiprev=None):
2002 def duplicatecopies(repo, rev, fromrev, skiprev=None):
1997 '''reproduce copies from fromrev to rev in the dirstate
2003 '''reproduce copies from fromrev to rev in the dirstate
1998
2004
1999 If skiprev is specified, it's a revision that should be used to
2005 If skiprev is specified, it's a revision that should be used to
2000 filter copy records. Any copies that occur between fromrev and
2006 filter copy records. Any copies that occur between fromrev and
2001 skiprev will not be duplicated, even if they appear in the set of
2007 skiprev will not be duplicated, even if they appear in the set of
2002 copies between fromrev and rev.
2008 copies between fromrev and rev.
2003 '''
2009 '''
2004 exclude = {}
2010 exclude = {}
2005 if skiprev is not None:
2011 if skiprev is not None:
2006 exclude = copies.pathcopies(repo[fromrev], repo[skiprev])
2012 exclude = copies.pathcopies(repo[fromrev], repo[skiprev])
2007 for dst, src in copies.pathcopies(repo[fromrev], repo[rev]).iteritems():
2013 for dst, src in copies.pathcopies(repo[fromrev], repo[rev]).iteritems():
2008 # copies.pathcopies returns backward renames, so dst might not
2014 # copies.pathcopies returns backward renames, so dst might not
2009 # actually be in the dirstate
2015 # actually be in the dirstate
2010 if dst in exclude:
2016 if dst in exclude:
2011 continue
2017 continue
2012 if repo.dirstate[dst] in "nma":
2018 if repo.dirstate[dst] in "nma":
2013 repo.dirstate.copy(src, dst)
2019 repo.dirstate.copy(src, dst)
2014
2020
2015 def commit(ui, repo, commitfunc, pats, opts):
2021 def commit(ui, repo, commitfunc, pats, opts):
2016 '''commit the specified files or all outstanding changes'''
2022 '''commit the specified files or all outstanding changes'''
2017 date = opts.get('date')
2023 date = opts.get('date')
2018 if date:
2024 if date:
2019 opts['date'] = util.parsedate(date)
2025 opts['date'] = util.parsedate(date)
2020 message = logmessage(ui, opts)
2026 message = logmessage(ui, opts)
2021
2027
2022 # extract addremove carefully -- this function can be called from a command
2028 # extract addremove carefully -- this function can be called from a command
2023 # that doesn't support addremove
2029 # that doesn't support addremove
2024 if opts.get('addremove'):
2030 if opts.get('addremove'):
2025 scmutil.addremove(repo, pats, opts)
2031 scmutil.addremove(repo, pats, opts)
2026
2032
2027 return commitfunc(ui, repo, message,
2033 return commitfunc(ui, repo, message,
2028 scmutil.match(repo[None], pats, opts), opts)
2034 scmutil.match(repo[None], pats, opts), opts)
2029
2035
2030 def amend(ui, repo, commitfunc, old, extra, pats, opts):
2036 def amend(ui, repo, commitfunc, old, extra, pats, opts):
2031 ui.note(_('amending changeset %s\n') % old)
2037 ui.note(_('amending changeset %s\n') % old)
2032 base = old.p1()
2038 base = old.p1()
2033
2039
2034 wlock = lock = newid = None
2040 wlock = lock = newid = None
2035 try:
2041 try:
2036 wlock = repo.wlock()
2042 wlock = repo.wlock()
2037 lock = repo.lock()
2043 lock = repo.lock()
2038 tr = repo.transaction('amend')
2044 tr = repo.transaction('amend')
2039 try:
2045 try:
2040 # See if we got a message from -m or -l, if not, open the editor
2046 # See if we got a message from -m or -l, if not, open the editor
2041 # with the message of the changeset to amend
2047 # with the message of the changeset to amend
2042 message = logmessage(ui, opts)
2048 message = logmessage(ui, opts)
2043 # ensure logfile does not conflict with later enforcement of the
2049 # ensure logfile does not conflict with later enforcement of the
2044 # message. potential logfile content has been processed by
2050 # message. potential logfile content has been processed by
2045 # `logmessage` anyway.
2051 # `logmessage` anyway.
2046 opts.pop('logfile')
2052 opts.pop('logfile')
2047 # First, do a regular commit to record all changes in the working
2053 # First, do a regular commit to record all changes in the working
2048 # directory (if there are any)
2054 # directory (if there are any)
2049 ui.callhooks = False
2055 ui.callhooks = False
2050 currentbookmark = repo._bookmarkcurrent
2056 currentbookmark = repo._bookmarkcurrent
2051 try:
2057 try:
2052 repo._bookmarkcurrent = None
2058 repo._bookmarkcurrent = None
2053 opts['message'] = 'temporary amend commit for %s' % old
2059 opts['message'] = 'temporary amend commit for %s' % old
2054 node = commit(ui, repo, commitfunc, pats, opts)
2060 node = commit(ui, repo, commitfunc, pats, opts)
2055 finally:
2061 finally:
2056 repo._bookmarkcurrent = currentbookmark
2062 repo._bookmarkcurrent = currentbookmark
2057 ui.callhooks = True
2063 ui.callhooks = True
2058 ctx = repo[node]
2064 ctx = repo[node]
2059
2065
2060 # Participating changesets:
2066 # Participating changesets:
2061 #
2067 #
2062 # node/ctx o - new (intermediate) commit that contains changes
2068 # node/ctx o - new (intermediate) commit that contains changes
2063 # | from working dir to go into amending commit
2069 # | from working dir to go into amending commit
2064 # | (or a workingctx if there were no changes)
2070 # | (or a workingctx if there were no changes)
2065 # |
2071 # |
2066 # old o - changeset to amend
2072 # old o - changeset to amend
2067 # |
2073 # |
2068 # base o - parent of amending changeset
2074 # base o - parent of amending changeset
2069
2075
2070 # Update extra dict from amended commit (e.g. to preserve graft
2076 # Update extra dict from amended commit (e.g. to preserve graft
2071 # source)
2077 # source)
2072 extra.update(old.extra())
2078 extra.update(old.extra())
2073
2079
2074 # Also update it from the intermediate commit or from the wctx
2080 # Also update it from the intermediate commit or from the wctx
2075 extra.update(ctx.extra())
2081 extra.update(ctx.extra())
2076
2082
2077 if len(old.parents()) > 1:
2083 if len(old.parents()) > 1:
2078 # ctx.files() isn't reliable for merges, so fall back to the
2084 # ctx.files() isn't reliable for merges, so fall back to the
2079 # slower repo.status() method
2085 # slower repo.status() method
2080 files = set([fn for st in repo.status(base, old)[:3]
2086 files = set([fn for st in repo.status(base, old)[:3]
2081 for fn in st])
2087 for fn in st])
2082 else:
2088 else:
2083 files = set(old.files())
2089 files = set(old.files())
2084
2090
2085 # Second, we use either the commit we just did, or if there were no
2091 # Second, we use either the commit we just did, or if there were no
2086 # changes the parent of the working directory as the version of the
2092 # changes the parent of the working directory as the version of the
2087 # files in the final amend commit
2093 # files in the final amend commit
2088 if node:
2094 if node:
2089 ui.note(_('copying changeset %s to %s\n') % (ctx, base))
2095 ui.note(_('copying changeset %s to %s\n') % (ctx, base))
2090
2096
2091 user = ctx.user()
2097 user = ctx.user()
2092 date = ctx.date()
2098 date = ctx.date()
2093 # Recompute copies (avoid recording a -> b -> a)
2099 # Recompute copies (avoid recording a -> b -> a)
2094 copied = copies.pathcopies(base, ctx)
2100 copied = copies.pathcopies(base, ctx)
2095
2101
2096 # Prune files which were reverted by the updates: if old
2102 # Prune files which were reverted by the updates: if old
2097 # introduced file X and our intermediate commit, node,
2103 # introduced file X and our intermediate commit, node,
2098 # renamed that file, then those two files are the same and
2104 # renamed that file, then those two files are the same and
2099 # we can discard X from our list of files. Likewise if X
2105 # we can discard X from our list of files. Likewise if X
2100 # was deleted, it's no longer relevant
2106 # was deleted, it's no longer relevant
2101 files.update(ctx.files())
2107 files.update(ctx.files())
2102
2108
2103 def samefile(f):
2109 def samefile(f):
2104 if f in ctx.manifest():
2110 if f in ctx.manifest():
2105 a = ctx.filectx(f)
2111 a = ctx.filectx(f)
2106 if f in base.manifest():
2112 if f in base.manifest():
2107 b = base.filectx(f)
2113 b = base.filectx(f)
2108 return (not a.cmp(b)
2114 return (not a.cmp(b)
2109 and a.flags() == b.flags())
2115 and a.flags() == b.flags())
2110 else:
2116 else:
2111 return False
2117 return False
2112 else:
2118 else:
2113 return f not in base.manifest()
2119 return f not in base.manifest()
2114 files = [f for f in files if not samefile(f)]
2120 files = [f for f in files if not samefile(f)]
2115
2121
2116 def filectxfn(repo, ctx_, path):
2122 def filectxfn(repo, ctx_, path):
2117 try:
2123 try:
2118 fctx = ctx[path]
2124 fctx = ctx[path]
2119 flags = fctx.flags()
2125 flags = fctx.flags()
2120 mctx = context.memfilectx(repo,
2126 mctx = context.memfilectx(repo,
2121 fctx.path(), fctx.data(),
2127 fctx.path(), fctx.data(),
2122 islink='l' in flags,
2128 islink='l' in flags,
2123 isexec='x' in flags,
2129 isexec='x' in flags,
2124 copied=copied.get(path))
2130 copied=copied.get(path))
2125 return mctx
2131 return mctx
2126 except KeyError:
2132 except KeyError:
2127 raise IOError
2133 raise IOError
2128 else:
2134 else:
2129 ui.note(_('copying changeset %s to %s\n') % (old, base))
2135 ui.note(_('copying changeset %s to %s\n') % (old, base))
2130
2136
2131 # Use version of files as in the old cset
2137 # Use version of files as in the old cset
2132 def filectxfn(repo, ctx_, path):
2138 def filectxfn(repo, ctx_, path):
2133 try:
2139 try:
2134 return old.filectx(path)
2140 return old.filectx(path)
2135 except KeyError:
2141 except KeyError:
2136 raise IOError
2142 raise IOError
2137
2143
2138 user = opts.get('user') or old.user()
2144 user = opts.get('user') or old.user()
2139 date = opts.get('date') or old.date()
2145 date = opts.get('date') or old.date()
2140 editform = mergeeditform(old, 'commit.amend')
2146 editform = mergeeditform(old, 'commit.amend')
2141 editor = getcommiteditor(editform=editform, **opts)
2147 editor = getcommiteditor(editform=editform, **opts)
2142 if not message:
2148 if not message:
2143 editor = getcommiteditor(edit=True, editform=editform)
2149 editor = getcommiteditor(edit=True, editform=editform)
2144 message = old.description()
2150 message = old.description()
2145
2151
2146 pureextra = extra.copy()
2152 pureextra = extra.copy()
2147 extra['amend_source'] = old.hex()
2153 extra['amend_source'] = old.hex()
2148
2154
2149 new = context.memctx(repo,
2155 new = context.memctx(repo,
2150 parents=[base.node(), old.p2().node()],
2156 parents=[base.node(), old.p2().node()],
2151 text=message,
2157 text=message,
2152 files=files,
2158 files=files,
2153 filectxfn=filectxfn,
2159 filectxfn=filectxfn,
2154 user=user,
2160 user=user,
2155 date=date,
2161 date=date,
2156 extra=extra,
2162 extra=extra,
2157 editor=editor)
2163 editor=editor)
2158
2164
2159 newdesc = changelog.stripdesc(new.description())
2165 newdesc = changelog.stripdesc(new.description())
2160 if ((not node)
2166 if ((not node)
2161 and newdesc == old.description()
2167 and newdesc == old.description()
2162 and user == old.user()
2168 and user == old.user()
2163 and date == old.date()
2169 and date == old.date()
2164 and pureextra == old.extra()):
2170 and pureextra == old.extra()):
2165 # nothing changed. continuing here would create a new node
2171 # nothing changed. continuing here would create a new node
2166 # anyway because of the amend_source noise.
2172 # anyway because of the amend_source noise.
2167 #
2173 #
2168 # This not what we expect from amend.
2174 # This not what we expect from amend.
2169 return old.node()
2175 return old.node()
2170
2176
2171 ph = repo.ui.config('phases', 'new-commit', phases.draft)
2177 ph = repo.ui.config('phases', 'new-commit', phases.draft)
2172 try:
2178 try:
2173 if opts.get('secret'):
2179 if opts.get('secret'):
2174 commitphase = 'secret'
2180 commitphase = 'secret'
2175 else:
2181 else:
2176 commitphase = old.phase()
2182 commitphase = old.phase()
2177 repo.ui.setconfig('phases', 'new-commit', commitphase, 'amend')
2183 repo.ui.setconfig('phases', 'new-commit', commitphase, 'amend')
2178 newid = repo.commitctx(new)
2184 newid = repo.commitctx(new)
2179 finally:
2185 finally:
2180 repo.ui.setconfig('phases', 'new-commit', ph, 'amend')
2186 repo.ui.setconfig('phases', 'new-commit', ph, 'amend')
2181 if newid != old.node():
2187 if newid != old.node():
2182 # Reroute the working copy parent to the new changeset
2188 # Reroute the working copy parent to the new changeset
2183 repo.setparents(newid, nullid)
2189 repo.setparents(newid, nullid)
2184
2190
2185 # Move bookmarks from old parent to amend commit
2191 # Move bookmarks from old parent to amend commit
2186 bms = repo.nodebookmarks(old.node())
2192 bms = repo.nodebookmarks(old.node())
2187 if bms:
2193 if bms:
2188 marks = repo._bookmarks
2194 marks = repo._bookmarks
2189 for bm in bms:
2195 for bm in bms:
2190 marks[bm] = newid
2196 marks[bm] = newid
2191 marks.write()
2197 marks.write()
2192 #commit the whole amend process
2198 #commit the whole amend process
2193 if obsolete._enabled and newid != old.node():
2199 if obsolete._enabled and newid != old.node():
2194 # mark the new changeset as successor of the rewritten one
2200 # mark the new changeset as successor of the rewritten one
2195 new = repo[newid]
2201 new = repo[newid]
2196 obs = [(old, (new,))]
2202 obs = [(old, (new,))]
2197 if node:
2203 if node:
2198 obs.append((ctx, ()))
2204 obs.append((ctx, ()))
2199
2205
2200 obsolete.createmarkers(repo, obs)
2206 obsolete.createmarkers(repo, obs)
2201 tr.close()
2207 tr.close()
2202 finally:
2208 finally:
2203 tr.release()
2209 tr.release()
2204 if (not obsolete._enabled) and newid != old.node():
2210 if (not obsolete._enabled) and newid != old.node():
2205 # Strip the intermediate commit (if there was one) and the amended
2211 # Strip the intermediate commit (if there was one) and the amended
2206 # commit
2212 # commit
2207 if node:
2213 if node:
2208 ui.note(_('stripping intermediate changeset %s\n') % ctx)
2214 ui.note(_('stripping intermediate changeset %s\n') % ctx)
2209 ui.note(_('stripping amended changeset %s\n') % old)
2215 ui.note(_('stripping amended changeset %s\n') % old)
2210 repair.strip(ui, repo, old.node(), topic='amend-backup')
2216 repair.strip(ui, repo, old.node(), topic='amend-backup')
2211 finally:
2217 finally:
2212 if newid is None:
2218 if newid is None:
2213 repo.dirstate.invalidate()
2219 repo.dirstate.invalidate()
2214 lockmod.release(lock, wlock)
2220 lockmod.release(lock, wlock)
2215 return newid
2221 return newid
2216
2222
2217 def commiteditor(repo, ctx, subs, editform=''):
2223 def commiteditor(repo, ctx, subs, editform=''):
2218 if ctx.description():
2224 if ctx.description():
2219 return ctx.description()
2225 return ctx.description()
2220 return commitforceeditor(repo, ctx, subs, editform=editform)
2226 return commitforceeditor(repo, ctx, subs, editform=editform)
2221
2227
2222 def commitforceeditor(repo, ctx, subs, finishdesc=None, extramsg=None,
2228 def commitforceeditor(repo, ctx, subs, finishdesc=None, extramsg=None,
2223 editform=''):
2229 editform=''):
2224 if not extramsg:
2230 if not extramsg:
2225 extramsg = _("Leave message empty to abort commit.")
2231 extramsg = _("Leave message empty to abort commit.")
2226
2232
2227 forms = [e for e in editform.split('.') if e]
2233 forms = [e for e in editform.split('.') if e]
2228 forms.insert(0, 'changeset')
2234 forms.insert(0, 'changeset')
2229 while forms:
2235 while forms:
2230 tmpl = repo.ui.config('committemplate', '.'.join(forms))
2236 tmpl = repo.ui.config('committemplate', '.'.join(forms))
2231 if tmpl:
2237 if tmpl:
2232 committext = buildcommittemplate(repo, ctx, subs, extramsg, tmpl)
2238 committext = buildcommittemplate(repo, ctx, subs, extramsg, tmpl)
2233 break
2239 break
2234 forms.pop()
2240 forms.pop()
2235 else:
2241 else:
2236 committext = buildcommittext(repo, ctx, subs, extramsg)
2242 committext = buildcommittext(repo, ctx, subs, extramsg)
2237
2243
2238 # run editor in the repository root
2244 # run editor in the repository root
2239 olddir = os.getcwd()
2245 olddir = os.getcwd()
2240 os.chdir(repo.root)
2246 os.chdir(repo.root)
2241 text = repo.ui.edit(committext, ctx.user(), ctx.extra(), editform=editform)
2247 text = repo.ui.edit(committext, ctx.user(), ctx.extra(), editform=editform)
2242 text = re.sub("(?m)^HG:.*(\n|$)", "", text)
2248 text = re.sub("(?m)^HG:.*(\n|$)", "", text)
2243 os.chdir(olddir)
2249 os.chdir(olddir)
2244
2250
2245 if finishdesc:
2251 if finishdesc:
2246 text = finishdesc(text)
2252 text = finishdesc(text)
2247 if not text.strip():
2253 if not text.strip():
2248 raise util.Abort(_("empty commit message"))
2254 raise util.Abort(_("empty commit message"))
2249
2255
2250 return text
2256 return text
2251
2257
2252 def buildcommittemplate(repo, ctx, subs, extramsg, tmpl):
2258 def buildcommittemplate(repo, ctx, subs, extramsg, tmpl):
2253 ui = repo.ui
2259 ui = repo.ui
2254 tmpl, mapfile = gettemplate(ui, tmpl, None)
2260 tmpl, mapfile = gettemplate(ui, tmpl, None)
2255
2261
2256 try:
2262 try:
2257 t = changeset_templater(ui, repo, None, {}, tmpl, mapfile, False)
2263 t = changeset_templater(ui, repo, None, {}, tmpl, mapfile, False)
2258 except SyntaxError, inst:
2264 except SyntaxError, inst:
2259 raise util.Abort(inst.args[0])
2265 raise util.Abort(inst.args[0])
2260
2266
2261 for k, v in repo.ui.configitems('committemplate'):
2267 for k, v in repo.ui.configitems('committemplate'):
2262 if k != 'changeset':
2268 if k != 'changeset':
2263 t.t.cache[k] = v
2269 t.t.cache[k] = v
2264
2270
2265 if not extramsg:
2271 if not extramsg:
2266 extramsg = '' # ensure that extramsg is string
2272 extramsg = '' # ensure that extramsg is string
2267
2273
2268 ui.pushbuffer()
2274 ui.pushbuffer()
2269 t.show(ctx, extramsg=extramsg)
2275 t.show(ctx, extramsg=extramsg)
2270 return ui.popbuffer()
2276 return ui.popbuffer()
2271
2277
2272 def buildcommittext(repo, ctx, subs, extramsg):
2278 def buildcommittext(repo, ctx, subs, extramsg):
2273 edittext = []
2279 edittext = []
2274 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
2280 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
2275 if ctx.description():
2281 if ctx.description():
2276 edittext.append(ctx.description())
2282 edittext.append(ctx.description())
2277 edittext.append("")
2283 edittext.append("")
2278 edittext.append("") # Empty line between message and comments.
2284 edittext.append("") # Empty line between message and comments.
2279 edittext.append(_("HG: Enter commit message."
2285 edittext.append(_("HG: Enter commit message."
2280 " Lines beginning with 'HG:' are removed."))
2286 " Lines beginning with 'HG:' are removed."))
2281 edittext.append("HG: %s" % extramsg)
2287 edittext.append("HG: %s" % extramsg)
2282 edittext.append("HG: --")
2288 edittext.append("HG: --")
2283 edittext.append(_("HG: user: %s") % ctx.user())
2289 edittext.append(_("HG: user: %s") % ctx.user())
2284 if ctx.p2():
2290 if ctx.p2():
2285 edittext.append(_("HG: branch merge"))
2291 edittext.append(_("HG: branch merge"))
2286 if ctx.branch():
2292 if ctx.branch():
2287 edittext.append(_("HG: branch '%s'") % ctx.branch())
2293 edittext.append(_("HG: branch '%s'") % ctx.branch())
2288 if bookmarks.iscurrent(repo):
2294 if bookmarks.iscurrent(repo):
2289 edittext.append(_("HG: bookmark '%s'") % repo._bookmarkcurrent)
2295 edittext.append(_("HG: bookmark '%s'") % repo._bookmarkcurrent)
2290 edittext.extend([_("HG: subrepo %s") % s for s in subs])
2296 edittext.extend([_("HG: subrepo %s") % s for s in subs])
2291 edittext.extend([_("HG: added %s") % f for f in added])
2297 edittext.extend([_("HG: added %s") % f for f in added])
2292 edittext.extend([_("HG: changed %s") % f for f in modified])
2298 edittext.extend([_("HG: changed %s") % f for f in modified])
2293 edittext.extend([_("HG: removed %s") % f for f in removed])
2299 edittext.extend([_("HG: removed %s") % f for f in removed])
2294 if not added and not modified and not removed:
2300 if not added and not modified and not removed:
2295 edittext.append(_("HG: no files changed"))
2301 edittext.append(_("HG: no files changed"))
2296 edittext.append("")
2302 edittext.append("")
2297
2303
2298 return "\n".join(edittext)
2304 return "\n".join(edittext)
2299
2305
2300 def commitstatus(repo, node, branch, bheads=None, opts={}):
2306 def commitstatus(repo, node, branch, bheads=None, opts={}):
2301 ctx = repo[node]
2307 ctx = repo[node]
2302 parents = ctx.parents()
2308 parents = ctx.parents()
2303
2309
2304 if (not opts.get('amend') and bheads and node not in bheads and not
2310 if (not opts.get('amend') and bheads and node not in bheads and not
2305 [x for x in parents if x.node() in bheads and x.branch() == branch]):
2311 [x for x in parents if x.node() in bheads and x.branch() == branch]):
2306 repo.ui.status(_('created new head\n'))
2312 repo.ui.status(_('created new head\n'))
2307 # The message is not printed for initial roots. For the other
2313 # The message is not printed for initial roots. For the other
2308 # changesets, it is printed in the following situations:
2314 # changesets, it is printed in the following situations:
2309 #
2315 #
2310 # Par column: for the 2 parents with ...
2316 # Par column: for the 2 parents with ...
2311 # N: null or no parent
2317 # N: null or no parent
2312 # B: parent is on another named branch
2318 # B: parent is on another named branch
2313 # C: parent is a regular non head changeset
2319 # C: parent is a regular non head changeset
2314 # H: parent was a branch head of the current branch
2320 # H: parent was a branch head of the current branch
2315 # Msg column: whether we print "created new head" message
2321 # Msg column: whether we print "created new head" message
2316 # In the following, it is assumed that there already exists some
2322 # In the following, it is assumed that there already exists some
2317 # initial branch heads of the current branch, otherwise nothing is
2323 # initial branch heads of the current branch, otherwise nothing is
2318 # printed anyway.
2324 # printed anyway.
2319 #
2325 #
2320 # Par Msg Comment
2326 # Par Msg Comment
2321 # N N y additional topo root
2327 # N N y additional topo root
2322 #
2328 #
2323 # B N y additional branch root
2329 # B N y additional branch root
2324 # C N y additional topo head
2330 # C N y additional topo head
2325 # H N n usual case
2331 # H N n usual case
2326 #
2332 #
2327 # B B y weird additional branch root
2333 # B B y weird additional branch root
2328 # C B y branch merge
2334 # C B y branch merge
2329 # H B n merge with named branch
2335 # H B n merge with named branch
2330 #
2336 #
2331 # C C y additional head from merge
2337 # C C y additional head from merge
2332 # C H n merge with a head
2338 # C H n merge with a head
2333 #
2339 #
2334 # H H n head merge: head count decreases
2340 # H H n head merge: head count decreases
2335
2341
2336 if not opts.get('close_branch'):
2342 if not opts.get('close_branch'):
2337 for r in parents:
2343 for r in parents:
2338 if r.closesbranch() and r.branch() == branch:
2344 if r.closesbranch() and r.branch() == branch:
2339 repo.ui.status(_('reopening closed branch head %d\n') % r)
2345 repo.ui.status(_('reopening closed branch head %d\n') % r)
2340
2346
2341 if repo.ui.debugflag:
2347 if repo.ui.debugflag:
2342 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
2348 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
2343 elif repo.ui.verbose:
2349 elif repo.ui.verbose:
2344 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
2350 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
2345
2351
2346 def revert(ui, repo, ctx, parents, *pats, **opts):
2352 def revert(ui, repo, ctx, parents, *pats, **opts):
2347 parent, p2 = parents
2353 parent, p2 = parents
2348 node = ctx.node()
2354 node = ctx.node()
2349
2355
2350 mf = ctx.manifest()
2356 mf = ctx.manifest()
2351 if node == p2:
2357 if node == p2:
2352 parent = p2
2358 parent = p2
2353 if node == parent:
2359 if node == parent:
2354 pmf = mf
2360 pmf = mf
2355 else:
2361 else:
2356 pmf = None
2362 pmf = None
2357
2363
2358 # need all matching names in dirstate and manifest of target rev,
2364 # need all matching names in dirstate and manifest of target rev,
2359 # so have to walk both. do not print errors if files exist in one
2365 # so have to walk both. do not print errors if files exist in one
2360 # but not other.
2366 # but not other.
2361
2367
2362 # `names` is a mapping for all elements in working copy and target revision
2368 # `names` is a mapping for all elements in working copy and target revision
2363 # The mapping is in the form:
2369 # The mapping is in the form:
2364 # <asb path in repo> -> (<path from CWD>, <exactly specified by matcher?>)
2370 # <asb path in repo> -> (<path from CWD>, <exactly specified by matcher?>)
2365 names = {}
2371 names = {}
2366
2372
2367 wlock = repo.wlock()
2373 wlock = repo.wlock()
2368 try:
2374 try:
2369 ## filling of the `names` mapping
2375 ## filling of the `names` mapping
2370 # walk dirstate to fill `names`
2376 # walk dirstate to fill `names`
2371
2377
2372 m = scmutil.match(repo[None], pats, opts)
2378 m = scmutil.match(repo[None], pats, opts)
2373 m.bad = lambda x, y: False
2379 m.bad = lambda x, y: False
2374 for abs in repo.walk(m):
2380 for abs in repo.walk(m):
2375 names[abs] = m.rel(abs), m.exact(abs)
2381 names[abs] = m.rel(abs), m.exact(abs)
2376
2382
2377 # walk target manifest to fill `names`
2383 # walk target manifest to fill `names`
2378
2384
2379 def badfn(path, msg):
2385 def badfn(path, msg):
2380 if path in names:
2386 if path in names:
2381 return
2387 return
2382 if path in ctx.substate:
2388 if path in ctx.substate:
2383 return
2389 return
2384 path_ = path + '/'
2390 path_ = path + '/'
2385 for f in names:
2391 for f in names:
2386 if f.startswith(path_):
2392 if f.startswith(path_):
2387 return
2393 return
2388 ui.warn("%s: %s\n" % (m.rel(path), msg))
2394 ui.warn("%s: %s\n" % (m.rel(path), msg))
2389
2395
2390 m = scmutil.match(ctx, pats, opts)
2396 m = scmutil.match(ctx, pats, opts)
2391 m.bad = badfn
2397 m.bad = badfn
2392 for abs in ctx.walk(m):
2398 for abs in ctx.walk(m):
2393 if abs not in names:
2399 if abs not in names:
2394 names[abs] = m.rel(abs), m.exact(abs)
2400 names[abs] = m.rel(abs), m.exact(abs)
2395
2401
2396 # get the list of subrepos that must be reverted
2402 # get the list of subrepos that must be reverted
2397 targetsubs = sorted(s for s in ctx.substate if m(s))
2403 targetsubs = sorted(s for s in ctx.substate if m(s))
2398
2404
2399 # Find status of all file in `names`.
2405 # Find status of all file in `names`.
2400 m = scmutil.matchfiles(repo, names)
2406 m = scmutil.matchfiles(repo, names)
2401
2407
2402 changes = repo.status(node1=node, match=m,
2408 changes = repo.status(node1=node, match=m,
2403 unknown=True, ignored=True, clean=True)
2409 unknown=True, ignored=True, clean=True)
2404 modified = set(changes[0])
2410 modified = set(changes[0])
2405 added = set(changes[1])
2411 added = set(changes[1])
2406 removed = set(changes[2])
2412 removed = set(changes[2])
2407 _deleted = set(changes[3])
2413 _deleted = set(changes[3])
2408 unknown = set(changes[4])
2414 unknown = set(changes[4])
2409 unknown.update(changes[5])
2415 unknown.update(changes[5])
2410 clean = set(changes[6])
2416 clean = set(changes[6])
2411
2417
2412 # split between files known in target manifest and the others
2418 # split between files known in target manifest and the others
2413 smf = set(mf)
2419 smf = set(mf)
2414
2420
2415 # determine the exact nature of the deleted changesets
2421 # determine the exact nature of the deleted changesets
2416 _deletedadded = _deleted - smf
2422 _deletedadded = _deleted - smf
2417 _deletedmodified = _deleted - _deletedadded
2423 _deletedmodified = _deleted - _deletedadded
2418 added |= _deletedadded
2424 added |= _deletedadded
2419 modified |= _deletedmodified
2425 modified |= _deletedmodified
2420
2426
2421 # We need to account for the state of file in the dirstate
2427 # We need to account for the state of file in the dirstate
2422 #
2428 #
2423 # Even, when we revert agains something else than parent. this will
2429 # Even, when we revert agains something else than parent. this will
2424 # slightly alter the behavior of revert (doing back up or not, delete
2430 # slightly alter the behavior of revert (doing back up or not, delete
2425 # or just forget etc)
2431 # or just forget etc)
2426 if parent == node:
2432 if parent == node:
2427 dsmodified = modified
2433 dsmodified = modified
2428 dsadded = added
2434 dsadded = added
2429 dsremoved = removed
2435 dsremoved = removed
2430 modified, added, removed = set(), set(), set()
2436 modified, added, removed = set(), set(), set()
2431 else:
2437 else:
2432 changes = repo.status(node1=parent, match=m)
2438 changes = repo.status(node1=parent, match=m)
2433 dsmodified = set(changes[0])
2439 dsmodified = set(changes[0])
2434 dsadded = set(changes[1])
2440 dsadded = set(changes[1])
2435 dsremoved = set(changes[2])
2441 dsremoved = set(changes[2])
2436
2442
2437 # only take into account for removes between wc and target
2443 # only take into account for removes between wc and target
2438 clean |= dsremoved - removed
2444 clean |= dsremoved - removed
2439 dsremoved &= removed
2445 dsremoved &= removed
2440 # distinct between dirstate remove and other
2446 # distinct between dirstate remove and other
2441 removed -= dsremoved
2447 removed -= dsremoved
2442
2448
2443 # tell newly modified apart.
2449 # tell newly modified apart.
2444 dsmodified &= modified
2450 dsmodified &= modified
2445 dsmodified |= modified & dsadded # dirstate added may needs backup
2451 dsmodified |= modified & dsadded # dirstate added may needs backup
2446 modified -= dsmodified
2452 modified -= dsmodified
2447
2453
2448 # There are three categories of added files
2454 # There are three categories of added files
2449 #
2455 #
2450 # 1. addition that just happened in the dirstate
2456 # 1. addition that just happened in the dirstate
2451 # (should be forgotten)
2457 # (should be forgotten)
2452 # 2. file is added since target revision and has local changes
2458 # 2. file is added since target revision and has local changes
2453 # (should be backed up and removed)
2459 # (should be backed up and removed)
2454 # 3. file is added since target revision and is clean
2460 # 3. file is added since target revision and is clean
2455 # (should be removed)
2461 # (should be removed)
2456 #
2462 #
2457 # However we do not need to split them yet. The current revert code
2463 # However we do not need to split them yet. The current revert code
2458 # will automatically recognize (1) when performing operation. And
2464 # will automatically recognize (1) when performing operation. And
2459 # the backup system is currently unabled to handle (2).
2465 # the backup system is currently unabled to handle (2).
2460 #
2466 #
2461 # So we just put them all in the same group.
2467 # So we just put them all in the same group.
2462 dsadded = added
2468 dsadded = added
2463
2469
2464 # in case of merge, files that are actually added can be reported as
2470 # in case of merge, files that are actually added can be reported as
2465 # modified, we need to post process the result
2471 # modified, we need to post process the result
2466 if p2 != nullid:
2472 if p2 != nullid:
2467 if pmf is None:
2473 if pmf is None:
2468 # only need parent manifest in the merge case,
2474 # only need parent manifest in the merge case,
2469 # so do not read by default
2475 # so do not read by default
2470 pmf = repo[parent].manifest()
2476 pmf = repo[parent].manifest()
2471 mergeadd = dsmodified - set(pmf)
2477 mergeadd = dsmodified - set(pmf)
2472 dsadded |= mergeadd
2478 dsadded |= mergeadd
2473 dsmodified -= mergeadd
2479 dsmodified -= mergeadd
2474
2480
2475 # if f is a rename, update `names` to also revert the source
2481 # if f is a rename, update `names` to also revert the source
2476 cwd = repo.getcwd()
2482 cwd = repo.getcwd()
2477 for f in dsadded:
2483 for f in dsadded:
2478 src = repo.dirstate.copied(f)
2484 src = repo.dirstate.copied(f)
2479 # XXX should we check for rename down to target node?
2485 # XXX should we check for rename down to target node?
2480 if src and src not in names and repo.dirstate[src] == 'r':
2486 if src and src not in names and repo.dirstate[src] == 'r':
2481 dsremoved.add(src)
2487 dsremoved.add(src)
2482 names[src] = (repo.pathto(src, cwd), True)
2488 names[src] = (repo.pathto(src, cwd), True)
2483
2489
2484 ## computation of the action to performs on `names` content.
2490 ## computation of the action to performs on `names` content.
2485
2491
2486 def removeforget(abs):
2492 def removeforget(abs):
2487 if repo.dirstate[abs] == 'a':
2493 if repo.dirstate[abs] == 'a':
2488 return _('forgetting %s\n')
2494 return _('forgetting %s\n')
2489 return _('removing %s\n')
2495 return _('removing %s\n')
2490
2496
2491 # action to be actually performed by revert
2497 # action to be actually performed by revert
2492 # (<list of file>, message>) tuple
2498 # (<list of file>, message>) tuple
2493 actions = {'revert': ([], _('reverting %s\n')),
2499 actions = {'revert': ([], _('reverting %s\n')),
2494 'add': ([], _('adding %s\n')),
2500 'add': ([], _('adding %s\n')),
2495 'remove': ([], removeforget),
2501 'remove': ([], removeforget),
2496 'undelete': ([], _('undeleting %s\n')),
2502 'undelete': ([], _('undeleting %s\n')),
2497 'noop': (None, _('no changes needed to %s\n')),
2503 'noop': (None, _('no changes needed to %s\n')),
2498 'unknown': (None, _('file not managed: %s\n')),
2504 'unknown': (None, _('file not managed: %s\n')),
2499 }
2505 }
2500
2506
2501
2507
2502 # should we do a backup?
2508 # should we do a backup?
2503 backup = not opts.get('no_backup')
2509 backup = not opts.get('no_backup')
2504 discard = False
2510 discard = False
2505
2511
2506 disptable = (
2512 disptable = (
2507 # dispatch table:
2513 # dispatch table:
2508 # file state
2514 # file state
2509 # action
2515 # action
2510 # make backup
2516 # make backup
2511 (modified, actions['revert'], discard),
2517 (modified, actions['revert'], discard),
2512 (dsmodified, actions['revert'], backup),
2518 (dsmodified, actions['revert'], backup),
2513 (dsadded, actions['remove'], backup),
2519 (dsadded, actions['remove'], backup),
2514 (removed, actions['add'], backup),
2520 (removed, actions['add'], backup),
2515 (dsremoved, actions['undelete'], backup),
2521 (dsremoved, actions['undelete'], backup),
2516 (clean, actions['noop'], discard),
2522 (clean, actions['noop'], discard),
2517 (unknown, actions['unknown'], discard),
2523 (unknown, actions['unknown'], discard),
2518 )
2524 )
2519
2525
2520 for abs, (rel, exact) in sorted(names.items()):
2526 for abs, (rel, exact) in sorted(names.items()):
2521 # target file to be touch on disk (relative to cwd)
2527 # target file to be touch on disk (relative to cwd)
2522 target = repo.wjoin(abs)
2528 target = repo.wjoin(abs)
2523 # search the entry in the dispatch table.
2529 # search the entry in the dispatch table.
2524 # if the file is in any of these sets, it was touched in the working
2530 # if the file is in any of these sets, it was touched in the working
2525 # directory parent and we are sure it needs to be reverted.
2531 # directory parent and we are sure it needs to be reverted.
2526 for table, (xlist, msg), dobackup in disptable:
2532 for table, (xlist, msg), dobackup in disptable:
2527 if abs not in table:
2533 if abs not in table:
2528 continue
2534 continue
2529 if xlist is not None:
2535 if xlist is not None:
2530 xlist.append(abs)
2536 xlist.append(abs)
2531 if (dobackup and os.path.lexists(target) and
2537 if (dobackup and os.path.lexists(target) and
2532 abs in ctx and repo[None][abs].cmp(ctx[abs])):
2538 abs in ctx and repo[None][abs].cmp(ctx[abs])):
2533 bakname = "%s.orig" % rel
2539 bakname = "%s.orig" % rel
2534 ui.note(_('saving current version of %s as %s\n') %
2540 ui.note(_('saving current version of %s as %s\n') %
2535 (rel, bakname))
2541 (rel, bakname))
2536 if not opts.get('dry_run'):
2542 if not opts.get('dry_run'):
2537 util.rename(target, bakname)
2543 util.rename(target, bakname)
2538 if ui.verbose or not exact:
2544 if ui.verbose or not exact:
2539 if not isinstance(msg, basestring):
2545 if not isinstance(msg, basestring):
2540 msg = msg(abs)
2546 msg = msg(abs)
2541 ui.status(msg % rel)
2547 ui.status(msg % rel)
2542 elif exact:
2548 elif exact:
2543 ui.warn(msg % rel)
2549 ui.warn(msg % rel)
2544 break
2550 break
2545
2551
2546
2552
2547 if not opts.get('dry_run'):
2553 if not opts.get('dry_run'):
2548 _performrevert(repo, parents, ctx, actions)
2554 _performrevert(repo, parents, ctx, actions)
2549
2555
2550 if targetsubs:
2556 if targetsubs:
2551 # Revert the subrepos on the revert list
2557 # Revert the subrepos on the revert list
2552 for sub in targetsubs:
2558 for sub in targetsubs:
2553 ctx.sub(sub).revert(ui, ctx.substate[sub], *pats, **opts)
2559 ctx.sub(sub).revert(ui, ctx.substate[sub], *pats, **opts)
2554 finally:
2560 finally:
2555 wlock.release()
2561 wlock.release()
2556
2562
2557 def _performrevert(repo, parents, ctx, actions):
2563 def _performrevert(repo, parents, ctx, actions):
2558 """function that actually perform all the actions computed for revert
2564 """function that actually perform all the actions computed for revert
2559
2565
2560 This is an independent function to let extension to plug in and react to
2566 This is an independent function to let extension to plug in and react to
2561 the imminent revert.
2567 the imminent revert.
2562
2568
2563 Make sure you have the working directory locked when calling this function.
2569 Make sure you have the working directory locked when calling this function.
2564 """
2570 """
2565 parent, p2 = parents
2571 parent, p2 = parents
2566 node = ctx.node()
2572 node = ctx.node()
2567 def checkout(f):
2573 def checkout(f):
2568 fc = ctx[f]
2574 fc = ctx[f]
2569 repo.wwrite(f, fc.data(), fc.flags())
2575 repo.wwrite(f, fc.data(), fc.flags())
2570
2576
2571 audit_path = pathutil.pathauditor(repo.root)
2577 audit_path = pathutil.pathauditor(repo.root)
2572 for f in actions['remove'][0]:
2578 for f in actions['remove'][0]:
2573 if repo.dirstate[f] == 'a':
2579 if repo.dirstate[f] == 'a':
2574 repo.dirstate.drop(f)
2580 repo.dirstate.drop(f)
2575 continue
2581 continue
2576 audit_path(f)
2582 audit_path(f)
2577 try:
2583 try:
2578 util.unlinkpath(repo.wjoin(f))
2584 util.unlinkpath(repo.wjoin(f))
2579 except OSError:
2585 except OSError:
2580 pass
2586 pass
2581 repo.dirstate.remove(f)
2587 repo.dirstate.remove(f)
2582
2588
2583 normal = None
2589 normal = None
2584 if node == parent:
2590 if node == parent:
2585 # We're reverting to our parent. If possible, we'd like status
2591 # We're reverting to our parent. If possible, we'd like status
2586 # to report the file as clean. We have to use normallookup for
2592 # to report the file as clean. We have to use normallookup for
2587 # merges to avoid losing information about merged/dirty files.
2593 # merges to avoid losing information about merged/dirty files.
2588 if p2 != nullid:
2594 if p2 != nullid:
2589 normal = repo.dirstate.normallookup
2595 normal = repo.dirstate.normallookup
2590 else:
2596 else:
2591 normal = repo.dirstate.normal
2597 normal = repo.dirstate.normal
2592 for f in actions['revert'][0]:
2598 for f in actions['revert'][0]:
2593 checkout(f)
2599 checkout(f)
2594 if normal:
2600 if normal:
2595 normal(f)
2601 normal(f)
2596
2602
2597 for f in actions['add'][0]:
2603 for f in actions['add'][0]:
2598 checkout(f)
2604 checkout(f)
2599 repo.dirstate.add(f)
2605 repo.dirstate.add(f)
2600
2606
2601 normal = repo.dirstate.normallookup
2607 normal = repo.dirstate.normallookup
2602 if node == parent and p2 == nullid:
2608 if node == parent and p2 == nullid:
2603 normal = repo.dirstate.normal
2609 normal = repo.dirstate.normal
2604 for f in actions['undelete'][0]:
2610 for f in actions['undelete'][0]:
2605 checkout(f)
2611 checkout(f)
2606 normal(f)
2612 normal(f)
2607
2613
2608 copied = copies.pathcopies(repo[parent], ctx)
2614 copied = copies.pathcopies(repo[parent], ctx)
2609
2615
2610 for f in actions['add'][0] + actions['undelete'][0] + actions['revert'][0]:
2616 for f in actions['add'][0] + actions['undelete'][0] + actions['revert'][0]:
2611 if f in copied:
2617 if f in copied:
2612 repo.dirstate.copy(copied[f], f)
2618 repo.dirstate.copy(copied[f], f)
2613
2619
2614 def command(table):
2620 def command(table):
2615 """Returns a function object to be used as a decorator for making commands.
2621 """Returns a function object to be used as a decorator for making commands.
2616
2622
2617 This function receives a command table as its argument. The table should
2623 This function receives a command table as its argument. The table should
2618 be a dict.
2624 be a dict.
2619
2625
2620 The returned function can be used as a decorator for adding commands
2626 The returned function can be used as a decorator for adding commands
2621 to that command table. This function accepts multiple arguments to define
2627 to that command table. This function accepts multiple arguments to define
2622 a command.
2628 a command.
2623
2629
2624 The first argument is the command name.
2630 The first argument is the command name.
2625
2631
2626 The options argument is an iterable of tuples defining command arguments.
2632 The options argument is an iterable of tuples defining command arguments.
2627 See ``mercurial.fancyopts.fancyopts()`` for the format of each tuple.
2633 See ``mercurial.fancyopts.fancyopts()`` for the format of each tuple.
2628
2634
2629 The synopsis argument defines a short, one line summary of how to use the
2635 The synopsis argument defines a short, one line summary of how to use the
2630 command. This shows up in the help output.
2636 command. This shows up in the help output.
2631
2637
2632 The norepo argument defines whether the command does not require a
2638 The norepo argument defines whether the command does not require a
2633 local repository. Most commands operate against a repository, thus the
2639 local repository. Most commands operate against a repository, thus the
2634 default is False.
2640 default is False.
2635
2641
2636 The optionalrepo argument defines whether the command optionally requires
2642 The optionalrepo argument defines whether the command optionally requires
2637 a local repository.
2643 a local repository.
2638
2644
2639 The inferrepo argument defines whether to try to find a repository from the
2645 The inferrepo argument defines whether to try to find a repository from the
2640 command line arguments. If True, arguments will be examined for potential
2646 command line arguments. If True, arguments will be examined for potential
2641 repository locations. See ``findrepo()``. If a repository is found, it
2647 repository locations. See ``findrepo()``. If a repository is found, it
2642 will be used.
2648 will be used.
2643 """
2649 """
2644 def cmd(name, options=(), synopsis=None, norepo=False, optionalrepo=False,
2650 def cmd(name, options=(), synopsis=None, norepo=False, optionalrepo=False,
2645 inferrepo=False):
2651 inferrepo=False):
2646 def decorator(func):
2652 def decorator(func):
2647 if synopsis:
2653 if synopsis:
2648 table[name] = func, list(options), synopsis
2654 table[name] = func, list(options), synopsis
2649 else:
2655 else:
2650 table[name] = func, list(options)
2656 table[name] = func, list(options)
2651
2657
2652 if norepo:
2658 if norepo:
2653 # Avoid import cycle.
2659 # Avoid import cycle.
2654 import commands
2660 import commands
2655 commands.norepo += ' %s' % ' '.join(parsealiases(name))
2661 commands.norepo += ' %s' % ' '.join(parsealiases(name))
2656
2662
2657 if optionalrepo:
2663 if optionalrepo:
2658 import commands
2664 import commands
2659 commands.optionalrepo += ' %s' % ' '.join(parsealiases(name))
2665 commands.optionalrepo += ' %s' % ' '.join(parsealiases(name))
2660
2666
2661 if inferrepo:
2667 if inferrepo:
2662 import commands
2668 import commands
2663 commands.inferrepo += ' %s' % ' '.join(parsealiases(name))
2669 commands.inferrepo += ' %s' % ' '.join(parsealiases(name))
2664
2670
2665 return func
2671 return func
2666 return decorator
2672 return decorator
2667
2673
2668 return cmd
2674 return cmd
2669
2675
2670 # a list of (ui, repo, otherpeer, opts, missing) functions called by
2676 # a list of (ui, repo, otherpeer, opts, missing) functions called by
2671 # commands.outgoing. "missing" is "missing" of the result of
2677 # commands.outgoing. "missing" is "missing" of the result of
2672 # "findcommonoutgoing()"
2678 # "findcommonoutgoing()"
2673 outgoinghooks = util.hooks()
2679 outgoinghooks = util.hooks()
2674
2680
2675 # a list of (ui, repo) functions called by commands.summary
2681 # a list of (ui, repo) functions called by commands.summary
2676 summaryhooks = util.hooks()
2682 summaryhooks = util.hooks()
2677
2683
2678 # a list of (ui, repo, opts, changes) functions called by commands.summary.
2684 # a list of (ui, repo, opts, changes) functions called by commands.summary.
2679 #
2685 #
2680 # functions should return tuple of booleans below, if 'changes' is None:
2686 # functions should return tuple of booleans below, if 'changes' is None:
2681 # (whether-incomings-are-needed, whether-outgoings-are-needed)
2687 # (whether-incomings-are-needed, whether-outgoings-are-needed)
2682 #
2688 #
2683 # otherwise, 'changes' is a tuple of tuples below:
2689 # otherwise, 'changes' is a tuple of tuples below:
2684 # - (sourceurl, sourcebranch, sourcepeer, incoming)
2690 # - (sourceurl, sourcebranch, sourcepeer, incoming)
2685 # - (desturl, destbranch, destpeer, outgoing)
2691 # - (desturl, destbranch, destpeer, outgoing)
2686 summaryremotehooks = util.hooks()
2692 summaryremotehooks = util.hooks()
2687
2693
2688 # A list of state files kept by multistep operations like graft.
2694 # A list of state files kept by multistep operations like graft.
2689 # Since graft cannot be aborted, it is considered 'clearable' by update.
2695 # Since graft cannot be aborted, it is considered 'clearable' by update.
2690 # note: bisect is intentionally excluded
2696 # note: bisect is intentionally excluded
2691 # (state file, clearable, allowcommit, error, hint)
2697 # (state file, clearable, allowcommit, error, hint)
2692 unfinishedstates = [
2698 unfinishedstates = [
2693 ('graftstate', True, False, _('graft in progress'),
2699 ('graftstate', True, False, _('graft in progress'),
2694 _("use 'hg graft --continue' or 'hg update' to abort")),
2700 _("use 'hg graft --continue' or 'hg update' to abort")),
2695 ('updatestate', True, False, _('last update was interrupted'),
2701 ('updatestate', True, False, _('last update was interrupted'),
2696 _("use 'hg update' to get a consistent checkout"))
2702 _("use 'hg update' to get a consistent checkout"))
2697 ]
2703 ]
2698
2704
2699 def checkunfinished(repo, commit=False):
2705 def checkunfinished(repo, commit=False):
2700 '''Look for an unfinished multistep operation, like graft, and abort
2706 '''Look for an unfinished multistep operation, like graft, and abort
2701 if found. It's probably good to check this right before
2707 if found. It's probably good to check this right before
2702 bailifchanged().
2708 bailifchanged().
2703 '''
2709 '''
2704 for f, clearable, allowcommit, msg, hint in unfinishedstates:
2710 for f, clearable, allowcommit, msg, hint in unfinishedstates:
2705 if commit and allowcommit:
2711 if commit and allowcommit:
2706 continue
2712 continue
2707 if repo.vfs.exists(f):
2713 if repo.vfs.exists(f):
2708 raise util.Abort(msg, hint=hint)
2714 raise util.Abort(msg, hint=hint)
2709
2715
2710 def clearunfinished(repo):
2716 def clearunfinished(repo):
2711 '''Check for unfinished operations (as above), and clear the ones
2717 '''Check for unfinished operations (as above), and clear the ones
2712 that are clearable.
2718 that are clearable.
2713 '''
2719 '''
2714 for f, clearable, allowcommit, msg, hint in unfinishedstates:
2720 for f, clearable, allowcommit, msg, hint in unfinishedstates:
2715 if not clearable and repo.vfs.exists(f):
2721 if not clearable and repo.vfs.exists(f):
2716 raise util.Abort(msg, hint=hint)
2722 raise util.Abort(msg, hint=hint)
2717 for f, clearable, allowcommit, msg, hint in unfinishedstates:
2723 for f, clearable, allowcommit, msg, hint in unfinishedstates:
2718 if clearable and repo.vfs.exists(f):
2724 if clearable and repo.vfs.exists(f):
2719 util.unlink(repo.join(f))
2725 util.unlink(repo.join(f))
@@ -1,280 +1,299 b''
1 $ echo "[extensions]" >> $HGRCPATH
1 $ echo "[extensions]" >> $HGRCPATH
2 $ echo "purge=" >> $HGRCPATH
2 $ echo "purge=" >> $HGRCPATH
3
3
4 $ shortlog() {
4 $ shortlog() {
5 > hg log -G --template '{rev}:{node|short} {author} {date|hgdate} - {branch} - {desc|firstline}\n'
5 > hg log -G --template '{rev}:{node|short} {author} {date|hgdate} - {branch} - {desc|firstline}\n'
6 > }
6 > }
7
7
8 Test --bypass with other options
8 Test --bypass with other options
9
9
10 $ hg init repo-options
10 $ hg init repo-options
11 $ cd repo-options
11 $ cd repo-options
12 $ echo a > a
12 $ echo a > a
13 $ hg ci -Am adda
13 $ hg ci -Am adda
14 adding a
14 adding a
15 $ echo a >> a
15 $ echo a >> a
16 $ hg branch foo
16 $ hg branch foo
17 marked working directory as branch foo
17 marked working directory as branch foo
18 (branches are permanent and global, did you want a bookmark?)
18 (branches are permanent and global, did you want a bookmark?)
19 $ hg ci -Am changea
19 $ hg ci -Am changea
20 $ hg export . > ../test.diff
20 $ hg export . > ../test.diff
21 $ hg up null
21 $ hg up null
22 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
22 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
23
23
24 Test importing an existing revision
24 Test importing an existing revision
25 (this also tests that "hg import" disallows combination of '--exact'
25 (this also tests that "hg import" disallows combination of '--exact'
26 and '--edit')
26 and '--edit')
27
27
28 $ hg import --bypass --exact --edit ../test.diff
28 $ hg import --bypass --exact --edit ../test.diff
29 abort: cannot use --exact with --edit
29 abort: cannot use --exact with --edit
30 [255]
30 [255]
31 $ hg import --bypass --exact ../test.diff
31 $ hg import --bypass --exact ../test.diff
32 applying ../test.diff
32 applying ../test.diff
33 $ shortlog
33 $ shortlog
34 o 1:4e322f7ce8e3 test 0 0 - foo - changea
34 o 1:4e322f7ce8e3 test 0 0 - foo - changea
35 |
35 |
36 o 0:07f494440405 test 0 0 - default - adda
36 o 0:07f494440405 test 0 0 - default - adda
37
37
38
38
39 Test failure without --exact
39 Test failure without --exact
40
40
41 $ hg import --bypass ../test.diff
41 $ hg import --bypass ../test.diff
42 applying ../test.diff
42 applying ../test.diff
43 unable to find 'a' for patching
43 unable to find 'a' for patching
44 abort: patch failed to apply
44 abort: patch failed to apply
45 [255]
45 [255]
46 $ hg st
46 $ hg st
47 $ shortlog
47 $ shortlog
48 o 1:4e322f7ce8e3 test 0 0 - foo - changea
48 o 1:4e322f7ce8e3 test 0 0 - foo - changea
49 |
49 |
50 o 0:07f494440405 test 0 0 - default - adda
50 o 0:07f494440405 test 0 0 - default - adda
51
51
52
52
53 Test --user, --date and --message
53 Test --user, --date and --message
54
54
55 $ hg up 0
55 $ hg up 0
56 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
56 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
57 $ hg import --bypass --u test2 -d '1 0' -m patch2 ../test.diff
57 $ hg import --bypass --u test2 -d '1 0' -m patch2 ../test.diff
58 applying ../test.diff
58 applying ../test.diff
59 $ cat .hg/last-message.txt
59 $ cat .hg/last-message.txt
60 patch2 (no-eol)
60 patch2 (no-eol)
61 $ shortlog
61 $ shortlog
62 o 2:2e127d1da504 test2 1 0 - default - patch2
62 o 2:2e127d1da504 test2 1 0 - default - patch2
63 |
63 |
64 | o 1:4e322f7ce8e3 test 0 0 - foo - changea
64 | o 1:4e322f7ce8e3 test 0 0 - foo - changea
65 |/
65 |/
66 @ 0:07f494440405 test 0 0 - default - adda
66 @ 0:07f494440405 test 0 0 - default - adda
67
67
68 $ hg rollback
68 $ hg rollback
69 repository tip rolled back to revision 1 (undo import)
69 repository tip rolled back to revision 1 (undo import)
70
70
71 Test --import-branch
71 Test --import-branch
72 (this also tests that editor is not invoked for '--bypass', if the
72 (this also tests that editor is not invoked for '--bypass', if the
73 patch contains the commit message, regardless of '--edit')
73 patch contains the commit message, regardless of '--edit')
74
74
75 $ HGEDITOR=cat hg import --bypass --import-branch --edit ../test.diff
75 $ HGEDITOR=cat hg import --bypass --import-branch --edit ../test.diff
76 applying ../test.diff
76 applying ../test.diff
77 $ shortlog
77 $ shortlog
78 o 1:4e322f7ce8e3 test 0 0 - foo - changea
78 o 1:4e322f7ce8e3 test 0 0 - foo - changea
79 |
79 |
80 @ 0:07f494440405 test 0 0 - default - adda
80 @ 0:07f494440405 test 0 0 - default - adda
81
81
82 $ hg rollback
82 $ hg rollback
83 repository tip rolled back to revision 1 (undo import)
83 repository tip rolled back to revision 1 (undo import)
84
84
85 Test --strip
85 Test --strip
86
86
87 $ hg import --bypass --strip 0 - <<EOF
87 $ hg import --bypass --strip 0 - <<EOF
88 > # HG changeset patch
88 > # HG changeset patch
89 > # User test
89 > # User test
90 > # Date 0 0
90 > # Date 0 0
91 > # Branch foo
91 > # Branch foo
92 > # Node ID 4e322f7ce8e3e4203950eac9ece27bf7e45ffa6c
92 > # Node ID 4e322f7ce8e3e4203950eac9ece27bf7e45ffa6c
93 > # Parent 07f4944404050f47db2e5c5071e0e84e7a27bba9
93 > # Parent 07f4944404050f47db2e5c5071e0e84e7a27bba9
94 > changea
94 > changea
95 >
95 >
96 > diff -r 07f494440405 -r 4e322f7ce8e3 a
96 > diff -r 07f494440405 -r 4e322f7ce8e3 a
97 > --- a Thu Jan 01 00:00:00 1970 +0000
97 > --- a Thu Jan 01 00:00:00 1970 +0000
98 > +++ a Thu Jan 01 00:00:00 1970 +0000
98 > +++ a Thu Jan 01 00:00:00 1970 +0000
99 > @@ -1,1 +1,2 @@
99 > @@ -1,1 +1,2 @@
100 > a
100 > a
101 > +a
101 > +a
102 > EOF
102 > EOF
103 applying patch from stdin
103 applying patch from stdin
104 $ hg rollback
104 $ hg rollback
105 repository tip rolled back to revision 1 (undo import)
105 repository tip rolled back to revision 1 (undo import)
106
106
107 Test unsupported combinations
107 Test unsupported combinations
108
108
109 $ hg import --bypass --no-commit ../test.diff
109 $ hg import --bypass --no-commit ../test.diff
110 abort: cannot use --no-commit with --bypass
110 abort: cannot use --no-commit with --bypass
111 [255]
111 [255]
112 $ hg import --bypass --similarity 50 ../test.diff
112 $ hg import --bypass --similarity 50 ../test.diff
113 abort: cannot use --similarity with --bypass
113 abort: cannot use --similarity with --bypass
114 [255]
114 [255]
115
115
116 Test commit editor
116 Test commit editor
117 (this also tests that editor is invoked, if the patch doesn't contain
117 (this also tests that editor is invoked, if the patch doesn't contain
118 the commit message, regardless of '--edit')
118 the commit message, regardless of '--edit')
119
119
120 $ cat > ../test.diff <<EOF
120 $ cat > ../test.diff <<EOF
121 > diff -r 07f494440405 -r 4e322f7ce8e3 a
121 > diff -r 07f494440405 -r 4e322f7ce8e3 a
122 > --- a/a Thu Jan 01 00:00:00 1970 +0000
122 > --- a/a Thu Jan 01 00:00:00 1970 +0000
123 > +++ b/a Thu Jan 01 00:00:00 1970 +0000
123 > +++ b/a Thu Jan 01 00:00:00 1970 +0000
124 > @@ -1,1 +1,2 @@
124 > @@ -1,1 +1,2 @@
125 > -a
125 > -a
126 > +b
126 > +b
127 > +c
127 > +c
128 > EOF
128 > EOF
129 $ HGEDITOR=cat hg import --bypass ../test.diff
129 $ HGEDITOR=cat hg import --bypass ../test.diff
130 applying ../test.diff
130 applying ../test.diff
131
131
132
132
133 HG: Enter commit message. Lines beginning with 'HG:' are removed.
133 HG: Enter commit message. Lines beginning with 'HG:' are removed.
134 HG: Leave message empty to abort commit.
134 HG: Leave message empty to abort commit.
135 HG: --
135 HG: --
136 HG: user: test
136 HG: user: test
137 HG: branch 'default'
137 HG: branch 'default'
138 HG: changed a
138 HG: changed a
139 abort: empty commit message
139 abort: empty commit message
140 [255]
140 [255]
141
141
142 Test patch.eol is handled
142 Test patch.eol is handled
143 (this also tests that editor is not invoked for '--bypass', if the
143 (this also tests that editor is not invoked for '--bypass', if the
144 commit message is explicitly specified, regardless of '--edit')
144 commit message is explicitly specified, regardless of '--edit')
145
145
146 $ python -c 'file("a", "wb").write("a\r\n")'
146 $ python -c 'file("a", "wb").write("a\r\n")'
147 $ hg ci -m makeacrlf
147 $ hg ci -m makeacrlf
148 $ HGEDITOR=cat hg import -m 'should fail because of eol' --edit --bypass ../test.diff
148 $ HGEDITOR=cat hg import -m 'should fail because of eol' --edit --bypass ../test.diff
149 applying ../test.diff
149 applying ../test.diff
150 patching file a
150 patching file a
151 Hunk #1 FAILED at 0
151 Hunk #1 FAILED at 0
152 abort: patch failed to apply
152 abort: patch failed to apply
153 [255]
153 [255]
154 $ hg --config patch.eol=auto import -d '0 0' -m 'test patch.eol' --bypass ../test.diff
154 $ hg --config patch.eol=auto import -d '0 0' -m 'test patch.eol' --bypass ../test.diff
155 applying ../test.diff
155 applying ../test.diff
156 $ shortlog
156 $ shortlog
157 o 3:c606edafba99 test 0 0 - default - test patch.eol
157 o 3:c606edafba99 test 0 0 - default - test patch.eol
158 |
158 |
159 @ 2:872023de769d test 0 0 - default - makeacrlf
159 @ 2:872023de769d test 0 0 - default - makeacrlf
160 |
160 |
161 | o 1:4e322f7ce8e3 test 0 0 - foo - changea
161 | o 1:4e322f7ce8e3 test 0 0 - foo - changea
162 |/
162 |/
163 o 0:07f494440405 test 0 0 - default - adda
163 o 0:07f494440405 test 0 0 - default - adda
164
164
165
165
166 Test applying multiple patches
166 Test applying multiple patches
167
167
168 $ hg up -qC 0
168 $ hg up -qC 0
169 $ echo e > e
169 $ echo e > e
170 $ hg ci -Am adde
170 $ hg ci -Am adde
171 adding e
171 adding e
172 created new head
172 created new head
173 $ hg export . > ../patch1.diff
173 $ hg export . > ../patch1.diff
174 $ hg up -qC 1
174 $ hg up -qC 1
175 $ echo f > f
175 $ echo f > f
176 $ hg ci -Am addf
176 $ hg ci -Am addf
177 adding f
177 adding f
178 $ hg export . > ../patch2.diff
178 $ hg export . > ../patch2.diff
179 $ cd ..
179 $ cd ..
180 $ hg clone -r1 repo-options repo-multi1
180 $ hg clone -r1 repo-options repo-multi1
181 adding changesets
181 adding changesets
182 adding manifests
182 adding manifests
183 adding file changes
183 adding file changes
184 added 2 changesets with 2 changes to 1 files
184 added 2 changesets with 2 changes to 1 files
185 updating to branch foo
185 updating to branch foo
186 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
186 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
187 $ cd repo-multi1
187 $ cd repo-multi1
188 $ hg up 0
188 $ hg up 0
189 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
189 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
190 $ hg import --bypass ../patch1.diff ../patch2.diff
190 $ hg import --bypass ../patch1.diff ../patch2.diff
191 applying ../patch1.diff
191 applying ../patch1.diff
192 applying ../patch2.diff
192 applying ../patch2.diff
193 $ shortlog
193 $ shortlog
194 o 3:bc8ca3f8a7c4 test 0 0 - default - addf
194 o 3:bc8ca3f8a7c4 test 0 0 - default - addf
195 |
195 |
196 o 2:16581080145e test 0 0 - default - adde
196 o 2:16581080145e test 0 0 - default - adde
197 |
197 |
198 | o 1:4e322f7ce8e3 test 0 0 - foo - changea
198 | o 1:4e322f7ce8e3 test 0 0 - foo - changea
199 |/
199 |/
200 @ 0:07f494440405 test 0 0 - default - adda
200 @ 0:07f494440405 test 0 0 - default - adda
201
201
202
202
203 Test applying multiple patches with --exact
203 Test applying multiple patches with --exact
204
204
205 $ cd ..
205 $ cd ..
206 $ hg clone -r1 repo-options repo-multi2
206 $ hg clone -r1 repo-options repo-multi2
207 adding changesets
207 adding changesets
208 adding manifests
208 adding manifests
209 adding file changes
209 adding file changes
210 added 2 changesets with 2 changes to 1 files
210 added 2 changesets with 2 changes to 1 files
211 updating to branch foo
211 updating to branch foo
212 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
212 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
213 $ cd repo-multi2
213 $ cd repo-multi2
214 $ hg import --bypass --exact ../patch1.diff ../patch2.diff
214 $ hg import --bypass --exact ../patch1.diff ../patch2.diff
215 applying ../patch1.diff
215 applying ../patch1.diff
216 applying ../patch2.diff
216 applying ../patch2.diff
217 $ shortlog
217 $ shortlog
218 o 3:d60cb8989666 test 0 0 - foo - addf
218 o 3:d60cb8989666 test 0 0 - foo - addf
219 |
219 |
220 | o 2:16581080145e test 0 0 - default - adde
220 | o 2:16581080145e test 0 0 - default - adde
221 | |
221 | |
222 @ | 1:4e322f7ce8e3 test 0 0 - foo - changea
222 @ | 1:4e322f7ce8e3 test 0 0 - foo - changea
223 |/
223 |/
224 o 0:07f494440405 test 0 0 - default - adda
224 o 0:07f494440405 test 0 0 - default - adda
225
225
226
226
227 $ cd ..
227 $ cd ..
228
228
229 Test avoiding editor invocation at applying the patch with --exact
230 even if commit message is empty
231
232 $ cd repo-options
233
234 $ echo a >> a
235 $ hg commit -m ' '
236 $ hg tip -T "{node}\n"
237 1b77bc7d1db9f0e7f1716d515b630516ab386c89
238 $ hg export -o ../empty-log.diff .
239 $ hg update -q -C ".^1"
240 $ hg --config extensions.strip= strip -q tip
241 $ HGEDITOR=cat hg import --exact --bypass ../empty-log.diff
242 applying ../empty-log.diff
243 $ hg tip -T "{node}\n"
244 1b77bc7d1db9f0e7f1716d515b630516ab386c89
245
246 $ cd ..
247
229 #if symlink execbit
248 #if symlink execbit
230
249
231 Test complicated patch with --exact
250 Test complicated patch with --exact
232
251
233 $ hg init repo-exact
252 $ hg init repo-exact
234 $ cd repo-exact
253 $ cd repo-exact
235 $ echo a > a
254 $ echo a > a
236 $ echo c > c
255 $ echo c > c
237 $ echo d > d
256 $ echo d > d
238 $ echo e > e
257 $ echo e > e
239 $ echo f > f
258 $ echo f > f
240 $ chmod +x f
259 $ chmod +x f
241 $ ln -s c linkc
260 $ ln -s c linkc
242 $ hg ci -Am t
261 $ hg ci -Am t
243 adding a
262 adding a
244 adding c
263 adding c
245 adding d
264 adding d
246 adding e
265 adding e
247 adding f
266 adding f
248 adding linkc
267 adding linkc
249 $ hg cp a aa1
268 $ hg cp a aa1
250 $ echo b >> a
269 $ echo b >> a
251 $ echo b > b
270 $ echo b > b
252 $ hg add b
271 $ hg add b
253 $ hg cp a aa2
272 $ hg cp a aa2
254 $ echo aa >> aa2
273 $ echo aa >> aa2
255 $ chmod +x e
274 $ chmod +x e
256 $ chmod -x f
275 $ chmod -x f
257 $ ln -s a linka
276 $ ln -s a linka
258 $ hg rm d
277 $ hg rm d
259 $ hg rm linkc
278 $ hg rm linkc
260 $ hg mv c cc
279 $ hg mv c cc
261 $ hg ci -m patch
280 $ hg ci -m patch
262 $ hg export --git . > ../test.diff
281 $ hg export --git . > ../test.diff
263 $ hg up -C null
282 $ hg up -C null
264 0 files updated, 0 files merged, 7 files removed, 0 files unresolved
283 0 files updated, 0 files merged, 7 files removed, 0 files unresolved
265 $ hg purge
284 $ hg purge
266 $ hg st
285 $ hg st
267 $ hg import --bypass --exact ../test.diff
286 $ hg import --bypass --exact ../test.diff
268 applying ../test.diff
287 applying ../test.diff
269
288
270 The patch should have matched the exported revision and generated no additional
289 The patch should have matched the exported revision and generated no additional
271 data. If not, diff both heads to debug it.
290 data. If not, diff both heads to debug it.
272
291
273 $ shortlog
292 $ shortlog
274 o 1:2978fd5c8aa4 test 0 0 - default - patch
293 o 1:2978fd5c8aa4 test 0 0 - default - patch
275 |
294 |
276 o 0:a0e19e636a43 test 0 0 - default - t
295 o 0:a0e19e636a43 test 0 0 - default - t
277
296
278 #endif
297 #endif
279
298
280 $ cd ..
299 $ cd ..
@@ -1,1457 +1,1473 b''
1 $ hg init a
1 $ hg init a
2 $ mkdir a/d1
2 $ mkdir a/d1
3 $ mkdir a/d1/d2
3 $ mkdir a/d1/d2
4 $ echo line 1 > a/a
4 $ echo line 1 > a/a
5 $ echo line 1 > a/d1/d2/a
5 $ echo line 1 > a/d1/d2/a
6 $ hg --cwd a ci -Ama
6 $ hg --cwd a ci -Ama
7 adding a
7 adding a
8 adding d1/d2/a
8 adding d1/d2/a
9
9
10 $ echo line 2 >> a/a
10 $ echo line 2 >> a/a
11 $ hg --cwd a ci -u someone -d '1 0' -m'second change'
11 $ hg --cwd a ci -u someone -d '1 0' -m'second change'
12
12
13 import with no args:
13 import with no args:
14
14
15 $ hg --cwd a import
15 $ hg --cwd a import
16 abort: need at least one patch to import
16 abort: need at least one patch to import
17 [255]
17 [255]
18
18
19 generate patches for the test
19 generate patches for the test
20
20
21 $ hg --cwd a export tip > exported-tip.patch
21 $ hg --cwd a export tip > exported-tip.patch
22 $ hg --cwd a diff -r0:1 > diffed-tip.patch
22 $ hg --cwd a diff -r0:1 > diffed-tip.patch
23
23
24
24
25 import exported patch
25 import exported patch
26 (this also tests that editor is not invoked, if the patch contains the
26 (this also tests that editor is not invoked, if the patch contains the
27 commit message and '--edit' is not specified)
27 commit message and '--edit' is not specified)
28
28
29 $ hg clone -r0 a b
29 $ hg clone -r0 a b
30 adding changesets
30 adding changesets
31 adding manifests
31 adding manifests
32 adding file changes
32 adding file changes
33 added 1 changesets with 2 changes to 2 files
33 added 1 changesets with 2 changes to 2 files
34 updating to branch default
34 updating to branch default
35 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
35 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
36 $ HGEDITOR=cat hg --cwd b import ../exported-tip.patch
36 $ HGEDITOR=cat hg --cwd b import ../exported-tip.patch
37 applying ../exported-tip.patch
37 applying ../exported-tip.patch
38
38
39 message and committer and date should be same
39 message and committer and date should be same
40
40
41 $ hg --cwd b tip
41 $ hg --cwd b tip
42 changeset: 1:1d4bd90af0e4
42 changeset: 1:1d4bd90af0e4
43 tag: tip
43 tag: tip
44 user: someone
44 user: someone
45 date: Thu Jan 01 00:00:01 1970 +0000
45 date: Thu Jan 01 00:00:01 1970 +0000
46 summary: second change
46 summary: second change
47
47
48 $ rm -r b
48 $ rm -r b
49
49
50
50
51 import exported patch with external patcher
51 import exported patch with external patcher
52 (this also tests that editor is invoked, if the '--edit' is specified,
52 (this also tests that editor is invoked, if the '--edit' is specified,
53 regardless of the commit message in the patch)
53 regardless of the commit message in the patch)
54
54
55 $ cat > dummypatch.py <<EOF
55 $ cat > dummypatch.py <<EOF
56 > print 'patching file a'
56 > print 'patching file a'
57 > file('a', 'wb').write('line2\n')
57 > file('a', 'wb').write('line2\n')
58 > EOF
58 > EOF
59 $ hg clone -r0 a b
59 $ hg clone -r0 a b
60 adding changesets
60 adding changesets
61 adding manifests
61 adding manifests
62 adding file changes
62 adding file changes
63 added 1 changesets with 2 changes to 2 files
63 added 1 changesets with 2 changes to 2 files
64 updating to branch default
64 updating to branch default
65 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
65 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
66 $ HGEDITOR=cat hg --config ui.patch='python ../dummypatch.py' --cwd b import --edit ../exported-tip.patch
66 $ HGEDITOR=cat hg --config ui.patch='python ../dummypatch.py' --cwd b import --edit ../exported-tip.patch
67 applying ../exported-tip.patch
67 applying ../exported-tip.patch
68 second change
68 second change
69
69
70
70
71 HG: Enter commit message. Lines beginning with 'HG:' are removed.
71 HG: Enter commit message. Lines beginning with 'HG:' are removed.
72 HG: Leave message empty to abort commit.
72 HG: Leave message empty to abort commit.
73 HG: --
73 HG: --
74 HG: user: someone
74 HG: user: someone
75 HG: branch 'default'
75 HG: branch 'default'
76 HG: changed a
76 HG: changed a
77 $ cat b/a
77 $ cat b/a
78 line2
78 line2
79 $ rm -r b
79 $ rm -r b
80
80
81
81
82 import of plain diff should fail without message
82 import of plain diff should fail without message
83 (this also tests that editor is invoked, if the patch doesn't contain
83 (this also tests that editor is invoked, if the patch doesn't contain
84 the commit message, regardless of '--edit')
84 the commit message, regardless of '--edit')
85
85
86 $ hg clone -r0 a b
86 $ hg clone -r0 a b
87 adding changesets
87 adding changesets
88 adding manifests
88 adding manifests
89 adding file changes
89 adding file changes
90 added 1 changesets with 2 changes to 2 files
90 added 1 changesets with 2 changes to 2 files
91 updating to branch default
91 updating to branch default
92 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
92 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
93 $ cat > $TESTTMP/editor.sh <<EOF
93 $ cat > $TESTTMP/editor.sh <<EOF
94 > env | grep HGEDITFORM
94 > env | grep HGEDITFORM
95 > cat \$1
95 > cat \$1
96 > EOF
96 > EOF
97 $ HGEDITOR="sh $TESTTMP/editor.sh" hg --cwd b import ../diffed-tip.patch
97 $ HGEDITOR="sh $TESTTMP/editor.sh" hg --cwd b import ../diffed-tip.patch
98 applying ../diffed-tip.patch
98 applying ../diffed-tip.patch
99 HGEDITFORM=import.normal.normal
99 HGEDITFORM=import.normal.normal
100
100
101
101
102 HG: Enter commit message. Lines beginning with 'HG:' are removed.
102 HG: Enter commit message. Lines beginning with 'HG:' are removed.
103 HG: Leave message empty to abort commit.
103 HG: Leave message empty to abort commit.
104 HG: --
104 HG: --
105 HG: user: test
105 HG: user: test
106 HG: branch 'default'
106 HG: branch 'default'
107 HG: changed a
107 HG: changed a
108 abort: empty commit message
108 abort: empty commit message
109 [255]
109 [255]
110
111 Test avoiding editor invocation at applying the patch with --exact,
112 even if commit message is empty
113
114 $ echo a >> b/a
115 $ hg --cwd b commit -m ' '
116 $ hg --cwd b tip -T "{node}\n"
117 d8804f3f5396d800812f579c8452796a5993bdb2
118 $ hg --cwd b export -o ../empty-log.diff .
119 $ hg --cwd b update -q -C ".^1"
120 $ hg --cwd b --config extensions.strip= strip -q tip
121 $ HGEDITOR=cat hg --cwd b import --exact ../empty-log.diff
122 applying ../empty-log.diff
123 $ hg --cwd b tip -T "{node}\n"
124 d8804f3f5396d800812f579c8452796a5993bdb2
125
110 $ rm -r b
126 $ rm -r b
111
127
112
128
113 import of plain diff should be ok with message
129 import of plain diff should be ok with message
114
130
115 $ hg clone -r0 a b
131 $ hg clone -r0 a b
116 adding changesets
132 adding changesets
117 adding manifests
133 adding manifests
118 adding file changes
134 adding file changes
119 added 1 changesets with 2 changes to 2 files
135 added 1 changesets with 2 changes to 2 files
120 updating to branch default
136 updating to branch default
121 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
137 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
122 $ hg --cwd b import -mpatch ../diffed-tip.patch
138 $ hg --cwd b import -mpatch ../diffed-tip.patch
123 applying ../diffed-tip.patch
139 applying ../diffed-tip.patch
124 $ rm -r b
140 $ rm -r b
125
141
126
142
127 import of plain diff with specific date and user
143 import of plain diff with specific date and user
128 (this also tests that editor is not invoked, if
144 (this also tests that editor is not invoked, if
129 '--message'/'--logfile' is specified and '--edit' is not)
145 '--message'/'--logfile' is specified and '--edit' is not)
130
146
131 $ hg clone -r0 a b
147 $ hg clone -r0 a b
132 adding changesets
148 adding changesets
133 adding manifests
149 adding manifests
134 adding file changes
150 adding file changes
135 added 1 changesets with 2 changes to 2 files
151 added 1 changesets with 2 changes to 2 files
136 updating to branch default
152 updating to branch default
137 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
153 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
138 $ hg --cwd b import -mpatch -d '1 0' -u 'user@nowhere.net' ../diffed-tip.patch
154 $ hg --cwd b import -mpatch -d '1 0' -u 'user@nowhere.net' ../diffed-tip.patch
139 applying ../diffed-tip.patch
155 applying ../diffed-tip.patch
140 $ hg -R b tip -pv
156 $ hg -R b tip -pv
141 changeset: 1:ca68f19f3a40
157 changeset: 1:ca68f19f3a40
142 tag: tip
158 tag: tip
143 user: user@nowhere.net
159 user: user@nowhere.net
144 date: Thu Jan 01 00:00:01 1970 +0000
160 date: Thu Jan 01 00:00:01 1970 +0000
145 files: a
161 files: a
146 description:
162 description:
147 patch
163 patch
148
164
149
165
150 diff -r 80971e65b431 -r ca68f19f3a40 a
166 diff -r 80971e65b431 -r ca68f19f3a40 a
151 --- a/a Thu Jan 01 00:00:00 1970 +0000
167 --- a/a Thu Jan 01 00:00:00 1970 +0000
152 +++ b/a Thu Jan 01 00:00:01 1970 +0000
168 +++ b/a Thu Jan 01 00:00:01 1970 +0000
153 @@ -1,1 +1,2 @@
169 @@ -1,1 +1,2 @@
154 line 1
170 line 1
155 +line 2
171 +line 2
156
172
157 $ rm -r b
173 $ rm -r b
158
174
159
175
160 import of plain diff should be ok with --no-commit
176 import of plain diff should be ok with --no-commit
161 (this also tests that editor is not invoked, if '--no-commit' is
177 (this also tests that editor is not invoked, if '--no-commit' is
162 specified, regardless of '--edit')
178 specified, regardless of '--edit')
163
179
164 $ hg clone -r0 a b
180 $ hg clone -r0 a b
165 adding changesets
181 adding changesets
166 adding manifests
182 adding manifests
167 adding file changes
183 adding file changes
168 added 1 changesets with 2 changes to 2 files
184 added 1 changesets with 2 changes to 2 files
169 updating to branch default
185 updating to branch default
170 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
186 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
171 $ HGEDITOR=cat hg --cwd b import --no-commit --edit ../diffed-tip.patch
187 $ HGEDITOR=cat hg --cwd b import --no-commit --edit ../diffed-tip.patch
172 applying ../diffed-tip.patch
188 applying ../diffed-tip.patch
173 $ hg --cwd b diff --nodates
189 $ hg --cwd b diff --nodates
174 diff -r 80971e65b431 a
190 diff -r 80971e65b431 a
175 --- a/a
191 --- a/a
176 +++ b/a
192 +++ b/a
177 @@ -1,1 +1,2 @@
193 @@ -1,1 +1,2 @@
178 line 1
194 line 1
179 +line 2
195 +line 2
180 $ rm -r b
196 $ rm -r b
181
197
182
198
183 import of malformed plain diff should fail
199 import of malformed plain diff should fail
184
200
185 $ hg clone -r0 a b
201 $ hg clone -r0 a b
186 adding changesets
202 adding changesets
187 adding manifests
203 adding manifests
188 adding file changes
204 adding file changes
189 added 1 changesets with 2 changes to 2 files
205 added 1 changesets with 2 changes to 2 files
190 updating to branch default
206 updating to branch default
191 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
207 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
192 $ sed 's/1,1/foo/' < diffed-tip.patch > broken.patch
208 $ sed 's/1,1/foo/' < diffed-tip.patch > broken.patch
193 $ hg --cwd b import -mpatch ../broken.patch
209 $ hg --cwd b import -mpatch ../broken.patch
194 applying ../broken.patch
210 applying ../broken.patch
195 abort: bad hunk #1
211 abort: bad hunk #1
196 [255]
212 [255]
197 $ rm -r b
213 $ rm -r b
198
214
199
215
200 hg -R repo import
216 hg -R repo import
201 put the clone in a subdir - having a directory named "a"
217 put the clone in a subdir - having a directory named "a"
202 used to hide a bug.
218 used to hide a bug.
203
219
204 $ mkdir dir
220 $ mkdir dir
205 $ hg clone -r0 a dir/b
221 $ hg clone -r0 a dir/b
206 adding changesets
222 adding changesets
207 adding manifests
223 adding manifests
208 adding file changes
224 adding file changes
209 added 1 changesets with 2 changes to 2 files
225 added 1 changesets with 2 changes to 2 files
210 updating to branch default
226 updating to branch default
211 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
227 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
212 $ cd dir
228 $ cd dir
213 $ hg -R b import ../exported-tip.patch
229 $ hg -R b import ../exported-tip.patch
214 applying ../exported-tip.patch
230 applying ../exported-tip.patch
215 $ cd ..
231 $ cd ..
216 $ rm -r dir
232 $ rm -r dir
217
233
218
234
219 import from stdin
235 import from stdin
220
236
221 $ hg clone -r0 a b
237 $ hg clone -r0 a b
222 adding changesets
238 adding changesets
223 adding manifests
239 adding manifests
224 adding file changes
240 adding file changes
225 added 1 changesets with 2 changes to 2 files
241 added 1 changesets with 2 changes to 2 files
226 updating to branch default
242 updating to branch default
227 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
243 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
228 $ hg --cwd b import - < exported-tip.patch
244 $ hg --cwd b import - < exported-tip.patch
229 applying patch from stdin
245 applying patch from stdin
230 $ rm -r b
246 $ rm -r b
231
247
232
248
233 import two patches in one stream
249 import two patches in one stream
234
250
235 $ hg init b
251 $ hg init b
236 $ hg --cwd a export 0:tip | hg --cwd b import -
252 $ hg --cwd a export 0:tip | hg --cwd b import -
237 applying patch from stdin
253 applying patch from stdin
238 $ hg --cwd a id
254 $ hg --cwd a id
239 1d4bd90af0e4 tip
255 1d4bd90af0e4 tip
240 $ hg --cwd b id
256 $ hg --cwd b id
241 1d4bd90af0e4 tip
257 1d4bd90af0e4 tip
242 $ rm -r b
258 $ rm -r b
243
259
244
260
245 override commit message
261 override commit message
246
262
247 $ hg clone -r0 a b
263 $ hg clone -r0 a b
248 adding changesets
264 adding changesets
249 adding manifests
265 adding manifests
250 adding file changes
266 adding file changes
251 added 1 changesets with 2 changes to 2 files
267 added 1 changesets with 2 changes to 2 files
252 updating to branch default
268 updating to branch default
253 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
269 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
254 $ hg --cwd b import -m 'override' - < exported-tip.patch
270 $ hg --cwd b import -m 'override' - < exported-tip.patch
255 applying patch from stdin
271 applying patch from stdin
256 $ hg --cwd b tip | grep override
272 $ hg --cwd b tip | grep override
257 summary: override
273 summary: override
258 $ rm -r b
274 $ rm -r b
259
275
260 $ cat > mkmsg.py <<EOF
276 $ cat > mkmsg.py <<EOF
261 > import email.Message, sys
277 > import email.Message, sys
262 > msg = email.Message.Message()
278 > msg = email.Message.Message()
263 > patch = open(sys.argv[1], 'rb').read()
279 > patch = open(sys.argv[1], 'rb').read()
264 > msg.set_payload('email commit message\n' + patch)
280 > msg.set_payload('email commit message\n' + patch)
265 > msg['Subject'] = 'email patch'
281 > msg['Subject'] = 'email patch'
266 > msg['From'] = 'email patcher'
282 > msg['From'] = 'email patcher'
267 > file(sys.argv[2], 'wb').write(msg.as_string())
283 > file(sys.argv[2], 'wb').write(msg.as_string())
268 > EOF
284 > EOF
269
285
270
286
271 plain diff in email, subject, message body
287 plain diff in email, subject, message body
272
288
273 $ hg clone -r0 a b
289 $ hg clone -r0 a b
274 adding changesets
290 adding changesets
275 adding manifests
291 adding manifests
276 adding file changes
292 adding file changes
277 added 1 changesets with 2 changes to 2 files
293 added 1 changesets with 2 changes to 2 files
278 updating to branch default
294 updating to branch default
279 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
295 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
280 $ python mkmsg.py diffed-tip.patch msg.patch
296 $ python mkmsg.py diffed-tip.patch msg.patch
281 $ hg --cwd b import ../msg.patch
297 $ hg --cwd b import ../msg.patch
282 applying ../msg.patch
298 applying ../msg.patch
283 $ hg --cwd b tip | grep email
299 $ hg --cwd b tip | grep email
284 user: email patcher
300 user: email patcher
285 summary: email patch
301 summary: email patch
286 $ rm -r b
302 $ rm -r b
287
303
288
304
289 plain diff in email, no subject, message body
305 plain diff in email, no subject, message body
290
306
291 $ hg clone -r0 a b
307 $ hg clone -r0 a b
292 adding changesets
308 adding changesets
293 adding manifests
309 adding manifests
294 adding file changes
310 adding file changes
295 added 1 changesets with 2 changes to 2 files
311 added 1 changesets with 2 changes to 2 files
296 updating to branch default
312 updating to branch default
297 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
313 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
298 $ grep -v '^Subject:' msg.patch | hg --cwd b import -
314 $ grep -v '^Subject:' msg.patch | hg --cwd b import -
299 applying patch from stdin
315 applying patch from stdin
300 $ rm -r b
316 $ rm -r b
301
317
302
318
303 plain diff in email, subject, no message body
319 plain diff in email, subject, no message body
304
320
305 $ hg clone -r0 a b
321 $ hg clone -r0 a b
306 adding changesets
322 adding changesets
307 adding manifests
323 adding manifests
308 adding file changes
324 adding file changes
309 added 1 changesets with 2 changes to 2 files
325 added 1 changesets with 2 changes to 2 files
310 updating to branch default
326 updating to branch default
311 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
327 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
312 $ grep -v '^email ' msg.patch | hg --cwd b import -
328 $ grep -v '^email ' msg.patch | hg --cwd b import -
313 applying patch from stdin
329 applying patch from stdin
314 $ rm -r b
330 $ rm -r b
315
331
316
332
317 plain diff in email, no subject, no message body, should fail
333 plain diff in email, no subject, no message body, should fail
318
334
319 $ hg clone -r0 a b
335 $ hg clone -r0 a b
320 adding changesets
336 adding changesets
321 adding manifests
337 adding manifests
322 adding file changes
338 adding file changes
323 added 1 changesets with 2 changes to 2 files
339 added 1 changesets with 2 changes to 2 files
324 updating to branch default
340 updating to branch default
325 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
341 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
326 $ egrep -v '^(Subject|email)' msg.patch | hg --cwd b import -
342 $ egrep -v '^(Subject|email)' msg.patch | hg --cwd b import -
327 applying patch from stdin
343 applying patch from stdin
328 abort: empty commit message
344 abort: empty commit message
329 [255]
345 [255]
330 $ rm -r b
346 $ rm -r b
331
347
332
348
333 hg export in email, should use patch header
349 hg export in email, should use patch header
334
350
335 $ hg clone -r0 a b
351 $ hg clone -r0 a b
336 adding changesets
352 adding changesets
337 adding manifests
353 adding manifests
338 adding file changes
354 adding file changes
339 added 1 changesets with 2 changes to 2 files
355 added 1 changesets with 2 changes to 2 files
340 updating to branch default
356 updating to branch default
341 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
357 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
342 $ python mkmsg.py exported-tip.patch msg.patch
358 $ python mkmsg.py exported-tip.patch msg.patch
343 $ cat msg.patch | hg --cwd b import -
359 $ cat msg.patch | hg --cwd b import -
344 applying patch from stdin
360 applying patch from stdin
345 $ hg --cwd b tip | grep second
361 $ hg --cwd b tip | grep second
346 summary: second change
362 summary: second change
347 $ rm -r b
363 $ rm -r b
348
364
349
365
350 subject: duplicate detection, removal of [PATCH]
366 subject: duplicate detection, removal of [PATCH]
351 The '---' tests the gitsendmail handling without proper mail headers
367 The '---' tests the gitsendmail handling without proper mail headers
352
368
353 $ cat > mkmsg2.py <<EOF
369 $ cat > mkmsg2.py <<EOF
354 > import email.Message, sys
370 > import email.Message, sys
355 > msg = email.Message.Message()
371 > msg = email.Message.Message()
356 > patch = open(sys.argv[1], 'rb').read()
372 > patch = open(sys.argv[1], 'rb').read()
357 > msg.set_payload('email patch\n\nnext line\n---\n' + patch)
373 > msg.set_payload('email patch\n\nnext line\n---\n' + patch)
358 > msg['Subject'] = '[PATCH] email patch'
374 > msg['Subject'] = '[PATCH] email patch'
359 > msg['From'] = 'email patcher'
375 > msg['From'] = 'email patcher'
360 > file(sys.argv[2], 'wb').write(msg.as_string())
376 > file(sys.argv[2], 'wb').write(msg.as_string())
361 > EOF
377 > EOF
362
378
363
379
364 plain diff in email, [PATCH] subject, message body with subject
380 plain diff in email, [PATCH] subject, message body with subject
365
381
366 $ hg clone -r0 a b
382 $ hg clone -r0 a b
367 adding changesets
383 adding changesets
368 adding manifests
384 adding manifests
369 adding file changes
385 adding file changes
370 added 1 changesets with 2 changes to 2 files
386 added 1 changesets with 2 changes to 2 files
371 updating to branch default
387 updating to branch default
372 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
388 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
373 $ python mkmsg2.py diffed-tip.patch msg.patch
389 $ python mkmsg2.py diffed-tip.patch msg.patch
374 $ cat msg.patch | hg --cwd b import -
390 $ cat msg.patch | hg --cwd b import -
375 applying patch from stdin
391 applying patch from stdin
376 $ hg --cwd b tip --template '{desc}\n'
392 $ hg --cwd b tip --template '{desc}\n'
377 email patch
393 email patch
378
394
379 next line
395 next line
380 $ rm -r b
396 $ rm -r b
381
397
382
398
383 Issue963: Parent of working dir incorrect after import of multiple
399 Issue963: Parent of working dir incorrect after import of multiple
384 patches and rollback
400 patches and rollback
385
401
386 We weren't backing up the correct dirstate file when importing many
402 We weren't backing up the correct dirstate file when importing many
387 patches: import patch1 patch2; rollback
403 patches: import patch1 patch2; rollback
388
404
389 $ echo line 3 >> a/a
405 $ echo line 3 >> a/a
390 $ hg --cwd a ci -m'third change'
406 $ hg --cwd a ci -m'third change'
391 $ hg --cwd a export -o '../patch%R' 1 2
407 $ hg --cwd a export -o '../patch%R' 1 2
392 $ hg clone -qr0 a b
408 $ hg clone -qr0 a b
393 $ hg --cwd b parents --template 'parent: {rev}\n'
409 $ hg --cwd b parents --template 'parent: {rev}\n'
394 parent: 0
410 parent: 0
395 $ hg --cwd b import -v ../patch1 ../patch2
411 $ hg --cwd b import -v ../patch1 ../patch2
396 applying ../patch1
412 applying ../patch1
397 patching file a
413 patching file a
398 a
414 a
399 created 1d4bd90af0e4
415 created 1d4bd90af0e4
400 applying ../patch2
416 applying ../patch2
401 patching file a
417 patching file a
402 a
418 a
403 created 6d019af21222
419 created 6d019af21222
404 $ hg --cwd b rollback
420 $ hg --cwd b rollback
405 repository tip rolled back to revision 0 (undo import)
421 repository tip rolled back to revision 0 (undo import)
406 working directory now based on revision 0
422 working directory now based on revision 0
407 $ hg --cwd b parents --template 'parent: {rev}\n'
423 $ hg --cwd b parents --template 'parent: {rev}\n'
408 parent: 0
424 parent: 0
409 $ rm -r b
425 $ rm -r b
410
426
411
427
412 importing a patch in a subdirectory failed at the commit stage
428 importing a patch in a subdirectory failed at the commit stage
413
429
414 $ echo line 2 >> a/d1/d2/a
430 $ echo line 2 >> a/d1/d2/a
415 $ hg --cwd a ci -u someoneelse -d '1 0' -m'subdir change'
431 $ hg --cwd a ci -u someoneelse -d '1 0' -m'subdir change'
416
432
417 hg import in a subdirectory
433 hg import in a subdirectory
418
434
419 $ hg clone -r0 a b
435 $ hg clone -r0 a b
420 adding changesets
436 adding changesets
421 adding manifests
437 adding manifests
422 adding file changes
438 adding file changes
423 added 1 changesets with 2 changes to 2 files
439 added 1 changesets with 2 changes to 2 files
424 updating to branch default
440 updating to branch default
425 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
441 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
426 $ hg --cwd a export tip > tmp
442 $ hg --cwd a export tip > tmp
427 $ sed -e 's/d1\/d2\///' < tmp > subdir-tip.patch
443 $ sed -e 's/d1\/d2\///' < tmp > subdir-tip.patch
428 $ dir=`pwd`
444 $ dir=`pwd`
429 $ cd b/d1/d2 2>&1 > /dev/null
445 $ cd b/d1/d2 2>&1 > /dev/null
430 $ hg import ../../../subdir-tip.patch
446 $ hg import ../../../subdir-tip.patch
431 applying ../../../subdir-tip.patch
447 applying ../../../subdir-tip.patch
432 $ cd "$dir"
448 $ cd "$dir"
433
449
434 message should be 'subdir change'
450 message should be 'subdir change'
435 committer should be 'someoneelse'
451 committer should be 'someoneelse'
436
452
437 $ hg --cwd b tip
453 $ hg --cwd b tip
438 changeset: 1:3577f5aea227
454 changeset: 1:3577f5aea227
439 tag: tip
455 tag: tip
440 user: someoneelse
456 user: someoneelse
441 date: Thu Jan 01 00:00:01 1970 +0000
457 date: Thu Jan 01 00:00:01 1970 +0000
442 summary: subdir change
458 summary: subdir change
443
459
444
460
445 should be empty
461 should be empty
446
462
447 $ hg --cwd b status
463 $ hg --cwd b status
448
464
449
465
450 Test fuzziness (ambiguous patch location, fuzz=2)
466 Test fuzziness (ambiguous patch location, fuzz=2)
451
467
452 $ hg init fuzzy
468 $ hg init fuzzy
453 $ cd fuzzy
469 $ cd fuzzy
454 $ echo line1 > a
470 $ echo line1 > a
455 $ echo line0 >> a
471 $ echo line0 >> a
456 $ echo line3 >> a
472 $ echo line3 >> a
457 $ hg ci -Am adda
473 $ hg ci -Am adda
458 adding a
474 adding a
459 $ echo line1 > a
475 $ echo line1 > a
460 $ echo line2 >> a
476 $ echo line2 >> a
461 $ echo line0 >> a
477 $ echo line0 >> a
462 $ echo line3 >> a
478 $ echo line3 >> a
463 $ hg ci -m change a
479 $ hg ci -m change a
464 $ hg export tip > fuzzy-tip.patch
480 $ hg export tip > fuzzy-tip.patch
465 $ hg up -C 0
481 $ hg up -C 0
466 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
482 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
467 $ echo line1 > a
483 $ echo line1 > a
468 $ echo line0 >> a
484 $ echo line0 >> a
469 $ echo line1 >> a
485 $ echo line1 >> a
470 $ echo line0 >> a
486 $ echo line0 >> a
471 $ hg ci -m brancha
487 $ hg ci -m brancha
472 created new head
488 created new head
473 $ hg import --no-commit -v fuzzy-tip.patch
489 $ hg import --no-commit -v fuzzy-tip.patch
474 applying fuzzy-tip.patch
490 applying fuzzy-tip.patch
475 patching file a
491 patching file a
476 Hunk #1 succeeded at 2 with fuzz 1 (offset 0 lines).
492 Hunk #1 succeeded at 2 with fuzz 1 (offset 0 lines).
477 applied to working directory
493 applied to working directory
478 $ hg revert -a
494 $ hg revert -a
479 reverting a
495 reverting a
480
496
481
497
482 import with --no-commit should have written .hg/last-message.txt
498 import with --no-commit should have written .hg/last-message.txt
483
499
484 $ cat .hg/last-message.txt
500 $ cat .hg/last-message.txt
485 change (no-eol)
501 change (no-eol)
486
502
487
503
488 test fuzziness with eol=auto
504 test fuzziness with eol=auto
489
505
490 $ hg --config patch.eol=auto import --no-commit -v fuzzy-tip.patch
506 $ hg --config patch.eol=auto import --no-commit -v fuzzy-tip.patch
491 applying fuzzy-tip.patch
507 applying fuzzy-tip.patch
492 patching file a
508 patching file a
493 Hunk #1 succeeded at 2 with fuzz 1 (offset 0 lines).
509 Hunk #1 succeeded at 2 with fuzz 1 (offset 0 lines).
494 applied to working directory
510 applied to working directory
495 $ cd ..
511 $ cd ..
496
512
497
513
498 Test hunk touching empty files (issue906)
514 Test hunk touching empty files (issue906)
499
515
500 $ hg init empty
516 $ hg init empty
501 $ cd empty
517 $ cd empty
502 $ touch a
518 $ touch a
503 $ touch b1
519 $ touch b1
504 $ touch c1
520 $ touch c1
505 $ echo d > d
521 $ echo d > d
506 $ hg ci -Am init
522 $ hg ci -Am init
507 adding a
523 adding a
508 adding b1
524 adding b1
509 adding c1
525 adding c1
510 adding d
526 adding d
511 $ echo a > a
527 $ echo a > a
512 $ echo b > b1
528 $ echo b > b1
513 $ hg mv b1 b2
529 $ hg mv b1 b2
514 $ echo c > c1
530 $ echo c > c1
515 $ hg copy c1 c2
531 $ hg copy c1 c2
516 $ rm d
532 $ rm d
517 $ touch d
533 $ touch d
518 $ hg diff --git
534 $ hg diff --git
519 diff --git a/a b/a
535 diff --git a/a b/a
520 --- a/a
536 --- a/a
521 +++ b/a
537 +++ b/a
522 @@ -0,0 +1,1 @@
538 @@ -0,0 +1,1 @@
523 +a
539 +a
524 diff --git a/b1 b/b2
540 diff --git a/b1 b/b2
525 rename from b1
541 rename from b1
526 rename to b2
542 rename to b2
527 --- a/b1
543 --- a/b1
528 +++ b/b2
544 +++ b/b2
529 @@ -0,0 +1,1 @@
545 @@ -0,0 +1,1 @@
530 +b
546 +b
531 diff --git a/c1 b/c1
547 diff --git a/c1 b/c1
532 --- a/c1
548 --- a/c1
533 +++ b/c1
549 +++ b/c1
534 @@ -0,0 +1,1 @@
550 @@ -0,0 +1,1 @@
535 +c
551 +c
536 diff --git a/c1 b/c2
552 diff --git a/c1 b/c2
537 copy from c1
553 copy from c1
538 copy to c2
554 copy to c2
539 --- a/c1
555 --- a/c1
540 +++ b/c2
556 +++ b/c2
541 @@ -0,0 +1,1 @@
557 @@ -0,0 +1,1 @@
542 +c
558 +c
543 diff --git a/d b/d
559 diff --git a/d b/d
544 --- a/d
560 --- a/d
545 +++ b/d
561 +++ b/d
546 @@ -1,1 +0,0 @@
562 @@ -1,1 +0,0 @@
547 -d
563 -d
548 $ hg ci -m empty
564 $ hg ci -m empty
549 $ hg export --git tip > empty.diff
565 $ hg export --git tip > empty.diff
550 $ hg up -C 0
566 $ hg up -C 0
551 4 files updated, 0 files merged, 2 files removed, 0 files unresolved
567 4 files updated, 0 files merged, 2 files removed, 0 files unresolved
552 $ hg import empty.diff
568 $ hg import empty.diff
553 applying empty.diff
569 applying empty.diff
554 $ for name in a b1 b2 c1 c2 d; do
570 $ for name in a b1 b2 c1 c2 d; do
555 > echo % $name file
571 > echo % $name file
556 > test -f $name && cat $name
572 > test -f $name && cat $name
557 > done
573 > done
558 % a file
574 % a file
559 a
575 a
560 % b1 file
576 % b1 file
561 % b2 file
577 % b2 file
562 b
578 b
563 % c1 file
579 % c1 file
564 c
580 c
565 % c2 file
581 % c2 file
566 c
582 c
567 % d file
583 % d file
568 $ cd ..
584 $ cd ..
569
585
570
586
571 Test importing a patch ending with a binary file removal
587 Test importing a patch ending with a binary file removal
572
588
573 $ hg init binaryremoval
589 $ hg init binaryremoval
574 $ cd binaryremoval
590 $ cd binaryremoval
575 $ echo a > a
591 $ echo a > a
576 $ python -c "file('b', 'wb').write('a\x00b')"
592 $ python -c "file('b', 'wb').write('a\x00b')"
577 $ hg ci -Am addall
593 $ hg ci -Am addall
578 adding a
594 adding a
579 adding b
595 adding b
580 $ hg rm a
596 $ hg rm a
581 $ hg rm b
597 $ hg rm b
582 $ hg st
598 $ hg st
583 R a
599 R a
584 R b
600 R b
585 $ hg ci -m remove
601 $ hg ci -m remove
586 $ hg export --git . > remove.diff
602 $ hg export --git . > remove.diff
587 $ cat remove.diff | grep git
603 $ cat remove.diff | grep git
588 diff --git a/a b/a
604 diff --git a/a b/a
589 diff --git a/b b/b
605 diff --git a/b b/b
590 $ hg up -C 0
606 $ hg up -C 0
591 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
607 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
592 $ hg import remove.diff
608 $ hg import remove.diff
593 applying remove.diff
609 applying remove.diff
594 $ hg manifest
610 $ hg manifest
595 $ cd ..
611 $ cd ..
596
612
597
613
598 Issue927: test update+rename with common name
614 Issue927: test update+rename with common name
599
615
600 $ hg init t
616 $ hg init t
601 $ cd t
617 $ cd t
602 $ touch a
618 $ touch a
603 $ hg ci -Am t
619 $ hg ci -Am t
604 adding a
620 adding a
605 $ echo a > a
621 $ echo a > a
606
622
607 Here, bfile.startswith(afile)
623 Here, bfile.startswith(afile)
608
624
609 $ hg copy a a2
625 $ hg copy a a2
610 $ hg ci -m copya
626 $ hg ci -m copya
611 $ hg export --git tip > copy.diff
627 $ hg export --git tip > copy.diff
612 $ hg up -C 0
628 $ hg up -C 0
613 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
629 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
614 $ hg import copy.diff
630 $ hg import copy.diff
615 applying copy.diff
631 applying copy.diff
616
632
617 a should contain an 'a'
633 a should contain an 'a'
618
634
619 $ cat a
635 $ cat a
620 a
636 a
621
637
622 and a2 should have duplicated it
638 and a2 should have duplicated it
623
639
624 $ cat a2
640 $ cat a2
625 a
641 a
626 $ cd ..
642 $ cd ..
627
643
628
644
629 test -p0
645 test -p0
630
646
631 $ hg init p0
647 $ hg init p0
632 $ cd p0
648 $ cd p0
633 $ echo a > a
649 $ echo a > a
634 $ hg ci -Am t
650 $ hg ci -Am t
635 adding a
651 adding a
636 $ hg import -p foo
652 $ hg import -p foo
637 abort: invalid value 'foo' for option -p, expected int
653 abort: invalid value 'foo' for option -p, expected int
638 [255]
654 [255]
639 $ hg import -p0 - << EOF
655 $ hg import -p0 - << EOF
640 > foobar
656 > foobar
641 > --- a Sat Apr 12 22:43:58 2008 -0400
657 > --- a Sat Apr 12 22:43:58 2008 -0400
642 > +++ a Sat Apr 12 22:44:05 2008 -0400
658 > +++ a Sat Apr 12 22:44:05 2008 -0400
643 > @@ -1,1 +1,1 @@
659 > @@ -1,1 +1,1 @@
644 > -a
660 > -a
645 > +bb
661 > +bb
646 > EOF
662 > EOF
647 applying patch from stdin
663 applying patch from stdin
648 $ hg status
664 $ hg status
649 $ cat a
665 $ cat a
650 bb
666 bb
651 $ cd ..
667 $ cd ..
652
668
653
669
654 test paths outside repo root
670 test paths outside repo root
655
671
656 $ mkdir outside
672 $ mkdir outside
657 $ touch outside/foo
673 $ touch outside/foo
658 $ hg init inside
674 $ hg init inside
659 $ cd inside
675 $ cd inside
660 $ hg import - <<EOF
676 $ hg import - <<EOF
661 > diff --git a/a b/b
677 > diff --git a/a b/b
662 > rename from ../outside/foo
678 > rename from ../outside/foo
663 > rename to bar
679 > rename to bar
664 > EOF
680 > EOF
665 applying patch from stdin
681 applying patch from stdin
666 abort: path contains illegal component: ../outside/foo (glob)
682 abort: path contains illegal component: ../outside/foo (glob)
667 [255]
683 [255]
668 $ cd ..
684 $ cd ..
669
685
670
686
671 test import with similarity and git and strip (issue295 et al.)
687 test import with similarity and git and strip (issue295 et al.)
672
688
673 $ hg init sim
689 $ hg init sim
674 $ cd sim
690 $ cd sim
675 $ echo 'this is a test' > a
691 $ echo 'this is a test' > a
676 $ hg ci -Ama
692 $ hg ci -Ama
677 adding a
693 adding a
678 $ cat > ../rename.diff <<EOF
694 $ cat > ../rename.diff <<EOF
679 > diff --git a/foo/a b/foo/a
695 > diff --git a/foo/a b/foo/a
680 > deleted file mode 100644
696 > deleted file mode 100644
681 > --- a/foo/a
697 > --- a/foo/a
682 > +++ /dev/null
698 > +++ /dev/null
683 > @@ -1,1 +0,0 @@
699 > @@ -1,1 +0,0 @@
684 > -this is a test
700 > -this is a test
685 > diff --git a/foo/b b/foo/b
701 > diff --git a/foo/b b/foo/b
686 > new file mode 100644
702 > new file mode 100644
687 > --- /dev/null
703 > --- /dev/null
688 > +++ b/foo/b
704 > +++ b/foo/b
689 > @@ -0,0 +1,2 @@
705 > @@ -0,0 +1,2 @@
690 > +this is a test
706 > +this is a test
691 > +foo
707 > +foo
692 > EOF
708 > EOF
693 $ hg import --no-commit -v -s 1 ../rename.diff -p2
709 $ hg import --no-commit -v -s 1 ../rename.diff -p2
694 applying ../rename.diff
710 applying ../rename.diff
695 patching file a
711 patching file a
696 patching file b
712 patching file b
697 adding b
713 adding b
698 recording removal of a as rename to b (88% similar)
714 recording removal of a as rename to b (88% similar)
699 applied to working directory
715 applied to working directory
700 $ hg st -C
716 $ hg st -C
701 A b
717 A b
702 a
718 a
703 R a
719 R a
704 $ hg revert -a
720 $ hg revert -a
705 undeleting a
721 undeleting a
706 forgetting b
722 forgetting b
707 $ rm b
723 $ rm b
708 $ hg import --no-commit -v -s 100 ../rename.diff -p2
724 $ hg import --no-commit -v -s 100 ../rename.diff -p2
709 applying ../rename.diff
725 applying ../rename.diff
710 patching file a
726 patching file a
711 patching file b
727 patching file b
712 adding b
728 adding b
713 applied to working directory
729 applied to working directory
714 $ hg st -C
730 $ hg st -C
715 A b
731 A b
716 R a
732 R a
717 $ cd ..
733 $ cd ..
718
734
719
735
720 Issue1495: add empty file from the end of patch
736 Issue1495: add empty file from the end of patch
721
737
722 $ hg init addemptyend
738 $ hg init addemptyend
723 $ cd addemptyend
739 $ cd addemptyend
724 $ touch a
740 $ touch a
725 $ hg addremove
741 $ hg addremove
726 adding a
742 adding a
727 $ hg ci -m "commit"
743 $ hg ci -m "commit"
728 $ cat > a.patch <<EOF
744 $ cat > a.patch <<EOF
729 > add a, b
745 > add a, b
730 > diff --git a/a b/a
746 > diff --git a/a b/a
731 > --- a/a
747 > --- a/a
732 > +++ b/a
748 > +++ b/a
733 > @@ -0,0 +1,1 @@
749 > @@ -0,0 +1,1 @@
734 > +a
750 > +a
735 > diff --git a/b b/b
751 > diff --git a/b b/b
736 > new file mode 100644
752 > new file mode 100644
737 > EOF
753 > EOF
738 $ hg import --no-commit a.patch
754 $ hg import --no-commit a.patch
739 applying a.patch
755 applying a.patch
740
756
741 apply a good patch followed by an empty patch (mainly to ensure
757 apply a good patch followed by an empty patch (mainly to ensure
742 that dirstate is *not* updated when import crashes)
758 that dirstate is *not* updated when import crashes)
743 $ hg update -q -C .
759 $ hg update -q -C .
744 $ rm b
760 $ rm b
745 $ touch empty.patch
761 $ touch empty.patch
746 $ hg import a.patch empty.patch
762 $ hg import a.patch empty.patch
747 applying a.patch
763 applying a.patch
748 applying empty.patch
764 applying empty.patch
749 transaction abort!
765 transaction abort!
750 rollback completed
766 rollback completed
751 abort: empty.patch: no diffs found
767 abort: empty.patch: no diffs found
752 [255]
768 [255]
753 $ hg tip --template '{rev} {desc|firstline}\n'
769 $ hg tip --template '{rev} {desc|firstline}\n'
754 0 commit
770 0 commit
755 $ hg -q status
771 $ hg -q status
756 M a
772 M a
757 $ cd ..
773 $ cd ..
758
774
759 create file when source is not /dev/null
775 create file when source is not /dev/null
760
776
761 $ cat > create.patch <<EOF
777 $ cat > create.patch <<EOF
762 > diff -Naur proj-orig/foo proj-new/foo
778 > diff -Naur proj-orig/foo proj-new/foo
763 > --- proj-orig/foo 1969-12-31 16:00:00.000000000 -0800
779 > --- proj-orig/foo 1969-12-31 16:00:00.000000000 -0800
764 > +++ proj-new/foo 2009-07-17 16:50:45.801368000 -0700
780 > +++ proj-new/foo 2009-07-17 16:50:45.801368000 -0700
765 > @@ -0,0 +1,1 @@
781 > @@ -0,0 +1,1 @@
766 > +a
782 > +a
767 > EOF
783 > EOF
768
784
769 some people have patches like the following too
785 some people have patches like the following too
770
786
771 $ cat > create2.patch <<EOF
787 $ cat > create2.patch <<EOF
772 > diff -Naur proj-orig/foo proj-new/foo
788 > diff -Naur proj-orig/foo proj-new/foo
773 > --- proj-orig/foo.orig 1969-12-31 16:00:00.000000000 -0800
789 > --- proj-orig/foo.orig 1969-12-31 16:00:00.000000000 -0800
774 > +++ proj-new/foo 2009-07-17 16:50:45.801368000 -0700
790 > +++ proj-new/foo 2009-07-17 16:50:45.801368000 -0700
775 > @@ -0,0 +1,1 @@
791 > @@ -0,0 +1,1 @@
776 > +a
792 > +a
777 > EOF
793 > EOF
778 $ hg init oddcreate
794 $ hg init oddcreate
779 $ cd oddcreate
795 $ cd oddcreate
780 $ hg import --no-commit ../create.patch
796 $ hg import --no-commit ../create.patch
781 applying ../create.patch
797 applying ../create.patch
782 $ cat foo
798 $ cat foo
783 a
799 a
784 $ rm foo
800 $ rm foo
785 $ hg revert foo
801 $ hg revert foo
786 $ hg import --no-commit ../create2.patch
802 $ hg import --no-commit ../create2.patch
787 applying ../create2.patch
803 applying ../create2.patch
788 $ cat foo
804 $ cat foo
789 a
805 a
790
806
791 $ cd ..
807 $ cd ..
792
808
793 Issue1859: first line mistaken for email headers
809 Issue1859: first line mistaken for email headers
794
810
795 $ hg init emailconfusion
811 $ hg init emailconfusion
796 $ cd emailconfusion
812 $ cd emailconfusion
797 $ cat > a.patch <<EOF
813 $ cat > a.patch <<EOF
798 > module: summary
814 > module: summary
799 >
815 >
800 > description
816 > description
801 >
817 >
802 >
818 >
803 > diff -r 000000000000 -r 9b4c1e343b55 test.txt
819 > diff -r 000000000000 -r 9b4c1e343b55 test.txt
804 > --- /dev/null
820 > --- /dev/null
805 > +++ b/a
821 > +++ b/a
806 > @@ -0,0 +1,1 @@
822 > @@ -0,0 +1,1 @@
807 > +a
823 > +a
808 > EOF
824 > EOF
809 $ hg import -d '0 0' a.patch
825 $ hg import -d '0 0' a.patch
810 applying a.patch
826 applying a.patch
811 $ hg parents -v
827 $ hg parents -v
812 changeset: 0:5a681217c0ad
828 changeset: 0:5a681217c0ad
813 tag: tip
829 tag: tip
814 user: test
830 user: test
815 date: Thu Jan 01 00:00:00 1970 +0000
831 date: Thu Jan 01 00:00:00 1970 +0000
816 files: a
832 files: a
817 description:
833 description:
818 module: summary
834 module: summary
819
835
820 description
836 description
821
837
822
838
823 $ cd ..
839 $ cd ..
824
840
825
841
826 in commit message
842 in commit message
827
843
828 $ hg init commitconfusion
844 $ hg init commitconfusion
829 $ cd commitconfusion
845 $ cd commitconfusion
830 $ cat > a.patch <<EOF
846 $ cat > a.patch <<EOF
831 > module: summary
847 > module: summary
832 >
848 >
833 > --- description
849 > --- description
834 >
850 >
835 > diff --git a/a b/a
851 > diff --git a/a b/a
836 > new file mode 100644
852 > new file mode 100644
837 > --- /dev/null
853 > --- /dev/null
838 > +++ b/a
854 > +++ b/a
839 > @@ -0,0 +1,1 @@
855 > @@ -0,0 +1,1 @@
840 > +a
856 > +a
841 > EOF
857 > EOF
842 > hg import -d '0 0' a.patch
858 > hg import -d '0 0' a.patch
843 > hg parents -v
859 > hg parents -v
844 > cd ..
860 > cd ..
845 >
861 >
846 > echo '% tricky header splitting'
862 > echo '% tricky header splitting'
847 > cat > trickyheaders.patch <<EOF
863 > cat > trickyheaders.patch <<EOF
848 > From: User A <user@a>
864 > From: User A <user@a>
849 > Subject: [PATCH] from: tricky!
865 > Subject: [PATCH] from: tricky!
850 >
866 >
851 > # HG changeset patch
867 > # HG changeset patch
852 > # User User B
868 > # User User B
853 > # Date 1266264441 18000
869 > # Date 1266264441 18000
854 > # Branch stable
870 > # Branch stable
855 > # Node ID f2be6a1170ac83bf31cb4ae0bad00d7678115bc0
871 > # Node ID f2be6a1170ac83bf31cb4ae0bad00d7678115bc0
856 > # Parent 0000000000000000000000000000000000000000
872 > # Parent 0000000000000000000000000000000000000000
857 > from: tricky!
873 > from: tricky!
858 >
874 >
859 > That is not a header.
875 > That is not a header.
860 >
876 >
861 > diff -r 000000000000 -r f2be6a1170ac foo
877 > diff -r 000000000000 -r f2be6a1170ac foo
862 > --- /dev/null
878 > --- /dev/null
863 > +++ b/foo
879 > +++ b/foo
864 > @@ -0,0 +1,1 @@
880 > @@ -0,0 +1,1 @@
865 > +foo
881 > +foo
866 > EOF
882 > EOF
867 applying a.patch
883 applying a.patch
868 changeset: 0:f34d9187897d
884 changeset: 0:f34d9187897d
869 tag: tip
885 tag: tip
870 user: test
886 user: test
871 date: Thu Jan 01 00:00:00 1970 +0000
887 date: Thu Jan 01 00:00:00 1970 +0000
872 files: a
888 files: a
873 description:
889 description:
874 module: summary
890 module: summary
875
891
876
892
877 % tricky header splitting
893 % tricky header splitting
878
894
879 $ hg init trickyheaders
895 $ hg init trickyheaders
880 $ cd trickyheaders
896 $ cd trickyheaders
881 $ hg import -d '0 0' ../trickyheaders.patch
897 $ hg import -d '0 0' ../trickyheaders.patch
882 applying ../trickyheaders.patch
898 applying ../trickyheaders.patch
883 $ hg export --git tip
899 $ hg export --git tip
884 # HG changeset patch
900 # HG changeset patch
885 # User User B
901 # User User B
886 # Date 0 0
902 # Date 0 0
887 # Thu Jan 01 00:00:00 1970 +0000
903 # Thu Jan 01 00:00:00 1970 +0000
888 # Node ID eb56ab91903632294ac504838508cb370c0901d2
904 # Node ID eb56ab91903632294ac504838508cb370c0901d2
889 # Parent 0000000000000000000000000000000000000000
905 # Parent 0000000000000000000000000000000000000000
890 from: tricky!
906 from: tricky!
891
907
892 That is not a header.
908 That is not a header.
893
909
894 diff --git a/foo b/foo
910 diff --git a/foo b/foo
895 new file mode 100644
911 new file mode 100644
896 --- /dev/null
912 --- /dev/null
897 +++ b/foo
913 +++ b/foo
898 @@ -0,0 +1,1 @@
914 @@ -0,0 +1,1 @@
899 +foo
915 +foo
900 $ cd ..
916 $ cd ..
901
917
902
918
903 Issue2102: hg export and hg import speak different languages
919 Issue2102: hg export and hg import speak different languages
904
920
905 $ hg init issue2102
921 $ hg init issue2102
906 $ cd issue2102
922 $ cd issue2102
907 $ mkdir -p src/cmd/gc
923 $ mkdir -p src/cmd/gc
908 $ touch src/cmd/gc/mksys.bash
924 $ touch src/cmd/gc/mksys.bash
909 $ hg ci -Am init
925 $ hg ci -Am init
910 adding src/cmd/gc/mksys.bash
926 adding src/cmd/gc/mksys.bash
911 $ hg import - <<EOF
927 $ hg import - <<EOF
912 > # HG changeset patch
928 > # HG changeset patch
913 > # User Rob Pike
929 > # User Rob Pike
914 > # Date 1216685449 25200
930 > # Date 1216685449 25200
915 > # Node ID 03aa2b206f499ad6eb50e6e207b9e710d6409c98
931 > # Node ID 03aa2b206f499ad6eb50e6e207b9e710d6409c98
916 > # Parent 93d10138ad8df586827ca90b4ddb5033e21a3a84
932 > # Parent 93d10138ad8df586827ca90b4ddb5033e21a3a84
917 > help management of empty pkg and lib directories in perforce
933 > help management of empty pkg and lib directories in perforce
918 >
934 >
919 > R=gri
935 > R=gri
920 > DELTA=4 (4 added, 0 deleted, 0 changed)
936 > DELTA=4 (4 added, 0 deleted, 0 changed)
921 > OCL=13328
937 > OCL=13328
922 > CL=13328
938 > CL=13328
923 >
939 >
924 > diff --git a/lib/place-holder b/lib/place-holder
940 > diff --git a/lib/place-holder b/lib/place-holder
925 > new file mode 100644
941 > new file mode 100644
926 > --- /dev/null
942 > --- /dev/null
927 > +++ b/lib/place-holder
943 > +++ b/lib/place-holder
928 > @@ -0,0 +1,2 @@
944 > @@ -0,0 +1,2 @@
929 > +perforce does not maintain empty directories.
945 > +perforce does not maintain empty directories.
930 > +this file helps.
946 > +this file helps.
931 > diff --git a/pkg/place-holder b/pkg/place-holder
947 > diff --git a/pkg/place-holder b/pkg/place-holder
932 > new file mode 100644
948 > new file mode 100644
933 > --- /dev/null
949 > --- /dev/null
934 > +++ b/pkg/place-holder
950 > +++ b/pkg/place-holder
935 > @@ -0,0 +1,2 @@
951 > @@ -0,0 +1,2 @@
936 > +perforce does not maintain empty directories.
952 > +perforce does not maintain empty directories.
937 > +this file helps.
953 > +this file helps.
938 > diff --git a/src/cmd/gc/mksys.bash b/src/cmd/gc/mksys.bash
954 > diff --git a/src/cmd/gc/mksys.bash b/src/cmd/gc/mksys.bash
939 > old mode 100644
955 > old mode 100644
940 > new mode 100755
956 > new mode 100755
941 > EOF
957 > EOF
942 applying patch from stdin
958 applying patch from stdin
943
959
944 #if execbit
960 #if execbit
945
961
946 $ hg sum
962 $ hg sum
947 parent: 1:d59915696727 tip
963 parent: 1:d59915696727 tip
948 help management of empty pkg and lib directories in perforce
964 help management of empty pkg and lib directories in perforce
949 branch: default
965 branch: default
950 commit: (clean)
966 commit: (clean)
951 update: (current)
967 update: (current)
952
968
953 $ hg diff --git -c tip
969 $ hg diff --git -c tip
954 diff --git a/lib/place-holder b/lib/place-holder
970 diff --git a/lib/place-holder b/lib/place-holder
955 new file mode 100644
971 new file mode 100644
956 --- /dev/null
972 --- /dev/null
957 +++ b/lib/place-holder
973 +++ b/lib/place-holder
958 @@ -0,0 +1,2 @@
974 @@ -0,0 +1,2 @@
959 +perforce does not maintain empty directories.
975 +perforce does not maintain empty directories.
960 +this file helps.
976 +this file helps.
961 diff --git a/pkg/place-holder b/pkg/place-holder
977 diff --git a/pkg/place-holder b/pkg/place-holder
962 new file mode 100644
978 new file mode 100644
963 --- /dev/null
979 --- /dev/null
964 +++ b/pkg/place-holder
980 +++ b/pkg/place-holder
965 @@ -0,0 +1,2 @@
981 @@ -0,0 +1,2 @@
966 +perforce does not maintain empty directories.
982 +perforce does not maintain empty directories.
967 +this file helps.
983 +this file helps.
968 diff --git a/src/cmd/gc/mksys.bash b/src/cmd/gc/mksys.bash
984 diff --git a/src/cmd/gc/mksys.bash b/src/cmd/gc/mksys.bash
969 old mode 100644
985 old mode 100644
970 new mode 100755
986 new mode 100755
971
987
972 #else
988 #else
973
989
974 $ hg sum
990 $ hg sum
975 parent: 1:28f089cc9ccc tip
991 parent: 1:28f089cc9ccc tip
976 help management of empty pkg and lib directories in perforce
992 help management of empty pkg and lib directories in perforce
977 branch: default
993 branch: default
978 commit: (clean)
994 commit: (clean)
979 update: (current)
995 update: (current)
980
996
981 $ hg diff --git -c tip
997 $ hg diff --git -c tip
982 diff --git a/lib/place-holder b/lib/place-holder
998 diff --git a/lib/place-holder b/lib/place-holder
983 new file mode 100644
999 new file mode 100644
984 --- /dev/null
1000 --- /dev/null
985 +++ b/lib/place-holder
1001 +++ b/lib/place-holder
986 @@ -0,0 +1,2 @@
1002 @@ -0,0 +1,2 @@
987 +perforce does not maintain empty directories.
1003 +perforce does not maintain empty directories.
988 +this file helps.
1004 +this file helps.
989 diff --git a/pkg/place-holder b/pkg/place-holder
1005 diff --git a/pkg/place-holder b/pkg/place-holder
990 new file mode 100644
1006 new file mode 100644
991 --- /dev/null
1007 --- /dev/null
992 +++ b/pkg/place-holder
1008 +++ b/pkg/place-holder
993 @@ -0,0 +1,2 @@
1009 @@ -0,0 +1,2 @@
994 +perforce does not maintain empty directories.
1010 +perforce does not maintain empty directories.
995 +this file helps.
1011 +this file helps.
996
1012
997 /* The mode change for mksys.bash is missing here, because on platforms */
1013 /* The mode change for mksys.bash is missing here, because on platforms */
998 /* that don't support execbits, mode changes in patches are ignored when */
1014 /* that don't support execbits, mode changes in patches are ignored when */
999 /* they are imported. This is obviously also the reason for why the hash */
1015 /* they are imported. This is obviously also the reason for why the hash */
1000 /* in the created changeset is different to the one you see above the */
1016 /* in the created changeset is different to the one you see above the */
1001 /* #else clause */
1017 /* #else clause */
1002
1018
1003 #endif
1019 #endif
1004 $ cd ..
1020 $ cd ..
1005
1021
1006
1022
1007 diff lines looking like headers
1023 diff lines looking like headers
1008
1024
1009 $ hg init difflineslikeheaders
1025 $ hg init difflineslikeheaders
1010 $ cd difflineslikeheaders
1026 $ cd difflineslikeheaders
1011 $ echo a >a
1027 $ echo a >a
1012 $ echo b >b
1028 $ echo b >b
1013 $ echo c >c
1029 $ echo c >c
1014 $ hg ci -Am1
1030 $ hg ci -Am1
1015 adding a
1031 adding a
1016 adding b
1032 adding b
1017 adding c
1033 adding c
1018
1034
1019 $ echo "key: value" >>a
1035 $ echo "key: value" >>a
1020 $ echo "key: value" >>b
1036 $ echo "key: value" >>b
1021 $ echo "foo" >>c
1037 $ echo "foo" >>c
1022 $ hg ci -m2
1038 $ hg ci -m2
1023
1039
1024 $ hg up -C 0
1040 $ hg up -C 0
1025 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
1041 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
1026 $ hg diff --git -c1 >want
1042 $ hg diff --git -c1 >want
1027 $ hg diff -c1 | hg import --no-commit -
1043 $ hg diff -c1 | hg import --no-commit -
1028 applying patch from stdin
1044 applying patch from stdin
1029 $ hg diff --git >have
1045 $ hg diff --git >have
1030 $ diff want have
1046 $ diff want have
1031 $ cd ..
1047 $ cd ..
1032
1048
1033 import a unified diff with no lines of context (diff -U0)
1049 import a unified diff with no lines of context (diff -U0)
1034
1050
1035 $ hg init diffzero
1051 $ hg init diffzero
1036 $ cd diffzero
1052 $ cd diffzero
1037 $ cat > f << EOF
1053 $ cat > f << EOF
1038 > c2
1054 > c2
1039 > c4
1055 > c4
1040 > c5
1056 > c5
1041 > EOF
1057 > EOF
1042 $ hg commit -Am0
1058 $ hg commit -Am0
1043 adding f
1059 adding f
1044
1060
1045 $ hg import --no-commit - << EOF
1061 $ hg import --no-commit - << EOF
1046 > # HG changeset patch
1062 > # HG changeset patch
1047 > # User test
1063 > # User test
1048 > # Date 0 0
1064 > # Date 0 0
1049 > # Node ID f4974ab632f3dee767567b0576c0ec9a4508575c
1065 > # Node ID f4974ab632f3dee767567b0576c0ec9a4508575c
1050 > # Parent 8679a12a975b819fae5f7ad3853a2886d143d794
1066 > # Parent 8679a12a975b819fae5f7ad3853a2886d143d794
1051 > 1
1067 > 1
1052 > diff -r 8679a12a975b -r f4974ab632f3 f
1068 > diff -r 8679a12a975b -r f4974ab632f3 f
1053 > --- a/f Thu Jan 01 00:00:00 1970 +0000
1069 > --- a/f Thu Jan 01 00:00:00 1970 +0000
1054 > +++ b/f Thu Jan 01 00:00:00 1970 +0000
1070 > +++ b/f Thu Jan 01 00:00:00 1970 +0000
1055 > @@ -0,0 +1,1 @@
1071 > @@ -0,0 +1,1 @@
1056 > +c1
1072 > +c1
1057 > @@ -1,0 +3,1 @@
1073 > @@ -1,0 +3,1 @@
1058 > +c3
1074 > +c3
1059 > @@ -3,1 +4,0 @@
1075 > @@ -3,1 +4,0 @@
1060 > -c5
1076 > -c5
1061 > EOF
1077 > EOF
1062 applying patch from stdin
1078 applying patch from stdin
1063
1079
1064 $ cat f
1080 $ cat f
1065 c1
1081 c1
1066 c2
1082 c2
1067 c3
1083 c3
1068 c4
1084 c4
1069
1085
1070 $ cd ..
1086 $ cd ..
1071
1087
1072 no segfault while importing a unified diff which start line is zero but chunk
1088 no segfault while importing a unified diff which start line is zero but chunk
1073 size is non-zero
1089 size is non-zero
1074
1090
1075 $ hg init startlinezero
1091 $ hg init startlinezero
1076 $ cd startlinezero
1092 $ cd startlinezero
1077 $ echo foo > foo
1093 $ echo foo > foo
1078 $ hg commit -Amfoo
1094 $ hg commit -Amfoo
1079 adding foo
1095 adding foo
1080
1096
1081 $ hg import --no-commit - << EOF
1097 $ hg import --no-commit - << EOF
1082 > diff a/foo b/foo
1098 > diff a/foo b/foo
1083 > --- a/foo
1099 > --- a/foo
1084 > +++ b/foo
1100 > +++ b/foo
1085 > @@ -0,1 +0,1 @@
1101 > @@ -0,1 +0,1 @@
1086 > foo
1102 > foo
1087 > EOF
1103 > EOF
1088 applying patch from stdin
1104 applying patch from stdin
1089
1105
1090 $ cd ..
1106 $ cd ..
1091
1107
1092 Test corner case involving fuzz and skew
1108 Test corner case involving fuzz and skew
1093
1109
1094 $ hg init morecornercases
1110 $ hg init morecornercases
1095 $ cd morecornercases
1111 $ cd morecornercases
1096
1112
1097 $ cat > 01-no-context-beginning-of-file.diff <<EOF
1113 $ cat > 01-no-context-beginning-of-file.diff <<EOF
1098 > diff --git a/a b/a
1114 > diff --git a/a b/a
1099 > --- a/a
1115 > --- a/a
1100 > +++ b/a
1116 > +++ b/a
1101 > @@ -1,0 +1,1 @@
1117 > @@ -1,0 +1,1 @@
1102 > +line
1118 > +line
1103 > EOF
1119 > EOF
1104
1120
1105 $ cat > 02-no-context-middle-of-file.diff <<EOF
1121 $ cat > 02-no-context-middle-of-file.diff <<EOF
1106 > diff --git a/a b/a
1122 > diff --git a/a b/a
1107 > --- a/a
1123 > --- a/a
1108 > +++ b/a
1124 > +++ b/a
1109 > @@ -1,1 +1,1 @@
1125 > @@ -1,1 +1,1 @@
1110 > -2
1126 > -2
1111 > +add some skew
1127 > +add some skew
1112 > @@ -2,0 +2,1 @@
1128 > @@ -2,0 +2,1 @@
1113 > +line
1129 > +line
1114 > EOF
1130 > EOF
1115
1131
1116 $ cat > 03-no-context-end-of-file.diff <<EOF
1132 $ cat > 03-no-context-end-of-file.diff <<EOF
1117 > diff --git a/a b/a
1133 > diff --git a/a b/a
1118 > --- a/a
1134 > --- a/a
1119 > +++ b/a
1135 > +++ b/a
1120 > @@ -10,0 +10,1 @@
1136 > @@ -10,0 +10,1 @@
1121 > +line
1137 > +line
1122 > EOF
1138 > EOF
1123
1139
1124 $ cat > 04-middle-of-file-completely-fuzzed.diff <<EOF
1140 $ cat > 04-middle-of-file-completely-fuzzed.diff <<EOF
1125 > diff --git a/a b/a
1141 > diff --git a/a b/a
1126 > --- a/a
1142 > --- a/a
1127 > +++ b/a
1143 > +++ b/a
1128 > @@ -1,1 +1,1 @@
1144 > @@ -1,1 +1,1 @@
1129 > -2
1145 > -2
1130 > +add some skew
1146 > +add some skew
1131 > @@ -2,2 +2,3 @@
1147 > @@ -2,2 +2,3 @@
1132 > not matching, should fuzz
1148 > not matching, should fuzz
1133 > ... a bit
1149 > ... a bit
1134 > +line
1150 > +line
1135 > EOF
1151 > EOF
1136
1152
1137 $ cat > a <<EOF
1153 $ cat > a <<EOF
1138 > 1
1154 > 1
1139 > 2
1155 > 2
1140 > 3
1156 > 3
1141 > 4
1157 > 4
1142 > EOF
1158 > EOF
1143 $ hg ci -Am adda a
1159 $ hg ci -Am adda a
1144 $ for p in *.diff; do
1160 $ for p in *.diff; do
1145 > hg import -v --no-commit $p
1161 > hg import -v --no-commit $p
1146 > cat a
1162 > cat a
1147 > hg revert -aqC a
1163 > hg revert -aqC a
1148 > # patch -p1 < $p
1164 > # patch -p1 < $p
1149 > # cat a
1165 > # cat a
1150 > # hg revert -aC a
1166 > # hg revert -aC a
1151 > done
1167 > done
1152 applying 01-no-context-beginning-of-file.diff
1168 applying 01-no-context-beginning-of-file.diff
1153 patching file a
1169 patching file a
1154 applied to working directory
1170 applied to working directory
1155 1
1171 1
1156 line
1172 line
1157 2
1173 2
1158 3
1174 3
1159 4
1175 4
1160 applying 02-no-context-middle-of-file.diff
1176 applying 02-no-context-middle-of-file.diff
1161 patching file a
1177 patching file a
1162 Hunk #1 succeeded at 2 (offset 1 lines).
1178 Hunk #1 succeeded at 2 (offset 1 lines).
1163 Hunk #2 succeeded at 4 (offset 1 lines).
1179 Hunk #2 succeeded at 4 (offset 1 lines).
1164 applied to working directory
1180 applied to working directory
1165 1
1181 1
1166 add some skew
1182 add some skew
1167 3
1183 3
1168 line
1184 line
1169 4
1185 4
1170 applying 03-no-context-end-of-file.diff
1186 applying 03-no-context-end-of-file.diff
1171 patching file a
1187 patching file a
1172 Hunk #1 succeeded at 5 (offset -6 lines).
1188 Hunk #1 succeeded at 5 (offset -6 lines).
1173 applied to working directory
1189 applied to working directory
1174 1
1190 1
1175 2
1191 2
1176 3
1192 3
1177 4
1193 4
1178 line
1194 line
1179 applying 04-middle-of-file-completely-fuzzed.diff
1195 applying 04-middle-of-file-completely-fuzzed.diff
1180 patching file a
1196 patching file a
1181 Hunk #1 succeeded at 2 (offset 1 lines).
1197 Hunk #1 succeeded at 2 (offset 1 lines).
1182 Hunk #2 succeeded at 5 with fuzz 2 (offset 1 lines).
1198 Hunk #2 succeeded at 5 with fuzz 2 (offset 1 lines).
1183 applied to working directory
1199 applied to working directory
1184 1
1200 1
1185 add some skew
1201 add some skew
1186 3
1202 3
1187 4
1203 4
1188 line
1204 line
1189 $ cd ..
1205 $ cd ..
1190
1206
1191 Test partial application
1207 Test partial application
1192 ------------------------
1208 ------------------------
1193
1209
1194 prepare a stack of patches depending on each other
1210 prepare a stack of patches depending on each other
1195
1211
1196 $ hg init partial
1212 $ hg init partial
1197 $ cd partial
1213 $ cd partial
1198 $ cat << EOF > a
1214 $ cat << EOF > a
1199 > one
1215 > one
1200 > two
1216 > two
1201 > three
1217 > three
1202 > four
1218 > four
1203 > five
1219 > five
1204 > six
1220 > six
1205 > seven
1221 > seven
1206 > EOF
1222 > EOF
1207 $ hg add a
1223 $ hg add a
1208 $ echo 'b' > b
1224 $ echo 'b' > b
1209 $ hg add b
1225 $ hg add b
1210 $ hg commit -m 'initial' -u Babar
1226 $ hg commit -m 'initial' -u Babar
1211 $ cat << EOF > a
1227 $ cat << EOF > a
1212 > one
1228 > one
1213 > two
1229 > two
1214 > 3
1230 > 3
1215 > four
1231 > four
1216 > five
1232 > five
1217 > six
1233 > six
1218 > seven
1234 > seven
1219 > EOF
1235 > EOF
1220 $ hg commit -m 'three' -u Celeste
1236 $ hg commit -m 'three' -u Celeste
1221 $ cat << EOF > a
1237 $ cat << EOF > a
1222 > one
1238 > one
1223 > two
1239 > two
1224 > 3
1240 > 3
1225 > 4
1241 > 4
1226 > five
1242 > five
1227 > six
1243 > six
1228 > seven
1244 > seven
1229 > EOF
1245 > EOF
1230 $ hg commit -m 'four' -u Rataxes
1246 $ hg commit -m 'four' -u Rataxes
1231 $ cat << EOF > a
1247 $ cat << EOF > a
1232 > one
1248 > one
1233 > two
1249 > two
1234 > 3
1250 > 3
1235 > 4
1251 > 4
1236 > 5
1252 > 5
1237 > six
1253 > six
1238 > seven
1254 > seven
1239 > EOF
1255 > EOF
1240 $ echo bb >> b
1256 $ echo bb >> b
1241 $ hg commit -m 'five' -u Arthur
1257 $ hg commit -m 'five' -u Arthur
1242 $ echo 'Babar' > jungle
1258 $ echo 'Babar' > jungle
1243 $ hg add jungle
1259 $ hg add jungle
1244 $ hg ci -m 'jungle' -u Zephir
1260 $ hg ci -m 'jungle' -u Zephir
1245 $ echo 'Celeste' >> jungle
1261 $ echo 'Celeste' >> jungle
1246 $ hg ci -m 'extended jungle' -u Cornelius
1262 $ hg ci -m 'extended jungle' -u Cornelius
1247 $ hg log -G --template '{desc|firstline} [{author}] {diffstat}\n'
1263 $ hg log -G --template '{desc|firstline} [{author}] {diffstat}\n'
1248 @ extended jungle [Cornelius] 1: +1/-0
1264 @ extended jungle [Cornelius] 1: +1/-0
1249 |
1265 |
1250 o jungle [Zephir] 1: +1/-0
1266 o jungle [Zephir] 1: +1/-0
1251 |
1267 |
1252 o five [Arthur] 2: +2/-1
1268 o five [Arthur] 2: +2/-1
1253 |
1269 |
1254 o four [Rataxes] 1: +1/-1
1270 o four [Rataxes] 1: +1/-1
1255 |
1271 |
1256 o three [Celeste] 1: +1/-1
1272 o three [Celeste] 1: +1/-1
1257 |
1273 |
1258 o initial [Babar] 2: +8/-0
1274 o initial [Babar] 2: +8/-0
1259
1275
1260
1276
1261 Importing with some success and some errors:
1277 Importing with some success and some errors:
1262
1278
1263 $ hg update --rev 'desc(initial)'
1279 $ hg update --rev 'desc(initial)'
1264 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
1280 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
1265 $ hg export --rev 'desc(five)' | hg import --partial -
1281 $ hg export --rev 'desc(five)' | hg import --partial -
1266 applying patch from stdin
1282 applying patch from stdin
1267 patching file a
1283 patching file a
1268 Hunk #1 FAILED at 1
1284 Hunk #1 FAILED at 1
1269 1 out of 1 hunks FAILED -- saving rejects to file a.rej
1285 1 out of 1 hunks FAILED -- saving rejects to file a.rej
1270 patch applied partially
1286 patch applied partially
1271 (fix the .rej files and run `hg commit --amend`)
1287 (fix the .rej files and run `hg commit --amend`)
1272 [1]
1288 [1]
1273
1289
1274 $ hg log -G --template '{desc|firstline} [{author}] {diffstat}\n'
1290 $ hg log -G --template '{desc|firstline} [{author}] {diffstat}\n'
1275 @ five [Arthur] 1: +1/-0
1291 @ five [Arthur] 1: +1/-0
1276 |
1292 |
1277 | o extended jungle [Cornelius] 1: +1/-0
1293 | o extended jungle [Cornelius] 1: +1/-0
1278 | |
1294 | |
1279 | o jungle [Zephir] 1: +1/-0
1295 | o jungle [Zephir] 1: +1/-0
1280 | |
1296 | |
1281 | o five [Arthur] 2: +2/-1
1297 | o five [Arthur] 2: +2/-1
1282 | |
1298 | |
1283 | o four [Rataxes] 1: +1/-1
1299 | o four [Rataxes] 1: +1/-1
1284 | |
1300 | |
1285 | o three [Celeste] 1: +1/-1
1301 | o three [Celeste] 1: +1/-1
1286 |/
1302 |/
1287 o initial [Babar] 2: +8/-0
1303 o initial [Babar] 2: +8/-0
1288
1304
1289 $ hg export
1305 $ hg export
1290 # HG changeset patch
1306 # HG changeset patch
1291 # User Arthur
1307 # User Arthur
1292 # Date 0 0
1308 # Date 0 0
1293 # Thu Jan 01 00:00:00 1970 +0000
1309 # Thu Jan 01 00:00:00 1970 +0000
1294 # Node ID 26e6446bb2526e2be1037935f5fca2b2706f1509
1310 # Node ID 26e6446bb2526e2be1037935f5fca2b2706f1509
1295 # Parent 8e4f0351909eae6b9cf68c2c076cb54c42b54b2e
1311 # Parent 8e4f0351909eae6b9cf68c2c076cb54c42b54b2e
1296 five
1312 five
1297
1313
1298 diff -r 8e4f0351909e -r 26e6446bb252 b
1314 diff -r 8e4f0351909e -r 26e6446bb252 b
1299 --- a/b Thu Jan 01 00:00:00 1970 +0000
1315 --- a/b Thu Jan 01 00:00:00 1970 +0000
1300 +++ b/b Thu Jan 01 00:00:00 1970 +0000
1316 +++ b/b Thu Jan 01 00:00:00 1970 +0000
1301 @@ -1,1 +1,2 @@
1317 @@ -1,1 +1,2 @@
1302 b
1318 b
1303 +bb
1319 +bb
1304 $ hg status -c .
1320 $ hg status -c .
1305 C a
1321 C a
1306 C b
1322 C b
1307 $ ls
1323 $ ls
1308 a
1324 a
1309 a.rej
1325 a.rej
1310 b
1326 b
1311
1327
1312 Importing with zero success:
1328 Importing with zero success:
1313
1329
1314 $ hg update --rev 'desc(initial)'
1330 $ hg update --rev 'desc(initial)'
1315 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1331 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1316 $ hg export --rev 'desc(four)' | hg import --partial -
1332 $ hg export --rev 'desc(four)' | hg import --partial -
1317 applying patch from stdin
1333 applying patch from stdin
1318 patching file a
1334 patching file a
1319 Hunk #1 FAILED at 0
1335 Hunk #1 FAILED at 0
1320 1 out of 1 hunks FAILED -- saving rejects to file a.rej
1336 1 out of 1 hunks FAILED -- saving rejects to file a.rej
1321 patch applied partially
1337 patch applied partially
1322 (fix the .rej files and run `hg commit --amend`)
1338 (fix the .rej files and run `hg commit --amend`)
1323 [1]
1339 [1]
1324
1340
1325 $ hg log -G --template '{desc|firstline} [{author}] {diffstat}\n'
1341 $ hg log -G --template '{desc|firstline} [{author}] {diffstat}\n'
1326 @ four [Rataxes] 0: +0/-0
1342 @ four [Rataxes] 0: +0/-0
1327 |
1343 |
1328 | o five [Arthur] 1: +1/-0
1344 | o five [Arthur] 1: +1/-0
1329 |/
1345 |/
1330 | o extended jungle [Cornelius] 1: +1/-0
1346 | o extended jungle [Cornelius] 1: +1/-0
1331 | |
1347 | |
1332 | o jungle [Zephir] 1: +1/-0
1348 | o jungle [Zephir] 1: +1/-0
1333 | |
1349 | |
1334 | o five [Arthur] 2: +2/-1
1350 | o five [Arthur] 2: +2/-1
1335 | |
1351 | |
1336 | o four [Rataxes] 1: +1/-1
1352 | o four [Rataxes] 1: +1/-1
1337 | |
1353 | |
1338 | o three [Celeste] 1: +1/-1
1354 | o three [Celeste] 1: +1/-1
1339 |/
1355 |/
1340 o initial [Babar] 2: +8/-0
1356 o initial [Babar] 2: +8/-0
1341
1357
1342 $ hg export
1358 $ hg export
1343 # HG changeset patch
1359 # HG changeset patch
1344 # User Rataxes
1360 # User Rataxes
1345 # Date 0 0
1361 # Date 0 0
1346 # Thu Jan 01 00:00:00 1970 +0000
1362 # Thu Jan 01 00:00:00 1970 +0000
1347 # Node ID cb9b1847a74d9ad52e93becaf14b98dbcc274e1e
1363 # Node ID cb9b1847a74d9ad52e93becaf14b98dbcc274e1e
1348 # Parent 8e4f0351909eae6b9cf68c2c076cb54c42b54b2e
1364 # Parent 8e4f0351909eae6b9cf68c2c076cb54c42b54b2e
1349 four
1365 four
1350
1366
1351 $ hg status -c .
1367 $ hg status -c .
1352 C a
1368 C a
1353 C b
1369 C b
1354 $ ls
1370 $ ls
1355 a
1371 a
1356 a.rej
1372 a.rej
1357 b
1373 b
1358
1374
1359 Importing with unknown file:
1375 Importing with unknown file:
1360
1376
1361 $ hg update --rev 'desc(initial)'
1377 $ hg update --rev 'desc(initial)'
1362 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1378 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1363 $ hg export --rev 'desc("extended jungle")' | hg import --partial -
1379 $ hg export --rev 'desc("extended jungle")' | hg import --partial -
1364 applying patch from stdin
1380 applying patch from stdin
1365 unable to find 'jungle' for patching
1381 unable to find 'jungle' for patching
1366 1 out of 1 hunks FAILED -- saving rejects to file jungle.rej
1382 1 out of 1 hunks FAILED -- saving rejects to file jungle.rej
1367 patch applied partially
1383 patch applied partially
1368 (fix the .rej files and run `hg commit --amend`)
1384 (fix the .rej files and run `hg commit --amend`)
1369 [1]
1385 [1]
1370
1386
1371 $ hg log -G --template '{desc|firstline} [{author}] {diffstat}\n'
1387 $ hg log -G --template '{desc|firstline} [{author}] {diffstat}\n'
1372 @ extended jungle [Cornelius] 0: +0/-0
1388 @ extended jungle [Cornelius] 0: +0/-0
1373 |
1389 |
1374 | o four [Rataxes] 0: +0/-0
1390 | o four [Rataxes] 0: +0/-0
1375 |/
1391 |/
1376 | o five [Arthur] 1: +1/-0
1392 | o five [Arthur] 1: +1/-0
1377 |/
1393 |/
1378 | o extended jungle [Cornelius] 1: +1/-0
1394 | o extended jungle [Cornelius] 1: +1/-0
1379 | |
1395 | |
1380 | o jungle [Zephir] 1: +1/-0
1396 | o jungle [Zephir] 1: +1/-0
1381 | |
1397 | |
1382 | o five [Arthur] 2: +2/-1
1398 | o five [Arthur] 2: +2/-1
1383 | |
1399 | |
1384 | o four [Rataxes] 1: +1/-1
1400 | o four [Rataxes] 1: +1/-1
1385 | |
1401 | |
1386 | o three [Celeste] 1: +1/-1
1402 | o three [Celeste] 1: +1/-1
1387 |/
1403 |/
1388 o initial [Babar] 2: +8/-0
1404 o initial [Babar] 2: +8/-0
1389
1405
1390 $ hg export
1406 $ hg export
1391 # HG changeset patch
1407 # HG changeset patch
1392 # User Cornelius
1408 # User Cornelius
1393 # Date 0 0
1409 # Date 0 0
1394 # Thu Jan 01 00:00:00 1970 +0000
1410 # Thu Jan 01 00:00:00 1970 +0000
1395 # Node ID 1fb1f86bef43c5a75918178f8d23c29fb0a7398d
1411 # Node ID 1fb1f86bef43c5a75918178f8d23c29fb0a7398d
1396 # Parent 8e4f0351909eae6b9cf68c2c076cb54c42b54b2e
1412 # Parent 8e4f0351909eae6b9cf68c2c076cb54c42b54b2e
1397 extended jungle
1413 extended jungle
1398
1414
1399 $ hg status -c .
1415 $ hg status -c .
1400 C a
1416 C a
1401 C b
1417 C b
1402 $ ls
1418 $ ls
1403 a
1419 a
1404 a.rej
1420 a.rej
1405 b
1421 b
1406 jungle.rej
1422 jungle.rej
1407
1423
1408 Importing multiple failing patches:
1424 Importing multiple failing patches:
1409
1425
1410 $ hg update --rev 'desc(initial)'
1426 $ hg update --rev 'desc(initial)'
1411 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1427 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1412 $ echo 'B' > b # just to make another commit
1428 $ echo 'B' > b # just to make another commit
1413 $ hg commit -m "a new base"
1429 $ hg commit -m "a new base"
1414 created new head
1430 created new head
1415 $ hg export --rev 'desc("extended jungle") + desc("four")' | hg import --partial -
1431 $ hg export --rev 'desc("extended jungle") + desc("four")' | hg import --partial -
1416 applying patch from stdin
1432 applying patch from stdin
1417 patching file a
1433 patching file a
1418 Hunk #1 FAILED at 0
1434 Hunk #1 FAILED at 0
1419 1 out of 1 hunks FAILED -- saving rejects to file a.rej
1435 1 out of 1 hunks FAILED -- saving rejects to file a.rej
1420 patch applied partially
1436 patch applied partially
1421 (fix the .rej files and run `hg commit --amend`)
1437 (fix the .rej files and run `hg commit --amend`)
1422 [1]
1438 [1]
1423 $ hg log -G --template '{desc|firstline} [{author}] {diffstat}\n'
1439 $ hg log -G --template '{desc|firstline} [{author}] {diffstat}\n'
1424 @ four [Rataxes] 0: +0/-0
1440 @ four [Rataxes] 0: +0/-0
1425 |
1441 |
1426 o a new base [test] 1: +1/-1
1442 o a new base [test] 1: +1/-1
1427 |
1443 |
1428 | o extended jungle [Cornelius] 0: +0/-0
1444 | o extended jungle [Cornelius] 0: +0/-0
1429 |/
1445 |/
1430 | o four [Rataxes] 0: +0/-0
1446 | o four [Rataxes] 0: +0/-0
1431 |/
1447 |/
1432 | o five [Arthur] 1: +1/-0
1448 | o five [Arthur] 1: +1/-0
1433 |/
1449 |/
1434 | o extended jungle [Cornelius] 1: +1/-0
1450 | o extended jungle [Cornelius] 1: +1/-0
1435 | |
1451 | |
1436 | o jungle [Zephir] 1: +1/-0
1452 | o jungle [Zephir] 1: +1/-0
1437 | |
1453 | |
1438 | o five [Arthur] 2: +2/-1
1454 | o five [Arthur] 2: +2/-1
1439 | |
1455 | |
1440 | o four [Rataxes] 1: +1/-1
1456 | o four [Rataxes] 1: +1/-1
1441 | |
1457 | |
1442 | o three [Celeste] 1: +1/-1
1458 | o three [Celeste] 1: +1/-1
1443 |/
1459 |/
1444 o initial [Babar] 2: +8/-0
1460 o initial [Babar] 2: +8/-0
1445
1461
1446 $ hg export
1462 $ hg export
1447 # HG changeset patch
1463 # HG changeset patch
1448 # User Rataxes
1464 # User Rataxes
1449 # Date 0 0
1465 # Date 0 0
1450 # Thu Jan 01 00:00:00 1970 +0000
1466 # Thu Jan 01 00:00:00 1970 +0000
1451 # Node ID a9d7b6d0ffbb4eb12b7d5939250fcd42e8930a1d
1467 # Node ID a9d7b6d0ffbb4eb12b7d5939250fcd42e8930a1d
1452 # Parent f59f8d2e95a8ca5b1b4ca64320140da85f3b44fd
1468 # Parent f59f8d2e95a8ca5b1b4ca64320140da85f3b44fd
1453 four
1469 four
1454
1470
1455 $ hg status -c .
1471 $ hg status -c .
1456 C a
1472 C a
1457 C b
1473 C b
General Comments 0
You need to be logged in to leave comments. Login now