mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-05-12 04:45:47 +02:00
merge with develop
This commit is contained in:
commit
a47c889cbb
12 changed files with 1178 additions and 1044 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -3,3 +3,4 @@ commands.log
|
|||
extra/lgit.rb
|
||||
notes/go.notes
|
||||
TODO.notes
|
||||
TODO.md
|
||||
|
|
|
@ -7,6 +7,10 @@ import (
|
|||
)
|
||||
|
||||
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)
|
||||
if output, err := gitCheckout(branch.Name, false); err != nil {
|
||||
createErrorPanel(g, output)
|
||||
|
@ -70,7 +74,7 @@ func renderBranchesOptions(g *gocui.Gui) error {
|
|||
"f": "force checkout",
|
||||
"m": "merge",
|
||||
"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 {
|
||||
stageFile(file.Name)
|
||||
} else {
|
||||
unStageFile(file.Name)
|
||||
unStageFile(file.Name, file.Tracked)
|
||||
}
|
||||
|
||||
if err := refreshFiles(g); err != nil {
|
||||
|
@ -101,6 +101,7 @@ func renderfilesOptions(g *gocui.Gui, gitFile *GitFile) error {
|
|||
"c": "commit changes",
|
||||
"o": "open",
|
||||
"s": "open in sublime",
|
||||
"v": "open in vscode",
|
||||
"i": "ignore",
|
||||
"d": "delete",
|
||||
"space": "toggle staged",
|
||||
|
@ -125,7 +126,6 @@ func handleFileSelect(g *gocui.Gui, v *gocui.View) error {
|
|||
return err
|
||||
}
|
||||
renderString(g, "main", "No changed files")
|
||||
colorLog(color.FgRed, "error")
|
||||
return renderfilesOptions(g, nil)
|
||||
}
|
||||
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 {
|
||||
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 {
|
||||
return refreshFiles(g)
|
||||
|
@ -197,7 +200,7 @@ func renderGitFile(gitFile GitFile, filesView *gocui.View) {
|
|||
// objects with each render
|
||||
red := color.New(color.FgRed)
|
||||
green := color.New(color.FgGreen)
|
||||
if !gitFile.Tracked {
|
||||
if !gitFile.Tracked && !gitFile.HasStagedChanges {
|
||||
red.Fprintln(filesView, gitFile.DisplayString)
|
||||
return
|
||||
}
|
||||
|
@ -251,10 +254,10 @@ func pullFiles(g *gocui.Gui, v *gocui.View) error {
|
|||
} else {
|
||||
closeConfirmationPrompt(g)
|
||||
refreshCommits(g)
|
||||
refreshFiles(g)
|
||||
refreshStatus(g)
|
||||
devLog("pulled.")
|
||||
}
|
||||
refreshFiles(g)
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
@ -300,5 +303,6 @@ func handleAbortMerge(g *gocui.Gui, v *gocui.View) error {
|
|||
return createErrorPanel(g, output)
|
||||
}
|
||||
createMessagePanel(g, v, "", "Merge aborted")
|
||||
refreshStatus(g)
|
||||
return refreshFiles(g)
|
||||
}
|
||||
|
|
116
gitcommands.go
116
gitcommands.go
|
@ -110,14 +110,21 @@ func runDirectCommand(command string) (string, error) {
|
|||
timeStart := time.Now()
|
||||
|
||||
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))
|
||||
|
||||
return string(cmdOut), err
|
||||
}
|
||||
|
||||
func branchStringParts(branchString string) (string, string) {
|
||||
// expect string to be something like '4w master`
|
||||
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]
|
||||
}
|
||||
|
||||
|
@ -138,6 +145,9 @@ func coloredString(str string, colour *color.Color) string {
|
|||
}
|
||||
|
||||
func withPadding(str string, padding int) string {
|
||||
if padding-len(str) < 0 {
|
||||
return str
|
||||
}
|
||||
return str + strings.Repeat(" ", padding-len(str))
|
||||
}
|
||||
|
||||
|
@ -162,19 +172,39 @@ func getGitBranches() []Branch {
|
|||
// check if there are any branches
|
||||
branchCheck, _ := runDirectCommand("git branch")
|
||||
if branchCheck == "" {
|
||||
return branches
|
||||
return append(branches, branchFromLine("master", 0))
|
||||
}
|
||||
rawString, _ := runDirectCommand(getBranchesCommand)
|
||||
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 {
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -200,27 +230,42 @@ func getStashEntryDiff(index int) (string, error) {
|
|||
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 {
|
||||
statusOutput, _ := getGitStatus()
|
||||
statusStrings := splitLines(statusOutput)
|
||||
gitFiles := make([]GitFile, 0)
|
||||
|
||||
for _, statusString := range statusStrings {
|
||||
stagedChange := statusString[0:1]
|
||||
change := statusString[0:2]
|
||||
stagedChange := change[0:1]
|
||||
unstagedChange := statusString[1:2]
|
||||
filename := statusString[3:]
|
||||
tracked := statusString[0:2] != "??"
|
||||
tracked := !includes([]string{"??", "A "}, change)
|
||||
gitFile := GitFile{
|
||||
Name: filename,
|
||||
DisplayString: statusString,
|
||||
HasStagedChanges: tracked && stagedChange != " " && stagedChange != "U",
|
||||
HasUnstagedChanges: !tracked || unstagedChange != " ",
|
||||
HasStagedChanges: !includes([]string{" ", "U", "?"}, stagedChange),
|
||||
HasUnstagedChanges: unstagedChange != " ",
|
||||
Tracked: tracked,
|
||||
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)
|
||||
}
|
||||
devLog(gitFiles)
|
||||
return gitFiles
|
||||
}
|
||||
|
||||
|
@ -262,6 +307,10 @@ func openFile(filename string) (string, error) {
|
|||
return runCommand("open " + filename)
|
||||
}
|
||||
|
||||
func vsCodeOpenFile(filename string) (string, error) {
|
||||
return runCommand("code -r " + filename)
|
||||
}
|
||||
|
||||
func sublimeOpenFile(filename string) (string, error) {
|
||||
return runCommand("subl " + filename)
|
||||
}
|
||||
|
@ -326,7 +375,7 @@ func gitShow(sha string) string {
|
|||
|
||||
func getDiff(file GitFile) string {
|
||||
cachedArg := ""
|
||||
if file.HasStagedChanges {
|
||||
if file.HasStagedChanges && !file.HasUnstagedChanges {
|
||||
cachedArg = "--cached "
|
||||
}
|
||||
deletedArg := ""
|
||||
|
@ -334,10 +383,10 @@ func getDiff(file GitFile) string {
|
|||
deletedArg = "-- "
|
||||
}
|
||||
trackedArg := ""
|
||||
if !file.Tracked {
|
||||
if !file.Tracked && !file.HasStagedChanges {
|
||||
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
|
||||
s, _ := runCommand(command)
|
||||
return s
|
||||
|
@ -352,8 +401,15 @@ func stageFile(file string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
func unStageFile(file string) error {
|
||||
_, err := runCommand("git reset HEAD " + file)
|
||||
func unStageFile(file string, tracked bool) error {
|
||||
var command string
|
||||
if tracked {
|
||||
command = "git reset HEAD "
|
||||
} else {
|
||||
command = "git rm --cached "
|
||||
}
|
||||
devLog(command)
|
||||
_, err := runCommand(command + file)
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -446,7 +502,7 @@ func gitCommitsToPush() []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 err != nil {
|
||||
return ""
|
||||
|
@ -467,6 +523,26 @@ git reflog -n100 --pretty='%cr|%gs' --grep-reflog='checkout: moving' HEAD | {
|
|||
printf "%s\t%s\n" "$date" "$branch"
|
||||
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"
|
||||
"time"
|
||||
|
||||
// "strings"
|
||||
"github.com/golang-collections/collections/stack"
|
||||
"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() {
|
||||
debuggingPointer := flag.Bool("debug", false, "a boolean")
|
||||
flag.Parse()
|
||||
|
@ -55,5 +64,6 @@ func main() {
|
|||
devLog("\n\n\n\n\n\n\n\n\n\n")
|
||||
startTime = time.Now()
|
||||
verifyInGitRepo()
|
||||
navigateToRepoRootDirectory()
|
||||
run()
|
||||
}
|
||||
|
|
1
newFile
Normal file
1
newFile
Normal file
|
@ -0,0 +1 @@
|
|||
newFile
|
1
newFile2
Normal file
1
newFile2
Normal file
|
@ -0,0 +1 @@
|
|||
newFile
|
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)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
return err
|
||||
}
|
||||
return switchFocus(g, v, focusedView)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue