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