зеркало из https://github.com/microsoft/git.git
Merge branch 'sparse-index-stuff'
This is random stuff that probably all got upstream in the meantime.
This commit is contained in:
Коммит
4cee542123
|
@ -1,3 +1,9 @@
|
|||
index.deleteSparseDirectories::
|
||||
When enabled, the cone mode sparse-checkout feature will delete
|
||||
directories that are outside of the sparse-checkout cone, unless
|
||||
such a directory contains an untracked, non-ignored file. Defaults
|
||||
to true.
|
||||
|
||||
index.recordEndOfIndexEntries::
|
||||
Specifies whether the index file should include an "End Of Index
|
||||
Entry" section. This reduces index load time on multiprocessor
|
||||
|
|
|
@ -47,6 +47,7 @@ static int chmod_pathspec(struct pathspec *pathspec, char flip, int show_only)
|
|||
int err;
|
||||
|
||||
if (!include_sparse &&
|
||||
!core_virtualfilesystem &&
|
||||
(ce_skip_worktree(ce) ||
|
||||
!path_in_sparse_checkout(ce->name, &the_index)))
|
||||
continue;
|
||||
|
@ -97,7 +98,8 @@ static void update_callback(struct diff_queue_struct *q,
|
|||
struct diff_filepair *p = q->queue[i];
|
||||
const char *path = p->one->path;
|
||||
|
||||
if (!include_sparse && !path_in_sparse_checkout(path, &the_index))
|
||||
if (!include_sparse && !core_virtualfilesystem &&
|
||||
!path_in_sparse_checkout(path, &the_index))
|
||||
continue;
|
||||
|
||||
switch (fix_unmerged_status(p, data)) {
|
||||
|
@ -215,8 +217,9 @@ static int refresh(int verbose, const struct pathspec *pathspec)
|
|||
if (!seen[i]) {
|
||||
const char *path = pathspec->items[i].original;
|
||||
|
||||
if (matches_skip_worktree(pathspec, i, &skip_worktree_seen) ||
|
||||
!path_in_sparse_checkout(path, &the_index)) {
|
||||
if (!core_virtualfilesystem &&
|
||||
(matches_skip_worktree(pathspec, i, &skip_worktree_seen) ||
|
||||
!path_in_sparse_checkout(path, &the_index))) {
|
||||
string_list_append(&only_match_skip_worktree,
|
||||
pathspec->items[i].original);
|
||||
} else {
|
||||
|
@ -226,7 +229,11 @@ static int refresh(int verbose, const struct pathspec *pathspec)
|
|||
}
|
||||
}
|
||||
|
||||
if (only_match_skip_worktree.nr) {
|
||||
/*
|
||||
* When using a virtual filesystem, we might re-add a path
|
||||
* that is currently virtual and we want that to succeed.
|
||||
*/
|
||||
if (!core_virtualfilesystem && only_match_skip_worktree.nr) {
|
||||
advise_on_updating_sparse_paths(&only_match_skip_worktree);
|
||||
ret = 1;
|
||||
}
|
||||
|
@ -648,7 +655,11 @@ int cmd_add(int argc, const char **argv, const char *prefix)
|
|||
if (seen[i])
|
||||
continue;
|
||||
|
||||
if (!include_sparse &&
|
||||
/*
|
||||
* When using a virtual filesystem, we might re-add a path
|
||||
* that is currently virtual and we want that to succeed.
|
||||
*/
|
||||
if (!include_sparse && !core_virtualfilesystem &&
|
||||
matches_skip_worktree(&pathspec, i, &skip_worktree_seen)) {
|
||||
string_list_append(&only_match_skip_worktree,
|
||||
pathspec.items[i].original);
|
||||
|
@ -672,7 +683,6 @@ int cmd_add(int argc, const char **argv, const char *prefix)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
if (only_match_skip_worktree.nr) {
|
||||
advise_on_updating_sparse_paths(&only_match_skip_worktree);
|
||||
exit_status = 1;
|
||||
|
|
|
@ -1614,7 +1614,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
|
|||
if (state->quiet)
|
||||
o.verbosity = 0;
|
||||
|
||||
if (merge_recursive_generic(&o, &our_tree, &their_tree, 1, bases, &result)) {
|
||||
if (merge_recursive_generic(&o, &our_tree, &their_tree, 1, bases, merge_recursive, &result)) {
|
||||
repo_rerere(the_repository, state->allow_rerere_autoupdate);
|
||||
free(their_tree_name);
|
||||
return error(_("Failed to merge in the changes."));
|
||||
|
|
21
builtin/gc.c
21
builtin/gc.c
|
@ -1276,6 +1276,8 @@ static int maintenance_run_tasks(struct maintenance_run_opts *opts)
|
|||
char *lock_path = xstrfmt("%s/maintenance", r->objects->odb->path);
|
||||
|
||||
if (hold_lock_file_for_update(&lk, lock_path, LOCK_NO_DEREF) < 0) {
|
||||
struct stat st;
|
||||
struct strbuf lock_dot_lock = STRBUF_INIT;
|
||||
/*
|
||||
* Another maintenance command is running.
|
||||
*
|
||||
|
@ -1286,6 +1288,25 @@ static int maintenance_run_tasks(struct maintenance_run_opts *opts)
|
|||
if (!opts->auto_flag && !opts->quiet)
|
||||
warning(_("lock file '%s' exists, skipping maintenance"),
|
||||
lock_path);
|
||||
|
||||
/*
|
||||
* Check timestamp on .lock file to see if we should
|
||||
* delete it to recover from a fail state.
|
||||
*/
|
||||
strbuf_addstr(&lock_dot_lock, lock_path);
|
||||
strbuf_addstr(&lock_dot_lock, ".lock");
|
||||
if (lstat(lock_dot_lock.buf, &st))
|
||||
warning_errno(_("unable to stat '%s'"), lock_dot_lock.buf);
|
||||
else {
|
||||
if (st.st_mtime < time(NULL) - (6 * 60 * 60)) {
|
||||
if (unlink(lock_dot_lock.buf))
|
||||
warning_errno(_("unable to delete stale lock file"));
|
||||
else
|
||||
warning(_("deleted stale lock file"));
|
||||
}
|
||||
}
|
||||
|
||||
strbuf_release(&lock_dot_lock);
|
||||
free(lock_path);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -669,6 +669,11 @@ int cmd_show(int argc, const char **argv, const char *prefix)
|
|||
init_log_defaults();
|
||||
git_config(git_log_config, NULL);
|
||||
|
||||
if (the_repository->gitdir) {
|
||||
prepare_repo_settings(the_repository);
|
||||
the_repository->settings.command_requires_full_index = 0;
|
||||
}
|
||||
|
||||
memset(&match_all, 0, sizeof(match_all));
|
||||
repo_init_revisions(the_repository, &rev, prefix);
|
||||
git_config(grep_config, &rev.grep_filter);
|
||||
|
|
|
@ -81,7 +81,7 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
|
|||
if (o.verbosity >= 3)
|
||||
printf(_("Merging %s with %s\n"), o.branch1, o.branch2);
|
||||
|
||||
failed = merge_recursive_generic(&o, &h1, &h2, bases_count, bases, &result);
|
||||
failed = merge_recursive_generic(&o, &h1, &h2, bases_count, bases, merge_recursive, &result);
|
||||
|
||||
free(better1);
|
||||
free(better2);
|
||||
|
|
|
@ -44,7 +44,7 @@ static char const * const builtin_multi_pack_index_usage[] = {
|
|||
};
|
||||
|
||||
static struct opts_multi_pack_index {
|
||||
const char *object_dir;
|
||||
char *object_dir;
|
||||
const char *preferred_pack;
|
||||
const char *refs_snapshot;
|
||||
unsigned long batch_size;
|
||||
|
@ -52,9 +52,23 @@ static struct opts_multi_pack_index {
|
|||
int stdin_packs;
|
||||
} opts;
|
||||
|
||||
|
||||
static int parse_object_dir(const struct option *opt, const char *arg,
|
||||
int unset)
|
||||
{
|
||||
free(opts.object_dir);
|
||||
if (unset)
|
||||
opts.object_dir = xstrdup(get_object_directory());
|
||||
else
|
||||
opts.object_dir = real_pathdup(arg, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct option common_opts[] = {
|
||||
OPT_FILENAME(0, "object-dir", &opts.object_dir,
|
||||
N_("object directory containing set of packfile and pack-index pairs")),
|
||||
OPT_CALLBACK(0, "object-dir", &opts.object_dir,
|
||||
N_("directory"),
|
||||
N_("object directory containing set of packfile and pack-index pairs"),
|
||||
parse_object_dir),
|
||||
OPT_END(),
|
||||
};
|
||||
|
||||
|
@ -232,31 +246,40 @@ static int cmd_multi_pack_index_repack(int argc, const char **argv)
|
|||
int cmd_multi_pack_index(int argc, const char **argv,
|
||||
const char *prefix)
|
||||
{
|
||||
int res;
|
||||
struct option *builtin_multi_pack_index_options = common_opts;
|
||||
|
||||
git_config(git_default_config, NULL);
|
||||
|
||||
if (the_repository &&
|
||||
the_repository->objects &&
|
||||
the_repository->objects->odb)
|
||||
opts.object_dir = xstrdup(the_repository->objects->odb->path);
|
||||
|
||||
argc = parse_options(argc, argv, prefix,
|
||||
builtin_multi_pack_index_options,
|
||||
builtin_multi_pack_index_usage,
|
||||
PARSE_OPT_STOP_AT_NON_OPTION);
|
||||
|
||||
if (!opts.object_dir)
|
||||
opts.object_dir = get_object_directory();
|
||||
|
||||
if (!argc)
|
||||
goto usage;
|
||||
|
||||
if (!strcmp(argv[0], "repack"))
|
||||
return cmd_multi_pack_index_repack(argc, argv);
|
||||
res = cmd_multi_pack_index_repack(argc, argv);
|
||||
else if (!strcmp(argv[0], "write"))
|
||||
return cmd_multi_pack_index_write(argc, argv);
|
||||
res = cmd_multi_pack_index_write(argc, argv);
|
||||
else if (!strcmp(argv[0], "verify"))
|
||||
return cmd_multi_pack_index_verify(argc, argv);
|
||||
res = cmd_multi_pack_index_verify(argc, argv);
|
||||
else if (!strcmp(argv[0], "expire"))
|
||||
return cmd_multi_pack_index_expire(argc, argv);
|
||||
res = cmd_multi_pack_index_expire(argc, argv);
|
||||
else {
|
||||
error(_("unrecognized subcommand: %s"), argv[0]);
|
||||
goto usage;
|
||||
}
|
||||
|
||||
free(opts.object_dir);
|
||||
return res;
|
||||
|
||||
error(_("unrecognized subcommand: %s"), argv[0]);
|
||||
usage:
|
||||
usage_with_options(builtin_multi_pack_index_usage,
|
||||
builtin_multi_pack_index_options);
|
||||
|
|
|
@ -28,6 +28,8 @@
|
|||
#include "dir.h"
|
||||
#include "strbuf.h"
|
||||
#include "quote.h"
|
||||
#include "dir.h"
|
||||
#include "entry.h"
|
||||
|
||||
#define REFRESH_INDEX_DELAY_WARNING_IN_MS (2 * 1000)
|
||||
|
||||
|
@ -141,9 +143,47 @@ static void update_index_from_diff(struct diff_queue_struct *q,
|
|||
|
||||
for (i = 0; i < q->nr; i++) {
|
||||
int pos;
|
||||
int respect_skip_worktree = 1;
|
||||
struct diff_filespec *one = q->queue[i]->one;
|
||||
struct diff_filespec *two = q->queue[i]->two;
|
||||
int is_in_reset_tree = one->mode && !is_null_oid(&one->oid);
|
||||
int is_missing = !(one->mode && !is_null_oid(&one->oid));
|
||||
int was_missing = !two->mode && is_null_oid(&two->oid);
|
||||
struct cache_entry *ce;
|
||||
struct cache_entry *ceBefore;
|
||||
struct checkout state = CHECKOUT_INIT;
|
||||
|
||||
/*
|
||||
* When using the virtual filesystem feature, the cache entries that are
|
||||
* added here will not have the skip-worktree bit set.
|
||||
*
|
||||
* Without this code there is data that is lost because the files that
|
||||
* would normally be in the working directory are not there and show as
|
||||
* deleted for the next status or in the case of added files just disappear.
|
||||
* We need to create the previous version of the files in the working
|
||||
* directory so that they will have the right content and the next
|
||||
* status call will show modified or untracked files correctly.
|
||||
*/
|
||||
if (core_virtualfilesystem && !file_exists(two->path))
|
||||
{
|
||||
pos = cache_name_pos(two->path, strlen(two->path));
|
||||
if ((pos >= 0 && ce_skip_worktree(active_cache[pos])) &&
|
||||
(is_missing || !was_missing))
|
||||
{
|
||||
state.force = 1;
|
||||
state.refresh_cache = 1;
|
||||
state.istate = &the_index;
|
||||
ceBefore = make_cache_entry(&the_index, two->mode,
|
||||
&two->oid, two->path,
|
||||
0, 0);
|
||||
if (!ceBefore)
|
||||
die(_("make_cache_entry failed for path '%s'"),
|
||||
two->path);
|
||||
|
||||
checkout_entry(ceBefore, &state, NULL, NULL);
|
||||
respect_skip_worktree = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_in_reset_tree && !intent_to_add) {
|
||||
remove_file_from_cache(one->path);
|
||||
|
@ -162,8 +202,14 @@ static void update_index_from_diff(struct diff_queue_struct *q,
|
|||
* to properly construct the reset sparse directory.
|
||||
*/
|
||||
pos = cache_name_pos(one->path, strlen(one->path));
|
||||
if ((pos >= 0 && ce_skip_worktree(active_cache[pos])) ||
|
||||
(pos < 0 && !path_in_sparse_checkout(one->path, &the_index)))
|
||||
|
||||
/*
|
||||
* Do not add the SKIP_WORKTREE bit back if we populated the
|
||||
* file on purpose in a virtual filesystem scenario.
|
||||
*/
|
||||
if (respect_skip_worktree &&
|
||||
((pos >= 0 && ce_skip_worktree(active_cache[pos])) ||
|
||||
(pos < 0 && !path_in_sparse_checkout(one->path, &the_index))))
|
||||
ce->ce_flags |= CE_SKIP_WORKTREE;
|
||||
|
||||
if (!ce)
|
||||
|
|
|
@ -301,7 +301,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
|
|||
for (i = 0; i < active_nr; i++) {
|
||||
const struct cache_entry *ce = active_cache[i];
|
||||
|
||||
if (!include_sparse &&
|
||||
if (!include_sparse && !core_virtualfilesystem &&
|
||||
(ce_skip_worktree(ce) ||
|
||||
!path_in_sparse_checkout(ce->name, &the_index)))
|
||||
continue;
|
||||
|
@ -338,7 +338,11 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
|
|||
*original ? original : ".");
|
||||
}
|
||||
|
||||
if (only_match_skip_worktree.nr) {
|
||||
/*
|
||||
* When using a virtual filesystem, we might re-add a path
|
||||
* that is currently virtual and we want that to succeed.
|
||||
*/
|
||||
if (!core_virtualfilesystem && only_match_skip_worktree.nr) {
|
||||
advise_on_updating_sparse_paths(&only_match_skip_worktree);
|
||||
ret = 1;
|
||||
}
|
||||
|
|
|
@ -106,7 +106,7 @@ static int sparse_checkout_list(int argc, const char **argv)
|
|||
|
||||
static void clean_tracked_sparse_directories(struct repository *r)
|
||||
{
|
||||
int i, was_full = 0;
|
||||
int i, value, was_full = 0;
|
||||
struct strbuf path = STRBUF_INIT;
|
||||
size_t pathlen;
|
||||
struct string_list_item *item;
|
||||
|
@ -122,13 +122,20 @@ static void clean_tracked_sparse_directories(struct repository *r)
|
|||
!r->index->sparse_checkout_patterns->use_cone_patterns)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Users can disable this behavior.
|
||||
*/
|
||||
if (!repo_config_get_bool(r, "index.deletesparsedirectories", &value) &&
|
||||
!value)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Use the sparse index as a data structure to assist finding
|
||||
* directories that are safe to delete. This conversion to a
|
||||
* sparse index will not delete directories that contain
|
||||
* conflicted entries or submodules.
|
||||
*/
|
||||
if (!r->index->sparse_index) {
|
||||
if (r->index->sparse_index == COMPLETELY_FULL) {
|
||||
/*
|
||||
* If something, such as a merge conflict or other concern,
|
||||
* prevents us from converting to a sparse index, then do
|
||||
|
@ -413,6 +420,9 @@ static int update_modes(int *cone_mode, int *sparse_index)
|
|||
/* force an index rewrite */
|
||||
repo_read_index(the_repository);
|
||||
the_repository->index->updated_workdir = 1;
|
||||
|
||||
if (!*sparse_index)
|
||||
ensure_full_index(the_repository->index);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -934,6 +944,9 @@ int cmd_sparse_checkout(int argc, const char **argv, const char *prefix)
|
|||
|
||||
git_config(git_default_config, NULL);
|
||||
|
||||
prepare_repo_settings(the_repository);
|
||||
the_repository->settings.command_requires_full_index = 0;
|
||||
|
||||
if (argc > 0) {
|
||||
if (!strcmp(argv[0], "list"))
|
||||
return sparse_checkout_list(argc, argv);
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "cache-tree.h"
|
||||
#include "unpack-trees.h"
|
||||
#include "merge-recursive.h"
|
||||
#include "merge-ort-wrappers.h"
|
||||
#include "strvec.h"
|
||||
#include "run-command.h"
|
||||
#include "dir.h"
|
||||
|
@ -554,7 +555,7 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,
|
|||
bases[0] = &info->b_tree;
|
||||
|
||||
ret = merge_recursive_generic(&o, &c_tree, &info->w_tree, 1, bases,
|
||||
&result);
|
||||
merge_ort_recursive, &result);
|
||||
if (ret) {
|
||||
rerere(0);
|
||||
|
||||
|
@ -1770,6 +1771,9 @@ int cmd_stash(int argc, const char **argv, const char *prefix)
|
|||
argc = parse_options(argc, argv, prefix, options, git_stash_usage,
|
||||
PARSE_OPT_KEEP_UNKNOWN | PARSE_OPT_KEEP_DASHDASH);
|
||||
|
||||
prepare_repo_settings(the_repository);
|
||||
the_repository->settings.command_requires_full_index = 0;
|
||||
|
||||
index_file = get_index_file();
|
||||
strbuf_addf(&stash_index_path, "%s.stash.%" PRIuMAX, index_file,
|
||||
(uintmax_t)pid);
|
||||
|
|
25
cache-tree.c
25
cache-tree.c
|
@ -102,6 +102,31 @@ struct cache_tree_sub *cache_tree_sub(struct cache_tree *it, const char *path)
|
|||
return find_subtree(it, path, pathlen, 1);
|
||||
}
|
||||
|
||||
struct cache_tree *cache_tree_find_path(struct cache_tree *it, const char *path)
|
||||
{
|
||||
const char *slash;
|
||||
int namelen;
|
||||
struct cache_tree_sub *down;
|
||||
|
||||
if (!it)
|
||||
return NULL;
|
||||
slash = strchrnul(path, '/');
|
||||
namelen = slash - path;
|
||||
it->entry_count = -1;
|
||||
if (!*slash) {
|
||||
int pos;
|
||||
pos = cache_tree_subtree_pos(it, path, namelen);
|
||||
if (0 <= pos) {
|
||||
return it->down[pos]->cache_tree;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
down = find_subtree(it, path, namelen, 0);
|
||||
if (down)
|
||||
return cache_tree_find_path(down->cache_tree, slash + 1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int do_invalidate_path(struct cache_tree *it, const char *path)
|
||||
{
|
||||
/* a/b/c
|
||||
|
|
|
@ -29,6 +29,8 @@ struct cache_tree_sub *cache_tree_sub(struct cache_tree *, const char *);
|
|||
|
||||
int cache_tree_subtree_pos(struct cache_tree *it, const char *path, int pathlen);
|
||||
|
||||
struct cache_tree *cache_tree_find_path(struct cache_tree *it, const char *path);
|
||||
|
||||
void cache_tree_write(struct strbuf *, struct cache_tree *root);
|
||||
struct cache_tree *cache_tree_read(const char *buffer, unsigned long size);
|
||||
|
||||
|
|
34
cache.h
34
cache.h
|
@ -310,6 +310,28 @@ struct untracked_cache;
|
|||
struct progress;
|
||||
struct pattern_list;
|
||||
|
||||
enum sparse_index_mode {
|
||||
/*
|
||||
* COMPLETELY_FULL: there are no sparse directories
|
||||
* in the index at all.
|
||||
*/
|
||||
COMPLETELY_FULL = 0,
|
||||
|
||||
/*
|
||||
* COLLAPSED: the index has already been collapsed to sparse
|
||||
* directories whereever possible.
|
||||
*/
|
||||
COLLAPSED = 1,
|
||||
|
||||
/*
|
||||
* PARTIALLY_SPARSE: the sparse directories that exist are
|
||||
* outside the sparse-checkout boundary, but it is possible
|
||||
* that some file entries could collapse to sparse directory
|
||||
* entries.
|
||||
*/
|
||||
PARTIALLY_SPARSE = 2,
|
||||
};
|
||||
|
||||
struct index_state {
|
||||
struct cache_entry **cache;
|
||||
unsigned int version;
|
||||
|
@ -323,14 +345,8 @@ struct index_state {
|
|||
drop_cache_tree : 1,
|
||||
updated_workdir : 1,
|
||||
updated_skipworktree : 1,
|
||||
fsmonitor_has_run_once : 1,
|
||||
|
||||
/*
|
||||
* sparse_index == 1 when sparse-directory
|
||||
* entries exist. Requires sparse-checkout
|
||||
* in cone mode.
|
||||
*/
|
||||
sparse_index : 1;
|
||||
fsmonitor_has_run_once : 1;
|
||||
enum sparse_index_mode sparse_index;
|
||||
struct hashmap name_hash;
|
||||
struct hashmap dir_hash;
|
||||
struct object_id oid;
|
||||
|
@ -566,7 +582,7 @@ extern char *git_work_tree_cfg;
|
|||
int is_inside_work_tree(void);
|
||||
const char *get_git_dir(void);
|
||||
const char *get_git_common_dir(void);
|
||||
char *get_object_directory(void);
|
||||
const char *get_object_directory(void);
|
||||
char *get_index_file(void);
|
||||
char *get_graft_file(struct repository *r);
|
||||
void set_git_dir(const char *path, int make_realpath);
|
||||
|
|
7
diff.c
7
diff.c
|
@ -3893,6 +3893,13 @@ static int reuse_worktree_file(struct index_state *istate,
|
|||
if (!FAST_WORKING_DIRECTORY && !want_file && has_object_pack(oid))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* If this path does not match our sparse-checkout definition,
|
||||
* then the file will not be in the working directory.
|
||||
*/
|
||||
if (!path_in_sparse_checkout(name, istate))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Similarly, if we'd have to convert the file contents anyway, that
|
||||
* makes the optimization not worthwhile.
|
||||
|
|
85
dir.c
85
dir.c
|
@ -1400,46 +1400,16 @@ static struct path_pattern *last_matching_pattern_from_list(const char *pathname
|
|||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Scan the list of patterns to determine if the ordered list
|
||||
* of patterns matches on 'pathname'.
|
||||
*
|
||||
* Return 1 for a match, 0 for not matched and -1 for undecided.
|
||||
*/
|
||||
enum pattern_match_result path_matches_pattern_list(
|
||||
enum pattern_match_result path_matches_cone_mode_pattern_list(
|
||||
const char *pathname, int pathlen,
|
||||
const char *basename, int *dtype,
|
||||
struct pattern_list *pl,
|
||||
struct index_state *istate)
|
||||
struct pattern_list *pl)
|
||||
{
|
||||
struct path_pattern *pattern;
|
||||
struct strbuf parent_pathname = STRBUF_INIT;
|
||||
int result = NOT_MATCHED;
|
||||
size_t slash_pos;
|
||||
|
||||
/*
|
||||
* The virtual file system data is used to prevent git from traversing
|
||||
* any part of the tree that is not in the virtual file system. Return
|
||||
* 1 to exclude the entry if it is not found in the virtual file system,
|
||||
* else fall through to the regular excludes logic as it may further exclude.
|
||||
*/
|
||||
if (*dtype == DT_UNKNOWN)
|
||||
*dtype = resolve_dtype(DT_UNKNOWN, istate, pathname, pathlen);
|
||||
if (is_excluded_from_virtualfilesystem(pathname, pathlen, *dtype) > 0)
|
||||
return 1;
|
||||
|
||||
if (!pl->use_cone_patterns) {
|
||||
pattern = last_matching_pattern_from_list(pathname, pathlen, basename,
|
||||
dtype, pl, istate);
|
||||
if (pattern) {
|
||||
if (pattern->flags & PATTERN_FLAG_NEGATIVE)
|
||||
return NOT_MATCHED;
|
||||
else
|
||||
return MATCHED;
|
||||
}
|
||||
|
||||
return UNDECIDED;
|
||||
}
|
||||
if (!pl->use_cone_patterns)
|
||||
BUG("path_matches_cone_mode_pattern_list requires cone mode patterns");
|
||||
|
||||
if (pl->full_cone)
|
||||
return MATCHED;
|
||||
|
@ -1492,6 +1462,46 @@ done:
|
|||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Scan the list of patterns to determine if the ordered list
|
||||
* of patterns matches on 'pathname'.
|
||||
*
|
||||
* Return 1 for a match, 0 for not matched and -1 for undecided.
|
||||
*/
|
||||
enum pattern_match_result path_matches_pattern_list(
|
||||
const char *pathname, int pathlen,
|
||||
const char *basename, int *dtype,
|
||||
struct pattern_list *pl,
|
||||
struct index_state *istate)
|
||||
{
|
||||
/*
|
||||
* The virtual file system data is used to prevent git from traversing
|
||||
* any part of the tree that is not in the virtual file system. Return
|
||||
* 1 to exclude the entry if it is not found in the virtual file system,
|
||||
* else fall through to the regular excludes logic as it may further exclude.
|
||||
*/
|
||||
if (*dtype == DT_UNKNOWN)
|
||||
*dtype = resolve_dtype(DT_UNKNOWN, istate, pathname, pathlen);
|
||||
if (is_excluded_from_virtualfilesystem(pathname, pathlen, *dtype) > 0)
|
||||
return 1;
|
||||
|
||||
if (!pl->use_cone_patterns) {
|
||||
struct path_pattern *pattern = last_matching_pattern_from_list(
|
||||
pathname, pathlen, basename,
|
||||
dtype, pl, istate);
|
||||
if (pattern) {
|
||||
if (pattern->flags & PATTERN_FLAG_NEGATIVE)
|
||||
return NOT_MATCHED;
|
||||
else
|
||||
return MATCHED;
|
||||
}
|
||||
|
||||
return UNDECIDED;
|
||||
}
|
||||
|
||||
return path_matches_cone_mode_pattern_list(pathname, pathlen, pl);
|
||||
}
|
||||
|
||||
int init_sparse_checkout_patterns(struct index_state *istate)
|
||||
{
|
||||
if (!core_apply_sparse_checkout)
|
||||
|
@ -1517,6 +1527,13 @@ static int path_in_sparse_checkout_1(const char *path,
|
|||
enum pattern_match_result match = UNDECIDED;
|
||||
const char *end, *slash;
|
||||
|
||||
/*
|
||||
* When using a virtual filesystem, there aren't really patterns
|
||||
* to follow, but be extra careful to skip this check.
|
||||
*/
|
||||
if (core_virtualfilesystem)
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* We default to accepting a path if the path is empty, there are no
|
||||
* patterns, or the patterns are of the wrong type.
|
||||
|
|
9
dir.h
9
dir.h
|
@ -383,6 +383,15 @@ enum pattern_match_result {
|
|||
MATCHED_RECURSIVE = 2,
|
||||
};
|
||||
|
||||
/*
|
||||
* Test if a given path is contained in the given pattern list.
|
||||
*
|
||||
* The given pattern list _must_ use cone mode patterns.
|
||||
*/
|
||||
enum pattern_match_result path_matches_cone_mode_pattern_list(
|
||||
const char *pathname, int pathlen,
|
||||
struct pattern_list *pl);
|
||||
|
||||
/*
|
||||
* Scan the list of patterns to determine if the ordered list
|
||||
* of patterns matches on 'pathname'.
|
||||
|
|
|
@ -279,7 +279,7 @@ const char *get_git_work_tree(void)
|
|||
return the_repository->worktree;
|
||||
}
|
||||
|
||||
char *get_object_directory(void)
|
||||
const char *get_object_directory(void)
|
||||
{
|
||||
if (!the_repository->objects->odb)
|
||||
BUG("git environment hasn't been setup");
|
||||
|
|
|
@ -4737,7 +4737,8 @@ void merge_incore_recursive(struct merge_options *opt,
|
|||
trace2_region_enter("merge", "incore_recursive", opt->repo);
|
||||
|
||||
/* We set the ancestor label based on the merge_bases */
|
||||
assert(opt->ancestor == NULL);
|
||||
assert(opt->ancestor == NULL ||
|
||||
!strcmp(opt->ancestor, "constructed merge base"));
|
||||
|
||||
trace2_region_enter("merge", "merge_start", opt->repo);
|
||||
merge_start(opt, result);
|
||||
|
|
|
@ -3806,6 +3806,7 @@ int merge_recursive_generic(struct merge_options *opt,
|
|||
const struct object_id *merge,
|
||||
int num_merge_bases,
|
||||
const struct object_id **merge_bases,
|
||||
recursive_merge_fn_t merge_fn,
|
||||
struct commit **result)
|
||||
{
|
||||
int clean;
|
||||
|
@ -3829,8 +3830,7 @@ int merge_recursive_generic(struct merge_options *opt,
|
|||
}
|
||||
|
||||
repo_hold_locked_index(opt->repo, &lock, LOCK_DIE_ON_ERROR);
|
||||
clean = merge_recursive(opt, head_commit, next_commit, ca,
|
||||
result);
|
||||
clean = merge_fn(opt, head_commit, next_commit, ca, result);
|
||||
if (clean < 0) {
|
||||
rollback_lock_file(&lock);
|
||||
return clean;
|
||||
|
|
|
@ -53,6 +53,12 @@ struct merge_options {
|
|||
struct merge_options_internal *priv;
|
||||
};
|
||||
|
||||
typedef int (*recursive_merge_fn_t)(struct merge_options *opt,
|
||||
struct commit *h1,
|
||||
struct commit *h2,
|
||||
struct commit_list *merge_bases,
|
||||
struct commit **result);
|
||||
|
||||
void init_merge_options(struct merge_options *opt, struct repository *repo);
|
||||
|
||||
/* parse the option in s and update the relevant field of opt */
|
||||
|
@ -105,7 +111,7 @@ int merge_recursive(struct merge_options *opt,
|
|||
|
||||
/*
|
||||
* merge_recursive_generic can operate on trees instead of commits, by
|
||||
* wrapping the trees into virtual commits, and calling merge_recursive().
|
||||
* wrapping the trees into virtual commits, and calling the provided merge_fn.
|
||||
* It also writes out the in-memory index to disk if the merge is successful.
|
||||
*
|
||||
* Outputs:
|
||||
|
@ -120,6 +126,7 @@ int merge_recursive_generic(struct merge_options *opt,
|
|||
const struct object_id *merge,
|
||||
int num_merge_bases,
|
||||
const struct object_id **merge_bases,
|
||||
recursive_merge_fn_t merge_fn,
|
||||
struct commit **result);
|
||||
|
||||
#endif
|
||||
|
|
17
midx.c
17
midx.c
|
@ -1132,17 +1132,26 @@ cleanup:
|
|||
static struct multi_pack_index *lookup_multi_pack_index(struct repository *r,
|
||||
const char *object_dir)
|
||||
{
|
||||
struct multi_pack_index *result = NULL;
|
||||
struct multi_pack_index *cur;
|
||||
char *obj_dir_real = real_pathdup(object_dir, 1);
|
||||
struct strbuf cur_path_real = STRBUF_INIT;
|
||||
|
||||
/* Ensure the given object_dir is local, or a known alternate. */
|
||||
find_odb(r, object_dir);
|
||||
find_odb(r, obj_dir_real);
|
||||
|
||||
for (cur = get_multi_pack_index(r); cur; cur = cur->next) {
|
||||
if (!strcmp(object_dir, cur->object_dir))
|
||||
return cur;
|
||||
strbuf_realpath(&cur_path_real, cur->object_dir, 1);
|
||||
if (!strcmp(obj_dir_real, cur_path_real.buf)) {
|
||||
result = cur;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
cleanup:
|
||||
free(obj_dir_real);
|
||||
strbuf_release(&cur_path_real);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int write_midx_internal(const char *object_dir,
|
||||
|
|
|
@ -114,7 +114,7 @@ static const char *alternate_index_output;
|
|||
static void set_index_entry(struct index_state *istate, int nr, struct cache_entry *ce)
|
||||
{
|
||||
if (S_ISSPARSEDIR(ce->ce_mode))
|
||||
istate->sparse_index = 1;
|
||||
istate->sparse_index = COLLAPSED;
|
||||
|
||||
istate->cache[nr] = ce;
|
||||
add_name_hash(istate, ce);
|
||||
|
@ -1874,7 +1874,7 @@ static int read_index_extension(struct index_state *istate,
|
|||
break;
|
||||
case CACHE_EXT_SPARSE_DIRECTORIES:
|
||||
/* no content, only an indicator */
|
||||
istate->sparse_index = 1;
|
||||
istate->sparse_index = COLLAPSED;
|
||||
break;
|
||||
default:
|
||||
if (*ext < 'A' || 'Z' < *ext)
|
||||
|
@ -3191,7 +3191,7 @@ static int do_write_locked_index(struct index_state *istate, struct lock_file *l
|
|||
unsigned flags)
|
||||
{
|
||||
int ret;
|
||||
int was_full = !istate->sparse_index;
|
||||
int was_full = istate->sparse_index == COMPLETELY_FULL;
|
||||
|
||||
ret = convert_to_sparse(istate, 0);
|
||||
|
||||
|
|
|
@ -74,7 +74,7 @@ void prepare_repo_settings(struct repository *r)
|
|||
repo_cfg_bool(r, "fetch.writecommitgraph", &r->settings.fetch_write_commit_graph, 0);
|
||||
repo_cfg_bool(r, "pack.usesparse", &r->settings.pack_use_sparse, 1);
|
||||
repo_cfg_bool(r, "core.multipackindex", &r->settings.core_multi_pack_index, 1);
|
||||
repo_cfg_bool(r, "index.sparse", &r->settings.sparse_index, 0);
|
||||
repo_cfg_bool(r, "index.sparse", &r->settings.sparse_index, 1);
|
||||
|
||||
/*
|
||||
* The GIT_TEST_MULTI_PACK_INDEX variable is special in that
|
||||
|
|
|
@ -640,7 +640,7 @@ static int do_recursive_merge(struct repository *r,
|
|||
o.branch2 = next ? next_label : "(empty tree)";
|
||||
if (is_rebase_i(opts))
|
||||
o.buffer_output = 2;
|
||||
o.show_rename_progress = 1;
|
||||
o.show_rename_progress = isatty(2);
|
||||
|
||||
head_tree = parse_tree_indirect(head);
|
||||
next_tree = next ? get_commit_tree(next) : empty_tree(r);
|
||||
|
|
122
sparse-index.c
122
sparse-index.c
|
@ -9,6 +9,11 @@
|
|||
#include "dir.h"
|
||||
#include "fsmonitor.h"
|
||||
|
||||
struct modify_index_context {
|
||||
struct index_state *write;
|
||||
struct pattern_list *pl;
|
||||
};
|
||||
|
||||
static struct cache_entry *construct_sparse_dir_entry(
|
||||
struct index_state *istate,
|
||||
const char *sparse_dir,
|
||||
|
@ -173,7 +178,7 @@ int convert_to_sparse(struct index_state *istate, int flags)
|
|||
* If the index is already sparse, empty, or otherwise
|
||||
* cannot be converted to sparse, do not convert.
|
||||
*/
|
||||
if (istate->sparse_index || !istate->cache_nr ||
|
||||
if (istate->sparse_index == COLLAPSED || !istate->cache_nr ||
|
||||
!is_sparse_index_allowed(istate, flags))
|
||||
return 0;
|
||||
|
||||
|
@ -214,7 +219,7 @@ int convert_to_sparse(struct index_state *istate, int flags)
|
|||
FREE_AND_NULL(istate->fsmonitor_dirty);
|
||||
FREE_AND_NULL(istate->fsmonitor_last_update);
|
||||
|
||||
istate->sparse_index = 1;
|
||||
istate->sparse_index = COLLAPSED;
|
||||
trace2_region_leave("index", "convert_to_sparse", istate->repo);
|
||||
return 0;
|
||||
}
|
||||
|
@ -231,47 +236,115 @@ static int add_path_to_index(const struct object_id *oid,
|
|||
struct strbuf *base, const char *path,
|
||||
unsigned int mode, void *context)
|
||||
{
|
||||
struct index_state *istate = (struct index_state *)context;
|
||||
struct modify_index_context *ctx = (struct modify_index_context *)context;
|
||||
struct cache_entry *ce;
|
||||
size_t len = base->len;
|
||||
|
||||
if (S_ISDIR(mode))
|
||||
return READ_TREE_RECURSIVE;
|
||||
if (S_ISDIR(mode)) {
|
||||
size_t baselen = base->len;
|
||||
if (!ctx->pl)
|
||||
return READ_TREE_RECURSIVE;
|
||||
|
||||
strbuf_addstr(base, path);
|
||||
/*
|
||||
* Have we expanded to a point outside of the sparse-checkout?
|
||||
*/
|
||||
strbuf_addstr(base, path);
|
||||
strbuf_add(base, "/-", 2);
|
||||
|
||||
ce = make_cache_entry(istate, mode, oid, base->buf, 0, 0);
|
||||
if (path_matches_cone_mode_pattern_list(base->buf, base->len, ctx->pl)) {
|
||||
strbuf_setlen(base, baselen);
|
||||
return READ_TREE_RECURSIVE;
|
||||
}
|
||||
|
||||
/*
|
||||
* The path "{base}{path}/" is a sparse directory. Create the correct
|
||||
* name for inserting the entry into the idnex.
|
||||
*/
|
||||
strbuf_setlen(base, base->len - 1);
|
||||
} else {
|
||||
strbuf_addstr(base, path);
|
||||
}
|
||||
|
||||
ce = make_cache_entry(ctx->write, mode, oid, base->buf, 0, 0);
|
||||
ce->ce_flags |= CE_SKIP_WORKTREE | CE_EXTENDED;
|
||||
set_index_entry(istate, istate->cache_nr++, ce);
|
||||
set_index_entry(ctx->write, ctx->write->cache_nr++, ce);
|
||||
|
||||
strbuf_setlen(base, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ensure_full_index(struct index_state *istate)
|
||||
void expand_to_pattern_list(struct index_state *istate,
|
||||
struct pattern_list *pl)
|
||||
{
|
||||
int i;
|
||||
struct index_state *full;
|
||||
struct strbuf base = STRBUF_INIT;
|
||||
struct modify_index_context ctx;
|
||||
|
||||
if (!istate || !istate->sparse_index)
|
||||
/*
|
||||
* If the index is already full, then keep it full. We will convert
|
||||
* it to a sparse index on write, if possible.
|
||||
*/
|
||||
if (!istate || istate->sparse_index == COMPLETELY_FULL)
|
||||
return;
|
||||
|
||||
/*
|
||||
* If our index is sparse, but our new pattern set does not use
|
||||
* cone mode patterns, then we need to expand the index before we
|
||||
* continue. A NULL pattern set indicates a full expansion to a
|
||||
* full index.
|
||||
*/
|
||||
if (pl && !pl->use_cone_patterns) {
|
||||
pl = NULL;
|
||||
} else {
|
||||
/*
|
||||
* We might contract file entries into sparse-directory
|
||||
* entries, and for that we will need the cache tree to
|
||||
* be recomputed.
|
||||
*/
|
||||
cache_tree_free(&istate->cache_tree);
|
||||
|
||||
/*
|
||||
* If there is a problem creating the cache tree, then we
|
||||
* need to expand to a full index since we cannot satisfy
|
||||
* the current request as a sparse index.
|
||||
*/
|
||||
if (cache_tree_update(istate, WRITE_TREE_MISSING_OK))
|
||||
pl = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* A NULL pattern set indicates we are expanding a full index, so
|
||||
* we use a special region name that indicates the full expansion.
|
||||
* This is used by test cases, but also helps to differentiate the
|
||||
* two cases.
|
||||
*/
|
||||
trace2_region_enter("index",
|
||||
pl ? "expand_to_pattern_list" : "ensure_full_index",
|
||||
istate->repo);
|
||||
|
||||
if (!istate->repo)
|
||||
istate->repo = the_repository;
|
||||
|
||||
trace2_region_enter("index", "ensure_full_index", istate->repo);
|
||||
|
||||
/* initialize basics of new index */
|
||||
full = xcalloc(1, sizeof(struct index_state));
|
||||
memcpy(full, istate, sizeof(struct index_state));
|
||||
|
||||
/*
|
||||
* This slightly-misnamed 'full' index might still be sparse if we
|
||||
* are only modifying the list of sparse directories. This hinges
|
||||
* on whether we have a non-NULL pattern list.
|
||||
*/
|
||||
full->sparse_index = pl ? PARTIALLY_SPARSE : COMPLETELY_FULL;
|
||||
|
||||
/* then change the necessary things */
|
||||
full->sparse_index = 0;
|
||||
full->cache_alloc = (3 * istate->cache_alloc) / 2;
|
||||
full->cache_nr = 0;
|
||||
ALLOC_ARRAY(full->cache, full->cache_alloc);
|
||||
|
||||
ctx.write = full;
|
||||
ctx.pl = pl;
|
||||
|
||||
for (i = 0; i < istate->cache_nr; i++) {
|
||||
struct cache_entry *ce = istate->cache[i];
|
||||
struct tree *tree;
|
||||
|
@ -281,6 +354,14 @@ void ensure_full_index(struct index_state *istate)
|
|||
set_index_entry(full, full->cache_nr++, ce);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* We now have a sparse directory entry. Should we expand? */
|
||||
if (pl &&
|
||||
path_matches_cone_mode_pattern_list(ce->name, ce->ce_namelen, pl) <= 0) {
|
||||
set_index_entry(full, full->cache_nr++, ce);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(ce->ce_flags & CE_SKIP_WORKTREE))
|
||||
warning(_("index entry is a directory, but not sparse (%08x)"),
|
||||
ce->ce_flags);
|
||||
|
@ -297,7 +378,7 @@ void ensure_full_index(struct index_state *istate)
|
|||
strbuf_add(&base, ce->name, strlen(ce->name));
|
||||
|
||||
read_tree_at(istate->repo, tree, &base, &ps,
|
||||
add_path_to_index, full);
|
||||
add_path_to_index, &ctx);
|
||||
|
||||
/* free directory entries. full entries are re-used */
|
||||
discard_cache_entry(ce);
|
||||
|
@ -306,7 +387,7 @@ void ensure_full_index(struct index_state *istate)
|
|||
/* Copy back into original index. */
|
||||
memcpy(&istate->name_hash, &full->name_hash, sizeof(full->name_hash));
|
||||
memcpy(&istate->dir_hash, &full->dir_hash, sizeof(full->dir_hash));
|
||||
istate->sparse_index = 0;
|
||||
istate->sparse_index = pl ? PARTIALLY_SPARSE : COMPLETELY_FULL;
|
||||
free(istate->cache);
|
||||
istate->cache = full->cache;
|
||||
istate->cache_nr = full->cache_nr;
|
||||
|
@ -320,9 +401,16 @@ void ensure_full_index(struct index_state *istate)
|
|||
|
||||
/* Clear and recompute the cache-tree */
|
||||
cache_tree_free(&istate->cache_tree);
|
||||
cache_tree_update(istate, 0);
|
||||
cache_tree_update(istate, WRITE_TREE_MISSING_OK);
|
||||
|
||||
trace2_region_leave("index", "ensure_full_index", istate->repo);
|
||||
trace2_region_leave("index",
|
||||
pl ? "expand_to_pattern_list" : "ensure_full_index",
|
||||
istate->repo);
|
||||
}
|
||||
|
||||
void ensure_full_index(struct index_state *istate)
|
||||
{
|
||||
expand_to_pattern_list(istate, NULL);
|
||||
}
|
||||
|
||||
void ensure_correct_sparsity(struct index_state *istate)
|
||||
|
|
|
@ -7,7 +7,7 @@ int convert_to_sparse(struct index_state *istate, int flags);
|
|||
void ensure_correct_sparsity(struct index_state *istate);
|
||||
void clear_skip_worktree_from_present_files(struct index_state *istate);
|
||||
|
||||
/*
|
||||
/**
|
||||
* Some places in the codebase expect to search for a specific path.
|
||||
* This path might be outside of the sparse-checkout definition, in
|
||||
* which case a sparse-index may not contain a path for that index.
|
||||
|
@ -23,4 +23,18 @@ void expand_to_path(struct index_state *istate,
|
|||
struct repository;
|
||||
int set_sparse_index_config(struct repository *repo, int enable);
|
||||
|
||||
struct pattern_list;
|
||||
|
||||
/**
|
||||
* Scan the given index and compare its entries to the given pattern list.
|
||||
* If the index is sparse and the pattern list uses cone mode patterns,
|
||||
* then modify the index to contain the all of the file entries within that
|
||||
* new pattern list. This expands sparse directories only as far as needed.
|
||||
*
|
||||
* If the pattern list is NULL or does not use cone mode patterns, then the
|
||||
* index is expanded to a full index.
|
||||
*/
|
||||
void expand_to_pattern_list(struct index_state *istate,
|
||||
struct pattern_list *pl);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -55,7 +55,7 @@ test_expect_success 'setup repo and indexes' '
|
|||
git -c core.sparseCheckoutCone=true clone --branch=wide --sparse . full-v3 &&
|
||||
(
|
||||
cd full-v3 &&
|
||||
git sparse-checkout init --cone &&
|
||||
git sparse-checkout init --cone --no-sparse-index &&
|
||||
git sparse-checkout set $SPARSE_CONE &&
|
||||
git config index.version 3 &&
|
||||
git update-index --index-version=3 &&
|
||||
|
@ -64,7 +64,7 @@ test_expect_success 'setup repo and indexes' '
|
|||
git -c core.sparseCheckoutCone=true clone --branch=wide --sparse . full-v4 &&
|
||||
(
|
||||
cd full-v4 &&
|
||||
git sparse-checkout init --cone &&
|
||||
git sparse-checkout init --cone --no-sparse-index &&
|
||||
git sparse-checkout set $SPARSE_CONE &&
|
||||
git config index.version 4 &&
|
||||
git update-index --index-version=4 &&
|
||||
|
@ -106,6 +106,8 @@ test_perf_on_all () {
|
|||
}
|
||||
|
||||
test_perf_on_all git status
|
||||
test_perf_on_all 'git stash && git stash pop'
|
||||
test_perf_on_all 'echo >>new && git stash -u && git stash pop'
|
||||
test_perf_on_all git add -A
|
||||
test_perf_on_all git add .
|
||||
test_perf_on_all git commit -a -m A
|
||||
|
@ -120,5 +122,6 @@ test_perf_on_all git blame $SPARSE_CONE/f3/a
|
|||
test_perf_on_all git read-tree -mu HEAD
|
||||
test_perf_on_all git checkout-index -f --all
|
||||
test_perf_on_all git update-index --add --remove $SPARSE_CONE/a
|
||||
test_perf_on_all git sparse-checkout reapply
|
||||
|
||||
test_done
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
|
||||
test_description='git init'
|
||||
|
||||
TEST_PASSES_SANITIZE_LEAK=true
|
||||
# Drop this leak check because it doesn't work on every platform.
|
||||
# TEST_PASSES_SANITIZE_LEAK=true
|
||||
. ./test-lib.sh
|
||||
|
||||
check_config () {
|
||||
|
|
|
@ -760,6 +760,10 @@ test_expect_success 'cone mode clears ignored subdirectories' '
|
|||
git -C repo status --porcelain=v2 >out &&
|
||||
test_must_be_empty out &&
|
||||
|
||||
git -C repo -c index.deleteSparseDirectories=false sparse-checkout reapply &&
|
||||
test_path_is_dir repo/folder1 &&
|
||||
test_path_is_dir repo/deep/deeper2 &&
|
||||
|
||||
git -C repo sparse-checkout reapply &&
|
||||
test_path_is_missing repo/folder1 &&
|
||||
test_path_is_missing repo/deep/deeper2 &&
|
||||
|
|
|
@ -155,6 +155,7 @@ init_repos () {
|
|||
git -C sparse-index reset --hard &&
|
||||
|
||||
# initialize sparse-checkout definitions
|
||||
git -C sparse-checkout config index.sparse false &&
|
||||
git -C sparse-checkout sparse-checkout init --cone &&
|
||||
git -C sparse-checkout sparse-checkout set deep &&
|
||||
git -C sparse-index sparse-checkout init --cone --sparse-index &&
|
||||
|
@ -455,6 +456,43 @@ test_expect_success 'diff --cached' '
|
|||
test_all_match git diff --cached
|
||||
'
|
||||
|
||||
test_expect_success 'diff partially-staged' '
|
||||
init_repos &&
|
||||
|
||||
write_script edit-contents <<-\EOF &&
|
||||
echo text >>$1
|
||||
EOF
|
||||
|
||||
# Add file within cone
|
||||
test_all_match git sparse-checkout set deep &&
|
||||
run_on_all ../edit-contents deep/testfile &&
|
||||
test_all_match git add deep/testfile &&
|
||||
run_on_all ../edit-contents deep/testfile &&
|
||||
|
||||
test_all_match git diff &&
|
||||
test_all_match git diff --staged &&
|
||||
|
||||
# Add file outside cone
|
||||
test_all_match git reset --hard &&
|
||||
run_on_all mkdir newdirectory &&
|
||||
run_on_all ../edit-contents newdirectory/testfile &&
|
||||
test_all_match git sparse-checkout set newdirectory &&
|
||||
test_all_match git add newdirectory/testfile &&
|
||||
run_on_all ../edit-contents newdirectory/testfile &&
|
||||
test_all_match git sparse-checkout set &&
|
||||
|
||||
test_all_match git diff &&
|
||||
test_all_match git diff --staged &&
|
||||
|
||||
# Merge conflict outside cone
|
||||
test_all_match git reset --hard &&
|
||||
test_all_match git checkout merge-left &&
|
||||
test_all_match test_must_fail git merge merge-right &&
|
||||
|
||||
test_all_match git diff &&
|
||||
test_all_match git diff --staged
|
||||
'
|
||||
|
||||
# NEEDSWORK: sparse-checkout behaves differently from full-checkout when
|
||||
# running this test with 'df-conflict-2' after 'df-conflict-1'.
|
||||
test_expect_success 'diff with renames and conflicts' '
|
||||
|
@ -912,7 +950,9 @@ test_expect_success 'read-tree --merge with directory-file conflicts' '
|
|||
test_expect_success 'merge, cherry-pick, and rebase' '
|
||||
init_repos &&
|
||||
|
||||
for OPERATION in "merge -m merge" cherry-pick "rebase --apply" "rebase --merge"
|
||||
# microsoft/git specific: we need to use "quiet" mode
|
||||
# to avoid different stderr for some rebases.
|
||||
for OPERATION in "merge -m merge" cherry-pick "rebase -q --apply" "rebase -q --merge"
|
||||
do
|
||||
test_all_match git checkout -B temp update-deep &&
|
||||
test_all_match git $OPERATION update-folder1 &&
|
||||
|
@ -1151,6 +1191,34 @@ test_expect_success 'clean' '
|
|||
test_sparse_match test_path_is_dir folder1
|
||||
'
|
||||
|
||||
test_expect_success 'show (cached blobs/trees)' '
|
||||
init_repos &&
|
||||
|
||||
test_all_match git show :a &&
|
||||
test_all_match git show :deep/a &&
|
||||
test_sparse_match git show :folder1/a &&
|
||||
|
||||
# Asking "git show" for directories in the index
|
||||
# does not work as implemented. The error message is
|
||||
# different for a full checkout and a sparse checkout
|
||||
# when the directory is outside of the cone.
|
||||
test_all_match test_must_fail git show :deep/ &&
|
||||
test_must_fail git -C full-checkout show :folder1/ &&
|
||||
test_must_fail git -C sparse-checkout show :folder1/ &&
|
||||
|
||||
# The sparse index actually has "folder1" inside, so
|
||||
# "git show :folder1/" succeeds when it did not before.
|
||||
git -C sparse-index show :folder1/ >actual &&
|
||||
git -C sparse-index show HEAD:folder1 >expect &&
|
||||
|
||||
# The output of "git show" includes the way we
|
||||
# referenced the objects, so strip that out.
|
||||
test_line_count = 4 actual &&
|
||||
tail -n 2 actual >actual-trunc &&
|
||||
tail -n 2 expect >expect-trunc &&
|
||||
test_cmp expect-trunc actual-trunc
|
||||
'
|
||||
|
||||
test_expect_success 'submodule handling' '
|
||||
init_repos &&
|
||||
|
||||
|
@ -1265,6 +1333,25 @@ test_expect_success 'sparse-index is not expanded' '
|
|||
echo >>sparse-index/untracked.txt &&
|
||||
ensure_not_expanded add . &&
|
||||
|
||||
ensure_not_expanded show :a &&
|
||||
ensure_not_expanded show :deep/a &&
|
||||
|
||||
echo >>sparse-index/a &&
|
||||
ensure_not_expanded stash &&
|
||||
ensure_not_expanded stash list &&
|
||||
ensure_not_expanded stash show stash@{0} &&
|
||||
ensure_not_expanded stash apply stash@{0} &&
|
||||
ensure_not_expanded stash drop stash@{0} &&
|
||||
|
||||
ensure_not_expanded stash -u &&
|
||||
ensure_not_expanded stash pop &&
|
||||
|
||||
ensure_not_expanded stash create &&
|
||||
oid=$(git -C sparse-index stash create) &&
|
||||
ensure_not_expanded stash store -m "test" $oid &&
|
||||
ensure_not_expanded reset --hard &&
|
||||
ensure_not_expanded stash pop &&
|
||||
|
||||
ensure_not_expanded checkout-index -f a &&
|
||||
ensure_not_expanded checkout-index -f --all &&
|
||||
for ref in update-deep update-folder1 update-folder2 update-deep
|
||||
|
@ -1279,6 +1366,11 @@ test_expect_success 'sparse-index is not expanded' '
|
|||
ensure_not_expanded reset --merge update-deep &&
|
||||
ensure_not_expanded reset --hard &&
|
||||
|
||||
echo a test change >>sparse-index/README.md &&
|
||||
ensure_not_expanded diff &&
|
||||
git -C sparse-index add README.md &&
|
||||
ensure_not_expanded diff --staged &&
|
||||
|
||||
ensure_not_expanded reset base -- deep/a &&
|
||||
ensure_not_expanded reset base -- nonexistent-file &&
|
||||
ensure_not_expanded reset deepest -- deep &&
|
||||
|
@ -1516,6 +1608,45 @@ test_expect_success 'ls-files' '
|
|||
ensure_not_expanded ls-files --sparse
|
||||
'
|
||||
|
||||
test_expect_success 'sparse index is not expanded: sparse-checkout' '
|
||||
init_repos &&
|
||||
|
||||
ensure_not_expanded sparse-checkout set deep/deeper2 &&
|
||||
ensure_not_expanded sparse-checkout set deep/deeper1 &&
|
||||
ensure_not_expanded sparse-checkout set deep &&
|
||||
ensure_not_expanded sparse-checkout add folder1 &&
|
||||
ensure_not_expanded sparse-checkout set deep/deeper1 &&
|
||||
ensure_not_expanded sparse-checkout set folder2 &&
|
||||
|
||||
echo >>sparse-index/folder2/a &&
|
||||
git -C sparse-index add folder2/a &&
|
||||
ensure_not_expanded sparse-checkout add folder1 &&
|
||||
|
||||
# Skip checks here, since deep/deeper1 is inside a sparse directory
|
||||
# that must be expanded to check whether `deep/deeper1` is a file
|
||||
# or not.
|
||||
ensure_not_expanded sparse-checkout set --skip-checks deep/deeper1 &&
|
||||
ensure_not_expanded sparse-checkout set
|
||||
'
|
||||
|
||||
# NEEDSWORK: similar to `git add`, untracked files outside of the sparse
|
||||
# checkout definition are successfully stashed and unstashed.
|
||||
test_expect_success 'stash -u outside sparse checkout definition' '
|
||||
init_repos &&
|
||||
|
||||
write_script edit-contents <<-\EOF &&
|
||||
echo text >>$1
|
||||
EOF
|
||||
|
||||
run_on_sparse mkdir -p folder1 &&
|
||||
run_on_all ../edit-contents folder1/new &&
|
||||
test_all_match git stash -u &&
|
||||
test_all_match git status --porcelain=v2 &&
|
||||
|
||||
test_all_match git stash pop -q &&
|
||||
test_all_match git status --porcelain=v2
|
||||
'
|
||||
|
||||
# NEEDSWORK: a sparse-checkout behaves differently from a full checkout
|
||||
# in this scenario, but it shouldn't.
|
||||
test_expect_success 'reset mixed and checkout orphan' '
|
||||
|
|
|
@ -400,7 +400,7 @@ EOF
|
|||
|
||||
'
|
||||
|
||||
test_expect_failure 'ensure deserialize -v does not crash' '
|
||||
test_expect_success 'ensure deserialize -v does not crash' '
|
||||
|
||||
git init -b main verbose_test &&
|
||||
touch verbose_test/a &&
|
||||
|
|
|
@ -49,7 +49,7 @@ test_expect_success 'setup' '
|
|||
echo "text" >B/b &&
|
||||
git add A B &&
|
||||
git commit -m sub &&
|
||||
git sparse-checkout init --cone &&
|
||||
git sparse-checkout init --cone --no-sparse-index &&
|
||||
git sparse-checkout set B
|
||||
) &&
|
||||
|
||||
|
|
|
@ -52,6 +52,23 @@ test_expect_success 'run [--auto|--quiet]' '
|
|||
test_subcommand git gc --no-quiet <run-no-quiet.txt
|
||||
'
|
||||
|
||||
test_expect_success 'lock file behavior' '
|
||||
test_when_finished git config --unset maintenance.commit-graph.schedule &&
|
||||
git config maintenance.commit-graph.schedule hourly &&
|
||||
|
||||
touch .git/objects/maintenance.lock &&
|
||||
git maintenance run --schedule=hourly --no-quiet 2>err &&
|
||||
grep "lock file .* exists, skipping maintenance" err &&
|
||||
|
||||
test-tool chmtime =-22000 .git/objects/maintenance.lock &&
|
||||
git maintenance run --schedule=hourly --no-quiet 2>err &&
|
||||
grep "deleted stale lock file" err &&
|
||||
test_path_is_missing .git/objects/maintenance.lock &&
|
||||
|
||||
git maintenance run --schedule=hourly 2>err &&
|
||||
test_must_be_empty err
|
||||
'
|
||||
|
||||
test_expect_success 'maintenance.auto config option' '
|
||||
GIT_TRACE2_EVENT="$(pwd)/default" git commit --quiet --allow-empty -m 1 &&
|
||||
test_subcommand git maintenance run --auto --quiet <default &&
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "promisor-remote.h"
|
||||
#include "entry.h"
|
||||
#include "parallel-checkout.h"
|
||||
#include "sparse-index.h"
|
||||
|
||||
/*
|
||||
* Error messages expected by scripts out of plumbing commands such as
|
||||
|
@ -2057,6 +2058,9 @@ enum update_sparsity_result update_sparsity(struct unpack_trees_options *o)
|
|||
goto skip_sparse_checkout;
|
||||
}
|
||||
|
||||
/* Expand sparse directories as needed */
|
||||
expand_to_pattern_list(o->src_index, o->pl);
|
||||
|
||||
/* Set NEW_SKIP_WORKTREE on existing entries. */
|
||||
mark_all_ce_unused(o->src_index);
|
||||
mark_new_skip_worktree(o->pl, o->src_index, 0,
|
||||
|
|
Загрузка…
Ссылка в новой задаче