commit 7154f6ce0618e7d8582cd42d577f01cbc3c18d16 from: Stefan Sperling date: Wed Mar 27 08:22:15 2019 UTC make 'got status' detect and indicate merge conflict markers 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 #include #include +#include #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