##// END OF EJS Templates
merge: don't try to merge subrepos twice (issue4988)...
merge: don't try to merge subrepos twice (issue4988) In my patch series ending with rev 25e4b2f000c5 I switched most change/delete conflicts to be handled at the resolve layer. .hgsubstate was the one file that we weren't able to handle, so we kept the old code path around for it. The old code path added .hgsubstate to one of the other lists as the user specifies, including possibly the 'g' list. Now since we did this check after converting the actions from being keyed by file to being keyed by action type, there was nothing that actually removed .hgsubstate from the 'cd' or 'dc' lists. This meant that the file would eventually make its way into the 'mergeactions' list, now freshly augmented with 'cd' and 'dc' actions. We call subrepo.submerge for both 'g' actions and merge actions. This means that if the resolution to an .hgsubstate change/delete conflict was to add it to the 'g' list, subrepo.submerge would be called twice. It turns out that this doesn't cause any adverse effects on Linux due to caching, but apparently breaks on other operating systems including Windows. The fix here moves this to before we convert the actions over. This ensures that it .hgsubstate doesn't make its way into multiple lists. The real fix here is going to be: (1) move .hgsubstate conflict resolution into the resolve layer, and (2) use a real data structure for the actions rather than shuffling data around between lists and dictionaries: we need a hash (or prefix-based) index by file and a list index by action type. There's a very tiny behavior change here: collision detection on case-insensitive systems will happen after this is resolved, not before. I think this is the right change -- .hgsubstate could theoretically collide with other files -- but in any case it makes no practical difference. Thanks to Yuya Nishihara for investigating this.

File last commit:

