1 package org.apache.maven.scm.provider.git.gitexe.command.status;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.File;
23 import java.io.UnsupportedEncodingException;
24 import java.net.URI;
25 import java.util.ArrayList;
26 import java.util.List;
27 import java.util.regex.Matcher;
28 import java.util.regex.Pattern;
29
30 import org.apache.commons.lang.StringUtils;
31 import org.apache.maven.scm.ScmFile;
32 import org.apache.maven.scm.ScmFileStatus;
33 import org.apache.maven.scm.log.ScmLogger;
34 import org.codehaus.plexus.util.cli.StreamConsumer;
35
36
37
38
39 public class GitStatusConsumer
40 implements StreamConsumer
41 {
42
43
44
45
46 private static final Pattern ADDED_PATTERN = Pattern.compile( "^A[ M]* (.*)$" );
47
48
49
50
51 private static final Pattern MODIFIED_PATTERN = Pattern.compile( "^ *M[ M]* (.*)$" );
52
53
54
55
56 private static final Pattern DELETED_PATTERN = Pattern.compile( "^ *D * (.*)$" );
57
58
59
60
61 private static final Pattern RENAMED_PATTERN = Pattern.compile( "^R (.*) -> (.*)$" );
62
63 private ScmLogger logger;
64
65 private File workingDirectory;
66
67
68
69
70 private List<ScmFile> changedFiles = new ArrayList<ScmFile>();
71
72 private URI relativeRepositoryPath;
73
74
75
76
77
78
79
80
81
82
83
84 public GitStatusConsumer( ScmLogger logger, File workingDirectory )
85 {
86 this.logger = logger;
87 this.workingDirectory = workingDirectory;
88 }
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103 public GitStatusConsumer( ScmLogger logger, File workingDirectory, URI relativeRepositoryPath )
104 {
105 this( logger, workingDirectory );
106 this.relativeRepositoryPath = relativeRepositoryPath;
107 }
108
109
110
111
112
113
114
115
116 public void consumeLine( String line )
117 {
118 if ( logger.isDebugEnabled() )
119 {
120 logger.debug( line );
121 }
122 if ( StringUtils.isEmpty( line ) )
123 {
124 return;
125 }
126
127 ScmFileStatus status = null;
128
129 List<String> files = new ArrayList<String>();
130
131 Matcher matcher;
132 if ( ( matcher = ADDED_PATTERN.matcher( line ) ).find() )
133 {
134 status = ScmFileStatus.ADDED;
135 files.add( resolvePath( matcher.group( 1 ), relativeRepositoryPath ) );
136 }
137 else if ( ( matcher = MODIFIED_PATTERN.matcher( line ) ).find() )
138 {
139 status = ScmFileStatus.MODIFIED;
140 files.add( resolvePath( matcher.group( 1 ), relativeRepositoryPath ) );
141 }
142 else if ( ( matcher = DELETED_PATTERN.matcher( line ) ).find() )
143 {
144 status = ScmFileStatus.DELETED;
145 files.add( resolvePath( matcher.group( 1 ), relativeRepositoryPath ) );
146 }
147 else if ( ( matcher = RENAMED_PATTERN.matcher( line ) ).find() )
148 {
149 status = ScmFileStatus.RENAMED;
150 files.add( resolvePath( matcher.group( 1 ), relativeRepositoryPath ) );
151 files.add( resolvePath( matcher.group( 2 ), relativeRepositoryPath ) );
152 logger.debug( "RENAMED status for line '" + line + "' files added '" + matcher.group( 1 ) + "' '"
153 + matcher.group( 2 ) );
154 }
155 else
156 {
157 logger.warn( "Ignoring unrecognized line: " + line );
158 return;
159 }
160
161
162 if ( !files.isEmpty() && status != null )
163 {
164 if ( workingDirectory != null )
165 {
166 if ( status == ScmFileStatus.RENAMED )
167 {
168 String oldFilePath = files.get( 0 );
169 String newFilePath = files.get( 1 );
170 if ( isFile( oldFilePath ) )
171 {
172 logger.debug( "file '" + oldFilePath + "' is a file" );
173 return;
174 }
175 else
176 {
177 logger.debug( "file '" + oldFilePath + "' not a file" );
178 }
179 if ( !isFile( newFilePath ) )
180 {
181 logger.debug( "file '" + newFilePath + "' not a file" );
182 return;
183 }
184 else
185 {
186 logger.debug( "file '" + newFilePath + "' is a file" );
187 }
188 }
189 else if ( status == ScmFileStatus.DELETED )
190 {
191 if ( isFile( files.get( 0 ) ) )
192 {
193 return;
194 }
195 }
196 else
197 {
198 if ( !isFile( files.get( 0 ) ) )
199 {
200 return;
201 }
202 }
203 }
204
205 for ( String file : files )
206 {
207 changedFiles.add( new ScmFile( file, status ) );
208 }
209 }
210 }
211
212 private boolean isFile( String file )
213 {
214 File targetFile;
215 if ( relativeRepositoryPath == null )
216 {
217 targetFile = new File( workingDirectory, file );
218 }
219 else
220 {
221 targetFile = new File( relativeRepositoryPath.getPath(), file );
222 }
223 return targetFile.isFile();
224 }
225
226 protected static String resolvePath( String fileEntry, URI path )
227 {
228
229 String cleanedEntry = stripQuotes( fileEntry );
230 if ( path != null )
231 {
232 return resolveURI( cleanedEntry, path ).getPath();
233 }
234 else
235 {
236 return cleanedEntry;
237 }
238 }
239
240
241
242
243
244
245
246 public static URI resolveURI( String fileEntry, URI path )
247 {
248
249
250
251 return path.relativize( URI.create( stripQuotes( fileEntry ).replace( " ", "%20" ) ) );
252 }
253
254
255 public List<ScmFile> getChangedFiles()
256 {
257 return changedFiles;
258 }
259
260
261
262
263
264 private static String stripQuotes( String str )
265 {
266 int strLen = str.length();
267 return ( strLen > 0 && str.startsWith( "\"" ) && str.endsWith( "\"" ) ) ? unescape( str.substring( 1, strLen - 1 ) ) : str;
268 }
269
270
271
272
273
274
275
276 private static String unescape( String fileEntry )
277 {
278
279 int pos = fileEntry.indexOf( '\\' );
280 if ( pos == -1 )
281 {
282 return fileEntry;
283 }
284
285
286 byte[] inba = fileEntry.getBytes();
287 int inSub = 0;
288 byte[] outba = new byte[fileEntry.length()];
289 int outSub = 0;
290
291 while ( true )
292 {
293 System.arraycopy( inba, inSub, outba, outSub, pos - inSub );
294 outSub += pos - inSub;
295 inSub = pos + 1;
296 switch ( (char) inba[inSub++] )
297 {
298 case '"':
299 outba[outSub++] = '"';
300 break;
301
302 case 'a':
303 outba[outSub++] = 7;
304 break;
305
306 case 'b':
307 outba[outSub++] = '\b';
308 break;
309
310 case 't':
311 outba[outSub++] = '\t';
312 break;
313
314 case 'n':
315 outba[outSub++] = '\n';
316 break;
317
318 case 'v':
319 outba[outSub++] = 11;
320 break;
321
322 case 'f':
323 outba[outSub++] = '\f';
324 break;
325
326 case 'r':
327 outba[outSub++] = '\f';
328 break;
329
330 case '\\':
331 outba[outSub++] = '\\';
332 break;
333
334 case '0':
335 case '1':
336 case '2':
337 case '3':
338
339 byte b = (byte) ( ( inba[inSub - 1] - '0' ) << 6 );
340 b |= (byte) ( ( inba[inSub++] - '0' ) << 3 );
341 b |= (byte) ( inba[inSub++] - '0' );
342 outba[outSub++] = b;
343 break;
344
345 default:
346
347 outba[outSub++] = '\\';
348 inSub--;
349 break;
350 }
351 pos = fileEntry.indexOf( '\\', inSub);
352 if ( pos == -1 )
353 {
354 System.arraycopy( inba, inSub, outba, outSub, inba.length - inSub );
355 outSub += inba.length - inSub;
356 break;
357 }
358 }
359 try
360 {
361
362 return new String(outba, 0, outSub, "UTF-8");
363 }
364 catch ( UnsupportedEncodingException e )
365 {
366 throw new RuntimeException( e );
367 }
368 }
369 }