Mercurial > hg
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 |