Show More
@@ -46,6 +46,7 b' from . import (' | |||
|
46 | 46 | match as matchmod, |
|
47 | 47 | mergestate as mergestatemod, |
|
48 | 48 | mergeutil, |
|
49 | metadata, | |
|
49 | 50 | namespaces, |
|
50 | 51 | narrowspec, |
|
51 | 52 | obsolete, |
@@ -3145,51 +3146,8 b' class localrepository(object):' | |||
|
3145 | 3146 | for f in drop: |
|
3146 | 3147 | del m[f] |
|
3147 | 3148 | if p2.rev() != nullrev: |
|
3148 | ||
|
3149 | @util.cachefunc | |
|
3150 | def mas(): | |
|
3151 | p1n = p1.node() | |
|
3152 | p2n = p2.node() | |
|
3153 | cahs = self.changelog.commonancestorsheads(p1n, p2n) | |
|
3154 | if not cahs: | |
|
3155 | cahs = [nullrev] | |
|
3156 | return [self[r].manifest() for r in cahs] | |
|
3157 | ||
|
3158 | def deletionfromparent(f): | |
|
3159 | # When a file is removed relative to p1 in a merge, this | |
|
3160 | # function determines whether the absence is due to a | |
|
3161 | # deletion from a parent, or whether the merge commit | |
|
3162 | # itself deletes the file. We decide this by doing a | |
|
3163 | # simplified three way merge of the manifest entry for | |
|
3164 | # the file. There are two ways we decide the merge | |
|
3165 | # itself didn't delete a file: | |
|
3166 | # - neither parent (nor the merge) contain the file | |
|
3167 | # - exactly one parent contains the file, and that | |
|
3168 | # parent has the same filelog entry as the merge | |
|
3169 | # ancestor (or all of them if there two). In other | |
|
3170 | # words, that parent left the file unchanged while the | |
|
3171 | # other one deleted it. | |
|
3172 | # One way to think about this is that deleting a file is | |
|
3173 | # similar to emptying it, so the list of changed files | |
|
3174 | # should be similar either way. The computation | |
|
3175 | # described above is not done directly in _filecommit | |
|
3176 | # when creating the list of changed files, however | |
|
3177 | # it does something very similar by comparing filelog | |
|
3178 | # nodes. | |
|
3179 | if f in m1: | |
|
3180 | return f not in m2 and all( | |
|
3181 | f in ma and ma.find(f) == m1.find(f) | |
|
3182 | for ma in mas() | |
|
3183 | ) | |
|
3184 | elif f in m2: | |
|
3185 | return all( | |
|
3186 | f in ma and ma.find(f) == m2.find(f) | |
|
3187 | for ma in mas() | |
|
3188 | ) | |
|
3189 | else: | |
|
3190 | return True | |
|
3191 | ||
|
3192 | removed = [f for f in removed if not deletionfromparent(f)] | |
|
3149 | rf = metadata.get_removal_filter(ctx, (p1, p2, m1, m2)) | |
|
3150 | removed = [f for f in removed if not rf(f)] | |
|
3193 | 3151 | |
|
3194 | 3152 | files = changed + removed |
|
3195 | 3153 | md = None |
@@ -11,6 +11,7 b' import multiprocessing' | |||
|
11 | 11 | |
|
12 | 12 | from . import ( |
|
13 | 13 | error, |
|
14 | node, | |
|
14 | 15 | pycompat, |
|
15 | 16 | util, |
|
16 | 17 | ) |
@@ -31,6 +32,61 b' def computechangesetfilesadded(ctx):' | |||
|
31 | 32 | return added |
|
32 | 33 | |
|
33 | 34 | |
|
35 | def get_removal_filter(ctx, x=None): | |
|
36 | """return a function to detect files "wrongly" detected as `removed` | |
|
37 | ||
|
38 | When a file is removed relative to p1 in a merge, this | |
|
39 | function determines whether the absence is due to a | |
|
40 | deletion from a parent, or whether the merge commit | |
|
41 | itself deletes the file. We decide this by doing a | |
|
42 | simplified three way merge of the manifest entry for | |
|
43 | the file. There are two ways we decide the merge | |
|
44 | itself didn't delete a file: | |
|
45 | - neither parent (nor the merge) contain the file | |
|
46 | - exactly one parent contains the file, and that | |
|
47 | parent has the same filelog entry as the merge | |
|
48 | ancestor (or all of them if there two). In other | |
|
49 | words, that parent left the file unchanged while the | |
|
50 | other one deleted it. | |
|
51 | One way to think about this is that deleting a file is | |
|
52 | similar to emptying it, so the list of changed files | |
|
53 | should be similar either way. The computation | |
|
54 | described above is not done directly in _filecommit | |
|
55 | when creating the list of changed files, however | |
|
56 | it does something very similar by comparing filelog | |
|
57 | nodes. | |
|
58 | """ | |
|
59 | ||
|
60 | if x is not None: | |
|
61 | p1, p2, m1, m2 = x | |
|
62 | else: | |
|
63 | p1 = ctx.p1() | |
|
64 | p2 = ctx.p2() | |
|
65 | m1 = p1.manifest() | |
|
66 | m2 = p2.manifest() | |
|
67 | ||
|
68 | @util.cachefunc | |
|
69 | def mas(): | |
|
70 | p1n = p1.node() | |
|
71 | p2n = p2.node() | |
|
72 | cahs = ctx.repo().changelog.commonancestorsheads(p1n, p2n) | |
|
73 | if not cahs: | |
|
74 | cahs = [node.nullrev] | |
|
75 | return [ctx.repo()[r].manifest() for r in cahs] | |
|
76 | ||
|
77 | def deletionfromparent(f): | |
|
78 | if f in m1: | |
|
79 | return f not in m2 and all( | |
|
80 | f in ma and ma.find(f) == m1.find(f) for ma in mas() | |
|
81 | ) | |
|
82 | elif f in m2: | |
|
83 | return all(f in ma and ma.find(f) == m2.find(f) for ma in mas()) | |
|
84 | else: | |
|
85 | return True | |
|
86 | ||
|
87 | return deletionfromparent | |
|
88 | ||
|
89 | ||
|
34 | 90 | def computechangesetfilesremoved(ctx): |
|
35 | 91 | """return the list of files removed in a changeset |
|
36 | 92 | """ |
General Comments 0
You need to be logged in to leave comments.
Login now