##// END OF EJS Templates
mergedriver: delete it...
Martin von Zweigbergk -
r46091:32ce4cba default
parent child Browse files
Show More
@@ -5986,10 +5986,6 b' def resolve(ui, repo, *pats, **opts):'
5986 5986 b'resolve.resolved',
5987 5987 b'R',
5988 5988 ),
5989 mergestatemod.MERGE_RECORD_DRIVER_RESOLVED: (
5990 b'resolve.driverresolved',
5991 b'D',
5992 ),
5993 5989 }
5994 5990
5995 5991 for f in ms:
@@ -6014,21 +6010,9 b' def resolve(ui, repo, *pats, **opts):'
6014 6010 )
6015 6011
6016 6012 wctx = repo[None]
6017
6018 if (
6019 ms.mergedriver
6020 and ms.mdstate() == mergestatemod.MERGE_DRIVER_STATE_UNMARKED
6021 ):
6022 proceed = mergemod.driverpreprocess(repo, ms, wctx)
6023 ms.commit()
6024 # allow mark and unmark to go through
6025 if not mark and not unmark and not proceed:
6026 return 1
6027
6028 6013 m = scmutil.match(wctx, pats, opts)
6029 6014 ret = 0
6030 6015 didwork = False
6031 runconclude = False
6032 6016
6033 6017 tocomplete = []
6034 6018 hasconflictmarkers = []
@@ -6043,26 +6027,6 b' def resolve(ui, repo, *pats, **opts):'
6043 6027
6044 6028 didwork = True
6045 6029
6046 # don't let driver-resolved files be marked, and run the conclude
6047 # step if asked to resolve
6048 if ms[f] == mergestatemod.MERGE_RECORD_DRIVER_RESOLVED:
6049 exact = m.exact(f)
6050 if mark:
6051 if exact:
6052 ui.warn(
6053 _(b'not marking %s as it is driver-resolved\n')
6054 % uipathfn(f)
6055 )
6056 elif unmark:
6057 if exact:
6058 ui.warn(
6059 _(b'not unmarking %s as it is driver-resolved\n')
6060 % uipathfn(f)
6061 )
6062 else:
6063 runconclude = True
6064 continue
6065
6066 6030 # path conflicts must be resolved manually
6067 6031 if ms[f] in (
6068 6032 mergestatemod.MERGE_RECORD_UNRESOLVED_PATH,
@@ -6184,32 +6148,11 b' def resolve(ui, repo, *pats, **opts):'
6184 6148 ui.warn(_(b"arguments do not match paths that need resolving\n"))
6185 6149 if hint:
6186 6150 ui.warn(hint)
6187 elif ms.mergedriver and ms.mdstate() != b's':
6188 # run conclude step when either a driver-resolved file is requested
6189 # or there are no driver-resolved files
6190 # we can't use 'ret' to determine whether any files are unresolved
6191 # because we might not have tried to resolve some
6192 if (runconclude or not list(ms.driverresolved())) and not list(
6193 ms.unresolved()
6194 ):
6195 proceed = mergemod.driverconclude(repo, ms, wctx)
6196 ms.commit()
6197 if not proceed:
6198 return 1
6199
6200 # Nudge users into finishing an unfinished operation
6151
6201 6152 unresolvedf = list(ms.unresolved())
6202 driverresolvedf = list(ms.driverresolved())
6203 if not unresolvedf and not driverresolvedf:
6153 if not unresolvedf:
6204 6154 ui.status(_(b'(no more unresolved files)\n'))
6205 6155 cmdutil.checkafterresolved(repo)
6206 elif not unresolvedf:
6207 ui.status(
6208 _(
6209 b'(no more unresolved files -- '
6210 b'run "hg resolve --all" to conclude)\n'
6211 )
6212 )
6213 6156
6214 6157 return ret
6215 6158
@@ -635,9 +635,6 b' coreconfigitem('
635 635 coreconfigitem(
636 636 b'experimental', b'httppostargs', default=False,
637 637 )
638 coreconfigitem(
639 b'experimental', b'mergedriver', default=None,
640 )
641 638 coreconfigitem(b'experimental', b'nointerrupt', default=False)
642 639 coreconfigitem(b'experimental', b'nointerrupt-interactiveonly', default=True)
643 640
@@ -37,30 +37,18 b' Currently known records:'
37 37 | * O: the node of the "other" part of the merge (hexified version)
38 38 | * F: a file to be merged entry
39 39 | * C: a change/delete or delete/change conflict
40 | * D: a file that the external merge driver will merge internally
41 | (experimental)
42 40 | * P: a path conflict (file vs directory)
43 | * m: the external merge driver defined for this merge plus its run state
44 | (experimental)
45 41 | * f: a (filename, dictionary) tuple of optional values for a given file
46 42 | * X: unsupported mandatory record type (used in tests)
47 43 | * x: unsupported advisory record type (used in tests)
48 44 | * l: the labels for the parts of the merge.
49 45
50 Merge driver run states (experimental):
51
52 | * u: driver-resolved files unmarked -- needs to be run next time we're
53 | about to resolve or commit
54 | * m: driver-resolved files marked -- only needs to be run before commit
55 | * s: success/skipped -- does not need to be run any more
56
57 46 Merge record states (indexed by filename):
58 47
59 48 | * u: unresolved conflict
60 49 | * r: resolved conflict
61 50 | * pu: unresolved path conflict (file conflicts with directory)
62 51 | * pr: resolved path conflict
63 | * d: driver-resolved conflict
64 52
65 53 The resolve command transitions between 'u' and 'r' for conflicts and
66 54 'pu' and 'pr' for path conflicts.
@@ -355,20 +355,6 b' def _checkcollision(repo, wmf, mresult):'
355 355 lastfull = f
356 356
357 357
358 def driverpreprocess(repo, ms, wctx, labels=None):
359 """run the preprocess step of the merge driver, if any
360
361 This is currently not implemented -- it's an extension point."""
362 return True
363
364
365 def driverconclude(repo, ms, wctx, labels=None):
366 """run the conclude step of the merge driver, if any
367
368 This is currently not implemented -- it's an extension point."""
369 return True
370
371
372 358 def _filesindirs(repo, manifest, dirs):
373 359 """
374 360 Generator that yields pairs of all the files in the manifest that are found
@@ -1605,27 +1591,6 b' def applyupdates('
1605 1591 mergestatemod.ACTION_DIR_RENAME_MOVE_LOCAL,
1606 1592 )
1607 1593 )
1608 # the ordering is important here -- ms.mergedriver will raise if the merge
1609 # driver has changed, and we want to be able to bypass it when overwrite is
1610 # True
1611 usemergedriver = not overwrite and mergeactions and ms.mergedriver
1612
1613 if usemergedriver:
1614 ms.commit()
1615 proceed = driverpreprocess(repo, ms, wctx, labels=labels)
1616 # the driver might leave some files unresolved
1617 unresolvedf = set(ms.unresolved())
1618 if not proceed:
1619 # XXX setting unresolved to at least 1 is a hack to make sure we
1620 # error out
1621 return updateresult(
1622 updated, merged, removed, max(len(unresolvedf), 1)
1623 )
1624 newactions = []
1625 for f, args, msg in mergeactions:
1626 if f in unresolvedf:
1627 newactions.append((f, args, msg))
1628 mergeactions = newactions
1629 1594
1630 1595 try:
1631 1596 # premerge
@@ -1655,18 +1620,6 b' def applyupdates('
1655 1620
1656 1621 unresolved = ms.unresolvedcount()
1657 1622
1658 if (
1659 usemergedriver
1660 and not unresolved
1661 and ms.mdstate() != mergestatemod.MERGE_DRIVER_STATE_SUCCESS
1662 ):
1663 if not driverconclude(repo, ms, wctx, labels=labels):
1664 # XXX setting unresolved to at least 1 is a hack to make sure we
1665 # error out
1666 unresolved = max(unresolved, 1)
1667
1668 ms.commit()
1669
1670 1623 msupdated, msmerged, msremoved = ms.counts()
1671 1624 updated += msupdated
1672 1625 merged += msmerged
@@ -1674,9 +1627,6 b' def applyupdates('
1674 1627
1675 1628 extraactions = ms.actions()
1676 1629 if extraactions:
1677 mfiles = {
1678 a[0] for a in mresult.getactions((mergestatemod.ACTION_MERGE,))
1679 }
1680 1630 for k, acts in pycompat.iteritems(extraactions):
1681 1631 for a in acts:
1682 1632 mresult.addfile(a[0], k, *a[1:])
@@ -1684,27 +1634,6 b' def applyupdates('
1684 1634 # no filedata until mergestate is updated to provide it
1685 1635 for a in acts:
1686 1636 getfiledata[a[0]] = None
1687 # Remove these files from actions[ACTION_MERGE] as well. This is
1688 # important because in recordupdates, files in actions[ACTION_MERGE]
1689 # are processed after files in other actions, and the merge driver
1690 # might add files to those actions via extraactions above. This can
1691 # lead to a file being recorded twice, with poor results. This is
1692 # especially problematic for actions[ACTION_REMOVE] (currently only
1693 # possible with the merge driver in the initial merge process;
1694 # interrupted merges don't go through this flow).
1695 #
1696 # The real fix here is to have indexes by both file and action so
1697 # that when the action for a file is changed it is automatically
1698 # reflected in the other action lists. But that involves a more
1699 # complex data structure, so this will do for now.
1700 #
1701 # We don't need to do the same operation for 'dc' and 'cd' because
1702 # those lists aren't consulted again.
1703 mfiles.difference_update(a[0] for a in acts)
1704
1705 for a in list(mresult.getactions((mergestatemod.ACTION_MERGE,))):
1706 if a[0] not in mfiles:
1707 mresult.removefile(a[0])
1708 1637
1709 1638 progress.complete()
1710 1639 assert len(getfiledata) == (
@@ -48,8 +48,6 b" RECORD_LOCAL = b'L'"
48 48 RECORD_OTHER = b'O'
49 49 # record merge labels
50 50 RECORD_LABELS = b'l'
51 # store info about merge driver used and it's state
52 RECORD_MERGE_DRIVER_STATE = b'm'
53 51
54 52 #####
55 53 # record extra information about files, with one entry containing info about one
@@ -65,7 +63,6 b" RECORD_FILE_VALUES = b'f'"
65 63 #####
66 64 RECORD_MERGED = b'F'
67 65 RECORD_CHANGEDELETE_CONFLICT = b'C'
68 RECORD_MERGE_DRIVER_MERGE = b'D'
69 66 # the path was dir on one side of merge and file on another
70 67 RECORD_PATH_CONFLICT = b'P'
71 68
@@ -77,7 +74,6 b" MERGE_RECORD_UNRESOLVED = b'u'"
77 74 MERGE_RECORD_RESOLVED = b'r'
78 75 MERGE_RECORD_UNRESOLVED_PATH = b'pu'
79 76 MERGE_RECORD_RESOLVED_PATH = b'pr'
80 MERGE_RECORD_DRIVER_RESOLVED = b'd'
81 77 # represents that the file was automatically merged in favor
82 78 # of other version. This info is used on commit.
83 79 # This is now deprecated and commit related information is now
@@ -91,18 +87,16 b" MERGE_RECORD_MERGED_OTHER = b'o'"
91 87 RECORD_OVERRIDE = b't'
92 88
93 89 #####
94 # possible states which a merge driver can have. These are stored inside a
95 # RECORD_MERGE_DRIVER_STATE entry
96 #####
97 MERGE_DRIVER_STATE_UNMARKED = b'u'
98 MERGE_DRIVER_STATE_MARKED = b'm'
99 MERGE_DRIVER_STATE_SUCCESS = b's'
100
101 #####
102 90 # legacy records which are no longer used but kept to prevent breaking BC
103 91 #####
104 92 # This record was release in 5.4 and usage was removed in 5.5
105 93 LEGACY_RECORD_RESOLVED_OTHER = b'R'
94 # This record was release in 3.7 and usage was removed in 5.6
95 LEGACY_RECORD_DRIVER_RESOLVED = b'd'
96 # This record was release in 3.7 and usage was removed in 5.6
97 LEGACY_MERGE_DRIVER_STATE = b'm'
98 # This record was release in 3.7 and usage was removed in 5.6
99 LEGACY_MERGE_DRIVER_MERGE = b'D'
106 100
107 101
108 102 ACTION_FORGET = b'f'
@@ -147,26 +141,15 b' class _mergestate_base(object):'
147 141 O: the node of the "other" part of the merge (hexified version)
148 142 F: a file to be merged entry
149 143 C: a change/delete or delete/change conflict
150 D: a file that the external merge driver will merge internally
151 (experimental)
152 144 P: a path conflict (file vs directory)
153 m: the external merge driver defined for this merge plus its run state
154 (experimental)
155 145 f: a (filename, dictionary) tuple of optional values for a given file
156 146 l: the labels for the parts of the merge.
157 147
158 Merge driver run states (experimental):
159 u: driver-resolved files unmarked -- needs to be run next time we're about
160 to resolve or commit
161 m: driver-resolved files marked -- only needs to be run before commit
162 s: success/skipped -- does not need to be run any more
163
164 148 Merge record states (stored in self._state, indexed by filename):
165 149 u: unresolved conflict
166 150 r: resolved conflict
167 151 pu: unresolved path conflict (file conflicts with directory)
168 152 pr: resolved path conflict
169 d: driver-resolved conflict
170 153
171 154 The resolve command transitions between 'u' and 'r' for conflicts and
172 155 'pu' and 'pr' for path conflicts.
@@ -182,8 +165,6 b' class _mergestate_base(object):'
182 165 self._local = None
183 166 self._other = None
184 167 self._labels = None
185 self._readmergedriver = None
186 self._mdstate = MERGE_DRIVER_STATE_UNMARKED
187 168 # contains a mapping of form:
188 169 # {filename : (merge_return_value, action_to_be_performed}
189 170 # these are results of re-running merge process
@@ -199,32 +180,6 b' class _mergestate_base(object):'
199 180 self._local = node
200 181 self._other = other
201 182 self._labels = labels
202 if self.mergedriver:
203 self._mdstate = MERGE_DRIVER_STATE_SUCCESS
204
205 @util.propertycache
206 def mergedriver(self):
207 # protect against the following:
208 # - A configures a malicious merge driver in their hgrc, then
209 # pauses the merge
210 # - A edits their hgrc to remove references to the merge driver
211 # - A gives a copy of their entire repo, including .hg, to B
212 # - B inspects .hgrc and finds it to be clean
213 # - B then continues the merge and the malicious merge driver
214 # gets invoked
215 configmergedriver = self._repo.ui.config(
216 b'experimental', b'mergedriver'
217 )
218 if (
219 self._readmergedriver is not None
220 and self._readmergedriver != configmergedriver
221 ):
222 raise error.ConfigError(
223 _(b"merge driver changed since merge started"),
224 hint=_(b"revert merge driver change or abort merge"),
225 )
226
227 return configmergedriver
228 183
229 184 @util.propertycache
230 185 def local(self):
@@ -330,9 +285,6 b' class _mergestate_base(object):'
330 285 self._state[dfile][0] = state
331 286 self._dirty = True
332 287
333 def mdstate(self):
334 return self._mdstate
335
336 288 def unresolved(self):
337 289 """Obtain the paths of unresolved files."""
338 290
@@ -343,13 +295,6 b' class _mergestate_base(object):'
343 295 ):
344 296 yield f
345 297
346 def driverresolved(self):
347 """Obtain the paths of driver-resolved files."""
348
349 for f, entry in self._state.items():
350 if entry[0] == MERGE_RECORD_DRIVER_RESOLVED:
351 yield f
352
353 298 def extras(self, filename):
354 299 return self._stateextras[filename]
355 300
@@ -358,7 +303,10 b' class _mergestate_base(object):'
358 303 Returns whether the merge was completed and the return value of merge
359 304 obtained from filemerge._filemerge().
360 305 """
361 if self[dfile] in (MERGE_RECORD_RESOLVED, MERGE_RECORD_DRIVER_RESOLVED):
306 if self[dfile] in (
307 MERGE_RECORD_RESOLVED,
308 LEGACY_RECORD_DRIVER_RESOLVED,
309 ):
362 310 return True, 0
363 311 stateentry = self._state[dfile]
364 312 state, localkey, lfile, afile, anode, ofile, onode, flags = stateentry
@@ -490,24 +438,6 b' class _mergestate_base(object):'
490 438 actions[action].append((f, None, b"merge result"))
491 439 return actions
492 440
493 def queueremove(self, f):
494 """queues a file to be removed from the dirstate
495
496 Meant for use by custom merge drivers."""
497 self._results[f] = 0, ACTION_REMOVE
498
499 def queueadd(self, f):
500 """queues a file to be added to the dirstate
501
502 Meant for use by custom merge drivers."""
503 self._results[f] = 0, ACTION_ADD
504
505 def queueget(self, f):
506 """queues a file to be marked modified in the dirstate
507
508 Meant for use by custom merge drivers."""
509 self._results[f] = 0, ACTION_GET
510
511 441
512 442 class mergestate(_mergestate_base):
513 443
@@ -535,7 +465,6 b' class mergestate(_mergestate_base):'
535 465 This function process "record" entry produced by the de-serialization
536 466 of on disk file.
537 467 """
538 self._mdstate = MERGE_DRIVER_STATE_SUCCESS
539 468 unsupported = set()
540 469 records = self._readrecords()
541 470 for rtype, record in records:
@@ -543,24 +472,13 b' class mergestate(_mergestate_base):'
543 472 self._local = bin(record)
544 473 elif rtype == RECORD_OTHER:
545 474 self._other = bin(record)
546 elif rtype == RECORD_MERGE_DRIVER_STATE:
547 bits = record.split(b'\0', 1)
548 mdstate = bits[1]
549 if len(mdstate) != 1 or mdstate not in (
550 MERGE_DRIVER_STATE_UNMARKED,
551 MERGE_DRIVER_STATE_MARKED,
552 MERGE_DRIVER_STATE_SUCCESS,
553 ):
554 # the merge driver should be idempotent, so just rerun it
555 mdstate = MERGE_DRIVER_STATE_UNMARKED
556
557 self._readmergedriver = bits[0]
558 self._mdstate = mdstate
475 elif rtype == LEGACY_MERGE_DRIVER_STATE:
476 pass
559 477 elif rtype in (
560 478 RECORD_MERGED,
561 479 RECORD_CHANGEDELETE_CONFLICT,
562 480 RECORD_PATH_CONFLICT,
563 RECORD_MERGE_DRIVER_MERGE,
481 LEGACY_MERGE_DRIVER_MERGE,
564 482 LEGACY_RECORD_RESOLVED_OTHER,
565 483 ):
566 484 bits = record.split(b'\0')
@@ -710,25 +628,13 b' class mergestate(_mergestate_base):'
710 628 records = []
711 629 records.append((RECORD_LOCAL, hex(self._local)))
712 630 records.append((RECORD_OTHER, hex(self._other)))
713 if self.mergedriver:
714 records.append(
715 (
716 RECORD_MERGE_DRIVER_STATE,
717 b'\0'.join([self.mergedriver, self._mdstate]),
718 )
719 )
720 631 # Write out state items. In all cases, the value of the state map entry
721 632 # is written as the contents of the record. The record type depends on
722 633 # the type of state that is stored, and capital-letter records are used
723 634 # to prevent older versions of Mercurial that do not support the feature
724 635 # from loading them.
725 636 for filename, v in pycompat.iteritems(self._state):
726 if v[0] == MERGE_RECORD_DRIVER_RESOLVED:
727 # Driver-resolved merge. These are stored in 'D' records.
728 records.append(
729 (RECORD_MERGE_DRIVER_MERGE, b'\0'.join([filename] + v))
730 )
731 elif v[0] in (
637 if v[0] in (
732 638 MERGE_RECORD_UNRESOLVED_PATH,
733 639 MERGE_RECORD_RESOLVED_PATH,
734 640 ):
@@ -814,17 +720,6 b' class memmergestate(_mergestate_base):'
814 720 def _restore_backup(self, fctx, localkey, flags):
815 721 fctx.write(self._backups[localkey], flags)
816 722
817 @util.propertycache
818 def mergedriver(self):
819 configmergedriver = self._repo.ui.config(
820 b'experimental', b'mergedriver'
821 )
822 if configmergedriver:
823 raise error.InMemoryMergeConflictsError(
824 b"in-memory merge does not support mergedriver"
825 )
826 return None
827
828 723
829 724 def recordupdates(repo, actions, branchmerge, getfiledata):
830 725 """record merge actions to the dirstate"""
@@ -17,8 +17,3 b' def checkunresolved(ms):'
17 17 raise error.Abort(
18 18 _(b"unresolved merge conflicts (see 'hg help resolve')")
19 19 )
20 if ms.mdstate() != b's' or list(ms.driverresolved()):
21 raise error.Abort(
22 _(b'driver-resolved merge conflicts'),
23 hint=_(b'run "hg resolve --all" to resolve'),
24 )
@@ -87,64 +87,6 b' cleanup'
87 87 $ cd ..
88 88 $ rmdir nested
89 89
90 don't allow marking or unmarking driver-resolved files
91
92 $ cat > $TESTTMP/markdriver.py << EOF
93 > '''mark and unmark files as driver-resolved'''
94 > from mercurial import (
95 > mergestate,
96 > pycompat,
97 > registrar,
98 > scmutil,
99 > )
100 > cmdtable = {}
101 > command = registrar.command(cmdtable)
102 > @command(b'markdriver',
103 > [(b'u', b'unmark', None, b'')],
104 > b'FILE...')
105 > def markdriver(ui, repo, *pats, **opts):
106 > wlock = repo.wlock()
107 > opts = pycompat.byteskwargs(opts)
108 > try:
109 > ms = mergestate.mergestate.read(repo)
110 > m = scmutil.match(repo[None], pats, opts)
111 > for f in ms:
112 > if not m(f):
113 > continue
114 > if not opts[b'unmark']:
115 > ms.mark(f, b'd')
116 > else:
117 > ms.mark(f, b'u')
118 > ms.commit()
119 > finally:
120 > wlock.release()
121 > EOF
122 $ hg --config extensions.markdriver=$TESTTMP/markdriver.py markdriver file1
123 $ hg resolve --list
124 D file1
125 U file2
126 $ hg resolve --mark file1
127 not marking file1 as it is driver-resolved
128 this should not print out file1
129 $ hg resolve --mark --all
130 (no more unresolved files -- run "hg resolve --all" to conclude)
131 $ hg resolve --mark 'glob:file*'
132 (no more unresolved files -- run "hg resolve --all" to conclude)
133 $ hg resolve --list
134 D file1
135 R file2
136 $ hg resolve --unmark file1
137 not unmarking file1 as it is driver-resolved
138 (no more unresolved files -- run "hg resolve --all" to conclude)
139 $ hg resolve --unmark --all
140 $ hg resolve --list
141 D file1
142 U file2
143 $ hg --config extensions.markdriver=$TESTTMP/markdriver.py markdriver --unmark file1
144 $ hg resolve --list
145 U file1
146 U file2
147
148 90 resolve the failure
149 91
150 92 $ echo resolved > file1
General Comments 0
You need to be logged in to leave comments. Login now