759
759
return self.sourcedir.get(self.path)
761
761
def ignore_file(self):
762
if type(self.content) is tuple:
763
content = self.content[1]
765
content = self.content
762
766
self.ui.output_log(
763
4, __name__, 'Ignoring %s %r' % (self.content.kind, self.path))
767
4, __name__, 'Ignoring %s %r' % (content.kind, self.path))
766
770
class StreamedAction(Action):
782
786
return BufferedFile(self.generator, content.length, self.ui)
784
788
def ignore_file(self):
789
if type(self.content) is tuple:
790
content = self.content[1]
792
content = self.content
785
793
self.ui.output_log(
786
4, __name__, 'Ignoring %s %r' % (self.content.kind, self.path))
787
self.get_file().close()
794
4, __name__, 'Ignoring %s %r' % (content.kind, self.path))
795
if self.type != 'del':
796
self.get_file().close()
790
799
class BufferedFile(object):
883
891
# TODO: make this more efficient: but wait till its shown to be a
884
892
# key issue to address.
885
893
some_bytes = self._next_bytes(4096)
886
896
tokens = some_bytes.split('\x00')
888
898
action = tokens[1]
890
900
if action in ('new', 'del'):
891
kind_data, pos = self.parse_kind_data(tokens)
901
kind_data, pos = self.parse_kind_data(tokens, 2)
892
902
elif action == 'replace':
893
kind_data1, pos = self.parse_kind_data(tokens)
894
kind_data2, pos = self.parse_kind_data(tokens)
903
kind_data1, pos = self.parse_kind_data(tokens, 2)
904
kind_data2, pos = self.parse_kind_data(tokens, pos)
895
905
kind_data = kind_data1, kind_data2
897
907
raise ValueError('unknown action %r' % action)
927
937
content = self._stream.read(65536)
940
class CancellableDelete:
941
"""Group a number of actions and allow them to all be cancelled at once."""
943
def __init__(self, content, contentdir, path, ui):
944
self.cancelled = False
945
self.content = content
946
self.contentdir = contentdir
951
# TODO: we may want to warn or perhaps have a strict mode here.
952
# e.g. handle already deleted things. This should become clear
953
# when recovery mode is done.
956
self.ui.output_log(4, __name__, 'Deleting %s %r' %
957
(self.content.kind, self.path))
959
if self.content.kind != 'dir':
960
self.contentdir.delete(self.path)
962
self.contentdir.rmdir(self.path)
963
except errors.NoSuchFile:
964
# Already gone, ignore it.
930
968
class TransportReplay(object):
931
969
"""Replay a journal reading content from a transport.
978
1016
if action == 'replace':
979
1017
# TODO: (again, to do with replacing files with dirs:)
980
1018
# do not delay creating dirs needed for files
982
to_rename.append(self.put_with_check(path, content[1],
984
to_delete.append((path, content[0]))
1019
# below them, or create the files in the temp
1021
cancellable = CancellableDelete(
1022
content[0], self.contentdir, path, self.ui)
1024
self.put_with_check(path, content[1], action_obj,
1026
to_delete.append(cancellable)
985
1027
if action == 'del':
986
to_delete.append((path, content))
987
for path, content in to_delete:
1028
cancellable = CancellableDelete(
1029
content, self.contentdir, path, self.ui)
1030
to_delete.append(cancellable)
1031
for cancellable in to_delete:
988
1032
# Second pass on the group to handle deletes as late as possible
989
# TODO: we may want to warn or perhaps have a strict mode here.
990
# e.g. handle already deleted things. This should become clear
991
# when recovery mode is done.
992
self.ui.output_log(4, __name__, 'Deleting %s %r' %
993
(content.kind, path))
995
if content.kind != 'dir':
996
self.contentdir.delete(path)
998
self.contentdir.rmdir(path)
999
except errors.NoSuchFile:
1000
# Already gone, ignore it.
1033
cancellable.delete()
1003
1035
for doit in to_rename:
1064
def put_with_check(self, path, content, action):
1096
def put_with_check(self, path, content, action, cancellable=None):
1065
1097
"""Put a_file at path checking that as received it matches content.
1067
1099
:param path: A relpath.
1068
1100
:param content: A content description of a file.
1069
1101
:param action: An action object which can supply file content.
1102
:param cancellable: A Cancellable object to use when the content is
1103
already present locally.
1070
1104
:return: A callable that will execute the rename-into-place - all the
1071
1105
IO has been done before returning.
1084
1118
if self.check_file(path, content):
1085
1119
action.ignore_file()
1121
cancellable.cancelled = True
1086
1122
return lambda:None
1087
1123
except (ValueError, IOError):
1088
1124
# If we can't read the file for some reason, we obviously need to