summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLibravatar Linus Torvalds <torvalds@ppc970.osdl.org>2005-06-05 20:02:31 -0700
committerLibravatar Linus Torvalds <torvalds@ppc970.osdl.org>2005-06-05 20:02:31 -0700
commit02ede67ad45973f9f8d07db7896a537de97d21b6 (patch)
treed13f55becd5ed77a9fd0b47162bfa2d7f56285db
parent[PATCH] Make git-update-cache --force-remove regular (diff)
downloadtgif-02ede67ad45973f9f8d07db7896a537de97d21b6.tar.xz
git-read-tree: be a lot more careful about merging dirty trees
We don't want to overwrite state that we haven't committed yet when merging, so it's better to make git-read-tree fail than end up with a merge tree that ends up not having the dirty changes. Update git-resolve-script to fail cleanly when git-read-tree fails.
-rw-r--r--git-resolve-script5
-rw-r--r--read-tree.c54
2 files changed, 54 insertions, 5 deletions
diff --git a/git-resolve-script b/git-resolve-script
index b7ccc20114..4fc7a6dce3 100644
--- a/git-resolve-script
+++ b/git-resolve-script
@@ -39,13 +39,14 @@ if [ "$common" == "$head" ]; then
echo "Destroying all noncommitted data!"
echo "Kill me within 3 seconds.."
sleep 3
- git-read-tree -m $merge && git-checkout-cache -f -u -a
+ git-read-tree -m $merge || exit 1
+ git-checkout-cache -f -u -a
echo $merge > "$GIT_DIR"/HEAD
git-diff-tree -p ORIG_HEAD HEAD | git-apply --stat
exit 0
fi
echo "Trying to merge $merge into $head"
-git-read-tree -m $common $head $merge
+git-read-tree -m $common $head $merge || exit 1
merge_msg="Merge of $merge_repo"
result_tree=$(git-write-tree 2> /dev/null)
if [ $? -ne 0 ]; then
diff --git a/read-tree.c b/read-tree.c
index 283f4d8577..647f501f6a 100644
--- a/read-tree.c
+++ b/read-tree.c
@@ -93,11 +93,46 @@ static struct cache_entry *merge_entries(struct cache_entry *a,
return NULL;
}
+/*
+ * When a CE gets turned into an unmerged entry, we
+ * want it to be up-to-date
+ */
+static void verify_uptodate(struct cache_entry *ce)
+{
+ struct stat st;
+
+ if (!lstat(ce->name, &st)) {
+ unsigned changed = ce_match_stat(ce, &st);
+ if (!changed)
+ return;
+ errno = 0;
+ }
+ if (errno == ENOENT)
+ return;
+ die("Entry '%s' not uptodate. Cannot merge.", ce->name);
+}
+
+/*
+ * If the old tree contained a CE that isn't even in the
+ * result, that's always a problem, regardless of whether
+ * it's up-to-date or not (ie it can be a file that we
+ * have updated but not committed yet).
+ */
+static void verify_cleared(struct cache_entry *ce)
+{
+ if (ce)
+ die("Entry '%s' would be overwritten by merge. Cannot merge.", ce->name);
+}
+
+static int old_match(struct cache_entry *old, struct cache_entry *a)
+{
+ return old && path_matches(old, a) && same(old, a);
+}
+
static void trivially_merge_cache(struct cache_entry **src, int nr)
{
- static struct cache_entry null_entry;
struct cache_entry **dst = src;
- struct cache_entry *old = &null_entry;
+ struct cache_entry *old = NULL;
while (nr) {
struct cache_entry *ce, *result;
@@ -106,6 +141,7 @@ static void trivially_merge_cache(struct cache_entry **src, int nr)
/* We throw away original cache entries except for the stat information */
if (!ce_stage(ce)) {
+ verify_cleared(old);
old = ce;
src++;
nr--;
@@ -117,18 +153,30 @@ static void trivially_merge_cache(struct cache_entry **src, int nr)
* See if we can re-use the old CE directly?
* That way we get the uptodate stat info.
*/
- if (path_matches(result, old) && same(result, old))
+ if (old_match(old, result)) {
*result = *old;
+ old = NULL;
+ }
ce = result;
ce->ce_flags &= ~htons(CE_STAGEMASK);
src += 2;
nr -= 2;
active_nr -= 2;
}
+
+ /*
+ * If we had an old entry that we now effectively
+ * overwrite, make sure it wasn't dirty.
+ */
+ if (old_match(old, ce)) {
+ verify_uptodate(old);
+ old = NULL;
+ }
*dst++ = ce;
src++;
nr--;
}
+ verify_cleared(old);
}
static void merge_stat_info(struct cache_entry **src, int nr)