r27951:6bce6d92 stable
r27951:6bce6d92 stable
Show More
merge.py
1587 lines | 58.4 KiB | text/x-python | PythonLexer
Matt Mackall
Move merge code to its own module...
r2775 # merge.py - directory-level update/merge handling for Mercurial
#
Thomas Arendsen Hein
Updated copyright notices and add "and others" to "hg version"
r4635 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
Matt Mackall
Move merge code to its own module...
r2775 #
Martin Geisler
updated license to be explicit about GPL version 2
r8225 # This software may be used and distributed according to the terms of the
Matt Mackall
Update license to GPLv2+
r10263 # GNU General Public License version 2 or any later version.
Matt Mackall
Move merge code to its own module...
r2775
Gregory Szorc
merge: use absolute_import
r25959 from __future__ import absolute_import
import errno
import os
import shutil
Pierre-Yves David
merge: introduce new format for the state file...
r20590 import struct
Gregory Szorc
merge: use absolute_import
r25959 from .i18n import _
from .node import (
bin,
hex,
Siddharth Agarwal
mergestate: allow storing and retrieving change/delete conflicts...
r27031 nullhex,
Gregory Szorc
merge: use absolute_import
r25959 nullid,
nullrev,
)
from . import (
copies,
Pierre-Yves David
merge: get the default update destination from the function...
r26570 destutil,
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 error,
Gregory Szorc
merge: use absolute_import
r25959 filemerge,
obsolete,
Siddharth Agarwal
batchget: add support for backing up files...
r27656 scmutil,
Gregory Szorc
merge: use absolute_import
r25959 subrepo,
util,
worker,
)
Matt Mackall
merge: introduce mergestate
r6512
Pierre-Yves David
merge: introduce new format for the state file...
r20590 _pack = struct.pack
_unpack = struct.unpack
Pierre-Yves David
merge: add "other" file node in the merge state file...
r20593 def _droponode(data):
# used for compatibility for v1
Martin von Zweigbergk
merge: consistently use single quotes for non-user-facing strings...
r23380 bits = data.split('\0')
Pierre-Yves David
merge: add "other" file node in the merge state file...
r20593 bits = bits[:-2] + bits[-1:]
Martin von Zweigbergk
merge: consistently use single quotes for non-user-facing strings...
r23380 return '\0'.join(bits)
Pierre-Yves David
merge: add "other" file node in the merge state file...
r20593
Matt Mackall
merge: introduce mergestate
r6512 class mergestate(object):
Pierre-Yves David
merge: introduce new format for the state file...
r20590 '''track 3-way merge state of individual files
Siddharth Agarwal
mergestate: move binary format documentation into _readrecordsv2...
r27022 The merge state is stored on disk when needed. Two files are used: one with
an old format (version 1), and one with a new format (version 2). Version 2
stores a superset of the data in version 1, including new kinds of records
in the future. For more about the new format, see the documentation for
`_readrecordsv2`.
Pierre-Yves David
merge: introduce new format for the state file...
r20590
Siddharth Agarwal
mergestate: move binary format documentation into _readrecordsv2...
r27022 Each record can contain arbitrary content, and has an associated type. This
`type` should be a letter. If `type` is uppercase, the record is mandatory:
versions of Mercurial that don't support it should abort. If `type` is
lowercase, the record can be safely ignored.
Pierre-Yves David
merge: introduce new format for the state file...
r20590
Siddharth Agarwal
mergestate: move binary format documentation into _readrecordsv2...
r27022 Currently known records:
Pierre-Yves David
merge: introduce new format for the state file...
r20590
L: the node of the "local" part of the merge (hexified version)
Pierre-Yves David
merge: record the "other" node in merge state...
r20591 O: the node of the "other" part of the merge (hexified version)
Pierre-Yves David
merge: introduce new format for the state file...
r20590 F: a file to be merged entry
Siddharth Agarwal
mergestate: allow storing and retrieving change/delete conflicts...
r27031 C: a change/delete or delete/change conflict
Siddharth Agarwal
merge.mergestate: add support for persisting driver-resolved files...
r26650 D: a file that the external merge driver will merge internally
(experimental)
Siddharth Agarwal
merge.mergestate: add support for persisting a custom merge driver...
r26649 m: the external merge driver defined for this merge plus its run state
(experimental)
Siddharth Agarwal
mergestate: handle additional record types specially...
r27027 X: unsupported mandatory record type (used in tests)
x: unsupported advisory record type (used in tests)
Siddharth Agarwal
merge.mergestate: add support for persisting a custom merge driver...
r26649
Merge driver run states (experimental):
u: driver-resolved files unmarked -- needs to be run next time we're about
to resolve or commit
m: driver-resolved files marked -- only needs to be run before commit
s: success/skipped -- does not need to be run any more
Siddharth Agarwal
mergestate: move binary format documentation into _readrecordsv2...
r27022
Pierre-Yves David
merge: introduce new format for the state file...
r20590 '''
Martin von Zweigbergk
merge: consistently use single quotes for non-user-facing strings...
r23380 statepathv1 = 'merge/state'
statepathv2 = 'merge/state2'
Pierre-Yves David
merge: add blank line between mergestate's method...
r20651
Siddharth Agarwal
mergestate: add a constructor that sets up a clean merge state...
r26987 @staticmethod
def clean(repo, node=None, other=None):
"""Initialize a brand new merge state, removing any existing state on
disk."""
ms = mergestate(repo)
ms.reset(node, other)
return ms
Siddharth Agarwal
mergestate: add a constructor that reads state from disk...
r26991 @staticmethod
def read(repo):
"""Initialize the merge state, reading it from disk."""
ms = mergestate(repo)
Siddharth Agarwal
mergestate: move _read() call to read constructor...
r27005 ms._read()
Siddharth Agarwal
mergestate: add a constructor that reads state from disk...
r26991 return ms
Matt Mackall
merge: introduce mergestate
r6512 def __init__(self, repo):
Siddharth Agarwal
mergestate: move _read() call to read constructor...
r27005 """Initialize the merge state.
Do not use this directly! Instead call read() or clean()."""
Matt Mackall
merge: introduce mergestate
r6512 self._repo = repo
Peter Arrenbrecht
merge: delay writing the mergestate during until commit is called...
r12369 self._dirty = False
Pierre-Yves David
merge: add blank line between mergestate's method...
r20651
Pierre-Yves David
merge: record the "other" node in merge state...
r20591 def reset(self, node=None, other=None):
Matt Mackall
merge: introduce mergestate
r6512 self._state = {}
Gregory Szorc
mergestate: consistently set variables to None...
r21261 self._local = None
self._other = None
Siddharth Agarwal
mergestate: add a cached property accessor for the local context...
r27130 for var in ('localctx', 'otherctx'):
if var in vars(self):
delattr(self, var)
Matt Mackall
resolve: move reset to localrepo.commit...
r7848 if node:
self._local = node
Pierre-Yves David
merge: record the "other" node in merge state...
r20591 self._other = other
Siddharth Agarwal
merge.mergestate: only check for merge driver when property is accessed...
r26768 self._readmergedriver = None
Siddharth Agarwal
merge.mergestate: set merge driver state to 's' if there's none present...
r26769 if self.mergedriver:
self._mdstate = 's'
else:
self._mdstate = 'u'
Martin von Zweigbergk
merge: consistently use single quotes for non-user-facing strings...
r23380 shutil.rmtree(self._repo.join('merge'), True)
Siddharth Agarwal
mergestate._resolve: store return code and action for each file...
r27074 self._results = {}
Peter Arrenbrecht
merge: delay writing the mergestate during until commit is called...
r12369 self._dirty = False
Pierre-Yves David
merge: add blank line between mergestate's method...
r20651
Matt Mackall
resolve: new command...
r6518 def _read(self):
Pierre-Yves David
merge: adds documentation to the mergestate class...
r20652 """Analyse each record content to restore a serialized state from disk
This function process "record" entry produced by the de-serialization
of on disk file.
"""
Matt Mackall
resolve: new command...
r6518 self._state = {}
Gregory Szorc
mergestate: consistently set variables to None...
r21261 self._local = None
self._other = None
Siddharth Agarwal
mergestate: add a cached property accessor for the local context...
r27130 for var in ('localctx', 'otherctx'):
if var in vars(self):
delattr(self, var)
Siddharth Agarwal
merge.mergestate: only check for merge driver when property is accessed...
r26768 self._readmergedriver = None
Siddharth Agarwal
merge.mergestate: set merge driver state to 's' if there's none present...
r26769 self._mdstate = 's'
Siddharth Agarwal
mergestate: raise structured exception for unsupported merge records...
r26986 unsupported = set()
Pierre-Yves David
merge: change the merge state serialisation to use a record based logic...
r20589 records = self._readrecords()
for rtype, record in records:
if rtype == 'L':
self._local = bin(record)
Pierre-Yves David
merge: record the "other" node in merge state...
r20591 elif rtype == 'O':
self._other = bin(record)
Siddharth Agarwal
merge.mergestate: add support for persisting a custom merge driver...
r26649 elif rtype == 'm':
bits = record.split('\0', 1)
mdstate = bits[1]
if len(mdstate) != 1 or mdstate not in 'ums':
# the merge driver should be idempotent, so just rerun it
mdstate = 'u'
Siddharth Agarwal
merge.mergestate: only check for merge driver when property is accessed...
r26768 self._readmergedriver = bits[0]
Siddharth Agarwal
merge.mergestate: add support for persisting a custom merge driver...
r26649 self._mdstate = mdstate
Siddharth Agarwal
mergestate: allow storing and retrieving change/delete conflicts...
r27031 elif rtype in 'FDC':
Martin von Zweigbergk
merge: consistently use single quotes for non-user-facing strings...
r23380 bits = record.split('\0')
Pierre-Yves David
merge: change the merge state serialisation to use a record based logic...
r20589 self._state[bits[0]] = bits[1:]
elif not rtype.islower():
Siddharth Agarwal
mergestate: raise structured exception for unsupported merge records...
r26986 unsupported.add(rtype)
Siddharth Agarwal
mergestate._resolve: store return code and action for each file...
r27074 self._results = {}
Pierre-Yves David
merge: change the merge state serialisation to use a record based logic...
r20589 self._dirty = False
Pierre-Yves David
merge: add blank line between mergestate's method...
r20651
Siddharth Agarwal
mergestate: raise structured exception for unsupported merge records...
r26986 if unsupported:
raise error.UnsupportedMergeRecords(unsupported)
Pierre-Yves David
merge: change the merge state serialisation to use a record based logic...
r20589 def _readrecords(self):
Pierre-Yves David
merge: adds documentation to the mergestate class...
r20652 """Read merge state from disk and return a list of record (TYPE, data)
Mads Kiilerich
spelling: fixes from spell checker
r21024 We read data from both v1 and v2 files and decide which one to use.
Pierre-Yves David
merge: adds documentation to the mergestate class...
r20652
Mads Kiilerich
spelling: fixes from spell checker
r21024 V1 has been used by version prior to 2.9.1 and contains less data than
v2. We read both versions and check if no data in v2 contradicts
Pierre-Yves David
merge: adds documentation to the mergestate class...
r20652 v1. If there is not contradiction we can safely assume that both v1
and v2 were written at the same time and use the extract data in v2. If
there is contradiction we ignore v2 content as we assume an old version
Mads Kiilerich
spelling: fixes from spell checker
r21024 of Mercurial has overwritten the mergestate file and left an old v2
Pierre-Yves David
merge: adds documentation to the mergestate class...
r20652 file around.
returns list of record [(TYPE, data), ...]"""
Pierre-Yves David
merge: introduce new format for the state file...
r20590 v1records = self._readrecordsv1()
v2records = self._readrecordsv2()
Siddharth Agarwal
merge.mergestate: factor out code to validate v1/v2 records...
r26500 if self._v1v2match(v1records, v2records):
return v2records
else:
# v1 file is newer than v2 file, use it
# we have to infer the "other" changeset of the merge
# we cannot do better than that with v1 of the format
mctx = self._repo[None].parents()[-1]
v1records.append(('O', mctx.hex()))
# add place holder "other" file node information
# nobody is using it yet so we do no need to fetch the data
# if mctx was wrong `mctx[bits[-2]]` may fails.
for idx, r in enumerate(v1records):
if r[0] == 'F':
bits = r[1].split('\0')
bits.insert(-2, '')
v1records[idx] = (r[0], '\0'.join(bits))
return v1records
def _v1v2match(self, v1records, v2records):
Pierre-Yves David
merge: add "other" file node in the merge state file...
r20593 oldv2 = set() # old format version of v2 record
for rec in v2records:
if rec[0] == 'L':
oldv2.add(rec)
elif rec[0] == 'F':
# drop the onode data (not contained in v1)
oldv2.add(('F', _droponode(rec[1])))
for rec in v1records:
if rec not in oldv2:
Siddharth Agarwal
merge.mergestate: factor out code to validate v1/v2 records...
r26500 return False
Pierre-Yves David
merge: introduce new format for the state file...
r20590 else:
Siddharth Agarwal
merge.mergestate: factor out code to validate v1/v2 records...
r26500 return True
Pierre-Yves David
merge: add blank line between mergestate's method...
r20651
Pierre-Yves David
merge: introduce new format for the state file...
r20590 def _readrecordsv1(self):
Pierre-Yves David
merge: adds documentation to the mergestate class...
r20652 """read on disk merge state for version 1 file
returns list of record [(TYPE, data), ...]
Note: the "F" data from this file are one entry short
(no "other file node" entry)
"""
Pierre-Yves David
merge: change the merge state serialisation to use a record based logic...
r20589 records = []
Matt Mackall
resolve: new command...
r6518 try:
Angel Ezquerra
localrepo: remove all external users of localrepo.opener...
r23877 f = self._repo.vfs(self.statepathv1)
Patrick Mezard
merge: replace readline() call, missing from posixfile_nt
r6530 for i, l in enumerate(f):
if i == 0:
Pierre-Yves David
merge: change the merge state serialisation to use a record based logic...
r20589 records.append(('L', l[:-1]))
Patrick Mezard
merge: replace readline() call, missing from posixfile_nt
r6530 else:
Pierre-Yves David
merge: change the merge state serialisation to use a record based logic...
r20589 records.append(('F', l[:-1]))
Dan Villiom Podlaski Christiansen
explicitly close files...
r13400 f.close()
Gregory Szorc
global: mass rewrite to use modern exception syntax...
r25660 except IOError as err:
Matt Mackall
resolve: new command...
r6518 if err.errno != errno.ENOENT:
raise
Pierre-Yves David
merge: change the merge state serialisation to use a record based logic...
r20589 return records
Pierre-Yves David
merge: add blank line between mergestate's method...
r20651
Pierre-Yves David
merge: introduce new format for the state file...
r20590 def _readrecordsv2(self):
Pierre-Yves David
merge: adds documentation to the mergestate class...
r20652 """read on disk merge state for version 2 file
Siddharth Agarwal
mergestate: move binary format documentation into _readrecordsv2...
r27022 This format is a list of arbitrary records of the form:
[type][length][content]
`type` is a single character, `length` is a 4 byte integer, and
`content` is an arbitrary byte sequence of length `length`.
Siddharth Agarwal
mergestate: handle additional record types specially...
r27027 Mercurial versions prior to 3.7 have a bug where if there are
unsupported mandatory merge records, attempting to clear out the merge
state with hg update --clean or similar aborts. The 't' record type
works around that by writing out what those versions treat as an
advisory record, but later versions interpret as special: the first
character is the 'real' record type and everything onwards is the data.
Siddharth Agarwal
mergestate: move binary format documentation into _readrecordsv2...
r27022 Returns list of records [(TYPE, data), ...]."""
Pierre-Yves David
merge: introduce new format for the state file...
r20590 records = []
try:
Angel Ezquerra
localrepo: remove all external users of localrepo.opener...
r23877 f = self._repo.vfs(self.statepathv2)
Pierre-Yves David
merge: introduce new format for the state file...
r20590 data = f.read()
off = 0
end = len(data)
while off < end:
rtype = data[off]
off += 1
Olle Lundberg
merge: fix spelling of length
r20607 length = _unpack('>I', data[off:(off + 4)])[0]
Pierre-Yves David
merge: introduce new format for the state file...
r20590 off += 4
Olle Lundberg
merge: fix spelling of length
r20607 record = data[off:(off + length)]
off += length
Siddharth Agarwal
mergestate: handle additional record types specially...
r27027 if rtype == 't':
rtype, record = record[0], record[1:]
Pierre-Yves David
merge: introduce new format for the state file...
r20590 records.append((rtype, record))
f.close()
Gregory Szorc
global: mass rewrite to use modern exception syntax...
r25660 except IOError as err:
Pierre-Yves David
merge: introduce new format for the state file...
r20590 if err.errno != errno.ENOENT:
raise
return records
Pierre-Yves David
merge: add blank line between mergestate's method...
r20651
Siddharth Agarwal
merge.mergestate: add support for persisting a custom merge driver...
r26649 @util.propertycache
def mergedriver(self):
Siddharth Agarwal
merge.mergestate: only check for merge driver when property is accessed...
r26768 # protect against the following:
# - A configures a malicious merge driver in their hgrc, then
# pauses the merge
# - A edits their hgrc to remove references to the merge driver
# - A gives a copy of their entire repo, including .hg, to B
# - B inspects .hgrc and finds it to be clean
# - B then continues the merge and the malicious merge driver
# gets invoked
configmergedriver = self._repo.ui.config('experimental', 'mergedriver')
if (self._readmergedriver is not None
and self._readmergedriver != configmergedriver):
raise error.ConfigError(
_("merge driver changed since merge started"),
hint=_("revert merge driver change or abort merge"))
return configmergedriver
Siddharth Agarwal
merge.mergestate: add a way to get the other side of the merge...
r26765 @util.propertycache
Siddharth Agarwal
mergestate: add a cached property accessor for the local context...
r27130 def localctx(self):
if self._local is None:
raise RuntimeError("localctx accessed but self._local isn't set")
return self._repo[self._local]
@util.propertycache
Siddharth Agarwal
merge.mergestate: add a way to get the other side of the merge...
r26765 def otherctx(self):
Siddharth Agarwal
mergestate: raise exception if otherctx is accessed but _other isn't set...
r27129 if self._other is None:
raise RuntimeError("localctx accessed but self._local isn't set")
Siddharth Agarwal
merge.mergestate: add a way to get the other side of the merge...
r26765 return self._repo[self._other]
Siddharth Agarwal
merge.mergestate: add support for persisting a custom merge driver...
r26649
Gregory Szorc
resolve: abort when not applicable (BC)...
r21264 def active(self):
"""Whether mergestate is active.
Returns True if there appears to be mergestate. This is a rough proxy
for "is a merge in progress."
"""
# Check local variables before looking at filesystem for performance
# reasons.
return bool(self._local) or bool(self._state) or \
Angel Ezquerra
localrepo: remove all external users of localrepo.opener...
r23877 self._repo.vfs.exists(self.statepathv1) or \
self._repo.vfs.exists(self.statepathv2)
Gregory Szorc
resolve: abort when not applicable (BC)...
r21264
Peter Arrenbrecht
merge: delay writing the mergestate during until commit is called...
r12369 def commit(self):
Pierre-Yves David
merge: adds documentation to the mergestate class...
r20652 """Write current state on disk (if necessary)"""
Peter Arrenbrecht
merge: delay writing the mergestate during until commit is called...
r12369 if self._dirty:
Siddharth Agarwal
mergestate.commit: factor out making the list of records...
r27006 records = self._makerecords()
Pierre-Yves David
merge: change the merge state serialisation to use a record based logic...
r20589 self._writerecords(records)
Peter Arrenbrecht
merge: delay writing the mergestate during until commit is called...
r12369 self._dirty = False
Pierre-Yves David
merge: add blank line between mergestate's method...
r20651
Siddharth Agarwal
mergestate.commit: factor out making the list of records...
r27006 def _makerecords(self):
records = []
records.append(('L', hex(self._local)))
records.append(('O', hex(self._other)))
if self.mergedriver:
records.append(('m', '\0'.join([
self.mergedriver, self._mdstate])))
for d, v in self._state.iteritems():
if v[0] == 'd':
records.append(('D', '\0'.join([d] + v)))
Siddharth Agarwal
mergestate: allow storing and retrieving change/delete conflicts...
r27031 # v[1] == local ('cd'), v[6] == other ('dc') -- not supported by
# older versions of Mercurial
elif v[1] == nullhex or v[6] == nullhex:
records.append(('C', '\0'.join([d] + v)))
Siddharth Agarwal
mergestate.commit: factor out making the list of records...
r27006 else:
records.append(('F', '\0'.join([d] + v)))
return records
Pierre-Yves David
merge: change the merge state serialisation to use a record based logic...
r20589 def _writerecords(self, records):
Pierre-Yves David
merge: adds documentation to the mergestate class...
r20652 """Write current state on disk (both v1 and v2)"""
Pierre-Yves David
merge: introduce new format for the state file...
r20590 self._writerecordsv1(records)
self._writerecordsv2(records)
Pierre-Yves David
merge: add blank line between mergestate's method...
r20651
Pierre-Yves David
merge: introduce new format for the state file...
r20590 def _writerecordsv1(self, records):
Pierre-Yves David
merge: adds documentation to the mergestate class...
r20652 """Write current state on disk in a version 1 file"""
Angel Ezquerra
localrepo: remove all external users of localrepo.opener...
r23877 f = self._repo.vfs(self.statepathv1, 'w')
Pierre-Yves David
merge: change the merge state serialisation to use a record based logic...
r20589 irecords = iter(records)
lrecords = irecords.next()
assert lrecords[0] == 'L'
Martin von Zweigbergk
merge: consistently use single quotes for non-user-facing strings...
r23380 f.write(hex(self._local) + '\n')
Pierre-Yves David
merge: change the merge state serialisation to use a record based logic...
r20589 for rtype, data in irecords:
Martin von Zweigbergk
merge: consistently use single quotes for non-user-facing strings...
r23380 if rtype == 'F':
f.write('%s\n' % _droponode(data))
Pierre-Yves David
merge: change the merge state serialisation to use a record based logic...
r20589 f.close()
Pierre-Yves David
merge: add blank line between mergestate's method...
r20651
Pierre-Yves David
merge: introduce new format for the state file...
r20590 def _writerecordsv2(self, records):
Siddharth Agarwal
mergestate: handle additional record types specially...
r27027 """Write current state on disk in a version 2 file
See the docstring for _readrecordsv2 for why we use 't'."""
# these are the records that all version 2 clients can read
whitelist = 'LOF'
Angel Ezquerra
localrepo: remove all external users of localrepo.opener...
r23877 f = self._repo.vfs(self.statepathv2, 'w')
Pierre-Yves David
merge: introduce new format for the state file...
r20590 for key, data in records:
assert len(key) == 1
Siddharth Agarwal
mergestate: handle additional record types specially...
r27027 if key not in whitelist:
key, data = 't', '%s%s' % (key, data)
Martin von Zweigbergk
merge: consistently use single quotes for non-user-facing strings...
r23380 format = '>sI%is' % len(data)
Pierre-Yves David
merge: introduce new format for the state file...
r20590 f.write(_pack(format, key, len(data), data))
f.close()
Pierre-Yves David
merge: add blank line between mergestate's method...
r20651
Mads Kiilerich
merge: merge file flags together with file content...
r18338 def add(self, fcl, fco, fca, fd):
Pierre-Yves David
merge: adds documentation to the mergestate class...
r20652 """add a new (potentially?) conflicting file the merge state
fcl: file context for local,
fco: file context for remote,
fca: file context for ancestors,
fd: file path of the resulting merge.
note: also write the local version to the `.hg/merge` directory.
"""
Siddharth Agarwal
mergestate.add: store absentfilectxes as nullhex...
r27049 if fcl.isabsent():
hash = nullhex
else:
hash = util.sha1(fcl.path()).hexdigest()
self._repo.vfs.write('merge/' + hash, fcl.data())
Pierre-Yves David
merge: add "other" file node in the merge state file...
r20593 self._state[fd] = ['u', hash, fcl.path(),
fca.path(), hex(fca.filenode()),
fco.path(), hex(fco.filenode()),
fcl.flags()]
Peter Arrenbrecht
merge: delay writing the mergestate during until commit is called...
r12369 self._dirty = True
Pierre-Yves David
merge: add blank line between mergestate's method...
r20651
Matt Mackall
merge: introduce mergestate
r6512 def __contains__(self, dfile):
return dfile in self._state
Pierre-Yves David
merge: add blank line between mergestate's method...
r20651
Matt Mackall
merge: introduce mergestate
r6512 def __getitem__(self, dfile):
Matt Mackall
resolve: new command...
r6518 return self._state[dfile][0]
Pierre-Yves David
merge: add blank line between mergestate's method...
r20651
Matt Mackall
resolve: new command...
r6518 def __iter__(self):
Mads Kiilerich
merge: simplify mergestate iter
r21268 return iter(sorted(self._state))
Pierre-Yves David
merge: add blank line between mergestate's method...
r20651
Bryan O'Sullivan
merge: add a files method to the mergestate class...
r19285 def files(self):
return self._state.keys()
Pierre-Yves David
merge: add blank line between mergestate's method...
r20651
Matt Mackall
merge: introduce mergestate
r6512 def mark(self, dfile, state):
Matt Mackall
resolve: new command...
r6518 self._state[dfile][0] = state
Peter Arrenbrecht
merge: delay writing the mergestate during until commit is called...
r12369 self._dirty = True
Pierre-Yves David
merge: add blank line between mergestate's method...
r20651
Siddharth Agarwal
merge.mergestate: add a way to get the merge driver state...
r26766 def mdstate(self):
return self._mdstate
Gregory Szorc
resolve: print message when no unresolved files remain (issue4214)...
r21266 def unresolved(self):
"""Obtain the paths of unresolved files."""
for f, entry in self._state.items():
if entry[0] == 'u':
yield f
Siddharth Agarwal
merge.mergestate: add a generator for driver-resolved files...
r26740 def driverresolved(self):
"""Obtain the paths of driver-resolved files."""
for f, entry in self._state.items():
if entry[0] == 'd':
yield f
Siddharth Agarwal
merge: introduce a preresolve function...
r26617 def _resolve(self, preresolve, dfile, wctx, labels=None):
Pierre-Yves David
merge: adds documentation to the mergestate class...
r20652 """rerun merge process for file path `dfile`"""
Siddharth Agarwal
merge.mergedriver: don't try resolving files marked driver-resolved...
r26651 if self[dfile] in 'rd':
Siddharth Agarwal
merge.mergestate._resolve: also return completed status...
r26616 return True, 0
Pierre-Yves David
merge: add "other" file node in the merge state file...
r20593 stateentry = self._state[dfile]
state, hash, lfile, afile, anode, ofile, onode, flags = stateentry
Pierre-Yves David
resolve: use "other" changeset from merge state (issue4163)...
r20594 octx = self._repo[self._other]
Siddharth Agarwal
mergestate._resolve: handle change/delete conflicts...
r27048 fcd = self._filectxorabsent(hash, wctx, dfile)
fco = self._filectxorabsent(onode, octx, ofile)
# TODO: move this to filectxorabsent
Mads Kiilerich
merge: merge file flags together with file content...
r18338 fca = self._repo.filectx(afile, fileid=anode)
# "premerge" x flags
flo = fco.flags()
fla = fca.flags()
if 'x' in flags + flo + fla and 'l' not in flags + flo + fla:
if fca.node() == nullid:
Siddharth Agarwal
merge: introduce a preresolve function...
r26617 if preresolve:
self._repo.ui.warn(
_('warning: cannot merge flags for %s\n') % afile)
Mads Kiilerich
merge: merge file flags together with file content...
r18338 elif flags == fla:
flags = flo
Siddharth Agarwal
merge: introduce a preresolve function...
r26617 if preresolve:
# restore local
Siddharth Agarwal
mergestate._resolve: handle change/delete conflicts...
r27048 if hash != nullhex:
f = self._repo.vfs('merge/' + hash)
self._repo.wwrite(dfile, f.read(), flags)
f.close()
else:
self._repo.wvfs.unlinkpath(dfile, ignoremissing=True)
Siddharth Agarwal
filemerge: return whether the file was deleted...
r27034 complete, r, deleted = filemerge.premerge(self._repo, self._local,
lfile, fcd, fco, fca,
labels=labels)
Siddharth Agarwal
merge: introduce a preresolve function...
r26617 else:
Siddharth Agarwal
filemerge: return whether the file was deleted...
r27034 complete, r, deleted = filemerge.filemerge(self._repo, self._local,
lfile, fcd, fco, fca,
labels=labels)
Matt Mackall
merge: drop resolve state for mergers with identical contents (issue2680)
r13536 if r is None:
# no real conflict
del self._state[dfile]
Mads Kiilerich
merge: mark mergestate as dirty when resolve changes _state...
r20792 self._dirty = True
Matt Mackall
merge: drop resolve state for mergers with identical contents (issue2680)
r13536 elif not r:
Matt Mackall
merge: introduce mergestate
r6512 self.mark(dfile, 'r')
Siddharth Agarwal
merge.mergestate: compute dirstate action...
r27035
if complete:
Siddharth Agarwal
mergestate._resolve: don't return the action any more...
r27075 action = None
Siddharth Agarwal
merge.mergestate: compute dirstate action...
r27035 if deleted:
Siddharth Agarwal
mergestate: explicitly forget 'dc' conflicts where the deleted side is picked...
r27122 if fcd.isabsent():
# dc: local picked. Need to drop if present, which may
# happen on re-resolves.
action = 'f'
else:
Siddharth Agarwal
merge.mergestate: compute dirstate action...
r27035 # cd: remote picked (or otherwise deleted)
action = 'r'
else:
if fcd.isabsent(): # dc: remote picked
action = 'g'
elif fco.isabsent(): # cd: local picked
Siddharth Agarwal
merge: add a new action type representing files to add/mark as modified...
r27131 if dfile in self.localctx:
action = 'am'
else:
action = 'a'
Siddharth Agarwal
merge.mergestate: compute dirstate action...
r27035 # else: regular merges (no action necessary)
Siddharth Agarwal
mergestate._resolve: store return code and action for each file...
r27074 self._results[dfile] = r, action
Siddharth Agarwal
merge.mergestate: compute dirstate action...
r27035
Siddharth Agarwal
merge.mergestate._resolve: also return completed status...
r26616 return complete, r
Matt Mackall
Move merge code to its own module...
r2775
Siddharth Agarwal
mergestate._resolve: handle change/delete conflicts...
r27048 def _filectxorabsent(self, hexnode, ctx, f):
if hexnode == nullhex:
return filemerge.absentfilectx(ctx, f)
else:
return ctx[f]
Siddharth Agarwal
merge: introduce a preresolve function...
r26617 def preresolve(self, dfile, wctx, labels=None):
Siddharth Agarwal
merge.mergestate: update docstrings for preresolve and resolve...
r26870 """run premerge process for dfile
Returns whether the merge is complete, and the exit code."""
Siddharth Agarwal
merge: introduce a preresolve function...
r26617 return self._resolve(True, dfile, wctx, labels=labels)
Siddharth Agarwal
merge.mergestate: add a wrapper around resolve...
r26615 def resolve(self, dfile, wctx, labels=None):
Siddharth Agarwal
merge.mergestate: update docstrings for preresolve and resolve...
r26870 """run merge process (assuming premerge was run) for dfile
Returns the exit code of the merge."""
Siddharth Agarwal
merge: introduce a preresolve function...
r26617 return self._resolve(False, dfile, wctx, labels=labels)[1]
Siddharth Agarwal
merge.mergestate: add a wrapper around resolve...
r26615
Siddharth Agarwal
mergestate: add a method to return updated/merged/removed counts...
r27076 def counts(self):
"""return counts for updated, merged and removed files in this
session"""
updated, merged, removed = 0, 0, 0
for r, action in self._results.itervalues():
if r is None:
updated += 1
elif r == 0:
if action == 'r':
removed += 1
else:
merged += 1
return updated, merged, removed
Siddharth Agarwal
mergestate: add a function to return the number of unresolved files...
r27077 def unresolvedcount(self):
"""get unresolved count for this merge (persistent)"""
return len([True for f, entry in self._state.iteritems()
if entry[0] == 'u'])
Siddharth Agarwal
mergestate: add a method to compute actions to perform on dirstate...
r27079 def actions(self):
"""return lists of actions to perform on the dirstate"""
Siddharth Agarwal
merge: add a new action type representing files to add/mark as modified...
r27131 actions = {'r': [], 'f': [], 'a': [], 'am': [], 'g': []}
Siddharth Agarwal
mergestate: add a method to compute actions to perform on dirstate...
r27079 for f, (r, action) in self._results.iteritems():
if action is not None:
actions[action].append((f, None, "merge result"))
return actions
Siddharth Agarwal
mergestate: add a way to record pending dirstate actions...
r27088 def recordactions(self):
"""record remove/add/get actions in the dirstate"""
branchmerge = self._repo.dirstate.p2() != nullid
recordupdates(self._repo, self.actions(), branchmerge)
Siddharth Agarwal
mergestate: add methods to queue files to remove, add or get...
r27090 def queueremove(self, f):
"""queues a file to be removed from the dirstate
Meant for use by custom merge drivers."""
self._results[f] = 0, 'r'
def queueadd(self, f):
"""queues a file to be added to the dirstate
Meant for use by custom merge drivers."""
self._results[f] = 0, 'a'
def queueget(self, f):
"""queues a file to be marked modified in the dirstate
Meant for use by custom merge drivers."""
self._results[f] = 0, 'g'
Siddharth Agarwal
merge: factor out code to get checkunknown config...
r27740 def _getcheckunknownconfig(repo, section, name):
config = repo.ui.config(section, name, default='abort')
valid = ['abort', 'ignore', 'warn']
if config not in valid:
validstr = ', '.join(["'" + v + "'" for v in valid])
raise error.ConfigError(_("%s.%s not valid "
"('%s' is none of %s)")
% (section, name, config, validstr))
return config
Martin von Zweigbergk
merge: don't overwrite untracked file at directory rename target...
r23653 def _checkunknownfile(repo, wctx, mctx, f, f2=None):
if f2 is None:
f2 = f
Siddharth Agarwal
merge: while checking for unknown files don't follow symlinks (issue5027)...
r27571 return (repo.wvfs.isfileorlink(f)
Angel Ezquerra
localrepo: remove all external users of localrepo.wopener...
r23879 and repo.wvfs.audit.check(f)
Matt Mackall
merge: fix unknown file merge detection for case-folding systems...
r16284 and repo.dirstate.normalize(f) not in repo.dirstate
Martin von Zweigbergk
merge: don't overwrite untracked file at directory rename target...
r23653 and mctx[f2].cmp(wctx[f]))
Matt Mackall
merge: refactor unknown file conflict checking...
r16093
Martin von Zweigbergk
merge: extract method for checking for conflicting untracked file...
r23655 def _checkunknownfiles(repo, wctx, mctx, force, actions):
"""
Considers any actions that care about the presence of conflicting unknown
files. For some actions, the result is to abort; for others, it is to
choose a different action.
"""
Siddharth Agarwal
_checkunknownfiles: turn 'conflicts' into a set...
r27654 conflicts = set()
Martin von Zweigbergk
merge: extract method for checking for conflicting untracked file...
r23655 if not force:
Siddharth Agarwal
merge: determine what untracked conflicts cause warns and aborts separately...
r27741 abortconflicts = set()
warnconflicts = set()
def collectconflicts(conflicts, config):
if config == 'abort':
abortconflicts.update(conflicts)
elif config == 'warn':
warnconflicts.update(conflicts)
Siddharth Agarwal
merge: split up checks for unknown and ignored files that differ...
r27742 unknownconfig = _getcheckunknownconfig(repo, 'merge', 'checkunknown')
ignoredconfig = _getcheckunknownconfig(repo, 'merge', 'checkignored')
Martin von Zweigbergk
merge: extract method for checking for conflicting untracked file...
r23655 for f, (m, args, msg) in actions.iteritems():
if m in ('c', 'dc'):
if _checkunknownfile(repo, wctx, mctx, f):
Siddharth Agarwal
_checkunknownfiles: turn 'conflicts' into a set...
r27654 conflicts.add(f)
Martin von Zweigbergk
merge: extract method for checking for conflicting untracked file...
r23655 elif m == 'dg':
if _checkunknownfile(repo, wctx, mctx, f, args[0]):
Siddharth Agarwal
_checkunknownfiles: turn 'conflicts' into a set...
r27654 conflicts.add(f)
Martin von Zweigbergk
merge: extract method for checking for conflicting untracked file...
r23655
Siddharth Agarwal
merge: split up checks for unknown and ignored files that differ...
r27742 ignoredconflicts = set([c for c in conflicts
if repo.dirstate._ignore(c)])
unknownconflicts = conflicts - ignoredconflicts
collectconflicts(ignoredconflicts, ignoredconfig)
collectconflicts(unknownconflicts, unknownconfig)
Siddharth Agarwal
merge: determine what untracked conflicts cause warns and aborts separately...
r27741 for f in sorted(abortconflicts):
repo.ui.warn(_("%s: untracked file differs\n") % f)
if abortconflicts:
raise error.Abort(_("untracked files in working directory "
"differ from files in requested revision"))
for f in sorted(warnconflicts):
repo.ui.warn(_("%s: replacing untracked file\n") % f)
Martin von Zweigbergk
merge: extract method for checking for conflicting untracked file...
r23655
for f, (m, args, msg) in actions.iteritems():
Siddharth Agarwal
merge: add options to warn or ignore on colliding unknown files...
r27657 backup = f in conflicts
Martin von Zweigbergk
merge: extract method for checking for conflicting untracked file...
r23655 if m == 'c':
Siddharth Agarwal
merge: add a new 'backup' argument to get actions...
r27655 flags, = args
Siddharth Agarwal
merge: add options to warn or ignore on colliding unknown files...
r27657 actions[f] = ('g', (flags, backup), msg)
Martin von Zweigbergk
merge: extract method for checking for conflicting untracked file...
r23655 elif m == 'cm':
fl2, anc = args
different = _checkunknownfile(repo, wctx, mctx, f)
if different:
actions[f] = ('m', (f, f, None, False, anc),
"remote differs from untracked local")
else:
Siddharth Agarwal
merge: add options to warn or ignore on colliding unknown files...
r27657 actions[f] = ('g', (fl2, backup), "remote created")
Martin von Zweigbergk
merge: extract method for checking for conflicting untracked file...
r23655
Matt Mackall
merge: privatize some functions, unnest some others
r6269 def _forgetremoved(wctx, mctx, branchmerge):
Matt Mackall
merge: pull manifest checks and updates into separate functions
r3107 """
Forget removed files
If we're jumping between revisions (as opposed to merging), and if
neither the working directory nor the target rev has the file,
then we need to remove it from the dirstate, to prevent the
dirstate from listing the file when it is no longer in the
manifest.
Alexis S. L. Carvalho
merge: fix handling of deleted files
r6242
If we're merging, and the other revision has removed a file
that is not present in the working directory, we need to mark it
as removed.
Matt Mackall
merge: pull manifest checks and updates into separate functions
r3107 """
Martin von Zweigbergk
merge: let _forgetremoved() work on the file->action dict...
r23640 actions = {}
m = 'f'
Mads Kiilerich
merge: use separate lists for each action type...
r21545 if branchmerge:
Martin von Zweigbergk
merge: let _forgetremoved() work on the file->action dict...
r23640 m = 'r'
Alexis S. L. Carvalho
merge: fix handling of deleted files
r6242 for f in wctx.deleted():
Matt Mackall
merge: simplify some helpers
r6272 if f not in mctx:
Martin von Zweigbergk
merge: let _forgetremoved() work on the file->action dict...
r23640 actions[f] = m, None, "forget deleted"
Alexis S. L. Carvalho
merge: fix handling of deleted files
r6242
if not branchmerge:
for f in wctx.removed():
Matt Mackall
merge: simplify some helpers
r6272 if f not in mctx:
Martin von Zweigbergk
merge: let _forgetremoved() work on the file->action dict...
r23640 actions[f] = 'f', None, "forget removed"
Matt Mackall
merge: pull manifest checks and updates into separate functions
r3107
Martin von Zweigbergk
merge: let _forgetremoved() work on the file->action dict...
r23640 return actions
Matt Mackall
merge: pull manifest checks and updates into separate functions
r3107
Mads Kiilerich
merge: handle create+delete prompts in calculateupdates...
r20640 def _checkcollision(repo, wmf, actions):
FUJIWARA Katsunori
icasefs: rewrite case-folding collision detection (issue3452)...
r19105 # build provisional merged manifest up
pmmf = set(wmf)
Mads Kiilerich
merge: use separate lists for each action type...
r21545 if actions:
# k, dr, e and rd are no-op
Siddharth Agarwal
merge: add a new action type representing files to add/mark as modified...
r27131 for m in 'a', 'am', 'f', 'g', 'cd', 'dc':
Mads Kiilerich
merge: use separate lists for each action type...
r21545 for f, args, msg in actions[m]:
pmmf.add(f)
for f, args, msg in actions['r']:
pmmf.discard(f)
for f, args, msg in actions['dm']:
f2, flags = args
pmmf.discard(f2)
pmmf.add(f)
for f, args, msg in actions['dg']:
pmmf.add(f)
for f, args, msg in actions['m']:
f1, f2, fa, move, anc = args
if move:
pmmf.discard(f1)
pmmf.add(f)
FUJIWARA Katsunori
icasefs: rewrite case-folding collision detection (issue3452)...
r19105
# check case-folding collision in provisional merged manifest
foldmap = {}
for f in sorted(pmmf):
fold = util.normcase(f)
if fold in foldmap:
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_("case-folding collision between %s and %s")
FUJIWARA Katsunori
icasefs: rewrite case-folding collision detection (issue3452)...
r19105 % (f, foldmap[fold]))
foldmap[fold] = f
Mads Kiilerich
merge: abort on file/directory case folding collisions (issue4892)...
r26661 # check case-folding of directories
foldprefix = unfoldprefix = lastfull = ''
for fold, f in sorted(foldmap.items()):
if fold.startswith(foldprefix) and not f.startswith(unfoldprefix):
# the folded prefix matches but actual casing is different
raise error.Abort(_("case-folding collision between "
"%s and directory of %s") % (lastfull, f))
foldprefix = fold + '/'
unfoldprefix = f + '/'
lastfull = f
Siddharth Agarwal
merge: add stubs for preprocess and conclude steps of merge driver...
r26785 def driverpreprocess(repo, ms, wctx, labels=None):
"""run the preprocess step of the merge driver, if any
This is currently not implemented -- it's an extension point."""
return True
def driverconclude(repo, ms, wctx, labels=None):
"""run the conclude step of the merge driver, if any
This is currently not implemented -- it's an extension point."""
return True
Augie Fackler
merge: rework manifestmerge to use a matcher...
r27346 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, matcher,
Mads Kiilerich
merge: move ancestor selection tweaking from manifestmerge to update function...
r21080 acceptremote, followcopies):
Matt Mackall
merge: pull manifest comparison out into separate function
r3105 """
Alecs King
merge: fix typo in docstring
r11817 Merge p1 and p2 with ancestor pa and generate merge action list
Matt Mackall
merge: update some docstrings
r3315
Siddharth Agarwal
manifestmerge: pass in branchmerge and force separately...
r18605 branchmerge and force are as passed in to update
Augie Fackler
merge: rework manifestmerge to use a matcher...
r27346 matcher = matcher to filter file lists
Durham Goode
rebase: fix --collapse when a file was added then removed...
r18778 acceptremote = accept the incoming changes without prompting
Matt Mackall
merge: pull manifest comparison out into separate function
r3105 """
Augie Fackler
merge: rework manifestmerge to use a matcher...
r27346 if matcher is not None and matcher.always():
matcher = None
Matt Mackall
merge: pull manifest comparison out into separate function
r3105
Martin von Zweigbergk
merge: don't treat 'diverge' and 'renamedelete' like actions...
r23526 copy, movewithdir, diverge, renamedelete = {}, {}, {}, {}
Matt Mackall
merge: refactor manifestmerge init to better report effective ancestor
r8753
Siddharth Agarwal
manifestmerge: fix order in which manifests are fetched...
r18651 # manifests fetched in order are going to be faster, so prime the caches
[x.manifest() for x in
sorted(wctx.parents() + [p2, pa], key=lambda x: x.rev())]
if followcopies:
Bryan O'Sullivan
merge: rename p1 to wctx in manifestmerge...
r18611 ret = copies.mergecopies(repo, wctx, p2, pa)
Siddharth Agarwal
copies: separate moves via directory renames from explicit copies...
r18134 copy, movewithdir, diverge, renamedelete = ret
Matt Mackall
merge: refactor manifestmerge init to better report effective ancestor
r8753
repo.ui.note(_("resolving manifests\n"))
Siddharth Agarwal
manifestmerge: pass in branchmerge and force separately...
r18605 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
Augie Fackler
merge: rework manifestmerge to use a matcher...
r27346 % (bool(branchmerge), bool(force), bool(matcher)))
Bryan O'Sullivan
merge: rename p1 to wctx in manifestmerge...
r18611 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
Matt Mackall
merge: refactor manifestmerge init to better report effective ancestor
r8753
Bryan O'Sullivan
merge: rename p1 to wctx in manifestmerge...
r18611 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
Matt Mackall
merge: refactor manifestmerge init to better report effective ancestor
r8753 copied = set(copy.values())
Siddharth Agarwal
copies: separate moves via directory renames from explicit copies...
r18134 copied.update(movewithdir.values())
Matt Mackall
merge: use contexts for manifestmerge...
r3295
Matt Mackall
subrepo: correctly handle update -C with modified subrepos (issue2022)...
r11470 if '.hgsubstate' in m1:
Matt Mackall
submerge: properly deal with overwrites...
r9783 # check whether sub state is modified
Bryan O'Sullivan
merge: rename p1 to wctx in manifestmerge...
r18611 for s in sorted(wctx.substate):
if wctx.sub(s).dirty():
Martin von Zweigbergk
merge: consistently use single quotes for non-user-facing strings...
r23380 m1['.hgsubstate'] += '+'
Matt Mackall
submerge: properly deal with overwrites...
r9783 break
Matt Mackall
merge: pull manifest comparison out into separate function
r3105 # Compare manifests
Augie Fackler
manifestmerge: have manifest do matching before diffing...
r27429 if matcher is not None:
m1 = m1.matches(matcher)
m2 = m2.matches(matcher)
Martin von Zweigbergk
manifest: repurpose flagsdiff() into (node-and-flag)diff()...
r22964 diff = m1.diff(m2)
Siddharth Agarwal
manifestmerge: use dicthelpers.diff and join...
r18822
Martin von Zweigbergk
merge: write manifestmerge() using dictionary with entry per file...
r23637 actions = {}
Martin von Zweigbergk
manifest: transpose pair of pairs from diff()...
r22966 for f, ((n1, fl1), (n2, fl2)) in diff.iteritems():
Martin von Zweigbergk
merge: branch code into {n1 and n2, n1, n2} top-level cases...
r23473 if n1 and n2: # file exists on both local and remote side
Martin von Zweigbergk
merge: separate out "both created" cases...
r23396 if f not in ma:
Martin von Zweigbergk
merge: break out "both renamed a -> b" case...
r23397 fa = copy.get(f, None)
if fa is not None:
Martin von Zweigbergk
merge: write manifestmerge() using dictionary with entry per file...
r23637 actions[f] = ('m', (f, f, fa, False, pa.node()),
"both renamed from " + fa)
Mads Kiilerich
merge: merge file flags together with file content...
r18338 else:
Martin von Zweigbergk
merge: write manifestmerge() using dictionary with entry per file...
r23637 actions[f] = ('m', (f, f, None, False, pa.node()),
"both created")
Matt Mackall
merge: don't use unknown()...
r16094 else:
Martin von Zweigbergk
merge: separate out "both created" cases...
r23396 a = ma[f]
fla = ma.flags(f)
Martin von Zweigbergk
merge: indent to prepare for next patch
r23395 nol = 'l' not in fl1 + fl2 + fla
if n2 == a and fl2 == fla:
Martin von Zweigbergk
merge: write manifestmerge() using dictionary with entry per file...
r23637 actions[f] = ('k' , (), "remote unchanged")
Martin von Zweigbergk
merge: indent to prepare for next patch
r23395 elif n1 == a and fl1 == fla: # local unchanged - use remote
if n1 == n2: # optimization: keep local content
Martin von Zweigbergk
merge: write manifestmerge() using dictionary with entry per file...
r23637 actions[f] = ('e', (fl2,), "update permissions")
Martin von Zweigbergk
merge: indent to prepare for next patch
r23395 else:
Siddharth Agarwal
merge: add a new 'backup' argument to get actions...
r27655 actions[f] = ('g', (fl2, False), "remote is newer")
Martin von Zweigbergk
merge: indent to prepare for next patch
r23395 elif nol and n2 == a: # remote only changed 'x'
Martin von Zweigbergk
merge: write manifestmerge() using dictionary with entry per file...
r23637 actions[f] = ('e', (fl2,), "update permissions")
Martin von Zweigbergk
merge: indent to prepare for next patch
r23395 elif nol and n1 == a: # local only changed 'x'
Siddharth Agarwal
merge: add a new 'backup' argument to get actions...
r27655 actions[f] = ('g', (fl1, False), "remote is newer")
Martin von Zweigbergk
merge: indent to prepare for next patch
r23395 else: # both changed something
Martin von Zweigbergk
merge: write manifestmerge() using dictionary with entry per file...
r23637 actions[f] = ('m', (f, f, f, False, pa.node()),
"versions differ")
Martin von Zweigbergk
merge: branch code into {n1 and n2, n1, n2} top-level cases...
r23473 elif n1: # file exists only on local side
Martin von Zweigbergk
merge: duplicate 'if f in copied' into each branch
r23474 if f in copied:
pass # we'll deal with it on m2 side
elif f in movewithdir: # directory rename, move local
Martin von Zweigbergk
merge: branch code into {n1 and n2, n1, n2} top-level cases...
r23473 f2 = movewithdir[f]
Martin von Zweigbergk
merge: don't ignore conflicting file in remote renamed directory...
r23475 if f2 in m2:
Martin von Zweigbergk
merge: write manifestmerge() using dictionary with entry per file...
r23637 actions[f2] = ('m', (f, f2, None, True, pa.node()),
"remote directory rename, both created")
Martin von Zweigbergk
merge: don't ignore conflicting file in remote renamed directory...
r23475 else:
Martin von Zweigbergk
merge: write manifestmerge() using dictionary with entry per file...
r23637 actions[f2] = ('dm', (f, fl1),
"remote directory rename - move from " + f)
Martin von Zweigbergk
merge: branch code into {n1 and n2, n1, n2} top-level cases...
r23473 elif f in copy:
f2 = copy[f]
Martin von Zweigbergk
merge: write manifestmerge() using dictionary with entry per file...
r23637 actions[f] = ('m', (f, f2, f2, False, pa.node()),
"local copied/moved from " + f2)
Martin von Zweigbergk
merge: branch code into {n1 and n2, n1, n2} top-level cases...
r23473 elif f in ma: # clean, a different, no remote
if n1 != ma[f]:
if acceptremote:
Martin von Zweigbergk
merge: write manifestmerge() using dictionary with entry per file...
r23637 actions[f] = ('r', None, "remote delete")
Martin von Zweigbergk
merge: branch code into {n1 and n2, n1, n2} top-level cases...
r23473 else:
Siddharth Agarwal
merge: make 'cd' and 'dc' actions store the same arguments as 'm'...
r26962 actions[f] = ('cd', (f, None, f, False, pa.node()),
"prompt changed/deleted")
Martin von Zweigbergk
merge: branch code into {n1 and n2, n1, n2} top-level cases...
r23473 elif n1[20:] == 'a':
# This extra 'a' is added by working copy manifest to mark
# the file as locally added. We should forget it instead of
# deleting it.
Martin von Zweigbergk
merge: write manifestmerge() using dictionary with entry per file...
r23637 actions[f] = ('f', None, "remote deleted")
Mads Kiilerich
merge: handle acceptremove of create+delete early in manifest merge
r20639 else:
Martin von Zweigbergk
merge: write manifestmerge() using dictionary with entry per file...
r23637 actions[f] = ('r', None, "other deleted")
Martin von Zweigbergk
merge: branch code into {n1 and n2, n1, n2} top-level cases...
r23473 elif n2: # file exists only on remote side
Martin von Zweigbergk
merge: duplicate 'if f in copied' into each branch
r23474 if f in copied:
pass # we'll deal with it on m1 side
elif f in movewithdir:
Martin von Zweigbergk
merge: branch code into {n1 and n2, n1, n2} top-level cases...
r23473 f2 = movewithdir[f]
Martin von Zweigbergk
merge: don't overwrite conflicting file in locally renamed directory...
r23476 if f2 in m1:
Martin von Zweigbergk
merge: write manifestmerge() using dictionary with entry per file...
r23637 actions[f2] = ('m', (f2, f, None, False, pa.node()),
"local directory rename, both created")
Martin von Zweigbergk
merge: don't overwrite conflicting file in locally renamed directory...
r23476 else:
Martin von Zweigbergk
merge: write manifestmerge() using dictionary with entry per file...
r23637 actions[f2] = ('dg', (f, fl2),
"local directory rename - get from " + f)
Martin von Zweigbergk
merge: branch code into {n1 and n2, n1, n2} top-level cases...
r23473 elif f in copy:
f2 = copy[f]
if f2 in m2:
Martin von Zweigbergk
merge: write manifestmerge() using dictionary with entry per file...
r23637 actions[f] = ('m', (f2, f, f2, False, pa.node()),
"remote copied from " + f2)
Martin von Zweigbergk
merge: branch code into {n1 and n2, n1, n2} top-level cases...
r23473 else:
Martin von Zweigbergk
merge: write manifestmerge() using dictionary with entry per file...
r23637 actions[f] = ('m', (f2, f, f2, True, pa.node()),
"remote moved from " + f2)
Martin von Zweigbergk
merge: branch code into {n1 and n2, n1, n2} top-level cases...
r23473 elif f not in ma:
# local unknown, remote created: the logic is described by the
# following table:
#
# force branchmerge different | action
Martin von Zweigbergk
merge: collect checking for unknown files at end of manifestmerge()...
r23651 # n * * | create
Martin von Zweigbergk
merge: introduce 'c' action like 'g', but with additional safety...
r23650 # y n * | create
# y y n | create
Martin von Zweigbergk
merge: branch code into {n1 and n2, n1, n2} top-level cases...
r23473 # y y y | merge
#
# Checking whether the files are different is expensive, so we
# don't do that when we can avoid it.
Martin von Zweigbergk
merge: structure 'remote created' code to match table...
r23649 if not force:
Martin von Zweigbergk
merge: collect checking for unknown files at end of manifestmerge()...
r23651 actions[f] = ('c', (fl2,), "remote created")
Martin von Zweigbergk
merge: structure 'remote created' code to match table...
r23649 elif not branchmerge:
Martin von Zweigbergk
merge: introduce 'c' action like 'g', but with additional safety...
r23650 actions[f] = ('c', (fl2,), "remote created")
Martin von Zweigbergk
merge: branch code into {n1 and n2, n1, n2} top-level cases...
r23473 else:
Martin von Zweigbergk
merge: create 'cm' action for 'get or merge' case...
r23654 actions[f] = ('cm', (fl2, pa.node()),
"remote created, get or merge")
Martin von Zweigbergk
merge: branch code into {n1 and n2, n1, n2} top-level cases...
r23473 elif n2 != ma[f]:
Martin von Zweigbergk
merge: collect checking for unknown files at end of manifestmerge()...
r23651 if acceptremote:
actions[f] = ('c', (fl2,), "remote recreating")
Siddharth Agarwal
manifestmerge: handle abort on local unknown, remote created files...
r18606 else:
Siddharth Agarwal
merge: make 'cd' and 'dc' actions store the same arguments as 'm'...
r26962 actions[f] = ('dc', (None, f, f, False, pa.node()),
"prompt deleted/changed")
Martin von Zweigbergk
merge: collect checking for unknown files at end of manifestmerge()...
r23651
Martin von Zweigbergk
merge: don't treat 'diverge' and 'renamedelete' like actions...
r23526 return actions, diverge, renamedelete
Matt Mackall
merge: pull manifest comparison out into separate function
r3105
Martin von Zweigbergk
merge: extract _resolvetrivial() function...
r23531 def _resolvetrivial(repo, wctx, mctx, ancestor, actions):
"""Resolves false conflicts where the nodeid changed but the content
remained the same."""
Martin von Zweigbergk
merge: let _resolvetrivial() work on the file->action dict...
r23639 for f, (m, args, msg) in actions.items():
if m == 'cd' and f in ancestor and not wctx[f].cmp(ancestor[f]):
Martin von Zweigbergk
merge: extract _resolvetrivial() function...
r23531 # local did change but ended up with same content
Martin von Zweigbergk
merge: let _resolvetrivial() work on the file->action dict...
r23639 actions[f] = 'r', None, "prompt same"
elif m == 'dc' and f in ancestor and not mctx[f].cmp(ancestor[f]):
Martin von Zweigbergk
merge: extract _resolvetrivial() function...
r23531 # remote did change but ended up with same content
Martin von Zweigbergk
merge: let _resolvetrivial() work on the file->action dict...
r23639 del actions[f] # don't get = keep local deleted
Martin von Zweigbergk
merge: extract _resolvetrivial() function...
r23531
Augie Fackler
merge: restate calculateupdates in terms of a matcher...
r27345 def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force,
acceptremote, followcopies, matcher=None):
Martin von Zweigbergk
merge: move calculateupdates() before applyupdated()...
r23385 "Calculate the actions needed to merge mctx into wctx using ancestors"
if len(ancestors) == 1: # default
Martin von Zweigbergk
merge: don't treat 'diverge' and 'renamedelete' like actions...
r23526 actions, diverge, renamedelete = manifestmerge(
Augie Fackler
merge: rework manifestmerge to use a matcher...
r27346 repo, wctx, mctx, ancestors[0], branchmerge, force, matcher,
Martin von Zweigbergk
merge: don't treat 'diverge' and 'renamedelete' like actions...
r23526 acceptremote, followcopies)
Martin von Zweigbergk
merge: move checking of unknown files out of manifestmerge()...
r23656 _checkunknownfiles(repo, wctx, mctx, force, actions)
Martin von Zweigbergk
merge: move calculateupdates() before applyupdated()...
r23385
else: # only when merge.preferancestor=* - the default
repo.ui.note(
_("note: merging %s and %s using bids from ancestors %s\n") %
(wctx, mctx, _(' and ').join(str(anc) for anc in ancestors)))
# Call for bids
fbids = {} # mapping filename to bids (action method to list af actions)
Martin von Zweigbergk
merge: don't treat 'diverge' and 'renamedelete' like actions...
r23526 diverge, renamedelete = None, None
Martin von Zweigbergk
merge: move calculateupdates() before applyupdated()...
r23385 for ancestor in ancestors:
repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
Martin von Zweigbergk
merge: don't treat 'diverge' and 'renamedelete' like actions...
r23526 actions, diverge1, renamedelete1 = manifestmerge(
Augie Fackler
merge: rework manifestmerge to use a matcher...
r27346 repo, wctx, mctx, ancestor, branchmerge, force, matcher,
Martin von Zweigbergk
merge: don't treat 'diverge' and 'renamedelete' like actions...
r23526 acceptremote, followcopies)
Martin von Zweigbergk
merge: move checking of unknown files out of manifestmerge()...
r23656 _checkunknownfiles(repo, wctx, mctx, force, actions)
Matt Mackall
bidmerge: choose shortest list of diverge and rename/delete warnings...
r26318
# Track the shortest set of warning on the theory that bid
# merge will correctly incorporate more information
if diverge is None or len(diverge1) < len(diverge):
Martin von Zweigbergk
merge: don't treat 'diverge' and 'renamedelete' like actions...
r23526 diverge = diverge1
Matt Mackall
bidmerge: choose shortest list of diverge and rename/delete warnings...
r26318 if renamedelete is None or len(renamedelete) < len(renamedelete1):
Martin von Zweigbergk
merge: don't treat 'diverge' and 'renamedelete' like actions...
r23526 renamedelete = renamedelete1
Matt Mackall
bidmerge: choose shortest list of diverge and rename/delete warnings...
r26318
Martin von Zweigbergk
merge: let bid merge work on the file->action dict...
r23638 for f, a in sorted(actions.iteritems()):
m, args, msg = a
repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m))
if f in fbids:
d = fbids[f]
if m in d:
d[m].append(a)
Martin von Zweigbergk
merge: move calculateupdates() before applyupdated()...
r23385 else:
Martin von Zweigbergk
merge: let bid merge work on the file->action dict...
r23638 d[m] = [a]
else:
fbids[f] = {m: [a]}
Martin von Zweigbergk
merge: move calculateupdates() before applyupdated()...
r23385
# Pick the best bid for each file
repo.ui.note(_('\nauction for merging merge bids\n'))
Martin von Zweigbergk
merge: let bid merge work on the file->action dict...
r23638 actions = {}
Martin von Zweigbergk
merge: move calculateupdates() before applyupdated()...
r23385 for f, bids in sorted(fbids.items()):
# bids is a mapping from action method to list af actions
# Consensus?
if len(bids) == 1: # all bids are the same kind of method
m, l = bids.items()[0]
Augie Fackler
cleanup: use __builtins__.all instead of util.all
r25151 if all(a == l[0] for a in l[1:]): # len(bids) is > 1
Martin von Zweigbergk
merge: move calculateupdates() before applyupdated()...
r23385 repo.ui.note(" %s: consensus for %s\n" % (f, m))
Martin von Zweigbergk
merge: let bid merge work on the file->action dict...
r23638 actions[f] = l[0]
Martin von Zweigbergk
merge: move calculateupdates() before applyupdated()...
r23385 continue
# If keep is an option, just do it.
if 'k' in bids:
repo.ui.note(" %s: picking 'keep' action\n" % f)
Martin von Zweigbergk
merge: let bid merge work on the file->action dict...
r23638 actions[f] = bids['k'][0]
Martin von Zweigbergk
merge: move calculateupdates() before applyupdated()...
r23385 continue
# If there are gets and they all agree [how could they not?], do it.
if 'g' in bids:
ga0 = bids['g'][0]
Augie Fackler
cleanup: use __builtins__.all instead of util.all
r25151 if all(a == ga0 for a in bids['g'][1:]):
Martin von Zweigbergk
merge: move calculateupdates() before applyupdated()...
r23385 repo.ui.note(" %s: picking 'get' action\n" % f)
Martin von Zweigbergk
merge: let bid merge work on the file->action dict...
r23638 actions[f] = ga0
Martin von Zweigbergk
merge: move calculateupdates() before applyupdated()...
r23385 continue
# TODO: Consider other simple actions such as mode changes
# Handle inefficient democrazy.
repo.ui.note(_(' %s: multiple bids for merge action:\n') % f)
for m, l in sorted(bids.items()):
for _f, args, msg in l:
repo.ui.note(' %s -> %s\n' % (msg, m))
# Pick random action. TODO: Instead, prompt user when resolving
m, l = bids.items()[0]
repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') %
(f, m))
Martin von Zweigbergk
merge: let bid merge work on the file->action dict...
r23638 actions[f] = l[0]
Martin von Zweigbergk
merge: move calculateupdates() before applyupdated()...
r23385 continue
repo.ui.note(_('end of auction\n\n'))
Martin von Zweigbergk
merge: let _resolvetrivial() work on the file->action dict...
r23639 _resolvetrivial(repo, wctx, mctx, ancestors[0], actions)
Martin von Zweigbergk
merge: let _forgetremoved() work on the file->action dict...
r23640 if wctx.rev() is None:
fractions = _forgetremoved(wctx, mctx, branchmerge)
actions.update(fractions)
Martin von Zweigbergk
merge: don't treat 'diverge' and 'renamedelete' like actions...
r23526 return actions, diverge, renamedelete
Martin von Zweigbergk
merge: move calculateupdates() before applyupdated()...
r23385
Mads Kiilerich
merge: separate worker functions for batch remove and batch get...
r21392 def batchremove(repo, actions):
"""apply removes to the working directory
Bryan O'Sullivan
merge: split out mostly-non-interactive working dir updates...
r18630
yields tuples for progress updates
"""
Bryan O'Sullivan
merge: don't fiddle with name lookups or i18n in hot loops...
r18640 verbose = repo.ui.verbose
unlink = util.unlinkpath
wjoin = repo.wjoin
Angel Ezquerra
localrepo: remove all external users of localrepo.wopener...
r23879 audit = repo.wvfs.audit
Bryan O'Sullivan
merge: report non-interactive progress in chunks...
r18633 i = 0
Mads Kiilerich
merge: use separate lists for each action type...
r21545 for f, args, msg in actions:
Mads Kiilerich
merge: separate worker functions for batch remove and batch get...
r21392 repo.ui.debug(" %s: %s -> r\n" % (f, msg))
Mads Kiilerich
merge: fix stupid indentation left over from previous refactorings
r21551 if verbose:
repo.ui.note(_("removing %s\n") % f)
audit(f)
try:
unlink(wjoin(f), ignoremissing=True)
Gregory Szorc
global: mass rewrite to use modern exception syntax...
r25660 except OSError as inst:
Mads Kiilerich
merge: fix stupid indentation left over from previous refactorings
r21551 repo.ui.warn(_("update failed to remove %s: %s!\n") %
(f, inst.strerror))
Mads Kiilerich
merge: separate worker functions for batch remove and batch get...
r21392 if i == 100:
yield i, f
i = 0
i += 1
if i > 0:
yield i, f
def batchget(repo, mctx, actions):
"""apply gets to the working directory
mctx is the context to get from
yields tuples for progress updates
"""
verbose = repo.ui.verbose
fctx = mctx.filectx
wwrite = repo.wwrite
Siddharth Agarwal
batchget: add support for backing up files...
r27656 ui = repo.ui
Mads Kiilerich
merge: separate worker functions for batch remove and batch get...
r21392 i = 0
Siddharth Agarwal
batchget: add support for backing up files...
r27656 for f, (flags, backup), msg in actions:
Mads Kiilerich
merge: separate worker functions for batch remove and batch get...
r21392 repo.ui.debug(" %s: %s -> g\n" % (f, msg))
Mads Kiilerich
merge: fix stupid indentation left over from previous refactorings
r21551 if verbose:
repo.ui.note(_("getting %s\n") % f)
Siddharth Agarwal
batchget: add support for backing up files...
r27656
if backup:
absf = repo.wjoin(f)
orig = scmutil.origpath(ui, repo, absf)
try:
# TODO Mercurial has always aborted if an untracked directory
# is replaced by a tracked file, or generally with
# file/directory merges. This needs to be sorted out.
if repo.wvfs.isfileorlink(f):
util.rename(absf, orig)
except OSError as e:
if e.errno != errno.ENOENT:
raise
wwrite(f, fctx(f).data(), flags)
Bryan O'Sullivan
merge: report non-interactive progress in chunks...
r18633 if i == 100:
yield i, f
i = 0
i += 1
if i > 0:
Bryan O'Sullivan
merge: split out mostly-non-interactive working dir updates...
r18630 yield i, f
Durham Goode
merge: add labels parameter from merge.update to filemerge...
r21524 def applyupdates(repo, actions, wctx, mctx, overwrite, labels=None):
Peter Arrenbrecht
merge: pass constant cset ancestor to fctx.ancestor
r11454 """apply the merge action list to the working directory
wctx is the working copy context
mctx is the context to be merged into the working copy
Greg Ward
merge: document some internal return values.
r13162
Return a tuple of counts (updated, merged, removed, unresolved) that
describes how many files were affected by the update.
Peter Arrenbrecht
merge: pass constant cset ancestor to fctx.ancestor
r11454 """
Matt Mackall
merge: update some docstrings
r3315
Siddharth Agarwal
merge.applyupdates: use counters from mergestate...
r27078 updated, merged, removed = 0, 0, 0
Siddharth Agarwal
merge.applyupdates: switch to mergestate.clean()...
r26990 ms = mergestate.clean(repo, wctx.p1().node(), mctx.node())
Matt Mackall
merge: introduce mergestate
r6512 moves = []
Mads Kiilerich
merge: use separate lists for each action type...
r21545 for m, l in actions.items():
l.sort()
Matt Mackall
merge: introduce mergestate
r6512
Siddharth Agarwal
merge: move almost all change/delete conflicts to resolve phase (BC) (API)...
r27137 # 'cd' and 'dc' actions are treated like other merge conflicts
mergeactions = sorted(actions['cd'])
mergeactions.extend(sorted(actions['dc']))
mergeactions.extend(actions['m'])
for f, args, msg in mergeactions:
Mads Kiilerich
merge: fix stupid indentation left over from previous refactorings
r21551 f1, f2, fa, move, anc = args
if f == '.hgsubstate': # merged internally
continue
Siddharth Agarwal
merge.applyupdates: create absentfilectxes for change/delete conflicts...
r27091 if f1 is None:
fcl = filemerge.absentfilectx(wctx, fa)
else:
repo.ui.debug(" preserving %s for resolve of %s\n" % (f1, f))
fcl = wctx[f1]
if f2 is None:
fco = filemerge.absentfilectx(mctx, fa)
else:
fco = mctx[f2]
Mads Kiilerich
merge: fix stupid indentation left over from previous refactorings
r21551 actx = repo[anc]
if fa in actx:
fca = actx[fa]
else:
Siddharth Agarwal
merge.applyupdates: create absentfilectxes for change/delete conflicts...
r27091 # TODO: move to absentfilectx
Mads Kiilerich
merge: fix stupid indentation left over from previous refactorings
r21551 fca = repo.filectx(f1, fileid=nullrev)
ms.add(fcl, fco, fca, f)
if f1 != f and move:
moves.append(f1)
Matt Mackall
merge: introduce mergestate
r6512
Angel Ezquerra
localrepo: remove all external users of localrepo.wopener...
r23879 audit = repo.wvfs.audit
Mads Kiilerich
merge: move constant assignments a bit and use them more
r21390 _updating = _('updating')
_files = _('files')
progress = repo.ui.progress
Adrian Buehlmann
applyupdates: audit unlinking of renamed files and directories
r14398
Matt Mackall
merge: introduce mergestate
r6512 # remove renamed files after safely stored
for f in moves:
Martin Geisler
util: remove lexists, Python 2.4 introduced os.path.lexists
r12032 if os.path.lexists(repo.wjoin(f)):
Martin Geisler
do not attempt to translate ui.debug output
r9467 repo.ui.debug("removing %s\n" % f)
Adrian Buehlmann
applyupdates: audit unlinking of renamed files and directories
r14398 audit(f)
Mads Kiilerich
merge: use util.unlinkpath for removing moved files...
r18333 util.unlinkpath(repo.wjoin(f))
Matt Mackall
merge: do early copy to deal with issue636...
r5042
Martin von Zweigbergk
merge: don't treat 'diverge' and 'renamedelete' like actions...
r23526 numupdates = sum(len(l) for m, l in actions.items() if m != 'k')
Bryan O'Sullivan
merge: split out mostly-non-interactive working dir updates...
r18630
Mads Kiilerich
merge: use separate lists for each action type...
r21545 if [a for a in actions['r'] if a[0] == '.hgsubstate']:
Bryan O'Sullivan
merge: handle subrepo merges and .hgsubstate specially...
r18632 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
Mads Kiilerich
merge: move constant assignments a bit and use them more
r21390 # remove in parallel (must come first)
Bryan O'Sullivan
merge: report non-interactive progress in chunks...
r18633 z = 0
Mads Kiilerich
merge: use separate lists for each action type...
r21545 prog = worker.worker(repo.ui, 0.001, batchremove, (repo,), actions['r'])
FUJIWARA Katsunori
merge: increase safety of parallel updating/removing on icasefs...
r19095 for i, item in prog:
z += i
Mads Kiilerich
merge: move constant assignments a bit and use them more
r21390 progress(_updating, z, item=item, total=numupdates, unit=_files)
Mads Kiilerich
merge: use separate lists for each action type...
r21545 removed = len(actions['r'])
Mads Kiilerich
merge: move constant assignments a bit and use them more
r21390
# get in parallel
Mads Kiilerich
merge: use separate lists for each action type...
r21545 prog = worker.worker(repo.ui, 0.001, batchget, (repo, mctx), actions['g'])
Bryan O'Sullivan
merge: apply non-interactive working dir updates in parallel...
r18639 for i, item in prog:
Bryan O'Sullivan
merge: report non-interactive progress in chunks...
r18633 z += i
Mads Kiilerich
merge: move constant assignments a bit and use them more
r21390 progress(_updating, z, item=item, total=numupdates, unit=_files)
Mads Kiilerich
merge: use separate lists for each action type...
r21545 updated = len(actions['g'])
Bryan O'Sullivan
merge: split out mostly-non-interactive working dir updates...
r18630
Mads Kiilerich
merge: use separate lists for each action type...
r21545 if [a for a in actions['g'] if a[0] == '.hgsubstate']:
Bryan O'Sullivan
merge: handle subrepo merges and .hgsubstate specially...
r18632 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
Mads Kiilerich
merge: fix stupid indentation left over from previous refactorings
r21551 # forget (manifest only, just log it) (must come first)
for f, args, msg in actions['f']:
repo.ui.debug(" %s: %s -> f\n" % (f, msg))
z += 1
progress(_updating, z, item=f, total=numupdates, unit=_files)
Mads Kiilerich
merge: change debug logging - test output changes but no real changes...
r21391
Mads Kiilerich
merge: fix stupid indentation left over from previous refactorings
r21551 # re-add (manifest only, just log it)
for f, args, msg in actions['a']:
repo.ui.debug(" %s: %s -> a\n" % (f, msg))
z += 1
progress(_updating, z, item=f, total=numupdates, unit=_files)
Mads Kiilerich
merge: change debug logging - test output changes but no real changes...
r21391
Siddharth Agarwal
merge: add a new action type representing files to add/mark as modified...
r27131 # re-add/mark as modified (manifest only, just log it)
for f, args, msg in actions['am']:
repo.ui.debug(" %s: %s -> am\n" % (f, msg))
z += 1
progress(_updating, z, item=f, total=numupdates, unit=_files)
Mads Kiilerich
merge: fix stupid indentation left over from previous refactorings
r21551 # keep (noop, just log it)
for f, args, msg in actions['k']:
repo.ui.debug(" %s: %s -> k\n" % (f, msg))
# no progress
Mads Kiilerich
merge: change debug logging - test output changes but no real changes...
r21391
Mads Kiilerich
merge: fix stupid indentation left over from previous refactorings
r21551 # directory rename, move local
for f, args, msg in actions['dm']:
repo.ui.debug(" %s: %s -> dm\n" % (f, msg))
z += 1
progress(_updating, z, item=f, total=numupdates, unit=_files)
f0, flags = args
repo.ui.note(_("moving %s to %s\n") % (f0, f))
audit(f)
repo.wwrite(f, wctx.filectx(f0).data(), flags)
util.unlinkpath(repo.wjoin(f0))
updated += 1
Mads Kiilerich
merge: change debug logging - test output changes but no real changes...
r21391
Mads Kiilerich
merge: fix stupid indentation left over from previous refactorings
r21551 # local directory rename, get
for f, args, msg in actions['dg']:
repo.ui.debug(" %s: %s -> dg\n" % (f, msg))
z += 1
progress(_updating, z, item=f, total=numupdates, unit=_files)
f0, flags = args
repo.ui.note(_("getting %s to %s\n") % (f0, f))
repo.wwrite(f, mctx.filectx(f0).data(), flags)
updated += 1
Mads Kiilerich
merge: change debug logging - test output changes but no real changes...
r21391
Mads Kiilerich
merge: fix stupid indentation left over from previous refactorings
r21551 # exec
for f, args, msg in actions['e']:
repo.ui.debug(" %s: %s -> e\n" % (f, msg))
z += 1
progress(_updating, z, item=f, total=numupdates, unit=_files)
flags, = args
audit(f)
util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
updated += 1
Mads Kiilerich
merge: change debug logging - test output changes but no real changes...
r21391
Siddharth Agarwal
merge.applyupdates: call driverpreprocess before starting merge actions...
r26786 # the ordering is important here -- ms.mergedriver will raise if the merge
# driver has changed, and we want to be able to bypass it when overwrite is
# True
usemergedriver = not overwrite and mergeactions and ms.mergedriver
if usemergedriver:
ms.commit()
proceed = driverpreprocess(repo, ms, wctx, labels=labels)
# the driver might leave some files unresolved
unresolvedf = set(ms.unresolved())
if not proceed:
# XXX setting unresolved to at least 1 is a hack to make sure we
# error out
return updated, merged, removed, max(len(unresolvedf), 1)
newactions = []
for f, args, msg in mergeactions:
if f in unresolvedf:
newactions.append((f, args, msg))
mergeactions = newactions
Siddharth Agarwal
merge.mergestate: perform all premerges before any merges (BC)...
r26618 # premerge
tocomplete = []
Siddharth Agarwal
merge.applyupdates: only attempt to merge files in mergeactions...
r26949 for f, args, msg in mergeactions:
Siddharth Agarwal
merge.mergestate: perform all premerges before any merges (BC)...
r26618 repo.ui.debug(" %s: %s -> m (premerge)\n" % (f, msg))
Siddharth Agarwal
merge: move merge step to the end...
r26292 z += 1
progress(_updating, z, item=f, total=numupdates, unit=_files)
if f == '.hgsubstate': # subrepo states need updating
subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
overwrite)
continue
audit(f)
Siddharth Agarwal
merge: introduce a preresolve function...
r26617 complete, r = ms.preresolve(f, wctx, labels=labels)
Siddharth Agarwal
merge.applyupdates: use counters from mergestate...
r27078 if not complete:
Siddharth Agarwal
merge.mergestate: perform all premerges before any merges (BC)...
r26618 numupdates += 1
tocomplete.append((f, args, msg))
# merge
for f, args, msg in tocomplete:
repo.ui.debug(" %s: %s -> m (merge)\n" % (f, msg))
z += 1
progress(_updating, z, item=f, total=numupdates, unit=_files)
Siddharth Agarwal
merge.applyupdates: use counters from mergestate...
r27078 ms.resolve(f, wctx, labels=labels)
Siddharth Agarwal
merge: move merge step to the end...
r26292
Peter Arrenbrecht
merge: delay writing the mergestate during until commit is called...
r12369 ms.commit()
Siddharth Agarwal
merge.applyupdates: call driverconclude after performing merge actions...
r26787
Siddharth Agarwal
merge.applyupdates: use counters from mergestate...
r27078 unresolved = ms.unresolvedcount()
Siddharth Agarwal
merge.applyupdates: call driverconclude after performing merge actions...
r26787 if usemergedriver and not unresolved and ms.mdstate() != 's':
if not driverconclude(repo, ms, wctx, labels=labels):
# XXX setting unresolved to at least 1 is a hack to make sure we
# error out
Siddharth Agarwal
merge.applyupdates: don't return early if merge driver's conclude failed...
r26975 unresolved = max(unresolved, 1)
Siddharth Agarwal
merge.applyupdates: call driverconclude after performing merge actions...
r26787
ms.commit()
Siddharth Agarwal
merge.applyupdates: use counters from mergestate...
r27078 msupdated, msmerged, msremoved = ms.counts()
updated += msupdated
merged += msmerged
removed += msremoved
Siddharth Agarwal
merge.applyupdates: extend action queues with ones returned from mergestate...
r27080
extraactions = ms.actions()
Siddharth Agarwal
merge.applyupdates: add all actions returned from merge state...
r27121 for k, acts in extraactions.iteritems():
actions[k].extend(acts)
Siddharth Agarwal
merge.applyupdates: extend action queues with ones returned from mergestate...
r27080
Bryan O'Sullivan
merge: don't fiddle with name lookups or i18n in hot loops...
r18640 progress(_updating, None, total=numupdates, unit=_files)
Matt Mackall
merge: move apply and dirstate code into separate functions
r3111
return updated, merged, removed, unresolved
Mads Kiilerich
merge: rename list of actions from action to actions
r18330 def recordupdates(repo, actions, branchmerge):
Matt Mackall
merge: update some docstrings
r3315 "record merge actions to the dirstate"
Mads Kiilerich
merge: fix stupid indentation left over from previous refactorings
r21551 # remove (must come first)
Siddharth Agarwal
merge.recordupdates: don't require action keys to be present in dict...
r27087 for f, args, msg in actions.get('r', []):
Mads Kiilerich
merge: fix stupid indentation left over from previous refactorings
r21551 if branchmerge:
repo.dirstate.remove(f)
else:
Mads Kiilerich
merge: change priority / ordering of merge actions...
r21389 repo.dirstate.drop(f)
Mads Kiilerich
merge: change debug logging - test output changes but no real changes...
r21391
Mads Kiilerich
merge: fix stupid indentation left over from previous refactorings
r21551 # forget (must come first)
Siddharth Agarwal
merge.recordupdates: don't require action keys to be present in dict...
r27087 for f, args, msg in actions.get('f', []):
Mads Kiilerich
merge: fix stupid indentation left over from previous refactorings
r21551 repo.dirstate.drop(f)
Mads Kiilerich
merge: change debug logging - test output changes but no real changes...
r21391
Mads Kiilerich
merge: fix stupid indentation left over from previous refactorings
r21551 # re-add
Siddharth Agarwal
merge.recordupdates: don't require action keys to be present in dict...
r27087 for f, args, msg in actions.get('a', []):
Siddharth Agarwal
merge.recordupdates: mark 'a' files as added unconditionally...
r27132 repo.dirstate.add(f)
Mads Kiilerich
merge: change debug logging - test output changes but no real changes...
r21391
Siddharth Agarwal
merge: add a new action type representing files to add/mark as modified...
r27131 # re-add/mark as modified
for f, args, msg in actions.get('am', []):
if branchmerge:
repo.dirstate.normallookup(f)
else:
Mads Kiilerich
merge: fix stupid indentation left over from previous refactorings
r21551 repo.dirstate.add(f)
Mads Kiilerich
merge: change debug logging - test output changes but no real changes...
r21391
Mads Kiilerich
merge: fix stupid indentation left over from previous refactorings
r21551 # exec change
Siddharth Agarwal
merge.recordupdates: don't require action keys to be present in dict...
r27087 for f, args, msg in actions.get('e', []):
Mads Kiilerich
merge: fix stupid indentation left over from previous refactorings
r21551 repo.dirstate.normallookup(f)
Mads Kiilerich
merge: change debug logging - test output changes but no real changes...
r21391
Mads Kiilerich
merge: fix stupid indentation left over from previous refactorings
r21551 # keep
Siddharth Agarwal
merge.recordupdates: don't require action keys to be present in dict...
r27087 for f, args, msg in actions.get('k', []):
Mads Kiilerich
merge: fix stupid indentation left over from previous refactorings
r21551 pass
# get
Siddharth Agarwal
merge.recordupdates: don't require action keys to be present in dict...
r27087 for f, args, msg in actions.get('g', []):
Mads Kiilerich
merge: fix stupid indentation left over from previous refactorings
r21551 if branchmerge:
repo.dirstate.otherparent(f)
else:
repo.dirstate.normal(f)
Mads Kiilerich
merge: change debug logging - test output changes but no real changes...
r21391
Mads Kiilerich
merge: fix stupid indentation left over from previous refactorings
r21551 # merge
Siddharth Agarwal
merge.recordupdates: don't require action keys to be present in dict...
r27087 for f, args, msg in actions.get('m', []):
Mads Kiilerich
merge: fix stupid indentation left over from previous refactorings
r21551 f1, f2, fa, move, anc = args
if branchmerge:
# We've done a branch merge, mark this file as merged
# so that we properly record the merger later
repo.dirstate.merge(f)
if f1 != f2: # copy/rename
Matt Mackall
merge: unify merge and copy actions
r3308 if move:
Mads Kiilerich
merge: fix stupid indentation left over from previous refactorings
r21551 repo.dirstate.remove(f1)
if f1 != f:
repo.dirstate.copy(f1, f)
else:
repo.dirstate.copy(f2, f)
else:
# We've update-merged a locally modified file, so
# we set the dirstate to emulate a normal checkout
# of that file some time in the past. Thus our
# merge will appear as a normal local file
# modification.
if f2 == f: # file not locally copied/moved
repo.dirstate.normallookup(f)
if move:
repo.dirstate.drop(f1)
Mads Kiilerich
merge: change debug logging - test output changes but no real changes...
r21391
Mads Kiilerich
merge: fix stupid indentation left over from previous refactorings
r21551 # directory rename, move local
Siddharth Agarwal
merge.recordupdates: don't require action keys to be present in dict...
r27087 for f, args, msg in actions.get('dm', []):
Mads Kiilerich
merge: fix stupid indentation left over from previous refactorings
r21551 f0, flag = args
if branchmerge:
repo.dirstate.add(f)
repo.dirstate.remove(f0)
repo.dirstate.copy(f0, f)
else:
repo.dirstate.normal(f)
repo.dirstate.drop(f0)
Mads Kiilerich
merge: change debug logging - test output changes but no real changes...
r21391
Mads Kiilerich
merge: fix stupid indentation left over from previous refactorings
r21551 # directory rename, get
Siddharth Agarwal
merge.recordupdates: don't require action keys to be present in dict...
r27087 for f, args, msg in actions.get('dg', []):
Mads Kiilerich
merge: fix stupid indentation left over from previous refactorings
r21551 f0, flag = args
if branchmerge:
repo.dirstate.add(f)
repo.dirstate.copy(f0, f)
else:
repo.dirstate.normal(f)
Matt Mackall
merge: move apply and dirstate code into separate functions
r3111
Augie Fackler
merge: have merge.update use a matcher instead of partial fn...
r27344 def update(repo, node, branchmerge, force, ancestor=None,
mergeancestor=False, labels=None, matcher=None):
Matt Mackall
merge: update some docstrings
r3315 """
Perform a merge between the working directory and the given node
Stuart W Marks
update: add comments and test cases for updating across branches...
r9716 node = the node to update to, or None if unspecified
Matt Mackall
merge: update some docstrings
r3315 branchmerge = whether to merge between branches
force = whether to force branch merging or file overwriting
Augie Fackler
merge: have merge.update use a matcher instead of partial fn...
r27344 matcher = a matcher to filter file lists (dirstate not updated)
Durham Goode
rebase: fix --collapse when a file was added then removed...
r18778 mergeancestor = whether it is merging with an ancestor. If true,
we should accept the incoming changes for any prompts that occur.
If false, merging with an ancestor (fast-forward) is only allowed
between different named branches. This flag is used by rebase extension
as a temporary fix and should be avoided in general.
Stuart W Marks
update: add comments and test cases for updating across branches...
r9716
The table below shows all the behaviors of the update command
given the -c and -C or no options, whether the working directory
is dirty, whether a revision is specified, and the relationship of
the parent rev to the target rev (linear, on the same named
branch, or on another named branch).
Adrian Buehlmann
combine tests
r12279 This logic is tested by test-update-branches.t.
Stuart W Marks
update: add comments and test cases for updating across branches...
r9716
Augie Fackler
merge: improve clarity of table in update docstring
r26821 -c -C dirty rev | linear same cross
n n n n | ok (1) x
n n n y | ok ok ok
n n y n | merge (2) (2)
n n y y | merge (3) (3)
n y * * | discard discard discard
y n y * | (4) (4) (4)
y n n * | ok ok ok
y y * * | (5) (5) (5)
Stuart W Marks
update: add comments and test cases for updating across branches...
r9716
x = can't happen
* = don't-care
Siddharth Agarwal
update: improve error message for clean non-linear update
r19798 1 = abort: not a linear update (merge or update --check to force update)
Siddharth Agarwal
update: improve error message for dirty non-linear update with rev
r19800 2 = abort: uncommitted changes (commit and merge, or update --clean to
discard changes)
Siddharth Agarwal
update: add error message for dirty non-linear update with no rev...
r19799 3 = abort: uncommitted changes (commit or update --clean to discard changes)
Siddharth Agarwal
update: standardize error message for dirty update --check...
r19801 4 = abort: uncommitted changes (checked in commands.py)
Siddharth Agarwal
update: add error message for dirty non-linear update with no rev...
r19799 5 = incompatible options (checked in commands.py)
Greg Ward
merge: document some internal return values.
r13162
Return the same tuple as applyupdates().
Matt Mackall
merge: update some docstrings
r3315 """
Matt Mackall
Merge: combine force and forcemerge arguments
r2815
Stuart W Marks
update: allow branch crossing without -c or -C, with no uncommitted changes...
r9717 onode = node
Augie Fackler
merge: have merge.update use a matcher instead of partial fn...
r27344 # If we're doing a partial update, we need to skip updating
# the dirstate, so make a note of any partial-ness to the
# update here.
if matcher is None or matcher.always():
partial = False
else:
partial = True
Bryan O'Sullivan
with: use context manager in merge update
r27852 with repo.wlock():
Matt Mackall
use repo[changeid] to get a changectx
r6747 wc = repo[None]
Sean Farley
merge: refactor initialization of variables in update...
r20279 pl = wc.parents()
p1 = pl[0]
Mads Kiilerich
merge: pass merge ancestor to calculateupdates as a list...
r21081 pas = [None]
Mads Kiilerich
merge: 0 is a valid ancestor different from None...
r23405 if ancestor is not None:
Mads Kiilerich
merge: pass merge ancestor to calculateupdates as a list...
r21081 pas = [repo[ancestor]]
Sean Farley
merge: refactor initialization of variables in update...
r20279
Matt Mackall
Use try/finally pattern to cleanup locks and transactions
r4915 if node is None:
Pierre-Yves David
update: "deprecate" call to 'merge.update' without a destination...
r26682 if (repo.ui.configbool('devel', 'all-warnings')
or repo.ui.configbool('devel', 'oldapi')):
repo.ui.develwarn('update with no target')
Pierre-Yves David
destupdate: also include bookmark related logic...
r26641 rev, _mark, _act = destutil.destupdate(repo)
node = repo[rev].node()
Sean Farley
merge: consider successor changesets for a bare update...
r20280
Matt Mackall
Use try/finally pattern to cleanup locks and transactions
r4915 overwrite = force and not branchmerge
Sean Farley
merge: refactor initialization of variables in update...
r20279
p2 = repo[node]
Mads Kiilerich
merge: pass merge ancestor to calculateupdates as a list...
r21081 if pas[0] is None:
Matt Mackall
merge: make merge.preferancestor type and default consistent...
r25844 if repo.ui.configlist('merge', 'preferancestor', ['*']) == ['*']:
Mads Kiilerich
merge: with merge.preferancestor=*, run an auction with bids from ancestors...
r21128 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
else:
Mads Kiilerich
merge: show the scary multiple ancestor hint for merges only, not for updates...
r22179 pas = [p1.ancestor(p2, warn=branchmerge)]
Matt Mackall
merge: add ancestor to the update function...
r13874
Matt Mackall
Use try/finally pattern to cleanup locks and transactions
r4915 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
Matt Mackall
merge: various tidying...
r3314
Matt Mackall
Use try/finally pattern to cleanup locks and transactions
r4915 ### check phase
Martin von Zweigbergk
merge: refuse update/merge if there are unresolved conflicts (BC)...
r27316 if not overwrite:
if len(pl) > 1:
raise error.Abort(_("outstanding uncommitted merge"))
ms = mergestate.read(repo)
if list(ms.unresolved()):
raise error.Abort(_("outstanding merge conflicts"))
Matt Mackall
update: better logic and messages for updates...
r6375 if branchmerge:
Mads Kiilerich
merge: pass merge ancestor to calculateupdates as a list...
r21081 if pas == [p2]:
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_("merging with a working directory ancestor"
Matt Mackall
merge: improve merge with ancestor message
r11417 " has no effect"))
Mads Kiilerich
merge: pass merge ancestor to calculateupdates as a list...
r21081 elif pas == [p1]:
Patrick Mezard
rebase: allow collapsing branches in place (issue3111)...
r16696 if not mergeancestor and p1.branch() == p2.branch():
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_("nothing to merge"),
Kevin Bullock
merge: make 'nothing to merge' aborts consistent...
r15619 hint=_("use 'hg update' "
"or check 'hg heads'"))
Matt Mackall
update: better logic and messages for updates...
r6375 if not force and (wc.files() or wc.deleted()):
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(_("uncommitted changes"),
Kevin Bullock
merge: make 'nothing to merge' aborts consistent...
r15619 hint=_("use 'hg status' to list changes"))
Mads Kiilerich
subrepos: process subrepos in sorted order...
r18364 for s in sorted(wc.substate):
FUJIWARA Katsunori
subrepo: add bailifchanged to centralize raising Abort if subrepo is dirty...
r24471 wc.sub(s).bailifchanged()
Oleg Stepanov
Do not allow merging with uncommitted changes in a subrepo
r13437
Matt Mackall
update: better logic and messages for updates...
r6375 elif not overwrite:
Siddharth Agarwal
merge: exit early during a no-op update (BC)...
r19929 if p1 == p2: # no-op update
# call the hooks and exit early
repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
repo.hook('update', parent1=xp2, parent2='', error=0)
return 0, 0, 0, 0
Mads Kiilerich
merge: pass merge ancestor to calculateupdates as a list...
r21081 if pas not in ([p1], [p2]): # nonlinear
Pierre-Yves David
update: allow dirty update to foreground (successors)...
r18985 dirty = wc.dirty(missing=True)
if dirty or onode is None:
# Branching is a bit strange to ensure we do the minimal
# amount of call to obsolete.background.
foreground = obsolete.foreground(repo, [p1.node()])
# note: the <node> variable contains a random identifier
if repo[node].node() in foreground:
Mads Kiilerich
merge: pass merge ancestor to calculateupdates as a list...
r21081 pas = [p1] # allow updating to successors
Siddharth Agarwal
update: add error message for dirty non-linear update with no rev...
r19799 elif dirty:
msg = _("uncommitted changes")
Siddharth Agarwal
update: improve error message for dirty non-linear update with rev
r19800 if onode is None:
hint = _("commit and merge, or update --clean to"
" discard changes")
else:
hint = _("commit or update --clean to discard"
" changes")
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(msg, hint=hint)
Pierre-Yves David
update: allow dirty update to foreground (successors)...
r18985 else: # node is none
Siddharth Agarwal
update: improve error message for clean non-linear update
r19798 msg = _("not a linear update")
hint = _("merge or update --check to force update")
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 raise error.Abort(msg, hint=hint)
Pierre-Yves David
update: allow dirty update to foreground (successors)...
r18985 else:
# Allow jumping branches if clean and specific rev given
Mads Kiilerich
merge: pass merge ancestor to calculateupdates as a list...
r21081 pas = [p1]
Matt Mackall
Merge: move most tests to the beginning
r2814
Matt Mackall
merge: mark ancient debugging option
r25843 # deprecated config: merge.followcopies
Mads Kiilerich
merge: move ancestor selection tweaking from manifestmerge to update function...
r21080 followcopies = False
if overwrite:
Mads Kiilerich
merge: pass merge ancestor to calculateupdates as a list...
r21081 pas = [wc]
elif pas == [p2]: # backwards
pas = [wc.p1()]
Mads Kiilerich
merge: move ancestor selection tweaking from manifestmerge to update function...
r21080 elif not branchmerge and not wc.dirty(missing=True):
pass
Martin von Zweigbergk
merge: consistently use single quotes for non-user-facing strings...
r23380 elif pas[0] and repo.ui.configbool('merge', 'followcopies', True):
Mads Kiilerich
merge: move ancestor selection tweaking from manifestmerge to update function...
r21080 followcopies = True
Matt Mackall
Use try/finally pattern to cleanup locks and transactions
r4915 ### calculate phase
Martin von Zweigbergk
merge: make calculateupdates() return file->action dict...
r23641 actionbyfile, diverge, renamedelete = calculateupdates(
Augie Fackler
merge: restate calculateupdates in terms of a matcher...
r27345 repo, wc, p2, pas, branchmerge, force, mergeancestor,
followcopies, matcher=matcher)
Siddharth Agarwal
merge: don't try to merge subrepos twice (issue4988)...
r27951
# Prompt and create actions. Most of this is in the resolve phase
# already, but we can't handle .hgsubstate in filemerge or
# subrepo.submerge yet so we have to keep prompting for it.
if '.hgsubstate' in actionbyfile:
f = '.hgsubstate'
m, args, msg = actionbyfile[f]
if m == 'cd':
if repo.ui.promptchoice(
_("local changed %s which remote deleted\n"
"use (c)hanged version or (d)elete?"
"$$ &Changed $$ &Delete") % f, 0):
actionbyfile[f] = ('r', None, "prompt delete")
elif f in p1:
actionbyfile[f] = ('am', None, "prompt keep")
else:
actionbyfile[f] = ('a', None, "prompt keep")
elif m == 'dc':
f1, f2, fa, move, anc = args
flags = p2[f2].flags()
if repo.ui.promptchoice(
_("remote changed %s which local deleted\n"
"use (c)hanged version or leave (d)eleted?"
"$$ &Changed $$ &Deleted") % f, 0) == 0:
actionbyfile[f] = ('g', (flags, False), "prompt recreating")
else:
del actionbyfile[f]
Martin von Zweigbergk
merge: make calculateupdates() return file->action dict...
r23641 # Convert to dictionary-of-lists format
Siddharth Agarwal
merge: add a new action type representing files to add/mark as modified...
r27131 actions = dict((m, []) for m in 'a am f g cd dc r dm dg m e k'.split())
Martin von Zweigbergk
merge: make calculateupdates() return file->action dict...
r23641 for f, (m, args, msg) in actionbyfile.iteritems():
if m not in actions:
actions[m] = []
actions[m].append((f, args, msg))
Matt Mackall
Move merge code to its own module...
r2775
Martin von Zweigbergk
merge: perform case-collision checking on final set of actions...
r23544 if not util.checkcase(repo.path):
# check collision between files only in p2 for clean update
if (not branchmerge and
(force or not wc.dirty(missing=True, branch=False))):
_checkcollision(repo, p2.manifest(), None)
else:
_checkcollision(repo, wc.manifest(), actions)
Martin von Zweigbergk
merge: move dr/rd warning messages out of applyupdates()...
r23525 # divergent renames
Martin von Zweigbergk
merge: don't treat 'diverge' and 'renamedelete' like actions...
r23526 for f, fl in sorted(diverge.iteritems()):
Martin von Zweigbergk
merge: move dr/rd warning messages out of applyupdates()...
r23525 repo.ui.warn(_("note: possible conflict - %s was renamed "
"multiple times to:\n") % f)
for nf in fl:
repo.ui.warn(" %s\n" % nf)
# rename and delete
Martin von Zweigbergk
merge: don't treat 'diverge' and 'renamedelete' like actions...
r23526 for f, fl in sorted(renamedelete.iteritems()):
Martin von Zweigbergk
merge: move dr/rd warning messages out of applyupdates()...
r23525 repo.ui.warn(_("note: possible conflict - %s was deleted "
"and renamed to:\n") % f)
for nf in fl:
repo.ui.warn(" %s\n" % nf)
Martin von Zweigbergk
merge: move messages about possible conflicts a litte earlier...
r26957 ### apply phase
if not branchmerge: # just jump to the new rev
fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
if not partial:
repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
# note that we're in the middle of an update
repo.vfs.write('updatestate', p2.hex())
stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels)
Matt Mackall
Use try/finally pattern to cleanup locks and transactions
r4915 if not partial:
Durham Goode
dirstate: wrap setparent calls with begin/endparentchange (issue4353)...
r22405 repo.dirstate.beginparentchange()
Patrick Mezard
localrepo: add setparents() to adjust dirstate copies (issue3407)...
r16551 repo.setparents(fp1, fp2)
Mads Kiilerich
merge: rename list of actions from action to actions
r18330 recordupdates(repo, actions, branchmerge)
Matt Mackall
update: add tracking of interrupted updates (issue3113)...
r19482 # update completed, clear state
util.unlink(repo.join('updatestate'))
Mads Kiilerich
merge: remove last traces of fastforward merging...
r13561 if not branchmerge:
Matt Mackall
Use try/finally pattern to cleanup locks and transactions
r4915 repo.dirstate.setbranch(p2.branch())
Durham Goode
dirstate: wrap setparent calls with begin/endparentchange (issue4353)...
r22405 repo.dirstate.endparentchange()
Sune Foldager
run commit and update hooks after command completion (issue1827)...
r10492
if not partial:
FUJIWARA Katsunori
merge: make in-memory changes visible to external update hooks...
r26752 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
Sune Foldager
run commit and update hooks after command completion (issue1827)...
r10492 return stats
Matt Mackall
merge: add merge.graft helper...
r22902
Andrew Halberstadt
merge.graft: add option to keep second parent...
r27267 def graft(repo, ctx, pctx, labels, keepparent=False):
Matt Mackall
merge: add merge.graft helper...
r22902 """Do a graft-like merge.
This is a merge where the merge ancestor is chosen such that one
or more changesets are grafted onto the current changeset. In
addition to the merge, this fixes up the dirstate to include only
Andrew Halberstadt
merge.graft: add option to keep second parent...
r27267 a single parent (if keepparent is False) and tries to duplicate any
renames/copies appropriately.
Matt Mackall
merge: add merge.graft helper...
r22902
ctx - changeset to rebase
pctx - merge base, usually ctx.p1()
labels - merge labels eg ['local', 'graft']
Andrew Halberstadt
merge.graft: add option to keep second parent...
r27267 keepparent - keep second parent if any
Matt Mackall
merge: add merge.graft helper...
r22902
"""
Durham Goode
graft: allow creating sibling grafts...
r24643 # If we're grafting a descendant onto an ancestor, be sure to pass
# mergeancestor=True to update. This does two things: 1) allows the merge if
# the destination is the same as the parent of the ctx (so we can use graft
# to copy commits), and 2) informs update that the incoming changes are
# newer than the destination so it doesn't prompt about "remote changed foo
# which local deleted".
mergeancestor = repo.changelog.isancestor(repo['.'].node(), ctx.node())
Matt Mackall
merge: add merge.graft helper...
r22902
Augie Fackler
merge: have merge.update use a matcher instead of partial fn...
r27344 stats = update(repo, ctx.node(), True, True, pctx.node(),
Durham Goode
graft: allow creating sibling grafts...
r24643 mergeancestor=mergeancestor, labels=labels)
Andrew Halberstadt
merge.graft: add option to keep second parent...
r27267 pother = nullid
parents = ctx.parents()
if keepparent and len(parents) == 2 and pctx in parents:
parents.remove(pctx)
pother = parents[0].node()
Matt Mackall
merge: add merge.graft helper...
r22902 repo.dirstate.beginparentchange()
Andrew Halberstadt
merge.graft: add option to keep second parent...
r27267 repo.setparents(repo['.'].node(), pother)
FUJIWARA Katsunori
dirstate: make dirstate.write() callers pass transaction object to it...
r26748 repo.dirstate.write(repo.currenttransaction())
Matt Mackall
merge: add merge.graft helper...
r22902 # fix up dirstate for copies and renames
copies.duplicatecopies(repo, ctx.rev(), pctx.rev())
repo.dirstate.endparentchange()
return stats