[SCM-772] GitStatusConsumer does not properly handle quoted paths output from GitStatusCommand
Original patch adjusted, just keep URI instead of String
diff --git a/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-gitexe/src/main/java/org/apache/maven/scm/provider/git/gitexe/command/status/GitStatusConsumer.java b/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-gitexe/src/main/java/org/apache/maven/scm/provider/git/gitexe/command/status/GitStatusConsumer.java
index 7e54888..7d850da 100644
--- a/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-gitexe/src/main/java/org/apache/maven/scm/provider/git/gitexe/command/status/GitStatusConsumer.java
+++ b/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-gitexe/src/main/java/org/apache/maven/scm/provider/git/gitexe/command/status/GitStatusConsumer.java
@@ -20,6 +20,7 @@
  */
 
 import java.io.File;
+import java.io.UnsupportedEncodingException;
 import java.net.URI;
 import java.util.ArrayList;
 import java.util.List;
@@ -263,6 +264,106 @@
     private static String stripQuotes( String str )
     {
         int strLen = str.length();
-        return ( strLen > 0 && str.startsWith( "\"" ) && str.endsWith( "\"" ) ) ? str.substring( 1, strLen - 1 ) : str;
+        return ( strLen > 0 && str.startsWith( "\"" ) && str.endsWith( "\"" ) ) ? unescape( str.substring( 1, strLen - 1 ) ) : str;
+    }
+    
+    /**
+     * Dequote a quoted string generated by git status --porcelain.
+     * The leading and trailing quotes have already been removed. 
+     * @param fileEntry
+     * @return
+     */
+    private static String unescape( String fileEntry )
+    {
+        // If there are no escaped characters, just return the input argument
+        int pos = fileEntry.indexOf( '\\' );
+        if ( pos == -1 )
+        {
+            return fileEntry;
+        }
+        
+        // We have escaped characters
+        byte[] inba = fileEntry.getBytes();
+        int inSub = 0;      // Input subscript into fileEntry
+        byte[] outba = new byte[fileEntry.length()];
+        int outSub = 0;     // Output subscript into outba
+        
+        while ( true )
+        {
+            System.arraycopy( inba,  inSub,  outba, outSub, pos - inSub );
+            outSub += pos - inSub;
+            inSub = pos + 1;
+            switch ( (char) inba[inSub++] )
+            {
+                case '"':
+                    outba[outSub++] = '"';
+                    break;
+                    
+                case 'a':
+                    outba[outSub++] = 7;        // Bell
+                    break;
+                    
+                case 'b':
+                    outba[outSub++] = '\b';
+                    break;
+                    
+                case 't':
+                    outba[outSub++] = '\t';
+                    break;
+                    
+                case 'n':
+                    outba[outSub++] = '\n';
+                    break;
+                    
+                case 'v':
+                    outba[outSub++] = 11;       // Vertical tab
+                    break;
+                    
+                case 'f':
+                    outba[outSub++] = '\f';
+                    break;
+                    
+                case 'r':
+                    outba[outSub++] = '\f';
+                    break;
+                    
+                case '\\':
+                    outba[outSub++] = '\\';
+                    break;
+                    
+                case '0':
+                case '1':
+                case '2':
+                case '3':
+                    // This assumes that the octal escape here is valid.
+                    byte b = (byte) ( ( inba[inSub - 1] - '0' ) << 6 );
+                    b |= (byte) ( ( inba[inSub++] - '0' ) << 3 );
+                    b |= (byte) ( inba[inSub++] - '0' );
+                    outba[outSub++] = b;
+                    break;
+                    
+                default:
+                    //This is an invalid escape in a string.  Just copy it.
+                    outba[outSub++] = '\\';
+                    inSub--;
+                    break;
+            }
+            pos = fileEntry.indexOf( '\\', inSub);
+            if ( pos == -1 )        // No more backslashes; we're done
+            {
+                System.arraycopy( inba, inSub, outba, outSub, inba.length - inSub );
+                outSub += inba.length - inSub;
+                break;
+            }
+        }
+        try
+        {
+            // explicit say UTF-8, otherwise it'll fail at least on Windows cmdline
+            return new String(outba, 0, outSub, "UTF-8");
+        }
+        catch ( UnsupportedEncodingException e )
+        {
+          throw new RuntimeException( e );    
+        }
     }
 }
