Commit Diff


commit - c333d3f7e18758890f7454075451edf36eb26c0f
commit + a769b60bb76783d3bc6637d75907b6ca5d623f40
blob - d1e3839c06a7f70e71410592024f426053df2ced
blob + aec06af5925a0cd63ffc6b150466bad036d5abd8
--- lib/fileindex.c
+++ lib/fileindex.c
@@ -46,6 +46,7 @@
 #define GOT_FILEIDX_F_NO_COMMIT		0x00040000
 #define GOT_FILEIDX_F_NO_FILE_ON_DISK	0x00080000
 #define GOT_FILEIDX_F_REMOVE_ON_FLUSH	0x00100000
+#define GOT_FILEIDX_F_SKIPPED		0x00200000
 
 struct got_fileindex {
 	struct got_fileindex_tree entries;
@@ -144,6 +145,12 @@ void
 got_fileindex_entry_mark_deleted_from_disk(struct got_fileindex_entry *ie)
 {
 	ie->flags |= GOT_FILEIDX_F_NO_FILE_ON_DISK;
+}
+
+void
+got_fileindex_entry_mark_skipped(struct got_fileindex_entry *ie)
+{
+	ie->flags |= GOT_FILEIDX_F_SKIPPED;
 }
 
 const struct got_error *
@@ -245,6 +252,12 @@ got_fileindex_entry_has_file_on_disk(struct got_filein
 	return (ie->flags & GOT_FILEIDX_F_NO_FILE_ON_DISK) == 0;
 }
 
+int
+got_fileindex_entry_was_skipped(struct got_fileindex_entry *ie)
+{
+	return (ie->flags & GOT_FILEIDX_F_SKIPPED) != 0;
+}
+
 static const struct got_error *
 add_entry(struct got_fileindex *fileindex, struct got_fileindex_entry *ie)
 {
@@ -497,6 +510,7 @@ got_fileindex_write(struct got_fileindex *fileindex, F
 
 	RB_FOREACH_SAFE(ie, got_fileindex_tree, &fileindex->entries, tmp) {
 		ie->flags &= ~GOT_FILEIDX_F_NOT_FLUSHED;
+		ie->flags &= ~GOT_FILEIDX_F_SKIPPED;
 		if (ie->flags & GOT_FILEIDX_F_REMOVE_ON_FLUSH) {
 			RB_REMOVE(got_fileindex_tree, &fileindex->entries, ie);
 			got_fileindex_entry_free(ie);
blob - 537374d719e84293f1b449465ab63e092b44a20a
blob + d03058e83c5f5c0a69baf66795455eb3ab8b7af7
--- lib/got_lib_fileindex.h
+++ lib/got_lib_fileindex.h
@@ -109,6 +109,7 @@ mode_t got_fileindex_perms_to_st(struct got_fileindex_
 
 const struct got_error *got_fileindex_entry_update(struct got_fileindex_entry *,
     int, const char *, uint8_t *, uint8_t *, int);
+void got_fileindex_entry_mark_skipped(struct got_fileindex_entry *);
 const struct got_error *got_fileindex_entry_alloc(struct got_fileindex_entry **,
     const char *);
 void got_fileindex_entry_free(struct got_fileindex_entry *);
@@ -164,6 +165,7 @@ const struct got_error *got_fileindex_diff_dir(struct 
 int got_fileindex_entry_has_blob(struct got_fileindex_entry *);
 int got_fileindex_entry_has_commit(struct got_fileindex_entry *);
 int got_fileindex_entry_has_file_on_disk(struct got_fileindex_entry *);
+int got_fileindex_entry_was_skipped(struct got_fileindex_entry *);
 uint32_t got_fileindex_entry_stage_get(const struct got_fileindex_entry *);
 void got_fileindex_entry_stage_set(struct got_fileindex_entry *ie, uint32_t);
 int got_fileindex_entry_filetype_get(struct got_fileindex_entry *);
blob - 57631f318cb2eb1b8a1bfc2cbe610407f87d90da
blob + 9cc4b321126df2564f860576e790daa70c927295
--- lib/worktree.c
+++ lib/worktree.c
@@ -1951,6 +1951,8 @@ update_blob(struct got_worktree *worktree,
 		goto done;
 	}
 	if (status == GOT_STATUS_CONFLICT) {
+		if (ie)
+			got_fileindex_entry_mark_skipped(ie);
 		err = (*progress_cb)(progress_arg, GOT_STATUS_CANNOT_UPDATE,
 		    path);
 		goto done;
@@ -2474,6 +2476,9 @@ bump_base_commit_id(void *arg, struct got_fileindex_en
 	} else if (!got_path_is_child(ie->path, a->path, a->path_len))
 		return NULL;
 
+	if (got_fileindex_entry_was_skipped(ie))
+		return NULL;
+
 	if (memcmp(ie->commit_sha1, a->base_commit_id->sha1,
 	    SHA1_DIGEST_LENGTH) == 0)
 		return NULL;
blob - fa0398814a0312e4d8d4d343c490f6b7b4129804
blob + 18691664018f8a6afd325e890af93afbcdd51b2b
--- regress/cmdline/update.sh
+++ regress/cmdline/update.sh
@@ -2389,6 +2389,187 @@ test_update_single_file() {
 
 	test_done "$testroot" "0"
 	return 0
+}
+
+test_update_file_skipped_due_to_conflict() {
+	local testroot=`test_init update_file_skipped_due_to_conflict`
+	local commit_id0=`git_show_head $testroot/repo`
+	blob_id0=`get_blob_id $testroot/repo "" beta`
+
+	echo "changed beta" > $testroot/repo/beta
+	git_commit $testroot/repo -m "changed beta"
+	local commit_id1=`git_show_head $testroot/repo`
+	blob_id1=`get_blob_id $testroot/repo "" beta`
+
+	got checkout $testroot/repo $testroot/wt > /dev/null
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	blob_id=`(cd $testroot/wt && got info beta | grep 'blob:' | \
+		cut -d ':' -f 2 | tr -d ' ')`
+	if [ "$blob_id" != "$blob_id1" ]; then
+		echo "file beta has the wrong base blob ID" >&2
+		test_done "$testroot" "1"
+		return 1
+	fi
+
+	commit_id=`(cd $testroot/wt && got info beta | \
+		grep 'based on commit:' | cut -d ':' -f 2 | tr -d ' ')`
+	if [ "$commit_id" != "$commit_id1" ]; then
+		echo "file beta has the wrong base commit ID" >&2
+		test_done "$testroot" "1"
+		return 1
+	fi
+
+	echo "modified beta" > $testroot/wt/beta
+
+	(cd $testroot/wt && got update -c $commit_id0 > $testroot/stdout)
+
+	echo "C  beta" > $testroot/stdout.expected
+	echo "Updated to commit $commit_id0" >> $testroot/stdout.expected
+	echo "Files with new merge conflicts: 1" >> $testroot/stdout.expected
+	cmp -s $testroot/stdout.expected $testroot/stdout
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		diff -u $testroot/stdout.expected $testroot/stdout
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	echo "<<<<<<< merged change: commit $commit_id0" \
+		> $testroot/content.expected
+	echo "beta" >> $testroot/content.expected
+	echo "||||||| 3-way merge base: commit $commit_id1" \
+		>> $testroot/content.expected
+	echo "changed beta" >> $testroot/content.expected
+	echo "=======" >> $testroot/content.expected
+	echo "modified beta" >> $testroot/content.expected
+	echo ">>>>>>>" >> $testroot/content.expected
+
+	cat $testroot/wt/beta > $testroot/content
+
+	cmp -s $testroot/content.expected $testroot/content
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		diff -u $testroot/content.expected $testroot/content
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	blob_id=`(cd $testroot/wt && got info beta | grep 'blob:' | \
+		cut -d ':' -f 2 | tr -d ' ')`
+	if [ "$blob_id" != "$blob_id0" ]; then
+		echo "file beta has the wrong base blob ID" >&2
+		test_done "$testroot" "1"
+		return 1
+	fi
+
+	commit_id=`(cd $testroot/wt && got info beta | \
+		grep 'based on commit:' | cut -d ':' -f 2 | tr -d ' ')`
+	if [ "$commit_id" != "$commit_id0" ]; then
+		echo "file beta has the wrong base commit ID" >&2
+		test_done "$testroot" "1"
+		return 1
+	fi
+
+	# update to the latest commit again; this skips beta
+	(cd $testroot/wt && got update > $testroot/stdout)
+	echo "#  beta" > $testroot/stdout.expected
+	echo "Updated to commit $commit_id1" >> $testroot/stdout.expected
+	echo "Files not updated because of existing merge conflicts: 1" \
+		>> $testroot/stdout.expected
+	cmp -s $testroot/stdout.expected $testroot/stdout
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		diff -u $testroot/stdout.expected $testroot/stdout
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	# blob ID of beta should not have changed
+	blob_id=`(cd $testroot/wt && got info beta | grep 'blob:' | \
+		cut -d ':' -f 2 | tr -d ' ')`
+	if [ "$blob_id" != "$blob_id0" ]; then
+		echo "file beta has the wrong base blob ID" >&2
+		test_done "$testroot" "1"
+		return 1
+	fi
+
+	# commit ID of beta should not have changed; There was a bug
+	# here where the commit ID had been changed even though the
+	# file was not updated.
+	commit_id=`(cd $testroot/wt && got info beta | \
+		grep 'based on commit:' | cut -d ':' -f 2 | tr -d ' ')`
+	if [ "$commit_id" != "$commit_id0" ]; then
+		echo "file beta has the wrong base commit ID: $commit_id" >&2
+		test_done "$testroot" "1"
+		return 1
+	fi
+
+	# beta is still conflicted and based on commit 0
+	echo 'C  beta' > $testroot/stdout.expected
+	(cd $testroot/wt && got status > $testroot/stdout)
+	cmp -s $testroot/stdout.expected $testroot/stdout
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		diff -u $testroot/stdout.expected $testroot/stdout
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	# resolve the conflict via revert
+	(cd $testroot/wt && got revert beta >/dev/null)
+
+	# beta now matches its base blob which is still from commit 0
+	echo "beta" > $testroot/content.expected
+	cat $testroot/wt/beta > $testroot/content
+	cmp -s $testroot/content.expected $testroot/content
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		diff -u $testroot/content.expected $testroot/content
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	# updating to the latest commit should now update beta
+	(cd $testroot/wt && got update > $testroot/stdout)
+	echo "U  beta" > $testroot/stdout.expected
+	echo "Updated to commit $commit_id1" >> $testroot/stdout.expected
+	cmp -s $testroot/stdout.expected $testroot/stdout
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		diff -u $testroot/stdout.expected $testroot/stdout
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	blob_id=`(cd $testroot/wt && got info beta | grep 'blob:' | \
+		cut -d ':' -f 2 | tr -d ' ')`
+	if [ "$blob_id" != "$blob_id1" ]; then
+		echo "file beta has the wrong base blob ID" >&2
+		test_done "$testroot" "1"
+		return 1
+	fi
+
+	commit_id=`(cd $testroot/wt && got info beta | \
+		grep 'based on commit:' | cut -d ':' -f 2 | tr -d ' ')`
+	if [ "$commit_id" != "$commit_id1" ]; then
+		echo "file beta has the wrong base commit ID: $commit_id" >&2
+		test_done "$testroot" "1"
+		return 1
+	fi
+
+	echo "changed beta" > $testroot/content.expected
+	cat $testroot/wt/beta > $testroot/content
+	cmp -s $testroot/content.expected $testroot/content
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		diff -u $testroot/content.expected $testroot/content
+	fi
+	test_done "$testroot" "$ret"
 }
 
 
@@ -2432,3 +2613,4 @@ run_test test_update_adds_symlink
 run_test test_update_deletes_symlink
 run_test test_update_symlink_conflicts
 run_test test_update_single_file
+run_test test_update_file_skipped_due_to_conflict