Repository navigation
worktree: use ReFS block cloning for unchanged files #6424
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
22bc067
9940edd
9095992
4cbcb87
cdb2b1a
22988a1
50ede48
2107368
ecfc290
484f7b7
60cb518
ff7e710
d078954
6723784
3c97d72
aac02f0
7de068d
979e245
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -33,9 +33,60 @@ | |
| #include <winternl.h> | ||
|
|
||
| #define STATUS_DELETE_PENDING ((NTSTATUS) 0xC0000056) | ||
|
|
||
| #define HCAST(type, handle) ((type)(intptr_t)handle) | ||
|
|
||
| int mingw_file_copy_on_write(int dst_fd, int src_fd, off_t size) | ||
| { | ||
| DUPLICATE_EXTENTS_DATA data = { | ||
| .FileHandle = HCAST(HANDLE, _get_osfhandle(src_fd)), | ||
| }; | ||
| HANDLE dst = HCAST(HANDLE, _get_osfhandle(dst_fd)); | ||
| /* | ||
| * FSCTL_DUPLICATE_EXTENTS_TO_FILE accepts ranges smaller than 4 GiB, | ||
| * aligned to the volume's cluster size. ReFS uses 64 KiB or 4 KiB | ||
| * clusters, so this is the largest valid range for either. | ||
| */ | ||
|
Comment on lines
+44
to
+48
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So you try 1GiB chunks until hitting EOF, and then immediately drop to 64KiB chunks? So if you block-clone a 1GiB-1 file, you chunk it in fine-grained 64KiB chunks? That sounds super inefficient. I don't know the first thing about block-cloning, but I would have expected multiples of the cluster size to be valid values for I wouldn't know because you totally failed on writing a useful commit message. Out of the "context, intent, justification, implementation unless obvious from the diff" parts, I miss pretty much all of them. Fix that via an
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Addressed in 50ede48. Full requests now use |
||
| const LONGLONG max_range = (4LL * 1024 * 1024 * 1024) - (64 * 1024); | ||
| const LONGLONG tail_alignments[] = { 64 * 1024, 4 * 1024 }; | ||
| DWORD bytes_returned; | ||
| size_t i; | ||
|
|
||
| /* The destination range must exist before extents can be cloned. */ | ||
| if (ftruncate(dst_fd, size) < 0) | ||
| return -1; | ||
| if (!size) | ||
| return 0; | ||
|
|
||
| while (data.SourceFileOffset.QuadPart + max_range < size) { | ||
| data.ByteCount.QuadPart = max_range; | ||
| if (!DeviceIoControl(dst, FSCTL_DUPLICATE_EXTENTS_TO_FILE, | ||
| &data, sizeof(data), NULL, 0, | ||
| &bytes_returned, NULL)) { | ||
| errno = ENOSYS; | ||
| return -1; | ||
| } | ||
| data.SourceFileOffset.QuadPart += data.ByteCount.QuadPart; | ||
| data.TargetFileOffset = data.SourceFileOffset; | ||
| } | ||
|
|
||
| for (i = 0; i < ARRAY_SIZE(tail_alignments); i++) { | ||
| LONGLONG remaining = size - data.SourceFileOffset.QuadPart; | ||
|
|
||
| data.ByteCount.QuadPart = | ||
| DIV_ROUND_UP(remaining, tail_alignments[i]) * | ||
| tail_alignments[i]; | ||
| if (DeviceIoControl(dst, FSCTL_DUPLICATE_EXTENTS_TO_FILE, | ||
| &data, sizeof(data), NULL, 0, | ||
| &bytes_returned, NULL)) { | ||
| /* Discard any allocation-unit padding in the tail. */ | ||
| return ftruncate(dst_fd, size); | ||
| } | ||
| } | ||
|
|
||
| errno = ENOSYS; | ||
| return -1; | ||
| } | ||
|
|
||
| void open_in_gdb(void) | ||
| { | ||
| static struct child_process cp = CHILD_PROCESS_INIT; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is unlikely to be enough. A repository might be bare, after all. And even if it is not bare, you need to have a look at the Git index to see which files are eligible for block cloning. It might actually be quite prohibitive to provide a list of those files to the
resetchild process. A better idea would probably to make the checks here whether it is a non-bare repository, to refresh the Git index if so, and most crucially, to record a timestamp. That way, the child process could just look up the file in the Git index, and if it is up to date, block clone it. That would also resolve the issue where you needlessly exclude filtered files from block-cloning (which, in the case of Git LFS, would hurt particularly much).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed in 484f7b7. Bare repositories omit source metadata; non-bare sources record time before a gentle index refresh/write and pass the source index to
reset. Checkout validates matching index/stat/timestamp state, allowing clean converted and LFS-managed files without hashing their working-tree bytes.