diff --git a/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-gitexe/src/test/java/org/apache/maven/scm/provider/git/gitexe/command/status/GitStatusConsumerTest.java b/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-gitexe/src/test/java/org/apache/maven/scm/provider/git/gitexe/command/status/GitStatusConsumerTest.java
index a2537f4..3b4d153 100644
--- a/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-gitexe/src/test/java/org/apache/maven/scm/provider/git/gitexe/command/status/GitStatusConsumerTest.java
+++ b/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-gitexe/src/test/java/org/apache/maven/scm/provider/git/gitexe/command/status/GitStatusConsumerTest.java
@@ -94,6 +94,11 @@
 
         assertNotNull( changedFiles );
         assertEquals( 0, changedFiles.size() );
+
+        changedFiles = getChangedFiles( "?? \"test file with spaces and a special \\177 character.xml\"", null );
+
+        assertNotNull( changedFiles );
+        assertEquals( 0, changedFiles.size() );
     }
 
     public void testConsumerAddedFile()
@@ -102,6 +107,13 @@
 
         assertNotNull( changedFiles );
         assertEquals( 1, changedFiles.size() );
+        assertEquals( "project.xml", changedFiles.get( 0 ).getPath() );
+
+        changedFiles = getChangedFiles( "A  \"test file with spaces and a special \\177 character.xml\"", null );
+        
+        assertNotNull( changedFiles );
+        assertEquals( 1, changedFiles.size() );
+        assertEquals("test file with spaces and a special \u007f character.xml", changedFiles.get( 0 ).getPath() );
     }
 
     public void testConsumerAddedAndModifiedFile()
@@ -110,7 +122,13 @@
 
         assertNotNull( changedFiles );
         assertEquals( 1, changedFiles.size() );
-        assertEquals( ScmFileStatus.ADDED, changedFiles.get( 0 ).getStatus() );
+        testScmFile( changedFiles.get( 0 ), "project.xml", ScmFileStatus.ADDED );
+        
+        changedFiles = getChangedFiles( "AM \"test file with spaces and a special \\177 character.xml\"", null );
+
+        assertNotNull( changedFiles );
+        assertEquals( 1, changedFiles.size() );
+        testScmFile( changedFiles.get( 0 ), "test file with spaces and a special \u007f character.xml", ScmFileStatus.ADDED );
     }
 
     public void testConsumerAddedFileWithDirectoryAndNoFile()
@@ -123,6 +141,11 @@
         assertNotNull( changedFiles );
         assertEquals( 0, changedFiles.size() );
 
+        changedFiles = getChangedFiles( "A  \"test file with spaces and a special \\177 character.xml\"", dir );
+
+        assertNotNull( changedFiles );
+        assertEquals( 0, changedFiles.size() );
+
         FileUtils.deleteDirectory( dir );
     }
 
@@ -136,6 +159,15 @@
 
         assertNotNull( changedFiles );
         assertEquals( 1, changedFiles.size() );
+        assertEquals( "project.xml", changedFiles.get( 0 ).getPath() );
+
+        FileUtils.write( new File( dir, "test file with spaces and a special \u007f character.xml" ), "data" );
+
+        changedFiles = getChangedFiles( "A  \"test file with spaces and a special \\177 character.xml\"", dir );
+
+        assertNotNull( changedFiles );
+        assertEquals( 1, changedFiles.size() );
+        assertEquals("test file with spaces and a special \u007f character.xml", changedFiles.get( 0 ).getPath() );
 
         FileUtils.deleteDirectory( dir );
     }
@@ -146,6 +178,13 @@
 
         assertNotNull( changedFiles );
         assertEquals( 1, changedFiles.size() );
