##// END OF EJS Templates
commitctx: no longer use the `writecopiesto` variable in the function...
marmoute -
r45790:4eb6466e default
parent child Browse files
Show More
@@ -1,379 +1,378
1 1 # commit.py - fonction to perform commit
2 2 #
3 3 # This software may be used and distributed according to the terms of the
4 4 # GNU General Public License version 2 or any later version.
5 5
6 6 from __future__ import absolute_import
7 7
8 8 import errno
9 9
10 10 from .i18n import _
11 11 from .node import (
12 12 hex,
13 13 nullid,
14 14 nullrev,
15 15 )
16 16
17 17 from . import (
18 18 context,
19 19 mergestate,
20 20 metadata,
21 21 phases,
22 22 scmutil,
23 23 subrepoutil,
24 24 )
25 25
26 26
27 27 def commitctx(repo, ctx, error=False, origctx=None):
28 28 """Add a new revision to the target repository.
29 29 Revision information is passed via the context argument.
30 30
31 31 ctx.files() should list all files involved in this commit, i.e.
32 32 modified/added/removed files. On merge, it may be wider than the
33 33 ctx.files() to be committed, since any file nodes derived directly
34 34 from p1 or p2 are excluded from the committed ctx.files().
35 35
36 36 origctx is for convert to work around the problem that bug
37 37 fixes to the files list in changesets change hashes. For
38 38 convert to be the identity, it can pass an origctx and this
39 39 function will use the same files list when it makes sense to
40 40 do so.
41 41 """
42 42 repo = repo.unfiltered()
43 43
44 44 p1, p2 = ctx.p1(), ctx.p2()
45 45 user = ctx.user()
46 46
47 47 if repo.filecopiesmode == b'changeset-sidedata':
48 48 writechangesetcopy = True
49 49 writefilecopymeta = True
50 writecopiesto = None
51 50 else:
52 51 writecopiesto = repo.ui.config(b'experimental', b'copies.write-to')
53 52 writefilecopymeta = writecopiesto != b'changeset-only'
54 53 writechangesetcopy = writecopiesto in (
55 54 b'changeset-only',
56 55 b'compatibility',
57 56 )
58 57 p1copies, p2copies = None, None
59 58 if writechangesetcopy:
60 59 p1copies = ctx.p1copies()
61 60 p2copies = ctx.p2copies()
62 61 filesadded, filesremoved = None, None
63 62 with repo.lock(), repo.transaction(b"commit") as tr:
64 63 if ctx.manifestnode():
65 64 # reuse an existing manifest revision
66 65 repo.ui.debug(b'reusing known manifest\n')
67 66 mn = ctx.manifestnode()
68 67 files = ctx.files()
69 68 if writechangesetcopy:
70 69 filesadded = ctx.filesadded()
71 70 filesremoved = ctx.filesremoved()
72 71 elif not ctx.files():
73 72 repo.ui.debug(b'reusing manifest from p1 (no file change)\n')
74 73 mn = p1.manifestnode()
75 74 files = []
76 75 else:
77 76 m1ctx = p1.manifestctx()
78 77 m2ctx = p2.manifestctx()
79 78 mctx = m1ctx.copy()
80 79
81 80 m = mctx.read()
82 81 m1 = m1ctx.read()
83 82 m2 = m2ctx.read()
84 83
85 84 # check in files
86 85 added = []
87 86 filesadded = []
88 87 removed = list(ctx.removed())
89 88 touched = []
90 89 linkrev = len(repo)
91 90 repo.ui.note(_(b"committing files:\n"))
92 91 uipathfn = scmutil.getuipathfn(repo)
93 92 for f in sorted(ctx.modified() + ctx.added()):
94 93 repo.ui.note(uipathfn(f) + b"\n")
95 94 try:
96 95 fctx = ctx[f]
97 96 if fctx is None:
98 97 removed.append(f)
99 98 else:
100 99 added.append(f)
101 100 m[f], is_touched = _filecommit(
102 101 repo, fctx, m1, m2, linkrev, tr, writefilecopymeta,
103 102 )
104 103 if is_touched:
105 104 touched.append(f)
106 105 if writechangesetcopy and is_touched == 'added':
107 106 filesadded.append(f)
108 107 m.setflag(f, fctx.flags())
109 108 except OSError:
110 109 repo.ui.warn(_(b"trouble committing %s!\n") % uipathfn(f))
111 110 raise
112 111 except IOError as inst:
113 112 errcode = getattr(inst, 'errno', errno.ENOENT)
114 113 if error or errcode and errcode != errno.ENOENT:
115 114 repo.ui.warn(
116 115 _(b"trouble committing %s!\n") % uipathfn(f)
117 116 )
118 117 raise
119 118
120 119 # update manifest
121 120 removed = [f for f in removed if f in m1 or f in m2]
122 121 drop = sorted([f for f in removed if f in m])
123 122 for f in drop:
124 123 del m[f]
125 124 if p2.rev() != nullrev:
126 125 rf = metadata.get_removal_filter(ctx, (p1, p2, m1, m2))
127 126 removed = [f for f in removed if not rf(f)]
128 127
129 128 touched.extend(removed)
130 129
131 130 if writechangesetcopy:
132 131 filesremoved = removed
133 132
134 133 files = touched
135 134 mn = _commit_manifest(tr, linkrev, ctx, mctx, files, added, drop)
136 135
137 if writecopiesto == b'changeset-only':
136 if not writefilecopymeta:
138 137 # If writing only to changeset extras, use None to indicate that
139 138 # no entry should be written. If writing to both, write an empty
140 139 # entry to prevent the reader from falling back to reading
141 140 # filelogs.
142 141 p1copies = p1copies or None
143 142 p2copies = p2copies or None
144 143 filesadded = filesadded or None
145 144 filesremoved = filesremoved or None
146 145
147 146 if origctx and origctx.manifestnode() == mn:
148 147 files = origctx.files()
149 148
150 149 # update changelog
151 150 repo.ui.note(_(b"committing changelog\n"))
152 151 repo.changelog.delayupdate(tr)
153 152 n = repo.changelog.add(
154 153 mn,
155 154 files,
156 155 ctx.description(),
157 156 tr,
158 157 p1.node(),
159 158 p2.node(),
160 159 user,
161 160 ctx.date(),
162 161 ctx.extra().copy(),
163 162 p1copies,
164 163 p2copies,
165 164 filesadded,
166 165 filesremoved,
167 166 )
168 167 xp1, xp2 = p1.hex(), p2 and p2.hex() or b''
169 168 repo.hook(
170 169 b'pretxncommit', throw=True, node=hex(n), parent1=xp1, parent2=xp2,
171 170 )
172 171 # set the new commit is proper phase
173 172 targetphase = subrepoutil.newcommitphase(repo.ui, ctx)
174 173 if targetphase:
175 174 # retract boundary do not alter parent changeset.
176 175 # if a parent have higher the resulting phase will
177 176 # be compliant anyway
178 177 #
179 178 # if minimal phase was 0 we don't need to retract anything
180 179 phases.registernew(repo, tr, targetphase, [n])
181 180 return n
182 181
183 182
184 183 def _filecommit(
185 184 repo, fctx, manifest1, manifest2, linkrev, tr, includecopymeta,
186 185 ):
187 186 """
188 187 commit an individual file as part of a larger transaction
189 188
190 189 input:
191 190
192 191 fctx: a file context with the content we are trying to commit
193 192 manifest1: manifest of changeset first parent
194 193 manifest2: manifest of changeset second parent
195 194 linkrev: revision number of the changeset being created
196 195 tr: current transation
197 196 individual: boolean, set to False to skip storing the copy data
198 197 (only used by the Google specific feature of using
199 198 changeset extra as copy source of truth).
200 199
201 200 output: (filenode, touched)
202 201
203 202 filenode: the filenode that should be used by this changeset
204 203 touched: one of: None (mean untouched), 'added' or 'modified'
205 204 """
206 205
207 206 fname = fctx.path()
208 207 fparent1 = manifest1.get(fname, nullid)
209 208 fparent2 = manifest2.get(fname, nullid)
210 209 touched = None
211 210 if fparent1 == fparent2 == nullid:
212 211 touched = 'added'
213 212
214 213 if isinstance(fctx, context.filectx):
215 214 # This block fast path most comparisons which are usually done. It
216 215 # assumes that bare filectx is used and no merge happened, hence no
217 216 # need to create a new file revision in this case.
218 217 node = fctx.filenode()
219 218 if node in [fparent1, fparent2]:
220 219 repo.ui.debug(b'reusing %s filelog entry\n' % fname)
221 220 if (
222 221 fparent1 != nullid and manifest1.flags(fname) != fctx.flags()
223 222 ) or (
224 223 fparent2 != nullid and manifest2.flags(fname) != fctx.flags()
225 224 ):
226 225 touched = 'modified'
227 226 return node, touched
228 227
229 228 flog = repo.file(fname)
230 229 meta = {}
231 230 cfname = fctx.copysource()
232 231 fnode = None
233 232
234 233 if cfname and cfname != fname:
235 234 # Mark the new revision of this file as a copy of another
236 235 # file. This copy data will effectively act as a parent
237 236 # of this new revision. If this is a merge, the first
238 237 # parent will be the nullid (meaning "look up the copy data")
239 238 # and the second one will be the other parent. For example:
240 239 #
241 240 # 0 --- 1 --- 3 rev1 changes file foo
242 241 # \ / rev2 renames foo to bar and changes it
243 242 # \- 2 -/ rev3 should have bar with all changes and
244 243 # should record that bar descends from
245 244 # bar in rev2 and foo in rev1
246 245 #
247 246 # this allows this merge to succeed:
248 247 #
249 248 # 0 --- 1 --- 3 rev4 reverts the content change from rev2
250 249 # \ / merging rev3 and rev4 should use bar@rev2
251 250 # \- 2 --- 4 as the merge base
252 251 #
253 252
254 253 cnode = manifest1.get(cfname)
255 254 newfparent = fparent2
256 255
257 256 if manifest2: # branch merge
258 257 if fparent2 == nullid or cnode is None: # copied on remote side
259 258 if cfname in manifest2:
260 259 cnode = manifest2[cfname]
261 260 newfparent = fparent1
262 261
263 262 # Here, we used to search backwards through history to try to find
264 263 # where the file copy came from if the source of a copy was not in
265 264 # the parent directory. However, this doesn't actually make sense to
266 265 # do (what does a copy from something not in your working copy even
267 266 # mean?) and it causes bugs (eg, issue4476). Instead, we will warn
268 267 # the user that copy information was dropped, so if they didn't
269 268 # expect this outcome it can be fixed, but this is the correct
270 269 # behavior in this circumstance.
271 270
272 271 if cnode:
273 272 repo.ui.debug(b" %s: copy %s:%s\n" % (fname, cfname, hex(cnode)))
274 273 if includecopymeta:
275 274 meta[b"copy"] = cfname
276 275 meta[b"copyrev"] = hex(cnode)
277 276 fparent1, fparent2 = nullid, newfparent
278 277 else:
279 278 repo.ui.warn(
280 279 _(
281 280 b"warning: can't find ancestor for '%s' "
282 281 b"copied from '%s'!\n"
283 282 )
284 283 % (fname, cfname)
285 284 )
286 285
287 286 elif fparent1 == nullid:
288 287 fparent1, fparent2 = fparent2, nullid
289 288 elif fparent2 != nullid:
290 289 # is one parent an ancestor of the other?
291 290 fparentancestors = flog.commonancestorsheads(fparent1, fparent2)
292 291 if fparent1 in fparentancestors:
293 292 fparent1, fparent2 = fparent2, nullid
294 293 elif fparent2 in fparentancestors:
295 294 fparent2 = nullid
296 295 elif not fparentancestors:
297 296 # TODO: this whole if-else might be simplified much more
298 297 ms = mergestate.mergestate.read(repo)
299 298 if (
300 299 fname in ms
301 300 and ms[fname] == mergestate.MERGE_RECORD_MERGED_OTHER
302 301 ):
303 302 fparent1, fparent2 = fparent2, nullid
304 303
305 304 # is the file changed?
306 305 text = fctx.data()
307 306 if fparent2 != nullid or meta or flog.cmp(fparent1, text):
308 307 if touched is None: # do not overwrite added
309 308 touched = 'modified'
310 309 fnode = flog.add(text, meta, tr, linkrev, fparent1, fparent2)
311 310 # are just the flags changed during merge?
312 311 elif fname in manifest1 and manifest1.flags(fname) != fctx.flags():
313 312 touched = 'modified'
314 313 fnode = fparent1
315 314 else:
316 315 fnode = fparent1
317 316 return fnode, touched
318 317
319 318
320 319 def _commit_manifest(tr, linkrev, ctx, mctx, files, added, drop):
321 320 """make a new manifest entry (or reuse a new one)
322 321
323 322 given an initialised manifest context and precomputed list of
324 323 - files: files affected by the commit
325 324 - added: new entries in the manifest
326 325 - drop: entries present in parents but absent of this one
327 326
328 327 Create a new manifest revision, reuse existing ones if possible.
329 328
330 329 Return the nodeid of the manifest revision.
331 330 """
332 331 repo = ctx.repo()
333 332
334 333 md = None
335 334
336 335 # all this is cached, so it is find to get them all from the ctx.
337 336 p1 = ctx.p1()
338 337 p2 = ctx.p2()
339 338 m1ctx = p1.manifestctx()
340 339
341 340 m1 = m1ctx.read()
342 341
343 342 manifest = mctx.read()
344 343
345 344 if not files:
346 345 # if no "files" actually changed in terms of the changelog,
347 346 # try hard to detect unmodified manifest entry so that the
348 347 # exact same commit can be reproduced later on convert.
349 348 md = m1.diff(manifest, scmutil.matchfiles(repo, ctx.files()))
350 349 if not files and md:
351 350 repo.ui.debug(
352 351 b'not reusing manifest (no file change in '
353 352 b'changelog, but manifest differs)\n'
354 353 )
355 354 if files or md:
356 355 repo.ui.note(_(b"committing manifest\n"))
357 356 # we're using narrowmatch here since it's already applied at
358 357 # other stages (such as dirstate.walk), so we're already
359 358 # ignoring things outside of narrowspec in most cases. The
360 359 # one case where we might have files outside the narrowspec
361 360 # at this point is merges, and we already error out in the
362 361 # case where the merge has files outside of the narrowspec,
363 362 # so this is safe.
364 363 mn = mctx.write(
365 364 tr,
366 365 linkrev,
367 366 p1.manifestnode(),
368 367 p2.manifestnode(),
369 368 added,
370 369 drop,
371 370 match=repo.narrowmatch(),
372 371 )
373 372 else:
374 373 repo.ui.debug(
375 374 b'reusing manifest from p1 (listed files ' b'actually unchanged)\n'
376 375 )
377 376 mn = p1.manifestnode()
378 377
379 378 return mn
General Comments 0
You need to be logged in to leave comments. Login now