Skip to content

Commit

Permalink
add tags panel
Browse files Browse the repository at this point in the history
  • Loading branch information
jesseduffield committed Nov 21, 2019
1 parent cea24c2 commit 3c13229
Show file tree
Hide file tree
Showing 14 changed files with 390 additions and 6 deletions.
8 changes: 7 additions & 1 deletion pkg/commands/commit.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package commands

import (
"strings"

"github.com/fatih/color"
"github.com/jesseduffield/lazygit/pkg/theme"
"github.com/jesseduffield/lazygit/pkg/utils"
Expand All @@ -14,6 +16,7 @@ type Commit struct {
DisplayString string
Action string // one of "", "pick", "edit", "squash", "reword", "drop", "fixup"
Copied bool // to know if this commit is ready to be cherry-picked somewhere
Tags []string
}

// GetDisplayStrings is a function.
Expand Down Expand Up @@ -52,9 +55,12 @@ func (c *Commit) GetDisplayStrings(isFocused bool) []string {
}

actionString := ""
tagString := ""
if c.Action != "" {
actionString = cyan.Sprint(utils.WithPadding(c.Action, 7)) + " "
} else if len(c.Tags) > 0 {
tagString = utils.ColoredString(strings.Join(c.Tags, " "), color.FgMagenta) + " "
}

return []string{shaColor.Sprint(c.Sha), actionString + defaultColor.Sprint(c.Name)}
return []string{shaColor.Sprint(c.Sha), actionString + tagString + defaultColor.Sprint(c.Name)}
}
1 change: 1 addition & 0 deletions pkg/commands/commit_list_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ func (c *CommitListBuilder) GetCommits() ([]*Commit, error) {
Name: strings.Join(splitLine[1:], " "),
Status: status,
DisplayString: strings.Join(splitLine, " "),
// TODO: add tags here
})
}
if rebaseMode != "" {
Expand Down
18 changes: 17 additions & 1 deletion pkg/commands/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,7 @@ func (c *GitCommand) Push(branchName string, force bool, upstream string, ask fu
setUpstreamArg = "--set-upstream " + upstream
}

cmd := fmt.Sprintf("git push %s %s", forceFlag, setUpstreamArg)
cmd := fmt.Sprintf("git push --follow-tags %s %s", forceFlag, setUpstreamArg)
return c.OSCommand.DetectUnamePass(cmd, ask)
}

Expand Down Expand Up @@ -1099,3 +1099,19 @@ func (c *GitCommand) RenameRemote(oldRemoteName string, newRemoteName string) er
func (c *GitCommand) UpdateRemoteUrl(remoteName string, updatedUrl string) error {
return c.OSCommand.RunCommand(fmt.Sprintf("git remote set-url %s %s", remoteName, updatedUrl))
}

func (c *GitCommand) CreateLightweightTag(tagName string, commitSha string) error {
return c.OSCommand.RunCommand(fmt.Sprintf("git tag %s %s", tagName, commitSha))
}

func (c *GitCommand) ShowTag(tagName string) (string, error) {
return c.OSCommand.RunCommandWithOutput(fmt.Sprintf("git tag -n99 %s", tagName))
}

func (c *GitCommand) DeleteTag(tagName string) error {
return c.OSCommand.RunCommand(fmt.Sprintf("git tag -d %s", tagName))
}

func (c *GitCommand) PushTag(remoteName string, tagName string) error {
return c.OSCommand.RunCommand(fmt.Sprintf("git push %s %s", remoteName, tagName))
}
78 changes: 78 additions & 0 deletions pkg/commands/loading_tags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package commands

import (
"regexp"
"sort"
"strings"

"github.com/jesseduffield/lazygit/pkg/utils"
)

const semverRegex = `v?((\d+\.?)+)([^\d]?.*)`

func (c *GitCommand) GetTags() ([]*Tag, error) {
// get remote branches
remoteBranchesStr, err := c.OSCommand.RunCommandWithOutput("git tag --list")
if err != nil {
return nil, err
}

content := utils.TrimTrailingNewline(remoteBranchesStr)
if content == "" {
return nil, nil
}

split := strings.Split(content, "\n")

// first step is to get our remotes from go-git
tags := make([]*Tag, len(split))
for i, tagName := range split {

tags[i] = &Tag{
Name: tagName,
}
}

// now lets sort our tags by name numerically
re := regexp.MustCompile(semverRegex)

// the reason this is complicated is because we're both sorting alphabetically
// and when we're dealing with semver strings
sort.Slice(tags, func(i, j int) bool {
a := tags[i].Name
b := tags[j].Name

matchA := re.FindStringSubmatch(a)
matchB := re.FindStringSubmatch(b)

if len(matchA) > 0 && len(matchB) > 0 {
numbersA := strings.Split(matchA[1], ".")
numbersB := strings.Split(matchB[1], ".")
k := 0
for {
if len(numbersA) == k && len(numbersB) == k {
break
}
if len(numbersA) == k {
return true
}
if len(numbersB) == k {
return false
}
if mustConvertToInt(numbersA[k]) < mustConvertToInt(numbersB[k]) {
return true
}
if mustConvertToInt(numbersA[k]) > mustConvertToInt(numbersB[k]) {
return false
}
k++
}

return strings.ToLower(matchA[3]) < strings.ToLower(matchB[3])
}

return strings.ToLower(a) < strings.ToLower(b)
})

return tags, nil
}
11 changes: 11 additions & 0 deletions pkg/commands/tag.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package commands

