From f2b52957d171102112f13b64d9b9ccd280c370ba Mon Sep 17 00:00:00 2001 From: Florian Kinder Date: Mon, 6 Oct 2025 23:33:43 +0900 Subject: [PATCH] Fix duplicate edges in package resolution mode When using package resolution, the tool was creating one edge per import statement rather than one edge per package dependency. This resulted in duplicate edges when multiple files in a package imported the same target package. For example, if package A had 67 files that imported package B, it would create 67 identical edges from A to B in the graph. This fix deduplicates edges at the package level by tracking unique package-to-package relationships. It also prioritizes cycle edges over normal edges when both exist for the same package pair. Before: - Large codebases could have thousands of duplicate edges - Graph visualization was cluttered and misleading After: - Each package pair has exactly one edge - Cleaner, more accurate package dependency graphs --- internal/dot/package_resolution.go | 42 ++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/internal/dot/package_resolution.go b/internal/dot/package_resolution.go index ef389c5..168f1ef 100644 --- a/internal/dot/package_resolution.go +++ b/internal/dot/package_resolution.go @@ -42,6 +42,14 @@ func writeRelationshipsForPackageResolution(buf *bytes.Buffer, cfg *config.Confi edgeDef := ` "%s" -> "%s" [color="%s"];` + // Track package-to-package edges to avoid duplicates + type edge struct { + from string + to string + color string + } + edges := make(map[string]edge) + for _, pkg := range pkgs { if pkg.IsStub { continue @@ -57,19 +65,37 @@ func writeRelationshipsForPackageResolution(buf *bytes.Buffer, cfg *config.Confi if imp.Package.IsStub { continue } + + fromNode := pkgNodeName(pkg) + toNode := pkgNodeName(imp.Package) + edgeKey := fromNode + "->" + toNode + arrowColor := cfg.Palette.Base.ImportArrow if imp.InImportCycle { arrowColor = cfg.Palette.Cycle.ImportArrow } - buf.WriteString( - fmt.Sprintf( - edgeDef, - pkgNodeName(pkg), - pkgNodeName(imp.Package), - arrowColor.Hex(), - ), - ) + + // Only add edge if not already seen, or if this one is in a cycle (higher priority) + if existing, exists := edges[edgeKey]; !exists || (imp.InImportCycle && existing.color != arrowColor.Hex()) { + edges[edgeKey] = edge{ + from: fromNode, + to: toNode, + color: arrowColor.Hex(), + } + } } } } + + // Write all unique edges + for _, e := range edges { + buf.WriteString( + fmt.Sprintf( + edgeDef, + e.from, + e.to, + e.color, + ), + ) + } }