From: Stefan Sperling Subject: Re: Got publishes local unversioned bytes during merge and rebase To: Yann Lorwyn , gameoftrees@openbsd.org Date: Sat, 19 Sep 2026 20:53:01 +0200 On Sat, Sep 19, 2026 at 10:30:45AM +0200, Stefan Sperling wrote: > On Fri, Sep 18, 2026 at 10:40:23AM +0800, Yann Lorwyn wrote: > > Hello, > > > > I am writing to report a potential bug int Got 0.128. > > > > Got 0.128 can publish a local unversioned path when an incoming merge or > > rebase adds the same pathname. The operation succeeds and can leave a clean > > work tree, but the resulting tree contains the local file, ignored file, or > > symlink rather than the incoming entry. The ignored-file case can publish > > local generated or sensitive bytes without an explicit add or conflict > > resolution. > > > > The attached report contains the source analysis and Git-core comparison. > > attachments.zip contains a shell reproducer that starts from fresh Got > > repositories and builds the pinned 0.128 source. > > > > Best regards, > > Yann > > Thanks, this sounds like a valid issue. > > Your report is much too detailed, you could have simply written about > 50 lines of shell script instead. Could you please do that by adding > a regression test for this problem to our test suite? Yann, I am sorry about my slightly annoyed initial response. The summary of the problem you wrote above is in fact excellent. It's just that being asked to look at walls of LLM generated text irritates me. I could have ignored those parts since you already provided a good summary of everything that needed to be said. If you end up finding and reporting more issues like this, feel free to omit the LLM output. I prefer to only receive a description of the problem you have written yourself. If you are able to include a new test for our test suite, similar to what I am adding in the patch below, that would be perfect. But I would rather receive a report without a test than no report at all, of course. In this case, it took me about 10 minutes to write a test for our test suite myself, which is not too bad. Sometimes it takes more time. With that out of the way, below are the test changes and a proposed fix. Does this work for you? Apart from the new test I've added, it turns out there is an existing test in histedit.sh which deliberately triggers the unversioned-added file merge situation while testing for an unrelated bug we have fixed. This test has been adjusted to expect the new behaviour. do not add unversioned files to version control if a file addition is merged Problem reported by Yann Lorwyn M lib/worktree.c | 15+ 0- M regress/cmdline/histedit.sh | 17+ 6- M regress/cmdline/merge.sh | 91+ 0- 3 files changed, 123 insertions(+), 6 deletions(-) commit - cb8fe661ec8c00388527dbc4d2744bc511c3cc62 commit + 9d912e77fc9355855259a24e44c4fcb3a146faad blob - b3c50d7a852f627a1de9da592bcf0d73dcdd5ac4 blob + 46fbbdf95999e8f764ffb85ca71d9f85375c7143 --- lib/worktree.c +++ lib/worktree.c @@ -3345,6 +3345,21 @@ merge_file_cb(void *arg, struct got_blob_object *blob1 goto done; } } else { + if (stat(ondisk_path, &sb) == -1) { + if (errno != ENOENT && errno != ENOTDIR) { + err = got_error_from_errno2("stat", + ondisk_path); + goto done; + } + } else { + if (S_ISREG(sb.st_mode) || S_ISLNK(sb.st_mode)) + status = GOT_STATUS_UNVERSIONED; + else + status = GOT_STATUS_OBSTRUCTED; + err = (*a->progress_cb)(a->progress_arg, + status, path2); + goto done; + } err = add_file(a->worktree, a->fileindex, NULL, ondisk_path, path2, blob2, mode2, 0, 0, 1, a->allow_bad_symlinks, blob - 1d993e2f9340895b63648eeed66a23d0497eaa8a blob + c936996937241a7920b5ca639a7275d88af88a1c --- regress/cmdline/histedit.sh +++ regress/cmdline/histedit.sh @@ -3030,17 +3030,17 @@ test_histedit_added_file() { # +2f81c7988242 -> no-op change: add new file echo new > $testroot/wt/new - (cd $testroot/wt && got histedit -f > $testroot/stdout) + (cd $testroot/wt && got histedit -f > $testroot/stdout \ + 2> $testroot/stderr) local new_commit=`git_show_head $testroot/repo` local short_commit2=`trim_obj_id 12 $commit2` local short_new_commit=`trim_obj_id 12 $new_commit` - echo "A new" > $testroot/stdout.expected - echo "$short_commit2 -> $short_new_commit: add new file" \ - >> $testroot/stdout.expected - echo "Switching work tree to refs/heads/master" \ - >> $testroot/stdout.expected + cat > $testroot/stdout.expected < $testroot/stderr.expected < $testroot/repo/gamma/new + git -C $testroot/repo add gamma/new > /dev/null + git_commit $testroot/repo -m "committing to new file on newbranch" + local branch_commit=`git_show_branch_head $testroot/repo newbranch` + + git -C $testroot/repo checkout -q master + echo gamma/new > $testroot/repo/.gitignore + git -C $testroot/repo add .gitignore > /dev/null + echo "ignoring new file on master branch" > $testroot/repo/epsilon/zeta + git_commit $testroot/repo -m "committing to zeta on master" + local master_commit=`git_show_head $testroot/repo` + + got checkout -b master $testroot/repo $testroot/wt > /dev/null + ret=$? + if [ $ret -ne 0 ]; then + echo "got checkout failed unexpectedly" >&2 + test_done "$testroot" "$ret" + return 1 + fi + + echo 'this file is being ignored' > $testroot/wt/gamma/new + + (cd $testroot/wt && got status > $testroot/stdout) + + echo -n > $testroot/stdout.expected + + cmp -s $testroot/stdout.expected $testroot/stdout + ret=$? + if [ $ret -ne 0 ]; then + diff -u $testroot/stdout.expected $testroot/stdout + test_done "$testroot" "$ret" + return 1 + fi + + (cd $testroot/wt && got status -I > $testroot/stdout) + + cat > $testroot/stdout.expected < $testroot/stderr.expected < $testroot/stdout 2> $testroot/stderr) + ret=$? + if [ $ret -eq 0 ]; then + echo "got merge succeeded unexpectedly" >&2 + test_done "$testroot" "1" + return 1 + fi + + local merge_commit=`git_show_head $testroot/repo` + + cat > $testroot/stdout.expected <