##// END OF EJS Templates
fileset: restrict getfileset() to not return a computed set (API)...
Yuya Nishihara -
r38631:760cc5dc default
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -1,2545 +1,2545 b''
1 # context.py - changeset and file context objects for mercurial
1 # context.py - changeset and file context objects for mercurial
2 #
2 #
3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2006, 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 filecmp
11 import filecmp
12 import os
12 import os
13 import stat
13 import stat
14
14
15 from .i18n import _
15 from .i18n import _
16 from .node import (
16 from .node import (
17 addednodeid,
17 addednodeid,
18 bin,
18 bin,
19 hex,
19 hex,
20 modifiednodeid,
20 modifiednodeid,
21 nullid,
21 nullid,
22 nullrev,
22 nullrev,
23 short,
23 short,
24 wdirfilenodeids,
24 wdirfilenodeids,
25 wdirid,
25 wdirid,
26 )
26 )
27 from . import (
27 from . import (
28 dagop,
28 dagop,
29 encoding,
29 encoding,
30 error,
30 error,
31 fileset,
31 fileset,
32 match as matchmod,
32 match as matchmod,
33 obsolete as obsmod,
33 obsolete as obsmod,
34 patch,
34 patch,
35 pathutil,
35 pathutil,
36 phases,
36 phases,
37 pycompat,
37 pycompat,
38 repoview,
38 repoview,
39 revlog,
39 revlog,
40 scmutil,
40 scmutil,
41 sparse,
41 sparse,
42 subrepo,
42 subrepo,
43 subrepoutil,
43 subrepoutil,
44 util,
44 util,
45 )
45 )
46 from .utils import (
46 from .utils import (
47 dateutil,
47 dateutil,
48 stringutil,
48 stringutil,
49 )
49 )
50
50
51 propertycache = util.propertycache
51 propertycache = util.propertycache
52
52
53 class basectx(object):
53 class basectx(object):
54 """A basectx object represents the common logic for its children:
54 """A basectx object represents the common logic for its children:
55 changectx: read-only context that is already present in the repo,
55 changectx: read-only context that is already present in the repo,
56 workingctx: a context that represents the working directory and can
56 workingctx: a context that represents the working directory and can
57 be committed,
57 be committed,
58 memctx: a context that represents changes in-memory and can also
58 memctx: a context that represents changes in-memory and can also
59 be committed."""
59 be committed."""
60
60
61 def __init__(self, repo):
61 def __init__(self, repo):
62 self._repo = repo
62 self._repo = repo
63
63
64 def __bytes__(self):
64 def __bytes__(self):
65 return short(self.node())
65 return short(self.node())
66
66
67 __str__ = encoding.strmethod(__bytes__)
67 __str__ = encoding.strmethod(__bytes__)
68
68
69 def __repr__(self):
69 def __repr__(self):
70 return r"<%s %s>" % (type(self).__name__, str(self))
70 return r"<%s %s>" % (type(self).__name__, str(self))
71
71
72 def __eq__(self, other):
72 def __eq__(self, other):
73 try:
73 try:
74 return type(self) == type(other) and self._rev == other._rev
74 return type(self) == type(other) and self._rev == other._rev
75 except AttributeError:
75 except AttributeError:
76 return False
76 return False
77
77
78 def __ne__(self, other):
78 def __ne__(self, other):
79 return not (self == other)
79 return not (self == other)
80
80
81 def __contains__(self, key):
81 def __contains__(self, key):
82 return key in self._manifest
82 return key in self._manifest
83
83
84 def __getitem__(self, key):
84 def __getitem__(self, key):
85 return self.filectx(key)
85 return self.filectx(key)
86
86
87 def __iter__(self):
87 def __iter__(self):
88 return iter(self._manifest)
88 return iter(self._manifest)
89
89
90 def _buildstatusmanifest(self, status):
90 def _buildstatusmanifest(self, status):
91 """Builds a manifest that includes the given status results, if this is
91 """Builds a manifest that includes the given status results, if this is
92 a working copy context. For non-working copy contexts, it just returns
92 a working copy context. For non-working copy contexts, it just returns
93 the normal manifest."""
93 the normal manifest."""
94 return self.manifest()
94 return self.manifest()
95
95
96 def _matchstatus(self, other, match):
96 def _matchstatus(self, other, match):
97 """This internal method provides a way for child objects to override the
97 """This internal method provides a way for child objects to override the
98 match operator.
98 match operator.
99 """
99 """
100 return match
100 return match
101
101
102 def _buildstatus(self, other, s, match, listignored, listclean,
102 def _buildstatus(self, other, s, match, listignored, listclean,
103 listunknown):
103 listunknown):
104 """build a status with respect to another context"""
104 """build a status with respect to another context"""
105 # Load earliest manifest first for caching reasons. More specifically,
105 # Load earliest manifest first for caching reasons. More specifically,
106 # if you have revisions 1000 and 1001, 1001 is probably stored as a
106 # if you have revisions 1000 and 1001, 1001 is probably stored as a
107 # delta against 1000. Thus, if you read 1000 first, we'll reconstruct
107 # delta against 1000. Thus, if you read 1000 first, we'll reconstruct
108 # 1000 and cache it so that when you read 1001, we just need to apply a
108 # 1000 and cache it so that when you read 1001, we just need to apply a
109 # delta to what's in the cache. So that's one full reconstruction + one
109 # delta to what's in the cache. So that's one full reconstruction + one
110 # delta application.
110 # delta application.
111 mf2 = None
111 mf2 = None
112 if self.rev() is not None and self.rev() < other.rev():
112 if self.rev() is not None and self.rev() < other.rev():
113 mf2 = self._buildstatusmanifest(s)
113 mf2 = self._buildstatusmanifest(s)
114 mf1 = other._buildstatusmanifest(s)
114 mf1 = other._buildstatusmanifest(s)
115 if mf2 is None:
115 if mf2 is None:
116 mf2 = self._buildstatusmanifest(s)
116 mf2 = self._buildstatusmanifest(s)
117
117
118 modified, added = [], []
118 modified, added = [], []
119 removed = []
119 removed = []
120 clean = []
120 clean = []
121 deleted, unknown, ignored = s.deleted, s.unknown, s.ignored
121 deleted, unknown, ignored = s.deleted, s.unknown, s.ignored
122 deletedset = set(deleted)
122 deletedset = set(deleted)
123 d = mf1.diff(mf2, match=match, clean=listclean)
123 d = mf1.diff(mf2, match=match, clean=listclean)
124 for fn, value in d.iteritems():
124 for fn, value in d.iteritems():
125 if fn in deletedset:
125 if fn in deletedset:
126 continue
126 continue
127 if value is None:
127 if value is None:
128 clean.append(fn)
128 clean.append(fn)
129 continue
129 continue
130 (node1, flag1), (node2, flag2) = value
130 (node1, flag1), (node2, flag2) = value
131 if node1 is None:
131 if node1 is None:
132 added.append(fn)
132 added.append(fn)
133 elif node2 is None:
133 elif node2 is None:
134 removed.append(fn)
134 removed.append(fn)
135 elif flag1 != flag2:
135 elif flag1 != flag2:
136 modified.append(fn)
136 modified.append(fn)
137 elif node2 not in wdirfilenodeids:
137 elif node2 not in wdirfilenodeids:
138 # When comparing files between two commits, we save time by
138 # When comparing files between two commits, we save time by
139 # not comparing the file contents when the nodeids differ.
139 # not comparing the file contents when the nodeids differ.
140 # Note that this means we incorrectly report a reverted change
140 # Note that this means we incorrectly report a reverted change
141 # to a file as a modification.
141 # to a file as a modification.
142 modified.append(fn)
142 modified.append(fn)
143 elif self[fn].cmp(other[fn]):
143 elif self[fn].cmp(other[fn]):
144 modified.append(fn)
144 modified.append(fn)
145 else:
145 else:
146 clean.append(fn)
146 clean.append(fn)
147
147
148 if removed:
148 if removed:
149 # need to filter files if they are already reported as removed
149 # need to filter files if they are already reported as removed
150 unknown = [fn for fn in unknown if fn not in mf1 and
150 unknown = [fn for fn in unknown if fn not in mf1 and
151 (not match or match(fn))]
151 (not match or match(fn))]
152 ignored = [fn for fn in ignored if fn not in mf1 and
152 ignored = [fn for fn in ignored if fn not in mf1 and
153 (not match or match(fn))]
153 (not match or match(fn))]
154 # if they're deleted, don't report them as removed
154 # if they're deleted, don't report them as removed
155 removed = [fn for fn in removed if fn not in deletedset]
155 removed = [fn for fn in removed if fn not in deletedset]
156
156
157 return scmutil.status(modified, added, removed, deleted, unknown,
157 return scmutil.status(modified, added, removed, deleted, unknown,
158 ignored, clean)
158 ignored, clean)
159
159
160 @propertycache
160 @propertycache
161 def substate(self):
161 def substate(self):
162 return subrepoutil.state(self, self._repo.ui)
162 return subrepoutil.state(self, self._repo.ui)
163
163
164 def subrev(self, subpath):
164 def subrev(self, subpath):
165 return self.substate[subpath][1]
165 return self.substate[subpath][1]
166
166
167 def rev(self):
167 def rev(self):
168 return self._rev
168 return self._rev
169 def node(self):
169 def node(self):
170 return self._node
170 return self._node
171 def hex(self):
171 def hex(self):
172 return hex(self.node())
172 return hex(self.node())
173 def manifest(self):
173 def manifest(self):
174 return self._manifest
174 return self._manifest
175 def manifestctx(self):
175 def manifestctx(self):
176 return self._manifestctx
176 return self._manifestctx
177 def repo(self):
177 def repo(self):
178 return self._repo
178 return self._repo
179 def phasestr(self):
179 def phasestr(self):
180 return phases.phasenames[self.phase()]
180 return phases.phasenames[self.phase()]
181 def mutable(self):
181 def mutable(self):
182 return self.phase() > phases.public
182 return self.phase() > phases.public
183
183
184 def getfileset(self, expr):
184 def matchfileset(self, expr, badfn=None):
185 return fileset.getfileset(self, expr)
185 return fileset.match(self, expr, badfn=badfn)
186
186
187 def obsolete(self):
187 def obsolete(self):
188 """True if the changeset is obsolete"""
188 """True if the changeset is obsolete"""
189 return self.rev() in obsmod.getrevs(self._repo, 'obsolete')
189 return self.rev() in obsmod.getrevs(self._repo, 'obsolete')
190
190
191 def extinct(self):
191 def extinct(self):
192 """True if the changeset is extinct"""
192 """True if the changeset is extinct"""
193 return self.rev() in obsmod.getrevs(self._repo, 'extinct')
193 return self.rev() in obsmod.getrevs(self._repo, 'extinct')
194
194
195 def orphan(self):
195 def orphan(self):
196 """True if the changeset is not obsolete but it's ancestor are"""
196 """True if the changeset is not obsolete but it's ancestor are"""
197 return self.rev() in obsmod.getrevs(self._repo, 'orphan')
197 return self.rev() in obsmod.getrevs(self._repo, 'orphan')
198
198
199 def phasedivergent(self):
199 def phasedivergent(self):
200 """True if the changeset try to be a successor of a public changeset
200 """True if the changeset try to be a successor of a public changeset
201
201
202 Only non-public and non-obsolete changesets may be bumped.
202 Only non-public and non-obsolete changesets may be bumped.
203 """
203 """
204 return self.rev() in obsmod.getrevs(self._repo, 'phasedivergent')
204 return self.rev() in obsmod.getrevs(self._repo, 'phasedivergent')
205
205
206 def contentdivergent(self):
206 def contentdivergent(self):
207 """Is a successors of a changeset with multiple possible successors set
207 """Is a successors of a changeset with multiple possible successors set
208
208
209 Only non-public and non-obsolete changesets may be divergent.
209 Only non-public and non-obsolete changesets may be divergent.
210 """
210 """
211 return self.rev() in obsmod.getrevs(self._repo, 'contentdivergent')
211 return self.rev() in obsmod.getrevs(self._repo, 'contentdivergent')
212
212
213 def isunstable(self):
213 def isunstable(self):
214 """True if the changeset is either unstable, bumped or divergent"""
214 """True if the changeset is either unstable, bumped or divergent"""
215 return self.orphan() or self.phasedivergent() or self.contentdivergent()
215 return self.orphan() or self.phasedivergent() or self.contentdivergent()
216
216
217 def instabilities(self):
217 def instabilities(self):
218 """return the list of instabilities affecting this changeset.
218 """return the list of instabilities affecting this changeset.
219
219
220 Instabilities are returned as strings. possible values are:
220 Instabilities are returned as strings. possible values are:
221 - orphan,
221 - orphan,
222 - phase-divergent,
222 - phase-divergent,
223 - content-divergent.
223 - content-divergent.
224 """
224 """
225 instabilities = []
225 instabilities = []
226 if self.orphan():
226 if self.orphan():
227 instabilities.append('orphan')
227 instabilities.append('orphan')
228 if self.phasedivergent():
228 if self.phasedivergent():
229 instabilities.append('phase-divergent')
229 instabilities.append('phase-divergent')
230 if self.contentdivergent():
230 if self.contentdivergent():
231 instabilities.append('content-divergent')
231 instabilities.append('content-divergent')
232 return instabilities
232 return instabilities
233
233
234 def parents(self):
234 def parents(self):
235 """return contexts for each parent changeset"""
235 """return contexts for each parent changeset"""
236 return self._parents
236 return self._parents
237
237
238 def p1(self):
238 def p1(self):
239 return self._parents[0]
239 return self._parents[0]
240
240
241 def p2(self):
241 def p2(self):
242 parents = self._parents
242 parents = self._parents
243 if len(parents) == 2:
243 if len(parents) == 2:
244 return parents[1]
244 return parents[1]
245 return changectx(self._repo, nullrev)
245 return changectx(self._repo, nullrev)
246
246
247 def _fileinfo(self, path):
247 def _fileinfo(self, path):
248 if r'_manifest' in self.__dict__:
248 if r'_manifest' in self.__dict__:
249 try:
249 try:
250 return self._manifest[path], self._manifest.flags(path)
250 return self._manifest[path], self._manifest.flags(path)
251 except KeyError:
251 except KeyError:
252 raise error.ManifestLookupError(self._node, path,
252 raise error.ManifestLookupError(self._node, path,
253 _('not found in manifest'))
253 _('not found in manifest'))
254 if r'_manifestdelta' in self.__dict__ or path in self.files():
254 if r'_manifestdelta' in self.__dict__ or path in self.files():
255 if path in self._manifestdelta:
255 if path in self._manifestdelta:
256 return (self._manifestdelta[path],
256 return (self._manifestdelta[path],
257 self._manifestdelta.flags(path))
257 self._manifestdelta.flags(path))
258 mfl = self._repo.manifestlog
258 mfl = self._repo.manifestlog
259 try:
259 try:
260 node, flag = mfl[self._changeset.manifest].find(path)
260 node, flag = mfl[self._changeset.manifest].find(path)
261 except KeyError:
261 except KeyError:
262 raise error.ManifestLookupError(self._node, path,
262 raise error.ManifestLookupError(self._node, path,
263 _('not found in manifest'))
263 _('not found in manifest'))
264
264
265 return node, flag
265 return node, flag
266
266
267 def filenode(self, path):
267 def filenode(self, path):
268 return self._fileinfo(path)[0]
268 return self._fileinfo(path)[0]
269
269
270 def flags(self, path):
270 def flags(self, path):
271 try:
271 try:
272 return self._fileinfo(path)[1]
272 return self._fileinfo(path)[1]
273 except error.LookupError:
273 except error.LookupError:
274 return ''
274 return ''
275
275
276 def sub(self, path, allowcreate=True):
276 def sub(self, path, allowcreate=True):
277 '''return a subrepo for the stored revision of path, never wdir()'''
277 '''return a subrepo for the stored revision of path, never wdir()'''
278 return subrepo.subrepo(self, path, allowcreate=allowcreate)
278 return subrepo.subrepo(self, path, allowcreate=allowcreate)
279
279
280 def nullsub(self, path, pctx):
280 def nullsub(self, path, pctx):
281 return subrepo.nullsubrepo(self, path, pctx)
281 return subrepo.nullsubrepo(self, path, pctx)
282
282
283 def workingsub(self, path):
283 def workingsub(self, path):
284 '''return a subrepo for the stored revision, or wdir if this is a wdir
284 '''return a subrepo for the stored revision, or wdir if this is a wdir
285 context.
285 context.
286 '''
286 '''
287 return subrepo.subrepo(self, path, allowwdir=True)
287 return subrepo.subrepo(self, path, allowwdir=True)
288
288
289 def match(self, pats=None, include=None, exclude=None, default='glob',
289 def match(self, pats=None, include=None, exclude=None, default='glob',
290 listsubrepos=False, badfn=None):
290 listsubrepos=False, badfn=None):
291 r = self._repo
291 r = self._repo
292 return matchmod.match(r.root, r.getcwd(), pats,
292 return matchmod.match(r.root, r.getcwd(), pats,
293 include, exclude, default,
293 include, exclude, default,
294 auditor=r.nofsauditor, ctx=self,
294 auditor=r.nofsauditor, ctx=self,
295 listsubrepos=listsubrepos, badfn=badfn)
295 listsubrepos=listsubrepos, badfn=badfn)
296
296
297 def diff(self, ctx2=None, match=None, changes=None, opts=None,
297 def diff(self, ctx2=None, match=None, changes=None, opts=None,
298 losedatafn=None, prefix='', relroot='', copy=None,
298 losedatafn=None, prefix='', relroot='', copy=None,
299 hunksfilterfn=None):
299 hunksfilterfn=None):
300 """Returns a diff generator for the given contexts and matcher"""
300 """Returns a diff generator for the given contexts and matcher"""
301 if ctx2 is None:
301 if ctx2 is None:
302 ctx2 = self.p1()
302 ctx2 = self.p1()
303 if ctx2 is not None:
303 if ctx2 is not None:
304 ctx2 = self._repo[ctx2]
304 ctx2 = self._repo[ctx2]
305 return patch.diff(self._repo, ctx2, self, match=match, changes=changes,
305 return patch.diff(self._repo, ctx2, self, match=match, changes=changes,
306 opts=opts, losedatafn=losedatafn, prefix=prefix,
306 opts=opts, losedatafn=losedatafn, prefix=prefix,
307 relroot=relroot, copy=copy,
307 relroot=relroot, copy=copy,
308 hunksfilterfn=hunksfilterfn)
308 hunksfilterfn=hunksfilterfn)
309
309
310 def dirs(self):
310 def dirs(self):
311 return self._manifest.dirs()
311 return self._manifest.dirs()
312
312
313 def hasdir(self, dir):
313 def hasdir(self, dir):
314 return self._manifest.hasdir(dir)
314 return self._manifest.hasdir(dir)
315
315
316 def status(self, other=None, match=None, listignored=False,
316 def status(self, other=None, match=None, listignored=False,
317 listclean=False, listunknown=False, listsubrepos=False):
317 listclean=False, listunknown=False, listsubrepos=False):
318 """return status of files between two nodes or node and working
318 """return status of files between two nodes or node and working
319 directory.
319 directory.
320
320
321 If other is None, compare this node with working directory.
321 If other is None, compare this node with working directory.
322
322
323 returns (modified, added, removed, deleted, unknown, ignored, clean)
323 returns (modified, added, removed, deleted, unknown, ignored, clean)
324 """
324 """
325
325
326 ctx1 = self
326 ctx1 = self
327 ctx2 = self._repo[other]
327 ctx2 = self._repo[other]
328
328
329 # This next code block is, admittedly, fragile logic that tests for
329 # This next code block is, admittedly, fragile logic that tests for
330 # reversing the contexts and wouldn't need to exist if it weren't for
330 # reversing the contexts and wouldn't need to exist if it weren't for
331 # the fast (and common) code path of comparing the working directory
331 # the fast (and common) code path of comparing the working directory
332 # with its first parent.
332 # with its first parent.
333 #
333 #
334 # What we're aiming for here is the ability to call:
334 # What we're aiming for here is the ability to call:
335 #
335 #
336 # workingctx.status(parentctx)
336 # workingctx.status(parentctx)
337 #
337 #
338 # If we always built the manifest for each context and compared those,
338 # If we always built the manifest for each context and compared those,
339 # then we'd be done. But the special case of the above call means we
339 # then we'd be done. But the special case of the above call means we
340 # just copy the manifest of the parent.
340 # just copy the manifest of the parent.
341 reversed = False
341 reversed = False
342 if (not isinstance(ctx1, changectx)
342 if (not isinstance(ctx1, changectx)
343 and isinstance(ctx2, changectx)):
343 and isinstance(ctx2, changectx)):
344 reversed = True
344 reversed = True
345 ctx1, ctx2 = ctx2, ctx1
345 ctx1, ctx2 = ctx2, ctx1
346
346
347 match = match or matchmod.always(self._repo.root, self._repo.getcwd())
347 match = match or matchmod.always(self._repo.root, self._repo.getcwd())
348 match = ctx2._matchstatus(ctx1, match)
348 match = ctx2._matchstatus(ctx1, match)
349 r = scmutil.status([], [], [], [], [], [], [])
349 r = scmutil.status([], [], [], [], [], [], [])
350 r = ctx2._buildstatus(ctx1, r, match, listignored, listclean,
350 r = ctx2._buildstatus(ctx1, r, match, listignored, listclean,
351 listunknown)
351 listunknown)
352
352
353 if reversed:
353 if reversed:
354 # Reverse added and removed. Clear deleted, unknown and ignored as
354 # Reverse added and removed. Clear deleted, unknown and ignored as
355 # these make no sense to reverse.
355 # these make no sense to reverse.
356 r = scmutil.status(r.modified, r.removed, r.added, [], [], [],
356 r = scmutil.status(r.modified, r.removed, r.added, [], [], [],
357 r.clean)
357 r.clean)
358
358
359 if listsubrepos:
359 if listsubrepos:
360 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
360 for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
361 try:
361 try:
362 rev2 = ctx2.subrev(subpath)
362 rev2 = ctx2.subrev(subpath)
363 except KeyError:
363 except KeyError:
364 # A subrepo that existed in node1 was deleted between
364 # A subrepo that existed in node1 was deleted between
365 # node1 and node2 (inclusive). Thus, ctx2's substate
365 # node1 and node2 (inclusive). Thus, ctx2's substate
366 # won't contain that subpath. The best we can do ignore it.
366 # won't contain that subpath. The best we can do ignore it.
367 rev2 = None
367 rev2 = None
368 submatch = matchmod.subdirmatcher(subpath, match)
368 submatch = matchmod.subdirmatcher(subpath, match)
369 s = sub.status(rev2, match=submatch, ignored=listignored,
369 s = sub.status(rev2, match=submatch, ignored=listignored,
370 clean=listclean, unknown=listunknown,
370 clean=listclean, unknown=listunknown,
371 listsubrepos=True)
371 listsubrepos=True)
372 for rfiles, sfiles in zip(r, s):
372 for rfiles, sfiles in zip(r, s):
373 rfiles.extend("%s/%s" % (subpath, f) for f in sfiles)
373 rfiles.extend("%s/%s" % (subpath, f) for f in sfiles)
374
374
375 for l in r:
375 for l in r:
376 l.sort()
376 l.sort()
377
377
378 return r
378 return r
379
379
380 class changectx(basectx):
380 class changectx(basectx):
381 """A changecontext object makes access to data related to a particular
381 """A changecontext object makes access to data related to a particular
382 changeset convenient. It represents a read-only context already present in
382 changeset convenient. It represents a read-only context already present in
383 the repo."""
383 the repo."""
384 def __init__(self, repo, changeid='.'):
384 def __init__(self, repo, changeid='.'):
385 """changeid is a revision number, node, or tag"""
385 """changeid is a revision number, node, or tag"""
386 super(changectx, self).__init__(repo)
386 super(changectx, self).__init__(repo)
387
387
388 try:
388 try:
389 if isinstance(changeid, int):
389 if isinstance(changeid, int):
390 self._node = repo.changelog.node(changeid)
390 self._node = repo.changelog.node(changeid)
391 self._rev = changeid
391 self._rev = changeid
392 return
392 return
393 elif changeid == 'null':
393 elif changeid == 'null':
394 self._node = nullid
394 self._node = nullid
395 self._rev = nullrev
395 self._rev = nullrev
396 return
396 return
397 elif changeid == 'tip':
397 elif changeid == 'tip':
398 self._node = repo.changelog.tip()
398 self._node = repo.changelog.tip()
399 self._rev = repo.changelog.rev(self._node)
399 self._rev = repo.changelog.rev(self._node)
400 return
400 return
401 elif (changeid == '.'
401 elif (changeid == '.'
402 or repo.local() and changeid == repo.dirstate.p1()):
402 or repo.local() and changeid == repo.dirstate.p1()):
403 # this is a hack to delay/avoid loading obsmarkers
403 # this is a hack to delay/avoid loading obsmarkers
404 # when we know that '.' won't be hidden
404 # when we know that '.' won't be hidden
405 self._node = repo.dirstate.p1()
405 self._node = repo.dirstate.p1()
406 self._rev = repo.unfiltered().changelog.rev(self._node)
406 self._rev = repo.unfiltered().changelog.rev(self._node)
407 return
407 return
408 elif len(changeid) == 20:
408 elif len(changeid) == 20:
409 try:
409 try:
410 self._node = changeid
410 self._node = changeid
411 self._rev = repo.changelog.rev(changeid)
411 self._rev = repo.changelog.rev(changeid)
412 return
412 return
413 except error.FilteredLookupError:
413 except error.FilteredLookupError:
414 raise
414 raise
415 except LookupError:
415 except LookupError:
416 # check if it might have come from damaged dirstate
416 # check if it might have come from damaged dirstate
417 #
417 #
418 # XXX we could avoid the unfiltered if we had a recognizable
418 # XXX we could avoid the unfiltered if we had a recognizable
419 # exception for filtered changeset access
419 # exception for filtered changeset access
420 if (repo.local()
420 if (repo.local()
421 and changeid in repo.unfiltered().dirstate.parents()):
421 and changeid in repo.unfiltered().dirstate.parents()):
422 msg = _("working directory has unknown parent '%s'!")
422 msg = _("working directory has unknown parent '%s'!")
423 raise error.Abort(msg % short(changeid))
423 raise error.Abort(msg % short(changeid))
424 changeid = hex(changeid) # for the error message
424 changeid = hex(changeid) # for the error message
425
425
426 elif len(changeid) == 40:
426 elif len(changeid) == 40:
427 try:
427 try:
428 self._node = bin(changeid)
428 self._node = bin(changeid)
429 self._rev = repo.changelog.rev(self._node)
429 self._rev = repo.changelog.rev(self._node)
430 return
430 return
431 except error.FilteredLookupError:
431 except error.FilteredLookupError:
432 raise
432 raise
433 except (TypeError, LookupError):
433 except (TypeError, LookupError):
434 pass
434 pass
435 else:
435 else:
436 raise error.ProgrammingError(
436 raise error.ProgrammingError(
437 "unsupported changeid '%s' of type %s" %
437 "unsupported changeid '%s' of type %s" %
438 (changeid, type(changeid)))
438 (changeid, type(changeid)))
439
439
440 # lookup failed
440 # lookup failed
441 except (error.FilteredIndexError, error.FilteredLookupError):
441 except (error.FilteredIndexError, error.FilteredLookupError):
442 raise error.FilteredRepoLookupError(_("filtered revision '%s'")
442 raise error.FilteredRepoLookupError(_("filtered revision '%s'")
443 % pycompat.bytestr(changeid))
443 % pycompat.bytestr(changeid))
444 except error.FilteredRepoLookupError:
444 except error.FilteredRepoLookupError:
445 raise
445 raise
446 except IndexError:
446 except IndexError:
447 pass
447 pass
448 raise error.RepoLookupError(
448 raise error.RepoLookupError(
449 _("unknown revision '%s'") % changeid)
449 _("unknown revision '%s'") % changeid)
450
450
451 def __hash__(self):
451 def __hash__(self):
452 try:
452 try:
453 return hash(self._rev)
453 return hash(self._rev)
454 except AttributeError:
454 except AttributeError:
455 return id(self)
455 return id(self)
456
456
457 def __nonzero__(self):
457 def __nonzero__(self):
458 return self._rev != nullrev
458 return self._rev != nullrev
459
459
460 __bool__ = __nonzero__
460 __bool__ = __nonzero__
461
461
462 @propertycache
462 @propertycache
463 def _changeset(self):
463 def _changeset(self):
464 return self._repo.changelog.changelogrevision(self.rev())
464 return self._repo.changelog.changelogrevision(self.rev())
465
465
466 @propertycache
466 @propertycache
467 def _manifest(self):
467 def _manifest(self):
468 return self._manifestctx.read()
468 return self._manifestctx.read()
469
469
470 @property
470 @property
471 def _manifestctx(self):
471 def _manifestctx(self):
472 return self._repo.manifestlog[self._changeset.manifest]
472 return self._repo.manifestlog[self._changeset.manifest]
473
473
474 @propertycache
474 @propertycache
475 def _manifestdelta(self):
475 def _manifestdelta(self):
476 return self._manifestctx.readdelta()
476 return self._manifestctx.readdelta()
477
477
478 @propertycache
478 @propertycache
479 def _parents(self):
479 def _parents(self):
480 repo = self._repo
480 repo = self._repo
481 p1, p2 = repo.changelog.parentrevs(self._rev)
481 p1, p2 = repo.changelog.parentrevs(self._rev)
482 if p2 == nullrev:
482 if p2 == nullrev:
483 return [changectx(repo, p1)]
483 return [changectx(repo, p1)]
484 return [changectx(repo, p1), changectx(repo, p2)]
484 return [changectx(repo, p1), changectx(repo, p2)]
485
485
486 def changeset(self):
486 def changeset(self):
487 c = self._changeset
487 c = self._changeset
488 return (
488 return (
489 c.manifest,
489 c.manifest,
490 c.user,
490 c.user,
491 c.date,
491 c.date,
492 c.files,
492 c.files,
493 c.description,
493 c.description,
494 c.extra,
494 c.extra,
495 )
495 )
496 def manifestnode(self):
496 def manifestnode(self):
497 return self._changeset.manifest
497 return self._changeset.manifest
498
498
499 def user(self):
499 def user(self):
500 return self._changeset.user
500 return self._changeset.user
501 def date(self):
501 def date(self):
502 return self._changeset.date
502 return self._changeset.date
503 def files(self):
503 def files(self):
504 return self._changeset.files
504 return self._changeset.files
505 def description(self):
505 def description(self):
506 return self._changeset.description
506 return self._changeset.description
507 def branch(self):
507 def branch(self):
508 return encoding.tolocal(self._changeset.extra.get("branch"))
508 return encoding.tolocal(self._changeset.extra.get("branch"))
509 def closesbranch(self):
509 def closesbranch(self):
510 return 'close' in self._changeset.extra
510 return 'close' in self._changeset.extra
511 def extra(self):
511 def extra(self):
512 """Return a dict of extra information."""
512 """Return a dict of extra information."""
513 return self._changeset.extra
513 return self._changeset.extra
514 def tags(self):
514 def tags(self):
515 """Return a list of byte tag names"""
515 """Return a list of byte tag names"""
516 return self._repo.nodetags(self._node)
516 return self._repo.nodetags(self._node)
517 def bookmarks(self):
517 def bookmarks(self):
518 """Return a list of byte bookmark names."""
518 """Return a list of byte bookmark names."""
519 return self._repo.nodebookmarks(self._node)
519 return self._repo.nodebookmarks(self._node)
520 def phase(self):
520 def phase(self):
521 return self._repo._phasecache.phase(self._repo, self._rev)
521 return self._repo._phasecache.phase(self._repo, self._rev)
522 def hidden(self):
522 def hidden(self):
523 return self._rev in repoview.filterrevs(self._repo, 'visible')
523 return self._rev in repoview.filterrevs(self._repo, 'visible')
524
524
525 def isinmemory(self):
525 def isinmemory(self):
526 return False
526 return False
527
527
528 def children(self):
528 def children(self):
529 """return list of changectx contexts for each child changeset.
529 """return list of changectx contexts for each child changeset.
530
530
531 This returns only the immediate child changesets. Use descendants() to
531 This returns only the immediate child changesets. Use descendants() to
532 recursively walk children.
532 recursively walk children.
533 """
533 """
534 c = self._repo.changelog.children(self._node)
534 c = self._repo.changelog.children(self._node)
535 return [changectx(self._repo, x) for x in c]
535 return [changectx(self._repo, x) for x in c]
536
536
537 def ancestors(self):
537 def ancestors(self):
538 for a in self._repo.changelog.ancestors([self._rev]):
538 for a in self._repo.changelog.ancestors([self._rev]):
539 yield changectx(self._repo, a)
539 yield changectx(self._repo, a)
540
540
541 def descendants(self):
541 def descendants(self):
542 """Recursively yield all children of the changeset.
542 """Recursively yield all children of the changeset.
543
543
544 For just the immediate children, use children()
544 For just the immediate children, use children()
545 """
545 """
546 for d in self._repo.changelog.descendants([self._rev]):
546 for d in self._repo.changelog.descendants([self._rev]):
547 yield changectx(self._repo, d)
547 yield changectx(self._repo, d)
548
548
549 def filectx(self, path, fileid=None, filelog=None):
549 def filectx(self, path, fileid=None, filelog=None):
550 """get a file context from this changeset"""
550 """get a file context from this changeset"""
551 if fileid is None:
551 if fileid is None:
552 fileid = self.filenode(path)
552 fileid = self.filenode(path)
553 return filectx(self._repo, path, fileid=fileid,
553 return filectx(self._repo, path, fileid=fileid,
554 changectx=self, filelog=filelog)
554 changectx=self, filelog=filelog)
555
555
556 def ancestor(self, c2, warn=False):
556 def ancestor(self, c2, warn=False):
557 """return the "best" ancestor context of self and c2
557 """return the "best" ancestor context of self and c2
558
558
559 If there are multiple candidates, it will show a message and check
559 If there are multiple candidates, it will show a message and check
560 merge.preferancestor configuration before falling back to the
560 merge.preferancestor configuration before falling back to the
561 revlog ancestor."""
561 revlog ancestor."""
562 # deal with workingctxs
562 # deal with workingctxs
563 n2 = c2._node
563 n2 = c2._node
564 if n2 is None:
564 if n2 is None:
565 n2 = c2._parents[0]._node
565 n2 = c2._parents[0]._node
566 cahs = self._repo.changelog.commonancestorsheads(self._node, n2)
566 cahs = self._repo.changelog.commonancestorsheads(self._node, n2)
567 if not cahs:
567 if not cahs:
568 anc = nullid
568 anc = nullid
569 elif len(cahs) == 1:
569 elif len(cahs) == 1:
570 anc = cahs[0]
570 anc = cahs[0]
571 else:
571 else:
572 # experimental config: merge.preferancestor
572 # experimental config: merge.preferancestor
573 for r in self._repo.ui.configlist('merge', 'preferancestor'):
573 for r in self._repo.ui.configlist('merge', 'preferancestor'):
574 try:
574 try:
575 ctx = scmutil.revsymbol(self._repo, r)
575 ctx = scmutil.revsymbol(self._repo, r)
576 except error.RepoLookupError:
576 except error.RepoLookupError:
577 continue
577 continue
578 anc = ctx.node()
578 anc = ctx.node()
579 if anc in cahs:
579 if anc in cahs:
580 break
580 break
581 else:
581 else:
582 anc = self._repo.changelog.ancestor(self._node, n2)
582 anc = self._repo.changelog.ancestor(self._node, n2)
583 if warn:
583 if warn:
584 self._repo.ui.status(
584 self._repo.ui.status(
585 (_("note: using %s as ancestor of %s and %s\n") %
585 (_("note: using %s as ancestor of %s and %s\n") %
586 (short(anc), short(self._node), short(n2))) +
586 (short(anc), short(self._node), short(n2))) +
587 ''.join(_(" alternatively, use --config "
587 ''.join(_(" alternatively, use --config "
588 "merge.preferancestor=%s\n") %
588 "merge.preferancestor=%s\n") %
589 short(n) for n in sorted(cahs) if n != anc))
589 short(n) for n in sorted(cahs) if n != anc))
590 return changectx(self._repo, anc)
590 return changectx(self._repo, anc)
591
591
592 def descendant(self, other):
592 def descendant(self, other):
593 """True if other is descendant of this changeset"""
593 """True if other is descendant of this changeset"""
594 return self._repo.changelog.descendant(self._rev, other._rev)
594 return self._repo.changelog.descendant(self._rev, other._rev)
595
595
596 def walk(self, match):
596 def walk(self, match):
597 '''Generates matching file names.'''
597 '''Generates matching file names.'''
598
598
599 # Wrap match.bad method to have message with nodeid
599 # Wrap match.bad method to have message with nodeid
600 def bad(fn, msg):
600 def bad(fn, msg):
601 # The manifest doesn't know about subrepos, so don't complain about
601 # The manifest doesn't know about subrepos, so don't complain about
602 # paths into valid subrepos.
602 # paths into valid subrepos.
603 if any(fn == s or fn.startswith(s + '/')
603 if any(fn == s or fn.startswith(s + '/')
604 for s in self.substate):
604 for s in self.substate):
605 return
605 return
606 match.bad(fn, _('no such file in rev %s') % self)
606 match.bad(fn, _('no such file in rev %s') % self)
607
607
608 m = matchmod.badmatch(match, bad)
608 m = matchmod.badmatch(match, bad)
609 return self._manifest.walk(m)
609 return self._manifest.walk(m)
610
610
611 def matches(self, match):
611 def matches(self, match):
612 return self.walk(match)
612 return self.walk(match)
613
613
614 class basefilectx(object):
614 class basefilectx(object):
615 """A filecontext object represents the common logic for its children:
615 """A filecontext object represents the common logic for its children:
616 filectx: read-only access to a filerevision that is already present
616 filectx: read-only access to a filerevision that is already present
617 in the repo,
617 in the repo,
618 workingfilectx: a filecontext that represents files from the working
618 workingfilectx: a filecontext that represents files from the working
619 directory,
619 directory,
620 memfilectx: a filecontext that represents files in-memory,
620 memfilectx: a filecontext that represents files in-memory,
621 overlayfilectx: duplicate another filecontext with some fields overridden.
621 overlayfilectx: duplicate another filecontext with some fields overridden.
622 """
622 """
623 @propertycache
623 @propertycache
624 def _filelog(self):
624 def _filelog(self):
625 return self._repo.file(self._path)
625 return self._repo.file(self._path)
626
626
627 @propertycache
627 @propertycache
628 def _changeid(self):
628 def _changeid(self):
629 if r'_changeid' in self.__dict__:
629 if r'_changeid' in self.__dict__:
630 return self._changeid
630 return self._changeid
631 elif r'_changectx' in self.__dict__:
631 elif r'_changectx' in self.__dict__:
632 return self._changectx.rev()
632 return self._changectx.rev()
633 elif r'_descendantrev' in self.__dict__:
633 elif r'_descendantrev' in self.__dict__:
634 # this file context was created from a revision with a known
634 # this file context was created from a revision with a known
635 # descendant, we can (lazily) correct for linkrev aliases
635 # descendant, we can (lazily) correct for linkrev aliases
636 return self._adjustlinkrev(self._descendantrev)
636 return self._adjustlinkrev(self._descendantrev)
637 else:
637 else:
638 return self._filelog.linkrev(self._filerev)
638 return self._filelog.linkrev(self._filerev)
639
639
640 @propertycache
640 @propertycache
641 def _filenode(self):
641 def _filenode(self):
642 if r'_fileid' in self.__dict__:
642 if r'_fileid' in self.__dict__:
643 return self._filelog.lookup(self._fileid)
643 return self._filelog.lookup(self._fileid)
644 else:
644 else:
645 return self._changectx.filenode(self._path)
645 return self._changectx.filenode(self._path)
646
646
647 @propertycache
647 @propertycache
648 def _filerev(self):
648 def _filerev(self):
649 return self._filelog.rev(self._filenode)
649 return self._filelog.rev(self._filenode)
650
650
651 @propertycache
651 @propertycache
652 def _repopath(self):
652 def _repopath(self):
653 return self._path
653 return self._path
654
654
655 def __nonzero__(self):
655 def __nonzero__(self):
656 try:
656 try:
657 self._filenode
657 self._filenode
658 return True
658 return True
659 except error.LookupError:
659 except error.LookupError:
660 # file is missing
660 # file is missing
661 return False
661 return False
662
662
663 __bool__ = __nonzero__
663 __bool__ = __nonzero__
664
664
665 def __bytes__(self):
665 def __bytes__(self):
666 try:
666 try:
667 return "%s@%s" % (self.path(), self._changectx)
667 return "%s@%s" % (self.path(), self._changectx)
668 except error.LookupError:
668 except error.LookupError:
669 return "%s@???" % self.path()
669 return "%s@???" % self.path()
670
670
671 __str__ = encoding.strmethod(__bytes__)
671 __str__ = encoding.strmethod(__bytes__)
672
672
673 def __repr__(self):
673 def __repr__(self):
674 return r"<%s %s>" % (type(self).__name__, str(self))
674 return r"<%s %s>" % (type(self).__name__, str(self))
675
675
676 def __hash__(self):
676 def __hash__(self):
677 try:
677 try:
678 return hash((self._path, self._filenode))
678 return hash((self._path, self._filenode))
679 except AttributeError:
679 except AttributeError:
680 return id(self)
680 return id(self)
681
681
682 def __eq__(self, other):
682 def __eq__(self, other):
683 try:
683 try:
684 return (type(self) == type(other) and self._path == other._path
684 return (type(self) == type(other) and self._path == other._path
685 and self._filenode == other._filenode)
685 and self._filenode == other._filenode)
686 except AttributeError:
686 except AttributeError:
687 return False
687 return False
688
688
689 def __ne__(self, other):
689 def __ne__(self, other):
690 return not (self == other)
690 return not (self == other)
691
691
692 def filerev(self):
692 def filerev(self):
693 return self._filerev
693 return self._filerev
694 def filenode(self):
694 def filenode(self):
695 return self._filenode
695 return self._filenode
696 @propertycache
696 @propertycache
697 def _flags(self):
697 def _flags(self):
698 return self._changectx.flags(self._path)
698 return self._changectx.flags(self._path)
699 def flags(self):
699 def flags(self):
700 return self._flags
700 return self._flags
701 def filelog(self):
701 def filelog(self):
702 return self._filelog
702 return self._filelog
703 def rev(self):
703 def rev(self):
704 return self._changeid
704 return self._changeid
705 def linkrev(self):
705 def linkrev(self):
706 return self._filelog.linkrev(self._filerev)
706 return self._filelog.linkrev(self._filerev)
707 def node(self):
707 def node(self):
708 return self._changectx.node()
708 return self._changectx.node()
709 def hex(self):
709 def hex(self):
710 return self._changectx.hex()
710 return self._changectx.hex()
711 def user(self):
711 def user(self):
712 return self._changectx.user()
712 return self._changectx.user()
713 def date(self):
713 def date(self):
714 return self._changectx.date()
714 return self._changectx.date()
715 def files(self):
715 def files(self):
716 return self._changectx.files()
716 return self._changectx.files()
717 def description(self):
717 def description(self):
718 return self._changectx.description()
718 return self._changectx.description()
719 def branch(self):
719 def branch(self):
720 return self._changectx.branch()
720 return self._changectx.branch()
721 def extra(self):
721 def extra(self):
722 return self._changectx.extra()
722 return self._changectx.extra()
723 def phase(self):
723 def phase(self):
724 return self._changectx.phase()
724 return self._changectx.phase()
725 def phasestr(self):
725 def phasestr(self):
726 return self._changectx.phasestr()
726 return self._changectx.phasestr()
727 def obsolete(self):
727 def obsolete(self):
728 return self._changectx.obsolete()
728 return self._changectx.obsolete()
729 def instabilities(self):
729 def instabilities(self):
730 return self._changectx.instabilities()
730 return self._changectx.instabilities()
731 def manifest(self):
731 def manifest(self):
732 return self._changectx.manifest()
732 return self._changectx.manifest()
733 def changectx(self):
733 def changectx(self):
734 return self._changectx
734 return self._changectx
735 def renamed(self):
735 def renamed(self):
736 return self._copied
736 return self._copied
737 def repo(self):
737 def repo(self):
738 return self._repo
738 return self._repo
739 def size(self):
739 def size(self):
740 return len(self.data())
740 return len(self.data())
741
741
742 def path(self):
742 def path(self):
743 return self._path
743 return self._path
744
744
745 def isbinary(self):
745 def isbinary(self):
746 try:
746 try:
747 return stringutil.binary(self.data())
747 return stringutil.binary(self.data())
748 except IOError:
748 except IOError:
749 return False
749 return False
750 def isexec(self):
750 def isexec(self):
751 return 'x' in self.flags()
751 return 'x' in self.flags()
752 def islink(self):
752 def islink(self):
753 return 'l' in self.flags()
753 return 'l' in self.flags()
754
754
755 def isabsent(self):
755 def isabsent(self):
756 """whether this filectx represents a file not in self._changectx
756 """whether this filectx represents a file not in self._changectx
757
757
758 This is mainly for merge code to detect change/delete conflicts. This is
758 This is mainly for merge code to detect change/delete conflicts. This is
759 expected to be True for all subclasses of basectx."""
759 expected to be True for all subclasses of basectx."""
760 return False
760 return False
761
761
762 _customcmp = False
762 _customcmp = False
763 def cmp(self, fctx):
763 def cmp(self, fctx):
764 """compare with other file context
764 """compare with other file context
765
765
766 returns True if different than fctx.
766 returns True if different than fctx.
767 """
767 """
768 if fctx._customcmp:
768 if fctx._customcmp:
769 return fctx.cmp(self)
769 return fctx.cmp(self)
770
770
771 if (fctx._filenode is None
771 if (fctx._filenode is None
772 and (self._repo._encodefilterpats
772 and (self._repo._encodefilterpats
773 # if file data starts with '\1\n', empty metadata block is
773 # if file data starts with '\1\n', empty metadata block is
774 # prepended, which adds 4 bytes to filelog.size().
774 # prepended, which adds 4 bytes to filelog.size().
775 or self.size() - 4 == fctx.size())
775 or self.size() - 4 == fctx.size())
776 or self.size() == fctx.size()):
776 or self.size() == fctx.size()):
777 return self._filelog.cmp(self._filenode, fctx.data())
777 return self._filelog.cmp(self._filenode, fctx.data())
778
778
779 return True
779 return True
780
780
781 def _adjustlinkrev(self, srcrev, inclusive=False):
781 def _adjustlinkrev(self, srcrev, inclusive=False):
782 """return the first ancestor of <srcrev> introducing <fnode>
782 """return the first ancestor of <srcrev> introducing <fnode>
783
783
784 If the linkrev of the file revision does not point to an ancestor of
784 If the linkrev of the file revision does not point to an ancestor of
785 srcrev, we'll walk down the ancestors until we find one introducing
785 srcrev, we'll walk down the ancestors until we find one introducing
786 this file revision.
786 this file revision.
787
787
788 :srcrev: the changeset revision we search ancestors from
788 :srcrev: the changeset revision we search ancestors from
789 :inclusive: if true, the src revision will also be checked
789 :inclusive: if true, the src revision will also be checked
790 """
790 """
791 repo = self._repo
791 repo = self._repo
792 cl = repo.unfiltered().changelog
792 cl = repo.unfiltered().changelog
793 mfl = repo.manifestlog
793 mfl = repo.manifestlog
794 # fetch the linkrev
794 # fetch the linkrev
795 lkr = self.linkrev()
795 lkr = self.linkrev()
796 # hack to reuse ancestor computation when searching for renames
796 # hack to reuse ancestor computation when searching for renames
797 memberanc = getattr(self, '_ancestrycontext', None)
797 memberanc = getattr(self, '_ancestrycontext', None)
798 iteranc = None
798 iteranc = None
799 if srcrev is None:
799 if srcrev is None:
800 # wctx case, used by workingfilectx during mergecopy
800 # wctx case, used by workingfilectx during mergecopy
801 revs = [p.rev() for p in self._repo[None].parents()]
801 revs = [p.rev() for p in self._repo[None].parents()]
802 inclusive = True # we skipped the real (revless) source
802 inclusive = True # we skipped the real (revless) source
803 else:
803 else:
804 revs = [srcrev]
804 revs = [srcrev]
805 if memberanc is None:
805 if memberanc is None:
806 memberanc = iteranc = cl.ancestors(revs, lkr,
806 memberanc = iteranc = cl.ancestors(revs, lkr,
807 inclusive=inclusive)
807 inclusive=inclusive)
808 # check if this linkrev is an ancestor of srcrev
808 # check if this linkrev is an ancestor of srcrev
809 if lkr not in memberanc:
809 if lkr not in memberanc:
810 if iteranc is None:
810 if iteranc is None:
811 iteranc = cl.ancestors(revs, lkr, inclusive=inclusive)
811 iteranc = cl.ancestors(revs, lkr, inclusive=inclusive)
812 fnode = self._filenode
812 fnode = self._filenode
813 path = self._path
813 path = self._path
814 for a in iteranc:
814 for a in iteranc:
815 ac = cl.read(a) # get changeset data (we avoid object creation)
815 ac = cl.read(a) # get changeset data (we avoid object creation)
816 if path in ac[3]: # checking the 'files' field.
816 if path in ac[3]: # checking the 'files' field.
817 # The file has been touched, check if the content is
817 # The file has been touched, check if the content is
818 # similar to the one we search for.
818 # similar to the one we search for.
819 if fnode == mfl[ac[0]].readfast().get(path):
819 if fnode == mfl[ac[0]].readfast().get(path):
820 return a
820 return a
821 # In theory, we should never get out of that loop without a result.
821 # In theory, we should never get out of that loop without a result.
822 # But if manifest uses a buggy file revision (not children of the
822 # But if manifest uses a buggy file revision (not children of the
823 # one it replaces) we could. Such a buggy situation will likely
823 # one it replaces) we could. Such a buggy situation will likely
824 # result is crash somewhere else at to some point.
824 # result is crash somewhere else at to some point.
825 return lkr
825 return lkr
826
826
827 def introrev(self):
827 def introrev(self):
828 """return the rev of the changeset which introduced this file revision
828 """return the rev of the changeset which introduced this file revision
829
829
830 This method is different from linkrev because it take into account the
830 This method is different from linkrev because it take into account the
831 changeset the filectx was created from. It ensures the returned
831 changeset the filectx was created from. It ensures the returned
832 revision is one of its ancestors. This prevents bugs from
832 revision is one of its ancestors. This prevents bugs from
833 'linkrev-shadowing' when a file revision is used by multiple
833 'linkrev-shadowing' when a file revision is used by multiple
834 changesets.
834 changesets.
835 """
835 """
836 lkr = self.linkrev()
836 lkr = self.linkrev()
837 attrs = vars(self)
837 attrs = vars(self)
838 noctx = not (r'_changeid' in attrs or r'_changectx' in attrs)
838 noctx = not (r'_changeid' in attrs or r'_changectx' in attrs)
839 if noctx or self.rev() == lkr:
839 if noctx or self.rev() == lkr:
840 return self.linkrev()
840 return self.linkrev()
841 return self._adjustlinkrev(self.rev(), inclusive=True)
841 return self._adjustlinkrev(self.rev(), inclusive=True)
842
842
843 def introfilectx(self):
843 def introfilectx(self):
844 """Return filectx having identical contents, but pointing to the
844 """Return filectx having identical contents, but pointing to the
845 changeset revision where this filectx was introduced"""
845 changeset revision where this filectx was introduced"""
846 introrev = self.introrev()
846 introrev = self.introrev()
847 if self.rev() == introrev:
847 if self.rev() == introrev:
848 return self
848 return self
849 return self.filectx(self.filenode(), changeid=introrev)
849 return self.filectx(self.filenode(), changeid=introrev)
850
850
851 def _parentfilectx(self, path, fileid, filelog):
851 def _parentfilectx(self, path, fileid, filelog):
852 """create parent filectx keeping ancestry info for _adjustlinkrev()"""
852 """create parent filectx keeping ancestry info for _adjustlinkrev()"""
853 fctx = filectx(self._repo, path, fileid=fileid, filelog=filelog)
853 fctx = filectx(self._repo, path, fileid=fileid, filelog=filelog)
854 if r'_changeid' in vars(self) or r'_changectx' in vars(self):
854 if r'_changeid' in vars(self) or r'_changectx' in vars(self):
855 # If self is associated with a changeset (probably explicitly
855 # If self is associated with a changeset (probably explicitly
856 # fed), ensure the created filectx is associated with a
856 # fed), ensure the created filectx is associated with a
857 # changeset that is an ancestor of self.changectx.
857 # changeset that is an ancestor of self.changectx.
858 # This lets us later use _adjustlinkrev to get a correct link.
858 # This lets us later use _adjustlinkrev to get a correct link.
859 fctx._descendantrev = self.rev()
859 fctx._descendantrev = self.rev()
860 fctx._ancestrycontext = getattr(self, '_ancestrycontext', None)
860 fctx._ancestrycontext = getattr(self, '_ancestrycontext', None)
861 elif r'_descendantrev' in vars(self):
861 elif r'_descendantrev' in vars(self):
862 # Otherwise propagate _descendantrev if we have one associated.
862 # Otherwise propagate _descendantrev if we have one associated.
863 fctx._descendantrev = self._descendantrev
863 fctx._descendantrev = self._descendantrev
864 fctx._ancestrycontext = getattr(self, '_ancestrycontext', None)
864 fctx._ancestrycontext = getattr(self, '_ancestrycontext', None)
865 return fctx
865 return fctx
866
866
867 def parents(self):
867 def parents(self):
868 _path = self._path
868 _path = self._path
869 fl = self._filelog
869 fl = self._filelog
870 parents = self._filelog.parents(self._filenode)
870 parents = self._filelog.parents(self._filenode)
871 pl = [(_path, node, fl) for node in parents if node != nullid]
871 pl = [(_path, node, fl) for node in parents if node != nullid]
872
872
873 r = fl.renamed(self._filenode)
873 r = fl.renamed(self._filenode)
874 if r:
874 if r:
875 # - In the simple rename case, both parent are nullid, pl is empty.
875 # - In the simple rename case, both parent are nullid, pl is empty.
876 # - In case of merge, only one of the parent is null id and should
876 # - In case of merge, only one of the parent is null id and should
877 # be replaced with the rename information. This parent is -always-
877 # be replaced with the rename information. This parent is -always-
878 # the first one.
878 # the first one.
879 #
879 #
880 # As null id have always been filtered out in the previous list
880 # As null id have always been filtered out in the previous list
881 # comprehension, inserting to 0 will always result in "replacing
881 # comprehension, inserting to 0 will always result in "replacing
882 # first nullid parent with rename information.
882 # first nullid parent with rename information.
883 pl.insert(0, (r[0], r[1], self._repo.file(r[0])))
883 pl.insert(0, (r[0], r[1], self._repo.file(r[0])))
884
884
885 return [self._parentfilectx(path, fnode, l) for path, fnode, l in pl]
885 return [self._parentfilectx(path, fnode, l) for path, fnode, l in pl]
886
886
887 def p1(self):
887 def p1(self):
888 return self.parents()[0]
888 return self.parents()[0]
889
889
890 def p2(self):
890 def p2(self):
891 p = self.parents()
891 p = self.parents()
892 if len(p) == 2:
892 if len(p) == 2:
893 return p[1]
893 return p[1]
894 return filectx(self._repo, self._path, fileid=-1, filelog=self._filelog)
894 return filectx(self._repo, self._path, fileid=-1, filelog=self._filelog)
895
895
896 def annotate(self, follow=False, skiprevs=None, diffopts=None):
896 def annotate(self, follow=False, skiprevs=None, diffopts=None):
897 """Returns a list of annotateline objects for each line in the file
897 """Returns a list of annotateline objects for each line in the file
898
898
899 - line.fctx is the filectx of the node where that line was last changed
899 - line.fctx is the filectx of the node where that line was last changed
900 - line.lineno is the line number at the first appearance in the managed
900 - line.lineno is the line number at the first appearance in the managed
901 file
901 file
902 - line.text is the data on that line (including newline character)
902 - line.text is the data on that line (including newline character)
903 """
903 """
904 getlog = util.lrucachefunc(lambda x: self._repo.file(x))
904 getlog = util.lrucachefunc(lambda x: self._repo.file(x))
905
905
906 def parents(f):
906 def parents(f):
907 # Cut _descendantrev here to mitigate the penalty of lazy linkrev
907 # Cut _descendantrev here to mitigate the penalty of lazy linkrev
908 # adjustment. Otherwise, p._adjustlinkrev() would walk changelog
908 # adjustment. Otherwise, p._adjustlinkrev() would walk changelog
909 # from the topmost introrev (= srcrev) down to p.linkrev() if it
909 # from the topmost introrev (= srcrev) down to p.linkrev() if it
910 # isn't an ancestor of the srcrev.
910 # isn't an ancestor of the srcrev.
911 f._changeid
911 f._changeid
912 pl = f.parents()
912 pl = f.parents()
913
913
914 # Don't return renamed parents if we aren't following.
914 # Don't return renamed parents if we aren't following.
915 if not follow:
915 if not follow:
916 pl = [p for p in pl if p.path() == f.path()]
916 pl = [p for p in pl if p.path() == f.path()]
917
917
918 # renamed filectx won't have a filelog yet, so set it
918 # renamed filectx won't have a filelog yet, so set it
919 # from the cache to save time
919 # from the cache to save time
920 for p in pl:
920 for p in pl:
921 if not r'_filelog' in p.__dict__:
921 if not r'_filelog' in p.__dict__:
922 p._filelog = getlog(p.path())
922 p._filelog = getlog(p.path())
923
923
924 return pl
924 return pl
925
925
926 # use linkrev to find the first changeset where self appeared
926 # use linkrev to find the first changeset where self appeared
927 base = self.introfilectx()
927 base = self.introfilectx()
928 if getattr(base, '_ancestrycontext', None) is None:
928 if getattr(base, '_ancestrycontext', None) is None:
929 cl = self._repo.changelog
929 cl = self._repo.changelog
930 if base.rev() is None:
930 if base.rev() is None:
931 # wctx is not inclusive, but works because _ancestrycontext
931 # wctx is not inclusive, but works because _ancestrycontext
932 # is used to test filelog revisions
932 # is used to test filelog revisions
933 ac = cl.ancestors([p.rev() for p in base.parents()],
933 ac = cl.ancestors([p.rev() for p in base.parents()],
934 inclusive=True)
934 inclusive=True)
935 else:
935 else:
936 ac = cl.ancestors([base.rev()], inclusive=True)
936 ac = cl.ancestors([base.rev()], inclusive=True)
937 base._ancestrycontext = ac
937 base._ancestrycontext = ac
938
938
939 return dagop.annotate(base, parents, skiprevs=skiprevs,
939 return dagop.annotate(base, parents, skiprevs=skiprevs,
940 diffopts=diffopts)
940 diffopts=diffopts)
941
941
942 def ancestors(self, followfirst=False):
942 def ancestors(self, followfirst=False):
943 visit = {}
943 visit = {}
944 c = self
944 c = self
945 if followfirst:
945 if followfirst:
946 cut = 1
946 cut = 1
947 else:
947 else:
948 cut = None
948 cut = None
949
949
950 while True:
950 while True:
951 for parent in c.parents()[:cut]:
951 for parent in c.parents()[:cut]:
952 visit[(parent.linkrev(), parent.filenode())] = parent
952 visit[(parent.linkrev(), parent.filenode())] = parent
953 if not visit:
953 if not visit:
954 break
954 break
955 c = visit.pop(max(visit))
955 c = visit.pop(max(visit))
956 yield c
956 yield c
957
957
958 def decodeddata(self):
958 def decodeddata(self):
959 """Returns `data()` after running repository decoding filters.
959 """Returns `data()` after running repository decoding filters.
960
960
961 This is often equivalent to how the data would be expressed on disk.
961 This is often equivalent to how the data would be expressed on disk.
962 """
962 """
963 return self._repo.wwritedata(self.path(), self.data())
963 return self._repo.wwritedata(self.path(), self.data())
964
964
965 class filectx(basefilectx):
965 class filectx(basefilectx):
966 """A filecontext object makes access to data related to a particular
966 """A filecontext object makes access to data related to a particular
967 filerevision convenient."""
967 filerevision convenient."""
968 def __init__(self, repo, path, changeid=None, fileid=None,
968 def __init__(self, repo, path, changeid=None, fileid=None,
969 filelog=None, changectx=None):
969 filelog=None, changectx=None):
970 """changeid can be a changeset revision, node, or tag.
970 """changeid can be a changeset revision, node, or tag.
971 fileid can be a file revision or node."""
971 fileid can be a file revision or node."""
972 self._repo = repo
972 self._repo = repo
973 self._path = path
973 self._path = path
974
974
975 assert (changeid is not None
975 assert (changeid is not None
976 or fileid is not None
976 or fileid is not None
977 or changectx is not None), \
977 or changectx is not None), \
978 ("bad args: changeid=%r, fileid=%r, changectx=%r"
978 ("bad args: changeid=%r, fileid=%r, changectx=%r"
979 % (changeid, fileid, changectx))
979 % (changeid, fileid, changectx))
980
980
981 if filelog is not None:
981 if filelog is not None:
982 self._filelog = filelog
982 self._filelog = filelog
983
983
984 if changeid is not None:
984 if changeid is not None:
985 self._changeid = changeid
985 self._changeid = changeid
986 if changectx is not None:
986 if changectx is not None:
987 self._changectx = changectx
987 self._changectx = changectx
988 if fileid is not None:
988 if fileid is not None:
989 self._fileid = fileid
989 self._fileid = fileid
990
990
991 @propertycache
991 @propertycache
992 def _changectx(self):
992 def _changectx(self):
993 try:
993 try:
994 return changectx(self._repo, self._changeid)
994 return changectx(self._repo, self._changeid)
995 except error.FilteredRepoLookupError:
995 except error.FilteredRepoLookupError:
996 # Linkrev may point to any revision in the repository. When the
996 # Linkrev may point to any revision in the repository. When the
997 # repository is filtered this may lead to `filectx` trying to build
997 # repository is filtered this may lead to `filectx` trying to build
998 # `changectx` for filtered revision. In such case we fallback to
998 # `changectx` for filtered revision. In such case we fallback to
999 # creating `changectx` on the unfiltered version of the reposition.
999 # creating `changectx` on the unfiltered version of the reposition.
1000 # This fallback should not be an issue because `changectx` from
1000 # This fallback should not be an issue because `changectx` from
1001 # `filectx` are not used in complex operations that care about
1001 # `filectx` are not used in complex operations that care about
1002 # filtering.
1002 # filtering.
1003 #
1003 #
1004 # This fallback is a cheap and dirty fix that prevent several
1004 # This fallback is a cheap and dirty fix that prevent several
1005 # crashes. It does not ensure the behavior is correct. However the
1005 # crashes. It does not ensure the behavior is correct. However the
1006 # behavior was not correct before filtering either and "incorrect
1006 # behavior was not correct before filtering either and "incorrect
1007 # behavior" is seen as better as "crash"
1007 # behavior" is seen as better as "crash"
1008 #
1008 #
1009 # Linkrevs have several serious troubles with filtering that are
1009 # Linkrevs have several serious troubles with filtering that are
1010 # complicated to solve. Proper handling of the issue here should be
1010 # complicated to solve. Proper handling of the issue here should be
1011 # considered when solving linkrev issue are on the table.
1011 # considered when solving linkrev issue are on the table.
1012 return changectx(self._repo.unfiltered(), self._changeid)
1012 return changectx(self._repo.unfiltered(), self._changeid)
1013
1013
1014 def filectx(self, fileid, changeid=None):
1014 def filectx(self, fileid, changeid=None):
1015 '''opens an arbitrary revision of the file without
1015 '''opens an arbitrary revision of the file without
1016 opening a new filelog'''
1016 opening a new filelog'''
1017 return filectx(self._repo, self._path, fileid=fileid,
1017 return filectx(self._repo, self._path, fileid=fileid,
1018 filelog=self._filelog, changeid=changeid)
1018 filelog=self._filelog, changeid=changeid)
1019
1019
1020 def rawdata(self):
1020 def rawdata(self):
1021 return self._filelog.revision(self._filenode, raw=True)
1021 return self._filelog.revision(self._filenode, raw=True)
1022
1022
1023 def rawflags(self):
1023 def rawflags(self):
1024 """low-level revlog flags"""
1024 """low-level revlog flags"""
1025 return self._filelog.flags(self._filerev)
1025 return self._filelog.flags(self._filerev)
1026
1026
1027 def data(self):
1027 def data(self):
1028 try:
1028 try:
1029 return self._filelog.read(self._filenode)
1029 return self._filelog.read(self._filenode)
1030 except error.CensoredNodeError:
1030 except error.CensoredNodeError:
1031 if self._repo.ui.config("censor", "policy") == "ignore":
1031 if self._repo.ui.config("censor", "policy") == "ignore":
1032 return ""
1032 return ""
1033 raise error.Abort(_("censored node: %s") % short(self._filenode),
1033 raise error.Abort(_("censored node: %s") % short(self._filenode),
1034 hint=_("set censor.policy to ignore errors"))
1034 hint=_("set censor.policy to ignore errors"))
1035
1035
1036 def size(self):
1036 def size(self):
1037 return self._filelog.size(self._filerev)
1037 return self._filelog.size(self._filerev)
1038
1038
1039 @propertycache
1039 @propertycache
1040 def _copied(self):
1040 def _copied(self):
1041 """check if file was actually renamed in this changeset revision
1041 """check if file was actually renamed in this changeset revision
1042
1042
1043 If rename logged in file revision, we report copy for changeset only
1043 If rename logged in file revision, we report copy for changeset only
1044 if file revisions linkrev points back to the changeset in question
1044 if file revisions linkrev points back to the changeset in question
1045 or both changeset parents contain different file revisions.
1045 or both changeset parents contain different file revisions.
1046 """
1046 """
1047
1047
1048 renamed = self._filelog.renamed(self._filenode)
1048 renamed = self._filelog.renamed(self._filenode)
1049 if not renamed:
1049 if not renamed:
1050 return renamed
1050 return renamed
1051
1051
1052 if self.rev() == self.linkrev():
1052 if self.rev() == self.linkrev():
1053 return renamed
1053 return renamed
1054
1054
1055 name = self.path()
1055 name = self.path()
1056 fnode = self._filenode
1056 fnode = self._filenode
1057 for p in self._changectx.parents():
1057 for p in self._changectx.parents():
1058 try:
1058 try:
1059 if fnode == p.filenode(name):
1059 if fnode == p.filenode(name):
1060 return None
1060 return None
1061 except error.LookupError:
1061 except error.LookupError:
1062 pass
1062 pass
1063 return renamed
1063 return renamed
1064
1064
1065 def children(self):
1065 def children(self):
1066 # hard for renames
1066 # hard for renames
1067 c = self._filelog.children(self._filenode)
1067 c = self._filelog.children(self._filenode)
1068 return [filectx(self._repo, self._path, fileid=x,
1068 return [filectx(self._repo, self._path, fileid=x,
1069 filelog=self._filelog) for x in c]
1069 filelog=self._filelog) for x in c]
1070
1070
1071 class committablectx(basectx):
1071 class committablectx(basectx):
1072 """A committablectx object provides common functionality for a context that
1072 """A committablectx object provides common functionality for a context that
1073 wants the ability to commit, e.g. workingctx or memctx."""
1073 wants the ability to commit, e.g. workingctx or memctx."""
1074 def __init__(self, repo, text="", user=None, date=None, extra=None,
1074 def __init__(self, repo, text="", user=None, date=None, extra=None,
1075 changes=None):
1075 changes=None):
1076 super(committablectx, self).__init__(repo)
1076 super(committablectx, self).__init__(repo)
1077 self._rev = None
1077 self._rev = None
1078 self._node = None
1078 self._node = None
1079 self._text = text
1079 self._text = text
1080 if date:
1080 if date:
1081 self._date = dateutil.parsedate(date)
1081 self._date = dateutil.parsedate(date)
1082 if user:
1082 if user:
1083 self._user = user
1083 self._user = user
1084 if changes:
1084 if changes:
1085 self._status = changes
1085 self._status = changes
1086
1086
1087 self._extra = {}
1087 self._extra = {}
1088 if extra:
1088 if extra:
1089 self._extra = extra.copy()
1089 self._extra = extra.copy()
1090 if 'branch' not in self._extra:
1090 if 'branch' not in self._extra:
1091 try:
1091 try:
1092 branch = encoding.fromlocal(self._repo.dirstate.branch())
1092 branch = encoding.fromlocal(self._repo.dirstate.branch())
1093 except UnicodeDecodeError:
1093 except UnicodeDecodeError:
1094 raise error.Abort(_('branch name not in UTF-8!'))
1094 raise error.Abort(_('branch name not in UTF-8!'))
1095 self._extra['branch'] = branch
1095 self._extra['branch'] = branch
1096 if self._extra['branch'] == '':
1096 if self._extra['branch'] == '':
1097 self._extra['branch'] = 'default'
1097 self._extra['branch'] = 'default'
1098
1098
1099 def __bytes__(self):
1099 def __bytes__(self):
1100 return bytes(self._parents[0]) + "+"
1100 return bytes(self._parents[0]) + "+"
1101
1101
1102 __str__ = encoding.strmethod(__bytes__)
1102 __str__ = encoding.strmethod(__bytes__)
1103
1103
1104 def __nonzero__(self):
1104 def __nonzero__(self):
1105 return True
1105 return True
1106
1106
1107 __bool__ = __nonzero__
1107 __bool__ = __nonzero__
1108
1108
1109 def _buildflagfunc(self):
1109 def _buildflagfunc(self):
1110 # Create a fallback function for getting file flags when the
1110 # Create a fallback function for getting file flags when the
1111 # filesystem doesn't support them
1111 # filesystem doesn't support them
1112
1112
1113 copiesget = self._repo.dirstate.copies().get
1113 copiesget = self._repo.dirstate.copies().get
1114 parents = self.parents()
1114 parents = self.parents()
1115 if len(parents) < 2:
1115 if len(parents) < 2:
1116 # when we have one parent, it's easy: copy from parent
1116 # when we have one parent, it's easy: copy from parent
1117 man = parents[0].manifest()
1117 man = parents[0].manifest()
1118 def func(f):
1118 def func(f):
1119 f = copiesget(f, f)
1119 f = copiesget(f, f)
1120 return man.flags(f)
1120 return man.flags(f)
1121 else:
1121 else:
1122 # merges are tricky: we try to reconstruct the unstored
1122 # merges are tricky: we try to reconstruct the unstored
1123 # result from the merge (issue1802)
1123 # result from the merge (issue1802)
1124 p1, p2 = parents
1124 p1, p2 = parents
1125 pa = p1.ancestor(p2)
1125 pa = p1.ancestor(p2)
1126 m1, m2, ma = p1.manifest(), p2.manifest(), pa.manifest()
1126 m1, m2, ma = p1.manifest(), p2.manifest(), pa.manifest()
1127
1127
1128 def func(f):
1128 def func(f):
1129 f = copiesget(f, f) # may be wrong for merges with copies
1129 f = copiesget(f, f) # may be wrong for merges with copies
1130 fl1, fl2, fla = m1.flags(f), m2.flags(f), ma.flags(f)
1130 fl1, fl2, fla = m1.flags(f), m2.flags(f), ma.flags(f)
1131 if fl1 == fl2:
1131 if fl1 == fl2:
1132 return fl1
1132 return fl1
1133 if fl1 == fla:
1133 if fl1 == fla:
1134 return fl2
1134 return fl2
1135 if fl2 == fla:
1135 if fl2 == fla:
1136 return fl1
1136 return fl1
1137 return '' # punt for conflicts
1137 return '' # punt for conflicts
1138
1138
1139 return func
1139 return func
1140
1140
1141 @propertycache
1141 @propertycache
1142 def _flagfunc(self):
1142 def _flagfunc(self):
1143 return self._repo.dirstate.flagfunc(self._buildflagfunc)
1143 return self._repo.dirstate.flagfunc(self._buildflagfunc)
1144
1144
1145 @propertycache
1145 @propertycache
1146 def _status(self):
1146 def _status(self):
1147 return self._repo.status()
1147 return self._repo.status()
1148
1148
1149 @propertycache
1149 @propertycache
1150 def _user(self):
1150 def _user(self):
1151 return self._repo.ui.username()
1151 return self._repo.ui.username()
1152
1152
1153 @propertycache
1153 @propertycache
1154 def _date(self):
1154 def _date(self):
1155 ui = self._repo.ui
1155 ui = self._repo.ui
1156 date = ui.configdate('devel', 'default-date')
1156 date = ui.configdate('devel', 'default-date')
1157 if date is None:
1157 if date is None:
1158 date = dateutil.makedate()
1158 date = dateutil.makedate()
1159 return date
1159 return date
1160
1160
1161 def subrev(self, subpath):
1161 def subrev(self, subpath):
1162 return None
1162 return None
1163
1163
1164 def manifestnode(self):
1164 def manifestnode(self):
1165 return None
1165 return None
1166 def user(self):
1166 def user(self):
1167 return self._user or self._repo.ui.username()
1167 return self._user or self._repo.ui.username()
1168 def date(self):
1168 def date(self):
1169 return self._date
1169 return self._date
1170 def description(self):
1170 def description(self):
1171 return self._text
1171 return self._text
1172 def files(self):
1172 def files(self):
1173 return sorted(self._status.modified + self._status.added +
1173 return sorted(self._status.modified + self._status.added +
1174 self._status.removed)
1174 self._status.removed)
1175
1175
1176 def modified(self):
1176 def modified(self):
1177 return self._status.modified
1177 return self._status.modified
1178 def added(self):
1178 def added(self):
1179 return self._status.added
1179 return self._status.added
1180 def removed(self):
1180 def removed(self):
1181 return self._status.removed
1181 return self._status.removed
1182 def deleted(self):
1182 def deleted(self):
1183 return self._status.deleted
1183 return self._status.deleted
1184 def branch(self):
1184 def branch(self):
1185 return encoding.tolocal(self._extra['branch'])
1185 return encoding.tolocal(self._extra['branch'])
1186 def closesbranch(self):
1186 def closesbranch(self):
1187 return 'close' in self._extra
1187 return 'close' in self._extra
1188 def extra(self):
1188 def extra(self):
1189 return self._extra
1189 return self._extra
1190
1190
1191 def isinmemory(self):
1191 def isinmemory(self):
1192 return False
1192 return False
1193
1193
1194 def tags(self):
1194 def tags(self):
1195 return []
1195 return []
1196
1196
1197 def bookmarks(self):
1197 def bookmarks(self):
1198 b = []
1198 b = []
1199 for p in self.parents():
1199 for p in self.parents():
1200 b.extend(p.bookmarks())
1200 b.extend(p.bookmarks())
1201 return b
1201 return b
1202
1202
1203 def phase(self):
1203 def phase(self):
1204 phase = phases.draft # default phase to draft
1204 phase = phases.draft # default phase to draft
1205 for p in self.parents():
1205 for p in self.parents():
1206 phase = max(phase, p.phase())
1206 phase = max(phase, p.phase())
1207 return phase
1207 return phase
1208
1208
1209 def hidden(self):
1209 def hidden(self):
1210 return False
1210 return False
1211
1211
1212 def children(self):
1212 def children(self):
1213 return []
1213 return []
1214
1214
1215 def flags(self, path):
1215 def flags(self, path):
1216 if r'_manifest' in self.__dict__:
1216 if r'_manifest' in self.__dict__:
1217 try:
1217 try:
1218 return self._manifest.flags(path)
1218 return self._manifest.flags(path)
1219 except KeyError:
1219 except KeyError:
1220 return ''
1220 return ''
1221
1221
1222 try:
1222 try:
1223 return self._flagfunc(path)
1223 return self._flagfunc(path)
1224 except OSError:
1224 except OSError:
1225 return ''
1225 return ''
1226
1226
1227 def ancestor(self, c2):
1227 def ancestor(self, c2):
1228 """return the "best" ancestor context of self and c2"""
1228 """return the "best" ancestor context of self and c2"""
1229 return self._parents[0].ancestor(c2) # punt on two parents for now
1229 return self._parents[0].ancestor(c2) # punt on two parents for now
1230
1230
1231 def walk(self, match):
1231 def walk(self, match):
1232 '''Generates matching file names.'''
1232 '''Generates matching file names.'''
1233 return sorted(self._repo.dirstate.walk(match,
1233 return sorted(self._repo.dirstate.walk(match,
1234 subrepos=sorted(self.substate),
1234 subrepos=sorted(self.substate),
1235 unknown=True, ignored=False))
1235 unknown=True, ignored=False))
1236
1236
1237 def matches(self, match):
1237 def matches(self, match):
1238 ds = self._repo.dirstate
1238 ds = self._repo.dirstate
1239 return sorted(f for f in ds.matches(match) if ds[f] != 'r')
1239 return sorted(f for f in ds.matches(match) if ds[f] != 'r')
1240
1240
1241 def ancestors(self):
1241 def ancestors(self):
1242 for p in self._parents:
1242 for p in self._parents:
1243 yield p
1243 yield p
1244 for a in self._repo.changelog.ancestors(
1244 for a in self._repo.changelog.ancestors(
1245 [p.rev() for p in self._parents]):
1245 [p.rev() for p in self._parents]):
1246 yield changectx(self._repo, a)
1246 yield changectx(self._repo, a)
1247
1247
1248 def markcommitted(self, node):
1248 def markcommitted(self, node):
1249 """Perform post-commit cleanup necessary after committing this ctx
1249 """Perform post-commit cleanup necessary after committing this ctx
1250
1250
1251 Specifically, this updates backing stores this working context
1251 Specifically, this updates backing stores this working context
1252 wraps to reflect the fact that the changes reflected by this
1252 wraps to reflect the fact that the changes reflected by this
1253 workingctx have been committed. For example, it marks
1253 workingctx have been committed. For example, it marks
1254 modified and added files as normal in the dirstate.
1254 modified and added files as normal in the dirstate.
1255
1255
1256 """
1256 """
1257
1257
1258 with self._repo.dirstate.parentchange():
1258 with self._repo.dirstate.parentchange():
1259 for f in self.modified() + self.added():
1259 for f in self.modified() + self.added():
1260 self._repo.dirstate.normal(f)
1260 self._repo.dirstate.normal(f)
1261 for f in self.removed():
1261 for f in self.removed():
1262 self._repo.dirstate.drop(f)
1262 self._repo.dirstate.drop(f)
1263 self._repo.dirstate.setparents(node)
1263 self._repo.dirstate.setparents(node)
1264
1264
1265 # write changes out explicitly, because nesting wlock at
1265 # write changes out explicitly, because nesting wlock at
1266 # runtime may prevent 'wlock.release()' in 'repo.commit()'
1266 # runtime may prevent 'wlock.release()' in 'repo.commit()'
1267 # from immediately doing so for subsequent changing files
1267 # from immediately doing so for subsequent changing files
1268 self._repo.dirstate.write(self._repo.currenttransaction())
1268 self._repo.dirstate.write(self._repo.currenttransaction())
1269
1269
1270 def dirty(self, missing=False, merge=True, branch=True):
1270 def dirty(self, missing=False, merge=True, branch=True):
1271 return False
1271 return False
1272
1272
1273 class workingctx(committablectx):
1273 class workingctx(committablectx):
1274 """A workingctx object makes access to data related to
1274 """A workingctx object makes access to data related to
1275 the current working directory convenient.
1275 the current working directory convenient.
1276 date - any valid date string or (unixtime, offset), or None.
1276 date - any valid date string or (unixtime, offset), or None.
1277 user - username string, or None.
1277 user - username string, or None.
1278 extra - a dictionary of extra values, or None.
1278 extra - a dictionary of extra values, or None.
1279 changes - a list of file lists as returned by localrepo.status()
1279 changes - a list of file lists as returned by localrepo.status()
1280 or None to use the repository status.
1280 or None to use the repository status.
1281 """
1281 """
1282 def __init__(self, repo, text="", user=None, date=None, extra=None,
1282 def __init__(self, repo, text="", user=None, date=None, extra=None,
1283 changes=None):
1283 changes=None):
1284 super(workingctx, self).__init__(repo, text, user, date, extra, changes)
1284 super(workingctx, self).__init__(repo, text, user, date, extra, changes)
1285
1285
1286 def __iter__(self):
1286 def __iter__(self):
1287 d = self._repo.dirstate
1287 d = self._repo.dirstate
1288 for f in d:
1288 for f in d:
1289 if d[f] != 'r':
1289 if d[f] != 'r':
1290 yield f
1290 yield f
1291
1291
1292 def __contains__(self, key):
1292 def __contains__(self, key):
1293 return self._repo.dirstate[key] not in "?r"
1293 return self._repo.dirstate[key] not in "?r"
1294
1294
1295 def hex(self):
1295 def hex(self):
1296 return hex(wdirid)
1296 return hex(wdirid)
1297
1297
1298 @propertycache
1298 @propertycache
1299 def _parents(self):
1299 def _parents(self):
1300 p = self._repo.dirstate.parents()
1300 p = self._repo.dirstate.parents()
1301 if p[1] == nullid:
1301 if p[1] == nullid:
1302 p = p[:-1]
1302 p = p[:-1]
1303 return [changectx(self._repo, x) for x in p]
1303 return [changectx(self._repo, x) for x in p]
1304
1304
1305 def _fileinfo(self, path):
1305 def _fileinfo(self, path):
1306 # populate __dict__['_manifest'] as workingctx has no _manifestdelta
1306 # populate __dict__['_manifest'] as workingctx has no _manifestdelta
1307 self._manifest
1307 self._manifest
1308 return super(workingctx, self)._fileinfo(path)
1308 return super(workingctx, self)._fileinfo(path)
1309
1309
1310 def filectx(self, path, filelog=None):
1310 def filectx(self, path, filelog=None):
1311 """get a file context from the working directory"""
1311 """get a file context from the working directory"""
1312 return workingfilectx(self._repo, path, workingctx=self,
1312 return workingfilectx(self._repo, path, workingctx=self,
1313 filelog=filelog)
1313 filelog=filelog)
1314
1314
1315 def dirty(self, missing=False, merge=True, branch=True):
1315 def dirty(self, missing=False, merge=True, branch=True):
1316 "check whether a working directory is modified"
1316 "check whether a working directory is modified"
1317 # check subrepos first
1317 # check subrepos first
1318 for s in sorted(self.substate):
1318 for s in sorted(self.substate):
1319 if self.sub(s).dirty(missing=missing):
1319 if self.sub(s).dirty(missing=missing):
1320 return True
1320 return True
1321 # check current working dir
1321 # check current working dir
1322 return ((merge and self.p2()) or
1322 return ((merge and self.p2()) or
1323 (branch and self.branch() != self.p1().branch()) or
1323 (branch and self.branch() != self.p1().branch()) or
1324 self.modified() or self.added() or self.removed() or
1324 self.modified() or self.added() or self.removed() or
1325 (missing and self.deleted()))
1325 (missing and self.deleted()))
1326
1326
1327 def add(self, list, prefix=""):
1327 def add(self, list, prefix=""):
1328 with self._repo.wlock():
1328 with self._repo.wlock():
1329 ui, ds = self._repo.ui, self._repo.dirstate
1329 ui, ds = self._repo.ui, self._repo.dirstate
1330 uipath = lambda f: ds.pathto(pathutil.join(prefix, f))
1330 uipath = lambda f: ds.pathto(pathutil.join(prefix, f))
1331 rejected = []
1331 rejected = []
1332 lstat = self._repo.wvfs.lstat
1332 lstat = self._repo.wvfs.lstat
1333 for f in list:
1333 for f in list:
1334 # ds.pathto() returns an absolute file when this is invoked from
1334 # ds.pathto() returns an absolute file when this is invoked from
1335 # the keyword extension. That gets flagged as non-portable on
1335 # the keyword extension. That gets flagged as non-portable on
1336 # Windows, since it contains the drive letter and colon.
1336 # Windows, since it contains the drive letter and colon.
1337 scmutil.checkportable(ui, os.path.join(prefix, f))
1337 scmutil.checkportable(ui, os.path.join(prefix, f))
1338 try:
1338 try:
1339 st = lstat(f)
1339 st = lstat(f)
1340 except OSError:
1340 except OSError:
1341 ui.warn(_("%s does not exist!\n") % uipath(f))
1341 ui.warn(_("%s does not exist!\n") % uipath(f))
1342 rejected.append(f)
1342 rejected.append(f)
1343 continue
1343 continue
1344 limit = ui.configbytes('ui', 'large-file-limit')
1344 limit = ui.configbytes('ui', 'large-file-limit')
1345 if limit != 0 and st.st_size > limit:
1345 if limit != 0 and st.st_size > limit:
1346 ui.warn(_("%s: up to %d MB of RAM may be required "
1346 ui.warn(_("%s: up to %d MB of RAM may be required "
1347 "to manage this file\n"
1347 "to manage this file\n"
1348 "(use 'hg revert %s' to cancel the "
1348 "(use 'hg revert %s' to cancel the "
1349 "pending addition)\n")
1349 "pending addition)\n")
1350 % (f, 3 * st.st_size // 1000000, uipath(f)))
1350 % (f, 3 * st.st_size // 1000000, uipath(f)))
1351 if not (stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode)):
1351 if not (stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode)):
1352 ui.warn(_("%s not added: only files and symlinks "
1352 ui.warn(_("%s not added: only files and symlinks "
1353 "supported currently\n") % uipath(f))
1353 "supported currently\n") % uipath(f))
1354 rejected.append(f)
1354 rejected.append(f)
1355 elif ds[f] in 'amn':
1355 elif ds[f] in 'amn':
1356 ui.warn(_("%s already tracked!\n") % uipath(f))
1356 ui.warn(_("%s already tracked!\n") % uipath(f))
1357 elif ds[f] == 'r':
1357 elif ds[f] == 'r':
1358 ds.normallookup(f)
1358 ds.normallookup(f)
1359 else:
1359 else:
1360 ds.add(f)
1360 ds.add(f)
1361 return rejected
1361 return rejected
1362
1362
1363 def forget(self, files, prefix=""):
1363 def forget(self, files, prefix=""):
1364 with self._repo.wlock():
1364 with self._repo.wlock():
1365 ds = self._repo.dirstate
1365 ds = self._repo.dirstate
1366 uipath = lambda f: ds.pathto(pathutil.join(prefix, f))
1366 uipath = lambda f: ds.pathto(pathutil.join(prefix, f))
1367 rejected = []
1367 rejected = []
1368 for f in files:
1368 for f in files:
1369 if f not in self._repo.dirstate:
1369 if f not in self._repo.dirstate:
1370 self._repo.ui.warn(_("%s not tracked!\n") % uipath(f))
1370 self._repo.ui.warn(_("%s not tracked!\n") % uipath(f))
1371 rejected.append(f)
1371 rejected.append(f)
1372 elif self._repo.dirstate[f] != 'a':
1372 elif self._repo.dirstate[f] != 'a':
1373 self._repo.dirstate.remove(f)
1373 self._repo.dirstate.remove(f)
1374 else:
1374 else:
1375 self._repo.dirstate.drop(f)
1375 self._repo.dirstate.drop(f)
1376 return rejected
1376 return rejected
1377
1377
1378 def undelete(self, list):
1378 def undelete(self, list):
1379 pctxs = self.parents()
1379 pctxs = self.parents()
1380 with self._repo.wlock():
1380 with self._repo.wlock():
1381 ds = self._repo.dirstate
1381 ds = self._repo.dirstate
1382 for f in list:
1382 for f in list:
1383 if self._repo.dirstate[f] != 'r':
1383 if self._repo.dirstate[f] != 'r':
1384 self._repo.ui.warn(_("%s not removed!\n") % ds.pathto(f))
1384 self._repo.ui.warn(_("%s not removed!\n") % ds.pathto(f))
1385 else:
1385 else:
1386 fctx = f in pctxs[0] and pctxs[0][f] or pctxs[1][f]
1386 fctx = f in pctxs[0] and pctxs[0][f] or pctxs[1][f]
1387 t = fctx.data()
1387 t = fctx.data()
1388 self._repo.wwrite(f, t, fctx.flags())
1388 self._repo.wwrite(f, t, fctx.flags())
1389 self._repo.dirstate.normal(f)
1389 self._repo.dirstate.normal(f)
1390
1390
1391 def copy(self, source, dest):
1391 def copy(self, source, dest):
1392 try:
1392 try:
1393 st = self._repo.wvfs.lstat(dest)
1393 st = self._repo.wvfs.lstat(dest)
1394 except OSError as err:
1394 except OSError as err:
1395 if err.errno != errno.ENOENT:
1395 if err.errno != errno.ENOENT:
1396 raise
1396 raise
1397 self._repo.ui.warn(_("%s does not exist!\n")
1397 self._repo.ui.warn(_("%s does not exist!\n")
1398 % self._repo.dirstate.pathto(dest))
1398 % self._repo.dirstate.pathto(dest))
1399 return
1399 return
1400 if not (stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode)):
1400 if not (stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode)):
1401 self._repo.ui.warn(_("copy failed: %s is not a file or a "
1401 self._repo.ui.warn(_("copy failed: %s is not a file or a "
1402 "symbolic link\n")
1402 "symbolic link\n")
1403 % self._repo.dirstate.pathto(dest))
1403 % self._repo.dirstate.pathto(dest))
1404 else:
1404 else:
1405 with self._repo.wlock():
1405 with self._repo.wlock():
1406 if self._repo.dirstate[dest] in '?':
1406 if self._repo.dirstate[dest] in '?':
1407 self._repo.dirstate.add(dest)
1407 self._repo.dirstate.add(dest)
1408 elif self._repo.dirstate[dest] in 'r':
1408 elif self._repo.dirstate[dest] in 'r':
1409 self._repo.dirstate.normallookup(dest)
1409 self._repo.dirstate.normallookup(dest)
1410 self._repo.dirstate.copy(source, dest)
1410 self._repo.dirstate.copy(source, dest)
1411
1411
1412 def match(self, pats=None, include=None, exclude=None, default='glob',
1412 def match(self, pats=None, include=None, exclude=None, default='glob',
1413 listsubrepos=False, badfn=None):
1413 listsubrepos=False, badfn=None):
1414 r = self._repo
1414 r = self._repo
1415
1415
1416 # Only a case insensitive filesystem needs magic to translate user input
1416 # Only a case insensitive filesystem needs magic to translate user input
1417 # to actual case in the filesystem.
1417 # to actual case in the filesystem.
1418 icasefs = not util.fscasesensitive(r.root)
1418 icasefs = not util.fscasesensitive(r.root)
1419 return matchmod.match(r.root, r.getcwd(), pats, include, exclude,
1419 return matchmod.match(r.root, r.getcwd(), pats, include, exclude,
1420 default, auditor=r.auditor, ctx=self,
1420 default, auditor=r.auditor, ctx=self,
1421 listsubrepos=listsubrepos, badfn=badfn,
1421 listsubrepos=listsubrepos, badfn=badfn,
1422 icasefs=icasefs)
1422 icasefs=icasefs)
1423
1423
1424 def _filtersuspectsymlink(self, files):
1424 def _filtersuspectsymlink(self, files):
1425 if not files or self._repo.dirstate._checklink:
1425 if not files or self._repo.dirstate._checklink:
1426 return files
1426 return files
1427
1427
1428 # Symlink placeholders may get non-symlink-like contents
1428 # Symlink placeholders may get non-symlink-like contents
1429 # via user error or dereferencing by NFS or Samba servers,
1429 # via user error or dereferencing by NFS or Samba servers,
1430 # so we filter out any placeholders that don't look like a
1430 # so we filter out any placeholders that don't look like a
1431 # symlink
1431 # symlink
1432 sane = []
1432 sane = []
1433 for f in files:
1433 for f in files:
1434 if self.flags(f) == 'l':
1434 if self.flags(f) == 'l':
1435 d = self[f].data()
1435 d = self[f].data()
1436 if (d == '' or len(d) >= 1024 or '\n' in d
1436 if (d == '' or len(d) >= 1024 or '\n' in d
1437 or stringutil.binary(d)):
1437 or stringutil.binary(d)):
1438 self._repo.ui.debug('ignoring suspect symlink placeholder'
1438 self._repo.ui.debug('ignoring suspect symlink placeholder'
1439 ' "%s"\n' % f)
1439 ' "%s"\n' % f)
1440 continue
1440 continue
1441 sane.append(f)
1441 sane.append(f)
1442 return sane
1442 return sane
1443
1443
1444 def _checklookup(self, files):
1444 def _checklookup(self, files):
1445 # check for any possibly clean files
1445 # check for any possibly clean files
1446 if not files:
1446 if not files:
1447 return [], [], []
1447 return [], [], []
1448
1448
1449 modified = []
1449 modified = []
1450 deleted = []
1450 deleted = []
1451 fixup = []
1451 fixup = []
1452 pctx = self._parents[0]
1452 pctx = self._parents[0]
1453 # do a full compare of any files that might have changed
1453 # do a full compare of any files that might have changed
1454 for f in sorted(files):
1454 for f in sorted(files):
1455 try:
1455 try:
1456 # This will return True for a file that got replaced by a
1456 # This will return True for a file that got replaced by a
1457 # directory in the interim, but fixing that is pretty hard.
1457 # directory in the interim, but fixing that is pretty hard.
1458 if (f not in pctx or self.flags(f) != pctx.flags(f)
1458 if (f not in pctx or self.flags(f) != pctx.flags(f)
1459 or pctx[f].cmp(self[f])):
1459 or pctx[f].cmp(self[f])):
1460 modified.append(f)
1460 modified.append(f)
1461 else:
1461 else:
1462 fixup.append(f)
1462 fixup.append(f)
1463 except (IOError, OSError):
1463 except (IOError, OSError):
1464 # A file become inaccessible in between? Mark it as deleted,
1464 # A file become inaccessible in between? Mark it as deleted,
1465 # matching dirstate behavior (issue5584).
1465 # matching dirstate behavior (issue5584).
1466 # The dirstate has more complex behavior around whether a
1466 # The dirstate has more complex behavior around whether a
1467 # missing file matches a directory, etc, but we don't need to
1467 # missing file matches a directory, etc, but we don't need to
1468 # bother with that: if f has made it to this point, we're sure
1468 # bother with that: if f has made it to this point, we're sure
1469 # it's in the dirstate.
1469 # it's in the dirstate.
1470 deleted.append(f)
1470 deleted.append(f)
1471
1471
1472 return modified, deleted, fixup
1472 return modified, deleted, fixup
1473
1473
1474 def _poststatusfixup(self, status, fixup):
1474 def _poststatusfixup(self, status, fixup):
1475 """update dirstate for files that are actually clean"""
1475 """update dirstate for files that are actually clean"""
1476 poststatus = self._repo.postdsstatus()
1476 poststatus = self._repo.postdsstatus()
1477 if fixup or poststatus:
1477 if fixup or poststatus:
1478 try:
1478 try:
1479 oldid = self._repo.dirstate.identity()
1479 oldid = self._repo.dirstate.identity()
1480
1480
1481 # updating the dirstate is optional
1481 # updating the dirstate is optional
1482 # so we don't wait on the lock
1482 # so we don't wait on the lock
1483 # wlock can invalidate the dirstate, so cache normal _after_
1483 # wlock can invalidate the dirstate, so cache normal _after_
1484 # taking the lock
1484 # taking the lock
1485 with self._repo.wlock(False):
1485 with self._repo.wlock(False):
1486 if self._repo.dirstate.identity() == oldid:
1486 if self._repo.dirstate.identity() == oldid:
1487 if fixup:
1487 if fixup:
1488 normal = self._repo.dirstate.normal
1488 normal = self._repo.dirstate.normal
1489 for f in fixup:
1489 for f in fixup:
1490 normal(f)
1490 normal(f)
1491 # write changes out explicitly, because nesting
1491 # write changes out explicitly, because nesting
1492 # wlock at runtime may prevent 'wlock.release()'
1492 # wlock at runtime may prevent 'wlock.release()'
1493 # after this block from doing so for subsequent
1493 # after this block from doing so for subsequent
1494 # changing files
1494 # changing files
1495 tr = self._repo.currenttransaction()
1495 tr = self._repo.currenttransaction()
1496 self._repo.dirstate.write(tr)
1496 self._repo.dirstate.write(tr)
1497
1497
1498 if poststatus:
1498 if poststatus:
1499 for ps in poststatus:
1499 for ps in poststatus:
1500 ps(self, status)
1500 ps(self, status)
1501 else:
1501 else:
1502 # in this case, writing changes out breaks
1502 # in this case, writing changes out breaks
1503 # consistency, because .hg/dirstate was
1503 # consistency, because .hg/dirstate was
1504 # already changed simultaneously after last
1504 # already changed simultaneously after last
1505 # caching (see also issue5584 for detail)
1505 # caching (see also issue5584 for detail)
1506 self._repo.ui.debug('skip updating dirstate: '
1506 self._repo.ui.debug('skip updating dirstate: '
1507 'identity mismatch\n')
1507 'identity mismatch\n')
1508 except error.LockError:
1508 except error.LockError:
1509 pass
1509 pass
1510 finally:
1510 finally:
1511 # Even if the wlock couldn't be grabbed, clear out the list.
1511 # Even if the wlock couldn't be grabbed, clear out the list.
1512 self._repo.clearpostdsstatus()
1512 self._repo.clearpostdsstatus()
1513
1513
1514 def _dirstatestatus(self, match, ignored=False, clean=False, unknown=False):
1514 def _dirstatestatus(self, match, ignored=False, clean=False, unknown=False):
1515 '''Gets the status from the dirstate -- internal use only.'''
1515 '''Gets the status from the dirstate -- internal use only.'''
1516 subrepos = []
1516 subrepos = []
1517 if '.hgsub' in self:
1517 if '.hgsub' in self:
1518 subrepos = sorted(self.substate)
1518 subrepos = sorted(self.substate)
1519 cmp, s = self._repo.dirstate.status(match, subrepos, ignored=ignored,
1519 cmp, s = self._repo.dirstate.status(match, subrepos, ignored=ignored,
1520 clean=clean, unknown=unknown)
1520 clean=clean, unknown=unknown)
1521
1521
1522 # check for any possibly clean files
1522 # check for any possibly clean files
1523 fixup = []
1523 fixup = []
1524 if cmp:
1524 if cmp:
1525 modified2, deleted2, fixup = self._checklookup(cmp)
1525 modified2, deleted2, fixup = self._checklookup(cmp)
1526 s.modified.extend(modified2)
1526 s.modified.extend(modified2)
1527 s.deleted.extend(deleted2)
1527 s.deleted.extend(deleted2)
1528
1528
1529 if fixup and clean:
1529 if fixup and clean:
1530 s.clean.extend(fixup)
1530 s.clean.extend(fixup)
1531
1531
1532 self._poststatusfixup(s, fixup)
1532 self._poststatusfixup(s, fixup)
1533
1533
1534 if match.always():
1534 if match.always():
1535 # cache for performance
1535 # cache for performance
1536 if s.unknown or s.ignored or s.clean:
1536 if s.unknown or s.ignored or s.clean:
1537 # "_status" is cached with list*=False in the normal route
1537 # "_status" is cached with list*=False in the normal route
1538 self._status = scmutil.status(s.modified, s.added, s.removed,
1538 self._status = scmutil.status(s.modified, s.added, s.removed,
1539 s.deleted, [], [], [])
1539 s.deleted, [], [], [])
1540 else:
1540 else:
1541 self._status = s
1541 self._status = s
1542
1542
1543 return s
1543 return s
1544
1544
1545 @propertycache
1545 @propertycache
1546 def _manifest(self):
1546 def _manifest(self):
1547 """generate a manifest corresponding to the values in self._status
1547 """generate a manifest corresponding to the values in self._status
1548
1548
1549 This reuse the file nodeid from parent, but we use special node
1549 This reuse the file nodeid from parent, but we use special node
1550 identifiers for added and modified files. This is used by manifests
1550 identifiers for added and modified files. This is used by manifests
1551 merge to see that files are different and by update logic to avoid
1551 merge to see that files are different and by update logic to avoid
1552 deleting newly added files.
1552 deleting newly added files.
1553 """
1553 """
1554 return self._buildstatusmanifest(self._status)
1554 return self._buildstatusmanifest(self._status)
1555
1555
1556 def _buildstatusmanifest(self, status):
1556 def _buildstatusmanifest(self, status):
1557 """Builds a manifest that includes the given status results."""
1557 """Builds a manifest that includes the given status results."""
1558 parents = self.parents()
1558 parents = self.parents()
1559
1559
1560 man = parents[0].manifest().copy()
1560 man = parents[0].manifest().copy()
1561
1561
1562 ff = self._flagfunc
1562 ff = self._flagfunc
1563 for i, l in ((addednodeid, status.added),
1563 for i, l in ((addednodeid, status.added),
1564 (modifiednodeid, status.modified)):
1564 (modifiednodeid, status.modified)):
1565 for f in l:
1565 for f in l:
1566 man[f] = i
1566 man[f] = i
1567 try:
1567 try:
1568 man.setflag(f, ff(f))
1568 man.setflag(f, ff(f))
1569 except OSError:
1569 except OSError:
1570 pass
1570 pass
1571
1571
1572 for f in status.deleted + status.removed:
1572 for f in status.deleted + status.removed:
1573 if f in man:
1573 if f in man:
1574 del man[f]
1574 del man[f]
1575
1575
1576 return man
1576 return man
1577
1577
1578 def _buildstatus(self, other, s, match, listignored, listclean,
1578 def _buildstatus(self, other, s, match, listignored, listclean,
1579 listunknown):
1579 listunknown):
1580 """build a status with respect to another context
1580 """build a status with respect to another context
1581
1581
1582 This includes logic for maintaining the fast path of status when
1582 This includes logic for maintaining the fast path of status when
1583 comparing the working directory against its parent, which is to skip
1583 comparing the working directory against its parent, which is to skip
1584 building a new manifest if self (working directory) is not comparing
1584 building a new manifest if self (working directory) is not comparing
1585 against its parent (repo['.']).
1585 against its parent (repo['.']).
1586 """
1586 """
1587 s = self._dirstatestatus(match, listignored, listclean, listunknown)
1587 s = self._dirstatestatus(match, listignored, listclean, listunknown)
1588 # Filter out symlinks that, in the case of FAT32 and NTFS filesystems,
1588 # Filter out symlinks that, in the case of FAT32 and NTFS filesystems,
1589 # might have accidentally ended up with the entire contents of the file
1589 # might have accidentally ended up with the entire contents of the file
1590 # they are supposed to be linking to.
1590 # they are supposed to be linking to.
1591 s.modified[:] = self._filtersuspectsymlink(s.modified)
1591 s.modified[:] = self._filtersuspectsymlink(s.modified)
1592 if other != self._repo['.']:
1592 if other != self._repo['.']:
1593 s = super(workingctx, self)._buildstatus(other, s, match,
1593 s = super(workingctx, self)._buildstatus(other, s, match,
1594 listignored, listclean,
1594 listignored, listclean,
1595 listunknown)
1595 listunknown)
1596 return s
1596 return s
1597
1597
1598 def _matchstatus(self, other, match):
1598 def _matchstatus(self, other, match):
1599 """override the match method with a filter for directory patterns
1599 """override the match method with a filter for directory patterns
1600
1600
1601 We use inheritance to customize the match.bad method only in cases of
1601 We use inheritance to customize the match.bad method only in cases of
1602 workingctx since it belongs only to the working directory when
1602 workingctx since it belongs only to the working directory when
1603 comparing against the parent changeset.
1603 comparing against the parent changeset.
1604
1604
1605 If we aren't comparing against the working directory's parent, then we
1605 If we aren't comparing against the working directory's parent, then we
1606 just use the default match object sent to us.
1606 just use the default match object sent to us.
1607 """
1607 """
1608 if other != self._repo['.']:
1608 if other != self._repo['.']:
1609 def bad(f, msg):
1609 def bad(f, msg):
1610 # 'f' may be a directory pattern from 'match.files()',
1610 # 'f' may be a directory pattern from 'match.files()',
1611 # so 'f not in ctx1' is not enough
1611 # so 'f not in ctx1' is not enough
1612 if f not in other and not other.hasdir(f):
1612 if f not in other and not other.hasdir(f):
1613 self._repo.ui.warn('%s: %s\n' %
1613 self._repo.ui.warn('%s: %s\n' %
1614 (self._repo.dirstate.pathto(f), msg))
1614 (self._repo.dirstate.pathto(f), msg))
1615 match.bad = bad
1615 match.bad = bad
1616 return match
1616 return match
1617
1617
1618 def markcommitted(self, node):
1618 def markcommitted(self, node):
1619 super(workingctx, self).markcommitted(node)
1619 super(workingctx, self).markcommitted(node)
1620
1620
1621 sparse.aftercommit(self._repo, node)
1621 sparse.aftercommit(self._repo, node)
1622
1622
1623 class committablefilectx(basefilectx):
1623 class committablefilectx(basefilectx):
1624 """A committablefilectx provides common functionality for a file context
1624 """A committablefilectx provides common functionality for a file context
1625 that wants the ability to commit, e.g. workingfilectx or memfilectx."""
1625 that wants the ability to commit, e.g. workingfilectx or memfilectx."""
1626 def __init__(self, repo, path, filelog=None, ctx=None):
1626 def __init__(self, repo, path, filelog=None, ctx=None):
1627 self._repo = repo
1627 self._repo = repo
1628 self._path = path
1628 self._path = path
1629 self._changeid = None
1629 self._changeid = None
1630 self._filerev = self._filenode = None
1630 self._filerev = self._filenode = None
1631
1631
1632 if filelog is not None:
1632 if filelog is not None:
1633 self._filelog = filelog
1633 self._filelog = filelog
1634 if ctx:
1634 if ctx:
1635 self._changectx = ctx
1635 self._changectx = ctx
1636
1636
1637 def __nonzero__(self):
1637 def __nonzero__(self):
1638 return True
1638 return True
1639
1639
1640 __bool__ = __nonzero__
1640 __bool__ = __nonzero__
1641
1641
1642 def linkrev(self):
1642 def linkrev(self):
1643 # linked to self._changectx no matter if file is modified or not
1643 # linked to self._changectx no matter if file is modified or not
1644 return self.rev()
1644 return self.rev()
1645
1645
1646 def parents(self):
1646 def parents(self):
1647 '''return parent filectxs, following copies if necessary'''
1647 '''return parent filectxs, following copies if necessary'''
1648 def filenode(ctx, path):
1648 def filenode(ctx, path):
1649 return ctx._manifest.get(path, nullid)
1649 return ctx._manifest.get(path, nullid)
1650
1650
1651 path = self._path
1651 path = self._path
1652 fl = self._filelog
1652 fl = self._filelog
1653 pcl = self._changectx._parents
1653 pcl = self._changectx._parents
1654 renamed = self.renamed()
1654 renamed = self.renamed()
1655
1655
1656 if renamed:
1656 if renamed:
1657 pl = [renamed + (None,)]
1657 pl = [renamed + (None,)]
1658 else:
1658 else:
1659 pl = [(path, filenode(pcl[0], path), fl)]
1659 pl = [(path, filenode(pcl[0], path), fl)]
1660
1660
1661 for pc in pcl[1:]:
1661 for pc in pcl[1:]:
1662 pl.append((path, filenode(pc, path), fl))
1662 pl.append((path, filenode(pc, path), fl))
1663
1663
1664 return [self._parentfilectx(p, fileid=n, filelog=l)
1664 return [self._parentfilectx(p, fileid=n, filelog=l)
1665 for p, n, l in pl if n != nullid]
1665 for p, n, l in pl if n != nullid]
1666
1666
1667 def children(self):
1667 def children(self):
1668 return []
1668 return []
1669
1669
1670 class workingfilectx(committablefilectx):
1670 class workingfilectx(committablefilectx):
1671 """A workingfilectx object makes access to data related to a particular
1671 """A workingfilectx object makes access to data related to a particular
1672 file in the working directory convenient."""
1672 file in the working directory convenient."""
1673 def __init__(self, repo, path, filelog=None, workingctx=None):
1673 def __init__(self, repo, path, filelog=None, workingctx=None):
1674 super(workingfilectx, self).__init__(repo, path, filelog, workingctx)
1674 super(workingfilectx, self).__init__(repo, path, filelog, workingctx)
1675
1675
1676 @propertycache
1676 @propertycache
1677 def _changectx(self):
1677 def _changectx(self):
1678 return workingctx(self._repo)
1678 return workingctx(self._repo)
1679
1679
1680 def data(self):
1680 def data(self):
1681 return self._repo.wread(self._path)
1681 return self._repo.wread(self._path)
1682 def renamed(self):
1682 def renamed(self):
1683 rp = self._repo.dirstate.copied(self._path)
1683 rp = self._repo.dirstate.copied(self._path)
1684 if not rp:
1684 if not rp:
1685 return None
1685 return None
1686 return rp, self._changectx._parents[0]._manifest.get(rp, nullid)
1686 return rp, self._changectx._parents[0]._manifest.get(rp, nullid)
1687
1687
1688 def size(self):
1688 def size(self):
1689 return self._repo.wvfs.lstat(self._path).st_size
1689 return self._repo.wvfs.lstat(self._path).st_size
1690 def date(self):
1690 def date(self):
1691 t, tz = self._changectx.date()
1691 t, tz = self._changectx.date()
1692 try:
1692 try:
1693 return (self._repo.wvfs.lstat(self._path)[stat.ST_MTIME], tz)
1693 return (self._repo.wvfs.lstat(self._path)[stat.ST_MTIME], tz)
1694 except OSError as err:
1694 except OSError as err:
1695 if err.errno != errno.ENOENT:
1695 if err.errno != errno.ENOENT:
1696 raise
1696 raise
1697 return (t, tz)
1697 return (t, tz)
1698
1698
1699 def exists(self):
1699 def exists(self):
1700 return self._repo.wvfs.exists(self._path)
1700 return self._repo.wvfs.exists(self._path)
1701
1701
1702 def lexists(self):
1702 def lexists(self):
1703 return self._repo.wvfs.lexists(self._path)
1703 return self._repo.wvfs.lexists(self._path)
1704
1704
1705 def audit(self):
1705 def audit(self):
1706 return self._repo.wvfs.audit(self._path)
1706 return self._repo.wvfs.audit(self._path)
1707
1707
1708 def cmp(self, fctx):
1708 def cmp(self, fctx):
1709 """compare with other file context
1709 """compare with other file context
1710
1710
1711 returns True if different than fctx.
1711 returns True if different than fctx.
1712 """
1712 """
1713 # fctx should be a filectx (not a workingfilectx)
1713 # fctx should be a filectx (not a workingfilectx)
1714 # invert comparison to reuse the same code path
1714 # invert comparison to reuse the same code path
1715 return fctx.cmp(self)
1715 return fctx.cmp(self)
1716
1716
1717 def remove(self, ignoremissing=False):
1717 def remove(self, ignoremissing=False):
1718 """wraps unlink for a repo's working directory"""
1718 """wraps unlink for a repo's working directory"""
1719 rmdir = self._repo.ui.configbool('experimental', 'removeemptydirs')
1719 rmdir = self._repo.ui.configbool('experimental', 'removeemptydirs')
1720 self._repo.wvfs.unlinkpath(self._path, ignoremissing=ignoremissing,
1720 self._repo.wvfs.unlinkpath(self._path, ignoremissing=ignoremissing,
1721 rmdir=rmdir)
1721 rmdir=rmdir)
1722
1722
1723 def write(self, data, flags, backgroundclose=False, **kwargs):
1723 def write(self, data, flags, backgroundclose=False, **kwargs):
1724 """wraps repo.wwrite"""
1724 """wraps repo.wwrite"""
1725 self._repo.wwrite(self._path, data, flags,
1725 self._repo.wwrite(self._path, data, flags,
1726 backgroundclose=backgroundclose,
1726 backgroundclose=backgroundclose,
1727 **kwargs)
1727 **kwargs)
1728
1728
1729 def markcopied(self, src):
1729 def markcopied(self, src):
1730 """marks this file a copy of `src`"""
1730 """marks this file a copy of `src`"""
1731 if self._repo.dirstate[self._path] in "nma":
1731 if self._repo.dirstate[self._path] in "nma":
1732 self._repo.dirstate.copy(src, self._path)
1732 self._repo.dirstate.copy(src, self._path)
1733
1733
1734 def clearunknown(self):
1734 def clearunknown(self):
1735 """Removes conflicting items in the working directory so that
1735 """Removes conflicting items in the working directory so that
1736 ``write()`` can be called successfully.
1736 ``write()`` can be called successfully.
1737 """
1737 """
1738 wvfs = self._repo.wvfs
1738 wvfs = self._repo.wvfs
1739 f = self._path
1739 f = self._path
1740 wvfs.audit(f)
1740 wvfs.audit(f)
1741 if wvfs.isdir(f) and not wvfs.islink(f):
1741 if wvfs.isdir(f) and not wvfs.islink(f):
1742 wvfs.rmtree(f, forcibly=True)
1742 wvfs.rmtree(f, forcibly=True)
1743 if self._repo.ui.configbool('experimental', 'merge.checkpathconflicts'):
1743 if self._repo.ui.configbool('experimental', 'merge.checkpathconflicts'):
1744 for p in reversed(list(util.finddirs(f))):
1744 for p in reversed(list(util.finddirs(f))):
1745 if wvfs.isfileorlink(p):
1745 if wvfs.isfileorlink(p):
1746 wvfs.unlink(p)
1746 wvfs.unlink(p)
1747 break
1747 break
1748
1748
1749 def setflags(self, l, x):
1749 def setflags(self, l, x):
1750 self._repo.wvfs.setflags(self._path, l, x)
1750 self._repo.wvfs.setflags(self._path, l, x)
1751
1751
1752 class overlayworkingctx(committablectx):
1752 class overlayworkingctx(committablectx):
1753 """Wraps another mutable context with a write-back cache that can be
1753 """Wraps another mutable context with a write-back cache that can be
1754 converted into a commit context.
1754 converted into a commit context.
1755
1755
1756 self._cache[path] maps to a dict with keys: {
1756 self._cache[path] maps to a dict with keys: {
1757 'exists': bool?
1757 'exists': bool?
1758 'date': date?
1758 'date': date?
1759 'data': str?
1759 'data': str?
1760 'flags': str?
1760 'flags': str?
1761 'copied': str? (path or None)
1761 'copied': str? (path or None)
1762 }
1762 }
1763 If `exists` is True, `flags` must be non-None and 'date' is non-None. If it
1763 If `exists` is True, `flags` must be non-None and 'date' is non-None. If it
1764 is `False`, the file was deleted.
1764 is `False`, the file was deleted.
1765 """
1765 """
1766
1766
1767 def __init__(self, repo):
1767 def __init__(self, repo):
1768 super(overlayworkingctx, self).__init__(repo)
1768 super(overlayworkingctx, self).__init__(repo)
1769 self.clean()
1769 self.clean()
1770
1770
1771 def setbase(self, wrappedctx):
1771 def setbase(self, wrappedctx):
1772 self._wrappedctx = wrappedctx
1772 self._wrappedctx = wrappedctx
1773 self._parents = [wrappedctx]
1773 self._parents = [wrappedctx]
1774 # Drop old manifest cache as it is now out of date.
1774 # Drop old manifest cache as it is now out of date.
1775 # This is necessary when, e.g., rebasing several nodes with one
1775 # This is necessary when, e.g., rebasing several nodes with one
1776 # ``overlayworkingctx`` (e.g. with --collapse).
1776 # ``overlayworkingctx`` (e.g. with --collapse).
1777 util.clearcachedproperty(self, '_manifest')
1777 util.clearcachedproperty(self, '_manifest')
1778
1778
1779 def data(self, path):
1779 def data(self, path):
1780 if self.isdirty(path):
1780 if self.isdirty(path):
1781 if self._cache[path]['exists']:
1781 if self._cache[path]['exists']:
1782 if self._cache[path]['data']:
1782 if self._cache[path]['data']:
1783 return self._cache[path]['data']
1783 return self._cache[path]['data']
1784 else:
1784 else:
1785 # Must fallback here, too, because we only set flags.
1785 # Must fallback here, too, because we only set flags.
1786 return self._wrappedctx[path].data()
1786 return self._wrappedctx[path].data()
1787 else:
1787 else:
1788 raise error.ProgrammingError("No such file or directory: %s" %
1788 raise error.ProgrammingError("No such file or directory: %s" %
1789 path)
1789 path)
1790 else:
1790 else:
1791 return self._wrappedctx[path].data()
1791 return self._wrappedctx[path].data()
1792
1792
1793 @propertycache
1793 @propertycache
1794 def _manifest(self):
1794 def _manifest(self):
1795 parents = self.parents()
1795 parents = self.parents()
1796 man = parents[0].manifest().copy()
1796 man = parents[0].manifest().copy()
1797
1797
1798 flag = self._flagfunc
1798 flag = self._flagfunc
1799 for path in self.added():
1799 for path in self.added():
1800 man[path] = addednodeid
1800 man[path] = addednodeid
1801 man.setflag(path, flag(path))
1801 man.setflag(path, flag(path))
1802 for path in self.modified():
1802 for path in self.modified():
1803 man[path] = modifiednodeid
1803 man[path] = modifiednodeid
1804 man.setflag(path, flag(path))
1804 man.setflag(path, flag(path))
1805 for path in self.removed():
1805 for path in self.removed():
1806 del man[path]
1806 del man[path]
1807 return man
1807 return man
1808
1808
1809 @propertycache
1809 @propertycache
1810 def _flagfunc(self):
1810 def _flagfunc(self):
1811 def f(path):
1811 def f(path):
1812 return self._cache[path]['flags']
1812 return self._cache[path]['flags']
1813 return f
1813 return f
1814
1814
1815 def files(self):
1815 def files(self):
1816 return sorted(self.added() + self.modified() + self.removed())
1816 return sorted(self.added() + self.modified() + self.removed())
1817
1817
1818 def modified(self):
1818 def modified(self):
1819 return [f for f in self._cache.keys() if self._cache[f]['exists'] and
1819 return [f for f in self._cache.keys() if self._cache[f]['exists'] and
1820 self._existsinparent(f)]
1820 self._existsinparent(f)]
1821
1821
1822 def added(self):
1822 def added(self):
1823 return [f for f in self._cache.keys() if self._cache[f]['exists'] and
1823 return [f for f in self._cache.keys() if self._cache[f]['exists'] and
1824 not self._existsinparent(f)]
1824 not self._existsinparent(f)]
1825
1825
1826 def removed(self):
1826 def removed(self):
1827 return [f for f in self._cache.keys() if
1827 return [f for f in self._cache.keys() if
1828 not self._cache[f]['exists'] and self._existsinparent(f)]
1828 not self._cache[f]['exists'] and self._existsinparent(f)]
1829
1829
1830 def isinmemory(self):
1830 def isinmemory(self):
1831 return True
1831 return True
1832
1832
1833 def filedate(self, path):
1833 def filedate(self, path):
1834 if self.isdirty(path):
1834 if self.isdirty(path):
1835 return self._cache[path]['date']
1835 return self._cache[path]['date']
1836 else:
1836 else:
1837 return self._wrappedctx[path].date()
1837 return self._wrappedctx[path].date()
1838
1838
1839 def markcopied(self, path, origin):
1839 def markcopied(self, path, origin):
1840 if self.isdirty(path):
1840 if self.isdirty(path):
1841 self._cache[path]['copied'] = origin
1841 self._cache[path]['copied'] = origin
1842 else:
1842 else:
1843 raise error.ProgrammingError('markcopied() called on clean context')
1843 raise error.ProgrammingError('markcopied() called on clean context')
1844
1844
1845 def copydata(self, path):
1845 def copydata(self, path):
1846 if self.isdirty(path):
1846 if self.isdirty(path):
1847 return self._cache[path]['copied']
1847 return self._cache[path]['copied']
1848 else:
1848 else:
1849 raise error.ProgrammingError('copydata() called on clean context')
1849 raise error.ProgrammingError('copydata() called on clean context')
1850
1850
1851 def flags(self, path):
1851 def flags(self, path):
1852 if self.isdirty(path):
1852 if self.isdirty(path):
1853 if self._cache[path]['exists']:
1853 if self._cache[path]['exists']:
1854 return self._cache[path]['flags']
1854 return self._cache[path]['flags']
1855 else:
1855 else:
1856 raise error.ProgrammingError("No such file or directory: %s" %
1856 raise error.ProgrammingError("No such file or directory: %s" %
1857 self._path)
1857 self._path)
1858 else:
1858 else:
1859 return self._wrappedctx[path].flags()
1859 return self._wrappedctx[path].flags()
1860
1860
1861 def _existsinparent(self, path):
1861 def _existsinparent(self, path):
1862 try:
1862 try:
1863 # ``commitctx` raises a ``ManifestLookupError`` if a path does not
1863 # ``commitctx` raises a ``ManifestLookupError`` if a path does not
1864 # exist, unlike ``workingctx``, which returns a ``workingfilectx``
1864 # exist, unlike ``workingctx``, which returns a ``workingfilectx``
1865 # with an ``exists()`` function.
1865 # with an ``exists()`` function.
1866 self._wrappedctx[path]
1866 self._wrappedctx[path]
1867 return True
1867 return True
1868 except error.ManifestLookupError:
1868 except error.ManifestLookupError:
1869 return False
1869 return False
1870
1870
1871 def _auditconflicts(self, path):
1871 def _auditconflicts(self, path):
1872 """Replicates conflict checks done by wvfs.write().
1872 """Replicates conflict checks done by wvfs.write().
1873
1873
1874 Since we never write to the filesystem and never call `applyupdates` in
1874 Since we never write to the filesystem and never call `applyupdates` in
1875 IMM, we'll never check that a path is actually writable -- e.g., because
1875 IMM, we'll never check that a path is actually writable -- e.g., because
1876 it adds `a/foo`, but `a` is actually a file in the other commit.
1876 it adds `a/foo`, but `a` is actually a file in the other commit.
1877 """
1877 """
1878 def fail(path, component):
1878 def fail(path, component):
1879 # p1() is the base and we're receiving "writes" for p2()'s
1879 # p1() is the base and we're receiving "writes" for p2()'s
1880 # files.
1880 # files.
1881 if 'l' in self.p1()[component].flags():
1881 if 'l' in self.p1()[component].flags():
1882 raise error.Abort("error: %s conflicts with symlink %s "
1882 raise error.Abort("error: %s conflicts with symlink %s "
1883 "in %s." % (path, component,
1883 "in %s." % (path, component,
1884 self.p1().rev()))
1884 self.p1().rev()))
1885 else:
1885 else:
1886 raise error.Abort("error: '%s' conflicts with file '%s' in "
1886 raise error.Abort("error: '%s' conflicts with file '%s' in "
1887 "%s." % (path, component,
1887 "%s." % (path, component,
1888 self.p1().rev()))
1888 self.p1().rev()))
1889
1889
1890 # Test that each new directory to be created to write this path from p2
1890 # Test that each new directory to be created to write this path from p2
1891 # is not a file in p1.
1891 # is not a file in p1.
1892 components = path.split('/')
1892 components = path.split('/')
1893 for i in xrange(len(components)):
1893 for i in xrange(len(components)):
1894 component = "/".join(components[0:i])
1894 component = "/".join(components[0:i])
1895 if component in self.p1():
1895 if component in self.p1():
1896 fail(path, component)
1896 fail(path, component)
1897
1897
1898 # Test the other direction -- that this path from p2 isn't a directory
1898 # Test the other direction -- that this path from p2 isn't a directory
1899 # in p1 (test that p1 doesn't any paths matching `path/*`).
1899 # in p1 (test that p1 doesn't any paths matching `path/*`).
1900 match = matchmod.match('/', '', [path + '/'], default=b'relpath')
1900 match = matchmod.match('/', '', [path + '/'], default=b'relpath')
1901 matches = self.p1().manifest().matches(match)
1901 matches = self.p1().manifest().matches(match)
1902 if len(matches) > 0:
1902 if len(matches) > 0:
1903 if len(matches) == 1 and matches.keys()[0] == path:
1903 if len(matches) == 1 and matches.keys()[0] == path:
1904 return
1904 return
1905 raise error.Abort("error: file '%s' cannot be written because "
1905 raise error.Abort("error: file '%s' cannot be written because "
1906 " '%s/' is a folder in %s (containing %d "
1906 " '%s/' is a folder in %s (containing %d "
1907 "entries: %s)"
1907 "entries: %s)"
1908 % (path, path, self.p1(), len(matches),
1908 % (path, path, self.p1(), len(matches),
1909 ', '.join(matches.keys())))
1909 ', '.join(matches.keys())))
1910
1910
1911 def write(self, path, data, flags='', **kwargs):
1911 def write(self, path, data, flags='', **kwargs):
1912 if data is None:
1912 if data is None:
1913 raise error.ProgrammingError("data must be non-None")
1913 raise error.ProgrammingError("data must be non-None")
1914 self._auditconflicts(path)
1914 self._auditconflicts(path)
1915 self._markdirty(path, exists=True, data=data, date=dateutil.makedate(),
1915 self._markdirty(path, exists=True, data=data, date=dateutil.makedate(),
1916 flags=flags)
1916 flags=flags)
1917
1917
1918 def setflags(self, path, l, x):
1918 def setflags(self, path, l, x):
1919 self._markdirty(path, exists=True, date=dateutil.makedate(),
1919 self._markdirty(path, exists=True, date=dateutil.makedate(),
1920 flags=(l and 'l' or '') + (x and 'x' or ''))
1920 flags=(l and 'l' or '') + (x and 'x' or ''))
1921
1921
1922 def remove(self, path):
1922 def remove(self, path):
1923 self._markdirty(path, exists=False)
1923 self._markdirty(path, exists=False)
1924
1924
1925 def exists(self, path):
1925 def exists(self, path):
1926 """exists behaves like `lexists`, but needs to follow symlinks and
1926 """exists behaves like `lexists`, but needs to follow symlinks and
1927 return False if they are broken.
1927 return False if they are broken.
1928 """
1928 """
1929 if self.isdirty(path):
1929 if self.isdirty(path):
1930 # If this path exists and is a symlink, "follow" it by calling
1930 # If this path exists and is a symlink, "follow" it by calling
1931 # exists on the destination path.
1931 # exists on the destination path.
1932 if (self._cache[path]['exists'] and
1932 if (self._cache[path]['exists'] and
1933 'l' in self._cache[path]['flags']):
1933 'l' in self._cache[path]['flags']):
1934 return self.exists(self._cache[path]['data'].strip())
1934 return self.exists(self._cache[path]['data'].strip())
1935 else:
1935 else:
1936 return self._cache[path]['exists']
1936 return self._cache[path]['exists']
1937
1937
1938 return self._existsinparent(path)
1938 return self._existsinparent(path)
1939
1939
1940 def lexists(self, path):
1940 def lexists(self, path):
1941 """lexists returns True if the path exists"""
1941 """lexists returns True if the path exists"""
1942 if self.isdirty(path):
1942 if self.isdirty(path):
1943 return self._cache[path]['exists']
1943 return self._cache[path]['exists']
1944
1944
1945 return self._existsinparent(path)
1945 return self._existsinparent(path)
1946
1946
1947 def size(self, path):
1947 def size(self, path):
1948 if self.isdirty(path):
1948 if self.isdirty(path):
1949 if self._cache[path]['exists']:
1949 if self._cache[path]['exists']:
1950 return len(self._cache[path]['data'])
1950 return len(self._cache[path]['data'])
1951 else:
1951 else:
1952 raise error.ProgrammingError("No such file or directory: %s" %
1952 raise error.ProgrammingError("No such file or directory: %s" %
1953 self._path)
1953 self._path)
1954 return self._wrappedctx[path].size()
1954 return self._wrappedctx[path].size()
1955
1955
1956 def tomemctx(self, text, branch=None, extra=None, date=None, parents=None,
1956 def tomemctx(self, text, branch=None, extra=None, date=None, parents=None,
1957 user=None, editor=None):
1957 user=None, editor=None):
1958 """Converts this ``overlayworkingctx`` into a ``memctx`` ready to be
1958 """Converts this ``overlayworkingctx`` into a ``memctx`` ready to be
1959 committed.
1959 committed.
1960
1960
1961 ``text`` is the commit message.
1961 ``text`` is the commit message.
1962 ``parents`` (optional) are rev numbers.
1962 ``parents`` (optional) are rev numbers.
1963 """
1963 """
1964 # Default parents to the wrapped contexts' if not passed.
1964 # Default parents to the wrapped contexts' if not passed.
1965 if parents is None:
1965 if parents is None:
1966 parents = self._wrappedctx.parents()
1966 parents = self._wrappedctx.parents()
1967 if len(parents) == 1:
1967 if len(parents) == 1:
1968 parents = (parents[0], None)
1968 parents = (parents[0], None)
1969
1969
1970 # ``parents`` is passed as rev numbers; convert to ``commitctxs``.
1970 # ``parents`` is passed as rev numbers; convert to ``commitctxs``.
1971 if parents[1] is None:
1971 if parents[1] is None:
1972 parents = (self._repo[parents[0]], None)
1972 parents = (self._repo[parents[0]], None)
1973 else:
1973 else:
1974 parents = (self._repo[parents[0]], self._repo[parents[1]])
1974 parents = (self._repo[parents[0]], self._repo[parents[1]])
1975
1975
1976 files = self._cache.keys()
1976 files = self._cache.keys()
1977 def getfile(repo, memctx, path):
1977 def getfile(repo, memctx, path):
1978 if self._cache[path]['exists']:
1978 if self._cache[path]['exists']:
1979 return memfilectx(repo, memctx, path,
1979 return memfilectx(repo, memctx, path,
1980 self._cache[path]['data'],
1980 self._cache[path]['data'],
1981 'l' in self._cache[path]['flags'],
1981 'l' in self._cache[path]['flags'],
1982 'x' in self._cache[path]['flags'],
1982 'x' in self._cache[path]['flags'],
1983 self._cache[path]['copied'])
1983 self._cache[path]['copied'])
1984 else:
1984 else:
1985 # Returning None, but including the path in `files`, is
1985 # Returning None, but including the path in `files`, is
1986 # necessary for memctx to register a deletion.
1986 # necessary for memctx to register a deletion.
1987 return None
1987 return None
1988 return memctx(self._repo, parents, text, files, getfile, date=date,
1988 return memctx(self._repo, parents, text, files, getfile, date=date,
1989 extra=extra, user=user, branch=branch, editor=editor)
1989 extra=extra, user=user, branch=branch, editor=editor)
1990
1990
1991 def isdirty(self, path):
1991 def isdirty(self, path):
1992 return path in self._cache
1992 return path in self._cache
1993
1993
1994 def isempty(self):
1994 def isempty(self):
1995 # We need to discard any keys that are actually clean before the empty
1995 # We need to discard any keys that are actually clean before the empty
1996 # commit check.
1996 # commit check.
1997 self._compact()
1997 self._compact()
1998 return len(self._cache) == 0
1998 return len(self._cache) == 0
1999
1999
2000 def clean(self):
2000 def clean(self):
2001 self._cache = {}
2001 self._cache = {}
2002
2002
2003 def _compact(self):
2003 def _compact(self):
2004 """Removes keys from the cache that are actually clean, by comparing
2004 """Removes keys from the cache that are actually clean, by comparing
2005 them with the underlying context.
2005 them with the underlying context.
2006
2006
2007 This can occur during the merge process, e.g. by passing --tool :local
2007 This can occur during the merge process, e.g. by passing --tool :local
2008 to resolve a conflict.
2008 to resolve a conflict.
2009 """
2009 """
2010 keys = []
2010 keys = []
2011 for path in self._cache.keys():
2011 for path in self._cache.keys():
2012 cache = self._cache[path]
2012 cache = self._cache[path]
2013 try:
2013 try:
2014 underlying = self._wrappedctx[path]
2014 underlying = self._wrappedctx[path]
2015 if (underlying.data() == cache['data'] and
2015 if (underlying.data() == cache['data'] and
2016 underlying.flags() == cache['flags']):
2016 underlying.flags() == cache['flags']):
2017 keys.append(path)
2017 keys.append(path)
2018 except error.ManifestLookupError:
2018 except error.ManifestLookupError:
2019 # Path not in the underlying manifest (created).
2019 # Path not in the underlying manifest (created).
2020 continue
2020 continue
2021
2021
2022 for path in keys:
2022 for path in keys:
2023 del self._cache[path]
2023 del self._cache[path]
2024 return keys
2024 return keys
2025
2025
2026 def _markdirty(self, path, exists, data=None, date=None, flags=''):
2026 def _markdirty(self, path, exists, data=None, date=None, flags=''):
2027 self._cache[path] = {
2027 self._cache[path] = {
2028 'exists': exists,
2028 'exists': exists,
2029 'data': data,
2029 'data': data,
2030 'date': date,
2030 'date': date,
2031 'flags': flags,
2031 'flags': flags,
2032 'copied': None,
2032 'copied': None,
2033 }
2033 }
2034
2034
2035 def filectx(self, path, filelog=None):
2035 def filectx(self, path, filelog=None):
2036 return overlayworkingfilectx(self._repo, path, parent=self,
2036 return overlayworkingfilectx(self._repo, path, parent=self,
2037 filelog=filelog)
2037 filelog=filelog)
2038
2038
2039 class overlayworkingfilectx(committablefilectx):
2039 class overlayworkingfilectx(committablefilectx):
2040 """Wrap a ``workingfilectx`` but intercepts all writes into an in-memory
2040 """Wrap a ``workingfilectx`` but intercepts all writes into an in-memory
2041 cache, which can be flushed through later by calling ``flush()``."""
2041 cache, which can be flushed through later by calling ``flush()``."""
2042
2042
2043 def __init__(self, repo, path, filelog=None, parent=None):
2043 def __init__(self, repo, path, filelog=None, parent=None):
2044 super(overlayworkingfilectx, self).__init__(repo, path, filelog,
2044 super(overlayworkingfilectx, self).__init__(repo, path, filelog,
2045 parent)
2045 parent)
2046 self._repo = repo
2046 self._repo = repo
2047 self._parent = parent
2047 self._parent = parent
2048 self._path = path
2048 self._path = path
2049
2049
2050 def cmp(self, fctx):
2050 def cmp(self, fctx):
2051 return self.data() != fctx.data()
2051 return self.data() != fctx.data()
2052
2052
2053 def changectx(self):
2053 def changectx(self):
2054 return self._parent
2054 return self._parent
2055
2055
2056 def data(self):
2056 def data(self):
2057 return self._parent.data(self._path)
2057 return self._parent.data(self._path)
2058
2058
2059 def date(self):
2059 def date(self):
2060 return self._parent.filedate(self._path)
2060 return self._parent.filedate(self._path)
2061
2061
2062 def exists(self):
2062 def exists(self):
2063 return self.lexists()
2063 return self.lexists()
2064
2064
2065 def lexists(self):
2065 def lexists(self):
2066 return self._parent.exists(self._path)
2066 return self._parent.exists(self._path)
2067
2067
2068 def renamed(self):
2068 def renamed(self):
2069 path = self._parent.copydata(self._path)
2069 path = self._parent.copydata(self._path)
2070 if not path:
2070 if not path:
2071 return None
2071 return None
2072 return path, self._changectx._parents[0]._manifest.get(path, nullid)
2072 return path, self._changectx._parents[0]._manifest.get(path, nullid)
2073
2073
2074 def size(self):
2074 def size(self):
2075 return self._parent.size(self._path)
2075 return self._parent.size(self._path)
2076
2076
2077 def markcopied(self, origin):
2077 def markcopied(self, origin):
2078 self._parent.markcopied(self._path, origin)
2078 self._parent.markcopied(self._path, origin)
2079
2079
2080 def audit(self):
2080 def audit(self):
2081 pass
2081 pass
2082
2082
2083 def flags(self):
2083 def flags(self):
2084 return self._parent.flags(self._path)
2084 return self._parent.flags(self._path)
2085
2085
2086 def setflags(self, islink, isexec):
2086 def setflags(self, islink, isexec):
2087 return self._parent.setflags(self._path, islink, isexec)
2087 return self._parent.setflags(self._path, islink, isexec)
2088
2088
2089 def write(self, data, flags, backgroundclose=False, **kwargs):
2089 def write(self, data, flags, backgroundclose=False, **kwargs):
2090 return self._parent.write(self._path, data, flags, **kwargs)
2090 return self._parent.write(self._path, data, flags, **kwargs)
2091
2091
2092 def remove(self, ignoremissing=False):
2092 def remove(self, ignoremissing=False):
2093 return self._parent.remove(self._path)
2093 return self._parent.remove(self._path)
2094
2094
2095 def clearunknown(self):
2095 def clearunknown(self):
2096 pass
2096 pass
2097
2097
2098 class workingcommitctx(workingctx):
2098 class workingcommitctx(workingctx):
2099 """A workingcommitctx object makes access to data related to
2099 """A workingcommitctx object makes access to data related to
2100 the revision being committed convenient.
2100 the revision being committed convenient.
2101
2101
2102 This hides changes in the working directory, if they aren't
2102 This hides changes in the working directory, if they aren't
2103 committed in this context.
2103 committed in this context.
2104 """
2104 """
2105 def __init__(self, repo, changes,
2105 def __init__(self, repo, changes,
2106 text="", user=None, date=None, extra=None):
2106 text="", user=None, date=None, extra=None):
2107 super(workingctx, self).__init__(repo, text, user, date, extra,
2107 super(workingctx, self).__init__(repo, text, user, date, extra,
2108 changes)
2108 changes)
2109
2109
2110 def _dirstatestatus(self, match, ignored=False, clean=False, unknown=False):
2110 def _dirstatestatus(self, match, ignored=False, clean=False, unknown=False):
2111 """Return matched files only in ``self._status``
2111 """Return matched files only in ``self._status``
2112
2112
2113 Uncommitted files appear "clean" via this context, even if
2113 Uncommitted files appear "clean" via this context, even if
2114 they aren't actually so in the working directory.
2114 they aren't actually so in the working directory.
2115 """
2115 """
2116 if clean:
2116 if clean:
2117 clean = [f for f in self._manifest if f not in self._changedset]
2117 clean = [f for f in self._manifest if f not in self._changedset]
2118 else:
2118 else:
2119 clean = []
2119 clean = []
2120 return scmutil.status([f for f in self._status.modified if match(f)],
2120 return scmutil.status([f for f in self._status.modified if match(f)],
2121 [f for f in self._status.added if match(f)],
2121 [f for f in self._status.added if match(f)],
2122 [f for f in self._status.removed if match(f)],
2122 [f for f in self._status.removed if match(f)],
2123 [], [], [], clean)
2123 [], [], [], clean)
2124
2124
2125 @propertycache
2125 @propertycache
2126 def _changedset(self):
2126 def _changedset(self):
2127 """Return the set of files changed in this context
2127 """Return the set of files changed in this context
2128 """
2128 """
2129 changed = set(self._status.modified)
2129 changed = set(self._status.modified)
2130 changed.update(self._status.added)
2130 changed.update(self._status.added)
2131 changed.update(self._status.removed)
2131 changed.update(self._status.removed)
2132 return changed
2132 return changed
2133
2133
2134 def makecachingfilectxfn(func):
2134 def makecachingfilectxfn(func):
2135 """Create a filectxfn that caches based on the path.
2135 """Create a filectxfn that caches based on the path.
2136
2136
2137 We can't use util.cachefunc because it uses all arguments as the cache
2137 We can't use util.cachefunc because it uses all arguments as the cache
2138 key and this creates a cycle since the arguments include the repo and
2138 key and this creates a cycle since the arguments include the repo and
2139 memctx.
2139 memctx.
2140 """
2140 """
2141 cache = {}
2141 cache = {}
2142
2142
2143 def getfilectx(repo, memctx, path):
2143 def getfilectx(repo, memctx, path):
2144 if path not in cache:
2144 if path not in cache:
2145 cache[path] = func(repo, memctx, path)
2145 cache[path] = func(repo, memctx, path)
2146 return cache[path]
2146 return cache[path]
2147
2147
2148 return getfilectx
2148 return getfilectx
2149
2149
2150 def memfilefromctx(ctx):
2150 def memfilefromctx(ctx):
2151 """Given a context return a memfilectx for ctx[path]
2151 """Given a context return a memfilectx for ctx[path]
2152
2152
2153 This is a convenience method for building a memctx based on another
2153 This is a convenience method for building a memctx based on another
2154 context.
2154 context.
2155 """
2155 """
2156 def getfilectx(repo, memctx, path):
2156 def getfilectx(repo, memctx, path):
2157 fctx = ctx[path]
2157 fctx = ctx[path]
2158 # this is weird but apparently we only keep track of one parent
2158 # this is weird but apparently we only keep track of one parent
2159 # (why not only store that instead of a tuple?)
2159 # (why not only store that instead of a tuple?)
2160 copied = fctx.renamed()
2160 copied = fctx.renamed()
2161 if copied:
2161 if copied:
2162 copied = copied[0]
2162 copied = copied[0]
2163 return memfilectx(repo, memctx, path, fctx.data(),
2163 return memfilectx(repo, memctx, path, fctx.data(),
2164 islink=fctx.islink(), isexec=fctx.isexec(),
2164 islink=fctx.islink(), isexec=fctx.isexec(),
2165 copied=copied)
2165 copied=copied)
2166
2166
2167 return getfilectx
2167 return getfilectx
2168
2168
2169 def memfilefrompatch(patchstore):
2169 def memfilefrompatch(patchstore):
2170 """Given a patch (e.g. patchstore object) return a memfilectx
2170 """Given a patch (e.g. patchstore object) return a memfilectx
2171
2171
2172 This is a convenience method for building a memctx based on a patchstore.
2172 This is a convenience method for building a memctx based on a patchstore.
2173 """
2173 """
2174 def getfilectx(repo, memctx, path):
2174 def getfilectx(repo, memctx, path):
2175 data, mode, copied = patchstore.getfile(path)
2175 data, mode, copied = patchstore.getfile(path)
2176 if data is None:
2176 if data is None:
2177 return None
2177 return None
2178 islink, isexec = mode
2178 islink, isexec = mode
2179 return memfilectx(repo, memctx, path, data, islink=islink,
2179 return memfilectx(repo, memctx, path, data, islink=islink,
2180 isexec=isexec, copied=copied)
2180 isexec=isexec, copied=copied)
2181
2181
2182 return getfilectx
2182 return getfilectx
2183
2183
2184 class memctx(committablectx):
2184 class memctx(committablectx):
2185 """Use memctx to perform in-memory commits via localrepo.commitctx().
2185 """Use memctx to perform in-memory commits via localrepo.commitctx().
2186
2186
2187 Revision information is supplied at initialization time while
2187 Revision information is supplied at initialization time while
2188 related files data and is made available through a callback
2188 related files data and is made available through a callback
2189 mechanism. 'repo' is the current localrepo, 'parents' is a
2189 mechanism. 'repo' is the current localrepo, 'parents' is a
2190 sequence of two parent revisions identifiers (pass None for every
2190 sequence of two parent revisions identifiers (pass None for every
2191 missing parent), 'text' is the commit message and 'files' lists
2191 missing parent), 'text' is the commit message and 'files' lists
2192 names of files touched by the revision (normalized and relative to
2192 names of files touched by the revision (normalized and relative to
2193 repository root).
2193 repository root).
2194
2194
2195 filectxfn(repo, memctx, path) is a callable receiving the
2195 filectxfn(repo, memctx, path) is a callable receiving the
2196 repository, the current memctx object and the normalized path of
2196 repository, the current memctx object and the normalized path of
2197 requested file, relative to repository root. It is fired by the
2197 requested file, relative to repository root. It is fired by the
2198 commit function for every file in 'files', but calls order is
2198 commit function for every file in 'files', but calls order is
2199 undefined. If the file is available in the revision being
2199 undefined. If the file is available in the revision being
2200 committed (updated or added), filectxfn returns a memfilectx
2200 committed (updated or added), filectxfn returns a memfilectx
2201 object. If the file was removed, filectxfn return None for recent
2201 object. If the file was removed, filectxfn return None for recent
2202 Mercurial. Moved files are represented by marking the source file
2202 Mercurial. Moved files are represented by marking the source file
2203 removed and the new file added with copy information (see
2203 removed and the new file added with copy information (see
2204 memfilectx).
2204 memfilectx).
2205
2205
2206 user receives the committer name and defaults to current
2206 user receives the committer name and defaults to current
2207 repository username, date is the commit date in any format
2207 repository username, date is the commit date in any format
2208 supported by dateutil.parsedate() and defaults to current date, extra
2208 supported by dateutil.parsedate() and defaults to current date, extra
2209 is a dictionary of metadata or is left empty.
2209 is a dictionary of metadata or is left empty.
2210 """
2210 """
2211
2211
2212 # Mercurial <= 3.1 expects the filectxfn to raise IOError for missing files.
2212 # Mercurial <= 3.1 expects the filectxfn to raise IOError for missing files.
2213 # Extensions that need to retain compatibility across Mercurial 3.1 can use
2213 # Extensions that need to retain compatibility across Mercurial 3.1 can use
2214 # this field to determine what to do in filectxfn.
2214 # this field to determine what to do in filectxfn.
2215 _returnnoneformissingfiles = True
2215 _returnnoneformissingfiles = True
2216
2216
2217 def __init__(self, repo, parents, text, files, filectxfn, user=None,
2217 def __init__(self, repo, parents, text, files, filectxfn, user=None,
2218 date=None, extra=None, branch=None, editor=False):
2218 date=None, extra=None, branch=None, editor=False):
2219 super(memctx, self).__init__(repo, text, user, date, extra)
2219 super(memctx, self).__init__(repo, text, user, date, extra)
2220 self._rev = None
2220 self._rev = None
2221 self._node = None
2221 self._node = None
2222 parents = [(p or nullid) for p in parents]
2222 parents = [(p or nullid) for p in parents]
2223 p1, p2 = parents
2223 p1, p2 = parents
2224 self._parents = [self._repo[p] for p in (p1, p2)]
2224 self._parents = [self._repo[p] for p in (p1, p2)]
2225 files = sorted(set(files))
2225 files = sorted(set(files))
2226 self._files = files
2226 self._files = files
2227 if branch is not None:
2227 if branch is not None:
2228 self._extra['branch'] = encoding.fromlocal(branch)
2228 self._extra['branch'] = encoding.fromlocal(branch)
2229 self.substate = {}
2229 self.substate = {}
2230
2230
2231 if isinstance(filectxfn, patch.filestore):
2231 if isinstance(filectxfn, patch.filestore):
2232 filectxfn = memfilefrompatch(filectxfn)
2232 filectxfn = memfilefrompatch(filectxfn)
2233 elif not callable(filectxfn):
2233 elif not callable(filectxfn):
2234 # if store is not callable, wrap it in a function
2234 # if store is not callable, wrap it in a function
2235 filectxfn = memfilefromctx(filectxfn)
2235 filectxfn = memfilefromctx(filectxfn)
2236
2236
2237 # memoizing increases performance for e.g. vcs convert scenarios.
2237 # memoizing increases performance for e.g. vcs convert scenarios.
2238 self._filectxfn = makecachingfilectxfn(filectxfn)
2238 self._filectxfn = makecachingfilectxfn(filectxfn)
2239
2239
2240 if editor:
2240 if editor:
2241 self._text = editor(self._repo, self, [])
2241 self._text = editor(self._repo, self, [])
2242 self._repo.savecommitmessage(self._text)
2242 self._repo.savecommitmessage(self._text)
2243
2243
2244 def filectx(self, path, filelog=None):
2244 def filectx(self, path, filelog=None):
2245 """get a file context from the working directory
2245 """get a file context from the working directory
2246
2246
2247 Returns None if file doesn't exist and should be removed."""
2247 Returns None if file doesn't exist and should be removed."""
2248 return self._filectxfn(self._repo, self, path)
2248 return self._filectxfn(self._repo, self, path)
2249
2249
2250 def commit(self):
2250 def commit(self):
2251 """commit context to the repo"""
2251 """commit context to the repo"""
2252 return self._repo.commitctx(self)
2252 return self._repo.commitctx(self)
2253
2253
2254 @propertycache
2254 @propertycache
2255 def _manifest(self):
2255 def _manifest(self):
2256 """generate a manifest based on the return values of filectxfn"""
2256 """generate a manifest based on the return values of filectxfn"""
2257
2257
2258 # keep this simple for now; just worry about p1
2258 # keep this simple for now; just worry about p1
2259 pctx = self._parents[0]
2259 pctx = self._parents[0]
2260 man = pctx.manifest().copy()
2260 man = pctx.manifest().copy()
2261
2261
2262 for f in self._status.modified:
2262 for f in self._status.modified:
2263 p1node = nullid
2263 p1node = nullid
2264 p2node = nullid
2264 p2node = nullid
2265 p = pctx[f].parents() # if file isn't in pctx, check p2?
2265 p = pctx[f].parents() # if file isn't in pctx, check p2?
2266 if len(p) > 0:
2266 if len(p) > 0:
2267 p1node = p[0].filenode()
2267 p1node = p[0].filenode()
2268 if len(p) > 1:
2268 if len(p) > 1:
2269 p2node = p[1].filenode()
2269 p2node = p[1].filenode()
2270 man[f] = revlog.hash(self[f].data(), p1node, p2node)
2270 man[f] = revlog.hash(self[f].data(), p1node, p2node)
2271
2271
2272 for f in self._status.added:
2272 for f in self._status.added:
2273 man[f] = revlog.hash(self[f].data(), nullid, nullid)
2273 man[f] = revlog.hash(self[f].data(), nullid, nullid)
2274
2274
2275 for f in self._status.removed:
2275 for f in self._status.removed:
2276 if f in man:
2276 if f in man:
2277 del man[f]
2277 del man[f]
2278
2278
2279 return man
2279 return man
2280
2280
2281 @propertycache
2281 @propertycache
2282 def _status(self):
2282 def _status(self):
2283 """Calculate exact status from ``files`` specified at construction
2283 """Calculate exact status from ``files`` specified at construction
2284 """
2284 """
2285 man1 = self.p1().manifest()
2285 man1 = self.p1().manifest()
2286 p2 = self._parents[1]
2286 p2 = self._parents[1]
2287 # "1 < len(self._parents)" can't be used for checking
2287 # "1 < len(self._parents)" can't be used for checking
2288 # existence of the 2nd parent, because "memctx._parents" is
2288 # existence of the 2nd parent, because "memctx._parents" is
2289 # explicitly initialized by the list, of which length is 2.
2289 # explicitly initialized by the list, of which length is 2.
2290 if p2.node() != nullid:
2290 if p2.node() != nullid:
2291 man2 = p2.manifest()
2291 man2 = p2.manifest()
2292 managing = lambda f: f in man1 or f in man2
2292 managing = lambda f: f in man1 or f in man2
2293 else:
2293 else:
2294 managing = lambda f: f in man1
2294 managing = lambda f: f in man1
2295
2295
2296 modified, added, removed = [], [], []
2296 modified, added, removed = [], [], []
2297 for f in self._files:
2297 for f in self._files:
2298 if not managing(f):
2298 if not managing(f):
2299 added.append(f)
2299 added.append(f)
2300 elif self[f]:
2300 elif self[f]:
2301 modified.append(f)
2301 modified.append(f)
2302 else:
2302 else:
2303 removed.append(f)
2303 removed.append(f)
2304
2304
2305 return scmutil.status(modified, added, removed, [], [], [], [])
2305 return scmutil.status(modified, added, removed, [], [], [], [])
2306
2306
2307 class memfilectx(committablefilectx):
2307 class memfilectx(committablefilectx):
2308 """memfilectx represents an in-memory file to commit.
2308 """memfilectx represents an in-memory file to commit.
2309
2309
2310 See memctx and committablefilectx for more details.
2310 See memctx and committablefilectx for more details.
2311 """
2311 """
2312 def __init__(self, repo, changectx, path, data, islink=False,
2312 def __init__(self, repo, changectx, path, data, islink=False,
2313 isexec=False, copied=None):
2313 isexec=False, copied=None):
2314 """
2314 """
2315 path is the normalized file path relative to repository root.
2315 path is the normalized file path relative to repository root.
2316 data is the file content as a string.
2316 data is the file content as a string.
2317 islink is True if the file is a symbolic link.
2317 islink is True if the file is a symbolic link.
2318 isexec is True if the file is executable.
2318 isexec is True if the file is executable.
2319 copied is the source file path if current file was copied in the
2319 copied is the source file path if current file was copied in the
2320 revision being committed, or None."""
2320 revision being committed, or None."""
2321 super(memfilectx, self).__init__(repo, path, None, changectx)
2321 super(memfilectx, self).__init__(repo, path, None, changectx)
2322 self._data = data
2322 self._data = data
2323 self._flags = (islink and 'l' or '') + (isexec and 'x' or '')
2323 self._flags = (islink and 'l' or '') + (isexec and 'x' or '')
2324 self._copied = None
2324 self._copied = None
2325 if copied:
2325 if copied:
2326 self._copied = (copied, nullid)
2326 self._copied = (copied, nullid)
2327
2327
2328 def data(self):
2328 def data(self):
2329 return self._data
2329 return self._data
2330
2330
2331 def remove(self, ignoremissing=False):
2331 def remove(self, ignoremissing=False):
2332 """wraps unlink for a repo's working directory"""
2332 """wraps unlink for a repo's working directory"""
2333 # need to figure out what to do here
2333 # need to figure out what to do here
2334 del self._changectx[self._path]
2334 del self._changectx[self._path]
2335
2335
2336 def write(self, data, flags, **kwargs):
2336 def write(self, data, flags, **kwargs):
2337 """wraps repo.wwrite"""
2337 """wraps repo.wwrite"""
2338 self._data = data
2338 self._data = data
2339
2339
2340 class overlayfilectx(committablefilectx):
2340 class overlayfilectx(committablefilectx):
2341 """Like memfilectx but take an original filectx and optional parameters to
2341 """Like memfilectx but take an original filectx and optional parameters to
2342 override parts of it. This is useful when fctx.data() is expensive (i.e.
2342 override parts of it. This is useful when fctx.data() is expensive (i.e.
2343 flag processor is expensive) and raw data, flags, and filenode could be
2343 flag processor is expensive) and raw data, flags, and filenode could be
2344 reused (ex. rebase or mode-only amend a REVIDX_EXTSTORED file).
2344 reused (ex. rebase or mode-only amend a REVIDX_EXTSTORED file).
2345 """
2345 """
2346
2346
2347 def __init__(self, originalfctx, datafunc=None, path=None, flags=None,
2347 def __init__(self, originalfctx, datafunc=None, path=None, flags=None,
2348 copied=None, ctx=None):
2348 copied=None, ctx=None):
2349 """originalfctx: filecontext to duplicate
2349 """originalfctx: filecontext to duplicate
2350
2350
2351 datafunc: None or a function to override data (file content). It is a
2351 datafunc: None or a function to override data (file content). It is a
2352 function to be lazy. path, flags, copied, ctx: None or overridden value
2352 function to be lazy. path, flags, copied, ctx: None or overridden value
2353
2353
2354 copied could be (path, rev), or False. copied could also be just path,
2354 copied could be (path, rev), or False. copied could also be just path,
2355 and will be converted to (path, nullid). This simplifies some callers.
2355 and will be converted to (path, nullid). This simplifies some callers.
2356 """
2356 """
2357
2357
2358 if path is None:
2358 if path is None:
2359 path = originalfctx.path()
2359 path = originalfctx.path()
2360 if ctx is None:
2360 if ctx is None:
2361 ctx = originalfctx.changectx()
2361 ctx = originalfctx.changectx()
2362 ctxmatch = lambda: True
2362 ctxmatch = lambda: True
2363 else:
2363 else:
2364 ctxmatch = lambda: ctx == originalfctx.changectx()
2364 ctxmatch = lambda: ctx == originalfctx.changectx()
2365
2365
2366 repo = originalfctx.repo()
2366 repo = originalfctx.repo()
2367 flog = originalfctx.filelog()
2367 flog = originalfctx.filelog()
2368 super(overlayfilectx, self).__init__(repo, path, flog, ctx)
2368 super(overlayfilectx, self).__init__(repo, path, flog, ctx)
2369
2369
2370 if copied is None:
2370 if copied is None:
2371 copied = originalfctx.renamed()
2371 copied = originalfctx.renamed()
2372 copiedmatch = lambda: True
2372 copiedmatch = lambda: True
2373 else:
2373 else:
2374 if copied and not isinstance(copied, tuple):
2374 if copied and not isinstance(copied, tuple):
2375 # repo._filecommit will recalculate copyrev so nullid is okay
2375 # repo._filecommit will recalculate copyrev so nullid is okay
2376 copied = (copied, nullid)
2376 copied = (copied, nullid)
2377 copiedmatch = lambda: copied == originalfctx.renamed()
2377 copiedmatch = lambda: copied == originalfctx.renamed()
2378
2378
2379 # When data, copied (could affect data), ctx (could affect filelog
2379 # When data, copied (could affect data), ctx (could affect filelog
2380 # parents) are not overridden, rawdata, rawflags, and filenode may be
2380 # parents) are not overridden, rawdata, rawflags, and filenode may be
2381 # reused (repo._filecommit should double check filelog parents).
2381 # reused (repo._filecommit should double check filelog parents).
2382 #
2382 #
2383 # path, flags are not hashed in filelog (but in manifestlog) so they do
2383 # path, flags are not hashed in filelog (but in manifestlog) so they do
2384 # not affect reusable here.
2384 # not affect reusable here.
2385 #
2385 #
2386 # If ctx or copied is overridden to a same value with originalfctx,
2386 # If ctx or copied is overridden to a same value with originalfctx,
2387 # still consider it's reusable. originalfctx.renamed() may be a bit
2387 # still consider it's reusable. originalfctx.renamed() may be a bit
2388 # expensive so it's not called unless necessary. Assuming datafunc is
2388 # expensive so it's not called unless necessary. Assuming datafunc is
2389 # always expensive, do not call it for this "reusable" test.
2389 # always expensive, do not call it for this "reusable" test.
2390 reusable = datafunc is None and ctxmatch() and copiedmatch()
2390 reusable = datafunc is None and ctxmatch() and copiedmatch()
2391
2391
2392 if datafunc is None:
2392 if datafunc is None:
2393 datafunc = originalfctx.data
2393 datafunc = originalfctx.data
2394 if flags is None:
2394 if flags is None:
2395 flags = originalfctx.flags()
2395 flags = originalfctx.flags()
2396
2396
2397 self._datafunc = datafunc
2397 self._datafunc = datafunc
2398 self._flags = flags
2398 self._flags = flags
2399 self._copied = copied
2399 self._copied = copied
2400
2400
2401 if reusable:
2401 if reusable:
2402 # copy extra fields from originalfctx
2402 # copy extra fields from originalfctx
2403 attrs = ['rawdata', 'rawflags', '_filenode', '_filerev']
2403 attrs = ['rawdata', 'rawflags', '_filenode', '_filerev']
2404 for attr_ in attrs:
2404 for attr_ in attrs:
2405 if util.safehasattr(originalfctx, attr_):
2405 if util.safehasattr(originalfctx, attr_):
2406 setattr(self, attr_, getattr(originalfctx, attr_))
2406 setattr(self, attr_, getattr(originalfctx, attr_))
2407
2407
2408 def data(self):
2408 def data(self):
2409 return self._datafunc()
2409 return self._datafunc()
2410
2410
2411 class metadataonlyctx(committablectx):
2411 class metadataonlyctx(committablectx):
2412 """Like memctx but it's reusing the manifest of different commit.
2412 """Like memctx but it's reusing the manifest of different commit.
2413 Intended to be used by lightweight operations that are creating
2413 Intended to be used by lightweight operations that are creating
2414 metadata-only changes.
2414 metadata-only changes.
2415
2415
2416 Revision information is supplied at initialization time. 'repo' is the
2416 Revision information is supplied at initialization time. 'repo' is the
2417 current localrepo, 'ctx' is original revision which manifest we're reuisng
2417 current localrepo, 'ctx' is original revision which manifest we're reuisng
2418 'parents' is a sequence of two parent revisions identifiers (pass None for
2418 'parents' is a sequence of two parent revisions identifiers (pass None for
2419 every missing parent), 'text' is the commit.
2419 every missing parent), 'text' is the commit.
2420
2420
2421 user receives the committer name and defaults to current repository
2421 user receives the committer name and defaults to current repository
2422 username, date is the commit date in any format supported by
2422 username, date is the commit date in any format supported by
2423 dateutil.parsedate() and defaults to current date, extra is a dictionary of
2423 dateutil.parsedate() and defaults to current date, extra is a dictionary of
2424 metadata or is left empty.
2424 metadata or is left empty.
2425 """
2425 """
2426 def __init__(self, repo, originalctx, parents=None, text=None, user=None,
2426 def __init__(self, repo, originalctx, parents=None, text=None, user=None,
2427 date=None, extra=None, editor=False):
2427 date=None, extra=None, editor=False):
2428 if text is None:
2428 if text is None:
2429 text = originalctx.description()
2429 text = originalctx.description()
2430 super(metadataonlyctx, self).__init__(repo, text, user, date, extra)
2430 super(metadataonlyctx, self).__init__(repo, text, user, date, extra)
2431 self._rev = None
2431 self._rev = None
2432 self._node = None
2432 self._node = None
2433 self._originalctx = originalctx
2433 self._originalctx = originalctx
2434 self._manifestnode = originalctx.manifestnode()
2434 self._manifestnode = originalctx.manifestnode()
2435 if parents is None:
2435 if parents is None:
2436 parents = originalctx.parents()
2436 parents = originalctx.parents()
2437 else:
2437 else:
2438 parents = [repo[p] for p in parents if p is not None]
2438 parents = [repo[p] for p in parents if p is not None]
2439 parents = parents[:]
2439 parents = parents[:]
2440 while len(parents) < 2:
2440 while len(parents) < 2:
2441 parents.append(repo[nullid])
2441 parents.append(repo[nullid])
2442 p1, p2 = self._parents = parents
2442 p1, p2 = self._parents = parents
2443
2443
2444 # sanity check to ensure that the reused manifest parents are
2444 # sanity check to ensure that the reused manifest parents are
2445 # manifests of our commit parents
2445 # manifests of our commit parents
2446 mp1, mp2 = self.manifestctx().parents
2446 mp1, mp2 = self.manifestctx().parents
2447 if p1 != nullid and p1.manifestnode() != mp1:
2447 if p1 != nullid and p1.manifestnode() != mp1:
2448 raise RuntimeError('can\'t reuse the manifest: '
2448 raise RuntimeError('can\'t reuse the manifest: '
2449 'its p1 doesn\'t match the new ctx p1')
2449 'its p1 doesn\'t match the new ctx p1')
2450 if p2 != nullid and p2.manifestnode() != mp2:
2450 if p2 != nullid and p2.manifestnode() != mp2:
2451 raise RuntimeError('can\'t reuse the manifest: '
2451 raise RuntimeError('can\'t reuse the manifest: '
2452 'its p2 doesn\'t match the new ctx p2')
2452 'its p2 doesn\'t match the new ctx p2')
2453
2453
2454 self._files = originalctx.files()
2454 self._files = originalctx.files()
2455 self.substate = {}
2455 self.substate = {}
2456
2456
2457 if editor:
2457 if editor:
2458 self._text = editor(self._repo, self, [])
2458 self._text = editor(self._repo, self, [])
2459 self._repo.savecommitmessage(self._text)
2459 self._repo.savecommitmessage(self._text)
2460
2460
2461 def manifestnode(self):
2461 def manifestnode(self):
2462 return self._manifestnode
2462 return self._manifestnode
2463
2463
2464 @property
2464 @property
2465 def _manifestctx(self):
2465 def _manifestctx(self):
2466 return self._repo.manifestlog[self._manifestnode]
2466 return self._repo.manifestlog[self._manifestnode]
2467
2467
2468 def filectx(self, path, filelog=None):
2468 def filectx(self, path, filelog=None):
2469 return self._originalctx.filectx(path, filelog=filelog)
2469 return self._originalctx.filectx(path, filelog=filelog)
2470
2470
2471 def commit(self):
2471 def commit(self):
2472 """commit context to the repo"""
2472 """commit context to the repo"""
2473 return self._repo.commitctx(self)
2473 return self._repo.commitctx(self)
2474
2474
2475 @property
2475 @property
2476 def _manifest(self):
2476 def _manifest(self):
2477 return self._originalctx.manifest()
2477 return self._originalctx.manifest()
2478
2478
2479 @propertycache
2479 @propertycache
2480 def _status(self):
2480 def _status(self):
2481 """Calculate exact status from ``files`` specified in the ``origctx``
2481 """Calculate exact status from ``files`` specified in the ``origctx``
2482 and parents manifests.
2482 and parents manifests.
2483 """
2483 """
2484 man1 = self.p1().manifest()
2484 man1 = self.p1().manifest()
2485 p2 = self._parents[1]
2485 p2 = self._parents[1]
2486 # "1 < len(self._parents)" can't be used for checking
2486 # "1 < len(self._parents)" can't be used for checking
2487 # existence of the 2nd parent, because "metadataonlyctx._parents" is
2487 # existence of the 2nd parent, because "metadataonlyctx._parents" is
2488 # explicitly initialized by the list, of which length is 2.
2488 # explicitly initialized by the list, of which length is 2.
2489 if p2.node() != nullid:
2489 if p2.node() != nullid:
2490 man2 = p2.manifest()
2490 man2 = p2.manifest()
2491 managing = lambda f: f in man1 or f in man2
2491 managing = lambda f: f in man1 or f in man2
2492 else:
2492 else:
2493 managing = lambda f: f in man1
2493 managing = lambda f: f in man1
2494
2494
2495 modified, added, removed = [], [], []
2495 modified, added, removed = [], [], []
2496 for f in self._files:
2496 for f in self._files:
2497 if not managing(f):
2497 if not managing(f):
2498 added.append(f)
2498 added.append(f)
2499 elif f in self:
2499 elif f in self:
2500 modified.append(f)
2500 modified.append(f)
2501 else:
2501 else:
2502 removed.append(f)
2502 removed.append(f)
2503
2503
2504 return scmutil.status(modified, added, removed, [], [], [], [])
2504 return scmutil.status(modified, added, removed, [], [], [], [])
2505
2505
2506 class arbitraryfilectx(object):
2506 class arbitraryfilectx(object):
2507 """Allows you to use filectx-like functions on a file in an arbitrary
2507 """Allows you to use filectx-like functions on a file in an arbitrary
2508 location on disk, possibly not in the working directory.
2508 location on disk, possibly not in the working directory.
2509 """
2509 """
2510 def __init__(self, path, repo=None):
2510 def __init__(self, path, repo=None):
2511 # Repo is optional because contrib/simplemerge uses this class.
2511 # Repo is optional because contrib/simplemerge uses this class.
2512 self._repo = repo
2512 self._repo = repo
2513 self._path = path
2513 self._path = path
2514
2514
2515 def cmp(self, fctx):
2515 def cmp(self, fctx):
2516 # filecmp follows symlinks whereas `cmp` should not, so skip the fast
2516 # filecmp follows symlinks whereas `cmp` should not, so skip the fast
2517 # path if either side is a symlink.
2517 # path if either side is a symlink.
2518 symlinks = ('l' in self.flags() or 'l' in fctx.flags())
2518 symlinks = ('l' in self.flags() or 'l' in fctx.flags())
2519 if not symlinks and isinstance(fctx, workingfilectx) and self._repo:
2519 if not symlinks and isinstance(fctx, workingfilectx) and self._repo:
2520 # Add a fast-path for merge if both sides are disk-backed.
2520 # Add a fast-path for merge if both sides are disk-backed.
2521 # Note that filecmp uses the opposite return values (True if same)
2521 # Note that filecmp uses the opposite return values (True if same)
2522 # from our cmp functions (True if different).
2522 # from our cmp functions (True if different).
2523 return not filecmp.cmp(self.path(), self._repo.wjoin(fctx.path()))
2523 return not filecmp.cmp(self.path(), self._repo.wjoin(fctx.path()))
2524 return self.data() != fctx.data()
2524 return self.data() != fctx.data()
2525
2525
2526 def path(self):
2526 def path(self):
2527 return self._path
2527 return self._path
2528
2528
2529 def flags(self):
2529 def flags(self):
2530 return ''
2530 return ''
2531
2531
2532 def data(self):
2532 def data(self):
2533 return util.readfile(self._path)
2533 return util.readfile(self._path)
2534
2534
2535 def decodeddata(self):
2535 def decodeddata(self):
2536 with open(self._path, "rb") as f:
2536 with open(self._path, "rb") as f:
2537 return f.read()
2537 return f.read()
2538
2538
2539 def remove(self):
2539 def remove(self):
2540 util.unlink(self._path)
2540 util.unlink(self._path)
2541
2541
2542 def write(self, data, flags, **kwargs):
2542 def write(self, data, flags, **kwargs):
2543 assert not flags
2543 assert not flags
2544 with open(self._path, "w") as f:
2544 with open(self._path, "w") as f:
2545 f.write(data)
2545 f.write(data)
@@ -1,3166 +1,3166 b''
1 # debugcommands.py - command processing for debug* commands
1 # debugcommands.py - command processing for debug* commands
2 #
2 #
3 # Copyright 2005-2016 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2016 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 codecs
10 import codecs
11 import collections
11 import collections
12 import difflib
12 import difflib
13 import errno
13 import errno
14 import operator
14 import operator
15 import os
15 import os
16 import random
16 import random
17 import re
17 import re
18 import socket
18 import socket
19 import ssl
19 import ssl
20 import stat
20 import stat
21 import string
21 import string
22 import subprocess
22 import subprocess
23 import sys
23 import sys
24 import time
24 import time
25
25
26 from .i18n import _
26 from .i18n import _
27 from .node import (
27 from .node import (
28 bin,
28 bin,
29 hex,
29 hex,
30 nullhex,
30 nullhex,
31 nullid,
31 nullid,
32 nullrev,
32 nullrev,
33 short,
33 short,
34 )
34 )
35 from .thirdparty import (
35 from .thirdparty import (
36 cbor,
36 cbor,
37 )
37 )
38 from . import (
38 from . import (
39 bundle2,
39 bundle2,
40 changegroup,
40 changegroup,
41 cmdutil,
41 cmdutil,
42 color,
42 color,
43 context,
43 context,
44 dagparser,
44 dagparser,
45 dagutil,
45 dagutil,
46 encoding,
46 encoding,
47 error,
47 error,
48 exchange,
48 exchange,
49 extensions,
49 extensions,
50 filemerge,
50 filemerge,
51 fileset,
51 fileset,
52 formatter,
52 formatter,
53 hg,
53 hg,
54 httppeer,
54 httppeer,
55 localrepo,
55 localrepo,
56 lock as lockmod,
56 lock as lockmod,
57 logcmdutil,
57 logcmdutil,
58 merge as mergemod,
58 merge as mergemod,
59 obsolete,
59 obsolete,
60 obsutil,
60 obsutil,
61 phases,
61 phases,
62 policy,
62 policy,
63 pvec,
63 pvec,
64 pycompat,
64 pycompat,
65 registrar,
65 registrar,
66 repair,
66 repair,
67 revlog,
67 revlog,
68 revset,
68 revset,
69 revsetlang,
69 revsetlang,
70 scmutil,
70 scmutil,
71 setdiscovery,
71 setdiscovery,
72 simplemerge,
72 simplemerge,
73 sshpeer,
73 sshpeer,
74 sslutil,
74 sslutil,
75 streamclone,
75 streamclone,
76 templater,
76 templater,
77 treediscovery,
77 treediscovery,
78 upgrade,
78 upgrade,
79 url as urlmod,
79 url as urlmod,
80 util,
80 util,
81 vfs as vfsmod,
81 vfs as vfsmod,
82 wireprotoframing,
82 wireprotoframing,
83 wireprotoserver,
83 wireprotoserver,
84 wireprotov2peer,
84 wireprotov2peer,
85 )
85 )
86 from .utils import (
86 from .utils import (
87 dateutil,
87 dateutil,
88 procutil,
88 procutil,
89 stringutil,
89 stringutil,
90 )
90 )
91
91
92 release = lockmod.release
92 release = lockmod.release
93
93
94 command = registrar.command()
94 command = registrar.command()
95
95
96 @command('debugancestor', [], _('[INDEX] REV1 REV2'), optionalrepo=True)
96 @command('debugancestor', [], _('[INDEX] REV1 REV2'), optionalrepo=True)
97 def debugancestor(ui, repo, *args):
97 def debugancestor(ui, repo, *args):
98 """find the ancestor revision of two revisions in a given index"""
98 """find the ancestor revision of two revisions in a given index"""
99 if len(args) == 3:
99 if len(args) == 3:
100 index, rev1, rev2 = args
100 index, rev1, rev2 = args
101 r = revlog.revlog(vfsmod.vfs(pycompat.getcwd(), audit=False), index)
101 r = revlog.revlog(vfsmod.vfs(pycompat.getcwd(), audit=False), index)
102 lookup = r.lookup
102 lookup = r.lookup
103 elif len(args) == 2:
103 elif len(args) == 2:
104 if not repo:
104 if not repo:
105 raise error.Abort(_('there is no Mercurial repository here '
105 raise error.Abort(_('there is no Mercurial repository here '
106 '(.hg not found)'))
106 '(.hg not found)'))
107 rev1, rev2 = args
107 rev1, rev2 = args
108 r = repo.changelog
108 r = repo.changelog
109 lookup = repo.lookup
109 lookup = repo.lookup
110 else:
110 else:
111 raise error.Abort(_('either two or three arguments required'))
111 raise error.Abort(_('either two or three arguments required'))
112 a = r.ancestor(lookup(rev1), lookup(rev2))
112 a = r.ancestor(lookup(rev1), lookup(rev2))
113 ui.write('%d:%s\n' % (r.rev(a), hex(a)))
113 ui.write('%d:%s\n' % (r.rev(a), hex(a)))
114
114
115 @command('debugapplystreamclonebundle', [], 'FILE')
115 @command('debugapplystreamclonebundle', [], 'FILE')
116 def debugapplystreamclonebundle(ui, repo, fname):
116 def debugapplystreamclonebundle(ui, repo, fname):
117 """apply a stream clone bundle file"""
117 """apply a stream clone bundle file"""
118 f = hg.openpath(ui, fname)
118 f = hg.openpath(ui, fname)
119 gen = exchange.readbundle(ui, f, fname)
119 gen = exchange.readbundle(ui, f, fname)
120 gen.apply(repo)
120 gen.apply(repo)
121
121
122 @command('debugbuilddag',
122 @command('debugbuilddag',
123 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
123 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
124 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
124 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
125 ('n', 'new-file', None, _('add new file at each rev'))],
125 ('n', 'new-file', None, _('add new file at each rev'))],
126 _('[OPTION]... [TEXT]'))
126 _('[OPTION]... [TEXT]'))
127 def debugbuilddag(ui, repo, text=None,
127 def debugbuilddag(ui, repo, text=None,
128 mergeable_file=False,
128 mergeable_file=False,
129 overwritten_file=False,
129 overwritten_file=False,
130 new_file=False):
130 new_file=False):
131 """builds a repo with a given DAG from scratch in the current empty repo
131 """builds a repo with a given DAG from scratch in the current empty repo
132
132
133 The description of the DAG is read from stdin if not given on the
133 The description of the DAG is read from stdin if not given on the
134 command line.
134 command line.
135
135
136 Elements:
136 Elements:
137
137
138 - "+n" is a linear run of n nodes based on the current default parent
138 - "+n" is a linear run of n nodes based on the current default parent
139 - "." is a single node based on the current default parent
139 - "." is a single node based on the current default parent
140 - "$" resets the default parent to null (implied at the start);
140 - "$" resets the default parent to null (implied at the start);
141 otherwise the default parent is always the last node created
141 otherwise the default parent is always the last node created
142 - "<p" sets the default parent to the backref p
142 - "<p" sets the default parent to the backref p
143 - "*p" is a fork at parent p, which is a backref
143 - "*p" is a fork at parent p, which is a backref
144 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
144 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
145 - "/p2" is a merge of the preceding node and p2
145 - "/p2" is a merge of the preceding node and p2
146 - ":tag" defines a local tag for the preceding node
146 - ":tag" defines a local tag for the preceding node
147 - "@branch" sets the named branch for subsequent nodes
147 - "@branch" sets the named branch for subsequent nodes
148 - "#...\\n" is a comment up to the end of the line
148 - "#...\\n" is a comment up to the end of the line
149
149
150 Whitespace between the above elements is ignored.
150 Whitespace between the above elements is ignored.
151
151
152 A backref is either
152 A backref is either
153
153
154 - a number n, which references the node curr-n, where curr is the current
154 - a number n, which references the node curr-n, where curr is the current
155 node, or
155 node, or
156 - the name of a local tag you placed earlier using ":tag", or
156 - the name of a local tag you placed earlier using ":tag", or
157 - empty to denote the default parent.
157 - empty to denote the default parent.
158
158
159 All string valued-elements are either strictly alphanumeric, or must
159 All string valued-elements are either strictly alphanumeric, or must
160 be enclosed in double quotes ("..."), with "\\" as escape character.
160 be enclosed in double quotes ("..."), with "\\" as escape character.
161 """
161 """
162
162
163 if text is None:
163 if text is None:
164 ui.status(_("reading DAG from stdin\n"))
164 ui.status(_("reading DAG from stdin\n"))
165 text = ui.fin.read()
165 text = ui.fin.read()
166
166
167 cl = repo.changelog
167 cl = repo.changelog
168 if len(cl) > 0:
168 if len(cl) > 0:
169 raise error.Abort(_('repository is not empty'))
169 raise error.Abort(_('repository is not empty'))
170
170
171 # determine number of revs in DAG
171 # determine number of revs in DAG
172 total = 0
172 total = 0
173 for type, data in dagparser.parsedag(text):
173 for type, data in dagparser.parsedag(text):
174 if type == 'n':
174 if type == 'n':
175 total += 1
175 total += 1
176
176
177 if mergeable_file:
177 if mergeable_file:
178 linesperrev = 2
178 linesperrev = 2
179 # make a file with k lines per rev
179 # make a file with k lines per rev
180 initialmergedlines = ['%d' % i for i in xrange(0, total * linesperrev)]
180 initialmergedlines = ['%d' % i for i in xrange(0, total * linesperrev)]
181 initialmergedlines.append("")
181 initialmergedlines.append("")
182
182
183 tags = []
183 tags = []
184 progress = ui.makeprogress(_('building'), unit=_('revisions'),
184 progress = ui.makeprogress(_('building'), unit=_('revisions'),
185 total=total)
185 total=total)
186 with progress, repo.wlock(), repo.lock(), repo.transaction("builddag"):
186 with progress, repo.wlock(), repo.lock(), repo.transaction("builddag"):
187 at = -1
187 at = -1
188 atbranch = 'default'
188 atbranch = 'default'
189 nodeids = []
189 nodeids = []
190 id = 0
190 id = 0
191 progress.update(id)
191 progress.update(id)
192 for type, data in dagparser.parsedag(text):
192 for type, data in dagparser.parsedag(text):
193 if type == 'n':
193 if type == 'n':
194 ui.note(('node %s\n' % pycompat.bytestr(data)))
194 ui.note(('node %s\n' % pycompat.bytestr(data)))
195 id, ps = data
195 id, ps = data
196
196
197 files = []
197 files = []
198 filecontent = {}
198 filecontent = {}
199
199
200 p2 = None
200 p2 = None
201 if mergeable_file:
201 if mergeable_file:
202 fn = "mf"
202 fn = "mf"
203 p1 = repo[ps[0]]
203 p1 = repo[ps[0]]
204 if len(ps) > 1:
204 if len(ps) > 1:
205 p2 = repo[ps[1]]
205 p2 = repo[ps[1]]
206 pa = p1.ancestor(p2)
206 pa = p1.ancestor(p2)
207 base, local, other = [x[fn].data() for x in (pa, p1,
207 base, local, other = [x[fn].data() for x in (pa, p1,
208 p2)]
208 p2)]
209 m3 = simplemerge.Merge3Text(base, local, other)
209 m3 = simplemerge.Merge3Text(base, local, other)
210 ml = [l.strip() for l in m3.merge_lines()]
210 ml = [l.strip() for l in m3.merge_lines()]
211 ml.append("")
211 ml.append("")
212 elif at > 0:
212 elif at > 0:
213 ml = p1[fn].data().split("\n")
213 ml = p1[fn].data().split("\n")
214 else:
214 else:
215 ml = initialmergedlines
215 ml = initialmergedlines
216 ml[id * linesperrev] += " r%i" % id
216 ml[id * linesperrev] += " r%i" % id
217 mergedtext = "\n".join(ml)
217 mergedtext = "\n".join(ml)
218 files.append(fn)
218 files.append(fn)
219 filecontent[fn] = mergedtext
219 filecontent[fn] = mergedtext
220
220
221 if overwritten_file:
221 if overwritten_file:
222 fn = "of"
222 fn = "of"
223 files.append(fn)
223 files.append(fn)
224 filecontent[fn] = "r%i\n" % id
224 filecontent[fn] = "r%i\n" % id
225
225
226 if new_file:
226 if new_file:
227 fn = "nf%i" % id
227 fn = "nf%i" % id
228 files.append(fn)
228 files.append(fn)
229 filecontent[fn] = "r%i\n" % id
229 filecontent[fn] = "r%i\n" % id
230 if len(ps) > 1:
230 if len(ps) > 1:
231 if not p2:
231 if not p2:
232 p2 = repo[ps[1]]
232 p2 = repo[ps[1]]
233 for fn in p2:
233 for fn in p2:
234 if fn.startswith("nf"):
234 if fn.startswith("nf"):
235 files.append(fn)
235 files.append(fn)
236 filecontent[fn] = p2[fn].data()
236 filecontent[fn] = p2[fn].data()
237
237
238 def fctxfn(repo, cx, path):
238 def fctxfn(repo, cx, path):
239 if path in filecontent:
239 if path in filecontent:
240 return context.memfilectx(repo, cx, path,
240 return context.memfilectx(repo, cx, path,
241 filecontent[path])
241 filecontent[path])
242 return None
242 return None
243
243
244 if len(ps) == 0 or ps[0] < 0:
244 if len(ps) == 0 or ps[0] < 0:
245 pars = [None, None]
245 pars = [None, None]
246 elif len(ps) == 1:
246 elif len(ps) == 1:
247 pars = [nodeids[ps[0]], None]
247 pars = [nodeids[ps[0]], None]
248 else:
248 else:
249 pars = [nodeids[p] for p in ps]
249 pars = [nodeids[p] for p in ps]
250 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
250 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
251 date=(id, 0),
251 date=(id, 0),
252 user="debugbuilddag",
252 user="debugbuilddag",
253 extra={'branch': atbranch})
253 extra={'branch': atbranch})
254 nodeid = repo.commitctx(cx)
254 nodeid = repo.commitctx(cx)
255 nodeids.append(nodeid)
255 nodeids.append(nodeid)
256 at = id
256 at = id
257 elif type == 'l':
257 elif type == 'l':
258 id, name = data
258 id, name = data
259 ui.note(('tag %s\n' % name))
259 ui.note(('tag %s\n' % name))
260 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
260 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
261 elif type == 'a':
261 elif type == 'a':
262 ui.note(('branch %s\n' % data))
262 ui.note(('branch %s\n' % data))
263 atbranch = data
263 atbranch = data
264 progress.update(id)
264 progress.update(id)
265
265
266 if tags:
266 if tags:
267 repo.vfs.write("localtags", "".join(tags))
267 repo.vfs.write("localtags", "".join(tags))
268
268
269 def _debugchangegroup(ui, gen, all=None, indent=0, **opts):
269 def _debugchangegroup(ui, gen, all=None, indent=0, **opts):
270 indent_string = ' ' * indent
270 indent_string = ' ' * indent
271 if all:
271 if all:
272 ui.write(("%sformat: id, p1, p2, cset, delta base, len(delta)\n")
272 ui.write(("%sformat: id, p1, p2, cset, delta base, len(delta)\n")
273 % indent_string)
273 % indent_string)
274
274
275 def showchunks(named):
275 def showchunks(named):
276 ui.write("\n%s%s\n" % (indent_string, named))
276 ui.write("\n%s%s\n" % (indent_string, named))
277 for deltadata in gen.deltaiter():
277 for deltadata in gen.deltaiter():
278 node, p1, p2, cs, deltabase, delta, flags = deltadata
278 node, p1, p2, cs, deltabase, delta, flags = deltadata
279 ui.write("%s%s %s %s %s %s %d\n" %
279 ui.write("%s%s %s %s %s %s %d\n" %
280 (indent_string, hex(node), hex(p1), hex(p2),
280 (indent_string, hex(node), hex(p1), hex(p2),
281 hex(cs), hex(deltabase), len(delta)))
281 hex(cs), hex(deltabase), len(delta)))
282
282
283 chunkdata = gen.changelogheader()
283 chunkdata = gen.changelogheader()
284 showchunks("changelog")
284 showchunks("changelog")
285 chunkdata = gen.manifestheader()
285 chunkdata = gen.manifestheader()
286 showchunks("manifest")
286 showchunks("manifest")
287 for chunkdata in iter(gen.filelogheader, {}):
287 for chunkdata in iter(gen.filelogheader, {}):
288 fname = chunkdata['filename']
288 fname = chunkdata['filename']
289 showchunks(fname)
289 showchunks(fname)
290 else:
290 else:
291 if isinstance(gen, bundle2.unbundle20):
291 if isinstance(gen, bundle2.unbundle20):
292 raise error.Abort(_('use debugbundle2 for this file'))
292 raise error.Abort(_('use debugbundle2 for this file'))
293 chunkdata = gen.changelogheader()
293 chunkdata = gen.changelogheader()
294 for deltadata in gen.deltaiter():
294 for deltadata in gen.deltaiter():
295 node, p1, p2, cs, deltabase, delta, flags = deltadata
295 node, p1, p2, cs, deltabase, delta, flags = deltadata
296 ui.write("%s%s\n" % (indent_string, hex(node)))
296 ui.write("%s%s\n" % (indent_string, hex(node)))
297
297
298 def _debugobsmarkers(ui, part, indent=0, **opts):
298 def _debugobsmarkers(ui, part, indent=0, **opts):
299 """display version and markers contained in 'data'"""
299 """display version and markers contained in 'data'"""
300 opts = pycompat.byteskwargs(opts)
300 opts = pycompat.byteskwargs(opts)
301 data = part.read()
301 data = part.read()
302 indent_string = ' ' * indent
302 indent_string = ' ' * indent
303 try:
303 try:
304 version, markers = obsolete._readmarkers(data)
304 version, markers = obsolete._readmarkers(data)
305 except error.UnknownVersion as exc:
305 except error.UnknownVersion as exc:
306 msg = "%sunsupported version: %s (%d bytes)\n"
306 msg = "%sunsupported version: %s (%d bytes)\n"
307 msg %= indent_string, exc.version, len(data)
307 msg %= indent_string, exc.version, len(data)
308 ui.write(msg)
308 ui.write(msg)
309 else:
309 else:
310 msg = "%sversion: %d (%d bytes)\n"
310 msg = "%sversion: %d (%d bytes)\n"
311 msg %= indent_string, version, len(data)
311 msg %= indent_string, version, len(data)
312 ui.write(msg)
312 ui.write(msg)
313 fm = ui.formatter('debugobsolete', opts)
313 fm = ui.formatter('debugobsolete', opts)
314 for rawmarker in sorted(markers):
314 for rawmarker in sorted(markers):
315 m = obsutil.marker(None, rawmarker)
315 m = obsutil.marker(None, rawmarker)
316 fm.startitem()
316 fm.startitem()
317 fm.plain(indent_string)
317 fm.plain(indent_string)
318 cmdutil.showmarker(fm, m)
318 cmdutil.showmarker(fm, m)
319 fm.end()
319 fm.end()
320
320
321 def _debugphaseheads(ui, data, indent=0):
321 def _debugphaseheads(ui, data, indent=0):
322 """display version and markers contained in 'data'"""
322 """display version and markers contained in 'data'"""
323 indent_string = ' ' * indent
323 indent_string = ' ' * indent
324 headsbyphase = phases.binarydecode(data)
324 headsbyphase = phases.binarydecode(data)
325 for phase in phases.allphases:
325 for phase in phases.allphases:
326 for head in headsbyphase[phase]:
326 for head in headsbyphase[phase]:
327 ui.write(indent_string)
327 ui.write(indent_string)
328 ui.write('%s %s\n' % (hex(head), phases.phasenames[phase]))
328 ui.write('%s %s\n' % (hex(head), phases.phasenames[phase]))
329
329
330 def _quasirepr(thing):
330 def _quasirepr(thing):
331 if isinstance(thing, (dict, util.sortdict, collections.OrderedDict)):
331 if isinstance(thing, (dict, util.sortdict, collections.OrderedDict)):
332 return '{%s}' % (
332 return '{%s}' % (
333 b', '.join(b'%s: %s' % (k, thing[k]) for k in sorted(thing)))
333 b', '.join(b'%s: %s' % (k, thing[k]) for k in sorted(thing)))
334 return pycompat.bytestr(repr(thing))
334 return pycompat.bytestr(repr(thing))
335
335
336 def _debugbundle2(ui, gen, all=None, **opts):
336 def _debugbundle2(ui, gen, all=None, **opts):
337 """lists the contents of a bundle2"""
337 """lists the contents of a bundle2"""
338 if not isinstance(gen, bundle2.unbundle20):
338 if not isinstance(gen, bundle2.unbundle20):
339 raise error.Abort(_('not a bundle2 file'))
339 raise error.Abort(_('not a bundle2 file'))
340 ui.write(('Stream params: %s\n' % _quasirepr(gen.params)))
340 ui.write(('Stream params: %s\n' % _quasirepr(gen.params)))
341 parttypes = opts.get(r'part_type', [])
341 parttypes = opts.get(r'part_type', [])
342 for part in gen.iterparts():
342 for part in gen.iterparts():
343 if parttypes and part.type not in parttypes:
343 if parttypes and part.type not in parttypes:
344 continue
344 continue
345 msg = '%s -- %s (mandatory: %r)\n'
345 msg = '%s -- %s (mandatory: %r)\n'
346 ui.write((msg % (part.type, _quasirepr(part.params), part.mandatory)))
346 ui.write((msg % (part.type, _quasirepr(part.params), part.mandatory)))
347 if part.type == 'changegroup':
347 if part.type == 'changegroup':
348 version = part.params.get('version', '01')
348 version = part.params.get('version', '01')
349 cg = changegroup.getunbundler(version, part, 'UN')
349 cg = changegroup.getunbundler(version, part, 'UN')
350 if not ui.quiet:
350 if not ui.quiet:
351 _debugchangegroup(ui, cg, all=all, indent=4, **opts)
351 _debugchangegroup(ui, cg, all=all, indent=4, **opts)
352 if part.type == 'obsmarkers':
352 if part.type == 'obsmarkers':
353 if not ui.quiet:
353 if not ui.quiet:
354 _debugobsmarkers(ui, part, indent=4, **opts)
354 _debugobsmarkers(ui, part, indent=4, **opts)
355 if part.type == 'phase-heads':
355 if part.type == 'phase-heads':
356 if not ui.quiet:
356 if not ui.quiet:
357 _debugphaseheads(ui, part, indent=4)
357 _debugphaseheads(ui, part, indent=4)
358
358
359 @command('debugbundle',
359 @command('debugbundle',
360 [('a', 'all', None, _('show all details')),
360 [('a', 'all', None, _('show all details')),
361 ('', 'part-type', [], _('show only the named part type')),
361 ('', 'part-type', [], _('show only the named part type')),
362 ('', 'spec', None, _('print the bundlespec of the bundle'))],
362 ('', 'spec', None, _('print the bundlespec of the bundle'))],
363 _('FILE'),
363 _('FILE'),
364 norepo=True)
364 norepo=True)
365 def debugbundle(ui, bundlepath, all=None, spec=None, **opts):
365 def debugbundle(ui, bundlepath, all=None, spec=None, **opts):
366 """lists the contents of a bundle"""
366 """lists the contents of a bundle"""
367 with hg.openpath(ui, bundlepath) as f:
367 with hg.openpath(ui, bundlepath) as f:
368 if spec:
368 if spec:
369 spec = exchange.getbundlespec(ui, f)
369 spec = exchange.getbundlespec(ui, f)
370 ui.write('%s\n' % spec)
370 ui.write('%s\n' % spec)
371 return
371 return
372
372
373 gen = exchange.readbundle(ui, f, bundlepath)
373 gen = exchange.readbundle(ui, f, bundlepath)
374 if isinstance(gen, bundle2.unbundle20):
374 if isinstance(gen, bundle2.unbundle20):
375 return _debugbundle2(ui, gen, all=all, **opts)
375 return _debugbundle2(ui, gen, all=all, **opts)
376 _debugchangegroup(ui, gen, all=all, **opts)
376 _debugchangegroup(ui, gen, all=all, **opts)
377
377
378 @command('debugcapabilities',
378 @command('debugcapabilities',
379 [], _('PATH'),
379 [], _('PATH'),
380 norepo=True)
380 norepo=True)
381 def debugcapabilities(ui, path, **opts):
381 def debugcapabilities(ui, path, **opts):
382 """lists the capabilities of a remote peer"""
382 """lists the capabilities of a remote peer"""
383 opts = pycompat.byteskwargs(opts)
383 opts = pycompat.byteskwargs(opts)
384 peer = hg.peer(ui, opts, path)
384 peer = hg.peer(ui, opts, path)
385 caps = peer.capabilities()
385 caps = peer.capabilities()
386 ui.write(('Main capabilities:\n'))
386 ui.write(('Main capabilities:\n'))
387 for c in sorted(caps):
387 for c in sorted(caps):
388 ui.write((' %s\n') % c)
388 ui.write((' %s\n') % c)
389 b2caps = bundle2.bundle2caps(peer)
389 b2caps = bundle2.bundle2caps(peer)
390 if b2caps:
390 if b2caps:
391 ui.write(('Bundle2 capabilities:\n'))
391 ui.write(('Bundle2 capabilities:\n'))
392 for key, values in sorted(b2caps.iteritems()):
392 for key, values in sorted(b2caps.iteritems()):
393 ui.write((' %s\n') % key)
393 ui.write((' %s\n') % key)
394 for v in values:
394 for v in values:
395 ui.write((' %s\n') % v)
395 ui.write((' %s\n') % v)
396
396
397 @command('debugcheckstate', [], '')
397 @command('debugcheckstate', [], '')
398 def debugcheckstate(ui, repo):
398 def debugcheckstate(ui, repo):
399 """validate the correctness of the current dirstate"""
399 """validate the correctness of the current dirstate"""
400 parent1, parent2 = repo.dirstate.parents()
400 parent1, parent2 = repo.dirstate.parents()
401 m1 = repo[parent1].manifest()
401 m1 = repo[parent1].manifest()
402 m2 = repo[parent2].manifest()
402 m2 = repo[parent2].manifest()
403 errors = 0
403 errors = 0
404 for f in repo.dirstate:
404 for f in repo.dirstate:
405 state = repo.dirstate[f]
405 state = repo.dirstate[f]
406 if state in "nr" and f not in m1:
406 if state in "nr" and f not in m1:
407 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
407 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
408 errors += 1
408 errors += 1
409 if state in "a" and f in m1:
409 if state in "a" and f in m1:
410 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
410 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
411 errors += 1
411 errors += 1
412 if state in "m" and f not in m1 and f not in m2:
412 if state in "m" and f not in m1 and f not in m2:
413 ui.warn(_("%s in state %s, but not in either manifest\n") %
413 ui.warn(_("%s in state %s, but not in either manifest\n") %
414 (f, state))
414 (f, state))
415 errors += 1
415 errors += 1
416 for f in m1:
416 for f in m1:
417 state = repo.dirstate[f]
417 state = repo.dirstate[f]
418 if state not in "nrm":
418 if state not in "nrm":
419 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
419 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
420 errors += 1
420 errors += 1
421 if errors:
421 if errors:
422 error = _(".hg/dirstate inconsistent with current parent's manifest")
422 error = _(".hg/dirstate inconsistent with current parent's manifest")
423 raise error.Abort(error)
423 raise error.Abort(error)
424
424
425 @command('debugcolor',
425 @command('debugcolor',
426 [('', 'style', None, _('show all configured styles'))],
426 [('', 'style', None, _('show all configured styles'))],
427 'hg debugcolor')
427 'hg debugcolor')
428 def debugcolor(ui, repo, **opts):
428 def debugcolor(ui, repo, **opts):
429 """show available color, effects or style"""
429 """show available color, effects or style"""
430 ui.write(('color mode: %s\n') % stringutil.pprint(ui._colormode))
430 ui.write(('color mode: %s\n') % stringutil.pprint(ui._colormode))
431 if opts.get(r'style'):
431 if opts.get(r'style'):
432 return _debugdisplaystyle(ui)
432 return _debugdisplaystyle(ui)
433 else:
433 else:
434 return _debugdisplaycolor(ui)
434 return _debugdisplaycolor(ui)
435
435
436 def _debugdisplaycolor(ui):
436 def _debugdisplaycolor(ui):
437 ui = ui.copy()
437 ui = ui.copy()
438 ui._styles.clear()
438 ui._styles.clear()
439 for effect in color._activeeffects(ui).keys():
439 for effect in color._activeeffects(ui).keys():
440 ui._styles[effect] = effect
440 ui._styles[effect] = effect
441 if ui._terminfoparams:
441 if ui._terminfoparams:
442 for k, v in ui.configitems('color'):
442 for k, v in ui.configitems('color'):
443 if k.startswith('color.'):
443 if k.startswith('color.'):
444 ui._styles[k] = k[6:]
444 ui._styles[k] = k[6:]
445 elif k.startswith('terminfo.'):
445 elif k.startswith('terminfo.'):
446 ui._styles[k] = k[9:]
446 ui._styles[k] = k[9:]
447 ui.write(_('available colors:\n'))
447 ui.write(_('available colors:\n'))
448 # sort label with a '_' after the other to group '_background' entry.
448 # sort label with a '_' after the other to group '_background' entry.
449 items = sorted(ui._styles.items(),
449 items = sorted(ui._styles.items(),
450 key=lambda i: ('_' in i[0], i[0], i[1]))
450 key=lambda i: ('_' in i[0], i[0], i[1]))
451 for colorname, label in items:
451 for colorname, label in items:
452 ui.write(('%s\n') % colorname, label=label)
452 ui.write(('%s\n') % colorname, label=label)
453
453
454 def _debugdisplaystyle(ui):
454 def _debugdisplaystyle(ui):
455 ui.write(_('available style:\n'))
455 ui.write(_('available style:\n'))
456 if not ui._styles:
456 if not ui._styles:
457 return
457 return
458 width = max(len(s) for s in ui._styles)
458 width = max(len(s) for s in ui._styles)
459 for label, effects in sorted(ui._styles.items()):
459 for label, effects in sorted(ui._styles.items()):
460 ui.write('%s' % label, label=label)
460 ui.write('%s' % label, label=label)
461 if effects:
461 if effects:
462 # 50
462 # 50
463 ui.write(': ')
463 ui.write(': ')
464 ui.write(' ' * (max(0, width - len(label))))
464 ui.write(' ' * (max(0, width - len(label))))
465 ui.write(', '.join(ui.label(e, e) for e in effects.split()))
465 ui.write(', '.join(ui.label(e, e) for e in effects.split()))
466 ui.write('\n')
466 ui.write('\n')
467
467
468 @command('debugcreatestreamclonebundle', [], 'FILE')
468 @command('debugcreatestreamclonebundle', [], 'FILE')
469 def debugcreatestreamclonebundle(ui, repo, fname):
469 def debugcreatestreamclonebundle(ui, repo, fname):
470 """create a stream clone bundle file
470 """create a stream clone bundle file
471
471
472 Stream bundles are special bundles that are essentially archives of
472 Stream bundles are special bundles that are essentially archives of
473 revlog files. They are commonly used for cloning very quickly.
473 revlog files. They are commonly used for cloning very quickly.
474 """
474 """
475 # TODO we may want to turn this into an abort when this functionality
475 # TODO we may want to turn this into an abort when this functionality
476 # is moved into `hg bundle`.
476 # is moved into `hg bundle`.
477 if phases.hassecret(repo):
477 if phases.hassecret(repo):
478 ui.warn(_('(warning: stream clone bundle will contain secret '
478 ui.warn(_('(warning: stream clone bundle will contain secret '
479 'revisions)\n'))
479 'revisions)\n'))
480
480
481 requirements, gen = streamclone.generatebundlev1(repo)
481 requirements, gen = streamclone.generatebundlev1(repo)
482 changegroup.writechunks(ui, gen, fname)
482 changegroup.writechunks(ui, gen, fname)
483
483
484 ui.write(_('bundle requirements: %s\n') % ', '.join(sorted(requirements)))
484 ui.write(_('bundle requirements: %s\n') % ', '.join(sorted(requirements)))
485
485
486 @command('debugdag',
486 @command('debugdag',
487 [('t', 'tags', None, _('use tags as labels')),
487 [('t', 'tags', None, _('use tags as labels')),
488 ('b', 'branches', None, _('annotate with branch names')),
488 ('b', 'branches', None, _('annotate with branch names')),
489 ('', 'dots', None, _('use dots for runs')),
489 ('', 'dots', None, _('use dots for runs')),
490 ('s', 'spaces', None, _('separate elements by spaces'))],
490 ('s', 'spaces', None, _('separate elements by spaces'))],
491 _('[OPTION]... [FILE [REV]...]'),
491 _('[OPTION]... [FILE [REV]...]'),
492 optionalrepo=True)
492 optionalrepo=True)
493 def debugdag(ui, repo, file_=None, *revs, **opts):
493 def debugdag(ui, repo, file_=None, *revs, **opts):
494 """format the changelog or an index DAG as a concise textual description
494 """format the changelog or an index DAG as a concise textual description
495
495
496 If you pass a revlog index, the revlog's DAG is emitted. If you list
496 If you pass a revlog index, the revlog's DAG is emitted. If you list
497 revision numbers, they get labeled in the output as rN.
497 revision numbers, they get labeled in the output as rN.
498
498
499 Otherwise, the changelog DAG of the current repo is emitted.
499 Otherwise, the changelog DAG of the current repo is emitted.
500 """
500 """
501 spaces = opts.get(r'spaces')
501 spaces = opts.get(r'spaces')
502 dots = opts.get(r'dots')
502 dots = opts.get(r'dots')
503 if file_:
503 if file_:
504 rlog = revlog.revlog(vfsmod.vfs(pycompat.getcwd(), audit=False),
504 rlog = revlog.revlog(vfsmod.vfs(pycompat.getcwd(), audit=False),
505 file_)
505 file_)
506 revs = set((int(r) for r in revs))
506 revs = set((int(r) for r in revs))
507 def events():
507 def events():
508 for r in rlog:
508 for r in rlog:
509 yield 'n', (r, list(p for p in rlog.parentrevs(r)
509 yield 'n', (r, list(p for p in rlog.parentrevs(r)
510 if p != -1))
510 if p != -1))
511 if r in revs:
511 if r in revs:
512 yield 'l', (r, "r%i" % r)
512 yield 'l', (r, "r%i" % r)
513 elif repo:
513 elif repo:
514 cl = repo.changelog
514 cl = repo.changelog
515 tags = opts.get(r'tags')
515 tags = opts.get(r'tags')
516 branches = opts.get(r'branches')
516 branches = opts.get(r'branches')
517 if tags:
517 if tags:
518 labels = {}
518 labels = {}
519 for l, n in repo.tags().items():
519 for l, n in repo.tags().items():
520 labels.setdefault(cl.rev(n), []).append(l)
520 labels.setdefault(cl.rev(n), []).append(l)
521 def events():
521 def events():
522 b = "default"
522 b = "default"
523 for r in cl:
523 for r in cl:
524 if branches:
524 if branches:
525 newb = cl.read(cl.node(r))[5]['branch']
525 newb = cl.read(cl.node(r))[5]['branch']
526 if newb != b:
526 if newb != b:
527 yield 'a', newb
527 yield 'a', newb
528 b = newb
528 b = newb
529 yield 'n', (r, list(p for p in cl.parentrevs(r)
529 yield 'n', (r, list(p for p in cl.parentrevs(r)
530 if p != -1))
530 if p != -1))
531 if tags:
531 if tags:
532 ls = labels.get(r)
532 ls = labels.get(r)
533 if ls:
533 if ls:
534 for l in ls:
534 for l in ls:
535 yield 'l', (r, l)
535 yield 'l', (r, l)
536 else:
536 else:
537 raise error.Abort(_('need repo for changelog dag'))
537 raise error.Abort(_('need repo for changelog dag'))
538
538
539 for line in dagparser.dagtextlines(events(),
539 for line in dagparser.dagtextlines(events(),
540 addspaces=spaces,
540 addspaces=spaces,
541 wraplabels=True,
541 wraplabels=True,
542 wrapannotations=True,
542 wrapannotations=True,
543 wrapnonlinear=dots,
543 wrapnonlinear=dots,
544 usedots=dots,
544 usedots=dots,
545 maxlinewidth=70):
545 maxlinewidth=70):
546 ui.write(line)
546 ui.write(line)
547 ui.write("\n")
547 ui.write("\n")
548
548
549 @command('debugdata', cmdutil.debugrevlogopts, _('-c|-m|FILE REV'))
549 @command('debugdata', cmdutil.debugrevlogopts, _('-c|-m|FILE REV'))
550 def debugdata(ui, repo, file_, rev=None, **opts):
550 def debugdata(ui, repo, file_, rev=None, **opts):
551 """dump the contents of a data file revision"""
551 """dump the contents of a data file revision"""
552 opts = pycompat.byteskwargs(opts)
552 opts = pycompat.byteskwargs(opts)
553 if opts.get('changelog') or opts.get('manifest') or opts.get('dir'):
553 if opts.get('changelog') or opts.get('manifest') or opts.get('dir'):
554 if rev is not None:
554 if rev is not None:
555 raise error.CommandError('debugdata', _('invalid arguments'))
555 raise error.CommandError('debugdata', _('invalid arguments'))
556 file_, rev = None, file_
556 file_, rev = None, file_
557 elif rev is None:
557 elif rev is None:
558 raise error.CommandError('debugdata', _('invalid arguments'))
558 raise error.CommandError('debugdata', _('invalid arguments'))
559 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
559 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
560 try:
560 try:
561 ui.write(r.revision(r.lookup(rev), raw=True))
561 ui.write(r.revision(r.lookup(rev), raw=True))
562 except KeyError:
562 except KeyError:
563 raise error.Abort(_('invalid revision identifier %s') % rev)
563 raise error.Abort(_('invalid revision identifier %s') % rev)
564
564
565 @command('debugdate',
565 @command('debugdate',
566 [('e', 'extended', None, _('try extended date formats'))],
566 [('e', 'extended', None, _('try extended date formats'))],
567 _('[-e] DATE [RANGE]'),
567 _('[-e] DATE [RANGE]'),
568 norepo=True, optionalrepo=True)
568 norepo=True, optionalrepo=True)
569 def debugdate(ui, date, range=None, **opts):
569 def debugdate(ui, date, range=None, **opts):
570 """parse and display a date"""
570 """parse and display a date"""
571 if opts[r"extended"]:
571 if opts[r"extended"]:
572 d = dateutil.parsedate(date, util.extendeddateformats)
572 d = dateutil.parsedate(date, util.extendeddateformats)
573 else:
573 else:
574 d = dateutil.parsedate(date)
574 d = dateutil.parsedate(date)
575 ui.write(("internal: %d %d\n") % d)
575 ui.write(("internal: %d %d\n") % d)
576 ui.write(("standard: %s\n") % dateutil.datestr(d))
576 ui.write(("standard: %s\n") % dateutil.datestr(d))
577 if range:
577 if range:
578 m = dateutil.matchdate(range)
578 m = dateutil.matchdate(range)
579 ui.write(("match: %s\n") % m(d[0]))
579 ui.write(("match: %s\n") % m(d[0]))
580
580
581 @command('debugdeltachain',
581 @command('debugdeltachain',
582 cmdutil.debugrevlogopts + cmdutil.formatteropts,
582 cmdutil.debugrevlogopts + cmdutil.formatteropts,
583 _('-c|-m|FILE'),
583 _('-c|-m|FILE'),
584 optionalrepo=True)
584 optionalrepo=True)
585 def debugdeltachain(ui, repo, file_=None, **opts):
585 def debugdeltachain(ui, repo, file_=None, **opts):
586 """dump information about delta chains in a revlog
586 """dump information about delta chains in a revlog
587
587
588 Output can be templatized. Available template keywords are:
588 Output can be templatized. Available template keywords are:
589
589
590 :``rev``: revision number
590 :``rev``: revision number
591 :``chainid``: delta chain identifier (numbered by unique base)
591 :``chainid``: delta chain identifier (numbered by unique base)
592 :``chainlen``: delta chain length to this revision
592 :``chainlen``: delta chain length to this revision
593 :``prevrev``: previous revision in delta chain
593 :``prevrev``: previous revision in delta chain
594 :``deltatype``: role of delta / how it was computed
594 :``deltatype``: role of delta / how it was computed
595 :``compsize``: compressed size of revision
595 :``compsize``: compressed size of revision
596 :``uncompsize``: uncompressed size of revision
596 :``uncompsize``: uncompressed size of revision
597 :``chainsize``: total size of compressed revisions in chain
597 :``chainsize``: total size of compressed revisions in chain
598 :``chainratio``: total chain size divided by uncompressed revision size
598 :``chainratio``: total chain size divided by uncompressed revision size
599 (new delta chains typically start at ratio 2.00)
599 (new delta chains typically start at ratio 2.00)
600 :``lindist``: linear distance from base revision in delta chain to end
600 :``lindist``: linear distance from base revision in delta chain to end
601 of this revision
601 of this revision
602 :``extradist``: total size of revisions not part of this delta chain from
602 :``extradist``: total size of revisions not part of this delta chain from
603 base of delta chain to end of this revision; a measurement
603 base of delta chain to end of this revision; a measurement
604 of how much extra data we need to read/seek across to read
604 of how much extra data we need to read/seek across to read
605 the delta chain for this revision
605 the delta chain for this revision
606 :``extraratio``: extradist divided by chainsize; another representation of
606 :``extraratio``: extradist divided by chainsize; another representation of
607 how much unrelated data is needed to load this delta chain
607 how much unrelated data is needed to load this delta chain
608
608
609 If the repository is configured to use the sparse read, additional keywords
609 If the repository is configured to use the sparse read, additional keywords
610 are available:
610 are available:
611
611
612 :``readsize``: total size of data read from the disk for a revision
612 :``readsize``: total size of data read from the disk for a revision
613 (sum of the sizes of all the blocks)
613 (sum of the sizes of all the blocks)
614 :``largestblock``: size of the largest block of data read from the disk
614 :``largestblock``: size of the largest block of data read from the disk
615 :``readdensity``: density of useful bytes in the data read from the disk
615 :``readdensity``: density of useful bytes in the data read from the disk
616 :``srchunks``: in how many data hunks the whole revision would be read
616 :``srchunks``: in how many data hunks the whole revision would be read
617
617
618 The sparse read can be enabled with experimental.sparse-read = True
618 The sparse read can be enabled with experimental.sparse-read = True
619 """
619 """
620 opts = pycompat.byteskwargs(opts)
620 opts = pycompat.byteskwargs(opts)
621 r = cmdutil.openrevlog(repo, 'debugdeltachain', file_, opts)
621 r = cmdutil.openrevlog(repo, 'debugdeltachain', file_, opts)
622 index = r.index
622 index = r.index
623 start = r.start
623 start = r.start
624 length = r.length
624 length = r.length
625 generaldelta = r.version & revlog.FLAG_GENERALDELTA
625 generaldelta = r.version & revlog.FLAG_GENERALDELTA
626 withsparseread = getattr(r, '_withsparseread', False)
626 withsparseread = getattr(r, '_withsparseread', False)
627
627
628 def revinfo(rev):
628 def revinfo(rev):
629 e = index[rev]
629 e = index[rev]
630 compsize = e[1]
630 compsize = e[1]
631 uncompsize = e[2]
631 uncompsize = e[2]
632 chainsize = 0
632 chainsize = 0
633
633
634 if generaldelta:
634 if generaldelta:
635 if e[3] == e[5]:
635 if e[3] == e[5]:
636 deltatype = 'p1'
636 deltatype = 'p1'
637 elif e[3] == e[6]:
637 elif e[3] == e[6]:
638 deltatype = 'p2'
638 deltatype = 'p2'
639 elif e[3] == rev - 1:
639 elif e[3] == rev - 1:
640 deltatype = 'prev'
640 deltatype = 'prev'
641 elif e[3] == rev:
641 elif e[3] == rev:
642 deltatype = 'base'
642 deltatype = 'base'
643 else:
643 else:
644 deltatype = 'other'
644 deltatype = 'other'
645 else:
645 else:
646 if e[3] == rev:
646 if e[3] == rev:
647 deltatype = 'base'
647 deltatype = 'base'
648 else:
648 else:
649 deltatype = 'prev'
649 deltatype = 'prev'
650
650
651 chain = r._deltachain(rev)[0]
651 chain = r._deltachain(rev)[0]
652 for iterrev in chain:
652 for iterrev in chain:
653 e = index[iterrev]
653 e = index[iterrev]
654 chainsize += e[1]
654 chainsize += e[1]
655
655
656 return compsize, uncompsize, deltatype, chain, chainsize
656 return compsize, uncompsize, deltatype, chain, chainsize
657
657
658 fm = ui.formatter('debugdeltachain', opts)
658 fm = ui.formatter('debugdeltachain', opts)
659
659
660 fm.plain(' rev chain# chainlen prev delta '
660 fm.plain(' rev chain# chainlen prev delta '
661 'size rawsize chainsize ratio lindist extradist '
661 'size rawsize chainsize ratio lindist extradist '
662 'extraratio')
662 'extraratio')
663 if withsparseread:
663 if withsparseread:
664 fm.plain(' readsize largestblk rddensity srchunks')
664 fm.plain(' readsize largestblk rddensity srchunks')
665 fm.plain('\n')
665 fm.plain('\n')
666
666
667 chainbases = {}
667 chainbases = {}
668 for rev in r:
668 for rev in r:
669 comp, uncomp, deltatype, chain, chainsize = revinfo(rev)
669 comp, uncomp, deltatype, chain, chainsize = revinfo(rev)
670 chainbase = chain[0]
670 chainbase = chain[0]
671 chainid = chainbases.setdefault(chainbase, len(chainbases) + 1)
671 chainid = chainbases.setdefault(chainbase, len(chainbases) + 1)
672 basestart = start(chainbase)
672 basestart = start(chainbase)
673 revstart = start(rev)
673 revstart = start(rev)
674 lineardist = revstart + comp - basestart
674 lineardist = revstart + comp - basestart
675 extradist = lineardist - chainsize
675 extradist = lineardist - chainsize
676 try:
676 try:
677 prevrev = chain[-2]
677 prevrev = chain[-2]
678 except IndexError:
678 except IndexError:
679 prevrev = -1
679 prevrev = -1
680
680
681 chainratio = float(chainsize) / float(uncomp)
681 chainratio = float(chainsize) / float(uncomp)
682 extraratio = float(extradist) / float(chainsize)
682 extraratio = float(extradist) / float(chainsize)
683
683
684 fm.startitem()
684 fm.startitem()
685 fm.write('rev chainid chainlen prevrev deltatype compsize '
685 fm.write('rev chainid chainlen prevrev deltatype compsize '
686 'uncompsize chainsize chainratio lindist extradist '
686 'uncompsize chainsize chainratio lindist extradist '
687 'extraratio',
687 'extraratio',
688 '%7d %7d %8d %8d %7s %10d %10d %10d %9.5f %9d %9d %10.5f',
688 '%7d %7d %8d %8d %7s %10d %10d %10d %9.5f %9d %9d %10.5f',
689 rev, chainid, len(chain), prevrev, deltatype, comp,
689 rev, chainid, len(chain), prevrev, deltatype, comp,
690 uncomp, chainsize, chainratio, lineardist, extradist,
690 uncomp, chainsize, chainratio, lineardist, extradist,
691 extraratio,
691 extraratio,
692 rev=rev, chainid=chainid, chainlen=len(chain),
692 rev=rev, chainid=chainid, chainlen=len(chain),
693 prevrev=prevrev, deltatype=deltatype, compsize=comp,
693 prevrev=prevrev, deltatype=deltatype, compsize=comp,
694 uncompsize=uncomp, chainsize=chainsize,
694 uncompsize=uncomp, chainsize=chainsize,
695 chainratio=chainratio, lindist=lineardist,
695 chainratio=chainratio, lindist=lineardist,
696 extradist=extradist, extraratio=extraratio)
696 extradist=extradist, extraratio=extraratio)
697 if withsparseread:
697 if withsparseread:
698 readsize = 0
698 readsize = 0
699 largestblock = 0
699 largestblock = 0
700 srchunks = 0
700 srchunks = 0
701
701
702 for revschunk in revlog._slicechunk(r, chain):
702 for revschunk in revlog._slicechunk(r, chain):
703 srchunks += 1
703 srchunks += 1
704 blkend = start(revschunk[-1]) + length(revschunk[-1])
704 blkend = start(revschunk[-1]) + length(revschunk[-1])
705 blksize = blkend - start(revschunk[0])
705 blksize = blkend - start(revschunk[0])
706
706
707 readsize += blksize
707 readsize += blksize
708 if largestblock < blksize:
708 if largestblock < blksize:
709 largestblock = blksize
709 largestblock = blksize
710
710
711 readdensity = float(chainsize) / float(readsize)
711 readdensity = float(chainsize) / float(readsize)
712
712
713 fm.write('readsize largestblock readdensity srchunks',
713 fm.write('readsize largestblock readdensity srchunks',
714 ' %10d %10d %9.5f %8d',
714 ' %10d %10d %9.5f %8d',
715 readsize, largestblock, readdensity, srchunks,
715 readsize, largestblock, readdensity, srchunks,
716 readsize=readsize, largestblock=largestblock,
716 readsize=readsize, largestblock=largestblock,
717 readdensity=readdensity, srchunks=srchunks)
717 readdensity=readdensity, srchunks=srchunks)
718
718
719 fm.plain('\n')
719 fm.plain('\n')
720
720
721 fm.end()
721 fm.end()
722
722
723 @command('debugdirstate|debugstate',
723 @command('debugdirstate|debugstate',
724 [('', 'nodates', None, _('do not display the saved mtime')),
724 [('', 'nodates', None, _('do not display the saved mtime')),
725 ('', 'datesort', None, _('sort by saved mtime'))],
725 ('', 'datesort', None, _('sort by saved mtime'))],
726 _('[OPTION]...'))
726 _('[OPTION]...'))
727 def debugstate(ui, repo, **opts):
727 def debugstate(ui, repo, **opts):
728 """show the contents of the current dirstate"""
728 """show the contents of the current dirstate"""
729
729
730 nodates = opts.get(r'nodates')
730 nodates = opts.get(r'nodates')
731 datesort = opts.get(r'datesort')
731 datesort = opts.get(r'datesort')
732
732
733 timestr = ""
733 timestr = ""
734 if datesort:
734 if datesort:
735 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
735 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
736 else:
736 else:
737 keyfunc = None # sort by filename
737 keyfunc = None # sort by filename
738 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
738 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
739 if ent[3] == -1:
739 if ent[3] == -1:
740 timestr = 'unset '
740 timestr = 'unset '
741 elif nodates:
741 elif nodates:
742 timestr = 'set '
742 timestr = 'set '
743 else:
743 else:
744 timestr = time.strftime(r"%Y-%m-%d %H:%M:%S ",
744 timestr = time.strftime(r"%Y-%m-%d %H:%M:%S ",
745 time.localtime(ent[3]))
745 time.localtime(ent[3]))
746 timestr = encoding.strtolocal(timestr)
746 timestr = encoding.strtolocal(timestr)
747 if ent[1] & 0o20000:
747 if ent[1] & 0o20000:
748 mode = 'lnk'
748 mode = 'lnk'
749 else:
749 else:
750 mode = '%3o' % (ent[1] & 0o777 & ~util.umask)
750 mode = '%3o' % (ent[1] & 0o777 & ~util.umask)
751 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
751 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
752 for f in repo.dirstate.copies():
752 for f in repo.dirstate.copies():
753 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
753 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
754
754
755 @command('debugdiscovery',
755 @command('debugdiscovery',
756 [('', 'old', None, _('use old-style discovery')),
756 [('', 'old', None, _('use old-style discovery')),
757 ('', 'nonheads', None,
757 ('', 'nonheads', None,
758 _('use old-style discovery with non-heads included')),
758 _('use old-style discovery with non-heads included')),
759 ('', 'rev', [], 'restrict discovery to this set of revs'),
759 ('', 'rev', [], 'restrict discovery to this set of revs'),
760 ] + cmdutil.remoteopts,
760 ] + cmdutil.remoteopts,
761 _('[--rev REV] [OTHER]'))
761 _('[--rev REV] [OTHER]'))
762 def debugdiscovery(ui, repo, remoteurl="default", **opts):
762 def debugdiscovery(ui, repo, remoteurl="default", **opts):
763 """runs the changeset discovery protocol in isolation"""
763 """runs the changeset discovery protocol in isolation"""
764 opts = pycompat.byteskwargs(opts)
764 opts = pycompat.byteskwargs(opts)
765 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl))
765 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl))
766 remote = hg.peer(repo, opts, remoteurl)
766 remote = hg.peer(repo, opts, remoteurl)
767 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
767 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
768
768
769 # make sure tests are repeatable
769 # make sure tests are repeatable
770 random.seed(12323)
770 random.seed(12323)
771
771
772 def doit(pushedrevs, remoteheads, remote=remote):
772 def doit(pushedrevs, remoteheads, remote=remote):
773 if opts.get('old'):
773 if opts.get('old'):
774 if not util.safehasattr(remote, 'branches'):
774 if not util.safehasattr(remote, 'branches'):
775 # enable in-client legacy support
775 # enable in-client legacy support
776 remote = localrepo.locallegacypeer(remote.local())
776 remote = localrepo.locallegacypeer(remote.local())
777 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
777 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
778 force=True)
778 force=True)
779 common = set(common)
779 common = set(common)
780 if not opts.get('nonheads'):
780 if not opts.get('nonheads'):
781 ui.write(("unpruned common: %s\n") %
781 ui.write(("unpruned common: %s\n") %
782 " ".join(sorted(short(n) for n in common)))
782 " ".join(sorted(short(n) for n in common)))
783 dag = dagutil.revlogdag(repo.changelog)
783 dag = dagutil.revlogdag(repo.changelog)
784 all = dag.ancestorset(dag.internalizeall(common))
784 all = dag.ancestorset(dag.internalizeall(common))
785 common = dag.externalizeall(dag.headsetofconnecteds(all))
785 common = dag.externalizeall(dag.headsetofconnecteds(all))
786 else:
786 else:
787 nodes = None
787 nodes = None
788 if pushedrevs:
788 if pushedrevs:
789 revs = scmutil.revrange(repo, pushedrevs)
789 revs = scmutil.revrange(repo, pushedrevs)
790 nodes = [repo[r].node() for r in revs]
790 nodes = [repo[r].node() for r in revs]
791 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote,
791 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote,
792 ancestorsof=nodes)
792 ancestorsof=nodes)
793 common = set(common)
793 common = set(common)
794 rheads = set(hds)
794 rheads = set(hds)
795 lheads = set(repo.heads())
795 lheads = set(repo.heads())
796 ui.write(("common heads: %s\n") %
796 ui.write(("common heads: %s\n") %
797 " ".join(sorted(short(n) for n in common)))
797 " ".join(sorted(short(n) for n in common)))
798 if lheads <= common:
798 if lheads <= common:
799 ui.write(("local is subset\n"))
799 ui.write(("local is subset\n"))
800 elif rheads <= common:
800 elif rheads <= common:
801 ui.write(("remote is subset\n"))
801 ui.write(("remote is subset\n"))
802
802
803 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches, revs=None)
803 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches, revs=None)
804 localrevs = opts['rev']
804 localrevs = opts['rev']
805 doit(localrevs, remoterevs)
805 doit(localrevs, remoterevs)
806
806
807 _chunksize = 4 << 10
807 _chunksize = 4 << 10
808
808
809 @command('debugdownload',
809 @command('debugdownload',
810 [
810 [
811 ('o', 'output', '', _('path')),
811 ('o', 'output', '', _('path')),
812 ],
812 ],
813 optionalrepo=True)
813 optionalrepo=True)
814 def debugdownload(ui, repo, url, output=None, **opts):
814 def debugdownload(ui, repo, url, output=None, **opts):
815 """download a resource using Mercurial logic and config
815 """download a resource using Mercurial logic and config
816 """
816 """
817 fh = urlmod.open(ui, url, output)
817 fh = urlmod.open(ui, url, output)
818
818
819 dest = ui
819 dest = ui
820 if output:
820 if output:
821 dest = open(output, "wb", _chunksize)
821 dest = open(output, "wb", _chunksize)
822 try:
822 try:
823 data = fh.read(_chunksize)
823 data = fh.read(_chunksize)
824 while data:
824 while data:
825 dest.write(data)
825 dest.write(data)
826 data = fh.read(_chunksize)
826 data = fh.read(_chunksize)
827 finally:
827 finally:
828 if output:
828 if output:
829 dest.close()
829 dest.close()
830
830
831 @command('debugextensions', cmdutil.formatteropts, [], optionalrepo=True)
831 @command('debugextensions', cmdutil.formatteropts, [], optionalrepo=True)
832 def debugextensions(ui, repo, **opts):
832 def debugextensions(ui, repo, **opts):
833 '''show information about active extensions'''
833 '''show information about active extensions'''
834 opts = pycompat.byteskwargs(opts)
834 opts = pycompat.byteskwargs(opts)
835 exts = extensions.extensions(ui)
835 exts = extensions.extensions(ui)
836 hgver = util.version()
836 hgver = util.version()
837 fm = ui.formatter('debugextensions', opts)
837 fm = ui.formatter('debugextensions', opts)
838 for extname, extmod in sorted(exts, key=operator.itemgetter(0)):
838 for extname, extmod in sorted(exts, key=operator.itemgetter(0)):
839 isinternal = extensions.ismoduleinternal(extmod)
839 isinternal = extensions.ismoduleinternal(extmod)
840 extsource = pycompat.fsencode(extmod.__file__)
840 extsource = pycompat.fsencode(extmod.__file__)
841 if isinternal:
841 if isinternal:
842 exttestedwith = [] # never expose magic string to users
842 exttestedwith = [] # never expose magic string to users
843 else:
843 else:
844 exttestedwith = getattr(extmod, 'testedwith', '').split()
844 exttestedwith = getattr(extmod, 'testedwith', '').split()
845 extbuglink = getattr(extmod, 'buglink', None)
845 extbuglink = getattr(extmod, 'buglink', None)
846
846
847 fm.startitem()
847 fm.startitem()
848
848
849 if ui.quiet or ui.verbose:
849 if ui.quiet or ui.verbose:
850 fm.write('name', '%s\n', extname)
850 fm.write('name', '%s\n', extname)
851 else:
851 else:
852 fm.write('name', '%s', extname)
852 fm.write('name', '%s', extname)
853 if isinternal or hgver in exttestedwith:
853 if isinternal or hgver in exttestedwith:
854 fm.plain('\n')
854 fm.plain('\n')
855 elif not exttestedwith:
855 elif not exttestedwith:
856 fm.plain(_(' (untested!)\n'))
856 fm.plain(_(' (untested!)\n'))
857 else:
857 else:
858 lasttestedversion = exttestedwith[-1]
858 lasttestedversion = exttestedwith[-1]
859 fm.plain(' (%s!)\n' % lasttestedversion)
859 fm.plain(' (%s!)\n' % lasttestedversion)
860
860
861 fm.condwrite(ui.verbose and extsource, 'source',
861 fm.condwrite(ui.verbose and extsource, 'source',
862 _(' location: %s\n'), extsource or "")
862 _(' location: %s\n'), extsource or "")
863
863
864 if ui.verbose:
864 if ui.verbose:
865 fm.plain(_(' bundled: %s\n') % ['no', 'yes'][isinternal])
865 fm.plain(_(' bundled: %s\n') % ['no', 'yes'][isinternal])
866 fm.data(bundled=isinternal)
866 fm.data(bundled=isinternal)
867
867
868 fm.condwrite(ui.verbose and exttestedwith, 'testedwith',
868 fm.condwrite(ui.verbose and exttestedwith, 'testedwith',
869 _(' tested with: %s\n'),
869 _(' tested with: %s\n'),
870 fm.formatlist(exttestedwith, name='ver'))
870 fm.formatlist(exttestedwith, name='ver'))
871
871
872 fm.condwrite(ui.verbose and extbuglink, 'buglink',
872 fm.condwrite(ui.verbose and extbuglink, 'buglink',
873 _(' bug reporting: %s\n'), extbuglink or "")
873 _(' bug reporting: %s\n'), extbuglink or "")
874
874
875 fm.end()
875 fm.end()
876
876
877 @command('debugfileset',
877 @command('debugfileset',
878 [('r', 'rev', '', _('apply the filespec on this revision'), _('REV')),
878 [('r', 'rev', '', _('apply the filespec on this revision'), _('REV')),
879 ('', 'all-files', False,
879 ('', 'all-files', False,
880 _('test files from all revisions and working directory'))],
880 _('test files from all revisions and working directory'))],
881 _('[-r REV] [--all-files] FILESPEC'))
881 _('[-r REV] [--all-files] FILESPEC'))
882 def debugfileset(ui, repo, expr, **opts):
882 def debugfileset(ui, repo, expr, **opts):
883 '''parse and apply a fileset specification'''
883 '''parse and apply a fileset specification'''
884 opts = pycompat.byteskwargs(opts)
884 opts = pycompat.byteskwargs(opts)
885 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
885 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
886 if ui.verbose:
886 if ui.verbose:
887 tree = fileset.parse(expr)
887 tree = fileset.parse(expr)
888 ui.note(fileset.prettyformat(tree), "\n")
888 ui.note(fileset.prettyformat(tree), "\n")
889
889
890 files = set()
890 files = set()
891 if opts['all_files']:
891 if opts['all_files']:
892 for r in repo:
892 for r in repo:
893 c = repo[r]
893 c = repo[r]
894 files.update(c.files())
894 files.update(c.files())
895 files.update(c.substate)
895 files.update(c.substate)
896 if opts['all_files'] or ctx.rev() is None:
896 if opts['all_files'] or ctx.rev() is None:
897 wctx = repo[None]
897 wctx = repo[None]
898 files.update(repo.dirstate.walk(scmutil.matchall(repo),
898 files.update(repo.dirstate.walk(scmutil.matchall(repo),
899 subrepos=list(wctx.substate),
899 subrepos=list(wctx.substate),
900 unknown=True, ignored=True))
900 unknown=True, ignored=True))
901 files.update(wctx.substate)
901 files.update(wctx.substate)
902 else:
902 else:
903 files.update(ctx.files())
903 files.update(ctx.files())
904 files.update(ctx.substate)
904 files.update(ctx.substate)
905
905
906 m = scmutil.matchfiles(repo, ctx.getfileset(expr))
906 m = ctx.matchfileset(expr)
907 for f in sorted(files):
907 for f in sorted(files):
908 if not m(f):
908 if not m(f):
909 continue
909 continue
910 ui.write("%s\n" % f)
910 ui.write("%s\n" % f)
911
911
912 @command('debugformat',
912 @command('debugformat',
913 [] + cmdutil.formatteropts,
913 [] + cmdutil.formatteropts,
914 _(''))
914 _(''))
915 def debugformat(ui, repo, **opts):
915 def debugformat(ui, repo, **opts):
916 """display format information about the current repository
916 """display format information about the current repository
917
917
918 Use --verbose to get extra information about current config value and
918 Use --verbose to get extra information about current config value and
919 Mercurial default."""
919 Mercurial default."""
920 opts = pycompat.byteskwargs(opts)
920 opts = pycompat.byteskwargs(opts)
921 maxvariantlength = max(len(fv.name) for fv in upgrade.allformatvariant)
921 maxvariantlength = max(len(fv.name) for fv in upgrade.allformatvariant)
922 maxvariantlength = max(len('format-variant'), maxvariantlength)
922 maxvariantlength = max(len('format-variant'), maxvariantlength)
923
923
924 def makeformatname(name):
924 def makeformatname(name):
925 return '%s:' + (' ' * (maxvariantlength - len(name)))
925 return '%s:' + (' ' * (maxvariantlength - len(name)))
926
926
927 fm = ui.formatter('debugformat', opts)
927 fm = ui.formatter('debugformat', opts)
928 if fm.isplain():
928 if fm.isplain():
929 def formatvalue(value):
929 def formatvalue(value):
930 if util.safehasattr(value, 'startswith'):
930 if util.safehasattr(value, 'startswith'):
931 return value
931 return value
932 if value:
932 if value:
933 return 'yes'
933 return 'yes'
934 else:
934 else:
935 return 'no'
935 return 'no'
936 else:
936 else:
937 formatvalue = pycompat.identity
937 formatvalue = pycompat.identity
938
938
939 fm.plain('format-variant')
939 fm.plain('format-variant')
940 fm.plain(' ' * (maxvariantlength - len('format-variant')))
940 fm.plain(' ' * (maxvariantlength - len('format-variant')))
941 fm.plain(' repo')
941 fm.plain(' repo')
942 if ui.verbose:
942 if ui.verbose:
943 fm.plain(' config default')
943 fm.plain(' config default')
944 fm.plain('\n')
944 fm.plain('\n')
945 for fv in upgrade.allformatvariant:
945 for fv in upgrade.allformatvariant:
946 fm.startitem()
946 fm.startitem()
947 repovalue = fv.fromrepo(repo)
947 repovalue = fv.fromrepo(repo)
948 configvalue = fv.fromconfig(repo)
948 configvalue = fv.fromconfig(repo)
949
949
950 if repovalue != configvalue:
950 if repovalue != configvalue:
951 namelabel = 'formatvariant.name.mismatchconfig'
951 namelabel = 'formatvariant.name.mismatchconfig'
952 repolabel = 'formatvariant.repo.mismatchconfig'
952 repolabel = 'formatvariant.repo.mismatchconfig'
953 elif repovalue != fv.default:
953 elif repovalue != fv.default:
954 namelabel = 'formatvariant.name.mismatchdefault'
954 namelabel = 'formatvariant.name.mismatchdefault'
955 repolabel = 'formatvariant.repo.mismatchdefault'
955 repolabel = 'formatvariant.repo.mismatchdefault'
956 else:
956 else:
957 namelabel = 'formatvariant.name.uptodate'
957 namelabel = 'formatvariant.name.uptodate'
958 repolabel = 'formatvariant.repo.uptodate'
958 repolabel = 'formatvariant.repo.uptodate'
959
959
960 fm.write('name', makeformatname(fv.name), fv.name,
960 fm.write('name', makeformatname(fv.name), fv.name,
961 label=namelabel)
961 label=namelabel)
962 fm.write('repo', ' %3s', formatvalue(repovalue),
962 fm.write('repo', ' %3s', formatvalue(repovalue),
963 label=repolabel)
963 label=repolabel)
964 if fv.default != configvalue:
964 if fv.default != configvalue:
965 configlabel = 'formatvariant.config.special'
965 configlabel = 'formatvariant.config.special'
966 else:
966 else:
967 configlabel = 'formatvariant.config.default'
967 configlabel = 'formatvariant.config.default'
968 fm.condwrite(ui.verbose, 'config', ' %6s', formatvalue(configvalue),
968 fm.condwrite(ui.verbose, 'config', ' %6s', formatvalue(configvalue),
969 label=configlabel)
969 label=configlabel)
970 fm.condwrite(ui.verbose, 'default', ' %7s', formatvalue(fv.default),
970 fm.condwrite(ui.verbose, 'default', ' %7s', formatvalue(fv.default),
971 label='formatvariant.default')
971 label='formatvariant.default')
972 fm.plain('\n')
972 fm.plain('\n')
973 fm.end()
973 fm.end()
974
974
975 @command('debugfsinfo', [], _('[PATH]'), norepo=True)
975 @command('debugfsinfo', [], _('[PATH]'), norepo=True)
976 def debugfsinfo(ui, path="."):
976 def debugfsinfo(ui, path="."):
977 """show information detected about current filesystem"""
977 """show information detected about current filesystem"""
978 ui.write(('path: %s\n') % path)
978 ui.write(('path: %s\n') % path)
979 ui.write(('mounted on: %s\n') % (util.getfsmountpoint(path) or '(unknown)'))
979 ui.write(('mounted on: %s\n') % (util.getfsmountpoint(path) or '(unknown)'))
980 ui.write(('exec: %s\n') % (util.checkexec(path) and 'yes' or 'no'))
980 ui.write(('exec: %s\n') % (util.checkexec(path) and 'yes' or 'no'))
981 ui.write(('fstype: %s\n') % (util.getfstype(path) or '(unknown)'))
981 ui.write(('fstype: %s\n') % (util.getfstype(path) or '(unknown)'))
982 ui.write(('symlink: %s\n') % (util.checklink(path) and 'yes' or 'no'))
982 ui.write(('symlink: %s\n') % (util.checklink(path) and 'yes' or 'no'))
983 ui.write(('hardlink: %s\n') % (util.checknlink(path) and 'yes' or 'no'))
983 ui.write(('hardlink: %s\n') % (util.checknlink(path) and 'yes' or 'no'))
984 casesensitive = '(unknown)'
984 casesensitive = '(unknown)'
985 try:
985 try:
986 with pycompat.namedtempfile(prefix='.debugfsinfo', dir=path) as f:
986 with pycompat.namedtempfile(prefix='.debugfsinfo', dir=path) as f:
987 casesensitive = util.fscasesensitive(f.name) and 'yes' or 'no'
987 casesensitive = util.fscasesensitive(f.name) and 'yes' or 'no'
988 except OSError:
988 except OSError:
989 pass
989 pass
990 ui.write(('case-sensitive: %s\n') % casesensitive)
990 ui.write(('case-sensitive: %s\n') % casesensitive)
991
991
992 @command('debuggetbundle',
992 @command('debuggetbundle',
993 [('H', 'head', [], _('id of head node'), _('ID')),
993 [('H', 'head', [], _('id of head node'), _('ID')),
994 ('C', 'common', [], _('id of common node'), _('ID')),
994 ('C', 'common', [], _('id of common node'), _('ID')),
995 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
995 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
996 _('REPO FILE [-H|-C ID]...'),
996 _('REPO FILE [-H|-C ID]...'),
997 norepo=True)
997 norepo=True)
998 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
998 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
999 """retrieves a bundle from a repo
999 """retrieves a bundle from a repo
1000
1000
1001 Every ID must be a full-length hex node id string. Saves the bundle to the
1001 Every ID must be a full-length hex node id string. Saves the bundle to the
1002 given file.
1002 given file.
1003 """
1003 """
1004 opts = pycompat.byteskwargs(opts)
1004 opts = pycompat.byteskwargs(opts)
1005 repo = hg.peer(ui, opts, repopath)
1005 repo = hg.peer(ui, opts, repopath)
1006 if not repo.capable('getbundle'):
1006 if not repo.capable('getbundle'):
1007 raise error.Abort("getbundle() not supported by target repository")
1007 raise error.Abort("getbundle() not supported by target repository")
1008 args = {}
1008 args = {}
1009 if common:
1009 if common:
1010 args[r'common'] = [bin(s) for s in common]
1010 args[r'common'] = [bin(s) for s in common]
1011 if head:
1011 if head:
1012 args[r'heads'] = [bin(s) for s in head]
1012 args[r'heads'] = [bin(s) for s in head]
1013 # TODO: get desired bundlecaps from command line.
1013 # TODO: get desired bundlecaps from command line.
1014 args[r'bundlecaps'] = None
1014 args[r'bundlecaps'] = None
1015 bundle = repo.getbundle('debug', **args)
1015 bundle = repo.getbundle('debug', **args)
1016
1016
1017 bundletype = opts.get('type', 'bzip2').lower()
1017 bundletype = opts.get('type', 'bzip2').lower()
1018 btypes = {'none': 'HG10UN',
1018 btypes = {'none': 'HG10UN',
1019 'bzip2': 'HG10BZ',
1019 'bzip2': 'HG10BZ',
1020 'gzip': 'HG10GZ',
1020 'gzip': 'HG10GZ',
1021 'bundle2': 'HG20'}
1021 'bundle2': 'HG20'}
1022 bundletype = btypes.get(bundletype)
1022 bundletype = btypes.get(bundletype)
1023 if bundletype not in bundle2.bundletypes:
1023 if bundletype not in bundle2.bundletypes:
1024 raise error.Abort(_('unknown bundle type specified with --type'))
1024 raise error.Abort(_('unknown bundle type specified with --type'))
1025 bundle2.writebundle(ui, bundle, bundlepath, bundletype)
1025 bundle2.writebundle(ui, bundle, bundlepath, bundletype)
1026
1026
1027 @command('debugignore', [], '[FILE]')
1027 @command('debugignore', [], '[FILE]')
1028 def debugignore(ui, repo, *files, **opts):
1028 def debugignore(ui, repo, *files, **opts):
1029 """display the combined ignore pattern and information about ignored files
1029 """display the combined ignore pattern and information about ignored files
1030
1030
1031 With no argument display the combined ignore pattern.
1031 With no argument display the combined ignore pattern.
1032
1032
1033 Given space separated file names, shows if the given file is ignored and
1033 Given space separated file names, shows if the given file is ignored and
1034 if so, show the ignore rule (file and line number) that matched it.
1034 if so, show the ignore rule (file and line number) that matched it.
1035 """
1035 """
1036 ignore = repo.dirstate._ignore
1036 ignore = repo.dirstate._ignore
1037 if not files:
1037 if not files:
1038 # Show all the patterns
1038 # Show all the patterns
1039 ui.write("%s\n" % pycompat.byterepr(ignore))
1039 ui.write("%s\n" % pycompat.byterepr(ignore))
1040 else:
1040 else:
1041 m = scmutil.match(repo[None], pats=files)
1041 m = scmutil.match(repo[None], pats=files)
1042 for f in m.files():
1042 for f in m.files():
1043 nf = util.normpath(f)
1043 nf = util.normpath(f)
1044 ignored = None
1044 ignored = None
1045 ignoredata = None
1045 ignoredata = None
1046 if nf != '.':
1046 if nf != '.':
1047 if ignore(nf):
1047 if ignore(nf):
1048 ignored = nf
1048 ignored = nf
1049 ignoredata = repo.dirstate._ignorefileandline(nf)
1049 ignoredata = repo.dirstate._ignorefileandline(nf)
1050 else:
1050 else:
1051 for p in util.finddirs(nf):
1051 for p in util.finddirs(nf):
1052 if ignore(p):
1052 if ignore(p):
1053 ignored = p
1053 ignored = p
1054 ignoredata = repo.dirstate._ignorefileandline(p)
1054 ignoredata = repo.dirstate._ignorefileandline(p)
1055 break
1055 break
1056 if ignored:
1056 if ignored:
1057 if ignored == nf:
1057 if ignored == nf:
1058 ui.write(_("%s is ignored\n") % m.uipath(f))
1058 ui.write(_("%s is ignored\n") % m.uipath(f))
1059 else:
1059 else:
1060 ui.write(_("%s is ignored because of "
1060 ui.write(_("%s is ignored because of "
1061 "containing folder %s\n")
1061 "containing folder %s\n")
1062 % (m.uipath(f), ignored))
1062 % (m.uipath(f), ignored))
1063 ignorefile, lineno, line = ignoredata
1063 ignorefile, lineno, line = ignoredata
1064 ui.write(_("(ignore rule in %s, line %d: '%s')\n")
1064 ui.write(_("(ignore rule in %s, line %d: '%s')\n")
1065 % (ignorefile, lineno, line))
1065 % (ignorefile, lineno, line))
1066 else:
1066 else:
1067 ui.write(_("%s is not ignored\n") % m.uipath(f))
1067 ui.write(_("%s is not ignored\n") % m.uipath(f))
1068
1068
1069 @command('debugindex', cmdutil.debugrevlogopts +
1069 @command('debugindex', cmdutil.debugrevlogopts +
1070 [('f', 'format', 0, _('revlog format'), _('FORMAT'))],
1070 [('f', 'format', 0, _('revlog format'), _('FORMAT'))],
1071 _('[-f FORMAT] -c|-m|FILE'),
1071 _('[-f FORMAT] -c|-m|FILE'),
1072 optionalrepo=True)
1072 optionalrepo=True)
1073 def debugindex(ui, repo, file_=None, **opts):
1073 def debugindex(ui, repo, file_=None, **opts):
1074 """dump the contents of an index file"""
1074 """dump the contents of an index file"""
1075 opts = pycompat.byteskwargs(opts)
1075 opts = pycompat.byteskwargs(opts)
1076 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
1076 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
1077 format = opts.get('format', 0)
1077 format = opts.get('format', 0)
1078 if format not in (0, 1):
1078 if format not in (0, 1):
1079 raise error.Abort(_("unknown format %d") % format)
1079 raise error.Abort(_("unknown format %d") % format)
1080
1080
1081 if ui.debugflag:
1081 if ui.debugflag:
1082 shortfn = hex
1082 shortfn = hex
1083 else:
1083 else:
1084 shortfn = short
1084 shortfn = short
1085
1085
1086 # There might not be anything in r, so have a sane default
1086 # There might not be anything in r, so have a sane default
1087 idlen = 12
1087 idlen = 12
1088 for i in r:
1088 for i in r:
1089 idlen = len(shortfn(r.node(i)))
1089 idlen = len(shortfn(r.node(i)))
1090 break
1090 break
1091
1091
1092 if format == 0:
1092 if format == 0:
1093 if ui.verbose:
1093 if ui.verbose:
1094 ui.write((" rev offset length linkrev"
1094 ui.write((" rev offset length linkrev"
1095 " %s %s p2\n") % ("nodeid".ljust(idlen),
1095 " %s %s p2\n") % ("nodeid".ljust(idlen),
1096 "p1".ljust(idlen)))
1096 "p1".ljust(idlen)))
1097 else:
1097 else:
1098 ui.write((" rev linkrev %s %s p2\n") % (
1098 ui.write((" rev linkrev %s %s p2\n") % (
1099 "nodeid".ljust(idlen), "p1".ljust(idlen)))
1099 "nodeid".ljust(idlen), "p1".ljust(idlen)))
1100 elif format == 1:
1100 elif format == 1:
1101 if ui.verbose:
1101 if ui.verbose:
1102 ui.write((" rev flag offset length size link p1"
1102 ui.write((" rev flag offset length size link p1"
1103 " p2 %s\n") % "nodeid".rjust(idlen))
1103 " p2 %s\n") % "nodeid".rjust(idlen))
1104 else:
1104 else:
1105 ui.write((" rev flag size link p1 p2 %s\n") %
1105 ui.write((" rev flag size link p1 p2 %s\n") %
1106 "nodeid".rjust(idlen))
1106 "nodeid".rjust(idlen))
1107
1107
1108 for i in r:
1108 for i in r:
1109 node = r.node(i)
1109 node = r.node(i)
1110 if format == 0:
1110 if format == 0:
1111 try:
1111 try:
1112 pp = r.parents(node)
1112 pp = r.parents(node)
1113 except Exception:
1113 except Exception:
1114 pp = [nullid, nullid]
1114 pp = [nullid, nullid]
1115 if ui.verbose:
1115 if ui.verbose:
1116 ui.write("% 6d % 9d % 7d % 7d %s %s %s\n" % (
1116 ui.write("% 6d % 9d % 7d % 7d %s %s %s\n" % (
1117 i, r.start(i), r.length(i), r.linkrev(i),
1117 i, r.start(i), r.length(i), r.linkrev(i),
1118 shortfn(node), shortfn(pp[0]), shortfn(pp[1])))
1118 shortfn(node), shortfn(pp[0]), shortfn(pp[1])))
1119 else:
1119 else:
1120 ui.write("% 6d % 7d %s %s %s\n" % (
1120 ui.write("% 6d % 7d %s %s %s\n" % (
1121 i, r.linkrev(i), shortfn(node), shortfn(pp[0]),
1121 i, r.linkrev(i), shortfn(node), shortfn(pp[0]),
1122 shortfn(pp[1])))
1122 shortfn(pp[1])))
1123 elif format == 1:
1123 elif format == 1:
1124 pr = r.parentrevs(i)
1124 pr = r.parentrevs(i)
1125 if ui.verbose:
1125 if ui.verbose:
1126 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d %s\n" % (
1126 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d %s\n" % (
1127 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1127 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1128 r.linkrev(i), pr[0], pr[1], shortfn(node)))
1128 r.linkrev(i), pr[0], pr[1], shortfn(node)))
1129 else:
1129 else:
1130 ui.write("% 6d %04x % 8d % 6d % 6d % 6d %s\n" % (
1130 ui.write("% 6d %04x % 8d % 6d % 6d % 6d %s\n" % (
1131 i, r.flags(i), r.rawsize(i), r.linkrev(i), pr[0], pr[1],
1131 i, r.flags(i), r.rawsize(i), r.linkrev(i), pr[0], pr[1],
1132 shortfn(node)))
1132 shortfn(node)))
1133
1133
1134 @command('debugindexdot', cmdutil.debugrevlogopts,
1134 @command('debugindexdot', cmdutil.debugrevlogopts,
1135 _('-c|-m|FILE'), optionalrepo=True)
1135 _('-c|-m|FILE'), optionalrepo=True)
1136 def debugindexdot(ui, repo, file_=None, **opts):
1136 def debugindexdot(ui, repo, file_=None, **opts):
1137 """dump an index DAG as a graphviz dot file"""
1137 """dump an index DAG as a graphviz dot file"""
1138 opts = pycompat.byteskwargs(opts)
1138 opts = pycompat.byteskwargs(opts)
1139 r = cmdutil.openrevlog(repo, 'debugindexdot', file_, opts)
1139 r = cmdutil.openrevlog(repo, 'debugindexdot', file_, opts)
1140 ui.write(("digraph G {\n"))
1140 ui.write(("digraph G {\n"))
1141 for i in r:
1141 for i in r:
1142 node = r.node(i)
1142 node = r.node(i)
1143 pp = r.parents(node)
1143 pp = r.parents(node)
1144 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1144 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1145 if pp[1] != nullid:
1145 if pp[1] != nullid:
1146 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1146 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1147 ui.write("}\n")
1147 ui.write("}\n")
1148
1148
1149 @command('debuginstall', [] + cmdutil.formatteropts, '', norepo=True)
1149 @command('debuginstall', [] + cmdutil.formatteropts, '', norepo=True)
1150 def debuginstall(ui, **opts):
1150 def debuginstall(ui, **opts):
1151 '''test Mercurial installation
1151 '''test Mercurial installation
1152
1152
1153 Returns 0 on success.
1153 Returns 0 on success.
1154 '''
1154 '''
1155 opts = pycompat.byteskwargs(opts)
1155 opts = pycompat.byteskwargs(opts)
1156
1156
1157 def writetemp(contents):
1157 def writetemp(contents):
1158 (fd, name) = pycompat.mkstemp(prefix="hg-debuginstall-")
1158 (fd, name) = pycompat.mkstemp(prefix="hg-debuginstall-")
1159 f = os.fdopen(fd, r"wb")
1159 f = os.fdopen(fd, r"wb")
1160 f.write(contents)
1160 f.write(contents)
1161 f.close()
1161 f.close()
1162 return name
1162 return name
1163
1163
1164 problems = 0
1164 problems = 0
1165
1165
1166 fm = ui.formatter('debuginstall', opts)
1166 fm = ui.formatter('debuginstall', opts)
1167 fm.startitem()
1167 fm.startitem()
1168
1168
1169 # encoding
1169 # encoding
1170 fm.write('encoding', _("checking encoding (%s)...\n"), encoding.encoding)
1170 fm.write('encoding', _("checking encoding (%s)...\n"), encoding.encoding)
1171 err = None
1171 err = None
1172 try:
1172 try:
1173 codecs.lookup(pycompat.sysstr(encoding.encoding))
1173 codecs.lookup(pycompat.sysstr(encoding.encoding))
1174 except LookupError as inst:
1174 except LookupError as inst:
1175 err = stringutil.forcebytestr(inst)
1175 err = stringutil.forcebytestr(inst)
1176 problems += 1
1176 problems += 1
1177 fm.condwrite(err, 'encodingerror', _(" %s\n"
1177 fm.condwrite(err, 'encodingerror', _(" %s\n"
1178 " (check that your locale is properly set)\n"), err)
1178 " (check that your locale is properly set)\n"), err)
1179
1179
1180 # Python
1180 # Python
1181 fm.write('pythonexe', _("checking Python executable (%s)\n"),
1181 fm.write('pythonexe', _("checking Python executable (%s)\n"),
1182 pycompat.sysexecutable)
1182 pycompat.sysexecutable)
1183 fm.write('pythonver', _("checking Python version (%s)\n"),
1183 fm.write('pythonver', _("checking Python version (%s)\n"),
1184 ("%d.%d.%d" % sys.version_info[:3]))
1184 ("%d.%d.%d" % sys.version_info[:3]))
1185 fm.write('pythonlib', _("checking Python lib (%s)...\n"),
1185 fm.write('pythonlib', _("checking Python lib (%s)...\n"),
1186 os.path.dirname(pycompat.fsencode(os.__file__)))
1186 os.path.dirname(pycompat.fsencode(os.__file__)))
1187
1187
1188 security = set(sslutil.supportedprotocols)
1188 security = set(sslutil.supportedprotocols)
1189 if sslutil.hassni:
1189 if sslutil.hassni:
1190 security.add('sni')
1190 security.add('sni')
1191
1191
1192 fm.write('pythonsecurity', _("checking Python security support (%s)\n"),
1192 fm.write('pythonsecurity', _("checking Python security support (%s)\n"),
1193 fm.formatlist(sorted(security), name='protocol',
1193 fm.formatlist(sorted(security), name='protocol',
1194 fmt='%s', sep=','))
1194 fmt='%s', sep=','))
1195
1195
1196 # These are warnings, not errors. So don't increment problem count. This
1196 # These are warnings, not errors. So don't increment problem count. This
1197 # may change in the future.
1197 # may change in the future.
1198 if 'tls1.2' not in security:
1198 if 'tls1.2' not in security:
1199 fm.plain(_(' TLS 1.2 not supported by Python install; '
1199 fm.plain(_(' TLS 1.2 not supported by Python install; '
1200 'network connections lack modern security\n'))
1200 'network connections lack modern security\n'))
1201 if 'sni' not in security:
1201 if 'sni' not in security:
1202 fm.plain(_(' SNI not supported by Python install; may have '
1202 fm.plain(_(' SNI not supported by Python install; may have '
1203 'connectivity issues with some servers\n'))
1203 'connectivity issues with some servers\n'))
1204
1204
1205 # TODO print CA cert info
1205 # TODO print CA cert info
1206
1206
1207 # hg version
1207 # hg version
1208 hgver = util.version()
1208 hgver = util.version()
1209 fm.write('hgver', _("checking Mercurial version (%s)\n"),
1209 fm.write('hgver', _("checking Mercurial version (%s)\n"),
1210 hgver.split('+')[0])
1210 hgver.split('+')[0])
1211 fm.write('hgverextra', _("checking Mercurial custom build (%s)\n"),
1211 fm.write('hgverextra', _("checking Mercurial custom build (%s)\n"),
1212 '+'.join(hgver.split('+')[1:]))
1212 '+'.join(hgver.split('+')[1:]))
1213
1213
1214 # compiled modules
1214 # compiled modules
1215 fm.write('hgmodulepolicy', _("checking module policy (%s)\n"),
1215 fm.write('hgmodulepolicy', _("checking module policy (%s)\n"),
1216 policy.policy)
1216 policy.policy)
1217 fm.write('hgmodules', _("checking installed modules (%s)...\n"),
1217 fm.write('hgmodules', _("checking installed modules (%s)...\n"),
1218 os.path.dirname(pycompat.fsencode(__file__)))
1218 os.path.dirname(pycompat.fsencode(__file__)))
1219
1219
1220 if policy.policy in ('c', 'allow'):
1220 if policy.policy in ('c', 'allow'):
1221 err = None
1221 err = None
1222 try:
1222 try:
1223 from .cext import (
1223 from .cext import (
1224 base85,
1224 base85,
1225 bdiff,
1225 bdiff,
1226 mpatch,
1226 mpatch,
1227 osutil,
1227 osutil,
1228 )
1228 )
1229 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
1229 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
1230 except Exception as inst:
1230 except Exception as inst:
1231 err = stringutil.forcebytestr(inst)
1231 err = stringutil.forcebytestr(inst)
1232 problems += 1
1232 problems += 1
1233 fm.condwrite(err, 'extensionserror', " %s\n", err)
1233 fm.condwrite(err, 'extensionserror', " %s\n", err)
1234
1234
1235 compengines = util.compengines._engines.values()
1235 compengines = util.compengines._engines.values()
1236 fm.write('compengines', _('checking registered compression engines (%s)\n'),
1236 fm.write('compengines', _('checking registered compression engines (%s)\n'),
1237 fm.formatlist(sorted(e.name() for e in compengines),
1237 fm.formatlist(sorted(e.name() for e in compengines),
1238 name='compengine', fmt='%s', sep=', '))
1238 name='compengine', fmt='%s', sep=', '))
1239 fm.write('compenginesavail', _('checking available compression engines '
1239 fm.write('compenginesavail', _('checking available compression engines '
1240 '(%s)\n'),
1240 '(%s)\n'),
1241 fm.formatlist(sorted(e.name() for e in compengines
1241 fm.formatlist(sorted(e.name() for e in compengines
1242 if e.available()),
1242 if e.available()),
1243 name='compengine', fmt='%s', sep=', '))
1243 name='compengine', fmt='%s', sep=', '))
1244 wirecompengines = util.compengines.supportedwireengines(util.SERVERROLE)
1244 wirecompengines = util.compengines.supportedwireengines(util.SERVERROLE)
1245 fm.write('compenginesserver', _('checking available compression engines '
1245 fm.write('compenginesserver', _('checking available compression engines '
1246 'for wire protocol (%s)\n'),
1246 'for wire protocol (%s)\n'),
1247 fm.formatlist([e.name() for e in wirecompengines
1247 fm.formatlist([e.name() for e in wirecompengines
1248 if e.wireprotosupport()],
1248 if e.wireprotosupport()],
1249 name='compengine', fmt='%s', sep=', '))
1249 name='compengine', fmt='%s', sep=', '))
1250 re2 = 'missing'
1250 re2 = 'missing'
1251 if util._re2:
1251 if util._re2:
1252 re2 = 'available'
1252 re2 = 'available'
1253 fm.plain(_('checking "re2" regexp engine (%s)\n') % re2)
1253 fm.plain(_('checking "re2" regexp engine (%s)\n') % re2)
1254 fm.data(re2=bool(util._re2))
1254 fm.data(re2=bool(util._re2))
1255
1255
1256 # templates
1256 # templates
1257 p = templater.templatepaths()
1257 p = templater.templatepaths()
1258 fm.write('templatedirs', 'checking templates (%s)...\n', ' '.join(p))
1258 fm.write('templatedirs', 'checking templates (%s)...\n', ' '.join(p))
1259 fm.condwrite(not p, '', _(" no template directories found\n"))
1259 fm.condwrite(not p, '', _(" no template directories found\n"))
1260 if p:
1260 if p:
1261 m = templater.templatepath("map-cmdline.default")
1261 m = templater.templatepath("map-cmdline.default")
1262 if m:
1262 if m:
1263 # template found, check if it is working
1263 # template found, check if it is working
1264 err = None
1264 err = None
1265 try:
1265 try:
1266 templater.templater.frommapfile(m)
1266 templater.templater.frommapfile(m)
1267 except Exception as inst:
1267 except Exception as inst:
1268 err = stringutil.forcebytestr(inst)
1268 err = stringutil.forcebytestr(inst)
1269 p = None
1269 p = None
1270 fm.condwrite(err, 'defaulttemplateerror', " %s\n", err)
1270 fm.condwrite(err, 'defaulttemplateerror', " %s\n", err)
1271 else:
1271 else:
1272 p = None
1272 p = None
1273 fm.condwrite(p, 'defaulttemplate',
1273 fm.condwrite(p, 'defaulttemplate',
1274 _("checking default template (%s)\n"), m)
1274 _("checking default template (%s)\n"), m)
1275 fm.condwrite(not m, 'defaulttemplatenotfound',
1275 fm.condwrite(not m, 'defaulttemplatenotfound',
1276 _(" template '%s' not found\n"), "default")
1276 _(" template '%s' not found\n"), "default")
1277 if not p:
1277 if not p:
1278 problems += 1
1278 problems += 1
1279 fm.condwrite(not p, '',
1279 fm.condwrite(not p, '',
1280 _(" (templates seem to have been installed incorrectly)\n"))
1280 _(" (templates seem to have been installed incorrectly)\n"))
1281
1281
1282 # editor
1282 # editor
1283 editor = ui.geteditor()
1283 editor = ui.geteditor()
1284 editor = util.expandpath(editor)
1284 editor = util.expandpath(editor)
1285 editorbin = procutil.shellsplit(editor)[0]
1285 editorbin = procutil.shellsplit(editor)[0]
1286 fm.write('editor', _("checking commit editor... (%s)\n"), editorbin)
1286 fm.write('editor', _("checking commit editor... (%s)\n"), editorbin)
1287 cmdpath = procutil.findexe(editorbin)
1287 cmdpath = procutil.findexe(editorbin)
1288 fm.condwrite(not cmdpath and editor == 'vi', 'vinotfound',
1288 fm.condwrite(not cmdpath and editor == 'vi', 'vinotfound',
1289 _(" No commit editor set and can't find %s in PATH\n"
1289 _(" No commit editor set and can't find %s in PATH\n"
1290 " (specify a commit editor in your configuration"
1290 " (specify a commit editor in your configuration"
1291 " file)\n"), not cmdpath and editor == 'vi' and editorbin)
1291 " file)\n"), not cmdpath and editor == 'vi' and editorbin)
1292 fm.condwrite(not cmdpath and editor != 'vi', 'editornotfound',
1292 fm.condwrite(not cmdpath and editor != 'vi', 'editornotfound',
1293 _(" Can't find editor '%s' in PATH\n"
1293 _(" Can't find editor '%s' in PATH\n"
1294 " (specify a commit editor in your configuration"
1294 " (specify a commit editor in your configuration"
1295 " file)\n"), not cmdpath and editorbin)
1295 " file)\n"), not cmdpath and editorbin)
1296 if not cmdpath and editor != 'vi':
1296 if not cmdpath and editor != 'vi':
1297 problems += 1
1297 problems += 1
1298
1298
1299 # check username
1299 # check username
1300 username = None
1300 username = None
1301 err = None
1301 err = None
1302 try:
1302 try:
1303 username = ui.username()
1303 username = ui.username()
1304 except error.Abort as e:
1304 except error.Abort as e:
1305 err = stringutil.forcebytestr(e)
1305 err = stringutil.forcebytestr(e)
1306 problems += 1
1306 problems += 1
1307
1307
1308 fm.condwrite(username, 'username', _("checking username (%s)\n"), username)
1308 fm.condwrite(username, 'username', _("checking username (%s)\n"), username)
1309 fm.condwrite(err, 'usernameerror', _("checking username...\n %s\n"
1309 fm.condwrite(err, 'usernameerror', _("checking username...\n %s\n"
1310 " (specify a username in your configuration file)\n"), err)
1310 " (specify a username in your configuration file)\n"), err)
1311
1311
1312 fm.condwrite(not problems, '',
1312 fm.condwrite(not problems, '',
1313 _("no problems detected\n"))
1313 _("no problems detected\n"))
1314 if not problems:
1314 if not problems:
1315 fm.data(problems=problems)
1315 fm.data(problems=problems)
1316 fm.condwrite(problems, 'problems',
1316 fm.condwrite(problems, 'problems',
1317 _("%d problems detected,"
1317 _("%d problems detected,"
1318 " please check your install!\n"), problems)
1318 " please check your install!\n"), problems)
1319 fm.end()
1319 fm.end()
1320
1320
1321 return problems
1321 return problems
1322
1322
1323 @command('debugknown', [], _('REPO ID...'), norepo=True)
1323 @command('debugknown', [], _('REPO ID...'), norepo=True)
1324 def debugknown(ui, repopath, *ids, **opts):
1324 def debugknown(ui, repopath, *ids, **opts):
1325 """test whether node ids are known to a repo
1325 """test whether node ids are known to a repo
1326
1326
1327 Every ID must be a full-length hex node id string. Returns a list of 0s
1327 Every ID must be a full-length hex node id string. Returns a list of 0s
1328 and 1s indicating unknown/known.
1328 and 1s indicating unknown/known.
1329 """
1329 """
1330 opts = pycompat.byteskwargs(opts)
1330 opts = pycompat.byteskwargs(opts)
1331 repo = hg.peer(ui, opts, repopath)
1331 repo = hg.peer(ui, opts, repopath)
1332 if not repo.capable('known'):
1332 if not repo.capable('known'):
1333 raise error.Abort("known() not supported by target repository")
1333 raise error.Abort("known() not supported by target repository")
1334 flags = repo.known([bin(s) for s in ids])
1334 flags = repo.known([bin(s) for s in ids])
1335 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
1335 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
1336
1336
1337 @command('debuglabelcomplete', [], _('LABEL...'))
1337 @command('debuglabelcomplete', [], _('LABEL...'))
1338 def debuglabelcomplete(ui, repo, *args):
1338 def debuglabelcomplete(ui, repo, *args):
1339 '''backwards compatibility with old bash completion scripts (DEPRECATED)'''
1339 '''backwards compatibility with old bash completion scripts (DEPRECATED)'''
1340 debugnamecomplete(ui, repo, *args)
1340 debugnamecomplete(ui, repo, *args)
1341
1341
1342 @command('debuglocks',
1342 @command('debuglocks',
1343 [('L', 'force-lock', None, _('free the store lock (DANGEROUS)')),
1343 [('L', 'force-lock', None, _('free the store lock (DANGEROUS)')),
1344 ('W', 'force-wlock', None,
1344 ('W', 'force-wlock', None,
1345 _('free the working state lock (DANGEROUS)')),
1345 _('free the working state lock (DANGEROUS)')),
1346 ('s', 'set-lock', None, _('set the store lock until stopped')),
1346 ('s', 'set-lock', None, _('set the store lock until stopped')),
1347 ('S', 'set-wlock', None,
1347 ('S', 'set-wlock', None,
1348 _('set the working state lock until stopped'))],
1348 _('set the working state lock until stopped'))],
1349 _('[OPTION]...'))
1349 _('[OPTION]...'))
1350 def debuglocks(ui, repo, **opts):
1350 def debuglocks(ui, repo, **opts):
1351 """show or modify state of locks
1351 """show or modify state of locks
1352
1352
1353 By default, this command will show which locks are held. This
1353 By default, this command will show which locks are held. This
1354 includes the user and process holding the lock, the amount of time
1354 includes the user and process holding the lock, the amount of time
1355 the lock has been held, and the machine name where the process is
1355 the lock has been held, and the machine name where the process is
1356 running if it's not local.
1356 running if it's not local.
1357
1357
1358 Locks protect the integrity of Mercurial's data, so should be
1358 Locks protect the integrity of Mercurial's data, so should be
1359 treated with care. System crashes or other interruptions may cause
1359 treated with care. System crashes or other interruptions may cause
1360 locks to not be properly released, though Mercurial will usually
1360 locks to not be properly released, though Mercurial will usually
1361 detect and remove such stale locks automatically.
1361 detect and remove such stale locks automatically.
1362
1362
1363 However, detecting stale locks may not always be possible (for
1363 However, detecting stale locks may not always be possible (for
1364 instance, on a shared filesystem). Removing locks may also be
1364 instance, on a shared filesystem). Removing locks may also be
1365 blocked by filesystem permissions.
1365 blocked by filesystem permissions.
1366
1366
1367 Setting a lock will prevent other commands from changing the data.
1367 Setting a lock will prevent other commands from changing the data.
1368 The command will wait until an interruption (SIGINT, SIGTERM, ...) occurs.
1368 The command will wait until an interruption (SIGINT, SIGTERM, ...) occurs.
1369 The set locks are removed when the command exits.
1369 The set locks are removed when the command exits.
1370
1370
1371 Returns 0 if no locks are held.
1371 Returns 0 if no locks are held.
1372
1372
1373 """
1373 """
1374
1374
1375 if opts.get(r'force_lock'):
1375 if opts.get(r'force_lock'):
1376 repo.svfs.unlink('lock')
1376 repo.svfs.unlink('lock')
1377 if opts.get(r'force_wlock'):
1377 if opts.get(r'force_wlock'):
1378 repo.vfs.unlink('wlock')
1378 repo.vfs.unlink('wlock')
1379 if opts.get(r'force_lock') or opts.get(r'force_wlock'):
1379 if opts.get(r'force_lock') or opts.get(r'force_wlock'):
1380 return 0
1380 return 0
1381
1381
1382 locks = []
1382 locks = []
1383 try:
1383 try:
1384 if opts.get(r'set_wlock'):
1384 if opts.get(r'set_wlock'):
1385 try:
1385 try:
1386 locks.append(repo.wlock(False))
1386 locks.append(repo.wlock(False))
1387 except error.LockHeld:
1387 except error.LockHeld:
1388 raise error.Abort(_('wlock is already held'))
1388 raise error.Abort(_('wlock is already held'))
1389 if opts.get(r'set_lock'):
1389 if opts.get(r'set_lock'):
1390 try:
1390 try:
1391 locks.append(repo.lock(False))
1391 locks.append(repo.lock(False))
1392 except error.LockHeld:
1392 except error.LockHeld:
1393 raise error.Abort(_('lock is already held'))
1393 raise error.Abort(_('lock is already held'))
1394 if len(locks):
1394 if len(locks):
1395 ui.promptchoice(_("ready to release the lock (y)? $$ &Yes"))
1395 ui.promptchoice(_("ready to release the lock (y)? $$ &Yes"))
1396 return 0
1396 return 0
1397 finally:
1397 finally:
1398 release(*locks)
1398 release(*locks)
1399
1399
1400 now = time.time()
1400 now = time.time()
1401 held = 0
1401 held = 0
1402
1402
1403 def report(vfs, name, method):
1403 def report(vfs, name, method):
1404 # this causes stale locks to get reaped for more accurate reporting
1404 # this causes stale locks to get reaped for more accurate reporting
1405 try:
1405 try:
1406 l = method(False)
1406 l = method(False)
1407 except error.LockHeld:
1407 except error.LockHeld:
1408 l = None
1408 l = None
1409
1409
1410 if l:
1410 if l:
1411 l.release()
1411 l.release()
1412 else:
1412 else:
1413 try:
1413 try:
1414 st = vfs.lstat(name)
1414 st = vfs.lstat(name)
1415 age = now - st[stat.ST_MTIME]
1415 age = now - st[stat.ST_MTIME]
1416 user = util.username(st.st_uid)
1416 user = util.username(st.st_uid)
1417 locker = vfs.readlock(name)
1417 locker = vfs.readlock(name)
1418 if ":" in locker:
1418 if ":" in locker:
1419 host, pid = locker.split(':')
1419 host, pid = locker.split(':')
1420 if host == socket.gethostname():
1420 if host == socket.gethostname():
1421 locker = 'user %s, process %s' % (user, pid)
1421 locker = 'user %s, process %s' % (user, pid)
1422 else:
1422 else:
1423 locker = 'user %s, process %s, host %s' \
1423 locker = 'user %s, process %s, host %s' \
1424 % (user, pid, host)
1424 % (user, pid, host)
1425 ui.write(("%-6s %s (%ds)\n") % (name + ":", locker, age))
1425 ui.write(("%-6s %s (%ds)\n") % (name + ":", locker, age))
1426 return 1
1426 return 1
1427 except OSError as e:
1427 except OSError as e:
1428 if e.errno != errno.ENOENT:
1428 if e.errno != errno.ENOENT:
1429 raise
1429 raise
1430
1430
1431 ui.write(("%-6s free\n") % (name + ":"))
1431 ui.write(("%-6s free\n") % (name + ":"))
1432 return 0
1432 return 0
1433
1433
1434 held += report(repo.svfs, "lock", repo.lock)
1434 held += report(repo.svfs, "lock", repo.lock)
1435 held += report(repo.vfs, "wlock", repo.wlock)
1435 held += report(repo.vfs, "wlock", repo.wlock)
1436
1436
1437 return held
1437 return held
1438
1438
1439 @command('debugmergestate', [], '')
1439 @command('debugmergestate', [], '')
1440 def debugmergestate(ui, repo, *args):
1440 def debugmergestate(ui, repo, *args):
1441 """print merge state
1441 """print merge state
1442
1442
1443 Use --verbose to print out information about whether v1 or v2 merge state
1443 Use --verbose to print out information about whether v1 or v2 merge state
1444 was chosen."""
1444 was chosen."""
1445 def _hashornull(h):
1445 def _hashornull(h):
1446 if h == nullhex:
1446 if h == nullhex:
1447 return 'null'
1447 return 'null'
1448 else:
1448 else:
1449 return h
1449 return h
1450
1450
1451 def printrecords(version):
1451 def printrecords(version):
1452 ui.write(('* version %d records\n') % version)
1452 ui.write(('* version %d records\n') % version)
1453 if version == 1:
1453 if version == 1:
1454 records = v1records
1454 records = v1records
1455 else:
1455 else:
1456 records = v2records
1456 records = v2records
1457
1457
1458 for rtype, record in records:
1458 for rtype, record in records:
1459 # pretty print some record types
1459 # pretty print some record types
1460 if rtype == 'L':
1460 if rtype == 'L':
1461 ui.write(('local: %s\n') % record)
1461 ui.write(('local: %s\n') % record)
1462 elif rtype == 'O':
1462 elif rtype == 'O':
1463 ui.write(('other: %s\n') % record)
1463 ui.write(('other: %s\n') % record)
1464 elif rtype == 'm':
1464 elif rtype == 'm':
1465 driver, mdstate = record.split('\0', 1)
1465 driver, mdstate = record.split('\0', 1)
1466 ui.write(('merge driver: %s (state "%s")\n')
1466 ui.write(('merge driver: %s (state "%s")\n')
1467 % (driver, mdstate))
1467 % (driver, mdstate))
1468 elif rtype in 'FDC':
1468 elif rtype in 'FDC':
1469 r = record.split('\0')
1469 r = record.split('\0')
1470 f, state, hash, lfile, afile, anode, ofile = r[0:7]
1470 f, state, hash, lfile, afile, anode, ofile = r[0:7]
1471 if version == 1:
1471 if version == 1:
1472 onode = 'not stored in v1 format'
1472 onode = 'not stored in v1 format'
1473 flags = r[7]
1473 flags = r[7]
1474 else:
1474 else:
1475 onode, flags = r[7:9]
1475 onode, flags = r[7:9]
1476 ui.write(('file: %s (record type "%s", state "%s", hash %s)\n')
1476 ui.write(('file: %s (record type "%s", state "%s", hash %s)\n')
1477 % (f, rtype, state, _hashornull(hash)))
1477 % (f, rtype, state, _hashornull(hash)))
1478 ui.write((' local path: %s (flags "%s")\n') % (lfile, flags))
1478 ui.write((' local path: %s (flags "%s")\n') % (lfile, flags))
1479 ui.write((' ancestor path: %s (node %s)\n')
1479 ui.write((' ancestor path: %s (node %s)\n')
1480 % (afile, _hashornull(anode)))
1480 % (afile, _hashornull(anode)))
1481 ui.write((' other path: %s (node %s)\n')
1481 ui.write((' other path: %s (node %s)\n')
1482 % (ofile, _hashornull(onode)))
1482 % (ofile, _hashornull(onode)))
1483 elif rtype == 'f':
1483 elif rtype == 'f':
1484 filename, rawextras = record.split('\0', 1)
1484 filename, rawextras = record.split('\0', 1)
1485 extras = rawextras.split('\0')
1485 extras = rawextras.split('\0')
1486 i = 0
1486 i = 0
1487 extrastrings = []
1487 extrastrings = []
1488 while i < len(extras):
1488 while i < len(extras):
1489 extrastrings.append('%s = %s' % (extras[i], extras[i + 1]))
1489 extrastrings.append('%s = %s' % (extras[i], extras[i + 1]))
1490 i += 2
1490 i += 2
1491
1491
1492 ui.write(('file extras: %s (%s)\n')
1492 ui.write(('file extras: %s (%s)\n')
1493 % (filename, ', '.join(extrastrings)))
1493 % (filename, ', '.join(extrastrings)))
1494 elif rtype == 'l':
1494 elif rtype == 'l':
1495 labels = record.split('\0', 2)
1495 labels = record.split('\0', 2)
1496 labels = [l for l in labels if len(l) > 0]
1496 labels = [l for l in labels if len(l) > 0]
1497 ui.write(('labels:\n'))
1497 ui.write(('labels:\n'))
1498 ui.write((' local: %s\n' % labels[0]))
1498 ui.write((' local: %s\n' % labels[0]))
1499 ui.write((' other: %s\n' % labels[1]))
1499 ui.write((' other: %s\n' % labels[1]))
1500 if len(labels) > 2:
1500 if len(labels) > 2:
1501 ui.write((' base: %s\n' % labels[2]))
1501 ui.write((' base: %s\n' % labels[2]))
1502 else:
1502 else:
1503 ui.write(('unrecognized entry: %s\t%s\n')
1503 ui.write(('unrecognized entry: %s\t%s\n')
1504 % (rtype, record.replace('\0', '\t')))
1504 % (rtype, record.replace('\0', '\t')))
1505
1505
1506 # Avoid mergestate.read() since it may raise an exception for unsupported
1506 # Avoid mergestate.read() since it may raise an exception for unsupported
1507 # merge state records. We shouldn't be doing this, but this is OK since this
1507 # merge state records. We shouldn't be doing this, but this is OK since this
1508 # command is pretty low-level.
1508 # command is pretty low-level.
1509 ms = mergemod.mergestate(repo)
1509 ms = mergemod.mergestate(repo)
1510
1510
1511 # sort so that reasonable information is on top
1511 # sort so that reasonable information is on top
1512 v1records = ms._readrecordsv1()
1512 v1records = ms._readrecordsv1()
1513 v2records = ms._readrecordsv2()
1513 v2records = ms._readrecordsv2()
1514 order = 'LOml'
1514 order = 'LOml'
1515 def key(r):
1515 def key(r):
1516 idx = order.find(r[0])
1516 idx = order.find(r[0])
1517 if idx == -1:
1517 if idx == -1:
1518 return (1, r[1])
1518 return (1, r[1])
1519 else:
1519 else:
1520 return (0, idx)
1520 return (0, idx)
1521 v1records.sort(key=key)
1521 v1records.sort(key=key)
1522 v2records.sort(key=key)
1522 v2records.sort(key=key)
1523
1523
1524 if not v1records and not v2records:
1524 if not v1records and not v2records:
1525 ui.write(('no merge state found\n'))
1525 ui.write(('no merge state found\n'))
1526 elif not v2records:
1526 elif not v2records:
1527 ui.note(('no version 2 merge state\n'))
1527 ui.note(('no version 2 merge state\n'))
1528 printrecords(1)
1528 printrecords(1)
1529 elif ms._v1v2match(v1records, v2records):
1529 elif ms._v1v2match(v1records, v2records):
1530 ui.note(('v1 and v2 states match: using v2\n'))
1530 ui.note(('v1 and v2 states match: using v2\n'))
1531 printrecords(2)
1531 printrecords(2)
1532 else:
1532 else:
1533 ui.note(('v1 and v2 states mismatch: using v1\n'))
1533 ui.note(('v1 and v2 states mismatch: using v1\n'))
1534 printrecords(1)
1534 printrecords(1)
1535 if ui.verbose:
1535 if ui.verbose:
1536 printrecords(2)
1536 printrecords(2)
1537
1537
1538 @command('debugnamecomplete', [], _('NAME...'))
1538 @command('debugnamecomplete', [], _('NAME...'))
1539 def debugnamecomplete(ui, repo, *args):
1539 def debugnamecomplete(ui, repo, *args):
1540 '''complete "names" - tags, open branch names, bookmark names'''
1540 '''complete "names" - tags, open branch names, bookmark names'''
1541
1541
1542 names = set()
1542 names = set()
1543 # since we previously only listed open branches, we will handle that
1543 # since we previously only listed open branches, we will handle that
1544 # specially (after this for loop)
1544 # specially (after this for loop)
1545 for name, ns in repo.names.iteritems():
1545 for name, ns in repo.names.iteritems():
1546 if name != 'branches':
1546 if name != 'branches':
1547 names.update(ns.listnames(repo))
1547 names.update(ns.listnames(repo))
1548 names.update(tag for (tag, heads, tip, closed)
1548 names.update(tag for (tag, heads, tip, closed)
1549 in repo.branchmap().iterbranches() if not closed)
1549 in repo.branchmap().iterbranches() if not closed)
1550 completions = set()
1550 completions = set()
1551 if not args:
1551 if not args:
1552 args = ['']
1552 args = ['']
1553 for a in args:
1553 for a in args:
1554 completions.update(n for n in names if n.startswith(a))
1554 completions.update(n for n in names if n.startswith(a))
1555 ui.write('\n'.join(sorted(completions)))
1555 ui.write('\n'.join(sorted(completions)))
1556 ui.write('\n')
1556 ui.write('\n')
1557
1557
1558 @command('debugobsolete',
1558 @command('debugobsolete',
1559 [('', 'flags', 0, _('markers flag')),
1559 [('', 'flags', 0, _('markers flag')),
1560 ('', 'record-parents', False,
1560 ('', 'record-parents', False,
1561 _('record parent information for the precursor')),
1561 _('record parent information for the precursor')),
1562 ('r', 'rev', [], _('display markers relevant to REV')),
1562 ('r', 'rev', [], _('display markers relevant to REV')),
1563 ('', 'exclusive', False, _('restrict display to markers only '
1563 ('', 'exclusive', False, _('restrict display to markers only '
1564 'relevant to REV')),
1564 'relevant to REV')),
1565 ('', 'index', False, _('display index of the marker')),
1565 ('', 'index', False, _('display index of the marker')),
1566 ('', 'delete', [], _('delete markers specified by indices')),
1566 ('', 'delete', [], _('delete markers specified by indices')),
1567 ] + cmdutil.commitopts2 + cmdutil.formatteropts,
1567 ] + cmdutil.commitopts2 + cmdutil.formatteropts,
1568 _('[OBSOLETED [REPLACEMENT ...]]'))
1568 _('[OBSOLETED [REPLACEMENT ...]]'))
1569 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
1569 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
1570 """create arbitrary obsolete marker
1570 """create arbitrary obsolete marker
1571
1571
1572 With no arguments, displays the list of obsolescence markers."""
1572 With no arguments, displays the list of obsolescence markers."""
1573
1573
1574 opts = pycompat.byteskwargs(opts)
1574 opts = pycompat.byteskwargs(opts)
1575
1575
1576 def parsenodeid(s):
1576 def parsenodeid(s):
1577 try:
1577 try:
1578 # We do not use revsingle/revrange functions here to accept
1578 # We do not use revsingle/revrange functions here to accept
1579 # arbitrary node identifiers, possibly not present in the
1579 # arbitrary node identifiers, possibly not present in the
1580 # local repository.
1580 # local repository.
1581 n = bin(s)
1581 n = bin(s)
1582 if len(n) != len(nullid):
1582 if len(n) != len(nullid):
1583 raise TypeError()
1583 raise TypeError()
1584 return n
1584 return n
1585 except TypeError:
1585 except TypeError:
1586 raise error.Abort('changeset references must be full hexadecimal '
1586 raise error.Abort('changeset references must be full hexadecimal '
1587 'node identifiers')
1587 'node identifiers')
1588
1588
1589 if opts.get('delete'):
1589 if opts.get('delete'):
1590 indices = []
1590 indices = []
1591 for v in opts.get('delete'):
1591 for v in opts.get('delete'):
1592 try:
1592 try:
1593 indices.append(int(v))
1593 indices.append(int(v))
1594 except ValueError:
1594 except ValueError:
1595 raise error.Abort(_('invalid index value: %r') % v,
1595 raise error.Abort(_('invalid index value: %r') % v,
1596 hint=_('use integers for indices'))
1596 hint=_('use integers for indices'))
1597
1597
1598 if repo.currenttransaction():
1598 if repo.currenttransaction():
1599 raise error.Abort(_('cannot delete obsmarkers in the middle '
1599 raise error.Abort(_('cannot delete obsmarkers in the middle '
1600 'of transaction.'))
1600 'of transaction.'))
1601
1601
1602 with repo.lock():
1602 with repo.lock():
1603 n = repair.deleteobsmarkers(repo.obsstore, indices)
1603 n = repair.deleteobsmarkers(repo.obsstore, indices)
1604 ui.write(_('deleted %i obsolescence markers\n') % n)
1604 ui.write(_('deleted %i obsolescence markers\n') % n)
1605
1605
1606 return
1606 return
1607
1607
1608 if precursor is not None:
1608 if precursor is not None:
1609 if opts['rev']:
1609 if opts['rev']:
1610 raise error.Abort('cannot select revision when creating marker')
1610 raise error.Abort('cannot select revision when creating marker')
1611 metadata = {}
1611 metadata = {}
1612 metadata['user'] = opts['user'] or ui.username()
1612 metadata['user'] = opts['user'] or ui.username()
1613 succs = tuple(parsenodeid(succ) for succ in successors)
1613 succs = tuple(parsenodeid(succ) for succ in successors)
1614 l = repo.lock()
1614 l = repo.lock()
1615 try:
1615 try:
1616 tr = repo.transaction('debugobsolete')
1616 tr = repo.transaction('debugobsolete')
1617 try:
1617 try:
1618 date = opts.get('date')
1618 date = opts.get('date')
1619 if date:
1619 if date:
1620 date = dateutil.parsedate(date)
1620 date = dateutil.parsedate(date)
1621 else:
1621 else:
1622 date = None
1622 date = None
1623 prec = parsenodeid(precursor)
1623 prec = parsenodeid(precursor)
1624 parents = None
1624 parents = None
1625 if opts['record_parents']:
1625 if opts['record_parents']:
1626 if prec not in repo.unfiltered():
1626 if prec not in repo.unfiltered():
1627 raise error.Abort('cannot used --record-parents on '
1627 raise error.Abort('cannot used --record-parents on '
1628 'unknown changesets')
1628 'unknown changesets')
1629 parents = repo.unfiltered()[prec].parents()
1629 parents = repo.unfiltered()[prec].parents()
1630 parents = tuple(p.node() for p in parents)
1630 parents = tuple(p.node() for p in parents)
1631 repo.obsstore.create(tr, prec, succs, opts['flags'],
1631 repo.obsstore.create(tr, prec, succs, opts['flags'],
1632 parents=parents, date=date,
1632 parents=parents, date=date,
1633 metadata=metadata, ui=ui)
1633 metadata=metadata, ui=ui)
1634 tr.close()
1634 tr.close()
1635 except ValueError as exc:
1635 except ValueError as exc:
1636 raise error.Abort(_('bad obsmarker input: %s') %
1636 raise error.Abort(_('bad obsmarker input: %s') %
1637 pycompat.bytestr(exc))
1637 pycompat.bytestr(exc))
1638 finally:
1638 finally:
1639 tr.release()
1639 tr.release()
1640 finally:
1640 finally:
1641 l.release()
1641 l.release()
1642 else:
1642 else:
1643 if opts['rev']:
1643 if opts['rev']:
1644 revs = scmutil.revrange(repo, opts['rev'])
1644 revs = scmutil.revrange(repo, opts['rev'])
1645 nodes = [repo[r].node() for r in revs]
1645 nodes = [repo[r].node() for r in revs]
1646 markers = list(obsutil.getmarkers(repo, nodes=nodes,
1646 markers = list(obsutil.getmarkers(repo, nodes=nodes,
1647 exclusive=opts['exclusive']))
1647 exclusive=opts['exclusive']))
1648 markers.sort(key=lambda x: x._data)
1648 markers.sort(key=lambda x: x._data)
1649 else:
1649 else:
1650 markers = obsutil.getmarkers(repo)
1650 markers = obsutil.getmarkers(repo)
1651
1651
1652 markerstoiter = markers
1652 markerstoiter = markers
1653 isrelevant = lambda m: True
1653 isrelevant = lambda m: True
1654 if opts.get('rev') and opts.get('index'):
1654 if opts.get('rev') and opts.get('index'):
1655 markerstoiter = obsutil.getmarkers(repo)
1655 markerstoiter = obsutil.getmarkers(repo)
1656 markerset = set(markers)
1656 markerset = set(markers)
1657 isrelevant = lambda m: m in markerset
1657 isrelevant = lambda m: m in markerset
1658
1658
1659 fm = ui.formatter('debugobsolete', opts)
1659 fm = ui.formatter('debugobsolete', opts)
1660 for i, m in enumerate(markerstoiter):
1660 for i, m in enumerate(markerstoiter):
1661 if not isrelevant(m):
1661 if not isrelevant(m):
1662 # marker can be irrelevant when we're iterating over a set
1662 # marker can be irrelevant when we're iterating over a set
1663 # of markers (markerstoiter) which is bigger than the set
1663 # of markers (markerstoiter) which is bigger than the set
1664 # of markers we want to display (markers)
1664 # of markers we want to display (markers)
1665 # this can happen if both --index and --rev options are
1665 # this can happen if both --index and --rev options are
1666 # provided and thus we need to iterate over all of the markers
1666 # provided and thus we need to iterate over all of the markers
1667 # to get the correct indices, but only display the ones that
1667 # to get the correct indices, but only display the ones that
1668 # are relevant to --rev value
1668 # are relevant to --rev value
1669 continue
1669 continue
1670 fm.startitem()
1670 fm.startitem()
1671 ind = i if opts.get('index') else None
1671 ind = i if opts.get('index') else None
1672 cmdutil.showmarker(fm, m, index=ind)
1672 cmdutil.showmarker(fm, m, index=ind)
1673 fm.end()
1673 fm.end()
1674
1674
1675 @command('debugpathcomplete',
1675 @command('debugpathcomplete',
1676 [('f', 'full', None, _('complete an entire path')),
1676 [('f', 'full', None, _('complete an entire path')),
1677 ('n', 'normal', None, _('show only normal files')),
1677 ('n', 'normal', None, _('show only normal files')),
1678 ('a', 'added', None, _('show only added files')),
1678 ('a', 'added', None, _('show only added files')),
1679 ('r', 'removed', None, _('show only removed files'))],
1679 ('r', 'removed', None, _('show only removed files'))],
1680 _('FILESPEC...'))
1680 _('FILESPEC...'))
1681 def debugpathcomplete(ui, repo, *specs, **opts):
1681 def debugpathcomplete(ui, repo, *specs, **opts):
1682 '''complete part or all of a tracked path
1682 '''complete part or all of a tracked path
1683
1683
1684 This command supports shells that offer path name completion. It
1684 This command supports shells that offer path name completion. It
1685 currently completes only files already known to the dirstate.
1685 currently completes only files already known to the dirstate.
1686
1686
1687 Completion extends only to the next path segment unless
1687 Completion extends only to the next path segment unless
1688 --full is specified, in which case entire paths are used.'''
1688 --full is specified, in which case entire paths are used.'''
1689
1689
1690 def complete(path, acceptable):
1690 def complete(path, acceptable):
1691 dirstate = repo.dirstate
1691 dirstate = repo.dirstate
1692 spec = os.path.normpath(os.path.join(pycompat.getcwd(), path))
1692 spec = os.path.normpath(os.path.join(pycompat.getcwd(), path))
1693 rootdir = repo.root + pycompat.ossep
1693 rootdir = repo.root + pycompat.ossep
1694 if spec != repo.root and not spec.startswith(rootdir):
1694 if spec != repo.root and not spec.startswith(rootdir):
1695 return [], []
1695 return [], []
1696 if os.path.isdir(spec):
1696 if os.path.isdir(spec):
1697 spec += '/'
1697 spec += '/'
1698 spec = spec[len(rootdir):]
1698 spec = spec[len(rootdir):]
1699 fixpaths = pycompat.ossep != '/'
1699 fixpaths = pycompat.ossep != '/'
1700 if fixpaths:
1700 if fixpaths:
1701 spec = spec.replace(pycompat.ossep, '/')
1701 spec = spec.replace(pycompat.ossep, '/')
1702 speclen = len(spec)
1702 speclen = len(spec)
1703 fullpaths = opts[r'full']
1703 fullpaths = opts[r'full']
1704 files, dirs = set(), set()
1704 files, dirs = set(), set()
1705 adddir, addfile = dirs.add, files.add
1705 adddir, addfile = dirs.add, files.add
1706 for f, st in dirstate.iteritems():
1706 for f, st in dirstate.iteritems():
1707 if f.startswith(spec) and st[0] in acceptable:
1707 if f.startswith(spec) and st[0] in acceptable:
1708 if fixpaths:
1708 if fixpaths:
1709 f = f.replace('/', pycompat.ossep)
1709 f = f.replace('/', pycompat.ossep)
1710 if fullpaths:
1710 if fullpaths:
1711 addfile(f)
1711 addfile(f)
1712 continue
1712 continue
1713 s = f.find(pycompat.ossep, speclen)
1713 s = f.find(pycompat.ossep, speclen)
1714 if s >= 0:
1714 if s >= 0:
1715 adddir(f[:s])
1715 adddir(f[:s])
1716 else:
1716 else:
1717 addfile(f)
1717 addfile(f)
1718 return files, dirs
1718 return files, dirs
1719
1719
1720 acceptable = ''
1720 acceptable = ''
1721 if opts[r'normal']:
1721 if opts[r'normal']:
1722 acceptable += 'nm'
1722 acceptable += 'nm'
1723 if opts[r'added']:
1723 if opts[r'added']:
1724 acceptable += 'a'
1724 acceptable += 'a'
1725 if opts[r'removed']:
1725 if opts[r'removed']:
1726 acceptable += 'r'
1726 acceptable += 'r'
1727 cwd = repo.getcwd()
1727 cwd = repo.getcwd()
1728 if not specs:
1728 if not specs:
1729 specs = ['.']
1729 specs = ['.']
1730
1730
1731 files, dirs = set(), set()
1731 files, dirs = set(), set()
1732 for spec in specs:
1732 for spec in specs:
1733 f, d = complete(spec, acceptable or 'nmar')
1733 f, d = complete(spec, acceptable or 'nmar')
1734 files.update(f)
1734 files.update(f)
1735 dirs.update(d)
1735 dirs.update(d)
1736 files.update(dirs)
1736 files.update(dirs)
1737 ui.write('\n'.join(repo.pathto(p, cwd) for p in sorted(files)))
1737 ui.write('\n'.join(repo.pathto(p, cwd) for p in sorted(files)))
1738 ui.write('\n')
1738 ui.write('\n')
1739
1739
1740 @command('debugpeer', [], _('PATH'), norepo=True)
1740 @command('debugpeer', [], _('PATH'), norepo=True)
1741 def debugpeer(ui, path):
1741 def debugpeer(ui, path):
1742 """establish a connection to a peer repository"""
1742 """establish a connection to a peer repository"""
1743 # Always enable peer request logging. Requires --debug to display
1743 # Always enable peer request logging. Requires --debug to display
1744 # though.
1744 # though.
1745 overrides = {
1745 overrides = {
1746 ('devel', 'debug.peer-request'): True,
1746 ('devel', 'debug.peer-request'): True,
1747 }
1747 }
1748
1748
1749 with ui.configoverride(overrides):
1749 with ui.configoverride(overrides):
1750 peer = hg.peer(ui, {}, path)
1750 peer = hg.peer(ui, {}, path)
1751
1751
1752 local = peer.local() is not None
1752 local = peer.local() is not None
1753 canpush = peer.canpush()
1753 canpush = peer.canpush()
1754
1754
1755 ui.write(_('url: %s\n') % peer.url())
1755 ui.write(_('url: %s\n') % peer.url())
1756 ui.write(_('local: %s\n') % (_('yes') if local else _('no')))
1756 ui.write(_('local: %s\n') % (_('yes') if local else _('no')))
1757 ui.write(_('pushable: %s\n') % (_('yes') if canpush else _('no')))
1757 ui.write(_('pushable: %s\n') % (_('yes') if canpush else _('no')))
1758
1758
1759 @command('debugpickmergetool',
1759 @command('debugpickmergetool',
1760 [('r', 'rev', '', _('check for files in this revision'), _('REV')),
1760 [('r', 'rev', '', _('check for files in this revision'), _('REV')),
1761 ('', 'changedelete', None, _('emulate merging change and delete')),
1761 ('', 'changedelete', None, _('emulate merging change and delete')),
1762 ] + cmdutil.walkopts + cmdutil.mergetoolopts,
1762 ] + cmdutil.walkopts + cmdutil.mergetoolopts,
1763 _('[PATTERN]...'),
1763 _('[PATTERN]...'),
1764 inferrepo=True)
1764 inferrepo=True)
1765 def debugpickmergetool(ui, repo, *pats, **opts):
1765 def debugpickmergetool(ui, repo, *pats, **opts):
1766 """examine which merge tool is chosen for specified file
1766 """examine which merge tool is chosen for specified file
1767
1767
1768 As described in :hg:`help merge-tools`, Mercurial examines
1768 As described in :hg:`help merge-tools`, Mercurial examines
1769 configurations below in this order to decide which merge tool is
1769 configurations below in this order to decide which merge tool is
1770 chosen for specified file.
1770 chosen for specified file.
1771
1771
1772 1. ``--tool`` option
1772 1. ``--tool`` option
1773 2. ``HGMERGE`` environment variable
1773 2. ``HGMERGE`` environment variable
1774 3. configurations in ``merge-patterns`` section
1774 3. configurations in ``merge-patterns`` section
1775 4. configuration of ``ui.merge``
1775 4. configuration of ``ui.merge``
1776 5. configurations in ``merge-tools`` section
1776 5. configurations in ``merge-tools`` section
1777 6. ``hgmerge`` tool (for historical reason only)
1777 6. ``hgmerge`` tool (for historical reason only)
1778 7. default tool for fallback (``:merge`` or ``:prompt``)
1778 7. default tool for fallback (``:merge`` or ``:prompt``)
1779
1779
1780 This command writes out examination result in the style below::
1780 This command writes out examination result in the style below::
1781
1781
1782 FILE = MERGETOOL
1782 FILE = MERGETOOL
1783
1783
1784 By default, all files known in the first parent context of the
1784 By default, all files known in the first parent context of the
1785 working directory are examined. Use file patterns and/or -I/-X
1785 working directory are examined. Use file patterns and/or -I/-X
1786 options to limit target files. -r/--rev is also useful to examine
1786 options to limit target files. -r/--rev is also useful to examine
1787 files in another context without actual updating to it.
1787 files in another context without actual updating to it.
1788
1788
1789 With --debug, this command shows warning messages while matching
1789 With --debug, this command shows warning messages while matching
1790 against ``merge-patterns`` and so on, too. It is recommended to
1790 against ``merge-patterns`` and so on, too. It is recommended to
1791 use this option with explicit file patterns and/or -I/-X options,
1791 use this option with explicit file patterns and/or -I/-X options,
1792 because this option increases amount of output per file according
1792 because this option increases amount of output per file according
1793 to configurations in hgrc.
1793 to configurations in hgrc.
1794
1794
1795 With -v/--verbose, this command shows configurations below at
1795 With -v/--verbose, this command shows configurations below at
1796 first (only if specified).
1796 first (only if specified).
1797
1797
1798 - ``--tool`` option
1798 - ``--tool`` option
1799 - ``HGMERGE`` environment variable
1799 - ``HGMERGE`` environment variable
1800 - configuration of ``ui.merge``
1800 - configuration of ``ui.merge``
1801
1801
1802 If merge tool is chosen before matching against
1802 If merge tool is chosen before matching against
1803 ``merge-patterns``, this command can't show any helpful
1803 ``merge-patterns``, this command can't show any helpful
1804 information, even with --debug. In such case, information above is
1804 information, even with --debug. In such case, information above is
1805 useful to know why a merge tool is chosen.
1805 useful to know why a merge tool is chosen.
1806 """
1806 """
1807 opts = pycompat.byteskwargs(opts)
1807 opts = pycompat.byteskwargs(opts)
1808 overrides = {}
1808 overrides = {}
1809 if opts['tool']:
1809 if opts['tool']:
1810 overrides[('ui', 'forcemerge')] = opts['tool']
1810 overrides[('ui', 'forcemerge')] = opts['tool']
1811 ui.note(('with --tool %r\n') % (pycompat.bytestr(opts['tool'])))
1811 ui.note(('with --tool %r\n') % (pycompat.bytestr(opts['tool'])))
1812
1812
1813 with ui.configoverride(overrides, 'debugmergepatterns'):
1813 with ui.configoverride(overrides, 'debugmergepatterns'):
1814 hgmerge = encoding.environ.get("HGMERGE")
1814 hgmerge = encoding.environ.get("HGMERGE")
1815 if hgmerge is not None:
1815 if hgmerge is not None:
1816 ui.note(('with HGMERGE=%r\n') % (pycompat.bytestr(hgmerge)))
1816 ui.note(('with HGMERGE=%r\n') % (pycompat.bytestr(hgmerge)))
1817 uimerge = ui.config("ui", "merge")
1817 uimerge = ui.config("ui", "merge")
1818 if uimerge:
1818 if uimerge:
1819 ui.note(('with ui.merge=%r\n') % (pycompat.bytestr(uimerge)))
1819 ui.note(('with ui.merge=%r\n') % (pycompat.bytestr(uimerge)))
1820
1820
1821 ctx = scmutil.revsingle(repo, opts.get('rev'))
1821 ctx = scmutil.revsingle(repo, opts.get('rev'))
1822 m = scmutil.match(ctx, pats, opts)
1822 m = scmutil.match(ctx, pats, opts)
1823 changedelete = opts['changedelete']
1823 changedelete = opts['changedelete']
1824 for path in ctx.walk(m):
1824 for path in ctx.walk(m):
1825 fctx = ctx[path]
1825 fctx = ctx[path]
1826 try:
1826 try:
1827 if not ui.debugflag:
1827 if not ui.debugflag:
1828 ui.pushbuffer(error=True)
1828 ui.pushbuffer(error=True)
1829 tool, toolpath = filemerge._picktool(repo, ui, path,
1829 tool, toolpath = filemerge._picktool(repo, ui, path,
1830 fctx.isbinary(),
1830 fctx.isbinary(),
1831 'l' in fctx.flags(),
1831 'l' in fctx.flags(),
1832 changedelete)
1832 changedelete)
1833 finally:
1833 finally:
1834 if not ui.debugflag:
1834 if not ui.debugflag:
1835 ui.popbuffer()
1835 ui.popbuffer()
1836 ui.write(('%s = %s\n') % (path, tool))
1836 ui.write(('%s = %s\n') % (path, tool))
1837
1837
1838 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'), norepo=True)
1838 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'), norepo=True)
1839 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
1839 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
1840 '''access the pushkey key/value protocol
1840 '''access the pushkey key/value protocol
1841
1841
1842 With two args, list the keys in the given namespace.
1842 With two args, list the keys in the given namespace.
1843
1843
1844 With five args, set a key to new if it currently is set to old.
1844 With five args, set a key to new if it currently is set to old.
1845 Reports success or failure.
1845 Reports success or failure.
1846 '''
1846 '''
1847
1847
1848 target = hg.peer(ui, {}, repopath)
1848 target = hg.peer(ui, {}, repopath)
1849 if keyinfo:
1849 if keyinfo:
1850 key, old, new = keyinfo
1850 key, old, new = keyinfo
1851 with target.commandexecutor() as e:
1851 with target.commandexecutor() as e:
1852 r = e.callcommand('pushkey', {
1852 r = e.callcommand('pushkey', {
1853 'namespace': namespace,
1853 'namespace': namespace,
1854 'key': key,
1854 'key': key,
1855 'old': old,
1855 'old': old,
1856 'new': new,
1856 'new': new,
1857 }).result()
1857 }).result()
1858
1858
1859 ui.status(pycompat.bytestr(r) + '\n')
1859 ui.status(pycompat.bytestr(r) + '\n')
1860 return not r
1860 return not r
1861 else:
1861 else:
1862 for k, v in sorted(target.listkeys(namespace).iteritems()):
1862 for k, v in sorted(target.listkeys(namespace).iteritems()):
1863 ui.write("%s\t%s\n" % (stringutil.escapestr(k),
1863 ui.write("%s\t%s\n" % (stringutil.escapestr(k),
1864 stringutil.escapestr(v)))
1864 stringutil.escapestr(v)))
1865
1865
1866 @command('debugpvec', [], _('A B'))
1866 @command('debugpvec', [], _('A B'))
1867 def debugpvec(ui, repo, a, b=None):
1867 def debugpvec(ui, repo, a, b=None):
1868 ca = scmutil.revsingle(repo, a)
1868 ca = scmutil.revsingle(repo, a)
1869 cb = scmutil.revsingle(repo, b)
1869 cb = scmutil.revsingle(repo, b)
1870 pa = pvec.ctxpvec(ca)
1870 pa = pvec.ctxpvec(ca)
1871 pb = pvec.ctxpvec(cb)
1871 pb = pvec.ctxpvec(cb)
1872 if pa == pb:
1872 if pa == pb:
1873 rel = "="
1873 rel = "="
1874 elif pa > pb:
1874 elif pa > pb:
1875 rel = ">"
1875 rel = ">"
1876 elif pa < pb:
1876 elif pa < pb:
1877 rel = "<"
1877 rel = "<"
1878 elif pa | pb:
1878 elif pa | pb:
1879 rel = "|"
1879 rel = "|"
1880 ui.write(_("a: %s\n") % pa)
1880 ui.write(_("a: %s\n") % pa)
1881 ui.write(_("b: %s\n") % pb)
1881 ui.write(_("b: %s\n") % pb)
1882 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
1882 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
1883 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
1883 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
1884 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
1884 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
1885 pa.distance(pb), rel))
1885 pa.distance(pb), rel))
1886
1886
1887 @command('debugrebuilddirstate|debugrebuildstate',
1887 @command('debugrebuilddirstate|debugrebuildstate',
1888 [('r', 'rev', '', _('revision to rebuild to'), _('REV')),
1888 [('r', 'rev', '', _('revision to rebuild to'), _('REV')),
1889 ('', 'minimal', None, _('only rebuild files that are inconsistent with '
1889 ('', 'minimal', None, _('only rebuild files that are inconsistent with '
1890 'the working copy parent')),
1890 'the working copy parent')),
1891 ],
1891 ],
1892 _('[-r REV]'))
1892 _('[-r REV]'))
1893 def debugrebuilddirstate(ui, repo, rev, **opts):
1893 def debugrebuilddirstate(ui, repo, rev, **opts):
1894 """rebuild the dirstate as it would look like for the given revision
1894 """rebuild the dirstate as it would look like for the given revision
1895
1895
1896 If no revision is specified the first current parent will be used.
1896 If no revision is specified the first current parent will be used.
1897
1897
1898 The dirstate will be set to the files of the given revision.
1898 The dirstate will be set to the files of the given revision.
1899 The actual working directory content or existing dirstate
1899 The actual working directory content or existing dirstate
1900 information such as adds or removes is not considered.
1900 information such as adds or removes is not considered.
1901
1901
1902 ``minimal`` will only rebuild the dirstate status for files that claim to be
1902 ``minimal`` will only rebuild the dirstate status for files that claim to be
1903 tracked but are not in the parent manifest, or that exist in the parent
1903 tracked but are not in the parent manifest, or that exist in the parent
1904 manifest but are not in the dirstate. It will not change adds, removes, or
1904 manifest but are not in the dirstate. It will not change adds, removes, or
1905 modified files that are in the working copy parent.
1905 modified files that are in the working copy parent.
1906
1906
1907 One use of this command is to make the next :hg:`status` invocation
1907 One use of this command is to make the next :hg:`status` invocation
1908 check the actual file content.
1908 check the actual file content.
1909 """
1909 """
1910 ctx = scmutil.revsingle(repo, rev)
1910 ctx = scmutil.revsingle(repo, rev)
1911 with repo.wlock():
1911 with repo.wlock():
1912 dirstate = repo.dirstate
1912 dirstate = repo.dirstate
1913 changedfiles = None
1913 changedfiles = None
1914 # See command doc for what minimal does.
1914 # See command doc for what minimal does.
1915 if opts.get(r'minimal'):
1915 if opts.get(r'minimal'):
1916 manifestfiles = set(ctx.manifest().keys())
1916 manifestfiles = set(ctx.manifest().keys())
1917 dirstatefiles = set(dirstate)
1917 dirstatefiles = set(dirstate)
1918 manifestonly = manifestfiles - dirstatefiles
1918 manifestonly = manifestfiles - dirstatefiles
1919 dsonly = dirstatefiles - manifestfiles
1919 dsonly = dirstatefiles - manifestfiles
1920 dsnotadded = set(f for f in dsonly if dirstate[f] != 'a')
1920 dsnotadded = set(f for f in dsonly if dirstate[f] != 'a')
1921 changedfiles = manifestonly | dsnotadded
1921 changedfiles = manifestonly | dsnotadded
1922
1922
1923 dirstate.rebuild(ctx.node(), ctx.manifest(), changedfiles)
1923 dirstate.rebuild(ctx.node(), ctx.manifest(), changedfiles)
1924
1924
1925 @command('debugrebuildfncache', [], '')
1925 @command('debugrebuildfncache', [], '')
1926 def debugrebuildfncache(ui, repo):
1926 def debugrebuildfncache(ui, repo):
1927 """rebuild the fncache file"""
1927 """rebuild the fncache file"""
1928 repair.rebuildfncache(ui, repo)
1928 repair.rebuildfncache(ui, repo)
1929
1929
1930 @command('debugrename',
1930 @command('debugrename',
1931 [('r', 'rev', '', _('revision to debug'), _('REV'))],
1931 [('r', 'rev', '', _('revision to debug'), _('REV'))],
1932 _('[-r REV] FILE'))
1932 _('[-r REV] FILE'))
1933 def debugrename(ui, repo, file1, *pats, **opts):
1933 def debugrename(ui, repo, file1, *pats, **opts):
1934 """dump rename information"""
1934 """dump rename information"""
1935
1935
1936 opts = pycompat.byteskwargs(opts)
1936 opts = pycompat.byteskwargs(opts)
1937 ctx = scmutil.revsingle(repo, opts.get('rev'))
1937 ctx = scmutil.revsingle(repo, opts.get('rev'))
1938 m = scmutil.match(ctx, (file1,) + pats, opts)
1938 m = scmutil.match(ctx, (file1,) + pats, opts)
1939 for abs in ctx.walk(m):
1939 for abs in ctx.walk(m):
1940 fctx = ctx[abs]
1940 fctx = ctx[abs]
1941 o = fctx.filelog().renamed(fctx.filenode())
1941 o = fctx.filelog().renamed(fctx.filenode())
1942 rel = m.rel(abs)
1942 rel = m.rel(abs)
1943 if o:
1943 if o:
1944 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1944 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1945 else:
1945 else:
1946 ui.write(_("%s not renamed\n") % rel)
1946 ui.write(_("%s not renamed\n") % rel)
1947
1947
1948 @command('debugrevlog', cmdutil.debugrevlogopts +
1948 @command('debugrevlog', cmdutil.debugrevlogopts +
1949 [('d', 'dump', False, _('dump index data'))],
1949 [('d', 'dump', False, _('dump index data'))],
1950 _('-c|-m|FILE'),
1950 _('-c|-m|FILE'),
1951 optionalrepo=True)
1951 optionalrepo=True)
1952 def debugrevlog(ui, repo, file_=None, **opts):
1952 def debugrevlog(ui, repo, file_=None, **opts):
1953 """show data and statistics about a revlog"""
1953 """show data and statistics about a revlog"""
1954 opts = pycompat.byteskwargs(opts)
1954 opts = pycompat.byteskwargs(opts)
1955 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
1955 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
1956
1956
1957 if opts.get("dump"):
1957 if opts.get("dump"):
1958 numrevs = len(r)
1958 numrevs = len(r)
1959 ui.write(("# rev p1rev p2rev start end deltastart base p1 p2"
1959 ui.write(("# rev p1rev p2rev start end deltastart base p1 p2"
1960 " rawsize totalsize compression heads chainlen\n"))
1960 " rawsize totalsize compression heads chainlen\n"))
1961 ts = 0
1961 ts = 0
1962 heads = set()
1962 heads = set()
1963
1963
1964 for rev in xrange(numrevs):
1964 for rev in xrange(numrevs):
1965 dbase = r.deltaparent(rev)
1965 dbase = r.deltaparent(rev)
1966 if dbase == -1:
1966 if dbase == -1:
1967 dbase = rev
1967 dbase = rev
1968 cbase = r.chainbase(rev)
1968 cbase = r.chainbase(rev)
1969 clen = r.chainlen(rev)
1969 clen = r.chainlen(rev)
1970 p1, p2 = r.parentrevs(rev)
1970 p1, p2 = r.parentrevs(rev)
1971 rs = r.rawsize(rev)
1971 rs = r.rawsize(rev)
1972 ts = ts + rs
1972 ts = ts + rs
1973 heads -= set(r.parentrevs(rev))
1973 heads -= set(r.parentrevs(rev))
1974 heads.add(rev)
1974 heads.add(rev)
1975 try:
1975 try:
1976 compression = ts / r.end(rev)
1976 compression = ts / r.end(rev)
1977 except ZeroDivisionError:
1977 except ZeroDivisionError:
1978 compression = 0
1978 compression = 0
1979 ui.write("%5d %5d %5d %5d %5d %10d %4d %4d %4d %7d %9d "
1979 ui.write("%5d %5d %5d %5d %5d %10d %4d %4d %4d %7d %9d "
1980 "%11d %5d %8d\n" %
1980 "%11d %5d %8d\n" %
1981 (rev, p1, p2, r.start(rev), r.end(rev),
1981 (rev, p1, p2, r.start(rev), r.end(rev),
1982 r.start(dbase), r.start(cbase),
1982 r.start(dbase), r.start(cbase),
1983 r.start(p1), r.start(p2),
1983 r.start(p1), r.start(p2),
1984 rs, ts, compression, len(heads), clen))
1984 rs, ts, compression, len(heads), clen))
1985 return 0
1985 return 0
1986
1986
1987 v = r.version
1987 v = r.version
1988 format = v & 0xFFFF
1988 format = v & 0xFFFF
1989 flags = []
1989 flags = []
1990 gdelta = False
1990 gdelta = False
1991 if v & revlog.FLAG_INLINE_DATA:
1991 if v & revlog.FLAG_INLINE_DATA:
1992 flags.append('inline')
1992 flags.append('inline')
1993 if v & revlog.FLAG_GENERALDELTA:
1993 if v & revlog.FLAG_GENERALDELTA:
1994 gdelta = True
1994 gdelta = True
1995 flags.append('generaldelta')
1995 flags.append('generaldelta')
1996 if not flags:
1996 if not flags:
1997 flags = ['(none)']
1997 flags = ['(none)']
1998
1998
1999 nummerges = 0
1999 nummerges = 0
2000 numfull = 0
2000 numfull = 0
2001 numprev = 0
2001 numprev = 0
2002 nump1 = 0
2002 nump1 = 0
2003 nump2 = 0
2003 nump2 = 0
2004 numother = 0
2004 numother = 0
2005 nump1prev = 0
2005 nump1prev = 0
2006 nump2prev = 0
2006 nump2prev = 0
2007 chainlengths = []
2007 chainlengths = []
2008 chainbases = []
2008 chainbases = []
2009 chainspans = []
2009 chainspans = []
2010
2010
2011 datasize = [None, 0, 0]
2011 datasize = [None, 0, 0]
2012 fullsize = [None, 0, 0]
2012 fullsize = [None, 0, 0]
2013 deltasize = [None, 0, 0]
2013 deltasize = [None, 0, 0]
2014 chunktypecounts = {}
2014 chunktypecounts = {}
2015 chunktypesizes = {}
2015 chunktypesizes = {}
2016
2016
2017 def addsize(size, l):
2017 def addsize(size, l):
2018 if l[0] is None or size < l[0]:
2018 if l[0] is None or size < l[0]:
2019 l[0] = size
2019 l[0] = size
2020 if size > l[1]:
2020 if size > l[1]:
2021 l[1] = size
2021 l[1] = size
2022 l[2] += size
2022 l[2] += size
2023
2023
2024 numrevs = len(r)
2024 numrevs = len(r)
2025 for rev in xrange(numrevs):
2025 for rev in xrange(numrevs):
2026 p1, p2 = r.parentrevs(rev)
2026 p1, p2 = r.parentrevs(rev)
2027 delta = r.deltaparent(rev)
2027 delta = r.deltaparent(rev)
2028 if format > 0:
2028 if format > 0:
2029 addsize(r.rawsize(rev), datasize)
2029 addsize(r.rawsize(rev), datasize)
2030 if p2 != nullrev:
2030 if p2 != nullrev:
2031 nummerges += 1
2031 nummerges += 1
2032 size = r.length(rev)
2032 size = r.length(rev)
2033 if delta == nullrev:
2033 if delta == nullrev:
2034 chainlengths.append(0)
2034 chainlengths.append(0)
2035 chainbases.append(r.start(rev))
2035 chainbases.append(r.start(rev))
2036 chainspans.append(size)
2036 chainspans.append(size)
2037 numfull += 1
2037 numfull += 1
2038 addsize(size, fullsize)
2038 addsize(size, fullsize)
2039 else:
2039 else:
2040 chainlengths.append(chainlengths[delta] + 1)
2040 chainlengths.append(chainlengths[delta] + 1)
2041 baseaddr = chainbases[delta]
2041 baseaddr = chainbases[delta]
2042 revaddr = r.start(rev)
2042 revaddr = r.start(rev)
2043 chainbases.append(baseaddr)
2043 chainbases.append(baseaddr)
2044 chainspans.append((revaddr - baseaddr) + size)
2044 chainspans.append((revaddr - baseaddr) + size)
2045 addsize(size, deltasize)
2045 addsize(size, deltasize)
2046 if delta == rev - 1:
2046 if delta == rev - 1:
2047 numprev += 1
2047 numprev += 1
2048 if delta == p1:
2048 if delta == p1:
2049 nump1prev += 1
2049 nump1prev += 1
2050 elif delta == p2:
2050 elif delta == p2:
2051 nump2prev += 1
2051 nump2prev += 1
2052 elif delta == p1:
2052 elif delta == p1:
2053 nump1 += 1
2053 nump1 += 1
2054 elif delta == p2:
2054 elif delta == p2:
2055 nump2 += 1
2055 nump2 += 1
2056 elif delta != nullrev:
2056 elif delta != nullrev:
2057 numother += 1
2057 numother += 1
2058
2058
2059 # Obtain data on the raw chunks in the revlog.
2059 # Obtain data on the raw chunks in the revlog.
2060 segment = r._getsegmentforrevs(rev, rev)[1]
2060 segment = r._getsegmentforrevs(rev, rev)[1]
2061 if segment:
2061 if segment:
2062 chunktype = bytes(segment[0:1])
2062 chunktype = bytes(segment[0:1])
2063 else:
2063 else:
2064 chunktype = 'empty'
2064 chunktype = 'empty'
2065
2065
2066 if chunktype not in chunktypecounts:
2066 if chunktype not in chunktypecounts:
2067 chunktypecounts[chunktype] = 0
2067 chunktypecounts[chunktype] = 0
2068 chunktypesizes[chunktype] = 0
2068 chunktypesizes[chunktype] = 0
2069
2069
2070 chunktypecounts[chunktype] += 1
2070 chunktypecounts[chunktype] += 1
2071 chunktypesizes[chunktype] += size
2071 chunktypesizes[chunktype] += size
2072
2072
2073 # Adjust size min value for empty cases
2073 # Adjust size min value for empty cases
2074 for size in (datasize, fullsize, deltasize):
2074 for size in (datasize, fullsize, deltasize):
2075 if size[0] is None:
2075 if size[0] is None:
2076 size[0] = 0
2076 size[0] = 0
2077
2077
2078 numdeltas = numrevs - numfull
2078 numdeltas = numrevs - numfull
2079 numoprev = numprev - nump1prev - nump2prev
2079 numoprev = numprev - nump1prev - nump2prev
2080 totalrawsize = datasize[2]
2080 totalrawsize = datasize[2]
2081 datasize[2] /= numrevs
2081 datasize[2] /= numrevs
2082 fulltotal = fullsize[2]
2082 fulltotal = fullsize[2]
2083 fullsize[2] /= numfull
2083 fullsize[2] /= numfull
2084 deltatotal = deltasize[2]
2084 deltatotal = deltasize[2]
2085 if numrevs - numfull > 0:
2085 if numrevs - numfull > 0:
2086 deltasize[2] /= numrevs - numfull
2086 deltasize[2] /= numrevs - numfull
2087 totalsize = fulltotal + deltatotal
2087 totalsize = fulltotal + deltatotal
2088 avgchainlen = sum(chainlengths) / numrevs
2088 avgchainlen = sum(chainlengths) / numrevs
2089 maxchainlen = max(chainlengths)
2089 maxchainlen = max(chainlengths)
2090 maxchainspan = max(chainspans)
2090 maxchainspan = max(chainspans)
2091 compratio = 1
2091 compratio = 1
2092 if totalsize:
2092 if totalsize:
2093 compratio = totalrawsize / totalsize
2093 compratio = totalrawsize / totalsize
2094
2094
2095 basedfmtstr = '%%%dd\n'
2095 basedfmtstr = '%%%dd\n'
2096 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2096 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2097
2097
2098 def dfmtstr(max):
2098 def dfmtstr(max):
2099 return basedfmtstr % len(str(max))
2099 return basedfmtstr % len(str(max))
2100 def pcfmtstr(max, padding=0):
2100 def pcfmtstr(max, padding=0):
2101 return basepcfmtstr % (len(str(max)), ' ' * padding)
2101 return basepcfmtstr % (len(str(max)), ' ' * padding)
2102
2102
2103 def pcfmt(value, total):
2103 def pcfmt(value, total):
2104 if total:
2104 if total:
2105 return (value, 100 * float(value) / total)
2105 return (value, 100 * float(value) / total)
2106 else:
2106 else:
2107 return value, 100.0
2107 return value, 100.0
2108
2108
2109 ui.write(('format : %d\n') % format)
2109 ui.write(('format : %d\n') % format)
2110 ui.write(('flags : %s\n') % ', '.join(flags))
2110 ui.write(('flags : %s\n') % ', '.join(flags))
2111
2111
2112 ui.write('\n')
2112 ui.write('\n')
2113 fmt = pcfmtstr(totalsize)
2113 fmt = pcfmtstr(totalsize)
2114 fmt2 = dfmtstr(totalsize)
2114 fmt2 = dfmtstr(totalsize)
2115 ui.write(('revisions : ') + fmt2 % numrevs)
2115 ui.write(('revisions : ') + fmt2 % numrevs)
2116 ui.write((' merges : ') + fmt % pcfmt(nummerges, numrevs))
2116 ui.write((' merges : ') + fmt % pcfmt(nummerges, numrevs))
2117 ui.write((' normal : ') + fmt % pcfmt(numrevs - nummerges, numrevs))
2117 ui.write((' normal : ') + fmt % pcfmt(numrevs - nummerges, numrevs))
2118 ui.write(('revisions : ') + fmt2 % numrevs)
2118 ui.write(('revisions : ') + fmt2 % numrevs)
2119 ui.write((' full : ') + fmt % pcfmt(numfull, numrevs))
2119 ui.write((' full : ') + fmt % pcfmt(numfull, numrevs))
2120 ui.write((' deltas : ') + fmt % pcfmt(numdeltas, numrevs))
2120 ui.write((' deltas : ') + fmt % pcfmt(numdeltas, numrevs))
2121 ui.write(('revision size : ') + fmt2 % totalsize)
2121 ui.write(('revision size : ') + fmt2 % totalsize)
2122 ui.write((' full : ') + fmt % pcfmt(fulltotal, totalsize))
2122 ui.write((' full : ') + fmt % pcfmt(fulltotal, totalsize))
2123 ui.write((' deltas : ') + fmt % pcfmt(deltatotal, totalsize))
2123 ui.write((' deltas : ') + fmt % pcfmt(deltatotal, totalsize))
2124
2124
2125 def fmtchunktype(chunktype):
2125 def fmtchunktype(chunktype):
2126 if chunktype == 'empty':
2126 if chunktype == 'empty':
2127 return ' %s : ' % chunktype
2127 return ' %s : ' % chunktype
2128 elif chunktype in pycompat.bytestr(string.ascii_letters):
2128 elif chunktype in pycompat.bytestr(string.ascii_letters):
2129 return ' 0x%s (%s) : ' % (hex(chunktype), chunktype)
2129 return ' 0x%s (%s) : ' % (hex(chunktype), chunktype)
2130 else:
2130 else:
2131 return ' 0x%s : ' % hex(chunktype)
2131 return ' 0x%s : ' % hex(chunktype)
2132
2132
2133 ui.write('\n')
2133 ui.write('\n')
2134 ui.write(('chunks : ') + fmt2 % numrevs)
2134 ui.write(('chunks : ') + fmt2 % numrevs)
2135 for chunktype in sorted(chunktypecounts):
2135 for chunktype in sorted(chunktypecounts):
2136 ui.write(fmtchunktype(chunktype))
2136 ui.write(fmtchunktype(chunktype))
2137 ui.write(fmt % pcfmt(chunktypecounts[chunktype], numrevs))
2137 ui.write(fmt % pcfmt(chunktypecounts[chunktype], numrevs))
2138 ui.write(('chunks size : ') + fmt2 % totalsize)
2138 ui.write(('chunks size : ') + fmt2 % totalsize)
2139 for chunktype in sorted(chunktypecounts):
2139 for chunktype in sorted(chunktypecounts):
2140 ui.write(fmtchunktype(chunktype))
2140 ui.write(fmtchunktype(chunktype))
2141 ui.write(fmt % pcfmt(chunktypesizes[chunktype], totalsize))
2141 ui.write(fmt % pcfmt(chunktypesizes[chunktype], totalsize))
2142
2142
2143 ui.write('\n')
2143 ui.write('\n')
2144 fmt = dfmtstr(max(avgchainlen, maxchainlen, maxchainspan, compratio))
2144 fmt = dfmtstr(max(avgchainlen, maxchainlen, maxchainspan, compratio))
2145 ui.write(('avg chain length : ') + fmt % avgchainlen)
2145 ui.write(('avg chain length : ') + fmt % avgchainlen)
2146 ui.write(('max chain length : ') + fmt % maxchainlen)
2146 ui.write(('max chain length : ') + fmt % maxchainlen)
2147 ui.write(('max chain reach : ') + fmt % maxchainspan)
2147 ui.write(('max chain reach : ') + fmt % maxchainspan)
2148 ui.write(('compression ratio : ') + fmt % compratio)
2148 ui.write(('compression ratio : ') + fmt % compratio)
2149
2149
2150 if format > 0:
2150 if format > 0:
2151 ui.write('\n')
2151 ui.write('\n')
2152 ui.write(('uncompressed data size (min/max/avg) : %d / %d / %d\n')
2152 ui.write(('uncompressed data size (min/max/avg) : %d / %d / %d\n')
2153 % tuple(datasize))
2153 % tuple(datasize))
2154 ui.write(('full revision size (min/max/avg) : %d / %d / %d\n')
2154 ui.write(('full revision size (min/max/avg) : %d / %d / %d\n')
2155 % tuple(fullsize))
2155 % tuple(fullsize))
2156 ui.write(('delta size (min/max/avg) : %d / %d / %d\n')
2156 ui.write(('delta size (min/max/avg) : %d / %d / %d\n')
2157 % tuple(deltasize))
2157 % tuple(deltasize))
2158
2158
2159 if numdeltas > 0:
2159 if numdeltas > 0:
2160 ui.write('\n')
2160 ui.write('\n')
2161 fmt = pcfmtstr(numdeltas)
2161 fmt = pcfmtstr(numdeltas)
2162 fmt2 = pcfmtstr(numdeltas, 4)
2162 fmt2 = pcfmtstr(numdeltas, 4)
2163 ui.write(('deltas against prev : ') + fmt % pcfmt(numprev, numdeltas))
2163 ui.write(('deltas against prev : ') + fmt % pcfmt(numprev, numdeltas))
2164 if numprev > 0:
2164 if numprev > 0:
2165 ui.write((' where prev = p1 : ') + fmt2 % pcfmt(nump1prev,
2165 ui.write((' where prev = p1 : ') + fmt2 % pcfmt(nump1prev,
2166 numprev))
2166 numprev))
2167 ui.write((' where prev = p2 : ') + fmt2 % pcfmt(nump2prev,
2167 ui.write((' where prev = p2 : ') + fmt2 % pcfmt(nump2prev,
2168 numprev))
2168 numprev))
2169 ui.write((' other : ') + fmt2 % pcfmt(numoprev,
2169 ui.write((' other : ') + fmt2 % pcfmt(numoprev,
2170 numprev))
2170 numprev))
2171 if gdelta:
2171 if gdelta:
2172 ui.write(('deltas against p1 : ')
2172 ui.write(('deltas against p1 : ')
2173 + fmt % pcfmt(nump1, numdeltas))
2173 + fmt % pcfmt(nump1, numdeltas))
2174 ui.write(('deltas against p2 : ')
2174 ui.write(('deltas against p2 : ')
2175 + fmt % pcfmt(nump2, numdeltas))
2175 + fmt % pcfmt(nump2, numdeltas))
2176 ui.write(('deltas against other : ') + fmt % pcfmt(numother,
2176 ui.write(('deltas against other : ') + fmt % pcfmt(numother,
2177 numdeltas))
2177 numdeltas))
2178
2178
2179 @command('debugrevspec',
2179 @command('debugrevspec',
2180 [('', 'optimize', None,
2180 [('', 'optimize', None,
2181 _('print parsed tree after optimizing (DEPRECATED)')),
2181 _('print parsed tree after optimizing (DEPRECATED)')),
2182 ('', 'show-revs', True, _('print list of result revisions (default)')),
2182 ('', 'show-revs', True, _('print list of result revisions (default)')),
2183 ('s', 'show-set', None, _('print internal representation of result set')),
2183 ('s', 'show-set', None, _('print internal representation of result set')),
2184 ('p', 'show-stage', [],
2184 ('p', 'show-stage', [],
2185 _('print parsed tree at the given stage'), _('NAME')),
2185 _('print parsed tree at the given stage'), _('NAME')),
2186 ('', 'no-optimized', False, _('evaluate tree without optimization')),
2186 ('', 'no-optimized', False, _('evaluate tree without optimization')),
2187 ('', 'verify-optimized', False, _('verify optimized result')),
2187 ('', 'verify-optimized', False, _('verify optimized result')),
2188 ],
2188 ],
2189 ('REVSPEC'))
2189 ('REVSPEC'))
2190 def debugrevspec(ui, repo, expr, **opts):
2190 def debugrevspec(ui, repo, expr, **opts):
2191 """parse and apply a revision specification
2191 """parse and apply a revision specification
2192
2192
2193 Use -p/--show-stage option to print the parsed tree at the given stages.
2193 Use -p/--show-stage option to print the parsed tree at the given stages.
2194 Use -p all to print tree at every stage.
2194 Use -p all to print tree at every stage.
2195
2195
2196 Use --no-show-revs option with -s or -p to print only the set
2196 Use --no-show-revs option with -s or -p to print only the set
2197 representation or the parsed tree respectively.
2197 representation or the parsed tree respectively.
2198
2198
2199 Use --verify-optimized to compare the optimized result with the unoptimized
2199 Use --verify-optimized to compare the optimized result with the unoptimized
2200 one. Returns 1 if the optimized result differs.
2200 one. Returns 1 if the optimized result differs.
2201 """
2201 """
2202 opts = pycompat.byteskwargs(opts)
2202 opts = pycompat.byteskwargs(opts)
2203 aliases = ui.configitems('revsetalias')
2203 aliases = ui.configitems('revsetalias')
2204 stages = [
2204 stages = [
2205 ('parsed', lambda tree: tree),
2205 ('parsed', lambda tree: tree),
2206 ('expanded', lambda tree: revsetlang.expandaliases(tree, aliases,
2206 ('expanded', lambda tree: revsetlang.expandaliases(tree, aliases,
2207 ui.warn)),
2207 ui.warn)),
2208 ('concatenated', revsetlang.foldconcat),
2208 ('concatenated', revsetlang.foldconcat),
2209 ('analyzed', revsetlang.analyze),
2209 ('analyzed', revsetlang.analyze),
2210 ('optimized', revsetlang.optimize),
2210 ('optimized', revsetlang.optimize),
2211 ]
2211 ]
2212 if opts['no_optimized']:
2212 if opts['no_optimized']:
2213 stages = stages[:-1]
2213 stages = stages[:-1]
2214 if opts['verify_optimized'] and opts['no_optimized']:
2214 if opts['verify_optimized'] and opts['no_optimized']:
2215 raise error.Abort(_('cannot use --verify-optimized with '
2215 raise error.Abort(_('cannot use --verify-optimized with '
2216 '--no-optimized'))
2216 '--no-optimized'))
2217 stagenames = set(n for n, f in stages)
2217 stagenames = set(n for n, f in stages)
2218
2218
2219 showalways = set()
2219 showalways = set()
2220 showchanged = set()
2220 showchanged = set()
2221 if ui.verbose and not opts['show_stage']:
2221 if ui.verbose and not opts['show_stage']:
2222 # show parsed tree by --verbose (deprecated)
2222 # show parsed tree by --verbose (deprecated)
2223 showalways.add('parsed')
2223 showalways.add('parsed')
2224 showchanged.update(['expanded', 'concatenated'])
2224 showchanged.update(['expanded', 'concatenated'])
2225 if opts['optimize']:
2225 if opts['optimize']:
2226 showalways.add('optimized')
2226 showalways.add('optimized')
2227 if opts['show_stage'] and opts['optimize']:
2227 if opts['show_stage'] and opts['optimize']:
2228 raise error.Abort(_('cannot use --optimize with --show-stage'))
2228 raise error.Abort(_('cannot use --optimize with --show-stage'))
2229 if opts['show_stage'] == ['all']:
2229 if opts['show_stage'] == ['all']:
2230 showalways.update(stagenames)
2230 showalways.update(stagenames)
2231 else:
2231 else:
2232 for n in opts['show_stage']:
2232 for n in opts['show_stage']:
2233 if n not in stagenames:
2233 if n not in stagenames:
2234 raise error.Abort(_('invalid stage name: %s') % n)
2234 raise error.Abort(_('invalid stage name: %s') % n)
2235 showalways.update(opts['show_stage'])
2235 showalways.update(opts['show_stage'])
2236
2236
2237 treebystage = {}
2237 treebystage = {}
2238 printedtree = None
2238 printedtree = None
2239 tree = revsetlang.parse(expr, lookup=revset.lookupfn(repo))
2239 tree = revsetlang.parse(expr, lookup=revset.lookupfn(repo))
2240 for n, f in stages:
2240 for n, f in stages:
2241 treebystage[n] = tree = f(tree)
2241 treebystage[n] = tree = f(tree)
2242 if n in showalways or (n in showchanged and tree != printedtree):
2242 if n in showalways or (n in showchanged and tree != printedtree):
2243 if opts['show_stage'] or n != 'parsed':
2243 if opts['show_stage'] or n != 'parsed':
2244 ui.write(("* %s:\n") % n)
2244 ui.write(("* %s:\n") % n)
2245 ui.write(revsetlang.prettyformat(tree), "\n")
2245 ui.write(revsetlang.prettyformat(tree), "\n")
2246 printedtree = tree
2246 printedtree = tree
2247
2247
2248 if opts['verify_optimized']:
2248 if opts['verify_optimized']:
2249 arevs = revset.makematcher(treebystage['analyzed'])(repo)
2249 arevs = revset.makematcher(treebystage['analyzed'])(repo)
2250 brevs = revset.makematcher(treebystage['optimized'])(repo)
2250 brevs = revset.makematcher(treebystage['optimized'])(repo)
2251 if opts['show_set'] or (opts['show_set'] is None and ui.verbose):
2251 if opts['show_set'] or (opts['show_set'] is None and ui.verbose):
2252 ui.write(("* analyzed set:\n"), stringutil.prettyrepr(arevs), "\n")
2252 ui.write(("* analyzed set:\n"), stringutil.prettyrepr(arevs), "\n")
2253 ui.write(("* optimized set:\n"), stringutil.prettyrepr(brevs), "\n")
2253 ui.write(("* optimized set:\n"), stringutil.prettyrepr(brevs), "\n")
2254 arevs = list(arevs)
2254 arevs = list(arevs)
2255 brevs = list(brevs)
2255 brevs = list(brevs)
2256 if arevs == brevs:
2256 if arevs == brevs:
2257 return 0
2257 return 0
2258 ui.write(('--- analyzed\n'), label='diff.file_a')
2258 ui.write(('--- analyzed\n'), label='diff.file_a')
2259 ui.write(('+++ optimized\n'), label='diff.file_b')
2259 ui.write(('+++ optimized\n'), label='diff.file_b')
2260 sm = difflib.SequenceMatcher(None, arevs, brevs)
2260 sm = difflib.SequenceMatcher(None, arevs, brevs)
2261 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2261 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2262 if tag in ('delete', 'replace'):
2262 if tag in ('delete', 'replace'):
2263 for c in arevs[alo:ahi]:
2263 for c in arevs[alo:ahi]:
2264 ui.write('-%s\n' % c, label='diff.deleted')
2264 ui.write('-%s\n' % c, label='diff.deleted')
2265 if tag in ('insert', 'replace'):
2265 if tag in ('insert', 'replace'):
2266 for c in brevs[blo:bhi]:
2266 for c in brevs[blo:bhi]:
2267 ui.write('+%s\n' % c, label='diff.inserted')
2267 ui.write('+%s\n' % c, label='diff.inserted')
2268 if tag == 'equal':
2268 if tag == 'equal':
2269 for c in arevs[alo:ahi]:
2269 for c in arevs[alo:ahi]:
2270 ui.write(' %s\n' % c)
2270 ui.write(' %s\n' % c)
2271 return 1
2271 return 1
2272
2272
2273 func = revset.makematcher(tree)
2273 func = revset.makematcher(tree)
2274 revs = func(repo)
2274 revs = func(repo)
2275 if opts['show_set'] or (opts['show_set'] is None and ui.verbose):
2275 if opts['show_set'] or (opts['show_set'] is None and ui.verbose):
2276 ui.write(("* set:\n"), stringutil.prettyrepr(revs), "\n")
2276 ui.write(("* set:\n"), stringutil.prettyrepr(revs), "\n")
2277 if not opts['show_revs']:
2277 if not opts['show_revs']:
2278 return
2278 return
2279 for c in revs:
2279 for c in revs:
2280 ui.write("%d\n" % c)
2280 ui.write("%d\n" % c)
2281
2281
2282 @command('debugserve', [
2282 @command('debugserve', [
2283 ('', 'sshstdio', False, _('run an SSH server bound to process handles')),
2283 ('', 'sshstdio', False, _('run an SSH server bound to process handles')),
2284 ('', 'logiofd', '', _('file descriptor to log server I/O to')),
2284 ('', 'logiofd', '', _('file descriptor to log server I/O to')),
2285 ('', 'logiofile', '', _('file to log server I/O to')),
2285 ('', 'logiofile', '', _('file to log server I/O to')),
2286 ], '')
2286 ], '')
2287 def debugserve(ui, repo, **opts):
2287 def debugserve(ui, repo, **opts):
2288 """run a server with advanced settings
2288 """run a server with advanced settings
2289
2289
2290 This command is similar to :hg:`serve`. It exists partially as a
2290 This command is similar to :hg:`serve`. It exists partially as a
2291 workaround to the fact that ``hg serve --stdio`` must have specific
2291 workaround to the fact that ``hg serve --stdio`` must have specific
2292 arguments for security reasons.
2292 arguments for security reasons.
2293 """
2293 """
2294 opts = pycompat.byteskwargs(opts)
2294 opts = pycompat.byteskwargs(opts)
2295
2295
2296 if not opts['sshstdio']:
2296 if not opts['sshstdio']:
2297 raise error.Abort(_('only --sshstdio is currently supported'))
2297 raise error.Abort(_('only --sshstdio is currently supported'))
2298
2298
2299 logfh = None
2299 logfh = None
2300
2300
2301 if opts['logiofd'] and opts['logiofile']:
2301 if opts['logiofd'] and opts['logiofile']:
2302 raise error.Abort(_('cannot use both --logiofd and --logiofile'))
2302 raise error.Abort(_('cannot use both --logiofd and --logiofile'))
2303
2303
2304 if opts['logiofd']:
2304 if opts['logiofd']:
2305 # Line buffered because output is line based.
2305 # Line buffered because output is line based.
2306 try:
2306 try:
2307 logfh = os.fdopen(int(opts['logiofd']), r'ab', 1)
2307 logfh = os.fdopen(int(opts['logiofd']), r'ab', 1)
2308 except OSError as e:
2308 except OSError as e:
2309 if e.errno != errno.ESPIPE:
2309 if e.errno != errno.ESPIPE:
2310 raise
2310 raise
2311 # can't seek a pipe, so `ab` mode fails on py3
2311 # can't seek a pipe, so `ab` mode fails on py3
2312 logfh = os.fdopen(int(opts['logiofd']), r'wb', 1)
2312 logfh = os.fdopen(int(opts['logiofd']), r'wb', 1)
2313 elif opts['logiofile']:
2313 elif opts['logiofile']:
2314 logfh = open(opts['logiofile'], 'ab', 1)
2314 logfh = open(opts['logiofile'], 'ab', 1)
2315
2315
2316 s = wireprotoserver.sshserver(ui, repo, logfh=logfh)
2316 s = wireprotoserver.sshserver(ui, repo, logfh=logfh)
2317 s.serve_forever()
2317 s.serve_forever()
2318
2318
2319 @command('debugsetparents', [], _('REV1 [REV2]'))
2319 @command('debugsetparents', [], _('REV1 [REV2]'))
2320 def debugsetparents(ui, repo, rev1, rev2=None):
2320 def debugsetparents(ui, repo, rev1, rev2=None):
2321 """manually set the parents of the current working directory
2321 """manually set the parents of the current working directory
2322
2322
2323 This is useful for writing repository conversion tools, but should
2323 This is useful for writing repository conversion tools, but should
2324 be used with care. For example, neither the working directory nor the
2324 be used with care. For example, neither the working directory nor the
2325 dirstate is updated, so file status may be incorrect after running this
2325 dirstate is updated, so file status may be incorrect after running this
2326 command.
2326 command.
2327
2327
2328 Returns 0 on success.
2328 Returns 0 on success.
2329 """
2329 """
2330
2330
2331 node1 = scmutil.revsingle(repo, rev1).node()
2331 node1 = scmutil.revsingle(repo, rev1).node()
2332 node2 = scmutil.revsingle(repo, rev2, 'null').node()
2332 node2 = scmutil.revsingle(repo, rev2, 'null').node()
2333
2333
2334 with repo.wlock():
2334 with repo.wlock():
2335 repo.setparents(node1, node2)
2335 repo.setparents(node1, node2)
2336
2336
2337 @command('debugssl', [], '[SOURCE]', optionalrepo=True)
2337 @command('debugssl', [], '[SOURCE]', optionalrepo=True)
2338 def debugssl(ui, repo, source=None, **opts):
2338 def debugssl(ui, repo, source=None, **opts):
2339 '''test a secure connection to a server
2339 '''test a secure connection to a server
2340
2340
2341 This builds the certificate chain for the server on Windows, installing the
2341 This builds the certificate chain for the server on Windows, installing the
2342 missing intermediates and trusted root via Windows Update if necessary. It
2342 missing intermediates and trusted root via Windows Update if necessary. It
2343 does nothing on other platforms.
2343 does nothing on other platforms.
2344
2344
2345 If SOURCE is omitted, the 'default' path will be used. If a URL is given,
2345 If SOURCE is omitted, the 'default' path will be used. If a URL is given,
2346 that server is used. See :hg:`help urls` for more information.
2346 that server is used. See :hg:`help urls` for more information.
2347
2347
2348 If the update succeeds, retry the original operation. Otherwise, the cause
2348 If the update succeeds, retry the original operation. Otherwise, the cause
2349 of the SSL error is likely another issue.
2349 of the SSL error is likely another issue.
2350 '''
2350 '''
2351 if not pycompat.iswindows:
2351 if not pycompat.iswindows:
2352 raise error.Abort(_('certificate chain building is only possible on '
2352 raise error.Abort(_('certificate chain building is only possible on '
2353 'Windows'))
2353 'Windows'))
2354
2354
2355 if not source:
2355 if not source:
2356 if not repo:
2356 if not repo:
2357 raise error.Abort(_("there is no Mercurial repository here, and no "
2357 raise error.Abort(_("there is no Mercurial repository here, and no "
2358 "server specified"))
2358 "server specified"))
2359 source = "default"
2359 source = "default"
2360
2360
2361 source, branches = hg.parseurl(ui.expandpath(source))
2361 source, branches = hg.parseurl(ui.expandpath(source))
2362 url = util.url(source)
2362 url = util.url(source)
2363 addr = None
2363 addr = None
2364
2364
2365 defaultport = {'https': 443, 'ssh': 22}
2365 defaultport = {'https': 443, 'ssh': 22}
2366 if url.scheme in defaultport:
2366 if url.scheme in defaultport:
2367 try:
2367 try:
2368 addr = (url.host, int(url.port or defaultport[url.scheme]))
2368 addr = (url.host, int(url.port or defaultport[url.scheme]))
2369 except ValueError:
2369 except ValueError:
2370 raise error.Abort(_("malformed port number in URL"))
2370 raise error.Abort(_("malformed port number in URL"))
2371 else:
2371 else:
2372 raise error.Abort(_("only https and ssh connections are supported"))
2372 raise error.Abort(_("only https and ssh connections are supported"))
2373
2373
2374 from . import win32
2374 from . import win32
2375
2375
2376 s = ssl.wrap_socket(socket.socket(), ssl_version=ssl.PROTOCOL_TLS,
2376 s = ssl.wrap_socket(socket.socket(), ssl_version=ssl.PROTOCOL_TLS,
2377 cert_reqs=ssl.CERT_NONE, ca_certs=None)
2377 cert_reqs=ssl.CERT_NONE, ca_certs=None)
2378
2378
2379 try:
2379 try:
2380 s.connect(addr)
2380 s.connect(addr)
2381 cert = s.getpeercert(True)
2381 cert = s.getpeercert(True)
2382
2382
2383 ui.status(_('checking the certificate chain for %s\n') % url.host)
2383 ui.status(_('checking the certificate chain for %s\n') % url.host)
2384
2384
2385 complete = win32.checkcertificatechain(cert, build=False)
2385 complete = win32.checkcertificatechain(cert, build=False)
2386
2386
2387 if not complete:
2387 if not complete:
2388 ui.status(_('certificate chain is incomplete, updating... '))
2388 ui.status(_('certificate chain is incomplete, updating... '))
2389
2389
2390 if not win32.checkcertificatechain(cert):
2390 if not win32.checkcertificatechain(cert):
2391 ui.status(_('failed.\n'))
2391 ui.status(_('failed.\n'))
2392 else:
2392 else:
2393 ui.status(_('done.\n'))
2393 ui.status(_('done.\n'))
2394 else:
2394 else:
2395 ui.status(_('full certificate chain is available\n'))
2395 ui.status(_('full certificate chain is available\n'))
2396 finally:
2396 finally:
2397 s.close()
2397 s.close()
2398
2398
2399 @command('debugsub',
2399 @command('debugsub',
2400 [('r', 'rev', '',
2400 [('r', 'rev', '',
2401 _('revision to check'), _('REV'))],
2401 _('revision to check'), _('REV'))],
2402 _('[-r REV] [REV]'))
2402 _('[-r REV] [REV]'))
2403 def debugsub(ui, repo, rev=None):
2403 def debugsub(ui, repo, rev=None):
2404 ctx = scmutil.revsingle(repo, rev, None)
2404 ctx = scmutil.revsingle(repo, rev, None)
2405 for k, v in sorted(ctx.substate.items()):
2405 for k, v in sorted(ctx.substate.items()):
2406 ui.write(('path %s\n') % k)
2406 ui.write(('path %s\n') % k)
2407 ui.write((' source %s\n') % v[0])
2407 ui.write((' source %s\n') % v[0])
2408 ui.write((' revision %s\n') % v[1])
2408 ui.write((' revision %s\n') % v[1])
2409
2409
2410 @command('debugsuccessorssets',
2410 @command('debugsuccessorssets',
2411 [('', 'closest', False, _('return closest successors sets only'))],
2411 [('', 'closest', False, _('return closest successors sets only'))],
2412 _('[REV]'))
2412 _('[REV]'))
2413 def debugsuccessorssets(ui, repo, *revs, **opts):
2413 def debugsuccessorssets(ui, repo, *revs, **opts):
2414 """show set of successors for revision
2414 """show set of successors for revision
2415
2415
2416 A successors set of changeset A is a consistent group of revisions that
2416 A successors set of changeset A is a consistent group of revisions that
2417 succeed A. It contains non-obsolete changesets only unless closests
2417 succeed A. It contains non-obsolete changesets only unless closests
2418 successors set is set.
2418 successors set is set.
2419
2419
2420 In most cases a changeset A has a single successors set containing a single
2420 In most cases a changeset A has a single successors set containing a single
2421 successor (changeset A replaced by A').
2421 successor (changeset A replaced by A').
2422
2422
2423 A changeset that is made obsolete with no successors are called "pruned".
2423 A changeset that is made obsolete with no successors are called "pruned".
2424 Such changesets have no successors sets at all.
2424 Such changesets have no successors sets at all.
2425
2425
2426 A changeset that has been "split" will have a successors set containing
2426 A changeset that has been "split" will have a successors set containing
2427 more than one successor.
2427 more than one successor.
2428
2428
2429 A changeset that has been rewritten in multiple different ways is called
2429 A changeset that has been rewritten in multiple different ways is called
2430 "divergent". Such changesets have multiple successor sets (each of which
2430 "divergent". Such changesets have multiple successor sets (each of which
2431 may also be split, i.e. have multiple successors).
2431 may also be split, i.e. have multiple successors).
2432
2432
2433 Results are displayed as follows::
2433 Results are displayed as follows::
2434
2434
2435 <rev1>
2435 <rev1>
2436 <successors-1A>
2436 <successors-1A>
2437 <rev2>
2437 <rev2>
2438 <successors-2A>
2438 <successors-2A>
2439 <successors-2B1> <successors-2B2> <successors-2B3>
2439 <successors-2B1> <successors-2B2> <successors-2B3>
2440
2440
2441 Here rev2 has two possible (i.e. divergent) successors sets. The first
2441 Here rev2 has two possible (i.e. divergent) successors sets. The first
2442 holds one element, whereas the second holds three (i.e. the changeset has
2442 holds one element, whereas the second holds three (i.e. the changeset has
2443 been split).
2443 been split).
2444 """
2444 """
2445 # passed to successorssets caching computation from one call to another
2445 # passed to successorssets caching computation from one call to another
2446 cache = {}
2446 cache = {}
2447 ctx2str = bytes
2447 ctx2str = bytes
2448 node2str = short
2448 node2str = short
2449 for rev in scmutil.revrange(repo, revs):
2449 for rev in scmutil.revrange(repo, revs):
2450 ctx = repo[rev]
2450 ctx = repo[rev]
2451 ui.write('%s\n'% ctx2str(ctx))
2451 ui.write('%s\n'% ctx2str(ctx))
2452 for succsset in obsutil.successorssets(repo, ctx.node(),
2452 for succsset in obsutil.successorssets(repo, ctx.node(),
2453 closest=opts[r'closest'],
2453 closest=opts[r'closest'],
2454 cache=cache):
2454 cache=cache):
2455 if succsset:
2455 if succsset:
2456 ui.write(' ')
2456 ui.write(' ')
2457 ui.write(node2str(succsset[0]))
2457 ui.write(node2str(succsset[0]))
2458 for node in succsset[1:]:
2458 for node in succsset[1:]:
2459 ui.write(' ')
2459 ui.write(' ')
2460 ui.write(node2str(node))
2460 ui.write(node2str(node))
2461 ui.write('\n')
2461 ui.write('\n')
2462
2462
2463 @command('debugtemplate',
2463 @command('debugtemplate',
2464 [('r', 'rev', [], _('apply template on changesets'), _('REV')),
2464 [('r', 'rev', [], _('apply template on changesets'), _('REV')),
2465 ('D', 'define', [], _('define template keyword'), _('KEY=VALUE'))],
2465 ('D', 'define', [], _('define template keyword'), _('KEY=VALUE'))],
2466 _('[-r REV]... [-D KEY=VALUE]... TEMPLATE'),
2466 _('[-r REV]... [-D KEY=VALUE]... TEMPLATE'),
2467 optionalrepo=True)
2467 optionalrepo=True)
2468 def debugtemplate(ui, repo, tmpl, **opts):
2468 def debugtemplate(ui, repo, tmpl, **opts):
2469 """parse and apply a template
2469 """parse and apply a template
2470
2470
2471 If -r/--rev is given, the template is processed as a log template and
2471 If -r/--rev is given, the template is processed as a log template and
2472 applied to the given changesets. Otherwise, it is processed as a generic
2472 applied to the given changesets. Otherwise, it is processed as a generic
2473 template.
2473 template.
2474
2474
2475 Use --verbose to print the parsed tree.
2475 Use --verbose to print the parsed tree.
2476 """
2476 """
2477 revs = None
2477 revs = None
2478 if opts[r'rev']:
2478 if opts[r'rev']:
2479 if repo is None:
2479 if repo is None:
2480 raise error.RepoError(_('there is no Mercurial repository here '
2480 raise error.RepoError(_('there is no Mercurial repository here '
2481 '(.hg not found)'))
2481 '(.hg not found)'))
2482 revs = scmutil.revrange(repo, opts[r'rev'])
2482 revs = scmutil.revrange(repo, opts[r'rev'])
2483
2483
2484 props = {}
2484 props = {}
2485 for d in opts[r'define']:
2485 for d in opts[r'define']:
2486 try:
2486 try:
2487 k, v = (e.strip() for e in d.split('=', 1))
2487 k, v = (e.strip() for e in d.split('=', 1))
2488 if not k or k == 'ui':
2488 if not k or k == 'ui':
2489 raise ValueError
2489 raise ValueError
2490 props[k] = v
2490 props[k] = v
2491 except ValueError:
2491 except ValueError:
2492 raise error.Abort(_('malformed keyword definition: %s') % d)
2492 raise error.Abort(_('malformed keyword definition: %s') % d)
2493
2493
2494 if ui.verbose:
2494 if ui.verbose:
2495 aliases = ui.configitems('templatealias')
2495 aliases = ui.configitems('templatealias')
2496 tree = templater.parse(tmpl)
2496 tree = templater.parse(tmpl)
2497 ui.note(templater.prettyformat(tree), '\n')
2497 ui.note(templater.prettyformat(tree), '\n')
2498 newtree = templater.expandaliases(tree, aliases)
2498 newtree = templater.expandaliases(tree, aliases)
2499 if newtree != tree:
2499 if newtree != tree:
2500 ui.note(("* expanded:\n"), templater.prettyformat(newtree), '\n')
2500 ui.note(("* expanded:\n"), templater.prettyformat(newtree), '\n')
2501
2501
2502 if revs is None:
2502 if revs is None:
2503 tres = formatter.templateresources(ui, repo)
2503 tres = formatter.templateresources(ui, repo)
2504 t = formatter.maketemplater(ui, tmpl, resources=tres)
2504 t = formatter.maketemplater(ui, tmpl, resources=tres)
2505 if ui.verbose:
2505 if ui.verbose:
2506 kwds, funcs = t.symbolsuseddefault()
2506 kwds, funcs = t.symbolsuseddefault()
2507 ui.write(("* keywords: %s\n") % ', '.join(sorted(kwds)))
2507 ui.write(("* keywords: %s\n") % ', '.join(sorted(kwds)))
2508 ui.write(("* functions: %s\n") % ', '.join(sorted(funcs)))
2508 ui.write(("* functions: %s\n") % ', '.join(sorted(funcs)))
2509 ui.write(t.renderdefault(props))
2509 ui.write(t.renderdefault(props))
2510 else:
2510 else:
2511 displayer = logcmdutil.maketemplater(ui, repo, tmpl)
2511 displayer = logcmdutil.maketemplater(ui, repo, tmpl)
2512 if ui.verbose:
2512 if ui.verbose:
2513 kwds, funcs = displayer.t.symbolsuseddefault()
2513 kwds, funcs = displayer.t.symbolsuseddefault()
2514 ui.write(("* keywords: %s\n") % ', '.join(sorted(kwds)))
2514 ui.write(("* keywords: %s\n") % ', '.join(sorted(kwds)))
2515 ui.write(("* functions: %s\n") % ', '.join(sorted(funcs)))
2515 ui.write(("* functions: %s\n") % ', '.join(sorted(funcs)))
2516 for r in revs:
2516 for r in revs:
2517 displayer.show(repo[r], **pycompat.strkwargs(props))
2517 displayer.show(repo[r], **pycompat.strkwargs(props))
2518 displayer.close()
2518 displayer.close()
2519
2519
2520 @command('debuguigetpass', [
2520 @command('debuguigetpass', [
2521 ('p', 'prompt', '', _('prompt text'), _('TEXT')),
2521 ('p', 'prompt', '', _('prompt text'), _('TEXT')),
2522 ], _('[-p TEXT]'), norepo=True)
2522 ], _('[-p TEXT]'), norepo=True)
2523 def debuguigetpass(ui, prompt=''):
2523 def debuguigetpass(ui, prompt=''):
2524 """show prompt to type password"""
2524 """show prompt to type password"""
2525 r = ui.getpass(prompt)
2525 r = ui.getpass(prompt)
2526 ui.write(('respose: %s\n') % r)
2526 ui.write(('respose: %s\n') % r)
2527
2527
2528 @command('debuguiprompt', [
2528 @command('debuguiprompt', [
2529 ('p', 'prompt', '', _('prompt text'), _('TEXT')),
2529 ('p', 'prompt', '', _('prompt text'), _('TEXT')),
2530 ], _('[-p TEXT]'), norepo=True)
2530 ], _('[-p TEXT]'), norepo=True)
2531 def debuguiprompt(ui, prompt=''):
2531 def debuguiprompt(ui, prompt=''):
2532 """show plain prompt"""
2532 """show plain prompt"""
2533 r = ui.prompt(prompt)
2533 r = ui.prompt(prompt)
2534 ui.write(('response: %s\n') % r)
2534 ui.write(('response: %s\n') % r)
2535
2535
2536 @command('debugupdatecaches', [])
2536 @command('debugupdatecaches', [])
2537 def debugupdatecaches(ui, repo, *pats, **opts):
2537 def debugupdatecaches(ui, repo, *pats, **opts):
2538 """warm all known caches in the repository"""
2538 """warm all known caches in the repository"""
2539 with repo.wlock(), repo.lock():
2539 with repo.wlock(), repo.lock():
2540 repo.updatecaches(full=True)
2540 repo.updatecaches(full=True)
2541
2541
2542 @command('debugupgraderepo', [
2542 @command('debugupgraderepo', [
2543 ('o', 'optimize', [], _('extra optimization to perform'), _('NAME')),
2543 ('o', 'optimize', [], _('extra optimization to perform'), _('NAME')),
2544 ('', 'run', False, _('performs an upgrade')),
2544 ('', 'run', False, _('performs an upgrade')),
2545 ])
2545 ])
2546 def debugupgraderepo(ui, repo, run=False, optimize=None):
2546 def debugupgraderepo(ui, repo, run=False, optimize=None):
2547 """upgrade a repository to use different features
2547 """upgrade a repository to use different features
2548
2548
2549 If no arguments are specified, the repository is evaluated for upgrade
2549 If no arguments are specified, the repository is evaluated for upgrade
2550 and a list of problems and potential optimizations is printed.
2550 and a list of problems and potential optimizations is printed.
2551
2551
2552 With ``--run``, a repository upgrade is performed. Behavior of the upgrade
2552 With ``--run``, a repository upgrade is performed. Behavior of the upgrade
2553 can be influenced via additional arguments. More details will be provided
2553 can be influenced via additional arguments. More details will be provided
2554 by the command output when run without ``--run``.
2554 by the command output when run without ``--run``.
2555
2555
2556 During the upgrade, the repository will be locked and no writes will be
2556 During the upgrade, the repository will be locked and no writes will be
2557 allowed.
2557 allowed.
2558
2558
2559 At the end of the upgrade, the repository may not be readable while new
2559 At the end of the upgrade, the repository may not be readable while new
2560 repository data is swapped in. This window will be as long as it takes to
2560 repository data is swapped in. This window will be as long as it takes to
2561 rename some directories inside the ``.hg`` directory. On most machines, this
2561 rename some directories inside the ``.hg`` directory. On most machines, this
2562 should complete almost instantaneously and the chances of a consumer being
2562 should complete almost instantaneously and the chances of a consumer being
2563 unable to access the repository should be low.
2563 unable to access the repository should be low.
2564 """
2564 """
2565 return upgrade.upgraderepo(ui, repo, run=run, optimize=optimize)
2565 return upgrade.upgraderepo(ui, repo, run=run, optimize=optimize)
2566
2566
2567 @command('debugwalk', cmdutil.walkopts, _('[OPTION]... [FILE]...'),
2567 @command('debugwalk', cmdutil.walkopts, _('[OPTION]... [FILE]...'),
2568 inferrepo=True)
2568 inferrepo=True)
2569 def debugwalk(ui, repo, *pats, **opts):
2569 def debugwalk(ui, repo, *pats, **opts):
2570 """show how files match on given patterns"""
2570 """show how files match on given patterns"""
2571 opts = pycompat.byteskwargs(opts)
2571 opts = pycompat.byteskwargs(opts)
2572 m = scmutil.match(repo[None], pats, opts)
2572 m = scmutil.match(repo[None], pats, opts)
2573 if ui.verbose:
2573 if ui.verbose:
2574 ui.write(('* matcher:\n'), stringutil.prettyrepr(m), '\n')
2574 ui.write(('* matcher:\n'), stringutil.prettyrepr(m), '\n')
2575 items = list(repo[None].walk(m))
2575 items = list(repo[None].walk(m))
2576 if not items:
2576 if not items:
2577 return
2577 return
2578 f = lambda fn: fn
2578 f = lambda fn: fn
2579 if ui.configbool('ui', 'slash') and pycompat.ossep != '/':
2579 if ui.configbool('ui', 'slash') and pycompat.ossep != '/':
2580 f = lambda fn: util.normpath(fn)
2580 f = lambda fn: util.normpath(fn)
2581 fmt = 'f %%-%ds %%-%ds %%s' % (
2581 fmt = 'f %%-%ds %%-%ds %%s' % (
2582 max([len(abs) for abs in items]),
2582 max([len(abs) for abs in items]),
2583 max([len(m.rel(abs)) for abs in items]))
2583 max([len(m.rel(abs)) for abs in items]))
2584 for abs in items:
2584 for abs in items:
2585 line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '')
2585 line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '')
2586 ui.write("%s\n" % line.rstrip())
2586 ui.write("%s\n" % line.rstrip())
2587
2587
2588 @command('debugwhyunstable', [], _('REV'))
2588 @command('debugwhyunstable', [], _('REV'))
2589 def debugwhyunstable(ui, repo, rev):
2589 def debugwhyunstable(ui, repo, rev):
2590 """explain instabilities of a changeset"""
2590 """explain instabilities of a changeset"""
2591 for entry in obsutil.whyunstable(repo, scmutil.revsingle(repo, rev)):
2591 for entry in obsutil.whyunstable(repo, scmutil.revsingle(repo, rev)):
2592 dnodes = ''
2592 dnodes = ''
2593 if entry.get('divergentnodes'):
2593 if entry.get('divergentnodes'):
2594 dnodes = ' '.join('%s (%s)' % (ctx.hex(), ctx.phasestr())
2594 dnodes = ' '.join('%s (%s)' % (ctx.hex(), ctx.phasestr())
2595 for ctx in entry['divergentnodes']) + ' '
2595 for ctx in entry['divergentnodes']) + ' '
2596 ui.write('%s: %s%s %s\n' % (entry['instability'], dnodes,
2596 ui.write('%s: %s%s %s\n' % (entry['instability'], dnodes,
2597 entry['reason'], entry['node']))
2597 entry['reason'], entry['node']))
2598
2598
2599 @command('debugwireargs',
2599 @command('debugwireargs',
2600 [('', 'three', '', 'three'),
2600 [('', 'three', '', 'three'),
2601 ('', 'four', '', 'four'),
2601 ('', 'four', '', 'four'),
2602 ('', 'five', '', 'five'),
2602 ('', 'five', '', 'five'),
2603 ] + cmdutil.remoteopts,
2603 ] + cmdutil.remoteopts,
2604 _('REPO [OPTIONS]... [ONE [TWO]]'),
2604 _('REPO [OPTIONS]... [ONE [TWO]]'),
2605 norepo=True)
2605 norepo=True)
2606 def debugwireargs(ui, repopath, *vals, **opts):
2606 def debugwireargs(ui, repopath, *vals, **opts):
2607 opts = pycompat.byteskwargs(opts)
2607 opts = pycompat.byteskwargs(opts)
2608 repo = hg.peer(ui, opts, repopath)
2608 repo = hg.peer(ui, opts, repopath)
2609 for opt in cmdutil.remoteopts:
2609 for opt in cmdutil.remoteopts:
2610 del opts[opt[1]]
2610 del opts[opt[1]]
2611 args = {}
2611 args = {}
2612 for k, v in opts.iteritems():
2612 for k, v in opts.iteritems():
2613 if v:
2613 if v:
2614 args[k] = v
2614 args[k] = v
2615 args = pycompat.strkwargs(args)
2615 args = pycompat.strkwargs(args)
2616 # run twice to check that we don't mess up the stream for the next command
2616 # run twice to check that we don't mess up the stream for the next command
2617 res1 = repo.debugwireargs(*vals, **args)
2617 res1 = repo.debugwireargs(*vals, **args)
2618 res2 = repo.debugwireargs(*vals, **args)
2618 res2 = repo.debugwireargs(*vals, **args)
2619 ui.write("%s\n" % res1)
2619 ui.write("%s\n" % res1)
2620 if res1 != res2:
2620 if res1 != res2:
2621 ui.warn("%s\n" % res2)
2621 ui.warn("%s\n" % res2)
2622
2622
2623 def _parsewirelangblocks(fh):
2623 def _parsewirelangblocks(fh):
2624 activeaction = None
2624 activeaction = None
2625 blocklines = []
2625 blocklines = []
2626
2626
2627 for line in fh:
2627 for line in fh:
2628 line = line.rstrip()
2628 line = line.rstrip()
2629 if not line:
2629 if not line:
2630 continue
2630 continue
2631
2631
2632 if line.startswith(b'#'):
2632 if line.startswith(b'#'):
2633 continue
2633 continue
2634
2634
2635 if not line.startswith(' '):
2635 if not line.startswith(' '):
2636 # New block. Flush previous one.
2636 # New block. Flush previous one.
2637 if activeaction:
2637 if activeaction:
2638 yield activeaction, blocklines
2638 yield activeaction, blocklines
2639
2639
2640 activeaction = line
2640 activeaction = line
2641 blocklines = []
2641 blocklines = []
2642 continue
2642 continue
2643
2643
2644 # Else we start with an indent.
2644 # Else we start with an indent.
2645
2645
2646 if not activeaction:
2646 if not activeaction:
2647 raise error.Abort(_('indented line outside of block'))
2647 raise error.Abort(_('indented line outside of block'))
2648
2648
2649 blocklines.append(line)
2649 blocklines.append(line)
2650
2650
2651 # Flush last block.
2651 # Flush last block.
2652 if activeaction:
2652 if activeaction:
2653 yield activeaction, blocklines
2653 yield activeaction, blocklines
2654
2654
2655 @command('debugwireproto',
2655 @command('debugwireproto',
2656 [
2656 [
2657 ('', 'localssh', False, _('start an SSH server for this repo')),
2657 ('', 'localssh', False, _('start an SSH server for this repo')),
2658 ('', 'peer', '', _('construct a specific version of the peer')),
2658 ('', 'peer', '', _('construct a specific version of the peer')),
2659 ('', 'noreadstderr', False, _('do not read from stderr of the remote')),
2659 ('', 'noreadstderr', False, _('do not read from stderr of the remote')),
2660 ('', 'nologhandshake', False,
2660 ('', 'nologhandshake', False,
2661 _('do not log I/O related to the peer handshake')),
2661 _('do not log I/O related to the peer handshake')),
2662 ] + cmdutil.remoteopts,
2662 ] + cmdutil.remoteopts,
2663 _('[PATH]'),
2663 _('[PATH]'),
2664 optionalrepo=True)
2664 optionalrepo=True)
2665 def debugwireproto(ui, repo, path=None, **opts):
2665 def debugwireproto(ui, repo, path=None, **opts):
2666 """send wire protocol commands to a server
2666 """send wire protocol commands to a server
2667
2667
2668 This command can be used to issue wire protocol commands to remote
2668 This command can be used to issue wire protocol commands to remote
2669 peers and to debug the raw data being exchanged.
2669 peers and to debug the raw data being exchanged.
2670
2670
2671 ``--localssh`` will start an SSH server against the current repository
2671 ``--localssh`` will start an SSH server against the current repository
2672 and connect to that. By default, the connection will perform a handshake
2672 and connect to that. By default, the connection will perform a handshake
2673 and establish an appropriate peer instance.
2673 and establish an appropriate peer instance.
2674
2674
2675 ``--peer`` can be used to bypass the handshake protocol and construct a
2675 ``--peer`` can be used to bypass the handshake protocol and construct a
2676 peer instance using the specified class type. Valid values are ``raw``,
2676 peer instance using the specified class type. Valid values are ``raw``,
2677 ``http2``, ``ssh1``, and ``ssh2``. ``raw`` instances only allow sending
2677 ``http2``, ``ssh1``, and ``ssh2``. ``raw`` instances only allow sending
2678 raw data payloads and don't support higher-level command actions.
2678 raw data payloads and don't support higher-level command actions.
2679
2679
2680 ``--noreadstderr`` can be used to disable automatic reading from stderr
2680 ``--noreadstderr`` can be used to disable automatic reading from stderr
2681 of the peer (for SSH connections only). Disabling automatic reading of
2681 of the peer (for SSH connections only). Disabling automatic reading of
2682 stderr is useful for making output more deterministic.
2682 stderr is useful for making output more deterministic.
2683
2683
2684 Commands are issued via a mini language which is specified via stdin.
2684 Commands are issued via a mini language which is specified via stdin.
2685 The language consists of individual actions to perform. An action is
2685 The language consists of individual actions to perform. An action is
2686 defined by a block. A block is defined as a line with no leading
2686 defined by a block. A block is defined as a line with no leading
2687 space followed by 0 or more lines with leading space. Blocks are
2687 space followed by 0 or more lines with leading space. Blocks are
2688 effectively a high-level command with additional metadata.
2688 effectively a high-level command with additional metadata.
2689
2689
2690 Lines beginning with ``#`` are ignored.
2690 Lines beginning with ``#`` are ignored.
2691
2691
2692 The following sections denote available actions.
2692 The following sections denote available actions.
2693
2693
2694 raw
2694 raw
2695 ---
2695 ---
2696
2696
2697 Send raw data to the server.
2697 Send raw data to the server.
2698
2698
2699 The block payload contains the raw data to send as one atomic send
2699 The block payload contains the raw data to send as one atomic send
2700 operation. The data may not actually be delivered in a single system
2700 operation. The data may not actually be delivered in a single system
2701 call: it depends on the abilities of the transport being used.
2701 call: it depends on the abilities of the transport being used.
2702
2702
2703 Each line in the block is de-indented and concatenated. Then, that
2703 Each line in the block is de-indented and concatenated. Then, that
2704 value is evaluated as a Python b'' literal. This allows the use of
2704 value is evaluated as a Python b'' literal. This allows the use of
2705 backslash escaping, etc.
2705 backslash escaping, etc.
2706
2706
2707 raw+
2707 raw+
2708 ----
2708 ----
2709
2709
2710 Behaves like ``raw`` except flushes output afterwards.
2710 Behaves like ``raw`` except flushes output afterwards.
2711
2711
2712 command <X>
2712 command <X>
2713 -----------
2713 -----------
2714
2714
2715 Send a request to run a named command, whose name follows the ``command``
2715 Send a request to run a named command, whose name follows the ``command``
2716 string.
2716 string.
2717
2717
2718 Arguments to the command are defined as lines in this block. The format of
2718 Arguments to the command are defined as lines in this block. The format of
2719 each line is ``<key> <value>``. e.g.::
2719 each line is ``<key> <value>``. e.g.::
2720
2720
2721 command listkeys
2721 command listkeys
2722 namespace bookmarks
2722 namespace bookmarks
2723
2723
2724 If the value begins with ``eval:``, it will be interpreted as a Python
2724 If the value begins with ``eval:``, it will be interpreted as a Python
2725 literal expression. Otherwise values are interpreted as Python b'' literals.
2725 literal expression. Otherwise values are interpreted as Python b'' literals.
2726 This allows sending complex types and encoding special byte sequences via
2726 This allows sending complex types and encoding special byte sequences via
2727 backslash escaping.
2727 backslash escaping.
2728
2728
2729 The following arguments have special meaning:
2729 The following arguments have special meaning:
2730
2730
2731 ``PUSHFILE``
2731 ``PUSHFILE``
2732 When defined, the *push* mechanism of the peer will be used instead
2732 When defined, the *push* mechanism of the peer will be used instead
2733 of the static request-response mechanism and the content of the
2733 of the static request-response mechanism and the content of the
2734 file specified in the value of this argument will be sent as the
2734 file specified in the value of this argument will be sent as the
2735 command payload.
2735 command payload.
2736
2736
2737 This can be used to submit a local bundle file to the remote.
2737 This can be used to submit a local bundle file to the remote.
2738
2738
2739 batchbegin
2739 batchbegin
2740 ----------
2740 ----------
2741
2741
2742 Instruct the peer to begin a batched send.
2742 Instruct the peer to begin a batched send.
2743
2743
2744 All ``command`` blocks are queued for execution until the next
2744 All ``command`` blocks are queued for execution until the next
2745 ``batchsubmit`` block.
2745 ``batchsubmit`` block.
2746
2746
2747 batchsubmit
2747 batchsubmit
2748 -----------
2748 -----------
2749
2749
2750 Submit previously queued ``command`` blocks as a batch request.
2750 Submit previously queued ``command`` blocks as a batch request.
2751
2751
2752 This action MUST be paired with a ``batchbegin`` action.
2752 This action MUST be paired with a ``batchbegin`` action.
2753
2753
2754 httprequest <method> <path>
2754 httprequest <method> <path>
2755 ---------------------------
2755 ---------------------------
2756
2756
2757 (HTTP peer only)
2757 (HTTP peer only)
2758
2758
2759 Send an HTTP request to the peer.
2759 Send an HTTP request to the peer.
2760
2760
2761 The HTTP request line follows the ``httprequest`` action. e.g. ``GET /foo``.
2761 The HTTP request line follows the ``httprequest`` action. e.g. ``GET /foo``.
2762
2762
2763 Arguments of the form ``<key>: <value>`` are interpreted as HTTP request
2763 Arguments of the form ``<key>: <value>`` are interpreted as HTTP request
2764 headers to add to the request. e.g. ``Accept: foo``.
2764 headers to add to the request. e.g. ``Accept: foo``.
2765
2765
2766 The following arguments are special:
2766 The following arguments are special:
2767
2767
2768 ``BODYFILE``
2768 ``BODYFILE``
2769 The content of the file defined as the value to this argument will be
2769 The content of the file defined as the value to this argument will be
2770 transferred verbatim as the HTTP request body.
2770 transferred verbatim as the HTTP request body.
2771
2771
2772 ``frame <type> <flags> <payload>``
2772 ``frame <type> <flags> <payload>``
2773 Send a unified protocol frame as part of the request body.
2773 Send a unified protocol frame as part of the request body.
2774
2774
2775 All frames will be collected and sent as the body to the HTTP
2775 All frames will be collected and sent as the body to the HTTP
2776 request.
2776 request.
2777
2777
2778 close
2778 close
2779 -----
2779 -----
2780
2780
2781 Close the connection to the server.
2781 Close the connection to the server.
2782
2782
2783 flush
2783 flush
2784 -----
2784 -----
2785
2785
2786 Flush data written to the server.
2786 Flush data written to the server.
2787
2787
2788 readavailable
2788 readavailable
2789 -------------
2789 -------------
2790
2790
2791 Close the write end of the connection and read all available data from
2791 Close the write end of the connection and read all available data from
2792 the server.
2792 the server.
2793
2793
2794 If the connection to the server encompasses multiple pipes, we poll both
2794 If the connection to the server encompasses multiple pipes, we poll both
2795 pipes and read available data.
2795 pipes and read available data.
2796
2796
2797 readline
2797 readline
2798 --------
2798 --------
2799
2799
2800 Read a line of output from the server. If there are multiple output
2800 Read a line of output from the server. If there are multiple output
2801 pipes, reads only the main pipe.
2801 pipes, reads only the main pipe.
2802
2802
2803 ereadline
2803 ereadline
2804 ---------
2804 ---------
2805
2805
2806 Like ``readline``, but read from the stderr pipe, if available.
2806 Like ``readline``, but read from the stderr pipe, if available.
2807
2807
2808 read <X>
2808 read <X>
2809 --------
2809 --------
2810
2810
2811 ``read()`` N bytes from the server's main output pipe.
2811 ``read()`` N bytes from the server's main output pipe.
2812
2812
2813 eread <X>
2813 eread <X>
2814 ---------
2814 ---------
2815
2815
2816 ``read()`` N bytes from the server's stderr pipe, if available.
2816 ``read()`` N bytes from the server's stderr pipe, if available.
2817
2817
2818 Specifying Unified Frame-Based Protocol Frames
2818 Specifying Unified Frame-Based Protocol Frames
2819 ----------------------------------------------
2819 ----------------------------------------------
2820
2820
2821 It is possible to emit a *Unified Frame-Based Protocol* by using special
2821 It is possible to emit a *Unified Frame-Based Protocol* by using special
2822 syntax.
2822 syntax.
2823
2823
2824 A frame is composed as a type, flags, and payload. These can be parsed
2824 A frame is composed as a type, flags, and payload. These can be parsed
2825 from a string of the form:
2825 from a string of the form:
2826
2826
2827 <request-id> <stream-id> <stream-flags> <type> <flags> <payload>
2827 <request-id> <stream-id> <stream-flags> <type> <flags> <payload>
2828
2828
2829 ``request-id`` and ``stream-id`` are integers defining the request and
2829 ``request-id`` and ``stream-id`` are integers defining the request and
2830 stream identifiers.
2830 stream identifiers.
2831
2831
2832 ``type`` can be an integer value for the frame type or the string name
2832 ``type`` can be an integer value for the frame type or the string name
2833 of the type. The strings are defined in ``wireprotoframing.py``. e.g.
2833 of the type. The strings are defined in ``wireprotoframing.py``. e.g.
2834 ``command-name``.
2834 ``command-name``.
2835
2835
2836 ``stream-flags`` and ``flags`` are a ``|`` delimited list of flag
2836 ``stream-flags`` and ``flags`` are a ``|`` delimited list of flag
2837 components. Each component (and there can be just one) can be an integer
2837 components. Each component (and there can be just one) can be an integer
2838 or a flag name for stream flags or frame flags, respectively. Values are
2838 or a flag name for stream flags or frame flags, respectively. Values are
2839 resolved to integers and then bitwise OR'd together.
2839 resolved to integers and then bitwise OR'd together.
2840
2840
2841 ``payload`` represents the raw frame payload. If it begins with
2841 ``payload`` represents the raw frame payload. If it begins with
2842 ``cbor:``, the following string is evaluated as Python code and the
2842 ``cbor:``, the following string is evaluated as Python code and the
2843 resulting object is fed into a CBOR encoder. Otherwise it is interpreted
2843 resulting object is fed into a CBOR encoder. Otherwise it is interpreted
2844 as a Python byte string literal.
2844 as a Python byte string literal.
2845 """
2845 """
2846 opts = pycompat.byteskwargs(opts)
2846 opts = pycompat.byteskwargs(opts)
2847
2847
2848 if opts['localssh'] and not repo:
2848 if opts['localssh'] and not repo:
2849 raise error.Abort(_('--localssh requires a repository'))
2849 raise error.Abort(_('--localssh requires a repository'))
2850
2850
2851 if opts['peer'] and opts['peer'] not in ('raw', 'http2', 'ssh1', 'ssh2'):
2851 if opts['peer'] and opts['peer'] not in ('raw', 'http2', 'ssh1', 'ssh2'):
2852 raise error.Abort(_('invalid value for --peer'),
2852 raise error.Abort(_('invalid value for --peer'),
2853 hint=_('valid values are "raw", "ssh1", and "ssh2"'))
2853 hint=_('valid values are "raw", "ssh1", and "ssh2"'))
2854
2854
2855 if path and opts['localssh']:
2855 if path and opts['localssh']:
2856 raise error.Abort(_('cannot specify --localssh with an explicit '
2856 raise error.Abort(_('cannot specify --localssh with an explicit '
2857 'path'))
2857 'path'))
2858
2858
2859 if ui.interactive():
2859 if ui.interactive():
2860 ui.write(_('(waiting for commands on stdin)\n'))
2860 ui.write(_('(waiting for commands on stdin)\n'))
2861
2861
2862 blocks = list(_parsewirelangblocks(ui.fin))
2862 blocks = list(_parsewirelangblocks(ui.fin))
2863
2863
2864 proc = None
2864 proc = None
2865 stdin = None
2865 stdin = None
2866 stdout = None
2866 stdout = None
2867 stderr = None
2867 stderr = None
2868 opener = None
2868 opener = None
2869
2869
2870 if opts['localssh']:
2870 if opts['localssh']:
2871 # We start the SSH server in its own process so there is process
2871 # We start the SSH server in its own process so there is process
2872 # separation. This prevents a whole class of potential bugs around
2872 # separation. This prevents a whole class of potential bugs around
2873 # shared state from interfering with server operation.
2873 # shared state from interfering with server operation.
2874 args = procutil.hgcmd() + [
2874 args = procutil.hgcmd() + [
2875 '-R', repo.root,
2875 '-R', repo.root,
2876 'debugserve', '--sshstdio',
2876 'debugserve', '--sshstdio',
2877 ]
2877 ]
2878 proc = subprocess.Popen(args, stdin=subprocess.PIPE,
2878 proc = subprocess.Popen(args, stdin=subprocess.PIPE,
2879 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
2879 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
2880 bufsize=0)
2880 bufsize=0)
2881
2881
2882 stdin = proc.stdin
2882 stdin = proc.stdin
2883 stdout = proc.stdout
2883 stdout = proc.stdout
2884 stderr = proc.stderr
2884 stderr = proc.stderr
2885
2885
2886 # We turn the pipes into observers so we can log I/O.
2886 # We turn the pipes into observers so we can log I/O.
2887 if ui.verbose or opts['peer'] == 'raw':
2887 if ui.verbose or opts['peer'] == 'raw':
2888 stdin = util.makeloggingfileobject(ui, proc.stdin, b'i',
2888 stdin = util.makeloggingfileobject(ui, proc.stdin, b'i',
2889 logdata=True)
2889 logdata=True)
2890 stdout = util.makeloggingfileobject(ui, proc.stdout, b'o',
2890 stdout = util.makeloggingfileobject(ui, proc.stdout, b'o',
2891 logdata=True)
2891 logdata=True)
2892 stderr = util.makeloggingfileobject(ui, proc.stderr, b'e',
2892 stderr = util.makeloggingfileobject(ui, proc.stderr, b'e',
2893 logdata=True)
2893 logdata=True)
2894
2894
2895 # --localssh also implies the peer connection settings.
2895 # --localssh also implies the peer connection settings.
2896
2896
2897 url = 'ssh://localserver'
2897 url = 'ssh://localserver'
2898 autoreadstderr = not opts['noreadstderr']
2898 autoreadstderr = not opts['noreadstderr']
2899
2899
2900 if opts['peer'] == 'ssh1':
2900 if opts['peer'] == 'ssh1':
2901 ui.write(_('creating ssh peer for wire protocol version 1\n'))
2901 ui.write(_('creating ssh peer for wire protocol version 1\n'))
2902 peer = sshpeer.sshv1peer(ui, url, proc, stdin, stdout, stderr,
2902 peer = sshpeer.sshv1peer(ui, url, proc, stdin, stdout, stderr,
2903 None, autoreadstderr=autoreadstderr)
2903 None, autoreadstderr=autoreadstderr)
2904 elif opts['peer'] == 'ssh2':
2904 elif opts['peer'] == 'ssh2':
2905 ui.write(_('creating ssh peer for wire protocol version 2\n'))
2905 ui.write(_('creating ssh peer for wire protocol version 2\n'))
2906 peer = sshpeer.sshv2peer(ui, url, proc, stdin, stdout, stderr,
2906 peer = sshpeer.sshv2peer(ui, url, proc, stdin, stdout, stderr,
2907 None, autoreadstderr=autoreadstderr)
2907 None, autoreadstderr=autoreadstderr)
2908 elif opts['peer'] == 'raw':
2908 elif opts['peer'] == 'raw':
2909 ui.write(_('using raw connection to peer\n'))
2909 ui.write(_('using raw connection to peer\n'))
2910 peer = None
2910 peer = None
2911 else:
2911 else:
2912 ui.write(_('creating ssh peer from handshake results\n'))
2912 ui.write(_('creating ssh peer from handshake results\n'))
2913 peer = sshpeer.makepeer(ui, url, proc, stdin, stdout, stderr,
2913 peer = sshpeer.makepeer(ui, url, proc, stdin, stdout, stderr,
2914 autoreadstderr=autoreadstderr)
2914 autoreadstderr=autoreadstderr)
2915
2915
2916 elif path:
2916 elif path:
2917 # We bypass hg.peer() so we can proxy the sockets.
2917 # We bypass hg.peer() so we can proxy the sockets.
2918 # TODO consider not doing this because we skip
2918 # TODO consider not doing this because we skip
2919 # ``hg.wirepeersetupfuncs`` and potentially other useful functionality.
2919 # ``hg.wirepeersetupfuncs`` and potentially other useful functionality.
2920 u = util.url(path)
2920 u = util.url(path)
2921 if u.scheme != 'http':
2921 if u.scheme != 'http':
2922 raise error.Abort(_('only http:// paths are currently supported'))
2922 raise error.Abort(_('only http:// paths are currently supported'))
2923
2923
2924 url, authinfo = u.authinfo()
2924 url, authinfo = u.authinfo()
2925 openerargs = {
2925 openerargs = {
2926 r'useragent': b'Mercurial debugwireproto',
2926 r'useragent': b'Mercurial debugwireproto',
2927 }
2927 }
2928
2928
2929 # Turn pipes/sockets into observers so we can log I/O.
2929 # Turn pipes/sockets into observers so we can log I/O.
2930 if ui.verbose:
2930 if ui.verbose:
2931 openerargs.update({
2931 openerargs.update({
2932 r'loggingfh': ui,
2932 r'loggingfh': ui,
2933 r'loggingname': b's',
2933 r'loggingname': b's',
2934 r'loggingopts': {
2934 r'loggingopts': {
2935 r'logdata': True,
2935 r'logdata': True,
2936 r'logdataapis': False,
2936 r'logdataapis': False,
2937 },
2937 },
2938 })
2938 })
2939
2939
2940 if ui.debugflag:
2940 if ui.debugflag:
2941 openerargs[r'loggingopts'][r'logdataapis'] = True
2941 openerargs[r'loggingopts'][r'logdataapis'] = True
2942
2942
2943 # Don't send default headers when in raw mode. This allows us to
2943 # Don't send default headers when in raw mode. This allows us to
2944 # bypass most of the behavior of our URL handling code so we can
2944 # bypass most of the behavior of our URL handling code so we can
2945 # have near complete control over what's sent on the wire.
2945 # have near complete control over what's sent on the wire.
2946 if opts['peer'] == 'raw':
2946 if opts['peer'] == 'raw':
2947 openerargs[r'sendaccept'] = False
2947 openerargs[r'sendaccept'] = False
2948
2948
2949 opener = urlmod.opener(ui, authinfo, **openerargs)
2949 opener = urlmod.opener(ui, authinfo, **openerargs)
2950
2950
2951 if opts['peer'] == 'http2':
2951 if opts['peer'] == 'http2':
2952 ui.write(_('creating http peer for wire protocol version 2\n'))
2952 ui.write(_('creating http peer for wire protocol version 2\n'))
2953 # We go through makepeer() because we need an API descriptor for
2953 # We go through makepeer() because we need an API descriptor for
2954 # the peer instance to be useful.
2954 # the peer instance to be useful.
2955 with ui.configoverride({
2955 with ui.configoverride({
2956 ('experimental', 'httppeer.advertise-v2'): True}):
2956 ('experimental', 'httppeer.advertise-v2'): True}):
2957 if opts['nologhandshake']:
2957 if opts['nologhandshake']:
2958 ui.pushbuffer()
2958 ui.pushbuffer()
2959
2959
2960 peer = httppeer.makepeer(ui, path, opener=opener)
2960 peer = httppeer.makepeer(ui, path, opener=opener)
2961
2961
2962 if opts['nologhandshake']:
2962 if opts['nologhandshake']:
2963 ui.popbuffer()
2963 ui.popbuffer()
2964
2964
2965 if not isinstance(peer, httppeer.httpv2peer):
2965 if not isinstance(peer, httppeer.httpv2peer):
2966 raise error.Abort(_('could not instantiate HTTP peer for '
2966 raise error.Abort(_('could not instantiate HTTP peer for '
2967 'wire protocol version 2'),
2967 'wire protocol version 2'),
2968 hint=_('the server may not have the feature '
2968 hint=_('the server may not have the feature '
2969 'enabled or is not allowing this '
2969 'enabled or is not allowing this '
2970 'client version'))
2970 'client version'))
2971
2971
2972 elif opts['peer'] == 'raw':
2972 elif opts['peer'] == 'raw':
2973 ui.write(_('using raw connection to peer\n'))
2973 ui.write(_('using raw connection to peer\n'))
2974 peer = None
2974 peer = None
2975 elif opts['peer']:
2975 elif opts['peer']:
2976 raise error.Abort(_('--peer %s not supported with HTTP peers') %
2976 raise error.Abort(_('--peer %s not supported with HTTP peers') %
2977 opts['peer'])
2977 opts['peer'])
2978 else:
2978 else:
2979 peer = httppeer.makepeer(ui, path, opener=opener)
2979 peer = httppeer.makepeer(ui, path, opener=opener)
2980
2980
2981 # We /could/ populate stdin/stdout with sock.makefile()...
2981 # We /could/ populate stdin/stdout with sock.makefile()...
2982 else:
2982 else:
2983 raise error.Abort(_('unsupported connection configuration'))
2983 raise error.Abort(_('unsupported connection configuration'))
2984
2984
2985 batchedcommands = None
2985 batchedcommands = None
2986
2986
2987 # Now perform actions based on the parsed wire language instructions.
2987 # Now perform actions based on the parsed wire language instructions.
2988 for action, lines in blocks:
2988 for action, lines in blocks:
2989 if action in ('raw', 'raw+'):
2989 if action in ('raw', 'raw+'):
2990 if not stdin:
2990 if not stdin:
2991 raise error.Abort(_('cannot call raw/raw+ on this peer'))
2991 raise error.Abort(_('cannot call raw/raw+ on this peer'))
2992
2992
2993 # Concatenate the data together.
2993 # Concatenate the data together.
2994 data = ''.join(l.lstrip() for l in lines)
2994 data = ''.join(l.lstrip() for l in lines)
2995 data = stringutil.unescapestr(data)
2995 data = stringutil.unescapestr(data)
2996 stdin.write(data)
2996 stdin.write(data)
2997
2997
2998 if action == 'raw+':
2998 if action == 'raw+':
2999 stdin.flush()
2999 stdin.flush()
3000 elif action == 'flush':
3000 elif action == 'flush':
3001 if not stdin:
3001 if not stdin:
3002 raise error.Abort(_('cannot call flush on this peer'))
3002 raise error.Abort(_('cannot call flush on this peer'))
3003 stdin.flush()
3003 stdin.flush()
3004 elif action.startswith('command'):
3004 elif action.startswith('command'):
3005 if not peer:
3005 if not peer:
3006 raise error.Abort(_('cannot send commands unless peer instance '
3006 raise error.Abort(_('cannot send commands unless peer instance '
3007 'is available'))
3007 'is available'))
3008
3008
3009 command = action.split(' ', 1)[1]
3009 command = action.split(' ', 1)[1]
3010
3010
3011 args = {}
3011 args = {}
3012 for line in lines:
3012 for line in lines:
3013 # We need to allow empty values.
3013 # We need to allow empty values.
3014 fields = line.lstrip().split(' ', 1)
3014 fields = line.lstrip().split(' ', 1)
3015 if len(fields) == 1:
3015 if len(fields) == 1:
3016 key = fields[0]
3016 key = fields[0]
3017 value = ''
3017 value = ''
3018 else:
3018 else:
3019 key, value = fields
3019 key, value = fields
3020
3020
3021 if value.startswith('eval:'):
3021 if value.startswith('eval:'):
3022 value = stringutil.evalpythonliteral(value[5:])
3022 value = stringutil.evalpythonliteral(value[5:])
3023 else:
3023 else:
3024 value = stringutil.unescapestr(value)
3024 value = stringutil.unescapestr(value)
3025
3025
3026 args[key] = value
3026 args[key] = value
3027
3027
3028 if batchedcommands is not None:
3028 if batchedcommands is not None:
3029 batchedcommands.append((command, args))
3029 batchedcommands.append((command, args))
3030 continue
3030 continue
3031
3031
3032 ui.status(_('sending %s command\n') % command)
3032 ui.status(_('sending %s command\n') % command)
3033
3033
3034 if 'PUSHFILE' in args:
3034 if 'PUSHFILE' in args:
3035 with open(args['PUSHFILE'], r'rb') as fh:
3035 with open(args['PUSHFILE'], r'rb') as fh:
3036 del args['PUSHFILE']
3036 del args['PUSHFILE']
3037 res, output = peer._callpush(command, fh,
3037 res, output = peer._callpush(command, fh,
3038 **pycompat.strkwargs(args))
3038 **pycompat.strkwargs(args))
3039 ui.status(_('result: %s\n') % stringutil.escapestr(res))
3039 ui.status(_('result: %s\n') % stringutil.escapestr(res))
3040 ui.status(_('remote output: %s\n') %
3040 ui.status(_('remote output: %s\n') %
3041 stringutil.escapestr(output))
3041 stringutil.escapestr(output))
3042 else:
3042 else:
3043 with peer.commandexecutor() as e:
3043 with peer.commandexecutor() as e:
3044 res = e.callcommand(command, args).result()
3044 res = e.callcommand(command, args).result()
3045
3045
3046 if isinstance(res, wireprotov2peer.commandresponse):
3046 if isinstance(res, wireprotov2peer.commandresponse):
3047 val = list(res.cborobjects())
3047 val = list(res.cborobjects())
3048 ui.status(_('response: %s\n') %
3048 ui.status(_('response: %s\n') %
3049 stringutil.pprint(val, bprefix=True))
3049 stringutil.pprint(val, bprefix=True))
3050
3050
3051 else:
3051 else:
3052 ui.status(_('response: %s\n') %
3052 ui.status(_('response: %s\n') %
3053 stringutil.pprint(res, bprefix=True))
3053 stringutil.pprint(res, bprefix=True))
3054
3054
3055 elif action == 'batchbegin':
3055 elif action == 'batchbegin':
3056 if batchedcommands is not None:
3056 if batchedcommands is not None:
3057 raise error.Abort(_('nested batchbegin not allowed'))
3057 raise error.Abort(_('nested batchbegin not allowed'))
3058
3058
3059 batchedcommands = []
3059 batchedcommands = []
3060 elif action == 'batchsubmit':
3060 elif action == 'batchsubmit':
3061 # There is a batching API we could go through. But it would be
3061 # There is a batching API we could go through. But it would be
3062 # difficult to normalize requests into function calls. It is easier
3062 # difficult to normalize requests into function calls. It is easier
3063 # to bypass this layer and normalize to commands + args.
3063 # to bypass this layer and normalize to commands + args.
3064 ui.status(_('sending batch with %d sub-commands\n') %
3064 ui.status(_('sending batch with %d sub-commands\n') %
3065 len(batchedcommands))
3065 len(batchedcommands))
3066 for i, chunk in enumerate(peer._submitbatch(batchedcommands)):
3066 for i, chunk in enumerate(peer._submitbatch(batchedcommands)):
3067 ui.status(_('response #%d: %s\n') %
3067 ui.status(_('response #%d: %s\n') %
3068 (i, stringutil.escapestr(chunk)))
3068 (i, stringutil.escapestr(chunk)))
3069
3069
3070 batchedcommands = None
3070 batchedcommands = None
3071
3071
3072 elif action.startswith('httprequest '):
3072 elif action.startswith('httprequest '):
3073 if not opener:
3073 if not opener:
3074 raise error.Abort(_('cannot use httprequest without an HTTP '
3074 raise error.Abort(_('cannot use httprequest without an HTTP '
3075 'peer'))
3075 'peer'))
3076
3076
3077 request = action.split(' ', 2)
3077 request = action.split(' ', 2)
3078 if len(request) != 3:
3078 if len(request) != 3:
3079 raise error.Abort(_('invalid httprequest: expected format is '
3079 raise error.Abort(_('invalid httprequest: expected format is '
3080 '"httprequest <method> <path>'))
3080 '"httprequest <method> <path>'))
3081
3081
3082 method, httppath = request[1:]
3082 method, httppath = request[1:]
3083 headers = {}
3083 headers = {}
3084 body = None
3084 body = None
3085 frames = []
3085 frames = []
3086 for line in lines:
3086 for line in lines:
3087 line = line.lstrip()
3087 line = line.lstrip()
3088 m = re.match(b'^([a-zA-Z0-9_-]+): (.*)$', line)
3088 m = re.match(b'^([a-zA-Z0-9_-]+): (.*)$', line)
3089 if m:
3089 if m:
3090 headers[m.group(1)] = m.group(2)
3090 headers[m.group(1)] = m.group(2)
3091 continue
3091 continue
3092
3092
3093 if line.startswith(b'BODYFILE '):
3093 if line.startswith(b'BODYFILE '):
3094 with open(line.split(b' ', 1), 'rb') as fh:
3094 with open(line.split(b' ', 1), 'rb') as fh:
3095 body = fh.read()
3095 body = fh.read()
3096 elif line.startswith(b'frame '):
3096 elif line.startswith(b'frame '):
3097 frame = wireprotoframing.makeframefromhumanstring(
3097 frame = wireprotoframing.makeframefromhumanstring(
3098 line[len(b'frame '):])
3098 line[len(b'frame '):])
3099
3099
3100 frames.append(frame)
3100 frames.append(frame)
3101 else:
3101 else:
3102 raise error.Abort(_('unknown argument to httprequest: %s') %
3102 raise error.Abort(_('unknown argument to httprequest: %s') %
3103 line)
3103 line)
3104
3104
3105 url = path + httppath
3105 url = path + httppath
3106
3106
3107 if frames:
3107 if frames:
3108 body = b''.join(bytes(f) for f in frames)
3108 body = b''.join(bytes(f) for f in frames)
3109
3109
3110 req = urlmod.urlreq.request(pycompat.strurl(url), body, headers)
3110 req = urlmod.urlreq.request(pycompat.strurl(url), body, headers)
3111
3111
3112 # urllib.Request insists on using has_data() as a proxy for
3112 # urllib.Request insists on using has_data() as a proxy for
3113 # determining the request method. Override that to use our
3113 # determining the request method. Override that to use our
3114 # explicitly requested method.
3114 # explicitly requested method.
3115 req.get_method = lambda: method
3115 req.get_method = lambda: method
3116
3116
3117 try:
3117 try:
3118 res = opener.open(req)
3118 res = opener.open(req)
3119 body = res.read()
3119 body = res.read()
3120 except util.urlerr.urlerror as e:
3120 except util.urlerr.urlerror as e:
3121 e.read()
3121 e.read()
3122 continue
3122 continue
3123
3123
3124 if res.headers.get('Content-Type') == 'application/mercurial-cbor':
3124 if res.headers.get('Content-Type') == 'application/mercurial-cbor':
3125 ui.write(_('cbor> %s\n') %
3125 ui.write(_('cbor> %s\n') %
3126 stringutil.pprint(cbor.loads(body), bprefix=True))
3126 stringutil.pprint(cbor.loads(body), bprefix=True))
3127
3127
3128 elif action == 'close':
3128 elif action == 'close':
3129 peer.close()
3129 peer.close()
3130 elif action == 'readavailable':
3130 elif action == 'readavailable':
3131 if not stdout or not stderr:
3131 if not stdout or not stderr:
3132 raise error.Abort(_('readavailable not available on this peer'))
3132 raise error.Abort(_('readavailable not available on this peer'))
3133
3133
3134 stdin.close()
3134 stdin.close()
3135 stdout.read()
3135 stdout.read()
3136 stderr.read()
3136 stderr.read()
3137
3137
3138 elif action == 'readline':
3138 elif action == 'readline':
3139 if not stdout:
3139 if not stdout:
3140 raise error.Abort(_('readline not available on this peer'))
3140 raise error.Abort(_('readline not available on this peer'))
3141 stdout.readline()
3141 stdout.readline()
3142 elif action == 'ereadline':
3142 elif action == 'ereadline':
3143 if not stderr:
3143 if not stderr:
3144 raise error.Abort(_('ereadline not available on this peer'))
3144 raise error.Abort(_('ereadline not available on this peer'))
3145 stderr.readline()
3145 stderr.readline()
3146 elif action.startswith('read '):
3146 elif action.startswith('read '):
3147 count = int(action.split(' ', 1)[1])
3147 count = int(action.split(' ', 1)[1])
3148 if not stdout:
3148 if not stdout:
3149 raise error.Abort(_('read not available on this peer'))
3149 raise error.Abort(_('read not available on this peer'))
3150 stdout.read(count)
3150 stdout.read(count)
3151 elif action.startswith('eread '):
3151 elif action.startswith('eread '):
3152 count = int(action.split(' ', 1)[1])
3152 count = int(action.split(' ', 1)[1])
3153 if not stderr:
3153 if not stderr:
3154 raise error.Abort(_('eread not available on this peer'))
3154 raise error.Abort(_('eread not available on this peer'))
3155 stderr.read(count)
3155 stderr.read(count)
3156 else:
3156 else:
3157 raise error.Abort(_('unknown action: %s') % action)
3157 raise error.Abort(_('unknown action: %s') % action)
3158
3158
3159 if batchedcommands is not None:
3159 if batchedcommands is not None:
3160 raise error.Abort(_('unclosed "batchbegin" request'))
3160 raise error.Abort(_('unclosed "batchbegin" request'))
3161
3161
3162 if peer:
3162 if peer:
3163 peer.close()
3163 peer.close()
3164
3164
3165 if proc:
3165 if proc:
3166 proc.kill()
3166 proc.kill()
@@ -1,665 +1,670 b''
1 # fileset.py - file set queries for mercurial
1 # fileset.py - file set queries for mercurial
2 #
2 #
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
3 # Copyright 2010 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 re
10 import re
11
11
12 from .i18n import _
12 from .i18n import _
13 from . import (
13 from . import (
14 error,
14 error,
15 match as matchmod,
15 match as matchmod,
16 merge,
16 merge,
17 parser,
17 parser,
18 pycompat,
18 pycompat,
19 registrar,
19 registrar,
20 scmutil,
20 scmutil,
21 util,
21 util,
22 )
22 )
23 from .utils import (
23 from .utils import (
24 stringutil,
24 stringutil,
25 )
25 )
26
26
27 elements = {
27 elements = {
28 # token-type: binding-strength, primary, prefix, infix, suffix
28 # token-type: binding-strength, primary, prefix, infix, suffix
29 "(": (20, None, ("group", 1, ")"), ("func", 1, ")"), None),
29 "(": (20, None, ("group", 1, ")"), ("func", 1, ")"), None),
30 ":": (15, None, None, ("kindpat", 15), None),
30 ":": (15, None, None, ("kindpat", 15), None),
31 "-": (5, None, ("negate", 19), ("minus", 5), None),
31 "-": (5, None, ("negate", 19), ("minus", 5), None),
32 "not": (10, None, ("not", 10), None, None),
32 "not": (10, None, ("not", 10), None, None),
33 "!": (10, None, ("not", 10), None, None),
33 "!": (10, None, ("not", 10), None, None),
34 "and": (5, None, None, ("and", 5), None),
34 "and": (5, None, None, ("and", 5), None),
35 "&": (5, None, None, ("and", 5), None),
35 "&": (5, None, None, ("and", 5), None),
36 "or": (4, None, None, ("or", 4), None),
36 "or": (4, None, None, ("or", 4), None),
37 "|": (4, None, None, ("or", 4), None),
37 "|": (4, None, None, ("or", 4), None),
38 "+": (4, None, None, ("or", 4), None),
38 "+": (4, None, None, ("or", 4), None),
39 ",": (2, None, None, ("list", 2), None),
39 ",": (2, None, None, ("list", 2), None),
40 ")": (0, None, None, None, None),
40 ")": (0, None, None, None, None),
41 "symbol": (0, "symbol", None, None, None),
41 "symbol": (0, "symbol", None, None, None),
42 "string": (0, "string", None, None, None),
42 "string": (0, "string", None, None, None),
43 "end": (0, None, None, None, None),
43 "end": (0, None, None, None, None),
44 }
44 }
45
45
46 keywords = {'and', 'or', 'not'}
46 keywords = {'and', 'or', 'not'}
47
47
48 globchars = ".*{}[]?/\\_"
48 globchars = ".*{}[]?/\\_"
49
49
50 def tokenize(program):
50 def tokenize(program):
51 pos, l = 0, len(program)
51 pos, l = 0, len(program)
52 program = pycompat.bytestr(program)
52 program = pycompat.bytestr(program)
53 while pos < l:
53 while pos < l:
54 c = program[pos]
54 c = program[pos]
55 if c.isspace(): # skip inter-token whitespace
55 if c.isspace(): # skip inter-token whitespace
56 pass
56 pass
57 elif c in "(),-:|&+!": # handle simple operators
57 elif c in "(),-:|&+!": # handle simple operators
58 yield (c, None, pos)
58 yield (c, None, pos)
59 elif (c in '"\'' or c == 'r' and
59 elif (c in '"\'' or c == 'r' and
60 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
60 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
61 if c == 'r':
61 if c == 'r':
62 pos += 1
62 pos += 1
63 c = program[pos]
63 c = program[pos]
64 decode = lambda x: x
64 decode = lambda x: x
65 else:
65 else:
66 decode = parser.unescapestr
66 decode = parser.unescapestr
67 pos += 1
67 pos += 1
68 s = pos
68 s = pos
69 while pos < l: # find closing quote
69 while pos < l: # find closing quote
70 d = program[pos]
70 d = program[pos]
71 if d == '\\': # skip over escaped characters
71 if d == '\\': # skip over escaped characters
72 pos += 2
72 pos += 2
73 continue
73 continue
74 if d == c:
74 if d == c:
75 yield ('string', decode(program[s:pos]), s)
75 yield ('string', decode(program[s:pos]), s)
76 break
76 break
77 pos += 1
77 pos += 1
78 else:
78 else:
79 raise error.ParseError(_("unterminated string"), s)
79 raise error.ParseError(_("unterminated string"), s)
80 elif c.isalnum() or c in globchars or ord(c) > 127:
80 elif c.isalnum() or c in globchars or ord(c) > 127:
81 # gather up a symbol/keyword
81 # gather up a symbol/keyword
82 s = pos
82 s = pos
83 pos += 1
83 pos += 1
84 while pos < l: # find end of symbol
84 while pos < l: # find end of symbol
85 d = program[pos]
85 d = program[pos]
86 if not (d.isalnum() or d in globchars or ord(d) > 127):
86 if not (d.isalnum() or d in globchars or ord(d) > 127):
87 break
87 break
88 pos += 1
88 pos += 1
89 sym = program[s:pos]
89 sym = program[s:pos]
90 if sym in keywords: # operator keywords
90 if sym in keywords: # operator keywords
91 yield (sym, None, s)
91 yield (sym, None, s)
92 else:
92 else:
93 yield ('symbol', sym, s)
93 yield ('symbol', sym, s)
94 pos -= 1
94 pos -= 1
95 else:
95 else:
96 raise error.ParseError(_("syntax error"), pos)
96 raise error.ParseError(_("syntax error"), pos)
97 pos += 1
97 pos += 1
98 yield ('end', None, pos)
98 yield ('end', None, pos)
99
99
100 def parse(expr):
100 def parse(expr):
101 p = parser.parser(elements)
101 p = parser.parser(elements)
102 tree, pos = p.parse(tokenize(expr))
102 tree, pos = p.parse(tokenize(expr))
103 if pos != len(expr):
103 if pos != len(expr):
104 raise error.ParseError(_("invalid token"), pos)
104 raise error.ParseError(_("invalid token"), pos)
105 return tree
105 return tree
106
106
107 def getsymbol(x):
107 def getsymbol(x):
108 if x and x[0] == 'symbol':
108 if x and x[0] == 'symbol':
109 return x[1]
109 return x[1]
110 raise error.ParseError(_('not a symbol'))
110 raise error.ParseError(_('not a symbol'))
111
111
112 def getstring(x, err):
112 def getstring(x, err):
113 if x and (x[0] == 'string' or x[0] == 'symbol'):
113 if x and (x[0] == 'string' or x[0] == 'symbol'):
114 return x[1]
114 return x[1]
115 raise error.ParseError(err)
115 raise error.ParseError(err)
116
116
117 def _getkindpat(x, y, allkinds, err):
117 def _getkindpat(x, y, allkinds, err):
118 kind = getsymbol(x)
118 kind = getsymbol(x)
119 pat = getstring(y, err)
119 pat = getstring(y, err)
120 if kind not in allkinds:
120 if kind not in allkinds:
121 raise error.ParseError(_("invalid pattern kind: %s") % kind)
121 raise error.ParseError(_("invalid pattern kind: %s") % kind)
122 return '%s:%s' % (kind, pat)
122 return '%s:%s' % (kind, pat)
123
123
124 def getpattern(x, allkinds, err):
124 def getpattern(x, allkinds, err):
125 if x and x[0] == 'kindpat':
125 if x and x[0] == 'kindpat':
126 return _getkindpat(x[1], x[2], allkinds, err)
126 return _getkindpat(x[1], x[2], allkinds, err)
127 return getstring(x, err)
127 return getstring(x, err)
128
128
129 def getlist(x):
129 def getlist(x):
130 if not x:
130 if not x:
131 return []
131 return []
132 if x[0] == 'list':
132 if x[0] == 'list':
133 return getlist(x[1]) + [x[2]]
133 return getlist(x[1]) + [x[2]]
134 return [x]
134 return [x]
135
135
136 def getargs(x, min, max, err):
136 def getargs(x, min, max, err):
137 l = getlist(x)
137 l = getlist(x)
138 if len(l) < min or len(l) > max:
138 if len(l) < min or len(l) > max:
139 raise error.ParseError(err)
139 raise error.ParseError(err)
140 return l
140 return l
141
141
142 def getset(mctx, x):
142 def getset(mctx, x):
143 if not x:
143 if not x:
144 raise error.ParseError(_("missing argument"))
144 raise error.ParseError(_("missing argument"))
145 return methods[x[0]](mctx, *x[1:])
145 return methods[x[0]](mctx, *x[1:])
146
146
147 def stringset(mctx, x):
147 def stringset(mctx, x):
148 m = mctx.matcher([x])
148 m = mctx.matcher([x])
149 return [f for f in mctx.subset if m(f)]
149 return [f for f in mctx.subset if m(f)]
150
150
151 def kindpatset(mctx, x, y):
151 def kindpatset(mctx, x, y):
152 return stringset(mctx, _getkindpat(x, y, matchmod.allpatternkinds,
152 return stringset(mctx, _getkindpat(x, y, matchmod.allpatternkinds,
153 _("pattern must be a string")))
153 _("pattern must be a string")))
154
154
155 def andset(mctx, x, y):
155 def andset(mctx, x, y):
156 return getset(mctx.narrow(getset(mctx, x)), y)
156 return getset(mctx.narrow(getset(mctx, x)), y)
157
157
158 def orset(mctx, x, y):
158 def orset(mctx, x, y):
159 # needs optimizing
159 # needs optimizing
160 xl = getset(mctx, x)
160 xl = getset(mctx, x)
161 yl = getset(mctx, y)
161 yl = getset(mctx, y)
162 return xl + [f for f in yl if f not in xl]
162 return xl + [f for f in yl if f not in xl]
163
163
164 def notset(mctx, x):
164 def notset(mctx, x):
165 s = set(getset(mctx, x))
165 s = set(getset(mctx, x))
166 return [r for r in mctx.subset if r not in s]
166 return [r for r in mctx.subset if r not in s]
167
167
168 def minusset(mctx, x, y):
168 def minusset(mctx, x, y):
169 xl = getset(mctx, x)
169 xl = getset(mctx, x)
170 yl = set(getset(mctx, y))
170 yl = set(getset(mctx, y))
171 return [f for f in xl if f not in yl]
171 return [f for f in xl if f not in yl]
172
172
173 def negateset(mctx, x):
173 def negateset(mctx, x):
174 raise error.ParseError(_("can't use negate operator in this context"))
174 raise error.ParseError(_("can't use negate operator in this context"))
175
175
176 def listset(mctx, a, b):
176 def listset(mctx, a, b):
177 raise error.ParseError(_("can't use a list in this context"),
177 raise error.ParseError(_("can't use a list in this context"),
178 hint=_('see hg help "filesets.x or y"'))
178 hint=_('see hg help "filesets.x or y"'))
179
179
180 def func(mctx, a, b):
180 def func(mctx, a, b):
181 funcname = getsymbol(a)
181 funcname = getsymbol(a)
182 if funcname in symbols:
182 if funcname in symbols:
183 enabled = mctx._existingenabled
183 enabled = mctx._existingenabled
184 mctx._existingenabled = funcname in _existingcallers
184 mctx._existingenabled = funcname in _existingcallers
185 try:
185 try:
186 return symbols[funcname](mctx, b)
186 return symbols[funcname](mctx, b)
187 finally:
187 finally:
188 mctx._existingenabled = enabled
188 mctx._existingenabled = enabled
189
189
190 keep = lambda fn: getattr(fn, '__doc__', None) is not None
190 keep = lambda fn: getattr(fn, '__doc__', None) is not None
191
191
192 syms = [s for (s, fn) in symbols.items() if keep(fn)]
192 syms = [s for (s, fn) in symbols.items() if keep(fn)]
193 raise error.UnknownIdentifier(funcname, syms)
193 raise error.UnknownIdentifier(funcname, syms)
194
194
195 # symbols are callable like:
195 # symbols are callable like:
196 # fun(mctx, x)
196 # fun(mctx, x)
197 # with:
197 # with:
198 # mctx - current matchctx instance
198 # mctx - current matchctx instance
199 # x - argument in tree form
199 # x - argument in tree form
200 symbols = {}
200 symbols = {}
201
201
202 # filesets using matchctx.status()
202 # filesets using matchctx.status()
203 _statuscallers = set()
203 _statuscallers = set()
204
204
205 # filesets using matchctx.existing()
205 # filesets using matchctx.existing()
206 _existingcallers = set()
206 _existingcallers = set()
207
207
208 predicate = registrar.filesetpredicate()
208 predicate = registrar.filesetpredicate()
209
209
210 @predicate('modified()', callstatus=True)
210 @predicate('modified()', callstatus=True)
211 def modified(mctx, x):
211 def modified(mctx, x):
212 """File that is modified according to :hg:`status`.
212 """File that is modified according to :hg:`status`.
213 """
213 """
214 # i18n: "modified" is a keyword
214 # i18n: "modified" is a keyword
215 getargs(x, 0, 0, _("modified takes no arguments"))
215 getargs(x, 0, 0, _("modified takes no arguments"))
216 s = set(mctx.status().modified)
216 s = set(mctx.status().modified)
217 return [f for f in mctx.subset if f in s]
217 return [f for f in mctx.subset if f in s]
218
218
219 @predicate('added()', callstatus=True)
219 @predicate('added()', callstatus=True)
220 def added(mctx, x):
220 def added(mctx, x):
221 """File that is added according to :hg:`status`.
221 """File that is added according to :hg:`status`.
222 """
222 """
223 # i18n: "added" is a keyword
223 # i18n: "added" is a keyword
224 getargs(x, 0, 0, _("added takes no arguments"))
224 getargs(x, 0, 0, _("added takes no arguments"))
225 s = set(mctx.status().added)
225 s = set(mctx.status().added)
226 return [f for f in mctx.subset if f in s]
226 return [f for f in mctx.subset if f in s]
227
227
228 @predicate('removed()', callstatus=True)
228 @predicate('removed()', callstatus=True)
229 def removed(mctx, x):
229 def removed(mctx, x):
230 """File that is removed according to :hg:`status`.
230 """File that is removed according to :hg:`status`.
231 """
231 """
232 # i18n: "removed" is a keyword
232 # i18n: "removed" is a keyword
233 getargs(x, 0, 0, _("removed takes no arguments"))
233 getargs(x, 0, 0, _("removed takes no arguments"))
234 s = set(mctx.status().removed)
234 s = set(mctx.status().removed)
235 return [f for f in mctx.subset if f in s]
235 return [f for f in mctx.subset if f in s]
236
236
237 @predicate('deleted()', callstatus=True)
237 @predicate('deleted()', callstatus=True)
238 def deleted(mctx, x):
238 def deleted(mctx, x):
239 """Alias for ``missing()``.
239 """Alias for ``missing()``.
240 """
240 """
241 # i18n: "deleted" is a keyword
241 # i18n: "deleted" is a keyword
242 getargs(x, 0, 0, _("deleted takes no arguments"))
242 getargs(x, 0, 0, _("deleted takes no arguments"))
243 s = set(mctx.status().deleted)
243 s = set(mctx.status().deleted)
244 return [f for f in mctx.subset if f in s]
244 return [f for f in mctx.subset if f in s]
245
245
246 @predicate('missing()', callstatus=True)
246 @predicate('missing()', callstatus=True)
247 def missing(mctx, x):
247 def missing(mctx, x):
248 """File that is missing according to :hg:`status`.
248 """File that is missing according to :hg:`status`.
249 """
249 """
250 # i18n: "missing" is a keyword
250 # i18n: "missing" is a keyword
251 getargs(x, 0, 0, _("missing takes no arguments"))
251 getargs(x, 0, 0, _("missing takes no arguments"))
252 s = set(mctx.status().deleted)
252 s = set(mctx.status().deleted)
253 return [f for f in mctx.subset if f in s]
253 return [f for f in mctx.subset if f in s]
254
254
255 @predicate('unknown()', callstatus=True)
255 @predicate('unknown()', callstatus=True)
256 def unknown(mctx, x):
256 def unknown(mctx, x):
257 """File that is unknown according to :hg:`status`. These files will only be
257 """File that is unknown according to :hg:`status`. These files will only be
258 considered if this predicate is used.
258 considered if this predicate is used.
259 """
259 """
260 # i18n: "unknown" is a keyword
260 # i18n: "unknown" is a keyword
261 getargs(x, 0, 0, _("unknown takes no arguments"))
261 getargs(x, 0, 0, _("unknown takes no arguments"))
262 s = set(mctx.status().unknown)
262 s = set(mctx.status().unknown)
263 return [f for f in mctx.subset if f in s]
263 return [f for f in mctx.subset if f in s]
264
264
265 @predicate('ignored()', callstatus=True)
265 @predicate('ignored()', callstatus=True)
266 def ignored(mctx, x):
266 def ignored(mctx, x):
267 """File that is ignored according to :hg:`status`. These files will only be
267 """File that is ignored according to :hg:`status`. These files will only be
268 considered if this predicate is used.
268 considered if this predicate is used.
269 """
269 """
270 # i18n: "ignored" is a keyword
270 # i18n: "ignored" is a keyword
271 getargs(x, 0, 0, _("ignored takes no arguments"))
271 getargs(x, 0, 0, _("ignored takes no arguments"))
272 s = set(mctx.status().ignored)
272 s = set(mctx.status().ignored)
273 return [f for f in mctx.subset if f in s]
273 return [f for f in mctx.subset if f in s]
274
274
275 @predicate('clean()', callstatus=True)
275 @predicate('clean()', callstatus=True)
276 def clean(mctx, x):
276 def clean(mctx, x):
277 """File that is clean according to :hg:`status`.
277 """File that is clean according to :hg:`status`.
278 """
278 """
279 # i18n: "clean" is a keyword
279 # i18n: "clean" is a keyword
280 getargs(x, 0, 0, _("clean takes no arguments"))
280 getargs(x, 0, 0, _("clean takes no arguments"))
281 s = set(mctx.status().clean)
281 s = set(mctx.status().clean)
282 return [f for f in mctx.subset if f in s]
282 return [f for f in mctx.subset if f in s]
283
283
284 @predicate('binary()', callexisting=True)
284 @predicate('binary()', callexisting=True)
285 def binary(mctx, x):
285 def binary(mctx, x):
286 """File that appears to be binary (contains NUL bytes).
286 """File that appears to be binary (contains NUL bytes).
287 """
287 """
288 # i18n: "binary" is a keyword
288 # i18n: "binary" is a keyword
289 getargs(x, 0, 0, _("binary takes no arguments"))
289 getargs(x, 0, 0, _("binary takes no arguments"))
290 return [f for f in mctx.existing() if mctx.ctx[f].isbinary()]
290 return [f for f in mctx.existing() if mctx.ctx[f].isbinary()]
291
291
292 @predicate('exec()', callexisting=True)
292 @predicate('exec()', callexisting=True)
293 def exec_(mctx, x):
293 def exec_(mctx, x):
294 """File that is marked as executable.
294 """File that is marked as executable.
295 """
295 """
296 # i18n: "exec" is a keyword
296 # i18n: "exec" is a keyword
297 getargs(x, 0, 0, _("exec takes no arguments"))
297 getargs(x, 0, 0, _("exec takes no arguments"))
298 return [f for f in mctx.existing() if mctx.ctx.flags(f) == 'x']
298 return [f for f in mctx.existing() if mctx.ctx.flags(f) == 'x']
299
299
300 @predicate('symlink()', callexisting=True)
300 @predicate('symlink()', callexisting=True)
301 def symlink(mctx, x):
301 def symlink(mctx, x):
302 """File that is marked as a symlink.
302 """File that is marked as a symlink.
303 """
303 """
304 # i18n: "symlink" is a keyword
304 # i18n: "symlink" is a keyword
305 getargs(x, 0, 0, _("symlink takes no arguments"))
305 getargs(x, 0, 0, _("symlink takes no arguments"))
306 return [f for f in mctx.existing() if mctx.ctx.flags(f) == 'l']
306 return [f for f in mctx.existing() if mctx.ctx.flags(f) == 'l']
307
307
308 @predicate('resolved()')
308 @predicate('resolved()')
309 def resolved(mctx, x):
309 def resolved(mctx, x):
310 """File that is marked resolved according to :hg:`resolve -l`.
310 """File that is marked resolved according to :hg:`resolve -l`.
311 """
311 """
312 # i18n: "resolved" is a keyword
312 # i18n: "resolved" is a keyword
313 getargs(x, 0, 0, _("resolved takes no arguments"))
313 getargs(x, 0, 0, _("resolved takes no arguments"))
314 if mctx.ctx.rev() is not None:
314 if mctx.ctx.rev() is not None:
315 return []
315 return []
316 ms = merge.mergestate.read(mctx.ctx.repo())
316 ms = merge.mergestate.read(mctx.ctx.repo())
317 return [f for f in mctx.subset if f in ms and ms[f] == 'r']
317 return [f for f in mctx.subset if f in ms and ms[f] == 'r']
318
318
319 @predicate('unresolved()')
319 @predicate('unresolved()')
320 def unresolved(mctx, x):
320 def unresolved(mctx, x):
321 """File that is marked unresolved according to :hg:`resolve -l`.
321 """File that is marked unresolved according to :hg:`resolve -l`.
322 """
322 """
323 # i18n: "unresolved" is a keyword
323 # i18n: "unresolved" is a keyword
324 getargs(x, 0, 0, _("unresolved takes no arguments"))
324 getargs(x, 0, 0, _("unresolved takes no arguments"))
325 if mctx.ctx.rev() is not None:
325 if mctx.ctx.rev() is not None:
326 return []
326 return []
327 ms = merge.mergestate.read(mctx.ctx.repo())
327 ms = merge.mergestate.read(mctx.ctx.repo())
328 return [f for f in mctx.subset if f in ms and ms[f] == 'u']
328 return [f for f in mctx.subset if f in ms and ms[f] == 'u']
329
329
330 @predicate('hgignore()')
330 @predicate('hgignore()')
331 def hgignore(mctx, x):
331 def hgignore(mctx, x):
332 """File that matches the active .hgignore pattern.
332 """File that matches the active .hgignore pattern.
333 """
333 """
334 # i18n: "hgignore" is a keyword
334 # i18n: "hgignore" is a keyword
335 getargs(x, 0, 0, _("hgignore takes no arguments"))
335 getargs(x, 0, 0, _("hgignore takes no arguments"))
336 ignore = mctx.ctx.repo().dirstate._ignore
336 ignore = mctx.ctx.repo().dirstate._ignore
337 return [f for f in mctx.subset if ignore(f)]
337 return [f for f in mctx.subset if ignore(f)]
338
338
339 @predicate('portable()')
339 @predicate('portable()')
340 def portable(mctx, x):
340 def portable(mctx, x):
341 """File that has a portable name. (This doesn't include filenames with case
341 """File that has a portable name. (This doesn't include filenames with case
342 collisions.)
342 collisions.)
343 """
343 """
344 # i18n: "portable" is a keyword
344 # i18n: "portable" is a keyword
345 getargs(x, 0, 0, _("portable takes no arguments"))
345 getargs(x, 0, 0, _("portable takes no arguments"))
346 checkwinfilename = util.checkwinfilename
346 checkwinfilename = util.checkwinfilename
347 return [f for f in mctx.subset if checkwinfilename(f) is None]
347 return [f for f in mctx.subset if checkwinfilename(f) is None]
348
348
349 @predicate('grep(regex)', callexisting=True)
349 @predicate('grep(regex)', callexisting=True)
350 def grep(mctx, x):
350 def grep(mctx, x):
351 """File contains the given regular expression.
351 """File contains the given regular expression.
352 """
352 """
353 try:
353 try:
354 # i18n: "grep" is a keyword
354 # i18n: "grep" is a keyword
355 r = re.compile(getstring(x, _("grep requires a pattern")))
355 r = re.compile(getstring(x, _("grep requires a pattern")))
356 except re.error as e:
356 except re.error as e:
357 raise error.ParseError(_('invalid match pattern: %s') %
357 raise error.ParseError(_('invalid match pattern: %s') %
358 stringutil.forcebytestr(e))
358 stringutil.forcebytestr(e))
359 return [f for f in mctx.existing() if r.search(mctx.ctx[f].data())]
359 return [f for f in mctx.existing() if r.search(mctx.ctx[f].data())]
360
360
361 def _sizetomax(s):
361 def _sizetomax(s):
362 try:
362 try:
363 s = s.strip().lower()
363 s = s.strip().lower()
364 for k, v in util._sizeunits:
364 for k, v in util._sizeunits:
365 if s.endswith(k):
365 if s.endswith(k):
366 # max(4k) = 5k - 1, max(4.5k) = 4.6k - 1
366 # max(4k) = 5k - 1, max(4.5k) = 4.6k - 1
367 n = s[:-len(k)]
367 n = s[:-len(k)]
368 inc = 1.0
368 inc = 1.0
369 if "." in n:
369 if "." in n:
370 inc /= 10 ** len(n.split(".")[1])
370 inc /= 10 ** len(n.split(".")[1])
371 return int((float(n) + inc) * v) - 1
371 return int((float(n) + inc) * v) - 1
372 # no extension, this is a precise value
372 # no extension, this is a precise value
373 return int(s)
373 return int(s)
374 except ValueError:
374 except ValueError:
375 raise error.ParseError(_("couldn't parse size: %s") % s)
375 raise error.ParseError(_("couldn't parse size: %s") % s)
376
376
377 def sizematcher(x):
377 def sizematcher(x):
378 """Return a function(size) -> bool from the ``size()`` expression"""
378 """Return a function(size) -> bool from the ``size()`` expression"""
379
379
380 # i18n: "size" is a keyword
380 # i18n: "size" is a keyword
381 expr = getstring(x, _("size requires an expression")).strip()
381 expr = getstring(x, _("size requires an expression")).strip()
382 if '-' in expr: # do we have a range?
382 if '-' in expr: # do we have a range?
383 a, b = expr.split('-', 1)
383 a, b = expr.split('-', 1)
384 a = util.sizetoint(a)
384 a = util.sizetoint(a)
385 b = util.sizetoint(b)
385 b = util.sizetoint(b)
386 return lambda x: x >= a and x <= b
386 return lambda x: x >= a and x <= b
387 elif expr.startswith("<="):
387 elif expr.startswith("<="):
388 a = util.sizetoint(expr[2:])
388 a = util.sizetoint(expr[2:])
389 return lambda x: x <= a
389 return lambda x: x <= a
390 elif expr.startswith("<"):
390 elif expr.startswith("<"):
391 a = util.sizetoint(expr[1:])
391 a = util.sizetoint(expr[1:])
392 return lambda x: x < a
392 return lambda x: x < a
393 elif expr.startswith(">="):
393 elif expr.startswith(">="):
394 a = util.sizetoint(expr[2:])
394 a = util.sizetoint(expr[2:])
395 return lambda x: x >= a
395 return lambda x: x >= a
396 elif expr.startswith(">"):
396 elif expr.startswith(">"):
397 a = util.sizetoint(expr[1:])
397 a = util.sizetoint(expr[1:])
398 return lambda x: x > a
398 return lambda x: x > a
399 else:
399 else:
400 a = util.sizetoint(expr)
400 a = util.sizetoint(expr)
401 b = _sizetomax(expr)
401 b = _sizetomax(expr)
402 return lambda x: x >= a and x <= b
402 return lambda x: x >= a and x <= b
403
403
404 @predicate('size(expression)', callexisting=True)
404 @predicate('size(expression)', callexisting=True)
405 def size(mctx, x):
405 def size(mctx, x):
406 """File size matches the given expression. Examples:
406 """File size matches the given expression. Examples:
407
407
408 - size('1k') - files from 1024 to 2047 bytes
408 - size('1k') - files from 1024 to 2047 bytes
409 - size('< 20k') - files less than 20480 bytes
409 - size('< 20k') - files less than 20480 bytes
410 - size('>= .5MB') - files at least 524288 bytes
410 - size('>= .5MB') - files at least 524288 bytes
411 - size('4k - 1MB') - files from 4096 bytes to 1048576 bytes
411 - size('4k - 1MB') - files from 4096 bytes to 1048576 bytes
412 """
412 """
413 m = sizematcher(x)
413 m = sizematcher(x)
414 return [f for f in mctx.existing() if m(mctx.ctx[f].size())]
414 return [f for f in mctx.existing() if m(mctx.ctx[f].size())]
415
415
416 @predicate('encoding(name)', callexisting=True)
416 @predicate('encoding(name)', callexisting=True)
417 def encoding(mctx, x):
417 def encoding(mctx, x):
418 """File can be successfully decoded with the given character
418 """File can be successfully decoded with the given character
419 encoding. May not be useful for encodings other than ASCII and
419 encoding. May not be useful for encodings other than ASCII and
420 UTF-8.
420 UTF-8.
421 """
421 """
422
422
423 # i18n: "encoding" is a keyword
423 # i18n: "encoding" is a keyword
424 enc = getstring(x, _("encoding requires an encoding name"))
424 enc = getstring(x, _("encoding requires an encoding name"))
425
425
426 s = []
426 s = []
427 for f in mctx.existing():
427 for f in mctx.existing():
428 d = mctx.ctx[f].data()
428 d = mctx.ctx[f].data()
429 try:
429 try:
430 d.decode(pycompat.sysstr(enc))
430 d.decode(pycompat.sysstr(enc))
431 except LookupError:
431 except LookupError:
432 raise error.Abort(_("unknown encoding '%s'") % enc)
432 raise error.Abort(_("unknown encoding '%s'") % enc)
433 except UnicodeDecodeError:
433 except UnicodeDecodeError:
434 continue
434 continue
435 s.append(f)
435 s.append(f)
436
436
437 return s
437 return s
438
438
439 @predicate('eol(style)', callexisting=True)
439 @predicate('eol(style)', callexisting=True)
440 def eol(mctx, x):
440 def eol(mctx, x):
441 """File contains newlines of the given style (dos, unix, mac). Binary
441 """File contains newlines of the given style (dos, unix, mac). Binary
442 files are excluded, files with mixed line endings match multiple
442 files are excluded, files with mixed line endings match multiple
443 styles.
443 styles.
444 """
444 """
445
445
446 # i18n: "eol" is a keyword
446 # i18n: "eol" is a keyword
447 enc = getstring(x, _("eol requires a style name"))
447 enc = getstring(x, _("eol requires a style name"))
448
448
449 s = []
449 s = []
450 for f in mctx.existing():
450 for f in mctx.existing():
451 fctx = mctx.ctx[f]
451 fctx = mctx.ctx[f]
452 if fctx.isbinary():
452 if fctx.isbinary():
453 continue
453 continue
454 d = fctx.data()
454 d = fctx.data()
455 if (enc == 'dos' or enc == 'win') and '\r\n' in d:
455 if (enc == 'dos' or enc == 'win') and '\r\n' in d:
456 s.append(f)
456 s.append(f)
457 elif enc == 'unix' and re.search('(?<!\r)\n', d):
457 elif enc == 'unix' and re.search('(?<!\r)\n', d):
458 s.append(f)
458 s.append(f)
459 elif enc == 'mac' and re.search('\r(?!\n)', d):
459 elif enc == 'mac' and re.search('\r(?!\n)', d):
460 s.append(f)
460 s.append(f)
461 return s
461 return s
462
462
463 @predicate('copied()')
463 @predicate('copied()')
464 def copied(mctx, x):
464 def copied(mctx, x):
465 """File that is recorded as being copied.
465 """File that is recorded as being copied.
466 """
466 """
467 # i18n: "copied" is a keyword
467 # i18n: "copied" is a keyword
468 getargs(x, 0, 0, _("copied takes no arguments"))
468 getargs(x, 0, 0, _("copied takes no arguments"))
469 s = []
469 s = []
470 for f in mctx.subset:
470 for f in mctx.subset:
471 if f in mctx.ctx:
471 if f in mctx.ctx:
472 p = mctx.ctx[f].parents()
472 p = mctx.ctx[f].parents()
473 if p and p[0].path() != f:
473 if p and p[0].path() != f:
474 s.append(f)
474 s.append(f)
475 return s
475 return s
476
476
477 @predicate('revs(revs, pattern)')
477 @predicate('revs(revs, pattern)')
478 def revs(mctx, x):
478 def revs(mctx, x):
479 """Evaluate set in the specified revisions. If the revset match multiple
479 """Evaluate set in the specified revisions. If the revset match multiple
480 revs, this will return file matching pattern in any of the revision.
480 revs, this will return file matching pattern in any of the revision.
481 """
481 """
482 # i18n: "revs" is a keyword
482 # i18n: "revs" is a keyword
483 r, x = getargs(x, 2, 2, _("revs takes two arguments"))
483 r, x = getargs(x, 2, 2, _("revs takes two arguments"))
484 # i18n: "revs" is a keyword
484 # i18n: "revs" is a keyword
485 revspec = getstring(r, _("first argument to revs must be a revision"))
485 revspec = getstring(r, _("first argument to revs must be a revision"))
486 repo = mctx.ctx.repo()
486 repo = mctx.ctx.repo()
487 revs = scmutil.revrange(repo, [revspec])
487 revs = scmutil.revrange(repo, [revspec])
488
488
489 found = set()
489 found = set()
490 result = []
490 result = []
491 for r in revs:
491 for r in revs:
492 ctx = repo[r]
492 ctx = repo[r]
493 for f in getset(mctx.switch(ctx, _buildstatus(ctx, x)), x):
493 for f in getset(mctx.switch(ctx, _buildstatus(ctx, x)), x):
494 if f not in found:
494 if f not in found:
495 found.add(f)
495 found.add(f)
496 result.append(f)
496 result.append(f)
497 return result
497 return result
498
498
499 @predicate('status(base, rev, pattern)')
499 @predicate('status(base, rev, pattern)')
500 def status(mctx, x):
500 def status(mctx, x):
501 """Evaluate predicate using status change between ``base`` and
501 """Evaluate predicate using status change between ``base`` and
502 ``rev``. Examples:
502 ``rev``. Examples:
503
503
504 - ``status(3, 7, added())`` - matches files added from "3" to "7"
504 - ``status(3, 7, added())`` - matches files added from "3" to "7"
505 """
505 """
506 repo = mctx.ctx.repo()
506 repo = mctx.ctx.repo()
507 # i18n: "status" is a keyword
507 # i18n: "status" is a keyword
508 b, r, x = getargs(x, 3, 3, _("status takes three arguments"))
508 b, r, x = getargs(x, 3, 3, _("status takes three arguments"))
509 # i18n: "status" is a keyword
509 # i18n: "status" is a keyword
510 baseerr = _("first argument to status must be a revision")
510 baseerr = _("first argument to status must be a revision")
511 baserevspec = getstring(b, baseerr)
511 baserevspec = getstring(b, baseerr)
512 if not baserevspec:
512 if not baserevspec:
513 raise error.ParseError(baseerr)
513 raise error.ParseError(baseerr)
514 reverr = _("second argument to status must be a revision")
514 reverr = _("second argument to status must be a revision")
515 revspec = getstring(r, reverr)
515 revspec = getstring(r, reverr)
516 if not revspec:
516 if not revspec:
517 raise error.ParseError(reverr)
517 raise error.ParseError(reverr)
518 basectx, ctx = scmutil.revpair(repo, [baserevspec, revspec])
518 basectx, ctx = scmutil.revpair(repo, [baserevspec, revspec])
519 return getset(mctx.switch(ctx, _buildstatus(ctx, x, basectx=basectx)), x)
519 return getset(mctx.switch(ctx, _buildstatus(ctx, x, basectx=basectx)), x)
520
520
521 @predicate('subrepo([pattern])')
521 @predicate('subrepo([pattern])')
522 def subrepo(mctx, x):
522 def subrepo(mctx, x):
523 """Subrepositories whose paths match the given pattern.
523 """Subrepositories whose paths match the given pattern.
524 """
524 """
525 # i18n: "subrepo" is a keyword
525 # i18n: "subrepo" is a keyword
526 getargs(x, 0, 1, _("subrepo takes at most one argument"))
526 getargs(x, 0, 1, _("subrepo takes at most one argument"))
527 ctx = mctx.ctx
527 ctx = mctx.ctx
528 sstate = sorted(ctx.substate)
528 sstate = sorted(ctx.substate)
529 if x:
529 if x:
530 pat = getpattern(x, matchmod.allpatternkinds,
530 pat = getpattern(x, matchmod.allpatternkinds,
531 # i18n: "subrepo" is a keyword
531 # i18n: "subrepo" is a keyword
532 _("subrepo requires a pattern or no arguments"))
532 _("subrepo requires a pattern or no arguments"))
533 fast = not matchmod.patkind(pat)
533 fast = not matchmod.patkind(pat)
534 if fast:
534 if fast:
535 def m(s):
535 def m(s):
536 return (s == pat)
536 return (s == pat)
537 else:
537 else:
538 m = matchmod.match(ctx.repo().root, '', [pat], ctx=ctx)
538 m = matchmod.match(ctx.repo().root, '', [pat], ctx=ctx)
539 return [sub for sub in sstate if m(sub)]
539 return [sub for sub in sstate if m(sub)]
540 else:
540 else:
541 return [sub for sub in sstate]
541 return [sub for sub in sstate]
542
542
543 methods = {
543 methods = {
544 'string': stringset,
544 'string': stringset,
545 'symbol': stringset,
545 'symbol': stringset,
546 'kindpat': kindpatset,
546 'kindpat': kindpatset,
547 'and': andset,
547 'and': andset,
548 'or': orset,
548 'or': orset,
549 'minus': minusset,
549 'minus': minusset,
550 'negate': negateset,
550 'negate': negateset,
551 'list': listset,
551 'list': listset,
552 'group': getset,
552 'group': getset,
553 'not': notset,
553 'not': notset,
554 'func': func,
554 'func': func,
555 }
555 }
556
556
557 class matchctx(object):
557 class matchctx(object):
558 def __init__(self, ctx, subset, status=None):
558 def __init__(self, ctx, subset, status=None):
559 self.ctx = ctx
559 self.ctx = ctx
560 self.subset = subset
560 self.subset = subset
561 self._status = status
561 self._status = status
562 self._existingenabled = False
562 self._existingenabled = False
563 def status(self):
563 def status(self):
564 return self._status
564 return self._status
565 def matcher(self, patterns):
565 def matcher(self, patterns):
566 return self.ctx.match(patterns)
566 return self.ctx.match(patterns)
567 def filter(self, files):
567 def filter(self, files):
568 return [f for f in files if f in self.subset]
568 return [f for f in files if f in self.subset]
569 def existing(self):
569 def existing(self):
570 if not self._existingenabled:
570 if not self._existingenabled:
571 raise error.ProgrammingError('unexpected existing() invocation')
571 raise error.ProgrammingError('unexpected existing() invocation')
572 if self._status is not None:
572 if self._status is not None:
573 removed = set(self._status[3])
573 removed = set(self._status[3])
574 unknown = set(self._status[4] + self._status[5])
574 unknown = set(self._status[4] + self._status[5])
575 else:
575 else:
576 removed = set()
576 removed = set()
577 unknown = set()
577 unknown = set()
578 return (f for f in self.subset
578 return (f for f in self.subset
579 if (f in self.ctx and f not in removed) or f in unknown)
579 if (f in self.ctx and f not in removed) or f in unknown)
580 def narrow(self, files):
580 def narrow(self, files):
581 return matchctx(self.ctx, self.filter(files), self._status)
581 return matchctx(self.ctx, self.filter(files), self._status)
582 def switch(self, ctx, status=None):
582 def switch(self, ctx, status=None):
583 subset = self.filter(_buildsubset(ctx, status))
583 subset = self.filter(_buildsubset(ctx, status))
584 return matchctx(ctx, subset, status)
584 return matchctx(ctx, subset, status)
585
585
586 class fullmatchctx(matchctx):
586 class fullmatchctx(matchctx):
587 """A match context where any files in any revisions should be valid"""
587 """A match context where any files in any revisions should be valid"""
588
588
589 def __init__(self, ctx, status=None):
589 def __init__(self, ctx, status=None):
590 subset = _buildsubset(ctx, status)
590 subset = _buildsubset(ctx, status)
591 super(fullmatchctx, self).__init__(ctx, subset, status)
591 super(fullmatchctx, self).__init__(ctx, subset, status)
592 def switch(self, ctx, status=None):
592 def switch(self, ctx, status=None):
593 return fullmatchctx(ctx, status)
593 return fullmatchctx(ctx, status)
594
594
595 # filesets using matchctx.switch()
595 # filesets using matchctx.switch()
596 _switchcallers = [
596 _switchcallers = [
597 'revs',
597 'revs',
598 'status',
598 'status',
599 ]
599 ]
600
600
601 def _intree(funcs, tree):
601 def _intree(funcs, tree):
602 if isinstance(tree, tuple):
602 if isinstance(tree, tuple):
603 if tree[0] == 'func' and tree[1][0] == 'symbol':
603 if tree[0] == 'func' and tree[1][0] == 'symbol':
604 if tree[1][1] in funcs:
604 if tree[1][1] in funcs:
605 return True
605 return True
606 if tree[1][1] in _switchcallers:
606 if tree[1][1] in _switchcallers:
607 # arguments won't be evaluated in the current context
607 # arguments won't be evaluated in the current context
608 return False
608 return False
609 for s in tree[1:]:
609 for s in tree[1:]:
610 if _intree(funcs, s):
610 if _intree(funcs, s):
611 return True
611 return True
612 return False
612 return False
613
613
614 def _buildsubset(ctx, status):
614 def _buildsubset(ctx, status):
615 if status:
615 if status:
616 subset = []
616 subset = []
617 for c in status:
617 for c in status:
618 subset.extend(c)
618 subset.extend(c)
619 return subset
619 return subset
620 else:
620 else:
621 return list(ctx.walk(ctx.match([])))
621 return list(ctx.walk(ctx.match([])))
622
622
623 def getfileset(ctx, expr):
623 def match(ctx, expr, badfn=None):
624 """Create a matcher for a single fileset expression"""
625 repo = ctx.repo()
624 tree = parse(expr)
626 tree = parse(expr)
625 return getset(fullmatchctx(ctx, _buildstatus(ctx, tree)), tree)
627 fset = getset(fullmatchctx(ctx, _buildstatus(ctx, tree)), tree)
628 return matchmod.predicatematcher(repo.root, repo.getcwd(),
629 fset.__contains__,
630 predrepr='fileset', badfn=badfn)
626
631
627 def _buildstatus(ctx, tree, basectx=None):
632 def _buildstatus(ctx, tree, basectx=None):
628 # do we need status info?
633 # do we need status info?
629
634
630 # temporaty boolean to simplify the next conditional
635 # temporaty boolean to simplify the next conditional
631 purewdir = ctx.rev() is None and basectx is None
636 purewdir = ctx.rev() is None and basectx is None
632
637
633 if (_intree(_statuscallers, tree) or
638 if (_intree(_statuscallers, tree) or
634 # Using matchctx.existing() on a workingctx requires us to check
639 # Using matchctx.existing() on a workingctx requires us to check
635 # for deleted files.
640 # for deleted files.
636 (purewdir and _intree(_existingcallers, tree))):
641 (purewdir and _intree(_existingcallers, tree))):
637 unknown = _intree(['unknown'], tree)
642 unknown = _intree(['unknown'], tree)
638 ignored = _intree(['ignored'], tree)
643 ignored = _intree(['ignored'], tree)
639
644
640 r = ctx.repo()
645 r = ctx.repo()
641 if basectx is None:
646 if basectx is None:
642 basectx = ctx.p1()
647 basectx = ctx.p1()
643 return r.status(basectx, ctx,
648 return r.status(basectx, ctx,
644 unknown=unknown, ignored=ignored, clean=True)
649 unknown=unknown, ignored=ignored, clean=True)
645 else:
650 else:
646 return None
651 return None
647
652
648 def prettyformat(tree):
653 def prettyformat(tree):
649 return parser.prettyformat(tree, ('string', 'symbol'))
654 return parser.prettyformat(tree, ('string', 'symbol'))
650
655
651 def loadpredicate(ui, extname, registrarobj):
656 def loadpredicate(ui, extname, registrarobj):
652 """Load fileset predicates from specified registrarobj
657 """Load fileset predicates from specified registrarobj
653 """
658 """
654 for name, func in registrarobj._table.iteritems():
659 for name, func in registrarobj._table.iteritems():
655 symbols[name] = func
660 symbols[name] = func
656 if func._callstatus:
661 if func._callstatus:
657 _statuscallers.add(name)
662 _statuscallers.add(name)
658 if func._callexisting:
663 if func._callexisting:
659 _existingcallers.add(name)
664 _existingcallers.add(name)
660
665
661 # load built-in predicates explicitly to setup _statuscallers/_existingcallers
666 # load built-in predicates explicitly to setup _statuscallers/_existingcallers
662 loadpredicate(None, None, predicate)
667 loadpredicate(None, None, predicate)
663
668
664 # tell hggettext to extract docstrings from these functions:
669 # tell hggettext to extract docstrings from these functions:
665 i18nfunctions = symbols.values()
670 i18nfunctions = symbols.values()
@@ -1,1133 +1,1132 b''
1 # match.py - filename matching
1 # match.py - filename matching
2 #
2 #
3 # Copyright 2008, 2009 Matt Mackall <mpm@selenic.com> and others
3 # Copyright 2008, 2009 Matt Mackall <mpm@selenic.com> and others
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, print_function
8 from __future__ import absolute_import, print_function
9
9
10 import copy
10 import copy
11 import os
11 import os
12 import re
12 import re
13
13
14 from .i18n import _
14 from .i18n import _
15 from . import (
15 from . import (
16 encoding,
16 encoding,
17 error,
17 error,
18 pathutil,
18 pathutil,
19 pycompat,
19 pycompat,
20 util,
20 util,
21 )
21 )
22 from .utils import (
22 from .utils import (
23 stringutil,
23 stringutil,
24 )
24 )
25
25
26 allpatternkinds = ('re', 'glob', 'path', 'relglob', 'relpath', 'relre',
26 allpatternkinds = ('re', 'glob', 'path', 'relglob', 'relpath', 'relre',
27 'listfile', 'listfile0', 'set', 'include', 'subinclude',
27 'listfile', 'listfile0', 'set', 'include', 'subinclude',
28 'rootfilesin')
28 'rootfilesin')
29 cwdrelativepatternkinds = ('relpath', 'glob')
29 cwdrelativepatternkinds = ('relpath', 'glob')
30
30
31 propertycache = util.propertycache
31 propertycache = util.propertycache
32
32
33 def _rematcher(regex):
33 def _rematcher(regex):
34 '''compile the regexp with the best available regexp engine and return a
34 '''compile the regexp with the best available regexp engine and return a
35 matcher function'''
35 matcher function'''
36 m = util.re.compile(regex)
36 m = util.re.compile(regex)
37 try:
37 try:
38 # slightly faster, provided by facebook's re2 bindings
38 # slightly faster, provided by facebook's re2 bindings
39 return m.test_match
39 return m.test_match
40 except AttributeError:
40 except AttributeError:
41 return m.match
41 return m.match
42
42
43 def _expandsets(kindpats, ctx, listsubrepos):
43 def _expandsets(root, cwd, kindpats, ctx, listsubrepos, badfn):
44 '''Returns the kindpats list with the 'set' patterns expanded.'''
44 '''Returns the kindpats list with the 'set' patterns expanded to matchers'''
45 fset = set()
45 matchers = []
46 other = []
46 other = []
47
47
48 for kind, pat, source in kindpats:
48 for kind, pat, source in kindpats:
49 if kind == 'set':
49 if kind == 'set':
50 if not ctx:
50 if not ctx:
51 raise error.ProgrammingError("fileset expression with no "
51 raise error.ProgrammingError("fileset expression with no "
52 "context")
52 "context")
53 s = ctx.getfileset(pat)
53 matchers.append(ctx.matchfileset(pat, badfn=badfn))
54 fset.update(s)
55
54
56 if listsubrepos:
55 if listsubrepos:
57 for subpath in ctx.substate:
56 for subpath in ctx.substate:
58 s = ctx.sub(subpath).getfileset(pat)
57 sm = ctx.sub(subpath).matchfileset(pat, badfn=badfn)
59 fset.update(subpath + '/' + f for f in s)
58 pm = prefixdirmatcher(root, cwd, subpath, sm, badfn=badfn)
59 matchers.append(pm)
60
60
61 continue
61 continue
62 other.append((kind, pat, source))
62 other.append((kind, pat, source))
63 return fset, other
63 return matchers, other
64
64
65 def _expandsubinclude(kindpats, root):
65 def _expandsubinclude(kindpats, root):
66 '''Returns the list of subinclude matcher args and the kindpats without the
66 '''Returns the list of subinclude matcher args and the kindpats without the
67 subincludes in it.'''
67 subincludes in it.'''
68 relmatchers = []
68 relmatchers = []
69 other = []
69 other = []
70
70
71 for kind, pat, source in kindpats:
71 for kind, pat, source in kindpats:
72 if kind == 'subinclude':
72 if kind == 'subinclude':
73 sourceroot = pathutil.dirname(util.normpath(source))
73 sourceroot = pathutil.dirname(util.normpath(source))
74 pat = util.pconvert(pat)
74 pat = util.pconvert(pat)
75 path = pathutil.join(sourceroot, pat)
75 path = pathutil.join(sourceroot, pat)
76
76
77 newroot = pathutil.dirname(path)
77 newroot = pathutil.dirname(path)
78 matcherargs = (newroot, '', [], ['include:%s' % path])
78 matcherargs = (newroot, '', [], ['include:%s' % path])
79
79
80 prefix = pathutil.canonpath(root, root, newroot)
80 prefix = pathutil.canonpath(root, root, newroot)
81 if prefix:
81 if prefix:
82 prefix += '/'
82 prefix += '/'
83 relmatchers.append((prefix, matcherargs))
83 relmatchers.append((prefix, matcherargs))
84 else:
84 else:
85 other.append((kind, pat, source))
85 other.append((kind, pat, source))
86
86
87 return relmatchers, other
87 return relmatchers, other
88
88
89 def _kindpatsalwaysmatch(kindpats):
89 def _kindpatsalwaysmatch(kindpats):
90 """"Checks whether the kindspats match everything, as e.g.
90 """"Checks whether the kindspats match everything, as e.g.
91 'relpath:.' does.
91 'relpath:.' does.
92 """
92 """
93 for kind, pat, source in kindpats:
93 for kind, pat, source in kindpats:
94 if pat != '' or kind not in ['relpath', 'glob']:
94 if pat != '' or kind not in ['relpath', 'glob']:
95 return False
95 return False
96 return True
96 return True
97
97
98 def _buildkindpatsmatcher(matchercls, root, cwd, kindpats, ctx=None,
98 def _buildkindpatsmatcher(matchercls, root, cwd, kindpats, ctx=None,
99 listsubrepos=False, badfn=None):
99 listsubrepos=False, badfn=None):
100 fset, kindpats = _expandsets(kindpats, ctx, listsubrepos)
101 matchers = []
100 matchers = []
101 fms, kindpats = _expandsets(root, cwd, kindpats, ctx=ctx,
102 listsubrepos=listsubrepos, badfn=badfn)
102 if kindpats:
103 if kindpats:
103 m = matchercls(root, cwd, kindpats, listsubrepos=listsubrepos,
104 m = matchercls(root, cwd, kindpats, listsubrepos=listsubrepos,
104 badfn=badfn)
105 badfn=badfn)
105 matchers.append(m)
106 matchers.append(m)
106 if fset:
107 if fms:
107 m = predicatematcher(root, cwd, fset.__contains__,
108 matchers.extend(fms)
108 predrepr='fileset', badfn=badfn)
109 matchers.append(m)
110 if not matchers:
109 if not matchers:
111 return nevermatcher(root, cwd, badfn=badfn)
110 return nevermatcher(root, cwd, badfn=badfn)
112 if len(matchers) == 1:
111 if len(matchers) == 1:
113 return matchers[0]
112 return matchers[0]
114 return unionmatcher(matchers)
113 return unionmatcher(matchers)
115
114
116 def match(root, cwd, patterns=None, include=None, exclude=None, default='glob',
115 def match(root, cwd, patterns=None, include=None, exclude=None, default='glob',
117 exact=False, auditor=None, ctx=None, listsubrepos=False, warn=None,
116 exact=False, auditor=None, ctx=None, listsubrepos=False, warn=None,
118 badfn=None, icasefs=False):
117 badfn=None, icasefs=False):
119 """build an object to match a set of file patterns
118 """build an object to match a set of file patterns
120
119
121 arguments:
120 arguments:
122 root - the canonical root of the tree you're matching against
121 root - the canonical root of the tree you're matching against
123 cwd - the current working directory, if relevant
122 cwd - the current working directory, if relevant
124 patterns - patterns to find
123 patterns - patterns to find
125 include - patterns to include (unless they are excluded)
124 include - patterns to include (unless they are excluded)
126 exclude - patterns to exclude (even if they are included)
125 exclude - patterns to exclude (even if they are included)
127 default - if a pattern in patterns has no explicit type, assume this one
126 default - if a pattern in patterns has no explicit type, assume this one
128 exact - patterns are actually filenames (include/exclude still apply)
127 exact - patterns are actually filenames (include/exclude still apply)
129 warn - optional function used for printing warnings
128 warn - optional function used for printing warnings
130 badfn - optional bad() callback for this matcher instead of the default
129 badfn - optional bad() callback for this matcher instead of the default
131 icasefs - make a matcher for wdir on case insensitive filesystems, which
130 icasefs - make a matcher for wdir on case insensitive filesystems, which
132 normalizes the given patterns to the case in the filesystem
131 normalizes the given patterns to the case in the filesystem
133
132
134 a pattern is one of:
133 a pattern is one of:
135 'glob:<glob>' - a glob relative to cwd
134 'glob:<glob>' - a glob relative to cwd
136 're:<regexp>' - a regular expression
135 're:<regexp>' - a regular expression
137 'path:<path>' - a path relative to repository root, which is matched
136 'path:<path>' - a path relative to repository root, which is matched
138 recursively
137 recursively
139 'rootfilesin:<path>' - a path relative to repository root, which is
138 'rootfilesin:<path>' - a path relative to repository root, which is
140 matched non-recursively (will not match subdirectories)
139 matched non-recursively (will not match subdirectories)
141 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
140 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
142 'relpath:<path>' - a path relative to cwd
141 'relpath:<path>' - a path relative to cwd
143 'relre:<regexp>' - a regexp that needn't match the start of a name
142 'relre:<regexp>' - a regexp that needn't match the start of a name
144 'set:<fileset>' - a fileset expression
143 'set:<fileset>' - a fileset expression
145 'include:<path>' - a file of patterns to read and include
144 'include:<path>' - a file of patterns to read and include
146 'subinclude:<path>' - a file of patterns to match against files under
145 'subinclude:<path>' - a file of patterns to match against files under
147 the same directory
146 the same directory
148 '<something>' - a pattern of the specified default type
147 '<something>' - a pattern of the specified default type
149 """
148 """
150 normalize = _donormalize
149 normalize = _donormalize
151 if icasefs:
150 if icasefs:
152 if exact:
151 if exact:
153 raise error.ProgrammingError("a case-insensitive exact matcher "
152 raise error.ProgrammingError("a case-insensitive exact matcher "
154 "doesn't make sense")
153 "doesn't make sense")
155 dirstate = ctx.repo().dirstate
154 dirstate = ctx.repo().dirstate
156 dsnormalize = dirstate.normalize
155 dsnormalize = dirstate.normalize
157
156
158 def normalize(patterns, default, root, cwd, auditor, warn):
157 def normalize(patterns, default, root, cwd, auditor, warn):
159 kp = _donormalize(patterns, default, root, cwd, auditor, warn)
158 kp = _donormalize(patterns, default, root, cwd, auditor, warn)
160 kindpats = []
159 kindpats = []
161 for kind, pats, source in kp:
160 for kind, pats, source in kp:
162 if kind not in ('re', 'relre'): # regex can't be normalized
161 if kind not in ('re', 'relre'): # regex can't be normalized
163 p = pats
162 p = pats
164 pats = dsnormalize(pats)
163 pats = dsnormalize(pats)
165
164
166 # Preserve the original to handle a case only rename.
165 # Preserve the original to handle a case only rename.
167 if p != pats and p in dirstate:
166 if p != pats and p in dirstate:
168 kindpats.append((kind, p, source))
167 kindpats.append((kind, p, source))
169
168
170 kindpats.append((kind, pats, source))
169 kindpats.append((kind, pats, source))
171 return kindpats
170 return kindpats
172
171
173 if exact:
172 if exact:
174 m = exactmatcher(root, cwd, patterns, badfn)
173 m = exactmatcher(root, cwd, patterns, badfn)
175 elif patterns:
174 elif patterns:
176 kindpats = normalize(patterns, default, root, cwd, auditor, warn)
175 kindpats = normalize(patterns, default, root, cwd, auditor, warn)
177 if _kindpatsalwaysmatch(kindpats):
176 if _kindpatsalwaysmatch(kindpats):
178 m = alwaysmatcher(root, cwd, badfn, relativeuipath=True)
177 m = alwaysmatcher(root, cwd, badfn, relativeuipath=True)
179 else:
178 else:
180 m = _buildkindpatsmatcher(patternmatcher, root, cwd, kindpats,
179 m = _buildkindpatsmatcher(patternmatcher, root, cwd, kindpats,
181 ctx=ctx, listsubrepos=listsubrepos,
180 ctx=ctx, listsubrepos=listsubrepos,
182 badfn=badfn)
181 badfn=badfn)
183 else:
182 else:
184 # It's a little strange that no patterns means to match everything.
183 # It's a little strange that no patterns means to match everything.
185 # Consider changing this to match nothing (probably using nevermatcher).
184 # Consider changing this to match nothing (probably using nevermatcher).
186 m = alwaysmatcher(root, cwd, badfn)
185 m = alwaysmatcher(root, cwd, badfn)
187
186
188 if include:
187 if include:
189 kindpats = normalize(include, 'glob', root, cwd, auditor, warn)
188 kindpats = normalize(include, 'glob', root, cwd, auditor, warn)
190 im = _buildkindpatsmatcher(includematcher, root, cwd, kindpats, ctx=ctx,
189 im = _buildkindpatsmatcher(includematcher, root, cwd, kindpats, ctx=ctx,
191 listsubrepos=listsubrepos, badfn=None)
190 listsubrepos=listsubrepos, badfn=None)
192 m = intersectmatchers(m, im)
191 m = intersectmatchers(m, im)
193 if exclude:
192 if exclude:
194 kindpats = normalize(exclude, 'glob', root, cwd, auditor, warn)
193 kindpats = normalize(exclude, 'glob', root, cwd, auditor, warn)
195 em = _buildkindpatsmatcher(includematcher, root, cwd, kindpats, ctx=ctx,
194 em = _buildkindpatsmatcher(includematcher, root, cwd, kindpats, ctx=ctx,
196 listsubrepos=listsubrepos, badfn=None)
195 listsubrepos=listsubrepos, badfn=None)
197 m = differencematcher(m, em)
196 m = differencematcher(m, em)
198 return m
197 return m
199
198
200 def exact(root, cwd, files, badfn=None):
199 def exact(root, cwd, files, badfn=None):
201 return exactmatcher(root, cwd, files, badfn=badfn)
200 return exactmatcher(root, cwd, files, badfn=badfn)
202
201
203 def always(root, cwd):
202 def always(root, cwd):
204 return alwaysmatcher(root, cwd)
203 return alwaysmatcher(root, cwd)
205
204
206 def never(root, cwd):
205 def never(root, cwd):
207 return nevermatcher(root, cwd)
206 return nevermatcher(root, cwd)
208
207
209 def badmatch(match, badfn):
208 def badmatch(match, badfn):
210 """Make a copy of the given matcher, replacing its bad method with the given
209 """Make a copy of the given matcher, replacing its bad method with the given
211 one.
210 one.
212 """
211 """
213 m = copy.copy(match)
212 m = copy.copy(match)
214 m.bad = badfn
213 m.bad = badfn
215 return m
214 return m
216
215
217 def _donormalize(patterns, default, root, cwd, auditor, warn):
216 def _donormalize(patterns, default, root, cwd, auditor, warn):
218 '''Convert 'kind:pat' from the patterns list to tuples with kind and
217 '''Convert 'kind:pat' from the patterns list to tuples with kind and
219 normalized and rooted patterns and with listfiles expanded.'''
218 normalized and rooted patterns and with listfiles expanded.'''
220 kindpats = []
219 kindpats = []
221 for kind, pat in [_patsplit(p, default) for p in patterns]:
220 for kind, pat in [_patsplit(p, default) for p in patterns]:
222 if kind in cwdrelativepatternkinds:
221 if kind in cwdrelativepatternkinds:
223 pat = pathutil.canonpath(root, cwd, pat, auditor)
222 pat = pathutil.canonpath(root, cwd, pat, auditor)
224 elif kind in ('relglob', 'path', 'rootfilesin'):
223 elif kind in ('relglob', 'path', 'rootfilesin'):
225 pat = util.normpath(pat)
224 pat = util.normpath(pat)
226 elif kind in ('listfile', 'listfile0'):
225 elif kind in ('listfile', 'listfile0'):
227 try:
226 try:
228 files = util.readfile(pat)
227 files = util.readfile(pat)
229 if kind == 'listfile0':
228 if kind == 'listfile0':
230 files = files.split('\0')
229 files = files.split('\0')
231 else:
230 else:
232 files = files.splitlines()
231 files = files.splitlines()
233 files = [f for f in files if f]
232 files = [f for f in files if f]
234 except EnvironmentError:
233 except EnvironmentError:
235 raise error.Abort(_("unable to read file list (%s)") % pat)
234 raise error.Abort(_("unable to read file list (%s)") % pat)
236 for k, p, source in _donormalize(files, default, root, cwd,
235 for k, p, source in _donormalize(files, default, root, cwd,
237 auditor, warn):
236 auditor, warn):
238 kindpats.append((k, p, pat))
237 kindpats.append((k, p, pat))
239 continue
238 continue
240 elif kind == 'include':
239 elif kind == 'include':
241 try:
240 try:
242 fullpath = os.path.join(root, util.localpath(pat))
241 fullpath = os.path.join(root, util.localpath(pat))
243 includepats = readpatternfile(fullpath, warn)
242 includepats = readpatternfile(fullpath, warn)
244 for k, p, source in _donormalize(includepats, default,
243 for k, p, source in _donormalize(includepats, default,
245 root, cwd, auditor, warn):
244 root, cwd, auditor, warn):
246 kindpats.append((k, p, source or pat))
245 kindpats.append((k, p, source or pat))
247 except error.Abort as inst:
246 except error.Abort as inst:
248 raise error.Abort('%s: %s' % (pat, inst[0]))
247 raise error.Abort('%s: %s' % (pat, inst[0]))
249 except IOError as inst:
248 except IOError as inst:
250 if warn:
249 if warn:
251 warn(_("skipping unreadable pattern file '%s': %s\n") %
250 warn(_("skipping unreadable pattern file '%s': %s\n") %
252 (pat, stringutil.forcebytestr(inst.strerror)))
251 (pat, stringutil.forcebytestr(inst.strerror)))
253 continue
252 continue
254 # else: re or relre - which cannot be normalized
253 # else: re or relre - which cannot be normalized
255 kindpats.append((kind, pat, ''))
254 kindpats.append((kind, pat, ''))
256 return kindpats
255 return kindpats
257
256
258 class basematcher(object):
257 class basematcher(object):
259
258
260 def __init__(self, root, cwd, badfn=None, relativeuipath=True):
259 def __init__(self, root, cwd, badfn=None, relativeuipath=True):
261 self._root = root
260 self._root = root
262 self._cwd = cwd
261 self._cwd = cwd
263 if badfn is not None:
262 if badfn is not None:
264 self.bad = badfn
263 self.bad = badfn
265 self._relativeuipath = relativeuipath
264 self._relativeuipath = relativeuipath
266
265
267 def __call__(self, fn):
266 def __call__(self, fn):
268 return self.matchfn(fn)
267 return self.matchfn(fn)
269 def __iter__(self):
268 def __iter__(self):
270 for f in self._files:
269 for f in self._files:
271 yield f
270 yield f
272 # Callbacks related to how the matcher is used by dirstate.walk.
271 # Callbacks related to how the matcher is used by dirstate.walk.
273 # Subscribers to these events must monkeypatch the matcher object.
272 # Subscribers to these events must monkeypatch the matcher object.
274 def bad(self, f, msg):
273 def bad(self, f, msg):
275 '''Callback from dirstate.walk for each explicit file that can't be
274 '''Callback from dirstate.walk for each explicit file that can't be
276 found/accessed, with an error message.'''
275 found/accessed, with an error message.'''
277
276
278 # If an explicitdir is set, it will be called when an explicitly listed
277 # If an explicitdir is set, it will be called when an explicitly listed
279 # directory is visited.
278 # directory is visited.
280 explicitdir = None
279 explicitdir = None
281
280
282 # If an traversedir is set, it will be called when a directory discovered
281 # If an traversedir is set, it will be called when a directory discovered
283 # by recursive traversal is visited.
282 # by recursive traversal is visited.
284 traversedir = None
283 traversedir = None
285
284
286 def abs(self, f):
285 def abs(self, f):
287 '''Convert a repo path back to path that is relative to the root of the
286 '''Convert a repo path back to path that is relative to the root of the
288 matcher.'''
287 matcher.'''
289 return f
288 return f
290
289
291 def rel(self, f):
290 def rel(self, f):
292 '''Convert repo path back to path that is relative to cwd of matcher.'''
291 '''Convert repo path back to path that is relative to cwd of matcher.'''
293 return util.pathto(self._root, self._cwd, f)
292 return util.pathto(self._root, self._cwd, f)
294
293
295 def uipath(self, f):
294 def uipath(self, f):
296 '''Convert repo path to a display path. If patterns or -I/-X were used
295 '''Convert repo path to a display path. If patterns or -I/-X were used
297 to create this matcher, the display path will be relative to cwd.
296 to create this matcher, the display path will be relative to cwd.
298 Otherwise it is relative to the root of the repo.'''
297 Otherwise it is relative to the root of the repo.'''
299 return (self._relativeuipath and self.rel(f)) or self.abs(f)
298 return (self._relativeuipath and self.rel(f)) or self.abs(f)
300
299
301 @propertycache
300 @propertycache
302 def _files(self):
301 def _files(self):
303 return []
302 return []
304
303
305 def files(self):
304 def files(self):
306 '''Explicitly listed files or patterns or roots:
305 '''Explicitly listed files or patterns or roots:
307 if no patterns or .always(): empty list,
306 if no patterns or .always(): empty list,
308 if exact: list exact files,
307 if exact: list exact files,
309 if not .anypats(): list all files and dirs,
308 if not .anypats(): list all files and dirs,
310 else: optimal roots'''
309 else: optimal roots'''
311 return self._files
310 return self._files
312
311
313 @propertycache
312 @propertycache
314 def _fileset(self):
313 def _fileset(self):
315 return set(self._files)
314 return set(self._files)
316
315
317 def exact(self, f):
316 def exact(self, f):
318 '''Returns True if f is in .files().'''
317 '''Returns True if f is in .files().'''
319 return f in self._fileset
318 return f in self._fileset
320
319
321 def matchfn(self, f):
320 def matchfn(self, f):
322 return False
321 return False
323
322
324 def visitdir(self, dir):
323 def visitdir(self, dir):
325 '''Decides whether a directory should be visited based on whether it
324 '''Decides whether a directory should be visited based on whether it
326 has potential matches in it or one of its subdirectories. This is
325 has potential matches in it or one of its subdirectories. This is
327 based on the match's primary, included, and excluded patterns.
326 based on the match's primary, included, and excluded patterns.
328
327
329 Returns the string 'all' if the given directory and all subdirectories
328 Returns the string 'all' if the given directory and all subdirectories
330 should be visited. Otherwise returns True or False indicating whether
329 should be visited. Otherwise returns True or False indicating whether
331 the given directory should be visited.
330 the given directory should be visited.
332 '''
331 '''
333 return True
332 return True
334
333
335 def always(self):
334 def always(self):
336 '''Matcher will match everything and .files() will be empty --
335 '''Matcher will match everything and .files() will be empty --
337 optimization might be possible.'''
336 optimization might be possible.'''
338 return False
337 return False
339
338
340 def isexact(self):
339 def isexact(self):
341 '''Matcher will match exactly the list of files in .files() --
340 '''Matcher will match exactly the list of files in .files() --
342 optimization might be possible.'''
341 optimization might be possible.'''
343 return False
342 return False
344
343
345 def prefix(self):
344 def prefix(self):
346 '''Matcher will match the paths in .files() recursively --
345 '''Matcher will match the paths in .files() recursively --
347 optimization might be possible.'''
346 optimization might be possible.'''
348 return False
347 return False
349
348
350 def anypats(self):
349 def anypats(self):
351 '''None of .always(), .isexact(), and .prefix() is true --
350 '''None of .always(), .isexact(), and .prefix() is true --
352 optimizations will be difficult.'''
351 optimizations will be difficult.'''
353 return not self.always() and not self.isexact() and not self.prefix()
352 return not self.always() and not self.isexact() and not self.prefix()
354
353
355 class alwaysmatcher(basematcher):
354 class alwaysmatcher(basematcher):
356 '''Matches everything.'''
355 '''Matches everything.'''
357
356
358 def __init__(self, root, cwd, badfn=None, relativeuipath=False):
357 def __init__(self, root, cwd, badfn=None, relativeuipath=False):
359 super(alwaysmatcher, self).__init__(root, cwd, badfn,
358 super(alwaysmatcher, self).__init__(root, cwd, badfn,
360 relativeuipath=relativeuipath)
359 relativeuipath=relativeuipath)
361
360
362 def always(self):
361 def always(self):
363 return True
362 return True
364
363
365 def matchfn(self, f):
364 def matchfn(self, f):
366 return True
365 return True
367
366
368 def visitdir(self, dir):
367 def visitdir(self, dir):
369 return 'all'
368 return 'all'
370
369
371 def __repr__(self):
370 def __repr__(self):
372 return r'<alwaysmatcher>'
371 return r'<alwaysmatcher>'
373
372
374 class nevermatcher(basematcher):
373 class nevermatcher(basematcher):
375 '''Matches nothing.'''
374 '''Matches nothing.'''
376
375
377 def __init__(self, root, cwd, badfn=None):
376 def __init__(self, root, cwd, badfn=None):
378 super(nevermatcher, self).__init__(root, cwd, badfn)
377 super(nevermatcher, self).__init__(root, cwd, badfn)
379
378
380 # It's a little weird to say that the nevermatcher is an exact matcher
379 # It's a little weird to say that the nevermatcher is an exact matcher
381 # or a prefix matcher, but it seems to make sense to let callers take
380 # or a prefix matcher, but it seems to make sense to let callers take
382 # fast paths based on either. There will be no exact matches, nor any
381 # fast paths based on either. There will be no exact matches, nor any
383 # prefixes (files() returns []), so fast paths iterating over them should
382 # prefixes (files() returns []), so fast paths iterating over them should
384 # be efficient (and correct).
383 # be efficient (and correct).
385 def isexact(self):
384 def isexact(self):
386 return True
385 return True
387
386
388 def prefix(self):
387 def prefix(self):
389 return True
388 return True
390
389
391 def visitdir(self, dir):
390 def visitdir(self, dir):
392 return False
391 return False
393
392
394 def __repr__(self):
393 def __repr__(self):
395 return r'<nevermatcher>'
394 return r'<nevermatcher>'
396
395
397 class predicatematcher(basematcher):
396 class predicatematcher(basematcher):
398 """A matcher adapter for a simple boolean function"""
397 """A matcher adapter for a simple boolean function"""
399
398
400 def __init__(self, root, cwd, predfn, predrepr=None, badfn=None):
399 def __init__(self, root, cwd, predfn, predrepr=None, badfn=None):
401 super(predicatematcher, self).__init__(root, cwd, badfn)
400 super(predicatematcher, self).__init__(root, cwd, badfn)
402 self.matchfn = predfn
401 self.matchfn = predfn
403 self._predrepr = predrepr
402 self._predrepr = predrepr
404
403
405 @encoding.strmethod
404 @encoding.strmethod
406 def __repr__(self):
405 def __repr__(self):
407 s = (stringutil.buildrepr(self._predrepr)
406 s = (stringutil.buildrepr(self._predrepr)
408 or pycompat.byterepr(self.matchfn))
407 or pycompat.byterepr(self.matchfn))
409 return '<predicatenmatcher pred=%s>' % s
408 return '<predicatenmatcher pred=%s>' % s
410
409
411 class patternmatcher(basematcher):
410 class patternmatcher(basematcher):
412
411
413 def __init__(self, root, cwd, kindpats, listsubrepos=False, badfn=None):
412 def __init__(self, root, cwd, kindpats, listsubrepos=False, badfn=None):
414 super(patternmatcher, self).__init__(root, cwd, badfn)
413 super(patternmatcher, self).__init__(root, cwd, badfn)
415
414
416 self._files = _explicitfiles(kindpats)
415 self._files = _explicitfiles(kindpats)
417 self._prefix = _prefix(kindpats)
416 self._prefix = _prefix(kindpats)
418 self._pats, self.matchfn = _buildmatch(kindpats, '$', listsubrepos,
417 self._pats, self.matchfn = _buildmatch(kindpats, '$', listsubrepos,
419 root)
418 root)
420
419
421 @propertycache
420 @propertycache
422 def _dirs(self):
421 def _dirs(self):
423 return set(util.dirs(self._fileset)) | {'.'}
422 return set(util.dirs(self._fileset)) | {'.'}
424
423
425 def visitdir(self, dir):
424 def visitdir(self, dir):
426 if self._prefix and dir in self._fileset:
425 if self._prefix and dir in self._fileset:
427 return 'all'
426 return 'all'
428 return ('.' in self._fileset or
427 return ('.' in self._fileset or
429 dir in self._fileset or
428 dir in self._fileset or
430 dir in self._dirs or
429 dir in self._dirs or
431 any(parentdir in self._fileset
430 any(parentdir in self._fileset
432 for parentdir in util.finddirs(dir)))
431 for parentdir in util.finddirs(dir)))
433
432
434 def prefix(self):
433 def prefix(self):
435 return self._prefix
434 return self._prefix
436
435
437 @encoding.strmethod
436 @encoding.strmethod
438 def __repr__(self):
437 def __repr__(self):
439 return ('<patternmatcher patterns=%r>' % pycompat.bytestr(self._pats))
438 return ('<patternmatcher patterns=%r>' % pycompat.bytestr(self._pats))
440
439
441 class includematcher(basematcher):
440 class includematcher(basematcher):
442
441
443 def __init__(self, root, cwd, kindpats, listsubrepos=False, badfn=None):
442 def __init__(self, root, cwd, kindpats, listsubrepos=False, badfn=None):
444 super(includematcher, self).__init__(root, cwd, badfn)
443 super(includematcher, self).__init__(root, cwd, badfn)
445
444
446 self._pats, self.matchfn = _buildmatch(kindpats, '(?:/|$)',
445 self._pats, self.matchfn = _buildmatch(kindpats, '(?:/|$)',
447 listsubrepos, root)
446 listsubrepos, root)
448 self._prefix = _prefix(kindpats)
447 self._prefix = _prefix(kindpats)
449 roots, dirs = _rootsanddirs(kindpats)
448 roots, dirs = _rootsanddirs(kindpats)
450 # roots are directories which are recursively included.
449 # roots are directories which are recursively included.
451 self._roots = set(roots)
450 self._roots = set(roots)
452 # dirs are directories which are non-recursively included.
451 # dirs are directories which are non-recursively included.
453 self._dirs = set(dirs)
452 self._dirs = set(dirs)
454
453
455 def visitdir(self, dir):
454 def visitdir(self, dir):
456 if self._prefix and dir in self._roots:
455 if self._prefix and dir in self._roots:
457 return 'all'
456 return 'all'
458 return ('.' in self._roots or
457 return ('.' in self._roots or
459 dir in self._roots or
458 dir in self._roots or
460 dir in self._dirs or
459 dir in self._dirs or
461 any(parentdir in self._roots
460 any(parentdir in self._roots
462 for parentdir in util.finddirs(dir)))
461 for parentdir in util.finddirs(dir)))
463
462
464 @encoding.strmethod
463 @encoding.strmethod
465 def __repr__(self):
464 def __repr__(self):
466 return ('<includematcher includes=%r>' % pycompat.bytestr(self._pats))
465 return ('<includematcher includes=%r>' % pycompat.bytestr(self._pats))
467
466
468 class exactmatcher(basematcher):
467 class exactmatcher(basematcher):
469 '''Matches the input files exactly. They are interpreted as paths, not
468 '''Matches the input files exactly. They are interpreted as paths, not
470 patterns (so no kind-prefixes).
469 patterns (so no kind-prefixes).
471 '''
470 '''
472
471
473 def __init__(self, root, cwd, files, badfn=None):
472 def __init__(self, root, cwd, files, badfn=None):
474 super(exactmatcher, self).__init__(root, cwd, badfn)
473 super(exactmatcher, self).__init__(root, cwd, badfn)
475
474
476 if isinstance(files, list):
475 if isinstance(files, list):
477 self._files = files
476 self._files = files
478 else:
477 else:
479 self._files = list(files)
478 self._files = list(files)
480
479
481 matchfn = basematcher.exact
480 matchfn = basematcher.exact
482
481
483 @propertycache
482 @propertycache
484 def _dirs(self):
483 def _dirs(self):
485 return set(util.dirs(self._fileset)) | {'.'}
484 return set(util.dirs(self._fileset)) | {'.'}
486
485
487 def visitdir(self, dir):
486 def visitdir(self, dir):
488 return dir in self._dirs
487 return dir in self._dirs
489
488
490 def isexact(self):
489 def isexact(self):
491 return True
490 return True
492
491
493 @encoding.strmethod
492 @encoding.strmethod
494 def __repr__(self):
493 def __repr__(self):
495 return ('<exactmatcher files=%r>' % self._files)
494 return ('<exactmatcher files=%r>' % self._files)
496
495
497 class differencematcher(basematcher):
496 class differencematcher(basematcher):
498 '''Composes two matchers by matching if the first matches and the second
497 '''Composes two matchers by matching if the first matches and the second
499 does not.
498 does not.
500
499
501 The second matcher's non-matching-attributes (root, cwd, bad, explicitdir,
500 The second matcher's non-matching-attributes (root, cwd, bad, explicitdir,
502 traversedir) are ignored.
501 traversedir) are ignored.
503 '''
502 '''
504 def __init__(self, m1, m2):
503 def __init__(self, m1, m2):
505 super(differencematcher, self).__init__(m1._root, m1._cwd)
504 super(differencematcher, self).__init__(m1._root, m1._cwd)
506 self._m1 = m1
505 self._m1 = m1
507 self._m2 = m2
506 self._m2 = m2
508 self.bad = m1.bad
507 self.bad = m1.bad
509 self.explicitdir = m1.explicitdir
508 self.explicitdir = m1.explicitdir
510 self.traversedir = m1.traversedir
509 self.traversedir = m1.traversedir
511
510
512 def matchfn(self, f):
511 def matchfn(self, f):
513 return self._m1(f) and not self._m2(f)
512 return self._m1(f) and not self._m2(f)
514
513
515 @propertycache
514 @propertycache
516 def _files(self):
515 def _files(self):
517 if self.isexact():
516 if self.isexact():
518 return [f for f in self._m1.files() if self(f)]
517 return [f for f in self._m1.files() if self(f)]
519 # If m1 is not an exact matcher, we can't easily figure out the set of
518 # If m1 is not an exact matcher, we can't easily figure out the set of
520 # files, because its files() are not always files. For example, if
519 # files, because its files() are not always files. For example, if
521 # m1 is "path:dir" and m2 is "rootfileins:.", we don't
520 # m1 is "path:dir" and m2 is "rootfileins:.", we don't
522 # want to remove "dir" from the set even though it would match m2,
521 # want to remove "dir" from the set even though it would match m2,
523 # because the "dir" in m1 may not be a file.
522 # because the "dir" in m1 may not be a file.
524 return self._m1.files()
523 return self._m1.files()
525
524
526 def visitdir(self, dir):
525 def visitdir(self, dir):
527 if self._m2.visitdir(dir) == 'all':
526 if self._m2.visitdir(dir) == 'all':
528 return False
527 return False
529 return bool(self._m1.visitdir(dir))
528 return bool(self._m1.visitdir(dir))
530
529
531 def isexact(self):
530 def isexact(self):
532 return self._m1.isexact()
531 return self._m1.isexact()
533
532
534 @encoding.strmethod
533 @encoding.strmethod
535 def __repr__(self):
534 def __repr__(self):
536 return ('<differencematcher m1=%r, m2=%r>' % (self._m1, self._m2))
535 return ('<differencematcher m1=%r, m2=%r>' % (self._m1, self._m2))
537
536
538 def intersectmatchers(m1, m2):
537 def intersectmatchers(m1, m2):
539 '''Composes two matchers by matching if both of them match.
538 '''Composes two matchers by matching if both of them match.
540
539
541 The second matcher's non-matching-attributes (root, cwd, bad, explicitdir,
540 The second matcher's non-matching-attributes (root, cwd, bad, explicitdir,
542 traversedir) are ignored.
541 traversedir) are ignored.
543 '''
542 '''
544 if m1 is None or m2 is None:
543 if m1 is None or m2 is None:
545 return m1 or m2
544 return m1 or m2
546 if m1.always():
545 if m1.always():
547 m = copy.copy(m2)
546 m = copy.copy(m2)
548 # TODO: Consider encapsulating these things in a class so there's only
547 # TODO: Consider encapsulating these things in a class so there's only
549 # one thing to copy from m1.
548 # one thing to copy from m1.
550 m.bad = m1.bad
549 m.bad = m1.bad
551 m.explicitdir = m1.explicitdir
550 m.explicitdir = m1.explicitdir
552 m.traversedir = m1.traversedir
551 m.traversedir = m1.traversedir
553 m.abs = m1.abs
552 m.abs = m1.abs
554 m.rel = m1.rel
553 m.rel = m1.rel
555 m._relativeuipath |= m1._relativeuipath
554 m._relativeuipath |= m1._relativeuipath
556 return m
555 return m
557 if m2.always():
556 if m2.always():
558 m = copy.copy(m1)
557 m = copy.copy(m1)
559 m._relativeuipath |= m2._relativeuipath
558 m._relativeuipath |= m2._relativeuipath
560 return m
559 return m
561 return intersectionmatcher(m1, m2)
560 return intersectionmatcher(m1, m2)
562
561
563 class intersectionmatcher(basematcher):
562 class intersectionmatcher(basematcher):
564 def __init__(self, m1, m2):
563 def __init__(self, m1, m2):
565 super(intersectionmatcher, self).__init__(m1._root, m1._cwd)
564 super(intersectionmatcher, self).__init__(m1._root, m1._cwd)
566 self._m1 = m1
565 self._m1 = m1
567 self._m2 = m2
566 self._m2 = m2
568 self.bad = m1.bad
567 self.bad = m1.bad
569 self.explicitdir = m1.explicitdir
568 self.explicitdir = m1.explicitdir
570 self.traversedir = m1.traversedir
569 self.traversedir = m1.traversedir
571
570
572 @propertycache
571 @propertycache
573 def _files(self):
572 def _files(self):
574 if self.isexact():
573 if self.isexact():
575 m1, m2 = self._m1, self._m2
574 m1, m2 = self._m1, self._m2
576 if not m1.isexact():
575 if not m1.isexact():
577 m1, m2 = m2, m1
576 m1, m2 = m2, m1
578 return [f for f in m1.files() if m2(f)]
577 return [f for f in m1.files() if m2(f)]
579 # It neither m1 nor m2 is an exact matcher, we can't easily intersect
578 # It neither m1 nor m2 is an exact matcher, we can't easily intersect
580 # the set of files, because their files() are not always files. For
579 # the set of files, because their files() are not always files. For
581 # example, if intersecting a matcher "-I glob:foo.txt" with matcher of
580 # example, if intersecting a matcher "-I glob:foo.txt" with matcher of
582 # "path:dir2", we don't want to remove "dir2" from the set.
581 # "path:dir2", we don't want to remove "dir2" from the set.
583 return self._m1.files() + self._m2.files()
582 return self._m1.files() + self._m2.files()
584
583
585 def matchfn(self, f):
584 def matchfn(self, f):
586 return self._m1(f) and self._m2(f)
585 return self._m1(f) and self._m2(f)
587
586
588 def visitdir(self, dir):
587 def visitdir(self, dir):
589 visit1 = self._m1.visitdir(dir)
588 visit1 = self._m1.visitdir(dir)
590 if visit1 == 'all':
589 if visit1 == 'all':
591 return self._m2.visitdir(dir)
590 return self._m2.visitdir(dir)
592 # bool() because visit1=True + visit2='all' should not be 'all'
591 # bool() because visit1=True + visit2='all' should not be 'all'
593 return bool(visit1 and self._m2.visitdir(dir))
592 return bool(visit1 and self._m2.visitdir(dir))
594
593
595 def always(self):
594 def always(self):
596 return self._m1.always() and self._m2.always()
595 return self._m1.always() and self._m2.always()
597
596
598 def isexact(self):
597 def isexact(self):
599 return self._m1.isexact() or self._m2.isexact()
598 return self._m1.isexact() or self._m2.isexact()
600
599
601 @encoding.strmethod
600 @encoding.strmethod
602 def __repr__(self):
601 def __repr__(self):
603 return ('<intersectionmatcher m1=%r, m2=%r>' % (self._m1, self._m2))
602 return ('<intersectionmatcher m1=%r, m2=%r>' % (self._m1, self._m2))
604
603
605 class subdirmatcher(basematcher):
604 class subdirmatcher(basematcher):
606 """Adapt a matcher to work on a subdirectory only.
605 """Adapt a matcher to work on a subdirectory only.
607
606
608 The paths are remapped to remove/insert the path as needed:
607 The paths are remapped to remove/insert the path as needed:
609
608
610 >>> from . import pycompat
609 >>> from . import pycompat
611 >>> m1 = match(b'root', b'', [b'a.txt', b'sub/b.txt'])
610 >>> m1 = match(b'root', b'', [b'a.txt', b'sub/b.txt'])
612 >>> m2 = subdirmatcher(b'sub', m1)
611 >>> m2 = subdirmatcher(b'sub', m1)
613 >>> bool(m2(b'a.txt'))
612 >>> bool(m2(b'a.txt'))
614 False
613 False
615 >>> bool(m2(b'b.txt'))
614 >>> bool(m2(b'b.txt'))
616 True
615 True
617 >>> bool(m2.matchfn(b'a.txt'))
616 >>> bool(m2.matchfn(b'a.txt'))
618 False
617 False
619 >>> bool(m2.matchfn(b'b.txt'))
618 >>> bool(m2.matchfn(b'b.txt'))
620 True
619 True
621 >>> m2.files()
620 >>> m2.files()
622 ['b.txt']
621 ['b.txt']
623 >>> m2.exact(b'b.txt')
622 >>> m2.exact(b'b.txt')
624 True
623 True
625 >>> util.pconvert(m2.rel(b'b.txt'))
624 >>> util.pconvert(m2.rel(b'b.txt'))
626 'sub/b.txt'
625 'sub/b.txt'
627 >>> def bad(f, msg):
626 >>> def bad(f, msg):
628 ... print(pycompat.sysstr(b"%s: %s" % (f, msg)))
627 ... print(pycompat.sysstr(b"%s: %s" % (f, msg)))
629 >>> m1.bad = bad
628 >>> m1.bad = bad
630 >>> m2.bad(b'x.txt', b'No such file')
629 >>> m2.bad(b'x.txt', b'No such file')
631 sub/x.txt: No such file
630 sub/x.txt: No such file
632 >>> m2.abs(b'c.txt')
631 >>> m2.abs(b'c.txt')
633 'sub/c.txt'
632 'sub/c.txt'
634 """
633 """
635
634
636 def __init__(self, path, matcher):
635 def __init__(self, path, matcher):
637 super(subdirmatcher, self).__init__(matcher._root, matcher._cwd)
636 super(subdirmatcher, self).__init__(matcher._root, matcher._cwd)
638 self._path = path
637 self._path = path
639 self._matcher = matcher
638 self._matcher = matcher
640 self._always = matcher.always()
639 self._always = matcher.always()
641
640
642 self._files = [f[len(path) + 1:] for f in matcher._files
641 self._files = [f[len(path) + 1:] for f in matcher._files
643 if f.startswith(path + "/")]
642 if f.startswith(path + "/")]
644
643
645 # If the parent repo had a path to this subrepo and the matcher is
644 # If the parent repo had a path to this subrepo and the matcher is
646 # a prefix matcher, this submatcher always matches.
645 # a prefix matcher, this submatcher always matches.
647 if matcher.prefix():
646 if matcher.prefix():
648 self._always = any(f == path for f in matcher._files)
647 self._always = any(f == path for f in matcher._files)
649
648
650 def bad(self, f, msg):
649 def bad(self, f, msg):
651 self._matcher.bad(self._path + "/" + f, msg)
650 self._matcher.bad(self._path + "/" + f, msg)
652
651
653 def abs(self, f):
652 def abs(self, f):
654 return self._matcher.abs(self._path + "/" + f)
653 return self._matcher.abs(self._path + "/" + f)
655
654
656 def rel(self, f):
655 def rel(self, f):
657 return self._matcher.rel(self._path + "/" + f)
656 return self._matcher.rel(self._path + "/" + f)
658
657
659 def uipath(self, f):
658 def uipath(self, f):
660 return self._matcher.uipath(self._path + "/" + f)
659 return self._matcher.uipath(self._path + "/" + f)
661
660
662 def matchfn(self, f):
661 def matchfn(self, f):
663 # Some information is lost in the superclass's constructor, so we
662 # Some information is lost in the superclass's constructor, so we
664 # can not accurately create the matching function for the subdirectory
663 # can not accurately create the matching function for the subdirectory
665 # from the inputs. Instead, we override matchfn() and visitdir() to
664 # from the inputs. Instead, we override matchfn() and visitdir() to
666 # call the original matcher with the subdirectory path prepended.
665 # call the original matcher with the subdirectory path prepended.
667 return self._matcher.matchfn(self._path + "/" + f)
666 return self._matcher.matchfn(self._path + "/" + f)
668
667
669 def visitdir(self, dir):
668 def visitdir(self, dir):
670 if dir == '.':
669 if dir == '.':
671 dir = self._path
670 dir = self._path
672 else:
671 else:
673 dir = self._path + "/" + dir
672 dir = self._path + "/" + dir
674 return self._matcher.visitdir(dir)
673 return self._matcher.visitdir(dir)
675
674
676 def always(self):
675 def always(self):
677 return self._always
676 return self._always
678
677
679 def prefix(self):
678 def prefix(self):
680 return self._matcher.prefix() and not self._always
679 return self._matcher.prefix() and not self._always
681
680
682 @encoding.strmethod
681 @encoding.strmethod
683 def __repr__(self):
682 def __repr__(self):
684 return ('<subdirmatcher path=%r, matcher=%r>' %
683 return ('<subdirmatcher path=%r, matcher=%r>' %
685 (self._path, self._matcher))
684 (self._path, self._matcher))
686
685
687 class prefixdirmatcher(basematcher):
686 class prefixdirmatcher(basematcher):
688 """Adapt a matcher to work on a parent directory.
687 """Adapt a matcher to work on a parent directory.
689
688
690 The matcher's non-matching-attributes (root, cwd, bad, explicitdir,
689 The matcher's non-matching-attributes (root, cwd, bad, explicitdir,
691 traversedir) are ignored.
690 traversedir) are ignored.
692
691
693 The prefix path should usually be the relative path from the root of
692 The prefix path should usually be the relative path from the root of
694 this matcher to the root of the wrapped matcher.
693 this matcher to the root of the wrapped matcher.
695
694
696 >>> m1 = match(b'root/d/e', b'f', [b'../a.txt', b'b.txt'])
695 >>> m1 = match(b'root/d/e', b'f', [b'../a.txt', b'b.txt'])
697 >>> m2 = prefixdirmatcher(b'root', b'd/e/f', b'd/e', m1)
696 >>> m2 = prefixdirmatcher(b'root', b'd/e/f', b'd/e', m1)
698 >>> bool(m2(b'a.txt'),)
697 >>> bool(m2(b'a.txt'),)
699 False
698 False
700 >>> bool(m2(b'd/e/a.txt'))
699 >>> bool(m2(b'd/e/a.txt'))
701 True
700 True
702 >>> bool(m2(b'd/e/b.txt'))
701 >>> bool(m2(b'd/e/b.txt'))
703 False
702 False
704 >>> m2.files()
703 >>> m2.files()
705 ['d/e/a.txt', 'd/e/f/b.txt']
704 ['d/e/a.txt', 'd/e/f/b.txt']
706 >>> m2.exact(b'd/e/a.txt')
705 >>> m2.exact(b'd/e/a.txt')
707 True
706 True
708 >>> m2.visitdir(b'd')
707 >>> m2.visitdir(b'd')
709 True
708 True
710 >>> m2.visitdir(b'd/e')
709 >>> m2.visitdir(b'd/e')
711 True
710 True
712 >>> m2.visitdir(b'd/e/f')
711 >>> m2.visitdir(b'd/e/f')
713 True
712 True
714 >>> m2.visitdir(b'd/e/g')
713 >>> m2.visitdir(b'd/e/g')
715 False
714 False
716 >>> m2.visitdir(b'd/ef')
715 >>> m2.visitdir(b'd/ef')
717 False
716 False
718 """
717 """
719
718
720 def __init__(self, root, cwd, path, matcher, badfn=None):
719 def __init__(self, root, cwd, path, matcher, badfn=None):
721 super(prefixdirmatcher, self).__init__(root, cwd, badfn)
720 super(prefixdirmatcher, self).__init__(root, cwd, badfn)
722 if not path:
721 if not path:
723 raise error.ProgrammingError('prefix path must not be empty')
722 raise error.ProgrammingError('prefix path must not be empty')
724 self._path = path
723 self._path = path
725 self._pathprefix = path + '/'
724 self._pathprefix = path + '/'
726 self._matcher = matcher
725 self._matcher = matcher
727
726
728 @propertycache
727 @propertycache
729 def _files(self):
728 def _files(self):
730 return [self._pathprefix + f for f in self._matcher._files]
729 return [self._pathprefix + f for f in self._matcher._files]
731
730
732 def matchfn(self, f):
731 def matchfn(self, f):
733 if not f.startswith(self._pathprefix):
732 if not f.startswith(self._pathprefix):
734 return False
733 return False
735 return self._matcher.matchfn(f[len(self._pathprefix):])
734 return self._matcher.matchfn(f[len(self._pathprefix):])
736
735
737 @propertycache
736 @propertycache
738 def _pathdirs(self):
737 def _pathdirs(self):
739 return set(util.finddirs(self._path)) | {'.'}
738 return set(util.finddirs(self._path)) | {'.'}
740
739
741 def visitdir(self, dir):
740 def visitdir(self, dir):
742 if dir == self._path:
741 if dir == self._path:
743 return self._matcher.visitdir('.')
742 return self._matcher.visitdir('.')
744 if dir.startswith(self._pathprefix):
743 if dir.startswith(self._pathprefix):
745 return self._matcher.visitdir(dir[len(self._pathprefix):])
744 return self._matcher.visitdir(dir[len(self._pathprefix):])
746 return dir in self._pathdirs
745 return dir in self._pathdirs
747
746
748 def isexact(self):
747 def isexact(self):
749 return self._matcher.isexact()
748 return self._matcher.isexact()
750
749
751 def prefix(self):
750 def prefix(self):
752 return self._matcher.prefix()
751 return self._matcher.prefix()
753
752
754 @encoding.strmethod
753 @encoding.strmethod
755 def __repr__(self):
754 def __repr__(self):
756 return ('<prefixdirmatcher path=%r, matcher=%r>'
755 return ('<prefixdirmatcher path=%r, matcher=%r>'
757 % (pycompat.bytestr(self._path), self._matcher))
756 % (pycompat.bytestr(self._path), self._matcher))
758
757
759 class unionmatcher(basematcher):
758 class unionmatcher(basematcher):
760 """A matcher that is the union of several matchers.
759 """A matcher that is the union of several matchers.
761
760
762 The non-matching-attributes (root, cwd, bad, explicitdir, traversedir) are
761 The non-matching-attributes (root, cwd, bad, explicitdir, traversedir) are
763 taken from the first matcher.
762 taken from the first matcher.
764 """
763 """
765
764
766 def __init__(self, matchers):
765 def __init__(self, matchers):
767 m1 = matchers[0]
766 m1 = matchers[0]
768 super(unionmatcher, self).__init__(m1._root, m1._cwd)
767 super(unionmatcher, self).__init__(m1._root, m1._cwd)
769 self.explicitdir = m1.explicitdir
768 self.explicitdir = m1.explicitdir
770 self.traversedir = m1.traversedir
769 self.traversedir = m1.traversedir
771 self._matchers = matchers
770 self._matchers = matchers
772
771
773 def matchfn(self, f):
772 def matchfn(self, f):
774 for match in self._matchers:
773 for match in self._matchers:
775 if match(f):
774 if match(f):
776 return True
775 return True
777 return False
776 return False
778
777
779 def visitdir(self, dir):
778 def visitdir(self, dir):
780 r = False
779 r = False
781 for m in self._matchers:
780 for m in self._matchers:
782 v = m.visitdir(dir)
781 v = m.visitdir(dir)
783 if v == 'all':
782 if v == 'all':
784 return v
783 return v
785 r |= v
784 r |= v
786 return r
785 return r
787
786
788 @encoding.strmethod
787 @encoding.strmethod
789 def __repr__(self):
788 def __repr__(self):
790 return ('<unionmatcher matchers=%r>' % self._matchers)
789 return ('<unionmatcher matchers=%r>' % self._matchers)
791
790
792 def patkind(pattern, default=None):
791 def patkind(pattern, default=None):
793 '''If pattern is 'kind:pat' with a known kind, return kind.'''
792 '''If pattern is 'kind:pat' with a known kind, return kind.'''
794 return _patsplit(pattern, default)[0]
793 return _patsplit(pattern, default)[0]
795
794
796 def _patsplit(pattern, default):
795 def _patsplit(pattern, default):
797 """Split a string into the optional pattern kind prefix and the actual
796 """Split a string into the optional pattern kind prefix and the actual
798 pattern."""
797 pattern."""
799 if ':' in pattern:
798 if ':' in pattern:
800 kind, pat = pattern.split(':', 1)
799 kind, pat = pattern.split(':', 1)
801 if kind in allpatternkinds:
800 if kind in allpatternkinds:
802 return kind, pat
801 return kind, pat
803 return default, pattern
802 return default, pattern
804
803
805 def _globre(pat):
804 def _globre(pat):
806 r'''Convert an extended glob string to a regexp string.
805 r'''Convert an extended glob string to a regexp string.
807
806
808 >>> from . import pycompat
807 >>> from . import pycompat
809 >>> def bprint(s):
808 >>> def bprint(s):
810 ... print(pycompat.sysstr(s))
809 ... print(pycompat.sysstr(s))
811 >>> bprint(_globre(br'?'))
810 >>> bprint(_globre(br'?'))
812 .
811 .
813 >>> bprint(_globre(br'*'))
812 >>> bprint(_globre(br'*'))
814 [^/]*
813 [^/]*
815 >>> bprint(_globre(br'**'))
814 >>> bprint(_globre(br'**'))
816 .*
815 .*
817 >>> bprint(_globre(br'**/a'))
816 >>> bprint(_globre(br'**/a'))
818 (?:.*/)?a
817 (?:.*/)?a
819 >>> bprint(_globre(br'a/**/b'))
818 >>> bprint(_globre(br'a/**/b'))
820 a/(?:.*/)?b
819 a/(?:.*/)?b
821 >>> bprint(_globre(br'[a*?!^][^b][!c]'))
820 >>> bprint(_globre(br'[a*?!^][^b][!c]'))
822 [a*?!^][\^b][^c]
821 [a*?!^][\^b][^c]
823 >>> bprint(_globre(br'{a,b}'))
822 >>> bprint(_globre(br'{a,b}'))
824 (?:a|b)
823 (?:a|b)
825 >>> bprint(_globre(br'.\*\?'))
824 >>> bprint(_globre(br'.\*\?'))
826 \.\*\?
825 \.\*\?
827 '''
826 '''
828 i, n = 0, len(pat)
827 i, n = 0, len(pat)
829 res = ''
828 res = ''
830 group = 0
829 group = 0
831 escape = util.stringutil.reescape
830 escape = util.stringutil.reescape
832 def peek():
831 def peek():
833 return i < n and pat[i:i + 1]
832 return i < n and pat[i:i + 1]
834 while i < n:
833 while i < n:
835 c = pat[i:i + 1]
834 c = pat[i:i + 1]
836 i += 1
835 i += 1
837 if c not in '*?[{},\\':
836 if c not in '*?[{},\\':
838 res += escape(c)
837 res += escape(c)
839 elif c == '*':
838 elif c == '*':
840 if peek() == '*':
839 if peek() == '*':
841 i += 1
840 i += 1
842 if peek() == '/':
841 if peek() == '/':
843 i += 1
842 i += 1
844 res += '(?:.*/)?'
843 res += '(?:.*/)?'
845 else:
844 else:
846 res += '.*'
845 res += '.*'
847 else:
846 else:
848 res += '[^/]*'
847 res += '[^/]*'
849 elif c == '?':
848 elif c == '?':
850 res += '.'
849 res += '.'
851 elif c == '[':
850 elif c == '[':
852 j = i
851 j = i
853 if j < n and pat[j:j + 1] in '!]':
852 if j < n and pat[j:j + 1] in '!]':
854 j += 1
853 j += 1
855 while j < n and pat[j:j + 1] != ']':
854 while j < n and pat[j:j + 1] != ']':
856 j += 1
855 j += 1
857 if j >= n:
856 if j >= n:
858 res += '\\['
857 res += '\\['
859 else:
858 else:
860 stuff = pat[i:j].replace('\\','\\\\')
859 stuff = pat[i:j].replace('\\','\\\\')
861 i = j + 1
860 i = j + 1
862 if stuff[0:1] == '!':
861 if stuff[0:1] == '!':
863 stuff = '^' + stuff[1:]
862 stuff = '^' + stuff[1:]
864 elif stuff[0:1] == '^':
863 elif stuff[0:1] == '^':
865 stuff = '\\' + stuff
864 stuff = '\\' + stuff
866 res = '%s[%s]' % (res, stuff)
865 res = '%s[%s]' % (res, stuff)
867 elif c == '{':
866 elif c == '{':
868 group += 1
867 group += 1
869 res += '(?:'
868 res += '(?:'
870 elif c == '}' and group:
869 elif c == '}' and group:
871 res += ')'
870 res += ')'
872 group -= 1
871 group -= 1
873 elif c == ',' and group:
872 elif c == ',' and group:
874 res += '|'
873 res += '|'
875 elif c == '\\':
874 elif c == '\\':
876 p = peek()
875 p = peek()
877 if p:
876 if p:
878 i += 1
877 i += 1
879 res += escape(p)
878 res += escape(p)
880 else:
879 else:
881 res += escape(c)
880 res += escape(c)
882 else:
881 else:
883 res += escape(c)
882 res += escape(c)
884 return res
883 return res
885
884
886 def _regex(kind, pat, globsuffix):
885 def _regex(kind, pat, globsuffix):
887 '''Convert a (normalized) pattern of any kind into a regular expression.
886 '''Convert a (normalized) pattern of any kind into a regular expression.
888 globsuffix is appended to the regexp of globs.'''
887 globsuffix is appended to the regexp of globs.'''
889 if not pat:
888 if not pat:
890 return ''
889 return ''
891 if kind == 're':
890 if kind == 're':
892 return pat
891 return pat
893 if kind in ('path', 'relpath'):
892 if kind in ('path', 'relpath'):
894 if pat == '.':
893 if pat == '.':
895 return ''
894 return ''
896 return util.stringutil.reescape(pat) + '(?:/|$)'
895 return util.stringutil.reescape(pat) + '(?:/|$)'
897 if kind == 'rootfilesin':
896 if kind == 'rootfilesin':
898 if pat == '.':
897 if pat == '.':
899 escaped = ''
898 escaped = ''
900 else:
899 else:
901 # Pattern is a directory name.
900 # Pattern is a directory name.
902 escaped = util.stringutil.reescape(pat) + '/'
901 escaped = util.stringutil.reescape(pat) + '/'
903 # Anything after the pattern must be a non-directory.
902 # Anything after the pattern must be a non-directory.
904 return escaped + '[^/]+$'
903 return escaped + '[^/]+$'
905 if kind == 'relglob':
904 if kind == 'relglob':
906 return '(?:|.*/)' + _globre(pat) + globsuffix
905 return '(?:|.*/)' + _globre(pat) + globsuffix
907 if kind == 'relre':
906 if kind == 'relre':
908 if pat.startswith('^'):
907 if pat.startswith('^'):
909 return pat
908 return pat
910 return '.*' + pat
909 return '.*' + pat
911 if kind == 'glob':
910 if kind == 'glob':
912 return _globre(pat) + globsuffix
911 return _globre(pat) + globsuffix
913 raise error.ProgrammingError('not a regex pattern: %s:%s' % (kind, pat))
912 raise error.ProgrammingError('not a regex pattern: %s:%s' % (kind, pat))
914
913
915 def _buildmatch(kindpats, globsuffix, listsubrepos, root):
914 def _buildmatch(kindpats, globsuffix, listsubrepos, root):
916 '''Return regexp string and a matcher function for kindpats.
915 '''Return regexp string and a matcher function for kindpats.
917 globsuffix is appended to the regexp of globs.'''
916 globsuffix is appended to the regexp of globs.'''
918 matchfuncs = []
917 matchfuncs = []
919
918
920 subincludes, kindpats = _expandsubinclude(kindpats, root)
919 subincludes, kindpats = _expandsubinclude(kindpats, root)
921 if subincludes:
920 if subincludes:
922 submatchers = {}
921 submatchers = {}
923 def matchsubinclude(f):
922 def matchsubinclude(f):
924 for prefix, matcherargs in subincludes:
923 for prefix, matcherargs in subincludes:
925 if f.startswith(prefix):
924 if f.startswith(prefix):
926 mf = submatchers.get(prefix)
925 mf = submatchers.get(prefix)
927 if mf is None:
926 if mf is None:
928 mf = match(*matcherargs)
927 mf = match(*matcherargs)
929 submatchers[prefix] = mf
928 submatchers[prefix] = mf
930
929
931 if mf(f[len(prefix):]):
930 if mf(f[len(prefix):]):
932 return True
931 return True
933 return False
932 return False
934 matchfuncs.append(matchsubinclude)
933 matchfuncs.append(matchsubinclude)
935
934
936 regex = ''
935 regex = ''
937 if kindpats:
936 if kindpats:
938 regex, mf = _buildregexmatch(kindpats, globsuffix)
937 regex, mf = _buildregexmatch(kindpats, globsuffix)
939 matchfuncs.append(mf)
938 matchfuncs.append(mf)
940
939
941 if len(matchfuncs) == 1:
940 if len(matchfuncs) == 1:
942 return regex, matchfuncs[0]
941 return regex, matchfuncs[0]
943 else:
942 else:
944 return regex, lambda f: any(mf(f) for mf in matchfuncs)
943 return regex, lambda f: any(mf(f) for mf in matchfuncs)
945
944
946 def _buildregexmatch(kindpats, globsuffix):
945 def _buildregexmatch(kindpats, globsuffix):
947 """Build a match function from a list of kinds and kindpats,
946 """Build a match function from a list of kinds and kindpats,
948 return regexp string and a matcher function."""
947 return regexp string and a matcher function."""
949 try:
948 try:
950 regex = '(?:%s)' % '|'.join([_regex(k, p, globsuffix)
949 regex = '(?:%s)' % '|'.join([_regex(k, p, globsuffix)
951 for (k, p, s) in kindpats])
950 for (k, p, s) in kindpats])
952 if len(regex) > 20000:
951 if len(regex) > 20000:
953 raise OverflowError
952 raise OverflowError
954 return regex, _rematcher(regex)
953 return regex, _rematcher(regex)
955 except OverflowError:
954 except OverflowError:
956 # We're using a Python with a tiny regex engine and we
955 # We're using a Python with a tiny regex engine and we
957 # made it explode, so we'll divide the pattern list in two
956 # made it explode, so we'll divide the pattern list in two
958 # until it works
957 # until it works
959 l = len(kindpats)
958 l = len(kindpats)
960 if l < 2:
959 if l < 2:
961 raise
960 raise
962 regexa, a = _buildregexmatch(kindpats[:l//2], globsuffix)
961 regexa, a = _buildregexmatch(kindpats[:l//2], globsuffix)
963 regexb, b = _buildregexmatch(kindpats[l//2:], globsuffix)
962 regexb, b = _buildregexmatch(kindpats[l//2:], globsuffix)
964 return regex, lambda s: a(s) or b(s)
963 return regex, lambda s: a(s) or b(s)
965 except re.error:
964 except re.error:
966 for k, p, s in kindpats:
965 for k, p, s in kindpats:
967 try:
966 try:
968 _rematcher('(?:%s)' % _regex(k, p, globsuffix))
967 _rematcher('(?:%s)' % _regex(k, p, globsuffix))
969 except re.error:
968 except re.error:
970 if s:
969 if s:
971 raise error.Abort(_("%s: invalid pattern (%s): %s") %
970 raise error.Abort(_("%s: invalid pattern (%s): %s") %
972 (s, k, p))
971 (s, k, p))
973 else:
972 else:
974 raise error.Abort(_("invalid pattern (%s): %s") % (k, p))
973 raise error.Abort(_("invalid pattern (%s): %s") % (k, p))
975 raise error.Abort(_("invalid pattern"))
974 raise error.Abort(_("invalid pattern"))
976
975
977 def _patternrootsanddirs(kindpats):
976 def _patternrootsanddirs(kindpats):
978 '''Returns roots and directories corresponding to each pattern.
977 '''Returns roots and directories corresponding to each pattern.
979
978
980 This calculates the roots and directories exactly matching the patterns and
979 This calculates the roots and directories exactly matching the patterns and
981 returns a tuple of (roots, dirs) for each. It does not return other
980 returns a tuple of (roots, dirs) for each. It does not return other
982 directories which may also need to be considered, like the parent
981 directories which may also need to be considered, like the parent
983 directories.
982 directories.
984 '''
983 '''
985 r = []
984 r = []
986 d = []
985 d = []
987 for kind, pat, source in kindpats:
986 for kind, pat, source in kindpats:
988 if kind == 'glob': # find the non-glob prefix
987 if kind == 'glob': # find the non-glob prefix
989 root = []
988 root = []
990 for p in pat.split('/'):
989 for p in pat.split('/'):
991 if '[' in p or '{' in p or '*' in p or '?' in p:
990 if '[' in p or '{' in p or '*' in p or '?' in p:
992 break
991 break
993 root.append(p)
992 root.append(p)
994 r.append('/'.join(root) or '.')
993 r.append('/'.join(root) or '.')
995 elif kind in ('relpath', 'path'):
994 elif kind in ('relpath', 'path'):
996 r.append(pat or '.')
995 r.append(pat or '.')
997 elif kind in ('rootfilesin',):
996 elif kind in ('rootfilesin',):
998 d.append(pat or '.')
997 d.append(pat or '.')
999 else: # relglob, re, relre
998 else: # relglob, re, relre
1000 r.append('.')
999 r.append('.')
1001 return r, d
1000 return r, d
1002
1001
1003 def _roots(kindpats):
1002 def _roots(kindpats):
1004 '''Returns root directories to match recursively from the given patterns.'''
1003 '''Returns root directories to match recursively from the given patterns.'''
1005 roots, dirs = _patternrootsanddirs(kindpats)
1004 roots, dirs = _patternrootsanddirs(kindpats)
1006 return roots
1005 return roots
1007
1006
1008 def _rootsanddirs(kindpats):
1007 def _rootsanddirs(kindpats):
1009 '''Returns roots and exact directories from patterns.
1008 '''Returns roots and exact directories from patterns.
1010
1009
1011 roots are directories to match recursively, whereas exact directories should
1010 roots are directories to match recursively, whereas exact directories should
1012 be matched non-recursively. The returned (roots, dirs) tuple will also
1011 be matched non-recursively. The returned (roots, dirs) tuple will also
1013 include directories that need to be implicitly considered as either, such as
1012 include directories that need to be implicitly considered as either, such as
1014 parent directories.
1013 parent directories.
1015
1014
1016 >>> _rootsanddirs(
1015 >>> _rootsanddirs(
1017 ... [(b'glob', b'g/h/*', b''), (b'glob', b'g/h', b''),
1016 ... [(b'glob', b'g/h/*', b''), (b'glob', b'g/h', b''),
1018 ... (b'glob', b'g*', b'')])
1017 ... (b'glob', b'g*', b'')])
1019 (['g/h', 'g/h', '.'], ['g', '.'])
1018 (['g/h', 'g/h', '.'], ['g', '.'])
1020 >>> _rootsanddirs(
1019 >>> _rootsanddirs(
1021 ... [(b'rootfilesin', b'g/h', b''), (b'rootfilesin', b'', b'')])
1020 ... [(b'rootfilesin', b'g/h', b''), (b'rootfilesin', b'', b'')])
1022 ([], ['g/h', '.', 'g', '.'])
1021 ([], ['g/h', '.', 'g', '.'])
1023 >>> _rootsanddirs(
1022 >>> _rootsanddirs(
1024 ... [(b'relpath', b'r', b''), (b'path', b'p/p', b''),
1023 ... [(b'relpath', b'r', b''), (b'path', b'p/p', b''),
1025 ... (b'path', b'', b'')])
1024 ... (b'path', b'', b'')])
1026 (['r', 'p/p', '.'], ['p', '.'])
1025 (['r', 'p/p', '.'], ['p', '.'])
1027 >>> _rootsanddirs(
1026 >>> _rootsanddirs(
1028 ... [(b'relglob', b'rg*', b''), (b're', b're/', b''),
1027 ... [(b'relglob', b'rg*', b''), (b're', b're/', b''),
1029 ... (b'relre', b'rr', b'')])
1028 ... (b'relre', b'rr', b'')])
1030 (['.', '.', '.'], ['.'])
1029 (['.', '.', '.'], ['.'])
1031 '''
1030 '''
1032 r, d = _patternrootsanddirs(kindpats)
1031 r, d = _patternrootsanddirs(kindpats)
1033
1032
1034 # Append the parents as non-recursive/exact directories, since they must be
1033 # Append the parents as non-recursive/exact directories, since they must be
1035 # scanned to get to either the roots or the other exact directories.
1034 # scanned to get to either the roots or the other exact directories.
1036 d.extend(util.dirs(d))
1035 d.extend(util.dirs(d))
1037 d.extend(util.dirs(r))
1036 d.extend(util.dirs(r))
1038 # util.dirs() does not include the root directory, so add it manually
1037 # util.dirs() does not include the root directory, so add it manually
1039 d.append('.')
1038 d.append('.')
1040
1039
1041 return r, d
1040 return r, d
1042
1041
1043 def _explicitfiles(kindpats):
1042 def _explicitfiles(kindpats):
1044 '''Returns the potential explicit filenames from the patterns.
1043 '''Returns the potential explicit filenames from the patterns.
1045
1044
1046 >>> _explicitfiles([(b'path', b'foo/bar', b'')])
1045 >>> _explicitfiles([(b'path', b'foo/bar', b'')])
1047 ['foo/bar']
1046 ['foo/bar']
1048 >>> _explicitfiles([(b'rootfilesin', b'foo/bar', b'')])
1047 >>> _explicitfiles([(b'rootfilesin', b'foo/bar', b'')])
1049 []
1048 []
1050 '''
1049 '''
1051 # Keep only the pattern kinds where one can specify filenames (vs only
1050 # Keep only the pattern kinds where one can specify filenames (vs only
1052 # directory names).
1051 # directory names).
1053 filable = [kp for kp in kindpats if kp[0] not in ('rootfilesin',)]
1052 filable = [kp for kp in kindpats if kp[0] not in ('rootfilesin',)]
1054 return _roots(filable)
1053 return _roots(filable)
1055
1054
1056 def _prefix(kindpats):
1055 def _prefix(kindpats):
1057 '''Whether all the patterns match a prefix (i.e. recursively)'''
1056 '''Whether all the patterns match a prefix (i.e. recursively)'''
1058 for kind, pat, source in kindpats:
1057 for kind, pat, source in kindpats:
1059 if kind not in ('path', 'relpath'):
1058 if kind not in ('path', 'relpath'):
1060 return False
1059 return False
1061 return True
1060 return True
1062
1061
1063 _commentre = None
1062 _commentre = None
1064
1063
1065 def readpatternfile(filepath, warn, sourceinfo=False):
1064 def readpatternfile(filepath, warn, sourceinfo=False):
1066 '''parse a pattern file, returning a list of
1065 '''parse a pattern file, returning a list of
1067 patterns. These patterns should be given to compile()
1066 patterns. These patterns should be given to compile()
1068 to be validated and converted into a match function.
1067 to be validated and converted into a match function.
1069
1068
1070 trailing white space is dropped.
1069 trailing white space is dropped.
1071 the escape character is backslash.
1070 the escape character is backslash.
1072 comments start with #.
1071 comments start with #.
1073 empty lines are skipped.
1072 empty lines are skipped.
1074
1073
1075 lines can be of the following formats:
1074 lines can be of the following formats:
1076
1075
1077 syntax: regexp # defaults following lines to non-rooted regexps
1076 syntax: regexp # defaults following lines to non-rooted regexps
1078 syntax: glob # defaults following lines to non-rooted globs
1077 syntax: glob # defaults following lines to non-rooted globs
1079 re:pattern # non-rooted regular expression
1078 re:pattern # non-rooted regular expression
1080 glob:pattern # non-rooted glob
1079 glob:pattern # non-rooted glob
1081 pattern # pattern of the current default type
1080 pattern # pattern of the current default type
1082
1081
1083 if sourceinfo is set, returns a list of tuples:
1082 if sourceinfo is set, returns a list of tuples:
1084 (pattern, lineno, originalline). This is useful to debug ignore patterns.
1083 (pattern, lineno, originalline). This is useful to debug ignore patterns.
1085 '''
1084 '''
1086
1085
1087 syntaxes = {'re': 'relre:', 'regexp': 'relre:', 'glob': 'relglob:',
1086 syntaxes = {'re': 'relre:', 'regexp': 'relre:', 'glob': 'relglob:',
1088 'include': 'include', 'subinclude': 'subinclude'}
1087 'include': 'include', 'subinclude': 'subinclude'}
1089 syntax = 'relre:'
1088 syntax = 'relre:'
1090 patterns = []
1089 patterns = []
1091
1090
1092 fp = open(filepath, 'rb')
1091 fp = open(filepath, 'rb')
1093 for lineno, line in enumerate(util.iterfile(fp), start=1):
1092 for lineno, line in enumerate(util.iterfile(fp), start=1):
1094 if "#" in line:
1093 if "#" in line:
1095 global _commentre
1094 global _commentre
1096 if not _commentre:
1095 if not _commentre:
1097 _commentre = util.re.compile(br'((?:^|[^\\])(?:\\\\)*)#.*')
1096 _commentre = util.re.compile(br'((?:^|[^\\])(?:\\\\)*)#.*')
1098 # remove comments prefixed by an even number of escapes
1097 # remove comments prefixed by an even number of escapes
1099 m = _commentre.search(line)
1098 m = _commentre.search(line)
1100 if m:
1099 if m:
1101 line = line[:m.end(1)]
1100 line = line[:m.end(1)]
1102 # fixup properly escaped comments that survived the above
1101 # fixup properly escaped comments that survived the above
1103 line = line.replace("\\#", "#")
1102 line = line.replace("\\#", "#")
1104 line = line.rstrip()
1103 line = line.rstrip()
1105 if not line:
1104 if not line:
1106 continue
1105 continue
1107
1106
1108 if line.startswith('syntax:'):
1107 if line.startswith('syntax:'):
1109 s = line[7:].strip()
1108 s = line[7:].strip()
1110 try:
1109 try:
1111 syntax = syntaxes[s]
1110 syntax = syntaxes[s]
1112 except KeyError:
1111 except KeyError:
1113 if warn:
1112 if warn:
1114 warn(_("%s: ignoring invalid syntax '%s'\n") %
1113 warn(_("%s: ignoring invalid syntax '%s'\n") %
1115 (filepath, s))
1114 (filepath, s))
1116 continue
1115 continue
1117
1116
1118 linesyntax = syntax
1117 linesyntax = syntax
1119 for s, rels in syntaxes.iteritems():
1118 for s, rels in syntaxes.iteritems():
1120 if line.startswith(rels):
1119 if line.startswith(rels):
1121 linesyntax = rels
1120 linesyntax = rels
1122 line = line[len(rels):]
1121 line = line[len(rels):]
1123 break
1122 break
1124 elif line.startswith(s+':'):
1123 elif line.startswith(s+':'):
1125 linesyntax = rels
1124 linesyntax = rels
1126 line = line[len(s) + 1:]
1125 line = line[len(s) + 1:]
1127 break
1126 break
1128 if sourceinfo:
1127 if sourceinfo:
1129 patterns.append((linesyntax + line, lineno, line))
1128 patterns.append((linesyntax + line, lineno, line))
1130 else:
1129 else:
1131 patterns.append(linesyntax + line)
1130 patterns.append(linesyntax + line)
1132 fp.close()
1131 fp.close()
1133 return patterns
1132 return patterns
@@ -1,1816 +1,1822 b''
1 # subrepo.py - sub-repository classes and factory
1 # subrepo.py - sub-repository classes and factory
2 #
2 #
3 # Copyright 2009-2010 Matt Mackall <mpm@selenic.com>
3 # Copyright 2009-2010 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 copy
10 import copy
11 import errno
11 import errno
12 import hashlib
12 import hashlib
13 import os
13 import os
14 import posixpath
14 import posixpath
15 import re
15 import re
16 import stat
16 import stat
17 import subprocess
17 import subprocess
18 import sys
18 import sys
19 import tarfile
19 import tarfile
20 import xml.dom.minidom
20 import xml.dom.minidom
21
21
22 from .i18n import _
22 from .i18n import _
23 from . import (
23 from . import (
24 cmdutil,
24 cmdutil,
25 encoding,
25 encoding,
26 error,
26 error,
27 exchange,
27 exchange,
28 logcmdutil,
28 logcmdutil,
29 match as matchmod,
29 match as matchmod,
30 node,
30 node,
31 pathutil,
31 pathutil,
32 phases,
32 phases,
33 pycompat,
33 pycompat,
34 scmutil,
34 scmutil,
35 subrepoutil,
35 subrepoutil,
36 util,
36 util,
37 vfs as vfsmod,
37 vfs as vfsmod,
38 )
38 )
39 from .utils import (
39 from .utils import (
40 dateutil,
40 dateutil,
41 procutil,
41 procutil,
42 stringutil,
42 stringutil,
43 )
43 )
44
44
45 hg = None
45 hg = None
46 reporelpath = subrepoutil.reporelpath
46 reporelpath = subrepoutil.reporelpath
47 subrelpath = subrepoutil.subrelpath
47 subrelpath = subrepoutil.subrelpath
48 _abssource = subrepoutil._abssource
48 _abssource = subrepoutil._abssource
49 propertycache = util.propertycache
49 propertycache = util.propertycache
50
50
51 def _expandedabspath(path):
51 def _expandedabspath(path):
52 '''
52 '''
53 get a path or url and if it is a path expand it and return an absolute path
53 get a path or url and if it is a path expand it and return an absolute path
54 '''
54 '''
55 expandedpath = util.urllocalpath(util.expandpath(path))
55 expandedpath = util.urllocalpath(util.expandpath(path))
56 u = util.url(expandedpath)
56 u = util.url(expandedpath)
57 if not u.scheme:
57 if not u.scheme:
58 path = util.normpath(os.path.abspath(u.path))
58 path = util.normpath(os.path.abspath(u.path))
59 return path
59 return path
60
60
61 def _getstorehashcachename(remotepath):
61 def _getstorehashcachename(remotepath):
62 '''get a unique filename for the store hash cache of a remote repository'''
62 '''get a unique filename for the store hash cache of a remote repository'''
63 return node.hex(hashlib.sha1(_expandedabspath(remotepath)).digest())[0:12]
63 return node.hex(hashlib.sha1(_expandedabspath(remotepath)).digest())[0:12]
64
64
65 class SubrepoAbort(error.Abort):
65 class SubrepoAbort(error.Abort):
66 """Exception class used to avoid handling a subrepo error more than once"""
66 """Exception class used to avoid handling a subrepo error more than once"""
67 def __init__(self, *args, **kw):
67 def __init__(self, *args, **kw):
68 self.subrepo = kw.pop(r'subrepo', None)
68 self.subrepo = kw.pop(r'subrepo', None)
69 self.cause = kw.pop(r'cause', None)
69 self.cause = kw.pop(r'cause', None)
70 error.Abort.__init__(self, *args, **kw)
70 error.Abort.__init__(self, *args, **kw)
71
71
72 def annotatesubrepoerror(func):
72 def annotatesubrepoerror(func):
73 def decoratedmethod(self, *args, **kargs):
73 def decoratedmethod(self, *args, **kargs):
74 try:
74 try:
75 res = func(self, *args, **kargs)
75 res = func(self, *args, **kargs)
76 except SubrepoAbort as ex:
76 except SubrepoAbort as ex:
77 # This exception has already been handled
77 # This exception has already been handled
78 raise ex
78 raise ex
79 except error.Abort as ex:
79 except error.Abort as ex:
80 subrepo = subrelpath(self)
80 subrepo = subrelpath(self)
81 errormsg = (stringutil.forcebytestr(ex) + ' '
81 errormsg = (stringutil.forcebytestr(ex) + ' '
82 + _('(in subrepository "%s")') % subrepo)
82 + _('(in subrepository "%s")') % subrepo)
83 # avoid handling this exception by raising a SubrepoAbort exception
83 # avoid handling this exception by raising a SubrepoAbort exception
84 raise SubrepoAbort(errormsg, hint=ex.hint, subrepo=subrepo,
84 raise SubrepoAbort(errormsg, hint=ex.hint, subrepo=subrepo,
85 cause=sys.exc_info())
85 cause=sys.exc_info())
86 return res
86 return res
87 return decoratedmethod
87 return decoratedmethod
88
88
89 def _updateprompt(ui, sub, dirty, local, remote):
89 def _updateprompt(ui, sub, dirty, local, remote):
90 if dirty:
90 if dirty:
91 msg = (_(' subrepository sources for %s differ\n'
91 msg = (_(' subrepository sources for %s differ\n'
92 'use (l)ocal source (%s) or (r)emote source (%s)?'
92 'use (l)ocal source (%s) or (r)emote source (%s)?'
93 '$$ &Local $$ &Remote')
93 '$$ &Local $$ &Remote')
94 % (subrelpath(sub), local, remote))
94 % (subrelpath(sub), local, remote))
95 else:
95 else:
96 msg = (_(' subrepository sources for %s differ (in checked out '
96 msg = (_(' subrepository sources for %s differ (in checked out '
97 'version)\n'
97 'version)\n'
98 'use (l)ocal source (%s) or (r)emote source (%s)?'
98 'use (l)ocal source (%s) or (r)emote source (%s)?'
99 '$$ &Local $$ &Remote')
99 '$$ &Local $$ &Remote')
100 % (subrelpath(sub), local, remote))
100 % (subrelpath(sub), local, remote))
101 return ui.promptchoice(msg, 0)
101 return ui.promptchoice(msg, 0)
102
102
103 def _sanitize(ui, vfs, ignore):
103 def _sanitize(ui, vfs, ignore):
104 for dirname, dirs, names in vfs.walk():
104 for dirname, dirs, names in vfs.walk():
105 for i, d in enumerate(dirs):
105 for i, d in enumerate(dirs):
106 if d.lower() == ignore:
106 if d.lower() == ignore:
107 del dirs[i]
107 del dirs[i]
108 break
108 break
109 if vfs.basename(dirname).lower() != '.hg':
109 if vfs.basename(dirname).lower() != '.hg':
110 continue
110 continue
111 for f in names:
111 for f in names:
112 if f.lower() == 'hgrc':
112 if f.lower() == 'hgrc':
113 ui.warn(_("warning: removing potentially hostile 'hgrc' "
113 ui.warn(_("warning: removing potentially hostile 'hgrc' "
114 "in '%s'\n") % vfs.join(dirname))
114 "in '%s'\n") % vfs.join(dirname))
115 vfs.unlink(vfs.reljoin(dirname, f))
115 vfs.unlink(vfs.reljoin(dirname, f))
116
116
117 def _auditsubrepopath(repo, path):
117 def _auditsubrepopath(repo, path):
118 # auditor doesn't check if the path itself is a symlink
118 # auditor doesn't check if the path itself is a symlink
119 pathutil.pathauditor(repo.root)(path)
119 pathutil.pathauditor(repo.root)(path)
120 if repo.wvfs.islink(path):
120 if repo.wvfs.islink(path):
121 raise error.Abort(_("subrepo '%s' traverses symbolic link") % path)
121 raise error.Abort(_("subrepo '%s' traverses symbolic link") % path)
122
122
123 SUBREPO_ALLOWED_DEFAULTS = {
123 SUBREPO_ALLOWED_DEFAULTS = {
124 'hg': True,
124 'hg': True,
125 'git': False,
125 'git': False,
126 'svn': False,
126 'svn': False,
127 }
127 }
128
128
129 def _checktype(ui, kind):
129 def _checktype(ui, kind):
130 # subrepos.allowed is a master kill switch. If disabled, subrepos are
130 # subrepos.allowed is a master kill switch. If disabled, subrepos are
131 # disabled period.
131 # disabled period.
132 if not ui.configbool('subrepos', 'allowed', True):
132 if not ui.configbool('subrepos', 'allowed', True):
133 raise error.Abort(_('subrepos not enabled'),
133 raise error.Abort(_('subrepos not enabled'),
134 hint=_("see 'hg help config.subrepos' for details"))
134 hint=_("see 'hg help config.subrepos' for details"))
135
135
136 default = SUBREPO_ALLOWED_DEFAULTS.get(kind, False)
136 default = SUBREPO_ALLOWED_DEFAULTS.get(kind, False)
137 if not ui.configbool('subrepos', '%s:allowed' % kind, default):
137 if not ui.configbool('subrepos', '%s:allowed' % kind, default):
138 raise error.Abort(_('%s subrepos not allowed') % kind,
138 raise error.Abort(_('%s subrepos not allowed') % kind,
139 hint=_("see 'hg help config.subrepos' for details"))
139 hint=_("see 'hg help config.subrepos' for details"))
140
140
141 if kind not in types:
141 if kind not in types:
142 raise error.Abort(_('unknown subrepo type %s') % kind)
142 raise error.Abort(_('unknown subrepo type %s') % kind)
143
143
144 def subrepo(ctx, path, allowwdir=False, allowcreate=True):
144 def subrepo(ctx, path, allowwdir=False, allowcreate=True):
145 """return instance of the right subrepo class for subrepo in path"""
145 """return instance of the right subrepo class for subrepo in path"""
146 # subrepo inherently violates our import layering rules
146 # subrepo inherently violates our import layering rules
147 # because it wants to make repo objects from deep inside the stack
147 # because it wants to make repo objects from deep inside the stack
148 # so we manually delay the circular imports to not break
148 # so we manually delay the circular imports to not break
149 # scripts that don't use our demand-loading
149 # scripts that don't use our demand-loading
150 global hg
150 global hg
151 from . import hg as h
151 from . import hg as h
152 hg = h
152 hg = h
153
153
154 repo = ctx.repo()
154 repo = ctx.repo()
155 _auditsubrepopath(repo, path)
155 _auditsubrepopath(repo, path)
156 state = ctx.substate[path]
156 state = ctx.substate[path]
157 _checktype(repo.ui, state[2])
157 _checktype(repo.ui, state[2])
158 if allowwdir:
158 if allowwdir:
159 state = (state[0], ctx.subrev(path), state[2])
159 state = (state[0], ctx.subrev(path), state[2])
160 return types[state[2]](ctx, path, state[:2], allowcreate)
160 return types[state[2]](ctx, path, state[:2], allowcreate)
161
161
162 def nullsubrepo(ctx, path, pctx):
162 def nullsubrepo(ctx, path, pctx):
163 """return an empty subrepo in pctx for the extant subrepo in ctx"""
163 """return an empty subrepo in pctx for the extant subrepo in ctx"""
164 # subrepo inherently violates our import layering rules
164 # subrepo inherently violates our import layering rules
165 # because it wants to make repo objects from deep inside the stack
165 # because it wants to make repo objects from deep inside the stack
166 # so we manually delay the circular imports to not break
166 # so we manually delay the circular imports to not break
167 # scripts that don't use our demand-loading
167 # scripts that don't use our demand-loading
168 global hg
168 global hg
169 from . import hg as h
169 from . import hg as h
170 hg = h
170 hg = h
171
171
172 repo = ctx.repo()
172 repo = ctx.repo()
173 _auditsubrepopath(repo, path)
173 _auditsubrepopath(repo, path)
174 state = ctx.substate[path]
174 state = ctx.substate[path]
175 _checktype(repo.ui, state[2])
175 _checktype(repo.ui, state[2])
176 subrev = ''
176 subrev = ''
177 if state[2] == 'hg':
177 if state[2] == 'hg':
178 subrev = "0" * 40
178 subrev = "0" * 40
179 return types[state[2]](pctx, path, (state[0], subrev), True)
179 return types[state[2]](pctx, path, (state[0], subrev), True)
180
180
181 # subrepo classes need to implement the following abstract class:
181 # subrepo classes need to implement the following abstract class:
182
182
183 class abstractsubrepo(object):
183 class abstractsubrepo(object):
184
184
185 def __init__(self, ctx, path):
185 def __init__(self, ctx, path):
186 """Initialize abstractsubrepo part
186 """Initialize abstractsubrepo part
187
187
188 ``ctx`` is the context referring this subrepository in the
188 ``ctx`` is the context referring this subrepository in the
189 parent repository.
189 parent repository.
190
190
191 ``path`` is the path to this subrepository as seen from
191 ``path`` is the path to this subrepository as seen from
192 innermost repository.
192 innermost repository.
193 """
193 """
194 self.ui = ctx.repo().ui
194 self.ui = ctx.repo().ui
195 self._ctx = ctx
195 self._ctx = ctx
196 self._path = path
196 self._path = path
197
197
198 def addwebdirpath(self, serverpath, webconf):
198 def addwebdirpath(self, serverpath, webconf):
199 """Add the hgwebdir entries for this subrepo, and any of its subrepos.
199 """Add the hgwebdir entries for this subrepo, and any of its subrepos.
200
200
201 ``serverpath`` is the path component of the URL for this repo.
201 ``serverpath`` is the path component of the URL for this repo.
202
202
203 ``webconf`` is the dictionary of hgwebdir entries.
203 ``webconf`` is the dictionary of hgwebdir entries.
204 """
204 """
205 pass
205 pass
206
206
207 def storeclean(self, path):
207 def storeclean(self, path):
208 """
208 """
209 returns true if the repository has not changed since it was last
209 returns true if the repository has not changed since it was last
210 cloned from or pushed to a given repository.
210 cloned from or pushed to a given repository.
211 """
211 """
212 return False
212 return False
213
213
214 def dirty(self, ignoreupdate=False, missing=False):
214 def dirty(self, ignoreupdate=False, missing=False):
215 """returns true if the dirstate of the subrepo is dirty or does not
215 """returns true if the dirstate of the subrepo is dirty or does not
216 match current stored state. If ignoreupdate is true, only check
216 match current stored state. If ignoreupdate is true, only check
217 whether the subrepo has uncommitted changes in its dirstate. If missing
217 whether the subrepo has uncommitted changes in its dirstate. If missing
218 is true, check for deleted files.
218 is true, check for deleted files.
219 """
219 """
220 raise NotImplementedError
220 raise NotImplementedError
221
221
222 def dirtyreason(self, ignoreupdate=False, missing=False):
222 def dirtyreason(self, ignoreupdate=False, missing=False):
223 """return reason string if it is ``dirty()``
223 """return reason string if it is ``dirty()``
224
224
225 Returned string should have enough information for the message
225 Returned string should have enough information for the message
226 of exception.
226 of exception.
227
227
228 This returns None, otherwise.
228 This returns None, otherwise.
229 """
229 """
230 if self.dirty(ignoreupdate=ignoreupdate, missing=missing):
230 if self.dirty(ignoreupdate=ignoreupdate, missing=missing):
231 return _('uncommitted changes in subrepository "%s"'
231 return _('uncommitted changes in subrepository "%s"'
232 ) % subrelpath(self)
232 ) % subrelpath(self)
233
233
234 def bailifchanged(self, ignoreupdate=False, hint=None):
234 def bailifchanged(self, ignoreupdate=False, hint=None):
235 """raise Abort if subrepository is ``dirty()``
235 """raise Abort if subrepository is ``dirty()``
236 """
236 """
237 dirtyreason = self.dirtyreason(ignoreupdate=ignoreupdate,
237 dirtyreason = self.dirtyreason(ignoreupdate=ignoreupdate,
238 missing=True)
238 missing=True)
239 if dirtyreason:
239 if dirtyreason:
240 raise error.Abort(dirtyreason, hint=hint)
240 raise error.Abort(dirtyreason, hint=hint)
241
241
242 def basestate(self):
242 def basestate(self):
243 """current working directory base state, disregarding .hgsubstate
243 """current working directory base state, disregarding .hgsubstate
244 state and working directory modifications"""
244 state and working directory modifications"""
245 raise NotImplementedError
245 raise NotImplementedError
246
246
247 def checknested(self, path):
247 def checknested(self, path):
248 """check if path is a subrepository within this repository"""
248 """check if path is a subrepository within this repository"""
249 return False
249 return False
250
250
251 def commit(self, text, user, date):
251 def commit(self, text, user, date):
252 """commit the current changes to the subrepo with the given
252 """commit the current changes to the subrepo with the given
253 log message. Use given user and date if possible. Return the
253 log message. Use given user and date if possible. Return the
254 new state of the subrepo.
254 new state of the subrepo.
255 """
255 """
256 raise NotImplementedError
256 raise NotImplementedError
257
257
258 def phase(self, state):
258 def phase(self, state):
259 """returns phase of specified state in the subrepository.
259 """returns phase of specified state in the subrepository.
260 """
260 """
261 return phases.public
261 return phases.public
262
262
263 def remove(self):
263 def remove(self):
264 """remove the subrepo
264 """remove the subrepo
265
265
266 (should verify the dirstate is not dirty first)
266 (should verify the dirstate is not dirty first)
267 """
267 """
268 raise NotImplementedError
268 raise NotImplementedError
269
269
270 def get(self, state, overwrite=False):
270 def get(self, state, overwrite=False):
271 """run whatever commands are needed to put the subrepo into
271 """run whatever commands are needed to put the subrepo into
272 this state
272 this state
273 """
273 """
274 raise NotImplementedError
274 raise NotImplementedError
275
275
276 def merge(self, state):
276 def merge(self, state):
277 """merge currently-saved state with the new state."""
277 """merge currently-saved state with the new state."""
278 raise NotImplementedError
278 raise NotImplementedError
279
279
280 def push(self, opts):
280 def push(self, opts):
281 """perform whatever action is analogous to 'hg push'
281 """perform whatever action is analogous to 'hg push'
282
282
283 This may be a no-op on some systems.
283 This may be a no-op on some systems.
284 """
284 """
285 raise NotImplementedError
285 raise NotImplementedError
286
286
287 def add(self, ui, match, prefix, explicitonly, **opts):
287 def add(self, ui, match, prefix, explicitonly, **opts):
288 return []
288 return []
289
289
290 def addremove(self, matcher, prefix, opts):
290 def addremove(self, matcher, prefix, opts):
291 self.ui.warn("%s: %s" % (prefix, _("addremove is not supported")))
291 self.ui.warn("%s: %s" % (prefix, _("addremove is not supported")))
292 return 1
292 return 1
293
293
294 def cat(self, match, fm, fntemplate, prefix, **opts):
294 def cat(self, match, fm, fntemplate, prefix, **opts):
295 return 1
295 return 1
296
296
297 def status(self, rev2, **opts):
297 def status(self, rev2, **opts):
298 return scmutil.status([], [], [], [], [], [], [])
298 return scmutil.status([], [], [], [], [], [], [])
299
299
300 def diff(self, ui, diffopts, node2, match, prefix, **opts):
300 def diff(self, ui, diffopts, node2, match, prefix, **opts):
301 pass
301 pass
302
302
303 def outgoing(self, ui, dest, opts):
303 def outgoing(self, ui, dest, opts):
304 return 1
304 return 1
305
305
306 def incoming(self, ui, source, opts):
306 def incoming(self, ui, source, opts):
307 return 1
307 return 1
308
308
309 def files(self):
309 def files(self):
310 """return filename iterator"""
310 """return filename iterator"""
311 raise NotImplementedError
311 raise NotImplementedError
312
312
313 def filedata(self, name, decode):
313 def filedata(self, name, decode):
314 """return file data, optionally passed through repo decoders"""
314 """return file data, optionally passed through repo decoders"""
315 raise NotImplementedError
315 raise NotImplementedError
316
316
317 def fileflags(self, name):
317 def fileflags(self, name):
318 """return file flags"""
318 """return file flags"""
319 return ''
319 return ''
320
320
321 def getfileset(self, expr):
321 def matchfileset(self, expr, badfn=None):
322 """Resolve the fileset expression for this repo"""
322 """Resolve the fileset expression for this repo"""
323 return set()
323 return matchmod.nevermatcher(self.wvfs.base, '', badfn=badfn)
324
324
325 def printfiles(self, ui, m, fm, fmt, subrepos):
325 def printfiles(self, ui, m, fm, fmt, subrepos):
326 """handle the files command for this subrepo"""
326 """handle the files command for this subrepo"""
327 return 1
327 return 1
328
328
329 def archive(self, archiver, prefix, match=None, decode=True):
329 def archive(self, archiver, prefix, match=None, decode=True):
330 if match is not None:
330 if match is not None:
331 files = [f for f in self.files() if match(f)]
331 files = [f for f in self.files() if match(f)]
332 else:
332 else:
333 files = self.files()
333 files = self.files()
334 total = len(files)
334 total = len(files)
335 relpath = subrelpath(self)
335 relpath = subrelpath(self)
336 progress = self.ui.makeprogress(_('archiving (%s)') % relpath,
336 progress = self.ui.makeprogress(_('archiving (%s)') % relpath,
337 unit=_('files'), total=total)
337 unit=_('files'), total=total)
338 progress.update(0)
338 progress.update(0)
339 for name in files:
339 for name in files:
340 flags = self.fileflags(name)
340 flags = self.fileflags(name)
341 mode = 'x' in flags and 0o755 or 0o644
341 mode = 'x' in flags and 0o755 or 0o644
342 symlink = 'l' in flags
342 symlink = 'l' in flags
343 archiver.addfile(prefix + self._path + '/' + name,
343 archiver.addfile(prefix + self._path + '/' + name,
344 mode, symlink, self.filedata(name, decode))
344 mode, symlink, self.filedata(name, decode))
345 progress.increment()
345 progress.increment()
346 progress.complete()
346 progress.complete()
347 return total
347 return total
348
348
349 def walk(self, match):
349 def walk(self, match):
350 '''
350 '''
351 walk recursively through the directory tree, finding all files
351 walk recursively through the directory tree, finding all files
352 matched by the match function
352 matched by the match function
353 '''
353 '''
354
354
355 def forget(self, match, prefix, dryrun, interactive):
355 def forget(self, match, prefix, dryrun, interactive):
356 return ([], [])
356 return ([], [])
357
357
358 def removefiles(self, matcher, prefix, after, force, subrepos,
358 def removefiles(self, matcher, prefix, after, force, subrepos,
359 dryrun, warnings):
359 dryrun, warnings):
360 """remove the matched files from the subrepository and the filesystem,
360 """remove the matched files from the subrepository and the filesystem,
361 possibly by force and/or after the file has been removed from the
361 possibly by force and/or after the file has been removed from the
362 filesystem. Return 0 on success, 1 on any warning.
362 filesystem. Return 0 on success, 1 on any warning.
363 """
363 """
364 warnings.append(_("warning: removefiles not implemented (%s)")
364 warnings.append(_("warning: removefiles not implemented (%s)")
365 % self._path)
365 % self._path)
366 return 1
366 return 1
367
367
368 def revert(self, substate, *pats, **opts):
368 def revert(self, substate, *pats, **opts):
369 self.ui.warn(_('%s: reverting %s subrepos is unsupported\n') \
369 self.ui.warn(_('%s: reverting %s subrepos is unsupported\n') \
370 % (substate[0], substate[2]))
370 % (substate[0], substate[2]))
371 return []
371 return []
372
372
373 def shortid(self, revid):
373 def shortid(self, revid):
374 return revid
374 return revid
375
375
376 def unshare(self):
376 def unshare(self):
377 '''
377 '''
378 convert this repository from shared to normal storage.
378 convert this repository from shared to normal storage.
379 '''
379 '''
380
380
381 def verify(self):
381 def verify(self):
382 '''verify the integrity of the repository. Return 0 on success or
382 '''verify the integrity of the repository. Return 0 on success or
383 warning, 1 on any error.
383 warning, 1 on any error.
384 '''
384 '''
385 return 0
385 return 0
386
386
387 @propertycache
387 @propertycache
388 def wvfs(self):
388 def wvfs(self):
389 """return vfs to access the working directory of this subrepository
389 """return vfs to access the working directory of this subrepository
390 """
390 """
391 return vfsmod.vfs(self._ctx.repo().wvfs.join(self._path))
391 return vfsmod.vfs(self._ctx.repo().wvfs.join(self._path))
392
392
393 @propertycache
393 @propertycache
394 def _relpath(self):
394 def _relpath(self):
395 """return path to this subrepository as seen from outermost repository
395 """return path to this subrepository as seen from outermost repository
396 """
396 """
397 return self.wvfs.reljoin(reporelpath(self._ctx.repo()), self._path)
397 return self.wvfs.reljoin(reporelpath(self._ctx.repo()), self._path)
398
398
399 class hgsubrepo(abstractsubrepo):
399 class hgsubrepo(abstractsubrepo):
400 def __init__(self, ctx, path, state, allowcreate):
400 def __init__(self, ctx, path, state, allowcreate):
401 super(hgsubrepo, self).__init__(ctx, path)
401 super(hgsubrepo, self).__init__(ctx, path)
402 self._state = state
402 self._state = state
403 r = ctx.repo()
403 r = ctx.repo()
404 root = r.wjoin(path)
404 root = r.wjoin(path)
405 create = allowcreate and not r.wvfs.exists('%s/.hg' % path)
405 create = allowcreate and not r.wvfs.exists('%s/.hg' % path)
406 self._repo = hg.repository(r.baseui, root, create=create)
406 self._repo = hg.repository(r.baseui, root, create=create)
407
407
408 # Propagate the parent's --hidden option
408 # Propagate the parent's --hidden option
409 if r is r.unfiltered():
409 if r is r.unfiltered():
410 self._repo = self._repo.unfiltered()
410 self._repo = self._repo.unfiltered()
411
411
412 self.ui = self._repo.ui
412 self.ui = self._repo.ui
413 for s, k in [('ui', 'commitsubrepos')]:
413 for s, k in [('ui', 'commitsubrepos')]:
414 v = r.ui.config(s, k)
414 v = r.ui.config(s, k)
415 if v:
415 if v:
416 self.ui.setconfig(s, k, v, 'subrepo')
416 self.ui.setconfig(s, k, v, 'subrepo')
417 # internal config: ui._usedassubrepo
417 # internal config: ui._usedassubrepo
418 self.ui.setconfig('ui', '_usedassubrepo', 'True', 'subrepo')
418 self.ui.setconfig('ui', '_usedassubrepo', 'True', 'subrepo')
419 self._initrepo(r, state[0], create)
419 self._initrepo(r, state[0], create)
420
420
421 @annotatesubrepoerror
421 @annotatesubrepoerror
422 def addwebdirpath(self, serverpath, webconf):
422 def addwebdirpath(self, serverpath, webconf):
423 cmdutil.addwebdirpath(self._repo, subrelpath(self), webconf)
423 cmdutil.addwebdirpath(self._repo, subrelpath(self), webconf)
424
424
425 def storeclean(self, path):
425 def storeclean(self, path):
426 with self._repo.lock():
426 with self._repo.lock():
427 return self._storeclean(path)
427 return self._storeclean(path)
428
428
429 def _storeclean(self, path):
429 def _storeclean(self, path):
430 clean = True
430 clean = True
431 itercache = self._calcstorehash(path)
431 itercache = self._calcstorehash(path)
432 for filehash in self._readstorehashcache(path):
432 for filehash in self._readstorehashcache(path):
433 if filehash != next(itercache, None):
433 if filehash != next(itercache, None):
434 clean = False
434 clean = False
435 break
435 break
436 if clean:
436 if clean:
437 # if not empty:
437 # if not empty:
438 # the cached and current pull states have a different size
438 # the cached and current pull states have a different size
439 clean = next(itercache, None) is None
439 clean = next(itercache, None) is None
440 return clean
440 return clean
441
441
442 def _calcstorehash(self, remotepath):
442 def _calcstorehash(self, remotepath):
443 '''calculate a unique "store hash"
443 '''calculate a unique "store hash"
444
444
445 This method is used to to detect when there are changes that may
445 This method is used to to detect when there are changes that may
446 require a push to a given remote path.'''
446 require a push to a given remote path.'''
447 # sort the files that will be hashed in increasing (likely) file size
447 # sort the files that will be hashed in increasing (likely) file size
448 filelist = ('bookmarks', 'store/phaseroots', 'store/00changelog.i')
448 filelist = ('bookmarks', 'store/phaseroots', 'store/00changelog.i')
449 yield '# %s\n' % _expandedabspath(remotepath)
449 yield '# %s\n' % _expandedabspath(remotepath)
450 vfs = self._repo.vfs
450 vfs = self._repo.vfs
451 for relname in filelist:
451 for relname in filelist:
452 filehash = node.hex(hashlib.sha1(vfs.tryread(relname)).digest())
452 filehash = node.hex(hashlib.sha1(vfs.tryread(relname)).digest())
453 yield '%s = %s\n' % (relname, filehash)
453 yield '%s = %s\n' % (relname, filehash)
454
454
455 @propertycache
455 @propertycache
456 def _cachestorehashvfs(self):
456 def _cachestorehashvfs(self):
457 return vfsmod.vfs(self._repo.vfs.join('cache/storehash'))
457 return vfsmod.vfs(self._repo.vfs.join('cache/storehash'))
458
458
459 def _readstorehashcache(self, remotepath):
459 def _readstorehashcache(self, remotepath):
460 '''read the store hash cache for a given remote repository'''
460 '''read the store hash cache for a given remote repository'''
461 cachefile = _getstorehashcachename(remotepath)
461 cachefile = _getstorehashcachename(remotepath)
462 return self._cachestorehashvfs.tryreadlines(cachefile, 'r')
462 return self._cachestorehashvfs.tryreadlines(cachefile, 'r')
463
463
464 def _cachestorehash(self, remotepath):
464 def _cachestorehash(self, remotepath):
465 '''cache the current store hash
465 '''cache the current store hash
466
466
467 Each remote repo requires its own store hash cache, because a subrepo
467 Each remote repo requires its own store hash cache, because a subrepo
468 store may be "clean" versus a given remote repo, but not versus another
468 store may be "clean" versus a given remote repo, but not versus another
469 '''
469 '''
470 cachefile = _getstorehashcachename(remotepath)
470 cachefile = _getstorehashcachename(remotepath)
471 with self._repo.lock():
471 with self._repo.lock():
472 storehash = list(self._calcstorehash(remotepath))
472 storehash = list(self._calcstorehash(remotepath))
473 vfs = self._cachestorehashvfs
473 vfs = self._cachestorehashvfs
474 vfs.writelines(cachefile, storehash, mode='wb', notindexed=True)
474 vfs.writelines(cachefile, storehash, mode='wb', notindexed=True)
475
475
476 def _getctx(self):
476 def _getctx(self):
477 '''fetch the context for this subrepo revision, possibly a workingctx
477 '''fetch the context for this subrepo revision, possibly a workingctx
478 '''
478 '''
479 if self._ctx.rev() is None:
479 if self._ctx.rev() is None:
480 return self._repo[None] # workingctx if parent is workingctx
480 return self._repo[None] # workingctx if parent is workingctx
481 else:
481 else:
482 rev = self._state[1]
482 rev = self._state[1]
483 return self._repo[rev]
483 return self._repo[rev]
484
484
485 @annotatesubrepoerror
485 @annotatesubrepoerror
486 def _initrepo(self, parentrepo, source, create):
486 def _initrepo(self, parentrepo, source, create):
487 self._repo._subparent = parentrepo
487 self._repo._subparent = parentrepo
488 self._repo._subsource = source
488 self._repo._subsource = source
489
489
490 if create:
490 if create:
491 lines = ['[paths]\n']
491 lines = ['[paths]\n']
492
492
493 def addpathconfig(key, value):
493 def addpathconfig(key, value):
494 if value:
494 if value:
495 lines.append('%s = %s\n' % (key, value))
495 lines.append('%s = %s\n' % (key, value))
496 self.ui.setconfig('paths', key, value, 'subrepo')
496 self.ui.setconfig('paths', key, value, 'subrepo')
497
497
498 defpath = _abssource(self._repo, abort=False)
498 defpath = _abssource(self._repo, abort=False)
499 defpushpath = _abssource(self._repo, True, abort=False)
499 defpushpath = _abssource(self._repo, True, abort=False)
500 addpathconfig('default', defpath)
500 addpathconfig('default', defpath)
501 if defpath != defpushpath:
501 if defpath != defpushpath:
502 addpathconfig('default-push', defpushpath)
502 addpathconfig('default-push', defpushpath)
503
503
504 self._repo.vfs.write('hgrc', util.tonativeeol(''.join(lines)))
504 self._repo.vfs.write('hgrc', util.tonativeeol(''.join(lines)))
505
505
506 @annotatesubrepoerror
506 @annotatesubrepoerror
507 def add(self, ui, match, prefix, explicitonly, **opts):
507 def add(self, ui, match, prefix, explicitonly, **opts):
508 return cmdutil.add(ui, self._repo, match,
508 return cmdutil.add(ui, self._repo, match,
509 self.wvfs.reljoin(prefix, self._path),
509 self.wvfs.reljoin(prefix, self._path),
510 explicitonly, **opts)
510 explicitonly, **opts)
511
511
512 @annotatesubrepoerror
512 @annotatesubrepoerror
513 def addremove(self, m, prefix, opts):
513 def addremove(self, m, prefix, opts):
514 # In the same way as sub directories are processed, once in a subrepo,
514 # In the same way as sub directories are processed, once in a subrepo,
515 # always entry any of its subrepos. Don't corrupt the options that will
515 # always entry any of its subrepos. Don't corrupt the options that will
516 # be used to process sibling subrepos however.
516 # be used to process sibling subrepos however.
517 opts = copy.copy(opts)
517 opts = copy.copy(opts)
518 opts['subrepos'] = True
518 opts['subrepos'] = True
519 return scmutil.addremove(self._repo, m,
519 return scmutil.addremove(self._repo, m,
520 self.wvfs.reljoin(prefix, self._path), opts)
520 self.wvfs.reljoin(prefix, self._path), opts)
521
521
522 @annotatesubrepoerror
522 @annotatesubrepoerror
523 def cat(self, match, fm, fntemplate, prefix, **opts):
523 def cat(self, match, fm, fntemplate, prefix, **opts):
524 rev = self._state[1]
524 rev = self._state[1]
525 ctx = self._repo[rev]
525 ctx = self._repo[rev]
526 return cmdutil.cat(self.ui, self._repo, ctx, match, fm, fntemplate,
526 return cmdutil.cat(self.ui, self._repo, ctx, match, fm, fntemplate,
527 prefix, **opts)
527 prefix, **opts)
528
528
529 @annotatesubrepoerror
529 @annotatesubrepoerror
530 def status(self, rev2, **opts):
530 def status(self, rev2, **opts):
531 try:
531 try:
532 rev1 = self._state[1]
532 rev1 = self._state[1]
533 ctx1 = self._repo[rev1]
533 ctx1 = self._repo[rev1]
534 ctx2 = self._repo[rev2]
534 ctx2 = self._repo[rev2]
535 return self._repo.status(ctx1, ctx2, **opts)
535 return self._repo.status(ctx1, ctx2, **opts)
536 except error.RepoLookupError as inst:
536 except error.RepoLookupError as inst:
537 self.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
537 self.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
538 % (inst, subrelpath(self)))
538 % (inst, subrelpath(self)))
539 return scmutil.status([], [], [], [], [], [], [])
539 return scmutil.status([], [], [], [], [], [], [])
540
540
541 @annotatesubrepoerror
541 @annotatesubrepoerror
542 def diff(self, ui, diffopts, node2, match, prefix, **opts):
542 def diff(self, ui, diffopts, node2, match, prefix, **opts):
543 try:
543 try:
544 node1 = node.bin(self._state[1])
544 node1 = node.bin(self._state[1])
545 # We currently expect node2 to come from substate and be
545 # We currently expect node2 to come from substate and be
546 # in hex format
546 # in hex format
547 if node2 is not None:
547 if node2 is not None:
548 node2 = node.bin(node2)
548 node2 = node.bin(node2)
549 logcmdutil.diffordiffstat(ui, self._repo, diffopts,
549 logcmdutil.diffordiffstat(ui, self._repo, diffopts,
550 node1, node2, match,
550 node1, node2, match,
551 prefix=posixpath.join(prefix, self._path),
551 prefix=posixpath.join(prefix, self._path),
552 listsubrepos=True, **opts)
552 listsubrepos=True, **opts)
553 except error.RepoLookupError as inst:
553 except error.RepoLookupError as inst:
554 self.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
554 self.ui.warn(_('warning: error "%s" in subrepository "%s"\n')
555 % (inst, subrelpath(self)))
555 % (inst, subrelpath(self)))
556
556
557 @annotatesubrepoerror
557 @annotatesubrepoerror
558 def archive(self, archiver, prefix, match=None, decode=True):
558 def archive(self, archiver, prefix, match=None, decode=True):
559 self._get(self._state + ('hg',))
559 self._get(self._state + ('hg',))
560 files = self.files()
560 files = self.files()
561 if match:
561 if match:
562 files = [f for f in files if match(f)]
562 files = [f for f in files if match(f)]
563 rev = self._state[1]
563 rev = self._state[1]
564 ctx = self._repo[rev]
564 ctx = self._repo[rev]
565 scmutil.prefetchfiles(self._repo, [ctx.rev()],
565 scmutil.prefetchfiles(self._repo, [ctx.rev()],
566 scmutil.matchfiles(self._repo, files))
566 scmutil.matchfiles(self._repo, files))
567 total = abstractsubrepo.archive(self, archiver, prefix, match)
567 total = abstractsubrepo.archive(self, archiver, prefix, match)
568 for subpath in ctx.substate:
568 for subpath in ctx.substate:
569 s = subrepo(ctx, subpath, True)
569 s = subrepo(ctx, subpath, True)
570 submatch = matchmod.subdirmatcher(subpath, match)
570 submatch = matchmod.subdirmatcher(subpath, match)
571 total += s.archive(archiver, prefix + self._path + '/', submatch,
571 total += s.archive(archiver, prefix + self._path + '/', submatch,
572 decode)
572 decode)
573 return total
573 return total
574
574
575 @annotatesubrepoerror
575 @annotatesubrepoerror
576 def dirty(self, ignoreupdate=False, missing=False):
576 def dirty(self, ignoreupdate=False, missing=False):
577 r = self._state[1]
577 r = self._state[1]
578 if r == '' and not ignoreupdate: # no state recorded
578 if r == '' and not ignoreupdate: # no state recorded
579 return True
579 return True
580 w = self._repo[None]
580 w = self._repo[None]
581 if r != w.p1().hex() and not ignoreupdate:
581 if r != w.p1().hex() and not ignoreupdate:
582 # different version checked out
582 # different version checked out
583 return True
583 return True
584 return w.dirty(missing=missing) # working directory changed
584 return w.dirty(missing=missing) # working directory changed
585
585
586 def basestate(self):
586 def basestate(self):
587 return self._repo['.'].hex()
587 return self._repo['.'].hex()
588
588
589 def checknested(self, path):
589 def checknested(self, path):
590 return self._repo._checknested(self._repo.wjoin(path))
590 return self._repo._checknested(self._repo.wjoin(path))
591
591
592 @annotatesubrepoerror
592 @annotatesubrepoerror
593 def commit(self, text, user, date):
593 def commit(self, text, user, date):
594 # don't bother committing in the subrepo if it's only been
594 # don't bother committing in the subrepo if it's only been
595 # updated
595 # updated
596 if not self.dirty(True):
596 if not self.dirty(True):
597 return self._repo['.'].hex()
597 return self._repo['.'].hex()
598 self.ui.debug("committing subrepo %s\n" % subrelpath(self))
598 self.ui.debug("committing subrepo %s\n" % subrelpath(self))
599 n = self._repo.commit(text, user, date)
599 n = self._repo.commit(text, user, date)
600 if not n:
600 if not n:
601 return self._repo['.'].hex() # different version checked out
601 return self._repo['.'].hex() # different version checked out
602 return node.hex(n)
602 return node.hex(n)
603
603
604 @annotatesubrepoerror
604 @annotatesubrepoerror
605 def phase(self, state):
605 def phase(self, state):
606 return self._repo[state or '.'].phase()
606 return self._repo[state or '.'].phase()
607
607
608 @annotatesubrepoerror
608 @annotatesubrepoerror
609 def remove(self):
609 def remove(self):
610 # we can't fully delete the repository as it may contain
610 # we can't fully delete the repository as it may contain
611 # local-only history
611 # local-only history
612 self.ui.note(_('removing subrepo %s\n') % subrelpath(self))
612 self.ui.note(_('removing subrepo %s\n') % subrelpath(self))
613 hg.clean(self._repo, node.nullid, False)
613 hg.clean(self._repo, node.nullid, False)
614
614
615 def _get(self, state):
615 def _get(self, state):
616 source, revision, kind = state
616 source, revision, kind = state
617 parentrepo = self._repo._subparent
617 parentrepo = self._repo._subparent
618
618
619 if revision in self._repo.unfiltered():
619 if revision in self._repo.unfiltered():
620 # Allow shared subrepos tracked at null to setup the sharedpath
620 # Allow shared subrepos tracked at null to setup the sharedpath
621 if len(self._repo) != 0 or not parentrepo.shared():
621 if len(self._repo) != 0 or not parentrepo.shared():
622 return True
622 return True
623 self._repo._subsource = source
623 self._repo._subsource = source
624 srcurl = _abssource(self._repo)
624 srcurl = _abssource(self._repo)
625 other = hg.peer(self._repo, {}, srcurl)
625 other = hg.peer(self._repo, {}, srcurl)
626 if len(self._repo) == 0:
626 if len(self._repo) == 0:
627 # use self._repo.vfs instead of self.wvfs to remove .hg only
627 # use self._repo.vfs instead of self.wvfs to remove .hg only
628 self._repo.vfs.rmtree()
628 self._repo.vfs.rmtree()
629
629
630 # A remote subrepo could be shared if there is a local copy
630 # A remote subrepo could be shared if there is a local copy
631 # relative to the parent's share source. But clone pooling doesn't
631 # relative to the parent's share source. But clone pooling doesn't
632 # assemble the repos in a tree, so that can't be consistently done.
632 # assemble the repos in a tree, so that can't be consistently done.
633 # A simpler option is for the user to configure clone pooling, and
633 # A simpler option is for the user to configure clone pooling, and
634 # work with that.
634 # work with that.
635 if parentrepo.shared() and hg.islocal(srcurl):
635 if parentrepo.shared() and hg.islocal(srcurl):
636 self.ui.status(_('sharing subrepo %s from %s\n')
636 self.ui.status(_('sharing subrepo %s from %s\n')
637 % (subrelpath(self), srcurl))
637 % (subrelpath(self), srcurl))
638 shared = hg.share(self._repo._subparent.baseui,
638 shared = hg.share(self._repo._subparent.baseui,
639 other, self._repo.root,
639 other, self._repo.root,
640 update=False, bookmarks=False)
640 update=False, bookmarks=False)
641 self._repo = shared.local()
641 self._repo = shared.local()
642 else:
642 else:
643 # TODO: find a common place for this and this code in the
643 # TODO: find a common place for this and this code in the
644 # share.py wrap of the clone command.
644 # share.py wrap of the clone command.
645 if parentrepo.shared():
645 if parentrepo.shared():
646 pool = self.ui.config('share', 'pool')
646 pool = self.ui.config('share', 'pool')
647 if pool:
647 if pool:
648 pool = util.expandpath(pool)
648 pool = util.expandpath(pool)
649
649
650 shareopts = {
650 shareopts = {
651 'pool': pool,
651 'pool': pool,
652 'mode': self.ui.config('share', 'poolnaming'),
652 'mode': self.ui.config('share', 'poolnaming'),
653 }
653 }
654 else:
654 else:
655 shareopts = {}
655 shareopts = {}
656
656
657 self.ui.status(_('cloning subrepo %s from %s\n')
657 self.ui.status(_('cloning subrepo %s from %s\n')
658 % (subrelpath(self), srcurl))
658 % (subrelpath(self), srcurl))
659 other, cloned = hg.clone(self._repo._subparent.baseui, {},
659 other, cloned = hg.clone(self._repo._subparent.baseui, {},
660 other, self._repo.root,
660 other, self._repo.root,
661 update=False, shareopts=shareopts)
661 update=False, shareopts=shareopts)
662 self._repo = cloned.local()
662 self._repo = cloned.local()
663 self._initrepo(parentrepo, source, create=True)
663 self._initrepo(parentrepo, source, create=True)
664 self._cachestorehash(srcurl)
664 self._cachestorehash(srcurl)
665 else:
665 else:
666 self.ui.status(_('pulling subrepo %s from %s\n')
666 self.ui.status(_('pulling subrepo %s from %s\n')
667 % (subrelpath(self), srcurl))
667 % (subrelpath(self), srcurl))
668 cleansub = self.storeclean(srcurl)
668 cleansub = self.storeclean(srcurl)
669 exchange.pull(self._repo, other)
669 exchange.pull(self._repo, other)
670 if cleansub:
670 if cleansub:
671 # keep the repo clean after pull
671 # keep the repo clean after pull
672 self._cachestorehash(srcurl)
672 self._cachestorehash(srcurl)
673 return False
673 return False
674
674
675 @annotatesubrepoerror
675 @annotatesubrepoerror
676 def get(self, state, overwrite=False):
676 def get(self, state, overwrite=False):
677 inrepo = self._get(state)
677 inrepo = self._get(state)
678 source, revision, kind = state
678 source, revision, kind = state
679 repo = self._repo
679 repo = self._repo
680 repo.ui.debug("getting subrepo %s\n" % self._path)
680 repo.ui.debug("getting subrepo %s\n" % self._path)
681 if inrepo:
681 if inrepo:
682 urepo = repo.unfiltered()
682 urepo = repo.unfiltered()
683 ctx = urepo[revision]
683 ctx = urepo[revision]
684 if ctx.hidden():
684 if ctx.hidden():
685 urepo.ui.warn(
685 urepo.ui.warn(
686 _('revision %s in subrepository "%s" is hidden\n') \
686 _('revision %s in subrepository "%s" is hidden\n') \
687 % (revision[0:12], self._path))
687 % (revision[0:12], self._path))
688 repo = urepo
688 repo = urepo
689 hg.updaterepo(repo, revision, overwrite)
689 hg.updaterepo(repo, revision, overwrite)
690
690
691 @annotatesubrepoerror
691 @annotatesubrepoerror
692 def merge(self, state):
692 def merge(self, state):
693 self._get(state)
693 self._get(state)
694 cur = self._repo['.']
694 cur = self._repo['.']
695 dst = self._repo[state[1]]
695 dst = self._repo[state[1]]
696 anc = dst.ancestor(cur)
696 anc = dst.ancestor(cur)
697
697
698 def mergefunc():
698 def mergefunc():
699 if anc == cur and dst.branch() == cur.branch():
699 if anc == cur and dst.branch() == cur.branch():
700 self.ui.debug('updating subrepository "%s"\n'
700 self.ui.debug('updating subrepository "%s"\n'
701 % subrelpath(self))
701 % subrelpath(self))
702 hg.update(self._repo, state[1])
702 hg.update(self._repo, state[1])
703 elif anc == dst:
703 elif anc == dst:
704 self.ui.debug('skipping subrepository "%s"\n'
704 self.ui.debug('skipping subrepository "%s"\n'
705 % subrelpath(self))
705 % subrelpath(self))
706 else:
706 else:
707 self.ui.debug('merging subrepository "%s"\n' % subrelpath(self))
707 self.ui.debug('merging subrepository "%s"\n' % subrelpath(self))
708 hg.merge(self._repo, state[1], remind=False)
708 hg.merge(self._repo, state[1], remind=False)
709
709
710 wctx = self._repo[None]
710 wctx = self._repo[None]
711 if self.dirty():
711 if self.dirty():
712 if anc != dst:
712 if anc != dst:
713 if _updateprompt(self.ui, self, wctx.dirty(), cur, dst):
713 if _updateprompt(self.ui, self, wctx.dirty(), cur, dst):
714 mergefunc()
714 mergefunc()
715 else:
715 else:
716 mergefunc()
716 mergefunc()
717 else:
717 else:
718 mergefunc()
718 mergefunc()
719
719
720 @annotatesubrepoerror
720 @annotatesubrepoerror
721 def push(self, opts):
721 def push(self, opts):
722 force = opts.get('force')
722 force = opts.get('force')
723 newbranch = opts.get('new_branch')
723 newbranch = opts.get('new_branch')
724 ssh = opts.get('ssh')
724 ssh = opts.get('ssh')
725
725
726 # push subrepos depth-first for coherent ordering
726 # push subrepos depth-first for coherent ordering
727 c = self._repo['.']
727 c = self._repo['.']
728 subs = c.substate # only repos that are committed
728 subs = c.substate # only repos that are committed
729 for s in sorted(subs):
729 for s in sorted(subs):
730 if c.sub(s).push(opts) == 0:
730 if c.sub(s).push(opts) == 0:
731 return False
731 return False
732
732
733 dsturl = _abssource(self._repo, True)
733 dsturl = _abssource(self._repo, True)
734 if not force:
734 if not force:
735 if self.storeclean(dsturl):
735 if self.storeclean(dsturl):
736 self.ui.status(
736 self.ui.status(
737 _('no changes made to subrepo %s since last push to %s\n')
737 _('no changes made to subrepo %s since last push to %s\n')
738 % (subrelpath(self), dsturl))
738 % (subrelpath(self), dsturl))
739 return None
739 return None
740 self.ui.status(_('pushing subrepo %s to %s\n') %
740 self.ui.status(_('pushing subrepo %s to %s\n') %
741 (subrelpath(self), dsturl))
741 (subrelpath(self), dsturl))
742 other = hg.peer(self._repo, {'ssh': ssh}, dsturl)
742 other = hg.peer(self._repo, {'ssh': ssh}, dsturl)
743 res = exchange.push(self._repo, other, force, newbranch=newbranch)
743 res = exchange.push(self._repo, other, force, newbranch=newbranch)
744
744
745 # the repo is now clean
745 # the repo is now clean
746 self._cachestorehash(dsturl)
746 self._cachestorehash(dsturl)
747 return res.cgresult
747 return res.cgresult
748
748
749 @annotatesubrepoerror
749 @annotatesubrepoerror
750 def outgoing(self, ui, dest, opts):
750 def outgoing(self, ui, dest, opts):
751 if 'rev' in opts or 'branch' in opts:
751 if 'rev' in opts or 'branch' in opts:
752 opts = copy.copy(opts)
752 opts = copy.copy(opts)
753 opts.pop('rev', None)
753 opts.pop('rev', None)
754 opts.pop('branch', None)
754 opts.pop('branch', None)
755 return hg.outgoing(ui, self._repo, _abssource(self._repo, True), opts)
755 return hg.outgoing(ui, self._repo, _abssource(self._repo, True), opts)
756
756
757 @annotatesubrepoerror
757 @annotatesubrepoerror
758 def incoming(self, ui, source, opts):
758 def incoming(self, ui, source, opts):
759 if 'rev' in opts or 'branch' in opts:
759 if 'rev' in opts or 'branch' in opts:
760 opts = copy.copy(opts)
760 opts = copy.copy(opts)
761 opts.pop('rev', None)
761 opts.pop('rev', None)
762 opts.pop('branch', None)
762 opts.pop('branch', None)
763 return hg.incoming(ui, self._repo, _abssource(self._repo, False), opts)
763 return hg.incoming(ui, self._repo, _abssource(self._repo, False), opts)
764
764
765 @annotatesubrepoerror
765 @annotatesubrepoerror
766 def files(self):
766 def files(self):
767 rev = self._state[1]
767 rev = self._state[1]
768 ctx = self._repo[rev]
768 ctx = self._repo[rev]
769 return ctx.manifest().keys()
769 return ctx.manifest().keys()
770
770
771 def filedata(self, name, decode):
771 def filedata(self, name, decode):
772 rev = self._state[1]
772 rev = self._state[1]
773 data = self._repo[rev][name].data()
773 data = self._repo[rev][name].data()
774 if decode:
774 if decode:
775 data = self._repo.wwritedata(name, data)
775 data = self._repo.wwritedata(name, data)
776 return data
776 return data
777
777
778 def fileflags(self, name):
778 def fileflags(self, name):
779 rev = self._state[1]
779 rev = self._state[1]
780 ctx = self._repo[rev]
780 ctx = self._repo[rev]
781 return ctx.flags(name)
781 return ctx.flags(name)
782
782
783 @annotatesubrepoerror
783 @annotatesubrepoerror
784 def printfiles(self, ui, m, fm, fmt, subrepos):
784 def printfiles(self, ui, m, fm, fmt, subrepos):
785 # If the parent context is a workingctx, use the workingctx here for
785 # If the parent context is a workingctx, use the workingctx here for
786 # consistency.
786 # consistency.
787 if self._ctx.rev() is None:
787 if self._ctx.rev() is None:
788 ctx = self._repo[None]
788 ctx = self._repo[None]
789 else:
789 else:
790 rev = self._state[1]
790 rev = self._state[1]
791 ctx = self._repo[rev]
791 ctx = self._repo[rev]
792 return cmdutil.files(ui, ctx, m, fm, fmt, subrepos)
792 return cmdutil.files(ui, ctx, m, fm, fmt, subrepos)
793
793
794 @annotatesubrepoerror
794 @annotatesubrepoerror
795 def getfileset(self, expr):
795 def matchfileset(self, expr, badfn=None):
796 repo = self._repo
796 if self._ctx.rev() is None:
797 if self._ctx.rev() is None:
797 ctx = self._repo[None]
798 ctx = repo[None]
798 else:
799 else:
799 rev = self._state[1]
800 rev = self._state[1]
800 ctx = self._repo[rev]
801 ctx = repo[rev]
801
802
802 files = ctx.getfileset(expr)
803 matchers = [ctx.matchfileset(expr, badfn=badfn)]
803
804
804 for subpath in ctx.substate:
805 for subpath in ctx.substate:
805 sub = ctx.sub(subpath)
806 sub = ctx.sub(subpath)
806
807
807 try:
808 try:
808 files.extend(subpath + '/' + f for f in sub.getfileset(expr))
809 sm = sub.matchfileset(expr, badfn=badfn)
810 pm = matchmod.prefixdirmatcher(repo.root, repo.getcwd(),
811 subpath, sm, badfn=badfn)
812 matchers.append(pm)
809 except error.LookupError:
813 except error.LookupError:
810 self.ui.status(_("skipping missing subrepository: %s\n")
814 self.ui.status(_("skipping missing subrepository: %s\n")
811 % self.wvfs.reljoin(reporelpath(self), subpath))
815 % self.wvfs.reljoin(reporelpath(self), subpath))
812 return files
816 if len(matchers) == 1:
817 return matchers[0]
818 return matchmod.unionmatcher(matchers)
813
819
814 def walk(self, match):
820 def walk(self, match):
815 ctx = self._repo[None]
821 ctx = self._repo[None]
816 return ctx.walk(match)
822 return ctx.walk(match)
817
823
818 @annotatesubrepoerror
824 @annotatesubrepoerror
819 def forget(self, match, prefix, dryrun, interactive):
825 def forget(self, match, prefix, dryrun, interactive):
820 return cmdutil.forget(self.ui, self._repo, match,
826 return cmdutil.forget(self.ui, self._repo, match,
821 self.wvfs.reljoin(prefix, self._path),
827 self.wvfs.reljoin(prefix, self._path),
822 True, dryrun=dryrun, interactive=interactive)
828 True, dryrun=dryrun, interactive=interactive)
823
829
824 @annotatesubrepoerror
830 @annotatesubrepoerror
825 def removefiles(self, matcher, prefix, after, force, subrepos,
831 def removefiles(self, matcher, prefix, after, force, subrepos,
826 dryrun, warnings):
832 dryrun, warnings):
827 return cmdutil.remove(self.ui, self._repo, matcher,
833 return cmdutil.remove(self.ui, self._repo, matcher,
828 self.wvfs.reljoin(prefix, self._path),
834 self.wvfs.reljoin(prefix, self._path),
829 after, force, subrepos, dryrun)
835 after, force, subrepos, dryrun)
830
836
831 @annotatesubrepoerror
837 @annotatesubrepoerror
832 def revert(self, substate, *pats, **opts):
838 def revert(self, substate, *pats, **opts):
833 # reverting a subrepo is a 2 step process:
839 # reverting a subrepo is a 2 step process:
834 # 1. if the no_backup is not set, revert all modified
840 # 1. if the no_backup is not set, revert all modified
835 # files inside the subrepo
841 # files inside the subrepo
836 # 2. update the subrepo to the revision specified in
842 # 2. update the subrepo to the revision specified in
837 # the corresponding substate dictionary
843 # the corresponding substate dictionary
838 self.ui.status(_('reverting subrepo %s\n') % substate[0])
844 self.ui.status(_('reverting subrepo %s\n') % substate[0])
839 if not opts.get(r'no_backup'):
845 if not opts.get(r'no_backup'):
840 # Revert all files on the subrepo, creating backups
846 # Revert all files on the subrepo, creating backups
841 # Note that this will not recursively revert subrepos
847 # Note that this will not recursively revert subrepos
842 # We could do it if there was a set:subrepos() predicate
848 # We could do it if there was a set:subrepos() predicate
843 opts = opts.copy()
849 opts = opts.copy()
844 opts[r'date'] = None
850 opts[r'date'] = None
845 opts[r'rev'] = substate[1]
851 opts[r'rev'] = substate[1]
846
852
847 self.filerevert(*pats, **opts)
853 self.filerevert(*pats, **opts)
848
854
849 # Update the repo to the revision specified in the given substate
855 # Update the repo to the revision specified in the given substate
850 if not opts.get(r'dry_run'):
856 if not opts.get(r'dry_run'):
851 self.get(substate, overwrite=True)
857 self.get(substate, overwrite=True)
852
858
853 def filerevert(self, *pats, **opts):
859 def filerevert(self, *pats, **opts):
854 ctx = self._repo[opts[r'rev']]
860 ctx = self._repo[opts[r'rev']]
855 parents = self._repo.dirstate.parents()
861 parents = self._repo.dirstate.parents()
856 if opts.get(r'all'):
862 if opts.get(r'all'):
857 pats = ['set:modified()']
863 pats = ['set:modified()']
858 else:
864 else:
859 pats = []
865 pats = []
860 cmdutil.revert(self.ui, self._repo, ctx, parents, *pats, **opts)
866 cmdutil.revert(self.ui, self._repo, ctx, parents, *pats, **opts)
861
867
862 def shortid(self, revid):
868 def shortid(self, revid):
863 return revid[:12]
869 return revid[:12]
864
870
865 @annotatesubrepoerror
871 @annotatesubrepoerror
866 def unshare(self):
872 def unshare(self):
867 # subrepo inherently violates our import layering rules
873 # subrepo inherently violates our import layering rules
868 # because it wants to make repo objects from deep inside the stack
874 # because it wants to make repo objects from deep inside the stack
869 # so we manually delay the circular imports to not break
875 # so we manually delay the circular imports to not break
870 # scripts that don't use our demand-loading
876 # scripts that don't use our demand-loading
871 global hg
877 global hg
872 from . import hg as h
878 from . import hg as h
873 hg = h
879 hg = h
874
880
875 # Nothing prevents a user from sharing in a repo, and then making that a
881 # Nothing prevents a user from sharing in a repo, and then making that a
876 # subrepo. Alternately, the previous unshare attempt may have failed
882 # subrepo. Alternately, the previous unshare attempt may have failed
877 # part way through. So recurse whether or not this layer is shared.
883 # part way through. So recurse whether or not this layer is shared.
878 if self._repo.shared():
884 if self._repo.shared():
879 self.ui.status(_("unsharing subrepo '%s'\n") % self._relpath)
885 self.ui.status(_("unsharing subrepo '%s'\n") % self._relpath)
880
886
881 hg.unshare(self.ui, self._repo)
887 hg.unshare(self.ui, self._repo)
882
888
883 def verify(self):
889 def verify(self):
884 try:
890 try:
885 rev = self._state[1]
891 rev = self._state[1]
886 ctx = self._repo.unfiltered()[rev]
892 ctx = self._repo.unfiltered()[rev]
887 if ctx.hidden():
893 if ctx.hidden():
888 # Since hidden revisions aren't pushed/pulled, it seems worth an
894 # Since hidden revisions aren't pushed/pulled, it seems worth an
889 # explicit warning.
895 # explicit warning.
890 ui = self._repo.ui
896 ui = self._repo.ui
891 ui.warn(_("subrepo '%s' is hidden in revision %s\n") %
897 ui.warn(_("subrepo '%s' is hidden in revision %s\n") %
892 (self._relpath, node.short(self._ctx.node())))
898 (self._relpath, node.short(self._ctx.node())))
893 return 0
899 return 0
894 except error.RepoLookupError:
900 except error.RepoLookupError:
895 # A missing subrepo revision may be a case of needing to pull it, so
901 # A missing subrepo revision may be a case of needing to pull it, so
896 # don't treat this as an error.
902 # don't treat this as an error.
897 self._repo.ui.warn(_("subrepo '%s' not found in revision %s\n") %
903 self._repo.ui.warn(_("subrepo '%s' not found in revision %s\n") %
898 (self._relpath, node.short(self._ctx.node())))
904 (self._relpath, node.short(self._ctx.node())))
899 return 0
905 return 0
900
906
901 @propertycache
907 @propertycache
902 def wvfs(self):
908 def wvfs(self):
903 """return own wvfs for efficiency and consistency
909 """return own wvfs for efficiency and consistency
904 """
910 """
905 return self._repo.wvfs
911 return self._repo.wvfs
906
912
907 @propertycache
913 @propertycache
908 def _relpath(self):
914 def _relpath(self):
909 """return path to this subrepository as seen from outermost repository
915 """return path to this subrepository as seen from outermost repository
910 """
916 """
911 # Keep consistent dir separators by avoiding vfs.join(self._path)
917 # Keep consistent dir separators by avoiding vfs.join(self._path)
912 return reporelpath(self._repo)
918 return reporelpath(self._repo)
913
919
914 class svnsubrepo(abstractsubrepo):
920 class svnsubrepo(abstractsubrepo):
915 def __init__(self, ctx, path, state, allowcreate):
921 def __init__(self, ctx, path, state, allowcreate):
916 super(svnsubrepo, self).__init__(ctx, path)
922 super(svnsubrepo, self).__init__(ctx, path)
917 self._state = state
923 self._state = state
918 self._exe = procutil.findexe('svn')
924 self._exe = procutil.findexe('svn')
919 if not self._exe:
925 if not self._exe:
920 raise error.Abort(_("'svn' executable not found for subrepo '%s'")
926 raise error.Abort(_("'svn' executable not found for subrepo '%s'")
921 % self._path)
927 % self._path)
922
928
923 def _svncommand(self, commands, filename='', failok=False):
929 def _svncommand(self, commands, filename='', failok=False):
924 cmd = [self._exe]
930 cmd = [self._exe]
925 extrakw = {}
931 extrakw = {}
926 if not self.ui.interactive():
932 if not self.ui.interactive():
927 # Making stdin be a pipe should prevent svn from behaving
933 # Making stdin be a pipe should prevent svn from behaving
928 # interactively even if we can't pass --non-interactive.
934 # interactively even if we can't pass --non-interactive.
929 extrakw[r'stdin'] = subprocess.PIPE
935 extrakw[r'stdin'] = subprocess.PIPE
930 # Starting in svn 1.5 --non-interactive is a global flag
936 # Starting in svn 1.5 --non-interactive is a global flag
931 # instead of being per-command, but we need to support 1.4 so
937 # instead of being per-command, but we need to support 1.4 so
932 # we have to be intelligent about what commands take
938 # we have to be intelligent about what commands take
933 # --non-interactive.
939 # --non-interactive.
934 if commands[0] in ('update', 'checkout', 'commit'):
940 if commands[0] in ('update', 'checkout', 'commit'):
935 cmd.append('--non-interactive')
941 cmd.append('--non-interactive')
936 cmd.extend(commands)
942 cmd.extend(commands)
937 if filename is not None:
943 if filename is not None:
938 path = self.wvfs.reljoin(self._ctx.repo().origroot,
944 path = self.wvfs.reljoin(self._ctx.repo().origroot,
939 self._path, filename)
945 self._path, filename)
940 cmd.append(path)
946 cmd.append(path)
941 env = dict(encoding.environ)
947 env = dict(encoding.environ)
942 # Avoid localized output, preserve current locale for everything else.
948 # Avoid localized output, preserve current locale for everything else.
943 lc_all = env.get('LC_ALL')
949 lc_all = env.get('LC_ALL')
944 if lc_all:
950 if lc_all:
945 env['LANG'] = lc_all
951 env['LANG'] = lc_all
946 del env['LC_ALL']
952 del env['LC_ALL']
947 env['LC_MESSAGES'] = 'C'
953 env['LC_MESSAGES'] = 'C'
948 p = subprocess.Popen(cmd, bufsize=-1, close_fds=procutil.closefds,
954 p = subprocess.Popen(cmd, bufsize=-1, close_fds=procutil.closefds,
949 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
955 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
950 universal_newlines=True, env=env, **extrakw)
956 universal_newlines=True, env=env, **extrakw)
951 stdout, stderr = p.communicate()
957 stdout, stderr = p.communicate()
952 stderr = stderr.strip()
958 stderr = stderr.strip()
953 if not failok:
959 if not failok:
954 if p.returncode:
960 if p.returncode:
955 raise error.Abort(stderr or 'exited with code %d'
961 raise error.Abort(stderr or 'exited with code %d'
956 % p.returncode)
962 % p.returncode)
957 if stderr:
963 if stderr:
958 self.ui.warn(stderr + '\n')
964 self.ui.warn(stderr + '\n')
959 return stdout, stderr
965 return stdout, stderr
960
966
961 @propertycache
967 @propertycache
962 def _svnversion(self):
968 def _svnversion(self):
963 output, err = self._svncommand(['--version', '--quiet'], filename=None)
969 output, err = self._svncommand(['--version', '--quiet'], filename=None)
964 m = re.search(br'^(\d+)\.(\d+)', output)
970 m = re.search(br'^(\d+)\.(\d+)', output)
965 if not m:
971 if not m:
966 raise error.Abort(_('cannot retrieve svn tool version'))
972 raise error.Abort(_('cannot retrieve svn tool version'))
967 return (int(m.group(1)), int(m.group(2)))
973 return (int(m.group(1)), int(m.group(2)))
968
974
969 def _svnmissing(self):
975 def _svnmissing(self):
970 return not self.wvfs.exists('.svn')
976 return not self.wvfs.exists('.svn')
971
977
972 def _wcrevs(self):
978 def _wcrevs(self):
973 # Get the working directory revision as well as the last
979 # Get the working directory revision as well as the last
974 # commit revision so we can compare the subrepo state with
980 # commit revision so we can compare the subrepo state with
975 # both. We used to store the working directory one.
981 # both. We used to store the working directory one.
976 output, err = self._svncommand(['info', '--xml'])
982 output, err = self._svncommand(['info', '--xml'])
977 doc = xml.dom.minidom.parseString(output)
983 doc = xml.dom.minidom.parseString(output)
978 entries = doc.getElementsByTagName('entry')
984 entries = doc.getElementsByTagName('entry')
979 lastrev, rev = '0', '0'
985 lastrev, rev = '0', '0'
980 if entries:
986 if entries:
981 rev = str(entries[0].getAttribute('revision')) or '0'
987 rev = str(entries[0].getAttribute('revision')) or '0'
982 commits = entries[0].getElementsByTagName('commit')
988 commits = entries[0].getElementsByTagName('commit')
983 if commits:
989 if commits:
984 lastrev = str(commits[0].getAttribute('revision')) or '0'
990 lastrev = str(commits[0].getAttribute('revision')) or '0'
985 return (lastrev, rev)
991 return (lastrev, rev)
986
992
987 def _wcrev(self):
993 def _wcrev(self):
988 return self._wcrevs()[0]
994 return self._wcrevs()[0]
989
995
990 def _wcchanged(self):
996 def _wcchanged(self):
991 """Return (changes, extchanges, missing) where changes is True
997 """Return (changes, extchanges, missing) where changes is True
992 if the working directory was changed, extchanges is
998 if the working directory was changed, extchanges is
993 True if any of these changes concern an external entry and missing
999 True if any of these changes concern an external entry and missing
994 is True if any change is a missing entry.
1000 is True if any change is a missing entry.
995 """
1001 """
996 output, err = self._svncommand(['status', '--xml'])
1002 output, err = self._svncommand(['status', '--xml'])
997 externals, changes, missing = [], [], []
1003 externals, changes, missing = [], [], []
998 doc = xml.dom.minidom.parseString(output)
1004 doc = xml.dom.minidom.parseString(output)
999 for e in doc.getElementsByTagName('entry'):
1005 for e in doc.getElementsByTagName('entry'):
1000 s = e.getElementsByTagName('wc-status')
1006 s = e.getElementsByTagName('wc-status')
1001 if not s:
1007 if not s:
1002 continue
1008 continue
1003 item = s[0].getAttribute('item')
1009 item = s[0].getAttribute('item')
1004 props = s[0].getAttribute('props')
1010 props = s[0].getAttribute('props')
1005 path = e.getAttribute('path')
1011 path = e.getAttribute('path')
1006 if item == 'external':
1012 if item == 'external':
1007 externals.append(path)
1013 externals.append(path)
1008 elif item == 'missing':
1014 elif item == 'missing':
1009 missing.append(path)
1015 missing.append(path)
1010 if (item not in ('', 'normal', 'unversioned', 'external')
1016 if (item not in ('', 'normal', 'unversioned', 'external')
1011 or props not in ('', 'none', 'normal')):
1017 or props not in ('', 'none', 'normal')):
1012 changes.append(path)
1018 changes.append(path)
1013 for path in changes:
1019 for path in changes:
1014 for ext in externals:
1020 for ext in externals:
1015 if path == ext or path.startswith(ext + pycompat.ossep):
1021 if path == ext or path.startswith(ext + pycompat.ossep):
1016 return True, True, bool(missing)
1022 return True, True, bool(missing)
1017 return bool(changes), False, bool(missing)
1023 return bool(changes), False, bool(missing)
1018
1024
1019 @annotatesubrepoerror
1025 @annotatesubrepoerror
1020 def dirty(self, ignoreupdate=False, missing=False):
1026 def dirty(self, ignoreupdate=False, missing=False):
1021 if self._svnmissing():
1027 if self._svnmissing():
1022 return self._state[1] != ''
1028 return self._state[1] != ''
1023 wcchanged = self._wcchanged()
1029 wcchanged = self._wcchanged()
1024 changed = wcchanged[0] or (missing and wcchanged[2])
1030 changed = wcchanged[0] or (missing and wcchanged[2])
1025 if not changed:
1031 if not changed:
1026 if self._state[1] in self._wcrevs() or ignoreupdate:
1032 if self._state[1] in self._wcrevs() or ignoreupdate:
1027 return False
1033 return False
1028 return True
1034 return True
1029
1035
1030 def basestate(self):
1036 def basestate(self):
1031 lastrev, rev = self._wcrevs()
1037 lastrev, rev = self._wcrevs()
1032 if lastrev != rev:
1038 if lastrev != rev:
1033 # Last committed rev is not the same than rev. We would
1039 # Last committed rev is not the same than rev. We would
1034 # like to take lastrev but we do not know if the subrepo
1040 # like to take lastrev but we do not know if the subrepo
1035 # URL exists at lastrev. Test it and fallback to rev it
1041 # URL exists at lastrev. Test it and fallback to rev it
1036 # is not there.
1042 # is not there.
1037 try:
1043 try:
1038 self._svncommand(['list', '%s@%s' % (self._state[0], lastrev)])
1044 self._svncommand(['list', '%s@%s' % (self._state[0], lastrev)])
1039 return lastrev
1045 return lastrev
1040 except error.Abort:
1046 except error.Abort:
1041 pass
1047 pass
1042 return rev
1048 return rev
1043
1049
1044 @annotatesubrepoerror
1050 @annotatesubrepoerror
1045 def commit(self, text, user, date):
1051 def commit(self, text, user, date):
1046 # user and date are out of our hands since svn is centralized
1052 # user and date are out of our hands since svn is centralized
1047 changed, extchanged, missing = self._wcchanged()
1053 changed, extchanged, missing = self._wcchanged()
1048 if not changed:
1054 if not changed:
1049 return self.basestate()
1055 return self.basestate()
1050 if extchanged:
1056 if extchanged:
1051 # Do not try to commit externals
1057 # Do not try to commit externals
1052 raise error.Abort(_('cannot commit svn externals'))
1058 raise error.Abort(_('cannot commit svn externals'))
1053 if missing:
1059 if missing:
1054 # svn can commit with missing entries but aborting like hg
1060 # svn can commit with missing entries but aborting like hg
1055 # seems a better approach.
1061 # seems a better approach.
1056 raise error.Abort(_('cannot commit missing svn entries'))
1062 raise error.Abort(_('cannot commit missing svn entries'))
1057 commitinfo, err = self._svncommand(['commit', '-m', text])
1063 commitinfo, err = self._svncommand(['commit', '-m', text])
1058 self.ui.status(commitinfo)
1064 self.ui.status(commitinfo)
1059 newrev = re.search('Committed revision ([0-9]+).', commitinfo)
1065 newrev = re.search('Committed revision ([0-9]+).', commitinfo)
1060 if not newrev:
1066 if not newrev:
1061 if not commitinfo.strip():
1067 if not commitinfo.strip():
1062 # Sometimes, our definition of "changed" differs from
1068 # Sometimes, our definition of "changed" differs from
1063 # svn one. For instance, svn ignores missing files
1069 # svn one. For instance, svn ignores missing files
1064 # when committing. If there are only missing files, no
1070 # when committing. If there are only missing files, no
1065 # commit is made, no output and no error code.
1071 # commit is made, no output and no error code.
1066 raise error.Abort(_('failed to commit svn changes'))
1072 raise error.Abort(_('failed to commit svn changes'))
1067 raise error.Abort(commitinfo.splitlines()[-1])
1073 raise error.Abort(commitinfo.splitlines()[-1])
1068 newrev = newrev.groups()[0]
1074 newrev = newrev.groups()[0]
1069 self.ui.status(self._svncommand(['update', '-r', newrev])[0])
1075 self.ui.status(self._svncommand(['update', '-r', newrev])[0])
1070 return newrev
1076 return newrev
1071
1077
1072 @annotatesubrepoerror
1078 @annotatesubrepoerror
1073 def remove(self):
1079 def remove(self):
1074 if self.dirty():
1080 if self.dirty():
1075 self.ui.warn(_('not removing repo %s because '
1081 self.ui.warn(_('not removing repo %s because '
1076 'it has changes.\n') % self._path)
1082 'it has changes.\n') % self._path)
1077 return
1083 return
1078 self.ui.note(_('removing subrepo %s\n') % self._path)
1084 self.ui.note(_('removing subrepo %s\n') % self._path)
1079
1085
1080 self.wvfs.rmtree(forcibly=True)
1086 self.wvfs.rmtree(forcibly=True)
1081 try:
1087 try:
1082 pwvfs = self._ctx.repo().wvfs
1088 pwvfs = self._ctx.repo().wvfs
1083 pwvfs.removedirs(pwvfs.dirname(self._path))
1089 pwvfs.removedirs(pwvfs.dirname(self._path))
1084 except OSError:
1090 except OSError:
1085 pass
1091 pass
1086
1092
1087 @annotatesubrepoerror
1093 @annotatesubrepoerror
1088 def get(self, state, overwrite=False):
1094 def get(self, state, overwrite=False):
1089 if overwrite:
1095 if overwrite:
1090 self._svncommand(['revert', '--recursive'])
1096 self._svncommand(['revert', '--recursive'])
1091 args = ['checkout']
1097 args = ['checkout']
1092 if self._svnversion >= (1, 5):
1098 if self._svnversion >= (1, 5):
1093 args.append('--force')
1099 args.append('--force')
1094 # The revision must be specified at the end of the URL to properly
1100 # The revision must be specified at the end of the URL to properly
1095 # update to a directory which has since been deleted and recreated.
1101 # update to a directory which has since been deleted and recreated.
1096 args.append('%s@%s' % (state[0], state[1]))
1102 args.append('%s@%s' % (state[0], state[1]))
1097
1103
1098 # SEC: check that the ssh url is safe
1104 # SEC: check that the ssh url is safe
1099 util.checksafessh(state[0])
1105 util.checksafessh(state[0])
1100
1106
1101 status, err = self._svncommand(args, failok=True)
1107 status, err = self._svncommand(args, failok=True)
1102 _sanitize(self.ui, self.wvfs, '.svn')
1108 _sanitize(self.ui, self.wvfs, '.svn')
1103 if not re.search('Checked out revision [0-9]+.', status):
1109 if not re.search('Checked out revision [0-9]+.', status):
1104 if ('is already a working copy for a different URL' in err
1110 if ('is already a working copy for a different URL' in err
1105 and (self._wcchanged()[:2] == (False, False))):
1111 and (self._wcchanged()[:2] == (False, False))):
1106 # obstructed but clean working copy, so just blow it away.
1112 # obstructed but clean working copy, so just blow it away.
1107 self.remove()
1113 self.remove()
1108 self.get(state, overwrite=False)
1114 self.get(state, overwrite=False)
1109 return
1115 return
1110 raise error.Abort((status or err).splitlines()[-1])
1116 raise error.Abort((status or err).splitlines()[-1])
1111 self.ui.status(status)
1117 self.ui.status(status)
1112
1118
1113 @annotatesubrepoerror
1119 @annotatesubrepoerror
1114 def merge(self, state):
1120 def merge(self, state):
1115 old = self._state[1]
1121 old = self._state[1]
1116 new = state[1]
1122 new = state[1]
1117 wcrev = self._wcrev()
1123 wcrev = self._wcrev()
1118 if new != wcrev:
1124 if new != wcrev:
1119 dirty = old == wcrev or self._wcchanged()[0]
1125 dirty = old == wcrev or self._wcchanged()[0]
1120 if _updateprompt(self.ui, self, dirty, wcrev, new):
1126 if _updateprompt(self.ui, self, dirty, wcrev, new):
1121 self.get(state, False)
1127 self.get(state, False)
1122
1128
1123 def push(self, opts):
1129 def push(self, opts):
1124 # push is a no-op for SVN
1130 # push is a no-op for SVN
1125 return True
1131 return True
1126
1132
1127 @annotatesubrepoerror
1133 @annotatesubrepoerror
1128 def files(self):
1134 def files(self):
1129 output = self._svncommand(['list', '--recursive', '--xml'])[0]
1135 output = self._svncommand(['list', '--recursive', '--xml'])[0]
1130 doc = xml.dom.minidom.parseString(output)
1136 doc = xml.dom.minidom.parseString(output)
1131 paths = []
1137 paths = []
1132 for e in doc.getElementsByTagName('entry'):
1138 for e in doc.getElementsByTagName('entry'):
1133 kind = pycompat.bytestr(e.getAttribute('kind'))
1139 kind = pycompat.bytestr(e.getAttribute('kind'))
1134 if kind != 'file':
1140 if kind != 'file':
1135 continue
1141 continue
1136 name = ''.join(c.data for c
1142 name = ''.join(c.data for c
1137 in e.getElementsByTagName('name')[0].childNodes
1143 in e.getElementsByTagName('name')[0].childNodes
1138 if c.nodeType == c.TEXT_NODE)
1144 if c.nodeType == c.TEXT_NODE)
1139 paths.append(name.encode('utf-8'))
1145 paths.append(name.encode('utf-8'))
1140 return paths
1146 return paths
1141
1147
1142 def filedata(self, name, decode):
1148 def filedata(self, name, decode):
1143 return self._svncommand(['cat'], name)[0]
1149 return self._svncommand(['cat'], name)[0]
1144
1150
1145
1151
1146 class gitsubrepo(abstractsubrepo):
1152 class gitsubrepo(abstractsubrepo):
1147 def __init__(self, ctx, path, state, allowcreate):
1153 def __init__(self, ctx, path, state, allowcreate):
1148 super(gitsubrepo, self).__init__(ctx, path)
1154 super(gitsubrepo, self).__init__(ctx, path)
1149 self._state = state
1155 self._state = state
1150 self._abspath = ctx.repo().wjoin(path)
1156 self._abspath = ctx.repo().wjoin(path)
1151 self._subparent = ctx.repo()
1157 self._subparent = ctx.repo()
1152 self._ensuregit()
1158 self._ensuregit()
1153
1159
1154 def _ensuregit(self):
1160 def _ensuregit(self):
1155 try:
1161 try:
1156 self._gitexecutable = 'git'
1162 self._gitexecutable = 'git'
1157 out, err = self._gitnodir(['--version'])
1163 out, err = self._gitnodir(['--version'])
1158 except OSError as e:
1164 except OSError as e:
1159 genericerror = _("error executing git for subrepo '%s': %s")
1165 genericerror = _("error executing git for subrepo '%s': %s")
1160 notfoundhint = _("check git is installed and in your PATH")
1166 notfoundhint = _("check git is installed and in your PATH")
1161 if e.errno != errno.ENOENT:
1167 if e.errno != errno.ENOENT:
1162 raise error.Abort(genericerror % (
1168 raise error.Abort(genericerror % (
1163 self._path, encoding.strtolocal(e.strerror)))
1169 self._path, encoding.strtolocal(e.strerror)))
1164 elif pycompat.iswindows:
1170 elif pycompat.iswindows:
1165 try:
1171 try:
1166 self._gitexecutable = 'git.cmd'
1172 self._gitexecutable = 'git.cmd'
1167 out, err = self._gitnodir(['--version'])
1173 out, err = self._gitnodir(['--version'])
1168 except OSError as e2:
1174 except OSError as e2:
1169 if e2.errno == errno.ENOENT:
1175 if e2.errno == errno.ENOENT:
1170 raise error.Abort(_("couldn't find 'git' or 'git.cmd'"
1176 raise error.Abort(_("couldn't find 'git' or 'git.cmd'"
1171 " for subrepo '%s'") % self._path,
1177 " for subrepo '%s'") % self._path,
1172 hint=notfoundhint)
1178 hint=notfoundhint)
1173 else:
1179 else:
1174 raise error.Abort(genericerror % (self._path,
1180 raise error.Abort(genericerror % (self._path,
1175 encoding.strtolocal(e2.strerror)))
1181 encoding.strtolocal(e2.strerror)))
1176 else:
1182 else:
1177 raise error.Abort(_("couldn't find git for subrepo '%s'")
1183 raise error.Abort(_("couldn't find git for subrepo '%s'")
1178 % self._path, hint=notfoundhint)
1184 % self._path, hint=notfoundhint)
1179 versionstatus = self._checkversion(out)
1185 versionstatus = self._checkversion(out)
1180 if versionstatus == 'unknown':
1186 if versionstatus == 'unknown':
1181 self.ui.warn(_('cannot retrieve git version\n'))
1187 self.ui.warn(_('cannot retrieve git version\n'))
1182 elif versionstatus == 'abort':
1188 elif versionstatus == 'abort':
1183 raise error.Abort(_('git subrepo requires at least 1.6.0 or later'))
1189 raise error.Abort(_('git subrepo requires at least 1.6.0 or later'))
1184 elif versionstatus == 'warning':
1190 elif versionstatus == 'warning':
1185 self.ui.warn(_('git subrepo requires at least 1.6.0 or later\n'))
1191 self.ui.warn(_('git subrepo requires at least 1.6.0 or later\n'))
1186
1192
1187 @staticmethod
1193 @staticmethod
1188 def _gitversion(out):
1194 def _gitversion(out):
1189 m = re.search(br'^git version (\d+)\.(\d+)\.(\d+)', out)
1195 m = re.search(br'^git version (\d+)\.(\d+)\.(\d+)', out)
1190 if m:
1196 if m:
1191 return (int(m.group(1)), int(m.group(2)), int(m.group(3)))
1197 return (int(m.group(1)), int(m.group(2)), int(m.group(3)))
1192
1198
1193 m = re.search(br'^git version (\d+)\.(\d+)', out)
1199 m = re.search(br'^git version (\d+)\.(\d+)', out)
1194 if m:
1200 if m:
1195 return (int(m.group(1)), int(m.group(2)), 0)
1201 return (int(m.group(1)), int(m.group(2)), 0)
1196
1202
1197 return -1
1203 return -1
1198
1204
1199 @staticmethod
1205 @staticmethod
1200 def _checkversion(out):
1206 def _checkversion(out):
1201 '''ensure git version is new enough
1207 '''ensure git version is new enough
1202
1208
1203 >>> _checkversion = gitsubrepo._checkversion
1209 >>> _checkversion = gitsubrepo._checkversion
1204 >>> _checkversion(b'git version 1.6.0')
1210 >>> _checkversion(b'git version 1.6.0')
1205 'ok'
1211 'ok'
1206 >>> _checkversion(b'git version 1.8.5')
1212 >>> _checkversion(b'git version 1.8.5')
1207 'ok'
1213 'ok'
1208 >>> _checkversion(b'git version 1.4.0')
1214 >>> _checkversion(b'git version 1.4.0')
1209 'abort'
1215 'abort'
1210 >>> _checkversion(b'git version 1.5.0')
1216 >>> _checkversion(b'git version 1.5.0')
1211 'warning'
1217 'warning'
1212 >>> _checkversion(b'git version 1.9-rc0')
1218 >>> _checkversion(b'git version 1.9-rc0')
1213 'ok'
1219 'ok'
1214 >>> _checkversion(b'git version 1.9.0.265.g81cdec2')
1220 >>> _checkversion(b'git version 1.9.0.265.g81cdec2')
1215 'ok'
1221 'ok'
1216 >>> _checkversion(b'git version 1.9.0.GIT')
1222 >>> _checkversion(b'git version 1.9.0.GIT')
1217 'ok'
1223 'ok'
1218 >>> _checkversion(b'git version 12345')
1224 >>> _checkversion(b'git version 12345')
1219 'unknown'
1225 'unknown'
1220 >>> _checkversion(b'no')
1226 >>> _checkversion(b'no')
1221 'unknown'
1227 'unknown'
1222 '''
1228 '''
1223 version = gitsubrepo._gitversion(out)
1229 version = gitsubrepo._gitversion(out)
1224 # git 1.4.0 can't work at all, but 1.5.X can in at least some cases,
1230 # git 1.4.0 can't work at all, but 1.5.X can in at least some cases,
1225 # despite the docstring comment. For now, error on 1.4.0, warn on
1231 # despite the docstring comment. For now, error on 1.4.0, warn on
1226 # 1.5.0 but attempt to continue.
1232 # 1.5.0 but attempt to continue.
1227 if version == -1:
1233 if version == -1:
1228 return 'unknown'
1234 return 'unknown'
1229 if version < (1, 5, 0):
1235 if version < (1, 5, 0):
1230 return 'abort'
1236 return 'abort'
1231 elif version < (1, 6, 0):
1237 elif version < (1, 6, 0):
1232 return 'warning'
1238 return 'warning'
1233 return 'ok'
1239 return 'ok'
1234
1240
1235 def _gitcommand(self, commands, env=None, stream=False):
1241 def _gitcommand(self, commands, env=None, stream=False):
1236 return self._gitdir(commands, env=env, stream=stream)[0]
1242 return self._gitdir(commands, env=env, stream=stream)[0]
1237
1243
1238 def _gitdir(self, commands, env=None, stream=False):
1244 def _gitdir(self, commands, env=None, stream=False):
1239 return self._gitnodir(commands, env=env, stream=stream,
1245 return self._gitnodir(commands, env=env, stream=stream,
1240 cwd=self._abspath)
1246 cwd=self._abspath)
1241
1247
1242 def _gitnodir(self, commands, env=None, stream=False, cwd=None):
1248 def _gitnodir(self, commands, env=None, stream=False, cwd=None):
1243 """Calls the git command
1249 """Calls the git command
1244
1250
1245 The methods tries to call the git command. versions prior to 1.6.0
1251 The methods tries to call the git command. versions prior to 1.6.0
1246 are not supported and very probably fail.
1252 are not supported and very probably fail.
1247 """
1253 """
1248 self.ui.debug('%s: git %s\n' % (self._relpath, ' '.join(commands)))
1254 self.ui.debug('%s: git %s\n' % (self._relpath, ' '.join(commands)))
1249 if env is None:
1255 if env is None:
1250 env = encoding.environ.copy()
1256 env = encoding.environ.copy()
1251 # disable localization for Git output (issue5176)
1257 # disable localization for Git output (issue5176)
1252 env['LC_ALL'] = 'C'
1258 env['LC_ALL'] = 'C'
1253 # fix for Git CVE-2015-7545
1259 # fix for Git CVE-2015-7545
1254 if 'GIT_ALLOW_PROTOCOL' not in env:
1260 if 'GIT_ALLOW_PROTOCOL' not in env:
1255 env['GIT_ALLOW_PROTOCOL'] = 'file:git:http:https:ssh'
1261 env['GIT_ALLOW_PROTOCOL'] = 'file:git:http:https:ssh'
1256 # unless ui.quiet is set, print git's stderr,
1262 # unless ui.quiet is set, print git's stderr,
1257 # which is mostly progress and useful info
1263 # which is mostly progress and useful info
1258 errpipe = None
1264 errpipe = None
1259 if self.ui.quiet:
1265 if self.ui.quiet:
1260 errpipe = open(os.devnull, 'w')
1266 errpipe = open(os.devnull, 'w')
1261 if self.ui._colormode and len(commands) and commands[0] == "diff":
1267 if self.ui._colormode and len(commands) and commands[0] == "diff":
1262 # insert the argument in the front,
1268 # insert the argument in the front,
1263 # the end of git diff arguments is used for paths
1269 # the end of git diff arguments is used for paths
1264 commands.insert(1, '--color')
1270 commands.insert(1, '--color')
1265 p = subprocess.Popen([self._gitexecutable] + commands, bufsize=-1,
1271 p = subprocess.Popen([self._gitexecutable] + commands, bufsize=-1,
1266 cwd=cwd, env=env, close_fds=procutil.closefds,
1272 cwd=cwd, env=env, close_fds=procutil.closefds,
1267 stdout=subprocess.PIPE, stderr=errpipe)
1273 stdout=subprocess.PIPE, stderr=errpipe)
1268 if stream:
1274 if stream:
1269 return p.stdout, None
1275 return p.stdout, None
1270
1276
1271 retdata = p.stdout.read().strip()
1277 retdata = p.stdout.read().strip()
1272 # wait for the child to exit to avoid race condition.
1278 # wait for the child to exit to avoid race condition.
1273 p.wait()
1279 p.wait()
1274
1280
1275 if p.returncode != 0 and p.returncode != 1:
1281 if p.returncode != 0 and p.returncode != 1:
1276 # there are certain error codes that are ok
1282 # there are certain error codes that are ok
1277 command = commands[0]
1283 command = commands[0]
1278 if command in ('cat-file', 'symbolic-ref'):
1284 if command in ('cat-file', 'symbolic-ref'):
1279 return retdata, p.returncode
1285 return retdata, p.returncode
1280 # for all others, abort
1286 # for all others, abort
1281 raise error.Abort(_('git %s error %d in %s') %
1287 raise error.Abort(_('git %s error %d in %s') %
1282 (command, p.returncode, self._relpath))
1288 (command, p.returncode, self._relpath))
1283
1289
1284 return retdata, p.returncode
1290 return retdata, p.returncode
1285
1291
1286 def _gitmissing(self):
1292 def _gitmissing(self):
1287 return not self.wvfs.exists('.git')
1293 return not self.wvfs.exists('.git')
1288
1294
1289 def _gitstate(self):
1295 def _gitstate(self):
1290 return self._gitcommand(['rev-parse', 'HEAD'])
1296 return self._gitcommand(['rev-parse', 'HEAD'])
1291
1297
1292 def _gitcurrentbranch(self):
1298 def _gitcurrentbranch(self):
1293 current, err = self._gitdir(['symbolic-ref', 'HEAD', '--quiet'])
1299 current, err = self._gitdir(['symbolic-ref', 'HEAD', '--quiet'])
1294 if err:
1300 if err:
1295 current = None
1301 current = None
1296 return current
1302 return current
1297
1303
1298 def _gitremote(self, remote):
1304 def _gitremote(self, remote):
1299 out = self._gitcommand(['remote', 'show', '-n', remote])
1305 out = self._gitcommand(['remote', 'show', '-n', remote])
1300 line = out.split('\n')[1]
1306 line = out.split('\n')[1]
1301 i = line.index('URL: ') + len('URL: ')
1307 i = line.index('URL: ') + len('URL: ')
1302 return line[i:]
1308 return line[i:]
1303
1309
1304 def _githavelocally(self, revision):
1310 def _githavelocally(self, revision):
1305 out, code = self._gitdir(['cat-file', '-e', revision])
1311 out, code = self._gitdir(['cat-file', '-e', revision])
1306 return code == 0
1312 return code == 0
1307
1313
1308 def _gitisancestor(self, r1, r2):
1314 def _gitisancestor(self, r1, r2):
1309 base = self._gitcommand(['merge-base', r1, r2])
1315 base = self._gitcommand(['merge-base', r1, r2])
1310 return base == r1
1316 return base == r1
1311
1317
1312 def _gitisbare(self):
1318 def _gitisbare(self):
1313 return self._gitcommand(['config', '--bool', 'core.bare']) == 'true'
1319 return self._gitcommand(['config', '--bool', 'core.bare']) == 'true'
1314
1320
1315 def _gitupdatestat(self):
1321 def _gitupdatestat(self):
1316 """This must be run before git diff-index.
1322 """This must be run before git diff-index.
1317 diff-index only looks at changes to file stat;
1323 diff-index only looks at changes to file stat;
1318 this command looks at file contents and updates the stat."""
1324 this command looks at file contents and updates the stat."""
1319 self._gitcommand(['update-index', '-q', '--refresh'])
1325 self._gitcommand(['update-index', '-q', '--refresh'])
1320
1326
1321 def _gitbranchmap(self):
1327 def _gitbranchmap(self):
1322 '''returns 2 things:
1328 '''returns 2 things:
1323 a map from git branch to revision
1329 a map from git branch to revision
1324 a map from revision to branches'''
1330 a map from revision to branches'''
1325 branch2rev = {}
1331 branch2rev = {}
1326 rev2branch = {}
1332 rev2branch = {}
1327
1333
1328 out = self._gitcommand(['for-each-ref', '--format',
1334 out = self._gitcommand(['for-each-ref', '--format',
1329 '%(objectname) %(refname)'])
1335 '%(objectname) %(refname)'])
1330 for line in out.split('\n'):
1336 for line in out.split('\n'):
1331 revision, ref = line.split(' ')
1337 revision, ref = line.split(' ')
1332 if (not ref.startswith('refs/heads/') and
1338 if (not ref.startswith('refs/heads/') and
1333 not ref.startswith('refs/remotes/')):
1339 not ref.startswith('refs/remotes/')):
1334 continue
1340 continue
1335 if ref.startswith('refs/remotes/') and ref.endswith('/HEAD'):
1341 if ref.startswith('refs/remotes/') and ref.endswith('/HEAD'):
1336 continue # ignore remote/HEAD redirects
1342 continue # ignore remote/HEAD redirects
1337 branch2rev[ref] = revision
1343 branch2rev[ref] = revision
1338 rev2branch.setdefault(revision, []).append(ref)
1344 rev2branch.setdefault(revision, []).append(ref)
1339 return branch2rev, rev2branch
1345 return branch2rev, rev2branch
1340
1346
1341 def _gittracking(self, branches):
1347 def _gittracking(self, branches):
1342 'return map of remote branch to local tracking branch'
1348 'return map of remote branch to local tracking branch'
1343 # assumes no more than one local tracking branch for each remote
1349 # assumes no more than one local tracking branch for each remote
1344 tracking = {}
1350 tracking = {}
1345 for b in branches:
1351 for b in branches:
1346 if b.startswith('refs/remotes/'):
1352 if b.startswith('refs/remotes/'):
1347 continue
1353 continue
1348 bname = b.split('/', 2)[2]
1354 bname = b.split('/', 2)[2]
1349 remote = self._gitcommand(['config', 'branch.%s.remote' % bname])
1355 remote = self._gitcommand(['config', 'branch.%s.remote' % bname])
1350 if remote:
1356 if remote:
1351 ref = self._gitcommand(['config', 'branch.%s.merge' % bname])
1357 ref = self._gitcommand(['config', 'branch.%s.merge' % bname])
1352 tracking['refs/remotes/%s/%s' %
1358 tracking['refs/remotes/%s/%s' %
1353 (remote, ref.split('/', 2)[2])] = b
1359 (remote, ref.split('/', 2)[2])] = b
1354 return tracking
1360 return tracking
1355
1361
1356 def _abssource(self, source):
1362 def _abssource(self, source):
1357 if '://' not in source:
1363 if '://' not in source:
1358 # recognize the scp syntax as an absolute source
1364 # recognize the scp syntax as an absolute source
1359 colon = source.find(':')
1365 colon = source.find(':')
1360 if colon != -1 and '/' not in source[:colon]:
1366 if colon != -1 and '/' not in source[:colon]:
1361 return source
1367 return source
1362 self._subsource = source
1368 self._subsource = source
1363 return _abssource(self)
1369 return _abssource(self)
1364
1370
1365 def _fetch(self, source, revision):
1371 def _fetch(self, source, revision):
1366 if self._gitmissing():
1372 if self._gitmissing():
1367 # SEC: check for safe ssh url
1373 # SEC: check for safe ssh url
1368 util.checksafessh(source)
1374 util.checksafessh(source)
1369
1375
1370 source = self._abssource(source)
1376 source = self._abssource(source)
1371 self.ui.status(_('cloning subrepo %s from %s\n') %
1377 self.ui.status(_('cloning subrepo %s from %s\n') %
1372 (self._relpath, source))
1378 (self._relpath, source))
1373 self._gitnodir(['clone', source, self._abspath])
1379 self._gitnodir(['clone', source, self._abspath])
1374 if self._githavelocally(revision):
1380 if self._githavelocally(revision):
1375 return
1381 return
1376 self.ui.status(_('pulling subrepo %s from %s\n') %
1382 self.ui.status(_('pulling subrepo %s from %s\n') %
1377 (self._relpath, self._gitremote('origin')))
1383 (self._relpath, self._gitremote('origin')))
1378 # try only origin: the originally cloned repo
1384 # try only origin: the originally cloned repo
1379 self._gitcommand(['fetch'])
1385 self._gitcommand(['fetch'])
1380 if not self._githavelocally(revision):
1386 if not self._githavelocally(revision):
1381 raise error.Abort(_('revision %s does not exist in subrepository '
1387 raise error.Abort(_('revision %s does not exist in subrepository '
1382 '"%s"\n') % (revision, self._relpath))
1388 '"%s"\n') % (revision, self._relpath))
1383
1389
1384 @annotatesubrepoerror
1390 @annotatesubrepoerror
1385 def dirty(self, ignoreupdate=False, missing=False):
1391 def dirty(self, ignoreupdate=False, missing=False):
1386 if self._gitmissing():
1392 if self._gitmissing():
1387 return self._state[1] != ''
1393 return self._state[1] != ''
1388 if self._gitisbare():
1394 if self._gitisbare():
1389 return True
1395 return True
1390 if not ignoreupdate and self._state[1] != self._gitstate():
1396 if not ignoreupdate and self._state[1] != self._gitstate():
1391 # different version checked out
1397 # different version checked out
1392 return True
1398 return True
1393 # check for staged changes or modified files; ignore untracked files
1399 # check for staged changes or modified files; ignore untracked files
1394 self._gitupdatestat()
1400 self._gitupdatestat()
1395 out, code = self._gitdir(['diff-index', '--quiet', 'HEAD'])
1401 out, code = self._gitdir(['diff-index', '--quiet', 'HEAD'])
1396 return code == 1
1402 return code == 1
1397
1403
1398 def basestate(self):
1404 def basestate(self):
1399 return self._gitstate()
1405 return self._gitstate()
1400
1406
1401 @annotatesubrepoerror
1407 @annotatesubrepoerror
1402 def get(self, state, overwrite=False):
1408 def get(self, state, overwrite=False):
1403 source, revision, kind = state
1409 source, revision, kind = state
1404 if not revision:
1410 if not revision:
1405 self.remove()
1411 self.remove()
1406 return
1412 return
1407 self._fetch(source, revision)
1413 self._fetch(source, revision)
1408 # if the repo was set to be bare, unbare it
1414 # if the repo was set to be bare, unbare it
1409 if self._gitisbare():
1415 if self._gitisbare():
1410 self._gitcommand(['config', 'core.bare', 'false'])
1416 self._gitcommand(['config', 'core.bare', 'false'])
1411 if self._gitstate() == revision:
1417 if self._gitstate() == revision:
1412 self._gitcommand(['reset', '--hard', 'HEAD'])
1418 self._gitcommand(['reset', '--hard', 'HEAD'])
1413 return
1419 return
1414 elif self._gitstate() == revision:
1420 elif self._gitstate() == revision:
1415 if overwrite:
1421 if overwrite:
1416 # first reset the index to unmark new files for commit, because
1422 # first reset the index to unmark new files for commit, because
1417 # reset --hard will otherwise throw away files added for commit,
1423 # reset --hard will otherwise throw away files added for commit,
1418 # not just unmark them.
1424 # not just unmark them.
1419 self._gitcommand(['reset', 'HEAD'])
1425 self._gitcommand(['reset', 'HEAD'])
1420 self._gitcommand(['reset', '--hard', 'HEAD'])
1426 self._gitcommand(['reset', '--hard', 'HEAD'])
1421 return
1427 return
1422 branch2rev, rev2branch = self._gitbranchmap()
1428 branch2rev, rev2branch = self._gitbranchmap()
1423
1429
1424 def checkout(args):
1430 def checkout(args):
1425 cmd = ['checkout']
1431 cmd = ['checkout']
1426 if overwrite:
1432 if overwrite:
1427 # first reset the index to unmark new files for commit, because
1433 # first reset the index to unmark new files for commit, because
1428 # the -f option will otherwise throw away files added for
1434 # the -f option will otherwise throw away files added for
1429 # commit, not just unmark them.
1435 # commit, not just unmark them.
1430 self._gitcommand(['reset', 'HEAD'])
1436 self._gitcommand(['reset', 'HEAD'])
1431 cmd.append('-f')
1437 cmd.append('-f')
1432 self._gitcommand(cmd + args)
1438 self._gitcommand(cmd + args)
1433 _sanitize(self.ui, self.wvfs, '.git')
1439 _sanitize(self.ui, self.wvfs, '.git')
1434
1440
1435 def rawcheckout():
1441 def rawcheckout():
1436 # no branch to checkout, check it out with no branch
1442 # no branch to checkout, check it out with no branch
1437 self.ui.warn(_('checking out detached HEAD in '
1443 self.ui.warn(_('checking out detached HEAD in '
1438 'subrepository "%s"\n') % self._relpath)
1444 'subrepository "%s"\n') % self._relpath)
1439 self.ui.warn(_('check out a git branch if you intend '
1445 self.ui.warn(_('check out a git branch if you intend '
1440 'to make changes\n'))
1446 'to make changes\n'))
1441 checkout(['-q', revision])
1447 checkout(['-q', revision])
1442
1448
1443 if revision not in rev2branch:
1449 if revision not in rev2branch:
1444 rawcheckout()
1450 rawcheckout()
1445 return
1451 return
1446 branches = rev2branch[revision]
1452 branches = rev2branch[revision]
1447 firstlocalbranch = None
1453 firstlocalbranch = None
1448 for b in branches:
1454 for b in branches:
1449 if b == 'refs/heads/master':
1455 if b == 'refs/heads/master':
1450 # master trumps all other branches
1456 # master trumps all other branches
1451 checkout(['refs/heads/master'])
1457 checkout(['refs/heads/master'])
1452 return
1458 return
1453 if not firstlocalbranch and not b.startswith('refs/remotes/'):
1459 if not firstlocalbranch and not b.startswith('refs/remotes/'):
1454 firstlocalbranch = b
1460 firstlocalbranch = b
1455 if firstlocalbranch:
1461 if firstlocalbranch:
1456 checkout([firstlocalbranch])
1462 checkout([firstlocalbranch])
1457 return
1463 return
1458
1464
1459 tracking = self._gittracking(branch2rev.keys())
1465 tracking = self._gittracking(branch2rev.keys())
1460 # choose a remote branch already tracked if possible
1466 # choose a remote branch already tracked if possible
1461 remote = branches[0]
1467 remote = branches[0]
1462 if remote not in tracking:
1468 if remote not in tracking:
1463 for b in branches:
1469 for b in branches:
1464 if b in tracking:
1470 if b in tracking:
1465 remote = b
1471 remote = b
1466 break
1472 break
1467
1473
1468 if remote not in tracking:
1474 if remote not in tracking:
1469 # create a new local tracking branch
1475 # create a new local tracking branch
1470 local = remote.split('/', 3)[3]
1476 local = remote.split('/', 3)[3]
1471 checkout(['-b', local, remote])
1477 checkout(['-b', local, remote])
1472 elif self._gitisancestor(branch2rev[tracking[remote]], remote):
1478 elif self._gitisancestor(branch2rev[tracking[remote]], remote):
1473 # When updating to a tracked remote branch,
1479 # When updating to a tracked remote branch,
1474 # if the local tracking branch is downstream of it,
1480 # if the local tracking branch is downstream of it,
1475 # a normal `git pull` would have performed a "fast-forward merge"
1481 # a normal `git pull` would have performed a "fast-forward merge"
1476 # which is equivalent to updating the local branch to the remote.
1482 # which is equivalent to updating the local branch to the remote.
1477 # Since we are only looking at branching at update, we need to
1483 # Since we are only looking at branching at update, we need to
1478 # detect this situation and perform this action lazily.
1484 # detect this situation and perform this action lazily.
1479 if tracking[remote] != self._gitcurrentbranch():
1485 if tracking[remote] != self._gitcurrentbranch():
1480 checkout([tracking[remote]])
1486 checkout([tracking[remote]])
1481 self._gitcommand(['merge', '--ff', remote])
1487 self._gitcommand(['merge', '--ff', remote])
1482 _sanitize(self.ui, self.wvfs, '.git')
1488 _sanitize(self.ui, self.wvfs, '.git')
1483 else:
1489 else:
1484 # a real merge would be required, just checkout the revision
1490 # a real merge would be required, just checkout the revision
1485 rawcheckout()
1491 rawcheckout()
1486
1492
1487 @annotatesubrepoerror
1493 @annotatesubrepoerror
1488 def commit(self, text, user, date):
1494 def commit(self, text, user, date):
1489 if self._gitmissing():
1495 if self._gitmissing():
1490 raise error.Abort(_("subrepo %s is missing") % self._relpath)
1496 raise error.Abort(_("subrepo %s is missing") % self._relpath)
1491 cmd = ['commit', '-a', '-m', text]
1497 cmd = ['commit', '-a', '-m', text]
1492 env = encoding.environ.copy()
1498 env = encoding.environ.copy()
1493 if user:
1499 if user:
1494 cmd += ['--author', user]
1500 cmd += ['--author', user]
1495 if date:
1501 if date:
1496 # git's date parser silently ignores when seconds < 1e9
1502 # git's date parser silently ignores when seconds < 1e9
1497 # convert to ISO8601
1503 # convert to ISO8601
1498 env['GIT_AUTHOR_DATE'] = dateutil.datestr(date,
1504 env['GIT_AUTHOR_DATE'] = dateutil.datestr(date,
1499 '%Y-%m-%dT%H:%M:%S %1%2')
1505 '%Y-%m-%dT%H:%M:%S %1%2')
1500 self._gitcommand(cmd, env=env)
1506 self._gitcommand(cmd, env=env)
1501 # make sure commit works otherwise HEAD might not exist under certain
1507 # make sure commit works otherwise HEAD might not exist under certain
1502 # circumstances
1508 # circumstances
1503 return self._gitstate()
1509 return self._gitstate()
1504
1510
1505 @annotatesubrepoerror
1511 @annotatesubrepoerror
1506 def merge(self, state):
1512 def merge(self, state):
1507 source, revision, kind = state
1513 source, revision, kind = state
1508 self._fetch(source, revision)
1514 self._fetch(source, revision)
1509 base = self._gitcommand(['merge-base', revision, self._state[1]])
1515 base = self._gitcommand(['merge-base', revision, self._state[1]])
1510 self._gitupdatestat()
1516 self._gitupdatestat()
1511 out, code = self._gitdir(['diff-index', '--quiet', 'HEAD'])
1517 out, code = self._gitdir(['diff-index', '--quiet', 'HEAD'])
1512
1518
1513 def mergefunc():
1519 def mergefunc():
1514 if base == revision:
1520 if base == revision:
1515 self.get(state) # fast forward merge
1521 self.get(state) # fast forward merge
1516 elif base != self._state[1]:
1522 elif base != self._state[1]:
1517 self._gitcommand(['merge', '--no-commit', revision])
1523 self._gitcommand(['merge', '--no-commit', revision])
1518 _sanitize(self.ui, self.wvfs, '.git')
1524 _sanitize(self.ui, self.wvfs, '.git')
1519
1525
1520 if self.dirty():
1526 if self.dirty():
1521 if self._gitstate() != revision:
1527 if self._gitstate() != revision:
1522 dirty = self._gitstate() == self._state[1] or code != 0
1528 dirty = self._gitstate() == self._state[1] or code != 0
1523 if _updateprompt(self.ui, self, dirty,
1529 if _updateprompt(self.ui, self, dirty,
1524 self._state[1][:7], revision[:7]):
1530 self._state[1][:7], revision[:7]):
1525 mergefunc()
1531 mergefunc()
1526 else:
1532 else:
1527 mergefunc()
1533 mergefunc()
1528
1534
1529 @annotatesubrepoerror
1535 @annotatesubrepoerror
1530 def push(self, opts):
1536 def push(self, opts):
1531 force = opts.get('force')
1537 force = opts.get('force')
1532
1538
1533 if not self._state[1]:
1539 if not self._state[1]:
1534 return True
1540 return True
1535 if self._gitmissing():
1541 if self._gitmissing():
1536 raise error.Abort(_("subrepo %s is missing") % self._relpath)
1542 raise error.Abort(_("subrepo %s is missing") % self._relpath)
1537 # if a branch in origin contains the revision, nothing to do
1543 # if a branch in origin contains the revision, nothing to do
1538 branch2rev, rev2branch = self._gitbranchmap()
1544 branch2rev, rev2branch = self._gitbranchmap()
1539 if self._state[1] in rev2branch:
1545 if self._state[1] in rev2branch:
1540 for b in rev2branch[self._state[1]]:
1546 for b in rev2branch[self._state[1]]:
1541 if b.startswith('refs/remotes/origin/'):
1547 if b.startswith('refs/remotes/origin/'):
1542 return True
1548 return True
1543 for b, revision in branch2rev.iteritems():
1549 for b, revision in branch2rev.iteritems():
1544 if b.startswith('refs/remotes/origin/'):
1550 if b.startswith('refs/remotes/origin/'):
1545 if self._gitisancestor(self._state[1], revision):
1551 if self._gitisancestor(self._state[1], revision):
1546 return True
1552 return True
1547 # otherwise, try to push the currently checked out branch
1553 # otherwise, try to push the currently checked out branch
1548 cmd = ['push']
1554 cmd = ['push']
1549 if force:
1555 if force:
1550 cmd.append('--force')
1556 cmd.append('--force')
1551
1557
1552 current = self._gitcurrentbranch()
1558 current = self._gitcurrentbranch()
1553 if current:
1559 if current:
1554 # determine if the current branch is even useful
1560 # determine if the current branch is even useful
1555 if not self._gitisancestor(self._state[1], current):
1561 if not self._gitisancestor(self._state[1], current):
1556 self.ui.warn(_('unrelated git branch checked out '
1562 self.ui.warn(_('unrelated git branch checked out '
1557 'in subrepository "%s"\n') % self._relpath)
1563 'in subrepository "%s"\n') % self._relpath)
1558 return False
1564 return False
1559 self.ui.status(_('pushing branch %s of subrepository "%s"\n') %
1565 self.ui.status(_('pushing branch %s of subrepository "%s"\n') %
1560 (current.split('/', 2)[2], self._relpath))
1566 (current.split('/', 2)[2], self._relpath))
1561 ret = self._gitdir(cmd + ['origin', current])
1567 ret = self._gitdir(cmd + ['origin', current])
1562 return ret[1] == 0
1568 return ret[1] == 0
1563 else:
1569 else:
1564 self.ui.warn(_('no branch checked out in subrepository "%s"\n'
1570 self.ui.warn(_('no branch checked out in subrepository "%s"\n'
1565 'cannot push revision %s\n') %
1571 'cannot push revision %s\n') %
1566 (self._relpath, self._state[1]))
1572 (self._relpath, self._state[1]))
1567 return False
1573 return False
1568
1574
1569 @annotatesubrepoerror
1575 @annotatesubrepoerror
1570 def add(self, ui, match, prefix, explicitonly, **opts):
1576 def add(self, ui, match, prefix, explicitonly, **opts):
1571 if self._gitmissing():
1577 if self._gitmissing():
1572 return []
1578 return []
1573
1579
1574 (modified, added, removed,
1580 (modified, added, removed,
1575 deleted, unknown, ignored, clean) = self.status(None, unknown=True,
1581 deleted, unknown, ignored, clean) = self.status(None, unknown=True,
1576 clean=True)
1582 clean=True)
1577
1583
1578 tracked = set()
1584 tracked = set()
1579 # dirstates 'amn' warn, 'r' is added again
1585 # dirstates 'amn' warn, 'r' is added again
1580 for l in (modified, added, deleted, clean):
1586 for l in (modified, added, deleted, clean):
1581 tracked.update(l)
1587 tracked.update(l)
1582
1588
1583 # Unknown files not of interest will be rejected by the matcher
1589 # Unknown files not of interest will be rejected by the matcher
1584 files = unknown
1590 files = unknown
1585 files.extend(match.files())
1591 files.extend(match.files())
1586
1592
1587 rejected = []
1593 rejected = []
1588
1594
1589 files = [f for f in sorted(set(files)) if match(f)]
1595 files = [f for f in sorted(set(files)) if match(f)]
1590 for f in files:
1596 for f in files:
1591 exact = match.exact(f)
1597 exact = match.exact(f)
1592 command = ["add"]
1598 command = ["add"]
1593 if exact:
1599 if exact:
1594 command.append("-f") #should be added, even if ignored
1600 command.append("-f") #should be added, even if ignored
1595 if ui.verbose or not exact:
1601 if ui.verbose or not exact:
1596 ui.status(_('adding %s\n') % match.rel(f))
1602 ui.status(_('adding %s\n') % match.rel(f))
1597
1603
1598 if f in tracked: # hg prints 'adding' even if already tracked
1604 if f in tracked: # hg prints 'adding' even if already tracked
1599 if exact:
1605 if exact:
1600 rejected.append(f)
1606 rejected.append(f)
1601 continue
1607 continue
1602 if not opts.get(r'dry_run'):
1608 if not opts.get(r'dry_run'):
1603 self._gitcommand(command + [f])
1609 self._gitcommand(command + [f])
1604
1610
1605 for f in rejected:
1611 for f in rejected:
1606 ui.warn(_("%s already tracked!\n") % match.abs(f))
1612 ui.warn(_("%s already tracked!\n") % match.abs(f))
1607
1613
1608 return rejected
1614 return rejected
1609
1615
1610 @annotatesubrepoerror
1616 @annotatesubrepoerror
1611 def remove(self):
1617 def remove(self):
1612 if self._gitmissing():
1618 if self._gitmissing():
1613 return
1619 return
1614 if self.dirty():
1620 if self.dirty():
1615 self.ui.warn(_('not removing repo %s because '
1621 self.ui.warn(_('not removing repo %s because '
1616 'it has changes.\n') % self._relpath)
1622 'it has changes.\n') % self._relpath)
1617 return
1623 return
1618 # we can't fully delete the repository as it may contain
1624 # we can't fully delete the repository as it may contain
1619 # local-only history
1625 # local-only history
1620 self.ui.note(_('removing subrepo %s\n') % self._relpath)
1626 self.ui.note(_('removing subrepo %s\n') % self._relpath)
1621 self._gitcommand(['config', 'core.bare', 'true'])
1627 self._gitcommand(['config', 'core.bare', 'true'])
1622 for f, kind in self.wvfs.readdir():
1628 for f, kind in self.wvfs.readdir():
1623 if f == '.git':
1629 if f == '.git':
1624 continue
1630 continue
1625 if kind == stat.S_IFDIR:
1631 if kind == stat.S_IFDIR:
1626 self.wvfs.rmtree(f)
1632 self.wvfs.rmtree(f)
1627 else:
1633 else:
1628 self.wvfs.unlink(f)
1634 self.wvfs.unlink(f)
1629
1635
1630 def archive(self, archiver, prefix, match=None, decode=True):
1636 def archive(self, archiver, prefix, match=None, decode=True):
1631 total = 0
1637 total = 0
1632 source, revision = self._state
1638 source, revision = self._state
1633 if not revision:
1639 if not revision:
1634 return total
1640 return total
1635 self._fetch(source, revision)
1641 self._fetch(source, revision)
1636
1642
1637 # Parse git's native archive command.
1643 # Parse git's native archive command.
1638 # This should be much faster than manually traversing the trees
1644 # This should be much faster than manually traversing the trees
1639 # and objects with many subprocess calls.
1645 # and objects with many subprocess calls.
1640 tarstream = self._gitcommand(['archive', revision], stream=True)
1646 tarstream = self._gitcommand(['archive', revision], stream=True)
1641 tar = tarfile.open(fileobj=tarstream, mode=r'r|')
1647 tar = tarfile.open(fileobj=tarstream, mode=r'r|')
1642 relpath = subrelpath(self)
1648 relpath = subrelpath(self)
1643 progress = self.ui.makeprogress(_('archiving (%s)') % relpath,
1649 progress = self.ui.makeprogress(_('archiving (%s)') % relpath,
1644 unit=_('files'))
1650 unit=_('files'))
1645 progress.update(0)
1651 progress.update(0)
1646 for info in tar:
1652 for info in tar:
1647 if info.isdir():
1653 if info.isdir():
1648 continue
1654 continue
1649 if match and not match(info.name):
1655 if match and not match(info.name):
1650 continue
1656 continue
1651 if info.issym():
1657 if info.issym():
1652 data = info.linkname
1658 data = info.linkname
1653 else:
1659 else:
1654 data = tar.extractfile(info).read()
1660 data = tar.extractfile(info).read()
1655 archiver.addfile(prefix + self._path + '/' + info.name,
1661 archiver.addfile(prefix + self._path + '/' + info.name,
1656 info.mode, info.issym(), data)
1662 info.mode, info.issym(), data)
1657 total += 1
1663 total += 1
1658 progress.increment()
1664 progress.increment()
1659 progress.complete()
1665 progress.complete()
1660 return total
1666 return total
1661
1667
1662
1668
1663 @annotatesubrepoerror
1669 @annotatesubrepoerror
1664 def cat(self, match, fm, fntemplate, prefix, **opts):
1670 def cat(self, match, fm, fntemplate, prefix, **opts):
1665 rev = self._state[1]
1671 rev = self._state[1]
1666 if match.anypats():
1672 if match.anypats():
1667 return 1 #No support for include/exclude yet
1673 return 1 #No support for include/exclude yet
1668
1674
1669 if not match.files():
1675 if not match.files():
1670 return 1
1676 return 1
1671
1677
1672 # TODO: add support for non-plain formatter (see cmdutil.cat())
1678 # TODO: add support for non-plain formatter (see cmdutil.cat())
1673 for f in match.files():
1679 for f in match.files():
1674 output = self._gitcommand(["show", "%s:%s" % (rev, f)])
1680 output = self._gitcommand(["show", "%s:%s" % (rev, f)])
1675 fp = cmdutil.makefileobj(self._ctx, fntemplate,
1681 fp = cmdutil.makefileobj(self._ctx, fntemplate,
1676 pathname=self.wvfs.reljoin(prefix, f))
1682 pathname=self.wvfs.reljoin(prefix, f))
1677 fp.write(output)
1683 fp.write(output)
1678 fp.close()
1684 fp.close()
1679 return 0
1685 return 0
1680
1686
1681
1687
1682 @annotatesubrepoerror
1688 @annotatesubrepoerror
1683 def status(self, rev2, **opts):
1689 def status(self, rev2, **opts):
1684 rev1 = self._state[1]
1690 rev1 = self._state[1]
1685 if self._gitmissing() or not rev1:
1691 if self._gitmissing() or not rev1:
1686 # if the repo is missing, return no results
1692 # if the repo is missing, return no results
1687 return scmutil.status([], [], [], [], [], [], [])
1693 return scmutil.status([], [], [], [], [], [], [])
1688 modified, added, removed = [], [], []
1694 modified, added, removed = [], [], []
1689 self._gitupdatestat()
1695 self._gitupdatestat()
1690 if rev2:
1696 if rev2:
1691 command = ['diff-tree', '--no-renames', '-r', rev1, rev2]
1697 command = ['diff-tree', '--no-renames', '-r', rev1, rev2]
1692 else:
1698 else:
1693 command = ['diff-index', '--no-renames', rev1]
1699 command = ['diff-index', '--no-renames', rev1]
1694 out = self._gitcommand(command)
1700 out = self._gitcommand(command)
1695 for line in out.split('\n'):
1701 for line in out.split('\n'):
1696 tab = line.find('\t')
1702 tab = line.find('\t')
1697 if tab == -1:
1703 if tab == -1:
1698 continue
1704 continue
1699 status, f = line[tab - 1:tab], line[tab + 1:]
1705 status, f = line[tab - 1:tab], line[tab + 1:]
1700 if status == 'M':
1706 if status == 'M':
1701 modified.append(f)
1707 modified.append(f)
1702 elif status == 'A':
1708 elif status == 'A':
1703 added.append(f)
1709 added.append(f)
1704 elif status == 'D':
1710 elif status == 'D':
1705 removed.append(f)
1711 removed.append(f)
1706
1712
1707 deleted, unknown, ignored, clean = [], [], [], []
1713 deleted, unknown, ignored, clean = [], [], [], []
1708
1714
1709 command = ['status', '--porcelain', '-z']
1715 command = ['status', '--porcelain', '-z']
1710 if opts.get(r'unknown'):
1716 if opts.get(r'unknown'):
1711 command += ['--untracked-files=all']
1717 command += ['--untracked-files=all']
1712 if opts.get(r'ignored'):
1718 if opts.get(r'ignored'):
1713 command += ['--ignored']
1719 command += ['--ignored']
1714 out = self._gitcommand(command)
1720 out = self._gitcommand(command)
1715
1721
1716 changedfiles = set()
1722 changedfiles = set()
1717 changedfiles.update(modified)
1723 changedfiles.update(modified)
1718 changedfiles.update(added)
1724 changedfiles.update(added)
1719 changedfiles.update(removed)
1725 changedfiles.update(removed)
1720 for line in out.split('\0'):
1726 for line in out.split('\0'):
1721 if not line:
1727 if not line:
1722 continue
1728 continue
1723 st = line[0:2]
1729 st = line[0:2]
1724 #moves and copies show 2 files on one line
1730 #moves and copies show 2 files on one line
1725 if line.find('\0') >= 0:
1731 if line.find('\0') >= 0:
1726 filename1, filename2 = line[3:].split('\0')
1732 filename1, filename2 = line[3:].split('\0')
1727 else:
1733 else:
1728 filename1 = line[3:]
1734 filename1 = line[3:]
1729 filename2 = None
1735 filename2 = None
1730
1736
1731 changedfiles.add(filename1)
1737 changedfiles.add(filename1)
1732 if filename2:
1738 if filename2:
1733 changedfiles.add(filename2)
1739 changedfiles.add(filename2)
1734
1740
1735 if st == '??':
1741 if st == '??':
1736 unknown.append(filename1)
1742 unknown.append(filename1)
1737 elif st == '!!':
1743 elif st == '!!':
1738 ignored.append(filename1)
1744 ignored.append(filename1)
1739
1745
1740 if opts.get(r'clean'):
1746 if opts.get(r'clean'):
1741 out = self._gitcommand(['ls-files'])
1747 out = self._gitcommand(['ls-files'])
1742 for f in out.split('\n'):
1748 for f in out.split('\n'):
1743 if not f in changedfiles:
1749 if not f in changedfiles:
1744 clean.append(f)
1750 clean.append(f)
1745
1751
1746 return scmutil.status(modified, added, removed, deleted,
1752 return scmutil.status(modified, added, removed, deleted,
1747 unknown, ignored, clean)
1753 unknown, ignored, clean)
1748
1754
1749 @annotatesubrepoerror
1755 @annotatesubrepoerror
1750 def diff(self, ui, diffopts, node2, match, prefix, **opts):
1756 def diff(self, ui, diffopts, node2, match, prefix, **opts):
1751 node1 = self._state[1]
1757 node1 = self._state[1]
1752 cmd = ['diff', '--no-renames']
1758 cmd = ['diff', '--no-renames']
1753 if opts[r'stat']:
1759 if opts[r'stat']:
1754 cmd.append('--stat')
1760 cmd.append('--stat')
1755 else:
1761 else:
1756 # for Git, this also implies '-p'
1762 # for Git, this also implies '-p'
1757 cmd.append('-U%d' % diffopts.context)
1763 cmd.append('-U%d' % diffopts.context)
1758
1764
1759 gitprefix = self.wvfs.reljoin(prefix, self._path)
1765 gitprefix = self.wvfs.reljoin(prefix, self._path)
1760
1766
1761 if diffopts.noprefix:
1767 if diffopts.noprefix:
1762 cmd.extend(['--src-prefix=%s/' % gitprefix,
1768 cmd.extend(['--src-prefix=%s/' % gitprefix,
1763 '--dst-prefix=%s/' % gitprefix])
1769 '--dst-prefix=%s/' % gitprefix])
1764 else:
1770 else:
1765 cmd.extend(['--src-prefix=a/%s/' % gitprefix,
1771 cmd.extend(['--src-prefix=a/%s/' % gitprefix,
1766 '--dst-prefix=b/%s/' % gitprefix])
1772 '--dst-prefix=b/%s/' % gitprefix])
1767
1773
1768 if diffopts.ignorews:
1774 if diffopts.ignorews:
1769 cmd.append('--ignore-all-space')
1775 cmd.append('--ignore-all-space')
1770 if diffopts.ignorewsamount:
1776 if diffopts.ignorewsamount:
1771 cmd.append('--ignore-space-change')
1777 cmd.append('--ignore-space-change')
1772 if self._gitversion(self._gitcommand(['--version'])) >= (1, 8, 4) \
1778 if self._gitversion(self._gitcommand(['--version'])) >= (1, 8, 4) \
1773 and diffopts.ignoreblanklines:
1779 and diffopts.ignoreblanklines:
1774 cmd.append('--ignore-blank-lines')
1780 cmd.append('--ignore-blank-lines')
1775
1781
1776 cmd.append(node1)
1782 cmd.append(node1)
1777 if node2:
1783 if node2:
1778 cmd.append(node2)
1784 cmd.append(node2)
1779
1785
1780 output = ""
1786 output = ""
1781 if match.always():
1787 if match.always():
1782 output += self._gitcommand(cmd) + '\n'
1788 output += self._gitcommand(cmd) + '\n'
1783 else:
1789 else:
1784 st = self.status(node2)[:3]
1790 st = self.status(node2)[:3]
1785 files = [f for sublist in st for f in sublist]
1791 files = [f for sublist in st for f in sublist]
1786 for f in files:
1792 for f in files:
1787 if match(f):
1793 if match(f):
1788 output += self._gitcommand(cmd + ['--', f]) + '\n'
1794 output += self._gitcommand(cmd + ['--', f]) + '\n'
1789
1795
1790 if output.strip():
1796 if output.strip():
1791 ui.write(output)
1797 ui.write(output)
1792
1798
1793 @annotatesubrepoerror
1799 @annotatesubrepoerror
1794 def revert(self, substate, *pats, **opts):
1800 def revert(self, substate, *pats, **opts):
1795 self.ui.status(_('reverting subrepo %s\n') % substate[0])
1801 self.ui.status(_('reverting subrepo %s\n') % substate[0])
1796 if not opts.get(r'no_backup'):
1802 if not opts.get(r'no_backup'):
1797 status = self.status(None)
1803 status = self.status(None)
1798 names = status.modified
1804 names = status.modified
1799 for name in names:
1805 for name in names:
1800 bakname = scmutil.origpath(self.ui, self._subparent, name)
1806 bakname = scmutil.origpath(self.ui, self._subparent, name)
1801 self.ui.note(_('saving current version of %s as %s\n') %
1807 self.ui.note(_('saving current version of %s as %s\n') %
1802 (name, bakname))
1808 (name, bakname))
1803 self.wvfs.rename(name, bakname)
1809 self.wvfs.rename(name, bakname)
1804
1810
1805 if not opts.get(r'dry_run'):
1811 if not opts.get(r'dry_run'):
1806 self.get(substate, overwrite=True)
1812 self.get(substate, overwrite=True)
1807 return []
1813 return []
1808
1814
1809 def shortid(self, revid):
1815 def shortid(self, revid):
1810 return revid[:7]
1816 return revid[:7]
1811
1817
1812 types = {
1818 types = {
1813 'hg': hgsubrepo,
1819 'hg': hgsubrepo,
1814 'svn': svnsubrepo,
1820 'svn': svnsubrepo,
1815 'git': gitsubrepo,
1821 'git': gitsubrepo,
1816 }
1822 }
@@ -1,3147 +1,3158 b''
1 @ (34) head
1 @ (34) head
2 |
2 |
3 | o (33) head
3 | o (33) head
4 | |
4 | |
5 o | (32) expand
5 o | (32) expand
6 |\ \
6 |\ \
7 | o \ (31) expand
7 | o \ (31) expand
8 | |\ \
8 | |\ \
9 | | o \ (30) expand
9 | | o \ (30) expand
10 | | |\ \
10 | | |\ \
11 | | | o | (29) regular commit
11 | | | o | (29) regular commit
12 | | | | |
12 | | | | |
13 | | o | | (28) merge zero known
13 | | o | | (28) merge zero known
14 | | |\ \ \
14 | | |\ \ \
15 o | | | | | (27) collapse
15 o | | | | | (27) collapse
16 |/ / / / /
16 |/ / / / /
17 | | o---+ (26) merge one known; far right
17 | | o---+ (26) merge one known; far right
18 | | | | |
18 | | | | |
19 +---o | | (25) merge one known; far left
19 +---o | | (25) merge one known; far left
20 | | | | |
20 | | | | |
21 | | o | | (24) merge one known; immediate right
21 | | o | | (24) merge one known; immediate right
22 | | |\| |
22 | | |\| |
23 | | o | | (23) merge one known; immediate left
23 | | o | | (23) merge one known; immediate left
24 | |/| | |
24 | |/| | |
25 +---o---+ (22) merge two known; one far left, one far right
25 +---o---+ (22) merge two known; one far left, one far right
26 | | / /
26 | | / /
27 o | | | (21) expand
27 o | | | (21) expand
28 |\ \ \ \
28 |\ \ \ \
29 | o---+-+ (20) merge two known; two far right
29 | o---+-+ (20) merge two known; two far right
30 | / / /
30 | / / /
31 o | | | (19) expand
31 o | | | (19) expand
32 |\ \ \ \
32 |\ \ \ \
33 +---+---o (18) merge two known; two far left
33 +---+---o (18) merge two known; two far left
34 | | | |
34 | | | |
35 | o | | (17) expand
35 | o | | (17) expand
36 | |\ \ \
36 | |\ \ \
37 | | o---+ (16) merge two known; one immediate right, one near right
37 | | o---+ (16) merge two known; one immediate right, one near right
38 | | |/ /
38 | | |/ /
39 o | | | (15) expand
39 o | | | (15) expand
40 |\ \ \ \
40 |\ \ \ \
41 | o-----+ (14) merge two known; one immediate right, one far right
41 | o-----+ (14) merge two known; one immediate right, one far right
42 | |/ / /
42 | |/ / /
43 o | | | (13) expand
43 o | | | (13) expand
44 |\ \ \ \
44 |\ \ \ \
45 +---o | | (12) merge two known; one immediate right, one far left
45 +---o | | (12) merge two known; one immediate right, one far left
46 | | |/ /
46 | | |/ /
47 | o | | (11) expand
47 | o | | (11) expand
48 | |\ \ \
48 | |\ \ \
49 | | o---+ (10) merge two known; one immediate left, one near right
49 | | o---+ (10) merge two known; one immediate left, one near right
50 | |/ / /
50 | |/ / /
51 o | | | (9) expand
51 o | | | (9) expand
52 |\ \ \ \
52 |\ \ \ \
53 | o-----+ (8) merge two known; one immediate left, one far right
53 | o-----+ (8) merge two known; one immediate left, one far right
54 |/ / / /
54 |/ / / /
55 o | | | (7) expand
55 o | | | (7) expand
56 |\ \ \ \
56 |\ \ \ \
57 +---o | | (6) merge two known; one immediate left, one far left
57 +---o | | (6) merge two known; one immediate left, one far left
58 | |/ / /
58 | |/ / /
59 | o | | (5) expand
59 | o | | (5) expand
60 | |\ \ \
60 | |\ \ \
61 | | o | | (4) merge two known; one immediate left, one immediate right
61 | | o | | (4) merge two known; one immediate left, one immediate right
62 | |/|/ /
62 | |/|/ /
63 | o / / (3) collapse
63 | o / / (3) collapse
64 |/ / /
64 |/ / /
65 o / / (2) collapse
65 o / / (2) collapse
66 |/ /
66 |/ /
67 o / (1) collapse
67 o / (1) collapse
68 |/
68 |/
69 o (0) root
69 o (0) root
70
70
71 $ commit()
71 $ commit()
72 > {
72 > {
73 > rev=$1
73 > rev=$1
74 > msg=$2
74 > msg=$2
75 > shift 2
75 > shift 2
76 > if [ "$#" -gt 0 ]; then
76 > if [ "$#" -gt 0 ]; then
77 > hg debugsetparents "$@"
77 > hg debugsetparents "$@"
78 > fi
78 > fi
79 > echo $rev > a
79 > echo $rev > a
80 > hg commit -Aqd "$rev 0" -m "($rev) $msg"
80 > hg commit -Aqd "$rev 0" -m "($rev) $msg"
81 > }
81 > }
82
82
83 $ cat > printrevset.py <<EOF
83 $ cat > printrevset.py <<EOF
84 > from __future__ import absolute_import
84 > from __future__ import absolute_import
85 > from mercurial import (
85 > from mercurial import (
86 > cmdutil,
86 > cmdutil,
87 > commands,
87 > commands,
88 > extensions,
88 > extensions,
89 > logcmdutil,
89 > logcmdutil,
90 > revsetlang,
90 > revsetlang,
91 > smartset,
91 > smartset,
92 > )
92 > )
93 >
93 >
94 > from mercurial.utils import (
94 > from mercurial.utils import (
95 > stringutil,
95 > stringutil,
96 > )
96 > )
97 >
97 >
98 > def logrevset(repo, pats, opts):
98 > def logrevset(repo, pats, opts):
99 > revs = logcmdutil._initialrevs(repo, opts)
99 > revs = logcmdutil._initialrevs(repo, opts)
100 > if not revs:
100 > if not revs:
101 > return None
101 > return None
102 > match, pats, slowpath = logcmdutil._makematcher(repo, revs, pats, opts)
102 > match, pats, slowpath = logcmdutil._makematcher(repo, revs, pats, opts)
103 > return logcmdutil._makerevset(repo, match, pats, slowpath, opts)
103 > return logcmdutil._makerevset(repo, match, pats, slowpath, opts)
104 >
104 >
105 > def uisetup(ui):
105 > def uisetup(ui):
106 > def printrevset(orig, repo, pats, opts):
106 > def printrevset(orig, repo, pats, opts):
107 > revs, filematcher = orig(repo, pats, opts)
107 > revs, filematcher = orig(repo, pats, opts)
108 > if opts.get(b'print_revset'):
108 > if opts.get(b'print_revset'):
109 > expr = logrevset(repo, pats, opts)
109 > expr = logrevset(repo, pats, opts)
110 > if expr:
110 > if expr:
111 > tree = revsetlang.parse(expr)
111 > tree = revsetlang.parse(expr)
112 > tree = revsetlang.analyze(tree)
112 > tree = revsetlang.analyze(tree)
113 > else:
113 > else:
114 > tree = []
114 > tree = []
115 > ui = repo.ui
115 > ui = repo.ui
116 > ui.write(b'%r\n' % (opts.get(b'rev', []),))
116 > ui.write(b'%r\n' % (opts.get(b'rev', []),))
117 > ui.write(revsetlang.prettyformat(tree) + b'\n')
117 > ui.write(revsetlang.prettyformat(tree) + b'\n')
118 > ui.write(stringutil.prettyrepr(revs) + b'\n')
118 > ui.write(stringutil.prettyrepr(revs) + b'\n')
119 > revs = smartset.baseset() # display no revisions
119 > revs = smartset.baseset() # display no revisions
120 > return revs, filematcher
120 > return revs, filematcher
121 > extensions.wrapfunction(logcmdutil, 'getrevs', printrevset)
121 > extensions.wrapfunction(logcmdutil, 'getrevs', printrevset)
122 > aliases, entry = cmdutil.findcmd(b'log', commands.table)
122 > aliases, entry = cmdutil.findcmd(b'log', commands.table)
123 > entry[1].append((b'', b'print-revset', False,
123 > entry[1].append((b'', b'print-revset', False,
124 > b'print generated revset and exit (DEPRECATED)'))
124 > b'print generated revset and exit (DEPRECATED)'))
125 > EOF
125 > EOF
126
126
127 $ echo "[extensions]" >> $HGRCPATH
127 $ echo "[extensions]" >> $HGRCPATH
128 $ echo "printrevset=`pwd`/printrevset.py" >> $HGRCPATH
128 $ echo "printrevset=`pwd`/printrevset.py" >> $HGRCPATH
129 $ echo "beautifygraph=" >> $HGRCPATH
129 $ echo "beautifygraph=" >> $HGRCPATH
130
130
131 Set a default of narrow-text UTF-8.
131 Set a default of narrow-text UTF-8.
132
132
133 $ HGENCODING=UTF-8; export HGENCODING
133 $ HGENCODING=UTF-8; export HGENCODING
134 $ HGENCODINGAMBIGUOUS=narrow; export HGENCODINGAMBIGUOUS
134 $ HGENCODINGAMBIGUOUS=narrow; export HGENCODINGAMBIGUOUS
135
135
136 Empty repo:
136 Empty repo:
137
137
138 $ hg init repo
138 $ hg init repo
139 $ cd repo
139 $ cd repo
140 $ hg log -G
140 $ hg log -G
141
141
142 Building DAG:
142 Building DAG:
143
143
144 $ commit 0 "root"
144 $ commit 0 "root"
145 $ commit 1 "collapse" 0
145 $ commit 1 "collapse" 0
146 $ commit 2 "collapse" 1
146 $ commit 2 "collapse" 1
147 $ commit 3 "collapse" 2
147 $ commit 3 "collapse" 2
148 $ commit 4 "merge two known; one immediate left, one immediate right" 1 3
148 $ commit 4 "merge two known; one immediate left, one immediate right" 1 3
149 $ commit 5 "expand" 3 4
149 $ commit 5 "expand" 3 4
150 $ commit 6 "merge two known; one immediate left, one far left" 2 5
150 $ commit 6 "merge two known; one immediate left, one far left" 2 5
151 $ commit 7 "expand" 2 5
151 $ commit 7 "expand" 2 5
152 $ commit 8 "merge two known; one immediate left, one far right" 0 7
152 $ commit 8 "merge two known; one immediate left, one far right" 0 7
153 $ commit 9 "expand" 7 8
153 $ commit 9 "expand" 7 8
154 $ commit 10 "merge two known; one immediate left, one near right" 0 6
154 $ commit 10 "merge two known; one immediate left, one near right" 0 6
155 $ commit 11 "expand" 6 10
155 $ commit 11 "expand" 6 10
156 $ commit 12 "merge two known; one immediate right, one far left" 1 9
156 $ commit 12 "merge two known; one immediate right, one far left" 1 9
157 $ commit 13 "expand" 9 11
157 $ commit 13 "expand" 9 11
158 $ commit 14 "merge two known; one immediate right, one far right" 0 12
158 $ commit 14 "merge two known; one immediate right, one far right" 0 12
159 $ commit 15 "expand" 13 14
159 $ commit 15 "expand" 13 14
160 $ commit 16 "merge two known; one immediate right, one near right" 0 1
160 $ commit 16 "merge two known; one immediate right, one near right" 0 1
161 $ commit 17 "expand" 12 16
161 $ commit 17 "expand" 12 16
162 $ commit 18 "merge two known; two far left" 1 15
162 $ commit 18 "merge two known; two far left" 1 15
163 $ commit 19 "expand" 15 17
163 $ commit 19 "expand" 15 17
164 $ commit 20 "merge two known; two far right" 0 18
164 $ commit 20 "merge two known; two far right" 0 18
165 $ commit 21 "expand" 19 20
165 $ commit 21 "expand" 19 20
166 $ commit 22 "merge two known; one far left, one far right" 18 21
166 $ commit 22 "merge two known; one far left, one far right" 18 21
167 $ commit 23 "merge one known; immediate left" 1 22
167 $ commit 23 "merge one known; immediate left" 1 22
168 $ commit 24 "merge one known; immediate right" 0 23
168 $ commit 24 "merge one known; immediate right" 0 23
169 $ commit 25 "merge one known; far left" 21 24
169 $ commit 25 "merge one known; far left" 21 24
170 $ commit 26 "merge one known; far right" 18 25
170 $ commit 26 "merge one known; far right" 18 25
171 $ commit 27 "collapse" 21
171 $ commit 27 "collapse" 21
172 $ commit 28 "merge zero known" 1 26
172 $ commit 28 "merge zero known" 1 26
173 $ commit 29 "regular commit" 0
173 $ commit 29 "regular commit" 0
174 $ commit 30 "expand" 28 29
174 $ commit 30 "expand" 28 29
175 $ commit 31 "expand" 21 30
175 $ commit 31 "expand" 21 30
176 $ commit 32 "expand" 27 31
176 $ commit 32 "expand" 27 31
177 $ commit 33 "head" 18
177 $ commit 33 "head" 18
178 $ commit 34 "head" 32
178 $ commit 34 "head" 32
179
179
180 The extension should not turn on unless we're in UTF-8.
180 The extension should not turn on unless we're in UTF-8.
181
181
182 $ HGENCODING=latin1 hg log -G -q
182 $ HGENCODING=latin1 hg log -G -q
183 beautifygraph: unsupported encoding, UTF-8 required
183 beautifygraph: unsupported encoding, UTF-8 required
184 @ 34:fea3ac5810e0
184 @ 34:fea3ac5810e0
185 |
185 |
186 | o 33:68608f5145f9
186 | o 33:68608f5145f9
187 | |
187 | |
188 o | 32:d06dffa21a31
188 o | 32:d06dffa21a31
189 |\ \
189 |\ \
190 | o \ 31:621d83e11f67
190 | o \ 31:621d83e11f67
191 | |\ \
191 | |\ \
192 | | o \ 30:6e11cd4b648f
192 | | o \ 30:6e11cd4b648f
193 | | |\ \
193 | | |\ \
194 | | | o | 29:cd9bb2be7593
194 | | | o | 29:cd9bb2be7593
195 | | | | |
195 | | | | |
196 | | o | | 28:44ecd0b9ae99
196 | | o | | 28:44ecd0b9ae99
197 | | |\ \ \
197 | | |\ \ \
198 o | | | | | 27:886ed638191b
198 o | | | | | 27:886ed638191b
199 |/ / / / /
199 |/ / / / /
200 | | o---+ 26:7f25b6c2f0b9
200 | | o---+ 26:7f25b6c2f0b9
201 | | | | |
201 | | | | |
202 +---o | | 25:91da8ed57247
202 +---o | | 25:91da8ed57247
203 | | | | |
203 | | | | |
204 | | o | | 24:a9c19a3d96b7
204 | | o | | 24:a9c19a3d96b7
205 | | |\| |
205 | | |\| |
206 | | o | | 23:a01cddf0766d
206 | | o | | 23:a01cddf0766d
207 | |/| | |
207 | |/| | |
208 +---o---+ 22:e0d9cccacb5d
208 +---o---+ 22:e0d9cccacb5d
209 | | / /
209 | | / /
210 o | | | 21:d42a756af44d
210 o | | | 21:d42a756af44d
211 |\ \ \ \
211 |\ \ \ \
212 | o---+-+ 20:d30ed6450e32
212 | o---+-+ 20:d30ed6450e32
213 | / / /
213 | / / /
214 o | | | 19:31ddc2c1573b
214 o | | | 19:31ddc2c1573b
215 |\ \ \ \
215 |\ \ \ \
216 +---+---o 18:1aa84d96232a
216 +---+---o 18:1aa84d96232a
217 | | | |
217 | | | |
218 | o | | 17:44765d7c06e0
218 | o | | 17:44765d7c06e0
219 | |\ \ \
219 | |\ \ \
220 | | o---+ 16:3677d192927d
220 | | o---+ 16:3677d192927d
221 | | |/ /
221 | | |/ /
222 o | | | 15:1dda3f72782d
222 o | | | 15:1dda3f72782d
223 |\ \ \ \
223 |\ \ \ \
224 | o-----+ 14:8eac370358ef
224 | o-----+ 14:8eac370358ef
225 | |/ / /
225 | |/ / /
226 o | | | 13:22d8966a97e3
226 o | | | 13:22d8966a97e3
227 |\ \ \ \
227 |\ \ \ \
228 +---o | | 12:86b91144a6e9
228 +---o | | 12:86b91144a6e9
229 | | |/ /
229 | | |/ /
230 | o | | 11:832d76e6bdf2
230 | o | | 11:832d76e6bdf2
231 | |\ \ \
231 | |\ \ \
232 | | o---+ 10:74c64d036d72
232 | | o---+ 10:74c64d036d72
233 | |/ / /
233 | |/ / /
234 o | | | 9:7010c0af0a35
234 o | | | 9:7010c0af0a35
235 |\ \ \ \
235 |\ \ \ \
236 | o-----+ 8:7a0b11f71937
236 | o-----+ 8:7a0b11f71937
237 |/ / / /
237 |/ / / /
238 o | | | 7:b632bb1b1224
238 o | | | 7:b632bb1b1224
239 |\ \ \ \
239 |\ \ \ \
240 +---o | | 6:b105a072e251
240 +---o | | 6:b105a072e251
241 | |/ / /
241 | |/ / /
242 | o | | 5:4409d547b708
242 | o | | 5:4409d547b708
243 | |\ \ \
243 | |\ \ \
244 | | o | | 4:26a8bac39d9f
244 | | o | | 4:26a8bac39d9f
245 | |/|/ /
245 | |/|/ /
246 | o / / 3:27eef8ed80b4
246 | o / / 3:27eef8ed80b4
247 |/ / /
247 |/ / /
248 o / / 2:3d9a33b8d1e1
248 o / / 2:3d9a33b8d1e1
249 |/ /
249 |/ /
250 o / 1:6db2ef61d156
250 o / 1:6db2ef61d156
251 |/
251 |/
252 o 0:e6eb3150255d
252 o 0:e6eb3150255d
253
253
254
254
255 The extension should not turn on if we're using wide text.
255 The extension should not turn on if we're using wide text.
256
256
257 $ HGENCODINGAMBIGUOUS=wide hg log -G -q
257 $ HGENCODINGAMBIGUOUS=wide hg log -G -q
258 beautifygraph: unsupported terminal settings, monospace narrow text required
258 beautifygraph: unsupported terminal settings, monospace narrow text required
259 @ 34:fea3ac5810e0
259 @ 34:fea3ac5810e0
260 |
260 |
261 | o 33:68608f5145f9
261 | o 33:68608f5145f9
262 | |
262 | |
263 o | 32:d06dffa21a31
263 o | 32:d06dffa21a31
264 |\ \
264 |\ \
265 | o \ 31:621d83e11f67
265 | o \ 31:621d83e11f67
266 | |\ \
266 | |\ \
267 | | o \ 30:6e11cd4b648f
267 | | o \ 30:6e11cd4b648f
268 | | |\ \
268 | | |\ \
269 | | | o | 29:cd9bb2be7593
269 | | | o | 29:cd9bb2be7593
270 | | | | |
270 | | | | |
271 | | o | | 28:44ecd0b9ae99
271 | | o | | 28:44ecd0b9ae99
272 | | |\ \ \
272 | | |\ \ \
273 o | | | | | 27:886ed638191b
273 o | | | | | 27:886ed638191b
274 |/ / / / /
274 |/ / / / /
275 | | o---+ 26:7f25b6c2f0b9
275 | | o---+ 26:7f25b6c2f0b9
276 | | | | |
276 | | | | |
277 +---o | | 25:91da8ed57247
277 +---o | | 25:91da8ed57247
278 | | | | |
278 | | | | |
279 | | o | | 24:a9c19a3d96b7
279 | | o | | 24:a9c19a3d96b7
280 | | |\| |
280 | | |\| |
281 | | o | | 23:a01cddf0766d
281 | | o | | 23:a01cddf0766d
282 | |/| | |
282 | |/| | |
283 +---o---+ 22:e0d9cccacb5d
283 +---o---+ 22:e0d9cccacb5d
284 | | / /
284 | | / /
285 o | | | 21:d42a756af44d
285 o | | | 21:d42a756af44d
286 |\ \ \ \
286 |\ \ \ \
287 | o---+-+ 20:d30ed6450e32
287 | o---+-+ 20:d30ed6450e32
288 | / / /
288 | / / /
289 o | | | 19:31ddc2c1573b
289 o | | | 19:31ddc2c1573b
290 |\ \ \ \
290 |\ \ \ \
291 +---+---o 18:1aa84d96232a
291 +---+---o 18:1aa84d96232a
292 | | | |
292 | | | |
293 | o | | 17:44765d7c06e0
293 | o | | 17:44765d7c06e0
294 | |\ \ \
294 | |\ \ \
295 | | o---+ 16:3677d192927d
295 | | o---+ 16:3677d192927d
296 | | |/ /
296 | | |/ /
297 o | | | 15:1dda3f72782d
297 o | | | 15:1dda3f72782d
298 |\ \ \ \
298 |\ \ \ \
299 | o-----+ 14:8eac370358ef
299 | o-----+ 14:8eac370358ef
300 | |/ / /
300 | |/ / /
301 o | | | 13:22d8966a97e3
301 o | | | 13:22d8966a97e3
302 |\ \ \ \
302 |\ \ \ \
303 +---o | | 12:86b91144a6e9
303 +---o | | 12:86b91144a6e9
304 | | |/ /
304 | | |/ /
305 | o | | 11:832d76e6bdf2
305 | o | | 11:832d76e6bdf2
306 | |\ \ \
306 | |\ \ \
307 | | o---+ 10:74c64d036d72
307 | | o---+ 10:74c64d036d72
308 | |/ / /
308 | |/ / /
309 o | | | 9:7010c0af0a35
309 o | | | 9:7010c0af0a35
310 |\ \ \ \
310 |\ \ \ \
311 | o-----+ 8:7a0b11f71937
311 | o-----+ 8:7a0b11f71937
312 |/ / / /
312 |/ / / /
313 o | | | 7:b632bb1b1224
313 o | | | 7:b632bb1b1224
314 |\ \ \ \
314 |\ \ \ \
315 +---o | | 6:b105a072e251
315 +---o | | 6:b105a072e251
316 | |/ / /
316 | |/ / /
317 | o | | 5:4409d547b708
317 | o | | 5:4409d547b708
318 | |\ \ \
318 | |\ \ \
319 | | o | | 4:26a8bac39d9f
319 | | o | | 4:26a8bac39d9f
320 | |/|/ /
320 | |/|/ /
321 | o / / 3:27eef8ed80b4
321 | o / / 3:27eef8ed80b4
322 |/ / /
322 |/ / /
323 o / / 2:3d9a33b8d1e1
323 o / / 2:3d9a33b8d1e1
324 |/ /
324 |/ /
325 o / 1:6db2ef61d156
325 o / 1:6db2ef61d156
326 |/
326 |/
327 o 0:e6eb3150255d
327 o 0:e6eb3150255d
328
328
329
329
330 The rest of our tests will use the default narrow text UTF-8.
330 The rest of our tests will use the default narrow text UTF-8.
331
331
332 $ hg log -G -q
332 $ hg log -G -q
333 \xe2\x97\x8d 34:fea3ac5810e0 (esc)
333 \xe2\x97\x8d 34:fea3ac5810e0 (esc)
334 \xe2\x94\x82 (esc)
334 \xe2\x94\x82 (esc)
335 \xe2\x94\x82 \xe2\x97\x8b 33:68608f5145f9 (esc)
335 \xe2\x94\x82 \xe2\x97\x8b 33:68608f5145f9 (esc)
336 \xe2\x94\x82 \xe2\x94\x82 (esc)
336 \xe2\x94\x82 \xe2\x94\x82 (esc)
337 \xe2\x97\x8b \xe2\x94\x82 32:d06dffa21a31 (esc)
337 \xe2\x97\x8b \xe2\x94\x82 32:d06dffa21a31 (esc)
338 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 (esc)
338 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 (esc)
339 \xe2\x94\x82 \xe2\x97\x8b \xe2\x95\xb2 31:621d83e11f67 (esc)
339 \xe2\x94\x82 \xe2\x97\x8b \xe2\x95\xb2 31:621d83e11f67 (esc)
340 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 (esc)
340 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 (esc)
341 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x95\xb2 30:6e11cd4b648f (esc)
341 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x95\xb2 30:6e11cd4b648f (esc)
342 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 (esc)
342 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 (esc)
343 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 29:cd9bb2be7593 (esc)
343 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 29:cd9bb2be7593 (esc)
344 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
344 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
345 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 28:44ecd0b9ae99 (esc)
345 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 28:44ecd0b9ae99 (esc)
346 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 (esc)
346 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 (esc)
347 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 27:886ed638191b (esc)
347 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 27:886ed638191b (esc)
348 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 (esc)
348 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 (esc)
349 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 26:7f25b6c2f0b9 (esc)
349 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 26:7f25b6c2f0b9 (esc)
350 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
350 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
351 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 25:91da8ed57247 (esc)
351 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 25:91da8ed57247 (esc)
352 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
352 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
353 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 24:a9c19a3d96b7 (esc)
353 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 24:a9c19a3d96b7 (esc)
354 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2\xe2\x94\x82 \xe2\x94\x82 (esc)
354 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2\xe2\x94\x82 \xe2\x94\x82 (esc)
355 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 23:a01cddf0766d (esc)
355 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 23:a01cddf0766d (esc)
356 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
356 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
357 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 22:e0d9cccacb5d (esc)
357 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 22:e0d9cccacb5d (esc)
358 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xb1 \xe2\x95\xb1 (esc)
358 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xb1 \xe2\x95\xb1 (esc)
359 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 21:d42a756af44d (esc)
359 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 21:d42a756af44d (esc)
360 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 (esc)
360 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 (esc)
361 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xbc\xe2\x94\x80\xe2\x94\xa4 20:d30ed6450e32 (esc)
361 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xbc\xe2\x94\x80\xe2\x94\xa4 20:d30ed6450e32 (esc)
362 \xe2\x94\x82 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 (esc)
362 \xe2\x94\x82 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 (esc)
363 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 19:31ddc2c1573b (esc)
363 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 19:31ddc2c1573b (esc)
364 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 (esc)
364 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 (esc)
365 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xbc\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b 18:1aa84d96232a (esc)
365 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xbc\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b 18:1aa84d96232a (esc)
366 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
366 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
367 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 17:44765d7c06e0 (esc)
367 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 17:44765d7c06e0 (esc)
368 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 (esc)
368 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 (esc)
369 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 16:3677d192927d (esc)
369 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 16:3677d192927d (esc)
370 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 (esc)
370 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 (esc)
371 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 15:1dda3f72782d (esc)
371 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 15:1dda3f72782d (esc)
372 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 (esc)
372 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 (esc)
373 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 14:8eac370358ef (esc)
373 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 14:8eac370358ef (esc)
374 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 (esc)
374 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 (esc)
375 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 13:22d8966a97e3 (esc)
375 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 13:22d8966a97e3 (esc)
376 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 (esc)
376 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 (esc)
377 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 12:86b91144a6e9 (esc)
377 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 12:86b91144a6e9 (esc)
378 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 (esc)
378 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 (esc)
379 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 11:832d76e6bdf2 (esc)
379 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 11:832d76e6bdf2 (esc)
380 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 (esc)
380 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 (esc)
381 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 10:74c64d036d72 (esc)
381 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 10:74c64d036d72 (esc)
382 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 (esc)
382 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 (esc)
383 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 9:7010c0af0a35 (esc)
383 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 9:7010c0af0a35 (esc)
384 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 (esc)
384 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 (esc)
385 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 8:7a0b11f71937 (esc)
385 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 8:7a0b11f71937 (esc)
386 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 (esc)
386 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 (esc)
387 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 7:b632bb1b1224 (esc)
387 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 7:b632bb1b1224 (esc)
388 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 (esc)
388 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 (esc)
389 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 6:b105a072e251 (esc)
389 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 6:b105a072e251 (esc)
390 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 (esc)
390 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 (esc)
391 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 5:4409d547b708 (esc)
391 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 5:4409d547b708 (esc)
392 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 (esc)
392 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 (esc)
393 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 4:26a8bac39d9f (esc)
393 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 4:26a8bac39d9f (esc)
394 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 (esc)
394 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 (esc)
395 \xe2\x94\x82 \xe2\x97\x8b \xe2\x95\xb1 \xe2\x95\xb1 3:27eef8ed80b4 (esc)
395 \xe2\x94\x82 \xe2\x97\x8b \xe2\x95\xb1 \xe2\x95\xb1 3:27eef8ed80b4 (esc)
396 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 (esc)
396 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 (esc)
397 \xe2\x97\x8b \xe2\x95\xb1 \xe2\x95\xb1 2:3d9a33b8d1e1 (esc)
397 \xe2\x97\x8b \xe2\x95\xb1 \xe2\x95\xb1 2:3d9a33b8d1e1 (esc)
398 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 (esc)
398 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 (esc)
399 \xe2\x97\x8b \xe2\x95\xb1 1:6db2ef61d156 (esc)
399 \xe2\x97\x8b \xe2\x95\xb1 1:6db2ef61d156 (esc)
400 \xe2\x94\x82\xe2\x95\xb1 (esc)
400 \xe2\x94\x82\xe2\x95\xb1 (esc)
401 \xe2\x97\x8b 0:e6eb3150255d (esc)
401 \xe2\x97\x8b 0:e6eb3150255d (esc)
402
402
403
403
404 $ hg log -G
404 $ hg log -G
405 \xe2\x97\x8d changeset: 34:fea3ac5810e0 (esc)
405 \xe2\x97\x8d changeset: 34:fea3ac5810e0 (esc)
406 \xe2\x94\x82 tag: tip (esc)
406 \xe2\x94\x82 tag: tip (esc)
407 \xe2\x94\x82 parent: 32:d06dffa21a31 (esc)
407 \xe2\x94\x82 parent: 32:d06dffa21a31 (esc)
408 \xe2\x94\x82 user: test (esc)
408 \xe2\x94\x82 user: test (esc)
409 \xe2\x94\x82 date: Thu Jan 01 00:00:34 1970 +0000 (esc)
409 \xe2\x94\x82 date: Thu Jan 01 00:00:34 1970 +0000 (esc)
410 \xe2\x94\x82 summary: (34) head (esc)
410 \xe2\x94\x82 summary: (34) head (esc)
411 \xe2\x94\x82 (esc)
411 \xe2\x94\x82 (esc)
412 \xe2\x94\x82 \xe2\x97\x8b changeset: 33:68608f5145f9 (esc)
412 \xe2\x94\x82 \xe2\x97\x8b changeset: 33:68608f5145f9 (esc)
413 \xe2\x94\x82 \xe2\x94\x82 parent: 18:1aa84d96232a (esc)
413 \xe2\x94\x82 \xe2\x94\x82 parent: 18:1aa84d96232a (esc)
414 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
414 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
415 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:33 1970 +0000 (esc)
415 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:33 1970 +0000 (esc)
416 \xe2\x94\x82 \xe2\x94\x82 summary: (33) head (esc)
416 \xe2\x94\x82 \xe2\x94\x82 summary: (33) head (esc)
417 \xe2\x94\x82 \xe2\x94\x82 (esc)
417 \xe2\x94\x82 \xe2\x94\x82 (esc)
418 \xe2\x97\x8b \xe2\x94\x82 changeset: 32:d06dffa21a31 (esc)
418 \xe2\x97\x8b \xe2\x94\x82 changeset: 32:d06dffa21a31 (esc)
419 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 27:886ed638191b (esc)
419 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 27:886ed638191b (esc)
420 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 31:621d83e11f67 (esc)
420 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 31:621d83e11f67 (esc)
421 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
421 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
422 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:32 1970 +0000 (esc)
422 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:32 1970 +0000 (esc)
423 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (32) expand (esc)
423 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (32) expand (esc)
424 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
424 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
425 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 changeset: 31:621d83e11f67 (esc)
425 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 changeset: 31:621d83e11f67 (esc)
426 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 21:d42a756af44d (esc)
426 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 21:d42a756af44d (esc)
427 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 30:6e11cd4b648f (esc)
427 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 30:6e11cd4b648f (esc)
428 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
428 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
429 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:31 1970 +0000 (esc)
429 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:31 1970 +0000 (esc)
430 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (31) expand (esc)
430 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (31) expand (esc)
431 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
431 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
432 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 changeset: 30:6e11cd4b648f (esc)
432 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 changeset: 30:6e11cd4b648f (esc)
433 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 28:44ecd0b9ae99 (esc)
433 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 28:44ecd0b9ae99 (esc)
434 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 29:cd9bb2be7593 (esc)
434 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 29:cd9bb2be7593 (esc)
435 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
435 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
436 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:30 1970 +0000 (esc)
436 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:30 1970 +0000 (esc)
437 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (30) expand (esc)
437 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (30) expand (esc)
438 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
438 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
439 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 changeset: 29:cd9bb2be7593 (esc)
439 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 changeset: 29:cd9bb2be7593 (esc)
440 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
440 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
441 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
441 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
442 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:29 1970 +0000 (esc)
442 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:29 1970 +0000 (esc)
443 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (29) regular commit (esc)
443 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (29) regular commit (esc)
444 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
444 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
445 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 28:44ecd0b9ae99 (esc)
445 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 28:44ecd0b9ae99 (esc)
446 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 1:6db2ef61d156 (esc)
446 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 1:6db2ef61d156 (esc)
447 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 26:7f25b6c2f0b9 (esc)
447 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 26:7f25b6c2f0b9 (esc)
448 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
448 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
449 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:28 1970 +0000 (esc)
449 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:28 1970 +0000 (esc)
450 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (28) merge zero known (esc)
450 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (28) merge zero known (esc)
451 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
451 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
452 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 27:886ed638191b (esc)
452 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 27:886ed638191b (esc)
453 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 21:d42a756af44d (esc)
453 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 21:d42a756af44d (esc)
454 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
454 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
455 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:27 1970 +0000 (esc)
455 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:27 1970 +0000 (esc)
456 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (27) collapse (esc)
456 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (27) collapse (esc)
457 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
457 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
458 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 26:7f25b6c2f0b9 (esc)
458 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 26:7f25b6c2f0b9 (esc)
459 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 18:1aa84d96232a (esc)
459 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 18:1aa84d96232a (esc)
460 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 25:91da8ed57247 (esc)
460 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 25:91da8ed57247 (esc)
461 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
461 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
462 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:26 1970 +0000 (esc)
462 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:26 1970 +0000 (esc)
463 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (26) merge one known; far right (esc)
463 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (26) merge one known; far right (esc)
464 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
464 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
465 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 25:91da8ed57247 (esc)
465 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 25:91da8ed57247 (esc)
466 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 21:d42a756af44d (esc)
466 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 21:d42a756af44d (esc)
467 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 24:a9c19a3d96b7 (esc)
467 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 24:a9c19a3d96b7 (esc)
468 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
468 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
469 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:25 1970 +0000 (esc)
469 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:25 1970 +0000 (esc)
470 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (25) merge one known; far left (esc)
470 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (25) merge one known; far left (esc)
471 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
471 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
472 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 24:a9c19a3d96b7 (esc)
472 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 24:a9c19a3d96b7 (esc)
473 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2\xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
473 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2\xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
474 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 23:a01cddf0766d (esc)
474 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 23:a01cddf0766d (esc)
475 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
475 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
476 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:24 1970 +0000 (esc)
476 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:24 1970 +0000 (esc)
477 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (24) merge one known; immediate right (esc)
477 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (24) merge one known; immediate right (esc)
478 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
478 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
479 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 23:a01cddf0766d (esc)
479 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 23:a01cddf0766d (esc)
480 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 1:6db2ef61d156 (esc)
480 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 1:6db2ef61d156 (esc)
481 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 22:e0d9cccacb5d (esc)
481 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 22:e0d9cccacb5d (esc)
482 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
482 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
483 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:23 1970 +0000 (esc)
483 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:23 1970 +0000 (esc)
484 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (23) merge one known; immediate left (esc)
484 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (23) merge one known; immediate left (esc)
485 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
485 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
486 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 22:e0d9cccacb5d (esc)
486 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 22:e0d9cccacb5d (esc)
487 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 18:1aa84d96232a (esc)
487 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 18:1aa84d96232a (esc)
488 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xb1 \xe2\x95\xb1 parent: 21:d42a756af44d (esc)
488 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xb1 \xe2\x95\xb1 parent: 21:d42a756af44d (esc)
489 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
489 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
490 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:22 1970 +0000 (esc)
490 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:22 1970 +0000 (esc)
491 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (22) merge two known; one far left, one far right (esc)
491 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (22) merge two known; one far left, one far right (esc)
492 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
492 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
493 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 21:d42a756af44d (esc)
493 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 21:d42a756af44d (esc)
494 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 19:31ddc2c1573b (esc)
494 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 19:31ddc2c1573b (esc)
495 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 20:d30ed6450e32 (esc)
495 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 20:d30ed6450e32 (esc)
496 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
496 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
497 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:21 1970 +0000 (esc)
497 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:21 1970 +0000 (esc)
498 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (21) expand (esc)
498 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (21) expand (esc)
499 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
499 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
500 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xbc\xe2\x94\x80\xe2\x94\xa4 changeset: 20:d30ed6450e32 (esc)
500 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xbc\xe2\x94\x80\xe2\x94\xa4 changeset: 20:d30ed6450e32 (esc)
501 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
501 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
502 \xe2\x94\x82 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 18:1aa84d96232a (esc)
502 \xe2\x94\x82 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 18:1aa84d96232a (esc)
503 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
503 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
504 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:20 1970 +0000 (esc)
504 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:20 1970 +0000 (esc)
505 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (20) merge two known; two far right (esc)
505 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (20) merge two known; two far right (esc)
506 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
506 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
507 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 19:31ddc2c1573b (esc)
507 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 19:31ddc2c1573b (esc)
508 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 15:1dda3f72782d (esc)
508 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 15:1dda3f72782d (esc)
509 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 17:44765d7c06e0 (esc)
509 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 17:44765d7c06e0 (esc)
510 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
510 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
511 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:19 1970 +0000 (esc)
511 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:19 1970 +0000 (esc)
512 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (19) expand (esc)
512 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (19) expand (esc)
513 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
513 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
514 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xbc\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b changeset: 18:1aa84d96232a (esc)
514 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xbc\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b changeset: 18:1aa84d96232a (esc)
515 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 1:6db2ef61d156 (esc)
515 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 1:6db2ef61d156 (esc)
516 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 15:1dda3f72782d (esc)
516 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 15:1dda3f72782d (esc)
517 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
517 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
518 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:18 1970 +0000 (esc)
518 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:18 1970 +0000 (esc)
519 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (18) merge two known; two far left (esc)
519 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (18) merge two known; two far left (esc)
520 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
520 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
521 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 17:44765d7c06e0 (esc)
521 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 17:44765d7c06e0 (esc)
522 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 12:86b91144a6e9 (esc)
522 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 12:86b91144a6e9 (esc)
523 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 16:3677d192927d (esc)
523 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 16:3677d192927d (esc)
524 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
524 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
525 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:17 1970 +0000 (esc)
525 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:17 1970 +0000 (esc)
526 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (17) expand (esc)
526 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (17) expand (esc)
527 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
527 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
528 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 16:3677d192927d (esc)
528 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 16:3677d192927d (esc)
529 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
529 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
530 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 parent: 1:6db2ef61d156 (esc)
530 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 parent: 1:6db2ef61d156 (esc)
531 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
531 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
532 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:16 1970 +0000 (esc)
532 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:16 1970 +0000 (esc)
533 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (16) merge two known; one immediate right, one near right (esc)
533 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (16) merge two known; one immediate right, one near right (esc)
534 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
534 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
535 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 15:1dda3f72782d (esc)
535 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 15:1dda3f72782d (esc)
536 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 13:22d8966a97e3 (esc)
536 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 13:22d8966a97e3 (esc)
537 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 14:8eac370358ef (esc)
537 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 14:8eac370358ef (esc)
538 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
538 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
539 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:15 1970 +0000 (esc)
539 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:15 1970 +0000 (esc)
540 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (15) expand (esc)
540 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (15) expand (esc)
541 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
541 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
542 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 14:8eac370358ef (esc)
542 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 14:8eac370358ef (esc)
543 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
543 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
544 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 12:86b91144a6e9 (esc)
544 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 12:86b91144a6e9 (esc)
545 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
545 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
546 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:14 1970 +0000 (esc)
546 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:14 1970 +0000 (esc)
547 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (14) merge two known; one immediate right, one far right (esc)
547 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (14) merge two known; one immediate right, one far right (esc)
548 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
548 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
549 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 13:22d8966a97e3 (esc)
549 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 13:22d8966a97e3 (esc)
550 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 9:7010c0af0a35 (esc)
550 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 9:7010c0af0a35 (esc)
551 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 11:832d76e6bdf2 (esc)
551 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 11:832d76e6bdf2 (esc)
552 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
552 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
553 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:13 1970 +0000 (esc)
553 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:13 1970 +0000 (esc)
554 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (13) expand (esc)
554 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (13) expand (esc)
555 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
555 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
556 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 12:86b91144a6e9 (esc)
556 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 12:86b91144a6e9 (esc)
557 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 parent: 1:6db2ef61d156 (esc)
557 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 parent: 1:6db2ef61d156 (esc)
558 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 9:7010c0af0a35 (esc)
558 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 9:7010c0af0a35 (esc)
559 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
559 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
560 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:12 1970 +0000 (esc)
560 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:12 1970 +0000 (esc)
561 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (12) merge two known; one immediate right, one far left (esc)
561 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (12) merge two known; one immediate right, one far left (esc)
562 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
562 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
563 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 11:832d76e6bdf2 (esc)
563 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 11:832d76e6bdf2 (esc)
564 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 6:b105a072e251 (esc)
564 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 6:b105a072e251 (esc)
565 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 10:74c64d036d72 (esc)
565 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 10:74c64d036d72 (esc)
566 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
566 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
567 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:11 1970 +0000 (esc)
567 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:11 1970 +0000 (esc)
568 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (11) expand (esc)
568 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (11) expand (esc)
569 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
569 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
570 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 10:74c64d036d72 (esc)
570 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 10:74c64d036d72 (esc)
571 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
571 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
572 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 6:b105a072e251 (esc)
572 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 6:b105a072e251 (esc)
573 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
573 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
574 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:10 1970 +0000 (esc)
574 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:10 1970 +0000 (esc)
575 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (10) merge two known; one immediate left, one near right (esc)
575 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (10) merge two known; one immediate left, one near right (esc)
576 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
576 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
577 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 9:7010c0af0a35 (esc)
577 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 9:7010c0af0a35 (esc)
578 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 7:b632bb1b1224 (esc)
578 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 7:b632bb1b1224 (esc)
579 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 8:7a0b11f71937 (esc)
579 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 8:7a0b11f71937 (esc)
580 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
580 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
581 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:09 1970 +0000 (esc)
581 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:09 1970 +0000 (esc)
582 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (9) expand (esc)
582 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (9) expand (esc)
583 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
583 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
584 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 8:7a0b11f71937 (esc)
584 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 8:7a0b11f71937 (esc)
585 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
585 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
586 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 7:b632bb1b1224 (esc)
586 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 7:b632bb1b1224 (esc)
587 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
587 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
588 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:08 1970 +0000 (esc)
588 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:08 1970 +0000 (esc)
589 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (8) merge two known; one immediate left, one far right (esc)
589 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (8) merge two known; one immediate left, one far right (esc)
590 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
590 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
591 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 7:b632bb1b1224 (esc)
591 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 7:b632bb1b1224 (esc)
592 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 2:3d9a33b8d1e1 (esc)
592 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 2:3d9a33b8d1e1 (esc)
593 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 5:4409d547b708 (esc)
593 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 5:4409d547b708 (esc)
594 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
594 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
595 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:07 1970 +0000 (esc)
595 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:07 1970 +0000 (esc)
596 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (7) expand (esc)
596 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (7) expand (esc)
597 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
597 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
598 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 6:b105a072e251 (esc)
598 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 6:b105a072e251 (esc)
599 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 2:3d9a33b8d1e1 (esc)
599 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 2:3d9a33b8d1e1 (esc)
600 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 5:4409d547b708 (esc)
600 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 5:4409d547b708 (esc)
601 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
601 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
602 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:06 1970 +0000 (esc)
602 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:06 1970 +0000 (esc)
603 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (6) merge two known; one immediate left, one far left (esc)
603 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (6) merge two known; one immediate left, one far left (esc)
604 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
604 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
605 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 5:4409d547b708 (esc)
605 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 5:4409d547b708 (esc)
606 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 3:27eef8ed80b4 (esc)
606 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 3:27eef8ed80b4 (esc)
607 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 4:26a8bac39d9f (esc)
607 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 4:26a8bac39d9f (esc)
608 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
608 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
609 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:05 1970 +0000 (esc)
609 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:05 1970 +0000 (esc)
610 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (5) expand (esc)
610 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (5) expand (esc)
611 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
611 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
612 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 4:26a8bac39d9f (esc)
612 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 4:26a8bac39d9f (esc)
613 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 parent: 1:6db2ef61d156 (esc)
613 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 parent: 1:6db2ef61d156 (esc)
614 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 3:27eef8ed80b4 (esc)
614 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 3:27eef8ed80b4 (esc)
615 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
615 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
616 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:04 1970 +0000 (esc)
616 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:04 1970 +0000 (esc)
617 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (4) merge two known; one immediate left, one immediate right (esc)
617 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (4) merge two known; one immediate left, one immediate right (esc)
618 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
618 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
619 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 3:27eef8ed80b4 (esc)
619 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 3:27eef8ed80b4 (esc)
620 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 user: test (esc)
620 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 user: test (esc)
621 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:03 1970 +0000 (esc)
621 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:03 1970 +0000 (esc)
622 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (3) collapse (esc)
622 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (3) collapse (esc)
623 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
623 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
624 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 2:3d9a33b8d1e1 (esc)
624 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 2:3d9a33b8d1e1 (esc)
625 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 user: test (esc)
625 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 user: test (esc)
626 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:02 1970 +0000 (esc)
626 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:02 1970 +0000 (esc)
627 \xe2\x94\x82 \xe2\x94\x82 summary: (2) collapse (esc)
627 \xe2\x94\x82 \xe2\x94\x82 summary: (2) collapse (esc)
628 \xe2\x94\x82 \xe2\x94\x82 (esc)
628 \xe2\x94\x82 \xe2\x94\x82 (esc)
629 \xe2\x97\x8b \xe2\x94\x82 changeset: 1:6db2ef61d156 (esc)
629 \xe2\x97\x8b \xe2\x94\x82 changeset: 1:6db2ef61d156 (esc)
630 \xe2\x94\x82\xe2\x95\xb1 user: test (esc)
630 \xe2\x94\x82\xe2\x95\xb1 user: test (esc)
631 \xe2\x94\x82 date: Thu Jan 01 00:00:01 1970 +0000 (esc)
631 \xe2\x94\x82 date: Thu Jan 01 00:00:01 1970 +0000 (esc)
632 \xe2\x94\x82 summary: (1) collapse (esc)
632 \xe2\x94\x82 summary: (1) collapse (esc)
633 \xe2\x94\x82 (esc)
633 \xe2\x94\x82 (esc)
634 \xe2\x97\x8b changeset: 0:e6eb3150255d (esc)
634 \xe2\x97\x8b changeset: 0:e6eb3150255d (esc)
635 user: test
635 user: test
636 date: Thu Jan 01 00:00:00 1970 +0000
636 date: Thu Jan 01 00:00:00 1970 +0000
637 summary: (0) root
637 summary: (0) root
638
638
639 File glog:
639 File glog:
640 $ hg log -G a
640 $ hg log -G a
641 \xe2\x97\x8d changeset: 34:fea3ac5810e0 (esc)
641 \xe2\x97\x8d changeset: 34:fea3ac5810e0 (esc)
642 \xe2\x94\x82 tag: tip (esc)
642 \xe2\x94\x82 tag: tip (esc)
643 \xe2\x94\x82 parent: 32:d06dffa21a31 (esc)
643 \xe2\x94\x82 parent: 32:d06dffa21a31 (esc)
644 \xe2\x94\x82 user: test (esc)
644 \xe2\x94\x82 user: test (esc)
645 \xe2\x94\x82 date: Thu Jan 01 00:00:34 1970 +0000 (esc)
645 \xe2\x94\x82 date: Thu Jan 01 00:00:34 1970 +0000 (esc)
646 \xe2\x94\x82 summary: (34) head (esc)
646 \xe2\x94\x82 summary: (34) head (esc)
647 \xe2\x94\x82 (esc)
647 \xe2\x94\x82 (esc)
648 \xe2\x94\x82 \xe2\x97\x8b changeset: 33:68608f5145f9 (esc)
648 \xe2\x94\x82 \xe2\x97\x8b changeset: 33:68608f5145f9 (esc)
649 \xe2\x94\x82 \xe2\x94\x82 parent: 18:1aa84d96232a (esc)
649 \xe2\x94\x82 \xe2\x94\x82 parent: 18:1aa84d96232a (esc)
650 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
650 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
651 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:33 1970 +0000 (esc)
651 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:33 1970 +0000 (esc)
652 \xe2\x94\x82 \xe2\x94\x82 summary: (33) head (esc)
652 \xe2\x94\x82 \xe2\x94\x82 summary: (33) head (esc)
653 \xe2\x94\x82 \xe2\x94\x82 (esc)
653 \xe2\x94\x82 \xe2\x94\x82 (esc)
654 \xe2\x97\x8b \xe2\x94\x82 changeset: 32:d06dffa21a31 (esc)
654 \xe2\x97\x8b \xe2\x94\x82 changeset: 32:d06dffa21a31 (esc)
655 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 27:886ed638191b (esc)
655 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 27:886ed638191b (esc)
656 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 31:621d83e11f67 (esc)
656 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 31:621d83e11f67 (esc)
657 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
657 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
658 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:32 1970 +0000 (esc)
658 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:32 1970 +0000 (esc)
659 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (32) expand (esc)
659 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (32) expand (esc)
660 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
660 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
661 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 changeset: 31:621d83e11f67 (esc)
661 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 changeset: 31:621d83e11f67 (esc)
662 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 21:d42a756af44d (esc)
662 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 21:d42a756af44d (esc)
663 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 30:6e11cd4b648f (esc)
663 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 30:6e11cd4b648f (esc)
664 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
664 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
665 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:31 1970 +0000 (esc)
665 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:31 1970 +0000 (esc)
666 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (31) expand (esc)
666 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (31) expand (esc)
667 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
667 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
668 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 changeset: 30:6e11cd4b648f (esc)
668 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 changeset: 30:6e11cd4b648f (esc)
669 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 28:44ecd0b9ae99 (esc)
669 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 28:44ecd0b9ae99 (esc)
670 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 29:cd9bb2be7593 (esc)
670 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 29:cd9bb2be7593 (esc)
671 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
671 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
672 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:30 1970 +0000 (esc)
672 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:30 1970 +0000 (esc)
673 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (30) expand (esc)
673 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (30) expand (esc)
674 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
674 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
675 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 changeset: 29:cd9bb2be7593 (esc)
675 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 changeset: 29:cd9bb2be7593 (esc)
676 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
676 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
677 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
677 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
678 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:29 1970 +0000 (esc)
678 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:29 1970 +0000 (esc)
679 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (29) regular commit (esc)
679 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (29) regular commit (esc)
680 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
680 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
681 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 28:44ecd0b9ae99 (esc)
681 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 28:44ecd0b9ae99 (esc)
682 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 1:6db2ef61d156 (esc)
682 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 1:6db2ef61d156 (esc)
683 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 26:7f25b6c2f0b9 (esc)
683 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 26:7f25b6c2f0b9 (esc)
684 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
684 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
685 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:28 1970 +0000 (esc)
685 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:28 1970 +0000 (esc)
686 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (28) merge zero known (esc)
686 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (28) merge zero known (esc)
687 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
687 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
688 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 27:886ed638191b (esc)
688 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 27:886ed638191b (esc)
689 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 21:d42a756af44d (esc)
689 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 21:d42a756af44d (esc)
690 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
690 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
691 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:27 1970 +0000 (esc)
691 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:27 1970 +0000 (esc)
692 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (27) collapse (esc)
692 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (27) collapse (esc)
693 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
693 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
694 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 26:7f25b6c2f0b9 (esc)
694 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 26:7f25b6c2f0b9 (esc)
695 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 18:1aa84d96232a (esc)
695 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 18:1aa84d96232a (esc)
696 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 25:91da8ed57247 (esc)
696 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 25:91da8ed57247 (esc)
697 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
697 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
698 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:26 1970 +0000 (esc)
698 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:26 1970 +0000 (esc)
699 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (26) merge one known; far right (esc)
699 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (26) merge one known; far right (esc)
700 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
700 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
701 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 25:91da8ed57247 (esc)
701 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 25:91da8ed57247 (esc)
702 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 21:d42a756af44d (esc)
702 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 21:d42a756af44d (esc)
703 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 24:a9c19a3d96b7 (esc)
703 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 24:a9c19a3d96b7 (esc)
704 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
704 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
705 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:25 1970 +0000 (esc)
705 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:25 1970 +0000 (esc)
706 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (25) merge one known; far left (esc)
706 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (25) merge one known; far left (esc)
707 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
707 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
708 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 24:a9c19a3d96b7 (esc)
708 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 24:a9c19a3d96b7 (esc)
709 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2\xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
709 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2\xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
710 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 23:a01cddf0766d (esc)
710 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 23:a01cddf0766d (esc)
711 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
711 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
712 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:24 1970 +0000 (esc)
712 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:24 1970 +0000 (esc)
713 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (24) merge one known; immediate right (esc)
713 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (24) merge one known; immediate right (esc)
714 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
714 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
715 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 23:a01cddf0766d (esc)
715 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 23:a01cddf0766d (esc)
716 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 1:6db2ef61d156 (esc)
716 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 1:6db2ef61d156 (esc)
717 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 22:e0d9cccacb5d (esc)
717 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 22:e0d9cccacb5d (esc)
718 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
718 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
719 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:23 1970 +0000 (esc)
719 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:23 1970 +0000 (esc)
720 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (23) merge one known; immediate left (esc)
720 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (23) merge one known; immediate left (esc)
721 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
721 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
722 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 22:e0d9cccacb5d (esc)
722 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 22:e0d9cccacb5d (esc)
723 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 18:1aa84d96232a (esc)
723 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 18:1aa84d96232a (esc)
724 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xb1 \xe2\x95\xb1 parent: 21:d42a756af44d (esc)
724 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xb1 \xe2\x95\xb1 parent: 21:d42a756af44d (esc)
725 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
725 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
726 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:22 1970 +0000 (esc)
726 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:22 1970 +0000 (esc)
727 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (22) merge two known; one far left, one far right (esc)
727 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (22) merge two known; one far left, one far right (esc)
728 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
728 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
729 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 21:d42a756af44d (esc)
729 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 21:d42a756af44d (esc)
730 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 19:31ddc2c1573b (esc)
730 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 19:31ddc2c1573b (esc)
731 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 20:d30ed6450e32 (esc)
731 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 20:d30ed6450e32 (esc)
732 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
732 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
733 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:21 1970 +0000 (esc)
733 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:21 1970 +0000 (esc)
734 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (21) expand (esc)
734 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (21) expand (esc)
735 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
735 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
736 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xbc\xe2\x94\x80\xe2\x94\xa4 changeset: 20:d30ed6450e32 (esc)
736 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xbc\xe2\x94\x80\xe2\x94\xa4 changeset: 20:d30ed6450e32 (esc)
737 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
737 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
738 \xe2\x94\x82 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 18:1aa84d96232a (esc)
738 \xe2\x94\x82 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 18:1aa84d96232a (esc)
739 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
739 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
740 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:20 1970 +0000 (esc)
740 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:20 1970 +0000 (esc)
741 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (20) merge two known; two far right (esc)
741 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (20) merge two known; two far right (esc)
742 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
742 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
743 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 19:31ddc2c1573b (esc)
743 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 19:31ddc2c1573b (esc)
744 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 15:1dda3f72782d (esc)
744 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 15:1dda3f72782d (esc)
745 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 17:44765d7c06e0 (esc)
745 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 17:44765d7c06e0 (esc)
746 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
746 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
747 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:19 1970 +0000 (esc)
747 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:19 1970 +0000 (esc)
748 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (19) expand (esc)
748 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (19) expand (esc)
749 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
749 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
750 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xbc\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b changeset: 18:1aa84d96232a (esc)
750 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xbc\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b changeset: 18:1aa84d96232a (esc)
751 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 1:6db2ef61d156 (esc)
751 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 1:6db2ef61d156 (esc)
752 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 15:1dda3f72782d (esc)
752 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 15:1dda3f72782d (esc)
753 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
753 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
754 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:18 1970 +0000 (esc)
754 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:18 1970 +0000 (esc)
755 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (18) merge two known; two far left (esc)
755 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (18) merge two known; two far left (esc)
756 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
756 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
757 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 17:44765d7c06e0 (esc)
757 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 17:44765d7c06e0 (esc)
758 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 12:86b91144a6e9 (esc)
758 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 12:86b91144a6e9 (esc)
759 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 16:3677d192927d (esc)
759 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 16:3677d192927d (esc)
760 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
760 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
761 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:17 1970 +0000 (esc)
761 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:17 1970 +0000 (esc)
762 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (17) expand (esc)
762 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (17) expand (esc)
763 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
763 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
764 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 16:3677d192927d (esc)
764 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 16:3677d192927d (esc)
765 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
765 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
766 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 parent: 1:6db2ef61d156 (esc)
766 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 parent: 1:6db2ef61d156 (esc)
767 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
767 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
768 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:16 1970 +0000 (esc)
768 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:16 1970 +0000 (esc)
769 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (16) merge two known; one immediate right, one near right (esc)
769 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (16) merge two known; one immediate right, one near right (esc)
770 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
770 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
771 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 15:1dda3f72782d (esc)
771 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 15:1dda3f72782d (esc)
772 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 13:22d8966a97e3 (esc)
772 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 13:22d8966a97e3 (esc)
773 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 14:8eac370358ef (esc)
773 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 14:8eac370358ef (esc)
774 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
774 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
775 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:15 1970 +0000 (esc)
775 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:15 1970 +0000 (esc)
776 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (15) expand (esc)
776 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (15) expand (esc)
777 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
777 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
778 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 14:8eac370358ef (esc)
778 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 14:8eac370358ef (esc)
779 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
779 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
780 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 12:86b91144a6e9 (esc)
780 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 12:86b91144a6e9 (esc)
781 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
781 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
782 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:14 1970 +0000 (esc)
782 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:14 1970 +0000 (esc)
783 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (14) merge two known; one immediate right, one far right (esc)
783 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (14) merge two known; one immediate right, one far right (esc)
784 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
784 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
785 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 13:22d8966a97e3 (esc)
785 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 13:22d8966a97e3 (esc)
786 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 9:7010c0af0a35 (esc)
786 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 9:7010c0af0a35 (esc)
787 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 11:832d76e6bdf2 (esc)
787 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 11:832d76e6bdf2 (esc)
788 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
788 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
789 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:13 1970 +0000 (esc)
789 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:13 1970 +0000 (esc)
790 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (13) expand (esc)
790 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (13) expand (esc)
791 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
791 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
792 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 12:86b91144a6e9 (esc)
792 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 12:86b91144a6e9 (esc)
793 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 parent: 1:6db2ef61d156 (esc)
793 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 parent: 1:6db2ef61d156 (esc)
794 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 9:7010c0af0a35 (esc)
794 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 9:7010c0af0a35 (esc)
795 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
795 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
796 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:12 1970 +0000 (esc)
796 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:12 1970 +0000 (esc)
797 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (12) merge two known; one immediate right, one far left (esc)
797 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (12) merge two known; one immediate right, one far left (esc)
798 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
798 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
799 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 11:832d76e6bdf2 (esc)
799 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 11:832d76e6bdf2 (esc)
800 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 6:b105a072e251 (esc)
800 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 6:b105a072e251 (esc)
801 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 10:74c64d036d72 (esc)
801 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 10:74c64d036d72 (esc)
802 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
802 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
803 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:11 1970 +0000 (esc)
803 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:11 1970 +0000 (esc)
804 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (11) expand (esc)
804 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (11) expand (esc)
805 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
805 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
806 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 10:74c64d036d72 (esc)
806 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 10:74c64d036d72 (esc)
807 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
807 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
808 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 6:b105a072e251 (esc)
808 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 6:b105a072e251 (esc)
809 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
809 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
810 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:10 1970 +0000 (esc)
810 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:10 1970 +0000 (esc)
811 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (10) merge two known; one immediate left, one near right (esc)
811 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (10) merge two known; one immediate left, one near right (esc)
812 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
812 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
813 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 9:7010c0af0a35 (esc)
813 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 9:7010c0af0a35 (esc)
814 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 7:b632bb1b1224 (esc)
814 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 7:b632bb1b1224 (esc)
815 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 8:7a0b11f71937 (esc)
815 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 8:7a0b11f71937 (esc)
816 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
816 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
817 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:09 1970 +0000 (esc)
817 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:09 1970 +0000 (esc)
818 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (9) expand (esc)
818 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (9) expand (esc)
819 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
819 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
820 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 8:7a0b11f71937 (esc)
820 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 8:7a0b11f71937 (esc)
821 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
821 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
822 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 7:b632bb1b1224 (esc)
822 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 7:b632bb1b1224 (esc)
823 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
823 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
824 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:08 1970 +0000 (esc)
824 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:08 1970 +0000 (esc)
825 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (8) merge two known; one immediate left, one far right (esc)
825 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (8) merge two known; one immediate left, one far right (esc)
826 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
826 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
827 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 7:b632bb1b1224 (esc)
827 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 7:b632bb1b1224 (esc)
828 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 2:3d9a33b8d1e1 (esc)
828 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 2:3d9a33b8d1e1 (esc)
829 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 5:4409d547b708 (esc)
829 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 5:4409d547b708 (esc)
830 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
830 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
831 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:07 1970 +0000 (esc)
831 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:07 1970 +0000 (esc)
832 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (7) expand (esc)
832 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (7) expand (esc)
833 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
833 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
834 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 6:b105a072e251 (esc)
834 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 6:b105a072e251 (esc)
835 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 2:3d9a33b8d1e1 (esc)
835 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 2:3d9a33b8d1e1 (esc)
836 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 5:4409d547b708 (esc)
836 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 5:4409d547b708 (esc)
837 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
837 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
838 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:06 1970 +0000 (esc)
838 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:06 1970 +0000 (esc)
839 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (6) merge two known; one immediate left, one far left (esc)
839 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (6) merge two known; one immediate left, one far left (esc)
840 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
840 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
841 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 5:4409d547b708 (esc)
841 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 5:4409d547b708 (esc)
842 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 3:27eef8ed80b4 (esc)
842 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 3:27eef8ed80b4 (esc)
843 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 4:26a8bac39d9f (esc)
843 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 4:26a8bac39d9f (esc)
844 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
844 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
845 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:05 1970 +0000 (esc)
845 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:05 1970 +0000 (esc)
846 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (5) expand (esc)
846 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (5) expand (esc)
847 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
847 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
848 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 4:26a8bac39d9f (esc)
848 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 4:26a8bac39d9f (esc)
849 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 parent: 1:6db2ef61d156 (esc)
849 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 parent: 1:6db2ef61d156 (esc)
850 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 3:27eef8ed80b4 (esc)
850 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 3:27eef8ed80b4 (esc)
851 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
851 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
852 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:04 1970 +0000 (esc)
852 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:04 1970 +0000 (esc)
853 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (4) merge two known; one immediate left, one immediate right (esc)
853 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (4) merge two known; one immediate left, one immediate right (esc)
854 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
854 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
855 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 3:27eef8ed80b4 (esc)
855 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 3:27eef8ed80b4 (esc)
856 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 user: test (esc)
856 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 user: test (esc)
857 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:03 1970 +0000 (esc)
857 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:03 1970 +0000 (esc)
858 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (3) collapse (esc)
858 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (3) collapse (esc)
859 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
859 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
860 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 2:3d9a33b8d1e1 (esc)
860 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 2:3d9a33b8d1e1 (esc)
861 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 user: test (esc)
861 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 user: test (esc)
862 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:02 1970 +0000 (esc)
862 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:02 1970 +0000 (esc)
863 \xe2\x94\x82 \xe2\x94\x82 summary: (2) collapse (esc)
863 \xe2\x94\x82 \xe2\x94\x82 summary: (2) collapse (esc)
864 \xe2\x94\x82 \xe2\x94\x82 (esc)
864 \xe2\x94\x82 \xe2\x94\x82 (esc)
865 \xe2\x97\x8b \xe2\x94\x82 changeset: 1:6db2ef61d156 (esc)
865 \xe2\x97\x8b \xe2\x94\x82 changeset: 1:6db2ef61d156 (esc)
866 \xe2\x94\x82\xe2\x95\xb1 user: test (esc)
866 \xe2\x94\x82\xe2\x95\xb1 user: test (esc)
867 \xe2\x94\x82 date: Thu Jan 01 00:00:01 1970 +0000 (esc)
867 \xe2\x94\x82 date: Thu Jan 01 00:00:01 1970 +0000 (esc)
868 \xe2\x94\x82 summary: (1) collapse (esc)
868 \xe2\x94\x82 summary: (1) collapse (esc)
869 \xe2\x94\x82 (esc)
869 \xe2\x94\x82 (esc)
870 \xe2\x97\x8b changeset: 0:e6eb3150255d (esc)
870 \xe2\x97\x8b changeset: 0:e6eb3150255d (esc)
871 user: test
871 user: test
872 date: Thu Jan 01 00:00:00 1970 +0000
872 date: Thu Jan 01 00:00:00 1970 +0000
873 summary: (0) root
873 summary: (0) root
874
874
875 File glog per revset:
875 File glog per revset:
876
876
877 $ hg log -G -r 'file("a")'
877 $ hg log -G -r 'file("a")'
878 \xe2\x97\x8d changeset: 34:fea3ac5810e0 (esc)
878 \xe2\x97\x8d changeset: 34:fea3ac5810e0 (esc)
879 \xe2\x94\x82 tag: tip (esc)
879 \xe2\x94\x82 tag: tip (esc)
880 \xe2\x94\x82 parent: 32:d06dffa21a31 (esc)
880 \xe2\x94\x82 parent: 32:d06dffa21a31 (esc)
881 \xe2\x94\x82 user: test (esc)
881 \xe2\x94\x82 user: test (esc)
882 \xe2\x94\x82 date: Thu Jan 01 00:00:34 1970 +0000 (esc)
882 \xe2\x94\x82 date: Thu Jan 01 00:00:34 1970 +0000 (esc)
883 \xe2\x94\x82 summary: (34) head (esc)
883 \xe2\x94\x82 summary: (34) head (esc)
884 \xe2\x94\x82 (esc)
884 \xe2\x94\x82 (esc)
885 \xe2\x94\x82 \xe2\x97\x8b changeset: 33:68608f5145f9 (esc)
885 \xe2\x94\x82 \xe2\x97\x8b changeset: 33:68608f5145f9 (esc)
886 \xe2\x94\x82 \xe2\x94\x82 parent: 18:1aa84d96232a (esc)
886 \xe2\x94\x82 \xe2\x94\x82 parent: 18:1aa84d96232a (esc)
887 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
887 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
888 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:33 1970 +0000 (esc)
888 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:33 1970 +0000 (esc)
889 \xe2\x94\x82 \xe2\x94\x82 summary: (33) head (esc)
889 \xe2\x94\x82 \xe2\x94\x82 summary: (33) head (esc)
890 \xe2\x94\x82 \xe2\x94\x82 (esc)
890 \xe2\x94\x82 \xe2\x94\x82 (esc)
891 \xe2\x97\x8b \xe2\x94\x82 changeset: 32:d06dffa21a31 (esc)
891 \xe2\x97\x8b \xe2\x94\x82 changeset: 32:d06dffa21a31 (esc)
892 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 27:886ed638191b (esc)
892 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 27:886ed638191b (esc)
893 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 31:621d83e11f67 (esc)
893 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 31:621d83e11f67 (esc)
894 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
894 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
895 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:32 1970 +0000 (esc)
895 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:32 1970 +0000 (esc)
896 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (32) expand (esc)
896 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (32) expand (esc)
897 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
897 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
898 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 changeset: 31:621d83e11f67 (esc)
898 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 changeset: 31:621d83e11f67 (esc)
899 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 21:d42a756af44d (esc)
899 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 21:d42a756af44d (esc)
900 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 30:6e11cd4b648f (esc)
900 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 30:6e11cd4b648f (esc)
901 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
901 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
902 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:31 1970 +0000 (esc)
902 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:31 1970 +0000 (esc)
903 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (31) expand (esc)
903 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (31) expand (esc)
904 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
904 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
905 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 changeset: 30:6e11cd4b648f (esc)
905 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 changeset: 30:6e11cd4b648f (esc)
906 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 28:44ecd0b9ae99 (esc)
906 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 28:44ecd0b9ae99 (esc)
907 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 29:cd9bb2be7593 (esc)
907 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 29:cd9bb2be7593 (esc)
908 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
908 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
909 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:30 1970 +0000 (esc)
909 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:30 1970 +0000 (esc)
910 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (30) expand (esc)
910 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (30) expand (esc)
911 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
911 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
912 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 changeset: 29:cd9bb2be7593 (esc)
912 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 changeset: 29:cd9bb2be7593 (esc)
913 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
913 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
914 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
914 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
915 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:29 1970 +0000 (esc)
915 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:29 1970 +0000 (esc)
916 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (29) regular commit (esc)
916 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (29) regular commit (esc)
917 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
917 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
918 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 28:44ecd0b9ae99 (esc)
918 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 28:44ecd0b9ae99 (esc)
919 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 1:6db2ef61d156 (esc)
919 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 1:6db2ef61d156 (esc)
920 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 26:7f25b6c2f0b9 (esc)
920 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 26:7f25b6c2f0b9 (esc)
921 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
921 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
922 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:28 1970 +0000 (esc)
922 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:28 1970 +0000 (esc)
923 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (28) merge zero known (esc)
923 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (28) merge zero known (esc)
924 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
924 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
925 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 27:886ed638191b (esc)
925 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 27:886ed638191b (esc)
926 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 21:d42a756af44d (esc)
926 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 21:d42a756af44d (esc)
927 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
927 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
928 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:27 1970 +0000 (esc)
928 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:27 1970 +0000 (esc)
929 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (27) collapse (esc)
929 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (27) collapse (esc)
930 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
930 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
931 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 26:7f25b6c2f0b9 (esc)
931 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 26:7f25b6c2f0b9 (esc)
932 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 18:1aa84d96232a (esc)
932 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 18:1aa84d96232a (esc)
933 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 25:91da8ed57247 (esc)
933 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 25:91da8ed57247 (esc)
934 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
934 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
935 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:26 1970 +0000 (esc)
935 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:26 1970 +0000 (esc)
936 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (26) merge one known; far right (esc)
936 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (26) merge one known; far right (esc)
937 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
937 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
938 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 25:91da8ed57247 (esc)
938 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 25:91da8ed57247 (esc)
939 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 21:d42a756af44d (esc)
939 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 21:d42a756af44d (esc)
940 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 24:a9c19a3d96b7 (esc)
940 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 24:a9c19a3d96b7 (esc)
941 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
941 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
942 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:25 1970 +0000 (esc)
942 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:25 1970 +0000 (esc)
943 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (25) merge one known; far left (esc)
943 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (25) merge one known; far left (esc)
944 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
944 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
945 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 24:a9c19a3d96b7 (esc)
945 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 24:a9c19a3d96b7 (esc)
946 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2\xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
946 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2\xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
947 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 23:a01cddf0766d (esc)
947 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 23:a01cddf0766d (esc)
948 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
948 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
949 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:24 1970 +0000 (esc)
949 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:24 1970 +0000 (esc)
950 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (24) merge one known; immediate right (esc)
950 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (24) merge one known; immediate right (esc)
951 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
951 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
952 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 23:a01cddf0766d (esc)
952 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 23:a01cddf0766d (esc)
953 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 1:6db2ef61d156 (esc)
953 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 1:6db2ef61d156 (esc)
954 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 22:e0d9cccacb5d (esc)
954 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 22:e0d9cccacb5d (esc)
955 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
955 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
956 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:23 1970 +0000 (esc)
956 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:23 1970 +0000 (esc)
957 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (23) merge one known; immediate left (esc)
957 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (23) merge one known; immediate left (esc)
958 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
958 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
959 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 22:e0d9cccacb5d (esc)
959 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 22:e0d9cccacb5d (esc)
960 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 18:1aa84d96232a (esc)
960 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 18:1aa84d96232a (esc)
961 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xb1 \xe2\x95\xb1 parent: 21:d42a756af44d (esc)
961 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xb1 \xe2\x95\xb1 parent: 21:d42a756af44d (esc)
962 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
962 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
963 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:22 1970 +0000 (esc)
963 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:22 1970 +0000 (esc)
964 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (22) merge two known; one far left, one far right (esc)
964 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (22) merge two known; one far left, one far right (esc)
965 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
965 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
966 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 21:d42a756af44d (esc)
966 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 21:d42a756af44d (esc)
967 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 19:31ddc2c1573b (esc)
967 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 19:31ddc2c1573b (esc)
968 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 20:d30ed6450e32 (esc)
968 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 20:d30ed6450e32 (esc)
969 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
969 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
970 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:21 1970 +0000 (esc)
970 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:21 1970 +0000 (esc)
971 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (21) expand (esc)
971 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (21) expand (esc)
972 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
972 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
973 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xbc\xe2\x94\x80\xe2\x94\xa4 changeset: 20:d30ed6450e32 (esc)
973 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xbc\xe2\x94\x80\xe2\x94\xa4 changeset: 20:d30ed6450e32 (esc)
974 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
974 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
975 \xe2\x94\x82 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 18:1aa84d96232a (esc)
975 \xe2\x94\x82 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 18:1aa84d96232a (esc)
976 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
976 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
977 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:20 1970 +0000 (esc)
977 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:20 1970 +0000 (esc)
978 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (20) merge two known; two far right (esc)
978 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (20) merge two known; two far right (esc)
979 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
979 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
980 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 19:31ddc2c1573b (esc)
980 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 19:31ddc2c1573b (esc)
981 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 15:1dda3f72782d (esc)
981 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 15:1dda3f72782d (esc)
982 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 17:44765d7c06e0 (esc)
982 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 17:44765d7c06e0 (esc)
983 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
983 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
984 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:19 1970 +0000 (esc)
984 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:19 1970 +0000 (esc)
985 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (19) expand (esc)
985 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (19) expand (esc)
986 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
986 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
987 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xbc\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b changeset: 18:1aa84d96232a (esc)
987 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xbc\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b changeset: 18:1aa84d96232a (esc)
988 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 1:6db2ef61d156 (esc)
988 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 1:6db2ef61d156 (esc)
989 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 15:1dda3f72782d (esc)
989 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 15:1dda3f72782d (esc)
990 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
990 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
991 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:18 1970 +0000 (esc)
991 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:18 1970 +0000 (esc)
992 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (18) merge two known; two far left (esc)
992 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (18) merge two known; two far left (esc)
993 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
993 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
994 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 17:44765d7c06e0 (esc)
994 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 17:44765d7c06e0 (esc)
995 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 12:86b91144a6e9 (esc)
995 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 12:86b91144a6e9 (esc)
996 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 16:3677d192927d (esc)
996 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 16:3677d192927d (esc)
997 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
997 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
998 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:17 1970 +0000 (esc)
998 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:17 1970 +0000 (esc)
999 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (17) expand (esc)
999 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (17) expand (esc)
1000 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1000 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1001 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 16:3677d192927d (esc)
1001 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 16:3677d192927d (esc)
1002 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
1002 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
1003 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 parent: 1:6db2ef61d156 (esc)
1003 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 parent: 1:6db2ef61d156 (esc)
1004 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1004 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1005 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:16 1970 +0000 (esc)
1005 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:16 1970 +0000 (esc)
1006 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (16) merge two known; one immediate right, one near right (esc)
1006 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (16) merge two known; one immediate right, one near right (esc)
1007 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1007 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1008 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 15:1dda3f72782d (esc)
1008 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 15:1dda3f72782d (esc)
1009 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 13:22d8966a97e3 (esc)
1009 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 13:22d8966a97e3 (esc)
1010 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 14:8eac370358ef (esc)
1010 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 14:8eac370358ef (esc)
1011 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1011 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1012 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:15 1970 +0000 (esc)
1012 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:15 1970 +0000 (esc)
1013 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (15) expand (esc)
1013 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (15) expand (esc)
1014 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1014 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1015 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 14:8eac370358ef (esc)
1015 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 14:8eac370358ef (esc)
1016 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
1016 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
1017 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 12:86b91144a6e9 (esc)
1017 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 12:86b91144a6e9 (esc)
1018 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1018 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1019 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:14 1970 +0000 (esc)
1019 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:14 1970 +0000 (esc)
1020 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (14) merge two known; one immediate right, one far right (esc)
1020 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (14) merge two known; one immediate right, one far right (esc)
1021 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1021 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1022 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 13:22d8966a97e3 (esc)
1022 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 13:22d8966a97e3 (esc)
1023 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 9:7010c0af0a35 (esc)
1023 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 9:7010c0af0a35 (esc)
1024 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 11:832d76e6bdf2 (esc)
1024 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 11:832d76e6bdf2 (esc)
1025 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1025 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1026 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:13 1970 +0000 (esc)
1026 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:13 1970 +0000 (esc)
1027 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (13) expand (esc)
1027 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (13) expand (esc)
1028 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1028 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1029 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 12:86b91144a6e9 (esc)
1029 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 12:86b91144a6e9 (esc)
1030 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 parent: 1:6db2ef61d156 (esc)
1030 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 parent: 1:6db2ef61d156 (esc)
1031 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 9:7010c0af0a35 (esc)
1031 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 9:7010c0af0a35 (esc)
1032 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1032 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1033 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:12 1970 +0000 (esc)
1033 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:12 1970 +0000 (esc)
1034 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (12) merge two known; one immediate right, one far left (esc)
1034 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (12) merge two known; one immediate right, one far left (esc)
1035 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1035 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1036 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 11:832d76e6bdf2 (esc)
1036 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 11:832d76e6bdf2 (esc)
1037 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 6:b105a072e251 (esc)
1037 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 6:b105a072e251 (esc)
1038 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 10:74c64d036d72 (esc)
1038 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 10:74c64d036d72 (esc)
1039 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1039 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1040 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:11 1970 +0000 (esc)
1040 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:11 1970 +0000 (esc)
1041 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (11) expand (esc)
1041 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (11) expand (esc)
1042 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1042 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1043 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 10:74c64d036d72 (esc)
1043 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 10:74c64d036d72 (esc)
1044 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
1044 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
1045 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 6:b105a072e251 (esc)
1045 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 6:b105a072e251 (esc)
1046 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1046 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1047 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:10 1970 +0000 (esc)
1047 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:10 1970 +0000 (esc)
1048 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (10) merge two known; one immediate left, one near right (esc)
1048 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (10) merge two known; one immediate left, one near right (esc)
1049 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1049 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1050 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 9:7010c0af0a35 (esc)
1050 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 9:7010c0af0a35 (esc)
1051 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 7:b632bb1b1224 (esc)
1051 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 7:b632bb1b1224 (esc)
1052 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 8:7a0b11f71937 (esc)
1052 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 8:7a0b11f71937 (esc)
1053 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1053 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1054 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:09 1970 +0000 (esc)
1054 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:09 1970 +0000 (esc)
1055 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (9) expand (esc)
1055 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (9) expand (esc)
1056 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1056 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1057 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 8:7a0b11f71937 (esc)
1057 \xe2\x94\x82 \xe2\x97\x8b\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4 changeset: 8:7a0b11f71937 (esc)
1058 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
1058 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
1059 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 7:b632bb1b1224 (esc)
1059 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 7:b632bb1b1224 (esc)
1060 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1060 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1061 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:08 1970 +0000 (esc)
1061 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:08 1970 +0000 (esc)
1062 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (8) merge two known; one immediate left, one far right (esc)
1062 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (8) merge two known; one immediate left, one far right (esc)
1063 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1063 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1064 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 7:b632bb1b1224 (esc)
1064 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 changeset: 7:b632bb1b1224 (esc)
1065 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 2:3d9a33b8d1e1 (esc)
1065 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 2:3d9a33b8d1e1 (esc)
1066 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 5:4409d547b708 (esc)
1066 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 5:4409d547b708 (esc)
1067 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1067 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1068 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:07 1970 +0000 (esc)
1068 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:07 1970 +0000 (esc)
1069 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (7) expand (esc)
1069 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (7) expand (esc)
1070 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1070 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1071 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 6:b105a072e251 (esc)
1071 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 6:b105a072e251 (esc)
1072 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 2:3d9a33b8d1e1 (esc)
1072 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 parent: 2:3d9a33b8d1e1 (esc)
1073 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 5:4409d547b708 (esc)
1073 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 5:4409d547b708 (esc)
1074 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1074 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1075 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:06 1970 +0000 (esc)
1075 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:06 1970 +0000 (esc)
1076 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (6) merge two known; one immediate left, one far left (esc)
1076 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (6) merge two known; one immediate left, one far left (esc)
1077 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1077 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1078 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 5:4409d547b708 (esc)
1078 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 5:4409d547b708 (esc)
1079 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 3:27eef8ed80b4 (esc)
1079 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 \xe2\x95\xb2 parent: 3:27eef8ed80b4 (esc)
1080 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 4:26a8bac39d9f (esc)
1080 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 4:26a8bac39d9f (esc)
1081 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1081 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1082 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:05 1970 +0000 (esc)
1082 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:05 1970 +0000 (esc)
1083 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (5) expand (esc)
1083 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (5) expand (esc)
1084 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1084 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1085 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 4:26a8bac39d9f (esc)
1085 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 4:26a8bac39d9f (esc)
1086 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 parent: 1:6db2ef61d156 (esc)
1086 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 parent: 1:6db2ef61d156 (esc)
1087 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 3:27eef8ed80b4 (esc)
1087 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 3:27eef8ed80b4 (esc)
1088 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1088 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1089 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:04 1970 +0000 (esc)
1089 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:04 1970 +0000 (esc)
1090 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (4) merge two known; one immediate left, one immediate right (esc)
1090 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (4) merge two known; one immediate left, one immediate right (esc)
1091 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1091 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1092 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 3:27eef8ed80b4 (esc)
1092 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 3:27eef8ed80b4 (esc)
1093 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 user: test (esc)
1093 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 \xe2\x95\xb1 user: test (esc)
1094 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:03 1970 +0000 (esc)
1094 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:03 1970 +0000 (esc)
1095 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (3) collapse (esc)
1095 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (3) collapse (esc)
1096 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1096 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1097 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 2:3d9a33b8d1e1 (esc)
1097 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 2:3d9a33b8d1e1 (esc)
1098 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 user: test (esc)
1098 \xe2\x94\x82\xe2\x95\xb1 \xe2\x95\xb1 user: test (esc)
1099 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:02 1970 +0000 (esc)
1099 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:02 1970 +0000 (esc)
1100 \xe2\x94\x82 \xe2\x94\x82 summary: (2) collapse (esc)
1100 \xe2\x94\x82 \xe2\x94\x82 summary: (2) collapse (esc)
1101 \xe2\x94\x82 \xe2\x94\x82 (esc)
1101 \xe2\x94\x82 \xe2\x94\x82 (esc)
1102 \xe2\x97\x8b \xe2\x94\x82 changeset: 1:6db2ef61d156 (esc)
1102 \xe2\x97\x8b \xe2\x94\x82 changeset: 1:6db2ef61d156 (esc)
1103 \xe2\x94\x82\xe2\x95\xb1 user: test (esc)
1103 \xe2\x94\x82\xe2\x95\xb1 user: test (esc)
1104 \xe2\x94\x82 date: Thu Jan 01 00:00:01 1970 +0000 (esc)
1104 \xe2\x94\x82 date: Thu Jan 01 00:00:01 1970 +0000 (esc)
1105 \xe2\x94\x82 summary: (1) collapse (esc)
1105 \xe2\x94\x82 summary: (1) collapse (esc)
1106 \xe2\x94\x82 (esc)
1106 \xe2\x94\x82 (esc)
1107 \xe2\x97\x8b changeset: 0:e6eb3150255d (esc)
1107 \xe2\x97\x8b changeset: 0:e6eb3150255d (esc)
1108 user: test
1108 user: test
1109 date: Thu Jan 01 00:00:00 1970 +0000
1109 date: Thu Jan 01 00:00:00 1970 +0000
1110 summary: (0) root
1110 summary: (0) root
1111
1111
1112
1112
1113 File glog per revset (only merges):
1113 File glog per revset (only merges):
1114
1114
1115 $ hg log -G -r 'file("a")' -m
1115 $ hg log -G -r 'file("a")' -m
1116 \xe2\x97\x8b changeset: 32:d06dffa21a31 (esc)
1116 \xe2\x97\x8b changeset: 32:d06dffa21a31 (esc)
1117 \xe2\x94\x82\xe2\x95\xb2 parent: 27:886ed638191b (esc)
1117 \xe2\x94\x82\xe2\x95\xb2 parent: 27:886ed638191b (esc)
1118 \xe2\x94\x82 \xe2\x94\x86 parent: 31:621d83e11f67 (esc)
1118 \xe2\x94\x82 \xe2\x94\x86 parent: 31:621d83e11f67 (esc)
1119 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
1119 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
1120 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:32 1970 +0000 (esc)
1120 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:32 1970 +0000 (esc)
1121 \xe2\x94\x82 \xe2\x94\x86 summary: (32) expand (esc)
1121 \xe2\x94\x82 \xe2\x94\x86 summary: (32) expand (esc)
1122 \xe2\x94\x82 \xe2\x94\x86 (esc)
1122 \xe2\x94\x82 \xe2\x94\x86 (esc)
1123 \xe2\x97\x8b \xe2\x94\x86 changeset: 31:621d83e11f67 (esc)
1123 \xe2\x97\x8b \xe2\x94\x86 changeset: 31:621d83e11f67 (esc)
1124 \xe2\x94\x82\xe2\x95\xb2\xe2\x94\x86 parent: 21:d42a756af44d (esc)
1124 \xe2\x94\x82\xe2\x95\xb2\xe2\x94\x86 parent: 21:d42a756af44d (esc)
1125 \xe2\x94\x82 \xe2\x94\x86 parent: 30:6e11cd4b648f (esc)
1125 \xe2\x94\x82 \xe2\x94\x86 parent: 30:6e11cd4b648f (esc)
1126 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
1126 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
1127 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:31 1970 +0000 (esc)
1127 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:31 1970 +0000 (esc)
1128 \xe2\x94\x82 \xe2\x94\x86 summary: (31) expand (esc)
1128 \xe2\x94\x82 \xe2\x94\x86 summary: (31) expand (esc)
1129 \xe2\x94\x82 \xe2\x94\x86 (esc)
1129 \xe2\x94\x82 \xe2\x94\x86 (esc)
1130 \xe2\x97\x8b \xe2\x94\x86 changeset: 30:6e11cd4b648f (esc)
1130 \xe2\x97\x8b \xe2\x94\x86 changeset: 30:6e11cd4b648f (esc)
1131 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 28:44ecd0b9ae99 (esc)
1131 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 28:44ecd0b9ae99 (esc)
1132 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x86 parent: 29:cd9bb2be7593 (esc)
1132 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x86 parent: 29:cd9bb2be7593 (esc)
1133 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
1133 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
1134 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:30 1970 +0000 (esc)
1134 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:30 1970 +0000 (esc)
1135 \xe2\x94\x82 \xe2\x94\x86 summary: (30) expand (esc)
1135 \xe2\x94\x82 \xe2\x94\x86 summary: (30) expand (esc)
1136 \xe2\x94\x82 \xe2\x95\xb1 (esc)
1136 \xe2\x94\x82 \xe2\x95\xb1 (esc)
1137 \xe2\x97\x8b \xe2\x94\x86 changeset: 28:44ecd0b9ae99 (esc)
1137 \xe2\x97\x8b \xe2\x94\x86 changeset: 28:44ecd0b9ae99 (esc)
1138 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 1:6db2ef61d156 (esc)
1138 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 1:6db2ef61d156 (esc)
1139 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x86 parent: 26:7f25b6c2f0b9 (esc)
1139 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x86 parent: 26:7f25b6c2f0b9 (esc)
1140 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
1140 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
1141 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:28 1970 +0000 (esc)
1141 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:28 1970 +0000 (esc)
1142 \xe2\x94\x82 \xe2\x94\x86 summary: (28) merge zero known (esc)
1142 \xe2\x94\x82 \xe2\x94\x86 summary: (28) merge zero known (esc)
1143 \xe2\x94\x82 \xe2\x95\xb1 (esc)
1143 \xe2\x94\x82 \xe2\x95\xb1 (esc)
1144 \xe2\x97\x8b \xe2\x94\x86 changeset: 26:7f25b6c2f0b9 (esc)
1144 \xe2\x97\x8b \xe2\x94\x86 changeset: 26:7f25b6c2f0b9 (esc)
1145 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 18:1aa84d96232a (esc)
1145 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 18:1aa84d96232a (esc)
1146 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 parent: 25:91da8ed57247 (esc)
1146 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 parent: 25:91da8ed57247 (esc)
1147 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
1147 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
1148 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:26 1970 +0000 (esc)
1148 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:26 1970 +0000 (esc)
1149 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 summary: (26) merge one known; far right (esc)
1149 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 summary: (26) merge one known; far right (esc)
1150 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 (esc)
1150 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 (esc)
1151 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x86 changeset: 25:91da8ed57247 (esc)
1151 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x86 changeset: 25:91da8ed57247 (esc)
1152 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2\xe2\x94\x86 parent: 21:d42a756af44d (esc)
1152 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2\xe2\x94\x86 parent: 21:d42a756af44d (esc)
1153 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 parent: 24:a9c19a3d96b7 (esc)
1153 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 parent: 24:a9c19a3d96b7 (esc)
1154 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
1154 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
1155 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:25 1970 +0000 (esc)
1155 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:25 1970 +0000 (esc)
1156 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 summary: (25) merge one known; far left (esc)
1156 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 summary: (25) merge one known; far left (esc)
1157 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 (esc)
1157 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 (esc)
1158 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x86 changeset: 24:a9c19a3d96b7 (esc)
1158 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x86 changeset: 24:a9c19a3d96b7 (esc)
1159 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 0:e6eb3150255d (esc)
1159 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 0:e6eb3150255d (esc)
1160 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x86 parent: 23:a01cddf0766d (esc)
1160 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x86 parent: 23:a01cddf0766d (esc)
1161 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
1161 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
1162 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:24 1970 +0000 (esc)
1162 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:24 1970 +0000 (esc)
1163 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 summary: (24) merge one known; immediate right (esc)
1163 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 summary: (24) merge one known; immediate right (esc)
1164 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xb1 (esc)
1164 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xb1 (esc)
1165 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x86 changeset: 23:a01cddf0766d (esc)
1165 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x86 changeset: 23:a01cddf0766d (esc)
1166 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 1:6db2ef61d156 (esc)
1166 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 1:6db2ef61d156 (esc)
1167 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x86 parent: 22:e0d9cccacb5d (esc)
1167 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x86 parent: 22:e0d9cccacb5d (esc)
1168 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
1168 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
1169 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:23 1970 +0000 (esc)
1169 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:23 1970 +0000 (esc)
1170 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 summary: (23) merge one known; immediate left (esc)
1170 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 summary: (23) merge one known; immediate left (esc)
1171 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xb1 (esc)
1171 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xb1 (esc)
1172 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x86 changeset: 22:e0d9cccacb5d (esc)
1172 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x86 changeset: 22:e0d9cccacb5d (esc)
1173 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x86\xe2\x95\xb1 parent: 18:1aa84d96232a (esc)
1173 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x86\xe2\x95\xb1 parent: 18:1aa84d96232a (esc)
1174 \xe2\x94\x82 \xe2\x94\x86 parent: 21:d42a756af44d (esc)
1174 \xe2\x94\x82 \xe2\x94\x86 parent: 21:d42a756af44d (esc)
1175 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
1175 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
1176 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:22 1970 +0000 (esc)
1176 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:22 1970 +0000 (esc)
1177 \xe2\x94\x82 \xe2\x94\x86 summary: (22) merge two known; one far left, one far right (esc)
1177 \xe2\x94\x82 \xe2\x94\x86 summary: (22) merge two known; one far left, one far right (esc)
1178 \xe2\x94\x82 \xe2\x94\x86 (esc)
1178 \xe2\x94\x82 \xe2\x94\x86 (esc)
1179 \xe2\x94\x82 \xe2\x97\x8b changeset: 21:d42a756af44d (esc)
1179 \xe2\x94\x82 \xe2\x97\x8b changeset: 21:d42a756af44d (esc)
1180 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 parent: 19:31ddc2c1573b (esc)
1180 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 parent: 19:31ddc2c1573b (esc)
1181 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 20:d30ed6450e32 (esc)
1181 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 20:d30ed6450e32 (esc)
1182 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1182 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1183 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:21 1970 +0000 (esc)
1183 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:21 1970 +0000 (esc)
1184 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (21) expand (esc)
1184 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (21) expand (esc)
1185 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1185 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1186 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b changeset: 20:d30ed6450e32 (esc)
1186 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b changeset: 20:d30ed6450e32 (esc)
1187 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
1187 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
1188 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xa7 parent: 18:1aa84d96232a (esc)
1188 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xa7 parent: 18:1aa84d96232a (esc)
1189 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1189 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1190 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:20 1970 +0000 (esc)
1190 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:20 1970 +0000 (esc)
1191 \xe2\x94\x82 \xe2\x94\x82 summary: (20) merge two known; two far right (esc)
1191 \xe2\x94\x82 \xe2\x94\x82 summary: (20) merge two known; two far right (esc)
1192 \xe2\x94\x82 \xe2\x94\x82 (esc)
1192 \xe2\x94\x82 \xe2\x94\x82 (esc)
1193 \xe2\x94\x82 \xe2\x97\x8b changeset: 19:31ddc2c1573b (esc)
1193 \xe2\x94\x82 \xe2\x97\x8b changeset: 19:31ddc2c1573b (esc)
1194 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 parent: 15:1dda3f72782d (esc)
1194 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 parent: 15:1dda3f72782d (esc)
1195 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 17:44765d7c06e0 (esc)
1195 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 17:44765d7c06e0 (esc)
1196 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1196 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1197 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:19 1970 +0000 (esc)
1197 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:19 1970 +0000 (esc)
1198 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (19) expand (esc)
1198 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (19) expand (esc)
1199 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1199 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1200 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 18:1aa84d96232a (esc)
1200 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 18:1aa84d96232a (esc)
1201 \xe2\x94\x82\xe2\x95\xb2\xe2\x94\x82 \xe2\x94\x82 parent: 1:6db2ef61d156 (esc)
1201 \xe2\x94\x82\xe2\x95\xb2\xe2\x94\x82 \xe2\x94\x82 parent: 1:6db2ef61d156 (esc)
1202 \xe2\x95\xa7 \xe2\x94\x82 \xe2\x94\x82 parent: 15:1dda3f72782d (esc)
1202 \xe2\x95\xa7 \xe2\x94\x82 \xe2\x94\x82 parent: 15:1dda3f72782d (esc)
1203 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1203 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1204 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:18 1970 +0000 (esc)
1204 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:18 1970 +0000 (esc)
1205 \xe2\x94\x82 \xe2\x94\x82 summary: (18) merge two known; two far left (esc)
1205 \xe2\x94\x82 \xe2\x94\x82 summary: (18) merge two known; two far left (esc)
1206 \xe2\x95\xb1 \xe2\x95\xb1 (esc)
1206 \xe2\x95\xb1 \xe2\x95\xb1 (esc)
1207 \xe2\x94\x82 \xe2\x97\x8b changeset: 17:44765d7c06e0 (esc)
1207 \xe2\x94\x82 \xe2\x97\x8b changeset: 17:44765d7c06e0 (esc)
1208 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 parent: 12:86b91144a6e9 (esc)
1208 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 parent: 12:86b91144a6e9 (esc)
1209 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 16:3677d192927d (esc)
1209 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 16:3677d192927d (esc)
1210 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1210 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1211 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:17 1970 +0000 (esc)
1211 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:17 1970 +0000 (esc)
1212 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (17) expand (esc)
1212 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (17) expand (esc)
1213 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1213 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1214 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b changeset: 16:3677d192927d (esc)
1214 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b changeset: 16:3677d192927d (esc)
1215 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 parent: 0:e6eb3150255d (esc)
1215 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 parent: 0:e6eb3150255d (esc)
1216 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x95\xa7 parent: 1:6db2ef61d156 (esc)
1216 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x95\xa7 parent: 1:6db2ef61d156 (esc)
1217 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1217 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1218 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:16 1970 +0000 (esc)
1218 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:16 1970 +0000 (esc)
1219 \xe2\x94\x82 \xe2\x94\x82 summary: (16) merge two known; one immediate right, one near right (esc)
1219 \xe2\x94\x82 \xe2\x94\x82 summary: (16) merge two known; one immediate right, one near right (esc)
1220 \xe2\x94\x82 \xe2\x94\x82 (esc)
1220 \xe2\x94\x82 \xe2\x94\x82 (esc)
1221 \xe2\x97\x8b \xe2\x94\x82 changeset: 15:1dda3f72782d (esc)
1221 \xe2\x97\x8b \xe2\x94\x82 changeset: 15:1dda3f72782d (esc)
1222 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 13:22d8966a97e3 (esc)
1222 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 13:22d8966a97e3 (esc)
1223 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 14:8eac370358ef (esc)
1223 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 14:8eac370358ef (esc)
1224 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1224 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1225 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:15 1970 +0000 (esc)
1225 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:15 1970 +0000 (esc)
1226 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (15) expand (esc)
1226 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (15) expand (esc)
1227 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1227 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1228 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 changeset: 14:8eac370358ef (esc)
1228 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 changeset: 14:8eac370358ef (esc)
1229 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2\xe2\x94\x82 parent: 0:e6eb3150255d (esc)
1229 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2\xe2\x94\x82 parent: 0:e6eb3150255d (esc)
1230 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x82 parent: 12:86b91144a6e9 (esc)
1230 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x82 parent: 12:86b91144a6e9 (esc)
1231 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1231 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1232 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:14 1970 +0000 (esc)
1232 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:14 1970 +0000 (esc)
1233 \xe2\x94\x82 \xe2\x94\x82 summary: (14) merge two known; one immediate right, one far right (esc)
1233 \xe2\x94\x82 \xe2\x94\x82 summary: (14) merge two known; one immediate right, one far right (esc)
1234 \xe2\x94\x82 \xe2\x95\xb1 (esc)
1234 \xe2\x94\x82 \xe2\x95\xb1 (esc)
1235 \xe2\x97\x8b \xe2\x94\x82 changeset: 13:22d8966a97e3 (esc)
1235 \xe2\x97\x8b \xe2\x94\x82 changeset: 13:22d8966a97e3 (esc)
1236 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 9:7010c0af0a35 (esc)
1236 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 9:7010c0af0a35 (esc)
1237 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 11:832d76e6bdf2 (esc)
1237 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 11:832d76e6bdf2 (esc)
1238 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1238 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1239 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:13 1970 +0000 (esc)
1239 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:13 1970 +0000 (esc)
1240 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (13) expand (esc)
1240 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (13) expand (esc)
1241 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1241 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1242 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b changeset: 12:86b91144a6e9 (esc)
1242 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b changeset: 12:86b91144a6e9 (esc)
1243 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 1:6db2ef61d156 (esc)
1243 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 1:6db2ef61d156 (esc)
1244 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xa7 parent: 9:7010c0af0a35 (esc)
1244 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xa7 parent: 9:7010c0af0a35 (esc)
1245 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1245 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1246 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:12 1970 +0000 (esc)
1246 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:12 1970 +0000 (esc)
1247 \xe2\x94\x82 \xe2\x94\x82 summary: (12) merge two known; one immediate right, one far left (esc)
1247 \xe2\x94\x82 \xe2\x94\x82 summary: (12) merge two known; one immediate right, one far left (esc)
1248 \xe2\x94\x82 \xe2\x94\x82 (esc)
1248 \xe2\x94\x82 \xe2\x94\x82 (esc)
1249 \xe2\x94\x82 \xe2\x97\x8b changeset: 11:832d76e6bdf2 (esc)
1249 \xe2\x94\x82 \xe2\x97\x8b changeset: 11:832d76e6bdf2 (esc)
1250 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 parent: 6:b105a072e251 (esc)
1250 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 parent: 6:b105a072e251 (esc)
1251 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 10:74c64d036d72 (esc)
1251 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 10:74c64d036d72 (esc)
1252 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1252 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1253 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:11 1970 +0000 (esc)
1253 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:11 1970 +0000 (esc)
1254 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (11) expand (esc)
1254 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (11) expand (esc)
1255 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1255 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1256 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b changeset: 10:74c64d036d72 (esc)
1256 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b changeset: 10:74c64d036d72 (esc)
1257 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82 parent: 0:e6eb3150255d (esc)
1257 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82 parent: 0:e6eb3150255d (esc)
1258 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xa7 parent: 6:b105a072e251 (esc)
1258 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xa7 parent: 6:b105a072e251 (esc)
1259 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1259 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1260 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:10 1970 +0000 (esc)
1260 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:10 1970 +0000 (esc)
1261 \xe2\x94\x82 \xe2\x94\x82 summary: (10) merge two known; one immediate left, one near right (esc)
1261 \xe2\x94\x82 \xe2\x94\x82 summary: (10) merge two known; one immediate left, one near right (esc)
1262 \xe2\x94\x82 \xe2\x94\x82 (esc)
1262 \xe2\x94\x82 \xe2\x94\x82 (esc)
1263 \xe2\x97\x8b \xe2\x94\x82 changeset: 9:7010c0af0a35 (esc)
1263 \xe2\x97\x8b \xe2\x94\x82 changeset: 9:7010c0af0a35 (esc)
1264 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 7:b632bb1b1224 (esc)
1264 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 7:b632bb1b1224 (esc)
1265 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 8:7a0b11f71937 (esc)
1265 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 8:7a0b11f71937 (esc)
1266 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1266 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1267 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:09 1970 +0000 (esc)
1267 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:09 1970 +0000 (esc)
1268 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (9) expand (esc)
1268 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (9) expand (esc)
1269 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1269 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
1270 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 changeset: 8:7a0b11f71937 (esc)
1270 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 changeset: 8:7a0b11f71937 (esc)
1271 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
1271 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
1272 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x82 parent: 7:b632bb1b1224 (esc)
1272 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x82 parent: 7:b632bb1b1224 (esc)
1273 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1273 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1274 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:08 1970 +0000 (esc)
1274 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:08 1970 +0000 (esc)
1275 \xe2\x94\x82 \xe2\x94\x82 summary: (8) merge two known; one immediate left, one far right (esc)
1275 \xe2\x94\x82 \xe2\x94\x82 summary: (8) merge two known; one immediate left, one far right (esc)
1276 \xe2\x94\x82 \xe2\x95\xb1 (esc)
1276 \xe2\x94\x82 \xe2\x95\xb1 (esc)
1277 \xe2\x97\x8b \xe2\x94\x82 changeset: 7:b632bb1b1224 (esc)
1277 \xe2\x97\x8b \xe2\x94\x82 changeset: 7:b632bb1b1224 (esc)
1278 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 2:3d9a33b8d1e1 (esc)
1278 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 2:3d9a33b8d1e1 (esc)
1279 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x82 parent: 5:4409d547b708 (esc)
1279 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x82 parent: 5:4409d547b708 (esc)
1280 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1280 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
1281 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:07 1970 +0000 (esc)
1281 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:07 1970 +0000 (esc)
1282 \xe2\x94\x82 \xe2\x94\x82 summary: (7) expand (esc)
1282 \xe2\x94\x82 \xe2\x94\x82 summary: (7) expand (esc)
1283 \xe2\x94\x82 \xe2\x95\xb1 (esc)
1283 \xe2\x94\x82 \xe2\x95\xb1 (esc)
1284 \xe2\x94\x82 \xe2\x97\x8b changeset: 6:b105a072e251 (esc)
1284 \xe2\x94\x82 \xe2\x97\x8b changeset: 6:b105a072e251 (esc)
1285 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82 parent: 2:3d9a33b8d1e1 (esc)
1285 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82 parent: 2:3d9a33b8d1e1 (esc)
1286 \xe2\x94\x82 \xe2\x95\xa7 parent: 5:4409d547b708 (esc)
1286 \xe2\x94\x82 \xe2\x95\xa7 parent: 5:4409d547b708 (esc)
1287 \xe2\x94\x82 user: test (esc)
1287 \xe2\x94\x82 user: test (esc)
1288 \xe2\x94\x82 date: Thu Jan 01 00:00:06 1970 +0000 (esc)
1288 \xe2\x94\x82 date: Thu Jan 01 00:00:06 1970 +0000 (esc)
1289 \xe2\x94\x82 summary: (6) merge two known; one immediate left, one far left (esc)
1289 \xe2\x94\x82 summary: (6) merge two known; one immediate left, one far left (esc)
1290 \xe2\x94\x82 (esc)
1290 \xe2\x94\x82 (esc)
1291 \xe2\x97\x8b changeset: 5:4409d547b708 (esc)
1291 \xe2\x97\x8b changeset: 5:4409d547b708 (esc)
1292 \xe2\x94\x82\xe2\x95\xb2 parent: 3:27eef8ed80b4 (esc)
1292 \xe2\x94\x82\xe2\x95\xb2 parent: 3:27eef8ed80b4 (esc)
1293 \xe2\x94\x82 \xe2\x95\xa7 parent: 4:26a8bac39d9f (esc)
1293 \xe2\x94\x82 \xe2\x95\xa7 parent: 4:26a8bac39d9f (esc)
1294 \xe2\x94\x82 user: test (esc)
1294 \xe2\x94\x82 user: test (esc)
1295 \xe2\x94\x82 date: Thu Jan 01 00:00:05 1970 +0000 (esc)
1295 \xe2\x94\x82 date: Thu Jan 01 00:00:05 1970 +0000 (esc)
1296 \xe2\x94\x82 summary: (5) expand (esc)
1296 \xe2\x94\x82 summary: (5) expand (esc)
1297 \xe2\x94\x82 (esc)
1297 \xe2\x94\x82 (esc)
1298 \xe2\x97\x8b changeset: 4:26a8bac39d9f (esc)
1298 \xe2\x97\x8b changeset: 4:26a8bac39d9f (esc)
1299 \xe2\x94\x82\xe2\x95\xb2 parent: 1:6db2ef61d156 (esc)
1299 \xe2\x94\x82\xe2\x95\xb2 parent: 1:6db2ef61d156 (esc)
1300 \xe2\x95\xa7 \xe2\x95\xa7 parent: 3:27eef8ed80b4 (esc)
1300 \xe2\x95\xa7 \xe2\x95\xa7 parent: 3:27eef8ed80b4 (esc)
1301 user: test
1301 user: test
1302 date: Thu Jan 01 00:00:04 1970 +0000
1302 date: Thu Jan 01 00:00:04 1970 +0000
1303 summary: (4) merge two known; one immediate left, one immediate right
1303 summary: (4) merge two known; one immediate left, one immediate right
1304
1304
1305
1305
1306 Empty revision range - display nothing:
1306 Empty revision range - display nothing:
1307 $ hg log -G -r 1..0
1307 $ hg log -G -r 1..0
1308
1308
1309 $ cd ..
1309 $ cd ..
1310
1310
1311 #if no-outer-repo
1311 #if no-outer-repo
1312
1312
1313 From outer space:
1313 From outer space:
1314 $ hg log -G -l1 repo
1314 $ hg log -G -l1 repo
1315 \xe2\x97\x8d changeset: 34:fea3ac5810e0 (esc)
1315 \xe2\x97\x8d changeset: 34:fea3ac5810e0 (esc)
1316 \xe2\x94\x82 tag: tip (esc)
1316 \xe2\x94\x82 tag: tip (esc)
1317 \xe2\x95\xa7 parent: 32:d06dffa21a31 (esc)
1317 \xe2\x95\xa7 parent: 32:d06dffa21a31 (esc)
1318 user: test
1318 user: test
1319 date: Thu Jan 01 00:00:34 1970 +0000
1319 date: Thu Jan 01 00:00:34 1970 +0000
1320 summary: (34) head
1320 summary: (34) head
1321
1321
1322 $ hg log -G -l1 repo/a
1322 $ hg log -G -l1 repo/a
1323 \xe2\x97\x8d changeset: 34:fea3ac5810e0 (esc)
1323 \xe2\x97\x8d changeset: 34:fea3ac5810e0 (esc)
1324 \xe2\x94\x82 tag: tip (esc)
1324 \xe2\x94\x82 tag: tip (esc)
1325 \xe2\x95\xa7 parent: 32:d06dffa21a31 (esc)
1325 \xe2\x95\xa7 parent: 32:d06dffa21a31 (esc)
1326 user: test
1326 user: test
1327 date: Thu Jan 01 00:00:34 1970 +0000
1327 date: Thu Jan 01 00:00:34 1970 +0000
1328 summary: (34) head
1328 summary: (34) head
1329
1329
1330 $ hg log -G -l1 repo/missing
1330 $ hg log -G -l1 repo/missing
1331
1331
1332 #endif
1332 #endif
1333
1333
1334 File log with revs != cset revs:
1334 File log with revs != cset revs:
1335 $ hg init flog
1335 $ hg init flog
1336 $ cd flog
1336 $ cd flog
1337 $ echo one >one
1337 $ echo one >one
1338 $ hg add one
1338 $ hg add one
1339 $ hg commit -mone
1339 $ hg commit -mone
1340 $ echo two >two
1340 $ echo two >two
1341 $ hg add two
1341 $ hg add two
1342 $ hg commit -mtwo
1342 $ hg commit -mtwo
1343 $ echo more >two
1343 $ echo more >two
1344 $ hg commit -mmore
1344 $ hg commit -mmore
1345 $ hg log -G two
1345 $ hg log -G two
1346 \xe2\x97\x8d changeset: 2:12c28321755b (esc)
1346 \xe2\x97\x8d changeset: 2:12c28321755b (esc)
1347 \xe2\x94\x82 tag: tip (esc)
1347 \xe2\x94\x82 tag: tip (esc)
1348 \xe2\x94\x82 user: test (esc)
1348 \xe2\x94\x82 user: test (esc)
1349 \xe2\x94\x82 date: Thu Jan 01 00:00:00 1970 +0000 (esc)
1349 \xe2\x94\x82 date: Thu Jan 01 00:00:00 1970 +0000 (esc)
1350 \xe2\x94\x82 summary: more (esc)
1350 \xe2\x94\x82 summary: more (esc)
1351 \xe2\x94\x82 (esc)
1351 \xe2\x94\x82 (esc)
1352 \xe2\x97\x8b changeset: 1:5ac72c0599bf (esc)
1352 \xe2\x97\x8b changeset: 1:5ac72c0599bf (esc)
1353 \xe2\x94\x82 user: test (esc)
1353 \xe2\x94\x82 user: test (esc)
1354 \xe2\x95\xa7 date: Thu Jan 01 00:00:00 1970 +0000 (esc)
1354 \xe2\x95\xa7 date: Thu Jan 01 00:00:00 1970 +0000 (esc)
1355 summary: two
1355 summary: two
1356
1356
1357
1357
1358 Issue1896: File log with explicit style
1358 Issue1896: File log with explicit style
1359 $ hg log -G --style=default one
1359 $ hg log -G --style=default one
1360 \xe2\x97\x8b changeset: 0:3d578b4a1f53 (esc)
1360 \xe2\x97\x8b changeset: 0:3d578b4a1f53 (esc)
1361 user: test
1361 user: test
1362 date: Thu Jan 01 00:00:00 1970 +0000
1362 date: Thu Jan 01 00:00:00 1970 +0000
1363 summary: one
1363 summary: one
1364
1364
1365 Issue2395: glog --style header and footer
1365 Issue2395: glog --style header and footer
1366 $ hg log -G --style=xml one
1366 $ hg log -G --style=xml one
1367 <?xml version="1.0"?>
1367 <?xml version="1.0"?>
1368 <log>
1368 <log>
1369 \xe2\x97\x8b <logentry revision="0" node="3d578b4a1f537d5fcf7301bfa9c0b97adfaa6fb1"> (esc)
1369 \xe2\x97\x8b <logentry revision="0" node="3d578b4a1f537d5fcf7301bfa9c0b97adfaa6fb1"> (esc)
1370 <author email="test">test</author>
1370 <author email="test">test</author>
1371 <date>1970-01-01T00:00:00+00:00</date>
1371 <date>1970-01-01T00:00:00+00:00</date>
1372 <msg xml:space="preserve">one</msg>
1372 <msg xml:space="preserve">one</msg>
1373 </logentry>
1373 </logentry>
1374 </log>
1374 </log>
1375
1375
1376 $ cd ..
1376 $ cd ..
1377
1377
1378 Incoming and outgoing:
1378 Incoming and outgoing:
1379
1379
1380 $ hg clone -U -r31 repo repo2
1380 $ hg clone -U -r31 repo repo2
1381 adding changesets
1381 adding changesets
1382 adding manifests
1382 adding manifests
1383 adding file changes
1383 adding file changes
1384 added 31 changesets with 31 changes to 1 files
1384 added 31 changesets with 31 changes to 1 files
1385 new changesets e6eb3150255d:621d83e11f67
1385 new changesets e6eb3150255d:621d83e11f67
1386 $ cd repo2
1386 $ cd repo2
1387
1387
1388 $ hg incoming --graph ../repo
1388 $ hg incoming --graph ../repo
1389 comparing with ../repo
1389 comparing with ../repo
1390 searching for changes
1390 searching for changes
1391 \xe2\x97\x8b changeset: 34:fea3ac5810e0 (esc)
1391 \xe2\x97\x8b changeset: 34:fea3ac5810e0 (esc)
1392 \xe2\x94\x82 tag: tip (esc)
1392 \xe2\x94\x82 tag: tip (esc)
1393 \xe2\x94\x82 parent: 32:d06dffa21a31 (esc)
1393 \xe2\x94\x82 parent: 32:d06dffa21a31 (esc)
1394 \xe2\x94\x82 user: test (esc)
1394 \xe2\x94\x82 user: test (esc)
1395 \xe2\x94\x82 date: Thu Jan 01 00:00:34 1970 +0000 (esc)
1395 \xe2\x94\x82 date: Thu Jan 01 00:00:34 1970 +0000 (esc)
1396 \xe2\x94\x82 summary: (34) head (esc)
1396 \xe2\x94\x82 summary: (34) head (esc)
1397 \xe2\x94\x82 (esc)
1397 \xe2\x94\x82 (esc)
1398 \xe2\x94\x82 \xe2\x97\x8b changeset: 33:68608f5145f9 (esc)
1398 \xe2\x94\x82 \xe2\x97\x8b changeset: 33:68608f5145f9 (esc)
1399 \xe2\x94\x82 parent: 18:1aa84d96232a (esc)
1399 \xe2\x94\x82 parent: 18:1aa84d96232a (esc)
1400 \xe2\x94\x82 user: test (esc)
1400 \xe2\x94\x82 user: test (esc)
1401 \xe2\x94\x82 date: Thu Jan 01 00:00:33 1970 +0000 (esc)
1401 \xe2\x94\x82 date: Thu Jan 01 00:00:33 1970 +0000 (esc)
1402 \xe2\x94\x82 summary: (33) head (esc)
1402 \xe2\x94\x82 summary: (33) head (esc)
1403 \xe2\x94\x82 (esc)
1403 \xe2\x94\x82 (esc)
1404 \xe2\x97\x8b changeset: 32:d06dffa21a31 (esc)
1404 \xe2\x97\x8b changeset: 32:d06dffa21a31 (esc)
1405 \xe2\x94\x82 parent: 27:886ed638191b (esc)
1405 \xe2\x94\x82 parent: 27:886ed638191b (esc)
1406 \xe2\x94\x82 parent: 31:621d83e11f67 (esc)
1406 \xe2\x94\x82 parent: 31:621d83e11f67 (esc)
1407 \xe2\x94\x82 user: test (esc)
1407 \xe2\x94\x82 user: test (esc)
1408 \xe2\x94\x82 date: Thu Jan 01 00:00:32 1970 +0000 (esc)
1408 \xe2\x94\x82 date: Thu Jan 01 00:00:32 1970 +0000 (esc)
1409 \xe2\x94\x82 summary: (32) expand (esc)
1409 \xe2\x94\x82 summary: (32) expand (esc)
1410 \xe2\x94\x82 (esc)
1410 \xe2\x94\x82 (esc)
1411 \xe2\x97\x8b changeset: 27:886ed638191b (esc)
1411 \xe2\x97\x8b changeset: 27:886ed638191b (esc)
1412 parent: 21:d42a756af44d
1412 parent: 21:d42a756af44d
1413 user: test
1413 user: test
1414 date: Thu Jan 01 00:00:27 1970 +0000
1414 date: Thu Jan 01 00:00:27 1970 +0000
1415 summary: (27) collapse
1415 summary: (27) collapse
1416
1416
1417 $ cd ..
1417 $ cd ..
1418
1418
1419 $ hg -R repo outgoing --graph repo2
1419 $ hg -R repo outgoing --graph repo2
1420 comparing with repo2
1420 comparing with repo2
1421 searching for changes
1421 searching for changes
1422 \xe2\x97\x8d changeset: 34:fea3ac5810e0 (esc)
1422 \xe2\x97\x8d changeset: 34:fea3ac5810e0 (esc)
1423 \xe2\x94\x82 tag: tip (esc)
1423 \xe2\x94\x82 tag: tip (esc)
1424 \xe2\x94\x82 parent: 32:d06dffa21a31 (esc)
1424 \xe2\x94\x82 parent: 32:d06dffa21a31 (esc)
1425 \xe2\x94\x82 user: test (esc)
1425 \xe2\x94\x82 user: test (esc)
1426 \xe2\x94\x82 date: Thu Jan 01 00:00:34 1970 +0000 (esc)
1426 \xe2\x94\x82 date: Thu Jan 01 00:00:34 1970 +0000 (esc)
1427 \xe2\x94\x82 summary: (34) head (esc)
1427 \xe2\x94\x82 summary: (34) head (esc)
1428 \xe2\x94\x82 (esc)
1428 \xe2\x94\x82 (esc)
1429 \xe2\x94\x82 \xe2\x97\x8b changeset: 33:68608f5145f9 (esc)
1429 \xe2\x94\x82 \xe2\x97\x8b changeset: 33:68608f5145f9 (esc)
1430 \xe2\x94\x82 parent: 18:1aa84d96232a (esc)
1430 \xe2\x94\x82 parent: 18:1aa84d96232a (esc)
1431 \xe2\x94\x82 user: test (esc)
1431 \xe2\x94\x82 user: test (esc)
1432 \xe2\x94\x82 date: Thu Jan 01 00:00:33 1970 +0000 (esc)
1432 \xe2\x94\x82 date: Thu Jan 01 00:00:33 1970 +0000 (esc)
1433 \xe2\x94\x82 summary: (33) head (esc)
1433 \xe2\x94\x82 summary: (33) head (esc)
1434 \xe2\x94\x82 (esc)
1434 \xe2\x94\x82 (esc)
1435 \xe2\x97\x8b changeset: 32:d06dffa21a31 (esc)
1435 \xe2\x97\x8b changeset: 32:d06dffa21a31 (esc)
1436 \xe2\x94\x82 parent: 27:886ed638191b (esc)
1436 \xe2\x94\x82 parent: 27:886ed638191b (esc)
1437 \xe2\x94\x82 parent: 31:621d83e11f67 (esc)
1437 \xe2\x94\x82 parent: 31:621d83e11f67 (esc)
1438 \xe2\x94\x82 user: test (esc)
1438 \xe2\x94\x82 user: test (esc)
1439 \xe2\x94\x82 date: Thu Jan 01 00:00:32 1970 +0000 (esc)
1439 \xe2\x94\x82 date: Thu Jan 01 00:00:32 1970 +0000 (esc)
1440 \xe2\x94\x82 summary: (32) expand (esc)
1440 \xe2\x94\x82 summary: (32) expand (esc)
1441 \xe2\x94\x82 (esc)
1441 \xe2\x94\x82 (esc)
1442 \xe2\x97\x8b changeset: 27:886ed638191b (esc)
1442 \xe2\x97\x8b changeset: 27:886ed638191b (esc)
1443 parent: 21:d42a756af44d
1443 parent: 21:d42a756af44d
1444 user: test
1444 user: test
1445 date: Thu Jan 01 00:00:27 1970 +0000
1445 date: Thu Jan 01 00:00:27 1970 +0000
1446 summary: (27) collapse
1446 summary: (27) collapse
1447
1447
1448
1448
1449 File + limit with revs != cset revs:
1449 File + limit with revs != cset revs:
1450 $ cd repo
1450 $ cd repo
1451 $ touch b
1451 $ touch b
1452 $ hg ci -Aqm0
1452 $ hg ci -Aqm0
1453 $ hg log -G -l2 a
1453 $ hg log -G -l2 a
1454 \xe2\x97\x8b changeset: 34:fea3ac5810e0 (esc)
1454 \xe2\x97\x8b changeset: 34:fea3ac5810e0 (esc)
1455 \xe2\x94\x82 parent: 32:d06dffa21a31 (esc)
1455 \xe2\x94\x82 parent: 32:d06dffa21a31 (esc)
1456 \xe2\x95\xa7 user: test (esc)
1456 \xe2\x95\xa7 user: test (esc)
1457 date: Thu Jan 01 00:00:34 1970 +0000
1457 date: Thu Jan 01 00:00:34 1970 +0000
1458 summary: (34) head
1458 summary: (34) head
1459
1459
1460 \xe2\x97\x8b changeset: 33:68608f5145f9 (esc)
1460 \xe2\x97\x8b changeset: 33:68608f5145f9 (esc)
1461 \xe2\x94\x82 parent: 18:1aa84d96232a (esc)
1461 \xe2\x94\x82 parent: 18:1aa84d96232a (esc)
1462 \xe2\x95\xa7 user: test (esc)
1462 \xe2\x95\xa7 user: test (esc)
1463 date: Thu Jan 01 00:00:33 1970 +0000
1463 date: Thu Jan 01 00:00:33 1970 +0000
1464 summary: (33) head
1464 summary: (33) head
1465
1465
1466
1466
1467 File + limit + -ra:b, (b - a) < limit:
1467 File + limit + -ra:b, (b - a) < limit:
1468 $ hg log -G -l3000 -r32:tip a
1468 $ hg log -G -l3000 -r32:tip a
1469 \xe2\x97\x8b changeset: 34:fea3ac5810e0 (esc)
1469 \xe2\x97\x8b changeset: 34:fea3ac5810e0 (esc)
1470 \xe2\x94\x82 parent: 32:d06dffa21a31 (esc)
1470 \xe2\x94\x82 parent: 32:d06dffa21a31 (esc)
1471 \xe2\x94\x82 user: test (esc)
1471 \xe2\x94\x82 user: test (esc)
1472 \xe2\x94\x82 date: Thu Jan 01 00:00:34 1970 +0000 (esc)
1472 \xe2\x94\x82 date: Thu Jan 01 00:00:34 1970 +0000 (esc)
1473 \xe2\x94\x82 summary: (34) head (esc)
1473 \xe2\x94\x82 summary: (34) head (esc)
1474 \xe2\x94\x82 (esc)
1474 \xe2\x94\x82 (esc)
1475 \xe2\x94\x82 \xe2\x97\x8b changeset: 33:68608f5145f9 (esc)
1475 \xe2\x94\x82 \xe2\x97\x8b changeset: 33:68608f5145f9 (esc)
1476 \xe2\x94\x82 \xe2\x94\x82 parent: 18:1aa84d96232a (esc)
1476 \xe2\x94\x82 \xe2\x94\x82 parent: 18:1aa84d96232a (esc)
1477 \xe2\x94\x82 \xe2\x95\xa7 user: test (esc)
1477 \xe2\x94\x82 \xe2\x95\xa7 user: test (esc)
1478 \xe2\x94\x82 date: Thu Jan 01 00:00:33 1970 +0000 (esc)
1478 \xe2\x94\x82 date: Thu Jan 01 00:00:33 1970 +0000 (esc)
1479 \xe2\x94\x82 summary: (33) head (esc)
1479 \xe2\x94\x82 summary: (33) head (esc)
1480 \xe2\x94\x82 (esc)
1480 \xe2\x94\x82 (esc)
1481 \xe2\x97\x8b changeset: 32:d06dffa21a31 (esc)
1481 \xe2\x97\x8b changeset: 32:d06dffa21a31 (esc)
1482 \xe2\x94\x82\xe2\x95\xb2 parent: 27:886ed638191b (esc)
1482 \xe2\x94\x82\xe2\x95\xb2 parent: 27:886ed638191b (esc)
1483 \xe2\x95\xa7 \xe2\x95\xa7 parent: 31:621d83e11f67 (esc)
1483 \xe2\x95\xa7 \xe2\x95\xa7 parent: 31:621d83e11f67 (esc)
1484 user: test
1484 user: test
1485 date: Thu Jan 01 00:00:32 1970 +0000
1485 date: Thu Jan 01 00:00:32 1970 +0000
1486 summary: (32) expand
1486 summary: (32) expand
1487
1487
1488
1488
1489 Point out a common and an uncommon unshown parent
1489 Point out a common and an uncommon unshown parent
1490
1490
1491 $ hg log -G -r 'rev(8) or rev(9)'
1491 $ hg log -G -r 'rev(8) or rev(9)'
1492 \xe2\x97\x8b changeset: 9:7010c0af0a35 (esc)
1492 \xe2\x97\x8b changeset: 9:7010c0af0a35 (esc)
1493 \xe2\x94\x82\xe2\x95\xb2 parent: 7:b632bb1b1224 (esc)
1493 \xe2\x94\x82\xe2\x95\xb2 parent: 7:b632bb1b1224 (esc)
1494 \xe2\x94\x82 \xe2\x95\xa7 parent: 8:7a0b11f71937 (esc)
1494 \xe2\x94\x82 \xe2\x95\xa7 parent: 8:7a0b11f71937 (esc)
1495 \xe2\x94\x82 user: test (esc)
1495 \xe2\x94\x82 user: test (esc)
1496 \xe2\x94\x82 date: Thu Jan 01 00:00:09 1970 +0000 (esc)
1496 \xe2\x94\x82 date: Thu Jan 01 00:00:09 1970 +0000 (esc)
1497 \xe2\x94\x82 summary: (9) expand (esc)
1497 \xe2\x94\x82 summary: (9) expand (esc)
1498 \xe2\x94\x82 (esc)
1498 \xe2\x94\x82 (esc)
1499 \xe2\x97\x8b changeset: 8:7a0b11f71937 (esc)
1499 \xe2\x97\x8b changeset: 8:7a0b11f71937 (esc)
1500 \xe2\x94\x82\xe2\x95\xb2 parent: 0:e6eb3150255d (esc)
1500 \xe2\x94\x82\xe2\x95\xb2 parent: 0:e6eb3150255d (esc)
1501 \xe2\x95\xa7 \xe2\x95\xa7 parent: 7:b632bb1b1224 (esc)
1501 \xe2\x95\xa7 \xe2\x95\xa7 parent: 7:b632bb1b1224 (esc)
1502 user: test
1502 user: test
1503 date: Thu Jan 01 00:00:08 1970 +0000
1503 date: Thu Jan 01 00:00:08 1970 +0000
1504 summary: (8) merge two known; one immediate left, one far right
1504 summary: (8) merge two known; one immediate left, one far right
1505
1505
1506
1506
1507 File + limit + -ra:b, b < tip:
1507 File + limit + -ra:b, b < tip:
1508
1508
1509 $ hg log -G -l1 -r32:34 a
1509 $ hg log -G -l1 -r32:34 a
1510 \xe2\x97\x8b changeset: 34:fea3ac5810e0 (esc)
1510 \xe2\x97\x8b changeset: 34:fea3ac5810e0 (esc)
1511 \xe2\x94\x82 parent: 32:d06dffa21a31 (esc)
1511 \xe2\x94\x82 parent: 32:d06dffa21a31 (esc)
1512 \xe2\x95\xa7 user: test (esc)
1512 \xe2\x95\xa7 user: test (esc)
1513 date: Thu Jan 01 00:00:34 1970 +0000
1513 date: Thu Jan 01 00:00:34 1970 +0000
1514 summary: (34) head
1514 summary: (34) head
1515
1515
1516
1516
1517 file(File) + limit + -ra:b, b < tip:
1517 file(File) + limit + -ra:b, b < tip:
1518
1518
1519 $ hg log -G -l1 -r32:34 -r 'file("a")'
1519 $ hg log -G -l1 -r32:34 -r 'file("a")'
1520 \xe2\x97\x8b changeset: 34:fea3ac5810e0 (esc)
1520 \xe2\x97\x8b changeset: 34:fea3ac5810e0 (esc)
1521 \xe2\x94\x82 parent: 32:d06dffa21a31 (esc)
1521 \xe2\x94\x82 parent: 32:d06dffa21a31 (esc)
1522 \xe2\x95\xa7 user: test (esc)
1522 \xe2\x95\xa7 user: test (esc)
1523 date: Thu Jan 01 00:00:34 1970 +0000
1523 date: Thu Jan 01 00:00:34 1970 +0000
1524 summary: (34) head
1524 summary: (34) head
1525
1525
1526
1526
1527 limit(file(File) and a::b), b < tip:
1527 limit(file(File) and a::b), b < tip:
1528
1528
1529 $ hg log -G -r 'limit(file("a") and 32::34, 1)'
1529 $ hg log -G -r 'limit(file("a") and 32::34, 1)'
1530 \xe2\x97\x8b changeset: 32:d06dffa21a31 (esc)
1530 \xe2\x97\x8b changeset: 32:d06dffa21a31 (esc)
1531 \xe2\x94\x82\xe2\x95\xb2 parent: 27:886ed638191b (esc)
1531 \xe2\x94\x82\xe2\x95\xb2 parent: 27:886ed638191b (esc)
1532 \xe2\x95\xa7 \xe2\x95\xa7 parent: 31:621d83e11f67 (esc)
1532 \xe2\x95\xa7 \xe2\x95\xa7 parent: 31:621d83e11f67 (esc)
1533 user: test
1533 user: test
1534 date: Thu Jan 01 00:00:32 1970 +0000
1534 date: Thu Jan 01 00:00:32 1970 +0000
1535 summary: (32) expand
1535 summary: (32) expand
1536
1536
1537
1537
1538 File + limit + -ra:b, b < tip:
1538 File + limit + -ra:b, b < tip:
1539
1539
1540 $ hg log -G -r 'limit(file("a") and 34::32, 1)'
1540 $ hg log -G -r 'limit(file("a") and 34::32, 1)'
1541
1541
1542 File + limit + -ra:b, b < tip, (b - a) < limit:
1542 File + limit + -ra:b, b < tip, (b - a) < limit:
1543
1543
1544 $ hg log -G -l10 -r33:34 a
1544 $ hg log -G -l10 -r33:34 a
1545 \xe2\x97\x8b changeset: 34:fea3ac5810e0 (esc)
1545 \xe2\x97\x8b changeset: 34:fea3ac5810e0 (esc)
1546 \xe2\x94\x82 parent: 32:d06dffa21a31 (esc)
1546 \xe2\x94\x82 parent: 32:d06dffa21a31 (esc)
1547 \xe2\x95\xa7 user: test (esc)
1547 \xe2\x95\xa7 user: test (esc)
1548 date: Thu Jan 01 00:00:34 1970 +0000
1548 date: Thu Jan 01 00:00:34 1970 +0000
1549 summary: (34) head
1549 summary: (34) head
1550
1550
1551 \xe2\x97\x8b changeset: 33:68608f5145f9 (esc)
1551 \xe2\x97\x8b changeset: 33:68608f5145f9 (esc)
1552 \xe2\x94\x82 parent: 18:1aa84d96232a (esc)
1552 \xe2\x94\x82 parent: 18:1aa84d96232a (esc)
1553 \xe2\x95\xa7 user: test (esc)
1553 \xe2\x95\xa7 user: test (esc)
1554 date: Thu Jan 01 00:00:33 1970 +0000
1554 date: Thu Jan 01 00:00:33 1970 +0000
1555 summary: (33) head
1555 summary: (33) head
1556
1556
1557
1557
1558 Do not crash or produce strange graphs if history is buggy
1558 Do not crash or produce strange graphs if history is buggy
1559
1559
1560 $ hg branch branch
1560 $ hg branch branch
1561 marked working directory as branch branch
1561 marked working directory as branch branch
1562 (branches are permanent and global, did you want a bookmark?)
1562 (branches are permanent and global, did you want a bookmark?)
1563 $ commit 36 "buggy merge: identical parents" 35 35
1563 $ commit 36 "buggy merge: identical parents" 35 35
1564 $ hg log -G -l5
1564 $ hg log -G -l5
1565 \xe2\x97\x8d changeset: 36:08a19a744424 (esc)
1565 \xe2\x97\x8d changeset: 36:08a19a744424 (esc)
1566 \xe2\x94\x82 branch: branch (esc)
1566 \xe2\x94\x82 branch: branch (esc)
1567 \xe2\x94\x82 tag: tip (esc)
1567 \xe2\x94\x82 tag: tip (esc)
1568 \xe2\x94\x82 parent: 35:9159c3644c5e (esc)
1568 \xe2\x94\x82 parent: 35:9159c3644c5e (esc)
1569 \xe2\x94\x82 parent: 35:9159c3644c5e (esc)
1569 \xe2\x94\x82 parent: 35:9159c3644c5e (esc)
1570 \xe2\x94\x82 user: test (esc)
1570 \xe2\x94\x82 user: test (esc)
1571 \xe2\x94\x82 date: Thu Jan 01 00:00:36 1970 +0000 (esc)
1571 \xe2\x94\x82 date: Thu Jan 01 00:00:36 1970 +0000 (esc)
1572 \xe2\x94\x82 summary: (36) buggy merge: identical parents (esc)
1572 \xe2\x94\x82 summary: (36) buggy merge: identical parents (esc)
1573 \xe2\x94\x82 (esc)
1573 \xe2\x94\x82 (esc)
1574 \xe2\x97\x8b changeset: 35:9159c3644c5e (esc)
1574 \xe2\x97\x8b changeset: 35:9159c3644c5e (esc)
1575 \xe2\x94\x82 user: test (esc)
1575 \xe2\x94\x82 user: test (esc)
1576 \xe2\x94\x82 date: Thu Jan 01 00:00:00 1970 +0000 (esc)
1576 \xe2\x94\x82 date: Thu Jan 01 00:00:00 1970 +0000 (esc)
1577 \xe2\x94\x82 summary: 0 (esc)
1577 \xe2\x94\x82 summary: 0 (esc)
1578 \xe2\x94\x82 (esc)
1578 \xe2\x94\x82 (esc)
1579 \xe2\x97\x8b changeset: 34:fea3ac5810e0 (esc)
1579 \xe2\x97\x8b changeset: 34:fea3ac5810e0 (esc)
1580 \xe2\x94\x82 parent: 32:d06dffa21a31 (esc)
1580 \xe2\x94\x82 parent: 32:d06dffa21a31 (esc)
1581 \xe2\x94\x82 user: test (esc)
1581 \xe2\x94\x82 user: test (esc)
1582 \xe2\x94\x82 date: Thu Jan 01 00:00:34 1970 +0000 (esc)
1582 \xe2\x94\x82 date: Thu Jan 01 00:00:34 1970 +0000 (esc)
1583 \xe2\x94\x82 summary: (34) head (esc)
1583 \xe2\x94\x82 summary: (34) head (esc)
1584 \xe2\x94\x82 (esc)
1584 \xe2\x94\x82 (esc)
1585 \xe2\x94\x82 \xe2\x97\x8b changeset: 33:68608f5145f9 (esc)
1585 \xe2\x94\x82 \xe2\x97\x8b changeset: 33:68608f5145f9 (esc)
1586 \xe2\x94\x82 \xe2\x94\x82 parent: 18:1aa84d96232a (esc)
1586 \xe2\x94\x82 \xe2\x94\x82 parent: 18:1aa84d96232a (esc)
1587 \xe2\x94\x82 \xe2\x95\xa7 user: test (esc)
1587 \xe2\x94\x82 \xe2\x95\xa7 user: test (esc)
1588 \xe2\x94\x82 date: Thu Jan 01 00:00:33 1970 +0000 (esc)
1588 \xe2\x94\x82 date: Thu Jan 01 00:00:33 1970 +0000 (esc)
1589 \xe2\x94\x82 summary: (33) head (esc)
1589 \xe2\x94\x82 summary: (33) head (esc)
1590 \xe2\x94\x82 (esc)
1590 \xe2\x94\x82 (esc)
1591 \xe2\x97\x8b changeset: 32:d06dffa21a31 (esc)
1591 \xe2\x97\x8b changeset: 32:d06dffa21a31 (esc)
1592 \xe2\x94\x82\xe2\x95\xb2 parent: 27:886ed638191b (esc)
1592 \xe2\x94\x82\xe2\x95\xb2 parent: 27:886ed638191b (esc)
1593 \xe2\x95\xa7 \xe2\x95\xa7 parent: 31:621d83e11f67 (esc)
1593 \xe2\x95\xa7 \xe2\x95\xa7 parent: 31:621d83e11f67 (esc)
1594 user: test
1594 user: test
1595 date: Thu Jan 01 00:00:32 1970 +0000
1595 date: Thu Jan 01 00:00:32 1970 +0000
1596 summary: (32) expand
1596 summary: (32) expand
1597
1597
1598
1598
1599 Test log -G options
1599 Test log -G options
1600
1600
1601 $ testlog() {
1601 $ testlog() {
1602 > hg log -G --print-revset "$@"
1602 > hg log -G --print-revset "$@"
1603 > hg log --template 'nodetag {rev}\n' "$@" | grep nodetag \
1603 > hg log --template 'nodetag {rev}\n' "$@" | grep nodetag \
1604 > | sed 's/.*nodetag/nodetag/' > log.nodes
1604 > | sed 's/.*nodetag/nodetag/' > log.nodes
1605 > hg log -G --template 'nodetag {rev}\n' "$@" | grep nodetag \
1605 > hg log -G --template 'nodetag {rev}\n' "$@" | grep nodetag \
1606 > | sed 's/.*nodetag/nodetag/' > glog.nodes
1606 > | sed 's/.*nodetag/nodetag/' > glog.nodes
1607 > (cmp log.nodes glog.nodes || diff -u log.nodes glog.nodes) \
1607 > (cmp log.nodes glog.nodes || diff -u log.nodes glog.nodes) \
1608 > | grep '^[-+@ ]' || :
1608 > | grep '^[-+@ ]' || :
1609 > }
1609 > }
1610
1610
1611 glog always reorders nodes which explains the difference with log
1611 glog always reorders nodes which explains the difference with log
1612
1612
1613 $ testlog -r 27 -r 25 -r 21 -r 34 -r 32 -r 31
1613 $ testlog -r 27 -r 25 -r 21 -r 34 -r 32 -r 31
1614 ['27', '25', '21', '34', '32', '31']
1614 ['27', '25', '21', '34', '32', '31']
1615 []
1615 []
1616 <baseset- [21, 25, 27, 31, 32, 34]>
1616 <baseset- [21, 25, 27, 31, 32, 34]>
1617 --- log.nodes * (glob)
1617 --- log.nodes * (glob)
1618 +++ glog.nodes * (glob)
1618 +++ glog.nodes * (glob)
1619 @@ -1,6 +1,6 @@
1619 @@ -1,6 +1,6 @@
1620 -nodetag 27
1620 -nodetag 27
1621 -nodetag 25
1621 -nodetag 25
1622 -nodetag 21
1622 -nodetag 21
1623 nodetag 34
1623 nodetag 34
1624 nodetag 32
1624 nodetag 32
1625 nodetag 31
1625 nodetag 31
1626 +nodetag 27
1626 +nodetag 27
1627 +nodetag 25
1627 +nodetag 25
1628 +nodetag 21
1628 +nodetag 21
1629 $ testlog -u test -u not-a-user
1629 $ testlog -u test -u not-a-user
1630 []
1630 []
1631 (or
1631 (or
1632 (list
1632 (list
1633 (func
1633 (func
1634 (symbol 'user')
1634 (symbol 'user')
1635 (string 'test'))
1635 (string 'test'))
1636 (func
1636 (func
1637 (symbol 'user')
1637 (symbol 'user')
1638 (string 'not-a-user'))))
1638 (string 'not-a-user'))))
1639 <filteredset
1639 <filteredset
1640 <spanset- 0:37>,
1640 <spanset- 0:37>,
1641 <addset
1641 <addset
1642 <filteredset
1642 <filteredset
1643 <fullreposet+ 0:37>,
1643 <fullreposet+ 0:37>,
1644 <user 'test'>>,
1644 <user 'test'>>,
1645 <filteredset
1645 <filteredset
1646 <fullreposet+ 0:37>,
1646 <fullreposet+ 0:37>,
1647 <user 'not-a-user'>>>>
1647 <user 'not-a-user'>>>>
1648 $ testlog -b not-a-branch
1648 $ testlog -b not-a-branch
1649 abort: unknown revision 'not-a-branch'!
1649 abort: unknown revision 'not-a-branch'!
1650 abort: unknown revision 'not-a-branch'!
1650 abort: unknown revision 'not-a-branch'!
1651 abort: unknown revision 'not-a-branch'!
1651 abort: unknown revision 'not-a-branch'!
1652 $ testlog -b 35 -b 36 --only-branch branch
1652 $ testlog -b 35 -b 36 --only-branch branch
1653 []
1653 []
1654 (or
1654 (or
1655 (list
1655 (list
1656 (func
1656 (func
1657 (symbol 'branch')
1657 (symbol 'branch')
1658 (string 'default'))
1658 (string 'default'))
1659 (or
1659 (or
1660 (list
1660 (list
1661 (func
1661 (func
1662 (symbol 'branch')
1662 (symbol 'branch')
1663 (string 'branch'))
1663 (string 'branch'))
1664 (func
1664 (func
1665 (symbol 'branch')
1665 (symbol 'branch')
1666 (string 'branch'))))))
1666 (string 'branch'))))))
1667 <filteredset
1667 <filteredset
1668 <spanset- 0:37>,
1668 <spanset- 0:37>,
1669 <addset
1669 <addset
1670 <filteredset
1670 <filteredset
1671 <fullreposet+ 0:37>,
1671 <fullreposet+ 0:37>,
1672 <branch 'default'>>,
1672 <branch 'default'>>,
1673 <addset
1673 <addset
1674 <filteredset
1674 <filteredset
1675 <fullreposet+ 0:37>,
1675 <fullreposet+ 0:37>,
1676 <branch 'branch'>>,
1676 <branch 'branch'>>,
1677 <filteredset
1677 <filteredset
1678 <fullreposet+ 0:37>,
1678 <fullreposet+ 0:37>,
1679 <branch 'branch'>>>>>
1679 <branch 'branch'>>>>>
1680 $ testlog -k expand -k merge
1680 $ testlog -k expand -k merge
1681 []
1681 []
1682 (or
1682 (or
1683 (list
1683 (list
1684 (func
1684 (func
1685 (symbol 'keyword')
1685 (symbol 'keyword')
1686 (string 'expand'))
1686 (string 'expand'))
1687 (func
1687 (func
1688 (symbol 'keyword')
1688 (symbol 'keyword')
1689 (string 'merge'))))
1689 (string 'merge'))))
1690 <filteredset
1690 <filteredset
1691 <spanset- 0:37>,
1691 <spanset- 0:37>,
1692 <addset
1692 <addset
1693 <filteredset
1693 <filteredset
1694 <fullreposet+ 0:37>,
1694 <fullreposet+ 0:37>,
1695 <keyword 'expand'>>,
1695 <keyword 'expand'>>,
1696 <filteredset
1696 <filteredset
1697 <fullreposet+ 0:37>,
1697 <fullreposet+ 0:37>,
1698 <keyword 'merge'>>>>
1698 <keyword 'merge'>>>>
1699 $ testlog --only-merges
1699 $ testlog --only-merges
1700 []
1700 []
1701 (func
1701 (func
1702 (symbol 'merge')
1702 (symbol 'merge')
1703 None)
1703 None)
1704 <filteredset
1704 <filteredset
1705 <spanset- 0:37>,
1705 <spanset- 0:37>,
1706 <merge>>
1706 <merge>>
1707 $ testlog --no-merges
1707 $ testlog --no-merges
1708 []
1708 []
1709 (not
1709 (not
1710 (func
1710 (func
1711 (symbol 'merge')
1711 (symbol 'merge')
1712 None))
1712 None))
1713 <filteredset
1713 <filteredset
1714 <spanset- 0:37>,
1714 <spanset- 0:37>,
1715 <not
1715 <not
1716 <filteredset
1716 <filteredset
1717 <spanset- 0:37>,
1717 <spanset- 0:37>,
1718 <merge>>>>
1718 <merge>>>>
1719 $ testlog --date '2 0 to 4 0'
1719 $ testlog --date '2 0 to 4 0'
1720 []
1720 []
1721 (func
1721 (func
1722 (symbol 'date')
1722 (symbol 'date')
1723 (string '2 0 to 4 0'))
1723 (string '2 0 to 4 0'))
1724 <filteredset
1724 <filteredset
1725 <spanset- 0:37>,
1725 <spanset- 0:37>,
1726 <date '2 0 to 4 0'>>
1726 <date '2 0 to 4 0'>>
1727 $ hg log -G -d 'brace ) in a date'
1727 $ hg log -G -d 'brace ) in a date'
1728 hg: parse error: invalid date: 'brace ) in a date'
1728 hg: parse error: invalid date: 'brace ) in a date'
1729 [255]
1729 [255]
1730 $ testlog --prune 31 --prune 32
1730 $ testlog --prune 31 --prune 32
1731 []
1731 []
1732 (not
1732 (not
1733 (or
1733 (or
1734 (list
1734 (list
1735 (func
1735 (func
1736 (symbol 'ancestors')
1736 (symbol 'ancestors')
1737 (string '31'))
1737 (string '31'))
1738 (func
1738 (func
1739 (symbol 'ancestors')
1739 (symbol 'ancestors')
1740 (string '32')))))
1740 (string '32')))))
1741 <filteredset
1741 <filteredset
1742 <spanset- 0:37>,
1742 <spanset- 0:37>,
1743 <not
1743 <not
1744 <addset
1744 <addset
1745 <filteredset
1745 <filteredset
1746 <spanset- 0:37>,
1746 <spanset- 0:37>,
1747 <generatorsetdesc+>>,
1747 <generatorsetdesc+>>,
1748 <filteredset
1748 <filteredset
1749 <spanset- 0:37>,
1749 <spanset- 0:37>,
1750 <generatorsetdesc+>>>>>
1750 <generatorsetdesc+>>>>>
1751
1751
1752 Dedicated repo for --follow and paths filtering. The g is crafted to
1752 Dedicated repo for --follow and paths filtering. The g is crafted to
1753 have 2 filelog topological heads in a linear changeset graph.
1753 have 2 filelog topological heads in a linear changeset graph.
1754
1754
1755 $ cd ..
1755 $ cd ..
1756 $ hg init follow
1756 $ hg init follow
1757 $ cd follow
1757 $ cd follow
1758 $ testlog --follow
1758 $ testlog --follow
1759 []
1759 []
1760 []
1760 []
1761 <baseset []>
1761 <baseset []>
1762 $ testlog -rnull
1762 $ testlog -rnull
1763 ['null']
1763 ['null']
1764 []
1764 []
1765 <baseset [-1]>
1765 <baseset [-1]>
1766 $ echo a > a
1766 $ echo a > a
1767 $ echo aa > aa
1767 $ echo aa > aa
1768 $ echo f > f
1768 $ echo f > f
1769 $ hg ci -Am "add a" a aa f
1769 $ hg ci -Am "add a" a aa f
1770 $ hg cp a b
1770 $ hg cp a b
1771 $ hg cp f g
1771 $ hg cp f g
1772 $ hg ci -m "copy a b"
1772 $ hg ci -m "copy a b"
1773 $ mkdir dir
1773 $ mkdir dir
1774 $ hg mv b dir
1774 $ hg mv b dir
1775 $ echo g >> g
1775 $ echo g >> g
1776 $ echo f >> f
1776 $ echo f >> f
1777 $ hg ci -m "mv b dir/b"
1777 $ hg ci -m "mv b dir/b"
1778 $ hg mv a b
1778 $ hg mv a b
1779 $ hg cp -f f g
1779 $ hg cp -f f g
1780 $ echo a > d
1780 $ echo a > d
1781 $ hg add d
1781 $ hg add d
1782 $ hg ci -m "mv a b; add d"
1782 $ hg ci -m "mv a b; add d"
1783 $ hg mv dir/b e
1783 $ hg mv dir/b e
1784 $ hg ci -m "mv dir/b e"
1784 $ hg ci -m "mv dir/b e"
1785 $ hg log -G --template '({rev}) {desc|firstline}\n'
1785 $ hg log -G --template '({rev}) {desc|firstline}\n'
1786 \xe2\x97\x8d (4) mv dir/b e (esc)
1786 \xe2\x97\x8d (4) mv dir/b e (esc)
1787 \xe2\x94\x82 (esc)
1787 \xe2\x94\x82 (esc)
1788 \xe2\x97\x8b (3) mv a b; add d (esc)
1788 \xe2\x97\x8b (3) mv a b; add d (esc)
1789 \xe2\x94\x82 (esc)
1789 \xe2\x94\x82 (esc)
1790 \xe2\x97\x8b (2) mv b dir/b (esc)
1790 \xe2\x97\x8b (2) mv b dir/b (esc)
1791 \xe2\x94\x82 (esc)
1791 \xe2\x94\x82 (esc)
1792 \xe2\x97\x8b (1) copy a b (esc)
1792 \xe2\x97\x8b (1) copy a b (esc)
1793 \xe2\x94\x82 (esc)
1793 \xe2\x94\x82 (esc)
1794 \xe2\x97\x8b (0) add a (esc)
1794 \xe2\x97\x8b (0) add a (esc)
1795
1795
1796
1796
1797 $ testlog a
1797 $ testlog a
1798 []
1798 []
1799 (func
1799 (func
1800 (symbol 'filelog')
1800 (symbol 'filelog')
1801 (string 'a'))
1801 (string 'a'))
1802 <filteredset
1802 <filteredset
1803 <spanset- 0:5>, set([0])>
1803 <spanset- 0:5>, set([0])>
1804 $ testlog a b
1804 $ testlog a b
1805 []
1805 []
1806 (or
1806 (or
1807 (list
1807 (list
1808 (func
1808 (func
1809 (symbol 'filelog')
1809 (symbol 'filelog')
1810 (string 'a'))
1810 (string 'a'))
1811 (func
1811 (func
1812 (symbol 'filelog')
1812 (symbol 'filelog')
1813 (string 'b'))))
1813 (string 'b'))))
1814 <filteredset
1814 <filteredset
1815 <spanset- 0:5>,
1815 <spanset- 0:5>,
1816 <addset
1816 <addset
1817 <baseset+ [0]>,
1817 <baseset+ [0]>,
1818 <baseset+ [1]>>>
1818 <baseset+ [1]>>>
1819
1819
1820 Test falling back to slow path for non-existing files
1820 Test falling back to slow path for non-existing files
1821
1821
1822 $ testlog a c
1822 $ testlog a c
1823 []
1823 []
1824 (func
1824 (func
1825 (symbol '_matchfiles')
1825 (symbol '_matchfiles')
1826 (list
1826 (list
1827 (string 'r:')
1827 (string 'r:')
1828 (string 'd:relpath')
1828 (string 'd:relpath')
1829 (string 'p:a')
1829 (string 'p:a')
1830 (string 'p:c')))
1830 (string 'p:c')))
1831 <filteredset
1831 <filteredset
1832 <spanset- 0:5>,
1832 <spanset- 0:5>,
1833 <matchfiles patterns=['a', 'c'], include=[] exclude=[], default='relpath', rev=2147483647>>
1833 <matchfiles patterns=['a', 'c'], include=[] exclude=[], default='relpath', rev=2147483647>>
1834
1834
1835 Test multiple --include/--exclude/paths
1835 Test multiple --include/--exclude/paths
1836
1836
1837 $ testlog --include a --include e --exclude b --exclude e a e
1837 $ testlog --include a --include e --exclude b --exclude e a e
1838 []
1838 []
1839 (func
1839 (func
1840 (symbol '_matchfiles')
1840 (symbol '_matchfiles')
1841 (list
1841 (list
1842 (string 'r:')
1842 (string 'r:')
1843 (string 'd:relpath')
1843 (string 'd:relpath')
1844 (string 'p:a')
1844 (string 'p:a')
1845 (string 'p:e')
1845 (string 'p:e')
1846 (string 'i:a')
1846 (string 'i:a')
1847 (string 'i:e')
1847 (string 'i:e')
1848 (string 'x:b')
1848 (string 'x:b')
1849 (string 'x:e')))
1849 (string 'x:e')))
1850 <filteredset
1850 <filteredset
1851 <spanset- 0:5>,
1851 <spanset- 0:5>,
1852 <matchfiles patterns=['a', 'e'], include=['a', 'e'] exclude=['b', 'e'], default='relpath', rev=2147483647>>
1852 <matchfiles patterns=['a', 'e'], include=['a', 'e'] exclude=['b', 'e'], default='relpath', rev=2147483647>>
1853
1853
1854 Test glob expansion of pats
1854 Test glob expansion of pats
1855
1855
1856 $ expandglobs=`$PYTHON -c "import mercurial.util; \
1856 $ expandglobs=`$PYTHON -c "import mercurial.util; \
1857 > print(mercurial.util.expandglobs and 'true' or 'false')"`
1857 > print(mercurial.util.expandglobs and 'true' or 'false')"`
1858 $ if [ $expandglobs = "true" ]; then
1858 $ if [ $expandglobs = "true" ]; then
1859 > testlog 'a*';
1859 > testlog 'a*';
1860 > else
1860 > else
1861 > testlog a*;
1861 > testlog a*;
1862 > fi;
1862 > fi;
1863 []
1863 []
1864 (func
1864 (func
1865 (symbol 'filelog')
1865 (symbol 'filelog')
1866 (string 'aa'))
1866 (string 'aa'))
1867 <filteredset
1867 <filteredset
1868 <spanset- 0:5>, set([0])>
1868 <spanset- 0:5>, set([0])>
1869
1869
1870 Test --follow on a non-existent directory
1870 Test --follow on a non-existent directory
1871
1871
1872 $ testlog -f dir
1872 $ testlog -f dir
1873 abort: cannot follow file not in parent revision: "dir"
1873 abort: cannot follow file not in parent revision: "dir"
1874 abort: cannot follow file not in parent revision: "dir"
1874 abort: cannot follow file not in parent revision: "dir"
1875 abort: cannot follow file not in parent revision: "dir"
1875 abort: cannot follow file not in parent revision: "dir"
1876
1876
1877 Test --follow on a directory
1877 Test --follow on a directory
1878
1878
1879 $ hg up -q '.^'
1879 $ hg up -q '.^'
1880 $ testlog -f dir
1880 $ testlog -f dir
1881 []
1881 []
1882 (func
1882 (func
1883 (symbol '_matchfiles')
1883 (symbol '_matchfiles')
1884 (list
1884 (list
1885 (string 'r:')
1885 (string 'r:')
1886 (string 'd:relpath')
1886 (string 'd:relpath')
1887 (string 'p:dir')))
1887 (string 'p:dir')))
1888 <filteredset
1888 <filteredset
1889 <generatorsetdesc->,
1889 <generatorsetdesc->,
1890 <matchfiles patterns=['dir'], include=[] exclude=[], default='relpath', rev=2147483647>>
1890 <matchfiles patterns=['dir'], include=[] exclude=[], default='relpath', rev=2147483647>>
1891 $ hg up -q tip
1891 $ hg up -q tip
1892
1892
1893 Test --follow on file not in parent revision
1893 Test --follow on file not in parent revision
1894
1894
1895 $ testlog -f a
1895 $ testlog -f a
1896 abort: cannot follow file not in parent revision: "a"
1896 abort: cannot follow file not in parent revision: "a"
1897 abort: cannot follow file not in parent revision: "a"
1897 abort: cannot follow file not in parent revision: "a"
1898 abort: cannot follow file not in parent revision: "a"
1898 abort: cannot follow file not in parent revision: "a"
1899
1899
1900 Test --follow and patterns
1900 Test --follow and patterns
1901
1901
1902 $ testlog -f 'glob:*'
1902 $ testlog -f 'glob:*'
1903 []
1903 []
1904 (func
1904 (func
1905 (symbol '_matchfiles')
1905 (symbol '_matchfiles')
1906 (list
1906 (list
1907 (string 'r:')
1907 (string 'r:')
1908 (string 'd:relpath')
1908 (string 'd:relpath')
1909 (string 'p:glob:*')))
1909 (string 'p:glob:*')))
1910 <filteredset
1910 <filteredset
1911 <generatorsetdesc->,
1911 <generatorsetdesc->,
1912 <matchfiles patterns=['glob:*'], include=[] exclude=[], default='relpath', rev=2147483647>>
1912 <matchfiles patterns=['glob:*'], include=[] exclude=[], default='relpath', rev=2147483647>>
1913
1913
1914 Test --follow on a single rename
1914 Test --follow on a single rename
1915
1915
1916 $ hg up -q 2
1916 $ hg up -q 2
1917 $ testlog -f a
1917 $ testlog -f a
1918 []
1918 []
1919 []
1919 []
1920 <generatorsetdesc->
1920 <generatorsetdesc->
1921
1921
1922 Test --follow and multiple renames
1922 Test --follow and multiple renames
1923
1923
1924 $ hg up -q tip
1924 $ hg up -q tip
1925 $ testlog -f e
1925 $ testlog -f e
1926 []
1926 []
1927 []
1927 []
1928 <generatorsetdesc->
1928 <generatorsetdesc->
1929
1929
1930 Test --follow and multiple filelog heads
1930 Test --follow and multiple filelog heads
1931
1931
1932 $ hg up -q 2
1932 $ hg up -q 2
1933 $ testlog -f g
1933 $ testlog -f g
1934 []
1934 []
1935 []
1935 []
1936 <generatorsetdesc->
1936 <generatorsetdesc->
1937 $ cat log.nodes
1937 $ cat log.nodes
1938 nodetag 2
1938 nodetag 2
1939 nodetag 1
1939 nodetag 1
1940 nodetag 0
1940 nodetag 0
1941 $ hg up -q tip
1941 $ hg up -q tip
1942 $ testlog -f g
1942 $ testlog -f g
1943 []
1943 []
1944 []
1944 []
1945 <generatorsetdesc->
1945 <generatorsetdesc->
1946 $ cat log.nodes
1946 $ cat log.nodes
1947 nodetag 3
1947 nodetag 3
1948 nodetag 2
1948 nodetag 2
1949 nodetag 0
1949 nodetag 0
1950
1950
1951 Test --follow and multiple files
1951 Test --follow and multiple files
1952
1952
1953 $ testlog -f g e
1953 $ testlog -f g e
1954 []
1954 []
1955 []
1955 []
1956 <generatorsetdesc->
1956 <generatorsetdesc->
1957 $ cat log.nodes
1957 $ cat log.nodes
1958 nodetag 4
1958 nodetag 4
1959 nodetag 3
1959 nodetag 3
1960 nodetag 2
1960 nodetag 2
1961 nodetag 1
1961 nodetag 1
1962 nodetag 0
1962 nodetag 0
1963
1963
1964 Test --follow null parent
1964 Test --follow null parent
1965
1965
1966 $ hg up -q null
1966 $ hg up -q null
1967 $ testlog -f
1967 $ testlog -f
1968 []
1968 []
1969 []
1969 []
1970 <baseset []>
1970 <baseset []>
1971
1971
1972 Test --follow-first
1972 Test --follow-first
1973
1973
1974 $ hg up -q 3
1974 $ hg up -q 3
1975 $ echo ee > e
1975 $ echo ee > e
1976 $ hg ci -Am "add another e" e
1976 $ hg ci -Am "add another e" e
1977 created new head
1977 created new head
1978 $ hg merge --tool internal:other 4
1978 $ hg merge --tool internal:other 4
1979 0 files updated, 1 files merged, 1 files removed, 0 files unresolved
1979 0 files updated, 1 files merged, 1 files removed, 0 files unresolved
1980 (branch merge, don't forget to commit)
1980 (branch merge, don't forget to commit)
1981 $ echo merge > e
1981 $ echo merge > e
1982 $ hg ci -m "merge 5 and 4"
1982 $ hg ci -m "merge 5 and 4"
1983 $ testlog --follow-first
1983 $ testlog --follow-first
1984 []
1984 []
1985 []
1985 []
1986 <generatorsetdesc->
1986 <generatorsetdesc->
1987
1987
1988 Cannot compare with log --follow-first FILE as it never worked
1988 Cannot compare with log --follow-first FILE as it never worked
1989
1989
1990 $ hg log -G --print-revset --follow-first e
1990 $ hg log -G --print-revset --follow-first e
1991 []
1991 []
1992 []
1992 []
1993 <generatorsetdesc->
1993 <generatorsetdesc->
1994 $ hg log -G --follow-first e --template '{rev} {desc|firstline}\n'
1994 $ hg log -G --follow-first e --template '{rev} {desc|firstline}\n'
1995 \xe2\x97\x8d 6 merge 5 and 4 (esc)
1995 \xe2\x97\x8d 6 merge 5 and 4 (esc)
1996 \xe2\x94\x82\xe2\x95\xb2 (esc)
1996 \xe2\x94\x82\xe2\x95\xb2 (esc)
1997 \xe2\x94\x82 \xe2\x95\xa7 (esc)
1997 \xe2\x94\x82 \xe2\x95\xa7 (esc)
1998 \xe2\x97\x8b 5 add another e (esc)
1998 \xe2\x97\x8b 5 add another e (esc)
1999 \xe2\x94\x82 (esc)
1999 \xe2\x94\x82 (esc)
2000 \xe2\x95\xa7 (esc)
2000 \xe2\x95\xa7 (esc)
2001
2001
2002 Test --copies
2002 Test --copies
2003
2003
2004 $ hg log -G --copies --template "{rev} {desc|firstline} \
2004 $ hg log -G --copies --template "{rev} {desc|firstline} \
2005 > copies: {file_copies_switch}\n"
2005 > copies: {file_copies_switch}\n"
2006 \xe2\x97\x8d 6 merge 5 and 4 copies: (esc)
2006 \xe2\x97\x8d 6 merge 5 and 4 copies: (esc)
2007 \xe2\x94\x82\xe2\x95\xb2 (esc)
2007 \xe2\x94\x82\xe2\x95\xb2 (esc)
2008 \xe2\x94\x82 \xe2\x97\x8b 5 add another e copies: (esc)
2008 \xe2\x94\x82 \xe2\x97\x8b 5 add another e copies: (esc)
2009 \xe2\x94\x82 \xe2\x94\x82 (esc)
2009 \xe2\x94\x82 \xe2\x94\x82 (esc)
2010 \xe2\x97\x8b \xe2\x94\x82 4 mv dir/b e copies: e (dir/b) (esc)
2010 \xe2\x97\x8b \xe2\x94\x82 4 mv dir/b e copies: e (dir/b) (esc)
2011 \xe2\x94\x82\xe2\x95\xb1 (esc)
2011 \xe2\x94\x82\xe2\x95\xb1 (esc)
2012 \xe2\x97\x8b 3 mv a b; add d copies: b (a)g (f) (esc)
2012 \xe2\x97\x8b 3 mv a b; add d copies: b (a)g (f) (esc)
2013 \xe2\x94\x82 (esc)
2013 \xe2\x94\x82 (esc)
2014 \xe2\x97\x8b 2 mv b dir/b copies: dir/b (b) (esc)
2014 \xe2\x97\x8b 2 mv b dir/b copies: dir/b (b) (esc)
2015 \xe2\x94\x82 (esc)
2015 \xe2\x94\x82 (esc)
2016 \xe2\x97\x8b 1 copy a b copies: b (a)g (f) (esc)
2016 \xe2\x97\x8b 1 copy a b copies: b (a)g (f) (esc)
2017 \xe2\x94\x82 (esc)
2017 \xe2\x94\x82 (esc)
2018 \xe2\x97\x8b 0 add a copies: (esc)
2018 \xe2\x97\x8b 0 add a copies: (esc)
2019
2019
2020 Test "set:..." and parent revision
2020 Test "set:..." and parent revision
2021
2021
2022 $ hg up -q 4
2022 $ hg up -q 4
2023 $ testlog "set:copied()"
2023 $ testlog "set:copied()"
2024 []
2024 []
2025 (func
2025 (func
2026 (symbol 'filelog')
2026 (symbol '_matchfiles')
2027 (string 'set:copied()'))
2027 (list
2028 (string 'r:')
2029 (string 'd:relpath')
2030 (string 'p:set:copied()')))
2028 <filteredset
2031 <filteredset
2029 <spanset- 0:7>, set([])>
2032 <spanset- 0:7>,
2033 <matchfiles patterns=['set:copied()'], include=[] exclude=[], default='relpath', rev=2147483647>>
2030 $ testlog --include "set:copied()"
2034 $ testlog --include "set:copied()"
2031 []
2035 []
2032 []
2036 (func
2033 <spanset- 0:7>
2037 (symbol '_matchfiles')
2038 (list
2039 (string 'r:')
2040 (string 'd:relpath')
2041 (string 'i:set:copied()')))
2042 <filteredset
2043 <spanset- 0:7>,
2044 <matchfiles patterns=[], include=['set:copied()'] exclude=[], default='relpath', rev=2147483647>>
2034 $ testlog -r "sort(file('set:copied()'), -rev)"
2045 $ testlog -r "sort(file('set:copied()'), -rev)"
2035 ["sort(file('set:copied()'), -rev)"]
2046 ["sort(file('set:copied()'), -rev)"]
2036 []
2047 []
2037 <filteredset
2048 <filteredset
2038 <fullreposet- 0:7>,
2049 <fullreposet- 0:7>,
2039 <matchfiles patterns=['set:copied()'], include=[] exclude=[], default='glob', rev=None>>
2050 <matchfiles patterns=['set:copied()'], include=[] exclude=[], default='glob', rev=None>>
2040
2051
2041 Test --removed
2052 Test --removed
2042
2053
2043 $ testlog --removed
2054 $ testlog --removed
2044 []
2055 []
2045 []
2056 []
2046 <spanset- 0:7>
2057 <spanset- 0:7>
2047 $ testlog --removed a
2058 $ testlog --removed a
2048 []
2059 []
2049 (func
2060 (func
2050 (symbol '_matchfiles')
2061 (symbol '_matchfiles')
2051 (list
2062 (list
2052 (string 'r:')
2063 (string 'r:')
2053 (string 'd:relpath')
2064 (string 'd:relpath')
2054 (string 'p:a')))
2065 (string 'p:a')))
2055 <filteredset
2066 <filteredset
2056 <spanset- 0:7>,
2067 <spanset- 0:7>,
2057 <matchfiles patterns=['a'], include=[] exclude=[], default='relpath', rev=2147483647>>
2068 <matchfiles patterns=['a'], include=[] exclude=[], default='relpath', rev=2147483647>>
2058 $ testlog --removed --follow a
2069 $ testlog --removed --follow a
2059 []
2070 []
2060 (func
2071 (func
2061 (symbol '_matchfiles')
2072 (symbol '_matchfiles')
2062 (list
2073 (list
2063 (string 'r:')
2074 (string 'r:')
2064 (string 'd:relpath')
2075 (string 'd:relpath')
2065 (string 'p:a')))
2076 (string 'p:a')))
2066 <filteredset
2077 <filteredset
2067 <generatorsetdesc->,
2078 <generatorsetdesc->,
2068 <matchfiles patterns=['a'], include=[] exclude=[], default='relpath', rev=2147483647>>
2079 <matchfiles patterns=['a'], include=[] exclude=[], default='relpath', rev=2147483647>>
2069
2080
2070 Test --patch and --stat with --follow and --follow-first
2081 Test --patch and --stat with --follow and --follow-first
2071
2082
2072 $ hg up -q 3
2083 $ hg up -q 3
2073 $ hg log -G --git --patch b
2084 $ hg log -G --git --patch b
2074 \xe2\x97\x8b changeset: 1:216d4c92cf98 (esc)
2085 \xe2\x97\x8b changeset: 1:216d4c92cf98 (esc)
2075 \xe2\x94\x82 user: test (esc)
2086 \xe2\x94\x82 user: test (esc)
2076 \xe2\x95\xa7 date: Thu Jan 01 00:00:00 1970 +0000 (esc)
2087 \xe2\x95\xa7 date: Thu Jan 01 00:00:00 1970 +0000 (esc)
2077 summary: copy a b
2088 summary: copy a b
2078
2089
2079 diff --git a/a b/b
2090 diff --git a/a b/b
2080 copy from a
2091 copy from a
2081 copy to b
2092 copy to b
2082
2093
2083
2094
2084 $ hg log -G --git --stat b
2095 $ hg log -G --git --stat b
2085 \xe2\x97\x8b changeset: 1:216d4c92cf98 (esc)
2096 \xe2\x97\x8b changeset: 1:216d4c92cf98 (esc)
2086 \xe2\x94\x82 user: test (esc)
2097 \xe2\x94\x82 user: test (esc)
2087 \xe2\x95\xa7 date: Thu Jan 01 00:00:00 1970 +0000 (esc)
2098 \xe2\x95\xa7 date: Thu Jan 01 00:00:00 1970 +0000 (esc)
2088 summary: copy a b
2099 summary: copy a b
2089
2100
2090 b | 0
2101 b | 0
2091 1 files changed, 0 insertions(+), 0 deletions(-)
2102 1 files changed, 0 insertions(+), 0 deletions(-)
2092
2103
2093
2104
2094 $ hg log -G --git --patch --follow b
2105 $ hg log -G --git --patch --follow b
2095 \xe2\x97\x8b changeset: 1:216d4c92cf98 (esc)
2106 \xe2\x97\x8b changeset: 1:216d4c92cf98 (esc)
2096 \xe2\x94\x82 user: test (esc)
2107 \xe2\x94\x82 user: test (esc)
2097 \xe2\x94\x82 date: Thu Jan 01 00:00:00 1970 +0000 (esc)
2108 \xe2\x94\x82 date: Thu Jan 01 00:00:00 1970 +0000 (esc)
2098 \xe2\x94\x82 summary: copy a b (esc)
2109 \xe2\x94\x82 summary: copy a b (esc)
2099 \xe2\x94\x82 (esc)
2110 \xe2\x94\x82 (esc)
2100 \xe2\x94\x82 diff --git a/a b/b (esc)
2111 \xe2\x94\x82 diff --git a/a b/b (esc)
2101 \xe2\x94\x82 copy from a (esc)
2112 \xe2\x94\x82 copy from a (esc)
2102 \xe2\x94\x82 copy to b (esc)
2113 \xe2\x94\x82 copy to b (esc)
2103 \xe2\x94\x82 (esc)
2114 \xe2\x94\x82 (esc)
2104 \xe2\x97\x8b changeset: 0:f8035bb17114 (esc)
2115 \xe2\x97\x8b changeset: 0:f8035bb17114 (esc)
2105 user: test
2116 user: test
2106 date: Thu Jan 01 00:00:00 1970 +0000
2117 date: Thu Jan 01 00:00:00 1970 +0000
2107 summary: add a
2118 summary: add a
2108
2119
2109 diff --git a/a b/a
2120 diff --git a/a b/a
2110 new file mode 100644
2121 new file mode 100644
2111 --- /dev/null
2122 --- /dev/null
2112 +++ b/a
2123 +++ b/a
2113 @@ -0,0 +1,1 @@
2124 @@ -0,0 +1,1 @@
2114 +a
2125 +a
2115
2126
2116
2127
2117 $ hg log -G --git --stat --follow b
2128 $ hg log -G --git --stat --follow b
2118 \xe2\x97\x8b changeset: 1:216d4c92cf98 (esc)
2129 \xe2\x97\x8b changeset: 1:216d4c92cf98 (esc)
2119 \xe2\x94\x82 user: test (esc)
2130 \xe2\x94\x82 user: test (esc)
2120 \xe2\x94\x82 date: Thu Jan 01 00:00:00 1970 +0000 (esc)
2131 \xe2\x94\x82 date: Thu Jan 01 00:00:00 1970 +0000 (esc)
2121 \xe2\x94\x82 summary: copy a b (esc)
2132 \xe2\x94\x82 summary: copy a b (esc)
2122 \xe2\x94\x82 (esc)
2133 \xe2\x94\x82 (esc)
2123 \xe2\x94\x82 b | 0 (esc)
2134 \xe2\x94\x82 b | 0 (esc)
2124 \xe2\x94\x82 1 files changed, 0 insertions(+), 0 deletions(-) (esc)
2135 \xe2\x94\x82 1 files changed, 0 insertions(+), 0 deletions(-) (esc)
2125 \xe2\x94\x82 (esc)
2136 \xe2\x94\x82 (esc)
2126 \xe2\x97\x8b changeset: 0:f8035bb17114 (esc)
2137 \xe2\x97\x8b changeset: 0:f8035bb17114 (esc)
2127 user: test
2138 user: test
2128 date: Thu Jan 01 00:00:00 1970 +0000
2139 date: Thu Jan 01 00:00:00 1970 +0000
2129 summary: add a
2140 summary: add a
2130
2141
2131 a | 1 +
2142 a | 1 +
2132 1 files changed, 1 insertions(+), 0 deletions(-)
2143 1 files changed, 1 insertions(+), 0 deletions(-)
2133
2144
2134
2145
2135 $ hg up -q 6
2146 $ hg up -q 6
2136 $ hg log -G --git --patch --follow-first e
2147 $ hg log -G --git --patch --follow-first e
2137 \xe2\x97\x8d changeset: 6:fc281d8ff18d (esc)
2148 \xe2\x97\x8d changeset: 6:fc281d8ff18d (esc)
2138 \xe2\x94\x82\xe2\x95\xb2 tag: tip (esc)
2149 \xe2\x94\x82\xe2\x95\xb2 tag: tip (esc)
2139 \xe2\x94\x82 \xe2\x95\xa7 parent: 5:99b31f1c2782 (esc)
2150 \xe2\x94\x82 \xe2\x95\xa7 parent: 5:99b31f1c2782 (esc)
2140 \xe2\x94\x82 parent: 4:17d952250a9d (esc)
2151 \xe2\x94\x82 parent: 4:17d952250a9d (esc)
2141 \xe2\x94\x82 user: test (esc)
2152 \xe2\x94\x82 user: test (esc)
2142 \xe2\x94\x82 date: Thu Jan 01 00:00:00 1970 +0000 (esc)
2153 \xe2\x94\x82 date: Thu Jan 01 00:00:00 1970 +0000 (esc)
2143 \xe2\x94\x82 summary: merge 5 and 4 (esc)
2154 \xe2\x94\x82 summary: merge 5 and 4 (esc)
2144 \xe2\x94\x82 (esc)
2155 \xe2\x94\x82 (esc)
2145 \xe2\x94\x82 diff --git a/e b/e (esc)
2156 \xe2\x94\x82 diff --git a/e b/e (esc)
2146 \xe2\x94\x82 --- a/e (esc)
2157 \xe2\x94\x82 --- a/e (esc)
2147 \xe2\x94\x82 +++ b/e (esc)
2158 \xe2\x94\x82 +++ b/e (esc)
2148 \xe2\x94\x82 @@ -1,1 +1,1 @@ (esc)
2159 \xe2\x94\x82 @@ -1,1 +1,1 @@ (esc)
2149 \xe2\x94\x82 -ee (esc)
2160 \xe2\x94\x82 -ee (esc)
2150 \xe2\x94\x82 +merge (esc)
2161 \xe2\x94\x82 +merge (esc)
2151 \xe2\x94\x82 (esc)
2162 \xe2\x94\x82 (esc)
2152 \xe2\x97\x8b changeset: 5:99b31f1c2782 (esc)
2163 \xe2\x97\x8b changeset: 5:99b31f1c2782 (esc)
2153 \xe2\x94\x82 parent: 3:5918b8d165d1 (esc)
2164 \xe2\x94\x82 parent: 3:5918b8d165d1 (esc)
2154 \xe2\x95\xa7 user: test (esc)
2165 \xe2\x95\xa7 user: test (esc)
2155 date: Thu Jan 01 00:00:00 1970 +0000
2166 date: Thu Jan 01 00:00:00 1970 +0000
2156 summary: add another e
2167 summary: add another e
2157
2168
2158 diff --git a/e b/e
2169 diff --git a/e b/e
2159 new file mode 100644
2170 new file mode 100644
2160 --- /dev/null
2171 --- /dev/null
2161 +++ b/e
2172 +++ b/e
2162 @@ -0,0 +1,1 @@
2173 @@ -0,0 +1,1 @@
2163 +ee
2174 +ee
2164
2175
2165
2176
2166 Test old-style --rev
2177 Test old-style --rev
2167
2178
2168 $ hg tag 'foo-bar'
2179 $ hg tag 'foo-bar'
2169 $ testlog -r 'foo-bar'
2180 $ testlog -r 'foo-bar'
2170 ['foo-bar']
2181 ['foo-bar']
2171 []
2182 []
2172 <baseset [6]>
2183 <baseset [6]>
2173
2184
2174 Test --follow and forward --rev
2185 Test --follow and forward --rev
2175
2186
2176 $ hg up -q 6
2187 $ hg up -q 6
2177 $ echo g > g
2188 $ echo g > g
2178 $ hg ci -Am 'add g' g
2189 $ hg ci -Am 'add g' g
2179 created new head
2190 created new head
2180 $ hg up -q 2
2191 $ hg up -q 2
2181 $ hg log -G --template "{rev} {desc|firstline}\n"
2192 $ hg log -G --template "{rev} {desc|firstline}\n"
2182 \xe2\x97\x8b 8 add g (esc)
2193 \xe2\x97\x8b 8 add g (esc)
2183 \xe2\x94\x82 (esc)
2194 \xe2\x94\x82 (esc)
2184 \xe2\x94\x82 \xe2\x97\x8b 7 Added tag foo-bar for changeset fc281d8ff18d (esc)
2195 \xe2\x94\x82 \xe2\x97\x8b 7 Added tag foo-bar for changeset fc281d8ff18d (esc)
2185 \xe2\x94\x82\xe2\x95\xb1 (esc)
2196 \xe2\x94\x82\xe2\x95\xb1 (esc)
2186 \xe2\x97\x8b 6 merge 5 and 4 (esc)
2197 \xe2\x97\x8b 6 merge 5 and 4 (esc)
2187 \xe2\x94\x82\xe2\x95\xb2 (esc)
2198 \xe2\x94\x82\xe2\x95\xb2 (esc)
2188 \xe2\x94\x82 \xe2\x97\x8b 5 add another e (esc)
2199 \xe2\x94\x82 \xe2\x97\x8b 5 add another e (esc)
2189 \xe2\x94\x82 \xe2\x94\x82 (esc)
2200 \xe2\x94\x82 \xe2\x94\x82 (esc)
2190 \xe2\x97\x8b \xe2\x94\x82 4 mv dir/b e (esc)
2201 \xe2\x97\x8b \xe2\x94\x82 4 mv dir/b e (esc)
2191 \xe2\x94\x82\xe2\x95\xb1 (esc)
2202 \xe2\x94\x82\xe2\x95\xb1 (esc)
2192 \xe2\x97\x8b 3 mv a b; add d (esc)
2203 \xe2\x97\x8b 3 mv a b; add d (esc)
2193 \xe2\x94\x82 (esc)
2204 \xe2\x94\x82 (esc)
2194 \xe2\x97\x8d 2 mv b dir/b (esc)
2205 \xe2\x97\x8d 2 mv b dir/b (esc)
2195 \xe2\x94\x82 (esc)
2206 \xe2\x94\x82 (esc)
2196 \xe2\x97\x8b 1 copy a b (esc)
2207 \xe2\x97\x8b 1 copy a b (esc)
2197 \xe2\x94\x82 (esc)
2208 \xe2\x94\x82 (esc)
2198 \xe2\x97\x8b 0 add a (esc)
2209 \xe2\x97\x8b 0 add a (esc)
2199
2210
2200 $ hg archive -r 7 archive
2211 $ hg archive -r 7 archive
2201 $ grep changessincelatesttag archive/.hg_archival.txt
2212 $ grep changessincelatesttag archive/.hg_archival.txt
2202 changessincelatesttag: 1
2213 changessincelatesttag: 1
2203 $ rm -r archive
2214 $ rm -r archive
2204
2215
2205 changessincelatesttag with no prior tag
2216 changessincelatesttag with no prior tag
2206 $ hg archive -r 4 archive
2217 $ hg archive -r 4 archive
2207 $ grep changessincelatesttag archive/.hg_archival.txt
2218 $ grep changessincelatesttag archive/.hg_archival.txt
2208 changessincelatesttag: 5
2219 changessincelatesttag: 5
2209
2220
2210 $ hg export 'all()'
2221 $ hg export 'all()'
2211 # HG changeset patch
2222 # HG changeset patch
2212 # User test
2223 # User test
2213 # Date 0 0
2224 # Date 0 0
2214 # Thu Jan 01 00:00:00 1970 +0000
2225 # Thu Jan 01 00:00:00 1970 +0000
2215 # Node ID f8035bb17114da16215af3436ec5222428ace8ee
2226 # Node ID f8035bb17114da16215af3436ec5222428ace8ee
2216 # Parent 0000000000000000000000000000000000000000
2227 # Parent 0000000000000000000000000000000000000000
2217 add a
2228 add a
2218
2229
2219 diff -r 000000000000 -r f8035bb17114 a
2230 diff -r 000000000000 -r f8035bb17114 a
2220 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2231 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2221 +++ b/a Thu Jan 01 00:00:00 1970 +0000
2232 +++ b/a Thu Jan 01 00:00:00 1970 +0000
2222 @@ -0,0 +1,1 @@
2233 @@ -0,0 +1,1 @@
2223 +a
2234 +a
2224 diff -r 000000000000 -r f8035bb17114 aa
2235 diff -r 000000000000 -r f8035bb17114 aa
2225 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2236 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2226 +++ b/aa Thu Jan 01 00:00:00 1970 +0000
2237 +++ b/aa Thu Jan 01 00:00:00 1970 +0000
2227 @@ -0,0 +1,1 @@
2238 @@ -0,0 +1,1 @@
2228 +aa
2239 +aa
2229 diff -r 000000000000 -r f8035bb17114 f
2240 diff -r 000000000000 -r f8035bb17114 f
2230 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2241 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2231 +++ b/f Thu Jan 01 00:00:00 1970 +0000
2242 +++ b/f Thu Jan 01 00:00:00 1970 +0000
2232 @@ -0,0 +1,1 @@
2243 @@ -0,0 +1,1 @@
2233 +f
2244 +f
2234 # HG changeset patch
2245 # HG changeset patch
2235 # User test
2246 # User test
2236 # Date 0 0
2247 # Date 0 0
2237 # Thu Jan 01 00:00:00 1970 +0000
2248 # Thu Jan 01 00:00:00 1970 +0000
2238 # Node ID 216d4c92cf98ff2b4641d508b76b529f3d424c92
2249 # Node ID 216d4c92cf98ff2b4641d508b76b529f3d424c92
2239 # Parent f8035bb17114da16215af3436ec5222428ace8ee
2250 # Parent f8035bb17114da16215af3436ec5222428ace8ee
2240 copy a b
2251 copy a b
2241
2252
2242 diff -r f8035bb17114 -r 216d4c92cf98 b
2253 diff -r f8035bb17114 -r 216d4c92cf98 b
2243 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2254 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2244 +++ b/b Thu Jan 01 00:00:00 1970 +0000
2255 +++ b/b Thu Jan 01 00:00:00 1970 +0000
2245 @@ -0,0 +1,1 @@
2256 @@ -0,0 +1,1 @@
2246 +a
2257 +a
2247 diff -r f8035bb17114 -r 216d4c92cf98 g
2258 diff -r f8035bb17114 -r 216d4c92cf98 g
2248 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2259 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2249 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2260 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2250 @@ -0,0 +1,1 @@
2261 @@ -0,0 +1,1 @@
2251 +f
2262 +f
2252 # HG changeset patch
2263 # HG changeset patch
2253 # User test
2264 # User test
2254 # Date 0 0
2265 # Date 0 0
2255 # Thu Jan 01 00:00:00 1970 +0000
2266 # Thu Jan 01 00:00:00 1970 +0000
2256 # Node ID bb573313a9e8349099b6ea2b2fb1fc7f424446f3
2267 # Node ID bb573313a9e8349099b6ea2b2fb1fc7f424446f3
2257 # Parent 216d4c92cf98ff2b4641d508b76b529f3d424c92
2268 # Parent 216d4c92cf98ff2b4641d508b76b529f3d424c92
2258 mv b dir/b
2269 mv b dir/b
2259
2270
2260 diff -r 216d4c92cf98 -r bb573313a9e8 b
2271 diff -r 216d4c92cf98 -r bb573313a9e8 b
2261 --- a/b Thu Jan 01 00:00:00 1970 +0000
2272 --- a/b Thu Jan 01 00:00:00 1970 +0000
2262 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2273 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2263 @@ -1,1 +0,0 @@
2274 @@ -1,1 +0,0 @@
2264 -a
2275 -a
2265 diff -r 216d4c92cf98 -r bb573313a9e8 dir/b
2276 diff -r 216d4c92cf98 -r bb573313a9e8 dir/b
2266 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2277 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2267 +++ b/dir/b Thu Jan 01 00:00:00 1970 +0000
2278 +++ b/dir/b Thu Jan 01 00:00:00 1970 +0000
2268 @@ -0,0 +1,1 @@
2279 @@ -0,0 +1,1 @@
2269 +a
2280 +a
2270 diff -r 216d4c92cf98 -r bb573313a9e8 f
2281 diff -r 216d4c92cf98 -r bb573313a9e8 f
2271 --- a/f Thu Jan 01 00:00:00 1970 +0000
2282 --- a/f Thu Jan 01 00:00:00 1970 +0000
2272 +++ b/f Thu Jan 01 00:00:00 1970 +0000
2283 +++ b/f Thu Jan 01 00:00:00 1970 +0000
2273 @@ -1,1 +1,2 @@
2284 @@ -1,1 +1,2 @@
2274 f
2285 f
2275 +f
2286 +f
2276 diff -r 216d4c92cf98 -r bb573313a9e8 g
2287 diff -r 216d4c92cf98 -r bb573313a9e8 g
2277 --- a/g Thu Jan 01 00:00:00 1970 +0000
2288 --- a/g Thu Jan 01 00:00:00 1970 +0000
2278 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2289 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2279 @@ -1,1 +1,2 @@
2290 @@ -1,1 +1,2 @@
2280 f
2291 f
2281 +g
2292 +g
2282 # HG changeset patch
2293 # HG changeset patch
2283 # User test
2294 # User test
2284 # Date 0 0
2295 # Date 0 0
2285 # Thu Jan 01 00:00:00 1970 +0000
2296 # Thu Jan 01 00:00:00 1970 +0000
2286 # Node ID 5918b8d165d1364e78a66d02e66caa0133c5d1ed
2297 # Node ID 5918b8d165d1364e78a66d02e66caa0133c5d1ed
2287 # Parent bb573313a9e8349099b6ea2b2fb1fc7f424446f3
2298 # Parent bb573313a9e8349099b6ea2b2fb1fc7f424446f3
2288 mv a b; add d
2299 mv a b; add d
2289
2300
2290 diff -r bb573313a9e8 -r 5918b8d165d1 a
2301 diff -r bb573313a9e8 -r 5918b8d165d1 a
2291 --- a/a Thu Jan 01 00:00:00 1970 +0000
2302 --- a/a Thu Jan 01 00:00:00 1970 +0000
2292 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2303 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2293 @@ -1,1 +0,0 @@
2304 @@ -1,1 +0,0 @@
2294 -a
2305 -a
2295 diff -r bb573313a9e8 -r 5918b8d165d1 b
2306 diff -r bb573313a9e8 -r 5918b8d165d1 b
2296 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2307 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2297 +++ b/b Thu Jan 01 00:00:00 1970 +0000
2308 +++ b/b Thu Jan 01 00:00:00 1970 +0000
2298 @@ -0,0 +1,1 @@
2309 @@ -0,0 +1,1 @@
2299 +a
2310 +a
2300 diff -r bb573313a9e8 -r 5918b8d165d1 d
2311 diff -r bb573313a9e8 -r 5918b8d165d1 d
2301 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2312 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2302 +++ b/d Thu Jan 01 00:00:00 1970 +0000
2313 +++ b/d Thu Jan 01 00:00:00 1970 +0000
2303 @@ -0,0 +1,1 @@
2314 @@ -0,0 +1,1 @@
2304 +a
2315 +a
2305 diff -r bb573313a9e8 -r 5918b8d165d1 g
2316 diff -r bb573313a9e8 -r 5918b8d165d1 g
2306 --- a/g Thu Jan 01 00:00:00 1970 +0000
2317 --- a/g Thu Jan 01 00:00:00 1970 +0000
2307 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2318 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2308 @@ -1,2 +1,2 @@
2319 @@ -1,2 +1,2 @@
2309 f
2320 f
2310 -g
2321 -g
2311 +f
2322 +f
2312 # HG changeset patch
2323 # HG changeset patch
2313 # User test
2324 # User test
2314 # Date 0 0
2325 # Date 0 0
2315 # Thu Jan 01 00:00:00 1970 +0000
2326 # Thu Jan 01 00:00:00 1970 +0000
2316 # Node ID 17d952250a9d03cc3dc77b199ab60e959b9b0260
2327 # Node ID 17d952250a9d03cc3dc77b199ab60e959b9b0260
2317 # Parent 5918b8d165d1364e78a66d02e66caa0133c5d1ed
2328 # Parent 5918b8d165d1364e78a66d02e66caa0133c5d1ed
2318 mv dir/b e
2329 mv dir/b e
2319
2330
2320 diff -r 5918b8d165d1 -r 17d952250a9d dir/b
2331 diff -r 5918b8d165d1 -r 17d952250a9d dir/b
2321 --- a/dir/b Thu Jan 01 00:00:00 1970 +0000
2332 --- a/dir/b Thu Jan 01 00:00:00 1970 +0000
2322 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2333 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2323 @@ -1,1 +0,0 @@
2334 @@ -1,1 +0,0 @@
2324 -a
2335 -a
2325 diff -r 5918b8d165d1 -r 17d952250a9d e
2336 diff -r 5918b8d165d1 -r 17d952250a9d e
2326 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2337 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2327 +++ b/e Thu Jan 01 00:00:00 1970 +0000
2338 +++ b/e Thu Jan 01 00:00:00 1970 +0000
2328 @@ -0,0 +1,1 @@
2339 @@ -0,0 +1,1 @@
2329 +a
2340 +a
2330 # HG changeset patch
2341 # HG changeset patch
2331 # User test
2342 # User test
2332 # Date 0 0
2343 # Date 0 0
2333 # Thu Jan 01 00:00:00 1970 +0000
2344 # Thu Jan 01 00:00:00 1970 +0000
2334 # Node ID 99b31f1c2782e2deb1723cef08930f70fc84b37b
2345 # Node ID 99b31f1c2782e2deb1723cef08930f70fc84b37b
2335 # Parent 5918b8d165d1364e78a66d02e66caa0133c5d1ed
2346 # Parent 5918b8d165d1364e78a66d02e66caa0133c5d1ed
2336 add another e
2347 add another e
2337
2348
2338 diff -r 5918b8d165d1 -r 99b31f1c2782 e
2349 diff -r 5918b8d165d1 -r 99b31f1c2782 e
2339 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2350 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2340 +++ b/e Thu Jan 01 00:00:00 1970 +0000
2351 +++ b/e Thu Jan 01 00:00:00 1970 +0000
2341 @@ -0,0 +1,1 @@
2352 @@ -0,0 +1,1 @@
2342 +ee
2353 +ee
2343 # HG changeset patch
2354 # HG changeset patch
2344 # User test
2355 # User test
2345 # Date 0 0
2356 # Date 0 0
2346 # Thu Jan 01 00:00:00 1970 +0000
2357 # Thu Jan 01 00:00:00 1970 +0000
2347 # Node ID fc281d8ff18d999ad6497b3d27390bcd695dcc73
2358 # Node ID fc281d8ff18d999ad6497b3d27390bcd695dcc73
2348 # Parent 99b31f1c2782e2deb1723cef08930f70fc84b37b
2359 # Parent 99b31f1c2782e2deb1723cef08930f70fc84b37b
2349 # Parent 17d952250a9d03cc3dc77b199ab60e959b9b0260
2360 # Parent 17d952250a9d03cc3dc77b199ab60e959b9b0260
2350 merge 5 and 4
2361 merge 5 and 4
2351
2362
2352 diff -r 99b31f1c2782 -r fc281d8ff18d dir/b
2363 diff -r 99b31f1c2782 -r fc281d8ff18d dir/b
2353 --- a/dir/b Thu Jan 01 00:00:00 1970 +0000
2364 --- a/dir/b Thu Jan 01 00:00:00 1970 +0000
2354 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2365 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
2355 @@ -1,1 +0,0 @@
2366 @@ -1,1 +0,0 @@
2356 -a
2367 -a
2357 diff -r 99b31f1c2782 -r fc281d8ff18d e
2368 diff -r 99b31f1c2782 -r fc281d8ff18d e
2358 --- a/e Thu Jan 01 00:00:00 1970 +0000
2369 --- a/e Thu Jan 01 00:00:00 1970 +0000
2359 +++ b/e Thu Jan 01 00:00:00 1970 +0000
2370 +++ b/e Thu Jan 01 00:00:00 1970 +0000
2360 @@ -1,1 +1,1 @@
2371 @@ -1,1 +1,1 @@
2361 -ee
2372 -ee
2362 +merge
2373 +merge
2363 # HG changeset patch
2374 # HG changeset patch
2364 # User test
2375 # User test
2365 # Date 0 0
2376 # Date 0 0
2366 # Thu Jan 01 00:00:00 1970 +0000
2377 # Thu Jan 01 00:00:00 1970 +0000
2367 # Node ID 02dbb8e276b8ab7abfd07cab50c901647e75c2dd
2378 # Node ID 02dbb8e276b8ab7abfd07cab50c901647e75c2dd
2368 # Parent fc281d8ff18d999ad6497b3d27390bcd695dcc73
2379 # Parent fc281d8ff18d999ad6497b3d27390bcd695dcc73
2369 Added tag foo-bar for changeset fc281d8ff18d
2380 Added tag foo-bar for changeset fc281d8ff18d
2370
2381
2371 diff -r fc281d8ff18d -r 02dbb8e276b8 .hgtags
2382 diff -r fc281d8ff18d -r 02dbb8e276b8 .hgtags
2372 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2383 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2373 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
2384 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
2374 @@ -0,0 +1,1 @@
2385 @@ -0,0 +1,1 @@
2375 +fc281d8ff18d999ad6497b3d27390bcd695dcc73 foo-bar
2386 +fc281d8ff18d999ad6497b3d27390bcd695dcc73 foo-bar
2376 # HG changeset patch
2387 # HG changeset patch
2377 # User test
2388 # User test
2378 # Date 0 0
2389 # Date 0 0
2379 # Thu Jan 01 00:00:00 1970 +0000
2390 # Thu Jan 01 00:00:00 1970 +0000
2380 # Node ID 24c2e826ddebf80f9dcd60b856bdb8e6715c5449
2391 # Node ID 24c2e826ddebf80f9dcd60b856bdb8e6715c5449
2381 # Parent fc281d8ff18d999ad6497b3d27390bcd695dcc73
2392 # Parent fc281d8ff18d999ad6497b3d27390bcd695dcc73
2382 add g
2393 add g
2383
2394
2384 diff -r fc281d8ff18d -r 24c2e826ddeb g
2395 diff -r fc281d8ff18d -r 24c2e826ddeb g
2385 --- a/g Thu Jan 01 00:00:00 1970 +0000
2396 --- a/g Thu Jan 01 00:00:00 1970 +0000
2386 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2397 +++ b/g Thu Jan 01 00:00:00 1970 +0000
2387 @@ -1,2 +1,1 @@
2398 @@ -1,2 +1,1 @@
2388 -f
2399 -f
2389 -f
2400 -f
2390 +g
2401 +g
2391 $ testlog --follow -r6 -r8 -r5 -r7 -r4
2402 $ testlog --follow -r6 -r8 -r5 -r7 -r4
2392 ['6', '8', '5', '7', '4']
2403 ['6', '8', '5', '7', '4']
2393 []
2404 []
2394 <generatorsetdesc->
2405 <generatorsetdesc->
2395
2406
2396 Test --follow-first and forward --rev
2407 Test --follow-first and forward --rev
2397
2408
2398 $ testlog --follow-first -r6 -r8 -r5 -r7 -r4
2409 $ testlog --follow-first -r6 -r8 -r5 -r7 -r4
2399 ['6', '8', '5', '7', '4']
2410 ['6', '8', '5', '7', '4']
2400 []
2411 []
2401 <generatorsetdesc->
2412 <generatorsetdesc->
2402
2413
2403 Test --follow and backward --rev
2414 Test --follow and backward --rev
2404
2415
2405 $ testlog --follow -r6 -r5 -r7 -r8 -r4
2416 $ testlog --follow -r6 -r5 -r7 -r8 -r4
2406 ['6', '5', '7', '8', '4']
2417 ['6', '5', '7', '8', '4']
2407 []
2418 []
2408 <generatorsetdesc->
2419 <generatorsetdesc->
2409
2420
2410 Test --follow-first and backward --rev
2421 Test --follow-first and backward --rev
2411
2422
2412 $ testlog --follow-first -r6 -r5 -r7 -r8 -r4
2423 $ testlog --follow-first -r6 -r5 -r7 -r8 -r4
2413 ['6', '5', '7', '8', '4']
2424 ['6', '5', '7', '8', '4']
2414 []
2425 []
2415 <generatorsetdesc->
2426 <generatorsetdesc->
2416
2427
2417 Test --follow with --rev of graphlog extension
2428 Test --follow with --rev of graphlog extension
2418
2429
2419 $ hg --config extensions.graphlog= glog -qfr1
2430 $ hg --config extensions.graphlog= glog -qfr1
2420 \xe2\x97\x8b 1:216d4c92cf98 (esc)
2431 \xe2\x97\x8b 1:216d4c92cf98 (esc)
2421 \xe2\x94\x82 (esc)
2432 \xe2\x94\x82 (esc)
2422 \xe2\x97\x8b 0:f8035bb17114 (esc)
2433 \xe2\x97\x8b 0:f8035bb17114 (esc)
2423
2434
2424
2435
2425 Test subdir
2436 Test subdir
2426
2437
2427 $ hg up -q 3
2438 $ hg up -q 3
2428 $ cd dir
2439 $ cd dir
2429 $ testlog .
2440 $ testlog .
2430 []
2441 []
2431 (func
2442 (func
2432 (symbol '_matchfiles')
2443 (symbol '_matchfiles')
2433 (list
2444 (list
2434 (string 'r:')
2445 (string 'r:')
2435 (string 'd:relpath')
2446 (string 'd:relpath')
2436 (string 'p:.')))
2447 (string 'p:.')))
2437 <filteredset
2448 <filteredset
2438 <spanset- 0:9>,
2449 <spanset- 0:9>,
2439 <matchfiles patterns=['.'], include=[] exclude=[], default='relpath', rev=2147483647>>
2450 <matchfiles patterns=['.'], include=[] exclude=[], default='relpath', rev=2147483647>>
2440 $ testlog ../b
2451 $ testlog ../b
2441 []
2452 []
2442 (func
2453 (func
2443 (symbol 'filelog')
2454 (symbol 'filelog')
2444 (string '../b'))
2455 (string '../b'))
2445 <filteredset
2456 <filteredset
2446 <spanset- 0:9>, set([1])>
2457 <spanset- 0:9>, set([1])>
2447 $ testlog -f ../b
2458 $ testlog -f ../b
2448 []
2459 []
2449 []
2460 []
2450 <generatorsetdesc->
2461 <generatorsetdesc->
2451 $ cd ..
2462 $ cd ..
2452
2463
2453 Test --hidden
2464 Test --hidden
2454 (enable obsolete)
2465 (enable obsolete)
2455
2466
2456 $ cat >> $HGRCPATH << EOF
2467 $ cat >> $HGRCPATH << EOF
2457 > [experimental]
2468 > [experimental]
2458 > evolution.createmarkers=True
2469 > evolution.createmarkers=True
2459 > EOF
2470 > EOF
2460
2471
2461 $ hg debugobsolete `hg id --debug -i -r 8`
2472 $ hg debugobsolete `hg id --debug -i -r 8`
2462 obsoleted 1 changesets
2473 obsoleted 1 changesets
2463 $ testlog
2474 $ testlog
2464 []
2475 []
2465 []
2476 []
2466 <spanset- 0:9>
2477 <spanset- 0:9>
2467 $ testlog --hidden
2478 $ testlog --hidden
2468 []
2479 []
2469 []
2480 []
2470 <spanset- 0:9>
2481 <spanset- 0:9>
2471 $ hg log -G --template '{rev} {desc}\n'
2482 $ hg log -G --template '{rev} {desc}\n'
2472 \xe2\x97\x8b 7 Added tag foo-bar for changeset fc281d8ff18d (esc)
2483 \xe2\x97\x8b 7 Added tag foo-bar for changeset fc281d8ff18d (esc)
2473 \xe2\x94\x82 (esc)
2484 \xe2\x94\x82 (esc)
2474 \xe2\x97\x8b 6 merge 5 and 4 (esc)
2485 \xe2\x97\x8b 6 merge 5 and 4 (esc)
2475 \xe2\x94\x82\xe2\x95\xb2 (esc)
2486 \xe2\x94\x82\xe2\x95\xb2 (esc)
2476 \xe2\x94\x82 \xe2\x97\x8b 5 add another e (esc)
2487 \xe2\x94\x82 \xe2\x97\x8b 5 add another e (esc)
2477 \xe2\x94\x82 \xe2\x94\x82 (esc)
2488 \xe2\x94\x82 \xe2\x94\x82 (esc)
2478 \xe2\x97\x8b \xe2\x94\x82 4 mv dir/b e (esc)
2489 \xe2\x97\x8b \xe2\x94\x82 4 mv dir/b e (esc)
2479 \xe2\x94\x82\xe2\x95\xb1 (esc)
2490 \xe2\x94\x82\xe2\x95\xb1 (esc)
2480 \xe2\x97\x8d 3 mv a b; add d (esc)
2491 \xe2\x97\x8d 3 mv a b; add d (esc)
2481 \xe2\x94\x82 (esc)
2492 \xe2\x94\x82 (esc)
2482 \xe2\x97\x8b 2 mv b dir/b (esc)
2493 \xe2\x97\x8b 2 mv b dir/b (esc)
2483 \xe2\x94\x82 (esc)
2494 \xe2\x94\x82 (esc)
2484 \xe2\x97\x8b 1 copy a b (esc)
2495 \xe2\x97\x8b 1 copy a b (esc)
2485 \xe2\x94\x82 (esc)
2496 \xe2\x94\x82 (esc)
2486 \xe2\x97\x8b 0 add a (esc)
2497 \xe2\x97\x8b 0 add a (esc)
2487
2498
2488
2499
2489 A template without trailing newline should do something sane
2500 A template without trailing newline should do something sane
2490
2501
2491 $ hg log -G -r ::2 --template '{rev} {desc}'
2502 $ hg log -G -r ::2 --template '{rev} {desc}'
2492 \xe2\x97\x8b 2 mv b dir/b (esc)
2503 \xe2\x97\x8b 2 mv b dir/b (esc)
2493 \xe2\x94\x82 (esc)
2504 \xe2\x94\x82 (esc)
2494 \xe2\x97\x8b 1 copy a b (esc)
2505 \xe2\x97\x8b 1 copy a b (esc)
2495 \xe2\x94\x82 (esc)
2506 \xe2\x94\x82 (esc)
2496 \xe2\x97\x8b 0 add a (esc)
2507 \xe2\x97\x8b 0 add a (esc)
2497
2508
2498
2509
2499 Extra newlines must be preserved
2510 Extra newlines must be preserved
2500
2511
2501 $ hg log -G -r ::2 --template '\n{rev} {desc}\n\n'
2512 $ hg log -G -r ::2 --template '\n{rev} {desc}\n\n'
2502 \xe2\x97\x8b (esc)
2513 \xe2\x97\x8b (esc)
2503 \xe2\x94\x82 2 mv b dir/b (esc)
2514 \xe2\x94\x82 2 mv b dir/b (esc)
2504 \xe2\x94\x82 (esc)
2515 \xe2\x94\x82 (esc)
2505 \xe2\x97\x8b (esc)
2516 \xe2\x97\x8b (esc)
2506 \xe2\x94\x82 1 copy a b (esc)
2517 \xe2\x94\x82 1 copy a b (esc)
2507 \xe2\x94\x82 (esc)
2518 \xe2\x94\x82 (esc)
2508 \xe2\x97\x8b (esc)
2519 \xe2\x97\x8b (esc)
2509 0 add a
2520 0 add a
2510
2521
2511
2522
2512 The almost-empty template should do something sane too ...
2523 The almost-empty template should do something sane too ...
2513
2524
2514 $ hg log -G -r ::2 --template '\n'
2525 $ hg log -G -r ::2 --template '\n'
2515 \xe2\x97\x8b (esc)
2526 \xe2\x97\x8b (esc)
2516 \xe2\x94\x82 (esc)
2527 \xe2\x94\x82 (esc)
2517 \xe2\x97\x8b (esc)
2528 \xe2\x97\x8b (esc)
2518 \xe2\x94\x82 (esc)
2529 \xe2\x94\x82 (esc)
2519 \xe2\x97\x8b (esc)
2530 \xe2\x97\x8b (esc)
2520
2531
2521
2532
2522 issue3772
2533 issue3772
2523
2534
2524 $ hg log -G -r :null
2535 $ hg log -G -r :null
2525 \xe2\x97\x8b changeset: 0:f8035bb17114 (esc)
2536 \xe2\x97\x8b changeset: 0:f8035bb17114 (esc)
2526 \xe2\x94\x82 user: test (esc)
2537 \xe2\x94\x82 user: test (esc)
2527 \xe2\x94\x82 date: Thu Jan 01 00:00:00 1970 +0000 (esc)
2538 \xe2\x94\x82 date: Thu Jan 01 00:00:00 1970 +0000 (esc)
2528 \xe2\x94\x82 summary: add a (esc)
2539 \xe2\x94\x82 summary: add a (esc)
2529 \xe2\x94\x82 (esc)
2540 \xe2\x94\x82 (esc)
2530 \xe2\x97\x8b changeset: -1:000000000000 (esc)
2541 \xe2\x97\x8b changeset: -1:000000000000 (esc)
2531 user:
2542 user:
2532 date: Thu Jan 01 00:00:00 1970 +0000
2543 date: Thu Jan 01 00:00:00 1970 +0000
2533
2544
2534 $ hg log -G -r null:null
2545 $ hg log -G -r null:null
2535 \xe2\x97\x8b changeset: -1:000000000000 (esc)
2546 \xe2\x97\x8b changeset: -1:000000000000 (esc)
2536 user:
2547 user:
2537 date: Thu Jan 01 00:00:00 1970 +0000
2548 date: Thu Jan 01 00:00:00 1970 +0000
2538
2549
2539
2550
2540 should not draw line down to null due to the magic of fullreposet
2551 should not draw line down to null due to the magic of fullreposet
2541
2552
2542 $ hg log -G -r 'all()' | tail -6
2553 $ hg log -G -r 'all()' | tail -6
2543 \xe2\x94\x82 (esc)
2554 \xe2\x94\x82 (esc)
2544 \xe2\x97\x8b changeset: 0:f8035bb17114 (esc)
2555 \xe2\x97\x8b changeset: 0:f8035bb17114 (esc)
2545 user: test
2556 user: test
2546 date: Thu Jan 01 00:00:00 1970 +0000
2557 date: Thu Jan 01 00:00:00 1970 +0000
2547 summary: add a
2558 summary: add a
2548
2559
2549
2560
2550 $ hg log -G -r 'branch(default)' | tail -6
2561 $ hg log -G -r 'branch(default)' | tail -6
2551 \xe2\x94\x82 (esc)
2562 \xe2\x94\x82 (esc)
2552 \xe2\x97\x8b changeset: 0:f8035bb17114 (esc)
2563 \xe2\x97\x8b changeset: 0:f8035bb17114 (esc)
2553 user: test
2564 user: test
2554 date: Thu Jan 01 00:00:00 1970 +0000
2565 date: Thu Jan 01 00:00:00 1970 +0000
2555 summary: add a
2566 summary: add a
2556
2567
2557
2568
2558 working-directory revision
2569 working-directory revision
2559
2570
2560 $ hg log -G -qr '. + wdir()'
2571 $ hg log -G -qr '. + wdir()'
2561 \xe2\x97\x8b 2147483647:ffffffffffff (esc)
2572 \xe2\x97\x8b 2147483647:ffffffffffff (esc)
2562 \xe2\x94\x82 (esc)
2573 \xe2\x94\x82 (esc)
2563 \xe2\x97\x8d 3:5918b8d165d1 (esc)
2574 \xe2\x97\x8d 3:5918b8d165d1 (esc)
2564 \xe2\x94\x82 (esc)
2575 \xe2\x94\x82 (esc)
2565 \xe2\x95\xa7 (esc)
2576 \xe2\x95\xa7 (esc)
2566
2577
2567 node template with changesetprinter:
2578 node template with changesetprinter:
2568
2579
2569 $ hg log -Gqr 5:7 --config ui.graphnodetemplate='"{rev}"'
2580 $ hg log -Gqr 5:7 --config ui.graphnodetemplate='"{rev}"'
2570 7 7:02dbb8e276b8
2581 7 7:02dbb8e276b8
2571 \xe2\x94\x82 (esc)
2582 \xe2\x94\x82 (esc)
2572 6 6:fc281d8ff18d
2583 6 6:fc281d8ff18d
2573 \xe2\x94\x82\xe2\x95\xb2 (esc)
2584 \xe2\x94\x82\xe2\x95\xb2 (esc)
2574 \xe2\x94\x82 \xe2\x95\xa7 (esc)
2585 \xe2\x94\x82 \xe2\x95\xa7 (esc)
2575 5 5:99b31f1c2782
2586 5 5:99b31f1c2782
2576 \xe2\x94\x82 (esc)
2587 \xe2\x94\x82 (esc)
2577 \xe2\x95\xa7 (esc)
2588 \xe2\x95\xa7 (esc)
2578
2589
2579 node template with changesettemplater (shared cache variable):
2590 node template with changesettemplater (shared cache variable):
2580
2591
2581 $ hg log -Gr 5:7 -T '{latesttag % "{rev} {tag}+{distance}"}\n' \
2592 $ hg log -Gr 5:7 -T '{latesttag % "{rev} {tag}+{distance}"}\n' \
2582 > --config ui.graphnodetemplate='{ifeq(latesttagdistance, 0, "#", graphnode)}'
2593 > --config ui.graphnodetemplate='{ifeq(latesttagdistance, 0, "#", graphnode)}'
2583 \xe2\x97\x8b 7 foo-bar+1 (esc)
2594 \xe2\x97\x8b 7 foo-bar+1 (esc)
2584 \xe2\x94\x82 (esc)
2595 \xe2\x94\x82 (esc)
2585 # 6 foo-bar+0
2596 # 6 foo-bar+0
2586 \xe2\x94\x82\xe2\x95\xb2 (esc)
2597 \xe2\x94\x82\xe2\x95\xb2 (esc)
2587 \xe2\x94\x82 \xe2\x95\xa7 (esc)
2598 \xe2\x94\x82 \xe2\x95\xa7 (esc)
2588 \xe2\x97\x8b 5 null+5 (esc)
2599 \xe2\x97\x8b 5 null+5 (esc)
2589 \xe2\x94\x82 (esc)
2600 \xe2\x94\x82 (esc)
2590 \xe2\x95\xa7 (esc)
2601 \xe2\x95\xa7 (esc)
2591
2602
2592 label() should just work in node template:
2603 label() should just work in node template:
2593
2604
2594 $ hg log -Gqr 7 --config extensions.color= --color=debug \
2605 $ hg log -Gqr 7 --config extensions.color= --color=debug \
2595 > --config ui.graphnodetemplate='{label("branch.{branch}", rev)}'
2606 > --config ui.graphnodetemplate='{label("branch.{branch}", rev)}'
2596 [branch.default\xe2\x94\x827] [log.node|7:02dbb8e276b8] (esc)
2607 [branch.default\xe2\x94\x827] [log.node|7:02dbb8e276b8] (esc)
2597 \xe2\x94\x82 (esc)
2608 \xe2\x94\x82 (esc)
2598 \xe2\x95\xa7 (esc)
2609 \xe2\x95\xa7 (esc)
2599
2610
2600 $ cd ..
2611 $ cd ..
2601
2612
2602 change graph edge styling
2613 change graph edge styling
2603
2614
2604 $ cd repo
2615 $ cd repo
2605
2616
2606 Setting HGPLAIN ignores graphmod styling:
2617 Setting HGPLAIN ignores graphmod styling:
2607
2618
2608 $ HGPLAIN=1 hg log -G -r 'file("a")' -m
2619 $ HGPLAIN=1 hg log -G -r 'file("a")' -m
2609 @ changeset: 36:08a19a744424
2620 @ changeset: 36:08a19a744424
2610 | branch: branch
2621 | branch: branch
2611 | tag: tip
2622 | tag: tip
2612 | parent: 35:9159c3644c5e
2623 | parent: 35:9159c3644c5e
2613 | parent: 35:9159c3644c5e
2624 | parent: 35:9159c3644c5e
2614 | user: test
2625 | user: test
2615 | date: Thu Jan 01 00:00:36 1970 +0000
2626 | date: Thu Jan 01 00:00:36 1970 +0000
2616 | summary: (36) buggy merge: identical parents
2627 | summary: (36) buggy merge: identical parents
2617 |
2628 |
2618 o changeset: 32:d06dffa21a31
2629 o changeset: 32:d06dffa21a31
2619 |\ parent: 27:886ed638191b
2630 |\ parent: 27:886ed638191b
2620 | | parent: 31:621d83e11f67
2631 | | parent: 31:621d83e11f67
2621 | | user: test
2632 | | user: test
2622 | | date: Thu Jan 01 00:00:32 1970 +0000
2633 | | date: Thu Jan 01 00:00:32 1970 +0000
2623 | | summary: (32) expand
2634 | | summary: (32) expand
2624 | |
2635 | |
2625 o | changeset: 31:621d83e11f67
2636 o | changeset: 31:621d83e11f67
2626 |\| parent: 21:d42a756af44d
2637 |\| parent: 21:d42a756af44d
2627 | | parent: 30:6e11cd4b648f
2638 | | parent: 30:6e11cd4b648f
2628 | | user: test
2639 | | user: test
2629 | | date: Thu Jan 01 00:00:31 1970 +0000
2640 | | date: Thu Jan 01 00:00:31 1970 +0000
2630 | | summary: (31) expand
2641 | | summary: (31) expand
2631 | |
2642 | |
2632 o | changeset: 30:6e11cd4b648f
2643 o | changeset: 30:6e11cd4b648f
2633 |\ \ parent: 28:44ecd0b9ae99
2644 |\ \ parent: 28:44ecd0b9ae99
2634 | | | parent: 29:cd9bb2be7593
2645 | | | parent: 29:cd9bb2be7593
2635 | | | user: test
2646 | | | user: test
2636 | | | date: Thu Jan 01 00:00:30 1970 +0000
2647 | | | date: Thu Jan 01 00:00:30 1970 +0000
2637 | | | summary: (30) expand
2648 | | | summary: (30) expand
2638 | | |
2649 | | |
2639 o | | changeset: 28:44ecd0b9ae99
2650 o | | changeset: 28:44ecd0b9ae99
2640 |\ \ \ parent: 1:6db2ef61d156
2651 |\ \ \ parent: 1:6db2ef61d156
2641 | | | | parent: 26:7f25b6c2f0b9
2652 | | | | parent: 26:7f25b6c2f0b9
2642 | | | | user: test
2653 | | | | user: test
2643 | | | | date: Thu Jan 01 00:00:28 1970 +0000
2654 | | | | date: Thu Jan 01 00:00:28 1970 +0000
2644 | | | | summary: (28) merge zero known
2655 | | | | summary: (28) merge zero known
2645 | | | |
2656 | | | |
2646 o | | | changeset: 26:7f25b6c2f0b9
2657 o | | | changeset: 26:7f25b6c2f0b9
2647 |\ \ \ \ parent: 18:1aa84d96232a
2658 |\ \ \ \ parent: 18:1aa84d96232a
2648 | | | | | parent: 25:91da8ed57247
2659 | | | | | parent: 25:91da8ed57247
2649 | | | | | user: test
2660 | | | | | user: test
2650 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
2661 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
2651 | | | | | summary: (26) merge one known; far right
2662 | | | | | summary: (26) merge one known; far right
2652 | | | | |
2663 | | | | |
2653 | o-----+ changeset: 25:91da8ed57247
2664 | o-----+ changeset: 25:91da8ed57247
2654 | | | | | parent: 21:d42a756af44d
2665 | | | | | parent: 21:d42a756af44d
2655 | | | | | parent: 24:a9c19a3d96b7
2666 | | | | | parent: 24:a9c19a3d96b7
2656 | | | | | user: test
2667 | | | | | user: test
2657 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
2668 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
2658 | | | | | summary: (25) merge one known; far left
2669 | | | | | summary: (25) merge one known; far left
2659 | | | | |
2670 | | | | |
2660 | o | | | changeset: 24:a9c19a3d96b7
2671 | o | | | changeset: 24:a9c19a3d96b7
2661 | |\ \ \ \ parent: 0:e6eb3150255d
2672 | |\ \ \ \ parent: 0:e6eb3150255d
2662 | | | | | | parent: 23:a01cddf0766d
2673 | | | | | | parent: 23:a01cddf0766d
2663 | | | | | | user: test
2674 | | | | | | user: test
2664 | | | | | | date: Thu Jan 01 00:00:24 1970 +0000
2675 | | | | | | date: Thu Jan 01 00:00:24 1970 +0000
2665 | | | | | | summary: (24) merge one known; immediate right
2676 | | | | | | summary: (24) merge one known; immediate right
2666 | | | | | |
2677 | | | | | |
2667 | o---+ | | changeset: 23:a01cddf0766d
2678 | o---+ | | changeset: 23:a01cddf0766d
2668 | | | | | | parent: 1:6db2ef61d156
2679 | | | | | | parent: 1:6db2ef61d156
2669 | | | | | | parent: 22:e0d9cccacb5d
2680 | | | | | | parent: 22:e0d9cccacb5d
2670 | | | | | | user: test
2681 | | | | | | user: test
2671 | | | | | | date: Thu Jan 01 00:00:23 1970 +0000
2682 | | | | | | date: Thu Jan 01 00:00:23 1970 +0000
2672 | | | | | | summary: (23) merge one known; immediate left
2683 | | | | | | summary: (23) merge one known; immediate left
2673 | | | | | |
2684 | | | | | |
2674 | o-------+ changeset: 22:e0d9cccacb5d
2685 | o-------+ changeset: 22:e0d9cccacb5d
2675 | | | | | | parent: 18:1aa84d96232a
2686 | | | | | | parent: 18:1aa84d96232a
2676 |/ / / / / parent: 21:d42a756af44d
2687 |/ / / / / parent: 21:d42a756af44d
2677 | | | | | user: test
2688 | | | | | user: test
2678 | | | | | date: Thu Jan 01 00:00:22 1970 +0000
2689 | | | | | date: Thu Jan 01 00:00:22 1970 +0000
2679 | | | | | summary: (22) merge two known; one far left, one far right
2690 | | | | | summary: (22) merge two known; one far left, one far right
2680 | | | | |
2691 | | | | |
2681 | | | | o changeset: 21:d42a756af44d
2692 | | | | o changeset: 21:d42a756af44d
2682 | | | | |\ parent: 19:31ddc2c1573b
2693 | | | | |\ parent: 19:31ddc2c1573b
2683 | | | | | | parent: 20:d30ed6450e32
2694 | | | | | | parent: 20:d30ed6450e32
2684 | | | | | | user: test
2695 | | | | | | user: test
2685 | | | | | | date: Thu Jan 01 00:00:21 1970 +0000
2696 | | | | | | date: Thu Jan 01 00:00:21 1970 +0000
2686 | | | | | | summary: (21) expand
2697 | | | | | | summary: (21) expand
2687 | | | | | |
2698 | | | | | |
2688 +-+-------o changeset: 20:d30ed6450e32
2699 +-+-------o changeset: 20:d30ed6450e32
2689 | | | | | parent: 0:e6eb3150255d
2700 | | | | | parent: 0:e6eb3150255d
2690 | | | | | parent: 18:1aa84d96232a
2701 | | | | | parent: 18:1aa84d96232a
2691 | | | | | user: test
2702 | | | | | user: test
2692 | | | | | date: Thu Jan 01 00:00:20 1970 +0000
2703 | | | | | date: Thu Jan 01 00:00:20 1970 +0000
2693 | | | | | summary: (20) merge two known; two far right
2704 | | | | | summary: (20) merge two known; two far right
2694 | | | | |
2705 | | | | |
2695 | | | | o changeset: 19:31ddc2c1573b
2706 | | | | o changeset: 19:31ddc2c1573b
2696 | | | | |\ parent: 15:1dda3f72782d
2707 | | | | |\ parent: 15:1dda3f72782d
2697 | | | | | | parent: 17:44765d7c06e0
2708 | | | | | | parent: 17:44765d7c06e0
2698 | | | | | | user: test
2709 | | | | | | user: test
2699 | | | | | | date: Thu Jan 01 00:00:19 1970 +0000
2710 | | | | | | date: Thu Jan 01 00:00:19 1970 +0000
2700 | | | | | | summary: (19) expand
2711 | | | | | | summary: (19) expand
2701 | | | | | |
2712 | | | | | |
2702 o---+---+ | changeset: 18:1aa84d96232a
2713 o---+---+ | changeset: 18:1aa84d96232a
2703 | | | | | parent: 1:6db2ef61d156
2714 | | | | | parent: 1:6db2ef61d156
2704 / / / / / parent: 15:1dda3f72782d
2715 / / / / / parent: 15:1dda3f72782d
2705 | | | | | user: test
2716 | | | | | user: test
2706 | | | | | date: Thu Jan 01 00:00:18 1970 +0000
2717 | | | | | date: Thu Jan 01 00:00:18 1970 +0000
2707 | | | | | summary: (18) merge two known; two far left
2718 | | | | | summary: (18) merge two known; two far left
2708 | | | | |
2719 | | | | |
2709 | | | | o changeset: 17:44765d7c06e0
2720 | | | | o changeset: 17:44765d7c06e0
2710 | | | | |\ parent: 12:86b91144a6e9
2721 | | | | |\ parent: 12:86b91144a6e9
2711 | | | | | | parent: 16:3677d192927d
2722 | | | | | | parent: 16:3677d192927d
2712 | | | | | | user: test
2723 | | | | | | user: test
2713 | | | | | | date: Thu Jan 01 00:00:17 1970 +0000
2724 | | | | | | date: Thu Jan 01 00:00:17 1970 +0000
2714 | | | | | | summary: (17) expand
2725 | | | | | | summary: (17) expand
2715 | | | | | |
2726 | | | | | |
2716 +-+-------o changeset: 16:3677d192927d
2727 +-+-------o changeset: 16:3677d192927d
2717 | | | | | parent: 0:e6eb3150255d
2728 | | | | | parent: 0:e6eb3150255d
2718 | | | | | parent: 1:6db2ef61d156
2729 | | | | | parent: 1:6db2ef61d156
2719 | | | | | user: test
2730 | | | | | user: test
2720 | | | | | date: Thu Jan 01 00:00:16 1970 +0000
2731 | | | | | date: Thu Jan 01 00:00:16 1970 +0000
2721 | | | | | summary: (16) merge two known; one immediate right, one near right
2732 | | | | | summary: (16) merge two known; one immediate right, one near right
2722 | | | | |
2733 | | | | |
2723 | | | o | changeset: 15:1dda3f72782d
2734 | | | o | changeset: 15:1dda3f72782d
2724 | | | |\ \ parent: 13:22d8966a97e3
2735 | | | |\ \ parent: 13:22d8966a97e3
2725 | | | | | | parent: 14:8eac370358ef
2736 | | | | | | parent: 14:8eac370358ef
2726 | | | | | | user: test
2737 | | | | | | user: test
2727 | | | | | | date: Thu Jan 01 00:00:15 1970 +0000
2738 | | | | | | date: Thu Jan 01 00:00:15 1970 +0000
2728 | | | | | | summary: (15) expand
2739 | | | | | | summary: (15) expand
2729 | | | | | |
2740 | | | | | |
2730 +-------o | changeset: 14:8eac370358ef
2741 +-------o | changeset: 14:8eac370358ef
2731 | | | | |/ parent: 0:e6eb3150255d
2742 | | | | |/ parent: 0:e6eb3150255d
2732 | | | | | parent: 12:86b91144a6e9
2743 | | | | | parent: 12:86b91144a6e9
2733 | | | | | user: test
2744 | | | | | user: test
2734 | | | | | date: Thu Jan 01 00:00:14 1970 +0000
2745 | | | | | date: Thu Jan 01 00:00:14 1970 +0000
2735 | | | | | summary: (14) merge two known; one immediate right, one far right
2746 | | | | | summary: (14) merge two known; one immediate right, one far right
2736 | | | | |
2747 | | | | |
2737 | | | o | changeset: 13:22d8966a97e3
2748 | | | o | changeset: 13:22d8966a97e3
2738 | | | |\ \ parent: 9:7010c0af0a35
2749 | | | |\ \ parent: 9:7010c0af0a35
2739 | | | | | | parent: 11:832d76e6bdf2
2750 | | | | | | parent: 11:832d76e6bdf2
2740 | | | | | | user: test
2751 | | | | | | user: test
2741 | | | | | | date: Thu Jan 01 00:00:13 1970 +0000
2752 | | | | | | date: Thu Jan 01 00:00:13 1970 +0000
2742 | | | | | | summary: (13) expand
2753 | | | | | | summary: (13) expand
2743 | | | | | |
2754 | | | | | |
2744 | +---+---o changeset: 12:86b91144a6e9
2755 | +---+---o changeset: 12:86b91144a6e9
2745 | | | | | parent: 1:6db2ef61d156
2756 | | | | | parent: 1:6db2ef61d156
2746 | | | | | parent: 9:7010c0af0a35
2757 | | | | | parent: 9:7010c0af0a35
2747 | | | | | user: test
2758 | | | | | user: test
2748 | | | | | date: Thu Jan 01 00:00:12 1970 +0000
2759 | | | | | date: Thu Jan 01 00:00:12 1970 +0000
2749 | | | | | summary: (12) merge two known; one immediate right, one far left
2760 | | | | | summary: (12) merge two known; one immediate right, one far left
2750 | | | | |
2761 | | | | |
2751 | | | | o changeset: 11:832d76e6bdf2
2762 | | | | o changeset: 11:832d76e6bdf2
2752 | | | | |\ parent: 6:b105a072e251
2763 | | | | |\ parent: 6:b105a072e251
2753 | | | | | | parent: 10:74c64d036d72
2764 | | | | | | parent: 10:74c64d036d72
2754 | | | | | | user: test
2765 | | | | | | user: test
2755 | | | | | | date: Thu Jan 01 00:00:11 1970 +0000
2766 | | | | | | date: Thu Jan 01 00:00:11 1970 +0000
2756 | | | | | | summary: (11) expand
2767 | | | | | | summary: (11) expand
2757 | | | | | |
2768 | | | | | |
2758 +---------o changeset: 10:74c64d036d72
2769 +---------o changeset: 10:74c64d036d72
2759 | | | | |/ parent: 0:e6eb3150255d
2770 | | | | |/ parent: 0:e6eb3150255d
2760 | | | | | parent: 6:b105a072e251
2771 | | | | | parent: 6:b105a072e251
2761 | | | | | user: test
2772 | | | | | user: test
2762 | | | | | date: Thu Jan 01 00:00:10 1970 +0000
2773 | | | | | date: Thu Jan 01 00:00:10 1970 +0000
2763 | | | | | summary: (10) merge two known; one immediate left, one near right
2774 | | | | | summary: (10) merge two known; one immediate left, one near right
2764 | | | | |
2775 | | | | |
2765 | | | o | changeset: 9:7010c0af0a35
2776 | | | o | changeset: 9:7010c0af0a35
2766 | | | |\ \ parent: 7:b632bb1b1224
2777 | | | |\ \ parent: 7:b632bb1b1224
2767 | | | | | | parent: 8:7a0b11f71937
2778 | | | | | | parent: 8:7a0b11f71937
2768 | | | | | | user: test
2779 | | | | | | user: test
2769 | | | | | | date: Thu Jan 01 00:00:09 1970 +0000
2780 | | | | | | date: Thu Jan 01 00:00:09 1970 +0000
2770 | | | | | | summary: (9) expand
2781 | | | | | | summary: (9) expand
2771 | | | | | |
2782 | | | | | |
2772 +-------o | changeset: 8:7a0b11f71937
2783 +-------o | changeset: 8:7a0b11f71937
2773 | | | |/ / parent: 0:e6eb3150255d
2784 | | | |/ / parent: 0:e6eb3150255d
2774 | | | | | parent: 7:b632bb1b1224
2785 | | | | | parent: 7:b632bb1b1224
2775 | | | | | user: test
2786 | | | | | user: test
2776 | | | | | date: Thu Jan 01 00:00:08 1970 +0000
2787 | | | | | date: Thu Jan 01 00:00:08 1970 +0000
2777 | | | | | summary: (8) merge two known; one immediate left, one far right
2788 | | | | | summary: (8) merge two known; one immediate left, one far right
2778 | | | | |
2789 | | | | |
2779 | | | o | changeset: 7:b632bb1b1224
2790 | | | o | changeset: 7:b632bb1b1224
2780 | | | |\ \ parent: 2:3d9a33b8d1e1
2791 | | | |\ \ parent: 2:3d9a33b8d1e1
2781 | | | | | | parent: 5:4409d547b708
2792 | | | | | | parent: 5:4409d547b708
2782 | | | | | | user: test
2793 | | | | | | user: test
2783 | | | | | | date: Thu Jan 01 00:00:07 1970 +0000
2794 | | | | | | date: Thu Jan 01 00:00:07 1970 +0000
2784 | | | | | | summary: (7) expand
2795 | | | | | | summary: (7) expand
2785 | | | | | |
2796 | | | | | |
2786 | | | +---o changeset: 6:b105a072e251
2797 | | | +---o changeset: 6:b105a072e251
2787 | | | | |/ parent: 2:3d9a33b8d1e1
2798 | | | | |/ parent: 2:3d9a33b8d1e1
2788 | | | | | parent: 5:4409d547b708
2799 | | | | | parent: 5:4409d547b708
2789 | | | | | user: test
2800 | | | | | user: test
2790 | | | | | date: Thu Jan 01 00:00:06 1970 +0000
2801 | | | | | date: Thu Jan 01 00:00:06 1970 +0000
2791 | | | | | summary: (6) merge two known; one immediate left, one far left
2802 | | | | | summary: (6) merge two known; one immediate left, one far left
2792 | | | | |
2803 | | | | |
2793 | | | o | changeset: 5:4409d547b708
2804 | | | o | changeset: 5:4409d547b708
2794 | | | |\ \ parent: 3:27eef8ed80b4
2805 | | | |\ \ parent: 3:27eef8ed80b4
2795 | | | | | | parent: 4:26a8bac39d9f
2806 | | | | | | parent: 4:26a8bac39d9f
2796 | | | | | | user: test
2807 | | | | | | user: test
2797 | | | | | | date: Thu Jan 01 00:00:05 1970 +0000
2808 | | | | | | date: Thu Jan 01 00:00:05 1970 +0000
2798 | | | | | | summary: (5) expand
2809 | | | | | | summary: (5) expand
2799 | | | | | |
2810 | | | | | |
2800 | +---o | | changeset: 4:26a8bac39d9f
2811 | +---o | | changeset: 4:26a8bac39d9f
2801 | | | |/ / parent: 1:6db2ef61d156
2812 | | | |/ / parent: 1:6db2ef61d156
2802 | | | | | parent: 3:27eef8ed80b4
2813 | | | | | parent: 3:27eef8ed80b4
2803 | | | | | user: test
2814 | | | | | user: test
2804 | | | | | date: Thu Jan 01 00:00:04 1970 +0000
2815 | | | | | date: Thu Jan 01 00:00:04 1970 +0000
2805 | | | | | summary: (4) merge two known; one immediate left, one immediate right
2816 | | | | | summary: (4) merge two known; one immediate left, one immediate right
2806 | | | | |
2817 | | | | |
2807
2818
2808 .. unless HGPLAINEXCEPT=graph is set:
2819 .. unless HGPLAINEXCEPT=graph is set:
2809
2820
2810 $ HGPLAIN=1 HGPLAINEXCEPT=graph hg log -G -r 'file("a")' -m
2821 $ HGPLAIN=1 HGPLAINEXCEPT=graph hg log -G -r 'file("a")' -m
2811 \xe2\x97\x8d changeset: 36:08a19a744424 (esc)
2822 \xe2\x97\x8d changeset: 36:08a19a744424 (esc)
2812 \xe2\x94\x86 branch: branch (esc)
2823 \xe2\x94\x86 branch: branch (esc)
2813 \xe2\x94\x86 tag: tip (esc)
2824 \xe2\x94\x86 tag: tip (esc)
2814 \xe2\x94\x86 parent: 35:9159c3644c5e (esc)
2825 \xe2\x94\x86 parent: 35:9159c3644c5e (esc)
2815 \xe2\x94\x86 parent: 35:9159c3644c5e (esc)
2826 \xe2\x94\x86 parent: 35:9159c3644c5e (esc)
2816 \xe2\x94\x86 user: test (esc)
2827 \xe2\x94\x86 user: test (esc)
2817 \xe2\x94\x86 date: Thu Jan 01 00:00:36 1970 +0000 (esc)
2828 \xe2\x94\x86 date: Thu Jan 01 00:00:36 1970 +0000 (esc)
2818 \xe2\x94\x86 summary: (36) buggy merge: identical parents (esc)
2829 \xe2\x94\x86 summary: (36) buggy merge: identical parents (esc)
2819 \xe2\x94\x86 (esc)
2830 \xe2\x94\x86 (esc)
2820 \xe2\x97\x8b changeset: 32:d06dffa21a31 (esc)
2831 \xe2\x97\x8b changeset: 32:d06dffa21a31 (esc)
2821 \xe2\x94\x82\xe2\x95\xb2 parent: 27:886ed638191b (esc)
2832 \xe2\x94\x82\xe2\x95\xb2 parent: 27:886ed638191b (esc)
2822 \xe2\x94\x82 \xe2\x94\x86 parent: 31:621d83e11f67 (esc)
2833 \xe2\x94\x82 \xe2\x94\x86 parent: 31:621d83e11f67 (esc)
2823 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
2834 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
2824 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:32 1970 +0000 (esc)
2835 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:32 1970 +0000 (esc)
2825 \xe2\x94\x82 \xe2\x94\x86 summary: (32) expand (esc)
2836 \xe2\x94\x82 \xe2\x94\x86 summary: (32) expand (esc)
2826 \xe2\x94\x82 \xe2\x94\x86 (esc)
2837 \xe2\x94\x82 \xe2\x94\x86 (esc)
2827 \xe2\x97\x8b \xe2\x94\x86 changeset: 31:621d83e11f67 (esc)
2838 \xe2\x97\x8b \xe2\x94\x86 changeset: 31:621d83e11f67 (esc)
2828 \xe2\x94\x82\xe2\x95\xb2\xe2\x94\x86 parent: 21:d42a756af44d (esc)
2839 \xe2\x94\x82\xe2\x95\xb2\xe2\x94\x86 parent: 21:d42a756af44d (esc)
2829 \xe2\x94\x82 \xe2\x94\x86 parent: 30:6e11cd4b648f (esc)
2840 \xe2\x94\x82 \xe2\x94\x86 parent: 30:6e11cd4b648f (esc)
2830 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
2841 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
2831 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:31 1970 +0000 (esc)
2842 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:31 1970 +0000 (esc)
2832 \xe2\x94\x82 \xe2\x94\x86 summary: (31) expand (esc)
2843 \xe2\x94\x82 \xe2\x94\x86 summary: (31) expand (esc)
2833 \xe2\x94\x82 \xe2\x94\x86 (esc)
2844 \xe2\x94\x82 \xe2\x94\x86 (esc)
2834 \xe2\x97\x8b \xe2\x94\x86 changeset: 30:6e11cd4b648f (esc)
2845 \xe2\x97\x8b \xe2\x94\x86 changeset: 30:6e11cd4b648f (esc)
2835 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 28:44ecd0b9ae99 (esc)
2846 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 28:44ecd0b9ae99 (esc)
2836 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x86 parent: 29:cd9bb2be7593 (esc)
2847 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x86 parent: 29:cd9bb2be7593 (esc)
2837 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
2848 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
2838 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:30 1970 +0000 (esc)
2849 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:30 1970 +0000 (esc)
2839 \xe2\x94\x82 \xe2\x94\x86 summary: (30) expand (esc)
2850 \xe2\x94\x82 \xe2\x94\x86 summary: (30) expand (esc)
2840 \xe2\x94\x82 \xe2\x95\xb1 (esc)
2851 \xe2\x94\x82 \xe2\x95\xb1 (esc)
2841 \xe2\x97\x8b \xe2\x94\x86 changeset: 28:44ecd0b9ae99 (esc)
2852 \xe2\x97\x8b \xe2\x94\x86 changeset: 28:44ecd0b9ae99 (esc)
2842 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 1:6db2ef61d156 (esc)
2853 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 1:6db2ef61d156 (esc)
2843 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x86 parent: 26:7f25b6c2f0b9 (esc)
2854 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x86 parent: 26:7f25b6c2f0b9 (esc)
2844 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
2855 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
2845 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:28 1970 +0000 (esc)
2856 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:28 1970 +0000 (esc)
2846 \xe2\x94\x82 \xe2\x94\x86 summary: (28) merge zero known (esc)
2857 \xe2\x94\x82 \xe2\x94\x86 summary: (28) merge zero known (esc)
2847 \xe2\x94\x82 \xe2\x95\xb1 (esc)
2858 \xe2\x94\x82 \xe2\x95\xb1 (esc)
2848 \xe2\x97\x8b \xe2\x94\x86 changeset: 26:7f25b6c2f0b9 (esc)
2859 \xe2\x97\x8b \xe2\x94\x86 changeset: 26:7f25b6c2f0b9 (esc)
2849 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 18:1aa84d96232a (esc)
2860 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 18:1aa84d96232a (esc)
2850 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 parent: 25:91da8ed57247 (esc)
2861 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 parent: 25:91da8ed57247 (esc)
2851 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
2862 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
2852 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:26 1970 +0000 (esc)
2863 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:26 1970 +0000 (esc)
2853 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 summary: (26) merge one known; far right (esc)
2864 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 summary: (26) merge one known; far right (esc)
2854 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 (esc)
2865 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 (esc)
2855 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x86 changeset: 25:91da8ed57247 (esc)
2866 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x86 changeset: 25:91da8ed57247 (esc)
2856 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2\xe2\x94\x86 parent: 21:d42a756af44d (esc)
2867 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2\xe2\x94\x86 parent: 21:d42a756af44d (esc)
2857 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 parent: 24:a9c19a3d96b7 (esc)
2868 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 parent: 24:a9c19a3d96b7 (esc)
2858 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
2869 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
2859 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:25 1970 +0000 (esc)
2870 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:25 1970 +0000 (esc)
2860 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 summary: (25) merge one known; far left (esc)
2871 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 summary: (25) merge one known; far left (esc)
2861 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 (esc)
2872 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 (esc)
2862 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x86 changeset: 24:a9c19a3d96b7 (esc)
2873 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x86 changeset: 24:a9c19a3d96b7 (esc)
2863 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 0:e6eb3150255d (esc)
2874 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 0:e6eb3150255d (esc)
2864 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x86 parent: 23:a01cddf0766d (esc)
2875 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x86 parent: 23:a01cddf0766d (esc)
2865 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
2876 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
2866 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:24 1970 +0000 (esc)
2877 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:24 1970 +0000 (esc)
2867 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 summary: (24) merge one known; immediate right (esc)
2878 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 summary: (24) merge one known; immediate right (esc)
2868 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xb1 (esc)
2879 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xb1 (esc)
2869 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x86 changeset: 23:a01cddf0766d (esc)
2880 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x86 changeset: 23:a01cddf0766d (esc)
2870 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 1:6db2ef61d156 (esc)
2881 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 1:6db2ef61d156 (esc)
2871 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x86 parent: 22:e0d9cccacb5d (esc)
2882 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x86 parent: 22:e0d9cccacb5d (esc)
2872 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
2883 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
2873 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:23 1970 +0000 (esc)
2884 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:23 1970 +0000 (esc)
2874 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 summary: (23) merge one known; immediate left (esc)
2885 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x86 summary: (23) merge one known; immediate left (esc)
2875 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xb1 (esc)
2886 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xb1 (esc)
2876 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x86 changeset: 22:e0d9cccacb5d (esc)
2887 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x86 changeset: 22:e0d9cccacb5d (esc)
2877 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x86\xe2\x95\xb1 parent: 18:1aa84d96232a (esc)
2888 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x86\xe2\x95\xb1 parent: 18:1aa84d96232a (esc)
2878 \xe2\x94\x82 \xe2\x94\x86 parent: 21:d42a756af44d (esc)
2889 \xe2\x94\x82 \xe2\x94\x86 parent: 21:d42a756af44d (esc)
2879 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
2890 \xe2\x94\x82 \xe2\x94\x86 user: test (esc)
2880 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:22 1970 +0000 (esc)
2891 \xe2\x94\x82 \xe2\x94\x86 date: Thu Jan 01 00:00:22 1970 +0000 (esc)
2881 \xe2\x94\x82 \xe2\x94\x86 summary: (22) merge two known; one far left, one far right (esc)
2892 \xe2\x94\x82 \xe2\x94\x86 summary: (22) merge two known; one far left, one far right (esc)
2882 \xe2\x94\x82 \xe2\x94\x86 (esc)
2893 \xe2\x94\x82 \xe2\x94\x86 (esc)
2883 \xe2\x94\x82 \xe2\x97\x8b changeset: 21:d42a756af44d (esc)
2894 \xe2\x94\x82 \xe2\x97\x8b changeset: 21:d42a756af44d (esc)
2884 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 parent: 19:31ddc2c1573b (esc)
2895 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 parent: 19:31ddc2c1573b (esc)
2885 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 20:d30ed6450e32 (esc)
2896 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 20:d30ed6450e32 (esc)
2886 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
2897 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
2887 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:21 1970 +0000 (esc)
2898 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:21 1970 +0000 (esc)
2888 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (21) expand (esc)
2899 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (21) expand (esc)
2889 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
2900 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
2890 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b changeset: 20:d30ed6450e32 (esc)
2901 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b changeset: 20:d30ed6450e32 (esc)
2891 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
2902 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
2892 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xa7 parent: 18:1aa84d96232a (esc)
2903 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xa7 parent: 18:1aa84d96232a (esc)
2893 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
2904 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
2894 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:20 1970 +0000 (esc)
2905 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:20 1970 +0000 (esc)
2895 \xe2\x94\x82 \xe2\x94\x82 summary: (20) merge two known; two far right (esc)
2906 \xe2\x94\x82 \xe2\x94\x82 summary: (20) merge two known; two far right (esc)
2896 \xe2\x94\x82 \xe2\x94\x82 (esc)
2907 \xe2\x94\x82 \xe2\x94\x82 (esc)
2897 \xe2\x94\x82 \xe2\x97\x8b changeset: 19:31ddc2c1573b (esc)
2908 \xe2\x94\x82 \xe2\x97\x8b changeset: 19:31ddc2c1573b (esc)
2898 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 parent: 15:1dda3f72782d (esc)
2909 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 parent: 15:1dda3f72782d (esc)
2899 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 17:44765d7c06e0 (esc)
2910 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 17:44765d7c06e0 (esc)
2900 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
2911 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
2901 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:19 1970 +0000 (esc)
2912 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:19 1970 +0000 (esc)
2902 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (19) expand (esc)
2913 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (19) expand (esc)
2903 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
2914 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
2904 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 18:1aa84d96232a (esc)
2915 \xe2\x97\x8b \xe2\x94\x82 \xe2\x94\x82 changeset: 18:1aa84d96232a (esc)
2905 \xe2\x94\x82\xe2\x95\xb2\xe2\x94\x82 \xe2\x94\x82 parent: 1:6db2ef61d156 (esc)
2916 \xe2\x94\x82\xe2\x95\xb2\xe2\x94\x82 \xe2\x94\x82 parent: 1:6db2ef61d156 (esc)
2906 \xe2\x95\xa7 \xe2\x94\x82 \xe2\x94\x82 parent: 15:1dda3f72782d (esc)
2917 \xe2\x95\xa7 \xe2\x94\x82 \xe2\x94\x82 parent: 15:1dda3f72782d (esc)
2907 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
2918 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
2908 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:18 1970 +0000 (esc)
2919 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:18 1970 +0000 (esc)
2909 \xe2\x94\x82 \xe2\x94\x82 summary: (18) merge two known; two far left (esc)
2920 \xe2\x94\x82 \xe2\x94\x82 summary: (18) merge two known; two far left (esc)
2910 \xe2\x95\xb1 \xe2\x95\xb1 (esc)
2921 \xe2\x95\xb1 \xe2\x95\xb1 (esc)
2911 \xe2\x94\x82 \xe2\x97\x8b changeset: 17:44765d7c06e0 (esc)
2922 \xe2\x94\x82 \xe2\x97\x8b changeset: 17:44765d7c06e0 (esc)
2912 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 parent: 12:86b91144a6e9 (esc)
2923 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 parent: 12:86b91144a6e9 (esc)
2913 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 16:3677d192927d (esc)
2924 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 16:3677d192927d (esc)
2914 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
2925 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
2915 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:17 1970 +0000 (esc)
2926 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:17 1970 +0000 (esc)
2916 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (17) expand (esc)
2927 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (17) expand (esc)
2917 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
2928 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
2918 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b changeset: 16:3677d192927d (esc)
2929 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b changeset: 16:3677d192927d (esc)
2919 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 parent: 0:e6eb3150255d (esc)
2930 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 parent: 0:e6eb3150255d (esc)
2920 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x95\xa7 parent: 1:6db2ef61d156 (esc)
2931 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x95\xa7 parent: 1:6db2ef61d156 (esc)
2921 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
2932 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
2922 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:16 1970 +0000 (esc)
2933 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:16 1970 +0000 (esc)
2923 \xe2\x94\x82 \xe2\x94\x82 summary: (16) merge two known; one immediate right, one near right (esc)
2934 \xe2\x94\x82 \xe2\x94\x82 summary: (16) merge two known; one immediate right, one near right (esc)
2924 \xe2\x94\x82 \xe2\x94\x82 (esc)
2935 \xe2\x94\x82 \xe2\x94\x82 (esc)
2925 \xe2\x97\x8b \xe2\x94\x82 changeset: 15:1dda3f72782d (esc)
2936 \xe2\x97\x8b \xe2\x94\x82 changeset: 15:1dda3f72782d (esc)
2926 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 13:22d8966a97e3 (esc)
2937 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 13:22d8966a97e3 (esc)
2927 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 14:8eac370358ef (esc)
2938 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 14:8eac370358ef (esc)
2928 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
2939 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
2929 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:15 1970 +0000 (esc)
2940 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:15 1970 +0000 (esc)
2930 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (15) expand (esc)
2941 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (15) expand (esc)
2931 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
2942 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
2932 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 changeset: 14:8eac370358ef (esc)
2943 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 changeset: 14:8eac370358ef (esc)
2933 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2\xe2\x94\x82 parent: 0:e6eb3150255d (esc)
2944 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2\xe2\x94\x82 parent: 0:e6eb3150255d (esc)
2934 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x82 parent: 12:86b91144a6e9 (esc)
2945 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x82 parent: 12:86b91144a6e9 (esc)
2935 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
2946 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
2936 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:14 1970 +0000 (esc)
2947 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:14 1970 +0000 (esc)
2937 \xe2\x94\x82 \xe2\x94\x82 summary: (14) merge two known; one immediate right, one far right (esc)
2948 \xe2\x94\x82 \xe2\x94\x82 summary: (14) merge two known; one immediate right, one far right (esc)
2938 \xe2\x94\x82 \xe2\x95\xb1 (esc)
2949 \xe2\x94\x82 \xe2\x95\xb1 (esc)
2939 \xe2\x97\x8b \xe2\x94\x82 changeset: 13:22d8966a97e3 (esc)
2950 \xe2\x97\x8b \xe2\x94\x82 changeset: 13:22d8966a97e3 (esc)
2940 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 9:7010c0af0a35 (esc)
2951 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 9:7010c0af0a35 (esc)
2941 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 11:832d76e6bdf2 (esc)
2952 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 11:832d76e6bdf2 (esc)
2942 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
2953 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
2943 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:13 1970 +0000 (esc)
2954 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:13 1970 +0000 (esc)
2944 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (13) expand (esc)
2955 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (13) expand (esc)
2945 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
2956 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
2946 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b changeset: 12:86b91144a6e9 (esc)
2957 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x97\x8b changeset: 12:86b91144a6e9 (esc)
2947 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 1:6db2ef61d156 (esc)
2958 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 1:6db2ef61d156 (esc)
2948 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xa7 parent: 9:7010c0af0a35 (esc)
2959 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xa7 parent: 9:7010c0af0a35 (esc)
2949 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
2960 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
2950 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:12 1970 +0000 (esc)
2961 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:12 1970 +0000 (esc)
2951 \xe2\x94\x82 \xe2\x94\x82 summary: (12) merge two known; one immediate right, one far left (esc)
2962 \xe2\x94\x82 \xe2\x94\x82 summary: (12) merge two known; one immediate right, one far left (esc)
2952 \xe2\x94\x82 \xe2\x94\x82 (esc)
2963 \xe2\x94\x82 \xe2\x94\x82 (esc)
2953 \xe2\x94\x82 \xe2\x97\x8b changeset: 11:832d76e6bdf2 (esc)
2964 \xe2\x94\x82 \xe2\x97\x8b changeset: 11:832d76e6bdf2 (esc)
2954 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 parent: 6:b105a072e251 (esc)
2965 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb2 parent: 6:b105a072e251 (esc)
2955 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 10:74c64d036d72 (esc)
2966 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 10:74c64d036d72 (esc)
2956 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
2967 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
2957 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:11 1970 +0000 (esc)
2968 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:11 1970 +0000 (esc)
2958 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (11) expand (esc)
2969 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (11) expand (esc)
2959 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
2970 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
2960 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b changeset: 10:74c64d036d72 (esc)
2971 \xe2\x94\x82 \xe2\x94\x82 \xe2\x97\x8b changeset: 10:74c64d036d72 (esc)
2961 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82 parent: 0:e6eb3150255d (esc)
2972 \xe2\x94\x82 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82 parent: 0:e6eb3150255d (esc)
2962 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xa7 parent: 6:b105a072e251 (esc)
2973 \xe2\x94\x82 \xe2\x94\x82 \xe2\x95\xa7 parent: 6:b105a072e251 (esc)
2963 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
2974 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
2964 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:10 1970 +0000 (esc)
2975 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:10 1970 +0000 (esc)
2965 \xe2\x94\x82 \xe2\x94\x82 summary: (10) merge two known; one immediate left, one near right (esc)
2976 \xe2\x94\x82 \xe2\x94\x82 summary: (10) merge two known; one immediate left, one near right (esc)
2966 \xe2\x94\x82 \xe2\x94\x82 (esc)
2977 \xe2\x94\x82 \xe2\x94\x82 (esc)
2967 \xe2\x97\x8b \xe2\x94\x82 changeset: 9:7010c0af0a35 (esc)
2978 \xe2\x97\x8b \xe2\x94\x82 changeset: 9:7010c0af0a35 (esc)
2968 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 7:b632bb1b1224 (esc)
2979 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 7:b632bb1b1224 (esc)
2969 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 8:7a0b11f71937 (esc)
2980 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 parent: 8:7a0b11f71937 (esc)
2970 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
2981 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
2971 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:09 1970 +0000 (esc)
2982 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:09 1970 +0000 (esc)
2972 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (9) expand (esc)
2983 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 summary: (9) expand (esc)
2973 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
2984 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 (esc)
2974 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 changeset: 8:7a0b11f71937 (esc)
2985 \xe2\x94\x82 \xe2\x97\x8b \xe2\x94\x82 changeset: 8:7a0b11f71937 (esc)
2975 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
2986 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82 \xe2\x94\x82 parent: 0:e6eb3150255d (esc)
2976 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x82 parent: 7:b632bb1b1224 (esc)
2987 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x82 parent: 7:b632bb1b1224 (esc)
2977 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
2988 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
2978 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:08 1970 +0000 (esc)
2989 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:08 1970 +0000 (esc)
2979 \xe2\x94\x82 \xe2\x94\x82 summary: (8) merge two known; one immediate left, one far right (esc)
2990 \xe2\x94\x82 \xe2\x94\x82 summary: (8) merge two known; one immediate left, one far right (esc)
2980 \xe2\x94\x82 \xe2\x95\xb1 (esc)
2991 \xe2\x94\x82 \xe2\x95\xb1 (esc)
2981 \xe2\x97\x8b \xe2\x94\x82 changeset: 7:b632bb1b1224 (esc)
2992 \xe2\x97\x8b \xe2\x94\x82 changeset: 7:b632bb1b1224 (esc)
2982 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 2:3d9a33b8d1e1 (esc)
2993 \xe2\x94\x82\xe2\x95\xb2 \xe2\x95\xb2 parent: 2:3d9a33b8d1e1 (esc)
2983 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x82 parent: 5:4409d547b708 (esc)
2994 \xe2\x94\x82 \xe2\x95\xa7 \xe2\x94\x82 parent: 5:4409d547b708 (esc)
2984 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
2995 \xe2\x94\x82 \xe2\x94\x82 user: test (esc)
2985 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:07 1970 +0000 (esc)
2996 \xe2\x94\x82 \xe2\x94\x82 date: Thu Jan 01 00:00:07 1970 +0000 (esc)
2986 \xe2\x94\x82 \xe2\x94\x82 summary: (7) expand (esc)
2997 \xe2\x94\x82 \xe2\x94\x82 summary: (7) expand (esc)
2987 \xe2\x94\x82 \xe2\x95\xb1 (esc)
2998 \xe2\x94\x82 \xe2\x95\xb1 (esc)
2988 \xe2\x94\x82 \xe2\x97\x8b changeset: 6:b105a072e251 (esc)
2999 \xe2\x94\x82 \xe2\x97\x8b changeset: 6:b105a072e251 (esc)
2989 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82 parent: 2:3d9a33b8d1e1 (esc)
3000 \xe2\x94\x82\xe2\x95\xb1\xe2\x94\x82 parent: 2:3d9a33b8d1e1 (esc)
2990 \xe2\x94\x82 \xe2\x95\xa7 parent: 5:4409d547b708 (esc)
3001 \xe2\x94\x82 \xe2\x95\xa7 parent: 5:4409d547b708 (esc)
2991 \xe2\x94\x82 user: test (esc)
3002 \xe2\x94\x82 user: test (esc)
2992 \xe2\x94\x82 date: Thu Jan 01 00:00:06 1970 +0000 (esc)
3003 \xe2\x94\x82 date: Thu Jan 01 00:00:06 1970 +0000 (esc)
2993 \xe2\x94\x82 summary: (6) merge two known; one immediate left, one far left (esc)
3004 \xe2\x94\x82 summary: (6) merge two known; one immediate left, one far left (esc)
2994 \xe2\x94\x82 (esc)
3005 \xe2\x94\x82 (esc)
2995 \xe2\x97\x8b changeset: 5:4409d547b708 (esc)
3006 \xe2\x97\x8b changeset: 5:4409d547b708 (esc)
2996 \xe2\x94\x82\xe2\x95\xb2 parent: 3:27eef8ed80b4 (esc)
3007 \xe2\x94\x82\xe2\x95\xb2 parent: 3:27eef8ed80b4 (esc)
2997 \xe2\x94\x82 \xe2\x95\xa7 parent: 4:26a8bac39d9f (esc)
3008 \xe2\x94\x82 \xe2\x95\xa7 parent: 4:26a8bac39d9f (esc)
2998 \xe2\x94\x82 user: test (esc)
3009 \xe2\x94\x82 user: test (esc)
2999 \xe2\x94\x82 date: Thu Jan 01 00:00:05 1970 +0000 (esc)
3010 \xe2\x94\x82 date: Thu Jan 01 00:00:05 1970 +0000 (esc)
3000 \xe2\x94\x82 summary: (5) expand (esc)
3011 \xe2\x94\x82 summary: (5) expand (esc)
3001 \xe2\x94\x82 (esc)
3012 \xe2\x94\x82 (esc)
3002 \xe2\x97\x8b changeset: 4:26a8bac39d9f (esc)
3013 \xe2\x97\x8b changeset: 4:26a8bac39d9f (esc)
3003 \xe2\x94\x82\xe2\x95\xb2 parent: 1:6db2ef61d156 (esc)
3014 \xe2\x94\x82\xe2\x95\xb2 parent: 1:6db2ef61d156 (esc)
3004 \xe2\x95\xa7 \xe2\x95\xa7 parent: 3:27eef8ed80b4 (esc)
3015 \xe2\x95\xa7 \xe2\x95\xa7 parent: 3:27eef8ed80b4 (esc)
3005 user: test
3016 user: test
3006 date: Thu Jan 01 00:00:04 1970 +0000
3017 date: Thu Jan 01 00:00:04 1970 +0000
3007 summary: (4) merge two known; one immediate left, one immediate right
3018 summary: (4) merge two known; one immediate left, one immediate right
3008
3019
3009 $ cd ..
3020 $ cd ..
3010 $ cd repo
3021 $ cd repo
3011
3022
3012 behavior with newlines
3023 behavior with newlines
3013
3024
3014 $ hg log -G -r ::2 -T '{rev} {desc}'
3025 $ hg log -G -r ::2 -T '{rev} {desc}'
3015 \xe2\x97\x8b 2 (2) collapse (esc)
3026 \xe2\x97\x8b 2 (2) collapse (esc)
3016 \xe2\x94\x82 (esc)
3027 \xe2\x94\x82 (esc)
3017 \xe2\x97\x8b 1 (1) collapse (esc)
3028 \xe2\x97\x8b 1 (1) collapse (esc)
3018 \xe2\x94\x82 (esc)
3029 \xe2\x94\x82 (esc)
3019 \xe2\x97\x8b 0 (0) root (esc)
3030 \xe2\x97\x8b 0 (0) root (esc)
3020
3031
3021
3032
3022 $ hg log -G -r ::2 -T '{rev} {desc}\n'
3033 $ hg log -G -r ::2 -T '{rev} {desc}\n'
3023 \xe2\x97\x8b 2 (2) collapse (esc)
3034 \xe2\x97\x8b 2 (2) collapse (esc)
3024 \xe2\x94\x82 (esc)
3035 \xe2\x94\x82 (esc)
3025 \xe2\x97\x8b 1 (1) collapse (esc)
3036 \xe2\x97\x8b 1 (1) collapse (esc)
3026 \xe2\x94\x82 (esc)
3037 \xe2\x94\x82 (esc)
3027 \xe2\x97\x8b 0 (0) root (esc)
3038 \xe2\x97\x8b 0 (0) root (esc)
3028
3039
3029
3040
3030 $ hg log -G -r ::2 -T '{rev} {desc}\n\n'
3041 $ hg log -G -r ::2 -T '{rev} {desc}\n\n'
3031 \xe2\x97\x8b 2 (2) collapse (esc)
3042 \xe2\x97\x8b 2 (2) collapse (esc)
3032 \xe2\x94\x82 (esc)
3043 \xe2\x94\x82 (esc)
3033 \xe2\x97\x8b 1 (1) collapse (esc)
3044 \xe2\x97\x8b 1 (1) collapse (esc)
3034 \xe2\x94\x82 (esc)
3045 \xe2\x94\x82 (esc)
3035 \xe2\x97\x8b 0 (0) root (esc)
3046 \xe2\x97\x8b 0 (0) root (esc)
3036
3047
3037
3048
3038 $ hg log -G -r ::2 -T '\n{rev} {desc}'
3049 $ hg log -G -r ::2 -T '\n{rev} {desc}'
3039 \xe2\x97\x8b (esc)
3050 \xe2\x97\x8b (esc)
3040 \xe2\x94\x82 2 (2) collapse (esc)
3051 \xe2\x94\x82 2 (2) collapse (esc)
3041 \xe2\x97\x8b (esc)
3052 \xe2\x97\x8b (esc)
3042 \xe2\x94\x82 1 (1) collapse (esc)
3053 \xe2\x94\x82 1 (1) collapse (esc)
3043 \xe2\x97\x8b (esc)
3054 \xe2\x97\x8b (esc)
3044 0 (0) root
3055 0 (0) root
3045
3056
3046 $ hg log -G -r ::2 -T '{rev} {desc}\n\n\n'
3057 $ hg log -G -r ::2 -T '{rev} {desc}\n\n\n'
3047 \xe2\x97\x8b 2 (2) collapse (esc)
3058 \xe2\x97\x8b 2 (2) collapse (esc)
3048 \xe2\x94\x82 (esc)
3059 \xe2\x94\x82 (esc)
3049 \xe2\x94\x82 (esc)
3060 \xe2\x94\x82 (esc)
3050 \xe2\x97\x8b 1 (1) collapse (esc)
3061 \xe2\x97\x8b 1 (1) collapse (esc)
3051 \xe2\x94\x82 (esc)
3062 \xe2\x94\x82 (esc)
3052 \xe2\x94\x82 (esc)
3063 \xe2\x94\x82 (esc)
3053 \xe2\x97\x8b 0 (0) root (esc)
3064 \xe2\x97\x8b 0 (0) root (esc)
3054
3065
3055
3066
3056 $ cd ..
3067 $ cd ..
3057
3068
3058 When inserting extra line nodes to handle more than 2 parents, ensure that
3069 When inserting extra line nodes to handle more than 2 parents, ensure that
3059 the right node styles are used (issue5174):
3070 the right node styles are used (issue5174):
3060
3071
3061 $ hg init repo-issue5174
3072 $ hg init repo-issue5174
3062 $ cd repo-issue5174
3073 $ cd repo-issue5174
3063 $ echo a > f0
3074 $ echo a > f0
3064 $ hg ci -Aqm 0
3075 $ hg ci -Aqm 0
3065 $ echo a > f1
3076 $ echo a > f1
3066 $ hg ci -Aqm 1
3077 $ hg ci -Aqm 1
3067 $ echo a > f2
3078 $ echo a > f2
3068 $ hg ci -Aqm 2
3079 $ hg ci -Aqm 2
3069 $ hg co ".^"
3080 $ hg co ".^"
3070 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
3081 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
3071 $ echo a > f3
3082 $ echo a > f3
3072 $ hg ci -Aqm 3
3083 $ hg ci -Aqm 3
3073 $ hg co ".^^"
3084 $ hg co ".^^"
3074 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
3085 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
3075 $ echo a > f4
3086 $ echo a > f4
3076 $ hg ci -Aqm 4
3087 $ hg ci -Aqm 4
3077 $ hg merge -r 2
3088 $ hg merge -r 2
3078 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
3089 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
3079 (branch merge, don't forget to commit)
3090 (branch merge, don't forget to commit)
3080 $ hg ci -qm 5
3091 $ hg ci -qm 5
3081 $ hg merge -r 3
3092 $ hg merge -r 3
3082 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
3093 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
3083 (branch merge, don't forget to commit)
3094 (branch merge, don't forget to commit)
3084 $ hg ci -qm 6
3095 $ hg ci -qm 6
3085 $ hg log -G -r '0 | 1 | 2 | 6'
3096 $ hg log -G -r '0 | 1 | 2 | 6'
3086 \xe2\x97\x8d changeset: 6:851fe89689ad (esc)
3097 \xe2\x97\x8d changeset: 6:851fe89689ad (esc)
3087 \xe2\x94\x86\xe2\x95\xb2 tag: tip (esc)
3098 \xe2\x94\x86\xe2\x95\xb2 tag: tip (esc)
3088 \xe2\x94\x86 \xe2\x94\x86 parent: 5:4f1e3cf15f5d (esc)
3099 \xe2\x94\x86 \xe2\x94\x86 parent: 5:4f1e3cf15f5d (esc)
3089 \xe2\x94\x86 \xe2\x94\x86 parent: 3:b74ba7084d2d (esc)
3100 \xe2\x94\x86 \xe2\x94\x86 parent: 3:b74ba7084d2d (esc)
3090 \xe2\x94\x86 \xe2\x94\x86 user: test (esc)
3101 \xe2\x94\x86 \xe2\x94\x86 user: test (esc)
3091 \xe2\x94\x86 \xe2\x94\x86 date: Thu Jan 01 00:00:00 1970 +0000 (esc)
3102 \xe2\x94\x86 \xe2\x94\x86 date: Thu Jan 01 00:00:00 1970 +0000 (esc)
3092 \xe2\x94\x86 \xe2\x94\x86 summary: 6 (esc)
3103 \xe2\x94\x86 \xe2\x94\x86 summary: 6 (esc)
3093 \xe2\x94\x86 \xe2\x94\x86 (esc)
3104 \xe2\x94\x86 \xe2\x94\x86 (esc)
3094 \xe2\x94\x86 \xe2\x95\xb2 (esc)
3105 \xe2\x94\x86 \xe2\x95\xb2 (esc)
3095 \xe2\x94\x86 \xe2\x94\x86\xe2\x95\xb2 (esc)
3106 \xe2\x94\x86 \xe2\x94\x86\xe2\x95\xb2 (esc)
3096 \xe2\x94\x86 \xe2\x97\x8b \xe2\x94\x86 changeset: 2:3e6599df4cce (esc)
3107 \xe2\x94\x86 \xe2\x97\x8b \xe2\x94\x86 changeset: 2:3e6599df4cce (esc)
3097 \xe2\x94\x86 \xe2\x94\x86\xe2\x95\xb1 user: test (esc)
3108 \xe2\x94\x86 \xe2\x94\x86\xe2\x95\xb1 user: test (esc)
3098 \xe2\x94\x86 \xe2\x94\x86 date: Thu Jan 01 00:00:00 1970 +0000 (esc)
3109 \xe2\x94\x86 \xe2\x94\x86 date: Thu Jan 01 00:00:00 1970 +0000 (esc)
3099 \xe2\x94\x86 \xe2\x94\x86 summary: 2 (esc)
3110 \xe2\x94\x86 \xe2\x94\x86 summary: 2 (esc)
3100 \xe2\x94\x86 \xe2\x94\x86 (esc)
3111 \xe2\x94\x86 \xe2\x94\x86 (esc)
3101 \xe2\x94\x86 \xe2\x97\x8b changeset: 1:bd9a55143933 (esc)
3112 \xe2\x94\x86 \xe2\x97\x8b changeset: 1:bd9a55143933 (esc)
3102 \xe2\x94\x86\xe2\x95\xb1 user: test (esc)
3113 \xe2\x94\x86\xe2\x95\xb1 user: test (esc)
3103 \xe2\x94\x86 date: Thu Jan 01 00:00:00 1970 +0000 (esc)
3114 \xe2\x94\x86 date: Thu Jan 01 00:00:00 1970 +0000 (esc)
3104 \xe2\x94\x86 summary: 1 (esc)
3115 \xe2\x94\x86 summary: 1 (esc)
3105 \xe2\x94\x86 (esc)
3116 \xe2\x94\x86 (esc)
3106 \xe2\x97\x8b changeset: 0:870a5edc339c (esc)
3117 \xe2\x97\x8b changeset: 0:870a5edc339c (esc)
3107 user: test
3118 user: test
3108 date: Thu Jan 01 00:00:00 1970 +0000
3119 date: Thu Jan 01 00:00:00 1970 +0000
3109 summary: 0
3120 summary: 0
3110
3121
3111
3122
3112 $ cd ..
3123 $ cd ..
3113
3124
3114 Multiple roots (issue5440):
3125 Multiple roots (issue5440):
3115
3126
3116 $ hg init multiroots
3127 $ hg init multiroots
3117 $ cd multiroots
3128 $ cd multiroots
3118 $ cat <<EOF > .hg/hgrc
3129 $ cat <<EOF > .hg/hgrc
3119 > [ui]
3130 > [ui]
3120 > logtemplate = '{rev} {desc}\n\n'
3131 > logtemplate = '{rev} {desc}\n\n'
3121 > EOF
3132 > EOF
3122
3133
3123 $ touch foo
3134 $ touch foo
3124 $ hg ci -Aqm foo
3135 $ hg ci -Aqm foo
3125 $ hg co -q null
3136 $ hg co -q null
3126 $ touch bar
3137 $ touch bar
3127 $ hg ci -Aqm bar
3138 $ hg ci -Aqm bar
3128
3139
3129 $ hg log -Gr null:
3140 $ hg log -Gr null:
3130 \xe2\x97\x8d 1 bar (esc)
3141 \xe2\x97\x8d 1 bar (esc)
3131 \xe2\x94\x82 (esc)
3142 \xe2\x94\x82 (esc)
3132 \xe2\x94\x82 \xe2\x97\x8b 0 foo (esc)
3143 \xe2\x94\x82 \xe2\x97\x8b 0 foo (esc)
3133 \xe2\x94\x82\xe2\x95\xb1 (esc)
3144 \xe2\x94\x82\xe2\x95\xb1 (esc)
3134 \xe2\x97\x8b -1 (esc)
3145 \xe2\x97\x8b -1 (esc)
3135
3146
3136 $ hg log -Gr null+0
3147 $ hg log -Gr null+0
3137 \xe2\x97\x8b 0 foo (esc)
3148 \xe2\x97\x8b 0 foo (esc)
3138 \xe2\x94\x82 (esc)
3149 \xe2\x94\x82 (esc)
3139 \xe2\x97\x8b -1 (esc)
3150 \xe2\x97\x8b -1 (esc)
3140
3151
3141 $ hg log -Gr null+1
3152 $ hg log -Gr null+1
3142 \xe2\x97\x8d 1 bar (esc)
3153 \xe2\x97\x8d 1 bar (esc)
3143 \xe2\x94\x82 (esc)
3154 \xe2\x94\x82 (esc)
3144 \xe2\x97\x8b -1 (esc)
3155 \xe2\x97\x8b -1 (esc)
3145
3156
3146
3157
3147 $ cd ..
3158 $ cd ..
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now