##// END OF EJS Templates
copytrace: add a a new config to limit the number of drafts in heuristics...
Pulkit Goyal -
r34312:1826d695 default
parent child Browse files
Show More
@@ -1,665 +1,668 b''
1 # configitems.py - centralized declaration of configuration option
1 # configitems.py - centralized declaration of configuration option
2 #
2 #
3 # Copyright 2017 Pierre-Yves David <pierre-yves.david@octobus.net>
3 # Copyright 2017 Pierre-Yves David <pierre-yves.david@octobus.net>
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 __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import functools
10 import functools
11
11
12 from . import (
12 from . import (
13 encoding,
13 encoding,
14 error,
14 error,
15 )
15 )
16
16
17 def loadconfigtable(ui, extname, configtable):
17 def loadconfigtable(ui, extname, configtable):
18 """update config item known to the ui with the extension ones"""
18 """update config item known to the ui with the extension ones"""
19 for section, items in configtable.items():
19 for section, items in configtable.items():
20 knownitems = ui._knownconfig.setdefault(section, {})
20 knownitems = ui._knownconfig.setdefault(section, {})
21 knownkeys = set(knownitems)
21 knownkeys = set(knownitems)
22 newkeys = set(items)
22 newkeys = set(items)
23 for key in sorted(knownkeys & newkeys):
23 for key in sorted(knownkeys & newkeys):
24 msg = "extension '%s' overwrite config item '%s.%s'"
24 msg = "extension '%s' overwrite config item '%s.%s'"
25 msg %= (extname, section, key)
25 msg %= (extname, section, key)
26 ui.develwarn(msg, config='warn-config')
26 ui.develwarn(msg, config='warn-config')
27
27
28 knownitems.update(items)
28 knownitems.update(items)
29
29
30 class configitem(object):
30 class configitem(object):
31 """represent a known config item
31 """represent a known config item
32
32
33 :section: the official config section where to find this item,
33 :section: the official config section where to find this item,
34 :name: the official name within the section,
34 :name: the official name within the section,
35 :default: default value for this item,
35 :default: default value for this item,
36 :alias: optional list of tuples as alternatives.
36 :alias: optional list of tuples as alternatives.
37 """
37 """
38
38
39 def __init__(self, section, name, default=None, alias=()):
39 def __init__(self, section, name, default=None, alias=()):
40 self.section = section
40 self.section = section
41 self.name = name
41 self.name = name
42 self.default = default
42 self.default = default
43 self.alias = list(alias)
43 self.alias = list(alias)
44
44
45 coreitems = {}
45 coreitems = {}
46
46
47 def _register(configtable, *args, **kwargs):
47 def _register(configtable, *args, **kwargs):
48 item = configitem(*args, **kwargs)
48 item = configitem(*args, **kwargs)
49 section = configtable.setdefault(item.section, {})
49 section = configtable.setdefault(item.section, {})
50 if item.name in section:
50 if item.name in section:
51 msg = "duplicated config item registration for '%s.%s'"
51 msg = "duplicated config item registration for '%s.%s'"
52 raise error.ProgrammingError(msg % (item.section, item.name))
52 raise error.ProgrammingError(msg % (item.section, item.name))
53 section[item.name] = item
53 section[item.name] = item
54
54
55 # special value for case where the default is derived from other values
55 # special value for case where the default is derived from other values
56 dynamicdefault = object()
56 dynamicdefault = object()
57
57
58 # Registering actual config items
58 # Registering actual config items
59
59
60 def getitemregister(configtable):
60 def getitemregister(configtable):
61 return functools.partial(_register, configtable)
61 return functools.partial(_register, configtable)
62
62
63 coreconfigitem = getitemregister(coreitems)
63 coreconfigitem = getitemregister(coreitems)
64
64
65 coreconfigitem('auth', 'cookiefile',
65 coreconfigitem('auth', 'cookiefile',
66 default=None,
66 default=None,
67 )
67 )
68 # bookmarks.pushing: internal hack for discovery
68 # bookmarks.pushing: internal hack for discovery
69 coreconfigitem('bookmarks', 'pushing',
69 coreconfigitem('bookmarks', 'pushing',
70 default=list,
70 default=list,
71 )
71 )
72 # bundle.mainreporoot: internal hack for bundlerepo
72 # bundle.mainreporoot: internal hack for bundlerepo
73 coreconfigitem('bundle', 'mainreporoot',
73 coreconfigitem('bundle', 'mainreporoot',
74 default='',
74 default='',
75 )
75 )
76 # bundle.reorder: experimental config
76 # bundle.reorder: experimental config
77 coreconfigitem('bundle', 'reorder',
77 coreconfigitem('bundle', 'reorder',
78 default='auto',
78 default='auto',
79 )
79 )
80 coreconfigitem('censor', 'policy',
80 coreconfigitem('censor', 'policy',
81 default='abort',
81 default='abort',
82 )
82 )
83 coreconfigitem('chgserver', 'idletimeout',
83 coreconfigitem('chgserver', 'idletimeout',
84 default=3600,
84 default=3600,
85 )
85 )
86 coreconfigitem('chgserver', 'skiphash',
86 coreconfigitem('chgserver', 'skiphash',
87 default=False,
87 default=False,
88 )
88 )
89 coreconfigitem('cmdserver', 'log',
89 coreconfigitem('cmdserver', 'log',
90 default=None,
90 default=None,
91 )
91 )
92 coreconfigitem('color', 'mode',
92 coreconfigitem('color', 'mode',
93 default='auto',
93 default='auto',
94 )
94 )
95 coreconfigitem('color', 'pagermode',
95 coreconfigitem('color', 'pagermode',
96 default=dynamicdefault,
96 default=dynamicdefault,
97 )
97 )
98 coreconfigitem('commands', 'status.relative',
98 coreconfigitem('commands', 'status.relative',
99 default=False,
99 default=False,
100 )
100 )
101 coreconfigitem('commands', 'status.skipstates',
101 coreconfigitem('commands', 'status.skipstates',
102 default=[],
102 default=[],
103 )
103 )
104 coreconfigitem('commands', 'status.verbose',
104 coreconfigitem('commands', 'status.verbose',
105 default=False,
105 default=False,
106 )
106 )
107 coreconfigitem('commands', 'update.requiredest',
107 coreconfigitem('commands', 'update.requiredest',
108 default=False,
108 default=False,
109 )
109 )
110 coreconfigitem('devel', 'all-warnings',
110 coreconfigitem('devel', 'all-warnings',
111 default=False,
111 default=False,
112 )
112 )
113 coreconfigitem('devel', 'bundle2.debug',
113 coreconfigitem('devel', 'bundle2.debug',
114 default=False,
114 default=False,
115 )
115 )
116 coreconfigitem('devel', 'check-locks',
116 coreconfigitem('devel', 'check-locks',
117 default=False,
117 default=False,
118 )
118 )
119 coreconfigitem('devel', 'check-relroot',
119 coreconfigitem('devel', 'check-relroot',
120 default=False,
120 default=False,
121 )
121 )
122 coreconfigitem('devel', 'default-date',
122 coreconfigitem('devel', 'default-date',
123 default=None,
123 default=None,
124 )
124 )
125 coreconfigitem('devel', 'deprec-warn',
125 coreconfigitem('devel', 'deprec-warn',
126 default=False,
126 default=False,
127 )
127 )
128 coreconfigitem('devel', 'disableloaddefaultcerts',
128 coreconfigitem('devel', 'disableloaddefaultcerts',
129 default=False,
129 default=False,
130 )
130 )
131 coreconfigitem('devel', 'legacy.exchange',
131 coreconfigitem('devel', 'legacy.exchange',
132 default=list,
132 default=list,
133 )
133 )
134 coreconfigitem('devel', 'servercafile',
134 coreconfigitem('devel', 'servercafile',
135 default='',
135 default='',
136 )
136 )
137 coreconfigitem('devel', 'serverexactprotocol',
137 coreconfigitem('devel', 'serverexactprotocol',
138 default='',
138 default='',
139 )
139 )
140 coreconfigitem('devel', 'serverrequirecert',
140 coreconfigitem('devel', 'serverrequirecert',
141 default=False,
141 default=False,
142 )
142 )
143 coreconfigitem('devel', 'strip-obsmarkers',
143 coreconfigitem('devel', 'strip-obsmarkers',
144 default=True,
144 default=True,
145 )
145 )
146 coreconfigitem('email', 'charsets',
146 coreconfigitem('email', 'charsets',
147 default=list,
147 default=list,
148 )
148 )
149 coreconfigitem('email', 'method',
149 coreconfigitem('email', 'method',
150 default='smtp',
150 default='smtp',
151 )
151 )
152 coreconfigitem('experimental', 'bundle-phases',
152 coreconfigitem('experimental', 'bundle-phases',
153 default=False,
153 default=False,
154 )
154 )
155 coreconfigitem('experimental', 'bundle2-advertise',
155 coreconfigitem('experimental', 'bundle2-advertise',
156 default=True,
156 default=True,
157 )
157 )
158 coreconfigitem('experimental', 'bundle2-output-capture',
158 coreconfigitem('experimental', 'bundle2-output-capture',
159 default=False,
159 default=False,
160 )
160 )
161 coreconfigitem('experimental', 'bundle2.pushback',
161 coreconfigitem('experimental', 'bundle2.pushback',
162 default=False,
162 default=False,
163 )
163 )
164 coreconfigitem('experimental', 'bundle2lazylocking',
164 coreconfigitem('experimental', 'bundle2lazylocking',
165 default=False,
165 default=False,
166 )
166 )
167 coreconfigitem('experimental', 'bundlecomplevel',
167 coreconfigitem('experimental', 'bundlecomplevel',
168 default=None,
168 default=None,
169 )
169 )
170 coreconfigitem('experimental', 'changegroup3',
170 coreconfigitem('experimental', 'changegroup3',
171 default=False,
171 default=False,
172 )
172 )
173 coreconfigitem('experimental', 'clientcompressionengines',
173 coreconfigitem('experimental', 'clientcompressionengines',
174 default=list,
174 default=list,
175 )
175 )
176 coreconfigitem('experimental', 'copytrace',
176 coreconfigitem('experimental', 'copytrace',
177 default='on',
177 default='on',
178 )
178 )
179 coreconfigitem('experimental', 'copytrace.sourcecommitlimit',
180 default=100,
181 )
179 coreconfigitem('experimental', 'crecordtest',
182 coreconfigitem('experimental', 'crecordtest',
180 default=None,
183 default=None,
181 )
184 )
182 coreconfigitem('experimental', 'editortmpinhg',
185 coreconfigitem('experimental', 'editortmpinhg',
183 default=False,
186 default=False,
184 )
187 )
185 coreconfigitem('experimental', 'stabilization',
188 coreconfigitem('experimental', 'stabilization',
186 default=list,
189 default=list,
187 alias=[('experimental', 'evolution')],
190 alias=[('experimental', 'evolution')],
188 )
191 )
189 coreconfigitem('experimental', 'stabilization.bundle-obsmarker',
192 coreconfigitem('experimental', 'stabilization.bundle-obsmarker',
190 default=False,
193 default=False,
191 alias=[('experimental', 'evolution.bundle-obsmarker')],
194 alias=[('experimental', 'evolution.bundle-obsmarker')],
192 )
195 )
193 coreconfigitem('experimental', 'stabilization.track-operation',
196 coreconfigitem('experimental', 'stabilization.track-operation',
194 default=True,
197 default=True,
195 alias=[('experimental', 'evolution.track-operation')]
198 alias=[('experimental', 'evolution.track-operation')]
196 )
199 )
197 coreconfigitem('experimental', 'exportableenviron',
200 coreconfigitem('experimental', 'exportableenviron',
198 default=list,
201 default=list,
199 )
202 )
200 coreconfigitem('experimental', 'extendedheader.index',
203 coreconfigitem('experimental', 'extendedheader.index',
201 default=None,
204 default=None,
202 )
205 )
203 coreconfigitem('experimental', 'extendedheader.similarity',
206 coreconfigitem('experimental', 'extendedheader.similarity',
204 default=False,
207 default=False,
205 )
208 )
206 coreconfigitem('experimental', 'format.compression',
209 coreconfigitem('experimental', 'format.compression',
207 default='zlib',
210 default='zlib',
208 )
211 )
209 coreconfigitem('experimental', 'graphshorten',
212 coreconfigitem('experimental', 'graphshorten',
210 default=False,
213 default=False,
211 )
214 )
212 coreconfigitem('experimental', 'hook-track-tags',
215 coreconfigitem('experimental', 'hook-track-tags',
213 default=False,
216 default=False,
214 )
217 )
215 coreconfigitem('experimental', 'httppostargs',
218 coreconfigitem('experimental', 'httppostargs',
216 default=False,
219 default=False,
217 )
220 )
218 coreconfigitem('experimental', 'manifestv2',
221 coreconfigitem('experimental', 'manifestv2',
219 default=False,
222 default=False,
220 )
223 )
221 coreconfigitem('experimental', 'mergedriver',
224 coreconfigitem('experimental', 'mergedriver',
222 default=None,
225 default=None,
223 )
226 )
224 coreconfigitem('experimental', 'obsmarkers-exchange-debug',
227 coreconfigitem('experimental', 'obsmarkers-exchange-debug',
225 default=False,
228 default=False,
226 )
229 )
227 coreconfigitem('experimental', 'rebase.multidest',
230 coreconfigitem('experimental', 'rebase.multidest',
228 default=False,
231 default=False,
229 )
232 )
230 coreconfigitem('experimental', 'revertalternateinteractivemode',
233 coreconfigitem('experimental', 'revertalternateinteractivemode',
231 default=True,
234 default=True,
232 )
235 )
233 coreconfigitem('experimental', 'revlogv2',
236 coreconfigitem('experimental', 'revlogv2',
234 default=None,
237 default=None,
235 )
238 )
236 coreconfigitem('experimental', 'spacemovesdown',
239 coreconfigitem('experimental', 'spacemovesdown',
237 default=False,
240 default=False,
238 )
241 )
239 coreconfigitem('experimental', 'treemanifest',
242 coreconfigitem('experimental', 'treemanifest',
240 default=False,
243 default=False,
241 )
244 )
242 coreconfigitem('experimental', 'updatecheck',
245 coreconfigitem('experimental', 'updatecheck',
243 default=None,
246 default=None,
244 )
247 )
245 coreconfigitem('format', 'aggressivemergedeltas',
248 coreconfigitem('format', 'aggressivemergedeltas',
246 default=False,
249 default=False,
247 )
250 )
248 coreconfigitem('format', 'chunkcachesize',
251 coreconfigitem('format', 'chunkcachesize',
249 default=None,
252 default=None,
250 )
253 )
251 coreconfigitem('format', 'dotencode',
254 coreconfigitem('format', 'dotencode',
252 default=True,
255 default=True,
253 )
256 )
254 coreconfigitem('format', 'generaldelta',
257 coreconfigitem('format', 'generaldelta',
255 default=False,
258 default=False,
256 )
259 )
257 coreconfigitem('format', 'manifestcachesize',
260 coreconfigitem('format', 'manifestcachesize',
258 default=None,
261 default=None,
259 )
262 )
260 coreconfigitem('format', 'maxchainlen',
263 coreconfigitem('format', 'maxchainlen',
261 default=None,
264 default=None,
262 )
265 )
263 coreconfigitem('format', 'obsstore-version',
266 coreconfigitem('format', 'obsstore-version',
264 default=None,
267 default=None,
265 )
268 )
266 coreconfigitem('format', 'usefncache',
269 coreconfigitem('format', 'usefncache',
267 default=True,
270 default=True,
268 )
271 )
269 coreconfigitem('format', 'usegeneraldelta',
272 coreconfigitem('format', 'usegeneraldelta',
270 default=True,
273 default=True,
271 )
274 )
272 coreconfigitem('format', 'usestore',
275 coreconfigitem('format', 'usestore',
273 default=True,
276 default=True,
274 )
277 )
275 coreconfigitem('hostsecurity', 'ciphers',
278 coreconfigitem('hostsecurity', 'ciphers',
276 default=None,
279 default=None,
277 )
280 )
278 coreconfigitem('hostsecurity', 'disabletls10warning',
281 coreconfigitem('hostsecurity', 'disabletls10warning',
279 default=False,
282 default=False,
280 )
283 )
281 coreconfigitem('http_proxy', 'always',
284 coreconfigitem('http_proxy', 'always',
282 default=False,
285 default=False,
283 )
286 )
284 coreconfigitem('http_proxy', 'host',
287 coreconfigitem('http_proxy', 'host',
285 default=None,
288 default=None,
286 )
289 )
287 coreconfigitem('http_proxy', 'no',
290 coreconfigitem('http_proxy', 'no',
288 default=list,
291 default=list,
289 )
292 )
290 coreconfigitem('http_proxy', 'passwd',
293 coreconfigitem('http_proxy', 'passwd',
291 default=None,
294 default=None,
292 )
295 )
293 coreconfigitem('http_proxy', 'user',
296 coreconfigitem('http_proxy', 'user',
294 default=None,
297 default=None,
295 )
298 )
296 coreconfigitem('merge', 'followcopies',
299 coreconfigitem('merge', 'followcopies',
297 default=True,
300 default=True,
298 )
301 )
299 coreconfigitem('pager', 'ignore',
302 coreconfigitem('pager', 'ignore',
300 default=list,
303 default=list,
301 )
304 )
302 coreconfigitem('patch', 'eol',
305 coreconfigitem('patch', 'eol',
303 default='strict',
306 default='strict',
304 )
307 )
305 coreconfigitem('patch', 'fuzz',
308 coreconfigitem('patch', 'fuzz',
306 default=2,
309 default=2,
307 )
310 )
308 coreconfigitem('paths', 'default',
311 coreconfigitem('paths', 'default',
309 default=None,
312 default=None,
310 )
313 )
311 coreconfigitem('paths', 'default-push',
314 coreconfigitem('paths', 'default-push',
312 default=None,
315 default=None,
313 )
316 )
314 coreconfigitem('phases', 'checksubrepos',
317 coreconfigitem('phases', 'checksubrepos',
315 default='follow',
318 default='follow',
316 )
319 )
317 coreconfigitem('phases', 'publish',
320 coreconfigitem('phases', 'publish',
318 default=True,
321 default=True,
319 )
322 )
320 coreconfigitem('profiling', 'enabled',
323 coreconfigitem('profiling', 'enabled',
321 default=False,
324 default=False,
322 )
325 )
323 coreconfigitem('profiling', 'format',
326 coreconfigitem('profiling', 'format',
324 default='text',
327 default='text',
325 )
328 )
326 coreconfigitem('profiling', 'freq',
329 coreconfigitem('profiling', 'freq',
327 default=1000,
330 default=1000,
328 )
331 )
329 coreconfigitem('profiling', 'limit',
332 coreconfigitem('profiling', 'limit',
330 default=30,
333 default=30,
331 )
334 )
332 coreconfigitem('profiling', 'nested',
335 coreconfigitem('profiling', 'nested',
333 default=0,
336 default=0,
334 )
337 )
335 coreconfigitem('profiling', 'sort',
338 coreconfigitem('profiling', 'sort',
336 default='inlinetime',
339 default='inlinetime',
337 )
340 )
338 coreconfigitem('profiling', 'statformat',
341 coreconfigitem('profiling', 'statformat',
339 default='hotpath',
342 default='hotpath',
340 )
343 )
341 coreconfigitem('progress', 'assume-tty',
344 coreconfigitem('progress', 'assume-tty',
342 default=False,
345 default=False,
343 )
346 )
344 coreconfigitem('progress', 'changedelay',
347 coreconfigitem('progress', 'changedelay',
345 default=1,
348 default=1,
346 )
349 )
347 coreconfigitem('progress', 'clear-complete',
350 coreconfigitem('progress', 'clear-complete',
348 default=True,
351 default=True,
349 )
352 )
350 coreconfigitem('progress', 'debug',
353 coreconfigitem('progress', 'debug',
351 default=False,
354 default=False,
352 )
355 )
353 coreconfigitem('progress', 'delay',
356 coreconfigitem('progress', 'delay',
354 default=3,
357 default=3,
355 )
358 )
356 coreconfigitem('progress', 'disable',
359 coreconfigitem('progress', 'disable',
357 default=False,
360 default=False,
358 )
361 )
359 coreconfigitem('progress', 'estimate',
362 coreconfigitem('progress', 'estimate',
360 default=2,
363 default=2,
361 )
364 )
362 coreconfigitem('progress', 'refresh',
365 coreconfigitem('progress', 'refresh',
363 default=0.1,
366 default=0.1,
364 )
367 )
365 coreconfigitem('progress', 'width',
368 coreconfigitem('progress', 'width',
366 default=dynamicdefault,
369 default=dynamicdefault,
367 )
370 )
368 coreconfigitem('push', 'pushvars.server',
371 coreconfigitem('push', 'pushvars.server',
369 default=False,
372 default=False,
370 )
373 )
371 coreconfigitem('server', 'bundle1',
374 coreconfigitem('server', 'bundle1',
372 default=True,
375 default=True,
373 )
376 )
374 coreconfigitem('server', 'bundle1gd',
377 coreconfigitem('server', 'bundle1gd',
375 default=None,
378 default=None,
376 )
379 )
377 coreconfigitem('server', 'compressionengines',
380 coreconfigitem('server', 'compressionengines',
378 default=list,
381 default=list,
379 )
382 )
380 coreconfigitem('server', 'concurrent-push-mode',
383 coreconfigitem('server', 'concurrent-push-mode',
381 default='strict',
384 default='strict',
382 )
385 )
383 coreconfigitem('server', 'disablefullbundle',
386 coreconfigitem('server', 'disablefullbundle',
384 default=False,
387 default=False,
385 )
388 )
386 coreconfigitem('server', 'maxhttpheaderlen',
389 coreconfigitem('server', 'maxhttpheaderlen',
387 default=1024,
390 default=1024,
388 )
391 )
389 coreconfigitem('server', 'preferuncompressed',
392 coreconfigitem('server', 'preferuncompressed',
390 default=False,
393 default=False,
391 )
394 )
392 coreconfigitem('server', 'uncompressed',
395 coreconfigitem('server', 'uncompressed',
393 default=True,
396 default=True,
394 )
397 )
395 coreconfigitem('server', 'uncompressedallowsecret',
398 coreconfigitem('server', 'uncompressedallowsecret',
396 default=False,
399 default=False,
397 )
400 )
398 coreconfigitem('server', 'validate',
401 coreconfigitem('server', 'validate',
399 default=False,
402 default=False,
400 )
403 )
401 coreconfigitem('server', 'zliblevel',
404 coreconfigitem('server', 'zliblevel',
402 default=-1,
405 default=-1,
403 )
406 )
404 coreconfigitem('smtp', 'host',
407 coreconfigitem('smtp', 'host',
405 default=None,
408 default=None,
406 )
409 )
407 coreconfigitem('smtp', 'local_hostname',
410 coreconfigitem('smtp', 'local_hostname',
408 default=None,
411 default=None,
409 )
412 )
410 coreconfigitem('smtp', 'password',
413 coreconfigitem('smtp', 'password',
411 default=None,
414 default=None,
412 )
415 )
413 coreconfigitem('smtp', 'tls',
416 coreconfigitem('smtp', 'tls',
414 default='none',
417 default='none',
415 )
418 )
416 coreconfigitem('smtp', 'username',
419 coreconfigitem('smtp', 'username',
417 default=None,
420 default=None,
418 )
421 )
419 coreconfigitem('sparse', 'missingwarning',
422 coreconfigitem('sparse', 'missingwarning',
420 default=True,
423 default=True,
421 )
424 )
422 coreconfigitem('trusted', 'groups',
425 coreconfigitem('trusted', 'groups',
423 default=list,
426 default=list,
424 )
427 )
425 coreconfigitem('trusted', 'users',
428 coreconfigitem('trusted', 'users',
426 default=list,
429 default=list,
427 )
430 )
428 coreconfigitem('ui', '_usedassubrepo',
431 coreconfigitem('ui', '_usedassubrepo',
429 default=False,
432 default=False,
430 )
433 )
431 coreconfigitem('ui', 'allowemptycommit',
434 coreconfigitem('ui', 'allowemptycommit',
432 default=False,
435 default=False,
433 )
436 )
434 coreconfigitem('ui', 'archivemeta',
437 coreconfigitem('ui', 'archivemeta',
435 default=True,
438 default=True,
436 )
439 )
437 coreconfigitem('ui', 'askusername',
440 coreconfigitem('ui', 'askusername',
438 default=False,
441 default=False,
439 )
442 )
440 coreconfigitem('ui', 'clonebundlefallback',
443 coreconfigitem('ui', 'clonebundlefallback',
441 default=False,
444 default=False,
442 )
445 )
443 coreconfigitem('ui', 'clonebundleprefers',
446 coreconfigitem('ui', 'clonebundleprefers',
444 default=list,
447 default=list,
445 )
448 )
446 coreconfigitem('ui', 'clonebundles',
449 coreconfigitem('ui', 'clonebundles',
447 default=True,
450 default=True,
448 )
451 )
449 coreconfigitem('ui', 'color',
452 coreconfigitem('ui', 'color',
450 default='auto',
453 default='auto',
451 )
454 )
452 coreconfigitem('ui', 'commitsubrepos',
455 coreconfigitem('ui', 'commitsubrepos',
453 default=False,
456 default=False,
454 )
457 )
455 coreconfigitem('ui', 'debug',
458 coreconfigitem('ui', 'debug',
456 default=False,
459 default=False,
457 )
460 )
458 coreconfigitem('ui', 'debugger',
461 coreconfigitem('ui', 'debugger',
459 default=None,
462 default=None,
460 )
463 )
461 coreconfigitem('ui', 'fallbackencoding',
464 coreconfigitem('ui', 'fallbackencoding',
462 default=None,
465 default=None,
463 )
466 )
464 coreconfigitem('ui', 'forcecwd',
467 coreconfigitem('ui', 'forcecwd',
465 default=None,
468 default=None,
466 )
469 )
467 coreconfigitem('ui', 'forcemerge',
470 coreconfigitem('ui', 'forcemerge',
468 default=None,
471 default=None,
469 )
472 )
470 coreconfigitem('ui', 'formatdebug',
473 coreconfigitem('ui', 'formatdebug',
471 default=False,
474 default=False,
472 )
475 )
473 coreconfigitem('ui', 'formatjson',
476 coreconfigitem('ui', 'formatjson',
474 default=False,
477 default=False,
475 )
478 )
476 coreconfigitem('ui', 'formatted',
479 coreconfigitem('ui', 'formatted',
477 default=None,
480 default=None,
478 )
481 )
479 coreconfigitem('ui', 'graphnodetemplate',
482 coreconfigitem('ui', 'graphnodetemplate',
480 default=None,
483 default=None,
481 )
484 )
482 coreconfigitem('ui', 'http2debuglevel',
485 coreconfigitem('ui', 'http2debuglevel',
483 default=None,
486 default=None,
484 )
487 )
485 coreconfigitem('ui', 'interactive',
488 coreconfigitem('ui', 'interactive',
486 default=None,
489 default=None,
487 )
490 )
488 coreconfigitem('ui', 'interface',
491 coreconfigitem('ui', 'interface',
489 default=None,
492 default=None,
490 )
493 )
491 coreconfigitem('ui', 'logblockedtimes',
494 coreconfigitem('ui', 'logblockedtimes',
492 default=False,
495 default=False,
493 )
496 )
494 coreconfigitem('ui', 'logtemplate',
497 coreconfigitem('ui', 'logtemplate',
495 default=None,
498 default=None,
496 )
499 )
497 coreconfigitem('ui', 'merge',
500 coreconfigitem('ui', 'merge',
498 default=None,
501 default=None,
499 )
502 )
500 coreconfigitem('ui', 'mergemarkers',
503 coreconfigitem('ui', 'mergemarkers',
501 default='basic',
504 default='basic',
502 )
505 )
503 coreconfigitem('ui', 'mergemarkertemplate',
506 coreconfigitem('ui', 'mergemarkertemplate',
504 default=('{node|short} '
507 default=('{node|short} '
505 '{ifeq(tags, "tip", "", '
508 '{ifeq(tags, "tip", "", '
506 'ifeq(tags, "", "", "{tags} "))}'
509 'ifeq(tags, "", "", "{tags} "))}'
507 '{if(bookmarks, "{bookmarks} ")}'
510 '{if(bookmarks, "{bookmarks} ")}'
508 '{ifeq(branch, "default", "", "{branch} ")}'
511 '{ifeq(branch, "default", "", "{branch} ")}'
509 '- {author|user}: {desc|firstline}')
512 '- {author|user}: {desc|firstline}')
510 )
513 )
511 coreconfigitem('ui', 'nontty',
514 coreconfigitem('ui', 'nontty',
512 default=False,
515 default=False,
513 )
516 )
514 coreconfigitem('ui', 'origbackuppath',
517 coreconfigitem('ui', 'origbackuppath',
515 default=None,
518 default=None,
516 )
519 )
517 coreconfigitem('ui', 'paginate',
520 coreconfigitem('ui', 'paginate',
518 default=True,
521 default=True,
519 )
522 )
520 coreconfigitem('ui', 'patch',
523 coreconfigitem('ui', 'patch',
521 default=None,
524 default=None,
522 )
525 )
523 coreconfigitem('ui', 'portablefilenames',
526 coreconfigitem('ui', 'portablefilenames',
524 default='warn',
527 default='warn',
525 )
528 )
526 coreconfigitem('ui', 'promptecho',
529 coreconfigitem('ui', 'promptecho',
527 default=False,
530 default=False,
528 )
531 )
529 coreconfigitem('ui', 'quiet',
532 coreconfigitem('ui', 'quiet',
530 default=False,
533 default=False,
531 )
534 )
532 coreconfigitem('ui', 'quietbookmarkmove',
535 coreconfigitem('ui', 'quietbookmarkmove',
533 default=False,
536 default=False,
534 )
537 )
535 coreconfigitem('ui', 'remotecmd',
538 coreconfigitem('ui', 'remotecmd',
536 default='hg',
539 default='hg',
537 )
540 )
538 coreconfigitem('ui', 'report_untrusted',
541 coreconfigitem('ui', 'report_untrusted',
539 default=True,
542 default=True,
540 )
543 )
541 coreconfigitem('ui', 'rollback',
544 coreconfigitem('ui', 'rollback',
542 default=True,
545 default=True,
543 )
546 )
544 coreconfigitem('ui', 'slash',
547 coreconfigitem('ui', 'slash',
545 default=False,
548 default=False,
546 )
549 )
547 coreconfigitem('ui', 'ssh',
550 coreconfigitem('ui', 'ssh',
548 default='ssh',
551 default='ssh',
549 )
552 )
550 coreconfigitem('ui', 'statuscopies',
553 coreconfigitem('ui', 'statuscopies',
551 default=False,
554 default=False,
552 )
555 )
553 coreconfigitem('ui', 'strict',
556 coreconfigitem('ui', 'strict',
554 default=False,
557 default=False,
555 )
558 )
556 coreconfigitem('ui', 'style',
559 coreconfigitem('ui', 'style',
557 default='',
560 default='',
558 )
561 )
559 coreconfigitem('ui', 'supportcontact',
562 coreconfigitem('ui', 'supportcontact',
560 default=None,
563 default=None,
561 )
564 )
562 coreconfigitem('ui', 'textwidth',
565 coreconfigitem('ui', 'textwidth',
563 default=78,
566 default=78,
564 )
567 )
565 coreconfigitem('ui', 'timeout',
568 coreconfigitem('ui', 'timeout',
566 default='600',
569 default='600',
567 )
570 )
568 coreconfigitem('ui', 'traceback',
571 coreconfigitem('ui', 'traceback',
569 default=False,
572 default=False,
570 )
573 )
571 coreconfigitem('ui', 'tweakdefaults',
574 coreconfigitem('ui', 'tweakdefaults',
572 default=False,
575 default=False,
573 )
576 )
574 coreconfigitem('ui', 'usehttp2',
577 coreconfigitem('ui', 'usehttp2',
575 default=False,
578 default=False,
576 )
579 )
577 coreconfigitem('ui', 'username',
580 coreconfigitem('ui', 'username',
578 alias=[('ui', 'user')]
581 alias=[('ui', 'user')]
579 )
582 )
580 coreconfigitem('ui', 'verbose',
583 coreconfigitem('ui', 'verbose',
581 default=False,
584 default=False,
582 )
585 )
583 coreconfigitem('verify', 'skipflags',
586 coreconfigitem('verify', 'skipflags',
584 default=None,
587 default=None,
585 )
588 )
586 coreconfigitem('web', 'accesslog',
589 coreconfigitem('web', 'accesslog',
587 default='-',
590 default='-',
588 )
591 )
589 coreconfigitem('web', 'address',
592 coreconfigitem('web', 'address',
590 default='',
593 default='',
591 )
594 )
592 coreconfigitem('web', 'allow_archive',
595 coreconfigitem('web', 'allow_archive',
593 default=list,
596 default=list,
594 )
597 )
595 coreconfigitem('web', 'allow_read',
598 coreconfigitem('web', 'allow_read',
596 default=list,
599 default=list,
597 )
600 )
598 coreconfigitem('web', 'baseurl',
601 coreconfigitem('web', 'baseurl',
599 default=None,
602 default=None,
600 )
603 )
601 coreconfigitem('web', 'cacerts',
604 coreconfigitem('web', 'cacerts',
602 default=None,
605 default=None,
603 )
606 )
604 coreconfigitem('web', 'certificate',
607 coreconfigitem('web', 'certificate',
605 default=None,
608 default=None,
606 )
609 )
607 coreconfigitem('web', 'collapse',
610 coreconfigitem('web', 'collapse',
608 default=False,
611 default=False,
609 )
612 )
610 coreconfigitem('web', 'csp',
613 coreconfigitem('web', 'csp',
611 default=None,
614 default=None,
612 )
615 )
613 coreconfigitem('web', 'deny_read',
616 coreconfigitem('web', 'deny_read',
614 default=list,
617 default=list,
615 )
618 )
616 coreconfigitem('web', 'descend',
619 coreconfigitem('web', 'descend',
617 default=True,
620 default=True,
618 )
621 )
619 coreconfigitem('web', 'description',
622 coreconfigitem('web', 'description',
620 default="",
623 default="",
621 )
624 )
622 coreconfigitem('web', 'encoding',
625 coreconfigitem('web', 'encoding',
623 default=lambda: encoding.encoding,
626 default=lambda: encoding.encoding,
624 )
627 )
625 coreconfigitem('web', 'errorlog',
628 coreconfigitem('web', 'errorlog',
626 default='-',
629 default='-',
627 )
630 )
628 coreconfigitem('web', 'ipv6',
631 coreconfigitem('web', 'ipv6',
629 default=False,
632 default=False,
630 )
633 )
631 coreconfigitem('web', 'port',
634 coreconfigitem('web', 'port',
632 default=8000,
635 default=8000,
633 )
636 )
634 coreconfigitem('web', 'prefix',
637 coreconfigitem('web', 'prefix',
635 default='',
638 default='',
636 )
639 )
637 coreconfigitem('web', 'refreshinterval',
640 coreconfigitem('web', 'refreshinterval',
638 default=20,
641 default=20,
639 )
642 )
640 coreconfigitem('web', 'stripes',
643 coreconfigitem('web', 'stripes',
641 default=1,
644 default=1,
642 )
645 )
643 coreconfigitem('web', 'style',
646 coreconfigitem('web', 'style',
644 default='paper',
647 default='paper',
645 )
648 )
646 coreconfigitem('web', 'templates',
649 coreconfigitem('web', 'templates',
647 default=None,
650 default=None,
648 )
651 )
649 coreconfigitem('worker', 'backgroundclose',
652 coreconfigitem('worker', 'backgroundclose',
650 default=dynamicdefault,
653 default=dynamicdefault,
651 )
654 )
652 # Windows defaults to a limit of 512 open files. A buffer of 128
655 # Windows defaults to a limit of 512 open files. A buffer of 128
653 # should give us enough headway.
656 # should give us enough headway.
654 coreconfigitem('worker', 'backgroundclosemaxqueue',
657 coreconfigitem('worker', 'backgroundclosemaxqueue',
655 default=384,
658 default=384,
656 )
659 )
657 coreconfigitem('worker', 'backgroundcloseminfilecount',
660 coreconfigitem('worker', 'backgroundcloseminfilecount',
658 default=2048,
661 default=2048,
659 )
662 )
660 coreconfigitem('worker', 'backgroundclosethreadcount',
663 coreconfigitem('worker', 'backgroundclosethreadcount',
661 default=4,
664 default=4,
662 )
665 )
663 coreconfigitem('worker', 'numcpus',
666 coreconfigitem('worker', 'numcpus',
664 default=None,
667 default=None,
665 )
668 )
@@ -1,862 +1,867 b''
1 # copies.py - copy detection for Mercurial
1 # copies.py - copy detection for Mercurial
2 #
2 #
3 # Copyright 2008 Matt Mackall <mpm@selenic.com>
3 # Copyright 2008 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import collections
10 import collections
11 import heapq
11 import heapq
12 import os
12 import os
13
13
14 from . import (
14 from . import (
15 match as matchmod,
15 match as matchmod,
16 node,
16 node,
17 pathutil,
17 pathutil,
18 phases,
18 phases,
19 scmutil,
19 scmutil,
20 util,
20 util,
21 )
21 )
22
22
23 def _findlimit(repo, a, b):
23 def _findlimit(repo, a, b):
24 """
24 """
25 Find the last revision that needs to be checked to ensure that a full
25 Find the last revision that needs to be checked to ensure that a full
26 transitive closure for file copies can be properly calculated.
26 transitive closure for file copies can be properly calculated.
27 Generally, this means finding the earliest revision number that's an
27 Generally, this means finding the earliest revision number that's an
28 ancestor of a or b but not both, except when a or b is a direct descendent
28 ancestor of a or b but not both, except when a or b is a direct descendent
29 of the other, in which case we can return the minimum revnum of a and b.
29 of the other, in which case we can return the minimum revnum of a and b.
30 None if no such revision exists.
30 None if no such revision exists.
31 """
31 """
32
32
33 # basic idea:
33 # basic idea:
34 # - mark a and b with different sides
34 # - mark a and b with different sides
35 # - if a parent's children are all on the same side, the parent is
35 # - if a parent's children are all on the same side, the parent is
36 # on that side, otherwise it is on no side
36 # on that side, otherwise it is on no side
37 # - walk the graph in topological order with the help of a heap;
37 # - walk the graph in topological order with the help of a heap;
38 # - add unseen parents to side map
38 # - add unseen parents to side map
39 # - clear side of any parent that has children on different sides
39 # - clear side of any parent that has children on different sides
40 # - track number of interesting revs that might still be on a side
40 # - track number of interesting revs that might still be on a side
41 # - track the lowest interesting rev seen
41 # - track the lowest interesting rev seen
42 # - quit when interesting revs is zero
42 # - quit when interesting revs is zero
43
43
44 cl = repo.changelog
44 cl = repo.changelog
45 working = len(cl) # pseudo rev for the working directory
45 working = len(cl) # pseudo rev for the working directory
46 if a is None:
46 if a is None:
47 a = working
47 a = working
48 if b is None:
48 if b is None:
49 b = working
49 b = working
50
50
51 side = {a: -1, b: 1}
51 side = {a: -1, b: 1}
52 visit = [-a, -b]
52 visit = [-a, -b]
53 heapq.heapify(visit)
53 heapq.heapify(visit)
54 interesting = len(visit)
54 interesting = len(visit)
55 hascommonancestor = False
55 hascommonancestor = False
56 limit = working
56 limit = working
57
57
58 while interesting:
58 while interesting:
59 r = -heapq.heappop(visit)
59 r = -heapq.heappop(visit)
60 if r == working:
60 if r == working:
61 parents = [cl.rev(p) for p in repo.dirstate.parents()]
61 parents = [cl.rev(p) for p in repo.dirstate.parents()]
62 else:
62 else:
63 parents = cl.parentrevs(r)
63 parents = cl.parentrevs(r)
64 for p in parents:
64 for p in parents:
65 if p < 0:
65 if p < 0:
66 continue
66 continue
67 if p not in side:
67 if p not in side:
68 # first time we see p; add it to visit
68 # first time we see p; add it to visit
69 side[p] = side[r]
69 side[p] = side[r]
70 if side[p]:
70 if side[p]:
71 interesting += 1
71 interesting += 1
72 heapq.heappush(visit, -p)
72 heapq.heappush(visit, -p)
73 elif side[p] and side[p] != side[r]:
73 elif side[p] and side[p] != side[r]:
74 # p was interesting but now we know better
74 # p was interesting but now we know better
75 side[p] = 0
75 side[p] = 0
76 interesting -= 1
76 interesting -= 1
77 hascommonancestor = True
77 hascommonancestor = True
78 if side[r]:
78 if side[r]:
79 limit = r # lowest rev visited
79 limit = r # lowest rev visited
80 interesting -= 1
80 interesting -= 1
81
81
82 if not hascommonancestor:
82 if not hascommonancestor:
83 return None
83 return None
84
84
85 # Consider the following flow (see test-commit-amend.t under issue4405):
85 # Consider the following flow (see test-commit-amend.t under issue4405):
86 # 1/ File 'a0' committed
86 # 1/ File 'a0' committed
87 # 2/ File renamed from 'a0' to 'a1' in a new commit (call it 'a1')
87 # 2/ File renamed from 'a0' to 'a1' in a new commit (call it 'a1')
88 # 3/ Move back to first commit
88 # 3/ Move back to first commit
89 # 4/ Create a new commit via revert to contents of 'a1' (call it 'a1-amend')
89 # 4/ Create a new commit via revert to contents of 'a1' (call it 'a1-amend')
90 # 5/ Rename file from 'a1' to 'a2' and commit --amend 'a1-msg'
90 # 5/ Rename file from 'a1' to 'a2' and commit --amend 'a1-msg'
91 #
91 #
92 # During the amend in step five, we will be in this state:
92 # During the amend in step five, we will be in this state:
93 #
93 #
94 # @ 3 temporary amend commit for a1-amend
94 # @ 3 temporary amend commit for a1-amend
95 # |
95 # |
96 # o 2 a1-amend
96 # o 2 a1-amend
97 # |
97 # |
98 # | o 1 a1
98 # | o 1 a1
99 # |/
99 # |/
100 # o 0 a0
100 # o 0 a0
101 #
101 #
102 # When _findlimit is called, a and b are revs 3 and 0, so limit will be 2,
102 # When _findlimit is called, a and b are revs 3 and 0, so limit will be 2,
103 # yet the filelog has the copy information in rev 1 and we will not look
103 # yet the filelog has the copy information in rev 1 and we will not look
104 # back far enough unless we also look at the a and b as candidates.
104 # back far enough unless we also look at the a and b as candidates.
105 # This only occurs when a is a descendent of b or visa-versa.
105 # This only occurs when a is a descendent of b or visa-versa.
106 return min(limit, a, b)
106 return min(limit, a, b)
107
107
108 def _chain(src, dst, a, b):
108 def _chain(src, dst, a, b):
109 '''chain two sets of copies a->b'''
109 '''chain two sets of copies a->b'''
110 t = a.copy()
110 t = a.copy()
111 for k, v in b.iteritems():
111 for k, v in b.iteritems():
112 if v in t:
112 if v in t:
113 # found a chain
113 # found a chain
114 if t[v] != k:
114 if t[v] != k:
115 # file wasn't renamed back to itself
115 # file wasn't renamed back to itself
116 t[k] = t[v]
116 t[k] = t[v]
117 if v not in dst:
117 if v not in dst:
118 # chain was a rename, not a copy
118 # chain was a rename, not a copy
119 del t[v]
119 del t[v]
120 if v in src:
120 if v in src:
121 # file is a copy of an existing file
121 # file is a copy of an existing file
122 t[k] = v
122 t[k] = v
123
123
124 # remove criss-crossed copies
124 # remove criss-crossed copies
125 for k, v in t.items():
125 for k, v in t.items():
126 if k in src and v in dst:
126 if k in src and v in dst:
127 del t[k]
127 del t[k]
128
128
129 return t
129 return t
130
130
131 def _tracefile(fctx, am, limit=-1):
131 def _tracefile(fctx, am, limit=-1):
132 '''return file context that is the ancestor of fctx present in ancestor
132 '''return file context that is the ancestor of fctx present in ancestor
133 manifest am, stopping after the first ancestor lower than limit'''
133 manifest am, stopping after the first ancestor lower than limit'''
134
134
135 for f in fctx.ancestors():
135 for f in fctx.ancestors():
136 if am.get(f.path(), None) == f.filenode():
136 if am.get(f.path(), None) == f.filenode():
137 return f
137 return f
138 if limit >= 0 and f.linkrev() < limit and f.rev() < limit:
138 if limit >= 0 and f.linkrev() < limit and f.rev() < limit:
139 return None
139 return None
140
140
141 def _dirstatecopies(d):
141 def _dirstatecopies(d):
142 ds = d._repo.dirstate
142 ds = d._repo.dirstate
143 c = ds.copies().copy()
143 c = ds.copies().copy()
144 for k in c.keys():
144 for k in c.keys():
145 if ds[k] not in 'anm':
145 if ds[k] not in 'anm':
146 del c[k]
146 del c[k]
147 return c
147 return c
148
148
149 def _computeforwardmissing(a, b, match=None):
149 def _computeforwardmissing(a, b, match=None):
150 """Computes which files are in b but not a.
150 """Computes which files are in b but not a.
151 This is its own function so extensions can easily wrap this call to see what
151 This is its own function so extensions can easily wrap this call to see what
152 files _forwardcopies is about to process.
152 files _forwardcopies is about to process.
153 """
153 """
154 ma = a.manifest()
154 ma = a.manifest()
155 mb = b.manifest()
155 mb = b.manifest()
156 return mb.filesnotin(ma, match=match)
156 return mb.filesnotin(ma, match=match)
157
157
158 def _forwardcopies(a, b, match=None):
158 def _forwardcopies(a, b, match=None):
159 '''find {dst@b: src@a} copy mapping where a is an ancestor of b'''
159 '''find {dst@b: src@a} copy mapping where a is an ancestor of b'''
160
160
161 # check for working copy
161 # check for working copy
162 w = None
162 w = None
163 if b.rev() is None:
163 if b.rev() is None:
164 w = b
164 w = b
165 b = w.p1()
165 b = w.p1()
166 if a == b:
166 if a == b:
167 # short-circuit to avoid issues with merge states
167 # short-circuit to avoid issues with merge states
168 return _dirstatecopies(w)
168 return _dirstatecopies(w)
169
169
170 # files might have to be traced back to the fctx parent of the last
170 # files might have to be traced back to the fctx parent of the last
171 # one-side-only changeset, but not further back than that
171 # one-side-only changeset, but not further back than that
172 limit = _findlimit(a._repo, a.rev(), b.rev())
172 limit = _findlimit(a._repo, a.rev(), b.rev())
173 if limit is None:
173 if limit is None:
174 limit = -1
174 limit = -1
175 am = a.manifest()
175 am = a.manifest()
176
176
177 # find where new files came from
177 # find where new files came from
178 # we currently don't try to find where old files went, too expensive
178 # we currently don't try to find where old files went, too expensive
179 # this means we can miss a case like 'hg rm b; hg cp a b'
179 # this means we can miss a case like 'hg rm b; hg cp a b'
180 cm = {}
180 cm = {}
181
181
182 # Computing the forward missing is quite expensive on large manifests, since
182 # Computing the forward missing is quite expensive on large manifests, since
183 # it compares the entire manifests. We can optimize it in the common use
183 # it compares the entire manifests. We can optimize it in the common use
184 # case of computing what copies are in a commit versus its parent (like
184 # case of computing what copies are in a commit versus its parent (like
185 # during a rebase or histedit). Note, we exclude merge commits from this
185 # during a rebase or histedit). Note, we exclude merge commits from this
186 # optimization, since the ctx.files() for a merge commit is not correct for
186 # optimization, since the ctx.files() for a merge commit is not correct for
187 # this comparison.
187 # this comparison.
188 forwardmissingmatch = match
188 forwardmissingmatch = match
189 if b.p1() == a and b.p2().node() == node.nullid:
189 if b.p1() == a and b.p2().node() == node.nullid:
190 filesmatcher = scmutil.matchfiles(a._repo, b.files())
190 filesmatcher = scmutil.matchfiles(a._repo, b.files())
191 forwardmissingmatch = matchmod.intersectmatchers(match, filesmatcher)
191 forwardmissingmatch = matchmod.intersectmatchers(match, filesmatcher)
192 missing = _computeforwardmissing(a, b, match=forwardmissingmatch)
192 missing = _computeforwardmissing(a, b, match=forwardmissingmatch)
193
193
194 ancestrycontext = a._repo.changelog.ancestors([b.rev()], inclusive=True)
194 ancestrycontext = a._repo.changelog.ancestors([b.rev()], inclusive=True)
195 for f in missing:
195 for f in missing:
196 fctx = b[f]
196 fctx = b[f]
197 fctx._ancestrycontext = ancestrycontext
197 fctx._ancestrycontext = ancestrycontext
198 ofctx = _tracefile(fctx, am, limit)
198 ofctx = _tracefile(fctx, am, limit)
199 if ofctx:
199 if ofctx:
200 cm[f] = ofctx.path()
200 cm[f] = ofctx.path()
201
201
202 # combine copies from dirstate if necessary
202 # combine copies from dirstate if necessary
203 if w is not None:
203 if w is not None:
204 cm = _chain(a, w, cm, _dirstatecopies(w))
204 cm = _chain(a, w, cm, _dirstatecopies(w))
205
205
206 return cm
206 return cm
207
207
208 def _backwardrenames(a, b):
208 def _backwardrenames(a, b):
209 if a._repo.ui.config('experimental', 'copytrace') == 'off':
209 if a._repo.ui.config('experimental', 'copytrace') == 'off':
210 return {}
210 return {}
211
211
212 # Even though we're not taking copies into account, 1:n rename situations
212 # Even though we're not taking copies into account, 1:n rename situations
213 # can still exist (e.g. hg cp a b; hg mv a c). In those cases we
213 # can still exist (e.g. hg cp a b; hg mv a c). In those cases we
214 # arbitrarily pick one of the renames.
214 # arbitrarily pick one of the renames.
215 f = _forwardcopies(b, a)
215 f = _forwardcopies(b, a)
216 r = {}
216 r = {}
217 for k, v in sorted(f.iteritems()):
217 for k, v in sorted(f.iteritems()):
218 # remove copies
218 # remove copies
219 if v in a:
219 if v in a:
220 continue
220 continue
221 r[v] = k
221 r[v] = k
222 return r
222 return r
223
223
224 def pathcopies(x, y, match=None):
224 def pathcopies(x, y, match=None):
225 '''find {dst@y: src@x} copy mapping for directed compare'''
225 '''find {dst@y: src@x} copy mapping for directed compare'''
226 if x == y or not x or not y:
226 if x == y or not x or not y:
227 return {}
227 return {}
228 a = y.ancestor(x)
228 a = y.ancestor(x)
229 if a == x:
229 if a == x:
230 return _forwardcopies(x, y, match=match)
230 return _forwardcopies(x, y, match=match)
231 if a == y:
231 if a == y:
232 return _backwardrenames(x, y)
232 return _backwardrenames(x, y)
233 return _chain(x, y, _backwardrenames(x, a),
233 return _chain(x, y, _backwardrenames(x, a),
234 _forwardcopies(a, y, match=match))
234 _forwardcopies(a, y, match=match))
235
235
236 def _computenonoverlap(repo, c1, c2, addedinm1, addedinm2, baselabel=''):
236 def _computenonoverlap(repo, c1, c2, addedinm1, addedinm2, baselabel=''):
237 """Computes, based on addedinm1 and addedinm2, the files exclusive to c1
237 """Computes, based on addedinm1 and addedinm2, the files exclusive to c1
238 and c2. This is its own function so extensions can easily wrap this call
238 and c2. This is its own function so extensions can easily wrap this call
239 to see what files mergecopies is about to process.
239 to see what files mergecopies is about to process.
240
240
241 Even though c1 and c2 are not used in this function, they are useful in
241 Even though c1 and c2 are not used in this function, they are useful in
242 other extensions for being able to read the file nodes of the changed files.
242 other extensions for being able to read the file nodes of the changed files.
243
243
244 "baselabel" can be passed to help distinguish the multiple computations
244 "baselabel" can be passed to help distinguish the multiple computations
245 done in the graft case.
245 done in the graft case.
246 """
246 """
247 u1 = sorted(addedinm1 - addedinm2)
247 u1 = sorted(addedinm1 - addedinm2)
248 u2 = sorted(addedinm2 - addedinm1)
248 u2 = sorted(addedinm2 - addedinm1)
249
249
250 header = " unmatched files in %s"
250 header = " unmatched files in %s"
251 if baselabel:
251 if baselabel:
252 header += ' (from %s)' % baselabel
252 header += ' (from %s)' % baselabel
253 if u1:
253 if u1:
254 repo.ui.debug("%s:\n %s\n" % (header % 'local', "\n ".join(u1)))
254 repo.ui.debug("%s:\n %s\n" % (header % 'local', "\n ".join(u1)))
255 if u2:
255 if u2:
256 repo.ui.debug("%s:\n %s\n" % (header % 'other', "\n ".join(u2)))
256 repo.ui.debug("%s:\n %s\n" % (header % 'other', "\n ".join(u2)))
257 return u1, u2
257 return u1, u2
258
258
259 def _makegetfctx(ctx):
259 def _makegetfctx(ctx):
260 """return a 'getfctx' function suitable for _checkcopies usage
260 """return a 'getfctx' function suitable for _checkcopies usage
261
261
262 We have to re-setup the function building 'filectx' for each
262 We have to re-setup the function building 'filectx' for each
263 '_checkcopies' to ensure the linkrev adjustment is properly setup for
263 '_checkcopies' to ensure the linkrev adjustment is properly setup for
264 each. Linkrev adjustment is important to avoid bug in rename
264 each. Linkrev adjustment is important to avoid bug in rename
265 detection. Moreover, having a proper '_ancestrycontext' setup ensures
265 detection. Moreover, having a proper '_ancestrycontext' setup ensures
266 the performance impact of this adjustment is kept limited. Without it,
266 the performance impact of this adjustment is kept limited. Without it,
267 each file could do a full dag traversal making the time complexity of
267 each file could do a full dag traversal making the time complexity of
268 the operation explode (see issue4537).
268 the operation explode (see issue4537).
269
269
270 This function exists here mostly to limit the impact on stable. Feel
270 This function exists here mostly to limit the impact on stable. Feel
271 free to refactor on default.
271 free to refactor on default.
272 """
272 """
273 rev = ctx.rev()
273 rev = ctx.rev()
274 repo = ctx._repo
274 repo = ctx._repo
275 ac = getattr(ctx, '_ancestrycontext', None)
275 ac = getattr(ctx, '_ancestrycontext', None)
276 if ac is None:
276 if ac is None:
277 revs = [rev]
277 revs = [rev]
278 if rev is None:
278 if rev is None:
279 revs = [p.rev() for p in ctx.parents()]
279 revs = [p.rev() for p in ctx.parents()]
280 ac = repo.changelog.ancestors(revs, inclusive=True)
280 ac = repo.changelog.ancestors(revs, inclusive=True)
281 ctx._ancestrycontext = ac
281 ctx._ancestrycontext = ac
282 def makectx(f, n):
282 def makectx(f, n):
283 if n in node.wdirnodes: # in a working context?
283 if n in node.wdirnodes: # in a working context?
284 if ctx.rev() is None:
284 if ctx.rev() is None:
285 return ctx.filectx(f)
285 return ctx.filectx(f)
286 return repo[None][f]
286 return repo[None][f]
287 fctx = repo.filectx(f, fileid=n)
287 fctx = repo.filectx(f, fileid=n)
288 # setup only needed for filectx not create from a changectx
288 # setup only needed for filectx not create from a changectx
289 fctx._ancestrycontext = ac
289 fctx._ancestrycontext = ac
290 fctx._descendantrev = rev
290 fctx._descendantrev = rev
291 return fctx
291 return fctx
292 return util.lrucachefunc(makectx)
292 return util.lrucachefunc(makectx)
293
293
294 def _combinecopies(copyfrom, copyto, finalcopy, diverge, incompletediverge):
294 def _combinecopies(copyfrom, copyto, finalcopy, diverge, incompletediverge):
295 """combine partial copy paths"""
295 """combine partial copy paths"""
296 remainder = {}
296 remainder = {}
297 for f in copyfrom:
297 for f in copyfrom:
298 if f in copyto:
298 if f in copyto:
299 finalcopy[copyto[f]] = copyfrom[f]
299 finalcopy[copyto[f]] = copyfrom[f]
300 del copyto[f]
300 del copyto[f]
301 for f in incompletediverge:
301 for f in incompletediverge:
302 assert f not in diverge
302 assert f not in diverge
303 ic = incompletediverge[f]
303 ic = incompletediverge[f]
304 if ic[0] in copyto:
304 if ic[0] in copyto:
305 diverge[f] = [copyto[ic[0]], ic[1]]
305 diverge[f] = [copyto[ic[0]], ic[1]]
306 else:
306 else:
307 remainder[f] = ic
307 remainder[f] = ic
308 return remainder
308 return remainder
309
309
310 def mergecopies(repo, c1, c2, base):
310 def mergecopies(repo, c1, c2, base):
311 """
311 """
312 The function calling different copytracing algorithms on the basis of config
312 The function calling different copytracing algorithms on the basis of config
313 which find moves and copies between context c1 and c2 that are relevant for
313 which find moves and copies between context c1 and c2 that are relevant for
314 merging. 'base' will be used as the merge base.
314 merging. 'base' will be used as the merge base.
315
315
316 Copytracing is used in commands like rebase, merge, unshelve, etc to merge
316 Copytracing is used in commands like rebase, merge, unshelve, etc to merge
317 files that were moved/ copied in one merge parent and modified in another.
317 files that were moved/ copied in one merge parent and modified in another.
318 For example:
318 For example:
319
319
320 o ---> 4 another commit
320 o ---> 4 another commit
321 |
321 |
322 | o ---> 3 commit that modifies a.txt
322 | o ---> 3 commit that modifies a.txt
323 | /
323 | /
324 o / ---> 2 commit that moves a.txt to b.txt
324 o / ---> 2 commit that moves a.txt to b.txt
325 |/
325 |/
326 o ---> 1 merge base
326 o ---> 1 merge base
327
327
328 If we try to rebase revision 3 on revision 4, since there is no a.txt in
328 If we try to rebase revision 3 on revision 4, since there is no a.txt in
329 revision 4, and if user have copytrace disabled, we prints the following
329 revision 4, and if user have copytrace disabled, we prints the following
330 message:
330 message:
331
331
332 ```other changed <file> which local deleted```
332 ```other changed <file> which local deleted```
333
333
334 Returns five dicts: "copy", "movewithdir", "diverge", "renamedelete" and
334 Returns five dicts: "copy", "movewithdir", "diverge", "renamedelete" and
335 "dirmove".
335 "dirmove".
336
336
337 "copy" is a mapping from destination name -> source name,
337 "copy" is a mapping from destination name -> source name,
338 where source is in c1 and destination is in c2 or vice-versa.
338 where source is in c1 and destination is in c2 or vice-versa.
339
339
340 "movewithdir" is a mapping from source name -> destination name,
340 "movewithdir" is a mapping from source name -> destination name,
341 where the file at source present in one context but not the other
341 where the file at source present in one context but not the other
342 needs to be moved to destination by the merge process, because the
342 needs to be moved to destination by the merge process, because the
343 other context moved the directory it is in.
343 other context moved the directory it is in.
344
344
345 "diverge" is a mapping of source name -> list of destination names
345 "diverge" is a mapping of source name -> list of destination names
346 for divergent renames.
346 for divergent renames.
347
347
348 "renamedelete" is a mapping of source name -> list of destination
348 "renamedelete" is a mapping of source name -> list of destination
349 names for files deleted in c1 that were renamed in c2 or vice-versa.
349 names for files deleted in c1 that were renamed in c2 or vice-versa.
350
350
351 "dirmove" is a mapping of detected source dir -> destination dir renames.
351 "dirmove" is a mapping of detected source dir -> destination dir renames.
352 This is needed for handling changes to new files previously grafted into
352 This is needed for handling changes to new files previously grafted into
353 renamed directories.
353 renamed directories.
354 """
354 """
355 # avoid silly behavior for update from empty dir
355 # avoid silly behavior for update from empty dir
356 if not c1 or not c2 or c1 == c2:
356 if not c1 or not c2 or c1 == c2:
357 return {}, {}, {}, {}, {}
357 return {}, {}, {}, {}, {}
358
358
359 # avoid silly behavior for parent -> working dir
359 # avoid silly behavior for parent -> working dir
360 if c2.node() is None and c1.node() == repo.dirstate.p1():
360 if c2.node() is None and c1.node() == repo.dirstate.p1():
361 return repo.dirstate.copies(), {}, {}, {}, {}
361 return repo.dirstate.copies(), {}, {}, {}, {}
362
362
363 copytracing = repo.ui.config('experimental', 'copytrace')
363 copytracing = repo.ui.config('experimental', 'copytrace')
364
364
365 # Copy trace disabling is explicitly below the node == p1 logic above
365 # Copy trace disabling is explicitly below the node == p1 logic above
366 # because the logic above is required for a simple copy to be kept across a
366 # because the logic above is required for a simple copy to be kept across a
367 # rebase.
367 # rebase.
368 if copytracing == 'off':
368 if copytracing == 'off':
369 return {}, {}, {}, {}, {}
369 return {}, {}, {}, {}, {}
370 elif copytracing == 'heuristics':
370 elif copytracing == 'heuristics':
371 # Do full copytracing if only drafts are involved as that will be fast
371 # Do full copytracing if only drafts are involved as that will be fast
372 # enough and will also cover the copies which can be missed by
372 # enough and will also cover the copies which can be missed by
373 # heuristics
373 # heuristics
374 if _isfullcopytraceable(c1, base):
374 if _isfullcopytraceable(repo, c1, base):
375 return _fullcopytracing(repo, c1, c2, base)
375 return _fullcopytracing(repo, c1, c2, base)
376 return _heuristicscopytracing(repo, c1, c2, base)
376 return _heuristicscopytracing(repo, c1, c2, base)
377 else:
377 else:
378 return _fullcopytracing(repo, c1, c2, base)
378 return _fullcopytracing(repo, c1, c2, base)
379
379
380 def _isfullcopytraceable(c1, base):
380 def _isfullcopytraceable(repo, c1, base):
381 """ Checks that if base, source and destination are all draft branches, if
381 """ Checks that if base, source and destination are all draft branches, if
382 yes let's use the full copytrace algorithm for increased capabilities since
382 yes let's use the full copytrace algorithm for increased capabilities since
383 it will be fast enough.
383 it will be fast enough.
384 """
384 """
385 if c1.rev() is None:
386 c1 = c1.p1()
385
387
386 nonpublicphases = set([phases.draft, phases.secret])
388 nonpublicphases = set([phases.draft, phases.secret])
387
389
388 if (c1.phase() in nonpublicphases) and (base.phase() in nonpublicphases):
390 if (c1.phase() in nonpublicphases) and (base.phase() in nonpublicphases):
389 return True
391 sourcecommitlimit = repo.ui.configint('experimental',
392 'copytrace.sourcecommitlimit')
393 commits = len(repo.revs('%d::%d', base.rev(), c1.rev()))
394 return commits < sourcecommitlimit
390 return False
395 return False
391
396
392 def _fullcopytracing(repo, c1, c2, base):
397 def _fullcopytracing(repo, c1, c2, base):
393 """ The full copytracing algorithm which finds all the new files that were
398 """ The full copytracing algorithm which finds all the new files that were
394 added from merge base up to the top commit and for each file it checks if
399 added from merge base up to the top commit and for each file it checks if
395 this file was copied from another file.
400 this file was copied from another file.
396
401
397 This is pretty slow when a lot of changesets are involved but will track all
402 This is pretty slow when a lot of changesets are involved but will track all
398 the copies.
403 the copies.
399 """
404 """
400 # In certain scenarios (e.g. graft, update or rebase), base can be
405 # In certain scenarios (e.g. graft, update or rebase), base can be
401 # overridden We still need to know a real common ancestor in this case We
406 # overridden We still need to know a real common ancestor in this case We
402 # can't just compute _c1.ancestor(_c2) and compare it to ca, because there
407 # can't just compute _c1.ancestor(_c2) and compare it to ca, because there
403 # can be multiple common ancestors, e.g. in case of bidmerge. Because our
408 # can be multiple common ancestors, e.g. in case of bidmerge. Because our
404 # caller may not know if the revision passed in lieu of the CA is a genuine
409 # caller may not know if the revision passed in lieu of the CA is a genuine
405 # common ancestor or not without explicitly checking it, it's better to
410 # common ancestor or not without explicitly checking it, it's better to
406 # determine that here.
411 # determine that here.
407 #
412 #
408 # base.descendant(wc) and base.descendant(base) are False, work around that
413 # base.descendant(wc) and base.descendant(base) are False, work around that
409 _c1 = c1.p1() if c1.rev() is None else c1
414 _c1 = c1.p1() if c1.rev() is None else c1
410 _c2 = c2.p1() if c2.rev() is None else c2
415 _c2 = c2.p1() if c2.rev() is None else c2
411 # an endpoint is "dirty" if it isn't a descendant of the merge base
416 # an endpoint is "dirty" if it isn't a descendant of the merge base
412 # if we have a dirty endpoint, we need to trigger graft logic, and also
417 # if we have a dirty endpoint, we need to trigger graft logic, and also
413 # keep track of which endpoint is dirty
418 # keep track of which endpoint is dirty
414 dirtyc1 = not (base == _c1 or base.descendant(_c1))
419 dirtyc1 = not (base == _c1 or base.descendant(_c1))
415 dirtyc2 = not (base == _c2 or base.descendant(_c2))
420 dirtyc2 = not (base == _c2 or base.descendant(_c2))
416 graft = dirtyc1 or dirtyc2
421 graft = dirtyc1 or dirtyc2
417 tca = base
422 tca = base
418 if graft:
423 if graft:
419 tca = _c1.ancestor(_c2)
424 tca = _c1.ancestor(_c2)
420
425
421 limit = _findlimit(repo, c1.rev(), c2.rev())
426 limit = _findlimit(repo, c1.rev(), c2.rev())
422 if limit is None:
427 if limit is None:
423 # no common ancestor, no copies
428 # no common ancestor, no copies
424 return {}, {}, {}, {}, {}
429 return {}, {}, {}, {}, {}
425 repo.ui.debug(" searching for copies back to rev %d\n" % limit)
430 repo.ui.debug(" searching for copies back to rev %d\n" % limit)
426
431
427 m1 = c1.manifest()
432 m1 = c1.manifest()
428 m2 = c2.manifest()
433 m2 = c2.manifest()
429 mb = base.manifest()
434 mb = base.manifest()
430
435
431 # gather data from _checkcopies:
436 # gather data from _checkcopies:
432 # - diverge = record all diverges in this dict
437 # - diverge = record all diverges in this dict
433 # - copy = record all non-divergent copies in this dict
438 # - copy = record all non-divergent copies in this dict
434 # - fullcopy = record all copies in this dict
439 # - fullcopy = record all copies in this dict
435 # - incomplete = record non-divergent partial copies here
440 # - incomplete = record non-divergent partial copies here
436 # - incompletediverge = record divergent partial copies here
441 # - incompletediverge = record divergent partial copies here
437 diverge = {} # divergence data is shared
442 diverge = {} # divergence data is shared
438 incompletediverge = {}
443 incompletediverge = {}
439 data1 = {'copy': {},
444 data1 = {'copy': {},
440 'fullcopy': {},
445 'fullcopy': {},
441 'incomplete': {},
446 'incomplete': {},
442 'diverge': diverge,
447 'diverge': diverge,
443 'incompletediverge': incompletediverge,
448 'incompletediverge': incompletediverge,
444 }
449 }
445 data2 = {'copy': {},
450 data2 = {'copy': {},
446 'fullcopy': {},
451 'fullcopy': {},
447 'incomplete': {},
452 'incomplete': {},
448 'diverge': diverge,
453 'diverge': diverge,
449 'incompletediverge': incompletediverge,
454 'incompletediverge': incompletediverge,
450 }
455 }
451
456
452 # find interesting file sets from manifests
457 # find interesting file sets from manifests
453 addedinm1 = m1.filesnotin(mb)
458 addedinm1 = m1.filesnotin(mb)
454 addedinm2 = m2.filesnotin(mb)
459 addedinm2 = m2.filesnotin(mb)
455 bothnew = sorted(addedinm1 & addedinm2)
460 bothnew = sorted(addedinm1 & addedinm2)
456 if tca == base:
461 if tca == base:
457 # unmatched file from base
462 # unmatched file from base
458 u1r, u2r = _computenonoverlap(repo, c1, c2, addedinm1, addedinm2)
463 u1r, u2r = _computenonoverlap(repo, c1, c2, addedinm1, addedinm2)
459 u1u, u2u = u1r, u2r
464 u1u, u2u = u1r, u2r
460 else:
465 else:
461 # unmatched file from base (DAG rotation in the graft case)
466 # unmatched file from base (DAG rotation in the graft case)
462 u1r, u2r = _computenonoverlap(repo, c1, c2, addedinm1, addedinm2,
467 u1r, u2r = _computenonoverlap(repo, c1, c2, addedinm1, addedinm2,
463 baselabel='base')
468 baselabel='base')
464 # unmatched file from topological common ancestors (no DAG rotation)
469 # unmatched file from topological common ancestors (no DAG rotation)
465 # need to recompute this for directory move handling when grafting
470 # need to recompute this for directory move handling when grafting
466 mta = tca.manifest()
471 mta = tca.manifest()
467 u1u, u2u = _computenonoverlap(repo, c1, c2, m1.filesnotin(mta),
472 u1u, u2u = _computenonoverlap(repo, c1, c2, m1.filesnotin(mta),
468 m2.filesnotin(mta),
473 m2.filesnotin(mta),
469 baselabel='topological common ancestor')
474 baselabel='topological common ancestor')
470
475
471 for f in u1u:
476 for f in u1u:
472 _checkcopies(c1, c2, f, base, tca, dirtyc1, limit, data1)
477 _checkcopies(c1, c2, f, base, tca, dirtyc1, limit, data1)
473
478
474 for f in u2u:
479 for f in u2u:
475 _checkcopies(c2, c1, f, base, tca, dirtyc2, limit, data2)
480 _checkcopies(c2, c1, f, base, tca, dirtyc2, limit, data2)
476
481
477 copy = dict(data1['copy'])
482 copy = dict(data1['copy'])
478 copy.update(data2['copy'])
483 copy.update(data2['copy'])
479 fullcopy = dict(data1['fullcopy'])
484 fullcopy = dict(data1['fullcopy'])
480 fullcopy.update(data2['fullcopy'])
485 fullcopy.update(data2['fullcopy'])
481
486
482 if dirtyc1:
487 if dirtyc1:
483 _combinecopies(data2['incomplete'], data1['incomplete'], copy, diverge,
488 _combinecopies(data2['incomplete'], data1['incomplete'], copy, diverge,
484 incompletediverge)
489 incompletediverge)
485 else:
490 else:
486 _combinecopies(data1['incomplete'], data2['incomplete'], copy, diverge,
491 _combinecopies(data1['incomplete'], data2['incomplete'], copy, diverge,
487 incompletediverge)
492 incompletediverge)
488
493
489 renamedelete = {}
494 renamedelete = {}
490 renamedeleteset = set()
495 renamedeleteset = set()
491 divergeset = set()
496 divergeset = set()
492 for of, fl in diverge.items():
497 for of, fl in diverge.items():
493 if len(fl) == 1 or of in c1 or of in c2:
498 if len(fl) == 1 or of in c1 or of in c2:
494 del diverge[of] # not actually divergent, or not a rename
499 del diverge[of] # not actually divergent, or not a rename
495 if of not in c1 and of not in c2:
500 if of not in c1 and of not in c2:
496 # renamed on one side, deleted on the other side, but filter
501 # renamed on one side, deleted on the other side, but filter
497 # out files that have been renamed and then deleted
502 # out files that have been renamed and then deleted
498 renamedelete[of] = [f for f in fl if f in c1 or f in c2]
503 renamedelete[of] = [f for f in fl if f in c1 or f in c2]
499 renamedeleteset.update(fl) # reverse map for below
504 renamedeleteset.update(fl) # reverse map for below
500 else:
505 else:
501 divergeset.update(fl) # reverse map for below
506 divergeset.update(fl) # reverse map for below
502
507
503 if bothnew:
508 if bothnew:
504 repo.ui.debug(" unmatched files new in both:\n %s\n"
509 repo.ui.debug(" unmatched files new in both:\n %s\n"
505 % "\n ".join(bothnew))
510 % "\n ".join(bothnew))
506 bothdiverge = {}
511 bothdiverge = {}
507 bothincompletediverge = {}
512 bothincompletediverge = {}
508 remainder = {}
513 remainder = {}
509 both1 = {'copy': {},
514 both1 = {'copy': {},
510 'fullcopy': {},
515 'fullcopy': {},
511 'incomplete': {},
516 'incomplete': {},
512 'diverge': bothdiverge,
517 'diverge': bothdiverge,
513 'incompletediverge': bothincompletediverge
518 'incompletediverge': bothincompletediverge
514 }
519 }
515 both2 = {'copy': {},
520 both2 = {'copy': {},
516 'fullcopy': {},
521 'fullcopy': {},
517 'incomplete': {},
522 'incomplete': {},
518 'diverge': bothdiverge,
523 'diverge': bothdiverge,
519 'incompletediverge': bothincompletediverge
524 'incompletediverge': bothincompletediverge
520 }
525 }
521 for f in bothnew:
526 for f in bothnew:
522 _checkcopies(c1, c2, f, base, tca, dirtyc1, limit, both1)
527 _checkcopies(c1, c2, f, base, tca, dirtyc1, limit, both1)
523 _checkcopies(c2, c1, f, base, tca, dirtyc2, limit, both2)
528 _checkcopies(c2, c1, f, base, tca, dirtyc2, limit, both2)
524 if dirtyc1:
529 if dirtyc1:
525 # incomplete copies may only be found on the "dirty" side for bothnew
530 # incomplete copies may only be found on the "dirty" side for bothnew
526 assert not both2['incomplete']
531 assert not both2['incomplete']
527 remainder = _combinecopies({}, both1['incomplete'], copy, bothdiverge,
532 remainder = _combinecopies({}, both1['incomplete'], copy, bothdiverge,
528 bothincompletediverge)
533 bothincompletediverge)
529 elif dirtyc2:
534 elif dirtyc2:
530 assert not both1['incomplete']
535 assert not both1['incomplete']
531 remainder = _combinecopies({}, both2['incomplete'], copy, bothdiverge,
536 remainder = _combinecopies({}, both2['incomplete'], copy, bothdiverge,
532 bothincompletediverge)
537 bothincompletediverge)
533 else:
538 else:
534 # incomplete copies and divergences can't happen outside grafts
539 # incomplete copies and divergences can't happen outside grafts
535 assert not both1['incomplete']
540 assert not both1['incomplete']
536 assert not both2['incomplete']
541 assert not both2['incomplete']
537 assert not bothincompletediverge
542 assert not bothincompletediverge
538 for f in remainder:
543 for f in remainder:
539 assert f not in bothdiverge
544 assert f not in bothdiverge
540 ic = remainder[f]
545 ic = remainder[f]
541 if ic[0] in (m1 if dirtyc1 else m2):
546 if ic[0] in (m1 if dirtyc1 else m2):
542 # backed-out rename on one side, but watch out for deleted files
547 # backed-out rename on one side, but watch out for deleted files
543 bothdiverge[f] = ic
548 bothdiverge[f] = ic
544 for of, fl in bothdiverge.items():
549 for of, fl in bothdiverge.items():
545 if len(fl) == 2 and fl[0] == fl[1]:
550 if len(fl) == 2 and fl[0] == fl[1]:
546 copy[fl[0]] = of # not actually divergent, just matching renames
551 copy[fl[0]] = of # not actually divergent, just matching renames
547
552
548 if fullcopy and repo.ui.debugflag:
553 if fullcopy and repo.ui.debugflag:
549 repo.ui.debug(" all copies found (* = to merge, ! = divergent, "
554 repo.ui.debug(" all copies found (* = to merge, ! = divergent, "
550 "% = renamed and deleted):\n")
555 "% = renamed and deleted):\n")
551 for f in sorted(fullcopy):
556 for f in sorted(fullcopy):
552 note = ""
557 note = ""
553 if f in copy:
558 if f in copy:
554 note += "*"
559 note += "*"
555 if f in divergeset:
560 if f in divergeset:
556 note += "!"
561 note += "!"
557 if f in renamedeleteset:
562 if f in renamedeleteset:
558 note += "%"
563 note += "%"
559 repo.ui.debug(" src: '%s' -> dst: '%s' %s\n" % (fullcopy[f], f,
564 repo.ui.debug(" src: '%s' -> dst: '%s' %s\n" % (fullcopy[f], f,
560 note))
565 note))
561 del divergeset
566 del divergeset
562
567
563 if not fullcopy:
568 if not fullcopy:
564 return copy, {}, diverge, renamedelete, {}
569 return copy, {}, diverge, renamedelete, {}
565
570
566 repo.ui.debug(" checking for directory renames\n")
571 repo.ui.debug(" checking for directory renames\n")
567
572
568 # generate a directory move map
573 # generate a directory move map
569 d1, d2 = c1.dirs(), c2.dirs()
574 d1, d2 = c1.dirs(), c2.dirs()
570 # Hack for adding '', which is not otherwise added, to d1 and d2
575 # Hack for adding '', which is not otherwise added, to d1 and d2
571 d1.addpath('/')
576 d1.addpath('/')
572 d2.addpath('/')
577 d2.addpath('/')
573 invalid = set()
578 invalid = set()
574 dirmove = {}
579 dirmove = {}
575
580
576 # examine each file copy for a potential directory move, which is
581 # examine each file copy for a potential directory move, which is
577 # when all the files in a directory are moved to a new directory
582 # when all the files in a directory are moved to a new directory
578 for dst, src in fullcopy.iteritems():
583 for dst, src in fullcopy.iteritems():
579 dsrc, ddst = pathutil.dirname(src), pathutil.dirname(dst)
584 dsrc, ddst = pathutil.dirname(src), pathutil.dirname(dst)
580 if dsrc in invalid:
585 if dsrc in invalid:
581 # already seen to be uninteresting
586 # already seen to be uninteresting
582 continue
587 continue
583 elif dsrc in d1 and ddst in d1:
588 elif dsrc in d1 and ddst in d1:
584 # directory wasn't entirely moved locally
589 # directory wasn't entirely moved locally
585 invalid.add(dsrc + "/")
590 invalid.add(dsrc + "/")
586 elif dsrc in d2 and ddst in d2:
591 elif dsrc in d2 and ddst in d2:
587 # directory wasn't entirely moved remotely
592 # directory wasn't entirely moved remotely
588 invalid.add(dsrc + "/")
593 invalid.add(dsrc + "/")
589 elif dsrc + "/" in dirmove and dirmove[dsrc + "/"] != ddst + "/":
594 elif dsrc + "/" in dirmove and dirmove[dsrc + "/"] != ddst + "/":
590 # files from the same directory moved to two different places
595 # files from the same directory moved to two different places
591 invalid.add(dsrc + "/")
596 invalid.add(dsrc + "/")
592 else:
597 else:
593 # looks good so far
598 # looks good so far
594 dirmove[dsrc + "/"] = ddst + "/"
599 dirmove[dsrc + "/"] = ddst + "/"
595
600
596 for i in invalid:
601 for i in invalid:
597 if i in dirmove:
602 if i in dirmove:
598 del dirmove[i]
603 del dirmove[i]
599 del d1, d2, invalid
604 del d1, d2, invalid
600
605
601 if not dirmove:
606 if not dirmove:
602 return copy, {}, diverge, renamedelete, {}
607 return copy, {}, diverge, renamedelete, {}
603
608
604 for d in dirmove:
609 for d in dirmove:
605 repo.ui.debug(" discovered dir src: '%s' -> dst: '%s'\n" %
610 repo.ui.debug(" discovered dir src: '%s' -> dst: '%s'\n" %
606 (d, dirmove[d]))
611 (d, dirmove[d]))
607
612
608 movewithdir = {}
613 movewithdir = {}
609 # check unaccounted nonoverlapping files against directory moves
614 # check unaccounted nonoverlapping files against directory moves
610 for f in u1r + u2r:
615 for f in u1r + u2r:
611 if f not in fullcopy:
616 if f not in fullcopy:
612 for d in dirmove:
617 for d in dirmove:
613 if f.startswith(d):
618 if f.startswith(d):
614 # new file added in a directory that was moved, move it
619 # new file added in a directory that was moved, move it
615 df = dirmove[d] + f[len(d):]
620 df = dirmove[d] + f[len(d):]
616 if df not in copy:
621 if df not in copy:
617 movewithdir[f] = df
622 movewithdir[f] = df
618 repo.ui.debug((" pending file src: '%s' -> "
623 repo.ui.debug((" pending file src: '%s' -> "
619 "dst: '%s'\n") % (f, df))
624 "dst: '%s'\n") % (f, df))
620 break
625 break
621
626
622 return copy, movewithdir, diverge, renamedelete, dirmove
627 return copy, movewithdir, diverge, renamedelete, dirmove
623
628
624 def _heuristicscopytracing(repo, c1, c2, base):
629 def _heuristicscopytracing(repo, c1, c2, base):
625 """ Fast copytracing using filename heuristics
630 """ Fast copytracing using filename heuristics
626
631
627 Assumes that moves or renames are of following two types:
632 Assumes that moves or renames are of following two types:
628
633
629 1) Inside a directory only (same directory name but different filenames)
634 1) Inside a directory only (same directory name but different filenames)
630 2) Move from one directory to another
635 2) Move from one directory to another
631 (same filenames but different directory names)
636 (same filenames but different directory names)
632
637
633 Works only when there are no merge commits in the "source branch".
638 Works only when there are no merge commits in the "source branch".
634 Source branch is commits from base up to c2 not including base.
639 Source branch is commits from base up to c2 not including base.
635
640
636 If merge is involved it fallbacks to _fullcopytracing().
641 If merge is involved it fallbacks to _fullcopytracing().
637
642
638 Can be used by setting the following config:
643 Can be used by setting the following config:
639
644
640 [experimental]
645 [experimental]
641 copytrace = heuristics
646 copytrace = heuristics
642 """
647 """
643
648
644 if c1.rev() is None:
649 if c1.rev() is None:
645 c1 = c1.p1()
650 c1 = c1.p1()
646 if c2.rev() is None:
651 if c2.rev() is None:
647 c2 = c2.p1()
652 c2 = c2.p1()
648
653
649 copies = {}
654 copies = {}
650
655
651 changedfiles = set()
656 changedfiles = set()
652 m1 = c1.manifest()
657 m1 = c1.manifest()
653 if not repo.revs('%d::%d', base.rev(), c2.rev()):
658 if not repo.revs('%d::%d', base.rev(), c2.rev()):
654 # If base is not in c2 branch, we switch to fullcopytracing
659 # If base is not in c2 branch, we switch to fullcopytracing
655 repo.ui.debug("switching to full copytracing as base is not "
660 repo.ui.debug("switching to full copytracing as base is not "
656 "an ancestor of c2\n")
661 "an ancestor of c2\n")
657 return _fullcopytracing(repo, c1, c2, base)
662 return _fullcopytracing(repo, c1, c2, base)
658
663
659 ctx = c2
664 ctx = c2
660 while ctx != base:
665 while ctx != base:
661 if len(ctx.parents()) == 2:
666 if len(ctx.parents()) == 2:
662 # To keep things simple let's not handle merges
667 # To keep things simple let's not handle merges
663 repo.ui.debug("switching to full copytracing because of merges\n")
668 repo.ui.debug("switching to full copytracing because of merges\n")
664 return _fullcopytracing(repo, c1, c2, base)
669 return _fullcopytracing(repo, c1, c2, base)
665 changedfiles.update(ctx.files())
670 changedfiles.update(ctx.files())
666 ctx = ctx.p1()
671 ctx = ctx.p1()
667
672
668 cp = _forwardcopies(base, c2)
673 cp = _forwardcopies(base, c2)
669 for dst, src in cp.iteritems():
674 for dst, src in cp.iteritems():
670 if src in m1:
675 if src in m1:
671 copies[dst] = src
676 copies[dst] = src
672
677
673 # file is missing if it isn't present in the destination, but is present in
678 # file is missing if it isn't present in the destination, but is present in
674 # the base and present in the source.
679 # the base and present in the source.
675 # Presence in the base is important to exclude added files, presence in the
680 # Presence in the base is important to exclude added files, presence in the
676 # source is important to exclude removed files.
681 # source is important to exclude removed files.
677 missingfiles = filter(lambda f: f not in m1 and f in base and f in c2,
682 missingfiles = filter(lambda f: f not in m1 and f in base and f in c2,
678 changedfiles)
683 changedfiles)
679
684
680 if missingfiles:
685 if missingfiles:
681 basenametofilename = collections.defaultdict(list)
686 basenametofilename = collections.defaultdict(list)
682 dirnametofilename = collections.defaultdict(list)
687 dirnametofilename = collections.defaultdict(list)
683
688
684 for f in m1.filesnotin(base.manifest()):
689 for f in m1.filesnotin(base.manifest()):
685 basename = os.path.basename(f)
690 basename = os.path.basename(f)
686 dirname = os.path.dirname(f)
691 dirname = os.path.dirname(f)
687 basenametofilename[basename].append(f)
692 basenametofilename[basename].append(f)
688 dirnametofilename[dirname].append(f)
693 dirnametofilename[dirname].append(f)
689
694
690 # in case of a rebase/graft, base may not be a common ancestor
695 # in case of a rebase/graft, base may not be a common ancestor
691 anc = c1.ancestor(c2)
696 anc = c1.ancestor(c2)
692
697
693 for f in missingfiles:
698 for f in missingfiles:
694 basename = os.path.basename(f)
699 basename = os.path.basename(f)
695 dirname = os.path.dirname(f)
700 dirname = os.path.dirname(f)
696 samebasename = basenametofilename[basename]
701 samebasename = basenametofilename[basename]
697 samedirname = dirnametofilename[dirname]
702 samedirname = dirnametofilename[dirname]
698 movecandidates = samebasename + samedirname
703 movecandidates = samebasename + samedirname
699 # f is guaranteed to be present in c2, that's why
704 # f is guaranteed to be present in c2, that's why
700 # c2.filectx(f) won't fail
705 # c2.filectx(f) won't fail
701 f2 = c2.filectx(f)
706 f2 = c2.filectx(f)
702 for candidate in movecandidates:
707 for candidate in movecandidates:
703 f1 = c1.filectx(candidate)
708 f1 = c1.filectx(candidate)
704 if _related(f1, f2, anc.rev()):
709 if _related(f1, f2, anc.rev()):
705 # if there are a few related copies then we'll merge
710 # if there are a few related copies then we'll merge
706 # changes into all of them. This matches the behaviour
711 # changes into all of them. This matches the behaviour
707 # of upstream copytracing
712 # of upstream copytracing
708 copies[candidate] = f
713 copies[candidate] = f
709
714
710 return copies, {}, {}, {}, {}
715 return copies, {}, {}, {}, {}
711
716
712 def _related(f1, f2, limit):
717 def _related(f1, f2, limit):
713 """return True if f1 and f2 filectx have a common ancestor
718 """return True if f1 and f2 filectx have a common ancestor
714
719
715 Walk back to common ancestor to see if the two files originate
720 Walk back to common ancestor to see if the two files originate
716 from the same file. Since workingfilectx's rev() is None it messes
721 from the same file. Since workingfilectx's rev() is None it messes
717 up the integer comparison logic, hence the pre-step check for
722 up the integer comparison logic, hence the pre-step check for
718 None (f1 and f2 can only be workingfilectx's initially).
723 None (f1 and f2 can only be workingfilectx's initially).
719 """
724 """
720
725
721 if f1 == f2:
726 if f1 == f2:
722 return f1 # a match
727 return f1 # a match
723
728
724 g1, g2 = f1.ancestors(), f2.ancestors()
729 g1, g2 = f1.ancestors(), f2.ancestors()
725 try:
730 try:
726 f1r, f2r = f1.linkrev(), f2.linkrev()
731 f1r, f2r = f1.linkrev(), f2.linkrev()
727
732
728 if f1r is None:
733 if f1r is None:
729 f1 = next(g1)
734 f1 = next(g1)
730 if f2r is None:
735 if f2r is None:
731 f2 = next(g2)
736 f2 = next(g2)
732
737
733 while True:
738 while True:
734 f1r, f2r = f1.linkrev(), f2.linkrev()
739 f1r, f2r = f1.linkrev(), f2.linkrev()
735 if f1r > f2r:
740 if f1r > f2r:
736 f1 = next(g1)
741 f1 = next(g1)
737 elif f2r > f1r:
742 elif f2r > f1r:
738 f2 = next(g2)
743 f2 = next(g2)
739 elif f1 == f2:
744 elif f1 == f2:
740 return f1 # a match
745 return f1 # a match
741 elif f1r == f2r or f1r < limit or f2r < limit:
746 elif f1r == f2r or f1r < limit or f2r < limit:
742 return False # copy no longer relevant
747 return False # copy no longer relevant
743 except StopIteration:
748 except StopIteration:
744 return False
749 return False
745
750
746 def _checkcopies(srcctx, dstctx, f, base, tca, remotebase, limit, data):
751 def _checkcopies(srcctx, dstctx, f, base, tca, remotebase, limit, data):
747 """
752 """
748 check possible copies of f from msrc to mdst
753 check possible copies of f from msrc to mdst
749
754
750 srcctx = starting context for f in msrc
755 srcctx = starting context for f in msrc
751 dstctx = destination context for f in mdst
756 dstctx = destination context for f in mdst
752 f = the filename to check (as in msrc)
757 f = the filename to check (as in msrc)
753 base = the changectx used as a merge base
758 base = the changectx used as a merge base
754 tca = topological common ancestor for graft-like scenarios
759 tca = topological common ancestor for graft-like scenarios
755 remotebase = True if base is outside tca::srcctx, False otherwise
760 remotebase = True if base is outside tca::srcctx, False otherwise
756 limit = the rev number to not search beyond
761 limit = the rev number to not search beyond
757 data = dictionary of dictionary to store copy data. (see mergecopies)
762 data = dictionary of dictionary to store copy data. (see mergecopies)
758
763
759 note: limit is only an optimization, and provides no guarantee that
764 note: limit is only an optimization, and provides no guarantee that
760 irrelevant revisions will not be visited
765 irrelevant revisions will not be visited
761 there is no easy way to make this algorithm stop in a guaranteed way
766 there is no easy way to make this algorithm stop in a guaranteed way
762 once it "goes behind a certain revision".
767 once it "goes behind a certain revision".
763 """
768 """
764
769
765 msrc = srcctx.manifest()
770 msrc = srcctx.manifest()
766 mdst = dstctx.manifest()
771 mdst = dstctx.manifest()
767 mb = base.manifest()
772 mb = base.manifest()
768 mta = tca.manifest()
773 mta = tca.manifest()
769 # Might be true if this call is about finding backward renames,
774 # Might be true if this call is about finding backward renames,
770 # This happens in the case of grafts because the DAG is then rotated.
775 # This happens in the case of grafts because the DAG is then rotated.
771 # If the file exists in both the base and the source, we are not looking
776 # If the file exists in both the base and the source, we are not looking
772 # for a rename on the source side, but on the part of the DAG that is
777 # for a rename on the source side, but on the part of the DAG that is
773 # traversed backwards.
778 # traversed backwards.
774 #
779 #
775 # In the case there is both backward and forward renames (before and after
780 # In the case there is both backward and forward renames (before and after
776 # the base) this is more complicated as we must detect a divergence.
781 # the base) this is more complicated as we must detect a divergence.
777 # We use 'backwards = False' in that case.
782 # We use 'backwards = False' in that case.
778 backwards = not remotebase and base != tca and f in mb
783 backwards = not remotebase and base != tca and f in mb
779 getsrcfctx = _makegetfctx(srcctx)
784 getsrcfctx = _makegetfctx(srcctx)
780 getdstfctx = _makegetfctx(dstctx)
785 getdstfctx = _makegetfctx(dstctx)
781
786
782 if msrc[f] == mb.get(f) and not remotebase:
787 if msrc[f] == mb.get(f) and not remotebase:
783 # Nothing to merge
788 # Nothing to merge
784 return
789 return
785
790
786 of = None
791 of = None
787 seen = {f}
792 seen = {f}
788 for oc in getsrcfctx(f, msrc[f]).ancestors():
793 for oc in getsrcfctx(f, msrc[f]).ancestors():
789 ocr = oc.linkrev()
794 ocr = oc.linkrev()
790 of = oc.path()
795 of = oc.path()
791 if of in seen:
796 if of in seen:
792 # check limit late - grab last rename before
797 # check limit late - grab last rename before
793 if ocr < limit:
798 if ocr < limit:
794 break
799 break
795 continue
800 continue
796 seen.add(of)
801 seen.add(of)
797
802
798 # remember for dir rename detection
803 # remember for dir rename detection
799 if backwards:
804 if backwards:
800 data['fullcopy'][of] = f # grafting backwards through renames
805 data['fullcopy'][of] = f # grafting backwards through renames
801 else:
806 else:
802 data['fullcopy'][f] = of
807 data['fullcopy'][f] = of
803 if of not in mdst:
808 if of not in mdst:
804 continue # no match, keep looking
809 continue # no match, keep looking
805 if mdst[of] == mb.get(of):
810 if mdst[of] == mb.get(of):
806 return # no merge needed, quit early
811 return # no merge needed, quit early
807 c2 = getdstfctx(of, mdst[of])
812 c2 = getdstfctx(of, mdst[of])
808 # c2 might be a plain new file on added on destination side that is
813 # c2 might be a plain new file on added on destination side that is
809 # unrelated to the droids we are looking for.
814 # unrelated to the droids we are looking for.
810 cr = _related(oc, c2, tca.rev())
815 cr = _related(oc, c2, tca.rev())
811 if cr and (of == f or of == c2.path()): # non-divergent
816 if cr and (of == f or of == c2.path()): # non-divergent
812 if backwards:
817 if backwards:
813 data['copy'][of] = f
818 data['copy'][of] = f
814 elif of in mb:
819 elif of in mb:
815 data['copy'][f] = of
820 data['copy'][f] = of
816 elif remotebase: # special case: a <- b <- a -> b "ping-pong" rename
821 elif remotebase: # special case: a <- b <- a -> b "ping-pong" rename
817 data['copy'][of] = f
822 data['copy'][of] = f
818 del data['fullcopy'][f]
823 del data['fullcopy'][f]
819 data['fullcopy'][of] = f
824 data['fullcopy'][of] = f
820 else: # divergence w.r.t. graft CA on one side of topological CA
825 else: # divergence w.r.t. graft CA on one side of topological CA
821 for sf in seen:
826 for sf in seen:
822 if sf in mb:
827 if sf in mb:
823 assert sf not in data['diverge']
828 assert sf not in data['diverge']
824 data['diverge'][sf] = [f, of]
829 data['diverge'][sf] = [f, of]
825 break
830 break
826 return
831 return
827
832
828 if of in mta:
833 if of in mta:
829 if backwards or remotebase:
834 if backwards or remotebase:
830 data['incomplete'][of] = f
835 data['incomplete'][of] = f
831 else:
836 else:
832 for sf in seen:
837 for sf in seen:
833 if sf in mb:
838 if sf in mb:
834 if tca == base:
839 if tca == base:
835 data['diverge'].setdefault(sf, []).append(f)
840 data['diverge'].setdefault(sf, []).append(f)
836 else:
841 else:
837 data['incompletediverge'][sf] = [of, f]
842 data['incompletediverge'][sf] = [of, f]
838 return
843 return
839
844
840 def duplicatecopies(repo, rev, fromrev, skiprev=None):
845 def duplicatecopies(repo, rev, fromrev, skiprev=None):
841 '''reproduce copies from fromrev to rev in the dirstate
846 '''reproduce copies from fromrev to rev in the dirstate
842
847
843 If skiprev is specified, it's a revision that should be used to
848 If skiprev is specified, it's a revision that should be used to
844 filter copy records. Any copies that occur between fromrev and
849 filter copy records. Any copies that occur between fromrev and
845 skiprev will not be duplicated, even if they appear in the set of
850 skiprev will not be duplicated, even if they appear in the set of
846 copies between fromrev and rev.
851 copies between fromrev and rev.
847 '''
852 '''
848 exclude = {}
853 exclude = {}
849 if (skiprev is not None and
854 if (skiprev is not None and
850 repo.ui.config('experimental', 'copytrace') != 'off'):
855 repo.ui.config('experimental', 'copytrace') != 'off'):
851 # copytrace='off' skips this line, but not the entire function because
856 # copytrace='off' skips this line, but not the entire function because
852 # the line below is O(size of the repo) during a rebase, while the rest
857 # the line below is O(size of the repo) during a rebase, while the rest
853 # of the function is much faster (and is required for carrying copy
858 # of the function is much faster (and is required for carrying copy
854 # metadata across the rebase anyway).
859 # metadata across the rebase anyway).
855 exclude = pathcopies(repo[fromrev], repo[skiprev])
860 exclude = pathcopies(repo[fromrev], repo[skiprev])
856 for dst, src in pathcopies(repo[fromrev], repo[rev]).iteritems():
861 for dst, src in pathcopies(repo[fromrev], repo[rev]).iteritems():
857 # copies.pathcopies returns backward renames, so dst might not
862 # copies.pathcopies returns backward renames, so dst might not
858 # actually be in the dirstate
863 # actually be in the dirstate
859 if dst in exclude:
864 if dst in exclude:
860 continue
865 continue
861 if repo.dirstate[dst] in "nma":
866 if repo.dirstate[dst] in "nma":
862 repo.dirstate.copy(src, dst)
867 repo.dirstate.copy(src, dst)
@@ -1,667 +1,714 b''
1 Test for the heuristic copytracing algorithm
1 Test for the heuristic copytracing algorithm
2 ============================================
2 ============================================
3
3
4 $ cat >> $TESTTMP/copytrace.sh << '__EOF__'
4 $ cat >> $TESTTMP/copytrace.sh << '__EOF__'
5 > initclient() {
5 > initclient() {
6 > cat >> $1/.hg/hgrc <<EOF
6 > cat >> $1/.hg/hgrc <<EOF
7 > [experimental]
7 > [experimental]
8 > copytrace = heuristics
8 > copytrace = heuristics
9 > EOF
9 > EOF
10 > }
10 > }
11 > __EOF__
11 > __EOF__
12 $ . "$TESTTMP/copytrace.sh"
12 $ . "$TESTTMP/copytrace.sh"
13
13
14 $ cat >> $HGRCPATH << EOF
14 $ cat >> $HGRCPATH << EOF
15 > [extensions]
15 > [extensions]
16 > rebase=
16 > rebase=
17 > shelve=
17 > shelve=
18 > EOF
18 > EOF
19
19
20 Check filename heuristics (same dirname and same basename)
20 Check filename heuristics (same dirname and same basename)
21 $ hg init server
21 $ hg init server
22 $ cd server
22 $ cd server
23 $ echo a > a
23 $ echo a > a
24 $ mkdir dir
24 $ mkdir dir
25 $ echo a > dir/file.txt
25 $ echo a > dir/file.txt
26 $ hg addremove
26 $ hg addremove
27 adding a
27 adding a
28 adding dir/file.txt
28 adding dir/file.txt
29 $ hg ci -m initial
29 $ hg ci -m initial
30 $ hg mv a b
30 $ hg mv a b
31 $ hg mv -q dir dir2
31 $ hg mv -q dir dir2
32 $ hg ci -m 'mv a b, mv dir/ dir2/'
32 $ hg ci -m 'mv a b, mv dir/ dir2/'
33 $ cd ..
33 $ cd ..
34 $ hg clone -q server repo
34 $ hg clone -q server repo
35 $ initclient repo
35 $ initclient repo
36 $ cd repo
36 $ cd repo
37 $ hg up -q 0
37 $ hg up -q 0
38 $ echo b > a
38 $ echo b > a
39 $ echo b > dir/file.txt
39 $ echo b > dir/file.txt
40 $ hg ci -qm 'mod a, mod dir/file.txt'
40 $ hg ci -qm 'mod a, mod dir/file.txt'
41
41
42 $ hg log -G -T 'changeset: {node}\n desc: {desc}, phase: {phase}\n'
42 $ hg log -G -T 'changeset: {node}\n desc: {desc}, phase: {phase}\n'
43 @ changeset: 557f403c0afd2a3cf15d7e2fb1f1001a8b85e081
43 @ changeset: 557f403c0afd2a3cf15d7e2fb1f1001a8b85e081
44 | desc: mod a, mod dir/file.txt, phase: draft
44 | desc: mod a, mod dir/file.txt, phase: draft
45 | o changeset: 928d74bc9110681920854d845c06959f6dfc9547
45 | o changeset: 928d74bc9110681920854d845c06959f6dfc9547
46 |/ desc: mv a b, mv dir/ dir2/, phase: public
46 |/ desc: mv a b, mv dir/ dir2/, phase: public
47 o changeset: 3c482b16e54596fed340d05ffaf155f156cda7ee
47 o changeset: 3c482b16e54596fed340d05ffaf155f156cda7ee
48 desc: initial, phase: public
48 desc: initial, phase: public
49
49
50 $ hg rebase -s . -d 1
50 $ hg rebase -s . -d 1
51 rebasing 2:557f403c0afd "mod a, mod dir/file.txt" (tip)
51 rebasing 2:557f403c0afd "mod a, mod dir/file.txt" (tip)
52 merging b and a to b
52 merging b and a to b
53 merging dir2/file.txt and dir/file.txt to dir2/file.txt
53 merging dir2/file.txt and dir/file.txt to dir2/file.txt
54 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/557f403c0afd-9926eeff-rebase.hg (glob)
54 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/557f403c0afd-9926eeff-rebase.hg (glob)
55 $ cd ..
55 $ cd ..
56 $ rm -rf server
56 $ rm -rf server
57 $ rm -rf repo
57 $ rm -rf repo
58
58
59 Make sure filename heuristics do not when they are not related
59 Make sure filename heuristics do not when they are not related
60 $ hg init server
60 $ hg init server
61 $ cd server
61 $ cd server
62 $ echo 'somecontent' > a
62 $ echo 'somecontent' > a
63 $ hg add a
63 $ hg add a
64 $ hg ci -m initial
64 $ hg ci -m initial
65 $ hg rm a
65 $ hg rm a
66 $ echo 'completelydifferentcontext' > b
66 $ echo 'completelydifferentcontext' > b
67 $ hg add b
67 $ hg add b
68 $ hg ci -m 'rm a, add b'
68 $ hg ci -m 'rm a, add b'
69 $ cd ..
69 $ cd ..
70 $ hg clone -q server repo
70 $ hg clone -q server repo
71 $ initclient repo
71 $ initclient repo
72 $ cd repo
72 $ cd repo
73 $ hg up -q 0
73 $ hg up -q 0
74 $ printf 'somecontent\nmoarcontent' > a
74 $ printf 'somecontent\nmoarcontent' > a
75 $ hg ci -qm 'mode a'
75 $ hg ci -qm 'mode a'
76
76
77 $ hg log -G -T 'changeset: {node}\n desc: {desc}, phase: {phase}\n'
77 $ hg log -G -T 'changeset: {node}\n desc: {desc}, phase: {phase}\n'
78 @ changeset: d526312210b9e8f795d576a77dc643796384d86e
78 @ changeset: d526312210b9e8f795d576a77dc643796384d86e
79 | desc: mode a, phase: draft
79 | desc: mode a, phase: draft
80 | o changeset: 46985f76c7e5e5123433527f5c8526806145650b
80 | o changeset: 46985f76c7e5e5123433527f5c8526806145650b
81 |/ desc: rm a, add b, phase: public
81 |/ desc: rm a, add b, phase: public
82 o changeset: e5b71fb099c29d9172ef4a23485aaffd497e4cc0
82 o changeset: e5b71fb099c29d9172ef4a23485aaffd497e4cc0
83 desc: initial, phase: public
83 desc: initial, phase: public
84
84
85 $ hg rebase -s . -d 1
85 $ hg rebase -s . -d 1
86 rebasing 2:d526312210b9 "mode a" (tip)
86 rebasing 2:d526312210b9 "mode a" (tip)
87 other [source] changed a which local [dest] deleted
87 other [source] changed a which local [dest] deleted
88 use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u
88 use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u
89 unresolved conflicts (see hg resolve, then hg rebase --continue)
89 unresolved conflicts (see hg resolve, then hg rebase --continue)
90 [1]
90 [1]
91
91
92 $ cd ..
92 $ cd ..
93 $ rm -rf server
93 $ rm -rf server
94 $ rm -rf repo
94 $ rm -rf repo
95
95
96 Test when lca didn't modified the file that was moved
96 Test when lca didn't modified the file that was moved
97 $ hg init server
97 $ hg init server
98 $ cd server
98 $ cd server
99 $ echo 'somecontent' > a
99 $ echo 'somecontent' > a
100 $ hg add a
100 $ hg add a
101 $ hg ci -m initial
101 $ hg ci -m initial
102 $ echo c > c
102 $ echo c > c
103 $ hg add c
103 $ hg add c
104 $ hg ci -m randomcommit
104 $ hg ci -m randomcommit
105 $ hg mv a b
105 $ hg mv a b
106 $ hg ci -m 'mv a b'
106 $ hg ci -m 'mv a b'
107 $ cd ..
107 $ cd ..
108 $ hg clone -q server repo
108 $ hg clone -q server repo
109 $ initclient repo
109 $ initclient repo
110 $ cd repo
110 $ cd repo
111 $ hg up -q 1
111 $ hg up -q 1
112 $ echo b > a
112 $ echo b > a
113 $ hg ci -qm 'mod a'
113 $ hg ci -qm 'mod a'
114
114
115 $ hg log -G -T 'changeset: {node}\n desc: {desc}, phase: {phase}\n'
115 $ hg log -G -T 'changeset: {node}\n desc: {desc}, phase: {phase}\n'
116 @ changeset: 9d5cf99c3d9f8e8b05ba55421f7f56530cfcf3bc
116 @ changeset: 9d5cf99c3d9f8e8b05ba55421f7f56530cfcf3bc
117 | desc: mod a, phase: draft
117 | desc: mod a, phase: draft
118 | o changeset: d760186dd240fc47b91eb9f0b58b0002aaeef95d
118 | o changeset: d760186dd240fc47b91eb9f0b58b0002aaeef95d
119 |/ desc: mv a b, phase: public
119 |/ desc: mv a b, phase: public
120 o changeset: 48e1b6ba639d5d7fb313fa7989eebabf99c9eb83
120 o changeset: 48e1b6ba639d5d7fb313fa7989eebabf99c9eb83
121 | desc: randomcommit, phase: public
121 | desc: randomcommit, phase: public
122 o changeset: e5b71fb099c29d9172ef4a23485aaffd497e4cc0
122 o changeset: e5b71fb099c29d9172ef4a23485aaffd497e4cc0
123 desc: initial, phase: public
123 desc: initial, phase: public
124
124
125 $ hg rebase -s . -d 2
125 $ hg rebase -s . -d 2
126 rebasing 3:9d5cf99c3d9f "mod a" (tip)
126 rebasing 3:9d5cf99c3d9f "mod a" (tip)
127 merging b and a to b
127 merging b and a to b
128 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/9d5cf99c3d9f-f02358cc-rebase.hg (glob)
128 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/9d5cf99c3d9f-f02358cc-rebase.hg (glob)
129 $ cd ..
129 $ cd ..
130 $ rm -rf server
130 $ rm -rf server
131 $ rm -rf repo
131 $ rm -rf repo
132
132
133 Rebase "backwards"
133 Rebase "backwards"
134 $ hg init server
134 $ hg init server
135 $ cd server
135 $ cd server
136 $ echo 'somecontent' > a
136 $ echo 'somecontent' > a
137 $ hg add a
137 $ hg add a
138 $ hg ci -m initial
138 $ hg ci -m initial
139 $ echo c > c
139 $ echo c > c
140 $ hg add c
140 $ hg add c
141 $ hg ci -m randomcommit
141 $ hg ci -m randomcommit
142 $ hg mv a b
142 $ hg mv a b
143 $ hg ci -m 'mv a b'
143 $ hg ci -m 'mv a b'
144 $ cd ..
144 $ cd ..
145 $ hg clone -q server repo
145 $ hg clone -q server repo
146 $ initclient repo
146 $ initclient repo
147 $ cd repo
147 $ cd repo
148 $ hg up -q 2
148 $ hg up -q 2
149 $ echo b > b
149 $ echo b > b
150 $ hg ci -qm 'mod b'
150 $ hg ci -qm 'mod b'
151
151
152 $ hg log -G -T 'changeset: {node}\n desc: {desc}, phase: {phase}\n'
152 $ hg log -G -T 'changeset: {node}\n desc: {desc}, phase: {phase}\n'
153 @ changeset: fbe97126b3969056795c462a67d93faf13e4d298
153 @ changeset: fbe97126b3969056795c462a67d93faf13e4d298
154 | desc: mod b, phase: draft
154 | desc: mod b, phase: draft
155 o changeset: d760186dd240fc47b91eb9f0b58b0002aaeef95d
155 o changeset: d760186dd240fc47b91eb9f0b58b0002aaeef95d
156 | desc: mv a b, phase: public
156 | desc: mv a b, phase: public
157 o changeset: 48e1b6ba639d5d7fb313fa7989eebabf99c9eb83
157 o changeset: 48e1b6ba639d5d7fb313fa7989eebabf99c9eb83
158 | desc: randomcommit, phase: public
158 | desc: randomcommit, phase: public
159 o changeset: e5b71fb099c29d9172ef4a23485aaffd497e4cc0
159 o changeset: e5b71fb099c29d9172ef4a23485aaffd497e4cc0
160 desc: initial, phase: public
160 desc: initial, phase: public
161
161
162 $ hg rebase -s . -d 0
162 $ hg rebase -s . -d 0
163 rebasing 3:fbe97126b396 "mod b" (tip)
163 rebasing 3:fbe97126b396 "mod b" (tip)
164 merging a and b to a
164 merging a and b to a
165 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/fbe97126b396-cf5452a1-rebase.hg (glob)
165 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/fbe97126b396-cf5452a1-rebase.hg (glob)
166 $ cd ..
166 $ cd ..
167 $ rm -rf server
167 $ rm -rf server
168 $ rm -rf repo
168 $ rm -rf repo
169
169
170 Rebase draft commit on top of draft commit
170 Rebase draft commit on top of draft commit
171 $ hg init repo
171 $ hg init repo
172 $ initclient repo
172 $ initclient repo
173 $ cd repo
173 $ cd repo
174 $ echo 'somecontent' > a
174 $ echo 'somecontent' > a
175 $ hg add a
175 $ hg add a
176 $ hg ci -m initial
176 $ hg ci -m initial
177 $ hg mv a b
177 $ hg mv a b
178 $ hg ci -m 'mv a b'
178 $ hg ci -m 'mv a b'
179 $ hg up -q ".^"
179 $ hg up -q ".^"
180 $ echo b > a
180 $ echo b > a
181 $ hg ci -qm 'mod a'
181 $ hg ci -qm 'mod a'
182
182
183 $ hg log -G -T 'changeset: {node}\n desc: {desc}, phase: {phase}\n'
183 $ hg log -G -T 'changeset: {node}\n desc: {desc}, phase: {phase}\n'
184 @ changeset: 5268f05aa1684cfb5741e9eb05eddcc1c5ee7508
184 @ changeset: 5268f05aa1684cfb5741e9eb05eddcc1c5ee7508
185 | desc: mod a, phase: draft
185 | desc: mod a, phase: draft
186 | o changeset: 542cb58df733ee48fa74729bd2cdb94c9310d362
186 | o changeset: 542cb58df733ee48fa74729bd2cdb94c9310d362
187 |/ desc: mv a b, phase: draft
187 |/ desc: mv a b, phase: draft
188 o changeset: e5b71fb099c29d9172ef4a23485aaffd497e4cc0
188 o changeset: e5b71fb099c29d9172ef4a23485aaffd497e4cc0
189 desc: initial, phase: draft
189 desc: initial, phase: draft
190
190
191 $ hg rebase -s . -d 1
191 $ hg rebase -s . -d 1
192 rebasing 2:5268f05aa168 "mod a" (tip)
192 rebasing 2:5268f05aa168 "mod a" (tip)
193 merging b and a to b
193 merging b and a to b
194 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/5268f05aa168-284f6515-rebase.hg (glob)
194 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/5268f05aa168-284f6515-rebase.hg (glob)
195 $ cd ..
195 $ cd ..
196 $ rm -rf server
196 $ rm -rf server
197 $ rm -rf repo
197 $ rm -rf repo
198
198
199 Check a few potential move candidates
199 Check a few potential move candidates
200 $ hg init server
200 $ hg init server
201 $ initclient server
201 $ initclient server
202 $ cd server
202 $ cd server
203 $ mkdir dir
203 $ mkdir dir
204 $ echo a > dir/a
204 $ echo a > dir/a
205 $ hg add dir/a
205 $ hg add dir/a
206 $ hg ci -qm initial
206 $ hg ci -qm initial
207 $ hg mv dir/a dir/b
207 $ hg mv dir/a dir/b
208 $ hg ci -qm 'mv dir/a dir/b'
208 $ hg ci -qm 'mv dir/a dir/b'
209 $ mkdir dir2
209 $ mkdir dir2
210 $ echo b > dir2/a
210 $ echo b > dir2/a
211 $ hg add dir2/a
211 $ hg add dir2/a
212 $ hg ci -qm 'create dir2/a'
212 $ hg ci -qm 'create dir2/a'
213 $ cd ..
213 $ cd ..
214 $ hg clone -q server repo
214 $ hg clone -q server repo
215 $ initclient repo
215 $ initclient repo
216 $ cd repo
216 $ cd repo
217 $ hg up -q 0
217 $ hg up -q 0
218 $ echo b > dir/a
218 $ echo b > dir/a
219 $ hg ci -qm 'mod dir/a'
219 $ hg ci -qm 'mod dir/a'
220
220
221 $ hg log -G -T 'changeset: {node}\n desc: {desc}, phase: {phase}\n'
221 $ hg log -G -T 'changeset: {node}\n desc: {desc}, phase: {phase}\n'
222 @ changeset: 6b2f4cece40fd320f41229f23821256ffc08efea
222 @ changeset: 6b2f4cece40fd320f41229f23821256ffc08efea
223 | desc: mod dir/a, phase: draft
223 | desc: mod dir/a, phase: draft
224 | o changeset: 4494bf7efd2e0dfdd388e767fb913a8a3731e3fa
224 | o changeset: 4494bf7efd2e0dfdd388e767fb913a8a3731e3fa
225 | | desc: create dir2/a, phase: public
225 | | desc: create dir2/a, phase: public
226 | o changeset: b1784dfab6ea6bfafeb11c0ac50a2981b0fe6ade
226 | o changeset: b1784dfab6ea6bfafeb11c0ac50a2981b0fe6ade
227 |/ desc: mv dir/a dir/b, phase: public
227 |/ desc: mv dir/a dir/b, phase: public
228 o changeset: 36859b8907c513a3a87ae34ba5b1e7eea8c20944
228 o changeset: 36859b8907c513a3a87ae34ba5b1e7eea8c20944
229 desc: initial, phase: public
229 desc: initial, phase: public
230
230
231 $ hg rebase -s . -d 2
231 $ hg rebase -s . -d 2
232 rebasing 3:6b2f4cece40f "mod dir/a" (tip)
232 rebasing 3:6b2f4cece40f "mod dir/a" (tip)
233 merging dir/b and dir/a to dir/b
233 merging dir/b and dir/a to dir/b
234 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/6b2f4cece40f-503efe60-rebase.hg (glob)
234 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/6b2f4cece40f-503efe60-rebase.hg (glob)
235 $ cd ..
235 $ cd ..
236 $ rm -rf server
236 $ rm -rf server
237 $ rm -rf repo
237 $ rm -rf repo
238
238
239 Move file in one branch and delete it in another
239 Move file in one branch and delete it in another
240 $ hg init server
240 $ hg init server
241 $ initclient server
241 $ initclient server
242 $ cd server
242 $ cd server
243 $ echo a > a
243 $ echo a > a
244 $ hg add a
244 $ hg add a
245 $ hg ci -m initial
245 $ hg ci -m initial
246 $ cd ..
246 $ cd ..
247 $ hg clone -q server repo
247 $ hg clone -q server repo
248 $ initclient repo
248 $ initclient repo
249 $ cd repo
249 $ cd repo
250 $ hg mv a b
250 $ hg mv a b
251 $ hg ci -m 'mv a b'
251 $ hg ci -m 'mv a b'
252 $ hg up -q ".^"
252 $ hg up -q ".^"
253 $ hg rm a
253 $ hg rm a
254 $ hg ci -m 'del a'
254 $ hg ci -m 'del a'
255 created new head
255 created new head
256
256
257 $ hg log -G -T 'changeset: {node}\n desc: {desc}, phase: {phase}\n'
257 $ hg log -G -T 'changeset: {node}\n desc: {desc}, phase: {phase}\n'
258 @ changeset: 7d61ee3b1e48577891a072024968428ba465c47b
258 @ changeset: 7d61ee3b1e48577891a072024968428ba465c47b
259 | desc: del a, phase: draft
259 | desc: del a, phase: draft
260 | o changeset: 472e38d57782172f6c6abed82a94ca0d998c3a22
260 | o changeset: 472e38d57782172f6c6abed82a94ca0d998c3a22
261 |/ desc: mv a b, phase: draft
261 |/ desc: mv a b, phase: draft
262 o changeset: 1451231c87572a7d3f92fc210b4b35711c949a98
262 o changeset: 1451231c87572a7d3f92fc210b4b35711c949a98
263 desc: initial, phase: public
263 desc: initial, phase: public
264
264
265 $ hg rebase -s 1 -d 2
265 $ hg rebase -s 1 -d 2
266 rebasing 1:472e38d57782 "mv a b"
266 rebasing 1:472e38d57782 "mv a b"
267 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/472e38d57782-17d50e29-rebase.hg (glob)
267 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/472e38d57782-17d50e29-rebase.hg (glob)
268 $ hg up -q c492ed3c7e35dcd1dc938053b8adf56e2cfbd062
268 $ hg up -q c492ed3c7e35dcd1dc938053b8adf56e2cfbd062
269 $ ls
269 $ ls
270 b
270 b
271 $ cd ..
271 $ cd ..
272 $ rm -rf server
272 $ rm -rf server
273 $ rm -rf repo
273 $ rm -rf repo
274
274
275 Move a directory in draft branch
275 Move a directory in draft branch
276 $ hg init server
276 $ hg init server
277 $ initclient server
277 $ initclient server
278 $ cd server
278 $ cd server
279 $ mkdir dir
279 $ mkdir dir
280 $ echo a > dir/a
280 $ echo a > dir/a
281 $ hg add dir/a
281 $ hg add dir/a
282 $ hg ci -qm initial
282 $ hg ci -qm initial
283 $ cd ..
283 $ cd ..
284 $ hg clone -q server repo
284 $ hg clone -q server repo
285 $ initclient repo
285 $ initclient repo
286 $ cd repo
286 $ cd repo
287 $ echo b > dir/a
287 $ echo b > dir/a
288 $ hg ci -qm 'mod dir/a'
288 $ hg ci -qm 'mod dir/a'
289 $ hg up -q ".^"
289 $ hg up -q ".^"
290 $ hg mv -q dir/ dir2
290 $ hg mv -q dir/ dir2
291 $ hg ci -qm 'mv dir/ dir2/'
291 $ hg ci -qm 'mv dir/ dir2/'
292
292
293 $ hg log -G -T 'changeset: {node}\n desc: {desc}, phase: {phase}\n'
293 $ hg log -G -T 'changeset: {node}\n desc: {desc}, phase: {phase}\n'
294 @ changeset: a33d80b6e352591dfd82784e1ad6cdd86b25a239
294 @ changeset: a33d80b6e352591dfd82784e1ad6cdd86b25a239
295 | desc: mv dir/ dir2/, phase: draft
295 | desc: mv dir/ dir2/, phase: draft
296 | o changeset: 6b2f4cece40fd320f41229f23821256ffc08efea
296 | o changeset: 6b2f4cece40fd320f41229f23821256ffc08efea
297 |/ desc: mod dir/a, phase: draft
297 |/ desc: mod dir/a, phase: draft
298 o changeset: 36859b8907c513a3a87ae34ba5b1e7eea8c20944
298 o changeset: 36859b8907c513a3a87ae34ba5b1e7eea8c20944
299 desc: initial, phase: public
299 desc: initial, phase: public
300
300
301 $ hg rebase -s . -d 1
301 $ hg rebase -s . -d 1
302 rebasing 2:a33d80b6e352 "mv dir/ dir2/" (tip)
302 rebasing 2:a33d80b6e352 "mv dir/ dir2/" (tip)
303 merging dir/a and dir2/a to dir2/a
303 merging dir/a and dir2/a to dir2/a
304 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/a33d80b6e352-fecb9ada-rebase.hg (glob)
304 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/a33d80b6e352-fecb9ada-rebase.hg (glob)
305 $ cd ..
305 $ cd ..
306 $ rm -rf server
306 $ rm -rf server
307 $ rm -rf repo
307 $ rm -rf repo
308
308
309 Move file twice and rebase mod on top of moves
309 Move file twice and rebase mod on top of moves
310 $ hg init server
310 $ hg init server
311 $ initclient server
311 $ initclient server
312 $ cd server
312 $ cd server
313 $ echo a > a
313 $ echo a > a
314 $ hg add a
314 $ hg add a
315 $ hg ci -m initial
315 $ hg ci -m initial
316 $ hg mv a b
316 $ hg mv a b
317 $ hg ci -m 'mv a b'
317 $ hg ci -m 'mv a b'
318 $ hg mv b c
318 $ hg mv b c
319 $ hg ci -m 'mv b c'
319 $ hg ci -m 'mv b c'
320 $ cd ..
320 $ cd ..
321 $ hg clone -q server repo
321 $ hg clone -q server repo
322 $ initclient repo
322 $ initclient repo
323 $ cd repo
323 $ cd repo
324 $ hg up -q 0
324 $ hg up -q 0
325 $ echo c > a
325 $ echo c > a
326 $ hg ci -m 'mod a'
326 $ hg ci -m 'mod a'
327 created new head
327 created new head
328 $ hg log -G -T 'changeset: {node}\n desc: {desc}, phase: {phase}\n'
328 $ hg log -G -T 'changeset: {node}\n desc: {desc}, phase: {phase}\n'
329 @ changeset: d413169422167a3fa5275fc5d71f7dea9f5775f3
329 @ changeset: d413169422167a3fa5275fc5d71f7dea9f5775f3
330 | desc: mod a, phase: draft
330 | desc: mod a, phase: draft
331 | o changeset: d3efd280421d24f9f229997c19e654761c942a71
331 | o changeset: d3efd280421d24f9f229997c19e654761c942a71
332 | | desc: mv b c, phase: public
332 | | desc: mv b c, phase: public
333 | o changeset: 472e38d57782172f6c6abed82a94ca0d998c3a22
333 | o changeset: 472e38d57782172f6c6abed82a94ca0d998c3a22
334 |/ desc: mv a b, phase: public
334 |/ desc: mv a b, phase: public
335 o changeset: 1451231c87572a7d3f92fc210b4b35711c949a98
335 o changeset: 1451231c87572a7d3f92fc210b4b35711c949a98
336 desc: initial, phase: public
336 desc: initial, phase: public
337 $ hg rebase -s . -d 2
337 $ hg rebase -s . -d 2
338 rebasing 3:d41316942216 "mod a" (tip)
338 rebasing 3:d41316942216 "mod a" (tip)
339 merging c and a to c
339 merging c and a to c
340 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/d41316942216-2b5949bc-rebase.hg (glob)
340 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/d41316942216-2b5949bc-rebase.hg (glob)
341
341
342 $ cd ..
342 $ cd ..
343 $ rm -rf server
343 $ rm -rf server
344 $ rm -rf repo
344 $ rm -rf repo
345
345
346 Move file twice and rebase moves on top of mods
346 Move file twice and rebase moves on top of mods
347 $ hg init server
347 $ hg init server
348 $ initclient server
348 $ initclient server
349 $ cd server
349 $ cd server
350 $ echo a > a
350 $ echo a > a
351 $ hg add a
351 $ hg add a
352 $ hg ci -m initial
352 $ hg ci -m initial
353 $ cd ..
353 $ cd ..
354 $ hg clone -q server repo
354 $ hg clone -q server repo
355 $ initclient repo
355 $ initclient repo
356 $ cd repo
356 $ cd repo
357 $ hg mv a b
357 $ hg mv a b
358 $ hg ci -m 'mv a b'
358 $ hg ci -m 'mv a b'
359 $ hg mv b c
359 $ hg mv b c
360 $ hg ci -m 'mv b c'
360 $ hg ci -m 'mv b c'
361 $ hg up -q 0
361 $ hg up -q 0
362 $ echo c > a
362 $ echo c > a
363 $ hg ci -m 'mod a'
363 $ hg ci -m 'mod a'
364 created new head
364 created new head
365 $ hg log -G -T 'changeset: {node}\n desc: {desc}, phase: {phase}\n'
365 $ hg log -G -T 'changeset: {node}\n desc: {desc}, phase: {phase}\n'
366 @ changeset: d413169422167a3fa5275fc5d71f7dea9f5775f3
366 @ changeset: d413169422167a3fa5275fc5d71f7dea9f5775f3
367 | desc: mod a, phase: draft
367 | desc: mod a, phase: draft
368 | o changeset: d3efd280421d24f9f229997c19e654761c942a71
368 | o changeset: d3efd280421d24f9f229997c19e654761c942a71
369 | | desc: mv b c, phase: draft
369 | | desc: mv b c, phase: draft
370 | o changeset: 472e38d57782172f6c6abed82a94ca0d998c3a22
370 | o changeset: 472e38d57782172f6c6abed82a94ca0d998c3a22
371 |/ desc: mv a b, phase: draft
371 |/ desc: mv a b, phase: draft
372 o changeset: 1451231c87572a7d3f92fc210b4b35711c949a98
372 o changeset: 1451231c87572a7d3f92fc210b4b35711c949a98
373 desc: initial, phase: public
373 desc: initial, phase: public
374 $ hg rebase -s 1 -d .
374 $ hg rebase -s 1 -d .
375 rebasing 1:472e38d57782 "mv a b"
375 rebasing 1:472e38d57782 "mv a b"
376 merging a and b to b
376 merging a and b to b
377 rebasing 2:d3efd280421d "mv b c"
377 rebasing 2:d3efd280421d "mv b c"
378 merging b and c to c
378 merging b and c to c
379 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/472e38d57782-ab8d3c58-rebase.hg (glob)
379 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/472e38d57782-ab8d3c58-rebase.hg (glob)
380
380
381 $ cd ..
381 $ cd ..
382 $ rm -rf server
382 $ rm -rf server
383 $ rm -rf repo
383 $ rm -rf repo
384
384
385 Move one file and add another file in the same folder in one branch, modify file in another branch
385 Move one file and add another file in the same folder in one branch, modify file in another branch
386 $ hg init server
386 $ hg init server
387 $ initclient server
387 $ initclient server
388 $ cd server
388 $ cd server
389 $ echo a > a
389 $ echo a > a
390 $ hg add a
390 $ hg add a
391 $ hg ci -m initial
391 $ hg ci -m initial
392 $ hg mv a b
392 $ hg mv a b
393 $ hg ci -m 'mv a b'
393 $ hg ci -m 'mv a b'
394 $ echo c > c
394 $ echo c > c
395 $ hg add c
395 $ hg add c
396 $ hg ci -m 'add c'
396 $ hg ci -m 'add c'
397 $ cd ..
397 $ cd ..
398 $ hg clone -q server repo
398 $ hg clone -q server repo
399 $ initclient repo
399 $ initclient repo
400 $ cd repo
400 $ cd repo
401 $ hg up -q 0
401 $ hg up -q 0
402 $ echo b > a
402 $ echo b > a
403 $ hg ci -m 'mod a'
403 $ hg ci -m 'mod a'
404 created new head
404 created new head
405
405
406 $ hg log -G -T 'changeset: {node}\n desc: {desc}, phase: {phase}\n'
406 $ hg log -G -T 'changeset: {node}\n desc: {desc}, phase: {phase}\n'
407 @ changeset: ef716627c70bf4ca0bdb623cfb0d6fe5b9acc51e
407 @ changeset: ef716627c70bf4ca0bdb623cfb0d6fe5b9acc51e
408 | desc: mod a, phase: draft
408 | desc: mod a, phase: draft
409 | o changeset: b1a6187e79fbce851bb584eadcb0cc4a80290fd9
409 | o changeset: b1a6187e79fbce851bb584eadcb0cc4a80290fd9
410 | | desc: add c, phase: public
410 | | desc: add c, phase: public
411 | o changeset: 472e38d57782172f6c6abed82a94ca0d998c3a22
411 | o changeset: 472e38d57782172f6c6abed82a94ca0d998c3a22
412 |/ desc: mv a b, phase: public
412 |/ desc: mv a b, phase: public
413 o changeset: 1451231c87572a7d3f92fc210b4b35711c949a98
413 o changeset: 1451231c87572a7d3f92fc210b4b35711c949a98
414 desc: initial, phase: public
414 desc: initial, phase: public
415
415
416 $ hg rebase -s . -d 2
416 $ hg rebase -s . -d 2
417 rebasing 3:ef716627c70b "mod a" (tip)
417 rebasing 3:ef716627c70b "mod a" (tip)
418 merging b and a to b
418 merging b and a to b
419 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/ef716627c70b-24681561-rebase.hg (glob)
419 saved backup bundle to $TESTTMP/repo/.hg/strip-backup/ef716627c70b-24681561-rebase.hg (glob)
420 $ ls
420 $ ls
421 b
421 b
422 c
422 c
423 $ cat b
423 $ cat b
424 b
424 b
425
425
426 Merge test
426 Merge test
427 $ hg init server
427 $ hg init server
428 $ cd server
428 $ cd server
429 $ echo a > a
429 $ echo a > a
430 $ hg add a
430 $ hg add a
431 $ hg ci -m initial
431 $ hg ci -m initial
432 $ echo b > a
432 $ echo b > a
433 $ hg ci -m 'modify a'
433 $ hg ci -m 'modify a'
434 $ hg up -q 0
434 $ hg up -q 0
435 $ hg mv a b
435 $ hg mv a b
436 $ hg ci -m 'mv a b'
436 $ hg ci -m 'mv a b'
437 created new head
437 created new head
438 $ cd ..
438 $ cd ..
439 $ hg clone -q server repo
439 $ hg clone -q server repo
440 $ initclient repo
440 $ initclient repo
441 $ cd repo
441 $ cd repo
442 $ hg up -q 2
442 $ hg up -q 2
443
443
444 $ hg log -G -T 'changeset: {node}\n desc: {desc}, phase: {phase}\n'
444 $ hg log -G -T 'changeset: {node}\n desc: {desc}, phase: {phase}\n'
445 @ changeset: 472e38d57782172f6c6abed82a94ca0d998c3a22
445 @ changeset: 472e38d57782172f6c6abed82a94ca0d998c3a22
446 | desc: mv a b, phase: public
446 | desc: mv a b, phase: public
447 | o changeset: b0357b07f79129a3d08a68621271ca1352ae8a09
447 | o changeset: b0357b07f79129a3d08a68621271ca1352ae8a09
448 |/ desc: modify a, phase: public
448 |/ desc: modify a, phase: public
449 o changeset: 1451231c87572a7d3f92fc210b4b35711c949a98
449 o changeset: 1451231c87572a7d3f92fc210b4b35711c949a98
450 desc: initial, phase: public
450 desc: initial, phase: public
451
451
452 $ hg merge 1
452 $ hg merge 1
453 merging b and a to b
453 merging b and a to b
454 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
454 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
455 (branch merge, don't forget to commit)
455 (branch merge, don't forget to commit)
456 $ hg ci -m merge
456 $ hg ci -m merge
457 $ ls
457 $ ls
458 b
458 b
459 $ cd ..
459 $ cd ..
460 $ rm -rf server
460 $ rm -rf server
461 $ rm -rf repo
461 $ rm -rf repo
462
462
463 Copy and move file
463 Copy and move file
464 $ hg init server
464 $ hg init server
465 $ initclient server
465 $ initclient server
466 $ cd server
466 $ cd server
467 $ echo a > a
467 $ echo a > a
468 $ hg add a
468 $ hg add a
469 $ hg ci -m initial
469 $ hg ci -m initial
470 $ hg cp a c
470 $ hg cp a c
471 $ hg mv a b
471 $ hg mv a b
472 $ hg ci -m 'cp a c, mv a b'
472 $ hg ci -m 'cp a c, mv a b'
473 $ cd ..
473 $ cd ..
474 $ hg clone -q server repo
474 $ hg clone -q server repo
475 $ initclient repo
475 $ initclient repo
476 $ cd repo
476 $ cd repo
477 $ hg up -q 0
477 $ hg up -q 0
478 $ echo b > a
478 $ echo b > a
479 $ hg ci -m 'mod a'
479 $ hg ci -m 'mod a'
480 created new head
480 created new head
481
481
482 $ hg log -G -T 'changeset: {node}\n desc: {desc}, phase: {phase}\n'
482 $ hg log -G -T 'changeset: {node}\n desc: {desc}, phase: {phase}\n'
483 @ changeset: ef716627c70bf4ca0bdb623cfb0d6fe5b9acc51e
483 @ changeset: ef716627c70bf4ca0bdb623cfb0d6fe5b9acc51e
484 | desc: mod a, phase: draft
484 | desc: mod a, phase: draft
485 | o changeset: 4fc3fd13fbdb89ada6b75bfcef3911a689a0dde8
485 | o changeset: 4fc3fd13fbdb89ada6b75bfcef3911a689a0dde8
486 |/ desc: cp a c, mv a b, phase: public
486 |/ desc: cp a c, mv a b, phase: public
487 o changeset: 1451231c87572a7d3f92fc210b4b35711c949a98
487 o changeset: 1451231c87572a7d3f92fc210b4b35711c949a98
488 desc: initial, phase: public
488 desc: initial, phase: public
489
489
490 $ hg rebase -s . -d 1
490 $ hg rebase -s . -d 1
491 rebasing 2:ef716627c70b "mod a" (tip)
491 rebasing 2:ef716627c70b "mod a" (tip)
492 merging b and a to b
492 merging b and a to b
493 merging c and a to c
493 merging c and a to c
494 saved backup bundle to $TESTTMP/repo/repo/.hg/strip-backup/ef716627c70b-24681561-rebase.hg (glob)
494 saved backup bundle to $TESTTMP/repo/repo/.hg/strip-backup/ef716627c70b-24681561-rebase.hg (glob)
495 $ ls
495 $ ls
496 b
496 b
497 c
497 c
498 $ cat b
498 $ cat b
499 b
499 b
500 $ cat c
500 $ cat c
501 b
501 b
502 $ cd ..
502 $ cd ..
503 $ rm -rf server
503 $ rm -rf server
504 $ rm -rf repo
504 $ rm -rf repo
505
505
506 Do a merge commit with many consequent moves in one branch
506 Do a merge commit with many consequent moves in one branch
507 $ hg init server
507 $ hg init server
508 $ initclient server
508 $ initclient server
509 $ cd server
509 $ cd server
510 $ echo a > a
510 $ echo a > a
511 $ hg add a
511 $ hg add a
512 $ hg ci -m initial
512 $ hg ci -m initial
513 $ echo b > a
513 $ echo b > a
514 $ hg ci -qm 'mod a'
514 $ hg ci -qm 'mod a'
515 $ cd ..
515 $ cd ..
516 $ hg clone -q server repo
516 $ hg clone -q server repo
517 $ initclient repo
517 $ initclient repo
518 $ cd repo
518 $ cd repo
519 $ hg up -q ".^"
519 $ hg up -q ".^"
520 $ hg mv a b
520 $ hg mv a b
521 $ hg ci -qm 'mv a b'
521 $ hg ci -qm 'mv a b'
522 $ hg mv b c
522 $ hg mv b c
523 $ hg ci -qm 'mv b c'
523 $ hg ci -qm 'mv b c'
524 $ hg up -q 1
524 $ hg up -q 1
525 $ hg log -G -T 'changeset: {node}\n desc: {desc}, phase: {phase}\n'
525 $ hg log -G -T 'changeset: {node}\n desc: {desc}, phase: {phase}\n'
526 o changeset: d3efd280421d24f9f229997c19e654761c942a71
526 o changeset: d3efd280421d24f9f229997c19e654761c942a71
527 | desc: mv b c, phase: draft
527 | desc: mv b c, phase: draft
528 o changeset: 472e38d57782172f6c6abed82a94ca0d998c3a22
528 o changeset: 472e38d57782172f6c6abed82a94ca0d998c3a22
529 | desc: mv a b, phase: draft
529 | desc: mv a b, phase: draft
530 | @ changeset: ef716627c70bf4ca0bdb623cfb0d6fe5b9acc51e
530 | @ changeset: ef716627c70bf4ca0bdb623cfb0d6fe5b9acc51e
531 |/ desc: mod a, phase: public
531 |/ desc: mod a, phase: public
532 o changeset: 1451231c87572a7d3f92fc210b4b35711c949a98
532 o changeset: 1451231c87572a7d3f92fc210b4b35711c949a98
533 desc: initial, phase: public
533 desc: initial, phase: public
534
534
535 $ hg merge 3
535 $ hg merge 3
536 merging a and c to c
536 merging a and c to c
537 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
537 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
538 (branch merge, don't forget to commit)
538 (branch merge, don't forget to commit)
539 $ hg ci -qm 'merge'
539 $ hg ci -qm 'merge'
540 $ hg log -G -T 'changeset: {node}\n desc: {desc}, phase: {phase}\n'
540 $ hg log -G -T 'changeset: {node}\n desc: {desc}, phase: {phase}\n'
541 @ changeset: cd29b0d08c0f39bfed4cde1b40e30f419db0c825
541 @ changeset: cd29b0d08c0f39bfed4cde1b40e30f419db0c825
542 |\ desc: merge, phase: draft
542 |\ desc: merge, phase: draft
543 | o changeset: d3efd280421d24f9f229997c19e654761c942a71
543 | o changeset: d3efd280421d24f9f229997c19e654761c942a71
544 | | desc: mv b c, phase: draft
544 | | desc: mv b c, phase: draft
545 | o changeset: 472e38d57782172f6c6abed82a94ca0d998c3a22
545 | o changeset: 472e38d57782172f6c6abed82a94ca0d998c3a22
546 | | desc: mv a b, phase: draft
546 | | desc: mv a b, phase: draft
547 o | changeset: ef716627c70bf4ca0bdb623cfb0d6fe5b9acc51e
547 o | changeset: ef716627c70bf4ca0bdb623cfb0d6fe5b9acc51e
548 |/ desc: mod a, phase: public
548 |/ desc: mod a, phase: public
549 o changeset: 1451231c87572a7d3f92fc210b4b35711c949a98
549 o changeset: 1451231c87572a7d3f92fc210b4b35711c949a98
550 desc: initial, phase: public
550 desc: initial, phase: public
551 $ ls
551 $ ls
552 c
552 c
553 $ cd ..
553 $ cd ..
554 $ rm -rf server
554 $ rm -rf server
555 $ rm -rf repo
555 $ rm -rf repo
556
556
557 Test shelve/unshelve
557 Test shelve/unshelve
558 $ hg init server
558 $ hg init server
559 $ initclient server
559 $ initclient server
560 $ cd server
560 $ cd server
561 $ echo a > a
561 $ echo a > a
562 $ hg add a
562 $ hg add a
563 $ hg ci -m initial
563 $ hg ci -m initial
564 $ cd ..
564 $ cd ..
565 $ hg clone -q server repo
565 $ hg clone -q server repo
566 $ initclient repo
566 $ initclient repo
567 $ cd repo
567 $ cd repo
568 $ echo b > a
568 $ echo b > a
569 $ hg shelve
569 $ hg shelve
570 shelved as default
570 shelved as default
571 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
571 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
572 $ hg mv a b
572 $ hg mv a b
573 $ hg ci -m 'mv a b'
573 $ hg ci -m 'mv a b'
574
574
575 $ hg log -G -T 'changeset: {node}\n desc: {desc}, phase: {phase}\n'
575 $ hg log -G -T 'changeset: {node}\n desc: {desc}, phase: {phase}\n'
576 @ changeset: 472e38d57782172f6c6abed82a94ca0d998c3a22
576 @ changeset: 472e38d57782172f6c6abed82a94ca0d998c3a22
577 | desc: mv a b, phase: draft
577 | desc: mv a b, phase: draft
578 o changeset: 1451231c87572a7d3f92fc210b4b35711c949a98
578 o changeset: 1451231c87572a7d3f92fc210b4b35711c949a98
579 desc: initial, phase: public
579 desc: initial, phase: public
580 $ hg unshelve
580 $ hg unshelve
581 unshelving change 'default'
581 unshelving change 'default'
582 rebasing shelved changes
582 rebasing shelved changes
583 rebasing 2:45f63161acea "changes to: initial" (tip)
583 rebasing 2:45f63161acea "changes to: initial" (tip)
584 merging b and a to b
584 merging b and a to b
585 $ ls
585 $ ls
586 b
586 b
587 $ cat b
587 $ cat b
588 b
588 b
589 $ cd ..
589 $ cd ..
590 $ rm -rf server
590 $ rm -rf server
591 $ rm -rf repo
591 $ rm -rf repo
592
592
593 Test full copytrace ability on draft branch
593 Test full copytrace ability on draft branch
594 -------------------------------------------
594 -------------------------------------------
595
595
596 File directory and base name changed in same move
596 File directory and base name changed in same move
597 $ hg init repo
597 $ hg init repo
598 $ initclient repo
598 $ initclient repo
599 $ mkdir repo/dir1
599 $ mkdir repo/dir1
600 $ cd repo/dir1
600 $ cd repo/dir1
601 $ echo a > a
601 $ echo a > a
602 $ hg add a
602 $ hg add a
603 $ hg ci -qm initial
603 $ hg ci -qm initial
604 $ cd ..
604 $ cd ..
605 $ hg mv -q dir1 dir2
605 $ hg mv -q dir1 dir2
606 $ hg mv dir2/a dir2/b
606 $ hg mv dir2/a dir2/b
607 $ hg ci -qm 'mv a b; mv dir1 dir2'
607 $ hg ci -qm 'mv a b; mv dir1 dir2'
608 $ hg up -q '.^'
608 $ hg up -q '.^'
609 $ cd dir1
609 $ cd dir1
610 $ echo b >> a
610 $ echo b >> a
611 $ cd ..
611 $ cd ..
612 $ hg ci -qm 'mod a'
612 $ hg ci -qm 'mod a'
613
613
614 $ hg log -G -T 'changeset {node}\n desc {desc}, phase: {phase}\n'
614 $ hg log -G -T 'changeset {node}\n desc {desc}, phase: {phase}\n'
615 @ changeset 6207d2d318e710b882e3d5ada2a89770efc42c96
615 @ changeset 6207d2d318e710b882e3d5ada2a89770efc42c96
616 | desc mod a, phase: draft
616 | desc mod a, phase: draft
617 | o changeset abffdd4e3dfc04bc375034b970299b2a309a1cce
617 | o changeset abffdd4e3dfc04bc375034b970299b2a309a1cce
618 |/ desc mv a b; mv dir1 dir2, phase: draft
618 |/ desc mv a b; mv dir1 dir2, phase: draft
619 o changeset 81973cd24b58db2fdf18ce3d64fb2cc3284e9ab3
619 o changeset 81973cd24b58db2fdf18ce3d64fb2cc3284e9ab3
620 desc initial, phase: draft
620 desc initial, phase: draft
621
621
622 $ hg rebase -s . -d 1
622 $ hg rebase -s . -d 1
623 rebasing 2:6207d2d318e7 "mod a" (tip)
623 rebasing 2:6207d2d318e7 "mod a" (tip)
624 merging dir2/b and dir1/a to dir2/b
624 merging dir2/b and dir1/a to dir2/b
625 saved backup bundle to $TESTTMP/repo/repo/.hg/strip-backup/6207d2d318e7-1c9779ad-rebase.hg (glob)
625 saved backup bundle to $TESTTMP/repo/repo/.hg/strip-backup/6207d2d318e7-1c9779ad-rebase.hg (glob)
626 $ cat dir2/b
626 $ cat dir2/b
627 a
627 a
628 b
628 b
629 $ cd ..
629 $ cd ..
630 $ rm -rf server
630 $ rm -rf server
631 $ rm -rf repo
631 $ rm -rf repo
632
632
633 Move directory in one merge parent, while adding file to original directory
633 Move directory in one merge parent, while adding file to original directory
634 in other merge parent. File moved on rebase.
634 in other merge parent. File moved on rebase.
635 $ hg init repo
635 $ hg init repo
636 $ initclient repo
636 $ initclient repo
637 $ mkdir repo/dir1
637 $ mkdir repo/dir1
638 $ cd repo/dir1
638 $ cd repo/dir1
639 $ echo dummy > dummy
639 $ echo dummy > dummy
640 $ hg add dummy
640 $ hg add dummy
641 $ cd ..
641 $ cd ..
642 $ hg ci -qm initial
642 $ hg ci -qm initial
643 $ cd dir1
643 $ cd dir1
644 $ echo a > a
644 $ echo a > a
645 $ hg add a
645 $ hg add a
646 $ cd ..
646 $ cd ..
647 $ hg ci -qm 'hg add dir1/a'
647 $ hg ci -qm 'hg add dir1/a'
648 $ hg up -q '.^'
648 $ hg up -q '.^'
649 $ hg mv -q dir1 dir2
649 $ hg mv -q dir1 dir2
650 $ hg ci -qm 'mv dir1 dir2'
650 $ hg ci -qm 'mv dir1 dir2'
651
651
652 $ hg log -G -T 'changeset {node}\n desc {desc}, phase: {phase}\n'
652 $ hg log -G -T 'changeset {node}\n desc {desc}, phase: {phase}\n'
653 @ changeset e8919e7df8d036e07b906045eddcd4a42ff1915f
653 @ changeset e8919e7df8d036e07b906045eddcd4a42ff1915f
654 | desc mv dir1 dir2, phase: draft
654 | desc mv dir1 dir2, phase: draft
655 | o changeset 7c7c6f339be00f849c3cb2df738ca91db78b32c8
655 | o changeset 7c7c6f339be00f849c3cb2df738ca91db78b32c8
656 |/ desc hg add dir1/a, phase: draft
656 |/ desc hg add dir1/a, phase: draft
657 o changeset a235dcce55dcf42034c4e374cb200662d0bb4a13
657 o changeset a235dcce55dcf42034c4e374cb200662d0bb4a13
658 desc initial, phase: draft
658 desc initial, phase: draft
659
659
660 $ hg rebase -s . -d 1
660 $ hg rebase -s . -d 1
661 rebasing 2:e8919e7df8d0 "mv dir1 dir2" (tip)
661 rebasing 2:e8919e7df8d0 "mv dir1 dir2" (tip)
662 saved backup bundle to $TESTTMP/repo/repo/.hg/strip-backup/e8919e7df8d0-f62fab62-rebase.hg (glob)
662 saved backup bundle to $TESTTMP/repo/repo/.hg/strip-backup/e8919e7df8d0-f62fab62-rebase.hg (glob)
663 $ ls dir2
663 $ ls dir2
664 a
664 a
665 dummy
665 dummy
666 $ rm -rf server
666 $ rm -rf server
667 $ rm -rf repo
667 $ rm -rf repo
668
669 Testing the sourcecommitlimit config
670
671 $ hg init repo
672 $ initclient repo
673 $ cd repo
674 $ echo a > a
675 $ hg ci -Aqm "added a"
676 $ echo "more things" >> a
677 $ hg ci -qm "added more things to a"
678 $ hg up 0
679 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
680 $ echo b > b
681 $ hg ci -Aqm "added b"
682 $ mkdir foo
683 $ hg mv a foo/bar
684 $ hg ci -m "Moved a to foo/bar"
685 $ hg log -G -T 'changeset {node}\n desc {desc}, phase: {phase}\n'
686 @ changeset b4b0f7880e500b5c364a5f07b4a2b167de7a6fb0
687 | desc Moved a to foo/bar, phase: draft
688 o changeset 5f6d8a4bf34ab274ccc9f631c2536964b8a3666d
689 | desc added b, phase: draft
690 | o changeset 8b6e13696c38e8445a759516474640c2f8dddef6
691 |/ desc added more things to a, phase: draft
692 o changeset 9092f1db7931481f93b37d5c9fbcfc341bcd7318
693 desc added a, phase: draft
694
695 When the sourcecommitlimit is small and we have more drafts, we use heuristics only
696
697 $ hg rebase -s 8b6e13696 -d . --config experimental.copytrace.sourcecommitlimit=0
698 rebasing 1:8b6e13696c38 "added more things to a"
699 other [source] changed a which local [dest] deleted
700 use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u
701 unresolved conflicts (see hg resolve, then hg rebase --continue)
702 [1]
703
704 But when we have "sourcecommitlimit > (no. of drafts from base to c1)", we do
705 fullcopytracing
706
707 $ hg rebase --abort
708 rebase aborted
709 $ hg rebase -s 8b6e13696 -d .
710 rebasing 1:8b6e13696c38 "added more things to a"
711 merging foo/bar and a to foo/bar
712 saved backup bundle to $TESTTMP/repo/repo/repo/.hg/strip-backup/8b6e13696c38-fc14ac83-rebase.hg (glob)
713 $ cd ..
714 $ rm -rf repo
General Comments 0
You need to be logged in to leave comments. Login now