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