private bool ReplayRevisionAlt(Revision revision, GitWrapper git, LinkedList <Revision> labels) { var needCommit = false; var actionType = revision.Action.Type; // We don't worry about projects! <smile> if ( (!revision.Item.IsProject) && (actionType == VssActionType.Create) || (actionType == VssActionType.Edit) ) { FileLocation fileLocation; if (fileAnalyzer.SortedFileLocations.TryGetValue(revision.Item.PhysicalName, out fileLocation)) { var path = VssPathMapper.GetWorkingPath(repoPath, fileLocation.Path, inheritProjectDir ? "" : fileAnalyzer.vssRootProjectPath); logger.WriteLine("{0}: {1} revision {2}", fileLocation.Path, actionType, revision.Version); if (WriteRevisionTo(revision.Item.PhysicalName, revision.Version, path)) { // add file explicitly, so it is visible to subsequent git operations git.Add(path); needCommit = true; } } } return(needCommit); }
private bool ReplayChangeset(VssPathMapper pathMapper, Changeset changeset, GitWrapper git, LinkedList <Revision> labels) { var needCommit = false; foreach (Revision revision in changeset.Revisions) { if (workQueue.IsAborting) { break; } AbortRetryIgnore(delegate { if (fileAnalyzer == null) { needCommit |= ReplayRevision(pathMapper, revision, git, labels); } else { needCommit |= ReplayRevisionAlt(revision, git, labels); } }); } return(needCommit); }
private void SkipChangeset(VssPathMapper pathMapper, Changeset changeset) { foreach (Revision revision in changeset.Revisions) { if (workQueue.IsAborting) { break; } SkipRevision(pathMapper, revision); } }
private void ReplayChangeset(VssPathMapper pathMapper, Changeset changeset, LinkedList <Revision> labels) { foreach (Revision revision in changeset.Revisions) { if (workQueue.IsAborting) { break; } AbortRetryIgnore(delegate { ReplayRevision(pathMapper, revision, labels); }); } }
private bool WriteRevision(VssPathMapper pathMapper, VssActionType actionType, string physicalName, int version, string underProject, GitWrapper git) { var needCommit = false; var paths = pathMapper.GetFilePaths(physicalName, underProject); foreach (string path in paths) { logger.WriteLine("{0}: {1} revision {2}", path, actionType, version); if (WriteRevisionTo(physicalName, version, path)) { // add file explicitly, so it is visible to subsequent git operations git.Add(path); needCommit = true; } } return(needCommit); }
private void WriteRevision(VssPathMapper pathMapper, VssActionType actionType, string physicalName, int version, string underProject) { var paths = pathMapper.GetFilePaths(physicalName, underProject); if (!paths.Any()) { logger.WriteLine("WARNING: {0}: no physical path, {1} revision {2} skipped", physicalName, actionType, version); } foreach (string path in paths) { logger.WriteLine("{0}: {1} revision {2}", path, actionType, version); if (WriteRevisionTo(physicalName, version, path)) { // add file explicitly, so it is visible to subsequent vcs operations vcsWrapper.Add(path); } } }
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 void ExportToGit(string repoPath) { workQueue.AddLast(delegate(object work) { var stopwatch = Stopwatch.StartNew(); logger.WriteSectionSeparator(); LogStatus(work, "Initializing Git repository"); // create repository directory if it does not exist if (!Directory.Exists(repoPath)) { Directory.CreateDirectory(repoPath); } var git = new GitWrapper(repoPath, logger); git.CommitEncoding = commitEncoding; while (!git.FindExecutable()) { var button = MessageBox.Show("Git 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; } } if (!RetryCancel(delegate { git.Init(); })) { return; } if (commitEncoding.WebName != "utf-8") { AbortRetryIgnore(delegate { git.SetConfig("i18n.commitencoding", commitEncoding.WebName); }); } var pathMapper = new VssPathMapper(); // create mappings for root projects foreach (var rootProject in revisionAnalyzer.RootProjects) { var rootPath = VssPathMapper.GetWorkingPath(repoPath, rootProject.Path); pathMapper.SetProjectPath(rootProject.PhysicalName, rootPath, rootProject.Path); } // replay each changeset var changesetId = 1; var changesets = changesetBuilder.Changesets; var commitCount = 0; var tagCount = 0; var replayStopwatch = new Stopwatch(); var labels = new LinkedList<Revision>(); tagsUsed.Clear(); foreach (var changeset in changesets) { var changesetDesc = string.Format(CultureInfo.InvariantCulture, "changeset {0} from {1}", changesetId, changeset.DateTime); // replay each revision in changeset LogStatus(work, "Replaying " + changesetDesc); labels.Clear(); replayStopwatch.Start(); bool needCommit; try { needCommit = ReplayChangeset(pathMapper, changeset, git, labels); } finally { replayStopwatch.Stop(); } if (workQueue.IsAborting) { return; } // commit changes if (needCommit) { LogStatus(work, "Committing " + changesetDesc); if (CommitChangeset(git, changeset)) { ++commitCount; } } if (workQueue.IsAborting) { return; } // 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 { var tagName = GetTagFromLabel(labelName); var tagMessage = "Creating tag " + tagName; if (tagName != labelName) { tagMessage += " for label '" + labelName + "'"; } LogStatus(work, tagMessage); // annotated tags require (and are implied by) a tag message; // tools like Mercurial's git converter only import annotated tags var tagComment = label.Comment; if (string.IsNullOrEmpty(tagComment) && forceAnnotatedTags) { // use the original VSS label as the tag message if none was provided tagComment = labelName; } if (AbortRetryIgnore( delegate { git.Tag(tagName, label.User, GetEmail(label.User), tagComment, label.DateTime); })) { ++tagCount; } } } } ++changesetId; } stopwatch.Stop(); logger.WriteSectionSeparator(); logger.WriteLine("Git 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("Git time: {0:HH:mm:ss}", new DateTime(git.ElapsedTime.Ticks)); logger.WriteLine("Git commits: {0}", commitCount); logger.WriteLine("Git tags: {0}", tagCount); }); }
private bool ReplayChangeset(VssPathMapper pathMapper, Changeset changeset, GitWrapper git, LinkedList<Revision> labels) { var needCommit = false; foreach (Revision revision in changeset.Revisions) { if (workQueue.IsAborting) { break; } AbortRetryIgnore(delegate { needCommit |= ReplayRevision(pathMapper, revision, git, labels); }); } return needCommit; }
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); } }
private void ReplayChangeset(VssPathMapper pathMapper, Changeset changeset, LinkedList<Revision> labels) { foreach (Revision revision in changeset.Revisions) { if (workQueue.IsAborting) { break; } AbortRetryIgnore(delegate { ReplayRevision(pathMapper, revision, labels); }); } }
public void ExportToVcs(string repoPath, DateTime? continueAfter) { this.continueAfter = continueAfter; workQueue.AddLast(delegate(object work) { var stopwatch = Stopwatch.StartNew(); logger.WriteSectionSeparator(); LogStatus(work, "Initializing repository"); if (continueAfter != null && resetRepo) throw new ProcessException("Unable to continue sync and reset repo at the same time!", null, null); // 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; } } if (!RetryCancel(delegate { vcsWrapper.Init(resetRepo); })) { return; } AbortRetryIgnore(delegate { vcsWrapper.Configure(continueAfter == null); }); var pathMapper = new VssPathMapper(); // 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 var rootPath = repoPath; 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++; } // 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(CultureInfo.InvariantCulture, "changeset {0} from {1}", changesetId, changeset.DateTime); // replay each revision in changeset logger.WriteSectionSeparator(); LogStatus(work, "Replaying " + changesetDesc); labels.Clear(); replayStopwatch.Start(); try { ReplayChangeset(pathMapper, changeset, labels); 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 { 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; } } } } } 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); }); }
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 writeProject = false; bool 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? 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 writeProject = true; } } } 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); writeProject = true; } else { writeFile = true; } } if (writeProject && pathMapper.IsProjectRooted(target.PhysicalName)) { // create all contained subdirectories foreach (var projectInfo in pathMapper.GetAllProjects(target.PhysicalName)) { 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(target.PhysicalName)) { if (WriteRevision(pathMapper, actionType, fileInfo.PhysicalName, fileInfo.Version, target.PhysicalName, 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 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 WriteRevision(VssPathMapper pathMapper, VssActionType actionType, string physicalName, int version, string underProject, GitWrapper git) { var needCommit = false; var paths = pathMapper.GetFilePaths(physicalName, underProject); foreach (string path in paths) { logger.WriteLine("{0}: {1} revision {2}", path, actionType, version); if (WriteRevisionTo(physicalName, version, path)) { // add file explicitly, so it is visible to subsequent git operations git.Add(path); needCommit = true; } } return needCommit; }
public void ExportToGit(string repoPath) { workQueue.AddLast(delegate(object work) { var stopwatch = Stopwatch.StartNew(); logger.WriteSectionSeparator(); LogStatus(work, "Initializing Git repository"); // create repository directory if it does not exist if (!Directory.Exists(repoPath)) { Directory.CreateDirectory(repoPath); } var git = new GitWrapper(repoPath, logger); git.CommitEncoding = commitEncoding; while (!git.FindExecutable()) { var button = MessageBox.Show("Git 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; } } if (!RetryCancel(delegate { git.Init(); })) { return; } if (commitEncoding.WebName != "utf-8") { AbortRetryIgnore(delegate { git.SetConfig("i18n.commitencoding", commitEncoding.WebName); }); } var pathMapper = new VssPathMapper(); // create mappings for root projects foreach (var rootProject in revisionAnalyzer.RootProjects) { var rootPath = VssPathMapper.GetWorkingPath(repoPath, rootProject.Path); pathMapper.SetProjectPath(rootProject.PhysicalName, rootPath, rootProject.Path); } // replay each changeset var changesetId = 1; var changesets = changesetBuilder.Changesets; var commitCount = 0; var tagCount = 0; var replayStopwatch = new Stopwatch(); var labels = new LinkedList <Revision>(); tagsUsed.Clear(); foreach (var changeset in changesets) { var changesetDesc = string.Format(CultureInfo.InvariantCulture, "changeset {0} from {1}", changesetId, changeset.DateTime); // replay each revision in changeset LogStatus(work, "Replaying " + changesetDesc); labels.Clear(); replayStopwatch.Start(); bool needCommit; try { needCommit = ReplayChangeset(pathMapper, changeset, git, labels); } finally { replayStopwatch.Stop(); } if (workQueue.IsAborting) { return; } // commit changes if (needCommit) { LogStatus(work, "Committing " + changesetDesc); if (CommitChangeset(git, changeset)) { ++commitCount; } } if (workQueue.IsAborting) { return; } // 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 { var tagName = GetTagFromLabel(labelName); var tagMessage = "Creating tag " + tagName; if (tagName != labelName) { tagMessage += " for label '" + labelName + "'"; } LogStatus(work, tagMessage); // annotated tags require (and are implied by) a tag message; // tools like Mercurial's git converter only import annotated tags var tagComment = label.Comment; if (string.IsNullOrEmpty(tagComment) && forceAnnotatedTags) { // use the original VSS label as the tag message if none was provided tagComment = labelName; } if (AbortRetryIgnore( delegate { git.Tag(tagName, label.User, GetEmail(label.User), tagComment, label.DateTime); })) { ++tagCount; } } } } ++changesetId; } stopwatch.Stop(); logger.WriteSectionSeparator(); logger.WriteLine("Git 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("Git time: {0:HH:mm:ss}", new DateTime(git.ElapsedTime.Ticks)); logger.WriteLine("Git commits: {0}", commitCount); logger.WriteLine("Git tags: {0}", tagCount); }); }
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;