// Tag : A git tag
type Tag struct {
Name string
}

// GetDisplayStrings returns the display string of a remote
func (r *Tag) GetDisplayStrings(isFocused bool) []string {
return []string{r.Name}
}
9 changes: 8 additions & 1 deletion pkg/gui/branches_panel.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ func (gui *Gui) refreshBranches(g *gocui.Gui) error {
return err
}

if err := gui.refreshTags(); err != nil {
return err
}

g.Update(func(g *gocui.Gui) error {
builder, err := commands.NewBranchListBuilder(gui.Log, gui.GitCommand)
if err != nil {
Expand Down Expand Up @@ -373,7 +377,7 @@ func (gui *Gui) handleFastForward(g *gocui.Gui, v *gocui.View) error {
}

func (gui *Gui) onBranchesTabClick(tabIndex int) error {
contexts := []string{"local-branches", "remotes", "tabs"}
contexts := []string{"local-branches", "remotes", "tags"}
branchesView := gui.getBranchesView()
branchesView.TabIndex = tabIndex

Expand All @@ -388,6 +392,7 @@ func (gui *Gui) switchBranchesPanelContext(context string) error {
"local-branches": 0,
"remotes": 1,
"remote-branches": 1,
"tags": 2,
}

branchesView.TabIndex = contextTabIndexMap[context]
Expand All @@ -399,6 +404,8 @@ func (gui *Gui) switchBranchesPanelContext(context string) error {
return gui.renderRemotesWithSelection()
case "remote-branches":
return gui.renderRemoteBranchesWithSelection()
case "tags":
return gui.renderTagsWithSelection()
}

return nil
Expand Down
27 changes: 27 additions & 0 deletions pkg/gui/commits_panel.go
Original file line number Diff line number Diff line change
Expand Up @@ -584,3 +584,30 @@ func (gui *Gui) handleCreateCommitResetMenu(g *gocui.Gui, v *gocui.View) error {

return gui.createMenu(fmt.Sprintf("%s %s", gui.Tr.SLocalize("resetTo"), commit.Sha), options, len(options), handleMenuPress)
}

func (gui *Gui) handleTagCommit(g *gocui.Gui, v *gocui.View) error {
// TODO: bring up menu asking if you want to make a lightweight or annotated tag
// if annotated, switch to a subprocess to create the message

commit := gui.getSelectedCommit(g)
if commit == nil {
return nil
}

return gui.handleCreateLightweightTag(commit.Sha)
}

func (gui *Gui) handleCreateLightweightTag(commitSha string) error {
return gui.createPromptPanel(gui.g, gui.getCommitsView(), gui.Tr.SLocalize("TagNameTitle"), "", func(g *gocui.Gui, v *gocui.View) error {
if err := gui.GitCommand.CreateLightweightTag(v.Buffer(), commitSha); err != nil {
return gui.createErrorPanel(g, err.Error())
}
if err := gui.refreshCommits(g); err != nil {
return gui.createErrorPanel(g, err.Error())
}
if err := gui.refreshTags(); err != nil {
return gui.createErrorPanel(g, err.Error())
}
return gui.handleCommitSelect(g, v)
})
}
9 changes: 8 additions & 1 deletion pkg/gui/gui.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,10 @@ type remoteBranchesState struct {
SelectedLine int
}

type tagsPanelState struct {
SelectedLine int
}

type commitPanelState struct {
SelectedLine int
SpecificDiffMode bool
Expand Down Expand Up @@ -148,6 +152,7 @@ type panelStates struct {
Branches *branchPanelState
Remotes *remotePanelState
RemoteBranches *remoteBranchesState
Tags *tagsPanelState
Commits *commitPanelState
Stash *stashPanelState
Menu *menuPanelState
Expand All @@ -166,6 +171,7 @@ type guiState struct {
DiffEntries []*commands.Commit
Remotes []*commands.Remote
RemoteBranches []*commands.RemoteBranch
Tags []*commands.Tag
MenuItemCount int // can't store the actual list because it's of interface{} type
PreviousView string
Platform commands.Platform
Expand Down Expand Up @@ -198,6 +204,7 @@ func NewGui(log *logrus.Entry, gitCommand *commands.GitCommand, oSCommand *comma
Branches: &branchPanelState{SelectedLine: 0},
Remotes: &remotePanelState{SelectedLine: 0},
RemoteBranches: &remoteBranchesState{SelectedLine: -1},
Tags: &tagsPanelState{SelectedLine: -1},
Commits: &commitPanelState{SelectedLine: -1},
CommitFiles: &commitFilesPanelState{SelectedLine: -1},
Stash: &stashPanelState{SelectedLine: -1},
Expand Down Expand Up @@ -497,7 +504,7 @@ func (gui *Gui) layout(g *gocui.Gui) error {
return err
}
branchesView.Title = gui.Tr.SLocalize("BranchesTitle")
branchesView.Tabs = []string{"Local Branches", "Remotes"}
branchesView.Tabs = []string{"Local Branches", "Remotes", "Tags"}
branchesView.FgColor = textColor
}

Expand Down
39 changes: 39 additions & 0 deletions pkg/gui/keybindings.go
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,38 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
Handler: gui.handleFastForward,
Description: gui.Tr.SLocalize("FastForward"),
},
{
ViewName: "branches",
Contexts: []string{"tags"},
Key: gocui.KeySpace,
Modifier: gocui.ModNone,
Handler: gui.handleCheckoutTag,
Description: gui.Tr.SLocalize("checkout"),
},
{
ViewName: "branches",
Contexts: []string{"tags"},
Key: 'd',
Modifier: gocui.ModNone,
Handler: gui.handleDeleteTag,
Description: gui.Tr.SLocalize("deleteTag"),
},
{
ViewName: "branches",
Contexts: []string{"tags"},
Key: 'P',
Modifier: gocui.ModNone,
Handler: gui.handlePushTag,
Description: gui.Tr.SLocalize("pushTags"),
},
{
ViewName: "branches",
Contexts: []string{"tags"},
Key: 'n',
Modifier: gocui.ModNone,
Handler: gui.handleCreateTag,
Description: gui.Tr.SLocalize("createTag"),
},
{
ViewName: "branches",
Key: ']',
Expand Down Expand Up @@ -555,6 +587,13 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
Handler: gui.handleToggleDiffCommit,
Description: gui.Tr.SLocalize("CommitsDiff"),
},
{
ViewName: "commits",
Key: 'T',
Modifier: gocui.ModNone,
Handler: gui.handleTagCommit,
Description: gui.Tr.SLocalize("tagCommit"),
},
{
ViewName: "stash",
Key: gocui.KeySpace,
Expand Down
10 changes: 10 additions & 0 deletions pkg/gui/list_view.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,16 @@ func (gui *Gui) getListViews() []*listView {
gui: gui,
rendersToMainView: true,
},
{
viewName: "branches",
context: "tags",
getItemsLength: func() int { return len(gui.State.Tags) },
getSelectedLineIdxPtr: func() *int { return &gui.State.Panels.Tags.SelectedLine },
handleFocus: gui.handleTagSelect,
handleItemSelect: gui.handleTagSelect,
gui: gui,
rendersToMainView: true,
},
{
viewName: "commits",
getItemsLength: func() int { return len(gui.State.Commits) },
Expand Down
8 changes: 6 additions & 2 deletions pkg/gui/remotes_panel.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,16 @@ func (gui *Gui) handleRemoteSelect(g *gocui.Gui, v *gocui.View) error {
gui.getMainView().Title = "Remote"

remote := gui.getSelectedRemote()
if remote == nil {
return gui.renderString(g, "main", "No remotes")
}
if err := gui.focusPoint(0, gui.State.Panels.Remotes.SelectedLine, len(gui.State.Remotes), v); err != nil {
return err
}

return gui.renderString(g, "main", fmt.Sprintf("%s\nUrls:\n%s", utils.ColoredString(remote.Name, color.FgGreen), strings.Join(remote.Urls, "\n")))
}

// gui.refreshStatus is called at the end of this because that's when we can
// be sure there is a state.Remotes array to pick the current remote from
func (gui *Gui) refreshRemotes() error {
prevSelectedRemote := gui.getSelectedRemote()

Expand Down Expand Up @@ -92,6 +93,9 @@ func (gui *Gui) renderRemotesWithSelection() error {
func (gui *Gui) handleRemoteEnter(g *gocui.Gui, v *gocui.View) error {
// naive implementation: get the branches and render them to the list, change the context
remote := gui.getSelectedRemote()
if remote == nil {
return nil
}

gui.State.RemoteBranches = remote.Branches

Expand Down
Loading

0 comments on commit 3c13229

Please sign in to comment.