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.net.URISyntaxException;
26 import java.util.ArrayList;
27 import java.util.List;
28 import java.util.regex.Matcher;
29 import java.util.regex.Pattern;
30
31 import org.apache.maven.scm.ScmFile;
32 import org.apache.maven.scm.ScmFileStatus;
33 import org.apache.maven.scm.ScmFileSet;
34 import org.apache.maven.scm.util.AbstractConsumer;
35 import org.codehaus.plexus.util.StringUtils;
36
37
38
39
40 public class GitStatusConsumer
41 extends AbstractConsumer
42 {
43
44
45
46
47 private static final Pattern ADDED_PATTERN = Pattern.compile( "^A[ M]* (.*)$" );
48
49
50
51
52 private static final Pattern MODIFIED_PATTERN = Pattern.compile( "^ *M[ M]* (.*)$" );
53
54
55
56
57 private static final Pattern DELETED_PATTERN = Pattern.compile( "^ *D * (.*)$" );
58
59
60
61
62 private static final Pattern RENAMED_PATTERN = Pattern.compile( "^R (.*) -> (.*)$" );
63
64 private final File workingDirectory;
65
66 private ScmFileSet scmFileSet;
67
68
69
70
71 private final List<ScmFile> changedFiles = new ArrayList<>();
72
73 private URI relativeRepositoryPath;
74
75
76
77
78
79
80
81
82
83
84 public GitStatusConsumer( File workingDirectory )
85 {
86 this.workingDirectory = workingDirectory;
87 }
88
89
90
91
92
93
94
95
96
97
98
99
100
101 public GitStatusConsumer( File workingDirectory, URI relativeRepositoryPath )
102 {
103 this( workingDirectory );
104 this.relativeRepositoryPath = relativeRepositoryPath;
105 }
106
107
108
109
110
111
112
113
114
115
116
117
118
119 public GitStatusConsumer( File workingDirectory, ScmFileSet scmFileSet )
120 {
121 this( workingDirectory );
122 this.scmFileSet = scmFileSet;
123 }
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138 public GitStatusConsumer( File workingDirectory, URI relativeRepositoryPath,
139 ScmFileSet scmFileSet )
140 {
141 this( workingDirectory, scmFileSet );
142 this.relativeRepositoryPath = relativeRepositoryPath;
143 }
144
145
146
147
148
149
150
151
152 public void consumeLine( String line )
153 {
154 if ( logger.isDebugEnabled() )
155 {
156 logger.debug( line );
157 }
158 if ( StringUtils.isEmpty( line ) )
159 {
160 return;
161 }
162
163 ScmFileStatus status = null;
164
165 List<String> files = new ArrayList<String>();
166
167 Matcher matcher;
168 if ( ( matcher = ADDED_PATTERN.matcher( line ) ).find() )
169 {
170 status = ScmFileStatus.ADDED;
171 files.add( resolvePath( matcher.group( 1 ), relativeRepositoryPath ) );
172 }
173 else if ( ( matcher = MODIFIED_PATTERN.matcher( line ) ).find() )
174 {
175 status = ScmFileStatus.MODIFIED;
176 files.add( resolvePath( matcher.group( 1 ), relativeRepositoryPath ) );
177 }
178 else if ( ( matcher = DELETED_PATTERN.matcher( line ) ).find() )
179 {
180 status = ScmFileStatus.DELETED;
181 files.add( resolvePath( matcher.group( 1 ), relativeRepositoryPath ) );
182 }
183 else if ( ( matcher = RENAMED_PATTERN.matcher( line ) ).find() )
184 {
185 status = ScmFileStatus.RENAMED;
186 files.add( resolvePath( matcher.group( 1 ), relativeRepositoryPath ) );
187 files.add( resolvePath( matcher.group( 2 ), relativeRepositoryPath ) );
188 logger.debug( "RENAMED status for line '" + line + "' files added '" + matcher.group( 1 ) + "' '"
189 + matcher.group( 2 ) );
190 }
191 else
192 {
193 logger.warn( "Ignoring unrecognized line: " + line );
194 return;
195 }
196
197
198 if ( !files.isEmpty() )
199 {
200 if ( workingDirectory != null )
201 {
202 if ( status == ScmFileStatus.RENAMED )
203 {
204 String oldFilePath = files.get( 0 );
205 String newFilePath = files.get( 1 );
206 if ( isFile( oldFilePath ) )
207 {
208 logger.debug( "file '" + oldFilePath + "' is a file" );
209 return;
210 }
211 else
212 {
213 logger.debug( "file '" + oldFilePath + "' not a file" );
214 }
215 if ( !isFile( newFilePath ) )
216 {
217 logger.debug( "file '" + newFilePath + "' not a file" );
218 return;
219 }
220 else
221 {
222 logger.debug( "file '" + newFilePath + "' is a file" );
223 }
224 }
225 else if ( status == ScmFileStatus.DELETED )
226 {
227 if ( isFile( files.get( 0 ) ) )
228 {
229 return;
230 }
231 }
232 else
233 {
234 if ( !isFile( files.get( 0 ) ) )
235 {
236 return;
237 }
238 }
239 }
240
241 for ( String file : files )
242 {
243 if ( this.scmFileSet != null && !isFileNameInFileList( this.scmFileSet.getFileList(), file ) )
244 {
245
246 }
247 else
248 {
249 changedFiles.add( new ScmFile( file, status ) );
250 }
251 }
252 }
253 }
254
255 private boolean isFileNameInFileList( List<File> fileList, String fileName )
256 {
257 if ( relativeRepositoryPath == null )
258 {
259 return fileList.contains( new File( fileName ) );
260 }
261 else
262 {
263 for ( File f : fileList )
264 {
265 File file = new File( relativeRepositoryPath.getPath(), fileName );
266 if ( file.getPath().endsWith( f.getName() ) )
267 {
268 return true;
269 }
270 }
271 return fileList.isEmpty();
272 }
273
274 }
275
276 private boolean isFile( String file )
277 {
278 File targetFile = new File( workingDirectory, file );
279 return targetFile.isFile();
280 }
281
282 public static String resolvePath( String fileEntry, URI path )
283 {
284
285 String cleanedEntry = stripQuotes( fileEntry );
286 if ( path != null )
287 {
288 return resolveURI( cleanedEntry, path ).getPath();
289 }
290 else
291 {
292 return cleanedEntry;
293 }
294 }
295
296
297
298
299
300
301
302 public static URI resolveURI( String fileEntry, URI path )
303 {
304
305
306
307 return path.relativize( uriFromPath( stripQuotes ( fileEntry ) ) );
308 }
309
310
311
312
313
314
315
316
317 public static URI uriFromPath( String path )
318 {
319 try
320 {
321 if ( path != null && path.indexOf( ':' ) != -1 )
322 {
323
324 String tmp = new URI( null, null, "/x" + path, null ).toString().substring( 2 );
325
326 return new URI( tmp.replace( ":", "%3A" ) );
327 }
328 else
329 {
330 return new URI( null, null, path, null );
331 }
332 }
333 catch ( URISyntaxException x )
334 {
335 throw new IllegalArgumentException( x.getMessage(), x );
336 }
337 }
338
339 public List<ScmFile> getChangedFiles()
340 {
341 return changedFiles;
342 }
343
344
345
346
347
348 private static String stripQuotes( String str )
349 {
350 int strLen = str.length();
351 return ( strLen > 0 && str.startsWith( "\"" ) && str.endsWith( "\"" ) )
352 ? unescape( str.substring( 1, strLen - 1 ) )
353 : str;
354 }
355
356
357
358
359
360
361
362 private static String unescape( String fileEntry )
363 {
364
365 int pos = fileEntry.indexOf( '\\' );
366 if ( pos == -1 )
367 {
368 return fileEntry;
369 }
370
371
372 byte[] inba = fileEntry.getBytes();
373 int inSub = 0;
374 byte[] outba = new byte[fileEntry.length()];
375 int outSub = 0;
376
377 while ( true )
378 {
379 System.arraycopy( inba, inSub, outba, outSub, pos - inSub );
380 outSub += pos - inSub;
381 inSub = pos + 1;
382 switch ( (char) inba[inSub++] )
383 {
384 case '"':
385 outba[outSub++] = '"';
386 break;
387
388 case 'a':
389 outba[outSub++] = 7;
390 break;
391
392 case 'b':
393 outba[outSub++] = '\b';
394 break;
395
396 case 't':
397 outba[outSub++] = '\t';
398 break;
399
400 case 'n':
401 outba[outSub++] = '\n';
402 break;
403
404 case 'v':
405 outba[outSub++] = 11;
406 break;
407
408 case 'f':
409 outba[outSub++] = '\f';
410 break;
411
412 case 'r':
413 outba[outSub++] = '\f';
414 break;
415
416 case '\\':
417 outba[outSub++] = '\\';
418 break;
419
420 case '0':
421 case '1':
422 case '2':
423 case '3':
424
425 byte b = (byte) ( ( inba[inSub - 1] - '0' ) << 6 );
426 b |= (byte) ( ( inba[inSub++] - '0' ) << 3 );
427 b |= (byte) ( inba[inSub++] - '0' );
428 outba[outSub++] = b;
429 break;
430
431 default:
432
433 outba[outSub++] = '\\';
434 inSub--;
435 break;
436 }
437 pos = fileEntry.indexOf( '\\', inSub );
438 if ( pos == -1 )
439 {
440 System.arraycopy( inba, inSub, outba, outSub, inba.length - inSub );
441 outSub += inba.length - inSub;
442 break;
443 }
444 }
445 try
446 {
447
448 return new String( outba, 0, outSub, "UTF-8" );
449 }
450 catch ( UnsupportedEncodingException e )
451 {
452 throw new RuntimeException( e );
453 }
454 }
455 }