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
62 // change sets.
63 // See the following links for additional information on the RTC "compare" command:
64 // RTC 2.0.0.2:
65 // http://publib.boulder.ibm.com/infocenter/rtc/v2r0m0/topic/com.ibm.team.scm.doc/topics/r_scm_cli_compare.html
66 // RTC 3.0:
67 // http://publib.boulder.ibm.com/infocenter/clmhelp/v3r0/topic/com.ibm.team.scm.doc/topics/r_scm_cli_compare.html
68 // RTC 3.0.1:
69 // http://publib.boulder.ibm.com/infocenter/clmhelp/v3r0m1/topic/com.ibm.team.scm.doc/topics/r_scm_cli_compare.html
70 //
71 // 2. RTC "diff" command: Compare two states of a file.
72 // See the following links for additional information on the RTC "diff" command:
73 // RTC 2.0.0.2:
74 // http://publib.boulder.ibm.com/infocenter/rtc/v2r0m0/topic/com.ibm.team.scm.doc/topics/r_scm_cli_diff.html
75 // RTC 3.0:
76 // http://publib.boulder.ibm.com/infocenter/clmhelp/v3r0/topic/com.ibm.team.scm.doc/topics/r_scm_cli_diff.html
77 // RTC 3.0.1:
78 // http://publib.boulder.ibm.com/infocenter/clmhelp/v3r0m1/topic/com.ibm.team.scm.doc/topics/r_scm_cli_diff.html
79 //
80 // 3. RTC "status" command: Show modification status of items in a workspace.
81 // See the following links for additional information on the RTC "status" command:
82 // RTC 2.0.0.2:
83 // http://publib.boulder.ibm.com/infocenter/rtc/v2r0m0/topic/com.ibm.team.scm.doc/topics/r_scm_cli_status.html
84 // RTC 3.0:
85 // http://publib.boulder.ibm.com/infocenter/clmhelp/v3r0/topic/com.ibm.team.scm.doc/topics/r_scm_cli_status.html
86 // RTC 3.0.1:
87 // http://publib.boulder.ibm.com/infocenter/clmhelp/v3r0m1/topic/com.ibm.team.scm.doc/topics/r_scm_cli_status.html
88 //
89
90 /**
91 * @author <a href="mailto:ChrisGWarp@gmail.com">Chris Graham</a>
92 */
93 public class JazzDiffCommand
94 extends AbstractDiffCommand
95 {
96 /**
97 * {@inheritDoc}
98 */
99 protected DiffScmResult executeDiffCommand( ScmProviderRepository repo, ScmFileSet fileSet,
100 ScmVersion startRevision, ScmVersion endRevision )
101 throws ScmException
102 {
103 if ( getLogger().isDebugEnabled() )
104 {
105 getLogger().debug( "Executing diff command..." );
106 }
107
108 File baseDir = fileSet.getBasedir();
109 File parentFolder = ( baseDir.getParentFile() != null ) ? baseDir.getParentFile() : baseDir;
110
111 // First execute the status command to get the list of changed files.
112 JazzStatusCommand statusCmd = new JazzStatusCommand();
113 statusCmd.setLogger( getLogger() );
114 StatusScmResult statusCmdResult = statusCmd.executeStatusCommand( repo, fileSet );
115 List<ScmFile> statusScmFiles = statusCmdResult.getChangedFiles();
116
117 // In this case, we also use it across multiple calls to "scm diff" so that we
118 // sum all output into on.
119 JazzScmCommand diffCmd = null;
120 StringBuilder patch = new StringBuilder();
121 Map<String, CharSequence> differences = new HashMap<String, CharSequence>();
122
123 // Now lets iterate through them
124 for ( ScmFile file : statusScmFiles )
125 {
126 if ( file.getStatus() == ScmFileStatus.MODIFIED )
127 {
128 // The "scm status" command returns files relative to the sandbox root.
129 // Whereas the "scm diff" command needs them relative to the working directory.
130 File fullPath = new File( parentFolder, file.getPath() );
131 String relativePath = fullPath.toString().substring( baseDir.toString().length() );
132 getLogger().debug( "Full Path : '" + fullPath + "'" );
133 getLogger().debug( "Relative Path : '" + relativePath + "'" );
134
135 // Now call "scm diff on it"
136 // In this case, we use the DebugLoggerConsumer's ability to store captured output
137 DebugLoggerConsumer diffConsumer = new DebugLoggerConsumer( getLogger() );
138 ErrorConsumer errConsumer = new ErrorConsumer( getLogger() );
139 diffCmd = createDiffCommand( repo, fileSet, relativePath );
140 int status = diffCmd.execute( diffConsumer, errConsumer );
141 if ( status != 0 )
142 {
143 // Return a false result (not the usual SCMResult)
144 return new DiffScmResult( diffCmd.toString(), "The scm diff command failed.",
145 errConsumer.getOutput(), false );
146 }
147 // Append to patch (all combined)
148 patch.append( diffConsumer.getOutput() );
149 // Set the differences map <File, <CharSequence>
150 differences.put( relativePath, diffConsumer.getOutput() );
151 }
152 }
153
154 return new DiffScmResult( diffCmd.toString(), statusCmdResult.getChangedFiles(), differences,
155 patch.toString() );
156 }
157
158 public JazzScmCommand createDiffCommand( ScmProviderRepository repo, ScmFileSet fileSet, String relativePath )
159 {
160 JazzScmCommand command = new JazzScmCommand( JazzConstants.CMD_DIFF, repo, fileSet, getLogger() );
161 command.addArgument( JazzConstants.ARG_FILE );
162 command.addArgument( relativePath );
163 return command;
164 }
165 }