Wednesday, May 6, 2015

Ignoring files recursively with Subversion - svn:ignore and svn:global-ignores in Subversion 1.8

This is fun. The good old svn:ignore property and its inheritable (but not recursive!) counterpart, the svn:global-ignores property (Subversion 1.8 only), not to be mistaken for the client-specific global-ignores runtime configuration variable. Throw in some information about recursive properties vs. inheritable properties and it's a nice little mess to deal with. But I think I get it know. Let me explain it on a .NET developer's typical example: ignoring all "bin" and "obj" folders in whole solution's folder hierarchy.
  1. First, the svn:ignore property does not work recursively, so setting it just for your solution folder is not helpful at all - you need to set it for each project's folder individually. This might be hard when you have 191 projects in a solution (you don't? I do! And I don't like it to be clear). And when somebody creates a new project, they need to remember to add the svn:ignore property on the new project's folder, and they will forget; again and again.
  2. Subversion has this feature to set properties recursively, like in svn propset svn:ignore bin --recursive .. This is a brute-force approach - it will actually set the svn:ignore on each and every single folder below current folder. Similar to the solution above, this is not future-proof either - you need to apply the svn:ignore property yourself when creating new projects/folders.
  3. In Subversion 1.8, "inheritable" properties were introduced, and all existing versioned properties were made inheritable. However, since the svn:ignore property has historically been non-recursive (see point 1), another property was introduced, which works recursively - enter svn:global-ignores. Setting this property on the root folder of the solution will work for every subfolder.
There are two catches in this whole story.
  1. First, don't be fooled by the svn:ignore property being "inheritable". You might set the svn:ignore property on the solution's root folder and look at a project's folder properties and see the svn:ignore property being "inherited" from the solution folder - but this means nothing. The svn:ignore property still only works in the folder where it was set, not in subfolders.
  2. Second, if you use TortoiseSVN, be sure to separate the items in the svn:global-ignores property with newlines, even though according to Subversion 1.8 specification, any whitespace should work. It is apparently a bug in TortoiseSVN, described here:
More info:
  • "Properties" chapter in the official SVN Book (with the "Inherited Properties" subchapter)
  • "Ignoring Unversioned Items" chapter in that same book
  • "Ignoring Files And Directories" chapter in TortoiseSVN documentation
  • Saturday, December 6, 2014

    Remote Desktop (RDP) resolution tweaks

    It's been five years since I last posted to this blog. What has been happening during all those years? I guess I just didn't have anything new to write about.
    Anyway, here's something new: the Remote Desktop (RDP) resolution changes. The "core wisdom" is the following:

    screen mode id:i:1
    smart sizing:i:1

    And here's the use case: imagine you have a laptop whose native resolution is 1600 x 900, and you need to connect with a remote computer using the Windows "Remote desktop" utility, but you *really* need to run 1920 x 1080 on the host computer (for example because you need to run an application that can only be started in Full HD). What do you do?

  • First, change the line screen mode id:i:2 to screen mode id:i:1, if it isn't this way already. The "2" at the end means "run Remote Desktop in full screen" and it will make the host computer always use the resolution of the guest, regardless of other settings.
  • Second, change the desktopwidth and desktopheight to your desired resolution.
  • This is enough to run the host in the desired resolution. If your guest resolution is lower then the one set with desktopwidth and desktopheight, you will get scrollbars in the Remote Desktop application's window:

    Sometimes this is what you want, but if you want to see the whole desktop at the cost of scaling and poorer quality, you need to add the last change:

  • Add a new setting smart sizing:i:1.
  • This will make the program scale the host resolution to the current size of the Remote desktop application:

    You can even drag the borders of the window to shrink or enlarge the window, and the host's desktop will be scaled accordingly:


    Friday, November 13, 2009

    Move SVN project to a different repository

    I had an SVN repository containing multiple projects in location "D:\svn":
    D:\>svnlook tree svn -N
    I needed to move my project "jarmel" to a totally different repository (at How did I do that?
    1. Dumped the whole repository (containing all projects: TarantinoSamples, wilser, oscommercel, jarmel) into a file "svn.dump":
      D:\svnadmin dump svn > svn.dump
    2. Filtered the dump to include only the "jarmel" project.
      D:\svndumpfilter include jarmel < svn.dump > jarmel.dump
    3. Hand-edited the file jarmel.dump to remove the initial adding of root directory "jarmel":
      Node-path: jarmel
      Node-action: add
      Node-kind: dir
      Prop-content-length: 10
      Content-length: 10
    4. Hand-edited the file jarmel.dump to replace all paths "jarmel/x/y/z" with "x/y/z":
      Find: "Node-path: jarmel/"
      Replace with: "Node-path: "
      Find: "Node-copyfrom-path: jarmel/"
      Replace with: "Node-copyfrom-path: "
    5. Import the new repository into newly created directory "D:\svnjarmel":
      D:\>svnadmin load svnjarmel --ignore-uuid svnjarmel < jarmel.dump

    Wednesday, September 16, 2009


    A CookieProxy class to access cookies in a strongly typed way:
    using System;
    using System.Linq;
    using System.Web;
    public class CookieProxy
        public HttpRequest Request { get; set; }
        public HttpResponse Response { get; set; }
        public CookieProxy()
        public CookieProxy(HttpRequest request, HttpResponse response)
            this.Request = request;
            this.Response = response;
        public string GetCookieKey(string name)
            return string.Format("{0}", name);
        private string GetCookie(string name)
            if (this.Response.Cookies.AllKeys.Contains(this.GetCookieKey(name)))
                return this.Response.Cookies[this.GetCookieKey(name)].Value;
                return this.Request.Cookies[this.GetCookieKey(name)] != null ? this.Request.Cookies[this.GetCookieKey(name)].Value : null;
        private void SetCookie(string name, string value)
            HttpCookie cookie = this.CreateCookie(name, value);
            if (this.Response.Cookies.AllKeys.Contains(this.GetCookieKey(name)))
        public HttpCookie CreateCookie(string name, string value)
            HttpCookie cookie = new HttpCookie(GetCookieKey(name), value);
            cookie.Path = this.Request.ApplicationPath;
            cookie.Expires = DateTime.Now.AddYears(10);
            return cookie;
        // Sample cookie.
        public string Culture
            get { return this.GetCookie("Culture"); }
            set { this.SetCookie("Culture", value); }
    And a property in your application's base page:
    private CookieProxy cookies;
    public CookieProxy Cookies
            if (this.cookies == null)
                this.cookies = new CookieProxy(this.Request, this.Response);
            return this.cookies;
    How do I use it in a page?
    string culture = this.Cookies.Culture;
    How do I use it in a MasterPage or a UserControl?
    string culture = this.Page.Cookies.Culture;
    Oh, and there's a catch when you are using cookies with path - the path is case sensitive. Here's the code to get rid of the problem (place it in your Global.asax file):
    void Application_BeginRequest(object sender, EventArgs e)
        // If the application path casing is different from ApplicationPath, retrieve the url again with proper casing to avoid cookies problems.
        string url = HttpContext.Current.Request.Url.PathAndQuery;
        string application = HttpContext.Current.Request.ApplicationPath;
        if (!url.StartsWith(application))
            HttpContext.Current.Response.Redirect(application + url.Substring(application.Length));

    Monday, September 14, 2009

    NUnit External Tool

    Command:C:\Program Files\NUnit 2.4.8\bin\nunit.exe
    Initial Directory:$(ProjectDir)/bin/Debug

    Wednesday, September 9, 2009

    The not-so-dark secret of File.WriteAllText

    When you use this method using the overload with Encoding as third parameter, it writes this encoding's signature into the file. Using this method without the encoding parameter gives you a UTF-8 encoded file WITHOUT the signature. This actually helped me a lot, since I needed a UTF-8 file with signature. Though nowhere documented, this little feature saved me some hacking like this. Nice!

    Monday, August 17, 2009

    The curious/furious case of ArrayOfString class

    If you happened to have a problem with Visual Studio generating an ArrayOfString class instead of simple string[] in your web service client proxy, then this might be useful:
    You are getting the ArrayOfString in the client, because you are using Add Service Reference to add a reference to the asmx service. You can get the string[] in the client in one of two ways: a) by creating a WCF service instead of an ASMX service, and using Add Service Reference. b) by keeping the asmx service, and using Add Web Reference instead of Add Service Reference.
    from: found through: If you are creating your web service client proxy in a separate class library project, (which you are forced to if you are using Web Site instead of Web Application and want to extend your proxy generated classes using partial classes), you have to make this a .NET Framework 2.0 project. Otherwise you won't even find the "Add Web Reference" option in the right-click menu, only "Add Service Reference".