summaryrefslogtreecommitdiff
path: root/unpack-trees.c
diff options
context:
space:
mode:
Diffstat (limited to 'unpack-trees.c')
-rw-r--r--unpack-trees.c600
1 files changed, 409 insertions, 191 deletions
diff --git a/unpack-trees.c b/unpack-trees.c
index 7bd1903464..f88a69f8e7 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -1,5 +1,5 @@
#include "cache.h"
-#include "argv-array.h"
+#include "strvec.h"
#include "repository.h"
#include "config.h"
#include "dir.h"
@@ -11,12 +11,13 @@
#include "refs.h"
#include "attr.h"
#include "split-index.h"
-#include "dir.h"
#include "submodule.h"
#include "submodule-config.h"
#include "fsmonitor.h"
#include "object-store.h"
-#include "fetch-object.h"
+#include "promisor-remote.h"
+#include "entry.h"
+#include "parallel-checkout.h"
/*
* Error messages expected by scripts out of plumbing commands such as
@@ -25,7 +26,7 @@
* situation better. See how "git checkout" and "git merge" replaces
* them using setup_unpack_trees_porcelain(), for example.
*/
-static const char *unpack_plumbing_errors[NB_UNPACK_TREES_ERROR_TYPES] = {
+static const char *unpack_plumbing_errors[NB_UNPACK_TREES_WARNING_TYPES] = {
/* ERROR_WOULD_OVERWRITE */
"Entry '%s' would be overwritten by merge. Cannot merge.",
@@ -44,17 +45,20 @@ static const char *unpack_plumbing_errors[NB_UNPACK_TREES_ERROR_TYPES] = {
/* ERROR_BIND_OVERLAP */
"Entry '%s' overlaps with '%s'. Cannot bind.",
- /* ERROR_SPARSE_NOT_UPTODATE_FILE */
- "Entry '%s' not uptodate. Cannot update sparse checkout.",
+ /* ERROR_WOULD_LOSE_SUBMODULE */
+ "Submodule '%s' cannot checkout new HEAD.",
- /* ERROR_WOULD_LOSE_ORPHANED_OVERWRITTEN */
- "Working tree file '%s' would be overwritten by sparse checkout update.",
+ /* NB_UNPACK_TREES_ERROR_TYPES; just a meta value */
+ "",
- /* ERROR_WOULD_LOSE_ORPHANED_REMOVED */
- "Working tree file '%s' would be removed by sparse checkout update.",
+ /* WARNING_SPARSE_NOT_UPTODATE_FILE */
+ "Path '%s' not uptodate; will not remove from working tree.",
- /* ERROR_WOULD_LOSE_SUBMODULE */
- "Submodule '%s' cannot checkout new HEAD.",
+ /* WARNING_SPARSE_UNMERGED_FILE */
+ "Path '%s' unmerged; will not remove from working tree.",
+
+ /* WARNING_SPARSE_ORPHANED_NOT_OVERWRITTEN */
+ "Path '%s' already present; will not overwrite with sparse update.",
};
#define ERRORMSG(o,type) \
@@ -104,7 +108,7 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts,
const char **msgs = opts->msgs;
const char *msg;
- argv_array_init(&opts->msgs_to_free);
+ strvec_init(&opts->msgs_to_free);
if (!strcmp(cmd, "checkout"))
msg = advice_commit_before_merge
@@ -122,7 +126,7 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts,
"Please commit your changes or stash them before you %s.")
: _("Your local changes to the following files would be overwritten by %s:\n%%s");
msgs[ERROR_WOULD_OVERWRITE] = msgs[ERROR_NOT_UPTODATE_FILE] =
- argv_array_pushf(&opts->msgs_to_free, msg, cmd, cmd);
+ strvec_pushf(&opts->msgs_to_free, msg, cmd, cmd);
msgs[ERROR_NOT_UPTODATE_DIR] =
_("Updating the following directories would lose untracked files in them:\n%s");
@@ -143,7 +147,7 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts,
"Please move or remove them before you %s.")
: _("The following untracked working tree files would be removed by %s:\n%%s");
msgs[ERROR_WOULD_LOSE_UNTRACKED_REMOVED] =
- argv_array_pushf(&opts->msgs_to_free, msg, cmd, cmd);
+ strvec_pushf(&opts->msgs_to_free, msg, cmd, cmd);
if (!strcmp(cmd, "checkout"))
msg = advice_commit_before_merge
@@ -161,7 +165,7 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts,
"Please move or remove them before you %s.")
: _("The following untracked working tree files would be overwritten by %s:\n%%s");
msgs[ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN] =
- argv_array_pushf(&opts->msgs_to_free, msg, cmd, cmd);
+ strvec_pushf(&opts->msgs_to_free, msg, cmd, cmd);
/*
* Special case: ERROR_BIND_OVERLAP refers to a pair of paths, we
@@ -169,15 +173,16 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts,
*/
msgs[ERROR_BIND_OVERLAP] = _("Entry '%s' overlaps with '%s'. Cannot bind.");
- msgs[ERROR_SPARSE_NOT_UPTODATE_FILE] =
- _("Cannot update sparse checkout: the following entries are not up to date:\n%s");
- msgs[ERROR_WOULD_LOSE_ORPHANED_OVERWRITTEN] =
- _("The following working tree files would be overwritten by sparse checkout update:\n%s");
- msgs[ERROR_WOULD_LOSE_ORPHANED_REMOVED] =
- _("The following working tree files would be removed by sparse checkout update:\n%s");
msgs[ERROR_WOULD_LOSE_SUBMODULE] =
_("Cannot update submodule:\n%s");
+ msgs[WARNING_SPARSE_NOT_UPTODATE_FILE] =
+ _("The following paths are not up to date and were left despite sparse patterns:\n%s");
+ msgs[WARNING_SPARSE_UNMERGED_FILE] =
+ _("The following paths are unmerged and were left despite sparse patterns:\n%s");
+ msgs[WARNING_SPARSE_ORPHANED_NOT_OVERWRITTEN] =
+ _("The following paths were already present and thus not updated despite sparse patterns:\n%s");
+
opts->show_all_errors = 1;
/* rejected paths may not have a static buffer */
for (i = 0; i < ARRAY_SIZE(opts->unpack_rejects); i++)
@@ -186,7 +191,7 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts,
void clear_unpack_trees_porcelain(struct unpack_trees_options *opts)
{
- argv_array_clear(&opts->msgs_to_free);
+ strvec_clear(&opts->msgs_to_free);
memset(opts->msgs, 0, sizeof(opts->msgs));
}
@@ -227,7 +232,7 @@ static int add_rejected_path(struct unpack_trees_options *o,
/*
* Otherwise, insert in a list for future display by
- * display_error_msgs()
+ * display_(error|warning)_msgs()
*/
string_list_append(&o->unpack_rejects[e], path);
return -1;
@@ -238,13 +243,16 @@ static int add_rejected_path(struct unpack_trees_options *o,
*/
static void display_error_msgs(struct unpack_trees_options *o)
{
- int e, i;
- int something_displayed = 0;
+ int e;
+ unsigned error_displayed = 0;
for (e = 0; e < NB_UNPACK_TREES_ERROR_TYPES; e++) {
struct string_list *rejects = &o->unpack_rejects[e];
+
if (rejects->nr > 0) {
+ int i;
struct strbuf path = STRBUF_INIT;
- something_displayed = 1;
+
+ error_displayed = 1;
for (i = 0; i < rejects->nr; i++)
strbuf_addf(&path, "\t%s\n", rejects->items[i].string);
error(ERRORMSG(o, e), super_prefixed(path.buf));
@@ -252,10 +260,36 @@ static void display_error_msgs(struct unpack_trees_options *o)
}
string_list_clear(rejects, 0);
}
- if (something_displayed)
+ if (error_displayed)
fprintf(stderr, _("Aborting\n"));
}
+/*
+ * display all the warning messages stored in a nice way
+ */
+static void display_warning_msgs(struct unpack_trees_options *o)
+{
+ int e;
+ unsigned warning_displayed = 0;
+ for (e = NB_UNPACK_TREES_ERROR_TYPES + 1;
+ e < NB_UNPACK_TREES_WARNING_TYPES; e++) {
+ struct string_list *rejects = &o->unpack_rejects[e];
+
+ if (rejects->nr > 0) {
+ int i;
+ struct strbuf path = STRBUF_INIT;
+
+ warning_displayed = 1;
+ for (i = 0; i < rejects->nr; i++)
+ strbuf_addf(&path, "\t%s\n", rejects->items[i].string);
+ warning(ERRORMSG(o, e), super_prefixed(path.buf));
+ strbuf_release(&path);
+ }
+ string_list_clear(rejects, 0);
+ }
+ if (warning_displayed)
+ fprintf(stderr, _("After fixing the above paths, you may want to run `git sparse-checkout reapply`.\n"));
+}
static int check_submodule_move_head(const struct cache_entry *ce,
const char *old_id,
const char *new_id,
@@ -276,9 +310,9 @@ static int check_submodule_move_head(const struct cache_entry *ce,
}
/*
- * Preform the loading of the repository's gitmodules file. This function is
+ * Perform the loading of the repository's gitmodules file. This function is
* used by 'check_update()' to perform loading of the gitmodules file in two
- * differnt situations:
+ * different situations:
* (1) before removing entries from the working tree if the gitmodules file has
* been marked for removal. This situation is specified by 'state' == NULL.
* (2) before checking out entries to the working tree if the gitmodules file
@@ -292,19 +326,19 @@ static void load_gitmodules_file(struct index_state *index,
if (pos >= 0) {
struct cache_entry *ce = index->cache[pos];
if (!state && ce->ce_flags & CE_WT_REMOVE) {
- repo_read_gitmodules(the_repository);
+ repo_read_gitmodules(the_repository, 0);
} else if (state && (ce->ce_flags & CE_UPDATE)) {
submodule_free(the_repository);
checkout_entry(ce, state, NULL, NULL);
- repo_read_gitmodules(the_repository);
+ repo_read_gitmodules(the_repository, 0);
}
}
}
-static struct progress *get_progress(struct unpack_trees_options *o)
+static struct progress *get_progress(struct unpack_trees_options *o,
+ struct index_state *index)
{
unsigned cnt = 0, total = 0;
- struct index_state *index = &o->result;
if (!o->update || !o->verbose_update)
return NULL;
@@ -315,7 +349,7 @@ static struct progress *get_progress(struct unpack_trees_options *o)
total++;
}
- return start_delayed_progress(_("Checking out files"), total);
+ return start_delayed_progress(_("Updating files"), total);
}
static void setup_collided_checkout_detection(struct checkout *state,
@@ -358,33 +392,39 @@ static void report_collided_checkout(struct index_state *index)
string_list_clear(&list, 0);
}
-static int check_updates(struct unpack_trees_options *o)
+static int check_updates(struct unpack_trees_options *o,
+ struct index_state *index)
{
unsigned cnt = 0;
int errs = 0;
struct progress *progress;
- struct index_state *index = &o->result;
struct checkout state = CHECKOUT_INIT;
- int i;
+ int i, pc_workers, pc_threshold;
trace_performance_enter();
state.force = 1;
state.quiet = 1;
state.refresh_cache = 1;
state.istate = index;
+ clone_checkout_metadata(&state.meta, &o->meta, NULL);
+
+ if (!o->update || o->dry_run) {
+ remove_marked_cache_entries(index, 0);
+ trace_performance_leave("check_updates");
+ return 0;
+ }
if (o->clone)
setup_collided_checkout_detection(&state, index);
- progress = get_progress(o);
+ progress = get_progress(o, index);
/* Start with clean cache to avoid using any possibly outdated info. */
invalidate_lstat_cache();
- if (o->update)
- git_attr_set_direction(GIT_ATTR_CHECKOUT);
+ git_attr_set_direction(GIT_ATTR_CHECKOUT);
- if (should_update_submodules() && o->update && !o->dry_run)
+ if (should_update_submodules())
load_gitmodules_file(index, NULL);
for (i = 0; i < index->cache_nr; i++) {
@@ -392,18 +432,17 @@ static int check_updates(struct unpack_trees_options *o)
if (ce->ce_flags & CE_WT_REMOVE) {
display_progress(progress, ++cnt);
- if (o->update && !o->dry_run)
- unlink_entry(ce);
+ unlink_entry(ce);
}
}
+
remove_marked_cache_entries(index, 0);
remove_scheduled_dirs();
- if (should_update_submodules() && o->update && !o->dry_run)
+ if (should_update_submodules())
load_gitmodules_file(index, &state);
- enable_delayed_checkout(&state);
- if (repository_format_partial_clone && o->update && !o->dry_run) {
+ if (has_promisor_remote()) {
/*
* Prefetch the objects that are to be checked out in the loop
* below.
@@ -421,29 +460,38 @@ static int check_updates(struct unpack_trees_options *o)
continue;
oid_array_append(&to_fetch, &ce->oid);
}
- if (to_fetch.nr)
- fetch_objects(repository_format_partial_clone,
- to_fetch.oid, to_fetch.nr);
+ promisor_remote_get_direct(the_repository,
+ to_fetch.oid, to_fetch.nr);
oid_array_clear(&to_fetch);
}
+
+ get_parallel_checkout_configs(&pc_workers, &pc_threshold);
+
+ enable_delayed_checkout(&state);
+ if (pc_workers > 1)
+ init_parallel_checkout();
for (i = 0; i < index->cache_nr; i++) {
struct cache_entry *ce = index->cache[i];
if (ce->ce_flags & CE_UPDATE) {
+ size_t last_pc_queue_size = pc_queue_size();
+
if (ce->ce_flags & CE_WT_REMOVE)
BUG("both update and delete flags are set on %s",
ce->name);
- display_progress(progress, ++cnt);
ce->ce_flags &= ~CE_UPDATE;
- if (o->update && !o->dry_run) {
- errs |= checkout_entry(ce, &state, NULL, NULL);
- }
+ errs |= checkout_entry(ce, &state, NULL, NULL);
+
+ if (last_pc_queue_size == pc_queue_size())
+ display_progress(progress, ++cnt);
}
}
+ if (pc_workers > 1)
+ errs |= run_parallel_checkout(&state, pc_workers, pc_threshold,
+ progress, &cnt);
stop_progress(&progress);
errs |= finish_delayed_checkout(&state, NULL);
- if (o->update)
- git_attr_set_direction(GIT_ATTR_CHECKIN);
+ git_attr_set_direction(GIT_ATTR_CHECKIN);
if (o->clone)
report_collided_checkout(index);
@@ -506,19 +554,39 @@ static int apply_sparse_checkout(struct index_state *istate,
* also stat info may have lost after merged_entry() so calling
* verify_uptodate() again may fail
*/
- if (!(ce->ce_flags & CE_UPDATE) && verify_uptodate_sparse(ce, o))
+ if (!(ce->ce_flags & CE_UPDATE) &&
+ verify_uptodate_sparse(ce, o)) {
+ ce->ce_flags &= ~CE_SKIP_WORKTREE;
return -1;
+ }
ce->ce_flags |= CE_WT_REMOVE;
ce->ce_flags &= ~CE_UPDATE;
}
if (was_skip_worktree && !ce_skip_worktree(ce)) {
- if (verify_absent_sparse(ce, ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN, o))
+ if (verify_absent_sparse(ce, WARNING_SPARSE_ORPHANED_NOT_OVERWRITTEN, o))
return -1;
ce->ce_flags |= CE_UPDATE;
}
return 0;
}
+static int warn_conflicted_path(struct index_state *istate,
+ int i,
+ struct unpack_trees_options *o)
+{
+ char *conflicting_path = istate->cache[i]->name;
+ int count = 0;
+
+ add_rejected_path(o, WARNING_SPARSE_UNMERGED_FILE, conflicting_path);
+
+ /* Find out how many higher stage entries are at same path */
+ while ((++count) + i < istate->cache_nr &&
+ !strcmp(conflicting_path, istate->cache[count + i]->name))
+ ; /* do nothing */
+
+ return count;
+}
+
static inline int call_unpack_fn(const struct cache_entry * const *src,
struct unpack_trees_options *o)
{
@@ -635,7 +703,7 @@ static int unpack_index_entry(struct cache_entry *ce,
return ret;
}
-static int find_cache_pos(struct traverse_info *, const struct name_entry *);
+static int find_cache_pos(struct traverse_info *, const char *p, size_t len);
static void restore_cache_bottom(struct traverse_info *info, int bottom)
{
@@ -654,7 +722,7 @@ static int switch_cache_bottom(struct traverse_info *info)
if (o->diff_index_cached)
return 0;
ret = o->cache_bottom;
- pos = find_cache_pos(info->prev, &info->name);
+ pos = find_cache_pos(info->prev, info->name, info->namelen);
if (pos < -1)
o->cache_bottom = -2 - pos;
@@ -689,21 +757,25 @@ static int index_pos_by_traverse_info(struct name_entry *names,
struct traverse_info *info)
{
struct unpack_trees_options *o = info->data;
- int len = traverse_path_len(info, names);
- char *name = xmalloc(len + 1 /* slash */ + 1 /* NUL */);
+ struct strbuf name = STRBUF_INIT;
int pos;
- make_traverse_path(name, info, names);
- name[len++] = '/';
- name[len] = '\0';
- pos = index_name_pos(o->src_index, name, len);
- if (pos >= 0)
- BUG("This is a directory and should not exist in index");
- pos = -pos - 1;
- if (!starts_with(o->src_index->cache[pos]->name, name) ||
- (pos > 0 && starts_with(o->src_index->cache[pos-1]->name, name)))
- BUG("pos must point at the first entry in this directory");
- free(name);
+ strbuf_make_traverse_path(&name, info, names->path, names->pathlen);
+ strbuf_addch(&name, '/');
+ pos = index_name_pos(o->src_index, name.buf, name.len);
+ if (pos >= 0) {
+ if (!o->src_index->sparse_index ||
+ !(o->src_index->cache[pos]->ce_flags & CE_SKIP_WORKTREE))
+ BUG("This is a directory and should not exist in index");
+ } else {
+ pos = -pos - 1;
+ }
+ if (pos >= o->src_index->cache_nr ||
+ !starts_with(o->src_index->cache[pos]->name, name.buf) ||
+ (pos > 0 && starts_with(o->src_index->cache[pos-1]->name, name.buf)))
+ BUG("pos %d doesn't point to the first entry of %s in index",
+ pos, name.buf);
+ strbuf_release(&name);
return pos;
}
@@ -814,8 +886,10 @@ static int traverse_trees_recursive(int n, unsigned long dirmask,
newinfo = *info;
newinfo.prev = info;
newinfo.pathspec = info->pathspec;
- newinfo.name = *p;
- newinfo.pathlen += tree_entry_len(p) + 1;
+ newinfo.name = p->path;
+ newinfo.namelen = p->pathlen;
+ newinfo.mode = p->mode;
+ newinfo.pathlen = st_add3(newinfo.pathlen, tree_entry_len(p), 1);
newinfo.df_conflicts |= df_conflicts;
/*
@@ -843,7 +917,7 @@ static int traverse_trees_recursive(int n, unsigned long dirmask,
const struct object_id *oid = NULL;
if (dirmask & 1)
oid = &names[i].oid;
- buf[nr_buf++] = fill_tree_descriptor(t + i, oid);
+ buf[nr_buf++] = fill_tree_descriptor(the_repository, t + i, oid);
}
}
@@ -866,14 +940,18 @@ static int traverse_trees_recursive(int n, unsigned long dirmask,
* itself - the caller needs to do the final check for the cache
* entry having more data at the end!
*/
-static int do_compare_entry_piecewise(const struct cache_entry *ce, const struct traverse_info *info, const struct name_entry *n)
+static int do_compare_entry_piecewise(const struct cache_entry *ce,
+ const struct traverse_info *info,
+ const char *name, size_t namelen,
+ unsigned mode)
{
- int len, pathlen, ce_len;
+ int pathlen, ce_len;
const char *ce_name;
if (info->prev) {
int cmp = do_compare_entry_piecewise(ce, info->prev,
- &info->name);
+ info->name, info->namelen,
+ info->mode);
if (cmp)
return cmp;
}
@@ -887,15 +965,15 @@ static int do_compare_entry_piecewise(const struct cache_entry *ce, const struct
ce_len -= pathlen;
ce_name = ce->name + pathlen;
- len = tree_entry_len(n);
- return df_name_compare(ce_name, ce_len, S_IFREG, n->path, len, n->mode);
+ return df_name_compare(ce_name, ce_len, S_IFREG, name, namelen, mode);
}
static int do_compare_entry(const struct cache_entry *ce,
const struct traverse_info *info,
- const struct name_entry *n)
+ const char *name, size_t namelen,
+ unsigned mode)
{
- int len, pathlen, ce_len;
+ int pathlen, ce_len;
const char *ce_name;
int cmp;
@@ -905,7 +983,7 @@ static int do_compare_entry(const struct cache_entry *ce,
* it is quicker to use the precomputed version.
*/
if (!info->traverse_path)
- return do_compare_entry_piecewise(ce, info, n);
+ return do_compare_entry_piecewise(ce, info, name, namelen, mode);
cmp = strncmp(ce->name, info->traverse_path, info->pathlen);
if (cmp)
@@ -920,13 +998,12 @@ static int do_compare_entry(const struct cache_entry *ce,
ce_len -= pathlen;
ce_name = ce->name + pathlen;
- len = tree_entry_len(n);
- return df_name_compare(ce_name, ce_len, S_IFREG, n->path, len, n->mode);
+ return df_name_compare(ce_name, ce_len, S_IFREG, name, namelen, mode);
}
static int compare_entry(const struct cache_entry *ce, const struct traverse_info *info, const struct name_entry *n)
{
- int cmp = do_compare_entry(ce, info, n);
+ int cmp = do_compare_entry(ce, info, n->path, n->pathlen, n->mode);
if (cmp)
return cmp;
@@ -934,7 +1011,7 @@ static int compare_entry(const struct cache_entry *ce, const struct traverse_inf
* Even if the beginning compared identically, the ce should
* compare as bigger than a directory leading up to it!
*/
- return ce_namelen(ce) > traverse_path_len(info, n);
+ return ce_namelen(ce) > traverse_path_len(info, tree_entry_len(n));
}
static int ce_in_traverse_path(const struct cache_entry *ce,
@@ -942,7 +1019,8 @@ static int ce_in_traverse_path(const struct cache_entry *ce,
{
if (!info->prev)
return 1;
- if (do_compare_entry(ce, info->prev, &info->name))
+ if (do_compare_entry(ce, info->prev,
+ info->name, info->namelen, info->mode))
return 0;
/*
* If ce (blob) is the same name as the path (which is a tree
@@ -957,17 +1035,18 @@ static struct cache_entry *create_ce_entry(const struct traverse_info *info,
struct index_state *istate,
int is_transient)
{
- int len = traverse_path_len(info, n);
+ size_t len = traverse_path_len(info, tree_entry_len(n));
struct cache_entry *ce =
is_transient ?
- make_empty_transient_cache_entry(len) :
+ make_empty_transient_cache_entry(len, NULL) :
make_empty_cache_entry(istate, len);
ce->ce_mode = create_ce_mode(n->mode);
ce->ce_flags = create_ce_flags(stage);
ce->ce_namelen = len;
oidcpy(&ce->oid, &n->oid);
- make_traverse_path(ce->name, info, n);
+ /* len+1 because the cache_entry allocates space for NUL */
+ make_traverse_path(ce->name, len + 1, info, n->path, n->pathlen);
return ce;
}
@@ -1060,13 +1139,12 @@ static int unpack_failed(struct unpack_trees_options *o, const char *message)
* the directory.
*/
static int find_cache_pos(struct traverse_info *info,
- const struct name_entry *p)
+ const char *p, size_t p_len)
{
int pos;
struct unpack_trees_options *o = info->data;
struct index_state *index = o->src_index;
int pfxlen = info->pathlen;
- int p_len = tree_entry_len(p);
for (pos = o->cache_bottom; pos < index->cache_nr; pos++) {
const struct cache_entry *ce = index->cache[pos];
@@ -1102,7 +1180,7 @@ static int find_cache_pos(struct traverse_info *info,
ce_len = ce_slash - ce_name;
else
ce_len = ce_namelen(ce) - pfxlen;
- cmp = name_compare(p->path, p_len, ce_name, ce_len);
+ cmp = name_compare(p, p_len, ce_name, ce_len);
/*
* Exact match; if we have a directory we need to
* delay returning it.
@@ -1117,7 +1195,7 @@ static int find_cache_pos(struct traverse_info *info,
* E.g. ce_name == "t-i", and p->path == "t"; we may
* have "t/a" in the index.
*/
- if (p_len < ce_len && !memcmp(ce_name, p->path, p_len) &&
+ if (p_len < ce_len && !memcmp(ce_name, p, p_len) &&
ce_name[p_len] < '/')
continue; /* keep looking */
break;
@@ -1128,7 +1206,7 @@ static int find_cache_pos(struct traverse_info *info,
static struct cache_entry *find_cache_entry(struct traverse_info *info,
const struct name_entry *p)
{
- int pos = find_cache_pos(info, p);
+ int pos = find_cache_pos(info, p->path, p->pathlen);
struct unpack_trees_options *o = info->data;
if (0 <= pos)
@@ -1141,10 +1219,10 @@ static void debug_path(struct traverse_info *info)
{
if (info->prev) {
debug_path(info->prev);
- if (*info->prev->name.path)
+ if (*info->prev->name)
putchar('/');
}
- printf("%s", info->name.path);
+ printf("%s", info->name);
}
static void debug_name_entry(int i, struct name_entry *n)
@@ -1268,7 +1346,9 @@ static int clear_ce_flags_1(struct index_state *istate,
struct cache_entry **cache, int nr,
struct strbuf *prefix,
int select_mask, int clear_mask,
- struct exclude_list *el, int defval);
+ struct pattern_list *pl,
+ enum pattern_match_result default_match,
+ int progress_nr);
/* Whole directory matching */
static int clear_ce_flags_dir(struct index_state *istate,
@@ -1276,19 +1356,24 @@ static int clear_ce_flags_dir(struct index_state *istate,
struct strbuf *prefix,
char *basename,
int select_mask, int clear_mask,
- struct exclude_list *el, int defval)
+ struct pattern_list *pl,
+ enum pattern_match_result default_match,
+ int progress_nr)
{
struct cache_entry **cache_end;
int dtype = DT_DIR;
- int ret = is_excluded_from_list(prefix->buf, prefix->len,
- basename, &dtype, el, istate);
int rc;
+ enum pattern_match_result ret, orig_ret;
+ orig_ret = path_matches_pattern_list(prefix->buf, prefix->len,
+ basename, &dtype, pl, istate);
strbuf_addch(prefix, '/');
/* If undecided, use matching result of parent dir in defval */
- if (ret < 0)
- ret = defval;
+ if (orig_ret == UNDECIDED)
+ ret = default_match;
+ else
+ ret = orig_ret;
for (cache_end = cache; cache_end != cache + nr; cache_end++) {
struct cache_entry *ce = *cache_end;
@@ -1296,24 +1381,31 @@ static int clear_ce_flags_dir(struct index_state *istate,
break;
}
- /*
- * TODO: check el, if there are no patterns that may conflict
- * with ret (iow, we know in advance the incl/excl
- * decision for the entire directory), clear flag here without
- * calling clear_ce_flags_1(). That function will call
- * the expensive is_excluded_from_list() on every entry.
- */
- rc = clear_ce_flags_1(istate, cache, cache_end - cache,
- prefix,
- select_mask, clear_mask,
- el, ret);
+ if (pl->use_cone_patterns && orig_ret == MATCHED_RECURSIVE) {
+ struct cache_entry **ce = cache;
+ rc = cache_end - cache;
+
+ while (ce < cache_end) {
+ (*ce)->ce_flags &= ~clear_mask;
+ ce++;
+ }
+ } else if (pl->use_cone_patterns && orig_ret == NOT_MATCHED) {
+ rc = cache_end - cache;
+ } else {
+ rc = clear_ce_flags_1(istate, cache, cache_end - cache,
+ prefix,
+ select_mask, clear_mask,
+ pl, ret,
+ progress_nr);
+ }
+
strbuf_setlen(prefix, prefix->len - 1);
return rc;
}
/*
* Traverse the index, find every entry that matches according to
- * o->el. Do "ce_flags &= ~clear_mask" on those entries. Return the
+ * o->pl. Do "ce_flags &= ~clear_mask" on those entries. Return the
* number of traversed entries.
*
* If select_mask is non-zero, only entries whose ce_flags has on of
@@ -1330,9 +1422,11 @@ static int clear_ce_flags_1(struct index_state *istate,
struct cache_entry **cache, int nr,
struct strbuf *prefix,
int select_mask, int clear_mask,
- struct exclude_list *el, int defval)
+ struct pattern_list *pl,
+ enum pattern_match_result default_match,
+ int progress_nr)
{
- struct cache_entry **cache_end = cache + nr;
+ struct cache_entry **cache_end = nr ? cache + nr : cache;
/*
* Process all entries that have the given prefix and meet
@@ -1341,10 +1435,14 @@ static int clear_ce_flags_1(struct index_state *istate,
while(cache != cache_end) {
struct cache_entry *ce = *cache;
const char *name, *slash;
- int len, dtype, ret;
+ int len, dtype;
+ enum pattern_match_result ret;
+
+ display_progress(istate->progress, progress_nr);
if (select_mask && !(ce->ce_flags & select_mask)) {
cache++;
+ progress_nr++;
continue;
}
@@ -1365,58 +1463,84 @@ static int clear_ce_flags_1(struct index_state *istate,
prefix,
prefix->buf + prefix->len - len,
select_mask, clear_mask,
- el, defval);
+ pl, default_match,
+ progress_nr);
/* clear_c_f_dir eats a whole dir already? */
if (processed) {
cache += processed;
+ progress_nr += processed;
strbuf_setlen(prefix, prefix->len - len);
continue;
}
strbuf_addch(prefix, '/');
- cache += clear_ce_flags_1(istate, cache, cache_end - cache,
- prefix,
- select_mask, clear_mask, el, defval);
+ processed = clear_ce_flags_1(istate, cache, cache_end - cache,
+ prefix,
+ select_mask, clear_mask, pl,
+ default_match, progress_nr);
+
+ cache += processed;
+ progress_nr += processed;
+
strbuf_setlen(prefix, prefix->len - len - 1);
continue;
}
/* Non-directory */
dtype = ce_to_dtype(ce);
- ret = is_excluded_from_list(ce->name, ce_namelen(ce),
- name, &dtype, el, istate);
- if (ret < 0)
- ret = defval;
- if (ret > 0)
+ ret = path_matches_pattern_list(ce->name,
+ ce_namelen(ce),
+ name, &dtype, pl, istate);
+ if (ret == UNDECIDED)
+ ret = default_match;
+ if (ret == MATCHED || ret == MATCHED_RECURSIVE)
ce->ce_flags &= ~clear_mask;
cache++;
+ progress_nr++;
}
+
+ display_progress(istate->progress, progress_nr);
return nr - (cache_end - cache);
}
static int clear_ce_flags(struct index_state *istate,
int select_mask, int clear_mask,
- struct exclude_list *el)
+ struct pattern_list *pl,
+ int show_progress)
{
static struct strbuf prefix = STRBUF_INIT;
+ char label[100];
+ int rval;
strbuf_reset(&prefix);
-
- return clear_ce_flags_1(istate,
+ if (show_progress)
+ istate->progress = start_delayed_progress(
+ _("Updating index flags"),
+ istate->cache_nr);
+
+ xsnprintf(label, sizeof(label), "clear_ce_flags(0x%08lx,0x%08lx)",
+ (unsigned long)select_mask, (unsigned long)clear_mask);
+ trace2_region_enter("unpack_trees", label, the_repository);
+ rval = clear_ce_flags_1(istate,
istate->cache,
istate->cache_nr,
&prefix,
select_mask, clear_mask,
- el, 0);
+ pl, 0, 0);
+ trace2_region_leave("unpack_trees", label, the_repository);
+
+ stop_progress(&istate->progress);
+ return rval;
}
/*
* Set/Clear CE_NEW_SKIP_WORKTREE according to $GIT_DIR/info/sparse-checkout
*/
-static void mark_new_skip_worktree(struct exclude_list *el,
+static void mark_new_skip_worktree(struct pattern_list *pl,
struct index_state *istate,
- int select_flag, int skip_wt_flag)
+ int select_flag, int skip_wt_flag,
+ int show_progress)
{
int i;
@@ -1440,9 +1564,19 @@ static void mark_new_skip_worktree(struct exclude_list *el,
* 2. Widen worktree according to sparse-checkout file.
* Matched entries will have skip_wt_flag cleared (i.e. "in")
*/
- clear_ce_flags(istate, select_flag, skip_wt_flag, el);
+ clear_ce_flags(istate, select_flag, skip_wt_flag, pl, show_progress);
}
+static void populate_from_existing_patterns(struct unpack_trees_options *o,
+ struct pattern_list *pl)
+{
+ if (get_sparse_checkout_patterns(pl) < 0)
+ o->skip_sparse_checkout = 1;
+ else
+ o->pl = pl;
+}
+
+
static int verify_absent(const struct cache_entry *,
enum unpack_trees_error_types,
struct unpack_trees_options *);
@@ -1454,24 +1588,30 @@ static int verify_absent(const struct cache_entry *,
*/
int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options *o)
{
+ struct repository *repo = the_repository;
int i, ret;
static struct cache_entry *dfc;
- struct exclude_list el;
+ struct pattern_list pl;
+ int free_pattern_list = 0;
if (len > MAX_UNPACK_TREES)
die("unpack_trees takes at most %d trees", MAX_UNPACK_TREES);
trace_performance_enter();
- memset(&el, 0, sizeof(el));
+ trace2_region_enter("unpack_trees", "unpack_trees", the_repository);
+
+ prepare_repo_settings(repo);
+ if (repo->settings.command_requires_full_index) {
+ ensure_full_index(o->src_index);
+ ensure_full_index(o->dst_index);
+ }
+
if (!core_apply_sparse_checkout || !o->update)
o->skip_sparse_checkout = 1;
- if (!o->skip_sparse_checkout) {
- char *sparse = git_pathdup("info/sparse-checkout");
- if (add_excludes_from_file_to_list(sparse, "", 0, &el, NULL) < 0)
- o->skip_sparse_checkout = 1;
- else
- o->el = &el;
- free(sparse);
+ if (!o->skip_sparse_checkout && !o->pl) {
+ memset(&pl, 0, sizeof(pl));
+ free_pattern_list = 1;
+ populate_from_existing_patterns(o, &pl);
}
memset(&o->result, 0, sizeof(o->result));
@@ -1497,11 +1637,15 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
o->merge_size = len;
mark_all_ce_unused(o->src_index);
+ o->result.fsmonitor_last_update =
+ xstrdup_or_null(o->src_index->fsmonitor_last_update);
+
/*
* Sparse checkout loop #1: set NEW_SKIP_WORKTREE on existing entries
*/
if (!o->skip_sparse_checkout)
- mark_new_skip_worktree(o->el, o->src_index, 0, CE_NEW_SKIP_WORKTREE);
+ mark_new_skip_worktree(o->pl, o->src_index, 0,
+ CE_NEW_SKIP_WORKTREE, o->verbose_update);
if (!dfc)
dfc = xcalloc(1, cache_entry_size(0));
@@ -1535,7 +1679,9 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
}
trace_performance_enter();
+ trace2_region_enter("unpack_trees", "traverse_trees", the_repository);
ret = traverse_trees(o->src_index, len, t, &info);
+ trace2_region_leave("unpack_trees", "traverse_trees", the_repository);
trace_performance_leave("traverse_trees");
if (ret < 0)
goto return_failed;
@@ -1559,14 +1705,14 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
}
if (!o->skip_sparse_checkout) {
- int empty_worktree = 1;
-
/*
* Sparse checkout loop #2: set NEW_SKIP_WORKTREE on entries not in loop #1
- * If the will have NEW_SKIP_WORKTREE, also set CE_SKIP_WORKTREE
+ * If they will have NEW_SKIP_WORKTREE, also set CE_SKIP_WORKTREE
* so apply_sparse_checkout() won't attempt to remove it from worktree
*/
- mark_new_skip_worktree(o->el, &o->result, CE_ADDED, CE_SKIP_WORKTREE | CE_NEW_SKIP_WORKTREE);
+ mark_new_skip_worktree(o->pl, &o->result,
+ CE_ADDED, CE_SKIP_WORKTREE | CE_NEW_SKIP_WORKTREE,
+ o->verbose_update);
ret = 0;
for (i = 0; i < o->result.cache_nr; i++) {
@@ -1581,43 +1727,29 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
* correct CE_NEW_SKIP_WORKTREE
*/
if (ce->ce_flags & CE_ADDED &&
- verify_absent(ce, ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN, o)) {
- if (!o->show_all_errors)
- goto return_failed;
- ret = -1;
- }
-
- if (apply_sparse_checkout(&o->result, ce, o)) {
- if (!o->show_all_errors)
- goto return_failed;
- ret = -1;
- }
- if (!ce_skip_worktree(ce))
- empty_worktree = 0;
+ verify_absent(ce, WARNING_SPARSE_ORPHANED_NOT_OVERWRITTEN, o))
+ ret = 1;
+ if (apply_sparse_checkout(&o->result, ce, o))
+ ret = 1;
}
- if (ret < 0)
- goto return_failed;
- /*
- * Sparse checkout is meant to narrow down checkout area
- * but it does not make sense to narrow down to empty working
- * tree. This is usually a mistake in sparse checkout rules.
- * Do not allow users to do that.
- */
- if (o->result.cache_nr && empty_worktree) {
- ret = unpack_failed(o, "Sparse checkout leaves no entry on working directory");
- goto done;
+ if (ret == 1) {
+ /*
+ * Inability to sparsify or de-sparsify individual
+ * paths is not an error, but just a warning.
+ */
+ if (o->show_all_errors)
+ display_warning_msgs(o);
+ ret = 0;
}
}
- ret = check_updates(o) ? (-2) : 0;
+ ret = check_updates(o, &o->result) ? (-2) : 0;
if (o->dst_index) {
move_index_extensions(&o->result, o->src_index);
if (!ret) {
if (git_env_bool("GIT_TEST_CHECK_CACHE_TREE", 0))
cache_tree_verify(the_repository, &o->result);
- if (!o->result.cache_tree)
- o->result.cache_tree = cache_tree();
if (!cache_tree_fully_valid(o->result.cache_tree))
cache_tree_update(&o->result,
WRITE_TREE_SILENT |
@@ -1633,8 +1765,10 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
o->src_index = NULL;
done:
+ if (free_pattern_list)
+ clear_pattern_list(&pl);
+ trace2_region_leave("unpack_trees", "unpack_trees", the_repository);
trace_performance_leave("unpack_trees");
- clear_exclude_list(&el);
return ret;
return_failed:
@@ -1647,6 +1781,74 @@ return_failed:
goto done;
}
+/*
+ * Update SKIP_WORKTREE bits according to sparsity patterns, and update
+ * working directory to match.
+ *
+ * CE_NEW_SKIP_WORKTREE is used internally.
+ */
+enum update_sparsity_result update_sparsity(struct unpack_trees_options *o)
+{
+ enum update_sparsity_result ret = UPDATE_SPARSITY_SUCCESS;
+ struct pattern_list pl;
+ int i;
+ unsigned old_show_all_errors;
+ int free_pattern_list = 0;
+
+ old_show_all_errors = o->show_all_errors;
+ o->show_all_errors = 1;
+
+ /* Sanity checks */
+ if (!o->update || o->index_only || o->skip_sparse_checkout)
+ BUG("update_sparsity() is for reflecting sparsity patterns in working directory");
+ if (o->src_index != o->dst_index || o->fn)
+ BUG("update_sparsity() called wrong");
+
+ trace_performance_enter();
+
+ /* If we weren't given patterns, use the recorded ones */
+ if (!o->pl) {
+ memset(&pl, 0, sizeof(pl));
+ free_pattern_list = 1;
+ populate_from_existing_patterns(o, &pl);
+ if (o->skip_sparse_checkout)
+ goto skip_sparse_checkout;
+ }
+
+ /* Set NEW_SKIP_WORKTREE on existing entries. */
+ mark_all_ce_unused(o->src_index);
+ mark_new_skip_worktree(o->pl, o->src_index, 0,
+ CE_NEW_SKIP_WORKTREE, o->verbose_update);
+
+ /* Then loop over entries and update/remove as needed */
+ ret = UPDATE_SPARSITY_SUCCESS;
+ for (i = 0; i < o->src_index->cache_nr; i++) {
+ struct cache_entry *ce = o->src_index->cache[i];
+
+
+ if (ce_stage(ce)) {
+ /* -1 because for loop will increment by 1 */
+ i += warn_conflicted_path(o->src_index, i, o) - 1;
+ ret = UPDATE_SPARSITY_WARNINGS;
+ continue;
+ }
+
+ if (apply_sparse_checkout(o->src_index, ce, o))
+ ret = UPDATE_SPARSITY_WARNINGS;
+ }
+
+skip_sparse_checkout:
+ if (check_updates(o, o->src_index))
+ ret = UPDATE_SPARSITY_WORKTREE_UPDATE_FAILURES;
+
+ display_warning_msgs(o);
+ o->show_all_errors = old_show_all_errors;
+ if (free_pattern_list)
+ clear_pattern_list(&pl);
+ trace_performance_leave("update_sparsity");
+ return ret;
+}
+
/* Here come the merge functions */
static int reject_merge(const struct cache_entry *ce,
@@ -1731,7 +1933,7 @@ int verify_uptodate(const struct cache_entry *ce,
static int verify_uptodate_sparse(const struct cache_entry *ce,
struct unpack_trees_options *o)
{
- return verify_uptodate_1(ce, o, ERROR_SPARSE_NOT_UPTODATE_FILE);
+ return verify_uptodate_1(ce, o, WARNING_SPARSE_NOT_UPTODATE_FILE);
}
/*
@@ -1757,9 +1959,6 @@ static void invalidate_ce_path(const struct cache_entry *ce,
/*
* Check that checking out ce->sha1 in subdir ce->name is not
* going to overwrite any working files.
- *
- * Currently, git does not checkout subprojects during a superproject
- * checkout, so it is not going to overwrite anything.
*/
static int verify_clean_submodule(const char *old_sha1,
const struct cache_entry *ce,
@@ -1923,7 +2122,7 @@ static int verify_absent_1(const struct cache_entry *ce,
if (o->index_only || o->reset || !o->update)
return 0;
- len = check_leading_path(ce->name, ce_namelen(ce));
+ len = check_leading_path(ce->name, ce_namelen(ce), 0);
if (!len)
return 0;
else if (len > 0) {
@@ -1972,11 +2171,7 @@ static int verify_absent_sparse(const struct cache_entry *ce,
enum unpack_trees_error_types error_type,
struct unpack_trees_options *o)
{
- enum unpack_trees_error_types orphaned_error = error_type;
- if (orphaned_error == ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN)
- orphaned_error = ERROR_WOULD_LOSE_ORPHANED_OVERWRITTEN;
-
- return verify_absent_1(ce, orphaned_error, o);
+ return verify_absent_1(ce, error_type, o);
}
static int merged_entry(const struct cache_entry *ce,
@@ -2009,7 +2204,7 @@ static int merged_entry(const struct cache_entry *ce,
}
invalidate_ce_path(merge, o);
- if (submodule_from_ce(ce)) {
+ if (submodule_from_ce(ce) && file_exists(ce->name)) {
int ret = check_submodule_move_head(ce, NULL,
oid_to_hex(&ce->oid),
o);
@@ -2038,7 +2233,7 @@ static int merged_entry(const struct cache_entry *ce,
invalidate_ce_path(old, o);
}
- if (submodule_from_ce(ce)) {
+ if (submodule_from_ce(ce) && file_exists(ce->name)) {
int ret = check_submodule_move_head(ce, oid_to_hex(&old->oid),
oid_to_hex(&ce->oid),
o);
@@ -2378,7 +2573,8 @@ int oneway_merge(const struct cache_entry * const *src,
if (old && same(old, a)) {
int update = 0;
- if (o->reset && o->update && !ce_uptodate(old) && !ce_skip_worktree(old)) {
+ if (o->reset && o->update && !ce_uptodate(old) && !ce_skip_worktree(old) &&
+ !(old->ce_flags & CE_FSMONITOR_VALID)) {
struct stat st;
if (lstat(old->name, &st) ||
ie_match_stat(o->src_index, old, &st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE))
@@ -2392,3 +2588,25 @@ int oneway_merge(const struct cache_entry * const *src,
}
return merged_entry(a, old, o);
}
+
+/*
+ * Merge worktree and untracked entries in a stash entry.
+ *
+ * Ignore all index entries. Collapse remaining trees but make sure that they
+ * don't have any conflicting files.
+ */
+int stash_worktree_untracked_merge(const struct cache_entry * const *src,
+ struct unpack_trees_options *o)
+{
+ const struct cache_entry *worktree = src[1];
+ const struct cache_entry *untracked = src[2];
+
+ if (o->merge_size != 2)
+ BUG("invalid merge_size: %d", o->merge_size);
+
+ if (worktree && untracked)
+ return error(_("worktree and untracked commit have duplicate entries: %s"),
+ super_prefixed(worktree->name));
+
+ return merged_entry(worktree ? worktree : untracked, NULL, o);
+}