Show More
@@ -5986,10 +5986,6 b' def resolve(ui, repo, *pats, **opts):' | |||||
5986 | b'resolve.resolved', |
|
5986 | b'resolve.resolved', | |
5987 | b'R', |
|
5987 | b'R', | |
5988 | ), |
|
5988 | ), | |
5989 | mergestatemod.MERGE_RECORD_DRIVER_RESOLVED: ( |
|
|||
5990 | b'resolve.driverresolved', |
|
|||
5991 | b'D', |
|
|||
5992 | ), |
|
|||
5993 | } |
|
5989 | } | |
5994 |
|
5990 | |||
5995 | for f in ms: |
|
5991 | for f in ms: | |
@@ -6014,21 +6010,9 b' def resolve(ui, repo, *pats, **opts):' | |||||
6014 | ) |
|
6010 | ) | |
6015 |
|
6011 | |||
6016 | wctx = repo[None] |
|
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 | m = scmutil.match(wctx, pats, opts) |
|
6013 | m = scmutil.match(wctx, pats, opts) | |
6029 | ret = 0 |
|
6014 | ret = 0 | |
6030 | didwork = False |
|
6015 | didwork = False | |
6031 | runconclude = False |
|
|||
6032 |
|
6016 | |||
6033 | tocomplete = [] |
|
6017 | tocomplete = [] | |
6034 | hasconflictmarkers = [] |
|
6018 | hasconflictmarkers = [] | |
@@ -6043,26 +6027,6 b' def resolve(ui, repo, *pats, **opts):' | |||||
6043 |
|
6027 | |||
6044 | didwork = True |
|
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 | # path conflicts must be resolved manually |
|
6030 | # path conflicts must be resolved manually | |
6067 | if ms[f] in ( |
|
6031 | if ms[f] in ( | |
6068 | mergestatemod.MERGE_RECORD_UNRESOLVED_PATH, |
|
6032 | mergestatemod.MERGE_RECORD_UNRESOLVED_PATH, | |
@@ -6184,32 +6148,11 b' def resolve(ui, repo, *pats, **opts):' | |||||
6184 | ui.warn(_(b"arguments do not match paths that need resolving\n")) |
|
6148 | ui.warn(_(b"arguments do not match paths that need resolving\n")) | |
6185 | if hint: |
|
6149 | if hint: | |
6186 | ui.warn(hint) |
|
6150 | ui.warn(hint) | |
6187 | elif ms.mergedriver and ms.mdstate() != b's': |
|
6151 | ||
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 |
|
|||
6201 | unresolvedf = list(ms.unresolved()) |
|
6152 | unresolvedf = list(ms.unresolved()) | |
6202 | driverresolvedf = list(ms.driverresolved()) |
|
6153 | if not unresolvedf: | |
6203 | if not unresolvedf and not driverresolvedf: |
|
|||
6204 | ui.status(_(b'(no more unresolved files)\n')) |
|
6154 | ui.status(_(b'(no more unresolved files)\n')) | |
6205 | cmdutil.checkafterresolved(repo) |
|
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 | return ret |
|
6157 | return ret | |
6215 |
|
6158 |
@@ -635,9 +635,6 b' coreconfigitem(' | |||||
635 | coreconfigitem( |
|
635 | coreconfigitem( | |
636 | b'experimental', b'httppostargs', default=False, |
|
636 | b'experimental', b'httppostargs', default=False, | |
637 | ) |
|
637 | ) | |
638 | coreconfigitem( |
|
|||
639 | b'experimental', b'mergedriver', default=None, |
|
|||
640 | ) |
|
|||
641 | coreconfigitem(b'experimental', b'nointerrupt', default=False) |
|
638 | coreconfigitem(b'experimental', b'nointerrupt', default=False) | |
642 | coreconfigitem(b'experimental', b'nointerrupt-interactiveonly', default=True) |
|
639 | coreconfigitem(b'experimental', b'nointerrupt-interactiveonly', default=True) | |
643 |
|
640 |
@@ -37,30 +37,18 b' Currently known records:' | |||||
37 | | * O: the node of the "other" part of the merge (hexified version) |
|
37 | | * O: the node of the "other" part of the merge (hexified version) | |
38 | | * F: a file to be merged entry |
|
38 | | * F: a file to be merged entry | |
39 | | * C: a change/delete or delete/change conflict |
|
39 | | * C: a change/delete or delete/change conflict | |
40 | | * D: a file that the external merge driver will merge internally |
|
|||
41 | | (experimental) |
|
|||
42 | | * P: a path conflict (file vs directory) |
|
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 | | * f: a (filename, dictionary) tuple of optional values for a given file |
|
41 | | * f: a (filename, dictionary) tuple of optional values for a given file | |
46 | | * X: unsupported mandatory record type (used in tests) |
|
42 | | * X: unsupported mandatory record type (used in tests) | |
47 | | * x: unsupported advisory record type (used in tests) |
|
43 | | * x: unsupported advisory record type (used in tests) | |
48 | | * l: the labels for the parts of the merge. |
|
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 | Merge record states (indexed by filename): |
|
46 | Merge record states (indexed by filename): | |
58 |
|
47 | |||
59 | | * u: unresolved conflict |
|
48 | | * u: unresolved conflict | |
60 | | * r: resolved conflict |
|
49 | | * r: resolved conflict | |
61 | | * pu: unresolved path conflict (file conflicts with directory) |
|
50 | | * pu: unresolved path conflict (file conflicts with directory) | |
62 | | * pr: resolved path conflict |
|
51 | | * pr: resolved path conflict | |
63 | | * d: driver-resolved conflict |
|
|||
64 |
|
52 | |||
65 | The resolve command transitions between 'u' and 'r' for conflicts and |
|
53 | The resolve command transitions between 'u' and 'r' for conflicts and | |
66 | 'pu' and 'pr' for path conflicts. |
|
54 | 'pu' and 'pr' for path conflicts. |
@@ -355,20 +355,6 b' def _checkcollision(repo, wmf, mresult):' | |||||
355 | lastfull = f |
|
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 | def _filesindirs(repo, manifest, dirs): |
|
358 | def _filesindirs(repo, manifest, dirs): | |
373 | """ |
|
359 | """ | |
374 | Generator that yields pairs of all the files in the manifest that are found |
|
360 | Generator that yields pairs of all the files in the manifest that are found | |
@@ -1605,27 +1591,6 b' def applyupdates(' | |||||
1605 | mergestatemod.ACTION_DIR_RENAME_MOVE_LOCAL, |
|
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 | try: |
|
1595 | try: | |
1631 | # premerge |
|
1596 | # premerge | |
@@ -1655,18 +1620,6 b' def applyupdates(' | |||||
1655 |
|
1620 | |||
1656 | unresolved = ms.unresolvedcount() |
|
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 | msupdated, msmerged, msremoved = ms.counts() |
|
1623 | msupdated, msmerged, msremoved = ms.counts() | |
1671 | updated += msupdated |
|
1624 | updated += msupdated | |
1672 | merged += msmerged |
|
1625 | merged += msmerged | |
@@ -1674,9 +1627,6 b' def applyupdates(' | |||||
1674 |
|
1627 | |||
1675 | extraactions = ms.actions() |
|
1628 | extraactions = ms.actions() | |
1676 | if extraactions: |
|
1629 | if extraactions: | |
1677 | mfiles = { |
|
|||
1678 | a[0] for a in mresult.getactions((mergestatemod.ACTION_MERGE,)) |
|
|||
1679 | } |
|
|||
1680 | for k, acts in pycompat.iteritems(extraactions): |
|
1630 | for k, acts in pycompat.iteritems(extraactions): | |
1681 | for a in acts: |
|
1631 | for a in acts: | |
1682 | mresult.addfile(a[0], k, *a[1:]) |
|
1632 | mresult.addfile(a[0], k, *a[1:]) | |
@@ -1684,27 +1634,6 b' def applyupdates(' | |||||
1684 | # no filedata until mergestate is updated to provide it |
|
1634 | # no filedata until mergestate is updated to provide it | |
1685 | for a in acts: |
|
1635 | for a in acts: | |
1686 | getfiledata[a[0]] = None |
|
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 | progress.complete() |
|
1638 | progress.complete() | |
1710 | assert len(getfiledata) == ( |
|
1639 | assert len(getfiledata) == ( |
@@ -48,8 +48,6 b" RECORD_LOCAL = b'L'" | |||||
48 | RECORD_OTHER = b'O' |
|
48 | RECORD_OTHER = b'O' | |
49 | # record merge labels |
|
49 | # record merge labels | |
50 | RECORD_LABELS = b'l' |
|
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 | # record extra information about files, with one entry containing info about one |
|
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 | RECORD_MERGED = b'F' |
|
64 | RECORD_MERGED = b'F' | |
67 | RECORD_CHANGEDELETE_CONFLICT = b'C' |
|
65 | RECORD_CHANGEDELETE_CONFLICT = b'C' | |
68 | RECORD_MERGE_DRIVER_MERGE = b'D' |
|
|||
69 | # the path was dir on one side of merge and file on another |
|
66 | # the path was dir on one side of merge and file on another | |
70 | RECORD_PATH_CONFLICT = b'P' |
|
67 | RECORD_PATH_CONFLICT = b'P' | |
71 |
|
68 | |||
@@ -77,7 +74,6 b" MERGE_RECORD_UNRESOLVED = b'u'" | |||||
77 | MERGE_RECORD_RESOLVED = b'r' |
|
74 | MERGE_RECORD_RESOLVED = b'r' | |
78 | MERGE_RECORD_UNRESOLVED_PATH = b'pu' |
|
75 | MERGE_RECORD_UNRESOLVED_PATH = b'pu' | |
79 | MERGE_RECORD_RESOLVED_PATH = b'pr' |
|
76 | MERGE_RECORD_RESOLVED_PATH = b'pr' | |
80 | MERGE_RECORD_DRIVER_RESOLVED = b'd' |
|
|||
81 | # represents that the file was automatically merged in favor |
|
77 | # represents that the file was automatically merged in favor | |
82 | # of other version. This info is used on commit. |
|
78 | # of other version. This info is used on commit. | |
83 | # This is now deprecated and commit related information is now |
|
79 | # This is now deprecated and commit related information is now | |
@@ -91,18 +87,16 b" MERGE_RECORD_MERGED_OTHER = b'o'" | |||||
91 | RECORD_OVERRIDE = b't' |
|
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 | # legacy records which are no longer used but kept to prevent breaking BC |
|
90 | # legacy records which are no longer used but kept to prevent breaking BC | |
103 | ##### |
|
91 | ##### | |
104 | # This record was release in 5.4 and usage was removed in 5.5 |
|
92 | # This record was release in 5.4 and usage was removed in 5.5 | |
105 | LEGACY_RECORD_RESOLVED_OTHER = b'R' |
|
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 | ACTION_FORGET = b'f' |
|
102 | ACTION_FORGET = b'f' | |
@@ -147,26 +141,15 b' class _mergestate_base(object):' | |||||
147 | O: the node of the "other" part of the merge (hexified version) |
|
141 | O: the node of the "other" part of the merge (hexified version) | |
148 | F: a file to be merged entry |
|
142 | F: a file to be merged entry | |
149 | C: a change/delete or delete/change conflict |
|
143 | C: a change/delete or delete/change conflict | |
150 | D: a file that the external merge driver will merge internally |
|
|||
151 | (experimental) |
|
|||
152 | P: a path conflict (file vs directory) |
|
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 | f: a (filename, dictionary) tuple of optional values for a given file |
|
145 | f: a (filename, dictionary) tuple of optional values for a given file | |
156 | l: the labels for the parts of the merge. |
|
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 | Merge record states (stored in self._state, indexed by filename): |
|
148 | Merge record states (stored in self._state, indexed by filename): | |
165 | u: unresolved conflict |
|
149 | u: unresolved conflict | |
166 | r: resolved conflict |
|
150 | r: resolved conflict | |
167 | pu: unresolved path conflict (file conflicts with directory) |
|
151 | pu: unresolved path conflict (file conflicts with directory) | |
168 | pr: resolved path conflict |
|
152 | pr: resolved path conflict | |
169 | d: driver-resolved conflict |
|
|||
170 |
|
153 | |||
171 | The resolve command transitions between 'u' and 'r' for conflicts and |
|
154 | The resolve command transitions between 'u' and 'r' for conflicts and | |
172 | 'pu' and 'pr' for path conflicts. |
|
155 | 'pu' and 'pr' for path conflicts. | |
@@ -182,8 +165,6 b' class _mergestate_base(object):' | |||||
182 | self._local = None |
|
165 | self._local = None | |
183 | self._other = None |
|
166 | self._other = None | |
184 | self._labels = None |
|
167 | self._labels = None | |
185 | self._readmergedriver = None |
|
|||
186 | self._mdstate = MERGE_DRIVER_STATE_UNMARKED |
|
|||
187 | # contains a mapping of form: |
|
168 | # contains a mapping of form: | |
188 | # {filename : (merge_return_value, action_to_be_performed} |
|
169 | # {filename : (merge_return_value, action_to_be_performed} | |
189 | # these are results of re-running merge process |
|
170 | # these are results of re-running merge process | |
@@ -199,32 +180,6 b' class _mergestate_base(object):' | |||||
199 | self._local = node |
|
180 | self._local = node | |
200 | self._other = other |
|
181 | self._other = other | |
201 | self._labels = labels |
|
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 | @util.propertycache |
|
184 | @util.propertycache | |
230 | def local(self): |
|
185 | def local(self): | |
@@ -330,9 +285,6 b' class _mergestate_base(object):' | |||||
330 | self._state[dfile][0] = state |
|
285 | self._state[dfile][0] = state | |
331 | self._dirty = True |
|
286 | self._dirty = True | |
332 |
|
287 | |||
333 | def mdstate(self): |
|
|||
334 | return self._mdstate |
|
|||
335 |
|
||||
336 | def unresolved(self): |
|
288 | def unresolved(self): | |
337 | """Obtain the paths of unresolved files.""" |
|
289 | """Obtain the paths of unresolved files.""" | |
338 |
|
290 | |||
@@ -343,13 +295,6 b' class _mergestate_base(object):' | |||||
343 | ): |
|
295 | ): | |
344 | yield f |
|
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 | def extras(self, filename): |
|
298 | def extras(self, filename): | |
354 | return self._stateextras[filename] |
|
299 | return self._stateextras[filename] | |
355 |
|
300 | |||
@@ -358,7 +303,10 b' class _mergestate_base(object):' | |||||
358 | Returns whether the merge was completed and the return value of merge |
|
303 | Returns whether the merge was completed and the return value of merge | |
359 | obtained from filemerge._filemerge(). |
|
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 | return True, 0 |
|
310 | return True, 0 | |
363 | stateentry = self._state[dfile] |
|
311 | stateentry = self._state[dfile] | |
364 | state, localkey, lfile, afile, anode, ofile, onode, flags = stateentry |
|
312 | state, localkey, lfile, afile, anode, ofile, onode, flags = stateentry | |
@@ -490,24 +438,6 b' class _mergestate_base(object):' | |||||
490 | actions[action].append((f, None, b"merge result")) |
|
438 | actions[action].append((f, None, b"merge result")) | |
491 | return actions |
|
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 | class mergestate(_mergestate_base): |
|
442 | class mergestate(_mergestate_base): | |
513 |
|
443 | |||
@@ -535,7 +465,6 b' class mergestate(_mergestate_base):' | |||||
535 | This function process "record" entry produced by the de-serialization |
|
465 | This function process "record" entry produced by the de-serialization | |
536 | of on disk file. |
|
466 | of on disk file. | |
537 | """ |
|
467 | """ | |
538 | self._mdstate = MERGE_DRIVER_STATE_SUCCESS |
|
|||
539 | unsupported = set() |
|
468 | unsupported = set() | |
540 | records = self._readrecords() |
|
469 | records = self._readrecords() | |
541 | for rtype, record in records: |
|
470 | for rtype, record in records: | |
@@ -543,24 +472,13 b' class mergestate(_mergestate_base):' | |||||
543 | self._local = bin(record) |
|
472 | self._local = bin(record) | |
544 | elif rtype == RECORD_OTHER: |
|
473 | elif rtype == RECORD_OTHER: | |
545 | self._other = bin(record) |
|
474 | self._other = bin(record) | |
546 |
elif rtype == |
|
475 | elif rtype == LEGACY_MERGE_DRIVER_STATE: | |
547 | bits = record.split(b'\0', 1) |
|
476 | pass | |
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 |
|
|||
559 | elif rtype in ( |
|
477 | elif rtype in ( | |
560 | RECORD_MERGED, |
|
478 | RECORD_MERGED, | |
561 | RECORD_CHANGEDELETE_CONFLICT, |
|
479 | RECORD_CHANGEDELETE_CONFLICT, | |
562 | RECORD_PATH_CONFLICT, |
|
480 | RECORD_PATH_CONFLICT, | |
563 |
|
|
481 | LEGACY_MERGE_DRIVER_MERGE, | |
564 | LEGACY_RECORD_RESOLVED_OTHER, |
|
482 | LEGACY_RECORD_RESOLVED_OTHER, | |
565 | ): |
|
483 | ): | |
566 | bits = record.split(b'\0') |
|
484 | bits = record.split(b'\0') | |
@@ -710,25 +628,13 b' class mergestate(_mergestate_base):' | |||||
710 | records = [] |
|
628 | records = [] | |
711 | records.append((RECORD_LOCAL, hex(self._local))) |
|
629 | records.append((RECORD_LOCAL, hex(self._local))) | |
712 | records.append((RECORD_OTHER, hex(self._other))) |
|
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 | # Write out state items. In all cases, the value of the state map entry |
|
631 | # Write out state items. In all cases, the value of the state map entry | |
721 | # is written as the contents of the record. The record type depends on |
|
632 | # is written as the contents of the record. The record type depends on | |
722 | # the type of state that is stored, and capital-letter records are used |
|
633 | # the type of state that is stored, and capital-letter records are used | |
723 | # to prevent older versions of Mercurial that do not support the feature |
|
634 | # to prevent older versions of Mercurial that do not support the feature | |
724 | # from loading them. |
|
635 | # from loading them. | |
725 | for filename, v in pycompat.iteritems(self._state): |
|
636 | for filename, v in pycompat.iteritems(self._state): | |
726 | if v[0] == MERGE_RECORD_DRIVER_RESOLVED: |
|
637 | if v[0] in ( | |
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 ( |
|
|||
732 | MERGE_RECORD_UNRESOLVED_PATH, |
|
638 | MERGE_RECORD_UNRESOLVED_PATH, | |
733 | MERGE_RECORD_RESOLVED_PATH, |
|
639 | MERGE_RECORD_RESOLVED_PATH, | |
734 | ): |
|
640 | ): | |
@@ -814,17 +720,6 b' class memmergestate(_mergestate_base):' | |||||
814 | def _restore_backup(self, fctx, localkey, flags): |
|
720 | def _restore_backup(self, fctx, localkey, flags): | |
815 | fctx.write(self._backups[localkey], flags) |
|
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 | def recordupdates(repo, actions, branchmerge, getfiledata): |
|
724 | def recordupdates(repo, actions, branchmerge, getfiledata): | |
830 | """record merge actions to the dirstate""" |
|
725 | """record merge actions to the dirstate""" |
@@ -17,8 +17,3 b' def checkunresolved(ms):' | |||||
17 | raise error.Abort( |
|
17 | raise error.Abort( | |
18 | _(b"unresolved merge conflicts (see 'hg help resolve')") |
|
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 | $ cd .. |
|
87 | $ cd .. | |
88 | $ rmdir nested |
|
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