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