Click here to Skip to main content
12,400,830 members (63,130 online)
Click here to Skip to main content
Add your own
alternative version

Stats

43.5K views
510 downloads
24 bookmarked
Posted

Remove SCC information revisited.

, 1 Jan 2005 MIT
Rate this:
Please Sign up or sign in to vote.
Removes SCC information from a solution or project file. Works with solutions, C#, VB.NET, ASP.NET, deployment projects, and project templates.

Sample Image - RemoveSCCInfo2.jpg

Introduction

Like mcarbenay, I needed and still need to remove or change source control information for a project or solution. If you have ever done it, you know it's a pain even if it really shouldn't be. The way it is hard coded into the project and solution files is pretty much ridiculous and one can spend a long time trying to do such a simple thing, especially with web applications.

So of course, I looked to see if someone already got frustrated enough to make a nice tool that does it all for me. And there was one! You can get the corresponding article here. Unfortunately, that tool doesn't handle many project types. In particular, template projects (*.etp) are not processed. Also, the way web projects are processed works about two thirds of the time for me.

Following these observations, I downloaded the source to see if I could patch it to suit my needs and then give back my contribution. Well, things being as they are, I figured it would take me less time to code it from scratch, taking into account that I would like to support even more project types in the near future and easily upgrade the parsing code for any new VS.NET versions.

So here you have the result: a tiny 200 lines program that relies heavily on regular expressions. There's not much more to say about it than that. I think the code speaks for itself, so I simply copied it in the following section.

Using the code

#region Using directives

using System;
using System.Collections;
using System.Collections.Specialized;
using System.Diagnostics;
using System.DirectoryServices;
using System.IO;
using System.Text.RegularExpressions;

#endregion

namespace RemoveSCCInfo
{
    /// <SUMMARY>
    /// Utility class to remove SCC informations from solutions and project files.
    /// </SUMMARY>
    public sealed class SCCInfoRemover
    {
        #region Constructors

        /// <SUMMARY>
        /// Enforces singleton status.
        /// </SUMMARY>
        private SCCInfoRemover()
        {
        }

        #endregion

        #region Methods

        /// <SUMMARY>
        /// Removes the SCC informations from a solution or project file.
        /// If the path points to a solution file,
        /// any referenced project file will also be processed.
        /// </SUMMARY>
        /// <PARAM name="path">The full path
        ///      of the solution or project file.</PARAM>
        /// <PARAM name="backup">true if solution and project files
        /// will be copied to a backup directory, false otherwise.</PARAM>
        /// <EXCEPTION cref="ArgumentNullException">
        ///        <PARAMREF name="path" /> is a null reference or an empty string.
        /// </EXCEPTION>
        public static void RemoveSCCInfo(string path, bool backup)
        {
            if (path == null || path == "")
                throw new ArgumentNullException("path");

            if (!File.Exists(path))
                throw new FileNotFoundException("Cannot" + 
                " find the path specified.", path);

            if (Path.GetExtension(path).ToLower() == ".sln")
                CleanSolution(path, backup);
            else
                CleanProject(path, backup);
        }

        /// <SUMMARY>
        /// Removes SCC informations from a solution file.
        /// Any referenced project file will also be processed.
        /// </SUMMARY>
        /// <PARAM name="path">The full path of the solution file.</PARAM>
        /// <PARAM name="backup">true if solution and project
        /// files will be copied to a backup directory, false otherwise.</PARAM>
        /// <EXCEPTION cref="ArgumentNullException">
        ///  <PARAMREF name="path" /> is a null reference or an empty string.
        /// </EXCEPTION>
        private static void CleanSolution(string path, bool backup)
        {
            if (path == null || path == "")
                throw new ArgumentNullException("path");

            if (!File.Exists(path))
                throw new FileNotFoundException("Cannot" + 
                " find the path specified.", path);

            // Load solution file

            string solution;

            using (StreamReader sr = new StreamReader(path))
            {
                solution = sr.ReadToEnd();
            }

            // Parse SCC informations section, if any

            Regex regex = new Regex(@"
                \tGlobalSection\(SourceCodeControl\).*\r\n #begin of SCC section
                (
                (.+\r\n)*?             #any number of lines
                \t\tSccProjectUniqueName\d+\s*=\s*(?<name>.+)\r\n  #unique name
                (.+\r\n)*?             #any number of lines
                \t\tSccLocalPath\d+\s*=\s*(?<local>.+)\r\n         #local name
                )*                #rinse and repeat for all projects
                (.+\r\n)*?        #any number of lines
                \tEndGlobalSection\r\n  #end of SCC section
                ", RegexOptions.IgnoreCase | RegexOptions.Multiline | 
                   RegexOptions.ExplicitCapture | 
                   RegexOptions.IgnorePatternWhitespace);
            Match m = regex.Match(solution);

            if (m.Success)
            {
                // Wil contains referenced projects in SCC section
                // key : SccProjectUniqueName
                // value : SccLocalPath
                HybridDictionary projects = new HybridDictionary();

                Group nameGroup = m.Groups["name"];
                Group localGroup = m.Groups["local"];

                for (int i = 0; i < nameGroup.Captures.Count; i++)
                    projects.Add(nameGroup.Captures[i].Value.Replace(@"\\", 
                                 @"\"), localGroup.Captures[i].Value);

                string solutionDir = Path.GetDirectoryName(path);

                // Process referenced projects

                foreach (DictionaryEntry project in projects)
                {
                    string name = (string) project.Key;
                    string local = (string) project.Value;

                    // Check if web project
                    if (name.StartsWith("http://"))
                    {
                        // Try to use local name
                        if (!local.StartsWith("http://"))
                        {
                            int pos = name.LastIndexOf('/') + 1;
                            string fileName = 
                              name.Substring(pos, name.Length - pos);

                            CleanProject(Path.Combine(solutionDir, local + 
                               Path.DirectorySeparatorChar + fileName), backup);
                        }
                        else
                        {
                            // Local name is also web location,
                            // so use get local name by other means

                            local = GetLocalPath(name);

                            if (local != null)
                                CleanProject(local, backup);
                        }
                    }
                    else
                    {
                        // Local project
                        CleanProject(Path.Combine(solutionDir, name), backup);
                    }
                }

                if (backup)
                    BackupFiles(path);

                RemoveReadOnlyAttribute(path);

                using (StreamWriter sw = new StreamWriter(path, false))
                {
                    sw.Write(solution.Replace(m.Value, ""));
                }

                CleanSCCFiles(path, backup);
            }
        }

        /// <SUMMARY>
        /// Removes SCC informations from a solution file.
        /// </SUMMARY>
        /// <PARAM name="path">The full path of the project file.</PARAM>
        /// <PARAM name="backup">true if project files will be copied
        /// to a backup directory, false otherwise.</PARAM>
        /// <EXCEPTION cref="ArgumentNullException">
        ///        <PARAMREF name="path" /> is a null reference or an empty string.
        /// </EXCEPTION>
        private static void CleanProject(string path, bool backup)
        {
            if (path == null || path == "")
                throw new ArgumentNullException("path");

            if (!File.Exists(path))
                throw new FileNotFoundException("Cannot" + 
                  " find the path specified.", path);

            // Load project file

            string project;

            using (StreamReader sr = new StreamReader(path))
            {
                project = sr.ReadToEnd();
            }

            // Parse SCC informations section, if any

            Regex regex = new Regex(@"^.*Scc\w+\s*=\s*.+\r\n", 
              RegexOptions.IgnoreCase | RegexOptions.Multiline | 
              RegexOptions.ExplicitCapture | 
              RegexOptions.IgnorePatternWhitespace);
            project = regex.Replace(project, "");

            regex = new Regex(@"
                .*<SOURCECONTROLSETTINGS>\r\n    #begin SCC settings section
                (.+\r\n)*?                          #any number of lines
                .*</SOURCECONTROLSETTINGS>\r\n    #end SCC settings section
                ", RegexOptions.IgnoreCase | RegexOptions.Multiline | 
                   RegexOptions.ExplicitCapture | 
                   RegexOptions.IgnorePatternWhitespace);

            project = regex.Replace(project, "");

            if (backup)
                BackupFiles(path);

            RemoveReadOnlyAttribute(path);

            using (StreamWriter sw = new StreamWriter(path, false))
            {
                sw.Write(project);
            }

            CleanSCCFiles(path, backup);
        }

        /// <SUMMARY>
        /// Removes SCC related files, such as '*.scc', '*.vssscc' and '*.vspscc'
        /// </SUMMARY>
        /// <PARAM name="path">The full path
        /// of the solution or project directory.</PARAM>
        /// <PARAM name="backup">true if SCC related files
        /// will be copied to a backup directory, false otherwise.</PARAM>
        /// <EXCEPTION cref="ArgumentNullException">
        ///   <PARAMREF name="path" /> is a null reference or an empty string.
        /// </EXCEPTION>
        private static void CleanSCCFiles(string path, bool backup)
        {
            if (path == null || path == "")
                throw new ArgumentNullException("path");

            if (!File.Exists(path))
                throw new FileNotFoundException("Cannot" + 
                " find the path specified.", path);

            path = Path.GetDirectoryName(path);

            DeleteFiles(Directory.GetFiles(path, "*.scc"), backup);
            DeleteFiles(Directory.GetFiles(path, "*.vssscc"), backup);
            DeleteFiles(Directory.GetFiles(path, "*.vspscc"), backup);
        }

        /// <SUMMARY>
        /// Deletes a collection of files.
        /// </SUMMARY>
        /// <PARAM name="files">The files to be deleted.</PARAM>
        /// <PARAM name="backup">true if the files will be copied
        /// to a backup directory, false otherwise.</PARAM>
        /// <EXCEPTION cref="ArgumentNullException">
        ///        <PARAMREF name="files" /> is a null reference.
        /// </EXCEPTION>
        private static void DeleteFiles(string[] files, bool backup)
        {
            if (files == null)
                throw new ArgumentNullException("files");

            if (files.Length > 0)
            {
                if (backup)
                    BackupFiles(files);

                foreach (string file in files)
                {
                    RemoveReadOnlyAttribute(file);
                    File.Delete(file);
                }
            }
        }

        /// <SUMMARY>
        /// Backups one or more files.
        /// Files will be stored in a directory named 'Backup_YYYY-MM-DD'
        /// where YYYY-MM-DD is the current date in ISO format.
        /// </SUMMARY>
        /// <PARAM name="files">The files to backup.</PARAM>
        /// <EXCEPTION cref="ArgumentNullException">
        ///        <PARAMREF name="files" /> is a null reference.
        /// </EXCEPTION>
        private static void BackupFiles(params string[] files)
        {
            if (files == null)
                throw new ArgumentNullException("files");

            string backupDirName = Path.DirectorySeparatorChar + 
                   "Backup_" + DateTime.Now.ToString("yyyy-MM-dd") + 
                   Path.DirectorySeparatorChar;

            foreach (string file in files)
            {
                string dir = Path.GetDirectoryName(file) + backupDirName;

                Directory.CreateDirectory(dir);
                File.Copy(file, dir + Path.GetFileName(file), true);
            }
        }

        /// <SUMMARY>
        /// Removes the read-only attribute on a file.
        /// </SUMMARY>
        /// <PARAM name="path">The full path to the file.</PARAM>
        /// <EXCEPTION cref="ArgumentNullException">
        ///   <PARAMREF name="path" /> is a null reference or an empty string.
        /// </EXCEPTION>
        private static void RemoveReadOnlyAttribute(string path)
        {
            if (path == null || path == "")
                throw new ArgumentNullException("path");

            FileAttributes attributes = File.GetAttributes(path);

            if ((attributes & FileAttributes.ReadOnly) != 0)
                File.SetAttributes(path, attributes & (~FileAttributes.ReadOnly));
        }

        /// <SUMMARY>
        /// Gets the local path of a locally hosted web application.
        /// </SUMMARY>
        /// <PARAM name="url">The url of the application.</PARAM>
        /// <RETURNS>The local path of the locally hosted web application.
        /// A null value is returned if the local path
        /// cannot be obtained.</RETURNS>
        /// <EXCEPTION cref="ArgumentNullException">
        ///        <PARAMREF name="url" /> is a null reference or an empty string.
        /// </EXCEPTION>
        private static string GetLocalPath(string url)
        {
            if (url == null || url == "")
                throw new ArgumentNullException("url");

            string local = null;

            try
            {
                Uri uri = new Uri(url);

                string adsiPath = String.Format("IIS://{0}/W3SVC/1/Root{1}", 
                         uri.Host, Path.GetDirectoryName(uri.AbsolutePath));
                DirectoryEntry dirEntry = new DirectoryEntry(adsiPath);

                string wwwPath = dirEntry.Properties["Path"].Value.ToString();

                local = Path.Combine(wwwPath, Path.GetFileName(uri.AbsolutePath));
            }
            catch (Exception ex)
            {
                Trace.WriteLine(ex.Source);
                Trace.WriteLine(ex.Message);
                Trace.WriteLine(ex.StackTrace);
            }

            return local;
        }

        #endregion
    }
}

History

