##// END OF EJS Templates
dirstate: properly clean-up some more merge state on setparents
Matt Mackall -
r22895:dfa44e25 default
parent child Browse files
Show More
@@ -1,923 +1,928
1 # dirstate.py - working directory tracking for mercurial
1 # dirstate.py - working directory tracking for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from node import nullid
8 from node import nullid
9 from i18n import _
9 from i18n import _
10 import scmutil, util, ignore, osutil, parsers, encoding, pathutil
10 import scmutil, util, ignore, osutil, parsers, encoding, pathutil
11 import os, stat, errno, gc
11 import os, stat, errno, gc
12
12
13 propertycache = util.propertycache
13 propertycache = util.propertycache
14 filecache = scmutil.filecache
14 filecache = scmutil.filecache
15 _rangemask = 0x7fffffff
15 _rangemask = 0x7fffffff
16
16
17 dirstatetuple = parsers.dirstatetuple
17 dirstatetuple = parsers.dirstatetuple
18
18
19 class repocache(filecache):
19 class repocache(filecache):
20 """filecache for files in .hg/"""
20 """filecache for files in .hg/"""
21 def join(self, obj, fname):
21 def join(self, obj, fname):
22 return obj._opener.join(fname)
22 return obj._opener.join(fname)
23
23
24 class rootcache(filecache):
24 class rootcache(filecache):
25 """filecache for files in the repository root"""
25 """filecache for files in the repository root"""
26 def join(self, obj, fname):
26 def join(self, obj, fname):
27 return obj._join(fname)
27 return obj._join(fname)
28
28
29 class dirstate(object):
29 class dirstate(object):
30
30
31 def __init__(self, opener, ui, root, validate):
31 def __init__(self, opener, ui, root, validate):
32 '''Create a new dirstate object.
32 '''Create a new dirstate object.
33
33
34 opener is an open()-like callable that can be used to open the
34 opener is an open()-like callable that can be used to open the
35 dirstate file; root is the root of the directory tracked by
35 dirstate file; root is the root of the directory tracked by
36 the dirstate.
36 the dirstate.
37 '''
37 '''
38 self._opener = opener
38 self._opener = opener
39 self._validate = validate
39 self._validate = validate
40 self._root = root
40 self._root = root
41 self._rootdir = os.path.join(root, '')
41 self._rootdir = os.path.join(root, '')
42 self._dirty = False
42 self._dirty = False
43 self._dirtypl = False
43 self._dirtypl = False
44 self._lastnormaltime = 0
44 self._lastnormaltime = 0
45 self._ui = ui
45 self._ui = ui
46 self._filecache = {}
46 self._filecache = {}
47 self._parentwriters = 0
47 self._parentwriters = 0
48
48
49 def beginparentchange(self):
49 def beginparentchange(self):
50 '''Marks the beginning of a set of changes that involve changing
50 '''Marks the beginning of a set of changes that involve changing
51 the dirstate parents. If there is an exception during this time,
51 the dirstate parents. If there is an exception during this time,
52 the dirstate will not be written when the wlock is released. This
52 the dirstate will not be written when the wlock is released. This
53 prevents writing an incoherent dirstate where the parent doesn't
53 prevents writing an incoherent dirstate where the parent doesn't
54 match the contents.
54 match the contents.
55 '''
55 '''
56 self._parentwriters += 1
56 self._parentwriters += 1
57
57
58 def endparentchange(self):
58 def endparentchange(self):
59 '''Marks the end of a set of changes that involve changing the
59 '''Marks the end of a set of changes that involve changing the
60 dirstate parents. Once all parent changes have been marked done,
60 dirstate parents. Once all parent changes have been marked done,
61 the wlock will be free to write the dirstate on release.
61 the wlock will be free to write the dirstate on release.
62 '''
62 '''
63 if self._parentwriters > 0:
63 if self._parentwriters > 0:
64 self._parentwriters -= 1
64 self._parentwriters -= 1
65
65
66 def pendingparentchange(self):
66 def pendingparentchange(self):
67 '''Returns true if the dirstate is in the middle of a set of changes
67 '''Returns true if the dirstate is in the middle of a set of changes
68 that modify the dirstate parent.
68 that modify the dirstate parent.
69 '''
69 '''
70 return self._parentwriters > 0
70 return self._parentwriters > 0
71
71
72 @propertycache
72 @propertycache
73 def _map(self):
73 def _map(self):
74 '''Return the dirstate contents as a map from filename to
74 '''Return the dirstate contents as a map from filename to
75 (state, mode, size, time).'''
75 (state, mode, size, time).'''
76 self._read()
76 self._read()
77 return self._map
77 return self._map
78
78
79 @propertycache
79 @propertycache
80 def _copymap(self):
80 def _copymap(self):
81 self._read()
81 self._read()
82 return self._copymap
82 return self._copymap
83
83
84 @propertycache
84 @propertycache
85 def _foldmap(self):
85 def _foldmap(self):
86 f = {}
86 f = {}
87 normcase = util.normcase
87 normcase = util.normcase
88 for name, s in self._map.iteritems():
88 for name, s in self._map.iteritems():
89 if s[0] != 'r':
89 if s[0] != 'r':
90 f[normcase(name)] = name
90 f[normcase(name)] = name
91 for name in self._dirs:
91 for name in self._dirs:
92 f[normcase(name)] = name
92 f[normcase(name)] = name
93 f['.'] = '.' # prevents useless util.fspath() invocation
93 f['.'] = '.' # prevents useless util.fspath() invocation
94 return f
94 return f
95
95
96 @repocache('branch')
96 @repocache('branch')
97 def _branch(self):
97 def _branch(self):
98 try:
98 try:
99 return self._opener.read("branch").strip() or "default"
99 return self._opener.read("branch").strip() or "default"
100 except IOError, inst:
100 except IOError, inst:
101 if inst.errno != errno.ENOENT:
101 if inst.errno != errno.ENOENT:
102 raise
102 raise
103 return "default"
103 return "default"
104
104
105 @propertycache
105 @propertycache
106 def _pl(self):
106 def _pl(self):
107 try:
107 try:
108 fp = self._opener("dirstate")
108 fp = self._opener("dirstate")
109 st = fp.read(40)
109 st = fp.read(40)
110 fp.close()
110 fp.close()
111 l = len(st)
111 l = len(st)
112 if l == 40:
112 if l == 40:
113 return st[:20], st[20:40]
113 return st[:20], st[20:40]
114 elif l > 0 and l < 40:
114 elif l > 0 and l < 40:
115 raise util.Abort(_('working directory state appears damaged!'))
115 raise util.Abort(_('working directory state appears damaged!'))
116 except IOError, err:
116 except IOError, err:
117 if err.errno != errno.ENOENT:
117 if err.errno != errno.ENOENT:
118 raise
118 raise
119 return [nullid, nullid]
119 return [nullid, nullid]
120
120
121 @propertycache
121 @propertycache
122 def _dirs(self):
122 def _dirs(self):
123 return scmutil.dirs(self._map, 'r')
123 return scmutil.dirs(self._map, 'r')
124
124
125 def dirs(self):
125 def dirs(self):
126 return self._dirs
126 return self._dirs
127
127
128 @rootcache('.hgignore')
128 @rootcache('.hgignore')
129 def _ignore(self):
129 def _ignore(self):
130 files = [self._join('.hgignore')]
130 files = [self._join('.hgignore')]
131 for name, path in self._ui.configitems("ui"):
131 for name, path in self._ui.configitems("ui"):
132 if name == 'ignore' or name.startswith('ignore.'):
132 if name == 'ignore' or name.startswith('ignore.'):
133 files.append(util.expandpath(path))
133 files.append(util.expandpath(path))
134 return ignore.ignore(self._root, files, self._ui.warn)
134 return ignore.ignore(self._root, files, self._ui.warn)
135
135
136 @propertycache
136 @propertycache
137 def _slash(self):
137 def _slash(self):
138 return self._ui.configbool('ui', 'slash') and os.sep != '/'
138 return self._ui.configbool('ui', 'slash') and os.sep != '/'
139
139
140 @propertycache
140 @propertycache
141 def _checklink(self):
141 def _checklink(self):
142 return util.checklink(self._root)
142 return util.checklink(self._root)
143
143
144 @propertycache
144 @propertycache
145 def _checkexec(self):
145 def _checkexec(self):
146 return util.checkexec(self._root)
146 return util.checkexec(self._root)
147
147
148 @propertycache
148 @propertycache
149 def _checkcase(self):
149 def _checkcase(self):
150 return not util.checkcase(self._join('.hg'))
150 return not util.checkcase(self._join('.hg'))
151
151
152 def _join(self, f):
152 def _join(self, f):
153 # much faster than os.path.join()
153 # much faster than os.path.join()
154 # it's safe because f is always a relative path
154 # it's safe because f is always a relative path
155 return self._rootdir + f
155 return self._rootdir + f
156
156
157 def flagfunc(self, buildfallback):
157 def flagfunc(self, buildfallback):
158 if self._checklink and self._checkexec:
158 if self._checklink and self._checkexec:
159 def f(x):
159 def f(x):
160 try:
160 try:
161 st = os.lstat(self._join(x))
161 st = os.lstat(self._join(x))
162 if util.statislink(st):
162 if util.statislink(st):
163 return 'l'
163 return 'l'
164 if util.statisexec(st):
164 if util.statisexec(st):
165 return 'x'
165 return 'x'
166 except OSError:
166 except OSError:
167 pass
167 pass
168 return ''
168 return ''
169 return f
169 return f
170
170
171 fallback = buildfallback()
171 fallback = buildfallback()
172 if self._checklink:
172 if self._checklink:
173 def f(x):
173 def f(x):
174 if os.path.islink(self._join(x)):
174 if os.path.islink(self._join(x)):
175 return 'l'
175 return 'l'
176 if 'x' in fallback(x):
176 if 'x' in fallback(x):
177 return 'x'
177 return 'x'
178 return ''
178 return ''
179 return f
179 return f
180 if self._checkexec:
180 if self._checkexec:
181 def f(x):
181 def f(x):
182 if 'l' in fallback(x):
182 if 'l' in fallback(x):
183 return 'l'
183 return 'l'
184 if util.isexec(self._join(x)):
184 if util.isexec(self._join(x)):
185 return 'x'
185 return 'x'
186 return ''
186 return ''
187 return f
187 return f
188 else:
188 else:
189 return fallback
189 return fallback
190
190
191 @propertycache
191 @propertycache
192 def _cwd(self):
192 def _cwd(self):
193 return os.getcwd()
193 return os.getcwd()
194
194
195 def getcwd(self):
195 def getcwd(self):
196 cwd = self._cwd
196 cwd = self._cwd
197 if cwd == self._root:
197 if cwd == self._root:
198 return ''
198 return ''
199 # self._root ends with a path separator if self._root is '/' or 'C:\'
199 # self._root ends with a path separator if self._root is '/' or 'C:\'
200 rootsep = self._root
200 rootsep = self._root
201 if not util.endswithsep(rootsep):
201 if not util.endswithsep(rootsep):
202 rootsep += os.sep
202 rootsep += os.sep
203 if cwd.startswith(rootsep):
203 if cwd.startswith(rootsep):
204 return cwd[len(rootsep):]
204 return cwd[len(rootsep):]
205 else:
205 else:
206 # we're outside the repo. return an absolute path.
206 # we're outside the repo. return an absolute path.
207 return cwd
207 return cwd
208
208
209 def pathto(self, f, cwd=None):
209 def pathto(self, f, cwd=None):
210 if cwd is None:
210 if cwd is None:
211 cwd = self.getcwd()
211 cwd = self.getcwd()
212 path = util.pathto(self._root, cwd, f)
212 path = util.pathto(self._root, cwd, f)
213 if self._slash:
213 if self._slash:
214 return util.pconvert(path)
214 return util.pconvert(path)
215 return path
215 return path
216
216
217 def __getitem__(self, key):
217 def __getitem__(self, key):
218 '''Return the current state of key (a filename) in the dirstate.
218 '''Return the current state of key (a filename) in the dirstate.
219
219
220 States are:
220 States are:
221 n normal
221 n normal
222 m needs merging
222 m needs merging
223 r marked for removal
223 r marked for removal
224 a marked for addition
224 a marked for addition
225 ? not tracked
225 ? not tracked
226 '''
226 '''
227 return self._map.get(key, ("?",))[0]
227 return self._map.get(key, ("?",))[0]
228
228
229 def __contains__(self, key):
229 def __contains__(self, key):
230 return key in self._map
230 return key in self._map
231
231
232 def __iter__(self):
232 def __iter__(self):
233 for x in sorted(self._map):
233 for x in sorted(self._map):
234 yield x
234 yield x
235
235
236 def iteritems(self):
236 def iteritems(self):
237 return self._map.iteritems()
237 return self._map.iteritems()
238
238
239 def parents(self):
239 def parents(self):
240 return [self._validate(p) for p in self._pl]
240 return [self._validate(p) for p in self._pl]
241
241
242 def p1(self):
242 def p1(self):
243 return self._validate(self._pl[0])
243 return self._validate(self._pl[0])
244
244
245 def p2(self):
245 def p2(self):
246 return self._validate(self._pl[1])
246 return self._validate(self._pl[1])
247
247
248 def branch(self):
248 def branch(self):
249 return encoding.tolocal(self._branch)
249 return encoding.tolocal(self._branch)
250
250
251 def setparents(self, p1, p2=nullid):
251 def setparents(self, p1, p2=nullid):
252 """Set dirstate parents to p1 and p2.
252 """Set dirstate parents to p1 and p2.
253
253
254 When moving from two parents to one, 'm' merged entries a
254 When moving from two parents to one, 'm' merged entries a
255 adjusted to normal and previous copy records discarded and
255 adjusted to normal and previous copy records discarded and
256 returned by the call.
256 returned by the call.
257
257
258 See localrepo.setparents()
258 See localrepo.setparents()
259 """
259 """
260 if self._parentwriters == 0:
260 if self._parentwriters == 0:
261 raise ValueError("cannot set dirstate parent without "
261 raise ValueError("cannot set dirstate parent without "
262 "calling dirstate.beginparentchange")
262 "calling dirstate.beginparentchange")
263
263
264 self._dirty = self._dirtypl = True
264 self._dirty = self._dirtypl = True
265 oldp2 = self._pl[1]
265 oldp2 = self._pl[1]
266 self._pl = p1, p2
266 self._pl = p1, p2
267 copies = {}
267 copies = {}
268 if oldp2 != nullid and p2 == nullid:
268 if oldp2 != nullid and p2 == nullid:
269 for f, s in self._map.iteritems():
269 # Discard 'm' markers when moving away from a merge state
270 # Discard 'm' markers when moving away from a merge state
270 for f, s in self._map.iteritems():
271 if s[0] == 'm':
271 if s[0] == 'm':
272 if f in self._copymap:
272 if f in self._copymap:
273 copies[f] = self._copymap[f]
273 copies[f] = self._copymap[f]
274 self.normallookup(f)
274 self.normallookup(f)
275 # Also fix up otherparent markers
276 elif s[0] == 'n' and s[2] == -2:
277 if f in self._copymap:
278 copies[f] = self._copymap[f]
279 self.add(f)
275 return copies
280 return copies
276
281
277 def setbranch(self, branch):
282 def setbranch(self, branch):
278 self._branch = encoding.fromlocal(branch)
283 self._branch = encoding.fromlocal(branch)
279 f = self._opener('branch', 'w', atomictemp=True)
284 f = self._opener('branch', 'w', atomictemp=True)
280 try:
285 try:
281 f.write(self._branch + '\n')
286 f.write(self._branch + '\n')
282 f.close()
287 f.close()
283
288
284 # make sure filecache has the correct stat info for _branch after
289 # make sure filecache has the correct stat info for _branch after
285 # replacing the underlying file
290 # replacing the underlying file
286 ce = self._filecache['_branch']
291 ce = self._filecache['_branch']
287 if ce:
292 if ce:
288 ce.refresh()
293 ce.refresh()
289 except: # re-raises
294 except: # re-raises
290 f.discard()
295 f.discard()
291 raise
296 raise
292
297
293 def _read(self):
298 def _read(self):
294 self._map = {}
299 self._map = {}
295 self._copymap = {}
300 self._copymap = {}
296 try:
301 try:
297 st = self._opener.read("dirstate")
302 st = self._opener.read("dirstate")
298 except IOError, err:
303 except IOError, err:
299 if err.errno != errno.ENOENT:
304 if err.errno != errno.ENOENT:
300 raise
305 raise
301 return
306 return
302 if not st:
307 if not st:
303 return
308 return
304
309
305 # Python's garbage collector triggers a GC each time a certain number
310 # Python's garbage collector triggers a GC each time a certain number
306 # of container objects (the number being defined by
311 # of container objects (the number being defined by
307 # gc.get_threshold()) are allocated. parse_dirstate creates a tuple
312 # gc.get_threshold()) are allocated. parse_dirstate creates a tuple
308 # for each file in the dirstate. The C version then immediately marks
313 # for each file in the dirstate. The C version then immediately marks
309 # them as not to be tracked by the collector. However, this has no
314 # them as not to be tracked by the collector. However, this has no
310 # effect on when GCs are triggered, only on what objects the GC looks
315 # effect on when GCs are triggered, only on what objects the GC looks
311 # into. This means that O(number of files) GCs are unavoidable.
316 # into. This means that O(number of files) GCs are unavoidable.
312 # Depending on when in the process's lifetime the dirstate is parsed,
317 # Depending on when in the process's lifetime the dirstate is parsed,
313 # this can get very expensive. As a workaround, disable GC while
318 # this can get very expensive. As a workaround, disable GC while
314 # parsing the dirstate.
319 # parsing the dirstate.
315 gcenabled = gc.isenabled()
320 gcenabled = gc.isenabled()
316 gc.disable()
321 gc.disable()
317 try:
322 try:
318 p = parsers.parse_dirstate(self._map, self._copymap, st)
323 p = parsers.parse_dirstate(self._map, self._copymap, st)
319 finally:
324 finally:
320 if gcenabled:
325 if gcenabled:
321 gc.enable()
326 gc.enable()
322 if not self._dirtypl:
327 if not self._dirtypl:
323 self._pl = p
328 self._pl = p
324
329
325 def invalidate(self):
330 def invalidate(self):
326 for a in ("_map", "_copymap", "_foldmap", "_branch", "_pl", "_dirs",
331 for a in ("_map", "_copymap", "_foldmap", "_branch", "_pl", "_dirs",
327 "_ignore"):
332 "_ignore"):
328 if a in self.__dict__:
333 if a in self.__dict__:
329 delattr(self, a)
334 delattr(self, a)
330 self._lastnormaltime = 0
335 self._lastnormaltime = 0
331 self._dirty = False
336 self._dirty = False
332 self._parentwriters = 0
337 self._parentwriters = 0
333
338
334 def copy(self, source, dest):
339 def copy(self, source, dest):
335 """Mark dest as a copy of source. Unmark dest if source is None."""
340 """Mark dest as a copy of source. Unmark dest if source is None."""
336 if source == dest:
341 if source == dest:
337 return
342 return
338 self._dirty = True
343 self._dirty = True
339 if source is not None:
344 if source is not None:
340 self._copymap[dest] = source
345 self._copymap[dest] = source
341 elif dest in self._copymap:
346 elif dest in self._copymap:
342 del self._copymap[dest]
347 del self._copymap[dest]
343
348
344 def copied(self, file):
349 def copied(self, file):
345 return self._copymap.get(file, None)
350 return self._copymap.get(file, None)
346
351
347 def copies(self):
352 def copies(self):
348 return self._copymap
353 return self._copymap
349
354
350 def _droppath(self, f):
355 def _droppath(self, f):
351 if self[f] not in "?r" and "_dirs" in self.__dict__:
356 if self[f] not in "?r" and "_dirs" in self.__dict__:
352 self._dirs.delpath(f)
357 self._dirs.delpath(f)
353
358
354 def _addpath(self, f, state, mode, size, mtime):
359 def _addpath(self, f, state, mode, size, mtime):
355 oldstate = self[f]
360 oldstate = self[f]
356 if state == 'a' or oldstate == 'r':
361 if state == 'a' or oldstate == 'r':
357 scmutil.checkfilename(f)
362 scmutil.checkfilename(f)
358 if f in self._dirs:
363 if f in self._dirs:
359 raise util.Abort(_('directory %r already in dirstate') % f)
364 raise util.Abort(_('directory %r already in dirstate') % f)
360 # shadows
365 # shadows
361 for d in scmutil.finddirs(f):
366 for d in scmutil.finddirs(f):
362 if d in self._dirs:
367 if d in self._dirs:
363 break
368 break
364 if d in self._map and self[d] != 'r':
369 if d in self._map and self[d] != 'r':
365 raise util.Abort(
370 raise util.Abort(
366 _('file %r in dirstate clashes with %r') % (d, f))
371 _('file %r in dirstate clashes with %r') % (d, f))
367 if oldstate in "?r" and "_dirs" in self.__dict__:
372 if oldstate in "?r" and "_dirs" in self.__dict__:
368 self._dirs.addpath(f)
373 self._dirs.addpath(f)
369 self._dirty = True
374 self._dirty = True
370 self._map[f] = dirstatetuple(state, mode, size, mtime)
375 self._map[f] = dirstatetuple(state, mode, size, mtime)
371
376
372 def normal(self, f):
377 def normal(self, f):
373 '''Mark a file normal and clean.'''
378 '''Mark a file normal and clean.'''
374 s = os.lstat(self._join(f))
379 s = os.lstat(self._join(f))
375 mtime = int(s.st_mtime)
380 mtime = int(s.st_mtime)
376 self._addpath(f, 'n', s.st_mode,
381 self._addpath(f, 'n', s.st_mode,
377 s.st_size & _rangemask, mtime & _rangemask)
382 s.st_size & _rangemask, mtime & _rangemask)
378 if f in self._copymap:
383 if f in self._copymap:
379 del self._copymap[f]
384 del self._copymap[f]
380 if mtime > self._lastnormaltime:
385 if mtime > self._lastnormaltime:
381 # Remember the most recent modification timeslot for status(),
386 # Remember the most recent modification timeslot for status(),
382 # to make sure we won't miss future size-preserving file content
387 # to make sure we won't miss future size-preserving file content
383 # modifications that happen within the same timeslot.
388 # modifications that happen within the same timeslot.
384 self._lastnormaltime = mtime
389 self._lastnormaltime = mtime
385
390
386 def normallookup(self, f):
391 def normallookup(self, f):
387 '''Mark a file normal, but possibly dirty.'''
392 '''Mark a file normal, but possibly dirty.'''
388 if self._pl[1] != nullid and f in self._map:
393 if self._pl[1] != nullid and f in self._map:
389 # if there is a merge going on and the file was either
394 # if there is a merge going on and the file was either
390 # in state 'm' (-1) or coming from other parent (-2) before
395 # in state 'm' (-1) or coming from other parent (-2) before
391 # being removed, restore that state.
396 # being removed, restore that state.
392 entry = self._map[f]
397 entry = self._map[f]
393 if entry[0] == 'r' and entry[2] in (-1, -2):
398 if entry[0] == 'r' and entry[2] in (-1, -2):
394 source = self._copymap.get(f)
399 source = self._copymap.get(f)
395 if entry[2] == -1:
400 if entry[2] == -1:
396 self.merge(f)
401 self.merge(f)
397 elif entry[2] == -2:
402 elif entry[2] == -2:
398 self.otherparent(f)
403 self.otherparent(f)
399 if source:
404 if source:
400 self.copy(source, f)
405 self.copy(source, f)
401 return
406 return
402 if entry[0] == 'm' or entry[0] == 'n' and entry[2] == -2:
407 if entry[0] == 'm' or entry[0] == 'n' and entry[2] == -2:
403 return
408 return
404 self._addpath(f, 'n', 0, -1, -1)
409 self._addpath(f, 'n', 0, -1, -1)
405 if f in self._copymap:
410 if f in self._copymap:
406 del self._copymap[f]
411 del self._copymap[f]
407
412
408 def otherparent(self, f):
413 def otherparent(self, f):
409 '''Mark as coming from the other parent, always dirty.'''
414 '''Mark as coming from the other parent, always dirty.'''
410 if self._pl[1] == nullid:
415 if self._pl[1] == nullid:
411 raise util.Abort(_("setting %r to other parent "
416 raise util.Abort(_("setting %r to other parent "
412 "only allowed in merges") % f)
417 "only allowed in merges") % f)
413 self._addpath(f, 'n', 0, -2, -1)
418 self._addpath(f, 'n', 0, -2, -1)
414 if f in self._copymap:
419 if f in self._copymap:
415 del self._copymap[f]
420 del self._copymap[f]
416
421
417 def add(self, f):
422 def add(self, f):
418 '''Mark a file added.'''
423 '''Mark a file added.'''
419 self._addpath(f, 'a', 0, -1, -1)
424 self._addpath(f, 'a', 0, -1, -1)
420 if f in self._copymap:
425 if f in self._copymap:
421 del self._copymap[f]
426 del self._copymap[f]
422
427
423 def remove(self, f):
428 def remove(self, f):
424 '''Mark a file removed.'''
429 '''Mark a file removed.'''
425 self._dirty = True
430 self._dirty = True
426 self._droppath(f)
431 self._droppath(f)
427 size = 0
432 size = 0
428 if self._pl[1] != nullid and f in self._map:
433 if self._pl[1] != nullid and f in self._map:
429 # backup the previous state
434 # backup the previous state
430 entry = self._map[f]
435 entry = self._map[f]
431 if entry[0] == 'm': # merge
436 if entry[0] == 'm': # merge
432 size = -1
437 size = -1
433 elif entry[0] == 'n' and entry[2] == -2: # other parent
438 elif entry[0] == 'n' and entry[2] == -2: # other parent
434 size = -2
439 size = -2
435 self._map[f] = dirstatetuple('r', 0, size, 0)
440 self._map[f] = dirstatetuple('r', 0, size, 0)
436 if size == 0 and f in self._copymap:
441 if size == 0 and f in self._copymap:
437 del self._copymap[f]
442 del self._copymap[f]
438
443
439 def merge(self, f):
444 def merge(self, f):
440 '''Mark a file merged.'''
445 '''Mark a file merged.'''
441 if self._pl[1] == nullid:
446 if self._pl[1] == nullid:
442 return self.normallookup(f)
447 return self.normallookup(f)
443 s = os.lstat(self._join(f))
448 s = os.lstat(self._join(f))
444 self._addpath(f, 'm', s.st_mode,
449 self._addpath(f, 'm', s.st_mode,
445 s.st_size & _rangemask, int(s.st_mtime) & _rangemask)
450 s.st_size & _rangemask, int(s.st_mtime) & _rangemask)
446 if f in self._copymap:
451 if f in self._copymap:
447 del self._copymap[f]
452 del self._copymap[f]
448
453
449 def drop(self, f):
454 def drop(self, f):
450 '''Drop a file from the dirstate'''
455 '''Drop a file from the dirstate'''
451 if f in self._map:
456 if f in self._map:
452 self._dirty = True
457 self._dirty = True
453 self._droppath(f)
458 self._droppath(f)
454 del self._map[f]
459 del self._map[f]
455
460
456 def _normalize(self, path, isknown, ignoremissing=False, exists=None):
461 def _normalize(self, path, isknown, ignoremissing=False, exists=None):
457 normed = util.normcase(path)
462 normed = util.normcase(path)
458 folded = self._foldmap.get(normed, None)
463 folded = self._foldmap.get(normed, None)
459 if folded is None:
464 if folded is None:
460 if isknown:
465 if isknown:
461 folded = path
466 folded = path
462 else:
467 else:
463 if exists is None:
468 if exists is None:
464 exists = os.path.lexists(os.path.join(self._root, path))
469 exists = os.path.lexists(os.path.join(self._root, path))
465 if not exists:
470 if not exists:
466 # Maybe a path component exists
471 # Maybe a path component exists
467 if not ignoremissing and '/' in path:
472 if not ignoremissing and '/' in path:
468 d, f = path.rsplit('/', 1)
473 d, f = path.rsplit('/', 1)
469 d = self._normalize(d, isknown, ignoremissing, None)
474 d = self._normalize(d, isknown, ignoremissing, None)
470 folded = d + "/" + f
475 folded = d + "/" + f
471 else:
476 else:
472 # No path components, preserve original case
477 # No path components, preserve original case
473 folded = path
478 folded = path
474 else:
479 else:
475 # recursively normalize leading directory components
480 # recursively normalize leading directory components
476 # against dirstate
481 # against dirstate
477 if '/' in normed:
482 if '/' in normed:
478 d, f = normed.rsplit('/', 1)
483 d, f = normed.rsplit('/', 1)
479 d = self._normalize(d, isknown, ignoremissing, True)
484 d = self._normalize(d, isknown, ignoremissing, True)
480 r = self._root + "/" + d
485 r = self._root + "/" + d
481 folded = d + "/" + util.fspath(f, r)
486 folded = d + "/" + util.fspath(f, r)
482 else:
487 else:
483 folded = util.fspath(normed, self._root)
488 folded = util.fspath(normed, self._root)
484 self._foldmap[normed] = folded
489 self._foldmap[normed] = folded
485
490
486 return folded
491 return folded
487
492
488 def normalize(self, path, isknown=False, ignoremissing=False):
493 def normalize(self, path, isknown=False, ignoremissing=False):
489 '''
494 '''
490 normalize the case of a pathname when on a casefolding filesystem
495 normalize the case of a pathname when on a casefolding filesystem
491
496
492 isknown specifies whether the filename came from walking the
497 isknown specifies whether the filename came from walking the
493 disk, to avoid extra filesystem access.
498 disk, to avoid extra filesystem access.
494
499
495 If ignoremissing is True, missing path are returned
500 If ignoremissing is True, missing path are returned
496 unchanged. Otherwise, we try harder to normalize possibly
501 unchanged. Otherwise, we try harder to normalize possibly
497 existing path components.
502 existing path components.
498
503
499 The normalized case is determined based on the following precedence:
504 The normalized case is determined based on the following precedence:
500
505
501 - version of name already stored in the dirstate
506 - version of name already stored in the dirstate
502 - version of name stored on disk
507 - version of name stored on disk
503 - version provided via command arguments
508 - version provided via command arguments
504 '''
509 '''
505
510
506 if self._checkcase:
511 if self._checkcase:
507 return self._normalize(path, isknown, ignoremissing)
512 return self._normalize(path, isknown, ignoremissing)
508 return path
513 return path
509
514
510 def clear(self):
515 def clear(self):
511 self._map = {}
516 self._map = {}
512 if "_dirs" in self.__dict__:
517 if "_dirs" in self.__dict__:
513 delattr(self, "_dirs")
518 delattr(self, "_dirs")
514 self._copymap = {}
519 self._copymap = {}
515 self._pl = [nullid, nullid]
520 self._pl = [nullid, nullid]
516 self._lastnormaltime = 0
521 self._lastnormaltime = 0
517 self._dirty = True
522 self._dirty = True
518
523
519 def rebuild(self, parent, allfiles, changedfiles=None):
524 def rebuild(self, parent, allfiles, changedfiles=None):
520 changedfiles = changedfiles or allfiles
525 changedfiles = changedfiles or allfiles
521 oldmap = self._map
526 oldmap = self._map
522 self.clear()
527 self.clear()
523 for f in allfiles:
528 for f in allfiles:
524 if f not in changedfiles:
529 if f not in changedfiles:
525 self._map[f] = oldmap[f]
530 self._map[f] = oldmap[f]
526 else:
531 else:
527 if 'x' in allfiles.flags(f):
532 if 'x' in allfiles.flags(f):
528 self._map[f] = dirstatetuple('n', 0777, -1, 0)
533 self._map[f] = dirstatetuple('n', 0777, -1, 0)
529 else:
534 else:
530 self._map[f] = dirstatetuple('n', 0666, -1, 0)
535 self._map[f] = dirstatetuple('n', 0666, -1, 0)
531 self._pl = (parent, nullid)
536 self._pl = (parent, nullid)
532 self._dirty = True
537 self._dirty = True
533
538
534 def write(self):
539 def write(self):
535 if not self._dirty:
540 if not self._dirty:
536 return
541 return
537
542
538 # enough 'delaywrite' prevents 'pack_dirstate' from dropping
543 # enough 'delaywrite' prevents 'pack_dirstate' from dropping
539 # timestamp of each entries in dirstate, because of 'now > mtime'
544 # timestamp of each entries in dirstate, because of 'now > mtime'
540 delaywrite = self._ui.configint('debug', 'dirstate.delaywrite', 0)
545 delaywrite = self._ui.configint('debug', 'dirstate.delaywrite', 0)
541 if delaywrite:
546 if delaywrite:
542 import time # to avoid useless import
547 import time # to avoid useless import
543 time.sleep(delaywrite)
548 time.sleep(delaywrite)
544
549
545 st = self._opener("dirstate", "w", atomictemp=True)
550 st = self._opener("dirstate", "w", atomictemp=True)
546 # use the modification time of the newly created temporary file as the
551 # use the modification time of the newly created temporary file as the
547 # filesystem's notion of 'now'
552 # filesystem's notion of 'now'
548 now = util.fstat(st).st_mtime
553 now = util.fstat(st).st_mtime
549 st.write(parsers.pack_dirstate(self._map, self._copymap, self._pl, now))
554 st.write(parsers.pack_dirstate(self._map, self._copymap, self._pl, now))
550 st.close()
555 st.close()
551 self._lastnormaltime = 0
556 self._lastnormaltime = 0
552 self._dirty = self._dirtypl = False
557 self._dirty = self._dirtypl = False
553
558
554 def _dirignore(self, f):
559 def _dirignore(self, f):
555 if f == '.':
560 if f == '.':
556 return False
561 return False
557 if self._ignore(f):
562 if self._ignore(f):
558 return True
563 return True
559 for p in scmutil.finddirs(f):
564 for p in scmutil.finddirs(f):
560 if self._ignore(p):
565 if self._ignore(p):
561 return True
566 return True
562 return False
567 return False
563
568
564 def _walkexplicit(self, match, subrepos):
569 def _walkexplicit(self, match, subrepos):
565 '''Get stat data about the files explicitly specified by match.
570 '''Get stat data about the files explicitly specified by match.
566
571
567 Return a triple (results, dirsfound, dirsnotfound).
572 Return a triple (results, dirsfound, dirsnotfound).
568 - results is a mapping from filename to stat result. It also contains
573 - results is a mapping from filename to stat result. It also contains
569 listings mapping subrepos and .hg to None.
574 listings mapping subrepos and .hg to None.
570 - dirsfound is a list of files found to be directories.
575 - dirsfound is a list of files found to be directories.
571 - dirsnotfound is a list of files that the dirstate thinks are
576 - dirsnotfound is a list of files that the dirstate thinks are
572 directories and that were not found.'''
577 directories and that were not found.'''
573
578
574 def badtype(mode):
579 def badtype(mode):
575 kind = _('unknown')
580 kind = _('unknown')
576 if stat.S_ISCHR(mode):
581 if stat.S_ISCHR(mode):
577 kind = _('character device')
582 kind = _('character device')
578 elif stat.S_ISBLK(mode):
583 elif stat.S_ISBLK(mode):
579 kind = _('block device')
584 kind = _('block device')
580 elif stat.S_ISFIFO(mode):
585 elif stat.S_ISFIFO(mode):
581 kind = _('fifo')
586 kind = _('fifo')
582 elif stat.S_ISSOCK(mode):
587 elif stat.S_ISSOCK(mode):
583 kind = _('socket')
588 kind = _('socket')
584 elif stat.S_ISDIR(mode):
589 elif stat.S_ISDIR(mode):
585 kind = _('directory')
590 kind = _('directory')
586 return _('unsupported file type (type is %s)') % kind
591 return _('unsupported file type (type is %s)') % kind
587
592
588 matchedir = match.explicitdir
593 matchedir = match.explicitdir
589 badfn = match.bad
594 badfn = match.bad
590 dmap = self._map
595 dmap = self._map
591 normpath = util.normpath
596 normpath = util.normpath
592 lstat = os.lstat
597 lstat = os.lstat
593 getkind = stat.S_IFMT
598 getkind = stat.S_IFMT
594 dirkind = stat.S_IFDIR
599 dirkind = stat.S_IFDIR
595 regkind = stat.S_IFREG
600 regkind = stat.S_IFREG
596 lnkkind = stat.S_IFLNK
601 lnkkind = stat.S_IFLNK
597 join = self._join
602 join = self._join
598 dirsfound = []
603 dirsfound = []
599 foundadd = dirsfound.append
604 foundadd = dirsfound.append
600 dirsnotfound = []
605 dirsnotfound = []
601 notfoundadd = dirsnotfound.append
606 notfoundadd = dirsnotfound.append
602
607
603 if match.matchfn != match.exact and self._checkcase:
608 if match.matchfn != match.exact and self._checkcase:
604 normalize = self._normalize
609 normalize = self._normalize
605 else:
610 else:
606 normalize = None
611 normalize = None
607
612
608 files = sorted(match.files())
613 files = sorted(match.files())
609 subrepos.sort()
614 subrepos.sort()
610 i, j = 0, 0
615 i, j = 0, 0
611 while i < len(files) and j < len(subrepos):
616 while i < len(files) and j < len(subrepos):
612 subpath = subrepos[j] + "/"
617 subpath = subrepos[j] + "/"
613 if files[i] < subpath:
618 if files[i] < subpath:
614 i += 1
619 i += 1
615 continue
620 continue
616 while i < len(files) and files[i].startswith(subpath):
621 while i < len(files) and files[i].startswith(subpath):
617 del files[i]
622 del files[i]
618 j += 1
623 j += 1
619
624
620 if not files or '.' in files:
625 if not files or '.' in files:
621 files = ['']
626 files = ['']
622 results = dict.fromkeys(subrepos)
627 results = dict.fromkeys(subrepos)
623 results['.hg'] = None
628 results['.hg'] = None
624
629
625 for ff in files:
630 for ff in files:
626 if normalize:
631 if normalize:
627 nf = normalize(normpath(ff), False, True)
632 nf = normalize(normpath(ff), False, True)
628 else:
633 else:
629 nf = normpath(ff)
634 nf = normpath(ff)
630 if nf in results:
635 if nf in results:
631 continue
636 continue
632
637
633 try:
638 try:
634 st = lstat(join(nf))
639 st = lstat(join(nf))
635 kind = getkind(st.st_mode)
640 kind = getkind(st.st_mode)
636 if kind == dirkind:
641 if kind == dirkind:
637 if nf in dmap:
642 if nf in dmap:
638 # file replaced by dir on disk but still in dirstate
643 # file replaced by dir on disk but still in dirstate
639 results[nf] = None
644 results[nf] = None
640 if matchedir:
645 if matchedir:
641 matchedir(nf)
646 matchedir(nf)
642 foundadd(nf)
647 foundadd(nf)
643 elif kind == regkind or kind == lnkkind:
648 elif kind == regkind or kind == lnkkind:
644 results[nf] = st
649 results[nf] = st
645 else:
650 else:
646 badfn(ff, badtype(kind))
651 badfn(ff, badtype(kind))
647 if nf in dmap:
652 if nf in dmap:
648 results[nf] = None
653 results[nf] = None
649 except OSError, inst: # nf not found on disk - it is dirstate only
654 except OSError, inst: # nf not found on disk - it is dirstate only
650 if nf in dmap: # does it exactly match a missing file?
655 if nf in dmap: # does it exactly match a missing file?
651 results[nf] = None
656 results[nf] = None
652 else: # does it match a missing directory?
657 else: # does it match a missing directory?
653 prefix = nf + "/"
658 prefix = nf + "/"
654 for fn in dmap:
659 for fn in dmap:
655 if fn.startswith(prefix):
660 if fn.startswith(prefix):
656 if matchedir:
661 if matchedir:
657 matchedir(nf)
662 matchedir(nf)
658 notfoundadd(nf)
663 notfoundadd(nf)
659 break
664 break
660 else:
665 else:
661 badfn(ff, inst.strerror)
666 badfn(ff, inst.strerror)
662
667
663 return results, dirsfound, dirsnotfound
668 return results, dirsfound, dirsnotfound
664
669
665 def walk(self, match, subrepos, unknown, ignored, full=True):
670 def walk(self, match, subrepos, unknown, ignored, full=True):
666 '''
671 '''
667 Walk recursively through the directory tree, finding all files
672 Walk recursively through the directory tree, finding all files
668 matched by match.
673 matched by match.
669
674
670 If full is False, maybe skip some known-clean files.
675 If full is False, maybe skip some known-clean files.
671
676
672 Return a dict mapping filename to stat-like object (either
677 Return a dict mapping filename to stat-like object (either
673 mercurial.osutil.stat instance or return value of os.stat()).
678 mercurial.osutil.stat instance or return value of os.stat()).
674
679
675 '''
680 '''
676 # full is a flag that extensions that hook into walk can use -- this
681 # full is a flag that extensions that hook into walk can use -- this
677 # implementation doesn't use it at all. This satisfies the contract
682 # implementation doesn't use it at all. This satisfies the contract
678 # because we only guarantee a "maybe".
683 # because we only guarantee a "maybe".
679
684
680 if ignored:
685 if ignored:
681 ignore = util.never
686 ignore = util.never
682 dirignore = util.never
687 dirignore = util.never
683 elif unknown:
688 elif unknown:
684 ignore = self._ignore
689 ignore = self._ignore
685 dirignore = self._dirignore
690 dirignore = self._dirignore
686 else:
691 else:
687 # if not unknown and not ignored, drop dir recursion and step 2
692 # if not unknown and not ignored, drop dir recursion and step 2
688 ignore = util.always
693 ignore = util.always
689 dirignore = util.always
694 dirignore = util.always
690
695
691 matchfn = match.matchfn
696 matchfn = match.matchfn
692 matchalways = match.always()
697 matchalways = match.always()
693 matchtdir = match.traversedir
698 matchtdir = match.traversedir
694 dmap = self._map
699 dmap = self._map
695 listdir = osutil.listdir
700 listdir = osutil.listdir
696 lstat = os.lstat
701 lstat = os.lstat
697 dirkind = stat.S_IFDIR
702 dirkind = stat.S_IFDIR
698 regkind = stat.S_IFREG
703 regkind = stat.S_IFREG
699 lnkkind = stat.S_IFLNK
704 lnkkind = stat.S_IFLNK
700 join = self._join
705 join = self._join
701
706
702 exact = skipstep3 = False
707 exact = skipstep3 = False
703 if matchfn == match.exact: # match.exact
708 if matchfn == match.exact: # match.exact
704 exact = True
709 exact = True
705 dirignore = util.always # skip step 2
710 dirignore = util.always # skip step 2
706 elif match.files() and not match.anypats(): # match.match, no patterns
711 elif match.files() and not match.anypats(): # match.match, no patterns
707 skipstep3 = True
712 skipstep3 = True
708
713
709 if not exact and self._checkcase:
714 if not exact and self._checkcase:
710 normalize = self._normalize
715 normalize = self._normalize
711 skipstep3 = False
716 skipstep3 = False
712 else:
717 else:
713 normalize = None
718 normalize = None
714
719
715 # step 1: find all explicit files
720 # step 1: find all explicit files
716 results, work, dirsnotfound = self._walkexplicit(match, subrepos)
721 results, work, dirsnotfound = self._walkexplicit(match, subrepos)
717
722
718 skipstep3 = skipstep3 and not (work or dirsnotfound)
723 skipstep3 = skipstep3 and not (work or dirsnotfound)
719 work = [d for d in work if not dirignore(d)]
724 work = [d for d in work if not dirignore(d)]
720 wadd = work.append
725 wadd = work.append
721
726
722 # step 2: visit subdirectories
727 # step 2: visit subdirectories
723 while work:
728 while work:
724 nd = work.pop()
729 nd = work.pop()
725 skip = None
730 skip = None
726 if nd == '.':
731 if nd == '.':
727 nd = ''
732 nd = ''
728 else:
733 else:
729 skip = '.hg'
734 skip = '.hg'
730 try:
735 try:
731 entries = listdir(join(nd), stat=True, skip=skip)
736 entries = listdir(join(nd), stat=True, skip=skip)
732 except OSError, inst:
737 except OSError, inst:
733 if inst.errno in (errno.EACCES, errno.ENOENT):
738 if inst.errno in (errno.EACCES, errno.ENOENT):
734 match.bad(self.pathto(nd), inst.strerror)
739 match.bad(self.pathto(nd), inst.strerror)
735 continue
740 continue
736 raise
741 raise
737 for f, kind, st in entries:
742 for f, kind, st in entries:
738 if normalize:
743 if normalize:
739 nf = normalize(nd and (nd + "/" + f) or f, True, True)
744 nf = normalize(nd and (nd + "/" + f) or f, True, True)
740 else:
745 else:
741 nf = nd and (nd + "/" + f) or f
746 nf = nd and (nd + "/" + f) or f
742 if nf not in results:
747 if nf not in results:
743 if kind == dirkind:
748 if kind == dirkind:
744 if not ignore(nf):
749 if not ignore(nf):
745 if matchtdir:
750 if matchtdir:
746 matchtdir(nf)
751 matchtdir(nf)
747 wadd(nf)
752 wadd(nf)
748 if nf in dmap and (matchalways or matchfn(nf)):
753 if nf in dmap and (matchalways or matchfn(nf)):
749 results[nf] = None
754 results[nf] = None
750 elif kind == regkind or kind == lnkkind:
755 elif kind == regkind or kind == lnkkind:
751 if nf in dmap:
756 if nf in dmap:
752 if matchalways or matchfn(nf):
757 if matchalways or matchfn(nf):
753 results[nf] = st
758 results[nf] = st
754 elif (matchalways or matchfn(nf)) and not ignore(nf):
759 elif (matchalways or matchfn(nf)) and not ignore(nf):
755 results[nf] = st
760 results[nf] = st
756 elif nf in dmap and (matchalways or matchfn(nf)):
761 elif nf in dmap and (matchalways or matchfn(nf)):
757 results[nf] = None
762 results[nf] = None
758
763
759 for s in subrepos:
764 for s in subrepos:
760 del results[s]
765 del results[s]
761 del results['.hg']
766 del results['.hg']
762
767
763 # step 3: visit remaining files from dmap
768 # step 3: visit remaining files from dmap
764 if not skipstep3 and not exact:
769 if not skipstep3 and not exact:
765 # If a dmap file is not in results yet, it was either
770 # If a dmap file is not in results yet, it was either
766 # a) not matching matchfn b) ignored, c) missing, or d) under a
771 # a) not matching matchfn b) ignored, c) missing, or d) under a
767 # symlink directory.
772 # symlink directory.
768 if not results and matchalways:
773 if not results and matchalways:
769 visit = dmap.keys()
774 visit = dmap.keys()
770 else:
775 else:
771 visit = [f for f in dmap if f not in results and matchfn(f)]
776 visit = [f for f in dmap if f not in results and matchfn(f)]
772 visit.sort()
777 visit.sort()
773
778
774 if unknown:
779 if unknown:
775 # unknown == True means we walked all dirs under the roots
780 # unknown == True means we walked all dirs under the roots
776 # that wasn't ignored, and everything that matched was stat'ed
781 # that wasn't ignored, and everything that matched was stat'ed
777 # and is already in results.
782 # and is already in results.
778 # The rest must thus be ignored or under a symlink.
783 # The rest must thus be ignored or under a symlink.
779 audit_path = pathutil.pathauditor(self._root)
784 audit_path = pathutil.pathauditor(self._root)
780
785
781 for nf in iter(visit):
786 for nf in iter(visit):
782 # Report ignored items in the dmap as long as they are not
787 # Report ignored items in the dmap as long as they are not
783 # under a symlink directory.
788 # under a symlink directory.
784 if audit_path.check(nf):
789 if audit_path.check(nf):
785 try:
790 try:
786 results[nf] = lstat(join(nf))
791 results[nf] = lstat(join(nf))
787 # file was just ignored, no links, and exists
792 # file was just ignored, no links, and exists
788 except OSError:
793 except OSError:
789 # file doesn't exist
794 # file doesn't exist
790 results[nf] = None
795 results[nf] = None
791 else:
796 else:
792 # It's either missing or under a symlink directory
797 # It's either missing or under a symlink directory
793 # which we in this case report as missing
798 # which we in this case report as missing
794 results[nf] = None
799 results[nf] = None
795 else:
800 else:
796 # We may not have walked the full directory tree above,
801 # We may not have walked the full directory tree above,
797 # so stat and check everything we missed.
802 # so stat and check everything we missed.
798 nf = iter(visit).next
803 nf = iter(visit).next
799 for st in util.statfiles([join(i) for i in visit]):
804 for st in util.statfiles([join(i) for i in visit]):
800 results[nf()] = st
805 results[nf()] = st
801 return results
806 return results
802
807
803 def status(self, match, subrepos, ignored, clean, unknown):
808 def status(self, match, subrepos, ignored, clean, unknown):
804 '''Determine the status of the working copy relative to the
809 '''Determine the status of the working copy relative to the
805 dirstate and return a tuple of lists (unsure, modified, added,
810 dirstate and return a tuple of lists (unsure, modified, added,
806 removed, deleted, unknown, ignored, clean), where:
811 removed, deleted, unknown, ignored, clean), where:
807
812
808 unsure:
813 unsure:
809 files that might have been modified since the dirstate was
814 files that might have been modified since the dirstate was
810 written, but need to be read to be sure (size is the same
815 written, but need to be read to be sure (size is the same
811 but mtime differs)
816 but mtime differs)
812 modified:
817 modified:
813 files that have definitely been modified since the dirstate
818 files that have definitely been modified since the dirstate
814 was written (different size or mode)
819 was written (different size or mode)
815 added:
820 added:
816 files that have been explicitly added with hg add
821 files that have been explicitly added with hg add
817 removed:
822 removed:
818 files that have been explicitly removed with hg remove
823 files that have been explicitly removed with hg remove
819 deleted:
824 deleted:
820 files that have been deleted through other means ("missing")
825 files that have been deleted through other means ("missing")
821 unknown:
826 unknown:
822 files not in the dirstate that are not ignored
827 files not in the dirstate that are not ignored
823 ignored:
828 ignored:
824 files not in the dirstate that are ignored
829 files not in the dirstate that are ignored
825 (by _dirignore())
830 (by _dirignore())
826 clean:
831 clean:
827 files that have definitely not been modified since the
832 files that have definitely not been modified since the
828 dirstate was written
833 dirstate was written
829 '''
834 '''
830 listignored, listclean, listunknown = ignored, clean, unknown
835 listignored, listclean, listunknown = ignored, clean, unknown
831 lookup, modified, added, unknown, ignored = [], [], [], [], []
836 lookup, modified, added, unknown, ignored = [], [], [], [], []
832 removed, deleted, clean = [], [], []
837 removed, deleted, clean = [], [], []
833
838
834 dmap = self._map
839 dmap = self._map
835 ladd = lookup.append # aka "unsure"
840 ladd = lookup.append # aka "unsure"
836 madd = modified.append
841 madd = modified.append
837 aadd = added.append
842 aadd = added.append
838 uadd = unknown.append
843 uadd = unknown.append
839 iadd = ignored.append
844 iadd = ignored.append
840 radd = removed.append
845 radd = removed.append
841 dadd = deleted.append
846 dadd = deleted.append
842 cadd = clean.append
847 cadd = clean.append
843 mexact = match.exact
848 mexact = match.exact
844 dirignore = self._dirignore
849 dirignore = self._dirignore
845 checkexec = self._checkexec
850 checkexec = self._checkexec
846 copymap = self._copymap
851 copymap = self._copymap
847 lastnormaltime = self._lastnormaltime
852 lastnormaltime = self._lastnormaltime
848
853
849 # We need to do full walks when either
854 # We need to do full walks when either
850 # - we're listing all clean files, or
855 # - we're listing all clean files, or
851 # - match.traversedir does something, because match.traversedir should
856 # - match.traversedir does something, because match.traversedir should
852 # be called for every dir in the working dir
857 # be called for every dir in the working dir
853 full = listclean or match.traversedir is not None
858 full = listclean or match.traversedir is not None
854 for fn, st in self.walk(match, subrepos, listunknown, listignored,
859 for fn, st in self.walk(match, subrepos, listunknown, listignored,
855 full=full).iteritems():
860 full=full).iteritems():
856 if fn not in dmap:
861 if fn not in dmap:
857 if (listignored or mexact(fn)) and dirignore(fn):
862 if (listignored or mexact(fn)) and dirignore(fn):
858 if listignored:
863 if listignored:
859 iadd(fn)
864 iadd(fn)
860 else:
865 else:
861 uadd(fn)
866 uadd(fn)
862 continue
867 continue
863
868
864 # This is equivalent to 'state, mode, size, time = dmap[fn]' but not
869 # This is equivalent to 'state, mode, size, time = dmap[fn]' but not
865 # written like that for performance reasons. dmap[fn] is not a
870 # written like that for performance reasons. dmap[fn] is not a
866 # Python tuple in compiled builds. The CPython UNPACK_SEQUENCE
871 # Python tuple in compiled builds. The CPython UNPACK_SEQUENCE
867 # opcode has fast paths when the value to be unpacked is a tuple or
872 # opcode has fast paths when the value to be unpacked is a tuple or
868 # a list, but falls back to creating a full-fledged iterator in
873 # a list, but falls back to creating a full-fledged iterator in
869 # general. That is much slower than simply accessing and storing the
874 # general. That is much slower than simply accessing and storing the
870 # tuple members one by one.
875 # tuple members one by one.
871 t = dmap[fn]
876 t = dmap[fn]
872 state = t[0]
877 state = t[0]
873 mode = t[1]
878 mode = t[1]
874 size = t[2]
879 size = t[2]
875 time = t[3]
880 time = t[3]
876
881
877 if not st and state in "nma":
882 if not st and state in "nma":
878 dadd(fn)
883 dadd(fn)
879 elif state == 'n':
884 elif state == 'n':
880 mtime = int(st.st_mtime)
885 mtime = int(st.st_mtime)
881 if (size >= 0 and
886 if (size >= 0 and
882 ((size != st.st_size and size != st.st_size & _rangemask)
887 ((size != st.st_size and size != st.st_size & _rangemask)
883 or ((mode ^ st.st_mode) & 0100 and checkexec))
888 or ((mode ^ st.st_mode) & 0100 and checkexec))
884 or size == -2 # other parent
889 or size == -2 # other parent
885 or fn in copymap):
890 or fn in copymap):
886 madd(fn)
891 madd(fn)
887 elif time != mtime and time != mtime & _rangemask:
892 elif time != mtime and time != mtime & _rangemask:
888 ladd(fn)
893 ladd(fn)
889 elif mtime == lastnormaltime:
894 elif mtime == lastnormaltime:
890 # fn may have been changed in the same timeslot without
895 # fn may have been changed in the same timeslot without
891 # changing its size. This can happen if we quickly do
896 # changing its size. This can happen if we quickly do
892 # multiple commits in a single transaction.
897 # multiple commits in a single transaction.
893 # Force lookup, so we don't miss such a racy file change.
898 # Force lookup, so we don't miss such a racy file change.
894 ladd(fn)
899 ladd(fn)
895 elif listclean:
900 elif listclean:
896 cadd(fn)
901 cadd(fn)
897 elif state == 'm':
902 elif state == 'm':
898 madd(fn)
903 madd(fn)
899 elif state == 'a':
904 elif state == 'a':
900 aadd(fn)
905 aadd(fn)
901 elif state == 'r':
906 elif state == 'r':
902 radd(fn)
907 radd(fn)
903
908
904 return (lookup, modified, added, removed, deleted, unknown, ignored,
909 return (lookup, modified, added, removed, deleted, unknown, ignored,
905 clean)
910 clean)
906
911
907 def matches(self, match):
912 def matches(self, match):
908 '''
913 '''
909 return files in the dirstate (in whatever state) filtered by match
914 return files in the dirstate (in whatever state) filtered by match
910 '''
915 '''
911 dmap = self._map
916 dmap = self._map
912 if match.always():
917 if match.always():
913 return dmap.keys()
918 return dmap.keys()
914 files = match.files()
919 files = match.files()
915 if match.matchfn == match.exact:
920 if match.matchfn == match.exact:
916 # fast path -- filter the other way around, since typically files is
921 # fast path -- filter the other way around, since typically files is
917 # much smaller than dmap
922 # much smaller than dmap
918 return [f for f in files if f in dmap]
923 return [f for f in files if f in dmap]
919 if not match.anypats() and util.all(fn in dmap for fn in files):
924 if not match.anypats() and util.all(fn in dmap for fn in files):
920 # fast path -- all the values are known to be files, so just return
925 # fast path -- all the values are known to be files, so just return
921 # that
926 # that
922 return list(files)
927 return list(files)
923 return [f for f in dmap if match(f)]
928 return [f for f in dmap if match(f)]
@@ -1,211 +1,211
1 test for old histedit issue #6:
1 test for old histedit issue #6:
2 editing a changeset without any actual change would corrupt the repository
2 editing a changeset without any actual change would corrupt the repository
3
3
4 $ . "$TESTDIR/histedit-helpers.sh"
4 $ . "$TESTDIR/histedit-helpers.sh"
5
5
6 $ cat >> $HGRCPATH <<EOF
6 $ cat >> $HGRCPATH <<EOF
7 > [extensions]
7 > [extensions]
8 > histedit=
8 > histedit=
9 > EOF
9 > EOF
10
10
11 $ initrepo ()
11 $ initrepo ()
12 > {
12 > {
13 > dir="$1"
13 > dir="$1"
14 > comment="$2"
14 > comment="$2"
15 > if [ -n "${comment}" ]; then
15 > if [ -n "${comment}" ]; then
16 > echo % ${comment}
16 > echo % ${comment}
17 > echo % ${comment} | sed 's:.:-:g'
17 > echo % ${comment} | sed 's:.:-:g'
18 > fi
18 > fi
19 > hg init ${dir}
19 > hg init ${dir}
20 > cd ${dir}
20 > cd ${dir}
21 > for x in a b c d e f ; do
21 > for x in a b c d e f ; do
22 > echo $x > $x
22 > echo $x > $x
23 > hg add $x
23 > hg add $x
24 > hg ci -m $x
24 > hg ci -m $x
25 > done
25 > done
26 > cd ..
26 > cd ..
27 > }
27 > }
28
28
29 $ geneditor ()
29 $ geneditor ()
30 > {
30 > {
31 > # generate an editor script for selecting changesets to be edited
31 > # generate an editor script for selecting changesets to be edited
32 > choice=$1 # changesets that should be edited (using sed line ranges)
32 > choice=$1 # changesets that should be edited (using sed line ranges)
33 > cat <<EOF | sed 's:^....::'
33 > cat <<EOF | sed 's:^....::'
34 > # editing the rules, replacing 'pick' with 'edit' for the chosen lines
34 > # editing the rules, replacing 'pick' with 'edit' for the chosen lines
35 > sed '${choice}s:^pick:edit:' "\$1" > "\${1}.tmp"
35 > sed '${choice}s:^pick:edit:' "\$1" > "\${1}.tmp"
36 > mv "\${1}.tmp" "\$1"
36 > mv "\${1}.tmp" "\$1"
37 > # displaying the resulting rules, minus comments and empty lines
37 > # displaying the resulting rules, minus comments and empty lines
38 > sed '/^#/d;/^$/d;s:^:| :' "\$1" >&2
38 > sed '/^#/d;/^$/d;s:^:| :' "\$1" >&2
39 > EOF
39 > EOF
40 > }
40 > }
41
41
42 $ startediting ()
42 $ startediting ()
43 > {
43 > {
44 > # begin an editing session
44 > # begin an editing session
45 > choice="$1" # changesets that should be edited
45 > choice="$1" # changesets that should be edited
46 > number="$2" # number of changesets considered (from tip)
46 > number="$2" # number of changesets considered (from tip)
47 > comment="$3"
47 > comment="$3"
48 > geneditor "${choice}" > edit.sh
48 > geneditor "${choice}" > edit.sh
49 > echo % start editing the history ${comment}
49 > echo % start editing the history ${comment}
50 > HGEDITOR="sh ./edit.sh" hg histedit -- -${number} 2>&1 | fixbundle
50 > HGEDITOR="sh ./edit.sh" hg histedit -- -${number} 2>&1 | fixbundle
51 > }
51 > }
52
52
53 $ continueediting ()
53 $ continueediting ()
54 > {
54 > {
55 > # continue an edit already in progress
55 > # continue an edit already in progress
56 > editor="$1" # message editor when finalizing editing
56 > editor="$1" # message editor when finalizing editing
57 > comment="$2"
57 > comment="$2"
58 > echo % finalize changeset editing ${comment}
58 > echo % finalize changeset editing ${comment}
59 > HGEDITOR=${editor} hg histedit --continue 2>&1 | fixbundle
59 > HGEDITOR=${editor} hg histedit --continue 2>&1 | fixbundle
60 > }
60 > }
61
61
62 $ graphlog ()
62 $ graphlog ()
63 > {
63 > {
64 > comment="${1:-log}"
64 > comment="${1:-log}"
65 > echo % "${comment}"
65 > echo % "${comment}"
66 > hg log -G --template '{rev} {node} \"{desc|firstline}\"\n'
66 > hg log -G --template '{rev} {node} \"{desc|firstline}\"\n'
67 > }
67 > }
68
68
69
69
70 $ initrepo r1 "test editing with no change"
70 $ initrepo r1 "test editing with no change"
71 % test editing with no change
71 % test editing with no change
72 -----------------------------
72 -----------------------------
73 $ cd r1
73 $ cd r1
74 $ graphlog "log before editing"
74 $ graphlog "log before editing"
75 % log before editing
75 % log before editing
76 @ 5 652413bf663ef2a641cab26574e46d5f5a64a55a "f"
76 @ 5 652413bf663ef2a641cab26574e46d5f5a64a55a "f"
77 |
77 |
78 o 4 e860deea161a2f77de56603b340ebbb4536308ae "e"
78 o 4 e860deea161a2f77de56603b340ebbb4536308ae "e"
79 |
79 |
80 o 3 055a42cdd88768532f9cf79daa407fc8d138de9b "d"
80 o 3 055a42cdd88768532f9cf79daa407fc8d138de9b "d"
81 |
81 |
82 o 2 177f92b773850b59254aa5e923436f921b55483b "c"
82 o 2 177f92b773850b59254aa5e923436f921b55483b "c"
83 |
83 |
84 o 1 d2ae7f538514cd87c17547b0de4cea71fe1af9fb "b"
84 o 1 d2ae7f538514cd87c17547b0de4cea71fe1af9fb "b"
85 |
85 |
86 o 0 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b "a"
86 o 0 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b "a"
87
87
88 $ startediting 2 3 "(not changing anything)" # edit the 2nd of 3 changesets
88 $ startediting 2 3 "(not changing anything)" # edit the 2nd of 3 changesets
89 % start editing the history (not changing anything)
89 % start editing the history (not changing anything)
90 | pick 055a42cdd887 3 d
90 | pick 055a42cdd887 3 d
91 | edit e860deea161a 4 e
91 | edit e860deea161a 4 e
92 | pick 652413bf663e 5 f
92 | pick 652413bf663e 5 f
93 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
93 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
94 Make changes as needed, you may commit or record as needed now.
94 Make changes as needed, you may commit or record as needed now.
95 When you are finished, run hg histedit --continue to resume.
95 When you are finished, run hg histedit --continue to resume.
96 $ continueediting true "(leaving commit message unaltered)"
96 $ continueediting true "(leaving commit message unaltered)"
97 % finalize changeset editing (leaving commit message unaltered)
97 % finalize changeset editing (leaving commit message unaltered)
98 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
98 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
99 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
99 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
100
100
101
101
102 check state of working copy
102 check state of working copy
103 $ hg id
103 $ hg id
104 794fe033d0a0 tip
104 794fe033d0a0 tip
105
105
106 $ graphlog "log after history editing"
106 $ graphlog "log after history editing"
107 % log after history editing
107 % log after history editing
108 @ 5 794fe033d0a030f8df77c5de945fca35c9181c30 "f"
108 @ 5 794fe033d0a030f8df77c5de945fca35c9181c30 "f"
109 |
109 |
110 o 4 04d2fab980779f332dec458cc944f28de8b43435 "e"
110 o 4 04d2fab980779f332dec458cc944f28de8b43435 "e"
111 |
111 |
112 o 3 055a42cdd88768532f9cf79daa407fc8d138de9b "d"
112 o 3 055a42cdd88768532f9cf79daa407fc8d138de9b "d"
113 |
113 |
114 o 2 177f92b773850b59254aa5e923436f921b55483b "c"
114 o 2 177f92b773850b59254aa5e923436f921b55483b "c"
115 |
115 |
116 o 1 d2ae7f538514cd87c17547b0de4cea71fe1af9fb "b"
116 o 1 d2ae7f538514cd87c17547b0de4cea71fe1af9fb "b"
117 |
117 |
118 o 0 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b "a"
118 o 0 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b "a"
119
119
120
120
121 $ cd ..
121 $ cd ..
122
122
123 $ initrepo r2 "test editing with no change, then abort"
123 $ initrepo r2 "test editing with no change, then abort"
124 % test editing with no change, then abort
124 % test editing with no change, then abort
125 -----------------------------------------
125 -----------------------------------------
126 $ cd r2
126 $ cd r2
127 $ graphlog "log before editing"
127 $ graphlog "log before editing"
128 % log before editing
128 % log before editing
129 @ 5 652413bf663ef2a641cab26574e46d5f5a64a55a "f"
129 @ 5 652413bf663ef2a641cab26574e46d5f5a64a55a "f"
130 |
130 |
131 o 4 e860deea161a2f77de56603b340ebbb4536308ae "e"
131 o 4 e860deea161a2f77de56603b340ebbb4536308ae "e"
132 |
132 |
133 o 3 055a42cdd88768532f9cf79daa407fc8d138de9b "d"
133 o 3 055a42cdd88768532f9cf79daa407fc8d138de9b "d"
134 |
134 |
135 o 2 177f92b773850b59254aa5e923436f921b55483b "c"
135 o 2 177f92b773850b59254aa5e923436f921b55483b "c"
136 |
136 |
137 o 1 d2ae7f538514cd87c17547b0de4cea71fe1af9fb "b"
137 o 1 d2ae7f538514cd87c17547b0de4cea71fe1af9fb "b"
138 |
138 |
139 o 0 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b "a"
139 o 0 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b "a"
140
140
141 $ startediting 1,2 3 "(not changing anything)" # edit the 1st two of 3 changesets
141 $ startediting 1,2 3 "(not changing anything)" # edit the 1st two of 3 changesets
142 % start editing the history (not changing anything)
142 % start editing the history (not changing anything)
143 | edit 055a42cdd887 3 d
143 | edit 055a42cdd887 3 d
144 | edit e860deea161a 4 e
144 | edit e860deea161a 4 e
145 | pick 652413bf663e 5 f
145 | pick 652413bf663e 5 f
146 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
146 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
147 Make changes as needed, you may commit or record as needed now.
147 Make changes as needed, you may commit or record as needed now.
148 When you are finished, run hg histedit --continue to resume.
148 When you are finished, run hg histedit --continue to resume.
149 $ continueediting true "(leaving commit message unaltered)"
149 $ continueediting true "(leaving commit message unaltered)"
150 % finalize changeset editing (leaving commit message unaltered)
150 % finalize changeset editing (leaving commit message unaltered)
151 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
151 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
152 Make changes as needed, you may commit or record as needed now.
152 Make changes as needed, you may commit or record as needed now.
153 When you are finished, run hg histedit --continue to resume.
153 When you are finished, run hg histedit --continue to resume.
154 $ graphlog "log after first edit"
154 $ graphlog "log after first edit"
155 % log after first edit
155 % log after first edit
156 @ 6 e5ae3ca2f1ffdbd89ec41ebc273a231f7c3022f2 "d"
156 @ 6 e5ae3ca2f1ffdbd89ec41ebc273a231f7c3022f2 "d"
157 |
157 |
158 | o 5 652413bf663ef2a641cab26574e46d5f5a64a55a "f"
158 | o 5 652413bf663ef2a641cab26574e46d5f5a64a55a "f"
159 | |
159 | |
160 | o 4 e860deea161a2f77de56603b340ebbb4536308ae "e"
160 | o 4 e860deea161a2f77de56603b340ebbb4536308ae "e"
161 | |
161 | |
162 | o 3 055a42cdd88768532f9cf79daa407fc8d138de9b "d"
162 | o 3 055a42cdd88768532f9cf79daa407fc8d138de9b "d"
163 |/
163 |/
164 o 2 177f92b773850b59254aa5e923436f921b55483b "c"
164 o 2 177f92b773850b59254aa5e923436f921b55483b "c"
165 |
165 |
166 o 1 d2ae7f538514cd87c17547b0de4cea71fe1af9fb "b"
166 o 1 d2ae7f538514cd87c17547b0de4cea71fe1af9fb "b"
167 |
167 |
168 o 0 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b "a"
168 o 0 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b "a"
169
169
170
170
171 abort editing session, after first forcibly updating away
171 abort editing session, after first forcibly updating away
172 $ hg up 0
172 $ hg up 0
173 abort: histedit in progress
173 abort: histedit in progress
174 (use 'hg histedit --continue' or 'hg histedit --abort')
174 (use 'hg histedit --continue' or 'hg histedit --abort')
175 [255]
175 [255]
176 $ mv .hg/histedit-state .hg/histedit-state-ignore
176 $ mv .hg/histedit-state .hg/histedit-state-ignore
177 $ hg up 0
177 $ hg up 0
178 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
178 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
179 $ mv .hg/histedit-state-ignore .hg/histedit-state
179 $ mv .hg/histedit-state-ignore .hg/histedit-state
180 $ hg sum
180 $ hg sum
181 parent: 0:cb9a9f314b8b
181 parent: 0:cb9a9f314b8b
182 a
182 a
183 branch: default
183 branch: default
184 commit: 1 modified, 1 unknown (new branch head)
184 commit: 1 added, 1 unknown (new branch head)
185 update: 6 new changesets (update)
185 update: 6 new changesets (update)
186 hist: 2 remaining (histedit --continue)
186 hist: 2 remaining (histedit --continue)
187
187
188 $ hg histedit --abort 2>&1 | fixbundle
188 $ hg histedit --abort 2>&1 | fixbundle
189 [1]
189 [1]
190
190
191 modified files should survive the abort when we've moved away already
191 modified files should survive the abort when we've moved away already
192 $ hg st
192 $ hg st
193 M e
193 A e
194 ? edit.sh
194 ? edit.sh
195
195
196 $ graphlog "log after abort"
196 $ graphlog "log after abort"
197 % log after abort
197 % log after abort
198 o 5 652413bf663ef2a641cab26574e46d5f5a64a55a "f"
198 o 5 652413bf663ef2a641cab26574e46d5f5a64a55a "f"
199 |
199 |
200 o 4 e860deea161a2f77de56603b340ebbb4536308ae "e"
200 o 4 e860deea161a2f77de56603b340ebbb4536308ae "e"
201 |
201 |
202 o 3 055a42cdd88768532f9cf79daa407fc8d138de9b "d"
202 o 3 055a42cdd88768532f9cf79daa407fc8d138de9b "d"
203 |
203 |
204 o 2 177f92b773850b59254aa5e923436f921b55483b "c"
204 o 2 177f92b773850b59254aa5e923436f921b55483b "c"
205 |
205 |
206 o 1 d2ae7f538514cd87c17547b0de4cea71fe1af9fb "b"
206 o 1 d2ae7f538514cd87c17547b0de4cea71fe1af9fb "b"
207 |
207 |
208 @ 0 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b "a"
208 @ 0 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b "a"
209
209
210
210
211 $ cd ..
211 $ cd ..
@@ -1,781 +1,781
1 $ cat >> $HGRCPATH <<EOF
1 $ cat >> $HGRCPATH <<EOF
2 > [extensions]
2 > [extensions]
3 > rebase=
3 > rebase=
4 > mq=
4 > mq=
5 >
5 >
6 > [phases]
6 > [phases]
7 > publish=False
7 > publish=False
8 >
8 >
9 > [alias]
9 > [alias]
10 > tglog = log -G --template "{rev}: '{desc}' {branches}\n"
10 > tglog = log -G --template "{rev}: '{desc}' {branches}\n"
11 > tglogp = log -G --template "{rev}:{phase} '{desc}' {branches}\n"
11 > tglogp = log -G --template "{rev}:{phase} '{desc}' {branches}\n"
12 > EOF
12 > EOF
13
13
14 Create repo a:
14 Create repo a:
15
15
16 $ hg init a
16 $ hg init a
17 $ cd a
17 $ cd a
18 $ hg unbundle "$TESTDIR/bundles/rebase.hg"
18 $ hg unbundle "$TESTDIR/bundles/rebase.hg"
19 adding changesets
19 adding changesets
20 adding manifests
20 adding manifests
21 adding file changes
21 adding file changes
22 added 8 changesets with 7 changes to 7 files (+2 heads)
22 added 8 changesets with 7 changes to 7 files (+2 heads)
23 (run 'hg heads' to see heads, 'hg merge' to merge)
23 (run 'hg heads' to see heads, 'hg merge' to merge)
24 $ hg up tip
24 $ hg up tip
25 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
25 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
26
26
27 $ hg tglog
27 $ hg tglog
28 @ 7: 'H'
28 @ 7: 'H'
29 |
29 |
30 | o 6: 'G'
30 | o 6: 'G'
31 |/|
31 |/|
32 o | 5: 'F'
32 o | 5: 'F'
33 | |
33 | |
34 | o 4: 'E'
34 | o 4: 'E'
35 |/
35 |/
36 | o 3: 'D'
36 | o 3: 'D'
37 | |
37 | |
38 | o 2: 'C'
38 | o 2: 'C'
39 | |
39 | |
40 | o 1: 'B'
40 | o 1: 'B'
41 |/
41 |/
42 o 0: 'A'
42 o 0: 'A'
43
43
44 $ cd ..
44 $ cd ..
45
45
46
46
47 Rebasing B onto H and collapsing changesets with different phases:
47 Rebasing B onto H and collapsing changesets with different phases:
48
48
49
49
50 $ hg clone -q -u 3 a a1
50 $ hg clone -q -u 3 a a1
51 $ cd a1
51 $ cd a1
52
52
53 $ hg phase --force --secret 3
53 $ hg phase --force --secret 3
54
54
55 $ cat > $TESTTMP/editor.sh <<EOF
55 $ cat > $TESTTMP/editor.sh <<EOF
56 > echo "==== before editing"
56 > echo "==== before editing"
57 > cat \$1
57 > cat \$1
58 > echo "===="
58 > echo "===="
59 > echo "edited manually" >> \$1
59 > echo "edited manually" >> \$1
60 > EOF
60 > EOF
61 $ HGEDITOR="sh $TESTTMP/editor.sh" hg rebase --collapse --keepbranches -e
61 $ HGEDITOR="sh $TESTTMP/editor.sh" hg rebase --collapse --keepbranches -e
62 ==== before editing
62 ==== before editing
63 Collapsed revision
63 Collapsed revision
64 * B
64 * B
65 * C
65 * C
66 * D
66 * D
67
67
68
68
69 HG: Enter commit message. Lines beginning with 'HG:' are removed.
69 HG: Enter commit message. Lines beginning with 'HG:' are removed.
70 HG: Leave message empty to abort commit.
70 HG: Leave message empty to abort commit.
71 HG: --
71 HG: --
72 HG: user: Nicolas Dumazet <nicdumz.commits@gmail.com>
72 HG: user: Nicolas Dumazet <nicdumz.commits@gmail.com>
73 HG: branch 'default'
73 HG: branch 'default'
74 HG: changed B
74 HG: added B
75 HG: changed C
75 HG: added C
76 HG: changed D
76 HG: added D
77 ====
77 ====
78 saved backup bundle to $TESTTMP/a1/.hg/strip-backup/*-backup.hg (glob)
78 saved backup bundle to $TESTTMP/a1/.hg/strip-backup/*-backup.hg (glob)
79
79
80 $ hg tglogp
80 $ hg tglogp
81 @ 5:secret 'Collapsed revision
81 @ 5:secret 'Collapsed revision
82 | * B
82 | * B
83 | * C
83 | * C
84 | * D
84 | * D
85 |
85 |
86 |
86 |
87 | edited manually'
87 | edited manually'
88 o 4:draft 'H'
88 o 4:draft 'H'
89 |
89 |
90 | o 3:draft 'G'
90 | o 3:draft 'G'
91 |/|
91 |/|
92 o | 2:draft 'F'
92 o | 2:draft 'F'
93 | |
93 | |
94 | o 1:draft 'E'
94 | o 1:draft 'E'
95 |/
95 |/
96 o 0:draft 'A'
96 o 0:draft 'A'
97
97
98 $ hg manifest --rev tip
98 $ hg manifest --rev tip
99 A
99 A
100 B
100 B
101 C
101 C
102 D
102 D
103 F
103 F
104 H
104 H
105
105
106 $ cd ..
106 $ cd ..
107
107
108
108
109 Rebasing E onto H:
109 Rebasing E onto H:
110
110
111 $ hg clone -q -u . a a2
111 $ hg clone -q -u . a a2
112 $ cd a2
112 $ cd a2
113
113
114 $ hg phase --force --secret 6
114 $ hg phase --force --secret 6
115 $ hg rebase --source 4 --collapse
115 $ hg rebase --source 4 --collapse
116 saved backup bundle to $TESTTMP/a2/.hg/strip-backup/*-backup.hg (glob)
116 saved backup bundle to $TESTTMP/a2/.hg/strip-backup/*-backup.hg (glob)
117
117
118 $ hg tglog
118 $ hg tglog
119 o 6: 'Collapsed revision
119 o 6: 'Collapsed revision
120 | * E
120 | * E
121 | * G'
121 | * G'
122 @ 5: 'H'
122 @ 5: 'H'
123 |
123 |
124 o 4: 'F'
124 o 4: 'F'
125 |
125 |
126 | o 3: 'D'
126 | o 3: 'D'
127 | |
127 | |
128 | o 2: 'C'
128 | o 2: 'C'
129 | |
129 | |
130 | o 1: 'B'
130 | o 1: 'B'
131 |/
131 |/
132 o 0: 'A'
132 o 0: 'A'
133
133
134 $ hg manifest --rev tip
134 $ hg manifest --rev tip
135 A
135 A
136 E
136 E
137 F
137 F
138 H
138 H
139
139
140 $ cd ..
140 $ cd ..
141
141
142 Rebasing G onto H with custom message:
142 Rebasing G onto H with custom message:
143
143
144 $ hg clone -q -u . a a3
144 $ hg clone -q -u . a a3
145 $ cd a3
145 $ cd a3
146
146
147 $ hg rebase --base 6 -m 'custom message'
147 $ hg rebase --base 6 -m 'custom message'
148 abort: message can only be specified with collapse
148 abort: message can only be specified with collapse
149 [255]
149 [255]
150
150
151 $ cat > $TESTTMP/checkeditform.sh <<EOF
151 $ cat > $TESTTMP/checkeditform.sh <<EOF
152 > env | grep HGEDITFORM
152 > env | grep HGEDITFORM
153 > true
153 > true
154 > EOF
154 > EOF
155 $ HGEDITOR="sh $TESTTMP/checkeditform.sh" hg rebase --source 4 --collapse -m 'custom message' -e
155 $ HGEDITOR="sh $TESTTMP/checkeditform.sh" hg rebase --source 4 --collapse -m 'custom message' -e
156 HGEDITFORM=rebase.collapse
156 HGEDITFORM=rebase.collapse
157 saved backup bundle to $TESTTMP/a3/.hg/strip-backup/*-backup.hg (glob)
157 saved backup bundle to $TESTTMP/a3/.hg/strip-backup/*-backup.hg (glob)
158
158
159 $ hg tglog
159 $ hg tglog
160 o 6: 'custom message'
160 o 6: 'custom message'
161 |
161 |
162 @ 5: 'H'
162 @ 5: 'H'
163 |
163 |
164 o 4: 'F'
164 o 4: 'F'
165 |
165 |
166 | o 3: 'D'
166 | o 3: 'D'
167 | |
167 | |
168 | o 2: 'C'
168 | o 2: 'C'
169 | |
169 | |
170 | o 1: 'B'
170 | o 1: 'B'
171 |/
171 |/
172 o 0: 'A'
172 o 0: 'A'
173
173
174 $ hg manifest --rev tip
174 $ hg manifest --rev tip
175 A
175 A
176 E
176 E
177 F
177 F
178 H
178 H
179
179
180 $ cd ..
180 $ cd ..
181
181
182 Create repo b:
182 Create repo b:
183
183
184 $ hg init b
184 $ hg init b
185 $ cd b
185 $ cd b
186
186
187 $ echo A > A
187 $ echo A > A
188 $ hg ci -Am A
188 $ hg ci -Am A
189 adding A
189 adding A
190 $ echo B > B
190 $ echo B > B
191 $ hg ci -Am B
191 $ hg ci -Am B
192 adding B
192 adding B
193
193
194 $ hg up -q 0
194 $ hg up -q 0
195
195
196 $ echo C > C
196 $ echo C > C
197 $ hg ci -Am C
197 $ hg ci -Am C
198 adding C
198 adding C
199 created new head
199 created new head
200
200
201 $ hg merge
201 $ hg merge
202 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
202 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
203 (branch merge, don't forget to commit)
203 (branch merge, don't forget to commit)
204
204
205 $ echo D > D
205 $ echo D > D
206 $ hg ci -Am D
206 $ hg ci -Am D
207 adding D
207 adding D
208
208
209 $ hg up -q 1
209 $ hg up -q 1
210
210
211 $ echo E > E
211 $ echo E > E
212 $ hg ci -Am E
212 $ hg ci -Am E
213 adding E
213 adding E
214 created new head
214 created new head
215
215
216 $ echo F > F
216 $ echo F > F
217 $ hg ci -Am F
217 $ hg ci -Am F
218 adding F
218 adding F
219
219
220 $ hg merge
220 $ hg merge
221 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
221 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
222 (branch merge, don't forget to commit)
222 (branch merge, don't forget to commit)
223 $ hg ci -m G
223 $ hg ci -m G
224
224
225 $ hg up -q 0
225 $ hg up -q 0
226
226
227 $ echo H > H
227 $ echo H > H
228 $ hg ci -Am H
228 $ hg ci -Am H
229 adding H
229 adding H
230 created new head
230 created new head
231
231
232 $ hg tglog
232 $ hg tglog
233 @ 7: 'H'
233 @ 7: 'H'
234 |
234 |
235 | o 6: 'G'
235 | o 6: 'G'
236 | |\
236 | |\
237 | | o 5: 'F'
237 | | o 5: 'F'
238 | | |
238 | | |
239 | | o 4: 'E'
239 | | o 4: 'E'
240 | | |
240 | | |
241 | o | 3: 'D'
241 | o | 3: 'D'
242 | |\|
242 | |\|
243 | o | 2: 'C'
243 | o | 2: 'C'
244 |/ /
244 |/ /
245 | o 1: 'B'
245 | o 1: 'B'
246 |/
246 |/
247 o 0: 'A'
247 o 0: 'A'
248
248
249 $ cd ..
249 $ cd ..
250
250
251
251
252 Rebase and collapse - more than one external (fail):
252 Rebase and collapse - more than one external (fail):
253
253
254 $ hg clone -q -u . b b1
254 $ hg clone -q -u . b b1
255 $ cd b1
255 $ cd b1
256
256
257 $ hg rebase -s 2 --collapse
257 $ hg rebase -s 2 --collapse
258 abort: unable to collapse on top of 7, there is more than one external parent: 1, 5
258 abort: unable to collapse on top of 7, there is more than one external parent: 1, 5
259 [255]
259 [255]
260
260
261 Rebase and collapse - E onto H:
261 Rebase and collapse - E onto H:
262
262
263 $ hg rebase -s 4 --collapse # root (4) is not a merge
263 $ hg rebase -s 4 --collapse # root (4) is not a merge
264 saved backup bundle to $TESTTMP/b1/.hg/strip-backup/*-backup.hg (glob)
264 saved backup bundle to $TESTTMP/b1/.hg/strip-backup/*-backup.hg (glob)
265
265
266 $ hg tglog
266 $ hg tglog
267 o 5: 'Collapsed revision
267 o 5: 'Collapsed revision
268 |\ * E
268 |\ * E
269 | | * F
269 | | * F
270 | | * G'
270 | | * G'
271 | @ 4: 'H'
271 | @ 4: 'H'
272 | |
272 | |
273 o | 3: 'D'
273 o | 3: 'D'
274 |\ \
274 |\ \
275 | o | 2: 'C'
275 | o | 2: 'C'
276 | |/
276 | |/
277 o / 1: 'B'
277 o / 1: 'B'
278 |/
278 |/
279 o 0: 'A'
279 o 0: 'A'
280
280
281 $ hg manifest --rev tip
281 $ hg manifest --rev tip
282 A
282 A
283 C
283 C
284 D
284 D
285 E
285 E
286 F
286 F
287 H
287 H
288
288
289 $ cd ..
289 $ cd ..
290
290
291
291
292
292
293
293
294 Test that branchheads cache is updated correctly when doing a strip in which
294 Test that branchheads cache is updated correctly when doing a strip in which
295 the parent of the ancestor node to be stripped does not become a head and also,
295 the parent of the ancestor node to be stripped does not become a head and also,
296 the parent of a node that is a child of the node stripped becomes a head (node
296 the parent of a node that is a child of the node stripped becomes a head (node
297 3). The code is now much simpler and we could just test a simpler scenario
297 3). The code is now much simpler and we could just test a simpler scenario
298 We keep it the test this way in case new complexity is injected.
298 We keep it the test this way in case new complexity is injected.
299
299
300 $ hg clone -q -u . b b2
300 $ hg clone -q -u . b b2
301 $ cd b2
301 $ cd b2
302
302
303 $ hg heads --template="{rev}:{node} {branch}\n"
303 $ hg heads --template="{rev}:{node} {branch}\n"
304 7:c65502d4178782309ce0574c5ae6ee9485a9bafa default
304 7:c65502d4178782309ce0574c5ae6ee9485a9bafa default
305 6:c772a8b2dc17629cec88a19d09c926c4814b12c7 default
305 6:c772a8b2dc17629cec88a19d09c926c4814b12c7 default
306
306
307 $ cat $TESTTMP/b2/.hg/cache/branch2-served
307 $ cat $TESTTMP/b2/.hg/cache/branch2-served
308 c65502d4178782309ce0574c5ae6ee9485a9bafa 7
308 c65502d4178782309ce0574c5ae6ee9485a9bafa 7
309 c772a8b2dc17629cec88a19d09c926c4814b12c7 o default
309 c772a8b2dc17629cec88a19d09c926c4814b12c7 o default
310 c65502d4178782309ce0574c5ae6ee9485a9bafa o default
310 c65502d4178782309ce0574c5ae6ee9485a9bafa o default
311
311
312 $ hg strip 4
312 $ hg strip 4
313 saved backup bundle to $TESTTMP/b2/.hg/strip-backup/8a5212ebc852-backup.hg (glob)
313 saved backup bundle to $TESTTMP/b2/.hg/strip-backup/8a5212ebc852-backup.hg (glob)
314
314
315 $ cat $TESTTMP/b2/.hg/cache/branch2-served
315 $ cat $TESTTMP/b2/.hg/cache/branch2-served
316 c65502d4178782309ce0574c5ae6ee9485a9bafa 4
316 c65502d4178782309ce0574c5ae6ee9485a9bafa 4
317 2870ad076e541e714f3c2bc32826b5c6a6e5b040 o default
317 2870ad076e541e714f3c2bc32826b5c6a6e5b040 o default
318 c65502d4178782309ce0574c5ae6ee9485a9bafa o default
318 c65502d4178782309ce0574c5ae6ee9485a9bafa o default
319
319
320 $ hg heads --template="{rev}:{node} {branch}\n"
320 $ hg heads --template="{rev}:{node} {branch}\n"
321 4:c65502d4178782309ce0574c5ae6ee9485a9bafa default
321 4:c65502d4178782309ce0574c5ae6ee9485a9bafa default
322 3:2870ad076e541e714f3c2bc32826b5c6a6e5b040 default
322 3:2870ad076e541e714f3c2bc32826b5c6a6e5b040 default
323
323
324 $ cd ..
324 $ cd ..
325
325
326
326
327
327
328
328
329
329
330
330
331 Create repo c:
331 Create repo c:
332
332
333 $ hg init c
333 $ hg init c
334 $ cd c
334 $ cd c
335
335
336 $ echo A > A
336 $ echo A > A
337 $ hg ci -Am A
337 $ hg ci -Am A
338 adding A
338 adding A
339 $ echo B > B
339 $ echo B > B
340 $ hg ci -Am B
340 $ hg ci -Am B
341 adding B
341 adding B
342
342
343 $ hg up -q 0
343 $ hg up -q 0
344
344
345 $ echo C > C
345 $ echo C > C
346 $ hg ci -Am C
346 $ hg ci -Am C
347 adding C
347 adding C
348 created new head
348 created new head
349
349
350 $ hg merge
350 $ hg merge
351 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
351 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
352 (branch merge, don't forget to commit)
352 (branch merge, don't forget to commit)
353
353
354 $ echo D > D
354 $ echo D > D
355 $ hg ci -Am D
355 $ hg ci -Am D
356 adding D
356 adding D
357
357
358 $ hg up -q 1
358 $ hg up -q 1
359
359
360 $ echo E > E
360 $ echo E > E
361 $ hg ci -Am E
361 $ hg ci -Am E
362 adding E
362 adding E
363 created new head
363 created new head
364 $ echo F > E
364 $ echo F > E
365 $ hg ci -m 'F'
365 $ hg ci -m 'F'
366
366
367 $ echo G > G
367 $ echo G > G
368 $ hg ci -Am G
368 $ hg ci -Am G
369 adding G
369 adding G
370
370
371 $ hg merge
371 $ hg merge
372 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
372 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
373 (branch merge, don't forget to commit)
373 (branch merge, don't forget to commit)
374
374
375 $ hg ci -m H
375 $ hg ci -m H
376
376
377 $ hg up -q 0
377 $ hg up -q 0
378
378
379 $ echo I > I
379 $ echo I > I
380 $ hg ci -Am I
380 $ hg ci -Am I
381 adding I
381 adding I
382 created new head
382 created new head
383
383
384 $ hg tglog
384 $ hg tglog
385 @ 8: 'I'
385 @ 8: 'I'
386 |
386 |
387 | o 7: 'H'
387 | o 7: 'H'
388 | |\
388 | |\
389 | | o 6: 'G'
389 | | o 6: 'G'
390 | | |
390 | | |
391 | | o 5: 'F'
391 | | o 5: 'F'
392 | | |
392 | | |
393 | | o 4: 'E'
393 | | o 4: 'E'
394 | | |
394 | | |
395 | o | 3: 'D'
395 | o | 3: 'D'
396 | |\|
396 | |\|
397 | o | 2: 'C'
397 | o | 2: 'C'
398 |/ /
398 |/ /
399 | o 1: 'B'
399 | o 1: 'B'
400 |/
400 |/
401 o 0: 'A'
401 o 0: 'A'
402
402
403 $ cd ..
403 $ cd ..
404
404
405
405
406 Rebase and collapse - E onto I:
406 Rebase and collapse - E onto I:
407
407
408 $ hg clone -q -u . c c1
408 $ hg clone -q -u . c c1
409 $ cd c1
409 $ cd c1
410
410
411 $ hg rebase -s 4 --collapse # root (4) is not a merge
411 $ hg rebase -s 4 --collapse # root (4) is not a merge
412 merging E
412 merging E
413 saved backup bundle to $TESTTMP/c1/.hg/strip-backup/*-backup.hg (glob)
413 saved backup bundle to $TESTTMP/c1/.hg/strip-backup/*-backup.hg (glob)
414
414
415 $ hg tglog
415 $ hg tglog
416 o 5: 'Collapsed revision
416 o 5: 'Collapsed revision
417 |\ * E
417 |\ * E
418 | | * F
418 | | * F
419 | | * G
419 | | * G
420 | | * H'
420 | | * H'
421 | @ 4: 'I'
421 | @ 4: 'I'
422 | |
422 | |
423 o | 3: 'D'
423 o | 3: 'D'
424 |\ \
424 |\ \
425 | o | 2: 'C'
425 | o | 2: 'C'
426 | |/
426 | |/
427 o / 1: 'B'
427 o / 1: 'B'
428 |/
428 |/
429 o 0: 'A'
429 o 0: 'A'
430
430
431 $ hg manifest --rev tip
431 $ hg manifest --rev tip
432 A
432 A
433 C
433 C
434 D
434 D
435 E
435 E
436 G
436 G
437 I
437 I
438
438
439 $ hg up tip -q
439 $ hg up tip -q
440 $ cat E
440 $ cat E
441 F
441 F
442
442
443 $ cd ..
443 $ cd ..
444
444
445
445
446 Create repo d:
446 Create repo d:
447
447
448 $ hg init d
448 $ hg init d
449 $ cd d
449 $ cd d
450
450
451 $ echo A > A
451 $ echo A > A
452 $ hg ci -Am A
452 $ hg ci -Am A
453 adding A
453 adding A
454 $ echo B > B
454 $ echo B > B
455 $ hg ci -Am B
455 $ hg ci -Am B
456 adding B
456 adding B
457 $ echo C > C
457 $ echo C > C
458 $ hg ci -Am C
458 $ hg ci -Am C
459 adding C
459 adding C
460
460
461 $ hg up -q 1
461 $ hg up -q 1
462
462
463 $ echo D > D
463 $ echo D > D
464 $ hg ci -Am D
464 $ hg ci -Am D
465 adding D
465 adding D
466 created new head
466 created new head
467 $ hg merge
467 $ hg merge
468 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
468 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
469 (branch merge, don't forget to commit)
469 (branch merge, don't forget to commit)
470
470
471 $ hg ci -m E
471 $ hg ci -m E
472
472
473 $ hg up -q 0
473 $ hg up -q 0
474
474
475 $ echo F > F
475 $ echo F > F
476 $ hg ci -Am F
476 $ hg ci -Am F
477 adding F
477 adding F
478 created new head
478 created new head
479
479
480 $ hg tglog
480 $ hg tglog
481 @ 5: 'F'
481 @ 5: 'F'
482 |
482 |
483 | o 4: 'E'
483 | o 4: 'E'
484 | |\
484 | |\
485 | | o 3: 'D'
485 | | o 3: 'D'
486 | | |
486 | | |
487 | o | 2: 'C'
487 | o | 2: 'C'
488 | |/
488 | |/
489 | o 1: 'B'
489 | o 1: 'B'
490 |/
490 |/
491 o 0: 'A'
491 o 0: 'A'
492
492
493 $ cd ..
493 $ cd ..
494
494
495
495
496 Rebase and collapse - B onto F:
496 Rebase and collapse - B onto F:
497
497
498 $ hg clone -q -u . d d1
498 $ hg clone -q -u . d d1
499 $ cd d1
499 $ cd d1
500
500
501 $ hg rebase -s 1 --collapse
501 $ hg rebase -s 1 --collapse
502 saved backup bundle to $TESTTMP/d1/.hg/strip-backup/*-backup.hg (glob)
502 saved backup bundle to $TESTTMP/d1/.hg/strip-backup/*-backup.hg (glob)
503
503
504 $ hg tglog
504 $ hg tglog
505 o 2: 'Collapsed revision
505 o 2: 'Collapsed revision
506 | * B
506 | * B
507 | * C
507 | * C
508 | * D
508 | * D
509 | * E'
509 | * E'
510 @ 1: 'F'
510 @ 1: 'F'
511 |
511 |
512 o 0: 'A'
512 o 0: 'A'
513
513
514 $ hg manifest --rev tip
514 $ hg manifest --rev tip
515 A
515 A
516 B
516 B
517 C
517 C
518 D
518 D
519 F
519 F
520
520
521 Interactions between collapse and keepbranches
521 Interactions between collapse and keepbranches
522 $ cd ..
522 $ cd ..
523 $ hg init e
523 $ hg init e
524 $ cd e
524 $ cd e
525 $ echo 'a' > a
525 $ echo 'a' > a
526 $ hg ci -Am 'A'
526 $ hg ci -Am 'A'
527 adding a
527 adding a
528
528
529 $ hg branch 'one'
529 $ hg branch 'one'
530 marked working directory as branch one
530 marked working directory as branch one
531 (branches are permanent and global, did you want a bookmark?)
531 (branches are permanent and global, did you want a bookmark?)
532 $ echo 'b' > b
532 $ echo 'b' > b
533 $ hg ci -Am 'B'
533 $ hg ci -Am 'B'
534 adding b
534 adding b
535
535
536 $ hg branch 'two'
536 $ hg branch 'two'
537 marked working directory as branch two
537 marked working directory as branch two
538 (branches are permanent and global, did you want a bookmark?)
538 (branches are permanent and global, did you want a bookmark?)
539 $ echo 'c' > c
539 $ echo 'c' > c
540 $ hg ci -Am 'C'
540 $ hg ci -Am 'C'
541 adding c
541 adding c
542
542
543 $ hg up -q 0
543 $ hg up -q 0
544 $ echo 'd' > d
544 $ echo 'd' > d
545 $ hg ci -Am 'D'
545 $ hg ci -Am 'D'
546 adding d
546 adding d
547
547
548 $ hg tglog
548 $ hg tglog
549 @ 3: 'D'
549 @ 3: 'D'
550 |
550 |
551 | o 2: 'C' two
551 | o 2: 'C' two
552 | |
552 | |
553 | o 1: 'B' one
553 | o 1: 'B' one
554 |/
554 |/
555 o 0: 'A'
555 o 0: 'A'
556
556
557 $ hg rebase --keepbranches --collapse -s 1 -d 3
557 $ hg rebase --keepbranches --collapse -s 1 -d 3
558 abort: cannot collapse multiple named branches
558 abort: cannot collapse multiple named branches
559 [255]
559 [255]
560
560
561 $ repeatchange() {
561 $ repeatchange() {
562 > hg checkout $1
562 > hg checkout $1
563 > hg cp d z
563 > hg cp d z
564 > echo blah >> z
564 > echo blah >> z
565 > hg commit -Am "$2" --user "$3"
565 > hg commit -Am "$2" --user "$3"
566 > }
566 > }
567 $ repeatchange 3 "E" "user1"
567 $ repeatchange 3 "E" "user1"
568 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
568 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
569 $ repeatchange 3 "E" "user2"
569 $ repeatchange 3 "E" "user2"
570 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
570 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
571 created new head
571 created new head
572 $ hg tglog
572 $ hg tglog
573 @ 5: 'E'
573 @ 5: 'E'
574 |
574 |
575 | o 4: 'E'
575 | o 4: 'E'
576 |/
576 |/
577 o 3: 'D'
577 o 3: 'D'
578 |
578 |
579 | o 2: 'C' two
579 | o 2: 'C' two
580 | |
580 | |
581 | o 1: 'B' one
581 | o 1: 'B' one
582 |/
582 |/
583 o 0: 'A'
583 o 0: 'A'
584
584
585 $ hg rebase -s 5 -d 4
585 $ hg rebase -s 5 -d 4
586 saved backup bundle to $TESTTMP/e/.hg/strip-backup/*-backup.hg (glob)
586 saved backup bundle to $TESTTMP/e/.hg/strip-backup/*-backup.hg (glob)
587 $ hg tglog
587 $ hg tglog
588 @ 4: 'E'
588 @ 4: 'E'
589 |
589 |
590 o 3: 'D'
590 o 3: 'D'
591 |
591 |
592 | o 2: 'C' two
592 | o 2: 'C' two
593 | |
593 | |
594 | o 1: 'B' one
594 | o 1: 'B' one
595 |/
595 |/
596 o 0: 'A'
596 o 0: 'A'
597
597
598 $ hg export tip
598 $ hg export tip
599 # HG changeset patch
599 # HG changeset patch
600 # User user1
600 # User user1
601 # Date 0 0
601 # Date 0 0
602 # Thu Jan 01 00:00:00 1970 +0000
602 # Thu Jan 01 00:00:00 1970 +0000
603 # Node ID f338eb3c2c7cc5b5915676a2376ba7ac558c5213
603 # Node ID f338eb3c2c7cc5b5915676a2376ba7ac558c5213
604 # Parent 41acb9dca9eb976e84cd21fcb756b4afa5a35c09
604 # Parent 41acb9dca9eb976e84cd21fcb756b4afa5a35c09
605 E
605 E
606
606
607 diff -r 41acb9dca9eb -r f338eb3c2c7c z
607 diff -r 41acb9dca9eb -r f338eb3c2c7c z
608 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
608 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
609 +++ b/z Thu Jan 01 00:00:00 1970 +0000
609 +++ b/z Thu Jan 01 00:00:00 1970 +0000
610 @@ -0,0 +1,2 @@
610 @@ -0,0 +1,2 @@
611 +d
611 +d
612 +blah
612 +blah
613
613
614 $ cd ..
614 $ cd ..
615
615
616 Rebase, collapse and copies
616 Rebase, collapse and copies
617
617
618 $ hg init copies
618 $ hg init copies
619 $ cd copies
619 $ cd copies
620 $ hg unbundle "$TESTDIR/bundles/renames.hg"
620 $ hg unbundle "$TESTDIR/bundles/renames.hg"
621 adding changesets
621 adding changesets
622 adding manifests
622 adding manifests
623 adding file changes
623 adding file changes
624 added 4 changesets with 11 changes to 7 files (+1 heads)
624 added 4 changesets with 11 changes to 7 files (+1 heads)
625 (run 'hg heads' to see heads, 'hg merge' to merge)
625 (run 'hg heads' to see heads, 'hg merge' to merge)
626 $ hg up -q tip
626 $ hg up -q tip
627 $ hg tglog
627 $ hg tglog
628 @ 3: 'move2'
628 @ 3: 'move2'
629 |
629 |
630 o 2: 'move1'
630 o 2: 'move1'
631 |
631 |
632 | o 1: 'change'
632 | o 1: 'change'
633 |/
633 |/
634 o 0: 'add'
634 o 0: 'add'
635
635
636 $ hg rebase --collapse -d 1
636 $ hg rebase --collapse -d 1
637 merging a and d to d
637 merging a and d to d
638 merging b and e to e
638 merging b and e to e
639 merging c and f to f
639 merging c and f to f
640 merging f and c to c
640 merging f and c to c
641 merging e and g to g
641 merging e and g to g
642 saved backup bundle to $TESTTMP/copies/.hg/strip-backup/*-backup.hg (glob)
642 saved backup bundle to $TESTTMP/copies/.hg/strip-backup/*-backup.hg (glob)
643 $ hg st
643 $ hg st
644 $ hg st --copies --change tip
644 $ hg st --copies --change tip
645 A d
645 A d
646 a
646 a
647 A g
647 A g
648 b
648 b
649 R b
649 R b
650 $ hg up tip -q
650 $ hg up tip -q
651 $ cat c
651 $ cat c
652 c
652 c
653 c
653 c
654 $ cat d
654 $ cat d
655 a
655 a
656 a
656 a
657 $ cat g
657 $ cat g
658 b
658 b
659 b
659 b
660 $ hg log -r . --template "{file_copies}\n"
660 $ hg log -r . --template "{file_copies}\n"
661 d (a)g (b)
661 d (a)g (b)
662
662
663 Test collapsing a middle revision in-place
663 Test collapsing a middle revision in-place
664
664
665 $ hg tglog
665 $ hg tglog
666 @ 2: 'Collapsed revision
666 @ 2: 'Collapsed revision
667 | * move1
667 | * move1
668 | * move2'
668 | * move2'
669 o 1: 'change'
669 o 1: 'change'
670 |
670 |
671 o 0: 'add'
671 o 0: 'add'
672
672
673 $ hg rebase --collapse -r 1 -d 0
673 $ hg rebase --collapse -r 1 -d 0
674 abort: can't remove original changesets with unrebased descendants
674 abort: can't remove original changesets with unrebased descendants
675 (use --keep to keep original changesets)
675 (use --keep to keep original changesets)
676 [255]
676 [255]
677
677
678 Test collapsing in place
678 Test collapsing in place
679
679
680 $ hg rebase --collapse -b . -d 0
680 $ hg rebase --collapse -b . -d 0
681 saved backup bundle to $TESTTMP/copies/.hg/strip-backup/*-backup.hg (glob)
681 saved backup bundle to $TESTTMP/copies/.hg/strip-backup/*-backup.hg (glob)
682 $ hg st --change tip --copies
682 $ hg st --change tip --copies
683 M a
683 M a
684 M c
684 M c
685 A d
685 A d
686 a
686 a
687 A g
687 A g
688 b
688 b
689 R b
689 R b
690 $ hg up tip -q
690 $ hg up tip -q
691 $ cat a
691 $ cat a
692 a
692 a
693 a
693 a
694 $ cat c
694 $ cat c
695 c
695 c
696 c
696 c
697 $ cat d
697 $ cat d
698 a
698 a
699 a
699 a
700 $ cat g
700 $ cat g
701 b
701 b
702 b
702 b
703 $ cd ..
703 $ cd ..
704
704
705
705
706 Test stripping a revision with another child
706 Test stripping a revision with another child
707
707
708 $ hg init f
708 $ hg init f
709 $ cd f
709 $ cd f
710
710
711 $ echo A > A
711 $ echo A > A
712 $ hg ci -Am A
712 $ hg ci -Am A
713 adding A
713 adding A
714 $ echo B > B
714 $ echo B > B
715 $ hg ci -Am B
715 $ hg ci -Am B
716 adding B
716 adding B
717
717
718 $ hg up -q 0
718 $ hg up -q 0
719
719
720 $ echo C > C
720 $ echo C > C
721 $ hg ci -Am C
721 $ hg ci -Am C
722 adding C
722 adding C
723 created new head
723 created new head
724
724
725 $ hg tglog
725 $ hg tglog
726 @ 2: 'C'
726 @ 2: 'C'
727 |
727 |
728 | o 1: 'B'
728 | o 1: 'B'
729 |/
729 |/
730 o 0: 'A'
730 o 0: 'A'
731
731
732
732
733
733
734 $ hg heads --template="{rev}:{node} {branch}: {desc}\n"
734 $ hg heads --template="{rev}:{node} {branch}: {desc}\n"
735 2:c5cefa58fd557f84b72b87f970135984337acbc5 default: C
735 2:c5cefa58fd557f84b72b87f970135984337acbc5 default: C
736 1:27547f69f25460a52fff66ad004e58da7ad3fb56 default: B
736 1:27547f69f25460a52fff66ad004e58da7ad3fb56 default: B
737
737
738 $ hg strip 2
738 $ hg strip 2
739 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
739 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
740 saved backup bundle to $TESTTMP/f/.hg/strip-backup/*-backup.hg (glob)
740 saved backup bundle to $TESTTMP/f/.hg/strip-backup/*-backup.hg (glob)
741
741
742 $ hg tglog
742 $ hg tglog
743 o 1: 'B'
743 o 1: 'B'
744 |
744 |
745 @ 0: 'A'
745 @ 0: 'A'
746
746
747
747
748
748
749 $ hg heads --template="{rev}:{node} {branch}: {desc}\n"
749 $ hg heads --template="{rev}:{node} {branch}: {desc}\n"
750 1:27547f69f25460a52fff66ad004e58da7ad3fb56 default: B
750 1:27547f69f25460a52fff66ad004e58da7ad3fb56 default: B
751
751
752 $ cd ..
752 $ cd ..
753
753
754 Test collapsing changes that add then remove a file
754 Test collapsing changes that add then remove a file
755
755
756 $ hg init collapseaddremove
756 $ hg init collapseaddremove
757 $ cd collapseaddremove
757 $ cd collapseaddremove
758
758
759 $ touch base
759 $ touch base
760 $ hg commit -Am base
760 $ hg commit -Am base
761 adding base
761 adding base
762 $ touch a
762 $ touch a
763 $ hg commit -Am a
763 $ hg commit -Am a
764 adding a
764 adding a
765 $ hg rm a
765 $ hg rm a
766 $ touch b
766 $ touch b
767 $ hg commit -Am b
767 $ hg commit -Am b
768 adding b
768 adding b
769 $ hg book foo
769 $ hg book foo
770 $ hg rebase -d 0 -r "1::2" --collapse -m collapsed
770 $ hg rebase -d 0 -r "1::2" --collapse -m collapsed
771 saved backup bundle to $TESTTMP/collapseaddremove/.hg/strip-backup/*-backup.hg (glob)
771 saved backup bundle to $TESTTMP/collapseaddremove/.hg/strip-backup/*-backup.hg (glob)
772 $ hg log -G --template "{rev}: '{desc}' {bookmarks}"
772 $ hg log -G --template "{rev}: '{desc}' {bookmarks}"
773 @ 1: 'collapsed' foo
773 @ 1: 'collapsed' foo
774 |
774 |
775 o 0: 'base'
775 o 0: 'base'
776
776
777 $ hg manifest --rev tip
777 $ hg manifest --rev tip
778 b
778 b
779 base
779 base
780
780
781 $ cd ..
781 $ cd ..
@@ -1,689 +1,689
1 $ cat >> $HGRCPATH <<EOF
1 $ cat >> $HGRCPATH <<EOF
2 > [extensions]
2 > [extensions]
3 > rebase=
3 > rebase=
4 >
4 >
5 > [phases]
5 > [phases]
6 > publish=False
6 > publish=False
7 >
7 >
8 > [alias]
8 > [alias]
9 > tglog = log -G --template "{rev}: '{desc}' {branches}\n"
9 > tglog = log -G --template "{rev}: '{desc}' {branches}\n"
10 > EOF
10 > EOF
11
11
12
12
13 $ hg init a
13 $ hg init a
14 $ cd a
14 $ cd a
15 $ hg unbundle "$TESTDIR/bundles/rebase.hg"
15 $ hg unbundle "$TESTDIR/bundles/rebase.hg"
16 adding changesets
16 adding changesets
17 adding manifests
17 adding manifests
18 adding file changes
18 adding file changes
19 added 8 changesets with 7 changes to 7 files (+2 heads)
19 added 8 changesets with 7 changes to 7 files (+2 heads)
20 (run 'hg heads' to see heads, 'hg merge' to merge)
20 (run 'hg heads' to see heads, 'hg merge' to merge)
21 $ hg up tip
21 $ hg up tip
22 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
22 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
23 $ cd ..
23 $ cd ..
24
24
25
25
26 Rebasing
26 Rebasing
27 D onto H - simple rebase:
27 D onto H - simple rebase:
28 (this also tests that editor is invoked if '--edit' is specified)
28 (this also tests that editor is invoked if '--edit' is specified)
29
29
30 $ hg clone -q -u . a a1
30 $ hg clone -q -u . a a1
31 $ cd a1
31 $ cd a1
32
32
33 $ hg tglog
33 $ hg tglog
34 @ 7: 'H'
34 @ 7: 'H'
35 |
35 |
36 | o 6: 'G'
36 | o 6: 'G'
37 |/|
37 |/|
38 o | 5: 'F'
38 o | 5: 'F'
39 | |
39 | |
40 | o 4: 'E'
40 | o 4: 'E'
41 |/
41 |/
42 | o 3: 'D'
42 | o 3: 'D'
43 | |
43 | |
44 | o 2: 'C'
44 | o 2: 'C'
45 | |
45 | |
46 | o 1: 'B'
46 | o 1: 'B'
47 |/
47 |/
48 o 0: 'A'
48 o 0: 'A'
49
49
50
50
51 $ hg status --rev "3^1" --rev 3
51 $ hg status --rev "3^1" --rev 3
52 A D
52 A D
53 $ HGEDITOR=cat hg rebase -s 3 -d 7 --edit
53 $ HGEDITOR=cat hg rebase -s 3 -d 7 --edit
54 D
54 D
55
55
56
56
57 HG: Enter commit message. Lines beginning with 'HG:' are removed.
57 HG: Enter commit message. Lines beginning with 'HG:' are removed.
58 HG: Leave message empty to abort commit.
58 HG: Leave message empty to abort commit.
59 HG: --
59 HG: --
60 HG: user: Nicolas Dumazet <nicdumz.commits@gmail.com>
60 HG: user: Nicolas Dumazet <nicdumz.commits@gmail.com>
61 HG: branch 'default'
61 HG: branch 'default'
62 HG: changed D
62 HG: added D
63 saved backup bundle to $TESTTMP/a1/.hg/strip-backup/*-backup.hg (glob)
63 saved backup bundle to $TESTTMP/a1/.hg/strip-backup/*-backup.hg (glob)
64
64
65 $ hg tglog
65 $ hg tglog
66 o 7: 'D'
66 o 7: 'D'
67 |
67 |
68 @ 6: 'H'
68 @ 6: 'H'
69 |
69 |
70 | o 5: 'G'
70 | o 5: 'G'
71 |/|
71 |/|
72 o | 4: 'F'
72 o | 4: 'F'
73 | |
73 | |
74 | o 3: 'E'
74 | o 3: 'E'
75 |/
75 |/
76 | o 2: 'C'
76 | o 2: 'C'
77 | |
77 | |
78 | o 1: 'B'
78 | o 1: 'B'
79 |/
79 |/
80 o 0: 'A'
80 o 0: 'A'
81
81
82 $ cd ..
82 $ cd ..
83
83
84
84
85 D onto F - intermediate point:
85 D onto F - intermediate point:
86 (this also tests that editor is not invoked if '--edit' is not specified)
86 (this also tests that editor is not invoked if '--edit' is not specified)
87
87
88 $ hg clone -q -u . a a2
88 $ hg clone -q -u . a a2
89 $ cd a2
89 $ cd a2
90
90
91 $ HGEDITOR=cat hg rebase -s 3 -d 5
91 $ HGEDITOR=cat hg rebase -s 3 -d 5
92 saved backup bundle to $TESTTMP/a2/.hg/strip-backup/*-backup.hg (glob)
92 saved backup bundle to $TESTTMP/a2/.hg/strip-backup/*-backup.hg (glob)
93
93
94 $ hg tglog
94 $ hg tglog
95 o 7: 'D'
95 o 7: 'D'
96 |
96 |
97 | @ 6: 'H'
97 | @ 6: 'H'
98 |/
98 |/
99 | o 5: 'G'
99 | o 5: 'G'
100 |/|
100 |/|
101 o | 4: 'F'
101 o | 4: 'F'
102 | |
102 | |
103 | o 3: 'E'
103 | o 3: 'E'
104 |/
104 |/
105 | o 2: 'C'
105 | o 2: 'C'
106 | |
106 | |
107 | o 1: 'B'
107 | o 1: 'B'
108 |/
108 |/
109 o 0: 'A'
109 o 0: 'A'
110
110
111 $ cd ..
111 $ cd ..
112
112
113
113
114 E onto H - skip of G:
114 E onto H - skip of G:
115
115
116 $ hg clone -q -u . a a3
116 $ hg clone -q -u . a a3
117 $ cd a3
117 $ cd a3
118
118
119 $ hg rebase -s 4 -d 7
119 $ hg rebase -s 4 -d 7
120 saved backup bundle to $TESTTMP/a3/.hg/strip-backup/*-backup.hg (glob)
120 saved backup bundle to $TESTTMP/a3/.hg/strip-backup/*-backup.hg (glob)
121
121
122 $ hg tglog
122 $ hg tglog
123 o 6: 'E'
123 o 6: 'E'
124 |
124 |
125 @ 5: 'H'
125 @ 5: 'H'
126 |
126 |
127 o 4: 'F'
127 o 4: 'F'
128 |
128 |
129 | o 3: 'D'
129 | o 3: 'D'
130 | |
130 | |
131 | o 2: 'C'
131 | o 2: 'C'
132 | |
132 | |
133 | o 1: 'B'
133 | o 1: 'B'
134 |/
134 |/
135 o 0: 'A'
135 o 0: 'A'
136
136
137 $ cd ..
137 $ cd ..
138
138
139
139
140 F onto E - rebase of a branching point (skip G):
140 F onto E - rebase of a branching point (skip G):
141
141
142 $ hg clone -q -u . a a4
142 $ hg clone -q -u . a a4
143 $ cd a4
143 $ cd a4
144
144
145 $ hg rebase -s 5 -d 4
145 $ hg rebase -s 5 -d 4
146 saved backup bundle to $TESTTMP/a4/.hg/strip-backup/*-backup.hg (glob)
146 saved backup bundle to $TESTTMP/a4/.hg/strip-backup/*-backup.hg (glob)
147
147
148 $ hg tglog
148 $ hg tglog
149 @ 6: 'H'
149 @ 6: 'H'
150 |
150 |
151 o 5: 'F'
151 o 5: 'F'
152 |
152 |
153 o 4: 'E'
153 o 4: 'E'
154 |
154 |
155 | o 3: 'D'
155 | o 3: 'D'
156 | |
156 | |
157 | o 2: 'C'
157 | o 2: 'C'
158 | |
158 | |
159 | o 1: 'B'
159 | o 1: 'B'
160 |/
160 |/
161 o 0: 'A'
161 o 0: 'A'
162
162
163 $ cd ..
163 $ cd ..
164
164
165
165
166 G onto H - merged revision having a parent in ancestors of target:
166 G onto H - merged revision having a parent in ancestors of target:
167
167
168 $ hg clone -q -u . a a5
168 $ hg clone -q -u . a a5
169 $ cd a5
169 $ cd a5
170
170
171 $ hg rebase -s 6 -d 7
171 $ hg rebase -s 6 -d 7
172 saved backup bundle to $TESTTMP/a5/.hg/strip-backup/*-backup.hg (glob)
172 saved backup bundle to $TESTTMP/a5/.hg/strip-backup/*-backup.hg (glob)
173
173
174 $ hg tglog
174 $ hg tglog
175 o 7: 'G'
175 o 7: 'G'
176 |\
176 |\
177 | @ 6: 'H'
177 | @ 6: 'H'
178 | |
178 | |
179 | o 5: 'F'
179 | o 5: 'F'
180 | |
180 | |
181 o | 4: 'E'
181 o | 4: 'E'
182 |/
182 |/
183 | o 3: 'D'
183 | o 3: 'D'
184 | |
184 | |
185 | o 2: 'C'
185 | o 2: 'C'
186 | |
186 | |
187 | o 1: 'B'
187 | o 1: 'B'
188 |/
188 |/
189 o 0: 'A'
189 o 0: 'A'
190
190
191 $ cd ..
191 $ cd ..
192
192
193
193
194 F onto B - G maintains E as parent:
194 F onto B - G maintains E as parent:
195
195
196 $ hg clone -q -u . a a6
196 $ hg clone -q -u . a a6
197 $ cd a6
197 $ cd a6
198
198
199 $ hg rebase -s 5 -d 1
199 $ hg rebase -s 5 -d 1
200 saved backup bundle to $TESTTMP/a6/.hg/strip-backup/*-backup.hg (glob)
200 saved backup bundle to $TESTTMP/a6/.hg/strip-backup/*-backup.hg (glob)
201
201
202 $ hg tglog
202 $ hg tglog
203 @ 7: 'H'
203 @ 7: 'H'
204 |
204 |
205 | o 6: 'G'
205 | o 6: 'G'
206 |/|
206 |/|
207 o | 5: 'F'
207 o | 5: 'F'
208 | |
208 | |
209 | o 4: 'E'
209 | o 4: 'E'
210 | |
210 | |
211 | | o 3: 'D'
211 | | o 3: 'D'
212 | | |
212 | | |
213 +---o 2: 'C'
213 +---o 2: 'C'
214 | |
214 | |
215 o | 1: 'B'
215 o | 1: 'B'
216 |/
216 |/
217 o 0: 'A'
217 o 0: 'A'
218
218
219 $ cd ..
219 $ cd ..
220
220
221
221
222 These will fail (using --source):
222 These will fail (using --source):
223
223
224 G onto F - rebase onto an ancestor:
224 G onto F - rebase onto an ancestor:
225
225
226 $ hg clone -q -u . a a7
226 $ hg clone -q -u . a a7
227 $ cd a7
227 $ cd a7
228
228
229 $ hg rebase -s 6 -d 5
229 $ hg rebase -s 6 -d 5
230 nothing to rebase
230 nothing to rebase
231 [1]
231 [1]
232
232
233 F onto G - rebase onto a descendant:
233 F onto G - rebase onto a descendant:
234
234
235 $ hg rebase -s 5 -d 6
235 $ hg rebase -s 5 -d 6
236 abort: source is ancestor of destination
236 abort: source is ancestor of destination
237 [255]
237 [255]
238
238
239 G onto B - merge revision with both parents not in ancestors of target:
239 G onto B - merge revision with both parents not in ancestors of target:
240
240
241 $ hg rebase -s 6 -d 1
241 $ hg rebase -s 6 -d 1
242 abort: cannot use revision 6 as base, result would have 3 parents
242 abort: cannot use revision 6 as base, result would have 3 parents
243 [255]
243 [255]
244
244
245
245
246 These will abort gracefully (using --base):
246 These will abort gracefully (using --base):
247
247
248 G onto G - rebase onto same changeset:
248 G onto G - rebase onto same changeset:
249
249
250 $ hg rebase -b 6 -d 6
250 $ hg rebase -b 6 -d 6
251 nothing to rebase - eea13746799a is both "base" and destination
251 nothing to rebase - eea13746799a is both "base" and destination
252 [1]
252 [1]
253
253
254 G onto F - rebase onto an ancestor:
254 G onto F - rebase onto an ancestor:
255
255
256 $ hg rebase -b 6 -d 5
256 $ hg rebase -b 6 -d 5
257 nothing to rebase
257 nothing to rebase
258 [1]
258 [1]
259
259
260 F onto G - rebase onto a descendant:
260 F onto G - rebase onto a descendant:
261
261
262 $ hg rebase -b 5 -d 6
262 $ hg rebase -b 5 -d 6
263 nothing to rebase - "base" 24b6387c8c8c is already an ancestor of destination eea13746799a
263 nothing to rebase - "base" 24b6387c8c8c is already an ancestor of destination eea13746799a
264 [1]
264 [1]
265
265
266 C onto A - rebase onto an ancestor:
266 C onto A - rebase onto an ancestor:
267
267
268 $ hg rebase -d 0 -s 2
268 $ hg rebase -d 0 -s 2
269 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/5fddd98957c8-backup.hg (glob)
269 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/5fddd98957c8-backup.hg (glob)
270 $ hg tglog
270 $ hg tglog
271 o 7: 'D'
271 o 7: 'D'
272 |
272 |
273 o 6: 'C'
273 o 6: 'C'
274 |
274 |
275 | @ 5: 'H'
275 | @ 5: 'H'
276 | |
276 | |
277 | | o 4: 'G'
277 | | o 4: 'G'
278 | |/|
278 | |/|
279 | o | 3: 'F'
279 | o | 3: 'F'
280 |/ /
280 |/ /
281 | o 2: 'E'
281 | o 2: 'E'
282 |/
282 |/
283 | o 1: 'B'
283 | o 1: 'B'
284 |/
284 |/
285 o 0: 'A'
285 o 0: 'A'
286
286
287
287
288 Check rebasing public changeset
288 Check rebasing public changeset
289
289
290 $ hg pull --config phases.publish=True -q -r 6 . # update phase of 6
290 $ hg pull --config phases.publish=True -q -r 6 . # update phase of 6
291 $ hg rebase -d 0 -b 6
291 $ hg rebase -d 0 -b 6
292 nothing to rebase
292 nothing to rebase
293 [1]
293 [1]
294 $ hg rebase -d 5 -b 6
294 $ hg rebase -d 5 -b 6
295 abort: can't rebase immutable changeset e1c4361dd923
295 abort: can't rebase immutable changeset e1c4361dd923
296 (see hg help phases for details)
296 (see hg help phases for details)
297 [255]
297 [255]
298
298
299 $ hg rebase -d 5 -b 6 --keep
299 $ hg rebase -d 5 -b 6 --keep
300
300
301 Check rebasing mutable changeset
301 Check rebasing mutable changeset
302 Source phase greater or equal to destination phase: new changeset get the phase of source:
302 Source phase greater or equal to destination phase: new changeset get the phase of source:
303 $ hg rebase -s9 -d0
303 $ hg rebase -s9 -d0
304 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/2b23e52411f4-backup.hg (glob)
304 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/2b23e52411f4-backup.hg (glob)
305 $ hg log --template "{phase}\n" -r 9
305 $ hg log --template "{phase}\n" -r 9
306 draft
306 draft
307 $ hg rebase -s9 -d1
307 $ hg rebase -s9 -d1
308 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/2cb10d0cfc6c-backup.hg (glob)
308 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/2cb10d0cfc6c-backup.hg (glob)
309 $ hg log --template "{phase}\n" -r 9
309 $ hg log --template "{phase}\n" -r 9
310 draft
310 draft
311 $ hg phase --force --secret 9
311 $ hg phase --force --secret 9
312 $ hg rebase -s9 -d0
312 $ hg rebase -s9 -d0
313 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/c5b12b67163a-backup.hg (glob)
313 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/c5b12b67163a-backup.hg (glob)
314 $ hg log --template "{phase}\n" -r 9
314 $ hg log --template "{phase}\n" -r 9
315 secret
315 secret
316 $ hg rebase -s9 -d1
316 $ hg rebase -s9 -d1
317 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/2a0524f868ac-backup.hg (glob)
317 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/2a0524f868ac-backup.hg (glob)
318 $ hg log --template "{phase}\n" -r 9
318 $ hg log --template "{phase}\n" -r 9
319 secret
319 secret
320 Source phase lower than destination phase: new changeset get the phase of destination:
320 Source phase lower than destination phase: new changeset get the phase of destination:
321 $ hg rebase -s8 -d9
321 $ hg rebase -s8 -d9
322 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/6d4f22462821-backup.hg (glob)
322 saved backup bundle to $TESTTMP/a7/.hg/strip-backup/6d4f22462821-backup.hg (glob)
323 $ hg log --template "{phase}\n" -r 'rev(9)'
323 $ hg log --template "{phase}\n" -r 'rev(9)'
324 secret
324 secret
325
325
326 $ cd ..
326 $ cd ..
327
327
328 Test for revset
328 Test for revset
329
329
330 We need a bit different graph
330 We need a bit different graph
331 All destination are B
331 All destination are B
332
332
333 $ hg init ah
333 $ hg init ah
334 $ cd ah
334 $ cd ah
335 $ hg unbundle "$TESTDIR/bundles/rebase-revset.hg"
335 $ hg unbundle "$TESTDIR/bundles/rebase-revset.hg"
336 adding changesets
336 adding changesets
337 adding manifests
337 adding manifests
338 adding file changes
338 adding file changes
339 added 9 changesets with 9 changes to 9 files (+2 heads)
339 added 9 changesets with 9 changes to 9 files (+2 heads)
340 (run 'hg heads' to see heads, 'hg merge' to merge)
340 (run 'hg heads' to see heads, 'hg merge' to merge)
341 $ hg tglog
341 $ hg tglog
342 o 8: 'I'
342 o 8: 'I'
343 |
343 |
344 o 7: 'H'
344 o 7: 'H'
345 |
345 |
346 o 6: 'G'
346 o 6: 'G'
347 |
347 |
348 | o 5: 'F'
348 | o 5: 'F'
349 | |
349 | |
350 | o 4: 'E'
350 | o 4: 'E'
351 |/
351 |/
352 o 3: 'D'
352 o 3: 'D'
353 |
353 |
354 o 2: 'C'
354 o 2: 'C'
355 |
355 |
356 | o 1: 'B'
356 | o 1: 'B'
357 |/
357 |/
358 o 0: 'A'
358 o 0: 'A'
359
359
360 $ cd ..
360 $ cd ..
361
361
362
362
363 Simple case with keep:
363 Simple case with keep:
364
364
365 Source on have two descendant heads but ask for one
365 Source on have two descendant heads but ask for one
366
366
367 $ hg clone -q -u . ah ah1
367 $ hg clone -q -u . ah ah1
368 $ cd ah1
368 $ cd ah1
369 $ hg rebase -r '2::8' -d 1
369 $ hg rebase -r '2::8' -d 1
370 abort: can't remove original changesets with unrebased descendants
370 abort: can't remove original changesets with unrebased descendants
371 (use --keep to keep original changesets)
371 (use --keep to keep original changesets)
372 [255]
372 [255]
373 $ hg rebase -r '2::8' -d 1 --keep
373 $ hg rebase -r '2::8' -d 1 --keep
374 $ hg tglog
374 $ hg tglog
375 o 13: 'I'
375 o 13: 'I'
376 |
376 |
377 o 12: 'H'
377 o 12: 'H'
378 |
378 |
379 o 11: 'G'
379 o 11: 'G'
380 |
380 |
381 o 10: 'D'
381 o 10: 'D'
382 |
382 |
383 o 9: 'C'
383 o 9: 'C'
384 |
384 |
385 | o 8: 'I'
385 | o 8: 'I'
386 | |
386 | |
387 | o 7: 'H'
387 | o 7: 'H'
388 | |
388 | |
389 | o 6: 'G'
389 | o 6: 'G'
390 | |
390 | |
391 | | o 5: 'F'
391 | | o 5: 'F'
392 | | |
392 | | |
393 | | o 4: 'E'
393 | | o 4: 'E'
394 | |/
394 | |/
395 | o 3: 'D'
395 | o 3: 'D'
396 | |
396 | |
397 | o 2: 'C'
397 | o 2: 'C'
398 | |
398 | |
399 o | 1: 'B'
399 o | 1: 'B'
400 |/
400 |/
401 o 0: 'A'
401 o 0: 'A'
402
402
403
403
404 $ cd ..
404 $ cd ..
405
405
406 Base on have one descendant heads we ask for but common ancestor have two
406 Base on have one descendant heads we ask for but common ancestor have two
407
407
408 $ hg clone -q -u . ah ah2
408 $ hg clone -q -u . ah ah2
409 $ cd ah2
409 $ cd ah2
410 $ hg rebase -r '3::8' -d 1
410 $ hg rebase -r '3::8' -d 1
411 abort: can't remove original changesets with unrebased descendants
411 abort: can't remove original changesets with unrebased descendants
412 (use --keep to keep original changesets)
412 (use --keep to keep original changesets)
413 [255]
413 [255]
414 $ hg rebase -r '3::8' -d 1 --keep
414 $ hg rebase -r '3::8' -d 1 --keep
415 $ hg tglog
415 $ hg tglog
416 o 12: 'I'
416 o 12: 'I'
417 |
417 |
418 o 11: 'H'
418 o 11: 'H'
419 |
419 |
420 o 10: 'G'
420 o 10: 'G'
421 |
421 |
422 o 9: 'D'
422 o 9: 'D'
423 |
423 |
424 | o 8: 'I'
424 | o 8: 'I'
425 | |
425 | |
426 | o 7: 'H'
426 | o 7: 'H'
427 | |
427 | |
428 | o 6: 'G'
428 | o 6: 'G'
429 | |
429 | |
430 | | o 5: 'F'
430 | | o 5: 'F'
431 | | |
431 | | |
432 | | o 4: 'E'
432 | | o 4: 'E'
433 | |/
433 | |/
434 | o 3: 'D'
434 | o 3: 'D'
435 | |
435 | |
436 | o 2: 'C'
436 | o 2: 'C'
437 | |
437 | |
438 o | 1: 'B'
438 o | 1: 'B'
439 |/
439 |/
440 o 0: 'A'
440 o 0: 'A'
441
441
442
442
443 $ cd ..
443 $ cd ..
444
444
445 rebase subset
445 rebase subset
446
446
447 $ hg clone -q -u . ah ah3
447 $ hg clone -q -u . ah ah3
448 $ cd ah3
448 $ cd ah3
449 $ hg rebase -r '3::7' -d 1
449 $ hg rebase -r '3::7' -d 1
450 abort: can't remove original changesets with unrebased descendants
450 abort: can't remove original changesets with unrebased descendants
451 (use --keep to keep original changesets)
451 (use --keep to keep original changesets)
452 [255]
452 [255]
453 $ hg rebase -r '3::7' -d 1 --keep
453 $ hg rebase -r '3::7' -d 1 --keep
454 $ hg tglog
454 $ hg tglog
455 o 11: 'H'
455 o 11: 'H'
456 |
456 |
457 o 10: 'G'
457 o 10: 'G'
458 |
458 |
459 o 9: 'D'
459 o 9: 'D'
460 |
460 |
461 | o 8: 'I'
461 | o 8: 'I'
462 | |
462 | |
463 | o 7: 'H'
463 | o 7: 'H'
464 | |
464 | |
465 | o 6: 'G'
465 | o 6: 'G'
466 | |
466 | |
467 | | o 5: 'F'
467 | | o 5: 'F'
468 | | |
468 | | |
469 | | o 4: 'E'
469 | | o 4: 'E'
470 | |/
470 | |/
471 | o 3: 'D'
471 | o 3: 'D'
472 | |
472 | |
473 | o 2: 'C'
473 | o 2: 'C'
474 | |
474 | |
475 o | 1: 'B'
475 o | 1: 'B'
476 |/
476 |/
477 o 0: 'A'
477 o 0: 'A'
478
478
479
479
480 $ cd ..
480 $ cd ..
481
481
482 rebase subset with multiple head
482 rebase subset with multiple head
483
483
484 $ hg clone -q -u . ah ah4
484 $ hg clone -q -u . ah ah4
485 $ cd ah4
485 $ cd ah4
486 $ hg rebase -r '3::(7+5)' -d 1
486 $ hg rebase -r '3::(7+5)' -d 1
487 abort: can't remove original changesets with unrebased descendants
487 abort: can't remove original changesets with unrebased descendants
488 (use --keep to keep original changesets)
488 (use --keep to keep original changesets)
489 [255]
489 [255]
490 $ hg rebase -r '3::(7+5)' -d 1 --keep
490 $ hg rebase -r '3::(7+5)' -d 1 --keep
491 $ hg tglog
491 $ hg tglog
492 o 13: 'H'
492 o 13: 'H'
493 |
493 |
494 o 12: 'G'
494 o 12: 'G'
495 |
495 |
496 | o 11: 'F'
496 | o 11: 'F'
497 | |
497 | |
498 | o 10: 'E'
498 | o 10: 'E'
499 |/
499 |/
500 o 9: 'D'
500 o 9: 'D'
501 |
501 |
502 | o 8: 'I'
502 | o 8: 'I'
503 | |
503 | |
504 | o 7: 'H'
504 | o 7: 'H'
505 | |
505 | |
506 | o 6: 'G'
506 | o 6: 'G'
507 | |
507 | |
508 | | o 5: 'F'
508 | | o 5: 'F'
509 | | |
509 | | |
510 | | o 4: 'E'
510 | | o 4: 'E'
511 | |/
511 | |/
512 | o 3: 'D'
512 | o 3: 'D'
513 | |
513 | |
514 | o 2: 'C'
514 | o 2: 'C'
515 | |
515 | |
516 o | 1: 'B'
516 o | 1: 'B'
517 |/
517 |/
518 o 0: 'A'
518 o 0: 'A'
519
519
520
520
521 $ cd ..
521 $ cd ..
522
522
523 More advanced tests
523 More advanced tests
524
524
525 rebase on ancestor with revset
525 rebase on ancestor with revset
526
526
527 $ hg clone -q -u . ah ah5
527 $ hg clone -q -u . ah ah5
528 $ cd ah5
528 $ cd ah5
529 $ hg rebase -r '6::' -d 2
529 $ hg rebase -r '6::' -d 2
530 saved backup bundle to $TESTTMP/ah5/.hg/strip-backup/3d8a618087a7-backup.hg (glob)
530 saved backup bundle to $TESTTMP/ah5/.hg/strip-backup/3d8a618087a7-backup.hg (glob)
531 $ hg tglog
531 $ hg tglog
532 o 8: 'I'
532 o 8: 'I'
533 |
533 |
534 o 7: 'H'
534 o 7: 'H'
535 |
535 |
536 o 6: 'G'
536 o 6: 'G'
537 |
537 |
538 | o 5: 'F'
538 | o 5: 'F'
539 | |
539 | |
540 | o 4: 'E'
540 | o 4: 'E'
541 | |
541 | |
542 | o 3: 'D'
542 | o 3: 'D'
543 |/
543 |/
544 o 2: 'C'
544 o 2: 'C'
545 |
545 |
546 | o 1: 'B'
546 | o 1: 'B'
547 |/
547 |/
548 o 0: 'A'
548 o 0: 'A'
549
549
550 $ cd ..
550 $ cd ..
551
551
552
552
553 rebase with multiple root.
553 rebase with multiple root.
554 We rebase E and G on B
554 We rebase E and G on B
555 We would expect heads are I, F if it was supported
555 We would expect heads are I, F if it was supported
556
556
557 $ hg clone -q -u . ah ah6
557 $ hg clone -q -u . ah ah6
558 $ cd ah6
558 $ cd ah6
559 $ hg rebase -r '(4+6)::' -d 1
559 $ hg rebase -r '(4+6)::' -d 1
560 saved backup bundle to $TESTTMP/ah6/.hg/strip-backup/3d8a618087a7-backup.hg (glob)
560 saved backup bundle to $TESTTMP/ah6/.hg/strip-backup/3d8a618087a7-backup.hg (glob)
561 $ hg tglog
561 $ hg tglog
562 o 8: 'I'
562 o 8: 'I'
563 |
563 |
564 o 7: 'H'
564 o 7: 'H'
565 |
565 |
566 o 6: 'G'
566 o 6: 'G'
567 |
567 |
568 | o 5: 'F'
568 | o 5: 'F'
569 | |
569 | |
570 | o 4: 'E'
570 | o 4: 'E'
571 |/
571 |/
572 | o 3: 'D'
572 | o 3: 'D'
573 | |
573 | |
574 | o 2: 'C'
574 | o 2: 'C'
575 | |
575 | |
576 o | 1: 'B'
576 o | 1: 'B'
577 |/
577 |/
578 o 0: 'A'
578 o 0: 'A'
579
579
580 $ cd ..
580 $ cd ..
581
581
582 More complex rebase with multiple roots
582 More complex rebase with multiple roots
583 each root have a different common ancestor with the destination and this is a detach
583 each root have a different common ancestor with the destination and this is a detach
584
584
585 (setup)
585 (setup)
586
586
587 $ hg clone -q -u . a a8
587 $ hg clone -q -u . a a8
588 $ cd a8
588 $ cd a8
589 $ echo I > I
589 $ echo I > I
590 $ hg add I
590 $ hg add I
591 $ hg commit -m I
591 $ hg commit -m I
592 $ hg up 4
592 $ hg up 4
593 1 files updated, 0 files merged, 3 files removed, 0 files unresolved
593 1 files updated, 0 files merged, 3 files removed, 0 files unresolved
594 $ echo I > J
594 $ echo I > J
595 $ hg add J
595 $ hg add J
596 $ hg commit -m J
596 $ hg commit -m J
597 created new head
597 created new head
598 $ echo I > K
598 $ echo I > K
599 $ hg add K
599 $ hg add K
600 $ hg commit -m K
600 $ hg commit -m K
601 $ hg tglog
601 $ hg tglog
602 @ 10: 'K'
602 @ 10: 'K'
603 |
603 |
604 o 9: 'J'
604 o 9: 'J'
605 |
605 |
606 | o 8: 'I'
606 | o 8: 'I'
607 | |
607 | |
608 | o 7: 'H'
608 | o 7: 'H'
609 | |
609 | |
610 +---o 6: 'G'
610 +---o 6: 'G'
611 | |/
611 | |/
612 | o 5: 'F'
612 | o 5: 'F'
613 | |
613 | |
614 o | 4: 'E'
614 o | 4: 'E'
615 |/
615 |/
616 | o 3: 'D'
616 | o 3: 'D'
617 | |
617 | |
618 | o 2: 'C'
618 | o 2: 'C'
619 | |
619 | |
620 | o 1: 'B'
620 | o 1: 'B'
621 |/
621 |/
622 o 0: 'A'
622 o 0: 'A'
623
623
624 (actual test)
624 (actual test)
625
625
626 $ hg rebase --dest 'desc(G)' --rev 'desc(K) + desc(I)'
626 $ hg rebase --dest 'desc(G)' --rev 'desc(K) + desc(I)'
627 saved backup bundle to $TESTTMP/a8/.hg/strip-backup/23a4ace37988-backup.hg (glob)
627 saved backup bundle to $TESTTMP/a8/.hg/strip-backup/23a4ace37988-backup.hg (glob)
628 $ hg log --rev 'children(desc(G))'
628 $ hg log --rev 'children(desc(G))'
629 changeset: 9:adb617877056
629 changeset: 9:adb617877056
630 parent: 6:eea13746799a
630 parent: 6:eea13746799a
631 user: test
631 user: test
632 date: Thu Jan 01 00:00:00 1970 +0000
632 date: Thu Jan 01 00:00:00 1970 +0000
633 summary: I
633 summary: I
634
634
635 changeset: 10:882431a34a0e
635 changeset: 10:882431a34a0e
636 tag: tip
636 tag: tip
637 parent: 6:eea13746799a
637 parent: 6:eea13746799a
638 user: test
638 user: test
639 date: Thu Jan 01 00:00:00 1970 +0000
639 date: Thu Jan 01 00:00:00 1970 +0000
640 summary: K
640 summary: K
641
641
642 $ hg tglog
642 $ hg tglog
643 @ 10: 'K'
643 @ 10: 'K'
644 |
644 |
645 | o 9: 'I'
645 | o 9: 'I'
646 |/
646 |/
647 | o 8: 'J'
647 | o 8: 'J'
648 | |
648 | |
649 | | o 7: 'H'
649 | | o 7: 'H'
650 | | |
650 | | |
651 o---+ 6: 'G'
651 o---+ 6: 'G'
652 |/ /
652 |/ /
653 | o 5: 'F'
653 | o 5: 'F'
654 | |
654 | |
655 o | 4: 'E'
655 o | 4: 'E'
656 |/
656 |/
657 | o 3: 'D'
657 | o 3: 'D'
658 | |
658 | |
659 | o 2: 'C'
659 | o 2: 'C'
660 | |
660 | |
661 | o 1: 'B'
661 | o 1: 'B'
662 |/
662 |/
663 o 0: 'A'
663 o 0: 'A'
664
664
665
665
666 Test that rebase is not confused by $CWD disappearing during rebase (issue4121)
666 Test that rebase is not confused by $CWD disappearing during rebase (issue4121)
667
667
668 $ cd ..
668 $ cd ..
669 $ hg init cwd-vanish
669 $ hg init cwd-vanish
670 $ cd cwd-vanish
670 $ cd cwd-vanish
671 $ touch initial-file
671 $ touch initial-file
672 $ hg add initial-file
672 $ hg add initial-file
673 $ hg commit -m 'initial commit'
673 $ hg commit -m 'initial commit'
674 $ touch dest-file
674 $ touch dest-file
675 $ hg add dest-file
675 $ hg add dest-file
676 $ hg commit -m 'dest commit'
676 $ hg commit -m 'dest commit'
677 $ hg up 0
677 $ hg up 0
678 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
678 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
679 $ touch other-file
679 $ touch other-file
680 $ hg add other-file
680 $ hg add other-file
681 $ hg commit -m 'first source commit'
681 $ hg commit -m 'first source commit'
682 created new head
682 created new head
683 $ mkdir subdir
683 $ mkdir subdir
684 $ cd subdir
684 $ cd subdir
685 $ touch subfile
685 $ touch subfile
686 $ hg add subfile
686 $ hg add subfile
687 $ hg commit -m 'second source with subdir'
687 $ hg commit -m 'second source with subdir'
688 $ hg rebase -b . -d 1 --traceback
688 $ hg rebase -b . -d 1 --traceback
689 saved backup bundle to $TESTTMP/cwd-vanish/.hg/strip-backup/779a07b1b7a0-backup.hg (glob)
689 saved backup bundle to $TESTTMP/cwd-vanish/.hg/strip-backup/779a07b1b7a0-backup.hg (glob)
General Comments 0
You need to be logged in to leave comments. Login now