Skip to content

Commit

Permalink
feat: git change detection works on any commit
Browse files Browse the repository at this point in the history
  • Loading branch information
snakster committed May 10, 2024
1 parent 17fd930 commit 3c0d658
Show file tree
Hide file tree
Showing 7 changed files with 915 additions and 62 deletions.
12 changes: 3 additions & 9 deletions cmd/terramate/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -766,22 +766,16 @@ func (c *cli) setupGit() {
return
}

remoteCheckFailed := false

if err := c.prj.checkDefaultRemote(); err != nil {
if c.prj.git.remoteConfigured {
fatalWithDetails(err, "checking git default remote")
} else {
remoteCheckFailed = true
}
}

if c.parsedArgs.GitChangeBase != "" {
c.prj.baseRef = c.parsedArgs.GitChangeBase
} else if remoteCheckFailed {
c.prj.baseRef = c.prj.defaultLocalBaseRef()
c.prj.baseRev = c.parsedArgs.GitChangeBase
} else {
c.prj.baseRef = c.prj.defaultBaseRef()
c.prj.baseRev = c.prj.selectChangeBase()
}
}

Expand Down Expand Up @@ -2248,7 +2242,7 @@ func (c *cli) gitSafeguardRemoteEnabled() bool {
func (c *cli) wd() string { return c.prj.wd }
func (c *cli) rootdir() string { return c.prj.rootdir }
func (c *cli) cfg() *config.Root { return &c.prj.root }
func (c *cli) baseRef() string { return c.prj.baseRef }
func (c *cli) baseRef() string { return c.prj.baseRev }
func (c *cli) stackManager() *stack.Manager { return c.prj.stackManager }
func (c *cli) rootNode() hcl.Config { return c.prj.root.Tree().Node }
func (c *cli) cred() credential { return c.cloud.client.Credential.(credential) }
Expand Down
90 changes: 43 additions & 47 deletions cmd/terramate/cli/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ type project struct {
wd string
isRepo bool
root config.Root
baseRef string
baseRev string
normalizedRepo string
stackManager *stack.Manager

Expand Down Expand Up @@ -63,21 +63,6 @@ func (p *project) prettyRepo() string {
return p.normalizedRepo
}

func (p *project) localDefaultBranchCommit() string {
if p.git.localDefaultBranchCommit != "" {
return p.git.localDefaultBranchCommit
}
gitcfg := p.gitcfg()
refName := gitcfg.DefaultRemote + "/" + gitcfg.DefaultBranch
val, err := p.git.wrapper.RevParse(refName)
if err != nil {
fatalWithDetails(err, "unable to git rev-parse")
}

p.git.localDefaultBranchCommit = val
return val
}

func (p *project) isGitFeaturesEnabled() bool {
return p.isRepo && p.hasCommit()
}
Expand Down Expand Up @@ -126,47 +111,58 @@ func (p *project) remoteDefaultCommit() string {
return p.git.remoteDefaultBranchCommit
}

func (p *project) isDefaultBranch() bool {
git := p.gitcfg()
branch, err := p.git.wrapper.CurrentBranch()
if err != nil {
// WHY?
// The current branch name (the symbolic-ref of the HEAD) is not always
// available, in this case we naively check if HEAD == local origin/main.
// This case usually happens in the git setup of CIs.
return p.localDefaultBranchCommit() == p.headCommit()
}
// selectChangeBase returns the revision used for change comparison based on the current Git state.
func (p *project) selectChangeBase() string {
gitcfg := p.gitcfg()
gw := p.git.wrapper

return branch == git.DefaultBranch
}
// Try using remote default branch first
defaultBranchRev, _ := gw.RevParse(gitcfg.DefaultRemote + "/" + gitcfg.DefaultBranch)
if defaultBranchRev == "" {
// Fall back to local default branch
defaultBranchRev, _ = gw.RevParse(gitcfg.DefaultBranch)

// defaultBaseRef returns the baseRef for the current git environment.
func (p *project) defaultBaseRef() string {
if p.isDefaultBranch() &&
p.remoteDefaultCommit() == p.headCommit() {
_, err := p.git.wrapper.RevParse(defaultBranchBaseRef)
if err == nil {
if defaultBranchRev == "" {
// There's no default branch available, so we can't look for a common parent with it.
return defaultBranchBaseRef
}
}
return p.defaultBranchRef()
}

// defaultLocalBaseRef returns the baseRef in case there's no remote setup.
func (p *project) defaultLocalBaseRef() string {
git := p.gitcfg()
if p.isDefaultBranch() {
_, err := p.git.wrapper.RevParse(defaultBranchBaseRef)
if err == nil {
branch, _ := gw.CurrentBranch()

// Either we are on a branch or at a detached HEAD.
if branch != "" {
if branch == gitcfg.DefaultBranch {
// We are at the tip of the default branch -> latest default commit.
return defaultBranchBaseRef
}

// Fallthrough to common parent if not on default branch
} else {
headRev, _ := gw.RevParse("HEAD")
isDetachedDefaultBranchTip := headRev == defaultBranchRev
if isDetachedDefaultBranchTip {
// We are at the latest commit of the default branch.
return defaultBranchBaseRef
}

isDefaultBranchAncestor, _ := gw.IsFirstParentAncestor("HEAD", defaultBranchRev)
if isDefaultBranchAncestor {
// We are at an older commit of the default branch.
return defaultBranchBaseRef
}

// Fallthrough to common parent if not at commit of default branch
}

commonParentWithDefaultBranch, _ := gw.FindNearestCommonParent(defaultBranchRev, "HEAD")
if commonParentWithDefaultBranch != "" {
// We have a nearest common parent with the default branch. Similar to the historic merge base.
return commonParentWithDefaultBranch
}
return git.DefaultBranch
}

func (p project) defaultBranchRef() string {
git := p.gitcfg()
return git.DefaultRemote + "/" + git.DefaultBranch
// Fall back to default. Should never happen unless running on an isolated commit.
return defaultBranchBaseRef
}

func (p *project) setDefaults() error {
Expand Down
Loading

0 comments on commit 3c0d658

Please sign in to comment.