##// END OF EJS Templates
destutil: remove duplicate check and leave it to merge.update()...
Martin von Zweigbergk -
r30961:330fbd51 default
parent child Browse files
Show More
@@ -1,433 +1,407 b''
1 1 # destutil.py - Mercurial utility function for command destination
2 2 #
3 3 # Copyright Matt Mackall <mpm@selenic.com> and other
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 from __future__ import absolute_import
9 9
10 10 from .i18n import _
11 11 from . import (
12 12 bookmarks,
13 13 error,
14 14 obsolete,
15 15 )
16 16
17 def _destupdatevalidate(repo, rev, clean, check):
18 """validate that the destination comply to various rules
19
20 This exists as its own function to help wrapping from extensions."""
21 wc = repo[None]
22 p1 = wc.p1()
23 if not clean:
24 # Check that the update is linear.
25 #
26 # Mercurial do not allow update-merge for non linear pattern
27 # (that would be technically possible but was considered too confusing
28 # for user a long time ago)
29 #
30 # See mercurial.merge.update for details
31 if p1.rev() not in repo.changelog.ancestors([rev], inclusive=True):
32 dirty = wc.dirty(missing=True)
33 foreground = obsolete.foreground(repo, [p1.node()])
34 if not repo[rev].node() in foreground:
35 if dirty:
36 msg = _("uncommitted changes")
37 hint = _("commit and merge, or update --clean to"
38 " discard changes")
39 raise error.UpdateAbort(msg, hint=hint)
40
41 17 def _destupdateobs(repo, clean, check):
42 18 """decide of an update destination from obsolescence markers"""
43 19 node = None
44 20 wc = repo[None]
45 21 p1 = wc.p1()
46 22 movemark = None
47 23
48 24 if p1.obsolete() and not p1.children():
49 25 # allow updating to successors
50 26 successors = obsolete.successorssets(repo, p1.node())
51 27
52 28 # behavior of certain cases is as follows,
53 29 #
54 30 # divergent changesets: update to highest rev, similar to what
55 31 # is currently done when there are more than one head
56 32 # (i.e. 'tip')
57 33 #
58 34 # replaced changesets: same as divergent except we know there
59 35 # is no conflict
60 36 #
61 37 # pruned changeset: no update is done; though, we could
62 38 # consider updating to the first non-obsolete parent,
63 39 # similar to what is current done for 'hg prune'
64 40
65 41 if successors:
66 42 # flatten the list here handles both divergent (len > 1)
67 43 # and the usual case (len = 1)
68 44 successors = [n for sub in successors for n in sub]
69 45
70 46 # get the max revision for the given successors set,
71 47 # i.e. the 'tip' of a set
72 48 node = repo.revs('max(%ln)', successors).first()
73 49 if bookmarks.isactivewdirparent(repo):
74 50 movemark = repo['.'].node()
75 51 return node, movemark, None
76 52
77 53 def _destupdatebook(repo, clean, check):
78 54 """decide on an update destination from active bookmark"""
79 55 # we also move the active bookmark, if any
80 56 activemark = None
81 57 node, movemark = bookmarks.calculateupdate(repo.ui, repo, None)
82 58 if node is not None:
83 59 activemark = node
84 60 return node, movemark, activemark
85 61
86 62 def _destupdatebranch(repo, clean, check):
87 63 """decide on an update destination from current branch
88 64
89 65 This ignores closed branch heads.
90 66 """
91 67 wc = repo[None]
92 68 movemark = node = None
93 69 currentbranch = wc.branch()
94 70
95 71 if clean:
96 72 currentbranch = repo['.'].branch()
97 73
98 74 if currentbranch in repo.branchmap():
99 75 heads = repo.branchheads(currentbranch)
100 76 if heads:
101 77 node = repo.revs('max(.::(%ln))', heads).first()
102 78 if bookmarks.isactivewdirparent(repo):
103 79 movemark = repo['.'].node()
104 80 elif currentbranch == 'default' and not wc.p1():
105 81 # "null" parent belongs to "default" branch, but it doesn't exist, so
106 82 # update to the tipmost non-closed branch head
107 83 node = repo.revs('max(head() and not closed())').first()
108 84 else:
109 85 node = repo['.'].node()
110 86 return node, movemark, None
111 87
112 88 def _destupdatebranchfallback(repo, clean, check):
113 89 """decide on an update destination from closed heads in current branch"""
114 90 wc = repo[None]
115 91 currentbranch = wc.branch()
116 92 movemark = None
117 93 if currentbranch in repo.branchmap():
118 94 # here, all descendant branch heads are closed
119 95 heads = repo.branchheads(currentbranch, closed=True)
120 96 assert heads, "any branch has at least one head"
121 97 node = repo.revs('max(.::(%ln))', heads).first()
122 98 assert node is not None, ("any revision has at least "
123 99 "one descendant branch head")
124 100 if bookmarks.isactivewdirparent(repo):
125 101 movemark = repo['.'].node()
126 102 else:
127 103 # here, no "default" branch, and all branches are closed
128 104 node = repo.lookup('tip')
129 105 assert node is not None, "'tip' exists even in empty repository"
130 106 return node, movemark, None
131 107
132 108 # order in which each step should be evaluated
133 109 # steps are run until one finds a destination
134 110 destupdatesteps = ['evolution', 'bookmark', 'branch', 'branchfallback']
135 111 # mapping to ease extension overriding steps.
136 112 destupdatestepmap = {'evolution': _destupdateobs,
137 113 'bookmark': _destupdatebook,
138 114 'branch': _destupdatebranch,
139 115 'branchfallback': _destupdatebranchfallback,
140 116 }
141 117
142 118 def destupdate(repo, clean=False, check=False):
143 119 """destination for bare update operation
144 120
145 121 return (rev, movemark, activemark)
146 122
147 123 - rev: the revision to update to,
148 124 - movemark: node to move the active bookmark from
149 125 (cf bookmark.calculate update),
150 126 - activemark: a bookmark to activate at the end of the update.
151 127 """
152 128 node = movemark = activemark = None
153 129
154 130 for step in destupdatesteps:
155 131 node, movemark, activemark = destupdatestepmap[step](repo, clean, check)
156 132 if node is not None:
157 133 break
158 134 rev = repo[node].rev()
159 135
160 _destupdatevalidate(repo, rev, clean, check)
161
162 136 return rev, movemark, activemark
163 137
164 138 msgdestmerge = {
165 139 # too many matching divergent bookmark
166 140 'toomanybookmarks':
167 141 {'merge':
168 142 (_("multiple matching bookmarks to merge -"
169 143 " please merge with an explicit rev or bookmark"),
170 144 _("run 'hg heads' to see all heads")),
171 145 'rebase':
172 146 (_("multiple matching bookmarks to rebase -"
173 147 " please rebase to an explicit rev or bookmark"),
174 148 _("run 'hg heads' to see all heads")),
175 149 },
176 150 # no other matching divergent bookmark
177 151 'nootherbookmarks':
178 152 {'merge':
179 153 (_("no matching bookmark to merge - "
180 154 "please merge with an explicit rev or bookmark"),
181 155 _("run 'hg heads' to see all heads")),
182 156 'rebase':
183 157 (_("no matching bookmark to rebase - "
184 158 "please rebase to an explicit rev or bookmark"),
185 159 _("run 'hg heads' to see all heads")),
186 160 },
187 161 # branch have too many unbookmarked heads, no obvious destination
188 162 'toomanyheads':
189 163 {'merge':
190 164 (_("branch '%s' has %d heads - please merge with an explicit rev"),
191 165 _("run 'hg heads .' to see heads")),
192 166 'rebase':
193 167 (_("branch '%s' has %d heads - please rebase to an explicit rev"),
194 168 _("run 'hg heads .' to see heads")),
195 169 },
196 170 # branch have no other unbookmarked heads
197 171 'bookmarkedheads':
198 172 {'merge':
199 173 (_("heads are bookmarked - please merge with an explicit rev"),
200 174 _("run 'hg heads' to see all heads")),
201 175 'rebase':
202 176 (_("heads are bookmarked - please rebase to an explicit rev"),
203 177 _("run 'hg heads' to see all heads")),
204 178 },
205 179 # branch have just a single heads, but there is other branches
206 180 'nootherbranchheads':
207 181 {'merge':
208 182 (_("branch '%s' has one head - please merge with an explicit rev"),
209 183 _("run 'hg heads' to see all heads")),
210 184 'rebase':
211 185 (_("branch '%s' has one head - please rebase to an explicit rev"),
212 186 _("run 'hg heads' to see all heads")),
213 187 },
214 188 # repository have a single head
215 189 'nootherheads':
216 190 {'merge':
217 191 (_('nothing to merge'),
218 192 None),
219 193 'rebase':
220 194 (_('nothing to rebase'),
221 195 None),
222 196 },
223 197 # repository have a single head and we are not on it
224 198 'nootherheadsbehind':
225 199 {'merge':
226 200 (_('nothing to merge'),
227 201 _("use 'hg update' instead")),
228 202 'rebase':
229 203 (_('nothing to rebase'),
230 204 _("use 'hg update' instead")),
231 205 },
232 206 # We are not on a head
233 207 'notatheads':
234 208 {'merge':
235 209 (_('working directory not at a head revision'),
236 210 _("use 'hg update' or merge with an explicit revision")),
237 211 'rebase':
238 212 (_('working directory not at a head revision'),
239 213 _("use 'hg update' or rebase to an explicit revision"))
240 214 },
241 215 'emptysourceset':
242 216 {'merge':
243 217 (_('source set is empty'),
244 218 None),
245 219 'rebase':
246 220 (_('source set is empty'),
247 221 None),
248 222 },
249 223 'multiplebranchessourceset':
250 224 {'merge':
251 225 (_('source set is rooted in multiple branches'),
252 226 None),
253 227 'rebase':
254 228 (_('rebaseset is rooted in multiple named branches'),
255 229 _('specify an explicit destination with --dest')),
256 230 },
257 231 }
258 232
259 233 def _destmergebook(repo, action='merge', sourceset=None, destspace=None):
260 234 """find merge destination in the active bookmark case"""
261 235 node = None
262 236 bmheads = repo.bookmarkheads(repo._activebookmark)
263 237 curhead = repo[repo._activebookmark].node()
264 238 if len(bmheads) == 2:
265 239 if curhead == bmheads[0]:
266 240 node = bmheads[1]
267 241 else:
268 242 node = bmheads[0]
269 243 elif len(bmheads) > 2:
270 244 msg, hint = msgdestmerge['toomanybookmarks'][action]
271 245 raise error.ManyMergeDestAbort(msg, hint=hint)
272 246 elif len(bmheads) <= 1:
273 247 msg, hint = msgdestmerge['nootherbookmarks'][action]
274 248 raise error.NoMergeDestAbort(msg, hint=hint)
275 249 assert node is not None
276 250 return node
277 251
278 252 def _destmergebranch(repo, action='merge', sourceset=None, onheadcheck=True,
279 253 destspace=None):
280 254 """find merge destination based on branch heads"""
281 255 node = None
282 256
283 257 if sourceset is None:
284 258 sourceset = [repo[repo.dirstate.p1()].rev()]
285 259 branch = repo.dirstate.branch()
286 260 elif not sourceset:
287 261 msg, hint = msgdestmerge['emptysourceset'][action]
288 262 raise error.NoMergeDestAbort(msg, hint=hint)
289 263 else:
290 264 branch = None
291 265 for ctx in repo.set('roots(%ld::%ld)', sourceset, sourceset):
292 266 if branch is not None and ctx.branch() != branch:
293 267 msg, hint = msgdestmerge['multiplebranchessourceset'][action]
294 268 raise error.ManyMergeDestAbort(msg, hint=hint)
295 269 branch = ctx.branch()
296 270
297 271 bheads = repo.branchheads(branch)
298 272 onhead = repo.revs('%ld and %ln', sourceset, bheads)
299 273 if onheadcheck and not onhead:
300 274 # Case A: working copy if not on a head. (merge only)
301 275 #
302 276 # This is probably a user mistake We bailout pointing at 'hg update'
303 277 if len(repo.heads()) <= 1:
304 278 msg, hint = msgdestmerge['nootherheadsbehind'][action]
305 279 else:
306 280 msg, hint = msgdestmerge['notatheads'][action]
307 281 raise error.Abort(msg, hint=hint)
308 282 # remove heads descendants of source from the set
309 283 bheads = list(repo.revs('%ln - (%ld::)', bheads, sourceset))
310 284 # filters out bookmarked heads
311 285 nbhs = list(repo.revs('%ld - bookmark()', bheads))
312 286
313 287 if destspace is not None:
314 288 # restrict search space
315 289 # used in the 'hg pull --rebase' case, see issue 5214.
316 290 nbhs = list(repo.revs('%ld and %ld', destspace, nbhs))
317 291
318 292 if len(nbhs) > 1:
319 293 # Case B: There is more than 1 other anonymous heads
320 294 #
321 295 # This means that there will be more than 1 candidate. This is
322 296 # ambiguous. We abort asking the user to pick as explicit destination
323 297 # instead.
324 298 msg, hint = msgdestmerge['toomanyheads'][action]
325 299 msg %= (branch, len(bheads) + 1)
326 300 raise error.ManyMergeDestAbort(msg, hint=hint)
327 301 elif not nbhs:
328 302 # Case B: There is no other anonymous heads
329 303 #
330 304 # This means that there is no natural candidate to merge with.
331 305 # We abort, with various messages for various cases.
332 306 if bheads:
333 307 msg, hint = msgdestmerge['bookmarkedheads'][action]
334 308 elif len(repo.heads()) > 1:
335 309 msg, hint = msgdestmerge['nootherbranchheads'][action]
336 310 msg %= branch
337 311 elif not onhead:
338 312 # if 'onheadcheck == False' (rebase case),
339 313 # this was not caught in Case A.
340 314 msg, hint = msgdestmerge['nootherheadsbehind'][action]
341 315 else:
342 316 msg, hint = msgdestmerge['nootherheads'][action]
343 317 raise error.NoMergeDestAbort(msg, hint=hint)
344 318 else:
345 319 node = nbhs[0]
346 320 assert node is not None
347 321 return node
348 322
349 323 def destmerge(repo, action='merge', sourceset=None, onheadcheck=True,
350 324 destspace=None):
351 325 """return the default destination for a merge
352 326
353 327 (or raise exception about why it can't pick one)
354 328
355 329 :action: the action being performed, controls emitted error message
356 330 """
357 331 # destspace is here to work around issues with `hg pull --rebase` see
358 332 # issue5214 for details
359 333 if repo._activebookmark:
360 334 node = _destmergebook(repo, action=action, sourceset=sourceset,
361 335 destspace=destspace)
362 336 else:
363 337 node = _destmergebranch(repo, action=action, sourceset=sourceset,
364 338 onheadcheck=onheadcheck, destspace=destspace)
365 339 return repo[node].rev()
366 340
367 341 histeditdefaultrevset = 'reverse(only(.) and not public() and not ::merge())'
368 342
369 343 def desthistedit(ui, repo):
370 344 """Default base revision to edit for `hg histedit`."""
371 345 # Avoid cycle: scmutil -> revset -> destutil
372 346 from . import scmutil
373 347
374 348 default = ui.config('histedit', 'defaultrev', histeditdefaultrevset)
375 349 if default:
376 350 revs = scmutil.revrange(repo, [default])
377 351 if revs:
378 352 # The revset supplied by the user may not be in ascending order nor
379 353 # take the first revision. So do this manually.
380 354 revs.sort()
381 355 return revs.first()
382 356
383 357 return None
384 358
385 359 def _statusotherbook(ui, repo):
386 360 bmheads = repo.bookmarkheads(repo._activebookmark)
387 361 curhead = repo[repo._activebookmark].node()
388 362 if repo.revs('%n and parents()', curhead):
389 363 # we are on the active bookmark
390 364 bmheads = [b for b in bmheads if curhead != b]
391 365 if bmheads:
392 366 msg = _('%i other divergent bookmarks for "%s"\n')
393 367 ui.status(msg % (len(bmheads), repo._activebookmark))
394 368
395 369 def _statusotherbranchheads(ui, repo):
396 370 currentbranch = repo.dirstate.branch()
397 371 allheads = repo.branchheads(currentbranch, closed=True)
398 372 heads = repo.branchheads(currentbranch)
399 373 if repo.revs('%ln and parents()', allheads):
400 374 # we are on a head, even though it might be closed
401 375 #
402 376 # on closed otherheads
403 377 # ========= ==========
404 378 # o 0 all heads for current branch are closed
405 379 # N only descendant branch heads are closed
406 380 # x 0 there is only one non-closed branch head
407 381 # N there are some non-closed branch heads
408 382 # ========= ==========
409 383 otherheads = repo.revs('%ln - parents()', heads)
410 384 if repo['.'].closesbranch():
411 385 ui.warn(_('no open descendant heads on branch "%s", '
412 386 'updating to a closed head\n') %
413 387 (currentbranch))
414 388 if otherheads:
415 389 ui.warn(_("(committing will reopen the head, "
416 390 "use 'hg heads .' to see %i other heads)\n") %
417 391 (len(otherheads)))
418 392 else:
419 393 ui.warn(_('(committing will reopen branch "%s")\n') %
420 394 (currentbranch))
421 395 elif otherheads:
422 396 ui.status(_('%i other heads for branch "%s"\n') %
423 397 (len(otherheads), currentbranch))
424 398
425 399 def statusotherdests(ui, repo):
426 400 """Print message about other head"""
427 401 # XXX we should probably include a hint:
428 402 # - about what to do
429 403 # - how to see such heads
430 404 if repo._activebookmark:
431 405 _statusotherbook(ui, repo)
432 406 else:
433 407 _statusotherbranchheads(ui, repo)
@@ -1,1710 +1,1710 b''
1 1 # merge.py - directory-level update/merge handling for Mercurial
2 2 #
3 3 # Copyright 2006, 2007 Matt Mackall <mpm@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 from __future__ import absolute_import
9 9
10 10 import errno
11 11 import hashlib
12 12 import os
13 13 import shutil
14 14 import struct
15 15
16 16 from .i18n import _
17 17 from .node import (
18 18 addednodeid,
19 19 bin,
20 20 hex,
21 21 modifiednodeid,
22 22 nullhex,
23 23 nullid,
24 24 nullrev,
25 25 )
26 26 from . import (
27 27 copies,
28 28 error,
29 29 filemerge,
30 30 obsolete,
31 31 pycompat,
32 32 scmutil,
33 33 subrepo,
34 34 util,
35 35 worker,
36 36 )
37 37
38 38 _pack = struct.pack
39 39 _unpack = struct.unpack
40 40
41 41 def _droponode(data):
42 42 # used for compatibility for v1
43 43 bits = data.split('\0')
44 44 bits = bits[:-2] + bits[-1:]
45 45 return '\0'.join(bits)
46 46
47 47 class mergestate(object):
48 48 '''track 3-way merge state of individual files
49 49
50 50 The merge state is stored on disk when needed. Two files are used: one with
51 51 an old format (version 1), and one with a new format (version 2). Version 2
52 52 stores a superset of the data in version 1, including new kinds of records
53 53 in the future. For more about the new format, see the documentation for
54 54 `_readrecordsv2`.
55 55
56 56 Each record can contain arbitrary content, and has an associated type. This
57 57 `type` should be a letter. If `type` is uppercase, the record is mandatory:
58 58 versions of Mercurial that don't support it should abort. If `type` is
59 59 lowercase, the record can be safely ignored.
60 60
61 61 Currently known records:
62 62
63 63 L: the node of the "local" part of the merge (hexified version)
64 64 O: the node of the "other" part of the merge (hexified version)
65 65 F: a file to be merged entry
66 66 C: a change/delete or delete/change conflict
67 67 D: a file that the external merge driver will merge internally
68 68 (experimental)
69 69 m: the external merge driver defined for this merge plus its run state
70 70 (experimental)
71 71 f: a (filename, dictionary) tuple of optional values for a given file
72 72 X: unsupported mandatory record type (used in tests)
73 73 x: unsupported advisory record type (used in tests)
74 74 l: the labels for the parts of the merge.
75 75
76 76 Merge driver run states (experimental):
77 77 u: driver-resolved files unmarked -- needs to be run next time we're about
78 78 to resolve or commit
79 79 m: driver-resolved files marked -- only needs to be run before commit
80 80 s: success/skipped -- does not need to be run any more
81 81
82 82 '''
83 83 statepathv1 = 'merge/state'
84 84 statepathv2 = 'merge/state2'
85 85
86 86 @staticmethod
87 87 def clean(repo, node=None, other=None, labels=None):
88 88 """Initialize a brand new merge state, removing any existing state on
89 89 disk."""
90 90 ms = mergestate(repo)
91 91 ms.reset(node, other, labels)
92 92 return ms
93 93
94 94 @staticmethod
95 95 def read(repo):
96 96 """Initialize the merge state, reading it from disk."""
97 97 ms = mergestate(repo)
98 98 ms._read()
99 99 return ms
100 100
101 101 def __init__(self, repo):
102 102 """Initialize the merge state.
103 103
104 104 Do not use this directly! Instead call read() or clean()."""
105 105 self._repo = repo
106 106 self._dirty = False
107 107 self._labels = None
108 108
109 109 def reset(self, node=None, other=None, labels=None):
110 110 self._state = {}
111 111 self._stateextras = {}
112 112 self._local = None
113 113 self._other = None
114 114 self._labels = labels
115 115 for var in ('localctx', 'otherctx'):
116 116 if var in vars(self):
117 117 delattr(self, var)
118 118 if node:
119 119 self._local = node
120 120 self._other = other
121 121 self._readmergedriver = None
122 122 if self.mergedriver:
123 123 self._mdstate = 's'
124 124 else:
125 125 self._mdstate = 'u'
126 126 shutil.rmtree(self._repo.join('merge'), True)
127 127 self._results = {}
128 128 self._dirty = False
129 129
130 130 def _read(self):
131 131 """Analyse each record content to restore a serialized state from disk
132 132
133 133 This function process "record" entry produced by the de-serialization
134 134 of on disk file.
135 135 """
136 136 self._state = {}
137 137 self._stateextras = {}
138 138 self._local = None
139 139 self._other = None
140 140 for var in ('localctx', 'otherctx'):
141 141 if var in vars(self):
142 142 delattr(self, var)
143 143 self._readmergedriver = None
144 144 self._mdstate = 's'
145 145 unsupported = set()
146 146 records = self._readrecords()
147 147 for rtype, record in records:
148 148 if rtype == 'L':
149 149 self._local = bin(record)
150 150 elif rtype == 'O':
151 151 self._other = bin(record)
152 152 elif rtype == 'm':
153 153 bits = record.split('\0', 1)
154 154 mdstate = bits[1]
155 155 if len(mdstate) != 1 or mdstate not in 'ums':
156 156 # the merge driver should be idempotent, so just rerun it
157 157 mdstate = 'u'
158 158
159 159 self._readmergedriver = bits[0]
160 160 self._mdstate = mdstate
161 161 elif rtype in 'FDC':
162 162 bits = record.split('\0')
163 163 self._state[bits[0]] = bits[1:]
164 164 elif rtype == 'f':
165 165 filename, rawextras = record.split('\0', 1)
166 166 extraparts = rawextras.split('\0')
167 167 extras = {}
168 168 i = 0
169 169 while i < len(extraparts):
170 170 extras[extraparts[i]] = extraparts[i + 1]
171 171 i += 2
172 172
173 173 self._stateextras[filename] = extras
174 174 elif rtype == 'l':
175 175 labels = record.split('\0', 2)
176 176 self._labels = [l for l in labels if len(l) > 0]
177 177 elif not rtype.islower():
178 178 unsupported.add(rtype)
179 179 self._results = {}
180 180 self._dirty = False
181 181
182 182 if unsupported:
183 183 raise error.UnsupportedMergeRecords(unsupported)
184 184
185 185 def _readrecords(self):
186 186 """Read merge state from disk and return a list of record (TYPE, data)
187 187
188 188 We read data from both v1 and v2 files and decide which one to use.
189 189
190 190 V1 has been used by version prior to 2.9.1 and contains less data than
191 191 v2. We read both versions and check if no data in v2 contradicts
192 192 v1. If there is not contradiction we can safely assume that both v1
193 193 and v2 were written at the same time and use the extract data in v2. If
194 194 there is contradiction we ignore v2 content as we assume an old version
195 195 of Mercurial has overwritten the mergestate file and left an old v2
196 196 file around.
197 197
198 198 returns list of record [(TYPE, data), ...]"""
199 199 v1records = self._readrecordsv1()
200 200 v2records = self._readrecordsv2()
201 201 if self._v1v2match(v1records, v2records):
202 202 return v2records
203 203 else:
204 204 # v1 file is newer than v2 file, use it
205 205 # we have to infer the "other" changeset of the merge
206 206 # we cannot do better than that with v1 of the format
207 207 mctx = self._repo[None].parents()[-1]
208 208 v1records.append(('O', mctx.hex()))
209 209 # add place holder "other" file node information
210 210 # nobody is using it yet so we do no need to fetch the data
211 211 # if mctx was wrong `mctx[bits[-2]]` may fails.
212 212 for idx, r in enumerate(v1records):
213 213 if r[0] == 'F':
214 214 bits = r[1].split('\0')
215 215 bits.insert(-2, '')
216 216 v1records[idx] = (r[0], '\0'.join(bits))
217 217 return v1records
218 218
219 219 def _v1v2match(self, v1records, v2records):
220 220 oldv2 = set() # old format version of v2 record
221 221 for rec in v2records:
222 222 if rec[0] == 'L':
223 223 oldv2.add(rec)
224 224 elif rec[0] == 'F':
225 225 # drop the onode data (not contained in v1)
226 226 oldv2.add(('F', _droponode(rec[1])))
227 227 for rec in v1records:
228 228 if rec not in oldv2:
229 229 return False
230 230 else:
231 231 return True
232 232
233 233 def _readrecordsv1(self):
234 234 """read on disk merge state for version 1 file
235 235
236 236 returns list of record [(TYPE, data), ...]
237 237
238 238 Note: the "F" data from this file are one entry short
239 239 (no "other file node" entry)
240 240 """
241 241 records = []
242 242 try:
243 243 f = self._repo.vfs(self.statepathv1)
244 244 for i, l in enumerate(f):
245 245 if i == 0:
246 246 records.append(('L', l[:-1]))
247 247 else:
248 248 records.append(('F', l[:-1]))
249 249 f.close()
250 250 except IOError as err:
251 251 if err.errno != errno.ENOENT:
252 252 raise
253 253 return records
254 254
255 255 def _readrecordsv2(self):
256 256 """read on disk merge state for version 2 file
257 257
258 258 This format is a list of arbitrary records of the form:
259 259
260 260 [type][length][content]
261 261
262 262 `type` is a single character, `length` is a 4 byte integer, and
263 263 `content` is an arbitrary byte sequence of length `length`.
264 264
265 265 Mercurial versions prior to 3.7 have a bug where if there are
266 266 unsupported mandatory merge records, attempting to clear out the merge
267 267 state with hg update --clean or similar aborts. The 't' record type
268 268 works around that by writing out what those versions treat as an
269 269 advisory record, but later versions interpret as special: the first
270 270 character is the 'real' record type and everything onwards is the data.
271 271
272 272 Returns list of records [(TYPE, data), ...]."""
273 273 records = []
274 274 try:
275 275 f = self._repo.vfs(self.statepathv2)
276 276 data = f.read()
277 277 off = 0
278 278 end = len(data)
279 279 while off < end:
280 280 rtype = data[off]
281 281 off += 1
282 282 length = _unpack('>I', data[off:(off + 4)])[0]
283 283 off += 4
284 284 record = data[off:(off + length)]
285 285 off += length
286 286 if rtype == 't':
287 287 rtype, record = record[0], record[1:]
288 288 records.append((rtype, record))
289 289 f.close()
290 290 except IOError as err:
291 291 if err.errno != errno.ENOENT:
292 292 raise
293 293 return records
294 294
295 295 @util.propertycache
296 296 def mergedriver(self):
297 297 # protect against the following:
298 298 # - A configures a malicious merge driver in their hgrc, then
299 299 # pauses the merge
300 300 # - A edits their hgrc to remove references to the merge driver
301 301 # - A gives a copy of their entire repo, including .hg, to B
302 302 # - B inspects .hgrc and finds it to be clean
303 303 # - B then continues the merge and the malicious merge driver
304 304 # gets invoked
305 305 configmergedriver = self._repo.ui.config('experimental', 'mergedriver')
306 306 if (self._readmergedriver is not None
307 307 and self._readmergedriver != configmergedriver):
308 308 raise error.ConfigError(
309 309 _("merge driver changed since merge started"),
310 310 hint=_("revert merge driver change or abort merge"))
311 311
312 312 return configmergedriver
313 313
314 314 @util.propertycache
315 315 def localctx(self):
316 316 if self._local is None:
317 317 raise RuntimeError("localctx accessed but self._local isn't set")
318 318 return self._repo[self._local]
319 319
320 320 @util.propertycache
321 321 def otherctx(self):
322 322 if self._other is None:
323 323 raise RuntimeError("otherctx accessed but self._other isn't set")
324 324 return self._repo[self._other]
325 325
326 326 def active(self):
327 327 """Whether mergestate is active.
328 328
329 329 Returns True if there appears to be mergestate. This is a rough proxy
330 330 for "is a merge in progress."
331 331 """
332 332 # Check local variables before looking at filesystem for performance
333 333 # reasons.
334 334 return bool(self._local) or bool(self._state) or \
335 335 self._repo.vfs.exists(self.statepathv1) or \
336 336 self._repo.vfs.exists(self.statepathv2)
337 337
338 338 def commit(self):
339 339 """Write current state on disk (if necessary)"""
340 340 if self._dirty:
341 341 records = self._makerecords()
342 342 self._writerecords(records)
343 343 self._dirty = False
344 344
345 345 def _makerecords(self):
346 346 records = []
347 347 records.append(('L', hex(self._local)))
348 348 records.append(('O', hex(self._other)))
349 349 if self.mergedriver:
350 350 records.append(('m', '\0'.join([
351 351 self.mergedriver, self._mdstate])))
352 352 for d, v in self._state.iteritems():
353 353 if v[0] == 'd':
354 354 records.append(('D', '\0'.join([d] + v)))
355 355 # v[1] == local ('cd'), v[6] == other ('dc') -- not supported by
356 356 # older versions of Mercurial
357 357 elif v[1] == nullhex or v[6] == nullhex:
358 358 records.append(('C', '\0'.join([d] + v)))
359 359 else:
360 360 records.append(('F', '\0'.join([d] + v)))
361 361 for filename, extras in sorted(self._stateextras.iteritems()):
362 362 rawextras = '\0'.join('%s\0%s' % (k, v) for k, v in
363 363 extras.iteritems())
364 364 records.append(('f', '%s\0%s' % (filename, rawextras)))
365 365 if self._labels is not None:
366 366 labels = '\0'.join(self._labels)
367 367 records.append(('l', labels))
368 368 return records
369 369
370 370 def _writerecords(self, records):
371 371 """Write current state on disk (both v1 and v2)"""
372 372 self._writerecordsv1(records)
373 373 self._writerecordsv2(records)
374 374
375 375 def _writerecordsv1(self, records):
376 376 """Write current state on disk in a version 1 file"""
377 377 f = self._repo.vfs(self.statepathv1, 'w')
378 378 irecords = iter(records)
379 379 lrecords = next(irecords)
380 380 assert lrecords[0] == 'L'
381 381 f.write(hex(self._local) + '\n')
382 382 for rtype, data in irecords:
383 383 if rtype == 'F':
384 384 f.write('%s\n' % _droponode(data))
385 385 f.close()
386 386
387 387 def _writerecordsv2(self, records):
388 388 """Write current state on disk in a version 2 file
389 389
390 390 See the docstring for _readrecordsv2 for why we use 't'."""
391 391 # these are the records that all version 2 clients can read
392 392 whitelist = 'LOF'
393 393 f = self._repo.vfs(self.statepathv2, 'w')
394 394 for key, data in records:
395 395 assert len(key) == 1
396 396 if key not in whitelist:
397 397 key, data = 't', '%s%s' % (key, data)
398 398 format = '>sI%is' % len(data)
399 399 f.write(_pack(format, key, len(data), data))
400 400 f.close()
401 401
402 402 def add(self, fcl, fco, fca, fd):
403 403 """add a new (potentially?) conflicting file the merge state
404 404 fcl: file context for local,
405 405 fco: file context for remote,
406 406 fca: file context for ancestors,
407 407 fd: file path of the resulting merge.
408 408
409 409 note: also write the local version to the `.hg/merge` directory.
410 410 """
411 411 if fcl.isabsent():
412 412 hash = nullhex
413 413 else:
414 414 hash = hashlib.sha1(fcl.path()).hexdigest()
415 415 self._repo.vfs.write('merge/' + hash, fcl.data())
416 416 self._state[fd] = ['u', hash, fcl.path(),
417 417 fca.path(), hex(fca.filenode()),
418 418 fco.path(), hex(fco.filenode()),
419 419 fcl.flags()]
420 420 self._stateextras[fd] = { 'ancestorlinknode' : hex(fca.node()) }
421 421 self._dirty = True
422 422
423 423 def __contains__(self, dfile):
424 424 return dfile in self._state
425 425
426 426 def __getitem__(self, dfile):
427 427 return self._state[dfile][0]
428 428
429 429 def __iter__(self):
430 430 return iter(sorted(self._state))
431 431
432 432 def files(self):
433 433 return self._state.keys()
434 434
435 435 def mark(self, dfile, state):
436 436 self._state[dfile][0] = state
437 437 self._dirty = True
438 438
439 439 def mdstate(self):
440 440 return self._mdstate
441 441
442 442 def unresolved(self):
443 443 """Obtain the paths of unresolved files."""
444 444
445 445 for f, entry in self._state.items():
446 446 if entry[0] == 'u':
447 447 yield f
448 448
449 449 def driverresolved(self):
450 450 """Obtain the paths of driver-resolved files."""
451 451
452 452 for f, entry in self._state.items():
453 453 if entry[0] == 'd':
454 454 yield f
455 455
456 456 def extras(self, filename):
457 457 return self._stateextras.setdefault(filename, {})
458 458
459 459 def _resolve(self, preresolve, dfile, wctx):
460 460 """rerun merge process for file path `dfile`"""
461 461 if self[dfile] in 'rd':
462 462 return True, 0
463 463 stateentry = self._state[dfile]
464 464 state, hash, lfile, afile, anode, ofile, onode, flags = stateentry
465 465 octx = self._repo[self._other]
466 466 extras = self.extras(dfile)
467 467 anccommitnode = extras.get('ancestorlinknode')
468 468 if anccommitnode:
469 469 actx = self._repo[anccommitnode]
470 470 else:
471 471 actx = None
472 472 fcd = self._filectxorabsent(hash, wctx, dfile)
473 473 fco = self._filectxorabsent(onode, octx, ofile)
474 474 # TODO: move this to filectxorabsent
475 475 fca = self._repo.filectx(afile, fileid=anode, changeid=actx)
476 476 # "premerge" x flags
477 477 flo = fco.flags()
478 478 fla = fca.flags()
479 479 if 'x' in flags + flo + fla and 'l' not in flags + flo + fla:
480 480 if fca.node() == nullid and flags != flo:
481 481 if preresolve:
482 482 self._repo.ui.warn(
483 483 _('warning: cannot merge flags for %s '
484 484 'without common ancestor - keeping local flags\n')
485 485 % afile)
486 486 elif flags == fla:
487 487 flags = flo
488 488 if preresolve:
489 489 # restore local
490 490 if hash != nullhex:
491 491 f = self._repo.vfs('merge/' + hash)
492 492 self._repo.wwrite(dfile, f.read(), flags)
493 493 f.close()
494 494 else:
495 495 self._repo.wvfs.unlinkpath(dfile, ignoremissing=True)
496 496 complete, r, deleted = filemerge.premerge(self._repo, self._local,
497 497 lfile, fcd, fco, fca,
498 498 labels=self._labels)
499 499 else:
500 500 complete, r, deleted = filemerge.filemerge(self._repo, self._local,
501 501 lfile, fcd, fco, fca,
502 502 labels=self._labels)
503 503 if r is None:
504 504 # no real conflict
505 505 del self._state[dfile]
506 506 self._stateextras.pop(dfile, None)
507 507 self._dirty = True
508 508 elif not r:
509 509 self.mark(dfile, 'r')
510 510
511 511 if complete:
512 512 action = None
513 513 if deleted:
514 514 if fcd.isabsent():
515 515 # dc: local picked. Need to drop if present, which may
516 516 # happen on re-resolves.
517 517 action = 'f'
518 518 else:
519 519 # cd: remote picked (or otherwise deleted)
520 520 action = 'r'
521 521 else:
522 522 if fcd.isabsent(): # dc: remote picked
523 523 action = 'g'
524 524 elif fco.isabsent(): # cd: local picked
525 525 if dfile in self.localctx:
526 526 action = 'am'
527 527 else:
528 528 action = 'a'
529 529 # else: regular merges (no action necessary)
530 530 self._results[dfile] = r, action
531 531
532 532 return complete, r
533 533
534 534 def _filectxorabsent(self, hexnode, ctx, f):
535 535 if hexnode == nullhex:
536 536 return filemerge.absentfilectx(ctx, f)
537 537 else:
538 538 return ctx[f]
539 539
540 540 def preresolve(self, dfile, wctx):
541 541 """run premerge process for dfile
542 542
543 543 Returns whether the merge is complete, and the exit code."""
544 544 return self._resolve(True, dfile, wctx)
545 545
546 546 def resolve(self, dfile, wctx):
547 547 """run merge process (assuming premerge was run) for dfile
548 548
549 549 Returns the exit code of the merge."""
550 550 return self._resolve(False, dfile, wctx)[1]
551 551
552 552 def counts(self):
553 553 """return counts for updated, merged and removed files in this
554 554 session"""
555 555 updated, merged, removed = 0, 0, 0
556 556 for r, action in self._results.itervalues():
557 557 if r is None:
558 558 updated += 1
559 559 elif r == 0:
560 560 if action == 'r':
561 561 removed += 1
562 562 else:
563 563 merged += 1
564 564 return updated, merged, removed
565 565
566 566 def unresolvedcount(self):
567 567 """get unresolved count for this merge (persistent)"""
568 568 return len([True for f, entry in self._state.iteritems()
569 569 if entry[0] == 'u'])
570 570
571 571 def actions(self):
572 572 """return lists of actions to perform on the dirstate"""
573 573 actions = {'r': [], 'f': [], 'a': [], 'am': [], 'g': []}
574 574 for f, (r, action) in self._results.iteritems():
575 575 if action is not None:
576 576 actions[action].append((f, None, "merge result"))
577 577 return actions
578 578
579 579 def recordactions(self):
580 580 """record remove/add/get actions in the dirstate"""
581 581 branchmerge = self._repo.dirstate.p2() != nullid
582 582 recordupdates(self._repo, self.actions(), branchmerge)
583 583
584 584 def queueremove(self, f):
585 585 """queues a file to be removed from the dirstate
586 586
587 587 Meant for use by custom merge drivers."""
588 588 self._results[f] = 0, 'r'
589 589
590 590 def queueadd(self, f):
591 591 """queues a file to be added to the dirstate
592 592
593 593 Meant for use by custom merge drivers."""
594 594 self._results[f] = 0, 'a'
595 595
596 596 def queueget(self, f):
597 597 """queues a file to be marked modified in the dirstate
598 598
599 599 Meant for use by custom merge drivers."""
600 600 self._results[f] = 0, 'g'
601 601
602 602 def _getcheckunknownconfig(repo, section, name):
603 603 config = repo.ui.config(section, name, default='abort')
604 604 valid = ['abort', 'ignore', 'warn']
605 605 if config not in valid:
606 606 validstr = ', '.join(["'" + v + "'" for v in valid])
607 607 raise error.ConfigError(_("%s.%s not valid "
608 608 "('%s' is none of %s)")
609 609 % (section, name, config, validstr))
610 610 return config
611 611
612 612 def _checkunknownfile(repo, wctx, mctx, f, f2=None):
613 613 if f2 is None:
614 614 f2 = f
615 615 return (repo.wvfs.audit.check(f)
616 616 and repo.wvfs.isfileorlink(f)
617 617 and repo.dirstate.normalize(f) not in repo.dirstate
618 618 and mctx[f2].cmp(wctx[f]))
619 619
620 620 def _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce):
621 621 """
622 622 Considers any actions that care about the presence of conflicting unknown
623 623 files. For some actions, the result is to abort; for others, it is to
624 624 choose a different action.
625 625 """
626 626 conflicts = set()
627 627 warnconflicts = set()
628 628 abortconflicts = set()
629 629 unknownconfig = _getcheckunknownconfig(repo, 'merge', 'checkunknown')
630 630 ignoredconfig = _getcheckunknownconfig(repo, 'merge', 'checkignored')
631 631 if not force:
632 632 def collectconflicts(conflicts, config):
633 633 if config == 'abort':
634 634 abortconflicts.update(conflicts)
635 635 elif config == 'warn':
636 636 warnconflicts.update(conflicts)
637 637
638 638 for f, (m, args, msg) in actions.iteritems():
639 639 if m in ('c', 'dc'):
640 640 if _checkunknownfile(repo, wctx, mctx, f):
641 641 conflicts.add(f)
642 642 elif m == 'dg':
643 643 if _checkunknownfile(repo, wctx, mctx, f, args[0]):
644 644 conflicts.add(f)
645 645
646 646 ignoredconflicts = set([c for c in conflicts
647 647 if repo.dirstate._ignore(c)])
648 648 unknownconflicts = conflicts - ignoredconflicts
649 649 collectconflicts(ignoredconflicts, ignoredconfig)
650 650 collectconflicts(unknownconflicts, unknownconfig)
651 651 else:
652 652 for f, (m, args, msg) in actions.iteritems():
653 653 if m == 'cm':
654 654 fl2, anc = args
655 655 different = _checkunknownfile(repo, wctx, mctx, f)
656 656 if repo.dirstate._ignore(f):
657 657 config = ignoredconfig
658 658 else:
659 659 config = unknownconfig
660 660
661 661 # The behavior when force is True is described by this table:
662 662 # config different mergeforce | action backup
663 663 # * n * | get n
664 664 # * y y | merge -
665 665 # abort y n | merge - (1)
666 666 # warn y n | warn + get y
667 667 # ignore y n | get y
668 668 #
669 669 # (1) this is probably the wrong behavior here -- we should
670 670 # probably abort, but some actions like rebases currently
671 671 # don't like an abort happening in the middle of
672 672 # merge.update.
673 673 if not different:
674 674 actions[f] = ('g', (fl2, False), "remote created")
675 675 elif mergeforce or config == 'abort':
676 676 actions[f] = ('m', (f, f, None, False, anc),
677 677 "remote differs from untracked local")
678 678 elif config == 'abort':
679 679 abortconflicts.add(f)
680 680 else:
681 681 if config == 'warn':
682 682 warnconflicts.add(f)
683 683 actions[f] = ('g', (fl2, True), "remote created")
684 684
685 685 for f in sorted(abortconflicts):
686 686 repo.ui.warn(_("%s: untracked file differs\n") % f)
687 687 if abortconflicts:
688 688 raise error.Abort(_("untracked files in working directory "
689 689 "differ from files in requested revision"))
690 690
691 691 for f in sorted(warnconflicts):
692 692 repo.ui.warn(_("%s: replacing untracked file\n") % f)
693 693
694 694 for f, (m, args, msg) in actions.iteritems():
695 695 backup = f in conflicts
696 696 if m == 'c':
697 697 flags, = args
698 698 actions[f] = ('g', (flags, backup), msg)
699 699
700 700 def _forgetremoved(wctx, mctx, branchmerge):
701 701 """
702 702 Forget removed files
703 703
704 704 If we're jumping between revisions (as opposed to merging), and if
705 705 neither the working directory nor the target rev has the file,
706 706 then we need to remove it from the dirstate, to prevent the
707 707 dirstate from listing the file when it is no longer in the
708 708 manifest.
709 709
710 710 If we're merging, and the other revision has removed a file
711 711 that is not present in the working directory, we need to mark it
712 712 as removed.
713 713 """
714 714
715 715 actions = {}
716 716 m = 'f'
717 717 if branchmerge:
718 718 m = 'r'
719 719 for f in wctx.deleted():
720 720 if f not in mctx:
721 721 actions[f] = m, None, "forget deleted"
722 722
723 723 if not branchmerge:
724 724 for f in wctx.removed():
725 725 if f not in mctx:
726 726 actions[f] = 'f', None, "forget removed"
727 727
728 728 return actions
729 729
730 730 def _checkcollision(repo, wmf, actions):
731 731 # build provisional merged manifest up
732 732 pmmf = set(wmf)
733 733
734 734 if actions:
735 735 # k, dr, e and rd are no-op
736 736 for m in 'a', 'am', 'f', 'g', 'cd', 'dc':
737 737 for f, args, msg in actions[m]:
738 738 pmmf.add(f)
739 739 for f, args, msg in actions['r']:
740 740 pmmf.discard(f)
741 741 for f, args, msg in actions['dm']:
742 742 f2, flags = args
743 743 pmmf.discard(f2)
744 744 pmmf.add(f)
745 745 for f, args, msg in actions['dg']:
746 746 pmmf.add(f)
747 747 for f, args, msg in actions['m']:
748 748 f1, f2, fa, move, anc = args
749 749 if move:
750 750 pmmf.discard(f1)
751 751 pmmf.add(f)
752 752
753 753 # check case-folding collision in provisional merged manifest
754 754 foldmap = {}
755 755 for f in sorted(pmmf):
756 756 fold = util.normcase(f)
757 757 if fold in foldmap:
758 758 raise error.Abort(_("case-folding collision between %s and %s")
759 759 % (f, foldmap[fold]))
760 760 foldmap[fold] = f
761 761
762 762 # check case-folding of directories
763 763 foldprefix = unfoldprefix = lastfull = ''
764 764 for fold, f in sorted(foldmap.items()):
765 765 if fold.startswith(foldprefix) and not f.startswith(unfoldprefix):
766 766 # the folded prefix matches but actual casing is different
767 767 raise error.Abort(_("case-folding collision between "
768 768 "%s and directory of %s") % (lastfull, f))
769 769 foldprefix = fold + '/'
770 770 unfoldprefix = f + '/'
771 771 lastfull = f
772 772
773 773 def driverpreprocess(repo, ms, wctx, labels=None):
774 774 """run the preprocess step of the merge driver, if any
775 775
776 776 This is currently not implemented -- it's an extension point."""
777 777 return True
778 778
779 779 def driverconclude(repo, ms, wctx, labels=None):
780 780 """run the conclude step of the merge driver, if any
781 781
782 782 This is currently not implemented -- it's an extension point."""
783 783 return True
784 784
785 785 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, matcher,
786 786 acceptremote, followcopies):
787 787 """
788 788 Merge wctx and p2 with ancestor pa and generate merge action list
789 789
790 790 branchmerge and force are as passed in to update
791 791 matcher = matcher to filter file lists
792 792 acceptremote = accept the incoming changes without prompting
793 793 """
794 794 if matcher is not None and matcher.always():
795 795 matcher = None
796 796
797 797 copy, movewithdir, diverge, renamedelete, dirmove = {}, {}, {}, {}, {}
798 798
799 799 # manifests fetched in order are going to be faster, so prime the caches
800 800 [x.manifest() for x in
801 801 sorted(wctx.parents() + [p2, pa], key=lambda x: x.rev())]
802 802
803 803 if followcopies:
804 804 ret = copies.mergecopies(repo, wctx, p2, pa)
805 805 copy, movewithdir, diverge, renamedelete, dirmove = ret
806 806
807 807 repo.ui.note(_("resolving manifests\n"))
808 808 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
809 809 % (bool(branchmerge), bool(force), bool(matcher)))
810 810 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
811 811
812 812 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
813 813 copied = set(copy.values())
814 814 copied.update(movewithdir.values())
815 815
816 816 if '.hgsubstate' in m1:
817 817 # check whether sub state is modified
818 818 if any(wctx.sub(s).dirty() for s in wctx.substate):
819 819 m1['.hgsubstate'] = modifiednodeid
820 820
821 821 # Compare manifests
822 822 if matcher is not None:
823 823 m1 = m1.matches(matcher)
824 824 m2 = m2.matches(matcher)
825 825 diff = m1.diff(m2)
826 826
827 827 actions = {}
828 828 for f, ((n1, fl1), (n2, fl2)) in diff.iteritems():
829 829 if n1 and n2: # file exists on both local and remote side
830 830 if f not in ma:
831 831 fa = copy.get(f, None)
832 832 if fa is not None:
833 833 actions[f] = ('m', (f, f, fa, False, pa.node()),
834 834 "both renamed from " + fa)
835 835 else:
836 836 actions[f] = ('m', (f, f, None, False, pa.node()),
837 837 "both created")
838 838 else:
839 839 a = ma[f]
840 840 fla = ma.flags(f)
841 841 nol = 'l' not in fl1 + fl2 + fla
842 842 if n2 == a and fl2 == fla:
843 843 actions[f] = ('k' , (), "remote unchanged")
844 844 elif n1 == a and fl1 == fla: # local unchanged - use remote
845 845 if n1 == n2: # optimization: keep local content
846 846 actions[f] = ('e', (fl2,), "update permissions")
847 847 else:
848 848 actions[f] = ('g', (fl2, False), "remote is newer")
849 849 elif nol and n2 == a: # remote only changed 'x'
850 850 actions[f] = ('e', (fl2,), "update permissions")
851 851 elif nol and n1 == a: # local only changed 'x'
852 852 actions[f] = ('g', (fl1, False), "remote is newer")
853 853 else: # both changed something
854 854 actions[f] = ('m', (f, f, f, False, pa.node()),
855 855 "versions differ")
856 856 elif n1: # file exists only on local side
857 857 if f in copied:
858 858 pass # we'll deal with it on m2 side
859 859 elif f in movewithdir: # directory rename, move local
860 860 f2 = movewithdir[f]
861 861 if f2 in m2:
862 862 actions[f2] = ('m', (f, f2, None, True, pa.node()),
863 863 "remote directory rename, both created")
864 864 else:
865 865 actions[f2] = ('dm', (f, fl1),
866 866 "remote directory rename - move from " + f)
867 867 elif f in copy:
868 868 f2 = copy[f]
869 869 actions[f] = ('m', (f, f2, f2, False, pa.node()),
870 870 "local copied/moved from " + f2)
871 871 elif f in ma: # clean, a different, no remote
872 872 if n1 != ma[f]:
873 873 if acceptremote:
874 874 actions[f] = ('r', None, "remote delete")
875 875 else:
876 876 actions[f] = ('cd', (f, None, f, False, pa.node()),
877 877 "prompt changed/deleted")
878 878 elif n1 == addednodeid:
879 879 # This extra 'a' is added by working copy manifest to mark
880 880 # the file as locally added. We should forget it instead of
881 881 # deleting it.
882 882 actions[f] = ('f', None, "remote deleted")
883 883 else:
884 884 actions[f] = ('r', None, "other deleted")
885 885 elif n2: # file exists only on remote side
886 886 if f in copied:
887 887 pass # we'll deal with it on m1 side
888 888 elif f in movewithdir:
889 889 f2 = movewithdir[f]
890 890 if f2 in m1:
891 891 actions[f2] = ('m', (f2, f, None, False, pa.node()),
892 892 "local directory rename, both created")
893 893 else:
894 894 actions[f2] = ('dg', (f, fl2),
895 895 "local directory rename - get from " + f)
896 896 elif f in copy:
897 897 f2 = copy[f]
898 898 if f2 in m2:
899 899 actions[f] = ('m', (f2, f, f2, False, pa.node()),
900 900 "remote copied from " + f2)
901 901 else:
902 902 actions[f] = ('m', (f2, f, f2, True, pa.node()),
903 903 "remote moved from " + f2)
904 904 elif f not in ma:
905 905 # local unknown, remote created: the logic is described by the
906 906 # following table:
907 907 #
908 908 # force branchmerge different | action
909 909 # n * * | create
910 910 # y n * | create
911 911 # y y n | create
912 912 # y y y | merge
913 913 #
914 914 # Checking whether the files are different is expensive, so we
915 915 # don't do that when we can avoid it.
916 916 if not force:
917 917 actions[f] = ('c', (fl2,), "remote created")
918 918 elif not branchmerge:
919 919 actions[f] = ('c', (fl2,), "remote created")
920 920 else:
921 921 actions[f] = ('cm', (fl2, pa.node()),
922 922 "remote created, get or merge")
923 923 elif n2 != ma[f]:
924 924 df = None
925 925 for d in dirmove:
926 926 if f.startswith(d):
927 927 # new file added in a directory that was moved
928 928 df = dirmove[d] + f[len(d):]
929 929 break
930 930 if df in m1:
931 931 actions[df] = ('m', (df, f, f, False, pa.node()),
932 932 "local directory rename - respect move from " + f)
933 933 elif acceptremote:
934 934 actions[f] = ('c', (fl2,), "remote recreating")
935 935 else:
936 936 actions[f] = ('dc', (None, f, f, False, pa.node()),
937 937 "prompt deleted/changed")
938 938
939 939 return actions, diverge, renamedelete
940 940
941 941 def _resolvetrivial(repo, wctx, mctx, ancestor, actions):
942 942 """Resolves false conflicts where the nodeid changed but the content
943 943 remained the same."""
944 944
945 945 for f, (m, args, msg) in actions.items():
946 946 if m == 'cd' and f in ancestor and not wctx[f].cmp(ancestor[f]):
947 947 # local did change but ended up with same content
948 948 actions[f] = 'r', None, "prompt same"
949 949 elif m == 'dc' and f in ancestor and not mctx[f].cmp(ancestor[f]):
950 950 # remote did change but ended up with same content
951 951 del actions[f] # don't get = keep local deleted
952 952
953 953 def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force,
954 954 acceptremote, followcopies, matcher=None,
955 955 mergeforce=False):
956 956 "Calculate the actions needed to merge mctx into wctx using ancestors"
957 957 if len(ancestors) == 1: # default
958 958 actions, diverge, renamedelete = manifestmerge(
959 959 repo, wctx, mctx, ancestors[0], branchmerge, force, matcher,
960 960 acceptremote, followcopies)
961 961 _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce)
962 962
963 963 else: # only when merge.preferancestor=* - the default
964 964 repo.ui.note(
965 965 _("note: merging %s and %s using bids from ancestors %s\n") %
966 966 (wctx, mctx, _(' and ').join(str(anc) for anc in ancestors)))
967 967
968 968 # Call for bids
969 969 fbids = {} # mapping filename to bids (action method to list af actions)
970 970 diverge, renamedelete = None, None
971 971 for ancestor in ancestors:
972 972 repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
973 973 actions, diverge1, renamedelete1 = manifestmerge(
974 974 repo, wctx, mctx, ancestor, branchmerge, force, matcher,
975 975 acceptremote, followcopies)
976 976 _checkunknownfiles(repo, wctx, mctx, force, actions, mergeforce)
977 977
978 978 # Track the shortest set of warning on the theory that bid
979 979 # merge will correctly incorporate more information
980 980 if diverge is None or len(diverge1) < len(diverge):
981 981 diverge = diverge1
982 982 if renamedelete is None or len(renamedelete) < len(renamedelete1):
983 983 renamedelete = renamedelete1
984 984
985 985 for f, a in sorted(actions.iteritems()):
986 986 m, args, msg = a
987 987 repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m))
988 988 if f in fbids:
989 989 d = fbids[f]
990 990 if m in d:
991 991 d[m].append(a)
992 992 else:
993 993 d[m] = [a]
994 994 else:
995 995 fbids[f] = {m: [a]}
996 996
997 997 # Pick the best bid for each file
998 998 repo.ui.note(_('\nauction for merging merge bids\n'))
999 999 actions = {}
1000 1000 dms = [] # filenames that have dm actions
1001 1001 for f, bids in sorted(fbids.items()):
1002 1002 # bids is a mapping from action method to list af actions
1003 1003 # Consensus?
1004 1004 if len(bids) == 1: # all bids are the same kind of method
1005 1005 m, l = bids.items()[0]
1006 1006 if all(a == l[0] for a in l[1:]): # len(bids) is > 1
1007 1007 repo.ui.note(_(" %s: consensus for %s\n") % (f, m))
1008 1008 actions[f] = l[0]
1009 1009 if m == 'dm':
1010 1010 dms.append(f)
1011 1011 continue
1012 1012 # If keep is an option, just do it.
1013 1013 if 'k' in bids:
1014 1014 repo.ui.note(_(" %s: picking 'keep' action\n") % f)
1015 1015 actions[f] = bids['k'][0]
1016 1016 continue
1017 1017 # If there are gets and they all agree [how could they not?], do it.
1018 1018 if 'g' in bids:
1019 1019 ga0 = bids['g'][0]
1020 1020 if all(a == ga0 for a in bids['g'][1:]):
1021 1021 repo.ui.note(_(" %s: picking 'get' action\n") % f)
1022 1022 actions[f] = ga0
1023 1023 continue
1024 1024 # TODO: Consider other simple actions such as mode changes
1025 1025 # Handle inefficient democrazy.
1026 1026 repo.ui.note(_(' %s: multiple bids for merge action:\n') % f)
1027 1027 for m, l in sorted(bids.items()):
1028 1028 for _f, args, msg in l:
1029 1029 repo.ui.note(' %s -> %s\n' % (msg, m))
1030 1030 # Pick random action. TODO: Instead, prompt user when resolving
1031 1031 m, l = bids.items()[0]
1032 1032 repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') %
1033 1033 (f, m))
1034 1034 actions[f] = l[0]
1035 1035 if m == 'dm':
1036 1036 dms.append(f)
1037 1037 continue
1038 1038 # Work around 'dm' that can cause multiple actions for the same file
1039 1039 for f in dms:
1040 1040 dm, (f0, flags), msg = actions[f]
1041 1041 assert dm == 'dm', dm
1042 1042 if f0 in actions and actions[f0][0] == 'r':
1043 1043 # We have one bid for removing a file and another for moving it.
1044 1044 # These two could be merged as first move and then delete ...
1045 1045 # but instead drop moving and just delete.
1046 1046 del actions[f]
1047 1047 repo.ui.note(_('end of auction\n\n'))
1048 1048
1049 1049 _resolvetrivial(repo, wctx, mctx, ancestors[0], actions)
1050 1050
1051 1051 if wctx.rev() is None:
1052 1052 fractions = _forgetremoved(wctx, mctx, branchmerge)
1053 1053 actions.update(fractions)
1054 1054
1055 1055 return actions, diverge, renamedelete
1056 1056
1057 1057 def batchremove(repo, actions):
1058 1058 """apply removes to the working directory
1059 1059
1060 1060 yields tuples for progress updates
1061 1061 """
1062 1062 verbose = repo.ui.verbose
1063 1063 unlink = util.unlinkpath
1064 1064 wjoin = repo.wjoin
1065 1065 audit = repo.wvfs.audit
1066 1066 try:
1067 1067 cwd = pycompat.getcwd()
1068 1068 except OSError as err:
1069 1069 if err.errno != errno.ENOENT:
1070 1070 raise
1071 1071 cwd = None
1072 1072 i = 0
1073 1073 for f, args, msg in actions:
1074 1074 repo.ui.debug(" %s: %s -> r\n" % (f, msg))
1075 1075 if verbose:
1076 1076 repo.ui.note(_("removing %s\n") % f)
1077 1077 audit(f)
1078 1078 try:
1079 1079 unlink(wjoin(f), ignoremissing=True)
1080 1080 except OSError as inst:
1081 1081 repo.ui.warn(_("update failed to remove %s: %s!\n") %
1082 1082 (f, inst.strerror))
1083 1083 if i == 100:
1084 1084 yield i, f
1085 1085 i = 0
1086 1086 i += 1
1087 1087 if i > 0:
1088 1088 yield i, f
1089 1089 if cwd:
1090 1090 # cwd was present before we started to remove files
1091 1091 # let's check if it is present after we removed them
1092 1092 try:
1093 1093 pycompat.getcwd()
1094 1094 except OSError as err:
1095 1095 if err.errno != errno.ENOENT:
1096 1096 raise
1097 1097 # Print a warning if cwd was deleted
1098 1098 repo.ui.warn(_("current directory was removed\n"
1099 1099 "(consider changing to repo root: %s)\n") %
1100 1100 repo.root)
1101 1101
1102 1102 def batchget(repo, mctx, actions):
1103 1103 """apply gets to the working directory
1104 1104
1105 1105 mctx is the context to get from
1106 1106
1107 1107 yields tuples for progress updates
1108 1108 """
1109 1109 verbose = repo.ui.verbose
1110 1110 fctx = mctx.filectx
1111 1111 wwrite = repo.wwrite
1112 1112 ui = repo.ui
1113 1113 i = 0
1114 1114 with repo.wvfs.backgroundclosing(ui, expectedcount=len(actions)):
1115 1115 for f, (flags, backup), msg in actions:
1116 1116 repo.ui.debug(" %s: %s -> g\n" % (f, msg))
1117 1117 if verbose:
1118 1118 repo.ui.note(_("getting %s\n") % f)
1119 1119
1120 1120 if backup:
1121 1121 absf = repo.wjoin(f)
1122 1122 orig = scmutil.origpath(ui, repo, absf)
1123 1123 try:
1124 1124 if repo.wvfs.isfileorlink(f):
1125 1125 util.rename(absf, orig)
1126 1126 except OSError as e:
1127 1127 if e.errno != errno.ENOENT:
1128 1128 raise
1129 1129
1130 1130 if repo.wvfs.isdir(f) and not repo.wvfs.islink(f):
1131 1131 repo.wvfs.removedirs(f)
1132 1132 wwrite(f, fctx(f).data(), flags, backgroundclose=True)
1133 1133 if i == 100:
1134 1134 yield i, f
1135 1135 i = 0
1136 1136 i += 1
1137 1137 if i > 0:
1138 1138 yield i, f
1139 1139
1140 1140 def applyupdates(repo, actions, wctx, mctx, overwrite, labels=None):
1141 1141 """apply the merge action list to the working directory
1142 1142
1143 1143 wctx is the working copy context
1144 1144 mctx is the context to be merged into the working copy
1145 1145
1146 1146 Return a tuple of counts (updated, merged, removed, unresolved) that
1147 1147 describes how many files were affected by the update.
1148 1148 """
1149 1149
1150 1150 updated, merged, removed = 0, 0, 0
1151 1151 ms = mergestate.clean(repo, wctx.p1().node(), mctx.node(), labels)
1152 1152 moves = []
1153 1153 for m, l in actions.items():
1154 1154 l.sort()
1155 1155
1156 1156 # 'cd' and 'dc' actions are treated like other merge conflicts
1157 1157 mergeactions = sorted(actions['cd'])
1158 1158 mergeactions.extend(sorted(actions['dc']))
1159 1159 mergeactions.extend(actions['m'])
1160 1160 for f, args, msg in mergeactions:
1161 1161 f1, f2, fa, move, anc = args
1162 1162 if f == '.hgsubstate': # merged internally
1163 1163 continue
1164 1164 if f1 is None:
1165 1165 fcl = filemerge.absentfilectx(wctx, fa)
1166 1166 else:
1167 1167 repo.ui.debug(" preserving %s for resolve of %s\n" % (f1, f))
1168 1168 fcl = wctx[f1]
1169 1169 if f2 is None:
1170 1170 fco = filemerge.absentfilectx(mctx, fa)
1171 1171 else:
1172 1172 fco = mctx[f2]
1173 1173 actx = repo[anc]
1174 1174 if fa in actx:
1175 1175 fca = actx[fa]
1176 1176 else:
1177 1177 # TODO: move to absentfilectx
1178 1178 fca = repo.filectx(f1, fileid=nullrev)
1179 1179 ms.add(fcl, fco, fca, f)
1180 1180 if f1 != f and move:
1181 1181 moves.append(f1)
1182 1182
1183 1183 audit = repo.wvfs.audit
1184 1184 _updating = _('updating')
1185 1185 _files = _('files')
1186 1186 progress = repo.ui.progress
1187 1187
1188 1188 # remove renamed files after safely stored
1189 1189 for f in moves:
1190 1190 if os.path.lexists(repo.wjoin(f)):
1191 1191 repo.ui.debug("removing %s\n" % f)
1192 1192 audit(f)
1193 1193 util.unlinkpath(repo.wjoin(f))
1194 1194
1195 1195 numupdates = sum(len(l) for m, l in actions.items() if m != 'k')
1196 1196
1197 1197 if [a for a in actions['r'] if a[0] == '.hgsubstate']:
1198 1198 subrepo.submerge(repo, wctx, mctx, wctx, overwrite, labels)
1199 1199
1200 1200 # remove in parallel (must come first)
1201 1201 z = 0
1202 1202 prog = worker.worker(repo.ui, 0.001, batchremove, (repo,), actions['r'])
1203 1203 for i, item in prog:
1204 1204 z += i
1205 1205 progress(_updating, z, item=item, total=numupdates, unit=_files)
1206 1206 removed = len(actions['r'])
1207 1207
1208 1208 # get in parallel
1209 1209 prog = worker.worker(repo.ui, 0.001, batchget, (repo, mctx), actions['g'])
1210 1210 for i, item in prog:
1211 1211 z += i
1212 1212 progress(_updating, z, item=item, total=numupdates, unit=_files)
1213 1213 updated = len(actions['g'])
1214 1214
1215 1215 if [a for a in actions['g'] if a[0] == '.hgsubstate']:
1216 1216 subrepo.submerge(repo, wctx, mctx, wctx, overwrite, labels)
1217 1217
1218 1218 # forget (manifest only, just log it) (must come first)
1219 1219 for f, args, msg in actions['f']:
1220 1220 repo.ui.debug(" %s: %s -> f\n" % (f, msg))
1221 1221 z += 1
1222 1222 progress(_updating, z, item=f, total=numupdates, unit=_files)
1223 1223
1224 1224 # re-add (manifest only, just log it)
1225 1225 for f, args, msg in actions['a']:
1226 1226 repo.ui.debug(" %s: %s -> a\n" % (f, msg))
1227 1227 z += 1
1228 1228 progress(_updating, z, item=f, total=numupdates, unit=_files)
1229 1229
1230 1230 # re-add/mark as modified (manifest only, just log it)
1231 1231 for f, args, msg in actions['am']:
1232 1232 repo.ui.debug(" %s: %s -> am\n" % (f, msg))
1233 1233 z += 1
1234 1234 progress(_updating, z, item=f, total=numupdates, unit=_files)
1235 1235
1236 1236 # keep (noop, just log it)
1237 1237 for f, args, msg in actions['k']:
1238 1238 repo.ui.debug(" %s: %s -> k\n" % (f, msg))
1239 1239 # no progress
1240 1240
1241 1241 # directory rename, move local
1242 1242 for f, args, msg in actions['dm']:
1243 1243 repo.ui.debug(" %s: %s -> dm\n" % (f, msg))
1244 1244 z += 1
1245 1245 progress(_updating, z, item=f, total=numupdates, unit=_files)
1246 1246 f0, flags = args
1247 1247 repo.ui.note(_("moving %s to %s\n") % (f0, f))
1248 1248 audit(f)
1249 1249 repo.wwrite(f, wctx.filectx(f0).data(), flags)
1250 1250 util.unlinkpath(repo.wjoin(f0))
1251 1251 updated += 1
1252 1252
1253 1253 # local directory rename, get
1254 1254 for f, args, msg in actions['dg']:
1255 1255 repo.ui.debug(" %s: %s -> dg\n" % (f, msg))
1256 1256 z += 1
1257 1257 progress(_updating, z, item=f, total=numupdates, unit=_files)
1258 1258 f0, flags = args
1259 1259 repo.ui.note(_("getting %s to %s\n") % (f0, f))
1260 1260 repo.wwrite(f, mctx.filectx(f0).data(), flags)
1261 1261 updated += 1
1262 1262
1263 1263 # exec
1264 1264 for f, args, msg in actions['e']:
1265 1265 repo.ui.debug(" %s: %s -> e\n" % (f, msg))
1266 1266 z += 1
1267 1267 progress(_updating, z, item=f, total=numupdates, unit=_files)
1268 1268 flags, = args
1269 1269 audit(f)
1270 1270 util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
1271 1271 updated += 1
1272 1272
1273 1273 # the ordering is important here -- ms.mergedriver will raise if the merge
1274 1274 # driver has changed, and we want to be able to bypass it when overwrite is
1275 1275 # True
1276 1276 usemergedriver = not overwrite and mergeactions and ms.mergedriver
1277 1277
1278 1278 if usemergedriver:
1279 1279 ms.commit()
1280 1280 proceed = driverpreprocess(repo, ms, wctx, labels=labels)
1281 1281 # the driver might leave some files unresolved
1282 1282 unresolvedf = set(ms.unresolved())
1283 1283 if not proceed:
1284 1284 # XXX setting unresolved to at least 1 is a hack to make sure we
1285 1285 # error out
1286 1286 return updated, merged, removed, max(len(unresolvedf), 1)
1287 1287 newactions = []
1288 1288 for f, args, msg in mergeactions:
1289 1289 if f in unresolvedf:
1290 1290 newactions.append((f, args, msg))
1291 1291 mergeactions = newactions
1292 1292
1293 1293 # premerge
1294 1294 tocomplete = []
1295 1295 for f, args, msg in mergeactions:
1296 1296 repo.ui.debug(" %s: %s -> m (premerge)\n" % (f, msg))
1297 1297 z += 1
1298 1298 progress(_updating, z, item=f, total=numupdates, unit=_files)
1299 1299 if f == '.hgsubstate': # subrepo states need updating
1300 1300 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
1301 1301 overwrite, labels)
1302 1302 continue
1303 1303 audit(f)
1304 1304 complete, r = ms.preresolve(f, wctx)
1305 1305 if not complete:
1306 1306 numupdates += 1
1307 1307 tocomplete.append((f, args, msg))
1308 1308
1309 1309 # merge
1310 1310 for f, args, msg in tocomplete:
1311 1311 repo.ui.debug(" %s: %s -> m (merge)\n" % (f, msg))
1312 1312 z += 1
1313 1313 progress(_updating, z, item=f, total=numupdates, unit=_files)
1314 1314 ms.resolve(f, wctx)
1315 1315
1316 1316 ms.commit()
1317 1317
1318 1318 unresolved = ms.unresolvedcount()
1319 1319
1320 1320 if usemergedriver and not unresolved and ms.mdstate() != 's':
1321 1321 if not driverconclude(repo, ms, wctx, labels=labels):
1322 1322 # XXX setting unresolved to at least 1 is a hack to make sure we
1323 1323 # error out
1324 1324 unresolved = max(unresolved, 1)
1325 1325
1326 1326 ms.commit()
1327 1327
1328 1328 msupdated, msmerged, msremoved = ms.counts()
1329 1329 updated += msupdated
1330 1330 merged += msmerged
1331 1331 removed += msremoved
1332 1332
1333 1333 extraactions = ms.actions()
1334 1334 if extraactions:
1335 1335 mfiles = set(a[0] for a in actions['m'])
1336 1336 for k, acts in extraactions.iteritems():
1337 1337 actions[k].extend(acts)
1338 1338 # Remove these files from actions['m'] as well. This is important
1339 1339 # because in recordupdates, files in actions['m'] are processed
1340 1340 # after files in other actions, and the merge driver might add
1341 1341 # files to those actions via extraactions above. This can lead to a
1342 1342 # file being recorded twice, with poor results. This is especially
1343 1343 # problematic for actions['r'] (currently only possible with the
1344 1344 # merge driver in the initial merge process; interrupted merges
1345 1345 # don't go through this flow).
1346 1346 #
1347 1347 # The real fix here is to have indexes by both file and action so
1348 1348 # that when the action for a file is changed it is automatically
1349 1349 # reflected in the other action lists. But that involves a more
1350 1350 # complex data structure, so this will do for now.
1351 1351 #
1352 1352 # We don't need to do the same operation for 'dc' and 'cd' because
1353 1353 # those lists aren't consulted again.
1354 1354 mfiles.difference_update(a[0] for a in acts)
1355 1355
1356 1356 actions['m'] = [a for a in actions['m'] if a[0] in mfiles]
1357 1357
1358 1358 progress(_updating, None, total=numupdates, unit=_files)
1359 1359
1360 1360 return updated, merged, removed, unresolved
1361 1361
1362 1362 def recordupdates(repo, actions, branchmerge):
1363 1363 "record merge actions to the dirstate"
1364 1364 # remove (must come first)
1365 1365 for f, args, msg in actions.get('r', []):
1366 1366 if branchmerge:
1367 1367 repo.dirstate.remove(f)
1368 1368 else:
1369 1369 repo.dirstate.drop(f)
1370 1370
1371 1371 # forget (must come first)
1372 1372 for f, args, msg in actions.get('f', []):
1373 1373 repo.dirstate.drop(f)
1374 1374
1375 1375 # re-add
1376 1376 for f, args, msg in actions.get('a', []):
1377 1377 repo.dirstate.add(f)
1378 1378
1379 1379 # re-add/mark as modified
1380 1380 for f, args, msg in actions.get('am', []):
1381 1381 if branchmerge:
1382 1382 repo.dirstate.normallookup(f)
1383 1383 else:
1384 1384 repo.dirstate.add(f)
1385 1385
1386 1386 # exec change
1387 1387 for f, args, msg in actions.get('e', []):
1388 1388 repo.dirstate.normallookup(f)
1389 1389
1390 1390 # keep
1391 1391 for f, args, msg in actions.get('k', []):
1392 1392 pass
1393 1393
1394 1394 # get
1395 1395 for f, args, msg in actions.get('g', []):
1396 1396 if branchmerge:
1397 1397 repo.dirstate.otherparent(f)
1398 1398 else:
1399 1399 repo.dirstate.normal(f)
1400 1400
1401 1401 # merge
1402 1402 for f, args, msg in actions.get('m', []):
1403 1403 f1, f2, fa, move, anc = args
1404 1404 if branchmerge:
1405 1405 # We've done a branch merge, mark this file as merged
1406 1406 # so that we properly record the merger later
1407 1407 repo.dirstate.merge(f)
1408 1408 if f1 != f2: # copy/rename
1409 1409 if move:
1410 1410 repo.dirstate.remove(f1)
1411 1411 if f1 != f:
1412 1412 repo.dirstate.copy(f1, f)
1413 1413 else:
1414 1414 repo.dirstate.copy(f2, f)
1415 1415 else:
1416 1416 # We've update-merged a locally modified file, so
1417 1417 # we set the dirstate to emulate a normal checkout
1418 1418 # of that file some time in the past. Thus our
1419 1419 # merge will appear as a normal local file
1420 1420 # modification.
1421 1421 if f2 == f: # file not locally copied/moved
1422 1422 repo.dirstate.normallookup(f)
1423 1423 if move:
1424 1424 repo.dirstate.drop(f1)
1425 1425
1426 1426 # directory rename, move local
1427 1427 for f, args, msg in actions.get('dm', []):
1428 1428 f0, flag = args
1429 1429 if branchmerge:
1430 1430 repo.dirstate.add(f)
1431 1431 repo.dirstate.remove(f0)
1432 1432 repo.dirstate.copy(f0, f)
1433 1433 else:
1434 1434 repo.dirstate.normal(f)
1435 1435 repo.dirstate.drop(f0)
1436 1436
1437 1437 # directory rename, get
1438 1438 for f, args, msg in actions.get('dg', []):
1439 1439 f0, flag = args
1440 1440 if branchmerge:
1441 1441 repo.dirstate.add(f)
1442 1442 repo.dirstate.copy(f0, f)
1443 1443 else:
1444 1444 repo.dirstate.normal(f)
1445 1445
1446 1446 def update(repo, node, branchmerge, force, ancestor=None,
1447 1447 mergeancestor=False, labels=None, matcher=None, mergeforce=False):
1448 1448 """
1449 1449 Perform a merge between the working directory and the given node
1450 1450
1451 1451 node = the node to update to
1452 1452 branchmerge = whether to merge between branches
1453 1453 force = whether to force branch merging or file overwriting
1454 1454 matcher = a matcher to filter file lists (dirstate not updated)
1455 1455 mergeancestor = whether it is merging with an ancestor. If true,
1456 1456 we should accept the incoming changes for any prompts that occur.
1457 1457 If false, merging with an ancestor (fast-forward) is only allowed
1458 1458 between different named branches. This flag is used by rebase extension
1459 1459 as a temporary fix and should be avoided in general.
1460 1460 labels = labels to use for base, local and other
1461 1461 mergeforce = whether the merge was run with 'merge --force' (deprecated): if
1462 1462 this is True, then 'force' should be True as well.
1463 1463
1464 1464 The table below shows all the behaviors of the update command
1465 1465 given the -c and -C or no options, whether the working directory
1466 1466 is dirty, whether a revision is specified, and the relationship of
1467 1467 the parent rev to the target rev (linear, on the same named
1468 1468 branch, or on another named branch).
1469 1469
1470 1470 This logic is tested by test-update-branches.t.
1471 1471
1472 1472 -c -C dirty rev | linear same cross
1473 1473 n n n n | ok (1) x
1474 1474 n n n y | ok ok ok
1475 1475 n n y n | merge (2) (2)
1476 1476 n n y y | merge (3) (3)
1477 1477 n y * * | discard discard discard
1478 1478 y n y * | (4) (4) (4)
1479 1479 y n n * | ok ok ok
1480 1480 y y * * | (5) (5) (5)
1481 1481
1482 1482 x = can't happen
1483 1483 * = don't-care
1484 1484 1 = abort: not a linear update (merge or update --check to force update)
1485 1485 2 = abort: uncommitted changes (commit and merge, or update --clean to
1486 1486 discard changes)
1487 1487 3 = abort: uncommitted changes (commit or update --clean to discard changes)
1488 1488 4 = abort: uncommitted changes (checked in commands.py)
1489 1489 5 = incompatible options (checked in commands.py)
1490 1490
1491 1491 Return the same tuple as applyupdates().
1492 1492 """
1493 1493
1494 1494 # This functon used to find the default destination if node was None, but
1495 1495 # that's now in destutil.py.
1496 1496 assert node is not None
1497 1497 # If we're doing a partial update, we need to skip updating
1498 1498 # the dirstate, so make a note of any partial-ness to the
1499 1499 # update here.
1500 1500 if matcher is None or matcher.always():
1501 1501 partial = False
1502 1502 else:
1503 1503 partial = True
1504 1504 with repo.wlock():
1505 1505 wc = repo[None]
1506 1506 pl = wc.parents()
1507 1507 p1 = pl[0]
1508 1508 pas = [None]
1509 1509 if ancestor is not None:
1510 1510 pas = [repo[ancestor]]
1511 1511
1512 1512 overwrite = force and not branchmerge
1513 1513
1514 1514 p2 = repo[node]
1515 1515 if pas[0] is None:
1516 1516 if repo.ui.configlist('merge', 'preferancestor', ['*']) == ['*']:
1517 1517 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
1518 1518 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
1519 1519 else:
1520 1520 pas = [p1.ancestor(p2, warn=branchmerge)]
1521 1521
1522 1522 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
1523 1523
1524 1524 ### check phase
1525 1525 if not overwrite:
1526 1526 if len(pl) > 1:
1527 1527 raise error.Abort(_("outstanding uncommitted merge"))
1528 1528 ms = mergestate.read(repo)
1529 1529 if list(ms.unresolved()):
1530 1530 raise error.Abort(_("outstanding merge conflicts"))
1531 1531 if branchmerge:
1532 1532 if pas == [p2]:
1533 1533 raise error.Abort(_("merging with a working directory ancestor"
1534 1534 " has no effect"))
1535 1535 elif pas == [p1]:
1536 1536 if not mergeancestor and p1.branch() == p2.branch():
1537 1537 raise error.Abort(_("nothing to merge"),
1538 1538 hint=_("use 'hg update' "
1539 1539 "or check 'hg heads'"))
1540 1540 if not force and (wc.files() or wc.deleted()):
1541 1541 raise error.Abort(_("uncommitted changes"),
1542 1542 hint=_("use 'hg status' to list changes"))
1543 1543 for s in sorted(wc.substate):
1544 1544 wc.sub(s).bailifchanged()
1545 1545
1546 1546 elif not overwrite:
1547 1547 if p1 == p2: # no-op update
1548 1548 # call the hooks and exit early
1549 1549 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
1550 1550 repo.hook('update', parent1=xp2, parent2='', error=0)
1551 1551 return 0, 0, 0, 0
1552 1552
1553 1553 if pas not in ([p1], [p2]): # nonlinear
1554 1554 dirty = wc.dirty(missing=True)
1555 1555 if dirty:
1556 1556 # Branching is a bit strange to ensure we do the minimal
1557 1557 # amount of call to obsolete.foreground.
1558 1558 foreground = obsolete.foreground(repo, [p1.node()])
1559 1559 # note: the <node> variable contains a random identifier
1560 1560 if repo[node].node() in foreground:
1561 1561 pass # allow updating to successors
1562 1562 else:
1563 1563 msg = _("uncommitted changes")
1564 1564 hint = _("commit or update --clean to discard changes")
1565 raise error.Abort(msg, hint=hint)
1565 raise error.UpdateAbort(msg, hint=hint)
1566 1566 else:
1567 1567 # Allow jumping branches if clean and specific rev given
1568 1568 pass
1569 1569
1570 1570 if overwrite:
1571 1571 pas = [wc]
1572 1572 elif not branchmerge:
1573 1573 pas = [p1]
1574 1574
1575 1575 # deprecated config: merge.followcopies
1576 1576 followcopies = repo.ui.configbool('merge', 'followcopies', True)
1577 1577 if overwrite:
1578 1578 followcopies = False
1579 1579 elif not pas[0]:
1580 1580 followcopies = False
1581 1581 if not branchmerge and not wc.dirty(missing=True):
1582 1582 followcopies = False
1583 1583
1584 1584 ### calculate phase
1585 1585 actionbyfile, diverge, renamedelete = calculateupdates(
1586 1586 repo, wc, p2, pas, branchmerge, force, mergeancestor,
1587 1587 followcopies, matcher=matcher, mergeforce=mergeforce)
1588 1588
1589 1589 # Prompt and create actions. Most of this is in the resolve phase
1590 1590 # already, but we can't handle .hgsubstate in filemerge or
1591 1591 # subrepo.submerge yet so we have to keep prompting for it.
1592 1592 if '.hgsubstate' in actionbyfile:
1593 1593 f = '.hgsubstate'
1594 1594 m, args, msg = actionbyfile[f]
1595 1595 prompts = filemerge.partextras(labels)
1596 1596 prompts['f'] = f
1597 1597 if m == 'cd':
1598 1598 if repo.ui.promptchoice(
1599 1599 _("local%(l)s changed %(f)s which other%(o)s deleted\n"
1600 1600 "use (c)hanged version or (d)elete?"
1601 1601 "$$ &Changed $$ &Delete") % prompts, 0):
1602 1602 actionbyfile[f] = ('r', None, "prompt delete")
1603 1603 elif f in p1:
1604 1604 actionbyfile[f] = ('am', None, "prompt keep")
1605 1605 else:
1606 1606 actionbyfile[f] = ('a', None, "prompt keep")
1607 1607 elif m == 'dc':
1608 1608 f1, f2, fa, move, anc = args
1609 1609 flags = p2[f2].flags()
1610 1610 if repo.ui.promptchoice(
1611 1611 _("other%(o)s changed %(f)s which local%(l)s deleted\n"
1612 1612 "use (c)hanged version or leave (d)eleted?"
1613 1613 "$$ &Changed $$ &Deleted") % prompts, 0) == 0:
1614 1614 actionbyfile[f] = ('g', (flags, False), "prompt recreating")
1615 1615 else:
1616 1616 del actionbyfile[f]
1617 1617
1618 1618 # Convert to dictionary-of-lists format
1619 1619 actions = dict((m, []) for m in 'a am f g cd dc r dm dg m e k'.split())
1620 1620 for f, (m, args, msg) in actionbyfile.iteritems():
1621 1621 if m not in actions:
1622 1622 actions[m] = []
1623 1623 actions[m].append((f, args, msg))
1624 1624
1625 1625 if not util.fscasesensitive(repo.path):
1626 1626 # check collision between files only in p2 for clean update
1627 1627 if (not branchmerge and
1628 1628 (force or not wc.dirty(missing=True, branch=False))):
1629 1629 _checkcollision(repo, p2.manifest(), None)
1630 1630 else:
1631 1631 _checkcollision(repo, wc.manifest(), actions)
1632 1632
1633 1633 # divergent renames
1634 1634 for f, fl in sorted(diverge.iteritems()):
1635 1635 repo.ui.warn(_("note: possible conflict - %s was renamed "
1636 1636 "multiple times to:\n") % f)
1637 1637 for nf in fl:
1638 1638 repo.ui.warn(" %s\n" % nf)
1639 1639
1640 1640 # rename and delete
1641 1641 for f, fl in sorted(renamedelete.iteritems()):
1642 1642 repo.ui.warn(_("note: possible conflict - %s was deleted "
1643 1643 "and renamed to:\n") % f)
1644 1644 for nf in fl:
1645 1645 repo.ui.warn(" %s\n" % nf)
1646 1646
1647 1647 ### apply phase
1648 1648 if not branchmerge: # just jump to the new rev
1649 1649 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
1650 1650 if not partial:
1651 1651 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
1652 1652 # note that we're in the middle of an update
1653 1653 repo.vfs.write('updatestate', p2.hex())
1654 1654
1655 1655 stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels)
1656 1656
1657 1657 if not partial:
1658 1658 repo.dirstate.beginparentchange()
1659 1659 repo.setparents(fp1, fp2)
1660 1660 recordupdates(repo, actions, branchmerge)
1661 1661 # update completed, clear state
1662 1662 util.unlink(repo.join('updatestate'))
1663 1663
1664 1664 if not branchmerge:
1665 1665 repo.dirstate.setbranch(p2.branch())
1666 1666 repo.dirstate.endparentchange()
1667 1667
1668 1668 if not partial:
1669 1669 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
1670 1670 return stats
1671 1671
1672 1672 def graft(repo, ctx, pctx, labels, keepparent=False):
1673 1673 """Do a graft-like merge.
1674 1674
1675 1675 This is a merge where the merge ancestor is chosen such that one
1676 1676 or more changesets are grafted onto the current changeset. In
1677 1677 addition to the merge, this fixes up the dirstate to include only
1678 1678 a single parent (if keepparent is False) and tries to duplicate any
1679 1679 renames/copies appropriately.
1680 1680
1681 1681 ctx - changeset to rebase
1682 1682 pctx - merge base, usually ctx.p1()
1683 1683 labels - merge labels eg ['local', 'graft']
1684 1684 keepparent - keep second parent if any
1685 1685
1686 1686 """
1687 1687 # If we're grafting a descendant onto an ancestor, be sure to pass
1688 1688 # mergeancestor=True to update. This does two things: 1) allows the merge if
1689 1689 # the destination is the same as the parent of the ctx (so we can use graft
1690 1690 # to copy commits), and 2) informs update that the incoming changes are
1691 1691 # newer than the destination so it doesn't prompt about "remote changed foo
1692 1692 # which local deleted".
1693 1693 mergeancestor = repo.changelog.isancestor(repo['.'].node(), ctx.node())
1694 1694
1695 1695 stats = update(repo, ctx.node(), True, True, pctx.node(),
1696 1696 mergeancestor=mergeancestor, labels=labels)
1697 1697
1698 1698 pother = nullid
1699 1699 parents = ctx.parents()
1700 1700 if keepparent and len(parents) == 2 and pctx in parents:
1701 1701 parents.remove(pctx)
1702 1702 pother = parents[0].node()
1703 1703
1704 1704 repo.dirstate.beginparentchange()
1705 1705 repo.setparents(repo['.'].node(), pother)
1706 1706 repo.dirstate.write(repo.currenttransaction())
1707 1707 # fix up dirstate for copies and renames
1708 1708 copies.duplicatecopies(repo, ctx.rev(), pctx.rev())
1709 1709 repo.dirstate.endparentchange()
1710 1710 return stats
General Comments 0
You need to be logged in to leave comments. Login now