Skip to content

fix(pull): handle unrelated history in pull requests better - #39312

Open
silverwind wants to merge 16 commits into
go-gitea:mainfrom
silverwind:fix-pr-unrelated-history
Open

silverwind wants to merge 16 commits into
go-gitea:mainfrom
silverwind:fix-pr-unrelated-history

Conversation

@silverwind

@silverwind silverwind commented Sep 14, 2026 •

Copy link
Copy Markdown
Member

When head and base of a pull request no longer share history, the files tab returns 404 and "Update branch" fails with a generic error. Broken since #35192.

  1. The files and commits views (web and API) and the diff downloads compare against the last merge base while the head contains it, otherwise against the empty tree, never the current base branch.
  2. A new unrelated status stops merges and auto merge.
  3. Update and Sync Fork report unrelated histories, the API with 422 like GitHub instead of 409.

GetCompareInfo takes the compare separator instead of the DirectComparison booleans, and the compare page's pull request creation rules move to Go.

Replaces: #39228
Fixes: #36644

When the base or head branch of a pull request is rewritten so both no
longer share history, the files view returned 404, the commits API was
empty and updating the branch failed with a generic error.

Keep the last known merge base and compare directly against it, or
against the base branch, when no merge base exists, so the web view, the
API and the diff and patch downloads keep showing the changes. Report
unrelated histories when updating by merge or rebase, and refuse creating
such pull requests via the API like the web form does.

Assisted-by: Claude Code:claude-opus-5
@GiteaBot GiteaBot added the lgtm/need 2 This PR needs two approvals by maintainers to be considered for merging. label Sep 14, 2026
@github-actions github-actions Bot added topic/pr Issues related to pull requests type/bug labels Sep 14, 2026
Existing tests create pull requests between unrelated branches via the API
on purpose, and fixture pull requests store merge bases that are not
ancestors of their head, so the creation check and the two-dot range for
diff and patch downloads broke them. Neither is needed to show the diff or
report the update error.

Assisted-by: Claude Code:claude-opus-5
@wxiaoguang

wxiaoguang commented Sep 14, 2026 •

Copy link
Copy Markdown
Contributor

I don't think the change is right.

  1. the root a potential bug is in GetCompareInfo: there are 3 kinds of ranges: ref1 ref2 , ref1..ref2 and ref1...ref2, but the first one (which doesn't require common history) is not supported.
    • maybe not really in this PR's scope, at least, you need to clearly define the PR response for the edge cases.
  2. this PR introduced more boolean traps (true, false, etc)
  3. brainless machine: DecodeJSON: AI has no memory, contributors also have no memory?

@wxiaoguang

wxiaoguang commented Sep 14, 2026 •

Copy link
Copy Markdown
Contributor

This keeps the last known merge base and falls back to comparing against it or the base branch,

I don't think or the base branch is right. It will only generate wrong and misleading diff results (if you think it should do so, please comment the details)

@silverwind

silverwind commented Sep 14, 2026 •

Copy link
Copy Markdown
Member Author

I will rework.

BTW: GitHub also auto-closes PRs when the base branch moves and produces unrelated histories, but I think that's a overly-invasive move so I'm aiming to keep PRs open, but of course un-mergable/un-updateable.

Comparing against the current base branch produced misleading diffs, so
fall back only to the last known merge base while the head still
contains it, and show a notice and empty file lists when no merge base
is left.

Replace the boolean parameters of GetCompareInfo with CompareOptions,
which also removes the DirectComparison helpers.

Assisted-by: Claude Code:claude-opus-5
@silverwind silverwind changed the title fix(pull): keep pull requests without common history working fix(pull): handle pull requests without common history Sep 14, 2026
@silverwind

silverwind commented Sep 14, 2026 •

Copy link
Copy Markdown
Member Author

Updated and also removed DirectComparison along with it's FIXME comment. Also found #36644 which will be fixed by this.

@silverwind

Copy link
Copy Markdown
Member Author

Also there's #39228 which is related. I'll check how to resolve this situation.

4RH1T3CT0R7 and others added 2 commits September 14, 2026 18:40
When head and base have no merge base, GetCompareInfo leaves
CompareBase empty and viewPullFiles looked up an empty commit ID,
so the files tab returned 404 (400 on older releases). Diff against
the empty tree instead, and let gitdiff accept the empty tree as a
diff base rather than loading it as a commit.
With the empty tree diff, pull requests without any merge base render
their files like a root commit, so the notice is no longer needed. The
files API follows the same base so both views agree, and one fixture
based test now covers both the rewritten base and unrelated head cases.

Assisted-by: Claude Code:claude-opus-5
@silverwind

Copy link
Copy Markdown
Member Author

Also there's #39228 which is related. I'll check how to resolve this situation.

Folded that PR into here via original commit cherry-pick.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Error handling, legacy Git behavior, and unrelated-history file counts remain incorrect.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Improves pull request behavior when base and head histories diverge.

Changes:

  • Falls back to the stored merge base or empty tree.
  • Reports unrelated histories during branch updates.
  • Adds integration coverage and refactors comparison options.
File summaries
File Description
tests/integration/pull_diff_test.go Tests unrelated-history diffs and updates.
templates/repo/diff/compare.tmpl Uses comparison separators directly.
services/pull/update_rebase.go Detects unrelated rebase histories.
services/pull/pull.go Adds stored merge-base fallback.
services/pull/merge_tree.go Preserves usable historical merge bases.
services/gitdiff/gitdiff.go Supports empty-tree diff bases.
services/gitdiff/git_diff_tree.go Accepts empty-tree identifiers.
services/git/compare.go Introduces structured comparison options.
routers/web/repo/pull.go Handles empty-tree views and update errors.
routers/web/repo/compare.go Adopts comparison options.
routers/common/compare.go Removes obsolete comparison helper.
routers/api/v1/repo/pull.go Updates pull files, commits, and errors.
modules/git/repo_compare.go Accepts complete comparison arguments.
models/issues/pull.go Centralizes pull comparison-base selection.
Review details
  • Files reviewed: 14/14 changed files
  • Comments generated: 4
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread services/pull/merge_tree.go Outdated
Comment thread services/pull/pull.go Outdated
Comment thread services/pull/update_rebase.go
Comment thread services/pull/pull.go
…ommon history

Pull requests without any merge base now compare from the empty tree with
all head commits listed, like GitHub, so the commits tab, file count and
commit range selection work. The legacy mergeability check for git older
than 2.40 keeps the last known merge base like the merge tree check, so
both paths behave the same. Git errors while listing the fallback
comparison or finding the rebase merge base are now reported, and review
comment patches use the empty tree when there is no merge base.

Assisted-by: Claude Code:claude-opus-5
Comment thread templates/repo/diff/compare.tmpl Outdated
Comment thread routers/api/v1/repo/pull.go Outdated
…e template

The compare template decided whether a pull request can be created and
which separator to switch to, which is logic that belongs in Go. The page
now gets both values from the handler, and the pull request creation POST
enforces the same rule so direct ".." comparisons can't be submitted. The
branch check left in the template could never fail inside the pull request
block, so its tag message is removed.

Updating a pull request without common history now returns a translatable
unprocessable error from the service, so the API and web handlers use the
generic error responses. Review comment patches use the same stored merge
base rule as the files view.

Assisted-by: Claude Code:claude-opus-5

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Merge-base failures can be misclassified as unrelated histories, and submitted empty pull requests bypass the rendered creation rules.

Get a fresh assessment by requesting another Copilot review.

Review details
  • Files reviewed: 20/20 changed files
  • Comments generated: 3
  • Review effort level: Balanced

Comment thread routers/web/repo/pull.go Outdated
Comment thread services/pull/merge_tree.go Outdated
Comment thread services/pull/patch.go
The pull request creation POST only checked the branch and separator part
of the rule shown on the compare page, because whether there is anything
to compare and whether empty pull requests are allowed was only computed
while rendering the diff. Both are now computed when parsing the
comparison, so the page and the POST share one rule.

The mergeability checks treated every merge base failure as unrelated
histories, marking the pull request empty and possibly dropping its last
merge base when git failed for another reason. Only a missing merge base
takes that path now, other errors are returned.

Assisted-by: Claude Code:claude-opus-5
* origin/main: (21 commits)
  fix(repo): surface unrelated histories on Sync Fork (go-gitea#39258)
  refactor: replace AWS SDK with a REST client for CodeCommit migration (go-gitea#39330)
  perf(frontend): enable vite module preload (go-gitea#39332)
  [skip ci] Updated translations via Crowdin
  fix: add default timeout and handle errors for HaveIBeenPwned API (go-gitea#39316)
  fix(user): unify email validation for registration and settings (go-gitea#39304)
  refactor: replace Azure Blob SDK with a REST client (go-gitea#39315)
  build(gogit): disable gogit builds for stable releases (go-gitea#39324)
  test(e2e): log out to switch users in pr-review test (go-gitea#39328)
  enhance: support `ETag` on streamed repository archives, support `If-None-Match: *` (go-gitea#39289)
  fix: match install page update checker setting with app.ini (go-gitea#39317)
  fix(actions): use gitea's clock for actions durations (go-gitea#39323)
  [skip ci] Updated translations via Crowdin
  enhance(notifications): mark current notification page as read (go-gitea#39294)
  fix(actions): never show negative running durations (go-gitea#39322)
  fix: classify git failures on stderr, restrict migration failure detail (go-gitea#39010)
  [skip ci] Updated translations via Crowdin
  chore: fix various problems (go-gitea#39298)
  fix: correct stdErr match in isErrBlameNotFoundOrNotEnoughLines (go-gitea#39309)
  chore(deps): update actionslib to v1.0.0 (go-gitea#39295)
  ...
… error

pull.Update now returns a translatable error for unrelated histories, so
the Sync Fork handlers no longer matched it and fell back to a server
error. Render it with the auto error helpers instead.

Assisted-by: Claude Code:claude-opus-5
Keep the compare separator switch in the template, derive the pull
request creation rule once, and restore the base/head signature of
GetDiffNumChangedFiles so the change stays close to the existing code.

Assisted-by: Claude Code:claude-opus-5
@silverwind silverwind changed the title fix(pull): handle pull requests without common history fix(pull): handle unrelated history in pull requests better Sep 17, 2026
Reuse the no common merge base fixture instead of rebuilding it, and drop
assertions whose paths are already covered by other checks.

Assisted-by: Claude Code:claude-opus-5
Treating them as empty let API merges and auto merge go ahead, and a
rebase merge would push the unrelated head history onto the base. A
dedicated status makes both refuse such pull requests while their views
keep working.

The squash message helper no longer panics and the diff and patch
downloads no longer fail when no merge base is left.

Assisted-by: Claude Code:claude-opus-5-5
Listing every head commit instead walks the whole history for co-authors
when all authors are collected, and a pull request without a merge base
can't be merged anyway.

Assisted-by: Claude Code:claude-opus-5-5
@silverwind

Copy link
Copy Markdown
Member Author

Cleaned up a bit and ensured this will merge cleanly against #38404.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lgtm/need 2 This PR needs two approvals by maintainers to be considered for merging. topic/pr Issues related to pull requests type/bug

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Gitea doesn't work with PRs that merge unrelated histories

5 participants