comparison mercurial/commit.py @ 45204:ce9ee81df9ff

commitctx: extract _filecommit too This function is exclusively used in `commitctx`. So we should extract it too for consistency and to reduce the `localrepo` bloat. This is part of a larger refactoring/cleanup of the commitctx code to clarify and augment the logic gathering metadata useful for copy tracing. The current code is a tad too long and entangled to make such update easy. Differential Revision: https://phab.mercurial-scm.org/D8710
author Pierre-Yves David <pierre-yves.david@octobus.net>
date Tue, 07 Jul 2020 00:18:15 +0200
parents ae5c1a3bc339
children d056a131c93f
comparison
equal deleted inserted replaced
45203:ae5c1a3bc339 45204:ce9ee81df9ff
9 import weakref 9 import weakref
10 10
11 from .i18n import _ 11 from .i18n import _
12 from .node import ( 12 from .node import (
13 hex, 13 hex,
14 nullid,
14 nullrev, 15 nullrev,
15 ) 16 )
16 17
17 from . import ( 18 from . import (
19 context,
20 mergestate,
18 metadata, 21 metadata,
19 phases, 22 phases,
20 scmutil, 23 scmutil,
21 subrepoutil, 24 subrepoutil,
22 ) 25 )
96 fctx = ctx[f] 99 fctx = ctx[f]
97 if fctx is None: 100 if fctx is None:
98 removed.append(f) 101 removed.append(f)
99 else: 102 else:
100 added.append(f) 103 added.append(f)
101 m[f], is_touched = repo._filecommit( 104 m[f], is_touched = _filecommit(
102 fctx, m1, m2, linkrev, trp, writefilecopymeta, 105 repo, fctx, m1, m2, linkrev, trp, writefilecopymeta,
103 ) 106 )
104 if is_touched: 107 if is_touched:
105 touched.append(f) 108 touched.append(f)
106 if writechangesetcopy and is_touched == 'added': 109 if writechangesetcopy and is_touched == 'added':
107 filesadded.append(f) 110 filesadded.append(f)
211 # be compliant anyway 214 # be compliant anyway
212 # 215 #
213 # if minimal phase was 0 we don't need to retract anything 216 # if minimal phase was 0 we don't need to retract anything
214 phases.registernew(repo, tr, targetphase, [n]) 217 phases.registernew(repo, tr, targetphase, [n])
215 return n 218 return n
219
220
221 def _filecommit(
222 repo, fctx, manifest1, manifest2, linkrev, tr, includecopymeta,
223 ):
224 """
225 commit an individual file as part of a larger transaction
226
227 input:
228
229 fctx: a file context with the content we are trying to commit
230 manifest1: manifest of changeset first parent
231 manifest2: manifest of changeset second parent
232 linkrev: revision number of the changeset being created
233 tr: current transation
234 individual: boolean, set to False to skip storing the copy data
235 (only used by the Google specific feature of using
236 changeset extra as copy source of truth).
237
238 output: (filenode, touched)
239
240 filenode: the filenode that should be used by this changeset
241 touched: one of: None, 'added' or 'modified'
242 """
243
244 fname = fctx.path()
245 fparent1 = manifest1.get(fname, nullid)
246 fparent2 = manifest2.get(fname, nullid)
247 touched = None
248 if fparent1 == fparent2 == nullid:
249 touched = 'added'
250
251 if isinstance(fctx, context.filectx):
252 # This block fast path most comparisons which are usually done. It
253 # assumes that bare filectx is used and no merge happened, hence no
254 # need to create a new file revision in this case.
255 node = fctx.filenode()
256 if node in [fparent1, fparent2]:
257 repo.ui.debug(b'reusing %s filelog entry\n' % fname)
258 if (
259 fparent1 != nullid and manifest1.flags(fname) != fctx.flags()
260 ) or (
261 fparent2 != nullid and manifest2.flags(fname) != fctx.flags()
262 ):
263 touched = 'modified'
264 return node, touched
265
266 flog = repo.file(fname)
267 meta = {}
268 cfname = fctx.copysource()
269 fnode = None
270
271 if cfname and cfname != fname:
272 # Mark the new revision of this file as a copy of another
273 # file. This copy data will effectively act as a parent
274 # of this new revision. If this is a merge, the first
275 # parent will be the nullid (meaning "look up the copy data")
276 # and the second one will be the other parent. For example:
277 #
278 # 0 --- 1 --- 3 rev1 changes file foo
279 # \ / rev2 renames foo to bar and changes it
280 # \- 2 -/ rev3 should have bar with all changes and
281 # should record that bar descends from
282 # bar in rev2 and foo in rev1
283 #
284 # this allows this merge to succeed:
285 #
286 # 0 --- 1 --- 3 rev4 reverts the content change from rev2
287 # \ / merging rev3 and rev4 should use bar@rev2
288 # \- 2 --- 4 as the merge base
289 #
290
291 cnode = manifest1.get(cfname)
292 newfparent = fparent2
293
294 if manifest2: # branch merge
295 if fparent2 == nullid or cnode is None: # copied on remote side
296 if cfname in manifest2:
297 cnode = manifest2[cfname]
298 newfparent = fparent1
299
300 # Here, we used to search backwards through history to try to find
301 # where the file copy came from if the source of a copy was not in
302 # the parent directory. However, this doesn't actually make sense to
303 # do (what does a copy from something not in your working copy even
304 # mean?) and it causes bugs (eg, issue4476). Instead, we will warn
305 # the user that copy information was dropped, so if they didn't
306 # expect this outcome it can be fixed, but this is the correct
307 # behavior in this circumstance.
308
309 if cnode:
310 repo.ui.debug(b" %s: copy %s:%s\n" % (fname, cfname, hex(cnode)))
311 if includecopymeta:
312 meta[b"copy"] = cfname
313 meta[b"copyrev"] = hex(cnode)
314 fparent1, fparent2 = nullid, newfparent
315 else:
316 repo.ui.warn(
317 _(
318 b"warning: can't find ancestor for '%s' "
319 b"copied from '%s'!\n"
320 )
321 % (fname, cfname)
322 )
323
324 elif fparent1 == nullid:
325 fparent1, fparent2 = fparent2, nullid
326 elif fparent2 != nullid:
327 # is one parent an ancestor of the other?
328 fparentancestors = flog.commonancestorsheads(fparent1, fparent2)
329 if fparent1 in fparentancestors:
330 fparent1, fparent2 = fparent2, nullid
331 elif fparent2 in fparentancestors:
332 fparent2 = nullid
333 elif not fparentancestors:
334 # TODO: this whole if-else might be simplified much more
335 ms = mergestate.mergestate.read(repo)
336 if (
337 fname in ms
338 and ms[fname] == mergestate.MERGE_RECORD_MERGED_OTHER
339 ):
340 fparent1, fparent2 = fparent2, nullid
341
342 # is the file changed?
343 text = fctx.data()
344 if fparent2 != nullid or meta or flog.cmp(fparent1, text):
345 if touched is None: # do not overwrite added
346 touched = 'modified'
347 fnode = flog.add(text, meta, tr, linkrev, fparent1, fparent2)
348 # are just the flags changed during merge?
349 elif fname in manifest1 and manifest1.flags(fname) != fctx.flags():
350 touched = 'modified'
351 fnode = fparent1
352 else:
353 fnode = fparent1
354 return fnode, touched