mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-04-21 12:16:54 +02:00
merge with develop
This commit is contained in:
commit
a47c889cbb
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,3 +3,4 @@ commands.log
|
|||||||
extra/lgit.rb
|
extra/lgit.rb
|
||||||
notes/go.notes
|
notes/go.notes
|
||||||
TODO.notes
|
TODO.notes
|
||||||
|
TODO.md
|
||||||
|
@ -7,6 +7,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func handleBranchPress(g *gocui.Gui, v *gocui.View) error {
|
func handleBranchPress(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
index := getItemPosition(v)
|
||||||
|
if index == 0 {
|
||||||
|
return createErrorPanel(g, "You have already checked out this branch")
|
||||||
|
}
|
||||||
branch := getSelectedBranch(v)
|
branch := getSelectedBranch(v)
|
||||||
if output, err := gitCheckout(branch.Name, false); err != nil {
|
if output, err := gitCheckout(branch.Name, false); err != nil {
|
||||||
createErrorPanel(g, output)
|
createErrorPanel(g, output)
|
||||||
@ -70,7 +74,7 @@ func renderBranchesOptions(g *gocui.Gui) error {
|
|||||||
"f": "force checkout",
|
"f": "force checkout",
|
||||||
"m": "merge",
|
"m": "merge",
|
||||||
"c": "checkout by name",
|
"c": "checkout by name",
|
||||||
"n": "checkout new branch",
|
"n": "new branch",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ func handleFilePress(g *gocui.Gui, v *gocui.View) error {
|
|||||||
if file.HasUnstagedChanges {
|
if file.HasUnstagedChanges {
|
||||||
stageFile(file.Name)
|
stageFile(file.Name)
|
||||||
} else {
|
} else {
|
||||||
unStageFile(file.Name)
|
unStageFile(file.Name, file.Tracked)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := refreshFiles(g); err != nil {
|
if err := refreshFiles(g); err != nil {
|
||||||
@ -101,6 +101,7 @@ func renderfilesOptions(g *gocui.Gui, gitFile *GitFile) error {
|
|||||||
"c": "commit changes",
|
"c": "commit changes",
|
||||||
"o": "open",
|
"o": "open",
|
||||||
"s": "open in sublime",
|
"s": "open in sublime",
|
||||||
|
"v": "open in vscode",
|
||||||
"i": "ignore",
|
"i": "ignore",
|
||||||
"d": "delete",
|
"d": "delete",
|
||||||
"space": "toggle staged",
|
"space": "toggle staged",
|
||||||
@ -125,7 +126,6 @@ func handleFileSelect(g *gocui.Gui, v *gocui.View) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
renderString(g, "main", "No changed files")
|
renderString(g, "main", "No changed files")
|
||||||
colorLog(color.FgRed, "error")
|
|
||||||
return renderfilesOptions(g, nil)
|
return renderfilesOptions(g, nil)
|
||||||
}
|
}
|
||||||
renderfilesOptions(g, &gitFile)
|
renderfilesOptions(g, &gitFile)
|
||||||
@ -171,6 +171,9 @@ func handleFileOpen(g *gocui.Gui, v *gocui.View) error {
|
|||||||
func handleSublimeFileOpen(g *gocui.Gui, v *gocui.View) error {
|
func handleSublimeFileOpen(g *gocui.Gui, v *gocui.View) error {
|
||||||
return genericFileOpen(g, v, sublimeOpenFile)
|
return genericFileOpen(g, v, sublimeOpenFile)
|
||||||
}
|
}
|
||||||
|
func handleVsCodeFileOpen(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
return genericFileOpen(g, v, vsCodeOpenFile)
|
||||||
|
}
|
||||||
|
|
||||||
func handleRefreshFiles(g *gocui.Gui, v *gocui.View) error {
|
func handleRefreshFiles(g *gocui.Gui, v *gocui.View) error {
|
||||||
return refreshFiles(g)
|
return refreshFiles(g)
|
||||||
@ -197,7 +200,7 @@ func renderGitFile(gitFile GitFile, filesView *gocui.View) {
|
|||||||
// objects with each render
|
// objects with each render
|
||||||
red := color.New(color.FgRed)
|
red := color.New(color.FgRed)
|
||||||
green := color.New(color.FgGreen)
|
green := color.New(color.FgGreen)
|
||||||
if !gitFile.Tracked {
|
if !gitFile.Tracked && !gitFile.HasStagedChanges {
|
||||||
red.Fprintln(filesView, gitFile.DisplayString)
|
red.Fprintln(filesView, gitFile.DisplayString)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -251,10 +254,10 @@ func pullFiles(g *gocui.Gui, v *gocui.View) error {
|
|||||||
} else {
|
} else {
|
||||||
closeConfirmationPrompt(g)
|
closeConfirmationPrompt(g)
|
||||||
refreshCommits(g)
|
refreshCommits(g)
|
||||||
refreshFiles(g)
|
|
||||||
refreshStatus(g)
|
refreshStatus(g)
|
||||||
devLog("pulled.")
|
devLog("pulled.")
|
||||||
}
|
}
|
||||||
|
refreshFiles(g)
|
||||||
}()
|
}()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -300,5 +303,6 @@ func handleAbortMerge(g *gocui.Gui, v *gocui.View) error {
|
|||||||
return createErrorPanel(g, output)
|
return createErrorPanel(g, output)
|
||||||
}
|
}
|
||||||
createMessagePanel(g, v, "", "Merge aborted")
|
createMessagePanel(g, v, "", "Merge aborted")
|
||||||
|
refreshStatus(g)
|
||||||
return refreshFiles(g)
|
return refreshFiles(g)
|
||||||
}
|
}
|
||||||
|
116
gitcommands.go
116
gitcommands.go
@ -110,14 +110,21 @@ func runDirectCommand(command string) (string, error) {
|
|||||||
timeStart := time.Now()
|
timeStart := time.Now()
|
||||||
|
|
||||||
commandLog(command)
|
commandLog(command)
|
||||||
cmdOut, err := exec.Command("bash", "-c", command).CombinedOutput()
|
cmdOut, err := exec.
|
||||||
|
Command("bash", "-c", command).
|
||||||
|
CombinedOutput()
|
||||||
devLog("run direct command time for command: ", command, time.Now().Sub(timeStart))
|
devLog("run direct command time for command: ", command, time.Now().Sub(timeStart))
|
||||||
|
|
||||||
return string(cmdOut), err
|
return string(cmdOut), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func branchStringParts(branchString string) (string, string) {
|
func branchStringParts(branchString string) (string, string) {
|
||||||
|
// expect string to be something like '4w master`
|
||||||
splitBranchName := strings.Split(branchString, "\t")
|
splitBranchName := strings.Split(branchString, "\t")
|
||||||
|
// if we have no \t then we have no recency, so just output that as blank
|
||||||
|
if len(splitBranchName) == 1 {
|
||||||
|
return "", branchString
|
||||||
|
}
|
||||||
return splitBranchName[0], splitBranchName[1]
|
return splitBranchName[0], splitBranchName[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,6 +145,9 @@ func coloredString(str string, colour *color.Color) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func withPadding(str string, padding int) string {
|
func withPadding(str string, padding int) string {
|
||||||
|
if padding-len(str) < 0 {
|
||||||
|
return str
|
||||||
|
}
|
||||||
return str + strings.Repeat(" ", padding-len(str))
|
return str + strings.Repeat(" ", padding-len(str))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,19 +172,39 @@ func getGitBranches() []Branch {
|
|||||||
// check if there are any branches
|
// check if there are any branches
|
||||||
branchCheck, _ := runDirectCommand("git branch")
|
branchCheck, _ := runDirectCommand("git branch")
|
||||||
if branchCheck == "" {
|
if branchCheck == "" {
|
||||||
return branches
|
return append(branches, branchFromLine("master", 0))
|
||||||
}
|
}
|
||||||
rawString, _ := runDirectCommand(getBranchesCommand)
|
rawString, _ := runDirectCommand(getBranchesCommand)
|
||||||
branchLines := splitLines(rawString)
|
branchLines := splitLines(rawString)
|
||||||
if len(branchLines) == 0 {
|
|
||||||
// sometimes the getBranchesCommand command returns nothing, in which case
|
|
||||||
// we assume you've just init'd or cloned the repo and you've got master
|
|
||||||
// checked out
|
|
||||||
branches = append(branches, branchFromLine(" *\tmaster", 0))
|
|
||||||
}
|
|
||||||
for i, line := range branchLines {
|
for i, line := range branchLines {
|
||||||
branches = append(branches, branchFromLine(line, i))
|
branches = append(branches, branchFromLine(line, i))
|
||||||
}
|
}
|
||||||
|
branches = getAndMergeFetchedBranches(branches)
|
||||||
|
return branches
|
||||||
|
}
|
||||||
|
|
||||||
|
func branchAlreadyStored(branchLine string, branches []Branch) bool {
|
||||||
|
for _, branch := range branches {
|
||||||
|
if branch.Name == branchLine {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// here branches contains all the branches that we've checked out, along with
|
||||||
|
// the recency. In this function we append the branches that are in our heads
|
||||||
|
// directory i.e. things we've fetched but haven't necessarily checked out.
|
||||||
|
// Worth mentioning this has nothing to do with the 'git merge' operation
|
||||||
|
func getAndMergeFetchedBranches(branches []Branch) []Branch {
|
||||||
|
rawString, _ := runDirectCommand(getHeadsCommand)
|
||||||
|
branchLines := splitLines(rawString)
|
||||||
|
for _, line := range branchLines {
|
||||||
|
if branchAlreadyStored(line, branches) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
branches = append(branches, branchFromLine(line, len(branches)))
|
||||||
|
}
|
||||||
return branches
|
return branches
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,27 +230,42 @@ func getStashEntryDiff(index int) (string, error) {
|
|||||||
return runCommand("git stash show -p --color stash@{" + fmt.Sprint(index) + "}")
|
return runCommand("git stash show -p --color stash@{" + fmt.Sprint(index) + "}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func includes(array []string, str string) bool {
|
||||||
|
for _, arrayStr := range array {
|
||||||
|
if arrayStr == str {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func getGitStatusFiles() []GitFile {
|
func getGitStatusFiles() []GitFile {
|
||||||
statusOutput, _ := getGitStatus()
|
statusOutput, _ := getGitStatus()
|
||||||
statusStrings := splitLines(statusOutput)
|
statusStrings := splitLines(statusOutput)
|
||||||
gitFiles := make([]GitFile, 0)
|
gitFiles := make([]GitFile, 0)
|
||||||
|
|
||||||
for _, statusString := range statusStrings {
|
for _, statusString := range statusStrings {
|
||||||
stagedChange := statusString[0:1]
|
change := statusString[0:2]
|
||||||
|
stagedChange := change[0:1]
|
||||||
unstagedChange := statusString[1:2]
|
unstagedChange := statusString[1:2]
|
||||||
filename := statusString[3:]
|
filename := statusString[3:]
|
||||||
tracked := statusString[0:2] != "??"
|
tracked := !includes([]string{"??", "A "}, change)
|
||||||
gitFile := GitFile{
|
gitFile := GitFile{
|
||||||
Name: filename,
|
Name: filename,
|
||||||
DisplayString: statusString,
|
DisplayString: statusString,
|
||||||
HasStagedChanges: tracked && stagedChange != " " && stagedChange != "U",
|
HasStagedChanges: !includes([]string{" ", "U", "?"}, stagedChange),
|
||||||
HasUnstagedChanges: !tracked || unstagedChange != " ",
|
HasUnstagedChanges: unstagedChange != " ",
|
||||||
Tracked: tracked,
|
Tracked: tracked,
|
||||||
Deleted: unstagedChange == "D" || stagedChange == "D",
|
Deleted: unstagedChange == "D" || stagedChange == "D",
|
||||||
HasMergeConflicts: statusString[0:2] == "UU",
|
HasMergeConflicts: change == "UU",
|
||||||
}
|
}
|
||||||
|
devLog("tracked", gitFile.Tracked)
|
||||||
|
devLog("hasUnstagedChanges", gitFile.HasUnstagedChanges)
|
||||||
|
devLog("HasStagedChanges", gitFile.HasStagedChanges)
|
||||||
|
devLog("DisplayString", gitFile.DisplayString)
|
||||||
gitFiles = append(gitFiles, gitFile)
|
gitFiles = append(gitFiles, gitFile)
|
||||||
}
|
}
|
||||||
|
devLog(gitFiles)
|
||||||
return gitFiles
|
return gitFiles
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -262,6 +307,10 @@ func openFile(filename string) (string, error) {
|
|||||||
return runCommand("open " + filename)
|
return runCommand("open " + filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func vsCodeOpenFile(filename string) (string, error) {
|
||||||
|
return runCommand("code -r " + filename)
|
||||||
|
}
|
||||||
|
|
||||||
func sublimeOpenFile(filename string) (string, error) {
|
func sublimeOpenFile(filename string) (string, error) {
|
||||||
return runCommand("subl " + filename)
|
return runCommand("subl " + filename)
|
||||||
}
|
}
|
||||||
@ -326,7 +375,7 @@ func gitShow(sha string) string {
|
|||||||
|
|
||||||
func getDiff(file GitFile) string {
|
func getDiff(file GitFile) string {
|
||||||
cachedArg := ""
|
cachedArg := ""
|
||||||
if file.HasStagedChanges {
|
if file.HasStagedChanges && !file.HasUnstagedChanges {
|
||||||
cachedArg = "--cached "
|
cachedArg = "--cached "
|
||||||
}
|
}
|
||||||
deletedArg := ""
|
deletedArg := ""
|
||||||
@ -334,10 +383,10 @@ func getDiff(file GitFile) string {
|
|||||||
deletedArg = "-- "
|
deletedArg = "-- "
|
||||||
}
|
}
|
||||||
trackedArg := ""
|
trackedArg := ""
|
||||||
if !file.Tracked {
|
if !file.Tracked && !file.HasStagedChanges {
|
||||||
trackedArg = "--no-index /dev/null "
|
trackedArg = "--no-index /dev/null "
|
||||||
}
|
}
|
||||||
command := "git diff --color " + cachedArg + deletedArg + trackedArg + file.Name
|
command := "git diff -b --color " + cachedArg + deletedArg + trackedArg + file.Name
|
||||||
// for now we assume an error means the file was deleted
|
// for now we assume an error means the file was deleted
|
||||||
s, _ := runCommand(command)
|
s, _ := runCommand(command)
|
||||||
return s
|
return s
|
||||||
@ -352,8 +401,15 @@ func stageFile(file string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func unStageFile(file string) error {
|
func unStageFile(file string, tracked bool) error {
|
||||||
_, err := runCommand("git reset HEAD " + file)
|
var command string
|
||||||
|
if tracked {
|
||||||
|
command = "git reset HEAD "
|
||||||
|
} else {
|
||||||
|
command = "git rm --cached "
|
||||||
|
}
|
||||||
|
devLog(command)
|
||||||
|
_, err := runCommand(command + file)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -446,7 +502,7 @@ func gitCommitsToPush() []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func gitCurrentBranchName() string {
|
func gitCurrentBranchName() string {
|
||||||
branchName, err := runDirectCommand("git rev-parse --abbrev-ref HEAD")
|
branchName, err := runDirectCommand("git symbolic-ref --short HEAD")
|
||||||
// if there is an error, assume there are no branches yet
|
// if there is an error, assume there are no branches yet
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ""
|
return ""
|
||||||
@ -467,6 +523,26 @@ git reflog -n100 --pretty='%cr|%gs' --grep-reflog='checkout: moving' HEAD | {
|
|||||||
printf "%s\t%s\n" "$date" "$branch"
|
printf "%s\t%s\n" "$date" "$branch"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
done | sed 's/ days /d /g' | sed 's/ weeks /w /g' | sed 's/ hours /h /g' | sed 's/ minutes /m /g' | sed 's/ seconds /m /g' | sed 's/ago//g' | tr -d ' '
|
done \
|
||||||
|
| sed 's/ days /d /g' \
|
||||||
|
| sed 's/ day /d /g' \
|
||||||
|
| sed 's/ weeks /w /g' \
|
||||||
|
| sed 's/ week /w /g' \
|
||||||
|
| sed 's/ hours /h /g' \
|
||||||
|
| sed 's/ hour /h /g' \
|
||||||
|
| sed 's/ minutes /m /g' \
|
||||||
|
| sed 's/ minute /m /g' \
|
||||||
|
| sed 's/ seconds /s /g' \
|
||||||
|
| sed 's/ second /s /g' \
|
||||||
|
| sed 's/ago//g' \
|
||||||
|
| tr -d ' '
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const getHeadsCommand = `git show-ref \
|
||||||
|
| grep 'refs/heads/\|refs/remotes/origin/' \
|
||||||
|
| sed 's/.*refs\/heads\///g' \
|
||||||
|
| sed 's/.*refs\/remotes\/origin\///g' \
|
||||||
|
| grep -v '^HEAD$' \
|
||||||
|
| sort \
|
||||||
|
| uniq`
|
||||||
|
1
gui.go
1
gui.go
@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
"log"
|
"log"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
// "strings"
|
// "strings"
|
||||||
"github.com/golang-collections/collections/stack"
|
"github.com/golang-collections/collections/stack"
|
||||||
"github.com/jesseduffield/gocui"
|
"github.com/jesseduffield/gocui"
|
||||||
|
10
main.go
10
main.go
@ -48,6 +48,15 @@ func localLog(colour color.Attribute, path string, objects ...interface{}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func navigateToRepoRootDirectory() {
|
||||||
|
_, err := os.Stat(".git")
|
||||||
|
for os.IsNotExist(err) {
|
||||||
|
devLog("going up a directory to find the root")
|
||||||
|
os.Chdir("..")
|
||||||
|
_, err = os.Stat(".git")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
debuggingPointer := flag.Bool("debug", false, "a boolean")
|
debuggingPointer := flag.Bool("debug", false, "a boolean")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
@ -55,5 +64,6 @@ func main() {
|
|||||||
devLog("\n\n\n\n\n\n\n\n\n\n")
|
devLog("\n\n\n\n\n\n\n\n\n\n")
|
||||||
startTime = time.Now()
|
startTime = time.Now()
|
||||||
verifyInGitRepo()
|
verifyInGitRepo()
|
||||||
|
navigateToRepoRootDirectory()
|
||||||
run()
|
run()
|
||||||
}
|
}
|
||||||
|
37
test/generate_basic_repo.sh
Executable file
37
test/generate_basic_repo.sh
Executable file
@ -0,0 +1,37 @@
|
|||||||
|
#!/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
|
||||||
|
|
||||||
|
echo "Here is a story that has been told throuhg the ages" >> file1
|
||||||
|
git add file1
|
||||||
|
git commit -m "first commit"
|
||||||
|
|
||||||
|
git checkout -b develop
|
||||||
|
|
||||||
|
echo "once upon a time there was a dog" >> file1
|
||||||
|
git add file1
|
||||||
|
git commit -m "first commit on develop"
|
||||||
|
|
||||||
|
git checkout master
|
||||||
|
|
||||||
|
echo "once upon a time there was a cat" >> file1
|
||||||
|
git add file1
|
||||||
|
git commit -m "first commit on develop"
|
||||||
|
|
||||||
|
git merge develop # should have a merge conflict here
|
@ -36,7 +36,6 @@ func nextView(g *gocui.Gui, v *gocui.View) error {
|
|||||||
focusedView, err := g.View(focusedViewName)
|
focusedView, err := g.View(focusedViewName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
return switchFocus(g, v, focusedView)
|
return switchFocus(g, v, focusedView)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user