Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
8240e9a
Initial plan
Copilot Sep 18, 2026
fa12f59
compat: add a block-cloning primitive
Copilot Sep 18, 2026
5161d81
worktree: clone unchanged files on capable volumes
Copilot Sep 18, 2026
f9c8abd
fixup! compat: add a block-cloning primitive
Copilot Sep 18, 2026
251825a
fixup! compat: add a block-cloning primitive
Copilot Sep 18, 2026
dfc3a3e
fixup! worktree: clone unchanged files on capable volumes
Copilot Sep 18, 2026
55c7a59
amend! compat: add a block-cloning primitive
Copilot Sep 18, 2026
8ddd5b9
amend! compat: add a block-cloning primitive
Copilot Sep 18, 2026
80913d5
amend! compat: add a block-cloning primitive
Copilot Sep 18, 2026
7a91083
amend! worktree: clone unchanged files on capable volumes
Copilot Sep 18, 2026
0deaaee
fixup! compat: add a block-cloning primitive
Copilot Sep 18, 2026
e34ff82
fixup! worktree: clone unchanged files on capable volumes
Copilot Sep 18, 2026
6de9e4e
fixup! worktree: clone unchanged files on capable volumes
Copilot Sep 18, 2026
8c436fa
fixup! worktree: clone unchanged files on capable volumes
Copilot Sep 18, 2026
700730d
fixup! worktree: clone unchanged files on capable volumes
Copilot Sep 18, 2026
78b53c4
fixup! worktree: clone unchanged files on capable volumes
Copilot Sep 18, 2026
9d6b7dd
fixup! worktree: clone unchanged files on capable volumes
Copilot Sep 18, 2026
e1d8f6a
fixup! worktree: clone unchanged files on capable volumes
Copilot Sep 18, 2026
d4127a9
files which needs conversion are skipped from cloning
qub1n Oct 3, 2026
5f75884
path of index needs be absolute to point old index so it can resolve …
qub1n Oct 3, 2026
422ce68
Test writes cloned blocks to output for later evaluation, CRLF are no…
qub1n Oct 3, 2026
bcd0e1f
significant performance improvement - cloned files are not prefilled …
qub1n Oct 4, 2026
a93d90e
ReFS alignment is cached for given drive, it also solves problem if f…
qub1n Oct 4, 2026
8d84c23
Removal of useless allocation padding which hurt performance. It is n…
qub1n Oct 4, 2026
0b65b8c
optimized cache lookup function without tolower() ans simplified most…
qub1n Oct 4, 2026
69712da
performance optimization, minimizing system calls
qub1n Oct 4, 2026
9aac596
performance optimization - minimizing system calls, some checks are s…
qub1n Oct 5, 2026
93e3568
cloning is done only for files over 160KB by default, last unaligned …
qub1n Oct 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Documentation/config/worktree.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
fails. If no such match can be found, it falls back to
creating a new branch from the current `HEAD`.

`worktree.copyOnWrite`::
When creating a linked worktree, use copy-on-write file copies when
supported by the platform and file system. Setting this to `false`
disables the optimization. Defaults to `true`.

`worktree.useRelativePaths`::
Link worktrees using relative paths (when "`true`") or absolute
paths (when "`false`"). This is particularly useful for setups
Expand Down
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -819,6 +819,7 @@ TEST_BUILTINS_OBJS += test-bundle-uri.o
TEST_BUILTINS_OBJS += test-cache-tree.o
TEST_BUILTINS_OBJS += test-chmtime.o
TEST_BUILTINS_OBJS += test-config.o
TEST_BUILTINS_OBJS += test-copy-on-write.o
TEST_BUILTINS_OBJS += test-crontab.o
TEST_BUILTINS_OBJS += test-csprng.o
TEST_BUILTINS_OBJS += test-date.o
Expand Down
66 changes: 63 additions & 3 deletions builtin/reset.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "refs.h"
#include "diff.h"
#include "diffcore.h"
#include "entry.h"
#include "tree.h"
#include "branch.h"
#include "object-name.h"
Expand Down Expand Up @@ -62,7 +63,8 @@ static inline int is_merge(void)
return !access(git_path_merge_head(the_repository), F_OK);
}

