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 |