public List <ElementVersion> Read(string directoriesFile, string elementsFile, string versionsFile) { List <ElementVersion> result = null; if (!string.IsNullOrWhiteSpace(elementsFile)) { Logger.TraceData(TraceEventType.Start | TraceEventType.Information, (int)TraceId.ReadCleartool, "Start reading file elements", elementsFile); var allActions = new List <Action>(); using (var files = new StreamReader(elementsFile, Encoding.Default)) { string line; int i = 0; while ((line = files.ReadLine()) != null) { int iTask = ++i; string currentLine = line; allActions.Add(() => { if (iTask % 100 == 0) { Logger.TraceData(TraceEventType.Information, (int)TraceId.ReadCleartool, "Reading file element " + iTask); } ReadElement(currentLine, false, _cleartools[iTask % _nbCleartool]); }); } } Parallel.Invoke(new ParallelOptions { MaxDegreeOfParallelism = _nbCleartool * 2 }, allActions.ToArray()); Logger.TraceData(TraceEventType.Stop | TraceEventType.Information, (int)TraceId.ReadCleartool, "Stop reading file elements", elementsFile); } if (!string.IsNullOrWhiteSpace(directoriesFile)) { Logger.TraceData(TraceEventType.Start | TraceEventType.Information, (int)TraceId.ReadCleartool, "Start reading directory elements", directoriesFile); var allActions = new List <Action>(); using (var directories = new StreamReader(directoriesFile, Encoding.Default)) { string line; int i = 0; while ((line = directories.ReadLine()) != null) { int iTask = ++i; string currentLine = line; allActions.Add(() => { if (iTask % 20 == 0) { Logger.TraceData(TraceEventType.Information, (int)TraceId.ReadCleartool, "Reading directory element " + iTask); } ReadElement(currentLine, true, _cleartools[iTask % _nbCleartool]); }); } } Parallel.Invoke(new ParallelOptions { MaxDegreeOfParallelism = _nbCleartool * 2 }, allActions.ToArray()); Logger.TraceData(TraceEventType.Stop | TraceEventType.Information, (int)TraceId.ReadCleartool, "Stop reading directory elements", directoriesFile); } if (!string.IsNullOrWhiteSpace(versionsFile)) { Logger.TraceData(TraceEventType.Start | TraceEventType.Information, (int)TraceId.ReadCleartool, "Start reading individual versions", versionsFile); result = new List <ElementVersion>(); using (var versions = new StreamReader(versionsFile)) { string line; int i = 0; // not parallel because not as useful, and trickier to handle versions in "random" order while ((line = versions.ReadLine()) != null) { if (++i % 100 == 0) { Logger.TraceData(TraceEventType.Information, (int)TraceId.ReadCleartool, "Reading version " + i); } ReadVersion(line, result, _cleartools[i % _nbCleartool]); } } Logger.TraceData(TraceEventType.Stop | TraceEventType.Information, (int)TraceId.ReadCleartool, "Stop reading individual versions", versionsFile); } // oids still in _oidsToCheck are not to be really imported if (_oidsToCheck.Count > 0) { foreach (var oid in _oidsToCheck) { ElementsByOid.Remove(oid); } foreach (var directory in ElementsByOid.Values.Where(e => e.IsDirectory)) { foreach (var branch in directory.Branches.Values) { foreach (DirectoryVersion directoryVersion in branch.Versions) { // use a copy to be able to remove var original = directoryVersion.Content.ToList(); directoryVersion.Content.Clear(); directoryVersion.Content.AddRange(original.Where(p => !_oidsToCheck.Contains(p.Value.Oid))); } } } } Logger.TraceData(TraceEventType.Start | TraceEventType.Information, (int)TraceId.ReadCleartool, "Start fixups"); foreach (var fixup in _contentFixups) { Element childElement; if (ElementsByOid.TryGetValue(fixup.Item3, out childElement)) { fixup.Item1.Content.Add(new KeyValuePair <string, Element>(fixup.Item2, childElement)); } else { Logger.TraceData(TraceEventType.Warning, (int)TraceId.ReadCleartool, "Element " + fixup.Item2 + " (oid:" + fixup.Item3 + ") referenced as " + fixup.Item2 + " in " + fixup.Item1 + " was not imported"); } } foreach (var fixup in _mergeFixups) { ElementVersion toFix = fixup.Item1; ElementVersion linkTo = toFix.Element.GetVersion(fixup.Item2, fixup.Item3); if (linkTo == null) { Logger.TraceData(TraceEventType.Warning, (int)TraceId.ReadCleartool, "Version " + fixup.Item2 + "/" + fixup.Item3 + " of " + toFix.Element + ", linked to " + toFix.Branch.BranchName + "/" + toFix.VersionNumber + ", was not imported"); continue; } (fixup.Item4 ? toFix.MergesTo : toFix.MergesFrom).Add(linkTo); } Logger.TraceData(TraceEventType.Stop | TraceEventType.Information, (int)TraceId.ReadCleartool, "Stop fixups"); Logger.TraceData(TraceEventType.Start | TraceEventType.Information, (int)TraceId.ReadCleartool, "Start reading label meta info"); var labelActions = new List <Action>(); int task = 0; List <string> labels; lock (_cleartools[0]) labels = _cleartools[0].ListLabels(); foreach (var label in labels) { int iTask = ++task; labelActions.Add(() => { if (iTask % 100 == 0) { Logger.TraceData(TraceEventType.Information, (int)TraceId.ReadCleartool, "Reading label meta info " + iTask); } string author; string login; DateTime date; var cleartool = _cleartools[task % _nbCleartool]; lock (cleartool) cleartool.GetLabelDetails(label, out author, out login, out date); LabelMeta labelMeta = new LabelMeta(); labelMeta.Name = label; labelMeta.AuthorName = author; labelMeta.AuthorLogin = login; labelMeta.Created = date; lock (_labelMetas) _labelMetas[label] = labelMeta; }); } Parallel.Invoke(new ParallelOptions { MaxDegreeOfParallelism = _nbCleartool * 2 }, labelActions.ToArray()); Logger.TraceData(TraceEventType.Stop | TraceEventType.Information, (int)TraceId.ReadCleartool, "Stop reading label meta info"); return(result); }
private void WriteChangeSet(ChangeSet changeSet, Dictionary <string, List <ChangeSet> > branches, Dictionary <string, LabelInfo> labels, Dictionary <string, LabelMeta> labelMetas) { if (changeSet.IsEmpty) { Logger.TraceData(TraceEventType.Information, (int)TraceId.ApplyChangeSet, "Skipped empty ChangeSet " + changeSet); return; } bool writeCommit = true; if (changeSet.IsEmptyGitCommit) { Logger.TraceData(TraceEventType.Information, (int)TraceId.ApplyChangeSet, "Only writing the tags (if any) for " + changeSet); writeCommit = false; } string branchName = changeSet.Branch == "main" ? "master" : MaybeRenamed(changeSet.Branch); if (writeCommit) { _writer.Write("commit refs/heads/" + branchName + "\n"); _writer.Write("mark :" + changeSet.Id + "\n"); _writer.Write("committer " + changeSet.AuthorName + " <" + changeSet.AuthorLogin + "> " + (changeSet.StartTime - Epoch).TotalSeconds + " +0200\n"); _writer.Write("# " + changeSet.StartTime + "\n"); InlineString(changeSet.GetComment()); if (changeSet.BranchingPoint != null) { _writer.Write("from :" + changeSet.BranchingPoint.Id + "\n"); _startedBranches.Add(branchName); } else if (_isIncremental && !_startedBranches.Contains(branchName)) { _writer.Write("from refs/heads/" + branchName + "^0\n"); _startedBranches.Add(branchName); } foreach (var merge in changeSet.Merges) { _writer.Write("merge :" + merge.Id + "\n"); } if (!_initialFilesAdded && branchName == "master") { _initialFilesAdded = true; foreach (var initialFile in InitialFiles) { var fileInfo = new FileInfo(initialFile.Item2); if (fileInfo.Exists) { _writer.Write("M 644 inline " + initialFile.Item1 + "\n"); InlineString(File.ReadAllText(initialFile.Item2)); } } } // order is significant : we must Rename and Copy files before (maybe) deleting their directory foreach (var pair in changeSet.Renamed) { _writer.Write("R \"" + RemoveDotRoot(pair.Item1) + "\" \"" + RemoveDotRoot(pair.Item2) + "\"\n"); } foreach (var pair in changeSet.Copied) { _writer.Write("C \"" + RemoveDotRoot(pair.Item1) + "\" \"" + RemoveDotRoot(pair.Item2) + "\"\n"); } foreach (var removed in changeSet.Removed) { _writer.Write("D " + RemoveDotRoot(removed) + "\n"); } foreach (var symLink in changeSet.SymLinks) { _writer.Write("M 120000 inline " + RemoveDotRoot(symLink.Item1) + "\n"); InlineString(RemoveDotRoot(symLink.Item2)); } foreach (var namedVersion in changeSet.Versions) { if (namedVersion.Version is DirectoryVersion || namedVersion.Names.Count == 0) { continue; } bool isEmptyFile = namedVersion.Version.VersionNumber == 0 && namedVersion.Version.Branch.BranchName == "main"; if (_doNotIncludeFileContent || isEmptyFile) { foreach (string name in namedVersion.Names.Select(RemoveDotRoot)) { if (isEmptyFile) { _writer.Write("M 644 inline " + name + "\ndata 0\n\n"); } else { // don't use InlineString here, so that /FetchFileContent is easy to implement _writer.Write("M 644 inline " + name + "\ndata <<EOF\n" + namedVersion.Version + "#" + namedVersion.Version.Element.Oid + "\nEOF\n\n"); // also include name in a comment for hooks in /FetchFileContent _writer.Write("#" + name + "\n"); } } continue; } InlineClearcaseFileVersion(namedVersion.Version.Element.Name, namedVersion.Version.Element.Oid, namedVersion.Version.VersionPath, namedVersion.Names.Select(RemoveDotRoot), true); } } foreach (var label in changeSet.Labels) { _writer.Write("tag " + label + "\n"); _writer.Write("from :" + changeSet.Id + "\n"); LabelMeta meta = labelMetas[label]; var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); _writer.Write("tagger " + meta.AuthorName + " <" + meta.AuthorLogin + "> " + (meta.Created - epoch).TotalSeconds + " +0000\n"); // var +0200 List <Tuple <ElementVersion, ElementVersion> > possibleBroken = labels[label].PossiblyBroken.Where( t => null != _relativeRoots.Find( r => RemoveDotRoot(t.Item1.ToString().Replace("\\", "/") + "/").StartsWith(r + "/") )).ToList(); if (possibleBroken.Count > 0) { Logger.TraceData(TraceEventType.Warning, (int)TraceId.ApplyChangeSet, "Label " + label + " was inconsistent when writing it. Count", possibleBroken.Count); string msg = "Warning! This tag could be incorrect.\nGot an unexpected result, though it could still be correct.\n\n"; foreach (Tuple <ElementVersion, ElementVersion> items in possibleBroken) { msg += "Expected \"" + RemoveDotRoot(items.Item1.ToString()) + "\", but got " + (items.Item2 == null ? "nothing" : "\"" + RemoveDotRoot(items.Item2.ToString()) + "\"") + ".\n"; } InlineString(msg); } else { _writer.Write("data 0\n\n"); } } }