summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Duffield <jessedduffield@gmail.com>2018-08-11 15:43:56 +1000
committerJesse Duffield <jessedduffield@gmail.com>2018-08-11 15:43:56 +1000
commit2c140445e5a014e60e12467ee70a80eaea041ff4 (patch)
tree6a820eb68e454d6f7a2bf29ee014e3b6cfa6dd86
parentbfa47d3b91d930697d3c6318bfc29f171475b543 (diff)
parent62a231abb78db5f807ff55ad77f39bcf633539b2 (diff)
Merge branch 'master' into feature/deleting-branches
-rw-r--r--.gitignore1
-rw-r--r--Gopkg.lock9
-rw-r--r--README.md19
-rw-r--r--branches_panel.go4
-rw-r--r--commit_message_panel.go45
-rw-r--r--commits_panel.go42
-rw-r--r--confirmation_panel.go35
-rw-r--r--files_panel.go27
-rw-r--r--gitcommands.go41
-rw-r--r--gui.go19
-rw-r--r--keybindings.go4
-rw-r--r--main.go10
-rw-r--r--stash_panel.go2
-rw-r--r--status_panel.go3
-rwxr-xr-xtest/lots_of_commits.sh30
-rwxr-xr-xtest/merge_conflict.sh (renamed from test/generate_basic_repo.sh)2
-rwxr-xr-xtest/unicode_characters.sh35
-rw-r--r--utils.go12
-rw-r--r--vendor/github.com/davecgh/go-spew/LICENSE15
-rw-r--r--vendor/github.com/davecgh/go-spew/spew/bypass.go152
-rw-r--r--vendor/github.com/davecgh/go-spew/spew/bypasssafe.go38
-rw-r--r--vendor/github.com/davecgh/go-spew/spew/common.go341
-rw-r--r--vendor/github.com/davecgh/go-spew/spew/config.go306
-rw-r--r--vendor/github.com/davecgh/go-spew/spew/doc.go211
-rw-r--r--vendor/github.com/davecgh/go-spew/spew/dump.go509
-rw-r--r--vendor/github.com/davecgh/go-spew/spew/format.go419
-rw-r--r--vendor/github.com/davecgh/go-spew/spew/spew.go148
-rw-r--r--view_helpers.go20
28 files changed, 2440 insertions, 59 deletions
diff --git a/.gitignore b/.gitignore
index 5210291e4..4a37f6ec0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,4 @@ extra/lgit.rb
notes/go.notes
TODO.notes
TODO.md
+test/testrepo/
diff --git a/Gopkg.lock b/Gopkg.lock
index f0eeb5c47..4323983be 100644
--- a/Gopkg.lock
+++ b/Gopkg.lock
@@ -2,6 +2,14 @@
[[projects]]
+ digest = "1:a2c1d0e43bd3baaa071d1b9ed72c27d78169b2b269f71c105ac4ba34b1be4a39"
+ name = "github.com/davecgh/go-spew"
+ packages = ["spew"]
+ pruneopts = "NUT"
+ revision = "346938d642f2ec3594ed81d874461961cd0faa76"
+ version = "v1.1.0"
+
+[[projects]]
digest = "1:de4a74b504df31145ffa8ca0c4edbffa2f3eb7f466753962184611b618fa5981"
name = "github.com/emirpasic/gods"
packages = [
@@ -274,6 +282,7 @@
analyzer-name = "dep"
analyzer-version = 1
input-imports = [
+ "github.com/davecgh/go-spew/spew",
"github.com/fatih/color",
"github.com/golang-collections/collections/stack",
"github.com/jesseduffield/gocui",
diff --git a/README.md b/README.md
index 0bd9b310f..3e5e1bae0 100644
--- a/README.md
+++ b/README.md
@@ -8,6 +8,8 @@ Are YOU tired of typing every git command directly into the terminal, but you're
![Gif](https://image.ibb.co/mmeXho/optimisedgif.gif)
+[Twitch Stream](https://www.twitch.tv/jesseduffield)
+
## Installation
### Homebrew
@@ -27,6 +29,20 @@ sudo apt-get update
sudo apt-get install lazygit
```
+### Arch Linux
+Packages for Arch Linux are available via AUR (Arch User Repository).
+
+There are two packages. The stable one which is built with the latest release and the git version which builds from the most recent commit.
+
+Stable:
+https://aur.archlinux.org/packages/lazygit/
+
+Development:
+https://aur.archlinux.org/packages/lazygit-git/
+
+Instruction of how to install AUR content can be found here:
+https://wiki.archlinux.org/index.php/Arch_User_Repository
+
### Binary Release (Windows/Linux/OSX)
You can download a binary release [here](https://github.com/jesseduffield/lazygit/releases)
@@ -74,3 +90,6 @@ We love your input! Please check out the [contributing guide](CONTRIBUTING.md).
## 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).
+
+## Social
+If you want to see what I (Jesse) am up to in terms of development, follow me on [twitter](https://twitter.com/DuffieldJesse) or watch me program on [twitch](https://www.twitch.tv/jesseduffield)
diff --git a/branches_panel.go b/branches_panel.go
index 0327b576e..64dba4f55 100644
--- a/branches_panel.go
+++ b/branches_panel.go
@@ -30,7 +30,7 @@ func handleForceCheckout(g *gocui.Gui, v *gocui.View) error {
}
func handleCheckoutByName(g *gocui.Gui, v *gocui.View) error {
- createPromptPanel(g, v, "Branch Name:", nil, func(g *gocui.Gui, v *gocui.View) error {
+ createPromptPanel(g, v, "Branch Name:", func(g *gocui.Gui, v *gocui.View) error {
if output, err := gitCheckout(trimmedContent(v), false); err != nil {
return createErrorPanel(g, output)
}
@@ -41,7 +41,7 @@ func handleCheckoutByName(g *gocui.Gui, v *gocui.View) error {
func handleNewBranch(g *gocui.Gui, v *gocui.View) error {
branch := state.Branches[0]
- createPromptPanel(g, v, "New Branch Name (Branch is off of "+branch.Name+")", nil, func(g *gocui.Gui, v *gocui.View) error {
+ createPromptPanel(g, v, "New Branch Name (Branch is off of "+branch.Name+")", func(g *gocui.Gui, v *gocui.View) error {
if output, err := gitNewBranch(trimmedContent(v)); err != nil {
return createErrorPanel(g, output)
}
diff --git a/commit_message_panel.go b/commit_message_panel.go
new file mode 100644
index 000000000..baef870cf
--- /dev/null
+++ b/commit_message_panel.go
@@ -0,0 +1,45 @@
+package main
+
+import "github.com/jesseduffield/gocui"
+
+func handleCommitConfirm(g *gocui.Gui, v *gocui.View) error {
+ message := trimmedContent(v)
+ if message == "" {
+ return createErrorPanel(g, "You cannot commit without a commit message")
+ }
+ if output, err := gitCommit(g, message); err != nil {
+ if err == errNoUsername {
+ return createErrorPanel(g, err.Error())
+ }
+ return createErrorPanel(g, output)
+ }
+ refreshFiles(g)
+ v.Clear()
+ v.SetCursor(0, 0)
+ g.SetViewOnBottom("commitMessage")
+ switchFocus(g, v, getFilesView(g))
+ return refreshCommits(g)
+}
+
+func handleCommitClose(g *gocui.Gui, v *gocui.View) error {
+ g.SetViewOnBottom("commitMessage")
+ return switchFocus(g, v, getFilesView(g))
+}
+
+func handleNewlineCommitMessage(g *gocui.Gui, v *gocui.View) error {
+ // resising ahead of time so that the top line doesn't get hidden to make
+ // room for the cursor on the second line
+ x0, y0, x1, y1 := getConfirmationPanelDimensions(g, v.Buffer())
+ if _, err := g.SetView("commitMessage", x0, y0, x1, y1+1, 0); err != nil {
+ if err != gocui.ErrUnknownView {
+ return err
+ }
+ }
+
+ v.EditNewLine()
+ return nil
+}
+
+func handleCommitFocused(g *gocui.Gui, v *gocui.View) error {
+ return renderString(g, "options", "esc: close, enter: confirm")
+}
diff --git a/commits_panel.go b/commits_panel.go
index e8ff68678..1a9976ffe 100644
--- a/commits_panel.go
+++ b/commits_panel.go
@@ -34,6 +34,9 @@ func refreshCommits(g *gocui.Gui) error {
white.Fprintln(v, commit.Name)
}
refreshStatus(g)
+ if g.CurrentView().Name() == "commits" {
+ handleCommitSelect(g, v)
+ }
return nil
})
return nil
@@ -42,7 +45,6 @@ func refreshCommits(g *gocui.Gui) error {
func handleResetToCommit(g *gocui.Gui, commitView *gocui.View) error {
return createConfirmationPanel(g, commitView, "Reset To Commit", "Are you sure you want to reset to this commit?", func(g *gocui.Gui, v *gocui.View) error {
commit, err := getSelectedCommit(g)
- devLog(commit)
if err != nil {
panic(err)
}
@@ -65,6 +67,7 @@ func renderCommitsOptions(g *gocui.Gui) error {
"s": "squash down",
"r": "rename",
"g": "reset to this commit",
+ "f": "fixup commit",
"← → ↑ ↓": "navigate",
})
}
@@ -105,11 +108,46 @@ func handleCommitSquashDown(g *gocui.Gui, v *gocui.View) error {
return handleCommitSelect(g, v)
}
+// TODO: move to files panel
+func anyUnStagedChanges(files []GitFile) bool {
+ for _, file := range files {
+ if file.Tracked && file.HasUnstagedChanges {
+ return true
+ }
+ }
+ return false
+}
+
+func handleCommitFixup(g *gocui.Gui, v *gocui.View) error {
+ if len(state.Commits) == 1 {
+ return createErrorPanel(g, "You have no commits to squash with")
+ }
+ objectLog(state.GitFiles)
+ if anyUnStagedChanges(state.GitFiles) {
+ return createErrorPanel(g, "Can't fixup while there are unstaged changes")
+ }
+ branch := state.Branches[0]
+ commit, err := getSelectedCommit(g)
+ if err != nil {
+ return err
+ }
+ createConfirmationPanel(g, v, "Fixup", "Are you sure you want to fixup this commit? The commit beneath will be squashed up into this one", func(g *gocui.Gui, v *gocui.View) error {
+ if output, err := gitSquashFixupCommit(branch.Name, commit.Sha); err != nil {
+ return createErrorPanel(g, output)
+ }
+ if err := refreshCommits(g); err != nil {
+ panic(err)
+ }
+ return refreshStatus(g)
+ }, nil)
+ return nil
+}
+
func handleRenameCommit(g *gocui.Gui, v *gocui.View) error {
if getItemPosition(v) != 0 {
return createErrorPanel(g, "Can only rename topmost commit")
}
- createPromptPanel(g, v, "Rename Commit", nil, func(g *gocui.Gui, v *gocui.View) error {
+ createPromptPanel(g, v, "Rename Commit", func(g *gocui.Gui, v *gocui.View) error {
if output, err := gitRenameCommit(v.Buffer()); err != nil {
return createErrorPanel(g, output)
}
diff --git a/confirmation_panel.go b/confirmation_panel.go
index 7ae55eaf9..563db72d8 100644
--- a/confirmation_panel.go
+++ b/confirmation_panel.go
@@ -55,7 +55,8 @@ func getConfirmationPanelDimensions(g *gocui.Gui, prompt string) (int, int, int,
height/2 + panelHeight/2
}
-func createPromptPanel(g *gocui.Gui, currentView *gocui.View, title string, initialValue *[]byte, handleConfirm func(*gocui.Gui, *gocui.View) error) error {
+func createPromptPanel(g *gocui.Gui, currentView *gocui.View, title string, handleConfirm func(*gocui.Gui, *gocui.View) error) error {
+ g.SetViewOnBottom("commitMessage")
// only need to fit one line
x0, y0, x1, y1 := getConfirmationPanelDimensions(g, "")
if confirmationView, err := g.SetView("confirmation", x0, y0, x1, y1, 0); err != nil {
@@ -63,31 +64,16 @@ func createPromptPanel(g *gocui.Gui, currentView *gocui.View, title string, init
return err
}
- g.Cursor = true
-
- handleConfirmAndClear := func(gui *gocui.Gui, view *gocui.View) error {
- *initialValue = nil
- return handleConfirm(g, view)
- }
-
- handleClose := func(gui *gocui.Gui, view *gocui.View) error {
- // FIXME: trimming a newline that is no doubt caused by the enter keybinding
- // on the editor. We should just define a new editor that doesn't do that
- *initialValue = []byte(strings.TrimSpace(view.Buffer()))
- return nil
- }
-
confirmationView.Editable = true
confirmationView.Title = title
- confirmationView.Write(*initialValue)
- confirmationView.SetCursor(len(*initialValue), 0)
switchFocus(g, currentView, confirmationView)
- return setKeyBindings(g, handleConfirmAndClear, handleClose)
+ return setKeyBindings(g, handleConfirm, nil)
}
return nil
}
func createConfirmationPanel(g *gocui.Gui, currentView *gocui.View, title, prompt string, handleConfirm, handleClose func(*gocui.Gui, *gocui.View) error) error {
+ g.SetViewOnBottom("commitMessage")
g.Update(func(g *gocui.Gui) error {
// delete the existing confirmation panel if it exists
if view, _ := g.View("confirmation"); view != nil {
@@ -154,15 +140,20 @@ func trimTrailingNewline(str string) string {
return str
}
-func resizeConfirmationPanel(g *gocui.Gui) error {
+func resizeConfirmationPanel(g *gocui.Gui, viewName string) error {
// If the confirmation panel is already displayed, just resize the width,
// otherwise continue
- if v, err := g.View("confirmation"); err == nil {
+ g.Update(func(g *gocui.Gui) error {
+ v, err := g.View(viewName)
+ if err != nil {
+ return nil
+ }
content := trimTrailingNewline(v.Buffer())
x0, y0, x1, y1 := getConfirmationPanelDimensions(g, content)
- if _, err = g.SetView("confirmation", x0, y0, x1, y1, 0); err != nil {
+ if _, err := g.SetView(viewName, x0, y0, x1, y1, 0); err != nil {
return err
}
- }
+ return nil
+ })
return nil
}
diff --git a/files_panel.go b/files_panel.go
index b2de2c226..2d7406c45 100644
--- a/files_panel.go
+++ b/files_panel.go
@@ -15,9 +15,8 @@ import (
)
var (
- savedCommitMessage = &[]byte{}
- errNoFiles = errors.New("No changed files")
- errNoUsername = errors.New(`No username set. Please do: git config --global user.name "Your Name"`)
+ errNoFiles = errors.New("No changed files")
+ errNoUsername = errors.New(`No username set. Please do: git config --global user.name "Your Name"`)
)
func stagedFiles(files []GitFile) []GitFile {
@@ -178,19 +177,11 @@ func handleCommitPress(g *gocui.Gui, filesView *gocui.View) error {
if len(stagedFiles(state.GitFiles)) == 0 && !state.HasMergeConflicts {
return createErrorPanel(g, "There are no staged files to commit")
}
- createPromptPanel(g, filesView, "Commit message", savedCommitMessage, func(g *gocui.Gui, v *gocui.View) error {
- message := trimmedContent(v)
- if message == "" {
- return createErrorPanel(g, "You cannot commit without a commit message")
- }
- if output, err := gitCommit(g, message); err != nil {
- if err == errNoUsername {
- return createErrorPanel(g, err.Error())
- }
- return createErrorPanel(g, output)
- }
- refreshFiles(g)
- return refreshCommits(g)
+ commitMessageView := getCommitMessageView(g)
+ g.Update(func(g *gocui.Gui) error {
+ g.SetViewOnTop("commitMessage")
+ switchFocus(g, filesView, commitMessageView)
+ return nil
})
return nil
}
@@ -296,7 +287,6 @@ func refreshFiles(g *gocui.Gui) error {
}
func pullFiles(g *gocui.Gui, v *gocui.View) error {
- devLog("pulling...")
createMessagePanel(g, v, "", "Pulling...")
go func() {
if output, err := gitPull(); err != nil {
@@ -305,7 +295,6 @@ func pullFiles(g *gocui.Gui, v *gocui.View) error {
closeConfirmationPrompt(g)
refreshCommits(g)
refreshStatus(g)
- devLog("pulled.")
}
refreshFiles(g)
}()
@@ -313,7 +302,6 @@ func pullFiles(g *gocui.Gui, v *gocui.View) error {
}
func pushFiles(g *gocui.Gui, v *gocui.View) error {
- devLog("pushing...")
createMessagePanel(g, v, "", "Pushing...")
go func() {
if output, err := gitPush(); err != nil {
@@ -322,7 +310,6 @@ func pushFiles(g *gocui.Gui, v *gocui.View) error {
closeConfirmationPrompt(g)
refreshCommits(g)
refreshStatus(g)
- devLog("pushed.")
}
}()
return nil
diff --git a/gitcommands.go b/gitcommands.go
index 2c68f561e..d28263a60 100644
--- a/gitcommands.go
+++ b/gitcommands.go
@@ -106,14 +106,18 @@ func mergeGitStatusFiles(oldGitFiles, newGitFiles []GitFile) []GitFile {
return result
}
+// only to be used when you're already in an error state
+func runDirectCommandIgnoringError(command string) string {
+ output, _ := runDirectCommand(command)
+ return output
+}
+
func runDirectCommand(command string) (string, error) {
- timeStart := time.Now()
commandLog(command)
cmdOut, err := exec.
Command(state.Platform.shell, state.Platform.shellArg, command).
CombinedOutput()
- devLog("run direct command time for command: ", command, time.Now().Sub(timeStart))
return sanitisedCommandOutput(cmdOut, err)
}
@@ -180,7 +184,7 @@ func getGitStatusFiles() []GitFile {
}
gitFiles = append(gitFiles, gitFile)
}
- devLog(gitFiles)
+ objectLog(gitFiles)
return gitFiles
}
@@ -218,12 +222,9 @@ func sanitisedCommandOutput(output []byte, err error) (string, error) {
}
func runCommand(command string) (string, error) {
- commandStartTime := time.Now()
commandLog(command)
splitCmd := strings.Split(command, " ")
- devLog(splitCmd)
cmdOut, err := exec.Command(splitCmd[0], splitCmd[1:]...).CombinedOutput()
- devLog("run command time: ", time.Now().Sub(commandStartTime))
return sanitisedCommandOutput(cmdOut, err)
}
@@ -379,7 +380,6 @@ func unStageFile(file string, tracked bool) error {
} else {
command = "git rm --cached "
}
- devLog(command)
_, err := runCommand(command + file)
return err
}
@@ -443,6 +443,33 @@ func gitSquashPreviousTwoCommits(message string) (string, error) {
return runDirectCommand("git reset --soft HEAD^ && git commit --amend -m \"" + message + "\"")
}
+func gitSquashFixupCommit(branchName string, shaValue string) (string, error) {
+ var err error
+ commands := []string{
+ "git checkout -q " + shaValue,
+ "git reset --soft " + shaValue + "^",
+ "git commit --amend -C " + shaValue + "^",
+ "git rebase --onto HEAD " + shaValue + " " + branchName,
+ }
+ ret := ""
+ for _, command := range commands {
+ devLog(command)
+ output, err := runDirectCommand(command)
+ ret += output
+ if err != nil {
+ devLog(ret)
+ break
+ }
+ }
+ if err != nil {
+ // We are already in an error state here so we're just going to append
+ // the output of these commands
+ ret += runDirectCommandIgnoringError("git branch -d " + shaValue)
+ ret += runDirectCommandIgnoringError("git checkout " + branchName)
+ }
+ return ret, err
+}
+
func gitRenameCommit(message string) (string, error) {
return runDirectCommand("git commit --allow-empty --amend -m \"" + message + "\"")
}
diff --git a/gui.go b/gui.go
index 4cf7d7df5..033dbe06a 100644
--- a/gui.go
+++ b/gui.go
@@ -199,12 +199,27 @@ func layout(g *gocui.Gui) error {
if err != gocui.ErrUnknownView {
return err
}
- v.BgColor = gocui.ColorDefault
v.FgColor = gocui.ColorBlue
v.Frame = false
}
- if err = resizeConfirmationPanel(g); err != nil {
+ if getCommitMessageView(g) == nil {
+ // doesn't matter where this view starts because it will be hidden
+ if commitMessageView, err := g.SetView("commitMessage", 0, 0, width, height, 0); err != nil {
+ if err != gocui.ErrUnknownView {
+ return err
+ }
+ g.SetViewOnBottom("commitMessage")
+ commitMessageView.Title = "Commit message"
+ commitMessageView.FgColor = gocui.ColorWhite
+ commitMessageView.Editable = true
+ }
+ }
+
+ if err = resizeConfirmationPanel(g, "commitMessage"); err != nil {
+ return err
+ }
+ if err = resizeConfirmationPanel(g, "confirmation"); err != nil {
return err
}
diff --git a/keybindings.go b/keybindings.go
index d68c6c5a6..2eab3aab5 100644
--- a/keybindings.go
+++ b/keybindings.go
@@ -56,9 +56,13 @@ func keybindings(g *gocui.Gui) error {
{ViewName: "commits", Key: 's', Modifier: gocui.ModNone, Handler: handleCommitSquashDown},
{ViewName: "commits", Key: 'r', Modifier: gocui.ModNone, Handler: handleRenameCommit},
{ViewName: "commits", Key: 'g', Modifier: gocui.ModNone, Handler: handleResetToCommit},
+ {ViewName: "commits", Key: 'f', Modifier: gocui.ModNone, Handler: handleCommitFixup},
{ViewName: "stash", Key: gocui.KeySpace, Modifier: gocui.ModNone, Handler: handleStashApply},
{ViewName: "stash", Key: 'g', Modifier: gocui.ModNone, Handler: handleStashPop},
{ViewName: "stash", Key: 'd', Modifier: gocui.ModNone, Handler: handleStashDrop},
+ {ViewName: "commitMessage", Key: gocui.KeyEnter, Modifier: gocui.ModNone, Handler: handleCommitConfirm},
+ {ViewName: "commitMessage", Key: gocui.KeyEsc, Modifier: gocui.ModNone, Handler: handleCommitClose},
+ {ViewName: "commitMessage", Key: gocui.KeyTab, Modifier: gocui.ModNone, Handler: handleNewlineCommitMessage},
}
// Would make these keybindings global but that interferes with editing
diff --git a/main.go b/main.go
index c36ca468b..aec4051bf 100644
--- a/main.go
+++ b/main.go
@@ -11,6 +11,8 @@ import (
"os/user"
"path/filepath"
+ "github.com/davecgh/go-spew/spew"
+
"github.com/jesseduffield/gocui"
git "gopkg.in/src-d/go-git.v4"
)
@@ -48,6 +50,14 @@ func devLog(objects ...interface{}) {
localLog("development.log", objects...)
}
+func objectLog(object interface{}) {
+ if !*debuggingFlag {
+ return
+ }
+ str := spew.Sdump(object)
+ localLog("development.log", str)
+}
+
func commandLog(objects ...interface{}) {
localLog("commands.log", objects...)
}
diff --git a/stash_panel.go b/stash_panel.go
index a4a2207d8..33c7e297b 100644
--- a/stash_panel.go
+++ b/stash_panel.go
@@ -82,7 +82,7 @@ func stashDo(g *gocui.Gui, v *gocui.View, method string) error {
}
func handleStashSave(g *gocui.Gui, filesView *gocui.View) error {
- createPromptPanel(g, filesView, "Stash changes", nil, func(g *gocui.Gui, v *gocui.View) error {
+ createPromptPanel(g, filesView, "Stash changes", func(g *gocui.Gui, v *gocui.View) error {
if output, err := gitStashSave(trimmedContent(v)); err != nil {
createErrorPanel(g, output)
}
diff --git a/status_panel.go b/status_panel.go
index 46bc394ae..f3fcb8078 100644
--- a/status_panel.go
+++ b/status_panel.go
@@ -32,7 +32,8 @@ func refreshStatus(g *gocui.Gui) error {
}
branch := branches[0]
name := coloredString(branch.Name, branch.getColor())
- fmt.Fprint(v, " "+name)
+ repo := getCurrentProject()
+ fmt.Fprint(v, " "+repo+" → "+name)
return nil
})
diff --git a/test/lots_of_commits.sh b/test/lots_of_commits.sh
new file mode 100755
index 000000000..6a92d32e2
--- /dev/null
+++ b/test/lots_of_commits.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+
+# this script makes a repo with lots of commits
+
+# call this command from the test directory:
+# ./lots_of_commits.sh; cd testrepo; gg; cd ..
+
+# -e means exit if something fails
+# -x means print out simple commands before running them
+set -ex
+
+reponame="testrepo"
+
+rm -rf ${reponame}
+mkdir ${reponame}
+cd ${reponame}
+
+git init
+
+i=2
+end=100
+while [ $i -le $end ]; do
+ echo "file${i}" > file${i}
+ git add file${i}
+ git commit -m file${i}
+
+ i=$(($i+1))
+done
+
+echo "unstaged change" > file100
diff --git a/test/generate_basic_repo.sh b/test/merge_conflict.sh
index ef04333c7..9bd12bf56 100755
--- a/test/generate_basic_repo.sh
+++ b/test/merge_conflict.sh
@@ -4,7 +4,7 @@
# on the master branch and if we try and merge master we get a merge conflict
# call this command from the test directory:
-# ./generate_basic_repo.sh; cd testrepo; gg; cd ..
+# ./merge_conflict.sh; cd testrepo; gg; cd ..
# -e means exit if something fails
# -x means print out simple commands before running them
diff --git a/test/unicode_characters.sh b/test/unicode_characters.sh
new file mode 100755
index 000000000..92640d52f
--- /dev/null
+++ b/test/unicode_characters.sh
@@ -0,0 +1,35 @@
+#!/bin/bash
+
+# this script will make a repo with a master and develop branch, where we end up
+# on the master branch and if we try and merge master we get a merge conflict
+
+# call this command from the test directory:
+# ./generate_basic_repo.sh; cd testrepo; gg; cd ..
+
+# -e means exit if something fails
+# -x means print out simple commands before running them
+set -ex
+
+reponame="testrepo"
+
+rm -rf ${reponame}
+mkdir ${reponame}
+cd ${reponame}
+
+git init
+
+# Add some ansi, unicode, zero width joiner caracters
+cat <<EOT >> charstest.txt
+ANSI Π(U+0152 &OElig; Latin capital ligature OE Latin Extended-A)
+ ¥ (0xA5 U+00A5 &yen; yes sign)
+ ƒ (0x83 U+0192 &fnof; Latin small letter f with hook)
+ZWJ https://en.wikipedia.org/wiki/Zero-width_joiner / https://unicode.org/Public/emoji/4.0/emoji-zwj-sequences.txt 👶(👨‍👦)
+UNICODE ☆ 🤓 え 术
+EOT
+git add charstest.txt
+git commit -m "Test chars Œ¥ƒ👶👨‍👦☆ 🤓 え 术 commit"
+echo "我喜歡編碼" >> charstest.txt
+echo "நான் குறியீடு விரும்புகிறேன்" >> charstest.txt
+git add charstest.txt
+git commit -m "Test chars 我喜歡編碼 நான் குறியீடு விரும்புகிறேன் commit"
+
diff --git a/utils.go b/utils.go
index 1ab5a9ed2..e2de46233 100644
--- a/utils.go
+++ b/utils.go
@@ -2,6 +2,9 @@ package main
import (
"fmt"
+ "log"
+ "os"
+ "path/filepath"
"strings"
"github.com/fatih/color"
@@ -40,3 +43,12 @@ func coloredString(str string, colorAttribute color.Attribute) string {
func coloredStringDirect(str string, colour *color.Color) string {
return colour.SprintFunc()(fmt.Sprint(str))
}
+
+// used to get the project name
+func getCurrentProject() string {
+ pwd, err := os.Getwd()
+ if err != nil {
+ log.Fatalln(err.Error())
+ }
+ return filepath.Base(pwd)
+}
diff --git a/vendor/github.com/davecgh/go-spew/LICENSE b/vendor/github.com/davecgh/go-spew/LICENSE
new file mode 100644
index 000000000..c83641619
--- /dev/null
+++ b/vendor/github.com/davecgh/go-spew/LICENSE
@@ -0,0 +1,15 @@
+ISC License
+
+Copyright (c) 2012-2016 Dave Collins <dave@davec.name>
+
+Permission to use, copy, modify, and distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/vendor/github.com/davecgh/go-spew/spew/bypass.go b/vendor/github.com/davecgh/go-spew/spew/bypass.go
new file mode 100644
index 000000000..8a4a6589a
--- /dev/null
+++ b/vendor/gi