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