+        assertEquals( "project.xml", changedFiles.get( 0 ).getPath() );
+
+        changedFiles = getChangedFiles( "M  \"test file with spaces and a special \\177 character.xml\"", null );
+
+        assertNotNull( changedFiles );
+        assertEquals( 1, changedFiles.size() );
+        assertEquals("test file with spaces and a special \u007f character.xml", changedFiles.get( 0 ).getPath() );
     }
 
 	// SCM-740
@@ -160,15 +199,56 @@
 
 		assertNotNull( changedFiles );
 		assertEquals( 1, changedFiles.size() );
+        assertEquals( "subDirectory/project.xml", changedFiles.get( 0 ).getPath() );
+
+        FileUtils.write( new File( subdir, "test file with spaces and a déjà vu character.xml" ), "data" );
+
+		changedFiles = getChangedFiles( "M  \"subDirectory/test file with spaces and a déjà vu character.xml\"", subdir, dir.toURI() );
+
+		assertNotNull( changedFiles );
+		assertEquals( 1, changedFiles.size() );
+        assertEquals( "subDirectory/test file with spaces and a déjà vu character.xml", changedFiles.get( 0 ).getPath() );
+
+        FileUtils.deleteDirectory( dir );
 	}
 
-    public void testConsumerModifiedFileUnstaged()
+	public void testConsumerModifiedFileInComplexDirectoryWithSpaces() throws IOException {
+
+		File dir = createTempDirectory();
+		File subdir = new File( dir.getAbsolutePath() + "/sub Directory déjà vu special/" );
+		subdir.mkdir();
+		FileUtils.write( new File( subdir, "project.xml" ), "data" );
+
+		List<ScmFile> changedFiles = getChangedFiles( "M  \"sub Directory déjà vu special/project.xml\"", subdir, dir.toURI() );
+
+		assertNotNull( changedFiles );
+		assertEquals( 1, changedFiles.size() );
+        assertEquals( "sub Directory déjà vu special/project.xml", changedFiles.get( 0 ).getPath() );
+
+        FileUtils.write( new File( subdir, "test file with spaces and a déjà vu character.xml" ), "data" );
+
+		changedFiles = getChangedFiles( "M  \"sub Directory déjà vu special/test file with spaces and a déjà vu character.xml\"", subdir, dir.toURI() );
+
+		assertNotNull( changedFiles );
+		assertEquals( 1, changedFiles.size() );
+        assertEquals( "sub Directory déjà vu special/test file with spaces and a déjà vu character.xml", changedFiles.get( 0 ).getPath() );
+
+        FileUtils.deleteDirectory( dir );
+	}
+
+	public void testConsumerModifiedFileUnstaged()
     {
         List<ScmFile> changedFiles = getChangedFiles( "M  project.xml", null );
 
         assertNotNull( changedFiles );
         assertEquals( 1, changedFiles.size() );
-        assertEquals( ScmFileStatus.MODIFIED, changedFiles.get( 0 ).getStatus() );
+        testScmFile( changedFiles.get( 0 ), "project.xml", ScmFileStatus.MODIFIED);
+
+        changedFiles = getChangedFiles( "M  \"test file with spaces and a special \\177 character.xml\"", null );
+
+        assertNotNull( changedFiles );
+        assertEquals( 1, changedFiles.size() );
+        testScmFile( changedFiles.get( 0 ), "test file with spaces and a special \u007f character.xml", ScmFileStatus.MODIFIED);
     }
 
     public void testConsumerModifiedFileBothStagedAndUnstaged()
@@ -177,7 +257,13 @@
 
         assertNotNull( changedFiles );
         assertEquals( 1, changedFiles.size() );
-        assertEquals( ScmFileStatus.MODIFIED, changedFiles.get( 0 ).getStatus() );
+        testScmFile( changedFiles.get( 0 ), "project.xml", ScmFileStatus.MODIFIED);
+
+        changedFiles = getChangedFiles( "MM \"test file with spaces and a special \\177 character.xml\"", null );
+
+        assertNotNull( changedFiles );
+        assertEquals( 1, changedFiles.size() );
+        testScmFile( changedFiles.get( 0 ), "test file with spaces and a special \u007f character.xml", ScmFileStatus.MODIFIED);
     }
 
     public void testConsumerModifiedFileWithDirectoryAndNoFile()
