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