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