SourceTree crashes when displaying a diff containing a historical filename with an invalid Windows character

XMLWordPrintable

    • Type: Bug
    • Resolution: Unresolved
    • Priority: Medium
    • None
    • Affects Version/s: 3.4.31
    • Component/s: General
    • None
    • Severity 2 - Major

      Hi SourceTree Team,

      I would like to report the following issue:

      SourceTree crashes when displaying a diff containing a historical filename with an invalid Windows character

      Environment

      • SourceTree for Windows: 3.4.31
      • SourceTree executable version: 3.4.31.0
      • Operating system: Windows
      • Windows build: 10.0.26100.8875
      • .NET Framework runtime: v4.0.30319
      • Repository hosted on GitLab

      Description

      SourceTree crashes when processing a Git diff that contains a historical filename with the Windows-invalid character >.

      The affected file was created by another user through the GitLab web interface. Because the filename contained >, checking out and merging the affected revision on Windows initially failed.

      The file was subsequently renamed to a Windows-compatible filename in a separate commit through the GitLab web interface. The current repository state can now be checked out successfully.

      However, the original invalid filename remains part of the Git history. SourceTree still crashes when it processes a revision or diff containing that historical filename.

      The crash is caused by an unhandled System.ArgumentException in SourceTree's diff parser. The missing Visual Studio Just-In-Time debugger is not the cause of the crash. It is only reported because the application terminates with an unhandled exception.

      Git command-line operations such as git log and git show can inspect the repository history without terminating the Git process. Checking out the historical revision may still fail on Windows because the filename cannot be created on an NTFS working tree.

      Steps to Reproduce

      1. Create a Git repository hosted on GitLab.
      2. Create a file through the GitLab web interface whose filename contains the character >.
        For example:
        invalid>filename.txt
        
      3. Commit the file through the GitLab web interface.
      4. In a later commit, rename the file to a Windows-compatible filename.
        For example:
        valid-filename.txt
        
      5. Clone or open the repository with SourceTree for Windows.
      6. Select the original commit, the rename commit, or another revision comparison whose generated diff contains the original filename.
      7. Wait for SourceTree to process and display the diff.

      Actual Result

      SourceTree terminates because of an unhandled .NET exception.

      A Visual Studio Just-In-Time Debugger dialog is displayed with the following message:

      Unhandled Microsoft .NET Framework exception occurred in SourceTree.exe.
      
      No installed debugger has Just-In-Time debugging enabled.
      

      The Windows Event Viewer reports the following application error:

      Faulting application name: SourceTree.exe
      Faulting application version: 3.4.31.0
      Faulting module name: KERNELBASE.dll
      Faulting module version: 10.0.26100.8875
      Exception code: 0xe0434352
      

      The corresponding .NET Runtime event contains the following exception:

      Application: SourceTree.exe
      Framework Version: v4.0.30319
      
      Description:
      The process was terminated due to an unhandled exception.
      
      Exception Info: System.ArgumentException
         at System.IO.Path.CheckInvalidPathChars(System.String, Boolean)
         at System.IO.Path.HasExtension(System.String)
         at SourceTree.Model.DiffFile.set_Filename(System.String)
         at SourceTree.Utils.DvcsHelper.ParseDiffToModel(
            System.String,
            Int32,
            SourceTree.Model.DiffEnvironment,
            SourceTree.Configuration.IConfigurationManager,
            Int32)
         at SourceTree.ViewModel.DiffViewModel.ProcessDiffForRevision(
            System.String,
            System.Collections.Generic.List`1<SourceTree.Model.FileStatusRecord>,
            System.String,
            System.String,
            System.String[])
         at SourceTree.ViewModel.DiffViewModel+<LaunchDiffProcessing>d__196.MoveNext()
         at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
         at System.Windows.Threading.ExceptionWrapper.InternalRealCall(
            System.Delegate,
            System.Object,
            Int32)
         at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(
            System.Object,
            System.Delegate,
            System.Object,
            Int32,
            System.Delegate)
         at System.Windows.Threading.DispatcherOperation.InvokeImpl()
         at MS.Internal.CulturePreservingExecutionContext.CallbackWrapper(System.Object)
         at System.Threading.ExecutionContext.RunInternal(
            System.Threading.ExecutionContext,
            System.Threading.ContextCallback,
            System.Object,
            Boolean)
         at System.Threading.ExecutionContext.Run(
            System.Threading.ExecutionContext,
            System.Threading.ContextCallback,
            System.Object,
            Boolean)
         at System.Threading.ExecutionContext.Run(
            System.Threading.ExecutionContext,
            System.Threading.ContextCallback,
            System.Object)
         at MS.Internal.CulturePreservingExecutionContext.Run(
            MS.Internal.CulturePreservingExecutionContext,
            System.Threading.ContextCallback,
            System.Object)
         at System.Windows.Threading.DispatcherOperation.Invoke()
         at System.Windows.Threading.Dispatcher.ProcessQueue()
         at System.Windows.Threading.Dispatcher.WndProcHook(
            IntPtr,
            Int32,
            IntPtr,
            IntPtr,
            Boolean ByRef)
         at MS.Win32.HwndWrapper.WndProc(
            IntPtr,
            Int32,
            IntPtr,
            IntPtr,
            Boolean ByRef)
         at MS.Win32.HwndSubclass.DispatcherCallbackOperation(System.Object)
         at System.Windows.Threading.ExceptionWrapper.InternalRealCall(
            System.Delegate,
            System.Object,
            Int32)
         at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(
            System.Object,
            System.Delegate,
            System.Object,
            Int32,
            System.Delegate)
         at System.Windows.Threading.Dispatcher.LegacyInvokeImpl(
            System.Windows.Threading.DispatcherPriority,
            System.TimeSpan,
            System.Delegate,
            System.Object,
            Int32)
         at MS.Win32.HwndSubclass.SubclassWndProc(
            IntPtr,
            Int32,
            IntPtr,
            IntPtr)
         at MS.Win32.UnsafeNativeMethods.DispatchMessage(
            System.Windows.Interop.MSG ByRef)
         at System.Windows.Threading.Dispatcher.PushFrameImpl(
            System.Windows.Threading.DispatcherFrame)
         at System.Windows.Application.RunDispatcher(System.Object)
         at System.Windows.Application.RunInternal(System.Windows.Window)
         at SourceTree.App.Main()
      

      Expected Result

      SourceTree must not terminate when a Git diff contains a filename that is invalid as a local Windows path.

      SourceTree should instead perform at least one of the following actions:

      • Display the historical filename without interpreting it as a local Windows path.
      • Escape the filename for display purposes.
      • Skip the affected diff entry and show a warning.
      • Report that the historical filename cannot be represented in a Windows working tree.
      • Catch the System.ArgumentException and keep the application running.

      Reproducibility

      The issue is reproducible when SourceTree processes a diff or revision containing the historical filename with >.

      The current revision no longer contains the invalid filename. The crash is triggered by accessing the affected repository history.

      Technical Assessment

      The stack trace indicates that SourceTree.Model.DiffFile.set_Filename passes a filename parsed from Git diff output to System.IO.Path.HasExtension.

      System.IO.Path.HasExtension validates the supplied value according to Windows path rules and throws System.ArgumentException when the value contains an invalid path character.

      A filename from Git repository metadata is not necessarily a valid local Windows path. SourceTree should therefore not pass an unvalidated Git filename directly to a Windows-specific path API.

      At minimum, the exception should be caught so that an invalid historical filename cannot terminate the entire application.

      KERNELBASE.dll is only the module through which the unhandled managed exception is reported. There is no indication that KERNELBASE.dll or the installed .NET Framework is the root cause.

      Impact

      • SourceTree cannot reliably display parts of an otherwise valid Git repository history.
      • Selecting the affected revision causes the complete SourceTree application to terminate.
      • Renaming the file in a later commit does not resolve the SourceTree crash for historical revisions.
      • Users must use Git CLI or the GitLab web interface to inspect the affected commits.

      Workaround

      • Use Git CLI commands such as git log or git show to inspect the affected history.
      • Use the GitLab web interface to inspect the affected commits.
      • Avoid selecting the affected historical commit or diff in SourceTree.
      • Do not check out the historical revision on Windows because Windows cannot create the original filename.
      • Use the current revision containing the corrected filename for normal Windows checkouts.

      Suggested Fix

      Treat filenames parsed from Git diff output as Git repository metadata rather than local Windows paths.

      Before calling System.IO.Path.HasExtension, SourceTree should validate the value, use a filename parser that supports Git path syntax, or catch System.ArgumentException.

      An invalid local Windows filename should result in a warning or unavailable diff entry, not an application crash.

      Severity

      Major

      Opening an affected revision causes the entire SourceTree application to terminate. No repository corruption has been observed.

      Kind regards,
      Stefan

              Assignee:
              Unassigned
              Reporter:
              Stefan Lehnert
              Votes:
              0 Vote for this issue
              Watchers:
              1 Start watching this issue

                Created:
                Updated: