Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
80867f6
entities/common: add Type enum; promote Status to 4-state
gilescope Apr 17, 2026
15b063e
entities/bug: extend Bug with PR shape and three PR-only ops
gilescope Apr 17, 2026
c4ac2a6
cache: excerpt and matcher learn about Kind and PR fields
gilescope Apr 17, 2026
ef00943
query: parse kind: / type: filters and the new status values
gilescope Apr 17, 2026
849c150
common,bug,cache,query,api: rename common.Type to common.Kind
gilescope Apr 17, 2026
6c16f88
api/graphql: expose PR fields, ops, reviews and the new status values
gilescope Apr 17, 2026
05b9a90
commands/bug: --pr on new; status merge and status ready subcommands
gilescope Apr 17, 2026
8536551
bridge/github: import pull-requests as Kind=PR bugs
gilescope Apr 17, 2026
8a6129f
doc: mark PR support as partial in the feature matrix
gilescope Apr 17, 2026
973d7c0
bridge/github,cache,core: import PR reviews and review comments
gilescope Apr 17, 2026
692b720
doc: reflect review / review comment import in feature matrix
gilescope Apr 17, 2026
a3f2aad
bridge/github: paginate review comments past the inline batch
gilescope Apr 17, 2026
dfb8f1a
doc: review comments are now fully imported
gilescope Apr 17, 2026
ecfe5a8
webui: PR-specific chrome in bug list and detail pages
gilescope Apr 17, 2026
f79aa89
bridge/github: export updates to already-imported pull-requests
gilescope Apr 17, 2026
b44eaf1
bridge/github: export-create for pull-requests
gilescope Apr 17, 2026
8641b30
commands,cache,bridge: author PR reviews locally and export them
gilescope Apr 17, 2026
0126ede
doc: review authoring via CLI and export now supported
gilescope Apr 17, 2026
5bdbdfa
doc: regenerate man pages for new PR commands; add local markdownlint…
gilescope Apr 17, 2026
3b574e7
api,webui: expose originUrl on Bug; show upstream #NNN linked to GitHub
gilescope Apr 17, 2026
61beed3
commands,api,webui: multi-repo webui via --repo and --root + bleve mi…
gilescope Apr 17, 2026
b135498
webui,query: cross-repo search via fan-out + repo:/org: query filters
gilescope Apr 18, 2026
06d216b
webui,api/http: add Sync button that re-pulls every registered repo
gilescope Apr 18, 2026
c929552
feat: PR support ( + dir of repo's checkouts support)
gilescope Apr 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
bridge/github: export-create for pull-requests
Removes the v1 "cannot create a new pull-request via export" bail-out.
When exportBug finds a git-bug entity with Kind=PR that has no
github-id metadata yet, it now issues a createPullRequest mutation
with RepositoryID + BaseRefName + HeadRefName + Title + Body + Draft
from the CreateOperation.

GitHub requires the head branch to already exist on its side — git-bug
does not push branches. If the branch is missing on the remote, the
createPullRequest call returns a clear GitHub error that surfaces via
NewExportError; the user pushes the branch and re-runs `git bug push`.
The base branch (usually main/master) is assumed to exist.

Adds createPullRequestMutation and createGithubPullRequest helper.
The helper strips the `refs/heads/` prefix from base/head refs since
GitHub's createPullRequest expects short branch names.

CLI --pr flag help now calls out the branch-must-be-pushed requirement.

Signed-off-by: Giles Cope <gilescope@gmail.com>
  • Loading branch information
gilescope committed Apr 17, 2026
commit b44eaf197e031b13a095abd7c570e3d4f03c8d5e
68 changes: 54 additions & 14 deletions bridge/github/export.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,14 +247,6 @@ func (ge *githubExporter) exportBug(ctx context.Context, b *cache.BugCache, out
bugGithubURL = githubURL

} else {
// For PRs we cannot create a GitHub pull-request from git-bug alone —
// GitHub requires an existing head branch on its side, which means
// pushing the branch is a prerequisite outside the bridge's scope.
if snapshot.Kind == common.PRKind {
out <- core.NewExportNothing(b.Id(), "cannot create a new pull-request via export; push the branch and open the PR on GitHub first, then re-import")
return
}

// check that we have a token for operation author
client, err := ge.getClientForIdentity(author.Id())
if err != nil {
Expand All @@ -263,12 +255,29 @@ func (ge *githubExporter) exportBug(ctx context.Context, b *cache.BugCache, out
return
}

// create bug
id, url, err := ge.createGithubIssue(ctx, client, ge.repositoryID, createOp.Title, createOp.Message)
if err != nil {
err := errors.Wrap(err, "exporting github issue")
out <- core.NewExportError(err, b.Id())
return
var id, url string
if snapshot.Kind == common.PRKind {
// createPullRequest requires the head branch to already exist on
// GitHub. If the user hasn't pushed it, GitHub returns a clear
// error which surfaces through NewExportError.
id, url, err = ge.createGithubPullRequest(
ctx, client, ge.repositoryID,
createOp.Title, createOp.Message,
createOp.BaseRef, createOp.HeadRef,
createOp.Draft,
)
if err != nil {
err := errors.Wrap(err, "exporting github pull-request")
out <- core.NewExportError(err, b.Id())
return
}
} else {
id, url, err = ge.createGithubIssue(ctx, client, ge.repositoryID, createOp.Title, createOp.Message)
if err != nil {
err := errors.Wrap(err, "exporting github issue")
out <- core.NewExportError(err, b.Id())
return
}
}

out <- core.NewExportBug(b.Id())
Expand Down Expand Up @@ -709,6 +718,37 @@ func (ge *githubExporter) createGithubIssue(ctx context.Context, gc *rateLimitHa
return issue.ID, issue.URL, nil
}

// createGithubPullRequest opens a pull-request against repositoryID. baseRef
// and headRef are expected in git-bug's normalised form (refs/heads/NAME);
// GitHub's createPullRequest takes short branch names, so the refs/heads/
// prefix is stripped. The head branch must already exist on the remote —
// git-bug does not push branches as part of the bridge.
func (ge *githubExporter) createGithubPullRequest(ctx context.Context, gc *rateLimitHandlerClient, repositoryID, title, body, baseRef, headRef string, draft bool) (string, string, error) {
m := &createPullRequestMutation{}
shortRef := func(r string) string {
if strings.HasPrefix(r, "refs/heads/") {
return r[len("refs/heads/"):]
}
return r
}
draftPtr := githubv4.Boolean(draft)
input := githubv4.CreatePullRequestInput{
RepositoryID: repositoryID,
BaseRefName: githubv4.String(shortRef(baseRef)),
HeadRefName: githubv4.String(shortRef(headRef)),
Title: githubv4.String(title),
Body: (*githubv4.String)(&body),
Draft: &draftPtr,
}

if err := gc.mutate(ctx, m, input, nil, ge.out); err != nil {
return "", "", err
}

pr := m.CreatePullRequest.PullRequest
return pr.ID, pr.URL, nil
}

// add a comment to an issue and return its ID
func (ge *githubExporter) addCommentGithubIssue(ctx context.Context, gc *rateLimitHandlerClient, subjectID string, body string) (string, string, error) {
m := &addCommentToIssueMutation{}
Expand Down
9 changes: 9 additions & 0 deletions bridge/github/export_mutation.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@ type updatePullRequestMutation struct {
} `graphql:"updatePullRequest(input:$input)"`
}

type createPullRequestMutation struct {
CreatePullRequest struct {
PullRequest struct {
ID string `graphql:"id"`
URL string `graphql:"url"`
}
} `graphql:"createPullRequest(input:$input)"`
}

type addCommentToIssueMutation struct {
AddComment struct {
CommentEdge struct {
Expand Down
4 changes: 3 additions & 1 deletion commands/bug/bug_new.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ func newBugNewCommand(env *execenv.Env) *cobra.Command {
flags.BoolVar(&options.nonInteractive, "non-interactive", false, "Do not ask for user input")

flags.BoolVar(&options.pr, "pr", false,
"Create a pull-request instead of a plain issue. Requires --base and --head.")
"Create a pull-request instead of a plain issue. Requires --base and --head. "+
"If you later `git bug push` to a GitHub bridge, the --head branch must "+
"already exist on the remote; git-bug does not push branches.")
flags.StringVar(&options.baseRef, "base", "",
"For --pr: the base branch the PR targets (e.g. refs/heads/main).")
flags.StringVar(&options.headRef, "head", "",
Expand Down
10 changes: 10 additions & 0 deletions doc/feature-matrix.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,16 @@ PR support (import only):
| draft state | ✅ | ❌ | ❌ | ❌ |
| reviews | ✅ | ❌ | ❌ | ❌ |
| review comments | ✅ | ❌ | ❌ | ❌ |
| export: create | ✅ | ❌ | ❌ | ❌ |
| export: updates | 🟠 | ❌ | ❌ | ❌ |

Creating a PR via `git bug bug new --pr` then `git bug push` to a
GitHub bridge opens the pull-request on GitHub, provided the --head
branch has already been pushed to the remote. git-bug does not push
git branches itself. Export of subsequent updates (status, title,
body, labels, comments) is supported; export of merges is skipped
because merging requires an actual git merge on GitHub's side, and
review-authoring export is not in v1.

### Exporters

Expand Down