Download raw body.
got patch: diff3 merge for git diffs
the idea is to parse also the index oldblobid...newblobid field in git diffs for the diff3 merge machinery. Yep, those are blob ids and not commit nor tree ids, I got it wrong initially too. The diff seems to work (only tested thru the added regress) but could use some help to tidy it a bit. One annoying thing is that git uses abbreviated blob ids, unlike got diff which prints the full blob id, at least with a default config. The change in `blobid' (got-read-patch.c) is to accomodate for it, got_parse_sha1_digest fails if the given blob id is abbreviated. Other than that, the diff should be trivial. Comments and improvements are welcome :) diff refs/heads/main refs/heads/gitdiff commit - 3d589bee0bbbe812bb91f3b0284fbf2596304132 commit + c0e8d0701b6a80242d834b624044f3fc81155902 blob - 965f8606f5dc0febfa47fceeba356519258f1021 blob + efe7f1a611a60b4528c47d210297d944f7e1fd38 --- lib/patch.c +++ lib/patch.c @@ -192,10 +192,11 @@ recv_patch(struct imsgbuf *ibuf, int *done, struct got goto done; } - if (*patch.cid != '\0' && *patch.blob != '\0') { + if (*patch.cid != '\0') strlcpy(p->cid, patch.cid, sizeof(p->cid)); + + if (*patch.blob != '\0') strlcpy(p->blob, patch.blob, sizeof(p->blob)); - } /* automatically set strip=1 for git-style diffs */ if (strip == -1 && patch.git && @@ -586,15 +587,25 @@ open_blob(char **path, FILE **fp, const char *blobid, { const struct got_error *err = NULL; struct got_blob_object *blob = NULL; - struct got_object_id id; + struct got_object_id id, *idptr, *matched_id = NULL; *fp = NULL; *path = NULL; - if (!got_parse_sha1_digest(id.sha1, blobid)) - return got_error(GOT_ERR_BAD_OBJ_ID_STR); + if (strlen(blobid) != 40) { + err = got_repo_match_object_id(&matched_id, NULL, blobid, + GOT_OBJ_TYPE_BLOB, NULL /* do not resolve tags */, + repo); + if (err) + return err; + idptr = matched_id; + } else { + if (!got_parse_sha1_digest(id.sha1, blobid)) + return got_error(GOT_ERR_BAD_OBJ_ID_STR); + idptr = &id; + } - err = got_object_open_as_blob(&blob, repo, &id, 8192); + err = got_object_open_as_blob(&blob, repo, idptr, 8192); if (err) goto done; @@ -609,6 +620,8 @@ open_blob(char **path, FILE **fp, const char *blobid, done: if (blob) got_object_blob_close(blob); + if (matched_id != NULL) + free(matched_id); if (err) { if (*fp != NULL) fclose(*fp); @@ -647,7 +660,8 @@ apply_patch(int *overlapcnt, struct got_worktree *work * ignore failures to open this blob, we might have * parsed gibberish. */ - if (err && !(err->code == GOT_ERR_ERRNO && errno == ENOENT)) + if (err && !(err->code == GOT_ERR_ERRNO && errno == ENOENT) && + err->code != GOT_ERR_NO_OBJ) return err; else if (err == NULL) do_merge = 1; @@ -689,6 +703,8 @@ apply_patch(int *overlapcnt, struct got_worktree *work goto done; if (do_merge) { + const char *type, *id; + if (fseeko(afile, 0, SEEK_SET) == -1 || fseeko(oldfile, 0, SEEK_SET) == -1 || fseeko(tmpfile, 0, SEEK_SET) == -1) { @@ -708,7 +724,15 @@ apply_patch(int *overlapcnt, struct got_worktree *work goto done; } - if (asprintf(&anclabel, "commit %s", p->cid) == -1) { + if (*p->cid != '\0') { + type = "commit"; + id = p->cid; + } else { + type = "blob"; + id = p->blob; + } + + if (asprintf(&anclabel, "%s %s", type, id) == -1) { err = got_error_from_errno("asprintf"); anclabel = NULL; goto done; blob - 05415ddde579ebfde446284ba961406bc37a4255 blob + d6eea51f8d3203ae05b110adaac04ee330bd4d28 --- libexec/got-read-patch/got-read-patch.c +++ libexec/got-read-patch/got-read-patch.c @@ -74,10 +74,11 @@ send_patch(const char *oldname, const char *newname, c if (newname != NULL) strlcpy(p.new, newname, sizeof(p.new)); - if (commitid != NULL && blob != NULL) { + if (commitid != NULL) strlcpy(p.cid, commitid, sizeof(p.cid)); + + if (blob != NULL) strlcpy(p.blob, blob, sizeof(p.blob)); - } p.git = git; if (imsg_compose(&ibuf, GOT_IMSG_PATCH, 0, 0, -1, &p, sizeof(p)) == -1) @@ -129,7 +130,7 @@ filename(const char *at, char **name) } static const struct got_error * -blobid(const char *line, char **blob) +blobid(const char *line, char **blob, int git) { uint8_t digest[SHA1_DIGEST_LENGTH]; size_t len; @@ -140,7 +141,7 @@ blobid(const char *line, char **blob) if ((*blob = strndup(line, len)) == NULL) return got_error_from_errno("strndup"); - if (!got_parse_sha1_digest(digest, *blob)) { + if (!git && !got_parse_sha1_digest(digest, *blob)) { /* silently ignore invalid blob ids */ free(*blob); *blob = NULL; @@ -176,13 +177,16 @@ find_patch(int *done, FILE *fp) err = filename(line+4, &new); } else if (!git && !strncmp(line, "blob - ", 7)) { free(blob); - err = blobid(line + 7, &blob); + err = blobid(line + 7, &blob, git); } else if (rename && !strncmp(line, "rename to ", 10)) { free(new); err = filename(line + 10, &new); } else if (git && !strncmp(line, "similarity index 100%", 21)) rename = 1; - else if (!strncmp(line, "diff --git a/", 13)) { + else if (git && !strncmp(line, "index ", 6)) { + free(blob); + err = blobid(line + 6, &blob, git); + } else if (!strncmp(line, "diff --git a/", 13)) { git = 1; free(commitid); commitid = NULL; @@ -190,10 +194,10 @@ find_patch(int *done, FILE *fp) blob = NULL; } else if (!git && !strncmp(line, "diff ", 5)) { free(commitid); - err = blobid(line + 5, &commitid); + err = blobid(line + 5, &commitid, git); } else if (!git && !strncmp(line, "commit - ", 9)) { free(commitid); - err = blobid(line + 9, &commitid); + err = blobid(line + 9, &commitid, git); } if (err) blob - 9fb1dd39304f1cd716283dd5684a1162ee8f745c blob + 51393375e18f15c1ea358bdef6876fc878f0d571 --- regress/cmdline/patch.sh +++ regress/cmdline/patch.sh @@ -1507,6 +1507,62 @@ test_patch_merge_simple() { test_done $testroot $ret } +test_patch_merge_gitdiff() { + local testroot=`test_init patch_merge_gitdiff` + + jot 10 > $testroot/repo/numbers + (cd $testroot/repo && git add numbers && \ + git_commit $testroot/repo -m "nums") + ret=$? + if [ $ret -ne 0 ]; then + test_done $testroot $ret + return 1 + fi + + jot 10 | sed 's/4/four/g' > $testroot/repo/numbers + (cd $testroot/repo && git diff > $testroot/old.diff) + ret=$? + if [ $ret -ne 0 ]; then + test_done $testroot $ret + return 1 + fi + + # restore numbers + jot 10 > $testroot/repo/numbers + + jot 10 | sed 's/6/six/g' > $testroot/repo/numbers + (cd $testroot/repo && git add numbers && \ + git_commit $testroot/repo -m "edit") + ret=$? + if [ $ret -ne 0 ]; then + test_done $testroot $ret + return 1 + fi + + # now work with got: + got checkout $testroot/repo $testroot/wt > /dev/null + ret=$? + if [ $ret -ne 0 ]; then + test_done $testroot $ret + return 1 + fi + + (cd $testroot/wt && got patch $testroot/old.diff) > $testroot/stdout + ret=$? + if [ $ret -ne 0 ]; then + test_done $testroot $ret + return 1 + fi + + echo 'G numbers' > $testroot/stdout.expected + cmp -s $testroot/stdout $testroot/stdout.expected + ret=$? + if [ $ret -ne 0 ]; then + diff -u $testroot/stdout $testroot/stdout.expected + fi + test_done $testroot $ret +} + test_patch_merge_conflict() { local testroot=`test_init patch_merge_conflict` @@ -1665,7 +1721,35 @@ EOF ret=$? if [ $ret -ne 0 ]; then diff -u $testroot/stdout.expected $testroot/stdout + test_done $testroot $ret + return 1 fi + + # try again with a git-style diff + cat <<EOF > $testroot/wt/patch +diff --git a/alpha b/alpha +index 01234567..abcdef0 +--- a/alpha ++++ b/alpha +@@ -1 +1 @@ +-alpha ++ALPHA +EOF + + (cd $testroot/wt && got revert alpha > /dev/null && got patch patch) \ + > $testroot/stdout + ret=$? + if [ $ret -ne 0 ]; then + test_done $testroot $ret + return 1 + fi + + echo 'M alpha' > $testroot/stdout.expected + cmp -s $testroot/stdout.expected $testroot/stdout + ret=$? + if [ $ret -ne 0 ]; then + diff -u $testroot/stdout.expected $testroot/stdout + fi test_done $testroot $ret } @@ -1695,5 +1779,6 @@ run_test test_patch_with_path_prefix run_test test_patch_relpath_with_path_prefix run_test test_patch_reverse run_test test_patch_merge_simple +run_test test_patch_merge_gitdiff run_test test_patch_merge_conflict run_test test_patch_merge_unknown_blob
got patch: diff3 merge for git diffs