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