install_branch_config(0, head, option_origin, our->name);
}
} else if (our) {
- struct commit *c = lookup_commit_reference(&our->old_oid);
+ struct commit *c = lookup_commit_reference(the_repository,
+ &our->old_oid);
/* --branch specifies a non-branch (i.e. tags), detach HEAD */
update_ref(msg, "HEAD", &c->object.oid, NULL, REF_NO_DEREF,
UPDATE_REFS_DIE_ON_ERR);
memset(&opts, 0, sizeof opts);
opts.update = 1;
opts.merge = 1;
+ opts.clone = 1;
opts.fn = oneway_merge;
opts.verbose_update = (option_verbosity >= 0);
opts.src_index = &the_index;
int err = 0, complete_refs_before_fetch = 1;
int submodule_progress;
- struct refspec_item refspec;
+ struct refspec rs = REFSPEC_INIT_FETCH;
+ struct argv_array ref_prefixes = ARGV_ARRAY_INIT;
fetch_if_missing = 0;
if (option_required_reference.nr || option_optional_reference.nr)
setup_reference();
- refspec_item_init_or_die(&refspec, value.buf, REFSPEC_FETCH);
+ refspec_append(&rs, value.buf);
strbuf_reset(&value);
if (transport->smart_options && !deepen && !filter_options.choice)
transport->smart_options->check_self_contained_and_connected = 1;
- refs = transport_get_remote_refs(transport, NULL);
+
+ argv_array_push(&ref_prefixes, "HEAD");
+ refspec_ref_prefixes(&rs, &ref_prefixes);
+ if (option_branch)
+ expand_ref_prefix(&ref_prefixes, option_branch);
+ if (!option_no_tags)
+ argv_array_push(&ref_prefixes, "refs/tags/");
+
+ refs = transport_get_remote_refs(transport, &ref_prefixes);
if (refs) {
- mapped_refs = wanted_peer_refs(refs, &refspec);
+ mapped_refs = wanted_peer_refs(refs, &rs.items[0]);
/*
* transport_get_remote_refs() may return refs with null sha-1
* in mapped_refs (see struct transport->get_refs_list
}
if (!is_local && !complete_refs_before_fetch)
- transport_fetch_refs(transport, mapped_refs, NULL);
+ transport_fetch_refs(transport, mapped_refs);
remote_head = find_ref_by_name(refs, "HEAD");
remote_head_points_at =
if (is_local)
clone_local(path, git_dir);
else if (refs && complete_refs_before_fetch)
- transport_fetch_refs(transport, mapped_refs, NULL);
+ transport_fetch_refs(transport, mapped_refs);
update_remote_refs(refs, mapped_refs, remote_head_points_at,
branch_top.buf, reflog_msg.buf, transport,
strbuf_release(&value);
junk_mode = JUNK_LEAVE_ALL;
- refspec_item_clear(&refspec);
+ refspec_clear(&rs);
+ argv_array_clear(&ref_prefixes);
return err;
}
#include "path.h"
#include "sha1-array.h"
#include "repository.h"
+#include "mem-pool.h"
#include <zlib.h>
typedef struct git_zstream {
struct stat_data ce_stat_data;
unsigned int ce_mode;
unsigned int ce_flags;
+ unsigned int mem_pool_allocated;
unsigned int ce_namelen;
unsigned int index; /* for link extension */
struct object_id oid;
/* Forward structure decls */
struct pathspec;
struct child_process;
+struct tree;
/*
* Copy the sha1 and stat state of a cache entry from one to
const struct cache_entry *src)
{
unsigned int state = dst->ce_flags & CE_HASHED;
+ int mem_pool_allocated = dst->mem_pool_allocated;
/* Don't copy hash chain and name */
memcpy(&dst->ce_stat_data, &src->ce_stat_data,
/* Restore the hash state */
dst->ce_flags = (dst->ce_flags & ~CE_HASHED) | state;
+
+ /* Restore the mem_pool_allocated flag */
+ dst->mem_pool_allocated = mem_pool_allocated;
}
static inline unsigned create_ce_flags(unsigned stage)
struct untracked_cache *untracked;
uint64_t fsmonitor_last_update;
struct ewah_bitmap *fsmonitor_dirty;
+ struct mem_pool *ce_mem_pool;
};
extern struct index_state the_index;
extern void free_name_hash(struct index_state *istate);
+/* Cache entry creation and cleanup */
+
+/*
+ * Create cache_entry intended for use in the specified index. Caller
+ * is responsible for discarding the cache_entry with
+ * `discard_cache_entry`.
+ */
+struct cache_entry *make_cache_entry(struct index_state *istate,
+ unsigned int mode,
+ const struct object_id *oid,
+ const char *path,
+ int stage,
+ unsigned int refresh_options);
+
+struct cache_entry *make_empty_cache_entry(struct index_state *istate,
+ size_t name_len);
+
+/*
+ * Create a cache_entry that is not intended to be added to an index.
+ * Caller is responsible for discarding the cache_entry
+ * with `discard_cache_entry`.
+ */
+struct cache_entry *make_transient_cache_entry(unsigned int mode,
+ const struct object_id *oid,
+ const char *path,
+ int stage);
+
+struct cache_entry *make_empty_transient_cache_entry(size_t name_len);
+
+/*
+ * Discard cache entry.
+ */
+void discard_cache_entry(struct cache_entry *ce);
+
+/*
+ * Check configuration if we should perform extra validation on cache
+ * entries.
+ */
+int should_validate_cache_entries(void);
+
+/*
+ * Duplicate a cache_entry. Allocate memory for the new entry from a
+ * memory_pool. Takes into account cache_entry fields that are meant
+ * for managing the underlying memory allocation of the cache_entry.
+ */
+struct cache_entry *dup_cache_entry(const struct cache_entry *ce, struct index_state *istate);
+
+/*
+ * Validate the cache entries in the index. This is an internal
+ * consistency check that the cache_entry structs are allocated from
+ * the expected memory pool.
+ */
+void validate_cache_entries(const struct index_state *istate);
+
#ifndef NO_THE_INDEX_COMPATIBILITY_MACROS
#define active_cache (the_index.cache)
#define active_nr (the_index.cache_nr)
extern int unmerged_index(const struct index_state *);
/**
- * Returns 1 if the index differs from HEAD, 0 otherwise. When on an unborn
- * branch, returns 1 if there are entries in the index, 0 otherwise. If an
- * strbuf is provided, the space-separated list of files that differ will be
- * appended to it.
+ * Returns 1 if istate differs from tree, 0 otherwise. If tree is NULL,
+ * compares istate to HEAD. If tree is NULL and on an unborn branch,
+ * returns 1 if there are entries in istate, 0 otherwise. If an strbuf is
+ * provided, the space-separated list of files that differ will be appended
+ * to it.
*/
-extern int index_has_changes(struct strbuf *sb);
+extern int index_has_changes(const struct index_state *istate,
+ struct tree *tree,
+ struct strbuf *sb);
extern int verify_path(const char *path, unsigned mode);
extern int strcmp_offset(const char *s1, const char *s2, size_t *first_change);
extern int add_to_index(struct index_state *, const char *path, struct stat *, int flags);
extern int add_file_to_index(struct index_state *, const char *path, int flags);
-extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, unsigned int refresh_options);
extern int chmod_index_entry(struct index_state *, struct cache_entry *ce, char flip);
extern int ce_same_name(const struct cache_entry *a, const struct cache_entry *b);
extern void set_object_name_for_intent_to_add_entry(struct cache_entry *ce);
#define REFRESH_IGNORE_SUBMODULES 0x0010 /* ignore submodules */
#define REFRESH_IN_PORCELAIN 0x0020 /* user friendly output, not "needs update" */
extern int refresh_index(struct index_state *, unsigned int flags, const struct pathspec *pathspec, char *seen, const char *header_msg);
-extern struct cache_entry *refresh_cache_entry(struct cache_entry *, unsigned int);
+extern struct cache_entry *refresh_cache_entry(struct index_state *, struct cache_entry *, unsigned int);
/*
* Opportunistically update the index but do not complain if we can't.
* Do replace refs need to be checked this run? This variable is
* initialized to true unless --no-replace-object is used or
* $GIT_NO_REPLACE_OBJECTS is set, but is set to false by some
- * commands that do not want replace references to be active. As an
- * optimization it is also set to false if replace references have
- * been sought but there were none.
+ * commands that do not want replace references to be active.
*/
-extern int check_replace_refs;
+extern int read_replace_refs;
extern char *git_replace_ref_base;
extern int fsync_object_files;
extern int core_preload_index;
-extern int core_commit_graph;
extern int core_apply_sparse_checkout;
extern int precomposed_unicode;
extern int protect_hfs;
};
extern enum log_refs_config log_all_ref_updates;
-enum branch_track {
- BRANCH_TRACK_UNSPECIFIED = -1,
- BRANCH_TRACK_NEVER = 0,
- BRANCH_TRACK_REMOTE,
- BRANCH_TRACK_ALWAYS,
- BRANCH_TRACK_EXPLICIT,
- BRANCH_TRACK_OVERRIDE
-};
-
enum rebase_setup_type {
AUTOREBASE_NEVER = 0,
AUTOREBASE_LOCAL,
PUSH_DEFAULT_UNSPECIFIED
};
-extern enum branch_track git_branch_track;
extern enum rebase_setup_type autorebase;
extern enum push_default_type push_default;
static inline int hashcmp(const unsigned char *sha1, const unsigned char *sha2)
{
- return memcmp(sha1, sha2, GIT_SHA1_RAWSZ);
+ /*
+ * This is a temporary optimization hack. By asserting the size here,
+ * we let the compiler know that it's always going to be 20, which lets
+ * it turn this fixed-size memcmp into a few inline instructions.
+ *
+ * This will need to be extended or ripped out when we learn about
+ * hashes of different sizes.
+ */
+ if (the_hash_algo->rawsz != 20)
+ BUG("hash size not yet supported by hashcmp");
+ return memcmp(sha1, sha2, the_hash_algo->rawsz);
}
static inline int oidcmp(const struct object_id *oid1, const struct object_id *oid2)
static inline void hashcpy(unsigned char *sha_dst, const unsigned char *sha_src)
{
- memcpy(sha_dst, sha_src, GIT_SHA1_RAWSZ);
+ memcpy(sha_dst, sha_src, the_hash_algo->rawsz);
}
static inline void oidcpy(struct object_id *dst, const struct object_id *src)
static inline void hashclr(unsigned char *hash)
{
- memset(hash, 0, GIT_SHA1_RAWSZ);
+ memset(hash, 0, the_hash_algo->rawsz);
}
static inline void oidclr(struct object_id *oid)
extern struct object *peel_to_type(const char *name, int namelen,
struct object *o, enum object_type);
+enum date_mode_type {
+ DATE_NORMAL = 0,
+ DATE_RELATIVE,
+ DATE_SHORT,
+ DATE_ISO8601,
+ DATE_ISO8601_STRICT,
+ DATE_RFC2822,
+ DATE_STRFTIME,
+ DATE_RAW,
+ DATE_UNIX
+};
+
struct date_mode {
- enum date_mode_type {
- DATE_NORMAL = 0,
- DATE_RELATIVE,
- DATE_SHORT,
- DATE_ISO8601,
- DATE_ISO8601_STRICT,
- DATE_RFC2822,
- DATE_STRFTIME,
- DATE_RAW,
- DATE_UNIX
- } type;
+ enum date_mode_type type;
const char *strftime_fmt;
int local;
};
unsigned force:1,
quiet:1,
not_new:1,
+ clone:1,
refresh_cache:1;
};
#define CHECKOUT_INIT { NULL, "" }
*/
extern int odb_pack_keep(const char *name);
-/*
- * Iterate over the files in the loose-object parts of the object
- * directory "path", triggering the following callbacks:
- *
- * - loose_object is called for each loose object we find.
- *
- * - loose_cruft is called for any files that do not appear to be
- * loose objects. Note that we only look in the loose object
- * directories "objects/[0-9a-f]{2}/", so we will not report
- * "objects/foobar" as cruft.
- *
- * - loose_subdir is called for each top-level hashed subdirectory
- * of the object directory (e.g., "$OBJDIR/f0"). It is called
- * after the objects in the directory are processed.
- *
- * Any callback that is NULL will be ignored. Callbacks returning non-zero
- * will end the iteration.
- *
- * In the "buf" variant, "path" is a strbuf which will also be used as a
- * scratch buffer, but restored to its original contents before
- * the function returns.
- */
-typedef int each_loose_object_fn(const struct object_id *oid,
- const char *path,
- void *data);
-typedef int each_loose_cruft_fn(const char *basename,
- const char *path,
- void *data);
-typedef int each_loose_subdir_fn(unsigned int nr,
- const char *path,
- void *data);
-int for_each_file_in_obj_subdir(unsigned int subdir_nr,
- struct strbuf *path,
- each_loose_object_fn obj_cb,
- each_loose_cruft_fn cruft_cb,
- each_loose_subdir_fn subdir_cb,
- void *data);
-int for_each_loose_file_in_objdir(const char *path,
- each_loose_object_fn obj_cb,
- each_loose_cruft_fn cruft_cb,
- each_loose_subdir_fn subdir_cb,
- void *data);
-int for_each_loose_file_in_objdir_buf(struct strbuf *path,
- each_loose_object_fn obj_cb,
- each_loose_cruft_fn cruft_cb,
- each_loose_subdir_fn subdir_cb,
- void *data);
-
-/*
- * Iterate over loose objects in both the local
- * repository and any alternates repositories (unless the
- * LOCAL_ONLY flag is set).
- */
-#define FOR_EACH_OBJECT_LOCAL_ONLY 0x1
-extern int for_each_loose_object(each_loose_object_fn, void *, unsigned flags);
-
/*
* Set this to 0 to prevent sha1_object_info_extended() from fetching missing
* blobs. This has a difference only if extensions.partialClone is set.
const struct submodule *sub;
if (ce_mode_s_ifmt == S_IFREG) {
- struct stream_filter *filter = get_stream_filter(ce->name,
+ struct stream_filter *filter = get_stream_filter(state->istate, ce->name,
&ce->oid);
if (filter &&
!streaming_write_entry(ce, path, filter,
* Convert from git internal format to working tree format
*/
if (dco && dco->state != CE_NO_DELAY) {
- ret = async_convert_to_working_tree(ce->name, new_blob,
+ ret = async_convert_to_working_tree(state->istate, ce->name, new_blob,
size, &buf, dco);
if (ret && string_list_has_string(&dco->paths, ce->name)) {
free(new_blob);
goto delayed;
}
} else
- ret = convert_to_working_tree(ce->name, new_blob, size, &buf);
+ ret = convert_to_working_tree(state->istate, ce->name, new_blob, size, &buf);
if (ret) {
free(new_blob);
return lstat(path, st);
}
+ static void mark_colliding_entries(const struct checkout *state,
+ struct cache_entry *ce, struct stat *st)
+ {
+ int i, trust_ino = check_stat;
+
+ #if defined(GIT_WINDOWS_NATIVE)
+ trust_ino = 0;
+ #endif
+
+ ce->ce_flags |= CE_MATCHED;
+
+ for (i = 0; i < state->istate->cache_nr; i++) {
+ struct cache_entry *dup = state->istate->cache[i];
+
+ if (dup == ce)
+ break;
+
+ if (dup->ce_flags & (CE_MATCHED | CE_VALID | CE_SKIP_WORKTREE))
+ continue;
+
+ if ((trust_ino && dup->ce_stat_data.sd_ino == st->st_ino) ||
+ (!trust_ino && !fspathcmp(ce->name, dup->name))) {
+ dup->ce_flags |= CE_MATCHED;
+ break;
+ }
+ }
+ }
+
/*
* Write the contents from ce out to the working tree.
*
if (!check_path(path.buf, path.len, &st, state->base_dir_len)) {
const struct submodule *sub;
- unsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE);
+ unsigned changed = ie_match_stat(state->istate, ce, &st,
+ CE_MATCH_IGNORE_VALID | CE_MATCH_IGNORE_SKIP_WORKTREE);
/*
* Needs to be checked before !changed returns early,
* as the possibly empty directory was not changed
return -1;
}
+ if (state->clone)
+ mark_colliding_entries(state, ce, &st);
+
/*
* We unlink the old file, to get the new one with the
* right permissions (including umask, which is nasty
test_expect_success 'clone on case-insensitive fs' '
git init icasefs &&
(
- cd icasefs
+ cd icasefs &&
o=$(git hash-object -w --stdin </dev/null | hex2oct) &&
t=$(printf "100644 X\0${o}100644 x\0${o}" |
git hash-object -w -t tree --stdin) &&
c=$(git commit-tree -m bogus $t) &&
git update-ref refs/heads/bogus $c &&
- git clone -b bogus . bogus
+ git clone -b bogus . bogus 2>warning
)
'
+ test_expect_success !MINGW,!CYGWIN,CASE_INSENSITIVE_FS 'colliding file detection' '
+ grep X icasefs/warning &&
+ grep x icasefs/warning &&
+ test_i18ngrep "the following paths have collided" icasefs/warning
+ '
+
partial_clone () {
SERVER="$1" &&
URL="$2" &&
ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
}
-static struct cache_entry *dup_entry(const struct cache_entry *ce)
-{
- unsigned int size = ce_size(ce);
- struct cache_entry *new_entry = xmalloc(size);
-
- memcpy(new_entry, ce, size);
- return new_entry;
-}
-
static void add_entry(struct unpack_trees_options *o,
const struct cache_entry *ce,
unsigned int set, unsigned int clear)
{
- do_add_entry(o, dup_entry(ce), set, clear);
+ do_add_entry(o, dup_cache_entry(ce, &o->result), set, clear);
}
/*
return start_delayed_progress(_("Checking out files"), total);
}
+ static void setup_collided_checkout_detection(struct checkout *state,
+ struct index_state *index)
+ {
+ int i;
+
+ state->clone = 1;
+ for (i = 0; i < index->cache_nr; i++)
+ index->cache[i]->ce_flags &= ~CE_MATCHED;
+ }
+
+ static void report_collided_checkout(struct index_state *index)
+ {
+ struct string_list list = STRING_LIST_INIT_NODUP;
+ int i;
+
+ for (i = 0; i < index->cache_nr; i++) {
+ struct cache_entry *ce = index->cache[i];
+
+ if (!(ce->ce_flags & CE_MATCHED))
+ continue;
+
+ string_list_append(&list, ce->name);
+ ce->ce_flags &= ~CE_MATCHED;
+ }
+
+ list.cmp = fspathcmp;
+ string_list_sort(&list);
+
+ if (list.nr) {
+ warning(_("the following paths have collided (e.g. case-sensitive paths\n"
+ "on a case-insensitive filesystem) and only one from the same\n"
+ "colliding group is in the working tree:\n"));
+
+ for (i = 0; i < list.nr; i++)
+ fprintf(stderr, " '%s'\n", list.items[i].string);
+ }
+
+ string_list_clear(&list, 0);
+ }
+
static int check_updates(struct unpack_trees_options *o)
{
unsigned cnt = 0;
state.refresh_cache = 1;
state.istate = index;
+ if (o->clone)
+ setup_collided_checkout_detection(&state, index);
+
progress = get_progress(o);
if (o->update)
- git_attr_set_direction(GIT_ATTR_CHECKOUT, index);
+ git_attr_set_direction(GIT_ATTR_CHECKOUT);
if (should_update_submodules() && o->update && !o->dry_run)
load_gitmodules_file(index, NULL);
stop_progress(&progress);
errs |= finish_delayed_checkout(&state);
if (o->update)
- git_attr_set_direction(GIT_ATTR_CHECKIN, NULL);
+ git_attr_set_direction(GIT_ATTR_CHECKIN);
+
+ if (o->clone)
+ report_collided_checkout(index);
+
return errs != 0;
}
return (info->pathlen < ce_namelen(ce));
}
-static struct cache_entry *create_ce_entry(const struct traverse_info *info, const struct name_entry *n, int stage)
+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 len = traverse_path_len(info, n);
- struct cache_entry *ce = xcalloc(1, cache_entry_size(len));
+ struct cache_entry *ce =
+ is_transient ?
+ make_empty_transient_cache_entry(len) :
+ make_empty_cache_entry(istate, len);
ce->ce_mode = create_ce_mode(n->mode);
ce->ce_flags = create_ce_flags(stage);
stage = 3;
else
stage = 2;
- src[i + o->merge] = create_ce_entry(info, names + i, stage);
+
+ /*
+ * If the merge bit is set, then the cache entries are
+ * discarded in the following block. In this case,
+ * construct "transient" cache_entries, as they are
+ * 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);
}
if (o->merge) {
for (i = 0; i < n; i++) {
struct cache_entry *ce = src[i + o->merge];
if (ce != o->df_conflict_entry)
- free(ce);
+ discard_cache_entry(ce);
}
return rc;
}
return mask;
}
-static int clear_ce_flags_1(struct cache_entry **cache, int nr,
+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);
/* Whole directory matching */
-static int clear_ce_flags_dir(struct cache_entry **cache, int nr,
+static int clear_ce_flags_dir(struct index_state *istate,
+ struct cache_entry **cache, int nr,
struct strbuf *prefix,
char *basename,
int select_mask, int clear_mask,
struct cache_entry **cache_end;
int dtype = DT_DIR;
int ret = is_excluded_from_list(prefix->buf, prefix->len,
- basename, &dtype, el, &the_index);
+ basename, &dtype, el, istate);
int rc;
strbuf_addch(prefix, '/');
* calling clear_ce_flags_1(). That function will call
* the expensive is_excluded_from_list() on every entry.
*/
- rc = clear_ce_flags_1(cache, cache_end - cache,
+ rc = clear_ce_flags_1(istate, cache, cache_end - cache,
prefix,
select_mask, clear_mask,
el, ret);
* cache[0]->name[0..(prefix_len-1)]
* Top level path has prefix_len zero.
*/
-static int clear_ce_flags_1(struct cache_entry **cache, int nr,
+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)
len = slash - name;
strbuf_add(prefix, name, len);
- processed = clear_ce_flags_dir(cache, cache_end - cache,
+ processed = clear_ce_flags_dir(istate, cache, cache_end - cache,
prefix,
prefix->buf + prefix->len - len,
select_mask, clear_mask,
}
strbuf_addch(prefix, '/');
- cache += clear_ce_flags_1(cache, cache_end - cache,
+ cache += clear_ce_flags_1(istate, cache, cache_end - cache,
prefix,
select_mask, clear_mask, el, defval);
strbuf_setlen(prefix, prefix->len - len - 1);
/* Non-directory */
dtype = ce_to_dtype(ce);
ret = is_excluded_from_list(ce->name, ce_namelen(ce),
- name, &dtype, el, &the_index);
+ name, &dtype, el, istate);
if (ret < 0)
ret = defval;
if (ret > 0)
return nr - (cache_end - cache);
}
-static int clear_ce_flags(struct cache_entry **cache, int nr,
- int select_mask, int clear_mask,
- struct exclude_list *el)
+static int clear_ce_flags(struct index_state *istate,
+ int select_mask, int clear_mask,
+ struct exclude_list *el)
{
static struct strbuf prefix = STRBUF_INIT;
strbuf_reset(&prefix);
- return clear_ce_flags_1(cache, nr,
+ return clear_ce_flags_1(istate,
+ istate->cache,
+ istate->cache_nr,
&prefix,
select_mask, clear_mask,
el, 0);
* Set/Clear CE_NEW_SKIP_WORKTREE according to $GIT_DIR/info/sparse-checkout
*/
static void mark_new_skip_worktree(struct exclude_list *el,
- struct index_state *the_index,
+ struct index_state *istate,
int select_flag, int skip_wt_flag)
{
int i;
* 1. Pretend the narrowest worktree: only unmerged entries
* are checked out
*/
- for (i = 0; i < the_index->cache_nr; i++) {
- struct cache_entry *ce = the_index->cache[i];
+ for (i = 0; i < istate->cache_nr; i++) {
+ struct cache_entry *ce = istate->cache[i];
if (select_flag && !(ce->ce_flags & select_flag))
continue;
* 2. Widen worktree according to sparse-checkout file.
* Matched entries will have skip_wt_flag cleared (i.e. "in")
*/
- clear_ce_flags(the_index->cache, the_index->cache_nr,
- select_flag, skip_wt_flag, el);
+ clear_ce_flags(istate, select_flag, skip_wt_flag, el);
}
static int verify_absent(const struct cache_entry *,
return verify_uptodate_1(ce, o, ERROR_SPARSE_NOT_UPTODATE_FILE);
}
+/*
+ * TODO: We should actually invalidate o->result, not src_index [1].
+ * But since cache tree and untracked cache both are not copied to
+ * o->result until unpacking is complete, we invalidate them on
+ * src_index instead with the assumption that they will be copied to
+ * dst_index at the end.
+ *
+ * [1] src_index->cache_tree is also used in unpack_callback() so if
+ * we invalidate o->result, we need to update it to use
+ * o->result.cache_tree as well.
+ */
static void invalidate_ce_path(const struct cache_entry *ce,
struct unpack_trees_options *o)
{
memset(&d, 0, sizeof(d));
if (o->dir)
d.exclude_per_dir = o->dir->exclude_per_dir;
- i = read_directory(&d, &the_index, pathbuf, namelen+1, NULL);
+ i = read_directory(&d, o->src_index, pathbuf, namelen+1, NULL);
if (i)
return o->gently ? -1 :
add_rejected_path(o, ERROR_NOT_UPTODATE_DIR, ce->name);
return 0;
if (o->dir &&
- is_excluded(o->dir, &the_index, name, &dtype))
+ is_excluded(o->dir, o->src_index, name, &dtype))
/*
* ce->name is explicitly excluded, so it is Ok to
* overwrite it.
struct unpack_trees_options *o)
{
int update = CE_UPDATE;
- struct cache_entry *merge = dup_entry(ce);
+ struct cache_entry *merge = dup_cache_entry(ce, &o->result);
if (!old) {
/*
if (verify_absent(merge,
ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN, o)) {
- free(merge);
+ discard_cache_entry(merge);
return -1;
}
invalidate_ce_path(merge, o);
update = 0;
} else {
if (verify_uptodate(old, o)) {
- free(merge);
+ discard_cache_entry(merge);
return -1;
}
/* Migrate old flags over */
#ifndef UNPACK_TREES_H
#define UNPACK_TREES_H
-#include "tree-walk.h"
+#include "cache.h"
#include "argv-array.h"
+#include "string-list.h"
+#include "tree-walk.h"
#define MAX_UNPACK_TREES 8
+struct cache_entry;
struct unpack_trees_options;
struct exclude_list;
unsigned int reset,
merge,
update,
+ clone,
index_only,
nontrivial_merge,
trivial_merges_only,
struct exclude_list *el; /* for internal use */
};
-extern int unpack_trees(unsigned n, struct tree_desc *t,
- struct unpack_trees_options *options);
+int unpack_trees(unsigned n, struct tree_desc *t,
+ struct unpack_trees_options *options);
int verify_uptodate(const struct cache_entry *ce,
struct unpack_trees_options *o);