001 package org.apache.maven.scm.provider.git.command.diff;
002
003 /*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements. See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership. The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License. You may obtain a copy of the License at
011 *
012 * http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied. See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022 import org.apache.maven.scm.ScmFile;
023 import org.apache.maven.scm.ScmFileStatus;
024 import org.apache.maven.scm.log.ScmLogger;
025 import org.apache.regexp.RE;
026 import org.apache.regexp.RESyntaxException;
027 import org.codehaus.plexus.util.cli.StreamConsumer;
028
029 import java.io.File;
030 import java.util.ArrayList;
031 import java.util.HashMap;
032 import java.util.List;
033 import java.util.Map;
034
035 /**
036 * @author <a href="mailto:brett@apache.org">Brett Porter</a>
037 * @author <a href="mailto:struberg@yahoo.de">Mark Struberg</a>
038 * @author Olivier Lamy
039 *
040 */
041 public class GitDiffConsumer
042 implements StreamConsumer
043 {
044 // diff --git a/readme.txt b/readme.txt
045 // index fea1611..9e131cf 100644
046 // --- a/readme.txt
047 // +++ b/readme.txt
048 // @@ -1 +1 @@
049 // -/readme.txt
050 // \ No newline at end of file
051 // +new version of /readme.txt
052
053
054 /**
055 * patern matches the index line of the diff comparison
056 * paren.1 matches the first file
057 * paren.2 matches the 2nd file
058 */
059 private static final String DIFF_FILES_PATTERN = "^diff --git\\sa/(.*)\\sb/(.*)";
060
061 private static final String START_REVISION_TOKEN = "---";
062
063 private static final String END_REVISION_TOKEN = "+++";
064
065 private static final String ADDED_LINE_TOKEN = "+";
066
067 private static final String REMOVED_LINE_TOKEN = "-";
068
069 private static final String UNCHANGED_LINE_TOKEN = " ";
070
071 private static final String CHANGE_SEPARATOR_TOKEN = "@@";
072
073 private static final String NO_NEWLINE_TOKEN = "\\ No newline at end of file";
074
075 private static final String INDEX_LINE_TOKEN = "index ";
076
077 private static final String NEW_FILE_MODE_TOKEN = "new file mode ";
078
079 private static final String DELETED_FILE_MODE_TOKEN = "deleted file mode ";
080
081 private ScmLogger logger;
082
083 private String currentFile;
084
085 private StringBuilder currentDifference;
086
087 private List<ScmFile> changedFiles = new ArrayList<ScmFile>();
088
089 private Map<String,CharSequence> differences = new HashMap<String,CharSequence>();
090
091 private StringBuilder patch = new StringBuilder();
092
093 /**
094 * @see #DIFF_FILES_PATTERN
095 */
096 private RE filesRegexp;
097
098 // ----------------------------------------------------------------------
099 //
100 // ----------------------------------------------------------------------
101
102 public GitDiffConsumer( ScmLogger logger, File workingDirectory )
103 {
104 this.logger = logger;
105 try
106 {
107 filesRegexp = new RE( DIFF_FILES_PATTERN );
108 }
109 catch ( RESyntaxException ex )
110 {
111 throw new RuntimeException(
112 "INTERNAL ERROR: Could not create regexp to parse git log file. Something is probably wrong with the oro installation.",
113 ex );
114 }
115
116 }
117
118 // ----------------------------------------------------------------------
119 // StreamConsumer Implementation
120 // ----------------------------------------------------------------------
121
122 /** {@inheritDoc} */
123 public void consumeLine( String line )
124 {
125 if ( filesRegexp.match( line ) )
126 {
127 // start a new file
128 currentFile = filesRegexp.getParen( 1 );
129
130 changedFiles.add( new ScmFile( currentFile, ScmFileStatus.MODIFIED ) );
131
132 currentDifference = new StringBuilder();
133
134 differences.put( currentFile, currentDifference );
135
136 patch.append( line ).append( "\n" );
137
138 return;
139 }
140
141 if ( currentFile == null )
142 {
143 if ( logger.isWarnEnabled() )
144 {
145 logger.warn( "Unparseable line: '" + line + "'" );
146 }
147 patch.append( line ).append( "\n" );
148 return;
149 }
150 else if ( line.startsWith( INDEX_LINE_TOKEN ) )
151 {
152 // skip, though could parse to verify start revision and end revision
153 patch.append( line ).append( "\n" );
154 }
155 else if ( line.startsWith( NEW_FILE_MODE_TOKEN ) || line.startsWith( DELETED_FILE_MODE_TOKEN ) )
156 {
157 // skip, though could parse to verify file mode
158 patch.append( line ).append( "\n" );
159 }
160 else if ( line.startsWith( START_REVISION_TOKEN ) )
161 {
162 // skip, though could parse to verify filename, start revision
163 patch.append( line ).append( "\n" );
164 }
165 else if ( line.startsWith( END_REVISION_TOKEN ) )
166 {
167 // skip, though could parse to verify filename, end revision
168 patch.append( line ).append( "\n" );
169 }
170 else if ( line.startsWith( ADDED_LINE_TOKEN ) || line.startsWith( REMOVED_LINE_TOKEN )
171 || line.startsWith( UNCHANGED_LINE_TOKEN ) || line.startsWith( CHANGE_SEPARATOR_TOKEN )
172 || line.equals( NO_NEWLINE_TOKEN ) )
173 {
174 // add to buffer
175 currentDifference.append( line ).append( "\n" );
176 patch.append( line ).append( "\n" );
177 }
178 else
179 {
180 // TODO: handle property differences
181
182 if ( logger.isWarnEnabled() )
183 {
184 logger.warn( "Unparseable line: '" + line + "'" );
185 }
186 patch.append( line ).append( "\n" );
187 // skip to next file
188 currentFile = null;
189 currentDifference = null;
190 }
191 }
192
193 public List<ScmFile> getChangedFiles()
194 {
195 return changedFiles;
196 }
197
198 public Map<String,CharSequence> getDifferences()
199 {
200 return differences;
201 }
202
203 public String getPatch()
204 {
205 return patch.toString();
206 }
207
208 }