summaryrefslogtreecommitdiff
path: root/unpack-trees.c
diff options
context:
space:
mode:
Diffstat (limited to 'unpack-trees.c')
-rw-r--r--unpack-trees.c263
1 files changed, 213 insertions, 50 deletions
diff --git a/unpack-trees.c b/unpack-trees.c
index f88a69f8e7..a7e1712d23 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -111,17 +111,17 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts,
strvec_init(&opts->msgs_to_free);
if (!strcmp(cmd, "checkout"))
- msg = advice_commit_before_merge
+ msg = advice_enabled(ADVICE_COMMIT_BEFORE_MERGE)
? _("Your local changes to the following files would be overwritten by checkout:\n%%s"
"Please commit your changes or stash them before you switch branches.")
: _("Your local changes to the following files would be overwritten by checkout:\n%%s");
else if (!strcmp(cmd, "merge"))
- msg = advice_commit_before_merge
+ msg = advice_enabled(ADVICE_COMMIT_BEFORE_MERGE)
? _("Your local changes to the following files would be overwritten by merge:\n%%s"
"Please commit your changes or stash them before you merge.")
: _("Your local changes to the following files would be overwritten by merge:\n%%s");
else
- msg = advice_commit_before_merge
+ msg = advice_enabled(ADVICE_COMMIT_BEFORE_MERGE)
? _("Your local changes to the following files would be overwritten by %s:\n%%s"
"Please commit your changes or stash them before you %s.")
: _("Your local changes to the following files would be overwritten by %s:\n%%s");
@@ -132,17 +132,17 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts,
_("Updating the following directories would lose untracked files in them:\n%s");
if (!strcmp(cmd, "checkout"))
- msg = advice_commit_before_merge
+ msg = advice_enabled(ADVICE_COMMIT_BEFORE_MERGE)
? _("The following untracked working tree files would be removed by checkout:\n%%s"
"Please move or remove them before you switch branches.")
: _("The following untracked working tree files would be removed by checkout:\n%%s");
else if (!strcmp(cmd, "merge"))
- msg = advice_commit_before_merge
+ msg = advice_enabled(ADVICE_COMMIT_BEFORE_MERGE)
? _("The following untracked working tree files would be removed by merge:\n%%s"
"Please move or remove them before you merge.")
: _("The following untracked working tree files would be removed by merge:\n%%s");
else
- msg = advice_commit_before_merge
+ msg = advice_enabled(ADVICE_COMMIT_BEFORE_MERGE)
? _("The following untracked working tree files would be removed by %s:\n%%s"
"Please move or remove them before you %s.")
: _("The following untracked working tree files would be removed by %s:\n%%s");
@@ -150,17 +150,17 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts,
strvec_pushf(&opts->msgs_to_free, msg, cmd, cmd);
if (!strcmp(cmd, "checkout"))
- msg = advice_commit_before_merge
+ msg = advice_enabled(ADVICE_COMMIT_BEFORE_MERGE)
? _("The following untracked working tree files would be overwritten by checkout:\n%%s"
"Please move or remove them before you switch branches.")
: _("The following untracked working tree files would be overwritten by checkout:\n%%s");
else if (!strcmp(cmd, "merge"))
- msg = advice_commit_before_merge
+ msg = advice_enabled(ADVICE_COMMIT_BEFORE_MERGE)
? _("The following untracked working tree files would be overwritten by merge:\n%%s"
"Please move or remove them before you merge.")
: _("The following untracked working tree files would be overwritten by merge:\n%%s");
else
- msg = advice_commit_before_merge
+ msg = advice_enabled(ADVICE_COMMIT_BEFORE_MERGE)
? _("The following untracked working tree files would be overwritten by %s:\n%%s"
"Please move or remove them before you %s.")
: _("The following untracked working tree files would be overwritten by %s:\n%%s");
@@ -392,6 +392,11 @@ static void report_collided_checkout(struct index_state *index)
string_list_clear(&list, 0);
}
+static int must_checkout(const struct cache_entry *ce)
+{
+ return ce->ce_flags & CE_UPDATE;
+}
+
static int check_updates(struct unpack_trees_options *o,
struct index_state *index)
{
@@ -442,28 +447,12 @@ static int check_updates(struct unpack_trees_options *o,
if (should_update_submodules())
load_gitmodules_file(index, &state);
- if (has_promisor_remote()) {
+ if (has_promisor_remote())
/*
* Prefetch the objects that are to be checked out in the loop
* below.
*/
- struct oid_array to_fetch = OID_ARRAY_INIT;
- for (i = 0; i < index->cache_nr; i++) {
- struct cache_entry *ce = index->cache[i];
-
- if (!(ce->ce_flags & CE_UPDATE) ||
- S_ISGITLINK(ce->ce_mode))
- continue;
- if (!oid_object_info_extended(the_repository, &ce->oid,
- NULL,
- OBJECT_INFO_FOR_PREFETCH))
- continue;
- oid_array_append(&to_fetch, &ce->oid);
- }
- promisor_remote_get_direct(the_repository,
- to_fetch.oid, to_fetch.nr);
- oid_array_clear(&to_fetch);
- }
+ prefetch_cache_entries(index, must_checkout);
get_parallel_checkout_configs(&pc_workers, &pc_threshold);
@@ -473,7 +462,7 @@ static int check_updates(struct unpack_trees_options *o,
for (i = 0; i < index->cache_nr; i++) {
struct cache_entry *ce = index->cache[i];
- if (ce->ce_flags & CE_UPDATE) {
+ if (must_checkout(ce)) {
size_t last_pc_queue_size = pc_queue_size();
if (ce->ce_flags & CE_WT_REMOVE)
@@ -490,7 +479,7 @@ static int check_updates(struct unpack_trees_options *o,
errs |= run_parallel_checkout(&state, pc_workers, pc_threshold,
progress, &cnt);
stop_progress(&progress);
- errs |= finish_delayed_checkout(&state, NULL);
+ errs |= finish_delayed_checkout(&state, NULL, o->verbose_update);
git_attr_set_direction(GIT_ATTR_CHECKIN);
if (o->clone)
@@ -600,6 +589,13 @@ static void mark_ce_used(struct cache_entry *ce, struct unpack_trees_options *o)
{
ce->ce_flags |= CE_UNPACKED;
+ /*
+ * If this is a sparse directory, don't advance cache_bottom.
+ * That will be advanced later using the cache-tree data.
+ */
+ if (S_ISSPARSEDIR(ce->ce_mode))
+ return;
+
if (o->cache_bottom < o->src_index->cache_nr &&
o->src_index->cache[o->cache_bottom] == ce) {
int bottom = o->cache_bottom;
@@ -797,7 +793,7 @@ static int traverse_by_cache_tree(int pos, int nr_entries, int nr_names,
BUG("We need cache-tree to do this optimization");
/*
- * Do what unpack_callback() and unpack_nondirectories() normally
+ * Do what unpack_callback() and unpack_single_entry() normally
* do. But we walk all paths in an iterative loop instead.
*
* D/F conflicts and higher stage entries are not a concern
@@ -976,6 +972,7 @@ static int do_compare_entry(const struct cache_entry *ce,
int pathlen, ce_len;
const char *ce_name;
int cmp;
+ unsigned ce_mode;
/*
* If we have not precomputed the traverse path, it is quicker
@@ -998,7 +995,8 @@ static int do_compare_entry(const struct cache_entry *ce,
ce_len -= pathlen;
ce_name = ce->name + pathlen;
- return df_name_compare(ce_name, ce_len, S_IFREG, name, namelen, mode);
+ ce_mode = S_ISSPARSEDIR(ce->ce_mode) ? S_IFDIR : S_IFREG;
+ return df_name_compare(ce_name, ce_len, ce_mode, name, namelen, mode);
}
static int compare_entry(const struct cache_entry *ce, const struct traverse_info *info, const struct name_entry *n)
@@ -1008,6 +1006,16 @@ static int compare_entry(const struct cache_entry *ce, const struct traverse_inf
return cmp;
/*
+ * At this point, we know that we have a prefix match. If ce
+ * is a sparse directory, then allow an exact match. This only
+ * works when the input name is a directory, since ce->name
+ * ends in a directory separator.
+ */
+ if (S_ISSPARSEDIR(ce->ce_mode) &&
+ ce->ce_namelen == traverse_path_len(info, tree_entry_len(n)) + 1)
+ return 0;
+
+ /*
* Even if the beginning compared identically, the ce should
* compare as bigger than a directory leading up to it!
*/
@@ -1033,13 +1041,15 @@ static struct cache_entry *create_ce_entry(const struct traverse_info *info,
const struct name_entry *n,
int stage,
struct index_state *istate,
- int is_transient)
+ int is_transient,
+ int is_sparse_directory)
{
size_t len = traverse_path_len(info, tree_entry_len(n));
+ size_t alloc_len = is_sparse_directory ? len + 1 : len;
struct cache_entry *ce =
is_transient ?
- make_empty_transient_cache_entry(len, NULL) :
- make_empty_cache_entry(istate, len);
+ make_empty_transient_cache_entry(alloc_len, NULL) :
+ make_empty_cache_entry(istate, alloc_len);
ce->ce_mode = create_ce_mode(n->mode);
ce->ce_flags = create_ce_flags(stage);
@@ -1048,6 +1058,13 @@ static struct cache_entry *create_ce_entry(const struct traverse_info *info,
/* len+1 because the cache_entry allocates space for NUL */
make_traverse_path(ce->name, len + 1, info, n->path, n->pathlen);
+ if (is_sparse_directory) {
+ ce->name[len] = '/';
+ ce->name[len + 1] = '\0';
+ ce->ce_namelen++;
+ ce->ce_flags |= CE_SKIP_WORKTREE;
+ }
+
return ce;
}
@@ -1056,21 +1073,28 @@ static struct cache_entry *create_ce_entry(const struct traverse_info *info,
* without actually calling it. If you change the logic here you may need to
* check and change there as well.
*/
-static int unpack_nondirectories(int n, unsigned long mask,
- unsigned long dirmask,
- struct cache_entry **src,
- const struct name_entry *names,
- const struct traverse_info *info)
+static int unpack_single_entry(int n, unsigned long mask,
+ unsigned long dirmask,
+ struct cache_entry **src,
+ const struct name_entry *names,
+ const struct traverse_info *info)
{
int i;
struct unpack_trees_options *o = info->data;
unsigned long conflicts = info->df_conflicts | dirmask;
- /* Do we have *only* directories? Nothing to do */
if (mask == dirmask && !src[0])
return 0;
/*
+ * When we have a sparse directory entry for src[0],
+ * then this isn't necessarily a directory-file conflict.
+ */
+ if (mask == dirmask && src[0] &&
+ S_ISSPARSEDIR(src[0]->ce_mode))
+ conflicts = 0;
+
+ /*
* Ok, we've filled in up to any potential index entry in src[0],
* now do the rest.
*/
@@ -1099,7 +1123,9 @@ static int unpack_nondirectories(int n, unsigned long mask,
* not stored in the index. otherwise construct the
* cache entry from the index aware logic.
*/
- src[i + o->merge] = create_ce_entry(info, names + i, stage, &o->result, o->merge);
+ src[i + o->merge] = create_ce_entry(info, names + i, stage,
+ &o->result, o->merge,
+ bit & dirmask);
}
if (o->merge) {
@@ -1203,16 +1229,73 @@ static int find_cache_pos(struct traverse_info *info,
return -1;
}
+/*
+ * Given a sparse directory entry 'ce', compare ce->name to
+ * info->name + '/' + p->path + '/' if info->name is non-empty.
+ * Compare ce->name to p->path + '/' otherwise. Note that
+ * ce->name must end in a trailing '/' because it is a sparse
+ * directory entry.
+ */
+static int sparse_dir_matches_path(const struct cache_entry *ce,
+ struct traverse_info *info,
+ const struct name_entry *p)
+{
+ assert(S_ISSPARSEDIR(ce->ce_mode));
+ assert(ce->name[ce->ce_namelen - 1] == '/');
+
+ if (info->namelen)
+ return ce->ce_namelen == info->namelen + p->pathlen + 2 &&
+ ce->name[info->namelen] == '/' &&
+ !strncmp(ce->name, info->name, info->namelen) &&
+ !strncmp(ce->name + info->namelen + 1, p->path, p->pathlen);
+ return ce->ce_namelen == p->pathlen + 1 &&
+ !strncmp(ce->name, p->path, p->pathlen);
+}
+
static struct cache_entry *find_cache_entry(struct traverse_info *info,
const struct name_entry *p)
{
+ const char *path;
int pos = find_cache_pos(info, p->path, p->pathlen);
struct unpack_trees_options *o = info->data;
if (0 <= pos)
return o->src_index->cache[pos];
- else
+
+ /*
+ * Check for a sparse-directory entry named "path/".
+ * Due to the input p->path not having a trailing
+ * slash, the negative 'pos' value overshoots the
+ * expected position, hence "-2" instead of "-1".
+ */
+ pos = -pos - 2;
+
+ if (pos < 0 || pos >= o->src_index->cache_nr)
return NULL;
+
+ /*
+ * Due to lexicographic sorting and sparse directory
+ * entries ending with a trailing slash, our path as a
+ * sparse directory (e.g "subdir/") and our path as a
+ * file (e.g. "subdir") might be separated by other
+ * paths (e.g. "subdir-").
+ */
+ while (pos >= 0) {
+ struct cache_entry *ce = o->src_index->cache[pos];
+
+ if (!skip_prefix(ce->name, info->traverse_path, &path) ||
+ strncmp(path, p->path, p->pathlen) ||
+ path[p->pathlen] != '/')
+ return NULL;
+
+ if (S_ISSPARSEDIR(ce->ce_mode) &&
+ sparse_dir_matches_path(ce, info, p))
+ return ce;
+
+ pos--;
+ }
+
+ return NULL;
}
static void debug_path(struct traverse_info *info)
@@ -1248,6 +1331,21 @@ static void debug_unpack_callback(int n,
}
/*
+ * Returns true if and only if the given cache_entry is a
+ * sparse-directory entry that matches the given name_entry
+ * from the tree walk at the given traverse_info.
+ */
+static int is_sparse_directory_entry(struct cache_entry *ce,
+ struct name_entry *name,
+ struct traverse_info *info)
+{
+ if (!ce || !name || !S_ISSPARSEDIR(ce->ce_mode))
+ return 0;
+
+ return sparse_dir_matches_path(ce, info, name);
+}
+
+/*
* Note that traverse_by_cache_tree() duplicates some logic in this function
* without actually calling it. If you change the logic here you may need to
* check and change there as well.
@@ -1303,7 +1401,7 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str
}
}
- if (unpack_nondirectories(n, mask, dirmask, src, names, info) < 0)
+ if (unpack_single_entry(n, mask, dirmask, src, names, info) < 0)
return -1;
if (o->merge && src[0]) {
@@ -1333,9 +1431,12 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str
}
}
- if (traverse_trees_recursive(n, dirmask, mask & ~dirmask,
- names, info) < 0)
+ if (!is_sparse_directory_entry(src[0], names, info) &&
+ traverse_trees_recursive(n, dirmask, mask & ~dirmask,
+ names, info) < 0) {
return -1;
+ }
+
return mask;
}
@@ -1593,9 +1694,15 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
static struct cache_entry *dfc;
struct pattern_list pl;
int free_pattern_list = 0;
+ struct dir_struct dir = DIR_INIT;
+
+ if (o->reset == UNPACK_RESET_INVALID)
+ BUG("o->reset had a value of 1; should be UNPACK_TREES_*_UNTRACKED");
if (len > MAX_UNPACK_TREES)
die("unpack_trees takes at most %d trees", MAX_UNPACK_TREES);
+ if (o->dir)
+ BUG("o->dir is for internal use only");
trace_performance_enter();
trace2_region_enter("unpack_trees", "unpack_trees", the_repository);
@@ -1606,6 +1713,16 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
ensure_full_index(o->dst_index);
}
+ if (o->reset == UNPACK_RESET_OVERWRITE_UNTRACKED &&
+ o->preserve_ignored)
+ BUG("UNPACK_RESET_OVERWRITE_UNTRACKED incompatible with preserved ignored files");
+
+ if (!o->preserve_ignored) {
+ o->dir = &dir;
+ o->dir->flags |= DIR_SHOW_IGNORED;
+ setup_standard_excludes(o->dir);
+ }
+
if (!core_apply_sparse_checkout || !o->update)
o->skip_sparse_checkout = 1;
if (!o->skip_sparse_checkout && !o->pl) {
@@ -1767,6 +1884,10 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
done:
if (free_pattern_list)
clear_pattern_list(&pl);
+ if (o->dir) {
+ dir_clear(o->dir);
+ o->dir = NULL;
+ }
trace2_region_leave("unpack_trees", "unpack_trees", the_repository);
trace_performance_leave("unpack_trees");
return ret;
@@ -2057,9 +2178,15 @@ static int icase_exists(struct unpack_trees_options *o, const char *name, int le
return src && !ie_match_stat(o->src_index, src, st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE);
}
+enum absent_checking_type {
+ COMPLETELY_ABSENT,
+ ABSENT_ANY_DIRECTORY
+};
+
static int check_ok_to_remove(const char *name, int len, int dtype,
const struct cache_entry *ce, struct stat *st,
enum unpack_trees_error_types error_type,
+ enum absent_checking_type absent_type,
struct unpack_trees_options *o)
{
const struct cache_entry *result;
@@ -2094,6 +2221,10 @@ static int check_ok_to_remove(const char *name, int len, int dtype,
return 0;
}
+ /* If we only care about directories, then we can remove */
+ if (absent_type == ABSENT_ANY_DIRECTORY)
+ return 0;
+
/*
* The previous round may already have decided to
* delete this path, which is in a subdirectory that
@@ -2114,12 +2245,14 @@ static int check_ok_to_remove(const char *name, int len, int dtype,
*/
static int verify_absent_1(const struct cache_entry *ce,
enum unpack_trees_error_types error_type,
+ enum absent_checking_type absent_type,
struct unpack_trees_options *o)
{
int len;
struct stat st;
- if (o->index_only || o->reset || !o->update)
+ if (o->index_only || !o->update ||
+ o->reset == UNPACK_RESET_OVERWRITE_UNTRACKED)
return 0;
len = check_leading_path(ce->name, ce_namelen(ce), 0);
@@ -2139,7 +2272,8 @@ static int verify_absent_1(const struct cache_entry *ce,
NULL, o);
else
ret = check_ok_to_remove(path, len, DT_UNKNOWN, NULL,
- &st, error_type, o);
+ &st, error_type,
+ absent_type, o);
}
free(path);
return ret;
@@ -2154,7 +2288,7 @@ static int verify_absent_1(const struct cache_entry *ce,
return check_ok_to_remove(ce->name, ce_namelen(ce),
ce_to_dtype(ce), ce, &st,
- error_type, o);
+ error_type, absent_type, o);
}
}
@@ -2164,14 +2298,23 @@ static int verify_absent(const struct cache_entry *ce,
{
if (!o->skip_sparse_checkout && (ce->ce_flags & CE_NEW_SKIP_WORKTREE))
return 0;
- return verify_absent_1(ce, error_type, o);
+ return verify_absent_1(ce, error_type, COMPLETELY_ABSENT, o);
+}
+
+static int verify_absent_if_directory(const struct cache_entry *ce,
+ enum unpack_trees_error_types error_type,
+ struct unpack_trees_options *o)
+{
+ if (!o->skip_sparse_checkout && (ce->ce_flags & CE_NEW_SKIP_WORKTREE))
+ return 0;
+ return verify_absent_1(ce, error_type, ABSENT_ANY_DIRECTORY, o);
}
static int verify_absent_sparse(const struct cache_entry *ce,
enum unpack_trees_error_types error_type,
struct unpack_trees_options *o)
{
- return verify_absent_1(ce, error_type, o);
+ return verify_absent_1(ce, error_type, COMPLETELY_ABSENT, o);
}
static int merged_entry(const struct cache_entry *ce,
@@ -2245,6 +2388,12 @@ static int merged_entry(const struct cache_entry *ce,
* Previously unmerged entry left as an existence
* marker by read_index_unmerged();
*/
+ if (verify_absent_if_directory(merge,
+ ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN, o)) {
+ discard_cache_entry(merge);
+ return -1;
+ }
+
invalidate_ce_path(old, o);
}
@@ -2262,7 +2411,10 @@ static int deleted_entry(const struct cache_entry *ce,
if (verify_absent(ce, ERROR_WOULD_LOSE_UNTRACKED_REMOVED, o))
return -1;
return 0;
+ } else if (verify_absent_if_directory(ce, ERROR_WOULD_LOSE_UNTRACKED_REMOVED, o)) {
+ return -1;
}
+
if (!(old->ce_flags & CE_CONFLICTED) && verify_uptodate(old, o))
return -1;
add_entry(o, ce, CE_REMOVE, 0);
@@ -2509,6 +2661,17 @@ int twoway_merge(const struct cache_entry * const *src,
same(current, oldtree) && !same(current, newtree)) {
/* 20 or 21 */
return merged_entry(newtree, current, o);
+ } else if (current && !oldtree && newtree &&
+ S_ISSPARSEDIR(current->ce_mode) != S_ISSPARSEDIR(newtree->ce_mode) &&
+ ce_stage(current) == 0) {
+ /*
+ * This case is a directory/file conflict across the sparse-index
+ * boundary. When we are changing from one path to another via
+ * 'git checkout', then we want to replace one entry with another
+ * via merged_entry(). If there are staged changes, then we should
+ * reject the merge instead.
+ */
+ return merged_entry(newtree, current, o);
} else
return reject_merge(current, o);
}