##// END OF EJS Templates
git: copy findmissingrevs() from revlog.py to gitlog.py (issue6472)
av6 -
r50281:1e12ea7d stable
parent child Browse files
Show More
@@ -1,546 +1,570 b''
1 from mercurial.i18n import _
1 from mercurial.i18n import _
2
2
3 from mercurial.node import (
3 from mercurial.node import (
4 bin,
4 bin,
5 hex,
5 hex,
6 nullrev,
6 nullrev,
7 sha1nodeconstants,
7 sha1nodeconstants,
8 )
8 )
9 from mercurial import (
9 from mercurial import (
10 ancestor,
10 ancestor,
11 changelog as hgchangelog,
11 changelog as hgchangelog,
12 dagop,
12 dagop,
13 encoding,
13 encoding,
14 error,
14 error,
15 manifest,
15 manifest,
16 pycompat,
16 pycompat,
17 )
17 )
18 from mercurial.interfaces import (
18 from mercurial.interfaces import (
19 repository,
19 repository,
20 util as interfaceutil,
20 util as interfaceutil,
21 )
21 )
22 from mercurial.utils import stringutil
22 from mercurial.utils import stringutil
23 from . import (
23 from . import (
24 gitutil,
24 gitutil,
25 index,
25 index,
26 manifest as gitmanifest,
26 manifest as gitmanifest,
27 )
27 )
28
28
29 pygit2 = gitutil.get_pygit2()
29 pygit2 = gitutil.get_pygit2()
30
30
31
31
32 class baselog: # revlog.revlog):
32 class baselog: # revlog.revlog):
33 """Common implementations between changelog and manifestlog."""
33 """Common implementations between changelog and manifestlog."""
34
34
35 def __init__(self, gr, db):
35 def __init__(self, gr, db):
36 self.gitrepo = gr
36 self.gitrepo = gr
37 self._db = db
37 self._db = db
38
38
39 def __len__(self):
39 def __len__(self):
40 return int(
40 return int(
41 self._db.execute('SELECT COUNT(*) FROM changelog').fetchone()[0]
41 self._db.execute('SELECT COUNT(*) FROM changelog').fetchone()[0]
42 )
42 )
43
43
44 def rev(self, n):
44 def rev(self, n):
45 if n == sha1nodeconstants.nullid:
45 if n == sha1nodeconstants.nullid:
46 return -1
46 return -1
47 t = self._db.execute(
47 t = self._db.execute(
48 'SELECT rev FROM changelog WHERE node = ?', (gitutil.togitnode(n),)
48 'SELECT rev FROM changelog WHERE node = ?', (gitutil.togitnode(n),)
49 ).fetchone()
49 ).fetchone()
50 if t is None:
50 if t is None:
51 raise error.LookupError(n, b'00changelog.i', _(b'no node %d'))
51 raise error.LookupError(n, b'00changelog.i', _(b'no node %d'))
52 return t[0]
52 return t[0]
53
53
54 def node(self, r):
54 def node(self, r):
55 if r == nullrev:
55 if r == nullrev:
56 return sha1nodeconstants.nullid
56 return sha1nodeconstants.nullid
57 t = self._db.execute(
57 t = self._db.execute(
58 'SELECT node FROM changelog WHERE rev = ?', (r,)
58 'SELECT node FROM changelog WHERE rev = ?', (r,)
59 ).fetchone()
59 ).fetchone()
60 if t is None:
60 if t is None:
61 raise error.LookupError(r, b'00changelog.i', _(b'no node'))
61 raise error.LookupError(r, b'00changelog.i', _(b'no node'))
62 return bin(t[0])
62 return bin(t[0])
63
63
64 def hasnode(self, n):
64 def hasnode(self, n):
65 t = self._db.execute(
65 t = self._db.execute(
66 'SELECT node FROM changelog WHERE node = ?',
66 'SELECT node FROM changelog WHERE node = ?',
67 (pycompat.sysstr(n),),
67 (pycompat.sysstr(n),),
68 ).fetchone()
68 ).fetchone()
69 return t is not None
69 return t is not None
70
70
71
71
72 class baselogindex:
72 class baselogindex:
73 def __init__(self, log):
73 def __init__(self, log):
74 self._log = log
74 self._log = log
75
75
76 def has_node(self, n):
76 def has_node(self, n):
77 return self._log.rev(n) != -1
77 return self._log.rev(n) != -1
78
78
79 def __len__(self):
79 def __len__(self):
80 return len(self._log)
80 return len(self._log)
81
81
82 def __getitem__(self, idx):
82 def __getitem__(self, idx):
83 p1rev, p2rev = self._log.parentrevs(idx)
83 p1rev, p2rev = self._log.parentrevs(idx)
84 # TODO: it's messy that the index leaks so far out of the
84 # TODO: it's messy that the index leaks so far out of the
85 # storage layer that we have to implement things like reading
85 # storage layer that we have to implement things like reading
86 # this raw tuple, which exposes revlog internals.
86 # this raw tuple, which exposes revlog internals.
87 return (
87 return (
88 # Pretend offset is just the index, since we don't really care.
88 # Pretend offset is just the index, since we don't really care.
89 idx,
89 idx,
90 # Same with lengths
90 # Same with lengths
91 idx, # length
91 idx, # length
92 idx, # rawsize
92 idx, # rawsize
93 -1, # delta base
93 -1, # delta base
94 idx, # linkrev TODO is this right?
94 idx, # linkrev TODO is this right?
95 p1rev,
95 p1rev,
96 p2rev,
96 p2rev,
97 self._log.node(idx),
97 self._log.node(idx),
98 )
98 )
99
99
100
100
101 # TODO: an interface for the changelog type?
101 # TODO: an interface for the changelog type?
102 class changelog(baselog):
102 class changelog(baselog):
103 # TODO: this appears to be an enumerated type, and should probably
103 # TODO: this appears to be an enumerated type, and should probably
104 # be part of the public changelog interface
104 # be part of the public changelog interface
105 _copiesstorage = b'extra'
105 _copiesstorage = b'extra'
106
106
107 def __contains__(self, rev):
107 def __contains__(self, rev):
108 try:
108 try:
109 self.node(rev)
109 self.node(rev)
110 return True
110 return True
111 except error.LookupError:
111 except error.LookupError:
112 return False
112 return False
113
113
114 def __iter__(self):
114 def __iter__(self):
115 return iter(range(len(self)))
115 return iter(range(len(self)))
116
116
117 @property
117 @property
118 def filteredrevs(self):
118 def filteredrevs(self):
119 # TODO: we should probably add a refs/hg/ namespace for hidden
119 # TODO: we should probably add a refs/hg/ namespace for hidden
120 # heads etc, but that's an idea for later.
120 # heads etc, but that's an idea for later.
121 return set()
121 return set()
122
122
123 @property
123 @property
124 def index(self):
124 def index(self):
125 return baselogindex(self)
125 return baselogindex(self)
126
126
127 @property
127 @property
128 def nodemap(self):
128 def nodemap(self):
129 r = {
129 r = {
130 bin(v[0]): v[1]
130 bin(v[0]): v[1]
131 for v in self._db.execute('SELECT node, rev FROM changelog')
131 for v in self._db.execute('SELECT node, rev FROM changelog')
132 }
132 }
133 r[sha1nodeconstants.nullid] = nullrev
133 r[sha1nodeconstants.nullid] = nullrev
134 return r
134 return r
135
135
136 def tip(self):
136 def tip(self):
137 t = self._db.execute(
137 t = self._db.execute(
138 'SELECT node FROM changelog ORDER BY rev DESC LIMIT 1'
138 'SELECT node FROM changelog ORDER BY rev DESC LIMIT 1'
139 ).fetchone()
139 ).fetchone()
140 if t:
140 if t:
141 return bin(t[0])
141 return bin(t[0])
142 return sha1nodeconstants.nullid
142 return sha1nodeconstants.nullid
143
143
144 def revs(self, start=0, stop=None):
144 def revs(self, start=0, stop=None):
145 if stop is None:
145 if stop is None:
146 stop = self.tiprev()
146 stop = self.tiprev()
147 t = self._db.execute(
147 t = self._db.execute(
148 'SELECT rev FROM changelog '
148 'SELECT rev FROM changelog '
149 'WHERE rev >= ? AND rev <= ? '
149 'WHERE rev >= ? AND rev <= ? '
150 'ORDER BY REV ASC',
150 'ORDER BY REV ASC',
151 (start, stop),
151 (start, stop),
152 )
152 )
153 return (int(r[0]) for r in t)
153 return (int(r[0]) for r in t)
154
154
155 def tiprev(self):
155 def tiprev(self):
156 t = self._db.execute(
156 t = self._db.execute(
157 'SELECT rev FROM changelog ' 'ORDER BY REV DESC ' 'LIMIT 1'
157 'SELECT rev FROM changelog ' 'ORDER BY REV DESC ' 'LIMIT 1'
158 ).fetchone()
158 ).fetchone()
159
159
160 if t is not None:
160 if t is not None:
161 return t[0]
161 return t[0]
162 return -1
162 return -1
163
163
164 def _partialmatch(self, id):
164 def _partialmatch(self, id):
165 if sha1nodeconstants.wdirhex.startswith(id):
165 if sha1nodeconstants.wdirhex.startswith(id):
166 raise error.WdirUnsupported
166 raise error.WdirUnsupported
167 candidates = [
167 candidates = [
168 bin(x[0])
168 bin(x[0])
169 for x in self._db.execute(
169 for x in self._db.execute(
170 'SELECT node FROM changelog WHERE node LIKE ?',
170 'SELECT node FROM changelog WHERE node LIKE ?',
171 (pycompat.sysstr(id + b'%'),),
171 (pycompat.sysstr(id + b'%'),),
172 )
172 )
173 ]
173 ]
174 if sha1nodeconstants.nullhex.startswith(id):
174 if sha1nodeconstants.nullhex.startswith(id):
175 candidates.append(sha1nodeconstants.nullid)
175 candidates.append(sha1nodeconstants.nullid)
176 if len(candidates) > 1:
176 if len(candidates) > 1:
177 raise error.AmbiguousPrefixLookupError(
177 raise error.AmbiguousPrefixLookupError(
178 id, b'00changelog.i', _(b'ambiguous identifier')
178 id, b'00changelog.i', _(b'ambiguous identifier')
179 )
179 )
180 if candidates:
180 if candidates:
181 return candidates[0]
181 return candidates[0]
182 return None
182 return None
183
183
184 def flags(self, rev):
184 def flags(self, rev):
185 return 0
185 return 0
186
186
187 def shortest(self, node, minlength=1):
187 def shortest(self, node, minlength=1):
188 nodehex = hex(node)
188 nodehex = hex(node)
189 for attempt in range(minlength, len(nodehex) + 1):
189 for attempt in range(minlength, len(nodehex) + 1):
190 candidate = nodehex[:attempt]
190 candidate = nodehex[:attempt]
191 matches = int(
191 matches = int(
192 self._db.execute(
192 self._db.execute(
193 'SELECT COUNT(*) FROM changelog WHERE node LIKE ?',
193 'SELECT COUNT(*) FROM changelog WHERE node LIKE ?',
194 (pycompat.sysstr(candidate + b'%'),),
194 (pycompat.sysstr(candidate + b'%'),),
195 ).fetchone()[0]
195 ).fetchone()[0]
196 )
196 )
197 if matches == 1:
197 if matches == 1:
198 return candidate
198 return candidate
199 return nodehex
199 return nodehex
200
200
201 def headrevs(self, revs=None):
201 def headrevs(self, revs=None):
202 realheads = [
202 realheads = [
203 int(x[0])
203 int(x[0])
204 for x in self._db.execute(
204 for x in self._db.execute(
205 'SELECT rev FROM changelog '
205 'SELECT rev FROM changelog '
206 'INNER JOIN heads ON changelog.node = heads.node'
206 'INNER JOIN heads ON changelog.node = heads.node'
207 )
207 )
208 ]
208 ]
209 if revs:
209 if revs:
210 return sorted([r for r in revs if r in realheads])
210 return sorted([r for r in revs if r in realheads])
211 return sorted(realheads)
211 return sorted(realheads)
212
212
213 def changelogrevision(self, nodeorrev):
213 def changelogrevision(self, nodeorrev):
214 # Ensure we have a node id
214 # Ensure we have a node id
215 if isinstance(nodeorrev, int):
215 if isinstance(nodeorrev, int):
216 n = self.node(nodeorrev)
216 n = self.node(nodeorrev)
217 else:
217 else:
218 n = nodeorrev
218 n = nodeorrev
219 extra = {b'branch': b'default'}
219 extra = {b'branch': b'default'}
220 # handle looking up nullid
220 # handle looking up nullid
221 if n == sha1nodeconstants.nullid:
221 if n == sha1nodeconstants.nullid:
222 return hgchangelog._changelogrevision(
222 return hgchangelog._changelogrevision(
223 extra=extra, manifest=sha1nodeconstants.nullid
223 extra=extra, manifest=sha1nodeconstants.nullid
224 )
224 )
225 hn = gitutil.togitnode(n)
225 hn = gitutil.togitnode(n)
226 # We've got a real commit!
226 # We've got a real commit!
227 files = [
227 files = [
228 r[0]
228 r[0]
229 for r in self._db.execute(
229 for r in self._db.execute(
230 'SELECT filename FROM changedfiles '
230 'SELECT filename FROM changedfiles '
231 'WHERE node = ? and filenode != ?',
231 'WHERE node = ? and filenode != ?',
232 (hn, gitutil.nullgit),
232 (hn, gitutil.nullgit),
233 )
233 )
234 ]
234 ]
235 filesremoved = [
235 filesremoved = [
236 r[0]
236 r[0]
237 for r in self._db.execute(
237 for r in self._db.execute(
238 'SELECT filename FROM changedfiles '
238 'SELECT filename FROM changedfiles '
239 'WHERE node = ? and filenode = ?',
239 'WHERE node = ? and filenode = ?',
240 (hn, gitutil.nullgit),
240 (hn, gitutil.nullgit),
241 )
241 )
242 ]
242 ]
243 c = self.gitrepo[hn]
243 c = self.gitrepo[hn]
244 return hgchangelog._changelogrevision(
244 return hgchangelog._changelogrevision(
245 manifest=n, # pretend manifest the same as the commit node
245 manifest=n, # pretend manifest the same as the commit node
246 user=b'%s <%s>'
246 user=b'%s <%s>'
247 % (c.author.name.encode('utf8'), c.author.email.encode('utf8')),
247 % (c.author.name.encode('utf8'), c.author.email.encode('utf8')),
248 date=(c.author.time, -c.author.offset * 60),
248 date=(c.author.time, -c.author.offset * 60),
249 files=files,
249 files=files,
250 # TODO filesadded in the index
250 # TODO filesadded in the index
251 filesremoved=filesremoved,
251 filesremoved=filesremoved,
252 description=c.message.encode('utf8'),
252 description=c.message.encode('utf8'),
253 # TODO do we want to handle extra? how?
253 # TODO do we want to handle extra? how?
254 extra=extra,
254 extra=extra,
255 )
255 )
256
256
257 def ancestors(self, revs, stoprev=0, inclusive=False):
257 def ancestors(self, revs, stoprev=0, inclusive=False):
258 revs = list(revs)
258 revs = list(revs)
259 tip = self.rev(self.tip())
259 tip = self.rev(self.tip())
260 for r in revs:
260 for r in revs:
261 if r > tip:
261 if r > tip:
262 raise IndexError(b'Invalid rev %r' % r)
262 raise IndexError(b'Invalid rev %r' % r)
263 return ancestor.lazyancestors(
263 return ancestor.lazyancestors(
264 self.parentrevs, revs, stoprev=stoprev, inclusive=inclusive
264 self.parentrevs, revs, stoprev=stoprev, inclusive=inclusive
265 )
265 )
266
266
267 # Cleanup opportunity: this is *identical* to the revlog.py version
267 # Cleanup opportunity: this is *identical* to the revlog.py version
268 def descendants(self, revs):
268 def descendants(self, revs):
269 return dagop.descendantrevs(revs, self.revs, self.parentrevs)
269 return dagop.descendantrevs(revs, self.revs, self.parentrevs)
270
270
271 def incrementalmissingrevs(self, common=None):
271 def incrementalmissingrevs(self, common=None):
272 """Return an object that can be used to incrementally compute the
272 """Return an object that can be used to incrementally compute the
273 revision numbers of the ancestors of arbitrary sets that are not
273 revision numbers of the ancestors of arbitrary sets that are not
274 ancestors of common. This is an ancestor.incrementalmissingancestors
274 ancestors of common. This is an ancestor.incrementalmissingancestors
275 object.
275 object.
276
276
277 'common' is a list of revision numbers. If common is not supplied, uses
277 'common' is a list of revision numbers. If common is not supplied, uses
278 nullrev.
278 nullrev.
279 """
279 """
280 if common is None:
280 if common is None:
281 common = [nullrev]
281 common = [nullrev]
282
282
283 return ancestor.incrementalmissingancestors(self.parentrevs, common)
283 return ancestor.incrementalmissingancestors(self.parentrevs, common)
284
284
285 def findmissingrevs(self, common=None, heads=None):
286 """Return the revision numbers of the ancestors of heads that
287 are not ancestors of common.
288
289 More specifically, return a list of revision numbers corresponding to
290 nodes N such that every N satisfies the following constraints:
291
292 1. N is an ancestor of some node in 'heads'
293 2. N is not an ancestor of any node in 'common'
294
295 The list is sorted by revision number, meaning it is
296 topologically sorted.
297
298 'heads' and 'common' are both lists of revision numbers. If heads is
299 not supplied, uses all of the revlog's heads. If common is not
300 supplied, uses nullid."""
301 if common is None:
302 common = [nullrev]
303 if heads is None:
304 heads = self.headrevs()
305
306 inc = self.incrementalmissingrevs(common=common)
307 return inc.missingancestors(heads)
308
285 def findmissing(self, common=None, heads=None):
309 def findmissing(self, common=None, heads=None):
286 """Return the ancestors of heads that are not ancestors of common.
310 """Return the ancestors of heads that are not ancestors of common.
287
311
288 More specifically, return a list of nodes N such that every N
312 More specifically, return a list of nodes N such that every N
289 satisfies the following constraints:
313 satisfies the following constraints:
290
314
291 1. N is an ancestor of some node in 'heads'
315 1. N is an ancestor of some node in 'heads'
292 2. N is not an ancestor of any node in 'common'
316 2. N is not an ancestor of any node in 'common'
293
317
294 The list is sorted by revision number, meaning it is
318 The list is sorted by revision number, meaning it is
295 topologically sorted.
319 topologically sorted.
296
320
297 'heads' and 'common' are both lists of node IDs. If heads is
321 'heads' and 'common' are both lists of node IDs. If heads is
298 not supplied, uses all of the revlog's heads. If common is not
322 not supplied, uses all of the revlog's heads. If common is not
299 supplied, uses nullid."""
323 supplied, uses nullid."""
300 if common is None:
324 if common is None:
301 common = [sha1nodeconstants.nullid]
325 common = [sha1nodeconstants.nullid]
302 if heads is None:
326 if heads is None:
303 heads = self.heads()
327 heads = self.heads()
304
328
305 common = [self.rev(n) for n in common]
329 common = [self.rev(n) for n in common]
306 heads = [self.rev(n) for n in heads]
330 heads = [self.rev(n) for n in heads]
307
331
308 inc = self.incrementalmissingrevs(common=common)
332 inc = self.incrementalmissingrevs(common=common)
309 return [self.node(r) for r in inc.missingancestors(heads)]
333 return [self.node(r) for r in inc.missingancestors(heads)]
310
334
311 def children(self, node):
335 def children(self, node):
312 """find the children of a given node"""
336 """find the children of a given node"""
313 c = []
337 c = []
314 p = self.rev(node)
338 p = self.rev(node)
315 for r in self.revs(start=p + 1):
339 for r in self.revs(start=p + 1):
316 prevs = [pr for pr in self.parentrevs(r) if pr != nullrev]
340 prevs = [pr for pr in self.parentrevs(r) if pr != nullrev]
317 if prevs:
341 if prevs:
318 for pr in prevs:
342 for pr in prevs:
319 if pr == p:
343 if pr == p:
320 c.append(self.node(r))
344 c.append(self.node(r))
321 elif p == nullrev:
345 elif p == nullrev:
322 c.append(self.node(r))
346 c.append(self.node(r))
323 return c
347 return c
324
348
325 def reachableroots(self, minroot, heads, roots, includepath=False):
349 def reachableroots(self, minroot, heads, roots, includepath=False):
326 return dagop._reachablerootspure(
350 return dagop._reachablerootspure(
327 self.parentrevs, minroot, roots, heads, includepath
351 self.parentrevs, minroot, roots, heads, includepath
328 )
352 )
329
353
330 # Cleanup opportunity: this is *identical* to the revlog.py version
354 # Cleanup opportunity: this is *identical* to the revlog.py version
331 def isancestor(self, a, b):
355 def isancestor(self, a, b):
332 a, b = self.rev(a), self.rev(b)
356 a, b = self.rev(a), self.rev(b)
333 return self.isancestorrev(a, b)
357 return self.isancestorrev(a, b)
334
358
335 # Cleanup opportunity: this is *identical* to the revlog.py version
359 # Cleanup opportunity: this is *identical* to the revlog.py version
336 def isancestorrev(self, a, b):
360 def isancestorrev(self, a, b):
337 if a == nullrev:
361 if a == nullrev:
338 return True
362 return True
339 elif a == b:
363 elif a == b:
340 return True
364 return True
341 elif a > b:
365 elif a > b:
342 return False
366 return False
343 return bool(self.reachableroots(a, [b], [a], includepath=False))
367 return bool(self.reachableroots(a, [b], [a], includepath=False))
344
368
345 def parentrevs(self, rev):
369 def parentrevs(self, rev):
346 n = self.node(rev)
370 n = self.node(rev)
347 hn = gitutil.togitnode(n)
371 hn = gitutil.togitnode(n)
348 if hn != gitutil.nullgit:
372 if hn != gitutil.nullgit:
349 c = self.gitrepo[hn]
373 c = self.gitrepo[hn]
350 else:
374 else:
351 return nullrev, nullrev
375 return nullrev, nullrev
352 p1 = p2 = nullrev
376 p1 = p2 = nullrev
353 if c.parents:
377 if c.parents:
354 p1 = self.rev(c.parents[0].id.raw)
378 p1 = self.rev(c.parents[0].id.raw)
355 if len(c.parents) > 2:
379 if len(c.parents) > 2:
356 raise error.Abort(b'TODO octopus merge handling')
380 raise error.Abort(b'TODO octopus merge handling')
357 if len(c.parents) == 2:
381 if len(c.parents) == 2:
358 p2 = self.rev(c.parents[1].id.raw)
382 p2 = self.rev(c.parents[1].id.raw)
359 return p1, p2
383 return p1, p2
360
384
361 # Private method is used at least by the tags code.
385 # Private method is used at least by the tags code.
362 _uncheckedparentrevs = parentrevs
386 _uncheckedparentrevs = parentrevs
363
387
364 def commonancestorsheads(self, a, b):
388 def commonancestorsheads(self, a, b):
365 # TODO the revlog verson of this has a C path, so we probably
389 # TODO the revlog verson of this has a C path, so we probably
366 # need to optimize this...
390 # need to optimize this...
367 a, b = self.rev(a), self.rev(b)
391 a, b = self.rev(a), self.rev(b)
368 return [
392 return [
369 self.node(n)
393 self.node(n)
370 for n in ancestor.commonancestorsheads(self.parentrevs, a, b)
394 for n in ancestor.commonancestorsheads(self.parentrevs, a, b)
371 ]
395 ]
372
396
373 def branchinfo(self, rev):
397 def branchinfo(self, rev):
374 """Git doesn't do named branches, so just put everything on default."""
398 """Git doesn't do named branches, so just put everything on default."""
375 return b'default', False
399 return b'default', False
376
400
377 def delayupdate(self, tr):
401 def delayupdate(self, tr):
378 # TODO: I think we can elide this because we're just dropping
402 # TODO: I think we can elide this because we're just dropping
379 # an object in the git repo?
403 # an object in the git repo?
380 pass
404 pass
381
405
382 def add(
406 def add(
383 self,
407 self,
384 manifest,
408 manifest,
385 files,
409 files,
386 desc,
410 desc,
387 transaction,
411 transaction,
388 p1,
412 p1,
389 p2,
413 p2,
390 user,
414 user,
391 date=None,
415 date=None,
392 extra=None,
416 extra=None,
393 p1copies=None,
417 p1copies=None,
394 p2copies=None,
418 p2copies=None,
395 filesadded=None,
419 filesadded=None,
396 filesremoved=None,
420 filesremoved=None,
397 ):
421 ):
398 parents = []
422 parents = []
399 hp1, hp2 = gitutil.togitnode(p1), gitutil.togitnode(p2)
423 hp1, hp2 = gitutil.togitnode(p1), gitutil.togitnode(p2)
400 if p1 != sha1nodeconstants.nullid:
424 if p1 != sha1nodeconstants.nullid:
401 parents.append(hp1)
425 parents.append(hp1)
402 if p2 and p2 != sha1nodeconstants.nullid:
426 if p2 and p2 != sha1nodeconstants.nullid:
403 parents.append(hp2)
427 parents.append(hp2)
404 assert date is not None
428 assert date is not None
405 timestamp, tz = date
429 timestamp, tz = date
406 sig = pygit2.Signature(
430 sig = pygit2.Signature(
407 encoding.unifromlocal(stringutil.person(user)),
431 encoding.unifromlocal(stringutil.person(user)),
408 encoding.unifromlocal(stringutil.email(user)),
432 encoding.unifromlocal(stringutil.email(user)),
409 int(timestamp),
433 int(timestamp),
410 -int(tz // 60),
434 -int(tz // 60),
411 )
435 )
412 oid = self.gitrepo.create_commit(
436 oid = self.gitrepo.create_commit(
413 None, sig, sig, desc, gitutil.togitnode(manifest), parents
437 None, sig, sig, desc, gitutil.togitnode(manifest), parents
414 )
438 )
415 # Set up an internal reference to force the commit into the
439 # Set up an internal reference to force the commit into the
416 # changelog. Hypothetically, we could even use this refs/hg/
440 # changelog. Hypothetically, we could even use this refs/hg/
417 # namespace to allow for anonymous heads on git repos, which
441 # namespace to allow for anonymous heads on git repos, which
418 # would be neat.
442 # would be neat.
419 self.gitrepo.references.create(
443 self.gitrepo.references.create(
420 'refs/hg/internal/latest-commit', oid, force=True
444 'refs/hg/internal/latest-commit', oid, force=True
421 )
445 )
422 # Reindex now to pick up changes. We omit the progress
446 # Reindex now to pick up changes. We omit the progress
423 # and log callbacks because this will be very quick.
447 # and log callbacks because this will be very quick.
424 index._index_repo(self.gitrepo, self._db)
448 index._index_repo(self.gitrepo, self._db)
425 return oid.raw
449 return oid.raw
426
450
427
451
428 class manifestlog(baselog):
452 class manifestlog(baselog):
429 nodeconstants = sha1nodeconstants
453 nodeconstants = sha1nodeconstants
430
454
431 def __getitem__(self, node):
455 def __getitem__(self, node):
432 return self.get(b'', node)
456 return self.get(b'', node)
433
457
434 def get(self, relpath, node):
458 def get(self, relpath, node):
435 if node == sha1nodeconstants.nullid:
459 if node == sha1nodeconstants.nullid:
436 # TODO: this should almost certainly be a memgittreemanifestctx
460 # TODO: this should almost certainly be a memgittreemanifestctx
437 return manifest.memtreemanifestctx(self, relpath)
461 return manifest.memtreemanifestctx(self, relpath)
438 commit = self.gitrepo[gitutil.togitnode(node)]
462 commit = self.gitrepo[gitutil.togitnode(node)]
439 t = commit.tree
463 t = commit.tree
440 if relpath:
464 if relpath:
441 parts = relpath.split(b'/')
465 parts = relpath.split(b'/')
442 for p in parts:
466 for p in parts:
443 te = t[p]
467 te = t[p]
444 t = self.gitrepo[te.id]
468 t = self.gitrepo[te.id]
445 return gitmanifest.gittreemanifestctx(self.gitrepo, t)
469 return gitmanifest.gittreemanifestctx(self.gitrepo, t)
446
470
447
471
448 @interfaceutil.implementer(repository.ifilestorage)
472 @interfaceutil.implementer(repository.ifilestorage)
449 class filelog(baselog):
473 class filelog(baselog):
450 def __init__(self, gr, db, path):
474 def __init__(self, gr, db, path):
451 super(filelog, self).__init__(gr, db)
475 super(filelog, self).__init__(gr, db)
452 assert isinstance(path, bytes)
476 assert isinstance(path, bytes)
453 self.path = path
477 self.path = path
454 self.nullid = sha1nodeconstants.nullid
478 self.nullid = sha1nodeconstants.nullid
455
479
456 def read(self, node):
480 def read(self, node):
457 if node == sha1nodeconstants.nullid:
481 if node == sha1nodeconstants.nullid:
458 return b''
482 return b''
459 return self.gitrepo[gitutil.togitnode(node)].data
483 return self.gitrepo[gitutil.togitnode(node)].data
460
484
461 def lookup(self, node):
485 def lookup(self, node):
462 if len(node) not in (20, 40):
486 if len(node) not in (20, 40):
463 node = int(node)
487 node = int(node)
464 if isinstance(node, int):
488 if isinstance(node, int):
465 assert False, b'todo revnums for nodes'
489 assert False, b'todo revnums for nodes'
466 if len(node) == 40:
490 if len(node) == 40:
467 node = bin(node)
491 node = bin(node)
468 hnode = gitutil.togitnode(node)
492 hnode = gitutil.togitnode(node)
469 if hnode in self.gitrepo:
493 if hnode in self.gitrepo:
470 return node
494 return node
471 raise error.LookupError(self.path, node, _(b'no match found'))
495 raise error.LookupError(self.path, node, _(b'no match found'))
472
496
473 def cmp(self, node, text):
497 def cmp(self, node, text):
474 """Returns True if text is different than content at `node`."""
498 """Returns True if text is different than content at `node`."""
475 return self.read(node) != text
499 return self.read(node) != text
476
500
477 def add(self, text, meta, transaction, link, p1=None, p2=None):
501 def add(self, text, meta, transaction, link, p1=None, p2=None):
478 assert not meta # Should we even try to handle this?
502 assert not meta # Should we even try to handle this?
479 return self.gitrepo.create_blob(text).raw
503 return self.gitrepo.create_blob(text).raw
480
504
481 def __iter__(self):
505 def __iter__(self):
482 for clrev in self._db.execute(
506 for clrev in self._db.execute(
483 '''
507 '''
484 SELECT rev FROM changelog
508 SELECT rev FROM changelog
485 INNER JOIN changedfiles ON changelog.node = changedfiles.node
509 INNER JOIN changedfiles ON changelog.node = changedfiles.node
486 WHERE changedfiles.filename = ? AND changedfiles.filenode != ?
510 WHERE changedfiles.filename = ? AND changedfiles.filenode != ?
487 ''',
511 ''',
488 (pycompat.fsdecode(self.path), gitutil.nullgit),
512 (pycompat.fsdecode(self.path), gitutil.nullgit),
489 ):
513 ):
490 yield clrev[0]
514 yield clrev[0]
491
515
492 def linkrev(self, fr):
516 def linkrev(self, fr):
493 return fr
517 return fr
494
518
495 def rev(self, node):
519 def rev(self, node):
496 row = self._db.execute(
520 row = self._db.execute(
497 '''
521 '''
498 SELECT rev FROM changelog
522 SELECT rev FROM changelog
499 INNER JOIN changedfiles ON changelog.node = changedfiles.node
523 INNER JOIN changedfiles ON changelog.node = changedfiles.node
500 WHERE changedfiles.filename = ? AND changedfiles.filenode = ?''',
524 WHERE changedfiles.filename = ? AND changedfiles.filenode = ?''',
501 (pycompat.fsdecode(self.path), gitutil.togitnode(node)),
525 (pycompat.fsdecode(self.path), gitutil.togitnode(node)),
502 ).fetchone()
526 ).fetchone()
503 if row is None:
527 if row is None:
504 raise error.LookupError(self.path, node, _(b'no such node'))
528 raise error.LookupError(self.path, node, _(b'no such node'))
505 return int(row[0])
529 return int(row[0])
506
530
507 def node(self, rev):
531 def node(self, rev):
508 maybe = self._db.execute(
532 maybe = self._db.execute(
509 '''SELECT filenode FROM changedfiles
533 '''SELECT filenode FROM changedfiles
510 INNER JOIN changelog ON changelog.node = changedfiles.node
534 INNER JOIN changelog ON changelog.node = changedfiles.node
511 WHERE changelog.rev = ? AND filename = ?
535 WHERE changelog.rev = ? AND filename = ?
512 ''',
536 ''',
513 (rev, pycompat.fsdecode(self.path)),
537 (rev, pycompat.fsdecode(self.path)),
514 ).fetchone()
538 ).fetchone()
515 if maybe is None:
539 if maybe is None:
516 raise IndexError('gitlog %r out of range %d' % (self.path, rev))
540 raise IndexError('gitlog %r out of range %d' % (self.path, rev))
517 return bin(maybe[0])
541 return bin(maybe[0])
518
542
519 def parents(self, node):
543 def parents(self, node):
520 gn = gitutil.togitnode(node)
544 gn = gitutil.togitnode(node)
521 gp = pycompat.fsdecode(self.path)
545 gp = pycompat.fsdecode(self.path)
522 ps = []
546 ps = []
523 for p in self._db.execute(
547 for p in self._db.execute(
524 '''SELECT p1filenode, p2filenode FROM changedfiles
548 '''SELECT p1filenode, p2filenode FROM changedfiles
525 WHERE filenode = ? AND filename = ?
549 WHERE filenode = ? AND filename = ?
526 ''',
550 ''',
527 (gn, gp),
551 (gn, gp),
528 ).fetchone():
552 ).fetchone():
529 if p is None:
553 if p is None:
530 commit = self._db.execute(
554 commit = self._db.execute(
531 "SELECT node FROM changedfiles "
555 "SELECT node FROM changedfiles "
532 "WHERE filenode = ? AND filename = ?",
556 "WHERE filenode = ? AND filename = ?",
533 (gn, gp),
557 (gn, gp),
534 ).fetchone()[0]
558 ).fetchone()[0]
535 # This filelog is missing some data. Build the
559 # This filelog is missing some data. Build the
536 # filelog, then recurse (which will always find data).
560 # filelog, then recurse (which will always find data).
537 commit = commit.decode('ascii')
561 commit = commit.decode('ascii')
538 index.fill_in_filelog(self.gitrepo, self._db, commit, gp, gn)
562 index.fill_in_filelog(self.gitrepo, self._db, commit, gp, gn)
539 return self.parents(node)
563 return self.parents(node)
540 else:
564 else:
541 ps.append(bin(p))
565 ps.append(bin(p))
542 return ps
566 return ps
543
567
544 def renamed(self, node):
568 def renamed(self, node):
545 # TODO: renames/copies
569 # TODO: renames/copies
546 return False
570 return False
@@ -1,429 +1,432 b''
1 #require pygit2 no-windows
1 #require pygit2 no-windows
2
2
3 Setup:
3 Setup:
4 $ GIT_AUTHOR_NAME='test'; export GIT_AUTHOR_NAME
4 $ GIT_AUTHOR_NAME='test'; export GIT_AUTHOR_NAME
5 > GIT_AUTHOR_EMAIL='test@example.org'; export GIT_AUTHOR_EMAIL
5 > GIT_AUTHOR_EMAIL='test@example.org'; export GIT_AUTHOR_EMAIL
6 > GIT_AUTHOR_DATE="2007-01-01 00:00:00 +0000"; export GIT_AUTHOR_DATE
6 > GIT_AUTHOR_DATE="2007-01-01 00:00:00 +0000"; export GIT_AUTHOR_DATE
7 > GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"; export GIT_COMMITTER_NAME
7 > GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"; export GIT_COMMITTER_NAME
8 > GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"; export GIT_COMMITTER_EMAIL
8 > GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"; export GIT_COMMITTER_EMAIL
9 > GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"; export GIT_COMMITTER_DATE
9 > GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"; export GIT_COMMITTER_DATE
10 > HGUSER="test <test@example.org>"; export HGUSER
10 > HGUSER="test <test@example.org>"; export HGUSER
11 > count=10
11 > count=10
12 > gitcommit() {
12 > gitcommit() {
13 > GIT_AUTHOR_DATE="2007-01-01 00:00:$count +0000";
13 > GIT_AUTHOR_DATE="2007-01-01 00:00:$count +0000";
14 > GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"
14 > GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"
15 > git commit "$@" >/dev/null 2>/dev/null || echo "git commit error"
15 > git commit "$@" >/dev/null 2>/dev/null || echo "git commit error"
16 > count=`expr $count + 1`
16 > count=`expr $count + 1`
17 > }
17 > }
18 $ git config --global init.defaultBranch master
18 $ git config --global init.defaultBranch master
19
19
20
20
21 $ hg version -v --config extensions.git= | grep '^[E ]'
21 $ hg version -v --config extensions.git= | grep '^[E ]'
22 Enabled extensions:
22 Enabled extensions:
23 git internal (pygit2 *) (glob)
23 git internal (pygit2 *) (glob)
24
24
25 Test auto-loading extension works:
25 Test auto-loading extension works:
26 $ mkdir nogit
26 $ mkdir nogit
27 $ cd nogit
27 $ cd nogit
28 $ mkdir .hg
28 $ mkdir .hg
29 $ echo git >> .hg/requires
29 $ echo git >> .hg/requires
30 $ hg status
30 $ hg status
31 abort: repository specified git format in .hg/requires but has no .git directory
31 abort: repository specified git format in .hg/requires but has no .git directory
32 [255]
32 [255]
33 $ git config --global init.defaultBranch master
33 $ git config --global init.defaultBranch master
34 $ git init
34 $ git init
35 Initialized empty Git repository in $TESTTMP/nogit/.git/
35 Initialized empty Git repository in $TESTTMP/nogit/.git/
36 This status invocation shows some hg gunk because we didn't use
36 This status invocation shows some hg gunk because we didn't use
37 `hg init --git`, which fixes up .git/info/exclude for us.
37 `hg init --git`, which fixes up .git/info/exclude for us.
38 $ hg status
38 $ hg status
39 ? .hg/cache/git-commits.sqlite
39 ? .hg/cache/git-commits.sqlite
40 ? .hg/cache/git-commits.sqlite-shm
40 ? .hg/cache/git-commits.sqlite-shm
41 ? .hg/cache/git-commits.sqlite-wal
41 ? .hg/cache/git-commits.sqlite-wal
42 ? .hg/requires
42 ? .hg/requires
43 $ cd ..
43 $ cd ..
44
44
45 Now globally enable extension for the rest of the test:
45 Now globally enable extension for the rest of the test:
46 $ cat <<EOF >> $HGRCPATH
46 $ cat <<EOF >> $HGRCPATH
47 > [extensions]
47 > [extensions]
48 > git=
48 > git=
49 > [git]
49 > [git]
50 > log-index-cache-miss = yes
50 > log-index-cache-miss = yes
51 > EOF
51 > EOF
52
52
53 Test some edge cases around a commitless repo first
53 Test some edge cases around a commitless repo first
54 $ mkdir empty
54 $ mkdir empty
55 $ cd empty
55 $ cd empty
56 $ git init
56 $ git init
57 Initialized empty Git repository in $TESTTMP/empty/.git/
57 Initialized empty Git repository in $TESTTMP/empty/.git/
58 $ hg init --git
58 $ hg init --git
59 $ hg heads
59 $ hg heads
60 [1]
60 [1]
61 $ hg tip
61 $ hg tip
62 changeset: -1:000000000000
62 changeset: -1:000000000000
63 tag: tip
63 tag: tip
64 user:
64 user:
65 date: Thu Jan 01 00:00:00 1970 +0000
65 date: Thu Jan 01 00:00:00 1970 +0000
66
66
67 $ cd ..
67 $ cd ..
68
68
69 Make a new repo with git:
69 Make a new repo with git:
70 $ mkdir foo
70 $ mkdir foo
71 $ cd foo
71 $ cd foo
72 $ git init
72 $ git init
73 Initialized empty Git repository in $TESTTMP/foo/.git/
73 Initialized empty Git repository in $TESTTMP/foo/.git/
74 Ignore the .hg directory within git:
74 Ignore the .hg directory within git:
75 $ echo .hg >> .git/info/exclude
75 $ echo .hg >> .git/info/exclude
76 $ echo alpha > alpha
76 $ echo alpha > alpha
77 $ git add alpha
77 $ git add alpha
78 $ gitcommit -am 'Add alpha'
78 $ gitcommit -am 'Add alpha'
79 $ echo beta > beta
79 $ echo beta > beta
80 $ git add beta
80 $ git add beta
81 $ gitcommit -am 'Add beta'
81 $ gitcommit -am 'Add beta'
82 $ echo gamma > gamma
82 $ echo gamma > gamma
83 $ git status
83 $ git status
84 On branch master
84 On branch master
85 Untracked files:
85 Untracked files:
86 (use "git add <file>..." to include in what will be committed)
86 (use "git add <file>..." to include in what will be committed)
87 (?)
87 (?)
88 gamma
88 gamma
89
89
90 nothing added to commit but untracked files present (use "git add" to track)
90 nothing added to commit but untracked files present (use "git add" to track)
91
91
92 Without creating the .hg, hg status fails:
92 Without creating the .hg, hg status fails:
93 $ hg status
93 $ hg status
94 abort: no repository found in '$TESTTMP/foo' (.hg not found)
94 abort: no repository found in '$TESTTMP/foo' (.hg not found)
95 [10]
95 [10]
96 But if you run hg init --git, it works:
96 But if you run hg init --git, it works:
97 $ hg init --git
97 $ hg init --git
98 $ hg id --traceback
98 $ hg id --traceback
99 heads mismatch, rebuilding dagcache
99 heads mismatch, rebuilding dagcache
100 3d9be8deba43 tip master
100 3d9be8deba43 tip master
101 $ hg status
101 $ hg status
102 ? gamma
102 ? gamma
103 Log works too:
103 Log works too:
104 $ hg log
104 $ hg log
105 changeset: 1:3d9be8deba43
105 changeset: 1:3d9be8deba43
106 bookmark: master
106 bookmark: master
107 tag: tip
107 tag: tip
108 user: test <test@example.org>
108 user: test <test@example.org>
109 date: Mon Jan 01 00:00:11 2007 +0000
109 date: Mon Jan 01 00:00:11 2007 +0000
110 summary: Add beta
110 summary: Add beta
111
111
112 changeset: 0:c5864c9d16fb
112 changeset: 0:c5864c9d16fb
113 user: test <test@example.org>
113 user: test <test@example.org>
114 date: Mon Jan 01 00:00:10 2007 +0000
114 date: Mon Jan 01 00:00:10 2007 +0000
115 summary: Add alpha
115 summary: Add alpha
116
116
117
117
118
118
119 and bookmarks:
119 and bookmarks:
120 $ hg bookmarks
120 $ hg bookmarks
121 * master 1:3d9be8deba43
121 * master 1:3d9be8deba43
122 $ hg up master
122 $ hg up master
123 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
123 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
124 $ hg bookmarks
124 $ hg bookmarks
125 * master 1:3d9be8deba43
125 * master 1:3d9be8deba43
126
126
127 diff even works transparently in both systems:
127 diff even works transparently in both systems:
128 $ echo blah >> alpha
128 $ echo blah >> alpha
129 $ git diff
129 $ git diff
130 diff --git a/alpha b/alpha
130 diff --git a/alpha b/alpha
131 index 4a58007..faed1b7 100644
131 index 4a58007..faed1b7 100644
132 --- a/alpha
132 --- a/alpha
133 +++ b/alpha
133 +++ b/alpha
134 @@ -1* +1,2 @@ (glob)
134 @@ -1* +1,2 @@ (glob)
135 alpha
135 alpha
136 +blah
136 +blah
137 $ hg diff --git
137 $ hg diff --git
138 diff --git a/alpha b/alpha
138 diff --git a/alpha b/alpha
139 --- a/alpha
139 --- a/alpha
140 +++ b/alpha
140 +++ b/alpha
141 @@ -1,1 +1,2 @@
141 @@ -1,1 +1,2 @@
142 alpha
142 alpha
143 +blah
143 +blah
144
144
145 Remove a file, it shows as such:
145 Remove a file, it shows as such:
146 $ rm alpha
146 $ rm alpha
147 $ hg status
147 $ hg status
148 ! alpha
148 ! alpha
149 ? gamma
149 ? gamma
150
150
151 Revert works:
151 Revert works:
152 $ hg revert alpha --traceback
152 $ hg revert alpha --traceback
153 $ hg status
153 $ hg status
154 ? gamma
154 ? gamma
155 $ git status
155 $ git status
156 On branch master
156 On branch master
157 Untracked files:
157 Untracked files:
158 (use "git add <file>..." to include in what will be committed)
158 (use "git add <file>..." to include in what will be committed)
159 (?)
159 (?)
160 gamma
160 gamma
161
161
162 nothing added to commit but untracked files present (use "git add" to track)
162 nothing added to commit but untracked files present (use "git add" to track)
163
163
164 Add shows sanely in both:
164 Add shows sanely in both:
165 $ hg add gamma
165 $ hg add gamma
166 $ hg status
166 $ hg status
167 A gamma
167 A gamma
168 $ hg files
168 $ hg files
169 alpha
169 alpha
170 beta
170 beta
171 gamma
171 gamma
172 $ git ls-files
172 $ git ls-files
173 alpha
173 alpha
174 beta
174 beta
175 gamma
175 gamma
176 $ git status
176 $ git status
177 On branch master
177 On branch master
178 Changes to be committed:
178 Changes to be committed:
179 (use "git restore --staged <file>..." to unstage) (?)
179 (use "git restore --staged <file>..." to unstage) (?)
180 (use "git reset HEAD <file>..." to unstage) (?)
180 (use "git reset HEAD <file>..." to unstage) (?)
181 (?)
181 (?)
182 new file: gamma
182 new file: gamma
183
183
184
184
185 forget does what it should as well:
185 forget does what it should as well:
186 $ hg forget gamma
186 $ hg forget gamma
187 $ hg status
187 $ hg status
188 ? gamma
188 ? gamma
189 $ git status
189 $ git status
190 On branch master
190 On branch master
191 Untracked files:
191 Untracked files:
192 (use "git add <file>..." to include in what will be committed)
192 (use "git add <file>..." to include in what will be committed)
193 (?)
193 (?)
194 gamma
194 gamma
195
195
196 nothing added to commit but untracked files present (use "git add" to track)
196 nothing added to commit but untracked files present (use "git add" to track)
197
197
198 clean up untracked file
198 clean up untracked file
199 $ rm gamma
199 $ rm gamma
200
200
201 hg log FILE
201 hg log FILE
202
202
203 $ echo a >> alpha
203 $ echo a >> alpha
204 $ hg ci -m 'more alpha' --traceback --date '1583558723 18000'
204 $ hg ci -m 'more alpha' --traceback --date '1583558723 18000'
205 $ echo b >> beta
205 $ echo b >> beta
206 $ hg ci -m 'more beta'
206 $ hg ci -m 'more beta'
207 heads mismatch, rebuilding dagcache
207 heads mismatch, rebuilding dagcache
208 $ echo a >> alpha
208 $ echo a >> alpha
209 $ hg ci -m 'even more alpha'
209 $ hg ci -m 'even more alpha'
210 heads mismatch, rebuilding dagcache
210 heads mismatch, rebuilding dagcache
211 $ hg log -G alpha
211 $ hg log -G alpha
212 heads mismatch, rebuilding dagcache
212 heads mismatch, rebuilding dagcache
213 @ changeset: 4:cf6ddf5d9b8a
213 @ changeset: 4:cf6ddf5d9b8a
214 : bookmark: master
214 : bookmark: master
215 : tag: tip
215 : tag: tip
216 : user: test <test@example.org>
216 : user: test <test@example.org>
217 : date: Thu Jan 01 00:00:00 1970 +0000
217 : date: Thu Jan 01 00:00:00 1970 +0000
218 : summary: even more alpha
218 : summary: even more alpha
219 :
219 :
220 o changeset: 2:5b2c80b027ce
220 o changeset: 2:5b2c80b027ce
221 : user: test <test@example.org>
221 : user: test <test@example.org>
222 : date: Sat Mar 07 00:25:23 2020 -0500
222 : date: Sat Mar 07 00:25:23 2020 -0500
223 : summary: more alpha
223 : summary: more alpha
224 :
224 :
225 o changeset: 0:c5864c9d16fb
225 o changeset: 0:c5864c9d16fb
226 user: test <test@example.org>
226 user: test <test@example.org>
227 date: Mon Jan 01 00:00:10 2007 +0000
227 date: Mon Jan 01 00:00:10 2007 +0000
228 summary: Add alpha
228 summary: Add alpha
229
229
230 $ hg log -G beta
230 $ hg log -G beta
231 o changeset: 3:980d4f79a9c6
231 o changeset: 3:980d4f79a9c6
232 : user: test <test@example.org>
232 : user: test <test@example.org>
233 : date: Thu Jan 01 00:00:00 1970 +0000
233 : date: Thu Jan 01 00:00:00 1970 +0000
234 : summary: more beta
234 : summary: more beta
235 :
235 :
236 o changeset: 1:3d9be8deba43
236 o changeset: 1:3d9be8deba43
237 | user: test <test@example.org>
237 | user: test <test@example.org>
238 ~ date: Mon Jan 01 00:00:11 2007 +0000
238 ~ date: Mon Jan 01 00:00:11 2007 +0000
239 summary: Add beta
239 summary: Add beta
240
240
241
241
242 $ hg log -r "children(3d9be8deba43)" -T"{node|short} {children}\n"
242 $ hg log -r "children(3d9be8deba43)" -T"{node|short} {children}\n"
243 5b2c80b027ce 3:980d4f79a9c6
243 5b2c80b027ce 3:980d4f79a9c6
244
244
245 hg annotate
245 hg annotate
246
246
247 $ hg annotate alpha
247 $ hg annotate alpha
248 0: alpha
248 0: alpha
249 2: a
249 2: a
250 4: a
250 4: a
251 $ hg annotate beta
251 $ hg annotate beta
252 1: beta
252 1: beta
253 3: b
253 3: b
254
254
255
255
256 Files in subdirectories. TODO: case-folding support, make this `A`
256 Files in subdirectories. TODO: case-folding support, make this `A`
257 instead of `a`.
257 instead of `a`.
258
258
259 $ mkdir a
259 $ mkdir a
260 $ echo "This is file mu." > a/mu
260 $ echo "This is file mu." > a/mu
261 $ hg ci -A -m 'Introduce file a/mu'
261 $ hg ci -A -m 'Introduce file a/mu'
262 adding a/mu
262 adding a/mu
263
263
264 Both hg and git agree a/mu is part of the repo
264 Both hg and git agree a/mu is part of the repo
265
265
266 $ git ls-files
266 $ git ls-files
267 a/mu
267 a/mu
268 alpha
268 alpha
269 beta
269 beta
270 $ hg files
270 $ hg files
271 a/mu
271 a/mu
272 alpha
272 alpha
273 beta
273 beta
274
274
275 hg and git status both clean
275 hg and git status both clean
276
276
277 $ git status
277 $ git status
278 On branch master
278 On branch master
279 nothing to commit, working tree clean
279 nothing to commit, working tree clean
280 $ hg status
280 $ hg status
281 heads mismatch, rebuilding dagcache
281 heads mismatch, rebuilding dagcache
282
282
283
283
284 node|shortest works correctly
284 node|shortest works correctly
285 $ hg log -T '{node}\n' | sort
285 $ hg log -T '{node}\n' | sort
286 3d9be8deba43482be2c81a4cb4be1f10d85fa8bc
286 3d9be8deba43482be2c81a4cb4be1f10d85fa8bc
287 5b2c80b027ce4250f88957326c199a2dc48dad60
287 5b2c80b027ce4250f88957326c199a2dc48dad60
288 980d4f79a9c617d60d0fe1fb383753c4a61bea8e
288 980d4f79a9c617d60d0fe1fb383753c4a61bea8e
289 c1a41c49866ecc9c5411be932653e5b430961dd5
289 c1a41c49866ecc9c5411be932653e5b430961dd5
290 c5864c9d16fb3431fe2c175ff84dc6accdbb2c18
290 c5864c9d16fb3431fe2c175ff84dc6accdbb2c18
291 cf6ddf5d9b8a120bf90020342bcf7a96d0167279
291 cf6ddf5d9b8a120bf90020342bcf7a96d0167279
292 $ hg log -r c1a41c49866ecc9c5411be932653e5b430961dd5 --template "{shortest(node,1)}\n"
292 $ hg log -r c1a41c49866ecc9c5411be932653e5b430961dd5 --template "{shortest(node,1)}\n"
293 c1
293 c1
294
294
295 This covers gitlog._partialmatch()
295 This covers gitlog._partialmatch()
296 $ hg log -r c
296 $ hg log -r c
297 abort: ambiguous revision identifier: c
297 abort: ambiguous revision identifier: c
298 [10]
298 [10]
299 $ hg log -r c1
299 $ hg log -r c1
300 changeset: 5:c1a41c49866e
300 changeset: 5:c1a41c49866e
301 bookmark: master
301 bookmark: master
302 tag: tip
302 tag: tip
303 user: test <test@example.org>
303 user: test <test@example.org>
304 date: Thu Jan 01 00:00:00 1970 +0000
304 date: Thu Jan 01 00:00:00 1970 +0000
305 summary: Introduce file a/mu
305 summary: Introduce file a/mu
306
306
307 $ hg log -r dead
307 $ hg log -r dead
308 abort: unknown revision 'dead'
308 abort: unknown revision 'dead'
309 [255]
309 [255]
310
310
311 This coveres changelog.findmissing()
311 This coveres changelog.findmissing()
312 $ hg merge --preview 3d9be8deba43
312 $ hg merge --preview 3d9be8deba43
313
313
314 This covers manifest.diff()
314 This covers manifest.diff()
315 $ hg diff -c 3d9be8deba43
315 $ hg diff -c 3d9be8deba43
316 diff -r c5864c9d16fb -r 3d9be8deba43 beta
316 diff -r c5864c9d16fb -r 3d9be8deba43 beta
317 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
317 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
318 +++ b/beta Mon Jan 01 00:00:11 2007 +0000
318 +++ b/beta Mon Jan 01 00:00:11 2007 +0000
319 @@ -0,0 +1,1 @@
319 @@ -0,0 +1,1 @@
320 +beta
320 +beta
321
321
322
322
323 Interactive commit should work as expected
323 Interactive commit should work as expected
324
324
325 $ echo bar >> alpha
325 $ echo bar >> alpha
326 $ echo bar >> beta
326 $ echo bar >> beta
327 $ hg commit -m "test interactive commit" -i --config ui.interactive=true --config ui.interface=text << EOF
327 $ hg commit -m "test interactive commit" -i --config ui.interactive=true --config ui.interface=text << EOF
328 > y
328 > y
329 > y
329 > y
330 > n
330 > n
331 > EOF
331 > EOF
332 diff --git a/alpha b/alpha
332 diff --git a/alpha b/alpha
333 1 hunks, 1 lines changed
333 1 hunks, 1 lines changed
334 examine changes to 'alpha'?
334 examine changes to 'alpha'?
335 (enter ? for help) [Ynesfdaq?] y
335 (enter ? for help) [Ynesfdaq?] y
336
336
337 @@ -1,3 +1,4 @@
337 @@ -1,3 +1,4 @@
338 alpha
338 alpha
339 a
339 a
340 a
340 a
341 +bar
341 +bar
342 record change 1/2 to 'alpha'?
342 record change 1/2 to 'alpha'?
343 (enter ? for help) [Ynesfdaq?] y
343 (enter ? for help) [Ynesfdaq?] y
344
344
345 diff --git a/beta b/beta
345 diff --git a/beta b/beta
346 1 hunks, 1 lines changed
346 1 hunks, 1 lines changed
347 examine changes to 'beta'?
347 examine changes to 'beta'?
348 (enter ? for help) [Ynesfdaq?] n
348 (enter ? for help) [Ynesfdaq?] n
349
349
350 Status should be consistent for both systems
350 Status should be consistent for both systems
351
351
352 $ hg status
352 $ hg status
353 heads mismatch, rebuilding dagcache
353 heads mismatch, rebuilding dagcache
354 M beta
354 M beta
355 $ git status | egrep -v '^$|^ \(use '
355 $ git status | egrep -v '^$|^ \(use '
356 On branch master
356 On branch master
357 Changes not staged for commit:
357 Changes not staged for commit:
358 modified: beta
358 modified: beta
359 no changes added to commit (use "git add" and/or "git commit -a")
359 no changes added to commit (use "git add" and/or "git commit -a")
360
360
361 Contents of each commit should be the same
361 Contents of each commit should be the same
362
362
363 $ hg ex -r .
363 $ hg ex -r .
364 # HG changeset patch
364 # HG changeset patch
365 # User test <test@example.org>
365 # User test <test@example.org>
366 # Date 0 0
366 # Date 0 0
367 # Thu Jan 01 00:00:00 1970 +0000
367 # Thu Jan 01 00:00:00 1970 +0000
368 # Node ID 6024eda7986da123aa6797dd4603bd399d49bf5c
368 # Node ID 6024eda7986da123aa6797dd4603bd399d49bf5c
369 # Parent c1a41c49866ecc9c5411be932653e5b430961dd5
369 # Parent c1a41c49866ecc9c5411be932653e5b430961dd5
370 test interactive commit
370 test interactive commit
371
371
372 diff -r c1a41c49866e -r 6024eda7986d alpha
372 diff -r c1a41c49866e -r 6024eda7986d alpha
373 --- a/alpha Thu Jan 01 00:00:00 1970 +0000
373 --- a/alpha Thu Jan 01 00:00:00 1970 +0000
374 +++ b/alpha Thu Jan 01 00:00:00 1970 +0000
374 +++ b/alpha Thu Jan 01 00:00:00 1970 +0000
375 @@ -1,3 +1,4 @@
375 @@ -1,3 +1,4 @@
376 alpha
376 alpha
377 a
377 a
378 a
378 a
379 +bar
379 +bar
380 $ git show
380 $ git show
381 commit 6024eda7986da123aa6797dd4603bd399d49bf5c
381 commit 6024eda7986da123aa6797dd4603bd399d49bf5c
382 Author: test <test@example.org>
382 Author: test <test@example.org>
383 Date: Thu Jan 1 00:00:00 1970 +0000
383 Date: Thu Jan 1 00:00:00 1970 +0000
384
384
385 test interactive commit
385 test interactive commit
386
386
387 diff --git a/alpha b/alpha
387 diff --git a/alpha b/alpha
388 index d112a75..d2a2e9a 100644
388 index d112a75..d2a2e9a 100644
389 --- a/alpha
389 --- a/alpha
390 +++ b/alpha
390 +++ b/alpha
391 @@ -1,3 +1,4 @@
391 @@ -1,3 +1,4 @@
392 alpha
392 alpha
393 a
393 a
394 a
394 a
395 +bar
395 +bar
396
396
397 Deleting files should also work (this was issue6398)
397 Deleting files should also work (this was issue6398)
398 $ hg revert -r . --all
398 $ hg revert -r . --all
399 reverting beta
399 reverting beta
400 $ hg rm beta
400 $ hg rm beta
401 $ hg ci -m 'remove beta'
401 $ hg ci -m 'remove beta'
402
402
403 This covers changelog.tiprev() (issue6510)
403 This covers changelog.tiprev() (issue6510)
404 $ hg log -r '(.^^):'
404 $ hg log -r '(.^^):'
405 heads mismatch, rebuilding dagcache
405 heads mismatch, rebuilding dagcache
406 changeset: 5:c1a41c49866e
406 changeset: 5:c1a41c49866e
407 user: test <test@example.org>
407 user: test <test@example.org>
408 date: Thu Jan 01 00:00:00 1970 +0000
408 date: Thu Jan 01 00:00:00 1970 +0000
409 summary: Introduce file a/mu
409 summary: Introduce file a/mu
410
410
411 changeset: 6:6024eda7986d
411 changeset: 6:6024eda7986d
412 user: test <test@example.org>
412 user: test <test@example.org>
413 date: Thu Jan 01 00:00:00 1970 +0000
413 date: Thu Jan 01 00:00:00 1970 +0000
414 summary: test interactive commit
414 summary: test interactive commit
415
415
416 changeset: 7:1a0fee76bfc4
416 changeset: 7:1a0fee76bfc4
417 bookmark: master
417 bookmark: master
418 tag: tip
418 tag: tip
419 user: test <test@example.org>
419 user: test <test@example.org>
420 date: Thu Jan 01 00:00:00 1970 +0000
420 date: Thu Jan 01 00:00:00 1970 +0000
421 summary: remove beta
421 summary: remove beta
422
422
423 This covers changelog.headrevs() with a non-None arg
423 This covers changelog.headrevs() with a non-None arg
424 $ hg log -r 'heads(.)' -Tcompact
424 $ hg log -r 'heads(.)' -Tcompact
425 7[tip][master] 1a0fee76bfc4 1970-01-01 00:00 +0000 test
425 7[tip][master] 1a0fee76bfc4 1970-01-01 00:00 +0000 test
426 remove beta
426 remove beta
427
427
428
428 This covers revlog.findmissingrevs() (issue6472)
429
429 $ hg log -r 'last(only(master))' -Tcompact
430 7[tip][master] 1a0fee76bfc4 1970-01-01 00:00 +0000 test
431 remove beta
432
General Comments 0
You need to be logged in to leave comments. Login now