Show More
@@ -11,10 +11,13 b' import weakref' | |||
|
11 | 11 | from .i18n import _ |
|
12 | 12 | from .node import ( |
|
13 | 13 | hex, |
|
14 | nullid, | |
|
14 | 15 | nullrev, |
|
15 | 16 | ) |
|
16 | 17 | |
|
17 | 18 | from . import ( |
|
19 | context, | |
|
20 | mergestate, | |
|
18 | 21 | metadata, |
|
19 | 22 | phases, |
|
20 | 23 | scmutil, |
@@ -98,8 +101,8 b' def commitctx(repo, ctx, error=False, or' | |||
|
98 | 101 | removed.append(f) |
|
99 | 102 | else: |
|
100 | 103 | added.append(f) |
|
101 |
m[f], is_touched = |
|
|
102 | fctx, m1, m2, linkrev, trp, writefilecopymeta, | |
|
104 | m[f], is_touched = _filecommit( | |
|
105 | repo, fctx, m1, m2, linkrev, trp, writefilecopymeta, | |
|
103 | 106 | ) |
|
104 | 107 | if is_touched: |
|
105 | 108 | touched.append(f) |
@@ -213,3 +216,139 b' def commitctx(repo, ctx, error=False, or' | |||
|
213 | 216 | # if minimal phase was 0 we don't need to retract anything |
|
214 | 217 | phases.registernew(repo, tr, targetphase, [n]) |
|
215 | 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 | 2771 | """Returns the wlock if it's held, or None if it's not.""" |
|
2772 | 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 | 2774 | def checkcommitpatterns(self, wctx, match, status, fail): |
|
2914 | 2775 | """check for commit arguments that aren't committable""" |
|
2915 | 2776 | if match.isexact() or match.prefix(): |
@@ -479,25 +479,24 b' and its ancestor by overriding "repo._fi' | |||
|
479 | 479 | |
|
480 | 480 | $ cat > ../legacyrepo.py <<EOF |
|
481 | 481 | > from __future__ import absolute_import |
|
482 | > from mercurial import error, node | |
|
483 | > def reposetup(ui, repo): | |
|
484 | > class legacyrepo(repo.__class__): | |
|
485 | > def _filecommit(self, fctx, manifest1, manifest2, | |
|
486 | > linkrev, tr, includecopymeta): | |
|
487 | > fname = fctx.path() | |
|
488 | > text = fctx.data() | |
|
489 | > flog = self.file(fname) | |
|
490 | > fparent1 = manifest1.get(fname, node.nullid) | |
|
491 | > fparent2 = manifest2.get(fname, node.nullid) | |
|
492 | > meta = {} | |
|
493 | > copy = fctx.copysource() | |
|
494 | > if copy and copy != fname: | |
|
495 | > raise error.Abort('copying is not supported') | |
|
496 |
> |
|
|
497 | > return flog.add(text, meta, tr, linkrev, | |
|
498 | > fparent1, fparent2), 'modified' | |
|
499 | > raise error.Abort('only merging is supported') | |
|
500 | > repo.__class__ = legacyrepo | |
|
482 | > from mercurial import commit, error, extensions, node | |
|
483 | > def _filecommit(orig, repo, fctx, manifest1, manifest2, | |
|
484 | > linkrev, tr, includecopymeta): | |
|
485 | > fname = fctx.path() | |
|
486 | > text = fctx.data() | |
|
487 | > flog = repo.file(fname) | |
|
488 | > fparent1 = manifest1.get(fname, node.nullid) | |
|
489 | > fparent2 = manifest2.get(fname, node.nullid) | |
|
490 | > meta = {} | |
|
491 | > copy = fctx.copysource() | |
|
492 | > if copy and copy != fname: | |
|
493 | > raise error.Abort('copying is not supported') | |
|
494 | > if fparent2 != node.nullid: | |
|
495 | > return flog.add(text, meta, tr, linkrev, | |
|
496 | > fparent1, fparent2), 'modified' | |
|
497 | > raise error.Abort('only merging is supported') | |
|
498 | > def uisetup(ui): | |
|
499 | > extensions.wrapfunction(commit, '_filecommit', _filecommit) | |
|
501 | 500 | > EOF |
|
502 | 501 | |
|
503 | 502 | $ cat > baz <<EOF |
@@ -481,25 +481,25 b' and (2) the extension to allow filelog m' | |||
|
481 | 481 | and its ancestor by overriding "repo._filecommit". |
|
482 | 482 | |
|
483 | 483 | $ cat > ../legacyrepo.py <<EOF |
|
484 | > from mercurial import error, node | |
|
485 | > def reposetup(ui, repo): | |
|
486 | > class legacyrepo(repo.__class__): | |
|
487 | > def _filecommit(self, fctx, manifest1, manifest2, | |
|
488 | > linkrev, tr, includecopymeta): | |
|
489 |
> |
|
|
490 | > text = fctx.data() | |
|
491 | > flog = self.file(fname) | |
|
492 |
> |
|
|
493 | > fparent2 = manifest2.get(fname, node.nullid) | |
|
494 | > meta = {} | |
|
495 | > copy = fctx.renamed() | |
|
496 | > if copy and copy[0] != fname: | |
|
497 | > raise error.Abort('copying is not supported') | |
|
498 | > if fparent2 != node.nullid: | |
|
499 | > return flog.add(text, meta, tr, linkrev, | |
|
500 | > fparent1, fparent2), 'modified' | |
|
501 | > raise error.Abort('only merging is supported') | |
|
502 | > repo.__class__ = legacyrepo | |
|
484 | > from __future__ import absolute_import | |
|
485 | > from mercurial import commit, error, extensions, node | |
|
486 | > def _filecommit(orig, repo, fctx, manifest1, manifest2, | |
|
487 | > linkrev, tr, includecopymeta): | |
|
488 | > fname = fctx.path() | |
|
489 | > text = fctx.data() | |
|
490 | > flog = repo.file(fname) | |
|
491 | > fparent1 = manifest1.get(fname, node.nullid) | |
|
492 | > fparent2 = manifest2.get(fname, node.nullid) | |
|
493 | > meta = {} | |
|
494 | > copy = fctx.copysource() | |
|
495 | > if copy and copy != fname: | |
|
496 | > raise error.Abort('copying is not supported') | |
|
497 | > if fparent2 != node.nullid: | |
|
498 | > return flog.add(text, meta, tr, linkrev, | |
|
499 | > fparent1, fparent2), 'modified' | |
|
500 | > raise error.Abort('only merging is supported') | |
|
501 | > def uisetup(ui): | |
|
502 | > extensions.wrapfunction(commit, '_filecommit', _filecommit) | |
|
503 | 503 | > EOF |
|
504 | 504 | |
|
505 | 505 | $ cat > baz <<EOF |
General Comments 0
You need to be logged in to leave comments.
Login now