##// END OF EJS Templates
bookmarks: don't use bookmarks.listbookmarks in local computations...
Kevin Bullock -
r18495:8260fa9f stable
parent child Browse files
Show More
@@ -1,219 +1,219 b''
1 # repoview.py - Filtered view of a localrepo object
1 # repoview.py - Filtered view of a localrepo object
2 #
2 #
3 # Copyright 2012 Pierre-Yves David <pierre-yves.david@ens-lyon.org>
3 # Copyright 2012 Pierre-Yves David <pierre-yves.david@ens-lyon.org>
4 # Logilab SA <contact@logilab.fr>
4 # Logilab SA <contact@logilab.fr>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 import copy
9 import copy
10 import phases
10 import phases
11 import util
11 import util
12 import obsolete, bookmarks, revset
12 import obsolete, revset
13
13
14
14
15 def hideablerevs(repo):
15 def hideablerevs(repo):
16 """Revisions candidates to be hidden
16 """Revisions candidates to be hidden
17
17
18 This is a standalone function to help extensions to wrap it."""
18 This is a standalone function to help extensions to wrap it."""
19 return obsolete.getrevs(repo, 'obsolete')
19 return obsolete.getrevs(repo, 'obsolete')
20
20
21 def computehidden(repo):
21 def computehidden(repo):
22 """compute the set of hidden revision to filter
22 """compute the set of hidden revision to filter
23
23
24 During most operation hidden should be filtered."""
24 During most operation hidden should be filtered."""
25 assert not repo.changelog.filteredrevs
25 assert not repo.changelog.filteredrevs
26 hideable = hideablerevs(repo)
26 hideable = hideablerevs(repo)
27 if hideable:
27 if hideable:
28 cl = repo.changelog
28 cl = repo.changelog
29 firsthideable = min(hideable)
29 firsthideable = min(hideable)
30 revs = cl.revs(start=firsthideable)
30 revs = cl.revs(start=firsthideable)
31 blockers = [r for r in revset._children(repo, revs, hideable)
31 blockers = [r for r in revset._children(repo, revs, hideable)
32 if r not in hideable]
32 if r not in hideable]
33 for par in repo[None].parents():
33 for par in repo[None].parents():
34 blockers.append(par.rev())
34 blockers.append(par.rev())
35 for bm in bookmarks.listbookmarks(repo).values():
35 for bm in repo._bookmarks.values():
36 blockers.append(repo[bm].rev())
36 blockers.append(repo[bm].rev())
37 blocked = cl.ancestors(blockers, inclusive=True)
37 blocked = cl.ancestors(blockers, inclusive=True)
38 return frozenset(r for r in hideable if r not in blocked)
38 return frozenset(r for r in hideable if r not in blocked)
39 return frozenset()
39 return frozenset()
40
40
41 def computeunserved(repo):
41 def computeunserved(repo):
42 """compute the set of revision that should be filtered when used a server
42 """compute the set of revision that should be filtered when used a server
43
43
44 Secret and hidden changeset should not pretend to be here."""
44 Secret and hidden changeset should not pretend to be here."""
45 assert not repo.changelog.filteredrevs
45 assert not repo.changelog.filteredrevs
46 # fast path in simple case to avoid impact of non optimised code
46 # fast path in simple case to avoid impact of non optimised code
47 hiddens = filterrevs(repo, 'visible')
47 hiddens = filterrevs(repo, 'visible')
48 if phases.hassecret(repo):
48 if phases.hassecret(repo):
49 cl = repo.changelog
49 cl = repo.changelog
50 secret = phases.secret
50 secret = phases.secret
51 getphase = repo._phasecache.phase
51 getphase = repo._phasecache.phase
52 first = min(cl.rev(n) for n in repo._phasecache.phaseroots[secret])
52 first = min(cl.rev(n) for n in repo._phasecache.phaseroots[secret])
53 revs = cl.revs(start=first)
53 revs = cl.revs(start=first)
54 secrets = set(r for r in revs if getphase(repo, r) >= secret)
54 secrets = set(r for r in revs if getphase(repo, r) >= secret)
55 return frozenset(hiddens | secrets)
55 return frozenset(hiddens | secrets)
56 else:
56 else:
57 return hiddens
57 return hiddens
58 return frozenset()
58 return frozenset()
59
59
60 def computemutable(repo):
60 def computemutable(repo):
61 """compute the set of revision that should be filtered when used a server
61 """compute the set of revision that should be filtered when used a server
62
62
63 Secret and hidden changeset should not pretend to be here."""
63 Secret and hidden changeset should not pretend to be here."""
64 assert not repo.changelog.filteredrevs
64 assert not repo.changelog.filteredrevs
65 # fast check to avoid revset call on huge repo
65 # fast check to avoid revset call on huge repo
66 if util.any(repo._phasecache.phaseroots[1:]):
66 if util.any(repo._phasecache.phaseroots[1:]):
67 getphase = repo._phasecache.phase
67 getphase = repo._phasecache.phase
68 maymutable = filterrevs(repo, 'base')
68 maymutable = filterrevs(repo, 'base')
69 return frozenset(r for r in maymutable if getphase(repo, r))
69 return frozenset(r for r in maymutable if getphase(repo, r))
70 return frozenset()
70 return frozenset()
71
71
72 def computeimpactable(repo):
72 def computeimpactable(repo):
73 """Everything impactable by mutable revision
73 """Everything impactable by mutable revision
74
74
75 The immutable filter still have some chance to get invalidated. This will
75 The immutable filter still have some chance to get invalidated. This will
76 happen when:
76 happen when:
77
77
78 - you garbage collect hidden changeset,
78 - you garbage collect hidden changeset,
79 - public phase is moved backward,
79 - public phase is moved backward,
80 - something is changed in the filtering (this could be fixed)
80 - something is changed in the filtering (this could be fixed)
81
81
82 This filter out any mutable changeset and any public changeset that may be
82 This filter out any mutable changeset and any public changeset that may be
83 impacted by something happening to a mutable revision.
83 impacted by something happening to a mutable revision.
84
84
85 This is achieved by filtered everything with a revision number egal or
85 This is achieved by filtered everything with a revision number egal or
86 higher than the first mutable changeset is filtered."""
86 higher than the first mutable changeset is filtered."""
87 assert not repo.changelog.filteredrevs
87 assert not repo.changelog.filteredrevs
88 cl = repo.changelog
88 cl = repo.changelog
89 firstmutable = len(cl)
89 firstmutable = len(cl)
90 for roots in repo._phasecache.phaseroots[1:]:
90 for roots in repo._phasecache.phaseroots[1:]:
91 if roots:
91 if roots:
92 firstmutable = min(firstmutable, min(cl.rev(r) for r in roots))
92 firstmutable = min(firstmutable, min(cl.rev(r) for r in roots))
93 # protect from nullrev root
93 # protect from nullrev root
94 firstmutable = max(0, firstmutable)
94 firstmutable = max(0, firstmutable)
95 return frozenset(xrange(firstmutable, len(cl)))
95 return frozenset(xrange(firstmutable, len(cl)))
96
96
97 # function to compute filtered set
97 # function to compute filtered set
98 filtertable = {'visible': computehidden,
98 filtertable = {'visible': computehidden,
99 'served': computeunserved,
99 'served': computeunserved,
100 'immutable': computemutable,
100 'immutable': computemutable,
101 'base': computeimpactable}
101 'base': computeimpactable}
102 ### Nearest subset relation
102 ### Nearest subset relation
103 # Nearest subset of filter X is a filter Y so that:
103 # Nearest subset of filter X is a filter Y so that:
104 # * Y is included in X,
104 # * Y is included in X,
105 # * X - Y is as small as possible.
105 # * X - Y is as small as possible.
106 # This create and ordering used for branchmap purpose.
106 # This create and ordering used for branchmap purpose.
107 # the ordering may be partial
107 # the ordering may be partial
108 subsettable = {None: 'visible',
108 subsettable = {None: 'visible',
109 'visible': 'served',
109 'visible': 'served',
110 'served': 'immutable',
110 'served': 'immutable',
111 'immutable': 'base'}
111 'immutable': 'base'}
112
112
113 def filterrevs(repo, filtername):
113 def filterrevs(repo, filtername):
114 """returns set of filtered revision for this filter name"""
114 """returns set of filtered revision for this filter name"""
115 if filtername not in repo.filteredrevcache:
115 if filtername not in repo.filteredrevcache:
116 func = filtertable[filtername]
116 func = filtertable[filtername]
117 repo.filteredrevcache[filtername] = func(repo.unfiltered())
117 repo.filteredrevcache[filtername] = func(repo.unfiltered())
118 return repo.filteredrevcache[filtername]
118 return repo.filteredrevcache[filtername]
119
119
120 class repoview(object):
120 class repoview(object):
121 """Provide a read/write view of a repo through a filtered changelog
121 """Provide a read/write view of a repo through a filtered changelog
122
122
123 This object is used to access a filtered version of a repository without
123 This object is used to access a filtered version of a repository without
124 altering the original repository object itself. We can not alter the
124 altering the original repository object itself. We can not alter the
125 original object for two main reasons:
125 original object for two main reasons:
126 - It prevents the use of a repo with multiple filters at the same time. In
126 - It prevents the use of a repo with multiple filters at the same time. In
127 particular when multiple threads are involved.
127 particular when multiple threads are involved.
128 - It makes scope of the filtering harder to control.
128 - It makes scope of the filtering harder to control.
129
129
130 This object behaves very closely to the original repository. All attribute
130 This object behaves very closely to the original repository. All attribute
131 operations are done on the original repository:
131 operations are done on the original repository:
132 - An access to `repoview.someattr` actually returns `repo.someattr`,
132 - An access to `repoview.someattr` actually returns `repo.someattr`,
133 - A write to `repoview.someattr` actually sets value of `repo.someattr`,
133 - A write to `repoview.someattr` actually sets value of `repo.someattr`,
134 - A deletion of `repoview.someattr` actually drops `someattr`
134 - A deletion of `repoview.someattr` actually drops `someattr`
135 from `repo.__dict__`.
135 from `repo.__dict__`.
136
136
137 The only exception is the `changelog` property. It is overridden to return
137 The only exception is the `changelog` property. It is overridden to return
138 a (surface) copy of `repo.changelog` with some revisions filtered. The
138 a (surface) copy of `repo.changelog` with some revisions filtered. The
139 `filtername` attribute of the view control the revisions that need to be
139 `filtername` attribute of the view control the revisions that need to be
140 filtered. (the fact the changelog is copied is an implementation detail).
140 filtered. (the fact the changelog is copied is an implementation detail).
141
141
142 Unlike attributes, this object intercepts all method calls. This means that
142 Unlike attributes, this object intercepts all method calls. This means that
143 all methods are run on the `repoview` object with the filtered `changelog`
143 all methods are run on the `repoview` object with the filtered `changelog`
144 property. For this purpose the simple `repoview` class must be mixed with
144 property. For this purpose the simple `repoview` class must be mixed with
145 the actual class of the repository. This ensures that the resulting
145 the actual class of the repository. This ensures that the resulting
146 `repoview` object have the very same methods than the repo object. This
146 `repoview` object have the very same methods than the repo object. This
147 leads to the property below.
147 leads to the property below.
148
148
149 repoview.method() --> repo.__class__.method(repoview)
149 repoview.method() --> repo.__class__.method(repoview)
150
150
151 The inheritance has to be done dynamically because `repo` can be of any
151 The inheritance has to be done dynamically because `repo` can be of any
152 subclasses of `localrepo`. Eg: `bundlerepo` or `httprepo`.
152 subclasses of `localrepo`. Eg: `bundlerepo` or `httprepo`.
153 """
153 """
154
154
155 def __init__(self, repo, filtername):
155 def __init__(self, repo, filtername):
156 object.__setattr__(self, '_unfilteredrepo', repo)
156 object.__setattr__(self, '_unfilteredrepo', repo)
157 object.__setattr__(self, 'filtername', filtername)
157 object.__setattr__(self, 'filtername', filtername)
158 object.__setattr__(self, '_clcachekey', None)
158 object.__setattr__(self, '_clcachekey', None)
159 object.__setattr__(self, '_clcache', None)
159 object.__setattr__(self, '_clcache', None)
160
160
161 # not a cacheproperty on purpose we shall implement a proper cache later
161 # not a cacheproperty on purpose we shall implement a proper cache later
162 @property
162 @property
163 def changelog(self):
163 def changelog(self):
164 """return a filtered version of the changeset
164 """return a filtered version of the changeset
165
165
166 this changelog must not be used for writing"""
166 this changelog must not be used for writing"""
167 # some cache may be implemented later
167 # some cache may be implemented later
168 unfi = self._unfilteredrepo
168 unfi = self._unfilteredrepo
169 unfichangelog = unfi.changelog
169 unfichangelog = unfi.changelog
170 revs = filterrevs(unfi, self.filtername)
170 revs = filterrevs(unfi, self.filtername)
171 cl = self._clcache
171 cl = self._clcache
172 newkey = (len(unfichangelog), unfichangelog.tip(), hash(revs))
172 newkey = (len(unfichangelog), unfichangelog.tip(), hash(revs))
173 if cl is not None:
173 if cl is not None:
174 # we need to check curkey too for some obscure reason.
174 # we need to check curkey too for some obscure reason.
175 # MQ test show a corruption of the underlying repo (in _clcache)
175 # MQ test show a corruption of the underlying repo (in _clcache)
176 # without change in the cachekey.
176 # without change in the cachekey.
177 oldfilter = cl.filteredrevs
177 oldfilter = cl.filteredrevs
178 try:
178 try:
179 cl.filterrevs = () # disable filtering for tip
179 cl.filterrevs = () # disable filtering for tip
180 curkey = (len(cl), cl.tip(), hash(oldfilter))
180 curkey = (len(cl), cl.tip(), hash(oldfilter))
181 finally:
181 finally:
182 cl.filteredrevs = oldfilter
182 cl.filteredrevs = oldfilter
183 if newkey != self._clcachekey or newkey != curkey:
183 if newkey != self._clcachekey or newkey != curkey:
184 cl = None
184 cl = None
185 # could have been made None by the previous if
185 # could have been made None by the previous if
186 if cl is None:
186 if cl is None:
187 cl = copy.copy(unfichangelog)
187 cl = copy.copy(unfichangelog)
188 cl.filteredrevs = revs
188 cl.filteredrevs = revs
189 object.__setattr__(self, '_clcache', cl)
189 object.__setattr__(self, '_clcache', cl)
190 object.__setattr__(self, '_clcachekey', newkey)
190 object.__setattr__(self, '_clcachekey', newkey)
191 return cl
191 return cl
192
192
193 def unfiltered(self):
193 def unfiltered(self):
194 """Return an unfiltered version of a repo"""
194 """Return an unfiltered version of a repo"""
195 return self._unfilteredrepo
195 return self._unfilteredrepo
196
196
197 def filtered(self, name):
197 def filtered(self, name):
198 """Return a filtered version of a repository"""
198 """Return a filtered version of a repository"""
199 if name == self.filtername:
199 if name == self.filtername:
200 return self
200 return self
201 return self.unfiltered().filtered(name)
201 return self.unfiltered().filtered(name)
202
202
203 # everything access are forwarded to the proxied repo
203 # everything access are forwarded to the proxied repo
204 def __getattr__(self, attr):
204 def __getattr__(self, attr):
205 return getattr(self._unfilteredrepo, attr)
205 return getattr(self._unfilteredrepo, attr)
206
206
207 def __setattr__(self, attr, value):
207 def __setattr__(self, attr, value):
208 return setattr(self._unfilteredrepo, attr, value)
208 return setattr(self._unfilteredrepo, attr, value)
209
209
210 def __delattr__(self, attr):
210 def __delattr__(self, attr):
211 return delattr(self._unfilteredrepo, attr)
211 return delattr(self._unfilteredrepo, attr)
212
212
213 # The `requirement` attribut is initialiazed during __init__. But
213 # The `requirement` attribut is initialiazed during __init__. But
214 # __getattr__ won't be called as it also exists on the class. We need
214 # __getattr__ won't be called as it also exists on the class. We need
215 # explicit forwarding to main repo here
215 # explicit forwarding to main repo here
216 @property
216 @property
217 def requirements(self):
217 def requirements(self):
218 return self._unfilteredrepo.requirements
218 return self._unfilteredrepo.requirements
219
219
@@ -1,1932 +1,1931 b''
1 # revset.py - revision set queries for mercurial
1 # revset.py - revision 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 import re
8 import re
9 import parser, util, error, discovery, hbisect, phases
9 import parser, util, error, discovery, hbisect, phases
10 import node
10 import node
11 import bookmarks as bookmarksmod
12 import match as matchmod
11 import match as matchmod
13 from i18n import _
12 from i18n import _
14 import encoding
13 import encoding
15 import obsolete as obsmod
14 import obsolete as obsmod
16 import repoview
15 import repoview
17
16
18 def _revancestors(repo, revs, followfirst):
17 def _revancestors(repo, revs, followfirst):
19 """Like revlog.ancestors(), but supports followfirst."""
18 """Like revlog.ancestors(), but supports followfirst."""
20 cut = followfirst and 1 or None
19 cut = followfirst and 1 or None
21 cl = repo.changelog
20 cl = repo.changelog
22 visit = util.deque(revs)
21 visit = util.deque(revs)
23 seen = set([node.nullrev])
22 seen = set([node.nullrev])
24 while visit:
23 while visit:
25 for parent in cl.parentrevs(visit.popleft())[:cut]:
24 for parent in cl.parentrevs(visit.popleft())[:cut]:
26 if parent not in seen:
25 if parent not in seen:
27 visit.append(parent)
26 visit.append(parent)
28 seen.add(parent)
27 seen.add(parent)
29 yield parent
28 yield parent
30
29
31 def _revdescendants(repo, revs, followfirst):
30 def _revdescendants(repo, revs, followfirst):
32 """Like revlog.descendants() but supports followfirst."""
31 """Like revlog.descendants() but supports followfirst."""
33 cut = followfirst and 1 or None
32 cut = followfirst and 1 or None
34 cl = repo.changelog
33 cl = repo.changelog
35 first = min(revs)
34 first = min(revs)
36 nullrev = node.nullrev
35 nullrev = node.nullrev
37 if first == nullrev:
36 if first == nullrev:
38 # Are there nodes with a null first parent and a non-null
37 # Are there nodes with a null first parent and a non-null
39 # second one? Maybe. Do we care? Probably not.
38 # second one? Maybe. Do we care? Probably not.
40 for i in cl:
39 for i in cl:
41 yield i
40 yield i
42 return
41 return
43
42
44 seen = set(revs)
43 seen = set(revs)
45 for i in cl.revs(first + 1):
44 for i in cl.revs(first + 1):
46 for x in cl.parentrevs(i)[:cut]:
45 for x in cl.parentrevs(i)[:cut]:
47 if x != nullrev and x in seen:
46 if x != nullrev and x in seen:
48 seen.add(i)
47 seen.add(i)
49 yield i
48 yield i
50 break
49 break
51
50
52 def _revsbetween(repo, roots, heads):
51 def _revsbetween(repo, roots, heads):
53 """Return all paths between roots and heads, inclusive of both endpoint
52 """Return all paths between roots and heads, inclusive of both endpoint
54 sets."""
53 sets."""
55 if not roots:
54 if not roots:
56 return []
55 return []
57 parentrevs = repo.changelog.parentrevs
56 parentrevs = repo.changelog.parentrevs
58 visit = heads[:]
57 visit = heads[:]
59 reachable = set()
58 reachable = set()
60 seen = {}
59 seen = {}
61 minroot = min(roots)
60 minroot = min(roots)
62 roots = set(roots)
61 roots = set(roots)
63 # open-code the post-order traversal due to the tiny size of
62 # open-code the post-order traversal due to the tiny size of
64 # sys.getrecursionlimit()
63 # sys.getrecursionlimit()
65 while visit:
64 while visit:
66 rev = visit.pop()
65 rev = visit.pop()
67 if rev in roots:
66 if rev in roots:
68 reachable.add(rev)
67 reachable.add(rev)
69 parents = parentrevs(rev)
68 parents = parentrevs(rev)
70 seen[rev] = parents
69 seen[rev] = parents
71 for parent in parents:
70 for parent in parents:
72 if parent >= minroot and parent not in seen:
71 if parent >= minroot and parent not in seen:
73 visit.append(parent)
72 visit.append(parent)
74 if not reachable:
73 if not reachable:
75 return []
74 return []
76 for rev in sorted(seen):
75 for rev in sorted(seen):
77 for parent in seen[rev]:
76 for parent in seen[rev]:
78 if parent in reachable:
77 if parent in reachable:
79 reachable.add(rev)
78 reachable.add(rev)
80 return sorted(reachable)
79 return sorted(reachable)
81
80
82 elements = {
81 elements = {
83 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
82 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
84 "~": (18, None, ("ancestor", 18)),
83 "~": (18, None, ("ancestor", 18)),
85 "^": (18, None, ("parent", 18), ("parentpost", 18)),
84 "^": (18, None, ("parent", 18), ("parentpost", 18)),
86 "-": (5, ("negate", 19), ("minus", 5)),
85 "-": (5, ("negate", 19), ("minus", 5)),
87 "::": (17, ("dagrangepre", 17), ("dagrange", 17),
86 "::": (17, ("dagrangepre", 17), ("dagrange", 17),
88 ("dagrangepost", 17)),
87 ("dagrangepost", 17)),
89 "..": (17, ("dagrangepre", 17), ("dagrange", 17),
88 "..": (17, ("dagrangepre", 17), ("dagrange", 17),
90 ("dagrangepost", 17)),
89 ("dagrangepost", 17)),
91 ":": (15, ("rangepre", 15), ("range", 15), ("rangepost", 15)),
90 ":": (15, ("rangepre", 15), ("range", 15), ("rangepost", 15)),
92 "not": (10, ("not", 10)),
91 "not": (10, ("not", 10)),
93 "!": (10, ("not", 10)),
92 "!": (10, ("not", 10)),
94 "and": (5, None, ("and", 5)),
93 "and": (5, None, ("and", 5)),
95 "&": (5, None, ("and", 5)),
94 "&": (5, None, ("and", 5)),
96 "or": (4, None, ("or", 4)),
95 "or": (4, None, ("or", 4)),
97 "|": (4, None, ("or", 4)),
96 "|": (4, None, ("or", 4)),
98 "+": (4, None, ("or", 4)),
97 "+": (4, None, ("or", 4)),
99 ",": (2, None, ("list", 2)),
98 ",": (2, None, ("list", 2)),
100 ")": (0, None, None),
99 ")": (0, None, None),
101 "symbol": (0, ("symbol",), None),
100 "symbol": (0, ("symbol",), None),
102 "string": (0, ("string",), None),
101 "string": (0, ("string",), None),
103 "end": (0, None, None),
102 "end": (0, None, None),
104 }
103 }
105
104
106 keywords = set(['and', 'or', 'not'])
105 keywords = set(['and', 'or', 'not'])
107
106
108 def tokenize(program):
107 def tokenize(program):
109 '''
108 '''
110 Parse a revset statement into a stream of tokens
109 Parse a revset statement into a stream of tokens
111
110
112 Check that @ is a valid unquoted token character (issue3686):
111 Check that @ is a valid unquoted token character (issue3686):
113 >>> list(tokenize("@::"))
112 >>> list(tokenize("@::"))
114 [('symbol', '@', 0), ('::', None, 1), ('end', None, 3)]
113 [('symbol', '@', 0), ('::', None, 1), ('end', None, 3)]
115
114
116 '''
115 '''
117
116
118 pos, l = 0, len(program)
117 pos, l = 0, len(program)
119 while pos < l:
118 while pos < l:
120 c = program[pos]
119 c = program[pos]
121 if c.isspace(): # skip inter-token whitespace
120 if c.isspace(): # skip inter-token whitespace
122 pass
121 pass
123 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
122 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
124 yield ('::', None, pos)
123 yield ('::', None, pos)
125 pos += 1 # skip ahead
124 pos += 1 # skip ahead
126 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
125 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
127 yield ('..', None, pos)
126 yield ('..', None, pos)
128 pos += 1 # skip ahead
127 pos += 1 # skip ahead
129 elif c in "():,-|&+!~^": # handle simple operators
128 elif c in "():,-|&+!~^": # handle simple operators
130 yield (c, None, pos)
129 yield (c, None, pos)
131 elif (c in '"\'' or c == 'r' and
130 elif (c in '"\'' or c == 'r' and
132 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
131 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
133 if c == 'r':
132 if c == 'r':
134 pos += 1
133 pos += 1
135 c = program[pos]
134 c = program[pos]
136 decode = lambda x: x
135 decode = lambda x: x
137 else:
136 else:
138 decode = lambda x: x.decode('string-escape')
137 decode = lambda x: x.decode('string-escape')
139 pos += 1
138 pos += 1
140 s = pos
139 s = pos
141 while pos < l: # find closing quote
140 while pos < l: # find closing quote
142 d = program[pos]
141 d = program[pos]
143 if d == '\\': # skip over escaped characters
142 if d == '\\': # skip over escaped characters
144 pos += 2
143 pos += 2
145 continue
144 continue
146 if d == c:
145 if d == c:
147 yield ('string', decode(program[s:pos]), s)
146 yield ('string', decode(program[s:pos]), s)
148 break
147 break
149 pos += 1
148 pos += 1
150 else:
149 else:
151 raise error.ParseError(_("unterminated string"), s)
150 raise error.ParseError(_("unterminated string"), s)
152 # gather up a symbol/keyword
151 # gather up a symbol/keyword
153 elif c.isalnum() or c in '._@' or ord(c) > 127:
152 elif c.isalnum() or c in '._@' or ord(c) > 127:
154 s = pos
153 s = pos
155 pos += 1
154 pos += 1
156 while pos < l: # find end of symbol
155 while pos < l: # find end of symbol
157 d = program[pos]
156 d = program[pos]
158 if not (d.isalnum() or d in "._/@" or ord(d) > 127):
157 if not (d.isalnum() or d in "._/@" or ord(d) > 127):
159 break
158 break
160 if d == '.' and program[pos - 1] == '.': # special case for ..
159 if d == '.' and program[pos - 1] == '.': # special case for ..
161 pos -= 1
160 pos -= 1
162 break
161 break
163 pos += 1
162 pos += 1
164 sym = program[s:pos]
163 sym = program[s:pos]
165 if sym in keywords: # operator keywords
164 if sym in keywords: # operator keywords
166 yield (sym, None, s)
165 yield (sym, None, s)
167 else:
166 else:
168 yield ('symbol', sym, s)
167 yield ('symbol', sym, s)
169 pos -= 1
168 pos -= 1
170 else:
169 else:
171 raise error.ParseError(_("syntax error"), pos)
170 raise error.ParseError(_("syntax error"), pos)
172 pos += 1
171 pos += 1
173 yield ('end', None, pos)
172 yield ('end', None, pos)
174
173
175 # helpers
174 # helpers
176
175
177 def getstring(x, err):
176 def getstring(x, err):
178 if x and (x[0] == 'string' or x[0] == 'symbol'):
177 if x and (x[0] == 'string' or x[0] == 'symbol'):
179 return x[1]
178 return x[1]
180 raise error.ParseError(err)
179 raise error.ParseError(err)
181
180
182 def getlist(x):
181 def getlist(x):
183 if not x:
182 if not x:
184 return []
183 return []
185 if x[0] == 'list':
184 if x[0] == 'list':
186 return getlist(x[1]) + [x[2]]
185 return getlist(x[1]) + [x[2]]
187 return [x]
186 return [x]
188
187
189 def getargs(x, min, max, err):
188 def getargs(x, min, max, err):
190 l = getlist(x)
189 l = getlist(x)
191 if len(l) < min or (max >= 0 and len(l) > max):
190 if len(l) < min or (max >= 0 and len(l) > max):
192 raise error.ParseError(err)
191 raise error.ParseError(err)
193 return l
192 return l
194
193
195 def getset(repo, subset, x):
194 def getset(repo, subset, x):
196 if not x:
195 if not x:
197 raise error.ParseError(_("missing argument"))
196 raise error.ParseError(_("missing argument"))
198 return methods[x[0]](repo, subset, *x[1:])
197 return methods[x[0]](repo, subset, *x[1:])
199
198
200 def _getrevsource(repo, r):
199 def _getrevsource(repo, r):
201 extra = repo[r].extra()
200 extra = repo[r].extra()
202 for label in ('source', 'transplant_source', 'rebase_source'):
201 for label in ('source', 'transplant_source', 'rebase_source'):
203 if label in extra:
202 if label in extra:
204 try:
203 try:
205 return repo[extra[label]].rev()
204 return repo[extra[label]].rev()
206 except error.RepoLookupError:
205 except error.RepoLookupError:
207 pass
206 pass
208 return None
207 return None
209
208
210 # operator methods
209 # operator methods
211
210
212 def stringset(repo, subset, x):
211 def stringset(repo, subset, x):
213 x = repo[x].rev()
212 x = repo[x].rev()
214 if x == -1 and len(subset) == len(repo):
213 if x == -1 and len(subset) == len(repo):
215 return [-1]
214 return [-1]
216 if len(subset) == len(repo) or x in subset:
215 if len(subset) == len(repo) or x in subset:
217 return [x]
216 return [x]
218 return []
217 return []
219
218
220 def symbolset(repo, subset, x):
219 def symbolset(repo, subset, x):
221 if x in symbols:
220 if x in symbols:
222 raise error.ParseError(_("can't use %s here") % x)
221 raise error.ParseError(_("can't use %s here") % x)
223 return stringset(repo, subset, x)
222 return stringset(repo, subset, x)
224
223
225 def rangeset(repo, subset, x, y):
224 def rangeset(repo, subset, x, y):
226 cl = repo.changelog
225 cl = repo.changelog
227 m = getset(repo, cl, x)
226 m = getset(repo, cl, x)
228 n = getset(repo, cl, y)
227 n = getset(repo, cl, y)
229
228
230 if not m or not n:
229 if not m or not n:
231 return []
230 return []
232 m, n = m[0], n[-1]
231 m, n = m[0], n[-1]
233
232
234 if m < n:
233 if m < n:
235 r = range(m, n + 1)
234 r = range(m, n + 1)
236 else:
235 else:
237 r = range(m, n - 1, -1)
236 r = range(m, n - 1, -1)
238 s = set(subset)
237 s = set(subset)
239 return [x for x in r if x in s]
238 return [x for x in r if x in s]
240
239
241 def dagrange(repo, subset, x, y):
240 def dagrange(repo, subset, x, y):
242 if subset:
241 if subset:
243 r = list(repo)
242 r = list(repo)
244 xs = _revsbetween(repo, getset(repo, r, x), getset(repo, r, y))
243 xs = _revsbetween(repo, getset(repo, r, x), getset(repo, r, y))
245 s = set(subset)
244 s = set(subset)
246 return [r for r in xs if r in s]
245 return [r for r in xs if r in s]
247 return []
246 return []
248
247
249 def andset(repo, subset, x, y):
248 def andset(repo, subset, x, y):
250 return getset(repo, getset(repo, subset, x), y)
249 return getset(repo, getset(repo, subset, x), y)
251
250
252 def orset(repo, subset, x, y):
251 def orset(repo, subset, x, y):
253 xl = getset(repo, subset, x)
252 xl = getset(repo, subset, x)
254 s = set(xl)
253 s = set(xl)
255 yl = getset(repo, [r for r in subset if r not in s], y)
254 yl = getset(repo, [r for r in subset if r not in s], y)
256 return xl + yl
255 return xl + yl
257
256
258 def notset(repo, subset, x):
257 def notset(repo, subset, x):
259 s = set(getset(repo, subset, x))
258 s = set(getset(repo, subset, x))
260 return [r for r in subset if r not in s]
259 return [r for r in subset if r not in s]
261
260
262 def listset(repo, subset, a, b):
261 def listset(repo, subset, a, b):
263 raise error.ParseError(_("can't use a list in this context"))
262 raise error.ParseError(_("can't use a list in this context"))
264
263
265 def func(repo, subset, a, b):
264 def func(repo, subset, a, b):
266 if a[0] == 'symbol' and a[1] in symbols:
265 if a[0] == 'symbol' and a[1] in symbols:
267 return symbols[a[1]](repo, subset, b)
266 return symbols[a[1]](repo, subset, b)
268 raise error.ParseError(_("not a function: %s") % a[1])
267 raise error.ParseError(_("not a function: %s") % a[1])
269
268
270 # functions
269 # functions
271
270
272 def adds(repo, subset, x):
271 def adds(repo, subset, x):
273 """``adds(pattern)``
272 """``adds(pattern)``
274 Changesets that add a file matching pattern.
273 Changesets that add a file matching pattern.
275 """
274 """
276 # i18n: "adds" is a keyword
275 # i18n: "adds" is a keyword
277 pat = getstring(x, _("adds requires a pattern"))
276 pat = getstring(x, _("adds requires a pattern"))
278 return checkstatus(repo, subset, pat, 1)
277 return checkstatus(repo, subset, pat, 1)
279
278
280 def ancestor(repo, subset, x):
279 def ancestor(repo, subset, x):
281 """``ancestor(single, single)``
280 """``ancestor(single, single)``
282 Greatest common ancestor of the two changesets.
281 Greatest common ancestor of the two changesets.
283 """
282 """
284 # i18n: "ancestor" is a keyword
283 # i18n: "ancestor" is a keyword
285 l = getargs(x, 2, 2, _("ancestor requires two arguments"))
284 l = getargs(x, 2, 2, _("ancestor requires two arguments"))
286 r = list(repo)
285 r = list(repo)
287 a = getset(repo, r, l[0])
286 a = getset(repo, r, l[0])
288 b = getset(repo, r, l[1])
287 b = getset(repo, r, l[1])
289 if len(a) != 1 or len(b) != 1:
288 if len(a) != 1 or len(b) != 1:
290 # i18n: "ancestor" is a keyword
289 # i18n: "ancestor" is a keyword
291 raise error.ParseError(_("ancestor arguments must be single revisions"))
290 raise error.ParseError(_("ancestor arguments must be single revisions"))
292 an = [repo[a[0]].ancestor(repo[b[0]]).rev()]
291 an = [repo[a[0]].ancestor(repo[b[0]]).rev()]
293
292
294 return [r for r in an if r in subset]
293 return [r for r in an if r in subset]
295
294
296 def _ancestors(repo, subset, x, followfirst=False):
295 def _ancestors(repo, subset, x, followfirst=False):
297 args = getset(repo, list(repo), x)
296 args = getset(repo, list(repo), x)
298 if not args:
297 if not args:
299 return []
298 return []
300 s = set(_revancestors(repo, args, followfirst)) | set(args)
299 s = set(_revancestors(repo, args, followfirst)) | set(args)
301 return [r for r in subset if r in s]
300 return [r for r in subset if r in s]
302
301
303 def ancestors(repo, subset, x):
302 def ancestors(repo, subset, x):
304 """``ancestors(set)``
303 """``ancestors(set)``
305 Changesets that are ancestors of a changeset in set.
304 Changesets that are ancestors of a changeset in set.
306 """
305 """
307 return _ancestors(repo, subset, x)
306 return _ancestors(repo, subset, x)
308
307
309 def _firstancestors(repo, subset, x):
308 def _firstancestors(repo, subset, x):
310 # ``_firstancestors(set)``
309 # ``_firstancestors(set)``
311 # Like ``ancestors(set)`` but follows only the first parents.
310 # Like ``ancestors(set)`` but follows only the first parents.
312 return _ancestors(repo, subset, x, followfirst=True)
311 return _ancestors(repo, subset, x, followfirst=True)
313
312
314 def ancestorspec(repo, subset, x, n):
313 def ancestorspec(repo, subset, x, n):
315 """``set~n``
314 """``set~n``
316 Changesets that are the Nth ancestor (first parents only) of a changeset
315 Changesets that are the Nth ancestor (first parents only) of a changeset
317 in set.
316 in set.
318 """
317 """
319 try:
318 try:
320 n = int(n[1])
319 n = int(n[1])
321 except (TypeError, ValueError):
320 except (TypeError, ValueError):
322 raise error.ParseError(_("~ expects a number"))
321 raise error.ParseError(_("~ expects a number"))
323 ps = set()
322 ps = set()
324 cl = repo.changelog
323 cl = repo.changelog
325 for r in getset(repo, cl, x):
324 for r in getset(repo, cl, x):
326 for i in range(n):
325 for i in range(n):
327 r = cl.parentrevs(r)[0]
326 r = cl.parentrevs(r)[0]
328 ps.add(r)
327 ps.add(r)
329 return [r for r in subset if r in ps]
328 return [r for r in subset if r in ps]
330
329
331 def author(repo, subset, x):
330 def author(repo, subset, x):
332 """``author(string)``
331 """``author(string)``
333 Alias for ``user(string)``.
332 Alias for ``user(string)``.
334 """
333 """
335 # i18n: "author" is a keyword
334 # i18n: "author" is a keyword
336 n = encoding.lower(getstring(x, _("author requires a string")))
335 n = encoding.lower(getstring(x, _("author requires a string")))
337 kind, pattern, matcher = _substringmatcher(n)
336 kind, pattern, matcher = _substringmatcher(n)
338 return [r for r in subset if matcher(encoding.lower(repo[r].user()))]
337 return [r for r in subset if matcher(encoding.lower(repo[r].user()))]
339
338
340 def bisect(repo, subset, x):
339 def bisect(repo, subset, x):
341 """``bisect(string)``
340 """``bisect(string)``
342 Changesets marked in the specified bisect status:
341 Changesets marked in the specified bisect status:
343
342
344 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
343 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
345 - ``goods``, ``bads`` : csets topologically good/bad
344 - ``goods``, ``bads`` : csets topologically good/bad
346 - ``range`` : csets taking part in the bisection
345 - ``range`` : csets taking part in the bisection
347 - ``pruned`` : csets that are goods, bads or skipped
346 - ``pruned`` : csets that are goods, bads or skipped
348 - ``untested`` : csets whose fate is yet unknown
347 - ``untested`` : csets whose fate is yet unknown
349 - ``ignored`` : csets ignored due to DAG topology
348 - ``ignored`` : csets ignored due to DAG topology
350 - ``current`` : the cset currently being bisected
349 - ``current`` : the cset currently being bisected
351 """
350 """
352 # i18n: "bisect" is a keyword
351 # i18n: "bisect" is a keyword
353 status = getstring(x, _("bisect requires a string")).lower()
352 status = getstring(x, _("bisect requires a string")).lower()
354 state = set(hbisect.get(repo, status))
353 state = set(hbisect.get(repo, status))
355 return [r for r in subset if r in state]
354 return [r for r in subset if r in state]
356
355
357 # Backward-compatibility
356 # Backward-compatibility
358 # - no help entry so that we do not advertise it any more
357 # - no help entry so that we do not advertise it any more
359 def bisected(repo, subset, x):
358 def bisected(repo, subset, x):
360 return bisect(repo, subset, x)
359 return bisect(repo, subset, x)
361
360
362 def bookmark(repo, subset, x):
361 def bookmark(repo, subset, x):
363 """``bookmark([name])``
362 """``bookmark([name])``
364 The named bookmark or all bookmarks.
363 The named bookmark or all bookmarks.
365
364
366 If `name` starts with `re:`, the remainder of the name is treated as
365 If `name` starts with `re:`, the remainder of the name is treated as
367 a regular expression. To match a bookmark that actually starts with `re:`,
366 a regular expression. To match a bookmark that actually starts with `re:`,
368 use the prefix `literal:`.
367 use the prefix `literal:`.
369 """
368 """
370 # i18n: "bookmark" is a keyword
369 # i18n: "bookmark" is a keyword
371 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
370 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
372 if args:
371 if args:
373 bm = getstring(args[0],
372 bm = getstring(args[0],
374 # i18n: "bookmark" is a keyword
373 # i18n: "bookmark" is a keyword
375 _('the argument to bookmark must be a string'))
374 _('the argument to bookmark must be a string'))
376 kind, pattern, matcher = _stringmatcher(bm)
375 kind, pattern, matcher = _stringmatcher(bm)
377 if kind == 'literal':
376 if kind == 'literal':
378 bmrev = bookmarksmod.listbookmarks(repo).get(bm, None)
377 bmrev = repo._bookmarks.get(bm, None)
379 if not bmrev:
378 if not bmrev:
380 raise util.Abort(_("bookmark '%s' does not exist") % bm)
379 raise util.Abort(_("bookmark '%s' does not exist") % bm)
381 bmrev = repo[bmrev].rev()
380 bmrev = repo[bmrev].rev()
382 return [r for r in subset if r == bmrev]
381 return [r for r in subset if r == bmrev]
383 else:
382 else:
384 matchrevs = set()
383 matchrevs = set()
385 for name, bmrev in bookmarksmod.listbookmarks(repo).iteritems():
384 for name, bmrev in repo._bookmarks.iteritems():
386 if matcher(name):
385 if matcher(name):
387 matchrevs.add(bmrev)
386 matchrevs.add(bmrev)
388 if not matchrevs:
387 if not matchrevs:
389 raise util.Abort(_("no bookmarks exist that match '%s'")
388 raise util.Abort(_("no bookmarks exist that match '%s'")
390 % pattern)
389 % pattern)
391 bmrevs = set()
390 bmrevs = set()
392 for bmrev in matchrevs:
391 for bmrev in matchrevs:
393 bmrevs.add(repo[bmrev].rev())
392 bmrevs.add(repo[bmrev].rev())
394 return [r for r in subset if r in bmrevs]
393 return [r for r in subset if r in bmrevs]
395
394
396 bms = set([repo[r].rev()
395 bms = set([repo[r].rev()
397 for r in bookmarksmod.listbookmarks(repo).values()])
396 for r in repo._bookmarks.values()])
398 return [r for r in subset if r in bms]
397 return [r for r in subset if r in bms]
399
398
400 def branch(repo, subset, x):
399 def branch(repo, subset, x):
401 """``branch(string or set)``
400 """``branch(string or set)``
402 All changesets belonging to the given branch or the branches of the given
401 All changesets belonging to the given branch or the branches of the given
403 changesets.
402 changesets.
404
403
405 If `string` starts with `re:`, the remainder of the name is treated as
404 If `string` starts with `re:`, the remainder of the name is treated as
406 a regular expression. To match a branch that actually starts with `re:`,
405 a regular expression. To match a branch that actually starts with `re:`,
407 use the prefix `literal:`.
406 use the prefix `literal:`.
408 """
407 """
409 try:
408 try:
410 b = getstring(x, '')
409 b = getstring(x, '')
411 except error.ParseError:
410 except error.ParseError:
412 # not a string, but another revspec, e.g. tip()
411 # not a string, but another revspec, e.g. tip()
413 pass
412 pass
414 else:
413 else:
415 kind, pattern, matcher = _stringmatcher(b)
414 kind, pattern, matcher = _stringmatcher(b)
416 if kind == 'literal':
415 if kind == 'literal':
417 # note: falls through to the revspec case if no branch with
416 # note: falls through to the revspec case if no branch with
418 # this name exists
417 # this name exists
419 if pattern in repo.branchmap():
418 if pattern in repo.branchmap():
420 return [r for r in subset if matcher(repo[r].branch())]
419 return [r for r in subset if matcher(repo[r].branch())]
421 else:
420 else:
422 return [r for r in subset if matcher(repo[r].branch())]
421 return [r for r in subset if matcher(repo[r].branch())]
423
422
424 s = getset(repo, list(repo), x)
423 s = getset(repo, list(repo), x)
425 b = set()
424 b = set()
426 for r in s:
425 for r in s:
427 b.add(repo[r].branch())
426 b.add(repo[r].branch())
428 s = set(s)
427 s = set(s)
429 return [r for r in subset if r in s or repo[r].branch() in b]
428 return [r for r in subset if r in s or repo[r].branch() in b]
430
429
431 def bumped(repo, subset, x):
430 def bumped(repo, subset, x):
432 """``bumped()``
431 """``bumped()``
433 Mutable changesets marked as successors of public changesets.
432 Mutable changesets marked as successors of public changesets.
434
433
435 Only non-public and non-obsolete changesets can be `bumped`.
434 Only non-public and non-obsolete changesets can be `bumped`.
436 """
435 """
437 # i18n: "bumped" is a keyword
436 # i18n: "bumped" is a keyword
438 getargs(x, 0, 0, _("bumped takes no arguments"))
437 getargs(x, 0, 0, _("bumped takes no arguments"))
439 bumped = obsmod.getrevs(repo, 'bumped')
438 bumped = obsmod.getrevs(repo, 'bumped')
440 return [r for r in subset if r in bumped]
439 return [r for r in subset if r in bumped]
441
440
442 def bundle(repo, subset, x):
441 def bundle(repo, subset, x):
443 """``bundle()``
442 """``bundle()``
444 Changesets in the bundle.
443 Changesets in the bundle.
445
444
446 Bundle must be specified by the -R option."""
445 Bundle must be specified by the -R option."""
447
446
448 try:
447 try:
449 bundlerevs = repo.changelog.bundlerevs
448 bundlerevs = repo.changelog.bundlerevs
450 except AttributeError:
449 except AttributeError:
451 raise util.Abort(_("no bundle provided - specify with -R"))
450 raise util.Abort(_("no bundle provided - specify with -R"))
452 return [r for r in subset if r in bundlerevs]
451 return [r for r in subset if r in bundlerevs]
453
452
454 def checkstatus(repo, subset, pat, field):
453 def checkstatus(repo, subset, pat, field):
455 m = None
454 m = None
456 s = []
455 s = []
457 hasset = matchmod.patkind(pat) == 'set'
456 hasset = matchmod.patkind(pat) == 'set'
458 fname = None
457 fname = None
459 for r in subset:
458 for r in subset:
460 c = repo[r]
459 c = repo[r]
461 if not m or hasset:
460 if not m or hasset:
462 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
461 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
463 if not m.anypats() and len(m.files()) == 1:
462 if not m.anypats() and len(m.files()) == 1:
464 fname = m.files()[0]
463 fname = m.files()[0]
465 if fname is not None:
464 if fname is not None:
466 if fname not in c.files():
465 if fname not in c.files():
467 continue
466 continue
468 else:
467 else:
469 for f in c.files():
468 for f in c.files():
470 if m(f):
469 if m(f):
471 break
470 break
472 else:
471 else:
473 continue
472 continue
474 files = repo.status(c.p1().node(), c.node())[field]
473 files = repo.status(c.p1().node(), c.node())[field]
475 if fname is not None:
474 if fname is not None:
476 if fname in files:
475 if fname in files:
477 s.append(r)
476 s.append(r)
478 else:
477 else:
479 for f in files:
478 for f in files:
480 if m(f):
479 if m(f):
481 s.append(r)
480 s.append(r)
482 break
481 break
483 return s
482 return s
484
483
485 def _children(repo, narrow, parentset):
484 def _children(repo, narrow, parentset):
486 cs = set()
485 cs = set()
487 if not parentset:
486 if not parentset:
488 return cs
487 return cs
489 pr = repo.changelog.parentrevs
488 pr = repo.changelog.parentrevs
490 minrev = min(parentset)
489 minrev = min(parentset)
491 for r in narrow:
490 for r in narrow:
492 if r <= minrev:
491 if r <= minrev:
493 continue
492 continue
494 for p in pr(r):
493 for p in pr(r):
495 if p in parentset:
494 if p in parentset:
496 cs.add(r)
495 cs.add(r)
497 return cs
496 return cs
498
497
499 def children(repo, subset, x):
498 def children(repo, subset, x):
500 """``children(set)``
499 """``children(set)``
501 Child changesets of changesets in set.
500 Child changesets of changesets in set.
502 """
501 """
503 s = set(getset(repo, list(repo), x))
502 s = set(getset(repo, list(repo), x))
504 cs = _children(repo, subset, s)
503 cs = _children(repo, subset, s)
505 return [r for r in subset if r in cs]
504 return [r for r in subset if r in cs]
506
505
507 def closed(repo, subset, x):
506 def closed(repo, subset, x):
508 """``closed()``
507 """``closed()``
509 Changeset is closed.
508 Changeset is closed.
510 """
509 """
511 # i18n: "closed" is a keyword
510 # i18n: "closed" is a keyword
512 getargs(x, 0, 0, _("closed takes no arguments"))
511 getargs(x, 0, 0, _("closed takes no arguments"))
513 return [r for r in subset if repo[r].closesbranch()]
512 return [r for r in subset if repo[r].closesbranch()]
514
513
515 def contains(repo, subset, x):
514 def contains(repo, subset, x):
516 """``contains(pattern)``
515 """``contains(pattern)``
517 Revision contains a file matching pattern. See :hg:`help patterns`
516 Revision contains a file matching pattern. See :hg:`help patterns`
518 for information about file patterns.
517 for information about file patterns.
519 """
518 """
520 # i18n: "contains" is a keyword
519 # i18n: "contains" is a keyword
521 pat = getstring(x, _("contains requires a pattern"))
520 pat = getstring(x, _("contains requires a pattern"))
522 m = None
521 m = None
523 s = []
522 s = []
524 if not matchmod.patkind(pat):
523 if not matchmod.patkind(pat):
525 for r in subset:
524 for r in subset:
526 if pat in repo[r]:
525 if pat in repo[r]:
527 s.append(r)
526 s.append(r)
528 else:
527 else:
529 for r in subset:
528 for r in subset:
530 c = repo[r]
529 c = repo[r]
531 if not m or matchmod.patkind(pat) == 'set':
530 if not m or matchmod.patkind(pat) == 'set':
532 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
531 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
533 for f in c.manifest():
532 for f in c.manifest():
534 if m(f):
533 if m(f):
535 s.append(r)
534 s.append(r)
536 break
535 break
537 return s
536 return s
538
537
539 def converted(repo, subset, x):
538 def converted(repo, subset, x):
540 """``converted([id])``
539 """``converted([id])``
541 Changesets converted from the given identifier in the old repository if
540 Changesets converted from the given identifier in the old repository if
542 present, or all converted changesets if no identifier is specified.
541 present, or all converted changesets if no identifier is specified.
543 """
542 """
544
543
545 # There is exactly no chance of resolving the revision, so do a simple
544 # There is exactly no chance of resolving the revision, so do a simple
546 # string compare and hope for the best
545 # string compare and hope for the best
547
546
548 rev = None
547 rev = None
549 # i18n: "converted" is a keyword
548 # i18n: "converted" is a keyword
550 l = getargs(x, 0, 1, _('converted takes one or no arguments'))
549 l = getargs(x, 0, 1, _('converted takes one or no arguments'))
551 if l:
550 if l:
552 # i18n: "converted" is a keyword
551 # i18n: "converted" is a keyword
553 rev = getstring(l[0], _('converted requires a revision'))
552 rev = getstring(l[0], _('converted requires a revision'))
554
553
555 def _matchvalue(r):
554 def _matchvalue(r):
556 source = repo[r].extra().get('convert_revision', None)
555 source = repo[r].extra().get('convert_revision', None)
557 return source is not None and (rev is None or source.startswith(rev))
556 return source is not None and (rev is None or source.startswith(rev))
558
557
559 return [r for r in subset if _matchvalue(r)]
558 return [r for r in subset if _matchvalue(r)]
560
559
561 def date(repo, subset, x):
560 def date(repo, subset, x):
562 """``date(interval)``
561 """``date(interval)``
563 Changesets within the interval, see :hg:`help dates`.
562 Changesets within the interval, see :hg:`help dates`.
564 """
563 """
565 # i18n: "date" is a keyword
564 # i18n: "date" is a keyword
566 ds = getstring(x, _("date requires a string"))
565 ds = getstring(x, _("date requires a string"))
567 dm = util.matchdate(ds)
566 dm = util.matchdate(ds)
568 return [r for r in subset if dm(repo[r].date()[0])]
567 return [r for r in subset if dm(repo[r].date()[0])]
569
568
570 def desc(repo, subset, x):
569 def desc(repo, subset, x):
571 """``desc(string)``
570 """``desc(string)``
572 Search commit message for string. The match is case-insensitive.
571 Search commit message for string. The match is case-insensitive.
573 """
572 """
574 # i18n: "desc" is a keyword
573 # i18n: "desc" is a keyword
575 ds = encoding.lower(getstring(x, _("desc requires a string")))
574 ds = encoding.lower(getstring(x, _("desc requires a string")))
576 l = []
575 l = []
577 for r in subset:
576 for r in subset:
578 c = repo[r]
577 c = repo[r]
579 if ds in encoding.lower(c.description()):
578 if ds in encoding.lower(c.description()):
580 l.append(r)
579 l.append(r)
581 return l
580 return l
582
581
583 def _descendants(repo, subset, x, followfirst=False):
582 def _descendants(repo, subset, x, followfirst=False):
584 args = getset(repo, list(repo), x)
583 args = getset(repo, list(repo), x)
585 if not args:
584 if not args:
586 return []
585 return []
587 s = set(_revdescendants(repo, args, followfirst)) | set(args)
586 s = set(_revdescendants(repo, args, followfirst)) | set(args)
588 return [r for r in subset if r in s]
587 return [r for r in subset if r in s]
589
588
590 def descendants(repo, subset, x):
589 def descendants(repo, subset, x):
591 """``descendants(set)``
590 """``descendants(set)``
592 Changesets which are descendants of changesets in set.
591 Changesets which are descendants of changesets in set.
593 """
592 """
594 return _descendants(repo, subset, x)
593 return _descendants(repo, subset, x)
595
594
596 def _firstdescendants(repo, subset, x):
595 def _firstdescendants(repo, subset, x):
597 # ``_firstdescendants(set)``
596 # ``_firstdescendants(set)``
598 # Like ``descendants(set)`` but follows only the first parents.
597 # Like ``descendants(set)`` but follows only the first parents.
599 return _descendants(repo, subset, x, followfirst=True)
598 return _descendants(repo, subset, x, followfirst=True)
600
599
601 def destination(repo, subset, x):
600 def destination(repo, subset, x):
602 """``destination([set])``
601 """``destination([set])``
603 Changesets that were created by a graft, transplant or rebase operation,
602 Changesets that were created by a graft, transplant or rebase operation,
604 with the given revisions specified as the source. Omitting the optional set
603 with the given revisions specified as the source. Omitting the optional set
605 is the same as passing all().
604 is the same as passing all().
606 """
605 """
607 if x is not None:
606 if x is not None:
608 args = set(getset(repo, list(repo), x))
607 args = set(getset(repo, list(repo), x))
609 else:
608 else:
610 args = set(getall(repo, list(repo), x))
609 args = set(getall(repo, list(repo), x))
611
610
612 dests = set()
611 dests = set()
613
612
614 # subset contains all of the possible destinations that can be returned, so
613 # subset contains all of the possible destinations that can be returned, so
615 # iterate over them and see if their source(s) were provided in the args.
614 # iterate over them and see if their source(s) were provided in the args.
616 # Even if the immediate src of r is not in the args, src's source (or
615 # Even if the immediate src of r is not in the args, src's source (or
617 # further back) may be. Scanning back further than the immediate src allows
616 # further back) may be. Scanning back further than the immediate src allows
618 # transitive transplants and rebases to yield the same results as transitive
617 # transitive transplants and rebases to yield the same results as transitive
619 # grafts.
618 # grafts.
620 for r in subset:
619 for r in subset:
621 src = _getrevsource(repo, r)
620 src = _getrevsource(repo, r)
622 lineage = None
621 lineage = None
623
622
624 while src is not None:
623 while src is not None:
625 if lineage is None:
624 if lineage is None:
626 lineage = list()
625 lineage = list()
627
626
628 lineage.append(r)
627 lineage.append(r)
629
628
630 # The visited lineage is a match if the current source is in the arg
629 # The visited lineage is a match if the current source is in the arg
631 # set. Since every candidate dest is visited by way of iterating
630 # set. Since every candidate dest is visited by way of iterating
632 # subset, any dests further back in the lineage will be tested by a
631 # subset, any dests further back in the lineage will be tested by a
633 # different iteration over subset. Likewise, if the src was already
632 # different iteration over subset. Likewise, if the src was already
634 # selected, the current lineage can be selected without going back
633 # selected, the current lineage can be selected without going back
635 # further.
634 # further.
636 if src in args or src in dests:
635 if src in args or src in dests:
637 dests.update(lineage)
636 dests.update(lineage)
638 break
637 break
639
638
640 r = src
639 r = src
641 src = _getrevsource(repo, r)
640 src = _getrevsource(repo, r)
642
641
643 return [r for r in subset if r in dests]
642 return [r for r in subset if r in dests]
644
643
645 def divergent(repo, subset, x):
644 def divergent(repo, subset, x):
646 """``divergent()``
645 """``divergent()``
647 Final successors of changesets with an alternative set of final successors.
646 Final successors of changesets with an alternative set of final successors.
648 """
647 """
649 # i18n: "divergent" is a keyword
648 # i18n: "divergent" is a keyword
650 getargs(x, 0, 0, _("divergent takes no arguments"))
649 getargs(x, 0, 0, _("divergent takes no arguments"))
651 divergent = obsmod.getrevs(repo, 'divergent')
650 divergent = obsmod.getrevs(repo, 'divergent')
652 return [r for r in subset if r in divergent]
651 return [r for r in subset if r in divergent]
653
652
654 def draft(repo, subset, x):
653 def draft(repo, subset, x):
655 """``draft()``
654 """``draft()``
656 Changeset in draft phase."""
655 Changeset in draft phase."""
657 # i18n: "draft" is a keyword
656 # i18n: "draft" is a keyword
658 getargs(x, 0, 0, _("draft takes no arguments"))
657 getargs(x, 0, 0, _("draft takes no arguments"))
659 pc = repo._phasecache
658 pc = repo._phasecache
660 return [r for r in subset if pc.phase(repo, r) == phases.draft]
659 return [r for r in subset if pc.phase(repo, r) == phases.draft]
661
660
662 def extinct(repo, subset, x):
661 def extinct(repo, subset, x):
663 """``extinct()``
662 """``extinct()``
664 Obsolete changesets with obsolete descendants only.
663 Obsolete changesets with obsolete descendants only.
665 """
664 """
666 # i18n: "extinct" is a keyword
665 # i18n: "extinct" is a keyword
667 getargs(x, 0, 0, _("extinct takes no arguments"))
666 getargs(x, 0, 0, _("extinct takes no arguments"))
668 extincts = obsmod.getrevs(repo, 'extinct')
667 extincts = obsmod.getrevs(repo, 'extinct')
669 return [r for r in subset if r in extincts]
668 return [r for r in subset if r in extincts]
670
669
671 def extra(repo, subset, x):
670 def extra(repo, subset, x):
672 """``extra(label, [value])``
671 """``extra(label, [value])``
673 Changesets with the given label in the extra metadata, with the given
672 Changesets with the given label in the extra metadata, with the given
674 optional value.
673 optional value.
675
674
676 If `value` starts with `re:`, the remainder of the value is treated as
675 If `value` starts with `re:`, the remainder of the value is treated as
677 a regular expression. To match a value that actually starts with `re:`,
676 a regular expression. To match a value that actually starts with `re:`,
678 use the prefix `literal:`.
677 use the prefix `literal:`.
679 """
678 """
680
679
681 # i18n: "extra" is a keyword
680 # i18n: "extra" is a keyword
682 l = getargs(x, 1, 2, _('extra takes at least 1 and at most 2 arguments'))
681 l = getargs(x, 1, 2, _('extra takes at least 1 and at most 2 arguments'))
683 # i18n: "extra" is a keyword
682 # i18n: "extra" is a keyword
684 label = getstring(l[0], _('first argument to extra must be a string'))
683 label = getstring(l[0], _('first argument to extra must be a string'))
685 value = None
684 value = None
686
685
687 if len(l) > 1:
686 if len(l) > 1:
688 # i18n: "extra" is a keyword
687 # i18n: "extra" is a keyword
689 value = getstring(l[1], _('second argument to extra must be a string'))
688 value = getstring(l[1], _('second argument to extra must be a string'))
690 kind, value, matcher = _stringmatcher(value)
689 kind, value, matcher = _stringmatcher(value)
691
690
692 def _matchvalue(r):
691 def _matchvalue(r):
693 extra = repo[r].extra()
692 extra = repo[r].extra()
694 return label in extra and (value is None or matcher(extra[label]))
693 return label in extra and (value is None or matcher(extra[label]))
695
694
696 return [r for r in subset if _matchvalue(r)]
695 return [r for r in subset if _matchvalue(r)]
697
696
698 def filelog(repo, subset, x):
697 def filelog(repo, subset, x):
699 """``filelog(pattern)``
698 """``filelog(pattern)``
700 Changesets connected to the specified filelog.
699 Changesets connected to the specified filelog.
701
700
702 For performance reasons, ``filelog()`` does not show every changeset
701 For performance reasons, ``filelog()`` does not show every changeset
703 that affects the requested file(s). See :hg:`help log` for details. For
702 that affects the requested file(s). See :hg:`help log` for details. For
704 a slower, more accurate result, use ``file()``.
703 a slower, more accurate result, use ``file()``.
705 """
704 """
706
705
707 # i18n: "filelog" is a keyword
706 # i18n: "filelog" is a keyword
708 pat = getstring(x, _("filelog requires a pattern"))
707 pat = getstring(x, _("filelog requires a pattern"))
709 m = matchmod.match(repo.root, repo.getcwd(), [pat], default='relpath',
708 m = matchmod.match(repo.root, repo.getcwd(), [pat], default='relpath',
710 ctx=repo[None])
709 ctx=repo[None])
711 s = set()
710 s = set()
712
711
713 if not matchmod.patkind(pat):
712 if not matchmod.patkind(pat):
714 for f in m.files():
713 for f in m.files():
715 fl = repo.file(f)
714 fl = repo.file(f)
716 for fr in fl:
715 for fr in fl:
717 s.add(fl.linkrev(fr))
716 s.add(fl.linkrev(fr))
718 else:
717 else:
719 for f in repo[None]:
718 for f in repo[None]:
720 if m(f):
719 if m(f):
721 fl = repo.file(f)
720 fl = repo.file(f)
722 for fr in fl:
721 for fr in fl:
723 s.add(fl.linkrev(fr))
722 s.add(fl.linkrev(fr))
724
723
725 return [r for r in subset if r in s]
724 return [r for r in subset if r in s]
726
725
727 def first(repo, subset, x):
726 def first(repo, subset, x):
728 """``first(set, [n])``
727 """``first(set, [n])``
729 An alias for limit().
728 An alias for limit().
730 """
729 """
731 return limit(repo, subset, x)
730 return limit(repo, subset, x)
732
731
733 def _follow(repo, subset, x, name, followfirst=False):
732 def _follow(repo, subset, x, name, followfirst=False):
734 l = getargs(x, 0, 1, _("%s takes no arguments or a filename") % name)
733 l = getargs(x, 0, 1, _("%s takes no arguments or a filename") % name)
735 c = repo['.']
734 c = repo['.']
736 if l:
735 if l:
737 x = getstring(l[0], _("%s expected a filename") % name)
736 x = getstring(l[0], _("%s expected a filename") % name)
738 if x in c:
737 if x in c:
739 cx = c[x]
738 cx = c[x]
740 s = set(ctx.rev() for ctx in cx.ancestors(followfirst=followfirst))
739 s = set(ctx.rev() for ctx in cx.ancestors(followfirst=followfirst))
741 # include the revision responsible for the most recent version
740 # include the revision responsible for the most recent version
742 s.add(cx.linkrev())
741 s.add(cx.linkrev())
743 else:
742 else:
744 return []
743 return []
745 else:
744 else:
746 s = set(_revancestors(repo, [c.rev()], followfirst)) | set([c.rev()])
745 s = set(_revancestors(repo, [c.rev()], followfirst)) | set([c.rev()])
747
746
748 return [r for r in subset if r in s]
747 return [r for r in subset if r in s]
749
748
750 def follow(repo, subset, x):
749 def follow(repo, subset, x):
751 """``follow([file])``
750 """``follow([file])``
752 An alias for ``::.`` (ancestors of the working copy's first parent).
751 An alias for ``::.`` (ancestors of the working copy's first parent).
753 If a filename is specified, the history of the given file is followed,
752 If a filename is specified, the history of the given file is followed,
754 including copies.
753 including copies.
755 """
754 """
756 return _follow(repo, subset, x, 'follow')
755 return _follow(repo, subset, x, 'follow')
757
756
758 def _followfirst(repo, subset, x):
757 def _followfirst(repo, subset, x):
759 # ``followfirst([file])``
758 # ``followfirst([file])``
760 # Like ``follow([file])`` but follows only the first parent of
759 # Like ``follow([file])`` but follows only the first parent of
761 # every revision or file revision.
760 # every revision or file revision.
762 return _follow(repo, subset, x, '_followfirst', followfirst=True)
761 return _follow(repo, subset, x, '_followfirst', followfirst=True)
763
762
764 def getall(repo, subset, x):
763 def getall(repo, subset, x):
765 """``all()``
764 """``all()``
766 All changesets, the same as ``0:tip``.
765 All changesets, the same as ``0:tip``.
767 """
766 """
768 # i18n: "all" is a keyword
767 # i18n: "all" is a keyword
769 getargs(x, 0, 0, _("all takes no arguments"))
768 getargs(x, 0, 0, _("all takes no arguments"))
770 return subset
769 return subset
771
770
772 def grep(repo, subset, x):
771 def grep(repo, subset, x):
773 """``grep(regex)``
772 """``grep(regex)``
774 Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
773 Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
775 to ensure special escape characters are handled correctly. Unlike
774 to ensure special escape characters are handled correctly. Unlike
776 ``keyword(string)``, the match is case-sensitive.
775 ``keyword(string)``, the match is case-sensitive.
777 """
776 """
778 try:
777 try:
779 # i18n: "grep" is a keyword
778 # i18n: "grep" is a keyword
780 gr = re.compile(getstring(x, _("grep requires a string")))
779 gr = re.compile(getstring(x, _("grep requires a string")))
781 except re.error, e:
780 except re.error, e:
782 raise error.ParseError(_('invalid match pattern: %s') % e)
781 raise error.ParseError(_('invalid match pattern: %s') % e)
783 l = []
782 l = []
784 for r in subset:
783 for r in subset:
785 c = repo[r]
784 c = repo[r]
786 for e in c.files() + [c.user(), c.description()]:
785 for e in c.files() + [c.user(), c.description()]:
787 if gr.search(e):
786 if gr.search(e):
788 l.append(r)
787 l.append(r)
789 break
788 break
790 return l
789 return l
791
790
792 def _matchfiles(repo, subset, x):
791 def _matchfiles(repo, subset, x):
793 # _matchfiles takes a revset list of prefixed arguments:
792 # _matchfiles takes a revset list of prefixed arguments:
794 #
793 #
795 # [p:foo, i:bar, x:baz]
794 # [p:foo, i:bar, x:baz]
796 #
795 #
797 # builds a match object from them and filters subset. Allowed
796 # builds a match object from them and filters subset. Allowed
798 # prefixes are 'p:' for regular patterns, 'i:' for include
797 # prefixes are 'p:' for regular patterns, 'i:' for include
799 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
798 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
800 # a revision identifier, or the empty string to reference the
799 # a revision identifier, or the empty string to reference the
801 # working directory, from which the match object is
800 # working directory, from which the match object is
802 # initialized. Use 'd:' to set the default matching mode, default
801 # initialized. Use 'd:' to set the default matching mode, default
803 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
802 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
804
803
805 # i18n: "_matchfiles" is a keyword
804 # i18n: "_matchfiles" is a keyword
806 l = getargs(x, 1, -1, _("_matchfiles requires at least one argument"))
805 l = getargs(x, 1, -1, _("_matchfiles requires at least one argument"))
807 pats, inc, exc = [], [], []
806 pats, inc, exc = [], [], []
808 hasset = False
807 hasset = False
809 rev, default = None, None
808 rev, default = None, None
810 for arg in l:
809 for arg in l:
811 # i18n: "_matchfiles" is a keyword
810 # i18n: "_matchfiles" is a keyword
812 s = getstring(arg, _("_matchfiles requires string arguments"))
811 s = getstring(arg, _("_matchfiles requires string arguments"))
813 prefix, value = s[:2], s[2:]
812 prefix, value = s[:2], s[2:]
814 if prefix == 'p:':
813 if prefix == 'p:':
815 pats.append(value)
814 pats.append(value)
816 elif prefix == 'i:':
815 elif prefix == 'i:':
817 inc.append(value)
816 inc.append(value)
818 elif prefix == 'x:':
817 elif prefix == 'x:':
819 exc.append(value)
818 exc.append(value)
820 elif prefix == 'r:':
819 elif prefix == 'r:':
821 if rev is not None:
820 if rev is not None:
822 # i18n: "_matchfiles" is a keyword
821 # i18n: "_matchfiles" is a keyword
823 raise error.ParseError(_('_matchfiles expected at most one '
822 raise error.ParseError(_('_matchfiles expected at most one '
824 'revision'))
823 'revision'))
825 rev = value
824 rev = value
826 elif prefix == 'd:':
825 elif prefix == 'd:':
827 if default is not None:
826 if default is not None:
828 # i18n: "_matchfiles" is a keyword
827 # i18n: "_matchfiles" is a keyword
829 raise error.ParseError(_('_matchfiles expected at most one '
828 raise error.ParseError(_('_matchfiles expected at most one '
830 'default mode'))
829 'default mode'))
831 default = value
830 default = value
832 else:
831 else:
833 # i18n: "_matchfiles" is a keyword
832 # i18n: "_matchfiles" is a keyword
834 raise error.ParseError(_('invalid _matchfiles prefix: %s') % prefix)
833 raise error.ParseError(_('invalid _matchfiles prefix: %s') % prefix)
835 if not hasset and matchmod.patkind(value) == 'set':
834 if not hasset and matchmod.patkind(value) == 'set':
836 hasset = True
835 hasset = True
837 if not default:
836 if not default:
838 default = 'glob'
837 default = 'glob'
839 m = None
838 m = None
840 s = []
839 s = []
841 for r in subset:
840 for r in subset:
842 c = repo[r]
841 c = repo[r]
843 if not m or (hasset and rev is None):
842 if not m or (hasset and rev is None):
844 ctx = c
843 ctx = c
845 if rev is not None:
844 if rev is not None:
846 ctx = repo[rev or None]
845 ctx = repo[rev or None]
847 m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc,
846 m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc,
848 exclude=exc, ctx=ctx, default=default)
847 exclude=exc, ctx=ctx, default=default)
849 for f in c.files():
848 for f in c.files():
850 if m(f):
849 if m(f):
851 s.append(r)
850 s.append(r)
852 break
851 break
853 return s
852 return s
854
853
855 def hasfile(repo, subset, x):
854 def hasfile(repo, subset, x):
856 """``file(pattern)``
855 """``file(pattern)``
857 Changesets affecting files matched by pattern.
856 Changesets affecting files matched by pattern.
858
857
859 For a faster but less accurate result, consider using ``filelog()``
858 For a faster but less accurate result, consider using ``filelog()``
860 instead.
859 instead.
861 """
860 """
862 # i18n: "file" is a keyword
861 # i18n: "file" is a keyword
863 pat = getstring(x, _("file requires a pattern"))
862 pat = getstring(x, _("file requires a pattern"))
864 return _matchfiles(repo, subset, ('string', 'p:' + pat))
863 return _matchfiles(repo, subset, ('string', 'p:' + pat))
865
864
866 def head(repo, subset, x):
865 def head(repo, subset, x):
867 """``head()``
866 """``head()``
868 Changeset is a named branch head.
867 Changeset is a named branch head.
869 """
868 """
870 # i18n: "head" is a keyword
869 # i18n: "head" is a keyword
871 getargs(x, 0, 0, _("head takes no arguments"))
870 getargs(x, 0, 0, _("head takes no arguments"))
872 hs = set()
871 hs = set()
873 for b, ls in repo.branchmap().iteritems():
872 for b, ls in repo.branchmap().iteritems():
874 hs.update(repo[h].rev() for h in ls)
873 hs.update(repo[h].rev() for h in ls)
875 return [r for r in subset if r in hs]
874 return [r for r in subset if r in hs]
876
875
877 def heads(repo, subset, x):
876 def heads(repo, subset, x):
878 """``heads(set)``
877 """``heads(set)``
879 Members of set with no children in set.
878 Members of set with no children in set.
880 """
879 """
881 s = getset(repo, subset, x)
880 s = getset(repo, subset, x)
882 ps = set(parents(repo, subset, x))
881 ps = set(parents(repo, subset, x))
883 return [r for r in s if r not in ps]
882 return [r for r in s if r not in ps]
884
883
885 def hidden(repo, subset, x):
884 def hidden(repo, subset, x):
886 """``hidden()``
885 """``hidden()``
887 Hidden changesets.
886 Hidden changesets.
888 """
887 """
889 # i18n: "hidden" is a keyword
888 # i18n: "hidden" is a keyword
890 getargs(x, 0, 0, _("hidden takes no arguments"))
889 getargs(x, 0, 0, _("hidden takes no arguments"))
891 hiddenrevs = repoview.filterrevs(repo, 'visible')
890 hiddenrevs = repoview.filterrevs(repo, 'visible')
892 return [r for r in subset if r in hiddenrevs]
891 return [r for r in subset if r in hiddenrevs]
893
892
894 def keyword(repo, subset, x):
893 def keyword(repo, subset, x):
895 """``keyword(string)``
894 """``keyword(string)``
896 Search commit message, user name, and names of changed files for
895 Search commit message, user name, and names of changed files for
897 string. The match is case-insensitive.
896 string. The match is case-insensitive.
898 """
897 """
899 # i18n: "keyword" is a keyword
898 # i18n: "keyword" is a keyword
900 kw = encoding.lower(getstring(x, _("keyword requires a string")))
899 kw = encoding.lower(getstring(x, _("keyword requires a string")))
901 l = []
900 l = []
902 for r in subset:
901 for r in subset:
903 c = repo[r]
902 c = repo[r]
904 t = " ".join(c.files() + [c.user(), c.description()])
903 t = " ".join(c.files() + [c.user(), c.description()])
905 if kw in encoding.lower(t):
904 if kw in encoding.lower(t):
906 l.append(r)
905 l.append(r)
907 return l
906 return l
908
907
909 def limit(repo, subset, x):
908 def limit(repo, subset, x):
910 """``limit(set, [n])``
909 """``limit(set, [n])``
911 First n members of set, defaulting to 1.
910 First n members of set, defaulting to 1.
912 """
911 """
913 # i18n: "limit" is a keyword
912 # i18n: "limit" is a keyword
914 l = getargs(x, 1, 2, _("limit requires one or two arguments"))
913 l = getargs(x, 1, 2, _("limit requires one or two arguments"))
915 try:
914 try:
916 lim = 1
915 lim = 1
917 if len(l) == 2:
916 if len(l) == 2:
918 # i18n: "limit" is a keyword
917 # i18n: "limit" is a keyword
919 lim = int(getstring(l[1], _("limit requires a number")))
918 lim = int(getstring(l[1], _("limit requires a number")))
920 except (TypeError, ValueError):
919 except (TypeError, ValueError):
921 # i18n: "limit" is a keyword
920 # i18n: "limit" is a keyword
922 raise error.ParseError(_("limit expects a number"))
921 raise error.ParseError(_("limit expects a number"))
923 ss = set(subset)
922 ss = set(subset)
924 os = getset(repo, list(repo), l[0])[:lim]
923 os = getset(repo, list(repo), l[0])[:lim]
925 return [r for r in os if r in ss]
924 return [r for r in os if r in ss]
926
925
927 def last(repo, subset, x):
926 def last(repo, subset, x):
928 """``last(set, [n])``
927 """``last(set, [n])``
929 Last n members of set, defaulting to 1.
928 Last n members of set, defaulting to 1.
930 """
929 """
931 # i18n: "last" is a keyword
930 # i18n: "last" is a keyword
932 l = getargs(x, 1, 2, _("last requires one or two arguments"))
931 l = getargs(x, 1, 2, _("last requires one or two arguments"))
933 try:
932 try:
934 lim = 1
933 lim = 1
935 if len(l) == 2:
934 if len(l) == 2:
936 # i18n: "last" is a keyword
935 # i18n: "last" is a keyword
937 lim = int(getstring(l[1], _("last requires a number")))
936 lim = int(getstring(l[1], _("last requires a number")))
938 except (TypeError, ValueError):
937 except (TypeError, ValueError):
939 # i18n: "last" is a keyword
938 # i18n: "last" is a keyword
940 raise error.ParseError(_("last expects a number"))
939 raise error.ParseError(_("last expects a number"))
941 ss = set(subset)
940 ss = set(subset)
942 os = getset(repo, list(repo), l[0])[-lim:]
941 os = getset(repo, list(repo), l[0])[-lim:]
943 return [r for r in os if r in ss]
942 return [r for r in os if r in ss]
944
943
945 def maxrev(repo, subset, x):
944 def maxrev(repo, subset, x):
946 """``max(set)``
945 """``max(set)``
947 Changeset with highest revision number in set.
946 Changeset with highest revision number in set.
948 """
947 """
949 os = getset(repo, list(repo), x)
948 os = getset(repo, list(repo), x)
950 if os:
949 if os:
951 m = max(os)
950 m = max(os)
952 if m in subset:
951 if m in subset:
953 return [m]
952 return [m]
954 return []
953 return []
955
954
956 def merge(repo, subset, x):
955 def merge(repo, subset, x):
957 """``merge()``
956 """``merge()``
958 Changeset is a merge changeset.
957 Changeset is a merge changeset.
959 """
958 """
960 # i18n: "merge" is a keyword
959 # i18n: "merge" is a keyword
961 getargs(x, 0, 0, _("merge takes no arguments"))
960 getargs(x, 0, 0, _("merge takes no arguments"))
962 cl = repo.changelog
961 cl = repo.changelog
963 return [r for r in subset if cl.parentrevs(r)[1] != -1]
962 return [r for r in subset if cl.parentrevs(r)[1] != -1]
964
963
965 def branchpoint(repo, subset, x):
964 def branchpoint(repo, subset, x):
966 """``branchpoint()``
965 """``branchpoint()``
967 Changesets with more than one child.
966 Changesets with more than one child.
968 """
967 """
969 # i18n: "branchpoint" is a keyword
968 # i18n: "branchpoint" is a keyword
970 getargs(x, 0, 0, _("branchpoint takes no arguments"))
969 getargs(x, 0, 0, _("branchpoint takes no arguments"))
971 cl = repo.changelog
970 cl = repo.changelog
972 if not subset:
971 if not subset:
973 return []
972 return []
974 baserev = min(subset)
973 baserev = min(subset)
975 parentscount = [0]*(len(repo) - baserev)
974 parentscount = [0]*(len(repo) - baserev)
976 for r in cl.revs(start=baserev + 1):
975 for r in cl.revs(start=baserev + 1):
977 for p in cl.parentrevs(r):
976 for p in cl.parentrevs(r):
978 if p >= baserev:
977 if p >= baserev:
979 parentscount[p - baserev] += 1
978 parentscount[p - baserev] += 1
980 return [r for r in subset if (parentscount[r - baserev] > 1)]
979 return [r for r in subset if (parentscount[r - baserev] > 1)]
981
980
982 def minrev(repo, subset, x):
981 def minrev(repo, subset, x):
983 """``min(set)``
982 """``min(set)``
984 Changeset with lowest revision number in set.
983 Changeset with lowest revision number in set.
985 """
984 """
986 os = getset(repo, list(repo), x)
985 os = getset(repo, list(repo), x)
987 if os:
986 if os:
988 m = min(os)
987 m = min(os)
989 if m in subset:
988 if m in subset:
990 return [m]
989 return [m]
991 return []
990 return []
992
991
993 def modifies(repo, subset, x):
992 def modifies(repo, subset, x):
994 """``modifies(pattern)``
993 """``modifies(pattern)``
995 Changesets modifying files matched by pattern.
994 Changesets modifying files matched by pattern.
996 """
995 """
997 # i18n: "modifies" is a keyword
996 # i18n: "modifies" is a keyword
998 pat = getstring(x, _("modifies requires a pattern"))
997 pat = getstring(x, _("modifies requires a pattern"))
999 return checkstatus(repo, subset, pat, 0)
998 return checkstatus(repo, subset, pat, 0)
1000
999
1001 def node_(repo, subset, x):
1000 def node_(repo, subset, x):
1002 """``id(string)``
1001 """``id(string)``
1003 Revision non-ambiguously specified by the given hex string prefix.
1002 Revision non-ambiguously specified by the given hex string prefix.
1004 """
1003 """
1005 # i18n: "id" is a keyword
1004 # i18n: "id" is a keyword
1006 l = getargs(x, 1, 1, _("id requires one argument"))
1005 l = getargs(x, 1, 1, _("id requires one argument"))
1007 # i18n: "id" is a keyword
1006 # i18n: "id" is a keyword
1008 n = getstring(l[0], _("id requires a string"))
1007 n = getstring(l[0], _("id requires a string"))
1009 if len(n) == 40:
1008 if len(n) == 40:
1010 rn = repo[n].rev()
1009 rn = repo[n].rev()
1011 else:
1010 else:
1012 rn = None
1011 rn = None
1013 pm = repo.changelog._partialmatch(n)
1012 pm = repo.changelog._partialmatch(n)
1014 if pm is not None:
1013 if pm is not None:
1015 rn = repo.changelog.rev(pm)
1014 rn = repo.changelog.rev(pm)
1016
1015
1017 return [r for r in subset if r == rn]
1016 return [r for r in subset if r == rn]
1018
1017
1019 def obsolete(repo, subset, x):
1018 def obsolete(repo, subset, x):
1020 """``obsolete()``
1019 """``obsolete()``
1021 Mutable changeset with a newer version."""
1020 Mutable changeset with a newer version."""
1022 # i18n: "obsolete" is a keyword
1021 # i18n: "obsolete" is a keyword
1023 getargs(x, 0, 0, _("obsolete takes no arguments"))
1022 getargs(x, 0, 0, _("obsolete takes no arguments"))
1024 obsoletes = obsmod.getrevs(repo, 'obsolete')
1023 obsoletes = obsmod.getrevs(repo, 'obsolete')
1025 return [r for r in subset if r in obsoletes]
1024 return [r for r in subset if r in obsoletes]
1026
1025
1027 def origin(repo, subset, x):
1026 def origin(repo, subset, x):
1028 """``origin([set])``
1027 """``origin([set])``
1029 Changesets that were specified as a source for the grafts, transplants or
1028 Changesets that were specified as a source for the grafts, transplants or
1030 rebases that created the given revisions. Omitting the optional set is the
1029 rebases that created the given revisions. Omitting the optional set is the
1031 same as passing all(). If a changeset created by these operations is itself
1030 same as passing all(). If a changeset created by these operations is itself
1032 specified as a source for one of these operations, only the source changeset
1031 specified as a source for one of these operations, only the source changeset
1033 for the first operation is selected.
1032 for the first operation is selected.
1034 """
1033 """
1035 if x is not None:
1034 if x is not None:
1036 args = set(getset(repo, list(repo), x))
1035 args = set(getset(repo, list(repo), x))
1037 else:
1036 else:
1038 args = set(getall(repo, list(repo), x))
1037 args = set(getall(repo, list(repo), x))
1039
1038
1040 def _firstsrc(rev):
1039 def _firstsrc(rev):
1041 src = _getrevsource(repo, rev)
1040 src = _getrevsource(repo, rev)
1042 if src is None:
1041 if src is None:
1043 return None
1042 return None
1044
1043
1045 while True:
1044 while True:
1046 prev = _getrevsource(repo, src)
1045 prev = _getrevsource(repo, src)
1047
1046
1048 if prev is None:
1047 if prev is None:
1049 return src
1048 return src
1050 src = prev
1049 src = prev
1051
1050
1052 o = set([_firstsrc(r) for r in args])
1051 o = set([_firstsrc(r) for r in args])
1053 return [r for r in subset if r in o]
1052 return [r for r in subset if r in o]
1054
1053
1055 def outgoing(repo, subset, x):
1054 def outgoing(repo, subset, x):
1056 """``outgoing([path])``
1055 """``outgoing([path])``
1057 Changesets not found in the specified destination repository, or the
1056 Changesets not found in the specified destination repository, or the
1058 default push location.
1057 default push location.
1059 """
1058 """
1060 import hg # avoid start-up nasties
1059 import hg # avoid start-up nasties
1061 # i18n: "outgoing" is a keyword
1060 # i18n: "outgoing" is a keyword
1062 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
1061 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
1063 # i18n: "outgoing" is a keyword
1062 # i18n: "outgoing" is a keyword
1064 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
1063 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
1065 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
1064 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
1066 dest, branches = hg.parseurl(dest)
1065 dest, branches = hg.parseurl(dest)
1067 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1066 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1068 if revs:
1067 if revs:
1069 revs = [repo.lookup(rev) for rev in revs]
1068 revs = [repo.lookup(rev) for rev in revs]
1070 other = hg.peer(repo, {}, dest)
1069 other = hg.peer(repo, {}, dest)
1071 repo.ui.pushbuffer()
1070 repo.ui.pushbuffer()
1072 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
1071 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
1073 repo.ui.popbuffer()
1072 repo.ui.popbuffer()
1074 cl = repo.changelog
1073 cl = repo.changelog
1075 o = set([cl.rev(r) for r in outgoing.missing])
1074 o = set([cl.rev(r) for r in outgoing.missing])
1076 return [r for r in subset if r in o]
1075 return [r for r in subset if r in o]
1077
1076
1078 def p1(repo, subset, x):
1077 def p1(repo, subset, x):
1079 """``p1([set])``
1078 """``p1([set])``
1080 First parent of changesets in set, or the working directory.
1079 First parent of changesets in set, or the working directory.
1081 """
1080 """
1082 if x is None:
1081 if x is None:
1083 p = repo[x].p1().rev()
1082 p = repo[x].p1().rev()
1084 return [r for r in subset if r == p]
1083 return [r for r in subset if r == p]
1085
1084
1086 ps = set()
1085 ps = set()
1087 cl = repo.changelog
1086 cl = repo.changelog
1088 for r in getset(repo, list(repo), x):
1087 for r in getset(repo, list(repo), x):
1089 ps.add(cl.parentrevs(r)[0])
1088 ps.add(cl.parentrevs(r)[0])
1090 return [r for r in subset if r in ps]
1089 return [r for r in subset if r in ps]
1091
1090
1092 def p2(repo, subset, x):
1091 def p2(repo, subset, x):
1093 """``p2([set])``
1092 """``p2([set])``
1094 Second parent of changesets in set, or the working directory.
1093 Second parent of changesets in set, or the working directory.
1095 """
1094 """
1096 if x is None:
1095 if x is None:
1097 ps = repo[x].parents()
1096 ps = repo[x].parents()
1098 try:
1097 try:
1099 p = ps[1].rev()
1098 p = ps[1].rev()
1100 return [r for r in subset if r == p]
1099 return [r for r in subset if r == p]
1101 except IndexError:
1100 except IndexError:
1102 return []
1101 return []
1103
1102
1104 ps = set()
1103 ps = set()
1105 cl = repo.changelog
1104 cl = repo.changelog
1106 for r in getset(repo, list(repo), x):
1105 for r in getset(repo, list(repo), x):
1107 ps.add(cl.parentrevs(r)[1])
1106 ps.add(cl.parentrevs(r)[1])
1108 return [r for r in subset if r in ps]
1107 return [r for r in subset if r in ps]
1109
1108
1110 def parents(repo, subset, x):
1109 def parents(repo, subset, x):
1111 """``parents([set])``
1110 """``parents([set])``
1112 The set of all parents for all changesets in set, or the working directory.
1111 The set of all parents for all changesets in set, or the working directory.
1113 """
1112 """
1114 if x is None:
1113 if x is None:
1115 ps = tuple(p.rev() for p in repo[x].parents())
1114 ps = tuple(p.rev() for p in repo[x].parents())
1116 return [r for r in subset if r in ps]
1115 return [r for r in subset if r in ps]
1117
1116
1118 ps = set()
1117 ps = set()
1119 cl = repo.changelog
1118 cl = repo.changelog
1120 for r in getset(repo, list(repo), x):
1119 for r in getset(repo, list(repo), x):
1121 ps.update(cl.parentrevs(r))
1120 ps.update(cl.parentrevs(r))
1122 return [r for r in subset if r in ps]
1121 return [r for r in subset if r in ps]
1123
1122
1124 def parentspec(repo, subset, x, n):
1123 def parentspec(repo, subset, x, n):
1125 """``set^0``
1124 """``set^0``
1126 The set.
1125 The set.
1127 ``set^1`` (or ``set^``), ``set^2``
1126 ``set^1`` (or ``set^``), ``set^2``
1128 First or second parent, respectively, of all changesets in set.
1127 First or second parent, respectively, of all changesets in set.
1129 """
1128 """
1130 try:
1129 try:
1131 n = int(n[1])
1130 n = int(n[1])
1132 if n not in (0, 1, 2):
1131 if n not in (0, 1, 2):
1133 raise ValueError
1132 raise ValueError
1134 except (TypeError, ValueError):
1133 except (TypeError, ValueError):
1135 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
1134 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
1136 ps = set()
1135 ps = set()
1137 cl = repo.changelog
1136 cl = repo.changelog
1138 for r in getset(repo, cl, x):
1137 for r in getset(repo, cl, x):
1139 if n == 0:
1138 if n == 0:
1140 ps.add(r)
1139 ps.add(r)
1141 elif n == 1:
1140 elif n == 1:
1142 ps.add(cl.parentrevs(r)[0])
1141 ps.add(cl.parentrevs(r)[0])
1143 elif n == 2:
1142 elif n == 2:
1144 parents = cl.parentrevs(r)
1143 parents = cl.parentrevs(r)
1145 if len(parents) > 1:
1144 if len(parents) > 1:
1146 ps.add(parents[1])
1145 ps.add(parents[1])
1147 return [r for r in subset if r in ps]
1146 return [r for r in subset if r in ps]
1148
1147
1149 def present(repo, subset, x):
1148 def present(repo, subset, x):
1150 """``present(set)``
1149 """``present(set)``
1151 An empty set, if any revision in set isn't found; otherwise,
1150 An empty set, if any revision in set isn't found; otherwise,
1152 all revisions in set.
1151 all revisions in set.
1153
1152
1154 If any of specified revisions is not present in the local repository,
1153 If any of specified revisions is not present in the local repository,
1155 the query is normally aborted. But this predicate allows the query
1154 the query is normally aborted. But this predicate allows the query
1156 to continue even in such cases.
1155 to continue even in such cases.
1157 """
1156 """
1158 try:
1157 try:
1159 return getset(repo, subset, x)
1158 return getset(repo, subset, x)
1160 except error.RepoLookupError:
1159 except error.RepoLookupError:
1161 return []
1160 return []
1162
1161
1163 def public(repo, subset, x):
1162 def public(repo, subset, x):
1164 """``public()``
1163 """``public()``
1165 Changeset in public phase."""
1164 Changeset in public phase."""
1166 # i18n: "public" is a keyword
1165 # i18n: "public" is a keyword
1167 getargs(x, 0, 0, _("public takes no arguments"))
1166 getargs(x, 0, 0, _("public takes no arguments"))
1168 pc = repo._phasecache
1167 pc = repo._phasecache
1169 return [r for r in subset if pc.phase(repo, r) == phases.public]
1168 return [r for r in subset if pc.phase(repo, r) == phases.public]
1170
1169
1171 def remote(repo, subset, x):
1170 def remote(repo, subset, x):
1172 """``remote([id [,path]])``
1171 """``remote([id [,path]])``
1173 Local revision that corresponds to the given identifier in a
1172 Local revision that corresponds to the given identifier in a
1174 remote repository, if present. Here, the '.' identifier is a
1173 remote repository, if present. Here, the '.' identifier is a
1175 synonym for the current local branch.
1174 synonym for the current local branch.
1176 """
1175 """
1177
1176
1178 import hg # avoid start-up nasties
1177 import hg # avoid start-up nasties
1179 # i18n: "remote" is a keyword
1178 # i18n: "remote" is a keyword
1180 l = getargs(x, 0, 2, _("remote takes one, two or no arguments"))
1179 l = getargs(x, 0, 2, _("remote takes one, two or no arguments"))
1181
1180
1182 q = '.'
1181 q = '.'
1183 if len(l) > 0:
1182 if len(l) > 0:
1184 # i18n: "remote" is a keyword
1183 # i18n: "remote" is a keyword
1185 q = getstring(l[0], _("remote requires a string id"))
1184 q = getstring(l[0], _("remote requires a string id"))
1186 if q == '.':
1185 if q == '.':
1187 q = repo['.'].branch()
1186 q = repo['.'].branch()
1188
1187
1189 dest = ''
1188 dest = ''
1190 if len(l) > 1:
1189 if len(l) > 1:
1191 # i18n: "remote" is a keyword
1190 # i18n: "remote" is a keyword
1192 dest = getstring(l[1], _("remote requires a repository path"))
1191 dest = getstring(l[1], _("remote requires a repository path"))
1193 dest = repo.ui.expandpath(dest or 'default')
1192 dest = repo.ui.expandpath(dest or 'default')
1194 dest, branches = hg.parseurl(dest)
1193 dest, branches = hg.parseurl(dest)
1195 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1194 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1196 if revs:
1195 if revs:
1197 revs = [repo.lookup(rev) for rev in revs]
1196 revs = [repo.lookup(rev) for rev in revs]
1198 other = hg.peer(repo, {}, dest)
1197 other = hg.peer(repo, {}, dest)
1199 n = other.lookup(q)
1198 n = other.lookup(q)
1200 if n in repo:
1199 if n in repo:
1201 r = repo[n].rev()
1200 r = repo[n].rev()
1202 if r in subset:
1201 if r in subset:
1203 return [r]
1202 return [r]
1204 return []
1203 return []
1205
1204
1206 def removes(repo, subset, x):
1205 def removes(repo, subset, x):
1207 """``removes(pattern)``
1206 """``removes(pattern)``
1208 Changesets which remove files matching pattern.
1207 Changesets which remove files matching pattern.
1209 """
1208 """
1210 # i18n: "removes" is a keyword
1209 # i18n: "removes" is a keyword
1211 pat = getstring(x, _("removes requires a pattern"))
1210 pat = getstring(x, _("removes requires a pattern"))
1212 return checkstatus(repo, subset, pat, 2)
1211 return checkstatus(repo, subset, pat, 2)
1213
1212
1214 def rev(repo, subset, x):
1213 def rev(repo, subset, x):
1215 """``rev(number)``
1214 """``rev(number)``
1216 Revision with the given numeric identifier.
1215 Revision with the given numeric identifier.
1217 """
1216 """
1218 # i18n: "rev" is a keyword
1217 # i18n: "rev" is a keyword
1219 l = getargs(x, 1, 1, _("rev requires one argument"))
1218 l = getargs(x, 1, 1, _("rev requires one argument"))
1220 try:
1219 try:
1221 # i18n: "rev" is a keyword
1220 # i18n: "rev" is a keyword
1222 l = int(getstring(l[0], _("rev requires a number")))
1221 l = int(getstring(l[0], _("rev requires a number")))
1223 except (TypeError, ValueError):
1222 except (TypeError, ValueError):
1224 # i18n: "rev" is a keyword
1223 # i18n: "rev" is a keyword
1225 raise error.ParseError(_("rev expects a number"))
1224 raise error.ParseError(_("rev expects a number"))
1226 return [r for r in subset if r == l]
1225 return [r for r in subset if r == l]
1227
1226
1228 def matching(repo, subset, x):
1227 def matching(repo, subset, x):
1229 """``matching(revision [, field])``
1228 """``matching(revision [, field])``
1230 Changesets in which a given set of fields match the set of fields in the
1229 Changesets in which a given set of fields match the set of fields in the
1231 selected revision or set.
1230 selected revision or set.
1232
1231
1233 To match more than one field pass the list of fields to match separated
1232 To match more than one field pass the list of fields to match separated
1234 by spaces (e.g. ``author description``).
1233 by spaces (e.g. ``author description``).
1235
1234
1236 Valid fields are most regular revision fields and some special fields.
1235 Valid fields are most regular revision fields and some special fields.
1237
1236
1238 Regular revision fields are ``description``, ``author``, ``branch``,
1237 Regular revision fields are ``description``, ``author``, ``branch``,
1239 ``date``, ``files``, ``phase``, ``parents``, ``substate``, ``user``
1238 ``date``, ``files``, ``phase``, ``parents``, ``substate``, ``user``
1240 and ``diff``.
1239 and ``diff``.
1241 Note that ``author`` and ``user`` are synonyms. ``diff`` refers to the
1240 Note that ``author`` and ``user`` are synonyms. ``diff`` refers to the
1242 contents of the revision. Two revisions matching their ``diff`` will
1241 contents of the revision. Two revisions matching their ``diff`` will
1243 also match their ``files``.
1242 also match their ``files``.
1244
1243
1245 Special fields are ``summary`` and ``metadata``:
1244 Special fields are ``summary`` and ``metadata``:
1246 ``summary`` matches the first line of the description.
1245 ``summary`` matches the first line of the description.
1247 ``metadata`` is equivalent to matching ``description user date``
1246 ``metadata`` is equivalent to matching ``description user date``
1248 (i.e. it matches the main metadata fields).
1247 (i.e. it matches the main metadata fields).
1249
1248
1250 ``metadata`` is the default field which is used when no fields are
1249 ``metadata`` is the default field which is used when no fields are
1251 specified. You can match more than one field at a time.
1250 specified. You can match more than one field at a time.
1252 """
1251 """
1253 # i18n: "matching" is a keyword
1252 # i18n: "matching" is a keyword
1254 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1253 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1255
1254
1256 revs = getset(repo, repo.changelog, l[0])
1255 revs = getset(repo, repo.changelog, l[0])
1257
1256
1258 fieldlist = ['metadata']
1257 fieldlist = ['metadata']
1259 if len(l) > 1:
1258 if len(l) > 1:
1260 fieldlist = getstring(l[1],
1259 fieldlist = getstring(l[1],
1261 # i18n: "matching" is a keyword
1260 # i18n: "matching" is a keyword
1262 _("matching requires a string "
1261 _("matching requires a string "
1263 "as its second argument")).split()
1262 "as its second argument")).split()
1264
1263
1265 # Make sure that there are no repeated fields,
1264 # Make sure that there are no repeated fields,
1266 # expand the 'special' 'metadata' field type
1265 # expand the 'special' 'metadata' field type
1267 # and check the 'files' whenever we check the 'diff'
1266 # and check the 'files' whenever we check the 'diff'
1268 fields = []
1267 fields = []
1269 for field in fieldlist:
1268 for field in fieldlist:
1270 if field == 'metadata':
1269 if field == 'metadata':
1271 fields += ['user', 'description', 'date']
1270 fields += ['user', 'description', 'date']
1272 elif field == 'diff':
1271 elif field == 'diff':
1273 # a revision matching the diff must also match the files
1272 # a revision matching the diff must also match the files
1274 # since matching the diff is very costly, make sure to
1273 # since matching the diff is very costly, make sure to
1275 # also match the files first
1274 # also match the files first
1276 fields += ['files', 'diff']
1275 fields += ['files', 'diff']
1277 else:
1276 else:
1278 if field == 'author':
1277 if field == 'author':
1279 field = 'user'
1278 field = 'user'
1280 fields.append(field)
1279 fields.append(field)
1281 fields = set(fields)
1280 fields = set(fields)
1282 if 'summary' in fields and 'description' in fields:
1281 if 'summary' in fields and 'description' in fields:
1283 # If a revision matches its description it also matches its summary
1282 # If a revision matches its description it also matches its summary
1284 fields.discard('summary')
1283 fields.discard('summary')
1285
1284
1286 # We may want to match more than one field
1285 # We may want to match more than one field
1287 # Not all fields take the same amount of time to be matched
1286 # Not all fields take the same amount of time to be matched
1288 # Sort the selected fields in order of increasing matching cost
1287 # Sort the selected fields in order of increasing matching cost
1289 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1288 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1290 'files', 'description', 'substate', 'diff']
1289 'files', 'description', 'substate', 'diff']
1291 def fieldkeyfunc(f):
1290 def fieldkeyfunc(f):
1292 try:
1291 try:
1293 return fieldorder.index(f)
1292 return fieldorder.index(f)
1294 except ValueError:
1293 except ValueError:
1295 # assume an unknown field is very costly
1294 # assume an unknown field is very costly
1296 return len(fieldorder)
1295 return len(fieldorder)
1297 fields = list(fields)
1296 fields = list(fields)
1298 fields.sort(key=fieldkeyfunc)
1297 fields.sort(key=fieldkeyfunc)
1299
1298
1300 # Each field will be matched with its own "getfield" function
1299 # Each field will be matched with its own "getfield" function
1301 # which will be added to the getfieldfuncs array of functions
1300 # which will be added to the getfieldfuncs array of functions
1302 getfieldfuncs = []
1301 getfieldfuncs = []
1303 _funcs = {
1302 _funcs = {
1304 'user': lambda r: repo[r].user(),
1303 'user': lambda r: repo[r].user(),
1305 'branch': lambda r: repo[r].branch(),
1304 'branch': lambda r: repo[r].branch(),
1306 'date': lambda r: repo[r].date(),
1305 'date': lambda r: repo[r].date(),
1307 'description': lambda r: repo[r].description(),
1306 'description': lambda r: repo[r].description(),
1308 'files': lambda r: repo[r].files(),
1307 'files': lambda r: repo[r].files(),
1309 'parents': lambda r: repo[r].parents(),
1308 'parents': lambda r: repo[r].parents(),
1310 'phase': lambda r: repo[r].phase(),
1309 'phase': lambda r: repo[r].phase(),
1311 'substate': lambda r: repo[r].substate,
1310 'substate': lambda r: repo[r].substate,
1312 'summary': lambda r: repo[r].description().splitlines()[0],
1311 'summary': lambda r: repo[r].description().splitlines()[0],
1313 'diff': lambda r: list(repo[r].diff(git=True),)
1312 'diff': lambda r: list(repo[r].diff(git=True),)
1314 }
1313 }
1315 for info in fields:
1314 for info in fields:
1316 getfield = _funcs.get(info, None)
1315 getfield = _funcs.get(info, None)
1317 if getfield is None:
1316 if getfield is None:
1318 raise error.ParseError(
1317 raise error.ParseError(
1319 # i18n: "matching" is a keyword
1318 # i18n: "matching" is a keyword
1320 _("unexpected field name passed to matching: %s") % info)
1319 _("unexpected field name passed to matching: %s") % info)
1321 getfieldfuncs.append(getfield)
1320 getfieldfuncs.append(getfield)
1322 # convert the getfield array of functions into a "getinfo" function
1321 # convert the getfield array of functions into a "getinfo" function
1323 # which returns an array of field values (or a single value if there
1322 # which returns an array of field values (or a single value if there
1324 # is only one field to match)
1323 # is only one field to match)
1325 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1324 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1326
1325
1327 matches = set()
1326 matches = set()
1328 for rev in revs:
1327 for rev in revs:
1329 target = getinfo(rev)
1328 target = getinfo(rev)
1330 for r in subset:
1329 for r in subset:
1331 match = True
1330 match = True
1332 for n, f in enumerate(getfieldfuncs):
1331 for n, f in enumerate(getfieldfuncs):
1333 if target[n] != f(r):
1332 if target[n] != f(r):
1334 match = False
1333 match = False
1335 break
1334 break
1336 if match:
1335 if match:
1337 matches.add(r)
1336 matches.add(r)
1338 return [r for r in subset if r in matches]
1337 return [r for r in subset if r in matches]
1339
1338
1340 def reverse(repo, subset, x):
1339 def reverse(repo, subset, x):
1341 """``reverse(set)``
1340 """``reverse(set)``
1342 Reverse order of set.
1341 Reverse order of set.
1343 """
1342 """
1344 l = getset(repo, subset, x)
1343 l = getset(repo, subset, x)
1345 if not isinstance(l, list):
1344 if not isinstance(l, list):
1346 l = list(l)
1345 l = list(l)
1347 l.reverse()
1346 l.reverse()
1348 return l
1347 return l
1349
1348
1350 def roots(repo, subset, x):
1349 def roots(repo, subset, x):
1351 """``roots(set)``
1350 """``roots(set)``
1352 Changesets in set with no parent changeset in set.
1351 Changesets in set with no parent changeset in set.
1353 """
1352 """
1354 s = set(getset(repo, repo.changelog, x))
1353 s = set(getset(repo, repo.changelog, x))
1355 subset = [r for r in subset if r in s]
1354 subset = [r for r in subset if r in s]
1356 cs = _children(repo, subset, s)
1355 cs = _children(repo, subset, s)
1357 return [r for r in subset if r not in cs]
1356 return [r for r in subset if r not in cs]
1358
1357
1359 def secret(repo, subset, x):
1358 def secret(repo, subset, x):
1360 """``secret()``
1359 """``secret()``
1361 Changeset in secret phase."""
1360 Changeset in secret phase."""
1362 # i18n: "secret" is a keyword
1361 # i18n: "secret" is a keyword
1363 getargs(x, 0, 0, _("secret takes no arguments"))
1362 getargs(x, 0, 0, _("secret takes no arguments"))
1364 pc = repo._phasecache
1363 pc = repo._phasecache
1365 return [r for r in subset if pc.phase(repo, r) == phases.secret]
1364 return [r for r in subset if pc.phase(repo, r) == phases.secret]
1366
1365
1367 def sort(repo, subset, x):
1366 def sort(repo, subset, x):
1368 """``sort(set[, [-]key...])``
1367 """``sort(set[, [-]key...])``
1369 Sort set by keys. The default sort order is ascending, specify a key
1368 Sort set by keys. The default sort order is ascending, specify a key
1370 as ``-key`` to sort in descending order.
1369 as ``-key`` to sort in descending order.
1371
1370
1372 The keys can be:
1371 The keys can be:
1373
1372
1374 - ``rev`` for the revision number,
1373 - ``rev`` for the revision number,
1375 - ``branch`` for the branch name,
1374 - ``branch`` for the branch name,
1376 - ``desc`` for the commit message (description),
1375 - ``desc`` for the commit message (description),
1377 - ``user`` for user name (``author`` can be used as an alias),
1376 - ``user`` for user name (``author`` can be used as an alias),
1378 - ``date`` for the commit date
1377 - ``date`` for the commit date
1379 """
1378 """
1380 # i18n: "sort" is a keyword
1379 # i18n: "sort" is a keyword
1381 l = getargs(x, 1, 2, _("sort requires one or two arguments"))
1380 l = getargs(x, 1, 2, _("sort requires one or two arguments"))
1382 keys = "rev"
1381 keys = "rev"
1383 if len(l) == 2:
1382 if len(l) == 2:
1384 # i18n: "sort" is a keyword
1383 # i18n: "sort" is a keyword
1385 keys = getstring(l[1], _("sort spec must be a string"))
1384 keys = getstring(l[1], _("sort spec must be a string"))
1386
1385
1387 s = l[0]
1386 s = l[0]
1388 keys = keys.split()
1387 keys = keys.split()
1389 l = []
1388 l = []
1390 def invert(s):
1389 def invert(s):
1391 return "".join(chr(255 - ord(c)) for c in s)
1390 return "".join(chr(255 - ord(c)) for c in s)
1392 for r in getset(repo, subset, s):
1391 for r in getset(repo, subset, s):
1393 c = repo[r]
1392 c = repo[r]
1394 e = []
1393 e = []
1395 for k in keys:
1394 for k in keys:
1396 if k == 'rev':
1395 if k == 'rev':
1397 e.append(r)
1396 e.append(r)
1398 elif k == '-rev':
1397 elif k == '-rev':
1399 e.append(-r)
1398 e.append(-r)
1400 elif k == 'branch':
1399 elif k == 'branch':
1401 e.append(c.branch())
1400 e.append(c.branch())
1402 elif k == '-branch':
1401 elif k == '-branch':
1403 e.append(invert(c.branch()))
1402 e.append(invert(c.branch()))
1404 elif k == 'desc':
1403 elif k == 'desc':
1405 e.append(c.description())
1404 e.append(c.description())
1406 elif k == '-desc':
1405 elif k == '-desc':
1407 e.append(invert(c.description()))
1406 e.append(invert(c.description()))
1408 elif k in 'user author':
1407 elif k in 'user author':
1409 e.append(c.user())
1408 e.append(c.user())
1410 elif k in '-user -author':
1409 elif k in '-user -author':
1411 e.append(invert(c.user()))
1410 e.append(invert(c.user()))
1412 elif k == 'date':
1411 elif k == 'date':
1413 e.append(c.date()[0])
1412 e.append(c.date()[0])
1414 elif k == '-date':
1413 elif k == '-date':
1415 e.append(-c.date()[0])
1414 e.append(-c.date()[0])
1416 else:
1415 else:
1417 raise error.ParseError(_("unknown sort key %r") % k)
1416 raise error.ParseError(_("unknown sort key %r") % k)
1418 e.append(r)
1417 e.append(r)
1419 l.append(e)
1418 l.append(e)
1420 l.sort()
1419 l.sort()
1421 return [e[-1] for e in l]
1420 return [e[-1] for e in l]
1422
1421
1423 def _stringmatcher(pattern):
1422 def _stringmatcher(pattern):
1424 """
1423 """
1425 accepts a string, possibly starting with 're:' or 'literal:' prefix.
1424 accepts a string, possibly starting with 're:' or 'literal:' prefix.
1426 returns the matcher name, pattern, and matcher function.
1425 returns the matcher name, pattern, and matcher function.
1427 missing or unknown prefixes are treated as literal matches.
1426 missing or unknown prefixes are treated as literal matches.
1428
1427
1429 helper for tests:
1428 helper for tests:
1430 >>> def test(pattern, *tests):
1429 >>> def test(pattern, *tests):
1431 ... kind, pattern, matcher = _stringmatcher(pattern)
1430 ... kind, pattern, matcher = _stringmatcher(pattern)
1432 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
1431 ... return (kind, pattern, [bool(matcher(t)) for t in tests])
1433
1432
1434 exact matching (no prefix):
1433 exact matching (no prefix):
1435 >>> test('abcdefg', 'abc', 'def', 'abcdefg')
1434 >>> test('abcdefg', 'abc', 'def', 'abcdefg')
1436 ('literal', 'abcdefg', [False, False, True])
1435 ('literal', 'abcdefg', [False, False, True])
1437
1436
1438 regex matching ('re:' prefix)
1437 regex matching ('re:' prefix)
1439 >>> test('re:a.+b', 'nomatch', 'fooadef', 'fooadefbar')
1438 >>> test('re:a.+b', 'nomatch', 'fooadef', 'fooadefbar')
1440 ('re', 'a.+b', [False, False, True])
1439 ('re', 'a.+b', [False, False, True])
1441
1440
1442 force exact matches ('literal:' prefix)
1441 force exact matches ('literal:' prefix)
1443 >>> test('literal:re:foobar', 'foobar', 're:foobar')
1442 >>> test('literal:re:foobar', 'foobar', 're:foobar')
1444 ('literal', 're:foobar', [False, True])
1443 ('literal', 're:foobar', [False, True])
1445
1444
1446 unknown prefixes are ignored and treated as literals
1445 unknown prefixes are ignored and treated as literals
1447 >>> test('foo:bar', 'foo', 'bar', 'foo:bar')
1446 >>> test('foo:bar', 'foo', 'bar', 'foo:bar')
1448 ('literal', 'foo:bar', [False, False, True])
1447 ('literal', 'foo:bar', [False, False, True])
1449 """
1448 """
1450 if pattern.startswith('re:'):
1449 if pattern.startswith('re:'):
1451 pattern = pattern[3:]
1450 pattern = pattern[3:]
1452 try:
1451 try:
1453 regex = re.compile(pattern)
1452 regex = re.compile(pattern)
1454 except re.error, e:
1453 except re.error, e:
1455 raise error.ParseError(_('invalid regular expression: %s')
1454 raise error.ParseError(_('invalid regular expression: %s')
1456 % e)
1455 % e)
1457 return 're', pattern, regex.search
1456 return 're', pattern, regex.search
1458 elif pattern.startswith('literal:'):
1457 elif pattern.startswith('literal:'):
1459 pattern = pattern[8:]
1458 pattern = pattern[8:]
1460 return 'literal', pattern, pattern.__eq__
1459 return 'literal', pattern, pattern.__eq__
1461
1460
1462 def _substringmatcher(pattern):
1461 def _substringmatcher(pattern):
1463 kind, pattern, matcher = _stringmatcher(pattern)
1462 kind, pattern, matcher = _stringmatcher(pattern)
1464 if kind == 'literal':
1463 if kind == 'literal':
1465 matcher = lambda s: pattern in s
1464 matcher = lambda s: pattern in s
1466 return kind, pattern, matcher
1465 return kind, pattern, matcher
1467
1466
1468 def tag(repo, subset, x):
1467 def tag(repo, subset, x):
1469 """``tag([name])``
1468 """``tag([name])``
1470 The specified tag by name, or all tagged revisions if no name is given.
1469 The specified tag by name, or all tagged revisions if no name is given.
1471 """
1470 """
1472 # i18n: "tag" is a keyword
1471 # i18n: "tag" is a keyword
1473 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
1472 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
1474 cl = repo.changelog
1473 cl = repo.changelog
1475 if args:
1474 if args:
1476 pattern = getstring(args[0],
1475 pattern = getstring(args[0],
1477 # i18n: "tag" is a keyword
1476 # i18n: "tag" is a keyword
1478 _('the argument to tag must be a string'))
1477 _('the argument to tag must be a string'))
1479 kind, pattern, matcher = _stringmatcher(pattern)
1478 kind, pattern, matcher = _stringmatcher(pattern)
1480 if kind == 'literal':
1479 if kind == 'literal':
1481 # avoid resolving all tags
1480 # avoid resolving all tags
1482 tn = repo._tagscache.tags.get(pattern, None)
1481 tn = repo._tagscache.tags.get(pattern, None)
1483 if tn is None:
1482 if tn is None:
1484 raise util.Abort(_("tag '%s' does not exist") % pattern)
1483 raise util.Abort(_("tag '%s' does not exist") % pattern)
1485 s = set([repo[tn].rev()])
1484 s = set([repo[tn].rev()])
1486 else:
1485 else:
1487 s = set([cl.rev(n) for t, n in repo.tagslist() if matcher(t)])
1486 s = set([cl.rev(n) for t, n in repo.tagslist() if matcher(t)])
1488 if not s:
1487 if not s:
1489 raise util.Abort(_("no tags exist that match '%s'") % pattern)
1488 raise util.Abort(_("no tags exist that match '%s'") % pattern)
1490 else:
1489 else:
1491 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
1490 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
1492 return [r for r in subset if r in s]
1491 return [r for r in subset if r in s]
1493
1492
1494 def tagged(repo, subset, x):
1493 def tagged(repo, subset, x):
1495 return tag(repo, subset, x)
1494 return tag(repo, subset, x)
1496
1495
1497 def unstable(repo, subset, x):
1496 def unstable(repo, subset, x):
1498 """``unstable()``
1497 """``unstable()``
1499 Non-obsolete changesets with obsolete ancestors.
1498 Non-obsolete changesets with obsolete ancestors.
1500 """
1499 """
1501 # i18n: "unstable" is a keyword
1500 # i18n: "unstable" is a keyword
1502 getargs(x, 0, 0, _("unstable takes no arguments"))
1501 getargs(x, 0, 0, _("unstable takes no arguments"))
1503 unstables = obsmod.getrevs(repo, 'unstable')
1502 unstables = obsmod.getrevs(repo, 'unstable')
1504 return [r for r in subset if r in unstables]
1503 return [r for r in subset if r in unstables]
1505
1504
1506
1505
1507 def user(repo, subset, x):
1506 def user(repo, subset, x):
1508 """``user(string)``
1507 """``user(string)``
1509 User name contains string. The match is case-insensitive.
1508 User name contains string. The match is case-insensitive.
1510
1509
1511 If `string` starts with `re:`, the remainder of the string is treated as
1510 If `string` starts with `re:`, the remainder of the string is treated as
1512 a regular expression. To match a user that actually contains `re:`, use
1511 a regular expression. To match a user that actually contains `re:`, use
1513 the prefix `literal:`.
1512 the prefix `literal:`.
1514 """
1513 """
1515 return author(repo, subset, x)
1514 return author(repo, subset, x)
1516
1515
1517 # for internal use
1516 # for internal use
1518 def _list(repo, subset, x):
1517 def _list(repo, subset, x):
1519 s = getstring(x, "internal error")
1518 s = getstring(x, "internal error")
1520 if not s:
1519 if not s:
1521 return []
1520 return []
1522 if not isinstance(subset, set):
1521 if not isinstance(subset, set):
1523 subset = set(subset)
1522 subset = set(subset)
1524 ls = [repo[r].rev() for r in s.split('\0')]
1523 ls = [repo[r].rev() for r in s.split('\0')]
1525 return [r for r in ls if r in subset]
1524 return [r for r in ls if r in subset]
1526
1525
1527 symbols = {
1526 symbols = {
1528 "adds": adds,
1527 "adds": adds,
1529 "all": getall,
1528 "all": getall,
1530 "ancestor": ancestor,
1529 "ancestor": ancestor,
1531 "ancestors": ancestors,
1530 "ancestors": ancestors,
1532 "_firstancestors": _firstancestors,
1531 "_firstancestors": _firstancestors,
1533 "author": author,
1532 "author": author,
1534 "bisect": bisect,
1533 "bisect": bisect,
1535 "bisected": bisected,
1534 "bisected": bisected,
1536 "bookmark": bookmark,
1535 "bookmark": bookmark,
1537 "branch": branch,
1536 "branch": branch,
1538 "branchpoint": branchpoint,
1537 "branchpoint": branchpoint,
1539 "bumped": bumped,
1538 "bumped": bumped,
1540 "bundle": bundle,
1539 "bundle": bundle,
1541 "children": children,
1540 "children": children,
1542 "closed": closed,
1541 "closed": closed,
1543 "contains": contains,
1542 "contains": contains,
1544 "converted": converted,
1543 "converted": converted,
1545 "date": date,
1544 "date": date,
1546 "desc": desc,
1545 "desc": desc,
1547 "descendants": descendants,
1546 "descendants": descendants,
1548 "_firstdescendants": _firstdescendants,
1547 "_firstdescendants": _firstdescendants,
1549 "destination": destination,
1548 "destination": destination,
1550 "divergent": divergent,
1549 "divergent": divergent,
1551 "draft": draft,
1550 "draft": draft,
1552 "extinct": extinct,
1551 "extinct": extinct,
1553 "extra": extra,
1552 "extra": extra,
1554 "file": hasfile,
1553 "file": hasfile,
1555 "filelog": filelog,
1554 "filelog": filelog,
1556 "first": first,
1555 "first": first,
1557 "follow": follow,
1556 "follow": follow,
1558 "_followfirst": _followfirst,
1557 "_followfirst": _followfirst,
1559 "grep": grep,
1558 "grep": grep,
1560 "head": head,
1559 "head": head,
1561 "heads": heads,
1560 "heads": heads,
1562 "hidden": hidden,
1561 "hidden": hidden,
1563 "id": node_,
1562 "id": node_,
1564 "keyword": keyword,
1563 "keyword": keyword,
1565 "last": last,
1564 "last": last,
1566 "limit": limit,
1565 "limit": limit,
1567 "_matchfiles": _matchfiles,
1566 "_matchfiles": _matchfiles,
1568 "max": maxrev,
1567 "max": maxrev,
1569 "merge": merge,
1568 "merge": merge,
1570 "min": minrev,
1569 "min": minrev,
1571 "modifies": modifies,
1570 "modifies": modifies,
1572 "obsolete": obsolete,
1571 "obsolete": obsolete,
1573 "origin": origin,
1572 "origin": origin,
1574 "outgoing": outgoing,
1573 "outgoing": outgoing,
1575 "p1": p1,
1574 "p1": p1,
1576 "p2": p2,
1575 "p2": p2,
1577 "parents": parents,
1576 "parents": parents,
1578 "present": present,
1577 "present": present,
1579 "public": public,
1578 "public": public,
1580 "remote": remote,
1579 "remote": remote,
1581 "removes": removes,
1580 "removes": removes,
1582 "rev": rev,
1581 "rev": rev,
1583 "reverse": reverse,
1582 "reverse": reverse,
1584 "roots": roots,
1583 "roots": roots,
1585 "sort": sort,
1584 "sort": sort,
1586 "secret": secret,
1585 "secret": secret,
1587 "matching": matching,
1586 "matching": matching,
1588 "tag": tag,
1587 "tag": tag,
1589 "tagged": tagged,
1588 "tagged": tagged,
1590 "user": user,
1589 "user": user,
1591 "unstable": unstable,
1590 "unstable": unstable,
1592 "_list": _list,
1591 "_list": _list,
1593 }
1592 }
1594
1593
1595 methods = {
1594 methods = {
1596 "range": rangeset,
1595 "range": rangeset,
1597 "dagrange": dagrange,
1596 "dagrange": dagrange,
1598 "string": stringset,
1597 "string": stringset,
1599 "symbol": symbolset,
1598 "symbol": symbolset,
1600 "and": andset,
1599 "and": andset,
1601 "or": orset,
1600 "or": orset,
1602 "not": notset,
1601 "not": notset,
1603 "list": listset,
1602 "list": listset,
1604 "func": func,
1603 "func": func,
1605 "ancestor": ancestorspec,
1604 "ancestor": ancestorspec,
1606 "parent": parentspec,
1605 "parent": parentspec,
1607 "parentpost": p1,
1606 "parentpost": p1,
1608 }
1607 }
1609
1608
1610 def optimize(x, small):
1609 def optimize(x, small):
1611 if x is None:
1610 if x is None:
1612 return 0, x
1611 return 0, x
1613
1612
1614 smallbonus = 1
1613 smallbonus = 1
1615 if small:
1614 if small:
1616 smallbonus = .5
1615 smallbonus = .5
1617
1616
1618 op = x[0]
1617 op = x[0]
1619 if op == 'minus':
1618 if op == 'minus':
1620 return optimize(('and', x[1], ('not', x[2])), small)
1619 return optimize(('and', x[1], ('not', x[2])), small)
1621 elif op == 'dagrangepre':
1620 elif op == 'dagrangepre':
1622 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
1621 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
1623 elif op == 'dagrangepost':
1622 elif op == 'dagrangepost':
1624 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
1623 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
1625 elif op == 'rangepre':
1624 elif op == 'rangepre':
1626 return optimize(('range', ('string', '0'), x[1]), small)
1625 return optimize(('range', ('string', '0'), x[1]), small)
1627 elif op == 'rangepost':
1626 elif op == 'rangepost':
1628 return optimize(('range', x[1], ('string', 'tip')), small)
1627 return optimize(('range', x[1], ('string', 'tip')), small)
1629 elif op == 'negate':
1628 elif op == 'negate':
1630 return optimize(('string',
1629 return optimize(('string',
1631 '-' + getstring(x[1], _("can't negate that"))), small)
1630 '-' + getstring(x[1], _("can't negate that"))), small)
1632 elif op in 'string symbol negate':
1631 elif op in 'string symbol negate':
1633 return smallbonus, x # single revisions are small
1632 return smallbonus, x # single revisions are small
1634 elif op == 'and':
1633 elif op == 'and':
1635 wa, ta = optimize(x[1], True)
1634 wa, ta = optimize(x[1], True)
1636 wb, tb = optimize(x[2], True)
1635 wb, tb = optimize(x[2], True)
1637 w = min(wa, wb)
1636 w = min(wa, wb)
1638 if wa > wb:
1637 if wa > wb:
1639 return w, (op, tb, ta)
1638 return w, (op, tb, ta)
1640 return w, (op, ta, tb)
1639 return w, (op, ta, tb)
1641 elif op == 'or':
1640 elif op == 'or':
1642 wa, ta = optimize(x[1], False)
1641 wa, ta = optimize(x[1], False)
1643 wb, tb = optimize(x[2], False)
1642 wb, tb = optimize(x[2], False)
1644 if wb < wa:
1643 if wb < wa:
1645 wb, wa = wa, wb
1644 wb, wa = wa, wb
1646 return max(wa, wb), (op, ta, tb)
1645 return max(wa, wb), (op, ta, tb)
1647 elif op == 'not':
1646 elif op == 'not':
1648 o = optimize(x[1], not small)
1647 o = optimize(x[1], not small)
1649 return o[0], (op, o[1])
1648 return o[0], (op, o[1])
1650 elif op == 'parentpost':
1649 elif op == 'parentpost':
1651 o = optimize(x[1], small)
1650 o = optimize(x[1], small)
1652 return o[0], (op, o[1])
1651 return o[0], (op, o[1])
1653 elif op == 'group':
1652 elif op == 'group':
1654 return optimize(x[1], small)
1653 return optimize(x[1], small)
1655 elif op in 'dagrange range list parent ancestorspec':
1654 elif op in 'dagrange range list parent ancestorspec':
1656 if op == 'parent':
1655 if op == 'parent':
1657 # x^:y means (x^) : y, not x ^ (:y)
1656 # x^:y means (x^) : y, not x ^ (:y)
1658 post = ('parentpost', x[1])
1657 post = ('parentpost', x[1])
1659 if x[2][0] == 'dagrangepre':
1658 if x[2][0] == 'dagrangepre':
1660 return optimize(('dagrange', post, x[2][1]), small)
1659 return optimize(('dagrange', post, x[2][1]), small)
1661 elif x[2][0] == 'rangepre':
1660 elif x[2][0] == 'rangepre':
1662 return optimize(('range', post, x[2][1]), small)
1661 return optimize(('range', post, x[2][1]), small)
1663
1662
1664 wa, ta = optimize(x[1], small)
1663 wa, ta = optimize(x[1], small)
1665 wb, tb = optimize(x[2], small)
1664 wb, tb = optimize(x[2], small)
1666 return wa + wb, (op, ta, tb)
1665 return wa + wb, (op, ta, tb)
1667 elif op == 'func':
1666 elif op == 'func':
1668 f = getstring(x[1], _("not a symbol"))
1667 f = getstring(x[1], _("not a symbol"))
1669 wa, ta = optimize(x[2], small)
1668 wa, ta = optimize(x[2], small)
1670 if f in ("author branch closed date desc file grep keyword "
1669 if f in ("author branch closed date desc file grep keyword "
1671 "outgoing user"):
1670 "outgoing user"):
1672 w = 10 # slow
1671 w = 10 # slow
1673 elif f in "modifies adds removes":
1672 elif f in "modifies adds removes":
1674 w = 30 # slower
1673 w = 30 # slower
1675 elif f == "contains":
1674 elif f == "contains":
1676 w = 100 # very slow
1675 w = 100 # very slow
1677 elif f == "ancestor":
1676 elif f == "ancestor":
1678 w = 1 * smallbonus
1677 w = 1 * smallbonus
1679 elif f in "reverse limit first":
1678 elif f in "reverse limit first":
1680 w = 0
1679 w = 0
1681 elif f in "sort":
1680 elif f in "sort":
1682 w = 10 # assume most sorts look at changelog
1681 w = 10 # assume most sorts look at changelog
1683 else:
1682 else:
1684 w = 1
1683 w = 1
1685 return w + wa, (op, x[1], ta)
1684 return w + wa, (op, x[1], ta)
1686 return 1, x
1685 return 1, x
1687
1686
1688 _aliasarg = ('func', ('symbol', '_aliasarg'))
1687 _aliasarg = ('func', ('symbol', '_aliasarg'))
1689 def _getaliasarg(tree):
1688 def _getaliasarg(tree):
1690 """If tree matches ('func', ('symbol', '_aliasarg'), ('string', X))
1689 """If tree matches ('func', ('symbol', '_aliasarg'), ('string', X))
1691 return X, None otherwise.
1690 return X, None otherwise.
1692 """
1691 """
1693 if (len(tree) == 3 and tree[:2] == _aliasarg
1692 if (len(tree) == 3 and tree[:2] == _aliasarg
1694 and tree[2][0] == 'string'):
1693 and tree[2][0] == 'string'):
1695 return tree[2][1]
1694 return tree[2][1]
1696 return None
1695 return None
1697
1696
1698 def _checkaliasarg(tree, known=None):
1697 def _checkaliasarg(tree, known=None):
1699 """Check tree contains no _aliasarg construct or only ones which
1698 """Check tree contains no _aliasarg construct or only ones which
1700 value is in known. Used to avoid alias placeholders injection.
1699 value is in known. Used to avoid alias placeholders injection.
1701 """
1700 """
1702 if isinstance(tree, tuple):
1701 if isinstance(tree, tuple):
1703 arg = _getaliasarg(tree)
1702 arg = _getaliasarg(tree)
1704 if arg is not None and (not known or arg not in known):
1703 if arg is not None and (not known or arg not in known):
1705 raise error.ParseError(_("not a function: %s") % '_aliasarg')
1704 raise error.ParseError(_("not a function: %s") % '_aliasarg')
1706 for t in tree:
1705 for t in tree:
1707 _checkaliasarg(t, known)
1706 _checkaliasarg(t, known)
1708
1707
1709 class revsetalias(object):
1708 class revsetalias(object):
1710 funcre = re.compile('^([^(]+)\(([^)]+)\)$')
1709 funcre = re.compile('^([^(]+)\(([^)]+)\)$')
1711 args = None
1710 args = None
1712
1711
1713 def __init__(self, name, value):
1712 def __init__(self, name, value):
1714 '''Aliases like:
1713 '''Aliases like:
1715
1714
1716 h = heads(default)
1715 h = heads(default)
1717 b($1) = ancestors($1) - ancestors(default)
1716 b($1) = ancestors($1) - ancestors(default)
1718 '''
1717 '''
1719 m = self.funcre.search(name)
1718 m = self.funcre.search(name)
1720 if m:
1719 if m:
1721 self.name = m.group(1)
1720 self.name = m.group(1)
1722 self.tree = ('func', ('symbol', m.group(1)))
1721 self.tree = ('func', ('symbol', m.group(1)))
1723 self.args = [x.strip() for x in m.group(2).split(',')]
1722 self.args = [x.strip() for x in m.group(2).split(',')]
1724 for arg in self.args:
1723 for arg in self.args:
1725 # _aliasarg() is an unknown symbol only used separate
1724 # _aliasarg() is an unknown symbol only used separate
1726 # alias argument placeholders from regular strings.
1725 # alias argument placeholders from regular strings.
1727 value = value.replace(arg, '_aliasarg(%r)' % (arg,))
1726 value = value.replace(arg, '_aliasarg(%r)' % (arg,))
1728 else:
1727 else:
1729 self.name = name
1728 self.name = name
1730 self.tree = ('symbol', name)
1729 self.tree = ('symbol', name)
1731
1730
1732 self.replacement, pos = parse(value)
1731 self.replacement, pos = parse(value)
1733 if pos != len(value):
1732 if pos != len(value):
1734 raise error.ParseError(_('invalid token'), pos)
1733 raise error.ParseError(_('invalid token'), pos)
1735 # Check for placeholder injection
1734 # Check for placeholder injection
1736 _checkaliasarg(self.replacement, self.args)
1735 _checkaliasarg(self.replacement, self.args)
1737
1736
1738 def _getalias(aliases, tree):
1737 def _getalias(aliases, tree):
1739 """If tree looks like an unexpanded alias, return it. Return None
1738 """If tree looks like an unexpanded alias, return it. Return None
1740 otherwise.
1739 otherwise.
1741 """
1740 """
1742 if isinstance(tree, tuple) and tree:
1741 if isinstance(tree, tuple) and tree:
1743 if tree[0] == 'symbol' and len(tree) == 2:
1742 if tree[0] == 'symbol' and len(tree) == 2:
1744 name = tree[1]
1743 name = tree[1]
1745 alias = aliases.get(name)
1744 alias = aliases.get(name)
1746 if alias and alias.args is None and alias.tree == tree:
1745 if alias and alias.args is None and alias.tree == tree:
1747 return alias
1746 return alias
1748 if tree[0] == 'func' and len(tree) > 1:
1747 if tree[0] == 'func' and len(tree) > 1:
1749 if tree[1][0] == 'symbol' and len(tree[1]) == 2:
1748 if tree[1][0] == 'symbol' and len(tree[1]) == 2:
1750 name = tree[1][1]
1749 name = tree[1][1]
1751 alias = aliases.get(name)
1750 alias = aliases.get(name)
1752 if alias and alias.args is not None and alias.tree == tree[:2]:
1751 if alias and alias.args is not None and alias.tree == tree[:2]:
1753 return alias
1752 return alias
1754 return None
1753 return None
1755
1754
1756 def _expandargs(tree, args):
1755 def _expandargs(tree, args):
1757 """Replace _aliasarg instances with the substitution value of the
1756 """Replace _aliasarg instances with the substitution value of the
1758 same name in args, recursively.
1757 same name in args, recursively.
1759 """
1758 """
1760 if not tree or not isinstance(tree, tuple):
1759 if not tree or not isinstance(tree, tuple):
1761 return tree
1760 return tree
1762 arg = _getaliasarg(tree)
1761 arg = _getaliasarg(tree)
1763 if arg is not None:
1762 if arg is not None:
1764 return args[arg]
1763 return args[arg]
1765 return tuple(_expandargs(t, args) for t in tree)
1764 return tuple(_expandargs(t, args) for t in tree)
1766
1765
1767 def _expandaliases(aliases, tree, expanding, cache):
1766 def _expandaliases(aliases, tree, expanding, cache):
1768 """Expand aliases in tree, recursively.
1767 """Expand aliases in tree, recursively.
1769
1768
1770 'aliases' is a dictionary mapping user defined aliases to
1769 'aliases' is a dictionary mapping user defined aliases to
1771 revsetalias objects.
1770 revsetalias objects.
1772 """
1771 """
1773 if not isinstance(tree, tuple):
1772 if not isinstance(tree, tuple):
1774 # Do not expand raw strings
1773 # Do not expand raw strings
1775 return tree
1774 return tree
1776 alias = _getalias(aliases, tree)
1775 alias = _getalias(aliases, tree)
1777 if alias is not None:
1776 if alias is not None:
1778 if alias in expanding:
1777 if alias in expanding:
1779 raise error.ParseError(_('infinite expansion of revset alias "%s" '
1778 raise error.ParseError(_('infinite expansion of revset alias "%s" '
1780 'detected') % alias.name)
1779 'detected') % alias.name)
1781 expanding.append(alias)
1780 expanding.append(alias)
1782 if alias.name not in cache:
1781 if alias.name not in cache:
1783 cache[alias.name] = _expandaliases(aliases, alias.replacement,
1782 cache[alias.name] = _expandaliases(aliases, alias.replacement,
1784 expanding, cache)
1783 expanding, cache)
1785 result = cache[alias.name]
1784 result = cache[alias.name]
1786 expanding.pop()
1785 expanding.pop()
1787 if alias.args is not None:
1786 if alias.args is not None:
1788 l = getlist(tree[2])
1787 l = getlist(tree[2])
1789 if len(l) != len(alias.args):
1788 if len(l) != len(alias.args):
1790 raise error.ParseError(
1789 raise error.ParseError(
1791 _('invalid number of arguments: %s') % len(l))
1790 _('invalid number of arguments: %s') % len(l))
1792 l = [_expandaliases(aliases, a, [], cache) for a in l]
1791 l = [_expandaliases(aliases, a, [], cache) for a in l]
1793 result = _expandargs(result, dict(zip(alias.args, l)))
1792 result = _expandargs(result, dict(zip(alias.args, l)))
1794 else:
1793 else:
1795 result = tuple(_expandaliases(aliases, t, expanding, cache)
1794 result = tuple(_expandaliases(aliases, t, expanding, cache)
1796 for t in tree)
1795 for t in tree)
1797 return result
1796 return result
1798
1797
1799 def findaliases(ui, tree):
1798 def findaliases(ui, tree):
1800 _checkaliasarg(tree)
1799 _checkaliasarg(tree)
1801 aliases = {}
1800 aliases = {}
1802 for k, v in ui.configitems('revsetalias'):
1801 for k, v in ui.configitems('revsetalias'):
1803 alias = revsetalias(k, v)
1802 alias = revsetalias(k, v)
1804 aliases[alias.name] = alias
1803 aliases[alias.name] = alias
1805 return _expandaliases(aliases, tree, [], {})
1804 return _expandaliases(aliases, tree, [], {})
1806
1805
1807 parse = parser.parser(tokenize, elements).parse
1806 parse = parser.parser(tokenize, elements).parse
1808
1807
1809 def match(ui, spec):
1808 def match(ui, spec):
1810 if not spec:
1809 if not spec:
1811 raise error.ParseError(_("empty query"))
1810 raise error.ParseError(_("empty query"))
1812 tree, pos = parse(spec)
1811 tree, pos = parse(spec)
1813 if (pos != len(spec)):
1812 if (pos != len(spec)):
1814 raise error.ParseError(_("invalid token"), pos)
1813 raise error.ParseError(_("invalid token"), pos)
1815 if ui:
1814 if ui:
1816 tree = findaliases(ui, tree)
1815 tree = findaliases(ui, tree)
1817 weight, tree = optimize(tree, True)
1816 weight, tree = optimize(tree, True)
1818 def mfunc(repo, subset):
1817 def mfunc(repo, subset):
1819 return getset(repo, subset, tree)
1818 return getset(repo, subset, tree)
1820 return mfunc
1819 return mfunc
1821
1820
1822 def formatspec(expr, *args):
1821 def formatspec(expr, *args):
1823 '''
1822 '''
1824 This is a convenience function for using revsets internally, and
1823 This is a convenience function for using revsets internally, and
1825 escapes arguments appropriately. Aliases are intentionally ignored
1824 escapes arguments appropriately. Aliases are intentionally ignored
1826 so that intended expression behavior isn't accidentally subverted.
1825 so that intended expression behavior isn't accidentally subverted.
1827
1826
1828 Supported arguments:
1827 Supported arguments:
1829
1828
1830 %r = revset expression, parenthesized
1829 %r = revset expression, parenthesized
1831 %d = int(arg), no quoting
1830 %d = int(arg), no quoting
1832 %s = string(arg), escaped and single-quoted
1831 %s = string(arg), escaped and single-quoted
1833 %b = arg.branch(), escaped and single-quoted
1832 %b = arg.branch(), escaped and single-quoted
1834 %n = hex(arg), single-quoted
1833 %n = hex(arg), single-quoted
1835 %% = a literal '%'
1834 %% = a literal '%'
1836
1835
1837 Prefixing the type with 'l' specifies a parenthesized list of that type.
1836 Prefixing the type with 'l' specifies a parenthesized list of that type.
1838
1837
1839 >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()"))
1838 >>> formatspec('%r:: and %lr', '10 or 11', ("this()", "that()"))
1840 '(10 or 11):: and ((this()) or (that()))'
1839 '(10 or 11):: and ((this()) or (that()))'
1841 >>> formatspec('%d:: and not %d::', 10, 20)
1840 >>> formatspec('%d:: and not %d::', 10, 20)
1842 '10:: and not 20::'
1841 '10:: and not 20::'
1843 >>> formatspec('%ld or %ld', [], [1])
1842 >>> formatspec('%ld or %ld', [], [1])
1844 "_list('') or 1"
1843 "_list('') or 1"
1845 >>> formatspec('keyword(%s)', 'foo\\xe9')
1844 >>> formatspec('keyword(%s)', 'foo\\xe9')
1846 "keyword('foo\\\\xe9')"
1845 "keyword('foo\\\\xe9')"
1847 >>> b = lambda: 'default'
1846 >>> b = lambda: 'default'
1848 >>> b.branch = b
1847 >>> b.branch = b
1849 >>> formatspec('branch(%b)', b)
1848 >>> formatspec('branch(%b)', b)
1850 "branch('default')"
1849 "branch('default')"
1851 >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
1850 >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
1852 "root(_list('a\\x00b\\x00c\\x00d'))"
1851 "root(_list('a\\x00b\\x00c\\x00d'))"
1853 '''
1852 '''
1854
1853
1855 def quote(s):
1854 def quote(s):
1856 return repr(str(s))
1855 return repr(str(s))
1857
1856
1858 def argtype(c, arg):
1857 def argtype(c, arg):
1859 if c == 'd':
1858 if c == 'd':
1860 return str(int(arg))
1859 return str(int(arg))
1861 elif c == 's':
1860 elif c == 's':
1862 return quote(arg)
1861 return quote(arg)
1863 elif c == 'r':
1862 elif c == 'r':
1864 parse(arg) # make sure syntax errors are confined
1863 parse(arg) # make sure syntax errors are confined
1865 return '(%s)' % arg
1864 return '(%s)' % arg
1866 elif c == 'n':
1865 elif c == 'n':
1867 return quote(node.hex(arg))
1866 return quote(node.hex(arg))
1868 elif c == 'b':
1867 elif c == 'b':
1869 return quote(arg.branch())
1868 return quote(arg.branch())
1870
1869
1871 def listexp(s, t):
1870 def listexp(s, t):
1872 l = len(s)
1871 l = len(s)
1873 if l == 0:
1872 if l == 0:
1874 return "_list('')"
1873 return "_list('')"
1875 elif l == 1:
1874 elif l == 1:
1876 return argtype(t, s[0])
1875 return argtype(t, s[0])
1877 elif t == 'd':
1876 elif t == 'd':
1878 return "_list('%s')" % "\0".join(str(int(a)) for a in s)
1877 return "_list('%s')" % "\0".join(str(int(a)) for a in s)
1879 elif t == 's':
1878 elif t == 's':
1880 return "_list('%s')" % "\0".join(s)
1879 return "_list('%s')" % "\0".join(s)
1881 elif t == 'n':
1880 elif t == 'n':
1882 return "_list('%s')" % "\0".join(node.hex(a) for a in s)
1881 return "_list('%s')" % "\0".join(node.hex(a) for a in s)
1883 elif t == 'b':
1882 elif t == 'b':
1884 return "_list('%s')" % "\0".join(a.branch() for a in s)
1883 return "_list('%s')" % "\0".join(a.branch() for a in s)
1885
1884
1886 m = l // 2
1885 m = l // 2
1887 return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
1886 return '(%s or %s)' % (listexp(s[:m], t), listexp(s[m:], t))
1888
1887
1889 ret = ''
1888 ret = ''
1890 pos = 0
1889 pos = 0
1891 arg = 0
1890 arg = 0
1892 while pos < len(expr):
1891 while pos < len(expr):
1893 c = expr[pos]
1892 c = expr[pos]
1894 if c == '%':
1893 if c == '%':
1895 pos += 1
1894 pos += 1
1896 d = expr[pos]
1895 d = expr[pos]
1897 if d == '%':
1896 if d == '%':
1898 ret += d
1897 ret += d
1899 elif d in 'dsnbr':
1898 elif d in 'dsnbr':
1900 ret += argtype(d, args[arg])
1899 ret += argtype(d, args[arg])
1901 arg += 1
1900 arg += 1
1902 elif d == 'l':
1901 elif d == 'l':
1903 # a list of some type
1902 # a list of some type
1904 pos += 1
1903 pos += 1
1905 d = expr[pos]
1904 d = expr[pos]
1906 ret += listexp(list(args[arg]), d)
1905 ret += listexp(list(args[arg]), d)
1907 arg += 1
1906 arg += 1
1908 else:
1907 else:
1909 raise util.Abort('unexpected revspec format character %s' % d)
1908 raise util.Abort('unexpected revspec format character %s' % d)
1910 else:
1909 else:
1911 ret += c
1910 ret += c
1912 pos += 1
1911 pos += 1
1913
1912
1914 return ret
1913 return ret
1915
1914
1916 def prettyformat(tree):
1915 def prettyformat(tree):
1917 def _prettyformat(tree, level, lines):
1916 def _prettyformat(tree, level, lines):
1918 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
1917 if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'):
1919 lines.append((level, str(tree)))
1918 lines.append((level, str(tree)))
1920 else:
1919 else:
1921 lines.append((level, '(%s' % tree[0]))
1920 lines.append((level, '(%s' % tree[0]))
1922 for s in tree[1:]:
1921 for s in tree[1:]:
1923 _prettyformat(s, level + 1, lines)
1922 _prettyformat(s, level + 1, lines)
1924 lines[-1:] = [(lines[-1][0], lines[-1][1] + ')')]
1923 lines[-1:] = [(lines[-1][0], lines[-1][1] + ')')]
1925
1924
1926 lines = []
1925 lines = []
1927 _prettyformat(tree, 0, lines)
1926 _prettyformat(tree, 0, lines)
1928 output = '\n'.join((' '*l + s) for l, s in lines)
1927 output = '\n'.join((' '*l + s) for l, s in lines)
1929 return output
1928 return output
1930
1929
1931 # tell hggettext to extract docstrings from these functions:
1930 # tell hggettext to extract docstrings from these functions:
1932 i18nfunctions = symbols.values()
1931 i18nfunctions = symbols.values()
@@ -1,383 +1,395 b''
1 $ "$TESTDIR/hghave" serve || exit 80
1 $ "$TESTDIR/hghave" serve || exit 80
2
2
3 $ cat << EOF >> $HGRCPATH
3 $ cat << EOF >> $HGRCPATH
4 > [ui]
4 > [ui]
5 > logtemplate={rev}:{node|short} {desc|firstline}
5 > logtemplate={rev}:{node|short} {desc|firstline}
6 > [phases]
6 > [phases]
7 > publish=False
7 > publish=False
8 > [extensions]
8 > [extensions]
9 > EOF
9 > EOF
10 $ cat > obs.py << EOF
10 $ cat > obs.py << EOF
11 > import mercurial.obsolete
11 > import mercurial.obsolete
12 > mercurial.obsolete._enabled = True
12 > mercurial.obsolete._enabled = True
13 > EOF
13 > EOF
14 $ echo "obs=${TESTTMP}/obs.py" >> $HGRCPATH
14 $ echo "obs=${TESTTMP}/obs.py" >> $HGRCPATH
15
15
16 initialize
16 initialize
17
17
18 $ hg init a
18 $ hg init a
19 $ cd a
19 $ cd a
20 $ echo 'test' > test
20 $ echo 'test' > test
21 $ hg commit -Am'test'
21 $ hg commit -Am'test'
22 adding test
22 adding test
23
23
24 set bookmarks
24 set bookmarks
25
25
26 $ hg bookmark X
26 $ hg bookmark X
27 $ hg bookmark Y
27 $ hg bookmark Y
28 $ hg bookmark Z
28 $ hg bookmark Z
29
29
30 import bookmark by name
30 import bookmark by name
31
31
32 $ hg init ../b
32 $ hg init ../b
33 $ cd ../b
33 $ cd ../b
34 $ hg book Y
34 $ hg book Y
35 $ hg book
35 $ hg book
36 * Y -1:000000000000
36 * Y -1:000000000000
37 $ hg pull ../a
37 $ hg pull ../a
38 pulling from ../a
38 pulling from ../a
39 requesting all changes
39 requesting all changes
40 adding changesets
40 adding changesets
41 adding manifests
41 adding manifests
42 adding file changes
42 adding file changes
43 added 1 changesets with 1 changes to 1 files
43 added 1 changesets with 1 changes to 1 files
44 adding remote bookmark X
44 adding remote bookmark X
45 updating bookmark Y
45 updating bookmark Y
46 adding remote bookmark Z
46 adding remote bookmark Z
47 (run 'hg update' to get a working copy)
47 (run 'hg update' to get a working copy)
48 $ hg bookmarks
48 $ hg bookmarks
49 X 0:4e3505fd9583
49 X 0:4e3505fd9583
50 * Y 0:4e3505fd9583
50 * Y 0:4e3505fd9583
51 Z 0:4e3505fd9583
51 Z 0:4e3505fd9583
52 $ hg debugpushkey ../a namespaces
52 $ hg debugpushkey ../a namespaces
53 bookmarks
53 bookmarks
54 namespaces
54 namespaces
55 obsolete
55 obsolete
56 phases
56 phases
57 $ hg debugpushkey ../a bookmarks
57 $ hg debugpushkey ../a bookmarks
58 X 4e3505fd95835d721066b76e75dbb8cc554d7f77
58 X 4e3505fd95835d721066b76e75dbb8cc554d7f77
59 Y 4e3505fd95835d721066b76e75dbb8cc554d7f77
59 Y 4e3505fd95835d721066b76e75dbb8cc554d7f77
60 Z 4e3505fd95835d721066b76e75dbb8cc554d7f77
60 Z 4e3505fd95835d721066b76e75dbb8cc554d7f77
61 $ hg pull -B X ../a
61 $ hg pull -B X ../a
62 pulling from ../a
62 pulling from ../a
63 no changes found
63 no changes found
64 importing bookmark X
64 importing bookmark X
65 $ hg bookmark
65 $ hg bookmark
66 X 0:4e3505fd9583
66 X 0:4e3505fd9583
67 * Y 0:4e3505fd9583
67 * Y 0:4e3505fd9583
68 Z 0:4e3505fd9583
68 Z 0:4e3505fd9583
69
69
70 export bookmark by name
70 export bookmark by name
71
71
72 $ hg bookmark W
72 $ hg bookmark W
73 $ hg bookmark foo
73 $ hg bookmark foo
74 $ hg bookmark foobar
74 $ hg bookmark foobar
75 $ hg push -B W ../a
75 $ hg push -B W ../a
76 pushing to ../a
76 pushing to ../a
77 searching for changes
77 searching for changes
78 no changes found
78 no changes found
79 exporting bookmark W
79 exporting bookmark W
80 [1]
80 [1]
81 $ hg -R ../a bookmarks
81 $ hg -R ../a bookmarks
82 W -1:000000000000
82 W -1:000000000000
83 X 0:4e3505fd9583
83 X 0:4e3505fd9583
84 Y 0:4e3505fd9583
84 Y 0:4e3505fd9583
85 * Z 0:4e3505fd9583
85 * Z 0:4e3505fd9583
86
86
87 delete a remote bookmark
87 delete a remote bookmark
88
88
89 $ hg book -d W
89 $ hg book -d W
90 $ hg push -B W ../a
90 $ hg push -B W ../a
91 pushing to ../a
91 pushing to ../a
92 searching for changes
92 searching for changes
93 no changes found
93 no changes found
94 deleting remote bookmark W
94 deleting remote bookmark W
95 [1]
95 [1]
96
96
97 push/pull name that doesn't exist
97 push/pull name that doesn't exist
98
98
99 $ hg push -B badname ../a
99 $ hg push -B badname ../a
100 pushing to ../a
100 pushing to ../a
101 searching for changes
101 searching for changes
102 no changes found
102 no changes found
103 bookmark badname does not exist on the local or remote repository!
103 bookmark badname does not exist on the local or remote repository!
104 [2]
104 [2]
105 $ hg pull -B anotherbadname ../a
105 $ hg pull -B anotherbadname ../a
106 pulling from ../a
106 pulling from ../a
107 abort: remote bookmark anotherbadname not found!
107 abort: remote bookmark anotherbadname not found!
108 [255]
108 [255]
109
109
110 divergent bookmarks
110 divergent bookmarks
111
111
112 $ cd ../a
112 $ cd ../a
113 $ echo c1 > f1
113 $ echo c1 > f1
114 $ hg ci -Am1
114 $ hg ci -Am1
115 adding f1
115 adding f1
116 $ hg book -f @
116 $ hg book -f @
117 $ hg book -f X
117 $ hg book -f X
118 $ hg book
118 $ hg book
119 @ 1:0d2164f0ce0d
119 @ 1:0d2164f0ce0d
120 * X 1:0d2164f0ce0d
120 * X 1:0d2164f0ce0d
121 Y 0:4e3505fd9583
121 Y 0:4e3505fd9583
122 Z 1:0d2164f0ce0d
122 Z 1:0d2164f0ce0d
123
123
124 $ cd ../b
124 $ cd ../b
125 $ hg up
125 $ hg up
126 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
126 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
127 updating bookmark foobar
127 updating bookmark foobar
128 $ echo c2 > f2
128 $ echo c2 > f2
129 $ hg ci -Am2
129 $ hg ci -Am2
130 adding f2
130 adding f2
131 $ hg book -if @
131 $ hg book -if @
132 $ hg book -if X
132 $ hg book -if X
133 $ hg book
133 $ hg book
134 @ 1:9b140be10808
134 @ 1:9b140be10808
135 X 1:9b140be10808
135 X 1:9b140be10808
136 Y 0:4e3505fd9583
136 Y 0:4e3505fd9583
137 Z 0:4e3505fd9583
137 Z 0:4e3505fd9583
138 foo -1:000000000000
138 foo -1:000000000000
139 * foobar 1:9b140be10808
139 * foobar 1:9b140be10808
140
140
141 $ hg pull --config paths.foo=../a foo
141 $ hg pull --config paths.foo=../a foo
142 pulling from $TESTTMP/a (glob)
142 pulling from $TESTTMP/a (glob)
143 searching for changes
143 searching for changes
144 adding changesets
144 adding changesets
145 adding manifests
145 adding manifests
146 adding file changes
146 adding file changes
147 added 1 changesets with 1 changes to 1 files (+1 heads)
147 added 1 changesets with 1 changes to 1 files (+1 heads)
148 divergent bookmark @ stored as @foo
148 divergent bookmark @ stored as @foo
149 divergent bookmark X stored as X@foo
149 divergent bookmark X stored as X@foo
150 updating bookmark Z
150 updating bookmark Z
151 (run 'hg heads' to see heads, 'hg merge' to merge)
151 (run 'hg heads' to see heads, 'hg merge' to merge)
152 $ hg book
152 $ hg book
153 @ 1:9b140be10808
153 @ 1:9b140be10808
154 @foo 2:0d2164f0ce0d
154 @foo 2:0d2164f0ce0d
155 X 1:9b140be10808
155 X 1:9b140be10808
156 X@foo 2:0d2164f0ce0d
156 X@foo 2:0d2164f0ce0d
157 Y 0:4e3505fd9583
157 Y 0:4e3505fd9583
158 Z 2:0d2164f0ce0d
158 Z 2:0d2164f0ce0d
159 foo -1:000000000000
159 foo -1:000000000000
160 * foobar 1:9b140be10808
160 * foobar 1:9b140be10808
161 $ hg push -f ../a
161 $ hg push -f ../a
162 pushing to ../a
162 pushing to ../a
163 searching for changes
163 searching for changes
164 adding changesets
164 adding changesets
165 adding manifests
165 adding manifests
166 adding file changes
166 adding file changes
167 added 1 changesets with 1 changes to 1 files (+1 heads)
167 added 1 changesets with 1 changes to 1 files (+1 heads)
168 $ hg -R ../a book
168 $ hg -R ../a book
169 @ 1:0d2164f0ce0d
169 @ 1:0d2164f0ce0d
170 * X 1:0d2164f0ce0d
170 * X 1:0d2164f0ce0d
171 Y 0:4e3505fd9583
171 Y 0:4e3505fd9583
172 Z 1:0d2164f0ce0d
172 Z 1:0d2164f0ce0d
173
173
174 revsets should not ignore divergent bookmarks
175
176 $ hg bookmark -fr 1 Z
177 $ hg log -r 'bookmark()' --template '{rev}:{node|short} {bookmarks}\n'
178 0:4e3505fd9583 Y
179 1:9b140be10808 @ X Z foobar
180 2:0d2164f0ce0d @foo X@foo
181 $ hg log -r 'bookmark("X@foo")' --template '{rev}:{node|short} {bookmarks}\n'
182 2:0d2164f0ce0d @foo X@foo
183 $ hg log -r 'bookmark("re:X@foo")' --template '{rev}:{node|short} {bookmarks}\n'
184 2:0d2164f0ce0d @foo X@foo
185
174 update a remote bookmark from a non-head to a head
186 update a remote bookmark from a non-head to a head
175
187
176 $ hg up -q Y
188 $ hg up -q Y
177 $ echo c3 > f2
189 $ echo c3 > f2
178 $ hg ci -Am3
190 $ hg ci -Am3
179 adding f2
191 adding f2
180 created new head
192 created new head
181 $ hg push ../a
193 $ hg push ../a
182 pushing to ../a
194 pushing to ../a
183 searching for changes
195 searching for changes
184 adding changesets
196 adding changesets
185 adding manifests
197 adding manifests
186 adding file changes
198 adding file changes
187 added 1 changesets with 1 changes to 1 files (+1 heads)
199 added 1 changesets with 1 changes to 1 files (+1 heads)
188 updating bookmark Y
200 updating bookmark Y
189 $ hg -R ../a book
201 $ hg -R ../a book
190 @ 1:0d2164f0ce0d
202 @ 1:0d2164f0ce0d
191 * X 1:0d2164f0ce0d
203 * X 1:0d2164f0ce0d
192 Y 3:f6fc62dde3c0
204 Y 3:f6fc62dde3c0
193 Z 1:0d2164f0ce0d
205 Z 1:0d2164f0ce0d
194
206
195 diverging a remote bookmark fails
207 diverging a remote bookmark fails
196
208
197 $ hg up -q 4e3505fd9583
209 $ hg up -q 4e3505fd9583
198 $ echo c4 > f2
210 $ echo c4 > f2
199 $ hg ci -Am4
211 $ hg ci -Am4
200 adding f2
212 adding f2
201 created new head
213 created new head
202 $ echo c5 > f2
214 $ echo c5 > f2
203 $ hg ci -Am5
215 $ hg ci -Am5
204 $ hg log -G
216 $ hg log -G
205 @ 5:c922c0139ca0 5
217 @ 5:c922c0139ca0 5
206 |
218 |
207 o 4:4efff6d98829 4
219 o 4:4efff6d98829 4
208 |
220 |
209 | o 3:f6fc62dde3c0 3
221 | o 3:f6fc62dde3c0 3
210 |/
222 |/
211 | o 2:0d2164f0ce0d 1
223 | o 2:0d2164f0ce0d 1
212 |/
224 |/
213 | o 1:9b140be10808 2
225 | o 1:9b140be10808 2
214 |/
226 |/
215 o 0:4e3505fd9583 test
227 o 0:4e3505fd9583 test
216
228
217
229
218 $ hg book -f Y
230 $ hg book -f Y
219
231
220 $ cat <<EOF > ../a/.hg/hgrc
232 $ cat <<EOF > ../a/.hg/hgrc
221 > [web]
233 > [web]
222 > push_ssl = false
234 > push_ssl = false
223 > allow_push = *
235 > allow_push = *
224 > EOF
236 > EOF
225
237
226 $ hg -R ../a serve -p $HGPORT2 -d --pid-file=../hg2.pid
238 $ hg -R ../a serve -p $HGPORT2 -d --pid-file=../hg2.pid
227 $ cat ../hg2.pid >> $DAEMON_PIDS
239 $ cat ../hg2.pid >> $DAEMON_PIDS
228
240
229 $ hg push http://localhost:$HGPORT2/
241 $ hg push http://localhost:$HGPORT2/
230 pushing to http://localhost:$HGPORT2/
242 pushing to http://localhost:$HGPORT2/
231 searching for changes
243 searching for changes
232 abort: push creates new remote head c922c0139ca0!
244 abort: push creates new remote head c922c0139ca0!
233 (did you forget to merge? use push -f to force)
245 (did you forget to merge? use push -f to force)
234 [255]
246 [255]
235 $ hg -R ../a book
247 $ hg -R ../a book
236 @ 1:0d2164f0ce0d
248 @ 1:0d2164f0ce0d
237 * X 1:0d2164f0ce0d
249 * X 1:0d2164f0ce0d
238 Y 3:f6fc62dde3c0
250 Y 3:f6fc62dde3c0
239 Z 1:0d2164f0ce0d
251 Z 1:0d2164f0ce0d
240
252
241
253
242 Unrelated marker does not alter the decision
254 Unrelated marker does not alter the decision
243
255
244 $ hg debugobsolete aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
256 $ hg debugobsolete aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
245 $ hg push http://localhost:$HGPORT2/
257 $ hg push http://localhost:$HGPORT2/
246 pushing to http://localhost:$HGPORT2/
258 pushing to http://localhost:$HGPORT2/
247 searching for changes
259 searching for changes
248 abort: push creates new remote head c922c0139ca0!
260 abort: push creates new remote head c922c0139ca0!
249 (did you forget to merge? use push -f to force)
261 (did you forget to merge? use push -f to force)
250 [255]
262 [255]
251 $ hg -R ../a book
263 $ hg -R ../a book
252 @ 1:0d2164f0ce0d
264 @ 1:0d2164f0ce0d
253 * X 1:0d2164f0ce0d
265 * X 1:0d2164f0ce0d
254 Y 3:f6fc62dde3c0
266 Y 3:f6fc62dde3c0
255 Z 1:0d2164f0ce0d
267 Z 1:0d2164f0ce0d
256
268
257 Update to a successor works
269 Update to a successor works
258
270
259 $ hg id --debug -r 3
271 $ hg id --debug -r 3
260 f6fc62dde3c0771e29704af56ba4d8af77abcc2f
272 f6fc62dde3c0771e29704af56ba4d8af77abcc2f
261 $ hg id --debug -r 4
273 $ hg id --debug -r 4
262 4efff6d98829d9c824c621afd6e3f01865f5439f
274 4efff6d98829d9c824c621afd6e3f01865f5439f
263 $ hg id --debug -r 5
275 $ hg id --debug -r 5
264 c922c0139ca03858f655e4a2af4dd02796a63969 tip Y
276 c922c0139ca03858f655e4a2af4dd02796a63969 tip Y
265 $ hg debugobsolete f6fc62dde3c0771e29704af56ba4d8af77abcc2f cccccccccccccccccccccccccccccccccccccccc
277 $ hg debugobsolete f6fc62dde3c0771e29704af56ba4d8af77abcc2f cccccccccccccccccccccccccccccccccccccccc
266 $ hg debugobsolete cccccccccccccccccccccccccccccccccccccccc 4efff6d98829d9c824c621afd6e3f01865f5439f
278 $ hg debugobsolete cccccccccccccccccccccccccccccccccccccccc 4efff6d98829d9c824c621afd6e3f01865f5439f
267 $ hg push http://localhost:$HGPORT2/
279 $ hg push http://localhost:$HGPORT2/
268 pushing to http://localhost:$HGPORT2/
280 pushing to http://localhost:$HGPORT2/
269 searching for changes
281 searching for changes
270 remote: adding changesets
282 remote: adding changesets
271 remote: adding manifests
283 remote: adding manifests
272 remote: adding file changes
284 remote: adding file changes
273 remote: added 2 changesets with 2 changes to 1 files (+1 heads)
285 remote: added 2 changesets with 2 changes to 1 files (+1 heads)
274 updating bookmark Y
286 updating bookmark Y
275 $ hg -R ../a book
287 $ hg -R ../a book
276 @ 1:0d2164f0ce0d
288 @ 1:0d2164f0ce0d
277 * X 1:0d2164f0ce0d
289 * X 1:0d2164f0ce0d
278 Y 5:c922c0139ca0
290 Y 5:c922c0139ca0
279 Z 1:0d2164f0ce0d
291 Z 1:0d2164f0ce0d
280
292
281 hgweb
293 hgweb
282
294
283 $ cat <<EOF > .hg/hgrc
295 $ cat <<EOF > .hg/hgrc
284 > [web]
296 > [web]
285 > push_ssl = false
297 > push_ssl = false
286 > allow_push = *
298 > allow_push = *
287 > EOF
299 > EOF
288
300
289 $ hg serve -p $HGPORT -d --pid-file=../hg.pid -E errors.log
301 $ hg serve -p $HGPORT -d --pid-file=../hg.pid -E errors.log
290 $ cat ../hg.pid >> $DAEMON_PIDS
302 $ cat ../hg.pid >> $DAEMON_PIDS
291 $ cd ../a
303 $ cd ../a
292
304
293 $ hg debugpushkey http://localhost:$HGPORT/ namespaces
305 $ hg debugpushkey http://localhost:$HGPORT/ namespaces
294 bookmarks
306 bookmarks
295 namespaces
307 namespaces
296 obsolete
308 obsolete
297 phases
309 phases
298 $ hg debugpushkey http://localhost:$HGPORT/ bookmarks
310 $ hg debugpushkey http://localhost:$HGPORT/ bookmarks
299 @ 9b140be1080824d768c5a4691a564088eede71f9
311 @ 9b140be1080824d768c5a4691a564088eede71f9
300 X 9b140be1080824d768c5a4691a564088eede71f9
312 X 9b140be1080824d768c5a4691a564088eede71f9
301 Y c922c0139ca03858f655e4a2af4dd02796a63969
313 Y c922c0139ca03858f655e4a2af4dd02796a63969
302 Z 0d2164f0ce0d8f1d6f94351eba04b794909be66c
314 Z 9b140be1080824d768c5a4691a564088eede71f9
303 foo 0000000000000000000000000000000000000000
315 foo 0000000000000000000000000000000000000000
304 foobar 9b140be1080824d768c5a4691a564088eede71f9
316 foobar 9b140be1080824d768c5a4691a564088eede71f9
305 $ hg out -B http://localhost:$HGPORT/
317 $ hg out -B http://localhost:$HGPORT/
306 comparing with http://localhost:$HGPORT/
318 comparing with http://localhost:$HGPORT/
307 searching for changed bookmarks
319 searching for changed bookmarks
308 no changed bookmarks found
320 no changed bookmarks found
309 [1]
321 [1]
310 $ hg push -B Z http://localhost:$HGPORT/
322 $ hg push -B Z http://localhost:$HGPORT/
311 pushing to http://localhost:$HGPORT/
323 pushing to http://localhost:$HGPORT/
312 searching for changes
324 searching for changes
313 no changes found
325 no changes found
314 exporting bookmark Z
326 exporting bookmark Z
315 [1]
327 [1]
316 $ hg book -d Z
328 $ hg book -d Z
317 $ hg in -B http://localhost:$HGPORT/
329 $ hg in -B http://localhost:$HGPORT/
318 comparing with http://localhost:$HGPORT/
330 comparing with http://localhost:$HGPORT/
319 searching for changed bookmarks
331 searching for changed bookmarks
320 Z 0d2164f0ce0d
332 Z 0d2164f0ce0d
321 foo 000000000000
333 foo 000000000000
322 foobar 9b140be10808
334 foobar 9b140be10808
323 $ hg pull -B Z http://localhost:$HGPORT/
335 $ hg pull -B Z http://localhost:$HGPORT/
324 pulling from http://localhost:$HGPORT/
336 pulling from http://localhost:$HGPORT/
325 no changes found
337 no changes found
326 divergent bookmark @ stored as @1
338 divergent bookmark @ stored as @1
327 divergent bookmark X stored as X@1
339 divergent bookmark X stored as X@1
328 adding remote bookmark Z
340 adding remote bookmark Z
329 adding remote bookmark foo
341 adding remote bookmark foo
330 adding remote bookmark foobar
342 adding remote bookmark foobar
331 importing bookmark Z
343 importing bookmark Z
332 $ hg clone http://localhost:$HGPORT/ cloned-bookmarks
344 $ hg clone http://localhost:$HGPORT/ cloned-bookmarks
333 requesting all changes
345 requesting all changes
334 adding changesets
346 adding changesets
335 adding manifests
347 adding manifests
336 adding file changes
348 adding file changes
337 added 5 changesets with 5 changes to 3 files (+2 heads)
349 added 5 changesets with 5 changes to 3 files (+2 heads)
338 updating to bookmark @
350 updating to bookmark @
339 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
351 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
340 $ hg -R cloned-bookmarks bookmarks
352 $ hg -R cloned-bookmarks bookmarks
341 * @ 1:9b140be10808
353 * @ 1:9b140be10808
342 X 1:9b140be10808
354 X 1:9b140be10808
343 Y 4:c922c0139ca0
355 Y 4:c922c0139ca0
344 Z 2:0d2164f0ce0d
356 Z 2:0d2164f0ce0d
345 foo -1:000000000000
357 foo -1:000000000000
346 foobar 1:9b140be10808
358 foobar 1:9b140be10808
347
359
348 $ cd ..
360 $ cd ..
349
361
350 Pushing a bookmark should only push the changes required by that
362 Pushing a bookmark should only push the changes required by that
351 bookmark, not all outgoing changes:
363 bookmark, not all outgoing changes:
352 $ hg clone http://localhost:$HGPORT/ addmarks
364 $ hg clone http://localhost:$HGPORT/ addmarks
353 requesting all changes
365 requesting all changes
354 adding changesets
366 adding changesets
355 adding manifests
367 adding manifests
356 adding file changes
368 adding file changes
357 added 5 changesets with 5 changes to 3 files (+2 heads)
369 added 5 changesets with 5 changes to 3 files (+2 heads)
358 updating to bookmark @
370 updating to bookmark @
359 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
371 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
360 $ cd addmarks
372 $ cd addmarks
361 $ echo foo > foo
373 $ echo foo > foo
362 $ hg add foo
374 $ hg add foo
363 $ hg commit -m 'add foo'
375 $ hg commit -m 'add foo'
364 $ echo bar > bar
376 $ echo bar > bar
365 $ hg add bar
377 $ hg add bar
366 $ hg commit -m 'add bar'
378 $ hg commit -m 'add bar'
367 $ hg co "tip^"
379 $ hg co "tip^"
368 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
380 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
369 $ hg book add-foo
381 $ hg book add-foo
370 $ hg book -r tip add-bar
382 $ hg book -r tip add-bar
371 Note: this push *must* push only a single changeset, as that's the point
383 Note: this push *must* push only a single changeset, as that's the point
372 of this test.
384 of this test.
373 $ hg push -B add-foo --traceback
385 $ hg push -B add-foo --traceback
374 pushing to http://localhost:$HGPORT/
386 pushing to http://localhost:$HGPORT/
375 searching for changes
387 searching for changes
376 remote: adding changesets
388 remote: adding changesets
377 remote: adding manifests
389 remote: adding manifests
378 remote: adding file changes
390 remote: adding file changes
379 remote: added 1 changesets with 1 changes to 1 files
391 remote: added 1 changesets with 1 changes to 1 files
380 updating bookmark @ failed!
392 updating bookmark @ failed!
381 exporting bookmark add-foo
393 exporting bookmark add-foo
382
394
383 $ cd ..
395 $ cd ..
@@ -1,1322 +1,1330 b''
1 The g is crafted to have 2 filelog topological heads in a linear
1 The g is crafted to have 2 filelog topological heads in a linear
2 changeset graph
2 changeset graph
3
3
4 $ hg init a
4 $ hg init a
5 $ cd a
5 $ cd a
6 $ echo a > a
6 $ echo a > a
7 $ echo f > f
7 $ echo f > f
8 $ hg ci -Ama -d '1 0'
8 $ hg ci -Ama -d '1 0'
9 adding a
9 adding a
10 adding f
10 adding f
11
11
12 $ hg cp a b
12 $ hg cp a b
13 $ hg cp f g
13 $ hg cp f g
14 $ hg ci -mb -d '2 0'
14 $ hg ci -mb -d '2 0'
15
15
16 $ mkdir dir
16 $ mkdir dir
17 $ hg mv b dir
17 $ hg mv b dir
18 $ echo g >> g
18 $ echo g >> g
19 $ echo f >> f
19 $ echo f >> f
20 $ hg ci -mc -d '3 0'
20 $ hg ci -mc -d '3 0'
21
21
22 $ hg mv a b
22 $ hg mv a b
23 $ hg cp -f f g
23 $ hg cp -f f g
24 $ echo a > d
24 $ echo a > d
25 $ hg add d
25 $ hg add d
26 $ hg ci -md -d '4 0'
26 $ hg ci -md -d '4 0'
27
27
28 $ hg mv dir/b e
28 $ hg mv dir/b e
29 $ hg ci -me -d '5 0'
29 $ hg ci -me -d '5 0'
30
30
31 $ hg log a
31 $ hg log a
32 changeset: 0:9161b9aeaf16
32 changeset: 0:9161b9aeaf16
33 user: test
33 user: test
34 date: Thu Jan 01 00:00:01 1970 +0000
34 date: Thu Jan 01 00:00:01 1970 +0000
35 summary: a
35 summary: a
36
36
37 log on directory
37 log on directory
38
38
39 $ hg log dir
39 $ hg log dir
40 changeset: 4:7e4639b4691b
40 changeset: 4:7e4639b4691b
41 tag: tip
41 tag: tip
42 user: test
42 user: test
43 date: Thu Jan 01 00:00:05 1970 +0000
43 date: Thu Jan 01 00:00:05 1970 +0000
44 summary: e
44 summary: e
45
45
46 changeset: 2:f8954cd4dc1f
46 changeset: 2:f8954cd4dc1f
47 user: test
47 user: test
48 date: Thu Jan 01 00:00:03 1970 +0000
48 date: Thu Jan 01 00:00:03 1970 +0000
49 summary: c
49 summary: c
50
50
51 $ hg log somethingthatdoesntexist dir
51 $ hg log somethingthatdoesntexist dir
52 changeset: 4:7e4639b4691b
52 changeset: 4:7e4639b4691b
53 tag: tip
53 tag: tip
54 user: test
54 user: test
55 date: Thu Jan 01 00:00:05 1970 +0000
55 date: Thu Jan 01 00:00:05 1970 +0000
56 summary: e
56 summary: e
57
57
58 changeset: 2:f8954cd4dc1f
58 changeset: 2:f8954cd4dc1f
59 user: test
59 user: test
60 date: Thu Jan 01 00:00:03 1970 +0000
60 date: Thu Jan 01 00:00:03 1970 +0000
61 summary: c
61 summary: c
62
62
63
63
64 -f, directory
64 -f, directory
65
65
66 $ hg log -f dir
66 $ hg log -f dir
67 abort: cannot follow file not in parent revision: "dir"
67 abort: cannot follow file not in parent revision: "dir"
68 [255]
68 [255]
69
69
70 -f, but no args
70 -f, but no args
71
71
72 $ hg log -f
72 $ hg log -f
73 changeset: 4:7e4639b4691b
73 changeset: 4:7e4639b4691b
74 tag: tip
74 tag: tip
75 user: test
75 user: test
76 date: Thu Jan 01 00:00:05 1970 +0000
76 date: Thu Jan 01 00:00:05 1970 +0000
77 summary: e
77 summary: e
78
78
79 changeset: 3:2ca5ba701980
79 changeset: 3:2ca5ba701980
80 user: test
80 user: test
81 date: Thu Jan 01 00:00:04 1970 +0000
81 date: Thu Jan 01 00:00:04 1970 +0000
82 summary: d
82 summary: d
83
83
84 changeset: 2:f8954cd4dc1f
84 changeset: 2:f8954cd4dc1f
85 user: test
85 user: test
86 date: Thu Jan 01 00:00:03 1970 +0000
86 date: Thu Jan 01 00:00:03 1970 +0000
87 summary: c
87 summary: c
88
88
89 changeset: 1:d89b0a12d229
89 changeset: 1:d89b0a12d229
90 user: test
90 user: test
91 date: Thu Jan 01 00:00:02 1970 +0000
91 date: Thu Jan 01 00:00:02 1970 +0000
92 summary: b
92 summary: b
93
93
94 changeset: 0:9161b9aeaf16
94 changeset: 0:9161b9aeaf16
95 user: test
95 user: test
96 date: Thu Jan 01 00:00:01 1970 +0000
96 date: Thu Jan 01 00:00:01 1970 +0000
97 summary: a
97 summary: a
98
98
99
99
100 one rename
100 one rename
101
101
102 $ hg up -q 2
102 $ hg up -q 2
103 $ hg log -vf a
103 $ hg log -vf a
104 changeset: 0:9161b9aeaf16
104 changeset: 0:9161b9aeaf16
105 user: test
105 user: test
106 date: Thu Jan 01 00:00:01 1970 +0000
106 date: Thu Jan 01 00:00:01 1970 +0000
107 files: a f
107 files: a f
108 description:
108 description:
109 a
109 a
110
110
111
111
112
112
113 many renames
113 many renames
114
114
115 $ hg up -q tip
115 $ hg up -q tip
116 $ hg log -vf e
116 $ hg log -vf e
117 changeset: 4:7e4639b4691b
117 changeset: 4:7e4639b4691b
118 tag: tip
118 tag: tip
119 user: test
119 user: test
120 date: Thu Jan 01 00:00:05 1970 +0000
120 date: Thu Jan 01 00:00:05 1970 +0000
121 files: dir/b e
121 files: dir/b e
122 description:
122 description:
123 e
123 e
124
124
125
125
126 changeset: 2:f8954cd4dc1f
126 changeset: 2:f8954cd4dc1f
127 user: test
127 user: test
128 date: Thu Jan 01 00:00:03 1970 +0000
128 date: Thu Jan 01 00:00:03 1970 +0000
129 files: b dir/b f g
129 files: b dir/b f g
130 description:
130 description:
131 c
131 c
132
132
133
133
134 changeset: 1:d89b0a12d229
134 changeset: 1:d89b0a12d229
135 user: test
135 user: test
136 date: Thu Jan 01 00:00:02 1970 +0000
136 date: Thu Jan 01 00:00:02 1970 +0000
137 files: b g
137 files: b g
138 description:
138 description:
139 b
139 b
140
140
141
141
142 changeset: 0:9161b9aeaf16
142 changeset: 0:9161b9aeaf16
143 user: test
143 user: test
144 date: Thu Jan 01 00:00:01 1970 +0000
144 date: Thu Jan 01 00:00:01 1970 +0000
145 files: a f
145 files: a f
146 description:
146 description:
147 a
147 a
148
148
149
149
150
150
151
151
152 log -pf dir/b
152 log -pf dir/b
153
153
154 $ hg up -q 3
154 $ hg up -q 3
155 $ hg log -pf dir/b
155 $ hg log -pf dir/b
156 changeset: 2:f8954cd4dc1f
156 changeset: 2:f8954cd4dc1f
157 user: test
157 user: test
158 date: Thu Jan 01 00:00:03 1970 +0000
158 date: Thu Jan 01 00:00:03 1970 +0000
159 summary: c
159 summary: c
160
160
161 diff -r d89b0a12d229 -r f8954cd4dc1f dir/b
161 diff -r d89b0a12d229 -r f8954cd4dc1f dir/b
162 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
162 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
163 +++ b/dir/b Thu Jan 01 00:00:03 1970 +0000
163 +++ b/dir/b Thu Jan 01 00:00:03 1970 +0000
164 @@ -0,0 +1,1 @@
164 @@ -0,0 +1,1 @@
165 +a
165 +a
166
166
167 changeset: 1:d89b0a12d229
167 changeset: 1:d89b0a12d229
168 user: test
168 user: test
169 date: Thu Jan 01 00:00:02 1970 +0000
169 date: Thu Jan 01 00:00:02 1970 +0000
170 summary: b
170 summary: b
171
171
172 diff -r 9161b9aeaf16 -r d89b0a12d229 b
172 diff -r 9161b9aeaf16 -r d89b0a12d229 b
173 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
173 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
174 +++ b/b Thu Jan 01 00:00:02 1970 +0000
174 +++ b/b Thu Jan 01 00:00:02 1970 +0000
175 @@ -0,0 +1,1 @@
175 @@ -0,0 +1,1 @@
176 +a
176 +a
177
177
178 changeset: 0:9161b9aeaf16
178 changeset: 0:9161b9aeaf16
179 user: test
179 user: test
180 date: Thu Jan 01 00:00:01 1970 +0000
180 date: Thu Jan 01 00:00:01 1970 +0000
181 summary: a
181 summary: a
182
182
183 diff -r 000000000000 -r 9161b9aeaf16 a
183 diff -r 000000000000 -r 9161b9aeaf16 a
184 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
184 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
185 +++ b/a Thu Jan 01 00:00:01 1970 +0000
185 +++ b/a Thu Jan 01 00:00:01 1970 +0000
186 @@ -0,0 +1,1 @@
186 @@ -0,0 +1,1 @@
187 +a
187 +a
188
188
189
189
190 log -vf dir/b
190 log -vf dir/b
191
191
192 $ hg log -vf dir/b
192 $ hg log -vf dir/b
193 changeset: 2:f8954cd4dc1f
193 changeset: 2:f8954cd4dc1f
194 user: test
194 user: test
195 date: Thu Jan 01 00:00:03 1970 +0000
195 date: Thu Jan 01 00:00:03 1970 +0000
196 files: b dir/b f g
196 files: b dir/b f g
197 description:
197 description:
198 c
198 c
199
199
200
200
201 changeset: 1:d89b0a12d229
201 changeset: 1:d89b0a12d229
202 user: test
202 user: test
203 date: Thu Jan 01 00:00:02 1970 +0000
203 date: Thu Jan 01 00:00:02 1970 +0000
204 files: b g
204 files: b g
205 description:
205 description:
206 b
206 b
207
207
208
208
209 changeset: 0:9161b9aeaf16
209 changeset: 0:9161b9aeaf16
210 user: test
210 user: test
211 date: Thu Jan 01 00:00:01 1970 +0000
211 date: Thu Jan 01 00:00:01 1970 +0000
212 files: a f
212 files: a f
213 description:
213 description:
214 a
214 a
215
215
216
216
217
217
218
218
219 -f and multiple filelog heads
219 -f and multiple filelog heads
220
220
221 $ hg up -q 2
221 $ hg up -q 2
222 $ hg log -f g --template '{rev}\n'
222 $ hg log -f g --template '{rev}\n'
223 2
223 2
224 1
224 1
225 0
225 0
226 $ hg up -q tip
226 $ hg up -q tip
227 $ hg log -f g --template '{rev}\n'
227 $ hg log -f g --template '{rev}\n'
228 3
228 3
229 2
229 2
230 0
230 0
231
231
232
232
233 log copies with --copies
233 log copies with --copies
234
234
235 $ hg log -vC --template '{rev} {file_copies}\n'
235 $ hg log -vC --template '{rev} {file_copies}\n'
236 4 e (dir/b)
236 4 e (dir/b)
237 3 b (a)g (f)
237 3 b (a)g (f)
238 2 dir/b (b)
238 2 dir/b (b)
239 1 b (a)g (f)
239 1 b (a)g (f)
240 0
240 0
241
241
242 log copies switch without --copies, with old filecopy template
242 log copies switch without --copies, with old filecopy template
243
243
244 $ hg log -v --template '{rev} {file_copies_switch%filecopy}\n'
244 $ hg log -v --template '{rev} {file_copies_switch%filecopy}\n'
245 4
245 4
246 3
246 3
247 2
247 2
248 1
248 1
249 0
249 0
250
250
251 log copies switch with --copies
251 log copies switch with --copies
252
252
253 $ hg log -vC --template '{rev} {file_copies_switch}\n'
253 $ hg log -vC --template '{rev} {file_copies_switch}\n'
254 4 e (dir/b)
254 4 e (dir/b)
255 3 b (a)g (f)
255 3 b (a)g (f)
256 2 dir/b (b)
256 2 dir/b (b)
257 1 b (a)g (f)
257 1 b (a)g (f)
258 0
258 0
259
259
260
260
261 log copies with hardcoded style and with --style=default
261 log copies with hardcoded style and with --style=default
262
262
263 $ hg log -vC -r4
263 $ hg log -vC -r4
264 changeset: 4:7e4639b4691b
264 changeset: 4:7e4639b4691b
265 tag: tip
265 tag: tip
266 user: test
266 user: test
267 date: Thu Jan 01 00:00:05 1970 +0000
267 date: Thu Jan 01 00:00:05 1970 +0000
268 files: dir/b e
268 files: dir/b e
269 copies: e (dir/b)
269 copies: e (dir/b)
270 description:
270 description:
271 e
271 e
272
272
273
273
274 $ hg log -vC -r4 --style=default
274 $ hg log -vC -r4 --style=default
275 changeset: 4:7e4639b4691b
275 changeset: 4:7e4639b4691b
276 tag: tip
276 tag: tip
277 user: test
277 user: test
278 date: Thu Jan 01 00:00:05 1970 +0000
278 date: Thu Jan 01 00:00:05 1970 +0000
279 files: dir/b e
279 files: dir/b e
280 copies: e (dir/b)
280 copies: e (dir/b)
281 description:
281 description:
282 e
282 e
283
283
284
284
285
285
286
286
287 log copies, non-linear manifest
287 log copies, non-linear manifest
288
288
289 $ hg up -C 3
289 $ hg up -C 3
290 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
290 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
291 $ hg mv dir/b e
291 $ hg mv dir/b e
292 $ echo foo > foo
292 $ echo foo > foo
293 $ hg ci -Ame2 -d '6 0'
293 $ hg ci -Ame2 -d '6 0'
294 adding foo
294 adding foo
295 created new head
295 created new head
296 $ hg log -v --template '{rev} {file_copies}\n' -r 5
296 $ hg log -v --template '{rev} {file_copies}\n' -r 5
297 5 e (dir/b)
297 5 e (dir/b)
298
298
299
299
300 log copies, execute bit set
300 log copies, execute bit set
301
301
302 #if execbit
302 #if execbit
303 $ chmod +x e
303 $ chmod +x e
304 $ hg ci -me3 -d '7 0'
304 $ hg ci -me3 -d '7 0'
305 $ hg log -v --template '{rev} {file_copies}\n' -r 6
305 $ hg log -v --template '{rev} {file_copies}\n' -r 6
306 6
306 6
307 #endif
307 #endif
308
308
309
309
310 log -p d
310 log -p d
311
311
312 $ hg log -pv d
312 $ hg log -pv d
313 changeset: 3:2ca5ba701980
313 changeset: 3:2ca5ba701980
314 user: test
314 user: test
315 date: Thu Jan 01 00:00:04 1970 +0000
315 date: Thu Jan 01 00:00:04 1970 +0000
316 files: a b d g
316 files: a b d g
317 description:
317 description:
318 d
318 d
319
319
320
320
321 diff -r f8954cd4dc1f -r 2ca5ba701980 d
321 diff -r f8954cd4dc1f -r 2ca5ba701980 d
322 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
322 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
323 +++ b/d Thu Jan 01 00:00:04 1970 +0000
323 +++ b/d Thu Jan 01 00:00:04 1970 +0000
324 @@ -0,0 +1,1 @@
324 @@ -0,0 +1,1 @@
325 +a
325 +a
326
326
327
327
328
328
329 log --removed file
329 log --removed file
330
330
331 $ hg log --removed -v a
331 $ hg log --removed -v a
332 changeset: 3:2ca5ba701980
332 changeset: 3:2ca5ba701980
333 user: test
333 user: test
334 date: Thu Jan 01 00:00:04 1970 +0000
334 date: Thu Jan 01 00:00:04 1970 +0000
335 files: a b d g
335 files: a b d g
336 description:
336 description:
337 d
337 d
338
338
339
339
340 changeset: 0:9161b9aeaf16
340 changeset: 0:9161b9aeaf16
341 user: test
341 user: test
342 date: Thu Jan 01 00:00:01 1970 +0000
342 date: Thu Jan 01 00:00:01 1970 +0000
343 files: a f
343 files: a f
344 description:
344 description:
345 a
345 a
346
346
347
347
348
348
349 log --removed revrange file
349 log --removed revrange file
350
350
351 $ hg log --removed -v -r0:2 a
351 $ hg log --removed -v -r0:2 a
352 changeset: 0:9161b9aeaf16
352 changeset: 0:9161b9aeaf16
353 user: test
353 user: test
354 date: Thu Jan 01 00:00:01 1970 +0000
354 date: Thu Jan 01 00:00:01 1970 +0000
355 files: a f
355 files: a f
356 description:
356 description:
357 a
357 a
358
358
359
359
360 $ cd ..
360 $ cd ..
361
361
362 log --follow tests
362 log --follow tests
363
363
364 $ hg init follow
364 $ hg init follow
365 $ cd follow
365 $ cd follow
366
366
367 $ echo base > base
367 $ echo base > base
368 $ hg ci -Ambase -d '1 0'
368 $ hg ci -Ambase -d '1 0'
369 adding base
369 adding base
370
370
371 $ echo r1 >> base
371 $ echo r1 >> base
372 $ hg ci -Amr1 -d '1 0'
372 $ hg ci -Amr1 -d '1 0'
373 $ echo r2 >> base
373 $ echo r2 >> base
374 $ hg ci -Amr2 -d '1 0'
374 $ hg ci -Amr2 -d '1 0'
375
375
376 $ hg up -C 1
376 $ hg up -C 1
377 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
377 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
378 $ echo b1 > b1
378 $ echo b1 > b1
379 $ hg ci -Amb1 -d '1 0'
379 $ hg ci -Amb1 -d '1 0'
380 adding b1
380 adding b1
381 created new head
381 created new head
382
382
383
383
384 log -f
384 log -f
385
385
386 $ hg log -f
386 $ hg log -f
387 changeset: 3:e62f78d544b4
387 changeset: 3:e62f78d544b4
388 tag: tip
388 tag: tip
389 parent: 1:3d5bf5654eda
389 parent: 1:3d5bf5654eda
390 user: test
390 user: test
391 date: Thu Jan 01 00:00:01 1970 +0000
391 date: Thu Jan 01 00:00:01 1970 +0000
392 summary: b1
392 summary: b1
393
393
394 changeset: 1:3d5bf5654eda
394 changeset: 1:3d5bf5654eda
395 user: test
395 user: test
396 date: Thu Jan 01 00:00:01 1970 +0000
396 date: Thu Jan 01 00:00:01 1970 +0000
397 summary: r1
397 summary: r1
398
398
399 changeset: 0:67e992f2c4f3
399 changeset: 0:67e992f2c4f3
400 user: test
400 user: test
401 date: Thu Jan 01 00:00:01 1970 +0000
401 date: Thu Jan 01 00:00:01 1970 +0000
402 summary: base
402 summary: base
403
403
404
404
405
405
406 log -f -r 1:tip
406 log -f -r 1:tip
407
407
408 $ hg up -C 0
408 $ hg up -C 0
409 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
409 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
410 $ echo b2 > b2
410 $ echo b2 > b2
411 $ hg ci -Amb2 -d '1 0'
411 $ hg ci -Amb2 -d '1 0'
412 adding b2
412 adding b2
413 created new head
413 created new head
414 $ hg log -f -r 1:tip
414 $ hg log -f -r 1:tip
415 changeset: 1:3d5bf5654eda
415 changeset: 1:3d5bf5654eda
416 user: test
416 user: test
417 date: Thu Jan 01 00:00:01 1970 +0000
417 date: Thu Jan 01 00:00:01 1970 +0000
418 summary: r1
418 summary: r1
419
419
420 changeset: 2:60c670bf5b30
420 changeset: 2:60c670bf5b30
421 user: test
421 user: test
422 date: Thu Jan 01 00:00:01 1970 +0000
422 date: Thu Jan 01 00:00:01 1970 +0000
423 summary: r2
423 summary: r2
424
424
425 changeset: 3:e62f78d544b4
425 changeset: 3:e62f78d544b4
426 parent: 1:3d5bf5654eda
426 parent: 1:3d5bf5654eda
427 user: test
427 user: test
428 date: Thu Jan 01 00:00:01 1970 +0000
428 date: Thu Jan 01 00:00:01 1970 +0000
429 summary: b1
429 summary: b1
430
430
431
431
432
432
433 log -r . with two parents
433 log -r . with two parents
434
434
435 $ hg up -C 3
435 $ hg up -C 3
436 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
436 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
437 $ hg merge tip
437 $ hg merge tip
438 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
438 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
439 (branch merge, don't forget to commit)
439 (branch merge, don't forget to commit)
440 $ hg log -r .
440 $ hg log -r .
441 changeset: 3:e62f78d544b4
441 changeset: 3:e62f78d544b4
442 parent: 1:3d5bf5654eda
442 parent: 1:3d5bf5654eda
443 user: test
443 user: test
444 date: Thu Jan 01 00:00:01 1970 +0000
444 date: Thu Jan 01 00:00:01 1970 +0000
445 summary: b1
445 summary: b1
446
446
447
447
448
448
449 log -r . with one parent
449 log -r . with one parent
450
450
451 $ hg ci -mm12 -d '1 0'
451 $ hg ci -mm12 -d '1 0'
452 $ hg log -r .
452 $ hg log -r .
453 changeset: 5:302e9dd6890d
453 changeset: 5:302e9dd6890d
454 tag: tip
454 tag: tip
455 parent: 3:e62f78d544b4
455 parent: 3:e62f78d544b4
456 parent: 4:ddb82e70d1a1
456 parent: 4:ddb82e70d1a1
457 user: test
457 user: test
458 date: Thu Jan 01 00:00:01 1970 +0000
458 date: Thu Jan 01 00:00:01 1970 +0000
459 summary: m12
459 summary: m12
460
460
461
461
462 $ echo postm >> b1
462 $ echo postm >> b1
463 $ hg ci -Amb1.1 -d'1 0'
463 $ hg ci -Amb1.1 -d'1 0'
464
464
465
465
466 log --follow-first
466 log --follow-first
467
467
468 $ hg log --follow-first
468 $ hg log --follow-first
469 changeset: 6:2404bbcab562
469 changeset: 6:2404bbcab562
470 tag: tip
470 tag: tip
471 user: test
471 user: test
472 date: Thu Jan 01 00:00:01 1970 +0000
472 date: Thu Jan 01 00:00:01 1970 +0000
473 summary: b1.1
473 summary: b1.1
474
474
475 changeset: 5:302e9dd6890d
475 changeset: 5:302e9dd6890d
476 parent: 3:e62f78d544b4
476 parent: 3:e62f78d544b4
477 parent: 4:ddb82e70d1a1
477 parent: 4:ddb82e70d1a1
478 user: test
478 user: test
479 date: Thu Jan 01 00:00:01 1970 +0000
479 date: Thu Jan 01 00:00:01 1970 +0000
480 summary: m12
480 summary: m12
481
481
482 changeset: 3:e62f78d544b4
482 changeset: 3:e62f78d544b4
483 parent: 1:3d5bf5654eda
483 parent: 1:3d5bf5654eda
484 user: test
484 user: test
485 date: Thu Jan 01 00:00:01 1970 +0000
485 date: Thu Jan 01 00:00:01 1970 +0000
486 summary: b1
486 summary: b1
487
487
488 changeset: 1:3d5bf5654eda
488 changeset: 1:3d5bf5654eda
489 user: test
489 user: test
490 date: Thu Jan 01 00:00:01 1970 +0000
490 date: Thu Jan 01 00:00:01 1970 +0000
491 summary: r1
491 summary: r1
492
492
493 changeset: 0:67e992f2c4f3
493 changeset: 0:67e992f2c4f3
494 user: test
494 user: test
495 date: Thu Jan 01 00:00:01 1970 +0000
495 date: Thu Jan 01 00:00:01 1970 +0000
496 summary: base
496 summary: base
497
497
498
498
499
499
500 log -P 2
500 log -P 2
501
501
502 $ hg log -P 2
502 $ hg log -P 2
503 changeset: 6:2404bbcab562
503 changeset: 6:2404bbcab562
504 tag: tip
504 tag: tip
505 user: test
505 user: test
506 date: Thu Jan 01 00:00:01 1970 +0000
506 date: Thu Jan 01 00:00:01 1970 +0000
507 summary: b1.1
507 summary: b1.1
508
508
509 changeset: 5:302e9dd6890d
509 changeset: 5:302e9dd6890d
510 parent: 3:e62f78d544b4
510 parent: 3:e62f78d544b4
511 parent: 4:ddb82e70d1a1
511 parent: 4:ddb82e70d1a1
512 user: test
512 user: test
513 date: Thu Jan 01 00:00:01 1970 +0000
513 date: Thu Jan 01 00:00:01 1970 +0000
514 summary: m12
514 summary: m12
515
515
516 changeset: 4:ddb82e70d1a1
516 changeset: 4:ddb82e70d1a1
517 parent: 0:67e992f2c4f3
517 parent: 0:67e992f2c4f3
518 user: test
518 user: test
519 date: Thu Jan 01 00:00:01 1970 +0000
519 date: Thu Jan 01 00:00:01 1970 +0000
520 summary: b2
520 summary: b2
521
521
522 changeset: 3:e62f78d544b4
522 changeset: 3:e62f78d544b4
523 parent: 1:3d5bf5654eda
523 parent: 1:3d5bf5654eda
524 user: test
524 user: test
525 date: Thu Jan 01 00:00:01 1970 +0000
525 date: Thu Jan 01 00:00:01 1970 +0000
526 summary: b1
526 summary: b1
527
527
528
528
529
529
530 log -r tip -p --git
530 log -r tip -p --git
531
531
532 $ hg log -r tip -p --git
532 $ hg log -r tip -p --git
533 changeset: 6:2404bbcab562
533 changeset: 6:2404bbcab562
534 tag: tip
534 tag: tip
535 user: test
535 user: test
536 date: Thu Jan 01 00:00:01 1970 +0000
536 date: Thu Jan 01 00:00:01 1970 +0000
537 summary: b1.1
537 summary: b1.1
538
538
539 diff --git a/b1 b/b1
539 diff --git a/b1 b/b1
540 --- a/b1
540 --- a/b1
541 +++ b/b1
541 +++ b/b1
542 @@ -1,1 +1,2 @@
542 @@ -1,1 +1,2 @@
543 b1
543 b1
544 +postm
544 +postm
545
545
546
546
547
547
548 log -r ""
548 log -r ""
549
549
550 $ hg log -r ''
550 $ hg log -r ''
551 hg: parse error: empty query
551 hg: parse error: empty query
552 [255]
552 [255]
553
553
554 log -r <some unknown node id>
554 log -r <some unknown node id>
555
555
556 $ hg log -r 1000000000000000000000000000000000000000
556 $ hg log -r 1000000000000000000000000000000000000000
557 abort: unknown revision '1000000000000000000000000000000000000000'!
557 abort: unknown revision '1000000000000000000000000000000000000000'!
558 [255]
558 [255]
559
559
560 log -k r1
560 log -k r1
561
561
562 $ hg log -k r1
562 $ hg log -k r1
563 changeset: 1:3d5bf5654eda
563 changeset: 1:3d5bf5654eda
564 user: test
564 user: test
565 date: Thu Jan 01 00:00:01 1970 +0000
565 date: Thu Jan 01 00:00:01 1970 +0000
566 summary: r1
566 summary: r1
567
567
568 log -p -l2 --color=always
568 log -p -l2 --color=always
569
569
570 $ hg --config extensions.color= --config color.mode=ansi \
570 $ hg --config extensions.color= --config color.mode=ansi \
571 > log -p -l2 --color=always
571 > log -p -l2 --color=always
572 \x1b[0;33mchangeset: 6:2404bbcab562\x1b[0m (esc)
572 \x1b[0;33mchangeset: 6:2404bbcab562\x1b[0m (esc)
573 tag: tip
573 tag: tip
574 user: test
574 user: test
575 date: Thu Jan 01 00:00:01 1970 +0000
575 date: Thu Jan 01 00:00:01 1970 +0000
576 summary: b1.1
576 summary: b1.1
577
577
578 \x1b[0;1mdiff -r 302e9dd6890d -r 2404bbcab562 b1\x1b[0m (esc)
578 \x1b[0;1mdiff -r 302e9dd6890d -r 2404bbcab562 b1\x1b[0m (esc)
579 \x1b[0;31;1m--- a/b1 Thu Jan 01 00:00:01 1970 +0000\x1b[0m (esc)
579 \x1b[0;31;1m--- a/b1 Thu Jan 01 00:00:01 1970 +0000\x1b[0m (esc)
580 \x1b[0;32;1m+++ b/b1 Thu Jan 01 00:00:01 1970 +0000\x1b[0m (esc)
580 \x1b[0;32;1m+++ b/b1 Thu Jan 01 00:00:01 1970 +0000\x1b[0m (esc)
581 \x1b[0;35m@@ -1,1 +1,2 @@\x1b[0m (esc)
581 \x1b[0;35m@@ -1,1 +1,2 @@\x1b[0m (esc)
582 b1
582 b1
583 \x1b[0;32m+postm\x1b[0m (esc)
583 \x1b[0;32m+postm\x1b[0m (esc)
584
584
585 \x1b[0;33mchangeset: 5:302e9dd6890d\x1b[0m (esc)
585 \x1b[0;33mchangeset: 5:302e9dd6890d\x1b[0m (esc)
586 parent: 3:e62f78d544b4
586 parent: 3:e62f78d544b4
587 parent: 4:ddb82e70d1a1
587 parent: 4:ddb82e70d1a1
588 user: test
588 user: test
589 date: Thu Jan 01 00:00:01 1970 +0000
589 date: Thu Jan 01 00:00:01 1970 +0000
590 summary: m12
590 summary: m12
591
591
592 \x1b[0;1mdiff -r e62f78d544b4 -r 302e9dd6890d b2\x1b[0m (esc)
592 \x1b[0;1mdiff -r e62f78d544b4 -r 302e9dd6890d b2\x1b[0m (esc)
593 \x1b[0;31;1m--- /dev/null Thu Jan 01 00:00:00 1970 +0000\x1b[0m (esc)
593 \x1b[0;31;1m--- /dev/null Thu Jan 01 00:00:00 1970 +0000\x1b[0m (esc)
594 \x1b[0;32;1m+++ b/b2 Thu Jan 01 00:00:01 1970 +0000\x1b[0m (esc)
594 \x1b[0;32;1m+++ b/b2 Thu Jan 01 00:00:01 1970 +0000\x1b[0m (esc)
595 \x1b[0;35m@@ -0,0 +1,1 @@\x1b[0m (esc)
595 \x1b[0;35m@@ -0,0 +1,1 @@\x1b[0m (esc)
596 \x1b[0;32m+b2\x1b[0m (esc)
596 \x1b[0;32m+b2\x1b[0m (esc)
597
597
598
598
599
599
600 log -r tip --stat
600 log -r tip --stat
601
601
602 $ hg log -r tip --stat
602 $ hg log -r tip --stat
603 changeset: 6:2404bbcab562
603 changeset: 6:2404bbcab562
604 tag: tip
604 tag: tip
605 user: test
605 user: test
606 date: Thu Jan 01 00:00:01 1970 +0000
606 date: Thu Jan 01 00:00:01 1970 +0000
607 summary: b1.1
607 summary: b1.1
608
608
609 b1 | 1 +
609 b1 | 1 +
610 1 files changed, 1 insertions(+), 0 deletions(-)
610 1 files changed, 1 insertions(+), 0 deletions(-)
611
611
612
612
613 $ cd ..
613 $ cd ..
614
614
615
615
616 User
616 User
617
617
618 $ hg init usertest
618 $ hg init usertest
619 $ cd usertest
619 $ cd usertest
620
620
621 $ echo a > a
621 $ echo a > a
622 $ hg ci -A -m "a" -u "User One <user1@example.org>"
622 $ hg ci -A -m "a" -u "User One <user1@example.org>"
623 adding a
623 adding a
624 $ echo b > b
624 $ echo b > b
625 $ hg ci -A -m "b" -u "User Two <user2@example.org>"
625 $ hg ci -A -m "b" -u "User Two <user2@example.org>"
626 adding b
626 adding b
627
627
628 $ hg log -u "User One <user1@example.org>"
628 $ hg log -u "User One <user1@example.org>"
629 changeset: 0:29a4c94f1924
629 changeset: 0:29a4c94f1924
630 user: User One <user1@example.org>
630 user: User One <user1@example.org>
631 date: Thu Jan 01 00:00:00 1970 +0000
631 date: Thu Jan 01 00:00:00 1970 +0000
632 summary: a
632 summary: a
633
633
634 $ hg log -u "user1" -u "user2"
634 $ hg log -u "user1" -u "user2"
635 changeset: 1:e834b5e69c0e
635 changeset: 1:e834b5e69c0e
636 tag: tip
636 tag: tip
637 user: User Two <user2@example.org>
637 user: User Two <user2@example.org>
638 date: Thu Jan 01 00:00:00 1970 +0000
638 date: Thu Jan 01 00:00:00 1970 +0000
639 summary: b
639 summary: b
640
640
641 changeset: 0:29a4c94f1924
641 changeset: 0:29a4c94f1924
642 user: User One <user1@example.org>
642 user: User One <user1@example.org>
643 date: Thu Jan 01 00:00:00 1970 +0000
643 date: Thu Jan 01 00:00:00 1970 +0000
644 summary: a
644 summary: a
645
645
646 $ hg log -u "user3"
646 $ hg log -u "user3"
647
647
648 $ cd ..
648 $ cd ..
649
649
650 $ hg init branches
650 $ hg init branches
651 $ cd branches
651 $ cd branches
652
652
653 $ echo a > a
653 $ echo a > a
654 $ hg ci -A -m "commit on default"
654 $ hg ci -A -m "commit on default"
655 adding a
655 adding a
656 $ hg branch test
656 $ hg branch test
657 marked working directory as branch test
657 marked working directory as branch test
658 (branches are permanent and global, did you want a bookmark?)
658 (branches are permanent and global, did you want a bookmark?)
659 $ echo b > b
659 $ echo b > b
660 $ hg ci -A -m "commit on test"
660 $ hg ci -A -m "commit on test"
661 adding b
661 adding b
662
662
663 $ hg up default
663 $ hg up default
664 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
664 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
665 $ echo c > c
665 $ echo c > c
666 $ hg ci -A -m "commit on default"
666 $ hg ci -A -m "commit on default"
667 adding c
667 adding c
668 $ hg up test
668 $ hg up test
669 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
669 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
670 $ echo c > c
670 $ echo c > c
671 $ hg ci -A -m "commit on test"
671 $ hg ci -A -m "commit on test"
672 adding c
672 adding c
673
673
674
674
675 log -b default
675 log -b default
676
676
677 $ hg log -b default
677 $ hg log -b default
678 changeset: 2:c3a4f03cc9a7
678 changeset: 2:c3a4f03cc9a7
679 parent: 0:24427303d56f
679 parent: 0:24427303d56f
680 user: test
680 user: test
681 date: Thu Jan 01 00:00:00 1970 +0000
681 date: Thu Jan 01 00:00:00 1970 +0000
682 summary: commit on default
682 summary: commit on default
683
683
684 changeset: 0:24427303d56f
684 changeset: 0:24427303d56f
685 user: test
685 user: test
686 date: Thu Jan 01 00:00:00 1970 +0000
686 date: Thu Jan 01 00:00:00 1970 +0000
687 summary: commit on default
687 summary: commit on default
688
688
689
689
690
690
691 log -b test
691 log -b test
692
692
693 $ hg log -b test
693 $ hg log -b test
694 changeset: 3:f5d8de11c2e2
694 changeset: 3:f5d8de11c2e2
695 branch: test
695 branch: test
696 tag: tip
696 tag: tip
697 parent: 1:d32277701ccb
697 parent: 1:d32277701ccb
698 user: test
698 user: test
699 date: Thu Jan 01 00:00:00 1970 +0000
699 date: Thu Jan 01 00:00:00 1970 +0000
700 summary: commit on test
700 summary: commit on test
701
701
702 changeset: 1:d32277701ccb
702 changeset: 1:d32277701ccb
703 branch: test
703 branch: test
704 user: test
704 user: test
705 date: Thu Jan 01 00:00:00 1970 +0000
705 date: Thu Jan 01 00:00:00 1970 +0000
706 summary: commit on test
706 summary: commit on test
707
707
708
708
709
709
710 log -b dummy
710 log -b dummy
711
711
712 $ hg log -b dummy
712 $ hg log -b dummy
713 abort: unknown revision 'dummy'!
713 abort: unknown revision 'dummy'!
714 [255]
714 [255]
715
715
716
716
717 log -b .
717 log -b .
718
718
719 $ hg log -b .
719 $ hg log -b .
720 changeset: 3:f5d8de11c2e2
720 changeset: 3:f5d8de11c2e2
721 branch: test
721 branch: test
722 tag: tip
722 tag: tip
723 parent: 1:d32277701ccb
723 parent: 1:d32277701ccb
724 user: test
724 user: test
725 date: Thu Jan 01 00:00:00 1970 +0000
725 date: Thu Jan 01 00:00:00 1970 +0000
726 summary: commit on test
726 summary: commit on test
727
727
728 changeset: 1:d32277701ccb
728 changeset: 1:d32277701ccb
729 branch: test
729 branch: test
730 user: test
730 user: test
731 date: Thu Jan 01 00:00:00 1970 +0000
731 date: Thu Jan 01 00:00:00 1970 +0000
732 summary: commit on test
732 summary: commit on test
733
733
734
734
735
735
736 log -b default -b test
736 log -b default -b test
737
737
738 $ hg log -b default -b test
738 $ hg log -b default -b test
739 changeset: 3:f5d8de11c2e2
739 changeset: 3:f5d8de11c2e2
740 branch: test
740 branch: test
741 tag: tip
741 tag: tip
742 parent: 1:d32277701ccb
742 parent: 1:d32277701ccb
743 user: test
743 user: test
744 date: Thu Jan 01 00:00:00 1970 +0000
744 date: Thu Jan 01 00:00:00 1970 +0000
745 summary: commit on test
745 summary: commit on test
746
746
747 changeset: 2:c3a4f03cc9a7
747 changeset: 2:c3a4f03cc9a7
748 parent: 0:24427303d56f
748 parent: 0:24427303d56f
749 user: test
749 user: test
750 date: Thu Jan 01 00:00:00 1970 +0000
750 date: Thu Jan 01 00:00:00 1970 +0000
751 summary: commit on default
751 summary: commit on default
752
752
753 changeset: 1:d32277701ccb
753 changeset: 1:d32277701ccb
754 branch: test
754 branch: test
755 user: test
755 user: test
756 date: Thu Jan 01 00:00:00 1970 +0000
756 date: Thu Jan 01 00:00:00 1970 +0000
757 summary: commit on test
757 summary: commit on test
758
758
759 changeset: 0:24427303d56f
759 changeset: 0:24427303d56f
760 user: test
760 user: test
761 date: Thu Jan 01 00:00:00 1970 +0000
761 date: Thu Jan 01 00:00:00 1970 +0000
762 summary: commit on default
762 summary: commit on default
763
763
764
764
765
765
766 log -b default -b .
766 log -b default -b .
767
767
768 $ hg log -b default -b .
768 $ hg log -b default -b .
769 changeset: 3:f5d8de11c2e2
769 changeset: 3:f5d8de11c2e2
770 branch: test
770 branch: test
771 tag: tip
771 tag: tip
772 parent: 1:d32277701ccb
772 parent: 1:d32277701ccb
773 user: test
773 user: test
774 date: Thu Jan 01 00:00:00 1970 +0000
774 date: Thu Jan 01 00:00:00 1970 +0000
775 summary: commit on test
775 summary: commit on test
776
776
777 changeset: 2:c3a4f03cc9a7
777 changeset: 2:c3a4f03cc9a7
778 parent: 0:24427303d56f
778 parent: 0:24427303d56f
779 user: test
779 user: test
780 date: Thu Jan 01 00:00:00 1970 +0000
780 date: Thu Jan 01 00:00:00 1970 +0000
781 summary: commit on default
781 summary: commit on default
782
782
783 changeset: 1:d32277701ccb
783 changeset: 1:d32277701ccb
784 branch: test
784 branch: test
785 user: test
785 user: test
786 date: Thu Jan 01 00:00:00 1970 +0000
786 date: Thu Jan 01 00:00:00 1970 +0000
787 summary: commit on test
787 summary: commit on test
788
788
789 changeset: 0:24427303d56f
789 changeset: 0:24427303d56f
790 user: test
790 user: test
791 date: Thu Jan 01 00:00:00 1970 +0000
791 date: Thu Jan 01 00:00:00 1970 +0000
792 summary: commit on default
792 summary: commit on default
793
793
794
794
795
795
796 log -b . -b test
796 log -b . -b test
797
797
798 $ hg log -b . -b test
798 $ hg log -b . -b test
799 changeset: 3:f5d8de11c2e2
799 changeset: 3:f5d8de11c2e2
800 branch: test
800 branch: test
801 tag: tip
801 tag: tip
802 parent: 1:d32277701ccb
802 parent: 1:d32277701ccb
803 user: test
803 user: test
804 date: Thu Jan 01 00:00:00 1970 +0000
804 date: Thu Jan 01 00:00:00 1970 +0000
805 summary: commit on test
805 summary: commit on test
806
806
807 changeset: 1:d32277701ccb
807 changeset: 1:d32277701ccb
808 branch: test
808 branch: test
809 user: test
809 user: test
810 date: Thu Jan 01 00:00:00 1970 +0000
810 date: Thu Jan 01 00:00:00 1970 +0000
811 summary: commit on test
811 summary: commit on test
812
812
813
813
814
814
815 log -b 2
815 log -b 2
816
816
817 $ hg log -b 2
817 $ hg log -b 2
818 changeset: 2:c3a4f03cc9a7
818 changeset: 2:c3a4f03cc9a7
819 parent: 0:24427303d56f
819 parent: 0:24427303d56f
820 user: test
820 user: test
821 date: Thu Jan 01 00:00:00 1970 +0000
821 date: Thu Jan 01 00:00:00 1970 +0000
822 summary: commit on default
822 summary: commit on default
823
823
824 changeset: 0:24427303d56f
824 changeset: 0:24427303d56f
825 user: test
825 user: test
826 date: Thu Jan 01 00:00:00 1970 +0000
826 date: Thu Jan 01 00:00:00 1970 +0000
827 summary: commit on default
827 summary: commit on default
828
828
829
829
830
830
831 log -p --cwd dir (in subdir)
831 log -p --cwd dir (in subdir)
832
832
833 $ mkdir dir
833 $ mkdir dir
834 $ hg log -p --cwd dir
834 $ hg log -p --cwd dir
835 changeset: 3:f5d8de11c2e2
835 changeset: 3:f5d8de11c2e2
836 branch: test
836 branch: test
837 tag: tip
837 tag: tip
838 parent: 1:d32277701ccb
838 parent: 1:d32277701ccb
839 user: test
839 user: test
840 date: Thu Jan 01 00:00:00 1970 +0000
840 date: Thu Jan 01 00:00:00 1970 +0000
841 summary: commit on test
841 summary: commit on test
842
842
843 diff -r d32277701ccb -r f5d8de11c2e2 c
843 diff -r d32277701ccb -r f5d8de11c2e2 c
844 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
844 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
845 +++ b/c Thu Jan 01 00:00:00 1970 +0000
845 +++ b/c Thu Jan 01 00:00:00 1970 +0000
846 @@ -0,0 +1,1 @@
846 @@ -0,0 +1,1 @@
847 +c
847 +c
848
848
849 changeset: 2:c3a4f03cc9a7
849 changeset: 2:c3a4f03cc9a7
850 parent: 0:24427303d56f
850 parent: 0:24427303d56f
851 user: test
851 user: test
852 date: Thu Jan 01 00:00:00 1970 +0000
852 date: Thu Jan 01 00:00:00 1970 +0000
853 summary: commit on default
853 summary: commit on default
854
854
855 diff -r 24427303d56f -r c3a4f03cc9a7 c
855 diff -r 24427303d56f -r c3a4f03cc9a7 c
856 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
856 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
857 +++ b/c Thu Jan 01 00:00:00 1970 +0000
857 +++ b/c Thu Jan 01 00:00:00 1970 +0000
858 @@ -0,0 +1,1 @@
858 @@ -0,0 +1,1 @@
859 +c
859 +c
860
860
861 changeset: 1:d32277701ccb
861 changeset: 1:d32277701ccb
862 branch: test
862 branch: test
863 user: test
863 user: test
864 date: Thu Jan 01 00:00:00 1970 +0000
864 date: Thu Jan 01 00:00:00 1970 +0000
865 summary: commit on test
865 summary: commit on test
866
866
867 diff -r 24427303d56f -r d32277701ccb b
867 diff -r 24427303d56f -r d32277701ccb b
868 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
868 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
869 +++ b/b Thu Jan 01 00:00:00 1970 +0000
869 +++ b/b Thu Jan 01 00:00:00 1970 +0000
870 @@ -0,0 +1,1 @@
870 @@ -0,0 +1,1 @@
871 +b
871 +b
872
872
873 changeset: 0:24427303d56f
873 changeset: 0:24427303d56f
874 user: test
874 user: test
875 date: Thu Jan 01 00:00:00 1970 +0000
875 date: Thu Jan 01 00:00:00 1970 +0000
876 summary: commit on default
876 summary: commit on default
877
877
878 diff -r 000000000000 -r 24427303d56f a
878 diff -r 000000000000 -r 24427303d56f a
879 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
879 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
880 +++ b/a Thu Jan 01 00:00:00 1970 +0000
880 +++ b/a Thu Jan 01 00:00:00 1970 +0000
881 @@ -0,0 +1,1 @@
881 @@ -0,0 +1,1 @@
882 +a
882 +a
883
883
884
884
885
885
886 log -p -R repo
886 log -p -R repo
887
887
888 $ cd dir
888 $ cd dir
889 $ hg log -p -R .. ../a
889 $ hg log -p -R .. ../a
890 changeset: 0:24427303d56f
890 changeset: 0:24427303d56f
891 user: test
891 user: test
892 date: Thu Jan 01 00:00:00 1970 +0000
892 date: Thu Jan 01 00:00:00 1970 +0000
893 summary: commit on default
893 summary: commit on default
894
894
895 diff -r 000000000000 -r 24427303d56f a
895 diff -r 000000000000 -r 24427303d56f a
896 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
896 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
897 +++ b/a Thu Jan 01 00:00:00 1970 +0000
897 +++ b/a Thu Jan 01 00:00:00 1970 +0000
898 @@ -0,0 +1,1 @@
898 @@ -0,0 +1,1 @@
899 +a
899 +a
900
900
901
901
902 $ cd ../..
902 $ cd ../..
903
903
904 $ hg init follow2
904 $ hg init follow2
905 $ cd follow2
905 $ cd follow2
906
906
907 # Build the following history:
907 # Build the following history:
908 # tip - o - x - o - x - x
908 # tip - o - x - o - x - x
909 # \ /
909 # \ /
910 # o - o - o - x
910 # o - o - o - x
911 # \ /
911 # \ /
912 # o
912 # o
913 #
913 #
914 # Where "o" is a revision containing "foo" and
914 # Where "o" is a revision containing "foo" and
915 # "x" is a revision without "foo"
915 # "x" is a revision without "foo"
916
916
917 $ touch init
917 $ touch init
918 $ hg ci -A -m "init, unrelated"
918 $ hg ci -A -m "init, unrelated"
919 adding init
919 adding init
920 $ echo 'foo' > init
920 $ echo 'foo' > init
921 $ hg ci -m "change, unrelated"
921 $ hg ci -m "change, unrelated"
922 $ echo 'foo' > foo
922 $ echo 'foo' > foo
923 $ hg ci -A -m "add unrelated old foo"
923 $ hg ci -A -m "add unrelated old foo"
924 adding foo
924 adding foo
925 $ hg rm foo
925 $ hg rm foo
926 $ hg ci -m "delete foo, unrelated"
926 $ hg ci -m "delete foo, unrelated"
927 $ echo 'related' > foo
927 $ echo 'related' > foo
928 $ hg ci -A -m "add foo, related"
928 $ hg ci -A -m "add foo, related"
929 adding foo
929 adding foo
930
930
931 $ hg up 0
931 $ hg up 0
932 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
932 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
933 $ touch branch
933 $ touch branch
934 $ hg ci -A -m "first branch, unrelated"
934 $ hg ci -A -m "first branch, unrelated"
935 adding branch
935 adding branch
936 created new head
936 created new head
937 $ touch foo
937 $ touch foo
938 $ hg ci -A -m "create foo, related"
938 $ hg ci -A -m "create foo, related"
939 adding foo
939 adding foo
940 $ echo 'change' > foo
940 $ echo 'change' > foo
941 $ hg ci -m "change foo, related"
941 $ hg ci -m "change foo, related"
942
942
943 $ hg up 6
943 $ hg up 6
944 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
944 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
945 $ echo 'change foo in branch' > foo
945 $ echo 'change foo in branch' > foo
946 $ hg ci -m "change foo in branch, related"
946 $ hg ci -m "change foo in branch, related"
947 created new head
947 created new head
948 $ hg merge 7
948 $ hg merge 7
949 merging foo
949 merging foo
950 warning: conflicts during merge.
950 warning: conflicts during merge.
951 merging foo incomplete! (edit conflicts, then use 'hg resolve --mark')
951 merging foo incomplete! (edit conflicts, then use 'hg resolve --mark')
952 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
952 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
953 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
953 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
954 [1]
954 [1]
955 $ echo 'merge 1' > foo
955 $ echo 'merge 1' > foo
956 $ hg resolve -m foo
956 $ hg resolve -m foo
957 $ hg ci -m "First merge, related"
957 $ hg ci -m "First merge, related"
958
958
959 $ hg merge 4
959 $ hg merge 4
960 merging foo
960 merging foo
961 warning: conflicts during merge.
961 warning: conflicts during merge.
962 merging foo incomplete! (edit conflicts, then use 'hg resolve --mark')
962 merging foo incomplete! (edit conflicts, then use 'hg resolve --mark')
963 1 files updated, 0 files merged, 0 files removed, 1 files unresolved
963 1 files updated, 0 files merged, 0 files removed, 1 files unresolved
964 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
964 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
965 [1]
965 [1]
966 $ echo 'merge 2' > foo
966 $ echo 'merge 2' > foo
967 $ hg resolve -m foo
967 $ hg resolve -m foo
968 $ hg ci -m "Last merge, related"
968 $ hg ci -m "Last merge, related"
969
969
970 $ hg log --graph
970 $ hg log --graph
971 @ changeset: 10:4dae8563d2c5
971 @ changeset: 10:4dae8563d2c5
972 |\ tag: tip
972 |\ tag: tip
973 | | parent: 9:7b35701b003e
973 | | parent: 9:7b35701b003e
974 | | parent: 4:88176d361b69
974 | | parent: 4:88176d361b69
975 | | user: test
975 | | user: test
976 | | date: Thu Jan 01 00:00:00 1970 +0000
976 | | date: Thu Jan 01 00:00:00 1970 +0000
977 | | summary: Last merge, related
977 | | summary: Last merge, related
978 | |
978 | |
979 | o changeset: 9:7b35701b003e
979 | o changeset: 9:7b35701b003e
980 | |\ parent: 8:e5416ad8a855
980 | |\ parent: 8:e5416ad8a855
981 | | | parent: 7:87fe3144dcfa
981 | | | parent: 7:87fe3144dcfa
982 | | | user: test
982 | | | user: test
983 | | | date: Thu Jan 01 00:00:00 1970 +0000
983 | | | date: Thu Jan 01 00:00:00 1970 +0000
984 | | | summary: First merge, related
984 | | | summary: First merge, related
985 | | |
985 | | |
986 | | o changeset: 8:e5416ad8a855
986 | | o changeset: 8:e5416ad8a855
987 | | | parent: 6:dc6c325fe5ee
987 | | | parent: 6:dc6c325fe5ee
988 | | | user: test
988 | | | user: test
989 | | | date: Thu Jan 01 00:00:00 1970 +0000
989 | | | date: Thu Jan 01 00:00:00 1970 +0000
990 | | | summary: change foo in branch, related
990 | | | summary: change foo in branch, related
991 | | |
991 | | |
992 | o | changeset: 7:87fe3144dcfa
992 | o | changeset: 7:87fe3144dcfa
993 | |/ user: test
993 | |/ user: test
994 | | date: Thu Jan 01 00:00:00 1970 +0000
994 | | date: Thu Jan 01 00:00:00 1970 +0000
995 | | summary: change foo, related
995 | | summary: change foo, related
996 | |
996 | |
997 | o changeset: 6:dc6c325fe5ee
997 | o changeset: 6:dc6c325fe5ee
998 | | user: test
998 | | user: test
999 | | date: Thu Jan 01 00:00:00 1970 +0000
999 | | date: Thu Jan 01 00:00:00 1970 +0000
1000 | | summary: create foo, related
1000 | | summary: create foo, related
1001 | |
1001 | |
1002 | o changeset: 5:73db34516eb9
1002 | o changeset: 5:73db34516eb9
1003 | | parent: 0:e87515fd044a
1003 | | parent: 0:e87515fd044a
1004 | | user: test
1004 | | user: test
1005 | | date: Thu Jan 01 00:00:00 1970 +0000
1005 | | date: Thu Jan 01 00:00:00 1970 +0000
1006 | | summary: first branch, unrelated
1006 | | summary: first branch, unrelated
1007 | |
1007 | |
1008 o | changeset: 4:88176d361b69
1008 o | changeset: 4:88176d361b69
1009 | | user: test
1009 | | user: test
1010 | | date: Thu Jan 01 00:00:00 1970 +0000
1010 | | date: Thu Jan 01 00:00:00 1970 +0000
1011 | | summary: add foo, related
1011 | | summary: add foo, related
1012 | |
1012 | |
1013 o | changeset: 3:dd78ae4afb56
1013 o | changeset: 3:dd78ae4afb56
1014 | | user: test
1014 | | user: test
1015 | | date: Thu Jan 01 00:00:00 1970 +0000
1015 | | date: Thu Jan 01 00:00:00 1970 +0000
1016 | | summary: delete foo, unrelated
1016 | | summary: delete foo, unrelated
1017 | |
1017 | |
1018 o | changeset: 2:c4c64aedf0f7
1018 o | changeset: 2:c4c64aedf0f7
1019 | | user: test
1019 | | user: test
1020 | | date: Thu Jan 01 00:00:00 1970 +0000
1020 | | date: Thu Jan 01 00:00:00 1970 +0000
1021 | | summary: add unrelated old foo
1021 | | summary: add unrelated old foo
1022 | |
1022 | |
1023 o | changeset: 1:e5faa7440653
1023 o | changeset: 1:e5faa7440653
1024 |/ user: test
1024 |/ user: test
1025 | date: Thu Jan 01 00:00:00 1970 +0000
1025 | date: Thu Jan 01 00:00:00 1970 +0000
1026 | summary: change, unrelated
1026 | summary: change, unrelated
1027 |
1027 |
1028 o changeset: 0:e87515fd044a
1028 o changeset: 0:e87515fd044a
1029 user: test
1029 user: test
1030 date: Thu Jan 01 00:00:00 1970 +0000
1030 date: Thu Jan 01 00:00:00 1970 +0000
1031 summary: init, unrelated
1031 summary: init, unrelated
1032
1032
1033
1033
1034 $ hg --traceback log -f foo
1034 $ hg --traceback log -f foo
1035 changeset: 10:4dae8563d2c5
1035 changeset: 10:4dae8563d2c5
1036 tag: tip
1036 tag: tip
1037 parent: 9:7b35701b003e
1037 parent: 9:7b35701b003e
1038 parent: 4:88176d361b69
1038 parent: 4:88176d361b69
1039 user: test
1039 user: test
1040 date: Thu Jan 01 00:00:00 1970 +0000
1040 date: Thu Jan 01 00:00:00 1970 +0000
1041 summary: Last merge, related
1041 summary: Last merge, related
1042
1042
1043 changeset: 9:7b35701b003e
1043 changeset: 9:7b35701b003e
1044 parent: 8:e5416ad8a855
1044 parent: 8:e5416ad8a855
1045 parent: 7:87fe3144dcfa
1045 parent: 7:87fe3144dcfa
1046 user: test
1046 user: test
1047 date: Thu Jan 01 00:00:00 1970 +0000
1047 date: Thu Jan 01 00:00:00 1970 +0000
1048 summary: First merge, related
1048 summary: First merge, related
1049
1049
1050 changeset: 8:e5416ad8a855
1050 changeset: 8:e5416ad8a855
1051 parent: 6:dc6c325fe5ee
1051 parent: 6:dc6c325fe5ee
1052 user: test
1052 user: test
1053 date: Thu Jan 01 00:00:00 1970 +0000
1053 date: Thu Jan 01 00:00:00 1970 +0000
1054 summary: change foo in branch, related
1054 summary: change foo in branch, related
1055
1055
1056 changeset: 7:87fe3144dcfa
1056 changeset: 7:87fe3144dcfa
1057 user: test
1057 user: test
1058 date: Thu Jan 01 00:00:00 1970 +0000
1058 date: Thu Jan 01 00:00:00 1970 +0000
1059 summary: change foo, related
1059 summary: change foo, related
1060
1060
1061 changeset: 6:dc6c325fe5ee
1061 changeset: 6:dc6c325fe5ee
1062 user: test
1062 user: test
1063 date: Thu Jan 01 00:00:00 1970 +0000
1063 date: Thu Jan 01 00:00:00 1970 +0000
1064 summary: create foo, related
1064 summary: create foo, related
1065
1065
1066 changeset: 4:88176d361b69
1066 changeset: 4:88176d361b69
1067 user: test
1067 user: test
1068 date: Thu Jan 01 00:00:00 1970 +0000
1068 date: Thu Jan 01 00:00:00 1970 +0000
1069 summary: add foo, related
1069 summary: add foo, related
1070
1070
1071
1071
1072 Also check when maxrev < lastrevfilelog
1072 Also check when maxrev < lastrevfilelog
1073
1073
1074 $ hg --traceback log -f -r4 foo
1074 $ hg --traceback log -f -r4 foo
1075 changeset: 4:88176d361b69
1075 changeset: 4:88176d361b69
1076 user: test
1076 user: test
1077 date: Thu Jan 01 00:00:00 1970 +0000
1077 date: Thu Jan 01 00:00:00 1970 +0000
1078 summary: add foo, related
1078 summary: add foo, related
1079
1079
1080 $ cd ..
1080 $ cd ..
1081
1081
1082 Issue2383: hg log showing _less_ differences than hg diff
1082 Issue2383: hg log showing _less_ differences than hg diff
1083
1083
1084 $ hg init issue2383
1084 $ hg init issue2383
1085 $ cd issue2383
1085 $ cd issue2383
1086
1086
1087 Create a test repo:
1087 Create a test repo:
1088
1088
1089 $ echo a > a
1089 $ echo a > a
1090 $ hg ci -Am0
1090 $ hg ci -Am0
1091 adding a
1091 adding a
1092 $ echo b > b
1092 $ echo b > b
1093 $ hg ci -Am1
1093 $ hg ci -Am1
1094 adding b
1094 adding b
1095 $ hg co 0
1095 $ hg co 0
1096 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1096 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1097 $ echo b > a
1097 $ echo b > a
1098 $ hg ci -m2
1098 $ hg ci -m2
1099 created new head
1099 created new head
1100
1100
1101 Merge:
1101 Merge:
1102
1102
1103 $ hg merge
1103 $ hg merge
1104 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1104 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1105 (branch merge, don't forget to commit)
1105 (branch merge, don't forget to commit)
1106
1106
1107 Make sure there's a file listed in the merge to trigger the bug:
1107 Make sure there's a file listed in the merge to trigger the bug:
1108
1108
1109 $ echo c > a
1109 $ echo c > a
1110 $ hg ci -m3
1110 $ hg ci -m3
1111
1111
1112 Two files shown here in diff:
1112 Two files shown here in diff:
1113
1113
1114 $ hg diff --rev 2:3
1114 $ hg diff --rev 2:3
1115 diff -r b09be438c43a -r 8e07aafe1edc a
1115 diff -r b09be438c43a -r 8e07aafe1edc a
1116 --- a/a Thu Jan 01 00:00:00 1970 +0000
1116 --- a/a Thu Jan 01 00:00:00 1970 +0000
1117 +++ b/a Thu Jan 01 00:00:00 1970 +0000
1117 +++ b/a Thu Jan 01 00:00:00 1970 +0000
1118 @@ -1,1 +1,1 @@
1118 @@ -1,1 +1,1 @@
1119 -b
1119 -b
1120 +c
1120 +c
1121 diff -r b09be438c43a -r 8e07aafe1edc b
1121 diff -r b09be438c43a -r 8e07aafe1edc b
1122 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1122 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1123 +++ b/b Thu Jan 01 00:00:00 1970 +0000
1123 +++ b/b Thu Jan 01 00:00:00 1970 +0000
1124 @@ -0,0 +1,1 @@
1124 @@ -0,0 +1,1 @@
1125 +b
1125 +b
1126
1126
1127 Diff here should be the same:
1127 Diff here should be the same:
1128
1128
1129 $ hg log -vpr 3
1129 $ hg log -vpr 3
1130 changeset: 3:8e07aafe1edc
1130 changeset: 3:8e07aafe1edc
1131 tag: tip
1131 tag: tip
1132 parent: 2:b09be438c43a
1132 parent: 2:b09be438c43a
1133 parent: 1:925d80f479bb
1133 parent: 1:925d80f479bb
1134 user: test
1134 user: test
1135 date: Thu Jan 01 00:00:00 1970 +0000
1135 date: Thu Jan 01 00:00:00 1970 +0000
1136 files: a
1136 files: a
1137 description:
1137 description:
1138 3
1138 3
1139
1139
1140
1140
1141 diff -r b09be438c43a -r 8e07aafe1edc a
1141 diff -r b09be438c43a -r 8e07aafe1edc a
1142 --- a/a Thu Jan 01 00:00:00 1970 +0000
1142 --- a/a Thu Jan 01 00:00:00 1970 +0000
1143 +++ b/a Thu Jan 01 00:00:00 1970 +0000
1143 +++ b/a Thu Jan 01 00:00:00 1970 +0000
1144 @@ -1,1 +1,1 @@
1144 @@ -1,1 +1,1 @@
1145 -b
1145 -b
1146 +c
1146 +c
1147 diff -r b09be438c43a -r 8e07aafe1edc b
1147 diff -r b09be438c43a -r 8e07aafe1edc b
1148 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1148 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1149 +++ b/b Thu Jan 01 00:00:00 1970 +0000
1149 +++ b/b Thu Jan 01 00:00:00 1970 +0000
1150 @@ -0,0 +1,1 @@
1150 @@ -0,0 +1,1 @@
1151 +b
1151 +b
1152
1152
1153 $ cd ..
1153 $ cd ..
1154
1154
1155 'hg log -r rev fn' when last(filelog(fn)) != rev
1155 'hg log -r rev fn' when last(filelog(fn)) != rev
1156
1156
1157 $ hg init simplelog
1157 $ hg init simplelog
1158 $ cd simplelog
1158 $ cd simplelog
1159 $ echo f > a
1159 $ echo f > a
1160 $ hg ci -Am'a' -d '0 0'
1160 $ hg ci -Am'a' -d '0 0'
1161 adding a
1161 adding a
1162 $ echo f >> a
1162 $ echo f >> a
1163 $ hg ci -Am'a bis' -d '1 0'
1163 $ hg ci -Am'a bis' -d '1 0'
1164
1164
1165 $ hg log -r0 a
1165 $ hg log -r0 a
1166 changeset: 0:9f758d63dcde
1166 changeset: 0:9f758d63dcde
1167 user: test
1167 user: test
1168 date: Thu Jan 01 00:00:00 1970 +0000
1168 date: Thu Jan 01 00:00:00 1970 +0000
1169 summary: a
1169 summary: a
1170
1170
1171 enable obsolete to test hidden feature
1171 enable obsolete to test hidden feature
1172
1172
1173 $ cat > ${TESTTMP}/obs.py << EOF
1173 $ cat > ${TESTTMP}/obs.py << EOF
1174 > import mercurial.obsolete
1174 > import mercurial.obsolete
1175 > mercurial.obsolete._enabled = True
1175 > mercurial.obsolete._enabled = True
1176 > EOF
1176 > EOF
1177 $ echo '[extensions]' >> $HGRCPATH
1177 $ echo '[extensions]' >> $HGRCPATH
1178 $ echo "obs=${TESTTMP}/obs.py" >> $HGRCPATH
1178 $ echo "obs=${TESTTMP}/obs.py" >> $HGRCPATH
1179
1179
1180 $ hg log --template='{rev}:{node}\n'
1180 $ hg log --template='{rev}:{node}\n'
1181 1:a765632148dc55d38c35c4f247c618701886cb2f
1181 1:a765632148dc55d38c35c4f247c618701886cb2f
1182 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1182 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1183 $ hg debugobsolete a765632148dc55d38c35c4f247c618701886cb2f
1183 $ hg debugobsolete a765632148dc55d38c35c4f247c618701886cb2f
1184 $ hg up null -q
1184 $ hg up null -q
1185 $ hg log --template='{rev}:{node}\n'
1185 $ hg log --template='{rev}:{node}\n'
1186 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1186 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1187 $ hg log --template='{rev}:{node}\n' --hidden
1187 $ hg log --template='{rev}:{node}\n' --hidden
1188 1:a765632148dc55d38c35c4f247c618701886cb2f
1188 1:a765632148dc55d38c35c4f247c618701886cb2f
1189 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1189 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1190
1190
1191 test that parent prevent a changeset to be hidden
1191 test that parent prevent a changeset to be hidden
1192
1192
1193 $ hg up 1 -q --hidden
1193 $ hg up 1 -q --hidden
1194 $ hg log --template='{rev}:{node}\n'
1194 $ hg log --template='{rev}:{node}\n'
1195 1:a765632148dc55d38c35c4f247c618701886cb2f
1195 1:a765632148dc55d38c35c4f247c618701886cb2f
1196 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1196 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1197
1197
1198 test that second parent prevent a changeset to be hidden too
1198 test that second parent prevent a changeset to be hidden too
1199
1199
1200 $ hg debugsetparents 0 1 # nothing suitable to merge here
1200 $ hg debugsetparents 0 1 # nothing suitable to merge here
1201 $ hg log --template='{rev}:{node}\n'
1201 $ hg log --template='{rev}:{node}\n'
1202 1:a765632148dc55d38c35c4f247c618701886cb2f
1202 1:a765632148dc55d38c35c4f247c618701886cb2f
1203 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1203 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1204 $ hg debugsetparents 1
1204 $ hg debugsetparents 1
1205 $ hg up -q null
1205 $ hg up -q null
1206
1206
1207 bookmarks prevent a changeset being hidden
1207 bookmarks prevent a changeset being hidden
1208
1208
1209 $ hg bookmark --hidden -r 1 X
1209 $ hg bookmark --hidden -r 1 X
1210 $ hg log --template '{rev}:{node}\n'
1210 $ hg log --template '{rev}:{node}\n'
1211 1:a765632148dc55d38c35c4f247c618701886cb2f
1211 1:a765632148dc55d38c35c4f247c618701886cb2f
1212 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1212 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1213 $ hg bookmark -d X
1214
1215 divergent bookmarks are not hidden
1216
1217 $ hg bookmark --hidden -r 1 X@foo
1218 $ hg log --template '{rev}:{node}\n'
1219 1:a765632148dc55d38c35c4f247c618701886cb2f
1220 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1213
1221
1214 clear extensions configuration
1222 clear extensions configuration
1215 $ echo '[extensions]' >> $HGRCPATH
1223 $ echo '[extensions]' >> $HGRCPATH
1216 $ echo "obs=!" >> $HGRCPATH
1224 $ echo "obs=!" >> $HGRCPATH
1217 $ cd ..
1225 $ cd ..
1218
1226
1219 test -u/-k for problematic encoding
1227 test -u/-k for problematic encoding
1220 # unicode: cp932:
1228 # unicode: cp932:
1221 # u30A2 0x83 0x41(= 'A')
1229 # u30A2 0x83 0x41(= 'A')
1222 # u30C2 0x83 0x61(= 'a')
1230 # u30C2 0x83 0x61(= 'a')
1223
1231
1224 $ hg init problematicencoding
1232 $ hg init problematicencoding
1225 $ cd problematicencoding
1233 $ cd problematicencoding
1226
1234
1227 $ python > setup.sh <<EOF
1235 $ python > setup.sh <<EOF
1228 > print u'''
1236 > print u'''
1229 > echo a > text
1237 > echo a > text
1230 > hg add text
1238 > hg add text
1231 > hg --encoding utf-8 commit -u '\u30A2' -m none
1239 > hg --encoding utf-8 commit -u '\u30A2' -m none
1232 > echo b > text
1240 > echo b > text
1233 > hg --encoding utf-8 commit -u '\u30C2' -m none
1241 > hg --encoding utf-8 commit -u '\u30C2' -m none
1234 > echo c > text
1242 > echo c > text
1235 > hg --encoding utf-8 commit -u none -m '\u30A2'
1243 > hg --encoding utf-8 commit -u none -m '\u30A2'
1236 > echo d > text
1244 > echo d > text
1237 > hg --encoding utf-8 commit -u none -m '\u30C2'
1245 > hg --encoding utf-8 commit -u none -m '\u30C2'
1238 > '''.encode('utf-8')
1246 > '''.encode('utf-8')
1239 > EOF
1247 > EOF
1240 $ sh < setup.sh
1248 $ sh < setup.sh
1241
1249
1242 test in problematic encoding
1250 test in problematic encoding
1243 $ python > test.sh <<EOF
1251 $ python > test.sh <<EOF
1244 > print u'''
1252 > print u'''
1245 > hg --encoding cp932 log --template '{rev}\\n' -u '\u30A2'
1253 > hg --encoding cp932 log --template '{rev}\\n' -u '\u30A2'
1246 > echo ====
1254 > echo ====
1247 > hg --encoding cp932 log --template '{rev}\\n' -u '\u30C2'
1255 > hg --encoding cp932 log --template '{rev}\\n' -u '\u30C2'
1248 > echo ====
1256 > echo ====
1249 > hg --encoding cp932 log --template '{rev}\\n' -k '\u30A2'
1257 > hg --encoding cp932 log --template '{rev}\\n' -k '\u30A2'
1250 > echo ====
1258 > echo ====
1251 > hg --encoding cp932 log --template '{rev}\\n' -k '\u30C2'
1259 > hg --encoding cp932 log --template '{rev}\\n' -k '\u30C2'
1252 > '''.encode('cp932')
1260 > '''.encode('cp932')
1253 > EOF
1261 > EOF
1254 $ sh < test.sh
1262 $ sh < test.sh
1255 0
1263 0
1256 ====
1264 ====
1257 1
1265 1
1258 ====
1266 ====
1259 2
1267 2
1260 0
1268 0
1261 ====
1269 ====
1262 3
1270 3
1263 1
1271 1
1264
1272
1265 $ cd ..
1273 $ cd ..
1266
1274
1267 test hg log on non-existent files and on directories
1275 test hg log on non-existent files and on directories
1268 $ hg init issue1340
1276 $ hg init issue1340
1269 $ cd issue1340
1277 $ cd issue1340
1270 $ mkdir d1; mkdir D2; mkdir D3.i; mkdir d4.hg; mkdir d5.d; mkdir .d6
1278 $ mkdir d1; mkdir D2; mkdir D3.i; mkdir d4.hg; mkdir d5.d; mkdir .d6
1271 $ echo 1 > d1/f1
1279 $ echo 1 > d1/f1
1272 $ echo 1 > D2/f1
1280 $ echo 1 > D2/f1
1273 $ echo 1 > D3.i/f1
1281 $ echo 1 > D3.i/f1
1274 $ echo 1 > d4.hg/f1
1282 $ echo 1 > d4.hg/f1
1275 $ echo 1 > d5.d/f1
1283 $ echo 1 > d5.d/f1
1276 $ echo 1 > .d6/f1
1284 $ echo 1 > .d6/f1
1277 $ hg -q add .
1285 $ hg -q add .
1278 $ hg commit -m "a bunch of weird directories"
1286 $ hg commit -m "a bunch of weird directories"
1279 $ hg log -l1 d1/f1 | grep changeset
1287 $ hg log -l1 d1/f1 | grep changeset
1280 changeset: 0:65624cd9070a
1288 changeset: 0:65624cd9070a
1281 $ hg log -l1 f1
1289 $ hg log -l1 f1
1282 $ hg log -l1 . | grep changeset
1290 $ hg log -l1 . | grep changeset
1283 changeset: 0:65624cd9070a
1291 changeset: 0:65624cd9070a
1284 $ hg log -l1 ./ | grep changeset
1292 $ hg log -l1 ./ | grep changeset
1285 changeset: 0:65624cd9070a
1293 changeset: 0:65624cd9070a
1286 $ hg log -l1 d1 | grep changeset
1294 $ hg log -l1 d1 | grep changeset
1287 changeset: 0:65624cd9070a
1295 changeset: 0:65624cd9070a
1288 $ hg log -l1 D2 | grep changeset
1296 $ hg log -l1 D2 | grep changeset
1289 changeset: 0:65624cd9070a
1297 changeset: 0:65624cd9070a
1290 $ hg log -l1 D2/f1 | grep changeset
1298 $ hg log -l1 D2/f1 | grep changeset
1291 changeset: 0:65624cd9070a
1299 changeset: 0:65624cd9070a
1292 $ hg log -l1 D3.i | grep changeset
1300 $ hg log -l1 D3.i | grep changeset
1293 changeset: 0:65624cd9070a
1301 changeset: 0:65624cd9070a
1294 $ hg log -l1 D3.i/f1 | grep changeset
1302 $ hg log -l1 D3.i/f1 | grep changeset
1295 changeset: 0:65624cd9070a
1303 changeset: 0:65624cd9070a
1296 $ hg log -l1 d4.hg | grep changeset
1304 $ hg log -l1 d4.hg | grep changeset
1297 changeset: 0:65624cd9070a
1305 changeset: 0:65624cd9070a
1298 $ hg log -l1 d4.hg/f1 | grep changeset
1306 $ hg log -l1 d4.hg/f1 | grep changeset
1299 changeset: 0:65624cd9070a
1307 changeset: 0:65624cd9070a
1300 $ hg log -l1 d5.d | grep changeset
1308 $ hg log -l1 d5.d | grep changeset
1301 changeset: 0:65624cd9070a
1309 changeset: 0:65624cd9070a
1302 $ hg log -l1 d5.d/f1 | grep changeset
1310 $ hg log -l1 d5.d/f1 | grep changeset
1303 changeset: 0:65624cd9070a
1311 changeset: 0:65624cd9070a
1304 $ hg log -l1 .d6 | grep changeset
1312 $ hg log -l1 .d6 | grep changeset
1305 changeset: 0:65624cd9070a
1313 changeset: 0:65624cd9070a
1306 $ hg log -l1 .d6/f1 | grep changeset
1314 $ hg log -l1 .d6/f1 | grep changeset
1307 changeset: 0:65624cd9070a
1315 changeset: 0:65624cd9070a
1308
1316
1309 issue3772: hg log -r :null showing revision 0 as well
1317 issue3772: hg log -r :null showing revision 0 as well
1310
1318
1311 $ hg log -r :null
1319 $ hg log -r :null
1312 changeset: -1:000000000000
1320 changeset: -1:000000000000
1313 user:
1321 user:
1314 date: Thu Jan 01 00:00:00 1970 +0000
1322 date: Thu Jan 01 00:00:00 1970 +0000
1315
1323
1316 $ hg log -r null:null
1324 $ hg log -r null:null
1317 changeset: -1:000000000000
1325 changeset: -1:000000000000
1318 user:
1326 user:
1319 date: Thu Jan 01 00:00:00 1970 +0000
1327 date: Thu Jan 01 00:00:00 1970 +0000
1320
1328
1321
1329
1322 $ cd ..
1330 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now