##// END OF EJS Templates
filemerge: add support for partial conflict resolution by external tool...
Martin von Zweigbergk -
r49838:f3aafd78 default
parent child Browse files
Show More
@@ -0,0 +1,209 b''
1 Test support for partial-resolution tools
2
3 Create a tool that resolves conflicts after line 5 by simply dropping those
4 lines (even if there are no conflicts there)
5 $ cat >> "$TESTTMP/head.sh" <<'EOF'
6 > #!/bin/sh
7 > for f in "$@"; do
8 > head -5 $f > tmp
9 > mv -f tmp $f
10 > done
11 > EOF
12 $ chmod +x "$TESTTMP/head.sh"
13 ...and another tool that keeps only the last 5 lines instead of the first 5.
14 $ cat >> "$TESTTMP/tail.sh" <<'EOF'
15 > #!/bin/sh
16 > for f in "$@"; do
17 > tail -5 $f > tmp
18 > mv -f tmp $f
19 > done
20 > EOF
21 $ chmod +x "$TESTTMP/tail.sh"
22
23 Set up both tools to run on all patterns (the default), and let the `tail` tool
24 run after the `head` tool, which means it will have no effect (we'll override it
25 to test order later)
26 $ cat >> "$HGRCPATH" <<EOF
27 > [partial-merge-tools]
28 > head.executable=$TESTTMP/head.sh
29 > tail.executable=$TESTTMP/tail.sh
30 > tail.order=1
31 > EOF
32
33 $ make_commit() {
34 > echo "$@" | xargs -n1 > file
35 > hg add file 2> /dev/null
36 > hg ci -m "$*"
37 > }
38
39
40 Let a partial-resolution tool resolve some conflicts and leave other conflicts
41 for the regular merge tool (:merge3 here)
42
43 $ hg init repo
44 $ cd repo
45 $ make_commit a b c d e f
46 $ make_commit a b2 c d e f2
47 $ hg up 0
48 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
49 $ make_commit a b3 c d e f3
50 created new head
51 $ hg merge 1 -t :merge3
52 merging file
53 warning: conflicts while merging file! (edit, then use 'hg resolve --mark')
54 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
55 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
56 [1]
57 $ cat file
58 a
59 <<<<<<< working copy: e11a49d4b620 - test: a b3 c d e f3
60 b3
61 ||||||| common ancestor: 8ae8bb9cc43a - test: a b c d e f
62 b
63 =======
64 b2
65 >>>>>>> merge rev: fbc096a40cc5 - test: a b2 c d e f2
66 c
67 d
68 e
69
70
71 With premerge=keep, the partial-resolution tools runs before and doesn't see
72 the conflict markers
73
74 $ hg up -C 2
75 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
76 $ cat >> .hg/hgrc <<EOF
77 > [merge-tools]
78 > my-local.executable = cat
79 > my-local.args = $local
80 > my-local.premerge = keep-merge3
81 > EOF
82 $ hg merge 1 -t my-local
83 merging file
84 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
85 (branch merge, don't forget to commit)
86 $ cat file
87 a
88 <<<<<<< working copy: e11a49d4b620 - test: a b3 c d e f3
89 b3
90 ||||||| common ancestor: 8ae8bb9cc43a - test: a b c d e f
91 b
92 =======
93 b2
94 >>>>>>> merge rev: fbc096a40cc5 - test: a b2 c d e f2
95 c
96 d
97 e
98
99
100 When a partial-resolution tool resolves all conflicts, the resolution should
101 be recorded and the regular merge tool should not be invoked for the file.
102
103 $ hg up -C 0
104 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
105 $ make_commit a b c d e f2
106 created new head
107 $ hg up 0
108 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
109 $ make_commit a b c d e f3
110 created new head
111 $ hg merge 3 -t false
112 merging file
113 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
114 (branch merge, don't forget to commit)
115 $ cat file
116 a
117 b
118 c
119 d
120 e
121
122
123 Only tools whose patterns match are run. We make `head` not match here, so
124 only `tail` should run
125
126 $ hg up -C 4
127 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
128 $ hg merge 3 -t :merge3 --config partial-merge-tools.head.patterns=other
129 merging file
130 warning: conflicts while merging file! (edit, then use 'hg resolve --mark')
131 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
132 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
133 [1]
134 $ cat file
135 b
136 c
137 d
138 e
139 <<<<<<< working copy: d57edaa6e21a - test: a b c d e f3
140 f3
141 ||||||| common ancestor: 8ae8bb9cc43a - test: a b c d e f
142 f
143 =======
144 f2
145 >>>>>>> merge rev: 8c217da987be - test: a b c d e f2
146
147
148 If there are several matching tools, they are run in requested order. We move
149 `head` after `tail` in order here so it has no effect (the conflict in "f" thus
150 remains).
151
152 $ hg up -C 4
153 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
154 $ hg merge 3 -t :merge3 --config partial-merge-tools.head.order=2
155 merging file
156 warning: conflicts while merging file! (edit, then use 'hg resolve --mark')
157 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
158 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
159 [1]
160 $ cat file
161 b
162 c
163 d
164 e
165 <<<<<<< working copy: d57edaa6e21a - test: a b c d e f3
166 f3
167 ||||||| common ancestor: 8ae8bb9cc43a - test: a b c d e f
168 f
169 =======
170 f2
171 >>>>>>> merge rev: 8c217da987be - test: a b c d e f2
172
173
174 When using "nomerge" tools (e.g. `:other`), the partial-resolution tools
175 should not be run.
176
177 $ hg up -C 4
178 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
179 $ hg merge 3 -t :other
180 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
181 (branch merge, don't forget to commit)
182 $ cat file
183 a
184 b
185 c
186 d
187 e
188 f2
189
190
191 If a partial-resolution tool resolved some conflict and simplemerge can
192 merge the rest, then the regular merge tool should not be used. Here we merge
193 "a b c d e3 f3" with "a b2 c d e f2". The `head` tool resolves the conflict in
194 "f" and the internal simplemerge merges the remaining changes in "b" and "e".
195
196 $ hg up -C 0
197 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
198 $ make_commit a b c d e3 f3
199 created new head
200 $ hg merge 1 -t false
201 merging file
202 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
203 (branch merge, don't forget to commit)
204 $ cat file
205 a
206 b2
207 c
208 d
209 e3
@@ -1,2709 +1,2740 b''
1 1 # configitems.py - centralized declaration of configuration option
2 2 #
3 3 # Copyright 2017 Pierre-Yves David <pierre-yves.david@octobus.net>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8
9 9 import functools
10 10 import re
11 11
12 12 from . import (
13 13 encoding,
14 14 error,
15 15 )
16 16
17 17
18 18 def loadconfigtable(ui, extname, configtable):
19 19 """update config item known to the ui with the extension ones"""
20 20 for section, items in sorted(configtable.items()):
21 21 knownitems = ui._knownconfig.setdefault(section, itemregister())
22 22 knownkeys = set(knownitems)
23 23 newkeys = set(items)
24 24 for key in sorted(knownkeys & newkeys):
25 25 msg = b"extension '%s' overwrite config item '%s.%s'"
26 26 msg %= (extname, section, key)
27 27 ui.develwarn(msg, config=b'warn-config')
28 28
29 29 knownitems.update(items)
30 30
31 31
32 32 class configitem:
33 33 """represent a known config item
34 34
35 35 :section: the official config section where to find this item,
36 36 :name: the official name within the section,
37 37 :default: default value for this item,
38 38 :alias: optional list of tuples as alternatives,
39 39 :generic: this is a generic definition, match name using regular expression.
40 40 """
41 41
42 42 def __init__(
43 43 self,
44 44 section,
45 45 name,
46 46 default=None,
47 47 alias=(),
48 48 generic=False,
49 49 priority=0,
50 50 experimental=False,
51 51 ):
52 52 self.section = section
53 53 self.name = name
54 54 self.default = default
55 55 self.alias = list(alias)
56 56 self.generic = generic
57 57 self.priority = priority
58 58 self.experimental = experimental
59 59 self._re = None
60 60 if generic:
61 61 self._re = re.compile(self.name)
62 62
63 63
64 64 class itemregister(dict):
65 65 """A specialized dictionary that can handle wild-card selection"""
66 66
67 67 def __init__(self):
68 68 super(itemregister, self).__init__()
69 69 self._generics = set()
70 70
71 71 def update(self, other):
72 72 super(itemregister, self).update(other)
73 73 self._generics.update(other._generics)
74 74
75 75 def __setitem__(self, key, item):
76 76 super(itemregister, self).__setitem__(key, item)
77 77 if item.generic:
78 78 self._generics.add(item)
79 79
80 80 def get(self, key):
81 81 baseitem = super(itemregister, self).get(key)
82 82 if baseitem is not None and not baseitem.generic:
83 83 return baseitem
84 84
85 85 # search for a matching generic item
86 86 generics = sorted(self._generics, key=(lambda x: (x.priority, x.name)))
87 87 for item in generics:
88 88 # we use 'match' instead of 'search' to make the matching simpler
89 89 # for people unfamiliar with regular expression. Having the match
90 90 # rooted to the start of the string will produce less surprising
91 91 # result for user writing simple regex for sub-attribute.
92 92 #
93 93 # For example using "color\..*" match produces an unsurprising
94 94 # result, while using search could suddenly match apparently
95 95 # unrelated configuration that happens to contains "color."
96 96 # anywhere. This is a tradeoff where we favor requiring ".*" on
97 97 # some match to avoid the need to prefix most pattern with "^".
98 98 # The "^" seems more error prone.
99 99 if item._re.match(key):
100 100 return item
101 101
102 102 return None
103 103
104 104
105 105 coreitems = {}
106 106
107 107
108 108 def _register(configtable, *args, **kwargs):
109 109 item = configitem(*args, **kwargs)
110 110 section = configtable.setdefault(item.section, itemregister())
111 111 if item.name in section:
112 112 msg = b"duplicated config item registration for '%s.%s'"
113 113 raise error.ProgrammingError(msg % (item.section, item.name))
114 114 section[item.name] = item
115 115
116 116
117 117 # special value for case where the default is derived from other values
118 118 dynamicdefault = object()
119 119
120 120 # Registering actual config items
121 121
122 122
123 123 def getitemregister(configtable):
124 124 f = functools.partial(_register, configtable)
125 125 # export pseudo enum as configitem.*
126 126 f.dynamicdefault = dynamicdefault
127 127 return f
128 128
129 129
130 130 coreconfigitem = getitemregister(coreitems)
131 131
132 132
133 133 def _registerdiffopts(section, configprefix=b''):
134 134 coreconfigitem(
135 135 section,
136 136 configprefix + b'nodates',
137 137 default=False,
138 138 )
139 139 coreconfigitem(
140 140 section,
141 141 configprefix + b'showfunc',
142 142 default=False,
143 143 )
144 144 coreconfigitem(
145 145 section,
146 146 configprefix + b'unified',
147 147 default=None,
148 148 )
149 149 coreconfigitem(
150 150 section,
151 151 configprefix + b'git',
152 152 default=False,
153 153 )
154 154 coreconfigitem(
155 155 section,
156 156 configprefix + b'ignorews',
157 157 default=False,
158 158 )
159 159 coreconfigitem(
160 160 section,
161 161 configprefix + b'ignorewsamount',
162 162 default=False,
163 163 )
164 164 coreconfigitem(
165 165 section,
166 166 configprefix + b'ignoreblanklines',
167 167 default=False,
168 168 )
169 169 coreconfigitem(
170 170 section,
171 171 configprefix + b'ignorewseol',
172 172 default=False,
173 173 )
174 174 coreconfigitem(
175 175 section,
176 176 configprefix + b'nobinary',
177 177 default=False,
178 178 )
179 179 coreconfigitem(
180 180 section,
181 181 configprefix + b'noprefix',
182 182 default=False,
183 183 )
184 184 coreconfigitem(
185 185 section,
186 186 configprefix + b'word-diff',
187 187 default=False,
188 188 )
189 189
190 190
191 191 coreconfigitem(
192 192 b'alias',
193 193 b'.*',
194 194 default=dynamicdefault,
195 195 generic=True,
196 196 )
197 197 coreconfigitem(
198 198 b'auth',
199 199 b'cookiefile',
200 200 default=None,
201 201 )
202 202 _registerdiffopts(section=b'annotate')
203 203 # bookmarks.pushing: internal hack for discovery
204 204 coreconfigitem(
205 205 b'bookmarks',
206 206 b'pushing',
207 207 default=list,
208 208 )
209 209 # bundle.mainreporoot: internal hack for bundlerepo
210 210 coreconfigitem(
211 211 b'bundle',
212 212 b'mainreporoot',
213 213 default=b'',
214 214 )
215 215 coreconfigitem(
216 216 b'censor',
217 217 b'policy',
218 218 default=b'abort',
219 219 experimental=True,
220 220 )
221 221 coreconfigitem(
222 222 b'chgserver',
223 223 b'idletimeout',
224 224 default=3600,
225 225 )
226 226 coreconfigitem(
227 227 b'chgserver',
228 228 b'skiphash',
229 229 default=False,
230 230 )
231 231 coreconfigitem(
232 232 b'cmdserver',
233 233 b'log',
234 234 default=None,
235 235 )
236 236 coreconfigitem(
237 237 b'cmdserver',
238 238 b'max-log-files',
239 239 default=7,
240 240 )
241 241 coreconfigitem(
242 242 b'cmdserver',
243 243 b'max-log-size',
244 244 default=b'1 MB',
245 245 )
246 246 coreconfigitem(
247 247 b'cmdserver',
248 248 b'max-repo-cache',
249 249 default=0,
250 250 experimental=True,
251 251 )
252 252 coreconfigitem(
253 253 b'cmdserver',
254 254 b'message-encodings',
255 255 default=list,
256 256 )
257 257 coreconfigitem(
258 258 b'cmdserver',
259 259 b'track-log',
260 260 default=lambda: [b'chgserver', b'cmdserver', b'repocache'],
261 261 )
262 262 coreconfigitem(
263 263 b'cmdserver',
264 264 b'shutdown-on-interrupt',
265 265 default=True,
266 266 )
267 267 coreconfigitem(
268 268 b'color',
269 269 b'.*',
270 270 default=None,
271 271 generic=True,
272 272 )
273 273 coreconfigitem(
274 274 b'color',
275 275 b'mode',
276 276 default=b'auto',
277 277 )
278 278 coreconfigitem(
279 279 b'color',
280 280 b'pagermode',
281 281 default=dynamicdefault,
282 282 )
283 283 coreconfigitem(
284 284 b'command-templates',
285 285 b'graphnode',
286 286 default=None,
287 287 alias=[(b'ui', b'graphnodetemplate')],
288 288 )
289 289 coreconfigitem(
290 290 b'command-templates',
291 291 b'log',
292 292 default=None,
293 293 alias=[(b'ui', b'logtemplate')],
294 294 )
295 295 coreconfigitem(
296 296 b'command-templates',
297 297 b'mergemarker',
298 298 default=(
299 299 b'{node|short} '
300 300 b'{ifeq(tags, "tip", "", '
301 301 b'ifeq(tags, "", "", "{tags} "))}'
302 302 b'{if(bookmarks, "{bookmarks} ")}'
303 303 b'{ifeq(branch, "default", "", "{branch} ")}'
304 304 b'- {author|user}: {desc|firstline}'
305 305 ),
306 306 alias=[(b'ui', b'mergemarkertemplate')],
307 307 )
308 308 coreconfigitem(
309 309 b'command-templates',
310 310 b'pre-merge-tool-output',
311 311 default=None,
312 312 alias=[(b'ui', b'pre-merge-tool-output-template')],
313 313 )
314 314 coreconfigitem(
315 315 b'command-templates',
316 316 b'oneline-summary',
317 317 default=None,
318 318 )
319 319 coreconfigitem(
320 320 b'command-templates',
321 321 b'oneline-summary.*',
322 322 default=dynamicdefault,
323 323 generic=True,
324 324 )
325 325 _registerdiffopts(section=b'commands', configprefix=b'commit.interactive.')
326 326 coreconfigitem(
327 327 b'commands',
328 328 b'commit.post-status',
329 329 default=False,
330 330 )
331 331 coreconfigitem(
332 332 b'commands',
333 333 b'grep.all-files',
334 334 default=False,
335 335 experimental=True,
336 336 )
337 337 coreconfigitem(
338 338 b'commands',
339 339 b'merge.require-rev',
340 340 default=False,
341 341 )
342 342 coreconfigitem(
343 343 b'commands',
344 344 b'push.require-revs',
345 345 default=False,
346 346 )
347 347 coreconfigitem(
348 348 b'commands',
349 349 b'resolve.confirm',
350 350 default=False,
351 351 )
352 352 coreconfigitem(
353 353 b'commands',
354 354 b'resolve.explicit-re-merge',
355 355 default=False,
356 356 )
357 357 coreconfigitem(
358 358 b'commands',
359 359 b'resolve.mark-check',
360 360 default=b'none',
361 361 )
362 362 _registerdiffopts(section=b'commands', configprefix=b'revert.interactive.')
363 363 coreconfigitem(
364 364 b'commands',
365 365 b'show.aliasprefix',
366 366 default=list,
367 367 )
368 368 coreconfigitem(
369 369 b'commands',
370 370 b'status.relative',
371 371 default=False,
372 372 )
373 373 coreconfigitem(
374 374 b'commands',
375 375 b'status.skipstates',
376 376 default=[],
377 377 experimental=True,
378 378 )
379 379 coreconfigitem(
380 380 b'commands',
381 381 b'status.terse',
382 382 default=b'',
383 383 )
384 384 coreconfigitem(
385 385 b'commands',
386 386 b'status.verbose',
387 387 default=False,
388 388 )
389 389 coreconfigitem(
390 390 b'commands',
391 391 b'update.check',
392 392 default=None,
393 393 )
394 394 coreconfigitem(
395 395 b'commands',
396 396 b'update.requiredest',
397 397 default=False,
398 398 )
399 399 coreconfigitem(
400 400 b'committemplate',
401 401 b'.*',
402 402 default=None,
403 403 generic=True,
404 404 )
405 405 coreconfigitem(
406 406 b'convert',
407 407 b'bzr.saverev',
408 408 default=True,
409 409 )
410 410 coreconfigitem(
411 411 b'convert',
412 412 b'cvsps.cache',
413 413 default=True,
414 414 )
415 415 coreconfigitem(
416 416 b'convert',
417 417 b'cvsps.fuzz',
418 418 default=60,
419 419 )
420 420 coreconfigitem(
421 421 b'convert',
422 422 b'cvsps.logencoding',
423 423 default=None,
424 424 )
425 425 coreconfigitem(
426 426 b'convert',
427 427 b'cvsps.mergefrom',
428 428 default=None,
429 429 )
430 430 coreconfigitem(
431 431 b'convert',
432 432 b'cvsps.mergeto',
433 433 default=None,
434 434 )
435 435 coreconfigitem(
436 436 b'convert',
437 437 b'git.committeractions',
438 438 default=lambda: [b'messagedifferent'],
439 439 )
440 440 coreconfigitem(
441 441 b'convert',
442 442 b'git.extrakeys',
443 443 default=list,
444 444 )
445 445 coreconfigitem(
446 446 b'convert',
447 447 b'git.findcopiesharder',
448 448 default=False,
449 449 )
450 450 coreconfigitem(
451 451 b'convert',
452 452 b'git.remoteprefix',
453 453 default=b'remote',
454 454 )
455 455 coreconfigitem(
456 456 b'convert',
457 457 b'git.renamelimit',
458 458 default=400,
459 459 )
460 460 coreconfigitem(
461 461 b'convert',
462 462 b'git.saverev',
463 463 default=True,
464 464 )
465 465 coreconfigitem(
466 466 b'convert',
467 467 b'git.similarity',
468 468 default=50,
469 469 )
470 470 coreconfigitem(
471 471 b'convert',
472 472 b'git.skipsubmodules',
473 473 default=False,
474 474 )
475 475 coreconfigitem(
476 476 b'convert',
477 477 b'hg.clonebranches',
478 478 default=False,
479 479 )
480 480 coreconfigitem(
481 481 b'convert',
482 482 b'hg.ignoreerrors',
483 483 default=False,
484 484 )
485 485 coreconfigitem(
486 486 b'convert',
487 487 b'hg.preserve-hash',
488 488 default=False,
489 489 )
490 490 coreconfigitem(
491 491 b'convert',
492 492 b'hg.revs',
493 493 default=None,
494 494 )
495 495 coreconfigitem(
496 496 b'convert',
497 497 b'hg.saverev',
498 498 default=False,
499 499 )
500 500 coreconfigitem(
501 501 b'convert',
502 502 b'hg.sourcename',
503 503 default=None,
504 504 )
505 505 coreconfigitem(
506 506 b'convert',
507 507 b'hg.startrev',
508 508 default=None,
509 509 )
510 510 coreconfigitem(
511 511 b'convert',
512 512 b'hg.tagsbranch',
513 513 default=b'default',
514 514 )
515 515 coreconfigitem(
516 516 b'convert',
517 517 b'hg.usebranchnames',
518 518 default=True,
519 519 )
520 520 coreconfigitem(
521 521 b'convert',
522 522 b'ignoreancestorcheck',
523 523 default=False,
524 524 experimental=True,
525 525 )
526 526 coreconfigitem(
527 527 b'convert',
528 528 b'localtimezone',
529 529 default=False,
530 530 )
531 531 coreconfigitem(
532 532 b'convert',
533 533 b'p4.encoding',
534 534 default=dynamicdefault,
535 535 )
536 536 coreconfigitem(
537 537 b'convert',
538 538 b'p4.startrev',
539 539 default=0,
540 540 )
541 541 coreconfigitem(
542 542 b'convert',
543 543 b'skiptags',
544 544 default=False,
545 545 )
546 546 coreconfigitem(
547 547 b'convert',
548 548 b'svn.debugsvnlog',
549 549 default=True,
550 550 )
551 551 coreconfigitem(
552 552 b'convert',
553 553 b'svn.trunk',
554 554 default=None,
555 555 )
556 556 coreconfigitem(
557 557 b'convert',
558 558 b'svn.tags',
559 559 default=None,
560 560 )
561 561 coreconfigitem(
562 562 b'convert',
563 563 b'svn.branches',
564 564 default=None,
565 565 )
566 566 coreconfigitem(
567 567 b'convert',
568 568 b'svn.startrev',
569 569 default=0,
570 570 )
571 571 coreconfigitem(
572 572 b'convert',
573 573 b'svn.dangerous-set-commit-dates',
574 574 default=False,
575 575 )
576 576 coreconfigitem(
577 577 b'debug',
578 578 b'dirstate.delaywrite',
579 579 default=0,
580 580 )
581 581 coreconfigitem(
582 582 b'debug',
583 583 b'revlog.verifyposition.changelog',
584 584 default=b'',
585 585 )
586 586 coreconfigitem(
587 587 b'defaults',
588 588 b'.*',
589 589 default=None,
590 590 generic=True,
591 591 )
592 592 coreconfigitem(
593 593 b'devel',
594 594 b'all-warnings',
595 595 default=False,
596 596 )
597 597 coreconfigitem(
598 598 b'devel',
599 599 b'bundle2.debug',
600 600 default=False,
601 601 )
602 602 coreconfigitem(
603 603 b'devel',
604 604 b'bundle.delta',
605 605 default=b'',
606 606 )
607 607 coreconfigitem(
608 608 b'devel',
609 609 b'cache-vfs',
610 610 default=None,
611 611 )
612 612 coreconfigitem(
613 613 b'devel',
614 614 b'check-locks',
615 615 default=False,
616 616 )
617 617 coreconfigitem(
618 618 b'devel',
619 619 b'check-relroot',
620 620 default=False,
621 621 )
622 622 # Track copy information for all file, not just "added" one (very slow)
623 623 coreconfigitem(
624 624 b'devel',
625 625 b'copy-tracing.trace-all-files',
626 626 default=False,
627 627 )
628 628 coreconfigitem(
629 629 b'devel',
630 630 b'default-date',
631 631 default=None,
632 632 )
633 633 coreconfigitem(
634 634 b'devel',
635 635 b'deprec-warn',
636 636 default=False,
637 637 )
638 638 coreconfigitem(
639 639 b'devel',
640 640 b'disableloaddefaultcerts',
641 641 default=False,
642 642 )
643 643 coreconfigitem(
644 644 b'devel',
645 645 b'warn-empty-changegroup',
646 646 default=False,
647 647 )
648 648 coreconfigitem(
649 649 b'devel',
650 650 b'legacy.exchange',
651 651 default=list,
652 652 )
653 653 # When True, revlogs use a special reference version of the nodemap, that is not
654 654 # performant but is "known" to behave properly.
655 655 coreconfigitem(
656 656 b'devel',
657 657 b'persistent-nodemap',
658 658 default=False,
659 659 )
660 660 coreconfigitem(
661 661 b'devel',
662 662 b'servercafile',
663 663 default=b'',
664 664 )
665 665 coreconfigitem(
666 666 b'devel',
667 667 b'serverexactprotocol',
668 668 default=b'',
669 669 )
670 670 coreconfigitem(
671 671 b'devel',
672 672 b'serverrequirecert',
673 673 default=False,
674 674 )
675 675 coreconfigitem(
676 676 b'devel',
677 677 b'strip-obsmarkers',
678 678 default=True,
679 679 )
680 680 coreconfigitem(
681 681 b'devel',
682 682 b'warn-config',
683 683 default=None,
684 684 )
685 685 coreconfigitem(
686 686 b'devel',
687 687 b'warn-config-default',
688 688 default=None,
689 689 )
690 690 coreconfigitem(
691 691 b'devel',
692 692 b'user.obsmarker',
693 693 default=None,
694 694 )
695 695 coreconfigitem(
696 696 b'devel',
697 697 b'warn-config-unknown',
698 698 default=None,
699 699 )
700 700 coreconfigitem(
701 701 b'devel',
702 702 b'debug.copies',
703 703 default=False,
704 704 )
705 705 coreconfigitem(
706 706 b'devel',
707 707 b'copy-tracing.multi-thread',
708 708 default=True,
709 709 )
710 710 coreconfigitem(
711 711 b'devel',
712 712 b'debug.extensions',
713 713 default=False,
714 714 )
715 715 coreconfigitem(
716 716 b'devel',
717 717 b'debug.repo-filters',
718 718 default=False,
719 719 )
720 720 coreconfigitem(
721 721 b'devel',
722 722 b'debug.peer-request',
723 723 default=False,
724 724 )
725 725 # If discovery.exchange-heads is False, the discovery will not start with
726 726 # remote head fetching and local head querying.
727 727 coreconfigitem(
728 728 b'devel',
729 729 b'discovery.exchange-heads',
730 730 default=True,
731 731 )
732 732 # If discovery.grow-sample is False, the sample size used in set discovery will
733 733 # not be increased through the process
734 734 coreconfigitem(
735 735 b'devel',
736 736 b'discovery.grow-sample',
737 737 default=True,
738 738 )
739 739 # When discovery.grow-sample.dynamic is True, the default, the sample size is
740 740 # adapted to the shape of the undecided set (it is set to the max of:
741 741 # <target-size>, len(roots(undecided)), len(heads(undecided)
742 742 coreconfigitem(
743 743 b'devel',
744 744 b'discovery.grow-sample.dynamic',
745 745 default=True,
746 746 )
747 747 # discovery.grow-sample.rate control the rate at which the sample grow
748 748 coreconfigitem(
749 749 b'devel',
750 750 b'discovery.grow-sample.rate',
751 751 default=1.05,
752 752 )
753 753 # If discovery.randomize is False, random sampling during discovery are
754 754 # deterministic. It is meant for integration tests.
755 755 coreconfigitem(
756 756 b'devel',
757 757 b'discovery.randomize',
758 758 default=True,
759 759 )
760 760 # Control the initial size of the discovery sample
761 761 coreconfigitem(
762 762 b'devel',
763 763 b'discovery.sample-size',
764 764 default=200,
765 765 )
766 766 # Control the initial size of the discovery for initial change
767 767 coreconfigitem(
768 768 b'devel',
769 769 b'discovery.sample-size.initial',
770 770 default=100,
771 771 )
772 772 _registerdiffopts(section=b'diff')
773 773 coreconfigitem(
774 774 b'diff',
775 775 b'merge',
776 776 default=False,
777 777 experimental=True,
778 778 )
779 779 coreconfigitem(
780 780 b'email',
781 781 b'bcc',
782 782 default=None,
783 783 )
784 784 coreconfigitem(
785 785 b'email',
786 786 b'cc',
787 787 default=None,
788 788 )
789 789 coreconfigitem(
790 790 b'email',
791 791 b'charsets',
792 792 default=list,
793 793 )
794 794 coreconfigitem(
795 795 b'email',
796 796 b'from',
797 797 default=None,
798 798 )
799 799 coreconfigitem(
800 800 b'email',
801 801 b'method',
802 802 default=b'smtp',
803 803 )
804 804 coreconfigitem(
805 805 b'email',
806 806 b'reply-to',
807 807 default=None,
808 808 )
809 809 coreconfigitem(
810 810 b'email',
811 811 b'to',
812 812 default=None,
813 813 )
814 814 coreconfigitem(
815 815 b'experimental',
816 816 b'archivemetatemplate',
817 817 default=dynamicdefault,
818 818 )
819 819 coreconfigitem(
820 820 b'experimental',
821 821 b'auto-publish',
822 822 default=b'publish',
823 823 )
824 824 coreconfigitem(
825 825 b'experimental',
826 826 b'bundle-phases',
827 827 default=False,
828 828 )
829 829 coreconfigitem(
830 830 b'experimental',
831 831 b'bundle2-advertise',
832 832 default=True,
833 833 )
834 834 coreconfigitem(
835 835 b'experimental',
836 836 b'bundle2-output-capture',
837 837 default=False,
838 838 )
839 839 coreconfigitem(
840 840 b'experimental',
841 841 b'bundle2.pushback',
842 842 default=False,
843 843 )
844 844 coreconfigitem(
845 845 b'experimental',
846 846 b'bundle2lazylocking',
847 847 default=False,
848 848 )
849 849 coreconfigitem(
850 850 b'experimental',
851 851 b'bundlecomplevel',
852 852 default=None,
853 853 )
854 854 coreconfigitem(
855 855 b'experimental',
856 856 b'bundlecomplevel.bzip2',
857 857 default=None,
858 858 )
859 859 coreconfigitem(
860 860 b'experimental',
861 861 b'bundlecomplevel.gzip',
862 862 default=None,
863 863 )
864 864 coreconfigitem(
865 865 b'experimental',
866 866 b'bundlecomplevel.none',
867 867 default=None,
868 868 )
869 869 coreconfigitem(
870 870 b'experimental',
871 871 b'bundlecomplevel.zstd',
872 872 default=None,
873 873 )
874 874 coreconfigitem(
875 875 b'experimental',
876 876 b'bundlecompthreads',
877 877 default=None,
878 878 )
879 879 coreconfigitem(
880 880 b'experimental',
881 881 b'bundlecompthreads.bzip2',
882 882 default=None,
883 883 )
884 884 coreconfigitem(
885 885 b'experimental',
886 886 b'bundlecompthreads.gzip',
887 887 default=None,
888 888 )
889 889 coreconfigitem(
890 890 b'experimental',
891 891 b'bundlecompthreads.none',
892 892 default=None,
893 893 )
894 894 coreconfigitem(
895 895 b'experimental',
896 896 b'bundlecompthreads.zstd',
897 897 default=None,
898 898 )
899 899 coreconfigitem(
900 900 b'experimental',
901 901 b'changegroup3',
902 902 default=False,
903 903 )
904 904 coreconfigitem(
905 905 b'experimental',
906 906 b'changegroup4',
907 907 default=False,
908 908 )
909 909 coreconfigitem(
910 910 b'experimental',
911 911 b'cleanup-as-archived',
912 912 default=False,
913 913 )
914 914 coreconfigitem(
915 915 b'experimental',
916 916 b'clientcompressionengines',
917 917 default=list,
918 918 )
919 919 coreconfigitem(
920 920 b'experimental',
921 921 b'copytrace',
922 922 default=b'on',
923 923 )
924 924 coreconfigitem(
925 925 b'experimental',
926 926 b'copytrace.movecandidateslimit',
927 927 default=100,
928 928 )
929 929 coreconfigitem(
930 930 b'experimental',
931 931 b'copytrace.sourcecommitlimit',
932 932 default=100,
933 933 )
934 934 coreconfigitem(
935 935 b'experimental',
936 936 b'copies.read-from',
937 937 default=b"filelog-only",
938 938 )
939 939 coreconfigitem(
940 940 b'experimental',
941 941 b'copies.write-to',
942 942 default=b'filelog-only',
943 943 )
944 944 coreconfigitem(
945 945 b'experimental',
946 946 b'crecordtest',
947 947 default=None,
948 948 )
949 949 coreconfigitem(
950 950 b'experimental',
951 951 b'directaccess',
952 952 default=False,
953 953 )
954 954 coreconfigitem(
955 955 b'experimental',
956 956 b'directaccess.revnums',
957 957 default=False,
958 958 )
959 959 coreconfigitem(
960 960 b'experimental',
961 961 b'editortmpinhg',
962 962 default=False,
963 963 )
964 964 coreconfigitem(
965 965 b'experimental',
966 966 b'evolution',
967 967 default=list,
968 968 )
969 969 coreconfigitem(
970 970 b'experimental',
971 971 b'evolution.allowdivergence',
972 972 default=False,
973 973 alias=[(b'experimental', b'allowdivergence')],
974 974 )
975 975 coreconfigitem(
976 976 b'experimental',
977 977 b'evolution.allowunstable',
978 978 default=None,
979 979 )
980 980 coreconfigitem(
981 981 b'experimental',
982 982 b'evolution.createmarkers',
983 983 default=None,
984 984 )
985 985 coreconfigitem(
986 986 b'experimental',
987 987 b'evolution.effect-flags',
988 988 default=True,
989 989 alias=[(b'experimental', b'effect-flags')],
990 990 )
991 991 coreconfigitem(
992 992 b'experimental',
993 993 b'evolution.exchange',
994 994 default=None,
995 995 )
996 996 coreconfigitem(
997 997 b'experimental',
998 998 b'evolution.bundle-obsmarker',
999 999 default=False,
1000 1000 )
1001 1001 coreconfigitem(
1002 1002 b'experimental',
1003 1003 b'evolution.bundle-obsmarker:mandatory',
1004 1004 default=True,
1005 1005 )
1006 1006 coreconfigitem(
1007 1007 b'experimental',
1008 1008 b'log.topo',
1009 1009 default=False,
1010 1010 )
1011 1011 coreconfigitem(
1012 1012 b'experimental',
1013 1013 b'evolution.report-instabilities',
1014 1014 default=True,
1015 1015 )
1016 1016 coreconfigitem(
1017 1017 b'experimental',
1018 1018 b'evolution.track-operation',
1019 1019 default=True,
1020 1020 )
1021 1021 # repo-level config to exclude a revset visibility
1022 1022 #
1023 1023 # The target use case is to use `share` to expose different subset of the same
1024 1024 # repository, especially server side. See also `server.view`.
1025 1025 coreconfigitem(
1026 1026 b'experimental',
1027 1027 b'extra-filter-revs',
1028 1028 default=None,
1029 1029 )
1030 1030 coreconfigitem(
1031 1031 b'experimental',
1032 1032 b'maxdeltachainspan',
1033 1033 default=-1,
1034 1034 )
1035 1035 # tracks files which were undeleted (merge might delete them but we explicitly
1036 1036 # kept/undeleted them) and creates new filenodes for them
1037 1037 coreconfigitem(
1038 1038 b'experimental',
1039 1039 b'merge-track-salvaged',
1040 1040 default=False,
1041 1041 )
1042 1042 coreconfigitem(
1043 1043 b'experimental',
1044 1044 b'mmapindexthreshold',
1045 1045 default=None,
1046 1046 )
1047 1047 coreconfigitem(
1048 1048 b'experimental',
1049 1049 b'narrow',
1050 1050 default=False,
1051 1051 )
1052 1052 coreconfigitem(
1053 1053 b'experimental',
1054 1054 b'nonnormalparanoidcheck',
1055 1055 default=False,
1056 1056 )
1057 1057 coreconfigitem(
1058 1058 b'experimental',
1059 1059 b'exportableenviron',
1060 1060 default=list,
1061 1061 )
1062 1062 coreconfigitem(
1063 1063 b'experimental',
1064 1064 b'extendedheader.index',
1065 1065 default=None,
1066 1066 )
1067 1067 coreconfigitem(
1068 1068 b'experimental',
1069 1069 b'extendedheader.similarity',
1070 1070 default=False,
1071 1071 )
1072 1072 coreconfigitem(
1073 1073 b'experimental',
1074 1074 b'graphshorten',
1075 1075 default=False,
1076 1076 )
1077 1077 coreconfigitem(
1078 1078 b'experimental',
1079 1079 b'graphstyle.parent',
1080 1080 default=dynamicdefault,
1081 1081 )
1082 1082 coreconfigitem(
1083 1083 b'experimental',
1084 1084 b'graphstyle.missing',
1085 1085 default=dynamicdefault,
1086 1086 )
1087 1087 coreconfigitem(
1088 1088 b'experimental',
1089 1089 b'graphstyle.grandparent',
1090 1090 default=dynamicdefault,
1091 1091 )
1092 1092 coreconfigitem(
1093 1093 b'experimental',
1094 1094 b'hook-track-tags',
1095 1095 default=False,
1096 1096 )
1097 1097 coreconfigitem(
1098 1098 b'experimental',
1099 1099 b'httppostargs',
1100 1100 default=False,
1101 1101 )
1102 1102 coreconfigitem(b'experimental', b'nointerrupt', default=False)
1103 1103 coreconfigitem(b'experimental', b'nointerrupt-interactiveonly', default=True)
1104 1104
1105 1105 coreconfigitem(
1106 1106 b'experimental',
1107 1107 b'obsmarkers-exchange-debug',
1108 1108 default=False,
1109 1109 )
1110 1110 coreconfigitem(
1111 1111 b'experimental',
1112 1112 b'remotenames',
1113 1113 default=False,
1114 1114 )
1115 1115 coreconfigitem(
1116 1116 b'experimental',
1117 1117 b'removeemptydirs',
1118 1118 default=True,
1119 1119 )
1120 1120 coreconfigitem(
1121 1121 b'experimental',
1122 1122 b'revert.interactive.select-to-keep',
1123 1123 default=False,
1124 1124 )
1125 1125 coreconfigitem(
1126 1126 b'experimental',
1127 1127 b'revisions.prefixhexnode',
1128 1128 default=False,
1129 1129 )
1130 1130 # "out of experimental" todo list.
1131 1131 #
1132 1132 # * include management of a persistent nodemap in the main docket
1133 1133 # * enforce a "no-truncate" policy for mmap safety
1134 1134 # - for censoring operation
1135 1135 # - for stripping operation
1136 1136 # - for rollback operation
1137 1137 # * proper streaming (race free) of the docket file
1138 1138 # * track garbage data to evemtually allow rewriting -existing- sidedata.
1139 1139 # * Exchange-wise, we will also need to do something more efficient than
1140 1140 # keeping references to the affected revlogs, especially memory-wise when
1141 1141 # rewriting sidedata.
1142 1142 # * introduce a proper solution to reduce the number of filelog related files.
1143 1143 # * use caching for reading sidedata (similar to what we do for data).
1144 1144 # * no longer set offset=0 if sidedata_size=0 (simplify cutoff computation).
1145 1145 # * Improvement to consider
1146 1146 # - avoid compression header in chunk using the default compression?
1147 1147 # - forbid "inline" compression mode entirely?
1148 1148 # - split the data offset and flag field (the 2 bytes save are mostly trouble)
1149 1149 # - keep track of uncompressed -chunk- size (to preallocate memory better)
1150 1150 # - keep track of chain base or size (probably not that useful anymore)
1151 1151 coreconfigitem(
1152 1152 b'experimental',
1153 1153 b'revlogv2',
1154 1154 default=None,
1155 1155 )
1156 1156 coreconfigitem(
1157 1157 b'experimental',
1158 1158 b'revisions.disambiguatewithin',
1159 1159 default=None,
1160 1160 )
1161 1161 coreconfigitem(
1162 1162 b'experimental',
1163 1163 b'rust.index',
1164 1164 default=False,
1165 1165 )
1166 1166 coreconfigitem(
1167 1167 b'experimental',
1168 1168 b'server.filesdata.recommended-batch-size',
1169 1169 default=50000,
1170 1170 )
1171 1171 coreconfigitem(
1172 1172 b'experimental',
1173 1173 b'server.manifestdata.recommended-batch-size',
1174 1174 default=100000,
1175 1175 )
1176 1176 coreconfigitem(
1177 1177 b'experimental',
1178 1178 b'server.stream-narrow-clones',
1179 1179 default=False,
1180 1180 )
1181 1181 coreconfigitem(
1182 1182 b'experimental',
1183 1183 b'single-head-per-branch',
1184 1184 default=False,
1185 1185 )
1186 1186 coreconfigitem(
1187 1187 b'experimental',
1188 1188 b'single-head-per-branch:account-closed-heads',
1189 1189 default=False,
1190 1190 )
1191 1191 coreconfigitem(
1192 1192 b'experimental',
1193 1193 b'single-head-per-branch:public-changes-only',
1194 1194 default=False,
1195 1195 )
1196 1196 coreconfigitem(
1197 1197 b'experimental',
1198 1198 b'sparse-read',
1199 1199 default=False,
1200 1200 )
1201 1201 coreconfigitem(
1202 1202 b'experimental',
1203 1203 b'sparse-read.density-threshold',
1204 1204 default=0.50,
1205 1205 )
1206 1206 coreconfigitem(
1207 1207 b'experimental',
1208 1208 b'sparse-read.min-gap-size',
1209 1209 default=b'65K',
1210 1210 )
1211 1211 coreconfigitem(
1212 1212 b'experimental',
1213 1213 b'treemanifest',
1214 1214 default=False,
1215 1215 )
1216 1216 coreconfigitem(
1217 1217 b'experimental',
1218 1218 b'update.atomic-file',
1219 1219 default=False,
1220 1220 )
1221 1221 coreconfigitem(
1222 1222 b'experimental',
1223 1223 b'web.full-garbage-collection-rate',
1224 1224 default=1, # still forcing a full collection on each request
1225 1225 )
1226 1226 coreconfigitem(
1227 1227 b'experimental',
1228 1228 b'worker.wdir-get-thread-safe',
1229 1229 default=False,
1230 1230 )
1231 1231 coreconfigitem(
1232 1232 b'experimental',
1233 1233 b'worker.repository-upgrade',
1234 1234 default=False,
1235 1235 )
1236 1236 coreconfigitem(
1237 1237 b'experimental',
1238 1238 b'xdiff',
1239 1239 default=False,
1240 1240 )
1241 1241 coreconfigitem(
1242 1242 b'extensions',
1243 1243 b'[^:]*',
1244 1244 default=None,
1245 1245 generic=True,
1246 1246 )
1247 1247 coreconfigitem(
1248 1248 b'extensions',
1249 1249 b'[^:]*:required',
1250 1250 default=False,
1251 1251 generic=True,
1252 1252 )
1253 1253 coreconfigitem(
1254 1254 b'extdata',
1255 1255 b'.*',
1256 1256 default=None,
1257 1257 generic=True,
1258 1258 )
1259 1259 coreconfigitem(
1260 1260 b'format',
1261 1261 b'bookmarks-in-store',
1262 1262 default=False,
1263 1263 )
1264 1264 coreconfigitem(
1265 1265 b'format',
1266 1266 b'chunkcachesize',
1267 1267 default=None,
1268 1268 experimental=True,
1269 1269 )
1270 1270 coreconfigitem(
1271 1271 # Enable this dirstate format *when creating a new repository*.
1272 1272 # Which format to use for existing repos is controlled by .hg/requires
1273 1273 b'format',
1274 1274 b'use-dirstate-v2',
1275 1275 default=False,
1276 1276 experimental=True,
1277 1277 alias=[(b'format', b'exp-rc-dirstate-v2')],
1278 1278 )
1279 1279 coreconfigitem(
1280 1280 b'format',
1281 1281 b'use-dirstate-tracked-hint',
1282 1282 default=False,
1283 1283 experimental=True,
1284 1284 )
1285 1285 coreconfigitem(
1286 1286 b'format',
1287 1287 b'use-dirstate-tracked-hint.version',
1288 1288 default=1,
1289 1289 experimental=True,
1290 1290 )
1291 1291 coreconfigitem(
1292 1292 b'format',
1293 1293 b'dotencode',
1294 1294 default=True,
1295 1295 )
1296 1296 coreconfigitem(
1297 1297 b'format',
1298 1298 b'generaldelta',
1299 1299 default=False,
1300 1300 experimental=True,
1301 1301 )
1302 1302 coreconfigitem(
1303 1303 b'format',
1304 1304 b'manifestcachesize',
1305 1305 default=None,
1306 1306 experimental=True,
1307 1307 )
1308 1308 coreconfigitem(
1309 1309 b'format',
1310 1310 b'maxchainlen',
1311 1311 default=dynamicdefault,
1312 1312 experimental=True,
1313 1313 )
1314 1314 coreconfigitem(
1315 1315 b'format',
1316 1316 b'obsstore-version',
1317 1317 default=None,
1318 1318 )
1319 1319 coreconfigitem(
1320 1320 b'format',
1321 1321 b'sparse-revlog',
1322 1322 default=True,
1323 1323 )
1324 1324 coreconfigitem(
1325 1325 b'format',
1326 1326 b'revlog-compression',
1327 1327 default=lambda: [b'zstd', b'zlib'],
1328 1328 alias=[(b'experimental', b'format.compression')],
1329 1329 )
1330 1330 # Experimental TODOs:
1331 1331 #
1332 1332 # * Same as for revlogv2 (but for the reduction of the number of files)
1333 1333 # * Actually computing the rank of changesets
1334 1334 # * Improvement to investigate
1335 1335 # - storing .hgtags fnode
1336 1336 # - storing branch related identifier
1337 1337
1338 1338 coreconfigitem(
1339 1339 b'format',
1340 1340 b'exp-use-changelog-v2',
1341 1341 default=None,
1342 1342 experimental=True,
1343 1343 )
1344 1344 coreconfigitem(
1345 1345 b'format',
1346 1346 b'usefncache',
1347 1347 default=True,
1348 1348 )
1349 1349 coreconfigitem(
1350 1350 b'format',
1351 1351 b'usegeneraldelta',
1352 1352 default=True,
1353 1353 )
1354 1354 coreconfigitem(
1355 1355 b'format',
1356 1356 b'usestore',
1357 1357 default=True,
1358 1358 )
1359 1359
1360 1360
1361 1361 def _persistent_nodemap_default():
1362 1362 """compute `use-persistent-nodemap` default value
1363 1363
1364 1364 The feature is disabled unless a fast implementation is available.
1365 1365 """
1366 1366 from . import policy
1367 1367
1368 1368 return policy.importrust('revlog') is not None
1369 1369
1370 1370
1371 1371 coreconfigitem(
1372 1372 b'format',
1373 1373 b'use-persistent-nodemap',
1374 1374 default=_persistent_nodemap_default,
1375 1375 )
1376 1376 coreconfigitem(
1377 1377 b'format',
1378 1378 b'exp-use-copies-side-data-changeset',
1379 1379 default=False,
1380 1380 experimental=True,
1381 1381 )
1382 1382 coreconfigitem(
1383 1383 b'format',
1384 1384 b'use-share-safe',
1385 1385 default=True,
1386 1386 )
1387 1387 coreconfigitem(
1388 1388 b'format',
1389 1389 b'internal-phase',
1390 1390 default=False,
1391 1391 experimental=True,
1392 1392 )
1393 1393 coreconfigitem(
1394 1394 b'fsmonitor',
1395 1395 b'warn_when_unused',
1396 1396 default=True,
1397 1397 )
1398 1398 coreconfigitem(
1399 1399 b'fsmonitor',
1400 1400 b'warn_update_file_count',
1401 1401 default=50000,
1402 1402 )
1403 1403 coreconfigitem(
1404 1404 b'fsmonitor',
1405 1405 b'warn_update_file_count_rust',
1406 1406 default=400000,
1407 1407 )
1408 1408 coreconfigitem(
1409 1409 b'help',
1410 1410 br'hidden-command\..*',
1411 1411 default=False,
1412 1412 generic=True,
1413 1413 )
1414 1414 coreconfigitem(
1415 1415 b'help',
1416 1416 br'hidden-topic\..*',
1417 1417 default=False,
1418 1418 generic=True,
1419 1419 )
1420 1420 coreconfigitem(
1421 1421 b'hooks',
1422 1422 b'[^:]*',
1423 1423 default=dynamicdefault,
1424 1424 generic=True,
1425 1425 )
1426 1426 coreconfigitem(
1427 1427 b'hooks',
1428 1428 b'.*:run-with-plain',
1429 1429 default=True,
1430 1430 generic=True,
1431 1431 )
1432 1432 coreconfigitem(
1433 1433 b'hgweb-paths',
1434 1434 b'.*',
1435 1435 default=list,
1436 1436 generic=True,
1437 1437 )
1438 1438 coreconfigitem(
1439 1439 b'hostfingerprints',
1440 1440 b'.*',
1441 1441 default=list,
1442 1442 generic=True,
1443 1443 )
1444 1444 coreconfigitem(
1445 1445 b'hostsecurity',
1446 1446 b'ciphers',
1447 1447 default=None,
1448 1448 )
1449 1449 coreconfigitem(
1450 1450 b'hostsecurity',
1451 1451 b'minimumprotocol',
1452 1452 default=dynamicdefault,
1453 1453 )
1454 1454 coreconfigitem(
1455 1455 b'hostsecurity',
1456 1456 b'.*:minimumprotocol$',
1457 1457 default=dynamicdefault,
1458 1458 generic=True,
1459 1459 )
1460 1460 coreconfigitem(
1461 1461 b'hostsecurity',
1462 1462 b'.*:ciphers$',
1463 1463 default=dynamicdefault,
1464 1464 generic=True,
1465 1465 )
1466 1466 coreconfigitem(
1467 1467 b'hostsecurity',
1468 1468 b'.*:fingerprints$',
1469 1469 default=list,
1470 1470 generic=True,
1471 1471 )
1472 1472 coreconfigitem(
1473 1473 b'hostsecurity',
1474 1474 b'.*:verifycertsfile$',
1475 1475 default=None,
1476 1476 generic=True,
1477 1477 )
1478 1478
1479 1479 coreconfigitem(
1480 1480 b'http_proxy',
1481 1481 b'always',
1482 1482 default=False,
1483 1483 )
1484 1484 coreconfigitem(
1485 1485 b'http_proxy',
1486 1486 b'host',
1487 1487 default=None,
1488 1488 )
1489 1489 coreconfigitem(
1490 1490 b'http_proxy',
1491 1491 b'no',
1492 1492 default=list,
1493 1493 )
1494 1494 coreconfigitem(
1495 1495 b'http_proxy',
1496 1496 b'passwd',
1497 1497 default=None,
1498 1498 )
1499 1499 coreconfigitem(
1500 1500 b'http_proxy',
1501 1501 b'user',
1502 1502 default=None,
1503 1503 )
1504 1504
1505 1505 coreconfigitem(
1506 1506 b'http',
1507 1507 b'timeout',
1508 1508 default=None,
1509 1509 )
1510 1510
1511 1511 coreconfigitem(
1512 1512 b'logtoprocess',
1513 1513 b'commandexception',
1514 1514 default=None,
1515 1515 )
1516 1516 coreconfigitem(
1517 1517 b'logtoprocess',
1518 1518 b'commandfinish',
1519 1519 default=None,
1520 1520 )
1521 1521 coreconfigitem(
1522 1522 b'logtoprocess',
1523 1523 b'command',
1524 1524 default=None,
1525 1525 )
1526 1526 coreconfigitem(
1527 1527 b'logtoprocess',
1528 1528 b'develwarn',
1529 1529 default=None,
1530 1530 )
1531 1531 coreconfigitem(
1532 1532 b'logtoprocess',
1533 1533 b'uiblocked',
1534 1534 default=None,
1535 1535 )
1536 1536 coreconfigitem(
1537 1537 b'merge',
1538 1538 b'checkunknown',
1539 1539 default=b'abort',
1540 1540 )
1541 1541 coreconfigitem(
1542 1542 b'merge',
1543 1543 b'checkignored',
1544 1544 default=b'abort',
1545 1545 )
1546 1546 coreconfigitem(
1547 1547 b'experimental',
1548 1548 b'merge.checkpathconflicts',
1549 1549 default=False,
1550 1550 )
1551 1551 coreconfigitem(
1552 1552 b'merge',
1553 1553 b'followcopies',
1554 1554 default=True,
1555 1555 )
1556 1556 coreconfigitem(
1557 1557 b'merge',
1558 1558 b'on-failure',
1559 1559 default=b'continue',
1560 1560 )
1561 1561 coreconfigitem(
1562 1562 b'merge',
1563 1563 b'preferancestor',
1564 1564 default=lambda: [b'*'],
1565 1565 experimental=True,
1566 1566 )
1567 1567 coreconfigitem(
1568 1568 b'merge',
1569 1569 b'strict-capability-check',
1570 1570 default=False,
1571 1571 )
1572 1572 coreconfigitem(
1573 b'partial-merge-tools',
1574 b'.*',
1575 default=None,
1576 generic=True,
1577 experimental=True,
1578 )
1579 coreconfigitem(
1580 b'partial-merge-tools',
1581 br'.*\.patterns',
1582 default=dynamicdefault,
1583 generic=True,
1584 priority=-1,
1585 experimental=True,
1586 )
1587 coreconfigitem(
1588 b'partial-merge-tools',
1589 br'.*\.executable$',
1590 default=dynamicdefault,
1591 generic=True,
1592 priority=-1,
1593 experimental=True,
1594 )
1595 coreconfigitem(
1596 b'partial-merge-tools',
1597 br'.*\.order',
1598 default=0,
1599 generic=True,
1600 priority=-1,
1601 experimental=True,
1602 )
1603 coreconfigitem(
1573 1604 b'merge-tools',
1574 1605 b'.*',
1575 1606 default=None,
1576 1607 generic=True,
1577 1608 )
1578 1609 coreconfigitem(
1579 1610 b'merge-tools',
1580 1611 br'.*\.args$',
1581 1612 default=b"$local $base $other",
1582 1613 generic=True,
1583 1614 priority=-1,
1584 1615 )
1585 1616 coreconfigitem(
1586 1617 b'merge-tools',
1587 1618 br'.*\.binary$',
1588 1619 default=False,
1589 1620 generic=True,
1590 1621 priority=-1,
1591 1622 )
1592 1623 coreconfigitem(
1593 1624 b'merge-tools',
1594 1625 br'.*\.check$',
1595 1626 default=list,
1596 1627 generic=True,
1597 1628 priority=-1,
1598 1629 )
1599 1630 coreconfigitem(
1600 1631 b'merge-tools',
1601 1632 br'.*\.checkchanged$',
1602 1633 default=False,
1603 1634 generic=True,
1604 1635 priority=-1,
1605 1636 )
1606 1637 coreconfigitem(
1607 1638 b'merge-tools',
1608 1639 br'.*\.executable$',
1609 1640 default=dynamicdefault,
1610 1641 generic=True,
1611 1642 priority=-1,
1612 1643 )
1613 1644 coreconfigitem(
1614 1645 b'merge-tools',
1615 1646 br'.*\.fixeol$',
1616 1647 default=False,
1617 1648 generic=True,
1618 1649 priority=-1,
1619 1650 )
1620 1651 coreconfigitem(
1621 1652 b'merge-tools',
1622 1653 br'.*\.gui$',
1623 1654 default=False,
1624 1655 generic=True,
1625 1656 priority=-1,
1626 1657 )
1627 1658 coreconfigitem(
1628 1659 b'merge-tools',
1629 1660 br'.*\.mergemarkers$',
1630 1661 default=b'basic',
1631 1662 generic=True,
1632 1663 priority=-1,
1633 1664 )
1634 1665 coreconfigitem(
1635 1666 b'merge-tools',
1636 1667 br'.*\.mergemarkertemplate$',
1637 1668 default=dynamicdefault, # take from command-templates.mergemarker
1638 1669 generic=True,
1639 1670 priority=-1,
1640 1671 )
1641 1672 coreconfigitem(
1642 1673 b'merge-tools',
1643 1674 br'.*\.priority$',
1644 1675 default=0,
1645 1676 generic=True,
1646 1677 priority=-1,
1647 1678 )
1648 1679 coreconfigitem(
1649 1680 b'merge-tools',
1650 1681 br'.*\.premerge$',
1651 1682 default=dynamicdefault,
1652 1683 generic=True,
1653 1684 priority=-1,
1654 1685 )
1655 1686 coreconfigitem(
1656 1687 b'merge-tools',
1657 1688 br'.*\.symlink$',
1658 1689 default=False,
1659 1690 generic=True,
1660 1691 priority=-1,
1661 1692 )
1662 1693 coreconfigitem(
1663 1694 b'pager',
1664 1695 b'attend-.*',
1665 1696 default=dynamicdefault,
1666 1697 generic=True,
1667 1698 )
1668 1699 coreconfigitem(
1669 1700 b'pager',
1670 1701 b'ignore',
1671 1702 default=list,
1672 1703 )
1673 1704 coreconfigitem(
1674 1705 b'pager',
1675 1706 b'pager',
1676 1707 default=dynamicdefault,
1677 1708 )
1678 1709 coreconfigitem(
1679 1710 b'patch',
1680 1711 b'eol',
1681 1712 default=b'strict',
1682 1713 )
1683 1714 coreconfigitem(
1684 1715 b'patch',
1685 1716 b'fuzz',
1686 1717 default=2,
1687 1718 )
1688 1719 coreconfigitem(
1689 1720 b'paths',
1690 1721 b'default',
1691 1722 default=None,
1692 1723 )
1693 1724 coreconfigitem(
1694 1725 b'paths',
1695 1726 b'default-push',
1696 1727 default=None,
1697 1728 )
1698 1729 coreconfigitem(
1699 1730 b'paths',
1700 1731 b'.*',
1701 1732 default=None,
1702 1733 generic=True,
1703 1734 )
1704 1735 coreconfigitem(
1705 1736 b'phases',
1706 1737 b'checksubrepos',
1707 1738 default=b'follow',
1708 1739 )
1709 1740 coreconfigitem(
1710 1741 b'phases',
1711 1742 b'new-commit',
1712 1743 default=b'draft',
1713 1744 )
1714 1745 coreconfigitem(
1715 1746 b'phases',
1716 1747 b'publish',
1717 1748 default=True,
1718 1749 )
1719 1750 coreconfigitem(
1720 1751 b'profiling',
1721 1752 b'enabled',
1722 1753 default=False,
1723 1754 )
1724 1755 coreconfigitem(
1725 1756 b'profiling',
1726 1757 b'format',
1727 1758 default=b'text',
1728 1759 )
1729 1760 coreconfigitem(
1730 1761 b'profiling',
1731 1762 b'freq',
1732 1763 default=1000,
1733 1764 )
1734 1765 coreconfigitem(
1735 1766 b'profiling',
1736 1767 b'limit',
1737 1768 default=30,
1738 1769 )
1739 1770 coreconfigitem(
1740 1771 b'profiling',
1741 1772 b'nested',
1742 1773 default=0,
1743 1774 )
1744 1775 coreconfigitem(
1745 1776 b'profiling',
1746 1777 b'output',
1747 1778 default=None,
1748 1779 )
1749 1780 coreconfigitem(
1750 1781 b'profiling',
1751 1782 b'showmax',
1752 1783 default=0.999,
1753 1784 )
1754 1785 coreconfigitem(
1755 1786 b'profiling',
1756 1787 b'showmin',
1757 1788 default=dynamicdefault,
1758 1789 )
1759 1790 coreconfigitem(
1760 1791 b'profiling',
1761 1792 b'showtime',
1762 1793 default=True,
1763 1794 )
1764 1795 coreconfigitem(
1765 1796 b'profiling',
1766 1797 b'sort',
1767 1798 default=b'inlinetime',
1768 1799 )
1769 1800 coreconfigitem(
1770 1801 b'profiling',
1771 1802 b'statformat',
1772 1803 default=b'hotpath',
1773 1804 )
1774 1805 coreconfigitem(
1775 1806 b'profiling',
1776 1807 b'time-track',
1777 1808 default=dynamicdefault,
1778 1809 )
1779 1810 coreconfigitem(
1780 1811 b'profiling',
1781 1812 b'type',
1782 1813 default=b'stat',
1783 1814 )
1784 1815 coreconfigitem(
1785 1816 b'progress',
1786 1817 b'assume-tty',
1787 1818 default=False,
1788 1819 )
1789 1820 coreconfigitem(
1790 1821 b'progress',
1791 1822 b'changedelay',
1792 1823 default=1,
1793 1824 )
1794 1825 coreconfigitem(
1795 1826 b'progress',
1796 1827 b'clear-complete',
1797 1828 default=True,
1798 1829 )
1799 1830 coreconfigitem(
1800 1831 b'progress',
1801 1832 b'debug',
1802 1833 default=False,
1803 1834 )
1804 1835 coreconfigitem(
1805 1836 b'progress',
1806 1837 b'delay',
1807 1838 default=3,
1808 1839 )
1809 1840 coreconfigitem(
1810 1841 b'progress',
1811 1842 b'disable',
1812 1843 default=False,
1813 1844 )
1814 1845 coreconfigitem(
1815 1846 b'progress',
1816 1847 b'estimateinterval',
1817 1848 default=60.0,
1818 1849 )
1819 1850 coreconfigitem(
1820 1851 b'progress',
1821 1852 b'format',
1822 1853 default=lambda: [b'topic', b'bar', b'number', b'estimate'],
1823 1854 )
1824 1855 coreconfigitem(
1825 1856 b'progress',
1826 1857 b'refresh',
1827 1858 default=0.1,
1828 1859 )
1829 1860 coreconfigitem(
1830 1861 b'progress',
1831 1862 b'width',
1832 1863 default=dynamicdefault,
1833 1864 )
1834 1865 coreconfigitem(
1835 1866 b'pull',
1836 1867 b'confirm',
1837 1868 default=False,
1838 1869 )
1839 1870 coreconfigitem(
1840 1871 b'push',
1841 1872 b'pushvars.server',
1842 1873 default=False,
1843 1874 )
1844 1875 coreconfigitem(
1845 1876 b'rewrite',
1846 1877 b'backup-bundle',
1847 1878 default=True,
1848 1879 alias=[(b'ui', b'history-editing-backup')],
1849 1880 )
1850 1881 coreconfigitem(
1851 1882 b'rewrite',
1852 1883 b'update-timestamp',
1853 1884 default=False,
1854 1885 )
1855 1886 coreconfigitem(
1856 1887 b'rewrite',
1857 1888 b'empty-successor',
1858 1889 default=b'skip',
1859 1890 experimental=True,
1860 1891 )
1861 1892 # experimental as long as format.use-dirstate-v2 is.
1862 1893 coreconfigitem(
1863 1894 b'storage',
1864 1895 b'dirstate-v2.slow-path',
1865 1896 default=b"abort",
1866 1897 experimental=True,
1867 1898 )
1868 1899 coreconfigitem(
1869 1900 b'storage',
1870 1901 b'new-repo-backend',
1871 1902 default=b'revlogv1',
1872 1903 experimental=True,
1873 1904 )
1874 1905 coreconfigitem(
1875 1906 b'storage',
1876 1907 b'revlog.optimize-delta-parent-choice',
1877 1908 default=True,
1878 1909 alias=[(b'format', b'aggressivemergedeltas')],
1879 1910 )
1880 1911 coreconfigitem(
1881 1912 b'storage',
1882 1913 b'revlog.issue6528.fix-incoming',
1883 1914 default=True,
1884 1915 )
1885 1916 # experimental as long as rust is experimental (or a C version is implemented)
1886 1917 coreconfigitem(
1887 1918 b'storage',
1888 1919 b'revlog.persistent-nodemap.mmap',
1889 1920 default=True,
1890 1921 )
1891 1922 # experimental as long as format.use-persistent-nodemap is.
1892 1923 coreconfigitem(
1893 1924 b'storage',
1894 1925 b'revlog.persistent-nodemap.slow-path',
1895 1926 default=b"abort",
1896 1927 )
1897 1928
1898 1929 coreconfigitem(
1899 1930 b'storage',
1900 1931 b'revlog.reuse-external-delta',
1901 1932 default=True,
1902 1933 )
1903 1934 coreconfigitem(
1904 1935 b'storage',
1905 1936 b'revlog.reuse-external-delta-parent',
1906 1937 default=None,
1907 1938 )
1908 1939 coreconfigitem(
1909 1940 b'storage',
1910 1941 b'revlog.zlib.level',
1911 1942 default=None,
1912 1943 )
1913 1944 coreconfigitem(
1914 1945 b'storage',
1915 1946 b'revlog.zstd.level',
1916 1947 default=None,
1917 1948 )
1918 1949 coreconfigitem(
1919 1950 b'server',
1920 1951 b'bookmarks-pushkey-compat',
1921 1952 default=True,
1922 1953 )
1923 1954 coreconfigitem(
1924 1955 b'server',
1925 1956 b'bundle1',
1926 1957 default=True,
1927 1958 )
1928 1959 coreconfigitem(
1929 1960 b'server',
1930 1961 b'bundle1gd',
1931 1962 default=None,
1932 1963 )
1933 1964 coreconfigitem(
1934 1965 b'server',
1935 1966 b'bundle1.pull',
1936 1967 default=None,
1937 1968 )
1938 1969 coreconfigitem(
1939 1970 b'server',
1940 1971 b'bundle1gd.pull',
1941 1972 default=None,
1942 1973 )
1943 1974 coreconfigitem(
1944 1975 b'server',
1945 1976 b'bundle1.push',
1946 1977 default=None,
1947 1978 )
1948 1979 coreconfigitem(
1949 1980 b'server',
1950 1981 b'bundle1gd.push',
1951 1982 default=None,
1952 1983 )
1953 1984 coreconfigitem(
1954 1985 b'server',
1955 1986 b'bundle2.stream',
1956 1987 default=True,
1957 1988 alias=[(b'experimental', b'bundle2.stream')],
1958 1989 )
1959 1990 coreconfigitem(
1960 1991 b'server',
1961 1992 b'compressionengines',
1962 1993 default=list,
1963 1994 )
1964 1995 coreconfigitem(
1965 1996 b'server',
1966 1997 b'concurrent-push-mode',
1967 1998 default=b'check-related',
1968 1999 )
1969 2000 coreconfigitem(
1970 2001 b'server',
1971 2002 b'disablefullbundle',
1972 2003 default=False,
1973 2004 )
1974 2005 coreconfigitem(
1975 2006 b'server',
1976 2007 b'maxhttpheaderlen',
1977 2008 default=1024,
1978 2009 )
1979 2010 coreconfigitem(
1980 2011 b'server',
1981 2012 b'pullbundle',
1982 2013 default=False,
1983 2014 )
1984 2015 coreconfigitem(
1985 2016 b'server',
1986 2017 b'preferuncompressed',
1987 2018 default=False,
1988 2019 )
1989 2020 coreconfigitem(
1990 2021 b'server',
1991 2022 b'streamunbundle',
1992 2023 default=False,
1993 2024 )
1994 2025 coreconfigitem(
1995 2026 b'server',
1996 2027 b'uncompressed',
1997 2028 default=True,
1998 2029 )
1999 2030 coreconfigitem(
2000 2031 b'server',
2001 2032 b'uncompressedallowsecret',
2002 2033 default=False,
2003 2034 )
2004 2035 coreconfigitem(
2005 2036 b'server',
2006 2037 b'view',
2007 2038 default=b'served',
2008 2039 )
2009 2040 coreconfigitem(
2010 2041 b'server',
2011 2042 b'validate',
2012 2043 default=False,
2013 2044 )
2014 2045 coreconfigitem(
2015 2046 b'server',
2016 2047 b'zliblevel',
2017 2048 default=-1,
2018 2049 )
2019 2050 coreconfigitem(
2020 2051 b'server',
2021 2052 b'zstdlevel',
2022 2053 default=3,
2023 2054 )
2024 2055 coreconfigitem(
2025 2056 b'share',
2026 2057 b'pool',
2027 2058 default=None,
2028 2059 )
2029 2060 coreconfigitem(
2030 2061 b'share',
2031 2062 b'poolnaming',
2032 2063 default=b'identity',
2033 2064 )
2034 2065 coreconfigitem(
2035 2066 b'share',
2036 2067 b'safe-mismatch.source-not-safe',
2037 2068 default=b'abort',
2038 2069 )
2039 2070 coreconfigitem(
2040 2071 b'share',
2041 2072 b'safe-mismatch.source-safe',
2042 2073 default=b'abort',
2043 2074 )
2044 2075 coreconfigitem(
2045 2076 b'share',
2046 2077 b'safe-mismatch.source-not-safe.warn',
2047 2078 default=True,
2048 2079 )
2049 2080 coreconfigitem(
2050 2081 b'share',
2051 2082 b'safe-mismatch.source-safe.warn',
2052 2083 default=True,
2053 2084 )
2054 2085 coreconfigitem(
2055 2086 b'shelve',
2056 2087 b'maxbackups',
2057 2088 default=10,
2058 2089 )
2059 2090 coreconfigitem(
2060 2091 b'smtp',
2061 2092 b'host',
2062 2093 default=None,
2063 2094 )
2064 2095 coreconfigitem(
2065 2096 b'smtp',
2066 2097 b'local_hostname',
2067 2098 default=None,
2068 2099 )
2069 2100 coreconfigitem(
2070 2101 b'smtp',
2071 2102 b'password',
2072 2103 default=None,
2073 2104 )
2074 2105 coreconfigitem(
2075 2106 b'smtp',
2076 2107 b'port',
2077 2108 default=dynamicdefault,
2078 2109 )
2079 2110 coreconfigitem(
2080 2111 b'smtp',
2081 2112 b'tls',
2082 2113 default=b'none',
2083 2114 )
2084 2115 coreconfigitem(
2085 2116 b'smtp',
2086 2117 b'username',
2087 2118 default=None,
2088 2119 )
2089 2120 coreconfigitem(
2090 2121 b'sparse',
2091 2122 b'missingwarning',
2092 2123 default=True,
2093 2124 experimental=True,
2094 2125 )
2095 2126 coreconfigitem(
2096 2127 b'subrepos',
2097 2128 b'allowed',
2098 2129 default=dynamicdefault, # to make backporting simpler
2099 2130 )
2100 2131 coreconfigitem(
2101 2132 b'subrepos',
2102 2133 b'hg:allowed',
2103 2134 default=dynamicdefault,
2104 2135 )
2105 2136 coreconfigitem(
2106 2137 b'subrepos',
2107 2138 b'git:allowed',
2108 2139 default=dynamicdefault,
2109 2140 )
2110 2141 coreconfigitem(
2111 2142 b'subrepos',
2112 2143 b'svn:allowed',
2113 2144 default=dynamicdefault,
2114 2145 )
2115 2146 coreconfigitem(
2116 2147 b'templates',
2117 2148 b'.*',
2118 2149 default=None,
2119 2150 generic=True,
2120 2151 )
2121 2152 coreconfigitem(
2122 2153 b'templateconfig',
2123 2154 b'.*',
2124 2155 default=dynamicdefault,
2125 2156 generic=True,
2126 2157 )
2127 2158 coreconfigitem(
2128 2159 b'trusted',
2129 2160 b'groups',
2130 2161 default=list,
2131 2162 )
2132 2163 coreconfigitem(
2133 2164 b'trusted',
2134 2165 b'users',
2135 2166 default=list,
2136 2167 )
2137 2168 coreconfigitem(
2138 2169 b'ui',
2139 2170 b'_usedassubrepo',
2140 2171 default=False,
2141 2172 )
2142 2173 coreconfigitem(
2143 2174 b'ui',
2144 2175 b'allowemptycommit',
2145 2176 default=False,
2146 2177 )
2147 2178 coreconfigitem(
2148 2179 b'ui',
2149 2180 b'archivemeta',
2150 2181 default=True,
2151 2182 )
2152 2183 coreconfigitem(
2153 2184 b'ui',
2154 2185 b'askusername',
2155 2186 default=False,
2156 2187 )
2157 2188 coreconfigitem(
2158 2189 b'ui',
2159 2190 b'available-memory',
2160 2191 default=None,
2161 2192 )
2162 2193
2163 2194 coreconfigitem(
2164 2195 b'ui',
2165 2196 b'clonebundlefallback',
2166 2197 default=False,
2167 2198 )
2168 2199 coreconfigitem(
2169 2200 b'ui',
2170 2201 b'clonebundleprefers',
2171 2202 default=list,
2172 2203 )
2173 2204 coreconfigitem(
2174 2205 b'ui',
2175 2206 b'clonebundles',
2176 2207 default=True,
2177 2208 )
2178 2209 coreconfigitem(
2179 2210 b'ui',
2180 2211 b'color',
2181 2212 default=b'auto',
2182 2213 )
2183 2214 coreconfigitem(
2184 2215 b'ui',
2185 2216 b'commitsubrepos',
2186 2217 default=False,
2187 2218 )
2188 2219 coreconfigitem(
2189 2220 b'ui',
2190 2221 b'debug',
2191 2222 default=False,
2192 2223 )
2193 2224 coreconfigitem(
2194 2225 b'ui',
2195 2226 b'debugger',
2196 2227 default=None,
2197 2228 )
2198 2229 coreconfigitem(
2199 2230 b'ui',
2200 2231 b'editor',
2201 2232 default=dynamicdefault,
2202 2233 )
2203 2234 coreconfigitem(
2204 2235 b'ui',
2205 2236 b'detailed-exit-code',
2206 2237 default=False,
2207 2238 experimental=True,
2208 2239 )
2209 2240 coreconfigitem(
2210 2241 b'ui',
2211 2242 b'fallbackencoding',
2212 2243 default=None,
2213 2244 )
2214 2245 coreconfigitem(
2215 2246 b'ui',
2216 2247 b'forcecwd',
2217 2248 default=None,
2218 2249 )
2219 2250 coreconfigitem(
2220 2251 b'ui',
2221 2252 b'forcemerge',
2222 2253 default=None,
2223 2254 )
2224 2255 coreconfigitem(
2225 2256 b'ui',
2226 2257 b'formatdebug',
2227 2258 default=False,
2228 2259 )
2229 2260 coreconfigitem(
2230 2261 b'ui',
2231 2262 b'formatjson',
2232 2263 default=False,
2233 2264 )
2234 2265 coreconfigitem(
2235 2266 b'ui',
2236 2267 b'formatted',
2237 2268 default=None,
2238 2269 )
2239 2270 coreconfigitem(
2240 2271 b'ui',
2241 2272 b'interactive',
2242 2273 default=None,
2243 2274 )
2244 2275 coreconfigitem(
2245 2276 b'ui',
2246 2277 b'interface',
2247 2278 default=None,
2248 2279 )
2249 2280 coreconfigitem(
2250 2281 b'ui',
2251 2282 b'interface.chunkselector',
2252 2283 default=None,
2253 2284 )
2254 2285 coreconfigitem(
2255 2286 b'ui',
2256 2287 b'large-file-limit',
2257 2288 default=10000000,
2258 2289 )
2259 2290 coreconfigitem(
2260 2291 b'ui',
2261 2292 b'logblockedtimes',
2262 2293 default=False,
2263 2294 )
2264 2295 coreconfigitem(
2265 2296 b'ui',
2266 2297 b'merge',
2267 2298 default=None,
2268 2299 )
2269 2300 coreconfigitem(
2270 2301 b'ui',
2271 2302 b'mergemarkers',
2272 2303 default=b'basic',
2273 2304 )
2274 2305 coreconfigitem(
2275 2306 b'ui',
2276 2307 b'message-output',
2277 2308 default=b'stdio',
2278 2309 )
2279 2310 coreconfigitem(
2280 2311 b'ui',
2281 2312 b'nontty',
2282 2313 default=False,
2283 2314 )
2284 2315 coreconfigitem(
2285 2316 b'ui',
2286 2317 b'origbackuppath',
2287 2318 default=None,
2288 2319 )
2289 2320 coreconfigitem(
2290 2321 b'ui',
2291 2322 b'paginate',
2292 2323 default=True,
2293 2324 )
2294 2325 coreconfigitem(
2295 2326 b'ui',
2296 2327 b'patch',
2297 2328 default=None,
2298 2329 )
2299 2330 coreconfigitem(
2300 2331 b'ui',
2301 2332 b'portablefilenames',
2302 2333 default=b'warn',
2303 2334 )
2304 2335 coreconfigitem(
2305 2336 b'ui',
2306 2337 b'promptecho',
2307 2338 default=False,
2308 2339 )
2309 2340 coreconfigitem(
2310 2341 b'ui',
2311 2342 b'quiet',
2312 2343 default=False,
2313 2344 )
2314 2345 coreconfigitem(
2315 2346 b'ui',
2316 2347 b'quietbookmarkmove',
2317 2348 default=False,
2318 2349 )
2319 2350 coreconfigitem(
2320 2351 b'ui',
2321 2352 b'relative-paths',
2322 2353 default=b'legacy',
2323 2354 )
2324 2355 coreconfigitem(
2325 2356 b'ui',
2326 2357 b'remotecmd',
2327 2358 default=b'hg',
2328 2359 )
2329 2360 coreconfigitem(
2330 2361 b'ui',
2331 2362 b'report_untrusted',
2332 2363 default=True,
2333 2364 )
2334 2365 coreconfigitem(
2335 2366 b'ui',
2336 2367 b'rollback',
2337 2368 default=True,
2338 2369 )
2339 2370 coreconfigitem(
2340 2371 b'ui',
2341 2372 b'signal-safe-lock',
2342 2373 default=True,
2343 2374 )
2344 2375 coreconfigitem(
2345 2376 b'ui',
2346 2377 b'slash',
2347 2378 default=False,
2348 2379 )
2349 2380 coreconfigitem(
2350 2381 b'ui',
2351 2382 b'ssh',
2352 2383 default=b'ssh',
2353 2384 )
2354 2385 coreconfigitem(
2355 2386 b'ui',
2356 2387 b'ssherrorhint',
2357 2388 default=None,
2358 2389 )
2359 2390 coreconfigitem(
2360 2391 b'ui',
2361 2392 b'statuscopies',
2362 2393 default=False,
2363 2394 )
2364 2395 coreconfigitem(
2365 2396 b'ui',
2366 2397 b'strict',
2367 2398 default=False,
2368 2399 )
2369 2400 coreconfigitem(
2370 2401 b'ui',
2371 2402 b'style',
2372 2403 default=b'',
2373 2404 )
2374 2405 coreconfigitem(
2375 2406 b'ui',
2376 2407 b'supportcontact',
2377 2408 default=None,
2378 2409 )
2379 2410 coreconfigitem(
2380 2411 b'ui',
2381 2412 b'textwidth',
2382 2413 default=78,
2383 2414 )
2384 2415 coreconfigitem(
2385 2416 b'ui',
2386 2417 b'timeout',
2387 2418 default=b'600',
2388 2419 )
2389 2420 coreconfigitem(
2390 2421 b'ui',
2391 2422 b'timeout.warn',
2392 2423 default=0,
2393 2424 )
2394 2425 coreconfigitem(
2395 2426 b'ui',
2396 2427 b'timestamp-output',
2397 2428 default=False,
2398 2429 )
2399 2430 coreconfigitem(
2400 2431 b'ui',
2401 2432 b'traceback',
2402 2433 default=False,
2403 2434 )
2404 2435 coreconfigitem(
2405 2436 b'ui',
2406 2437 b'tweakdefaults',
2407 2438 default=False,
2408 2439 )
2409 2440 coreconfigitem(b'ui', b'username', alias=[(b'ui', b'user')])
2410 2441 coreconfigitem(
2411 2442 b'ui',
2412 2443 b'verbose',
2413 2444 default=False,
2414 2445 )
2415 2446 coreconfigitem(
2416 2447 b'verify',
2417 2448 b'skipflags',
2418 2449 default=None,
2419 2450 )
2420 2451 coreconfigitem(
2421 2452 b'web',
2422 2453 b'allowbz2',
2423 2454 default=False,
2424 2455 )
2425 2456 coreconfigitem(
2426 2457 b'web',
2427 2458 b'allowgz',
2428 2459 default=False,
2429 2460 )
2430 2461 coreconfigitem(
2431 2462 b'web',
2432 2463 b'allow-pull',
2433 2464 alias=[(b'web', b'allowpull')],
2434 2465 default=True,
2435 2466 )
2436 2467 coreconfigitem(
2437 2468 b'web',
2438 2469 b'allow-push',
2439 2470 alias=[(b'web', b'allow_push')],
2440 2471 default=list,
2441 2472 )
2442 2473 coreconfigitem(
2443 2474 b'web',
2444 2475 b'allowzip',
2445 2476 default=False,
2446 2477 )
2447 2478 coreconfigitem(
2448 2479 b'web',
2449 2480 b'archivesubrepos',
2450 2481 default=False,
2451 2482 )
2452 2483 coreconfigitem(
2453 2484 b'web',
2454 2485 b'cache',
2455 2486 default=True,
2456 2487 )
2457 2488 coreconfigitem(
2458 2489 b'web',
2459 2490 b'comparisoncontext',
2460 2491 default=5,
2461 2492 )
2462 2493 coreconfigitem(
2463 2494 b'web',
2464 2495 b'contact',
2465 2496 default=None,
2466 2497 )
2467 2498 coreconfigitem(
2468 2499 b'web',
2469 2500 b'deny_push',
2470 2501 default=list,
2471 2502 )
2472 2503 coreconfigitem(
2473 2504 b'web',
2474 2505 b'guessmime',
2475 2506 default=False,
2476 2507 )
2477 2508 coreconfigitem(
2478 2509 b'web',
2479 2510 b'hidden',
2480 2511 default=False,
2481 2512 )
2482 2513 coreconfigitem(
2483 2514 b'web',
2484 2515 b'labels',
2485 2516 default=list,
2486 2517 )
2487 2518 coreconfigitem(
2488 2519 b'web',
2489 2520 b'logoimg',
2490 2521 default=b'hglogo.png',
2491 2522 )
2492 2523 coreconfigitem(
2493 2524 b'web',
2494 2525 b'logourl',
2495 2526 default=b'https://mercurial-scm.org/',
2496 2527 )
2497 2528 coreconfigitem(
2498 2529 b'web',
2499 2530 b'accesslog',
2500 2531 default=b'-',
2501 2532 )
2502 2533 coreconfigitem(
2503 2534 b'web',
2504 2535 b'address',
2505 2536 default=b'',
2506 2537 )
2507 2538 coreconfigitem(
2508 2539 b'web',
2509 2540 b'allow-archive',
2510 2541 alias=[(b'web', b'allow_archive')],
2511 2542 default=list,
2512 2543 )
2513 2544 coreconfigitem(
2514 2545 b'web',
2515 2546 b'allow_read',
2516 2547 default=list,
2517 2548 )
2518 2549 coreconfigitem(
2519 2550 b'web',
2520 2551 b'baseurl',
2521 2552 default=None,
2522 2553 )
2523 2554 coreconfigitem(
2524 2555 b'web',
2525 2556 b'cacerts',
2526 2557 default=None,
2527 2558 )
2528 2559 coreconfigitem(
2529 2560 b'web',
2530 2561 b'certificate',
2531 2562 default=None,
2532 2563 )
2533 2564 coreconfigitem(
2534 2565 b'web',
2535 2566 b'collapse',
2536 2567 default=False,
2537 2568 )
2538 2569 coreconfigitem(
2539 2570 b'web',
2540 2571 b'csp',
2541 2572 default=None,
2542 2573 )
2543 2574 coreconfigitem(
2544 2575 b'web',
2545 2576 b'deny_read',
2546 2577 default=list,
2547 2578 )
2548 2579 coreconfigitem(
2549 2580 b'web',
2550 2581 b'descend',
2551 2582 default=True,
2552 2583 )
2553 2584 coreconfigitem(
2554 2585 b'web',
2555 2586 b'description',
2556 2587 default=b"",
2557 2588 )
2558 2589 coreconfigitem(
2559 2590 b'web',
2560 2591 b'encoding',
2561 2592 default=lambda: encoding.encoding,
2562 2593 )
2563 2594 coreconfigitem(
2564 2595 b'web',
2565 2596 b'errorlog',
2566 2597 default=b'-',
2567 2598 )
2568 2599 coreconfigitem(
2569 2600 b'web',
2570 2601 b'ipv6',
2571 2602 default=False,
2572 2603 )
2573 2604 coreconfigitem(
2574 2605 b'web',
2575 2606 b'maxchanges',
2576 2607 default=10,
2577 2608 )
2578 2609 coreconfigitem(
2579 2610 b'web',
2580 2611 b'maxfiles',
2581 2612 default=10,
2582 2613 )
2583 2614 coreconfigitem(
2584 2615 b'web',
2585 2616 b'maxshortchanges',
2586 2617 default=60,
2587 2618 )
2588 2619 coreconfigitem(
2589 2620 b'web',
2590 2621 b'motd',
2591 2622 default=b'',
2592 2623 )
2593 2624 coreconfigitem(
2594 2625 b'web',
2595 2626 b'name',
2596 2627 default=dynamicdefault,
2597 2628 )
2598 2629 coreconfigitem(
2599 2630 b'web',
2600 2631 b'port',
2601 2632 default=8000,
2602 2633 )
2603 2634 coreconfigitem(
2604 2635 b'web',
2605 2636 b'prefix',
2606 2637 default=b'',
2607 2638 )
2608 2639 coreconfigitem(
2609 2640 b'web',
2610 2641 b'push_ssl',
2611 2642 default=True,
2612 2643 )
2613 2644 coreconfigitem(
2614 2645 b'web',
2615 2646 b'refreshinterval',
2616 2647 default=20,
2617 2648 )
2618 2649 coreconfigitem(
2619 2650 b'web',
2620 2651 b'server-header',
2621 2652 default=None,
2622 2653 )
2623 2654 coreconfigitem(
2624 2655 b'web',
2625 2656 b'static',
2626 2657 default=None,
2627 2658 )
2628 2659 coreconfigitem(
2629 2660 b'web',
2630 2661 b'staticurl',
2631 2662 default=None,
2632 2663 )
2633 2664 coreconfigitem(
2634 2665 b'web',
2635 2666 b'stripes',
2636 2667 default=1,
2637 2668 )
2638 2669 coreconfigitem(
2639 2670 b'web',
2640 2671 b'style',
2641 2672 default=b'paper',
2642 2673 )
2643 2674 coreconfigitem(
2644 2675 b'web',
2645 2676 b'templates',
2646 2677 default=None,
2647 2678 )
2648 2679 coreconfigitem(
2649 2680 b'web',
2650 2681 b'view',
2651 2682 default=b'served',
2652 2683 experimental=True,
2653 2684 )
2654 2685 coreconfigitem(
2655 2686 b'worker',
2656 2687 b'backgroundclose',
2657 2688 default=dynamicdefault,
2658 2689 )
2659 2690 # Windows defaults to a limit of 512 open files. A buffer of 128
2660 2691 # should give us enough headway.
2661 2692 coreconfigitem(
2662 2693 b'worker',
2663 2694 b'backgroundclosemaxqueue',
2664 2695 default=384,
2665 2696 )
2666 2697 coreconfigitem(
2667 2698 b'worker',
2668 2699 b'backgroundcloseminfilecount',
2669 2700 default=2048,
2670 2701 )
2671 2702 coreconfigitem(
2672 2703 b'worker',
2673 2704 b'backgroundclosethreadcount',
2674 2705 default=4,
2675 2706 )
2676 2707 coreconfigitem(
2677 2708 b'worker',
2678 2709 b'enabled',
2679 2710 default=True,
2680 2711 )
2681 2712 coreconfigitem(
2682 2713 b'worker',
2683 2714 b'numcpus',
2684 2715 default=None,
2685 2716 )
2686 2717
2687 2718 # Rebase related configuration moved to core because other extension are doing
2688 2719 # strange things. For example, shelve import the extensions to reuse some bit
2689 2720 # without formally loading it.
2690 2721 coreconfigitem(
2691 2722 b'commands',
2692 2723 b'rebase.requiredest',
2693 2724 default=False,
2694 2725 )
2695 2726 coreconfigitem(
2696 2727 b'experimental',
2697 2728 b'rebaseskipobsolete',
2698 2729 default=True,
2699 2730 )
2700 2731 coreconfigitem(
2701 2732 b'rebase',
2702 2733 b'singletransaction',
2703 2734 default=False,
2704 2735 )
2705 2736 coreconfigitem(
2706 2737 b'rebase',
2707 2738 b'experimental.inmemory',
2708 2739 default=False,
2709 2740 )
@@ -1,1231 +1,1288 b''
1 1 # filemerge.py - file-level merge handling for Mercurial
2 2 #
3 3 # Copyright 2006, 2007, 2008 Olivia Mackall <olivia@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8
9 9 import contextlib
10 10 import os
11 11 import re
12 12 import shutil
13 13
14 14 from .i18n import _
15 15 from .node import (
16 16 hex,
17 17 short,
18 18 )
19 19 from .pycompat import (
20 20 getattr,
21 21 )
22 22
23 23 from . import (
24 24 encoding,
25 25 error,
26 26 formatter,
27 27 match,
28 28 pycompat,
29 29 registrar,
30 30 scmutil,
31 31 simplemerge,
32 32 tagmerge,
33 33 templatekw,
34 34 templater,
35 35 templateutil,
36 36 util,
37 37 )
38 38
39 39 from .utils import (
40 40 procutil,
41 41 stringutil,
42 42 )
43 43
44 44
45 45 def _toolstr(ui, tool, part, *args):
46 46 return ui.config(b"merge-tools", tool + b"." + part, *args)
47 47
48 48
49 49 def _toolbool(ui, tool, part, *args):
50 50 return ui.configbool(b"merge-tools", tool + b"." + part, *args)
51 51
52 52
53 53 def _toollist(ui, tool, part):
54 54 return ui.configlist(b"merge-tools", tool + b"." + part)
55 55
56 56
57 57 internals = {}
58 58 # Merge tools to document.
59 59 internalsdoc = {}
60 60
61 61 internaltool = registrar.internalmerge()
62 62
63 63 # internal tool merge types
64 64 nomerge = internaltool.nomerge
65 65 mergeonly = internaltool.mergeonly # just the full merge, no premerge
66 66 fullmerge = internaltool.fullmerge # both premerge and merge
67 67
68 68 # IMPORTANT: keep the last line of this prompt very short ("What do you want to
69 69 # do?") because of issue6158, ideally to <40 English characters (to allow other
70 70 # languages that may take more columns to still have a chance to fit in an
71 71 # 80-column screen).
72 72 _localchangedotherdeletedmsg = _(
73 73 b"file '%(fd)s' was deleted in other%(o)s but was modified in local%(l)s.\n"
74 74 b"You can use (c)hanged version, (d)elete, or leave (u)nresolved.\n"
75 75 b"What do you want to do?"
76 76 b"$$ &Changed $$ &Delete $$ &Unresolved"
77 77 )
78 78
79 79 _otherchangedlocaldeletedmsg = _(
80 80 b"file '%(fd)s' was deleted in local%(l)s but was modified in other%(o)s.\n"
81 81 b"You can use (c)hanged version, leave (d)eleted, or leave (u)nresolved.\n"
82 82 b"What do you want to do?"
83 83 b"$$ &Changed $$ &Deleted $$ &Unresolved"
84 84 )
85 85
86 86
87 87 class absentfilectx:
88 88 """Represents a file that's ostensibly in a context but is actually not
89 89 present in it.
90 90
91 91 This is here because it's very specific to the filemerge code for now --
92 92 other code is likely going to break with the values this returns."""
93 93
94 94 def __init__(self, ctx, f):
95 95 self._ctx = ctx
96 96 self._f = f
97 97
98 98 def __bytes__(self):
99 99 return b'absent file %s@%s' % (self._f, self._ctx)
100 100
101 101 def path(self):
102 102 return self._f
103 103
104 104 def size(self):
105 105 return None
106 106
107 107 def data(self):
108 108 return None
109 109
110 110 def filenode(self):
111 111 return self._ctx.repo().nullid
112 112
113 113 _customcmp = True
114 114
115 115 def cmp(self, fctx):
116 116 """compare with other file context
117 117
118 118 returns True if different from fctx.
119 119 """
120 120 return not (
121 121 fctx.isabsent()
122 122 and fctx.changectx() == self.changectx()
123 123 and fctx.path() == self.path()
124 124 )
125 125
126 126 def flags(self):
127 127 return b''
128 128
129 129 def changectx(self):
130 130 return self._ctx
131 131
132 132 def isbinary(self):
133 133 return False
134 134
135 135 def isabsent(self):
136 136 return True
137 137
138 138
139 139 def _findtool(ui, tool):
140 140 if tool in internals:
141 141 return tool
142 142 cmd = _toolstr(ui, tool, b"executable", tool)
143 143 if cmd.startswith(b'python:'):
144 144 return cmd
145 145 return findexternaltool(ui, tool)
146 146
147 147
148 148 def _quotetoolpath(cmd):
149 149 if cmd.startswith(b'python:'):
150 150 return cmd
151 151 return procutil.shellquote(cmd)
152 152
153 153
154 154 def findexternaltool(ui, tool):
155 155 for kn in (b"regkey", b"regkeyalt"):
156 156 k = _toolstr(ui, tool, kn)
157 157 if not k:
158 158 continue
159 159 p = util.lookupreg(k, _toolstr(ui, tool, b"regname"))
160 160 if p:
161 161 p = procutil.findexe(p + _toolstr(ui, tool, b"regappend", b""))
162 162 if p:
163 163 return p
164 164 exe = _toolstr(ui, tool, b"executable", tool)
165 165 return procutil.findexe(util.expandpath(exe))
166 166
167 167
168 168 def _picktool(repo, ui, path, binary, symlink, changedelete):
169 169 strictcheck = ui.configbool(b'merge', b'strict-capability-check')
170 170
171 171 def hascapability(tool, capability, strict=False):
172 172 if tool in internals:
173 173 return strict and internals[tool].capabilities.get(capability)
174 174 return _toolbool(ui, tool, capability)
175 175
176 176 def supportscd(tool):
177 177 return tool in internals and internals[tool].mergetype == nomerge
178 178
179 179 def check(tool, pat, symlink, binary, changedelete):
180 180 tmsg = tool
181 181 if pat:
182 182 tmsg = _(b"%s (for pattern %s)") % (tool, pat)
183 183 if not _findtool(ui, tool):
184 184 if pat: # explicitly requested tool deserves a warning
185 185 ui.warn(_(b"couldn't find merge tool %s\n") % tmsg)
186 186 else: # configured but non-existing tools are more silent
187 187 ui.note(_(b"couldn't find merge tool %s\n") % tmsg)
188 188 elif symlink and not hascapability(tool, b"symlink", strictcheck):
189 189 ui.warn(_(b"tool %s can't handle symlinks\n") % tmsg)
190 190 elif binary and not hascapability(tool, b"binary", strictcheck):
191 191 ui.warn(_(b"tool %s can't handle binary\n") % tmsg)
192 192 elif changedelete and not supportscd(tool):
193 193 # the nomerge tools are the only tools that support change/delete
194 194 # conflicts
195 195 pass
196 196 elif not procutil.gui() and _toolbool(ui, tool, b"gui"):
197 197 ui.warn(_(b"tool %s requires a GUI\n") % tmsg)
198 198 else:
199 199 return True
200 200 return False
201 201
202 202 # internal config: ui.forcemerge
203 203 # forcemerge comes from command line arguments, highest priority
204 204 force = ui.config(b'ui', b'forcemerge')
205 205 if force:
206 206 toolpath = _findtool(ui, force)
207 207 if changedelete and not supportscd(toolpath):
208 208 return b":prompt", None
209 209 else:
210 210 if toolpath:
211 211 return (force, _quotetoolpath(toolpath))
212 212 else:
213 213 # mimic HGMERGE if given tool not found
214 214 return (force, force)
215 215
216 216 # HGMERGE takes next precedence
217 217 hgmerge = encoding.environ.get(b"HGMERGE")
218 218 if hgmerge:
219 219 if changedelete and not supportscd(hgmerge):
220 220 return b":prompt", None
221 221 else:
222 222 return (hgmerge, hgmerge)
223 223
224 224 # then patterns
225 225
226 226 # whether binary capability should be checked strictly
227 227 binarycap = binary and strictcheck
228 228
229 229 for pat, tool in ui.configitems(b"merge-patterns"):
230 230 mf = match.match(repo.root, b'', [pat])
231 231 if mf(path) and check(tool, pat, symlink, binarycap, changedelete):
232 232 if binary and not hascapability(tool, b"binary", strict=True):
233 233 ui.warn(
234 234 _(
235 235 b"warning: check merge-patterns configurations,"
236 236 b" if %r for binary file %r is unintentional\n"
237 237 b"(see 'hg help merge-tools'"
238 238 b" for binary files capability)\n"
239 239 )
240 240 % (pycompat.bytestr(tool), pycompat.bytestr(path))
241 241 )
242 242 toolpath = _findtool(ui, tool)
243 243 return (tool, _quotetoolpath(toolpath))
244 244
245 245 # then merge tools
246 246 tools = {}
247 247 disabled = set()
248 248 for k, v in ui.configitems(b"merge-tools"):
249 249 t = k.split(b'.')[0]
250 250 if t not in tools:
251 251 tools[t] = int(_toolstr(ui, t, b"priority"))
252 252 if _toolbool(ui, t, b"disabled"):
253 253 disabled.add(t)
254 254 names = tools.keys()
255 255 tools = sorted(
256 256 [(-p, tool) for tool, p in tools.items() if tool not in disabled]
257 257 )
258 258 uimerge = ui.config(b"ui", b"merge")
259 259 if uimerge:
260 260 # external tools defined in uimerge won't be able to handle
261 261 # change/delete conflicts
262 262 if check(uimerge, path, symlink, binary, changedelete):
263 263 if uimerge not in names and not changedelete:
264 264 return (uimerge, uimerge)
265 265 tools.insert(0, (None, uimerge)) # highest priority
266 266 tools.append((None, b"hgmerge")) # the old default, if found
267 267 for p, t in tools:
268 268 if check(t, None, symlink, binary, changedelete):
269 269 toolpath = _findtool(ui, t)
270 270 return (t, _quotetoolpath(toolpath))
271 271
272 272 # internal merge or prompt as last resort
273 273 if symlink or binary or changedelete:
274 274 if not changedelete and len(tools):
275 275 # any tool is rejected by capability for symlink or binary
276 276 ui.warn(_(b"no tool found to merge %s\n") % path)
277 277 return b":prompt", None
278 278 return b":merge", None
279 279
280 280
281 281 def _eoltype(data):
282 282 """Guess the EOL type of a file"""
283 283 if b'\0' in data: # binary
284 284 return None
285 285 if b'\r\n' in data: # Windows
286 286 return b'\r\n'
287 287 if b'\r' in data: # Old Mac
288 288 return b'\r'
289 289 if b'\n' in data: # UNIX
290 290 return b'\n'
291 291 return None # unknown
292 292
293 293
294 294 def _matcheol(file, backup):
295 295 """Convert EOL markers in a file to match origfile"""
296 296 tostyle = _eoltype(backup.data()) # No repo.wread filters?
297 297 if tostyle:
298 298 data = util.readfile(file)
299 299 style = _eoltype(data)
300 300 if style:
301 301 newdata = data.replace(style, tostyle)
302 302 if newdata != data:
303 303 util.writefile(file, newdata)
304 304
305 305
306 306 @internaltool(b'prompt', nomerge)
307 307 def _iprompt(repo, mynode, local, other, base, toolconf):
308 308 """Asks the user which of the local `p1()` or the other `p2()` version to
309 309 keep as the merged version."""
310 310 ui = repo.ui
311 311 fd = local.fctx.path()
312 312 uipathfn = scmutil.getuipathfn(repo)
313 313
314 314 # Avoid prompting during an in-memory merge since it doesn't support merge
315 315 # conflicts.
316 316 if local.fctx.changectx().isinmemory():
317 317 raise error.InMemoryMergeConflictsError(
318 318 b'in-memory merge does not support file conflicts'
319 319 )
320 320
321 321 prompts = partextras([local.label, other.label])
322 322 prompts[b'fd'] = uipathfn(fd)
323 323 try:
324 324 if other.fctx.isabsent():
325 325 index = ui.promptchoice(_localchangedotherdeletedmsg % prompts, 2)
326 326 choice = [b'local', b'other', b'unresolved'][index]
327 327 elif local.fctx.isabsent():
328 328 index = ui.promptchoice(_otherchangedlocaldeletedmsg % prompts, 2)
329 329 choice = [b'other', b'local', b'unresolved'][index]
330 330 else:
331 331 # IMPORTANT: keep the last line of this prompt ("What do you want to
332 332 # do?") very short, see comment next to _localchangedotherdeletedmsg
333 333 # at the top of the file for details.
334 334 index = ui.promptchoice(
335 335 _(
336 336 b"file '%(fd)s' needs to be resolved.\n"
337 337 b"You can keep (l)ocal%(l)s, take (o)ther%(o)s, or leave "
338 338 b"(u)nresolved.\n"
339 339 b"What do you want to do?"
340 340 b"$$ &Local $$ &Other $$ &Unresolved"
341 341 )
342 342 % prompts,
343 343 2,
344 344 )
345 345 choice = [b'local', b'other', b'unresolved'][index]
346 346
347 347 if choice == b'other':
348 348 return _iother(repo, mynode, local, other, base, toolconf)
349 349 elif choice == b'local':
350 350 return _ilocal(repo, mynode, local, other, base, toolconf)
351 351 elif choice == b'unresolved':
352 352 return _ifail(repo, mynode, local, other, base, toolconf)
353 353 except error.ResponseExpected:
354 354 ui.write(b"\n")
355 355 return _ifail(repo, mynode, local, other, base, toolconf)
356 356
357 357
358 358 @internaltool(b'local', nomerge)
359 359 def _ilocal(repo, mynode, local, other, base, toolconf):
360 360 """Uses the local `p1()` version of files as the merged version."""
361 361 return 0, local.fctx.isabsent()
362 362
363 363
364 364 @internaltool(b'other', nomerge)
365 365 def _iother(repo, mynode, local, other, base, toolconf):
366 366 """Uses the other `p2()` version of files as the merged version."""
367 367 if other.fctx.isabsent():
368 368 # local changed, remote deleted -- 'deleted' picked
369 369 _underlyingfctxifabsent(local.fctx).remove()
370 370 deleted = True
371 371 else:
372 372 _underlyingfctxifabsent(local.fctx).write(
373 373 other.fctx.data(), other.fctx.flags()
374 374 )
375 375 deleted = False
376 376 return 0, deleted
377 377
378 378
379 379 @internaltool(b'fail', nomerge)
380 380 def _ifail(repo, mynode, local, other, base, toolconf):
381 381 """
382 382 Rather than attempting to merge files that were modified on both
383 383 branches, it marks them as unresolved. The resolve command must be
384 384 used to resolve these conflicts."""
385 385 # for change/delete conflicts write out the changed version, then fail
386 386 if local.fctx.isabsent():
387 387 _underlyingfctxifabsent(local.fctx).write(
388 388 other.fctx.data(), other.fctx.flags()
389 389 )
390 390 return 1, False
391 391
392 392
393 393 def _underlyingfctxifabsent(filectx):
394 394 """Sometimes when resolving, our fcd is actually an absentfilectx, but
395 395 we want to write to it (to do the resolve). This helper returns the
396 396 underyling workingfilectx in that case.
397 397 """
398 398 if filectx.isabsent():
399 399 return filectx.changectx()[filectx.path()]
400 400 else:
401 401 return filectx
402 402
403 403
404 404 def _verifytext(input, ui):
405 405 """verifies that text is non-binary"""
406 406 if stringutil.binary(input.text()):
407 407 msg = _(b"%s looks like a binary file.") % input.fctx.path()
408 408 ui.warn(_(b'warning: %s\n') % msg)
409 409 raise error.Abort(msg)
410 410
411 411
412 412 def _premerge(repo, local, other, base, toolconf):
413 413 tool, toolpath, binary, symlink, scriptfn = toolconf
414 414 if symlink or local.fctx.isabsent() or other.fctx.isabsent():
415 415 return 1
416 416
417 417 ui = repo.ui
418 418
419 419 validkeep = [b'keep', b'keep-merge3', b'keep-mergediff']
420 420
421 421 # do we attempt to simplemerge first?
422 422 try:
423 423 premerge = _toolbool(ui, tool, b"premerge", not binary)
424 424 except error.ConfigError:
425 425 premerge = _toolstr(ui, tool, b"premerge", b"").lower()
426 426 if premerge not in validkeep:
427 427 _valid = b', '.join([b"'" + v + b"'" for v in validkeep])
428 428 raise error.ConfigError(
429 429 _(b"%s.premerge not valid ('%s' is neither boolean nor %s)")
430 430 % (tool, premerge, _valid)
431 431 )
432 432
433 433 if premerge:
434 434 mode = b'merge'
435 435 if premerge == b'keep-mergediff':
436 436 mode = b'mergediff'
437 437 elif premerge == b'keep-merge3':
438 438 mode = b'merge3'
439 439 if any(
440 440 stringutil.binary(input.text()) for input in (local, base, other)
441 441 ):
442 442 return 1 # continue merging
443 443 merged_text, conflicts = simplemerge.simplemerge(
444 444 local, base, other, mode=mode
445 445 )
446 446 if not conflicts or premerge in validkeep:
447 447 # fcd.flags() already has the merged flags (done in
448 448 # mergestate.resolve())
449 449 local.fctx.write(merged_text, local.fctx.flags())
450 450 if not conflicts:
451 451 ui.debug(b" premerge successful\n")
452 452 return 0
453 453 return 1 # continue merging
454 454
455 455
456 456 def _mergecheck(repo, mynode, fcd, fco, fca, toolconf):
457 457 tool, toolpath, binary, symlink, scriptfn = toolconf
458 458 uipathfn = scmutil.getuipathfn(repo)
459 459 if symlink:
460 460 repo.ui.warn(
461 461 _(b'warning: internal %s cannot merge symlinks for %s\n')
462 462 % (tool, uipathfn(fcd.path()))
463 463 )
464 464 return False
465 465 if fcd.isabsent() or fco.isabsent():
466 466 repo.ui.warn(
467 467 _(
468 468 b'warning: internal %s cannot merge change/delete '
469 469 b'conflict for %s\n'
470 470 )
471 471 % (tool, uipathfn(fcd.path()))
472 472 )
473 473 return False
474 474 return True
475 475
476 476
477 477 def _merge(repo, local, other, base, mode):
478 478 """
479 479 Uses the internal non-interactive simple merge algorithm for merging
480 480 files. It will fail if there are any conflicts and leave markers in
481 481 the partially merged file. Markers will have two sections, one for each side
482 482 of merge, unless mode equals 'union' which suppresses the markers."""
483 483 ui = repo.ui
484 484
485 485 try:
486 486 _verifytext(local, ui)
487 487 _verifytext(base, ui)
488 488 _verifytext(other, ui)
489 489 except error.Abort:
490 490 return True, True, False
491 491 else:
492 492 merged_text, conflicts = simplemerge.simplemerge(
493 493 local, base, other, mode=mode
494 494 )
495 495 # fcd.flags() already has the merged flags (done in
496 496 # mergestate.resolve())
497 497 local.fctx.write(merged_text, local.fctx.flags())
498 498 return True, conflicts, False
499 499
500 500
501 501 @internaltool(
502 502 b'union',
503 503 fullmerge,
504 504 _(
505 505 b"warning: conflicts while merging %s! "
506 506 b"(edit, then use 'hg resolve --mark')\n"
507 507 ),
508 508 precheck=_mergecheck,
509 509 )
510 510 def _iunion(repo, mynode, local, other, base, toolconf, backup):
511 511 """
512 512 Uses the internal non-interactive simple merge algorithm for merging
513 513 files. It will use both left and right sides for conflict regions.
514 514 No markers are inserted."""
515 515 return _merge(repo, local, other, base, b'union')
516 516
517 517
518 518 @internaltool(
519 519 b'merge',
520 520 fullmerge,
521 521 _(
522 522 b"warning: conflicts while merging %s! "
523 523 b"(edit, then use 'hg resolve --mark')\n"
524 524 ),
525 525 precheck=_mergecheck,
526 526 )
527 527 def _imerge(repo, mynode, local, other, base, toolconf, backup):
528 528 """
529 529 Uses the internal non-interactive simple merge algorithm for merging
530 530 files. It will fail if there are any conflicts and leave markers in
531 531 the partially merged file. Markers will have two sections, one for each side
532 532 of merge."""
533 533 return _merge(repo, local, other, base, b'merge')
534 534
535 535
536 536 @internaltool(
537 537 b'merge3',
538 538 fullmerge,
539 539 _(
540 540 b"warning: conflicts while merging %s! "
541 541 b"(edit, then use 'hg resolve --mark')\n"
542 542 ),
543 543 precheck=_mergecheck,
544 544 )
545 545 def _imerge3(repo, mynode, local, other, base, toolconf, backup):
546 546 """
547 547 Uses the internal non-interactive simple merge algorithm for merging
548 548 files. It will fail if there are any conflicts and leave markers in
549 549 the partially merged file. Marker will have three sections, one from each
550 550 side of the merge and one for the base content."""
551 551 return _merge(repo, local, other, base, b'merge3')
552 552
553 553
554 554 @internaltool(
555 555 b'merge3-lie-about-conflicts',
556 556 fullmerge,
557 557 b'',
558 558 precheck=_mergecheck,
559 559 )
560 560 def _imerge3alwaysgood(*args, **kwargs):
561 561 # Like merge3, but record conflicts as resolved with markers in place.
562 562 #
563 563 # This is used for `diff.merge` to show the differences between
564 564 # the auto-merge state and the committed merge state. It may be
565 565 # useful for other things.
566 566 b1, junk, b2 = _imerge3(*args, **kwargs)
567 567 # TODO is this right? I'm not sure what these return values mean,
568 568 # but as far as I can tell this will indicate to callers tha the
569 569 # merge succeeded.
570 570 return b1, False, b2
571 571
572 572
573 573 @internaltool(
574 574 b'mergediff',
575 575 fullmerge,
576 576 _(
577 577 b"warning: conflicts while merging %s! "
578 578 b"(edit, then use 'hg resolve --mark')\n"
579 579 ),
580 580 precheck=_mergecheck,
581 581 )
582 582 def _imerge_diff(repo, mynode, local, other, base, toolconf, backup):
583 583 """
584 584 Uses the internal non-interactive simple merge algorithm for merging
585 585 files. It will fail if there are any conflicts and leave markers in
586 586 the partially merged file. The marker will have two sections, one with the
587 587 content from one side of the merge, and one with a diff from the base
588 588 content to the content on the other side. (experimental)"""
589 589 return _merge(repo, local, other, base, b'mergediff')
590 590
591 591
592 592 @internaltool(b'merge-local', mergeonly, precheck=_mergecheck)
593 593 def _imergelocal(repo, mynode, local, other, base, toolconf, backup):
594 594 """
595 595 Like :merge, but resolve all conflicts non-interactively in favor
596 596 of the local `p1()` changes."""
597 597 return _merge(repo, local, other, base, b'local')
598 598
599 599
600 600 @internaltool(b'merge-other', mergeonly, precheck=_mergecheck)
601 601 def _imergeother(repo, mynode, local, other, base, toolconf, backup):
602 602 """
603 603 Like :merge, but resolve all conflicts non-interactively in favor
604 604 of the other `p2()` changes."""
605 605 return _merge(repo, local, other, base, b'other')
606 606
607 607
608 608 @internaltool(
609 609 b'tagmerge',
610 610 mergeonly,
611 611 _(
612 612 b"automatic tag merging of %s failed! "
613 613 b"(use 'hg resolve --tool :merge' or another merge "
614 614 b"tool of your choice)\n"
615 615 ),
616 616 )
617 617 def _itagmerge(repo, mynode, local, other, base, toolconf, backup):
618 618 """
619 619 Uses the internal tag merge algorithm (experimental).
620 620 """
621 621 success, status = tagmerge.merge(repo, local.fctx, other.fctx, base.fctx)
622 622 return success, status, False
623 623
624 624
625 625 @internaltool(b'dump', fullmerge, binary=True, symlink=True)
626 626 def _idump(repo, mynode, local, other, base, toolconf, backup):
627 627 """
628 628 Creates three versions of the files to merge, containing the
629 629 contents of local, other and base. These files can then be used to
630 630 perform a merge manually. If the file to be merged is named
631 631 ``a.txt``, these files will accordingly be named ``a.txt.local``,
632 632 ``a.txt.other`` and ``a.txt.base`` and they will be placed in the
633 633 same directory as ``a.txt``.
634 634
635 635 This implies premerge. Therefore, files aren't dumped, if premerge
636 636 runs successfully. Use :forcedump to forcibly write files out.
637 637 """
638 638 a = _workingpath(repo, local.fctx)
639 639 fd = local.fctx.path()
640 640
641 641 from . import context
642 642
643 643 if isinstance(local.fctx, context.overlayworkingfilectx):
644 644 raise error.InMemoryMergeConflictsError(
645 645 b'in-memory merge does not support the :dump tool.'
646 646 )
647 647
648 648 util.writefile(a + b".local", local.fctx.decodeddata())
649 649 repo.wwrite(fd + b".other", other.fctx.data(), other.fctx.flags())
650 650 repo.wwrite(fd + b".base", base.fctx.data(), base.fctx.flags())
651 651 return False, 1, False
652 652
653 653
654 654 @internaltool(b'forcedump', mergeonly, binary=True, symlink=True)
655 655 def _forcedump(repo, mynode, local, other, base, toolconf, backup):
656 656 """
657 657 Creates three versions of the files as same as :dump, but omits premerge.
658 658 """
659 659 return _idump(repo, mynode, local, other, base, toolconf, backup)
660 660
661 661
662 662 def _xmergeimm(repo, mynode, local, other, base, toolconf, backup):
663 663 # In-memory merge simply raises an exception on all external merge tools,
664 664 # for now.
665 665 #
666 666 # It would be possible to run most tools with temporary files, but this
667 667 # raises the question of what to do if the user only partially resolves the
668 668 # file -- we can't leave a merge state. (Copy to somewhere in the .hg/
669 669 # directory and tell the user how to get it is my best idea, but it's
670 670 # clunky.)
671 671 raise error.InMemoryMergeConflictsError(
672 672 b'in-memory merge does not support external merge tools'
673 673 )
674 674
675 675
676 676 def _describemerge(ui, repo, mynode, fcl, fcb, fco, env, toolpath, args):
677 677 tmpl = ui.config(b'command-templates', b'pre-merge-tool-output')
678 678 if not tmpl:
679 679 return
680 680
681 681 mappingdict = templateutil.mappingdict
682 682 props = {
683 683 b'ctx': fcl.changectx(),
684 684 b'node': hex(mynode),
685 685 b'path': fcl.path(),
686 686 b'local': mappingdict(
687 687 {
688 688 b'ctx': fcl.changectx(),
689 689 b'fctx': fcl,
690 690 b'node': hex(mynode),
691 691 b'name': _(b'local'),
692 692 b'islink': b'l' in fcl.flags(),
693 693 b'label': env[b'HG_MY_LABEL'],
694 694 }
695 695 ),
696 696 b'base': mappingdict(
697 697 {
698 698 b'ctx': fcb.changectx(),
699 699 b'fctx': fcb,
700 700 b'name': _(b'base'),
701 701 b'islink': b'l' in fcb.flags(),
702 702 b'label': env[b'HG_BASE_LABEL'],
703 703 }
704 704 ),
705 705 b'other': mappingdict(
706 706 {
707 707 b'ctx': fco.changectx(),
708 708 b'fctx': fco,
709 709 b'name': _(b'other'),
710 710 b'islink': b'l' in fco.flags(),
711 711 b'label': env[b'HG_OTHER_LABEL'],
712 712 }
713 713 ),
714 714 b'toolpath': toolpath,
715 715 b'toolargs': args,
716 716 }
717 717
718 718 # TODO: make all of this something that can be specified on a per-tool basis
719 719 tmpl = templater.unquotestring(tmpl)
720 720
721 721 # Not using cmdutil.rendertemplate here since it causes errors importing
722 722 # things for us to import cmdutil.
723 723 tres = formatter.templateresources(ui, repo)
724 724 t = formatter.maketemplater(
725 725 ui, tmpl, defaults=templatekw.keywords, resources=tres
726 726 )
727 727 ui.status(t.renderdefault(props))
728 728
729 729
730 730 def _xmerge(repo, mynode, local, other, base, toolconf, backup):
731 731 fcd = local.fctx
732 732 fco = other.fctx
733 733 fca = base.fctx
734 734 tool, toolpath, binary, symlink, scriptfn = toolconf
735 735 uipathfn = scmutil.getuipathfn(repo)
736 736 if fcd.isabsent() or fco.isabsent():
737 737 repo.ui.warn(
738 738 _(b'warning: %s cannot merge change/delete conflict for %s\n')
739 739 % (tool, uipathfn(fcd.path()))
740 740 )
741 741 return False, 1, None
742 742 localpath = _workingpath(repo, fcd)
743 743 args = _toolstr(repo.ui, tool, b"args")
744 744
745 745 files = [
746 746 (b"base", fca.path(), fca.decodeddata()),
747 747 (b"other", fco.path(), fco.decodeddata()),
748 748 ]
749 749 outpath = b""
750 750 if b"$output" in args:
751 751 # read input from backup, write to original
752 752 outpath = localpath
753 753 localoutputpath = backup.path()
754 754 # Remove the .orig to make syntax-highlighting more likely.
755 755 if localoutputpath.endswith(b'.orig'):
756 756 localoutputpath, ext = os.path.splitext(localoutputpath)
757 757 files.append((b"local", localoutputpath, backup.data()))
758 758
759 759 with _maketempfiles(files) as temppaths:
760 760 basepath, otherpath = temppaths[:2]
761 761 if len(temppaths) == 3:
762 762 localpath = temppaths[2]
763 763
764 764 def format_label(input):
765 765 if input.label_detail:
766 766 return b'%s: %s' % (input.label, input.label_detail)
767 767 else:
768 768 return input.label
769 769
770 770 env = {
771 771 b'HG_FILE': fcd.path(),
772 772 b'HG_MY_NODE': short(mynode),
773 773 b'HG_OTHER_NODE': short(fco.changectx().node()),
774 774 b'HG_BASE_NODE': short(fca.changectx().node()),
775 775 b'HG_MY_ISLINK': b'l' in fcd.flags(),
776 776 b'HG_OTHER_ISLINK': b'l' in fco.flags(),
777 777 b'HG_BASE_ISLINK': b'l' in fca.flags(),
778 778 b'HG_MY_LABEL': format_label(local),
779 779 b'HG_OTHER_LABEL': format_label(other),
780 780 b'HG_BASE_LABEL': format_label(base),
781 781 }
782 782 ui = repo.ui
783 783
784 784 replace = {
785 785 b'local': localpath,
786 786 b'base': basepath,
787 787 b'other': otherpath,
788 788 b'output': outpath,
789 789 b'labellocal': format_label(local),
790 790 b'labelother': format_label(other),
791 791 b'labelbase': format_label(base),
792 792 }
793 793 args = util.interpolate(
794 794 br'\$',
795 795 replace,
796 796 args,
797 797 lambda s: procutil.shellquote(util.localpath(s)),
798 798 )
799 799 if _toolbool(ui, tool, b"gui"):
800 800 repo.ui.status(
801 801 _(b'running merge tool %s for file %s\n')
802 802 % (tool, uipathfn(fcd.path()))
803 803 )
804 804 if scriptfn is None:
805 805 cmd = toolpath + b' ' + args
806 806 repo.ui.debug(b'launching merge tool: %s\n' % cmd)
807 807 _describemerge(ui, repo, mynode, fcd, fca, fco, env, toolpath, args)
808 808 r = ui.system(
809 809 cmd, cwd=repo.root, environ=env, blockedtag=b'mergetool'
810 810 )
811 811 else:
812 812 repo.ui.debug(
813 813 b'launching python merge script: %s:%s\n' % (toolpath, scriptfn)
814 814 )
815 815 r = 0
816 816 try:
817 817 # avoid cycle cmdutil->merge->filemerge->extensions->cmdutil
818 818 from . import extensions
819 819
820 820 mod = extensions.loadpath(toolpath, b'hgmerge.%s' % tool)
821 821 except Exception:
822 822 raise error.Abort(
823 823 _(b"loading python merge script failed: %s") % toolpath
824 824 )
825 825 mergefn = getattr(mod, scriptfn, None)
826 826 if mergefn is None:
827 827 raise error.Abort(
828 828 _(b"%s does not have function: %s") % (toolpath, scriptfn)
829 829 )
830 830 argslist = procutil.shellsplit(args)
831 831 # avoid cycle cmdutil->merge->filemerge->hook->extensions->cmdutil
832 832 from . import hook
833 833
834 834 ret, raised = hook.pythonhook(
835 835 ui, repo, b"merge", toolpath, mergefn, {b'args': argslist}, True
836 836 )
837 837 if raised:
838 838 r = 1
839 839 repo.ui.debug(b'merge tool returned: %d\n' % r)
840 840 return True, r, False
841 841
842 842
843 843 def _populate_label_detail(input, template):
844 844 """Applies the given template to the ctx and stores it in the input."""
845 845 ctx = input.fctx.changectx()
846 846 if ctx.node() is None:
847 847 ctx = ctx.p1()
848 848
849 849 props = {b'ctx': ctx}
850 850 templateresult = template.renderdefault(props)
851 851 input.label_detail = templateresult.splitlines()[0] # split for safety
852 852
853 853
854 854 def _populate_label_details(repo, inputs, tool=None):
855 855 """Populates the label details using the conflict marker template."""
856 856 ui = repo.ui
857 857 template = ui.config(b'command-templates', b'mergemarker')
858 858 if tool is not None:
859 859 template = _toolstr(ui, tool, b'mergemarkertemplate', template)
860 860 template = templater.unquotestring(template)
861 861 tres = formatter.templateresources(ui, repo)
862 862 tmpl = formatter.maketemplater(
863 863 ui, template, defaults=templatekw.keywords, resources=tres
864 864 )
865 865
866 866 for input in inputs:
867 867 _populate_label_detail(input, tmpl)
868 868
869 869
870 870 def partextras(labels):
871 871 """Return a dictionary of extra labels for use in prompts to the user
872 872
873 873 Intended use is in strings of the form "(l)ocal%(l)s".
874 874 """
875 875 if labels is None:
876 876 return {
877 877 b"l": b"",
878 878 b"o": b"",
879 879 }
880 880
881 881 return {
882 882 b"l": b" [%s]" % labels[0],
883 883 b"o": b" [%s]" % labels[1],
884 884 }
885 885
886 886
887 887 def _makebackup(repo, ui, fcd):
888 888 """Makes and returns a filectx-like object for ``fcd``'s backup file.
889 889
890 890 In addition to preserving the user's pre-existing modifications to `fcd`
891 891 (if any), the backup is used to undo certain premerges, confirm whether a
892 892 merge changed anything, and determine what line endings the new file should
893 893 have.
894 894
895 895 Backups only need to be written once since their content doesn't change
896 896 afterwards.
897 897 """
898 898 if fcd.isabsent():
899 899 return None
900 900 # TODO: Break this import cycle somehow. (filectx -> ctx -> fileset ->
901 901 # merge -> filemerge). (I suspect the fileset import is the weakest link)
902 902 from . import context
903 903
904 904 if isinstance(fcd, context.overlayworkingfilectx):
905 905 # If we're merging in-memory, we're free to put the backup anywhere.
906 906 fd, backup = pycompat.mkstemp(b'hg-merge-backup')
907 907 with os.fdopen(fd, 'wb') as f:
908 908 f.write(fcd.data())
909 909 else:
910 910 backup = scmutil.backuppath(ui, repo, fcd.path())
911 911 a = _workingpath(repo, fcd)
912 912 util.copyfile(a, backup)
913 913
914 914 return context.arbitraryfilectx(backup, repo=repo)
915 915
916 916
917 917 @contextlib.contextmanager
918 918 def _maketempfiles(files):
919 919 """Creates a temporary file for each (prefix, path, data) tuple in `files`,
920 920 so an external merge tool may use them.
921 921 """
922 922 tmproot = pycompat.mkdtemp(prefix=b'hgmerge-')
923 923
924 924 def maketempfrompath(prefix, path, data):
925 925 fullbase, ext = os.path.splitext(path)
926 926 pre = b"%s~%s" % (os.path.basename(fullbase), prefix)
927 927 name = os.path.join(tmproot, pre)
928 928 if ext:
929 929 name += ext
930 930 util.writefile(name, data)
931 931 return name
932 932
933 933 temp_files = []
934 934 for prefix, path, data in files:
935 935 temp_files.append(maketempfrompath(prefix, path, data))
936 936 try:
937 937 yield temp_files
938 938 finally:
939 939 shutil.rmtree(tmproot)
940 940
941 941
942 942 def filemerge(repo, wctx, mynode, orig, fcd, fco, fca, labels=None):
943 943 """perform a 3-way merge in the working directory
944 944
945 945 mynode = parent node before merge
946 946 orig = original local filename before merge
947 947 fco = other file context
948 948 fca = ancestor file context
949 949 fcd = local file context for current/destination file
950 950
951 951 Returns whether the merge is complete, the return value of the merge, and
952 952 a boolean indicating whether the file was deleted from disk."""
953 953 ui = repo.ui
954 954 fd = fcd.path()
955 955 uipathfn = scmutil.getuipathfn(repo)
956 956 fduipath = uipathfn(fd)
957 957 binary = fcd.isbinary() or fco.isbinary() or fca.isbinary()
958 958 symlink = b'l' in fcd.flags() + fco.flags()
959 959 changedelete = fcd.isabsent() or fco.isabsent()
960 960 tool, toolpath = _picktool(repo, ui, fd, binary, symlink, changedelete)
961 961 scriptfn = None
962 962 if tool in internals and tool.startswith(b'internal:'):
963 963 # normalize to new-style names (':merge' etc)
964 964 tool = tool[len(b'internal') :]
965 965 if toolpath and toolpath.startswith(b'python:'):
966 966 invalidsyntax = False
967 967 if toolpath.count(b':') >= 2:
968 968 script, scriptfn = toolpath[7:].rsplit(b':', 1)
969 969 if not scriptfn:
970 970 invalidsyntax = True
971 971 # missing :callable can lead to spliting on windows drive letter
972 972 if b'\\' in scriptfn or b'/' in scriptfn:
973 973 invalidsyntax = True
974 974 else:
975 975 invalidsyntax = True
976 976 if invalidsyntax:
977 977 raise error.Abort(_(b"invalid 'python:' syntax: %s") % toolpath)
978 978 toolpath = script
979 979 ui.debug(
980 980 b"picked tool '%s' for %s (binary %s symlink %s changedelete %s)\n"
981 981 % (
982 982 tool,
983 983 fduipath,
984 984 pycompat.bytestr(binary),
985 985 pycompat.bytestr(symlink),
986 986 pycompat.bytestr(changedelete),
987 987 )
988 988 )
989 989
990 990 if tool in internals:
991 991 func = internals[tool]
992 992 mergetype = func.mergetype
993 993 onfailure = func.onfailure
994 994 precheck = func.precheck
995 995 isexternal = False
996 996 else:
997 997 if wctx.isinmemory():
998 998 func = _xmergeimm
999 999 else:
1000 1000 func = _xmerge
1001 1001 mergetype = fullmerge
1002 1002 onfailure = _(b"merging %s failed!\n")
1003 1003 precheck = None
1004 1004 isexternal = True
1005 1005
1006 1006 toolconf = tool, toolpath, binary, symlink, scriptfn
1007 1007
1008 1008 if not labels:
1009 1009 labels = [b'local', b'other']
1010 1010 if len(labels) < 3:
1011 1011 labels.append(b'base')
1012 1012 local = simplemerge.MergeInput(fcd, labels[0])
1013 1013 other = simplemerge.MergeInput(fco, labels[1])
1014 1014 base = simplemerge.MergeInput(fca, labels[2])
1015 1015 if mergetype == nomerge:
1016 1016 return func(
1017 1017 repo,
1018 1018 mynode,
1019 1019 local,
1020 1020 other,
1021 1021 base,
1022 1022 toolconf,
1023 1023 )
1024 1024
1025 1025 if orig != fco.path():
1026 1026 ui.status(
1027 1027 _(b"merging %s and %s to %s\n")
1028 1028 % (uipathfn(orig), uipathfn(fco.path()), fduipath)
1029 1029 )
1030 1030 else:
1031 1031 ui.status(_(b"merging %s\n") % fduipath)
1032 1032
1033 1033 ui.debug(b"my %s other %s ancestor %s\n" % (fcd, fco, fca))
1034 1034
1035 1035 if precheck and not precheck(repo, mynode, fcd, fco, fca, toolconf):
1036 1036 if onfailure:
1037 1037 if wctx.isinmemory():
1038 1038 raise error.InMemoryMergeConflictsError(
1039 1039 b'in-memory merge does not support merge conflicts'
1040 1040 )
1041 1041 ui.warn(onfailure % fduipath)
1042 1042 return 1, False
1043 1043
1044 1044 backup = _makebackup(repo, ui, fcd)
1045 1045 r = 1
1046 1046 try:
1047 1047 internalmarkerstyle = ui.config(b'ui', b'mergemarkers')
1048 1048 if isexternal:
1049 1049 markerstyle = _toolstr(ui, tool, b'mergemarkers')
1050 1050 else:
1051 1051 markerstyle = internalmarkerstyle
1052 1052
1053 1053 if mergetype == fullmerge:
1054 _run_partial_resolution_tools(repo, local, other, base)
1054 1055 # conflict markers generated by premerge will use 'detailed'
1055 1056 # settings if either ui.mergemarkers or the tool's mergemarkers
1056 1057 # setting is 'detailed'. This way tools can have basic labels in
1057 1058 # space-constrained areas of the UI, but still get full information
1058 1059 # in conflict markers if premerge is 'keep' or 'keep-merge3'.
1059 1060 labeltool = None
1060 1061 if markerstyle != b'basic':
1061 1062 # respect 'tool's mergemarkertemplate (which defaults to
1062 1063 # command-templates.mergemarker)
1063 1064 labeltool = tool
1064 1065 if internalmarkerstyle != b'basic' or markerstyle != b'basic':
1065 1066 _populate_label_details(
1066 1067 repo, [local, other, base], tool=labeltool
1067 1068 )
1068 1069
1069 1070 r = _premerge(
1070 1071 repo,
1071 1072 local,
1072 1073 other,
1073 1074 base,
1074 1075 toolconf,
1075 1076 )
1076 1077 # we're done if premerge was successful (r is 0)
1077 1078 if not r:
1078 1079 return r, False
1079 1080
1080 1081 # Reset to basic labels
1081 1082 local.label_detail = None
1082 1083 other.label_detail = None
1083 1084 base.label_detail = None
1084 1085
1085 1086 if markerstyle != b'basic':
1086 1087 _populate_label_details(repo, [local, other, base], tool=tool)
1087 1088
1088 1089 needcheck, r, deleted = func(
1089 1090 repo,
1090 1091 mynode,
1091 1092 local,
1092 1093 other,
1093 1094 base,
1094 1095 toolconf,
1095 1096 backup,
1096 1097 )
1097 1098
1098 1099 if needcheck:
1099 1100 r = _check(repo, r, ui, tool, fcd, backup)
1100 1101
1101 1102 if r:
1102 1103 if onfailure:
1103 1104 if wctx.isinmemory():
1104 1105 raise error.InMemoryMergeConflictsError(
1105 1106 b'in-memory merge '
1106 1107 b'does not support '
1107 1108 b'merge conflicts'
1108 1109 )
1109 1110 ui.warn(onfailure % fduipath)
1110 1111 _onfilemergefailure(ui)
1111 1112
1112 1113 return r, deleted
1113 1114 finally:
1114 1115 if not r and backup is not None:
1115 1116 backup.remove()
1116 1117
1117 1118
1119 def _run_partial_resolution_tools(repo, local, other, base):
1120 """Runs partial-resolution tools on the three inputs and updates them."""
1121 ui = repo.ui
1122 # Tuples of (order, name, executable path)
1123 tools = []
1124 seen = set()
1125 section = b"partial-merge-tools"
1126 for k, v in ui.configitems(section):
1127 name = k.split(b'.')[0]
1128 if name in seen:
1129 continue
1130 patterns = ui.configlist(section, b'%s.patterns' % name, [])
1131 is_match = True
1132 if patterns:
1133 m = match.match(repo.root, b'', patterns)
1134 is_match = m(local.fctx.path())
1135 if is_match:
1136 order = ui.configint(section, b'%s.order' % name, 0)
1137 executable = ui.config(section, b'%s.executable' % name, name)
1138 tools.append((order, name, executable))
1139
1140 if not tools:
1141 return
1142 # Sort in configured order (first in tuple)
1143 tools.sort()
1144
1145 files = [
1146 (b"local", local.fctx.path(), local.text()),
1147 (b"base", base.fctx.path(), base.text()),
1148 (b"other", other.fctx.path(), other.text()),
1149 ]
1150
1151 with _maketempfiles(files) as temppaths:
1152 localpath, basepath, otherpath = temppaths
1153
1154 for order, name, executable in tools:
1155 cmd = procutil.shellquote(executable)
1156 # TODO: Allow the user to configure the command line using
1157 # $local, $base, $other.
1158 cmd = b'%s %s %s %s' % (cmd, localpath, basepath, otherpath)
1159 r = ui.system(cmd, cwd=repo.root, blockedtag=b'partial-mergetool')
1160 if r:
1161 raise error.StateError(
1162 b'partial merge tool %s exited with code %d' % (name, r)
1163 )
1164 local_text = util.readfile(localpath)
1165 other_text = util.readfile(otherpath)
1166 if local_text == other_text:
1167 # No need to run other tools if all conflicts have been resolved
1168 break
1169
1170 local.set_text(local_text)
1171 base.set_text(util.readfile(basepath))
1172 other.set_text(other_text)
1173
1174
1118 1175 def _haltmerge():
1119 1176 msg = _(b'merge halted after failed merge (see hg resolve)')
1120 1177 raise error.InterventionRequired(msg)
1121 1178
1122 1179
1123 1180 def _onfilemergefailure(ui):
1124 1181 action = ui.config(b'merge', b'on-failure')
1125 1182 if action == b'prompt':
1126 1183 msg = _(b'continue merge operation (yn)?$$ &Yes $$ &No')
1127 1184 if ui.promptchoice(msg, 0) == 1:
1128 1185 _haltmerge()
1129 1186 if action == b'halt':
1130 1187 _haltmerge()
1131 1188 # default action is 'continue', in which case we neither prompt nor halt
1132 1189
1133 1190
1134 1191 def hasconflictmarkers(data):
1135 1192 # Detect lines starting with a string of 7 identical characters from the
1136 1193 # subset Mercurial uses for conflict markers, followed by either the end of
1137 1194 # line or a space and some text. Note that using [<>=+|-]{7} would detect
1138 1195 # `<><><><><` as a conflict marker, which we don't want.
1139 1196 return bool(
1140 1197 re.search(
1141 1198 br"^([<>=+|-])\1{6}( .*)$",
1142 1199 data,
1143 1200 re.MULTILINE,
1144 1201 )
1145 1202 )
1146 1203
1147 1204
1148 1205 def _check(repo, r, ui, tool, fcd, backup):
1149 1206 fd = fcd.path()
1150 1207 uipathfn = scmutil.getuipathfn(repo)
1151 1208
1152 1209 if not r and (
1153 1210 _toolbool(ui, tool, b"checkconflicts")
1154 1211 or b'conflicts' in _toollist(ui, tool, b"check")
1155 1212 ):
1156 1213 if hasconflictmarkers(fcd.data()):
1157 1214 r = 1
1158 1215
1159 1216 checked = False
1160 1217 if b'prompt' in _toollist(ui, tool, b"check"):
1161 1218 checked = True
1162 1219 if ui.promptchoice(
1163 1220 _(b"was merge of '%s' successful (yn)?$$ &Yes $$ &No")
1164 1221 % uipathfn(fd),
1165 1222 1,
1166 1223 ):
1167 1224 r = 1
1168 1225
1169 1226 if (
1170 1227 not r
1171 1228 and not checked
1172 1229 and (
1173 1230 _toolbool(ui, tool, b"checkchanged")
1174 1231 or b'changed' in _toollist(ui, tool, b"check")
1175 1232 )
1176 1233 ):
1177 1234 if backup is not None and not fcd.cmp(backup):
1178 1235 if ui.promptchoice(
1179 1236 _(
1180 1237 b" output file %s appears unchanged\n"
1181 1238 b"was merge successful (yn)?"
1182 1239 b"$$ &Yes $$ &No"
1183 1240 )
1184 1241 % uipathfn(fd),
1185 1242 1,
1186 1243 ):
1187 1244 r = 1
1188 1245
1189 1246 if backup is not None and _toolbool(ui, tool, b"fixeol"):
1190 1247 _matcheol(_workingpath(repo, fcd), backup)
1191 1248
1192 1249 return r
1193 1250
1194 1251
1195 1252 def _workingpath(repo, ctx):
1196 1253 return repo.wjoin(ctx.path())
1197 1254
1198 1255
1199 1256 def loadinternalmerge(ui, extname, registrarobj):
1200 1257 """Load internal merge tool from specified registrarobj"""
1201 1258 for name, func in registrarobj._table.items():
1202 1259 fullname = b':' + name
1203 1260 internals[fullname] = func
1204 1261 internals[b'internal:' + name] = func
1205 1262 internalsdoc[fullname] = func
1206 1263
1207 1264 capabilities = sorted([k for k, v in func.capabilities.items() if v])
1208 1265 if capabilities:
1209 1266 capdesc = b" (actual capabilities: %s)" % b', '.join(
1210 1267 capabilities
1211 1268 )
1212 1269 func.__doc__ = func.__doc__ + pycompat.sysstr(b"\n\n%s" % capdesc)
1213 1270
1214 1271 # to put i18n comments into hg.pot for automatically generated texts
1215 1272
1216 1273 # i18n: "binary" and "symlink" are keywords
1217 1274 # i18n: this text is added automatically
1218 1275 _(b" (actual capabilities: binary, symlink)")
1219 1276 # i18n: "binary" is keyword
1220 1277 # i18n: this text is added automatically
1221 1278 _(b" (actual capabilities: binary)")
1222 1279 # i18n: "symlink" is keyword
1223 1280 # i18n: this text is added automatically
1224 1281 _(b" (actual capabilities: symlink)")
1225 1282
1226 1283
1227 1284 # load built-in merge tools explicitly to setup internalsdoc
1228 1285 loadinternalmerge(None, None, internaltool)
1229 1286
1230 1287 # tell hggettext to extract docstrings from these functions:
1231 1288 i18nfunctions = internals.values()
@@ -1,531 +1,534 b''
1 1 # Copyright (C) 2004, 2005 Canonical Ltd
2 2 #
3 3 # This program is free software; you can redistribute it and/or modify
4 4 # it under the terms of the GNU General Public License as published by
5 5 # the Free Software Foundation; either version 2 of the License, or
6 6 # (at your option) any later version.
7 7 #
8 8 # This program is distributed in the hope that it will be useful,
9 9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 11 # GNU General Public License for more details.
12 12 #
13 13 # You should have received a copy of the GNU General Public License
14 14 # along with this program; if not, see <http://www.gnu.org/licenses/>.
15 15
16 16 # mbp: "you know that thing where cvs gives you conflict markers?"
17 17 # s: "i hate that."
18 18
19 19
20 20 from .i18n import _
21 21 from . import (
22 22 error,
23 23 mdiff,
24 24 pycompat,
25 25 )
26 26 from .utils import stringutil
27 27
28 28
29 29 def intersect(ra, rb):
30 30 """Given two ranges return the range where they intersect or None.
31 31
32 32 >>> intersect((0, 10), (0, 6))
33 33 (0, 6)
34 34 >>> intersect((0, 10), (5, 15))
35 35 (5, 10)
36 36 >>> intersect((0, 10), (10, 15))
37 37 >>> intersect((0, 9), (10, 15))
38 38 >>> intersect((0, 9), (7, 15))
39 39 (7, 9)
40 40 """
41 41 assert ra[0] <= ra[1]
42 42 assert rb[0] <= rb[1]
43 43
44 44 sa = max(ra[0], rb[0])
45 45 sb = min(ra[1], rb[1])
46 46 if sa < sb:
47 47 return sa, sb
48 48 else:
49 49 return None
50 50
51 51
52 52 def compare_range(a, astart, aend, b, bstart, bend):
53 53 """Compare a[astart:aend] == b[bstart:bend], without slicing."""
54 54 if (aend - astart) != (bend - bstart):
55 55 return False
56 56 for ia, ib in zip(
57 57 pycompat.xrange(astart, aend), pycompat.xrange(bstart, bend)
58 58 ):
59 59 if a[ia] != b[ib]:
60 60 return False
61 61 else:
62 62 return True
63 63
64 64
65 65 class Merge3Text:
66 66 """3-way merge of texts.
67 67
68 68 Given strings BASE, OTHER, THIS, tries to produce a combined text
69 69 incorporating the changes from both BASE->OTHER and BASE->THIS."""
70 70
71 71 def __init__(self, basetext, atext, btext, base=None, a=None, b=None):
72 72 self.basetext = basetext
73 73 self.atext = atext
74 74 self.btext = btext
75 75 if base is None:
76 76 base = mdiff.splitnewlines(basetext)
77 77 if a is None:
78 78 a = mdiff.splitnewlines(atext)
79 79 if b is None:
80 80 b = mdiff.splitnewlines(btext)
81 81 self.base = base
82 82 self.a = a
83 83 self.b = b
84 84
85 85 def merge_groups(self):
86 86 """Yield sequence of line groups. Each one is a tuple:
87 87
88 88 'unchanged', lines
89 89 Lines unchanged from base
90 90
91 91 'a', lines
92 92 Lines taken from a
93 93
94 94 'same', lines
95 95 Lines taken from a (and equal to b)
96 96
97 97 'b', lines
98 98 Lines taken from b
99 99
100 100 'conflict', (base_lines, a_lines, b_lines)
101 101 Lines from base were changed to either a or b and conflict.
102 102 """
103 103 for t in self.merge_regions():
104 104 what = t[0]
105 105 if what == b'unchanged':
106 106 yield what, self.base[t[1] : t[2]]
107 107 elif what == b'a' or what == b'same':
108 108 yield what, self.a[t[1] : t[2]]
109 109 elif what == b'b':
110 110 yield what, self.b[t[1] : t[2]]
111 111 elif what == b'conflict':
112 112 yield (
113 113 what,
114 114 (
115 115 self.base[t[1] : t[2]],
116 116 self.a[t[3] : t[4]],
117 117 self.b[t[5] : t[6]],
118 118 ),
119 119 )
120 120 else:
121 121 raise ValueError(what)
122 122
123 123 def merge_regions(self):
124 124 """Return sequences of matching and conflicting regions.
125 125
126 126 This returns tuples, where the first value says what kind we
127 127 have:
128 128
129 129 'unchanged', start, end
130 130 Take a region of base[start:end]
131 131
132 132 'same', astart, aend
133 133 b and a are different from base but give the same result
134 134
135 135 'a', start, end
136 136 Non-clashing insertion from a[start:end]
137 137
138 138 'conflict', zstart, zend, astart, aend, bstart, bend
139 139 Conflict between a and b, with z as common ancestor
140 140
141 141 Method is as follows:
142 142
143 143 The two sequences align only on regions which match the base
144 144 and both descendants. These are found by doing a two-way diff
145 145 of each one against the base, and then finding the
146 146 intersections between those regions. These "sync regions"
147 147 are by definition unchanged in both and easily dealt with.
148 148
149 149 The regions in between can be in any of three cases:
150 150 conflicted, or changed on only one side.
151 151 """
152 152
153 153 # section a[0:ia] has been disposed of, etc
154 154 iz = ia = ib = 0
155 155
156 156 for region in self.find_sync_regions():
157 157 zmatch, zend, amatch, aend, bmatch, bend = region
158 158 # print 'match base [%d:%d]' % (zmatch, zend)
159 159
160 160 matchlen = zend - zmatch
161 161 assert matchlen >= 0
162 162 assert matchlen == (aend - amatch)
163 163 assert matchlen == (bend - bmatch)
164 164
165 165 len_a = amatch - ia
166 166 len_b = bmatch - ib
167 167 len_base = zmatch - iz
168 168 assert len_a >= 0
169 169 assert len_b >= 0
170 170 assert len_base >= 0
171 171
172 172 # print 'unmatched a=%d, b=%d' % (len_a, len_b)
173 173
174 174 if len_a or len_b:
175 175 # try to avoid actually slicing the lists
176 176 equal_a = compare_range(
177 177 self.a, ia, amatch, self.base, iz, zmatch
178 178 )
179 179 equal_b = compare_range(
180 180 self.b, ib, bmatch, self.base, iz, zmatch
181 181 )
182 182 same = compare_range(self.a, ia, amatch, self.b, ib, bmatch)
183 183
184 184 if same:
185 185 yield b'same', ia, amatch
186 186 elif equal_a and not equal_b:
187 187 yield b'b', ib, bmatch
188 188 elif equal_b and not equal_a:
189 189 yield b'a', ia, amatch
190 190 elif not equal_a and not equal_b:
191 191 yield b'conflict', iz, zmatch, ia, amatch, ib, bmatch
192 192 else:
193 193 raise AssertionError(b"can't handle a=b=base but unmatched")
194 194
195 195 ia = amatch
196 196 ib = bmatch
197 197 iz = zmatch
198 198
199 199 # if the same part of the base was deleted on both sides
200 200 # that's OK, we can just skip it.
201 201
202 202 if matchlen > 0:
203 203 assert ia == amatch
204 204 assert ib == bmatch
205 205 assert iz == zmatch
206 206
207 207 yield b'unchanged', zmatch, zend
208 208 iz = zend
209 209 ia = aend
210 210 ib = bend
211 211
212 212 def find_sync_regions(self):
213 213 """Return a list of sync regions, where both descendants match the base.
214 214
215 215 Generates a list of (base1, base2, a1, a2, b1, b2). There is
216 216 always a zero-length sync region at the end of all the files.
217 217 """
218 218
219 219 ia = ib = 0
220 220 amatches = mdiff.get_matching_blocks(self.basetext, self.atext)
221 221 bmatches = mdiff.get_matching_blocks(self.basetext, self.btext)
222 222 len_a = len(amatches)
223 223 len_b = len(bmatches)
224 224
225 225 sl = []
226 226
227 227 while ia < len_a and ib < len_b:
228 228 abase, amatch, alen = amatches[ia]
229 229 bbase, bmatch, blen = bmatches[ib]
230 230
231 231 # there is an unconflicted block at i; how long does it
232 232 # extend? until whichever one ends earlier.
233 233 i = intersect((abase, abase + alen), (bbase, bbase + blen))
234 234 if i:
235 235 intbase = i[0]
236 236 intend = i[1]
237 237 intlen = intend - intbase
238 238
239 239 # found a match of base[i[0], i[1]]; this may be less than
240 240 # the region that matches in either one
241 241 assert intlen <= alen
242 242 assert intlen <= blen
243 243 assert abase <= intbase
244 244 assert bbase <= intbase
245 245
246 246 asub = amatch + (intbase - abase)
247 247 bsub = bmatch + (intbase - bbase)
248 248 aend = asub + intlen
249 249 bend = bsub + intlen
250 250
251 251 assert self.base[intbase:intend] == self.a[asub:aend], (
252 252 self.base[intbase:intend],
253 253 self.a[asub:aend],
254 254 )
255 255
256 256 assert self.base[intbase:intend] == self.b[bsub:bend]
257 257
258 258 sl.append((intbase, intend, asub, aend, bsub, bend))
259 259
260 260 # advance whichever one ends first in the base text
261 261 if (abase + alen) < (bbase + blen):
262 262 ia += 1
263 263 else:
264 264 ib += 1
265 265
266 266 intbase = len(self.base)
267 267 abase = len(self.a)
268 268 bbase = len(self.b)
269 269 sl.append((intbase, intbase, abase, abase, bbase, bbase))
270 270
271 271 return sl
272 272
273 273
274 274 def _verifytext(input):
275 275 """verifies that text is non-binary (unless opts[text] is passed,
276 276 then we just warn)"""
277 277 if stringutil.binary(input.text()):
278 278 msg = _(b"%s looks like a binary file.") % input.fctx.path()
279 279 raise error.Abort(msg)
280 280
281 281
282 282 def _format_labels(*inputs):
283 283 pad = max(len(input.label) if input.label else 0 for input in inputs)
284 284 labels = []
285 285 for input in inputs:
286 286 if input.label:
287 287 if input.label_detail:
288 288 label = (
289 289 (input.label + b':').ljust(pad + 1)
290 290 + b' '
291 291 + input.label_detail
292 292 )
293 293 else:
294 294 label = input.label
295 295 # 8 for the prefix of conflict marker lines (e.g. '<<<<<<< ')
296 296 labels.append(stringutil.ellipsis(label, 80 - 8))
297 297 else:
298 298 labels.append(None)
299 299 return labels
300 300
301 301
302 302 def _detect_newline(m3):
303 303 if len(m3.a) > 0:
304 304 if m3.a[0].endswith(b'\r\n'):
305 305 return b'\r\n'
306 306 elif m3.a[0].endswith(b'\r'):
307 307 return b'\r'
308 308 return b'\n'
309 309
310 310
311 311 def _minimize(a_lines, b_lines):
312 312 """Trim conflict regions of lines where A and B sides match.
313 313
314 314 Lines where both A and B have made the same changes at the beginning
315 315 or the end of each merge region are eliminated from the conflict
316 316 region and are instead considered the same.
317 317 """
318 318 alen = len(a_lines)
319 319 blen = len(b_lines)
320 320
321 321 # find matches at the front
322 322 ii = 0
323 323 while ii < alen and ii < blen and a_lines[ii] == b_lines[ii]:
324 324 ii += 1
325 325 startmatches = ii
326 326
327 327 # find matches at the end
328 328 ii = 0
329 329 while ii < alen and ii < blen and a_lines[-ii - 1] == b_lines[-ii - 1]:
330 330 ii += 1
331 331 endmatches = ii
332 332
333 333 lines_before = a_lines[:startmatches]
334 334 new_a_lines = a_lines[startmatches : alen - endmatches]
335 335 new_b_lines = b_lines[startmatches : blen - endmatches]
336 336 lines_after = a_lines[alen - endmatches :]
337 337 return lines_before, new_a_lines, new_b_lines, lines_after
338 338
339 339
340 340 def render_minimized(
341 341 m3,
342 342 name_a=None,
343 343 name_b=None,
344 344 start_marker=b'<<<<<<<',
345 345 mid_marker=b'=======',
346 346 end_marker=b'>>>>>>>',
347 347 ):
348 348 """Return merge in cvs-like form."""
349 349 newline = _detect_newline(m3)
350 350 conflicts = False
351 351 if name_a:
352 352 start_marker = start_marker + b' ' + name_a
353 353 if name_b:
354 354 end_marker = end_marker + b' ' + name_b
355 355 merge_groups = m3.merge_groups()
356 356 lines = []
357 357 for what, group_lines in merge_groups:
358 358 if what == b'conflict':
359 359 conflicts = True
360 360 base_lines, a_lines, b_lines = group_lines
361 361 minimized = _minimize(a_lines, b_lines)
362 362 lines_before, a_lines, b_lines, lines_after = minimized
363 363 lines.extend(lines_before)
364 364 lines.append(start_marker + newline)
365 365 lines.extend(a_lines)
366 366 lines.append(mid_marker + newline)
367 367 lines.extend(b_lines)
368 368 lines.append(end_marker + newline)
369 369 lines.extend(lines_after)
370 370 else:
371 371 lines.extend(group_lines)
372 372 return lines, conflicts
373 373
374 374
375 375 def render_merge3(m3, name_a, name_b, name_base):
376 376 """Render conflicts as 3-way conflict markers."""
377 377 newline = _detect_newline(m3)
378 378 conflicts = False
379 379 lines = []
380 380 for what, group_lines in m3.merge_groups():
381 381 if what == b'conflict':
382 382 base_lines, a_lines, b_lines = group_lines
383 383 conflicts = True
384 384 lines.append(b'<<<<<<< ' + name_a + newline)
385 385 lines.extend(a_lines)
386 386 lines.append(b'||||||| ' + name_base + newline)
387 387 lines.extend(base_lines)
388 388 lines.append(b'=======' + newline)
389 389 lines.extend(b_lines)
390 390 lines.append(b'>>>>>>> ' + name_b + newline)
391 391 else:
392 392 lines.extend(group_lines)
393 393 return lines, conflicts
394 394
395 395
396 396 def render_mergediff(m3, name_a, name_b, name_base):
397 397 """Render conflicts as conflict markers with one snapshot and one diff."""
398 398 newline = _detect_newline(m3)
399 399 lines = []
400 400 conflicts = False
401 401 for what, group_lines in m3.merge_groups():
402 402 if what == b'conflict':
403 403 base_lines, a_lines, b_lines = group_lines
404 404 base_text = b''.join(base_lines)
405 405 b_blocks = list(
406 406 mdiff.allblocks(
407 407 base_text,
408 408 b''.join(b_lines),
409 409 lines1=base_lines,
410 410 lines2=b_lines,
411 411 )
412 412 )
413 413 a_blocks = list(
414 414 mdiff.allblocks(
415 415 base_text,
416 416 b''.join(a_lines),
417 417 lines1=base_lines,
418 418 lines2=b_lines,
419 419 )
420 420 )
421 421
422 422 def matching_lines(blocks):
423 423 return sum(
424 424 block[1] - block[0]
425 425 for block, kind in blocks
426 426 if kind == b'='
427 427 )
428 428
429 429 def diff_lines(blocks, lines1, lines2):
430 430 for block, kind in blocks:
431 431 if kind == b'=':
432 432 for line in lines1[block[0] : block[1]]:
433 433 yield b' ' + line
434 434 else:
435 435 for line in lines1[block[0] : block[1]]:
436 436 yield b'-' + line
437 437 for line in lines2[block[2] : block[3]]:
438 438 yield b'+' + line
439 439
440 440 lines.append(b"<<<<<<<" + newline)
441 441 if matching_lines(a_blocks) < matching_lines(b_blocks):
442 442 lines.append(b"======= " + name_a + newline)
443 443 lines.extend(a_lines)
444 444 lines.append(b"------- " + name_base + newline)
445 445 lines.append(b"+++++++ " + name_b + newline)
446 446 lines.extend(diff_lines(b_blocks, base_lines, b_lines))
447 447 else:
448 448 lines.append(b"------- " + name_base + newline)
449 449 lines.append(b"+++++++ " + name_a + newline)
450 450 lines.extend(diff_lines(a_blocks, base_lines, a_lines))
451 451 lines.append(b"======= " + name_b + newline)
452 452 lines.extend(b_lines)
453 453 lines.append(b">>>>>>>" + newline)
454 454 conflicts = True
455 455 else:
456 456 lines.extend(group_lines)
457 457 return lines, conflicts
458 458
459 459
460 460 def _resolve(m3, sides):
461 461 lines = []
462 462 for what, group_lines in m3.merge_groups():
463 463 if what == b'conflict':
464 464 for side in sides:
465 465 lines.extend(group_lines[side])
466 466 else:
467 467 lines.extend(group_lines)
468 468 return lines
469 469
470 470
471 471 class MergeInput:
472 472 def __init__(self, fctx, label=None, label_detail=None):
473 473 self.fctx = fctx
474 474 self.label = label
475 475 # If the "detail" part is set, then that is rendered after the label and
476 476 # separated by a ':'. The label is padded to make the ':' aligned among
477 477 # all merge inputs.
478 478 self.label_detail = label_detail
479 479 self._text = None
480 480
481 481 def text(self):
482 482 if self._text is None:
483 483 # Merges were always run in the working copy before, which means
484 484 # they used decoded data, if the user defined any repository
485 485 # filters.
486 486 #
487 487 # Maintain that behavior today for BC, though perhaps in the future
488 488 # it'd be worth considering whether merging encoded data (what the
489 489 # repository usually sees) might be more useful.
490 490 self._text = self.fctx.decodeddata()
491 491 return self._text
492 492
493 def set_text(self, text):
494 self._text = text
495
493 496
494 497 def simplemerge(
495 498 local,
496 499 base,
497 500 other,
498 501 mode=b'merge',
499 502 allow_binary=False,
500 503 ):
501 504 """Performs the simplemerge algorithm.
502 505
503 506 The merged result is written into `localctx`.
504 507 """
505 508
506 509 if not allow_binary:
507 510 _verifytext(local)
508 511 _verifytext(base)
509 512 _verifytext(other)
510 513
511 514 m3 = Merge3Text(base.text(), local.text(), other.text())
512 515 conflicts = False
513 516 if mode == b'union':
514 517 lines = _resolve(m3, (1, 2))
515 518 elif mode == b'local':
516 519 lines = _resolve(m3, (1,))
517 520 elif mode == b'other':
518 521 lines = _resolve(m3, (2,))
519 522 else:
520 523 if mode == b'mergediff':
521 524 labels = _format_labels(local, other, base)
522 525 lines, conflicts = render_mergediff(m3, *labels)
523 526 elif mode == b'merge3':
524 527 labels = _format_labels(local, other, base)
525 528 lines, conflicts = render_merge3(m3, *labels)
526 529 else:
527 530 labels = _format_labels(local, other)
528 531 lines, conflicts = render_minimized(m3, *labels)
529 532
530 533 mergedtext = b''.join(lines)
531 534 return mergedtext, conflicts
General Comments 0
You need to be logged in to leave comments. Login now