From 76b99b81613abea4cc16e45e1b11dbbec82a4b4d Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 14 May 2006 10:43:50 -0700 Subject: [PATCH 1/4] Allow one-way tree merge to remove old files For some random reason (probably just because nobody noticed), the one-way merge strategy didn't mark deleted files as deleted, so if you used git-read-tree -m -u it would update the files that got changed in the index, but it would not delete the files that got deleted. This should fix it, and I can't imagine that anybody depends on the old strange "update only existing files" behaviour. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- read-tree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/read-tree.c b/read-tree.c index e926e4c880..11157f4242 100644 --- a/read-tree.c +++ b/read-tree.c @@ -684,7 +684,7 @@ static int oneway_merge(struct cache_entry **src) merge_size); if (!a) - return 0; + return deleted_entry(old, NULL); if (old && same(old, a)) { return keep_entry(old); } From c68998f5b5f43717a27da82fac08a76d6588bae7 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 14 May 2006 11:20:37 -0700 Subject: [PATCH 2/4] Simplify "git reset --hard" Now that the one-way merge strategy does the right thing wrt files that do not exist in the result, just remove all the random crud we did in "git reset" to do this all by hand. Instead, just pass in "-u" to git-read-tree when we do a hard reset, and depend on git-read-tree to update the working tree appropriately. This basically means that git reset turns into # Always update the HEAD ref git update-ref HEAD "$rev" case "--soft" # do nothing to index/working tree case "--hard" # read index _and_ update working tree git-read-tree --reset -u "$rev" case "--mixed" # update just index, report on working tree differences git-read-tree --reset "$rev" git-update-index --refresh which is what it was always semantically doing, it just did it in a rather strange way because it was written to not expect git-read-tree to do anything to the working tree. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- git-reset.sh | 50 ++++---------------------------------------------- 1 file changed, 4 insertions(+), 46 deletions(-) diff --git a/git-reset.sh b/git-reset.sh index 6cb073cb16..0ee3e3e154 100755 --- a/git-reset.sh +++ b/git-reset.sh @@ -6,6 +6,7 @@ USAGE='[--mixed | --soft | --hard] []' tmp=${GIT_DIR}/reset.$$ trap 'rm -f $tmp-*' 0 1 2 3 15 +update= reset_type=--mixed case "$1" in --mixed | --soft | --hard) @@ -23,24 +24,7 @@ rev=$(git-rev-parse --verify $rev^0) || exit # behind before a hard reset, so that we can remove them. if test "$reset_type" = "--hard" then - { - git-ls-files --stage -z - git-rev-parse --verify HEAD 2>/dev/null && - git-ls-tree -r -z HEAD - } | perl -e ' - use strict; - my %seen; - $/ = "\0"; - while (<>) { - chomp; - my ($info, $path) = split(/\t/, $_); - next if ($info =~ / tree /); - if (!$seen{$path}) { - $seen{$path} = 1; - print "$path\0"; - } - } - ' >$tmp-exists + update=-u fi # Soft reset does not touch the index file nor the working tree @@ -54,7 +38,7 @@ then die "Cannot do a soft reset in the middle of a merge." fi else - git-read-tree --reset "$rev" || exit + git-read-tree --reset $update "$rev" || exit fi # Any resets update HEAD to the head being switched to. @@ -68,33 +52,7 @@ git-update-ref HEAD "$rev" case "$reset_type" in --hard ) - # Hard reset matches the working tree to that of the tree - # being switched to. - git-checkout-index -f -u -q -a - git-ls-files --cached -z | - perl -e ' - use strict; - my (%keep, $fh); - $/ = "\0"; - while () { - chomp; - $keep{$_} = 1; - } - open $fh, "<", $ARGV[0] - or die "cannot open $ARGV[0]"; - while (<$fh>) { - chomp; - if (! exists $keep{$_}) { - # it is ok if this fails -- it may already - # have been culled by checkout-index. - unlink $_; - while (s|/[^/]*$||) { - rmdir($_) or last; - } - } - } - ' $tmp-exists - ;; + ;; # Nothing else to do --soft ) ;; # Nothing else to do --mixed ) From 613f02739a49337592a32936caa04b1590ca1109 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 15 May 2006 00:46:05 -0700 Subject: [PATCH 3/4] read-tree -u one-way merge fix to check out locally modified paths. The "-u" flag means "update the working tree files", but to other types of merges, it also implies "I want to keep my local changes" -- because they prevent local changes from getting lost by using verify_uptodate. The one-way merge is different from other merges in that its purpose is opposite of doing something else while keeping unrelated local changes. The point of one-way merge is to nuke local changes. So while it feels somewhat wrong that this actively loses local changes, it is the right thing to do. Signed-off-by: Junio C Hamano --- read-tree.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/read-tree.c b/read-tree.c index 11157f4242..59b6a6bff9 100644 --- a/read-tree.c +++ b/read-tree.c @@ -686,6 +686,9 @@ static int oneway_merge(struct cache_entry **src) if (!a) return deleted_entry(old, NULL); if (old && same(old, a)) { + struct stat st; + if (lstat(old->name, &st) || ce_match_stat(old, &st, 1)) + old->ce_flags |= htons(CE_UPDATE); return keep_entry(old); } return merged_entry(a, NULL); From 6d6776cb497ea7fbf5fe43912dbe3286f76c9933 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 15 May 2006 08:09:31 -0700 Subject: [PATCH 4/4] read-tree --reset -u fix. The previous commit makes -u to mean "I do want to remove the local changes, just update it from the read tree" only for one-way merge. It makes sense to have it depend on the "--reset" flag instead. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- read-tree.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/read-tree.c b/read-tree.c index 59b6a6bff9..e16e91b173 100644 --- a/read-tree.c +++ b/read-tree.c @@ -12,6 +12,7 @@ #include #include +static int reset = 0; static int merge = 0; static int update = 0; static int index_only = 0; @@ -416,6 +417,10 @@ static void verify_uptodate(struct cache_entry *ce) return; errno = 0; } + if (reset) { + ce->ce_flags |= htons(CE_UPDATE); + return; + } if (errno == ENOENT) return; die("Entry '%s' not uptodate. Cannot merge.", ce->name); @@ -686,9 +691,12 @@ static int oneway_merge(struct cache_entry **src) if (!a) return deleted_entry(old, NULL); if (old && same(old, a)) { - struct stat st; - if (lstat(old->name, &st) || ce_match_stat(old, &st, 1)) - old->ce_flags |= htons(CE_UPDATE); + if (reset) { + struct stat st; + if (lstat(old->name, &st) || + ce_match_stat(old, &st, 1)) + old->ce_flags |= htons(CE_UPDATE); + } return keep_entry(old); } return merged_entry(a, NULL); @@ -722,7 +730,7 @@ static struct cache_file cache_file; int main(int argc, char **argv) { - int i, newfd, reset, stage = 0; + int i, newfd, stage = 0; unsigned char sha1[20]; merge_fn_t fn = NULL;