Documentation ¶
Index ¶
- Variables
- func Execute()
- type ConflictResult
- type ExRepository
- func (r *ExRepository) ExecuteCommand(arguments ...string) (string, error)
- func (r *ExRepository) LocalUserEmail() string
- func (r *ExRepository) MergeBase(references ...*plumbing.Reference) (*object.Commit, error)
- func (r *ExRepository) MergeTree(mergeBase *object.Commit, references ...*plumbing.Reference) (string, error)
Constants ¶
This section is empty.
Variables ¶
View Source
var L *log.Logger
View Source
var RootCmd = &cobra.Command{ Use: "confligt", Short: "Find conflicting branches in git repositories", Long: `Confligt finds conflicting branches in git repositories. Without any arguments or flags, confligt will inspect all local & remote branches in the current working directory - that have commits since 7 days ago - against each other and other remote branches (from the default origin) to find conflicting pairs.`, Example: ` # Filter by branches that were updated a day ago $ confligt --since='1 day' # Filter by branches that start with foo- or bar- $ confligt --filter='\b(foo|bar)-' # Inspect branches in the remote named alice. Use develop as the default branch. $ confligt --remote=alice --main=develop `, DisableAutoGenTag: true, Args: cobra.MaximumNArgs(1), Run: func(cmd *cobra.Command, args []string) { var secondsSince float64 if millisSince, err := gohumantime.ToMilliseconds(viper.GetString("since")); err == nil && millisSince != 0 { secondsSince = float64(millisSince) / 1000 } else { L.Fatalf("Unable to parse: %s", viper.GetString("since")) } var repoPath string if len(args) > 0 { repoPath = args[0] } else { repoPath = "." } refBranchName := path.Join("refs/remotes", viper.GetString("remote"), viper.GetString("main")) remoteName := viper.GetString("remote") bare, err := git.PlainOpen(repoPath) repository := &ExRepository{bare} if err != nil { L.Fatalf("Unable to open git repository at %s", repoPath) } var currentUserEmail string if viper.GetBool("mine") { currentUserEmail = repository.LocalUserEmail() if currentUserEmail == "" { L.Fatal("Unable to infer current user") } } if viper.GetBool("fetch") { if V { L.Printf("Fetching from remote %s...", remoteName) } _, err := repository.ExecuteCommand("fetch") if err != nil { L.Fatalf("Error fetching from %s", remoteName) } } branchFilter := func(ref *plumbing.Reference) bool { matchesFilter := true if viper.GetString("filter") != "" { match, _ := regexp.MatchString(viper.GetString("filter"), ref.Name().Short()) matchesFilter = matchesFilter && match } if viper.GetBool("mine") { commit, _ := repository.CommitObject(ref.Hash()) matchesFilter = matchesFilter && commit.Author.Email == currentUserEmail } return matchesFilter } var mainBranch *plumbing.Reference conflicts := 0 references, err := repository.References() remoteBranches := make(map[string]*plumbing.Reference) localBranches := make(map[string]*plumbing.Reference) references.ForEach(func(reference *plumbing.Reference) error { if reference.Name().String() == refBranchName { mainBranch = reference } else { if commit, err := repository.CommitObject(reference.Hash()); err == nil && time.Now().Sub(commit.Author.When).Seconds() < secondsSince { if reference.Name().IsBranch() { localBranches[reference.Name().String()] = reference } else { if strings.HasPrefix(reference.Name().String(), "refs/remotes/"+remoteName) && !strings.Contains(reference.Name().String(), "HEAD") { remoteBranches[reference.Name().String()] = reference } } } } return nil }) branchesToCheck := make(map[string]*plumbing.Reference) if mainBranch == nil { L.Fatalf("Unable to find main branch with name %s", refBranchName) } else { if viper.GetBool("include-local") { for k, v := range localBranches { branchesToCheck[k] = v } } if viper.GetBool("include-remote") { for k, v := range remoteBranches { branchesToCheck[k] = v } } for name, branch := range branchesToCheck { if branchFilter(branch) { commit, _ := repository.MergeBase(branch, mainBranch) if commit.Hash == branch.Hash() { if V && !strings.Contains(mainBranch.Name().Short(), branch.Name().Short()) { L.Printf( "%s is already merged. Consider deleting the branch", yellow(branch.Name().Short()), ) } delete(branchesToCheck, name) } } } } if V { L.Printf("Inspecting %d branch(es)...", len(branchesToCheck)) } conflictingWithMaster := make([]*plumbing.Reference, 0) rebasedWithMaster := make([]*plumbing.Reference, 0) wg := sizedwaitgroup.New(viper.GetInt("concurrency")) for _, reference := range branchesToCheck { wg.Add() go func(source *plumbing.Reference, target *plumbing.Reference) { resultC, errC := checkConflict(repository, source, target) select { case _ = <-errC: wg.Done() case res := <-resultC: if res > 0 && branchFilter(source) { fmt.Printf( "%v conflicts with %s [%d conflict(s)]\n", boolColor(target.Name().Short(), res == 1, color.FgYellow, color.FgRed), cyan(source.Name().Short()), res, ) conflicts = +1 conflictingWithMaster = append(conflictingWithMaster, target) } else { rebasedWithMaster = append(rebasedWithMaster, target) } } wg.Done() }(mainBranch, reference) } wg.Wait() referenceBranches := make([]*plumbing.Reference, 0) remainingValidBranches := make([]*plumbing.Reference, 0) fullList := make([]*plumbing.Reference, 0) for _, branch := range rebasedWithMaster { if branchFilter(branch) { referenceBranches = append(referenceBranches, branch) } else { remainingValidBranches = append(remainingValidBranches, branch) } } fullList = append(fullList, referenceBranches...) fullList = append(fullList, remainingValidBranches...) if V { masterConflicts := len(conflictingWithMaster) L.Printf( "Found %v branch(es) conflicting with %v", boolColor(masterConflicts, masterConflicts == 0), mainBranch.Name().Short(), ) L.Printf("Inspecting %d branch combinations...", (len(referenceBranches)*(len(fullList)-1))/2) } sadBranches := make(map[string]byte) for i, source := range referenceBranches { for _, target := range fullList[1+i:] { wg.Add() go func(source *plumbing.Reference, target *plumbing.Reference) { resultC, errC := checkConflict(repository, source, target) select { case _ = <-errC: wg.Done() case res := <-resultC: if res > 0 { L.Printf( "%v conflicts with %v [%d conflict(s)]\n", boolColor(source.Name().Short(), res == 1, color.FgYellow, color.FgRed), boolColor(target.Name().Short(), res == 1, color.FgYellow, color.FgRed), res, ) sadBranches[source.Name().String()] = 1 sadBranches[target.Name().String()] = 1 conflicts = +1 } wg.Done() } }(source, target) } } wg.Wait() if V { if conflicts == 0 { L.Println(green("No conflicting branches found")) } else { L.Printf("Found %s branch(es) conflicting with each other", boolColor(len(sadBranches), len(sadBranches) == 0)) } } }, }
View Source
var V bool
Functions ¶
Types ¶
type ConflictResult ¶
type ExRepository ¶
type ExRepository struct {
*git.Repository
}
func (*ExRepository) ExecuteCommand ¶
func (r *ExRepository) ExecuteCommand(arguments ...string) (string, error)
func (*ExRepository) LocalUserEmail ¶
func (r *ExRepository) LocalUserEmail() string
Click to show internal directories.
Click to hide internal directories.