In "Semantic import versioning by example" we briefly touched on the fact that there are options when it comes to deciding how to structure your git repository to support breaking changes, i.e. multiple major versions. In this guide we explore the two main options.
This example creates two modules that have the same package structure as the github.com/go-modules-by-example/goinfo
package in the "Semantic import versioning by
example" guide,
but each uses a different strategy when it comes to repository structure:
github.com/go-modules-by-example/goinfo-maj-branch
uses the major branch strategy (the strategy we used in "Semantic import versioning by example")github.com/go-modules-by-example/goinfo-maj-subdir
uses the major subdirectory strategy
For each strategy, we pick things up at the point where we are about to make the breaking change. We highlight the strategy-specific steps and any relevant points/differences.
We then make the breaking change in each module and use mod
to "upgrade" to
the next major version. The results are committed, pushed and tagged.
Finally, we create github.com/go-modules-by-example/multi-peopleprinter
which contains a main
package that
uses all major versions of the two modules to "test" that each strategy "behaves" in the same way.
Steps:
- Create a new branch for the current major version; code for the next major version will be pushed to
master
- Make the breaking change
- Run
mod
in the repository root - Commit the breaking change to
master
and push - Tag to release
$ git log --decorate -1
commit f924bb5b5bd458e272d5a20414a57c5f3aa80b5e (HEAD -> master, tag: v1.0.0, origin/master)
Author: myitcv <[email protected]>
Date: Mon May 13 15:09:38 2019 +0000
Initial commit of goinfo-maj-branch
$ git branch master.v1
$ git push -q origin master.v1
remote:
remote: Create a pull request for 'master.v1' on GitHub by visiting:
remote: https://github.com/go-modules-by-example/goinfo-maj-branch/pull/new/master.v1
remote:
Points to note:
- There are now two branches in this repo
v1
changes would be submitted as PRs that merge into thev1
branch- The tag for
v2.0.0
points to a commit that is only referenced by themaster
branch - The tag for
v1.0.0
points to a commit that is referenced by both branches
Steps:
- Create a subdirectory at the top level of the repo for the next major version (
v2
in this case) - Copy the code for the current major version into this subdirectory
- Make the breaking change in the subdirectory
- Run
mod
in the subdirectory - Commit the breaking change to
master
and push - Tag to release
$ git log --decorate -1
commit c54481ce0ccd862093e0c501fc3544da35ebc121 (HEAD -> master, tag: v1.0.0, origin/master)
Author: myitcv <[email protected]>
Date: Mon May 13 15:09:28 2019 +0000
Initial commit of goinfo-maj-subdir
$ mkdir v2
$ cp -rp $(git ls-tree --name-only HEAD) v2
Points to note:
- We only have one branch in this repo;
master
- All changes to all major versions are submitted as PRs that merge into
master
- All tags for all versions point to commits that are referenced by
master
Prepare the github.com/go-modules-by-example/multi-peopleprinter
module:
$ mkdir -p /home/gopher/scratchpad/$r
$ cd /home/gopher/scratchpad/$r
$ git init -q
$ git remote add origin https://github.com/go-modules-by-example/${r}
$ go mod init
go: creating new go.mod: module github.com/go-modules-by-example/multi-peopleprinter
Define the main
package:
// /home/gopher/scratchpad/multi-peopleprinter/main.go
package main
import (
"fmt"
"os"
"text/tabwriter"
majBranch "github.com/go-modules-by-example/goinfo-maj-branch/designers"
majBranch2 "github.com/go-modules-by-example/goinfo-maj-branch/v2/designers"
majSubdir "github.com/go-modules-by-example/goinfo-maj-subdir/designers"
majSubdir2 "github.com/go-modules-by-example/goinfo-maj-subdir/v2/designers"
)
func main() {
tw := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
w := func(format string, args ...interface{}) {
fmt.Fprintf(tw, format, args...)
}
w(".../goinfo-maj-branch/designers.Names():\t%v\n", majBranch.Names())
w(".../goinfo-maj-branch/v2/designers.FullNames():\t%v\n", majBranch2.FullNames())
w(".../goinfo-maj-subdir/designers.Names():\t%v\n", majSubdir.Names())
w(".../goinfo-maj-subdir/v2/designers.FullNames():\t%v\n", majSubdir2.FullNames())
tw.Flush()
}
Run this as a "test":
$ go run .
go: finding github.com/go-modules-by-example/goinfo-maj-branch v1.0.0
go: finding github.com/go-modules-by-example/goinfo-maj-subdir v1.0.0
go: finding github.com/go-modules-by-example/goinfo-maj-subdir/v2 v2.0.0
go: finding github.com/go-modules-by-example/goinfo-maj-branch/v2 v2.0.0
go: downloading github.com/go-modules-by-example/goinfo-maj-branch v1.0.0
go: downloading github.com/go-modules-by-example/goinfo-maj-branch/v2 v2.0.0
go: downloading github.com/go-modules-by-example/goinfo-maj-subdir v1.0.0
go: downloading github.com/go-modules-by-example/goinfo-maj-subdir/v2 v2.0.0
go: extracting github.com/go-modules-by-example/goinfo-maj-branch v1.0.0
go: extracting github.com/go-modules-by-example/goinfo-maj-branch/v2 v2.0.0
go: extracting github.com/go-modules-by-example/goinfo-maj-subdir v1.0.0
go: extracting github.com/go-modules-by-example/goinfo-maj-subdir/v2 v2.0.0
go: finding github.com/go-modules-by-example/goinfo v1.0.0
go: downloading github.com/go-modules-by-example/goinfo v1.0.0
go: extracting github.com/go-modules-by-example/goinfo v1.0.0
.../goinfo-maj-branch/designers.Names(): [Robert Griesemer Rob Pike Ken Thompson]
.../goinfo-maj-branch/v2/designers.FullNames(): [Robert Griesemer Rob Pike Ken Thompson]
.../goinfo-maj-subdir/designers.Names(): [Robert Griesemer Rob Pike Ken Thompson]
.../goinfo-maj-subdir/v2/designers.FullNames(): [Robert Griesemer Rob Pike Ken Thompson]
Commit, push and tag an initial release of github.com/go-modules-by-example/multi-peopleprinter
:
$ git add -A
$ git commit -q -am "Initial commit of $r"
$ git push -q origin
$ git tag v1.0.0
$ git push -q origin v1.0.0
go version go1.12.5 linux/amd64
github.com/marwan-at-work/mod v0.1.0