001package org.apache.maven.scm.provider.git.gitexe.command.status; 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 022import java.io.File; 023import java.net.URI; 024import java.util.ArrayList; 025import java.util.List; 026import java.util.regex.Matcher; 027import java.util.regex.Pattern; 028 029import org.apache.commons.lang.StringUtils; 030import org.apache.maven.scm.ScmFile; 031import org.apache.maven.scm.ScmFileStatus; 032import org.apache.maven.scm.log.ScmLogger; 033import org.codehaus.plexus.util.cli.StreamConsumer; 034 035/** 036 * @author <a href="mailto:struberg@yahoo.de">Mark Struberg</a> 037 */ 038public class GitStatusConsumer 039 implements StreamConsumer 040{ 041 042 /** 043 * The pattern used to match added file lines 044 */ 045 private static final Pattern ADDED_PATTERN = Pattern.compile( "^A[ M]* (.*)$" ); 046 047 /** 048 * The pattern used to match modified file lines 049 */ 050 private static final Pattern MODIFIED_PATTERN = Pattern.compile( "^ *M[ M]* (.*)$" ); 051 052 /** 053 * The pattern used to match deleted file lines 054 */ 055 private static final Pattern DELETED_PATTERN = Pattern.compile( "^ *D * (.*)$" ); 056 057 /** 058 * The pattern used to match renamed file lines 059 */ 060 private static final Pattern RENAMED_PATTERN = Pattern.compile( "^R (.*) -> (.*)$" ); 061 062 private ScmLogger logger; 063 064 private File workingDirectory; 065 066 /** 067 * Entries are relative to working directory, not to the repositoryroot 068 */ 069 private List<ScmFile> changedFiles = new ArrayList<ScmFile>(); 070 071 private URI relativeRepositoryPath; 072 073 // ---------------------------------------------------------------------- 074 // 075 // ---------------------------------------------------------------------- 076 077 /** 078 * Consumer when workingDirectory and repositoryRootDirectory are the same 079 * 080 * @param logger the logger 081 * @param workingDirectory the working directory 082 */ 083 public GitStatusConsumer( ScmLogger logger, File workingDirectory ) 084 { 085 this.logger = logger; 086 this.workingDirectory = workingDirectory; 087 } 088 089 /** 090 * Assuming that you have to discover the repositoryRoot, this is how you can get the 091 * <code>relativeRepositoryPath</code> 092 * <pre> 093 * URI.create( repositoryRoot ).relativize( fileSet.getBasedir().toURI() ) 094 * </pre> 095 * 096 * @param logger the logger 097 * @param workingDirectory the working directory 098 * @param relativeRepositoryPath the working directory relative to the repository root 099 * @since 1.9 100 * @see GitStatusCommand#createRevparseShowToplevelCommand(org.apache.maven.scm.ScmFileSet) 101 */ 102 public GitStatusConsumer( ScmLogger logger, File workingDirectory, URI relativeRepositoryPath ) 103 { 104 this( logger, workingDirectory ); 105 this.relativeRepositoryPath = relativeRepositoryPath; 106 } 107 108 // ---------------------------------------------------------------------- 109 // StreamConsumer Implementation 110 // ---------------------------------------------------------------------- 111 112 /** 113 * {@inheritDoc} 114 */ 115 public void consumeLine( String line ) 116 { 117 if ( logger.isDebugEnabled() ) 118 { 119 logger.debug( line ); 120 } 121 if ( StringUtils.isEmpty( line ) ) 122 { 123 return; 124 } 125 126 ScmFileStatus status = null; 127 128 List<String> files = new ArrayList<String>(); 129 130 Matcher matcher; 131 if ( ( matcher = ADDED_PATTERN.matcher( line ) ).find() ) 132 { 133 status = ScmFileStatus.ADDED; 134 files.add( resolvePath( matcher.group( 1 ), relativeRepositoryPath ) ); 135 } 136 else if ( ( matcher = MODIFIED_PATTERN.matcher( line ) ).find() ) 137 { 138 status = ScmFileStatus.MODIFIED; 139 files.add( resolvePath( matcher.group( 1 ), relativeRepositoryPath ) ); 140 } 141 else if ( ( matcher = DELETED_PATTERN.matcher( line ) ).find() ) 142 { 143 status = ScmFileStatus.DELETED; 144 files.add( resolvePath( matcher.group( 1 ), relativeRepositoryPath ) ); 145 } 146 else if ( ( matcher = RENAMED_PATTERN.matcher( line ) ).find() ) 147 { 148 status = ScmFileStatus.RENAMED; 149 files.add( resolvePath( matcher.group( 1 ), relativeRepositoryPath ) ); 150 files.add( resolvePath( matcher.group( 2 ), relativeRepositoryPath ) ); 151 logger.debug( "RENAMED status for line '" + line + "' files added '" + matcher.group( 1 ) + "' '" 152 + matcher.group( 2 ) ); 153 } 154 else 155 { 156 logger.warn( "Ignoring unrecognized line: " + line ); 157 return; 158 } 159 160 // If the file isn't a file; don't add it. 161 if ( !files.isEmpty() && status != null ) 162 { 163 if ( workingDirectory != null ) 164 { 165 if ( status == ScmFileStatus.RENAMED ) 166 { 167 String oldFilePath = files.get( 0 ); 168 String newFilePath = files.get( 1 ); 169 if ( isFile( oldFilePath ) ) 170 { 171 logger.debug( "file '" + oldFilePath + "' is a file" ); 172 return; 173 } 174 else 175 { 176 logger.debug( "file '" + oldFilePath + "' not a file" ); 177 } 178 if ( !isFile( newFilePath ) ) 179 { 180 logger.debug( "file '" + newFilePath + "' not a file" ); 181 return; 182 } 183 else 184 { 185 logger.debug( "file '" + newFilePath + "' is a file" ); 186 } 187 } 188 else if ( status == ScmFileStatus.DELETED ) 189 { 190 if ( isFile( files.get( 0 ) ) ) 191 { 192 return; 193 } 194 } 195 else 196 { 197 if ( !isFile( files.get( 0 ) ) ) 198 { 199 return; 200 } 201 } 202 } 203 204 for ( String file : files ) 205 { 206 changedFiles.add( new ScmFile( file, status ) ); 207 } 208 } 209 } 210 211 private boolean isFile( String file ) 212 { 213 File targetFile; 214 if ( relativeRepositoryPath == null ) 215 { 216 targetFile = new File( workingDirectory, file ); 217 } 218 else 219 { 220 targetFile = new File( relativeRepositoryPath.getPath(), file ); 221 } 222 return targetFile.isFile(); 223 } 224 225 protected static String resolvePath( String fileEntry, URI path ) 226 { 227 if ( path != null ) 228 { 229 return resolveURI( fileEntry, path ).getPath(); 230 } 231 else 232 { 233 return fileEntry; 234 } 235 } 236 237 /** 238 * 239 * @param fileEntry the fileEntry, must not be {@code null} 240 * @param path the path, must not be {@code null} 241 * @return 242 */ 243 public static URI resolveURI( String fileEntry, URI path ) 244 { 245 // When using URI.create, spaces need to be escaped but not the slashes, so we can't use 246 // URLEncoder.encode( String, String ) 247 // new File( String ).toURI() results in an absolute URI while path is relative, so that can't be used either. 248 String str = fileEntry.replace( " ", "%20" ); 249 return path.relativize( URI.create( str ) ); 250 } 251 252 253 public List<ScmFile> getChangedFiles() 254 { 255 return changedFiles; 256 } 257}