Commit Diff


commit - 01a44956166c76e44f58809e5a1ea1477adfef80
commit + 7154f6ce0618e7d8582cd42d577f01cbc3c18d16
blob - 9538b04b4710bad47462be5278aeea7bd4768ac2
blob + 265c9850b620a3a3ca13d48618052cfb435aadff
--- got/got.1
+++ got/got.1
@@ -121,6 +121,7 @@ using the following status codes:
 .It M Ta modified file
 .It A Ta file scheduled for addition in next commit
 .It D Ta file scheduled for deletion in next commit
+.It C Ta modified or added file which contains merge conflicts
 .It ! Ta versioned file was expected on disk but is missing
 .It ~ Ta versioned file is obstructed by a non-regular file
 .It ? Ta unversioned item not tracked by
blob - b6b518579e34e85b27052c4b28680fef25d63669
blob + 4a3c791020dff8d53212a4d0fac32cd6b5925676
--- got/got.c
+++ got/got.c
@@ -1001,7 +1001,7 @@ print_diff(void *arg, unsigned char status, const char
 	struct stat sb;
 
 	if (status != GOT_STATUS_MODIFY && status != GOT_STATUS_ADD &&
-	    status != GOT_STATUS_DELETE)
+	    status != GOT_STATUS_DELETE && status != GOT_STATUS_CONFLICT)
 		return NULL;
 
 	if (!a->header_shown) {
@@ -1010,14 +1010,14 @@ print_diff(void *arg, unsigned char status, const char
 		a->header_shown = 1;
 	}
 
-	if (status == GOT_STATUS_MODIFY || status == GOT_STATUS_DELETE) {
+	if (status != GOT_STATUS_ADD) {
 		err = got_object_open_as_blob(&blob1, a->repo, id, 8192);
 		if (err)
 			goto done;
 
 	}
 
-	if (status == GOT_STATUS_MODIFY || status == GOT_STATUS_ADD) {
+	if (status != GOT_STATUS_DELETE) {
 		if (asprintf(&abspath, "%s/%s",
 		    got_worktree_get_root_path(a->worktree), path) == -1) {
 			err = got_error_from_errno();
blob - cd22378a777b353c7f39ca3b244c95414d187c8a
blob + 5d07d5a5961de07dae868ca1e72e47e990d7620d
--- lib/worktree.c
+++ lib/worktree.c
@@ -32,6 +32,7 @@
 #include <fnmatch.h>
 #include <libgen.h>
 #include <uuid.h>
+#include <util.h>
 
 #include "got_error.h"
 #include "got_repository.h"
@@ -956,7 +957,42 @@ done:
 	return err;
 }
 
+/* Upgrade STATUS_MODIFY to STATUS_CONFLICT if a conflict marker is found. */
 static const struct got_error *
+get_modified_file_content_status(unsigned char *status, FILE *f)
+{
+	const struct got_error *err = NULL;
+	const char *markers[3] = {
+		GOT_DIFF_CONFLICT_MARKER_BEGIN,
+		GOT_DIFF_CONFLICT_MARKER_SEP,
+		GOT_DIFF_CONFLICT_MARKER_END
+	};
+	int i = 0;
+	char *line;
+	size_t len;
+	const char delim[3] = {'\0', '\0', '\0'};
+
+	while (*status == GOT_STATUS_MODIFY) {
+		line = fparseln(f, &len, NULL, delim, 0);
+		if (line == NULL) {
+			if (feof(f))
+				break;
+			err = got_ferror(f, GOT_ERR_IO);
+			break;
+		}
+
+		if (strncmp(line, markers[i], strlen(markers[i])) == 0) {
+			if (markers[i] == GOT_DIFF_CONFLICT_MARKER_END)
+				*status = GOT_STATUS_CONFLICT;
+			else
+				i++;
+		}
+	}
+
+	return err;
+}
+
+static const struct got_error *
 get_file_status(unsigned char *status, struct stat *sb,
     struct got_fileindex_entry *ie, const char *abspath,
     struct got_repository *repo)
@@ -1027,12 +1063,12 @@ get_file_status(unsigned char *status, struct stat *sb
 		const uint8_t *bbuf = got_object_blob_get_read_buf(blob);
 		err = got_object_blob_read_block(&blen, blob);
 		if (err)
-			break;
+			goto done;
 		/* Skip length of blob object header first time around. */
 		flen = fread(fbuf, 1, sizeof(fbuf) - hdrlen, f);
 		if (flen == 0 && ferror(f)) {
 			err = got_error_from_errno();
-			break;
+			goto done;
 		}
 		if (blen == 0) {
 			if (flen != 0)
@@ -1054,6 +1090,11 @@ get_file_status(unsigned char *status, struct stat *sb
 		}
 		hdrlen = 0;
 	}
+
+	if (*status == GOT_STATUS_MODIFY) {
+		rewind(f);
+		err = get_modified_file_content_status(status, f);
+	}
 done:
 	if (blob)
 		got_object_blob_close(blob);
blob - 84bbf2e8d42e2b4bfd8d1ad97db33cc0361a41d7
blob + 6153d36c2d585a109a3c72d16d412dda24c7d84e
--- regress/cmdline/status.sh
+++ regress/cmdline/status.sh
@@ -337,7 +337,61 @@ function test_status_shows_no_mods_after_complete_merg
 	fi
 	test_done "$testroot" "$ret"
 }
+
+function test_status_shows_conflict {
+	local testroot=`test_init status_shows_conflict 1`
 
+	echo "1" > $testroot/repo/numbers
+	echo "2" >> $testroot/repo/numbers
+	echo "3" >> $testroot/repo/numbers
+	echo "4" >> $testroot/repo/numbers
+	echo "5" >> $testroot/repo/numbers
+	echo "6" >> $testroot/repo/numbers
+	echo "7" >> $testroot/repo/numbers
+	echo "8" >> $testroot/repo/numbers
+	(cd $testroot/repo && git add numbers)
+	git_commit $testroot/repo -m "added numbers file"
+
+	got checkout $testroot/repo $testroot/wt > /dev/null
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		test_done "$testroot" "$ret"
+		return 1
+	fi
+
+	sed -i 's/2/22/' $testroot/repo/numbers
+	git_commit $testroot/repo -m "modified line 2"
+
+	# modify line 2 in a conflicting way
+	sed -i 's/2/77/' $testroot/wt/numbers
+
+	echo "C  numbers" > $testroot/stdout.expected
+	echo -n "Updated to commit " >> $testroot/stdout.expected
+	git_show_head $testroot/repo >> $testroot/stdout.expected
+	echo >> $testroot/stdout.expected
+
+	(cd $testroot/wt && got update > $testroot/stdout)
+
+	cmp $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 'C  numbers' > $testroot/stdout.expected
+
+	(cd $testroot/wt && got status > $testroot/stdout)
+
+	cmp $testroot/stdout.expected $testroot/stdout
+	ret="$?"
+	if [ "$ret" != "0" ]; then
+		diff -u $testroot/stdout.expected $testroot/stdout
+	fi
+	test_done "$testroot" "$ret"
+}
+
 run_test test_status_basic
 run_test test_status_subdir_no_mods
 run_test test_status_subdir_no_mods2
@@ -346,3 +400,4 @@ run_test test_status_shows_local_mods_after_update
 run_test test_status_unversioned_subdirs
 run_test test_status_ignores_symlink
 run_test test_status_shows_no_mods_after_complete_merge
+run_test test_status_shows_conflict