chore: adopt Dart 3.9 toolchain — honest floor + tall-style reformat (T-353)
Raise the declared minimums in pubspec.yaml to what our deps already require: Flutter >=3.35.0 / Dart >=3.9.0 (was 3.19.0 / 3.5.0). alchemist 0.12 needs Flutter 3.32; Dart 3.9 first ships in Flutter 3.35, so 3.35 is the binding floor. Pin the exact build toolchain in .fvmrc (Flutter 3.44.1). Moving to the Dart 3.9 language level switches `dart format` to the new "tall" style and enables two new lints. This commit is the resulting mechanical churn, isolated from any behaviour change: - whole-tree `dart format` reformat (tall style) - `dart fix` for unnecessary_underscores + use_null_aware_elements No runtime behaviour change; `make test` green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+13
-26
@@ -61,13 +61,7 @@ class GitClient {
|
||||
return GitStatus(branch: branch, upstream: upstream, ahead: ahead, behind: behind, entries: const []);
|
||||
}
|
||||
|
||||
return GitStatus(
|
||||
branch: branch,
|
||||
upstream: upstream,
|
||||
ahead: ahead,
|
||||
behind: behind,
|
||||
entries: parsePorcelainV1(result.stdout as String),
|
||||
);
|
||||
return GitStatus(branch: branch, upstream: upstream, ahead: ahead, behind: behind, entries: parsePorcelainV1(result.stdout as String));
|
||||
}
|
||||
|
||||
Future<List<GitDiff>> diff({bool staged = false, List<String> paths = const []}) async {
|
||||
@@ -83,12 +77,7 @@ class GitClient {
|
||||
}
|
||||
|
||||
Future<List<GitLogEntry>> log({int count = 20}) async {
|
||||
final r = await _run([
|
||||
'log',
|
||||
'--format=%H%x00%h%x00%s%x00%an%x00%aI%x00%b%x01',
|
||||
'-n',
|
||||
'$count',
|
||||
]);
|
||||
final r = await _run(['log', '--format=%H%x00%h%x00%s%x00%an%x00%aI%x00%b%x01', '-n', '$count']);
|
||||
if (r.exitCode != 0) return const [];
|
||||
return parseLog(r.stdout as String);
|
||||
}
|
||||
@@ -115,11 +104,7 @@ class GitClient {
|
||||
/// Resolve a path to its git repo root. Returns null if not a git repo.
|
||||
Future<String?> repoRoot(String path) async {
|
||||
try {
|
||||
final r = await Process.run(
|
||||
toolchain.git,
|
||||
['rev-parse', '--show-toplevel'],
|
||||
workingDirectory: path,
|
||||
);
|
||||
final r = await Process.run(toolchain.git, ['rev-parse', '--show-toplevel'], workingDirectory: path);
|
||||
if (r.exitCode != 0) return null;
|
||||
final out = (r.stdout as String).trim();
|
||||
return out.isEmpty ? null : out;
|
||||
@@ -252,14 +237,16 @@ List<GitLogEntry> parseLog(String output) {
|
||||
if (trimmed.isEmpty) continue;
|
||||
final fields = trimmed.split('\x00');
|
||||
if (fields.length < 5) continue;
|
||||
entries.add(GitLogEntry(
|
||||
hash: fields[0],
|
||||
shortHash: fields[1],
|
||||
subject: fields[2],
|
||||
author: fields[3],
|
||||
date: fields[4],
|
||||
body: fields.length > 5 ? fields[5].trim() : '',
|
||||
));
|
||||
entries.add(
|
||||
GitLogEntry(
|
||||
hash: fields[0],
|
||||
shortHash: fields[1],
|
||||
subject: fields[2],
|
||||
author: fields[3],
|
||||
date: fields[4],
|
||||
body: fields.length > 5 ? fields[5].trim() : '',
|
||||
),
|
||||
);
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
+36
-88
@@ -12,12 +12,7 @@ import 'operations.dart' show gitBin;
|
||||
enum DiffLineKind { context, addition, removal, header }
|
||||
|
||||
class DiffLine {
|
||||
const DiffLine({
|
||||
required this.kind,
|
||||
required this.text,
|
||||
this.oldLineNo,
|
||||
this.newLineNo,
|
||||
});
|
||||
const DiffLine({required this.kind, required this.text, this.oldLineNo, this.newLineNo});
|
||||
|
||||
final DiffLineKind kind;
|
||||
final String text;
|
||||
@@ -25,22 +20,15 @@ class DiffLine {
|
||||
final int? newLineNo;
|
||||
|
||||
Map<String, Object?> toJson() => {
|
||||
'kind': kind.name,
|
||||
'text': text,
|
||||
if (oldLineNo != null) 'oldLineNo': oldLineNo,
|
||||
if (newLineNo != null) 'newLineNo': newLineNo,
|
||||
};
|
||||
'kind': kind.name,
|
||||
'text': text,
|
||||
if (oldLineNo != null) 'oldLineNo': oldLineNo,
|
||||
if (newLineNo != null) 'newLineNo': newLineNo,
|
||||
};
|
||||
}
|
||||
|
||||
class GitHunk {
|
||||
const GitHunk({
|
||||
required this.header,
|
||||
required this.oldStart,
|
||||
required this.oldCount,
|
||||
required this.newStart,
|
||||
required this.newCount,
|
||||
required this.lines,
|
||||
});
|
||||
const GitHunk({required this.header, required this.oldStart, required this.oldCount, required this.newStart, required this.newCount, required this.lines});
|
||||
|
||||
final String header;
|
||||
final int oldStart;
|
||||
@@ -70,15 +58,15 @@ class GitHunk {
|
||||
}
|
||||
|
||||
Map<String, Object?> toJson() => {
|
||||
'header': header,
|
||||
'oldStart': oldStart,
|
||||
'oldCount': oldCount,
|
||||
'newStart': newStart,
|
||||
'newCount': newCount,
|
||||
'additions': additions,
|
||||
'removals': removals,
|
||||
'lines': [for (final l in lines) l.toJson()],
|
||||
};
|
||||
'header': header,
|
||||
'oldStart': oldStart,
|
||||
'oldCount': oldCount,
|
||||
'newStart': newStart,
|
||||
'newCount': newCount,
|
||||
'additions': additions,
|
||||
'removals': removals,
|
||||
'lines': [for (final l in lines) l.toJson()],
|
||||
};
|
||||
}
|
||||
|
||||
class GitDiff {
|
||||
@@ -104,37 +92,29 @@ class GitDiff {
|
||||
int get removals => hunks.fold(0, (s, h) => s + h.removals);
|
||||
|
||||
Map<String, Object?> toJson() => {
|
||||
'path': path,
|
||||
if (oldPath != null) 'oldPath': oldPath,
|
||||
'binary': isBinary,
|
||||
'new': isNew,
|
||||
'deleted': isDeleted,
|
||||
'renamed': isRenamed,
|
||||
'additions': additions,
|
||||
'removals': removals,
|
||||
'hunks': [for (final h in hunks) h.toJson()],
|
||||
};
|
||||
'path': path,
|
||||
if (oldPath != null) 'oldPath': oldPath,
|
||||
'binary': isBinary,
|
||||
'new': isNew,
|
||||
'deleted': isDeleted,
|
||||
'renamed': isRenamed,
|
||||
'additions': additions,
|
||||
'removals': removals,
|
||||
'hunks': [for (final h in hunks) h.toJson()],
|
||||
};
|
||||
}
|
||||
|
||||
/// Run `git diff` and parse the result.
|
||||
///
|
||||
/// [staged] controls `--cached`. [paths] narrows to specific files.
|
||||
Future<List<GitDiff>> gitDiff(
|
||||
Directory workDir, {
|
||||
bool staged = false,
|
||||
List<String> paths = const [],
|
||||
}) async {
|
||||
Future<List<GitDiff>> gitDiff(Directory workDir, {bool staged = false, List<String> paths = const []}) async {
|
||||
final args = ['diff', '--unified=3'];
|
||||
if (staged) args.add('--cached');
|
||||
if (paths.isNotEmpty) {
|
||||
args.add('--');
|
||||
args.addAll(paths);
|
||||
}
|
||||
final result = await Process.run(
|
||||
gitBin,
|
||||
args,
|
||||
workingDirectory: workDir.path,
|
||||
);
|
||||
final result = await Process.run(gitBin, args, workingDirectory: workDir.path);
|
||||
if (result.exitCode != 0) return const [];
|
||||
return parseDiffOutput(result.stdout as String);
|
||||
}
|
||||
@@ -210,15 +190,9 @@ List<GitDiff> parseDiffOutput(String output) {
|
||||
}
|
||||
}
|
||||
|
||||
diffs.add(GitDiff(
|
||||
path: path,
|
||||
oldPath: isRenamed ? oldPath : null,
|
||||
hunks: hunks,
|
||||
isBinary: isBinary,
|
||||
isNew: isNew,
|
||||
isDeleted: isDeleted,
|
||||
isRenamed: isRenamed,
|
||||
));
|
||||
diffs.add(
|
||||
GitDiff(path: path, oldPath: isRenamed ? oldPath : null, hunks: hunks, isBinary: isBinary, isNew: isNew, isDeleted: isDeleted, isRenamed: isRenamed),
|
||||
);
|
||||
}
|
||||
return diffs;
|
||||
}
|
||||
@@ -251,48 +225,22 @@ _HunkParseResult? _parseHunk(List<String> lines, int start) {
|
||||
if (line.startsWith('diff --git ') || line.startsWith('@@')) break;
|
||||
|
||||
if (line.startsWith('+')) {
|
||||
hunkLines.add(DiffLine(
|
||||
kind: DiffLineKind.addition,
|
||||
text: line.substring(1),
|
||||
newLineNo: newLine,
|
||||
));
|
||||
hunkLines.add(DiffLine(kind: DiffLineKind.addition, text: line.substring(1), newLineNo: newLine));
|
||||
newLine++;
|
||||
} else if (line.startsWith('-')) {
|
||||
hunkLines.add(DiffLine(
|
||||
kind: DiffLineKind.removal,
|
||||
text: line.substring(1),
|
||||
oldLineNo: oldLine,
|
||||
));
|
||||
hunkLines.add(DiffLine(kind: DiffLineKind.removal, text: line.substring(1), oldLineNo: oldLine));
|
||||
oldLine++;
|
||||
} else if (line.startsWith(' ')) {
|
||||
hunkLines.add(DiffLine(
|
||||
kind: DiffLineKind.context,
|
||||
text: line.substring(1),
|
||||
oldLineNo: oldLine,
|
||||
newLineNo: newLine,
|
||||
));
|
||||
hunkLines.add(DiffLine(kind: DiffLineKind.context, text: line.substring(1), oldLineNo: oldLine, newLineNo: newLine));
|
||||
oldLine++;
|
||||
newLine++;
|
||||
} else if (line == r'\ No newline at end of file') {
|
||||
hunkLines.add(DiffLine(
|
||||
kind: DiffLineKind.header,
|
||||
text: line,
|
||||
));
|
||||
hunkLines.add(DiffLine(kind: DiffLineKind.header, text: line));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
return _HunkParseResult(
|
||||
GitHunk(
|
||||
header: header,
|
||||
oldStart: oldStart,
|
||||
oldCount: oldCount,
|
||||
newStart: newStart,
|
||||
newCount: newCount,
|
||||
lines: hunkLines,
|
||||
),
|
||||
i,
|
||||
);
|
||||
return _HunkParseResult(GitHunk(header: header, oldStart: oldStart, oldCount: oldCount, newStart: newStart, newCount: newCount, lines: hunkLines), i);
|
||||
}
|
||||
|
||||
+33
-103
@@ -53,14 +53,7 @@ void validateGitRef(String? value, {required String kind}) {
|
||||
}
|
||||
|
||||
class GitLogEntry {
|
||||
const GitLogEntry({
|
||||
required this.hash,
|
||||
required this.shortHash,
|
||||
required this.subject,
|
||||
required this.author,
|
||||
required this.date,
|
||||
this.body = '',
|
||||
});
|
||||
const GitLogEntry({required this.hash, required this.shortHash, required this.subject, required this.author, required this.date, this.body = ''});
|
||||
|
||||
final String hash;
|
||||
final String shortHash;
|
||||
@@ -70,13 +63,13 @@ class GitLogEntry {
|
||||
final String body;
|
||||
|
||||
Map<String, Object?> toJson() => {
|
||||
'hash': hash,
|
||||
'shortHash': shortHash,
|
||||
'subject': subject,
|
||||
'author': author,
|
||||
'date': date,
|
||||
if (body.isNotEmpty) 'body': body,
|
||||
};
|
||||
'hash': hash,
|
||||
'shortHash': shortHash,
|
||||
'subject': subject,
|
||||
'author': author,
|
||||
'date': date,
|
||||
if (body.isNotEmpty) 'body': body,
|
||||
};
|
||||
}
|
||||
|
||||
/// Stage files. Empty [paths] means stage all (`git add -A`).
|
||||
@@ -120,22 +113,14 @@ Future<void> gitUnstageHunk(Directory workDir, String patch) async {
|
||||
/// Discard unstaged changes for [paths]. Uses `git checkout -- <paths>`.
|
||||
Future<void> gitDiscard(Directory workDir, List<String> paths) async {
|
||||
if (paths.isEmpty) return;
|
||||
final r = await Process.run(
|
||||
gitBin,
|
||||
['checkout', '--', ...paths],
|
||||
workingDirectory: workDir.path,
|
||||
);
|
||||
final r = await Process.run(gitBin, ['checkout', '--', ...paths], workingDirectory: workDir.path);
|
||||
if (r.exitCode != 0) {
|
||||
throw GitException('git checkout failed', stderr: r.stderr as String);
|
||||
}
|
||||
}
|
||||
|
||||
/// Commit staged changes.
|
||||
Future<String> gitCommit(
|
||||
Directory workDir,
|
||||
String message, {
|
||||
bool amend = false,
|
||||
}) async {
|
||||
Future<String> gitCommit(Directory workDir, String message, {bool amend = false}) async {
|
||||
final args = ['commit', '-m', message];
|
||||
if (amend) args.add('--amend');
|
||||
final r = await Process.run(gitBin, args, workingDirectory: workDir.path);
|
||||
@@ -143,20 +128,12 @@ Future<String> gitCommit(
|
||||
throw GitException('git commit failed', stderr: r.stderr as String);
|
||||
}
|
||||
// Return the new commit hash.
|
||||
final hashResult = await Process.run(
|
||||
gitBin,
|
||||
['rev-parse', 'HEAD'],
|
||||
workingDirectory: workDir.path,
|
||||
);
|
||||
final hashResult = await Process.run(gitBin, ['rev-parse', 'HEAD'], workingDirectory: workDir.path);
|
||||
return (hashResult.stdout as String).trim();
|
||||
}
|
||||
|
||||
/// Stash working changes.
|
||||
Future<void> gitStash(
|
||||
Directory workDir, {
|
||||
String? message,
|
||||
bool includeUntracked = false,
|
||||
}) async {
|
||||
Future<void> gitStash(Directory workDir, {String? message, bool includeUntracked = false}) async {
|
||||
final args = ['stash', 'push'];
|
||||
if (message != null) {
|
||||
args.addAll(['-m', message]);
|
||||
@@ -170,42 +147,22 @@ Future<void> gitStash(
|
||||
|
||||
/// Pop the top stash entry.
|
||||
Future<void> gitStashPop(Directory workDir) async {
|
||||
final r = await Process.run(
|
||||
gitBin,
|
||||
['stash', 'pop'],
|
||||
workingDirectory: workDir.path,
|
||||
);
|
||||
final r = await Process.run(gitBin, ['stash', 'pop'], workingDirectory: workDir.path);
|
||||
if (r.exitCode != 0) {
|
||||
throw GitException('git stash pop failed', stderr: r.stderr as String);
|
||||
}
|
||||
}
|
||||
|
||||
/// Git log. Returns the most recent [count] entries.
|
||||
Future<List<GitLogEntry>> gitLog(
|
||||
Directory workDir, {
|
||||
int count = 20,
|
||||
}) async {
|
||||
final r = await Process.run(
|
||||
gitBin,
|
||||
[
|
||||
'log',
|
||||
'--format=%H%x00%h%x00%s%x00%an%x00%aI%x00%b%x01',
|
||||
'-n',
|
||||
'$count',
|
||||
],
|
||||
workingDirectory: workDir.path,
|
||||
);
|
||||
Future<List<GitLogEntry>> gitLog(Directory workDir, {int count = 20}) async {
|
||||
final r = await Process.run(gitBin, ['log', '--format=%H%x00%h%x00%s%x00%an%x00%aI%x00%b%x01', '-n', '$count'], workingDirectory: workDir.path);
|
||||
if (r.exitCode != 0) return const [];
|
||||
return _parseLog(r.stdout as String);
|
||||
}
|
||||
|
||||
/// Pull from remote.
|
||||
Future<String> gitPull(Directory workDir) async {
|
||||
final r = await Process.run(
|
||||
gitBin,
|
||||
['pull'],
|
||||
workingDirectory: workDir.path,
|
||||
);
|
||||
final r = await Process.run(gitBin, ['pull'], workingDirectory: workDir.path);
|
||||
if (r.exitCode != 0) {
|
||||
throw GitException('git pull failed', stderr: r.stderr as String);
|
||||
}
|
||||
@@ -213,12 +170,7 @@ Future<String> gitPull(Directory workDir) async {
|
||||
}
|
||||
|
||||
/// Push to remote.
|
||||
Future<String> gitPush(
|
||||
Directory workDir, {
|
||||
String? remote,
|
||||
String? branch,
|
||||
bool setUpstream = false,
|
||||
}) async {
|
||||
Future<String> gitPush(Directory workDir, {String? remote, String? branch, bool setUpstream = false}) async {
|
||||
if (remote != null) validateGitRef(remote, kind: 'remote');
|
||||
if (branch != null) validateGitRef(branch, kind: 'branch');
|
||||
final args = ['push'];
|
||||
@@ -238,11 +190,7 @@ Future<String> gitPush(
|
||||
|
||||
/// List local branches. Returns (name, isCurrent) pairs.
|
||||
Future<List<({String name, bool current})>> gitBranches(Directory workDir) async {
|
||||
final r = await Process.run(
|
||||
gitBin,
|
||||
['branch', '--format=%(refname:short)|%(HEAD)'],
|
||||
workingDirectory: workDir.path,
|
||||
);
|
||||
final r = await Process.run(gitBin, ['branch', '--format=%(refname:short)|%(HEAD)'], workingDirectory: workDir.path);
|
||||
if (r.exitCode != 0) return const [];
|
||||
final out = <({String name, bool current})>[];
|
||||
for (final line in (r.stdout as String).split('\n')) {
|
||||
@@ -265,11 +213,7 @@ Future<List<({String name, bool current})>> gitBranches(Directory workDir) async
|
||||
/// argv-injection defence here. Use `gitSwitch` if/when we adopt it.
|
||||
Future<void> gitCheckout(Directory workDir, String branch) async {
|
||||
validateGitRef(branch, kind: 'branch');
|
||||
final r = await Process.run(
|
||||
gitBin,
|
||||
['checkout', branch],
|
||||
workingDirectory: workDir.path,
|
||||
);
|
||||
final r = await Process.run(gitBin, ['checkout', branch], workingDirectory: workDir.path);
|
||||
if (r.exitCode != 0) {
|
||||
throw GitException('git checkout failed', stderr: r.stderr as String);
|
||||
}
|
||||
@@ -277,11 +221,7 @@ Future<void> gitCheckout(Directory workDir, String branch) async {
|
||||
|
||||
/// Get the current branch name.
|
||||
Future<String?> gitCurrentBranch(Directory workDir) async {
|
||||
final r = await Process.run(
|
||||
gitBin,
|
||||
['symbolic-ref', '--short', 'HEAD'],
|
||||
workingDirectory: workDir.path,
|
||||
);
|
||||
final r = await Process.run(gitBin, ['symbolic-ref', '--short', 'HEAD'], workingDirectory: workDir.path);
|
||||
if (r.exitCode != 0) return null;
|
||||
return (r.stdout as String).trim();
|
||||
}
|
||||
@@ -297,43 +237,33 @@ List<GitLogEntry> _parseLog(String output) {
|
||||
if (trimmed.isEmpty) continue;
|
||||
final fields = trimmed.split('\x00');
|
||||
if (fields.length < 5) continue;
|
||||
entries.add(GitLogEntry(
|
||||
hash: fields[0],
|
||||
shortHash: fields[1],
|
||||
subject: fields[2],
|
||||
author: fields[3],
|
||||
date: fields[4],
|
||||
body: fields.length > 5 ? fields[5].trim() : '',
|
||||
));
|
||||
entries.add(
|
||||
GitLogEntry(
|
||||
hash: fields[0],
|
||||
shortHash: fields[1],
|
||||
subject: fields[2],
|
||||
author: fields[3],
|
||||
date: fields[4],
|
||||
body: fields.length > 5 ? fields[5].trim() : '',
|
||||
),
|
||||
);
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
Future<void> _applyPatch(
|
||||
Directory workDir,
|
||||
String patch, {
|
||||
bool cached = false,
|
||||
bool reverse = false,
|
||||
}) async {
|
||||
Future<void> _applyPatch(Directory workDir, String patch, {bool cached = false, bool reverse = false}) async {
|
||||
final args = ['apply'];
|
||||
if (cached) args.add('--cached');
|
||||
if (reverse) args.add('--reverse');
|
||||
args.add('--unidiff-zero');
|
||||
args.add('-');
|
||||
|
||||
final proc = await Process.start(
|
||||
'git',
|
||||
args,
|
||||
workingDirectory: workDir.path,
|
||||
);
|
||||
final proc = await Process.start('git', args, workingDirectory: workDir.path);
|
||||
proc.stdin.write(patch);
|
||||
await proc.stdin.close();
|
||||
final exitCode = await proc.exitCode;
|
||||
if (exitCode != 0) {
|
||||
final stderr = await proc.stderr.transform(const SystemEncoding().decoder).join();
|
||||
throw GitException(
|
||||
'git apply failed',
|
||||
stderr: stderr,
|
||||
);
|
||||
throw GitException('git apply failed', stderr: stderr);
|
||||
}
|
||||
}
|
||||
|
||||
+38
-84
@@ -9,34 +9,12 @@ import 'dart:io';
|
||||
|
||||
import 'operations.dart' show gitBin;
|
||||
|
||||
enum GitFileState {
|
||||
added,
|
||||
modified,
|
||||
deleted,
|
||||
renamed,
|
||||
copied,
|
||||
untracked,
|
||||
ignored,
|
||||
}
|
||||
enum GitFileState { added, modified, deleted, renamed, copied, untracked, ignored }
|
||||
|
||||
enum GitConflictType {
|
||||
bothModified,
|
||||
bothAdded,
|
||||
addedByUs,
|
||||
addedByThem,
|
||||
deletedByUs,
|
||||
deletedByThem,
|
||||
bothDeleted,
|
||||
}
|
||||
enum GitConflictType { bothModified, bothAdded, addedByUs, addedByThem, deletedByUs, deletedByThem, bothDeleted }
|
||||
|
||||
class GitFileStatus {
|
||||
const GitFileStatus({
|
||||
required this.path,
|
||||
required this.indexState,
|
||||
required this.workTreeState,
|
||||
this.origPath,
|
||||
this.conflictType,
|
||||
});
|
||||
const GitFileStatus({required this.path, required this.indexState, required this.workTreeState, this.origPath, this.conflictType});
|
||||
|
||||
final String path;
|
||||
final GitFileState? indexState;
|
||||
@@ -50,26 +28,20 @@ class GitFileStatus {
|
||||
bool get isConflicted => conflictType != null;
|
||||
|
||||
Map<String, Object?> toJson() => {
|
||||
'path': path,
|
||||
if (indexState != null) 'indexState': indexState!.name,
|
||||
if (workTreeState != null) 'workTreeState': workTreeState!.name,
|
||||
if (origPath != null) 'origPath': origPath,
|
||||
if (conflictType != null) 'conflictType': conflictType!.name,
|
||||
'staged': isStaged,
|
||||
'unstaged': isUnstaged,
|
||||
'untracked': isUntracked,
|
||||
'conflicted': isConflicted,
|
||||
};
|
||||
'path': path,
|
||||
if (indexState != null) 'indexState': indexState!.name,
|
||||
if (workTreeState != null) 'workTreeState': workTreeState!.name,
|
||||
if (origPath != null) 'origPath': origPath,
|
||||
if (conflictType != null) 'conflictType': conflictType!.name,
|
||||
'staged': isStaged,
|
||||
'unstaged': isUnstaged,
|
||||
'untracked': isUntracked,
|
||||
'conflicted': isConflicted,
|
||||
};
|
||||
}
|
||||
|
||||
class GitStatus {
|
||||
const GitStatus({
|
||||
required this.branch,
|
||||
required this.entries,
|
||||
this.upstream,
|
||||
this.ahead = 0,
|
||||
this.behind = 0,
|
||||
});
|
||||
const GitStatus({required this.branch, required this.entries, this.upstream, this.ahead = 0, this.behind = 0});
|
||||
|
||||
final String? branch;
|
||||
final String? upstream;
|
||||
@@ -86,28 +58,24 @@ class GitStatus {
|
||||
bool get hasConflicts => entries.any((e) => e.isConflicted);
|
||||
|
||||
Map<String, Object?> toJson() => {
|
||||
'branch': branch,
|
||||
if (upstream != null) 'upstream': upstream,
|
||||
'ahead': ahead,
|
||||
'behind': behind,
|
||||
'clean': isClean,
|
||||
'hasConflicts': hasConflicts,
|
||||
'staged': [for (final e in staged) e.toJson()],
|
||||
'unstaged': [for (final e in unstaged) e.toJson()],
|
||||
'untracked': [for (final e in untracked) e.toJson()],
|
||||
'conflicted': [for (final e in conflicted) e.toJson()],
|
||||
};
|
||||
'branch': branch,
|
||||
if (upstream != null) 'upstream': upstream,
|
||||
'ahead': ahead,
|
||||
'behind': behind,
|
||||
'clean': isClean,
|
||||
'hasConflicts': hasConflicts,
|
||||
'staged': [for (final e in staged) e.toJson()],
|
||||
'unstaged': [for (final e in unstaged) e.toJson()],
|
||||
'untracked': [for (final e in untracked) e.toJson()],
|
||||
'conflicted': [for (final e in conflicted) e.toJson()],
|
||||
};
|
||||
}
|
||||
|
||||
/// Run `git status` and parse the result.
|
||||
Future<GitStatus> gitStatus(Directory workDir) async {
|
||||
final ProcessResult branchResult;
|
||||
try {
|
||||
branchResult = await Process.run(
|
||||
gitBin,
|
||||
['status', '--porcelain=v2', '--branch', '-z'],
|
||||
workingDirectory: workDir.path,
|
||||
);
|
||||
branchResult = await Process.run(gitBin, ['status', '--porcelain=v2', '--branch', '-z'], workingDirectory: workDir.path);
|
||||
} on ProcessException {
|
||||
return const GitStatus(branch: null, entries: []);
|
||||
}
|
||||
@@ -136,33 +104,17 @@ Future<GitStatus> gitStatus(Directory workDir) async {
|
||||
|
||||
final ProcessResult result;
|
||||
try {
|
||||
result = await Process.run(
|
||||
gitBin,
|
||||
['status', '--porcelain=v1', '-z'],
|
||||
workingDirectory: workDir.path,
|
||||
);
|
||||
result = await Process.run(gitBin, ['status', '--porcelain=v1', '-z'], workingDirectory: workDir.path);
|
||||
} on ProcessException {
|
||||
return GitStatus(branch: branch, entries: const [], upstream: upstream, ahead: ahead, behind: behind);
|
||||
}
|
||||
|
||||
if (result.exitCode != 0) {
|
||||
return GitStatus(
|
||||
branch: branch,
|
||||
upstream: upstream,
|
||||
ahead: ahead,
|
||||
behind: behind,
|
||||
entries: const [],
|
||||
);
|
||||
return GitStatus(branch: branch, upstream: upstream, ahead: ahead, behind: behind, entries: const []);
|
||||
}
|
||||
|
||||
final entries = parsePorcelainV1(result.stdout as String);
|
||||
return GitStatus(
|
||||
branch: branch,
|
||||
upstream: upstream,
|
||||
ahead: ahead,
|
||||
behind: behind,
|
||||
entries: entries,
|
||||
);
|
||||
return GitStatus(branch: branch, upstream: upstream, ahead: ahead, behind: behind, entries: entries);
|
||||
}
|
||||
|
||||
List<GitFileStatus> parsePorcelainV1(String output) {
|
||||
@@ -193,13 +145,15 @@ List<GitFileStatus> parsePorcelainV1(String output) {
|
||||
}
|
||||
|
||||
final conflict = _conflictType(x, y);
|
||||
entries.add(GitFileStatus(
|
||||
path: path,
|
||||
indexState: conflict != null ? null : _parseState(x),
|
||||
workTreeState: conflict != null ? null : _parseState(y),
|
||||
origPath: origPath,
|
||||
conflictType: conflict,
|
||||
));
|
||||
entries.add(
|
||||
GitFileStatus(
|
||||
path: path,
|
||||
indexState: conflict != null ? null : _parseState(x),
|
||||
workTreeState: conflict != null ? null : _parseState(y),
|
||||
origPath: origPath,
|
||||
conflictType: conflict,
|
||||
),
|
||||
);
|
||||
i++;
|
||||
}
|
||||
return entries;
|
||||
|
||||
Reference in New Issue
Block a user