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 = |
|
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 |
> |
|
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 |
> |
|
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 |
> |
|
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