summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Tan <pyokagan@gmail.com>2015-08-19 16:22:22 +0800
committerJunio C Hamano <gitster@pobox.com>2015-08-19 10:51:39 -0700
commit3ecc7040eff29fea0051df9faf21b0a73ee6d911 (patch)
treefbe6086d2dfcf89395832c6e957f182864e32060
parente97a5e765db06995c3b027a5d7dceb6b4f2cba02 (diff)
downloadgit-pt/am-builtin-abort-fix.tar.gz
am --skip/--abort: merge HEAD/ORIG_HEAD tree into indexpt/am-builtin-abort-fix
After running "git am --abort", and then running "git reset --hard", files that were not modified would still be re-checked out. This is because clean_index() in builtin/am.c mistakenly called the read_tree() function, which overwrites all entries in the index, including the stat info. "git am --skip" did not seem to have this issue because am_skip() called am_run(), which called refresh_cache() to update the stat info. However, there's still a performance penalty as the lack of stat info meant that refresh_cache() would have to scan all files for changes. Fix this by using unpack_trees() instead to merge the tree into the index, so that the stat info from the index is kept. Reported-by: Linus Torvalds <torvalds@linux-foundation.org> Helped-by: Junio C Hamano <gitster@pobox.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Paul Tan <pyokagan@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
-rw-r--r--builtin/am.c49
-rwxr-xr-xt/t4151-am-abort.sh24
2 files changed, 60 insertions, 13 deletions
diff --git a/builtin/am.c b/builtin/am.c
index 1399c8dd88..3e7e66f82d 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -1940,15 +1940,48 @@ static int fast_forward_to(struct tree *head, struct tree *remote, int reset)
}
/**
+ * Merges a tree into the index. The index's stat info will take precedence
+ * over the merged tree's. Returns 0 on success, -1 on failure.
+ */
+static int merge_tree(struct tree *tree)
+{
+ struct lock_file *lock_file;
+ struct unpack_trees_options opts;
+ struct tree_desc t[1];
+
+ if (parse_tree(tree))
+ return -1;
+
+ lock_file = xcalloc(1, sizeof(struct lock_file));
+ hold_locked_index(lock_file, 1);
+
+ memset(&opts, 0, sizeof(opts));
+ opts.head_idx = 1;
+ opts.src_index = &the_index;
+ opts.dst_index = &the_index;
+ opts.merge = 1;
+ opts.fn = oneway_merge;
+ init_tree_desc(&t[0], tree->buffer, tree->size);
+
+ if (unpack_trees(1, t, &opts)) {
+ rollback_lock_file(lock_file);
+ return -1;
+ }
+
+ if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
+ die(_("unable to write new index file"));
+
+ return 0;
+}
+
+/**
* Clean the index without touching entries that are not modified between
* `head` and `remote`.
*/
static int clean_index(const unsigned char *head, const unsigned char *remote)
{
- struct lock_file *lock_file;
struct tree *head_tree, *remote_tree, *index_tree;
unsigned char index[GIT_SHA1_RAWSZ];
- struct pathspec pathspec;
head_tree = parse_tree_indirect(head);
if (!head_tree)
@@ -1973,18 +2006,8 @@ static int clean_index(const unsigned char *head, const unsigned char *remote)
if (fast_forward_to(index_tree, remote_tree, 0))
return -1;
- memset(&pathspec, 0, sizeof(pathspec));
-
- lock_file = xcalloc(1, sizeof(struct lock_file));
- hold_locked_index(lock_file, 1);
-
- if (read_tree(remote_tree, 0, &pathspec)) {
- rollback_lock_file(lock_file);
+ if (merge_tree(remote_tree))
return -1;
- }
-
- if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
- die(_("unable to write new index file"));
remove_branch_state();
diff --git a/t/t4151-am-abort.sh b/t/t4151-am-abort.sh
index 05bdc3ee49..ea5ace99a1 100755
--- a/t/t4151-am-abort.sh
+++ b/t/t4151-am-abort.sh
@@ -168,4 +168,28 @@ test_expect_success 'am --abort on unborn branch will keep local commits intact'
test_cmp expect actual
'
+test_expect_success 'am --skip leaves index stat info alone' '
+ git checkout -f --orphan skip-stat-info &&
+ git reset &&
+ test_commit skip-should-be-untouched &&
+ test-chmtime =0 skip-should-be-untouched.t &&
+ git update-index --refresh &&
+ git diff-files --exit-code --quiet &&
+ test_must_fail git am 0001-*.patch &&
+ git am --skip &&
+ git diff-files --exit-code --quiet
+'
+
+test_expect_success 'am --abort leaves index stat info alone' '
+ git checkout -f --orphan abort-stat-info &&
+ git reset &&
+ test_commit abort-should-be-untouched &&
+ test-chmtime =0 abort-should-be-untouched.t &&
+ git update-index --refresh &&
+ git diff-files --exit-code --quiet &&
+ test_must_fail git am 0001-*.patch &&
+ git am --abort &&
+ git diff-files --exit-code --quiet
+'
+
test_done