mirror of
https://github.com/borgbackup/borg.git
synced 2026-10-12 02:11:36 +00:00
extract --continue: skip complete dirs, count skipped files in --progress, refs #6857
Directories: same_item() already considered an existing directory with the archived mode and mtime as complete, but do_extract never passed continue_extraction when restoring directory metadata, so all directories got their metadata restored again. --progress: the total includes the files that --continue skips, but their size was never added to the progress, so the percentage stayed far below the real one. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
c4c067e0f5
commit
10bbecb90f
@@ -1031,6 +1031,8 @@ Duration: {0.duration}
|
||||
st = os.stat(path, follow_symlinks=False)
|
||||
if continue_extraction and same_item(item, st):
|
||||
# we already have fully extracted this file in a previous run.
|
||||
if pi:
|
||||
pi.show(increase=item.get_size(), info=[remove_surrogates(item.path)])
|
||||
if "hlid" not in item or not has_link:
|
||||
return # done!
|
||||
# it is part of a group of hard links, keep the group together:
|
||||
|
||||
@@ -88,7 +88,7 @@ class ExtractMixIn:
|
||||
while dirs and not item.path.startswith(dirs[-1].path):
|
||||
dir_item = dirs.pop(-1)
|
||||
try:
|
||||
archive.extract_item(dir_item, stdout=stdout)
|
||||
archive.extract_item(dir_item, stdout=stdout, continue_extraction=continue_extraction)
|
||||
except BackupError as e:
|
||||
self.print_warning_instance(BackupWarning(remove_surrogates(dir_item.path), e))
|
||||
|
||||
@@ -122,7 +122,7 @@ class ExtractMixIn:
|
||||
pi.show()
|
||||
dir_item = dirs.pop(-1)
|
||||
try:
|
||||
archive.extract_item(dir_item, stdout=stdout)
|
||||
archive.extract_item(dir_item, stdout=stdout, continue_extraction=continue_extraction)
|
||||
except BackupError as e:
|
||||
self.print_warning_instance(BackupWarning(remove_surrogates(dir_item.path), e))
|
||||
for pattern in matcher.get_unmatched_include_patterns():
|
||||
@@ -170,8 +170,10 @@ class ExtractMixIn:
|
||||
``--continue`` extracts into a non-empty directory. It is made for continuing a previously
|
||||
interrupted extraction of the same archive into the same directory: an existing regular
|
||||
file that has the same type, permissions (mode), size and modification time as the
|
||||
archived file is considered to be fully extracted already and is skipped. Everything else
|
||||
is extracted, replacing existing files. Files that are in the directory, but not in the
|
||||
archived file is considered to be fully extracted already and is skipped. Likewise, the
|
||||
metadata of an existing directory that has the same permissions (mode) and modification
|
||||
time as the archived directory is not restored again. Everything else is extracted,
|
||||
replacing existing files. Files that are in the directory, but not in the
|
||||
archive, are left as they are. ``--continue`` is thus also needed to restore files into an
|
||||
existing directory tree. Note that a file that was damaged without a change of its size
|
||||
and modification time (e.g. by bit rot) is skipped, not replaced: remove it before
|
||||
|
||||
@@ -1035,6 +1035,7 @@ def test_extract_continue(archivers, request):
|
||||
assert file3_st.st_mtime_ns == new_file3_st.st_mtime_ns # file3 was extracted again
|
||||
# windows has a strange ctime behaviour when deleting and recreating a file
|
||||
if not is_win32:
|
||||
assert dir1_st.st_ctime_ns == now_dir1_st.st_ctime_ns # dir1 metadata not restored again
|
||||
assert file1_st.st_ctime_ns == now_file1_st.st_ctime_ns # file not extracted again
|
||||
assert file2_st.st_ctime_ns != new_file2_st.st_ctime_ns # file extracted again
|
||||
assert file3_st.st_ctime_ns != new_file3_st.st_ctime_ns # file extracted again
|
||||
@@ -1047,6 +1048,22 @@ def test_extract_continue(archivers, request):
|
||||
assert f.read() == CONTENTS3
|
||||
|
||||
|
||||
def test_extract_continue_progress(archivers, request, monkeypatch):
|
||||
# --progress must count the files that --continue skips as extracted.
|
||||
archiver = request.getfixturevalue(archivers)
|
||||
monkeypatch.setenv("BORG_PROGRESS_FPS", "1000000") # do not rate limit the progress output
|
||||
cmd(archiver, "repo-create", RK_ENCRYPTION)
|
||||
create_regular_file(archiver.input_path, "file1", size=900 * 1024)
|
||||
create_regular_file(archiver.input_path, "file2", size=100 * 1024)
|
||||
cmd(archiver, "create", "arch", "input/file1", "input/file2") # extract file1 before file2
|
||||
with changedir("output"):
|
||||
cmd(archiver, "extract", "arch")
|
||||
os.truncate("input/file2", 123) # file1 is fully extracted, file2 is not
|
||||
output = cmd(archiver, "extract", "arch", "--continue", "--progress")
|
||||
# file2 has a single chunk, its extraction starts after the skipped 90% of the data in file1:
|
||||
assert " 90.0% Extracting: input/file2" in output
|
||||
|
||||
|
||||
@requires_hardlinks
|
||||
@pytest.mark.parametrize("missing", ["first", "last"])
|
||||
def test_extract_continue_hardlinks(archivers, request, missing):
|
||||
|
||||
Reference in New Issue
Block a user