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