Show More
@@ -46,6 +46,7 b' from . import (' | |||||
46 | match as matchmod, |
|
46 | match as matchmod, | |
47 | mergestate as mergestatemod, |
|
47 | mergestate as mergestatemod, | |
48 | mergeutil, |
|
48 | mergeutil, | |
|
49 | metadata, | |||
49 | namespaces, |
|
50 | namespaces, | |
50 | narrowspec, |
|
51 | narrowspec, | |
51 | obsolete, |
|
52 | obsolete, | |
@@ -3145,51 +3146,8 b' class localrepository(object):' | |||||
3145 | for f in drop: |
|
3146 | for f in drop: | |
3146 | del m[f] |
|
3147 | del m[f] | |
3147 | if p2.rev() != nullrev: |
|
3148 | if p2.rev() != nullrev: | |
3148 |
|
3149 | rf = metadata.get_removal_filter(ctx, (p1, p2, m1, m2)) | ||
3149 | @util.cachefunc |
|
3150 | removed = [f for f in removed if not rf(f)] | |
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)] |
|
|||
3193 |
|
3151 | |||
3194 | files = changed + removed |
|
3152 | files = changed + removed | |
3195 | md = None |
|
3153 | md = None |
@@ -11,6 +11,7 b' import multiprocessing' | |||||
11 |
|
11 | |||
12 | from . import ( |
|
12 | from . import ( | |
13 | error, |
|
13 | error, | |
|
14 | node, | |||
14 | pycompat, |
|
15 | pycompat, | |
15 | util, |
|
16 | util, | |
16 | ) |
|
17 | ) | |
@@ -31,6 +32,61 b' def computechangesetfilesadded(ctx):' | |||||
31 | return added |
|
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 | def computechangesetfilesremoved(ctx): |
|
90 | def computechangesetfilesremoved(ctx): | |
35 | """return the list of files removed in a changeset |
|
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