Project branch naming logic
Added logic for naming git branches when creating/renaming branches.
BUG=chromium:980346
TEST=new and existing unit tests
Change-Id: Ia773c399d8445e63c3fd0ebbae9d3187e3425430
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/infra/go/+/1695711
Reviewed-by: Evan Hernandez <evanhernandez@chromium.org>
Commit-Queue: Jack Neus <jackneus@google.com>
Tested-by: Jack Neus <jackneus@google.com>
diff --git a/cmd/branch_util/branch.go b/cmd/branch_util/branch.go
new file mode 100644
index 0000000..b9cedc5
--- /dev/null
+++ b/cmd/branch_util/branch.go
@@ -0,0 +1,45 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+package main
+
+import (
+ "strings"
+
+ "go.chromium.org/chromiumos/infra/go/internal/git"
+ "go.chromium.org/chromiumos/infra/go/internal/repo"
+)
+
+// projectBranchName determines the git branch name for the project.
+func (c *createBranchRun) projectBranchName(branch string, project repo.Project, original string) string {
+ // If the project has only one checkout, then the base branch name is fine.
+ var checkouts []string
+ manifest := checkout.Manifest()
+ for _, proj := range manifest.Projects {
+ if proj.Name == project.Name {
+ checkouts = append(checkouts, proj.Name)
+ }
+ }
+
+ if len(checkouts) == 1 {
+ return branch
+ }
+
+ // Otherwise, the project name needs a suffix. We append its upstream or
+ // revision to distinguish it from other checkouts.
+ suffix := "-"
+ if project.Upstream != "" {
+ suffix += git.StripRefs(project.Upstream)
+ } else {
+ suffix += git.StripRefs(project.Revision)
+ }
+
+ // If the revision is itself a branch, we need to strip the old branch name
+ // from the suffix to keep naming consistent.
+ if original != "" {
+ if strings.HasPrefix(suffix, "-"+original+"-") {
+ suffix = strings.TrimPrefix(suffix, "-"+original)
+ }
+ }
+ return branch + suffix
+}
diff --git a/cmd/branch_util/branch_test.go b/cmd/branch_util/branch_test.go
new file mode 100644
index 0000000..88b3a0f
--- /dev/null
+++ b/cmd/branch_util/branch_test.go
@@ -0,0 +1,53 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+package main
+
+import (
+ "github.com/golang/mock/gomock"
+ mock_checkout "go.chromium.org/chromiumos/infra/go/internal/checkout/mock"
+ "go.chromium.org/chromiumos/infra/go/internal/repo"
+ "gotest.tools/assert"
+ "testing"
+)
+
+var testManifest = repo.Manifest{
+ Projects: []repo.Project{
+ {Path: "foo1/", Name: "foo", Revision: "100", Upstream: "refs/heads/factory-100"},
+ {Path: "foo2/", Name: "foo", Revision: "101"},
+ {Path: "bar/", Name: "bar"},
+ {Path: "baz1/", Name: "baz", Upstream: "refs/heads/oldbranch-factory-100"},
+ {Path: "baz2/", Name: "baz", Upstream: "refs/heads/oldbranch-factory-101"},
+ },
+}
+
+func TestProjectBranchName(t *testing.T) {
+ ctl := gomock.NewController(t)
+ defer ctl.Finish()
+
+ m := mock_checkout.NewMockCheckout(ctl)
+ checkout = m
+ c := &createBranchRun{}
+ m.EXPECT().
+ Manifest().
+ Return(testManifest).
+ AnyTimes()
+ assert.Equal(t, c.projectBranchName("mybranch", testManifest.Projects[0], ""), "mybranch-factory-100")
+ assert.Equal(t, c.projectBranchName("mybranch", testManifest.Projects[1], ""), "mybranch-101")
+ assert.Equal(t, c.projectBranchName("mybranch", testManifest.Projects[2], ""), "mybranch")
+}
+
+func TestProjectBranchName_withOriginal(t *testing.T) {
+ ctl := gomock.NewController(t)
+ defer ctl.Finish()
+
+ m := mock_checkout.NewMockCheckout(ctl)
+ checkout = m
+ c := &createBranchRun{}
+ m.EXPECT().
+ Manifest().
+ Return(testManifest).
+ AnyTimes()
+ assert.Equal(t, c.projectBranchName("mybranch", testManifest.Projects[3], "oldbranch"), "mybranch-factory-100")
+ assert.Equal(t, c.projectBranchName("mybranch", testManifest.Projects[4], "oldbranch"), "mybranch-factory-101")
+}
diff --git a/cmd/branch_util/create.go b/cmd/branch_util/create.go
index 79e29d7..1cb8c5e 100644
--- a/cmd/branch_util/create.go
+++ b/cmd/branch_util/create.go
@@ -207,5 +207,7 @@
"would like to proceed.", vinfo.VersionString())
}
+ // TODO(@jackneus): double check name with user via boolean CLI prompt
+
return 0
}
diff --git a/internal/repo/manifest.go b/internal/repo/manifest.go
index 829e7d2..d95007d 100644
--- a/internal/repo/manifest.go
+++ b/internal/repo/manifest.go
@@ -1,3 +1,6 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
package repo
import (
@@ -22,14 +25,16 @@
Includes []Include `xml:"include"`
Projects []Project `xml:"project"`
Remotes []Remote `xml:"remote"`
- Default []Default `xml:"default"`
+ Default Default `xml:"default"`
}
// Project is an element of a manifest containing a Gerrit project to source path definition.
type Project struct {
- Path string `xml:"path,attr"`
- Name string `xml:"name,attr"`
- Revision string `xml:"revision,attr"`
+ Path string `xml:"path,attr"`
+ Name string `xml:"name,attr"`
+ Revision string `xml:"revision,attr"`
+ Upstream string `xml:"upstream,attr"`
+ RemoteName string `xml:"remote,attr"`
}
// Include is a manifest element that imports another manifest file.
@@ -46,8 +51,17 @@
// Default is a manifest element that lists the default.
type Default struct {
- Remote string `xml:"remote,attr"`
- Revision string `xml:"revision,attr"`
+ RemoteName string `xml:"remote,attr"`
+ Revision string `xml:"revision,attr"`
+}
+
+func (m *Manifest) getRemoteByName(name string) *Remote {
+ for _, remote := range m.Remotes {
+ if remote.Name == name {
+ return &remote
+ }
+ }
+ return &Remote{}
}
// LoadManifestFromFile loads the manifest at the given file path into
@@ -64,6 +78,22 @@
if err = xml.Unmarshal(data, manifest); err != nil {
return nil, errors.Annotate(err, "failed to unmarshal %s", file).Err()
}
+ for i, project := range manifest.Projects {
+ // Set default remote on projects without an explicit remote
+ if project.RemoteName == "" {
+ project.RemoteName = manifest.Default.RemoteName
+ }
+ // Set default revision on projects without an explicit revision
+ if project.Revision == "" {
+ remote := manifest.getRemoteByName(project.RemoteName)
+ if remote.Revision == "" {
+ project.Revision = manifest.Default.Revision
+ } else {
+ project.Revision = remote.Revision
+ }
+ }
+ manifest.Projects[i] = project
+ }
results[file] = manifest
// Recursively fetch manifests listed in "include" elements.
diff --git a/internal/repo/manifest_test.go b/internal/repo/manifest_test.go
index 829d575..7c87973 100644
--- a/internal/repo/manifest_test.go
+++ b/internal/repo/manifest_test.go
@@ -1,3 +1,6 @@
+// Copyright 2019 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
package repo
import (
@@ -85,7 +88,9 @@
expected_results := make(map[string]*Manifest)
expected_results["test_data/foo.xml"] = &Manifest{
Projects: []Project{
- {"baz/", "baz", "123"},
+ {Path: "baz/", Name: "baz", Revision: "123", RemoteName: "chromium"},
+ {Path: "fiz/", Name: "fiz", Revision: "124", RemoteName: "chromeos"},
+ {Path: "buz/", Name: "buz", Revision: "125", RemoteName: "google"},
},
Includes: []Include{
{"bar.xml"},
@@ -93,7 +98,7 @@
}
expected_results["test_data/bar.xml"] = &Manifest{
Projects: []Project{
- {"baz/", "baz", ""},
+ {Path: "baz/", Name: "baz"},
},
}
@@ -117,9 +122,9 @@
func TestGetUniqueProject(t *testing.T) {
manifest := &Manifest{
Projects: []Project{
- {"foo-a/", "foo", ""},
- {"foo-b/", "foo", ""},
- {"bar/", "bar", ""},
+ {Path: "foo-a/", Name: "foo"},
+ {Path: "foo-b/", Name: "foo"},
+ {Path: "bar/", Name: "bar"},
},
}
diff --git a/internal/repo/test_data/foo.xml b/internal/repo/test_data/foo.xml
index ecf06c6..0b600ba 100644
--- a/internal/repo/test_data/foo.xml
+++ b/internal/repo/test_data/foo.xml
@@ -3,5 +3,8 @@
<include name="bar.xml" />
<default remote="chromeos" revision="123"/>
<remote fetch="https://chromium.org/remote" name="chromium"/>
- <project name="baz" path="baz/" revision="123"/>
+ <remote fetch="https://google.com/remote" name="google" revision="125"/>
+ <project name="baz" path="baz/" remote="chromium"/>
+ <project name="fiz" path="fiz/" revision="124" />
+ <project name="buz" path="buz/" remote="google" />
</manifest>
\ No newline at end of file
diff --git a/internal/repo/test_data/unspecified_revisions.xml b/internal/repo/test_data/unspecified_revisions.xml
new file mode 100644
index 0000000..df5fa79
--- /dev/null
+++ b/internal/repo/test_data/unspecified_revisions.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<manifest>
+ <include name="bar.xml" />
+ <default remote="chromeos" revision="123"/>
+ <remote fetch="https://chromium.org/remote" name="chromium"/>
+ <project name="baz" path="baz/"/>
+</manifest>
\ No newline at end of file
diff --git a/internal/repo_util/repo_util_test.go b/internal/repo_util/repo_util_test.go
index e8f89dd..1663146 100644
--- a/internal/repo_util/repo_util_test.go
+++ b/internal/repo_util/repo_util_test.go
@@ -175,9 +175,9 @@
}
expectedManifest := repo.Manifest{
Projects: []repo.Project{
- repo.Project{Path: "src/foo", Name: "foo"},
- repo.Project{Path: "src/bar", Name: "bar"},
- repo.Project{Path: "src/baz", Name: "baz"},
+ {Path: "src/foo", Name: "foo"},
+ {Path: "src/bar", Name: "bar"},
+ {Path: "src/baz", Name: "baz"},
},
}