| // Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| import 'dart:async'; |
| import 'dart:io'; |
| |
| void main(List<String> args) { |
| // Use `runZonedGuarded` instead of try/catch, and do it here before anything |
| // else has been printed --- both because of |
| // https://github.com/dart-lang/sdk/issues/54911. |
| runZonedGuarded(() { |
| mainImpl(args); |
| }, (e, _) { |
| stderr.writeln("Error: $e"); |
| }); |
| } |
| |
| const String CSI = "\x1b["; |
| |
| Map<String, GitBranch> branches = {}; |
| |
| GitBranch getBranch(String name) { |
| GitBranch? result = branches[name]; |
| if (result == null) { |
| result = branches[name] = new GitBranch(name); |
| } |
| return result; |
| } |
| |
| void mainImpl(List<String> args) { |
| ProcessResult result = Process.runSync("git", |
| ["branch", "--list", "--format=%(refname:short)%09%(upstream:short)"], |
| runInShell: true); |
| result.stdout.split("\n").forEach(processGitBranchLine); |
| |
| result = |
| Process.runSync("git", ["branch", "--show-current"], runInShell: true); |
| String currentBranchName = result.stdout; |
| currentBranchName = currentBranchName.trim(); |
| GitBranch currentBranch = branches[currentBranchName]!; |
| Set<String> involvedBranchNames = {}; |
| currentBranch.collectSelfAndParentNames(involvedBranchNames); |
| currentBranch.collectSelfAndChildrenNames(involvedBranchNames); |
| |
| result = Process.runSync( |
| "git", |
| [ |
| "branch", |
| "--list", |
| "--format=%(refname:short)%09%(upstream:track)", |
| ...involvedBranchNames |
| ], |
| runInShell: true); |
| result.stdout.split("\n").forEach(processGitBranchTrackLine); |
| |
| int indentation = currentBranch.parent?.printSelfAndParentChain() ?? 0; |
| |
| currentBranch.printSelfAndChildren(indentation, color: true); |
| } |
| |
| void processGitBranchLine(String gitLine) { |
| if (gitLine.isEmpty) return; |
| int pos = gitLine.indexOf("\t"); |
| String thisName = gitLine.substring(0, pos); |
| String parentName = gitLine.substring(pos + 1).trim(); |
| GitBranch thisBranch = getBranch(thisName); |
| GitBranch parentBranch = getBranch(parentName); |
| parentBranch.registerChild(thisBranch); |
| } |
| |
| void processGitBranchTrackLine(String gitLine) { |
| if (gitLine.isEmpty) return; |
| int pos = gitLine.indexOf("\t"); |
| String thisName = gitLine.substring(0, pos); |
| String tracking = gitLine.substring(pos + 1).trim(); |
| GitBranch thisBranch = getBranch(thisName); |
| thisBranch.tracking = tracking; |
| } |
| |
| class GitBranch { |
| final String name; |
| GitBranch? parent; |
| final List<GitBranch> children = []; |
| String? tracking; |
| |
| GitBranch(this.name); |
| |
| void collectSelfAndChildrenNames(Set<String> names) { |
| names.add(name); |
| for (GitBranch child in children) { |
| child.collectSelfAndChildrenNames(names); |
| } |
| } |
| |
| void collectSelfAndParentNames(Set<String> names) { |
| parent?.collectSelfAndParentNames(names); |
| names.add(name); |
| } |
| |
| void printSelfAndChildren(int indention, {bool color = false}) { |
| _printLineWithIndention(indention, color: color); |
| for (GitBranch child in children) { |
| child.printSelfAndChildren(indention + 1); |
| } |
| } |
| |
| int printSelfAndParentChain() { |
| int indention = 0; |
| GitBranch? parent = this.parent; |
| if (parent != null) { |
| indention = parent.printSelfAndParentChain(); |
| } |
| _printLineWithIndention(indention); |
| |
| return indention + 1; |
| } |
| |
| void registerChild(GitBranch child) { |
| children.add(child); |
| child.parent = this; |
| } |
| |
| @override |
| String toString() { |
| return "GitBranch[$name, children = $children]"; |
| } |
| |
| void _printLineWithIndention(int indention, {bool color = false}) { |
| stdout.write("│ " * (indention)); |
| stdout.write("├── "); |
| if (color) { |
| stdout.write("${CSI}31m"); |
| } |
| stdout.write("$name"); |
| if (color) { |
| stdout.write("${CSI}0m"); |
| } |
| if (tracking != null) { |
| stdout.write(" $tracking"); |
| } |
| stdout.write("\n"); |
| } |
| } |