Skip to content

Commit

Permalink
feat(branches): fast-forward + refresh on interval (#427)
Browse files Browse the repository at this point in the history
* feat(branch): pull

* fix: auto-refresh

* fix(branch): pull curr checked out branch

* fix(branches): fast-forward to use fetch if not current branch

* fix: remove log
  • Loading branch information
dlvhdr authored Aug 30, 2024
1 parent 39b54bc commit 4e10f81
Show file tree
Hide file tree
Showing 9 changed files with 228 additions and 85 deletions.
27 changes: 22 additions & 5 deletions git/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"errors"
"fmt"
"sort"
"strings"
"time"

gitm "github.com/aymanbagabas/git-module"
Expand All @@ -13,6 +14,7 @@ import (

// Extends git.Repository
type Repo struct {
gitm.Repository
Origin string
Remotes []string
Branches []Branch
Expand Down Expand Up @@ -88,10 +90,7 @@ func GetRepo(dir string) (*Repo, error) {
if err != nil {
commitsBehind = 0
}
remotes, err := repo.RemoteGetURL(b)
if err != nil {
commitsBehind = 0
}
remotes, _ := repo.RemoteGetURL(b)
branches[i] = Branch{
Name: b,
LastUpdatedAt: updatedAt,
Expand Down Expand Up @@ -119,5 +118,23 @@ func GetRepo(dir string) (*Repo, error) {
return nil, err
}

return &Repo{Origin: origin[0], Remotes: remotes, Branches: branches}, nil
return &Repo{Repository: *repo, Origin: origin[0], Remotes: remotes, Branches: branches}, nil
}

func FetchRepo(dir string) (*Repo, error) {
repo, err := gitm.Open(dir)
if err != nil {
return nil, err
}
err = repo.Fetch(gitm.FetchOptions{CommandOptions: gitm.CommandOptions{Args: []string{"--all"}}})
if err != nil {
return nil, err
}
return GetRepo(dir)
}

func GetRepoShortName(url string) string {
r, _ := strings.CutPrefix(url, "https://github.com/")
r, _ = strings.CutSuffix(r, ".git")
return r
}
6 changes: 3 additions & 3 deletions ui/components/footer/footer.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/charmbracelet/lipgloss"

"github.com/dlvhdr/gh-dash/v4/config"
"github.com/dlvhdr/gh-dash/v4/git"
"github.com/dlvhdr/gh-dash/v4/ui/context"
"github.com/dlvhdr/gh-dash/v4/ui/keys"
"github.com/dlvhdr/gh-dash/v4/utils"
Expand Down Expand Up @@ -124,9 +125,8 @@ func (m *Model) renderViewSwitcher(ctx context.ProgramContext) string {
} else if ctx.View == config.RepoView {
repo := m.ctx.RepoPath
if m.ctx.RepoUrl != nil {
r, _ := strings.CutPrefix(*m.ctx.RepoUrl, "https://github.com/")
r, _ = strings.CutSuffix(r, ".git")
repo = &r
shortName := git.GetRepoShortName(*m.ctx.RepoUrl)
repo = &shortName
}
view += fmt.Sprintf(" %s", *repo)
}
Expand Down
141 changes: 125 additions & 16 deletions ui/components/reposection/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,56 @@ type UpdatePRMsg struct {
RemovedAssignees *data.Assignees
}

func (m *Model) fastForward() (tea.Cmd, error) {
b := m.getCurrBranch()

taskId := fmt.Sprintf("fast-forward_%s_%d", b.Data.Name, time.Now().Unix())
task := context.Task{
Id: taskId,
StartText: fmt.Sprintf("Fast-forwarding branch %s", b.Data.Name),
FinishedText: fmt.Sprintf("Branch %s has been fast-forwarded", b.Data.Name),
State: context.TaskStart,
Error: nil,
}
startCmd := m.Ctx.StartTask(task)
return tea.Batch(startCmd, func() tea.Msg {
var err error
repo, err := git.GetRepo(*m.Ctx.RepoPath)
if err != nil {
return constants.TaskFinishedMsg{TaskId: taskId, Err: err}
}

if b.Data.IsCheckedOut {
err = repo.Pull(gitm.PullOptions{
All: false,
Remote: "origin",
Branch: b.Data.Name,
CommandOptions: gitm.CommandOptions{Args: []string{"--ff-only", "--no-edit"}},
})
} else {
err = repo.Fetch(gitm.FetchOptions{CommandOptions: gitm.CommandOptions{Args: []string{
"--no-write-fetch-head",
"origin",
b.Data.Name + ":" + b.Data.Name,
}}})
}
if err != nil {
return constants.TaskFinishedMsg{TaskId: taskId, Err: err}
}
if err != nil {
return constants.TaskFinishedMsg{TaskId: taskId, Err: err}
}

return constants.TaskFinishedMsg{
SectionId: 0,
SectionType: SectionType,
TaskId: taskId,
Msg: repoMsg{repo: repo},
Err: err,
}
}), nil
}

func (m *Model) push() (tea.Cmd, error) {
b := m.getCurrBranch()

Expand Down Expand Up @@ -94,28 +144,27 @@ func (m *Model) checkout() (tea.Cmd, error) {

type repoMsg struct {
repo *git.Repo
err error
}

func (m *Model) readRepoCmd() []tea.Cmd {
cmds := make([]tea.Cmd, 0)
branchesTaskId := fmt.Sprintf("fetching_branches_%d", time.Now().Unix())
if m.Ctx.RepoPath != nil {
branchesTask := context.Task{
Id: branchesTaskId,
StartText: "Reading local branches",
FinishedText: fmt.Sprintf(
`Read branches successfully for "%s"`,
*m.Ctx.RepoPath,
),
State: context.TaskStart,
Error: nil,
Id: branchesTaskId,
StartText: "Reading local branches",
FinishedText: "Branches read",
State: context.TaskStart,
Error: nil,
}
bCmd := m.Ctx.StartTask(branchesTask)
cmds = append(cmds, bCmd)
}
cmds = append(cmds, func() tea.Msg {
repo, err := git.GetRepo(*m.Ctx.RepoPath)
if err != nil {
return constants.TaskFinishedMsg{TaskId: branchesTaskId, Err: err}
}
return constants.TaskFinishedMsg{
SectionId: 0,
SectionType: SectionType,
Expand All @@ -127,24 +176,52 @@ func (m *Model) readRepoCmd() []tea.Cmd {
return cmds
}

func (m *Model) fetchPRsCmd() []tea.Cmd {
prsTaskId := fmt.Sprintf("fetching_pr_branches_%d", time.Now().Unix())
func (m *Model) fetchRepoCmd() []tea.Cmd {
cmds := make([]tea.Cmd, 0)
fetchTaskId := fmt.Sprintf("git_fetch_repo_%d", time.Now().Unix())
if m.Ctx.RepoPath == nil {
return []tea.Cmd{}
}
fetchTask := context.Task{
Id: fetchTaskId,
StartText: "Fetching branches from origin",
FinishedText: "Fetched origin branches",
State: context.TaskStart,
Error: nil,
}
cmds = append(cmds, m.Ctx.StartTask(fetchTask))
cmds = append(cmds, func() tea.Msg {
repo, err := git.FetchRepo(*m.Ctx.RepoPath)
if err != nil {
return constants.TaskFinishedMsg{TaskId: fetchTaskId, Err: err}
}
return constants.TaskFinishedMsg{
SectionId: 0,
SectionType: SectionType,
TaskId: fetchTaskId,
Msg: repoMsg{repo: repo},
Err: err,
}
})
return cmds
}

func (m *Model) fetchPRsCmd() tea.Cmd {
prsTaskId := fmt.Sprintf("fetching_pr_branches_%d", time.Now().Unix())
task := context.Task{
Id: prsTaskId,
StartText: "Fetching PRs for your branches",
FinishedText: "PRs for your branches have been fetched",
State: context.TaskStart,
Error: nil,
}
cmds = append(cmds, m.Ctx.StartTask(task))
cmds = append(cmds, func() tea.Msg {
startCmd := m.Ctx.StartTask(task)
return tea.Batch(startCmd, func() tea.Msg {
limit := m.Config.Limit
if limit == nil {
limit = &m.Ctx.Config.Defaults.PrsLimit
}
res, err := data.FetchPullRequests("author:@me", *limit, nil)
// TODO: enrich with branches only for section with branches
res, err := data.FetchPullRequests(fmt.Sprintf("author:@me repo:%s", git.GetRepoShortName(*m.Ctx.RepoUrl)), *limit, nil)
if err != nil {
return constants.TaskFinishedMsg{
SectionId: 0,
Expand All @@ -153,7 +230,6 @@ func (m *Model) fetchPRsCmd() []tea.Cmd {
Err: err,
}
}

return constants.TaskFinishedMsg{
SectionId: 0,
SectionType: SectionType,
Expand All @@ -166,5 +242,38 @@ func (m *Model) fetchPRsCmd() []tea.Cmd {
},
}
})
}

type RefreshBranchesMsg time.Time

type FetchMsg time.Time

const refreshIntervalSec = 30

const fetchIntervalSec = 60

func (m *Model) tickRefreshBranchesCmd() tea.Cmd {
return tea.Tick(time.Second*refreshIntervalSec, func(t time.Time) tea.Msg {
return RefreshBranchesMsg(t)
})
}

func (m *Model) tickFetchCmd() tea.Cmd {
return tea.Tick(time.Second*fetchIntervalSec, func(t time.Time) tea.Msg {
return FetchMsg(t)
})
}

func (m *Model) onRefreshBranchesMsg() []tea.Cmd {
cmds := make([]tea.Cmd, 0)
cmds = append(cmds, m.readRepoCmd()...)
cmds = append(cmds, m.tickRefreshBranchesCmd())
return cmds
}

func (m *Model) onFetchMsg() []tea.Cmd {
cmds := make([]tea.Cmd, 0)
cmds = append(cmds, m.fetchRepoCmd()...)
cmds = append(cmds, m.tickRefreshBranchesCmd())
return cmds
}
57 changes: 43 additions & 14 deletions ui/components/reposection/reposection.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,10 @@ const SectionType = "repo"

type Model struct {
section.BaseModel
repo *git.Repo
Branches []branch.Branch
Prs []data.PullRequestData
repo *git.Repo
Branches []branch.Branch
Prs []data.PullRequestData
isRefreshSetUp bool
}

func NewModel(
Expand All @@ -53,12 +54,14 @@ func NewModel(
m.repo = &git.Repo{Branches: []git.Branch{}}
m.Branches = []branch.Branch{}
m.Prs = []data.PullRequestData{}
m.isRefreshSetUp = false

return m
}

func (m *Model) Update(msg tea.Msg) (section.Section, tea.Cmd) {
var cmd tea.Cmd
var cmds []tea.Cmd
var err error

switch msg := msg.(type) {
Expand All @@ -77,6 +80,11 @@ func (m *Model) Update(msg tea.Msg) (section.Section, tea.Cmd) {
if err != nil {
m.Ctx.Error = err
}
case key.Matches(msg, keys.BranchKeys.FastForward):
cmd, err = m.fastForward()
if err != nil {
m.Ctx.Error = err
}

}

Expand All @@ -85,16 +93,32 @@ func (m *Model) Update(msg tea.Msg) (section.Section, tea.Cmd) {
m.Table.SetIsLoading(false)
m.updateBranches()
m.Table.SetRows(m.BuildRows())
m.Table.ResetCurrItem()

case SectionPullRequestsFetchedMsg:
m.Prs = msg.Prs
m.updateBranches()
m.TotalCount = msg.TotalCount
m.PageInfo = &msg.PageInfo
m.Table.SetIsLoading(false)
m.Table.SetRows(m.BuildRows())
m.Table.UpdateLastUpdated(time.Now())
m.UpdateTotalItemsCount(m.TotalCount)

case RefreshBranchesMsg:
cmds = append(cmds, m.onRefreshBranchesMsg()...)

case FetchMsg:
cmds = append(cmds, m.onFetchMsg()...)
}

m.Table.SetRows(m.BuildRows())
cmds = append(cmds, cmd)

m.Table.SetRows(m.BuildRows())
table, tableCmd := m.Table.Update(msg)
m.Table = table
cmds = append(cmds, tableCmd)

return m, tea.Batch(cmd, tableCmd)
return m, tea.Batch(cmds...)
}

func (m *Model) View() string {
Expand Down Expand Up @@ -335,12 +359,12 @@ func (m *Model) FetchNextPageSectionRows() []tea.Cmd {
var cmds []tea.Cmd
if m.Ctx.RepoPath != nil {
cmds = append(cmds, m.readRepoCmd()...)
cmds = append(cmds, m.fetchPRsCmd()...)
cmds = append(cmds, m.fetchRepoCmd()...)
cmds = append(cmds, m.fetchPRsCmd())
}

m.Table.SetIsLoading(true)
cmds = append(cmds, m.Table.StartLoadingSpinner())

return cmds
}

Expand All @@ -349,22 +373,27 @@ func FetchAllBranches(ctx context.ProgramContext) (Model, tea.Cmd) {

t := config.RepoView
cfg := config.PrsSectionConfig{
Title: "Local Branches",
Filters: "author:@me",
Limit: utils.IntPtr(20),
Type: &t,
Title: "Local Branches",
Type: &t,
}
m := NewModel(
1,
0,
&ctx,
cfg,
time.Now(),
)

if ctx.RepoPath != nil {
cmds = append(cmds, m.readRepoCmd()...)
cmds = append(cmds, m.fetchRepoCmd()...)
cmds = append(cmds, m.fetchPRsCmd())
}

if !m.isRefreshSetUp {
m.isRefreshSetUp = true
cmds = append(cmds, m.tickRefreshBranchesCmd())
cmds = append(cmds, m.tickFetchCmd())
}
cmds = append(cmds, m.FetchNextPageSectionRows()...)

return m, tea.Batch(cmds...)
}
Expand Down
Loading

0 comments on commit 4e10f81

Please sign in to comment.