public List<FileRevision> Load(string file = DataFileName) { var list = new List<FileRevision>(); using(var r = File.OpenText(file)) { string line; while((line = r.ReadLine()) != null) { var m = _versionRx.Match(line); if(!m.Success) continue; var v = new FileRevision { At = new DateTime(long.Parse(m.Groups["at"].Value), DateTimeKind.Utc), User = m.Groups["user"].Value, FileSpec = m.Groups["spec"].Value, VssVersion = int.Parse(m.Groups["ver"].Value), Physical = m.Groups["phys"].Value, Comment = m.Groups["comment"].Value.Replace('\u0001', '\n') }; list.Add(v); } } return list; }
void UnrecognizedError(FileRevision file, Exception ex = null) { _log.WriteLine("UNRECOGNIZED ERROR: {0}", file.FileSpec); Console.Error.WriteLine("\n!!! Unrecognized error. See logs.\n{0}@{1}", file.FileSpec, file.VssVersion); if(ex != null) { _log.WriteLine(ex.ToString()); Console.Error.WriteLine(" ERROR: {0}", ex.Message); } }
void GetFromVss(FileRevision file) { // clean destination foreach (var tempFile in Directory.GetFiles(_tempDir, "*", SearchOption.AllDirectories)) { try { File.SetAttributes(tempFile, FileAttributes.Normal); File.Delete(tempFile); } catch (Exception ex) { Console.WriteLine("\nCan't remove temp file: " + tempFile + "\n" + ex.Message); } } try { var vssItem = _db.VSSItem[file.FileSpec]; // move to correct veriosn if (vssItem.VersionNumber != file.VssVersion) vssItem = vssItem.Version[file.VssVersion]; var dstFileName = Path.GetFileName(file.FileSpec.TrimStart('$', '/', '\\')); var path = Path.Combine(_tempDir, Guid.NewGuid().ToString("N") + "-" + dstFileName); try { vssItem.Get(path, (int)VSSFlags.VSSFLAG_FORCEDIRNO | (int)VSSFlags.VSSFLAG_USERRONO | (int)VSSFlags.VSSFLAG_REPREPLACE); } catch(Exception ex) { if (string.IsNullOrWhiteSpace(_options.SSPath)) throw; // special case when physical file not correspond to var m = Regex.Match(ex.Message, "File ['\"](?<phys>[^'\"]+)['\"] not found"); if (!m.Success) throw; if (m.Groups["phys"].Value == vssItem.Physical) throw; Console.WriteLine("\nPhysical file mismatch. Try get with ss.exe"); path = new SSExeHelper(_options.SSPath, _log).Get(file.FileSpec, file.VssVersion, _tempDir); if (path == null) { Console.WriteLine("Get with ss.exe failed"); throw; } } // in force mode check if file already in cache and coincidence by hash if(_options.Force) { var ce = _cache.GetFileInfo(file.FileSpec, file.VssVersion); if(ce != null) { string hash; using(var s = new FileStream(path, FileMode.Open, FileAccess.Read)) hash = Convert.ToBase64String(_hashAlgo.ComputeHash(s)); if(hash != ce.Sha1Hash) { _log.WriteLine("!!! Cache contains different content for: " + file.FileSpec); _log.WriteLine("{0} != {1}", hash, ce.Sha1Hash); _cache.AddFile(file.FileSpec, file.VssVersion, path, false); } return; } } _cache.AddFile(file.FileSpec, file.VssVersion, path, false); } catch (Exception ex) { if (ex.Message.Contains("does not retain old versions of itself")) { Console.WriteLine("{0} hasn't retain version {1}.", file.FileSpec, file.VssVersion); _cache.AddError(file.FileSpec, file.VssVersion, "not-retained"); return; } // last revision shall always present // known error if (ex.Message.Contains("SourceSafe was unable to finish writing a file. Check your available disk space, and ask the administrator to analyze your SourceSafe database.")) { Console.Error.WriteLine("\nAbsent file revision: {0}@{1}", file.FileSpec, file.VssVersion); _cache.AddError(file.FileSpec, file.VssVersion, "broken-revision"); return; } _cache.AddError(file.FileSpec, file.VssVersion, ex.Message); UnrecognizedError(file, ex); } }
void Process(FileRevision file) { if (!IsShouldBeProcessed(file.FileSpec)) return; var sw = Stopwatch.StartNew(); GetFromVss(file); sw.Stop(); lock (_log) { _log.WriteLine("[{2:s} +{3,-7}ms] Get: {0}@{1}", file.FileSpec, file.VssVersion, DateTimeOffset.Now, sw.ElapsedMilliseconds); Console.Write('.'); } }
public void Build(Options opts, IList<Tuple<string, int>> files, Action<float> progress = null) { var stopWatch = new Stopwatch(); stopWatch.Start(); using (var cache = new VssFileCache(opts.CacheDir + "-revs", opts.SourceSafeIni)) using(var wr = File.CreateText(DataFileName)) using(var log = File.CreateText(LogFileName)) { wr.AutoFlush = log.AutoFlush = true; var db = opts.DB.Value; var findex = 0; foreach (var spec in files.Select(t => t.Item1)) { findex++; try{ IVSSItem item = db.VSSItem[spec]; var head = item.VersionNumber; var timestamp = item.VSSVersion.Date.Ticks; var cachedData = cache.GetFilePath(spec, head, timestamp); if (cachedData != null) { Console.Write("c"); Save(wr, Load(cachedData)); // next file continue; } Console.Write("[{0,5}/{1,5}] {2} ", findex, files.Count, item.Spec); if (progress != null) progress((float)findex / files.Count); var rotationIndex = 0; var rotationArray = @"|/-\|/-\".ToCharArray(); var latestOnly = IsLatestOnly(opts, spec); var itemRevisions = new List<FileRevision>(); foreach (IVSSVersion ver in item.Versions) { Console.Write("{0}\b", rotationArray[rotationIndex++ % rotationArray.Length]); if (ver.Action.StartsWith("Labeled ") || ver.Action.StartsWith("Branched ")) continue; if (!ver.Action.StartsWith("Checked in ") && !ver.Action.StartsWith("Created ") && !ver.Action.StartsWith("Archived ") && !ver.Action.StartsWith("Rollback to")) { log.WriteLine("Unknown action: " + ver.Action); } var user = ver.Username.ToLowerInvariant(); var fileVersionInfo = new FileRevision { FileSpec = item.Spec, At = ver.Date.ToUniversalTime(), Comment = ver.Comment, VssVersion = ver.VersionNumber, User = user }; try { // can throw exception, but it is not critical fileVersionInfo.Physical = ver.VSSItem.Physical; } catch (Exception ex) { Console.WriteLine("ERROR: Get Physical: " + ex.Message); log.WriteLine("ERROR: Get Physical: {0}", spec); log.WriteLine(ex.ToString()); fileVersionInfo.Physical = "_UNKNOWN_"; } itemRevisions.Add(fileVersionInfo); if (latestOnly) break; Console.Write('.'); } Console.WriteLine(" "); if (itemRevisions.Count > 0) { // some time date of items wrong, but versions - correct. // sort items in correct order and fix dates itemRevisions = itemRevisions.OrderBy(i => i.VssVersion).ToList(); // fix time. make time of each next item greater than all previous var notEarlierThan = itemRevisions[0].At; for (int i = 1; i < itemRevisions.Count; i++) { if (itemRevisions[i].At < notEarlierThan) { itemRevisions[i].At = notEarlierThan + TimeSpan.FromMilliseconds(1); itemRevisions[i].Comment += "\n! Time was fixed during VSS -> SVN conversion. Time can be incorrect !\n"; itemRevisions[i].Comment = itemRevisions[i].Comment.Trim(); } notEarlierThan = itemRevisions[i].At; } } Save(wr, itemRevisions); var tempFile = Path.GetTempFileName(); try { using (var sw = new StreamWriter(tempFile, false, Encoding.UTF8)) Save(sw, itemRevisions); cache.AddFile(spec, head, timestamp, tempFile, false); } finally { if (File.Exists(tempFile)) File.Delete(tempFile); } } catch(Exception ex) { Console.WriteLine("ERROR: {0}", spec); log.WriteLine("ERROR: {0}", spec); log.WriteLine(ex.ToString()); } } } stopWatch.Stop(); Console.WriteLine("Build files versions list complete. Take: {0}", stopWatch.Elapsed); }