@@ -190,6 +276,11 @@
         assertNotNull( changedFiles );
         assertEquals( 0, changedFiles.size() );
 
+        changedFiles = getChangedFiles( "M  \"test file with spaces and a special \\177 character.xml\"", dir );
+
+        assertNotNull( changedFiles );
+        assertEquals( 0, changedFiles.size() );
+
         FileUtils.deleteDirectory( dir );
     }
 
@@ -203,6 +294,15 @@
 
         assertNotNull( changedFiles );
         assertEquals( 1, changedFiles.size() );
+        assertEquals( "project.xml", changedFiles.get( 0 ).getPath() );
+
+        FileUtils.write( new File( dir, "test file with spaces and a special \u007f character.xml" ), "data" );
+
+        changedFiles = getChangedFiles( "M  \"test file with spaces and a special \\177 character.xml\"", dir );
+
+        assertNotNull( changedFiles );
+        assertEquals( 1, changedFiles.size() );
+        assertEquals( "test file with spaces and a special \u007f character.xml", changedFiles.get( 0 ).getPath() );
 
         FileUtils.deleteDirectory( dir );
     }
@@ -213,6 +313,13 @@
 
         assertNotNull( changedFiles );
         assertEquals( 1, changedFiles.size() );
+        assertEquals( "Capfile", changedFiles.get( 0 ).getPath() );
+
+        changedFiles = getChangedFiles( "D  \"test file with spaces and a déjà vu character.xml\"", null );
+
+        assertNotNull( changedFiles );
+        assertEquals( 1, changedFiles.size() );
+        assertEquals( "test file with spaces and a déjà vu character.xml", changedFiles.get( 0 ).getPath() );
     }
 
     public void testConsumerRemovedFileUnstaged()
@@ -222,6 +329,12 @@
         assertNotNull( changedFiles );
         assertEquals( 1, changedFiles.size() );
         assertEquals( ScmFileStatus.DELETED, changedFiles.get( 0 ).getStatus() );
+
+        changedFiles = getChangedFiles( "D  \"test file with spaces and a special \\177 character.xml\"", null );
+
+        assertNotNull( changedFiles );
+        assertEquals( 1, changedFiles.size() );
+        assertEquals( ScmFileStatus.DELETED, changedFiles.get( 0 ).getStatus() );
     }
 
     public void testConsumerRemovedFileWithDirectoryAndNoFile()
@@ -233,6 +346,14 @@
 
         assertNotNull( changedFiles );
         assertEquals( 1, changedFiles.size() );
+        assertEquals( "Capfile", changedFiles.get( 0 ).getPath() );
+
+        changedFiles = getChangedFiles( "D  \"test file with spaces and a special \\177 character.xml\"", dir );
+
+        assertNotNull( changedFiles );
+        assertEquals( 1, changedFiles.size() );
+        assertEquals( "test file with spaces and a special \u007f character.xml", changedFiles.get( 0 ).getPath() );
+        
         FileUtils.deleteDirectory( dir );
     }
 
@@ -246,11 +367,18 @@
 
         assertNotNull( changedFiles );
         assertEquals( 0, changedFiles.size() );
+
+        FileUtils.write( new File( dir, "test file with spaces and a special \u007f character.xml" ), "data" );
+
+        changedFiles = getChangedFiles( "D  \"test file with spaces and a special \\177 character.xml\"", dir );
+
+        assertNotNull( changedFiles );
+        assertEquals( 0, changedFiles.size() );
         FileUtils.deleteDirectory( dir );
     }
 
     // Test reproducing SCM-694
