##// END OF EJS Templates
commitctx: extract the function in a dedicated module...
marmoute -
r45759:ae5c1a3b default
parent child Browse files
Show More
@@ -0,0 +1,215 b''
1 # commit.py - fonction to perform commit
2 #
3 # This software may be used and distributed according to the terms of the
4 # GNU General Public License version 2 or any later version.
5
6 from __future__ import absolute_import
7
8 import errno
9 import weakref
10
11 from .i18n import _
12 from .node import (
13 hex,
14 nullrev,
15 )
16
17 from . import (
18 metadata,
19 phases,
20 scmutil,
21 subrepoutil,
22 )
23
24
25 def commitctx(repo, ctx, error=False, origctx=None):
26 """Add a new revision to the target repository.
27 Revision information is passed via the context argument.
28
29 ctx.files() should list all files involved in this commit, i.e.
30 modified/added/removed files. On merge, it may be wider than the
31 ctx.files() to be committed, since any file nodes derived directly
32 from p1 or p2 are excluded from the committed ctx.files().
33
34 origctx is for convert to work around the problem that bug
35 fixes to the files list in changesets change hashes. For
36 convert to be the identity, it can pass an origctx and this
37 function will use the same files list when it makes sense to
38 do so.
39 """
40 repo = repo.unfiltered()
41
42 p1, p2 = ctx.p1(), ctx.p2()
43 user = ctx.user()
44
45 if repo.filecopiesmode == b'changeset-sidedata':
46 writechangesetcopy = True
47 writefilecopymeta = True
48 writecopiesto = None
49 else:
50 writecopiesto = repo.ui.config(b'experimental', b'copies.write-to')
51 writefilecopymeta = writecopiesto != b'changeset-only'
52 writechangesetcopy = writecopiesto in (
53 b'changeset-only',
54 b'compatibility',
55 )
56 p1copies, p2copies = None, None
57 if writechangesetcopy:
58 p1copies = ctx.p1copies()
59 p2copies = ctx.p2copies()
60 filesadded, filesremoved = None, None
61 with repo.lock(), repo.transaction(b"commit") as tr:
62 trp = weakref.proxy(tr)
63
64 if ctx.manifestnode():
65 # reuse an existing manifest revision
66 repo.ui.debug(b'reusing known manifest\n')
67 mn = ctx.manifestnode()
68 files = ctx.files()
69 if writechangesetcopy:
70 filesadded = ctx.filesadded()
71 filesremoved = ctx.filesremoved()
72 elif not ctx.files():
73 repo.ui.debug(b'reusing manifest from p1 (no file change)\n')
74 mn = p1.manifestnode()
75 files = []
76 else:
77 m1ctx = p1.manifestctx()
78 m2ctx = p2.manifestctx()
79 mctx = m1ctx.copy()
80
81 m = mctx.read()
82 m1 = m1ctx.read()
83 m2 = m2ctx.read()
84
85 # check in files
86 added = []
87 filesadded = []
88 removed = list(ctx.removed())
89 touched = []
90 linkrev = len(repo)
91 repo.ui.note(_(b"committing files:\n"))
92 uipathfn = scmutil.getuipathfn(repo)
93 for f in sorted(ctx.modified() + ctx.added()):
94 repo.ui.note(uipathfn(f) + b"\n")
95 try:
96 fctx = ctx[f]
97 if fctx is None:
98 removed.append(f)
99 else:
100 added.append(f)
101 m[f], is_touched = repo._filecommit(
102 fctx, m1, m2, linkrev, trp, writefilecopymeta,
103 )
104 if is_touched:
105 touched.append(f)
106 if writechangesetcopy and is_touched == 'added':
107 filesadded.append(f)
108 m.setflag(f, fctx.flags())
109 except OSError:
110 repo.ui.warn(_(b"trouble committing %s!\n") % uipathfn(f))
111 raise
112 except IOError as inst:
113 errcode = getattr(inst, 'errno', errno.ENOENT)
114 if error or errcode and errcode != errno.ENOENT:
115 repo.ui.warn(
116 _(b"trouble committing %s!\n") % uipathfn(f)
117 )
118 raise
119
120 # update manifest
121 removed = [f for f in removed if f in m1 or f in m2]
122 drop = sorted([f for f in removed if f in m])
123 for f in drop:
124 del m[f]
125 if p2.rev() != nullrev:
126 rf = metadata.get_removal_filter(ctx, (p1, p2, m1, m2))
127 removed = [f for f in removed if not rf(f)]
128
129 touched.extend(removed)
130
131 if writechangesetcopy:
132 filesremoved = removed
133
134 files = touched
135 md = None
136 if not files:
137 # if no "files" actually changed in terms of the changelog,
138 # try hard to detect unmodified manifest entry so that the
139 # exact same commit can be reproduced later on convert.
140 md = m1.diff(m, scmutil.matchfiles(repo, ctx.files()))
141 if not files and md:
142 repo.ui.debug(
143 b'not reusing manifest (no file change in '
144 b'changelog, but manifest differs)\n'
145 )
146 if files or md:
147 repo.ui.note(_(b"committing manifest\n"))
148 # we're using narrowmatch here since it's already applied at
149 # other stages (such as dirstate.walk), so we're already
150 # ignoring things outside of narrowspec in most cases. The
151 # one case where we might have files outside the narrowspec
152 # at this point is merges, and we already error out in the
153 # case where the merge has files outside of the narrowspec,
154 # so this is safe.
155 mn = mctx.write(
156 trp,
157 linkrev,
158 p1.manifestnode(),
159 p2.manifestnode(),
160 added,
161 drop,
162 match=repo.narrowmatch(),
163 )
164 else:
165 repo.ui.debug(
166 b'reusing manifest from p1 (listed files '
167 b'actually unchanged)\n'
168 )
169 mn = p1.manifestnode()
170
171 if writecopiesto == b'changeset-only':
172 # If writing only to changeset extras, use None to indicate that
173 # no entry should be written. If writing to both, write an empty
174 # entry to prevent the reader from falling back to reading
175 # filelogs.
176 p1copies = p1copies or None
177 p2copies = p2copies or None
178 filesadded = filesadded or None
179 filesremoved = filesremoved or None
180
181 if origctx and origctx.manifestnode() == mn:
182 files = origctx.files()
183
184 # update changelog
185 repo.ui.note(_(b"committing changelog\n"))
186 repo.changelog.delayupdate(tr)
187 n = repo.changelog.add(
188 mn,
189 files,
190 ctx.description(),
191 trp,
192 p1.node(),
193 p2.node(),
194 user,
195 ctx.date(),
196 ctx.extra().copy(),
197 p1copies,
198 p2copies,
199 filesadded,
200 filesremoved,
201 )
202 xp1, xp2 = p1.hex(), p2 and p2.hex() or b''
203 repo.hook(
204 b'pretxncommit', throw=True, node=hex(n), parent1=xp1, parent2=xp2,
205 )
206 # set the new commit is proper phase
207 targetphase = subrepoutil.newcommitphase(repo.ui, ctx)
208 if targetphase:
209 # retract boundary do not alter parent changeset.
210 # if a parent have higher the resulting phase will
211 # be compliant anyway
212 #
213 # if minimal phase was 0 we don't need to retract anything
214 phases.registernew(repo, tr, targetphase, [n])
215 return n
@@ -32,6 +32,7 b' from . import ('
32 bundle2,
32 bundle2,
33 changegroup,
33 changegroup,
34 color,
34 color,
35 commit,
35 context,
36 context,
36 dirstate,
37 dirstate,
37 dirstateguard,
38 dirstateguard,
@@ -46,7 +47,6 b' from . import ('
46 match as matchmod,
47 match as matchmod,
47 mergestate as mergestatemod,
48 mergestate as mergestatemod,
48 mergeutil,
49 mergeutil,
49 metadata,
50 namespaces,
50 namespaces,
51 narrowspec,
51 narrowspec,
52 obsolete,
52 obsolete,
@@ -3067,201 +3067,7 b' class localrepository(object):'
3067
3067
3068 @unfilteredmethod
3068 @unfilteredmethod
3069 def commitctx(self, ctx, error=False, origctx=None):
3069 def commitctx(self, ctx, error=False, origctx=None):
3070 """Add a new revision to current repository.
3070 return commit.commitctx(self, ctx, error=error, origctx=origctx)
3071 Revision information is passed via the context argument.
3072
3073 ctx.files() should list all files involved in this commit, i.e.
3074 modified/added/removed files. On merge, it may be wider than the
3075 ctx.files() to be committed, since any file nodes derived directly
3076 from p1 or p2 are excluded from the committed ctx.files().
3077
3078 origctx is for convert to work around the problem that bug
3079 fixes to the files list in changesets change hashes. For
3080 convert to be the identity, it can pass an origctx and this
3081 function will use the same files list when it makes sense to
3082 do so.
3083 """
3084
3085 p1, p2 = ctx.p1(), ctx.p2()
3086 user = ctx.user()
3087
3088 if self.filecopiesmode == b'changeset-sidedata':
3089 writechangesetcopy = True
3090 writefilecopymeta = True
3091 writecopiesto = None
3092 else:
3093 writecopiesto = self.ui.config(b'experimental', b'copies.write-to')
3094 writefilecopymeta = writecopiesto != b'changeset-only'
3095 writechangesetcopy = writecopiesto in (
3096 b'changeset-only',
3097 b'compatibility',
3098 )
3099 p1copies, p2copies = None, None
3100 if writechangesetcopy:
3101 p1copies = ctx.p1copies()
3102 p2copies = ctx.p2copies()
3103 filesadded, filesremoved = None, None
3104 with self.lock(), self.transaction(b"commit") as tr:
3105 trp = weakref.proxy(tr)
3106
3107 if ctx.manifestnode():
3108 # reuse an existing manifest revision
3109 self.ui.debug(b'reusing known manifest\n')
3110 mn = ctx.manifestnode()
3111 files = ctx.files()
3112 if writechangesetcopy:
3113 filesadded = ctx.filesadded()
3114 filesremoved = ctx.filesremoved()
3115 elif not ctx.files():
3116 self.ui.debug(b'reusing manifest from p1 (no file change)\n')
3117 mn = p1.manifestnode()
3118 files = []
3119 else:
3120 m1ctx = p1.manifestctx()
3121 m2ctx = p2.manifestctx()
3122 mctx = m1ctx.copy()
3123
3124 m = mctx.read()
3125 m1 = m1ctx.read()
3126 m2 = m2ctx.read()
3127
3128 # check in files
3129 added = []
3130 filesadded = []
3131 removed = list(ctx.removed())
3132 touched = []
3133 linkrev = len(self)
3134 self.ui.note(_(b"committing files:\n"))
3135 uipathfn = scmutil.getuipathfn(self)
3136 for f in sorted(ctx.modified() + ctx.added()):
3137 self.ui.note(uipathfn(f) + b"\n")
3138 try:
3139 fctx = ctx[f]
3140 if fctx is None:
3141 removed.append(f)
3142 else:
3143 added.append(f)
3144 m[f], is_touched = self._filecommit(
3145 fctx, m1, m2, linkrev, trp, writefilecopymeta,
3146 )
3147 if is_touched:
3148 touched.append(f)
3149 if writechangesetcopy and is_touched == 'added':
3150 filesadded.append(f)
3151 m.setflag(f, fctx.flags())
3152 except OSError:
3153 self.ui.warn(
3154 _(b"trouble committing %s!\n") % uipathfn(f)
3155 )
3156 raise
3157 except IOError as inst:
3158 errcode = getattr(inst, 'errno', errno.ENOENT)
3159 if error or errcode and errcode != errno.ENOENT:
3160 self.ui.warn(
3161 _(b"trouble committing %s!\n") % uipathfn(f)
3162 )
3163 raise
3164
3165 # update manifest
3166 removed = [f for f in removed if f in m1 or f in m2]
3167 drop = sorted([f for f in removed if f in m])
3168 for f in drop:
3169 del m[f]
3170 if p2.rev() != nullrev:
3171 rf = metadata.get_removal_filter(ctx, (p1, p2, m1, m2))
3172 removed = [f for f in removed if not rf(f)]
3173
3174 touched.extend(removed)
3175
3176 if writechangesetcopy:
3177 filesremoved = removed
3178
3179 files = touched
3180 md = None
3181 if not files:
3182 # if no "files" actually changed in terms of the changelog,
3183 # try hard to detect unmodified manifest entry so that the
3184 # exact same commit can be reproduced later on convert.
3185 md = m1.diff(m, scmutil.matchfiles(self, ctx.files()))
3186 if not files and md:
3187 self.ui.debug(
3188 b'not reusing manifest (no file change in '
3189 b'changelog, but manifest differs)\n'
3190 )
3191 if files or md:
3192 self.ui.note(_(b"committing manifest\n"))
3193 # we're using narrowmatch here since it's already applied at
3194 # other stages (such as dirstate.walk), so we're already
3195 # ignoring things outside of narrowspec in most cases. The
3196 # one case where we might have files outside the narrowspec
3197 # at this point is merges, and we already error out in the
3198 # case where the merge has files outside of the narrowspec,
3199 # so this is safe.
3200 mn = mctx.write(
3201 trp,
3202 linkrev,
3203 p1.manifestnode(),
3204 p2.manifestnode(),
3205 added,
3206 drop,
3207 match=self.narrowmatch(),
3208 )
3209 else:
3210 self.ui.debug(
3211 b'reusing manifest from p1 (listed files '
3212 b'actually unchanged)\n'
3213 )
3214 mn = p1.manifestnode()
3215
3216 if writecopiesto == b'changeset-only':
3217 # If writing only to changeset extras, use None to indicate that
3218 # no entry should be written. If writing to both, write an empty
3219 # entry to prevent the reader from falling back to reading
3220 # filelogs.
3221 p1copies = p1copies or None
3222 p2copies = p2copies or None
3223 filesadded = filesadded or None
3224 filesremoved = filesremoved or None
3225
3226 if origctx and origctx.manifestnode() == mn:
3227 files = origctx.files()
3228
3229 # update changelog
3230 self.ui.note(_(b"committing changelog\n"))
3231 self.changelog.delayupdate(tr)
3232 n = self.changelog.add(
3233 mn,
3234 files,
3235 ctx.description(),
3236 trp,
3237 p1.node(),
3238 p2.node(),
3239 user,
3240 ctx.date(),
3241 ctx.extra().copy(),
3242 p1copies,
3243 p2copies,
3244 filesadded,
3245 filesremoved,
3246 )
3247 xp1, xp2 = p1.hex(), p2 and p2.hex() or b''
3248 self.hook(
3249 b'pretxncommit',
3250 throw=True,
3251 node=hex(n),
3252 parent1=xp1,
3253 parent2=xp2,
3254 )
3255 # set the new commit is proper phase
3256 targetphase = subrepoutil.newcommitphase(self.ui, ctx)
3257 if targetphase:
3258 # retract boundary do not alter parent changeset.
3259 # if a parent have higher the resulting phase will
3260 # be compliant anyway
3261 #
3262 # if minimal phase was 0 we don't need to retract anything
3263 phases.registernew(self, tr, targetphase, [n])
3264 return n
3265
3071
3266 @unfilteredmethod
3072 @unfilteredmethod
3267 def destroying(self):
3073 def destroying(self):
General Comments 0
You need to be logged in to leave comments. Login now