View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.scm.provider.git.command.diff;
20  
21  import java.io.File;
22  import java.util.ArrayList;
23  import java.util.HashMap;
24  import java.util.List;
25  import java.util.Map;
26  import java.util.regex.Matcher;
27  import java.util.regex.Pattern;
28  
29  import org.apache.maven.scm.ScmFile;
30  import org.apache.maven.scm.ScmFileStatus;
31  import org.apache.maven.scm.util.AbstractConsumer;
32  
33  /**
34   * @author <a href="mailto:brett@apache.org">Brett Porter</a>
35   * @author <a href="mailto:struberg@yahoo.de">Mark Struberg</a>
36   * @author Olivier Lamy
37   *
38   */
39  public class GitDiffConsumer extends AbstractConsumer {
40      // diff --git a/readme.txt b/readme.txt
41      // index fea1611..9e131cf 100644
42      // --- a/readme.txt
43      // +++ b/readme.txt
44      // @@ -1 +1 @@
45      // -/readme.txt
46      // \ No newline at end of file
47      // +new version of /readme.txt
48  
49      /**
50       * patern matches the index line of the diff comparison
51       * paren.1 matches the first file
52       * paren.2 matches the 2nd file
53       */
54      private static final Pattern DIFF_FILES_PATTERN = Pattern.compile("^diff --git\\sa/(.*)\\sb/(.*)");
55  
56      private static final String START_REVISION_TOKEN = "---";
57  
58      private static final String END_REVISION_TOKEN = "+++";
59  
60      private static final String ADDED_LINE_TOKEN = "+";
61  
62      private static final String REMOVED_LINE_TOKEN = "-";
63  
64      private static final String UNCHANGED_LINE_TOKEN = " ";
65  
66      private static final String CHANGE_SEPARATOR_TOKEN = "@@";
67  
68      private static final String NO_NEWLINE_TOKEN = "\\ No newline at end of file";
69  
70      private static final String INDEX_LINE_TOKEN = "index ";
71  
72      private static final String SIMILARITY_INDEX_LINE_TOKEN = "similarity index ";
73  
74      private static final String RENAME_FROM_LINE_TOKEN = "rename from ";
75  
76      private static final String RENAME_TO_LINE_TOKEN = "rename to ";
77  
78      private static final String NEW_FILE_MODE_TOKEN = "new file mode ";
79  
80      private static final String DELETED_FILE_MODE_TOKEN = "deleted file mode ";
81  
82      private String currentFile;
83  
84      private StringBuilder currentDifference;
85  
86      private final List<ScmFile> changedFiles = new ArrayList<>();
87  
88      private final Map<String, CharSequence> differences = new HashMap<>();
89  
90      private final StringBuilder patch = new StringBuilder();
91  
92      // ----------------------------------------------------------------------
93      //
94      // ----------------------------------------------------------------------
95  
96      public GitDiffConsumer(File workingDirectory) {
97          // empty
98      }
99  
100     // ----------------------------------------------------------------------
101     // StreamConsumer Implementation
102     // ----------------------------------------------------------------------
103 
104     /** {@inheritDoc} */
105     public void consumeLine(String line) {
106         Matcher matcher = DIFF_FILES_PATTERN.matcher(line);
107         if (matcher.matches()) {
108             // start a new file
109             currentFile = matcher.group(1);
110 
111             changedFiles.add(new ScmFile(currentFile, ScmFileStatus.MODIFIED));
112 
113             currentDifference = new StringBuilder();
114 
115             differences.put(currentFile, currentDifference);
116 
117             patch.append(line).append("\n");
118 
119             return;
120         }
121 
122         if (currentFile == null) {
123             if (logger.isWarnEnabled()) {
124                 logger.warn("Unparseable line: '" + line + "'");
125             }
126             patch.append(line).append("\n");
127             return;
128         } else if (line.startsWith(INDEX_LINE_TOKEN)) {
129             // skip, though could parse to verify start revision and end revision
130             patch.append(line).append("\n");
131         } else if (line.startsWith(NEW_FILE_MODE_TOKEN) || line.startsWith(DELETED_FILE_MODE_TOKEN)) {
132             // skip, though could parse to verify file mode
133             patch.append(line).append("\n");
134         } else if (line.startsWith(START_REVISION_TOKEN)) {
135             // skip, though could parse to verify filename, start revision
136             patch.append(line).append("\n");
137         } else if (line.startsWith(END_REVISION_TOKEN)) {
138             // skip, though could parse to verify filename, end revision
139             patch.append(line).append("\n");
140         } else if (line.startsWith(SIMILARITY_INDEX_LINE_TOKEN)) {
141             // skip
142             patch.append(line).append("\n");
143         } else if (line.startsWith(RENAME_FROM_LINE_TOKEN) || line.startsWith(RENAME_TO_LINE_TOKEN)) {
144             // skip, though could parse to verify filename
145             patch.append(line).append("\n");
146         } else if (line.startsWith(ADDED_LINE_TOKEN)
147                 || line.startsWith(REMOVED_LINE_TOKEN)
148                 || line.startsWith(UNCHANGED_LINE_TOKEN)
149                 || line.startsWith(CHANGE_SEPARATOR_TOKEN)
150                 || line.equals(NO_NEWLINE_TOKEN)) {
151             // add to buffer
152             currentDifference.append(line).append("\n");
153             patch.append(line).append("\n");
154         } else {
155             // TODO: handle property differences
156 
157             if (logger.isWarnEnabled()) {
158                 logger.warn("Unparseable line: '" + line + "'");
159             }
160             patch.append(line).append("\n");
161             // skip to next file
162             currentFile = null;
163             currentDifference = null;
164         }
165     }
166 
167     public List<ScmFile> getChangedFiles() {
168         return changedFiles;
169     }
170 
171     public Map<String, CharSequence> getDifferences() {
172         return differences;
173     }
174 
175     public String getPatch() {
176         return patch.toString();
177     }
178 }