Spent most of the day not doing what I should've been doing. I have a number of balls in the air at the moment and it feels like I've just added a spinning plate act at the same time...noisy disaster may ensue. Right now I'm working on another Codeplex release, working on a private build of some beta bits for the Insiders, getting our bugs migrated to the right place so we can get cracking on ASP.NET v.Next, getting the notes together for the last meeting (before the next one happens!) as well as getting the Hands-On-Labs ready for this thing...All of which are pretty  much due right now...oh and I just got out of a multi-month relationship with my now ex-girlfriend...so let's say my time is now exactly my own right now. (Ideally I'd be sitting in a little boat in the Caribbean for a couple of weeks but alas...). Anyway in the heart of this perfect storm I managed to do a bit of fiddling with code...Essentially I'm putting a method together to help us make quicker, less onerous Codeplex releases (we want to get as much stuff there as often as possible). I've been writing (and I've written about it before) a little directory comparer tool which I'm about to expand into adding changed items into TFS (for Codeplex pushes)...for various reasons the current way TFS makes us do this is a bit problematic for us...which meant we were using a python based tool which was 1. a bit flaky (poor errors) and 2. unmaintainable since of all the languages used in my team, Python ain't one...

Well, here's some code for the most recent incarnation (solution for follow)...it's getting a few add-ons like the ability to ignore certain directories / file extensions, multi-part configuration system and just a few general performance fixes.

Oh, and next week I plan to take a deep dive into the ASP.NET Page framework, giving an overly detailed guided tour of how it does it's stuff...

using System;

using System.Collections.Generic;

using System.Collections;

using System.Collections.Specialized;

using System.Text;

using System.IO;

 

namespace MergeDirs

{

    class Program

    {

        #region Sample Config

        /* Sample code for default config creation       

        * {           ConfigurationGroup cg = new ConfigurationGroup();

            cg.ConfigurationItems.Add( new Config() { SourceDirectory = "source1", DestinationDirectory = "dest1", ClearDestination = true, ExtensionsToExclude = new List<string>(new string[] { "vssscc", "dll", "pdb", "vspscc" }), DirectoriesToExclude = new List<string>(new string[] { "bin" }) });

            cg.ConfigurationItems.Add(new Config() { SourceDirectory = "source2", DestinationDirectory = "dest2", ClearDestination = true, ExtensionsToExclude = new List<string>(new string[] { "vssscc", "dll", "pdb", "vspscc" }), DirectoriesToExclude = new List<string>(new string[] { "bin" }) });

            cg.Serialize(@"C:\TestConfig\ConfigGroup.xml");

            return;*/

 

 

        #endregion

 

        private ConfigurationGroup Cfg { get; set; }

        private static string ConfigFile = string.Empty;

        static void Main(string[] args)

        {

            Program pgm = new Program();

            pgm.Run(args);

        }

 

 

        public void Run(string[] args)

        {

            Cfg = new ConfigurationGroup();

            if (args.Length > 0)

            {

                foreach (string argument in args)

                {

                    string tokenName = argument.Substring(1, 1);

                    switch (tokenName)

                    {

                        case ("f"):

                            ConfigFile = args[0].Substring(3).Trim();

                            break;

                    }

                }

 

            }

            Cfg.Deserialize(ConfigFile);

            if (Cfg == null)

            {

                Console.WriteLine("No config file at " + ConfigFile + " and no arguments...I can't guess!");

                Console.ReadLine();

                return;

            }

            foreach (Config cfg in Cfg.ConfigurationItems)

            {

                if (!string.IsNullOrEmpty(cfg.SourceDirectory) && !string.IsNullOrEmpty(cfg.DestinationDirectory))

                {

                    if (Directory.Exists(cfg.SourceDirectory))

                    {

 

                        if (!Directory.Exists(cfg.DestinationDirectory))

                            Directory.CreateDirectory(cfg.DestinationDirectory);

                        if (!Directory.Exists(cfg.DestinationDirectory))

                            throw new IOException("Failed to create directory:" + cfg.DestinationDirectory);

 

                        CompareDirs(cfg.SourceDirectory, cfg.DestinationDirectory, cfg.ClearDestination, cfg.ExtensionsToExclude, cfg.DirectoriesToExclude, cfg.ExtensionsToLeave);

 

                    }

                    else

                    {

                        Console.WriteLine("Both source and destination directories must already exist!!");

                    }

                }

                else

                {

                    Console.WriteLine("You must specify both source and destination directories!");

                }

            }

            Console.WriteLine("Finished!");

#if DEBUG

            Console.ReadLine();

#endif

        }

 

 

