##// END OF EJS Templates
clone: copy obsolete markers during local clone...
Pierre-Yves.David@ens-lyon.org -
r17249:7d4747c7 stable
parent child Browse files
Show More
@@ -1,429 +1,430
1 1 # store.py - repository store handling for Mercurial
2 2 #
3 3 # Copyright 2008 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from i18n import _
9 9 import osutil, scmutil, util
10 10 import os, stat
11 11
12 12 _sha = util.sha1
13 13
14 14 # This avoids a collision between a file named foo and a dir named
15 15 # foo.i or foo.d
16 16 def encodedir(path):
17 17 '''
18 18 >>> encodedir('data/foo.i')
19 19 'data/foo.i'
20 20 >>> encodedir('data/foo.i/bla.i')
21 21 'data/foo.i.hg/bla.i'
22 22 >>> encodedir('data/foo.i.hg/bla.i')
23 23 'data/foo.i.hg.hg/bla.i'
24 24 '''
25 25 if not path.startswith('data/'):
26 26 return path
27 27 return (path
28 28 .replace(".hg/", ".hg.hg/")
29 29 .replace(".i/", ".i.hg/")
30 30 .replace(".d/", ".d.hg/"))
31 31
32 32 def decodedir(path):
33 33 '''
34 34 >>> decodedir('data/foo.i')
35 35 'data/foo.i'
36 36 >>> decodedir('data/foo.i.hg/bla.i')
37 37 'data/foo.i/bla.i'
38 38 >>> decodedir('data/foo.i.hg.hg/bla.i')
39 39 'data/foo.i.hg/bla.i'
40 40 '''
41 41 if not path.startswith('data/') or ".hg/" not in path:
42 42 return path
43 43 return (path
44 44 .replace(".d.hg/", ".d/")
45 45 .replace(".i.hg/", ".i/")
46 46 .replace(".hg.hg/", ".hg/"))
47 47
48 48 def _buildencodefun():
49 49 '''
50 50 >>> enc, dec = _buildencodefun()
51 51
52 52 >>> enc('nothing/special.txt')
53 53 'nothing/special.txt'
54 54 >>> dec('nothing/special.txt')
55 55 'nothing/special.txt'
56 56
57 57 >>> enc('HELLO')
58 58 '_h_e_l_l_o'
59 59 >>> dec('_h_e_l_l_o')
60 60 'HELLO'
61 61
62 62 >>> enc('hello:world?')
63 63 'hello~3aworld~3f'
64 64 >>> dec('hello~3aworld~3f')
65 65 'hello:world?'
66 66
67 67 >>> enc('the\x07quick\xADshot')
68 68 'the~07quick~adshot'
69 69 >>> dec('the~07quick~adshot')
70 70 'the\\x07quick\\xadshot'
71 71 '''
72 72 e = '_'
73 73 winreserved = [ord(x) for x in '\\:*?"<>|']
74 74 cmap = dict([(chr(x), chr(x)) for x in xrange(127)])
75 75 for x in (range(32) + range(126, 256) + winreserved):
76 76 cmap[chr(x)] = "~%02x" % x
77 77 for x in range(ord("A"), ord("Z")+1) + [ord(e)]:
78 78 cmap[chr(x)] = e + chr(x).lower()
79 79 dmap = {}
80 80 for k, v in cmap.iteritems():
81 81 dmap[v] = k
82 82 def decode(s):
83 83 i = 0
84 84 while i < len(s):
85 85 for l in xrange(1, 4):
86 86 try:
87 87 yield dmap[s[i:i + l]]
88 88 i += l
89 89 break
90 90 except KeyError:
91 91 pass
92 92 else:
93 93 raise KeyError
94 94 return (lambda s: "".join([cmap[c] for c in encodedir(s)]),
95 95 lambda s: decodedir("".join(list(decode(s)))))
96 96
97 97 encodefilename, decodefilename = _buildencodefun()
98 98
99 99 def _buildlowerencodefun():
100 100 '''
101 101 >>> f = _buildlowerencodefun()
102 102 >>> f('nothing/special.txt')
103 103 'nothing/special.txt'
104 104 >>> f('HELLO')
105 105 'hello'
106 106 >>> f('hello:world?')
107 107 'hello~3aworld~3f'
108 108 >>> f('the\x07quick\xADshot')
109 109 'the~07quick~adshot'
110 110 '''
111 111 winreserved = [ord(x) for x in '\\:*?"<>|']
112 112 cmap = dict([(chr(x), chr(x)) for x in xrange(127)])
113 113 for x in (range(32) + range(126, 256) + winreserved):
114 114 cmap[chr(x)] = "~%02x" % x
115 115 for x in range(ord("A"), ord("Z")+1):
116 116 cmap[chr(x)] = chr(x).lower()
117 117 return lambda s: "".join([cmap[c] for c in s])
118 118
119 119 lowerencode = _buildlowerencodefun()
120 120
121 121 _winreservednames = '''con prn aux nul
122 122 com1 com2 com3 com4 com5 com6 com7 com8 com9
123 123 lpt1 lpt2 lpt3 lpt4 lpt5 lpt6 lpt7 lpt8 lpt9'''.split()
124 124 def _auxencode(path, dotencode):
125 125 '''
126 126 Encodes filenames containing names reserved by Windows or which end in
127 127 period or space. Does not touch other single reserved characters c.
128 128 Specifically, c in '\\:*?"<>|' or ord(c) <= 31 are *not* encoded here.
129 129 Additionally encodes space or period at the beginning, if dotencode is
130 130 True.
131 131 path is assumed to be all lowercase.
132 132
133 133 >>> _auxencode('.foo/aux.txt/txt.aux/con/prn/nul/foo.', True)
134 134 '~2efoo/au~78.txt/txt.aux/co~6e/pr~6e/nu~6c/foo~2e'
135 135 >>> _auxencode('.com1com2/lpt9.lpt4.lpt1/conprn/foo.', False)
136 136 '.com1com2/lp~749.lpt4.lpt1/conprn/foo~2e'
137 137 >>> _auxencode('foo. ', True)
138 138 'foo.~20'
139 139 >>> _auxencode(' .foo', True)
140 140 '~20.foo'
141 141 '''
142 142 res = []
143 143 for n in path.split('/'):
144 144 if n:
145 145 base = n.split('.')[0]
146 146 if base and (base in _winreservednames):
147 147 # encode third letter ('aux' -> 'au~78')
148 148 ec = "~%02x" % ord(n[2])
149 149 n = n[0:2] + ec + n[3:]
150 150 if n[-1] in '. ':
151 151 # encode last period or space ('foo...' -> 'foo..~2e')
152 152 n = n[:-1] + "~%02x" % ord(n[-1])
153 153 if dotencode and n[0] in '. ':
154 154 n = "~%02x" % ord(n[0]) + n[1:]
155 155 res.append(n)
156 156 return '/'.join(res)
157 157
158 158 _maxstorepathlen = 120
159 159 _dirprefixlen = 8
160 160 _maxshortdirslen = 8 * (_dirprefixlen + 1) - 4
161 161 def _hybridencode(path, auxencode):
162 162 '''encodes path with a length limit
163 163
164 164 Encodes all paths that begin with 'data/', according to the following.
165 165
166 166 Default encoding (reversible):
167 167
168 168 Encodes all uppercase letters 'X' as '_x'. All reserved or illegal
169 169 characters are encoded as '~xx', where xx is the two digit hex code
170 170 of the character (see encodefilename).
171 171 Relevant path components consisting of Windows reserved filenames are
172 172 masked by encoding the third character ('aux' -> 'au~78', see auxencode).
173 173
174 174 Hashed encoding (not reversible):
175 175
176 176 If the default-encoded path is longer than _maxstorepathlen, a
177 177 non-reversible hybrid hashing of the path is done instead.
178 178 This encoding uses up to _dirprefixlen characters of all directory
179 179 levels of the lowerencoded path, but not more levels than can fit into
180 180 _maxshortdirslen.
181 181 Then follows the filler followed by the sha digest of the full path.
182 182 The filler is the beginning of the basename of the lowerencoded path
183 183 (the basename is everything after the last path separator). The filler
184 184 is as long as possible, filling in characters from the basename until
185 185 the encoded path has _maxstorepathlen characters (or all chars of the
186 186 basename have been taken).
187 187 The extension (e.g. '.i' or '.d') is preserved.
188 188
189 189 The string 'data/' at the beginning is replaced with 'dh/', if the hashed
190 190 encoding was used.
191 191 '''
192 192 if not path.startswith('data/'):
193 193 return path
194 194 # escape directories ending with .i and .d
195 195 path = encodedir(path)
196 196 ndpath = path[len('data/'):]
197 197 res = 'data/' + auxencode(encodefilename(ndpath))
198 198 if len(res) > _maxstorepathlen:
199 199 digest = _sha(path).hexdigest()
200 200 aep = auxencode(lowerencode(ndpath))
201 201 _root, ext = os.path.splitext(aep)
202 202 parts = aep.split('/')
203 203 basename = parts[-1]
204 204 sdirs = []
205 205 for p in parts[:-1]:
206 206 d = p[:_dirprefixlen]
207 207 if d[-1] in '. ':
208 208 # Windows can't access dirs ending in period or space
209 209 d = d[:-1] + '_'
210 210 t = '/'.join(sdirs) + '/' + d
211 211 if len(t) > _maxshortdirslen:
212 212 break
213 213 sdirs.append(d)
214 214 dirs = '/'.join(sdirs)
215 215 if len(dirs) > 0:
216 216 dirs += '/'
217 217 res = 'dh/' + dirs + digest + ext
218 218 spaceleft = _maxstorepathlen - len(res)
219 219 if spaceleft > 0:
220 220 filler = basename[:spaceleft]
221 221 res = 'dh/' + dirs + filler + digest + ext
222 222 return res
223 223
224 224 def _calcmode(path):
225 225 try:
226 226 # files in .hg/ will be created using this mode
227 227 mode = os.stat(path).st_mode
228 228 # avoid some useless chmods
229 229 if (0777 & ~util.umask) == (0777 & mode):
230 230 mode = None
231 231 except OSError:
232 232 mode = None
233 233 return mode
234 234
235 _data = 'data 00manifest.d 00manifest.i 00changelog.d 00changelog.i phaseroots'
235 _data = ('data 00manifest.d 00manifest.i 00changelog.d 00changelog.i'
236 ' phaseroots obsstore')
236 237
237 238 class basicstore(object):
238 239 '''base class for local repository stores'''
239 240 def __init__(self, path, openertype):
240 241 self.path = path
241 242 self.createmode = _calcmode(path)
242 243 op = openertype(self.path)
243 244 op.createmode = self.createmode
244 245 self.opener = scmutil.filteropener(op, encodedir)
245 246
246 247 def join(self, f):
247 248 return self.path + '/' + encodedir(f)
248 249
249 250 def _walk(self, relpath, recurse):
250 251 '''yields (unencoded, encoded, size)'''
251 252 path = self.path
252 253 if relpath:
253 254 path += '/' + relpath
254 255 striplen = len(self.path) + 1
255 256 l = []
256 257 if os.path.isdir(path):
257 258 visit = [path]
258 259 while visit:
259 260 p = visit.pop()
260 261 for f, kind, st in osutil.listdir(p, stat=True):
261 262 fp = p + '/' + f
262 263 if kind == stat.S_IFREG and f[-2:] in ('.d', '.i'):
263 264 n = util.pconvert(fp[striplen:])
264 265 l.append((decodedir(n), n, st.st_size))
265 266 elif kind == stat.S_IFDIR and recurse:
266 267 visit.append(fp)
267 268 l.sort()
268 269 return l
269 270
270 271 def datafiles(self):
271 272 return self._walk('data', True)
272 273
273 274 def walk(self):
274 275 '''yields (unencoded, encoded, size)'''
275 276 # yield data files first
276 277 for x in self.datafiles():
277 278 yield x
278 279 # yield manifest before changelog
279 280 for x in reversed(self._walk('', False)):
280 281 yield x
281 282
282 283 def copylist(self):
283 284 return ['requires'] + _data.split()
284 285
285 286 def write(self):
286 287 pass
287 288
288 289 class encodedstore(basicstore):
289 290 def __init__(self, path, openertype):
290 291 self.path = path + '/store'
291 292 self.createmode = _calcmode(self.path)
292 293 op = openertype(self.path)
293 294 op.createmode = self.createmode
294 295 self.opener = scmutil.filteropener(op, encodefilename)
295 296
296 297 def datafiles(self):
297 298 for a, b, size in self._walk('data', True):
298 299 try:
299 300 a = decodefilename(a)
300 301 except KeyError:
301 302 a = None
302 303 yield a, b, size
303 304
304 305 def join(self, f):
305 306 return self.path + '/' + encodefilename(f)
306 307
307 308 def copylist(self):
308 309 return (['requires', '00changelog.i'] +
309 310 ['store/' + f for f in _data.split()])
310 311
311 312 class fncache(object):
312 313 # the filename used to be partially encoded
313 314 # hence the encodedir/decodedir dance
314 315 def __init__(self, opener):
315 316 self.opener = opener
316 317 self.entries = None
317 318 self._dirty = False
318 319
319 320 def _load(self):
320 321 '''fill the entries from the fncache file'''
321 322 self._dirty = False
322 323 try:
323 324 fp = self.opener('fncache', mode='rb')
324 325 except IOError:
325 326 # skip nonexistent file
326 327 self.entries = set()
327 328 return
328 329 self.entries = set(map(decodedir, fp.read().splitlines()))
329 330 if '' in self.entries:
330 331 fp.seek(0)
331 332 for n, line in enumerate(fp):
332 333 if not line.rstrip('\n'):
333 334 t = _('invalid entry in fncache, line %s') % (n + 1)
334 335 raise util.Abort(t)
335 336 fp.close()
336 337
337 338 def _write(self, files, atomictemp):
338 339 fp = self.opener('fncache', mode='wb', atomictemp=atomictemp)
339 340 if files:
340 341 fp.write('\n'.join(map(encodedir, files)) + '\n')
341 342 fp.close()
342 343 self._dirty = False
343 344
344 345 def rewrite(self, files):
345 346 self._write(files, False)
346 347 self.entries = set(files)
347 348
348 349 def write(self):
349 350 if self._dirty:
350 351 self._write(self.entries, True)
351 352
352 353 def add(self, fn):
353 354 if self.entries is None:
354 355 self._load()
355 356 if fn not in self.entries:
356 357 self._dirty = True
357 358 self.entries.add(fn)
358 359
359 360 def __contains__(self, fn):
360 361 if self.entries is None:
361 362 self._load()
362 363 return fn in self.entries
363 364
364 365 def __iter__(self):
365 366 if self.entries is None:
366 367 self._load()
367 368 return iter(self.entries)
368 369
369 370 class _fncacheopener(scmutil.abstractopener):
370 371 def __init__(self, op, fnc, encode):
371 372 self.opener = op
372 373 self.fncache = fnc
373 374 self.encode = encode
374 375
375 376 def __call__(self, path, mode='r', *args, **kw):
376 377 if mode not in ('r', 'rb') and path.startswith('data/'):
377 378 self.fncache.add(path)
378 379 return self.opener(self.encode(path), mode, *args, **kw)
379 380
380 381 class fncachestore(basicstore):
381 382 def __init__(self, path, openertype, encode):
382 383 self.encode = encode
383 384 self.path = path + '/store'
384 385 self.createmode = _calcmode(self.path)
385 386 op = openertype(self.path)
386 387 op.createmode = self.createmode
387 388 fnc = fncache(op)
388 389 self.fncache = fnc
389 390 self.opener = _fncacheopener(op, fnc, encode)
390 391
391 392 def join(self, f):
392 393 return self.path + '/' + self.encode(f)
393 394
394 395 def getsize(self, path):
395 396 return os.stat(self.path + '/' + path).st_size
396 397
397 398 def datafiles(self):
398 399 rewrite = False
399 400 existing = []
400 401 for f in self.fncache:
401 402 ef = self.encode(f)
402 403 try:
403 404 yield f, ef, self.getsize(ef)
404 405 existing.append(f)
405 406 except OSError:
406 407 # nonexistent entry
407 408 rewrite = True
408 409 if rewrite:
409 410 # rewrite fncache to remove nonexistent entries
410 411 # (may be caused by rollback / strip)
411 412 self.fncache.rewrite(existing)
412 413
413 414 def copylist(self):
414 d = ('data dh fncache phaseroots'
415 d = ('data dh fncache phaseroots obsstore'
415 416 ' 00manifest.d 00manifest.i 00changelog.d 00changelog.i')
416 417 return (['requires', '00changelog.i'] +
417 418 ['store/' + f for f in d.split()])
418 419
419 420 def write(self):
420 421 self.fncache.write()
421 422
422 423 def store(requirements, path, openertype):
423 424 if 'store' in requirements:
424 425 if 'fncache' in requirements:
425 426 auxencode = lambda f: _auxencode(f, 'dotencode' in requirements)
426 427 encode = lambda f: _hybridencode(f, auxencode)
427 428 return fncachestore(path, openertype, encode)
428 429 return encodedstore(path, openertype)
429 430 return basicstore(path, openertype)
@@ -1,406 +1,453
1 1 $ cat >> $HGRCPATH << EOF
2 2 > [extensions]
3 3 > graphlog=
4 4 > [phases]
5 5 > # public changeset are not obsolete
6 6 > publish=false
7 7 > EOF
8 8 $ mkcommit() {
9 9 > echo "$1" > "$1"
10 10 > hg add "$1"
11 11 > hg ci -m "add $1"
12 12 > }
13 13 $ getid() {
14 14 > hg id --debug -ir "desc('$1')"
15 15 > }
16 16
17 17
18 18 $ hg init tmpa
19 19 $ cd tmpa
20 20
21 21 Killing a single changeset without replacement
22 22
23 23 $ mkcommit kill_me
24 24 $ hg debugobsolete -d '0 0' `getid kill_me` -u babar
25 25 $ hg debugobsolete
26 26 97b7c2d76b1845ed3eb988cd612611e72406cef0 0 {'date': '0 0', 'user': 'babar'}
27 27 $ cd ..
28 28
29 29 Killing a single changeset with replacement
30 30
31 31 $ hg init tmpb
32 32 $ cd tmpb
33 33 $ mkcommit a
34 34 $ mkcommit b
35 35 $ mkcommit original_c
36 36 $ hg up "desc('b')"
37 37 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
38 38 $ mkcommit new_c
39 39 created new head
40 40 $ hg debugobsolete `getid original_c` `getid new_c` -d '56 12'
41 41 $ hg debugobsolete
42 42 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f 0 {'date': '56 12', 'user': 'test'}
43 43
44 44 do it again (it read the obsstore before adding new changeset)
45 45
46 46 $ hg up '.^'
47 47 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
48 48 $ mkcommit new_2_c
49 49 created new head
50 50 $ hg debugobsolete -d '1337 0' `getid new_c` `getid new_2_c`
51 51 $ hg debugobsolete
52 52 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f 0 {'date': '56 12', 'user': 'test'}
53 53 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 {'date': '1337 0', 'user': 'test'}
54 54
55 55 Register two markers with a missing node
56 56
57 57 $ hg up '.^'
58 58 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
59 59 $ mkcommit new_3_c
60 60 created new head
61 61 $ hg debugobsolete -d '1338 0' `getid new_2_c` 1337133713371337133713371337133713371337
62 62 $ hg debugobsolete -d '1339 0' 1337133713371337133713371337133713371337 `getid new_3_c`
63 63 $ hg debugobsolete
64 64 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f 0 {'date': '56 12', 'user': 'test'}
65 65 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 {'date': '1337 0', 'user': 'test'}
66 66 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 {'date': '1338 0', 'user': 'test'}
67 67 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 {'date': '1339 0', 'user': 'test'}
68 68
69 69 Check that graphlog detect that a changeset is obsolete:
70 70
71 71 $ hg glog
72 72 @ changeset: 5:5601fb93a350
73 73 | tag: tip
74 74 | parent: 1:7c3bad9141dc
75 75 | user: test
76 76 | date: Thu Jan 01 00:00:00 1970 +0000
77 77 | summary: add new_3_c
78 78 |
79 79 o changeset: 1:7c3bad9141dc
80 80 | user: test
81 81 | date: Thu Jan 01 00:00:00 1970 +0000
82 82 | summary: add b
83 83 |
84 84 o changeset: 0:1f0dee641bb7
85 85 user: test
86 86 date: Thu Jan 01 00:00:00 1970 +0000
87 87 summary: add a
88 88
89 89
90 90 Check that public changeset are not accounted as obsolete:
91 91
92 92 $ hg phase --public 2
93 93 $ hg --config 'extensions.graphlog=' glog
94 94 @ changeset: 5:5601fb93a350
95 95 | tag: tip
96 96 | parent: 1:7c3bad9141dc
97 97 | user: test
98 98 | date: Thu Jan 01 00:00:00 1970 +0000
99 99 | summary: add new_3_c
100 100 |
101 101 | o changeset: 2:245bde4270cd
102 102 |/ user: test
103 103 | date: Thu Jan 01 00:00:00 1970 +0000
104 104 | summary: add original_c
105 105 |
106 106 o changeset: 1:7c3bad9141dc
107 107 | user: test
108 108 | date: Thu Jan 01 00:00:00 1970 +0000
109 109 | summary: add b
110 110 |
111 111 o changeset: 0:1f0dee641bb7
112 112 user: test
113 113 date: Thu Jan 01 00:00:00 1970 +0000
114 114 summary: add a
115 115
116 116
117 117 $ cd ..
118 118
119 119 Exchange Test
120 120 ============================
121 121
122 122 Destination repo does not have any data
123 123 ---------------------------------------
124 124
125 125 Try to pull markers
126 126 (extinct changeset are excluded but marker are pushed)
127 127
128 128 $ hg init tmpc
129 129 $ cd tmpc
130 130 $ hg pull ../tmpb
131 131 pulling from ../tmpb
132 132 requesting all changes
133 133 adding changesets
134 134 adding manifests
135 135 adding file changes
136 136 added 4 changesets with 4 changes to 4 files (+1 heads)
137 137 (run 'hg heads' to see heads, 'hg merge' to merge)
138 138 $ hg debugobsolete
139 139 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f 0 {'date': '56 12', 'user': 'test'}
140 140 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 {'date': '1337 0', 'user': 'test'}
141 141 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 {'date': '1338 0', 'user': 'test'}
142 142 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 {'date': '1339 0', 'user': 'test'}
143 143
144 144 Rollback//Transaction support
145 145
146 146 $ hg debugobsolete -d '1340 0' aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
147 147 $ hg debugobsolete
148 148 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f 0 {'date': '56 12', 'user': 'test'}
149 149 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 {'date': '1337 0', 'user': 'test'}
150 150 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 {'date': '1338 0', 'user': 'test'}
151 151 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 {'date': '1339 0', 'user': 'test'}
152 152 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 0 {'date': '1340 0', 'user': 'test'}
153 153 $ hg rollback -n
154 154 repository tip rolled back to revision 3 (undo debugobsolete)
155 155 $ hg rollback
156 156 repository tip rolled back to revision 3 (undo debugobsolete)
157 157 $ hg debugobsolete
158 158 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f 0 {'date': '56 12', 'user': 'test'}
159 159 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 {'date': '1337 0', 'user': 'test'}
160 160 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 {'date': '1338 0', 'user': 'test'}
161 161 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 {'date': '1339 0', 'user': 'test'}
162 162
163 163 $ cd ..
164 164
165 165 Try to pull markers
166 166
167 167 $ hg init tmpd
168 168 $ hg -R tmpb push tmpd
169 169 pushing to tmpd
170 170 searching for changes
171 171 adding changesets
172 172 adding manifests
173 173 adding file changes
174 174 added 4 changesets with 4 changes to 4 files (+1 heads)
175 175 $ hg -R tmpd debugobsolete
176 176 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f 0 {'date': '56 12', 'user': 'test'}
177 177 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 {'date': '1337 0', 'user': 'test'}
178 178 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 {'date': '1338 0', 'user': 'test'}
179 179 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 {'date': '1339 0', 'user': 'test'}
180 180
181 clone support
182 (markers are copied and extinct changesets are included to allow hardlinks)
183
184 $ hg clone tmpb clone-dest
185 updating to branch default
186 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
187 $ hg -R clone-dest log -G --hidden
188 @ changeset: 5:5601fb93a350
189 | tag: tip
190 | parent: 1:7c3bad9141dc
191 | user: test
192 | date: Thu Jan 01 00:00:00 1970 +0000
193 | summary: add new_3_c
194 |
195 | x changeset: 4:ca819180edb9
196 |/ parent: 1:7c3bad9141dc
197 | user: test
198 | date: Thu Jan 01 00:00:00 1970 +0000
199 | summary: add new_2_c
200 |
201 | x changeset: 3:cdbce2fbb163
202 |/ parent: 1:7c3bad9141dc
203 | user: test
204 | date: Thu Jan 01 00:00:00 1970 +0000
205 | summary: add new_c
206 |
207 | o changeset: 2:245bde4270cd
208 |/ user: test
209 | date: Thu Jan 01 00:00:00 1970 +0000
210 | summary: add original_c
211 |
212 o changeset: 1:7c3bad9141dc
213 | user: test
214 | date: Thu Jan 01 00:00:00 1970 +0000
215 | summary: add b
216 |
217 o changeset: 0:1f0dee641bb7
218 user: test
219 date: Thu Jan 01 00:00:00 1970 +0000
220 summary: add a
221
222 $ hg -R clone-dest debugobsolete
223 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f 0 {'date': '56 12', 'user': 'test'}
224 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 {'date': '1337 0', 'user': 'test'}
225 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 {'date': '1338 0', 'user': 'test'}
226 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 {'date': '1339 0', 'user': 'test'}
227
181 228
182 229 Destination repo have existing data
183 230 ---------------------------------------
184 231
185 232 On pull
186 233
187 234 $ hg init tmpe
188 235 $ cd tmpe
189 236 $ hg debugobsolete -d '1339 0' 2448244824482448244824482448244824482448 1339133913391339133913391339133913391339
190 237 $ hg pull ../tmpb
191 238 pulling from ../tmpb
192 239 requesting all changes
193 240 adding changesets
194 241 adding manifests
195 242 adding file changes
196 243 added 4 changesets with 4 changes to 4 files (+1 heads)
197 244 (run 'hg heads' to see heads, 'hg merge' to merge)
198 245 $ hg debugobsolete
199 246 2448244824482448244824482448244824482448 1339133913391339133913391339133913391339 0 {'date': '1339 0', 'user': 'test'}
200 247 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f 0 {'date': '56 12', 'user': 'test'}
201 248 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 {'date': '1337 0', 'user': 'test'}
202 249 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 {'date': '1338 0', 'user': 'test'}
203 250 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 {'date': '1339 0', 'user': 'test'}
204 251
205 252
206 253 On push
207 254
208 255 $ hg push ../tmpc
209 256 pushing to ../tmpc
210 257 searching for changes
211 258 no changes found
212 259 [1]
213 260 $ hg -R ../tmpc debugobsolete
214 261 245bde4270cd1072a27757984f9cda8ba26f08ca cdbce2fbb16313928851e97e0d85413f3f7eb77f 0 {'date': '56 12', 'user': 'test'}
215 262 cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 {'date': '1337 0', 'user': 'test'}
216 263 ca819180edb99ed25ceafb3e9584ac287e240b00 1337133713371337133713371337133713371337 0 {'date': '1338 0', 'user': 'test'}
217 264 1337133713371337133713371337133713371337 5601fb93a350734d935195fee37f4054c529ff39 0 {'date': '1339 0', 'user': 'test'}
218 265 2448244824482448244824482448244824482448 1339133913391339133913391339133913391339 0 {'date': '1339 0', 'user': 'test'}
219 266
220 267 detect outgoing obsolete and unstable
221 268 ---------------------------------------
222 269
223 270
224 271 $ hg glog
225 272 o changeset: 3:5601fb93a350
226 273 | tag: tip
227 274 | parent: 1:7c3bad9141dc
228 275 | user: test
229 276 | date: Thu Jan 01 00:00:00 1970 +0000
230 277 | summary: add new_3_c
231 278 |
232 279 | o changeset: 2:245bde4270cd
233 280 |/ user: test
234 281 | date: Thu Jan 01 00:00:00 1970 +0000
235 282 | summary: add original_c
236 283 |
237 284 o changeset: 1:7c3bad9141dc
238 285 | user: test
239 286 | date: Thu Jan 01 00:00:00 1970 +0000
240 287 | summary: add b
241 288 |
242 289 o changeset: 0:1f0dee641bb7
243 290 user: test
244 291 date: Thu Jan 01 00:00:00 1970 +0000
245 292 summary: add a
246 293
247 294 $ hg up 'desc("new_3_c")'
248 295 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
249 296 $ mkcommit original_d
250 297 $ mkcommit original_e
251 298 $ hg debugobsolete `getid original_d` -d '0 0'
252 299 $ hg log -r 'obsolete()'
253 300 changeset: 4:7c694bff0650
254 301 user: test
255 302 date: Thu Jan 01 00:00:00 1970 +0000
256 303 summary: add original_d
257 304
258 305 $ hg glog -r '::unstable()'
259 306 @ changeset: 5:6e572121998e
260 307 | tag: tip
261 308 | user: test
262 309 | date: Thu Jan 01 00:00:00 1970 +0000
263 310 | summary: add original_e
264 311 |
265 312 x changeset: 4:7c694bff0650
266 313 | user: test
267 314 | date: Thu Jan 01 00:00:00 1970 +0000
268 315 | summary: add original_d
269 316 |
270 317 o changeset: 3:5601fb93a350
271 318 | parent: 1:7c3bad9141dc
272 319 | user: test
273 320 | date: Thu Jan 01 00:00:00 1970 +0000
274 321 | summary: add new_3_c
275 322 |
276 323 o changeset: 1:7c3bad9141dc
277 324 | user: test
278 325 | date: Thu Jan 01 00:00:00 1970 +0000
279 326 | summary: add b
280 327 |
281 328 o changeset: 0:1f0dee641bb7
282 329 user: test
283 330 date: Thu Jan 01 00:00:00 1970 +0000
284 331 summary: add a
285 332
286 333
287 334 refuse to push obsolete changeset
288 335
289 336 $ hg push ../tmpc/ -r 'desc("original_d")'
290 337 pushing to ../tmpc/
291 338 searching for changes
292 339 abort: push includes an obsolete changeset: 7c694bff0650!
293 340 [255]
294 341
295 342 refuse to push unstable changeset
296 343
297 344 $ hg push ../tmpc/
298 345 pushing to ../tmpc/
299 346 searching for changes
300 347 abort: push includes an unstable changeset: 6e572121998e!
301 348 [255]
302 349
303 350 Test that extinct changeset are properly detected
304 351
305 352 $ hg log -r 'extinct()'
306 353
307 354 Don't try to push extinct changeset
308 355
309 356 $ hg init ../tmpf
310 357 $ hg out ../tmpf
311 358 comparing with ../tmpf
312 359 searching for changes
313 360 changeset: 0:1f0dee641bb7
314 361 user: test
315 362 date: Thu Jan 01 00:00:00 1970 +0000
316 363 summary: add a
317 364
318 365 changeset: 1:7c3bad9141dc
319 366 user: test
320 367 date: Thu Jan 01 00:00:00 1970 +0000
321 368 summary: add b
322 369
323 370 changeset: 2:245bde4270cd
324 371 user: test
325 372 date: Thu Jan 01 00:00:00 1970 +0000
326 373 summary: add original_c
327 374
328 375 changeset: 3:5601fb93a350
329 376 parent: 1:7c3bad9141dc
330 377 user: test
331 378 date: Thu Jan 01 00:00:00 1970 +0000
332 379 summary: add new_3_c
333 380
334 381 changeset: 4:7c694bff0650
335 382 user: test
336 383 date: Thu Jan 01 00:00:00 1970 +0000
337 384 summary: add original_d
338 385
339 386 changeset: 5:6e572121998e
340 387 tag: tip
341 388 user: test
342 389 date: Thu Jan 01 00:00:00 1970 +0000
343 390 summary: add original_e
344 391
345 392 $ hg push ../tmpf -f # -f because be push unstable too
346 393 pushing to ../tmpf
347 394 searching for changes
348 395 adding changesets
349 396 adding manifests
350 397 adding file changes
351 398 added 6 changesets with 6 changes to 6 files (+1 heads)
352 399
353 400 no warning displayed
354 401
355 402 $ hg push ../tmpf
356 403 pushing to ../tmpf
357 404 searching for changes
358 405 no changes found
359 406 [1]
360 407
361 408 Do not warn about new head when the new head is a successors of a remote one
362 409
363 410 $ hg glog
364 411 @ changeset: 5:6e572121998e
365 412 | tag: tip
366 413 | user: test
367 414 | date: Thu Jan 01 00:00:00 1970 +0000
368 415 | summary: add original_e
369 416 |
370 417 x changeset: 4:7c694bff0650
371 418 | user: test
372 419 | date: Thu Jan 01 00:00:00 1970 +0000
373 420 | summary: add original_d
374 421 |
375 422 o changeset: 3:5601fb93a350
376 423 | parent: 1:7c3bad9141dc
377 424 | user: test
378 425 | date: Thu Jan 01 00:00:00 1970 +0000
379 426 | summary: add new_3_c
380 427 |
381 428 | o changeset: 2:245bde4270cd
382 429 |/ user: test
383 430 | date: Thu Jan 01 00:00:00 1970 +0000
384 431 | summary: add original_c
385 432 |
386 433 o changeset: 1:7c3bad9141dc
387 434 | user: test
388 435 | date: Thu Jan 01 00:00:00 1970 +0000
389 436 | summary: add b
390 437 |
391 438 o changeset: 0:1f0dee641bb7
392 439 user: test
393 440 date: Thu Jan 01 00:00:00 1970 +0000
394 441 summary: add a
395 442
396 443 $ hg up -q 'desc(new_3_c)'
397 444 $ mkcommit obsolete_e
398 445 created new head
399 446 $ hg debugobsolete `getid 'original_e'` `getid 'obsolete_e'`
400 447 $ hg push ../tmpf
401 448 pushing to ../tmpf
402 449 searching for changes
403 450 adding changesets
404 451 adding manifests
405 452 adding file changes
406 453 added 1 changesets with 1 changes to 1 files (+1 heads)
General Comments 0
You need to be logged in to leave comments. Login now