public VssProjectInfo MoveProjectFrom(VssItemName project, VssItemName subproject, string oldProjectSpec) { Debug.Assert(subproject.IsProject); var parentInfo = GetOrCreateProject(project); var subprojectInfo = GetOrCreateProject(subproject); subprojectInfo.Parent = parentInfo; return(subprojectInfo); }
public Revision(DateTime dateTime, string user, VssItemName item, int version, string comment, VssAction action) { this.dateTime = dateTime; this.user = user; this.item = item; this.version = version; this.comment = comment; this.action = action; }
private VssFileInfo GetOrCreateFile(VssItemName name) { VssFileInfo fileInfo; if (!fileInfos.TryGetValue(name.PhysicalName, out fileInfo)) { fileInfo = new VssFileInfo(name.PhysicalName, name.LogicalName); fileInfos[name.PhysicalName] = fileInfo; } return(fileInfo); }
private VssProjectInfo GetOrCreateProject(VssItemName name) { VssProjectInfo projectInfo; if (!projectInfos.TryGetValue(name.PhysicalName, out projectInfo)) { projectInfo = new VssProjectInfo(name.PhysicalName, name.LogicalName); projectInfos[name.PhysicalName] = projectInfo; } return(projectInfo); }
public VssItemInfo RenameItem(VssItemName name) { VssItemInfo itemInfo; if (name.IsProject) { itemInfo = GetOrCreateProject(name); } else { itemInfo = GetOrCreateFile(name); } itemInfo.LogicalName = name.LogicalName; return(itemInfo); }
public VssItemInfo RecoverItem(VssItemName project, VssItemName name) { var parentInfo = GetOrCreateProject(project); VssItemInfo itemInfo; if (name.IsProject) { var projectInfo = GetOrCreateProject(name); projectInfo.Parent = parentInfo; itemInfo = projectInfo; } else { var fileInfo = GetOrCreateFile(name); fileInfo.AddProject(parentInfo); parentInfo.AddItem(fileInfo); itemInfo = fileInfo; } return(itemInfo); }
public VssItemInfo DeleteItem(VssItemName project, VssItemName name) { var parentInfo = GetOrCreateProject(project); VssItemInfo itemInfo; if (name.IsProject) { var projectInfo = GetOrCreateProject(name); projectInfo.Parent = null; itemInfo = projectInfo; } else { var fileInfo = GetOrCreateFile(name); fileInfo.RemoveProject(parentInfo); parentInfo.RemoveItem(fileInfo); itemInfo = fileInfo; } return(itemInfo); }
public VssProjectInfo MoveProjectTo(VssItemName project, VssItemName subproject, string newProjectSpec) { var subprojectInfo = GetOrCreateProject(subproject); var lastSlash = newProjectSpec.LastIndexOf('/'); if (lastSlash > 0) { var newParentSpec = newProjectSpec.Substring(0, lastSlash); var parentInfo = ResolveProjectSpec(newParentSpec); if (parentInfo != null) { // propagate the destroyed flag from the new parent subprojectInfo.Parent = parentInfo; subprojectInfo.Destroyed |= parentInfo.Destroyed; } else { // if resolution fails, the target project has been destroyed // or is outside the set of projects being mapped subprojectInfo.Destroyed = true; } } return(subprojectInfo); }
public void ExportToVcs(string repoPath, DateTime?continueAfter) { if (resetRepo) { continueAfter = null; } this.continueAfter = continueAfter; workQueue.AddLast(delegate(object work) { var stopwatch = Stopwatch.StartNew(); logger.WriteSectionSeparator(); LogStatus(work, "Initializing repository"); // create repository directory if it does not exist if (!Directory.Exists(repoPath)) { if (continueAfter != null) { throw new ProcessException("Unable to continue: " + repoPath + " does not exist.", null, null); } Directory.CreateDirectory(repoPath); } string vcs = vcsWrapper.GetVcs(); while (!vcsWrapper.FindExecutable()) { var button = MessageBox.Show(vcs + " not found in PATH. " + "If you need to modify your PATH variable, please " + "restart the program for the changes to take effect.", "Error", MessageBoxButtons.RetryCancel, MessageBoxIcon.Error); if (button == DialogResult.Cancel) { workQueue.Abort(); return; } } bool makeInitialCommit = resetRepo; if (!RetryCancel(delegate { vcsWrapper.Init(resetRepo); })) { return; } AbortRetryIgnore(delegate { vcsWrapper.Configure(continueAfter == null); }); var pathMapper = new VssPathMapper(); if (UseProjectsFile) { string rootPath = repoPath; foreach (var rootProject in revisionAnalyzer.RootProjects) { string path = rootProject.RootPath; path = path.Replace("/", "\\"); if (path.StartsWith("\\")) { path = rootPath + path; } else { path = rootPath + "\\" + path; } pathMapper.SetProjectPath(rootProject.PhysicalName, path, rootProject.Path); } } else { // create mappings for root projects foreach (var rootProject in revisionAnalyzer.RootProjects) { // root must be repo path here - the path mapper uses paths relative to this one VssPathMapper vssPathMapper = new VssPathMapper(); var rootPath = repoPath; if (RemovePath != "") { if (rootProject.Path.StartsWith(RemovePath)) { string path = rootProject.Path.Replace(RemovePath, ""); path = path.Replace("/", "\\"); if (path.StartsWith("\\")) { rootPath = rootPath + path; } else { rootPath = rootPath + "\\" + path; } } else { var button = MessageBox.Show($"{RemovePath} not found in path, Do you wanne continue with all contint in the root folder.", "Error", MessageBoxButtons.OKCancel, MessageBoxIcon.Error); if (button == DialogResult.Cancel) { workQueue.Abort(); return; } } } pathMapper.SetProjectPath(rootProject.PhysicalName, rootPath, rootProject.Path); } } var changesets = changesetBuilder.Changesets; // create a log of all MoveFrom and MoveTo actions logger.WriteSectionSeparator(); logger.WriteLine("List of Move Operations"); logger.WriteSectionSeparator(); int changeSetNo = 0; int revisionNo = 0; foreach (var changeset in changesets) { foreach (var revision in changeset.Revisions) { var actionType = revision.Action.Type; VssItemName target = null; var namedAction = revision.Action as VssNamedAction; if (namedAction != null) { target = namedAction.Name; } switch (actionType) { case VssActionType.MoveFrom: var moveFromAction = (VssMoveFromAction)revision.Action; logger.WriteLine("{3}-{4}-{0}: MoveFrom {1} to {2}", revision.Item, moveFromAction.OriginalProject, target, changeSetNo, revisionNo); break; case VssActionType.MoveTo: var moveToAction = (VssMoveToAction)revision.Action; logger.WriteLine("{3}-{4}-{0}: MoveTo {1} from {2}", revision.Item, moveToAction.NewProject, target, changeSetNo, revisionNo); break; } revisionNo++; } changeSetNo++; //AdvancedTaskbar.SetPosition((uint)changeSetNo); } // replay each changeset logger.WriteSectionSeparator(); logger.WriteLine("Replaying Changesets"); var changesetId = 0; var commitCount = 0; var tagCount = 0; var replayStopwatch = new Stopwatch(); var labels = new LinkedList <Revision>(); tagsUsed.Clear(); bool found = continueAfter == null; foreach (var changeset in changesets) { ++changesetId; if (workQueue.IsAborting) { return; } if (!found) { if (continueAfter.Equals(changeset.DateTime)) { found = true; } SkipChangeset(pathMapper, changeset); continue; } var changesetDesc = string.Format("changeset {0} from {1:yyyy-MM-dd HH:mm:ss}", changesetId, changeset.DateTime); // replay each revision in changeset logger.WriteSectionSeparator(); LogStatus(work, "Replaying " + changesetDesc); labels.Clear(); replayStopwatch.Start(); try { ReplayChangeset(pathMapper, changeset, labels); if (makeInitialCommit) { vcsWrapper.Init(changeset, repoPath); makeInitialCommit = false; } vcsWrapper.NeedsCommit(); // to flush outstanding adds/deletes } finally { replayStopwatch.Stop(); } if (workQueue.IsAborting) { return; } // commit changes if (vcsWrapper.NeedsCommit()) { LogStatus(work, "Committing " + changesetDesc); if (CommitChangeset(changeset)) { ++commitCount; } } // create tags for any labels in the changeset if (labels.Count > 0) { foreach (Revision label in labels) { var labelName = ((VssLabelAction)label.Action).Label; if (string.IsNullOrEmpty(labelName)) { logger.WriteLine("NOTE: Ignoring empty label"); } else if (commitCount == 0) { logger.WriteLine("NOTE: Ignoring label '{0}' before initial commit", labelName); } else { //Datell B.V. Specific operation of adding folder structure to label. //It is optional by using the checkbox if (FolderBeforeLabel && label.Item.IsProject) { labelName = pathMapper.GetProjectPath(label.Item.PhysicalName) + "_" + labelName; labelName = labelName.Replace(repoPath, "").TrimStart('\\'); } var tagName = GetTagFromLabel(labelName); var tagMessage = "Creating tag " + tagName; if (tagName != labelName) { tagMessage += " for label '" + labelName + "'"; } LogStatus(work, tagMessage); // tags always get a tag message; var tagComment = label.Comment; if (string.IsNullOrEmpty(tagComment)) { // use the original VSS label as the tag message if none was provided tagComment = labelName; } if (AbortRetryIgnore( delegate { vcsWrapper.Tag(tagName, label.User, GetEmail(label.User), tagComment, label.DateTime); })) { ++tagCount; } } } } AdvancedTaskbar.SetPosition((uint)changesetId); } stopwatch.Stop(); if (!found) { logger.WriteLine("Cannot Sync: VSS changeset at {0} not found.", continueAfter); throw new ProcessException(string.Format("Cannot Sync: VSS changeset at {0} not found.", continueAfter), null, null); } logger.WriteSectionSeparator(); logger.WriteLine(vcs + " export complete in {0:HH:mm:ss}", new DateTime(stopwatch.ElapsedTicks)); logger.WriteLine("Replay time: {0:HH:mm:ss}", new DateTime(replayStopwatch.ElapsedTicks)); logger.WriteLine(vcs + " time: {0:HH:mm:ss}", new DateTime(vcsWrapper.ElapsedTime().Ticks)); logger.WriteLine(vcs + " commits: {0}", commitCount); logger.WriteLine(vcs + " tags: {0}", tagCount); }); }
public bool ProjectContainsLogicalName(VssItemName project, VssItemName name) { var parentInfo = GetOrCreateProject(project); return(parentInfo.ContainsLogicalName(name.LogicalName)); }
public VssItemInfo UnpinItem(VssItemName project, VssItemName name) { // unpinning restores the project to the list of // sharing projects, so it receives edits return(RecoverItem(project, name)); }
public void SetFileVersion(VssItemName name, int version) { VssFileInfo fileInfo = GetOrCreateFile(name); fileInfo.Version = version; }
public string TryToGetPhysicalNameContainedInProject(VssItemName project, VssItemName name) { var parentInfo = GetOrCreateProject(project); return(parentInfo.TryToGetPhysicalName(name.LogicalName)); }
private void SkipRevision(VssPathMapper pathMapper, Revision revision) { var actionType = revision.Action.Type; if (revision.Item.IsProject) { var project = revision.Item; var projectPath = pathMapper.GetProjectPath(project.PhysicalName); VssItemName target = null; string targetPath = null; var namedAction = revision.Action as VssNamedAction; if (namedAction != null) { target = namedAction.Name; if (projectPath != null) { targetPath = Path.Combine(projectPath, target.LogicalName); } } switch (actionType) { case VssActionType.Add: case VssActionType.Share: pathMapper.AddItem(project, target); break; case VssActionType.Recover: pathMapper.RecoverItem(project, target); break; case VssActionType.Delete: case VssActionType.Destroy: pathMapper.DeleteItem(project, target); break; case VssActionType.Rename: pathMapper.RenameItem(target); break; case VssActionType.MoveFrom: { var moveFromAction = (VssMoveFromAction)revision.Action; var isInside = pathMapper.IsInRoot(moveFromAction.OriginalProject); if (isInside) { pathMapper.MoveProjectFrom(project, target, moveFromAction.OriginalProject); } else { pathMapper.RecoverItem(project, target); } } break; case VssActionType.MoveTo: { var moveToAction = (VssMoveToAction)revision.Action; var isInside = pathMapper.IsInRoot(moveToAction.NewProject); if (!isInside) { pathMapper.DeleteItem(project, target); } } break; case VssActionType.Pin: { var pinAction = (VssPinAction)revision.Action; if (pinAction.Pinned) { pathMapper.PinItem(project, target); } else { pathMapper.UnpinItem(project, target); } } break; case VssActionType.Branch: { var branchAction = (VssBranchAction)revision.Action; pathMapper.BranchFile(project, target, branchAction.Source); } break; case VssActionType.Restore: pathMapper.AddItem(project, target); break; } } // item is a file, not a project else if (actionType == VssActionType.Edit || actionType == VssActionType.Branch) { pathMapper.SetFileVersion(revision.Item, revision.Version); } }
public VssItemInfo PinItem(VssItemName project, VssItemName name) { // pinning removes the project from the list of // sharing projects, so it no longer receives edits return(DeleteItem(project, name)); }
private bool ReplayRevision(VssPathMapper pathMapper, Revision revision, GitWrapper git, LinkedList <Revision> labels) { var needCommit = false; var actionType = revision.Action.Type; if (revision.Item.IsProject) { // note that project path (and therefore target path) can be // null if a project was moved and its original location was // subsequently destroyed var project = revision.Item; var projectName = project.LogicalName; var projectPath = pathMapper.GetProjectPath(project.PhysicalName); var projectDesc = projectPath; if (projectPath == null) { projectDesc = revision.Item.ToString(); logger.WriteLine("NOTE: {0} is currently unmapped", project); } VssItemName target = null; string targetPath = null; var namedAction = revision.Action as VssNamedAction; if (namedAction != null) { target = namedAction.Name; if (projectPath != null) { targetPath = Path.Combine(projectPath, target.LogicalName); } } bool isAddAction = false; bool writeFile = false; string writeProjectPhysicalName = null; VssItemInfo itemInfo = null; switch (actionType) { case VssActionType.Label: // defer tagging until after commit labels.AddLast(revision); break; case VssActionType.Create: // ignored; items are actually created when added to a project break; case VssActionType.Add: case VssActionType.Share: logger.WriteLine("{0}: {1} {2}", projectDesc, actionType, target.LogicalName); itemInfo = pathMapper.AddItem(project, target); isAddAction = true; break; case VssActionType.Recover: logger.WriteLine("{0}: {1} {2}", projectDesc, actionType, target.LogicalName); itemInfo = pathMapper.RecoverItem(project, target); isAddAction = true; break; case VssActionType.Delete: case VssActionType.Destroy: { logger.WriteLine("{0}: {1} {2}", projectDesc, actionType, target.LogicalName); itemInfo = pathMapper.DeleteItem(project, target); if (targetPath != null && !itemInfo.Destroyed) { if (target.IsProject) { if (Directory.Exists(targetPath)) { string successor = pathMapper.TryToGetPhysicalNameContainedInProject(project, target); if (successor != null) { // we already have another project with the same logical name logger.WriteLine("NOTE: {0} contains another directory named {1}; not deleting directory", projectDesc, target.LogicalName); writeProjectPhysicalName = successor; // rewrite this project because it gets deleted below } if (((VssProjectInfo)itemInfo).ContainsFiles()) { git.Remove(targetPath, true); needCommit = true; } else { // git doesn't care about directories with no files Directory.Delete(targetPath, true); } } } else { if (File.Exists(targetPath)) { // not sure how it can happen, but a project can evidently // contain another file with the same logical name, so check // that this is not the case before deleting the file if (pathMapper.TryToGetPhysicalNameContainedInProject(project, target) != null) { logger.WriteLine("NOTE: {0} contains another file named {1}; not deleting file", projectDesc, target.LogicalName); } else { File.Delete(targetPath); needCommit = true; } } } } } break; case VssActionType.Rename: { var renameAction = (VssRenameAction)revision.Action; logger.WriteLine("{0}: {1} {2} to {3}", projectDesc, actionType, renameAction.OriginalName, target.LogicalName); itemInfo = pathMapper.RenameItem(target); if (targetPath != null && !itemInfo.Destroyed) { var sourcePath = Path.Combine(projectPath, renameAction.OriginalName); if (target.IsProject ? Directory.Exists(sourcePath) : File.Exists(sourcePath)) { // renaming a file or a project that contains files? var projectInfo = itemInfo as VssProjectInfo; if (projectInfo == null || projectInfo.ContainsFiles()) { CaseSensitiveRename(sourcePath, targetPath, git.Move); needCommit = true; } else { // git doesn't care about directories with no files CaseSensitiveRename(sourcePath, targetPath, Directory.Move); } } else { logger.WriteLine("NOTE: Skipping rename because {0} does not exist", sourcePath); } } } break; case VssActionType.MoveFrom: // if both MoveFrom & MoveTo are present (e.g. // one of them has not been destroyed), only one // can succeed, so check that the source exists { var moveFromAction = (VssMoveFromAction)revision.Action; logger.WriteLine("{0}: Move from {1} to {2}", projectDesc, moveFromAction.OriginalProject, targetPath ?? target.LogicalName); var sourcePath = pathMapper.GetProjectPath(target.PhysicalName); var projectInfo = pathMapper.MoveProjectFrom( project, target, moveFromAction.OriginalProject); if (targetPath != null && !projectInfo.Destroyed) { if (sourcePath != null && Directory.Exists(sourcePath)) { if (projectInfo.ContainsFiles()) { git.Move(sourcePath, targetPath); needCommit = true; } else { // git doesn't care about directories with no files Directory.Move(sourcePath, targetPath); } } else { // project was moved from a now-destroyed project writeProjectPhysicalName = target.PhysicalName; } } } break; case VssActionType.MoveTo: { // handle actual moves in MoveFrom; this just does cleanup of destroyed projects var moveToAction = (VssMoveToAction)revision.Action; logger.WriteLine("{0}: Move to {1} from {2}", projectDesc, moveToAction.NewProject, targetPath ?? target.LogicalName); var projectInfo = pathMapper.MoveProjectTo( project, target, moveToAction.NewProject); if (projectInfo.Destroyed && targetPath != null && Directory.Exists(targetPath)) { // project was moved to a now-destroyed project; remove empty directory Directory.Delete(targetPath, true); } } break; case VssActionType.Pin: { var pinAction = (VssPinAction)revision.Action; if (pinAction.Pinned) { logger.WriteLine("{0}: Pin {1}", projectDesc, target.LogicalName); itemInfo = pathMapper.PinItem(project, target); } else { logger.WriteLine("{0}: Unpin {1}", projectDesc, target.LogicalName); itemInfo = pathMapper.UnpinItem(project, target); writeFile = !itemInfo.Destroyed; } } break; case VssActionType.Branch: { var branchAction = (VssBranchAction)revision.Action; logger.WriteLine("{0}: {1} {2}", projectDesc, actionType, target.LogicalName); itemInfo = pathMapper.BranchFile(project, target, branchAction.Source); // branching within the project might happen after branching of the file writeFile = true; } break; case VssActionType.Archive: // currently ignored { var archiveAction = (VssArchiveAction)revision.Action; logger.WriteLine("{0}: Archive {1} to {2} (ignored)", projectDesc, target.LogicalName, archiveAction.ArchivePath); } break; case VssActionType.Restore: { var restoreAction = (VssRestoreAction)revision.Action; logger.WriteLine("{0}: Restore {1} from archive {2}", projectDesc, target.LogicalName, restoreAction.ArchivePath); itemInfo = pathMapper.AddItem(project, target); isAddAction = true; } break; } if (targetPath != null) { if (isAddAction) { if (revisionAnalyzer.IsDestroyed(target.PhysicalName) && !database.ItemExists(target.PhysicalName)) { logger.WriteLine("NOTE: Skipping destroyed file: {0}", targetPath); itemInfo.Destroyed = true; } else if (target.IsProject) { Directory.CreateDirectory(targetPath); writeProjectPhysicalName = target.PhysicalName; } else { writeFile = true; } } if (writeProjectPhysicalName != null && pathMapper.IsProjectRooted(writeProjectPhysicalName)) { // create all contained subdirectories foreach (var projectInfo in pathMapper.GetAllProjects(writeProjectPhysicalName)) { logger.WriteLine("{0}: Creating subdirectory {1}", projectDesc, projectInfo.LogicalName); Directory.CreateDirectory(projectInfo.GetPath()); } // write current rev of all contained files foreach (var fileInfo in pathMapper.GetAllFiles(writeProjectPhysicalName)) { if (WriteRevision(pathMapper, actionType, fileInfo.PhysicalName, fileInfo.Version, writeProjectPhysicalName, git)) { // one or more files were written needCommit = true; } } } else if (writeFile) { // write current rev to working path int version = pathMapper.GetFileVersion(target.PhysicalName); if (WriteRevisionTo(target.PhysicalName, version, targetPath)) { // add file explicitly, so it is visible to subsequent git operations git.Add(targetPath); needCommit = true; } } } } // item is a file, not a project else if (actionType == VssActionType.Edit || actionType == VssActionType.Branch) { // if the action is Branch, the following code is necessary only if the item // was branched from a file that is not part of the migration subset; it will // make sure we start with the correct revision instead of the first revision var target = revision.Item; // update current rev pathMapper.SetFileVersion(target, revision.Version); // write current rev to all sharing projects WriteRevision(pathMapper, actionType, target.PhysicalName, revision.Version, null, git); needCommit = true; } return(needCommit); }
private bool ReplayRevision(VssPathMapper pathMapper, Revision revision, GitWrapper git, LinkedList <Revision> labels) { var needCommit = false; var actionType = revision.Action.Type; if (revision.Item.IsProject) { // note that project path (and therefore target path) can be // null if a project was moved and its original location was // subsequently destroyed var project = revision.Item; var projectPath = pathMapper.GetProjectPath(project.PhysicalName); var projectDesc = projectPath; if (projectPath == null) { projectDesc = revision.Item.ToString(); logger.WriteLine("NOTE: {0} is currently unmapped", project); } VssItemName target = null; string targetPath = null; if (revision.Action is VssNamedAction namedAction) { target = namedAction.Name; if (projectPath != null) { targetPath = Path.Combine(projectPath, target.LogicalName); } } var isAddAction = false; var writeProject = false; var writeFile = false; VssItemInfo itemInfo = null; switch (actionType) { case VssActionType.Label: // defer tagging until after commit labels.AddLast(revision); break; case VssActionType.Create: // ignored; items are actually created when added to a project break; case VssActionType.Add: case VssActionType.Share: logger.WriteLine("{0}: {1} {2}", projectDesc, actionType, target.LogicalName); itemInfo = pathMapper.AddItem(project, target); isAddAction = true; break; case VssActionType.Recover: logger.WriteLine("{0}: {1} {2}", projectDesc, actionType, target.LogicalName); itemInfo = pathMapper.RecoverItem(project, target); isAddAction = true; break; case VssActionType.Delete: case VssActionType.Destroy: { logger.WriteLine("{0}: {1} {2}", projectDesc, actionType, target.LogicalName); itemInfo = pathMapper.DeleteItem(project, target); if (targetPath != null && !itemInfo.Destroyed) { if (target.IsProject) { if (Directory.Exists(targetPath)) { if (((VssProjectInfo)itemInfo).ContainsFiles()) { git.Remove(targetPath, true); needCommit = true; } else { // git doesn't care about directories with no files Directory.Delete(targetPath, true); } } } else { if (File.Exists(targetPath)) { // not sure how it can happen, but a project can evidently // contain another file with the same logical name, so check // that this is not the case before deleting the file if (pathMapper.ProjectContainsLogicalName(project, target)) { logger.WriteLine("NOTE: {0} contains another file named {1}; not deleting file", projectDesc, target.LogicalName); } else { File.Delete(targetPath); needCommit = true; } } } } } break; case VssActionType.Rename: { var renameAction = (VssRenameAction)revision.Action; logger.WriteLine("{0}: {1} {2} to {3}", projectDesc, actionType, renameAction.OriginalName, target.LogicalName); itemInfo = pathMapper.RenameItem(target); if (targetPath != null && !itemInfo.Destroyed) { var sourcePath = Path.Combine(projectPath, renameAction.OriginalName); if (target.IsProject ? Directory.Exists(sourcePath) : File.Exists(sourcePath)) { // renaming a file or a project that contains files? if (!(itemInfo is VssProjectInfo projectInfo) || (projectInfo.ContainsFiles() && projectInfo.Items.All(x => !x.Destroyed))) { CaseSensitiveRename(sourcePath, targetPath, git.Move); needCommit = true; } else { // git doesn't care about directories with no files CaseSensitiveRename(sourcePath, targetPath, CaseSensitiveDirectoryMove); } } else { logger.WriteLine("NOTE: Skipping rename because {0} does not exist", sourcePath); } } } break;