@@ -7,7 +7,6 @@ import 'package:path/path.dart' as path;
77import '../models/flutter_version_model.dart' ;
88import '../models/git_reference_model.dart' ;
99import '../utils/exceptions.dart' ;
10- import '../utils/file_lock.dart' ;
1110import '../utils/git_clone_progress_tracker.dart' ;
1211import 'base_service.dart' ;
1312import 'cache_service.dart' ;
@@ -16,15 +15,9 @@ import 'process_service.dart';
1615/// Service for Git operations
1716/// Handles git cache management and repository operations
1817class GitService extends ContextualService {
19- late final FileLocker _updatingCacheLock;
2018 List <GitReference >? _referencesCache;
2119
22- GitService (super .context) {
23- _updatingCacheLock = context.createLock (
24- 'updating-cache' ,
25- expiresIn: const Duration (minutes: 10 ),
26- );
27- }
20+ GitService (super .context);
2821
2922 // Create a custom Process.start, that prints using the progress bar
3023 Future <void > _createLocalMirror () async {
@@ -108,110 +101,104 @@ class GitService extends ContextualService {
108101 }
109102
110103 Future <void > updateLocalMirror () async {
111- final unlock = await _updatingCacheLock.getLock ();
112-
113104 final gitCacheDir = Directory (context.gitCachePath);
114105 final isGitDir = await GitDir .isGitDir (gitCacheDir.path);
115106 final dotGitType = FileSystemEntity .typeSync (
116107 path.join (gitCacheDir.path, '.git' ),
117108 );
118109 final isBareRepo = dotGitType == FileSystemEntityType .notFound;
119110
120- try {
121- if (isGitDir) {
122- try {
123- logger.debug ('Updating local mirror...' );
124- final processService = get <ProcessService >();
125- Future <ProcessResult > runInCache (List <String > args) {
126- if (isBareRepo) {
127- return processService.run (
128- 'git' ,
129- args: ['--git-dir' , gitCacheDir.path, ...args],
130- );
131- }
132-
111+ if (isGitDir) {
112+ try {
113+ logger.debug ('Updating local mirror...' );
114+ final processService = get <ProcessService >();
115+ Future <ProcessResult > runInCache (List <String > args) {
116+ if (isBareRepo) {
133117 return processService.run (
134118 'git' ,
135- args: ['-C ' , gitCacheDir.path, ...args],
119+ args: ['--git-dir ' , gitCacheDir.path, ...args],
136120 );
137121 }
138122
139- if (! isBareRepo) {
140- // Ensure clean working directory before fetch operations
141- // This prevents merge conflicts during fetch (fixes #819)
142- logger.debug ('Ensuring clean working directory...' );
143- await runInCache (['reset' , '--hard' , 'HEAD' ]);
144- await runInCache (['clean' , '-fd' ]);
145- }
123+ return processService.run (
124+ 'git' ,
125+ args: ['-C' , gitCacheDir.path, ...args],
126+ );
127+ }
146128
147- // First, prune any stale references
148- logger.debug ('Pruning stale references...' );
149- await runInCache (['remote' , 'prune' , 'origin' ]);
150-
151- // Then fetch all refs including tags
152- logger.debug ('Fetching all refs...' );
153- await runInCache (['fetch' , '--all' , '--tags' , '--prune' ]);
154-
155- if (! isBareRepo) {
156- // Check if there are any uncommitted changes
157- logger.debug ('Checking for uncommitted changes...' );
158- final statusResult = await runInCache (['status' , '--porcelain' ]);
159- final output = (statusResult.stdout as String ).trim ();
160- if (output.isEmpty) {
161- logger.debug (
162- 'No uncommitted changes. Working directory is clean.' ,
163- );
164- } else {
165- logger.warn (
166- 'Local mirror has unexpected changes. Recreating mirror...' ,
167- );
168- if (gitCacheDir.existsSync ()) {
169- gitCacheDir.deleteSync (recursive: true );
170- }
171- await _createLocalMirror ();
172- return ;
129+ if (! isBareRepo) {
130+ // Ensure clean working directory before fetch operations
131+ // This prevents merge conflicts during fetch (fixes #819)
132+ logger.debug ('Ensuring clean working directory...' );
133+ await runInCache (['reset' , '--hard' , 'HEAD' ]);
134+ await runInCache (['clean' , '-fd' ]);
135+ }
136+
137+ // First, prune any stale references
138+ logger.debug ('Pruning stale references...' );
139+ await runInCache (['remote' , 'prune' , 'origin' ]);
140+
141+ // Then fetch all refs including tags
142+ logger.debug ('Fetching all refs...' );
143+ await runInCache (['fetch' , '--all' , '--tags' , '--prune' ]);
144+
145+ if (! isBareRepo) {
146+ // Check if there are any uncommitted changes
147+ logger.debug ('Checking for uncommitted changes...' );
148+ final statusResult = await runInCache (['status' , '--porcelain' ]);
149+ final output = (statusResult.stdout as String ).trim ();
150+ if (output.isEmpty) {
151+ logger.debug (
152+ 'No uncommitted changes. Working directory is clean.' ,
153+ );
154+ } else {
155+ logger.warn (
156+ 'Local mirror has unexpected changes. Recreating mirror...' ,
157+ );
158+ if (gitCacheDir.existsSync ()) {
159+ gitCacheDir.deleteSync (recursive: true );
173160 }
161+ await _createLocalMirror ();
162+ return ;
174163 }
164+ }
175165
176- logger.debug ('Local mirror updated successfully' );
177- } catch (e) {
178- // Only recreate the mirror if it's a critical git error that indicates
179- // the repository is unrecoverable. Other errors (network, permissions, etc.)
180- // are re-thrown so callers can handle them appropriately.
181- // Known critical error patterns: "not a git repository", "corrupt", "damaged",
182- // "hash mismatch", "object file...empty"
183- if (e is ProcessException ) {
184- final messageLower = e.message.toLowerCase ();
185- if (messageLower.contains ('not a git repository' ) ||
186- messageLower.contains ('corrupt' ) ||
187- messageLower.contains ('damaged' ) ||
188- messageLower.contains ('hash mismatch' ) ||
189- (messageLower.contains ('object file' ) &&
190- messageLower.contains ('empty' ))) {
191- logger.warn (
192- 'Local mirror appears to be corrupted (${e .message }). '
193- 'Recreating mirror...' ,
194- );
195- if (gitCacheDir.existsSync ()) {
196- gitCacheDir.deleteSync (recursive: true );
197- }
198- await _createLocalMirror ();
199-
200- return ;
166+ logger.debug ('Local mirror updated successfully' );
167+ } catch (e) {
168+ // Only recreate the mirror if it's a critical git error that indicates
169+ // the repository is unrecoverable. Other errors (network, permissions, etc.)
170+ // are re-thrown so callers can handle them appropriately.
171+ // Known critical error patterns: "not a git repository", "corrupt", "damaged",
172+ // "hash mismatch", "object file...empty"
173+ if (e is ProcessException ) {
174+ final messageLower = e.message.toLowerCase ();
175+ if (messageLower.contains ('not a git repository' ) ||
176+ messageLower.contains ('corrupt' ) ||
177+ messageLower.contains ('damaged' ) ||
178+ messageLower.contains ('hash mismatch' ) ||
179+ (messageLower.contains ('object file' ) &&
180+ messageLower.contains ('empty' ))) {
181+ logger.warn (
182+ 'Local mirror appears to be corrupted (${e .message }). '
183+ 'Recreating mirror...' ,
184+ );
185+ if (gitCacheDir.existsSync ()) {
186+ gitCacheDir.deleteSync (recursive: true );
201187 }
202- }
188+ await _createLocalMirror ();
203189
204- logger.err (
205- 'Failed to update local mirror: $e . '
206- 'Try running "fvm doctor" to diagnose issues.' ,
207- );
208- rethrow ;
190+ return ;
191+ }
209192 }
210- } else {
211- await _createLocalMirror ();
193+
194+ logger.err (
195+ 'Failed to update local mirror: $e . '
196+ 'Try running "fvm doctor" to diagnose issues.' ,
197+ );
198+ rethrow ;
212199 }
213- } finally {
214- unlock ();
200+ } else {
201+ await _createLocalMirror ();
215202 }
216203 }
217204
0 commit comments