218
219
:param path: The path the entry is at in the tree.
219
220
:param tree: The tree which contains this entry and should be used to
222
:param content_summary: Summary data from the tree about the paths
223
content - stat, length, exec, sha/link target. This is only
224
accessed when the entry has a revision of None - that is when it is
225
a candidate to commit.
221
226
:return: True if a new version of the entry has been recorded.
222
227
(Committing a merge where a file was only changed on the other side
223
228
will not return True.)
225
230
if self.new_inventory.root is None:
226
231
self._check_root(ie, parent_invs, tree)
232
if ie.revision is None:
233
kind = content_summary[0]
235
# ie is carried over from a prior commit
237
# XXX: repository specific check for nested tree support goes here - if
238
# the repo doesn't want nested trees we skip it ?
239
if (kind == 'tree-reference' and
240
not self.repository._format.supports_tree_reference):
241
# mismatch between commit builder logic and repository:
242
# this needs the entry creation pushed down into the builder.
243
raise NotImplementedError('Missing repository subtree support.')
244
# transitional assert only, will remove before release.
245
assert ie.kind == kind
227
246
self.new_inventory.add(ie)
229
248
# ie.revision is always None if the InventoryEntry is considered
230
# for committing. ie.snapshot will record the correct revision
231
# which may be the sole parent if it is untouched.
249
# for committing. We may record the previous parents revision if the
250
# content is actually unchanged against a sole head.
232
251
if ie.revision is not None:
233
252
return ie.revision == self._new_revision_id and (path != '' or
234
253
self._versioned_root)
255
# XXX: Friction: parent_candidates should return a list not a dict
256
# so that we don't have to walk the inventories again.
236
257
parent_candiate_entries = ie.parent_candidates(parent_invs)
237
heads = self.repository.get_graph().heads(parent_candiate_entries.keys())
238
# XXX: Note that this is unordered - and this is tolerable because
239
# the previous code was also unordered.
240
previous_entries = dict((head, parent_candiate_entries[head]) for head
242
# we are creating a new revision for ie in the history store and
244
ie.snapshot(self._new_revision_id, path, previous_entries, tree, self)
245
return ie.revision == self._new_revision_id and (path != '' or
246
self._versioned_root)
248
def modified_directory(self, file_id, file_parents):
249
"""Record the presence of a symbolic link.
251
:param file_id: The file_id of the link to record.
252
:param file_parents: The per-file parent revision ids.
254
self._add_text_to_weave(file_id, [], file_parents.keys())
256
def modified_reference(self, file_id, file_parents):
257
"""Record the modification of a reference.
259
:param file_id: The file_id of the link to record.
260
:param file_parents: The per-file parent revision ids.
262
self._add_text_to_weave(file_id, [], file_parents.keys())
264
def modified_file_text(self, file_id, file_parents,
265
get_content_byte_lines, text_sha1=None,
267
"""Record the text of file file_id
269
:param file_id: The file_id of the file to record the text of.
270
:param file_parents: The per-file parent revision ids.
271
:param get_content_byte_lines: A callable which will return the byte
273
:param text_sha1: Optional SHA1 of the file contents.
274
:param text_size: Optional size of the file contents.
276
# mutter('storing text of file {%s} in revision {%s} into %r',
277
# file_id, self._new_revision_id, self.repository.weave_store)
278
# special case to avoid diffing on renames or
280
if (len(file_parents) == 1
281
and text_sha1 == file_parents.values()[0].text_sha1
282
and text_size == file_parents.values()[0].text_size):
283
previous_ie = file_parents.values()[0]
284
versionedfile = self.repository.weave_store.get_weave(file_id,
285
self.repository.get_transaction())
286
versionedfile.clone_text(self._new_revision_id,
287
previous_ie.revision, file_parents.keys())
288
return text_sha1, text_size
258
head_set = self.repository.get_graph().heads(parent_candiate_entries.keys())
260
for inv in parent_invs:
261
if ie.file_id in inv:
262
old_rev = inv[ie.file_id].revision
263
if old_rev in head_set:
264
heads.append(inv[ie.file_id].revision)
265
head_set.remove(inv[ie.file_id].revision)
268
# now we check to see if we need to write a new record to the
270
# We write a new entry unless there is one head to the ancestors, and
271
# the kind-derived content is unchanged.
273
# Cheapest check first: no ancestors, or more the one head in the
274
# ancestors, we write a new node.
278
# There is a single head, look it up for comparison
279
parent_entry = parent_candiate_entries[heads[0]]
280
# if the non-content specific data has changed, we'll be writing a
282
if (parent_entry.parent_id != ie.parent_id or
283
parent_entry.name != ie.name):
285
# now we need to do content specific checks:
287
# if the kind changed the content obviously has
288
if kind != parent_entry.kind:
292
if (# if the file length changed we have to store:
293
parent_entry.text_size != content_summary[1] or
294
# if the exec bit has changed we have to store:
295
parent_entry.executable != content_summary[2]):
297
elif parent_entry.text_sha1 == content_summary[3]:
298
# all meta and content is unchanged (using a hash cache
299
# hit to check the sha)
300
ie.revision = parent_entry.revision
301
ie.text_size = parent_entry.text_size
302
ie.text_sha1 = parent_entry.text_sha1
303
ie.executable = parent_entry.executable
306
# Either there is only a hash change(no hash cache entry,
307
# or same size content change), or there is no change on
309
# Provide the parent's hash to the store layer, so that the
310
# content is unchanged we will not store a new node.
311
nostore_sha = parent_entry.text_sha1
313
# We want to record a new node regardless of the presence or
314
# absence of a content change in the file.
316
ie.executable = content_summary[2]
317
lines = tree.get_file(ie.file_id, path).readlines()
319
ie.text_sha1, ie.text_size = self._add_text_to_weave(
320
ie.file_id, lines, heads, nostore_sha)
321
except errors.ExistingContent:
322
# Turns out that the file content was unchanged, and we were
323
# only going to store a new node if it was changed. Carry over
325
ie.revision = parent_entry.revision
326
ie.text_size = parent_entry.text_size
327
ie.text_sha1 = parent_entry.text_sha1
328
ie.executable = parent_entry.executable
330
elif kind == 'directory':
332
# all data is meta here, nothing specific to directory, so
334
ie.revision = parent_entry.revision
337
self._add_text_to_weave(ie.file_id, lines, heads, None)
338
elif kind == 'symlink':
339
current_link_target = content_summary[3]
341
# symlink target is not generic metadata, check if it has
343
if current_link_target != parent_entry.symlink_target:
346
# unchanged, carry over.
347
ie.revision = parent_entry.revision
348
ie.symlink_target = parent_entry.symlink_target
350
ie.symlink_target = current_link_target
352
self._add_text_to_weave(ie.file_id, lines, heads, None)
353
elif kind == 'tree-reference':
355
if content_summary[3] != parent_entry.reference_revision:
358
# unchanged, carry over.
359
ie.reference_revision = parent_entry.reference_revision
360
ie.revision = parent_entry.revision
362
ie.reference_revision = content_summary[3]
364
self._add_text_to_weave(ie.file_id, lines, heads, None)
290
new_lines = get_content_byte_lines()
291
return self._add_text_to_weave(file_id, new_lines,
294
def modified_link(self, file_id, file_parents, link_target):
295
"""Record the presence of a symbolic link.
297
:param file_id: The file_id of the link to record.
298
:param file_parents: The per-file parent revision ids.
299
:param link_target: Target location of this link.
301
self._add_text_to_weave(file_id, [], file_parents.keys())
303
def _add_text_to_weave(self, file_id, new_lines, parents):
366
raise NotImplementedError('unknown kind')
367
ie.revision = self._new_revision_id
370
def _add_text_to_weave(self, file_id, new_lines, parents, nostore_sha):
304
371
versionedfile = self.repository.weave_store.get_weave_or_empty(
305
372
file_id, self.repository.get_transaction())
306
373
# Don't change this to add_lines - add_lines_with_ghosts is cheaper