##// END OF EJS Templates
configitems: register the 'ui.mergemarkertemplate' config
Boris Feld -
r33523:11025c4f default
parent child Browse files
Show More
@@ -1,578 +1,586 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 error,
13 error,
14 )
14 )
15
15
16 def loadconfigtable(ui, extname, configtable):
16 def loadconfigtable(ui, extname, configtable):
17 """update config item known to the ui with the extension ones"""
17 """update config item known to the ui with the extension ones"""
18 for section, items in configtable.items():
18 for section, items in configtable.items():
19 knownitems = ui._knownconfig.setdefault(section, {})
19 knownitems = ui._knownconfig.setdefault(section, {})
20 knownkeys = set(knownitems)
20 knownkeys = set(knownitems)
21 newkeys = set(items)
21 newkeys = set(items)
22 for key in sorted(knownkeys & newkeys):
22 for key in sorted(knownkeys & newkeys):
23 msg = "extension '%s' overwrite config item '%s.%s'"
23 msg = "extension '%s' overwrite config item '%s.%s'"
24 msg %= (extname, section, key)
24 msg %= (extname, section, key)
25 ui.develwarn(msg, config='warn-config')
25 ui.develwarn(msg, config='warn-config')
26
26
27 knownitems.update(items)
27 knownitems.update(items)
28
28
29 class configitem(object):
29 class configitem(object):
30 """represent a known config item
30 """represent a known config item
31
31
32 :section: the official config section where to find this item,
32 :section: the official config section where to find this item,
33 :name: the official name within the section,
33 :name: the official name within the section,
34 :default: default value for this item,
34 :default: default value for this item,
35 :alias: optional list of tuples as alternatives.
35 :alias: optional list of tuples as alternatives.
36 """
36 """
37
37
38 def __init__(self, section, name, default=None, alias=()):
38 def __init__(self, section, name, default=None, alias=()):
39 self.section = section
39 self.section = section
40 self.name = name
40 self.name = name
41 self.default = default
41 self.default = default
42 self.alias = list(alias)
42 self.alias = list(alias)
43
43
44 coreitems = {}
44 coreitems = {}
45
45
46 def _register(configtable, *args, **kwargs):
46 def _register(configtable, *args, **kwargs):
47 item = configitem(*args, **kwargs)
47 item = configitem(*args, **kwargs)
48 section = configtable.setdefault(item.section, {})
48 section = configtable.setdefault(item.section, {})
49 if item.name in section:
49 if item.name in section:
50 msg = "duplicated config item registration for '%s.%s'"
50 msg = "duplicated config item registration for '%s.%s'"
51 raise error.ProgrammingError(msg % (item.section, item.name))
51 raise error.ProgrammingError(msg % (item.section, item.name))
52 section[item.name] = item
52 section[item.name] = item
53
53
54 # special value for case where the default is derived from other values
54 # special value for case where the default is derived from other values
55 dynamicdefault = object()
55 dynamicdefault = object()
56
56
57 # Registering actual config items
57 # Registering actual config items
58
58
59 def getitemregister(configtable):
59 def getitemregister(configtable):
60 return functools.partial(_register, configtable)
60 return functools.partial(_register, configtable)
61
61
62 coreconfigitem = getitemregister(coreitems)
62 coreconfigitem = getitemregister(coreitems)
63
63
64 coreconfigitem('auth', 'cookiefile',
64 coreconfigitem('auth', 'cookiefile',
65 default=None,
65 default=None,
66 )
66 )
67 # bookmarks.pushing: internal hack for discovery
67 # bookmarks.pushing: internal hack for discovery
68 coreconfigitem('bookmarks', 'pushing',
68 coreconfigitem('bookmarks', 'pushing',
69 default=list,
69 default=list,
70 )
70 )
71 # bundle.mainreporoot: internal hack for bundlerepo
71 # bundle.mainreporoot: internal hack for bundlerepo
72 coreconfigitem('bundle', 'mainreporoot',
72 coreconfigitem('bundle', 'mainreporoot',
73 default='',
73 default='',
74 )
74 )
75 # bundle.reorder: experimental config
75 # bundle.reorder: experimental config
76 coreconfigitem('bundle', 'reorder',
76 coreconfigitem('bundle', 'reorder',
77 default='auto',
77 default='auto',
78 )
78 )
79 coreconfigitem('censor', 'policy',
79 coreconfigitem('censor', 'policy',
80 default='abort',
80 default='abort',
81 )
81 )
82 coreconfigitem('chgserver', 'idletimeout',
82 coreconfigitem('chgserver', 'idletimeout',
83 default=3600,
83 default=3600,
84 )
84 )
85 coreconfigitem('chgserver', 'skiphash',
85 coreconfigitem('chgserver', 'skiphash',
86 default=False,
86 default=False,
87 )
87 )
88 coreconfigitem('cmdserver', 'log',
88 coreconfigitem('cmdserver', 'log',
89 default=None,
89 default=None,
90 )
90 )
91 coreconfigitem('color', 'mode',
91 coreconfigitem('color', 'mode',
92 default='auto',
92 default='auto',
93 )
93 )
94 coreconfigitem('color', 'pagermode',
94 coreconfigitem('color', 'pagermode',
95 default=dynamicdefault,
95 default=dynamicdefault,
96 )
96 )
97 coreconfigitem('commands', 'status.relative',
97 coreconfigitem('commands', 'status.relative',
98 default=False,
98 default=False,
99 )
99 )
100 coreconfigitem('commands', 'update.requiredest',
100 coreconfigitem('commands', 'update.requiredest',
101 default=False,
101 default=False,
102 )
102 )
103 coreconfigitem('devel', 'all-warnings',
103 coreconfigitem('devel', 'all-warnings',
104 default=False,
104 default=False,
105 )
105 )
106 coreconfigitem('devel', 'bundle2.debug',
106 coreconfigitem('devel', 'bundle2.debug',
107 default=False,
107 default=False,
108 )
108 )
109 coreconfigitem('devel', 'check-locks',
109 coreconfigitem('devel', 'check-locks',
110 default=False,
110 default=False,
111 )
111 )
112 coreconfigitem('devel', 'check-relroot',
112 coreconfigitem('devel', 'check-relroot',
113 default=False,
113 default=False,
114 )
114 )
115 coreconfigitem('devel', 'default-date',
115 coreconfigitem('devel', 'default-date',
116 default=None,
116 default=None,
117 )
117 )
118 coreconfigitem('devel', 'deprec-warn',
118 coreconfigitem('devel', 'deprec-warn',
119 default=False,
119 default=False,
120 )
120 )
121 coreconfigitem('devel', 'disableloaddefaultcerts',
121 coreconfigitem('devel', 'disableloaddefaultcerts',
122 default=False,
122 default=False,
123 )
123 )
124 coreconfigitem('devel', 'legacy.exchange',
124 coreconfigitem('devel', 'legacy.exchange',
125 default=list,
125 default=list,
126 )
126 )
127 coreconfigitem('devel', 'servercafile',
127 coreconfigitem('devel', 'servercafile',
128 default='',
128 default='',
129 )
129 )
130 coreconfigitem('devel', 'serverexactprotocol',
130 coreconfigitem('devel', 'serverexactprotocol',
131 default='',
131 default='',
132 )
132 )
133 coreconfigitem('devel', 'serverrequirecert',
133 coreconfigitem('devel', 'serverrequirecert',
134 default=False,
134 default=False,
135 )
135 )
136 coreconfigitem('devel', 'strip-obsmarkers',
136 coreconfigitem('devel', 'strip-obsmarkers',
137 default=True,
137 default=True,
138 )
138 )
139 coreconfigitem('email', 'charsets',
139 coreconfigitem('email', 'charsets',
140 default=list,
140 default=list,
141 )
141 )
142 coreconfigitem('email', 'method',
142 coreconfigitem('email', 'method',
143 default='smtp',
143 default='smtp',
144 )
144 )
145 coreconfigitem('experimental', 'bundle-phases',
145 coreconfigitem('experimental', 'bundle-phases',
146 default=False,
146 default=False,
147 )
147 )
148 coreconfigitem('experimental', 'bundle2-advertise',
148 coreconfigitem('experimental', 'bundle2-advertise',
149 default=True,
149 default=True,
150 )
150 )
151 coreconfigitem('experimental', 'bundle2-output-capture',
151 coreconfigitem('experimental', 'bundle2-output-capture',
152 default=False,
152 default=False,
153 )
153 )
154 coreconfigitem('experimental', 'bundle2.pushback',
154 coreconfigitem('experimental', 'bundle2.pushback',
155 default=False,
155 default=False,
156 )
156 )
157 coreconfigitem('experimental', 'bundle2lazylocking',
157 coreconfigitem('experimental', 'bundle2lazylocking',
158 default=False,
158 default=False,
159 )
159 )
160 coreconfigitem('experimental', 'bundlecomplevel',
160 coreconfigitem('experimental', 'bundlecomplevel',
161 default=None,
161 default=None,
162 )
162 )
163 coreconfigitem('experimental', 'changegroup3',
163 coreconfigitem('experimental', 'changegroup3',
164 default=False,
164 default=False,
165 )
165 )
166 coreconfigitem('experimental', 'clientcompressionengines',
166 coreconfigitem('experimental', 'clientcompressionengines',
167 default=list,
167 default=list,
168 )
168 )
169 coreconfigitem('experimental', 'crecordtest',
169 coreconfigitem('experimental', 'crecordtest',
170 default=None,
170 default=None,
171 )
171 )
172 coreconfigitem('experimental', 'disablecopytrace',
172 coreconfigitem('experimental', 'disablecopytrace',
173 default=False,
173 default=False,
174 )
174 )
175 coreconfigitem('experimental', 'editortmpinhg',
175 coreconfigitem('experimental', 'editortmpinhg',
176 default=False,
176 default=False,
177 )
177 )
178 coreconfigitem('experimental', 'evolution',
178 coreconfigitem('experimental', 'evolution',
179 default=list,
179 default=list,
180 )
180 )
181 coreconfigitem('experimental', 'evolution.bundle-obsmarker',
181 coreconfigitem('experimental', 'evolution.bundle-obsmarker',
182 default=False,
182 default=False,
183 )
183 )
184 coreconfigitem('experimental', 'evolution.track-operation',
184 coreconfigitem('experimental', 'evolution.track-operation',
185 default=False,
185 default=False,
186 )
186 )
187 coreconfigitem('experimental', 'exportableenviron',
187 coreconfigitem('experimental', 'exportableenviron',
188 default=list,
188 default=list,
189 )
189 )
190 coreconfigitem('experimental', 'extendedheader.index',
190 coreconfigitem('experimental', 'extendedheader.index',
191 default=None,
191 default=None,
192 )
192 )
193 coreconfigitem('experimental', 'extendedheader.similarity',
193 coreconfigitem('experimental', 'extendedheader.similarity',
194 default=False,
194 default=False,
195 )
195 )
196 coreconfigitem('experimental', 'format.compression',
196 coreconfigitem('experimental', 'format.compression',
197 default='zlib',
197 default='zlib',
198 )
198 )
199 coreconfigitem('experimental', 'graphshorten',
199 coreconfigitem('experimental', 'graphshorten',
200 default=False,
200 default=False,
201 )
201 )
202 coreconfigitem('experimental', 'hook-track-tags',
202 coreconfigitem('experimental', 'hook-track-tags',
203 default=False,
203 default=False,
204 )
204 )
205 coreconfigitem('experimental', 'httppostargs',
205 coreconfigitem('experimental', 'httppostargs',
206 default=False,
206 default=False,
207 )
207 )
208 coreconfigitem('experimental', 'manifestv2',
208 coreconfigitem('experimental', 'manifestv2',
209 default=False,
209 default=False,
210 )
210 )
211 coreconfigitem('experimental', 'mergedriver',
211 coreconfigitem('experimental', 'mergedriver',
212 default=None,
212 default=None,
213 )
213 )
214 coreconfigitem('experimental', 'obsmarkers-exchange-debug',
214 coreconfigitem('experimental', 'obsmarkers-exchange-debug',
215 default=False,
215 default=False,
216 )
216 )
217 coreconfigitem('experimental', 'revertalternateinteractivemode',
217 coreconfigitem('experimental', 'revertalternateinteractivemode',
218 default=True,
218 default=True,
219 )
219 )
220 coreconfigitem('experimental', 'revlogv2',
220 coreconfigitem('experimental', 'revlogv2',
221 default=None,
221 default=None,
222 )
222 )
223 coreconfigitem('experimental', 'spacemovesdown',
223 coreconfigitem('experimental', 'spacemovesdown',
224 default=False,
224 default=False,
225 )
225 )
226 coreconfigitem('experimental', 'treemanifest',
226 coreconfigitem('experimental', 'treemanifest',
227 default=False,
227 default=False,
228 )
228 )
229 coreconfigitem('experimental', 'updatecheck',
229 coreconfigitem('experimental', 'updatecheck',
230 default=None,
230 default=None,
231 )
231 )
232 coreconfigitem('format', 'aggressivemergedeltas',
232 coreconfigitem('format', 'aggressivemergedeltas',
233 default=False,
233 default=False,
234 )
234 )
235 coreconfigitem('format', 'chunkcachesize',
235 coreconfigitem('format', 'chunkcachesize',
236 default=None,
236 default=None,
237 )
237 )
238 coreconfigitem('format', 'dotencode',
238 coreconfigitem('format', 'dotencode',
239 default=True,
239 default=True,
240 )
240 )
241 coreconfigitem('format', 'generaldelta',
241 coreconfigitem('format', 'generaldelta',
242 default=False,
242 default=False,
243 )
243 )
244 coreconfigitem('format', 'manifestcachesize',
244 coreconfigitem('format', 'manifestcachesize',
245 default=None,
245 default=None,
246 )
246 )
247 coreconfigitem('format', 'maxchainlen',
247 coreconfigitem('format', 'maxchainlen',
248 default=None,
248 default=None,
249 )
249 )
250 coreconfigitem('format', 'obsstore-version',
250 coreconfigitem('format', 'obsstore-version',
251 default=None,
251 default=None,
252 )
252 )
253 coreconfigitem('format', 'usefncache',
253 coreconfigitem('format', 'usefncache',
254 default=True,
254 default=True,
255 )
255 )
256 coreconfigitem('format', 'usegeneraldelta',
256 coreconfigitem('format', 'usegeneraldelta',
257 default=True,
257 default=True,
258 )
258 )
259 coreconfigitem('format', 'usestore',
259 coreconfigitem('format', 'usestore',
260 default=True,
260 default=True,
261 )
261 )
262 coreconfigitem('hostsecurity', 'ciphers',
262 coreconfigitem('hostsecurity', 'ciphers',
263 default=None,
263 default=None,
264 )
264 )
265 coreconfigitem('hostsecurity', 'disabletls10warning',
265 coreconfigitem('hostsecurity', 'disabletls10warning',
266 default=False,
266 default=False,
267 )
267 )
268 coreconfigitem('http_proxy', 'always',
268 coreconfigitem('http_proxy', 'always',
269 default=False,
269 default=False,
270 )
270 )
271 coreconfigitem('http_proxy', 'host',
271 coreconfigitem('http_proxy', 'host',
272 default=None,
272 default=None,
273 )
273 )
274 coreconfigitem('http_proxy', 'no',
274 coreconfigitem('http_proxy', 'no',
275 default=list,
275 default=list,
276 )
276 )
277 coreconfigitem('http_proxy', 'passwd',
277 coreconfigitem('http_proxy', 'passwd',
278 default=None,
278 default=None,
279 )
279 )
280 coreconfigitem('http_proxy', 'user',
280 coreconfigitem('http_proxy', 'user',
281 default=None,
281 default=None,
282 )
282 )
283 coreconfigitem('merge', 'followcopies',
283 coreconfigitem('merge', 'followcopies',
284 default=True,
284 default=True,
285 )
285 )
286 coreconfigitem('pager', 'ignore',
286 coreconfigitem('pager', 'ignore',
287 default=list,
287 default=list,
288 )
288 )
289 coreconfigitem('patch', 'eol',
289 coreconfigitem('patch', 'eol',
290 default='strict',
290 default='strict',
291 )
291 )
292 coreconfigitem('patch', 'fuzz',
292 coreconfigitem('patch', 'fuzz',
293 default=2,
293 default=2,
294 )
294 )
295 coreconfigitem('paths', 'default',
295 coreconfigitem('paths', 'default',
296 default=None,
296 default=None,
297 )
297 )
298 coreconfigitem('paths', 'default-push',
298 coreconfigitem('paths', 'default-push',
299 default=None,
299 default=None,
300 )
300 )
301 coreconfigitem('phases', 'checksubrepos',
301 coreconfigitem('phases', 'checksubrepos',
302 default='follow',
302 default='follow',
303 )
303 )
304 coreconfigitem('phases', 'publish',
304 coreconfigitem('phases', 'publish',
305 default=True,
305 default=True,
306 )
306 )
307 coreconfigitem('profiling', 'enabled',
307 coreconfigitem('profiling', 'enabled',
308 default=False,
308 default=False,
309 )
309 )
310 coreconfigitem('profiling', 'format',
310 coreconfigitem('profiling', 'format',
311 default='text',
311 default='text',
312 )
312 )
313 coreconfigitem('profiling', 'freq',
313 coreconfigitem('profiling', 'freq',
314 default=1000,
314 default=1000,
315 )
315 )
316 coreconfigitem('profiling', 'limit',
316 coreconfigitem('profiling', 'limit',
317 default=30,
317 default=30,
318 )
318 )
319 coreconfigitem('profiling', 'nested',
319 coreconfigitem('profiling', 'nested',
320 default=0,
320 default=0,
321 )
321 )
322 coreconfigitem('profiling', 'sort',
322 coreconfigitem('profiling', 'sort',
323 default='inlinetime',
323 default='inlinetime',
324 )
324 )
325 coreconfigitem('profiling', 'statformat',
325 coreconfigitem('profiling', 'statformat',
326 default='hotpath',
326 default='hotpath',
327 )
327 )
328 coreconfigitem('progress', 'assume-tty',
328 coreconfigitem('progress', 'assume-tty',
329 default=False,
329 default=False,
330 )
330 )
331 coreconfigitem('progress', 'changedelay',
331 coreconfigitem('progress', 'changedelay',
332 default=1,
332 default=1,
333 )
333 )
334 coreconfigitem('progress', 'clear-complete',
334 coreconfigitem('progress', 'clear-complete',
335 default=True,
335 default=True,
336 )
336 )
337 coreconfigitem('progress', 'debug',
337 coreconfigitem('progress', 'debug',
338 default=False,
338 default=False,
339 )
339 )
340 coreconfigitem('progress', 'delay',
340 coreconfigitem('progress', 'delay',
341 default=3,
341 default=3,
342 )
342 )
343 coreconfigitem('progress', 'disable',
343 coreconfigitem('progress', 'disable',
344 default=False,
344 default=False,
345 )
345 )
346 coreconfigitem('progress', 'estimate',
346 coreconfigitem('progress', 'estimate',
347 default=2,
347 default=2,
348 )
348 )
349 coreconfigitem('progress', 'refresh',
349 coreconfigitem('progress', 'refresh',
350 default=0.1,
350 default=0.1,
351 )
351 )
352 coreconfigitem('progress', 'width',
352 coreconfigitem('progress', 'width',
353 default=dynamicdefault,
353 default=dynamicdefault,
354 )
354 )
355 coreconfigitem('server', 'bundle1',
355 coreconfigitem('server', 'bundle1',
356 default=True,
356 default=True,
357 )
357 )
358 coreconfigitem('server', 'bundle1gd',
358 coreconfigitem('server', 'bundle1gd',
359 default=None,
359 default=None,
360 )
360 )
361 coreconfigitem('server', 'compressionengines',
361 coreconfigitem('server', 'compressionengines',
362 default=list,
362 default=list,
363 )
363 )
364 coreconfigitem('server', 'concurrent-push-mode',
364 coreconfigitem('server', 'concurrent-push-mode',
365 default='strict',
365 default='strict',
366 )
366 )
367 coreconfigitem('server', 'disablefullbundle',
367 coreconfigitem('server', 'disablefullbundle',
368 default=False,
368 default=False,
369 )
369 )
370 coreconfigitem('server', 'maxhttpheaderlen',
370 coreconfigitem('server', 'maxhttpheaderlen',
371 default=1024,
371 default=1024,
372 )
372 )
373 coreconfigitem('server', 'preferuncompressed',
373 coreconfigitem('server', 'preferuncompressed',
374 default=False,
374 default=False,
375 )
375 )
376 coreconfigitem('server', 'uncompressed',
376 coreconfigitem('server', 'uncompressed',
377 default=True,
377 default=True,
378 )
378 )
379 coreconfigitem('server', 'uncompressedallowsecret',
379 coreconfigitem('server', 'uncompressedallowsecret',
380 default=False,
380 default=False,
381 )
381 )
382 coreconfigitem('server', 'validate',
382 coreconfigitem('server', 'validate',
383 default=False,
383 default=False,
384 )
384 )
385 coreconfigitem('server', 'zliblevel',
385 coreconfigitem('server', 'zliblevel',
386 default=-1,
386 default=-1,
387 )
387 )
388 coreconfigitem('smtp', 'host',
388 coreconfigitem('smtp', 'host',
389 default=None,
389 default=None,
390 )
390 )
391 coreconfigitem('smtp', 'local_hostname',
391 coreconfigitem('smtp', 'local_hostname',
392 default=None,
392 default=None,
393 )
393 )
394 coreconfigitem('smtp', 'password',
394 coreconfigitem('smtp', 'password',
395 default=None,
395 default=None,
396 )
396 )
397 coreconfigitem('smtp', 'tls',
397 coreconfigitem('smtp', 'tls',
398 default='none',
398 default='none',
399 )
399 )
400 coreconfigitem('smtp', 'username',
400 coreconfigitem('smtp', 'username',
401 default=None,
401 default=None,
402 )
402 )
403 coreconfigitem('sparse', 'missingwarning',
403 coreconfigitem('sparse', 'missingwarning',
404 default=True,
404 default=True,
405 )
405 )
406 coreconfigitem('trusted', 'groups',
406 coreconfigitem('trusted', 'groups',
407 default=list,
407 default=list,
408 )
408 )
409 coreconfigitem('trusted', 'users',
409 coreconfigitem('trusted', 'users',
410 default=list,
410 default=list,
411 )
411 )
412 coreconfigitem('ui', '_usedassubrepo',
412 coreconfigitem('ui', '_usedassubrepo',
413 default=False,
413 default=False,
414 )
414 )
415 coreconfigitem('ui', 'allowemptycommit',
415 coreconfigitem('ui', 'allowemptycommit',
416 default=False,
416 default=False,
417 )
417 )
418 coreconfigitem('ui', 'archivemeta',
418 coreconfigitem('ui', 'archivemeta',
419 default=True,
419 default=True,
420 )
420 )
421 coreconfigitem('ui', 'askusername',
421 coreconfigitem('ui', 'askusername',
422 default=False,
422 default=False,
423 )
423 )
424 coreconfigitem('ui', 'clonebundlefallback',
424 coreconfigitem('ui', 'clonebundlefallback',
425 default=False,
425 default=False,
426 )
426 )
427 coreconfigitem('ui', 'clonebundleprefers',
427 coreconfigitem('ui', 'clonebundleprefers',
428 default=list,
428 default=list,
429 )
429 )
430 coreconfigitem('ui', 'clonebundles',
430 coreconfigitem('ui', 'clonebundles',
431 default=True,
431 default=True,
432 )
432 )
433 coreconfigitem('ui', 'color',
433 coreconfigitem('ui', 'color',
434 default='auto',
434 default='auto',
435 )
435 )
436 coreconfigitem('ui', 'commitsubrepos',
436 coreconfigitem('ui', 'commitsubrepos',
437 default=False,
437 default=False,
438 )
438 )
439 coreconfigitem('ui', 'debug',
439 coreconfigitem('ui', 'debug',
440 default=False,
440 default=False,
441 )
441 )
442 coreconfigitem('ui', 'debugger',
442 coreconfigitem('ui', 'debugger',
443 default=None,
443 default=None,
444 )
444 )
445 coreconfigitem('ui', 'fallbackencoding',
445 coreconfigitem('ui', 'fallbackencoding',
446 default=None,
446 default=None,
447 )
447 )
448 coreconfigitem('ui', 'forcecwd',
448 coreconfigitem('ui', 'forcecwd',
449 default=None,
449 default=None,
450 )
450 )
451 coreconfigitem('ui', 'forcemerge',
451 coreconfigitem('ui', 'forcemerge',
452 default=None,
452 default=None,
453 )
453 )
454 coreconfigitem('ui', 'formatdebug',
454 coreconfigitem('ui', 'formatdebug',
455 default=False,
455 default=False,
456 )
456 )
457 coreconfigitem('ui', 'formatjson',
457 coreconfigitem('ui', 'formatjson',
458 default=False,
458 default=False,
459 )
459 )
460 coreconfigitem('ui', 'formatted',
460 coreconfigitem('ui', 'formatted',
461 default=None,
461 default=None,
462 )
462 )
463 coreconfigitem('ui', 'graphnodetemplate',
463 coreconfigitem('ui', 'graphnodetemplate',
464 default=None,
464 default=None,
465 )
465 )
466 coreconfigitem('ui', 'http2debuglevel',
466 coreconfigitem('ui', 'http2debuglevel',
467 default=None,
467 default=None,
468 )
468 )
469 coreconfigitem('ui', 'interactive',
469 coreconfigitem('ui', 'interactive',
470 default=None,
470 default=None,
471 )
471 )
472 coreconfigitem('ui', 'interface',
472 coreconfigitem('ui', 'interface',
473 default=None,
473 default=None,
474 )
474 )
475 coreconfigitem('ui', 'logblockedtimes',
475 coreconfigitem('ui', 'logblockedtimes',
476 default=False,
476 default=False,
477 )
477 )
478 coreconfigitem('ui', 'logtemplate',
478 coreconfigitem('ui', 'logtemplate',
479 default=None,
479 default=None,
480 )
480 )
481 coreconfigitem('ui', 'merge',
481 coreconfigitem('ui', 'merge',
482 default=None,
482 default=None,
483 )
483 )
484 coreconfigitem('ui', 'mergemarkers',
484 coreconfigitem('ui', 'mergemarkers',
485 default='basic',
485 default='basic',
486 )
486 )
487 coreconfigitem('ui', 'mergemarkertemplate',
488 default=('{node|short} '
489 '{ifeq(tags, "tip", "", '
490 'ifeq(tags, "", "", "{tags} "))}'
491 '{if(bookmarks, "{bookmarks} ")}'
492 '{ifeq(branch, "default", "", "{branch} ")}'
493 '- {author|user}: {desc|firstline}')
494 )
487 coreconfigitem('ui', 'nontty',
495 coreconfigitem('ui', 'nontty',
488 default=False,
496 default=False,
489 )
497 )
490 coreconfigitem('ui', 'origbackuppath',
498 coreconfigitem('ui', 'origbackuppath',
491 default=None,
499 default=None,
492 )
500 )
493 coreconfigitem('ui', 'paginate',
501 coreconfigitem('ui', 'paginate',
494 default=True,
502 default=True,
495 )
503 )
496 coreconfigitem('ui', 'patch',
504 coreconfigitem('ui', 'patch',
497 default=None,
505 default=None,
498 )
506 )
499 coreconfigitem('ui', 'portablefilenames',
507 coreconfigitem('ui', 'portablefilenames',
500 default='warn',
508 default='warn',
501 )
509 )
502 coreconfigitem('ui', 'promptecho',
510 coreconfigitem('ui', 'promptecho',
503 default=False,
511 default=False,
504 )
512 )
505 coreconfigitem('ui', 'quiet',
513 coreconfigitem('ui', 'quiet',
506 default=False,
514 default=False,
507 )
515 )
508 coreconfigitem('ui', 'quietbookmarkmove',
516 coreconfigitem('ui', 'quietbookmarkmove',
509 default=False,
517 default=False,
510 )
518 )
511 coreconfigitem('ui', 'remotecmd',
519 coreconfigitem('ui', 'remotecmd',
512 default='hg',
520 default='hg',
513 )
521 )
514 coreconfigitem('ui', 'report_untrusted',
522 coreconfigitem('ui', 'report_untrusted',
515 default=True,
523 default=True,
516 )
524 )
517 coreconfigitem('ui', 'rollback',
525 coreconfigitem('ui', 'rollback',
518 default=True,
526 default=True,
519 )
527 )
520 coreconfigitem('ui', 'slash',
528 coreconfigitem('ui', 'slash',
521 default=False,
529 default=False,
522 )
530 )
523 coreconfigitem('ui', 'ssh',
531 coreconfigitem('ui', 'ssh',
524 default='ssh',
532 default='ssh',
525 )
533 )
526 coreconfigitem('ui', 'statuscopies',
534 coreconfigitem('ui', 'statuscopies',
527 default=False,
535 default=False,
528 )
536 )
529 coreconfigitem('ui', 'strict',
537 coreconfigitem('ui', 'strict',
530 default=False,
538 default=False,
531 )
539 )
532 coreconfigitem('ui', 'style',
540 coreconfigitem('ui', 'style',
533 default='',
541 default='',
534 )
542 )
535 coreconfigitem('ui', 'supportcontact',
543 coreconfigitem('ui', 'supportcontact',
536 default=None,
544 default=None,
537 )
545 )
538 coreconfigitem('ui', 'textwidth',
546 coreconfigitem('ui', 'textwidth',
539 default=78,
547 default=78,
540 )
548 )
541 coreconfigitem('ui', 'timeout',
549 coreconfigitem('ui', 'timeout',
542 default='600',
550 default='600',
543 )
551 )
544 coreconfigitem('ui', 'traceback',
552 coreconfigitem('ui', 'traceback',
545 default=False,
553 default=False,
546 )
554 )
547 coreconfigitem('ui', 'tweakdefaults',
555 coreconfigitem('ui', 'tweakdefaults',
548 default=False,
556 default=False,
549 )
557 )
550 coreconfigitem('ui', 'usehttp2',
558 coreconfigitem('ui', 'usehttp2',
551 default=False,
559 default=False,
552 )
560 )
553 coreconfigitem('ui', 'username',
561 coreconfigitem('ui', 'username',
554 alias=[('ui', 'user')]
562 alias=[('ui', 'user')]
555 )
563 )
556 coreconfigitem('ui', 'verbose',
564 coreconfigitem('ui', 'verbose',
557 default=False,
565 default=False,
558 )
566 )
559 coreconfigitem('verify', 'skipflags',
567 coreconfigitem('verify', 'skipflags',
560 default=None,
568 default=None,
561 )
569 )
562 coreconfigitem('worker', 'backgroundclose',
570 coreconfigitem('worker', 'backgroundclose',
563 default=dynamicdefault,
571 default=dynamicdefault,
564 )
572 )
565 # Windows defaults to a limit of 512 open files. A buffer of 128
573 # Windows defaults to a limit of 512 open files. A buffer of 128
566 # should give us enough headway.
574 # should give us enough headway.
567 coreconfigitem('worker', 'backgroundclosemaxqueue',
575 coreconfigitem('worker', 'backgroundclosemaxqueue',
568 default=384,
576 default=384,
569 )
577 )
570 coreconfigitem('worker', 'backgroundcloseminfilecount',
578 coreconfigitem('worker', 'backgroundcloseminfilecount',
571 default=2048,
579 default=2048,
572 )
580 )
573 coreconfigitem('worker', 'backgroundclosethreadcount',
581 coreconfigitem('worker', 'backgroundclosethreadcount',
574 default=4,
582 default=4,
575 )
583 )
576 coreconfigitem('worker', 'numcpus',
584 coreconfigitem('worker', 'numcpus',
577 default=None,
585 default=None,
578 )
586 )
@@ -1,754 +1,747 b''
1 # filemerge.py - file-level merge handling for Mercurial
1 # filemerge.py - file-level merge handling for Mercurial
2 #
2 #
3 # Copyright 2006, 2007, 2008 Matt Mackall <mpm@selenic.com>
3 # Copyright 2006, 2007, 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 filecmp
10 import filecmp
11 import os
11 import os
12 import re
12 import re
13 import tempfile
13 import tempfile
14
14
15 from .i18n import _
15 from .i18n import _
16 from .node import nullid, short
16 from .node import nullid, short
17
17
18 from . import (
18 from . import (
19 encoding,
19 encoding,
20 error,
20 error,
21 formatter,
21 formatter,
22 match,
22 match,
23 pycompat,
23 pycompat,
24 scmutil,
24 scmutil,
25 simplemerge,
25 simplemerge,
26 tagmerge,
26 tagmerge,
27 templatekw,
27 templatekw,
28 templater,
28 templater,
29 util,
29 util,
30 )
30 )
31
31
32 def _toolstr(ui, tool, part, default=""):
32 def _toolstr(ui, tool, part, default=""):
33 return ui.config("merge-tools", tool + "." + part, default)
33 return ui.config("merge-tools", tool + "." + part, default)
34
34
35 def _toolbool(ui, tool, part, default=False):
35 def _toolbool(ui, tool, part, default=False):
36 return ui.configbool("merge-tools", tool + "." + part, default)
36 return ui.configbool("merge-tools", tool + "." + part, default)
37
37
38 def _toollist(ui, tool, part, default=None):
38 def _toollist(ui, tool, part, default=None):
39 if default is None:
39 if default is None:
40 default = []
40 default = []
41 return ui.configlist("merge-tools", tool + "." + part, default)
41 return ui.configlist("merge-tools", tool + "." + part, default)
42
42
43 internals = {}
43 internals = {}
44 # Merge tools to document.
44 # Merge tools to document.
45 internalsdoc = {}
45 internalsdoc = {}
46
46
47 # internal tool merge types
47 # internal tool merge types
48 nomerge = None
48 nomerge = None
49 mergeonly = 'mergeonly' # just the full merge, no premerge
49 mergeonly = 'mergeonly' # just the full merge, no premerge
50 fullmerge = 'fullmerge' # both premerge and merge
50 fullmerge = 'fullmerge' # both premerge and merge
51
51
52 _localchangedotherdeletedmsg = _(
52 _localchangedotherdeletedmsg = _(
53 "local%(l)s changed %(fd)s which other%(o)s deleted\n"
53 "local%(l)s changed %(fd)s which other%(o)s deleted\n"
54 "use (c)hanged version, (d)elete, or leave (u)nresolved?"
54 "use (c)hanged version, (d)elete, or leave (u)nresolved?"
55 "$$ &Changed $$ &Delete $$ &Unresolved")
55 "$$ &Changed $$ &Delete $$ &Unresolved")
56
56
57 _otherchangedlocaldeletedmsg = _(
57 _otherchangedlocaldeletedmsg = _(
58 "other%(o)s changed %(fd)s which local%(l)s deleted\n"
58 "other%(o)s changed %(fd)s which local%(l)s deleted\n"
59 "use (c)hanged version, leave (d)eleted, or "
59 "use (c)hanged version, leave (d)eleted, or "
60 "leave (u)nresolved?"
60 "leave (u)nresolved?"
61 "$$ &Changed $$ &Deleted $$ &Unresolved")
61 "$$ &Changed $$ &Deleted $$ &Unresolved")
62
62
63 class absentfilectx(object):
63 class absentfilectx(object):
64 """Represents a file that's ostensibly in a context but is actually not
64 """Represents a file that's ostensibly in a context but is actually not
65 present in it.
65 present in it.
66
66
67 This is here because it's very specific to the filemerge code for now --
67 This is here because it's very specific to the filemerge code for now --
68 other code is likely going to break with the values this returns."""
68 other code is likely going to break with the values this returns."""
69 def __init__(self, ctx, f):
69 def __init__(self, ctx, f):
70 self._ctx = ctx
70 self._ctx = ctx
71 self._f = f
71 self._f = f
72
72
73 def path(self):
73 def path(self):
74 return self._f
74 return self._f
75
75
76 def size(self):
76 def size(self):
77 return None
77 return None
78
78
79 def data(self):
79 def data(self):
80 return None
80 return None
81
81
82 def filenode(self):
82 def filenode(self):
83 return nullid
83 return nullid
84
84
85 _customcmp = True
85 _customcmp = True
86 def cmp(self, fctx):
86 def cmp(self, fctx):
87 """compare with other file context
87 """compare with other file context
88
88
89 returns True if different from fctx.
89 returns True if different from fctx.
90 """
90 """
91 return not (fctx.isabsent() and
91 return not (fctx.isabsent() and
92 fctx.ctx() == self.ctx() and
92 fctx.ctx() == self.ctx() and
93 fctx.path() == self.path())
93 fctx.path() == self.path())
94
94
95 def flags(self):
95 def flags(self):
96 return ''
96 return ''
97
97
98 def changectx(self):
98 def changectx(self):
99 return self._ctx
99 return self._ctx
100
100
101 def isbinary(self):
101 def isbinary(self):
102 return False
102 return False
103
103
104 def isabsent(self):
104 def isabsent(self):
105 return True
105 return True
106
106
107 def internaltool(name, mergetype, onfailure=None, precheck=None):
107 def internaltool(name, mergetype, onfailure=None, precheck=None):
108 '''return a decorator for populating internal merge tool table'''
108 '''return a decorator for populating internal merge tool table'''
109 def decorator(func):
109 def decorator(func):
110 fullname = ':' + name
110 fullname = ':' + name
111 func.__doc__ = (pycompat.sysstr("``%s``\n" % fullname)
111 func.__doc__ = (pycompat.sysstr("``%s``\n" % fullname)
112 + func.__doc__.strip())
112 + func.__doc__.strip())
113 internals[fullname] = func
113 internals[fullname] = func
114 internals['internal:' + name] = func
114 internals['internal:' + name] = func
115 internalsdoc[fullname] = func
115 internalsdoc[fullname] = func
116 func.mergetype = mergetype
116 func.mergetype = mergetype
117 func.onfailure = onfailure
117 func.onfailure = onfailure
118 func.precheck = precheck
118 func.precheck = precheck
119 return func
119 return func
120 return decorator
120 return decorator
121
121
122 def _findtool(ui, tool):
122 def _findtool(ui, tool):
123 if tool in internals:
123 if tool in internals:
124 return tool
124 return tool
125 return findexternaltool(ui, tool)
125 return findexternaltool(ui, tool)
126
126
127 def findexternaltool(ui, tool):
127 def findexternaltool(ui, tool):
128 for kn in ("regkey", "regkeyalt"):
128 for kn in ("regkey", "regkeyalt"):
129 k = _toolstr(ui, tool, kn)
129 k = _toolstr(ui, tool, kn)
130 if not k:
130 if not k:
131 continue
131 continue
132 p = util.lookupreg(k, _toolstr(ui, tool, "regname"))
132 p = util.lookupreg(k, _toolstr(ui, tool, "regname"))
133 if p:
133 if p:
134 p = util.findexe(p + _toolstr(ui, tool, "regappend"))
134 p = util.findexe(p + _toolstr(ui, tool, "regappend"))
135 if p:
135 if p:
136 return p
136 return p
137 exe = _toolstr(ui, tool, "executable", tool)
137 exe = _toolstr(ui, tool, "executable", tool)
138 return util.findexe(util.expandpath(exe))
138 return util.findexe(util.expandpath(exe))
139
139
140 def _picktool(repo, ui, path, binary, symlink, changedelete):
140 def _picktool(repo, ui, path, binary, symlink, changedelete):
141 def supportscd(tool):
141 def supportscd(tool):
142 return tool in internals and internals[tool].mergetype == nomerge
142 return tool in internals and internals[tool].mergetype == nomerge
143
143
144 def check(tool, pat, symlink, binary, changedelete):
144 def check(tool, pat, symlink, binary, changedelete):
145 tmsg = tool
145 tmsg = tool
146 if pat:
146 if pat:
147 tmsg = _("%s (for pattern %s)") % (tool, pat)
147 tmsg = _("%s (for pattern %s)") % (tool, pat)
148 if not _findtool(ui, tool):
148 if not _findtool(ui, tool):
149 if pat: # explicitly requested tool deserves a warning
149 if pat: # explicitly requested tool deserves a warning
150 ui.warn(_("couldn't find merge tool %s\n") % tmsg)
150 ui.warn(_("couldn't find merge tool %s\n") % tmsg)
151 else: # configured but non-existing tools are more silent
151 else: # configured but non-existing tools are more silent
152 ui.note(_("couldn't find merge tool %s\n") % tmsg)
152 ui.note(_("couldn't find merge tool %s\n") % tmsg)
153 elif symlink and not _toolbool(ui, tool, "symlink"):
153 elif symlink and not _toolbool(ui, tool, "symlink"):
154 ui.warn(_("tool %s can't handle symlinks\n") % tmsg)
154 ui.warn(_("tool %s can't handle symlinks\n") % tmsg)
155 elif binary and not _toolbool(ui, tool, "binary"):
155 elif binary and not _toolbool(ui, tool, "binary"):
156 ui.warn(_("tool %s can't handle binary\n") % tmsg)
156 ui.warn(_("tool %s can't handle binary\n") % tmsg)
157 elif changedelete and not supportscd(tool):
157 elif changedelete and not supportscd(tool):
158 # the nomerge tools are the only tools that support change/delete
158 # the nomerge tools are the only tools that support change/delete
159 # conflicts
159 # conflicts
160 pass
160 pass
161 elif not util.gui() and _toolbool(ui, tool, "gui"):
161 elif not util.gui() and _toolbool(ui, tool, "gui"):
162 ui.warn(_("tool %s requires a GUI\n") % tmsg)
162 ui.warn(_("tool %s requires a GUI\n") % tmsg)
163 else:
163 else:
164 return True
164 return True
165 return False
165 return False
166
166
167 # internal config: ui.forcemerge
167 # internal config: ui.forcemerge
168 # forcemerge comes from command line arguments, highest priority
168 # forcemerge comes from command line arguments, highest priority
169 force = ui.config('ui', 'forcemerge')
169 force = ui.config('ui', 'forcemerge')
170 if force:
170 if force:
171 toolpath = _findtool(ui, force)
171 toolpath = _findtool(ui, force)
172 if changedelete and not supportscd(toolpath):
172 if changedelete and not supportscd(toolpath):
173 return ":prompt", None
173 return ":prompt", None
174 else:
174 else:
175 if toolpath:
175 if toolpath:
176 return (force, util.shellquote(toolpath))
176 return (force, util.shellquote(toolpath))
177 else:
177 else:
178 # mimic HGMERGE if given tool not found
178 # mimic HGMERGE if given tool not found
179 return (force, force)
179 return (force, force)
180
180
181 # HGMERGE takes next precedence
181 # HGMERGE takes next precedence
182 hgmerge = encoding.environ.get("HGMERGE")
182 hgmerge = encoding.environ.get("HGMERGE")
183 if hgmerge:
183 if hgmerge:
184 if changedelete and not supportscd(hgmerge):
184 if changedelete and not supportscd(hgmerge):
185 return ":prompt", None
185 return ":prompt", None
186 else:
186 else:
187 return (hgmerge, hgmerge)
187 return (hgmerge, hgmerge)
188
188
189 # then patterns
189 # then patterns
190 for pat, tool in ui.configitems("merge-patterns"):
190 for pat, tool in ui.configitems("merge-patterns"):
191 mf = match.match(repo.root, '', [pat])
191 mf = match.match(repo.root, '', [pat])
192 if mf(path) and check(tool, pat, symlink, False, changedelete):
192 if mf(path) and check(tool, pat, symlink, False, changedelete):
193 toolpath = _findtool(ui, tool)
193 toolpath = _findtool(ui, tool)
194 return (tool, util.shellquote(toolpath))
194 return (tool, util.shellquote(toolpath))
195
195
196 # then merge tools
196 # then merge tools
197 tools = {}
197 tools = {}
198 disabled = set()
198 disabled = set()
199 for k, v in ui.configitems("merge-tools"):
199 for k, v in ui.configitems("merge-tools"):
200 t = k.split('.')[0]
200 t = k.split('.')[0]
201 if t not in tools:
201 if t not in tools:
202 tools[t] = int(_toolstr(ui, t, "priority", "0"))
202 tools[t] = int(_toolstr(ui, t, "priority", "0"))
203 if _toolbool(ui, t, "disabled", False):
203 if _toolbool(ui, t, "disabled", False):
204 disabled.add(t)
204 disabled.add(t)
205 names = tools.keys()
205 names = tools.keys()
206 tools = sorted([(-p, tool) for tool, p in tools.items()
206 tools = sorted([(-p, tool) for tool, p in tools.items()
207 if tool not in disabled])
207 if tool not in disabled])
208 uimerge = ui.config("ui", "merge")
208 uimerge = ui.config("ui", "merge")
209 if uimerge:
209 if uimerge:
210 # external tools defined in uimerge won't be able to handle
210 # external tools defined in uimerge won't be able to handle
211 # change/delete conflicts
211 # change/delete conflicts
212 if uimerge not in names and not changedelete:
212 if uimerge not in names and not changedelete:
213 return (uimerge, uimerge)
213 return (uimerge, uimerge)
214 tools.insert(0, (None, uimerge)) # highest priority
214 tools.insert(0, (None, uimerge)) # highest priority
215 tools.append((None, "hgmerge")) # the old default, if found
215 tools.append((None, "hgmerge")) # the old default, if found
216 for p, t in tools:
216 for p, t in tools:
217 if check(t, None, symlink, binary, changedelete):
217 if check(t, None, symlink, binary, changedelete):
218 toolpath = _findtool(ui, t)
218 toolpath = _findtool(ui, t)
219 return (t, util.shellquote(toolpath))
219 return (t, util.shellquote(toolpath))
220
220
221 # internal merge or prompt as last resort
221 # internal merge or prompt as last resort
222 if symlink or binary or changedelete:
222 if symlink or binary or changedelete:
223 if not changedelete and len(tools):
223 if not changedelete and len(tools):
224 # any tool is rejected by capability for symlink or binary
224 # any tool is rejected by capability for symlink or binary
225 ui.warn(_("no tool found to merge %s\n") % path)
225 ui.warn(_("no tool found to merge %s\n") % path)
226 return ":prompt", None
226 return ":prompt", None
227 return ":merge", None
227 return ":merge", None
228
228
229 def _eoltype(data):
229 def _eoltype(data):
230 "Guess the EOL type of a file"
230 "Guess the EOL type of a file"
231 if '\0' in data: # binary
231 if '\0' in data: # binary
232 return None
232 return None
233 if '\r\n' in data: # Windows
233 if '\r\n' in data: # Windows
234 return '\r\n'
234 return '\r\n'
235 if '\r' in data: # Old Mac
235 if '\r' in data: # Old Mac
236 return '\r'
236 return '\r'
237 if '\n' in data: # UNIX
237 if '\n' in data: # UNIX
238 return '\n'
238 return '\n'
239 return None # unknown
239 return None # unknown
240
240
241 def _matcheol(file, origfile):
241 def _matcheol(file, origfile):
242 "Convert EOL markers in a file to match origfile"
242 "Convert EOL markers in a file to match origfile"
243 tostyle = _eoltype(util.readfile(origfile))
243 tostyle = _eoltype(util.readfile(origfile))
244 if tostyle:
244 if tostyle:
245 data = util.readfile(file)
245 data = util.readfile(file)
246 style = _eoltype(data)
246 style = _eoltype(data)
247 if style:
247 if style:
248 newdata = data.replace(style, tostyle)
248 newdata = data.replace(style, tostyle)
249 if newdata != data:
249 if newdata != data:
250 util.writefile(file, newdata)
250 util.writefile(file, newdata)
251
251
252 @internaltool('prompt', nomerge)
252 @internaltool('prompt', nomerge)
253 def _iprompt(repo, mynode, orig, fcd, fco, fca, toolconf, labels=None):
253 def _iprompt(repo, mynode, orig, fcd, fco, fca, toolconf, labels=None):
254 """Asks the user which of the local `p1()` or the other `p2()` version to
254 """Asks the user which of the local `p1()` or the other `p2()` version to
255 keep as the merged version."""
255 keep as the merged version."""
256 ui = repo.ui
256 ui = repo.ui
257 fd = fcd.path()
257 fd = fcd.path()
258
258
259 prompts = partextras(labels)
259 prompts = partextras(labels)
260 prompts['fd'] = fd
260 prompts['fd'] = fd
261 try:
261 try:
262 if fco.isabsent():
262 if fco.isabsent():
263 index = ui.promptchoice(
263 index = ui.promptchoice(
264 _localchangedotherdeletedmsg % prompts, 2)
264 _localchangedotherdeletedmsg % prompts, 2)
265 choice = ['local', 'other', 'unresolved'][index]
265 choice = ['local', 'other', 'unresolved'][index]
266 elif fcd.isabsent():
266 elif fcd.isabsent():
267 index = ui.promptchoice(
267 index = ui.promptchoice(
268 _otherchangedlocaldeletedmsg % prompts, 2)
268 _otherchangedlocaldeletedmsg % prompts, 2)
269 choice = ['other', 'local', 'unresolved'][index]
269 choice = ['other', 'local', 'unresolved'][index]
270 else:
270 else:
271 index = ui.promptchoice(
271 index = ui.promptchoice(
272 _("keep (l)ocal%(l)s, take (o)ther%(o)s, or leave (u)nresolved"
272 _("keep (l)ocal%(l)s, take (o)ther%(o)s, or leave (u)nresolved"
273 " for %(fd)s?"
273 " for %(fd)s?"
274 "$$ &Local $$ &Other $$ &Unresolved") % prompts, 2)
274 "$$ &Local $$ &Other $$ &Unresolved") % prompts, 2)
275 choice = ['local', 'other', 'unresolved'][index]
275 choice = ['local', 'other', 'unresolved'][index]
276
276
277 if choice == 'other':
277 if choice == 'other':
278 return _iother(repo, mynode, orig, fcd, fco, fca, toolconf,
278 return _iother(repo, mynode, orig, fcd, fco, fca, toolconf,
279 labels)
279 labels)
280 elif choice == 'local':
280 elif choice == 'local':
281 return _ilocal(repo, mynode, orig, fcd, fco, fca, toolconf,
281 return _ilocal(repo, mynode, orig, fcd, fco, fca, toolconf,
282 labels)
282 labels)
283 elif choice == 'unresolved':
283 elif choice == 'unresolved':
284 return _ifail(repo, mynode, orig, fcd, fco, fca, toolconf,
284 return _ifail(repo, mynode, orig, fcd, fco, fca, toolconf,
285 labels)
285 labels)
286 except error.ResponseExpected:
286 except error.ResponseExpected:
287 ui.write("\n")
287 ui.write("\n")
288 return _ifail(repo, mynode, orig, fcd, fco, fca, toolconf,
288 return _ifail(repo, mynode, orig, fcd, fco, fca, toolconf,
289 labels)
289 labels)
290
290
291 @internaltool('local', nomerge)
291 @internaltool('local', nomerge)
292 def _ilocal(repo, mynode, orig, fcd, fco, fca, toolconf, labels=None):
292 def _ilocal(repo, mynode, orig, fcd, fco, fca, toolconf, labels=None):
293 """Uses the local `p1()` version of files as the merged version."""
293 """Uses the local `p1()` version of files as the merged version."""
294 return 0, fcd.isabsent()
294 return 0, fcd.isabsent()
295
295
296 @internaltool('other', nomerge)
296 @internaltool('other', nomerge)
297 def _iother(repo, mynode, orig, fcd, fco, fca, toolconf, labels=None):
297 def _iother(repo, mynode, orig, fcd, fco, fca, toolconf, labels=None):
298 """Uses the other `p2()` version of files as the merged version."""
298 """Uses the other `p2()` version of files as the merged version."""
299 if fco.isabsent():
299 if fco.isabsent():
300 # local changed, remote deleted -- 'deleted' picked
300 # local changed, remote deleted -- 'deleted' picked
301 _underlyingfctxifabsent(fcd).remove()
301 _underlyingfctxifabsent(fcd).remove()
302 deleted = True
302 deleted = True
303 else:
303 else:
304 _underlyingfctxifabsent(fcd).write(fco.data(), fco.flags())
304 _underlyingfctxifabsent(fcd).write(fco.data(), fco.flags())
305 deleted = False
305 deleted = False
306 return 0, deleted
306 return 0, deleted
307
307
308 @internaltool('fail', nomerge)
308 @internaltool('fail', nomerge)
309 def _ifail(repo, mynode, orig, fcd, fco, fca, toolconf, labels=None):
309 def _ifail(repo, mynode, orig, fcd, fco, fca, toolconf, labels=None):
310 """
310 """
311 Rather than attempting to merge files that were modified on both
311 Rather than attempting to merge files that were modified on both
312 branches, it marks them as unresolved. The resolve command must be
312 branches, it marks them as unresolved. The resolve command must be
313 used to resolve these conflicts."""
313 used to resolve these conflicts."""
314 # for change/delete conflicts write out the changed version, then fail
314 # for change/delete conflicts write out the changed version, then fail
315 if fcd.isabsent():
315 if fcd.isabsent():
316 _underlyingfctxifabsent(fcd).write(fco.data(), fco.flags())
316 _underlyingfctxifabsent(fcd).write(fco.data(), fco.flags())
317 return 1, False
317 return 1, False
318
318
319 def _underlyingfctxifabsent(filectx):
319 def _underlyingfctxifabsent(filectx):
320 """Sometimes when resolving, our fcd is actually an absentfilectx, but
320 """Sometimes when resolving, our fcd is actually an absentfilectx, but
321 we want to write to it (to do the resolve). This helper returns the
321 we want to write to it (to do the resolve). This helper returns the
322 underyling workingfilectx in that case.
322 underyling workingfilectx in that case.
323 """
323 """
324 if filectx.isabsent():
324 if filectx.isabsent():
325 return filectx.changectx()[filectx.path()]
325 return filectx.changectx()[filectx.path()]
326 else:
326 else:
327 return filectx
327 return filectx
328
328
329 def _premerge(repo, fcd, fco, fca, toolconf, files, labels=None):
329 def _premerge(repo, fcd, fco, fca, toolconf, files, labels=None):
330 tool, toolpath, binary, symlink = toolconf
330 tool, toolpath, binary, symlink = toolconf
331 if symlink or fcd.isabsent() or fco.isabsent():
331 if symlink or fcd.isabsent() or fco.isabsent():
332 return 1
332 return 1
333 a, b, c, back = files
333 a, b, c, back = files
334
334
335 ui = repo.ui
335 ui = repo.ui
336
336
337 validkeep = ['keep', 'keep-merge3']
337 validkeep = ['keep', 'keep-merge3']
338
338
339 # do we attempt to simplemerge first?
339 # do we attempt to simplemerge first?
340 try:
340 try:
341 premerge = _toolbool(ui, tool, "premerge", not binary)
341 premerge = _toolbool(ui, tool, "premerge", not binary)
342 except error.ConfigError:
342 except error.ConfigError:
343 premerge = _toolstr(ui, tool, "premerge").lower()
343 premerge = _toolstr(ui, tool, "premerge").lower()
344 if premerge not in validkeep:
344 if premerge not in validkeep:
345 _valid = ', '.join(["'" + v + "'" for v in validkeep])
345 _valid = ', '.join(["'" + v + "'" for v in validkeep])
346 raise error.ConfigError(_("%s.premerge not valid "
346 raise error.ConfigError(_("%s.premerge not valid "
347 "('%s' is neither boolean nor %s)") %
347 "('%s' is neither boolean nor %s)") %
348 (tool, premerge, _valid))
348 (tool, premerge, _valid))
349
349
350 if premerge:
350 if premerge:
351 if premerge == 'keep-merge3':
351 if premerge == 'keep-merge3':
352 if not labels:
352 if not labels:
353 labels = _defaultconflictlabels
353 labels = _defaultconflictlabels
354 if len(labels) < 3:
354 if len(labels) < 3:
355 labels.append('base')
355 labels.append('base')
356 r = simplemerge.simplemerge(ui, a, b, c, quiet=True, label=labels)
356 r = simplemerge.simplemerge(ui, a, b, c, quiet=True, label=labels)
357 if not r:
357 if not r:
358 ui.debug(" premerge successful\n")
358 ui.debug(" premerge successful\n")
359 return 0
359 return 0
360 if premerge not in validkeep:
360 if premerge not in validkeep:
361 util.copyfile(back, a) # restore from backup and try again
361 util.copyfile(back, a) # restore from backup and try again
362 return 1 # continue merging
362 return 1 # continue merging
363
363
364 def _mergecheck(repo, mynode, orig, fcd, fco, fca, toolconf):
364 def _mergecheck(repo, mynode, orig, fcd, fco, fca, toolconf):
365 tool, toolpath, binary, symlink = toolconf
365 tool, toolpath, binary, symlink = toolconf
366 if symlink:
366 if symlink:
367 repo.ui.warn(_('warning: internal %s cannot merge symlinks '
367 repo.ui.warn(_('warning: internal %s cannot merge symlinks '
368 'for %s\n') % (tool, fcd.path()))
368 'for %s\n') % (tool, fcd.path()))
369 return False
369 return False
370 if fcd.isabsent() or fco.isabsent():
370 if fcd.isabsent() or fco.isabsent():
371 repo.ui.warn(_('warning: internal %s cannot merge change/delete '
371 repo.ui.warn(_('warning: internal %s cannot merge change/delete '
372 'conflict for %s\n') % (tool, fcd.path()))
372 'conflict for %s\n') % (tool, fcd.path()))
373 return False
373 return False
374 return True
374 return True
375
375
376 def _merge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels, mode):
376 def _merge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels, mode):
377 """
377 """
378 Uses the internal non-interactive simple merge algorithm for merging
378 Uses the internal non-interactive simple merge algorithm for merging
379 files. It will fail if there are any conflicts and leave markers in
379 files. It will fail if there are any conflicts and leave markers in
380 the partially merged file. Markers will have two sections, one for each side
380 the partially merged file. Markers will have two sections, one for each side
381 of merge, unless mode equals 'union' which suppresses the markers."""
381 of merge, unless mode equals 'union' which suppresses the markers."""
382 a, b, c, back = files
382 a, b, c, back = files
383
383
384 ui = repo.ui
384 ui = repo.ui
385
385
386 r = simplemerge.simplemerge(ui, a, b, c, label=labels, mode=mode)
386 r = simplemerge.simplemerge(ui, a, b, c, label=labels, mode=mode)
387 return True, r, False
387 return True, r, False
388
388
389 @internaltool('union', fullmerge,
389 @internaltool('union', fullmerge,
390 _("warning: conflicts while merging %s! "
390 _("warning: conflicts while merging %s! "
391 "(edit, then use 'hg resolve --mark')\n"),
391 "(edit, then use 'hg resolve --mark')\n"),
392 precheck=_mergecheck)
392 precheck=_mergecheck)
393 def _iunion(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
393 def _iunion(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
394 """
394 """
395 Uses the internal non-interactive simple merge algorithm for merging
395 Uses the internal non-interactive simple merge algorithm for merging
396 files. It will use both left and right sides for conflict regions.
396 files. It will use both left and right sides for conflict regions.
397 No markers are inserted."""
397 No markers are inserted."""
398 return _merge(repo, mynode, orig, fcd, fco, fca, toolconf,
398 return _merge(repo, mynode, orig, fcd, fco, fca, toolconf,
399 files, labels, 'union')
399 files, labels, 'union')
400
400
401 @internaltool('merge', fullmerge,
401 @internaltool('merge', fullmerge,
402 _("warning: conflicts while merging %s! "
402 _("warning: conflicts while merging %s! "
403 "(edit, then use 'hg resolve --mark')\n"),
403 "(edit, then use 'hg resolve --mark')\n"),
404 precheck=_mergecheck)
404 precheck=_mergecheck)
405 def _imerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
405 def _imerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
406 """
406 """
407 Uses the internal non-interactive simple merge algorithm for merging
407 Uses the internal non-interactive simple merge algorithm for merging
408 files. It will fail if there are any conflicts and leave markers in
408 files. It will fail if there are any conflicts and leave markers in
409 the partially merged file. Markers will have two sections, one for each side
409 the partially merged file. Markers will have two sections, one for each side
410 of merge."""
410 of merge."""
411 return _merge(repo, mynode, orig, fcd, fco, fca, toolconf,
411 return _merge(repo, mynode, orig, fcd, fco, fca, toolconf,
412 files, labels, 'merge')
412 files, labels, 'merge')
413
413
414 @internaltool('merge3', fullmerge,
414 @internaltool('merge3', fullmerge,
415 _("warning: conflicts while merging %s! "
415 _("warning: conflicts while merging %s! "
416 "(edit, then use 'hg resolve --mark')\n"),
416 "(edit, then use 'hg resolve --mark')\n"),
417 precheck=_mergecheck)
417 precheck=_mergecheck)
418 def _imerge3(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
418 def _imerge3(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
419 """
419 """
420 Uses the internal non-interactive simple merge algorithm for merging
420 Uses the internal non-interactive simple merge algorithm for merging
421 files. It will fail if there are any conflicts and leave markers in
421 files. It will fail if there are any conflicts and leave markers in
422 the partially merged file. Marker will have three sections, one from each
422 the partially merged file. Marker will have three sections, one from each
423 side of the merge and one for the base content."""
423 side of the merge and one for the base content."""
424 if not labels:
424 if not labels:
425 labels = _defaultconflictlabels
425 labels = _defaultconflictlabels
426 if len(labels) < 3:
426 if len(labels) < 3:
427 labels.append('base')
427 labels.append('base')
428 return _imerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels)
428 return _imerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels)
429
429
430 def _imergeauto(repo, mynode, orig, fcd, fco, fca, toolconf, files,
430 def _imergeauto(repo, mynode, orig, fcd, fco, fca, toolconf, files,
431 labels=None, localorother=None):
431 labels=None, localorother=None):
432 """
432 """
433 Generic driver for _imergelocal and _imergeother
433 Generic driver for _imergelocal and _imergeother
434 """
434 """
435 assert localorother is not None
435 assert localorother is not None
436 tool, toolpath, binary, symlink = toolconf
436 tool, toolpath, binary, symlink = toolconf
437 a, b, c, back = files
437 a, b, c, back = files
438 r = simplemerge.simplemerge(repo.ui, a, b, c, label=labels,
438 r = simplemerge.simplemerge(repo.ui, a, b, c, label=labels,
439 localorother=localorother)
439 localorother=localorother)
440 return True, r
440 return True, r
441
441
442 @internaltool('merge-local', mergeonly, precheck=_mergecheck)
442 @internaltool('merge-local', mergeonly, precheck=_mergecheck)
443 def _imergelocal(*args, **kwargs):
443 def _imergelocal(*args, **kwargs):
444 """
444 """
445 Like :merge, but resolve all conflicts non-interactively in favor
445 Like :merge, but resolve all conflicts non-interactively in favor
446 of the local `p1()` changes."""
446 of the local `p1()` changes."""
447 success, status = _imergeauto(localorother='local', *args, **kwargs)
447 success, status = _imergeauto(localorother='local', *args, **kwargs)
448 return success, status, False
448 return success, status, False
449
449
450 @internaltool('merge-other', mergeonly, precheck=_mergecheck)
450 @internaltool('merge-other', mergeonly, precheck=_mergecheck)
451 def _imergeother(*args, **kwargs):
451 def _imergeother(*args, **kwargs):
452 """
452 """
453 Like :merge, but resolve all conflicts non-interactively in favor
453 Like :merge, but resolve all conflicts non-interactively in favor
454 of the other `p2()` changes."""
454 of the other `p2()` changes."""
455 success, status = _imergeauto(localorother='other', *args, **kwargs)
455 success, status = _imergeauto(localorother='other', *args, **kwargs)
456 return success, status, False
456 return success, status, False
457
457
458 @internaltool('tagmerge', mergeonly,
458 @internaltool('tagmerge', mergeonly,
459 _("automatic tag merging of %s failed! "
459 _("automatic tag merging of %s failed! "
460 "(use 'hg resolve --tool :merge' or another merge "
460 "(use 'hg resolve --tool :merge' or another merge "
461 "tool of your choice)\n"))
461 "tool of your choice)\n"))
462 def _itagmerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
462 def _itagmerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
463 """
463 """
464 Uses the internal tag merge algorithm (experimental).
464 Uses the internal tag merge algorithm (experimental).
465 """
465 """
466 success, status = tagmerge.merge(repo, fcd, fco, fca)
466 success, status = tagmerge.merge(repo, fcd, fco, fca)
467 return success, status, False
467 return success, status, False
468
468
469 @internaltool('dump', fullmerge)
469 @internaltool('dump', fullmerge)
470 def _idump(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
470 def _idump(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
471 """
471 """
472 Creates three versions of the files to merge, containing the
472 Creates three versions of the files to merge, containing the
473 contents of local, other and base. These files can then be used to
473 contents of local, other and base. These files can then be used to
474 perform a merge manually. If the file to be merged is named
474 perform a merge manually. If the file to be merged is named
475 ``a.txt``, these files will accordingly be named ``a.txt.local``,
475 ``a.txt``, these files will accordingly be named ``a.txt.local``,
476 ``a.txt.other`` and ``a.txt.base`` and they will be placed in the
476 ``a.txt.other`` and ``a.txt.base`` and they will be placed in the
477 same directory as ``a.txt``.
477 same directory as ``a.txt``.
478
478
479 This implies permerge. Therefore, files aren't dumped, if premerge
479 This implies permerge. Therefore, files aren't dumped, if premerge
480 runs successfully. Use :forcedump to forcibly write files out.
480 runs successfully. Use :forcedump to forcibly write files out.
481 """
481 """
482 a, b, c, back = files
482 a, b, c, back = files
483
483
484 fd = fcd.path()
484 fd = fcd.path()
485
485
486 util.copyfile(a, a + ".local")
486 util.copyfile(a, a + ".local")
487 repo.wwrite(fd + ".other", fco.data(), fco.flags())
487 repo.wwrite(fd + ".other", fco.data(), fco.flags())
488 repo.wwrite(fd + ".base", fca.data(), fca.flags())
488 repo.wwrite(fd + ".base", fca.data(), fca.flags())
489 return False, 1, False
489 return False, 1, False
490
490
491 @internaltool('forcedump', mergeonly)
491 @internaltool('forcedump', mergeonly)
492 def _forcedump(repo, mynode, orig, fcd, fco, fca, toolconf, files,
492 def _forcedump(repo, mynode, orig, fcd, fco, fca, toolconf, files,
493 labels=None):
493 labels=None):
494 """
494 """
495 Creates three versions of the files as same as :dump, but omits premerge.
495 Creates three versions of the files as same as :dump, but omits premerge.
496 """
496 """
497 return _idump(repo, mynode, orig, fcd, fco, fca, toolconf, files,
497 return _idump(repo, mynode, orig, fcd, fco, fca, toolconf, files,
498 labels=labels)
498 labels=labels)
499
499
500 def _xmerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
500 def _xmerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None):
501 tool, toolpath, binary, symlink = toolconf
501 tool, toolpath, binary, symlink = toolconf
502 if fcd.isabsent() or fco.isabsent():
502 if fcd.isabsent() or fco.isabsent():
503 repo.ui.warn(_('warning: %s cannot merge change/delete conflict '
503 repo.ui.warn(_('warning: %s cannot merge change/delete conflict '
504 'for %s\n') % (tool, fcd.path()))
504 'for %s\n') % (tool, fcd.path()))
505 return False, 1, None
505 return False, 1, None
506 a, b, c, back = files
506 a, b, c, back = files
507 out = ""
507 out = ""
508 env = {'HG_FILE': fcd.path(),
508 env = {'HG_FILE': fcd.path(),
509 'HG_MY_NODE': short(mynode),
509 'HG_MY_NODE': short(mynode),
510 'HG_OTHER_NODE': str(fco.changectx()),
510 'HG_OTHER_NODE': str(fco.changectx()),
511 'HG_BASE_NODE': str(fca.changectx()),
511 'HG_BASE_NODE': str(fca.changectx()),
512 'HG_MY_ISLINK': 'l' in fcd.flags(),
512 'HG_MY_ISLINK': 'l' in fcd.flags(),
513 'HG_OTHER_ISLINK': 'l' in fco.flags(),
513 'HG_OTHER_ISLINK': 'l' in fco.flags(),
514 'HG_BASE_ISLINK': 'l' in fca.flags(),
514 'HG_BASE_ISLINK': 'l' in fca.flags(),
515 }
515 }
516
516
517 ui = repo.ui
517 ui = repo.ui
518
518
519 args = _toolstr(ui, tool, "args", '$local $base $other')
519 args = _toolstr(ui, tool, "args", '$local $base $other')
520 if "$output" in args:
520 if "$output" in args:
521 out, a = a, back # read input from backup, write to original
521 out, a = a, back # read input from backup, write to original
522 replace = {'local': a, 'base': b, 'other': c, 'output': out}
522 replace = {'local': a, 'base': b, 'other': c, 'output': out}
523 args = util.interpolate(r'\$', replace, args,
523 args = util.interpolate(r'\$', replace, args,
524 lambda s: util.shellquote(util.localpath(s)))
524 lambda s: util.shellquote(util.localpath(s)))
525 cmd = toolpath + ' ' + args
525 cmd = toolpath + ' ' + args
526 if _toolbool(ui, tool, "gui"):
526 if _toolbool(ui, tool, "gui"):
527 repo.ui.status(_('running merge tool %s for file %s\n') %
527 repo.ui.status(_('running merge tool %s for file %s\n') %
528 (tool, fcd.path()))
528 (tool, fcd.path()))
529 repo.ui.debug('launching merge tool: %s\n' % cmd)
529 repo.ui.debug('launching merge tool: %s\n' % cmd)
530 r = ui.system(cmd, cwd=repo.root, environ=env, blockedtag='mergetool')
530 r = ui.system(cmd, cwd=repo.root, environ=env, blockedtag='mergetool')
531 repo.ui.debug('merge tool returned: %s\n' % r)
531 repo.ui.debug('merge tool returned: %s\n' % r)
532 return True, r, False
532 return True, r, False
533
533
534 def _formatconflictmarker(repo, ctx, template, label, pad):
534 def _formatconflictmarker(repo, ctx, template, label, pad):
535 """Applies the given template to the ctx, prefixed by the label.
535 """Applies the given template to the ctx, prefixed by the label.
536
536
537 Pad is the minimum width of the label prefix, so that multiple markers
537 Pad is the minimum width of the label prefix, so that multiple markers
538 can have aligned templated parts.
538 can have aligned templated parts.
539 """
539 """
540 if ctx.node() is None:
540 if ctx.node() is None:
541 ctx = ctx.p1()
541 ctx = ctx.p1()
542
542
543 props = templatekw.keywords.copy()
543 props = templatekw.keywords.copy()
544 props['templ'] = template
544 props['templ'] = template
545 props['ctx'] = ctx
545 props['ctx'] = ctx
546 props['repo'] = repo
546 props['repo'] = repo
547 templateresult = template.render(props)
547 templateresult = template.render(props)
548
548
549 label = ('%s:' % label).ljust(pad + 1)
549 label = ('%s:' % label).ljust(pad + 1)
550 mark = '%s %s' % (label, templateresult)
550 mark = '%s %s' % (label, templateresult)
551
551
552 if mark:
552 if mark:
553 mark = mark.splitlines()[0] # split for safety
553 mark = mark.splitlines()[0] # split for safety
554
554
555 # 8 for the prefix of conflict marker lines (e.g. '<<<<<<< ')
555 # 8 for the prefix of conflict marker lines (e.g. '<<<<<<< ')
556 return util.ellipsis(mark, 80 - 8)
556 return util.ellipsis(mark, 80 - 8)
557
557
558 _defaultconflictmarker = ('{node|short} '
559 '{ifeq(tags, "tip", "", '
560 'ifeq(tags, "", "", "{tags} "))}'
561 '{if(bookmarks, "{bookmarks} ")}'
562 '{ifeq(branch, "default", "", "{branch} ")}'
563 '- {author|user}: {desc|firstline}')
564
565 _defaultconflictlabels = ['local', 'other']
558 _defaultconflictlabels = ['local', 'other']
566
559
567 def _formatlabels(repo, fcd, fco, fca, labels):
560 def _formatlabels(repo, fcd, fco, fca, labels):
568 """Formats the given labels using the conflict marker template.
561 """Formats the given labels using the conflict marker template.
569
562
570 Returns a list of formatted labels.
563 Returns a list of formatted labels.
571 """
564 """
572 cd = fcd.changectx()
565 cd = fcd.changectx()
573 co = fco.changectx()
566 co = fco.changectx()
574 ca = fca.changectx()
567 ca = fca.changectx()
575
568
576 ui = repo.ui
569 ui = repo.ui
577 template = ui.config('ui', 'mergemarkertemplate', _defaultconflictmarker)
570 template = ui.config('ui', 'mergemarkertemplate')
578 template = templater.unquotestring(template)
571 template = templater.unquotestring(template)
579 tmpl = formatter.maketemplater(ui, template)
572 tmpl = formatter.maketemplater(ui, template)
580
573
581 pad = max(len(l) for l in labels)
574 pad = max(len(l) for l in labels)
582
575
583 newlabels = [_formatconflictmarker(repo, cd, tmpl, labels[0], pad),
576 newlabels = [_formatconflictmarker(repo, cd, tmpl, labels[0], pad),
584 _formatconflictmarker(repo, co, tmpl, labels[1], pad)]
577 _formatconflictmarker(repo, co, tmpl, labels[1], pad)]
585 if len(labels) > 2:
578 if len(labels) > 2:
586 newlabels.append(_formatconflictmarker(repo, ca, tmpl, labels[2], pad))
579 newlabels.append(_formatconflictmarker(repo, ca, tmpl, labels[2], pad))
587 return newlabels
580 return newlabels
588
581
589 def partextras(labels):
582 def partextras(labels):
590 """Return a dictionary of extra labels for use in prompts to the user
583 """Return a dictionary of extra labels for use in prompts to the user
591
584
592 Intended use is in strings of the form "(l)ocal%(l)s".
585 Intended use is in strings of the form "(l)ocal%(l)s".
593 """
586 """
594 if labels is None:
587 if labels is None:
595 return {
588 return {
596 "l": "",
589 "l": "",
597 "o": "",
590 "o": "",
598 }
591 }
599
592
600 return {
593 return {
601 "l": " [%s]" % labels[0],
594 "l": " [%s]" % labels[0],
602 "o": " [%s]" % labels[1],
595 "o": " [%s]" % labels[1],
603 }
596 }
604
597
605 def _filemerge(premerge, repo, mynode, orig, fcd, fco, fca, labels=None):
598 def _filemerge(premerge, repo, mynode, orig, fcd, fco, fca, labels=None):
606 """perform a 3-way merge in the working directory
599 """perform a 3-way merge in the working directory
607
600
608 premerge = whether this is a premerge
601 premerge = whether this is a premerge
609 mynode = parent node before merge
602 mynode = parent node before merge
610 orig = original local filename before merge
603 orig = original local filename before merge
611 fco = other file context
604 fco = other file context
612 fca = ancestor file context
605 fca = ancestor file context
613 fcd = local file context for current/destination file
606 fcd = local file context for current/destination file
614
607
615 Returns whether the merge is complete, the return value of the merge, and
608 Returns whether the merge is complete, the return value of the merge, and
616 a boolean indicating whether the file was deleted from disk."""
609 a boolean indicating whether the file was deleted from disk."""
617
610
618 def temp(prefix, ctx):
611 def temp(prefix, ctx):
619 fullbase, ext = os.path.splitext(ctx.path())
612 fullbase, ext = os.path.splitext(ctx.path())
620 pre = "%s~%s." % (os.path.basename(fullbase), prefix)
613 pre = "%s~%s." % (os.path.basename(fullbase), prefix)
621 (fd, name) = tempfile.mkstemp(prefix=pre, suffix=ext)
614 (fd, name) = tempfile.mkstemp(prefix=pre, suffix=ext)
622 data = repo.wwritedata(ctx.path(), ctx.data())
615 data = repo.wwritedata(ctx.path(), ctx.data())
623 f = os.fdopen(fd, pycompat.sysstr("wb"))
616 f = os.fdopen(fd, pycompat.sysstr("wb"))
624 f.write(data)
617 f.write(data)
625 f.close()
618 f.close()
626 return name
619 return name
627
620
628 if not fco.cmp(fcd): # files identical?
621 if not fco.cmp(fcd): # files identical?
629 return True, None, False
622 return True, None, False
630
623
631 ui = repo.ui
624 ui = repo.ui
632 fd = fcd.path()
625 fd = fcd.path()
633 binary = fcd.isbinary() or fco.isbinary() or fca.isbinary()
626 binary = fcd.isbinary() or fco.isbinary() or fca.isbinary()
634 symlink = 'l' in fcd.flags() + fco.flags()
627 symlink = 'l' in fcd.flags() + fco.flags()
635 changedelete = fcd.isabsent() or fco.isabsent()
628 changedelete = fcd.isabsent() or fco.isabsent()
636 tool, toolpath = _picktool(repo, ui, fd, binary, symlink, changedelete)
629 tool, toolpath = _picktool(repo, ui, fd, binary, symlink, changedelete)
637 if tool in internals and tool.startswith('internal:'):
630 if tool in internals and tool.startswith('internal:'):
638 # normalize to new-style names (':merge' etc)
631 # normalize to new-style names (':merge' etc)
639 tool = tool[len('internal'):]
632 tool = tool[len('internal'):]
640 ui.debug("picked tool '%s' for %s (binary %s symlink %s changedelete %s)\n"
633 ui.debug("picked tool '%s' for %s (binary %s symlink %s changedelete %s)\n"
641 % (tool, fd, pycompat.bytestr(binary), pycompat.bytestr(symlink),
634 % (tool, fd, pycompat.bytestr(binary), pycompat.bytestr(symlink),
642 pycompat.bytestr(changedelete)))
635 pycompat.bytestr(changedelete)))
643
636
644 if tool in internals:
637 if tool in internals:
645 func = internals[tool]
638 func = internals[tool]
646 mergetype = func.mergetype
639 mergetype = func.mergetype
647 onfailure = func.onfailure
640 onfailure = func.onfailure
648 precheck = func.precheck
641 precheck = func.precheck
649 else:
642 else:
650 func = _xmerge
643 func = _xmerge
651 mergetype = fullmerge
644 mergetype = fullmerge
652 onfailure = _("merging %s failed!\n")
645 onfailure = _("merging %s failed!\n")
653 precheck = None
646 precheck = None
654
647
655 toolconf = tool, toolpath, binary, symlink
648 toolconf = tool, toolpath, binary, symlink
656
649
657 if mergetype == nomerge:
650 if mergetype == nomerge:
658 r, deleted = func(repo, mynode, orig, fcd, fco, fca, toolconf, labels)
651 r, deleted = func(repo, mynode, orig, fcd, fco, fca, toolconf, labels)
659 return True, r, deleted
652 return True, r, deleted
660
653
661 if premerge:
654 if premerge:
662 if orig != fco.path():
655 if orig != fco.path():
663 ui.status(_("merging %s and %s to %s\n") % (orig, fco.path(), fd))
656 ui.status(_("merging %s and %s to %s\n") % (orig, fco.path(), fd))
664 else:
657 else:
665 ui.status(_("merging %s\n") % fd)
658 ui.status(_("merging %s\n") % fd)
666
659
667 ui.debug("my %s other %s ancestor %s\n" % (fcd, fco, fca))
660 ui.debug("my %s other %s ancestor %s\n" % (fcd, fco, fca))
668
661
669 if precheck and not precheck(repo, mynode, orig, fcd, fco, fca,
662 if precheck and not precheck(repo, mynode, orig, fcd, fco, fca,
670 toolconf):
663 toolconf):
671 if onfailure:
664 if onfailure:
672 ui.warn(onfailure % fd)
665 ui.warn(onfailure % fd)
673 return True, 1, False
666 return True, 1, False
674
667
675 a = repo.wjoin(fd)
668 a = repo.wjoin(fd)
676 b = temp("base", fca)
669 b = temp("base", fca)
677 c = temp("other", fco)
670 c = temp("other", fco)
678 if not fcd.isabsent():
671 if not fcd.isabsent():
679 back = scmutil.origpath(ui, repo, a)
672 back = scmutil.origpath(ui, repo, a)
680 if premerge:
673 if premerge:
681 util.copyfile(a, back)
674 util.copyfile(a, back)
682 else:
675 else:
683 back = None
676 back = None
684 files = (a, b, c, back)
677 files = (a, b, c, back)
685
678
686 r = 1
679 r = 1
687 try:
680 try:
688 markerstyle = ui.config('ui', 'mergemarkers')
681 markerstyle = ui.config('ui', 'mergemarkers')
689 if not labels:
682 if not labels:
690 labels = _defaultconflictlabels
683 labels = _defaultconflictlabels
691 if markerstyle != 'basic':
684 if markerstyle != 'basic':
692 labels = _formatlabels(repo, fcd, fco, fca, labels)
685 labels = _formatlabels(repo, fcd, fco, fca, labels)
693
686
694 if premerge and mergetype == fullmerge:
687 if premerge and mergetype == fullmerge:
695 r = _premerge(repo, fcd, fco, fca, toolconf, files, labels=labels)
688 r = _premerge(repo, fcd, fco, fca, toolconf, files, labels=labels)
696 # complete if premerge successful (r is 0)
689 # complete if premerge successful (r is 0)
697 return not r, r, False
690 return not r, r, False
698
691
699 needcheck, r, deleted = func(repo, mynode, orig, fcd, fco, fca,
692 needcheck, r, deleted = func(repo, mynode, orig, fcd, fco, fca,
700 toolconf, files, labels=labels)
693 toolconf, files, labels=labels)
701
694
702 if needcheck:
695 if needcheck:
703 r = _check(r, ui, tool, fcd, files)
696 r = _check(r, ui, tool, fcd, files)
704
697
705 if r:
698 if r:
706 if onfailure:
699 if onfailure:
707 ui.warn(onfailure % fd)
700 ui.warn(onfailure % fd)
708
701
709 return True, r, deleted
702 return True, r, deleted
710 finally:
703 finally:
711 if not r and back is not None:
704 if not r and back is not None:
712 util.unlink(back)
705 util.unlink(back)
713 util.unlink(b)
706 util.unlink(b)
714 util.unlink(c)
707 util.unlink(c)
715
708
716 def _check(r, ui, tool, fcd, files):
709 def _check(r, ui, tool, fcd, files):
717 fd = fcd.path()
710 fd = fcd.path()
718 a, b, c, back = files
711 a, b, c, back = files
719
712
720 if not r and (_toolbool(ui, tool, "checkconflicts") or
713 if not r and (_toolbool(ui, tool, "checkconflicts") or
721 'conflicts' in _toollist(ui, tool, "check")):
714 'conflicts' in _toollist(ui, tool, "check")):
722 if re.search("^(<<<<<<< .*|=======|>>>>>>> .*)$", fcd.data(),
715 if re.search("^(<<<<<<< .*|=======|>>>>>>> .*)$", fcd.data(),
723 re.MULTILINE):
716 re.MULTILINE):
724 r = 1
717 r = 1
725
718
726 checked = False
719 checked = False
727 if 'prompt' in _toollist(ui, tool, "check"):
720 if 'prompt' in _toollist(ui, tool, "check"):
728 checked = True
721 checked = True
729 if ui.promptchoice(_("was merge of '%s' successful (yn)?"
722 if ui.promptchoice(_("was merge of '%s' successful (yn)?"
730 "$$ &Yes $$ &No") % fd, 1):
723 "$$ &Yes $$ &No") % fd, 1):
731 r = 1
724 r = 1
732
725
733 if not r and not checked and (_toolbool(ui, tool, "checkchanged") or
726 if not r and not checked and (_toolbool(ui, tool, "checkchanged") or
734 'changed' in
727 'changed' in
735 _toollist(ui, tool, "check")):
728 _toollist(ui, tool, "check")):
736 if back is not None and filecmp.cmp(a, back):
729 if back is not None and filecmp.cmp(a, back):
737 if ui.promptchoice(_(" output file %s appears unchanged\n"
730 if ui.promptchoice(_(" output file %s appears unchanged\n"
738 "was merge successful (yn)?"
731 "was merge successful (yn)?"
739 "$$ &Yes $$ &No") % fd, 1):
732 "$$ &Yes $$ &No") % fd, 1):
740 r = 1
733 r = 1
741
734
742 if back is not None and _toolbool(ui, tool, "fixeol"):
735 if back is not None and _toolbool(ui, tool, "fixeol"):
743 _matcheol(a, back)
736 _matcheol(a, back)
744
737
745 return r
738 return r
746
739
747 def premerge(repo, mynode, orig, fcd, fco, fca, labels=None):
740 def premerge(repo, mynode, orig, fcd, fco, fca, labels=None):
748 return _filemerge(True, repo, mynode, orig, fcd, fco, fca, labels=labels)
741 return _filemerge(True, repo, mynode, orig, fcd, fco, fca, labels=labels)
749
742
750 def filemerge(repo, mynode, orig, fcd, fco, fca, labels=None):
743 def filemerge(repo, mynode, orig, fcd, fco, fca, labels=None):
751 return _filemerge(False, repo, mynode, orig, fcd, fco, fca, labels=labels)
744 return _filemerge(False, repo, mynode, orig, fcd, fco, fca, labels=labels)
752
745
753 # tell hggettext to extract docstrings from these functions:
746 # tell hggettext to extract docstrings from these functions:
754 i18nfunctions = internals.values()
747 i18nfunctions = internals.values()
General Comments 0
You need to be logged in to leave comments. Login now