public ElementBranch(Element element, string branchName, ElementVersion branchingPoint) { Element = element; BranchName = branchName; BranchingPoint = branchingPoint; Versions = new List <ElementVersion>(); }
public NamedVersion(ElementVersion version, string name, bool inRawChangeSet) : this() { Version = version; InRawChangeSet = inRawChangeSet; if (name != null) { Names.Add(name); } }
public void GetVersionDetails(ElementVersion version, out List <Tuple <string, int> > mergesTo, out List <Tuple <string, int> > mergesFrom) { bool isDir = version.Element.IsDirectory; // not interested in directory merges string format = "%Fu" + _separator + "%u" + _separator + "%d" + _separator + "%Nc" + _separator + "%Nl" + (isDir ? "" : _separator + "%[hlink:Merge]p"); // string.Join to handle multi-line comments string raw = string.Join("\r\n", ExecuteCommand("desc -fmt \"" + format + "\" \"" + version + "\"")); string[] parts = _separator.Split(raw); version.AuthorName = string.Intern(parts[0]); version.AuthorLogin = string.Intern(parts[1]); version.Date = DateTime.Parse(parts[2], null, DateTimeStyles.RoundtripKind).ToUniversalTime(); version.Comment = string.Intern(parts[3]); foreach (string label in parts[4].Split(' ')) { if (!string.IsNullOrWhiteSpace(label) && _labelFilter.ShouldKeep(label)) { version.Labels.Add(string.Intern(label)); } } mergesTo = mergesFrom = null; if (isDir && parts.Count() >= 6 && !string.IsNullOrEmpty(parts[5])) { Logger.TraceData(TraceEventType.Verbose, (int)TraceId.Cleartool, "Ignoring directory merge info for", version); } if (isDir || string.IsNullOrEmpty(parts[5])) { return; } Match match = _mergeRegex.Match(parts[5]); if (!match.Success) { Logger.TraceData(TraceEventType.Warning, (int)TraceId.Cleartool, "Failed to parse merge data '" + parts[5] + "'"); return; } mergesTo = new List <Tuple <string, int> >(); mergesFrom = new List <Tuple <string, int> >(); int count = match.Groups[1].Captures.Count; for (int i = 0; i < count; i++) { var addTo = match.Groups[2].Captures[i].Value == "->" ? mergesTo : mergesFrom; var branch = match.Groups[3].Captures[i].Value; var versionNumber = match.Groups[4].Captures[i].Value; if (versionNumber.StartsWith("CHECKEDOUT")) { continue; } addTo.Add(new Tuple <string, int>(branch, int.Parse(versionNumber))); } }
public NamedVersion Add(ElementVersion version, string name, bool inRawChangeSet) { NamedVersion result; NamedVersion existing = Versions.Find(v => v.Version.Element == version.Element); if (!version.Element.IsDirectory && existing != null) { // we are always on the same branch => we keep the latest version number for file elements, // which should always be the new version due to the way we retrieve them if (existing.Names.Count > 0 && name != null && !existing.Names.Contains(name)) { existing.Names.Add(name); Logger.TraceData(TraceEventType.Information, (int)TraceId.CreateChangeSet, "Version " + version + " has several names : " + string.Join(", ", existing.Names)); } ElementVersion skippedVersion = null; if (existing.Version.VersionNumber < version.VersionNumber) { skippedVersion = existing.Version; existing.Version = version; } else if (existing.Version.VersionNumber > version.VersionNumber) { skippedVersion = version; } if (skippedVersion != null && (skippedVersion.Labels.Count > 0 || skippedVersion.MergesFrom.Count > 0 || skippedVersion.MergesTo.Count > 0)) { SkippedVersions.Add(skippedVersion); } result = existing; } else { Versions.Add(result = new NamedVersion(version, name, inRawChangeSet)); } if (inRawChangeSet) { if (version.Date < StartTime) { StartTime = version.Date; } if (version.Date > FinishTime) { FinishTime = version.Date; } } return(result); }
public bool IsAncestorOf(ElementVersion version) { if (Element != version.Element) { return(false); } if (Branch == version.Branch) { return(VersionNumber < version.VersionNumber); } if (version.Branch.BranchingPoint != null) { return(IsAncestorOf(version.Branch.BranchingPoint)); } return(false); }
private void ProcessVersion(ElementVersion version, Dictionary <string, List <ChangeSet> > branchChangeSets, HashSet <ElementVersion> newVersions) { // we don't really handle versions 0 on branches : always consider BranchingPoint ElementVersion versionForLabel = version; while (versionForLabel.VersionNumber == 0 && versionForLabel.Branch.BranchingPoint != null) { versionForLabel = versionForLabel.Branch.BranchingPoint; } // don't "move" the label on versions that won't be processed (we need to assume these are correct) if (newVersions == null || newVersions.Contains(versionForLabel)) { foreach (var label in version.Labels) { LabelInfo labelInfo; if (!Labels.TryGetValue(label, out labelInfo)) { labelInfo = new LabelInfo(label); Labels.Add(label, labelInfo); } labelInfo.Versions.Add(versionForLabel); // also actually "move" the label if (versionForLabel != version) { versionForLabel.Labels.Add(label); } } } // end of label move if (versionForLabel != version) { version.Labels.Clear(); } if (version.VersionNumber == 0 && (version.Element.IsDirectory || version.Branch.BranchName != "main")) { return; } List <ChangeSet> authorChangeSets; if (!branchChangeSets.TryGetValue(version.AuthorLogin, out authorChangeSets)) { authorChangeSets = new List <ChangeSet>(); branchChangeSets.Add(version.AuthorLogin, authorChangeSets); } AddVersion(authorChangeSets, version); }
private IEnumerable <string> ProcessVersionLabels(ElementVersion version, Dictionary <Element, ElementVersion> elementsVersions, HashSet <string> finishedLabels) { var result = new List <string>(); foreach (var label in version.Labels) { LabelInfo labelInfo; if (!_labels.TryGetValue(label, out labelInfo)) { continue; } labelInfo.MissingVersions.RemoveFromCollection(version.Branch.BranchName, version); if (labelInfo.MissingVersions.Count > 0) { continue; } Logger.TraceData(TraceEventType.Verbose, (int)TraceId.CreateChangeSet, "Label " + label + " completed with version " + version); // so we removed the last missing version, check that everything is still OK _labels.Remove(label); finishedLabels.Add(label); bool ok = true; foreach (var toCheck in labelInfo.Versions) { ElementVersion inCurrentVersions; elementsVersions.TryGetValue(toCheck.Element, out inCurrentVersions); if ((inCurrentVersions == null && toCheck.VersionNumber != 0) || (inCurrentVersions != null && inCurrentVersions != toCheck)) { string msg = "Label " + label + " is inconsistent : should be on " + toCheck + (inCurrentVersions == null ? ", but element has no current version" : ", not on " + inCurrentVersions); Logger.TraceData(TraceEventType.Verbose, (int)TraceId.CreateChangeSet, msg); ok = false; } } if (!ok) { Logger.TraceData(TraceEventType.Warning, (int)TraceId.CreateChangeSet, "Label " + label + " was inconsistent : not applied"); } else { Logger.TraceData(TraceEventType.Verbose, (int)TraceId.CreateChangeSet, "Label " + label + " was applied"); result.Add(label); } } return(result); }
private bool AddVersionToBranch(ElementBranch branch, int versionNumber, bool isDir, List <ElementVersion> newVersions, Cleartool cleartool) { ElementVersion version; if (isDir) { var dirVersion = new DirectoryVersion(branch, versionNumber); Dictionary <string, string> res; lock (cleartool) res = cleartool.Ls(dirVersion.ToString()); foreach (var child in res) { lock (ElementsByOid) { Element childElement; if (ElementsByOid.TryGetValue(child.Value, out childElement)) { dirVersion.Content.Add(new KeyValuePair <string, Element>(child.Key, childElement)); } else if (child.Value.StartsWith(SymLinkElement.SYMLINK)) { Element symLink = new SymLinkElement(branch.Element, child.Value); Element existing; if (ElementsByOid.TryGetValue(symLink.Oid, out existing)) { symLink = existing; } else { ElementsByOid.Add(symLink.Oid, symLink); } dirVersion.Content.Add(new KeyValuePair <string, Element>(child.Key, symLink)); } else { _contentFixups.Add(new Tuple <DirectoryVersion, string, string>(dirVersion, child.Key, child.Value)); } } } version = dirVersion; } else { version = new ElementVersion(branch, versionNumber); } List <Tuple <string, int> > mergesTo, mergesFrom; lock (cleartool) cleartool.GetVersionDetails(version, out mergesTo, out mergesFrom); if (mergesTo != null) { foreach (var merge in mergesTo) { // only merges between branches are interesting if (merge.Item1 != branch.BranchName) { lock (_mergeFixups) _mergeFixups.Add(new Tuple <ElementVersion, string, int, bool>(version, merge.Item1, merge.Item2, true)); } } } if (mergesFrom != null) { foreach (var merge in mergesFrom) { // only merges between branches are interesting if (merge.Item1 != branch.BranchName) { lock (_mergeFixups) _mergeFixups.Add(new Tuple <ElementVersion, string, int, bool>(version, merge.Item1, merge.Item2, false)); } } } if (version.Date > _originDate) { Logger.TraceData(TraceEventType.Information, (int)TraceId.ReadCleartool, string.Format("Skipping version {0} : {1} > {2}", version, version.Date, _originDate)); return(false); } branch.Versions.Add(version); if (newVersions != null) { newVersions.Add(version); } return(true); }
private void ReadElement(string elementName, bool isDir, Cleartool cleartool) { // canonical name of elements is without the trailing '@@' if (elementName.EndsWith("@@")) { elementName = elementName.Substring(0, elementName.Length - 2); } string oid; lock (cleartool) oid = cleartool.GetOid(elementName); if (string.IsNullOrEmpty(oid)) { Logger.TraceData(TraceEventType.Warning, (int)TraceId.ReadCleartool, "Could not find oid for element " + elementName); return; } lock (_oidsToCheck) _oidsToCheck.Remove(oid); lock (ElementsByOid) if (ElementsByOid.ContainsKey(oid)) { return; } Logger.TraceData(TraceEventType.Start | TraceEventType.Verbose, (int)TraceId.ReadCleartool, "Start reading " + (isDir ? "directory" : "file") + " element", elementName); var element = new Element(elementName, isDir) { Oid = oid }; lock (ElementsByOid) ElementsByOid[oid] = element; List <string> versionStrings; lock (cleartool) versionStrings = cleartool.Lsvtree(elementName); foreach (string versionString in versionStrings) { // there is a first "version" for each branch, without a version number if (!_isFullVersionRegex.IsMatch(versionString)) { continue; } Logger.TraceData(TraceEventType.Verbose, (int)TraceId.ReadCleartool, "Creating version", versionString); string[] versionPath = versionString.Split(new[] { '\\' }, StringSplitOptions.RemoveEmptyEntries); string branchName = versionPath[versionPath.Length - 2]; int versionNumber = int.Parse(versionPath[versionPath.Length - 1]); ElementBranch branch; if (!element.Branches.TryGetValue(branchName, out branch)) { // a new branch always start from the last seen version of the parent branch ElementVersion branchingPoint = null; if (versionPath.Length > 2) { // The version could be missing on a parent branch because // it was created (or merged) directly from a grandparent. // We need to traverse the whole branch tree up to the main // branch to find any. int index = versionPath.Length - 3; while (index >= 0) { string b = versionPath[index]; if (element.Branches.ContainsKey(b)) { branchingPoint = element.Branches[b].Versions.Last(); break; } index--; } if (branchingPoint == null) { throw new Exception("Unable to find any branching point for version " + versionString + " and element " + elementName + "!"); } // Go back up to the missing one while (index < versionPath.Length - 3) { index++; string skippedName = versionPath[index]; ElementBranch skippedBranch = new ElementBranch(element, skippedName, branchingPoint); element.Branches[skippedName] = skippedBranch; if (!AddVersionToBranch(skippedBranch, 0, isDir, null, cleartool)) { throw new Exception("Failed to add missing branch " + skippedName + " for element " + elementName + "!"); } branchingPoint = skippedBranch.Versions.Last(); } } branch = new ElementBranch(element, branchName, branchingPoint); element.Branches[branchName] = branch; } bool added = AddVersionToBranch(branch, versionNumber, isDir, null, cleartool); if (!added) { // versions was too recent if (branch.Versions.Count == 0) { // do not leave an empty branch element.Branches.Remove(branchName); } // versions are retrieved in order of creation only within a branch : // we still may have eligible versions on a parent branch, so we must continue } } Logger.TraceData(TraceEventType.Stop | TraceEventType.Verbose, (int)TraceId.ReadCleartool, "Stop reading element", elementName); }
private void ReadElement(string elementName, bool isDir, Cleartool cleartool) { // canonical name of elements is without the trailing '@@' if (elementName.EndsWith("@@")) { elementName = elementName.Substring(0, elementName.Length - 2); } string oid; lock (cleartool) oid = cleartool.GetOid(elementName); if (string.IsNullOrEmpty(oid)) { Logger.TraceData(TraceEventType.Warning, (int)TraceId.ReadCleartool, "Could not find oid for element " + elementName); return; } lock (_oidsToCheck) _oidsToCheck.Remove(oid); lock (ElementsByOid) if (ElementsByOid.ContainsKey(oid)) { return; } Logger.TraceData(TraceEventType.Start | TraceEventType.Verbose, (int)TraceId.ReadCleartool, "Start reading " + (isDir ? "directory" : "file") + " element", elementName); var element = new Element(elementName, isDir) { Oid = oid }; lock (ElementsByOid) ElementsByOid[oid] = element; List <string> versionStrings; lock (cleartool) versionStrings = cleartool.Lsvtree(elementName); foreach (string versionString in versionStrings) { // there is a first "version" for each branch, without a version number if (!_isFullVersionRegex.IsMatch(versionString)) { continue; } Logger.TraceData(TraceEventType.Verbose, (int)TraceId.ReadCleartool, "Creating version", versionString); string[] versionPath = versionString.Split(new[] { '\\' }, StringSplitOptions.RemoveEmptyEntries); string branchName = versionPath[versionPath.Length - 2]; int versionNumber = int.Parse(versionPath[versionPath.Length - 1]); ElementBranch branch; if (!element.Branches.TryGetValue(branchName, out branch)) { // a new branch always start from the last seen version of the parent branch ElementVersion branchingPoint = null; if (versionPath.Length > 2) { branchingPoint = element.Branches[versionPath[versionPath.Length - 3]].Versions.Last(); } branch = new ElementBranch(element, branchName, branchingPoint); element.Branches[branchName] = branch; } bool added = AddVersionToBranch(branch, versionNumber, isDir, null, cleartool); if (!added) { // versions was too recent if (branch.Versions.Count == 0) { // do not leave an empty branch element.Branches.Remove(branchName); } // versions are retrieved in order of creation only within a branch : // we still may have eligible versions on a parent branch, so we must continue } } Logger.TraceData(TraceEventType.Stop | TraceEventType.Verbose, (int)TraceId.ReadCleartool, "Stop reading element", elementName); }
public NamedVersion Add(ElementVersion version) { return(Add(version, null, true)); }
public Reference(ElementVersion version) { ElementOid = version.Element.Oid; BranchName = version.Branch.BranchName; VersionNumber = version.VersionNumber; }
private static void AddVersion(List <ChangeSet> changeSets, ElementVersion version) { // used either for search or for new ChangeSet var changeSet = new ChangeSet(version.AuthorName, version.AuthorLogin, version.Branch.BranchName, version.Date); if (changeSets.Count == 0) { changeSet.Add(version); changeSets.Add(changeSet); return; } int index = changeSets.BinarySearch(changeSet, _timeComparer); if (index >= 0) { changeSets[index].Add(version); return; } index = ~index; // index of first element bigger if (index == changeSets.Count) { // so even the last one is not bigger ChangeSet candidate = changeSets[index - 1]; if (version.Date <= candidate.FinishTime.AddSeconds(MAX_DELAY)) { candidate.Add(version); } else { changeSet.Add(version); changeSets.Add(changeSet); } return; } if (index == 0) { ChangeSet candidate = changeSets[0]; if (version.Date >= candidate.StartTime.AddSeconds(-MAX_DELAY)) { candidate.Add(version); } else { changeSet.Add(version); changeSets.Insert(0, changeSet); } return; } DateTime lowerBound = changeSets[index - 1].FinishTime; DateTime upperBound = changeSets[index].StartTime; if (version.Date <= lowerBound.AddSeconds(MAX_DELAY) && version.Date < upperBound.AddSeconds(-MAX_DELAY)) { changeSets[index - 1].Add(version); return; } if (version.Date > lowerBound.AddSeconds(MAX_DELAY) && version.Date >= upperBound.AddSeconds(-MAX_DELAY)) { changeSets[index].Add(version); return; } if (version.Date > lowerBound.AddSeconds(MAX_DELAY) && version.Date < upperBound.AddSeconds(-MAX_DELAY)) { changeSet.Add(version); changeSets.Insert(index, changeSet); return; } // last case : we should merge the two ChangeSets (that are now "linked" by the version we are adding) changeSets[index - 1].Add(version); foreach (var v in changeSets[index].Versions) { changeSets[index - 1].Add(v.Version); } changeSets.RemoveAt(index); }
private static bool IsLatestMerge(ElementVersion fromVersion, ElementVersion toVersion) { return(!fromVersion.MergesTo.Any(v => v.Branch == toVersion.Branch && v.VersionNumber > toVersion.VersionNumber) && !toVersion.MergesFrom.Any(v => v.Branch == fromVersion.Branch && v.VersionNumber > fromVersion.VersionNumber)); }
private void ProcessMerge(ChangeSet changeSet, ElementVersion fromVersion, ElementVersion toVersion, bool isToVersionInChangeSet, HashSet <ElementVersion> lostVersions) { if (lostVersions.Contains(fromVersion) || lostVersions.Contains(toVersion)) { return; } var from = fromVersion.Branch.BranchName; var to = toVersion.Branch.BranchName; // for now we only merge back to parent branch, assuming other merges are cherry-picking string fromParent; if (!_globalBranches.TryGetValue(from, out fromParent) || fromParent != to) { return; } // since version 0 is identical to branching point : not interesting if (fromVersion.VersionNumber == 0) { return; } // handle several merges with a common end : we consider only the latest if (!IsLatestMerge(fromVersion, toVersion)) { return; } var key = new Tuple <string, string>(from, to); MergeInfo mergeInfo; if (!_merges.TryGetValue(key, out mergeInfo)) { mergeInfo = new MergeInfo(from, to); _merges.Add(key, mergeInfo); } // a version may be seen several times if it appears under a new name, // or if a move of files happened in several steps, needing to re-create them (instead of simply moving them) if (isToVersionInChangeSet) { if (mergeInfo.SeenToVersions.Contains(toVersion)) { return; } mergeInfo.SeenToVersions.Add(toVersion); // either the fromVersion has already been seen, and toVersion is in MissingToVersions // or we add fromVersion to MissingFromVersions ChangeSet fromChangeSet; if (mergeInfo.MissingToVersions.TryGetValue(toVersion, out fromChangeSet)) { mergeInfo.MissingToVersions.Remove(toVersion); var missingInChangeSet = mergeInfo.MissingToVersionsByChangeSet[fromChangeSet]; missingInChangeSet.Remove(toVersion); if (missingInChangeSet.Count == 0) { mergeInfo.MissingToVersionsByChangeSet.Remove(fromChangeSet); mergeInfo.Merges[fromChangeSet] = changeSet; } } else { mergeInfo.MissingFromVersions[fromVersion] = changeSet; } } else { if (mergeInfo.SeenFromVersions.Contains(fromVersion)) { return; } mergeInfo.SeenFromVersions.Add(fromVersion); // either toVersion has already been seen, and fromVersion is in MissingFromVersions // or we add toVersion to MissingToVersions ChangeSet toChangeSet; if (mergeInfo.MissingFromVersions.TryGetValue(fromVersion, out toChangeSet)) { mergeInfo.MissingFromVersions.Remove(fromVersion); ChangeSet existingTo; if (!mergeInfo.Merges.TryGetValue(changeSet, out existingTo) || existingTo.Id < toChangeSet.Id) { mergeInfo.Merges[changeSet] = toChangeSet; } } else { mergeInfo.MissingToVersions[toVersion] = changeSet; mergeInfo.MissingToVersionsByChangeSet.AddToCollection(changeSet, toVersion); } } }
private void ReadVersion(string version, List <ElementVersion> newVersions, Cleartool cleartool) { Match match = _versionRegex.Match(version); if (!match.Success) { Logger.TraceData(TraceEventType.Warning, (int)TraceId.ReadCleartool, "Could not parse '" + version + "' as a clearcase version"); return; } string elementName = match.Groups[1].Value; bool isDir; string oid; lock (cleartool) oid = cleartool.GetOid(elementName, out isDir); if (string.IsNullOrEmpty(oid)) { Logger.TraceData(TraceEventType.Warning, (int)TraceId.ReadCleartool, "Could not find oid for element " + elementName); return; } lock (_oidsToCheck) _oidsToCheck.Remove(oid); Element element; lock (ElementsByOid) if (!ElementsByOid.TryGetValue(oid, out element)) { element = new Element(elementName, isDir) { Oid = oid }; ElementsByOid.Add(oid, element); } else if (element.Name != elementName) { // the element is now seen with a different name in the currently used view Logger.TraceData(TraceEventType.Information, (int)TraceId.ReadCleartool, string.Format("element with oid {0} has a different name : now using {1} instead of {2}", oid, elementName, element.Name)); element.Name = elementName; } string[] versionPath = match.Groups[2].Value.Split(new[] { '\\' }, StringSplitOptions.RemoveEmptyEntries); string branchName = versionPath[versionPath.Length - 2]; int versionNumber = int.Parse(versionPath[versionPath.Length - 1]); // since we call ourself recursively to check the previous version, we first check the recursion end condition ElementBranch branch; if (element.Branches.TryGetValue(branchName, out branch) && branch.Versions.Count > 0 && branch.Versions.Last().VersionNumber >= versionNumber) { // already read return; } Logger.TraceData(TraceEventType.Start | TraceEventType.Verbose, (int)TraceId.ReadCleartool, "Start reading version", version); string previousVersion; lock (cleartool) previousVersion = cleartool.GetPredecessor(version); int previousVersionNumber = -1; if (previousVersion == null) { if (branchName != "main" || versionNumber != 0) { throw new Exception("Failed to retrieve predecessor of " + version); } branch = new ElementBranch(element, branchName, null); element.Branches[branchName] = branch; } else { ReadVersion(elementName + "@@" + previousVersion, newVersions, cleartool); string[] parts = previousVersion.Split('\\'); previousVersionNumber = int.Parse(parts[parts.Length - 1]); } if (!element.Branches.TryGetValue(branchName, out branch)) { if (versionNumber != 0) { // we should have completed in ReadVersion(elementName + "@@" + previousVersion) throw new Exception("Could not complete branch " + branchName); } ElementVersion branchingPoint = null; if (versionPath.Length > 2) { string parentBranchName = versionPath[versionPath.Length - 3]; ElementBranch parentBranch; if (!element.Branches.TryGetValue(parentBranchName, out parentBranch) || (branchingPoint = parentBranch.Versions.FirstOrDefault(v => v.VersionNumber == previousVersionNumber)) == null) { throw new Exception("Could not complete branch " + parentBranchName); } } branch = new ElementBranch(element, branchName, branchingPoint); element.Branches[branchName] = branch; } bool added = AddVersionToBranch(branch, versionNumber, isDir, newVersions, cleartool); if (!added && branch.Versions.Count == 0) { // do not leave an empty branch element.Branches.Remove(branchName); } Logger.TraceData(TraceEventType.Stop | TraceEventType.Verbose, (int)TraceId.ReadCleartool, "Stop reading version", version); }
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); }
/// <summary> /// Semantic of the file parameter is that the "directory" part (if present) is the path relative to the global clearcase root /// the file itself should therefore be in the working directory /// (although we could cheat using '/' for the root vs '\' for the actual file path) /// </summary> /// <param name="file"></param> public void ReadFile(string file) { string root = ""; int pos = file.LastIndexOf('/'); if (pos != -1) { root = file.Substring(0, pos + 1).Replace('/', '\\'); file = file.Substring(pos + 1); } Logger.TraceData(TraceEventType.Start | TraceEventType.Information, (int)TraceId.ReadExport, "Start reading export file", file); TextReader reader = new StreamReader(file); string line; string currentElementName = null; Element currentElement = null; ElementBranch currentBranch = null; ElementVersion currentVersion = null; List <Tuple <ElementVersion, string, int, bool> > currentElementMerges = new List <Tuple <ElementVersion, string, int, bool> >(); Match match; int lineNb = 0; int missingCommentChars = 0; string currentComment = null; // hack around end of lines string eol; while ((line = ReadLine(reader, out eol)) != null) { lineNb++; if (missingCommentChars > 0) { Debug.Assert(currentVersion != null); currentComment += line; missingCommentChars -= line.Length; if (missingCommentChars < 0) { throw new Exception(file + ", line " + lineNb + " : Unexpected comment length"); } if (missingCommentChars > 0) { currentComment += eol; missingCommentChars -= eol.Length; } if (missingCommentChars == 0) { currentVersion.Comment = string.Intern(currentComment); currentComment = null; } continue; } if (line == "ELEMENT_BEGIN") { currentElementName = null; currentBranch = null; currentElement = null; currentVersion = null; currentElementMerges.Clear(); continue; } if (currentElement == null && (match = _elementNameRegex.Match(line)).Success) { currentElementName = root + match.Groups[1].Value; currentElement = new Element(currentElementName, false); // no directories in export files Elements.Add(currentElement); Logger.TraceData(TraceEventType.Start | TraceEventType.Verbose, (int)TraceId.ReadExport, "Start reading element", currentElementName); continue; } if (line == "ELEMENT_END") { if (currentElement == null) { throw new Exception(file + ", line " + lineNb + " : Unexpected ELEMENT_END before it was named"); } foreach (var merge in currentElementMerges) { var version = merge.Item1; var other = currentElement.GetVersion(merge.Item2, merge.Item3); if (other == null || version.Date > _originDate) { // skip merges to or from skipped versions (that were too recent) continue; } (merge.Item4 ? version.MergesTo : version.MergesFrom).Add(other); } Logger.TraceData(TraceEventType.Stop | TraceEventType.Verbose, (int)TraceId.ReadExport, "Stop reading element", currentElementName); continue; } if (line == "VERSION_BEGIN" || line == "VERSION_END") { currentVersion = null; continue; } if (currentElement != null && currentVersion == null && (match = _versionIdRegex.Match(line)).Success) { string[] branchPath = match.Groups[1].Value.Split('\\'); string branchName = branchPath[branchPath.Length - 1]; if (currentBranch == null || (currentBranch.BranchName != branchName && !currentElement.Branches.TryGetValue(branchName, out currentBranch))) { if (branchName != "main") { throw new Exception(file + ", line " + lineNb + " : Unexpected branch " + branchName); } currentBranch = new ElementBranch(currentElement, branchName, null); currentElement.Branches[branchName] = currentBranch; } currentVersion = new ElementVersion(currentBranch, int.Parse(match.Groups[2].Value)); currentBranch.Versions.Add(currentVersion); Logger.TraceData(TraceEventType.Verbose, (int)TraceId.ReadExport, "Creating version", currentVersion); continue; } if (currentVersion != null && (match = _userRegex.Match(line)).Success) { currentVersion.AuthorLogin = string.Intern(match.Groups[1].Value); continue; } if (currentVersion != null && (match = _userNameRegex.Match(line)).Success) { currentVersion.AuthorName = string.Intern(match.Groups[1].Value); continue; } if (currentVersion != null && (match = _timeRegex.Match(line)).Success) { currentVersion.Date = _epoch.AddSeconds(long.Parse(match.Groups[1].Value)); if (currentVersion.Date > _originDate) { Logger.TraceData(TraceEventType.Information, (int)TraceId.ReadExport, string.Format("Skipping version {0} : {1} > {2}", currentVersion, currentVersion.Date, _originDate)); currentBranch.Versions.Remove(currentVersion); } continue; } if (currentVersion != null && (match = _labelRegex.Match(line)).Success) { var label = string.Intern(match.Groups[1].Value); if (_labelFilter.ShouldKeep(label)) { currentVersion.Labels.Add(label); } continue; } if (currentVersion != null && (match = _commentRegex.Match(line)).Success) { currentComment = match.Groups[2].Value; missingCommentChars = int.Parse(match.Groups[1].Value) - currentComment.Length; if (missingCommentChars > 0) { currentComment += eol; missingCommentChars -= eol.Length; } if (missingCommentChars == 0 && currentComment.Length > 0) { currentVersion.Comment = string.Intern(currentComment); currentComment = null; } continue; } if (currentVersion != null && (match = _subBranchRegex.Match(line)).Success) { string branchName = match.Groups[1].Value; if (currentElement.Branches.ContainsKey(branchName)) { throw new Exception(file + ", line " + lineNb + " : Duplicated branch " + branchName); } currentElement.Branches[branchName] = new ElementBranch(currentElement, branchName, currentVersion); continue; } if (currentVersion != null && (match = _mergeRegex.Match(line)).Success) { bool mergeTo = match.Groups[1].Success; // Groups[i].Value is the last capture : ok here string branchCapture = match.Groups[2].Value; string branchName = string.IsNullOrEmpty(branchCapture) ? "main" : branchCapture.Substring(0, branchCapture.Length - 1); int versionNumber = int.Parse(match.Groups[3].Value); // not interested in merges from same branch if (branchName != currentBranch.BranchName) { currentElementMerges.Add(new Tuple <ElementVersion, string, int, bool>(currentVersion, branchName, versionNumber, mergeTo)); } continue; } } Logger.TraceData(TraceEventType.Start | TraceEventType.Information, (int)TraceId.ReadExport, "Stop reading export file", file); }