public ElementVersion(ElementBranch branch, int versionNumber) { Branch = branch; VersionNumber = versionNumber; MergesFrom = new List <ElementVersion>(); MergesTo = new List <ElementVersion>(); Labels = new List <string>(); }
public void Fixup(ElementBranch branch) { Branch = branch; MergesFrom = _rawMergesFrom == null ? new List <ElementVersion>() : _rawMergesFrom.Select(r => Element.GetVersion(r.BranchName, r.VersionNumber)).ToList(); _rawMergesFrom = null; MergesTo = _rawMergesTo == null ? new List <ElementVersion>() : _rawMergesTo.Select(r => Element.GetVersion(r.BranchName, r.VersionNumber)).ToList(); _rawMergesTo = null; if (Labels == null) { Labels = new List <string>(); } }
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); }
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); }
/// <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); }
public DirectoryVersion(ElementBranch branch, int versionNumber) : base(branch, versionNumber) { Content = new List <KeyValuePair <string, Element> >(); }