1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-03-05 15:15:49 +02:00

Merge branch 'master' into feature/use-dep

This commit is contained in:
Jesse Duffield 2018-08-07 12:31:51 +10:00
commit eabf07248a
6 changed files with 159 additions and 139 deletions

View File

@ -17,6 +17,21 @@ Then just call `lazygit` in your terminal inside a git repository.
If you want, you can also add an alias for this with `echo "alias lg='lazygit'" >> ~/.zshrc` (or whichever rc file you're using).
Please note:
On MacOS you may have to add `~/go/bin` to your $PATH variable.
### Ubuntu
Packages for Ubuntu 14.04 and up are available via Launchpad PPA.
They are built daily, straight from master branch.
```sh
sudo add-apt-repository ppa:lazygit-team/daily
sudo apt-get update
sudo apt-get install lazygit
```
## Cool features
- Adding files easily
- Resolving merge conflicts
@ -31,5 +46,18 @@ If you want, you can also add an alias for this with `echo "alias lg='lazygit'"
### Viewing commit diffs
![Viewing Commit Diffs](https://image.ibb.co/gPD02o/capture.png)
## Docs
[Keybindings](https://github.com/jesseduffield/lazygit/blob/master/docs/Keybindings.md)
## Milestones
- [ ] Easy Installation (homebrew, release binaries)
- [ ] Configurable Keybindings
- [ ] Configurable Color Themes
- [ ] Spawning Subprocesses (help needed - have a look at https://github.com/jesseduffield/lazygit/pull/18)
- [ ] i18n
## Contributing
I'll find a good template for contributing and then add it to the repo (or if somebody has a suggestion please put up a PR)
## Work in progress
This is still a work in progress so there's still bugs to iron out and as this is my first project in Go the code could no doubt use an increase in quality, but I'll be improving on it whenever I find the time. If you have any feedback feel free to [raise an issue](https://github.com/jesseduffield/lazygit/issues)/[submit a PR](https://github.com/jesseduffield/lazygit/pulls).

View File

@ -90,7 +90,7 @@ func handleBranchSelect(g *gocui.Gui, v *gocui.View) error {
}
go func() {
branch := getSelectedBranch(v)
diff, _ := getBranchDiff(branch.Name, branch.BaseBranch)
diff, _ := getBranchGraph(branch.Name, branch.BaseBranch)
renderString(g, "main", diff)
}()
return nil

54
docs/Keybindings.md Normal file
View File

@ -0,0 +1,54 @@
# Keybindings:
## Global:
← → ↑ ↓: navigate
PgUp/PgDn: scroll diff panel (use fn+up/down on osx)
q: quit
p: pull
shift+P: push
## Files Panel:
space: toggle staged
c: commit changes
shift+S: stash files
o: open (osx only)
s: open in sublime (requires 'subl' command)
v: open in vscode (requires 'code' command)
i: add to .gitignore
d: delete if untracked checkout if tracked (aka go away)
shift+R: refresh files
## Branches Panel:
space: checkout branch
f: force checkout branch
m: merge into currently checked out branch
c: checkout by name
n: new branch
## Commits Panel:
s: squash down (only available for topmost commit)
r: rename commit
g: reset to this commit
## Stash Panel:
space: apply
k: pop
d: drop
## Popup Panel:
esc: close/cancel
enter: confirm
## Resolving Merge Conflicts (Diff Panel):
← →: navigate conflicts
↑ ↓: select hunk
space: pick hunk
b: pick both hunks
z: undo (only available while still inside diff panel)

View File

@ -178,10 +178,14 @@ func getGitBranches() []Branch {
if branchCheck == "" {
return append(branches, branchFromLine("master", 0))
}
rawString, _ := runDirectCommand(getBranchesCommand)
branchLines := splitLines(rawString)
for i, line := range branchLines {
branches = append(branches, branchFromLine(line, i))
if rawString, err := runDirectCommand(getBranchesCommand); err == nil {
branchLines := splitLines(rawString)
for i, line := range branchLines {
branches = append(branches, branchFromLine(line, i))
}
} else {
// TODO: DRY this up
branches = append(branches, branchFromLine(gitCurrentBranchName(), 0))
}
branches = getAndMergeFetchedBranches(branches)
return branches
@ -327,10 +331,12 @@ func sublimeOpenFile(filename string) (string, error) {
return runCommand("subl " + filename)
}
func getBranchDiff(branch string, baseBranch string) (string, error) {
func getBranchGraph(branch string, baseBranch string) (string, error) {
return runCommand("git log --graph --color --abbrev-commit --decorate --date=relative --pretty=medium -100 " + branch)
return runCommand("git log -p -30 --color --no-merges " + branch)
// return runCommand("git diff --color " + baseBranch + "..." + branch)
// Leaving this guy commented out in case there's backlash from the design
// change and I want to make this configurable
// return runCommand("git log -p -30 --color --no-merges " + branch)
}
func verifyInGitRepo() {
@ -465,7 +471,7 @@ func gitPush() (string, error) {
}
func gitSquashPreviousTwoCommits(message string) (string, error) {
return runDirectCommand("git reset --soft head^ && git commit --amend -m \"" + message + "\"")
return runDirectCommand("git reset --soft HEAD^ && git commit --amend -m \"" + message + "\"")
}
func gitRenameCommit(message string) (string, error) {
@ -522,7 +528,7 @@ func gitCurrentBranchName() string {
if err != nil {
return ""
}
return branchName
return strings.TrimSpace(branchName)
}
const getBranchesCommand = `set -e

188
gui.go
View File

@ -70,135 +70,67 @@ func handleRefresh(g *gocui.Gui, v *gocui.View) error {
return refreshSidePanels(g)
}
func keybindings(g *gocui.Gui) error {
if err := g.SetKeybinding("", gocui.KeyArrowRight, gocui.ModNone, nextView); err != nil {
return err
}
if err := g.SetKeybinding("", gocui.KeyTab, gocui.ModNone, nextView); err != nil {
return err
}
if err := g.SetKeybinding("", gocui.KeyArrowLeft, gocui.ModNone, previousView); err != nil {
return err
}
if err := g.SetKeybinding("", 'q', gocui.ModNone, quit); err != nil {
return err
}
if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil {
return err
}
if err := g.SetKeybinding("", gocui.KeyArrowDown, gocui.ModNone, cursorDown); err != nil {
return err
}
if err := g.SetKeybinding("", gocui.KeyArrowUp, gocui.ModNone, cursorUp); err != nil {
return err
}
if err := g.SetKeybinding("", gocui.KeyPgup, gocui.ModNone, scrollUpMain); err != nil {
return err
}
if err := g.SetKeybinding("", gocui.KeyPgdn, gocui.ModNone, scrollDownMain); err != nil {
return err
}
if err := g.SetKeybinding("", 'P', gocui.ModNone, pushFiles); err != nil {
return err
}
if err := g.SetKeybinding("", 'p', gocui.ModNone, pullFiles); err != nil {
return err
}
if err := g.SetKeybinding("", 'R', gocui.ModNone, handleRefresh); err != nil {
return err
}
if err := g.SetKeybinding("files", 'c', gocui.ModNone, handleCommitPress); err != nil {
return err
}
if err := g.SetKeybinding("files", gocui.KeySpace, gocui.ModNone, handleFilePress); err != nil {
return err
}
if err := g.SetKeybinding("files", 'd', gocui.ModNone, handleFileRemove); err != nil {
return err
}
if err := g.SetKeybinding("files", 'm', gocui.ModNone, handleSwitchToMerge); err != nil {
return err
}
if err := g.SetKeybinding("files", 'o', gocui.ModNone, handleFileOpen); err != nil {
return err
}
if err := g.SetKeybinding("files", 's', gocui.ModNone, handleSublimeFileOpen); err != nil {
return err
}
if err := g.SetKeybinding("files", 'v', gocui.ModNone, handleVsCodeFileOpen); err != nil {
return err
}
if err := g.SetKeybinding("files", 'i', gocui.ModNone, handleIgnoreFile); err != nil {
return err
}
if err := g.SetKeybinding("files", 'r', gocui.ModNone, handleRefreshFiles); err != nil {
return err
}
if err := g.SetKeybinding("files", 'S', gocui.ModNone, handleStashSave); err != nil {
return err
}
if err := g.SetKeybinding("files", 'a', gocui.ModNone, handleAbortMerge); err != nil {
return err
}
if err := g.SetKeybinding("main", gocui.KeyArrowUp, gocui.ModNone, handleSelectTop); err != nil {
return err
}
if err := g.SetKeybinding("main", gocui.KeyEsc, gocui.ModNone, handleEscapeMerge); err != nil {
return err
}
if err := g.SetKeybinding("main", gocui.KeyArrowDown, gocui.ModNone, handleSelectBottom); err != nil {
return err
}
if err := g.SetKeybinding("main", gocui.KeySpace, gocui.ModNone, handlePickHunk); err != nil {
return err
}
if err := g.SetKeybinding("main", 'b', gocui.ModNone, handlePickBothHunks); err != nil {
return err
}
if err := g.SetKeybinding("main", gocui.KeyArrowLeft, gocui.ModNone, handleSelectPrevConflict); err != nil {
return err
}
if err := g.SetKeybinding("main", gocui.KeyArrowRight, gocui.ModNone, handleSelectNextConflict); err != nil {
return err
}
if err := g.SetKeybinding("main", 'z', gocui.ModNone, handlePopFileSnapshot); err != nil {
return err
}
if err := g.SetKeybinding("branches", gocui.KeySpace, gocui.ModNone, handleBranchPress); err != nil {
return err
}
if err := g.SetKeybinding("branches", 'c', gocui.ModNone, handleCheckoutByName); err != nil {
return err
}
if err := g.SetKeybinding("branches", 'F', gocui.ModNone, handleForceCheckout); err != nil {
return err
}
if err := g.SetKeybinding("branches", 'n', gocui.ModNone, handleNewBranch); err != nil {
return err
}
if err := g.SetKeybinding("branches", 'm', gocui.ModNone, handleMerge); err != nil {
return err
}
if err := g.SetKeybinding("commits", 's', gocui.ModNone, handleCommitSquashDown); err != nil {
return err
}
if err := g.SetKeybinding("commits", 'r', gocui.ModNone, handleRenameCommit); err != nil {
return err
}
if err := g.SetKeybinding("commits", 'g', gocui.ModNone, handleResetToCommit); err != nil {
return err
}
if err := g.SetKeybinding("stash", gocui.KeySpace, gocui.ModNone, handleStashApply); err != nil {
return err
}
// TODO: come up with a better keybinding (p/P used for pushing/pulling which
// I'd like to be global. Perhaps all global keybindings should use a modifier
// like command? But then there's gonna be hotkey conflicts with the terminal
if err := g.SetKeybinding("stash", 'k', gocui.ModNone, handleStashPop); err != nil {
return err
}
// Binding - a keybinding mapping a key and modifier to a handler. The keypress
// is only handled if the given view has focus, or handled globally if the view
// is ""
type Binding struct {
ViewName string
Handler func(*gocui.Gui, *gocui.View) error
Key interface{} // FIXME: find out how to get `gocui.Key | rune`
Modifier gocui.Modifier
}
return g.SetKeybinding("stash", 'd', gocui.ModNone, handleStashDrop)
func keybindings(g *gocui.Gui) error {
bindings := []Binding{
Binding{ViewName: "", Key: gocui.KeyArrowLeft, Modifier: gocui.ModNone, Handler: previousView},
Binding{ViewName: "", Key: gocui.KeyArrowRight, Modifier: gocui.ModNone, Handler: nextView},
Binding{ViewName: "", Key: gocui.KeyTab, Modifier: gocui.ModNone, Handler: nextView},
Binding{ViewName: "", Key: 'q', Modifier: gocui.ModNone, Handler: quit},
Binding{ViewName: "", Key: gocui.KeyCtrlC, Modifier: gocui.ModNone, Handler: quit},
Binding{ViewName: "", Key: gocui.KeyArrowDown, Modifier: gocui.ModNone, Handler: cursorDown},
Binding{ViewName: "", Key: gocui.KeyArrowUp, Modifier: gocui.ModNone, Handler: cursorUp},
Binding{ViewName: "", Key: gocui.KeyPgup, Modifier: gocui.ModNone, Handler: scrollUpMain},
Binding{ViewName: "", Key: gocui.KeyPgdn, Modifier: gocui.ModNone, Handler: scrollDownMain},
Binding{ViewName: "", Key: 'P', Modifier: gocui.ModNone, Handler: pushFiles},
Binding{ViewName: "", Key: 'p', Modifier: gocui.ModNone, Handler: pullFiles},
Binding{ViewName: "", Key: 'R', Modifier: gocui.ModNone, Handler: handleRefresh},
Binding{ViewName: "files", Key: 'c', Modifier: gocui.ModNone, Handler: handleCommitPress},
Binding{ViewName: "files", Key: gocui.KeySpace, Modifier: gocui.ModNone, Handler: handleFilePress},
Binding{ViewName: "files", Key: 'd', Modifier: gocui.ModNone, Handler: handleFileRemove},
Binding{ViewName: "files", Key: 'm', Modifier: gocui.ModNone, Handler: handleSwitchToMerge},
Binding{ViewName: "files", Key: 'o', Modifier: gocui.ModNone, Handler: handleFileOpen},
Binding{ViewName: "files", Key: 's', Modifier: gocui.ModNone, Handler: handleSublimeFileOpen},
Binding{ViewName: "files", Key: 'v', Modifier: gocui.ModNone, Handler: handleVsCodeFileOpen},
Binding{ViewName: "files", Key: 'i', Modifier: gocui.ModNone, Handler: handleIgnoreFile},
Binding{ViewName: "files", Key: 'r', Modifier: gocui.ModNone, Handler: handleRefreshFiles},
Binding{ViewName: "files", Key: 'S', Modifier: gocui.ModNone, Handler: handleStashSave},
Binding{ViewName: "files", Key: 'a', Modifier: gocui.ModNone, Handler: handleAbortMerge},
Binding{ViewName: "main", Key: gocui.KeyArrowUp, Modifier: gocui.ModNone, Handler: handleSelectTop},
Binding{ViewName: "main", Key: gocui.KeyArrowDown, Modifier: gocui.ModNone, Handler: handleSelectBottom},
Binding{ViewName: "main", Key: gocui.KeyEsc, Modifier: gocui.ModNone, Handler: handleEscapeMerge},
Binding{ViewName: "main", Key: gocui.KeySpace, Modifier: gocui.ModNone, Handler: handlePickHunk},
Binding{ViewName: "main", Key: 'b', Modifier: gocui.ModNone, Handler: handlePickBothHunks},
Binding{ViewName: "main", Key: gocui.KeyArrowLeft, Modifier: gocui.ModNone, Handler: handleSelectPrevConflict},
Binding{ViewName: "main", Key: gocui.KeyArrowRight, Modifier: gocui.ModNone, Handler: handleSelectNextConflict},
Binding{ViewName: "main", Key: 'z', Modifier: gocui.ModNone, Handler: handlePopFileSnapshot},
Binding{ViewName: "branches", Key: gocui.KeySpace, Modifier: gocui.ModNone, Handler: handleBranchPress},
Binding{ViewName: "branches", Key: 'c', Modifier: gocui.ModNone, Handler: handleCheckoutByName},
Binding{ViewName: "branches", Key: 'F', Modifier: gocui.ModNone, Handler: handleForceCheckout},
Binding{ViewName: "branches", Key: 'n', Modifier: gocui.ModNone, Handler: handleNewBranch},
Binding{ViewName: "branches", Key: 'm', Modifier: gocui.ModNone, Handler: handleMerge},
Binding{ViewName: "commits", Key: 's', Modifier: gocui.ModNone, Handler: handleCommitSquashDown},
Binding{ViewName: "commits", Key: 'r', Modifier: gocui.ModNone, Handler: handleRenameCommit},
Binding{ViewName: "commits", Key: 'g', Modifier: gocui.ModNone, Handler: handleResetToCommit},
Binding{ViewName: "stash", Key: gocui.KeySpace, Modifier: gocui.ModNone, Handler: handleStashApply},
Binding{ViewName: "stash", Key: 'k', Modifier: gocui.ModNone, Handler: handleStashPop},
Binding{ViewName: "stash", Key: 'd', Modifier: gocui.ModNone, Handler: handleStashDrop},
}
for _, binding := range bindings {
if err := g.SetKeybinding(binding.ViewName, binding.Key, binding.Modifier, binding.Handler); err != nil {
return err
}
}
return nil
}
func layout(g *gocui.Gui) error {

View File

@ -18,7 +18,7 @@ func findConflicts(content string) ([]conflict, error) {
conflicts := make([]conflict, 0)
var newConflict conflict
for i, line := range splitLines(content) {
if line == "<<<<<<< HEAD" {
if line == "<<<<<<< HEAD" || line == "<<<<<<< MERGE_HEAD" || line == "<<<<<<< Updated upstream" {
newConflict = conflict{start: i}
} else if line == "=======" {
newConflict.middle = i