View Javadoc
1   package org.apache.maven.scm.provider.jazz.command.diff;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   * http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.File;
23  import java.util.HashMap;
24  import java.util.List;
25  import java.util.Map;
26  
27  import org.apache.maven.scm.ScmException;
28  import org.apache.maven.scm.ScmFile;
29  import org.apache.maven.scm.ScmFileSet;
30  import org.apache.maven.scm.ScmFileStatus;
31  import org.apache.maven.scm.ScmVersion;
32  import org.apache.maven.scm.command.diff.AbstractDiffCommand;
33  import org.apache.maven.scm.command.diff.DiffScmResult;
34  import org.apache.maven.scm.command.status.StatusScmResult;
35  import org.apache.maven.scm.provider.ScmProviderRepository;
36  import org.apache.maven.scm.provider.jazz.command.JazzConstants;
37  import org.apache.maven.scm.provider.jazz.command.JazzScmCommand;
38  import org.apache.maven.scm.provider.jazz.command.consumer.DebugLoggerConsumer;
39  import org.apache.maven.scm.provider.jazz.command.consumer.ErrorConsumer;
40  import org.apache.maven.scm.provider.jazz.command.status.JazzStatusCommand;
41  
42  // The Maven SCM plugin "diff" goal may have different interpretations in RTC depending on how
43  // the user is using RTC. In one instance, the user may expect the diff to report back on the differences between
44  // the local 'sandbox' and their connected repository workspace (ie. What files are 'unresolved'). 
45  // Other users may want the diff the report back the differences between their connected repository workspace
46  // and the stream that it flows with (ie. What files are 'outgoing' / 'incoming').
47  // As a first step, we would have to figure out how to distinguish between these two use cases when using this goal.
48  
49  // Whilst, the above is true, based upon the SVN implementation, its diff does a difference
50  // between the local working copy (sandbox) vs's repository (workspace repository).
51  //
52  // So this implementation will compare the sandbox with the workspace repository (even if there is
53  // a valid flow target). As the "scm diff" command does not support this direct comparison (I have
54  // had an Enhancement Work Item opened to do so), we will call the "scm status" command to get all
55  // of the change files, and then iterate through all of them to get a diff of all of them. The combined
56  // output of all of the various diffs will then be returned as a single output operation.
57  // -Chris 24/02/12
58  
59  // The following RTC commands may be useful however it retrieving the required information. 
60  //
61  // 1. RTC "compare" command:  Compare two workspaces/streams/baselines/snapshots, showing differing baselines and change sets. 
62  // See the following links for additional information on the RTC "compare" command:
63  // RTC 2.0.0.2:
64  // http://publib.boulder.ibm.com/infocenter/rtc/v2r0m0/topic/com.ibm.team.scm.doc/topics/r_scm_cli_compare.html
65  // RTC 3.0:
66  // http://publib.boulder.ibm.com/infocenter/clmhelp/v3r0/topic/com.ibm.team.scm.doc/topics/r_scm_cli_compare.html
67  // RTC 3.0.1:
68  // http://publib.boulder.ibm.com/infocenter/clmhelp/v3r0m1/topic/com.ibm.team.scm.doc/topics/r_scm_cli_compare.html
69  //
70  // 2. RTC "diff" command:  Compare two states of a file. 
71  // See the following links for additional information on the RTC "diff" command:
72  // RTC 2.0.0.2:
73  // http://publib.boulder.ibm.com/infocenter/rtc/v2r0m0/topic/com.ibm.team.scm.doc/topics/r_scm_cli_diff.html
74  // RTC 3.0:
75  // http://publib.boulder.ibm.com/infocenter/clmhelp/v3r0/topic/com.ibm.team.scm.doc/topics/r_scm_cli_diff.html
76  // RTC 3.0.1:
77  // http://publib.boulder.ibm.com/infocenter/clmhelp/v3r0m1/topic/com.ibm.team.scm.doc/topics/r_scm_cli_diff.html
78  //
79  // 3. RTC "status" command:  Show modification status of items in a workspace. 
80  // See the following links for additional information on the RTC "status" command:
81  // RTC 2.0.0.2:
82  // http://publib.boulder.ibm.com/infocenter/rtc/v2r0m0/topic/com.ibm.team.scm.doc/topics/r_scm_cli_status.html
83  // RTC 3.0:
84  // http://publib.boulder.ibm.com/infocenter/clmhelp/v3r0/topic/com.ibm.team.scm.doc/topics/r_scm_cli_status.html
85  // RTC 3.0.1:
86  // http://publib.boulder.ibm.com/infocenter/clmhelp/v3r0m1/topic/com.ibm.team.scm.doc/topics/r_scm_cli_status.html
87  //
88  
89  /**
90   * @author <a href="mailto:ChrisGWarp@gmail.com">Chris Graham</a>
91   */
92  public class JazzDiffCommand
93      extends AbstractDiffCommand
94  {
95      /**
96       * {@inheritDoc}
97       */
98      protected DiffScmResult executeDiffCommand( ScmProviderRepository repo, ScmFileSet fileSet,
99                                                  ScmVersion startRevision, ScmVersion endRevision )
100         throws ScmException
101     {
102         if ( getLogger().isDebugEnabled() )
103         {
104             getLogger().debug( "Executing diff command..." );
105         }
106 
107         File baseDir = fileSet.getBasedir();
108         File parentFolder = ( baseDir.getParentFile() != null ) ? baseDir.getParentFile() : baseDir;
109 
110         // First execute the status command to get the list of changed files.
111         JazzStatusCommand statusCmd = new JazzStatusCommand();
112         statusCmd.setLogger( getLogger() );
113         StatusScmResult statusCmdResult = statusCmd.executeStatusCommand( repo, fileSet );
114         List<ScmFile> statusScmFiles = statusCmdResult.getChangedFiles();
115 
116         // In this case, we also use it across multiple calls to "scm diff" so that we
117         // sum all output into on.
118         JazzScmCommand diffCmd = null;
119         StringBuilder patch = new StringBuilder();
120         Map<String, CharSequence> differences = new HashMap<String, CharSequence>();
121 
122         // Now lets iterate through them
123         for ( ScmFile file : statusScmFiles )
124         {
125             if ( file.getStatus() == ScmFileStatus.MODIFIED )
126             {
127                 // The "scm status" command returns files relative to the sandbox root.
128                 // Whereas the "scm diff" command needs them relative to the working directory.
129                 File fullPath = new File( parentFolder, file.getPath() );
130                 String relativePath = fullPath.toString().substring( baseDir.toString().length() );
131                 getLogger().debug( "Full Path     : '" + fullPath + "'" );
132                 getLogger().debug( "Relative Path : '" + relativePath + "'" );
133 
134                 // Now call "scm diff on it"
135                 // In this case, we use the DebugLoggerConsumer's ability to store captured output
136                 DebugLoggerConsumer diffConsumer = new DebugLoggerConsumer( getLogger() );
137                 ErrorConsumer errConsumer = new ErrorConsumer( getLogger() );
138                 diffCmd = createDiffCommand( repo, fileSet, relativePath );
139                 int status = diffCmd.execute( diffConsumer, errConsumer );
140                 if ( status != 0 || errConsumer.hasBeenFed() )
141                 {
142                     // Return a false result (not the usual SCMResult)
143                     return new DiffScmResult( diffCmd.toString(), "The scm diff command failed.",
144                                               errConsumer.getOutput(), false );
145                 }
146                 // Append to patch (all combined)
147                 patch.append( diffConsumer.getOutput() );
148                 // Set the differences map <File, <CharSequence>
149                 differences.put( relativePath, diffConsumer.getOutput() );
150             }
151         }
152 
153         return new DiffScmResult( diffCmd.toString(), statusCmdResult.getChangedFiles(), differences,
154                                   patch.toString() );
155     }
156 
157     public JazzScmCommand createDiffCommand( ScmProviderRepository repo, ScmFileSet fileSet, String relativePath )
158     {
159         JazzScmCommand command = new JazzScmCommand( JazzConstants.CMD_DIFF, repo, fileSet, getLogger() );
160         command.addArgument( JazzConstants.ARG_FILE );
161         command.addArgument( relativePath );
162         return command;
163     }
164 }