summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorİbrahim Serdar Açıkgöz <serdaracikgoz86@gmail.com>2018-12-18 15:35:54 +0300
committerİbrahim Serdar Açıkgöz <serdaracikgoz86@gmail.com>2018-12-18 15:35:54 +0300
commitbb2ed3b8dd613ea9b974d15e5823f072659dbc32 (patch)
tree2dbddd081f3a6c3349b3e1d7aebd2b2f5dc680a1
parentbetter gui refreshing with event dispatcher and minor refacotrs on gui (diff)
downloadgitbatch-bb2ed3b8dd613ea9b974d15e5823f072659dbc32.tar.gz
repository construction revised
-rw-r--r--pkg/git/branch.go87
-rw-r--r--pkg/git/cmd-diff.go89
-rw-r--r--pkg/git/cmd-reset.go2
-rw-r--r--pkg/git/commit.go79
-rw-r--r--pkg/git/repository.go202
-rw-r--r--pkg/gui/diffview.go2
6 files changed, 230 insertions, 231 deletions
diff --git a/pkg/git/branch.go b/pkg/git/branch.go
index 085ecb1..7e78059 100644
--- a/pkg/git/branch.go
+++ b/pkg/git/branch.go
@@ -5,7 +5,7 @@ import (
log "github.com/sirupsen/logrus"
"gopkg.in/src-d/go-git.v4"
"gopkg.in/src-d/go-git.v4/plumbing"
- "regexp"
+
"strconv"
"strings"
)
@@ -22,18 +22,6 @@ type Branch struct {
Clean bool
}
-// returns the active branch of the repository entity by simply getting the
-// head reference and searching it from the entities branch slice
-func (entity *RepoEntity) getActiveBranch() (branch *Branch) {
- headRef, _ := entity.Repository.Head()
- for _, lb := range entity.Branches {
- if lb.Name == headRef.Name().Short() {
- return lb
- }
- }
- return nil
-}
-
// search for branches in go-git way. It is useful to do so that checkout and
// checkout error handling can be handled by code rather than struggling with
// git cammand and its output
@@ -45,6 +33,7 @@ func (entity *RepoEntity) loadLocalBranches() error {
return err
}
defer branches.Close()
+ headRef, _ := entity.Repository.Head()
branches.ForEach(func(b *plumbing.Reference) error {
if b.Type() == plumbing.HashReference {
var push, pull string
@@ -67,7 +56,16 @@ func (entity *RepoEntity) loadLocalBranches() error {
pull = strconv.Itoa(len(pullables))
}
clean := entity.isClean()
- branch := &Branch{Name: b.Name().Short(), Reference: b, Pushables: push, Pullables: pull, Clean: clean}
+ branch := &Branch{
+ Name: b.Name().Short(),
+ Reference: b,
+ Pushables: push,
+ Pullables: pull,
+ Clean: clean,
+ }
+ if b.Hash() == headRef.Hash() {
+ entity.Branch = branch
+ }
lbs = append(lbs, branch)
}
return nil
@@ -128,7 +126,7 @@ func (entity *RepoEntity) Checkout(branch *Branch) error {
entity.loadCommits()
entity.Commit = entity.Commits[0]
entity.Branch = branch
- entity.RefreshPushPull()
+
// make this conditional on global scale
err = entity.Remote.SyncBranches(branch.Name)
return entity.Refresh()
@@ -152,62 +150,3 @@ func (entity *RepoEntity) isClean() bool {
}
return false
}
-
-// RefreshPushPull refreshes the active branchs pushable and pullable count
-func (entity *RepoEntity) RefreshPushPull() {
- pushables, err := RevList(entity, RevListOptions{
- Ref1: "@{u}",
- Ref2: "HEAD",
- })
- if err != nil {
- entity.Branch.Pushables = pushables[0]
- } else {
- entity.Branch.Pushables = strconv.Itoa(len(pushables))
- }
- pullables, err := RevList(entity, RevListOptions{
- Ref1: "HEAD",
- Ref2: "@{u}",
- })
- if err != nil {
- entity.Branch.Pullables = pullables[0]
- } else {
- entity.Branch.Pullables = strconv.Itoa(len(pullables))
- }
-}
-
-// this function creates the commit entities according to active branchs diffs
-// to *its* configured upstream
-func (entity *RepoEntity) pullDiffsToUpstream() ([]*Commit, error) {
- remoteCommits := make([]*Commit, 0)
- pullables, err := RevList(entity, RevListOptions{
- Ref1: "HEAD",
- Ref2: "@{u}",
- })
- if err != nil {
- // possibly found nothing or no upstream set
- } else {
- re := regexp.MustCompile(`\r?\n`)
- for _, s := range pullables {
- commit := &Commit{
- Hash: s,
- Author: GitShowEmail(entity.AbsPath, s),
- Message: re.ReplaceAllString(GitShowBody(entity.AbsPath, s), " "),
- Time: GitShowDate(entity.AbsPath, s),
- CommitType: RemoteCommit,
- }
- remoteCommits = append(remoteCommits, commit)
- }
- }
- return remoteCommits, nil
-}
-
-func (entity *RepoEntity) pushDiffsToUpstream() ([]string, error) {
- pushables, err := RevList(entity, RevListOptions{
- Ref1: "@{u}",
- Ref2: "HEAD",
- })
- if err != nil {
- return make([]string, 0), nil
- }
- return pushables, nil
-}
diff --git a/pkg/git/cmd-diff.go b/pkg/git/cmd-diff.go
new file mode 100644
index 0000000..3fe0bd6
--- /dev/null
+++ b/pkg/git/cmd-diff.go
@@ -0,0 +1,89 @@
+package git
+
+import (
+ "errors"
+
+ "gopkg.in/src-d/go-git.v4"
+ "gopkg.in/src-d/go-git.v4/plumbing"
+)
+
+var (
+ diffCmdMode string
+
+ diffCommand = "diff"
+ diffCmdModeLegacy = "git"
+ diffCmdModeNative = "go-git"
+)
+
+// Diff is a wrapper function for "git diff" command
+// Diff function returns the diff to previous commit detail of the given has
+// of a specific commit
+func Diff(entity *RepoEntity, hash string) (diff string, err error) {
+ diffCmdMode = diffCmdModeNative
+
+ switch diffCmdMode {
+ case diffCmdModeLegacy:
+ return diffWithGit(entity, hash)
+ case diffCmdModeNative:
+ return diffWithGoGit(entity, hash)
+ }
+ return diff, errors.New("Unhandled diff operation")
+}
+
+func diffWithGit(entity *RepoEntity, hash string) (diff string, err error) {
+ return diff, nil
+}
+
+func diffWithGoGit(entity *RepoEntity, hash string) (diff string, err error) {
+ currentCommitIndex := 0
+ for i, cs := range entity.Commits {
+ if cs.Hash == hash {
+ currentCommitIndex = i
+ }
+ }
+ if len(entity.Commits)-currentCommitIndex <= 1 {
+ return "there is no diff", nil
+ }
+
+ // maybe we dont need to log the repo again?
+ commits, err := entity.Repository.Log(&git.LogOptions{
+ From: plumbing.NewHash(entity.Commit.Hash),
+ Order: git.LogOrderCommitterTime,
+ })
+ if err != nil {
+ return "", err
+ }
+
+ currentCommit, err := commits.Next()
+ if err != nil {
+ return "", err
+ }
+ currentTree, err := currentCommit.Tree()
+ if err != nil {
+ return diff, err
+ }
+
+ prevCommit, err := commits.Next()
+ if err != nil {
+ return "", err
+ }
+ prevTree, err := prevCommit.Tree()
+ if err != nil {
+ return diff, err
+ }
+
+ changes, err := prevTree.Diff(currentTree)
+ if err != nil {
+ return "", err
+ }
+
+ // here we collect the actual diff
+ for _, c := range changes {
+ patch, err := c.Patch()
+ if err != nil {
+ break
+ }
+ diff = diff + patch.String() + "\n"
+ }
+ return diff, nil
+}
diff --git a/pkg/git/cmd-reset.go b/pkg/git/cmd-reset.go
index 2e6ed91..b5e8a99 100644
--- a/pkg/git/cmd-reset.go
+++ b/pkg/git/cmd-reset.go
@@ -48,7 +48,7 @@ const (
// Reset is the wrapper of "git reset" command
func Reset(entity *RepoEntity, file *File, option ResetOptions) error {
- resetCmdMode = addCmdModeLegacy
+ resetCmdMode = resetCmdModeLegacy
switch resetCmdMode {
case resetCmdModeLegacy:
diff --git a/pkg/git/commit.go b/pkg/git/commit.go
index 8694f1a..ef12890 100644
--- a/pkg/git/commit.go
+++ b/pkg/git/commit.go
@@ -5,7 +5,6 @@ import (
log "github.com/sirupsen/logrus"
"gopkg.in/src-d/go-git.v4"
- "gopkg.in/src-d/go-git.v4/plumbing"
"gopkg.in/src-d/go-git.v4/plumbing/object"
)
@@ -126,59 +125,39 @@ func (entity *RepoEntity) loadCommits() error {
return nil
}
-// Diff function returns the diff to previous commit detail of the given has
-// of a specific commit
-func (entity *RepoEntity) Diff(hash string) (diff string, err error) {
-
- currentCommitIndex := 0
- for i, cs := range entity.Commits {
- if cs.Hash == hash {
- currentCommitIndex = i
- }
- }
- if len(entity.Commits)-currentCommitIndex <= 1 {
- return "there is no diff", nil
- }
-
- // maybe we dont need to log the repo again?
- commits, err := entity.Repository.Log(&git.LogOptions{
- From: plumbing.NewHash(entity.Commit.Hash),
- Order: git.LogOrderCommitterTime,
+// this function creates the commit entities according to active branchs diffs
+// to *its* configured upstream
+func (entity *RepoEntity) pullDiffsToUpstream() ([]*Commit, error) {
+ remoteCommits := make([]*Commit, 0)
+ pullables, err := RevList(entity, RevListOptions{
+ Ref1: "HEAD",
+ Ref2: "@{u}",
})
if err != nil {
- return "", err
- }
-
- currentCommit, err := commits.Next()
- if err != nil {
- return "", err
- }
- currentTree, err := currentCommit.Tree()
- if err != nil {
- return diff, err
- }
-
- prevCommit, err := commits.Next()
- if err != nil {
- return "", err
- }
- prevTree, err := prevCommit.Tree()
- if err != nil {
- return diff, err
+ // possibly found nothing or no upstream set
+ } else {
+ re := regexp.MustCompile(`\r?\n`)
+ for _, s := range pullables {
+ commit := &Commit{
+ Hash: s,
+ Author: GitShowEmail(entity.AbsPath, s),
+ Message: re.ReplaceAllString(GitShowBody(entity.AbsPath, s), " "),
+ Time: GitShowDate(entity.AbsPath, s),
+ CommitType: RemoteCommit,
+ }
+ remoteCommits = append(remoteCommits, commit)
+ }
}
+ return remoteCommits, nil
+}
- changes, err := prevTree.Diff(currentTree)
+func (entity *RepoEntity) pushDiffsToUpstream() ([]string, error) {
+ pushables, err := RevList(entity, RevListOptions{
+ Ref1: "@{u}",
+ Ref2: "HEAD",
+ })
if err != nil {
- return "", err
- }
-
- // here we collect the actual diff
- for _, c := range changes {
- patch, err := c.Patch()
- if err != nil {
- break
- }
- diff = diff + patch.String() + "\n"
+ return make([]string, 0), nil
}
- return diff, nil
+ return pushables, nil
}
diff --git a/pkg/git/repository.go b/pkg/git/repository.go
index 3debe44..6d9226f 100644
--- a/pkg/git/repository.go
+++ b/pkg/git/repository.go
@@ -64,98 +64,123 @@ const (
RepositoryUpdated = "repository.updated"
)
+// FastInitializeRepo initializes a RepoEntity struct without its belongings.
+func FastInitializeRepo(dir string) (e *RepoEntity, err error) {
+ f, err := os.Open(dir)
+ if err != nil {
+ return nil, err
+ }
+ // get status of the file
+ fstat, _ := f.Stat()
+ r, err := git.PlainOpen(dir)
+ if err != nil {
+ return nil, err
+ }
+ // initialize RepoEntity with minimum viable fields
+ e = &RepoEntity{RepoID: helpers.RandomString(8),
+ Name: fstat.Name(),
+ AbsPath: dir,
+ ModTime: fstat.ModTime(),
+ Repository: *r,
+ state: Available,
+ mutex: &sync.RWMutex{},
+ listeners: make(map[string][]RepositoryListener),
+ }
+ return e, nil
+}
+
// InitializeRepo initializes a RepoEntity struct with its belongings.
-func InitializeRepo(directory string) (entity *RepoEntity, err error) {
- entity, err = FastInitializeRepo(directory)
+func InitializeRepo(directory string) (e *RepoEntity, err error) {
+ e, err = FastInitializeRepo(directory)
if err != nil {
return nil, err
}
+ // need nothing extra but loading additional components
+ return e, e.loadComponents(true)
+}
- // after we intiate the struct we can fill its values
- entity.loadLocalBranches()
- entity.loadCommits()
- // handle if there is no commit, maybe?
- if len(entity.Commits) > 0 {
- // select first commit
- entity.Commit = entity.Commits[0]
- } else {
- return entity, errors.New("There is no commit for this repository: " + directory)
+// loadComponents initializes the fields of a repository such as branches,
+// remotes, commits etc.
+func (e *RepoEntity) loadComponents(hard bool) error {
+ if err := e.loadLocalBranches(); err != nil {
+ return err
}
- // lets load remotes this time
- entity.loadRemotes()
- // set the active branch to repositories HEAD
- entity.Branch = entity.getActiveBranch()
- if err = entity.loadStashedItems(); err != nil {
- // TODO: fix here.
+ if err := e.loadCommits(); err != nil {
+ return err
+ }
+ if err := e.loadRemotes(); err != nil {
+ return err
+ }
+ if err := e.loadStashedItems(); err != nil {
+ log.Warn("Cannot load stashes")
}
- if len(entity.Remotes) > 0 {
- // TODO: tend to take origin/master as default
- entity.Remote = entity.Remotes[0]
- if entity.Branch == nil {
- return nil, errors.New("Unable to find a valid branch")
+ if hard {
+ // handle if there is no commit, maybe?
+ // set commit pointer for repository
+ if len(e.Commits) > 0 {
+ // select first commit
+ e.Commit = e.Commits[0]
}
- if err = entity.Remote.SyncBranches(entity.Branch.Name); err != nil {
- // probably couldn't find, but its ok.
+ // set remote pointer for repository
+ if len(e.Remotes) > 0 {
+ // TODO: tend to take origin/master as default
+ e.Remote = e.Remotes[0]
+ // if couldn't find, its ok.
+ e.Remote.SyncBranches(e.Branch.Name)
+ } else {
+ // if there is no remote, this project is totally useless actually
+ return errors.New("There is no remote for this repository")
}
- } else {
- // if there is no remote, this project is totally useless actually
- return entity, errors.New("There is no remote for this repository: " + directory)
}
- return entity, nil
+ return nil
}
-// FastInitializeRepo initializes a RepoEntity struct without its belongings.
-func FastInitializeRepo(directory string) (entity *RepoEntity, err error) {
- file, err := os.Open(directory)
- if err != nil {
- log.WithFields(log.Fields{
- "directory": directory,
- }).Trace("Cannot open as directory")
- return nil, err
- }
- fileInfo, err := file.Stat()
- if err != nil {
- return nil, err
+// Refresh the belongings of a repositoriy, this function is called right after
+// fetch/pull/merge operations
+func (e *RepoEntity) Refresh() error {
+ var err error
+ // error can be ignored since the file already exists when app is loading
+ // if the RepoEntity is only fast initialized, no need to refresh because
+ // it won't contain its belongings
+ if e.Branch == nil {
+ return nil
}
- r, err := git.PlainOpen(directory)
+ file, _ := os.Open(e.AbsPath)
+ fstat, _ := file.Stat()
+ // re-initialize the go-git repository struct after supposed update
+ r, err := git.PlainOpen(e.AbsPath)
if err != nil {
- log.WithFields(log.Fields{
- "directory": directory,
- }).Trace("Cannot open directory as a git repository")
- return nil, err
+ return err
}
- // initialize entity with minimum viable fields
- entity = &RepoEntity{RepoID: helpers.RandomString(8),
- Name: fileInfo.Name(),
- AbsPath: directory,
- ModTime: fileInfo.ModTime(),
- Repository: *r,
- state: Available,
- mutex: &sync.RWMutex{},
- listeners: make(map[string][]RepositoryListener),
+ e.Repository = *r
+ // modification date may be changed
+ e.ModTime = fstat.ModTime()
+ if err := e.loadComponents(false); err != nil {
+ return err
}
- return entity, nil
+ // we could send an event data but we don't need for this topic
+ return e.Publish(RepositoryUpdated, nil)
}
// On adds new listener.
// listener is a callback function that will be called when event emits
-func (entity *RepoEntity) On(event string, listener RepositoryListener) {
- entity.mutex.Lock()
- defer entity.mutex.Unlock()
-
- entity.listeners[event] = append(entity.listeners[event], listener)
+func (e *RepoEntity) On(event string, listener RepositoryListener) {
+ e.mutex.Lock()
+ defer e.mutex.Unlock()
+ // add listener to the specific event topic
+ e.listeners[event] = append(e.listeners[event], listener)
}
// Emit notifies listeners about the event
-func (entity *RepoEntity) Emit(eventName string, data interface{}) error {
- entity.mutex.RLock()
- defer entity.mutex.RUnlock()
-
- listeners, ok := entity.listeners[eventName]
+func (e *RepoEntity) Publish(eventName string, data interface{}) error {
+ e.mutex.RLock()
+ defer e.mutex.RUnlock()
+ // let's find listeners for this event topic
+ listeners, ok := e.listeners[eventName]
if !ok {
return nil
}
-
+ // now notify the listeners and channel the data
for i := range listeners {
event := &RepositoryEvent{
Name: eventName,
@@ -169,48 +194,15 @@ func (entity *RepoEntity) Emit(eventName string, data interface{}) error {
}
// State returns the state of the repository such as queued, failed etc.
-func (entity *RepoEntity) State() RepoState {
- return entity.state
+func (e *RepoEntity) State() RepoState {
+ return e.state
}
// SetState sets the state of repository and sends repository updated event
-func (entity *RepoEntity) SetState(state RepoState) {
- entity.state = state
+func (e *RepoEntity) SetState(state RepoState) {
+ e.state = state
// we could send an event data but we don't need for this topic
- entity.Emit(RepositoryUpdated, nil)
-}
-
-// Refresh the belongings of a repositoriy, this function is called right after
-// fetch/pull/merge operations
-func (entity *RepoEntity) Refresh() error {
- var err error
- // error can be ignored since the file already exists when app is loading
- if entity.Branch == nil {
- return nil
- }
- file, _ := os.Open(entity.AbsPath)
- fileInfo, err := file.Stat()
- if err != nil {
- return err
- }
- r, err := git.PlainOpen(entity.AbsPath)
- if err != nil {
- return err
+ if err := e.Publish(RepositoryUpdated, nil); err != nil {
+ log.Warnf("Cannot publish on %s topic.\n", RepositoryUpdated)
}
- entity.Repository = *r
- entity.ModTime = fileInfo.ModTime()
- if err := entity.loadLocalBranches(); err != nil {
- return err
- }
- entity.Branch.Clean = entity.isClean()
- entity.RefreshPushPull()
- if err := entity.loadCommits(); err != nil {
- return err
- }
- if err := entity.loadRemotes(); err != nil {
- return err
- }
- err = entity.loadStashedItems()
- // we could send an event data but we don't need for this topic
- return entity.Emit(RepositoryUpdated, nil)
}
diff --git a/pkg/gui/diffview.go b/pkg/gui/diffview.go
index 55286e9..caf42fc 100644
--- a/pkg/gui/diffview.go
+++ b/pkg/gui/diffview.go
@@ -38,7 +38,7 @@ func (gui *Gui) openCommitDiffView(g *gocui.Gui, v *gocui.View) (err error) {
commit := entity.Commit
commitDetail := []string{("Hash: " + cyan.Sprint(commit.Hash) + "\n" + "Author: " + commit.Author +
"\n" + commit.Time + "\n" + "\n" + "\t\t" + commit.Message + "\n")}
- diff, err := entity.Diff(entity.Commit.Hash)
+ diff, err := git.Diff(entity, entity.Commit.Hash)
if err != nil {
return err
}