  • 1.0: supports C#, VB.NET, ASP.NET, deployment projects, and project templates.

License

This article, along with any associated source code and files, is licensed under The MIT License

Share

About the Author

Sebastien Lorion
Architect
Canada Canada
Sébastien Lorion is software architect as day job.

He is also a musician, actually singing outside the shower Smile | :)

He needs constant mental and emotional stimulation, so all of this might change someday ...

You may also be interested in...

Comments and Discussions

 
QuestionCan't get it to work for VS 2002 Pin
MabaitRoy12-Aug-08 20:23
memberMabaitRoy12-Aug-08 20:23 
AnswerRe: Can't get it to work for VS 2002 Pin
Sebastien Lorion12-Aug-08 21:04
memberSebastien Lorion12-Aug-08 21:04 
QuestionRe: Can't get it to work for VS 2002 Pin
MabaitRoy12-Aug-08 22:26
memberMabaitRoy12-Aug-08 22:26 
GeneralRe: Can't get it to work for VS 2002 Pin
MabaitRoy12-Aug-08 22:55
memberMabaitRoy12-Aug-08 22:55 
AnswerRe: Can't get it to work for VS 2002 Pin
Sebastien Lorion13-Aug-08 9:24
memberSebastien Lorion13-Aug-08 9:24 
QuestionDoes this work on VS2005? Pin
Keith Mallen21-Apr-08 9:33
memberKeith Mallen21-Apr-08 9:33 
AnswerRe: Does this work on VS2005? Pin
Sebastien Lorion22-Apr-08 15:40
memberSebastien Lorion22-Apr-08 15:40 
Generalgreat tool, but... Pin
Perica Zivkovic24-Oct-05 21:43
memberPerica Zivkovic24-Oct-05 21:43 
GeneralRe: great tool, but... Pin
Sébastien Lorion6-Nov-05 6:45
memberSébastien Lorion6-Nov-05 6:45 
GeneralMan without fear Pin
Anonymous9-Aug-05 1:36
sussAnonymous9-Aug-05 1:36 
GeneralThanks and some observations Pin
andrew mark stone11-Apr-05 18:33
memberandrew mark stone11-Apr-05 18:33 
GeneralRe: Thanks and some observations Pin
Sébastien Lorion11-Apr-05 18:47
memberSébastien Lorion11-Apr-05 18:47 
Questions does it shows that much ? Pin
mcarbenay4-Jan-05 0:10
membermcarbenay4-Jan-05 0:10 
AnswerRe: s does it shows that much ? Pin
Sébastien Lorion4-Jan-05 0:21
memberSébastien Lorion4-Jan-05 0:21 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.160721.1 | Last Updated 2 Jan 2005
Article Copyright 2005 by Sebastien Lorion
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid