internal void OnSolutionRenamedFile(string oldName, string newName, VSRENAMEFILEFLAGS flags) { if (!IsActive) return; _solutionDirectory = _solutionFile = null; // Get new data after this rename using (GitSccContext git = new GitSccContext(Context)) { if (!git.IsUnversioned(oldName)) { try { git.SafeWcMoveFixup(oldName, newName); } catch (IOException) { if (_delayedMove == null) _delayedMove = new List<FixUp>(); _delayedMove.Add(new FixUp(oldName, newName)); RegisterForSccCleanup(); } MarkDirty(new string[] { oldName, newName }, true); } } Monitor.ScheduleGlyphUpdate(SolutionFilename); }
/// <summary> /// Fixes working copies which are invalidated by a rename operation /// </summary> /// <param name="rgszMkOldNames"></param> /// <param name="rgszMkNewNames"></param> private void FixWorkingCopyAfterRenames(string[] rgszMkOldNames, string[] rgszMkNewNames) { if (rgszMkNewNames == null || rgszMkOldNames == null || rgszMkOldNames.Length != rgszMkNewNames.Length) return; for (int i = 0; i < rgszMkOldNames.Length; i++) { string oldName = rgszMkOldNames[i]; string newName = rgszMkNewNames[i]; if (string.IsNullOrEmpty(oldName) || !GitItem.IsValidPath(oldName)) continue; oldName = GitTools.GetNormalizedFullPath(oldName); newName = GitTools.GetNormalizedFullPath(newName); string oldDir; string newDir; bool safeRename =false; if (!Directory.Exists(newName)) { // Try to fix the parent of the new item oldDir = GitTools.GetNormalizedDirectoryName(oldName); newDir = GitTools.GetNormalizedDirectoryName(newName); } else { // The item itself is the directory to fix oldDir = oldName; newDir = newName; safeRename = true; } if (Directory.Exists(oldDir)) continue; // Nothing to fix up string parent = GitTools.GetNormalizedDirectoryName(oldDir); if (!Directory.Exists(parent)) { continue; // We can't fix up more than one level at this time // We probably fix it with one of the following renames; as paths are included too } GitItem item = StatusCache[oldDir]; if (!item.IsVersioned && item.Status.State != GitStatus.Missing) continue; // Item was not cached as versioned or now-missing (Missing implicits Versioned) StatusCache.MarkDirty(oldDir); StatusCache.MarkDirty(newDir); item = StatusCache[oldDir]; if (item.Status.State != GitStatus.Missing) continue; GitItem newItem = StatusCache[newDir]; using (GitSccContext git = new GitSccContext(Context)) { GitStatusEventArgs wa = git.SafeGetEntry(newDir); string newParent = GitTools.GetNormalizedDirectoryName(newDir); if (wa != null) continue; // Not an unexpected WC root else if (!GitTools.IsBelowManagedPath(newDir)) continue; // Not a wc root at all git.SafeWcDirectoryCopyFixUp(oldDir, newDir, safeRename); // Recreate the old WC directory _delayedDeletes.Add(oldDir); // Delete everything in the old wc when done // TODO: Once Git understands true renames, fixup the renames in the delayed hook RegisterForSccCleanup(); // We have all files of the old wc directory unversioned in the new location now StatusCache.MarkDirtyRecursive(oldDir); StatusCache.MarkDirtyRecursive(newDir); } } }
/// <summary> /// Called when a file is removed from a project /// </summary> /// <param name="project">The project.</param> /// <param name="filename">The filename.</param> /// <param name="flags">The flags.</param> internal void OnProjectFileRemoved(IVsSccProject2 project, string filename, VSREMOVEFILEFLAGS flags) { SccProjectData data; if (!_projectMap.TryGetValue(project, out data)) return; // Not managed by us data.RemovePath(filename); if (!IsActive) return; // Let the other SCC package manage it MarkDirty(filename); if (GitUpdatesDisabled || !StatusCache[filename].IsVersioned) return; // Don't bother using (GitSccContext git = new GitSccContext(Context)) { if (File.Exists(filename)) { // The file was only removed from the project. We should not touch it // Some projects delete the file before (C#) and some after (C++) calling OnProjectFileRemoved if (_delayedDelete == null) _delayedDelete = new List<string>(); if (!_delayedDelete.Contains(filename)) _delayedDelete.Add(filename); RegisterForSccCleanup(); return; } if (git.IsUnversioned(filename)) return; git.SafeDeleteFile(filename); } }
/// <summary> /// Called when a file in a project is renamed /// </summary> /// <param name="project">The SCC project.</param> /// <param name="oldName">The old name.</param> /// <param name="newName">The new name.</param> /// <param name="flags">The flags.</param> internal void OnProjectRenamedFile(IVsSccProject2 project, string oldName, string newName, VSRENAMEFILEFLAGS flags) { SccProjectData data; if (!_projectMap.TryGetValue(project, out data)) return; // Not managed by us else data.CheckProjectRename(project, oldName, newName); // Just to be sure (should be handled by other event) data.RemovePath(oldName); data.AddPath(newName); if (!IsActive) return; using (GitSccContext git = new GitSccContext(Context)) { if (!git.IsUnversioned(oldName)) { if (!Directory.Exists(newName)) // Fails if the new name is a directory! git.SafeWcMoveFixup(oldName, newName); } MarkDirty(new string[] { oldName, newName }, true); } }
internal void OnBeforeRemoveDirectory(IVsSccProject2 project, string fullPath, out bool ok) { ok = true; SccProjectData data; if (!_projectMap.TryGetValue(project, out data)) return; // Not managed by us else if (!IsActive) return; if (_backupMap.ContainsKey(fullPath)) { // Don't backup twice string oldBackup = _backupMap[fullPath]; _backupMap.Remove(fullPath); using (GitSccContext git = new GitSccContext(this)) { git.DeleteDirectory(oldBackup); } } else { GitItem dir = StatusCache[fullPath]; if (!dir.IsVersioned) return; // Nothing to do for us } using (GitSccContext git = new GitSccContext(this)) { _backupMap.Add(fullPath, git.MakeBackup(fullPath)); } RegisterForSccCleanup(); }
internal void OnBeforeSolutionRenameFile(string oldName, string newName, VSQUERYRENAMEFILEFLAGS flags, out bool ok) { ok = true; if (!IsActive) return; //if (IsProjectFileOrSolution(oldName)) //{ // // TODO: Is enlisted -> Ask user! //} using (GitSccContext git = new GitSccContext(Context)) { if (!git.CouldAdd(newName, GitNodeKind.File)) { ok = false; return; } if (git.IsUnversioned(oldName)) return; } }
/// <summary> /// Called just before a file in a project is renamed /// </summary> /// <param name="project">The SCC project.</param> /// <param name="oldName">The old name.</param> /// <param name="newName">The new name.</param> /// <param name="flags">The flags.</param> /// <param name="ok">if set to <c>true</c> [ok].</param> internal void OnBeforeProjectRenameFile(IVsSccProject2 project, string oldName, string newName, VSQUERYRENAMEFILEFLAGS flags, out bool ok) { ok = true; if (!_projectMap.ContainsKey(project)) return; // Not managed by us if (!IsActive) return; using (GitSccContext git = new GitSccContext(Context)) { if (!git.CouldAdd(newName, GitNodeKind.File)) { ok = false; return; } if (git.IsUnversioned(oldName)) return; } }
internal void OnSccCleanup(CommandEventArgs e) { _registeredSccCleanup = false; if ((_ensureIcons || _syncMap) && IsActive) { // Enable our custom glyphs when we are set active IVisualGitSolutionExplorerWindow solutionExplorer = GetService<IVisualGitSolutionExplorerWindow>(); if (solutionExplorer != null) solutionExplorer.EnableVisualGitIcons(true); } if (_syncMap) { _syncMap = false; foreach (SccProjectData pd in _projectMap.Values) pd.Load(); } if (_delayedDelete != null) { List<string> files = _delayedDelete; _delayedDelete = null; using (GitSccContext git = new GitSccContext(Context)) { foreach (string file in files) { if (!File.Exists(file)) { git.SafeDeleteFile(file); MarkDirty(file); } } } } if (_delayedMove != null) { List<FixUp> files = _delayedMove; _delayedMove = null; using (GitSccContext git = new GitSccContext(Context)) { foreach (FixUp fu in files) { if (!git.IsUnversioned(fu.From) && git.IsUnversioned(fu.To)) { git.SafeWcMoveFixup(fu.From, fu.To); } } } } if (_backupMap.Count > 0) { using (GitSccContext git = new GitSccContext(Context)) { foreach (KeyValuePair<string, string> dir in _backupMap) { string originalDir = dir.Key; string backupDir = dir.Value; if (!Directory.Exists(backupDir)) continue; // No backupdir, we can't delete or move it if (Directory.Exists(originalDir)) { // The original has not been deleted by visual studio, must be an exclude. git.DeleteDirectory(backupDir); } else { // Original is gone, must be a delete, put back backup so we can git-delete it GitSccContext.RetriedRename(backupDir, originalDir); // Use retried rename, to prevent virus-scanner locks git.WcDelete(originalDir); } } } _backupMap.Clear(); } }
/// <summary> /// /// </summary> /// <param name="cProjects"></param> /// <param name="cDirectories"></param> /// <param name="rgpProjects"></param> /// <param name="rgFirstIndices"></param> /// <param name="rgpszMkDocuments"></param> /// <param name="rgFlags"></param> /// <returns></returns> /// <remarks>Deny a query only if allowing the operation would compromise your stable state</remarks> public int OnAfterAddDirectoriesEx(int cProjects, int cDirectories, IVsProject[] rgpProjects, int[] rgFirstIndices, string[] rgpszMkDocuments, VSADDDIRECTORYFLAGS[] rgFlags) { if (rgpProjects == null || rgpszMkDocuments == null) return VSConstants.E_POINTER; for (int i = 0; i < cDirectories; i++) { string dir = rgpszMkDocuments[i]; if (string.IsNullOrEmpty(dir) || !GitItem.IsValidPath(dir)) continue; dir = GitTools.GetNormalizedFullPath(dir); StatusCache.MarkDirty(dir); GitItem item = StatusCache[dir]; if (!item.Exists || !item.IsDirectory || !GitTools.IsBelowManagedPath(dir)) continue; if ((DateTime.UtcNow - GetCreated(item)) > new TimeSpan(0, 1, 0)) continue; // Directory is older than one minute.. Not just copied using (GitSccContext git = new GitSccContext(Context)) { // Ok; we have a 'new' directory here.. Lets check if VS broke the Git working copy GitStatusEventArgs entry = git.SafeGetEntry(dir); if (entry != null && entry.NodeKind == GitNodeKind.Directory) // Entry exists, valid dir continue; // VS Added a versioned dir below our project -> Unversion the directory to allow adding files // Don't unversion the directory if the parent is not versioned string parentDir = GitTools.GetNormalizedDirectoryName(dir); if (parentDir == null || !GitTools.IsBelowManagedPath(parentDir)) continue; git.UnversionRecursive(dir); } } for (int iProject = 0, iDir=0; (iProject < cProjects) && (iDir < cDirectories); iProject++) { int iLastDirectoryThisProject = (iProject < cProjects - 1) ? rgFirstIndices[iProject + 1] : cDirectories; IVsSccProject2 sccProject = rgpProjects[iProject] as IVsSccProject2; bool track = SccProvider.TrackProjectChanges(sccProject); for (; iDir < iLastDirectoryThisProject; iDir++) { if (!track) continue; string dir = rgpszMkDocuments[iDir]; if (string.IsNullOrEmpty(dir) || !GitItem.IsValidPath(dir)) continue; dir = GitTools.GetNormalizedFullPath(dir); if (sccProject != null) SccProvider.OnProjectDirectoryAdded(sccProject, dir, rgFlags[iDir]); } } return VSConstants.S_OK; }
protected bool GitCanAddPath(string fullpath, GitNodeKind nodeKind) { using (GitSccContext git = new GitSccContext(Context)) { // Determine if we could add fullname if (!git.CouldAdd(fullpath, nodeKind)) { if (git.BelowAdminDir(fullpath)) _batchErrors.Add(string.Format(Resources.GitPathXBlocked, fullpath)); else _batchErrors.Add(string.Format(Resources.PathXBlocked, fullpath)); return false; } else return true; } }
internal void OnSccCleanup(CommandEventArgs e) { _registeredSccCleanup = false; _collectHints = false; _fileHints.Clear(); _fileOrigins.Clear(); if (_delayedDeletes.Count > 0) { using (GitSccContext git = new GitSccContext(Context)) { foreach (string d in _delayedDeletes.ToArray()) { _delayedDeletes.Remove(d); git.WcDelete(d); } } } }