##// END OF EJS Templates
obsmarker: precnode was renamed into prednode...
Boris Feld -
r33856:eae63a9e default
parent child Browse files
Show More
@@ -1,3862 +1,3862 b''
1 # cmdutil.py - help for command processing in mercurial
1 # cmdutil.py - help for command processing in mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import errno
10 import errno
11 import itertools
11 import itertools
12 import os
12 import os
13 import re
13 import re
14 import tempfile
14 import tempfile
15
15
16 from .i18n import _
16 from .i18n import _
17 from .node import (
17 from .node import (
18 hex,
18 hex,
19 nullid,
19 nullid,
20 nullrev,
20 nullrev,
21 short,
21 short,
22 )
22 )
23
23
24 from . import (
24 from . import (
25 bookmarks,
25 bookmarks,
26 changelog,
26 changelog,
27 copies,
27 copies,
28 crecord as crecordmod,
28 crecord as crecordmod,
29 dirstateguard,
29 dirstateguard,
30 encoding,
30 encoding,
31 error,
31 error,
32 formatter,
32 formatter,
33 graphmod,
33 graphmod,
34 match as matchmod,
34 match as matchmod,
35 obsolete,
35 obsolete,
36 patch,
36 patch,
37 pathutil,
37 pathutil,
38 phases,
38 phases,
39 pycompat,
39 pycompat,
40 registrar,
40 registrar,
41 revlog,
41 revlog,
42 revset,
42 revset,
43 scmutil,
43 scmutil,
44 smartset,
44 smartset,
45 templatekw,
45 templatekw,
46 templater,
46 templater,
47 util,
47 util,
48 vfs as vfsmod,
48 vfs as vfsmod,
49 )
49 )
50 stringio = util.stringio
50 stringio = util.stringio
51
51
52 # templates of common command options
52 # templates of common command options
53
53
54 dryrunopts = [
54 dryrunopts = [
55 ('n', 'dry-run', None,
55 ('n', 'dry-run', None,
56 _('do not perform actions, just print output')),
56 _('do not perform actions, just print output')),
57 ]
57 ]
58
58
59 remoteopts = [
59 remoteopts = [
60 ('e', 'ssh', '',
60 ('e', 'ssh', '',
61 _('specify ssh command to use'), _('CMD')),
61 _('specify ssh command to use'), _('CMD')),
62 ('', 'remotecmd', '',
62 ('', 'remotecmd', '',
63 _('specify hg command to run on the remote side'), _('CMD')),
63 _('specify hg command to run on the remote side'), _('CMD')),
64 ('', 'insecure', None,
64 ('', 'insecure', None,
65 _('do not verify server certificate (ignoring web.cacerts config)')),
65 _('do not verify server certificate (ignoring web.cacerts config)')),
66 ]
66 ]
67
67
68 walkopts = [
68 walkopts = [
69 ('I', 'include', [],
69 ('I', 'include', [],
70 _('include names matching the given patterns'), _('PATTERN')),
70 _('include names matching the given patterns'), _('PATTERN')),
71 ('X', 'exclude', [],
71 ('X', 'exclude', [],
72 _('exclude names matching the given patterns'), _('PATTERN')),
72 _('exclude names matching the given patterns'), _('PATTERN')),
73 ]
73 ]
74
74
75 commitopts = [
75 commitopts = [
76 ('m', 'message', '',
76 ('m', 'message', '',
77 _('use text as commit message'), _('TEXT')),
77 _('use text as commit message'), _('TEXT')),
78 ('l', 'logfile', '',
78 ('l', 'logfile', '',
79 _('read commit message from file'), _('FILE')),
79 _('read commit message from file'), _('FILE')),
80 ]
80 ]
81
81
82 commitopts2 = [
82 commitopts2 = [
83 ('d', 'date', '',
83 ('d', 'date', '',
84 _('record the specified date as commit date'), _('DATE')),
84 _('record the specified date as commit date'), _('DATE')),
85 ('u', 'user', '',
85 ('u', 'user', '',
86 _('record the specified user as committer'), _('USER')),
86 _('record the specified user as committer'), _('USER')),
87 ]
87 ]
88
88
89 # hidden for now
89 # hidden for now
90 formatteropts = [
90 formatteropts = [
91 ('T', 'template', '',
91 ('T', 'template', '',
92 _('display with template (EXPERIMENTAL)'), _('TEMPLATE')),
92 _('display with template (EXPERIMENTAL)'), _('TEMPLATE')),
93 ]
93 ]
94
94
95 templateopts = [
95 templateopts = [
96 ('', 'style', '',
96 ('', 'style', '',
97 _('display using template map file (DEPRECATED)'), _('STYLE')),
97 _('display using template map file (DEPRECATED)'), _('STYLE')),
98 ('T', 'template', '',
98 ('T', 'template', '',
99 _('display with template'), _('TEMPLATE')),
99 _('display with template'), _('TEMPLATE')),
100 ]
100 ]
101
101
102 logopts = [
102 logopts = [
103 ('p', 'patch', None, _('show patch')),
103 ('p', 'patch', None, _('show patch')),
104 ('g', 'git', None, _('use git extended diff format')),
104 ('g', 'git', None, _('use git extended diff format')),
105 ('l', 'limit', '',
105 ('l', 'limit', '',
106 _('limit number of changes displayed'), _('NUM')),
106 _('limit number of changes displayed'), _('NUM')),
107 ('M', 'no-merges', None, _('do not show merges')),
107 ('M', 'no-merges', None, _('do not show merges')),
108 ('', 'stat', None, _('output diffstat-style summary of changes')),
108 ('', 'stat', None, _('output diffstat-style summary of changes')),
109 ('G', 'graph', None, _("show the revision DAG")),
109 ('G', 'graph', None, _("show the revision DAG")),
110 ] + templateopts
110 ] + templateopts
111
111
112 diffopts = [
112 diffopts = [
113 ('a', 'text', None, _('treat all files as text')),
113 ('a', 'text', None, _('treat all files as text')),
114 ('g', 'git', None, _('use git extended diff format')),
114 ('g', 'git', None, _('use git extended diff format')),
115 ('', 'binary', None, _('generate binary diffs in git mode (default)')),
115 ('', 'binary', None, _('generate binary diffs in git mode (default)')),
116 ('', 'nodates', None, _('omit dates from diff headers'))
116 ('', 'nodates', None, _('omit dates from diff headers'))
117 ]
117 ]
118
118
119 diffwsopts = [
119 diffwsopts = [
120 ('w', 'ignore-all-space', None,
120 ('w', 'ignore-all-space', None,
121 _('ignore white space when comparing lines')),
121 _('ignore white space when comparing lines')),
122 ('b', 'ignore-space-change', None,
122 ('b', 'ignore-space-change', None,
123 _('ignore changes in the amount of white space')),
123 _('ignore changes in the amount of white space')),
124 ('B', 'ignore-blank-lines', None,
124 ('B', 'ignore-blank-lines', None,
125 _('ignore changes whose lines are all blank')),
125 _('ignore changes whose lines are all blank')),
126 ]
126 ]
127
127
128 diffopts2 = [
128 diffopts2 = [
129 ('', 'noprefix', None, _('omit a/ and b/ prefixes from filenames')),
129 ('', 'noprefix', None, _('omit a/ and b/ prefixes from filenames')),
130 ('p', 'show-function', None, _('show which function each change is in')),
130 ('p', 'show-function', None, _('show which function each change is in')),
131 ('', 'reverse', None, _('produce a diff that undoes the changes')),
131 ('', 'reverse', None, _('produce a diff that undoes the changes')),
132 ] + diffwsopts + [
132 ] + diffwsopts + [
133 ('U', 'unified', '',
133 ('U', 'unified', '',
134 _('number of lines of context to show'), _('NUM')),
134 _('number of lines of context to show'), _('NUM')),
135 ('', 'stat', None, _('output diffstat-style summary of changes')),
135 ('', 'stat', None, _('output diffstat-style summary of changes')),
136 ('', 'root', '', _('produce diffs relative to subdirectory'), _('DIR')),
136 ('', 'root', '', _('produce diffs relative to subdirectory'), _('DIR')),
137 ]
137 ]
138
138
139 mergetoolopts = [
139 mergetoolopts = [
140 ('t', 'tool', '', _('specify merge tool')),
140 ('t', 'tool', '', _('specify merge tool')),
141 ]
141 ]
142
142
143 similarityopts = [
143 similarityopts = [
144 ('s', 'similarity', '',
144 ('s', 'similarity', '',
145 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
145 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
146 ]
146 ]
147
147
148 subrepoopts = [
148 subrepoopts = [
149 ('S', 'subrepos', None,
149 ('S', 'subrepos', None,
150 _('recurse into subrepositories'))
150 _('recurse into subrepositories'))
151 ]
151 ]
152
152
153 debugrevlogopts = [
153 debugrevlogopts = [
154 ('c', 'changelog', False, _('open changelog')),
154 ('c', 'changelog', False, _('open changelog')),
155 ('m', 'manifest', False, _('open manifest')),
155 ('m', 'manifest', False, _('open manifest')),
156 ('', 'dir', '', _('open directory manifest')),
156 ('', 'dir', '', _('open directory manifest')),
157 ]
157 ]
158
158
159 # special string such that everything below this line will be ingored in the
159 # special string such that everything below this line will be ingored in the
160 # editor text
160 # editor text
161 _linebelow = "^HG: ------------------------ >8 ------------------------$"
161 _linebelow = "^HG: ------------------------ >8 ------------------------$"
162
162
163 def ishunk(x):
163 def ishunk(x):
164 hunkclasses = (crecordmod.uihunk, patch.recordhunk)
164 hunkclasses = (crecordmod.uihunk, patch.recordhunk)
165 return isinstance(x, hunkclasses)
165 return isinstance(x, hunkclasses)
166
166
167 def newandmodified(chunks, originalchunks):
167 def newandmodified(chunks, originalchunks):
168 newlyaddedandmodifiedfiles = set()
168 newlyaddedandmodifiedfiles = set()
169 for chunk in chunks:
169 for chunk in chunks:
170 if ishunk(chunk) and chunk.header.isnewfile() and chunk not in \
170 if ishunk(chunk) and chunk.header.isnewfile() and chunk not in \
171 originalchunks:
171 originalchunks:
172 newlyaddedandmodifiedfiles.add(chunk.header.filename())
172 newlyaddedandmodifiedfiles.add(chunk.header.filename())
173 return newlyaddedandmodifiedfiles
173 return newlyaddedandmodifiedfiles
174
174
175 def parsealiases(cmd):
175 def parsealiases(cmd):
176 return cmd.lstrip("^").split("|")
176 return cmd.lstrip("^").split("|")
177
177
178 def setupwrapcolorwrite(ui):
178 def setupwrapcolorwrite(ui):
179 # wrap ui.write so diff output can be labeled/colorized
179 # wrap ui.write so diff output can be labeled/colorized
180 def wrapwrite(orig, *args, **kw):
180 def wrapwrite(orig, *args, **kw):
181 label = kw.pop('label', '')
181 label = kw.pop('label', '')
182 for chunk, l in patch.difflabel(lambda: args):
182 for chunk, l in patch.difflabel(lambda: args):
183 orig(chunk, label=label + l)
183 orig(chunk, label=label + l)
184
184
185 oldwrite = ui.write
185 oldwrite = ui.write
186 def wrap(*args, **kwargs):
186 def wrap(*args, **kwargs):
187 return wrapwrite(oldwrite, *args, **kwargs)
187 return wrapwrite(oldwrite, *args, **kwargs)
188 setattr(ui, 'write', wrap)
188 setattr(ui, 'write', wrap)
189 return oldwrite
189 return oldwrite
190
190
191 def filterchunks(ui, originalhunks, usecurses, testfile, operation=None):
191 def filterchunks(ui, originalhunks, usecurses, testfile, operation=None):
192 if usecurses:
192 if usecurses:
193 if testfile:
193 if testfile:
194 recordfn = crecordmod.testdecorator(testfile,
194 recordfn = crecordmod.testdecorator(testfile,
195 crecordmod.testchunkselector)
195 crecordmod.testchunkselector)
196 else:
196 else:
197 recordfn = crecordmod.chunkselector
197 recordfn = crecordmod.chunkselector
198
198
199 return crecordmod.filterpatch(ui, originalhunks, recordfn, operation)
199 return crecordmod.filterpatch(ui, originalhunks, recordfn, operation)
200
200
201 else:
201 else:
202 return patch.filterpatch(ui, originalhunks, operation)
202 return patch.filterpatch(ui, originalhunks, operation)
203
203
204 def recordfilter(ui, originalhunks, operation=None):
204 def recordfilter(ui, originalhunks, operation=None):
205 """ Prompts the user to filter the originalhunks and return a list of
205 """ Prompts the user to filter the originalhunks and return a list of
206 selected hunks.
206 selected hunks.
207 *operation* is used for to build ui messages to indicate the user what
207 *operation* is used for to build ui messages to indicate the user what
208 kind of filtering they are doing: reverting, committing, shelving, etc.
208 kind of filtering they are doing: reverting, committing, shelving, etc.
209 (see patch.filterpatch).
209 (see patch.filterpatch).
210 """
210 """
211 usecurses = crecordmod.checkcurses(ui)
211 usecurses = crecordmod.checkcurses(ui)
212 testfile = ui.config('experimental', 'crecordtest')
212 testfile = ui.config('experimental', 'crecordtest')
213 oldwrite = setupwrapcolorwrite(ui)
213 oldwrite = setupwrapcolorwrite(ui)
214 try:
214 try:
215 newchunks, newopts = filterchunks(ui, originalhunks, usecurses,
215 newchunks, newopts = filterchunks(ui, originalhunks, usecurses,
216 testfile, operation)
216 testfile, operation)
217 finally:
217 finally:
218 ui.write = oldwrite
218 ui.write = oldwrite
219 return newchunks, newopts
219 return newchunks, newopts
220
220
221 def dorecord(ui, repo, commitfunc, cmdsuggest, backupall,
221 def dorecord(ui, repo, commitfunc, cmdsuggest, backupall,
222 filterfn, *pats, **opts):
222 filterfn, *pats, **opts):
223 from . import merge as mergemod
223 from . import merge as mergemod
224 opts = pycompat.byteskwargs(opts)
224 opts = pycompat.byteskwargs(opts)
225 if not ui.interactive():
225 if not ui.interactive():
226 if cmdsuggest:
226 if cmdsuggest:
227 msg = _('running non-interactively, use %s instead') % cmdsuggest
227 msg = _('running non-interactively, use %s instead') % cmdsuggest
228 else:
228 else:
229 msg = _('running non-interactively')
229 msg = _('running non-interactively')
230 raise error.Abort(msg)
230 raise error.Abort(msg)
231
231
232 # make sure username is set before going interactive
232 # make sure username is set before going interactive
233 if not opts.get('user'):
233 if not opts.get('user'):
234 ui.username() # raise exception, username not provided
234 ui.username() # raise exception, username not provided
235
235
236 def recordfunc(ui, repo, message, match, opts):
236 def recordfunc(ui, repo, message, match, opts):
237 """This is generic record driver.
237 """This is generic record driver.
238
238
239 Its job is to interactively filter local changes, and
239 Its job is to interactively filter local changes, and
240 accordingly prepare working directory into a state in which the
240 accordingly prepare working directory into a state in which the
241 job can be delegated to a non-interactive commit command such as
241 job can be delegated to a non-interactive commit command such as
242 'commit' or 'qrefresh'.
242 'commit' or 'qrefresh'.
243
243
244 After the actual job is done by non-interactive command, the
244 After the actual job is done by non-interactive command, the
245 working directory is restored to its original state.
245 working directory is restored to its original state.
246
246
247 In the end we'll record interesting changes, and everything else
247 In the end we'll record interesting changes, and everything else
248 will be left in place, so the user can continue working.
248 will be left in place, so the user can continue working.
249 """
249 """
250
250
251 checkunfinished(repo, commit=True)
251 checkunfinished(repo, commit=True)
252 wctx = repo[None]
252 wctx = repo[None]
253 merge = len(wctx.parents()) > 1
253 merge = len(wctx.parents()) > 1
254 if merge:
254 if merge:
255 raise error.Abort(_('cannot partially commit a merge '
255 raise error.Abort(_('cannot partially commit a merge '
256 '(use "hg commit" instead)'))
256 '(use "hg commit" instead)'))
257
257
258 def fail(f, msg):
258 def fail(f, msg):
259 raise error.Abort('%s: %s' % (f, msg))
259 raise error.Abort('%s: %s' % (f, msg))
260
260
261 force = opts.get('force')
261 force = opts.get('force')
262 if not force:
262 if not force:
263 vdirs = []
263 vdirs = []
264 match.explicitdir = vdirs.append
264 match.explicitdir = vdirs.append
265 match.bad = fail
265 match.bad = fail
266
266
267 status = repo.status(match=match)
267 status = repo.status(match=match)
268 if not force:
268 if not force:
269 repo.checkcommitpatterns(wctx, vdirs, match, status, fail)
269 repo.checkcommitpatterns(wctx, vdirs, match, status, fail)
270 diffopts = patch.difffeatureopts(ui, opts=opts, whitespace=True)
270 diffopts = patch.difffeatureopts(ui, opts=opts, whitespace=True)
271 diffopts.nodates = True
271 diffopts.nodates = True
272 diffopts.git = True
272 diffopts.git = True
273 diffopts.showfunc = True
273 diffopts.showfunc = True
274 originaldiff = patch.diff(repo, changes=status, opts=diffopts)
274 originaldiff = patch.diff(repo, changes=status, opts=diffopts)
275 originalchunks = patch.parsepatch(originaldiff)
275 originalchunks = patch.parsepatch(originaldiff)
276
276
277 # 1. filter patch, since we are intending to apply subset of it
277 # 1. filter patch, since we are intending to apply subset of it
278 try:
278 try:
279 chunks, newopts = filterfn(ui, originalchunks)
279 chunks, newopts = filterfn(ui, originalchunks)
280 except patch.PatchError as err:
280 except patch.PatchError as err:
281 raise error.Abort(_('error parsing patch: %s') % err)
281 raise error.Abort(_('error parsing patch: %s') % err)
282 opts.update(newopts)
282 opts.update(newopts)
283
283
284 # We need to keep a backup of files that have been newly added and
284 # We need to keep a backup of files that have been newly added and
285 # modified during the recording process because there is a previous
285 # modified during the recording process because there is a previous
286 # version without the edit in the workdir
286 # version without the edit in the workdir
287 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
287 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
288 contenders = set()
288 contenders = set()
289 for h in chunks:
289 for h in chunks:
290 try:
290 try:
291 contenders.update(set(h.files()))
291 contenders.update(set(h.files()))
292 except AttributeError:
292 except AttributeError:
293 pass
293 pass
294
294
295 changed = status.modified + status.added + status.removed
295 changed = status.modified + status.added + status.removed
296 newfiles = [f for f in changed if f in contenders]
296 newfiles = [f for f in changed if f in contenders]
297 if not newfiles:
297 if not newfiles:
298 ui.status(_('no changes to record\n'))
298 ui.status(_('no changes to record\n'))
299 return 0
299 return 0
300
300
301 modified = set(status.modified)
301 modified = set(status.modified)
302
302
303 # 2. backup changed files, so we can restore them in the end
303 # 2. backup changed files, so we can restore them in the end
304
304
305 if backupall:
305 if backupall:
306 tobackup = changed
306 tobackup = changed
307 else:
307 else:
308 tobackup = [f for f in newfiles if f in modified or f in \
308 tobackup = [f for f in newfiles if f in modified or f in \
309 newlyaddedandmodifiedfiles]
309 newlyaddedandmodifiedfiles]
310 backups = {}
310 backups = {}
311 if tobackup:
311 if tobackup:
312 backupdir = repo.vfs.join('record-backups')
312 backupdir = repo.vfs.join('record-backups')
313 try:
313 try:
314 os.mkdir(backupdir)
314 os.mkdir(backupdir)
315 except OSError as err:
315 except OSError as err:
316 if err.errno != errno.EEXIST:
316 if err.errno != errno.EEXIST:
317 raise
317 raise
318 try:
318 try:
319 # backup continues
319 # backup continues
320 for f in tobackup:
320 for f in tobackup:
321 fd, tmpname = tempfile.mkstemp(prefix=f.replace('/', '_')+'.',
321 fd, tmpname = tempfile.mkstemp(prefix=f.replace('/', '_')+'.',
322 dir=backupdir)
322 dir=backupdir)
323 os.close(fd)
323 os.close(fd)
324 ui.debug('backup %r as %r\n' % (f, tmpname))
324 ui.debug('backup %r as %r\n' % (f, tmpname))
325 util.copyfile(repo.wjoin(f), tmpname, copystat=True)
325 util.copyfile(repo.wjoin(f), tmpname, copystat=True)
326 backups[f] = tmpname
326 backups[f] = tmpname
327
327
328 fp = stringio()
328 fp = stringio()
329 for c in chunks:
329 for c in chunks:
330 fname = c.filename()
330 fname = c.filename()
331 if fname in backups:
331 if fname in backups:
332 c.write(fp)
332 c.write(fp)
333 dopatch = fp.tell()
333 dopatch = fp.tell()
334 fp.seek(0)
334 fp.seek(0)
335
335
336 # 2.5 optionally review / modify patch in text editor
336 # 2.5 optionally review / modify patch in text editor
337 if opts.get('review', False):
337 if opts.get('review', False):
338 patchtext = (crecordmod.diffhelptext
338 patchtext = (crecordmod.diffhelptext
339 + crecordmod.patchhelptext
339 + crecordmod.patchhelptext
340 + fp.read())
340 + fp.read())
341 reviewedpatch = ui.edit(patchtext, "",
341 reviewedpatch = ui.edit(patchtext, "",
342 extra={"suffix": ".diff"},
342 extra={"suffix": ".diff"},
343 repopath=repo.path)
343 repopath=repo.path)
344 fp.truncate(0)
344 fp.truncate(0)
345 fp.write(reviewedpatch)
345 fp.write(reviewedpatch)
346 fp.seek(0)
346 fp.seek(0)
347
347
348 [os.unlink(repo.wjoin(c)) for c in newlyaddedandmodifiedfiles]
348 [os.unlink(repo.wjoin(c)) for c in newlyaddedandmodifiedfiles]
349 # 3a. apply filtered patch to clean repo (clean)
349 # 3a. apply filtered patch to clean repo (clean)
350 if backups:
350 if backups:
351 # Equivalent to hg.revert
351 # Equivalent to hg.revert
352 m = scmutil.matchfiles(repo, backups.keys())
352 m = scmutil.matchfiles(repo, backups.keys())
353 mergemod.update(repo, repo.dirstate.p1(),
353 mergemod.update(repo, repo.dirstate.p1(),
354 False, True, matcher=m)
354 False, True, matcher=m)
355
355
356 # 3b. (apply)
356 # 3b. (apply)
357 if dopatch:
357 if dopatch:
358 try:
358 try:
359 ui.debug('applying patch\n')
359 ui.debug('applying patch\n')
360 ui.debug(fp.getvalue())
360 ui.debug(fp.getvalue())
361 patch.internalpatch(ui, repo, fp, 1, eolmode=None)
361 patch.internalpatch(ui, repo, fp, 1, eolmode=None)
362 except patch.PatchError as err:
362 except patch.PatchError as err:
363 raise error.Abort(str(err))
363 raise error.Abort(str(err))
364 del fp
364 del fp
365
365
366 # 4. We prepared working directory according to filtered
366 # 4. We prepared working directory according to filtered
367 # patch. Now is the time to delegate the job to
367 # patch. Now is the time to delegate the job to
368 # commit/qrefresh or the like!
368 # commit/qrefresh or the like!
369
369
370 # Make all of the pathnames absolute.
370 # Make all of the pathnames absolute.
371 newfiles = [repo.wjoin(nf) for nf in newfiles]
371 newfiles = [repo.wjoin(nf) for nf in newfiles]
372 return commitfunc(ui, repo, *newfiles, **opts)
372 return commitfunc(ui, repo, *newfiles, **opts)
373 finally:
373 finally:
374 # 5. finally restore backed-up files
374 # 5. finally restore backed-up files
375 try:
375 try:
376 dirstate = repo.dirstate
376 dirstate = repo.dirstate
377 for realname, tmpname in backups.iteritems():
377 for realname, tmpname in backups.iteritems():
378 ui.debug('restoring %r to %r\n' % (tmpname, realname))
378 ui.debug('restoring %r to %r\n' % (tmpname, realname))
379
379
380 if dirstate[realname] == 'n':
380 if dirstate[realname] == 'n':
381 # without normallookup, restoring timestamp
381 # without normallookup, restoring timestamp
382 # may cause partially committed files
382 # may cause partially committed files
383 # to be treated as unmodified
383 # to be treated as unmodified
384 dirstate.normallookup(realname)
384 dirstate.normallookup(realname)
385
385
386 # copystat=True here and above are a hack to trick any
386 # copystat=True here and above are a hack to trick any
387 # editors that have f open that we haven't modified them.
387 # editors that have f open that we haven't modified them.
388 #
388 #
389 # Also note that this racy as an editor could notice the
389 # Also note that this racy as an editor could notice the
390 # file's mtime before we've finished writing it.
390 # file's mtime before we've finished writing it.
391 util.copyfile(tmpname, repo.wjoin(realname), copystat=True)
391 util.copyfile(tmpname, repo.wjoin(realname), copystat=True)
392 os.unlink(tmpname)
392 os.unlink(tmpname)
393 if tobackup:
393 if tobackup:
394 os.rmdir(backupdir)
394 os.rmdir(backupdir)
395 except OSError:
395 except OSError:
396 pass
396 pass
397
397
398 def recordinwlock(ui, repo, message, match, opts):
398 def recordinwlock(ui, repo, message, match, opts):
399 with repo.wlock():
399 with repo.wlock():
400 return recordfunc(ui, repo, message, match, opts)
400 return recordfunc(ui, repo, message, match, opts)
401
401
402 return commit(ui, repo, recordinwlock, pats, opts)
402 return commit(ui, repo, recordinwlock, pats, opts)
403
403
404 def tersestatus(root, statlist, status, ignorefn, ignore):
404 def tersestatus(root, statlist, status, ignorefn, ignore):
405 """
405 """
406 Returns a list of statuses with directory collapsed if all the files in the
406 Returns a list of statuses with directory collapsed if all the files in the
407 directory has the same status.
407 directory has the same status.
408 """
408 """
409
409
410 def numfiles(dirname):
410 def numfiles(dirname):
411 """
411 """
412 Calculates the number of tracked files in a given directory which also
412 Calculates the number of tracked files in a given directory which also
413 includes files which were removed or deleted. Considers ignored files
413 includes files which were removed or deleted. Considers ignored files
414 if ignore argument is True or 'i' is present in status argument.
414 if ignore argument is True or 'i' is present in status argument.
415 """
415 """
416 if lencache.get(dirname):
416 if lencache.get(dirname):
417 return lencache[dirname]
417 return lencache[dirname]
418 if 'i' in status or ignore:
418 if 'i' in status or ignore:
419 def match(localpath):
419 def match(localpath):
420 absolutepath = os.path.join(root, localpath)
420 absolutepath = os.path.join(root, localpath)
421 if os.path.isdir(absolutepath) and isemptydir(absolutepath):
421 if os.path.isdir(absolutepath) and isemptydir(absolutepath):
422 return True
422 return True
423 return False
423 return False
424 else:
424 else:
425 def match(localpath):
425 def match(localpath):
426 # there can be directory whose all the files are ignored and
426 # there can be directory whose all the files are ignored and
427 # hence the drectory should also be ignored while counting
427 # hence the drectory should also be ignored while counting
428 # number of files or subdirs in it's parent directory. This
428 # number of files or subdirs in it's parent directory. This
429 # checks the same.
429 # checks the same.
430 # XXX: We need a better logic here.
430 # XXX: We need a better logic here.
431 if os.path.isdir(os.path.join(root, localpath)):
431 if os.path.isdir(os.path.join(root, localpath)):
432 return isignoreddir(localpath)
432 return isignoreddir(localpath)
433 else:
433 else:
434 # XXX: there can be files which have the ignored pattern but
434 # XXX: there can be files which have the ignored pattern but
435 # are not ignored. That leads to bug in counting number of
435 # are not ignored. That leads to bug in counting number of
436 # tracked files in the directory.
436 # tracked files in the directory.
437 return ignorefn(localpath)
437 return ignorefn(localpath)
438 lendir = 0
438 lendir = 0
439 abspath = os.path.join(root, dirname)
439 abspath = os.path.join(root, dirname)
440 # There might be cases when a directory does not exists as the whole
440 # There might be cases when a directory does not exists as the whole
441 # directory can be removed and/or deleted.
441 # directory can be removed and/or deleted.
442 try:
442 try:
443 for f in os.listdir(abspath):
443 for f in os.listdir(abspath):
444 localpath = os.path.join(dirname, f)
444 localpath = os.path.join(dirname, f)
445 if not match(localpath):
445 if not match(localpath):
446 lendir += 1
446 lendir += 1
447 except OSError:
447 except OSError:
448 pass
448 pass
449 lendir += len(absentdir.get(dirname, []))
449 lendir += len(absentdir.get(dirname, []))
450 lencache[dirname] = lendir
450 lencache[dirname] = lendir
451 return lendir
451 return lendir
452
452
453 def isemptydir(abspath):
453 def isemptydir(abspath):
454 """
454 """
455 Check whether a directory is empty or not, i.e. there is no files in the
455 Check whether a directory is empty or not, i.e. there is no files in the
456 directory and all its subdirectories.
456 directory and all its subdirectories.
457 """
457 """
458 for f in os.listdir(abspath):
458 for f in os.listdir(abspath):
459 fullpath = os.path.join(abspath, f)
459 fullpath = os.path.join(abspath, f)
460 if os.path.isdir(fullpath):
460 if os.path.isdir(fullpath):
461 # recursion here
461 # recursion here
462 ret = isemptydir(fullpath)
462 ret = isemptydir(fullpath)
463 if not ret:
463 if not ret:
464 return False
464 return False
465 else:
465 else:
466 return False
466 return False
467 return True
467 return True
468
468
469 def isignoreddir(localpath):
469 def isignoreddir(localpath):
470 """Return True if `localpath` directory is ignored or contains only
470 """Return True if `localpath` directory is ignored or contains only
471 ignored files and should hence be considered ignored.
471 ignored files and should hence be considered ignored.
472 """
472 """
473 dirpath = os.path.join(root, localpath)
473 dirpath = os.path.join(root, localpath)
474 if ignorefn(dirpath):
474 if ignorefn(dirpath):
475 return True
475 return True
476 for f in os.listdir(dirpath):
476 for f in os.listdir(dirpath):
477 filepath = os.path.join(dirpath, f)
477 filepath = os.path.join(dirpath, f)
478 if os.path.isdir(filepath):
478 if os.path.isdir(filepath):
479 # recursion here
479 # recursion here
480 ret = isignoreddir(os.path.join(localpath, f))
480 ret = isignoreddir(os.path.join(localpath, f))
481 if not ret:
481 if not ret:
482 return False
482 return False
483 else:
483 else:
484 if not ignorefn(os.path.join(localpath, f)):
484 if not ignorefn(os.path.join(localpath, f)):
485 return False
485 return False
486 return True
486 return True
487
487
488 def absentones(removedfiles, missingfiles):
488 def absentones(removedfiles, missingfiles):
489 """
489 """
490 Returns a dictionary of directories with files in it which are either
490 Returns a dictionary of directories with files in it which are either
491 removed or missing (deleted) in them.
491 removed or missing (deleted) in them.
492 """
492 """
493 absentdir = {}
493 absentdir = {}
494 absentfiles = removedfiles + missingfiles
494 absentfiles = removedfiles + missingfiles
495 while absentfiles:
495 while absentfiles:
496 f = absentfiles.pop()
496 f = absentfiles.pop()
497 par = os.path.dirname(f)
497 par = os.path.dirname(f)
498 if par == '':
498 if par == '':
499 continue
499 continue
500 # we need to store files rather than number of files as some files
500 # we need to store files rather than number of files as some files
501 # or subdirectories in a directory can be counted twice. This is
501 # or subdirectories in a directory can be counted twice. This is
502 # also we have used sets here.
502 # also we have used sets here.
503 try:
503 try:
504 absentdir[par].add(f)
504 absentdir[par].add(f)
505 except KeyError:
505 except KeyError:
506 absentdir[par] = set([f])
506 absentdir[par] = set([f])
507 absentfiles.append(par)
507 absentfiles.append(par)
508 return absentdir
508 return absentdir
509
509
510 indexes = {'m': 0, 'a': 1, 'r': 2, 'd': 3, 'u': 4, 'i': 5, 'c': 6}
510 indexes = {'m': 0, 'a': 1, 'r': 2, 'd': 3, 'u': 4, 'i': 5, 'c': 6}
511 # get a dictonary of directories and files which are missing as os.listdir()
511 # get a dictonary of directories and files which are missing as os.listdir()
512 # won't be able to list them.
512 # won't be able to list them.
513 absentdir = absentones(statlist[2], statlist[3])
513 absentdir = absentones(statlist[2], statlist[3])
514 finalrs = [[]] * len(indexes)
514 finalrs = [[]] * len(indexes)
515 didsomethingchanged = False
515 didsomethingchanged = False
516 # dictionary to store number of files and subdir in a directory so that we
516 # dictionary to store number of files and subdir in a directory so that we
517 # don't compute that again.
517 # don't compute that again.
518 lencache = {}
518 lencache = {}
519
519
520 for st in pycompat.bytestr(status):
520 for st in pycompat.bytestr(status):
521
521
522 try:
522 try:
523 ind = indexes[st]
523 ind = indexes[st]
524 except KeyError:
524 except KeyError:
525 # TODO: Need a better error message here
525 # TODO: Need a better error message here
526 raise error.Abort("'%s' not recognized" % st)
526 raise error.Abort("'%s' not recognized" % st)
527
527
528 sfiles = statlist[ind]
528 sfiles = statlist[ind]
529 if not sfiles:
529 if not sfiles:
530 continue
530 continue
531 pardict = {}
531 pardict = {}
532 for a in sfiles:
532 for a in sfiles:
533 par = os.path.dirname(a)
533 par = os.path.dirname(a)
534 pardict.setdefault(par, []).append(a)
534 pardict.setdefault(par, []).append(a)
535
535
536 rs = []
536 rs = []
537 newls = []
537 newls = []
538 for par, files in pardict.iteritems():
538 for par, files in pardict.iteritems():
539 lenpar = numfiles(par)
539 lenpar = numfiles(par)
540 if lenpar == len(files):
540 if lenpar == len(files):
541 newls.append(par)
541 newls.append(par)
542
542
543 if not newls:
543 if not newls:
544 continue
544 continue
545
545
546 while newls:
546 while newls:
547 newel = newls.pop()
547 newel = newls.pop()
548 if newel == '':
548 if newel == '':
549 continue
549 continue
550 parn = os.path.dirname(newel)
550 parn = os.path.dirname(newel)
551 pardict[newel] = []
551 pardict[newel] = []
552 # Adding pycompat.ossep as newel is a directory.
552 # Adding pycompat.ossep as newel is a directory.
553 pardict.setdefault(parn, []).append(newel + pycompat.ossep)
553 pardict.setdefault(parn, []).append(newel + pycompat.ossep)
554 lenpar = numfiles(parn)
554 lenpar = numfiles(parn)
555 if lenpar == len(pardict[parn]):
555 if lenpar == len(pardict[parn]):
556 newls.append(parn)
556 newls.append(parn)
557
557
558 # dict.values() for Py3 compatibility
558 # dict.values() for Py3 compatibility
559 for files in pardict.values():
559 for files in pardict.values():
560 rs.extend(files)
560 rs.extend(files)
561
561
562 rs.sort()
562 rs.sort()
563 finalrs[ind] = rs
563 finalrs[ind] = rs
564 didsomethingchanged = True
564 didsomethingchanged = True
565
565
566 # If nothing is changed, make sure the order of files is preserved.
566 # If nothing is changed, make sure the order of files is preserved.
567 if not didsomethingchanged:
567 if not didsomethingchanged:
568 return statlist
568 return statlist
569
569
570 for x in xrange(len(indexes)):
570 for x in xrange(len(indexes)):
571 if not finalrs[x]:
571 if not finalrs[x]:
572 finalrs[x] = statlist[x]
572 finalrs[x] = statlist[x]
573
573
574 return finalrs
574 return finalrs
575
575
576 def _commentlines(raw):
576 def _commentlines(raw):
577 '''Surround lineswith a comment char and a new line'''
577 '''Surround lineswith a comment char and a new line'''
578 lines = raw.splitlines()
578 lines = raw.splitlines()
579 commentedlines = ['# %s' % line for line in lines]
579 commentedlines = ['# %s' % line for line in lines]
580 return '\n'.join(commentedlines) + '\n'
580 return '\n'.join(commentedlines) + '\n'
581
581
582 def _conflictsmsg(repo):
582 def _conflictsmsg(repo):
583 # avoid merge cycle
583 # avoid merge cycle
584 from . import merge as mergemod
584 from . import merge as mergemod
585 mergestate = mergemod.mergestate.read(repo)
585 mergestate = mergemod.mergestate.read(repo)
586 if not mergestate.active():
586 if not mergestate.active():
587 return
587 return
588
588
589 m = scmutil.match(repo[None])
589 m = scmutil.match(repo[None])
590 unresolvedlist = [f for f in mergestate if m(f) and mergestate[f] == 'u']
590 unresolvedlist = [f for f in mergestate if m(f) and mergestate[f] == 'u']
591 if unresolvedlist:
591 if unresolvedlist:
592 mergeliststr = '\n'.join(
592 mergeliststr = '\n'.join(
593 [' %s' % os.path.relpath(
593 [' %s' % os.path.relpath(
594 os.path.join(repo.root, path),
594 os.path.join(repo.root, path),
595 pycompat.getcwd()) for path in unresolvedlist])
595 pycompat.getcwd()) for path in unresolvedlist])
596 msg = _('''Unresolved merge conflicts:
596 msg = _('''Unresolved merge conflicts:
597
597
598 %s
598 %s
599
599
600 To mark files as resolved: hg resolve --mark FILE''') % mergeliststr
600 To mark files as resolved: hg resolve --mark FILE''') % mergeliststr
601 else:
601 else:
602 msg = _('No unresolved merge conflicts.')
602 msg = _('No unresolved merge conflicts.')
603
603
604 return _commentlines(msg)
604 return _commentlines(msg)
605
605
606 def _helpmessage(continuecmd, abortcmd):
606 def _helpmessage(continuecmd, abortcmd):
607 msg = _('To continue: %s\n'
607 msg = _('To continue: %s\n'
608 'To abort: %s') % (continuecmd, abortcmd)
608 'To abort: %s') % (continuecmd, abortcmd)
609 return _commentlines(msg)
609 return _commentlines(msg)
610
610
611 def _rebasemsg():
611 def _rebasemsg():
612 return _helpmessage('hg rebase --continue', 'hg rebase --abort')
612 return _helpmessage('hg rebase --continue', 'hg rebase --abort')
613
613
614 def _histeditmsg():
614 def _histeditmsg():
615 return _helpmessage('hg histedit --continue', 'hg histedit --abort')
615 return _helpmessage('hg histedit --continue', 'hg histedit --abort')
616
616
617 def _unshelvemsg():
617 def _unshelvemsg():
618 return _helpmessage('hg unshelve --continue', 'hg unshelve --abort')
618 return _helpmessage('hg unshelve --continue', 'hg unshelve --abort')
619
619
620 def _updatecleanmsg(dest=None):
620 def _updatecleanmsg(dest=None):
621 warning = _('warning: this will discard uncommitted changes')
621 warning = _('warning: this will discard uncommitted changes')
622 return 'hg update --clean %s (%s)' % (dest or '.', warning)
622 return 'hg update --clean %s (%s)' % (dest or '.', warning)
623
623
624 def _graftmsg():
624 def _graftmsg():
625 # tweakdefaults requires `update` to have a rev hence the `.`
625 # tweakdefaults requires `update` to have a rev hence the `.`
626 return _helpmessage('hg graft --continue', _updatecleanmsg())
626 return _helpmessage('hg graft --continue', _updatecleanmsg())
627
627
628 def _mergemsg():
628 def _mergemsg():
629 # tweakdefaults requires `update` to have a rev hence the `.`
629 # tweakdefaults requires `update` to have a rev hence the `.`
630 return _helpmessage('hg commit', _updatecleanmsg())
630 return _helpmessage('hg commit', _updatecleanmsg())
631
631
632 def _bisectmsg():
632 def _bisectmsg():
633 msg = _('To mark the changeset good: hg bisect --good\n'
633 msg = _('To mark the changeset good: hg bisect --good\n'
634 'To mark the changeset bad: hg bisect --bad\n'
634 'To mark the changeset bad: hg bisect --bad\n'
635 'To abort: hg bisect --reset\n')
635 'To abort: hg bisect --reset\n')
636 return _commentlines(msg)
636 return _commentlines(msg)
637
637
638 def fileexistspredicate(filename):
638 def fileexistspredicate(filename):
639 return lambda repo: repo.vfs.exists(filename)
639 return lambda repo: repo.vfs.exists(filename)
640
640
641 def _mergepredicate(repo):
641 def _mergepredicate(repo):
642 return len(repo[None].parents()) > 1
642 return len(repo[None].parents()) > 1
643
643
644 STATES = (
644 STATES = (
645 # (state, predicate to detect states, helpful message function)
645 # (state, predicate to detect states, helpful message function)
646 ('histedit', fileexistspredicate('histedit-state'), _histeditmsg),
646 ('histedit', fileexistspredicate('histedit-state'), _histeditmsg),
647 ('bisect', fileexistspredicate('bisect.state'), _bisectmsg),
647 ('bisect', fileexistspredicate('bisect.state'), _bisectmsg),
648 ('graft', fileexistspredicate('graftstate'), _graftmsg),
648 ('graft', fileexistspredicate('graftstate'), _graftmsg),
649 ('unshelve', fileexistspredicate('unshelverebasestate'), _unshelvemsg),
649 ('unshelve', fileexistspredicate('unshelverebasestate'), _unshelvemsg),
650 ('rebase', fileexistspredicate('rebasestate'), _rebasemsg),
650 ('rebase', fileexistspredicate('rebasestate'), _rebasemsg),
651 # The merge state is part of a list that will be iterated over.
651 # The merge state is part of a list that will be iterated over.
652 # They need to be last because some of the other unfinished states may also
652 # They need to be last because some of the other unfinished states may also
653 # be in a merge or update state (eg. rebase, histedit, graft, etc).
653 # be in a merge or update state (eg. rebase, histedit, graft, etc).
654 # We want those to have priority.
654 # We want those to have priority.
655 ('merge', _mergepredicate, _mergemsg),
655 ('merge', _mergepredicate, _mergemsg),
656 )
656 )
657
657
658 def _getrepostate(repo):
658 def _getrepostate(repo):
659 # experimental config: commands.status.skipstates
659 # experimental config: commands.status.skipstates
660 skip = set(repo.ui.configlist('commands', 'status.skipstates'))
660 skip = set(repo.ui.configlist('commands', 'status.skipstates'))
661 for state, statedetectionpredicate, msgfn in STATES:
661 for state, statedetectionpredicate, msgfn in STATES:
662 if state in skip:
662 if state in skip:
663 continue
663 continue
664 if statedetectionpredicate(repo):
664 if statedetectionpredicate(repo):
665 return (state, statedetectionpredicate, msgfn)
665 return (state, statedetectionpredicate, msgfn)
666
666
667 def morestatus(repo, fm):
667 def morestatus(repo, fm):
668 statetuple = _getrepostate(repo)
668 statetuple = _getrepostate(repo)
669 label = 'status.morestatus'
669 label = 'status.morestatus'
670 if statetuple:
670 if statetuple:
671 fm.startitem()
671 fm.startitem()
672 state, statedetectionpredicate, helpfulmsg = statetuple
672 state, statedetectionpredicate, helpfulmsg = statetuple
673 statemsg = _('The repository is in an unfinished *%s* state.') % state
673 statemsg = _('The repository is in an unfinished *%s* state.') % state
674 fm.write('statemsg', '%s\n', _commentlines(statemsg), label=label)
674 fm.write('statemsg', '%s\n', _commentlines(statemsg), label=label)
675 conmsg = _conflictsmsg(repo)
675 conmsg = _conflictsmsg(repo)
676 fm.write('conflictsmsg', '%s\n', conmsg, label=label)
676 fm.write('conflictsmsg', '%s\n', conmsg, label=label)
677 if helpfulmsg:
677 if helpfulmsg:
678 helpmsg = helpfulmsg()
678 helpmsg = helpfulmsg()
679 fm.write('helpmsg', '%s\n', helpmsg, label=label)
679 fm.write('helpmsg', '%s\n', helpmsg, label=label)
680
680
681 def findpossible(cmd, table, strict=False):
681 def findpossible(cmd, table, strict=False):
682 """
682 """
683 Return cmd -> (aliases, command table entry)
683 Return cmd -> (aliases, command table entry)
684 for each matching command.
684 for each matching command.
685 Return debug commands (or their aliases) only if no normal command matches.
685 Return debug commands (or their aliases) only if no normal command matches.
686 """
686 """
687 choice = {}
687 choice = {}
688 debugchoice = {}
688 debugchoice = {}
689
689
690 if cmd in table:
690 if cmd in table:
691 # short-circuit exact matches, "log" alias beats "^log|history"
691 # short-circuit exact matches, "log" alias beats "^log|history"
692 keys = [cmd]
692 keys = [cmd]
693 else:
693 else:
694 keys = table.keys()
694 keys = table.keys()
695
695
696 allcmds = []
696 allcmds = []
697 for e in keys:
697 for e in keys:
698 aliases = parsealiases(e)
698 aliases = parsealiases(e)
699 allcmds.extend(aliases)
699 allcmds.extend(aliases)
700 found = None
700 found = None
701 if cmd in aliases:
701 if cmd in aliases:
702 found = cmd
702 found = cmd
703 elif not strict:
703 elif not strict:
704 for a in aliases:
704 for a in aliases:
705 if a.startswith(cmd):
705 if a.startswith(cmd):
706 found = a
706 found = a
707 break
707 break
708 if found is not None:
708 if found is not None:
709 if aliases[0].startswith("debug") or found.startswith("debug"):
709 if aliases[0].startswith("debug") or found.startswith("debug"):
710 debugchoice[found] = (aliases, table[e])
710 debugchoice[found] = (aliases, table[e])
711 else:
711 else:
712 choice[found] = (aliases, table[e])
712 choice[found] = (aliases, table[e])
713
713
714 if not choice and debugchoice:
714 if not choice and debugchoice:
715 choice = debugchoice
715 choice = debugchoice
716
716
717 return choice, allcmds
717 return choice, allcmds
718
718
719 def findcmd(cmd, table, strict=True):
719 def findcmd(cmd, table, strict=True):
720 """Return (aliases, command table entry) for command string."""
720 """Return (aliases, command table entry) for command string."""
721 choice, allcmds = findpossible(cmd, table, strict)
721 choice, allcmds = findpossible(cmd, table, strict)
722
722
723 if cmd in choice:
723 if cmd in choice:
724 return choice[cmd]
724 return choice[cmd]
725
725
726 if len(choice) > 1:
726 if len(choice) > 1:
727 clist = sorted(choice)
727 clist = sorted(choice)
728 raise error.AmbiguousCommand(cmd, clist)
728 raise error.AmbiguousCommand(cmd, clist)
729
729
730 if choice:
730 if choice:
731 return list(choice.values())[0]
731 return list(choice.values())[0]
732
732
733 raise error.UnknownCommand(cmd, allcmds)
733 raise error.UnknownCommand(cmd, allcmds)
734
734
735 def findrepo(p):
735 def findrepo(p):
736 while not os.path.isdir(os.path.join(p, ".hg")):
736 while not os.path.isdir(os.path.join(p, ".hg")):
737 oldp, p = p, os.path.dirname(p)
737 oldp, p = p, os.path.dirname(p)
738 if p == oldp:
738 if p == oldp:
739 return None
739 return None
740
740
741 return p
741 return p
742
742
743 def bailifchanged(repo, merge=True, hint=None):
743 def bailifchanged(repo, merge=True, hint=None):
744 """ enforce the precondition that working directory must be clean.
744 """ enforce the precondition that working directory must be clean.
745
745
746 'merge' can be set to false if a pending uncommitted merge should be
746 'merge' can be set to false if a pending uncommitted merge should be
747 ignored (such as when 'update --check' runs).
747 ignored (such as when 'update --check' runs).
748
748
749 'hint' is the usual hint given to Abort exception.
749 'hint' is the usual hint given to Abort exception.
750 """
750 """
751
751
752 if merge and repo.dirstate.p2() != nullid:
752 if merge and repo.dirstate.p2() != nullid:
753 raise error.Abort(_('outstanding uncommitted merge'), hint=hint)
753 raise error.Abort(_('outstanding uncommitted merge'), hint=hint)
754 modified, added, removed, deleted = repo.status()[:4]
754 modified, added, removed, deleted = repo.status()[:4]
755 if modified or added or removed or deleted:
755 if modified or added or removed or deleted:
756 raise error.Abort(_('uncommitted changes'), hint=hint)
756 raise error.Abort(_('uncommitted changes'), hint=hint)
757 ctx = repo[None]
757 ctx = repo[None]
758 for s in sorted(ctx.substate):
758 for s in sorted(ctx.substate):
759 ctx.sub(s).bailifchanged(hint=hint)
759 ctx.sub(s).bailifchanged(hint=hint)
760
760
761 def logmessage(ui, opts):
761 def logmessage(ui, opts):
762 """ get the log message according to -m and -l option """
762 """ get the log message according to -m and -l option """
763 message = opts.get('message')
763 message = opts.get('message')
764 logfile = opts.get('logfile')
764 logfile = opts.get('logfile')
765
765
766 if message and logfile:
766 if message and logfile:
767 raise error.Abort(_('options --message and --logfile are mutually '
767 raise error.Abort(_('options --message and --logfile are mutually '
768 'exclusive'))
768 'exclusive'))
769 if not message and logfile:
769 if not message and logfile:
770 try:
770 try:
771 if isstdiofilename(logfile):
771 if isstdiofilename(logfile):
772 message = ui.fin.read()
772 message = ui.fin.read()
773 else:
773 else:
774 message = '\n'.join(util.readfile(logfile).splitlines())
774 message = '\n'.join(util.readfile(logfile).splitlines())
775 except IOError as inst:
775 except IOError as inst:
776 raise error.Abort(_("can't read commit message '%s': %s") %
776 raise error.Abort(_("can't read commit message '%s': %s") %
777 (logfile, inst.strerror))
777 (logfile, inst.strerror))
778 return message
778 return message
779
779
780 def mergeeditform(ctxorbool, baseformname):
780 def mergeeditform(ctxorbool, baseformname):
781 """return appropriate editform name (referencing a committemplate)
781 """return appropriate editform name (referencing a committemplate)
782
782
783 'ctxorbool' is either a ctx to be committed, or a bool indicating whether
783 'ctxorbool' is either a ctx to be committed, or a bool indicating whether
784 merging is committed.
784 merging is committed.
785
785
786 This returns baseformname with '.merge' appended if it is a merge,
786 This returns baseformname with '.merge' appended if it is a merge,
787 otherwise '.normal' is appended.
787 otherwise '.normal' is appended.
788 """
788 """
789 if isinstance(ctxorbool, bool):
789 if isinstance(ctxorbool, bool):
790 if ctxorbool:
790 if ctxorbool:
791 return baseformname + ".merge"
791 return baseformname + ".merge"
792 elif 1 < len(ctxorbool.parents()):
792 elif 1 < len(ctxorbool.parents()):
793 return baseformname + ".merge"
793 return baseformname + ".merge"
794
794
795 return baseformname + ".normal"
795 return baseformname + ".normal"
796
796
797 def getcommiteditor(edit=False, finishdesc=None, extramsg=None,
797 def getcommiteditor(edit=False, finishdesc=None, extramsg=None,
798 editform='', **opts):
798 editform='', **opts):
799 """get appropriate commit message editor according to '--edit' option
799 """get appropriate commit message editor according to '--edit' option
800
800
801 'finishdesc' is a function to be called with edited commit message
801 'finishdesc' is a function to be called with edited commit message
802 (= 'description' of the new changeset) just after editing, but
802 (= 'description' of the new changeset) just after editing, but
803 before checking empty-ness. It should return actual text to be
803 before checking empty-ness. It should return actual text to be
804 stored into history. This allows to change description before
804 stored into history. This allows to change description before
805 storing.
805 storing.
806
806
807 'extramsg' is a extra message to be shown in the editor instead of
807 'extramsg' is a extra message to be shown in the editor instead of
808 'Leave message empty to abort commit' line. 'HG: ' prefix and EOL
808 'Leave message empty to abort commit' line. 'HG: ' prefix and EOL
809 is automatically added.
809 is automatically added.
810
810
811 'editform' is a dot-separated list of names, to distinguish
811 'editform' is a dot-separated list of names, to distinguish
812 the purpose of commit text editing.
812 the purpose of commit text editing.
813
813
814 'getcommiteditor' returns 'commitforceeditor' regardless of
814 'getcommiteditor' returns 'commitforceeditor' regardless of
815 'edit', if one of 'finishdesc' or 'extramsg' is specified, because
815 'edit', if one of 'finishdesc' or 'extramsg' is specified, because
816 they are specific for usage in MQ.
816 they are specific for usage in MQ.
817 """
817 """
818 if edit or finishdesc or extramsg:
818 if edit or finishdesc or extramsg:
819 return lambda r, c, s: commitforceeditor(r, c, s,
819 return lambda r, c, s: commitforceeditor(r, c, s,
820 finishdesc=finishdesc,
820 finishdesc=finishdesc,
821 extramsg=extramsg,
821 extramsg=extramsg,
822 editform=editform)
822 editform=editform)
823 elif editform:
823 elif editform:
824 return lambda r, c, s: commiteditor(r, c, s, editform=editform)
824 return lambda r, c, s: commiteditor(r, c, s, editform=editform)
825 else:
825 else:
826 return commiteditor
826 return commiteditor
827
827
828 def loglimit(opts):
828 def loglimit(opts):
829 """get the log limit according to option -l/--limit"""
829 """get the log limit according to option -l/--limit"""
830 limit = opts.get('limit')
830 limit = opts.get('limit')
831 if limit:
831 if limit:
832 try:
832 try:
833 limit = int(limit)
833 limit = int(limit)
834 except ValueError:
834 except ValueError:
835 raise error.Abort(_('limit must be a positive integer'))
835 raise error.Abort(_('limit must be a positive integer'))
836 if limit <= 0:
836 if limit <= 0:
837 raise error.Abort(_('limit must be positive'))
837 raise error.Abort(_('limit must be positive'))
838 else:
838 else:
839 limit = None
839 limit = None
840 return limit
840 return limit
841
841
842 def makefilename(repo, pat, node, desc=None,
842 def makefilename(repo, pat, node, desc=None,
843 total=None, seqno=None, revwidth=None, pathname=None):
843 total=None, seqno=None, revwidth=None, pathname=None):
844 node_expander = {
844 node_expander = {
845 'H': lambda: hex(node),
845 'H': lambda: hex(node),
846 'R': lambda: str(repo.changelog.rev(node)),
846 'R': lambda: str(repo.changelog.rev(node)),
847 'h': lambda: short(node),
847 'h': lambda: short(node),
848 'm': lambda: re.sub('[^\w]', '_', str(desc))
848 'm': lambda: re.sub('[^\w]', '_', str(desc))
849 }
849 }
850 expander = {
850 expander = {
851 '%': lambda: '%',
851 '%': lambda: '%',
852 'b': lambda: os.path.basename(repo.root),
852 'b': lambda: os.path.basename(repo.root),
853 }
853 }
854
854
855 try:
855 try:
856 if node:
856 if node:
857 expander.update(node_expander)
857 expander.update(node_expander)
858 if node:
858 if node:
859 expander['r'] = (lambda:
859 expander['r'] = (lambda:
860 str(repo.changelog.rev(node)).zfill(revwidth or 0))
860 str(repo.changelog.rev(node)).zfill(revwidth or 0))
861 if total is not None:
861 if total is not None:
862 expander['N'] = lambda: str(total)
862 expander['N'] = lambda: str(total)
863 if seqno is not None:
863 if seqno is not None:
864 expander['n'] = lambda: str(seqno)
864 expander['n'] = lambda: str(seqno)
865 if total is not None and seqno is not None:
865 if total is not None and seqno is not None:
866 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
866 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
867 if pathname is not None:
867 if pathname is not None:
868 expander['s'] = lambda: os.path.basename(pathname)
868 expander['s'] = lambda: os.path.basename(pathname)
869 expander['d'] = lambda: os.path.dirname(pathname) or '.'
869 expander['d'] = lambda: os.path.dirname(pathname) or '.'
870 expander['p'] = lambda: pathname
870 expander['p'] = lambda: pathname
871
871
872 newname = []
872 newname = []
873 patlen = len(pat)
873 patlen = len(pat)
874 i = 0
874 i = 0
875 while i < patlen:
875 while i < patlen:
876 c = pat[i:i + 1]
876 c = pat[i:i + 1]
877 if c == '%':
877 if c == '%':
878 i += 1
878 i += 1
879 c = pat[i:i + 1]
879 c = pat[i:i + 1]
880 c = expander[c]()
880 c = expander[c]()
881 newname.append(c)
881 newname.append(c)
882 i += 1
882 i += 1
883 return ''.join(newname)
883 return ''.join(newname)
884 except KeyError as inst:
884 except KeyError as inst:
885 raise error.Abort(_("invalid format spec '%%%s' in output filename") %
885 raise error.Abort(_("invalid format spec '%%%s' in output filename") %
886 inst.args[0])
886 inst.args[0])
887
887
888 def isstdiofilename(pat):
888 def isstdiofilename(pat):
889 """True if the given pat looks like a filename denoting stdin/stdout"""
889 """True if the given pat looks like a filename denoting stdin/stdout"""
890 return not pat or pat == '-'
890 return not pat or pat == '-'
891
891
892 class _unclosablefile(object):
892 class _unclosablefile(object):
893 def __init__(self, fp):
893 def __init__(self, fp):
894 self._fp = fp
894 self._fp = fp
895
895
896 def close(self):
896 def close(self):
897 pass
897 pass
898
898
899 def __iter__(self):
899 def __iter__(self):
900 return iter(self._fp)
900 return iter(self._fp)
901
901
902 def __getattr__(self, attr):
902 def __getattr__(self, attr):
903 return getattr(self._fp, attr)
903 return getattr(self._fp, attr)
904
904
905 def __enter__(self):
905 def __enter__(self):
906 return self
906 return self
907
907
908 def __exit__(self, exc_type, exc_value, exc_tb):
908 def __exit__(self, exc_type, exc_value, exc_tb):
909 pass
909 pass
910
910
911 def makefileobj(repo, pat, node=None, desc=None, total=None,
911 def makefileobj(repo, pat, node=None, desc=None, total=None,
912 seqno=None, revwidth=None, mode='wb', modemap=None,
912 seqno=None, revwidth=None, mode='wb', modemap=None,
913 pathname=None):
913 pathname=None):
914
914
915 writable = mode not in ('r', 'rb')
915 writable = mode not in ('r', 'rb')
916
916
917 if isstdiofilename(pat):
917 if isstdiofilename(pat):
918 if writable:
918 if writable:
919 fp = repo.ui.fout
919 fp = repo.ui.fout
920 else:
920 else:
921 fp = repo.ui.fin
921 fp = repo.ui.fin
922 return _unclosablefile(fp)
922 return _unclosablefile(fp)
923 fn = makefilename(repo, pat, node, desc, total, seqno, revwidth, pathname)
923 fn = makefilename(repo, pat, node, desc, total, seqno, revwidth, pathname)
924 if modemap is not None:
924 if modemap is not None:
925 mode = modemap.get(fn, mode)
925 mode = modemap.get(fn, mode)
926 if mode == 'wb':
926 if mode == 'wb':
927 modemap[fn] = 'ab'
927 modemap[fn] = 'ab'
928 return open(fn, mode)
928 return open(fn, mode)
929
929
930 def openrevlog(repo, cmd, file_, opts):
930 def openrevlog(repo, cmd, file_, opts):
931 """opens the changelog, manifest, a filelog or a given revlog"""
931 """opens the changelog, manifest, a filelog or a given revlog"""
932 cl = opts['changelog']
932 cl = opts['changelog']
933 mf = opts['manifest']
933 mf = opts['manifest']
934 dir = opts['dir']
934 dir = opts['dir']
935 msg = None
935 msg = None
936 if cl and mf:
936 if cl and mf:
937 msg = _('cannot specify --changelog and --manifest at the same time')
937 msg = _('cannot specify --changelog and --manifest at the same time')
938 elif cl and dir:
938 elif cl and dir:
939 msg = _('cannot specify --changelog and --dir at the same time')
939 msg = _('cannot specify --changelog and --dir at the same time')
940 elif cl or mf or dir:
940 elif cl or mf or dir:
941 if file_:
941 if file_:
942 msg = _('cannot specify filename with --changelog or --manifest')
942 msg = _('cannot specify filename with --changelog or --manifest')
943 elif not repo:
943 elif not repo:
944 msg = _('cannot specify --changelog or --manifest or --dir '
944 msg = _('cannot specify --changelog or --manifest or --dir '
945 'without a repository')
945 'without a repository')
946 if msg:
946 if msg:
947 raise error.Abort(msg)
947 raise error.Abort(msg)
948
948
949 r = None
949 r = None
950 if repo:
950 if repo:
951 if cl:
951 if cl:
952 r = repo.unfiltered().changelog
952 r = repo.unfiltered().changelog
953 elif dir:
953 elif dir:
954 if 'treemanifest' not in repo.requirements:
954 if 'treemanifest' not in repo.requirements:
955 raise error.Abort(_("--dir can only be used on repos with "
955 raise error.Abort(_("--dir can only be used on repos with "
956 "treemanifest enabled"))
956 "treemanifest enabled"))
957 dirlog = repo.manifestlog._revlog.dirlog(dir)
957 dirlog = repo.manifestlog._revlog.dirlog(dir)
958 if len(dirlog):
958 if len(dirlog):
959 r = dirlog
959 r = dirlog
960 elif mf:
960 elif mf:
961 r = repo.manifestlog._revlog
961 r = repo.manifestlog._revlog
962 elif file_:
962 elif file_:
963 filelog = repo.file(file_)
963 filelog = repo.file(file_)
964 if len(filelog):
964 if len(filelog):
965 r = filelog
965 r = filelog
966 if not r:
966 if not r:
967 if not file_:
967 if not file_:
968 raise error.CommandError(cmd, _('invalid arguments'))
968 raise error.CommandError(cmd, _('invalid arguments'))
969 if not os.path.isfile(file_):
969 if not os.path.isfile(file_):
970 raise error.Abort(_("revlog '%s' not found") % file_)
970 raise error.Abort(_("revlog '%s' not found") % file_)
971 r = revlog.revlog(vfsmod.vfs(pycompat.getcwd(), audit=False),
971 r = revlog.revlog(vfsmod.vfs(pycompat.getcwd(), audit=False),
972 file_[:-2] + ".i")
972 file_[:-2] + ".i")
973 return r
973 return r
974
974
975 def copy(ui, repo, pats, opts, rename=False):
975 def copy(ui, repo, pats, opts, rename=False):
976 # called with the repo lock held
976 # called with the repo lock held
977 #
977 #
978 # hgsep => pathname that uses "/" to separate directories
978 # hgsep => pathname that uses "/" to separate directories
979 # ossep => pathname that uses os.sep to separate directories
979 # ossep => pathname that uses os.sep to separate directories
980 cwd = repo.getcwd()
980 cwd = repo.getcwd()
981 targets = {}
981 targets = {}
982 after = opts.get("after")
982 after = opts.get("after")
983 dryrun = opts.get("dry_run")
983 dryrun = opts.get("dry_run")
984 wctx = repo[None]
984 wctx = repo[None]
985
985
986 def walkpat(pat):
986 def walkpat(pat):
987 srcs = []
987 srcs = []
988 if after:
988 if after:
989 badstates = '?'
989 badstates = '?'
990 else:
990 else:
991 badstates = '?r'
991 badstates = '?r'
992 m = scmutil.match(wctx, [pat], opts, globbed=True)
992 m = scmutil.match(wctx, [pat], opts, globbed=True)
993 for abs in wctx.walk(m):
993 for abs in wctx.walk(m):
994 state = repo.dirstate[abs]
994 state = repo.dirstate[abs]
995 rel = m.rel(abs)
995 rel = m.rel(abs)
996 exact = m.exact(abs)
996 exact = m.exact(abs)
997 if state in badstates:
997 if state in badstates:
998 if exact and state == '?':
998 if exact and state == '?':
999 ui.warn(_('%s: not copying - file is not managed\n') % rel)
999 ui.warn(_('%s: not copying - file is not managed\n') % rel)
1000 if exact and state == 'r':
1000 if exact and state == 'r':
1001 ui.warn(_('%s: not copying - file has been marked for'
1001 ui.warn(_('%s: not copying - file has been marked for'
1002 ' remove\n') % rel)
1002 ' remove\n') % rel)
1003 continue
1003 continue
1004 # abs: hgsep
1004 # abs: hgsep
1005 # rel: ossep
1005 # rel: ossep
1006 srcs.append((abs, rel, exact))
1006 srcs.append((abs, rel, exact))
1007 return srcs
1007 return srcs
1008
1008
1009 # abssrc: hgsep
1009 # abssrc: hgsep
1010 # relsrc: ossep
1010 # relsrc: ossep
1011 # otarget: ossep
1011 # otarget: ossep
1012 def copyfile(abssrc, relsrc, otarget, exact):
1012 def copyfile(abssrc, relsrc, otarget, exact):
1013 abstarget = pathutil.canonpath(repo.root, cwd, otarget)
1013 abstarget = pathutil.canonpath(repo.root, cwd, otarget)
1014 if '/' in abstarget:
1014 if '/' in abstarget:
1015 # We cannot normalize abstarget itself, this would prevent
1015 # We cannot normalize abstarget itself, this would prevent
1016 # case only renames, like a => A.
1016 # case only renames, like a => A.
1017 abspath, absname = abstarget.rsplit('/', 1)
1017 abspath, absname = abstarget.rsplit('/', 1)
1018 abstarget = repo.dirstate.normalize(abspath) + '/' + absname
1018 abstarget = repo.dirstate.normalize(abspath) + '/' + absname
1019 reltarget = repo.pathto(abstarget, cwd)
1019 reltarget = repo.pathto(abstarget, cwd)
1020 target = repo.wjoin(abstarget)
1020 target = repo.wjoin(abstarget)
1021 src = repo.wjoin(abssrc)
1021 src = repo.wjoin(abssrc)
1022 state = repo.dirstate[abstarget]
1022 state = repo.dirstate[abstarget]
1023
1023
1024 scmutil.checkportable(ui, abstarget)
1024 scmutil.checkportable(ui, abstarget)
1025
1025
1026 # check for collisions
1026 # check for collisions
1027 prevsrc = targets.get(abstarget)
1027 prevsrc = targets.get(abstarget)
1028 if prevsrc is not None:
1028 if prevsrc is not None:
1029 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
1029 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
1030 (reltarget, repo.pathto(abssrc, cwd),
1030 (reltarget, repo.pathto(abssrc, cwd),
1031 repo.pathto(prevsrc, cwd)))
1031 repo.pathto(prevsrc, cwd)))
1032 return
1032 return
1033
1033
1034 # check for overwrites
1034 # check for overwrites
1035 exists = os.path.lexists(target)
1035 exists = os.path.lexists(target)
1036 samefile = False
1036 samefile = False
1037 if exists and abssrc != abstarget:
1037 if exists and abssrc != abstarget:
1038 if (repo.dirstate.normalize(abssrc) ==
1038 if (repo.dirstate.normalize(abssrc) ==
1039 repo.dirstate.normalize(abstarget)):
1039 repo.dirstate.normalize(abstarget)):
1040 if not rename:
1040 if not rename:
1041 ui.warn(_("%s: can't copy - same file\n") % reltarget)
1041 ui.warn(_("%s: can't copy - same file\n") % reltarget)
1042 return
1042 return
1043 exists = False
1043 exists = False
1044 samefile = True
1044 samefile = True
1045
1045
1046 if not after and exists or after and state in 'mn':
1046 if not after and exists or after and state in 'mn':
1047 if not opts['force']:
1047 if not opts['force']:
1048 if state in 'mn':
1048 if state in 'mn':
1049 msg = _('%s: not overwriting - file already committed\n')
1049 msg = _('%s: not overwriting - file already committed\n')
1050 if after:
1050 if after:
1051 flags = '--after --force'
1051 flags = '--after --force'
1052 else:
1052 else:
1053 flags = '--force'
1053 flags = '--force'
1054 if rename:
1054 if rename:
1055 hint = _('(hg rename %s to replace the file by '
1055 hint = _('(hg rename %s to replace the file by '
1056 'recording a rename)\n') % flags
1056 'recording a rename)\n') % flags
1057 else:
1057 else:
1058 hint = _('(hg copy %s to replace the file by '
1058 hint = _('(hg copy %s to replace the file by '
1059 'recording a copy)\n') % flags
1059 'recording a copy)\n') % flags
1060 else:
1060 else:
1061 msg = _('%s: not overwriting - file exists\n')
1061 msg = _('%s: not overwriting - file exists\n')
1062 if rename:
1062 if rename:
1063 hint = _('(hg rename --after to record the rename)\n')
1063 hint = _('(hg rename --after to record the rename)\n')
1064 else:
1064 else:
1065 hint = _('(hg copy --after to record the copy)\n')
1065 hint = _('(hg copy --after to record the copy)\n')
1066 ui.warn(msg % reltarget)
1066 ui.warn(msg % reltarget)
1067 ui.warn(hint)
1067 ui.warn(hint)
1068 return
1068 return
1069
1069
1070 if after:
1070 if after:
1071 if not exists:
1071 if not exists:
1072 if rename:
1072 if rename:
1073 ui.warn(_('%s: not recording move - %s does not exist\n') %
1073 ui.warn(_('%s: not recording move - %s does not exist\n') %
1074 (relsrc, reltarget))
1074 (relsrc, reltarget))
1075 else:
1075 else:
1076 ui.warn(_('%s: not recording copy - %s does not exist\n') %
1076 ui.warn(_('%s: not recording copy - %s does not exist\n') %
1077 (relsrc, reltarget))
1077 (relsrc, reltarget))
1078 return
1078 return
1079 elif not dryrun:
1079 elif not dryrun:
1080 try:
1080 try:
1081 if exists:
1081 if exists:
1082 os.unlink(target)
1082 os.unlink(target)
1083 targetdir = os.path.dirname(target) or '.'
1083 targetdir = os.path.dirname(target) or '.'
1084 if not os.path.isdir(targetdir):
1084 if not os.path.isdir(targetdir):
1085 os.makedirs(targetdir)
1085 os.makedirs(targetdir)
1086 if samefile:
1086 if samefile:
1087 tmp = target + "~hgrename"
1087 tmp = target + "~hgrename"
1088 os.rename(src, tmp)
1088 os.rename(src, tmp)
1089 os.rename(tmp, target)
1089 os.rename(tmp, target)
1090 else:
1090 else:
1091 util.copyfile(src, target)
1091 util.copyfile(src, target)
1092 srcexists = True
1092 srcexists = True
1093 except IOError as inst:
1093 except IOError as inst:
1094 if inst.errno == errno.ENOENT:
1094 if inst.errno == errno.ENOENT:
1095 ui.warn(_('%s: deleted in working directory\n') % relsrc)
1095 ui.warn(_('%s: deleted in working directory\n') % relsrc)
1096 srcexists = False
1096 srcexists = False
1097 else:
1097 else:
1098 ui.warn(_('%s: cannot copy - %s\n') %
1098 ui.warn(_('%s: cannot copy - %s\n') %
1099 (relsrc, inst.strerror))
1099 (relsrc, inst.strerror))
1100 return True # report a failure
1100 return True # report a failure
1101
1101
1102 if ui.verbose or not exact:
1102 if ui.verbose or not exact:
1103 if rename:
1103 if rename:
1104 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
1104 ui.status(_('moving %s to %s\n') % (relsrc, reltarget))
1105 else:
1105 else:
1106 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
1106 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
1107
1107
1108 targets[abstarget] = abssrc
1108 targets[abstarget] = abssrc
1109
1109
1110 # fix up dirstate
1110 # fix up dirstate
1111 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
1111 scmutil.dirstatecopy(ui, repo, wctx, abssrc, abstarget,
1112 dryrun=dryrun, cwd=cwd)
1112 dryrun=dryrun, cwd=cwd)
1113 if rename and not dryrun:
1113 if rename and not dryrun:
1114 if not after and srcexists and not samefile:
1114 if not after and srcexists and not samefile:
1115 repo.wvfs.unlinkpath(abssrc)
1115 repo.wvfs.unlinkpath(abssrc)
1116 wctx.forget([abssrc])
1116 wctx.forget([abssrc])
1117
1117
1118 # pat: ossep
1118 # pat: ossep
1119 # dest ossep
1119 # dest ossep
1120 # srcs: list of (hgsep, hgsep, ossep, bool)
1120 # srcs: list of (hgsep, hgsep, ossep, bool)
1121 # return: function that takes hgsep and returns ossep
1121 # return: function that takes hgsep and returns ossep
1122 def targetpathfn(pat, dest, srcs):
1122 def targetpathfn(pat, dest, srcs):
1123 if os.path.isdir(pat):
1123 if os.path.isdir(pat):
1124 abspfx = pathutil.canonpath(repo.root, cwd, pat)
1124 abspfx = pathutil.canonpath(repo.root, cwd, pat)
1125 abspfx = util.localpath(abspfx)
1125 abspfx = util.localpath(abspfx)
1126 if destdirexists:
1126 if destdirexists:
1127 striplen = len(os.path.split(abspfx)[0])
1127 striplen = len(os.path.split(abspfx)[0])
1128 else:
1128 else:
1129 striplen = len(abspfx)
1129 striplen = len(abspfx)
1130 if striplen:
1130 if striplen:
1131 striplen += len(pycompat.ossep)
1131 striplen += len(pycompat.ossep)
1132 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
1132 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
1133 elif destdirexists:
1133 elif destdirexists:
1134 res = lambda p: os.path.join(dest,
1134 res = lambda p: os.path.join(dest,
1135 os.path.basename(util.localpath(p)))
1135 os.path.basename(util.localpath(p)))
1136 else:
1136 else:
1137 res = lambda p: dest
1137 res = lambda p: dest
1138 return res
1138 return res
1139
1139
1140 # pat: ossep
1140 # pat: ossep
1141 # dest ossep
1141 # dest ossep
1142 # srcs: list of (hgsep, hgsep, ossep, bool)
1142 # srcs: list of (hgsep, hgsep, ossep, bool)
1143 # return: function that takes hgsep and returns ossep
1143 # return: function that takes hgsep and returns ossep
1144 def targetpathafterfn(pat, dest, srcs):
1144 def targetpathafterfn(pat, dest, srcs):
1145 if matchmod.patkind(pat):
1145 if matchmod.patkind(pat):
1146 # a mercurial pattern
1146 # a mercurial pattern
1147 res = lambda p: os.path.join(dest,
1147 res = lambda p: os.path.join(dest,
1148 os.path.basename(util.localpath(p)))
1148 os.path.basename(util.localpath(p)))
1149 else:
1149 else:
1150 abspfx = pathutil.canonpath(repo.root, cwd, pat)
1150 abspfx = pathutil.canonpath(repo.root, cwd, pat)
1151 if len(abspfx) < len(srcs[0][0]):
1151 if len(abspfx) < len(srcs[0][0]):
1152 # A directory. Either the target path contains the last
1152 # A directory. Either the target path contains the last
1153 # component of the source path or it does not.
1153 # component of the source path or it does not.
1154 def evalpath(striplen):
1154 def evalpath(striplen):
1155 score = 0
1155 score = 0
1156 for s in srcs:
1156 for s in srcs:
1157 t = os.path.join(dest, util.localpath(s[0])[striplen:])
1157 t = os.path.join(dest, util.localpath(s[0])[striplen:])
1158 if os.path.lexists(t):
1158 if os.path.lexists(t):
1159 score += 1
1159 score += 1
1160 return score
1160 return score
1161
1161
1162 abspfx = util.localpath(abspfx)
1162 abspfx = util.localpath(abspfx)
1163 striplen = len(abspfx)
1163 striplen = len(abspfx)
1164 if striplen:
1164 if striplen:
1165 striplen += len(pycompat.ossep)
1165 striplen += len(pycompat.ossep)
1166 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
1166 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
1167 score = evalpath(striplen)
1167 score = evalpath(striplen)
1168 striplen1 = len(os.path.split(abspfx)[0])
1168 striplen1 = len(os.path.split(abspfx)[0])
1169 if striplen1:
1169 if striplen1:
1170 striplen1 += len(pycompat.ossep)
1170 striplen1 += len(pycompat.ossep)
1171 if evalpath(striplen1) > score:
1171 if evalpath(striplen1) > score:
1172 striplen = striplen1
1172 striplen = striplen1
1173 res = lambda p: os.path.join(dest,
1173 res = lambda p: os.path.join(dest,
1174 util.localpath(p)[striplen:])
1174 util.localpath(p)[striplen:])
1175 else:
1175 else:
1176 # a file
1176 # a file
1177 if destdirexists:
1177 if destdirexists:
1178 res = lambda p: os.path.join(dest,
1178 res = lambda p: os.path.join(dest,
1179 os.path.basename(util.localpath(p)))
1179 os.path.basename(util.localpath(p)))
1180 else:
1180 else:
1181 res = lambda p: dest
1181 res = lambda p: dest
1182 return res
1182 return res
1183
1183
1184 pats = scmutil.expandpats(pats)
1184 pats = scmutil.expandpats(pats)
1185 if not pats:
1185 if not pats:
1186 raise error.Abort(_('no source or destination specified'))
1186 raise error.Abort(_('no source or destination specified'))
1187 if len(pats) == 1:
1187 if len(pats) == 1:
1188 raise error.Abort(_('no destination specified'))
1188 raise error.Abort(_('no destination specified'))
1189 dest = pats.pop()
1189 dest = pats.pop()
1190 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
1190 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
1191 if not destdirexists:
1191 if not destdirexists:
1192 if len(pats) > 1 or matchmod.patkind(pats[0]):
1192 if len(pats) > 1 or matchmod.patkind(pats[0]):
1193 raise error.Abort(_('with multiple sources, destination must be an '
1193 raise error.Abort(_('with multiple sources, destination must be an '
1194 'existing directory'))
1194 'existing directory'))
1195 if util.endswithsep(dest):
1195 if util.endswithsep(dest):
1196 raise error.Abort(_('destination %s is not a directory') % dest)
1196 raise error.Abort(_('destination %s is not a directory') % dest)
1197
1197
1198 tfn = targetpathfn
1198 tfn = targetpathfn
1199 if after:
1199 if after:
1200 tfn = targetpathafterfn
1200 tfn = targetpathafterfn
1201 copylist = []
1201 copylist = []
1202 for pat in pats:
1202 for pat in pats:
1203 srcs = walkpat(pat)
1203 srcs = walkpat(pat)
1204 if not srcs:
1204 if not srcs:
1205 continue
1205 continue
1206 copylist.append((tfn(pat, dest, srcs), srcs))
1206 copylist.append((tfn(pat, dest, srcs), srcs))
1207 if not copylist:
1207 if not copylist:
1208 raise error.Abort(_('no files to copy'))
1208 raise error.Abort(_('no files to copy'))
1209
1209
1210 errors = 0
1210 errors = 0
1211 for targetpath, srcs in copylist:
1211 for targetpath, srcs in copylist:
1212 for abssrc, relsrc, exact in srcs:
1212 for abssrc, relsrc, exact in srcs:
1213 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
1213 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
1214 errors += 1
1214 errors += 1
1215
1215
1216 if errors:
1216 if errors:
1217 ui.warn(_('(consider using --after)\n'))
1217 ui.warn(_('(consider using --after)\n'))
1218
1218
1219 return errors != 0
1219 return errors != 0
1220
1220
1221 ## facility to let extension process additional data into an import patch
1221 ## facility to let extension process additional data into an import patch
1222 # list of identifier to be executed in order
1222 # list of identifier to be executed in order
1223 extrapreimport = [] # run before commit
1223 extrapreimport = [] # run before commit
1224 extrapostimport = [] # run after commit
1224 extrapostimport = [] # run after commit
1225 # mapping from identifier to actual import function
1225 # mapping from identifier to actual import function
1226 #
1226 #
1227 # 'preimport' are run before the commit is made and are provided the following
1227 # 'preimport' are run before the commit is made and are provided the following
1228 # arguments:
1228 # arguments:
1229 # - repo: the localrepository instance,
1229 # - repo: the localrepository instance,
1230 # - patchdata: data extracted from patch header (cf m.patch.patchheadermap),
1230 # - patchdata: data extracted from patch header (cf m.patch.patchheadermap),
1231 # - extra: the future extra dictionary of the changeset, please mutate it,
1231 # - extra: the future extra dictionary of the changeset, please mutate it,
1232 # - opts: the import options.
1232 # - opts: the import options.
1233 # XXX ideally, we would just pass an ctx ready to be computed, that would allow
1233 # XXX ideally, we would just pass an ctx ready to be computed, that would allow
1234 # mutation of in memory commit and more. Feel free to rework the code to get
1234 # mutation of in memory commit and more. Feel free to rework the code to get
1235 # there.
1235 # there.
1236 extrapreimportmap = {}
1236 extrapreimportmap = {}
1237 # 'postimport' are run after the commit is made and are provided the following
1237 # 'postimport' are run after the commit is made and are provided the following
1238 # argument:
1238 # argument:
1239 # - ctx: the changectx created by import.
1239 # - ctx: the changectx created by import.
1240 extrapostimportmap = {}
1240 extrapostimportmap = {}
1241
1241
1242 def tryimportone(ui, repo, hunk, parents, opts, msgs, updatefunc):
1242 def tryimportone(ui, repo, hunk, parents, opts, msgs, updatefunc):
1243 """Utility function used by commands.import to import a single patch
1243 """Utility function used by commands.import to import a single patch
1244
1244
1245 This function is explicitly defined here to help the evolve extension to
1245 This function is explicitly defined here to help the evolve extension to
1246 wrap this part of the import logic.
1246 wrap this part of the import logic.
1247
1247
1248 The API is currently a bit ugly because it a simple code translation from
1248 The API is currently a bit ugly because it a simple code translation from
1249 the import command. Feel free to make it better.
1249 the import command. Feel free to make it better.
1250
1250
1251 :hunk: a patch (as a binary string)
1251 :hunk: a patch (as a binary string)
1252 :parents: nodes that will be parent of the created commit
1252 :parents: nodes that will be parent of the created commit
1253 :opts: the full dict of option passed to the import command
1253 :opts: the full dict of option passed to the import command
1254 :msgs: list to save commit message to.
1254 :msgs: list to save commit message to.
1255 (used in case we need to save it when failing)
1255 (used in case we need to save it when failing)
1256 :updatefunc: a function that update a repo to a given node
1256 :updatefunc: a function that update a repo to a given node
1257 updatefunc(<repo>, <node>)
1257 updatefunc(<repo>, <node>)
1258 """
1258 """
1259 # avoid cycle context -> subrepo -> cmdutil
1259 # avoid cycle context -> subrepo -> cmdutil
1260 from . import context
1260 from . import context
1261 extractdata = patch.extract(ui, hunk)
1261 extractdata = patch.extract(ui, hunk)
1262 tmpname = extractdata.get('filename')
1262 tmpname = extractdata.get('filename')
1263 message = extractdata.get('message')
1263 message = extractdata.get('message')
1264 user = opts.get('user') or extractdata.get('user')
1264 user = opts.get('user') or extractdata.get('user')
1265 date = opts.get('date') or extractdata.get('date')
1265 date = opts.get('date') or extractdata.get('date')
1266 branch = extractdata.get('branch')
1266 branch = extractdata.get('branch')
1267 nodeid = extractdata.get('nodeid')
1267 nodeid = extractdata.get('nodeid')
1268 p1 = extractdata.get('p1')
1268 p1 = extractdata.get('p1')
1269 p2 = extractdata.get('p2')
1269 p2 = extractdata.get('p2')
1270
1270
1271 nocommit = opts.get('no_commit')
1271 nocommit = opts.get('no_commit')
1272 importbranch = opts.get('import_branch')
1272 importbranch = opts.get('import_branch')
1273 update = not opts.get('bypass')
1273 update = not opts.get('bypass')
1274 strip = opts["strip"]
1274 strip = opts["strip"]
1275 prefix = opts["prefix"]
1275 prefix = opts["prefix"]
1276 sim = float(opts.get('similarity') or 0)
1276 sim = float(opts.get('similarity') or 0)
1277 if not tmpname:
1277 if not tmpname:
1278 return (None, None, False)
1278 return (None, None, False)
1279
1279
1280 rejects = False
1280 rejects = False
1281
1281
1282 try:
1282 try:
1283 cmdline_message = logmessage(ui, opts)
1283 cmdline_message = logmessage(ui, opts)
1284 if cmdline_message:
1284 if cmdline_message:
1285 # pickup the cmdline msg
1285 # pickup the cmdline msg
1286 message = cmdline_message
1286 message = cmdline_message
1287 elif message:
1287 elif message:
1288 # pickup the patch msg
1288 # pickup the patch msg
1289 message = message.strip()
1289 message = message.strip()
1290 else:
1290 else:
1291 # launch the editor
1291 # launch the editor
1292 message = None
1292 message = None
1293 ui.debug('message:\n%s\n' % message)
1293 ui.debug('message:\n%s\n' % message)
1294
1294
1295 if len(parents) == 1:
1295 if len(parents) == 1:
1296 parents.append(repo[nullid])
1296 parents.append(repo[nullid])
1297 if opts.get('exact'):
1297 if opts.get('exact'):
1298 if not nodeid or not p1:
1298 if not nodeid or not p1:
1299 raise error.Abort(_('not a Mercurial patch'))
1299 raise error.Abort(_('not a Mercurial patch'))
1300 p1 = repo[p1]
1300 p1 = repo[p1]
1301 p2 = repo[p2 or nullid]
1301 p2 = repo[p2 or nullid]
1302 elif p2:
1302 elif p2:
1303 try:
1303 try:
1304 p1 = repo[p1]
1304 p1 = repo[p1]
1305 p2 = repo[p2]
1305 p2 = repo[p2]
1306 # Without any options, consider p2 only if the
1306 # Without any options, consider p2 only if the
1307 # patch is being applied on top of the recorded
1307 # patch is being applied on top of the recorded
1308 # first parent.
1308 # first parent.
1309 if p1 != parents[0]:
1309 if p1 != parents[0]:
1310 p1 = parents[0]
1310 p1 = parents[0]
1311 p2 = repo[nullid]
1311 p2 = repo[nullid]
1312 except error.RepoError:
1312 except error.RepoError:
1313 p1, p2 = parents
1313 p1, p2 = parents
1314 if p2.node() == nullid:
1314 if p2.node() == nullid:
1315 ui.warn(_("warning: import the patch as a normal revision\n"
1315 ui.warn(_("warning: import the patch as a normal revision\n"
1316 "(use --exact to import the patch as a merge)\n"))
1316 "(use --exact to import the patch as a merge)\n"))
1317 else:
1317 else:
1318 p1, p2 = parents
1318 p1, p2 = parents
1319
1319
1320 n = None
1320 n = None
1321 if update:
1321 if update:
1322 if p1 != parents[0]:
1322 if p1 != parents[0]:
1323 updatefunc(repo, p1.node())
1323 updatefunc(repo, p1.node())
1324 if p2 != parents[1]:
1324 if p2 != parents[1]:
1325 repo.setparents(p1.node(), p2.node())
1325 repo.setparents(p1.node(), p2.node())
1326
1326
1327 if opts.get('exact') or importbranch:
1327 if opts.get('exact') or importbranch:
1328 repo.dirstate.setbranch(branch or 'default')
1328 repo.dirstate.setbranch(branch or 'default')
1329
1329
1330 partial = opts.get('partial', False)
1330 partial = opts.get('partial', False)
1331 files = set()
1331 files = set()
1332 try:
1332 try:
1333 patch.patch(ui, repo, tmpname, strip=strip, prefix=prefix,
1333 patch.patch(ui, repo, tmpname, strip=strip, prefix=prefix,
1334 files=files, eolmode=None, similarity=sim / 100.0)
1334 files=files, eolmode=None, similarity=sim / 100.0)
1335 except patch.PatchError as e:
1335 except patch.PatchError as e:
1336 if not partial:
1336 if not partial:
1337 raise error.Abort(str(e))
1337 raise error.Abort(str(e))
1338 if partial:
1338 if partial:
1339 rejects = True
1339 rejects = True
1340
1340
1341 files = list(files)
1341 files = list(files)
1342 if nocommit:
1342 if nocommit:
1343 if message:
1343 if message:
1344 msgs.append(message)
1344 msgs.append(message)
1345 else:
1345 else:
1346 if opts.get('exact') or p2:
1346 if opts.get('exact') or p2:
1347 # If you got here, you either use --force and know what
1347 # If you got here, you either use --force and know what
1348 # you are doing or used --exact or a merge patch while
1348 # you are doing or used --exact or a merge patch while
1349 # being updated to its first parent.
1349 # being updated to its first parent.
1350 m = None
1350 m = None
1351 else:
1351 else:
1352 m = scmutil.matchfiles(repo, files or [])
1352 m = scmutil.matchfiles(repo, files or [])
1353 editform = mergeeditform(repo[None], 'import.normal')
1353 editform = mergeeditform(repo[None], 'import.normal')
1354 if opts.get('exact'):
1354 if opts.get('exact'):
1355 editor = None
1355 editor = None
1356 else:
1356 else:
1357 editor = getcommiteditor(editform=editform, **opts)
1357 editor = getcommiteditor(editform=editform, **opts)
1358 extra = {}
1358 extra = {}
1359 for idfunc in extrapreimport:
1359 for idfunc in extrapreimport:
1360 extrapreimportmap[idfunc](repo, extractdata, extra, opts)
1360 extrapreimportmap[idfunc](repo, extractdata, extra, opts)
1361 overrides = {}
1361 overrides = {}
1362 if partial:
1362 if partial:
1363 overrides[('ui', 'allowemptycommit')] = True
1363 overrides[('ui', 'allowemptycommit')] = True
1364 with repo.ui.configoverride(overrides, 'import'):
1364 with repo.ui.configoverride(overrides, 'import'):
1365 n = repo.commit(message, user,
1365 n = repo.commit(message, user,
1366 date, match=m,
1366 date, match=m,
1367 editor=editor, extra=extra)
1367 editor=editor, extra=extra)
1368 for idfunc in extrapostimport:
1368 for idfunc in extrapostimport:
1369 extrapostimportmap[idfunc](repo[n])
1369 extrapostimportmap[idfunc](repo[n])
1370 else:
1370 else:
1371 if opts.get('exact') or importbranch:
1371 if opts.get('exact') or importbranch:
1372 branch = branch or 'default'
1372 branch = branch or 'default'
1373 else:
1373 else:
1374 branch = p1.branch()
1374 branch = p1.branch()
1375 store = patch.filestore()
1375 store = patch.filestore()
1376 try:
1376 try:
1377 files = set()
1377 files = set()
1378 try:
1378 try:
1379 patch.patchrepo(ui, repo, p1, store, tmpname, strip, prefix,
1379 patch.patchrepo(ui, repo, p1, store, tmpname, strip, prefix,
1380 files, eolmode=None)
1380 files, eolmode=None)
1381 except patch.PatchError as e:
1381 except patch.PatchError as e:
1382 raise error.Abort(str(e))
1382 raise error.Abort(str(e))
1383 if opts.get('exact'):
1383 if opts.get('exact'):
1384 editor = None
1384 editor = None
1385 else:
1385 else:
1386 editor = getcommiteditor(editform='import.bypass')
1386 editor = getcommiteditor(editform='import.bypass')
1387 memctx = context.memctx(repo, (p1.node(), p2.node()),
1387 memctx = context.memctx(repo, (p1.node(), p2.node()),
1388 message,
1388 message,
1389 files=files,
1389 files=files,
1390 filectxfn=store,
1390 filectxfn=store,
1391 user=user,
1391 user=user,
1392 date=date,
1392 date=date,
1393 branch=branch,
1393 branch=branch,
1394 editor=editor)
1394 editor=editor)
1395 n = memctx.commit()
1395 n = memctx.commit()
1396 finally:
1396 finally:
1397 store.close()
1397 store.close()
1398 if opts.get('exact') and nocommit:
1398 if opts.get('exact') and nocommit:
1399 # --exact with --no-commit is still useful in that it does merge
1399 # --exact with --no-commit is still useful in that it does merge
1400 # and branch bits
1400 # and branch bits
1401 ui.warn(_("warning: can't check exact import with --no-commit\n"))
1401 ui.warn(_("warning: can't check exact import with --no-commit\n"))
1402 elif opts.get('exact') and hex(n) != nodeid:
1402 elif opts.get('exact') and hex(n) != nodeid:
1403 raise error.Abort(_('patch is damaged or loses information'))
1403 raise error.Abort(_('patch is damaged or loses information'))
1404 msg = _('applied to working directory')
1404 msg = _('applied to working directory')
1405 if n:
1405 if n:
1406 # i18n: refers to a short changeset id
1406 # i18n: refers to a short changeset id
1407 msg = _('created %s') % short(n)
1407 msg = _('created %s') % short(n)
1408 return (msg, n, rejects)
1408 return (msg, n, rejects)
1409 finally:
1409 finally:
1410 os.unlink(tmpname)
1410 os.unlink(tmpname)
1411
1411
1412 # facility to let extensions include additional data in an exported patch
1412 # facility to let extensions include additional data in an exported patch
1413 # list of identifiers to be executed in order
1413 # list of identifiers to be executed in order
1414 extraexport = []
1414 extraexport = []
1415 # mapping from identifier to actual export function
1415 # mapping from identifier to actual export function
1416 # function as to return a string to be added to the header or None
1416 # function as to return a string to be added to the header or None
1417 # it is given two arguments (sequencenumber, changectx)
1417 # it is given two arguments (sequencenumber, changectx)
1418 extraexportmap = {}
1418 extraexportmap = {}
1419
1419
1420 def _exportsingle(repo, ctx, match, switch_parent, rev, seqno, write, diffopts):
1420 def _exportsingle(repo, ctx, match, switch_parent, rev, seqno, write, diffopts):
1421 node = scmutil.binnode(ctx)
1421 node = scmutil.binnode(ctx)
1422 parents = [p.node() for p in ctx.parents() if p]
1422 parents = [p.node() for p in ctx.parents() if p]
1423 branch = ctx.branch()
1423 branch = ctx.branch()
1424 if switch_parent:
1424 if switch_parent:
1425 parents.reverse()
1425 parents.reverse()
1426
1426
1427 if parents:
1427 if parents:
1428 prev = parents[0]
1428 prev = parents[0]
1429 else:
1429 else:
1430 prev = nullid
1430 prev = nullid
1431
1431
1432 write("# HG changeset patch\n")
1432 write("# HG changeset patch\n")
1433 write("# User %s\n" % ctx.user())
1433 write("# User %s\n" % ctx.user())
1434 write("# Date %d %d\n" % ctx.date())
1434 write("# Date %d %d\n" % ctx.date())
1435 write("# %s\n" % util.datestr(ctx.date()))
1435 write("# %s\n" % util.datestr(ctx.date()))
1436 if branch and branch != 'default':
1436 if branch and branch != 'default':
1437 write("# Branch %s\n" % branch)
1437 write("# Branch %s\n" % branch)
1438 write("# Node ID %s\n" % hex(node))
1438 write("# Node ID %s\n" % hex(node))
1439 write("# Parent %s\n" % hex(prev))
1439 write("# Parent %s\n" % hex(prev))
1440 if len(parents) > 1:
1440 if len(parents) > 1:
1441 write("# Parent %s\n" % hex(parents[1]))
1441 write("# Parent %s\n" % hex(parents[1]))
1442
1442
1443 for headerid in extraexport:
1443 for headerid in extraexport:
1444 header = extraexportmap[headerid](seqno, ctx)
1444 header = extraexportmap[headerid](seqno, ctx)
1445 if header is not None:
1445 if header is not None:
1446 write('# %s\n' % header)
1446 write('# %s\n' % header)
1447 write(ctx.description().rstrip())
1447 write(ctx.description().rstrip())
1448 write("\n\n")
1448 write("\n\n")
1449
1449
1450 for chunk, label in patch.diffui(repo, prev, node, match, opts=diffopts):
1450 for chunk, label in patch.diffui(repo, prev, node, match, opts=diffopts):
1451 write(chunk, label=label)
1451 write(chunk, label=label)
1452
1452
1453 def export(repo, revs, fntemplate='hg-%h.patch', fp=None, switch_parent=False,
1453 def export(repo, revs, fntemplate='hg-%h.patch', fp=None, switch_parent=False,
1454 opts=None, match=None):
1454 opts=None, match=None):
1455 '''export changesets as hg patches
1455 '''export changesets as hg patches
1456
1456
1457 Args:
1457 Args:
1458 repo: The repository from which we're exporting revisions.
1458 repo: The repository from which we're exporting revisions.
1459 revs: A list of revisions to export as revision numbers.
1459 revs: A list of revisions to export as revision numbers.
1460 fntemplate: An optional string to use for generating patch file names.
1460 fntemplate: An optional string to use for generating patch file names.
1461 fp: An optional file-like object to which patches should be written.
1461 fp: An optional file-like object to which patches should be written.
1462 switch_parent: If True, show diffs against second parent when not nullid.
1462 switch_parent: If True, show diffs against second parent when not nullid.
1463 Default is false, which always shows diff against p1.
1463 Default is false, which always shows diff against p1.
1464 opts: diff options to use for generating the patch.
1464 opts: diff options to use for generating the patch.
1465 match: If specified, only export changes to files matching this matcher.
1465 match: If specified, only export changes to files matching this matcher.
1466
1466
1467 Returns:
1467 Returns:
1468 Nothing.
1468 Nothing.
1469
1469
1470 Side Effect:
1470 Side Effect:
1471 "HG Changeset Patch" data is emitted to one of the following
1471 "HG Changeset Patch" data is emitted to one of the following
1472 destinations:
1472 destinations:
1473 fp is specified: All revs are written to the specified
1473 fp is specified: All revs are written to the specified
1474 file-like object.
1474 file-like object.
1475 fntemplate specified: Each rev is written to a unique file named using
1475 fntemplate specified: Each rev is written to a unique file named using
1476 the given template.
1476 the given template.
1477 Neither fp nor template specified: All revs written to repo.ui.write()
1477 Neither fp nor template specified: All revs written to repo.ui.write()
1478 '''
1478 '''
1479
1479
1480 total = len(revs)
1480 total = len(revs)
1481 revwidth = max(len(str(rev)) for rev in revs)
1481 revwidth = max(len(str(rev)) for rev in revs)
1482 filemode = {}
1482 filemode = {}
1483
1483
1484 write = None
1484 write = None
1485 dest = '<unnamed>'
1485 dest = '<unnamed>'
1486 if fp:
1486 if fp:
1487 dest = getattr(fp, 'name', dest)
1487 dest = getattr(fp, 'name', dest)
1488 def write(s, **kw):
1488 def write(s, **kw):
1489 fp.write(s)
1489 fp.write(s)
1490 elif not fntemplate:
1490 elif not fntemplate:
1491 write = repo.ui.write
1491 write = repo.ui.write
1492
1492
1493 for seqno, rev in enumerate(revs, 1):
1493 for seqno, rev in enumerate(revs, 1):
1494 ctx = repo[rev]
1494 ctx = repo[rev]
1495 fo = None
1495 fo = None
1496 if not fp and fntemplate:
1496 if not fp and fntemplate:
1497 desc_lines = ctx.description().rstrip().split('\n')
1497 desc_lines = ctx.description().rstrip().split('\n')
1498 desc = desc_lines[0] #Commit always has a first line.
1498 desc = desc_lines[0] #Commit always has a first line.
1499 fo = makefileobj(repo, fntemplate, ctx.node(), desc=desc,
1499 fo = makefileobj(repo, fntemplate, ctx.node(), desc=desc,
1500 total=total, seqno=seqno, revwidth=revwidth,
1500 total=total, seqno=seqno, revwidth=revwidth,
1501 mode='wb', modemap=filemode)
1501 mode='wb', modemap=filemode)
1502 dest = fo.name
1502 dest = fo.name
1503 def write(s, **kw):
1503 def write(s, **kw):
1504 fo.write(s)
1504 fo.write(s)
1505 if not dest.startswith('<'):
1505 if not dest.startswith('<'):
1506 repo.ui.note("%s\n" % dest)
1506 repo.ui.note("%s\n" % dest)
1507 _exportsingle(
1507 _exportsingle(
1508 repo, ctx, match, switch_parent, rev, seqno, write, opts)
1508 repo, ctx, match, switch_parent, rev, seqno, write, opts)
1509 if fo is not None:
1509 if fo is not None:
1510 fo.close()
1510 fo.close()
1511
1511
1512 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
1512 def diffordiffstat(ui, repo, diffopts, node1, node2, match,
1513 changes=None, stat=False, fp=None, prefix='',
1513 changes=None, stat=False, fp=None, prefix='',
1514 root='', listsubrepos=False):
1514 root='', listsubrepos=False):
1515 '''show diff or diffstat.'''
1515 '''show diff or diffstat.'''
1516 if fp is None:
1516 if fp is None:
1517 write = ui.write
1517 write = ui.write
1518 else:
1518 else:
1519 def write(s, **kw):
1519 def write(s, **kw):
1520 fp.write(s)
1520 fp.write(s)
1521
1521
1522 if root:
1522 if root:
1523 relroot = pathutil.canonpath(repo.root, repo.getcwd(), root)
1523 relroot = pathutil.canonpath(repo.root, repo.getcwd(), root)
1524 else:
1524 else:
1525 relroot = ''
1525 relroot = ''
1526 if relroot != '':
1526 if relroot != '':
1527 # XXX relative roots currently don't work if the root is within a
1527 # XXX relative roots currently don't work if the root is within a
1528 # subrepo
1528 # subrepo
1529 uirelroot = match.uipath(relroot)
1529 uirelroot = match.uipath(relroot)
1530 relroot += '/'
1530 relroot += '/'
1531 for matchroot in match.files():
1531 for matchroot in match.files():
1532 if not matchroot.startswith(relroot):
1532 if not matchroot.startswith(relroot):
1533 ui.warn(_('warning: %s not inside relative root %s\n') % (
1533 ui.warn(_('warning: %s not inside relative root %s\n') % (
1534 match.uipath(matchroot), uirelroot))
1534 match.uipath(matchroot), uirelroot))
1535
1535
1536 if stat:
1536 if stat:
1537 diffopts = diffopts.copy(context=0)
1537 diffopts = diffopts.copy(context=0)
1538 width = 80
1538 width = 80
1539 if not ui.plain():
1539 if not ui.plain():
1540 width = ui.termwidth()
1540 width = ui.termwidth()
1541 chunks = patch.diff(repo, node1, node2, match, changes, diffopts,
1541 chunks = patch.diff(repo, node1, node2, match, changes, diffopts,
1542 prefix=prefix, relroot=relroot)
1542 prefix=prefix, relroot=relroot)
1543 for chunk, label in patch.diffstatui(util.iterlines(chunks),
1543 for chunk, label in patch.diffstatui(util.iterlines(chunks),
1544 width=width):
1544 width=width):
1545 write(chunk, label=label)
1545 write(chunk, label=label)
1546 else:
1546 else:
1547 for chunk, label in patch.diffui(repo, node1, node2, match,
1547 for chunk, label in patch.diffui(repo, node1, node2, match,
1548 changes, diffopts, prefix=prefix,
1548 changes, diffopts, prefix=prefix,
1549 relroot=relroot):
1549 relroot=relroot):
1550 write(chunk, label=label)
1550 write(chunk, label=label)
1551
1551
1552 if listsubrepos:
1552 if listsubrepos:
1553 ctx1 = repo[node1]
1553 ctx1 = repo[node1]
1554 ctx2 = repo[node2]
1554 ctx2 = repo[node2]
1555 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
1555 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
1556 tempnode2 = node2
1556 tempnode2 = node2
1557 try:
1557 try:
1558 if node2 is not None:
1558 if node2 is not None:
1559 tempnode2 = ctx2.substate[subpath][1]
1559 tempnode2 = ctx2.substate[subpath][1]
1560 except KeyError:
1560 except KeyError:
1561 # A subrepo that existed in node1 was deleted between node1 and
1561 # A subrepo that existed in node1 was deleted between node1 and
1562 # node2 (inclusive). Thus, ctx2's substate won't contain that
1562 # node2 (inclusive). Thus, ctx2's substate won't contain that
1563 # subpath. The best we can do is to ignore it.
1563 # subpath. The best we can do is to ignore it.
1564 tempnode2 = None
1564 tempnode2 = None
1565 submatch = matchmod.subdirmatcher(subpath, match)
1565 submatch = matchmod.subdirmatcher(subpath, match)
1566 sub.diff(ui, diffopts, tempnode2, submatch, changes=changes,
1566 sub.diff(ui, diffopts, tempnode2, submatch, changes=changes,
1567 stat=stat, fp=fp, prefix=prefix)
1567 stat=stat, fp=fp, prefix=prefix)
1568
1568
1569 def _changesetlabels(ctx):
1569 def _changesetlabels(ctx):
1570 labels = ['log.changeset', 'changeset.%s' % ctx.phasestr()]
1570 labels = ['log.changeset', 'changeset.%s' % ctx.phasestr()]
1571 if ctx.obsolete():
1571 if ctx.obsolete():
1572 labels.append('changeset.obsolete')
1572 labels.append('changeset.obsolete')
1573 if ctx.isunstable():
1573 if ctx.isunstable():
1574 labels.append('changeset.unstable')
1574 labels.append('changeset.unstable')
1575 for instability in ctx.instabilities():
1575 for instability in ctx.instabilities():
1576 labels.append('instability.%s' % instability)
1576 labels.append('instability.%s' % instability)
1577 return ' '.join(labels)
1577 return ' '.join(labels)
1578
1578
1579 class changeset_printer(object):
1579 class changeset_printer(object):
1580 '''show changeset information when templating not requested.'''
1580 '''show changeset information when templating not requested.'''
1581
1581
1582 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1582 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1583 self.ui = ui
1583 self.ui = ui
1584 self.repo = repo
1584 self.repo = repo
1585 self.buffered = buffered
1585 self.buffered = buffered
1586 self.matchfn = matchfn
1586 self.matchfn = matchfn
1587 self.diffopts = diffopts
1587 self.diffopts = diffopts
1588 self.header = {}
1588 self.header = {}
1589 self.hunk = {}
1589 self.hunk = {}
1590 self.lastheader = None
1590 self.lastheader = None
1591 self.footer = None
1591 self.footer = None
1592
1592
1593 def flush(self, ctx):
1593 def flush(self, ctx):
1594 rev = ctx.rev()
1594 rev = ctx.rev()
1595 if rev in self.header:
1595 if rev in self.header:
1596 h = self.header[rev]
1596 h = self.header[rev]
1597 if h != self.lastheader:
1597 if h != self.lastheader:
1598 self.lastheader = h
1598 self.lastheader = h
1599 self.ui.write(h)
1599 self.ui.write(h)
1600 del self.header[rev]
1600 del self.header[rev]
1601 if rev in self.hunk:
1601 if rev in self.hunk:
1602 self.ui.write(self.hunk[rev])
1602 self.ui.write(self.hunk[rev])
1603 del self.hunk[rev]
1603 del self.hunk[rev]
1604 return 1
1604 return 1
1605 return 0
1605 return 0
1606
1606
1607 def close(self):
1607 def close(self):
1608 if self.footer:
1608 if self.footer:
1609 self.ui.write(self.footer)
1609 self.ui.write(self.footer)
1610
1610
1611 def show(self, ctx, copies=None, matchfn=None, **props):
1611 def show(self, ctx, copies=None, matchfn=None, **props):
1612 props = pycompat.byteskwargs(props)
1612 props = pycompat.byteskwargs(props)
1613 if self.buffered:
1613 if self.buffered:
1614 self.ui.pushbuffer(labeled=True)
1614 self.ui.pushbuffer(labeled=True)
1615 self._show(ctx, copies, matchfn, props)
1615 self._show(ctx, copies, matchfn, props)
1616 self.hunk[ctx.rev()] = self.ui.popbuffer()
1616 self.hunk[ctx.rev()] = self.ui.popbuffer()
1617 else:
1617 else:
1618 self._show(ctx, copies, matchfn, props)
1618 self._show(ctx, copies, matchfn, props)
1619
1619
1620 def _show(self, ctx, copies, matchfn, props):
1620 def _show(self, ctx, copies, matchfn, props):
1621 '''show a single changeset or file revision'''
1621 '''show a single changeset or file revision'''
1622 changenode = ctx.node()
1622 changenode = ctx.node()
1623 rev = ctx.rev()
1623 rev = ctx.rev()
1624 if self.ui.debugflag:
1624 if self.ui.debugflag:
1625 hexfunc = hex
1625 hexfunc = hex
1626 else:
1626 else:
1627 hexfunc = short
1627 hexfunc = short
1628 # as of now, wctx.node() and wctx.rev() return None, but we want to
1628 # as of now, wctx.node() and wctx.rev() return None, but we want to
1629 # show the same values as {node} and {rev} templatekw
1629 # show the same values as {node} and {rev} templatekw
1630 revnode = (scmutil.intrev(ctx), hexfunc(scmutil.binnode(ctx)))
1630 revnode = (scmutil.intrev(ctx), hexfunc(scmutil.binnode(ctx)))
1631
1631
1632 if self.ui.quiet:
1632 if self.ui.quiet:
1633 self.ui.write("%d:%s\n" % revnode, label='log.node')
1633 self.ui.write("%d:%s\n" % revnode, label='log.node')
1634 return
1634 return
1635
1635
1636 date = util.datestr(ctx.date())
1636 date = util.datestr(ctx.date())
1637
1637
1638 # i18n: column positioning for "hg log"
1638 # i18n: column positioning for "hg log"
1639 self.ui.write(_("changeset: %d:%s\n") % revnode,
1639 self.ui.write(_("changeset: %d:%s\n") % revnode,
1640 label=_changesetlabels(ctx))
1640 label=_changesetlabels(ctx))
1641
1641
1642 # branches are shown first before any other names due to backwards
1642 # branches are shown first before any other names due to backwards
1643 # compatibility
1643 # compatibility
1644 branch = ctx.branch()
1644 branch = ctx.branch()
1645 # don't show the default branch name
1645 # don't show the default branch name
1646 if branch != 'default':
1646 if branch != 'default':
1647 # i18n: column positioning for "hg log"
1647 # i18n: column positioning for "hg log"
1648 self.ui.write(_("branch: %s\n") % branch,
1648 self.ui.write(_("branch: %s\n") % branch,
1649 label='log.branch')
1649 label='log.branch')
1650
1650
1651 for nsname, ns in self.repo.names.iteritems():
1651 for nsname, ns in self.repo.names.iteritems():
1652 # branches has special logic already handled above, so here we just
1652 # branches has special logic already handled above, so here we just
1653 # skip it
1653 # skip it
1654 if nsname == 'branches':
1654 if nsname == 'branches':
1655 continue
1655 continue
1656 # we will use the templatename as the color name since those two
1656 # we will use the templatename as the color name since those two
1657 # should be the same
1657 # should be the same
1658 for name in ns.names(self.repo, changenode):
1658 for name in ns.names(self.repo, changenode):
1659 self.ui.write(ns.logfmt % name,
1659 self.ui.write(ns.logfmt % name,
1660 label='log.%s' % ns.colorname)
1660 label='log.%s' % ns.colorname)
1661 if self.ui.debugflag:
1661 if self.ui.debugflag:
1662 # i18n: column positioning for "hg log"
1662 # i18n: column positioning for "hg log"
1663 self.ui.write(_("phase: %s\n") % ctx.phasestr(),
1663 self.ui.write(_("phase: %s\n") % ctx.phasestr(),
1664 label='log.phase')
1664 label='log.phase')
1665 for pctx in scmutil.meaningfulparents(self.repo, ctx):
1665 for pctx in scmutil.meaningfulparents(self.repo, ctx):
1666 label = 'log.parent changeset.%s' % pctx.phasestr()
1666 label = 'log.parent changeset.%s' % pctx.phasestr()
1667 # i18n: column positioning for "hg log"
1667 # i18n: column positioning for "hg log"
1668 self.ui.write(_("parent: %d:%s\n")
1668 self.ui.write(_("parent: %d:%s\n")
1669 % (pctx.rev(), hexfunc(pctx.node())),
1669 % (pctx.rev(), hexfunc(pctx.node())),
1670 label=label)
1670 label=label)
1671
1671
1672 if self.ui.debugflag and rev is not None:
1672 if self.ui.debugflag and rev is not None:
1673 mnode = ctx.manifestnode()
1673 mnode = ctx.manifestnode()
1674 # i18n: column positioning for "hg log"
1674 # i18n: column positioning for "hg log"
1675 self.ui.write(_("manifest: %d:%s\n") %
1675 self.ui.write(_("manifest: %d:%s\n") %
1676 (self.repo.manifestlog._revlog.rev(mnode),
1676 (self.repo.manifestlog._revlog.rev(mnode),
1677 hex(mnode)),
1677 hex(mnode)),
1678 label='ui.debug log.manifest')
1678 label='ui.debug log.manifest')
1679 # i18n: column positioning for "hg log"
1679 # i18n: column positioning for "hg log"
1680 self.ui.write(_("user: %s\n") % ctx.user(),
1680 self.ui.write(_("user: %s\n") % ctx.user(),
1681 label='log.user')
1681 label='log.user')
1682 # i18n: column positioning for "hg log"
1682 # i18n: column positioning for "hg log"
1683 self.ui.write(_("date: %s\n") % date,
1683 self.ui.write(_("date: %s\n") % date,
1684 label='log.date')
1684 label='log.date')
1685
1685
1686 if ctx.isunstable():
1686 if ctx.isunstable():
1687 # i18n: column positioning for "hg log"
1687 # i18n: column positioning for "hg log"
1688 instabilities = ctx.instabilities()
1688 instabilities = ctx.instabilities()
1689 self.ui.write(_("instability: %s\n") % ', '.join(instabilities),
1689 self.ui.write(_("instability: %s\n") % ', '.join(instabilities),
1690 label='log.instability')
1690 label='log.instability')
1691
1691
1692 self._exthook(ctx)
1692 self._exthook(ctx)
1693
1693
1694 if self.ui.debugflag:
1694 if self.ui.debugflag:
1695 files = ctx.p1().status(ctx)[:3]
1695 files = ctx.p1().status(ctx)[:3]
1696 for key, value in zip([# i18n: column positioning for "hg log"
1696 for key, value in zip([# i18n: column positioning for "hg log"
1697 _("files:"),
1697 _("files:"),
1698 # i18n: column positioning for "hg log"
1698 # i18n: column positioning for "hg log"
1699 _("files+:"),
1699 _("files+:"),
1700 # i18n: column positioning for "hg log"
1700 # i18n: column positioning for "hg log"
1701 _("files-:")], files):
1701 _("files-:")], files):
1702 if value:
1702 if value:
1703 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
1703 self.ui.write("%-12s %s\n" % (key, " ".join(value)),
1704 label='ui.debug log.files')
1704 label='ui.debug log.files')
1705 elif ctx.files() and self.ui.verbose:
1705 elif ctx.files() and self.ui.verbose:
1706 # i18n: column positioning for "hg log"
1706 # i18n: column positioning for "hg log"
1707 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
1707 self.ui.write(_("files: %s\n") % " ".join(ctx.files()),
1708 label='ui.note log.files')
1708 label='ui.note log.files')
1709 if copies and self.ui.verbose:
1709 if copies and self.ui.verbose:
1710 copies = ['%s (%s)' % c for c in copies]
1710 copies = ['%s (%s)' % c for c in copies]
1711 # i18n: column positioning for "hg log"
1711 # i18n: column positioning for "hg log"
1712 self.ui.write(_("copies: %s\n") % ' '.join(copies),
1712 self.ui.write(_("copies: %s\n") % ' '.join(copies),
1713 label='ui.note log.copies')
1713 label='ui.note log.copies')
1714
1714
1715 extra = ctx.extra()
1715 extra = ctx.extra()
1716 if extra and self.ui.debugflag:
1716 if extra and self.ui.debugflag:
1717 for key, value in sorted(extra.items()):
1717 for key, value in sorted(extra.items()):
1718 # i18n: column positioning for "hg log"
1718 # i18n: column positioning for "hg log"
1719 self.ui.write(_("extra: %s=%s\n")
1719 self.ui.write(_("extra: %s=%s\n")
1720 % (key, util.escapestr(value)),
1720 % (key, util.escapestr(value)),
1721 label='ui.debug log.extra')
1721 label='ui.debug log.extra')
1722
1722
1723 description = ctx.description().strip()
1723 description = ctx.description().strip()
1724 if description:
1724 if description:
1725 if self.ui.verbose:
1725 if self.ui.verbose:
1726 self.ui.write(_("description:\n"),
1726 self.ui.write(_("description:\n"),
1727 label='ui.note log.description')
1727 label='ui.note log.description')
1728 self.ui.write(description,
1728 self.ui.write(description,
1729 label='ui.note log.description')
1729 label='ui.note log.description')
1730 self.ui.write("\n\n")
1730 self.ui.write("\n\n")
1731 else:
1731 else:
1732 # i18n: column positioning for "hg log"
1732 # i18n: column positioning for "hg log"
1733 self.ui.write(_("summary: %s\n") %
1733 self.ui.write(_("summary: %s\n") %
1734 description.splitlines()[0],
1734 description.splitlines()[0],
1735 label='log.summary')
1735 label='log.summary')
1736 self.ui.write("\n")
1736 self.ui.write("\n")
1737
1737
1738 self.showpatch(ctx, matchfn)
1738 self.showpatch(ctx, matchfn)
1739
1739
1740 def _exthook(self, ctx):
1740 def _exthook(self, ctx):
1741 '''empty method used by extension as a hook point
1741 '''empty method used by extension as a hook point
1742 '''
1742 '''
1743 pass
1743 pass
1744
1744
1745 def showpatch(self, ctx, matchfn):
1745 def showpatch(self, ctx, matchfn):
1746 if not matchfn:
1746 if not matchfn:
1747 matchfn = self.matchfn
1747 matchfn = self.matchfn
1748 if matchfn:
1748 if matchfn:
1749 stat = self.diffopts.get('stat')
1749 stat = self.diffopts.get('stat')
1750 diff = self.diffopts.get('patch')
1750 diff = self.diffopts.get('patch')
1751 diffopts = patch.diffallopts(self.ui, self.diffopts)
1751 diffopts = patch.diffallopts(self.ui, self.diffopts)
1752 node = ctx.node()
1752 node = ctx.node()
1753 prev = ctx.p1().node()
1753 prev = ctx.p1().node()
1754 if stat:
1754 if stat:
1755 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1755 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1756 match=matchfn, stat=True)
1756 match=matchfn, stat=True)
1757 if diff:
1757 if diff:
1758 if stat:
1758 if stat:
1759 self.ui.write("\n")
1759 self.ui.write("\n")
1760 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1760 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1761 match=matchfn, stat=False)
1761 match=matchfn, stat=False)
1762 self.ui.write("\n")
1762 self.ui.write("\n")
1763
1763
1764 class jsonchangeset(changeset_printer):
1764 class jsonchangeset(changeset_printer):
1765 '''format changeset information.'''
1765 '''format changeset information.'''
1766
1766
1767 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1767 def __init__(self, ui, repo, matchfn, diffopts, buffered):
1768 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1768 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1769 self.cache = {}
1769 self.cache = {}
1770 self._first = True
1770 self._first = True
1771
1771
1772 def close(self):
1772 def close(self):
1773 if not self._first:
1773 if not self._first:
1774 self.ui.write("\n]\n")
1774 self.ui.write("\n]\n")
1775 else:
1775 else:
1776 self.ui.write("[]\n")
1776 self.ui.write("[]\n")
1777
1777
1778 def _show(self, ctx, copies, matchfn, props):
1778 def _show(self, ctx, copies, matchfn, props):
1779 '''show a single changeset or file revision'''
1779 '''show a single changeset or file revision'''
1780 rev = ctx.rev()
1780 rev = ctx.rev()
1781 if rev is None:
1781 if rev is None:
1782 jrev = jnode = 'null'
1782 jrev = jnode = 'null'
1783 else:
1783 else:
1784 jrev = '%d' % rev
1784 jrev = '%d' % rev
1785 jnode = '"%s"' % hex(ctx.node())
1785 jnode = '"%s"' % hex(ctx.node())
1786 j = encoding.jsonescape
1786 j = encoding.jsonescape
1787
1787
1788 if self._first:
1788 if self._first:
1789 self.ui.write("[\n {")
1789 self.ui.write("[\n {")
1790 self._first = False
1790 self._first = False
1791 else:
1791 else:
1792 self.ui.write(",\n {")
1792 self.ui.write(",\n {")
1793
1793
1794 if self.ui.quiet:
1794 if self.ui.quiet:
1795 self.ui.write(('\n "rev": %s') % jrev)
1795 self.ui.write(('\n "rev": %s') % jrev)
1796 self.ui.write((',\n "node": %s') % jnode)
1796 self.ui.write((',\n "node": %s') % jnode)
1797 self.ui.write('\n }')
1797 self.ui.write('\n }')
1798 return
1798 return
1799
1799
1800 self.ui.write(('\n "rev": %s') % jrev)
1800 self.ui.write(('\n "rev": %s') % jrev)
1801 self.ui.write((',\n "node": %s') % jnode)
1801 self.ui.write((',\n "node": %s') % jnode)
1802 self.ui.write((',\n "branch": "%s"') % j(ctx.branch()))
1802 self.ui.write((',\n "branch": "%s"') % j(ctx.branch()))
1803 self.ui.write((',\n "phase": "%s"') % ctx.phasestr())
1803 self.ui.write((',\n "phase": "%s"') % ctx.phasestr())
1804 self.ui.write((',\n "user": "%s"') % j(ctx.user()))
1804 self.ui.write((',\n "user": "%s"') % j(ctx.user()))
1805 self.ui.write((',\n "date": [%d, %d]') % ctx.date())
1805 self.ui.write((',\n "date": [%d, %d]') % ctx.date())
1806 self.ui.write((',\n "desc": "%s"') % j(ctx.description()))
1806 self.ui.write((',\n "desc": "%s"') % j(ctx.description()))
1807
1807
1808 self.ui.write((',\n "bookmarks": [%s]') %
1808 self.ui.write((',\n "bookmarks": [%s]') %
1809 ", ".join('"%s"' % j(b) for b in ctx.bookmarks()))
1809 ", ".join('"%s"' % j(b) for b in ctx.bookmarks()))
1810 self.ui.write((',\n "tags": [%s]') %
1810 self.ui.write((',\n "tags": [%s]') %
1811 ", ".join('"%s"' % j(t) for t in ctx.tags()))
1811 ", ".join('"%s"' % j(t) for t in ctx.tags()))
1812 self.ui.write((',\n "parents": [%s]') %
1812 self.ui.write((',\n "parents": [%s]') %
1813 ", ".join('"%s"' % c.hex() for c in ctx.parents()))
1813 ", ".join('"%s"' % c.hex() for c in ctx.parents()))
1814
1814
1815 if self.ui.debugflag:
1815 if self.ui.debugflag:
1816 if rev is None:
1816 if rev is None:
1817 jmanifestnode = 'null'
1817 jmanifestnode = 'null'
1818 else:
1818 else:
1819 jmanifestnode = '"%s"' % hex(ctx.manifestnode())
1819 jmanifestnode = '"%s"' % hex(ctx.manifestnode())
1820 self.ui.write((',\n "manifest": %s') % jmanifestnode)
1820 self.ui.write((',\n "manifest": %s') % jmanifestnode)
1821
1821
1822 self.ui.write((',\n "extra": {%s}') %
1822 self.ui.write((',\n "extra": {%s}') %
1823 ", ".join('"%s": "%s"' % (j(k), j(v))
1823 ", ".join('"%s": "%s"' % (j(k), j(v))
1824 for k, v in ctx.extra().items()))
1824 for k, v in ctx.extra().items()))
1825
1825
1826 files = ctx.p1().status(ctx)
1826 files = ctx.p1().status(ctx)
1827 self.ui.write((',\n "modified": [%s]') %
1827 self.ui.write((',\n "modified": [%s]') %
1828 ", ".join('"%s"' % j(f) for f in files[0]))
1828 ", ".join('"%s"' % j(f) for f in files[0]))
1829 self.ui.write((',\n "added": [%s]') %
1829 self.ui.write((',\n "added": [%s]') %
1830 ", ".join('"%s"' % j(f) for f in files[1]))
1830 ", ".join('"%s"' % j(f) for f in files[1]))
1831 self.ui.write((',\n "removed": [%s]') %
1831 self.ui.write((',\n "removed": [%s]') %
1832 ", ".join('"%s"' % j(f) for f in files[2]))
1832 ", ".join('"%s"' % j(f) for f in files[2]))
1833
1833
1834 elif self.ui.verbose:
1834 elif self.ui.verbose:
1835 self.ui.write((',\n "files": [%s]') %
1835 self.ui.write((',\n "files": [%s]') %
1836 ", ".join('"%s"' % j(f) for f in ctx.files()))
1836 ", ".join('"%s"' % j(f) for f in ctx.files()))
1837
1837
1838 if copies:
1838 if copies:
1839 self.ui.write((',\n "copies": {%s}') %
1839 self.ui.write((',\n "copies": {%s}') %
1840 ", ".join('"%s": "%s"' % (j(k), j(v))
1840 ", ".join('"%s": "%s"' % (j(k), j(v))
1841 for k, v in copies))
1841 for k, v in copies))
1842
1842
1843 matchfn = self.matchfn
1843 matchfn = self.matchfn
1844 if matchfn:
1844 if matchfn:
1845 stat = self.diffopts.get('stat')
1845 stat = self.diffopts.get('stat')
1846 diff = self.diffopts.get('patch')
1846 diff = self.diffopts.get('patch')
1847 diffopts = patch.difffeatureopts(self.ui, self.diffopts, git=True)
1847 diffopts = patch.difffeatureopts(self.ui, self.diffopts, git=True)
1848 node, prev = ctx.node(), ctx.p1().node()
1848 node, prev = ctx.node(), ctx.p1().node()
1849 if stat:
1849 if stat:
1850 self.ui.pushbuffer()
1850 self.ui.pushbuffer()
1851 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1851 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1852 match=matchfn, stat=True)
1852 match=matchfn, stat=True)
1853 self.ui.write((',\n "diffstat": "%s"')
1853 self.ui.write((',\n "diffstat": "%s"')
1854 % j(self.ui.popbuffer()))
1854 % j(self.ui.popbuffer()))
1855 if diff:
1855 if diff:
1856 self.ui.pushbuffer()
1856 self.ui.pushbuffer()
1857 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1857 diffordiffstat(self.ui, self.repo, diffopts, prev, node,
1858 match=matchfn, stat=False)
1858 match=matchfn, stat=False)
1859 self.ui.write((',\n "diff": "%s"') % j(self.ui.popbuffer()))
1859 self.ui.write((',\n "diff": "%s"') % j(self.ui.popbuffer()))
1860
1860
1861 self.ui.write("\n }")
1861 self.ui.write("\n }")
1862
1862
1863 class changeset_templater(changeset_printer):
1863 class changeset_templater(changeset_printer):
1864 '''format changeset information.'''
1864 '''format changeset information.'''
1865
1865
1866 # Arguments before "buffered" used to be positional. Consider not
1866 # Arguments before "buffered" used to be positional. Consider not
1867 # adding/removing arguments before "buffered" to not break callers.
1867 # adding/removing arguments before "buffered" to not break callers.
1868 def __init__(self, ui, repo, tmplspec, matchfn=None, diffopts=None,
1868 def __init__(self, ui, repo, tmplspec, matchfn=None, diffopts=None,
1869 buffered=False):
1869 buffered=False):
1870 diffopts = diffopts or {}
1870 diffopts = diffopts or {}
1871
1871
1872 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1872 changeset_printer.__init__(self, ui, repo, matchfn, diffopts, buffered)
1873 self.t = formatter.loadtemplater(ui, tmplspec,
1873 self.t = formatter.loadtemplater(ui, tmplspec,
1874 cache=templatekw.defaulttempl)
1874 cache=templatekw.defaulttempl)
1875 self._counter = itertools.count()
1875 self._counter = itertools.count()
1876 self.cache = {}
1876 self.cache = {}
1877
1877
1878 self._tref = tmplspec.ref
1878 self._tref = tmplspec.ref
1879 self._parts = {'header': '', 'footer': '',
1879 self._parts = {'header': '', 'footer': '',
1880 tmplspec.ref: tmplspec.ref,
1880 tmplspec.ref: tmplspec.ref,
1881 'docheader': '', 'docfooter': '',
1881 'docheader': '', 'docfooter': '',
1882 'separator': ''}
1882 'separator': ''}
1883 if tmplspec.mapfile:
1883 if tmplspec.mapfile:
1884 # find correct templates for current mode, for backward
1884 # find correct templates for current mode, for backward
1885 # compatibility with 'log -v/-q/--debug' using a mapfile
1885 # compatibility with 'log -v/-q/--debug' using a mapfile
1886 tmplmodes = [
1886 tmplmodes = [
1887 (True, ''),
1887 (True, ''),
1888 (self.ui.verbose, '_verbose'),
1888 (self.ui.verbose, '_verbose'),
1889 (self.ui.quiet, '_quiet'),
1889 (self.ui.quiet, '_quiet'),
1890 (self.ui.debugflag, '_debug'),
1890 (self.ui.debugflag, '_debug'),
1891 ]
1891 ]
1892 for mode, postfix in tmplmodes:
1892 for mode, postfix in tmplmodes:
1893 for t in self._parts:
1893 for t in self._parts:
1894 cur = t + postfix
1894 cur = t + postfix
1895 if mode and cur in self.t:
1895 if mode and cur in self.t:
1896 self._parts[t] = cur
1896 self._parts[t] = cur
1897 else:
1897 else:
1898 partnames = [p for p in self._parts.keys() if p != tmplspec.ref]
1898 partnames = [p for p in self._parts.keys() if p != tmplspec.ref]
1899 m = formatter.templatepartsmap(tmplspec, self.t, partnames)
1899 m = formatter.templatepartsmap(tmplspec, self.t, partnames)
1900 self._parts.update(m)
1900 self._parts.update(m)
1901
1901
1902 if self._parts['docheader']:
1902 if self._parts['docheader']:
1903 self.ui.write(templater.stringify(self.t(self._parts['docheader'])))
1903 self.ui.write(templater.stringify(self.t(self._parts['docheader'])))
1904
1904
1905 def close(self):
1905 def close(self):
1906 if self._parts['docfooter']:
1906 if self._parts['docfooter']:
1907 if not self.footer:
1907 if not self.footer:
1908 self.footer = ""
1908 self.footer = ""
1909 self.footer += templater.stringify(self.t(self._parts['docfooter']))
1909 self.footer += templater.stringify(self.t(self._parts['docfooter']))
1910 return super(changeset_templater, self).close()
1910 return super(changeset_templater, self).close()
1911
1911
1912 def _show(self, ctx, copies, matchfn, props):
1912 def _show(self, ctx, copies, matchfn, props):
1913 '''show a single changeset or file revision'''
1913 '''show a single changeset or file revision'''
1914 props = props.copy()
1914 props = props.copy()
1915 props.update(templatekw.keywords)
1915 props.update(templatekw.keywords)
1916 props['templ'] = self.t
1916 props['templ'] = self.t
1917 props['ctx'] = ctx
1917 props['ctx'] = ctx
1918 props['repo'] = self.repo
1918 props['repo'] = self.repo
1919 props['ui'] = self.repo.ui
1919 props['ui'] = self.repo.ui
1920 props['index'] = index = next(self._counter)
1920 props['index'] = index = next(self._counter)
1921 props['revcache'] = {'copies': copies}
1921 props['revcache'] = {'copies': copies}
1922 props['cache'] = self.cache
1922 props['cache'] = self.cache
1923 props = pycompat.strkwargs(props)
1923 props = pycompat.strkwargs(props)
1924
1924
1925 # write separator, which wouldn't work well with the header part below
1925 # write separator, which wouldn't work well with the header part below
1926 # since there's inherently a conflict between header (across items) and
1926 # since there's inherently a conflict between header (across items) and
1927 # separator (per item)
1927 # separator (per item)
1928 if self._parts['separator'] and index > 0:
1928 if self._parts['separator'] and index > 0:
1929 self.ui.write(templater.stringify(self.t(self._parts['separator'])))
1929 self.ui.write(templater.stringify(self.t(self._parts['separator'])))
1930
1930
1931 # write header
1931 # write header
1932 if self._parts['header']:
1932 if self._parts['header']:
1933 h = templater.stringify(self.t(self._parts['header'], **props))
1933 h = templater.stringify(self.t(self._parts['header'], **props))
1934 if self.buffered:
1934 if self.buffered:
1935 self.header[ctx.rev()] = h
1935 self.header[ctx.rev()] = h
1936 else:
1936 else:
1937 if self.lastheader != h:
1937 if self.lastheader != h:
1938 self.lastheader = h
1938 self.lastheader = h
1939 self.ui.write(h)
1939 self.ui.write(h)
1940
1940
1941 # write changeset metadata, then patch if requested
1941 # write changeset metadata, then patch if requested
1942 key = self._parts[self._tref]
1942 key = self._parts[self._tref]
1943 self.ui.write(templater.stringify(self.t(key, **props)))
1943 self.ui.write(templater.stringify(self.t(key, **props)))
1944 self.showpatch(ctx, matchfn)
1944 self.showpatch(ctx, matchfn)
1945
1945
1946 if self._parts['footer']:
1946 if self._parts['footer']:
1947 if not self.footer:
1947 if not self.footer:
1948 self.footer = templater.stringify(
1948 self.footer = templater.stringify(
1949 self.t(self._parts['footer'], **props))
1949 self.t(self._parts['footer'], **props))
1950
1950
1951 def logtemplatespec(tmpl, mapfile):
1951 def logtemplatespec(tmpl, mapfile):
1952 if mapfile:
1952 if mapfile:
1953 return formatter.templatespec('changeset', tmpl, mapfile)
1953 return formatter.templatespec('changeset', tmpl, mapfile)
1954 else:
1954 else:
1955 return formatter.templatespec('', tmpl, None)
1955 return formatter.templatespec('', tmpl, None)
1956
1956
1957 def _lookuplogtemplate(ui, tmpl, style):
1957 def _lookuplogtemplate(ui, tmpl, style):
1958 """Find the template matching the given template spec or style
1958 """Find the template matching the given template spec or style
1959
1959
1960 See formatter.lookuptemplate() for details.
1960 See formatter.lookuptemplate() for details.
1961 """
1961 """
1962
1962
1963 # ui settings
1963 # ui settings
1964 if not tmpl and not style: # template are stronger than style
1964 if not tmpl and not style: # template are stronger than style
1965 tmpl = ui.config('ui', 'logtemplate')
1965 tmpl = ui.config('ui', 'logtemplate')
1966 if tmpl:
1966 if tmpl:
1967 return logtemplatespec(templater.unquotestring(tmpl), None)
1967 return logtemplatespec(templater.unquotestring(tmpl), None)
1968 else:
1968 else:
1969 style = util.expandpath(ui.config('ui', 'style'))
1969 style = util.expandpath(ui.config('ui', 'style'))
1970
1970
1971 if not tmpl and style:
1971 if not tmpl and style:
1972 mapfile = style
1972 mapfile = style
1973 if not os.path.split(mapfile)[0]:
1973 if not os.path.split(mapfile)[0]:
1974 mapname = (templater.templatepath('map-cmdline.' + mapfile)
1974 mapname = (templater.templatepath('map-cmdline.' + mapfile)
1975 or templater.templatepath(mapfile))
1975 or templater.templatepath(mapfile))
1976 if mapname:
1976 if mapname:
1977 mapfile = mapname
1977 mapfile = mapname
1978 return logtemplatespec(None, mapfile)
1978 return logtemplatespec(None, mapfile)
1979
1979
1980 if not tmpl:
1980 if not tmpl:
1981 return logtemplatespec(None, None)
1981 return logtemplatespec(None, None)
1982
1982
1983 return formatter.lookuptemplate(ui, 'changeset', tmpl)
1983 return formatter.lookuptemplate(ui, 'changeset', tmpl)
1984
1984
1985 def makelogtemplater(ui, repo, tmpl, buffered=False):
1985 def makelogtemplater(ui, repo, tmpl, buffered=False):
1986 """Create a changeset_templater from a literal template 'tmpl'"""
1986 """Create a changeset_templater from a literal template 'tmpl'"""
1987 spec = logtemplatespec(tmpl, None)
1987 spec = logtemplatespec(tmpl, None)
1988 return changeset_templater(ui, repo, spec, buffered=buffered)
1988 return changeset_templater(ui, repo, spec, buffered=buffered)
1989
1989
1990 def show_changeset(ui, repo, opts, buffered=False):
1990 def show_changeset(ui, repo, opts, buffered=False):
1991 """show one changeset using template or regular display.
1991 """show one changeset using template or regular display.
1992
1992
1993 Display format will be the first non-empty hit of:
1993 Display format will be the first non-empty hit of:
1994 1. option 'template'
1994 1. option 'template'
1995 2. option 'style'
1995 2. option 'style'
1996 3. [ui] setting 'logtemplate'
1996 3. [ui] setting 'logtemplate'
1997 4. [ui] setting 'style'
1997 4. [ui] setting 'style'
1998 If all of these values are either the unset or the empty string,
1998 If all of these values are either the unset or the empty string,
1999 regular display via changeset_printer() is done.
1999 regular display via changeset_printer() is done.
2000 """
2000 """
2001 # options
2001 # options
2002 matchfn = None
2002 matchfn = None
2003 if opts.get('patch') or opts.get('stat'):
2003 if opts.get('patch') or opts.get('stat'):
2004 matchfn = scmutil.matchall(repo)
2004 matchfn = scmutil.matchall(repo)
2005
2005
2006 if opts.get('template') == 'json':
2006 if opts.get('template') == 'json':
2007 return jsonchangeset(ui, repo, matchfn, opts, buffered)
2007 return jsonchangeset(ui, repo, matchfn, opts, buffered)
2008
2008
2009 spec = _lookuplogtemplate(ui, opts.get('template'), opts.get('style'))
2009 spec = _lookuplogtemplate(ui, opts.get('template'), opts.get('style'))
2010
2010
2011 if not spec.ref and not spec.tmpl and not spec.mapfile:
2011 if not spec.ref and not spec.tmpl and not spec.mapfile:
2012 return changeset_printer(ui, repo, matchfn, opts, buffered)
2012 return changeset_printer(ui, repo, matchfn, opts, buffered)
2013
2013
2014 return changeset_templater(ui, repo, spec, matchfn, opts, buffered)
2014 return changeset_templater(ui, repo, spec, matchfn, opts, buffered)
2015
2015
2016 def showmarker(fm, marker, index=None):
2016 def showmarker(fm, marker, index=None):
2017 """utility function to display obsolescence marker in a readable way
2017 """utility function to display obsolescence marker in a readable way
2018
2018
2019 To be used by debug function."""
2019 To be used by debug function."""
2020 if index is not None:
2020 if index is not None:
2021 fm.write('index', '%i ', index)
2021 fm.write('index', '%i ', index)
2022 fm.write('precnode', '%s ', hex(marker.prednode()))
2022 fm.write('prednode', '%s ', hex(marker.prednode()))
2023 succs = marker.succnodes()
2023 succs = marker.succnodes()
2024 fm.condwrite(succs, 'succnodes', '%s ',
2024 fm.condwrite(succs, 'succnodes', '%s ',
2025 fm.formatlist(map(hex, succs), name='node'))
2025 fm.formatlist(map(hex, succs), name='node'))
2026 fm.write('flag', '%X ', marker.flags())
2026 fm.write('flag', '%X ', marker.flags())
2027 parents = marker.parentnodes()
2027 parents = marker.parentnodes()
2028 if parents is not None:
2028 if parents is not None:
2029 fm.write('parentnodes', '{%s} ',
2029 fm.write('parentnodes', '{%s} ',
2030 fm.formatlist(map(hex, parents), name='node', sep=', '))
2030 fm.formatlist(map(hex, parents), name='node', sep=', '))
2031 fm.write('date', '(%s) ', fm.formatdate(marker.date()))
2031 fm.write('date', '(%s) ', fm.formatdate(marker.date()))
2032 meta = marker.metadata().copy()
2032 meta = marker.metadata().copy()
2033 meta.pop('date', None)
2033 meta.pop('date', None)
2034 fm.write('metadata', '{%s}', fm.formatdict(meta, fmt='%r: %r', sep=', '))
2034 fm.write('metadata', '{%s}', fm.formatdict(meta, fmt='%r: %r', sep=', '))
2035 fm.plain('\n')
2035 fm.plain('\n')
2036
2036
2037 def finddate(ui, repo, date):
2037 def finddate(ui, repo, date):
2038 """Find the tipmost changeset that matches the given date spec"""
2038 """Find the tipmost changeset that matches the given date spec"""
2039
2039
2040 df = util.matchdate(date)
2040 df = util.matchdate(date)
2041 m = scmutil.matchall(repo)
2041 m = scmutil.matchall(repo)
2042 results = {}
2042 results = {}
2043
2043
2044 def prep(ctx, fns):
2044 def prep(ctx, fns):
2045 d = ctx.date()
2045 d = ctx.date()
2046 if df(d[0]):
2046 if df(d[0]):
2047 results[ctx.rev()] = d
2047 results[ctx.rev()] = d
2048
2048
2049 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
2049 for ctx in walkchangerevs(repo, m, {'rev': None}, prep):
2050 rev = ctx.rev()
2050 rev = ctx.rev()
2051 if rev in results:
2051 if rev in results:
2052 ui.status(_("found revision %s from %s\n") %
2052 ui.status(_("found revision %s from %s\n") %
2053 (rev, util.datestr(results[rev])))
2053 (rev, util.datestr(results[rev])))
2054 return '%d' % rev
2054 return '%d' % rev
2055
2055
2056 raise error.Abort(_("revision matching date not found"))
2056 raise error.Abort(_("revision matching date not found"))
2057
2057
2058 def increasingwindows(windowsize=8, sizelimit=512):
2058 def increasingwindows(windowsize=8, sizelimit=512):
2059 while True:
2059 while True:
2060 yield windowsize
2060 yield windowsize
2061 if windowsize < sizelimit:
2061 if windowsize < sizelimit:
2062 windowsize *= 2
2062 windowsize *= 2
2063
2063
2064 class FileWalkError(Exception):
2064 class FileWalkError(Exception):
2065 pass
2065 pass
2066
2066
2067 def walkfilerevs(repo, match, follow, revs, fncache):
2067 def walkfilerevs(repo, match, follow, revs, fncache):
2068 '''Walks the file history for the matched files.
2068 '''Walks the file history for the matched files.
2069
2069
2070 Returns the changeset revs that are involved in the file history.
2070 Returns the changeset revs that are involved in the file history.
2071
2071
2072 Throws FileWalkError if the file history can't be walked using
2072 Throws FileWalkError if the file history can't be walked using
2073 filelogs alone.
2073 filelogs alone.
2074 '''
2074 '''
2075 wanted = set()
2075 wanted = set()
2076 copies = []
2076 copies = []
2077 minrev, maxrev = min(revs), max(revs)
2077 minrev, maxrev = min(revs), max(revs)
2078 def filerevgen(filelog, last):
2078 def filerevgen(filelog, last):
2079 """
2079 """
2080 Only files, no patterns. Check the history of each file.
2080 Only files, no patterns. Check the history of each file.
2081
2081
2082 Examines filelog entries within minrev, maxrev linkrev range
2082 Examines filelog entries within minrev, maxrev linkrev range
2083 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
2083 Returns an iterator yielding (linkrev, parentlinkrevs, copied)
2084 tuples in backwards order
2084 tuples in backwards order
2085 """
2085 """
2086 cl_count = len(repo)
2086 cl_count = len(repo)
2087 revs = []
2087 revs = []
2088 for j in xrange(0, last + 1):
2088 for j in xrange(0, last + 1):
2089 linkrev = filelog.linkrev(j)
2089 linkrev = filelog.linkrev(j)
2090 if linkrev < minrev:
2090 if linkrev < minrev:
2091 continue
2091 continue
2092 # only yield rev for which we have the changelog, it can
2092 # only yield rev for which we have the changelog, it can
2093 # happen while doing "hg log" during a pull or commit
2093 # happen while doing "hg log" during a pull or commit
2094 if linkrev >= cl_count:
2094 if linkrev >= cl_count:
2095 break
2095 break
2096
2096
2097 parentlinkrevs = []
2097 parentlinkrevs = []
2098 for p in filelog.parentrevs(j):
2098 for p in filelog.parentrevs(j):
2099 if p != nullrev:
2099 if p != nullrev:
2100 parentlinkrevs.append(filelog.linkrev(p))
2100 parentlinkrevs.append(filelog.linkrev(p))
2101 n = filelog.node(j)
2101 n = filelog.node(j)
2102 revs.append((linkrev, parentlinkrevs,
2102 revs.append((linkrev, parentlinkrevs,
2103 follow and filelog.renamed(n)))
2103 follow and filelog.renamed(n)))
2104
2104
2105 return reversed(revs)
2105 return reversed(revs)
2106 def iterfiles():
2106 def iterfiles():
2107 pctx = repo['.']
2107 pctx = repo['.']
2108 for filename in match.files():
2108 for filename in match.files():
2109 if follow:
2109 if follow:
2110 if filename not in pctx:
2110 if filename not in pctx:
2111 raise error.Abort(_('cannot follow file not in parent '
2111 raise error.Abort(_('cannot follow file not in parent '
2112 'revision: "%s"') % filename)
2112 'revision: "%s"') % filename)
2113 yield filename, pctx[filename].filenode()
2113 yield filename, pctx[filename].filenode()
2114 else:
2114 else:
2115 yield filename, None
2115 yield filename, None
2116 for filename_node in copies:
2116 for filename_node in copies:
2117 yield filename_node
2117 yield filename_node
2118
2118
2119 for file_, node in iterfiles():
2119 for file_, node in iterfiles():
2120 filelog = repo.file(file_)
2120 filelog = repo.file(file_)
2121 if not len(filelog):
2121 if not len(filelog):
2122 if node is None:
2122 if node is None:
2123 # A zero count may be a directory or deleted file, so
2123 # A zero count may be a directory or deleted file, so
2124 # try to find matching entries on the slow path.
2124 # try to find matching entries on the slow path.
2125 if follow:
2125 if follow:
2126 raise error.Abort(
2126 raise error.Abort(
2127 _('cannot follow nonexistent file: "%s"') % file_)
2127 _('cannot follow nonexistent file: "%s"') % file_)
2128 raise FileWalkError("Cannot walk via filelog")
2128 raise FileWalkError("Cannot walk via filelog")
2129 else:
2129 else:
2130 continue
2130 continue
2131
2131
2132 if node is None:
2132 if node is None:
2133 last = len(filelog) - 1
2133 last = len(filelog) - 1
2134 else:
2134 else:
2135 last = filelog.rev(node)
2135 last = filelog.rev(node)
2136
2136
2137 # keep track of all ancestors of the file
2137 # keep track of all ancestors of the file
2138 ancestors = {filelog.linkrev(last)}
2138 ancestors = {filelog.linkrev(last)}
2139
2139
2140 # iterate from latest to oldest revision
2140 # iterate from latest to oldest revision
2141 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
2141 for rev, flparentlinkrevs, copied in filerevgen(filelog, last):
2142 if not follow:
2142 if not follow:
2143 if rev > maxrev:
2143 if rev > maxrev:
2144 continue
2144 continue
2145 else:
2145 else:
2146 # Note that last might not be the first interesting
2146 # Note that last might not be the first interesting
2147 # rev to us:
2147 # rev to us:
2148 # if the file has been changed after maxrev, we'll
2148 # if the file has been changed after maxrev, we'll
2149 # have linkrev(last) > maxrev, and we still need
2149 # have linkrev(last) > maxrev, and we still need
2150 # to explore the file graph
2150 # to explore the file graph
2151 if rev not in ancestors:
2151 if rev not in ancestors:
2152 continue
2152 continue
2153 # XXX insert 1327 fix here
2153 # XXX insert 1327 fix here
2154 if flparentlinkrevs:
2154 if flparentlinkrevs:
2155 ancestors.update(flparentlinkrevs)
2155 ancestors.update(flparentlinkrevs)
2156
2156
2157 fncache.setdefault(rev, []).append(file_)
2157 fncache.setdefault(rev, []).append(file_)
2158 wanted.add(rev)
2158 wanted.add(rev)
2159 if copied:
2159 if copied:
2160 copies.append(copied)
2160 copies.append(copied)
2161
2161
2162 return wanted
2162 return wanted
2163
2163
2164 class _followfilter(object):
2164 class _followfilter(object):
2165 def __init__(self, repo, onlyfirst=False):
2165 def __init__(self, repo, onlyfirst=False):
2166 self.repo = repo
2166 self.repo = repo
2167 self.startrev = nullrev
2167 self.startrev = nullrev
2168 self.roots = set()
2168 self.roots = set()
2169 self.onlyfirst = onlyfirst
2169 self.onlyfirst = onlyfirst
2170
2170
2171 def match(self, rev):
2171 def match(self, rev):
2172 def realparents(rev):
2172 def realparents(rev):
2173 if self.onlyfirst:
2173 if self.onlyfirst:
2174 return self.repo.changelog.parentrevs(rev)[0:1]
2174 return self.repo.changelog.parentrevs(rev)[0:1]
2175 else:
2175 else:
2176 return filter(lambda x: x != nullrev,
2176 return filter(lambda x: x != nullrev,
2177 self.repo.changelog.parentrevs(rev))
2177 self.repo.changelog.parentrevs(rev))
2178
2178
2179 if self.startrev == nullrev:
2179 if self.startrev == nullrev:
2180 self.startrev = rev
2180 self.startrev = rev
2181 return True
2181 return True
2182
2182
2183 if rev > self.startrev:
2183 if rev > self.startrev:
2184 # forward: all descendants
2184 # forward: all descendants
2185 if not self.roots:
2185 if not self.roots:
2186 self.roots.add(self.startrev)
2186 self.roots.add(self.startrev)
2187 for parent in realparents(rev):
2187 for parent in realparents(rev):
2188 if parent in self.roots:
2188 if parent in self.roots:
2189 self.roots.add(rev)
2189 self.roots.add(rev)
2190 return True
2190 return True
2191 else:
2191 else:
2192 # backwards: all parents
2192 # backwards: all parents
2193 if not self.roots:
2193 if not self.roots:
2194 self.roots.update(realparents(self.startrev))
2194 self.roots.update(realparents(self.startrev))
2195 if rev in self.roots:
2195 if rev in self.roots:
2196 self.roots.remove(rev)
2196 self.roots.remove(rev)
2197 self.roots.update(realparents(rev))
2197 self.roots.update(realparents(rev))
2198 return True
2198 return True
2199
2199
2200 return False
2200 return False
2201
2201
2202 def walkchangerevs(repo, match, opts, prepare):
2202 def walkchangerevs(repo, match, opts, prepare):
2203 '''Iterate over files and the revs in which they changed.
2203 '''Iterate over files and the revs in which they changed.
2204
2204
2205 Callers most commonly need to iterate backwards over the history
2205 Callers most commonly need to iterate backwards over the history
2206 in which they are interested. Doing so has awful (quadratic-looking)
2206 in which they are interested. Doing so has awful (quadratic-looking)
2207 performance, so we use iterators in a "windowed" way.
2207 performance, so we use iterators in a "windowed" way.
2208
2208
2209 We walk a window of revisions in the desired order. Within the
2209 We walk a window of revisions in the desired order. Within the
2210 window, we first walk forwards to gather data, then in the desired
2210 window, we first walk forwards to gather data, then in the desired
2211 order (usually backwards) to display it.
2211 order (usually backwards) to display it.
2212
2212
2213 This function returns an iterator yielding contexts. Before
2213 This function returns an iterator yielding contexts. Before
2214 yielding each context, the iterator will first call the prepare
2214 yielding each context, the iterator will first call the prepare
2215 function on each context in the window in forward order.'''
2215 function on each context in the window in forward order.'''
2216
2216
2217 follow = opts.get('follow') or opts.get('follow_first')
2217 follow = opts.get('follow') or opts.get('follow_first')
2218 revs = _logrevs(repo, opts)
2218 revs = _logrevs(repo, opts)
2219 if not revs:
2219 if not revs:
2220 return []
2220 return []
2221 wanted = set()
2221 wanted = set()
2222 slowpath = match.anypats() or ((match.isexact() or match.prefix()) and
2222 slowpath = match.anypats() or ((match.isexact() or match.prefix()) and
2223 opts.get('removed'))
2223 opts.get('removed'))
2224 fncache = {}
2224 fncache = {}
2225 change = repo.changectx
2225 change = repo.changectx
2226
2226
2227 # First step is to fill wanted, the set of revisions that we want to yield.
2227 # First step is to fill wanted, the set of revisions that we want to yield.
2228 # When it does not induce extra cost, we also fill fncache for revisions in
2228 # When it does not induce extra cost, we also fill fncache for revisions in
2229 # wanted: a cache of filenames that were changed (ctx.files()) and that
2229 # wanted: a cache of filenames that were changed (ctx.files()) and that
2230 # match the file filtering conditions.
2230 # match the file filtering conditions.
2231
2231
2232 if match.always():
2232 if match.always():
2233 # No files, no patterns. Display all revs.
2233 # No files, no patterns. Display all revs.
2234 wanted = revs
2234 wanted = revs
2235 elif not slowpath:
2235 elif not slowpath:
2236 # We only have to read through the filelog to find wanted revisions
2236 # We only have to read through the filelog to find wanted revisions
2237
2237
2238 try:
2238 try:
2239 wanted = walkfilerevs(repo, match, follow, revs, fncache)
2239 wanted = walkfilerevs(repo, match, follow, revs, fncache)
2240 except FileWalkError:
2240 except FileWalkError:
2241 slowpath = True
2241 slowpath = True
2242
2242
2243 # We decided to fall back to the slowpath because at least one
2243 # We decided to fall back to the slowpath because at least one
2244 # of the paths was not a file. Check to see if at least one of them
2244 # of the paths was not a file. Check to see if at least one of them
2245 # existed in history, otherwise simply return
2245 # existed in history, otherwise simply return
2246 for path in match.files():
2246 for path in match.files():
2247 if path == '.' or path in repo.store:
2247 if path == '.' or path in repo.store:
2248 break
2248 break
2249 else:
2249 else:
2250 return []
2250 return []
2251
2251
2252 if slowpath:
2252 if slowpath:
2253 # We have to read the changelog to match filenames against
2253 # We have to read the changelog to match filenames against
2254 # changed files
2254 # changed files
2255
2255
2256 if follow:
2256 if follow:
2257 raise error.Abort(_('can only follow copies/renames for explicit '
2257 raise error.Abort(_('can only follow copies/renames for explicit '
2258 'filenames'))
2258 'filenames'))
2259
2259
2260 # The slow path checks files modified in every changeset.
2260 # The slow path checks files modified in every changeset.
2261 # This is really slow on large repos, so compute the set lazily.
2261 # This is really slow on large repos, so compute the set lazily.
2262 class lazywantedset(object):
2262 class lazywantedset(object):
2263 def __init__(self):
2263 def __init__(self):
2264 self.set = set()
2264 self.set = set()
2265 self.revs = set(revs)
2265 self.revs = set(revs)
2266
2266
2267 # No need to worry about locality here because it will be accessed
2267 # No need to worry about locality here because it will be accessed
2268 # in the same order as the increasing window below.
2268 # in the same order as the increasing window below.
2269 def __contains__(self, value):
2269 def __contains__(self, value):
2270 if value in self.set:
2270 if value in self.set:
2271 return True
2271 return True
2272 elif not value in self.revs:
2272 elif not value in self.revs:
2273 return False
2273 return False
2274 else:
2274 else:
2275 self.revs.discard(value)
2275 self.revs.discard(value)
2276 ctx = change(value)
2276 ctx = change(value)
2277 matches = filter(match, ctx.files())
2277 matches = filter(match, ctx.files())
2278 if matches:
2278 if matches:
2279 fncache[value] = matches
2279 fncache[value] = matches
2280 self.set.add(value)
2280 self.set.add(value)
2281 return True
2281 return True
2282 return False
2282 return False
2283
2283
2284 def discard(self, value):
2284 def discard(self, value):
2285 self.revs.discard(value)
2285 self.revs.discard(value)
2286 self.set.discard(value)
2286 self.set.discard(value)
2287
2287
2288 wanted = lazywantedset()
2288 wanted = lazywantedset()
2289
2289
2290 # it might be worthwhile to do this in the iterator if the rev range
2290 # it might be worthwhile to do this in the iterator if the rev range
2291 # is descending and the prune args are all within that range
2291 # is descending and the prune args are all within that range
2292 for rev in opts.get('prune', ()):
2292 for rev in opts.get('prune', ()):
2293 rev = repo[rev].rev()
2293 rev = repo[rev].rev()
2294 ff = _followfilter(repo)
2294 ff = _followfilter(repo)
2295 stop = min(revs[0], revs[-1])
2295 stop = min(revs[0], revs[-1])
2296 for x in xrange(rev, stop - 1, -1):
2296 for x in xrange(rev, stop - 1, -1):
2297 if ff.match(x):
2297 if ff.match(x):
2298 wanted = wanted - [x]
2298 wanted = wanted - [x]
2299
2299
2300 # Now that wanted is correctly initialized, we can iterate over the
2300 # Now that wanted is correctly initialized, we can iterate over the
2301 # revision range, yielding only revisions in wanted.
2301 # revision range, yielding only revisions in wanted.
2302 def iterate():
2302 def iterate():
2303 if follow and match.always():
2303 if follow and match.always():
2304 ff = _followfilter(repo, onlyfirst=opts.get('follow_first'))
2304 ff = _followfilter(repo, onlyfirst=opts.get('follow_first'))
2305 def want(rev):
2305 def want(rev):
2306 return ff.match(rev) and rev in wanted
2306 return ff.match(rev) and rev in wanted
2307 else:
2307 else:
2308 def want(rev):
2308 def want(rev):
2309 return rev in wanted
2309 return rev in wanted
2310
2310
2311 it = iter(revs)
2311 it = iter(revs)
2312 stopiteration = False
2312 stopiteration = False
2313 for windowsize in increasingwindows():
2313 for windowsize in increasingwindows():
2314 nrevs = []
2314 nrevs = []
2315 for i in xrange(windowsize):
2315 for i in xrange(windowsize):
2316 rev = next(it, None)
2316 rev = next(it, None)
2317 if rev is None:
2317 if rev is None:
2318 stopiteration = True
2318 stopiteration = True
2319 break
2319 break
2320 elif want(rev):
2320 elif want(rev):
2321 nrevs.append(rev)
2321 nrevs.append(rev)
2322 for rev in sorted(nrevs):
2322 for rev in sorted(nrevs):
2323 fns = fncache.get(rev)
2323 fns = fncache.get(rev)
2324 ctx = change(rev)
2324 ctx = change(rev)
2325 if not fns:
2325 if not fns:
2326 def fns_generator():
2326 def fns_generator():
2327 for f in ctx.files():
2327 for f in ctx.files():
2328 if match(f):
2328 if match(f):
2329 yield f
2329 yield f
2330 fns = fns_generator()
2330 fns = fns_generator()
2331 prepare(ctx, fns)
2331 prepare(ctx, fns)
2332 for rev in nrevs:
2332 for rev in nrevs:
2333 yield change(rev)
2333 yield change(rev)
2334
2334
2335 if stopiteration:
2335 if stopiteration:
2336 break
2336 break
2337
2337
2338 return iterate()
2338 return iterate()
2339
2339
2340 def _makefollowlogfilematcher(repo, files, followfirst):
2340 def _makefollowlogfilematcher(repo, files, followfirst):
2341 # When displaying a revision with --patch --follow FILE, we have
2341 # When displaying a revision with --patch --follow FILE, we have
2342 # to know which file of the revision must be diffed. With
2342 # to know which file of the revision must be diffed. With
2343 # --follow, we want the names of the ancestors of FILE in the
2343 # --follow, we want the names of the ancestors of FILE in the
2344 # revision, stored in "fcache". "fcache" is populated by
2344 # revision, stored in "fcache". "fcache" is populated by
2345 # reproducing the graph traversal already done by --follow revset
2345 # reproducing the graph traversal already done by --follow revset
2346 # and relating revs to file names (which is not "correct" but
2346 # and relating revs to file names (which is not "correct" but
2347 # good enough).
2347 # good enough).
2348 fcache = {}
2348 fcache = {}
2349 fcacheready = [False]
2349 fcacheready = [False]
2350 pctx = repo['.']
2350 pctx = repo['.']
2351
2351
2352 def populate():
2352 def populate():
2353 for fn in files:
2353 for fn in files:
2354 fctx = pctx[fn]
2354 fctx = pctx[fn]
2355 fcache.setdefault(fctx.introrev(), set()).add(fctx.path())
2355 fcache.setdefault(fctx.introrev(), set()).add(fctx.path())
2356 for c in fctx.ancestors(followfirst=followfirst):
2356 for c in fctx.ancestors(followfirst=followfirst):
2357 fcache.setdefault(c.rev(), set()).add(c.path())
2357 fcache.setdefault(c.rev(), set()).add(c.path())
2358
2358
2359 def filematcher(rev):
2359 def filematcher(rev):
2360 if not fcacheready[0]:
2360 if not fcacheready[0]:
2361 # Lazy initialization
2361 # Lazy initialization
2362 fcacheready[0] = True
2362 fcacheready[0] = True
2363 populate()
2363 populate()
2364 return scmutil.matchfiles(repo, fcache.get(rev, []))
2364 return scmutil.matchfiles(repo, fcache.get(rev, []))
2365
2365
2366 return filematcher
2366 return filematcher
2367
2367
2368 def _makenofollowlogfilematcher(repo, pats, opts):
2368 def _makenofollowlogfilematcher(repo, pats, opts):
2369 '''hook for extensions to override the filematcher for non-follow cases'''
2369 '''hook for extensions to override the filematcher for non-follow cases'''
2370 return None
2370 return None
2371
2371
2372 def _makelogrevset(repo, pats, opts, revs):
2372 def _makelogrevset(repo, pats, opts, revs):
2373 """Return (expr, filematcher) where expr is a revset string built
2373 """Return (expr, filematcher) where expr is a revset string built
2374 from log options and file patterns or None. If --stat or --patch
2374 from log options and file patterns or None. If --stat or --patch
2375 are not passed filematcher is None. Otherwise it is a callable
2375 are not passed filematcher is None. Otherwise it is a callable
2376 taking a revision number and returning a match objects filtering
2376 taking a revision number and returning a match objects filtering
2377 the files to be detailed when displaying the revision.
2377 the files to be detailed when displaying the revision.
2378 """
2378 """
2379 opt2revset = {
2379 opt2revset = {
2380 'no_merges': ('not merge()', None),
2380 'no_merges': ('not merge()', None),
2381 'only_merges': ('merge()', None),
2381 'only_merges': ('merge()', None),
2382 '_ancestors': ('ancestors(%(val)s)', None),
2382 '_ancestors': ('ancestors(%(val)s)', None),
2383 '_fancestors': ('_firstancestors(%(val)s)', None),
2383 '_fancestors': ('_firstancestors(%(val)s)', None),
2384 '_descendants': ('descendants(%(val)s)', None),
2384 '_descendants': ('descendants(%(val)s)', None),
2385 '_fdescendants': ('_firstdescendants(%(val)s)', None),
2385 '_fdescendants': ('_firstdescendants(%(val)s)', None),
2386 '_matchfiles': ('_matchfiles(%(val)s)', None),
2386 '_matchfiles': ('_matchfiles(%(val)s)', None),
2387 'date': ('date(%(val)r)', None),
2387 'date': ('date(%(val)r)', None),
2388 'branch': ('branch(%(val)r)', ' or '),
2388 'branch': ('branch(%(val)r)', ' or '),
2389 '_patslog': ('filelog(%(val)r)', ' or '),
2389 '_patslog': ('filelog(%(val)r)', ' or '),
2390 '_patsfollow': ('follow(%(val)r)', ' or '),
2390 '_patsfollow': ('follow(%(val)r)', ' or '),
2391 '_patsfollowfirst': ('_followfirst(%(val)r)', ' or '),
2391 '_patsfollowfirst': ('_followfirst(%(val)r)', ' or '),
2392 'keyword': ('keyword(%(val)r)', ' or '),
2392 'keyword': ('keyword(%(val)r)', ' or '),
2393 'prune': ('not (%(val)r or ancestors(%(val)r))', ' and '),
2393 'prune': ('not (%(val)r or ancestors(%(val)r))', ' and '),
2394 'user': ('user(%(val)r)', ' or '),
2394 'user': ('user(%(val)r)', ' or '),
2395 }
2395 }
2396
2396
2397 opts = dict(opts)
2397 opts = dict(opts)
2398 # follow or not follow?
2398 # follow or not follow?
2399 follow = opts.get('follow') or opts.get('follow_first')
2399 follow = opts.get('follow') or opts.get('follow_first')
2400 if opts.get('follow_first'):
2400 if opts.get('follow_first'):
2401 followfirst = 1
2401 followfirst = 1
2402 else:
2402 else:
2403 followfirst = 0
2403 followfirst = 0
2404 # --follow with FILE behavior depends on revs...
2404 # --follow with FILE behavior depends on revs...
2405 it = iter(revs)
2405 it = iter(revs)
2406 startrev = next(it)
2406 startrev = next(it)
2407 followdescendants = startrev < next(it, startrev)
2407 followdescendants = startrev < next(it, startrev)
2408
2408
2409 # branch and only_branch are really aliases and must be handled at
2409 # branch and only_branch are really aliases and must be handled at
2410 # the same time
2410 # the same time
2411 opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
2411 opts['branch'] = opts.get('branch', []) + opts.get('only_branch', [])
2412 opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']]
2412 opts['branch'] = [repo.lookupbranch(b) for b in opts['branch']]
2413 # pats/include/exclude are passed to match.match() directly in
2413 # pats/include/exclude are passed to match.match() directly in
2414 # _matchfiles() revset but walkchangerevs() builds its matcher with
2414 # _matchfiles() revset but walkchangerevs() builds its matcher with
2415 # scmutil.match(). The difference is input pats are globbed on
2415 # scmutil.match(). The difference is input pats are globbed on
2416 # platforms without shell expansion (windows).
2416 # platforms without shell expansion (windows).
2417 wctx = repo[None]
2417 wctx = repo[None]
2418 match, pats = scmutil.matchandpats(wctx, pats, opts)
2418 match, pats = scmutil.matchandpats(wctx, pats, opts)
2419 slowpath = match.anypats() or ((match.isexact() or match.prefix()) and
2419 slowpath = match.anypats() or ((match.isexact() or match.prefix()) and
2420 opts.get('removed'))
2420 opts.get('removed'))
2421 if not slowpath:
2421 if not slowpath:
2422 for f in match.files():
2422 for f in match.files():
2423 if follow and f not in wctx:
2423 if follow and f not in wctx:
2424 # If the file exists, it may be a directory, so let it
2424 # If the file exists, it may be a directory, so let it
2425 # take the slow path.
2425 # take the slow path.
2426 if os.path.exists(repo.wjoin(f)):
2426 if os.path.exists(repo.wjoin(f)):
2427 slowpath = True
2427 slowpath = True
2428 continue
2428 continue
2429 else:
2429 else:
2430 raise error.Abort(_('cannot follow file not in parent '
2430 raise error.Abort(_('cannot follow file not in parent '
2431 'revision: "%s"') % f)
2431 'revision: "%s"') % f)
2432 filelog = repo.file(f)
2432 filelog = repo.file(f)
2433 if not filelog:
2433 if not filelog:
2434 # A zero count may be a directory or deleted file, so
2434 # A zero count may be a directory or deleted file, so
2435 # try to find matching entries on the slow path.
2435 # try to find matching entries on the slow path.
2436 if follow:
2436 if follow:
2437 raise error.Abort(
2437 raise error.Abort(
2438 _('cannot follow nonexistent file: "%s"') % f)
2438 _('cannot follow nonexistent file: "%s"') % f)
2439 slowpath = True
2439 slowpath = True
2440
2440
2441 # We decided to fall back to the slowpath because at least one
2441 # We decided to fall back to the slowpath because at least one
2442 # of the paths was not a file. Check to see if at least one of them
2442 # of the paths was not a file. Check to see if at least one of them
2443 # existed in history - in that case, we'll continue down the
2443 # existed in history - in that case, we'll continue down the
2444 # slowpath; otherwise, we can turn off the slowpath
2444 # slowpath; otherwise, we can turn off the slowpath
2445 if slowpath:
2445 if slowpath:
2446 for path in match.files():
2446 for path in match.files():
2447 if path == '.' or path in repo.store:
2447 if path == '.' or path in repo.store:
2448 break
2448 break
2449 else:
2449 else:
2450 slowpath = False
2450 slowpath = False
2451
2451
2452 fpats = ('_patsfollow', '_patsfollowfirst')
2452 fpats = ('_patsfollow', '_patsfollowfirst')
2453 fnopats = (('_ancestors', '_fancestors'),
2453 fnopats = (('_ancestors', '_fancestors'),
2454 ('_descendants', '_fdescendants'))
2454 ('_descendants', '_fdescendants'))
2455 if slowpath:
2455 if slowpath:
2456 # See walkchangerevs() slow path.
2456 # See walkchangerevs() slow path.
2457 #
2457 #
2458 # pats/include/exclude cannot be represented as separate
2458 # pats/include/exclude cannot be represented as separate
2459 # revset expressions as their filtering logic applies at file
2459 # revset expressions as their filtering logic applies at file
2460 # level. For instance "-I a -X a" matches a revision touching
2460 # level. For instance "-I a -X a" matches a revision touching
2461 # "a" and "b" while "file(a) and not file(b)" does
2461 # "a" and "b" while "file(a) and not file(b)" does
2462 # not. Besides, filesets are evaluated against the working
2462 # not. Besides, filesets are evaluated against the working
2463 # directory.
2463 # directory.
2464 matchargs = ['r:', 'd:relpath']
2464 matchargs = ['r:', 'd:relpath']
2465 for p in pats:
2465 for p in pats:
2466 matchargs.append('p:' + p)
2466 matchargs.append('p:' + p)
2467 for p in opts.get('include', []):
2467 for p in opts.get('include', []):
2468 matchargs.append('i:' + p)
2468 matchargs.append('i:' + p)
2469 for p in opts.get('exclude', []):
2469 for p in opts.get('exclude', []):
2470 matchargs.append('x:' + p)
2470 matchargs.append('x:' + p)
2471 matchargs = ','.join(('%r' % p) for p in matchargs)
2471 matchargs = ','.join(('%r' % p) for p in matchargs)
2472 opts['_matchfiles'] = matchargs
2472 opts['_matchfiles'] = matchargs
2473 if follow:
2473 if follow:
2474 opts[fnopats[0][followfirst]] = '.'
2474 opts[fnopats[0][followfirst]] = '.'
2475 else:
2475 else:
2476 if follow:
2476 if follow:
2477 if pats:
2477 if pats:
2478 # follow() revset interprets its file argument as a
2478 # follow() revset interprets its file argument as a
2479 # manifest entry, so use match.files(), not pats.
2479 # manifest entry, so use match.files(), not pats.
2480 opts[fpats[followfirst]] = list(match.files())
2480 opts[fpats[followfirst]] = list(match.files())
2481 else:
2481 else:
2482 op = fnopats[followdescendants][followfirst]
2482 op = fnopats[followdescendants][followfirst]
2483 opts[op] = 'rev(%d)' % startrev
2483 opts[op] = 'rev(%d)' % startrev
2484 else:
2484 else:
2485 opts['_patslog'] = list(pats)
2485 opts['_patslog'] = list(pats)
2486
2486
2487 filematcher = None
2487 filematcher = None
2488 if opts.get('patch') or opts.get('stat'):
2488 if opts.get('patch') or opts.get('stat'):
2489 # When following files, track renames via a special matcher.
2489 # When following files, track renames via a special matcher.
2490 # If we're forced to take the slowpath it means we're following
2490 # If we're forced to take the slowpath it means we're following
2491 # at least one pattern/directory, so don't bother with rename tracking.
2491 # at least one pattern/directory, so don't bother with rename tracking.
2492 if follow and not match.always() and not slowpath:
2492 if follow and not match.always() and not slowpath:
2493 # _makefollowlogfilematcher expects its files argument to be
2493 # _makefollowlogfilematcher expects its files argument to be
2494 # relative to the repo root, so use match.files(), not pats.
2494 # relative to the repo root, so use match.files(), not pats.
2495 filematcher = _makefollowlogfilematcher(repo, match.files(),
2495 filematcher = _makefollowlogfilematcher(repo, match.files(),
2496 followfirst)
2496 followfirst)
2497 else:
2497 else:
2498 filematcher = _makenofollowlogfilematcher(repo, pats, opts)
2498 filematcher = _makenofollowlogfilematcher(repo, pats, opts)
2499 if filematcher is None:
2499 if filematcher is None:
2500 filematcher = lambda rev: match
2500 filematcher = lambda rev: match
2501
2501
2502 expr = []
2502 expr = []
2503 for op, val in sorted(opts.iteritems()):
2503 for op, val in sorted(opts.iteritems()):
2504 if not val:
2504 if not val:
2505 continue
2505 continue
2506 if op not in opt2revset:
2506 if op not in opt2revset:
2507 continue
2507 continue
2508 revop, andor = opt2revset[op]
2508 revop, andor = opt2revset[op]
2509 if '%(val)' not in revop:
2509 if '%(val)' not in revop:
2510 expr.append(revop)
2510 expr.append(revop)
2511 else:
2511 else:
2512 if not isinstance(val, list):
2512 if not isinstance(val, list):
2513 e = revop % {'val': val}
2513 e = revop % {'val': val}
2514 else:
2514 else:
2515 e = '(' + andor.join((revop % {'val': v}) for v in val) + ')'
2515 e = '(' + andor.join((revop % {'val': v}) for v in val) + ')'
2516 expr.append(e)
2516 expr.append(e)
2517
2517
2518 if expr:
2518 if expr:
2519 expr = '(' + ' and '.join(expr) + ')'
2519 expr = '(' + ' and '.join(expr) + ')'
2520 else:
2520 else:
2521 expr = None
2521 expr = None
2522 return expr, filematcher
2522 return expr, filematcher
2523
2523
2524 def _logrevs(repo, opts):
2524 def _logrevs(repo, opts):
2525 # Default --rev value depends on --follow but --follow behavior
2525 # Default --rev value depends on --follow but --follow behavior
2526 # depends on revisions resolved from --rev...
2526 # depends on revisions resolved from --rev...
2527 follow = opts.get('follow') or opts.get('follow_first')
2527 follow = opts.get('follow') or opts.get('follow_first')
2528 if opts.get('rev'):
2528 if opts.get('rev'):
2529 revs = scmutil.revrange(repo, opts['rev'])
2529 revs = scmutil.revrange(repo, opts['rev'])
2530 elif follow and repo.dirstate.p1() == nullid:
2530 elif follow and repo.dirstate.p1() == nullid:
2531 revs = smartset.baseset()
2531 revs = smartset.baseset()
2532 elif follow:
2532 elif follow:
2533 revs = repo.revs('reverse(:.)')
2533 revs = repo.revs('reverse(:.)')
2534 else:
2534 else:
2535 revs = smartset.spanset(repo)
2535 revs = smartset.spanset(repo)
2536 revs.reverse()
2536 revs.reverse()
2537 return revs
2537 return revs
2538
2538
2539 def getgraphlogrevs(repo, pats, opts):
2539 def getgraphlogrevs(repo, pats, opts):
2540 """Return (revs, expr, filematcher) where revs is an iterable of
2540 """Return (revs, expr, filematcher) where revs is an iterable of
2541 revision numbers, expr is a revset string built from log options
2541 revision numbers, expr is a revset string built from log options
2542 and file patterns or None, and used to filter 'revs'. If --stat or
2542 and file patterns or None, and used to filter 'revs'. If --stat or
2543 --patch are not passed filematcher is None. Otherwise it is a
2543 --patch are not passed filematcher is None. Otherwise it is a
2544 callable taking a revision number and returning a match objects
2544 callable taking a revision number and returning a match objects
2545 filtering the files to be detailed when displaying the revision.
2545 filtering the files to be detailed when displaying the revision.
2546 """
2546 """
2547 limit = loglimit(opts)
2547 limit = loglimit(opts)
2548 revs = _logrevs(repo, opts)
2548 revs = _logrevs(repo, opts)
2549 if not revs:
2549 if not revs:
2550 return smartset.baseset(), None, None
2550 return smartset.baseset(), None, None
2551 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
2551 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
2552 if opts.get('rev'):
2552 if opts.get('rev'):
2553 # User-specified revs might be unsorted, but don't sort before
2553 # User-specified revs might be unsorted, but don't sort before
2554 # _makelogrevset because it might depend on the order of revs
2554 # _makelogrevset because it might depend on the order of revs
2555 if not (revs.isdescending() or revs.istopo()):
2555 if not (revs.isdescending() or revs.istopo()):
2556 revs.sort(reverse=True)
2556 revs.sort(reverse=True)
2557 if expr:
2557 if expr:
2558 matcher = revset.match(repo.ui, expr, order=revset.followorder)
2558 matcher = revset.match(repo.ui, expr, order=revset.followorder)
2559 revs = matcher(repo, revs)
2559 revs = matcher(repo, revs)
2560 if limit is not None:
2560 if limit is not None:
2561 limitedrevs = []
2561 limitedrevs = []
2562 for idx, rev in enumerate(revs):
2562 for idx, rev in enumerate(revs):
2563 if idx >= limit:
2563 if idx >= limit:
2564 break
2564 break
2565 limitedrevs.append(rev)
2565 limitedrevs.append(rev)
2566 revs = smartset.baseset(limitedrevs)
2566 revs = smartset.baseset(limitedrevs)
2567
2567
2568 return revs, expr, filematcher
2568 return revs, expr, filematcher
2569
2569
2570 def getlogrevs(repo, pats, opts):
2570 def getlogrevs(repo, pats, opts):
2571 """Return (revs, expr, filematcher) where revs is an iterable of
2571 """Return (revs, expr, filematcher) where revs is an iterable of
2572 revision numbers, expr is a revset string built from log options
2572 revision numbers, expr is a revset string built from log options
2573 and file patterns or None, and used to filter 'revs'. If --stat or
2573 and file patterns or None, and used to filter 'revs'. If --stat or
2574 --patch are not passed filematcher is None. Otherwise it is a
2574 --patch are not passed filematcher is None. Otherwise it is a
2575 callable taking a revision number and returning a match objects
2575 callable taking a revision number and returning a match objects
2576 filtering the files to be detailed when displaying the revision.
2576 filtering the files to be detailed when displaying the revision.
2577 """
2577 """
2578 limit = loglimit(opts)
2578 limit = loglimit(opts)
2579 revs = _logrevs(repo, opts)
2579 revs = _logrevs(repo, opts)
2580 if not revs:
2580 if not revs:
2581 return smartset.baseset([]), None, None
2581 return smartset.baseset([]), None, None
2582 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
2582 expr, filematcher = _makelogrevset(repo, pats, opts, revs)
2583 if expr:
2583 if expr:
2584 matcher = revset.match(repo.ui, expr, order=revset.followorder)
2584 matcher = revset.match(repo.ui, expr, order=revset.followorder)
2585 revs = matcher(repo, revs)
2585 revs = matcher(repo, revs)
2586 if limit is not None:
2586 if limit is not None:
2587 limitedrevs = []
2587 limitedrevs = []
2588 for idx, r in enumerate(revs):
2588 for idx, r in enumerate(revs):
2589 if limit <= idx:
2589 if limit <= idx:
2590 break
2590 break
2591 limitedrevs.append(r)
2591 limitedrevs.append(r)
2592 revs = smartset.baseset(limitedrevs)
2592 revs = smartset.baseset(limitedrevs)
2593
2593
2594 return revs, expr, filematcher
2594 return revs, expr, filematcher
2595
2595
2596 def _graphnodeformatter(ui, displayer):
2596 def _graphnodeformatter(ui, displayer):
2597 spec = ui.config('ui', 'graphnodetemplate')
2597 spec = ui.config('ui', 'graphnodetemplate')
2598 if not spec:
2598 if not spec:
2599 return templatekw.showgraphnode # fast path for "{graphnode}"
2599 return templatekw.showgraphnode # fast path for "{graphnode}"
2600
2600
2601 spec = templater.unquotestring(spec)
2601 spec = templater.unquotestring(spec)
2602 templ = formatter.maketemplater(ui, spec)
2602 templ = formatter.maketemplater(ui, spec)
2603 cache = {}
2603 cache = {}
2604 if isinstance(displayer, changeset_templater):
2604 if isinstance(displayer, changeset_templater):
2605 cache = displayer.cache # reuse cache of slow templates
2605 cache = displayer.cache # reuse cache of slow templates
2606 props = templatekw.keywords.copy()
2606 props = templatekw.keywords.copy()
2607 props['templ'] = templ
2607 props['templ'] = templ
2608 props['cache'] = cache
2608 props['cache'] = cache
2609 def formatnode(repo, ctx):
2609 def formatnode(repo, ctx):
2610 props['ctx'] = ctx
2610 props['ctx'] = ctx
2611 props['repo'] = repo
2611 props['repo'] = repo
2612 props['ui'] = repo.ui
2612 props['ui'] = repo.ui
2613 props['revcache'] = {}
2613 props['revcache'] = {}
2614 return templ.render(props)
2614 return templ.render(props)
2615 return formatnode
2615 return formatnode
2616
2616
2617 def displaygraph(ui, repo, dag, displayer, edgefn, getrenamed=None,
2617 def displaygraph(ui, repo, dag, displayer, edgefn, getrenamed=None,
2618 filematcher=None):
2618 filematcher=None):
2619 formatnode = _graphnodeformatter(ui, displayer)
2619 formatnode = _graphnodeformatter(ui, displayer)
2620 state = graphmod.asciistate()
2620 state = graphmod.asciistate()
2621 styles = state['styles']
2621 styles = state['styles']
2622
2622
2623 # only set graph styling if HGPLAIN is not set.
2623 # only set graph styling if HGPLAIN is not set.
2624 if ui.plain('graph'):
2624 if ui.plain('graph'):
2625 # set all edge styles to |, the default pre-3.8 behaviour
2625 # set all edge styles to |, the default pre-3.8 behaviour
2626 styles.update(dict.fromkeys(styles, '|'))
2626 styles.update(dict.fromkeys(styles, '|'))
2627 else:
2627 else:
2628 edgetypes = {
2628 edgetypes = {
2629 'parent': graphmod.PARENT,
2629 'parent': graphmod.PARENT,
2630 'grandparent': graphmod.GRANDPARENT,
2630 'grandparent': graphmod.GRANDPARENT,
2631 'missing': graphmod.MISSINGPARENT
2631 'missing': graphmod.MISSINGPARENT
2632 }
2632 }
2633 for name, key in edgetypes.items():
2633 for name, key in edgetypes.items():
2634 # experimental config: experimental.graphstyle.*
2634 # experimental config: experimental.graphstyle.*
2635 styles[key] = ui.config('experimental', 'graphstyle.%s' % name,
2635 styles[key] = ui.config('experimental', 'graphstyle.%s' % name,
2636 styles[key])
2636 styles[key])
2637 if not styles[key]:
2637 if not styles[key]:
2638 styles[key] = None
2638 styles[key] = None
2639
2639
2640 # experimental config: experimental.graphshorten
2640 # experimental config: experimental.graphshorten
2641 state['graphshorten'] = ui.configbool('experimental', 'graphshorten')
2641 state['graphshorten'] = ui.configbool('experimental', 'graphshorten')
2642
2642
2643 for rev, type, ctx, parents in dag:
2643 for rev, type, ctx, parents in dag:
2644 char = formatnode(repo, ctx)
2644 char = formatnode(repo, ctx)
2645 copies = None
2645 copies = None
2646 if getrenamed and ctx.rev():
2646 if getrenamed and ctx.rev():
2647 copies = []
2647 copies = []
2648 for fn in ctx.files():
2648 for fn in ctx.files():
2649 rename = getrenamed(fn, ctx.rev())
2649 rename = getrenamed(fn, ctx.rev())
2650 if rename:
2650 if rename:
2651 copies.append((fn, rename[0]))
2651 copies.append((fn, rename[0]))
2652 revmatchfn = None
2652 revmatchfn = None
2653 if filematcher is not None:
2653 if filematcher is not None:
2654 revmatchfn = filematcher(ctx.rev())
2654 revmatchfn = filematcher(ctx.rev())
2655 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
2655 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
2656 lines = displayer.hunk.pop(rev).split('\n')
2656 lines = displayer.hunk.pop(rev).split('\n')
2657 if not lines[-1]:
2657 if not lines[-1]:
2658 del lines[-1]
2658 del lines[-1]
2659 displayer.flush(ctx)
2659 displayer.flush(ctx)
2660 edges = edgefn(type, char, lines, state, rev, parents)
2660 edges = edgefn(type, char, lines, state, rev, parents)
2661 for type, char, lines, coldata in edges:
2661 for type, char, lines, coldata in edges:
2662 graphmod.ascii(ui, state, type, char, lines, coldata)
2662 graphmod.ascii(ui, state, type, char, lines, coldata)
2663 displayer.close()
2663 displayer.close()
2664
2664
2665 def graphlog(ui, repo, pats, opts):
2665 def graphlog(ui, repo, pats, opts):
2666 # Parameters are identical to log command ones
2666 # Parameters are identical to log command ones
2667 revs, expr, filematcher = getgraphlogrevs(repo, pats, opts)
2667 revs, expr, filematcher = getgraphlogrevs(repo, pats, opts)
2668 revdag = graphmod.dagwalker(repo, revs)
2668 revdag = graphmod.dagwalker(repo, revs)
2669
2669
2670 getrenamed = None
2670 getrenamed = None
2671 if opts.get('copies'):
2671 if opts.get('copies'):
2672 endrev = None
2672 endrev = None
2673 if opts.get('rev'):
2673 if opts.get('rev'):
2674 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
2674 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
2675 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2675 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
2676
2676
2677 ui.pager('log')
2677 ui.pager('log')
2678 displayer = show_changeset(ui, repo, opts, buffered=True)
2678 displayer = show_changeset(ui, repo, opts, buffered=True)
2679 displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges, getrenamed,
2679 displaygraph(ui, repo, revdag, displayer, graphmod.asciiedges, getrenamed,
2680 filematcher)
2680 filematcher)
2681
2681
2682 def checkunsupportedgraphflags(pats, opts):
2682 def checkunsupportedgraphflags(pats, opts):
2683 for op in ["newest_first"]:
2683 for op in ["newest_first"]:
2684 if op in opts and opts[op]:
2684 if op in opts and opts[op]:
2685 raise error.Abort(_("-G/--graph option is incompatible with --%s")
2685 raise error.Abort(_("-G/--graph option is incompatible with --%s")
2686 % op.replace("_", "-"))
2686 % op.replace("_", "-"))
2687
2687
2688 def graphrevs(repo, nodes, opts):
2688 def graphrevs(repo, nodes, opts):
2689 limit = loglimit(opts)
2689 limit = loglimit(opts)
2690 nodes.reverse()
2690 nodes.reverse()
2691 if limit is not None:
2691 if limit is not None:
2692 nodes = nodes[:limit]
2692 nodes = nodes[:limit]
2693 return graphmod.nodes(repo, nodes)
2693 return graphmod.nodes(repo, nodes)
2694
2694
2695 def add(ui, repo, match, prefix, explicitonly, **opts):
2695 def add(ui, repo, match, prefix, explicitonly, **opts):
2696 join = lambda f: os.path.join(prefix, f)
2696 join = lambda f: os.path.join(prefix, f)
2697 bad = []
2697 bad = []
2698
2698
2699 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2699 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2700 names = []
2700 names = []
2701 wctx = repo[None]
2701 wctx = repo[None]
2702 cca = None
2702 cca = None
2703 abort, warn = scmutil.checkportabilityalert(ui)
2703 abort, warn = scmutil.checkportabilityalert(ui)
2704 if abort or warn:
2704 if abort or warn:
2705 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
2705 cca = scmutil.casecollisionauditor(ui, abort, repo.dirstate)
2706
2706
2707 badmatch = matchmod.badmatch(match, badfn)
2707 badmatch = matchmod.badmatch(match, badfn)
2708 dirstate = repo.dirstate
2708 dirstate = repo.dirstate
2709 # We don't want to just call wctx.walk here, since it would return a lot of
2709 # We don't want to just call wctx.walk here, since it would return a lot of
2710 # clean files, which we aren't interested in and takes time.
2710 # clean files, which we aren't interested in and takes time.
2711 for f in sorted(dirstate.walk(badmatch, sorted(wctx.substate),
2711 for f in sorted(dirstate.walk(badmatch, sorted(wctx.substate),
2712 True, False, full=False)):
2712 True, False, full=False)):
2713 exact = match.exact(f)
2713 exact = match.exact(f)
2714 if exact or not explicitonly and f not in wctx and repo.wvfs.lexists(f):
2714 if exact or not explicitonly and f not in wctx and repo.wvfs.lexists(f):
2715 if cca:
2715 if cca:
2716 cca(f)
2716 cca(f)
2717 names.append(f)
2717 names.append(f)
2718 if ui.verbose or not exact:
2718 if ui.verbose or not exact:
2719 ui.status(_('adding %s\n') % match.rel(f))
2719 ui.status(_('adding %s\n') % match.rel(f))
2720
2720
2721 for subpath in sorted(wctx.substate):
2721 for subpath in sorted(wctx.substate):
2722 sub = wctx.sub(subpath)
2722 sub = wctx.sub(subpath)
2723 try:
2723 try:
2724 submatch = matchmod.subdirmatcher(subpath, match)
2724 submatch = matchmod.subdirmatcher(subpath, match)
2725 if opts.get(r'subrepos'):
2725 if opts.get(r'subrepos'):
2726 bad.extend(sub.add(ui, submatch, prefix, False, **opts))
2726 bad.extend(sub.add(ui, submatch, prefix, False, **opts))
2727 else:
2727 else:
2728 bad.extend(sub.add(ui, submatch, prefix, True, **opts))
2728 bad.extend(sub.add(ui, submatch, prefix, True, **opts))
2729 except error.LookupError:
2729 except error.LookupError:
2730 ui.status(_("skipping missing subrepository: %s\n")
2730 ui.status(_("skipping missing subrepository: %s\n")
2731 % join(subpath))
2731 % join(subpath))
2732
2732
2733 if not opts.get(r'dry_run'):
2733 if not opts.get(r'dry_run'):
2734 rejected = wctx.add(names, prefix)
2734 rejected = wctx.add(names, prefix)
2735 bad.extend(f for f in rejected if f in match.files())
2735 bad.extend(f for f in rejected if f in match.files())
2736 return bad
2736 return bad
2737
2737
2738 def addwebdirpath(repo, serverpath, webconf):
2738 def addwebdirpath(repo, serverpath, webconf):
2739 webconf[serverpath] = repo.root
2739 webconf[serverpath] = repo.root
2740 repo.ui.debug('adding %s = %s\n' % (serverpath, repo.root))
2740 repo.ui.debug('adding %s = %s\n' % (serverpath, repo.root))
2741
2741
2742 for r in repo.revs('filelog("path:.hgsub")'):
2742 for r in repo.revs('filelog("path:.hgsub")'):
2743 ctx = repo[r]
2743 ctx = repo[r]
2744 for subpath in ctx.substate:
2744 for subpath in ctx.substate:
2745 ctx.sub(subpath).addwebdirpath(serverpath, webconf)
2745 ctx.sub(subpath).addwebdirpath(serverpath, webconf)
2746
2746
2747 def forget(ui, repo, match, prefix, explicitonly):
2747 def forget(ui, repo, match, prefix, explicitonly):
2748 join = lambda f: os.path.join(prefix, f)
2748 join = lambda f: os.path.join(prefix, f)
2749 bad = []
2749 bad = []
2750 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2750 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
2751 wctx = repo[None]
2751 wctx = repo[None]
2752 forgot = []
2752 forgot = []
2753
2753
2754 s = repo.status(match=matchmod.badmatch(match, badfn), clean=True)
2754 s = repo.status(match=matchmod.badmatch(match, badfn), clean=True)
2755 forget = sorted(s.modified + s.added + s.deleted + s.clean)
2755 forget = sorted(s.modified + s.added + s.deleted + s.clean)
2756 if explicitonly:
2756 if explicitonly:
2757 forget = [f for f in forget if match.exact(f)]
2757 forget = [f for f in forget if match.exact(f)]
2758
2758
2759 for subpath in sorted(wctx.substate):
2759 for subpath in sorted(wctx.substate):
2760 sub = wctx.sub(subpath)
2760 sub = wctx.sub(subpath)
2761 try:
2761 try:
2762 submatch = matchmod.subdirmatcher(subpath, match)
2762 submatch = matchmod.subdirmatcher(subpath, match)
2763 subbad, subforgot = sub.forget(submatch, prefix)
2763 subbad, subforgot = sub.forget(submatch, prefix)
2764 bad.extend([subpath + '/' + f for f in subbad])
2764 bad.extend([subpath + '/' + f for f in subbad])
2765 forgot.extend([subpath + '/' + f for f in subforgot])
2765 forgot.extend([subpath + '/' + f for f in subforgot])
2766 except error.LookupError:
2766 except error.LookupError:
2767 ui.status(_("skipping missing subrepository: %s\n")
2767 ui.status(_("skipping missing subrepository: %s\n")
2768 % join(subpath))
2768 % join(subpath))
2769
2769
2770 if not explicitonly:
2770 if not explicitonly:
2771 for f in match.files():
2771 for f in match.files():
2772 if f not in repo.dirstate and not repo.wvfs.isdir(f):
2772 if f not in repo.dirstate and not repo.wvfs.isdir(f):
2773 if f not in forgot:
2773 if f not in forgot:
2774 if repo.wvfs.exists(f):
2774 if repo.wvfs.exists(f):
2775 # Don't complain if the exact case match wasn't given.
2775 # Don't complain if the exact case match wasn't given.
2776 # But don't do this until after checking 'forgot', so
2776 # But don't do this until after checking 'forgot', so
2777 # that subrepo files aren't normalized, and this op is
2777 # that subrepo files aren't normalized, and this op is
2778 # purely from data cached by the status walk above.
2778 # purely from data cached by the status walk above.
2779 if repo.dirstate.normalize(f) in repo.dirstate:
2779 if repo.dirstate.normalize(f) in repo.dirstate:
2780 continue
2780 continue
2781 ui.warn(_('not removing %s: '
2781 ui.warn(_('not removing %s: '
2782 'file is already untracked\n')
2782 'file is already untracked\n')
2783 % match.rel(f))
2783 % match.rel(f))
2784 bad.append(f)
2784 bad.append(f)
2785
2785
2786 for f in forget:
2786 for f in forget:
2787 if ui.verbose or not match.exact(f):
2787 if ui.verbose or not match.exact(f):
2788 ui.status(_('removing %s\n') % match.rel(f))
2788 ui.status(_('removing %s\n') % match.rel(f))
2789
2789
2790 rejected = wctx.forget(forget, prefix)
2790 rejected = wctx.forget(forget, prefix)
2791 bad.extend(f for f in rejected if f in match.files())
2791 bad.extend(f for f in rejected if f in match.files())
2792 forgot.extend(f for f in forget if f not in rejected)
2792 forgot.extend(f for f in forget if f not in rejected)
2793 return bad, forgot
2793 return bad, forgot
2794
2794
2795 def files(ui, ctx, m, fm, fmt, subrepos):
2795 def files(ui, ctx, m, fm, fmt, subrepos):
2796 rev = ctx.rev()
2796 rev = ctx.rev()
2797 ret = 1
2797 ret = 1
2798 ds = ctx.repo().dirstate
2798 ds = ctx.repo().dirstate
2799
2799
2800 for f in ctx.matches(m):
2800 for f in ctx.matches(m):
2801 if rev is None and ds[f] == 'r':
2801 if rev is None and ds[f] == 'r':
2802 continue
2802 continue
2803 fm.startitem()
2803 fm.startitem()
2804 if ui.verbose:
2804 if ui.verbose:
2805 fc = ctx[f]
2805 fc = ctx[f]
2806 fm.write('size flags', '% 10d % 1s ', fc.size(), fc.flags())
2806 fm.write('size flags', '% 10d % 1s ', fc.size(), fc.flags())
2807 fm.data(abspath=f)
2807 fm.data(abspath=f)
2808 fm.write('path', fmt, m.rel(f))
2808 fm.write('path', fmt, m.rel(f))
2809 ret = 0
2809 ret = 0
2810
2810
2811 for subpath in sorted(ctx.substate):
2811 for subpath in sorted(ctx.substate):
2812 submatch = matchmod.subdirmatcher(subpath, m)
2812 submatch = matchmod.subdirmatcher(subpath, m)
2813 if (subrepos or m.exact(subpath) or any(submatch.files())):
2813 if (subrepos or m.exact(subpath) or any(submatch.files())):
2814 sub = ctx.sub(subpath)
2814 sub = ctx.sub(subpath)
2815 try:
2815 try:
2816 recurse = m.exact(subpath) or subrepos
2816 recurse = m.exact(subpath) or subrepos
2817 if sub.printfiles(ui, submatch, fm, fmt, recurse) == 0:
2817 if sub.printfiles(ui, submatch, fm, fmt, recurse) == 0:
2818 ret = 0
2818 ret = 0
2819 except error.LookupError:
2819 except error.LookupError:
2820 ui.status(_("skipping missing subrepository: %s\n")
2820 ui.status(_("skipping missing subrepository: %s\n")
2821 % m.abs(subpath))
2821 % m.abs(subpath))
2822
2822
2823 return ret
2823 return ret
2824
2824
2825 def remove(ui, repo, m, prefix, after, force, subrepos, warnings=None):
2825 def remove(ui, repo, m, prefix, after, force, subrepos, warnings=None):
2826 join = lambda f: os.path.join(prefix, f)
2826 join = lambda f: os.path.join(prefix, f)
2827 ret = 0
2827 ret = 0
2828 s = repo.status(match=m, clean=True)
2828 s = repo.status(match=m, clean=True)
2829 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2829 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2830
2830
2831 wctx = repo[None]
2831 wctx = repo[None]
2832
2832
2833 if warnings is None:
2833 if warnings is None:
2834 warnings = []
2834 warnings = []
2835 warn = True
2835 warn = True
2836 else:
2836 else:
2837 warn = False
2837 warn = False
2838
2838
2839 subs = sorted(wctx.substate)
2839 subs = sorted(wctx.substate)
2840 total = len(subs)
2840 total = len(subs)
2841 count = 0
2841 count = 0
2842 for subpath in subs:
2842 for subpath in subs:
2843 count += 1
2843 count += 1
2844 submatch = matchmod.subdirmatcher(subpath, m)
2844 submatch = matchmod.subdirmatcher(subpath, m)
2845 if subrepos or m.exact(subpath) or any(submatch.files()):
2845 if subrepos or m.exact(subpath) or any(submatch.files()):
2846 ui.progress(_('searching'), count, total=total, unit=_('subrepos'))
2846 ui.progress(_('searching'), count, total=total, unit=_('subrepos'))
2847 sub = wctx.sub(subpath)
2847 sub = wctx.sub(subpath)
2848 try:
2848 try:
2849 if sub.removefiles(submatch, prefix, after, force, subrepos,
2849 if sub.removefiles(submatch, prefix, after, force, subrepos,
2850 warnings):
2850 warnings):
2851 ret = 1
2851 ret = 1
2852 except error.LookupError:
2852 except error.LookupError:
2853 warnings.append(_("skipping missing subrepository: %s\n")
2853 warnings.append(_("skipping missing subrepository: %s\n")
2854 % join(subpath))
2854 % join(subpath))
2855 ui.progress(_('searching'), None)
2855 ui.progress(_('searching'), None)
2856
2856
2857 # warn about failure to delete explicit files/dirs
2857 # warn about failure to delete explicit files/dirs
2858 deleteddirs = util.dirs(deleted)
2858 deleteddirs = util.dirs(deleted)
2859 files = m.files()
2859 files = m.files()
2860 total = len(files)
2860 total = len(files)
2861 count = 0
2861 count = 0
2862 for f in files:
2862 for f in files:
2863 def insubrepo():
2863 def insubrepo():
2864 for subpath in wctx.substate:
2864 for subpath in wctx.substate:
2865 if f.startswith(subpath + '/'):
2865 if f.startswith(subpath + '/'):
2866 return True
2866 return True
2867 return False
2867 return False
2868
2868
2869 count += 1
2869 count += 1
2870 ui.progress(_('deleting'), count, total=total, unit=_('files'))
2870 ui.progress(_('deleting'), count, total=total, unit=_('files'))
2871 isdir = f in deleteddirs or wctx.hasdir(f)
2871 isdir = f in deleteddirs or wctx.hasdir(f)
2872 if (f in repo.dirstate or isdir or f == '.'
2872 if (f in repo.dirstate or isdir or f == '.'
2873 or insubrepo() or f in subs):
2873 or insubrepo() or f in subs):
2874 continue
2874 continue
2875
2875
2876 if repo.wvfs.exists(f):
2876 if repo.wvfs.exists(f):
2877 if repo.wvfs.isdir(f):
2877 if repo.wvfs.isdir(f):
2878 warnings.append(_('not removing %s: no tracked files\n')
2878 warnings.append(_('not removing %s: no tracked files\n')
2879 % m.rel(f))
2879 % m.rel(f))
2880 else:
2880 else:
2881 warnings.append(_('not removing %s: file is untracked\n')
2881 warnings.append(_('not removing %s: file is untracked\n')
2882 % m.rel(f))
2882 % m.rel(f))
2883 # missing files will generate a warning elsewhere
2883 # missing files will generate a warning elsewhere
2884 ret = 1
2884 ret = 1
2885 ui.progress(_('deleting'), None)
2885 ui.progress(_('deleting'), None)
2886
2886
2887 if force:
2887 if force:
2888 list = modified + deleted + clean + added
2888 list = modified + deleted + clean + added
2889 elif after:
2889 elif after:
2890 list = deleted
2890 list = deleted
2891 remaining = modified + added + clean
2891 remaining = modified + added + clean
2892 total = len(remaining)
2892 total = len(remaining)
2893 count = 0
2893 count = 0
2894 for f in remaining:
2894 for f in remaining:
2895 count += 1
2895 count += 1
2896 ui.progress(_('skipping'), count, total=total, unit=_('files'))
2896 ui.progress(_('skipping'), count, total=total, unit=_('files'))
2897 warnings.append(_('not removing %s: file still exists\n')
2897 warnings.append(_('not removing %s: file still exists\n')
2898 % m.rel(f))
2898 % m.rel(f))
2899 ret = 1
2899 ret = 1
2900 ui.progress(_('skipping'), None)
2900 ui.progress(_('skipping'), None)
2901 else:
2901 else:
2902 list = deleted + clean
2902 list = deleted + clean
2903 total = len(modified) + len(added)
2903 total = len(modified) + len(added)
2904 count = 0
2904 count = 0
2905 for f in modified:
2905 for f in modified:
2906 count += 1
2906 count += 1
2907 ui.progress(_('skipping'), count, total=total, unit=_('files'))
2907 ui.progress(_('skipping'), count, total=total, unit=_('files'))
2908 warnings.append(_('not removing %s: file is modified (use -f'
2908 warnings.append(_('not removing %s: file is modified (use -f'
2909 ' to force removal)\n') % m.rel(f))
2909 ' to force removal)\n') % m.rel(f))
2910 ret = 1
2910 ret = 1
2911 for f in added:
2911 for f in added:
2912 count += 1
2912 count += 1
2913 ui.progress(_('skipping'), count, total=total, unit=_('files'))
2913 ui.progress(_('skipping'), count, total=total, unit=_('files'))
2914 warnings.append(_("not removing %s: file has been marked for add"
2914 warnings.append(_("not removing %s: file has been marked for add"
2915 " (use 'hg forget' to undo add)\n") % m.rel(f))
2915 " (use 'hg forget' to undo add)\n") % m.rel(f))
2916 ret = 1
2916 ret = 1
2917 ui.progress(_('skipping'), None)
2917 ui.progress(_('skipping'), None)
2918
2918
2919 list = sorted(list)
2919 list = sorted(list)
2920 total = len(list)
2920 total = len(list)
2921 count = 0
2921 count = 0
2922 for f in list:
2922 for f in list:
2923 count += 1
2923 count += 1
2924 if ui.verbose or not m.exact(f):
2924 if ui.verbose or not m.exact(f):
2925 ui.progress(_('deleting'), count, total=total, unit=_('files'))
2925 ui.progress(_('deleting'), count, total=total, unit=_('files'))
2926 ui.status(_('removing %s\n') % m.rel(f))
2926 ui.status(_('removing %s\n') % m.rel(f))
2927 ui.progress(_('deleting'), None)
2927 ui.progress(_('deleting'), None)
2928
2928
2929 with repo.wlock():
2929 with repo.wlock():
2930 if not after:
2930 if not after:
2931 for f in list:
2931 for f in list:
2932 if f in added:
2932 if f in added:
2933 continue # we never unlink added files on remove
2933 continue # we never unlink added files on remove
2934 repo.wvfs.unlinkpath(f, ignoremissing=True)
2934 repo.wvfs.unlinkpath(f, ignoremissing=True)
2935 repo[None].forget(list)
2935 repo[None].forget(list)
2936
2936
2937 if warn:
2937 if warn:
2938 for warning in warnings:
2938 for warning in warnings:
2939 ui.warn(warning)
2939 ui.warn(warning)
2940
2940
2941 return ret
2941 return ret
2942
2942
2943 def cat(ui, repo, ctx, matcher, basefm, fntemplate, prefix, **opts):
2943 def cat(ui, repo, ctx, matcher, basefm, fntemplate, prefix, **opts):
2944 err = 1
2944 err = 1
2945
2945
2946 def write(path):
2946 def write(path):
2947 filename = None
2947 filename = None
2948 if fntemplate:
2948 if fntemplate:
2949 filename = makefilename(repo, fntemplate, ctx.node(),
2949 filename = makefilename(repo, fntemplate, ctx.node(),
2950 pathname=os.path.join(prefix, path))
2950 pathname=os.path.join(prefix, path))
2951 with formatter.maybereopen(basefm, filename, opts) as fm:
2951 with formatter.maybereopen(basefm, filename, opts) as fm:
2952 data = ctx[path].data()
2952 data = ctx[path].data()
2953 if opts.get('decode'):
2953 if opts.get('decode'):
2954 data = repo.wwritedata(path, data)
2954 data = repo.wwritedata(path, data)
2955 fm.startitem()
2955 fm.startitem()
2956 fm.write('data', '%s', data)
2956 fm.write('data', '%s', data)
2957 fm.data(abspath=path, path=matcher.rel(path))
2957 fm.data(abspath=path, path=matcher.rel(path))
2958
2958
2959 # Automation often uses hg cat on single files, so special case it
2959 # Automation often uses hg cat on single files, so special case it
2960 # for performance to avoid the cost of parsing the manifest.
2960 # for performance to avoid the cost of parsing the manifest.
2961 if len(matcher.files()) == 1 and not matcher.anypats():
2961 if len(matcher.files()) == 1 and not matcher.anypats():
2962 file = matcher.files()[0]
2962 file = matcher.files()[0]
2963 mfl = repo.manifestlog
2963 mfl = repo.manifestlog
2964 mfnode = ctx.manifestnode()
2964 mfnode = ctx.manifestnode()
2965 try:
2965 try:
2966 if mfnode and mfl[mfnode].find(file)[0]:
2966 if mfnode and mfl[mfnode].find(file)[0]:
2967 write(file)
2967 write(file)
2968 return 0
2968 return 0
2969 except KeyError:
2969 except KeyError:
2970 pass
2970 pass
2971
2971
2972 for abs in ctx.walk(matcher):
2972 for abs in ctx.walk(matcher):
2973 write(abs)
2973 write(abs)
2974 err = 0
2974 err = 0
2975
2975
2976 for subpath in sorted(ctx.substate):
2976 for subpath in sorted(ctx.substate):
2977 sub = ctx.sub(subpath)
2977 sub = ctx.sub(subpath)
2978 try:
2978 try:
2979 submatch = matchmod.subdirmatcher(subpath, matcher)
2979 submatch = matchmod.subdirmatcher(subpath, matcher)
2980
2980
2981 if not sub.cat(submatch, basefm, fntemplate,
2981 if not sub.cat(submatch, basefm, fntemplate,
2982 os.path.join(prefix, sub._path), **opts):
2982 os.path.join(prefix, sub._path), **opts):
2983 err = 0
2983 err = 0
2984 except error.RepoLookupError:
2984 except error.RepoLookupError:
2985 ui.status(_("skipping missing subrepository: %s\n")
2985 ui.status(_("skipping missing subrepository: %s\n")
2986 % os.path.join(prefix, subpath))
2986 % os.path.join(prefix, subpath))
2987
2987
2988 return err
2988 return err
2989
2989
2990 def commit(ui, repo, commitfunc, pats, opts):
2990 def commit(ui, repo, commitfunc, pats, opts):
2991 '''commit the specified files or all outstanding changes'''
2991 '''commit the specified files or all outstanding changes'''
2992 date = opts.get('date')
2992 date = opts.get('date')
2993 if date:
2993 if date:
2994 opts['date'] = util.parsedate(date)
2994 opts['date'] = util.parsedate(date)
2995 message = logmessage(ui, opts)
2995 message = logmessage(ui, opts)
2996 matcher = scmutil.match(repo[None], pats, opts)
2996 matcher = scmutil.match(repo[None], pats, opts)
2997
2997
2998 dsguard = None
2998 dsguard = None
2999 # extract addremove carefully -- this function can be called from a command
2999 # extract addremove carefully -- this function can be called from a command
3000 # that doesn't support addremove
3000 # that doesn't support addremove
3001 if opts.get('addremove'):
3001 if opts.get('addremove'):
3002 dsguard = dirstateguard.dirstateguard(repo, 'commit')
3002 dsguard = dirstateguard.dirstateguard(repo, 'commit')
3003 with dsguard or util.nullcontextmanager():
3003 with dsguard or util.nullcontextmanager():
3004 if dsguard:
3004 if dsguard:
3005 if scmutil.addremove(repo, matcher, "", opts) != 0:
3005 if scmutil.addremove(repo, matcher, "", opts) != 0:
3006 raise error.Abort(
3006 raise error.Abort(
3007 _("failed to mark all new/missing files as added/removed"))
3007 _("failed to mark all new/missing files as added/removed"))
3008
3008
3009 return commitfunc(ui, repo, message, matcher, opts)
3009 return commitfunc(ui, repo, message, matcher, opts)
3010
3010
3011 def samefile(f, ctx1, ctx2):
3011 def samefile(f, ctx1, ctx2):
3012 if f in ctx1.manifest():
3012 if f in ctx1.manifest():
3013 a = ctx1.filectx(f)
3013 a = ctx1.filectx(f)
3014 if f in ctx2.manifest():
3014 if f in ctx2.manifest():
3015 b = ctx2.filectx(f)
3015 b = ctx2.filectx(f)
3016 return (not a.cmp(b)
3016 return (not a.cmp(b)
3017 and a.flags() == b.flags())
3017 and a.flags() == b.flags())
3018 else:
3018 else:
3019 return False
3019 return False
3020 else:
3020 else:
3021 return f not in ctx2.manifest()
3021 return f not in ctx2.manifest()
3022
3022
3023 def amend(ui, repo, commitfunc, old, extra, pats, opts):
3023 def amend(ui, repo, commitfunc, old, extra, pats, opts):
3024 # avoid cycle context -> subrepo -> cmdutil
3024 # avoid cycle context -> subrepo -> cmdutil
3025 from . import context
3025 from . import context
3026
3026
3027 # amend will reuse the existing user if not specified, but the obsolete
3027 # amend will reuse the existing user if not specified, but the obsolete
3028 # marker creation requires that the current user's name is specified.
3028 # marker creation requires that the current user's name is specified.
3029 if obsolete.isenabled(repo, obsolete.createmarkersopt):
3029 if obsolete.isenabled(repo, obsolete.createmarkersopt):
3030 ui.username() # raise exception if username not set
3030 ui.username() # raise exception if username not set
3031
3031
3032 ui.note(_('amending changeset %s\n') % old)
3032 ui.note(_('amending changeset %s\n') % old)
3033 base = old.p1()
3033 base = old.p1()
3034
3034
3035 newid = None
3035 newid = None
3036 with repo.wlock(), repo.lock(), repo.transaction('amend'):
3036 with repo.wlock(), repo.lock(), repo.transaction('amend'):
3037 # See if we got a message from -m or -l, if not, open the editor
3037 # See if we got a message from -m or -l, if not, open the editor
3038 # with the message of the changeset to amend
3038 # with the message of the changeset to amend
3039 message = logmessage(ui, opts)
3039 message = logmessage(ui, opts)
3040 # ensure logfile does not conflict with later enforcement of the
3040 # ensure logfile does not conflict with later enforcement of the
3041 # message. potential logfile content has been processed by
3041 # message. potential logfile content has been processed by
3042 # `logmessage` anyway.
3042 # `logmessage` anyway.
3043 opts.pop('logfile')
3043 opts.pop('logfile')
3044 # First, do a regular commit to record all changes in the working
3044 # First, do a regular commit to record all changes in the working
3045 # directory (if there are any)
3045 # directory (if there are any)
3046 ui.callhooks = False
3046 ui.callhooks = False
3047 activebookmark = repo._bookmarks.active
3047 activebookmark = repo._bookmarks.active
3048 try:
3048 try:
3049 repo._bookmarks.active = None
3049 repo._bookmarks.active = None
3050 opts['message'] = 'temporary amend commit for %s' % old
3050 opts['message'] = 'temporary amend commit for %s' % old
3051 node = commit(ui, repo, commitfunc, pats, opts)
3051 node = commit(ui, repo, commitfunc, pats, opts)
3052 finally:
3052 finally:
3053 repo._bookmarks.active = activebookmark
3053 repo._bookmarks.active = activebookmark
3054 ui.callhooks = True
3054 ui.callhooks = True
3055 ctx = repo[node]
3055 ctx = repo[node]
3056
3056
3057 # Participating changesets:
3057 # Participating changesets:
3058 #
3058 #
3059 # node/ctx o - new (intermediate) commit that contains changes
3059 # node/ctx o - new (intermediate) commit that contains changes
3060 # | from working dir to go into amending commit
3060 # | from working dir to go into amending commit
3061 # | (or a workingctx if there were no changes)
3061 # | (or a workingctx if there were no changes)
3062 # |
3062 # |
3063 # old o - changeset to amend
3063 # old o - changeset to amend
3064 # |
3064 # |
3065 # base o - parent of amending changeset
3065 # base o - parent of amending changeset
3066
3066
3067 # Update extra dict from amended commit (e.g. to preserve graft
3067 # Update extra dict from amended commit (e.g. to preserve graft
3068 # source)
3068 # source)
3069 extra.update(old.extra())
3069 extra.update(old.extra())
3070
3070
3071 # Also update it from the intermediate commit or from the wctx
3071 # Also update it from the intermediate commit or from the wctx
3072 extra.update(ctx.extra())
3072 extra.update(ctx.extra())
3073
3073
3074 if len(old.parents()) > 1:
3074 if len(old.parents()) > 1:
3075 # ctx.files() isn't reliable for merges, so fall back to the
3075 # ctx.files() isn't reliable for merges, so fall back to the
3076 # slower repo.status() method
3076 # slower repo.status() method
3077 files = set([fn for st in repo.status(base, old)[:3]
3077 files = set([fn for st in repo.status(base, old)[:3]
3078 for fn in st])
3078 for fn in st])
3079 else:
3079 else:
3080 files = set(old.files())
3080 files = set(old.files())
3081
3081
3082 # Second, we use either the commit we just did, or if there were no
3082 # Second, we use either the commit we just did, or if there were no
3083 # changes the parent of the working directory as the version of the
3083 # changes the parent of the working directory as the version of the
3084 # files in the final amend commit
3084 # files in the final amend commit
3085 if node:
3085 if node:
3086 ui.note(_('copying changeset %s to %s\n') % (ctx, base))
3086 ui.note(_('copying changeset %s to %s\n') % (ctx, base))
3087
3087
3088 user = ctx.user()
3088 user = ctx.user()
3089 date = ctx.date()
3089 date = ctx.date()
3090 # Recompute copies (avoid recording a -> b -> a)
3090 # Recompute copies (avoid recording a -> b -> a)
3091 copied = copies.pathcopies(base, ctx)
3091 copied = copies.pathcopies(base, ctx)
3092 if old.p2:
3092 if old.p2:
3093 copied.update(copies.pathcopies(old.p2(), ctx))
3093 copied.update(copies.pathcopies(old.p2(), ctx))
3094
3094
3095 # Prune files which were reverted by the updates: if old
3095 # Prune files which were reverted by the updates: if old
3096 # introduced file X and our intermediate commit, node,
3096 # introduced file X and our intermediate commit, node,
3097 # renamed that file, then those two files are the same and
3097 # renamed that file, then those two files are the same and
3098 # we can discard X from our list of files. Likewise if X
3098 # we can discard X from our list of files. Likewise if X
3099 # was deleted, it's no longer relevant
3099 # was deleted, it's no longer relevant
3100 files.update(ctx.files())
3100 files.update(ctx.files())
3101 files = [f for f in files if not samefile(f, ctx, base)]
3101 files = [f for f in files if not samefile(f, ctx, base)]
3102
3102
3103 def filectxfn(repo, ctx_, path):
3103 def filectxfn(repo, ctx_, path):
3104 try:
3104 try:
3105 fctx = ctx[path]
3105 fctx = ctx[path]
3106 flags = fctx.flags()
3106 flags = fctx.flags()
3107 mctx = context.memfilectx(repo,
3107 mctx = context.memfilectx(repo,
3108 fctx.path(), fctx.data(),
3108 fctx.path(), fctx.data(),
3109 islink='l' in flags,
3109 islink='l' in flags,
3110 isexec='x' in flags,
3110 isexec='x' in flags,
3111 copied=copied.get(path))
3111 copied=copied.get(path))
3112 return mctx
3112 return mctx
3113 except KeyError:
3113 except KeyError:
3114 return None
3114 return None
3115 else:
3115 else:
3116 ui.note(_('copying changeset %s to %s\n') % (old, base))
3116 ui.note(_('copying changeset %s to %s\n') % (old, base))
3117
3117
3118 # Use version of files as in the old cset
3118 # Use version of files as in the old cset
3119 def filectxfn(repo, ctx_, path):
3119 def filectxfn(repo, ctx_, path):
3120 try:
3120 try:
3121 return old.filectx(path)
3121 return old.filectx(path)
3122 except KeyError:
3122 except KeyError:
3123 return None
3123 return None
3124
3124
3125 user = opts.get('user') or old.user()
3125 user = opts.get('user') or old.user()
3126 date = opts.get('date') or old.date()
3126 date = opts.get('date') or old.date()
3127 editform = mergeeditform(old, 'commit.amend')
3127 editform = mergeeditform(old, 'commit.amend')
3128 editor = getcommiteditor(editform=editform,
3128 editor = getcommiteditor(editform=editform,
3129 **pycompat.strkwargs(opts))
3129 **pycompat.strkwargs(opts))
3130 if not message:
3130 if not message:
3131 editor = getcommiteditor(edit=True, editform=editform)
3131 editor = getcommiteditor(edit=True, editform=editform)
3132 message = old.description()
3132 message = old.description()
3133
3133
3134 pureextra = extra.copy()
3134 pureextra = extra.copy()
3135 extra['amend_source'] = old.hex()
3135 extra['amend_source'] = old.hex()
3136
3136
3137 new = context.memctx(repo,
3137 new = context.memctx(repo,
3138 parents=[base.node(), old.p2().node()],
3138 parents=[base.node(), old.p2().node()],
3139 text=message,
3139 text=message,
3140 files=files,
3140 files=files,
3141 filectxfn=filectxfn,
3141 filectxfn=filectxfn,
3142 user=user,
3142 user=user,
3143 date=date,
3143 date=date,
3144 extra=extra,
3144 extra=extra,
3145 editor=editor)
3145 editor=editor)
3146
3146
3147 newdesc = changelog.stripdesc(new.description())
3147 newdesc = changelog.stripdesc(new.description())
3148 if ((not node)
3148 if ((not node)
3149 and newdesc == old.description()
3149 and newdesc == old.description()
3150 and user == old.user()
3150 and user == old.user()
3151 and date == old.date()
3151 and date == old.date()
3152 and pureextra == old.extra()):
3152 and pureextra == old.extra()):
3153 # nothing changed. continuing here would create a new node
3153 # nothing changed. continuing here would create a new node
3154 # anyway because of the amend_source noise.
3154 # anyway because of the amend_source noise.
3155 #
3155 #
3156 # This not what we expect from amend.
3156 # This not what we expect from amend.
3157 return old.node()
3157 return old.node()
3158
3158
3159 ph = repo.ui.config('phases', 'new-commit', phases.draft)
3159 ph = repo.ui.config('phases', 'new-commit', phases.draft)
3160 try:
3160 try:
3161 if opts.get('secret'):
3161 if opts.get('secret'):
3162 commitphase = 'secret'
3162 commitphase = 'secret'
3163 else:
3163 else:
3164 commitphase = old.phase()
3164 commitphase = old.phase()
3165 repo.ui.setconfig('phases', 'new-commit', commitphase, 'amend')
3165 repo.ui.setconfig('phases', 'new-commit', commitphase, 'amend')
3166 newid = repo.commitctx(new)
3166 newid = repo.commitctx(new)
3167 finally:
3167 finally:
3168 repo.ui.setconfig('phases', 'new-commit', ph, 'amend')
3168 repo.ui.setconfig('phases', 'new-commit', ph, 'amend')
3169 if newid != old.node():
3169 if newid != old.node():
3170 # Reroute the working copy parent to the new changeset
3170 # Reroute the working copy parent to the new changeset
3171 repo.setparents(newid, nullid)
3171 repo.setparents(newid, nullid)
3172 mapping = {old.node(): (newid,)}
3172 mapping = {old.node(): (newid,)}
3173 if node:
3173 if node:
3174 mapping[node] = ()
3174 mapping[node] = ()
3175 scmutil.cleanupnodes(repo, mapping, 'amend')
3175 scmutil.cleanupnodes(repo, mapping, 'amend')
3176 return newid
3176 return newid
3177
3177
3178 def commiteditor(repo, ctx, subs, editform=''):
3178 def commiteditor(repo, ctx, subs, editform=''):
3179 if ctx.description():
3179 if ctx.description():
3180 return ctx.description()
3180 return ctx.description()
3181 return commitforceeditor(repo, ctx, subs, editform=editform,
3181 return commitforceeditor(repo, ctx, subs, editform=editform,
3182 unchangedmessagedetection=True)
3182 unchangedmessagedetection=True)
3183
3183
3184 def commitforceeditor(repo, ctx, subs, finishdesc=None, extramsg=None,
3184 def commitforceeditor(repo, ctx, subs, finishdesc=None, extramsg=None,
3185 editform='', unchangedmessagedetection=False):
3185 editform='', unchangedmessagedetection=False):
3186 if not extramsg:
3186 if not extramsg:
3187 extramsg = _("Leave message empty to abort commit.")
3187 extramsg = _("Leave message empty to abort commit.")
3188
3188
3189 forms = [e for e in editform.split('.') if e]
3189 forms = [e for e in editform.split('.') if e]
3190 forms.insert(0, 'changeset')
3190 forms.insert(0, 'changeset')
3191 templatetext = None
3191 templatetext = None
3192 while forms:
3192 while forms:
3193 ref = '.'.join(forms)
3193 ref = '.'.join(forms)
3194 if repo.ui.config('committemplate', ref):
3194 if repo.ui.config('committemplate', ref):
3195 templatetext = committext = buildcommittemplate(
3195 templatetext = committext = buildcommittemplate(
3196 repo, ctx, subs, extramsg, ref)
3196 repo, ctx, subs, extramsg, ref)
3197 break
3197 break
3198 forms.pop()
3198 forms.pop()
3199 else:
3199 else:
3200 committext = buildcommittext(repo, ctx, subs, extramsg)
3200 committext = buildcommittext(repo, ctx, subs, extramsg)
3201
3201
3202 # run editor in the repository root
3202 # run editor in the repository root
3203 olddir = pycompat.getcwd()
3203 olddir = pycompat.getcwd()
3204 os.chdir(repo.root)
3204 os.chdir(repo.root)
3205
3205
3206 # make in-memory changes visible to external process
3206 # make in-memory changes visible to external process
3207 tr = repo.currenttransaction()
3207 tr = repo.currenttransaction()
3208 repo.dirstate.write(tr)
3208 repo.dirstate.write(tr)
3209 pending = tr and tr.writepending() and repo.root
3209 pending = tr and tr.writepending() and repo.root
3210
3210
3211 editortext = repo.ui.edit(committext, ctx.user(), ctx.extra(),
3211 editortext = repo.ui.edit(committext, ctx.user(), ctx.extra(),
3212 editform=editform, pending=pending,
3212 editform=editform, pending=pending,
3213 repopath=repo.path)
3213 repopath=repo.path)
3214 text = editortext
3214 text = editortext
3215
3215
3216 # strip away anything below this special string (used for editors that want
3216 # strip away anything below this special string (used for editors that want
3217 # to display the diff)
3217 # to display the diff)
3218 stripbelow = re.search(_linebelow, text, flags=re.MULTILINE)
3218 stripbelow = re.search(_linebelow, text, flags=re.MULTILINE)
3219 if stripbelow:
3219 if stripbelow:
3220 text = text[:stripbelow.start()]
3220 text = text[:stripbelow.start()]
3221
3221
3222 text = re.sub("(?m)^HG:.*(\n|$)", "", text)
3222 text = re.sub("(?m)^HG:.*(\n|$)", "", text)
3223 os.chdir(olddir)
3223 os.chdir(olddir)
3224
3224
3225 if finishdesc:
3225 if finishdesc:
3226 text = finishdesc(text)
3226 text = finishdesc(text)
3227 if not text.strip():
3227 if not text.strip():
3228 raise error.Abort(_("empty commit message"))
3228 raise error.Abort(_("empty commit message"))
3229 if unchangedmessagedetection and editortext == templatetext:
3229 if unchangedmessagedetection and editortext == templatetext:
3230 raise error.Abort(_("commit message unchanged"))
3230 raise error.Abort(_("commit message unchanged"))
3231
3231
3232 return text
3232 return text
3233
3233
3234 def buildcommittemplate(repo, ctx, subs, extramsg, ref):
3234 def buildcommittemplate(repo, ctx, subs, extramsg, ref):
3235 ui = repo.ui
3235 ui = repo.ui
3236 spec = formatter.templatespec(ref, None, None)
3236 spec = formatter.templatespec(ref, None, None)
3237 t = changeset_templater(ui, repo, spec, None, {}, False)
3237 t = changeset_templater(ui, repo, spec, None, {}, False)
3238 t.t.cache.update((k, templater.unquotestring(v))
3238 t.t.cache.update((k, templater.unquotestring(v))
3239 for k, v in repo.ui.configitems('committemplate'))
3239 for k, v in repo.ui.configitems('committemplate'))
3240
3240
3241 if not extramsg:
3241 if not extramsg:
3242 extramsg = '' # ensure that extramsg is string
3242 extramsg = '' # ensure that extramsg is string
3243
3243
3244 ui.pushbuffer()
3244 ui.pushbuffer()
3245 t.show(ctx, extramsg=extramsg)
3245 t.show(ctx, extramsg=extramsg)
3246 return ui.popbuffer()
3246 return ui.popbuffer()
3247
3247
3248 def hgprefix(msg):
3248 def hgprefix(msg):
3249 return "\n".join(["HG: %s" % a for a in msg.split("\n") if a])
3249 return "\n".join(["HG: %s" % a for a in msg.split("\n") if a])
3250
3250
3251 def buildcommittext(repo, ctx, subs, extramsg):
3251 def buildcommittext(repo, ctx, subs, extramsg):
3252 edittext = []
3252 edittext = []
3253 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
3253 modified, added, removed = ctx.modified(), ctx.added(), ctx.removed()
3254 if ctx.description():
3254 if ctx.description():
3255 edittext.append(ctx.description())
3255 edittext.append(ctx.description())
3256 edittext.append("")
3256 edittext.append("")
3257 edittext.append("") # Empty line between message and comments.
3257 edittext.append("") # Empty line between message and comments.
3258 edittext.append(hgprefix(_("Enter commit message."
3258 edittext.append(hgprefix(_("Enter commit message."
3259 " Lines beginning with 'HG:' are removed.")))
3259 " Lines beginning with 'HG:' are removed.")))
3260 edittext.append(hgprefix(extramsg))
3260 edittext.append(hgprefix(extramsg))
3261 edittext.append("HG: --")
3261 edittext.append("HG: --")
3262 edittext.append(hgprefix(_("user: %s") % ctx.user()))
3262 edittext.append(hgprefix(_("user: %s") % ctx.user()))
3263 if ctx.p2():
3263 if ctx.p2():
3264 edittext.append(hgprefix(_("branch merge")))
3264 edittext.append(hgprefix(_("branch merge")))
3265 if ctx.branch():
3265 if ctx.branch():
3266 edittext.append(hgprefix(_("branch '%s'") % ctx.branch()))
3266 edittext.append(hgprefix(_("branch '%s'") % ctx.branch()))
3267 if bookmarks.isactivewdirparent(repo):
3267 if bookmarks.isactivewdirparent(repo):
3268 edittext.append(hgprefix(_("bookmark '%s'") % repo._activebookmark))
3268 edittext.append(hgprefix(_("bookmark '%s'") % repo._activebookmark))
3269 edittext.extend([hgprefix(_("subrepo %s") % s) for s in subs])
3269 edittext.extend([hgprefix(_("subrepo %s") % s) for s in subs])
3270 edittext.extend([hgprefix(_("added %s") % f) for f in added])
3270 edittext.extend([hgprefix(_("added %s") % f) for f in added])
3271 edittext.extend([hgprefix(_("changed %s") % f) for f in modified])
3271 edittext.extend([hgprefix(_("changed %s") % f) for f in modified])
3272 edittext.extend([hgprefix(_("removed %s") % f) for f in removed])
3272 edittext.extend([hgprefix(_("removed %s") % f) for f in removed])
3273 if not added and not modified and not removed:
3273 if not added and not modified and not removed:
3274 edittext.append(hgprefix(_("no files changed")))
3274 edittext.append(hgprefix(_("no files changed")))
3275 edittext.append("")
3275 edittext.append("")
3276
3276
3277 return "\n".join(edittext)
3277 return "\n".join(edittext)
3278
3278
3279 def commitstatus(repo, node, branch, bheads=None, opts=None):
3279 def commitstatus(repo, node, branch, bheads=None, opts=None):
3280 if opts is None:
3280 if opts is None:
3281 opts = {}
3281 opts = {}
3282 ctx = repo[node]
3282 ctx = repo[node]
3283 parents = ctx.parents()
3283 parents = ctx.parents()
3284
3284
3285 if (not opts.get('amend') and bheads and node not in bheads and not
3285 if (not opts.get('amend') and bheads and node not in bheads and not
3286 [x for x in parents if x.node() in bheads and x.branch() == branch]):
3286 [x for x in parents if x.node() in bheads and x.branch() == branch]):
3287 repo.ui.status(_('created new head\n'))
3287 repo.ui.status(_('created new head\n'))
3288 # The message is not printed for initial roots. For the other
3288 # The message is not printed for initial roots. For the other
3289 # changesets, it is printed in the following situations:
3289 # changesets, it is printed in the following situations:
3290 #
3290 #
3291 # Par column: for the 2 parents with ...
3291 # Par column: for the 2 parents with ...
3292 # N: null or no parent
3292 # N: null or no parent
3293 # B: parent is on another named branch
3293 # B: parent is on another named branch
3294 # C: parent is a regular non head changeset
3294 # C: parent is a regular non head changeset
3295 # H: parent was a branch head of the current branch
3295 # H: parent was a branch head of the current branch
3296 # Msg column: whether we print "created new head" message
3296 # Msg column: whether we print "created new head" message
3297 # In the following, it is assumed that there already exists some
3297 # In the following, it is assumed that there already exists some
3298 # initial branch heads of the current branch, otherwise nothing is
3298 # initial branch heads of the current branch, otherwise nothing is
3299 # printed anyway.
3299 # printed anyway.
3300 #
3300 #
3301 # Par Msg Comment
3301 # Par Msg Comment
3302 # N N y additional topo root
3302 # N N y additional topo root
3303 #
3303 #
3304 # B N y additional branch root
3304 # B N y additional branch root
3305 # C N y additional topo head
3305 # C N y additional topo head
3306 # H N n usual case
3306 # H N n usual case
3307 #
3307 #
3308 # B B y weird additional branch root
3308 # B B y weird additional branch root
3309 # C B y branch merge
3309 # C B y branch merge
3310 # H B n merge with named branch
3310 # H B n merge with named branch
3311 #
3311 #
3312 # C C y additional head from merge
3312 # C C y additional head from merge
3313 # C H n merge with a head
3313 # C H n merge with a head
3314 #
3314 #
3315 # H H n head merge: head count decreases
3315 # H H n head merge: head count decreases
3316
3316
3317 if not opts.get('close_branch'):
3317 if not opts.get('close_branch'):
3318 for r in parents:
3318 for r in parents:
3319 if r.closesbranch() and r.branch() == branch:
3319 if r.closesbranch() and r.branch() == branch:
3320 repo.ui.status(_('reopening closed branch head %d\n') % r)
3320 repo.ui.status(_('reopening closed branch head %d\n') % r)
3321
3321
3322 if repo.ui.debugflag:
3322 if repo.ui.debugflag:
3323 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
3323 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
3324 elif repo.ui.verbose:
3324 elif repo.ui.verbose:
3325 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
3325 repo.ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
3326
3326
3327 def postcommitstatus(repo, pats, opts):
3327 def postcommitstatus(repo, pats, opts):
3328 return repo.status(match=scmutil.match(repo[None], pats, opts))
3328 return repo.status(match=scmutil.match(repo[None], pats, opts))
3329
3329
3330 def revert(ui, repo, ctx, parents, *pats, **opts):
3330 def revert(ui, repo, ctx, parents, *pats, **opts):
3331 parent, p2 = parents
3331 parent, p2 = parents
3332 node = ctx.node()
3332 node = ctx.node()
3333
3333
3334 mf = ctx.manifest()
3334 mf = ctx.manifest()
3335 if node == p2:
3335 if node == p2:
3336 parent = p2
3336 parent = p2
3337
3337
3338 # need all matching names in dirstate and manifest of target rev,
3338 # need all matching names in dirstate and manifest of target rev,
3339 # so have to walk both. do not print errors if files exist in one
3339 # so have to walk both. do not print errors if files exist in one
3340 # but not other. in both cases, filesets should be evaluated against
3340 # but not other. in both cases, filesets should be evaluated against
3341 # workingctx to get consistent result (issue4497). this means 'set:**'
3341 # workingctx to get consistent result (issue4497). this means 'set:**'
3342 # cannot be used to select missing files from target rev.
3342 # cannot be used to select missing files from target rev.
3343
3343
3344 # `names` is a mapping for all elements in working copy and target revision
3344 # `names` is a mapping for all elements in working copy and target revision
3345 # The mapping is in the form:
3345 # The mapping is in the form:
3346 # <asb path in repo> -> (<path from CWD>, <exactly specified by matcher?>)
3346 # <asb path in repo> -> (<path from CWD>, <exactly specified by matcher?>)
3347 names = {}
3347 names = {}
3348
3348
3349 with repo.wlock():
3349 with repo.wlock():
3350 ## filling of the `names` mapping
3350 ## filling of the `names` mapping
3351 # walk dirstate to fill `names`
3351 # walk dirstate to fill `names`
3352
3352
3353 interactive = opts.get('interactive', False)
3353 interactive = opts.get('interactive', False)
3354 wctx = repo[None]
3354 wctx = repo[None]
3355 m = scmutil.match(wctx, pats, opts)
3355 m = scmutil.match(wctx, pats, opts)
3356
3356
3357 # we'll need this later
3357 # we'll need this later
3358 targetsubs = sorted(s for s in wctx.substate if m(s))
3358 targetsubs = sorted(s for s in wctx.substate if m(s))
3359
3359
3360 if not m.always():
3360 if not m.always():
3361 matcher = matchmod.badmatch(m, lambda x, y: False)
3361 matcher = matchmod.badmatch(m, lambda x, y: False)
3362 for abs in wctx.walk(matcher):
3362 for abs in wctx.walk(matcher):
3363 names[abs] = m.rel(abs), m.exact(abs)
3363 names[abs] = m.rel(abs), m.exact(abs)
3364
3364
3365 # walk target manifest to fill `names`
3365 # walk target manifest to fill `names`
3366
3366
3367 def badfn(path, msg):
3367 def badfn(path, msg):
3368 if path in names:
3368 if path in names:
3369 return
3369 return
3370 if path in ctx.substate:
3370 if path in ctx.substate:
3371 return
3371 return
3372 path_ = path + '/'
3372 path_ = path + '/'
3373 for f in names:
3373 for f in names:
3374 if f.startswith(path_):
3374 if f.startswith(path_):
3375 return
3375 return
3376 ui.warn("%s: %s\n" % (m.rel(path), msg))
3376 ui.warn("%s: %s\n" % (m.rel(path), msg))
3377
3377
3378 for abs in ctx.walk(matchmod.badmatch(m, badfn)):
3378 for abs in ctx.walk(matchmod.badmatch(m, badfn)):
3379 if abs not in names:
3379 if abs not in names:
3380 names[abs] = m.rel(abs), m.exact(abs)
3380 names[abs] = m.rel(abs), m.exact(abs)
3381
3381
3382 # Find status of all file in `names`.
3382 # Find status of all file in `names`.
3383 m = scmutil.matchfiles(repo, names)
3383 m = scmutil.matchfiles(repo, names)
3384
3384
3385 changes = repo.status(node1=node, match=m,
3385 changes = repo.status(node1=node, match=m,
3386 unknown=True, ignored=True, clean=True)
3386 unknown=True, ignored=True, clean=True)
3387 else:
3387 else:
3388 changes = repo.status(node1=node, match=m)
3388 changes = repo.status(node1=node, match=m)
3389 for kind in changes:
3389 for kind in changes:
3390 for abs in kind:
3390 for abs in kind:
3391 names[abs] = m.rel(abs), m.exact(abs)
3391 names[abs] = m.rel(abs), m.exact(abs)
3392
3392
3393 m = scmutil.matchfiles(repo, names)
3393 m = scmutil.matchfiles(repo, names)
3394
3394
3395 modified = set(changes.modified)
3395 modified = set(changes.modified)
3396 added = set(changes.added)
3396 added = set(changes.added)
3397 removed = set(changes.removed)
3397 removed = set(changes.removed)
3398 _deleted = set(changes.deleted)
3398 _deleted = set(changes.deleted)
3399 unknown = set(changes.unknown)
3399 unknown = set(changes.unknown)
3400 unknown.update(changes.ignored)
3400 unknown.update(changes.ignored)
3401 clean = set(changes.clean)
3401 clean = set(changes.clean)
3402 modadded = set()
3402 modadded = set()
3403
3403
3404 # We need to account for the state of the file in the dirstate,
3404 # We need to account for the state of the file in the dirstate,
3405 # even when we revert against something else than parent. This will
3405 # even when we revert against something else than parent. This will
3406 # slightly alter the behavior of revert (doing back up or not, delete
3406 # slightly alter the behavior of revert (doing back up or not, delete
3407 # or just forget etc).
3407 # or just forget etc).
3408 if parent == node:
3408 if parent == node:
3409 dsmodified = modified
3409 dsmodified = modified
3410 dsadded = added
3410 dsadded = added
3411 dsremoved = removed
3411 dsremoved = removed
3412 # store all local modifications, useful later for rename detection
3412 # store all local modifications, useful later for rename detection
3413 localchanges = dsmodified | dsadded
3413 localchanges = dsmodified | dsadded
3414 modified, added, removed = set(), set(), set()
3414 modified, added, removed = set(), set(), set()
3415 else:
3415 else:
3416 changes = repo.status(node1=parent, match=m)
3416 changes = repo.status(node1=parent, match=m)
3417 dsmodified = set(changes.modified)
3417 dsmodified = set(changes.modified)
3418 dsadded = set(changes.added)
3418 dsadded = set(changes.added)
3419 dsremoved = set(changes.removed)
3419 dsremoved = set(changes.removed)
3420 # store all local modifications, useful later for rename detection
3420 # store all local modifications, useful later for rename detection
3421 localchanges = dsmodified | dsadded
3421 localchanges = dsmodified | dsadded
3422
3422
3423 # only take into account for removes between wc and target
3423 # only take into account for removes between wc and target
3424 clean |= dsremoved - removed
3424 clean |= dsremoved - removed
3425 dsremoved &= removed
3425 dsremoved &= removed
3426 # distinct between dirstate remove and other
3426 # distinct between dirstate remove and other
3427 removed -= dsremoved
3427 removed -= dsremoved
3428
3428
3429 modadded = added & dsmodified
3429 modadded = added & dsmodified
3430 added -= modadded
3430 added -= modadded
3431
3431
3432 # tell newly modified apart.
3432 # tell newly modified apart.
3433 dsmodified &= modified
3433 dsmodified &= modified
3434 dsmodified |= modified & dsadded # dirstate added may need backup
3434 dsmodified |= modified & dsadded # dirstate added may need backup
3435 modified -= dsmodified
3435 modified -= dsmodified
3436
3436
3437 # We need to wait for some post-processing to update this set
3437 # We need to wait for some post-processing to update this set
3438 # before making the distinction. The dirstate will be used for
3438 # before making the distinction. The dirstate will be used for
3439 # that purpose.
3439 # that purpose.
3440 dsadded = added
3440 dsadded = added
3441
3441
3442 # in case of merge, files that are actually added can be reported as
3442 # in case of merge, files that are actually added can be reported as
3443 # modified, we need to post process the result
3443 # modified, we need to post process the result
3444 if p2 != nullid:
3444 if p2 != nullid:
3445 mergeadd = set(dsmodified)
3445 mergeadd = set(dsmodified)
3446 for path in dsmodified:
3446 for path in dsmodified:
3447 if path in mf:
3447 if path in mf:
3448 mergeadd.remove(path)
3448 mergeadd.remove(path)
3449 dsadded |= mergeadd
3449 dsadded |= mergeadd
3450 dsmodified -= mergeadd
3450 dsmodified -= mergeadd
3451
3451
3452 # if f is a rename, update `names` to also revert the source
3452 # if f is a rename, update `names` to also revert the source
3453 cwd = repo.getcwd()
3453 cwd = repo.getcwd()
3454 for f in localchanges:
3454 for f in localchanges:
3455 src = repo.dirstate.copied(f)
3455 src = repo.dirstate.copied(f)
3456 # XXX should we check for rename down to target node?
3456 # XXX should we check for rename down to target node?
3457 if src and src not in names and repo.dirstate[src] == 'r':
3457 if src and src not in names and repo.dirstate[src] == 'r':
3458 dsremoved.add(src)
3458 dsremoved.add(src)
3459 names[src] = (repo.pathto(src, cwd), True)
3459 names[src] = (repo.pathto(src, cwd), True)
3460
3460
3461 # determine the exact nature of the deleted changesets
3461 # determine the exact nature of the deleted changesets
3462 deladded = set(_deleted)
3462 deladded = set(_deleted)
3463 for path in _deleted:
3463 for path in _deleted:
3464 if path in mf:
3464 if path in mf:
3465 deladded.remove(path)
3465 deladded.remove(path)
3466 deleted = _deleted - deladded
3466 deleted = _deleted - deladded
3467
3467
3468 # distinguish between file to forget and the other
3468 # distinguish between file to forget and the other
3469 added = set()
3469 added = set()
3470 for abs in dsadded:
3470 for abs in dsadded:
3471 if repo.dirstate[abs] != 'a':
3471 if repo.dirstate[abs] != 'a':
3472 added.add(abs)
3472 added.add(abs)
3473 dsadded -= added
3473 dsadded -= added
3474
3474
3475 for abs in deladded:
3475 for abs in deladded:
3476 if repo.dirstate[abs] == 'a':
3476 if repo.dirstate[abs] == 'a':
3477 dsadded.add(abs)
3477 dsadded.add(abs)
3478 deladded -= dsadded
3478 deladded -= dsadded
3479
3479
3480 # For files marked as removed, we check if an unknown file is present at
3480 # For files marked as removed, we check if an unknown file is present at
3481 # the same path. If a such file exists it may need to be backed up.
3481 # the same path. If a such file exists it may need to be backed up.
3482 # Making the distinction at this stage helps have simpler backup
3482 # Making the distinction at this stage helps have simpler backup
3483 # logic.
3483 # logic.
3484 removunk = set()
3484 removunk = set()
3485 for abs in removed:
3485 for abs in removed:
3486 target = repo.wjoin(abs)
3486 target = repo.wjoin(abs)
3487 if os.path.lexists(target):
3487 if os.path.lexists(target):
3488 removunk.add(abs)
3488 removunk.add(abs)
3489 removed -= removunk
3489 removed -= removunk
3490
3490
3491 dsremovunk = set()
3491 dsremovunk = set()
3492 for abs in dsremoved:
3492 for abs in dsremoved:
3493 target = repo.wjoin(abs)
3493 target = repo.wjoin(abs)
3494 if os.path.lexists(target):
3494 if os.path.lexists(target):
3495 dsremovunk.add(abs)
3495 dsremovunk.add(abs)
3496 dsremoved -= dsremovunk
3496 dsremoved -= dsremovunk
3497
3497
3498 # action to be actually performed by revert
3498 # action to be actually performed by revert
3499 # (<list of file>, message>) tuple
3499 # (<list of file>, message>) tuple
3500 actions = {'revert': ([], _('reverting %s\n')),
3500 actions = {'revert': ([], _('reverting %s\n')),
3501 'add': ([], _('adding %s\n')),
3501 'add': ([], _('adding %s\n')),
3502 'remove': ([], _('removing %s\n')),
3502 'remove': ([], _('removing %s\n')),
3503 'drop': ([], _('removing %s\n')),
3503 'drop': ([], _('removing %s\n')),
3504 'forget': ([], _('forgetting %s\n')),
3504 'forget': ([], _('forgetting %s\n')),
3505 'undelete': ([], _('undeleting %s\n')),
3505 'undelete': ([], _('undeleting %s\n')),
3506 'noop': (None, _('no changes needed to %s\n')),
3506 'noop': (None, _('no changes needed to %s\n')),
3507 'unknown': (None, _('file not managed: %s\n')),
3507 'unknown': (None, _('file not managed: %s\n')),
3508 }
3508 }
3509
3509
3510 # "constant" that convey the backup strategy.
3510 # "constant" that convey the backup strategy.
3511 # All set to `discard` if `no-backup` is set do avoid checking
3511 # All set to `discard` if `no-backup` is set do avoid checking
3512 # no_backup lower in the code.
3512 # no_backup lower in the code.
3513 # These values are ordered for comparison purposes
3513 # These values are ordered for comparison purposes
3514 backupinteractive = 3 # do backup if interactively modified
3514 backupinteractive = 3 # do backup if interactively modified
3515 backup = 2 # unconditionally do backup
3515 backup = 2 # unconditionally do backup
3516 check = 1 # check if the existing file differs from target
3516 check = 1 # check if the existing file differs from target
3517 discard = 0 # never do backup
3517 discard = 0 # never do backup
3518 if opts.get('no_backup'):
3518 if opts.get('no_backup'):
3519 backupinteractive = backup = check = discard
3519 backupinteractive = backup = check = discard
3520 if interactive:
3520 if interactive:
3521 dsmodifiedbackup = backupinteractive
3521 dsmodifiedbackup = backupinteractive
3522 else:
3522 else:
3523 dsmodifiedbackup = backup
3523 dsmodifiedbackup = backup
3524 tobackup = set()
3524 tobackup = set()
3525
3525
3526 backupanddel = actions['remove']
3526 backupanddel = actions['remove']
3527 if not opts.get('no_backup'):
3527 if not opts.get('no_backup'):
3528 backupanddel = actions['drop']
3528 backupanddel = actions['drop']
3529
3529
3530 disptable = (
3530 disptable = (
3531 # dispatch table:
3531 # dispatch table:
3532 # file state
3532 # file state
3533 # action
3533 # action
3534 # make backup
3534 # make backup
3535
3535
3536 ## Sets that results that will change file on disk
3536 ## Sets that results that will change file on disk
3537 # Modified compared to target, no local change
3537 # Modified compared to target, no local change
3538 (modified, actions['revert'], discard),
3538 (modified, actions['revert'], discard),
3539 # Modified compared to target, but local file is deleted
3539 # Modified compared to target, but local file is deleted
3540 (deleted, actions['revert'], discard),
3540 (deleted, actions['revert'], discard),
3541 # Modified compared to target, local change
3541 # Modified compared to target, local change
3542 (dsmodified, actions['revert'], dsmodifiedbackup),
3542 (dsmodified, actions['revert'], dsmodifiedbackup),
3543 # Added since target
3543 # Added since target
3544 (added, actions['remove'], discard),
3544 (added, actions['remove'], discard),
3545 # Added in working directory
3545 # Added in working directory
3546 (dsadded, actions['forget'], discard),
3546 (dsadded, actions['forget'], discard),
3547 # Added since target, have local modification
3547 # Added since target, have local modification
3548 (modadded, backupanddel, backup),
3548 (modadded, backupanddel, backup),
3549 # Added since target but file is missing in working directory
3549 # Added since target but file is missing in working directory
3550 (deladded, actions['drop'], discard),
3550 (deladded, actions['drop'], discard),
3551 # Removed since target, before working copy parent
3551 # Removed since target, before working copy parent
3552 (removed, actions['add'], discard),
3552 (removed, actions['add'], discard),
3553 # Same as `removed` but an unknown file exists at the same path
3553 # Same as `removed` but an unknown file exists at the same path
3554 (removunk, actions['add'], check),
3554 (removunk, actions['add'], check),
3555 # Removed since targe, marked as such in working copy parent
3555 # Removed since targe, marked as such in working copy parent
3556 (dsremoved, actions['undelete'], discard),
3556 (dsremoved, actions['undelete'], discard),
3557 # Same as `dsremoved` but an unknown file exists at the same path
3557 # Same as `dsremoved` but an unknown file exists at the same path
3558 (dsremovunk, actions['undelete'], check),
3558 (dsremovunk, actions['undelete'], check),
3559 ## the following sets does not result in any file changes
3559 ## the following sets does not result in any file changes
3560 # File with no modification
3560 # File with no modification
3561 (clean, actions['noop'], discard),
3561 (clean, actions['noop'], discard),
3562 # Existing file, not tracked anywhere
3562 # Existing file, not tracked anywhere
3563 (unknown, actions['unknown'], discard),
3563 (unknown, actions['unknown'], discard),
3564 )
3564 )
3565
3565
3566 for abs, (rel, exact) in sorted(names.items()):
3566 for abs, (rel, exact) in sorted(names.items()):
3567 # target file to be touch on disk (relative to cwd)
3567 # target file to be touch on disk (relative to cwd)
3568 target = repo.wjoin(abs)
3568 target = repo.wjoin(abs)
3569 # search the entry in the dispatch table.
3569 # search the entry in the dispatch table.
3570 # if the file is in any of these sets, it was touched in the working
3570 # if the file is in any of these sets, it was touched in the working
3571 # directory parent and we are sure it needs to be reverted.
3571 # directory parent and we are sure it needs to be reverted.
3572 for table, (xlist, msg), dobackup in disptable:
3572 for table, (xlist, msg), dobackup in disptable:
3573 if abs not in table:
3573 if abs not in table:
3574 continue
3574 continue
3575 if xlist is not None:
3575 if xlist is not None:
3576 xlist.append(abs)
3576 xlist.append(abs)
3577 if dobackup:
3577 if dobackup:
3578 # If in interactive mode, don't automatically create
3578 # If in interactive mode, don't automatically create
3579 # .orig files (issue4793)
3579 # .orig files (issue4793)
3580 if dobackup == backupinteractive:
3580 if dobackup == backupinteractive:
3581 tobackup.add(abs)
3581 tobackup.add(abs)
3582 elif (backup <= dobackup or wctx[abs].cmp(ctx[abs])):
3582 elif (backup <= dobackup or wctx[abs].cmp(ctx[abs])):
3583 bakname = scmutil.origpath(ui, repo, rel)
3583 bakname = scmutil.origpath(ui, repo, rel)
3584 ui.note(_('saving current version of %s as %s\n') %
3584 ui.note(_('saving current version of %s as %s\n') %
3585 (rel, bakname))
3585 (rel, bakname))
3586 if not opts.get('dry_run'):
3586 if not opts.get('dry_run'):
3587 if interactive:
3587 if interactive:
3588 util.copyfile(target, bakname)
3588 util.copyfile(target, bakname)
3589 else:
3589 else:
3590 util.rename(target, bakname)
3590 util.rename(target, bakname)
3591 if ui.verbose or not exact:
3591 if ui.verbose or not exact:
3592 if not isinstance(msg, basestring):
3592 if not isinstance(msg, basestring):
3593 msg = msg(abs)
3593 msg = msg(abs)
3594 ui.status(msg % rel)
3594 ui.status(msg % rel)
3595 elif exact:
3595 elif exact:
3596 ui.warn(msg % rel)
3596 ui.warn(msg % rel)
3597 break
3597 break
3598
3598
3599 if not opts.get('dry_run'):
3599 if not opts.get('dry_run'):
3600 needdata = ('revert', 'add', 'undelete')
3600 needdata = ('revert', 'add', 'undelete')
3601 _revertprefetch(repo, ctx, *[actions[name][0] for name in needdata])
3601 _revertprefetch(repo, ctx, *[actions[name][0] for name in needdata])
3602 _performrevert(repo, parents, ctx, actions, interactive, tobackup)
3602 _performrevert(repo, parents, ctx, actions, interactive, tobackup)
3603
3603
3604 if targetsubs:
3604 if targetsubs:
3605 # Revert the subrepos on the revert list
3605 # Revert the subrepos on the revert list
3606 for sub in targetsubs:
3606 for sub in targetsubs:
3607 try:
3607 try:
3608 wctx.sub(sub).revert(ctx.substate[sub], *pats, **opts)
3608 wctx.sub(sub).revert(ctx.substate[sub], *pats, **opts)
3609 except KeyError:
3609 except KeyError:
3610 raise error.Abort("subrepository '%s' does not exist in %s!"
3610 raise error.Abort("subrepository '%s' does not exist in %s!"
3611 % (sub, short(ctx.node())))
3611 % (sub, short(ctx.node())))
3612
3612
3613 def _revertprefetch(repo, ctx, *files):
3613 def _revertprefetch(repo, ctx, *files):
3614 """Let extension changing the storage layer prefetch content"""
3614 """Let extension changing the storage layer prefetch content"""
3615 pass
3615 pass
3616
3616
3617 def _performrevert(repo, parents, ctx, actions, interactive=False,
3617 def _performrevert(repo, parents, ctx, actions, interactive=False,
3618 tobackup=None):
3618 tobackup=None):
3619 """function that actually perform all the actions computed for revert
3619 """function that actually perform all the actions computed for revert
3620
3620
3621 This is an independent function to let extension to plug in and react to
3621 This is an independent function to let extension to plug in and react to
3622 the imminent revert.
3622 the imminent revert.
3623
3623
3624 Make sure you have the working directory locked when calling this function.
3624 Make sure you have the working directory locked when calling this function.
3625 """
3625 """
3626 parent, p2 = parents
3626 parent, p2 = parents
3627 node = ctx.node()
3627 node = ctx.node()
3628 excluded_files = []
3628 excluded_files = []
3629 matcher_opts = {"exclude": excluded_files}
3629 matcher_opts = {"exclude": excluded_files}
3630
3630
3631 def checkout(f):
3631 def checkout(f):
3632 fc = ctx[f]
3632 fc = ctx[f]
3633 repo.wwrite(f, fc.data(), fc.flags())
3633 repo.wwrite(f, fc.data(), fc.flags())
3634
3634
3635 def doremove(f):
3635 def doremove(f):
3636 try:
3636 try:
3637 repo.wvfs.unlinkpath(f)
3637 repo.wvfs.unlinkpath(f)
3638 except OSError:
3638 except OSError:
3639 pass
3639 pass
3640 repo.dirstate.remove(f)
3640 repo.dirstate.remove(f)
3641
3641
3642 audit_path = pathutil.pathauditor(repo.root, cached=True)
3642 audit_path = pathutil.pathauditor(repo.root, cached=True)
3643 for f in actions['forget'][0]:
3643 for f in actions['forget'][0]:
3644 if interactive:
3644 if interactive:
3645 choice = repo.ui.promptchoice(
3645 choice = repo.ui.promptchoice(
3646 _("forget added file %s (Yn)?$$ &Yes $$ &No") % f)
3646 _("forget added file %s (Yn)?$$ &Yes $$ &No") % f)
3647 if choice == 0:
3647 if choice == 0:
3648 repo.dirstate.drop(f)
3648 repo.dirstate.drop(f)
3649 else:
3649 else:
3650 excluded_files.append(repo.wjoin(f))
3650 excluded_files.append(repo.wjoin(f))
3651 else:
3651 else:
3652 repo.dirstate.drop(f)
3652 repo.dirstate.drop(f)
3653 for f in actions['remove'][0]:
3653 for f in actions['remove'][0]:
3654 audit_path(f)
3654 audit_path(f)
3655 if interactive:
3655 if interactive:
3656 choice = repo.ui.promptchoice(
3656 choice = repo.ui.promptchoice(
3657 _("remove added file %s (Yn)?$$ &Yes $$ &No") % f)
3657 _("remove added file %s (Yn)?$$ &Yes $$ &No") % f)
3658 if choice == 0:
3658 if choice == 0:
3659 doremove(f)
3659 doremove(f)
3660 else:
3660 else:
3661 excluded_files.append(repo.wjoin(f))
3661 excluded_files.append(repo.wjoin(f))
3662 else:
3662 else:
3663 doremove(f)
3663 doremove(f)
3664 for f in actions['drop'][0]:
3664 for f in actions['drop'][0]:
3665 audit_path(f)
3665 audit_path(f)
3666 repo.dirstate.remove(f)
3666 repo.dirstate.remove(f)
3667
3667
3668 normal = None
3668 normal = None
3669 if node == parent:
3669 if node == parent:
3670 # We're reverting to our parent. If possible, we'd like status
3670 # We're reverting to our parent. If possible, we'd like status
3671 # to report the file as clean. We have to use normallookup for
3671 # to report the file as clean. We have to use normallookup for
3672 # merges to avoid losing information about merged/dirty files.
3672 # merges to avoid losing information about merged/dirty files.
3673 if p2 != nullid:
3673 if p2 != nullid:
3674 normal = repo.dirstate.normallookup
3674 normal = repo.dirstate.normallookup
3675 else:
3675 else:
3676 normal = repo.dirstate.normal
3676 normal = repo.dirstate.normal
3677
3677
3678 newlyaddedandmodifiedfiles = set()
3678 newlyaddedandmodifiedfiles = set()
3679 if interactive:
3679 if interactive:
3680 # Prompt the user for changes to revert
3680 # Prompt the user for changes to revert
3681 torevert = [repo.wjoin(f) for f in actions['revert'][0]]
3681 torevert = [repo.wjoin(f) for f in actions['revert'][0]]
3682 m = scmutil.match(ctx, torevert, matcher_opts)
3682 m = scmutil.match(ctx, torevert, matcher_opts)
3683 diffopts = patch.difffeatureopts(repo.ui, whitespace=True)
3683 diffopts = patch.difffeatureopts(repo.ui, whitespace=True)
3684 diffopts.nodates = True
3684 diffopts.nodates = True
3685 diffopts.git = True
3685 diffopts.git = True
3686 operation = 'discard'
3686 operation = 'discard'
3687 reversehunks = True
3687 reversehunks = True
3688 if node != parent:
3688 if node != parent:
3689 operation = 'revert'
3689 operation = 'revert'
3690 reversehunks = repo.ui.configbool('experimental',
3690 reversehunks = repo.ui.configbool('experimental',
3691 'revertalternateinteractivemode')
3691 'revertalternateinteractivemode')
3692 if reversehunks:
3692 if reversehunks:
3693 diff = patch.diff(repo, ctx.node(), None, m, opts=diffopts)
3693 diff = patch.diff(repo, ctx.node(), None, m, opts=diffopts)
3694 else:
3694 else:
3695 diff = patch.diff(repo, None, ctx.node(), m, opts=diffopts)
3695 diff = patch.diff(repo, None, ctx.node(), m, opts=diffopts)
3696 originalchunks = patch.parsepatch(diff)
3696 originalchunks = patch.parsepatch(diff)
3697
3697
3698 try:
3698 try:
3699
3699
3700 chunks, opts = recordfilter(repo.ui, originalchunks,
3700 chunks, opts = recordfilter(repo.ui, originalchunks,
3701 operation=operation)
3701 operation=operation)
3702 if reversehunks:
3702 if reversehunks:
3703 chunks = patch.reversehunks(chunks)
3703 chunks = patch.reversehunks(chunks)
3704
3704
3705 except patch.PatchError as err:
3705 except patch.PatchError as err:
3706 raise error.Abort(_('error parsing patch: %s') % err)
3706 raise error.Abort(_('error parsing patch: %s') % err)
3707
3707
3708 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
3708 newlyaddedandmodifiedfiles = newandmodified(chunks, originalchunks)
3709 if tobackup is None:
3709 if tobackup is None:
3710 tobackup = set()
3710 tobackup = set()
3711 # Apply changes
3711 # Apply changes
3712 fp = stringio()
3712 fp = stringio()
3713 for c in chunks:
3713 for c in chunks:
3714 # Create a backup file only if this hunk should be backed up
3714 # Create a backup file only if this hunk should be backed up
3715 if ishunk(c) and c.header.filename() in tobackup:
3715 if ishunk(c) and c.header.filename() in tobackup:
3716 abs = c.header.filename()
3716 abs = c.header.filename()
3717 target = repo.wjoin(abs)
3717 target = repo.wjoin(abs)
3718 bakname = scmutil.origpath(repo.ui, repo, m.rel(abs))
3718 bakname = scmutil.origpath(repo.ui, repo, m.rel(abs))
3719 util.copyfile(target, bakname)
3719 util.copyfile(target, bakname)
3720 tobackup.remove(abs)
3720 tobackup.remove(abs)
3721 c.write(fp)
3721 c.write(fp)
3722 dopatch = fp.tell()
3722 dopatch = fp.tell()
3723 fp.seek(0)
3723 fp.seek(0)
3724 if dopatch:
3724 if dopatch:
3725 try:
3725 try:
3726 patch.internalpatch(repo.ui, repo, fp, 1, eolmode=None)
3726 patch.internalpatch(repo.ui, repo, fp, 1, eolmode=None)
3727 except patch.PatchError as err:
3727 except patch.PatchError as err:
3728 raise error.Abort(str(err))
3728 raise error.Abort(str(err))
3729 del fp
3729 del fp
3730 else:
3730 else:
3731 for f in actions['revert'][0]:
3731 for f in actions['revert'][0]:
3732 checkout(f)
3732 checkout(f)
3733 if normal:
3733 if normal:
3734 normal(f)
3734 normal(f)
3735
3735
3736 for f in actions['add'][0]:
3736 for f in actions['add'][0]:
3737 # Don't checkout modified files, they are already created by the diff
3737 # Don't checkout modified files, they are already created by the diff
3738 if f not in newlyaddedandmodifiedfiles:
3738 if f not in newlyaddedandmodifiedfiles:
3739 checkout(f)
3739 checkout(f)
3740 repo.dirstate.add(f)
3740 repo.dirstate.add(f)
3741
3741
3742 normal = repo.dirstate.normallookup
3742 normal = repo.dirstate.normallookup
3743 if node == parent and p2 == nullid:
3743 if node == parent and p2 == nullid:
3744 normal = repo.dirstate.normal
3744 normal = repo.dirstate.normal
3745 for f in actions['undelete'][0]:
3745 for f in actions['undelete'][0]:
3746 checkout(f)
3746 checkout(f)
3747 normal(f)
3747 normal(f)
3748
3748
3749 copied = copies.pathcopies(repo[parent], ctx)
3749 copied = copies.pathcopies(repo[parent], ctx)
3750
3750
3751 for f in actions['add'][0] + actions['undelete'][0] + actions['revert'][0]:
3751 for f in actions['add'][0] + actions['undelete'][0] + actions['revert'][0]:
3752 if f in copied:
3752 if f in copied:
3753 repo.dirstate.copy(copied[f], f)
3753 repo.dirstate.copy(copied[f], f)
3754
3754
3755 class command(registrar.command):
3755 class command(registrar.command):
3756 def _doregister(self, func, name, *args, **kwargs):
3756 def _doregister(self, func, name, *args, **kwargs):
3757 func._deprecatedregistrar = True # flag for deprecwarn in extensions.py
3757 func._deprecatedregistrar = True # flag for deprecwarn in extensions.py
3758 return super(command, self)._doregister(func, name, *args, **kwargs)
3758 return super(command, self)._doregister(func, name, *args, **kwargs)
3759
3759
3760 # a list of (ui, repo, otherpeer, opts, missing) functions called by
3760 # a list of (ui, repo, otherpeer, opts, missing) functions called by
3761 # commands.outgoing. "missing" is "missing" of the result of
3761 # commands.outgoing. "missing" is "missing" of the result of
3762 # "findcommonoutgoing()"
3762 # "findcommonoutgoing()"
3763 outgoinghooks = util.hooks()
3763 outgoinghooks = util.hooks()
3764
3764
3765 # a list of (ui, repo) functions called by commands.summary
3765 # a list of (ui, repo) functions called by commands.summary
3766 summaryhooks = util.hooks()
3766 summaryhooks = util.hooks()
3767
3767
3768 # a list of (ui, repo, opts, changes) functions called by commands.summary.
3768 # a list of (ui, repo, opts, changes) functions called by commands.summary.
3769 #
3769 #
3770 # functions should return tuple of booleans below, if 'changes' is None:
3770 # functions should return tuple of booleans below, if 'changes' is None:
3771 # (whether-incomings-are-needed, whether-outgoings-are-needed)
3771 # (whether-incomings-are-needed, whether-outgoings-are-needed)
3772 #
3772 #
3773 # otherwise, 'changes' is a tuple of tuples below:
3773 # otherwise, 'changes' is a tuple of tuples below:
3774 # - (sourceurl, sourcebranch, sourcepeer, incoming)
3774 # - (sourceurl, sourcebranch, sourcepeer, incoming)
3775 # - (desturl, destbranch, destpeer, outgoing)
3775 # - (desturl, destbranch, destpeer, outgoing)
3776 summaryremotehooks = util.hooks()
3776 summaryremotehooks = util.hooks()
3777
3777
3778 # A list of state files kept by multistep operations like graft.
3778 # A list of state files kept by multistep operations like graft.
3779 # Since graft cannot be aborted, it is considered 'clearable' by update.
3779 # Since graft cannot be aborted, it is considered 'clearable' by update.
3780 # note: bisect is intentionally excluded
3780 # note: bisect is intentionally excluded
3781 # (state file, clearable, allowcommit, error, hint)
3781 # (state file, clearable, allowcommit, error, hint)
3782 unfinishedstates = [
3782 unfinishedstates = [
3783 ('graftstate', True, False, _('graft in progress'),
3783 ('graftstate', True, False, _('graft in progress'),
3784 _("use 'hg graft --continue' or 'hg update' to abort")),
3784 _("use 'hg graft --continue' or 'hg update' to abort")),
3785 ('updatestate', True, False, _('last update was interrupted'),
3785 ('updatestate', True, False, _('last update was interrupted'),
3786 _("use 'hg update' to get a consistent checkout"))
3786 _("use 'hg update' to get a consistent checkout"))
3787 ]
3787 ]
3788
3788
3789 def checkunfinished(repo, commit=False):
3789 def checkunfinished(repo, commit=False):
3790 '''Look for an unfinished multistep operation, like graft, and abort
3790 '''Look for an unfinished multistep operation, like graft, and abort
3791 if found. It's probably good to check this right before
3791 if found. It's probably good to check this right before
3792 bailifchanged().
3792 bailifchanged().
3793 '''
3793 '''
3794 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3794 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3795 if commit and allowcommit:
3795 if commit and allowcommit:
3796 continue
3796 continue
3797 if repo.vfs.exists(f):
3797 if repo.vfs.exists(f):
3798 raise error.Abort(msg, hint=hint)
3798 raise error.Abort(msg, hint=hint)
3799
3799
3800 def clearunfinished(repo):
3800 def clearunfinished(repo):
3801 '''Check for unfinished operations (as above), and clear the ones
3801 '''Check for unfinished operations (as above), and clear the ones
3802 that are clearable.
3802 that are clearable.
3803 '''
3803 '''
3804 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3804 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3805 if not clearable and repo.vfs.exists(f):
3805 if not clearable and repo.vfs.exists(f):
3806 raise error.Abort(msg, hint=hint)
3806 raise error.Abort(msg, hint=hint)
3807 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3807 for f, clearable, allowcommit, msg, hint in unfinishedstates:
3808 if clearable and repo.vfs.exists(f):
3808 if clearable and repo.vfs.exists(f):
3809 util.unlink(repo.vfs.join(f))
3809 util.unlink(repo.vfs.join(f))
3810
3810
3811 afterresolvedstates = [
3811 afterresolvedstates = [
3812 ('graftstate',
3812 ('graftstate',
3813 _('hg graft --continue')),
3813 _('hg graft --continue')),
3814 ]
3814 ]
3815
3815
3816 def howtocontinue(repo):
3816 def howtocontinue(repo):
3817 '''Check for an unfinished operation and return the command to finish
3817 '''Check for an unfinished operation and return the command to finish
3818 it.
3818 it.
3819
3819
3820 afterresolvedstates tuples define a .hg/{file} and the corresponding
3820 afterresolvedstates tuples define a .hg/{file} and the corresponding
3821 command needed to finish it.
3821 command needed to finish it.
3822
3822
3823 Returns a (msg, warning) tuple. 'msg' is a string and 'warning' is
3823 Returns a (msg, warning) tuple. 'msg' is a string and 'warning' is
3824 a boolean.
3824 a boolean.
3825 '''
3825 '''
3826 contmsg = _("continue: %s")
3826 contmsg = _("continue: %s")
3827 for f, msg in afterresolvedstates:
3827 for f, msg in afterresolvedstates:
3828 if repo.vfs.exists(f):
3828 if repo.vfs.exists(f):
3829 return contmsg % msg, True
3829 return contmsg % msg, True
3830 if repo[None].dirty(missing=True, merge=False, branch=False):
3830 if repo[None].dirty(missing=True, merge=False, branch=False):
3831 return contmsg % _("hg commit"), False
3831 return contmsg % _("hg commit"), False
3832 return None, None
3832 return None, None
3833
3833
3834 def checkafterresolved(repo):
3834 def checkafterresolved(repo):
3835 '''Inform the user about the next action after completing hg resolve
3835 '''Inform the user about the next action after completing hg resolve
3836
3836
3837 If there's a matching afterresolvedstates, howtocontinue will yield
3837 If there's a matching afterresolvedstates, howtocontinue will yield
3838 repo.ui.warn as the reporter.
3838 repo.ui.warn as the reporter.
3839
3839
3840 Otherwise, it will yield repo.ui.note.
3840 Otherwise, it will yield repo.ui.note.
3841 '''
3841 '''
3842 msg, warning = howtocontinue(repo)
3842 msg, warning = howtocontinue(repo)
3843 if msg is not None:
3843 if msg is not None:
3844 if warning:
3844 if warning:
3845 repo.ui.warn("%s\n" % msg)
3845 repo.ui.warn("%s\n" % msg)
3846 else:
3846 else:
3847 repo.ui.note("%s\n" % msg)
3847 repo.ui.note("%s\n" % msg)
3848
3848
3849 def wrongtooltocontinue(repo, task):
3849 def wrongtooltocontinue(repo, task):
3850 '''Raise an abort suggesting how to properly continue if there is an
3850 '''Raise an abort suggesting how to properly continue if there is an
3851 active task.
3851 active task.
3852
3852
3853 Uses howtocontinue() to find the active task.
3853 Uses howtocontinue() to find the active task.
3854
3854
3855 If there's no task (repo.ui.note for 'hg commit'), it does not offer
3855 If there's no task (repo.ui.note for 'hg commit'), it does not offer
3856 a hint.
3856 a hint.
3857 '''
3857 '''
3858 after = howtocontinue(repo)
3858 after = howtocontinue(repo)
3859 hint = None
3859 hint = None
3860 if after[1]:
3860 if after[1]:
3861 hint = after[0]
3861 hint = after[0]
3862 raise error.Abort(_('no %s in progress') % task, hint=hint)
3862 raise error.Abort(_('no %s in progress') % task, hint=hint)
@@ -1,1506 +1,1506 b''
1 $ cat >> $HGRCPATH << EOF
1 $ cat >> $HGRCPATH << EOF
2 > [phases]
2 > [phases]
3 > # public changeset are not obsolete
3 > # public changeset are not obsolete
4 > publish=false
4 > publish=false
5 > [ui]
5 > [ui]
6 > logtemplate="{rev}:{node|short} ({phase}{if(obsolete, ' *{obsolete}*')}{if(instabilities, ' {instabilities}')}) [{tags} {bookmarks}] {desc|firstline}\n"
6 > logtemplate="{rev}:{node|short} ({phase}{if(obsolete, ' *{obsolete}*')}{if(instabilities, ' {instabilities}')}) [{tags} {bookmarks}] {desc|firstline}\n"
7 > EOF
7 > EOF
8 $ mkcommit() {
8 $ mkcommit() {
9 > echo "$1" > "$1"
9 > echo "$1" > "$1"
10 > hg add "$1"
10 > hg add "$1"
11 > hg ci -m "add $1"
11 > hg ci -m "add $1"
12 > }
12 > }
13 $ getid() {
13 $ getid() {
14 > hg log -T "{node}\n" --hidden -r "desc('$1')"
14 > hg log -T "{node}\n" --hidden -r "desc('$1')"
15 > }
15 > }
16
16
17 $ cat > debugkeys.py <<EOF
17 $ cat > debugkeys.py <<EOF
18 > def reposetup(ui, repo):
18 > def reposetup(ui, repo):
19 > class debugkeysrepo(repo.__class__):
19 > class debugkeysrepo(repo.__class__):
20 > def listkeys(self, namespace):
20 > def listkeys(self, namespace):
21 > ui.write('listkeys %s\n' % (namespace,))
21 > ui.write('listkeys %s\n' % (namespace,))
22 > return super(debugkeysrepo, self).listkeys(namespace)
22 > return super(debugkeysrepo, self).listkeys(namespace)
23 >
23 >
24 > if repo.local():
24 > if repo.local():
25 > repo.__class__ = debugkeysrepo
25 > repo.__class__ = debugkeysrepo
26 > EOF
26 > EOF
27
27
28 $ hg init tmpa
28 $ hg init tmpa
29 $ cd tmpa
29 $ cd tmpa
30 $ mkcommit kill_me
30 $ mkcommit kill_me
31
31
32 Checking that the feature is properly disabled
32 Checking that the feature is properly disabled
33
33
34 $ hg debugobsolete -d '0 0' `getid kill_me` -u babar
34 $ hg debugobsolete -d '0 0' `getid kill_me` -u babar
35 abort: creating obsolete markers is not enabled on this repo
35 abort: creating obsolete markers is not enabled on this repo
36 [255]
36 [255]
37
37
38 Enabling it
38 Enabling it
39
39
40 $ cat >> $HGRCPATH << EOF
40 $ cat >> $HGRCPATH << EOF
41 > [experimental]
41 > [experimental]
42 > stabilization=createmarkers,exchange
42 > stabilization=createmarkers,exchange
43 > EOF
43 > EOF
44
44
45 Killing a single changeset without replacement
45 Killing a single changeset without replacement
46
46
47 $ hg debugobsolete 0
47 $ hg debugobsolete 0
48 abort: changeset references must be full hexadecimal node identifiers
48 abort: changeset references must be full hexadecimal node identifiers
49 [255]
49 [255]
50 $ hg debugobsolete '00'
50 $ hg debugobsolete '00'
51 abort: changeset references must be full hexadecimal node identifiers
51 abort: changeset references must be full hexadecimal node identifiers
52 [255]
52 [255]
53 $ hg debugobsolete -d '0 0' `getid kill_me` -u babar
53 $ hg debugobsolete -d '0 0' `getid kill_me` -u babar
54 obsoleted 1 changesets
54 obsoleted 1 changesets
55 $ hg debugobsolete
55 $ hg debugobsolete
56 97b7c2d76b1845ed3eb988cd612611e72406cef0 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'babar'}
56 97b7c2d76b1845ed3eb988cd612611e72406cef0 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'babar'}
57
57
58 (test that mercurial is not confused)
58 (test that mercurial is not confused)
59
59
60 $ hg up null --quiet # having 0 as parent prevents it to be hidden
60 $ hg up null --quiet # having 0 as parent prevents it to be hidden
61 $ hg tip
61 $ hg tip
62 -1:000000000000 (public) [tip ]
62 -1:000000000000 (public) [tip ]
63 $ hg up --hidden tip --quiet
63 $ hg up --hidden tip --quiet
64
64
65 Killing a single changeset with itself should fail
65 Killing a single changeset with itself should fail
66 (simple local safeguard)
66 (simple local safeguard)
67
67
68 $ hg debugobsolete `getid kill_me` `getid kill_me`
68 $ hg debugobsolete `getid kill_me` `getid kill_me`
69 abort: bad obsmarker input: in-marker cycle with 97b7c2d76b1845ed3eb988cd612611e72406cef0
69 abort: bad obsmarker input: in-marker cycle with 97b7c2d76b1845ed3eb988cd612611e72406cef0
70 [255]
70 [255]
71
71
72 $ cd ..
72 $ cd ..
73
73
74 Killing a single changeset with replacement
74 Killing a single changeset with replacement
75 (and testing the format option)
75 (and testing the format option)
76
76
77 $ hg init tmpb
77 $ hg init tmpb
78 $ cd tmpb
78 $ cd tmpb
79 $ mkcommit a
79 $ mkcommit a
80 $ mkcommit b
80 $ mkcommit b
81 $ mkcommit original_c
81 $ mkcommit original_c
82 $ hg up "desc('b')"
82 $ hg up "desc('b')"
83 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
83 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
84 $ mkcommit new_c
84 $ mkcommit new_c
85 created new head
85 created new head
86 $ hg log -r 'hidden()' --template '{rev}:{node|short} {desc}\n' --hidden
86 $ hg log -r 'hidden()' --template '{rev}:{node|short} {desc}\n' --hidden
87 $ hg debugobsolete --config format.obsstore-version=0 --flag 12 `getid original_c` `getid new_c` -d '121 120'
87 $ hg debugobsolete --config format.obsstore-version=0 --flag 12 `getid original_c` `getid new_c` -d '121 120'
88 obsoleted 1 changesets
88 obsoleted 1 changesets
89 $ hg log -r 'hidden()' --template '{rev}:{node|short} {desc}\n' --hidden
89 $ hg log -r 'hidden()' --template '{rev}:{node|short} {desc}\n' --hidden
90 2:245bde4270cd add original_c
90 2:245bde4270cd add original_c
91 $ hg debugrevlog -cd
91 $ hg debugrevlog -cd
92 # rev p1rev p2rev start end deltastart base p1 p2 rawsize totalsize compression heads chainlen
92 # rev p1rev p2rev start end deltastart base p1 p2 rawsize totalsize compression heads chainlen
93 0 -1 -1 0 59 0 0 0 0 58 58 0 1 0
93 0 -1 -1 0 59 0 0 0 0 58 58 0 1 0
94 1 0 -1 59 118 59 59 0 0 58 116 0 1 0
94 1 0 -1 59 118 59 59 0 0 58 116 0 1 0
95 2 1 -1 118 193 118 118 59 0 76 192 0 1 0
95 2 1 -1 118 193 118 118 59 0 76 192 0 1 0
96 3 1 -1 193 260 193 193 59 0 66 258 0 2 0
96 3 1 -1 193 260 193 193 59 0 66 258 0 2 0
97 $ hg debugobsolete
97 $ hg debugobsolete
98 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
98 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
99
99
100 (check for version number of the obsstore)
100 (check for version number of the obsstore)
101
101
102 $ dd bs=1 count=1 if=.hg/store/obsstore 2>/dev/null
102 $ dd bs=1 count=1 if=.hg/store/obsstore 2>/dev/null
103 \x00 (no-eol) (esc)
103 \x00 (no-eol) (esc)
104
104
105 do it again (it read the obsstore before adding new changeset)
105 do it again (it read the obsstore before adding new changeset)
106
106
107 $ hg up '.^'
107 $ hg up '.^'
108 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
108 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
109 $ mkcommit new_2_c
109 $ mkcommit new_2_c
110 created new head
110 created new head
111 $ hg debugobsolete -d '1337 0' `getid new_c` `getid new_2_c`
111 $ hg debugobsolete -d '1337 0' `getid new_c` `getid new_2_c`
112 obsoleted 1 changesets
112 obsoleted 1 changesets
113 $ hg debugobsolete
113 $ hg debugobsolete
114 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
114 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
115 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
115 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
116
116
117 Register two markers with a missing node
117 Register two markers with a missing node
118
118
119 $ hg up '.^'
119 $ hg up '.^'
120 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
120 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
121 $ mkcommit new_3_c
121 $ mkcommit new_3_c
122 created new head
122 created new head
123 $ hg debugobsolete -d '1338 0' `getid new_2_c` 1337133713371337133713371337133713371337
123 $ hg debugobsolete -d '1338 0' `getid new_2_c` 1337133713371337133713371337133713371337
124 obsoleted 1 changesets
124 obsoleted 1 changesets
125 $ hg debugobsolete -d '1339 0' 1337133713371337133713371337133713371337 `getid new_3_c`
125 $ hg debugobsolete -d '1339 0' 1337133713371337133713371337133713371337 `getid new_3_c`
126 $ hg debugobsolete
126 $ hg debugobsolete
127 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
127 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
128 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
128 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
129 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
129 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
130 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
130 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
131
131
132 Test the --index option of debugobsolete command
132 Test the --index option of debugobsolete command
133 $ hg debugobsolete --index
133 $ hg debugobsolete --index
134 0 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
134 0 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
135 1 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
135 1 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
136 2 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
136 2 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
137 3 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
137 3 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
138
138
139 Refuse pathological nullid successors
139 Refuse pathological nullid successors
140 $ hg debugobsolete -d '9001 0' 1337133713371337133713371337133713371337 0000000000000000000000000000000000000000
140 $ hg debugobsolete -d '9001 0' 1337133713371337133713371337133713371337 0000000000000000000000000000000000000000
141 transaction abort!
141 transaction abort!
142 rollback completed
142 rollback completed
143 abort: bad obsolescence marker detected: invalid successors nullid
143 abort: bad obsolescence marker detected: invalid successors nullid
144 [255]
144 [255]
145
145
146 Check that graphlog detect that a changeset is obsolete:
146 Check that graphlog detect that a changeset is obsolete:
147
147
148 $ hg log -G
148 $ hg log -G
149 @ 5:5601fb93a350 (draft) [tip ] add new_3_c
149 @ 5:5601fb93a350 (draft) [tip ] add new_3_c
150 |
150 |
151 o 1:7c3bad9141dc (draft) [ ] add b
151 o 1:7c3bad9141dc (draft) [ ] add b
152 |
152 |
153 o 0:1f0dee641bb7 (draft) [ ] add a
153 o 0:1f0dee641bb7 (draft) [ ] add a
154
154
155
155
156 check that heads does not report them
156 check that heads does not report them
157
157
158 $ hg heads
158 $ hg heads
159 5:5601fb93a350 (draft) [tip ] add new_3_c
159 5:5601fb93a350 (draft) [tip ] add new_3_c
160 $ hg heads --hidden
160 $ hg heads --hidden
161 5:5601fb93a350 (draft) [tip ] add new_3_c
161 5:5601fb93a350 (draft) [tip ] add new_3_c
162 4:ca819180edb9 (draft *obsolete*) [ ] add new_2_c
162 4:ca819180edb9 (draft *obsolete*) [ ] add new_2_c
163 3:cdbce2fbb163 (draft *obsolete*) [ ] add new_c
163 3:cdbce2fbb163 (draft *obsolete*) [ ] add new_c
164 2:245bde4270cd (draft *obsolete*) [ ] add original_c
164 2:245bde4270cd (draft *obsolete*) [ ] add original_c
165
165
166
166
167 check that summary does not report them
167 check that summary does not report them
168
168
169 $ hg init ../sink
169 $ hg init ../sink
170 $ echo '[paths]' >> .hg/hgrc
170 $ echo '[paths]' >> .hg/hgrc
171 $ echo 'default=../sink' >> .hg/hgrc
171 $ echo 'default=../sink' >> .hg/hgrc
172 $ hg summary --remote
172 $ hg summary --remote
173 parent: 5:5601fb93a350 tip
173 parent: 5:5601fb93a350 tip
174 add new_3_c
174 add new_3_c
175 branch: default
175 branch: default
176 commit: (clean)
176 commit: (clean)
177 update: (current)
177 update: (current)
178 phases: 3 draft
178 phases: 3 draft
179 remote: 3 outgoing
179 remote: 3 outgoing
180
180
181 $ hg summary --remote --hidden
181 $ hg summary --remote --hidden
182 parent: 5:5601fb93a350 tip
182 parent: 5:5601fb93a350 tip
183 add new_3_c
183 add new_3_c
184 branch: default
184 branch: default
185 commit: (clean)
185 commit: (clean)
186 update: 3 new changesets, 4 branch heads (merge)
186 update: 3 new changesets, 4 branch heads (merge)
187 phases: 6 draft
187 phases: 6 draft
188 remote: 3 outgoing
188 remote: 3 outgoing
189
189
190 check that various commands work well with filtering
190 check that various commands work well with filtering
191
191
192 $ hg tip
192 $ hg tip
193 5:5601fb93a350 (draft) [tip ] add new_3_c
193 5:5601fb93a350 (draft) [tip ] add new_3_c
194 $ hg log -r 6
194 $ hg log -r 6
195 abort: unknown revision '6'!
195 abort: unknown revision '6'!
196 [255]
196 [255]
197 $ hg log -r 4
197 $ hg log -r 4
198 abort: hidden revision '4'!
198 abort: hidden revision '4'!
199 (use --hidden to access hidden revisions)
199 (use --hidden to access hidden revisions)
200 [255]
200 [255]
201 $ hg debugrevspec 'rev(6)'
201 $ hg debugrevspec 'rev(6)'
202 $ hg debugrevspec 'rev(4)'
202 $ hg debugrevspec 'rev(4)'
203 $ hg debugrevspec 'null'
203 $ hg debugrevspec 'null'
204 -1
204 -1
205
205
206 Check that public changeset are not accounted as obsolete:
206 Check that public changeset are not accounted as obsolete:
207
207
208 $ hg --hidden phase --public 2
208 $ hg --hidden phase --public 2
209 $ hg log -G
209 $ hg log -G
210 @ 5:5601fb93a350 (draft phase-divergent) [tip ] add new_3_c
210 @ 5:5601fb93a350 (draft phase-divergent) [tip ] add new_3_c
211 |
211 |
212 | o 2:245bde4270cd (public) [ ] add original_c
212 | o 2:245bde4270cd (public) [ ] add original_c
213 |/
213 |/
214 o 1:7c3bad9141dc (public) [ ] add b
214 o 1:7c3bad9141dc (public) [ ] add b
215 |
215 |
216 o 0:1f0dee641bb7 (public) [ ] add a
216 o 0:1f0dee641bb7 (public) [ ] add a
217
217
218
218
219 And that bumped changeset are detected
219 And that bumped changeset are detected
220 --------------------------------------
220 --------------------------------------
221
221
222 If we didn't filtered obsolete changesets out, 3 and 4 would show up too. Also
222 If we didn't filtered obsolete changesets out, 3 and 4 would show up too. Also
223 note that the bumped changeset (5:5601fb93a350) is not a direct successor of
223 note that the bumped changeset (5:5601fb93a350) is not a direct successor of
224 the public changeset
224 the public changeset
225
225
226 $ hg log --hidden -r 'phasedivergent()'
226 $ hg log --hidden -r 'phasedivergent()'
227 5:5601fb93a350 (draft phase-divergent) [tip ] add new_3_c
227 5:5601fb93a350 (draft phase-divergent) [tip ] add new_3_c
228
228
229 And that we can't push bumped changeset
229 And that we can't push bumped changeset
230
230
231 $ hg push ../tmpa -r 0 --force #(make repo related)
231 $ hg push ../tmpa -r 0 --force #(make repo related)
232 pushing to ../tmpa
232 pushing to ../tmpa
233 searching for changes
233 searching for changes
234 warning: repository is unrelated
234 warning: repository is unrelated
235 adding changesets
235 adding changesets
236 adding manifests
236 adding manifests
237 adding file changes
237 adding file changes
238 added 1 changesets with 1 changes to 1 files (+1 heads)
238 added 1 changesets with 1 changes to 1 files (+1 heads)
239 $ hg push ../tmpa
239 $ hg push ../tmpa
240 pushing to ../tmpa
240 pushing to ../tmpa
241 searching for changes
241 searching for changes
242 abort: push includes phase-divergent changeset: 5601fb93a350!
242 abort: push includes phase-divergent changeset: 5601fb93a350!
243 [255]
243 [255]
244
244
245 Fixing "bumped" situation
245 Fixing "bumped" situation
246 We need to create a clone of 5 and add a special marker with a flag
246 We need to create a clone of 5 and add a special marker with a flag
247
247
248 $ hg summary
248 $ hg summary
249 parent: 5:5601fb93a350 tip (phase-divergent)
249 parent: 5:5601fb93a350 tip (phase-divergent)
250 add new_3_c
250 add new_3_c
251 branch: default
251 branch: default
252 commit: (clean)
252 commit: (clean)
253 update: 1 new changesets, 2 branch heads (merge)
253 update: 1 new changesets, 2 branch heads (merge)
254 phases: 1 draft
254 phases: 1 draft
255 phase-divergent: 1 changesets
255 phase-divergent: 1 changesets
256 $ hg up '5^'
256 $ hg up '5^'
257 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
257 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
258 $ hg revert -ar 5
258 $ hg revert -ar 5
259 adding new_3_c
259 adding new_3_c
260 $ hg ci -m 'add n3w_3_c'
260 $ hg ci -m 'add n3w_3_c'
261 created new head
261 created new head
262 $ hg debugobsolete -d '1338 0' --flags 1 `getid new_3_c` `getid n3w_3_c`
262 $ hg debugobsolete -d '1338 0' --flags 1 `getid new_3_c` `getid n3w_3_c`
263 obsoleted 1 changesets
263 obsoleted 1 changesets
264 $ hg log -r 'phasedivergent()'
264 $ hg log -r 'phasedivergent()'
265 $ hg log -G
265 $ hg log -G
266 @ 6:6f9641995072 (draft) [tip ] add n3w_3_c
266 @ 6:6f9641995072 (draft) [tip ] add n3w_3_c
267 |
267 |
268 | o 2:245bde4270cd (public) [ ] add original_c
268 | o 2:245bde4270cd (public) [ ] add original_c
269 |/
269 |/
270 o 1:7c3bad9141dc (public) [ ] add b
270 o 1:7c3bad9141dc (public) [ ] add b
271 |
271 |
272 o 0:1f0dee641bb7 (public) [ ] add a
272 o 0:1f0dee641bb7 (public) [ ] add a
273
273
274
274
275 Basic exclusive testing
275 Basic exclusive testing
276
276
277 $ hg log -G --hidden
277 $ hg log -G --hidden
278 @ 6:6f9641995072 (draft) [tip ] add n3w_3_c
278 @ 6:6f9641995072 (draft) [tip ] add n3w_3_c
279 |
279 |
280 | x 5:5601fb93a350 (draft *obsolete*) [ ] add new_3_c
280 | x 5:5601fb93a350 (draft *obsolete*) [ ] add new_3_c
281 |/
281 |/
282 | x 4:ca819180edb9 (draft *obsolete*) [ ] add new_2_c
282 | x 4:ca819180edb9 (draft *obsolete*) [ ] add new_2_c
283 |/
283 |/
284 | x 3:cdbce2fbb163 (draft *obsolete*) [ ] add new_c
284 | x 3:cdbce2fbb163 (draft *obsolete*) [ ] add new_c
285 |/
285 |/
286 | o 2:245bde4270cd (public) [ ] add original_c
286 | o 2:245bde4270cd (public) [ ] add original_c
287 |/
287 |/
288 o 1:7c3bad9141dc (public) [ ] add b
288 o 1:7c3bad9141dc (public) [ ] add b
289 |
289 |
290 o 0:1f0dee641bb7 (public) [ ] add a
290 o 0:1f0dee641bb7 (public) [ ] add a
291
291
292 $ hg debugobsolete --rev 6f9641995072
292 $ hg debugobsolete --rev 6f9641995072
293 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
293 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
294 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
294 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
295 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
295 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
296 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
296 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
297 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
297 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
298 $ hg debugobsolete --rev 6f9641995072 --exclusive
298 $ hg debugobsolete --rev 6f9641995072 --exclusive
299 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
299 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
300 $ hg debugobsolete --rev 5601fb93a350 --hidden
300 $ hg debugobsolete --rev 5601fb93a350 --hidden
301 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
301 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
302 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
302 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
303 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
303 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
304 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
304 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
305 $ hg debugobsolete --rev 5601fb93a350 --hidden --exclusive
305 $ hg debugobsolete --rev 5601fb93a350 --hidden --exclusive
306 $ hg debugobsolete --rev 5601fb93a350+6f9641995072 --hidden --exclusive
306 $ hg debugobsolete --rev 5601fb93a350+6f9641995072 --hidden --exclusive
307 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
307 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
308 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
308 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
309 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
309 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
310
310
311 $ cd ..
311 $ cd ..
312
312
313 Revision 0 is hidden
313 Revision 0 is hidden
314 --------------------
314 --------------------
315
315
316 $ hg init rev0hidden
316 $ hg init rev0hidden
317 $ cd rev0hidden
317 $ cd rev0hidden
318
318
319 $ mkcommit kill0
319 $ mkcommit kill0
320 $ hg up -q null
320 $ hg up -q null
321 $ hg debugobsolete `getid kill0`
321 $ hg debugobsolete `getid kill0`
322 obsoleted 1 changesets
322 obsoleted 1 changesets
323 $ mkcommit a
323 $ mkcommit a
324 $ mkcommit b
324 $ mkcommit b
325
325
326 Should pick the first visible revision as "repo" node
326 Should pick the first visible revision as "repo" node
327
327
328 $ hg archive ../archive-null
328 $ hg archive ../archive-null
329 $ cat ../archive-null/.hg_archival.txt
329 $ cat ../archive-null/.hg_archival.txt
330 repo: 1f0dee641bb7258c56bd60e93edfa2405381c41e
330 repo: 1f0dee641bb7258c56bd60e93edfa2405381c41e
331 node: 7c3bad9141dcb46ff89abf5f61856facd56e476c
331 node: 7c3bad9141dcb46ff89abf5f61856facd56e476c
332 branch: default
332 branch: default
333 latesttag: null
333 latesttag: null
334 latesttagdistance: 2
334 latesttagdistance: 2
335 changessincelatesttag: 2
335 changessincelatesttag: 2
336
336
337
337
338 $ cd ..
338 $ cd ..
339
339
340 Exchange Test
340 Exchange Test
341 ============================
341 ============================
342
342
343 Destination repo does not have any data
343 Destination repo does not have any data
344 ---------------------------------------
344 ---------------------------------------
345
345
346 Simple incoming test
346 Simple incoming test
347
347
348 $ hg init tmpc
348 $ hg init tmpc
349 $ cd tmpc
349 $ cd tmpc
350 $ hg incoming ../tmpb
350 $ hg incoming ../tmpb
351 comparing with ../tmpb
351 comparing with ../tmpb
352 0:1f0dee641bb7 (public) [ ] add a
352 0:1f0dee641bb7 (public) [ ] add a
353 1:7c3bad9141dc (public) [ ] add b
353 1:7c3bad9141dc (public) [ ] add b
354 2:245bde4270cd (public) [ ] add original_c
354 2:245bde4270cd (public) [ ] add original_c
355 6:6f9641995072 (draft) [tip ] add n3w_3_c
355 6:6f9641995072 (draft) [tip ] add n3w_3_c
356
356
357 Try to pull markers
357 Try to pull markers
358 (extinct changeset are excluded but marker are pushed)
358 (extinct changeset are excluded but marker are pushed)
359
359
360 $ hg pull ../tmpb
360 $ hg pull ../tmpb
361 pulling from ../tmpb
361 pulling from ../tmpb
362 requesting all changes
362 requesting all changes
363 adding changesets
363 adding changesets
364 adding manifests
364 adding manifests
365 adding file changes
365 adding file changes
366 added 4 changesets with 4 changes to 4 files (+1 heads)
366 added 4 changesets with 4 changes to 4 files (+1 heads)
367 5 new obsolescence markers
367 5 new obsolescence markers
368 (run 'hg heads' to see heads, 'hg merge' to merge)
368 (run 'hg heads' to see heads, 'hg merge' to merge)
369 $ hg debugobsolete
369 $ hg debugobsolete
370 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
370 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
371 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
371 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
372 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
372 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
373 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
373 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
374 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
374 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
375
375
376 Rollback//Transaction support
376 Rollback//Transaction support
377
377
378 $ hg debugobsolete -d '1340 0' aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
378 $ hg debugobsolete -d '1340 0' aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
379 $ hg debugobsolete
379 $ hg debugobsolete
380 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
380 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
381 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
381 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
382 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
382 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
383 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
383 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
384 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
384 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
385 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 0 (Thu Jan 01 00:22:20 1970 +0000) {'user': 'test'}
385 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 0 (Thu Jan 01 00:22:20 1970 +0000) {'user': 'test'}
386 $ hg rollback -n
386 $ hg rollback -n
387 repository tip rolled back to revision 3 (undo debugobsolete)
387 repository tip rolled back to revision 3 (undo debugobsolete)
388 $ hg rollback
388 $ hg rollback
389 repository tip rolled back to revision 3 (undo debugobsolete)
389 repository tip rolled back to revision 3 (undo debugobsolete)
390 $ hg debugobsolete
390 $ hg debugobsolete
391 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
391 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
392 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
392 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
393 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
393 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
394 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
394 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
395 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
395 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
396
396
397 $ cd ..
397 $ cd ..
398
398
399 Try to push markers
399 Try to push markers
400
400
401 $ hg init tmpd
401 $ hg init tmpd
402 $ hg -R tmpb push tmpd
402 $ hg -R tmpb push tmpd
403 pushing to tmpd
403 pushing to tmpd
404 searching for changes
404 searching for changes
405 adding changesets
405 adding changesets
406 adding manifests
406 adding manifests
407 adding file changes
407 adding file changes
408 added 4 changesets with 4 changes to 4 files (+1 heads)
408 added 4 changesets with 4 changes to 4 files (+1 heads)
409 5 new obsolescence markers
409 5 new obsolescence markers
410 $ hg -R tmpd debugobsolete | sort
410 $ hg -R tmpd debugobsolete | sort
411 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
411 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
412 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
412 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
413 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
413 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
414 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
414 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
415 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
415 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
416
416
417 Check obsolete keys are exchanged only if source has an obsolete store
417 Check obsolete keys are exchanged only if source has an obsolete store
418
418
419 $ hg init empty
419 $ hg init empty
420 $ hg --config extensions.debugkeys=debugkeys.py -R empty push tmpd
420 $ hg --config extensions.debugkeys=debugkeys.py -R empty push tmpd
421 pushing to tmpd
421 pushing to tmpd
422 listkeys phases
422 listkeys phases
423 listkeys bookmarks
423 listkeys bookmarks
424 no changes found
424 no changes found
425 listkeys phases
425 listkeys phases
426 [1]
426 [1]
427
427
428 clone support
428 clone support
429 (markers are copied and extinct changesets are included to allow hardlinks)
429 (markers are copied and extinct changesets are included to allow hardlinks)
430
430
431 $ hg clone tmpb clone-dest
431 $ hg clone tmpb clone-dest
432 updating to branch default
432 updating to branch default
433 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
433 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
434 $ hg -R clone-dest log -G --hidden
434 $ hg -R clone-dest log -G --hidden
435 @ 6:6f9641995072 (draft) [tip ] add n3w_3_c
435 @ 6:6f9641995072 (draft) [tip ] add n3w_3_c
436 |
436 |
437 | x 5:5601fb93a350 (draft *obsolete*) [ ] add new_3_c
437 | x 5:5601fb93a350 (draft *obsolete*) [ ] add new_3_c
438 |/
438 |/
439 | x 4:ca819180edb9 (draft *obsolete*) [ ] add new_2_c
439 | x 4:ca819180edb9 (draft *obsolete*) [ ] add new_2_c
440 |/
440 |/
441 | x 3:cdbce2fbb163 (draft *obsolete*) [ ] add new_c
441 | x 3:cdbce2fbb163 (draft *obsolete*) [ ] add new_c
442 |/
442 |/
443 | o 2:245bde4270cd (public) [ ] add original_c
443 | o 2:245bde4270cd (public) [ ] add original_c
444 |/
444 |/
445 o 1:7c3bad9141dc (public) [ ] add b
445 o 1:7c3bad9141dc (public) [ ] add b
446 |
446 |
447 o 0:1f0dee641bb7 (public) [ ] add a
447 o 0:1f0dee641bb7 (public) [ ] add a
448
448
449 $ hg -R clone-dest debugobsolete
449 $ hg -R clone-dest debugobsolete
450 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
450 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
451 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
451 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
452 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
452 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
453 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
453 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
454 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
454 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
455
455
456
456
457 Destination repo have existing data
457 Destination repo have existing data
458 ---------------------------------------
458 ---------------------------------------
459
459
460 On pull
460 On pull
461
461
462 $ hg init tmpe
462 $ hg init tmpe
463 $ cd tmpe
463 $ cd tmpe
464 $ hg debugobsolete -d '1339 0' 1339133913391339133913391339133913391339 ca819180edb99ed25ceafb3e9584ac287e240b00
464 $ hg debugobsolete -d '1339 0' 1339133913391339133913391339133913391339 ca819180edb99ed25ceafb3e9584ac287e240b00
465 $ hg pull ../tmpb
465 $ hg pull ../tmpb
466 pulling from ../tmpb
466 pulling from ../tmpb
467 requesting all changes
467 requesting all changes
468 adding changesets
468 adding changesets
469 adding manifests
469 adding manifests
470 adding file changes
470 adding file changes
471 added 4 changesets with 4 changes to 4 files (+1 heads)
471 added 4 changesets with 4 changes to 4 files (+1 heads)
472 5 new obsolescence markers
472 5 new obsolescence markers
473 (run 'hg heads' to see heads, 'hg merge' to merge)
473 (run 'hg heads' to see heads, 'hg merge' to merge)
474 $ hg debugobsolete
474 $ hg debugobsolete
475 1339133913391339133913391339133913391339 ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
475 1339133913391339133913391339133913391339 ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
476 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
476 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
477 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
477 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
478 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
478 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
479 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
479 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
480 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
480 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
481
481
482
482
483 On push
483 On push
484
484
485 $ hg push ../tmpc
485 $ hg push ../tmpc
486 pushing to ../tmpc
486 pushing to ../tmpc
487 searching for changes
487 searching for changes
488 no changes found
488 no changes found
489 1 new obsolescence markers
489 1 new obsolescence markers
490 [1]
490 [1]
491 $ hg -R ../tmpc debugobsolete
491 $ hg -R ../tmpc debugobsolete
492 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
492 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
493 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
493 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
494 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
494 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
495 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
495 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
496 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
496 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
497 1339133913391339133913391339133913391339 ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
497 1339133913391339133913391339133913391339 ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
498
498
499 detect outgoing obsolete and unstable
499 detect outgoing obsolete and unstable
500 ---------------------------------------
500 ---------------------------------------
501
501
502
502
503 $ hg log -G
503 $ hg log -G
504 o 3:6f9641995072 (draft) [tip ] add n3w_3_c
504 o 3:6f9641995072 (draft) [tip ] add n3w_3_c
505 |
505 |
506 | o 2:245bde4270cd (public) [ ] add original_c
506 | o 2:245bde4270cd (public) [ ] add original_c
507 |/
507 |/
508 o 1:7c3bad9141dc (public) [ ] add b
508 o 1:7c3bad9141dc (public) [ ] add b
509 |
509 |
510 o 0:1f0dee641bb7 (public) [ ] add a
510 o 0:1f0dee641bb7 (public) [ ] add a
511
511
512 $ hg up 'desc("n3w_3_c")'
512 $ hg up 'desc("n3w_3_c")'
513 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
513 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
514 $ mkcommit original_d
514 $ mkcommit original_d
515 $ mkcommit original_e
515 $ mkcommit original_e
516 $ hg debugobsolete --record-parents `getid original_d` -d '0 0'
516 $ hg debugobsolete --record-parents `getid original_d` -d '0 0'
517 obsoleted 1 changesets
517 obsoleted 1 changesets
518 $ hg debugobsolete | grep `getid original_d`
518 $ hg debugobsolete | grep `getid original_d`
519 94b33453f93bdb8d457ef9b770851a618bf413e1 0 {6f96419950729f3671185b847352890f074f7557} (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
519 94b33453f93bdb8d457ef9b770851a618bf413e1 0 {6f96419950729f3671185b847352890f074f7557} (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
520 $ hg log -r 'obsolete()'
520 $ hg log -r 'obsolete()'
521 4:94b33453f93b (draft *obsolete*) [ ] add original_d
521 4:94b33453f93b (draft *obsolete*) [ ] add original_d
522 $ hg summary
522 $ hg summary
523 parent: 5:cda648ca50f5 tip (orphan)
523 parent: 5:cda648ca50f5 tip (orphan)
524 add original_e
524 add original_e
525 branch: default
525 branch: default
526 commit: (clean)
526 commit: (clean)
527 update: 1 new changesets, 2 branch heads (merge)
527 update: 1 new changesets, 2 branch heads (merge)
528 phases: 3 draft
528 phases: 3 draft
529 orphan: 1 changesets
529 orphan: 1 changesets
530 $ hg log -G -r '::orphan()'
530 $ hg log -G -r '::orphan()'
531 @ 5:cda648ca50f5 (draft orphan) [tip ] add original_e
531 @ 5:cda648ca50f5 (draft orphan) [tip ] add original_e
532 |
532 |
533 x 4:94b33453f93b (draft *obsolete*) [ ] add original_d
533 x 4:94b33453f93b (draft *obsolete*) [ ] add original_d
534 |
534 |
535 o 3:6f9641995072 (draft) [ ] add n3w_3_c
535 o 3:6f9641995072 (draft) [ ] add n3w_3_c
536 |
536 |
537 o 1:7c3bad9141dc (public) [ ] add b
537 o 1:7c3bad9141dc (public) [ ] add b
538 |
538 |
539 o 0:1f0dee641bb7 (public) [ ] add a
539 o 0:1f0dee641bb7 (public) [ ] add a
540
540
541
541
542 refuse to push obsolete changeset
542 refuse to push obsolete changeset
543
543
544 $ hg push ../tmpc/ -r 'desc("original_d")'
544 $ hg push ../tmpc/ -r 'desc("original_d")'
545 pushing to ../tmpc/
545 pushing to ../tmpc/
546 searching for changes
546 searching for changes
547 abort: push includes obsolete changeset: 94b33453f93b!
547 abort: push includes obsolete changeset: 94b33453f93b!
548 [255]
548 [255]
549
549
550 refuse to push unstable changeset
550 refuse to push unstable changeset
551
551
552 $ hg push ../tmpc/
552 $ hg push ../tmpc/
553 pushing to ../tmpc/
553 pushing to ../tmpc/
554 searching for changes
554 searching for changes
555 abort: push includes orphan changeset: cda648ca50f5!
555 abort: push includes orphan changeset: cda648ca50f5!
556 [255]
556 [255]
557
557
558 Test that extinct changeset are properly detected
558 Test that extinct changeset are properly detected
559
559
560 $ hg log -r 'extinct()'
560 $ hg log -r 'extinct()'
561
561
562 Don't try to push extinct changeset
562 Don't try to push extinct changeset
563
563
564 $ hg init ../tmpf
564 $ hg init ../tmpf
565 $ hg out ../tmpf
565 $ hg out ../tmpf
566 comparing with ../tmpf
566 comparing with ../tmpf
567 searching for changes
567 searching for changes
568 0:1f0dee641bb7 (public) [ ] add a
568 0:1f0dee641bb7 (public) [ ] add a
569 1:7c3bad9141dc (public) [ ] add b
569 1:7c3bad9141dc (public) [ ] add b
570 2:245bde4270cd (public) [ ] add original_c
570 2:245bde4270cd (public) [ ] add original_c
571 3:6f9641995072 (draft) [ ] add n3w_3_c
571 3:6f9641995072 (draft) [ ] add n3w_3_c
572 4:94b33453f93b (draft *obsolete*) [ ] add original_d
572 4:94b33453f93b (draft *obsolete*) [ ] add original_d
573 5:cda648ca50f5 (draft orphan) [tip ] add original_e
573 5:cda648ca50f5 (draft orphan) [tip ] add original_e
574 $ hg push ../tmpf -f # -f because be push unstable too
574 $ hg push ../tmpf -f # -f because be push unstable too
575 pushing to ../tmpf
575 pushing to ../tmpf
576 searching for changes
576 searching for changes
577 adding changesets
577 adding changesets
578 adding manifests
578 adding manifests
579 adding file changes
579 adding file changes
580 added 6 changesets with 6 changes to 6 files (+1 heads)
580 added 6 changesets with 6 changes to 6 files (+1 heads)
581 7 new obsolescence markers
581 7 new obsolescence markers
582
582
583 no warning displayed
583 no warning displayed
584
584
585 $ hg push ../tmpf
585 $ hg push ../tmpf
586 pushing to ../tmpf
586 pushing to ../tmpf
587 searching for changes
587 searching for changes
588 no changes found
588 no changes found
589 [1]
589 [1]
590
590
591 Do not warn about new head when the new head is a successors of a remote one
591 Do not warn about new head when the new head is a successors of a remote one
592
592
593 $ hg log -G
593 $ hg log -G
594 @ 5:cda648ca50f5 (draft orphan) [tip ] add original_e
594 @ 5:cda648ca50f5 (draft orphan) [tip ] add original_e
595 |
595 |
596 x 4:94b33453f93b (draft *obsolete*) [ ] add original_d
596 x 4:94b33453f93b (draft *obsolete*) [ ] add original_d
597 |
597 |
598 o 3:6f9641995072 (draft) [ ] add n3w_3_c
598 o 3:6f9641995072 (draft) [ ] add n3w_3_c
599 |
599 |
600 | o 2:245bde4270cd (public) [ ] add original_c
600 | o 2:245bde4270cd (public) [ ] add original_c
601 |/
601 |/
602 o 1:7c3bad9141dc (public) [ ] add b
602 o 1:7c3bad9141dc (public) [ ] add b
603 |
603 |
604 o 0:1f0dee641bb7 (public) [ ] add a
604 o 0:1f0dee641bb7 (public) [ ] add a
605
605
606 $ hg up -q 'desc(n3w_3_c)'
606 $ hg up -q 'desc(n3w_3_c)'
607 $ mkcommit obsolete_e
607 $ mkcommit obsolete_e
608 created new head
608 created new head
609 $ hg debugobsolete `getid 'original_e'` `getid 'obsolete_e'` \
609 $ hg debugobsolete `getid 'original_e'` `getid 'obsolete_e'` \
610 > -u 'test <test@example.net>'
610 > -u 'test <test@example.net>'
611 obsoleted 1 changesets
611 obsoleted 1 changesets
612 $ hg outgoing ../tmpf # parasite hg outgoing testin
612 $ hg outgoing ../tmpf # parasite hg outgoing testin
613 comparing with ../tmpf
613 comparing with ../tmpf
614 searching for changes
614 searching for changes
615 6:3de5eca88c00 (draft) [tip ] add obsolete_e
615 6:3de5eca88c00 (draft) [tip ] add obsolete_e
616 $ hg push ../tmpf
616 $ hg push ../tmpf
617 pushing to ../tmpf
617 pushing to ../tmpf
618 searching for changes
618 searching for changes
619 adding changesets
619 adding changesets
620 adding manifests
620 adding manifests
621 adding file changes
621 adding file changes
622 added 1 changesets with 1 changes to 1 files (+1 heads)
622 added 1 changesets with 1 changes to 1 files (+1 heads)
623 1 new obsolescence markers
623 1 new obsolescence markers
624 obsoleted 1 changesets
624 obsoleted 1 changesets
625
625
626 test relevance computation
626 test relevance computation
627 ---------------------------------------
627 ---------------------------------------
628
628
629 Checking simple case of "marker relevance".
629 Checking simple case of "marker relevance".
630
630
631
631
632 Reminder of the repo situation
632 Reminder of the repo situation
633
633
634 $ hg log --hidden --graph
634 $ hg log --hidden --graph
635 @ 6:3de5eca88c00 (draft) [tip ] add obsolete_e
635 @ 6:3de5eca88c00 (draft) [tip ] add obsolete_e
636 |
636 |
637 | x 5:cda648ca50f5 (draft *obsolete*) [ ] add original_e
637 | x 5:cda648ca50f5 (draft *obsolete*) [ ] add original_e
638 | |
638 | |
639 | x 4:94b33453f93b (draft *obsolete*) [ ] add original_d
639 | x 4:94b33453f93b (draft *obsolete*) [ ] add original_d
640 |/
640 |/
641 o 3:6f9641995072 (draft) [ ] add n3w_3_c
641 o 3:6f9641995072 (draft) [ ] add n3w_3_c
642 |
642 |
643 | o 2:245bde4270cd (public) [ ] add original_c
643 | o 2:245bde4270cd (public) [ ] add original_c
644 |/
644 |/
645 o 1:7c3bad9141dc (public) [ ] add b
645 o 1:7c3bad9141dc (public) [ ] add b
646 |
646 |
647 o 0:1f0dee641bb7 (public) [ ] add a
647 o 0:1f0dee641bb7 (public) [ ] add a
648
648
649
649
650 List of all markers
650 List of all markers
651
651
652 $ hg debugobsolete
652 $ hg debugobsolete
653 1339133913391339133913391339133913391339 ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
653 1339133913391339133913391339133913391339 ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
654 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
654 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
655 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
655 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
656 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
656 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
657 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
657 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
658 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
658 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
659 94b33453f93bdb8d457ef9b770851a618bf413e1 0 {6f96419950729f3671185b847352890f074f7557} (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
659 94b33453f93bdb8d457ef9b770851a618bf413e1 0 {6f96419950729f3671185b847352890f074f7557} (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
660 cda648ca50f50482b7055c0b0c4c117bba6733d9 3de5eca88c00aa039da7399a220f4a5221faa585 0 (*) {'user': 'test <test@example.net>'} (glob)
660 cda648ca50f50482b7055c0b0c4c117bba6733d9 3de5eca88c00aa039da7399a220f4a5221faa585 0 (*) {'user': 'test <test@example.net>'} (glob)
661
661
662 List of changesets with no chain
662 List of changesets with no chain
663
663
664 $ hg debugobsolete --hidden --rev ::2
664 $ hg debugobsolete --hidden --rev ::2
665
665
666 List of changesets that are included on marker chain
666 List of changesets that are included on marker chain
667
667
668 $ hg debugobsolete --hidden --rev 6
668 $ hg debugobsolete --hidden --rev 6
669 cda648ca50f50482b7055c0b0c4c117bba6733d9 3de5eca88c00aa039da7399a220f4a5221faa585 0 (*) {'user': 'test <test@example.net>'} (glob)
669 cda648ca50f50482b7055c0b0c4c117bba6733d9 3de5eca88c00aa039da7399a220f4a5221faa585 0 (*) {'user': 'test <test@example.net>'} (glob)
670
670
671 List of changesets with a longer chain, (including a pruned children)
671 List of changesets with a longer chain, (including a pruned children)
672
672
673 $ hg debugobsolete --hidden --rev 3
673 $ hg debugobsolete --hidden --rev 3
674 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
674 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
675 1339133913391339133913391339133913391339 ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
675 1339133913391339133913391339133913391339 ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
676 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
676 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
677 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
677 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
678 94b33453f93bdb8d457ef9b770851a618bf413e1 0 {6f96419950729f3671185b847352890f074f7557} (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
678 94b33453f93bdb8d457ef9b770851a618bf413e1 0 {6f96419950729f3671185b847352890f074f7557} (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
679 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
679 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
680 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
680 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
681
681
682 List of both
682 List of both
683
683
684 $ hg debugobsolete --hidden --rev 3::6
684 $ hg debugobsolete --hidden --rev 3::6
685 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
685 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
686 1339133913391339133913391339133913391339 ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
686 1339133913391339133913391339133913391339 ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:19 1970 +0000) {'user': 'test'}
687 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
687 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f C (Thu Jan 01 00:00:01 1970 -0002) {'user': 'test'}
688 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
688 5601fb93a350734d935195fee37f4054c529ff39 6f96419950729f3671185b847352890f074f7557 1 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
689 94b33453f93bdb8d457ef9b770851a618bf413e1 0 {6f96419950729f3671185b847352890f074f7557} (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
689 94b33453f93bdb8d457ef9b770851a618bf413e1 0 {6f96419950729f3671185b847352890f074f7557} (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
690 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
690 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 (Thu Jan 01 00:22:18 1970 +0000) {'user': 'test'}
691 cda648ca50f50482b7055c0b0c4c117bba6733d9 3de5eca88c00aa039da7399a220f4a5221faa585 0 (*) {'user': 'test <test@example.net>'} (glob)
691 cda648ca50f50482b7055c0b0c4c117bba6733d9 3de5eca88c00aa039da7399a220f4a5221faa585 0 (*) {'user': 'test <test@example.net>'} (glob)
692 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
692 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'}
693
693
694 List of all markers in JSON
694 List of all markers in JSON
695
695
696 $ hg debugobsolete -Tjson
696 $ hg debugobsolete -Tjson
697 [
697 [
698 {
698 {
699 "date": [1339.0, 0],
699 "date": [1339.0, 0],
700 "flag": 0,
700 "flag": 0,
701 "metadata": {"user": "test"},
701 "metadata": {"user": "test"},
702 "precnode": "1339133913391339133913391339133913391339",
702 "prednode": "1339133913391339133913391339133913391339",
703 "succnodes": ["ca819180edb99ed25ceafb3e9584ac287e240b00"]
703 "succnodes": ["ca819180edb99ed25ceafb3e9584ac287e240b00"]
704 },
704 },
705 {
705 {
706 "date": [1339.0, 0],
706 "date": [1339.0, 0],
707 "flag": 0,
707 "flag": 0,
708 "metadata": {"user": "test"},
708 "metadata": {"user": "test"},
709 "precnode": "1337133713371337133713371337133713371337",
709 "prednode": "1337133713371337133713371337133713371337",
710 "succnodes": ["5601fb93a350734d935195fee37f4054c529ff39"]
710 "succnodes": ["5601fb93a350734d935195fee37f4054c529ff39"]
711 },
711 },
712 {
712 {
713 "date": [121.0, 120],
713 "date": [121.0, 120],
714 "flag": 12,
714 "flag": 12,
715 "metadata": {"user": "test"},
715 "metadata": {"user": "test"},
716 "precnode": "245bde4270cd1072a27757984f9cda8ba26f08ca",
716 "prednode": "245bde4270cd1072a27757984f9cda8ba26f08ca",
717 "succnodes": ["cdbce2fbb16313928851e97e0d85413f3f7eb77f"]
717 "succnodes": ["cdbce2fbb16313928851e97e0d85413f3f7eb77f"]
718 },
718 },
719 {
719 {
720 "date": [1338.0, 0],
720 "date": [1338.0, 0],
721 "flag": 1,
721 "flag": 1,
722 "metadata": {"user": "test"},
722 "metadata": {"user": "test"},
723 "precnode": "5601fb93a350734d935195fee37f4054c529ff39",
723 "prednode": "5601fb93a350734d935195fee37f4054c529ff39",
724 "succnodes": ["6f96419950729f3671185b847352890f074f7557"]
724 "succnodes": ["6f96419950729f3671185b847352890f074f7557"]
725 },
725 },
726 {
726 {
727 "date": [1338.0, 0],
727 "date": [1338.0, 0],
728 "flag": 0,
728 "flag": 0,
729 "metadata": {"user": "test"},
729 "metadata": {"user": "test"},
730 "precnode": "ca819180edb99ed25ceafb3e9584ac287e240b00",
730 "prednode": "ca819180edb99ed25ceafb3e9584ac287e240b00",
731 "succnodes": ["1337133713371337133713371337133713371337"]
731 "succnodes": ["1337133713371337133713371337133713371337"]
732 },
732 },
733 {
733 {
734 "date": [1337.0, 0],
734 "date": [1337.0, 0],
735 "flag": 0,
735 "flag": 0,
736 "metadata": {"user": "test"},
736 "metadata": {"user": "test"},
737 "precnode": "cdbce2fbb16313928851e97e0d85413f3f7eb77f",
737 "prednode": "cdbce2fbb16313928851e97e0d85413f3f7eb77f",
738 "succnodes": ["ca819180edb99ed25ceafb3e9584ac287e240b00"]
738 "succnodes": ["ca819180edb99ed25ceafb3e9584ac287e240b00"]
739 },
739 },
740 {
740 {
741 "date": [0.0, 0],
741 "date": [0.0, 0],
742 "flag": 0,
742 "flag": 0,
743 "metadata": {"user": "test"},
743 "metadata": {"user": "test"},
744 "parentnodes": ["6f96419950729f3671185b847352890f074f7557"],
744 "parentnodes": ["6f96419950729f3671185b847352890f074f7557"],
745 "precnode": "94b33453f93bdb8d457ef9b770851a618bf413e1",
745 "prednode": "94b33453f93bdb8d457ef9b770851a618bf413e1",
746 "succnodes": []
746 "succnodes": []
747 },
747 },
748 {
748 {
749 "date": *, (glob)
749 "date": *, (glob)
750 "flag": 0,
750 "flag": 0,
751 "metadata": {"user": "test <test@example.net>"},
751 "metadata": {"user": "test <test@example.net>"},
752 "precnode": "cda648ca50f50482b7055c0b0c4c117bba6733d9",
752 "prednode": "cda648ca50f50482b7055c0b0c4c117bba6733d9",
753 "succnodes": ["3de5eca88c00aa039da7399a220f4a5221faa585"]
753 "succnodes": ["3de5eca88c00aa039da7399a220f4a5221faa585"]
754 }
754 }
755 ]
755 ]
756
756
757 Template keywords
757 Template keywords
758
758
759 $ hg debugobsolete -r6 -T '{succnodes % "{node|short}"} {date|shortdate}\n'
759 $ hg debugobsolete -r6 -T '{succnodes % "{node|short}"} {date|shortdate}\n'
760 3de5eca88c00 ????-??-?? (glob)
760 3de5eca88c00 ????-??-?? (glob)
761 $ hg debugobsolete -r6 -T '{join(metadata % "{key}={value}", " ")}\n'
761 $ hg debugobsolete -r6 -T '{join(metadata % "{key}={value}", " ")}\n'
762 user=test <test@example.net>
762 user=test <test@example.net>
763 $ hg debugobsolete -r6 -T '{metadata}\n'
763 $ hg debugobsolete -r6 -T '{metadata}\n'
764 'user': 'test <test@example.net>'
764 'user': 'test <test@example.net>'
765 $ hg debugobsolete -r6 -T '{flag} {get(metadata, "user")}\n'
765 $ hg debugobsolete -r6 -T '{flag} {get(metadata, "user")}\n'
766 0 test <test@example.net>
766 0 test <test@example.net>
767
767
768 Test the debug output for exchange
768 Test the debug output for exchange
769 ----------------------------------
769 ----------------------------------
770
770
771 $ hg pull ../tmpb --config 'experimental.obsmarkers-exchange-debug=True' # bundle2
771 $ hg pull ../tmpb --config 'experimental.obsmarkers-exchange-debug=True' # bundle2
772 pulling from ../tmpb
772 pulling from ../tmpb
773 searching for changes
773 searching for changes
774 no changes found
774 no changes found
775 obsmarker-exchange: 346 bytes received
775 obsmarker-exchange: 346 bytes received
776
776
777 check hgweb does not explode
777 check hgweb does not explode
778 ====================================
778 ====================================
779
779
780 $ hg unbundle $TESTDIR/bundles/hgweb+obs.hg
780 $ hg unbundle $TESTDIR/bundles/hgweb+obs.hg
781 adding changesets
781 adding changesets
782 adding manifests
782 adding manifests
783 adding file changes
783 adding file changes
784 added 62 changesets with 63 changes to 9 files (+60 heads)
784 added 62 changesets with 63 changes to 9 files (+60 heads)
785 (run 'hg heads .' to see heads, 'hg merge' to merge)
785 (run 'hg heads .' to see heads, 'hg merge' to merge)
786 $ for node in `hg log -r 'desc(babar_)' --template '{node}\n'`;
786 $ for node in `hg log -r 'desc(babar_)' --template '{node}\n'`;
787 > do
787 > do
788 > hg debugobsolete $node
788 > hg debugobsolete $node
789 > done
789 > done
790 obsoleted 1 changesets
790 obsoleted 1 changesets
791 obsoleted 1 changesets
791 obsoleted 1 changesets
792 obsoleted 1 changesets
792 obsoleted 1 changesets
793 obsoleted 1 changesets
793 obsoleted 1 changesets
794 obsoleted 1 changesets
794 obsoleted 1 changesets
795 obsoleted 1 changesets
795 obsoleted 1 changesets
796 obsoleted 1 changesets
796 obsoleted 1 changesets
797 obsoleted 1 changesets
797 obsoleted 1 changesets
798 obsoleted 1 changesets
798 obsoleted 1 changesets
799 obsoleted 1 changesets
799 obsoleted 1 changesets
800 obsoleted 1 changesets
800 obsoleted 1 changesets
801 obsoleted 1 changesets
801 obsoleted 1 changesets
802 obsoleted 1 changesets
802 obsoleted 1 changesets
803 obsoleted 1 changesets
803 obsoleted 1 changesets
804 obsoleted 1 changesets
804 obsoleted 1 changesets
805 obsoleted 1 changesets
805 obsoleted 1 changesets
806 obsoleted 1 changesets
806 obsoleted 1 changesets
807 obsoleted 1 changesets
807 obsoleted 1 changesets
808 obsoleted 1 changesets
808 obsoleted 1 changesets
809 obsoleted 1 changesets
809 obsoleted 1 changesets
810 obsoleted 1 changesets
810 obsoleted 1 changesets
811 obsoleted 1 changesets
811 obsoleted 1 changesets
812 obsoleted 1 changesets
812 obsoleted 1 changesets
813 obsoleted 1 changesets
813 obsoleted 1 changesets
814 obsoleted 1 changesets
814 obsoleted 1 changesets
815 obsoleted 1 changesets
815 obsoleted 1 changesets
816 obsoleted 1 changesets
816 obsoleted 1 changesets
817 obsoleted 1 changesets
817 obsoleted 1 changesets
818 obsoleted 1 changesets
818 obsoleted 1 changesets
819 obsoleted 1 changesets
819 obsoleted 1 changesets
820 obsoleted 1 changesets
820 obsoleted 1 changesets
821 obsoleted 1 changesets
821 obsoleted 1 changesets
822 obsoleted 1 changesets
822 obsoleted 1 changesets
823 obsoleted 1 changesets
823 obsoleted 1 changesets
824 obsoleted 1 changesets
824 obsoleted 1 changesets
825 obsoleted 1 changesets
825 obsoleted 1 changesets
826 obsoleted 1 changesets
826 obsoleted 1 changesets
827 obsoleted 1 changesets
827 obsoleted 1 changesets
828 obsoleted 1 changesets
828 obsoleted 1 changesets
829 obsoleted 1 changesets
829 obsoleted 1 changesets
830 obsoleted 1 changesets
830 obsoleted 1 changesets
831 obsoleted 1 changesets
831 obsoleted 1 changesets
832 obsoleted 1 changesets
832 obsoleted 1 changesets
833 obsoleted 1 changesets
833 obsoleted 1 changesets
834 obsoleted 1 changesets
834 obsoleted 1 changesets
835 obsoleted 1 changesets
835 obsoleted 1 changesets
836 obsoleted 1 changesets
836 obsoleted 1 changesets
837 obsoleted 1 changesets
837 obsoleted 1 changesets
838 obsoleted 1 changesets
838 obsoleted 1 changesets
839 obsoleted 1 changesets
839 obsoleted 1 changesets
840 obsoleted 1 changesets
840 obsoleted 1 changesets
841 obsoleted 1 changesets
841 obsoleted 1 changesets
842 obsoleted 1 changesets
842 obsoleted 1 changesets
843 obsoleted 1 changesets
843 obsoleted 1 changesets
844 obsoleted 1 changesets
844 obsoleted 1 changesets
845 obsoleted 1 changesets
845 obsoleted 1 changesets
846 obsoleted 1 changesets
846 obsoleted 1 changesets
847 obsoleted 1 changesets
847 obsoleted 1 changesets
848 obsoleted 1 changesets
848 obsoleted 1 changesets
849 obsoleted 1 changesets
849 obsoleted 1 changesets
850 $ hg up tip
850 $ hg up tip
851 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
851 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
852
852
853 #if serve
853 #if serve
854
854
855 $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
855 $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
856 $ cat hg.pid >> $DAEMON_PIDS
856 $ cat hg.pid >> $DAEMON_PIDS
857
857
858 check changelog view
858 check changelog view
859
859
860 $ get-with-headers.py --headeronly localhost:$HGPORT 'shortlog/'
860 $ get-with-headers.py --headeronly localhost:$HGPORT 'shortlog/'
861 200 Script output follows
861 200 Script output follows
862
862
863 check graph view
863 check graph view
864
864
865 $ get-with-headers.py --headeronly localhost:$HGPORT 'graph'
865 $ get-with-headers.py --headeronly localhost:$HGPORT 'graph'
866 200 Script output follows
866 200 Script output follows
867
867
868 check filelog view
868 check filelog view
869
869
870 $ get-with-headers.py --headeronly localhost:$HGPORT 'log/'`hg log -r . -T "{node}"`/'babar'
870 $ get-with-headers.py --headeronly localhost:$HGPORT 'log/'`hg log -r . -T "{node}"`/'babar'
871 200 Script output follows
871 200 Script output follows
872
872
873 $ get-with-headers.py --headeronly localhost:$HGPORT 'rev/68'
873 $ get-with-headers.py --headeronly localhost:$HGPORT 'rev/68'
874 200 Script output follows
874 200 Script output follows
875 $ get-with-headers.py --headeronly localhost:$HGPORT 'rev/67'
875 $ get-with-headers.py --headeronly localhost:$HGPORT 'rev/67'
876 404 Not Found
876 404 Not Found
877 [1]
877 [1]
878
878
879 check that web.view config option:
879 check that web.view config option:
880
880
881 $ killdaemons.py hg.pid
881 $ killdaemons.py hg.pid
882 $ cat >> .hg/hgrc << EOF
882 $ cat >> .hg/hgrc << EOF
883 > [web]
883 > [web]
884 > view=all
884 > view=all
885 > EOF
885 > EOF
886 $ wait
886 $ wait
887 $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
887 $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
888 $ get-with-headers.py --headeronly localhost:$HGPORT 'rev/67'
888 $ get-with-headers.py --headeronly localhost:$HGPORT 'rev/67'
889 200 Script output follows
889 200 Script output follows
890 $ killdaemons.py hg.pid
890 $ killdaemons.py hg.pid
891
891
892 Checking _enable=False warning if obsolete marker exists
892 Checking _enable=False warning if obsolete marker exists
893
893
894 $ echo '[experimental]' >> $HGRCPATH
894 $ echo '[experimental]' >> $HGRCPATH
895 $ echo "stabilization=" >> $HGRCPATH
895 $ echo "stabilization=" >> $HGRCPATH
896 $ hg log -r tip
896 $ hg log -r tip
897 obsolete feature not enabled but 68 markers found!
897 obsolete feature not enabled but 68 markers found!
898 68:c15e9edfca13 (draft) [tip ] add celestine
898 68:c15e9edfca13 (draft) [tip ] add celestine
899
899
900 reenable for later test
900 reenable for later test
901
901
902 $ echo '[experimental]' >> $HGRCPATH
902 $ echo '[experimental]' >> $HGRCPATH
903 $ echo "stabilization=createmarkers,exchange" >> $HGRCPATH
903 $ echo "stabilization=createmarkers,exchange" >> $HGRCPATH
904
904
905 $ rm hg.pid access.log errors.log
905 $ rm hg.pid access.log errors.log
906 #endif
906 #endif
907
907
908 Several troubles on the same changeset (create an unstable and bumped changeset)
908 Several troubles on the same changeset (create an unstable and bumped changeset)
909
909
910 $ hg debugobsolete `getid obsolete_e`
910 $ hg debugobsolete `getid obsolete_e`
911 obsoleted 1 changesets
911 obsoleted 1 changesets
912 $ hg debugobsolete `getid original_c` `getid babar`
912 $ hg debugobsolete `getid original_c` `getid babar`
913 $ hg log --config ui.logtemplate= -r 'phasedivergent() and orphan()'
913 $ hg log --config ui.logtemplate= -r 'phasedivergent() and orphan()'
914 changeset: 7:50c51b361e60
914 changeset: 7:50c51b361e60
915 user: test
915 user: test
916 date: Thu Jan 01 00:00:00 1970 +0000
916 date: Thu Jan 01 00:00:00 1970 +0000
917 instability: orphan, phase-divergent
917 instability: orphan, phase-divergent
918 summary: add babar
918 summary: add babar
919
919
920
920
921 test the "obsolete" templatekw
921 test the "obsolete" templatekw
922
922
923 $ hg log -r 'obsolete()'
923 $ hg log -r 'obsolete()'
924 6:3de5eca88c00 (draft *obsolete*) [ ] add obsolete_e
924 6:3de5eca88c00 (draft *obsolete*) [ ] add obsolete_e
925
925
926 test the "troubles" templatekw
926 test the "troubles" templatekw
927
927
928 $ hg log -r 'phasedivergent() and orphan()'
928 $ hg log -r 'phasedivergent() and orphan()'
929 7:50c51b361e60 (draft orphan phase-divergent) [ ] add babar
929 7:50c51b361e60 (draft orphan phase-divergent) [ ] add babar
930
930
931 test the default cmdline template
931 test the default cmdline template
932
932
933 $ hg log -T default -r 'phasedivergent()'
933 $ hg log -T default -r 'phasedivergent()'
934 changeset: 7:50c51b361e60
934 changeset: 7:50c51b361e60
935 user: test
935 user: test
936 date: Thu Jan 01 00:00:00 1970 +0000
936 date: Thu Jan 01 00:00:00 1970 +0000
937 instability: orphan, phase-divergent
937 instability: orphan, phase-divergent
938 summary: add babar
938 summary: add babar
939
939
940 $ hg log -T default -r 'obsolete()'
940 $ hg log -T default -r 'obsolete()'
941 changeset: 6:3de5eca88c00
941 changeset: 6:3de5eca88c00
942 parent: 3:6f9641995072
942 parent: 3:6f9641995072
943 user: test
943 user: test
944 date: Thu Jan 01 00:00:00 1970 +0000
944 date: Thu Jan 01 00:00:00 1970 +0000
945 summary: add obsolete_e
945 summary: add obsolete_e
946
946
947
947
948 test the obsolete labels
948 test the obsolete labels
949
949
950 $ hg log --config ui.logtemplate= --color=debug -r 'phasedivergent()'
950 $ hg log --config ui.logtemplate= --color=debug -r 'phasedivergent()'
951 [log.changeset changeset.draft changeset.unstable instability.orphan instability.phase-divergent|changeset: 7:50c51b361e60]
951 [log.changeset changeset.draft changeset.unstable instability.orphan instability.phase-divergent|changeset: 7:50c51b361e60]
952 [log.user|user: test]
952 [log.user|user: test]
953 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
953 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
954 [log.instability|instability: orphan, phase-divergent]
954 [log.instability|instability: orphan, phase-divergent]
955 [log.summary|summary: add babar]
955 [log.summary|summary: add babar]
956
956
957
957
958 $ hg log -T default -r 'phasedivergent()' --color=debug
958 $ hg log -T default -r 'phasedivergent()' --color=debug
959 [log.changeset changeset.draft changeset.unstable instability.orphaninstability.phase-divergent|changeset: 7:50c51b361e60]
959 [log.changeset changeset.draft changeset.unstable instability.orphaninstability.phase-divergent|changeset: 7:50c51b361e60]
960 [log.user|user: test]
960 [log.user|user: test]
961 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
961 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
962 [log.instability|instability: orphan, phase-divergent]
962 [log.instability|instability: orphan, phase-divergent]
963 [log.summary|summary: add babar]
963 [log.summary|summary: add babar]
964
964
965
965
966 $ hg log --config ui.logtemplate= --color=debug -r "obsolete()"
966 $ hg log --config ui.logtemplate= --color=debug -r "obsolete()"
967 [log.changeset changeset.draft changeset.obsolete|changeset: 6:3de5eca88c00]
967 [log.changeset changeset.draft changeset.obsolete|changeset: 6:3de5eca88c00]
968 [log.parent changeset.draft|parent: 3:6f9641995072]
968 [log.parent changeset.draft|parent: 3:6f9641995072]
969 [log.user|user: test]
969 [log.user|user: test]
970 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
970 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
971 [log.summary|summary: add obsolete_e]
971 [log.summary|summary: add obsolete_e]
972
972
973
973
974 $ hg log -T default -r 'obsolete()' --color=debug
974 $ hg log -T default -r 'obsolete()' --color=debug
975 [log.changeset changeset.draft changeset.obsolete|changeset: 6:3de5eca88c00]
975 [log.changeset changeset.draft changeset.obsolete|changeset: 6:3de5eca88c00]
976 [log.parent changeset.draft|parent: 3:6f9641995072]
976 [log.parent changeset.draft|parent: 3:6f9641995072]
977 [log.user|user: test]
977 [log.user|user: test]
978 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
978 [log.date|date: Thu Jan 01 00:00:00 1970 +0000]
979 [log.summary|summary: add obsolete_e]
979 [log.summary|summary: add obsolete_e]
980
980
981
981
982 test summary output
982 test summary output
983
983
984 $ hg up -r 'phasedivergent() and orphan()'
984 $ hg up -r 'phasedivergent() and orphan()'
985 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
985 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
986 $ hg summary
986 $ hg summary
987 parent: 7:50c51b361e60 (orphan, phase-divergent)
987 parent: 7:50c51b361e60 (orphan, phase-divergent)
988 add babar
988 add babar
989 branch: default
989 branch: default
990 commit: (clean)
990 commit: (clean)
991 update: 2 new changesets (update)
991 update: 2 new changesets (update)
992 phases: 4 draft
992 phases: 4 draft
993 orphan: 2 changesets
993 orphan: 2 changesets
994 phase-divergent: 1 changesets
994 phase-divergent: 1 changesets
995 $ hg up -r 'obsolete()'
995 $ hg up -r 'obsolete()'
996 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
996 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
997 $ hg summary
997 $ hg summary
998 parent: 6:3de5eca88c00 (obsolete)
998 parent: 6:3de5eca88c00 (obsolete)
999 add obsolete_e
999 add obsolete_e
1000 branch: default
1000 branch: default
1001 commit: (clean)
1001 commit: (clean)
1002 update: 3 new changesets (update)
1002 update: 3 new changesets (update)
1003 phases: 4 draft
1003 phases: 4 draft
1004 orphan: 2 changesets
1004 orphan: 2 changesets
1005 phase-divergent: 1 changesets
1005 phase-divergent: 1 changesets
1006
1006
1007 Test incoming/outcoming with changesets obsoleted remotely, known locally
1007 Test incoming/outcoming with changesets obsoleted remotely, known locally
1008 ===============================================================================
1008 ===============================================================================
1009
1009
1010 This test issue 3805
1010 This test issue 3805
1011
1011
1012 $ hg init repo-issue3805
1012 $ hg init repo-issue3805
1013 $ cd repo-issue3805
1013 $ cd repo-issue3805
1014 $ echo "base" > base
1014 $ echo "base" > base
1015 $ hg ci -Am "base"
1015 $ hg ci -Am "base"
1016 adding base
1016 adding base
1017 $ echo "foo" > foo
1017 $ echo "foo" > foo
1018 $ hg ci -Am "A"
1018 $ hg ci -Am "A"
1019 adding foo
1019 adding foo
1020 $ hg clone . ../other-issue3805
1020 $ hg clone . ../other-issue3805
1021 updating to branch default
1021 updating to branch default
1022 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1022 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1023 $ echo "bar" >> foo
1023 $ echo "bar" >> foo
1024 $ hg ci --amend
1024 $ hg ci --amend
1025 $ cd ../other-issue3805
1025 $ cd ../other-issue3805
1026 $ hg log -G
1026 $ hg log -G
1027 @ 1:29f0c6921ddd (draft) [tip ] A
1027 @ 1:29f0c6921ddd (draft) [tip ] A
1028 |
1028 |
1029 o 0:d20a80d4def3 (draft) [ ] base
1029 o 0:d20a80d4def3 (draft) [ ] base
1030
1030
1031 $ hg log -G -R ../repo-issue3805
1031 $ hg log -G -R ../repo-issue3805
1032 @ 3:323a9c3ddd91 (draft) [tip ] A
1032 @ 3:323a9c3ddd91 (draft) [tip ] A
1033 |
1033 |
1034 o 0:d20a80d4def3 (draft) [ ] base
1034 o 0:d20a80d4def3 (draft) [ ] base
1035
1035
1036 $ hg incoming
1036 $ hg incoming
1037 comparing with $TESTTMP/tmpe/repo-issue3805 (glob)
1037 comparing with $TESTTMP/tmpe/repo-issue3805 (glob)
1038 searching for changes
1038 searching for changes
1039 3:323a9c3ddd91 (draft) [tip ] A
1039 3:323a9c3ddd91 (draft) [tip ] A
1040 $ hg incoming --bundle ../issue3805.hg
1040 $ hg incoming --bundle ../issue3805.hg
1041 comparing with $TESTTMP/tmpe/repo-issue3805 (glob)
1041 comparing with $TESTTMP/tmpe/repo-issue3805 (glob)
1042 searching for changes
1042 searching for changes
1043 3:323a9c3ddd91 (draft) [tip ] A
1043 3:323a9c3ddd91 (draft) [tip ] A
1044 $ hg outgoing
1044 $ hg outgoing
1045 comparing with $TESTTMP/tmpe/repo-issue3805 (glob)
1045 comparing with $TESTTMP/tmpe/repo-issue3805 (glob)
1046 searching for changes
1046 searching for changes
1047 1:29f0c6921ddd (draft) [tip ] A
1047 1:29f0c6921ddd (draft) [tip ] A
1048
1048
1049 #if serve
1049 #if serve
1050
1050
1051 $ hg serve -R ../repo-issue3805 -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
1051 $ hg serve -R ../repo-issue3805 -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
1052 $ cat hg.pid >> $DAEMON_PIDS
1052 $ cat hg.pid >> $DAEMON_PIDS
1053
1053
1054 $ hg incoming http://localhost:$HGPORT
1054 $ hg incoming http://localhost:$HGPORT
1055 comparing with http://localhost:$HGPORT/
1055 comparing with http://localhost:$HGPORT/
1056 searching for changes
1056 searching for changes
1057 2:323a9c3ddd91 (draft) [tip ] A
1057 2:323a9c3ddd91 (draft) [tip ] A
1058 $ hg outgoing http://localhost:$HGPORT
1058 $ hg outgoing http://localhost:$HGPORT
1059 comparing with http://localhost:$HGPORT/
1059 comparing with http://localhost:$HGPORT/
1060 searching for changes
1060 searching for changes
1061 1:29f0c6921ddd (draft) [tip ] A
1061 1:29f0c6921ddd (draft) [tip ] A
1062
1062
1063 $ killdaemons.py
1063 $ killdaemons.py
1064
1064
1065 #endif
1065 #endif
1066
1066
1067 This test issue 3814
1067 This test issue 3814
1068
1068
1069 (nothing to push but locally hidden changeset)
1069 (nothing to push but locally hidden changeset)
1070
1070
1071 $ cd ..
1071 $ cd ..
1072 $ hg init repo-issue3814
1072 $ hg init repo-issue3814
1073 $ cd repo-issue3805
1073 $ cd repo-issue3805
1074 $ hg push -r 323a9c3ddd91 ../repo-issue3814
1074 $ hg push -r 323a9c3ddd91 ../repo-issue3814
1075 pushing to ../repo-issue3814
1075 pushing to ../repo-issue3814
1076 searching for changes
1076 searching for changes
1077 adding changesets
1077 adding changesets
1078 adding manifests
1078 adding manifests
1079 adding file changes
1079 adding file changes
1080 added 2 changesets with 2 changes to 2 files
1080 added 2 changesets with 2 changes to 2 files
1081 2 new obsolescence markers
1081 2 new obsolescence markers
1082 $ hg out ../repo-issue3814
1082 $ hg out ../repo-issue3814
1083 comparing with ../repo-issue3814
1083 comparing with ../repo-issue3814
1084 searching for changes
1084 searching for changes
1085 no changes found
1085 no changes found
1086 [1]
1086 [1]
1087
1087
1088 Test that a local tag blocks a changeset from being hidden
1088 Test that a local tag blocks a changeset from being hidden
1089
1089
1090 $ hg tag -l visible -r 1 --hidden
1090 $ hg tag -l visible -r 1 --hidden
1091 $ hg log -G
1091 $ hg log -G
1092 @ 3:323a9c3ddd91 (draft) [tip ] A
1092 @ 3:323a9c3ddd91 (draft) [tip ] A
1093 |
1093 |
1094 | x 1:29f0c6921ddd (draft *obsolete*) [visible ] A
1094 | x 1:29f0c6921ddd (draft *obsolete*) [visible ] A
1095 |/
1095 |/
1096 o 0:d20a80d4def3 (draft) [ ] base
1096 o 0:d20a80d4def3 (draft) [ ] base
1097
1097
1098 Test that removing a local tag does not cause some commands to fail
1098 Test that removing a local tag does not cause some commands to fail
1099
1099
1100 $ hg tag -l -r tip tiptag
1100 $ hg tag -l -r tip tiptag
1101 $ hg tags
1101 $ hg tags
1102 tiptag 3:323a9c3ddd91
1102 tiptag 3:323a9c3ddd91
1103 tip 3:323a9c3ddd91
1103 tip 3:323a9c3ddd91
1104 visible 1:29f0c6921ddd
1104 visible 1:29f0c6921ddd
1105 $ hg --config extensions.strip= strip -r tip --no-backup
1105 $ hg --config extensions.strip= strip -r tip --no-backup
1106 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1106 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1107 $ hg tags
1107 $ hg tags
1108 visible 1:29f0c6921ddd
1108 visible 1:29f0c6921ddd
1109 tip 1:29f0c6921ddd
1109 tip 1:29f0c6921ddd
1110
1110
1111 Test bundle overlay onto hidden revision
1111 Test bundle overlay onto hidden revision
1112
1112
1113 $ cd ..
1113 $ cd ..
1114 $ hg init repo-bundleoverlay
1114 $ hg init repo-bundleoverlay
1115 $ cd repo-bundleoverlay
1115 $ cd repo-bundleoverlay
1116 $ echo "A" > foo
1116 $ echo "A" > foo
1117 $ hg ci -Am "A"
1117 $ hg ci -Am "A"
1118 adding foo
1118 adding foo
1119 $ echo "B" >> foo
1119 $ echo "B" >> foo
1120 $ hg ci -m "B"
1120 $ hg ci -m "B"
1121 $ hg up 0
1121 $ hg up 0
1122 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1122 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1123 $ echo "C" >> foo
1123 $ echo "C" >> foo
1124 $ hg ci -m "C"
1124 $ hg ci -m "C"
1125 created new head
1125 created new head
1126 $ hg log -G
1126 $ hg log -G
1127 @ 2:c186d7714947 (draft) [tip ] C
1127 @ 2:c186d7714947 (draft) [tip ] C
1128 |
1128 |
1129 | o 1:44526ebb0f98 (draft) [ ] B
1129 | o 1:44526ebb0f98 (draft) [ ] B
1130 |/
1130 |/
1131 o 0:4b34ecfb0d56 (draft) [ ] A
1131 o 0:4b34ecfb0d56 (draft) [ ] A
1132
1132
1133
1133
1134 $ hg clone -r1 . ../other-bundleoverlay
1134 $ hg clone -r1 . ../other-bundleoverlay
1135 adding changesets
1135 adding changesets
1136 adding manifests
1136 adding manifests
1137 adding file changes
1137 adding file changes
1138 added 2 changesets with 2 changes to 1 files
1138 added 2 changesets with 2 changes to 1 files
1139 updating to branch default
1139 updating to branch default
1140 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1140 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1141 $ cd ../other-bundleoverlay
1141 $ cd ../other-bundleoverlay
1142 $ echo "B+" >> foo
1142 $ echo "B+" >> foo
1143 $ hg ci --amend -m "B+"
1143 $ hg ci --amend -m "B+"
1144 $ hg log -G --hidden
1144 $ hg log -G --hidden
1145 @ 3:b7d587542d40 (draft) [tip ] B+
1145 @ 3:b7d587542d40 (draft) [tip ] B+
1146 |
1146 |
1147 | x 2:eb95e9297e18 (draft *obsolete*) [ ] temporary amend commit for 44526ebb0f98
1147 | x 2:eb95e9297e18 (draft *obsolete*) [ ] temporary amend commit for 44526ebb0f98
1148 | |
1148 | |
1149 | x 1:44526ebb0f98 (draft *obsolete*) [ ] B
1149 | x 1:44526ebb0f98 (draft *obsolete*) [ ] B
1150 |/
1150 |/
1151 o 0:4b34ecfb0d56 (draft) [ ] A
1151 o 0:4b34ecfb0d56 (draft) [ ] A
1152
1152
1153
1153
1154 $ hg incoming ../repo-bundleoverlay --bundle ../bundleoverlay.hg
1154 $ hg incoming ../repo-bundleoverlay --bundle ../bundleoverlay.hg
1155 comparing with ../repo-bundleoverlay
1155 comparing with ../repo-bundleoverlay
1156 searching for changes
1156 searching for changes
1157 1:44526ebb0f98 (draft) [ ] B
1157 1:44526ebb0f98 (draft) [ ] B
1158 2:c186d7714947 (draft) [tip ] C
1158 2:c186d7714947 (draft) [tip ] C
1159 $ hg log -G -R ../bundleoverlay.hg
1159 $ hg log -G -R ../bundleoverlay.hg
1160 o 4:c186d7714947 (draft) [tip ] C
1160 o 4:c186d7714947 (draft) [tip ] C
1161 |
1161 |
1162 | @ 3:b7d587542d40 (draft) [ ] B+
1162 | @ 3:b7d587542d40 (draft) [ ] B+
1163 |/
1163 |/
1164 o 0:4b34ecfb0d56 (draft) [ ] A
1164 o 0:4b34ecfb0d56 (draft) [ ] A
1165
1165
1166
1166
1167 #if serve
1167 #if serve
1168
1168
1169 Test issue 4506
1169 Test issue 4506
1170
1170
1171 $ cd ..
1171 $ cd ..
1172 $ hg init repo-issue4506
1172 $ hg init repo-issue4506
1173 $ cd repo-issue4506
1173 $ cd repo-issue4506
1174 $ echo "0" > foo
1174 $ echo "0" > foo
1175 $ hg add foo
1175 $ hg add foo
1176 $ hg ci -m "content-0"
1176 $ hg ci -m "content-0"
1177
1177
1178 $ hg up null
1178 $ hg up null
1179 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1179 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1180 $ echo "1" > bar
1180 $ echo "1" > bar
1181 $ hg add bar
1181 $ hg add bar
1182 $ hg ci -m "content-1"
1182 $ hg ci -m "content-1"
1183 created new head
1183 created new head
1184 $ hg up 0
1184 $ hg up 0
1185 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1185 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1186 $ hg graft 1
1186 $ hg graft 1
1187 grafting 1:1c9eddb02162 "content-1" (tip)
1187 grafting 1:1c9eddb02162 "content-1" (tip)
1188
1188
1189 $ hg debugobsolete `hg log -r1 -T'{node}'` `hg log -r2 -T'{node}'`
1189 $ hg debugobsolete `hg log -r1 -T'{node}'` `hg log -r2 -T'{node}'`
1190 obsoleted 1 changesets
1190 obsoleted 1 changesets
1191
1191
1192 $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
1192 $ hg serve -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
1193 $ cat hg.pid >> $DAEMON_PIDS
1193 $ cat hg.pid >> $DAEMON_PIDS
1194
1194
1195 $ get-with-headers.py --headeronly localhost:$HGPORT 'rev/1'
1195 $ get-with-headers.py --headeronly localhost:$HGPORT 'rev/1'
1196 404 Not Found
1196 404 Not Found
1197 [1]
1197 [1]
1198 $ get-with-headers.py --headeronly localhost:$HGPORT 'file/tip/bar'
1198 $ get-with-headers.py --headeronly localhost:$HGPORT 'file/tip/bar'
1199 200 Script output follows
1199 200 Script output follows
1200 $ get-with-headers.py --headeronly localhost:$HGPORT 'annotate/tip/bar'
1200 $ get-with-headers.py --headeronly localhost:$HGPORT 'annotate/tip/bar'
1201 200 Script output follows
1201 200 Script output follows
1202
1202
1203 $ killdaemons.py
1203 $ killdaemons.py
1204
1204
1205 #endif
1205 #endif
1206
1206
1207 Test heads computation on pending index changes with obsolescence markers
1207 Test heads computation on pending index changes with obsolescence markers
1208 $ cd ..
1208 $ cd ..
1209 $ cat >$TESTTMP/test_extension.py << EOF
1209 $ cat >$TESTTMP/test_extension.py << EOF
1210 > from mercurial import cmdutil, registrar
1210 > from mercurial import cmdutil, registrar
1211 > from mercurial.i18n import _
1211 > from mercurial.i18n import _
1212 >
1212 >
1213 > cmdtable = {}
1213 > cmdtable = {}
1214 > command = registrar.command(cmdtable)
1214 > command = registrar.command(cmdtable)
1215 > @command(b"amendtransient",[], _('hg amendtransient [rev]'))
1215 > @command(b"amendtransient",[], _('hg amendtransient [rev]'))
1216 > def amend(ui, repo, *pats, **opts):
1216 > def amend(ui, repo, *pats, **opts):
1217 > def commitfunc(ui, repo, message, match, opts):
1217 > def commitfunc(ui, repo, message, match, opts):
1218 > return repo.commit(message, repo['.'].user(), repo['.'].date(), match)
1218 > return repo.commit(message, repo['.'].user(), repo['.'].date(), match)
1219 > opts['message'] = 'Test'
1219 > opts['message'] = 'Test'
1220 > opts['logfile'] = None
1220 > opts['logfile'] = None
1221 > cmdutil.amend(ui, repo, commitfunc, repo['.'], {}, pats, opts)
1221 > cmdutil.amend(ui, repo, commitfunc, repo['.'], {}, pats, opts)
1222 > ui.write('%s\n' % repo.changelog.headrevs())
1222 > ui.write('%s\n' % repo.changelog.headrevs())
1223 > EOF
1223 > EOF
1224 $ cat >> $HGRCPATH << EOF
1224 $ cat >> $HGRCPATH << EOF
1225 > [extensions]
1225 > [extensions]
1226 > testextension=$TESTTMP/test_extension.py
1226 > testextension=$TESTTMP/test_extension.py
1227 > EOF
1227 > EOF
1228 $ hg init repo-issue-nativerevs-pending-changes
1228 $ hg init repo-issue-nativerevs-pending-changes
1229 $ cd repo-issue-nativerevs-pending-changes
1229 $ cd repo-issue-nativerevs-pending-changes
1230 $ mkcommit a
1230 $ mkcommit a
1231 $ mkcommit b
1231 $ mkcommit b
1232 $ hg up ".^"
1232 $ hg up ".^"
1233 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1233 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1234 $ echo aa > a
1234 $ echo aa > a
1235 $ hg amendtransient
1235 $ hg amendtransient
1236 [1, 3]
1236 [1, 3]
1237
1237
1238 Test cache consistency for the visible filter
1238 Test cache consistency for the visible filter
1239 1) We want to make sure that the cached filtered revs are invalidated when
1239 1) We want to make sure that the cached filtered revs are invalidated when
1240 bookmarks change
1240 bookmarks change
1241 $ cd ..
1241 $ cd ..
1242 $ cat >$TESTTMP/test_extension.py << EOF
1242 $ cat >$TESTTMP/test_extension.py << EOF
1243 > import weakref
1243 > import weakref
1244 > from mercurial import cmdutil, extensions, bookmarks, repoview
1244 > from mercurial import cmdutil, extensions, bookmarks, repoview
1245 > def _bookmarkchanged(orig, bkmstoreinst, *args, **kwargs):
1245 > def _bookmarkchanged(orig, bkmstoreinst, *args, **kwargs):
1246 > reporef = weakref.ref(bkmstoreinst._repo)
1246 > reporef = weakref.ref(bkmstoreinst._repo)
1247 > def trhook(tr):
1247 > def trhook(tr):
1248 > repo = reporef()
1248 > repo = reporef()
1249 > hidden1 = repoview.computehidden(repo)
1249 > hidden1 = repoview.computehidden(repo)
1250 > hidden = repoview.filterrevs(repo, 'visible')
1250 > hidden = repoview.filterrevs(repo, 'visible')
1251 > if sorted(hidden1) != sorted(hidden):
1251 > if sorted(hidden1) != sorted(hidden):
1252 > print "cache inconsistency"
1252 > print "cache inconsistency"
1253 > bkmstoreinst._repo.currenttransaction().addpostclose('test_extension', trhook)
1253 > bkmstoreinst._repo.currenttransaction().addpostclose('test_extension', trhook)
1254 > orig(bkmstoreinst, *args, **kwargs)
1254 > orig(bkmstoreinst, *args, **kwargs)
1255 > def extsetup(ui):
1255 > def extsetup(ui):
1256 > extensions.wrapfunction(bookmarks.bmstore, '_recordchange',
1256 > extensions.wrapfunction(bookmarks.bmstore, '_recordchange',
1257 > _bookmarkchanged)
1257 > _bookmarkchanged)
1258 > EOF
1258 > EOF
1259
1259
1260 $ hg init repo-cache-inconsistency
1260 $ hg init repo-cache-inconsistency
1261 $ cd repo-issue-nativerevs-pending-changes
1261 $ cd repo-issue-nativerevs-pending-changes
1262 $ mkcommit a
1262 $ mkcommit a
1263 a already tracked!
1263 a already tracked!
1264 $ mkcommit b
1264 $ mkcommit b
1265 $ hg id
1265 $ hg id
1266 13bedc178fce tip
1266 13bedc178fce tip
1267 $ echo "hello" > b
1267 $ echo "hello" > b
1268 $ hg commit --amend -m "message"
1268 $ hg commit --amend -m "message"
1269 $ hg book bookb -r 13bedc178fce --hidden
1269 $ hg book bookb -r 13bedc178fce --hidden
1270 $ hg log -r 13bedc178fce
1270 $ hg log -r 13bedc178fce
1271 5:13bedc178fce (draft *obsolete*) [ bookb] add b
1271 5:13bedc178fce (draft *obsolete*) [ bookb] add b
1272 $ hg book -d bookb
1272 $ hg book -d bookb
1273 $ hg log -r 13bedc178fce
1273 $ hg log -r 13bedc178fce
1274 abort: hidden revision '13bedc178fce'!
1274 abort: hidden revision '13bedc178fce'!
1275 (use --hidden to access hidden revisions)
1275 (use --hidden to access hidden revisions)
1276 [255]
1276 [255]
1277
1277
1278 Empty out the test extension, as it isn't compatible with later parts
1278 Empty out the test extension, as it isn't compatible with later parts
1279 of the test.
1279 of the test.
1280 $ echo > $TESTTMP/test_extension.py
1280 $ echo > $TESTTMP/test_extension.py
1281
1281
1282 Test ability to pull changeset with locally applying obsolescence markers
1282 Test ability to pull changeset with locally applying obsolescence markers
1283 (issue4945)
1283 (issue4945)
1284
1284
1285 $ cd ..
1285 $ cd ..
1286 $ hg init issue4845
1286 $ hg init issue4845
1287 $ cd issue4845
1287 $ cd issue4845
1288
1288
1289 $ echo foo > f0
1289 $ echo foo > f0
1290 $ hg add f0
1290 $ hg add f0
1291 $ hg ci -m '0'
1291 $ hg ci -m '0'
1292 $ echo foo > f1
1292 $ echo foo > f1
1293 $ hg add f1
1293 $ hg add f1
1294 $ hg ci -m '1'
1294 $ hg ci -m '1'
1295 $ echo foo > f2
1295 $ echo foo > f2
1296 $ hg add f2
1296 $ hg add f2
1297 $ hg ci -m '2'
1297 $ hg ci -m '2'
1298
1298
1299 $ echo bar > f2
1299 $ echo bar > f2
1300 $ hg commit --amend --config experimetnal.stabilization=createmarkers
1300 $ hg commit --amend --config experimetnal.stabilization=createmarkers
1301 $ hg log -G
1301 $ hg log -G
1302 @ 4:b0551702f918 (draft) [tip ] 2
1302 @ 4:b0551702f918 (draft) [tip ] 2
1303 |
1303 |
1304 o 1:e016b03fd86f (draft) [ ] 1
1304 o 1:e016b03fd86f (draft) [ ] 1
1305 |
1305 |
1306 o 0:a78f55e5508c (draft) [ ] 0
1306 o 0:a78f55e5508c (draft) [ ] 0
1307
1307
1308 $ hg log -G --hidden
1308 $ hg log -G --hidden
1309 @ 4:b0551702f918 (draft) [tip ] 2
1309 @ 4:b0551702f918 (draft) [tip ] 2
1310 |
1310 |
1311 | x 3:f27abbcc1f77 (draft *obsolete*) [ ] temporary amend commit for e008cf283490
1311 | x 3:f27abbcc1f77 (draft *obsolete*) [ ] temporary amend commit for e008cf283490
1312 | |
1312 | |
1313 | x 2:e008cf283490 (draft *obsolete*) [ ] 2
1313 | x 2:e008cf283490 (draft *obsolete*) [ ] 2
1314 |/
1314 |/
1315 o 1:e016b03fd86f (draft) [ ] 1
1315 o 1:e016b03fd86f (draft) [ ] 1
1316 |
1316 |
1317 o 0:a78f55e5508c (draft) [ ] 0
1317 o 0:a78f55e5508c (draft) [ ] 0
1318
1318
1319
1319
1320 $ hg strip --hidden -r 2 --config extensions.strip= --config devel.strip-obsmarkers=no
1320 $ hg strip --hidden -r 2 --config extensions.strip= --config devel.strip-obsmarkers=no
1321 saved backup bundle to $TESTTMP/tmpe/issue4845/.hg/strip-backup/e008cf283490-39c978dc-backup.hg (glob)
1321 saved backup bundle to $TESTTMP/tmpe/issue4845/.hg/strip-backup/e008cf283490-39c978dc-backup.hg (glob)
1322 $ hg debugobsolete
1322 $ hg debugobsolete
1323 e008cf2834908e5d6b0f792a9d4b0e2272260fb8 b0551702f918510f01ae838ab03a463054c67b46 0 (*) {'user': 'test'} (glob)
1323 e008cf2834908e5d6b0f792a9d4b0e2272260fb8 b0551702f918510f01ae838ab03a463054c67b46 0 (*) {'user': 'test'} (glob)
1324 f27abbcc1f77fb409cf9160482fe619541e2d605 0 {e008cf2834908e5d6b0f792a9d4b0e2272260fb8} (*) {'user': 'test'} (glob)
1324 f27abbcc1f77fb409cf9160482fe619541e2d605 0 {e008cf2834908e5d6b0f792a9d4b0e2272260fb8} (*) {'user': 'test'} (glob)
1325 $ hg log -G
1325 $ hg log -G
1326 @ 2:b0551702f918 (draft) [tip ] 2
1326 @ 2:b0551702f918 (draft) [tip ] 2
1327 |
1327 |
1328 o 1:e016b03fd86f (draft) [ ] 1
1328 o 1:e016b03fd86f (draft) [ ] 1
1329 |
1329 |
1330 o 0:a78f55e5508c (draft) [ ] 0
1330 o 0:a78f55e5508c (draft) [ ] 0
1331
1331
1332 $ hg log -G --hidden
1332 $ hg log -G --hidden
1333 @ 2:b0551702f918 (draft) [tip ] 2
1333 @ 2:b0551702f918 (draft) [tip ] 2
1334 |
1334 |
1335 o 1:e016b03fd86f (draft) [ ] 1
1335 o 1:e016b03fd86f (draft) [ ] 1
1336 |
1336 |
1337 o 0:a78f55e5508c (draft) [ ] 0
1337 o 0:a78f55e5508c (draft) [ ] 0
1338
1338
1339 $ hg debugbundle .hg/strip-backup/e008cf283490-*-backup.hg
1339 $ hg debugbundle .hg/strip-backup/e008cf283490-*-backup.hg
1340 Stream params: sortdict([('Compression', 'BZ')])
1340 Stream params: sortdict([('Compression', 'BZ')])
1341 changegroup -- "sortdict([('version', '02'), ('nbchanges', '2')])"
1341 changegroup -- "sortdict([('version', '02'), ('nbchanges', '2')])"
1342 e008cf2834908e5d6b0f792a9d4b0e2272260fb8
1342 e008cf2834908e5d6b0f792a9d4b0e2272260fb8
1343 f27abbcc1f77fb409cf9160482fe619541e2d605
1343 f27abbcc1f77fb409cf9160482fe619541e2d605
1344 obsmarkers -- 'sortdict()'
1344 obsmarkers -- 'sortdict()'
1345 version: 1 (70 bytes)
1345 version: 1 (70 bytes)
1346 f27abbcc1f77fb409cf9160482fe619541e2d605 0 {e008cf2834908e5d6b0f792a9d4b0e2272260fb8} (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
1346 f27abbcc1f77fb409cf9160482fe619541e2d605 0 {e008cf2834908e5d6b0f792a9d4b0e2272260fb8} (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
1347 phase-heads -- 'sortdict()'
1347 phase-heads -- 'sortdict()'
1348 f27abbcc1f77fb409cf9160482fe619541e2d605 draft
1348 f27abbcc1f77fb409cf9160482fe619541e2d605 draft
1349
1349
1350 $ hg pull .hg/strip-backup/e008cf283490-*-backup.hg
1350 $ hg pull .hg/strip-backup/e008cf283490-*-backup.hg
1351 pulling from .hg/strip-backup/e008cf283490-39c978dc-backup.hg
1351 pulling from .hg/strip-backup/e008cf283490-39c978dc-backup.hg
1352 searching for changes
1352 searching for changes
1353 no changes found
1353 no changes found
1354 $ hg debugobsolete
1354 $ hg debugobsolete
1355 e008cf2834908e5d6b0f792a9d4b0e2272260fb8 b0551702f918510f01ae838ab03a463054c67b46 0 (*) {'user': 'test'} (glob)
1355 e008cf2834908e5d6b0f792a9d4b0e2272260fb8 b0551702f918510f01ae838ab03a463054c67b46 0 (*) {'user': 'test'} (glob)
1356 f27abbcc1f77fb409cf9160482fe619541e2d605 0 {e008cf2834908e5d6b0f792a9d4b0e2272260fb8} (*) {'user': 'test'} (glob)
1356 f27abbcc1f77fb409cf9160482fe619541e2d605 0 {e008cf2834908e5d6b0f792a9d4b0e2272260fb8} (*) {'user': 'test'} (glob)
1357 $ hg log -G
1357 $ hg log -G
1358 @ 2:b0551702f918 (draft) [tip ] 2
1358 @ 2:b0551702f918 (draft) [tip ] 2
1359 |
1359 |
1360 o 1:e016b03fd86f (draft) [ ] 1
1360 o 1:e016b03fd86f (draft) [ ] 1
1361 |
1361 |
1362 o 0:a78f55e5508c (draft) [ ] 0
1362 o 0:a78f55e5508c (draft) [ ] 0
1363
1363
1364 $ hg log -G --hidden
1364 $ hg log -G --hidden
1365 @ 2:b0551702f918 (draft) [tip ] 2
1365 @ 2:b0551702f918 (draft) [tip ] 2
1366 |
1366 |
1367 o 1:e016b03fd86f (draft) [ ] 1
1367 o 1:e016b03fd86f (draft) [ ] 1
1368 |
1368 |
1369 o 0:a78f55e5508c (draft) [ ] 0
1369 o 0:a78f55e5508c (draft) [ ] 0
1370
1370
1371
1371
1372 Testing that strip remove markers:
1372 Testing that strip remove markers:
1373
1373
1374 $ hg strip -r 1 --config extensions.strip=
1374 $ hg strip -r 1 --config extensions.strip=
1375 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
1375 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
1376 saved backup bundle to $TESTTMP/tmpe/issue4845/.hg/strip-backup/e016b03fd86f-65ede734-backup.hg (glob)
1376 saved backup bundle to $TESTTMP/tmpe/issue4845/.hg/strip-backup/e016b03fd86f-65ede734-backup.hg (glob)
1377 $ hg debugobsolete
1377 $ hg debugobsolete
1378 $ hg log -G
1378 $ hg log -G
1379 @ 0:a78f55e5508c (draft) [tip ] 0
1379 @ 0:a78f55e5508c (draft) [tip ] 0
1380
1380
1381 $ hg log -G --hidden
1381 $ hg log -G --hidden
1382 @ 0:a78f55e5508c (draft) [tip ] 0
1382 @ 0:a78f55e5508c (draft) [tip ] 0
1383
1383
1384 $ hg debugbundle .hg/strip-backup/e016b03fd86f-*-backup.hg
1384 $ hg debugbundle .hg/strip-backup/e016b03fd86f-*-backup.hg
1385 Stream params: sortdict([('Compression', 'BZ')])
1385 Stream params: sortdict([('Compression', 'BZ')])
1386 changegroup -- "sortdict([('version', '02'), ('nbchanges', '2')])"
1386 changegroup -- "sortdict([('version', '02'), ('nbchanges', '2')])"
1387 e016b03fd86fcccc54817d120b90b751aaf367d6
1387 e016b03fd86fcccc54817d120b90b751aaf367d6
1388 b0551702f918510f01ae838ab03a463054c67b46
1388 b0551702f918510f01ae838ab03a463054c67b46
1389 obsmarkers -- 'sortdict()'
1389 obsmarkers -- 'sortdict()'
1390 version: 1 (139 bytes)
1390 version: 1 (139 bytes)
1391 e008cf2834908e5d6b0f792a9d4b0e2272260fb8 b0551702f918510f01ae838ab03a463054c67b46 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
1391 e008cf2834908e5d6b0f792a9d4b0e2272260fb8 b0551702f918510f01ae838ab03a463054c67b46 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
1392 f27abbcc1f77fb409cf9160482fe619541e2d605 0 {e008cf2834908e5d6b0f792a9d4b0e2272260fb8} (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
1392 f27abbcc1f77fb409cf9160482fe619541e2d605 0 {e008cf2834908e5d6b0f792a9d4b0e2272260fb8} (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
1393 phase-heads -- 'sortdict()'
1393 phase-heads -- 'sortdict()'
1394 b0551702f918510f01ae838ab03a463054c67b46 draft
1394 b0551702f918510f01ae838ab03a463054c67b46 draft
1395
1395
1396 $ hg unbundle .hg/strip-backup/e016b03fd86f-*-backup.hg
1396 $ hg unbundle .hg/strip-backup/e016b03fd86f-*-backup.hg
1397 adding changesets
1397 adding changesets
1398 adding manifests
1398 adding manifests
1399 adding file changes
1399 adding file changes
1400 added 2 changesets with 2 changes to 2 files
1400 added 2 changesets with 2 changes to 2 files
1401 2 new obsolescence markers
1401 2 new obsolescence markers
1402 (run 'hg update' to get a working copy)
1402 (run 'hg update' to get a working copy)
1403 $ hg debugobsolete | sort
1403 $ hg debugobsolete | sort
1404 e008cf2834908e5d6b0f792a9d4b0e2272260fb8 b0551702f918510f01ae838ab03a463054c67b46 0 (*) {'user': 'test'} (glob)
1404 e008cf2834908e5d6b0f792a9d4b0e2272260fb8 b0551702f918510f01ae838ab03a463054c67b46 0 (*) {'user': 'test'} (glob)
1405 f27abbcc1f77fb409cf9160482fe619541e2d605 0 {e008cf2834908e5d6b0f792a9d4b0e2272260fb8} (*) {'user': 'test'} (glob)
1405 f27abbcc1f77fb409cf9160482fe619541e2d605 0 {e008cf2834908e5d6b0f792a9d4b0e2272260fb8} (*) {'user': 'test'} (glob)
1406 $ hg log -G
1406 $ hg log -G
1407 o 2:b0551702f918 (draft) [tip ] 2
1407 o 2:b0551702f918 (draft) [tip ] 2
1408 |
1408 |
1409 o 1:e016b03fd86f (draft) [ ] 1
1409 o 1:e016b03fd86f (draft) [ ] 1
1410 |
1410 |
1411 @ 0:a78f55e5508c (draft) [ ] 0
1411 @ 0:a78f55e5508c (draft) [ ] 0
1412
1412
1413 $ hg log -G --hidden
1413 $ hg log -G --hidden
1414 o 2:b0551702f918 (draft) [tip ] 2
1414 o 2:b0551702f918 (draft) [tip ] 2
1415 |
1415 |
1416 o 1:e016b03fd86f (draft) [ ] 1
1416 o 1:e016b03fd86f (draft) [ ] 1
1417 |
1417 |
1418 @ 0:a78f55e5508c (draft) [ ] 0
1418 @ 0:a78f55e5508c (draft) [ ] 0
1419
1419
1420 Test that 'hg debugobsolete --index --rev' can show indices of obsmarkers when
1420 Test that 'hg debugobsolete --index --rev' can show indices of obsmarkers when
1421 only a subset of those are displayed (because of --rev option)
1421 only a subset of those are displayed (because of --rev option)
1422 $ hg init doindexrev
1422 $ hg init doindexrev
1423 $ cd doindexrev
1423 $ cd doindexrev
1424 $ echo a > a
1424 $ echo a > a
1425 $ hg ci -Am a
1425 $ hg ci -Am a
1426 adding a
1426 adding a
1427 $ hg ci --amend -m aa
1427 $ hg ci --amend -m aa
1428 $ echo b > b
1428 $ echo b > b
1429 $ hg ci -Am b
1429 $ hg ci -Am b
1430 adding b
1430 adding b
1431 $ hg ci --amend -m bb
1431 $ hg ci --amend -m bb
1432 $ echo c > c
1432 $ echo c > c
1433 $ hg ci -Am c
1433 $ hg ci -Am c
1434 adding c
1434 adding c
1435 $ hg ci --amend -m cc
1435 $ hg ci --amend -m cc
1436 $ echo d > d
1436 $ echo d > d
1437 $ hg ci -Am d
1437 $ hg ci -Am d
1438 adding d
1438 adding d
1439 $ hg ci --amend -m dd --config experimental.stabilization.track-operation=1
1439 $ hg ci --amend -m dd --config experimental.stabilization.track-operation=1
1440 $ hg debugobsolete --index --rev "3+7"
1440 $ hg debugobsolete --index --rev "3+7"
1441 1 6fdef60fcbabbd3d50e9b9cbc2a240724b91a5e1 d27fb9b066076fd921277a4b9e8b9cb48c95bc6a 0 \(.*\) {'user': 'test'} (re)
1441 1 6fdef60fcbabbd3d50e9b9cbc2a240724b91a5e1 d27fb9b066076fd921277a4b9e8b9cb48c95bc6a 0 \(.*\) {'user': 'test'} (re)
1442 3 4715cf767440ed891755448016c2b8cf70760c30 7ae79c5d60f049c7b0dd02f5f25b9d60aaf7b36d 0 \(.*\) {'operation': 'amend', 'user': 'test'} (re)
1442 3 4715cf767440ed891755448016c2b8cf70760c30 7ae79c5d60f049c7b0dd02f5f25b9d60aaf7b36d 0 \(.*\) {'operation': 'amend', 'user': 'test'} (re)
1443 $ hg debugobsolete --index --rev "3+7" -Tjson
1443 $ hg debugobsolete --index --rev "3+7" -Tjson
1444 [
1444 [
1445 {
1445 {
1446 "date": [0.0, 0],
1446 "date": [0.0, 0],
1447 "flag": 0,
1447 "flag": 0,
1448 "index": 1,
1448 "index": 1,
1449 "metadata": {"user": "test"},
1449 "metadata": {"user": "test"},
1450 "precnode": "6fdef60fcbabbd3d50e9b9cbc2a240724b91a5e1",
1450 "prednode": "6fdef60fcbabbd3d50e9b9cbc2a240724b91a5e1",
1451 "succnodes": ["d27fb9b066076fd921277a4b9e8b9cb48c95bc6a"]
1451 "succnodes": ["d27fb9b066076fd921277a4b9e8b9cb48c95bc6a"]
1452 },
1452 },
1453 {
1453 {
1454 "date": [0.0, 0],
1454 "date": [0.0, 0],
1455 "flag": 0,
1455 "flag": 0,
1456 "index": 3,
1456 "index": 3,
1457 "metadata": {"operation": "amend", "user": "test"},
1457 "metadata": {"operation": "amend", "user": "test"},
1458 "precnode": "4715cf767440ed891755448016c2b8cf70760c30",
1458 "prednode": "4715cf767440ed891755448016c2b8cf70760c30",
1459 "succnodes": ["7ae79c5d60f049c7b0dd02f5f25b9d60aaf7b36d"]
1459 "succnodes": ["7ae79c5d60f049c7b0dd02f5f25b9d60aaf7b36d"]
1460 }
1460 }
1461 ]
1461 ]
1462
1462
1463 Test the --delete option of debugobsolete command
1463 Test the --delete option of debugobsolete command
1464 $ hg debugobsolete --index
1464 $ hg debugobsolete --index
1465 0 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b f9bd49731b0b175e42992a3c8fa6c678b2bc11f1 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
1465 0 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b f9bd49731b0b175e42992a3c8fa6c678b2bc11f1 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
1466 1 6fdef60fcbabbd3d50e9b9cbc2a240724b91a5e1 d27fb9b066076fd921277a4b9e8b9cb48c95bc6a 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
1466 1 6fdef60fcbabbd3d50e9b9cbc2a240724b91a5e1 d27fb9b066076fd921277a4b9e8b9cb48c95bc6a 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
1467 2 1ab51af8f9b41ef8c7f6f3312d4706d870b1fb74 29346082e4a9e27042b62d2da0e2de211c027621 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
1467 2 1ab51af8f9b41ef8c7f6f3312d4706d870b1fb74 29346082e4a9e27042b62d2da0e2de211c027621 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
1468 3 4715cf767440ed891755448016c2b8cf70760c30 7ae79c5d60f049c7b0dd02f5f25b9d60aaf7b36d 0 (Thu Jan 01 00:00:00 1970 +0000) {'operation': 'amend', 'user': 'test'}
1468 3 4715cf767440ed891755448016c2b8cf70760c30 7ae79c5d60f049c7b0dd02f5f25b9d60aaf7b36d 0 (Thu Jan 01 00:00:00 1970 +0000) {'operation': 'amend', 'user': 'test'}
1469 $ hg debugobsolete --delete 1 --delete 3
1469 $ hg debugobsolete --delete 1 --delete 3
1470 deleted 2 obsolescence markers
1470 deleted 2 obsolescence markers
1471 $ hg debugobsolete
1471 $ hg debugobsolete
1472 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b f9bd49731b0b175e42992a3c8fa6c678b2bc11f1 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
1472 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b f9bd49731b0b175e42992a3c8fa6c678b2bc11f1 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
1473 1ab51af8f9b41ef8c7f6f3312d4706d870b1fb74 29346082e4a9e27042b62d2da0e2de211c027621 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
1473 1ab51af8f9b41ef8c7f6f3312d4706d870b1fb74 29346082e4a9e27042b62d2da0e2de211c027621 0 (Thu Jan 01 00:00:00 1970 +0000) {'user': 'test'}
1474
1474
1475 Test adding changeset after obsmarkers affecting it
1475 Test adding changeset after obsmarkers affecting it
1476 (eg: during pull, or unbundle)
1476 (eg: during pull, or unbundle)
1477
1477
1478 $ mkcommit e
1478 $ mkcommit e
1479 $ hg bundle -r . --base .~1 ../bundle-2.hg
1479 $ hg bundle -r . --base .~1 ../bundle-2.hg
1480 1 changesets found
1480 1 changesets found
1481 $ getid .
1481 $ getid .
1482 $ hg --config extensions.strip= strip -r .
1482 $ hg --config extensions.strip= strip -r .
1483 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1483 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1484 saved backup bundle to $TESTTMP/tmpe/issue4845/doindexrev/.hg/strip-backup/9bc153528424-ee80edd4-backup.hg (glob)
1484 saved backup bundle to $TESTTMP/tmpe/issue4845/doindexrev/.hg/strip-backup/9bc153528424-ee80edd4-backup.hg (glob)
1485 $ hg debugobsolete 9bc153528424ea266d13e57f9ff0d799dfe61e4b
1485 $ hg debugobsolete 9bc153528424ea266d13e57f9ff0d799dfe61e4b
1486 $ hg unbundle ../bundle-2.hg
1486 $ hg unbundle ../bundle-2.hg
1487 adding changesets
1487 adding changesets
1488 adding manifests
1488 adding manifests
1489 adding file changes
1489 adding file changes
1490 added 1 changesets with 1 changes to 1 files
1490 added 1 changesets with 1 changes to 1 files
1491 (run 'hg update' to get a working copy)
1491 (run 'hg update' to get a working copy)
1492 $ hg log -G
1492 $ hg log -G
1493 @ 7:7ae79c5d60f0 (draft) [tip ] dd
1493 @ 7:7ae79c5d60f0 (draft) [tip ] dd
1494 |
1494 |
1495 | o 6:4715cf767440 (draft) [ ] d
1495 | o 6:4715cf767440 (draft) [ ] d
1496 |/
1496 |/
1497 o 5:29346082e4a9 (draft) [ ] cc
1497 o 5:29346082e4a9 (draft) [ ] cc
1498 |
1498 |
1499 o 3:d27fb9b06607 (draft) [ ] bb
1499 o 3:d27fb9b06607 (draft) [ ] bb
1500 |
1500 |
1501 | o 2:6fdef60fcbab (draft) [ ] b
1501 | o 2:6fdef60fcbab (draft) [ ] b
1502 |/
1502 |/
1503 o 1:f9bd49731b0b (draft) [ ] aa
1503 o 1:f9bd49731b0b (draft) [ ] aa
1504
1504
1505
1505
1506 $ cd ..
1506 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now