##// END OF EJS Templates
commitctx: extract _filecommit too...
marmoute -
r45760:ce9ee81d default
parent child Browse files
Show More
@@ -11,10 +11,13 b' import weakref'
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,
@@ -98,8 +101,8 b' def commitctx(repo, ctx, error=False, or'
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)
@@ -213,3 +216,139 b' def commitctx(repo, ctx, error=False, or'
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
@@ -2771,145 +2771,6 b' class localrepository(object):'
2771 """Returns the wlock if it's held, or None if it's not."""
2771 """Returns the wlock if it's held, or None if it's not."""
2772 return self._currentlock(self._wlockref)
2772 return self._currentlock(self._wlockref)
2773
2773
2774 def _filecommit(
2775 self, fctx, manifest1, manifest2, linkrev, tr, includecopymeta,
2776 ):
2777 """
2778 commit an individual file as part of a larger transaction
2779
2780 input:
2781
2782 fctx: a file context with the content we are trying to commit
2783 manifest1: manifest of changeset first parent
2784 manifest2: manifest of changeset second parent
2785 linkrev: revision number of the changeset being created
2786 tr: current transation
2787 individual: boolean, set to False to skip storing the copy data
2788 (only used by the Google specific feature of using
2789 changeset extra as copy source of truth).
2790
2791 output: (filenode, touched)
2792
2793 filenode: the filenode that should be used by this changeset
2794 touched: one of: None, 'added' or 'modified'
2795 """
2796
2797 fname = fctx.path()
2798 fparent1 = manifest1.get(fname, nullid)
2799 fparent2 = manifest2.get(fname, nullid)
2800 touched = None
2801 if fparent1 == fparent2 == nullid:
2802 touched = 'added'
2803
2804 if isinstance(fctx, context.filectx):
2805 # This block fast path most comparisons which are usually done. It
2806 # assumes that bare filectx is used and no merge happened, hence no
2807 # need to create a new file revision in this case.
2808 node = fctx.filenode()
2809 if node in [fparent1, fparent2]:
2810 self.ui.debug(b'reusing %s filelog entry\n' % fname)
2811 if (
2812 fparent1 != nullid
2813 and manifest1.flags(fname) != fctx.flags()
2814 ) or (
2815 fparent2 != nullid
2816 and manifest2.flags(fname) != fctx.flags()
2817 ):
2818 touched = 'modified'
2819 return node, touched
2820
2821 flog = self.file(fname)
2822 meta = {}
2823 cfname = fctx.copysource()
2824 fnode = None
2825
2826 if cfname and cfname != fname:
2827 # Mark the new revision of this file as a copy of another
2828 # file. This copy data will effectively act as a parent
2829 # of this new revision. If this is a merge, the first
2830 # parent will be the nullid (meaning "look up the copy data")
2831 # and the second one will be the other parent. For example:
2832 #
2833 # 0 --- 1 --- 3 rev1 changes file foo
2834 # \ / rev2 renames foo to bar and changes it
2835 # \- 2 -/ rev3 should have bar with all changes and
2836 # should record that bar descends from
2837 # bar in rev2 and foo in rev1
2838 #
2839 # this allows this merge to succeed:
2840 #
2841 # 0 --- 1 --- 3 rev4 reverts the content change from rev2
2842 # \ / merging rev3 and rev4 should use bar@rev2
2843 # \- 2 --- 4 as the merge base
2844 #
2845
2846 cnode = manifest1.get(cfname)
2847 newfparent = fparent2
2848
2849 if manifest2: # branch merge
2850 if fparent2 == nullid or cnode is None: # copied on remote side
2851 if cfname in manifest2:
2852 cnode = manifest2[cfname]
2853 newfparent = fparent1
2854
2855 # Here, we used to search backwards through history to try to find
2856 # where the file copy came from if the source of a copy was not in
2857 # the parent directory. However, this doesn't actually make sense to
2858 # do (what does a copy from something not in your working copy even
2859 # mean?) and it causes bugs (eg, issue4476). Instead, we will warn
2860 # the user that copy information was dropped, so if they didn't
2861 # expect this outcome it can be fixed, but this is the correct
2862 # behavior in this circumstance.
2863
2864 if cnode:
2865 self.ui.debug(
2866 b" %s: copy %s:%s\n" % (fname, cfname, hex(cnode))
2867 )
2868 if includecopymeta:
2869 meta[b"copy"] = cfname
2870 meta[b"copyrev"] = hex(cnode)
2871 fparent1, fparent2 = nullid, newfparent
2872 else:
2873 self.ui.warn(
2874 _(
2875 b"warning: can't find ancestor for '%s' "
2876 b"copied from '%s'!\n"
2877 )
2878 % (fname, cfname)
2879 )
2880
2881 elif fparent1 == nullid:
2882 fparent1, fparent2 = fparent2, nullid
2883 elif fparent2 != nullid:
2884 # is one parent an ancestor of the other?
2885 fparentancestors = flog.commonancestorsheads(fparent1, fparent2)
2886 if fparent1 in fparentancestors:
2887 fparent1, fparent2 = fparent2, nullid
2888 elif fparent2 in fparentancestors:
2889 fparent2 = nullid
2890 elif not fparentancestors:
2891 # TODO: this whole if-else might be simplified much more
2892 ms = mergestatemod.mergestate.read(self)
2893 if (
2894 fname in ms
2895 and ms[fname] == mergestatemod.MERGE_RECORD_MERGED_OTHER
2896 ):
2897 fparent1, fparent2 = fparent2, nullid
2898
2899 # is the file changed?
2900 text = fctx.data()
2901 if fparent2 != nullid or meta or flog.cmp(fparent1, text):
2902 if touched is None: # do not overwrite added
2903 touched = 'modified'
2904 fnode = flog.add(text, meta, tr, linkrev, fparent1, fparent2)
2905 # are just the flags changed during merge?
2906 elif fname in manifest1 and manifest1.flags(fname) != fctx.flags():
2907 touched = 'modified'
2908 fnode = fparent1
2909 else:
2910 fnode = fparent1
2911 return fnode, touched
2912
2913 def checkcommitpatterns(self, wctx, match, status, fail):
2774 def checkcommitpatterns(self, wctx, match, status, fail):
2914 """check for commit arguments that aren't committable"""
2775 """check for commit arguments that aren't committable"""
2915 if match.isexact() or match.prefix():
2776 if match.isexact() or match.prefix():
@@ -479,25 +479,24 b' and its ancestor by overriding "repo._fi'
479
479
480 $ cat > ../legacyrepo.py <<EOF
480 $ cat > ../legacyrepo.py <<EOF
481 > from __future__ import absolute_import
481 > from __future__ import absolute_import
482 > from mercurial import error, node
482 > from mercurial import commit, error, extensions, node
483 > def reposetup(ui, repo):
483 > def _filecommit(orig, repo, fctx, manifest1, manifest2,
484 > class legacyrepo(repo.__class__):
484 > linkrev, tr, includecopymeta):
485 > def _filecommit(self, fctx, manifest1, manifest2,
485 > fname = fctx.path()
486 > linkrev, tr, includecopymeta):
486 > text = fctx.data()
487 > fname = fctx.path()
487 > flog = repo.file(fname)
488 > text = fctx.data()
488 > fparent1 = manifest1.get(fname, node.nullid)
489 > flog = self.file(fname)
489 > fparent2 = manifest2.get(fname, node.nullid)
490 > fparent1 = manifest1.get(fname, node.nullid)
490 > meta = {}
491 > fparent2 = manifest2.get(fname, node.nullid)
491 > copy = fctx.copysource()
492 > meta = {}
492 > if copy and copy != fname:
493 > copy = fctx.copysource()
493 > raise error.Abort('copying is not supported')
494 > if copy and copy != fname:
494 > if fparent2 != node.nullid:
495 > raise error.Abort('copying is not supported')
495 > return flog.add(text, meta, tr, linkrev,
496 > if fparent2 != node.nullid:
496 > fparent1, fparent2), 'modified'
497 > return flog.add(text, meta, tr, linkrev,
497 > raise error.Abort('only merging is supported')
498 > fparent1, fparent2), 'modified'
498 > def uisetup(ui):
499 > raise error.Abort('only merging is supported')
499 > extensions.wrapfunction(commit, '_filecommit', _filecommit)
500 > repo.__class__ = legacyrepo
501 > EOF
500 > EOF
502
501
503 $ cat > baz <<EOF
502 $ cat > baz <<EOF
@@ -481,25 +481,25 b' and (2) the extension to allow filelog m'
481 and its ancestor by overriding "repo._filecommit".
481 and its ancestor by overriding "repo._filecommit".
482
482
483 $ cat > ../legacyrepo.py <<EOF
483 $ cat > ../legacyrepo.py <<EOF
484 > from mercurial import error, node
484 > from __future__ import absolute_import
485 > def reposetup(ui, repo):
485 > from mercurial import commit, error, extensions, node
486 > class legacyrepo(repo.__class__):
486 > def _filecommit(orig, repo, fctx, manifest1, manifest2,
487 > def _filecommit(self, fctx, manifest1, manifest2,
487 > linkrev, tr, includecopymeta):
488 > linkrev, tr, includecopymeta):
488 > fname = fctx.path()
489 > fname = fctx.path()
489 > text = fctx.data()
490 > text = fctx.data()
490 > flog = repo.file(fname)
491 > flog = self.file(fname)
491 > fparent1 = manifest1.get(fname, node.nullid)
492 > fparent1 = manifest1.get(fname, node.nullid)
492 > fparent2 = manifest2.get(fname, node.nullid)
493 > fparent2 = manifest2.get(fname, node.nullid)
493 > meta = {}
494 > meta = {}
494 > copy = fctx.copysource()
495 > copy = fctx.renamed()
495 > if copy and copy != fname:
496 > if copy and copy[0] != fname:
496 > raise error.Abort('copying is not supported')
497 > raise error.Abort('copying is not supported')
497 > if fparent2 != node.nullid:
498 > if fparent2 != node.nullid:
498 > return flog.add(text, meta, tr, linkrev,
499 > return flog.add(text, meta, tr, linkrev,
499 > fparent1, fparent2), 'modified'
500 > fparent1, fparent2), 'modified'
500 > raise error.Abort('only merging is supported')
501 > raise error.Abort('only merging is supported')
501 > def uisetup(ui):
502 > repo.__class__ = legacyrepo
502 > extensions.wrapfunction(commit, '_filecommit', _filecommit)
503 > EOF
503 > EOF
504
504
505 $ cat > baz <<EOF
505 $ cat > baz <<EOF
General Comments 0
You need to be logged in to leave comments. Login now