@@ -181,16 +181,16 @@ from a parent of the folders list.
181181
182182 this . Output . WriteLine ( ) ;
183183
184- this . Unmount ( tracer ) ;
185-
186- string error ;
187- if ( ! DiskLayoutUpgrade . TryCheckDiskLayoutVersion ( tracer , enlistment . EnlistmentRoot , out error ) )
188- {
189- this . ReportErrorAndExit ( tracer , error ) ;
190- }
191-
192184 if ( fullDehydrate )
193185 {
186+ this . Unmount ( tracer ) ;
187+
188+ string error ;
189+ if ( ! DiskLayoutUpgrade . TryCheckDiskLayoutVersion ( tracer , enlistment . EnlistmentRoot , out error ) )
190+ {
191+ this . ReportErrorAndExit ( tracer , error ) ;
192+ }
193+
194194 RetryConfig retryConfig ;
195195 if ( ! RetryConfig . TryLoadFromGitConfig ( tracer , enlistment , out retryConfig , out error ) )
196196 {
@@ -216,7 +216,7 @@ from a parent of the folders list.
216216 {
217217 if ( cleanStatus )
218218 {
219- this . DehydrateFolders ( tracer , enlistment , folders ) ;
219+ this . DehydrateFolders ( tracer , enlistment , folders , backupRoot ) ;
220220 }
221221 else
222222 {
@@ -231,8 +231,15 @@ from a parent of the folders list.
231231 }
232232 }
233233
234- private void DehydrateFolders ( JsonTracer tracer , GVFSEnlistment enlistment , string [ ] folders )
234+ private void DehydrateFolders ( JsonTracer tracer , GVFSEnlistment enlistment , string [ ] folders , string backupRoot )
235235 {
236+ if ( ! this . TryBackupNonSrcFiles ( tracer , enlistment , backupRoot ) )
237+ {
238+ this . Output . WriteLine ( ) ;
239+ this . WriteMessage ( tracer , "ERROR: Backup failed. " ) ;
240+ return ;
241+ }
242+
236243 List < string > foldersToDehydrate = new List < string > ( ) ;
237244 List < string > folderErrors = new List < string > ( ) ;
238245
@@ -241,7 +248,7 @@ private void DehydrateFolders(JsonTracer tracer, GVFSEnlistment enlistment, stri
241248 {
242249 if ( ! ModifiedPathsDatabase . TryLoadOrCreate (
243250 tracer ,
244- Path . Combine ( enlistment . DotGVFSRoot , GVFSConstants . DotGVFS . Databases . ModifiedPaths ) ,
251+ Path . Combine ( GetBackupDatabasesPath ( backupRoot ) , GVFSConstants . DotGVFS . Databases . ModifiedPaths ) ,
245252 this . fileSystem ,
246253 out ModifiedPathsDatabase modifiedPaths ,
247254 out string error ) )
@@ -271,26 +278,13 @@ private void DehydrateFolders(JsonTracer tracer, GVFSEnlistment enlistment, stri
271278 else
272279 {
273280 string fullPath = Path . Combine ( enlistment . WorkingDirectoryBackingRoot , folder ) ;
274- if ( this . fileSystem . DirectoryExists ( fullPath ) )
281+ if ( ! this . fileSystem . DirectoryExists ( fullPath ) )
275282 {
276- // Since directories are deleted last and will be empty at that point we can skip errors
277- // while trying to delete it and leave the empty directory and continue to dehydrate
278- if ( ! this . TryIO ( tracer , ( ) => this . fileSystem . DeleteDirectory ( fullPath , ignoreDirectoryDeleteExceptions : true ) , $ "Deleting '{ fullPath } '", out ioError ) )
279- {
280- this . WriteMessage ( tracer , $ "Cannot { this . ActionName } folder '{ folder } ': removing '{ folder } ' failed.") ;
281- this . WriteMessage ( tracer , "Ensure no applications are accessing the folder and retry." ) ;
282- this . WriteMessage ( tracer , $ "More details: { ioError } ") ;
283- folderErrors . Add ( $ "{ folder } \0 { ioError } ") ;
284- }
285- else
286- {
287- foldersToDehydrate . Add ( folder ) ;
288- }
283+ this . WriteMessage ( tracer , $ "Cannot { this . ActionName } folder '{ folder } ': '{ folder } ' does not exist.") ;
284+ foldersToDehydrate . Add ( folder ) ;
289285 }
290286 else
291287 {
292- this . WriteMessage ( tracer , $ "Cannot { this . ActionName } folder '{ folder } ': '{ folder } ' does not exist.") ;
293-
294288 // Still add to foldersToDehydrate so that any placeholders or modified paths get cleaned up
295289 foldersToDehydrate . Add ( folder ) ;
296290 }
@@ -306,15 +300,9 @@ private void DehydrateFolders(JsonTracer tracer, GVFSEnlistment enlistment, stri
306300 this . ReportErrorAndExit ( tracer , $ "{ this . ActionName } for folders failed.") ;
307301 }
308302
309- // We can skip the version check because dehydrating folders requires that a git status
310- // be run first, and running git status requires that the repo already be mounted (meaning
311- // we don't need to perform another version check again)
312- this . Mount (
313- tracer ,
314- skipVersionCheck : true ) ;
315-
316303 if ( foldersToDehydrate . Count > 0 )
317304 {
305+ string backupSrc = GetBackupSrcPath ( backupRoot ) ;
318306 this . SendDehydrateMessage ( tracer , enlistment , folderErrors , foldersToDehydrate ) ;
319307 }
320308
@@ -329,6 +317,11 @@ private void DehydrateFolders(JsonTracer tracer, GVFSEnlistment enlistment, stri
329317 }
330318 }
331319
320+ private static string GetBackupSrcPath ( string backupRoot )
321+ {
322+ return Path . Combine ( backupRoot , "src" ) ;
323+ }
324+
332325 private bool IsFolderValid ( string folderPath )
333326 {
334327 if ( folderPath == GVFSConstants . DotGit . Root ||
@@ -353,10 +346,15 @@ private void SendDehydrateMessage(ITracer tracer, GVFSEnlistment enlistment, Lis
353346 {
354347 if ( ! pipeClient . Connect ( ) )
355348 {
356- this . ReportErrorAndExit ( "Unable to connect to GVFS. Try running 'gvfs mount'" ) ;
349+ this . Output . WriteLine ( "Mounting..." ) ;
350+ this . Mount ( tracer , skipVersionCheck : false ) ;
351+ if ( ! pipeClient . Connect ( ) )
352+ {
353+ this . ReportErrorAndExit ( "Unable to connect to GVFS. Try running 'gvfs mount'" ) ;
354+ }
357355 }
358356
359- NamedPipeMessages . DehydrateFolders . Request request = new NamedPipeMessages . DehydrateFolders . Request ( string . Join ( FolderListSeparator , folders ) ) ;
357+ NamedPipeMessages . DehydrateFolders . Request request = new NamedPipeMessages . DehydrateFolders . Request ( string . Join ( ";" , folders ) ) ;
360358 pipeClient . SendRequest ( request . CreateMessage ( ) ) ;
361359 response = NamedPipeMessages . DehydrateFolders . Response . FromMessage ( NamedPipeMessages . Message . FromString ( pipeClient . ReadRawResponse ( ) ) ) ;
362360 }
@@ -535,12 +533,83 @@ private void PrepareSrcFolder(ITracer tracer, GVFSEnlistment enlistment)
535533 }
536534 }
537535
536+ private bool TryBackupNonSrcFiles ( ITracer tracer , GVFSEnlistment enlistment , string backupRoot )
537+ {
538+ string backupSrc = GetBackupSrcPath ( backupRoot ) ;
539+ string backupGit = Path . Combine ( backupRoot , ".git" ) ;
540+ string backupGvfs = Path . Combine ( backupRoot , GVFSPlatform . Instance . Constants . DotGVFSRoot ) ;
541+ string backupDatabases = GetBackupDatabasesPath ( backupGvfs ) ;
542+
543+ string errorMessage = string . Empty ;
544+ if ( ! this . ShowStatusWhileRunning (
545+ ( ) =>
546+ {
547+ string ioError ;
548+ if ( ! this . TryIO ( tracer , ( ) => Directory . CreateDirectory ( backupRoot ) , "Create backup directory" , out ioError ) ||
549+ ! this . TryIO ( tracer , ( ) => Directory . CreateDirectory ( backupGit ) , "Create backup .git directory" , out ioError ) ||
550+ ! this . TryIO ( tracer , ( ) => Directory . CreateDirectory ( backupGvfs ) , "Create backup .gvfs directory" , out ioError ) ||
551+ ! this . TryIO ( tracer , ( ) => Directory . CreateDirectory ( backupDatabases ) , "Create backup .gvfs databases directory" , out ioError ) )
552+ {
553+ errorMessage = "Failed to create backup folders at " + backupRoot + ": " + ioError ;
554+ return false ;
555+ }
556+
557+ // ... backup the .gvfs hydration-related data structures...
558+ string databasesFolder = Path . Combine ( enlistment . DotGVFSRoot , GVFSConstants . DotGVFS . Databases . Name ) ;
559+ if ( ! this . TryCopyFilesInFolder ( tracer , databasesFolder , backupDatabases , searchPattern : "*" , filenamesToSkip : "RepoMetadata.dat" ) )
560+ {
561+ return false ;
562+ }
563+
564+ // ... backup everything related to the .git\index...
565+ if ( ! this . TryIO (
566+ tracer ,
567+ ( ) => File . Copy (
568+ Path . Combine ( enlistment . DotGitRoot , GVFSConstants . DotGit . IndexName ) ,
569+ Path . Combine ( backupGit , GVFSConstants . DotGit . IndexName ) ) ,
570+ "Backup the git index" ,
571+ out errorMessage ) ||
572+ ! this . TryIO (
573+ tracer ,
574+ ( ) => File . Copy (
575+ Path . Combine ( enlistment . DotGVFSRoot , GitIndexProjection . ProjectionIndexBackupName ) ,
576+ Path . Combine ( backupGvfs , GitIndexProjection . ProjectionIndexBackupName ) ) ,
577+ "Backup GVFS_projection" ,
578+ out errorMessage ) )
579+ {
580+ return false ;
581+ }
582+
583+ // ... backup all .git\*.lock files
584+ if ( ! this . TryCopyFilesInFolder ( tracer , enlistment . DotGitRoot , backupGit , searchPattern : "*.lock" ) )
585+ {
586+ return false ;
587+ }
588+
589+ return true ;
590+ } ,
591+ "Backing up your files" ) )
592+ {
593+ this . Output . WriteLine ( ) ;
594+ this . WriteMessage ( tracer , "ERROR: " + errorMessage ) ;
595+
596+ return false ;
597+ }
598+
599+ return true ;
600+ }
601+
602+ private static string GetBackupDatabasesPath ( string backupGvfs )
603+ {
604+ return Path . Combine ( backupGvfs , GVFSConstants . DotGVFS . Databases . Name ) ;
605+ }
606+
538607 private bool TryBackupFiles ( ITracer tracer , GVFSEnlistment enlistment , string backupRoot )
539608 {
540- string backupSrc = Path . Combine ( backupRoot , "src" ) ;
609+ string backupSrc = GetBackupSrcPath ( backupRoot ) ;
541610 string backupGit = Path . Combine ( backupRoot , ".git" ) ;
542611 string backupGvfs = Path . Combine ( backupRoot , GVFSPlatform . Instance . Constants . DotGVFSRoot ) ;
543- string backupDatabases = Path . Combine ( backupGvfs , GVFSConstants . DotGVFS . Databases . Name ) ;
612+ string backupDatabases = GetBackupDatabasesPath ( backupRoot ) ;
544613
545614 string errorMessage = string . Empty ;
546615 if ( ! this . ShowStatusWhileRunning (
@@ -638,6 +707,28 @@ private bool TryBackupFilesInFolder(ITracer tracer, string folderPath, string ba
638707 return true ;
639708 }
640709
710+ private bool TryCopyFilesInFolder ( ITracer tracer , string folderPath , string backupPath , string searchPattern , params string [ ] filenamesToSkip )
711+ {
712+ string errorMessage ;
713+ foreach ( string file in Directory . GetFiles ( folderPath , searchPattern ) )
714+ {
715+ string fileName = Path . GetFileName ( file ) ;
716+ if ( ! filenamesToSkip . Any ( x => x . Equals ( fileName , GVFSPlatform . Instance . Constants . PathComparison ) ) )
717+ {
718+ if ( ! this . TryIO (
719+ tracer ,
720+ ( ) => File . Copy ( file , file . Replace ( folderPath , backupPath ) ) ,
721+ $ "Backing up { Path . GetFileName ( file ) } ",
722+ out errorMessage ) )
723+ {
724+ return false ;
725+ }
726+ }
727+ }
728+
729+ return true ;
730+ }
731+
641732 private bool TryDownloadGitObjects ( ITracer tracer , GVFSEnlistment enlistment , RetryConfig retryConfig )
642733 {
643734 string errorMessage = null ;
0 commit comments