Files
BrowserOS/packages/browseros/tools/patch/internal/engine/extract.go
Nikhil e7105ae50b fix: improve browseros-patch workspace feedback (#921)
* fix: make patch list registry-only

* feat: add patch command progress logs

* fix: address review feedback for PR #921
2026-05-02 15:09:31 -07:00

125 lines
3.4 KiB
Go

package engine
import (
"context"
"time"
"github.com/browseros-ai/BrowserOS/packages/browseros/tools/patch/internal/git"
"github.com/browseros-ai/BrowserOS/packages/browseros/tools/patch/internal/patch"
"github.com/browseros-ai/BrowserOS/packages/browseros/tools/patch/internal/repo"
"github.com/browseros-ai/BrowserOS/packages/browseros/tools/patch/internal/workspace"
)
type ExtractOptions struct {
Workspace workspace.Entry
Repo *repo.Info
Commit string
RangeStart string
RangeEnd string
Squash bool
Base string
Filters []string
Progress Progress
}
type ExtractResult struct {
Workspace string `json:"workspace"`
Mode string `json:"mode"`
BaseCommit string `json:"base_commit"`
Written []string `json:"written"`
Deleted []string `json:"deleted"`
}
func Extract(ctx context.Context, opts ExtractOptions) (*ExtractResult, error) {
base := opts.Base
if base == "" {
base = opts.Repo.BaseCommit
}
var (
set patch.PatchSet
scope []string
err error
mode string
)
switch {
case opts.Commit != "":
mode = "commit"
reportProgress(opts.Progress, "Extracting patches from commit %s", opts.Commit)
set, err = patch.BuildCommitPatchSet(ctx, opts.Workspace.Path, opts.Commit, opts.Base, opts.Filters)
if err == nil {
if opts.Base != "" {
changes, err := git.DiffTreeNameStatus(ctx, opts.Workspace.Path, opts.Commit, opts.Filters)
if err != nil {
return nil, err
}
scope = changedScope(changes)
} else {
scope = patch.ScopeFromSet(set)
}
}
case opts.RangeStart != "" && opts.RangeEnd != "":
mode = "range"
reportProgress(opts.Progress, "Extracting patches from range %s..%s", opts.RangeStart, opts.RangeEnd)
set, err = patch.BuildRangePatchSet(ctx, opts.Workspace.Path, opts.RangeStart, opts.RangeEnd, opts.Base, opts.Squash, opts.Filters)
if err == nil {
if opts.Base != "" || opts.Squash {
changes, err := git.DiffNameStatusBetween(ctx, opts.Workspace.Path, opts.RangeStart, opts.RangeEnd, opts.Filters)
if err != nil {
return nil, err
}
scope = changedScope(changes)
} else {
scope = patch.ScopeFromSet(set)
}
}
default:
mode = "working-tree"
reportProgress(opts.Progress, "Extracting workspace changes")
set, err = patch.BuildWorkingTreePatchSet(ctx, opts.Workspace.Path, base, opts.Filters)
if err == nil && len(opts.Filters) > 0 {
scope = opts.Filters
}
}
if err != nil {
return nil, err
}
reportProgress(opts.Progress, "Writing %d patch %s", len(set), plural(len(set), "file", "files"))
written, deleted, err := patch.WriteRepoPatchSet(opts.Repo.PatchesDir, set, scope)
if err != nil {
return nil, err
}
state, err := workspace.LoadState(opts.Workspace.Path)
if err != nil {
return nil, err
}
head, err := git.HeadRev(ctx, opts.Workspace.Path)
if err != nil {
return nil, err
}
state.BaseCommit = opts.Repo.BaseCommit
state.LastExtractRev = head
state.LastExtractAt = time.Now().UTC()
if err := workspace.SaveState(opts.Workspace.Path, state); err != nil {
return nil, err
}
return &ExtractResult{
Workspace: opts.Workspace.Name,
Mode: mode,
BaseCommit: base,
Written: written,
Deleted: deleted,
}, nil
}
func changedScope(changes []git.FileChange) []string {
scope := make([]string, 0, len(changes))
for _, change := range changes {
rel := patch.NormalizeChromiumPath(change.Path)
if patch.IsInternalPath(rel) {
continue
}
scope = append(scope, rel)
}
return scope
}