001package org.apache.maven.scm.provider.git.jgit.command.checkout; 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 org.apache.maven.scm.ScmException; 023import org.apache.maven.scm.ScmFile; 024import org.apache.maven.scm.ScmFileSet; 025import org.apache.maven.scm.ScmFileStatus; 026import org.apache.maven.scm.ScmTag; 027import org.apache.maven.scm.ScmVersion; 028import org.apache.maven.scm.command.checkout.AbstractCheckOutCommand; 029import org.apache.maven.scm.command.checkout.CheckOutScmResult; 030import org.apache.maven.scm.command.remoteinfo.RemoteInfoScmResult; 031import org.apache.maven.scm.provider.ScmProviderRepository; 032import org.apache.maven.scm.provider.git.command.GitCommand; 033import org.apache.maven.scm.provider.git.jgit.command.JGitTransportConfigCallback; 034import org.apache.maven.scm.provider.git.jgit.command.JGitUtils; 035import org.apache.maven.scm.provider.git.jgit.command.branch.JGitBranchCommand; 036import org.apache.maven.scm.provider.git.jgit.command.remoteinfo.JGitRemoteInfoCommand; 037import org.apache.maven.scm.provider.git.repository.GitScmProviderRepository; 038import org.codehaus.plexus.util.StringUtils; 039import org.eclipse.jgit.api.CloneCommand; 040import org.eclipse.jgit.api.FetchCommand; 041import org.eclipse.jgit.api.Git; 042import org.eclipse.jgit.api.PullCommand; 043import org.eclipse.jgit.api.TransportConfigCallback; 044import org.eclipse.jgit.lib.Constants; 045import org.eclipse.jgit.lib.ProgressMonitor; 046import org.eclipse.jgit.revwalk.RevCommit; 047import org.eclipse.jgit.revwalk.RevWalk; 048import org.eclipse.jgit.storage.file.WindowCacheConfig; 049import org.eclipse.jgit.transport.CredentialsProvider; 050import org.eclipse.jgit.treewalk.TreeWalk; 051 052import java.io.File; 053import java.util.ArrayList; 054import java.util.List; 055import java.util.Set; 056 057/** 058 * @author <a href="mailto:struberg@yahoo.de">Mark Struberg</a> 059 * @author Dominik Bartholdi (imod) 060 * @since 1.9 061 */ 062public class JGitCheckOutCommand 063 extends AbstractCheckOutCommand 064 implements GitCommand 065{ 066 /** 067 * For git, the given repository is a remote one. We have to clone it first if the working directory does not 068 * contain a git repo yet, otherwise we have to git-pull it. 069 * <p> 070 * {@inheritDoc} 071 */ 072 protected CheckOutScmResult executeCheckOutCommand( ScmProviderRepository repo, ScmFileSet fileSet, 073 ScmVersion version, boolean recursive, boolean shallow ) 074 throws ScmException 075 { 076 GitScmProviderRepository repository = (GitScmProviderRepository) repo; 077 078 if ( GitScmProviderRepository.PROTOCOL_FILE.equals( repository.getFetchInfo().getProtocol() ) 079 && repository.getFetchInfo().getPath().indexOf( fileSet.getBasedir().getPath() ) >= 0 ) 080 { 081 throw new ScmException( "remote repository must not be the working directory" ); 082 } 083 084 Git git = null; 085 try 086 { 087 088 ProgressMonitor monitor = JGitUtils.getMonitor( getLogger() ); 089 090 String branch = version != null ? version.getName() : null; 091 092 if ( StringUtils.isBlank( branch ) ) 093 { 094 branch = Constants.MASTER; 095 } 096 097 getLogger().debug( "try checkout of branch: " + branch ); 098 099 if ( !fileSet.getBasedir().exists() || !( new File( fileSet.getBasedir(), ".git" ).exists() ) ) 100 { 101 if ( fileSet.getBasedir().exists() ) 102 { 103 // git refuses to clone otherwise 104 fileSet.getBasedir().delete(); 105 } 106 107 // FIXME only if windauze 108 WindowCacheConfig cfg = new WindowCacheConfig(); 109 cfg.setPackedGitMMAP( false ); 110 cfg.install(); 111 112 // no git repo seems to exist, let's clone the original repo 113 CredentialsProvider credentials = JGitUtils.getCredentials( (GitScmProviderRepository) repo ); 114 getLogger().info( "cloning [" + branch + "] to " + fileSet.getBasedir() ); 115 CloneCommand command = Git.cloneRepository().setURI( repository.getFetchUrl() ); 116 117 command.setCredentialsProvider( credentials ).setBranch( branch ).setDirectory( fileSet.getBasedir() ); 118 119 TransportConfigCallback transportConfigCallback = new JGitTransportConfigCallback( 120 (GitScmProviderRepository) repo, getLogger() ); 121 command.setTransportConfigCallback( transportConfigCallback ); 122 123 command.setProgressMonitor( monitor ); 124 git = command.call(); 125 } 126 127 JGitRemoteInfoCommand remoteInfoCommand = new JGitRemoteInfoCommand(); 128 remoteInfoCommand.setLogger( getLogger() ); 129 RemoteInfoScmResult result = remoteInfoCommand.executeRemoteInfoCommand( repository, fileSet, null ); 130 131 if ( git == null ) 132 { 133 // deliberately not using JGitUtils.openRepo(), the caller told us exactly where to checkout 134 git = Git.open( fileSet.getBasedir() ); 135 } 136 137 if ( fileSet.getBasedir().exists() && new File( fileSet.getBasedir(), ".git" ).exists() 138 && result.getBranches().size() > 0 ) 139 { 140 // git repo exists, so we must git-pull the changes 141 CredentialsProvider credentials = JGitUtils.prepareSession( getLogger(), git, repository ); 142 TransportConfigCallback transportConfigCallback = new JGitTransportConfigCallback( 143 (GitScmProviderRepository) repo, getLogger() ); 144 145 if ( version != null && StringUtils.isNotEmpty( version.getName() ) && ( version instanceof ScmTag ) ) 146 { 147 // A tag will not be pulled but we only fetch all the commits from the upstream repo 148 // This is done because checking out a tag might not happen on the current branch 149 // but create a 'detached HEAD'. 150 // In fact, a tag in git may be in multiple branches. This occurs if 151 // you create a branch after the tag has been created 152 getLogger().debug( "fetch..." ); 153 FetchCommand command = git.fetch().setCredentialsProvider( credentials ) 154 .setProgressMonitor( monitor ); 155 command.setTransportConfigCallback( transportConfigCallback ); 156 command.call(); 157 158 } 159 else 160 { 161 getLogger().debug( "pull..." ); 162 PullCommand command = git.pull().setCredentialsProvider( credentials ) 163 .setProgressMonitor( monitor ); 164 command.setTransportConfigCallback( transportConfigCallback ); 165 command.call(); 166 } 167 } 168 169 Set<String> localBranchNames = JGitBranchCommand.getShortLocalBranchNames( git ); 170 if ( version instanceof ScmTag ) 171 { 172 getLogger().info( "checkout tag [" + branch + "] at " + fileSet.getBasedir() ); 173 git.checkout().setName( branch ).call(); 174 } 175 else if ( localBranchNames.contains( branch ) ) 176 { 177 getLogger().info( "checkout [" + branch + "] at " + fileSet.getBasedir() ); 178 git.checkout().setName( branch ).call(); 179 } 180 else 181 { 182 getLogger().info( "checkout remote branch [" + branch + "] at " + fileSet.getBasedir() ); 183 git.checkout().setName( branch ).setCreateBranch( true ).setStartPoint( Constants.DEFAULT_REMOTE_NAME 184 + "/" + branch ).call(); 185 } 186 187 RevWalk revWalk = new RevWalk( git.getRepository() ); 188 RevCommit commit = revWalk.parseCommit( git.getRepository().resolve( Constants.HEAD ) ); 189 revWalk.close(); 190 191 final TreeWalk walk = new TreeWalk( git.getRepository() ); 192 walk.reset(); // drop the first empty tree, which we do not need here 193 walk.setRecursive( true ); 194 walk.addTree( commit.getTree() ); 195 196 List<ScmFile> listedFiles = new ArrayList<ScmFile>(); 197 while ( walk.next() ) 198 { 199 listedFiles.add( new ScmFile( walk.getPathString(), ScmFileStatus.CHECKED_OUT ) ); 200 } 201 walk.close(); 202 203 getLogger().debug( "current branch: " + git.getRepository().getBranch() ); 204 205 return new CheckOutScmResult( "checkout via JGit", listedFiles ); 206 } 207 catch ( Exception e ) 208 { 209 throw new ScmException( "JGit checkout failure!", e ); 210 } 211 finally 212 { 213 JGitUtils.closeRepo( git ); 214 } 215 } 216 217}