diff options
| 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 |
| commit | bb2ed3b8dd613ea9b974d15e5823f072659dbc32 (patch) | |
| tree | 2dbddd081f3a6c3349b3e1d7aebd2b2f5dc680a1 | |
| parent | better gui refreshing with event dispatcher and minor refacotrs on gui (diff) | |
| download | gitbatch-bb2ed3b8dd613ea9b974d15e5823f072659dbc32.tar.gz | |
repository construction revised
| -rw-r--r-- | pkg/git/branch.go | 87 | ||||
| -rw-r--r-- | pkg/git/cmd-diff.go | 89 | ||||
| -rw-r--r-- | pkg/git/cmd-reset.go | 2 | ||||
| -rw-r--r-- | pkg/git/commit.go | 79 | ||||
| -rw-r--r-- | pkg/git/repository.go | 202 | ||||
| -rw-r--r-- | pkg/gui/diffview.go | 2 |
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 } |
