##// END OF EJS Templates
commit: pass mergestate into `_process_files` instead of re-reading it...
Pulkit Goyal -
r46299:035302e6 default
parent child Browse files
Show More
@@ -1,480 +1,479
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 mn, files = _prepare_files(tr, ctx, error=error, origctx=origctx)
67 mn, files = _prepare_files(tr, ctx, error=error, origctx=origctx)
68
68
69 extra = ctx.extra().copy()
69 extra = ctx.extra().copy()
70
70
71 if extra is not None:
71 if extra is not None:
72 for name in (
72 for name in (
73 b'p1copies',
73 b'p1copies',
74 b'p2copies',
74 b'p2copies',
75 b'filesadded',
75 b'filesadded',
76 b'filesremoved',
76 b'filesremoved',
77 ):
77 ):
78 extra.pop(name, None)
78 extra.pop(name, None)
79 if repo.changelog._copiesstorage == b'extra':
79 if repo.changelog._copiesstorage == b'extra':
80 extra = _extra_with_copies(repo, extra, files)
80 extra = _extra_with_copies(repo, extra, files)
81
81
82 # update changelog
82 # update changelog
83 repo.ui.note(_(b"committing changelog\n"))
83 repo.ui.note(_(b"committing changelog\n"))
84 repo.changelog.delayupdate(tr)
84 repo.changelog.delayupdate(tr)
85 n = repo.changelog.add(
85 n = repo.changelog.add(
86 mn,
86 mn,
87 files,
87 files,
88 ctx.description(),
88 ctx.description(),
89 tr,
89 tr,
90 p1.node(),
90 p1.node(),
91 p2.node(),
91 p2.node(),
92 user,
92 user,
93 ctx.date(),
93 ctx.date(),
94 extra,
94 extra,
95 )
95 )
96 xp1, xp2 = p1.hex(), p2 and p2.hex() or b''
96 xp1, xp2 = p1.hex(), p2 and p2.hex() or b''
97 repo.hook(
97 repo.hook(
98 b'pretxncommit', throw=True, node=hex(n), parent1=xp1, parent2=xp2,
98 b'pretxncommit', throw=True, node=hex(n), parent1=xp1, parent2=xp2,
99 )
99 )
100 # set the new commit is proper phase
100 # set the new commit is proper phase
101 targetphase = subrepoutil.newcommitphase(repo.ui, ctx)
101 targetphase = subrepoutil.newcommitphase(repo.ui, ctx)
102 if targetphase:
102 if targetphase:
103 # retract boundary do not alter parent changeset.
103 # retract boundary do not alter parent changeset.
104 # if a parent have higher the resulting phase will
104 # if a parent have higher the resulting phase will
105 # be compliant anyway
105 # be compliant anyway
106 #
106 #
107 # if minimal phase was 0 we don't need to retract anything
107 # if minimal phase was 0 we don't need to retract anything
108 phases.registernew(repo, tr, targetphase, [n])
108 phases.registernew(repo, tr, targetphase, [n])
109 return n
109 return n
110
110
111
111
112 def _prepare_files(tr, ctx, error=False, origctx=None):
112 def _prepare_files(tr, ctx, error=False, origctx=None):
113 repo = ctx.repo()
113 repo = ctx.repo()
114 p1 = ctx.p1()
114 p1 = ctx.p1()
115
115
116 writechangesetcopy, writefilecopymeta = _write_copy_meta(repo)
116 writechangesetcopy, writefilecopymeta = _write_copy_meta(repo)
117 ms = mergestate.mergestate.read(repo)
117 ms = mergestate.mergestate.read(repo)
118 salvaged = _get_salvaged(repo, ms, ctx)
118 salvaged = _get_salvaged(repo, ms, ctx)
119
119
120 if ctx.manifestnode():
120 if ctx.manifestnode():
121 # reuse an existing manifest revision
121 # reuse an existing manifest revision
122 repo.ui.debug(b'reusing known manifest\n')
122 repo.ui.debug(b'reusing known manifest\n')
123 mn = ctx.manifestnode()
123 mn = ctx.manifestnode()
124 files = metadata.ChangingFiles()
124 files = metadata.ChangingFiles()
125 files.update_touched(ctx.files())
125 files.update_touched(ctx.files())
126 if writechangesetcopy:
126 if writechangesetcopy:
127 files.update_added(ctx.filesadded())
127 files.update_added(ctx.filesadded())
128 files.update_removed(ctx.filesremoved())
128 files.update_removed(ctx.filesremoved())
129 elif not ctx.files():
129 elif not ctx.files():
130 repo.ui.debug(b'reusing manifest from p1 (no file change)\n')
130 repo.ui.debug(b'reusing manifest from p1 (no file change)\n')
131 mn = p1.manifestnode()
131 mn = p1.manifestnode()
132 files = metadata.ChangingFiles()
132 files = metadata.ChangingFiles()
133 else:
133 else:
134 mn, files = _process_files(tr, ctx, error=error)
134 mn, files = _process_files(tr, ctx, ms, error=error)
135
135
136 if origctx and origctx.manifestnode() == mn:
136 if origctx and origctx.manifestnode() == mn:
137 origfiles = origctx.files()
137 origfiles = origctx.files()
138 assert files.touched.issubset(origfiles)
138 assert files.touched.issubset(origfiles)
139 files.update_touched(origfiles)
139 files.update_touched(origfiles)
140
140
141 if writechangesetcopy:
141 if writechangesetcopy:
142 files.update_copies_from_p1(ctx.p1copies())
142 files.update_copies_from_p1(ctx.p1copies())
143 files.update_copies_from_p2(ctx.p2copies())
143 files.update_copies_from_p2(ctx.p2copies())
144
144
145 for s in salvaged:
145 for s in salvaged:
146 files.mark_salvaged(s)
146 files.mark_salvaged(s)
147
147
148 return mn, files
148 return mn, files
149
149
150
150
151 def _get_salvaged(repo, ms, ctx):
151 def _get_salvaged(repo, ms, ctx):
152 """ returns a list of salvaged files
152 """ returns a list of salvaged files
153
153
154 returns empty list if config option which process salvaged files are
154 returns empty list if config option which process salvaged files are
155 not enabled """
155 not enabled """
156 salvaged = []
156 salvaged = []
157 copy_sd = repo.filecopiesmode == b'changeset-sidedata'
157 copy_sd = repo.filecopiesmode == b'changeset-sidedata'
158 if copy_sd and len(ctx.parents()) > 1:
158 if copy_sd and len(ctx.parents()) > 1:
159 if ms.active():
159 if ms.active():
160 for fname in sorted(ms._stateextras.keys()):
160 for fname in sorted(ms._stateextras.keys()):
161 might_removed = ms.extras(fname).get(b'merge-removal-candidate')
161 might_removed = ms.extras(fname).get(b'merge-removal-candidate')
162 if might_removed == b'yes':
162 if might_removed == b'yes':
163 if fname in ctx:
163 if fname in ctx:
164 salvaged.append(fname)
164 salvaged.append(fname)
165 return salvaged
165 return salvaged
166
166
167
167
168 def _process_files(tr, ctx, error=False):
168 def _process_files(tr, ctx, ms, error=False):
169 repo = ctx.repo()
169 repo = ctx.repo()
170 p1 = ctx.p1()
170 p1 = ctx.p1()
171 p2 = ctx.p2()
171 p2 = ctx.p2()
172
172
173 writechangesetcopy, writefilecopymeta = _write_copy_meta(repo)
173 writechangesetcopy, writefilecopymeta = _write_copy_meta(repo)
174
174
175 m1ctx = p1.manifestctx()
175 m1ctx = p1.manifestctx()
176 m2ctx = p2.manifestctx()
176 m2ctx = p2.manifestctx()
177 mctx = m1ctx.copy()
177 mctx = m1ctx.copy()
178
178
179 m = mctx.read()
179 m = mctx.read()
180 m1 = m1ctx.read()
180 m1 = m1ctx.read()
181 m2 = m2ctx.read()
181 m2 = m2ctx.read()
182 ms = mergestate.mergestate.read(repo)
183
182
184 files = metadata.ChangingFiles()
183 files = metadata.ChangingFiles()
185
184
186 # check in files
185 # check in files
187 added = []
186 added = []
188 removed = list(ctx.removed())
187 removed = list(ctx.removed())
189 linkrev = len(repo)
188 linkrev = len(repo)
190 repo.ui.note(_(b"committing files:\n"))
189 repo.ui.note(_(b"committing files:\n"))
191 uipathfn = scmutil.getuipathfn(repo)
190 uipathfn = scmutil.getuipathfn(repo)
192 for f in sorted(ctx.modified() + ctx.added()):
191 for f in sorted(ctx.modified() + ctx.added()):
193 repo.ui.note(uipathfn(f) + b"\n")
192 repo.ui.note(uipathfn(f) + b"\n")
194 try:
193 try:
195 fctx = ctx[f]
194 fctx = ctx[f]
196 if fctx is None:
195 if fctx is None:
197 removed.append(f)
196 removed.append(f)
198 else:
197 else:
199 added.append(f)
198 added.append(f)
200 m[f], is_touched = _filecommit(
199 m[f], is_touched = _filecommit(
201 repo, fctx, m1, m2, linkrev, tr, writefilecopymeta, ms
200 repo, fctx, m1, m2, linkrev, tr, writefilecopymeta, ms
202 )
201 )
203 if is_touched:
202 if is_touched:
204 if is_touched == 'added':
203 if is_touched == 'added':
205 files.mark_added(f)
204 files.mark_added(f)
206 elif is_touched == 'merged':
205 elif is_touched == 'merged':
207 files.mark_merged(f)
206 files.mark_merged(f)
208 else:
207 else:
209 files.mark_touched(f)
208 files.mark_touched(f)
210 m.setflag(f, fctx.flags())
209 m.setflag(f, fctx.flags())
211 except OSError:
210 except OSError:
212 repo.ui.warn(_(b"trouble committing %s!\n") % uipathfn(f))
211 repo.ui.warn(_(b"trouble committing %s!\n") % uipathfn(f))
213 raise
212 raise
214 except IOError as inst:
213 except IOError as inst:
215 errcode = getattr(inst, 'errno', errno.ENOENT)
214 errcode = getattr(inst, 'errno', errno.ENOENT)
216 if error or errcode and errcode != errno.ENOENT:
215 if error or errcode and errcode != errno.ENOENT:
217 repo.ui.warn(_(b"trouble committing %s!\n") % uipathfn(f))
216 repo.ui.warn(_(b"trouble committing %s!\n") % uipathfn(f))
218 raise
217 raise
219
218
220 # update manifest
219 # update manifest
221 removed = [f for f in removed if f in m1 or f in m2]
220 removed = [f for f in removed if f in m1 or f in m2]
222 drop = sorted([f for f in removed if f in m])
221 drop = sorted([f for f in removed if f in m])
223 for f in drop:
222 for f in drop:
224 del m[f]
223 del m[f]
225 if p2.rev() == nullrev:
224 if p2.rev() == nullrev:
226 files.update_removed(removed)
225 files.update_removed(removed)
227 else:
226 else:
228 rf = metadata.get_removal_filter(ctx, (p1, p2, m1, m2))
227 rf = metadata.get_removal_filter(ctx, (p1, p2, m1, m2))
229 for f in removed:
228 for f in removed:
230 if not rf(f):
229 if not rf(f):
231 files.mark_removed(f)
230 files.mark_removed(f)
232
231
233 mn = _commit_manifest(tr, linkrev, ctx, mctx, m, files.touched, added, drop)
232 mn = _commit_manifest(tr, linkrev, ctx, mctx, m, files.touched, added, drop)
234
233
235 return mn, files
234 return mn, files
236
235
237
236
238 def _filecommit(
237 def _filecommit(
239 repo, fctx, manifest1, manifest2, linkrev, tr, includecopymeta, ms,
238 repo, fctx, manifest1, manifest2, linkrev, tr, includecopymeta, ms,
240 ):
239 ):
241 """
240 """
242 commit an individual file as part of a larger transaction
241 commit an individual file as part of a larger transaction
243
242
244 input:
243 input:
245
244
246 fctx: a file context with the content we are trying to commit
245 fctx: a file context with the content we are trying to commit
247 manifest1: manifest of changeset first parent
246 manifest1: manifest of changeset first parent
248 manifest2: manifest of changeset second parent
247 manifest2: manifest of changeset second parent
249 linkrev: revision number of the changeset being created
248 linkrev: revision number of the changeset being created
250 tr: current transation
249 tr: current transation
251 includecopymeta: boolean, set to False to skip storing the copy data
250 includecopymeta: boolean, set to False to skip storing the copy data
252 (only used by the Google specific feature of using
251 (only used by the Google specific feature of using
253 changeset extra as copy source of truth).
252 changeset extra as copy source of truth).
254 ms: mergestate object
253 ms: mergestate object
255
254
256 output: (filenode, touched)
255 output: (filenode, touched)
257
256
258 filenode: the filenode that should be used by this changeset
257 filenode: the filenode that should be used by this changeset
259 touched: one of: None (mean untouched), 'added' or 'modified'
258 touched: one of: None (mean untouched), 'added' or 'modified'
260 """
259 """
261
260
262 fname = fctx.path()
261 fname = fctx.path()
263 fparent1 = manifest1.get(fname, nullid)
262 fparent1 = manifest1.get(fname, nullid)
264 fparent2 = manifest2.get(fname, nullid)
263 fparent2 = manifest2.get(fname, nullid)
265 touched = None
264 touched = None
266 if fparent1 == fparent2 == nullid:
265 if fparent1 == fparent2 == nullid:
267 touched = 'added'
266 touched = 'added'
268
267
269 if isinstance(fctx, context.filectx):
268 if isinstance(fctx, context.filectx):
270 # This block fast path most comparisons which are usually done. It
269 # This block fast path most comparisons which are usually done. It
271 # assumes that bare filectx is used and no merge happened, hence no
270 # assumes that bare filectx is used and no merge happened, hence no
272 # need to create a new file revision in this case.
271 # need to create a new file revision in this case.
273 node = fctx.filenode()
272 node = fctx.filenode()
274 if node in [fparent1, fparent2]:
273 if node in [fparent1, fparent2]:
275 repo.ui.debug(b'reusing %s filelog entry\n' % fname)
274 repo.ui.debug(b'reusing %s filelog entry\n' % fname)
276 if (
275 if (
277 fparent1 != nullid and manifest1.flags(fname) != fctx.flags()
276 fparent1 != nullid and manifest1.flags(fname) != fctx.flags()
278 ) or (
277 ) or (
279 fparent2 != nullid and manifest2.flags(fname) != fctx.flags()
278 fparent2 != nullid and manifest2.flags(fname) != fctx.flags()
280 ):
279 ):
281 touched = 'modified'
280 touched = 'modified'
282 return node, touched
281 return node, touched
283
282
284 flog = repo.file(fname)
283 flog = repo.file(fname)
285 meta = {}
284 meta = {}
286 cfname = fctx.copysource()
285 cfname = fctx.copysource()
287 fnode = None
286 fnode = None
288
287
289 if cfname and cfname != fname:
288 if cfname and cfname != fname:
290 # Mark the new revision of this file as a copy of another
289 # Mark the new revision of this file as a copy of another
291 # file. This copy data will effectively act as a parent
290 # file. This copy data will effectively act as a parent
292 # of this new revision. If this is a merge, the first
291 # of this new revision. If this is a merge, the first
293 # parent will be the nullid (meaning "look up the copy data")
292 # parent will be the nullid (meaning "look up the copy data")
294 # and the second one will be the other parent. For example:
293 # and the second one will be the other parent. For example:
295 #
294 #
296 # 0 --- 1 --- 3 rev1 changes file foo
295 # 0 --- 1 --- 3 rev1 changes file foo
297 # \ / rev2 renames foo to bar and changes it
296 # \ / rev2 renames foo to bar and changes it
298 # \- 2 -/ rev3 should have bar with all changes and
297 # \- 2 -/ rev3 should have bar with all changes and
299 # should record that bar descends from
298 # should record that bar descends from
300 # bar in rev2 and foo in rev1
299 # bar in rev2 and foo in rev1
301 #
300 #
302 # this allows this merge to succeed:
301 # this allows this merge to succeed:
303 #
302 #
304 # 0 --- 1 --- 3 rev4 reverts the content change from rev2
303 # 0 --- 1 --- 3 rev4 reverts the content change from rev2
305 # \ / merging rev3 and rev4 should use bar@rev2
304 # \ / merging rev3 and rev4 should use bar@rev2
306 # \- 2 --- 4 as the merge base
305 # \- 2 --- 4 as the merge base
307 #
306 #
308
307
309 cnode = manifest1.get(cfname)
308 cnode = manifest1.get(cfname)
310 newfparent = fparent2
309 newfparent = fparent2
311
310
312 if manifest2: # branch merge
311 if manifest2: # branch merge
313 if fparent2 == nullid or cnode is None: # copied on remote side
312 if fparent2 == nullid or cnode is None: # copied on remote side
314 if cfname in manifest2:
313 if cfname in manifest2:
315 cnode = manifest2[cfname]
314 cnode = manifest2[cfname]
316 newfparent = fparent1
315 newfparent = fparent1
317
316
318 # Here, we used to search backwards through history to try to find
317 # Here, we used to search backwards through history to try to find
319 # where the file copy came from if the source of a copy was not in
318 # where the file copy came from if the source of a copy was not in
320 # the parent directory. However, this doesn't actually make sense to
319 # the parent directory. However, this doesn't actually make sense to
321 # do (what does a copy from something not in your working copy even
320 # do (what does a copy from something not in your working copy even
322 # mean?) and it causes bugs (eg, issue4476). Instead, we will warn
321 # mean?) and it causes bugs (eg, issue4476). Instead, we will warn
323 # the user that copy information was dropped, so if they didn't
322 # the user that copy information was dropped, so if they didn't
324 # expect this outcome it can be fixed, but this is the correct
323 # expect this outcome it can be fixed, but this is the correct
325 # behavior in this circumstance.
324 # behavior in this circumstance.
326
325
327 if cnode:
326 if cnode:
328 repo.ui.debug(b" %s: copy %s:%s\n" % (fname, cfname, hex(cnode)))
327 repo.ui.debug(b" %s: copy %s:%s\n" % (fname, cfname, hex(cnode)))
329 if includecopymeta:
328 if includecopymeta:
330 meta[b"copy"] = cfname
329 meta[b"copy"] = cfname
331 meta[b"copyrev"] = hex(cnode)
330 meta[b"copyrev"] = hex(cnode)
332 fparent1, fparent2 = nullid, newfparent
331 fparent1, fparent2 = nullid, newfparent
333 else:
332 else:
334 repo.ui.warn(
333 repo.ui.warn(
335 _(
334 _(
336 b"warning: can't find ancestor for '%s' "
335 b"warning: can't find ancestor for '%s' "
337 b"copied from '%s'!\n"
336 b"copied from '%s'!\n"
338 )
337 )
339 % (fname, cfname)
338 % (fname, cfname)
340 )
339 )
341
340
342 elif fparent1 == nullid:
341 elif fparent1 == nullid:
343 fparent1, fparent2 = fparent2, nullid
342 fparent1, fparent2 = fparent2, nullid
344 elif fparent2 != nullid:
343 elif fparent2 != nullid:
345 # is one parent an ancestor of the other?
344 # is one parent an ancestor of the other?
346 fparentancestors = flog.commonancestorsheads(fparent1, fparent2)
345 fparentancestors = flog.commonancestorsheads(fparent1, fparent2)
347 if fparent1 in fparentancestors:
346 if fparent1 in fparentancestors:
348 fparent1, fparent2 = fparent2, nullid
347 fparent1, fparent2 = fparent2, nullid
349 elif fparent2 in fparentancestors:
348 elif fparent2 in fparentancestors:
350 fparent2 = nullid
349 fparent2 = nullid
351 elif not fparentancestors:
350 elif not fparentancestors:
352 # TODO: this whole if-else might be simplified much more
351 # TODO: this whole if-else might be simplified much more
353 if (
352 if (
354 ms.active()
353 ms.active()
355 and ms.extras(fname).get(b'filenode-source') == b'other'
354 and ms.extras(fname).get(b'filenode-source') == b'other'
356 ):
355 ):
357 fparent1, fparent2 = fparent2, nullid
356 fparent1, fparent2 = fparent2, nullid
358
357
359 force_new_node = False
358 force_new_node = False
360 # The file might have been deleted by merge code and user explicitly choose
359 # The file might have been deleted by merge code and user explicitly choose
361 # to revert the file and keep it. The other case can be where there is
360 # to revert the file and keep it. The other case can be where there is
362 # change-delete or delete-change conflict and user explicitly choose to keep
361 # change-delete or delete-change conflict and user explicitly choose to keep
363 # the file. The goal is to create a new filenode for users explicit choices
362 # the file. The goal is to create a new filenode for users explicit choices
364 if (
363 if (
365 repo.ui.configbool(b'experimental', b'merge-track-salvaged')
364 repo.ui.configbool(b'experimental', b'merge-track-salvaged')
366 and ms.active()
365 and ms.active()
367 and ms.extras(fname).get(b'merge-removal-candidate') == b'yes'
366 and ms.extras(fname).get(b'merge-removal-candidate') == b'yes'
368 ):
367 ):
369 force_new_node = True
368 force_new_node = True
370 # is the file changed?
369 # is the file changed?
371 text = fctx.data()
370 text = fctx.data()
372 if fparent2 != nullid or meta or flog.cmp(fparent1, text) or force_new_node:
371 if fparent2 != nullid or meta or flog.cmp(fparent1, text) or force_new_node:
373 if touched is None: # do not overwrite added
372 if touched is None: # do not overwrite added
374 if fparent2 == nullid:
373 if fparent2 == nullid:
375 touched = 'modified'
374 touched = 'modified'
376 else:
375 else:
377 touched = 'merged'
376 touched = 'merged'
378 fnode = flog.add(text, meta, tr, linkrev, fparent1, fparent2)
377 fnode = flog.add(text, meta, tr, linkrev, fparent1, fparent2)
379 # are just the flags changed during merge?
378 # are just the flags changed during merge?
380 elif fname in manifest1 and manifest1.flags(fname) != fctx.flags():
379 elif fname in manifest1 and manifest1.flags(fname) != fctx.flags():
381 touched = 'modified'
380 touched = 'modified'
382 fnode = fparent1
381 fnode = fparent1
383 else:
382 else:
384 fnode = fparent1
383 fnode = fparent1
385 return fnode, touched
384 return fnode, touched
386
385
387
386
388 def _commit_manifest(tr, linkrev, ctx, mctx, manifest, files, added, drop):
387 def _commit_manifest(tr, linkrev, ctx, mctx, manifest, files, added, drop):
389 """make a new manifest entry (or reuse a new one)
388 """make a new manifest entry (or reuse a new one)
390
389
391 given an initialised manifest context and precomputed list of
390 given an initialised manifest context and precomputed list of
392 - files: files affected by the commit
391 - files: files affected by the commit
393 - added: new entries in the manifest
392 - added: new entries in the manifest
394 - drop: entries present in parents but absent of this one
393 - drop: entries present in parents but absent of this one
395
394
396 Create a new manifest revision, reuse existing ones if possible.
395 Create a new manifest revision, reuse existing ones if possible.
397
396
398 Return the nodeid of the manifest revision.
397 Return the nodeid of the manifest revision.
399 """
398 """
400 repo = ctx.repo()
399 repo = ctx.repo()
401
400
402 md = None
401 md = None
403
402
404 # all this is cached, so it is find to get them all from the ctx.
403 # all this is cached, so it is find to get them all from the ctx.
405 p1 = ctx.p1()
404 p1 = ctx.p1()
406 p2 = ctx.p2()
405 p2 = ctx.p2()
407 m1ctx = p1.manifestctx()
406 m1ctx = p1.manifestctx()
408
407
409 m1 = m1ctx.read()
408 m1 = m1ctx.read()
410
409
411 if not files:
410 if not files:
412 # if no "files" actually changed in terms of the changelog,
411 # if no "files" actually changed in terms of the changelog,
413 # try hard to detect unmodified manifest entry so that the
412 # try hard to detect unmodified manifest entry so that the
414 # exact same commit can be reproduced later on convert.
413 # exact same commit can be reproduced later on convert.
415 md = m1.diff(manifest, scmutil.matchfiles(repo, ctx.files()))
414 md = m1.diff(manifest, scmutil.matchfiles(repo, ctx.files()))
416 if not files and md:
415 if not files and md:
417 repo.ui.debug(
416 repo.ui.debug(
418 b'not reusing manifest (no file change in '
417 b'not reusing manifest (no file change in '
419 b'changelog, but manifest differs)\n'
418 b'changelog, but manifest differs)\n'
420 )
419 )
421 if files or md:
420 if files or md:
422 repo.ui.note(_(b"committing manifest\n"))
421 repo.ui.note(_(b"committing manifest\n"))
423 # we're using narrowmatch here since it's already applied at
422 # we're using narrowmatch here since it's already applied at
424 # other stages (such as dirstate.walk), so we're already
423 # other stages (such as dirstate.walk), so we're already
425 # ignoring things outside of narrowspec in most cases. The
424 # ignoring things outside of narrowspec in most cases. The
426 # one case where we might have files outside the narrowspec
425 # one case where we might have files outside the narrowspec
427 # at this point is merges, and we already error out in the
426 # at this point is merges, and we already error out in the
428 # case where the merge has files outside of the narrowspec,
427 # case where the merge has files outside of the narrowspec,
429 # so this is safe.
428 # so this is safe.
430 mn = mctx.write(
429 mn = mctx.write(
431 tr,
430 tr,
432 linkrev,
431 linkrev,
433 p1.manifestnode(),
432 p1.manifestnode(),
434 p2.manifestnode(),
433 p2.manifestnode(),
435 added,
434 added,
436 drop,
435 drop,
437 match=repo.narrowmatch(),
436 match=repo.narrowmatch(),
438 )
437 )
439 else:
438 else:
440 repo.ui.debug(
439 repo.ui.debug(
441 b'reusing manifest from p1 (listed files ' b'actually unchanged)\n'
440 b'reusing manifest from p1 (listed files ' b'actually unchanged)\n'
442 )
441 )
443 mn = p1.manifestnode()
442 mn = p1.manifestnode()
444
443
445 return mn
444 return mn
446
445
447
446
448 def _extra_with_copies(repo, extra, files):
447 def _extra_with_copies(repo, extra, files):
449 """encode copy information into a `extra` dictionnary"""
448 """encode copy information into a `extra` dictionnary"""
450 p1copies = files.copied_from_p1
449 p1copies = files.copied_from_p1
451 p2copies = files.copied_from_p2
450 p2copies = files.copied_from_p2
452 filesadded = files.added
451 filesadded = files.added
453 filesremoved = files.removed
452 filesremoved = files.removed
454 files = sorted(files.touched)
453 files = sorted(files.touched)
455 if not _write_copy_meta(repo)[1]:
454 if not _write_copy_meta(repo)[1]:
456 # If writing only to changeset extras, use None to indicate that
455 # If writing only to changeset extras, use None to indicate that
457 # no entry should be written. If writing to both, write an empty
456 # no entry should be written. If writing to both, write an empty
458 # entry to prevent the reader from falling back to reading
457 # entry to prevent the reader from falling back to reading
459 # filelogs.
458 # filelogs.
460 p1copies = p1copies or None
459 p1copies = p1copies or None
461 p2copies = p2copies or None
460 p2copies = p2copies or None
462 filesadded = filesadded or None
461 filesadded = filesadded or None
463 filesremoved = filesremoved or None
462 filesremoved = filesremoved or None
464
463
465 extrasentries = p1copies, p2copies, filesadded, filesremoved
464 extrasentries = p1copies, p2copies, filesadded, filesremoved
466 if extra is None and any(x is not None for x in extrasentries):
465 if extra is None and any(x is not None for x in extrasentries):
467 extra = {}
466 extra = {}
468 if p1copies is not None:
467 if p1copies is not None:
469 p1copies = metadata.encodecopies(files, p1copies)
468 p1copies = metadata.encodecopies(files, p1copies)
470 extra[b'p1copies'] = p1copies
469 extra[b'p1copies'] = p1copies
471 if p2copies is not None:
470 if p2copies is not None:
472 p2copies = metadata.encodecopies(files, p2copies)
471 p2copies = metadata.encodecopies(files, p2copies)
473 extra[b'p2copies'] = p2copies
472 extra[b'p2copies'] = p2copies
474 if filesadded is not None:
473 if filesadded is not None:
475 filesadded = metadata.encodefileindices(files, filesadded)
474 filesadded = metadata.encodefileindices(files, filesadded)
476 extra[b'filesadded'] = filesadded
475 extra[b'filesadded'] = filesadded
477 if filesremoved is not None:
476 if filesremoved is not None:
478 filesremoved = metadata.encodefileindices(files, filesremoved)
477 filesremoved = metadata.encodefileindices(files, filesremoved)
479 extra[b'filesremoved'] = filesremoved
478 extra[b'filesremoved'] = filesremoved
480 return extra
479 return extra
General Comments 0
You need to be logged in to leave comments. Login now