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