summaryrefslogtreecommitdiff
path: root/unpack-trees.c
diff options
context:
space:
mode:
Diffstat (limited to 'unpack-trees.c')
-rw-r--r--unpack-trees.c112
1 files changed, 58 insertions, 54 deletions
diff --git a/unpack-trees.c b/unpack-trees.c
index 3a4e181af4..dd5999c356 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -7,6 +7,7 @@
#include "unpack-trees.h"
#include "progress.h"
#include "refs.h"
+#include "attr.h"
/*
* Error messages expected by scripts out of plumbing commands such as
@@ -49,39 +50,20 @@ static void add_entry(struct unpack_trees_options *o, struct cache_entry *ce,
memcpy(new, ce, size);
new->next = NULL;
new->ce_flags = (new->ce_flags & ~clear) | set;
- add_index_entry(&o->result, new, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE|ADD_CACHE_SKIP_DFCHECK);
+ add_index_entry(&o->result, new, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
}
-/* Unlink the last component and attempt to remove leading
- * directories, in case this unlink is the removal of the
- * last entry in the directory -- empty directories are removed.
+/*
+ * Unlink the last component and schedule the leading directories for
+ * removal, such that empty directories get removed.
*/
static void unlink_entry(struct cache_entry *ce)
{
- char *cp, *prev;
- char *name = ce->name;
-
- if (has_symlink_leading_path(ce_namelen(ce), ce->name))
+ if (has_symlink_or_noent_leading_path(ce->name, ce_namelen(ce)))
return;
- if (unlink(name))
+ if (unlink_or_warn(ce->name))
return;
- prev = NULL;
- while (1) {
- int status;
- cp = strrchr(name, '/');
- if (prev)
- *prev = '/';
- if (!cp)
- break;
-
- *cp = 0;
- status = rmdir(name);
- if (status) {
- *cp = '/';
- break;
- }
- prev = cp;
- }
+ schedule_dir_for_removal(ce->name, ce_namelen(ce));
}
static struct checkout state;
@@ -105,6 +87,8 @@ static int check_updates(struct unpack_trees_options *o)
cnt = 0;
}
+ if (o->update)
+ git_attr_set_direction(GIT_ATTR_CHECKOUT, &o->result);
for (i = 0; i < index->cache_nr; i++) {
struct cache_entry *ce = index->cache[i];
@@ -112,11 +96,10 @@ static int check_updates(struct unpack_trees_options *o)
display_progress(progress, ++cnt);
if (o->update)
unlink_entry(ce);
- remove_index_entry_at(&o->result, i);
- i--;
- continue;
}
}
+ remove_marked_cache_entries(&o->result);
+ remove_scheduled_dirs();
for (i = 0; i < index->cache_nr; i++) {
struct cache_entry *ce = index->cache[i];
@@ -130,6 +113,8 @@ static int check_updates(struct unpack_trees_options *o)
}
}
stop_progress(&progress);
+ if (o->update)
+ git_attr_set_direction(GIT_ATTR_CHECKIN, NULL);
return errs != 0;
}
@@ -143,7 +128,7 @@ static inline int call_unpack_fn(struct cache_entry **src, struct unpack_trees_o
static int unpack_index_entry(struct cache_entry *ce, struct unpack_trees_options *o)
{
- struct cache_entry *src[5] = { ce, };
+ struct cache_entry *src[5] = { ce, NULL, };
o->pos++;
if (ce_stage(ce)) {
@@ -155,7 +140,7 @@ static int unpack_index_entry(struct cache_entry *ce, struct unpack_trees_option
return call_unpack_fn(src, o);
}
-int traverse_trees_recursive(int n, unsigned long dirmask, unsigned long df_conflicts, struct name_entry *names, struct traverse_info *info)
+static int traverse_trees_recursive(int n, unsigned long dirmask, unsigned long df_conflicts, struct name_entry *names, struct traverse_info *info)
{
int i;
struct tree_desc t[MAX_UNPACK_TREES];
@@ -286,12 +271,23 @@ static int unpack_nondirectories(int n, unsigned long mask,
if (o->merge)
return call_unpack_fn(src, o);
- n += o->merge;
for (i = 0; i < n; i++)
- add_entry(o, src[i], 0, 0);
+ if (src[i] && src[i] != o->df_conflict_entry)
+ add_entry(o, src[i], 0, 0);
return 0;
}
+static int unpack_failed(struct unpack_trees_options *o, const char *message)
+{
+ discard_index(&o->result);
+ if (!o->gently) {
+ if (message)
+ return error("%s", message);
+ return -1;
+ }
+ return -1;
+}
+
static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, struct name_entry *names, struct traverse_info *info)
{
struct cache_entry *src[MAX_UNPACK_TREES + 1] = { NULL, };
@@ -309,7 +305,7 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str
int cmp = compare_entry(ce, info, p);
if (cmp < 0) {
if (unpack_index_entry(ce, o) < 0)
- return -1;
+ return unpack_failed(o, NULL);
continue;
}
if (!cmp) {
@@ -341,6 +337,23 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str
if (src[0])
conflicts |= 1;
}
+
+ /* special case: "diff-index --cached" looking at a tree */
+ if (o->diff_index_cached &&
+ n == 1 && dirmask == 1 && S_ISDIR(names->mode)) {
+ int matches;
+ matches = cache_tree_matches_traversal(o->src_index->cache_tree,
+ names, info);
+ /*
+ * Everything under the name matches. Adjust o->pos to
+ * skip the entire hierarchy.
+ */
+ if (matches) {
+ o->pos += matches;
+ return mask;
+ }
+ }
+
if (traverse_trees_recursive(n, dirmask, conflicts,
names, info) < 0)
return -1;
@@ -350,17 +363,6 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str
return mask;
}
-static int unpack_failed(struct unpack_trees_options *o, const char *message)
-{
- discard_index(&o->result);
- if (!o->gently) {
- if (message)
- return error("%s", message);
- return -1;
- }
- return -1;
-}
-
/*
* N-way merge "len" trees. Returns 0 on success, -1 on failure to manipulate the
* resulting index, -2 on failure to reflect the changes to the work tree.
@@ -380,8 +382,10 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
memset(&o->result, 0, sizeof(o->result));
o->result.initialized = 1;
- if (o->src_index)
- o->result.timestamp = o->src_index->timestamp;
+ if (o->src_index) {
+ o->result.timestamp.sec = o->src_index->timestamp.sec;
+ o->result.timestamp.nsec = o->src_index->timestamp.nsec;
+ }
o->merge_size = len;
if (!dfc)
@@ -446,7 +450,7 @@ static int verify_uptodate(struct cache_entry *ce,
{
struct stat st;
- if (o->index_only || o->reset)
+ if (o->index_only || o->reset || ce_uptodate(ce))
return 0;
if (!lstat(ce->name, &st)) {
@@ -547,7 +551,7 @@ static int verify_clean_subdirectory(struct cache_entry *ce, const char *action,
memset(&d, 0, sizeof(d));
if (o->dir)
d.exclude_per_dir = o->dir->exclude_per_dir;
- i = read_directory(&d, ce->name, pathbuf, namelen+1, NULL);
+ i = read_directory(&d, pathbuf, namelen+1, NULL);
if (i)
return o->gently ? -1 :
error(ERRORMSG(o, not_uptodate_dir), ce->name);
@@ -583,7 +587,7 @@ static int verify_absent(struct cache_entry *ce, const char *action,
if (o->index_only || o->reset || !o->update)
return 0;
- if (has_symlink_leading_path(ce_namelen(ce), ce->name))
+ if (has_symlink_or_noent_leading_path(ce->name, ce_namelen(ce)))
return 0;
if (!lstat(ce->name, &st)) {
@@ -613,7 +617,7 @@ static int verify_absent(struct cache_entry *ce, const char *action,
* found "foo/." in the working tree.
* This is tricky -- if we have modified
* files that are in "foo/" we would lose
- * it.
+ * them.
*/
ret = verify_clean_subdirectory(ce, action, o);
if (ret < 0)
@@ -891,7 +895,7 @@ int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o)
* Two-way merge.
*
* The rule is to "carry forward" what is in the index without losing
- * information across a "fast forward", favoring a successful merge
+ * information across a "fast-forward", favoring a successful merge
* over a merge failure when it makes sense. For details of the
* "carry forward" rule, please see <Documentation/git-read-tree.txt>.
*
@@ -995,12 +999,12 @@ int oneway_merge(struct cache_entry **src, struct unpack_trees_options *o)
return error("Cannot do a oneway merge of %d trees",
o->merge_size);
- if (!a)
+ if (!a || a == o->df_conflict_entry)
return deleted_entry(old, old, o);
if (old && same(old, a)) {
int update = 0;
- if (o->reset) {
+ if (o->reset && !ce_uptodate(old)) {
struct stat st;
if (lstat(old->name, &st) ||
ie_match_stat(o->src_index, old, &st, CE_MATCH_IGNORE_VALID))