-    public void testConsumeRenamedFile()
+    public void testConsumerRenamedFile()
         throws Exception
     {
         File dir = createTempDirectory();
@@ -265,6 +393,17 @@
         assertEquals( 2, changedFiles.size() );
         assertEquals( "OldCapfile", changedFiles.get(0).getPath() );
         assertEquals( "NewCapFile", changedFiles.get(1).getPath() );
+
+        tmpFile = new File( dir, "New test file with spaces and a special \u007f character.xml" );
+
+        FileUtils.write( tmpFile, "data" );
+
+        changedFiles = getChangedFiles( "R  \"Old test file with spaces and a special \\177 character.xml\" -> \"New test file with spaces and a special \\177 character.xml\"", dir );
+
+        assertNotNull( changedFiles );
+        assertEquals( 2, changedFiles.size() );
+        assertEquals( "Old test file with spaces and a special \u007f character.xml", changedFiles.get(0).getPath() );
+        assertEquals( "New test file with spaces and a special \u007f character.xml", changedFiles.get(1).getPath() );
         FileUtils.deleteDirectory( dir );
     }
 
@@ -273,10 +412,12 @@
     {
         List<ScmFile> changedFiles = getChangedFiles( getTestFile( "/src/test/resources/git/status/gitstatus1.gitlog" ) );
 
-        assertEquals( 2, changedFiles.size() );
+        assertEquals( 4, changedFiles.size() );
 
         testScmFile( changedFiles.get( 0 ), "project.xml", ScmFileStatus.ADDED );
         testScmFile( changedFiles.get( 1 ), "readme.txt", ScmFileStatus.MODIFIED );
+        testScmFile( changedFiles.get( 2 ), "d\u00e9j\u00e0 vu.xml", ScmFileStatus.ADDED );
+        testScmFile( changedFiles.get( 3 ), "d\u00e9j\u00e0 vu.txt", ScmFileStatus.MODIFIED );
     }
 
     public void testEmptyLogConsumer()
@@ -287,7 +428,6 @@
         assertEquals( 0, changedFiles.size() );
     }
 
-
     public void testLog2Consumer()
         throws Exception
     {
@@ -309,27 +449,6 @@
                      ScmFileStatus.MODIFIED );
     }
 
-    public void testLog3Consumer()
-                    throws Exception
-                {
-                    List<ScmFile> changedFiles = getChangedFiles( getTestFile( "/src/test/resources/git/status/gitstatus2.gitlog" ), URI.create( "maven-scm-provider-gitexe" ) );
-
-                    assertEquals( 4, changedFiles.size() );
-
-                    testScmFile( changedFiles.get( 0 ),
-                                 "src/main/java/org/apache/maven/scm/provider/git/gitexe/command/add/GitAddCommand.java",
-                                 ScmFileStatus.MODIFIED );
-                    testScmFile( changedFiles.get( 1 ),
-                                 "src/main/java/org/apache/maven/scm/provider/git/gitexe/command/checkin/GitCheckInCommand.java",
-                                 ScmFileStatus.MODIFIED );
-                    testScmFile( changedFiles.get( 2 ),
-                                 "src/main/java/org/apache/maven/scm/provider/git/gitexe/command/checkin/GitCheckInConsumer.java",
-                                 ScmFileStatus.DELETED );
-                    testScmFile( changedFiles.get( 3 ),
-                                 "src/main/java/org/apache/maven/scm/provider/git/gitexe/command/status/GitStatusConsumer.java",
-                                 ScmFileStatus.MODIFIED );
-                }
-
     // SCM-709
     public void testResolvePath()
     {
diff --git a/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-gitexe/src/test/resources/git/status/gitstatus1.gitlog b/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-gitexe/src/test/resources/git/status/gitstatus1.gitlog
index 4f656a4..677cc9b 100644
--- a/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-gitexe/src/test/resources/git/status/gitstatus1.gitlog
+++ b/maven-scm-providers/maven-scm-providers-git/maven-scm-provider-gitexe/src/test/resources/git/status/gitstatus1.gitlog
@@ -1,2 +1,4 @@
 A  project.xml
  M readme.txt
+A  "d\303\251j\303\240 vu.xml"
+ M "d\303\251j\303\240 vu.txt"