Update(NuGitRepository repository) { if (repository == null) { throw new ArgumentNullException("repository"); } var lockDependencies = new List <LockDependency>(); Traverse( repository.Workspace, repository.ReadDotNuGit().Dependencies, repository, new Dictionary <GitRepositoryName, GitCommitName>() { { repository.Name, new GitCommitName("HEAD") } }, new HashSet <GitRepositoryName>() { repository.Name }, (d, r) => { lockDependencies.Add(new LockDependency(d.Url, d.CommitName, r.GetCommitId())); }); repository.WriteDotNuGitLock(lockDependencies); }
Restore(NuGitRepository repository, bool exact) { var workspace = repository.Workspace; var lockDependencies = repository.ReadDotNuGitLock(); foreach (var d in lockDependencies) { using (LogicalOperation.Start($"Restoring {d.Url.RepositoryName} to {d.CommitName} ({d.CommitId})")) { var name = d.Url.RepositoryName; var r = workspace.FindRepository(name); if (r == null) { Clone(workspace.RootPath, d.Url); r = workspace.GetRepository(name); } var head = r.GetCommitId(new GitCommitName("HEAD")); var isCheckedOutToExact = head == d.CommitId; var isCheckedOutToDescendent = r.IsAncestor(d.CommitId, head); var hasUncommittedChanges = r.HasUncommittedChanges(); var isCommitNameAtCommitId = r.GetCommitId(d.CommitName) == d.CommitId; if (!exact && isCheckedOutToExact && hasUncommittedChanges) { Trace.TraceInformation($"Already checked out with uncommitted changes"); } else if (!exact && isCheckedOutToExact) { Trace.TraceInformation($"Already checked out"); } else if (!exact && isCheckedOutToDescendent && hasUncommittedChanges) { Trace.TraceInformation($"Already checked out to descendent with uncommitted changes"); } else if (!exact && isCheckedOutToDescendent) { Trace.TraceInformation($"Already checked out to descendent"); } else if (r.HasUncommittedChanges()) { Trace.TraceError("Uncommitted changes"); throw new UserException($"Uncommitted changes in {name}"); } else if (isCheckedOutToExact) { Trace.TraceInformation($"Already checked out"); } else if (isCommitNameAtCommitId) { CheckOut(r, d.CommitName); } else { CheckOut(r, d.CommitId); } } } }
CheckOut(NuGitRepository repository, GitCommitName commit) { using (LogicalOperation.Start($"Checking out {commit}")) { repository.Checkout(commit); } }
Install(NuGitRepository repository, IEnumerable <Dependency> dependencies) { Guard.NotNull(repository, nameof(repository)); Guard.NotNull(dependencies, nameof(dependencies)); var sln = repository.FindVisualStudioSolution(); if (sln == null) { throw new UserException("No Visual Studio solution found in repo"); } var oldFolderIds = sln.SolutionFolders .Where(f => IsNuGitFolder(f.Name)) .ToDictionary(f => f.Name, f => f.Id); DeleteNuGitFolders(sln); foreach (var dependency in dependencies) { var name = dependency.Url.RepositoryName; var folderName = NuGitFolderPrefix + name; oldFolderIds.TryGetValue(folderName, out string folderId); Install(repository, sln, name, folderName, folderId); } sln.Save(); }
FindDependencyProjects(NuGitRepository repository, VisualStudioSolution sln) { Guard.NotNull(repository, nameof(repository)); Guard.NotNull(sln, nameof(sln)); return (sln.ProjectReferences .Where(p => p.TypeId == VisualStudioProjectTypeIds.CSharp || p.TypeId == VisualStudioProjectTypeIds.CSharpNew) .Where(p => !IsTestProject(p)) .Where(p => !string.IsNullOrWhiteSpace(p.Location)) .Where(p => PathExtensions.IsDescendantOf(p.AbsoluteLocation, repository.Path)) .OrderBy(p => p.Name) .ToList()); }
Traverse( NuGitWorkspace workspace, IEnumerable <Dependency> dependencies, NuGitRepository requiredBy, IDictionary <GitRepositoryName, GitCommitName> checkedOut, ISet <GitRepositoryName> visited, Action <Dependency, NuGitRepository> onVisited ) { Guard.NotNull(workspace, nameof(workspace)); Guard.NotNull(dependencies, nameof(dependencies)); Guard.NotNull(checkedOut, nameof(checkedOut)); Guard.NotNull(visited, nameof(visited)); Guard.NotNull(onVisited, nameof(onVisited)); var unvisited = dependencies.Where(d => !visited.Contains(d.Url.RepositoryName)).ToList().AsReadOnly(); // // Clone any dependency repos that aren't present // foreach (var d in unvisited) { if (workspace.FindRepository(d.Url.RepositoryName) != null) { continue; } Clone(workspace.RootPath, d.Url); } // // Visit each dependency // foreach (var d in dependencies) { var name = d.Url.RepositoryName; var repo = workspace.GetRepository(name); var commit = d.CommitName; checkedOut.TryGetValue(name, out var checkedOutCommit); // // First visit wins // if (checkedOutCommit == null) { using (LogicalOperation.Start($"Restoring {d.Url.RepositoryName} to {d.CommitName}")) { var commitId = repo.GetCommitId(commit); var headId = repo.GetCommitId(); var isCheckedOutToCommit = headId == commitId; var hasUncommittedChanges = repo.HasUncommittedChanges(); if (repo.HasUncommittedChanges()) { Trace.TraceError("Uncommitted changes"); throw new UserException($"Uncommitted changes in {name}"); } else if (isCheckedOutToCommit) { Trace.TraceInformation($"Already checked out"); } else { CheckOut(repo, commit); } checkedOut.Add(name, commit); visited.Add(name); onVisited(d, repo); continue; } } // // Subsequent visits specifying different commits get a warning // if (commit != checkedOutCommit) { Trace.TraceWarning( StringExtensions.FormatInvariant( "{0} depends on {1}#{2} but #{3} has already been checked out", requiredBy.Name, name, commit, checkedOutCommit)); continue; } // // Subsequent visits specifying the same commit do nothing // } // // Recurse // foreach (var d in unvisited) { var name = d.Url.RepositoryName; var repo = workspace.FindRepository(name); Traverse( workspace, repo.ReadDotNuGit().Dependencies, repo, checkedOut, visited, onVisited); } }
Install( NuGitRepository repository, VisualStudioSolution sln, GitRepositoryName dependencyName, string folderName, string folderId ) { var workspace = repository.Workspace; var slnLocalPath = PathExtensions.GetPathFromAncestor(sln.Path, repository.Path); var slnLocalPathComponents = PathExtensions.Split(slnLocalPath); var slnToWorkspacePath = Path.Combine(Enumerable.Repeat("..", slnLocalPathComponents.Length).ToArray()); var dependencyRepository = workspace.GetRepository(dependencyName); var dependencySln = dependencyRepository.FindVisualStudioSolution(); if (dependencySln == null) { return; } var dependencyProjects = FindDependencyProjects(dependencyRepository, dependencySln); if (dependencyProjects.Count == 0) { return; } using (LogicalOperation.Start($"Installing projects from {dependencyName}")) { // TODO Consider configurations in each individual dependency project, not just the solution var configurationsInCommon = sln.SolutionConfigurations.Intersect(dependencySln.SolutionConfigurations) .OrderBy(s => s) .ToList(); var folder = folderId != null ? sln.AddSolutionFolder(folderName, folderId) : sln.AddSolutionFolder(folderName); foreach (var project in dependencyProjects) { using (LogicalOperation.Start($"Installing {project.Name}")) { var projectLocalPath = PathExtensions.GetPathFromAncestor(project.AbsoluteLocation, dependencyRepository.Path); // // Add reference to the dependency project // sln.AddProjectReference( project.TypeId, project.Name, Path.Combine(slnToWorkspacePath, dependencyName, projectLocalPath), project.Id); // // Put it in the dependency's solution folder // sln.AddNestedProject(project.Id, folder.Id); // // Add solution -> project configuration mappings // foreach (string configuration in configurationsInCommon) { sln.AddProjectConfiguration(project.Id, configuration, "ActiveCfg", configuration); sln.AddProjectConfiguration(project.Id, configuration, "Build.0", configuration); } } } } }