257
289
return delta, ie.revision == self._new_revision_id and (path != '' or
258
290
self._versioned_root)
292
# XXX: Friction: parent_candidates should return a list not a dict
293
# so that we don't have to walk the inventories again.
260
294
parent_candiate_entries = ie.parent_candidates(parent_invs)
261
heads = self.repository.get_graph().heads(parent_candiate_entries.keys())
262
# XXX: Note that this is unordered - and this is tolerable because
263
# the previous code was also unordered.
264
previous_entries = dict((head, parent_candiate_entries[head]) for head
266
# we are creating a new revision for ie in the history store and
268
ie.snapshot(self._new_revision_id, path, previous_entries, tree, self)
269
if ie.file_id not in basis_inv:
271
delta = (None, path, ie.file_id, ie)
272
elif ie != basis_inv[ie.file_id]:
274
delta = (basis_inv.id2path(ie.file_id), path, ie.file_id,
279
return delta, ie.revision == self._new_revision_id and (path != '' or
280
self._versioned_root)
282
def modified_directory(self, file_id, file_parents):
283
"""Record the presence of a symbolic link.
285
:param file_id: The file_id of the link to record.
286
:param file_parents: The per-file parent revision ids.
288
self._add_text_to_weave(file_id, [], file_parents.keys())
290
def modified_reference(self, file_id, file_parents):
291
"""Record the modification of a reference.
293
:param file_id: The file_id of the link to record.
294
:param file_parents: The per-file parent revision ids.
296
self._add_text_to_weave(file_id, [], file_parents.keys())
298
def modified_file_text(self, file_id, file_parents,
299
get_content_byte_lines, text_sha1=None,
301
"""Record the text of file file_id
303
:param file_id: The file_id of the file to record the text of.
304
:param file_parents: The per-file parent revision ids.
305
:param get_content_byte_lines: A callable which will return the byte
307
:param text_sha1: Optional SHA1 of the file contents.
308
:param text_size: Optional size of the file contents.
310
# mutter('storing text of file {%s} in revision {%s} into %r',
311
# file_id, self._new_revision_id, self.repository.weave_store)
312
# special case to avoid diffing on renames or
314
if (len(file_parents) == 1
315
and text_sha1 == file_parents.values()[0].text_sha1
316
and text_size == file_parents.values()[0].text_size):
317
previous_ie = file_parents.values()[0]
318
versionedfile = self.repository.weave_store.get_weave(file_id,
319
self.repository.get_transaction())
320
versionedfile.clone_text(self._new_revision_id,
321
previous_ie.revision, file_parents.keys())
322
return text_sha1, text_size
324
new_lines = get_content_byte_lines()
325
return self._add_text_to_weave(file_id, new_lines,
328
def modified_link(self, file_id, file_parents, link_target):
329
"""Record the presence of a symbolic link.
331
:param file_id: The file_id of the link to record.
332
:param file_parents: The per-file parent revision ids.
333
:param link_target: Target location of this link.
335
self._add_text_to_weave(file_id, [], file_parents.keys())
337
def _add_text_to_weave(self, file_id, new_lines, parents):
295
head_set = self.repository.get_graph().heads(parent_candiate_entries.keys())
297
for inv in parent_invs:
298
if ie.file_id in inv:
299
old_rev = inv[ie.file_id].revision
300
if old_rev in head_set:
301
heads.append(inv[ie.file_id].revision)
302
head_set.remove(inv[ie.file_id].revision)
305
# now we check to see if we need to write a new record to the
307
# We write a new entry unless there is one head to the ancestors, and
308
# the kind-derived content is unchanged.
310
# Cheapest check first: no ancestors, or more the one head in the
311
# ancestors, we write a new node.
315
# There is a single head, look it up for comparison
316
parent_entry = parent_candiate_entries[heads[0]]
317
# if the non-content specific data has changed, we'll be writing a
319
if (parent_entry.parent_id != ie.parent_id or
320
parent_entry.name != ie.name):
322
# now we need to do content specific checks:
324
# if the kind changed the content obviously has
325
if kind != parent_entry.kind:
329
if (# if the file length changed we have to store:
330
parent_entry.text_size != content_summary[1] or
331
# if the exec bit has changed we have to store:
332
parent_entry.executable != content_summary[2]):
334
elif parent_entry.text_sha1 == content_summary[3]:
335
# all meta and content is unchanged (using a hash cache
336
# hit to check the sha)
337
ie.revision = parent_entry.revision
338
ie.text_size = parent_entry.text_size
339
ie.text_sha1 = parent_entry.text_sha1
340
ie.executable = parent_entry.executable
341
return self._get_delta(ie, basis_inv, path), False
343
# Either there is only a hash change(no hash cache entry,
344
# or same size content change), or there is no change on
346
# Provide the parent's hash to the store layer, so that the
347
# content is unchanged we will not store a new node.
348
nostore_sha = parent_entry.text_sha1
350
# We want to record a new node regardless of the presence or
351
# absence of a content change in the file.
353
ie.executable = content_summary[2]
354
lines = tree.get_file(ie.file_id, path).readlines()
356
ie.text_sha1, ie.text_size = self._add_text_to_weave(
357
ie.file_id, lines, heads, nostore_sha)
358
except errors.ExistingContent:
359
# Turns out that the file content was unchanged, and we were
360
# only going to store a new node if it was changed. Carry over
362
ie.revision = parent_entry.revision
363
ie.text_size = parent_entry.text_size
364
ie.text_sha1 = parent_entry.text_sha1
365
ie.executable = parent_entry.executable
366
return self._get_delta(ie, basis_inv, path), False
367
elif kind == 'directory':
369
# all data is meta here, nothing specific to directory, so
371
ie.revision = parent_entry.revision
372
return self._get_delta(ie, basis_inv, path), False
374
self._add_text_to_weave(ie.file_id, lines, heads, None)
375
elif kind == 'symlink':
376
current_link_target = content_summary[3]
378
# symlink target is not generic metadata, check if it has
380
if current_link_target != parent_entry.symlink_target:
383
# unchanged, carry over.
384
ie.revision = parent_entry.revision
385
ie.symlink_target = parent_entry.symlink_target
386
return self._get_delta(ie, basis_inv, path), False
387
ie.symlink_target = current_link_target
389
self._add_text_to_weave(ie.file_id, lines, heads, None)
390
elif kind == 'tree-reference':
392
if content_summary[3] != parent_entry.reference_revision:
395
# unchanged, carry over.
396
ie.reference_revision = parent_entry.reference_revision
397
ie.revision = parent_entry.revision
398
return self._get_delta(ie, basis_inv, path), False
399
ie.reference_revision = content_summary[3]
401
self._add_text_to_weave(ie.file_id, lines, heads, None)
403
raise NotImplementedError('unknown kind')
404
ie.revision = self._new_revision_id
405
return self._get_delta(ie, basis_inv, path), True
407
def _add_text_to_weave(self, file_id, new_lines, parents, nostore_sha):
338
408
versionedfile = self.repository.weave_store.get_weave_or_empty(
339
409
file_id, self.repository.get_transaction())
340
410
# Don't change this to add_lines - add_lines_with_ghosts is cheaper