make 'got status' detect and indicate merge conflict markers
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
diff --git a/got/got.1 b/got/got.1
index 9538b04..265c985 100644
--- a/got/got.1
+++ b/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
diff --git a/got/got.c b/got/got.c
index b6b5185..4a3c791 100644
--- a/got/got.c
+++ b/got/got.c
@@ -1001,7 +1001,7 @@ print_diff(void *arg, unsigned char status, const char *path,
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 *path,
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();
diff --git a/lib/worktree.c b/lib/worktree.c
index cd22378..5d07d5a 100644
--- a/lib/worktree.c
+++ b/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,6 +957,41 @@ 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,
@@ -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);
diff --git a/regress/cmdline/status.sh b/regress/cmdline/status.sh
index 84bbf2e..6153d36 100755
--- a/regress/cmdline/status.sh
+++ b/regress/cmdline/status.sh
@@ -338,6 +338,60 @@ function test_status_shows_no_mods_after_complete_merge {
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