##// END OF EJS Templates
import: allow processing of extra part header during import...
Pierre-Yves David -
r26561:1f14920a default
parent child Browse files
Show More
@@ -1,3405 +1,3424 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, bin, nullid, nullrev, short
8 from node import hex, bin, nullid, nullrev, short
9 from i18n import _
9 from i18n import _
10 import os, sys, errno, re, tempfile, cStringIO, shutil
10 import os, sys, errno, re, tempfile, cStringIO, shutil
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 repair, graphmod, revset, phases, obsolete, pathutil
13 import repair, graphmod, revset, phases, obsolete, pathutil
14 import changelog
14 import changelog
15 import bookmarks
15 import bookmarks
16 import encoding
16 import encoding
17 import formatter
17 import formatter
18 import crecord as crecordmod
18 import crecord as crecordmod
19 import lock as lockmod
19 import lock as lockmod
20
20
21 def ishunk(x):
21 def ishunk(x):
22 hunkclasses = (crecordmod.uihunk, patch.recordhunk)
22 hunkclasses = (crecordmod.uihunk, patch.recordhunk)
23 return isinstance(x, hunkclasses)
23 return isinstance(x, hunkclasses)
24
24
25 def newandmodified(chunks, originalchunks):
25 def newandmodified(chunks, originalchunks):
26 newlyaddedandmodifiedfiles = set()
26 newlyaddedandmodifiedfiles = set()
27 for chunk in chunks:
27 for chunk in chunks:
28 if ishunk(chunk) and chunk.header.isnewfile() and chunk not in \
28 if ishunk(chunk) and chunk.header.isnewfile() and chunk not in \
29 originalchunks:
29 originalchunks:
30 newlyaddedandmodifiedfiles.add(chunk.header.filename())
30 newlyaddedandmodifiedfiles.add(chunk.header.filename())
31 return newlyaddedandmodifiedfiles
31 return newlyaddedandmodifiedfiles
32
32
33 def parsealiases(cmd):
33 def parsealiases(cmd):
34 return cmd.lstrip("^").split("|")
34 return cmd.lstrip("^").split("|")
35
35
36 def setupwrapcolorwrite(ui):
36 def setupwrapcolorwrite(ui):
37 # wrap ui.write so diff output can be labeled/colorized
37 # wrap ui.write so diff output can be labeled/colorized
38 def wrapwrite(orig, *args, **kw):
38 def wrapwrite(orig, *args, **kw):
39 label = kw.pop('label', '')
39 label = kw.pop('label', '')
40 for chunk, l in patch.difflabel(lambda: args):
40 for chunk, l in patch.difflabel(lambda: args):
41 orig(chunk, label=label + l)
41 orig(chunk, label=label + l)
42
42
43 oldwrite = ui.write
43 oldwrite = ui.write
44 def wrap(*args, **kwargs):
44 def wrap(*args, **kwargs):
45 return wrapwrite(oldwrite, *args, **kwargs)
45 return wrapwrite(oldwrite, *args, **kwargs)
46 setattr(ui, 'write', wrap)
46 setattr(ui, 'write', wrap)
47 return oldwrite
47 return oldwrite
48
48
49 def filterchunks(ui, originalhunks, usecurses, testfile, operation=None):
49 def filterchunks(ui, originalhunks, usecurses, testfile, operation=None):
50 if usecurses:
50 if usecurses:
51 if testfile:
51 if testfile:
52 recordfn = crecordmod.testdecorator(testfile,
52 recordfn = crecordmod.testdecorator(testfile,
53 crecordmod.testchunkselector)
53 crecordmod.testchunkselector)
54 else:
54 else:
55 recordfn = crecordmod.chunkselector
55 recordfn = crecordmod.chunkselector
56
56
57 return crecordmod.filterpatch(ui, originalhunks, recordfn, operation)
57 return crecordmod.filterpatch(ui, originalhunks, recordfn, operation)
58
58
59 else:
59 else:
60 return patch.filterpatch(ui, originalhunks, operation)
60 return patch.filterpatch(ui, originalhunks, operation)
61
61
62 def recordfilter(ui, originalhunks, operation=None):
62 def recordfilter(ui, originalhunks, operation=None):
63 """ Prompts the user to filter the originalhunks and return a list of
63 """ Prompts the user to filter the originalhunks and return a list of
64 selected hunks.
64 selected hunks.
65 *operation* is used for ui purposes to indicate the user
65 *operation* is used for ui purposes to indicate the user
66 what kind of filtering they are doing: reverting, commiting, shelving, etc.
66 what kind of filtering they are doing: reverting, commiting, shelving, etc.
67 *operation* has to be a translated string.
67 *operation* has to be a translated string.
68 """
68 """
69 usecurses = ui.configbool('experimental', 'crecord', False)
69 usecurses = ui.configbool('experimental', 'crecord', False)
70 testfile = ui.config('experimental', 'crecordtest', None)
70 testfile = ui.config('experimental', 'crecordtest', None)
71 oldwrite = setupwrapcolorwrite(ui)
71 oldwrite = setupwrapcolorwrite(ui)
72 try:
72 try:
73 newchunks = filterchunks(ui, originalhunks, usecurses, testfile,
73 newchunks = filterchunks(ui, originalhunks, usecurses, testfile,
74 operation)
74 operation)
75 finally:
75 finally:
76 ui.write = oldwrite
76 ui.write = oldwrite
77 return newchunks
77 return newchunks
78
78
79 def dorecord(ui, repo, commitfunc, cmdsuggest, backupall,
79 def dorecord(ui, repo, commitfunc, cmdsuggest, backupall,
80 filterfn, *pats, **opts):
80 filterfn, *pats, **opts):
81 import merge as mergemod
81 import merge as mergemod
82
82
83 if not ui.interactive():
83 if not ui.interactive():
84 if cmdsuggest:
84 if cmdsuggest:
85 msg = _('running non-interactively, use %s instead') % cmdsuggest
85 msg = _('running non-interactively, use %s instead') % cmdsuggest
86 else:
86 else:
87 msg = _('running non-interactively')
87 msg = _('running non-interactively')
88 raise util.Abort(msg)
88 raise util.Abort(msg)
89
89
90 # make sure username is set before going interactive
90 # make sure username is set before going interactive
91 if not opts.get('user'):
91 if not opts.get('user'):
92 ui.username() # raise exception, username not provided
92 ui.username() # raise exception, username not provided
93
93
94 def recordfunc(ui, repo, message, match, opts):
94 def recordfunc(ui, repo, message, match, opts):
95 """This is generic record driver.
95 """This is generic record driver.
96
96
97 Its job is to interactively filter local changes, and
97 Its job is to interactively filter local changes, and
98 accordingly prepare working directory into a state in which the
98 accordingly prepare working directory into a state in which the
99 job can be delegated to a non-interactive commit command such as
99 job can be delegated to a non-interactive commit command such as
100 'commit' or 'qrefresh'.
100 'commit' or 'qrefresh'.
101
101
102 After the actual job is done by non-interactive command, the
102 After the actual job is done by non-interactive command, the
103 working directory is restored to its original state.
103 working directory is restored to its original state.
104
104
105 In the end we'll record interesting changes, and everything else
105 In the end we'll record interesting changes, and everything else
106 will be left in place, so the user can continue working.
106 will be left in place, so the user can continue working.
107 """
107 """
108
108
109 checkunfinished(repo, commit=True)
109 checkunfinished(repo, commit=True)
110 merge = len(repo[None].parents()) > 1
110 merge = len(repo[None].parents()) > 1
111 if merge:
111 if merge:
112 raise util.Abort(_('cannot partially commit a merge '
112 raise util.Abort(_('cannot partially commit a merge '
113 '(use "hg commit" instead)'))
113 '(use "hg commit" instead)'))
114
114
115 status = repo.status(match=match)
115 status = repo.status(match=match)
116 diffopts = patch.difffeatureopts(ui, opts=opts, whitespace=True)
116 diffopts = patch.difffeatureopts(ui, opts=opts, whitespace=True)
117 diffopts.nodates = True
117 diffopts.nodates = True
118 diffopts.git = True
118 diffopts.git = True
119 originaldiff = patch.diff(repo, changes=status, opts=diffopts)
119 originaldiff = patch.diff(repo, changes=status, opts=diffopts)
120 originalchunks = patch.parsepatch(originaldiff)
120 originalchunks = patch.parsepatch(originaldiff)
121
121
122 # 1. filter patch, so we have intending-to apply subset of it
122 # 1. filter patch, so we have intending-to apply subset of it
123 try:
123 try:
124 chunks = filterfn(ui, originalchunks)
124 chunks = filterfn(ui, originalchunks)
125 except patch.PatchError as err:
125 except patch.PatchError as err:
126 raise util.Abort(_('error parsing patch: %s') % err)
126 raise util.Abort(_('error parsing patch: %s') % err)
127
127
128 # We need to keep a backup of files that have been newly added and
128 # We need to keep a backup of files that have been newly added and
129 # modified during the recording process because there is a previous
129 # modified during the recording process because there is a previous
130 # version without the edit in the workdir
130 # version without the edit in the workdir
131 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
131 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
132 contenders = set()
132 contenders = set()
133 for h in chunks:
133 for h in chunks:
134 try:
134 try:
135 contenders.update(set(h.files()))
135 contenders.update(set(h.files()))
136 except AttributeError:
136 except AttributeError:
137 pass
137 pass
138
138
139 changed = status.modified + status.added + status.removed
139 changed = status.modified + status.added + status.removed
140 newfiles = [f for f in changed if f in contenders]
140 newfiles = [f for f in changed if f in contenders]
141 if not newfiles:
141 if not newfiles:
142 ui.status(_('no changes to record\n'))
142 ui.status(_('no changes to record\n'))
143 return 0
143 return 0
144
144
145 modified = set(status.modified)
145 modified = set(status.modified)
146
146
147 # 2. backup changed files, so we can restore them in the end
147 # 2. backup changed files, so we can restore them in the end
148
148
149 if backupall:
149 if backupall:
150 tobackup = changed
150 tobackup = changed
151 else:
151 else:
152 tobackup = [f for f in newfiles if f in modified or f in \
152 tobackup = [f for f in newfiles if f in modified or f in \
153 newlyaddedandmodifiedfiles]
153 newlyaddedandmodifiedfiles]
154 backups = {}
154 backups = {}
155 if tobackup:
155 if tobackup:
156 backupdir = repo.join('record-backups')
156 backupdir = repo.join('record-backups')
157 try:
157 try:
158 os.mkdir(backupdir)
158 os.mkdir(backupdir)
159 except OSError as err:
159 except OSError as err:
160 if err.errno != errno.EEXIST:
160 if err.errno != errno.EEXIST:
161 raise
161 raise
162 try:
162 try:
163 # backup continues
163 # backup continues
164 for f in tobackup:
164 for f in tobackup:
165 fd, tmpname = tempfile.mkstemp(prefix=f.replace('/', '_')+'.',
165 fd, tmpname = tempfile.mkstemp(prefix=f.replace('/', '_')+'.',
166 dir=backupdir)
166 dir=backupdir)
167 os.close(fd)
167 os.close(fd)
168 ui.debug('backup %r as %r\n' % (f, tmpname))
168 ui.debug('backup %r as %r\n' % (f, tmpname))
169 util.copyfile(repo.wjoin(f), tmpname)
169 util.copyfile(repo.wjoin(f), tmpname)
170 shutil.copystat(repo.wjoin(f), tmpname)
170 shutil.copystat(repo.wjoin(f), tmpname)
171 backups[f] = tmpname
171 backups[f] = tmpname
172
172
173 fp = cStringIO.StringIO()
173 fp = cStringIO.StringIO()
174 for c in chunks:
174 for c in chunks:
175 fname = c.filename()
175 fname = c.filename()
176 if fname in backups:
176 if fname in backups:
177 c.write(fp)
177 c.write(fp)
178 dopatch = fp.tell()
178 dopatch = fp.tell()
179 fp.seek(0)
179 fp.seek(0)
180
180
181 [os.unlink(repo.wjoin(c)) for c in newlyaddedandmodifiedfiles]
181 [os.unlink(repo.wjoin(c)) for c in newlyaddedandmodifiedfiles]
182 # 3a. apply filtered patch to clean repo (clean)
182 # 3a. apply filtered patch to clean repo (clean)
183 if backups:
183 if backups:
184 # Equivalent to hg.revert
184 # Equivalent to hg.revert
185 choices = lambda key: key in backups
185 choices = lambda key: key in backups
186 mergemod.update(repo, repo.dirstate.p1(),
186 mergemod.update(repo, repo.dirstate.p1(),
187 False, True, choices)
187 False, True, choices)
188
188
189 # 3b. (apply)
189 # 3b. (apply)
190 if dopatch:
190 if dopatch:
191 try:
191 try:
192 ui.debug('applying patch\n')
192 ui.debug('applying patch\n')
193 ui.debug(fp.getvalue())
193 ui.debug(fp.getvalue())
194 patch.internalpatch(ui, repo, fp, 1, eolmode=None)
194 patch.internalpatch(ui, repo, fp, 1, eolmode=None)
195 except patch.PatchError as err:
195 except patch.PatchError as err:
196 raise util.Abort(str(err))
196 raise util.Abort(str(err))
197 del fp
197 del fp
198
198
199 # 4. We prepared working directory according to filtered
199 # 4. We prepared working directory according to filtered
200 # patch. Now is the time to delegate the job to
200 # patch. Now is the time to delegate the job to
201 # commit/qrefresh or the like!
201 # commit/qrefresh or the like!
202
202
203 # Make all of the pathnames absolute.
203 # Make all of the pathnames absolute.
204 newfiles = [repo.wjoin(nf) for nf in newfiles]
204 newfiles = [repo.wjoin(nf) for nf in newfiles]
205 return commitfunc(ui, repo, *newfiles, **opts)
205 return commitfunc(ui, repo, *newfiles, **opts)
206 finally:
206 finally:
207 # 5. finally restore backed-up files
207 # 5. finally restore backed-up files
208 try:
208 try:
209 dirstate = repo.dirstate
209 dirstate = repo.dirstate
210 for realname, tmpname in backups.iteritems():
210 for realname, tmpname in backups.iteritems():
211 ui.debug('restoring %r to %r\n' % (tmpname, realname))
211 ui.debug('restoring %r to %r\n' % (tmpname, realname))
212
212
213 if dirstate[realname] == 'n':
213 if dirstate[realname] == 'n':
214 # without normallookup, restoring timestamp
214 # without normallookup, restoring timestamp
215 # may cause partially committed files
215 # may cause partially committed files
216 # to be treated as unmodified
216 # to be treated as unmodified
217 dirstate.normallookup(realname)
217 dirstate.normallookup(realname)
218
218
219 util.copyfile(tmpname, repo.wjoin(realname))
219 util.copyfile(tmpname, repo.wjoin(realname))
220 # Our calls to copystat() here and above are a
220 # Our calls to copystat() here and above are a
221 # hack to trick any editors that have f open that
221 # hack to trick any editors that have f open that
222 # we haven't modified them.
222 # we haven't modified them.
223 #
223 #
224 # Also note that this racy as an editor could
224 # Also note that this racy as an editor could
225 # notice the file's mtime before we've finished
225 # notice the file's mtime before we've finished
226 # writing it.
226 # writing it.
227 shutil.copystat(tmpname, repo.wjoin(realname))
227 shutil.copystat(tmpname, repo.wjoin(realname))
228 os.unlink(tmpname)
228 os.unlink(tmpname)
229 if tobackup:
229 if tobackup:
230 os.rmdir(backupdir)
230 os.rmdir(backupdir)
231 except OSError:
231 except OSError:
232 pass
232 pass
233
233
234 def recordinwlock(ui, repo, message, match, opts):
234 def recordinwlock(ui, repo, message, match, opts):
235 wlock = repo.wlock()
235 wlock = repo.wlock()
236 try:
236 try:
237 return recordfunc(ui, repo, message, match, opts)
237 return recordfunc(ui, repo, message, match, opts)
238 finally:
238 finally:
239 wlock.release()
239 wlock.release()
240
240
241 return commit(ui, repo, recordinwlock, pats, opts)
241 return commit(ui, repo, recordinwlock, pats, opts)
242
242
243 def findpossible(cmd, table, strict=False):
243 def findpossible(cmd, table, strict=False):
244 """
244 """
245 Return cmd -> (aliases, command table entry)
245 Return cmd -> (aliases, command table entry)
246 for each matching command.
246 for each matching command.
247 Return debug commands (or their aliases) only if no normal command matches.
247 Return debug commands (or their aliases) only if no normal command matches.
248 """
248 """
249 choice = {}
249 choice = {}
250 debugchoice = {}
250 debugchoice = {}
251
251
252 if cmd in table:
252 if cmd in table:
253 # short-circuit exact matches, "log" alias beats "^log|history"
253 # short-circuit exact matches, "log" alias beats "^log|history"
254 keys = [cmd]
254 keys = [cmd]
255 else:
255 else:
256 keys = table.keys()
256 keys = table.keys()
257
257
258 allcmds = []
258 allcmds = []
259 for e in keys:
259 for e in keys:
260 aliases = parsealiases(e)
260 aliases = parsealiases(e)
261 allcmds.extend(aliases)
261 allcmds.extend(aliases)
262 found = None
262 found = None
263 if cmd in aliases:
263 if cmd in aliases:
264 found = cmd
264 found = cmd
265 elif not strict:
265 elif not strict:
266 for a in aliases:
266 for a in aliases:
267 if a.startswith(cmd):
267 if a.startswith(cmd):
268 found = a
268 found = a
269 break
269 break
270 if found is not None:
270 if found is not None:
271 if aliases[0].startswith("debug") or found.startswith("debug"):
271 if aliases[0].startswith("debug") or found.startswith("debug"):
272 debugchoice[found] = (aliases, table[e])
272 debugchoice[found] = (aliases, table[e])
273 else:
273 else:
274 choice[found] = (aliases, table[e])
274 choice[found] = (aliases, table[e])
275
275
276 if not choice and debugchoice:
276 if not choice and debugchoice:
277 choice = debugchoice
277 choice = debugchoice
278
278
279 return choice, allcmds
279 return choice, allcmds
280
280
281 def findcmd(cmd, table, strict=True):
281 def findcmd(cmd, table, strict=True):
282 """Return (aliases, command table entry) for command string."""
282 """Return (aliases, command table entry) for command string."""
283 choice, allcmds = findpossible(cmd, table, strict)
283 choice, allcmds = findpossible(cmd, table, strict)
284
284
285 if cmd in choice:
285 if cmd in choice:
286 return choice[cmd]
286 return choice[cmd]
287
287
288 if len(choice) > 1:
288 if len(choice) > 1:
289 clist = choice.keys()
289 clist = choice.keys()
290 clist.sort()
290 clist.sort()
291 raise error.AmbiguousCommand(cmd, clist)
291 raise error.AmbiguousCommand(cmd, clist)
292
292
293 if choice:
293 if choice:
294 return choice.values()[0]
294 return choice.values()[0]
295
295
296 raise error.UnknownCommand(cmd, allcmds)
296 raise error.UnknownCommand(cmd, allcmds)
297
297
298 def findrepo(p):
298 def findrepo(p):
299 while not os.path.isdir(os.path.join(p, ".hg")):
299 while not os.path.isdir(os.path.join(p, ".hg")):
300 oldp, p = p, os.path.dirname(p)
300 oldp, p = p, os.path.dirname(p)
301 if p == oldp:
301 if p == oldp:
302 return None
302 return None
303
303
304 return p
304 return p
305
305
306 def bailifchanged(repo, merge=True):
306 def bailifchanged(repo, merge=True):
307 if merge and repo.dirstate.p2() != nullid:
307 if merge and repo.dirstate.p2() != nullid:
308 raise util.Abort(_('outstanding uncommitted merge'))
308 raise util.Abort(_('outstanding uncommitted merge'))
309 modified, added, removed, deleted = repo.status()[:4]
309 modified, added, removed, deleted = repo.status()[:4]
310 if modified or added or removed or deleted:
310 if modified or added or removed or deleted:
311 raise util.Abort(_('uncommitted changes'))
311 raise util.Abort(_('uncommitted changes'))
312 ctx = repo[None]
312 ctx = repo[None]
313 for s in sorted(ctx.substate):
313 for s in sorted(ctx.substate):
314 ctx.sub(s).bailifchanged()
314 ctx.sub(s).bailifchanged()
315
315
316 def logmessage(ui, opts):
316 def logmessage(ui, opts):
317 """ get the log message according to -m and -l option """
317 """ get the log message according to -m and -l option """
318 message = opts.get('message')
318 message = opts.get('message')
319 logfile = opts.get('logfile')
319 logfile = opts.get('logfile')
320
320
321 if message and logfile:
321 if message and logfile:
322 raise util.Abort(_('options --message and --logfile are mutually '
322 raise util.Abort(_('options --message and --logfile are mutually '
323 'exclusive'))
323 'exclusive'))
324 if not message and logfile:
324 if not message and logfile:
325 try:
325 try:
326 if logfile == '-':
326 if logfile == '-':
327 message = ui.fin.read()
327 message = ui.fin.read()
328 else:
328 else:
329 message = '\n'.join(util.readfile(logfile).splitlines())
329 message = '\n'.join(util.readfile(logfile).splitlines())
330 except IOError as inst:
330 except IOError as inst:
331 raise util.Abort(_("can't read commit message '%s': %s") %
331 raise util.Abort(_("can't read commit message '%s': %s") %
332 (logfile, inst.strerror))
332 (logfile, inst.strerror))
333 return message
333 return message
334
334
335 def mergeeditform(ctxorbool, baseformname):
335 def mergeeditform(ctxorbool, baseformname):
336 """return appropriate editform name (referencing a committemplate)
336 """return appropriate editform name (referencing a committemplate)
337
337
338 'ctxorbool' is either a ctx to be committed, or a bool indicating whether
338 'ctxorbool' is either a ctx to be committed, or a bool indicating whether
339 merging is committed.
339 merging is committed.
340
340
341 This returns baseformname with '.merge' appended if it is a merge,
341 This returns baseformname with '.merge' appended if it is a merge,
342 otherwise '.normal' is appended.
342 otherwise '.normal' is appended.
343 """
343 """
344 if isinstance(ctxorbool, bool):
344 if isinstance(ctxorbool, bool):
345 if ctxorbool:
345 if ctxorbool:
346 return baseformname + ".merge"
346 return baseformname + ".merge"
347 elif 1 < len(ctxorbool.parents()):
347 elif 1 < len(ctxorbool.parents()):
348 return baseformname + ".merge"
348 return baseformname + ".merge"
349
349
350 return baseformname + ".normal"
350 return baseformname + ".normal"
351
351
352 def getcommiteditor(edit=False, finishdesc=None, extramsg=None,
352 def getcommiteditor(edit=False, finishdesc=None, extramsg=None,
353 editform='', **opts):
353 editform='', **opts):
354 """get appropriate commit message editor according to '--edit' option
354 """get appropriate commit message editor according to '--edit' option
355
355
356 'finishdesc' is a function to be called with edited commit message
356 'finishdesc' is a function to be called with edited commit message
357 (= 'description' of the new changeset) just after editing, but
357 (= 'description' of the new changeset) just after editing, but
358 before checking empty-ness. It should return actual text to be
358 before checking empty-ness. It should return actual text to be
359 stored into history. This allows to change description before
359 stored into history. This allows to change description before
360 storing.
360 storing.
361
361
362 'extramsg' is a extra message to be shown in the editor instead of
362 'extramsg' is a extra message to be shown in the editor instead of
363 'Leave message empty to abort commit' line. 'HG: ' prefix and EOL
363 'Leave message empty to abort commit' line. 'HG: ' prefix and EOL
364 is automatically added.
364 is automatically added.
365
365
366 'editform' is a dot-separated list of names, to distinguish
366 'editform' is a dot-separated list of names, to distinguish
367 the purpose of commit text editing.
367 the purpose of commit text editing.
368
368
369 'getcommiteditor' returns 'commitforceeditor' regardless of
369 'getcommiteditor' returns 'commitforceeditor' regardless of
370 'edit', if one of 'finishdesc' or 'extramsg' is specified, because
370 'edit', if one of 'finishdesc' or 'extramsg' is specified, because
371 they are specific for usage in MQ.
371 they are specific for usage in MQ.
372 """
372 """
373 if edit or finishdesc or extramsg:
373 if edit or finishdesc or extramsg:
374 return lambda r, c, s: commitforceeditor(r, c, s,
374 return lambda r, c, s: commitforceeditor(r, c, s,
375 finishdesc=finishdesc,
375 finishdesc=finishdesc,
376 extramsg=extramsg,
376 extramsg=extramsg,
377 editform=editform)
377 editform=editform)
378 elif editform:
378 elif editform:
379 return lambda r, c, s: commiteditor(r, c, s, editform=editform)
379 return lambda r, c, s: commiteditor(r, c, s, editform=editform)
380 else:
380 else:
381 return commiteditor
381 return commiteditor
382
382
383 def loglimit(opts):
383 def loglimit(opts):
384 """get the log limit according to option -l/--limit"""
384 """get the log limit according to option -l/--limit"""
385 limit = opts.get('limit')
385 limit = opts.get('limit')
386 if limit:
386 if limit:
387 try:
387 try:
388 limit = int(limit)
388 limit = int(limit)
389 except ValueError:
389 except ValueError:
390 raise util.Abort(_('limit must be a positive integer'))
390 raise util.Abort(_('limit must be a positive integer'))
391 if limit <= 0:
391 if limit <= 0:
392 raise util.Abort(_('limit must be positive'))
392 raise util.Abort(_('limit must be positive'))
393 else:
393 else:
394 limit = None
394 limit = None
395 return limit
395 return limit
396
396
397 def makefilename(repo, pat, node, desc=None,
397 def makefilename(repo, pat, node, desc=None,
398 total=None, seqno=None, revwidth=None, pathname=None):
398 total=None, seqno=None, revwidth=None, pathname=None):
399 node_expander = {
399 node_expander = {
400 'H': lambda: hex(node),
400 'H': lambda: hex(node),
401 'R': lambda: str(repo.changelog.rev(node)),
401 'R': lambda: str(repo.changelog.rev(node)),
402 'h': lambda: short(node),
402 'h': lambda: short(node),
403 'm': lambda: re.sub('[^\w]', '_', str(desc))
403 'm': lambda: re.sub('[^\w]', '_', str(desc))
404 }
404 }
405 expander = {
405 expander = {
406 '%': lambda: '%',
406 '%': lambda: '%',
407 'b': lambda: os.path.basename(repo.root),
407 'b': lambda: os.path.basename(repo.root),
408 }
408 }
409
409
410 try:
410 try:
411 if node:
411 if node:
412 expander.update(node_expander)
412 expander.update(node_expander)
413 if node:
413 if node:
414 expander['r'] = (lambda:
414 expander['r'] = (lambda:
415 str(repo.changelog.rev(node)).zfill(revwidth or 0))
415 str(repo.changelog.rev(node)).zfill(revwidth or 0))
416 if total is not None:
416 if total is not None:
417 expander['N'] = lambda: str(total)
417 expander['N'] = lambda: str(total)
418 if seqno is not None:
418 if seqno is not None:
419 expander['n'] = lambda: str(seqno)
419 expander['n'] = lambda: str(seqno)
420 if total is not None and seqno is not None:
420 if total is not None and seqno is not None:
421 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
421 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
422 if pathname is not None:
422 if pathname is not None:
423 expander['s'] = lambda: os.path.basename(pathname)
423 expander['s'] = lambda: os.path.basename(pathname)
424 expander['d'] = lambda: os.path.dirname(pathname) or '.'
424 expander['d'] = lambda: os.path.dirname(pathname) or '.'
425 expander['p'] = lambda: pathname
425 expander['p'] = lambda: pathname
426
426
427 newname = []
427 newname = []
428 patlen = len(pat)
428 patlen = len(pat)
429 i = 0
429 i = 0
430 while i < patlen:
430 while i < patlen:
431 c = pat[i]
431 c = pat[i]
432 if c == '%':
432 if c == '%':
433 i += 1
433 i += 1
434 c = pat[i]
434 c = pat[i]
435 c = expander[c]()
435 c = expander[c]()
436 newname.append(c)
436 newname.append(c)
437 i += 1
437 i += 1
438 return ''.join(newname)
438 return ''.join(newname)
439 except KeyError as inst:
439 except KeyError as inst:
440 raise util.Abort(_("invalid format spec '%%%s' in output filename") %
440 raise util.Abort(_("invalid format spec '%%%s' in output filename") %
441 inst.args[0])
441 inst.args[0])
442
442
443 def makefileobj(repo, pat, node=None, desc=None, total=None,
443 def makefileobj(repo, pat, node=None, desc=None, total=None,
444 seqno=None, revwidth=None, mode='wb', modemap=None,
444 seqno=None, revwidth=None, mode='wb', modemap=None,
445 pathname=None):
445 pathname=None):
446
446
447 writable = mode not in ('r', 'rb')
447 writable = mode not in ('r', 'rb')
448
448
449 if not pat or pat == '-':
449 if not pat or pat == '-':
450 if writable:
450 if writable:
451 fp = repo.ui.fout
451 fp = repo.ui.fout
452 else:
452 else:
453 fp = repo.ui.fin
453 fp = repo.ui.fin
454 if util.safehasattr(fp, 'fileno'):
454 if util.safehasattr(fp, 'fileno'):
455 return os.fdopen(os.dup(fp.fileno()), mode)
455 return os.fdopen(os.dup(fp.fileno()), mode)
456 else:
456 else:
457 # if this fp can't be duped properly, return
457 # if this fp can't be duped properly, return
458 # a dummy object that can be closed
458 # a dummy object that can be closed
459 class wrappedfileobj(object):
459 class wrappedfileobj(object):
460 noop = lambda x: None
460 noop = lambda x: None
461 def __init__(self, f):
461 def __init__(self, f):
462 self.f = f
462 self.f = f
463 def __getattr__(self, attr):
463 def __getattr__(self, attr):
464 if attr == 'close':
464 if attr == 'close':
465 return self.noop
465 return self.noop
466 else:
466 else:
467 return getattr(self.f, attr)
467 return getattr(self.f, attr)
468
468
469 return wrappedfileobj(fp)
469 return wrappedfileobj(fp)
470 if util.safehasattr(pat, 'write') and writable:
470 if util.safehasattr(pat, 'write') and writable:
471 return pat
471 return pat
472 if util.safehasattr(pat, 'read') and 'r' in mode:
472 if util.safehasattr(pat, 'read') and 'r' in mode:
473 return pat
473 return pat
474 fn = makefilename(repo, pat, node, desc, total, seqno, revwidth, pathname)
474 fn = makefilename(repo, pat, node, desc, total, seqno, revwidth, pathname)
475 if modemap is not None:
475 if modemap is not None:
476 mode = modemap.get(fn, mode)
476 mode = modemap.get(fn, mode)
477 if mode == 'wb':
477 if mode == 'wb':
478 modemap[fn] = 'ab'
478 modemap[fn] = 'ab'
479 return open(fn, mode)
479 return open(fn, mode)
480
480
481 def openrevlog(repo, cmd, file_, opts):
481 def openrevlog(repo, cmd, file_, opts):
482 """opens the changelog, manifest, a filelog or a given revlog"""
482 """opens the changelog, manifest, a filelog or a given revlog"""
483 cl = opts['changelog']
483 cl = opts['changelog']
484 mf = opts['manifest']
484 mf = opts['manifest']
485 dir = opts['dir']
485 dir = opts['dir']
486 msg = None
486 msg = None
487 if cl and mf:
487 if cl and mf:
488 msg = _('cannot specify --changelog and --manifest at the same time')
488 msg = _('cannot specify --changelog and --manifest at the same time')
489 elif cl and dir:
489 elif cl and dir:
490 msg = _('cannot specify --changelog and --dir at the same time')
490 msg = _('cannot specify --changelog and --dir at the same time')
491 elif cl or mf:
491 elif cl or mf:
492 if file_:
492 if file_:
493 msg = _('cannot specify filename with --changelog or --manifest')
493 msg = _('cannot specify filename with --changelog or --manifest')
494 elif not repo:
494 elif not repo:
495 msg = _('cannot specify --changelog or --manifest or --dir '
495 msg = _('cannot specify --changelog or --manifest or --dir '
496 'without a repository')
496 'without a repository')
497 if msg:
497 if msg:
498 raise util.Abort(msg)
498 raise util.Abort(msg)
499
499
500 r = None
500 r = None
501 if repo:
501 if repo:
502 if cl:
502 if cl:
503 r = repo.unfiltered().changelog
503 r = repo.unfiltered().changelog
504 elif dir:
504 elif dir:
505 if 'treemanifest' not in repo.requirements:
505 if 'treemanifest' not in repo.requirements:
506 raise util.Abort(_("--dir can only be used on repos with "
506 raise util.Abort(_("--dir can only be used on repos with "
507 "treemanifest enabled"))
507 "treemanifest enabled"))
508 dirlog = repo.dirlog(file_)
508 dirlog = repo.dirlog(file_)
509 if len(dirlog):
509 if len(dirlog):
510 r = dirlog
510 r = dirlog
511 elif mf:
511 elif mf:
512 r = repo.manifest
512 r = repo.manifest
513 elif file_:
513 elif file_:
514 filelog = repo.file(file_)
514 filelog = repo.file(file_)
515 if len(filelog):
515 if len(filelog):
516 r = filelog
516 r = filelog
517 if not r:
517 if not r:
518 if not file_:
518 if not file_:
519 raise error.CommandError(cmd, _('invalid arguments'))
519 raise error.CommandError(cmd, _('invalid arguments'))
520 if not os.path.isfile(file_):
520 if not os.path.isfile(file_):
521 raise util.Abort(_("revlog '%s' not found") % file_)
521 raise util.Abort(_("revlog '%s' not found") % file_)
522 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False),
522 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False),
523 file_[:-2] + ".i")
523 file_[:-2] + ".i")
524 return r
524 return r
525
525
526 def copy(ui, repo, pats, opts, rename=False):
526 def copy(ui, repo, pats, opts, rename=False):
527 # called with the repo lock held
527 # called with the repo lock held
528 #
528 #
529 # hgsep => pathname that uses "/" to separate directories
529 # hgsep => pathname that uses "/" to separate directories
530 # ossep => pathname that uses os.sep to separate directories
530 # ossep => pathname that uses os.sep to separate directories
531 cwd = repo.getcwd()
531 cwd = repo.getcwd()
532 targets = {}
532 targets = {}
533 after = opts.get("after")
533 after = opts.get("after")
534 dryrun = opts.get("dry_run")
534 dryrun = opts.get("dry_run")
535 wctx = repo[None]
535 wctx = repo[None]
536
536
537 def walkpat(pat):
537 def walkpat(pat):
538 srcs = []
538 srcs = []
539 if after:
539 if after:
540 badstates = '?'
540 badstates = '?'
541 else:
541 else:
542 badstates = '?r'
542 badstates = '?r'
543 m = scmutil.match(repo[None], [pat], opts, globbed=True)
543 m = scmutil.match(repo[None], [pat], opts, globbed=True)
544 for abs in repo.walk(m):
544 for abs in repo.walk(m):
545 state = repo.dirstate[abs]
545 state = repo.dirstate[abs]
546 rel = m.rel(abs)
546 rel = m.rel(abs)
547 exact = m.exact(abs)
547 exact = m.exact(abs)
548 if state in badstates:
548 if state in badstates:
549 if exact and state == '?':
549 if exact and state == '?':
550 ui.warn(_('%s: not copying - file is not managed\n') % rel)
550 ui.warn(_('%s: not copying - file is not managed\n') % rel)
551 if exact and state == 'r':
551 if exact and state == 'r':
552 ui.warn(_('%s: not copying - file has been marked for'
552 ui.warn(_('%s: not copying - file has been marked for'
553 ' remove\n') % rel)
553 ' remove\n') % rel)
554 continue
554 continue
555 # abs: hgsep
555 # abs: hgsep
556 # rel: ossep
556 # rel: ossep
557 srcs.append((abs, rel, exact))
557 srcs.append((abs, rel, exact))
558 return srcs
558 return srcs
559
559
560 # abssrc: hgsep
560 # abssrc: hgsep
561 # relsrc: ossep
561 # relsrc: ossep
562 # otarget: ossep
562 # otarget: ossep
563 def copyfile(abssrc, relsrc, otarget, exact):
563 def copyfile(abssrc, relsrc, otarget, exact):
564 abstarget = pathutil.canonpath(repo.root, cwd, otarget)
564 abstarget = pathutil.canonpath(repo.root, cwd, otarget)
565 if '/' in abstarget:
565 if '/' in abstarget:
566 # We cannot normalize abstarget itself, this would prevent
566 # We cannot normalize abstarget itself, this would prevent
567 # case only renames, like a => A.
567 # case only renames, like a => A.
568 abspath, absname = abstarget.rsplit('/', 1)
568 abspath, absname = abstarget.rsplit('/', 1)
569 abstarget = repo.dirstate.normalize(abspath) + '/' + absname
569 abstarget = repo.dirstate.normalize(abspath) + '/' + absname
570 reltarget = repo.pathto(abstarget, cwd)
570 reltarget = repo.pathto(abstarget, cwd)
571 target = repo.wjoin(abstarget)
571 target = repo.wjoin(abstarget)
572 src = repo.wjoin(abssrc)
572 src = repo.wjoin(abssrc)
573 state = repo.dirstate[abstarget]
573 state = repo.dirstate[abstarget]
574
574
575 scmutil.checkportable(ui, abstarget)
575 scmutil.checkportable(ui, abstarget)
576
576
577 # check for collisions
577 # check for collisions
578 prevsrc = targets.get(abstarget)
578 prevsrc = targets.get(abstarget)
579 if prevsrc is not None:
579 if prevsrc is not None:
580 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
580 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
581 (reltarget, repo.pathto(abssrc, cwd),
581 (reltarget, repo.pathto(abssrc, cwd),
582 repo.pathto(prevsrc, cwd)))
582 repo.pathto(prevsrc, cwd)))
583 return
583 return
584
584
585 # check for overwrites
585 # check for overwrites
586 exists = os.path.lexists(target)
586 exists = os.path.lexists(target)
587 samefile = False
587 samefile = False
588 if exists and abssrc != abstarget:
588 if exists and abssrc != abstarget:
589 if (repo.dirstate.normalize(abssrc) ==
589 if (repo.dirstate.normalize(abssrc) ==
590 repo.dirstate.normalize(abstarget)):
590 repo.dirstate.normalize(abstarget)):
591 if not rename:
591 if not rename:
592 ui.warn(_("%s: can't copy - same file\n") % reltarget)
592 ui.warn(_("%s: can't copy - same file\n") % reltarget)
593 return
593 return
594 exists = False
594 exists = False
595 samefile = True
595 samefile = True
596
596
597 if not after and exists or after and state in 'mn':
597 if not after and exists or after and state in 'mn':
598 if not opts['force']:
598 if not opts['force']:
599 ui.warn(_('%s: not overwriting - file exists\n') %
599 ui.warn(_('%s: not overwriting - file exists\n') %
600 reltarget)
600 reltarget)
601 return
601 return
602
602
603 if after:
603 if after:
604 if not exists:
604 if not exists:
605 if rename:
605 if rename:
606 ui.warn(_('%s: not recording move - %s does not exist\n') %
606 ui.warn(_('%s: not recording move - %s does not exist\n') %
607 (relsrc, reltarget))
607 (relsrc, reltarget))
608 else:
608 else:
609 ui.warn(_('%s: not recording copy - %s does not exist\n') %
609 ui.warn(_('%s: not recording copy - %s does not exist\n') %
610 (relsrc, reltarget))
610 (relsrc, reltarget))
611 return
611 return
612 elif not dryrun:
612 elif not dryrun:
613 try:
613 try:
614 if exists:
614 if exists:
615 os.unlink(target)
615 os.unlink(target)
616 targetdir = os.path.dirname(target) or '.'
616 targetdir = os.path.dirname(target) or '.'
617 if not os.path.isdir(targetdir):
617 if not os.path.isdir(targetdir):
618 os.makedirs(targetdir)
618 os.makedirs(targetdir)
619 if samefile:
619 if samefile:
620 tmp = target + "~hgrename"
620 tmp = target + "~hgrename"
621 os.rename(src, tmp)
621 os.rename(src, tmp)
622 os.rename(tmp, target)
622 os.rename(tmp, target)
623 else:
623 else:
624 util.copyfile(src, target)
624 util.copyfile(src, target)
625 srcexists = True
625 srcexists = True
626 except IOError as inst:
626 except IOError as inst:
627 if inst.errno == errno.ENOENT:
627 if inst.errno == errno.ENOENT:
628 ui.warn(_('%s: deleted in working directory\n') % relsrc)
628 ui.warn(_('%s: deleted in working directory\n') % relsrc)
629 srcexists = False
629 srcexists = False
630 else:
630 else:
631 ui.warn(_('%s: cannot copy - %s\n') %
631 ui.warn(_('%s: cannot copy - %s\n') %
632 (relsrc, inst.strerror))
632 (relsrc, inst.strerror))
633 return True # report a failure
633 return True # report a failure
634
634
635 if ui.verbose or not exact:
635 if ui.verbose or not exact:
636 if rename:
636 if rename:
637 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
637 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
638 else:
638 else:
639 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
639 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
640
640
641 targets[abstarget] = abssrc
641 targets[abstarget] = abssrc
642
642
643 # fix up dirstate
643 # fix up dirstate
644 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
644 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
645 dryrun=dryrun, cwd=cwd)
645 dryrun=dryrun, cwd=cwd)
646 if rename and not dryrun:
646 if rename and not dryrun:
647 if not after and srcexists and not samefile:
647 if not after and srcexists and not samefile:
648 util.unlinkpath(repo.wjoin(abssrc))
648 util.unlinkpath(repo.wjoin(abssrc))
649 wctx.forget([abssrc])
649 wctx.forget([abssrc])
650
650
651 # pat: ossep
651 # pat: ossep
652 # dest ossep
652 # dest ossep
653 # srcs: list of (hgsep, hgsep, ossep, bool)
653 # srcs: list of (hgsep, hgsep, ossep, bool)
654 # return: function that takes hgsep and returns ossep
654 # return: function that takes hgsep and returns ossep
655 def targetpathfn(pat, dest, srcs):
655 def targetpathfn(pat, dest, srcs):
656 if os.path.isdir(pat):
656 if os.path.isdir(pat):
657 abspfx = pathutil.canonpath(repo.root, cwd, pat)
657 abspfx = pathutil.canonpath(repo.root, cwd, pat)
658 abspfx = util.localpath(abspfx)
658 abspfx = util.localpath(abspfx)
659 if destdirexists:
659 if destdirexists:
660 striplen = len(os.path.split(abspfx)[0])
660 striplen = len(os.path.split(abspfx)[0])
661 else:
661 else:
662 striplen = len(abspfx)
662 striplen = len(abspfx)
663 if striplen:
663 if striplen:
664 striplen += len(os.sep)
664 striplen += len(os.sep)
665 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
665 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
666 elif destdirexists:
666 elif destdirexists:
667 res = lambda p: os.path.join(dest,
667 res = lambda p: os.path.join(dest,
668 os.path.basename(util.localpath(p)))
668 os.path.basename(util.localpath(p)))
669 else:
669 else:
670 res = lambda p: dest
670 res = lambda p: dest
671 return res
671 return res
672
672
673 # pat: ossep
673 # pat: ossep
674 # dest ossep
674 # dest ossep
675 # srcs: list of (hgsep, hgsep, ossep, bool)
675 # srcs: list of (hgsep, hgsep, ossep, bool)
676 # return: function that takes hgsep and returns ossep
676 # return: function that takes hgsep and returns ossep
677 def targetpathafterfn(pat, dest, srcs):
677 def targetpathafterfn(pat, dest, srcs):
678 if matchmod.patkind(pat):
678 if matchmod.patkind(pat):
679 # a mercurial pattern
679 # a mercurial pattern
680 res = lambda p: os.path.join(dest,
680 res = lambda p: os.path.join(dest,
681 os.path.basename(util.localpath(p)))
681 os.path.basename(util.localpath(p)))
682 else:
682 else:
683 abspfx = pathutil.canonpath(repo.root, cwd, pat)
683 abspfx = pathutil.canonpath(repo.root, cwd, pat)
684 if len(abspfx) < len(srcs[0][0]):
684 if len(abspfx) < len(srcs[0][0]):
685 # A directory. Either the target path contains the last
685 # A directory. Either the target path contains the last
686 # component of the source path or it does not.
686 # component of the source path or it does not.
687 def evalpath(striplen):
687 def evalpath(striplen):
688 score = 0
688 score = 0
689 for s in srcs:
689 for s in srcs:
690 t = os.path.join(dest, util.localpath(s[0])[striplen:])
690 t = os.path.join(dest, util.localpath(s[0])[striplen:])
691 if os.path.lexists(t):
691 if os.path.lexists(t):
692 score += 1
692 score += 1
693 return score
693 return score
694
694
695 abspfx = util.localpath(abspfx)
695 abspfx = util.localpath(abspfx)
696 striplen = len(abspfx)
696 striplen = len(abspfx)
697 if striplen:
697 if striplen:
698 striplen += len(os.sep)
698 striplen += len(os.sep)
699 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
699 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
700 score = evalpath(striplen)
700 score = evalpath(striplen)
701 striplen1 = len(os.path.split(abspfx)[0])
701 striplen1 = len(os.path.split(abspfx)[0])
702 if striplen1:
702 if striplen1:
703 striplen1 += len(os.sep)
703 striplen1 += len(os.sep)
704 if evalpath(striplen1) > score:
704 if evalpath(striplen1) > score:
705 striplen = striplen1
705 striplen = striplen1
706 res = lambda p: os.path.join(dest,
706 res = lambda p: os.path.join(dest,
707 util.localpath(p)[striplen:])
707 util.localpath(p)[striplen:])
708 else:
708 else:
709 # a file
709 # a file
710 if destdirexists:
710 if destdirexists:
711 res = lambda p: os.path.join(dest,
711 res = lambda p: os.path.join(dest,
712 os.path.basename(util.localpath(p)))
712 os.path.basename(util.localpath(p)))
713 else:
713 else:
714 res = lambda p: dest
714 res = lambda p: dest
715 return res
715 return res
716
716
717 pats = scmutil.expandpats(pats)
717 pats = scmutil.expandpats(pats)
718 if not pats:
718 if not pats:
719 raise util.Abort(_('no source or destination specified'))
719 raise util.Abort(_('no source or destination specified'))
720 if len(pats) == 1:
720 if len(pats) == 1:
721 raise util.Abort(_('no destination specified'))
721 raise util.Abort(_('no destination specified'))
722 dest = pats.pop()
722 dest = pats.pop()
723 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
723 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
724 if not destdirexists:
724 if not destdirexists:
725 if len(pats) > 1 or matchmod.patkind(pats[0]):
725 if len(pats) > 1 or matchmod.patkind(pats[0]):
726 raise util.Abort(_('with multiple sources, destination must be an '
726 raise util.Abort(_('with multiple sources, destination must be an '
727 'existing directory'))
727 'existing directory'))
728 if util.endswithsep(dest):
728 if util.endswithsep(dest):
729 raise util.Abort(_('destination %s is not a directory') % dest)
729 raise util.Abort(_('destination %s is not a directory') % dest)
730
730
731 tfn = targetpathfn
731 tfn = targetpathfn
732 if after:
732 if after:
733 tfn = targetpathafterfn
733 tfn = targetpathafterfn
734 copylist = []
734 copylist = []
735 for pat in pats:
735 for pat in pats:
736 srcs = walkpat(pat)
736 srcs = walkpat(pat)
737 if not srcs:
737 if not srcs:
738 continue
738 continue
739 copylist.append((tfn(pat, dest, srcs), srcs))
739 copylist.append((tfn(pat, dest, srcs), srcs))
740 if not copylist:
740 if not copylist:
741 raise util.Abort(_('no files to copy'))
741 raise util.Abort(_('no files to copy'))
742
742
743 errors = 0
743 errors = 0
744 for targetpath, srcs in copylist:
744 for targetpath, srcs in copylist:
745 for abssrc, relsrc, exact in srcs:
745 for abssrc, relsrc, exact in srcs:
746 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
746 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
747 errors += 1
747 errors += 1
748
748
749 if errors:
749 if errors:
750 ui.warn(_('(consider using --after)\n'))
750 ui.warn(_('(consider using --after)\n'))
751
751
752 return errors != 0
752 return errors != 0
753
753
754 def service(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
754 def service(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
755 runargs=None, appendpid=False):
755 runargs=None, appendpid=False):
756 '''Run a command as a service.'''
756 '''Run a command as a service.'''
757
757
758 def writepid(pid):
758 def writepid(pid):
759 if opts['pid_file']:
759 if opts['pid_file']:
760 if appendpid:
760 if appendpid:
761 mode = 'a'
761 mode = 'a'
762 else:
762 else:
763 mode = 'w'
763 mode = 'w'
764 fp = open(opts['pid_file'], mode)
764 fp = open(opts['pid_file'], mode)
765 fp.write(str(pid) + '\n')
765 fp.write(str(pid) + '\n')
766 fp.close()
766 fp.close()
767
767
768 if opts['daemon'] and not opts['daemon_pipefds']:
768 if opts['daemon'] and not opts['daemon_pipefds']:
769 # Signal child process startup with file removal
769 # Signal child process startup with file removal
770 lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-')
770 lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-')
771 os.close(lockfd)
771 os.close(lockfd)
772 try:
772 try:
773 if not runargs:
773 if not runargs:
774 runargs = util.hgcmd() + sys.argv[1:]
774 runargs = util.hgcmd() + sys.argv[1:]
775 runargs.append('--daemon-pipefds=%s' % lockpath)
775 runargs.append('--daemon-pipefds=%s' % lockpath)
776 # Don't pass --cwd to the child process, because we've already
776 # Don't pass --cwd to the child process, because we've already
777 # changed directory.
777 # changed directory.
778 for i in xrange(1, len(runargs)):
778 for i in xrange(1, len(runargs)):
779 if runargs[i].startswith('--cwd='):
779 if runargs[i].startswith('--cwd='):
780 del runargs[i]
780 del runargs[i]
781 break
781 break
782 elif runargs[i].startswith('--cwd'):
782 elif runargs[i].startswith('--cwd'):
783 del runargs[i:i + 2]
783 del runargs[i:i + 2]
784 break
784 break
785 def condfn():
785 def condfn():
786 return not os.path.exists(lockpath)
786 return not os.path.exists(lockpath)
787 pid = util.rundetached(runargs, condfn)
787 pid = util.rundetached(runargs, condfn)
788 if pid < 0:
788 if pid < 0:
789 raise util.Abort(_('child process failed to start'))
789 raise util.Abort(_('child process failed to start'))
790 writepid(pid)
790 writepid(pid)
791 finally:
791 finally:
792 try:
792 try:
793 os.unlink(lockpath)
793 os.unlink(lockpath)
794 except OSError as e:
794 except OSError as e:
795 if e.errno != errno.ENOENT:
795 if e.errno != errno.ENOENT:
796 raise
796 raise
797 if parentfn:
797 if parentfn:
798 return parentfn(pid)
798 return parentfn(pid)
799 else:
799 else:
800 return
800 return
801
801
802 if initfn:
802 if initfn:
803 initfn()
803 initfn()
804
804
805 if not opts['daemon']:
805 if not opts['daemon']:
806 writepid(os.getpid())
806 writepid(os.getpid())
807
807
808 if opts['daemon_pipefds']:
808 if opts['daemon_pipefds']:
809 lockpath = opts['daemon_pipefds']
809 lockpath = opts['daemon_pipefds']
810 try:
810 try:
811 os.setsid()
811 os.setsid()
812 except AttributeError:
812 except AttributeError:
813 pass
813 pass
814 os.unlink(lockpath)
814 os.unlink(lockpath)
815 util.hidewindow()
815 util.hidewindow()
816 sys.stdout.flush()
816 sys.stdout.flush()
817 sys.stderr.flush()
817 sys.stderr.flush()
818
818
819 nullfd = os.open(os.devnull, os.O_RDWR)
819 nullfd = os.open(os.devnull, os.O_RDWR)
820 logfilefd = nullfd
820 logfilefd = nullfd
821 if logfile:
821 if logfile:
822 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
822 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
823 os.dup2(nullfd, 0)
823 os.dup2(nullfd, 0)
824 os.dup2(logfilefd, 1)
824 os.dup2(logfilefd, 1)
825 os.dup2(logfilefd, 2)
825 os.dup2(logfilefd, 2)
826 if nullfd not in (0, 1, 2):
826 if nullfd not in (0, 1, 2):
827 os.close(nullfd)
827 os.close(nullfd)
828 if logfile and logfilefd not in (0, 1, 2):
828 if logfile and logfilefd not in (0, 1, 2):
829 os.close(logfilefd)
829 os.close(logfilefd)
830
830
831 if runfn:
831 if runfn:
832 return runfn()
832 return runfn()
833
833
834 ## facility to let extension process additional data into an import patch
835 # list of identifier to be executed in order
836 extrapreimport = [] # run before commit
837 # mapping from identifier to actual import function
838 #
839 # 'preimport' are run before the commit is made and are provided the following
840 # arguments:
841 # - repo: the localrepository instance,
842 # - patchdata: data extracted from patch header (cf m.patch.patchheadermap),
843 # - extra: the future extra dictionnary of the changeset, please mutate it,
844 # - opts: the import options.
845 # XXX ideally, we would just pass an ctx ready to be computed, that would allow
846 # mutation of in memory commit and more. Feel free to rework the code to get
847 # there.
848 extrapreimportmap = {}
849
834 def tryimportone(ui, repo, hunk, parents, opts, msgs, updatefunc):
850 def tryimportone(ui, repo, hunk, parents, opts, msgs, updatefunc):
835 """Utility function used by commands.import to import a single patch
851 """Utility function used by commands.import to import a single patch
836
852
837 This function is explicitly defined here to help the evolve extension to
853 This function is explicitly defined here to help the evolve extension to
838 wrap this part of the import logic.
854 wrap this part of the import logic.
839
855
840 The API is currently a bit ugly because it a simple code translation from
856 The API is currently a bit ugly because it a simple code translation from
841 the import command. Feel free to make it better.
857 the import command. Feel free to make it better.
842
858
843 :hunk: a patch (as a binary string)
859 :hunk: a patch (as a binary string)
844 :parents: nodes that will be parent of the created commit
860 :parents: nodes that will be parent of the created commit
845 :opts: the full dict of option passed to the import command
861 :opts: the full dict of option passed to the import command
846 :msgs: list to save commit message to.
862 :msgs: list to save commit message to.
847 (used in case we need to save it when failing)
863 (used in case we need to save it when failing)
848 :updatefunc: a function that update a repo to a given node
864 :updatefunc: a function that update a repo to a given node
849 updatefunc(<repo>, <node>)
865 updatefunc(<repo>, <node>)
850 """
866 """
851 # avoid cycle context -> subrepo -> cmdutil
867 # avoid cycle context -> subrepo -> cmdutil
852 import context
868 import context
853 extractdata = patch.extract(ui, hunk)
869 extractdata = patch.extract(ui, hunk)
854 tmpname = extractdata.get('filename')
870 tmpname = extractdata.get('filename')
855 message = extractdata.get('message')
871 message = extractdata.get('message')
856 user = extractdata.get('user')
872 user = extractdata.get('user')
857 date = extractdata.get('date')
873 date = extractdata.get('date')
858 branch = extractdata.get('branch')
874 branch = extractdata.get('branch')
859 nodeid = extractdata.get('nodeid')
875 nodeid = extractdata.get('nodeid')
860 p1 = extractdata.get('p1')
876 p1 = extractdata.get('p1')
861 p2 = extractdata.get('p2')
877 p2 = extractdata.get('p2')
862
878
863 update = not opts.get('bypass')
879 update = not opts.get('bypass')
864 strip = opts["strip"]
880 strip = opts["strip"]
865 prefix = opts["prefix"]
881 prefix = opts["prefix"]
866 sim = float(opts.get('similarity') or 0)
882 sim = float(opts.get('similarity') or 0)
867 if not tmpname:
883 if not tmpname:
868 return (None, None, False)
884 return (None, None, False)
869 msg = _('applied to working directory')
885 msg = _('applied to working directory')
870
886
871 rejects = False
887 rejects = False
872 dsguard = None
888 dsguard = None
873
889
874 try:
890 try:
875 cmdline_message = logmessage(ui, opts)
891 cmdline_message = logmessage(ui, opts)
876 if cmdline_message:
892 if cmdline_message:
877 # pickup the cmdline msg
893 # pickup the cmdline msg
878 message = cmdline_message
894 message = cmdline_message
879 elif message:
895 elif message:
880 # pickup the patch msg
896 # pickup the patch msg
881 message = message.strip()
897 message = message.strip()
882 else:
898 else:
883 # launch the editor
899 # launch the editor
884 message = None
900 message = None
885 ui.debug('message:\n%s\n' % message)
901 ui.debug('message:\n%s\n' % message)
886
902
887 if len(parents) == 1:
903 if len(parents) == 1:
888 parents.append(repo[nullid])
904 parents.append(repo[nullid])
889 if opts.get('exact'):
905 if opts.get('exact'):
890 if not nodeid or not p1:
906 if not nodeid or not p1:
891 raise util.Abort(_('not a Mercurial patch'))
907 raise util.Abort(_('not a Mercurial patch'))
892 p1 = repo[p1]
908 p1 = repo[p1]
893 p2 = repo[p2 or nullid]
909 p2 = repo[p2 or nullid]
894 elif p2:
910 elif p2:
895 try:
911 try:
896 p1 = repo[p1]
912 p1 = repo[p1]
897 p2 = repo[p2]
913 p2 = repo[p2]
898 # Without any options, consider p2 only if the
914 # Without any options, consider p2 only if the
899 # patch is being applied on top of the recorded
915 # patch is being applied on top of the recorded
900 # first parent.
916 # first parent.
901 if p1 != parents[0]:
917 if p1 != parents[0]:
902 p1 = parents[0]
918 p1 = parents[0]
903 p2 = repo[nullid]
919 p2 = repo[nullid]
904 except error.RepoError:
920 except error.RepoError:
905 p1, p2 = parents
921 p1, p2 = parents
906 if p2.node() == nullid:
922 if p2.node() == nullid:
907 ui.warn(_("warning: import the patch as a normal revision\n"
923 ui.warn(_("warning: import the patch as a normal revision\n"
908 "(use --exact to import the patch as a merge)\n"))
924 "(use --exact to import the patch as a merge)\n"))
909 else:
925 else:
910 p1, p2 = parents
926 p1, p2 = parents
911
927
912 n = None
928 n = None
913 if update:
929 if update:
914 dsguard = dirstateguard(repo, 'tryimportone')
930 dsguard = dirstateguard(repo, 'tryimportone')
915 if p1 != parents[0]:
931 if p1 != parents[0]:
916 updatefunc(repo, p1.node())
932 updatefunc(repo, p1.node())
917 if p2 != parents[1]:
933 if p2 != parents[1]:
918 repo.setparents(p1.node(), p2.node())
934 repo.setparents(p1.node(), p2.node())
919
935
920 if opts.get('exact') or opts.get('import_branch'):
936 if opts.get('exact') or opts.get('import_branch'):
921 repo.dirstate.setbranch(branch or 'default')
937 repo.dirstate.setbranch(branch or 'default')
922
938
923 partial = opts.get('partial', False)
939 partial = opts.get('partial', False)
924 files = set()
940 files = set()
925 try:
941 try:
926 patch.patch(ui, repo, tmpname, strip=strip, prefix=prefix,
942 patch.patch(ui, repo, tmpname, strip=strip, prefix=prefix,
927 files=files, eolmode=None, similarity=sim / 100.0)
943 files=files, eolmode=None, similarity=sim / 100.0)
928 except patch.PatchError as e:
944 except patch.PatchError as e:
929 if not partial:
945 if not partial:
930 raise util.Abort(str(e))
946 raise util.Abort(str(e))
931 if partial:
947 if partial:
932 rejects = True
948 rejects = True
933
949
934 files = list(files)
950 files = list(files)
935 if opts.get('no_commit'):
951 if opts.get('no_commit'):
936 if message:
952 if message:
937 msgs.append(message)
953 msgs.append(message)
938 else:
954 else:
939 if opts.get('exact') or p2:
955 if opts.get('exact') or p2:
940 # If you got here, you either use --force and know what
956 # If you got here, you either use --force and know what
941 # you are doing or used --exact or a merge patch while
957 # you are doing or used --exact or a merge patch while
942 # being updated to its first parent.
958 # being updated to its first parent.
943 m = None
959 m = None
944 else:
960 else:
945 m = scmutil.matchfiles(repo, files or [])
961 m = scmutil.matchfiles(repo, files or [])
946 editform = mergeeditform(repo[None], 'import.normal')
962 editform = mergeeditform(repo[None], 'import.normal')
947 if opts.get('exact'):
963 if opts.get('exact'):
948 editor = None
964 editor = None
949 else:
965 else:
950 editor = getcommiteditor(editform=editform, **opts)
966 editor = getcommiteditor(editform=editform, **opts)
951 allowemptyback = repo.ui.backupconfig('ui', 'allowemptycommit')
967 allowemptyback = repo.ui.backupconfig('ui', 'allowemptycommit')
968 extra = {}
969 for idfunc in extrapreimport:
970 extrapreimportmap[idfunc](repo, extractdata, extra, opts)
952 try:
971 try:
953 if partial:
972 if partial:
954 repo.ui.setconfig('ui', 'allowemptycommit', True)
973 repo.ui.setconfig('ui', 'allowemptycommit', True)
955 n = repo.commit(message, opts.get('user') or user,
974 n = repo.commit(message, opts.get('user') or user,
956 opts.get('date') or date, match=m,
975 opts.get('date') or date, match=m,
957 editor=editor)
976 editor=editor, extra=extra)
958 finally:
977 finally:
959 repo.ui.restoreconfig(allowemptyback)
978 repo.ui.restoreconfig(allowemptyback)
960 dsguard.close()
979 dsguard.close()
961 else:
980 else:
962 if opts.get('exact') or opts.get('import_branch'):
981 if opts.get('exact') or opts.get('import_branch'):
963 branch = branch or 'default'
982 branch = branch or 'default'
964 else:
983 else:
965 branch = p1.branch()
984 branch = p1.branch()
966 store = patch.filestore()
985 store = patch.filestore()
967 try:
986 try:
968 files = set()
987 files = set()
969 try:
988 try:
970 patch.patchrepo(ui, repo, p1, store, tmpname, strip, prefix,
989 patch.patchrepo(ui, repo, p1, store, tmpname, strip, prefix,
971 files, eolmode=None)
990 files, eolmode=None)
972 except patch.PatchError as e:
991 except patch.PatchError as e:
973 raise util.Abort(str(e))
992 raise util.Abort(str(e))
974 if opts.get('exact'):
993 if opts.get('exact'):
975 editor = None
994 editor = None
976 else:
995 else:
977 editor = getcommiteditor(editform='import.bypass')
996 editor = getcommiteditor(editform='import.bypass')
978 memctx = context.makememctx(repo, (p1.node(), p2.node()),
997 memctx = context.makememctx(repo, (p1.node(), p2.node()),
979 message,
998 message,
980 opts.get('user') or user,
999 opts.get('user') or user,
981 opts.get('date') or date,
1000 opts.get('date') or date,
982 branch, files, store,
1001 branch, files, store,
983 editor=editor)
1002 editor=editor)
984 n = memctx.commit()
1003 n = memctx.commit()
985 finally:
1004 finally:
986 store.close()
1005 store.close()
987 if opts.get('exact') and opts.get('no_commit'):
1006 if opts.get('exact') and opts.get('no_commit'):
988 # --exact with --no-commit is still useful in that it does merge
1007 # --exact with --no-commit is still useful in that it does merge
989 # and branch bits
1008 # and branch bits
990 ui.warn(_("warning: can't check exact import with --no-commit\n"))
1009 ui.warn(_("warning: can't check exact import with --no-commit\n"))
991 elif opts.get('exact') and hex(n) != nodeid:
1010 elif opts.get('exact') and hex(n) != nodeid:
992 raise util.Abort(_('patch is damaged or loses information'))
1011 raise util.Abort(_('patch is damaged or loses information'))
993 if n:
1012 if n:
994 # i18n: refers to a short changeset id
1013 # i18n: refers to a short changeset id
995 msg = _('created %s') % short(n)
1014 msg = _('created %s') % short(n)
996 return (msg, n, rejects)
1015 return (msg, n, rejects)
997 finally:
1016 finally:
998 lockmod.release(dsguard)
1017 lockmod.release(dsguard)
999 os.unlink(tmpname)
1018 os.unlink(tmpname)
1000
1019
1001 # facility to let extensions include additional data in an exported patch
1020 # facility to let extensions include additional data in an exported patch
1002 # list of identifiers to be executed in order
1021 # list of identifiers to be executed in order
1003 extraexport = []
1022 extraexport = []
1004 # mapping from identifier to actual export function
1023 # mapping from identifier to actual export function
1005 # function as to return a string to be added to the header or None
1024 # function as to return a string to be added to the header or None
1006 # it is given two arguments (sequencenumber, changectx)
1025 # it is given two arguments (sequencenumber, changectx)
1007 extraexportmap = {}
1026 extraexportmap = {}
1008
1027
1009 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
1028 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
1010 opts=None, match=None):
1029 opts=None, match=None):
1011 '''export changesets as hg patches.'''
1030 '''export changesets as hg patches.'''
1012
1031
1013 total = len(revs)
1032 total = len(revs)
1014 revwidth = max([len(str(rev)) for rev in revs])
1033 revwidth = max([len(str(rev)) for rev in revs])
1015 filemode = {}
1034 filemode = {}
1016
1035
1017 def single(rev, seqno, fp):
1036 def single(rev, seqno, fp):
1018 ctx = repo[rev]
1037 ctx = repo[rev]
1019 node = ctx.node()
1038 node = ctx.node()
1020 parents = [p.node() for p in ctx.parents() if p]
1039 parents = [p.node() for p in ctx.parents() if p]
1021 branch = ctx.branch()
1040 branch = ctx.branch()
1022 if switch_parent:
1041 if switch_parent:
1023 parents.reverse()
1042 parents.reverse()
1024
1043
1025 if parents:
1044 if parents:
1026 prev = parents[0]
1045 prev = parents[0]
1027 else:
1046 else:
1028 prev = nullid
1047 prev = nullid
1029
1048
1030 shouldclose = False
1049 shouldclose = False
1031 if not fp and len(template) > 0:
1050 if not fp and len(template) > 0:
1032 desc_lines = ctx.description().rstrip().split('\n')
1051 desc_lines = ctx.description().rstrip().split('\n')
1033 desc = desc_lines[0] #Commit always has a first line.
1052 desc = desc_lines[0] #Commit always has a first line.
1034 fp = makefileobj(repo, template, node, desc=desc, total=total,
1053 fp = makefileobj(repo, template, node, desc=desc, total=total,
1035 seqno=seqno, revwidth=revwidth, mode='wb',
1054 seqno=seqno, revwidth=revwidth, mode='wb',
1036 modemap=filemode)
1055 modemap=filemode)
1037 if fp != template:
1056 if fp != template:
1038 shouldclose = True
1057 shouldclose = True
1039 if fp and fp != sys.stdout and util.safehasattr(fp, 'name'):
1058 if fp and fp != sys.stdout and util.safehasattr(fp, 'name'):
1040 repo.ui.note("%s\n" % fp.name)
1059 repo.ui.note("%s\n" % fp.name)
1041
1060
1042 if not fp:
1061 if not fp:
1043 write = repo.ui.write
1062 write = repo.ui.write
1044 else:
1063 else:
1045 def write(s, **kw):
1064 def write(s, **kw):
1046 fp.write(s)
1065 fp.write(s)
1047
1066
1048 write("# HG changeset patch\n")
1067 write("# HG changeset patch\n")
1049 write("# User %s\n" % ctx.user())
1068 write("# User %s\n" % ctx.user())
1050 write("# Date %d %d\n" % ctx.date())
1069 write("# Date %d %d\n" % ctx.date())
1051 write("# %s\n" % util.datestr(ctx.date()))
1070 write("# %s\n" % util.datestr(ctx.date()))
1052 if branch and branch != 'default':
1071 if branch and branch != 'default':
1053 write("# Branch %s\n" % branch)
1072 write("# Branch %s\n" % branch)
1054 write("# Node ID %s\n" % hex(node))
1073 write("# Node ID %s\n" % hex(node))
1055 write("# Parent %s\n" % hex(prev))
1074 write("# Parent %s\n" % hex(prev))
1056 if len(parents) > 1:
1075 if len(parents) > 1:
1057 write("# Parent %s\n" % hex(parents[1]))
1076 write("# Parent %s\n" % hex(parents[1]))
1058
1077
1059 for headerid in extraexport:
1078 for headerid in extraexport:
1060 header = extraexportmap[headerid](seqno, ctx)
1079 header = extraexportmap[headerid](seqno, ctx)
1061 if header is not None:
1080 if header is not None:
1062 write('# %s\n' % header)
1081 write('# %s\n' % header)
1063 write(ctx.description().rstrip())
1082 write(ctx.description().rstrip())
1064 write("\n\n")
1083 write("\n\n")
1065
1084
1066 for chunk, label in patch.diffui(repo, prev, node, match, opts=opts):
1085 for chunk, label in patch.diffui(repo, prev, node, match, opts=opts):
1067 write(chunk, label=label)
1086 write(chunk, label=label)
1068
1087
1069 if shouldclose:
1088 if shouldclose:
1070 fp.close()
1089 fp.close()
1071
1090
1072 for seqno, rev in enumerate(revs):
1091 for seqno, rev in enumerate(revs):
1073 single(rev, seqno + 1, fp)
1092 single(rev, seqno + 1, fp)
1074
1093
1075 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
1094 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
1076 changes=None, stat=False, fp=None, prefix='',
1095 changes=None, stat=False, fp=None, prefix='',
1077 root='', listsubrepos=False):
1096 root='', listsubrepos=False):
1078 '''show diff or diffstat.'''
1097 '''show diff or diffstat.'''
1079 if fp is None:
1098 if fp is None:
1080 write = ui.write
1099 write = ui.write
1081 else:
1100 else:
1082 def write(s, **kw):
1101 def write(s, **kw):
1083 fp.write(s)
1102 fp.write(s)
1084
1103
1085 if root:
1104 if root:
1086 relroot = pathutil.canonpath(repo.root, repo.getcwd(), root)
1105 relroot = pathutil.canonpath(repo.root, repo.getcwd(), root)
1087 else:
1106 else:
1088 relroot = ''
1107 relroot = ''
1089 if relroot != '':
1108 if relroot != '':
1090 # XXX relative roots currently don't work if the root is within a
1109 # XXX relative roots currently don't work if the root is within a
1091 # subrepo
1110 # subrepo
1092 uirelroot = match.uipath(relroot)
1111 uirelroot = match.uipath(relroot)
1093 relroot += '/'
1112 relroot += '/'
1094 for matchroot in match.files():
1113 for matchroot in match.files():
1095 if not matchroot.startswith(relroot):
1114 if not matchroot.startswith(relroot):
1096 ui.warn(_('warning: %s not inside relative root %s\n') % (
1115 ui.warn(_('warning: %s not inside relative root %s\n') % (
1097 match.uipath(matchroot), uirelroot))
1116 match.uipath(matchroot), uirelroot))
1098
1117
1099 if stat:
1118 if stat:
1100 diffopts = diffopts.copy(context=0)
1119 diffopts = diffopts.copy(context=0)
1101 width = 80
1120 width = 80
1102 if not ui.plain():
1121 if not ui.plain():
1103 width = ui.termwidth()
1122 width = ui.termwidth()
1104 chunks = patch.diff(repo, node1, node2, match, changes, diffopts,
1123 chunks = patch.diff(repo, node1, node2, match, changes, diffopts,
1105 prefix=prefix, relroot=relroot)
1124 prefix=prefix, relroot=relroot)
1106 for chunk, label in patch.diffstatui(util.iterlines(chunks),
1125 for chunk, label in patch.diffstatui(util.iterlines(chunks),
1107 width=width,
1126 width=width,
1108 git=diffopts.git):
1127 git=diffopts.git):
1109 write(chunk, label=label)
1128 write(chunk, label=label)
1110 else:
1129 else:
1111 for chunk, label in patch.diffui(repo, node1, node2, match,
1130 for chunk, label in patch.diffui(repo, node1, node2, match,
1112 changes, diffopts, prefix=prefix,
1131 changes, diffopts, prefix=prefix,
1113 relroot=relroot):
1132 relroot=relroot):
1114 write(chunk, label=label)
1133 write(chunk, label=label)
1115
1134
1116 if listsubrepos:
1135 if listsubrepos:
1117 ctx1 = repo[node1]
1136 ctx1 = repo[node1]
1118 ctx2 = repo[node2]
1137 ctx2 = repo[node2]
1119 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
1138 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
1120 tempnode2 = node2
1139 tempnode2 = node2
1121 try:
1140 try:
1122 if node2 is not None:
1141 if node2 is not None:
1123 tempnode2 = ctx2.substate[subpath][1]
1142 tempnode2 = ctx2.substate[subpath][1]
1124 except KeyError:
1143 except KeyError:
1125 # A subrepo that existed in node1 was deleted between node1 and
1144 # A subrepo that existed in node1 was deleted between node1 and
1126 # node2 (inclusive). Thus, ctx2's substate won't contain that
1145 # node2 (inclusive). Thus, ctx2's substate won't contain that
1127 # subpath. The best we can do is to ignore it.
1146 # subpath. The best we can do is to ignore it.
1128 tempnode2 = None
1147 tempnode2 = None
1129 submatch = matchmod.narrowmatcher(subpath, match)
1148 submatch = matchmod.narrowmatcher(subpath, match)
1130 sub.diff(ui, diffopts, tempnode2, submatch, changes=changes,
1149 sub.diff(ui, diffopts, tempnode2, submatch, changes=changes,
1131 stat=stat, fp=fp, prefix=prefix)
1150 stat=stat, fp=fp, prefix=prefix)
1132
1151
1133 class changeset_printer(object):
1152 class changeset_printer(object):
1134 '''show changeset information when templating not requested.'''
1153 '''show changeset information when templating not requested.'''
1135
1154
1136 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1155 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1137 self.ui = ui
1156 self.ui = ui
1138 self.repo = repo
1157 self.repo = repo
1139 self.buffered = buffered
1158 self.buffered = buffered
1140 self.matchfn = matchfn
1159 self.matchfn = matchfn
1141 self.diffopts = diffopts
1160 self.diffopts = diffopts
1142 self.header = {}
1161 self.header = {}
1143 self.hunk = {}
1162 self.hunk = {}
1144 self.lastheader = None
1163 self.lastheader = None
1145 self.footer = None
1164 self.footer = None
1146
1165
1147 def flush(self, ctx):
1166 def flush(self, ctx):
1148 rev = ctx.rev()
1167 rev = ctx.rev()
1149 if rev in self.header:
1168 if rev in self.header:
1150 h = self.header[rev]
1169 h = self.header[rev]
1151 if h != self.lastheader:
1170 if h != self.lastheader:
1152 self.lastheader = h
1171 self.lastheader = h
1153 self.ui.write(h)
1172 self.ui.write(h)
1154 del self.header[rev]
1173 del self.header[rev]
1155 if rev in self.hunk:
1174 if rev in self.hunk:
1156 self.ui.write(self.hunk[rev])
1175 self.ui.write(self.hunk[rev])
1157 del self.hunk[rev]
1176 del self.hunk[rev]
1158 return 1
1177 return 1
1159 return 0
1178 return 0
1160
1179
1161 def close(self):
1180 def close(self):
1162 if self.footer:
1181 if self.footer:
1163 self.ui.write(self.footer)
1182 self.ui.write(self.footer)
1164
1183
1165 def show(self, ctx, copies=None, matchfn=None, **props):
1184 def show(self, ctx, copies=None, matchfn=None, **props):
1166 if self.buffered:
1185 if self.buffered:
1167 self.ui.pushbuffer()
1186 self.ui.pushbuffer()
1168 self._show(ctx, copies, matchfn, props)
1187 self._show(ctx, copies, matchfn, props)
1169 self.hunk[ctx.rev()] = self.ui.popbuffer(labeled=True)
1188 self.hunk[ctx.rev()] = self.ui.popbuffer(labeled=True)
1170 else:
1189 else:
1171 self._show(ctx, copies, matchfn, props)
1190 self._show(ctx, copies, matchfn, props)
1172
1191
1173 def _show(self, ctx, copies, matchfn, props):
1192 def _show(self, ctx, copies, matchfn, props):
1174 '''show a single changeset or file revision'''
1193 '''show a single changeset or file revision'''
1175 changenode = ctx.node()
1194 changenode = ctx.node()
1176 rev = ctx.rev()
1195 rev = ctx.rev()
1177 if self.ui.debugflag:
1196 if self.ui.debugflag:
1178 hexfunc = hex
1197 hexfunc = hex
1179 else:
1198 else:
1180 hexfunc = short
1199 hexfunc = short
1181 # as of now, wctx.node() and wctx.rev() return None, but we want to
1200 # as of now, wctx.node() and wctx.rev() return None, but we want to
1182 # show the same values as {node} and {rev} templatekw
1201 # show the same values as {node} and {rev} templatekw
1183 revnode = (scmutil.intrev(rev), hexfunc(bin(ctx.hex())))
1202 revnode = (scmutil.intrev(rev), hexfunc(bin(ctx.hex())))
1184
1203
1185 if self.ui.quiet:
1204 if self.ui.quiet:
1186 self.ui.write("%d:%s\n" % revnode, label='log.node')
1205 self.ui.write("%d:%s\n" % revnode, label='log.node')
1187 return
1206 return
1188
1207
1189 date = util.datestr(ctx.date())
1208 date = util.datestr(ctx.date())
1190
1209
1191 # i18n: column positioning for "hg log"
1210 # i18n: column positioning for "hg log"
1192 self.ui.write(_("changeset: %d:%s\n") % revnode,
1211 self.ui.write(_("changeset: %d:%s\n") % revnode,
1193 label='log.changeset changeset.%s' % ctx.phasestr())
1212 label='log.changeset changeset.%s' % ctx.phasestr())
1194
1213
1195 # branches are shown first before any other names due to backwards
1214 # branches are shown first before any other names due to backwards
1196 # compatibility
1215 # compatibility
1197 branch = ctx.branch()
1216 branch = ctx.branch()
1198 # don't show the default branch name
1217 # don't show the default branch name
1199 if branch != 'default':
1218 if branch != 'default':
1200 # i18n: column positioning for "hg log"
1219 # i18n: column positioning for "hg log"
1201 self.ui.write(_("branch: %s\n") % branch,
1220 self.ui.write(_("branch: %s\n") % branch,
1202 label='log.branch')
1221 label='log.branch')
1203
1222
1204 for name, ns in self.repo.names.iteritems():
1223 for name, ns in self.repo.names.iteritems():
1205 # branches has special logic already handled above, so here we just
1224 # branches has special logic already handled above, so here we just
1206 # skip it
1225 # skip it
1207 if name == 'branches':
1226 if name == 'branches':
1208 continue
1227 continue
1209 # we will use the templatename as the color name since those two
1228 # we will use the templatename as the color name since those two
1210 # should be the same
1229 # should be the same
1211 for name in ns.names(self.repo, changenode):
1230 for name in ns.names(self.repo, changenode):
1212 self.ui.write(ns.logfmt % name,
1231 self.ui.write(ns.logfmt % name,
1213 label='log.%s' % ns.colorname)
1232 label='log.%s' % ns.colorname)
1214 if self.ui.debugflag:
1233 if self.ui.debugflag:
1215 # i18n: column positioning for "hg log"
1234 # i18n: column positioning for "hg log"
1216 self.ui.write(_("phase: %s\n") % ctx.phasestr(),
1235 self.ui.write(_("phase: %s\n") % ctx.phasestr(),
1217 label='log.phase')
1236 label='log.phase')
1218 for pctx in scmutil.meaningfulparents(self.repo, ctx):
1237 for pctx in scmutil.meaningfulparents(self.repo, ctx):
1219 label = 'log.parent changeset.%s' % pctx.phasestr()
1238 label = 'log.parent changeset.%s' % pctx.phasestr()
1220 # i18n: column positioning for "hg log"
1239 # i18n: column positioning for "hg log"
1221 self.ui.write(_("parent: %d:%s\n")
1240 self.ui.write(_("parent: %d:%s\n")
1222 % (pctx.rev(), hexfunc(pctx.node())),
1241 % (pctx.rev(), hexfunc(pctx.node())),
1223 label=label)
1242 label=label)
1224
1243
1225 if self.ui.debugflag and rev is not None:
1244 if self.ui.debugflag and rev is not None:
1226 mnode = ctx.manifestnode()
1245 mnode = ctx.manifestnode()
1227 # i18n: column positioning for "hg log"
1246 # i18n: column positioning for "hg log"
1228 self.ui.write(_("manifest: %d:%s\n") %
1247 self.ui.write(_("manifest: %d:%s\n") %
1229 (self.repo.manifest.rev(mnode), hex(mnode)),
1248 (self.repo.manifest.rev(mnode), hex(mnode)),
1230 label='ui.debug log.manifest')
1249 label='ui.debug log.manifest')
1231 # i18n: column positioning for "hg log"
1250 # i18n: column positioning for "hg log"
1232 self.ui.write(_("user: %s\n") % ctx.user(),
1251 self.ui.write(_("user: %s\n") % ctx.user(),
1233 label='log.user')
1252 label='log.user')
1234 # i18n: column positioning for "hg log"
1253 # i18n: column positioning for "hg log"
1235 self.ui.write(_("date: %s\n") % date,
1254 self.ui.write(_("date: %s\n") % date,
1236 label='log.date')
1255 label='log.date')
1237
1256
1238 if self.ui.debugflag:
1257 if self.ui.debugflag:
1239 files = ctx.p1().status(ctx)[:3]
1258 files = ctx.p1().status(ctx)[:3]
1240 for key, value in zip([# i18n: column positioning for "hg log"
1259 for key, value in zip([# i18n: column positioning for "hg log"
1241 _("files:"),
1260 _("files:"),
1242 # i18n: column positioning for "hg log"
1261 # i18n: column positioning for "hg log"
1243 _("files+:"),
1262 _("files+:"),
1244 # i18n: column positioning for "hg log"
1263 # i18n: column positioning for "hg log"
1245 _("files-:")], files):
1264 _("files-:")], files):
1246 if value:
1265 if value:
1247 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
1266 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
1248 label='ui.debug log.files')
1267 label='ui.debug log.files')
1249 elif ctx.files() and self.ui.verbose:
1268 elif ctx.files() and self.ui.verbose:
1250 # i18n: column positioning for "hg log"
1269 # i18n: column positioning for "hg log"
1251 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
1270 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
1252 label='ui.note log.files')
1271 label='ui.note log.files')
1253 if copies and self.ui.verbose:
1272 if copies and self.ui.verbose:
1254 copies = ['%s (%s)' % c for c in copies]
1273 copies = ['%s (%s)' % c for c in copies]
1255 # i18n: column positioning for "hg log"
1274 # i18n: column positioning for "hg log"
1256 self.ui.write(_("copies: %s\n") % ' '.join(copies),
1275 self.ui.write(_("copies: %s\n") % ' '.join(copies),
1257 label='ui.note log.copies')
1276 label='ui.note log.copies')
1258
1277
1259 extra = ctx.extra()
1278 extra = ctx.extra()
1260 if extra and self.ui.debugflag:
1279 if extra and self.ui.debugflag:
1261 for key, value in sorted(extra.items()):
1280 for key, value in sorted(extra.items()):
1262 # i18n: column positioning for "hg log"
1281 # i18n: column positioning for "hg log"
1263 self.ui.write(_("extra: %s=%s\n")
1282 self.ui.write(_("extra: %s=%s\n")
1264 % (key, value.encode('string_escape')),
1283 % (key, value.encode('string_escape')),
1265 label='ui.debug log.extra')
1284 label='ui.debug log.extra')
1266
1285
1267 description = ctx.description().strip()
1286 description = ctx.description().strip()
1268 if description:
1287 if description:
1269 if self.ui.verbose:
1288 if self.ui.verbose:
1270 self.ui.write(_("description:\n"),
1289 self.ui.write(_("description:\n"),
1271 label='ui.note log.description')
1290 label='ui.note log.description')
1272 self.ui.write(description,
1291 self.ui.write(description,
1273 label='ui.note log.description')
1292 label='ui.note log.description')
1274 self.ui.write("\n\n")
1293 self.ui.write("\n\n")
1275 else:
1294 else:
1276 # i18n: column positioning for "hg log"
1295 # i18n: column positioning for "hg log"
1277 self.ui.write(_("summary: %s\n") %
1296 self.ui.write(_("summary: %s\n") %
1278 description.splitlines()[0],
1297 description.splitlines()[0],
1279 label='log.summary')
1298 label='log.summary')
1280 self.ui.write("\n")
1299 self.ui.write("\n")
1281
1300
1282 self.showpatch(changenode, matchfn)
1301 self.showpatch(changenode, matchfn)
1283
1302
1284 def showpatch(self, node, matchfn):
1303 def showpatch(self, node, matchfn):
1285 if not matchfn:
1304 if not matchfn:
1286 matchfn = self.matchfn
1305 matchfn = self.matchfn
1287 if matchfn:
1306 if matchfn:
1288 stat = self.diffopts.get('stat')
1307 stat = self.diffopts.get('stat')
1289 diff = self.diffopts.get('patch')
1308 diff = self.diffopts.get('patch')
1290 diffopts = patch.diffallopts(self.ui, self.diffopts)
1309 diffopts = patch.diffallopts(self.ui, self.diffopts)
1291 prev = self.repo.changelog.parents(node)[0]
1310 prev = self.repo.changelog.parents(node)[0]
1292 if stat:
1311 if stat:
1293 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1312 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1294 match=matchfn, stat=True)
1313 match=matchfn, stat=True)
1295 if diff:
1314 if diff:
1296 if stat:
1315 if stat:
1297 self.ui.write("\n")
1316 self.ui.write("\n")
1298 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1317 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1299 match=matchfn, stat=False)
1318 match=matchfn, stat=False)
1300 self.ui.write("\n")
1319 self.ui.write("\n")
1301
1320
1302 class jsonchangeset(changeset_printer):
1321 class jsonchangeset(changeset_printer):
1303 '''format changeset information.'''
1322 '''format changeset information.'''
1304
1323
1305 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1324 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1306 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1325 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1307 self.cache = {}
1326 self.cache = {}
1308 self._first = True
1327 self._first = True
1309
1328
1310 def close(self):
1329 def close(self):
1311 if not self._first:
1330 if not self._first:
1312 self.ui.write("\n]\n")
1331 self.ui.write("\n]\n")
1313 else:
1332 else:
1314 self.ui.write("[]\n")
1333 self.ui.write("[]\n")
1315
1334
1316 def _show(self, ctx, copies, matchfn, props):
1335 def _show(self, ctx, copies, matchfn, props):
1317 '''show a single changeset or file revision'''
1336 '''show a single changeset or file revision'''
1318 rev = ctx.rev()
1337 rev = ctx.rev()
1319 if rev is None:
1338 if rev is None:
1320 jrev = jnode = 'null'
1339 jrev = jnode = 'null'
1321 else:
1340 else:
1322 jrev = str(rev)
1341 jrev = str(rev)
1323 jnode = '"%s"' % hex(ctx.node())
1342 jnode = '"%s"' % hex(ctx.node())
1324 j = encoding.jsonescape
1343 j = encoding.jsonescape
1325
1344
1326 if self._first:
1345 if self._first:
1327 self.ui.write("[\n {")
1346 self.ui.write("[\n {")
1328 self._first = False
1347 self._first = False
1329 else:
1348 else:
1330 self.ui.write(",\n {")
1349 self.ui.write(",\n {")
1331
1350
1332 if self.ui.quiet:
1351 if self.ui.quiet:
1333 self.ui.write('\n "rev": %s' % jrev)
1352 self.ui.write('\n "rev": %s' % jrev)
1334 self.ui.write(',\n "node": %s' % jnode)
1353 self.ui.write(',\n "node": %s' % jnode)
1335 self.ui.write('\n }')
1354 self.ui.write('\n }')
1336 return
1355 return
1337
1356
1338 self.ui.write('\n "rev": %s' % jrev)
1357 self.ui.write('\n "rev": %s' % jrev)
1339 self.ui.write(',\n "node": %s' % jnode)
1358 self.ui.write(',\n "node": %s' % jnode)
1340 self.ui.write(',\n "branch": "%s"' % j(ctx.branch()))
1359 self.ui.write(',\n "branch": "%s"' % j(ctx.branch()))
1341 self.ui.write(',\n "phase": "%s"' % ctx.phasestr())
1360 self.ui.write(',\n "phase": "%s"' % ctx.phasestr())
1342 self.ui.write(',\n "user": "%s"' % j(ctx.user()))
1361 self.ui.write(',\n "user": "%s"' % j(ctx.user()))
1343 self.ui.write(',\n "date": [%d, %d]' % ctx.date())
1362 self.ui.write(',\n "date": [%d, %d]' % ctx.date())
1344 self.ui.write(',\n "desc": "%s"' % j(ctx.description()))
1363 self.ui.write(',\n "desc": "%s"' % j(ctx.description()))
1345
1364
1346 self.ui.write(',\n "bookmarks": [%s]' %
1365 self.ui.write(',\n "bookmarks": [%s]' %
1347 ", ".join('"%s"' % j(b) for b in ctx.bookmarks()))
1366 ", ".join('"%s"' % j(b) for b in ctx.bookmarks()))
1348 self.ui.write(',\n "tags": [%s]' %
1367 self.ui.write(',\n "tags": [%s]' %
1349 ", ".join('"%s"' % j(t) for t in ctx.tags()))
1368 ", ".join('"%s"' % j(t) for t in ctx.tags()))
1350 self.ui.write(',\n "parents": [%s]' %
1369 self.ui.write(',\n "parents": [%s]' %
1351 ", ".join('"%s"' % c.hex() for c in ctx.parents()))
1370 ", ".join('"%s"' % c.hex() for c in ctx.parents()))
1352
1371
1353 if self.ui.debugflag:
1372 if self.ui.debugflag:
1354 if rev is None:
1373 if rev is None:
1355 jmanifestnode = 'null'
1374 jmanifestnode = 'null'
1356 else:
1375 else:
1357 jmanifestnode = '"%s"' % hex(ctx.manifestnode())
1376 jmanifestnode = '"%s"' % hex(ctx.manifestnode())
1358 self.ui.write(',\n "manifest": %s' % jmanifestnode)
1377 self.ui.write(',\n "manifest": %s' % jmanifestnode)
1359
1378
1360 self.ui.write(',\n "extra": {%s}' %
1379 self.ui.write(',\n "extra": {%s}' %
1361 ", ".join('"%s": "%s"' % (j(k), j(v))
1380 ", ".join('"%s": "%s"' % (j(k), j(v))
1362 for k, v in ctx.extra().items()))
1381 for k, v in ctx.extra().items()))
1363
1382
1364 files = ctx.p1().status(ctx)
1383 files = ctx.p1().status(ctx)
1365 self.ui.write(',\n "modified": [%s]' %
1384 self.ui.write(',\n "modified": [%s]' %
1366 ", ".join('"%s"' % j(f) for f in files[0]))
1385 ", ".join('"%s"' % j(f) for f in files[0]))
1367 self.ui.write(',\n "added": [%s]' %
1386 self.ui.write(',\n "added": [%s]' %
1368 ", ".join('"%s"' % j(f) for f in files[1]))
1387 ", ".join('"%s"' % j(f) for f in files[1]))
1369 self.ui.write(',\n "removed": [%s]' %
1388 self.ui.write(',\n "removed": [%s]' %
1370 ", ".join('"%s"' % j(f) for f in files[2]))
1389 ", ".join('"%s"' % j(f) for f in files[2]))
1371
1390
1372 elif self.ui.verbose:
1391 elif self.ui.verbose:
1373 self.ui.write(',\n "files": [%s]' %
1392 self.ui.write(',\n "files": [%s]' %
1374 ", ".join('"%s"' % j(f) for f in ctx.files()))
1393 ", ".join('"%s"' % j(f) for f in ctx.files()))
1375
1394
1376 if copies:
1395 if copies:
1377 self.ui.write(',\n "copies": {%s}' %
1396 self.ui.write(',\n "copies": {%s}' %
1378 ", ".join('"%s": "%s"' % (j(k), j(v))
1397 ", ".join('"%s": "%s"' % (j(k), j(v))
1379 for k, v in copies))
1398 for k, v in copies))
1380
1399
1381 matchfn = self.matchfn
1400 matchfn = self.matchfn
1382 if matchfn:
1401 if matchfn:
1383 stat = self.diffopts.get('stat')
1402 stat = self.diffopts.get('stat')
1384 diff = self.diffopts.get('patch')
1403 diff = self.diffopts.get('patch')
1385 diffopts = patch.difffeatureopts(self.ui, self.diffopts, git=True)
1404 diffopts = patch.difffeatureopts(self.ui, self.diffopts, git=True)
1386 node, prev = ctx.node(), ctx.p1().node()
1405 node, prev = ctx.node(), ctx.p1().node()
1387 if stat:
1406 if stat:
1388 self.ui.pushbuffer()
1407 self.ui.pushbuffer()
1389 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1408 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1390 match=matchfn, stat=True)
1409 match=matchfn, stat=True)
1391 self.ui.write(',\n "diffstat": "%s"' % j(self.ui.popbuffer()))
1410 self.ui.write(',\n "diffstat": "%s"' % j(self.ui.popbuffer()))
1392 if diff:
1411 if diff:
1393 self.ui.pushbuffer()
1412 self.ui.pushbuffer()
1394 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1413 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1395 match=matchfn, stat=False)
1414 match=matchfn, stat=False)
1396 self.ui.write(',\n "diff": "%s"' % j(self.ui.popbuffer()))
1415 self.ui.write(',\n "diff": "%s"' % j(self.ui.popbuffer()))
1397
1416
1398 self.ui.write("\n }")
1417 self.ui.write("\n }")
1399
1418
1400 class changeset_templater(changeset_printer):
1419 class changeset_templater(changeset_printer):
1401 '''format changeset information.'''
1420 '''format changeset information.'''
1402
1421
1403 def __init__(self, ui, repo, matchfn, diffopts, tmpl, mapfile, buffered):
1422 def __init__(self, ui, repo, matchfn, diffopts, tmpl, mapfile, buffered):
1404 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1423 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1405 formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12])
1424 formatnode = ui.debugflag and (lambda x: x) or (lambda x: x[:12])
1406 defaulttempl = {
1425 defaulttempl = {
1407 'parent': '{rev}:{node|formatnode} ',
1426 'parent': '{rev}:{node|formatnode} ',
1408 'manifest': '{rev}:{node|formatnode}',
1427 'manifest': '{rev}:{node|formatnode}',
1409 'file_copy': '{name} ({source})',
1428 'file_copy': '{name} ({source})',
1410 'extra': '{key}={value|stringescape}'
1429 'extra': '{key}={value|stringescape}'
1411 }
1430 }
1412 # filecopy is preserved for compatibility reasons
1431 # filecopy is preserved for compatibility reasons
1413 defaulttempl['filecopy'] = defaulttempl['file_copy']
1432 defaulttempl['filecopy'] = defaulttempl['file_copy']
1414 self.t = templater.templater(mapfile, {'formatnode': formatnode},
1433 self.t = templater.templater(mapfile, {'formatnode': formatnode},
1415 cache=defaulttempl)
1434 cache=defaulttempl)
1416 if tmpl:
1435 if tmpl:
1417 self.t.cache['changeset'] = tmpl
1436 self.t.cache['changeset'] = tmpl
1418
1437
1419 self.cache = {}
1438 self.cache = {}
1420
1439
1421 # find correct templates for current mode
1440 # find correct templates for current mode
1422 tmplmodes = [
1441 tmplmodes = [
1423 (True, None),
1442 (True, None),
1424 (self.ui.verbose, 'verbose'),
1443 (self.ui.verbose, 'verbose'),
1425 (self.ui.quiet, 'quiet'),
1444 (self.ui.quiet, 'quiet'),
1426 (self.ui.debugflag, 'debug'),
1445 (self.ui.debugflag, 'debug'),
1427 ]
1446 ]
1428
1447
1429 self._parts = {'header': '', 'footer': '', 'changeset': 'changeset',
1448 self._parts = {'header': '', 'footer': '', 'changeset': 'changeset',
1430 'docheader': '', 'docfooter': ''}
1449 'docheader': '', 'docfooter': ''}
1431 for mode, postfix in tmplmodes:
1450 for mode, postfix in tmplmodes:
1432 for t in self._parts:
1451 for t in self._parts:
1433 cur = t
1452 cur = t
1434 if postfix:
1453 if postfix:
1435 cur += "_" + postfix
1454 cur += "_" + postfix
1436 if mode and cur in self.t:
1455 if mode and cur in self.t:
1437 self._parts[t] = cur
1456 self._parts[t] = cur
1438
1457
1439 if self._parts['docheader']:
1458 if self._parts['docheader']:
1440 self.ui.write(templater.stringify(self.t(self._parts['docheader'])))
1459 self.ui.write(templater.stringify(self.t(self._parts['docheader'])))
1441
1460
1442 def close(self):
1461 def close(self):
1443 if self._parts['docfooter']:
1462 if self._parts['docfooter']:
1444 if not self.footer:
1463 if not self.footer:
1445 self.footer = ""
1464 self.footer = ""
1446 self.footer += templater.stringify(self.t(self._parts['docfooter']))
1465 self.footer += templater.stringify(self.t(self._parts['docfooter']))
1447 return super(changeset_templater, self).close()
1466 return super(changeset_templater, self).close()
1448
1467
1449 def _show(self, ctx, copies, matchfn, props):
1468 def _show(self, ctx, copies, matchfn, props):
1450 '''show a single changeset or file revision'''
1469 '''show a single changeset or file revision'''
1451 props = props.copy()
1470 props = props.copy()
1452 props.update(templatekw.keywords)
1471 props.update(templatekw.keywords)
1453 props['templ'] = self.t
1472 props['templ'] = self.t
1454 props['ctx'] = ctx
1473 props['ctx'] = ctx
1455 props['repo'] = self.repo
1474 props['repo'] = self.repo
1456 props['revcache'] = {'copies': copies}
1475 props['revcache'] = {'copies': copies}
1457 props['cache'] = self.cache
1476 props['cache'] = self.cache
1458
1477
1459 try:
1478 try:
1460 # write header
1479 # write header
1461 if self._parts['header']:
1480 if self._parts['header']:
1462 h = templater.stringify(self.t(self._parts['header'], **props))
1481 h = templater.stringify(self.t(self._parts['header'], **props))
1463 if self.buffered:
1482 if self.buffered:
1464 self.header[ctx.rev()] = h
1483 self.header[ctx.rev()] = h
1465 else:
1484 else:
1466 if self.lastheader != h:
1485 if self.lastheader != h:
1467 self.lastheader = h
1486 self.lastheader = h
1468 self.ui.write(h)
1487 self.ui.write(h)
1469
1488
1470 # write changeset metadata, then patch if requested
1489 # write changeset metadata, then patch if requested
1471 key = self._parts['changeset']
1490 key = self._parts['changeset']
1472 self.ui.write(templater.stringify(self.t(key, **props)))
1491 self.ui.write(templater.stringify(self.t(key, **props)))
1473 self.showpatch(ctx.node(), matchfn)
1492 self.showpatch(ctx.node(), matchfn)
1474
1493
1475 if self._parts['footer']:
1494 if self._parts['footer']:
1476 if not self.footer:
1495 if not self.footer:
1477 self.footer = templater.stringify(
1496 self.footer = templater.stringify(
1478 self.t(self._parts['footer'], **props))
1497 self.t(self._parts['footer'], **props))
1479 except KeyError as inst:
1498 except KeyError as inst:
1480 msg = _("%s: no key named '%s'")
1499 msg = _("%s: no key named '%s'")
1481 raise util.Abort(msg % (self.t.mapfile, inst.args[0]))
1500 raise util.Abort(msg % (self.t.mapfile, inst.args[0]))
1482 except SyntaxError as inst:
1501 except SyntaxError as inst:
1483 raise util.Abort('%s: %s' % (self.t.mapfile, inst.args[0]))
1502 raise util.Abort('%s: %s' % (self.t.mapfile, inst.args[0]))
1484
1503
1485 def gettemplate(ui, tmpl, style):
1504 def gettemplate(ui, tmpl, style):
1486 """
1505 """
1487 Find the template matching the given template spec or style.
1506 Find the template matching the given template spec or style.
1488 """
1507 """
1489
1508
1490 # ui settings
1509 # ui settings
1491 if not tmpl and not style: # template are stronger than style
1510 if not tmpl and not style: # template are stronger than style
1492 tmpl = ui.config('ui', 'logtemplate')
1511 tmpl = ui.config('ui', 'logtemplate')
1493 if tmpl:
1512 if tmpl:
1494 try:
1513 try:
1495 tmpl = templater.unquotestring(tmpl)
1514 tmpl = templater.unquotestring(tmpl)
1496 except SyntaxError:
1515 except SyntaxError:
1497 pass
1516 pass
1498 return tmpl, None
1517 return tmpl, None
1499 else:
1518 else:
1500 style = util.expandpath(ui.config('ui', 'style', ''))
1519 style = util.expandpath(ui.config('ui', 'style', ''))
1501
1520
1502 if not tmpl and style:
1521 if not tmpl and style:
1503 mapfile = style
1522 mapfile = style
1504 if not os.path.split(mapfile)[0]:
1523 if not os.path.split(mapfile)[0]:
1505 mapname = (templater.templatepath('map-cmdline.' + mapfile)
1524 mapname = (templater.templatepath('map-cmdline.' + mapfile)
1506 or templater.templatepath(mapfile))
1525 or templater.templatepath(mapfile))
1507 if mapname:
1526 if mapname:
1508 mapfile = mapname
1527 mapfile = mapname
1509 return None, mapfile
1528 return None, mapfile
1510
1529
1511 if not tmpl:
1530 if not tmpl:
1512 return None, None
1531 return None, None
1513
1532
1514 return formatter.lookuptemplate(ui, 'changeset', tmpl)
1533 return formatter.lookuptemplate(ui, 'changeset', tmpl)
1515
1534
1516 def show_changeset(ui, repo, opts, buffered=False):
1535 def show_changeset(ui, repo, opts, buffered=False):
1517 """show one changeset using template or regular display.
1536 """show one changeset using template or regular display.
1518
1537
1519 Display format will be the first non-empty hit of:
1538 Display format will be the first non-empty hit of:
1520 1. option 'template'
1539 1. option 'template'
1521 2. option 'style'
1540 2. option 'style'
1522 3. [ui] setting 'logtemplate'
1541 3. [ui] setting 'logtemplate'
1523 4. [ui] setting 'style'
1542 4. [ui] setting 'style'
1524 If all of these values are either the unset or the empty string,
1543 If all of these values are either the unset or the empty string,
1525 regular display via changeset_printer() is done.
1544 regular display via changeset_printer() is done.
1526 """
1545 """
1527 # options
1546 # options
1528 matchfn = None
1547 matchfn = None
1529 if opts.get('patch') or opts.get('stat'):
1548 if opts.get('patch') or opts.get('stat'):
1530 matchfn = scmutil.matchall(repo)
1549 matchfn = scmutil.matchall(repo)
1531
1550
1532 if opts.get('template') == 'json':
1551 if opts.get('template') == 'json':
1533 return jsonchangeset(ui, repo, matchfn, opts, buffered)
1552 return jsonchangeset(ui, repo, matchfn, opts, buffered)
1534
1553
1535 tmpl, mapfile = gettemplate(ui, opts.get('template'), opts.get('style'))
1554 tmpl, mapfile = gettemplate(ui, opts.get('template'), opts.get('style'))
1536
1555
1537 if not tmpl and not mapfile:
1556 if not tmpl and not mapfile:
1538 return changeset_printer(ui, repo, matchfn, opts, buffered)
1557 return changeset_printer(ui, repo, matchfn, opts, buffered)
1539
1558
1540 try:
1559 try:
1541 t = changeset_templater(ui, repo, matchfn, opts, tmpl, mapfile,
1560 t = changeset_templater(ui, repo, matchfn, opts, tmpl, mapfile,
1542 buffered)
1561 buffered)
1543 except SyntaxError as inst:
1562 except SyntaxError as inst:
1544 raise util.Abort(inst.args[0])
1563 raise util.Abort(inst.args[0])
1545 return t
1564 return t
1546
1565
1547 def showmarker(ui, marker):
1566 def showmarker(ui, marker):
1548 """utility function to display obsolescence marker in a readable way
1567 """utility function to display obsolescence marker in a readable way
1549
1568
1550 To be used by debug function."""
1569 To be used by debug function."""
1551 ui.write(hex(marker.precnode()))
1570 ui.write(hex(marker.precnode()))
1552 for repl in marker.succnodes():
1571 for repl in marker.succnodes():
1553 ui.write(' ')
1572 ui.write(' ')
1554 ui.write(hex(repl))
1573 ui.write(hex(repl))
1555 ui.write(' %X ' % marker.flags())
1574 ui.write(' %X ' % marker.flags())
1556 parents = marker.parentnodes()
1575 parents = marker.parentnodes()
1557 if parents is not None:
1576 if parents is not None:
1558 ui.write('{%s} ' % ', '.join(hex(p) for p in parents))
1577 ui.write('{%s} ' % ', '.join(hex(p) for p in parents))
1559 ui.write('(%s) ' % util.datestr(marker.date()))
1578 ui.write('(%s) ' % util.datestr(marker.date()))
1560 ui.write('{%s}' % (', '.join('%r: %r' % t for t in
1579 ui.write('{%s}' % (', '.join('%r: %r' % t for t in
1561 sorted(marker.metadata().items())
1580 sorted(marker.metadata().items())
1562 if t[0] != 'date')))
1581 if t[0] != 'date')))
1563 ui.write('\n')
1582 ui.write('\n')
1564
1583
1565 def finddate(ui, repo, date):
1584 def finddate(ui, repo, date):
1566 """Find the tipmost changeset that matches the given date spec"""
1585 """Find the tipmost changeset that matches the given date spec"""
1567
1586
1568 df = util.matchdate(date)
1587 df = util.matchdate(date)
1569 m = scmutil.matchall(repo)
1588 m = scmutil.matchall(repo)
1570 results = {}
1589 results = {}
1571
1590
1572 def prep(ctx, fns):
1591 def prep(ctx, fns):
1573 d = ctx.date()
1592 d = ctx.date()
1574 if df(d[0]):
1593 if df(d[0]):
1575 results[ctx.rev()] = d
1594 results[ctx.rev()] = d
1576
1595
1577 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
1596 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
1578 rev = ctx.rev()
1597 rev = ctx.rev()
1579 if rev in results:
1598 if rev in results:
1580 ui.status(_("found revision %s from %s\n") %
1599 ui.status(_("found revision %s from %s\n") %
1581 (rev, util.datestr(results[rev])))
1600 (rev, util.datestr(results[rev])))
1582 return str(rev)
1601 return str(rev)
1583
1602
1584 raise util.Abort(_("revision matching date not found"))
1603 raise util.Abort(_("revision matching date not found"))
1585
1604
1586 def increasingwindows(windowsize=8, sizelimit=512):
1605 def increasingwindows(windowsize=8, sizelimit=512):
1587 while True:
1606 while True:
1588 yield windowsize
1607 yield windowsize
1589 if windowsize < sizelimit:
1608 if windowsize < sizelimit:
1590 windowsize *= 2
1609 windowsize *= 2
1591
1610
1592 class FileWalkError(Exception):
1611 class FileWalkError(Exception):
1593 pass
1612 pass
1594
1613
1595 def walkfilerevs(repo, match, follow, revs, fncache):
1614 def walkfilerevs(repo, match, follow, revs, fncache):
1596 '''Walks the file history for the matched files.
1615 '''Walks the file history for the matched files.
1597
1616
1598 Returns the changeset revs that are involved in the file history.
1617 Returns the changeset revs that are involved in the file history.
1599
1618
1600 Throws FileWalkError if the file history can't be walked using
1619 Throws FileWalkError if the file history can't be walked using
1601 filelogs alone.
1620 filelogs alone.
1602 '''
1621 '''
1603 wanted = set()
1622 wanted = set()
1604 copies = []
1623 copies = []
1605 minrev, maxrev = min(revs), max(revs)
1624 minrev, maxrev = min(revs), max(revs)
1606 def filerevgen(filelog, last):
1625 def filerevgen(filelog, last):
1607 """
1626 """
1608 Only files, no patterns. Check the history of each file.
1627 Only files, no patterns. Check the history of each file.
1609
1628
1610 Examines filelog entries within minrev, maxrev linkrev range
1629 Examines filelog entries within minrev, maxrev linkrev range
1611 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
1630 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
1612 tuples in backwards order
1631 tuples in backwards order
1613 """
1632 """
1614 cl_count = len(repo)
1633 cl_count = len(repo)
1615 revs = []
1634 revs = []
1616 for j in xrange(0, last + 1):
1635 for j in xrange(0, last + 1):
1617 linkrev = filelog.linkrev(j)
1636 linkrev = filelog.linkrev(j)
1618 if linkrev < minrev:
1637 if linkrev < minrev:
1619 continue
1638 continue
1620 # only yield rev for which we have the changelog, it can
1639 # only yield rev for which we have the changelog, it can
1621 # happen while doing "hg log" during a pull or commit
1640 # happen while doing "hg log" during a pull or commit
1622 if linkrev >= cl_count:
1641 if linkrev >= cl_count:
1623 break
1642 break
1624
1643
1625 parentlinkrevs = []
1644 parentlinkrevs = []
1626 for p in filelog.parentrevs(j):
1645 for p in filelog.parentrevs(j):
1627 if p != nullrev:
1646 if p != nullrev:
1628 parentlinkrevs.append(filelog.linkrev(p))
1647 parentlinkrevs.append(filelog.linkrev(p))
1629 n = filelog.node(j)
1648 n = filelog.node(j)
1630 revs.append((linkrev, parentlinkrevs,
1649 revs.append((linkrev, parentlinkrevs,
1631 follow and filelog.renamed(n)))
1650 follow and filelog.renamed(n)))
1632
1651
1633 return reversed(revs)
1652 return reversed(revs)
1634 def iterfiles():
1653 def iterfiles():
1635 pctx = repo['.']
1654 pctx = repo['.']
1636 for filename in match.files():
1655 for filename in match.files():
1637 if follow:
1656 if follow:
1638 if filename not in pctx:
1657 if filename not in pctx:
1639 raise util.Abort(_('cannot follow file not in parent '
1658 raise util.Abort(_('cannot follow file not in parent '
1640 'revision: "%s"') % filename)
1659 'revision: "%s"') % filename)
1641 yield filename, pctx[filename].filenode()
1660 yield filename, pctx[filename].filenode()
1642 else:
1661 else:
1643 yield filename, None
1662 yield filename, None
1644 for filename_node in copies:
1663 for filename_node in copies:
1645 yield filename_node
1664 yield filename_node
1646
1665
1647 for file_, node in iterfiles():
1666 for file_, node in iterfiles():
1648 filelog = repo.file(file_)
1667 filelog = repo.file(file_)
1649 if not len(filelog):
1668 if not len(filelog):
1650 if node is None:
1669 if node is None:
1651 # A zero count may be a directory or deleted file, so
1670 # A zero count may be a directory or deleted file, so
1652 # try to find matching entries on the slow path.
1671 # try to find matching entries on the slow path.
1653 if follow:
1672 if follow:
1654 raise util.Abort(
1673 raise util.Abort(
1655 _('cannot follow nonexistent file: "%s"') % file_)
1674 _('cannot follow nonexistent file: "%s"') % file_)
1656 raise FileWalkError("Cannot walk via filelog")
1675 raise FileWalkError("Cannot walk via filelog")
1657 else:
1676 else:
1658 continue
1677 continue
1659
1678
1660 if node is None:
1679 if node is None:
1661 last = len(filelog) - 1
1680 last = len(filelog) - 1
1662 else:
1681 else:
1663 last = filelog.rev(node)
1682 last = filelog.rev(node)
1664
1683
1665 # keep track of all ancestors of the file
1684 # keep track of all ancestors of the file
1666 ancestors = set([filelog.linkrev(last)])
1685 ancestors = set([filelog.linkrev(last)])
1667
1686
1668 # iterate from latest to oldest revision
1687 # iterate from latest to oldest revision
1669 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
1688 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
1670 if not follow:
1689 if not follow:
1671 if rev > maxrev:
1690 if rev > maxrev:
1672 continue
1691 continue
1673 else:
1692 else:
1674 # Note that last might not be the first interesting
1693 # Note that last might not be the first interesting
1675 # rev to us:
1694 # rev to us:
1676 # if the file has been changed after maxrev, we'll
1695 # if the file has been changed after maxrev, we'll
1677 # have linkrev(last) > maxrev, and we still need
1696 # have linkrev(last) > maxrev, and we still need
1678 # to explore the file graph
1697 # to explore the file graph
1679 if rev not in ancestors:
1698 if rev not in ancestors:
1680 continue
1699 continue
1681 # XXX insert 1327 fix here
1700 # XXX insert 1327 fix here
1682 if flparentlinkrevs:
1701 if flparentlinkrevs:
1683 ancestors.update(flparentlinkrevs)
1702 ancestors.update(flparentlinkrevs)
1684
1703
1685 fncache.setdefault(rev, []).append(file_)
1704 fncache.setdefault(rev, []).append(file_)
1686 wanted.add(rev)
1705 wanted.add(rev)
1687 if copied:
1706 if copied:
1688 copies.append(copied)
1707 copies.append(copied)
1689
1708
1690 return wanted
1709 return wanted
1691
1710
1692 class _followfilter(object):
1711 class _followfilter(object):
1693 def __init__(self, repo, onlyfirst=False):
1712 def __init__(self, repo, onlyfirst=False):
1694 self.repo = repo
1713 self.repo = repo
1695 self.startrev = nullrev
1714 self.startrev = nullrev
1696 self.roots = set()
1715 self.roots = set()
1697 self.onlyfirst = onlyfirst
1716 self.onlyfirst = onlyfirst
1698
1717
1699 def match(self, rev):
1718 def match(self, rev):
1700 def realparents(rev):
1719 def realparents(rev):
1701 if self.onlyfirst:
1720 if self.onlyfirst:
1702 return self.repo.changelog.parentrevs(rev)[0:1]
1721 return self.repo.changelog.parentrevs(rev)[0:1]
1703 else:
1722 else:
1704 return filter(lambda x: x != nullrev,
1723 return filter(lambda x: x != nullrev,
1705 self.repo.changelog.parentrevs(rev))
1724 self.repo.changelog.parentrevs(rev))
1706
1725
1707 if self.startrev == nullrev:
1726 if self.startrev == nullrev:
1708 self.startrev = rev
1727 self.startrev = rev
1709 return True
1728 return True
1710
1729
1711 if rev > self.startrev:
1730 if rev > self.startrev:
1712 # forward: all descendants
1731 # forward: all descendants
1713 if not self.roots:
1732 if not self.roots:
1714 self.roots.add(self.startrev)
1733 self.roots.add(self.startrev)
1715 for parent in realparents(rev):
1734 for parent in realparents(rev):
1716 if parent in self.roots:
1735 if parent in self.roots:
1717 self.roots.add(rev)
1736 self.roots.add(rev)
1718 return True
1737 return True
1719 else:
1738 else:
1720 # backwards: all parents
1739 # backwards: all parents
1721 if not self.roots:
1740 if not self.roots:
1722 self.roots.update(realparents(self.startrev))
1741 self.roots.update(realparents(self.startrev))
1723 if rev in self.roots:
1742 if rev in self.roots:
1724 self.roots.remove(rev)
1743 self.roots.remove(rev)
1725 self.roots.update(realparents(rev))
1744 self.roots.update(realparents(rev))
1726 return True
1745 return True
1727
1746
1728 return False
1747 return False
1729
1748
1730 def walkchangerevs(repo, match, opts, prepare):
1749 def walkchangerevs(repo, match, opts, prepare):
1731 '''Iterate over files and the revs in which they changed.
1750 '''Iterate over files and the revs in which they changed.
1732
1751
1733 Callers most commonly need to iterate backwards over the history
1752 Callers most commonly need to iterate backwards over the history
1734 in which they are interested. Doing so has awful (quadratic-looking)
1753 in which they are interested. Doing so has awful (quadratic-looking)
1735 performance, so we use iterators in a "windowed" way.
1754 performance, so we use iterators in a "windowed" way.
1736
1755
1737 We walk a window of revisions in the desired order. Within the
1756 We walk a window of revisions in the desired order. Within the
1738 window, we first walk forwards to gather data, then in the desired
1757 window, we first walk forwards to gather data, then in the desired
1739 order (usually backwards) to display it.
1758 order (usually backwards) to display it.
1740
1759
1741 This function returns an iterator yielding contexts. Before
1760 This function returns an iterator yielding contexts. Before
1742 yielding each context, the iterator will first call the prepare
1761 yielding each context, the iterator will first call the prepare
1743 function on each context in the window in forward order.'''
1762 function on each context in the window in forward order.'''
1744
1763
1745 follow = opts.get('follow') or opts.get('follow_first')
1764 follow = opts.get('follow') or opts.get('follow_first')
1746 revs = _logrevs(repo, opts)
1765 revs = _logrevs(repo, opts)
1747 if not revs:
1766 if not revs:
1748 return []
1767 return []
1749 wanted = set()
1768 wanted = set()
1750 slowpath = match.anypats() or ((match.isexact() or match.prefix()) and
1769 slowpath = match.anypats() or ((match.isexact() or match.prefix()) and
1751 opts.get('removed'))
1770 opts.get('removed'))
1752 fncache = {}
1771 fncache = {}
1753 change = repo.changectx
1772 change = repo.changectx
1754
1773
1755 # First step is to fill wanted, the set of revisions that we want to yield.
1774 # First step is to fill wanted, the set of revisions that we want to yield.
1756 # When it does not induce extra cost, we also fill fncache for revisions in
1775 # When it does not induce extra cost, we also fill fncache for revisions in
1757 # wanted: a cache of filenames that were changed (ctx.files()) and that
1776 # wanted: a cache of filenames that were changed (ctx.files()) and that
1758 # match the file filtering conditions.
1777 # match the file filtering conditions.
1759
1778
1760 if match.always():
1779 if match.always():
1761 # No files, no patterns. Display all revs.
1780 # No files, no patterns. Display all revs.
1762 wanted = revs
1781 wanted = revs
1763 elif not slowpath:
1782 elif not slowpath:
1764 # We only have to read through the filelog to find wanted revisions
1783 # We only have to read through the filelog to find wanted revisions
1765
1784
1766 try:
1785 try:
1767 wanted = walkfilerevs(repo, match, follow, revs, fncache)
1786 wanted = walkfilerevs(repo, match, follow, revs, fncache)
1768 except FileWalkError:
1787 except FileWalkError:
1769 slowpath = True
1788 slowpath = True
1770
1789
1771 # We decided to fall back to the slowpath because at least one
1790 # We decided to fall back to the slowpath because at least one
1772 # of the paths was not a file. Check to see if at least one of them
1791 # of the paths was not a file. Check to see if at least one of them
1773 # existed in history, otherwise simply return
1792 # existed in history, otherwise simply return
1774 for path in match.files():
1793 for path in match.files():
1775 if path == '.' or path in repo.store:
1794 if path == '.' or path in repo.store:
1776 break
1795 break
1777 else:
1796 else:
1778 return []
1797 return []
1779
1798
1780 if slowpath:
1799 if slowpath:
1781 # We have to read the changelog to match filenames against
1800 # We have to read the changelog to match filenames against
1782 # changed files
1801 # changed files
1783
1802
1784 if follow:
1803 if follow:
1785 raise util.Abort(_('can only follow copies/renames for explicit '
1804 raise util.Abort(_('can only follow copies/renames for explicit '
1786 'filenames'))
1805 'filenames'))
1787
1806
1788 # The slow path checks files modified in every changeset.
1807 # The slow path checks files modified in every changeset.
1789 # This is really slow on large repos, so compute the set lazily.
1808 # This is really slow on large repos, so compute the set lazily.
1790 class lazywantedset(object):
1809 class lazywantedset(object):
1791 def __init__(self):
1810 def __init__(self):
1792 self.set = set()
1811 self.set = set()
1793 self.revs = set(revs)
1812 self.revs = set(revs)
1794
1813
1795 # No need to worry about locality here because it will be accessed
1814 # No need to worry about locality here because it will be accessed
1796 # in the same order as the increasing window below.
1815 # in the same order as the increasing window below.
1797 def __contains__(self, value):
1816 def __contains__(self, value):
1798 if value in self.set:
1817 if value in self.set:
1799 return True
1818 return True
1800 elif not value in self.revs:
1819 elif not value in self.revs:
1801 return False
1820 return False
1802 else:
1821 else:
1803 self.revs.discard(value)
1822 self.revs.discard(value)
1804 ctx = change(value)
1823 ctx = change(value)
1805 matches = filter(match, ctx.files())
1824 matches = filter(match, ctx.files())
1806 if matches:
1825 if matches:
1807 fncache[value] = matches
1826 fncache[value] = matches
1808 self.set.add(value)
1827 self.set.add(value)
1809 return True
1828 return True
1810 return False
1829 return False
1811
1830
1812 def discard(self, value):
1831 def discard(self, value):
1813 self.revs.discard(value)
1832 self.revs.discard(value)
1814 self.set.discard(value)
1833 self.set.discard(value)
1815
1834
1816 wanted = lazywantedset()
1835 wanted = lazywantedset()
1817
1836
1818 # it might be worthwhile to do this in the iterator if the rev range
1837 # it might be worthwhile to do this in the iterator if the rev range
1819 # is descending and the prune args are all within that range
1838 # is descending and the prune args are all within that range
1820 for rev in opts.get('prune', ()):
1839 for rev in opts.get('prune', ()):
1821 rev = repo[rev].rev()
1840 rev = repo[rev].rev()
1822 ff = _followfilter(repo)
1841 ff = _followfilter(repo)
1823 stop = min(revs[0], revs[-1])
1842 stop = min(revs[0], revs[-1])
1824 for x in xrange(rev, stop - 1, -1):
1843 for x in xrange(rev, stop - 1, -1):
1825 if ff.match(x):
1844 if ff.match(x):
1826 wanted = wanted - [x]
1845 wanted = wanted - [x]
1827
1846
1828 # Now that wanted is correctly initialized, we can iterate over the
1847 # Now that wanted is correctly initialized, we can iterate over the
1829 # revision range, yielding only revisions in wanted.
1848 # revision range, yielding only revisions in wanted.
1830 def iterate():
1849 def iterate():
1831 if follow and match.always():
1850 if follow and match.always():
1832 ff = _followfilter(repo, onlyfirst=opts.get('follow_first'))
1851 ff = _followfilter(repo, onlyfirst=opts.get('follow_first'))
1833 def want(rev):
1852 def want(rev):
1834 return ff.match(rev) and rev in wanted
1853 return ff.match(rev) and rev in wanted
1835 else:
1854 else:
1836 def want(rev):
1855 def want(rev):
1837 return rev in wanted
1856 return rev in wanted
1838
1857
1839 it = iter(revs)
1858 it = iter(revs)
1840 stopiteration = False
1859 stopiteration = False
1841 for windowsize in increasingwindows():
1860 for windowsize in increasingwindows():
1842 nrevs = []
1861 nrevs = []
1843 for i in xrange(windowsize):
1862 for i in xrange(windowsize):
1844 rev = next(it, None)
1863 rev = next(it, None)
1845 if rev is None:
1864 if rev is None:
1846 stopiteration = True
1865 stopiteration = True
1847 break
1866 break
1848 elif want(rev):
1867 elif want(rev):
1849 nrevs.append(rev)
1868 nrevs.append(rev)
1850 for rev in sorted(nrevs):
1869 for rev in sorted(nrevs):
1851 fns = fncache.get(rev)
1870 fns = fncache.get(rev)
1852 ctx = change(rev)
1871 ctx = change(rev)
1853 if not fns:
1872 if not fns:
1854 def fns_generator():
1873 def fns_generator():
1855 for f in ctx.files():
1874 for f in ctx.files():
1856 if match(f):
1875 if match(f):
1857 yield f
1876 yield f
1858 fns = fns_generator()
1877 fns = fns_generator()
1859 prepare(ctx, fns)
1878 prepare(ctx, fns)
1860 for rev in nrevs:
1879 for rev in nrevs:
1861 yield change(rev)
1880 yield change(rev)
1862
1881
1863 if stopiteration:
1882 if stopiteration:
1864 break
1883 break
1865
1884
1866 return iterate()
1885 return iterate()
1867
1886
1868 def _makefollowlogfilematcher(repo, files, followfirst):
1887 def _makefollowlogfilematcher(repo, files, followfirst):
1869 # When displaying a revision with --patch --follow FILE, we have
1888 # When displaying a revision with --patch --follow FILE, we have
1870 # to know which file of the revision must be diffed. With
1889 # to know which file of the revision must be diffed. With
1871 # --follow, we want the names of the ancestors of FILE in the
1890 # --follow, we want the names of the ancestors of FILE in the
1872 # revision, stored in "fcache". "fcache" is populated by
1891 # revision, stored in "fcache". "fcache" is populated by
1873 # reproducing the graph traversal already done by --follow revset
1892 # reproducing the graph traversal already done by --follow revset
1874 # and relating linkrevs to file names (which is not "correct" but
1893 # and relating linkrevs to file names (which is not "correct" but
1875 # good enough).
1894 # good enough).
1876 fcache = {}
1895 fcache = {}
1877 fcacheready = [False]
1896 fcacheready = [False]
1878 pctx = repo['.']
1897 pctx = repo['.']
1879
1898
1880 def populate():
1899 def populate():
1881 for fn in files:
1900 for fn in files:
1882 for i in ((pctx[fn],), pctx[fn].ancestors(followfirst=followfirst)):
1901 for i in ((pctx[fn],), pctx[fn].ancestors(followfirst=followfirst)):
1883 for c in i:
1902 for c in i:
1884 fcache.setdefault(c.linkrev(), set()).add(c.path())
1903 fcache.setdefault(c.linkrev(), set()).add(c.path())
1885
1904
1886 def filematcher(rev):
1905 def filematcher(rev):
1887 if not fcacheready[0]:
1906 if not fcacheready[0]:
1888 # Lazy initialization
1907 # Lazy initialization
1889 fcacheready[0] = True
1908 fcacheready[0] = True
1890 populate()
1909 populate()
1891 return scmutil.matchfiles(repo, fcache.get(rev, []))
1910 return scmutil.matchfiles(repo, fcache.get(rev, []))
1892
1911
1893 return filematcher
1912 return filematcher
1894
1913
1895 def _makenofollowlogfilematcher(repo, pats, opts):
1914 def _makenofollowlogfilematcher(repo, pats, opts):
1896 '''hook for extensions to override the filematcher for non-follow cases'''
1915 '''hook for extensions to override the filematcher for non-follow cases'''
1897 return None
1916 return None
1898
1917
1899 def _makelogrevset(repo, pats, opts, revs):
1918 def _makelogrevset(repo, pats, opts, revs):
1900 """Return (expr, filematcher) where expr is a revset string built
1919 """Return (expr, filematcher) where expr is a revset string built
1901 from log options and file patterns or None. If --stat or --patch
1920 from log options and file patterns or None. If --stat or --patch
1902 are not passed filematcher is None. Otherwise it is a callable
1921 are not passed filematcher is None. Otherwise it is a callable
1903 taking a revision number and returning a match objects filtering
1922 taking a revision number and returning a match objects filtering
1904 the files to be detailed when displaying the revision.
1923 the files to be detailed when displaying the revision.
1905 """
1924 """
1906 opt2revset = {
1925 opt2revset = {
1907 'no_merges': ('not merge()', None),
1926 'no_merges': ('not merge()', None),
1908 'only_merges': ('merge()', None),
1927 'only_merges': ('merge()', None),
1909 '_ancestors': ('ancestors(%(val)s)', None),
1928 '_ancestors': ('ancestors(%(val)s)', None),
1910 '_fancestors': ('_firstancestors(%(val)s)', None),
1929 '_fancestors': ('_firstancestors(%(val)s)', None),
1911 '_descendants': ('descendants(%(val)s)', None),
1930 '_descendants': ('descendants(%(val)s)', None),
1912 '_fdescendants': ('_firstdescendants(%(val)s)', None),
1931 '_fdescendants': ('_firstdescendants(%(val)s)', None),
1913 '_matchfiles': ('_matchfiles(%(val)s)', None),
1932 '_matchfiles': ('_matchfiles(%(val)s)', None),
1914 'date': ('date(%(val)r)', None),
1933 'date': ('date(%(val)r)', None),
1915 'branch': ('branch(%(val)r)', ' or '),
1934 'branch': ('branch(%(val)r)', ' or '),
1916 '_patslog': ('filelog(%(val)r)', ' or '),
1935 '_patslog': ('filelog(%(val)r)', ' or '),
1917 '_patsfollow': ('follow(%(val)r)', ' or '),
1936 '_patsfollow': ('follow(%(val)r)', ' or '),
1918 '_patsfollowfirst': ('_followfirst(%(val)r)', ' or '),
1937 '_patsfollowfirst': ('_followfirst(%(val)r)', ' or '),
1919 'keyword': ('keyword(%(val)r)', ' or '),
1938 'keyword': ('keyword(%(val)r)', ' or '),
1920 'prune': ('not (%(val)r or ancestors(%(val)r))', ' and '),
1939 'prune': ('not (%(val)r or ancestors(%(val)r))', ' and '),
1921 'user': ('user(%(val)r)', ' or '),
1940 'user': ('user(%(val)r)', ' or '),
1922 }
1941 }
1923
1942
1924 opts = dict(opts)
1943 opts = dict(opts)
1925 # follow or not follow?
1944 # follow or not follow?
1926 follow = opts.get('follow') or opts.get('follow_first')
1945 follow = opts.get('follow') or opts.get('follow_first')
1927 if opts.get('follow_first'):
1946 if opts.get('follow_first'):
1928 followfirst = 1
1947 followfirst = 1
1929 else:
1948 else:
1930 followfirst = 0
1949 followfirst = 0
1931 # --follow with FILE behavior depends on revs...
1950 # --follow with FILE behavior depends on revs...
1932 it = iter(revs)
1951 it = iter(revs)
1933 startrev = it.next()
1952 startrev = it.next()
1934 followdescendants = startrev < next(it, startrev)
1953 followdescendants = startrev < next(it, startrev)
1935
1954
1936 # branch and only_branch are really aliases and must be handled at
1955 # branch and only_branch are really aliases and must be handled at
1937 # the same time
1956 # the same time
1938 opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
1957 opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
1939 opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']]
1958 opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']]
1940 # pats/include/exclude are passed to match.match() directly in
1959 # pats/include/exclude are passed to match.match() directly in
1941 # _matchfiles() revset but walkchangerevs() builds its matcher with
1960 # _matchfiles() revset but walkchangerevs() builds its matcher with
1942 # scmutil.match(). The difference is input pats are globbed on
1961 # scmutil.match(). The difference is input pats are globbed on
1943 # platforms without shell expansion (windows).
1962 # platforms without shell expansion (windows).
1944 wctx = repo[None]
1963 wctx = repo[None]
1945 match, pats = scmutil.matchandpats(wctx, pats, opts)
1964 match, pats = scmutil.matchandpats(wctx, pats, opts)
1946 slowpath = match.anypats() or ((match.isexact() or match.prefix()) and
1965 slowpath = match.anypats() or ((match.isexact() or match.prefix()) and
1947 opts.get('removed'))
1966 opts.get('removed'))
1948 if not slowpath:
1967 if not slowpath:
1949 for f in match.files():
1968 for f in match.files():
1950 if follow and f not in wctx:
1969 if follow and f not in wctx:
1951 # If the file exists, it may be a directory, so let it
1970 # If the file exists, it may be a directory, so let it
1952 # take the slow path.
1971 # take the slow path.
1953 if os.path.exists(repo.wjoin(f)):
1972 if os.path.exists(repo.wjoin(f)):
1954 slowpath = True
1973 slowpath = True
1955 continue
1974 continue
1956 else:
1975 else:
1957 raise util.Abort(_('cannot follow file not in parent '
1976 raise util.Abort(_('cannot follow file not in parent '
1958 'revision: "%s"') % f)
1977 'revision: "%s"') % f)
1959 filelog = repo.file(f)
1978 filelog = repo.file(f)
1960 if not filelog:
1979 if not filelog:
1961 # A zero count may be a directory or deleted file, so
1980 # A zero count may be a directory or deleted file, so
1962 # try to find matching entries on the slow path.
1981 # try to find matching entries on the slow path.
1963 if follow:
1982 if follow:
1964 raise util.Abort(
1983 raise util.Abort(
1965 _('cannot follow nonexistent file: "%s"') % f)
1984 _('cannot follow nonexistent file: "%s"') % f)
1966 slowpath = True
1985 slowpath = True
1967
1986
1968 # We decided to fall back to the slowpath because at least one
1987 # We decided to fall back to the slowpath because at least one
1969 # of the paths was not a file. Check to see if at least one of them
1988 # of the paths was not a file. Check to see if at least one of them
1970 # existed in history - in that case, we'll continue down the
1989 # existed in history - in that case, we'll continue down the
1971 # slowpath; otherwise, we can turn off the slowpath
1990 # slowpath; otherwise, we can turn off the slowpath
1972 if slowpath:
1991 if slowpath:
1973 for path in match.files():
1992 for path in match.files():
1974 if path == '.' or path in repo.store:
1993 if path == '.' or path in repo.store:
1975 break
1994 break
1976 else:
1995 else:
1977 slowpath = False
1996 slowpath = False
1978
1997
1979 fpats = ('_patsfollow', '_patsfollowfirst')
1998 fpats = ('_patsfollow', '_patsfollowfirst')
1980 fnopats = (('_ancestors', '_fancestors'),
1999 fnopats = (('_ancestors', '_fancestors'),
1981 ('_descendants', '_fdescendants'))
2000 ('_descendants', '_fdescendants'))
1982 if slowpath:
2001 if slowpath:
1983 # See walkchangerevs() slow path.
2002 # See walkchangerevs() slow path.
1984 #
2003 #
1985 # pats/include/exclude cannot be represented as separate
2004 # pats/include/exclude cannot be represented as separate
1986 # revset expressions as their filtering logic applies at file
2005 # revset expressions as their filtering logic applies at file
1987 # level. For instance "-I a -X a" matches a revision touching
2006 # level. For instance "-I a -X a" matches a revision touching
1988 # "a" and "b" while "file(a) and not file(b)" does
2007 # "a" and "b" while "file(a) and not file(b)" does
1989 # not. Besides, filesets are evaluated against the working
2008 # not. Besides, filesets are evaluated against the working
1990 # directory.
2009 # directory.
1991 matchargs = ['r:', 'd:relpath']
2010 matchargs = ['r:', 'd:relpath']
1992 for p in pats:
2011 for p in pats:
1993 matchargs.append('p:' + p)
2012 matchargs.append('p:' + p)
1994 for p in opts.get('include', []):
2013 for p in opts.get('include', []):
1995 matchargs.append('i:' + p)
2014 matchargs.append('i:' + p)
1996 for p in opts.get('exclude', []):
2015 for p in opts.get('exclude', []):
1997 matchargs.append('x:' + p)
2016 matchargs.append('x:' + p)
1998 matchargs = ','.join(('%r' % p) for p in matchargs)
2017 matchargs = ','.join(('%r' % p) for p in matchargs)
1999 opts['_matchfiles'] = matchargs
2018 opts['_matchfiles'] = matchargs
2000 if follow:
2019 if follow:
2001 opts[fnopats[0][followfirst]] = '.'
2020 opts[fnopats[0][followfirst]] = '.'
2002 else:
2021 else:
2003 if follow:
2022 if follow:
2004 if pats:
2023 if pats:
2005 # follow() revset interprets its file argument as a
2024 # follow() revset interprets its file argument as a
2006 # manifest entry, so use match.files(), not pats.
2025 # manifest entry, so use match.files(), not pats.
2007 opts[fpats[followfirst]] = list(match.files())
2026 opts[fpats[followfirst]] = list(match.files())
2008 else:
2027 else:
2009 op = fnopats[followdescendants][followfirst]
2028 op = fnopats[followdescendants][followfirst]
2010 opts[op] = 'rev(%d)' % startrev
2029 opts[op] = 'rev(%d)' % startrev
2011 else:
2030 else:
2012 opts['_patslog'] = list(pats)
2031 opts['_patslog'] = list(pats)
2013
2032
2014 filematcher = None
2033 filematcher = None
2015 if opts.get('patch') or opts.get('stat'):
2034 if opts.get('patch') or opts.get('stat'):
2016 # When following files, track renames via a special matcher.
2035 # When following files, track renames via a special matcher.
2017 # If we're forced to take the slowpath it means we're following
2036 # If we're forced to take the slowpath it means we're following
2018 # at least one pattern/directory, so don't bother with rename tracking.
2037 # at least one pattern/directory, so don't bother with rename tracking.
2019 if follow and not match.always() and not slowpath:
2038 if follow and not match.always() and not slowpath:
2020 # _makefollowlogfilematcher expects its files argument to be
2039 # _makefollowlogfilematcher expects its files argument to be
2021 # relative to the repo root, so use match.files(), not pats.
2040 # relative to the repo root, so use match.files(), not pats.
2022 filematcher = _makefollowlogfilematcher(repo, match.files(),
2041 filematcher = _makefollowlogfilematcher(repo, match.files(),
2023 followfirst)
2042 followfirst)
2024 else:
2043 else:
2025 filematcher = _makenofollowlogfilematcher(repo, pats, opts)
2044 filematcher = _makenofollowlogfilematcher(repo, pats, opts)
2026 if filematcher is None:
2045 if filematcher is None:
2027 filematcher = lambda rev: match
2046 filematcher = lambda rev: match
2028
2047
2029 expr = []
2048 expr = []
2030 for op, val in sorted(opts.iteritems()):
2049 for op, val in sorted(opts.iteritems()):
2031 if not val:
2050 if not val:
2032 continue
2051 continue
2033 if op not in opt2revset:
2052 if op not in opt2revset:
2034 continue
2053 continue
2035 revop, andor = opt2revset[op]
2054 revop, andor = opt2revset[op]
2036 if '%(val)' not in revop:
2055 if '%(val)' not in revop:
2037 expr.append(revop)
2056 expr.append(revop)
2038 else:
2057 else:
2039 if not isinstance(val, list):
2058 if not isinstance(val, list):
2040 e = revop % {'val': val}
2059 e = revop % {'val': val}
2041 else:
2060 else:
2042 e = '(' + andor.join((revop % {'val': v}) for v in val) + ')'
2061 e = '(' + andor.join((revop % {'val': v}) for v in val) + ')'
2043 expr.append(e)
2062 expr.append(e)
2044
2063
2045 if expr:
2064 if expr:
2046 expr = '(' + ' and '.join(expr) + ')'
2065 expr = '(' + ' and '.join(expr) + ')'
2047 else:
2066 else:
2048 expr = None
2067 expr = None
2049 return expr, filematcher
2068 return expr, filematcher
2050
2069
2051 def _logrevs(repo, opts):
2070 def _logrevs(repo, opts):
2052 # Default --rev value depends on --follow but --follow behavior
2071 # Default --rev value depends on --follow but --follow behavior
2053 # depends on revisions resolved from --rev...
2072 # depends on revisions resolved from --rev...
2054 follow = opts.get('follow') or opts.get('follow_first')
2073 follow = opts.get('follow') or opts.get('follow_first')
2055 if opts.get('rev'):
2074 if opts.get('rev'):
2056 revs = scmutil.revrange(repo, opts['rev'])
2075 revs = scmutil.revrange(repo, opts['rev'])
2057 elif follow and repo.dirstate.p1() == nullid:
2076 elif follow and repo.dirstate.p1() == nullid:
2058 revs = revset.baseset()
2077 revs = revset.baseset()
2059 elif follow:
2078 elif follow:
2060 revs = repo.revs('reverse(:.)')
2079 revs = repo.revs('reverse(:.)')
2061 else:
2080 else:
2062 revs = revset.spanset(repo)
2081 revs = revset.spanset(repo)
2063 revs.reverse()
2082 revs.reverse()
2064 return revs
2083 return revs
2065
2084
2066 def getgraphlogrevs(repo, pats, opts):
2085 def getgraphlogrevs(repo, pats, opts):
2067 """Return (revs, expr, filematcher) where revs is an iterable of
2086 """Return (revs, expr, filematcher) where revs is an iterable of
2068 revision numbers, expr is a revset string built from log options
2087 revision numbers, expr is a revset string built from log options
2069 and file patterns or None, and used to filter 'revs'. If --stat or
2088 and file patterns or None, and used to filter 'revs'. If --stat or
2070 --patch are not passed filematcher is None. Otherwise it is a
2089 --patch are not passed filematcher is None. Otherwise it is a
2071 callable taking a revision number and returning a match objects
2090 callable taking a revision number and returning a match objects
2072 filtering the files to be detailed when displaying the revision.
2091 filtering the files to be detailed when displaying the revision.
2073 """
2092 """
2074 limit = loglimit(opts)
2093 limit = loglimit(opts)
2075 revs = _logrevs(repo, opts)
2094 revs = _logrevs(repo, opts)
2076 if not revs:
2095 if not revs:
2077 return revset.baseset(), None, None
2096 return revset.baseset(), None, None
2078 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
2097 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
2079 if opts.get('rev'):
2098 if opts.get('rev'):
2080 # User-specified revs might be unsorted, but don't sort before
2099 # User-specified revs might be unsorted, but don't sort before
2081 # _makelogrevset because it might depend on the order of revs
2100 # _makelogrevset because it might depend on the order of revs
2082 revs.sort(reverse=True)
2101 revs.sort(reverse=True)
2083 if expr:
2102 if expr:
2084 # Revset matchers often operate faster on revisions in changelog
2103 # Revset matchers often operate faster on revisions in changelog
2085 # order, because most filters deal with the changelog.
2104 # order, because most filters deal with the changelog.
2086 revs.reverse()
2105 revs.reverse()
2087 matcher = revset.match(repo.ui, expr)
2106 matcher = revset.match(repo.ui, expr)
2088 # Revset matches can reorder revisions. "A or B" typically returns
2107 # Revset matches can reorder revisions. "A or B" typically returns
2089 # returns the revision matching A then the revision matching B. Sort
2108 # returns the revision matching A then the revision matching B. Sort
2090 # again to fix that.
2109 # again to fix that.
2091 revs = matcher(repo, revs)
2110 revs = matcher(repo, revs)
2092 revs.sort(reverse=True)
2111 revs.sort(reverse=True)
2093 if limit is not None:
2112 if limit is not None:
2094 limitedrevs = []
2113 limitedrevs = []
2095 for idx, rev in enumerate(revs):
2114 for idx, rev in enumerate(revs):
2096 if idx >= limit:
2115 if idx >= limit:
2097 break
2116 break
2098 limitedrevs.append(rev)
2117 limitedrevs.append(rev)
2099 revs = revset.baseset(limitedrevs)
2118 revs = revset.baseset(limitedrevs)
2100
2119
2101 return revs, expr, filematcher
2120 return revs, expr, filematcher
2102
2121
2103 def getlogrevs(repo, pats, opts):
2122 def getlogrevs(repo, pats, opts):
2104 """Return (revs, expr, filematcher) where revs is an iterable of
2123 """Return (revs, expr, filematcher) where revs is an iterable of
2105 revision numbers, expr is a revset string built from log options
2124 revision numbers, expr is a revset string built from log options
2106 and file patterns or None, and used to filter 'revs'. If --stat or
2125 and file patterns or None, and used to filter 'revs'. If --stat or
2107 --patch are not passed filematcher is None. Otherwise it is a
2126 --patch are not passed filematcher is None. Otherwise it is a
2108 callable taking a revision number and returning a match objects
2127 callable taking a revision number and returning a match objects
2109 filtering the files to be detailed when displaying the revision.
2128 filtering the files to be detailed when displaying the revision.
2110 """
2129 """
2111 limit = loglimit(opts)
2130 limit = loglimit(opts)
2112 revs = _logrevs(repo, opts)
2131 revs = _logrevs(repo, opts)
2113 if not revs:
2132 if not revs:
2114 return revset.baseset([]), None, None
2133 return revset.baseset([]), None, None
2115 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
2134 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
2116 if expr:
2135 if expr:
2117 # Revset matchers often operate faster on revisions in changelog
2136 # Revset matchers often operate faster on revisions in changelog
2118 # order, because most filters deal with the changelog.
2137 # order, because most filters deal with the changelog.
2119 if not opts.get('rev'):
2138 if not opts.get('rev'):
2120 revs.reverse()
2139 revs.reverse()
2121 matcher = revset.match(repo.ui, expr)
2140 matcher = revset.match(repo.ui, expr)
2122 # Revset matches can reorder revisions. "A or B" typically returns
2141 # Revset matches can reorder revisions. "A or B" typically returns
2123 # returns the revision matching A then the revision matching B. Sort
2142 # returns the revision matching A then the revision matching B. Sort
2124 # again to fix that.
2143 # again to fix that.
2125 revs = matcher(repo, revs)
2144 revs = matcher(repo, revs)
2126 if not opts.get('rev'):
2145 if not opts.get('rev'):
2127 revs.sort(reverse=True)
2146 revs.sort(reverse=True)
2128 if limit is not None:
2147 if limit is not None:
2129 limitedrevs = []
2148 limitedrevs = []
2130 for idx, r in enumerate(revs):
2149 for idx, r in enumerate(revs):
2131 if limit <= idx:
2150 if limit <= idx:
2132 break
2151 break
2133 limitedrevs.append(r)
2152 limitedrevs.append(r)
2134 revs = revset.baseset(limitedrevs)
2153 revs = revset.baseset(limitedrevs)
2135
2154
2136 return revs, expr, filematcher
2155 return revs, expr, filematcher
2137
2156
2138 def displaygraph(ui, dag, displayer, showparents, edgefn, getrenamed=None,
2157 def displaygraph(ui, dag, displayer, showparents, edgefn, getrenamed=None,
2139 filematcher=None):
2158 filematcher=None):
2140 seen, state = [], graphmod.asciistate()
2159 seen, state = [], graphmod.asciistate()
2141 for rev, type, ctx, parents in dag:
2160 for rev, type, ctx, parents in dag:
2142 char = 'o'
2161 char = 'o'
2143 if ctx.node() in showparents:
2162 if ctx.node() in showparents:
2144 char = '@'
2163 char = '@'
2145 elif ctx.obsolete():
2164 elif ctx.obsolete():
2146 char = 'x'
2165 char = 'x'
2147 elif ctx.closesbranch():
2166 elif ctx.closesbranch():
2148 char = '_'
2167 char = '_'
2149 copies = None
2168 copies = None
2150 if getrenamed and ctx.rev():
2169 if getrenamed and ctx.rev():
2151 copies = []
2170 copies = []
2152 for fn in ctx.files():
2171 for fn in ctx.files():
2153 rename = getrenamed(fn, ctx.rev())
2172 rename = getrenamed(fn, ctx.rev())
2154 if rename:
2173 if rename:
2155 copies.append((fn, rename[0]))
2174 copies.append((fn, rename[0]))
2156 revmatchfn = None
2175 revmatchfn = None
2157 if filematcher is not None:
2176 if filematcher is not None:
2158 revmatchfn = filematcher(ctx.rev())
2177 revmatchfn = filematcher(ctx.rev())
2159 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
2178 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
2160 lines = displayer.hunk.pop(rev).split('\n')
2179 lines = displayer.hunk.pop(rev).split('\n')
2161 if not lines[-1]:
2180 if not lines[-1]:
2162 del lines[-1]
2181 del lines[-1]
2163 displayer.flush(ctx)
2182 displayer.flush(ctx)
2164 edges = edgefn(type, char, lines, seen, rev, parents)
2183 edges = edgefn(type, char, lines, seen, rev, parents)
2165 for type, char, lines, coldata in edges:
2184 for type, char, lines, coldata in edges:
2166 graphmod.ascii(ui, state, type, char, lines, coldata)
2185 graphmod.ascii(ui, state, type, char, lines, coldata)
2167 displayer.close()
2186 displayer.close()
2168
2187
2169 def graphlog(ui, repo, *pats, **opts):
2188 def graphlog(ui, repo, *pats, **opts):
2170 # Parameters are identical to log command ones
2189 # Parameters are identical to log command ones
2171 revs, expr, filematcher = getgraphlogrevs(repo, pats, opts)
2190 revs, expr, filematcher = getgraphlogrevs(repo, pats, opts)
2172 revdag = graphmod.dagwalker(repo, revs)
2191 revdag = graphmod.dagwalker(repo, revs)
2173
2192
2174 getrenamed = None
2193 getrenamed = None
2175 if opts.get('copies'):
2194 if opts.get('copies'):
2176 endrev = None
2195 endrev = None
2177 if opts.get('rev'):
2196 if opts.get('rev'):
2178 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
2197 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
2179 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2198 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2180 displayer = show_changeset(ui, repo, opts, buffered=True)
2199 displayer = show_changeset(ui, repo, opts, buffered=True)
2181 showparents = [ctx.node() for ctx in repo[None].parents()]
2200 showparents = [ctx.node() for ctx in repo[None].parents()]
2182 displaygraph(ui, revdag, displayer, showparents,
2201 displaygraph(ui, revdag, displayer, showparents,
2183 graphmod.asciiedges, getrenamed, filematcher)
2202 graphmod.asciiedges, getrenamed, filematcher)
2184
2203
2185 def checkunsupportedgraphflags(pats, opts):
2204 def checkunsupportedgraphflags(pats, opts):
2186 for op in ["newest_first"]:
2205 for op in ["newest_first"]:
2187 if op in opts and opts[op]:
2206 if op in opts and opts[op]:
2188 raise util.Abort(_("-G/--graph option is incompatible with --%s")
2207 raise util.Abort(_("-G/--graph option is incompatible with --%s")
2189 % op.replace("_", "-"))
2208 % op.replace("_", "-"))
2190
2209
2191 def graphrevs(repo, nodes, opts):
2210 def graphrevs(repo, nodes, opts):
2192 limit = loglimit(opts)
2211 limit = loglimit(opts)
2193 nodes.reverse()
2212 nodes.reverse()
2194 if limit is not None:
2213 if limit is not None:
2195 nodes = nodes[:limit]
2214 nodes = nodes[:limit]
2196 return graphmod.nodes(repo, nodes)
2215 return graphmod.nodes(repo, nodes)
2197
2216
2198 def add(ui, repo, match, prefix, explicitonly, **opts):
2217 def add(ui, repo, match, prefix, explicitonly, **opts):
2199 join = lambda f: os.path.join(prefix, f)
2218 join = lambda f: os.path.join(prefix, f)
2200 bad = []
2219 bad = []
2201
2220
2202 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2221 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2203 names = []
2222 names = []
2204 wctx = repo[None]
2223 wctx = repo[None]
2205 cca = None
2224 cca = None
2206 abort, warn = scmutil.checkportabilityalert(ui)
2225 abort, warn = scmutil.checkportabilityalert(ui)
2207 if abort or warn:
2226 if abort or warn:
2208 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
2227 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
2209
2228
2210 badmatch = matchmod.badmatch(match, badfn)
2229 badmatch = matchmod.badmatch(match, badfn)
2211 dirstate = repo.dirstate
2230 dirstate = repo.dirstate
2212 # We don't want to just call wctx.walk here, since it would return a lot of
2231 # We don't want to just call wctx.walk here, since it would return a lot of
2213 # clean files, which we aren't interested in and takes time.
2232 # clean files, which we aren't interested in and takes time.
2214 for f in sorted(dirstate.walk(badmatch, sorted(wctx.substate),
2233 for f in sorted(dirstate.walk(badmatch, sorted(wctx.substate),
2215 True, False, full=False)):
2234 True, False, full=False)):
2216 exact = match.exact(f)
2235 exact = match.exact(f)
2217 if exact or not explicitonly and f not in wctx and repo.wvfs.lexists(f):
2236 if exact or not explicitonly and f not in wctx and repo.wvfs.lexists(f):
2218 if cca:
2237 if cca:
2219 cca(f)
2238 cca(f)
2220 names.append(f)
2239 names.append(f)
2221 if ui.verbose or not exact:
2240 if ui.verbose or not exact:
2222 ui.status(_('adding %s\n') % match.rel(f))
2241 ui.status(_('adding %s\n') % match.rel(f))
2223
2242
2224 for subpath in sorted(wctx.substate):
2243 for subpath in sorted(wctx.substate):
2225 sub = wctx.sub(subpath)
2244 sub = wctx.sub(subpath)
2226 try:
2245 try:
2227 submatch = matchmod.narrowmatcher(subpath, match)
2246 submatch = matchmod.narrowmatcher(subpath, match)
2228 if opts.get('subrepos'):
2247 if opts.get('subrepos'):
2229 bad.extend(sub.add(ui, submatch, prefix, False, **opts))
2248 bad.extend(sub.add(ui, submatch, prefix, False, **opts))
2230 else:
2249 else:
2231 bad.extend(sub.add(ui, submatch, prefix, True, **opts))
2250 bad.extend(sub.add(ui, submatch, prefix, True, **opts))
2232 except error.LookupError:
2251 except error.LookupError:
2233 ui.status(_("skipping missing subrepository: %s\n")
2252 ui.status(_("skipping missing subrepository: %s\n")
2234 % join(subpath))
2253 % join(subpath))
2235
2254
2236 if not opts.get('dry_run'):
2255 if not opts.get('dry_run'):
2237 rejected = wctx.add(names, prefix)
2256 rejected = wctx.add(names, prefix)
2238 bad.extend(f for f in rejected if f in match.files())
2257 bad.extend(f for f in rejected if f in match.files())
2239 return bad
2258 return bad
2240
2259
2241 def forget(ui, repo, match, prefix, explicitonly):
2260 def forget(ui, repo, match, prefix, explicitonly):
2242 join = lambda f: os.path.join(prefix, f)
2261 join = lambda f: os.path.join(prefix, f)
2243 bad = []
2262 bad = []
2244 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2263 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2245 wctx = repo[None]
2264 wctx = repo[None]
2246 forgot = []
2265 forgot = []
2247
2266
2248 s = repo.status(match=matchmod.badmatch(match, badfn), clean=True)
2267 s = repo.status(match=matchmod.badmatch(match, badfn), clean=True)
2249 forget = sorted(s[0] + s[1] + s[3] + s[6])
2268 forget = sorted(s[0] + s[1] + s[3] + s[6])
2250 if explicitonly:
2269 if explicitonly:
2251 forget = [f for f in forget if match.exact(f)]
2270 forget = [f for f in forget if match.exact(f)]
2252
2271
2253 for subpath in sorted(wctx.substate):
2272 for subpath in sorted(wctx.substate):
2254 sub = wctx.sub(subpath)
2273 sub = wctx.sub(subpath)
2255 try:
2274 try:
2256 submatch = matchmod.narrowmatcher(subpath, match)
2275 submatch = matchmod.narrowmatcher(subpath, match)
2257 subbad, subforgot = sub.forget(submatch, prefix)
2276 subbad, subforgot = sub.forget(submatch, prefix)
2258 bad.extend([subpath + '/' + f for f in subbad])
2277 bad.extend([subpath + '/' + f for f in subbad])
2259 forgot.extend([subpath + '/' + f for f in subforgot])
2278 forgot.extend([subpath + '/' + f for f in subforgot])
2260 except error.LookupError:
2279 except error.LookupError:
2261 ui.status(_("skipping missing subrepository: %s\n")
2280 ui.status(_("skipping missing subrepository: %s\n")
2262 % join(subpath))
2281 % join(subpath))
2263
2282
2264 if not explicitonly:
2283 if not explicitonly:
2265 for f in match.files():
2284 for f in match.files():
2266 if f not in repo.dirstate and not repo.wvfs.isdir(f):
2285 if f not in repo.dirstate and not repo.wvfs.isdir(f):
2267 if f not in forgot:
2286 if f not in forgot:
2268 if repo.wvfs.exists(f):
2287 if repo.wvfs.exists(f):
2269 # Don't complain if the exact case match wasn't given.
2288 # Don't complain if the exact case match wasn't given.
2270 # But don't do this until after checking 'forgot', so
2289 # But don't do this until after checking 'forgot', so
2271 # that subrepo files aren't normalized, and this op is
2290 # that subrepo files aren't normalized, and this op is
2272 # purely from data cached by the status walk above.
2291 # purely from data cached by the status walk above.
2273 if repo.dirstate.normalize(f) in repo.dirstate:
2292 if repo.dirstate.normalize(f) in repo.dirstate:
2274 continue
2293 continue
2275 ui.warn(_('not removing %s: '
2294 ui.warn(_('not removing %s: '
2276 'file is already untracked\n')
2295 'file is already untracked\n')
2277 % match.rel(f))
2296 % match.rel(f))
2278 bad.append(f)
2297 bad.append(f)
2279
2298
2280 for f in forget:
2299 for f in forget:
2281 if ui.verbose or not match.exact(f):
2300 if ui.verbose or not match.exact(f):
2282 ui.status(_('removing %s\n') % match.rel(f))
2301 ui.status(_('removing %s\n') % match.rel(f))
2283
2302
2284 rejected = wctx.forget(forget, prefix)
2303 rejected = wctx.forget(forget, prefix)
2285 bad.extend(f for f in rejected if f in match.files())
2304 bad.extend(f for f in rejected if f in match.files())
2286 forgot.extend(f for f in forget if f not in rejected)
2305 forgot.extend(f for f in forget if f not in rejected)
2287 return bad, forgot
2306 return bad, forgot
2288
2307
2289 def files(ui, ctx, m, fm, fmt, subrepos):
2308 def files(ui, ctx, m, fm, fmt, subrepos):
2290 rev = ctx.rev()
2309 rev = ctx.rev()
2291 ret = 1
2310 ret = 1
2292 ds = ctx.repo().dirstate
2311 ds = ctx.repo().dirstate
2293
2312
2294 for f in ctx.matches(m):
2313 for f in ctx.matches(m):
2295 if rev is None and ds[f] == 'r':
2314 if rev is None and ds[f] == 'r':
2296 continue
2315 continue
2297 fm.startitem()
2316 fm.startitem()
2298 if ui.verbose:
2317 if ui.verbose:
2299 fc = ctx[f]
2318 fc = ctx[f]
2300 fm.write('size flags', '% 10d % 1s ', fc.size(), fc.flags())
2319 fm.write('size flags', '% 10d % 1s ', fc.size(), fc.flags())
2301 fm.data(abspath=f)
2320 fm.data(abspath=f)
2302 fm.write('path', fmt, m.rel(f))
2321 fm.write('path', fmt, m.rel(f))
2303 ret = 0
2322 ret = 0
2304
2323
2305 for subpath in sorted(ctx.substate):
2324 for subpath in sorted(ctx.substate):
2306 def matchessubrepo(subpath):
2325 def matchessubrepo(subpath):
2307 return (m.always() or m.exact(subpath)
2326 return (m.always() or m.exact(subpath)
2308 or any(f.startswith(subpath + '/') for f in m.files()))
2327 or any(f.startswith(subpath + '/') for f in m.files()))
2309
2328
2310 if subrepos or matchessubrepo(subpath):
2329 if subrepos or matchessubrepo(subpath):
2311 sub = ctx.sub(subpath)
2330 sub = ctx.sub(subpath)
2312 try:
2331 try:
2313 submatch = matchmod.narrowmatcher(subpath, m)
2332 submatch = matchmod.narrowmatcher(subpath, m)
2314 if sub.printfiles(ui, submatch, fm, fmt, subrepos) == 0:
2333 if sub.printfiles(ui, submatch, fm, fmt, subrepos) == 0:
2315 ret = 0
2334 ret = 0
2316 except error.LookupError:
2335 except error.LookupError:
2317 ui.status(_("skipping missing subrepository: %s\n")
2336 ui.status(_("skipping missing subrepository: %s\n")
2318 % m.abs(subpath))
2337 % m.abs(subpath))
2319
2338
2320 return ret
2339 return ret
2321
2340
2322 def remove(ui, repo, m, prefix, after, force, subrepos):
2341 def remove(ui, repo, m, prefix, after, force, subrepos):
2323 join = lambda f: os.path.join(prefix, f)
2342 join = lambda f: os.path.join(prefix, f)
2324 ret = 0
2343 ret = 0
2325 s = repo.status(match=m, clean=True)
2344 s = repo.status(match=m, clean=True)
2326 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2345 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2327
2346
2328 wctx = repo[None]
2347 wctx = repo[None]
2329
2348
2330 for subpath in sorted(wctx.substate):
2349 for subpath in sorted(wctx.substate):
2331 def matchessubrepo(matcher, subpath):
2350 def matchessubrepo(matcher, subpath):
2332 if matcher.exact(subpath):
2351 if matcher.exact(subpath):
2333 return True
2352 return True
2334 for f in matcher.files():
2353 for f in matcher.files():
2335 if f.startswith(subpath):
2354 if f.startswith(subpath):
2336 return True
2355 return True
2337 return False
2356 return False
2338
2357
2339 if subrepos or matchessubrepo(m, subpath):
2358 if subrepos or matchessubrepo(m, subpath):
2340 sub = wctx.sub(subpath)
2359 sub = wctx.sub(subpath)
2341 try:
2360 try:
2342 submatch = matchmod.narrowmatcher(subpath, m)
2361 submatch = matchmod.narrowmatcher(subpath, m)
2343 if sub.removefiles(submatch, prefix, after, force, subrepos):
2362 if sub.removefiles(submatch, prefix, after, force, subrepos):
2344 ret = 1
2363 ret = 1
2345 except error.LookupError:
2364 except error.LookupError:
2346 ui.status(_("skipping missing subrepository: %s\n")
2365 ui.status(_("skipping missing subrepository: %s\n")
2347 % join(subpath))
2366 % join(subpath))
2348
2367
2349 # warn about failure to delete explicit files/dirs
2368 # warn about failure to delete explicit files/dirs
2350 deleteddirs = util.dirs(deleted)
2369 deleteddirs = util.dirs(deleted)
2351 for f in m.files():
2370 for f in m.files():
2352 def insubrepo():
2371 def insubrepo():
2353 for subpath in wctx.substate:
2372 for subpath in wctx.substate:
2354 if f.startswith(subpath):
2373 if f.startswith(subpath):
2355 return True
2374 return True
2356 return False
2375 return False
2357
2376
2358 isdir = f in deleteddirs or wctx.hasdir(f)
2377 isdir = f in deleteddirs or wctx.hasdir(f)
2359 if f in repo.dirstate or isdir or f == '.' or insubrepo():
2378 if f in repo.dirstate or isdir or f == '.' or insubrepo():
2360 continue
2379 continue
2361
2380
2362 if repo.wvfs.exists(f):
2381 if repo.wvfs.exists(f):
2363 if repo.wvfs.isdir(f):
2382 if repo.wvfs.isdir(f):
2364 ui.warn(_('not removing %s: no tracked files\n')
2383 ui.warn(_('not removing %s: no tracked files\n')
2365 % m.rel(f))
2384 % m.rel(f))
2366 else:
2385 else:
2367 ui.warn(_('not removing %s: file is untracked\n')
2386 ui.warn(_('not removing %s: file is untracked\n')
2368 % m.rel(f))
2387 % m.rel(f))
2369 # missing files will generate a warning elsewhere
2388 # missing files will generate a warning elsewhere
2370 ret = 1
2389 ret = 1
2371
2390
2372 if force:
2391 if force:
2373 list = modified + deleted + clean + added
2392 list = modified + deleted + clean + added
2374 elif after:
2393 elif after:
2375 list = deleted
2394 list = deleted
2376 for f in modified + added + clean:
2395 for f in modified + added + clean:
2377 ui.warn(_('not removing %s: file still exists\n') % m.rel(f))
2396 ui.warn(_('not removing %s: file still exists\n') % m.rel(f))
2378 ret = 1
2397 ret = 1
2379 else:
2398 else:
2380 list = deleted + clean
2399 list = deleted + clean
2381 for f in modified:
2400 for f in modified:
2382 ui.warn(_('not removing %s: file is modified (use -f'
2401 ui.warn(_('not removing %s: file is modified (use -f'
2383 ' to force removal)\n') % m.rel(f))
2402 ' to force removal)\n') % m.rel(f))
2384 ret = 1
2403 ret = 1
2385 for f in added:
2404 for f in added:
2386 ui.warn(_('not removing %s: file has been marked for add'
2405 ui.warn(_('not removing %s: file has been marked for add'
2387 ' (use forget to undo)\n') % m.rel(f))
2406 ' (use forget to undo)\n') % m.rel(f))
2388 ret = 1
2407 ret = 1
2389
2408
2390 for f in sorted(list):
2409 for f in sorted(list):
2391 if ui.verbose or not m.exact(f):
2410 if ui.verbose or not m.exact(f):
2392 ui.status(_('removing %s\n') % m.rel(f))
2411 ui.status(_('removing %s\n') % m.rel(f))
2393
2412
2394 wlock = repo.wlock()
2413 wlock = repo.wlock()
2395 try:
2414 try:
2396 if not after:
2415 if not after:
2397 for f in list:
2416 for f in list:
2398 if f in added:
2417 if f in added:
2399 continue # we never unlink added files on remove
2418 continue # we never unlink added files on remove
2400 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
2419 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
2401 repo[None].forget(list)
2420 repo[None].forget(list)
2402 finally:
2421 finally:
2403 wlock.release()
2422 wlock.release()
2404
2423
2405 return ret
2424 return ret
2406
2425
2407 def cat(ui, repo, ctx, matcher, prefix, **opts):
2426 def cat(ui, repo, ctx, matcher, prefix, **opts):
2408 err = 1
2427 err = 1
2409
2428
2410 def write(path):
2429 def write(path):
2411 fp = makefileobj(repo, opts.get('output'), ctx.node(),
2430 fp = makefileobj(repo, opts.get('output'), ctx.node(),
2412 pathname=os.path.join(prefix, path))
2431 pathname=os.path.join(prefix, path))
2413 data = ctx[path].data()
2432 data = ctx[path].data()
2414 if opts.get('decode'):
2433 if opts.get('decode'):
2415 data = repo.wwritedata(path, data)
2434 data = repo.wwritedata(path, data)
2416 fp.write(data)
2435 fp.write(data)
2417 fp.close()
2436 fp.close()
2418
2437
2419 # Automation often uses hg cat on single files, so special case it
2438 # Automation often uses hg cat on single files, so special case it
2420 # for performance to avoid the cost of parsing the manifest.
2439 # for performance to avoid the cost of parsing the manifest.
2421 if len(matcher.files()) == 1 and not matcher.anypats():
2440 if len(matcher.files()) == 1 and not matcher.anypats():
2422 file = matcher.files()[0]
2441 file = matcher.files()[0]
2423 mf = repo.manifest
2442 mf = repo.manifest
2424 mfnode = ctx.manifestnode()
2443 mfnode = ctx.manifestnode()
2425 if mfnode and mf.find(mfnode, file)[0]:
2444 if mfnode and mf.find(mfnode, file)[0]:
2426 write(file)
2445 write(file)
2427 return 0
2446 return 0
2428
2447
2429 # Don't warn about "missing" files that are really in subrepos
2448 # Don't warn about "missing" files that are really in subrepos
2430 def badfn(path, msg):
2449 def badfn(path, msg):
2431 for subpath in ctx.substate:
2450 for subpath in ctx.substate:
2432 if path.startswith(subpath):
2451 if path.startswith(subpath):
2433 return
2452 return
2434 matcher.bad(path, msg)
2453 matcher.bad(path, msg)
2435
2454
2436 for abs in ctx.walk(matchmod.badmatch(matcher, badfn)):
2455 for abs in ctx.walk(matchmod.badmatch(matcher, badfn)):
2437 write(abs)
2456 write(abs)
2438 err = 0
2457 err = 0
2439
2458
2440 for subpath in sorted(ctx.substate):
2459 for subpath in sorted(ctx.substate):
2441 sub = ctx.sub(subpath)
2460 sub = ctx.sub(subpath)
2442 try:
2461 try:
2443 submatch = matchmod.narrowmatcher(subpath, matcher)
2462 submatch = matchmod.narrowmatcher(subpath, matcher)
2444
2463
2445 if not sub.cat(submatch, os.path.join(prefix, sub._path),
2464 if not sub.cat(submatch, os.path.join(prefix, sub._path),
2446 **opts):
2465 **opts):
2447 err = 0
2466 err = 0
2448 except error.RepoLookupError:
2467 except error.RepoLookupError:
2449 ui.status(_("skipping missing subrepository: %s\n")
2468 ui.status(_("skipping missing subrepository: %s\n")
2450 % os.path.join(prefix, subpath))
2469 % os.path.join(prefix, subpath))
2451
2470
2452 return err
2471 return err
2453
2472
2454 def commit(ui, repo, commitfunc, pats, opts):
2473 def commit(ui, repo, commitfunc, pats, opts):
2455 '''commit the specified files or all outstanding changes'''
2474 '''commit the specified files or all outstanding changes'''
2456 date = opts.get('date')
2475 date = opts.get('date')
2457 if date:
2476 if date:
2458 opts['date'] = util.parsedate(date)
2477 opts['date'] = util.parsedate(date)
2459 message = logmessage(ui, opts)
2478 message = logmessage(ui, opts)
2460 matcher = scmutil.match(repo[None], pats, opts)
2479 matcher = scmutil.match(repo[None], pats, opts)
2461
2480
2462 # extract addremove carefully -- this function can be called from a command
2481 # extract addremove carefully -- this function can be called from a command
2463 # that doesn't support addremove
2482 # that doesn't support addremove
2464 if opts.get('addremove'):
2483 if opts.get('addremove'):
2465 if scmutil.addremove(repo, matcher, "", opts) != 0:
2484 if scmutil.addremove(repo, matcher, "", opts) != 0:
2466 raise util.Abort(
2485 raise util.Abort(
2467 _("failed to mark all new/missing files as added/removed"))
2486 _("failed to mark all new/missing files as added/removed"))
2468
2487
2469 return commitfunc(ui, repo, message, matcher, opts)
2488 return commitfunc(ui, repo, message, matcher, opts)
2470
2489
2471 def amend(ui, repo, commitfunc, old, extra, pats, opts):
2490 def amend(ui, repo, commitfunc, old, extra, pats, opts):
2472 # avoid cycle context -> subrepo -> cmdutil
2491 # avoid cycle context -> subrepo -> cmdutil
2473 import context
2492 import context
2474
2493
2475 # amend will reuse the existing user if not specified, but the obsolete
2494 # amend will reuse the existing user if not specified, but the obsolete
2476 # marker creation requires that the current user's name is specified.
2495 # marker creation requires that the current user's name is specified.
2477 if obsolete.isenabled(repo, obsolete.createmarkersopt):
2496 if obsolete.isenabled(repo, obsolete.createmarkersopt):
2478 ui.username() # raise exception if username not set
2497 ui.username() # raise exception if username not set
2479
2498
2480 ui.note(_('amending changeset %s\n') % old)
2499 ui.note(_('amending changeset %s\n') % old)
2481 base = old.p1()
2500 base = old.p1()
2482 createmarkers = obsolete.isenabled(repo, obsolete.createmarkersopt)
2501 createmarkers = obsolete.isenabled(repo, obsolete.createmarkersopt)
2483
2502
2484 wlock = dsguard = lock = newid = None
2503 wlock = dsguard = lock = newid = None
2485 try:
2504 try:
2486 wlock = repo.wlock()
2505 wlock = repo.wlock()
2487 dsguard = dirstateguard(repo, 'amend')
2506 dsguard = dirstateguard(repo, 'amend')
2488 lock = repo.lock()
2507 lock = repo.lock()
2489 tr = repo.transaction('amend')
2508 tr = repo.transaction('amend')
2490 try:
2509 try:
2491 # See if we got a message from -m or -l, if not, open the editor
2510 # See if we got a message from -m or -l, if not, open the editor
2492 # with the message of the changeset to amend
2511 # with the message of the changeset to amend
2493 message = logmessage(ui, opts)
2512 message = logmessage(ui, opts)
2494 # ensure logfile does not conflict with later enforcement of the
2513 # ensure logfile does not conflict with later enforcement of the
2495 # message. potential logfile content has been processed by
2514 # message. potential logfile content has been processed by
2496 # `logmessage` anyway.
2515 # `logmessage` anyway.
2497 opts.pop('logfile')
2516 opts.pop('logfile')
2498 # First, do a regular commit to record all changes in the working
2517 # First, do a regular commit to record all changes in the working
2499 # directory (if there are any)
2518 # directory (if there are any)
2500 ui.callhooks = False
2519 ui.callhooks = False
2501 activebookmark = repo._activebookmark
2520 activebookmark = repo._activebookmark
2502 try:
2521 try:
2503 repo._activebookmark = None
2522 repo._activebookmark = None
2504 opts['message'] = 'temporary amend commit for %s' % old
2523 opts['message'] = 'temporary amend commit for %s' % old
2505 node = commit(ui, repo, commitfunc, pats, opts)
2524 node = commit(ui, repo, commitfunc, pats, opts)
2506 finally:
2525 finally:
2507 repo._activebookmark = activebookmark
2526 repo._activebookmark = activebookmark
2508 ui.callhooks = True
2527 ui.callhooks = True
2509 ctx = repo[node]
2528 ctx = repo[node]
2510
2529
2511 # Participating changesets:
2530 # Participating changesets:
2512 #
2531 #
2513 # node/ctx o - new (intermediate) commit that contains changes
2532 # node/ctx o - new (intermediate) commit that contains changes
2514 # | from working dir to go into amending commit
2533 # | from working dir to go into amending commit
2515 # | (or a workingctx if there were no changes)
2534 # | (or a workingctx if there were no changes)
2516 # |
2535 # |
2517 # old o - changeset to amend
2536 # old o - changeset to amend
2518 # |
2537 # |
2519 # base o - parent of amending changeset
2538 # base o - parent of amending changeset
2520
2539
2521 # Update extra dict from amended commit (e.g. to preserve graft
2540 # Update extra dict from amended commit (e.g. to preserve graft
2522 # source)
2541 # source)
2523 extra.update(old.extra())
2542 extra.update(old.extra())
2524
2543
2525 # Also update it from the intermediate commit or from the wctx
2544 # Also update it from the intermediate commit or from the wctx
2526 extra.update(ctx.extra())
2545 extra.update(ctx.extra())
2527
2546
2528 if len(old.parents()) > 1:
2547 if len(old.parents()) > 1:
2529 # ctx.files() isn't reliable for merges, so fall back to the
2548 # ctx.files() isn't reliable for merges, so fall back to the
2530 # slower repo.status() method
2549 # slower repo.status() method
2531 files = set([fn for st in repo.status(base, old)[:3]
2550 files = set([fn for st in repo.status(base, old)[:3]
2532 for fn in st])
2551 for fn in st])
2533 else:
2552 else:
2534 files = set(old.files())
2553 files = set(old.files())
2535
2554
2536 # Second, we use either the commit we just did, or if there were no
2555 # Second, we use either the commit we just did, or if there were no
2537 # changes the parent of the working directory as the version of the
2556 # changes the parent of the working directory as the version of the
2538 # files in the final amend commit
2557 # files in the final amend commit
2539 if node:
2558 if node:
2540 ui.note(_('copying changeset %s to %s\n') % (ctx, base))
2559 ui.note(_('copying changeset %s to %s\n') % (ctx, base))
2541
2560
2542 user = ctx.user()
2561 user = ctx.user()
2543 date = ctx.date()
2562 date = ctx.date()
2544 # Recompute copies (avoid recording a -> b -> a)
2563 # Recompute copies (avoid recording a -> b -> a)
2545 copied = copies.pathcopies(base, ctx)
2564 copied = copies.pathcopies(base, ctx)
2546 if old.p2:
2565 if old.p2:
2547 copied.update(copies.pathcopies(old.p2(), ctx))
2566 copied.update(copies.pathcopies(old.p2(), ctx))
2548
2567
2549 # Prune files which were reverted by the updates: if old
2568 # Prune files which were reverted by the updates: if old
2550 # introduced file X and our intermediate commit, node,
2569 # introduced file X and our intermediate commit, node,
2551 # renamed that file, then those two files are the same and
2570 # renamed that file, then those two files are the same and
2552 # we can discard X from our list of files. Likewise if X
2571 # we can discard X from our list of files. Likewise if X
2553 # was deleted, it's no longer relevant
2572 # was deleted, it's no longer relevant
2554 files.update(ctx.files())
2573 files.update(ctx.files())
2555
2574
2556 def samefile(f):
2575 def samefile(f):
2557 if f in ctx.manifest():
2576 if f in ctx.manifest():
2558 a = ctx.filectx(f)
2577 a = ctx.filectx(f)
2559 if f in base.manifest():
2578 if f in base.manifest():
2560 b = base.filectx(f)
2579 b = base.filectx(f)
2561 return (not a.cmp(b)
2580 return (not a.cmp(b)
2562 and a.flags() == b.flags())
2581 and a.flags() == b.flags())
2563 else:
2582 else:
2564 return False
2583 return False
2565 else:
2584 else:
2566 return f not in base.manifest()
2585 return f not in base.manifest()
2567 files = [f for f in files if not samefile(f)]
2586 files = [f for f in files if not samefile(f)]
2568
2587
2569 def filectxfn(repo, ctx_, path):
2588 def filectxfn(repo, ctx_, path):
2570 try:
2589 try:
2571 fctx = ctx[path]
2590 fctx = ctx[path]
2572 flags = fctx.flags()
2591 flags = fctx.flags()
2573 mctx = context.memfilectx(repo,
2592 mctx = context.memfilectx(repo,
2574 fctx.path(), fctx.data(),
2593 fctx.path(), fctx.data(),
2575 islink='l' in flags,
2594 islink='l' in flags,
2576 isexec='x' in flags,
2595 isexec='x' in flags,
2577 copied=copied.get(path))
2596 copied=copied.get(path))
2578 return mctx
2597 return mctx
2579 except KeyError:
2598 except KeyError:
2580 return None
2599 return None
2581 else:
2600 else:
2582 ui.note(_('copying changeset %s to %s\n') % (old, base))
2601 ui.note(_('copying changeset %s to %s\n') % (old, base))
2583
2602
2584 # Use version of files as in the old cset
2603 # Use version of files as in the old cset
2585 def filectxfn(repo, ctx_, path):
2604 def filectxfn(repo, ctx_, path):
2586 try:
2605 try:
2587 return old.filectx(path)
2606 return old.filectx(path)
2588 except KeyError:
2607 except KeyError:
2589 return None
2608 return None
2590
2609
2591 user = opts.get('user') or old.user()
2610 user = opts.get('user') or old.user()
2592 date = opts.get('date') or old.date()
2611 date = opts.get('date') or old.date()
2593 editform = mergeeditform(old, 'commit.amend')
2612 editform = mergeeditform(old, 'commit.amend')
2594 editor = getcommiteditor(editform=editform, **opts)
2613 editor = getcommiteditor(editform=editform, **opts)
2595 if not message:
2614 if not message:
2596 editor = getcommiteditor(edit=True, editform=editform)
2615 editor = getcommiteditor(edit=True, editform=editform)
2597 message = old.description()
2616 message = old.description()
2598
2617
2599 pureextra = extra.copy()
2618 pureextra = extra.copy()
2600 extra['amend_source'] = old.hex()
2619 extra['amend_source'] = old.hex()
2601
2620
2602 new = context.memctx(repo,
2621 new = context.memctx(repo,
2603 parents=[base.node(), old.p2().node()],
2622 parents=[base.node(), old.p2().node()],
2604 text=message,
2623 text=message,
2605 files=files,
2624 files=files,
2606 filectxfn=filectxfn,
2625 filectxfn=filectxfn,
2607 user=user,
2626 user=user,
2608 date=date,
2627 date=date,
2609 extra=extra,
2628 extra=extra,
2610 editor=editor)
2629 editor=editor)
2611
2630
2612 newdesc = changelog.stripdesc(new.description())
2631 newdesc = changelog.stripdesc(new.description())
2613 if ((not node)
2632 if ((not node)
2614 and newdesc == old.description()
2633 and newdesc == old.description()
2615 and user == old.user()
2634 and user == old.user()
2616 and date == old.date()
2635 and date == old.date()
2617 and pureextra == old.extra()):
2636 and pureextra == old.extra()):
2618 # nothing changed. continuing here would create a new node
2637 # nothing changed. continuing here would create a new node
2619 # anyway because of the amend_source noise.
2638 # anyway because of the amend_source noise.
2620 #
2639 #
2621 # This not what we expect from amend.
2640 # This not what we expect from amend.
2622 return old.node()
2641 return old.node()
2623
2642
2624 ph = repo.ui.config('phases', 'new-commit', phases.draft)
2643 ph = repo.ui.config('phases', 'new-commit', phases.draft)
2625 try:
2644 try:
2626 if opts.get('secret'):
2645 if opts.get('secret'):
2627 commitphase = 'secret'
2646 commitphase = 'secret'
2628 else:
2647 else:
2629 commitphase = old.phase()
2648 commitphase = old.phase()
2630 repo.ui.setconfig('phases', 'new-commit', commitphase, 'amend')
2649 repo.ui.setconfig('phases', 'new-commit', commitphase, 'amend')
2631 newid = repo.commitctx(new)
2650 newid = repo.commitctx(new)
2632 finally:
2651 finally:
2633 repo.ui.setconfig('phases', 'new-commit', ph, 'amend')
2652 repo.ui.setconfig('phases', 'new-commit', ph, 'amend')
2634 if newid != old.node():
2653 if newid != old.node():
2635 # Reroute the working copy parent to the new changeset
2654 # Reroute the working copy parent to the new changeset
2636 repo.setparents(newid, nullid)
2655 repo.setparents(newid, nullid)
2637
2656
2638 # Move bookmarks from old parent to amend commit
2657 # Move bookmarks from old parent to amend commit
2639 bms = repo.nodebookmarks(old.node())
2658 bms = repo.nodebookmarks(old.node())
2640 if bms:
2659 if bms:
2641 marks = repo._bookmarks
2660 marks = repo._bookmarks
2642 for bm in bms:
2661 for bm in bms:
2643 ui.debug('moving bookmarks %r from %s to %s\n' %
2662 ui.debug('moving bookmarks %r from %s to %s\n' %
2644 (marks, old.hex(), hex(newid)))
2663 (marks, old.hex(), hex(newid)))
2645 marks[bm] = newid
2664 marks[bm] = newid
2646 marks.recordchange(tr)
2665 marks.recordchange(tr)
2647 #commit the whole amend process
2666 #commit the whole amend process
2648 if createmarkers:
2667 if createmarkers:
2649 # mark the new changeset as successor of the rewritten one
2668 # mark the new changeset as successor of the rewritten one
2650 new = repo[newid]
2669 new = repo[newid]
2651 obs = [(old, (new,))]
2670 obs = [(old, (new,))]
2652 if node:
2671 if node:
2653 obs.append((ctx, ()))
2672 obs.append((ctx, ()))
2654
2673
2655 obsolete.createmarkers(repo, obs)
2674 obsolete.createmarkers(repo, obs)
2656 tr.close()
2675 tr.close()
2657 finally:
2676 finally:
2658 tr.release()
2677 tr.release()
2659 dsguard.close()
2678 dsguard.close()
2660 if not createmarkers and newid != old.node():
2679 if not createmarkers and newid != old.node():
2661 # Strip the intermediate commit (if there was one) and the amended
2680 # Strip the intermediate commit (if there was one) and the amended
2662 # commit
2681 # commit
2663 if node:
2682 if node:
2664 ui.note(_('stripping intermediate changeset %s\n') % ctx)
2683 ui.note(_('stripping intermediate changeset %s\n') % ctx)
2665 ui.note(_('stripping amended changeset %s\n') % old)
2684 ui.note(_('stripping amended changeset %s\n') % old)
2666 repair.strip(ui, repo, old.node(), topic='amend-backup')
2685 repair.strip(ui, repo, old.node(), topic='amend-backup')
2667 finally:
2686 finally:
2668 lockmod.release(lock, dsguard, wlock)
2687 lockmod.release(lock, dsguard, wlock)
2669 return newid
2688 return newid
2670
2689
2671 def commiteditor(repo, ctx, subs, editform=''):
2690 def commiteditor(repo, ctx, subs, editform=''):
2672 if ctx.description():
2691 if ctx.description():
2673 return ctx.description()
2692 return ctx.description()
2674 return commitforceeditor(repo, ctx, subs, editform=editform)
2693 return commitforceeditor(repo, ctx, subs, editform=editform)
2675
2694
2676 def commitforceeditor(repo, ctx, subs, finishdesc=None, extramsg=None,
2695 def commitforceeditor(repo, ctx, subs, finishdesc=None, extramsg=None,
2677 editform=''):
2696 editform=''):
2678 if not extramsg:
2697 if not extramsg:
2679 extramsg = _("Leave message empty to abort commit.")
2698 extramsg = _("Leave message empty to abort commit.")
2680
2699
2681 forms = [e for e in editform.split('.') if e]
2700 forms = [e for e in editform.split('.') if e]
2682 forms.insert(0, 'changeset')
2701 forms.insert(0, 'changeset')
2683 while forms:
2702 while forms:
2684 tmpl = repo.ui.config('committemplate', '.'.join(forms))
2703 tmpl = repo.ui.config('committemplate', '.'.join(forms))
2685 if tmpl:
2704 if tmpl:
2686 committext = buildcommittemplate(repo, ctx, subs, extramsg, tmpl)
2705 committext = buildcommittemplate(repo, ctx, subs, extramsg, tmpl)
2687 break
2706 break
2688 forms.pop()
2707 forms.pop()
2689 else:
2708 else:
2690 committext = buildcommittext(repo, ctx, subs, extramsg)
2709 committext = buildcommittext(repo, ctx, subs, extramsg)
2691
2710
2692 # run editor in the repository root
2711 # run editor in the repository root
2693 olddir = os.getcwd()
2712 olddir = os.getcwd()
2694 os.chdir(repo.root)
2713 os.chdir(repo.root)
2695 text = repo.ui.edit(committext, ctx.user(), ctx.extra(), editform=editform)
2714 text = repo.ui.edit(committext, ctx.user(), ctx.extra(), editform=editform)
2696 text = re.sub("(?m)^HG:.*(\n|$)", "", text)
2715 text = re.sub("(?m)^HG:.*(\n|$)", "", text)
2697 os.chdir(olddir)
2716 os.chdir(olddir)
2698
2717
2699 if finishdesc:
2718 if finishdesc:
2700 text = finishdesc(text)
2719 text = finishdesc(text)
2701 if not text.strip():
2720 if not text.strip():
2702 raise util.Abort(_("empty commit message"))
2721 raise util.Abort(_("empty commit message"))
2703
2722
2704 return text
2723 return text
2705
2724
2706 def buildcommittemplate(repo, ctx, subs, extramsg, tmpl):
2725 def buildcommittemplate(repo, ctx, subs, extramsg, tmpl):
2707 ui = repo.ui
2726 ui = repo.ui
2708 tmpl, mapfile = gettemplate(ui, tmpl, None)
2727 tmpl, mapfile = gettemplate(ui, tmpl, None)
2709
2728
2710 try:
2729 try:
2711 t = changeset_templater(ui, repo, None, {}, tmpl, mapfile, False)
2730 t = changeset_templater(ui, repo, None, {}, tmpl, mapfile, False)
2712 except SyntaxError as inst:
2731 except SyntaxError as inst:
2713 raise util.Abort(inst.args[0])
2732 raise util.Abort(inst.args[0])
2714
2733
2715 for k, v in repo.ui.configitems('committemplate'):
2734 for k, v in repo.ui.configitems('committemplate'):
2716 if k != 'changeset':
2735 if k != 'changeset':
2717 t.t.cache[k] = v
2736 t.t.cache[k] = v
2718
2737
2719 if not extramsg:
2738 if not extramsg:
2720 extramsg = '' # ensure that extramsg is string
2739 extramsg = '' # ensure that extramsg is string
2721
2740
2722 ui.pushbuffer()
2741 ui.pushbuffer()
2723 t.show(ctx, extramsg=extramsg)
2742 t.show(ctx, extramsg=extramsg)
2724 return ui.popbuffer()
2743 return ui.popbuffer()
2725
2744
2726 def hgprefix(msg):
2745 def hgprefix(msg):
2727 return "\n".join(["HG: %s" % a for a in msg.split("\n") if a])
2746 return "\n".join(["HG: %s" % a for a in msg.split("\n") if a])
2728
2747
2729 def buildcommittext(repo, ctx, subs, extramsg):
2748 def buildcommittext(repo, ctx, subs, extramsg):
2730 edittext = []
2749 edittext = []
2731 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
2750 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
2732 if ctx.description():
2751 if ctx.description():
2733 edittext.append(ctx.description())
2752 edittext.append(ctx.description())
2734 edittext.append("")
2753 edittext.append("")
2735 edittext.append("") # Empty line between message and comments.
2754 edittext.append("") # Empty line between message and comments.
2736 edittext.append(hgprefix(_("Enter commit message."
2755 edittext.append(hgprefix(_("Enter commit message."
2737 " Lines beginning with 'HG:' are removed.")))
2756 " Lines beginning with 'HG:' are removed.")))
2738 edittext.append(hgprefix(extramsg))
2757 edittext.append(hgprefix(extramsg))
2739 edittext.append("HG: --")
2758 edittext.append("HG: --")
2740 edittext.append(hgprefix(_("user: %s") % ctx.user()))
2759 edittext.append(hgprefix(_("user: %s") % ctx.user()))
2741 if ctx.p2():
2760 if ctx.p2():
2742 edittext.append(hgprefix(_("branch merge")))
2761 edittext.append(hgprefix(_("branch merge")))
2743 if ctx.branch():
2762 if ctx.branch():
2744 edittext.append(hgprefix(_("branch '%s'") % ctx.branch()))
2763 edittext.append(hgprefix(_("branch '%s'") % ctx.branch()))
2745 if bookmarks.isactivewdirparent(repo):
2764 if bookmarks.isactivewdirparent(repo):
2746 edittext.append(hgprefix(_("bookmark '%s'") % repo._activebookmark))
2765 edittext.append(hgprefix(_("bookmark '%s'") % repo._activebookmark))
2747 edittext.extend([hgprefix(_("subrepo %s") % s) for s in subs])
2766 edittext.extend([hgprefix(_("subrepo %s") % s) for s in subs])
2748 edittext.extend([hgprefix(_("added %s") % f) for f in added])
2767 edittext.extend([hgprefix(_("added %s") % f) for f in added])
2749 edittext.extend([hgprefix(_("changed %s") % f) for f in modified])
2768 edittext.extend([hgprefix(_("changed %s") % f) for f in modified])
2750 edittext.extend([hgprefix(_("removed %s") % f) for f in removed])
2769 edittext.extend([hgprefix(_("removed %s") % f) for f in removed])
2751 if not added and not modified and not removed:
2770 if not added and not modified and not removed:
2752 edittext.append(hgprefix(_("no files changed")))
2771 edittext.append(hgprefix(_("no files changed")))
2753 edittext.append("")
2772 edittext.append("")
2754
2773
2755 return "\n".join(edittext)
2774 return "\n".join(edittext)
2756
2775
2757 def commitstatus(repo, node, branch, bheads=None, opts=None):
2776 def commitstatus(repo, node, branch, bheads=None, opts=None):
2758 if opts is None:
2777 if opts is None:
2759 opts = {}
2778 opts = {}
2760 ctx = repo[node]
2779 ctx = repo[node]
2761 parents = ctx.parents()
2780 parents = ctx.parents()
2762
2781
2763 if (not opts.get('amend') and bheads and node not in bheads and not
2782 if (not opts.get('amend') and bheads and node not in bheads and not
2764 [x for x in parents if x.node() in bheads and x.branch() == branch]):
2783 [x for x in parents if x.node() in bheads and x.branch() == branch]):
2765 repo.ui.status(_('created new head\n'))
2784 repo.ui.status(_('created new head\n'))
2766 # The message is not printed for initial roots. For the other
2785 # The message is not printed for initial roots. For the other
2767 # changesets, it is printed in the following situations:
2786 # changesets, it is printed in the following situations:
2768 #
2787 #
2769 # Par column: for the 2 parents with ...
2788 # Par column: for the 2 parents with ...
2770 # N: null or no parent
2789 # N: null or no parent
2771 # B: parent is on another named branch
2790 # B: parent is on another named branch
2772 # C: parent is a regular non head changeset
2791 # C: parent is a regular non head changeset
2773 # H: parent was a branch head of the current branch
2792 # H: parent was a branch head of the current branch
2774 # Msg column: whether we print "created new head" message
2793 # Msg column: whether we print "created new head" message
2775 # In the following, it is assumed that there already exists some
2794 # In the following, it is assumed that there already exists some
2776 # initial branch heads of the current branch, otherwise nothing is
2795 # initial branch heads of the current branch, otherwise nothing is
2777 # printed anyway.
2796 # printed anyway.
2778 #
2797 #
2779 # Par Msg Comment
2798 # Par Msg Comment
2780 # N N y additional topo root
2799 # N N y additional topo root
2781 #
2800 #
2782 # B N y additional branch root
2801 # B N y additional branch root
2783 # C N y additional topo head
2802 # C N y additional topo head
2784 # H N n usual case
2803 # H N n usual case
2785 #
2804 #
2786 # B B y weird additional branch root
2805 # B B y weird additional branch root
2787 # C B y branch merge
2806 # C B y branch merge
2788 # H B n merge with named branch
2807 # H B n merge with named branch
2789 #
2808 #
2790 # C C y additional head from merge
2809 # C C y additional head from merge
2791 # C H n merge with a head
2810 # C H n merge with a head
2792 #
2811 #
2793 # H H n head merge: head count decreases
2812 # H H n head merge: head count decreases
2794
2813
2795 if not opts.get('close_branch'):
2814 if not opts.get('close_branch'):
2796 for r in parents:
2815 for r in parents:
2797 if r.closesbranch() and r.branch() == branch:
2816 if r.closesbranch() and r.branch() == branch:
2798 repo.ui.status(_('reopening closed branch head %d\n') % r)
2817 repo.ui.status(_('reopening closed branch head %d\n') % r)
2799
2818
2800 if repo.ui.debugflag:
2819 if repo.ui.debugflag:
2801 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
2820 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
2802 elif repo.ui.verbose:
2821 elif repo.ui.verbose:
2803 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
2822 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
2804
2823
2805 def revert(ui, repo, ctx, parents, *pats, **opts):
2824 def revert(ui, repo, ctx, parents, *pats, **opts):
2806 parent, p2 = parents
2825 parent, p2 = parents
2807 node = ctx.node()
2826 node = ctx.node()
2808
2827
2809 mf = ctx.manifest()
2828 mf = ctx.manifest()
2810 if node == p2:
2829 if node == p2:
2811 parent = p2
2830 parent = p2
2812 if node == parent:
2831 if node == parent:
2813 pmf = mf
2832 pmf = mf
2814 else:
2833 else:
2815 pmf = None
2834 pmf = None
2816
2835
2817 # need all matching names in dirstate and manifest of target rev,
2836 # need all matching names in dirstate and manifest of target rev,
2818 # so have to walk both. do not print errors if files exist in one
2837 # so have to walk both. do not print errors if files exist in one
2819 # but not other. in both cases, filesets should be evaluated against
2838 # but not other. in both cases, filesets should be evaluated against
2820 # workingctx to get consistent result (issue4497). this means 'set:**'
2839 # workingctx to get consistent result (issue4497). this means 'set:**'
2821 # cannot be used to select missing files from target rev.
2840 # cannot be used to select missing files from target rev.
2822
2841
2823 # `names` is a mapping for all elements in working copy and target revision
2842 # `names` is a mapping for all elements in working copy and target revision
2824 # The mapping is in the form:
2843 # The mapping is in the form:
2825 # <asb path in repo> -> (<path from CWD>, <exactly specified by matcher?>)
2844 # <asb path in repo> -> (<path from CWD>, <exactly specified by matcher?>)
2826 names = {}
2845 names = {}
2827
2846
2828 wlock = repo.wlock()
2847 wlock = repo.wlock()
2829 try:
2848 try:
2830 ## filling of the `names` mapping
2849 ## filling of the `names` mapping
2831 # walk dirstate to fill `names`
2850 # walk dirstate to fill `names`
2832
2851
2833 interactive = opts.get('interactive', False)
2852 interactive = opts.get('interactive', False)
2834 wctx = repo[None]
2853 wctx = repo[None]
2835 m = scmutil.match(wctx, pats, opts)
2854 m = scmutil.match(wctx, pats, opts)
2836
2855
2837 # we'll need this later
2856 # we'll need this later
2838 targetsubs = sorted(s for s in wctx.substate if m(s))
2857 targetsubs = sorted(s for s in wctx.substate if m(s))
2839
2858
2840 if not m.always():
2859 if not m.always():
2841 for abs in repo.walk(matchmod.badmatch(m, lambda x, y: False)):
2860 for abs in repo.walk(matchmod.badmatch(m, lambda x, y: False)):
2842 names[abs] = m.rel(abs), m.exact(abs)
2861 names[abs] = m.rel(abs), m.exact(abs)
2843
2862
2844 # walk target manifest to fill `names`
2863 # walk target manifest to fill `names`
2845
2864
2846 def badfn(path, msg):
2865 def badfn(path, msg):
2847 if path in names:
2866 if path in names:
2848 return
2867 return
2849 if path in ctx.substate:
2868 if path in ctx.substate:
2850 return
2869 return
2851 path_ = path + '/'
2870 path_ = path + '/'
2852 for f in names:
2871 for f in names:
2853 if f.startswith(path_):
2872 if f.startswith(path_):
2854 return
2873 return
2855 ui.warn("%s: %s\n" % (m.rel(path), msg))
2874 ui.warn("%s: %s\n" % (m.rel(path), msg))
2856
2875
2857 for abs in ctx.walk(matchmod.badmatch(m, badfn)):
2876 for abs in ctx.walk(matchmod.badmatch(m, badfn)):
2858 if abs not in names:
2877 if abs not in names:
2859 names[abs] = m.rel(abs), m.exact(abs)
2878 names[abs] = m.rel(abs), m.exact(abs)
2860
2879
2861 # Find status of all file in `names`.
2880 # Find status of all file in `names`.
2862 m = scmutil.matchfiles(repo, names)
2881 m = scmutil.matchfiles(repo, names)
2863
2882
2864 changes = repo.status(node1=node, match=m,
2883 changes = repo.status(node1=node, match=m,
2865 unknown=True, ignored=True, clean=True)
2884 unknown=True, ignored=True, clean=True)
2866 else:
2885 else:
2867 changes = repo.status(node1=node, match=m)
2886 changes = repo.status(node1=node, match=m)
2868 for kind in changes:
2887 for kind in changes:
2869 for abs in kind:
2888 for abs in kind:
2870 names[abs] = m.rel(abs), m.exact(abs)
2889 names[abs] = m.rel(abs), m.exact(abs)
2871
2890
2872 m = scmutil.matchfiles(repo, names)
2891 m = scmutil.matchfiles(repo, names)
2873
2892
2874 modified = set(changes.modified)
2893 modified = set(changes.modified)
2875 added = set(changes.added)
2894 added = set(changes.added)
2876 removed = set(changes.removed)
2895 removed = set(changes.removed)
2877 _deleted = set(changes.deleted)
2896 _deleted = set(changes.deleted)
2878 unknown = set(changes.unknown)
2897 unknown = set(changes.unknown)
2879 unknown.update(changes.ignored)
2898 unknown.update(changes.ignored)
2880 clean = set(changes.clean)
2899 clean = set(changes.clean)
2881 modadded = set()
2900 modadded = set()
2882
2901
2883 # split between files known in target manifest and the others
2902 # split between files known in target manifest and the others
2884 smf = set(mf)
2903 smf = set(mf)
2885
2904
2886 # determine the exact nature of the deleted changesets
2905 # determine the exact nature of the deleted changesets
2887 deladded = _deleted - smf
2906 deladded = _deleted - smf
2888 deleted = _deleted - deladded
2907 deleted = _deleted - deladded
2889
2908
2890 # We need to account for the state of the file in the dirstate,
2909 # We need to account for the state of the file in the dirstate,
2891 # even when we revert against something else than parent. This will
2910 # even when we revert against something else than parent. This will
2892 # slightly alter the behavior of revert (doing back up or not, delete
2911 # slightly alter the behavior of revert (doing back up or not, delete
2893 # or just forget etc).
2912 # or just forget etc).
2894 if parent == node:
2913 if parent == node:
2895 dsmodified = modified
2914 dsmodified = modified
2896 dsadded = added
2915 dsadded = added
2897 dsremoved = removed
2916 dsremoved = removed
2898 # store all local modifications, useful later for rename detection
2917 # store all local modifications, useful later for rename detection
2899 localchanges = dsmodified | dsadded
2918 localchanges = dsmodified | dsadded
2900 modified, added, removed = set(), set(), set()
2919 modified, added, removed = set(), set(), set()
2901 else:
2920 else:
2902 changes = repo.status(node1=parent, match=m)
2921 changes = repo.status(node1=parent, match=m)
2903 dsmodified = set(changes.modified)
2922 dsmodified = set(changes.modified)
2904 dsadded = set(changes.added)
2923 dsadded = set(changes.added)
2905 dsremoved = set(changes.removed)
2924 dsremoved = set(changes.removed)
2906 # store all local modifications, useful later for rename detection
2925 # store all local modifications, useful later for rename detection
2907 localchanges = dsmodified | dsadded
2926 localchanges = dsmodified | dsadded
2908
2927
2909 # only take into account for removes between wc and target
2928 # only take into account for removes between wc and target
2910 clean |= dsremoved - removed
2929 clean |= dsremoved - removed
2911 dsremoved &= removed
2930 dsremoved &= removed
2912 # distinct between dirstate remove and other
2931 # distinct between dirstate remove and other
2913 removed -= dsremoved
2932 removed -= dsremoved
2914
2933
2915 modadded = added & dsmodified
2934 modadded = added & dsmodified
2916 added -= modadded
2935 added -= modadded
2917
2936
2918 # tell newly modified apart.
2937 # tell newly modified apart.
2919 dsmodified &= modified
2938 dsmodified &= modified
2920 dsmodified |= modified & dsadded # dirstate added may needs backup
2939 dsmodified |= modified & dsadded # dirstate added may needs backup
2921 modified -= dsmodified
2940 modified -= dsmodified
2922
2941
2923 # We need to wait for some post-processing to update this set
2942 # We need to wait for some post-processing to update this set
2924 # before making the distinction. The dirstate will be used for
2943 # before making the distinction. The dirstate will be used for
2925 # that purpose.
2944 # that purpose.
2926 dsadded = added
2945 dsadded = added
2927
2946
2928 # in case of merge, files that are actually added can be reported as
2947 # in case of merge, files that are actually added can be reported as
2929 # modified, we need to post process the result
2948 # modified, we need to post process the result
2930 if p2 != nullid:
2949 if p2 != nullid:
2931 if pmf is None:
2950 if pmf is None:
2932 # only need parent manifest in the merge case,
2951 # only need parent manifest in the merge case,
2933 # so do not read by default
2952 # so do not read by default
2934 pmf = repo[parent].manifest()
2953 pmf = repo[parent].manifest()
2935 mergeadd = dsmodified - set(pmf)
2954 mergeadd = dsmodified - set(pmf)
2936 dsadded |= mergeadd
2955 dsadded |= mergeadd
2937 dsmodified -= mergeadd
2956 dsmodified -= mergeadd
2938
2957
2939 # if f is a rename, update `names` to also revert the source
2958 # if f is a rename, update `names` to also revert the source
2940 cwd = repo.getcwd()
2959 cwd = repo.getcwd()
2941 for f in localchanges:
2960 for f in localchanges:
2942 src = repo.dirstate.copied(f)
2961 src = repo.dirstate.copied(f)
2943 # XXX should we check for rename down to target node?
2962 # XXX should we check for rename down to target node?
2944 if src and src not in names and repo.dirstate[src] == 'r':
2963 if src and src not in names and repo.dirstate[src] == 'r':
2945 dsremoved.add(src)
2964 dsremoved.add(src)
2946 names[src] = (repo.pathto(src, cwd), True)
2965 names[src] = (repo.pathto(src, cwd), True)
2947
2966
2948 # distinguish between file to forget and the other
2967 # distinguish between file to forget and the other
2949 added = set()
2968 added = set()
2950 for abs in dsadded:
2969 for abs in dsadded:
2951 if repo.dirstate[abs] != 'a':
2970 if repo.dirstate[abs] != 'a':
2952 added.add(abs)
2971 added.add(abs)
2953 dsadded -= added
2972 dsadded -= added
2954
2973
2955 for abs in deladded:
2974 for abs in deladded:
2956 if repo.dirstate[abs] == 'a':
2975 if repo.dirstate[abs] == 'a':
2957 dsadded.add(abs)
2976 dsadded.add(abs)
2958 deladded -= dsadded
2977 deladded -= dsadded
2959
2978
2960 # For files marked as removed, we check if an unknown file is present at
2979 # For files marked as removed, we check if an unknown file is present at
2961 # the same path. If a such file exists it may need to be backed up.
2980 # the same path. If a such file exists it may need to be backed up.
2962 # Making the distinction at this stage helps have simpler backup
2981 # Making the distinction at this stage helps have simpler backup
2963 # logic.
2982 # logic.
2964 removunk = set()
2983 removunk = set()
2965 for abs in removed:
2984 for abs in removed:
2966 target = repo.wjoin(abs)
2985 target = repo.wjoin(abs)
2967 if os.path.lexists(target):
2986 if os.path.lexists(target):
2968 removunk.add(abs)
2987 removunk.add(abs)
2969 removed -= removunk
2988 removed -= removunk
2970
2989
2971 dsremovunk = set()
2990 dsremovunk = set()
2972 for abs in dsremoved:
2991 for abs in dsremoved:
2973 target = repo.wjoin(abs)
2992 target = repo.wjoin(abs)
2974 if os.path.lexists(target):
2993 if os.path.lexists(target):
2975 dsremovunk.add(abs)
2994 dsremovunk.add(abs)
2976 dsremoved -= dsremovunk
2995 dsremoved -= dsremovunk
2977
2996
2978 # action to be actually performed by revert
2997 # action to be actually performed by revert
2979 # (<list of file>, message>) tuple
2998 # (<list of file>, message>) tuple
2980 actions = {'revert': ([], _('reverting %s\n')),
2999 actions = {'revert': ([], _('reverting %s\n')),
2981 'add': ([], _('adding %s\n')),
3000 'add': ([], _('adding %s\n')),
2982 'remove': ([], _('removing %s\n')),
3001 'remove': ([], _('removing %s\n')),
2983 'drop': ([], _('removing %s\n')),
3002 'drop': ([], _('removing %s\n')),
2984 'forget': ([], _('forgetting %s\n')),
3003 'forget': ([], _('forgetting %s\n')),
2985 'undelete': ([], _('undeleting %s\n')),
3004 'undelete': ([], _('undeleting %s\n')),
2986 'noop': (None, _('no changes needed to %s\n')),
3005 'noop': (None, _('no changes needed to %s\n')),
2987 'unknown': (None, _('file not managed: %s\n')),
3006 'unknown': (None, _('file not managed: %s\n')),
2988 }
3007 }
2989
3008
2990 # "constant" that convey the backup strategy.
3009 # "constant" that convey the backup strategy.
2991 # All set to `discard` if `no-backup` is set do avoid checking
3010 # All set to `discard` if `no-backup` is set do avoid checking
2992 # no_backup lower in the code.
3011 # no_backup lower in the code.
2993 # These values are ordered for comparison purposes
3012 # These values are ordered for comparison purposes
2994 backup = 2 # unconditionally do backup
3013 backup = 2 # unconditionally do backup
2995 check = 1 # check if the existing file differs from target
3014 check = 1 # check if the existing file differs from target
2996 discard = 0 # never do backup
3015 discard = 0 # never do backup
2997 if opts.get('no_backup'):
3016 if opts.get('no_backup'):
2998 backup = check = discard
3017 backup = check = discard
2999
3018
3000 backupanddel = actions['remove']
3019 backupanddel = actions['remove']
3001 if not opts.get('no_backup'):
3020 if not opts.get('no_backup'):
3002 backupanddel = actions['drop']
3021 backupanddel = actions['drop']
3003
3022
3004 disptable = (
3023 disptable = (
3005 # dispatch table:
3024 # dispatch table:
3006 # file state
3025 # file state
3007 # action
3026 # action
3008 # make backup
3027 # make backup
3009
3028
3010 ## Sets that results that will change file on disk
3029 ## Sets that results that will change file on disk
3011 # Modified compared to target, no local change
3030 # Modified compared to target, no local change
3012 (modified, actions['revert'], discard),
3031 (modified, actions['revert'], discard),
3013 # Modified compared to target, but local file is deleted
3032 # Modified compared to target, but local file is deleted
3014 (deleted, actions['revert'], discard),
3033 (deleted, actions['revert'], discard),
3015 # Modified compared to target, local change
3034 # Modified compared to target, local change
3016 (dsmodified, actions['revert'], backup),
3035 (dsmodified, actions['revert'], backup),
3017 # Added since target
3036 # Added since target
3018 (added, actions['remove'], discard),
3037 (added, actions['remove'], discard),
3019 # Added in working directory
3038 # Added in working directory
3020 (dsadded, actions['forget'], discard),
3039 (dsadded, actions['forget'], discard),
3021 # Added since target, have local modification
3040 # Added since target, have local modification
3022 (modadded, backupanddel, backup),
3041 (modadded, backupanddel, backup),
3023 # Added since target but file is missing in working directory
3042 # Added since target but file is missing in working directory
3024 (deladded, actions['drop'], discard),
3043 (deladded, actions['drop'], discard),
3025 # Removed since target, before working copy parent
3044 # Removed since target, before working copy parent
3026 (removed, actions['add'], discard),
3045 (removed, actions['add'], discard),
3027 # Same as `removed` but an unknown file exists at the same path
3046 # Same as `removed` but an unknown file exists at the same path
3028 (removunk, actions['add'], check),
3047 (removunk, actions['add'], check),
3029 # Removed since targe, marked as such in working copy parent
3048 # Removed since targe, marked as such in working copy parent
3030 (dsremoved, actions['undelete'], discard),
3049 (dsremoved, actions['undelete'], discard),
3031 # Same as `dsremoved` but an unknown file exists at the same path
3050 # Same as `dsremoved` but an unknown file exists at the same path
3032 (dsremovunk, actions['undelete'], check),
3051 (dsremovunk, actions['undelete'], check),
3033 ## the following sets does not result in any file changes
3052 ## the following sets does not result in any file changes
3034 # File with no modification
3053 # File with no modification
3035 (clean, actions['noop'], discard),
3054 (clean, actions['noop'], discard),
3036 # Existing file, not tracked anywhere
3055 # Existing file, not tracked anywhere
3037 (unknown, actions['unknown'], discard),
3056 (unknown, actions['unknown'], discard),
3038 )
3057 )
3039
3058
3040 for abs, (rel, exact) in sorted(names.items()):
3059 for abs, (rel, exact) in sorted(names.items()):
3041 # target file to be touch on disk (relative to cwd)
3060 # target file to be touch on disk (relative to cwd)
3042 target = repo.wjoin(abs)
3061 target = repo.wjoin(abs)
3043 # search the entry in the dispatch table.
3062 # search the entry in the dispatch table.
3044 # if the file is in any of these sets, it was touched in the working
3063 # if the file is in any of these sets, it was touched in the working
3045 # directory parent and we are sure it needs to be reverted.
3064 # directory parent and we are sure it needs to be reverted.
3046 for table, (xlist, msg), dobackup in disptable:
3065 for table, (xlist, msg), dobackup in disptable:
3047 if abs not in table:
3066 if abs not in table:
3048 continue
3067 continue
3049 if xlist is not None:
3068 if xlist is not None:
3050 xlist.append(abs)
3069 xlist.append(abs)
3051 if dobackup and (backup <= dobackup
3070 if dobackup and (backup <= dobackup
3052 or wctx[abs].cmp(ctx[abs])):
3071 or wctx[abs].cmp(ctx[abs])):
3053 bakname = "%s.orig" % rel
3072 bakname = "%s.orig" % rel
3054 ui.note(_('saving current version of %s as %s\n') %
3073 ui.note(_('saving current version of %s as %s\n') %
3055 (rel, bakname))
3074 (rel, bakname))
3056 if not opts.get('dry_run'):
3075 if not opts.get('dry_run'):
3057 if interactive:
3076 if interactive:
3058 util.copyfile(target, bakname)
3077 util.copyfile(target, bakname)
3059 else:
3078 else:
3060 util.rename(target, bakname)
3079 util.rename(target, bakname)
3061 if ui.verbose or not exact:
3080 if ui.verbose or not exact:
3062 if not isinstance(msg, basestring):
3081 if not isinstance(msg, basestring):
3063 msg = msg(abs)
3082 msg = msg(abs)
3064 ui.status(msg % rel)
3083 ui.status(msg % rel)
3065 elif exact:
3084 elif exact:
3066 ui.warn(msg % rel)
3085 ui.warn(msg % rel)
3067 break
3086 break
3068
3087
3069 if not opts.get('dry_run'):
3088 if not opts.get('dry_run'):
3070 needdata = ('revert', 'add', 'undelete')
3089 needdata = ('revert', 'add', 'undelete')
3071 _revertprefetch(repo, ctx, *[actions[name][0] for name in needdata])
3090 _revertprefetch(repo, ctx, *[actions[name][0] for name in needdata])
3072 _performrevert(repo, parents, ctx, actions, interactive)
3091 _performrevert(repo, parents, ctx, actions, interactive)
3073
3092
3074 if targetsubs:
3093 if targetsubs:
3075 # Revert the subrepos on the revert list
3094 # Revert the subrepos on the revert list
3076 for sub in targetsubs:
3095 for sub in targetsubs:
3077 try:
3096 try:
3078 wctx.sub(sub).revert(ctx.substate[sub], *pats, **opts)
3097 wctx.sub(sub).revert(ctx.substate[sub], *pats, **opts)
3079 except KeyError:
3098 except KeyError:
3080 raise util.Abort("subrepository '%s' does not exist in %s!"
3099 raise util.Abort("subrepository '%s' does not exist in %s!"
3081 % (sub, short(ctx.node())))
3100 % (sub, short(ctx.node())))
3082 finally:
3101 finally:
3083 wlock.release()
3102 wlock.release()
3084
3103
3085 def _revertprefetch(repo, ctx, *files):
3104 def _revertprefetch(repo, ctx, *files):
3086 """Let extension changing the storage layer prefetch content"""
3105 """Let extension changing the storage layer prefetch content"""
3087 pass
3106 pass
3088
3107
3089 def _performrevert(repo, parents, ctx, actions, interactive=False):
3108 def _performrevert(repo, parents, ctx, actions, interactive=False):
3090 """function that actually perform all the actions computed for revert
3109 """function that actually perform all the actions computed for revert
3091
3110
3092 This is an independent function to let extension to plug in and react to
3111 This is an independent function to let extension to plug in and react to
3093 the imminent revert.
3112 the imminent revert.
3094
3113
3095 Make sure you have the working directory locked when calling this function.
3114 Make sure you have the working directory locked when calling this function.
3096 """
3115 """
3097 parent, p2 = parents
3116 parent, p2 = parents
3098 node = ctx.node()
3117 node = ctx.node()
3099 def checkout(f):
3118 def checkout(f):
3100 fc = ctx[f]
3119 fc = ctx[f]
3101 repo.wwrite(f, fc.data(), fc.flags())
3120 repo.wwrite(f, fc.data(), fc.flags())
3102
3121
3103 audit_path = pathutil.pathauditor(repo.root)
3122 audit_path = pathutil.pathauditor(repo.root)
3104 for f in actions['forget'][0]:
3123 for f in actions['forget'][0]:
3105 repo.dirstate.drop(f)
3124 repo.dirstate.drop(f)
3106 for f in actions['remove'][0]:
3125 for f in actions['remove'][0]:
3107 audit_path(f)
3126 audit_path(f)
3108 try:
3127 try:
3109 util.unlinkpath(repo.wjoin(f))
3128 util.unlinkpath(repo.wjoin(f))
3110 except OSError:
3129 except OSError:
3111 pass
3130 pass
3112 repo.dirstate.remove(f)
3131 repo.dirstate.remove(f)
3113 for f in actions['drop'][0]:
3132 for f in actions['drop'][0]:
3114 audit_path(f)
3133 audit_path(f)
3115 repo.dirstate.remove(f)
3134 repo.dirstate.remove(f)
3116
3135
3117 normal = None
3136 normal = None
3118 if node == parent:
3137 if node == parent:
3119 # We're reverting to our parent. If possible, we'd like status
3138 # We're reverting to our parent. If possible, we'd like status
3120 # to report the file as clean. We have to use normallookup for
3139 # to report the file as clean. We have to use normallookup for
3121 # merges to avoid losing information about merged/dirty files.
3140 # merges to avoid losing information about merged/dirty files.
3122 if p2 != nullid:
3141 if p2 != nullid:
3123 normal = repo.dirstate.normallookup
3142 normal = repo.dirstate.normallookup
3124 else:
3143 else:
3125 normal = repo.dirstate.normal
3144 normal = repo.dirstate.normal
3126
3145
3127 newlyaddedandmodifiedfiles = set()
3146 newlyaddedandmodifiedfiles = set()
3128 if interactive:
3147 if interactive:
3129 # Prompt the user for changes to revert
3148 # Prompt the user for changes to revert
3130 torevert = [repo.wjoin(f) for f in actions['revert'][0]]
3149 torevert = [repo.wjoin(f) for f in actions['revert'][0]]
3131 m = scmutil.match(ctx, torevert, {})
3150 m = scmutil.match(ctx, torevert, {})
3132 diffopts = patch.difffeatureopts(repo.ui, whitespace=True)
3151 diffopts = patch.difffeatureopts(repo.ui, whitespace=True)
3133 diffopts.nodates = True
3152 diffopts.nodates = True
3134 diffopts.git = True
3153 diffopts.git = True
3135 reversehunks = repo.ui.configbool('experimental',
3154 reversehunks = repo.ui.configbool('experimental',
3136 'revertalternateinteractivemode',
3155 'revertalternateinteractivemode',
3137 True)
3156 True)
3138 if reversehunks:
3157 if reversehunks:
3139 diff = patch.diff(repo, ctx.node(), None, m, opts=diffopts)
3158 diff = patch.diff(repo, ctx.node(), None, m, opts=diffopts)
3140 else:
3159 else:
3141 diff = patch.diff(repo, None, ctx.node(), m, opts=diffopts)
3160 diff = patch.diff(repo, None, ctx.node(), m, opts=diffopts)
3142 originalchunks = patch.parsepatch(diff)
3161 originalchunks = patch.parsepatch(diff)
3143
3162
3144 try:
3163 try:
3145
3164
3146 chunks = recordfilter(repo.ui, originalchunks)
3165 chunks = recordfilter(repo.ui, originalchunks)
3147 if reversehunks:
3166 if reversehunks:
3148 chunks = patch.reversehunks(chunks)
3167 chunks = patch.reversehunks(chunks)
3149
3168
3150 except patch.PatchError as err:
3169 except patch.PatchError as err:
3151 raise util.Abort(_('error parsing patch: %s') % err)
3170 raise util.Abort(_('error parsing patch: %s') % err)
3152
3171
3153 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
3172 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
3154 # Apply changes
3173 # Apply changes
3155 fp = cStringIO.StringIO()
3174 fp = cStringIO.StringIO()
3156 for c in chunks:
3175 for c in chunks:
3157 c.write(fp)
3176 c.write(fp)
3158 dopatch = fp.tell()
3177 dopatch = fp.tell()
3159 fp.seek(0)
3178 fp.seek(0)
3160 if dopatch:
3179 if dopatch:
3161 try:
3180 try:
3162 patch.internalpatch(repo.ui, repo, fp, 1, eolmode=None)
3181 patch.internalpatch(repo.ui, repo, fp, 1, eolmode=None)
3163 except patch.PatchError as err:
3182 except patch.PatchError as err:
3164 raise util.Abort(str(err))
3183 raise util.Abort(str(err))
3165 del fp
3184 del fp
3166 else:
3185 else:
3167 for f in actions['revert'][0]:
3186 for f in actions['revert'][0]:
3168 checkout(f)
3187 checkout(f)
3169 if normal:
3188 if normal:
3170 normal(f)
3189 normal(f)
3171
3190
3172 for f in actions['add'][0]:
3191 for f in actions['add'][0]:
3173 # Don't checkout modified files, they are already created by the diff
3192 # Don't checkout modified files, they are already created by the diff
3174 if f not in newlyaddedandmodifiedfiles:
3193 if f not in newlyaddedandmodifiedfiles:
3175 checkout(f)
3194 checkout(f)
3176 repo.dirstate.add(f)
3195 repo.dirstate.add(f)
3177
3196
3178 normal = repo.dirstate.normallookup
3197 normal = repo.dirstate.normallookup
3179 if node == parent and p2 == nullid:
3198 if node == parent and p2 == nullid:
3180 normal = repo.dirstate.normal
3199 normal = repo.dirstate.normal
3181 for f in actions['undelete'][0]:
3200 for f in actions['undelete'][0]:
3182 checkout(f)
3201 checkout(f)
3183 normal(f)
3202 normal(f)
3184
3203
3185 copied = copies.pathcopies(repo[parent], ctx)
3204 copied = copies.pathcopies(repo[parent], ctx)
3186
3205
3187 for f in actions['add'][0] + actions['undelete'][0] + actions['revert'][0]:
3206 for f in actions['add'][0] + actions['undelete'][0] + actions['revert'][0]:
3188 if f in copied:
3207 if f in copied:
3189 repo.dirstate.copy(copied[f], f)
3208 repo.dirstate.copy(copied[f], f)
3190
3209
3191 def command(table):
3210 def command(table):
3192 """Returns a function object to be used as a decorator for making commands.
3211 """Returns a function object to be used as a decorator for making commands.
3193
3212
3194 This function receives a command table as its argument. The table should
3213 This function receives a command table as its argument. The table should
3195 be a dict.
3214 be a dict.
3196
3215
3197 The returned function can be used as a decorator for adding commands
3216 The returned function can be used as a decorator for adding commands
3198 to that command table. This function accepts multiple arguments to define
3217 to that command table. This function accepts multiple arguments to define
3199 a command.
3218 a command.
3200
3219
3201 The first argument is the command name.
3220 The first argument is the command name.
3202
3221
3203 The options argument is an iterable of tuples defining command arguments.
3222 The options argument is an iterable of tuples defining command arguments.
3204 See ``mercurial.fancyopts.fancyopts()`` for the format of each tuple.
3223 See ``mercurial.fancyopts.fancyopts()`` for the format of each tuple.
3205
3224
3206 The synopsis argument defines a short, one line summary of how to use the
3225 The synopsis argument defines a short, one line summary of how to use the
3207 command. This shows up in the help output.
3226 command. This shows up in the help output.
3208
3227
3209 The norepo argument defines whether the command does not require a
3228 The norepo argument defines whether the command does not require a
3210 local repository. Most commands operate against a repository, thus the
3229 local repository. Most commands operate against a repository, thus the
3211 default is False.
3230 default is False.
3212
3231
3213 The optionalrepo argument defines whether the command optionally requires
3232 The optionalrepo argument defines whether the command optionally requires
3214 a local repository.
3233 a local repository.
3215
3234
3216 The inferrepo argument defines whether to try to find a repository from the
3235 The inferrepo argument defines whether to try to find a repository from the
3217 command line arguments. If True, arguments will be examined for potential
3236 command line arguments. If True, arguments will be examined for potential
3218 repository locations. See ``findrepo()``. If a repository is found, it
3237 repository locations. See ``findrepo()``. If a repository is found, it
3219 will be used.
3238 will be used.
3220 """
3239 """
3221 def cmd(name, options=(), synopsis=None, norepo=False, optionalrepo=False,
3240 def cmd(name, options=(), synopsis=None, norepo=False, optionalrepo=False,
3222 inferrepo=False):
3241 inferrepo=False):
3223 def decorator(func):
3242 def decorator(func):
3224 if synopsis:
3243 if synopsis:
3225 table[name] = func, list(options), synopsis
3244 table[name] = func, list(options), synopsis
3226 else:
3245 else:
3227 table[name] = func, list(options)
3246 table[name] = func, list(options)
3228
3247
3229 if norepo:
3248 if norepo:
3230 # Avoid import cycle.
3249 # Avoid import cycle.
3231 import commands
3250 import commands
3232 commands.norepo += ' %s' % ' '.join(parsealiases(name))
3251 commands.norepo += ' %s' % ' '.join(parsealiases(name))
3233
3252
3234 if optionalrepo:
3253 if optionalrepo:
3235 import commands
3254 import commands
3236 commands.optionalrepo += ' %s' % ' '.join(parsealiases(name))
3255 commands.optionalrepo += ' %s' % ' '.join(parsealiases(name))
3237
3256
3238 if inferrepo:
3257 if inferrepo:
3239 import commands
3258 import commands
3240 commands.inferrepo += ' %s' % ' '.join(parsealiases(name))
3259 commands.inferrepo += ' %s' % ' '.join(parsealiases(name))
3241
3260
3242 return func
3261 return func
3243 return decorator
3262 return decorator
3244
3263
3245 return cmd
3264 return cmd
3246
3265
3247 # a list of (ui, repo, otherpeer, opts, missing) functions called by
3266 # a list of (ui, repo, otherpeer, opts, missing) functions called by
3248 # commands.outgoing. "missing" is "missing" of the result of
3267 # commands.outgoing. "missing" is "missing" of the result of
3249 # "findcommonoutgoing()"
3268 # "findcommonoutgoing()"
3250 outgoinghooks = util.hooks()
3269 outgoinghooks = util.hooks()
3251
3270
3252 # a list of (ui, repo) functions called by commands.summary
3271 # a list of (ui, repo) functions called by commands.summary
3253 summaryhooks = util.hooks()
3272 summaryhooks = util.hooks()
3254
3273
3255 # a list of (ui, repo, opts, changes) functions called by commands.summary.
3274 # a list of (ui, repo, opts, changes) functions called by commands.summary.
3256 #
3275 #
3257 # functions should return tuple of booleans below, if 'changes' is None:
3276 # functions should return tuple of booleans below, if 'changes' is None:
3258 # (whether-incomings-are-needed, whether-outgoings-are-needed)
3277 # (whether-incomings-are-needed, whether-outgoings-are-needed)
3259 #
3278 #
3260 # otherwise, 'changes' is a tuple of tuples below:
3279 # otherwise, 'changes' is a tuple of tuples below:
3261 # - (sourceurl, sourcebranch, sourcepeer, incoming)
3280 # - (sourceurl, sourcebranch, sourcepeer, incoming)
3262 # - (desturl, destbranch, destpeer, outgoing)
3281 # - (desturl, destbranch, destpeer, outgoing)
3263 summaryremotehooks = util.hooks()
3282 summaryremotehooks = util.hooks()
3264
3283
3265 # A list of state files kept by multistep operations like graft.
3284 # A list of state files kept by multistep operations like graft.
3266 # Since graft cannot be aborted, it is considered 'clearable' by update.
3285 # Since graft cannot be aborted, it is considered 'clearable' by update.
3267 # note: bisect is intentionally excluded
3286 # note: bisect is intentionally excluded
3268 # (state file, clearable, allowcommit, error, hint)
3287 # (state file, clearable, allowcommit, error, hint)
3269 unfinishedstates = [
3288 unfinishedstates = [
3270 ('graftstate', True, False, _('graft in progress'),
3289 ('graftstate', True, False, _('graft in progress'),
3271 _("use 'hg graft --continue' or 'hg update' to abort")),
3290 _("use 'hg graft --continue' or 'hg update' to abort")),
3272 ('updatestate', True, False, _('last update was interrupted'),
3291 ('updatestate', True, False, _('last update was interrupted'),
3273 _("use 'hg update' to get a consistent checkout"))
3292 _("use 'hg update' to get a consistent checkout"))
3274 ]
3293 ]
3275
3294
3276 def checkunfinished(repo, commit=False):
3295 def checkunfinished(repo, commit=False):
3277 '''Look for an unfinished multistep operation, like graft, and abort
3296 '''Look for an unfinished multistep operation, like graft, and abort
3278 if found. It's probably good to check this right before
3297 if found. It's probably good to check this right before
3279 bailifchanged().
3298 bailifchanged().
3280 '''
3299 '''
3281 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3300 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3282 if commit and allowcommit:
3301 if commit and allowcommit:
3283 continue
3302 continue
3284 if repo.vfs.exists(f):
3303 if repo.vfs.exists(f):
3285 raise util.Abort(msg, hint=hint)
3304 raise util.Abort(msg, hint=hint)
3286
3305
3287 def clearunfinished(repo):
3306 def clearunfinished(repo):
3288 '''Check for unfinished operations (as above), and clear the ones
3307 '''Check for unfinished operations (as above), and clear the ones
3289 that are clearable.
3308 that are clearable.
3290 '''
3309 '''
3291 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3310 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3292 if not clearable and repo.vfs.exists(f):
3311 if not clearable and repo.vfs.exists(f):
3293 raise util.Abort(msg, hint=hint)
3312 raise util.Abort(msg, hint=hint)
3294 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3313 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3295 if clearable and repo.vfs.exists(f):
3314 if clearable and repo.vfs.exists(f):
3296 util.unlink(repo.join(f))
3315 util.unlink(repo.join(f))
3297
3316
3298 class dirstateguard(object):
3317 class dirstateguard(object):
3299 '''Restore dirstate at unexpected failure.
3318 '''Restore dirstate at unexpected failure.
3300
3319
3301 At the construction, this class does:
3320 At the construction, this class does:
3302
3321
3303 - write current ``repo.dirstate`` out, and
3322 - write current ``repo.dirstate`` out, and
3304 - save ``.hg/dirstate`` into the backup file
3323 - save ``.hg/dirstate`` into the backup file
3305
3324
3306 This restores ``.hg/dirstate`` from backup file, if ``release()``
3325 This restores ``.hg/dirstate`` from backup file, if ``release()``
3307 is invoked before ``close()``.
3326 is invoked before ``close()``.
3308
3327
3309 This just removes the backup file at ``close()`` before ``release()``.
3328 This just removes the backup file at ``close()`` before ``release()``.
3310 '''
3329 '''
3311
3330
3312 def __init__(self, repo, name):
3331 def __init__(self, repo, name):
3313 repo.dirstate.write()
3332 repo.dirstate.write()
3314 self._repo = repo
3333 self._repo = repo
3315 self._filename = 'dirstate.backup.%s.%d' % (name, id(self))
3334 self._filename = 'dirstate.backup.%s.%d' % (name, id(self))
3316 repo.vfs.write(self._filename, repo.vfs.tryread('dirstate'))
3335 repo.vfs.write(self._filename, repo.vfs.tryread('dirstate'))
3317 self._active = True
3336 self._active = True
3318 self._closed = False
3337 self._closed = False
3319
3338
3320 def __del__(self):
3339 def __del__(self):
3321 if self._active: # still active
3340 if self._active: # still active
3322 # this may occur, even if this class is used correctly:
3341 # this may occur, even if this class is used correctly:
3323 # for example, releasing other resources like transaction
3342 # for example, releasing other resources like transaction
3324 # may raise exception before ``dirstateguard.release`` in
3343 # may raise exception before ``dirstateguard.release`` in
3325 # ``release(tr, ....)``.
3344 # ``release(tr, ....)``.
3326 self._abort()
3345 self._abort()
3327
3346
3328 def close(self):
3347 def close(self):
3329 if not self._active: # already inactivated
3348 if not self._active: # already inactivated
3330 msg = (_("can't close already inactivated backup: %s")
3349 msg = (_("can't close already inactivated backup: %s")
3331 % self._filename)
3350 % self._filename)
3332 raise util.Abort(msg)
3351 raise util.Abort(msg)
3333
3352
3334 self._repo.vfs.unlink(self._filename)
3353 self._repo.vfs.unlink(self._filename)
3335 self._active = False
3354 self._active = False
3336 self._closed = True
3355 self._closed = True
3337
3356
3338 def _abort(self):
3357 def _abort(self):
3339 # this "invalidate()" prevents "wlock.release()" from writing
3358 # this "invalidate()" prevents "wlock.release()" from writing
3340 # changes of dirstate out after restoring to original status
3359 # changes of dirstate out after restoring to original status
3341 self._repo.dirstate.invalidate()
3360 self._repo.dirstate.invalidate()
3342
3361
3343 self._repo.vfs.rename(self._filename, 'dirstate')
3362 self._repo.vfs.rename(self._filename, 'dirstate')
3344 self._active = False
3363 self._active = False
3345
3364
3346 def release(self):
3365 def release(self):
3347 if not self._closed:
3366 if not self._closed:
3348 if not self._active: # already inactivated
3367 if not self._active: # already inactivated
3349 msg = (_("can't release already inactivated backup: %s")
3368 msg = (_("can't release already inactivated backup: %s")
3350 % self._filename)
3369 % self._filename)
3351 raise util.Abort(msg)
3370 raise util.Abort(msg)
3352 self._abort()
3371 self._abort()
3353
3372
3354 _bundlecompspecs = {'none': None,
3373 _bundlecompspecs = {'none': None,
3355 'bzip2': 'BZ',
3374 'bzip2': 'BZ',
3356 'gzip': 'GZ',
3375 'gzip': 'GZ',
3357 }
3376 }
3358
3377
3359 _bundleversionspecs = {'v1': '01',
3378 _bundleversionspecs = {'v1': '01',
3360 'v2': '02',
3379 'v2': '02',
3361 'bundle2': '02', #legacy
3380 'bundle2': '02', #legacy
3362 }
3381 }
3363
3382
3364 def parsebundletype(repo, spec):
3383 def parsebundletype(repo, spec):
3365 """return the internal bundle type to use from a user input
3384 """return the internal bundle type to use from a user input
3366
3385
3367 This is parsing user specified bundle type as accepted in:
3386 This is parsing user specified bundle type as accepted in:
3368
3387
3369 'hg bundle --type TYPE'.
3388 'hg bundle --type TYPE'.
3370
3389
3371 It accept format in the form [compression][-version]|[version]
3390 It accept format in the form [compression][-version]|[version]
3372
3391
3373 Consensus about extensions of the format for various bundle2 feature
3392 Consensus about extensions of the format for various bundle2 feature
3374 is to prefix any feature with "+". eg "+treemanifest" or "gzip+phases"
3393 is to prefix any feature with "+". eg "+treemanifest" or "gzip+phases"
3375 """
3394 """
3376 comp, version = None, None
3395 comp, version = None, None
3377
3396
3378 if '-' in spec:
3397 if '-' in spec:
3379 comp, version = spec.split('-', 1)
3398 comp, version = spec.split('-', 1)
3380 elif spec in _bundlecompspecs:
3399 elif spec in _bundlecompspecs:
3381 comp = spec
3400 comp = spec
3382 elif spec in _bundleversionspecs:
3401 elif spec in _bundleversionspecs:
3383 version = spec
3402 version = spec
3384 else:
3403 else:
3385 raise util.Abort(_('unknown bundle type specified with --type'))
3404 raise util.Abort(_('unknown bundle type specified with --type'))
3386
3405
3387 if comp is None:
3406 if comp is None:
3388 comp = 'BZ'
3407 comp = 'BZ'
3389 else:
3408 else:
3390 try:
3409 try:
3391 comp = _bundlecompspecs[comp]
3410 comp = _bundlecompspecs[comp]
3392 except KeyError:
3411 except KeyError:
3393 raise util.Abort(_('unknown bundle type specified with --type'))
3412 raise util.Abort(_('unknown bundle type specified with --type'))
3394
3413
3395 if version is None:
3414 if version is None:
3396 version = '01'
3415 version = '01'
3397 if 'generaldelta' in repo.requirements:
3416 if 'generaldelta' in repo.requirements:
3398 version = '02'
3417 version = '02'
3399 else:
3418 else:
3400 try:
3419 try:
3401 version = _bundleversionspecs[version]
3420 version = _bundleversionspecs[version]
3402 except KeyError:
3421 except KeyError:
3403 raise util.Abort(_('unknown bundle type specified with --type'))
3422 raise util.Abort(_('unknown bundle type specified with --type'))
3404
3423
3405 return version, comp
3424 return version, comp
@@ -1,1507 +1,1547 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
110
111 Test avoiding editor invocation at applying the patch with --exact,
111 Test avoiding editor invocation at applying the patch with --exact,
112 even if commit message is empty
112 even if commit message is empty
113
113
114 $ echo a >> b/a
114 $ echo a >> b/a
115 $ hg --cwd b commit -m ' '
115 $ hg --cwd b commit -m ' '
116 $ hg --cwd b tip -T "{node}\n"
116 $ hg --cwd b tip -T "{node}\n"
117 d8804f3f5396d800812f579c8452796a5993bdb2
117 d8804f3f5396d800812f579c8452796a5993bdb2
118 $ hg --cwd b export -o ../empty-log.diff .
118 $ hg --cwd b export -o ../empty-log.diff .
119 $ hg --cwd b update -q -C ".^1"
119 $ hg --cwd b update -q -C ".^1"
120 $ hg --cwd b --config extensions.strip= strip -q tip
120 $ hg --cwd b --config extensions.strip= strip -q tip
121 $ HGEDITOR=cat hg --cwd b import --exact ../empty-log.diff
121 $ HGEDITOR=cat hg --cwd b import --exact ../empty-log.diff
122 applying ../empty-log.diff
122 applying ../empty-log.diff
123 $ hg --cwd b tip -T "{node}\n"
123 $ hg --cwd b tip -T "{node}\n"
124 d8804f3f5396d800812f579c8452796a5993bdb2
124 d8804f3f5396d800812f579c8452796a5993bdb2
125
125
126 $ rm -r b
126 $ rm -r b
127
127
128
128
129 import of plain diff should be ok with message
129 import of plain diff should be ok with message
130
130
131 $ hg clone -r0 a b
131 $ hg clone -r0 a b
132 adding changesets
132 adding changesets
133 adding manifests
133 adding manifests
134 adding file changes
134 adding file changes
135 added 1 changesets with 2 changes to 2 files
135 added 1 changesets with 2 changes to 2 files
136 updating to branch default
136 updating to branch default
137 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
138 $ hg --cwd b import -mpatch ../diffed-tip.patch
138 $ hg --cwd b import -mpatch ../diffed-tip.patch
139 applying ../diffed-tip.patch
139 applying ../diffed-tip.patch
140 $ rm -r b
140 $ rm -r b
141
141
142
142
143 import of plain diff with specific date and user
143 import of plain diff with specific date and user
144 (this also tests that editor is not invoked, if
144 (this also tests that editor is not invoked, if
145 '--message'/'--logfile' is specified and '--edit' is not)
145 '--message'/'--logfile' is specified and '--edit' is not)
146
146
147 $ hg clone -r0 a b
147 $ hg clone -r0 a b
148 adding changesets
148 adding changesets
149 adding manifests
149 adding manifests
150 adding file changes
150 adding file changes
151 added 1 changesets with 2 changes to 2 files
151 added 1 changesets with 2 changes to 2 files
152 updating to branch default
152 updating to branch default
153 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
154 $ 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
155 applying ../diffed-tip.patch
155 applying ../diffed-tip.patch
156 $ hg -R b tip -pv
156 $ hg -R b tip -pv
157 changeset: 1:ca68f19f3a40
157 changeset: 1:ca68f19f3a40
158 tag: tip
158 tag: tip
159 user: user@nowhere.net
159 user: user@nowhere.net
160 date: Thu Jan 01 00:00:01 1970 +0000
160 date: Thu Jan 01 00:00:01 1970 +0000
161 files: a
161 files: a
162 description:
162 description:
163 patch
163 patch
164
164
165
165
166 diff -r 80971e65b431 -r ca68f19f3a40 a
166 diff -r 80971e65b431 -r ca68f19f3a40 a
167 --- a/a Thu Jan 01 00:00:00 1970 +0000
167 --- a/a Thu Jan 01 00:00:00 1970 +0000
168 +++ b/a Thu Jan 01 00:00:01 1970 +0000
168 +++ b/a Thu Jan 01 00:00:01 1970 +0000
169 @@ -1,1 +1,2 @@
169 @@ -1,1 +1,2 @@
170 line 1
170 line 1
171 +line 2
171 +line 2
172
172
173 $ rm -r b
173 $ rm -r b
174
174
175
175
176 import of plain diff should be ok with --no-commit
176 import of plain diff should be ok with --no-commit
177 (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
178 specified, regardless of '--edit')
178 specified, regardless of '--edit')
179
179
180 $ hg clone -r0 a b
180 $ hg clone -r0 a b
181 adding changesets
181 adding changesets
182 adding manifests
182 adding manifests
183 adding file changes
183 adding file changes
184 added 1 changesets with 2 changes to 2 files
184 added 1 changesets with 2 changes to 2 files
185 updating to branch default
185 updating to branch default
186 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
187 $ 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
188 applying ../diffed-tip.patch
188 applying ../diffed-tip.patch
189 $ hg --cwd b diff --nodates
189 $ hg --cwd b diff --nodates
190 diff -r 80971e65b431 a
190 diff -r 80971e65b431 a
191 --- a/a
191 --- a/a
192 +++ b/a
192 +++ b/a
193 @@ -1,1 +1,2 @@
193 @@ -1,1 +1,2 @@
194 line 1
194 line 1
195 +line 2
195 +line 2
196 $ rm -r b
196 $ rm -r b
197
197
198
198
199 import of malformed plain diff should fail
199 import of malformed plain diff should fail
200
200
201 $ hg clone -r0 a b
201 $ hg clone -r0 a b
202 adding changesets
202 adding changesets
203 adding manifests
203 adding manifests
204 adding file changes
204 adding file changes
205 added 1 changesets with 2 changes to 2 files
205 added 1 changesets with 2 changes to 2 files
206 updating to branch default
206 updating to branch default
207 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
208 $ sed 's/1,1/foo/' < diffed-tip.patch > broken.patch
208 $ sed 's/1,1/foo/' < diffed-tip.patch > broken.patch
209 $ hg --cwd b import -mpatch ../broken.patch
209 $ hg --cwd b import -mpatch ../broken.patch
210 applying ../broken.patch
210 applying ../broken.patch
211 abort: bad hunk #1
211 abort: bad hunk #1
212 [255]
212 [255]
213 $ rm -r b
213 $ rm -r b
214
214
215
215
216 hg -R repo import
216 hg -R repo import
217 put the clone in a subdir - having a directory named "a"
217 put the clone in a subdir - having a directory named "a"
218 used to hide a bug.
218 used to hide a bug.
219
219
220 $ mkdir dir
220 $ mkdir dir
221 $ hg clone -r0 a dir/b
221 $ hg clone -r0 a dir/b
222 adding changesets
222 adding changesets
223 adding manifests
223 adding manifests
224 adding file changes
224 adding file changes
225 added 1 changesets with 2 changes to 2 files
225 added 1 changesets with 2 changes to 2 files
226 updating to branch default
226 updating to branch default
227 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
228 $ cd dir
228 $ cd dir
229 $ hg -R b import ../exported-tip.patch
229 $ hg -R b import ../exported-tip.patch
230 applying ../exported-tip.patch
230 applying ../exported-tip.patch
231 $ cd ..
231 $ cd ..
232 $ rm -r dir
232 $ rm -r dir
233
233
234
234
235 import from stdin
235 import from stdin
236
236
237 $ hg clone -r0 a b
237 $ hg clone -r0 a b
238 adding changesets
238 adding changesets
239 adding manifests
239 adding manifests
240 adding file changes
240 adding file changes
241 added 1 changesets with 2 changes to 2 files
241 added 1 changesets with 2 changes to 2 files
242 updating to branch default
242 updating to branch default
243 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
244 $ hg --cwd b import - < exported-tip.patch
244 $ hg --cwd b import - < exported-tip.patch
245 applying patch from stdin
245 applying patch from stdin
246 $ rm -r b
246 $ rm -r b
247
247
248
248
249 import two patches in one stream
249 import two patches in one stream
250
250
251 $ hg init b
251 $ hg init b
252 $ hg --cwd a export 0:tip | hg --cwd b import -
252 $ hg --cwd a export 0:tip | hg --cwd b import -
253 applying patch from stdin
253 applying patch from stdin
254 $ hg --cwd a id
254 $ hg --cwd a id
255 1d4bd90af0e4 tip
255 1d4bd90af0e4 tip
256 $ hg --cwd b id
256 $ hg --cwd b id
257 1d4bd90af0e4 tip
257 1d4bd90af0e4 tip
258 $ rm -r b
258 $ rm -r b
259
259
260
260
261 override commit message
261 override commit message
262
262
263 $ hg clone -r0 a b
263 $ hg clone -r0 a b
264 adding changesets
264 adding changesets
265 adding manifests
265 adding manifests
266 adding file changes
266 adding file changes
267 added 1 changesets with 2 changes to 2 files
267 added 1 changesets with 2 changes to 2 files
268 updating to branch default
268 updating to branch default
269 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
270 $ hg --cwd b import -m 'override' - < exported-tip.patch
270 $ hg --cwd b import -m 'override' - < exported-tip.patch
271 applying patch from stdin
271 applying patch from stdin
272 $ hg --cwd b tip | grep override
272 $ hg --cwd b tip | grep override
273 summary: override
273 summary: override
274 $ rm -r b
274 $ rm -r b
275
275
276 $ cat > mkmsg.py <<EOF
276 $ cat > mkmsg.py <<EOF
277 > import email.Message, sys
277 > import email.Message, sys
278 > msg = email.Message.Message()
278 > msg = email.Message.Message()
279 > patch = open(sys.argv[1], 'rb').read()
279 > patch = open(sys.argv[1], 'rb').read()
280 > msg.set_payload('email commit message\n' + patch)
280 > msg.set_payload('email commit message\n' + patch)
281 > msg['Subject'] = 'email patch'
281 > msg['Subject'] = 'email patch'
282 > msg['From'] = 'email patcher'
282 > msg['From'] = 'email patcher'
283 > file(sys.argv[2], 'wb').write(msg.as_string())
283 > file(sys.argv[2], 'wb').write(msg.as_string())
284 > EOF
284 > EOF
285
285
286
286
287 plain diff in email, subject, message body
287 plain diff in email, subject, message body
288
288
289 $ hg clone -r0 a b
289 $ hg clone -r0 a b
290 adding changesets
290 adding changesets
291 adding manifests
291 adding manifests
292 adding file changes
292 adding file changes
293 added 1 changesets with 2 changes to 2 files
293 added 1 changesets with 2 changes to 2 files
294 updating to branch default
294 updating to branch default
295 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
296 $ python mkmsg.py diffed-tip.patch msg.patch
296 $ python mkmsg.py diffed-tip.patch msg.patch
297 $ hg --cwd b import ../msg.patch
297 $ hg --cwd b import ../msg.patch
298 applying ../msg.patch
298 applying ../msg.patch
299 $ hg --cwd b tip | grep email
299 $ hg --cwd b tip | grep email
300 user: email patcher
300 user: email patcher
301 summary: email patch
301 summary: email patch
302 $ rm -r b
302 $ rm -r b
303
303
304
304
305 plain diff in email, no subject, message body
305 plain diff in email, no subject, message body
306
306
307 $ hg clone -r0 a b
307 $ hg clone -r0 a b
308 adding changesets
308 adding changesets
309 adding manifests
309 adding manifests
310 adding file changes
310 adding file changes
311 added 1 changesets with 2 changes to 2 files
311 added 1 changesets with 2 changes to 2 files
312 updating to branch default
312 updating to branch default
313 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
314 $ grep -v '^Subject:' msg.patch | hg --cwd b import -
314 $ grep -v '^Subject:' msg.patch | hg --cwd b import -
315 applying patch from stdin
315 applying patch from stdin
316 $ rm -r b
316 $ rm -r b
317
317
318
318
319 plain diff in email, subject, no message body
319 plain diff in email, subject, no message body
320
320
321 $ hg clone -r0 a b
321 $ hg clone -r0 a b
322 adding changesets
322 adding changesets
323 adding manifests
323 adding manifests
324 adding file changes
324 adding file changes
325 added 1 changesets with 2 changes to 2 files
325 added 1 changesets with 2 changes to 2 files
326 updating to branch default
326 updating to branch default
327 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
328 $ grep -v '^email ' msg.patch | hg --cwd b import -
328 $ grep -v '^email ' msg.patch | hg --cwd b import -
329 applying patch from stdin
329 applying patch from stdin
330 $ rm -r b
330 $ rm -r b
331
331
332
332
333 plain diff in email, no subject, no message body, should fail
333 plain diff in email, no subject, no message body, should fail
334
334
335 $ hg clone -r0 a b
335 $ hg clone -r0 a b
336 adding changesets
336 adding changesets
337 adding manifests
337 adding manifests
338 adding file changes
338 adding file changes
339 added 1 changesets with 2 changes to 2 files
339 added 1 changesets with 2 changes to 2 files
340 updating to branch default
340 updating to branch default
341 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
342 $ egrep -v '^(Subject|email)' msg.patch | hg --cwd b import -
342 $ egrep -v '^(Subject|email)' msg.patch | hg --cwd b import -
343 applying patch from stdin
343 applying patch from stdin
344 abort: empty commit message
344 abort: empty commit message
345 [255]
345 [255]
346 $ rm -r b
346 $ rm -r b
347
347
348
348
349 hg export in email, should use patch header
349 hg export in email, should use patch header
350
350
351 $ hg clone -r0 a b
351 $ hg clone -r0 a b
352 adding changesets
352 adding changesets
353 adding manifests
353 adding manifests
354 adding file changes
354 adding file changes
355 added 1 changesets with 2 changes to 2 files
355 added 1 changesets with 2 changes to 2 files
356 updating to branch default
356 updating to branch default
357 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
358 $ python mkmsg.py exported-tip.patch msg.patch
358 $ python mkmsg.py exported-tip.patch msg.patch
359 $ cat msg.patch | hg --cwd b import -
359 $ cat msg.patch | hg --cwd b import -
360 applying patch from stdin
360 applying patch from stdin
361 $ hg --cwd b tip | grep second
361 $ hg --cwd b tip | grep second
362 summary: second change
362 summary: second change
363 $ rm -r b
363 $ rm -r b
364
364
365
365
366 subject: duplicate detection, removal of [PATCH]
366 subject: duplicate detection, removal of [PATCH]
367 The '---' tests the gitsendmail handling without proper mail headers
367 The '---' tests the gitsendmail handling without proper mail headers
368
368
369 $ cat > mkmsg2.py <<EOF
369 $ cat > mkmsg2.py <<EOF
370 > import email.Message, sys
370 > import email.Message, sys
371 > msg = email.Message.Message()
371 > msg = email.Message.Message()
372 > patch = open(sys.argv[1], 'rb').read()
372 > patch = open(sys.argv[1], 'rb').read()
373 > msg.set_payload('email patch\n\nnext line\n---\n' + patch)
373 > msg.set_payload('email patch\n\nnext line\n---\n' + patch)
374 > msg['Subject'] = '[PATCH] email patch'
374 > msg['Subject'] = '[PATCH] email patch'
375 > msg['From'] = 'email patcher'
375 > msg['From'] = 'email patcher'
376 > file(sys.argv[2], 'wb').write(msg.as_string())
376 > file(sys.argv[2], 'wb').write(msg.as_string())
377 > EOF
377 > EOF
378
378
379
379
380 plain diff in email, [PATCH] subject, message body with subject
380 plain diff in email, [PATCH] subject, message body with subject
381
381
382 $ hg clone -r0 a b
382 $ hg clone -r0 a b
383 adding changesets
383 adding changesets
384 adding manifests
384 adding manifests
385 adding file changes
385 adding file changes
386 added 1 changesets with 2 changes to 2 files
386 added 1 changesets with 2 changes to 2 files
387 updating to branch default
387 updating to branch default
388 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
389 $ python mkmsg2.py diffed-tip.patch msg.patch
389 $ python mkmsg2.py diffed-tip.patch msg.patch
390 $ cat msg.patch | hg --cwd b import -
390 $ cat msg.patch | hg --cwd b import -
391 applying patch from stdin
391 applying patch from stdin
392 $ hg --cwd b tip --template '{desc}\n'
392 $ hg --cwd b tip --template '{desc}\n'
393 email patch
393 email patch
394
394
395 next line
395 next line
396 $ rm -r b
396 $ rm -r b
397
397
398
398
399 Issue963: Parent of working dir incorrect after import of multiple
399 Issue963: Parent of working dir incorrect after import of multiple
400 patches and rollback
400 patches and rollback
401
401
402 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
403 patches: import patch1 patch2; rollback
403 patches: import patch1 patch2; rollback
404
404
405 $ echo line 3 >> a/a
405 $ echo line 3 >> a/a
406 $ hg --cwd a ci -m'third change'
406 $ hg --cwd a ci -m'third change'
407 $ hg --cwd a export -o '../patch%R' 1 2
407 $ hg --cwd a export -o '../patch%R' 1 2
408 $ hg clone -qr0 a b
408 $ hg clone -qr0 a b
409 $ hg --cwd b parents --template 'parent: {rev}\n'
409 $ hg --cwd b parents --template 'parent: {rev}\n'
410 parent: 0
410 parent: 0
411 $ hg --cwd b import -v ../patch1 ../patch2
411 $ hg --cwd b import -v ../patch1 ../patch2
412 applying ../patch1
412 applying ../patch1
413 patching file a
413 patching file a
414 committing files:
414 committing files:
415 a
415 a
416 committing manifest
416 committing manifest
417 committing changelog
417 committing changelog
418 created 1d4bd90af0e4
418 created 1d4bd90af0e4
419 applying ../patch2
419 applying ../patch2
420 patching file a
420 patching file a
421 committing files:
421 committing files:
422 a
422 a
423 committing manifest
423 committing manifest
424 committing changelog
424 committing changelog
425 created 6d019af21222
425 created 6d019af21222
426 $ hg --cwd b rollback
426 $ hg --cwd b rollback
427 repository tip rolled back to revision 0 (undo import)
427 repository tip rolled back to revision 0 (undo import)
428 working directory now based on revision 0
428 working directory now based on revision 0
429 $ hg --cwd b parents --template 'parent: {rev}\n'
429 $ hg --cwd b parents --template 'parent: {rev}\n'
430 parent: 0
430 parent: 0
431 $ rm -r b
431 $ rm -r b
432
432
433
433
434 importing a patch in a subdirectory failed at the commit stage
434 importing a patch in a subdirectory failed at the commit stage
435
435
436 $ echo line 2 >> a/d1/d2/a
436 $ echo line 2 >> a/d1/d2/a
437 $ hg --cwd a ci -u someoneelse -d '1 0' -m'subdir change'
437 $ hg --cwd a ci -u someoneelse -d '1 0' -m'subdir change'
438
438
439 hg import in a subdirectory
439 hg import in a subdirectory
440
440
441 $ hg clone -r0 a b
441 $ hg clone -r0 a b
442 adding changesets
442 adding changesets
443 adding manifests
443 adding manifests
444 adding file changes
444 adding file changes
445 added 1 changesets with 2 changes to 2 files
445 added 1 changesets with 2 changes to 2 files
446 updating to branch default
446 updating to branch default
447 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
447 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
448 $ hg --cwd a export tip > tmp
448 $ hg --cwd a export tip > tmp
449 $ sed -e 's/d1\/d2\///' < tmp > subdir-tip.patch
449 $ sed -e 's/d1\/d2\///' < tmp > subdir-tip.patch
450 $ dir=`pwd`
450 $ dir=`pwd`
451 $ cd b/d1/d2 2>&1 > /dev/null
451 $ cd b/d1/d2 2>&1 > /dev/null
452 $ hg import ../../../subdir-tip.patch
452 $ hg import ../../../subdir-tip.patch
453 applying ../../../subdir-tip.patch
453 applying ../../../subdir-tip.patch
454 $ cd "$dir"
454 $ cd "$dir"
455
455
456 message should be 'subdir change'
456 message should be 'subdir change'
457 committer should be 'someoneelse'
457 committer should be 'someoneelse'
458
458
459 $ hg --cwd b tip
459 $ hg --cwd b tip
460 changeset: 1:3577f5aea227
460 changeset: 1:3577f5aea227
461 tag: tip
461 tag: tip
462 user: someoneelse
462 user: someoneelse
463 date: Thu Jan 01 00:00:01 1970 +0000
463 date: Thu Jan 01 00:00:01 1970 +0000
464 summary: subdir change
464 summary: subdir change
465
465
466
466
467 should be empty
467 should be empty
468
468
469 $ hg --cwd b status
469 $ hg --cwd b status
470
470
471
471
472 Test fuzziness (ambiguous patch location, fuzz=2)
472 Test fuzziness (ambiguous patch location, fuzz=2)
473
473
474 $ hg init fuzzy
474 $ hg init fuzzy
475 $ cd fuzzy
475 $ cd fuzzy
476 $ echo line1 > a
476 $ echo line1 > a
477 $ echo line0 >> a
477 $ echo line0 >> a
478 $ echo line3 >> a
478 $ echo line3 >> a
479 $ hg ci -Am adda
479 $ hg ci -Am adda
480 adding a
480 adding a
481 $ echo line1 > a
481 $ echo line1 > a
482 $ echo line2 >> a
482 $ echo line2 >> a
483 $ echo line0 >> a
483 $ echo line0 >> a
484 $ echo line3 >> a
484 $ echo line3 >> a
485 $ hg ci -m change a
485 $ hg ci -m change a
486 $ hg export tip > fuzzy-tip.patch
486 $ hg export tip > fuzzy-tip.patch
487 $ hg up -C 0
487 $ hg up -C 0
488 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
488 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
489 $ echo line1 > a
489 $ echo line1 > a
490 $ echo line0 >> a
490 $ echo line0 >> a
491 $ echo line1 >> a
491 $ echo line1 >> a
492 $ echo line0 >> a
492 $ echo line0 >> a
493 $ hg ci -m brancha
493 $ hg ci -m brancha
494 created new head
494 created new head
495 $ hg import --config patch.fuzz=0 -v fuzzy-tip.patch
495 $ hg import --config patch.fuzz=0 -v fuzzy-tip.patch
496 applying fuzzy-tip.patch
496 applying fuzzy-tip.patch
497 patching file a
497 patching file a
498 Hunk #1 FAILED at 0
498 Hunk #1 FAILED at 0
499 1 out of 1 hunks FAILED -- saving rejects to file a.rej
499 1 out of 1 hunks FAILED -- saving rejects to file a.rej
500 abort: patch failed to apply
500 abort: patch failed to apply
501 [255]
501 [255]
502 $ hg import --no-commit -v fuzzy-tip.patch
502 $ hg import --no-commit -v fuzzy-tip.patch
503 applying fuzzy-tip.patch
503 applying fuzzy-tip.patch
504 patching file a
504 patching file a
505 Hunk #1 succeeded at 2 with fuzz 1 (offset 0 lines).
505 Hunk #1 succeeded at 2 with fuzz 1 (offset 0 lines).
506 applied to working directory
506 applied to working directory
507 $ hg revert -a
507 $ hg revert -a
508 reverting a
508 reverting a
509
509
510
510
511 import with --no-commit should have written .hg/last-message.txt
511 import with --no-commit should have written .hg/last-message.txt
512
512
513 $ cat .hg/last-message.txt
513 $ cat .hg/last-message.txt
514 change (no-eol)
514 change (no-eol)
515
515
516
516
517 test fuzziness with eol=auto
517 test fuzziness with eol=auto
518
518
519 $ hg --config patch.eol=auto import --no-commit -v fuzzy-tip.patch
519 $ hg --config patch.eol=auto import --no-commit -v fuzzy-tip.patch
520 applying fuzzy-tip.patch
520 applying fuzzy-tip.patch
521 patching file a
521 patching file a
522 Hunk #1 succeeded at 2 with fuzz 1 (offset 0 lines).
522 Hunk #1 succeeded at 2 with fuzz 1 (offset 0 lines).
523 applied to working directory
523 applied to working directory
524 $ cd ..
524 $ cd ..
525
525
526
526
527 Test hunk touching empty files (issue906)
527 Test hunk touching empty files (issue906)
528
528
529 $ hg init empty
529 $ hg init empty
530 $ cd empty
530 $ cd empty
531 $ touch a
531 $ touch a
532 $ touch b1
532 $ touch b1
533 $ touch c1
533 $ touch c1
534 $ echo d > d
534 $ echo d > d
535 $ hg ci -Am init
535 $ hg ci -Am init
536 adding a
536 adding a
537 adding b1
537 adding b1
538 adding c1
538 adding c1
539 adding d
539 adding d
540 $ echo a > a
540 $ echo a > a
541 $ echo b > b1
541 $ echo b > b1
542 $ hg mv b1 b2
542 $ hg mv b1 b2
543 $ echo c > c1
543 $ echo c > c1
544 $ hg copy c1 c2
544 $ hg copy c1 c2
545 $ rm d
545 $ rm d
546 $ touch d
546 $ touch d
547 $ hg diff --git
547 $ hg diff --git
548 diff --git a/a b/a
548 diff --git a/a b/a
549 --- a/a
549 --- a/a
550 +++ b/a
550 +++ b/a
551 @@ -0,0 +1,1 @@
551 @@ -0,0 +1,1 @@
552 +a
552 +a
553 diff --git a/b1 b/b2
553 diff --git a/b1 b/b2
554 rename from b1
554 rename from b1
555 rename to b2
555 rename to b2
556 --- a/b1
556 --- a/b1
557 +++ b/b2
557 +++ b/b2
558 @@ -0,0 +1,1 @@
558 @@ -0,0 +1,1 @@
559 +b
559 +b
560 diff --git a/c1 b/c1
560 diff --git a/c1 b/c1
561 --- a/c1
561 --- a/c1
562 +++ b/c1
562 +++ b/c1
563 @@ -0,0 +1,1 @@
563 @@ -0,0 +1,1 @@
564 +c
564 +c
565 diff --git a/c1 b/c2
565 diff --git a/c1 b/c2
566 copy from c1
566 copy from c1
567 copy to c2
567 copy to c2
568 --- a/c1
568 --- a/c1
569 +++ b/c2
569 +++ b/c2
570 @@ -0,0 +1,1 @@
570 @@ -0,0 +1,1 @@
571 +c
571 +c
572 diff --git a/d b/d
572 diff --git a/d b/d
573 --- a/d
573 --- a/d
574 +++ b/d
574 +++ b/d
575 @@ -1,1 +0,0 @@
575 @@ -1,1 +0,0 @@
576 -d
576 -d
577 $ hg ci -m empty
577 $ hg ci -m empty
578 $ hg export --git tip > empty.diff
578 $ hg export --git tip > empty.diff
579 $ hg up -C 0
579 $ hg up -C 0
580 4 files updated, 0 files merged, 2 files removed, 0 files unresolved
580 4 files updated, 0 files merged, 2 files removed, 0 files unresolved
581 $ hg import empty.diff
581 $ hg import empty.diff
582 applying empty.diff
582 applying empty.diff
583 $ for name in a b1 b2 c1 c2 d; do
583 $ for name in a b1 b2 c1 c2 d; do
584 > echo % $name file
584 > echo % $name file
585 > test -f $name && cat $name
585 > test -f $name && cat $name
586 > done
586 > done
587 % a file
587 % a file
588 a
588 a
589 % b1 file
589 % b1 file
590 % b2 file
590 % b2 file
591 b
591 b
592 % c1 file
592 % c1 file
593 c
593 c
594 % c2 file
594 % c2 file
595 c
595 c
596 % d file
596 % d file
597 $ cd ..
597 $ cd ..
598
598
599
599
600 Test importing a patch ending with a binary file removal
600 Test importing a patch ending with a binary file removal
601
601
602 $ hg init binaryremoval
602 $ hg init binaryremoval
603 $ cd binaryremoval
603 $ cd binaryremoval
604 $ echo a > a
604 $ echo a > a
605 $ $PYTHON -c "file('b', 'wb').write('a\x00b')"
605 $ $PYTHON -c "file('b', 'wb').write('a\x00b')"
606 $ hg ci -Am addall
606 $ hg ci -Am addall
607 adding a
607 adding a
608 adding b
608 adding b
609 $ hg rm a
609 $ hg rm a
610 $ hg rm b
610 $ hg rm b
611 $ hg st
611 $ hg st
612 R a
612 R a
613 R b
613 R b
614 $ hg ci -m remove
614 $ hg ci -m remove
615 $ hg export --git . > remove.diff
615 $ hg export --git . > remove.diff
616 $ cat remove.diff | grep git
616 $ cat remove.diff | grep git
617 diff --git a/a b/a
617 diff --git a/a b/a
618 diff --git a/b b/b
618 diff --git a/b b/b
619 $ hg up -C 0
619 $ hg up -C 0
620 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
620 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
621 $ hg import remove.diff
621 $ hg import remove.diff
622 applying remove.diff
622 applying remove.diff
623 $ hg manifest
623 $ hg manifest
624 $ cd ..
624 $ cd ..
625
625
626
626
627 Issue927: test update+rename with common name
627 Issue927: test update+rename with common name
628
628
629 $ hg init t
629 $ hg init t
630 $ cd t
630 $ cd t
631 $ touch a
631 $ touch a
632 $ hg ci -Am t
632 $ hg ci -Am t
633 adding a
633 adding a
634 $ echo a > a
634 $ echo a > a
635
635
636 Here, bfile.startswith(afile)
636 Here, bfile.startswith(afile)
637
637
638 $ hg copy a a2
638 $ hg copy a a2
639 $ hg ci -m copya
639 $ hg ci -m copya
640 $ hg export --git tip > copy.diff
640 $ hg export --git tip > copy.diff
641 $ hg up -C 0
641 $ hg up -C 0
642 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
642 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
643 $ hg import copy.diff
643 $ hg import copy.diff
644 applying copy.diff
644 applying copy.diff
645
645
646 a should contain an 'a'
646 a should contain an 'a'
647
647
648 $ cat a
648 $ cat a
649 a
649 a
650
650
651 and a2 should have duplicated it
651 and a2 should have duplicated it
652
652
653 $ cat a2
653 $ cat a2
654 a
654 a
655 $ cd ..
655 $ cd ..
656
656
657
657
658 test -p0
658 test -p0
659
659
660 $ hg init p0
660 $ hg init p0
661 $ cd p0
661 $ cd p0
662 $ echo a > a
662 $ echo a > a
663 $ hg ci -Am t
663 $ hg ci -Am t
664 adding a
664 adding a
665 $ hg import -p foo
665 $ hg import -p foo
666 abort: invalid value 'foo' for option -p, expected int
666 abort: invalid value 'foo' for option -p, expected int
667 [255]
667 [255]
668 $ hg import -p0 - << EOF
668 $ hg import -p0 - << EOF
669 > foobar
669 > foobar
670 > --- a Sat Apr 12 22:43:58 2008 -0400
670 > --- a Sat Apr 12 22:43:58 2008 -0400
671 > +++ a Sat Apr 12 22:44:05 2008 -0400
671 > +++ a Sat Apr 12 22:44:05 2008 -0400
672 > @@ -1,1 +1,1 @@
672 > @@ -1,1 +1,1 @@
673 > -a
673 > -a
674 > +bb
674 > +bb
675 > EOF
675 > EOF
676 applying patch from stdin
676 applying patch from stdin
677 $ hg status
677 $ hg status
678 $ cat a
678 $ cat a
679 bb
679 bb
680
680
681 test --prefix
681 test --prefix
682
682
683 $ mkdir -p dir/dir2
683 $ mkdir -p dir/dir2
684 $ echo b > dir/dir2/b
684 $ echo b > dir/dir2/b
685 $ hg ci -Am b
685 $ hg ci -Am b
686 adding dir/dir2/b
686 adding dir/dir2/b
687 $ hg import -p2 --prefix dir - << EOF
687 $ hg import -p2 --prefix dir - << EOF
688 > foobar
688 > foobar
689 > --- drop1/drop2/dir2/b
689 > --- drop1/drop2/dir2/b
690 > +++ drop1/drop2/dir2/b
690 > +++ drop1/drop2/dir2/b
691 > @@ -1,1 +1,1 @@
691 > @@ -1,1 +1,1 @@
692 > -b
692 > -b
693 > +cc
693 > +cc
694 > EOF
694 > EOF
695 applying patch from stdin
695 applying patch from stdin
696 $ hg status
696 $ hg status
697 $ cat dir/dir2/b
697 $ cat dir/dir2/b
698 cc
698 cc
699 $ cd ..
699 $ cd ..
700
700
701
701
702 test paths outside repo root
702 test paths outside repo root
703
703
704 $ mkdir outside
704 $ mkdir outside
705 $ touch outside/foo
705 $ touch outside/foo
706 $ hg init inside
706 $ hg init inside
707 $ cd inside
707 $ cd inside
708 $ hg import - <<EOF
708 $ hg import - <<EOF
709 > diff --git a/a b/b
709 > diff --git a/a b/b
710 > rename from ../outside/foo
710 > rename from ../outside/foo
711 > rename to bar
711 > rename to bar
712 > EOF
712 > EOF
713 applying patch from stdin
713 applying patch from stdin
714 abort: path contains illegal component: ../outside/foo (glob)
714 abort: path contains illegal component: ../outside/foo (glob)
715 [255]
715 [255]
716 $ cd ..
716 $ cd ..
717
717
718
718
719 test import with similarity and git and strip (issue295 et al.)
719 test import with similarity and git and strip (issue295 et al.)
720
720
721 $ hg init sim
721 $ hg init sim
722 $ cd sim
722 $ cd sim
723 $ echo 'this is a test' > a
723 $ echo 'this is a test' > a
724 $ hg ci -Ama
724 $ hg ci -Ama
725 adding a
725 adding a
726 $ cat > ../rename.diff <<EOF
726 $ cat > ../rename.diff <<EOF
727 > diff --git a/foo/a b/foo/a
727 > diff --git a/foo/a b/foo/a
728 > deleted file mode 100644
728 > deleted file mode 100644
729 > --- a/foo/a
729 > --- a/foo/a
730 > +++ /dev/null
730 > +++ /dev/null
731 > @@ -1,1 +0,0 @@
731 > @@ -1,1 +0,0 @@
732 > -this is a test
732 > -this is a test
733 > diff --git a/foo/b b/foo/b
733 > diff --git a/foo/b b/foo/b
734 > new file mode 100644
734 > new file mode 100644
735 > --- /dev/null
735 > --- /dev/null
736 > +++ b/foo/b
736 > +++ b/foo/b
737 > @@ -0,0 +1,2 @@
737 > @@ -0,0 +1,2 @@
738 > +this is a test
738 > +this is a test
739 > +foo
739 > +foo
740 > EOF
740 > EOF
741 $ hg import --no-commit -v -s 1 ../rename.diff -p2
741 $ hg import --no-commit -v -s 1 ../rename.diff -p2
742 applying ../rename.diff
742 applying ../rename.diff
743 patching file a
743 patching file a
744 patching file b
744 patching file b
745 adding b
745 adding b
746 recording removal of a as rename to b (88% similar)
746 recording removal of a as rename to b (88% similar)
747 applied to working directory
747 applied to working directory
748 $ hg st -C
748 $ hg st -C
749 A b
749 A b
750 a
750 a
751 R a
751 R a
752 $ hg revert -a
752 $ hg revert -a
753 undeleting a
753 undeleting a
754 forgetting b
754 forgetting b
755 $ rm b
755 $ rm b
756 $ hg import --no-commit -v -s 100 ../rename.diff -p2
756 $ hg import --no-commit -v -s 100 ../rename.diff -p2
757 applying ../rename.diff
757 applying ../rename.diff
758 patching file a
758 patching file a
759 patching file b
759 patching file b
760 adding b
760 adding b
761 applied to working directory
761 applied to working directory
762 $ hg st -C
762 $ hg st -C
763 A b
763 A b
764 R a
764 R a
765 $ cd ..
765 $ cd ..
766
766
767
767
768 Issue1495: add empty file from the end of patch
768 Issue1495: add empty file from the end of patch
769
769
770 $ hg init addemptyend
770 $ hg init addemptyend
771 $ cd addemptyend
771 $ cd addemptyend
772 $ touch a
772 $ touch a
773 $ hg addremove
773 $ hg addremove
774 adding a
774 adding a
775 $ hg ci -m "commit"
775 $ hg ci -m "commit"
776 $ cat > a.patch <<EOF
776 $ cat > a.patch <<EOF
777 > add a, b
777 > add a, b
778 > diff --git a/a b/a
778 > diff --git a/a b/a
779 > --- a/a
779 > --- a/a
780 > +++ b/a
780 > +++ b/a
781 > @@ -0,0 +1,1 @@
781 > @@ -0,0 +1,1 @@
782 > +a
782 > +a
783 > diff --git a/b b/b
783 > diff --git a/b b/b
784 > new file mode 100644
784 > new file mode 100644
785 > EOF
785 > EOF
786 $ hg import --no-commit a.patch
786 $ hg import --no-commit a.patch
787 applying a.patch
787 applying a.patch
788
788
789 apply a good patch followed by an empty patch (mainly to ensure
789 apply a good patch followed by an empty patch (mainly to ensure
790 that dirstate is *not* updated when import crashes)
790 that dirstate is *not* updated when import crashes)
791 $ hg update -q -C .
791 $ hg update -q -C .
792 $ rm b
792 $ rm b
793 $ touch empty.patch
793 $ touch empty.patch
794 $ hg import a.patch empty.patch
794 $ hg import a.patch empty.patch
795 applying a.patch
795 applying a.patch
796 applying empty.patch
796 applying empty.patch
797 transaction abort!
797 transaction abort!
798 rollback completed
798 rollback completed
799 abort: empty.patch: no diffs found
799 abort: empty.patch: no diffs found
800 [255]
800 [255]
801 $ hg tip --template '{rev} {desc|firstline}\n'
801 $ hg tip --template '{rev} {desc|firstline}\n'
802 0 commit
802 0 commit
803 $ hg -q status
803 $ hg -q status
804 M a
804 M a
805 $ cd ..
805 $ cd ..
806
806
807 create file when source is not /dev/null
807 create file when source is not /dev/null
808
808
809 $ cat > create.patch <<EOF
809 $ cat > create.patch <<EOF
810 > diff -Naur proj-orig/foo proj-new/foo
810 > diff -Naur proj-orig/foo proj-new/foo
811 > --- proj-orig/foo 1969-12-31 16:00:00.000000000 -0800
811 > --- proj-orig/foo 1969-12-31 16:00:00.000000000 -0800
812 > +++ proj-new/foo 2009-07-17 16:50:45.801368000 -0700
812 > +++ proj-new/foo 2009-07-17 16:50:45.801368000 -0700
813 > @@ -0,0 +1,1 @@
813 > @@ -0,0 +1,1 @@
814 > +a
814 > +a
815 > EOF
815 > EOF
816
816
817 some people have patches like the following too
817 some people have patches like the following too
818
818
819 $ cat > create2.patch <<EOF
819 $ cat > create2.patch <<EOF
820 > diff -Naur proj-orig/foo proj-new/foo
820 > diff -Naur proj-orig/foo proj-new/foo
821 > --- proj-orig/foo.orig 1969-12-31 16:00:00.000000000 -0800
821 > --- proj-orig/foo.orig 1969-12-31 16:00:00.000000000 -0800
822 > +++ proj-new/foo 2009-07-17 16:50:45.801368000 -0700
822 > +++ proj-new/foo 2009-07-17 16:50:45.801368000 -0700
823 > @@ -0,0 +1,1 @@
823 > @@ -0,0 +1,1 @@
824 > +a
824 > +a
825 > EOF
825 > EOF
826 $ hg init oddcreate
826 $ hg init oddcreate
827 $ cd oddcreate
827 $ cd oddcreate
828 $ hg import --no-commit ../create.patch
828 $ hg import --no-commit ../create.patch
829 applying ../create.patch
829 applying ../create.patch
830 $ cat foo
830 $ cat foo
831 a
831 a
832 $ rm foo
832 $ rm foo
833 $ hg revert foo
833 $ hg revert foo
834 $ hg import --no-commit ../create2.patch
834 $ hg import --no-commit ../create2.patch
835 applying ../create2.patch
835 applying ../create2.patch
836 $ cat foo
836 $ cat foo
837 a
837 a
838
838
839 $ cd ..
839 $ cd ..
840
840
841 Issue1859: first line mistaken for email headers
841 Issue1859: first line mistaken for email headers
842
842
843 $ hg init emailconfusion
843 $ hg init emailconfusion
844 $ cd emailconfusion
844 $ cd emailconfusion
845 $ cat > a.patch <<EOF
845 $ cat > a.patch <<EOF
846 > module: summary
846 > module: summary
847 >
847 >
848 > description
848 > description
849 >
849 >
850 >
850 >
851 > diff -r 000000000000 -r 9b4c1e343b55 test.txt
851 > diff -r 000000000000 -r 9b4c1e343b55 test.txt
852 > --- /dev/null
852 > --- /dev/null
853 > +++ b/a
853 > +++ b/a
854 > @@ -0,0 +1,1 @@
854 > @@ -0,0 +1,1 @@
855 > +a
855 > +a
856 > EOF
856 > EOF
857 $ hg import -d '0 0' a.patch
857 $ hg import -d '0 0' a.patch
858 applying a.patch
858 applying a.patch
859 $ hg parents -v
859 $ hg parents -v
860 changeset: 0:5a681217c0ad
860 changeset: 0:5a681217c0ad
861 tag: tip
861 tag: tip
862 user: test
862 user: test
863 date: Thu Jan 01 00:00:00 1970 +0000
863 date: Thu Jan 01 00:00:00 1970 +0000
864 files: a
864 files: a
865 description:
865 description:
866 module: summary
866 module: summary
867
867
868 description
868 description
869
869
870
870
871 $ cd ..
871 $ cd ..
872
872
873
873
874 in commit message
874 in commit message
875
875
876 $ hg init commitconfusion
876 $ hg init commitconfusion
877 $ cd commitconfusion
877 $ cd commitconfusion
878 $ cat > a.patch <<EOF
878 $ cat > a.patch <<EOF
879 > module: summary
879 > module: summary
880 >
880 >
881 > --- description
881 > --- description
882 >
882 >
883 > diff --git a/a b/a
883 > diff --git a/a b/a
884 > new file mode 100644
884 > new file mode 100644
885 > --- /dev/null
885 > --- /dev/null
886 > +++ b/a
886 > +++ b/a
887 > @@ -0,0 +1,1 @@
887 > @@ -0,0 +1,1 @@
888 > +a
888 > +a
889 > EOF
889 > EOF
890 > hg import -d '0 0' a.patch
890 > hg import -d '0 0' a.patch
891 > hg parents -v
891 > hg parents -v
892 > cd ..
892 > cd ..
893 >
893 >
894 > echo '% tricky header splitting'
894 > echo '% tricky header splitting'
895 > cat > trickyheaders.patch <<EOF
895 > cat > trickyheaders.patch <<EOF
896 > From: User A <user@a>
896 > From: User A <user@a>
897 > Subject: [PATCH] from: tricky!
897 > Subject: [PATCH] from: tricky!
898 >
898 >
899 > # HG changeset patch
899 > # HG changeset patch
900 > # User User B
900 > # User User B
901 > # Date 1266264441 18000
901 > # Date 1266264441 18000
902 > # Branch stable
902 > # Branch stable
903 > # Node ID f2be6a1170ac83bf31cb4ae0bad00d7678115bc0
903 > # Node ID f2be6a1170ac83bf31cb4ae0bad00d7678115bc0
904 > # Parent 0000000000000000000000000000000000000000
904 > # Parent 0000000000000000000000000000000000000000
905 > from: tricky!
905 > from: tricky!
906 >
906 >
907 > That is not a header.
907 > That is not a header.
908 >
908 >
909 > diff -r 000000000000 -r f2be6a1170ac foo
909 > diff -r 000000000000 -r f2be6a1170ac foo
910 > --- /dev/null
910 > --- /dev/null
911 > +++ b/foo
911 > +++ b/foo
912 > @@ -0,0 +1,1 @@
912 > @@ -0,0 +1,1 @@
913 > +foo
913 > +foo
914 > EOF
914 > EOF
915 applying a.patch
915 applying a.patch
916 changeset: 0:f34d9187897d
916 changeset: 0:f34d9187897d
917 tag: tip
917 tag: tip
918 user: test
918 user: test
919 date: Thu Jan 01 00:00:00 1970 +0000
919 date: Thu Jan 01 00:00:00 1970 +0000
920 files: a
920 files: a
921 description:
921 description:
922 module: summary
922 module: summary
923
923
924
924
925 % tricky header splitting
925 % tricky header splitting
926
926
927 $ hg init trickyheaders
927 $ hg init trickyheaders
928 $ cd trickyheaders
928 $ cd trickyheaders
929 $ hg import -d '0 0' ../trickyheaders.patch
929 $ hg import -d '0 0' ../trickyheaders.patch
930 applying ../trickyheaders.patch
930 applying ../trickyheaders.patch
931 $ hg export --git tip
931 $ hg export --git tip
932 # HG changeset patch
932 # HG changeset patch
933 # User User B
933 # User User B
934 # Date 0 0
934 # Date 0 0
935 # Thu Jan 01 00:00:00 1970 +0000
935 # Thu Jan 01 00:00:00 1970 +0000
936 # Node ID eb56ab91903632294ac504838508cb370c0901d2
936 # Node ID eb56ab91903632294ac504838508cb370c0901d2
937 # Parent 0000000000000000000000000000000000000000
937 # Parent 0000000000000000000000000000000000000000
938 from: tricky!
938 from: tricky!
939
939
940 That is not a header.
940 That is not a header.
941
941
942 diff --git a/foo b/foo
942 diff --git a/foo b/foo
943 new file mode 100644
943 new file mode 100644
944 --- /dev/null
944 --- /dev/null
945 +++ b/foo
945 +++ b/foo
946 @@ -0,0 +1,1 @@
946 @@ -0,0 +1,1 @@
947 +foo
947 +foo
948 $ cd ..
948 $ cd ..
949
949
950
950
951 Issue2102: hg export and hg import speak different languages
951 Issue2102: hg export and hg import speak different languages
952
952
953 $ hg init issue2102
953 $ hg init issue2102
954 $ cd issue2102
954 $ cd issue2102
955 $ mkdir -p src/cmd/gc
955 $ mkdir -p src/cmd/gc
956 $ touch src/cmd/gc/mksys.bash
956 $ touch src/cmd/gc/mksys.bash
957 $ hg ci -Am init
957 $ hg ci -Am init
958 adding src/cmd/gc/mksys.bash
958 adding src/cmd/gc/mksys.bash
959 $ hg import - <<EOF
959 $ hg import - <<EOF
960 > # HG changeset patch
960 > # HG changeset patch
961 > # User Rob Pike
961 > # User Rob Pike
962 > # Date 1216685449 25200
962 > # Date 1216685449 25200
963 > # Node ID 03aa2b206f499ad6eb50e6e207b9e710d6409c98
963 > # Node ID 03aa2b206f499ad6eb50e6e207b9e710d6409c98
964 > # Parent 93d10138ad8df586827ca90b4ddb5033e21a3a84
964 > # Parent 93d10138ad8df586827ca90b4ddb5033e21a3a84
965 > help management of empty pkg and lib directories in perforce
965 > help management of empty pkg and lib directories in perforce
966 >
966 >
967 > R=gri
967 > R=gri
968 > DELTA=4 (4 added, 0 deleted, 0 changed)
968 > DELTA=4 (4 added, 0 deleted, 0 changed)
969 > OCL=13328
969 > OCL=13328
970 > CL=13328
970 > CL=13328
971 >
971 >
972 > diff --git a/lib/place-holder b/lib/place-holder
972 > diff --git a/lib/place-holder b/lib/place-holder
973 > new file mode 100644
973 > new file mode 100644
974 > --- /dev/null
974 > --- /dev/null
975 > +++ b/lib/place-holder
975 > +++ b/lib/place-holder
976 > @@ -0,0 +1,2 @@
976 > @@ -0,0 +1,2 @@
977 > +perforce does not maintain empty directories.
977 > +perforce does not maintain empty directories.
978 > +this file helps.
978 > +this file helps.
979 > diff --git a/pkg/place-holder b/pkg/place-holder
979 > diff --git a/pkg/place-holder b/pkg/place-holder
980 > new file mode 100644
980 > new file mode 100644
981 > --- /dev/null
981 > --- /dev/null
982 > +++ b/pkg/place-holder
982 > +++ b/pkg/place-holder
983 > @@ -0,0 +1,2 @@
983 > @@ -0,0 +1,2 @@
984 > +perforce does not maintain empty directories.
984 > +perforce does not maintain empty directories.
985 > +this file helps.
985 > +this file helps.
986 > diff --git a/src/cmd/gc/mksys.bash b/src/cmd/gc/mksys.bash
986 > diff --git a/src/cmd/gc/mksys.bash b/src/cmd/gc/mksys.bash
987 > old mode 100644
987 > old mode 100644
988 > new mode 100755
988 > new mode 100755
989 > EOF
989 > EOF
990 applying patch from stdin
990 applying patch from stdin
991
991
992 #if execbit
992 #if execbit
993
993
994 $ hg sum
994 $ hg sum
995 parent: 1:d59915696727 tip
995 parent: 1:d59915696727 tip
996 help management of empty pkg and lib directories in perforce
996 help management of empty pkg and lib directories in perforce
997 branch: default
997 branch: default
998 commit: (clean)
998 commit: (clean)
999 update: (current)
999 update: (current)
1000 phases: 2 draft
1000 phases: 2 draft
1001
1001
1002 $ hg diff --git -c tip
1002 $ hg diff --git -c tip
1003 diff --git a/lib/place-holder b/lib/place-holder
1003 diff --git a/lib/place-holder b/lib/place-holder
1004 new file mode 100644
1004 new file mode 100644
1005 --- /dev/null
1005 --- /dev/null
1006 +++ b/lib/place-holder
1006 +++ b/lib/place-holder
1007 @@ -0,0 +1,2 @@
1007 @@ -0,0 +1,2 @@
1008 +perforce does not maintain empty directories.
1008 +perforce does not maintain empty directories.
1009 +this file helps.
1009 +this file helps.
1010 diff --git a/pkg/place-holder b/pkg/place-holder
1010 diff --git a/pkg/place-holder b/pkg/place-holder
1011 new file mode 100644
1011 new file mode 100644
1012 --- /dev/null
1012 --- /dev/null
1013 +++ b/pkg/place-holder
1013 +++ b/pkg/place-holder
1014 @@ -0,0 +1,2 @@
1014 @@ -0,0 +1,2 @@
1015 +perforce does not maintain empty directories.
1015 +perforce does not maintain empty directories.
1016 +this file helps.
1016 +this file helps.
1017 diff --git a/src/cmd/gc/mksys.bash b/src/cmd/gc/mksys.bash
1017 diff --git a/src/cmd/gc/mksys.bash b/src/cmd/gc/mksys.bash
1018 old mode 100644
1018 old mode 100644
1019 new mode 100755
1019 new mode 100755
1020
1020
1021 #else
1021 #else
1022
1022
1023 $ hg sum
1023 $ hg sum
1024 parent: 1:28f089cc9ccc tip
1024 parent: 1:28f089cc9ccc tip
1025 help management of empty pkg and lib directories in perforce
1025 help management of empty pkg and lib directories in perforce
1026 branch: default
1026 branch: default
1027 commit: (clean)
1027 commit: (clean)
1028 update: (current)
1028 update: (current)
1029 phases: 2 draft
1029 phases: 2 draft
1030
1030
1031 $ hg diff --git -c tip
1031 $ hg diff --git -c tip
1032 diff --git a/lib/place-holder b/lib/place-holder
1032 diff --git a/lib/place-holder b/lib/place-holder
1033 new file mode 100644
1033 new file mode 100644
1034 --- /dev/null
1034 --- /dev/null
1035 +++ b/lib/place-holder
1035 +++ b/lib/place-holder
1036 @@ -0,0 +1,2 @@
1036 @@ -0,0 +1,2 @@
1037 +perforce does not maintain empty directories.
1037 +perforce does not maintain empty directories.
1038 +this file helps.
1038 +this file helps.
1039 diff --git a/pkg/place-holder b/pkg/place-holder
1039 diff --git a/pkg/place-holder b/pkg/place-holder
1040 new file mode 100644
1040 new file mode 100644
1041 --- /dev/null
1041 --- /dev/null
1042 +++ b/pkg/place-holder
1042 +++ b/pkg/place-holder
1043 @@ -0,0 +1,2 @@
1043 @@ -0,0 +1,2 @@
1044 +perforce does not maintain empty directories.
1044 +perforce does not maintain empty directories.
1045 +this file helps.
1045 +this file helps.
1046
1046
1047 /* The mode change for mksys.bash is missing here, because on platforms */
1047 /* The mode change for mksys.bash is missing here, because on platforms */
1048 /* that don't support execbits, mode changes in patches are ignored when */
1048 /* that don't support execbits, mode changes in patches are ignored when */
1049 /* they are imported. This is obviously also the reason for why the hash */
1049 /* they are imported. This is obviously also the reason for why the hash */
1050 /* in the created changeset is different to the one you see above the */
1050 /* in the created changeset is different to the one you see above the */
1051 /* #else clause */
1051 /* #else clause */
1052
1052
1053 #endif
1053 #endif
1054 $ cd ..
1054 $ cd ..
1055
1055
1056
1056
1057 diff lines looking like headers
1057 diff lines looking like headers
1058
1058
1059 $ hg init difflineslikeheaders
1059 $ hg init difflineslikeheaders
1060 $ cd difflineslikeheaders
1060 $ cd difflineslikeheaders
1061 $ echo a >a
1061 $ echo a >a
1062 $ echo b >b
1062 $ echo b >b
1063 $ echo c >c
1063 $ echo c >c
1064 $ hg ci -Am1
1064 $ hg ci -Am1
1065 adding a
1065 adding a
1066 adding b
1066 adding b
1067 adding c
1067 adding c
1068
1068
1069 $ echo "key: value" >>a
1069 $ echo "key: value" >>a
1070 $ echo "key: value" >>b
1070 $ echo "key: value" >>b
1071 $ echo "foo" >>c
1071 $ echo "foo" >>c
1072 $ hg ci -m2
1072 $ hg ci -m2
1073
1073
1074 $ hg up -C 0
1074 $ hg up -C 0
1075 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
1075 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
1076 $ hg diff --git -c1 >want
1076 $ hg diff --git -c1 >want
1077 $ hg diff -c1 | hg import --no-commit -
1077 $ hg diff -c1 | hg import --no-commit -
1078 applying patch from stdin
1078 applying patch from stdin
1079 $ hg diff --git >have
1079 $ hg diff --git >have
1080 $ diff want have
1080 $ diff want have
1081 $ cd ..
1081 $ cd ..
1082
1082
1083 import a unified diff with no lines of context (diff -U0)
1083 import a unified diff with no lines of context (diff -U0)
1084
1084
1085 $ hg init diffzero
1085 $ hg init diffzero
1086 $ cd diffzero
1086 $ cd diffzero
1087 $ cat > f << EOF
1087 $ cat > f << EOF
1088 > c2
1088 > c2
1089 > c4
1089 > c4
1090 > c5
1090 > c5
1091 > EOF
1091 > EOF
1092 $ hg commit -Am0
1092 $ hg commit -Am0
1093 adding f
1093 adding f
1094
1094
1095 $ hg import --no-commit - << EOF
1095 $ hg import --no-commit - << EOF
1096 > # HG changeset patch
1096 > # HG changeset patch
1097 > # User test
1097 > # User test
1098 > # Date 0 0
1098 > # Date 0 0
1099 > # Node ID f4974ab632f3dee767567b0576c0ec9a4508575c
1099 > # Node ID f4974ab632f3dee767567b0576c0ec9a4508575c
1100 > # Parent 8679a12a975b819fae5f7ad3853a2886d143d794
1100 > # Parent 8679a12a975b819fae5f7ad3853a2886d143d794
1101 > 1
1101 > 1
1102 > diff -r 8679a12a975b -r f4974ab632f3 f
1102 > diff -r 8679a12a975b -r f4974ab632f3 f
1103 > --- a/f Thu Jan 01 00:00:00 1970 +0000
1103 > --- a/f Thu Jan 01 00:00:00 1970 +0000
1104 > +++ b/f Thu Jan 01 00:00:00 1970 +0000
1104 > +++ b/f Thu Jan 01 00:00:00 1970 +0000
1105 > @@ -0,0 +1,1 @@
1105 > @@ -0,0 +1,1 @@
1106 > +c1
1106 > +c1
1107 > @@ -1,0 +3,1 @@
1107 > @@ -1,0 +3,1 @@
1108 > +c3
1108 > +c3
1109 > @@ -3,1 +4,0 @@
1109 > @@ -3,1 +4,0 @@
1110 > -c5
1110 > -c5
1111 > EOF
1111 > EOF
1112 applying patch from stdin
1112 applying patch from stdin
1113
1113
1114 $ cat f
1114 $ cat f
1115 c1
1115 c1
1116 c2
1116 c2
1117 c3
1117 c3
1118 c4
1118 c4
1119
1119
1120 $ cd ..
1120 $ cd ..
1121
1121
1122 no segfault while importing a unified diff which start line is zero but chunk
1122 no segfault while importing a unified diff which start line is zero but chunk
1123 size is non-zero
1123 size is non-zero
1124
1124
1125 $ hg init startlinezero
1125 $ hg init startlinezero
1126 $ cd startlinezero
1126 $ cd startlinezero
1127 $ echo foo > foo
1127 $ echo foo > foo
1128 $ hg commit -Amfoo
1128 $ hg commit -Amfoo
1129 adding foo
1129 adding foo
1130
1130
1131 $ hg import --no-commit - << EOF
1131 $ hg import --no-commit - << EOF
1132 > diff a/foo b/foo
1132 > diff a/foo b/foo
1133 > --- a/foo
1133 > --- a/foo
1134 > +++ b/foo
1134 > +++ b/foo
1135 > @@ -0,1 +0,1 @@
1135 > @@ -0,1 +0,1 @@
1136 > foo
1136 > foo
1137 > EOF
1137 > EOF
1138 applying patch from stdin
1138 applying patch from stdin
1139
1139
1140 $ cd ..
1140 $ cd ..
1141
1141
1142 Test corner case involving fuzz and skew
1142 Test corner case involving fuzz and skew
1143
1143
1144 $ hg init morecornercases
1144 $ hg init morecornercases
1145 $ cd morecornercases
1145 $ cd morecornercases
1146
1146
1147 $ cat > 01-no-context-beginning-of-file.diff <<EOF
1147 $ cat > 01-no-context-beginning-of-file.diff <<EOF
1148 > diff --git a/a b/a
1148 > diff --git a/a b/a
1149 > --- a/a
1149 > --- a/a
1150 > +++ b/a
1150 > +++ b/a
1151 > @@ -1,0 +1,1 @@
1151 > @@ -1,0 +1,1 @@
1152 > +line
1152 > +line
1153 > EOF
1153 > EOF
1154
1154
1155 $ cat > 02-no-context-middle-of-file.diff <<EOF
1155 $ cat > 02-no-context-middle-of-file.diff <<EOF
1156 > diff --git a/a b/a
1156 > diff --git a/a b/a
1157 > --- a/a
1157 > --- a/a
1158 > +++ b/a
1158 > +++ b/a
1159 > @@ -1,1 +1,1 @@
1159 > @@ -1,1 +1,1 @@
1160 > -2
1160 > -2
1161 > +add some skew
1161 > +add some skew
1162 > @@ -2,0 +2,1 @@
1162 > @@ -2,0 +2,1 @@
1163 > +line
1163 > +line
1164 > EOF
1164 > EOF
1165
1165
1166 $ cat > 03-no-context-end-of-file.diff <<EOF
1166 $ cat > 03-no-context-end-of-file.diff <<EOF
1167 > diff --git a/a b/a
1167 > diff --git a/a b/a
1168 > --- a/a
1168 > --- a/a
1169 > +++ b/a
1169 > +++ b/a
1170 > @@ -10,0 +10,1 @@
1170 > @@ -10,0 +10,1 @@
1171 > +line
1171 > +line
1172 > EOF
1172 > EOF
1173
1173
1174 $ cat > 04-middle-of-file-completely-fuzzed.diff <<EOF
1174 $ cat > 04-middle-of-file-completely-fuzzed.diff <<EOF
1175 > diff --git a/a b/a
1175 > diff --git a/a b/a
1176 > --- a/a
1176 > --- a/a
1177 > +++ b/a
1177 > +++ b/a
1178 > @@ -1,1 +1,1 @@
1178 > @@ -1,1 +1,1 @@
1179 > -2
1179 > -2
1180 > +add some skew
1180 > +add some skew
1181 > @@ -2,2 +2,3 @@
1181 > @@ -2,2 +2,3 @@
1182 > not matching, should fuzz
1182 > not matching, should fuzz
1183 > ... a bit
1183 > ... a bit
1184 > +line
1184 > +line
1185 > EOF
1185 > EOF
1186
1186
1187 $ cat > a <<EOF
1187 $ cat > a <<EOF
1188 > 1
1188 > 1
1189 > 2
1189 > 2
1190 > 3
1190 > 3
1191 > 4
1191 > 4
1192 > EOF
1192 > EOF
1193 $ hg ci -Am adda a
1193 $ hg ci -Am adda a
1194 $ for p in *.diff; do
1194 $ for p in *.diff; do
1195 > hg import -v --no-commit $p
1195 > hg import -v --no-commit $p
1196 > cat a
1196 > cat a
1197 > hg revert -aqC a
1197 > hg revert -aqC a
1198 > # patch -p1 < $p
1198 > # patch -p1 < $p
1199 > # cat a
1199 > # cat a
1200 > # hg revert -aC a
1200 > # hg revert -aC a
1201 > done
1201 > done
1202 applying 01-no-context-beginning-of-file.diff
1202 applying 01-no-context-beginning-of-file.diff
1203 patching file a
1203 patching file a
1204 applied to working directory
1204 applied to working directory
1205 1
1205 1
1206 line
1206 line
1207 2
1207 2
1208 3
1208 3
1209 4
1209 4
1210 applying 02-no-context-middle-of-file.diff
1210 applying 02-no-context-middle-of-file.diff
1211 patching file a
1211 patching file a
1212 Hunk #1 succeeded at 2 (offset 1 lines).
1212 Hunk #1 succeeded at 2 (offset 1 lines).
1213 Hunk #2 succeeded at 4 (offset 1 lines).
1213 Hunk #2 succeeded at 4 (offset 1 lines).
1214 applied to working directory
1214 applied to working directory
1215 1
1215 1
1216 add some skew
1216 add some skew
1217 3
1217 3
1218 line
1218 line
1219 4
1219 4
1220 applying 03-no-context-end-of-file.diff
1220 applying 03-no-context-end-of-file.diff
1221 patching file a
1221 patching file a
1222 Hunk #1 succeeded at 5 (offset -6 lines).
1222 Hunk #1 succeeded at 5 (offset -6 lines).
1223 applied to working directory
1223 applied to working directory
1224 1
1224 1
1225 2
1225 2
1226 3
1226 3
1227 4
1227 4
1228 line
1228 line
1229 applying 04-middle-of-file-completely-fuzzed.diff
1229 applying 04-middle-of-file-completely-fuzzed.diff
1230 patching file a
1230 patching file a
1231 Hunk #1 succeeded at 2 (offset 1 lines).
1231 Hunk #1 succeeded at 2 (offset 1 lines).
1232 Hunk #2 succeeded at 5 with fuzz 2 (offset 1 lines).
1232 Hunk #2 succeeded at 5 with fuzz 2 (offset 1 lines).
1233 applied to working directory
1233 applied to working directory
1234 1
1234 1
1235 add some skew
1235 add some skew
1236 3
1236 3
1237 4
1237 4
1238 line
1238 line
1239 $ cd ..
1239 $ cd ..
1240
1240
1241 Test partial application
1241 Test partial application
1242 ------------------------
1242 ------------------------
1243
1243
1244 prepare a stack of patches depending on each other
1244 prepare a stack of patches depending on each other
1245
1245
1246 $ hg init partial
1246 $ hg init partial
1247 $ cd partial
1247 $ cd partial
1248 $ cat << EOF > a
1248 $ cat << EOF > a
1249 > one
1249 > one
1250 > two
1250 > two
1251 > three
1251 > three
1252 > four
1252 > four
1253 > five
1253 > five
1254 > six
1254 > six
1255 > seven
1255 > seven
1256 > EOF
1256 > EOF
1257 $ hg add a
1257 $ hg add a
1258 $ echo 'b' > b
1258 $ echo 'b' > b
1259 $ hg add b
1259 $ hg add b
1260 $ hg commit -m 'initial' -u Babar
1260 $ hg commit -m 'initial' -u Babar
1261 $ cat << EOF > a
1261 $ cat << EOF > a
1262 > one
1262 > one
1263 > two
1263 > two
1264 > 3
1264 > 3
1265 > four
1265 > four
1266 > five
1266 > five
1267 > six
1267 > six
1268 > seven
1268 > seven
1269 > EOF
1269 > EOF
1270 $ hg commit -m 'three' -u Celeste
1270 $ hg commit -m 'three' -u Celeste
1271 $ cat << EOF > a
1271 $ cat << EOF > a
1272 > one
1272 > one
1273 > two
1273 > two
1274 > 3
1274 > 3
1275 > 4
1275 > 4
1276 > five
1276 > five
1277 > six
1277 > six
1278 > seven
1278 > seven
1279 > EOF
1279 > EOF
1280 $ hg commit -m 'four' -u Rataxes
1280 $ hg commit -m 'four' -u Rataxes
1281 $ cat << EOF > a
1281 $ cat << EOF > a
1282 > one
1282 > one
1283 > two
1283 > two
1284 > 3
1284 > 3
1285 > 4
1285 > 4
1286 > 5
1286 > 5
1287 > six
1287 > six
1288 > seven
1288 > seven
1289 > EOF
1289 > EOF
1290 $ echo bb >> b
1290 $ echo bb >> b
1291 $ hg commit -m 'five' -u Arthur
1291 $ hg commit -m 'five' -u Arthur
1292 $ echo 'Babar' > jungle
1292 $ echo 'Babar' > jungle
1293 $ hg add jungle
1293 $ hg add jungle
1294 $ hg ci -m 'jungle' -u Zephir
1294 $ hg ci -m 'jungle' -u Zephir
1295 $ echo 'Celeste' >> jungle
1295 $ echo 'Celeste' >> jungle
1296 $ hg ci -m 'extended jungle' -u Cornelius
1296 $ hg ci -m 'extended jungle' -u Cornelius
1297 $ hg log -G --template '{desc|firstline} [{author}] {diffstat}\n'
1297 $ hg log -G --template '{desc|firstline} [{author}] {diffstat}\n'
1298 @ extended jungle [Cornelius] 1: +1/-0
1298 @ extended jungle [Cornelius] 1: +1/-0
1299 |
1299 |
1300 o jungle [Zephir] 1: +1/-0
1300 o jungle [Zephir] 1: +1/-0
1301 |
1301 |
1302 o five [Arthur] 2: +2/-1
1302 o five [Arthur] 2: +2/-1
1303 |
1303 |
1304 o four [Rataxes] 1: +1/-1
1304 o four [Rataxes] 1: +1/-1
1305 |
1305 |
1306 o three [Celeste] 1: +1/-1
1306 o three [Celeste] 1: +1/-1
1307 |
1307 |
1308 o initial [Babar] 2: +8/-0
1308 o initial [Babar] 2: +8/-0
1309
1309
1310
1310
1311 Importing with some success and some errors:
1311 Importing with some success and some errors:
1312
1312
1313 $ hg update --rev 'desc(initial)'
1313 $ hg update --rev 'desc(initial)'
1314 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
1314 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
1315 $ hg export --rev 'desc(five)' | hg import --partial -
1315 $ hg export --rev 'desc(five)' | hg import --partial -
1316 applying patch from stdin
1316 applying patch from stdin
1317 patching file a
1317 patching file a
1318 Hunk #1 FAILED at 1
1318 Hunk #1 FAILED at 1
1319 1 out of 1 hunks FAILED -- saving rejects to file a.rej
1319 1 out of 1 hunks FAILED -- saving rejects to file a.rej
1320 patch applied partially
1320 patch applied partially
1321 (fix the .rej files and run `hg commit --amend`)
1321 (fix the .rej files and run `hg commit --amend`)
1322 [1]
1322 [1]
1323
1323
1324 $ hg log -G --template '{desc|firstline} [{author}] {diffstat}\n'
1324 $ hg log -G --template '{desc|firstline} [{author}] {diffstat}\n'
1325 @ five [Arthur] 1: +1/-0
1325 @ five [Arthur] 1: +1/-0
1326 |
1326 |
1327 | o extended jungle [Cornelius] 1: +1/-0
1327 | o extended jungle [Cornelius] 1: +1/-0
1328 | |
1328 | |
1329 | o jungle [Zephir] 1: +1/-0
1329 | o jungle [Zephir] 1: +1/-0
1330 | |
1330 | |
1331 | o five [Arthur] 2: +2/-1
1331 | o five [Arthur] 2: +2/-1
1332 | |
1332 | |
1333 | o four [Rataxes] 1: +1/-1
1333 | o four [Rataxes] 1: +1/-1
1334 | |
1334 | |
1335 | o three [Celeste] 1: +1/-1
1335 | o three [Celeste] 1: +1/-1
1336 |/
1336 |/
1337 o initial [Babar] 2: +8/-0
1337 o initial [Babar] 2: +8/-0
1338
1338
1339 $ hg export
1339 $ hg export
1340 # HG changeset patch
1340 # HG changeset patch
1341 # User Arthur
1341 # User Arthur
1342 # Date 0 0
1342 # Date 0 0
1343 # Thu Jan 01 00:00:00 1970 +0000
1343 # Thu Jan 01 00:00:00 1970 +0000
1344 # Node ID 26e6446bb2526e2be1037935f5fca2b2706f1509
1344 # Node ID 26e6446bb2526e2be1037935f5fca2b2706f1509
1345 # Parent 8e4f0351909eae6b9cf68c2c076cb54c42b54b2e
1345 # Parent 8e4f0351909eae6b9cf68c2c076cb54c42b54b2e
1346 five
1346 five
1347
1347
1348 diff -r 8e4f0351909e -r 26e6446bb252 b
1348 diff -r 8e4f0351909e -r 26e6446bb252 b
1349 --- a/b Thu Jan 01 00:00:00 1970 +0000
1349 --- a/b Thu Jan 01 00:00:00 1970 +0000
1350 +++ b/b Thu Jan 01 00:00:00 1970 +0000
1350 +++ b/b Thu Jan 01 00:00:00 1970 +0000
1351 @@ -1,1 +1,2 @@
1351 @@ -1,1 +1,2 @@
1352 b
1352 b
1353 +bb
1353 +bb
1354 $ hg status -c .
1354 $ hg status -c .
1355 C a
1355 C a
1356 C b
1356 C b
1357 $ ls
1357 $ ls
1358 a
1358 a
1359 a.rej
1359 a.rej
1360 b
1360 b
1361
1361
1362 Importing with zero success:
1362 Importing with zero success:
1363
1363
1364 $ hg update --rev 'desc(initial)'
1364 $ hg update --rev 'desc(initial)'
1365 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1365 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1366 $ hg export --rev 'desc(four)' | hg import --partial -
1366 $ hg export --rev 'desc(four)' | hg import --partial -
1367 applying patch from stdin
1367 applying patch from stdin
1368 patching file a
1368 patching file a
1369 Hunk #1 FAILED at 0
1369 Hunk #1 FAILED at 0
1370 1 out of 1 hunks FAILED -- saving rejects to file a.rej
1370 1 out of 1 hunks FAILED -- saving rejects to file a.rej
1371 patch applied partially
1371 patch applied partially
1372 (fix the .rej files and run `hg commit --amend`)
1372 (fix the .rej files and run `hg commit --amend`)
1373 [1]
1373 [1]
1374
1374
1375 $ hg log -G --template '{desc|firstline} [{author}] {diffstat}\n'
1375 $ hg log -G --template '{desc|firstline} [{author}] {diffstat}\n'
1376 @ four [Rataxes] 0: +0/-0
1376 @ four [Rataxes] 0: +0/-0
1377 |
1377 |
1378 | o five [Arthur] 1: +1/-0
1378 | o five [Arthur] 1: +1/-0
1379 |/
1379 |/
1380 | o extended jungle [Cornelius] 1: +1/-0
1380 | o extended jungle [Cornelius] 1: +1/-0
1381 | |
1381 | |
1382 | o jungle [Zephir] 1: +1/-0
1382 | o jungle [Zephir] 1: +1/-0
1383 | |
1383 | |
1384 | o five [Arthur] 2: +2/-1
1384 | o five [Arthur] 2: +2/-1
1385 | |
1385 | |
1386 | o four [Rataxes] 1: +1/-1
1386 | o four [Rataxes] 1: +1/-1
1387 | |
1387 | |
1388 | o three [Celeste] 1: +1/-1
1388 | o three [Celeste] 1: +1/-1
1389 |/
1389 |/
1390 o initial [Babar] 2: +8/-0
1390 o initial [Babar] 2: +8/-0
1391
1391
1392 $ hg export
1392 $ hg export
1393 # HG changeset patch
1393 # HG changeset patch
1394 # User Rataxes
1394 # User Rataxes
1395 # Date 0 0
1395 # Date 0 0
1396 # Thu Jan 01 00:00:00 1970 +0000
1396 # Thu Jan 01 00:00:00 1970 +0000
1397 # Node ID cb9b1847a74d9ad52e93becaf14b98dbcc274e1e
1397 # Node ID cb9b1847a74d9ad52e93becaf14b98dbcc274e1e
1398 # Parent 8e4f0351909eae6b9cf68c2c076cb54c42b54b2e
1398 # Parent 8e4f0351909eae6b9cf68c2c076cb54c42b54b2e
1399 four
1399 four
1400
1400
1401 $ hg status -c .
1401 $ hg status -c .
1402 C a
1402 C a
1403 C b
1403 C b
1404 $ ls
1404 $ ls
1405 a
1405 a
1406 a.rej
1406 a.rej
1407 b
1407 b
1408
1408
1409 Importing with unknown file:
1409 Importing with unknown file:
1410
1410
1411 $ hg update --rev 'desc(initial)'
1411 $ hg update --rev 'desc(initial)'
1412 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1412 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1413 $ hg export --rev 'desc("extended jungle")' | hg import --partial -
1413 $ hg export --rev 'desc("extended jungle")' | hg import --partial -
1414 applying patch from stdin
1414 applying patch from stdin
1415 unable to find 'jungle' for patching
1415 unable to find 'jungle' for patching
1416 1 out of 1 hunks FAILED -- saving rejects to file jungle.rej
1416 1 out of 1 hunks FAILED -- saving rejects to file jungle.rej
1417 patch applied partially
1417 patch applied partially
1418 (fix the .rej files and run `hg commit --amend`)
1418 (fix the .rej files and run `hg commit --amend`)
1419 [1]
1419 [1]
1420
1420
1421 $ hg log -G --template '{desc|firstline} [{author}] {diffstat}\n'
1421 $ hg log -G --template '{desc|firstline} [{author}] {diffstat}\n'
1422 @ extended jungle [Cornelius] 0: +0/-0
1422 @ extended jungle [Cornelius] 0: +0/-0
1423 |
1423 |
1424 | o four [Rataxes] 0: +0/-0
1424 | o four [Rataxes] 0: +0/-0
1425 |/
1425 |/
1426 | o five [Arthur] 1: +1/-0
1426 | o five [Arthur] 1: +1/-0
1427 |/
1427 |/
1428 | o extended jungle [Cornelius] 1: +1/-0
1428 | o extended jungle [Cornelius] 1: +1/-0
1429 | |
1429 | |
1430 | o jungle [Zephir] 1: +1/-0
1430 | o jungle [Zephir] 1: +1/-0
1431 | |
1431 | |
1432 | o five [Arthur] 2: +2/-1
1432 | o five [Arthur] 2: +2/-1
1433 | |
1433 | |
1434 | o four [Rataxes] 1: +1/-1
1434 | o four [Rataxes] 1: +1/-1
1435 | |
1435 | |
1436 | o three [Celeste] 1: +1/-1
1436 | o three [Celeste] 1: +1/-1
1437 |/
1437 |/
1438 o initial [Babar] 2: +8/-0
1438 o initial [Babar] 2: +8/-0
1439
1439
1440 $ hg export
1440 $ hg export
1441 # HG changeset patch
1441 # HG changeset patch
1442 # User Cornelius
1442 # User Cornelius
1443 # Date 0 0
1443 # Date 0 0
1444 # Thu Jan 01 00:00:00 1970 +0000
1444 # Thu Jan 01 00:00:00 1970 +0000
1445 # Node ID 1fb1f86bef43c5a75918178f8d23c29fb0a7398d
1445 # Node ID 1fb1f86bef43c5a75918178f8d23c29fb0a7398d
1446 # Parent 8e4f0351909eae6b9cf68c2c076cb54c42b54b2e
1446 # Parent 8e4f0351909eae6b9cf68c2c076cb54c42b54b2e
1447 extended jungle
1447 extended jungle
1448
1448
1449 $ hg status -c .
1449 $ hg status -c .
1450 C a
1450 C a
1451 C b
1451 C b
1452 $ ls
1452 $ ls
1453 a
1453 a
1454 a.rej
1454 a.rej
1455 b
1455 b
1456 jungle.rej
1456 jungle.rej
1457
1457
1458 Importing multiple failing patches:
1458 Importing multiple failing patches:
1459
1459
1460 $ hg update --rev 'desc(initial)'
1460 $ hg update --rev 'desc(initial)'
1461 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1461 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1462 $ echo 'B' > b # just to make another commit
1462 $ echo 'B' > b # just to make another commit
1463 $ hg commit -m "a new base"
1463 $ hg commit -m "a new base"
1464 created new head
1464 created new head
1465 $ hg export --rev 'desc("four") + desc("extended jungle")' | hg import --partial -
1465 $ hg export --rev 'desc("four") + desc("extended jungle")' | hg import --partial -
1466 applying patch from stdin
1466 applying patch from stdin
1467 patching file a
1467 patching file a
1468 Hunk #1 FAILED at 0
1468 Hunk #1 FAILED at 0
1469 1 out of 1 hunks FAILED -- saving rejects to file a.rej
1469 1 out of 1 hunks FAILED -- saving rejects to file a.rej
1470 patch applied partially
1470 patch applied partially
1471 (fix the .rej files and run `hg commit --amend`)
1471 (fix the .rej files and run `hg commit --amend`)
1472 [1]
1472 [1]
1473 $ hg log -G --template '{desc|firstline} [{author}] {diffstat}\n'
1473 $ hg log -G --template '{desc|firstline} [{author}] {diffstat}\n'
1474 @ four [Rataxes] 0: +0/-0
1474 @ four [Rataxes] 0: +0/-0
1475 |
1475 |
1476 o a new base [test] 1: +1/-1
1476 o a new base [test] 1: +1/-1
1477 |
1477 |
1478 | o extended jungle [Cornelius] 0: +0/-0
1478 | o extended jungle [Cornelius] 0: +0/-0
1479 |/
1479 |/
1480 | o four [Rataxes] 0: +0/-0
1480 | o four [Rataxes] 0: +0/-0
1481 |/
1481 |/
1482 | o five [Arthur] 1: +1/-0
1482 | o five [Arthur] 1: +1/-0
1483 |/
1483 |/
1484 | o extended jungle [Cornelius] 1: +1/-0
1484 | o extended jungle [Cornelius] 1: +1/-0
1485 | |
1485 | |
1486 | o jungle [Zephir] 1: +1/-0
1486 | o jungle [Zephir] 1: +1/-0
1487 | |
1487 | |
1488 | o five [Arthur] 2: +2/-1
1488 | o five [Arthur] 2: +2/-1
1489 | |
1489 | |
1490 | o four [Rataxes] 1: +1/-1
1490 | o four [Rataxes] 1: +1/-1
1491 | |
1491 | |
1492 | o three [Celeste] 1: +1/-1
1492 | o three [Celeste] 1: +1/-1
1493 |/
1493 |/
1494 o initial [Babar] 2: +8/-0
1494 o initial [Babar] 2: +8/-0
1495
1495
1496 $ hg export
1496 $ hg export
1497 # HG changeset patch
1497 # HG changeset patch
1498 # User Rataxes
1498 # User Rataxes
1499 # Date 0 0
1499 # Date 0 0
1500 # Thu Jan 01 00:00:00 1970 +0000
1500 # Thu Jan 01 00:00:00 1970 +0000
1501 # Node ID a9d7b6d0ffbb4eb12b7d5939250fcd42e8930a1d
1501 # Node ID a9d7b6d0ffbb4eb12b7d5939250fcd42e8930a1d
1502 # Parent f59f8d2e95a8ca5b1b4ca64320140da85f3b44fd
1502 # Parent f59f8d2e95a8ca5b1b4ca64320140da85f3b44fd
1503 four
1503 four
1504
1504
1505 $ hg status -c .
1505 $ hg status -c .
1506 C a
1506 C a
1507 C b
1507 C b
1508
1509 Importing some extra header
1510 ===========================
1511
1512 $ cat > $TESTTMP/parseextra.py <<EOF
1513 > import mercurial.patch
1514 > import mercurial.cmdutil
1515 >
1516 > def processfoo(repo, data, extra, opts):
1517 > if 'foo' in data:
1518 > extra['foo'] = data['foo']
1519 >
1520 > mercurial.patch.patchheadermap.append(('Foo', 'foo'))
1521 > mercurial.cmdutil.extrapreimport.append('foo')
1522 > mercurial.cmdutil.extrapreimportmap['foo'] = processfoo
1523 > EOF
1524 $ printf "[extensions]\nparseextra=$TESTTMP/parseextra.py" >> $HGRCPATH
1525 $ hg up -C tip
1526 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1527 $ cat > $TESTTMP/foo.patch <<EOF
1528 > # HG changeset patch
1529 > # User Rataxes
1530 > # Date 0 0
1531 > # Thu Jan 01 00:00:00 1970 +0000
1532 > # Foo bar
1533 > height
1534 >
1535 > --- a/a Thu Jan 01 00:00:00 1970 +0000
1536 > +++ b/a Wed Oct 07 09:17:44 2015 +0000
1537 > @@ -5,3 +5,4 @@
1538 > five
1539 > six
1540 > seven
1541 > +heigt
1542 > EOF
1543 $ hg import $TESTTMP/foo.patch
1544 applying $TESTTMP/foo.patch
1545 $ hg log --debug -r . | grep extra
1546 extra: branch=default
1547 extra: foo=bar
General Comments 0
You need to be logged in to leave comments. Login now