While testing my configuration editor app, one of the QA guys asked if it made a backup copy of the file being edited. My reply was “not yet”. And I thought about it a bit and decided to implement the backup feature and to kick it up a notch.
In prehistoric times, VMS would automagically version your files when you saved them. When you saved a file, the file system would append a version number starting at 1. You could open any version of a file by including the version number. If you left out the version number, you got the latest version. This goes back to the days when monitors supported both colors, green and not green. To clear out older versions, you would periodically need to run the purge command.
I wanted something close to that in my code, except without having to manually purge the file system. I wrote a simple static class that takes the name of the file and the number of versions to keep.
public static class FileHelper { /// <summary> /// Make a numbered backup copy of the specified files. Backup files have the name filename.exe.yymmdd##, where yymmdd is the date and ## is a zero justified sequence number starting at 1 /// </summary> /// <param name="fileName">Name of the file to backup.</param> /// <param name="maxBackups">The maximum backups to keep.</param> public static void MakeBackup(string fileName, int maxBackups) { // Make sure that the file exists, you don't backup a new file if (File.Exists(fileName)) { // First backup copy of the day starts at 1 int newSequence = 1; // Get the list of previous backups of the file, skipping the current file var backupFiles = Directory.GetFiles(Path.GetDirectoryName(fileName), Path.GetFileName(fileName) + ".*") .ToList() .Where(d => !d.Equals(fileName)) .OrderBy(d => d); // Get the name of the last backup performed var lastBackupFilename = backupFiles.LastOrDefault(); // If we have at least one previous backup copy if (lastBackupFilename != null) { // Get the last sequence number back taking the last 2 characters and convert them to an int. And add 1 to that number if (Int32.TryParse(Path.GetExtension(lastBackupFilename).GetLast(2), out newSequence)) newSequence++; // If we have more backups than we need to keep if (backupFiles.Count() >= maxBackups) { // Get a list of the oldest files to delele var expiredFiles = backupFiles.Take(backupFiles.Count() - maxBackups + 1); foreach (var expiredFile in expiredFiles) { File.Delete(expiredFile); } } } // Create the file name for the newest back up file. var latestBackup = String.Format("{0}.{1:yyMMdd}{2:00}", fileName, DateTime.Now, newSequence); // Copy the current file to the new backup name and overwrite any existing copy File.Copy(fileName, latestBackup, true); } } } // String Extension that was used in the code but left out when I first published public static class StringExtension { public static string GetLast(this string source, int tail_length) { if(tail_length >= source.Length) return source; return source.Substring(source.Length - tail_length); } }
You would use this method like this:
FileHelper.MakeBackup("web.config", 5);
The first time you called it, you would get web.config.14031401. The next time would get web.config.14031402. When web.config.14031406 was created, web.config.14031401 would be deleted to keep the number of backups limited to 5.
[Updated August 17th, 2015]
When I posted this, I left out a string extension methiod that I use to get the last N characters from a string. That code is now included.