Click here to Skip to main content
15,885,213 members
Articles / Programming Languages / C#

SVN Shelve

Rate me:
Please Sign up or sign in to vote.
3.53/5 (9 votes)
23 Sep 2008CPOL5 min read 55.6K   423   25  
A simple utility to shelve your projects under SVN.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.IO;
using System.Configuration;
using System.Xml.Serialization;

using SvnShelve.Model;

namespace SvnShelve.Forms
{
    public partial class ShelveForm : FormBase
    {
        public ShelveForm()
        {
            InitializeComponent();
        }


        private void button2_Click(object sender, EventArgs e)
        {
        }

        private string _projectPath;

        public string ProjectPath
        {
            get { return _projectPath; }
            set { _projectPath = value; }
        }
	
        private void Form1_Load(object sender, EventArgs e)
        {
            _path.Text = this.ProjectPath;

            _fileList.BasePath = ProjectPath;
            _fileList.EnableDiff = true;
            _fileList.EnableLog = true;
            _fileList.EnableRepositoryBrowsing = true;
            _fileList.EnableOpenFolder = true;

            FolderInfo folderInfo = SVN.GetFolderInfo(ProjectPath);
            _url.Text = folderInfo.Url;
            _revision.Text = folderInfo.Revision.ToString();
            timer1.Enabled = true;
            this.Cursor = System.Windows.Forms.Cursors.WaitCursor;
        }

        private List<FileStatusInfo> _files = new List<FileStatusInfo>();