static int reset_index(const char *ref, const struct object_id *oid, int reset_type, int quiet)
static int reset_index(const char *ref, const struct object_id *oid, int reset_type, int quiet,
struct checkout_copy_source *copy_source)
{
int i, nr = 0;
struct tree_desc desc[2];
Expand All @@ -76,6 +78,7 @@ static int reset_index(const char *ref, const struct object_id *oid, int reset_t
opts.dst_index = the_repository->index;
opts.fn = oneway_merge;
opts.merge = 1;
opts.copy_source = copy_source;
init_checkout_metadata(&opts.meta, ref, oid, NULL);
if (!quiet)
opts.verbose_update = 1;
Expand Down Expand Up @@ -336,13 +339,64 @@ static int git_reset_config(const char *var, const char *value,
return git_default_config(var, value, ctx, cb);
}

static int init_copy_source(struct checkout_copy_source *source,
struct index_state *source_index)
{
const char *worktree = getenv(GIT_WORKTREE_COPY_SOURCE);
const char *index_file = getenv(GIT_WORKTREE_COPY_SOURCE_INDEX);
const char *git_dir = getenv(GIT_WORKTREE_COPY_SOURCE_GIT_DIR);
const char *time_string = getenv(GIT_WORKTREE_COPY_SOURCE_TIME);
char *end;
int enabled = 1;
uintmax_t refreshed_at;

if (!copy_on_write_supported ||
!worktree || !index_file || !git_dir || !time_string)
return 0;
if (!repo_config_get_bool(the_repository, "worktree.copyonwrite",
&enabled) && !enabled)
return 0;

errno = 0;
refreshed_at = strtoumax(time_string, &end, 10);
if (errno || *end)
return 0;
if (read_index_from(source_index, index_file, git_dir) < 0)
return 0;
ensure_full_index(source_index);

source->istate = source_index;
source->worktree = worktree;
source->refreshed_at = refreshed_at;
/* The linked worktree is created before reset, so it contains .git. */
{
DIR *dir = opendir(repo_get_work_tree(the_repository));
struct dirent *de;

if (dir) {
source->fresh_worktree = 1;
while ((de = readdir_skip_dot_and_dotdot(dir)) != NULL)
if (strcmp(de->d_name, ".git")) {
source->fresh_worktree = 0;
break;
}
closedir(dir);
}
}
return 1;
}

int cmd_reset(int argc,
const char **argv,
const char *prefix,
struct repository *repo UNUSED)
{
int reset_type = NONE, update_ref_status = 0, quiet = 0;
int no_refresh = 0;
struct index_state copy_source_index =
INDEX_STATE_INIT(the_repository);
struct checkout_copy_source copy_source = { 0 };
struct checkout_copy_source *copy_source_ptr = NULL;
int patch_mode = 0, pathspec_file_nul = 0, unborn;
const char *rev;
char *pathspec_from_file = NULL;
Expand Down Expand Up @@ -495,6 +549,9 @@ int cmd_reset(int argc,

if (repo_read_index(the_repository) < 0)
die(_("index file corrupt"));
if (reset_type == HARD &&
init_copy_source(&copy_source, &copy_source_index))
copy_source_ptr = &copy_source;

/* Soft reset does not touch the index file nor the working tree
* at all, but requires them in a good order. Other resets reset
Expand Down Expand Up @@ -535,9 +592,11 @@ int cmd_reset(int argc,
if (ref && !starts_with(ref, "refs/"))
FREE_AND_NULL(ref);

err = reset_index(ref, &oid, reset_type, quiet);
err = reset_index(ref, &oid, reset_type, quiet,
copy_source_ptr);
if (reset_type == KEEP && !err)
err = reset_index(ref, &oid, MIXED, quiet);
err = reset_index(ref, &oid, MIXED, quiet,
copy_source_ptr);
if (err)
die(_("Could not reset index file to revision '%s'."), rev);
free(ref);
Expand All @@ -561,6 +620,7 @@ int cmd_reset(int argc,
discard_index(the_repository->index);

cleanup:
release_index(&copy_source_index);
clear_pathspec(&pathspec);
free(pathspec_from_file);
return update_ref_status;
Expand Down
25 changes: 23 additions & 2 deletions builtin/worktree.c
Original file line number Diff line number Diff line change
Expand Up @@ -395,14 +395,35 @@ static void copy_filtered_worktree_config(const char *worktree_git_dir)
}

static int checkout_worktree(const struct add_opts *opts,
struct strvec *child_env)
struct strvec *child_env, const char *path)
{
struct child_process cp = CHILD_PROCESS_INIT;
const char *source = repo_get_work_tree(the_repository);
struct timeval refreshed_at;

cp.git_cmd = 1;
strvec_pushl(&cp.args, "reset", "--hard", "--no-recurse-submodules", NULL);
if (opts->quiet)
strvec_push(&cp.args, "--quiet");
strvec_pushv(&cp.env, child_env->v);
if (file_copy_on_write_supported(path) && source &&
repo_read_index(the_repository) >= 0) {
gettimeofday(&refreshed_at, NULL);

strvec_pushf(&cp.env, "%s=%s",
GIT_WORKTREE_COPY_SOURCE, source);
strvec_pushf(&cp.env, "%s=%s",
GIT_WORKTREE_COPY_SOURCE_INDEX,
absolute_path(repo_get_index_file(the_repository)));
strvec_pushf(&cp.env, "%s=%s",
GIT_WORKTREE_COPY_SOURCE_GIT_DIR,
absolute_path(repo_get_git_dir(the_repository)));
strvec_pushf(&cp.env, "%s=%"PRIuMAX,
GIT_WORKTREE_COPY_SOURCE_TIME,
(uintmax_t)refreshed_at.tv_sec *
1000000000 +
refreshed_at.tv_usec * 1000);
}
return run_command(&cp);
}

Expand Down Expand Up @@ -589,7 +610,7 @@ static int add_worktree(const char *path, const char *refname,
goto done;

if (opts->checkout &&
(ret = checkout_worktree(opts, &child_env)))
(ret = checkout_worktree(opts, &child_env, path)))
goto done;

is_junk = 0;
Expand Down
Loading