8
votes

For some reason nobody ever has set up decent svn:ignore properties in our repository, and when I asked about it the response was "nobody has complained about that before, can't you just leave 'unversioned items' unchecked?" (We pretty much globally use TortoiseSVN).

I'm new to the team, so I want to set up some local ignore lists instead of making changes to the repository willy-nilly.

I know about the global ignore list, but the global ignore list does not accept specific paths, only simple one-level file patterns. I read a question about a local ignore list but that question is actually not asking about "ignoring" unversioned files, but actually is asking about blocking commits on specific versioned files. I read another question about ignoring a directory locally like I want to do, but the accepted answer on that question uses the global ignore list, which doesn't work for me due to not being able to specify specific paths within my working copy.

I can set some svn:ignore properties locally, but directories cannot be added to changelists (as recommended in the question above for avoiding commits of versioned files), so I risk accidentally committing those changes.

Is there a way to locally ignore specific unversioned directories or files in my working copy tree, without using svn:ignore?


Update: The current top answer gets me very close to what I want. It allows me to filter out arbitrary files in either the "check for modifications" or the "commit" dialogs. However, if I want to include directory changes (i.e. changes to the versioned properties of a directory) then TortoiseSVN will also include all files under the directory automatically (i.e. I cannot both include a directory in the list, and filter out files under the directory).

1

1 Answers

5
votes

I could not a find a seamless way to accomplish what you're looking for, but I did find an option that might suit your needs, considering that this is something you want solely for your local machine.

It's possible to manually invoke the TortoiseSVN Commit dialog, providing the paths for it to search for files to commit, like this:

C:\Program Files\TortoiseSVN\bin\TortoiseProc.exe /command:commit /pathfile:"<a file containing paths>"

So, since you know which directories & files you want to ignore, you could provide a paths file containing only those directories & files you wish to commit.

Bear in mind that the path file must be in UTF-16 (little-endian) encoding, without the BOM. Paths are separated by new line characters. You can create such a valid paths file (in C#), like this:

string[] paths = { @"c:\code\1\dir1", @"c:\code\1\dir2", @"c:\code\1\dir3\file1.txt" };
UnicodeEncoding encoding = new UnicodeEncoding(false, false);
System.IO.File.WriteAllText(@"C:\temp\pathslist.txt", string.Join("\n", paths), encoding);

In my experimentation with the paths, both directory and file paths are supported, but not wildcards, so any filtering would need to be done at the time you produce the paths file.

There are a couple of additional command line options for TortoiseProc /command:commit, which you can find here.

The same method will work for "Check for modifications", substituting /command:repostatus in place of /command:commit. In summary:

  1. Create a script that grabs all file names of interest
  2. Filter out all file names that are not interesting (possibly from an "ignore file" somewhere on your machine).
  3. Write the list of files in UTF-16 LE format (without BOM).
  4. Call TortoiseProc with a /pathfile argument specifying the file list and /command of either repostatus or commit.
  5. Optionally, delete the file list.

Optional - Integrating with "Commit" using a hook script

As an aside, I did find one path of "integration" by making use of the TortoiseSVN Hook Script options in Settings. It still uses the TortoiseProc procedure above, but additionally provides a way to still make use of the TortoiseSVN Commit context menu (with one quirk).

The specific language you use for the hook script is not important, only that it be able to process command line arguments and return an exit code. From the documentation (here):

This could be a batch file, an executable file or any other file which has a valid windows file association, e.g. a perl script.

For this example, I used C#:

static void Main(string[] args)
{
    // Process the three command-line arguments
    string PATH = args[0]
        , MESSAGEFILE = args[1]
        , CWD = args[2];

    const string tortoiseProcPath = @"C:\Program Files\TortoiseSVN\bin\TortoiseProc.exe";
    const string someArgForOurUse = @"/2E040D90-E3AD-4AC5-AA46-E6D9F1034E55";
    const string wantedPathsFile = @"C:\temp\svn_paths_list.txt";

    //System.Diagnostics.Debugger.Launch();

    System.Diagnostics.Process parentProc = ParentProcessUtilities.GetParentProcess();

    // If the parent process isn't what is expected or it has the proprietary argument then exit.
    if ((parentProc == null) || (!parentProc.MainModule.FileName.Equals(tortoiseProcPath, StringComparison.InvariantCultureIgnoreCase))
        || GetProcessCommandLine(parentProc.Id).Contains(someArgForOurUse))
        return;

    // Read all selected path from passed-in the temp file
    // Each line contains a file/directory selected in Explorer
    string[] fileLines = System.IO.File.ReadAllLines(PATH);
    IEnumerable<string> wantedPaths = GetWantedPaths(fileLines);

    UnicodeEncoding encoding = new UnicodeEncoding(false, false);
    System.IO.File.WriteAllText(wantedPathsFile, string.Join("\n", wantedPaths), encoding);

    System.Diagnostics.Process.Start(tortoiseProcPath, "/command:commit /pathfile:\"" + wantedPathsFile + "\" " + someArgForOurUse);

    Console.Error.WriteLine("Don't worry.  Everything will be ok.");
    Environment.Exit(1);
}

private static IEnumerable<string> GetWantedPaths(string[] selectedPaths)
{
    // Do whatever you want here to filter directories and files
    return selectedPaths;
}

// Add System.Management reference
private static string GetProcessCommandLine(int processId)
{
    System.Management.SelectQuery wmiQuery = new System.Management.SelectQuery("SELECT CommandLine FROM Win32_Process WHERE ProcessId = " + processId.ToString());
    System.Management.ManagementObjectSearcher searcher = new System.Management.ManagementObjectSearcher(wmiQuery);

    foreach (System.Management.ManagementObject obj in searcher.Get())
        return obj["CommandLine"].ToString();
    return null;
}

For brevity's sake, ParentProcessUtilities.GetParentProcess, which I used from another SO post, can be found here.

Explanation:

The PATH input argument points to a temp file which contains all of the directories and files selected in Explorer. From this, you can build your list of wanted paths. When the list is constructed and written to file, we then relaunch TortoiseProc, passing the new file as an argument, as well as a proprietary argument, which can be used for tracking.

At the end, we return an exit code other than 0, which prevents the original TortoiseSVN Commit window from appearing, with one caveat. An "error" dialog will appear, displaying the message written to stderr; the quirk of which I was speaking. One of the most important parts of this code is checking the parent process for the proprietary argument before actually doing all of this work. This check prevents the hook from running an infinite number of times, because when TortoiseProc is relaunched by the above code, the hook script will be triggered again and, subsequently, call this code again.

I also looked into whether or not the originally passed-in PATH file's contents could just be altered in place, but it cannot. It is unfortunately just a file that TortoiseSVN outputs explicitly for the purpose of the hook script, then deletes it. Perhaps this could be a future feature.