        private void LoadModificationList()
        {
            List<string> topFolders = new List<string>();
            bool hasConflictedFiles = false;

            if (IsAuthenticated)
            {

                FileStatusInfo[] fileList = SVN.GetStatus(this.ProjectPath);
                foreach (FileStatusInfo file in fileList)
                {
                    switch (file.State)
                    {
                        case FileStatusInfo.StatusEnum.Conflicted:
                            hasConflictedFiles = true;
                            break;
                        case FileStatusInfo.StatusEnum.Added:
                        case FileStatusInfo.StatusEnum.Deleted:
                        case FileStatusInfo.StatusEnum.Modified:
                            string checkFolder = null;
                            string[] parts = file.FilePath.Split('\\');
                            if (parts.Length > 1 && file.FilePath != this.ProjectPath)
                            {
                                string folder = parts[0];
                                if (!topFolders.Contains(folder))
                                {
                                    topFolders.Add(folder);
                                    checkFolder = ProjectPath + "\\" + folder;
                                }
                            }
                            else
                            {
                                if (!topFolders.Contains(string.Empty))
                                {
                                    topFolders.Add(string.Empty);
                                    checkFolder = ProjectPath;
                                }
                            }
                            if (checkFolder != null)
                            {
                                FolderInfo folderInfo = SVN.GetFolderInfo(checkFolder);
                                string url = folderInfo.Url;
                                if (!url.EndsWith("/"))
                                    url += "/";
                                //this._fileList.Add(FileStatusInfo.StatusEnum.None, url);
                            }
                            _files.Add(file);
                            this._fileList.Add(file.State, file.FilePath);
                            break;
                    }
                }

                List<string> urls = new List<string>();
                foreach (string folder in topFolders)
                {
                    FolderInfo folderInfo = null;
                    if (folder.Length > 0)
                        folderInfo = SVN.GetFolderInfo(ProjectPath + "\\" + folder);
                    else
                        folderInfo = SVN.GetFolderInfo(ProjectPath);

                    string url = folderInfo.Url.TrimEnd(folder.ToCharArray());
                    if (!url.EndsWith("/"))
                        url += "/";
                    if (!urls.Contains(url))
                        urls.Add(url);
                }
                string errorMessage = string.Empty;
                if (hasConflictedFiles)
                {
                    errorMessage = string.Format("Folder '{0}' has conflicted files.", this.ProjectPath);
                    MessageBox.Show(errorMessage, "Folder is invalid to shelve", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    this._description.Enabled = false;
                }
                else
                {
                    if (urls.Count > 1)
                    {
                        foreach (string urlItem in urls)
                        {
                            errorMessage += "Url: " + urlItem + "\n";
                        }
                    }


                    if (errorMessage.Length > 0)
                    {
                        MessageBox.Show("Multiple Urls found. Only one supported.\n" + errorMessage +
                            "\n\nSelect folder, which has the same folder structure in SVN repository", "Folder is invalid to shelve", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        this._description.Enabled = false;
                    }
                }
            }
        }

        private void _shelve_Click(object sender, EventArgs e)
        {
            using (StatusForm status = new StatusForm())
            {
                status.FormClosed += new FormClosedEventHandler(Shelve_FormClosed);
                status.DoWork += new EventHandler<DoWorkEventArgs>(status_DoWork);
                status.Text = string.Format("Shelve '{0}'", _path.Text);
                status.ShowDialog(this);
            }
        }

        void status_DoWork(object sender, DoWorkEventArgs e)
        {
            Shelve(sender as StatusForm);
        }

        private void Shelve(StatusForm status)
        {
            System.Collections.Specialized.StringCollection recent = Properties.Settings.Default.RecentDescriptionList;
            if (recent == null)
                recent = new System.Collections.Specialized.StringCollection();

            if (!recent.Contains(_description.Text))
            {
                recent.Add(_description.Text);
                Properties.Settings.Default.RecentDescriptionList = recent;
                Properties.Settings.Default.Save();
            }

            FolderInfo folderInfo = SVN.GetFolderInfo(ProjectPath);
            string targetFolder = string.Format("{0}\\{1}\\",
                SVN.ShelveLocation,
                Guid.NewGuid().ToString().Replace("-", string.Empty));
            try
            {
                status.Add("", "");
                status.Add("Step 1", "******************************************************************");
                status.Add("Copy Files", "");
                foreach (FileStatusInfo file in _files)
                {
                    folderInfo.AddFile(file);
                    string sourceFile = null;
                    if (file.FilePath == "(root)")
                        sourceFile = this.ProjectPath;
                    else
                        sourceFile = this.ProjectPath + "\\" + file.FilePath;
                    if (file.State != FileStatusInfo.StatusEnum.Deleted)
                    {
                        if (SVN.IsWorkingCopy(sourceFile))
                        {
                            FolderInfo fileInfo = SVN.GetFolderInfo(sourceFile);
                            if (!fileInfo.Url.StartsWith(folderInfo.Url))
                            {
                                throw new ArgumentException(string.Format("SVN Location mismatch:\n" +
                                    "File: '{0}'\n" +
                                    "Url: '{1}'\n" +
                                    "SVN Url: '{2}'.\n" +
                                    "You have to shelve files, which are located under the same url as folder you have selected",
                                    file.FilePath, fileInfo.Url, folderInfo.Url));
                            }
                        }
                        string targetFile = targetFolder + file.FilePath;
                        string dir = Path.GetDirectoryName(targetFile);
                        if (!Directory.Exists(dir))
                            Directory.CreateDirectory(dir);

                        if (!file.IsFolder)
                        {
                            status.Add("Copy", sourceFile);
                            File.Copy(sourceFile, targetFile, true);
                            string svnPath = Path.GetDirectoryName(sourceFile) + "\\.svn";
                            if(!Directory.Exists(svnPath))
                                svnPath = Path.GetDirectoryName(sourceFile) + "\\_svn";
                            if (Directory.Exists(svnPath))
                            {
                                string baseFile = svnPath + "\\text-base\\" + Path.GetFileName(sourceFile) + ".svn-base";
                                if (!File.Exists(baseFile))
                                {
                                    baseFile = svnPath + "\\prop-base\\" + Path.GetFileName(sourceFile) + ".svn-base";
                                }

                                if (File.Exists(baseFile))
                                {
                                    File.Copy(baseFile, targetFile + ".shelf-base", true);
                                }
                            }
                        }
                        else
                        {
                            status.Add("Create", sourceFile);
                        }
                    }
                }

                folderInfo.Description = _description.Text;
                XmlSerializer s = new XmlSerializer(typeof(FolderInfo));
                using (StreamWriter infoFile = File.CreateText(targetFolder + "\\info.xml"))
                {
                    s.Serialize(infoFile, folderInfo);
                    infoFile.Close();
                }

                DialogResult result = MessageBox.Show("Do you want to restore Project folder to the point where it was before you made changes?",
                    "Confirm", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1);
                if (result == DialogResult.Yes)
                {
                    status.Add("", "");
                    status.Add("Step 2", "******************************************************************");
                    status.Add("Revert", this.ProjectPath);
                    string[] lines = SVN.Revert(this.ProjectPath);
                    status.PrintSvnResult(lines);

                    status.Add("", "");
                    status.Add("Step 3", "******************************************************************");
                    status.Add("Cleanup", this.ProjectPath);
                    foreach (FileStatusInfo file in _files)
                    {
                        try
                        {
                            switch (file.State)
                            {
                                case FileStatusInfo.StatusEnum.Added:
                                    if (file.IsFolder)
                                    {
                                        string dir = null;
                                        if (file.FilePath == "(root)")
                                            dir = ProjectPath;
                                        else
                                        {
                                            if (!file.FilePath.Contains(this.ProjectPath))
                                                dir = this.ProjectPath + "\\" + file.FilePath;
                                            else
                                                dir = file.FilePath;
                                        }
                                        if (Directory.Exists(dir))
                                        {
                                            status.Add("Delete", dir);
                                            Directory.Delete(dir, true);
                                        }
                                    }
                                    else
                                    {
                                        string p = this.ProjectPath + "\\" + file.FilePath;
                                        if (File.Exists(p))
                                        {
                                            status.Add("Delete", p);
                                            File.Delete(p);
                                        }
                                    }
                                    break;
                            }
                        }
                        catch
                        {
                            //ignore delete errors
                        }
                    }
                }

                if (SVN.IsWorkingCopy(SVN.ShelveLocation))
                {
                    if (targetFolder.EndsWith("\\"))
                        targetFolder = targetFolder.Remove(targetFolder.Length - 1, 1);
                    SVN.Add(targetFolder);
                    status.Commit(string.Empty, _description.Text);
                }

                status.Add("", "");
                status.Add("Complete", this.ProjectPath);
            }
            catch (Exception ex)
            {
                if (Directory.Exists(targetFolder))
                {
                    if (SVN.IsWorkingCopy(targetFolder))
                    {
                        SVN.Delete(targetFolder);
                    }
                    else
                    {
                        Directory.Delete(targetFolder, true);
                    }
                }

                status.Add("", "");
                status.Add("Failed", ex.Message);
                MessageBox.Show(ex.Message, "Error while shelving your project", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }


        void Shelve_FormClosed(object sender, FormClosedEventArgs e)
        {
            this.Close();
        }

        private void _description_TextChanged(object sender, EventArgs e)
        {
            _shelve.Enabled = _description.Text.Trim().Length > 10;
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            timer1.Enabled = false;
            LoadModificationList();
            this.Cursor = System.Windows.Forms.Cursors.Default;
            _cancel.Enabled = true;
        }

        private void _cancel_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void _recent_Click(object sender, EventArgs e)
        {
            RecentForm f = new RecentForm();
            DialogResult result = f.ShowDialog(this);
            if (result == DialogResult.OK)
            {
                _description.Text = f.SelectedItem;
            }
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) VORLAN Group, Inc.
United States United States
writing code for the past 20 years, and now decided to share some thoughts. Smile | :)
Also known as Oleg Vorkunov.

Comments and Discussions