Download raw body.
Got publishes local unversioned bytes during merge and rebase
Got publishes local unversioned bytes during merge and rebase
Got publishes local unversioned bytes during merge and rebase
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 <<EOF
+? new
+Files not merged because an unversioned file was found in the work tree: 1
+EOF
cmp -s $testroot/stdout.expected $testroot/stdout
ret=$?
@@ -3050,6 +3050,17 @@ test_histedit_added_file() {
return 1
fi
+ cat > $testroot/stderr.expected <<EOF
+got: changes destined for some files were not yet merged and should be merged manually if required before the histedit operation is continued
+EOF
+ cmp -s $testroot/stderr.expected $testroot/stderr
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ diff -u $testroot/stderr.expected $testroot/stderr
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
test_done "$testroot" "$ret"
}
blob - 576400c021a6d57ecea5c86d8e2478d39b7be3b9
blob + 2721a8cce3d6dc8ef3aaab0b48f987cd7f67e2de
--- regress/cmdline/merge.sh
+++ regress/cmdline/merge.sh
@@ -2331,6 +2331,96 @@ EOF
test_done "$testroot" "$ret"
}
+test_merge_adds_ignored_path() {
+ local testroot=`test_init merge_adds_ignored_path`
+ local commit0=`git_show_head $testroot/repo`
+ local commit0_author_time=`git_show_author_time $testroot/repo`
+
+ git -C $testroot/repo checkout -q -b newbranch
+ echo "new file on branch" > $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 <<EOF
+? gamma/new
+EOF
+ 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
+
+ cat > $testroot/stderr.expected <<EOF
+got: changes destined for some files were not yet merged and should be merged manually if required before the merge operation is continued
+EOF
+ (cd $testroot/wt && got merge newbranch \
+ > $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 <<EOF
+? gamma/new
+Files not merged because an unversioned file was found in the work tree: 1
+EOF
+ 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
+
+ cmp -s $testroot/stderr.expected $testroot/stderr
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ diff -u $testroot/stderr.expected $testroot/stderr
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ test_done "$testroot" "$ret"
+}
+
test_parseargs "$@"
run_test test_merge_basic
run_test test_merge_forward
@@ -2352,3 +2442,4 @@ run_test test_merge_fetched_branch
run_test test_merge_fetched_branch_remote
run_test test_merge_tag
run_test test_merge_tag_abort
+run_test test_merge_adds_ignored_path
Got publishes local unversioned bytes during merge and rebase
Got publishes local unversioned bytes during merge and rebase
Got publishes local unversioned bytes during merge and rebase