Skip to content

Commit 7da3e77

Browse files
committed
fix: stash uncommitted changes instead of aborting during ref checkout
1 parent 3b4886c commit 7da3e77

File tree

2 files changed

+48
-5
lines changed

2 files changed

+48
-5
lines changed

lib/doc_generator/doc_generator.dart

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,13 @@ Future<PackageApi> generateDocs({
2929
exit(1);
3030
}
3131

32-
// Check for uncommitted changes if we're going to checkout a ref
32+
// Stash uncommitted changes if we're going to checkout a different ref.
33+
// This allows the tool to work in CI environments where prior steps
34+
// (e.g. `flutter pub get`) create uncommitted artefacts.
35+
bool didStash = false;
3336
if (gitRef != 'HEAD' && GitUtils.hasUncommittedChanges(gitRoot.path)) {
34-
logger.err('Repository has uncommitted changes. Please commit or stash them before proceeding.');
35-
logger.err('This prevents potential data loss during ref checkout.');
36-
exit(1);
37+
logger.info('Stashing uncommitted changes before checking out ref...');
38+
didStash = await GitUtils.stashChanges(gitRoot.path);
3739
}
3840

3941
final originalRef = await GitUtils.getCurrentRef(gitRoot.path);
@@ -60,6 +62,10 @@ Future<PackageApi> generateDocs({
6062
} else {
6163
await GitUtils.checkoutRef(originalRef, gitRoot.path);
6264
}
65+
if (didStash) {
66+
logger.info('Restoring stashed changes...');
67+
await GitUtils.popStash(gitRoot.path);
68+
}
6369
logger.info('Successfully restored original state');
6470
} catch (e) {
6571
logger.err('Warning: Failed to restore original state: $e');
@@ -246,6 +252,6 @@ Future<PackageApi> generateDocs({
246252

247253
return packageApi;
248254
} finally {
249-
restoreOriginalState();
255+
await restoreOriginalState();
250256
}
251257
}

lib/doc_generator/git_utils.dart

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,43 @@ class GitUtils {
9999
}
100100
}
101101

102+
/// Stashes any uncommitted changes (including untracked files)
103+
/// Returns true if changes were stashed, false if the working tree was clean.
104+
static Future<bool> stashChanges(String? root) async {
105+
try {
106+
final result = await Process.run(
107+
'git',
108+
['stash', 'push', '--include-untracked', '-m', 'mtrust_api_guard_auto_stash'],
109+
workingDirectory: root,
110+
);
111+
if (result.exitCode != 0) {
112+
throw GitException('Failed to stash changes: ${result.stderr}');
113+
}
114+
// "No local changes to save" means nothing was stashed
115+
return !result.stdout.toString().contains('No local changes to save');
116+
} on ProcessException catch (e) {
117+
throw GitException('Git command not found: ${e.message}');
118+
}
119+
}
120+
121+
/// Pops the most recent stash entry.
122+
/// Silently succeeds if there is nothing to pop.
123+
static Future<void> popStash(String? root) async {
124+
try {
125+
final result = await Process.run(
126+
'git',
127+
['stash', 'pop'],
128+
workingDirectory: root,
129+
);
130+
if (result.exitCode != 0) {
131+
// Log but don't throw – a pop failure shouldn't crash the tool
132+
logger.err('Warning: Failed to pop stash: ${result.stderr}');
133+
}
134+
} on ProcessException catch (e) {
135+
throw GitException('Git command not found: ${e.message}');
136+
}
137+
}
138+
102139
/// Checks out a specific git ref
103140
/// Throws [GitException] if the operation fails
104141
static Future<void> checkoutRef(String ref, String? root) async {

0 commit comments

Comments
 (0)