Beispiel #1
0
// DiffPackages takes two packages to produce the changes between them.
func DiffPackages(pkg1, pkg2 *Package) PackageChanges {
	diff := PackageChanges{
		Before: pkg1,
		After:  pkg2,
		Changes: map[ObjectCategory]map[string]Change{
			ObjectCategoryFunc:  {},
			ObjectCategoryType:  {},
			ObjectCategoryValue: {},
		},
	}

	for _, name := range util.SortedStringSet(util.MapKeys(pkg1.Funcs), util.MapKeys(pkg2.Funcs)) {
		Debugf("%q", name)
		diff.Changes[ObjectCategoryFunc][name] = FuncChange{
			Before: pkg1.Funcs[name],
			After:  pkg2.Funcs[name],
		}
	}

	for _, name := range util.SortedStringSet(util.MapKeys(pkg1.Types), util.MapKeys(pkg2.Types)) {
		type1 := pkg1.Types[name]
		type2 := pkg2.Types[name]

		diff.Changes[ObjectCategoryType][name] = TypeChange{
			Before: pkg1.Types[name],
			After:  pkg2.Types[name],
		}

		if type1 != nil && type2 != nil {
			for _, fname := range util.SortedStringSet(util.MapKeys(type1.Funcs), util.MapKeys(type2.Funcs)) {
				diff.Changes[ObjectCategoryFunc][fname] = FuncChange{
					Before: type1.Funcs[fname],
					After:  type2.Funcs[fname],
				}
			}

			for _, mname := range util.SortedStringSet(util.MapKeys(type1.Methods), util.MapKeys(type2.Methods)) {
				diff.Changes[ObjectCategoryFunc][name+"."+mname] = FuncChange{
					Before: type1.Methods[mname],
					After:  type2.Methods[mname],
				}
			}
		}
	}

	for _, name := range util.SortedStringSet(util.MapKeys(pkg1.Values), util.MapKeys(pkg2.Values)) {
		Debugf("%q", name)
		diff.Changes[ObjectCategoryValue][name] = ValueChange{
			Before: pkg1.Values[name],
			After:  pkg2.Values[name],
		}
	}

	return diff
}
Beispiel #2
0
func main() {
	var (
		flagAll     = flag.Bool("a", false, "show also unchanged APIs")
		flagRecurse = flag.Bool("r", false, `recurse into subdirectories (can be specified by "/..." suffix to the import path)`)
		flagDiff    = flag.Bool("d", false, "run diff on multi-line changes")
	)
	flag.Parse()
	flag.Usage = usage

	args := flag.Args()
	if len(args) < 1 {
		usage()
	}

	// TODO: support mercurial and other vcs
	vcsType := "git"

	revs := strings.SplitN(args[0], "..", 2)
	if len(revs) == 1 {
		revs = []string{revs[0] + "~1", revs[0]}
	} else if revs[1] == "" {
		revs = []string{revs[0], ""}
	}

	path := "."
	if len(args) >= 2 {
		path = args[1]

		if strings.HasSuffix(path, "...") {
			path = strings.TrimSuffix(path, "...")
			*flagRecurse = true
		}
	}

	dir1, err := gompatible.NewDirSpec(path, vcsType, revs[0])
	dieIf(err)

	pkgs1, err := gompatible.LoadDir(dir1, *flagRecurse)
	dieIf(err)

	dir2, err := gompatible.NewDirSpec(path, vcsType, revs[1])
	dieIf(err)

	pkgs2, err := gompatible.LoadDir(dir2, *flagRecurse)
	dieIf(err)

	diffs := map[string]gompatible.PackageChanges{}

	for _, name := range util.SortedStringSet(util.MapKeys(pkgs1), util.MapKeys(pkgs2)) {
		diffs[name] = gompatible.DiffPackages(
			pkgs1[name], pkgs2[name],
		)
	}

	var packageIndex int
	var hasBreaking bool
	for _, name := range util.SortedStringSet(util.MapKeys(diffs)) {
		diff := diffs[name]

		var headerShown bool
		printHeader := func() {
			if *flagRecurse == false {
				return
			}

			if !headerShown {
				// FIXME strictly not a package if inspecting local import
				if packageIndex > 0 {
					fmt.Println()
				}
				fmt.Printf("package %s\n", name)
				headerShown = true
				packageIndex++
			}
		}

		funcs := diff.Funcs()
		for _, name := range util.SortedStringSet(util.MapKeys(funcs)) {
			change := funcs[name]
			if *flagAll || change.Kind() != gompatible.ChangeUnchanged {
				printHeader()
				printChange(change, *flagDiff)
			}
			if change.Kind() == gompatible.ChangeBreaking || change.Kind() == gompatible.ChangeRemoved {
				hasBreaking = true
			}
		}

		types := diff.Types()
		for _, name := range util.SortedStringSet(util.MapKeys(types)) {
			change := types[name]
			if *flagAll || change.Kind() != gompatible.ChangeUnchanged {
				printHeader()
				printChange(change, *flagDiff)
			}
			if change.Kind() == gompatible.ChangeBreaking || change.Kind() == gompatible.ChangeRemoved {
				hasBreaking = true
			}
		}

		values := diff.Values()
		for _, name := range util.SortedStringSet(util.MapKeys(values)) {
			change := values[name]
			if *flagAll || change.Kind() != gompatible.ChangeUnchanged {
				printHeader()
				printChange(change, *flagDiff)
			}
			if change.Kind() == gompatible.ChangeBreaking || change.Kind() == gompatible.ChangeRemoved {
				hasBreaking = true
			}
		}
	}

	if hasBreaking {
		os.Exit(1)
	}
}