        private bool CompareDirs(string sourceDir, string destDir, bool clearRight, List<string> extensionsToExclude, List<string> directoriesToExclude, List<string> extensionsToLeave)

        {

            List<string> srcFileNames = new List<string>(GetFSItems(new DirectoryInfo(sourceDir).GetFiles()));

            List<string> dstFileNames = new List<string>(GetFSItems(new DirectoryInfo(destDir).GetFiles()));

            foreach (string fileName in srcFileNames)

            {

                //Console.WriteLine(string.Format("Comparing:{0} and {1}", Path.Combine(sourceDir, fileName), Path.Combine(destDir, fileName)));

                bool excludeFile = false;

                foreach (string excludeExt in extensionsToExclude)

                {

                    if (fileName.EndsWith("." + excludeExt))

                    {

                        excludeFile = true;

                        break;

                    }

                }

                if (excludeFile)

                    continue;

                if (!dstFileNames.Contains(fileName))

                {

                    File.Copy(Path.Combine(sourceDir, fileName), Path.Combine(destDir, fileName));

                    Console.WriteLine("Adding:" + Path.Combine(destDir, fileName));

                }

                else

                {

                    DateTime srcInf = new FileInfo(Path.Combine(sourceDir, fileName)).LastWriteTime;

                    DateTime dstInf = new FileInfo(Path.Combine(destDir, fileName)).LastWriteTime;

                    if (dstInf != srcInf)

                    {

                        File.Copy(Path.Combine(sourceDir, fileName), Path.Combine(destDir, fileName), true);

                        Console.WriteLine("Overwriting:" + Path.Combine(destDir, fileName));

                    }

                }

            }

            foreach (string fileName in dstFileNames)

            {

                string extension = fileName.Substring(fileName.LastIndexOf('.') + 1);

                if (!srcFileNames.Contains(fileName) || extensionsToExclude.Contains(extension))

                {

                    if (extensionsToLeave.Contains(extension))

                    {

                        Console.WriteLine("Leaving:" + fileName);

                        continue;

                    }

                    else

                    {

                        File.Delete(Path.Combine(destDir, fileName));

                        Console.WriteLine("Deleteing:" + fileName);

                    }

                }

            }

            List<string> sourceNames = new List<string>(GetFSItems(new DirectoryInfo(sourceDir).GetDirectories()));

            List<string> destNames = new List<string>(GetFSItems(new DirectoryInfo(destDir).GetDirectories()));

            foreach (string dirName in sourceNames)

            {

                if (directoriesToExclude.Contains(dirName))

                {

                    Console.WriteLine("Skipping: " + dirName);

                    continue;

                }

                //Console.WriteLine(string.Format("Comparing:{0} and {1}", Path.Combine(sourceDir, dirName), Path.Combine(destDir, dirName)));

                if (!destNames.Contains(dirName))

                {

                    string newDestDir = Path.Combine(destDir, dirName);

                    Directory.CreateDirectory(newDestDir);

                    Console.WriteLine("Adding:" + Path.Combine(destDir, dirName));

                    CompareDirs(Path.Combine(sourceDir, dirName), newDestDir, clearRight, extensionsToExclude, directoriesToExclude, extensionsToLeave);

                }

                else

                {

                    CompareDirs(Path.Combine(sourceDir, dirName), Path.Combine(destDir, dirName), clearRight, extensionsToExclude, directoriesToExclude, extensionsToLeave);

                }

            }

 

            foreach (string dirName in destNames)

            {

                if (!sourceNames.Contains(dirName) || directoriesToExclude.Contains(dirName))

                {

                    Directory.Delete(Path.Combine(destDir, dirName), true);

                    Console.WriteLine("Deleteing:" + Path.Combine(destDir, dirName));

                }

            }

            return false;

        }

 

        private IEnumerable<string> GetFSItems(FileSystemInfo[] dirInfos)

        {

            foreach (FileSystemInfo dinf in dirInfos)

            {

                yield return dinf.Name;

            }

        }

 

    }

}