View Javadoc

1   /*
2    * Copyright (c) 2010 Kathryn Huxtable
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kathrynhuxtable.maven.wagon.gitsite;
17  
18  import java.io.File;
19  import java.io.IOException;
20  
21  import java.text.DecimalFormat;
22  
23  import java.util.ArrayList;
24  import java.util.List;
25  import java.util.Random;
26  import java.util.Stack;
27  
28  import org.apache.maven.scm.CommandParameter;
29  import org.apache.maven.scm.CommandParameters;
30  import org.apache.maven.scm.ScmBranch;
31  import org.apache.maven.scm.ScmException;
32  import org.apache.maven.scm.ScmFile;
33  import org.apache.maven.scm.ScmFileSet;
34  import org.apache.maven.scm.ScmResult;
35  import org.apache.maven.scm.ScmVersion;
36  import org.apache.maven.scm.command.add.AddScmResult;
37  import org.apache.maven.scm.command.checkin.CheckInScmResult;
38  import org.apache.maven.scm.command.checkout.CheckOutScmResult;
39  import org.apache.maven.scm.command.list.ListScmResult;
40  import org.apache.maven.scm.manager.NoSuchScmProviderException;
41  import org.apache.maven.scm.manager.ScmManager;
42  import org.apache.maven.scm.provider.ScmProvider;
43  import org.apache.maven.scm.provider.ScmProviderRepository;
44  import org.apache.maven.scm.provider.ScmProviderRepositoryWithHost;
45  import org.apache.maven.scm.provider.git.command.GitCommand;
46  import org.apache.maven.scm.provider.git.gitexe.GitExeScmProvider;
47  import org.apache.maven.scm.repository.ScmRepository;
48  import org.apache.maven.scm.repository.ScmRepositoryException;
49  import org.apache.maven.wagon.AbstractWagon;
50  import org.apache.maven.wagon.ConnectionException;
51  import org.apache.maven.wagon.ResourceDoesNotExistException;
52  import org.apache.maven.wagon.TransferFailedException;
53  import org.apache.maven.wagon.authentication.AuthenticationException;
54  import org.apache.maven.wagon.authentication.AuthenticationInfo;
55  import org.apache.maven.wagon.authorization.AuthorizationException;
56  import org.apache.maven.wagon.events.TransferEvent;
57  import org.apache.maven.wagon.proxy.ProxyInfoProvider;
58  import org.apache.maven.wagon.repository.Repository;
59  import org.apache.maven.wagon.resource.Resource;
60  
61  import org.codehaus.plexus.util.FileUtils;
62  import org.codehaus.plexus.util.StringUtils;
63  
64  import org.kathrynhuxtable.maven.wagon.gitsite.git.GitSiteCheckInCommand;
65  import org.kathrynhuxtable.maven.wagon.gitsite.git.GitSiteCheckOutCommand;
66  
67  /**
68   * Wagon provider to deploy site documentation to GitHub's pages system.
69   *
70   * <p>This should do more or less the following, but doesn't because it doesn't
71   * actually delete old files.</p>
72   *
73   * <pre>
74   * mkdir ${checkoutDirectory}
75   * cd ${checkoutDirectory}
76   * git init
77   * git remote add origin ${gitRepoUrl}
78   * git pull origin refs/heads/${siteBranch}
79   * <replace the contents of the checkout directory, except for the .git subdirectory, with the site docs>
80   * git add .
81   * git commit -a -m "Wagon: Deploying site to repository"
82   * git push origin master:${siteBranch}
83   * rm -Rf ${checkoutDirectory}
84   * </pre>
85   * 
86   * We <em>need</em> to create the gh-pages branch if it doesn't already exist:
87   * 
88   * <pre>
89   * cd ${checkoutDirectory}
90   * git symbolic-ref HEAD refs/heads/gh-pages
91   * rm .git/index
92   * git clean -fdx
93   * git add .
94   * git commit -a -m "First pages commit"
95   * git push origin gh-pages
96   * </pre>
97   *
98   * @plexus.component role="org.apache.maven.wagon.Wagon" role-hint="gitsite"
99   *                   instantiation-strategy="per-lookup"
100  * @author           <a href="kathryn@kathrynhuxtable.org">Kathryn Huxtable</a>
101  * @author           <a href="brett@apache.org">Brett Porter</a>
102  * @author           <a href="evenisse@apache.org">Emmanuel Venisse</a>
103  * @author           <a href="carlos@apache.org">Carlos Sanchez</a>
104  * @author           Jason van Zyl
105  */
106 public class GitSiteWagon extends AbstractWagon {
107 
108     /**
109      * The SCM Manager.
110      *
111      * @plexus.requirement
112      */
113     private ScmManager scmManager;
114 
115     /** The site branch. Set in connect. */
116     private String siteBranch;
117 
118     /** The check-out directory. */
119     private File checkoutDirectory;
120 
121     /**
122      * Get the {@link ScmManager} used in this Wagon.
123      *
124      * @return the {@link ScmManager}.
125      */
126     public ScmManager getScmManager() {
127         return scmManager;
128     }
129 
130     /**
131      * Set the {@link ScmManager} used in this Wagon.
132      *
133      * @param scmManager the scmManager to set.
134      */
135     public void setScmManager(ScmManager scmManager) {
136         this.scmManager = scmManager;
137     }
138 
139     /**
140      * Get the {@link siteBranch} used in this Wagon.
141      *
142      * @return the {@link siteBranch}.
143      */
144     public String getSiteBranch() {
145         return siteBranch;
146     }
147 
148     /**
149      * Set the {@link siteBranch} used in this Wagon.
150      *
151      * @param siteBranch the siteBranch to set.
152      */
153     public void setSiteBranch(String siteBranch) {
154         this.siteBranch = siteBranch;
155     }
156 
157     /**
158      * Get the directory where Wagon will checkout files from SCM. This
159      * directory will be deleted!
160      *
161      * @return the {@link checkoutDirectory}.
162      */
163     public File getCheckoutDirectory() {
164         return checkoutDirectory;
165     }
166 
167     /**
168      * Set the directory where Wagon will checkout files from SCM. This
169      * directory will be deleted!
170      *
171      * @param checkoutDirectory the check-out directory to set.
172      */
173     public void setCheckoutDirectory(File checkoutDirectory) {
174         this.checkoutDirectory = checkoutDirectory;
175     }
176 
177     /**
178      * Convenience method to get the {@link ScmProvider} implementation to
179      * handle the provided SCM type.
180      *
181      * @param  scmType type of SCM, eg. <code>svn</code>, <code>cvs</code>
182      *
183      * @return the {@link ScmProvider} that will handle provided SCM type.
184      *
185      * @throws NoSuchScmProviderException if there is no {@link ScmProvider}
186      *                                    able to handle that SCM type.
187      */
188     public ScmProvider getScmProvider(String scmType) throws NoSuchScmProviderException {
189         return getScmManager().getProviderByType(scmType);
190     }
191 
192     /**
193      * This will clean up the checkout directory.
194      *
195      * @throws ConnectionException
196      */
197     public void openConnectionInternal() throws ConnectionException {
198         if (checkoutDirectory == null) {
199             checkoutDirectory = createCheckoutDirectory();
200         }
201 
202         if (checkoutDirectory.exists()) {
203             removeCheckoutDirectory();
204         }
205 
206         checkoutDirectory.mkdirs();
207     }
208 
209     /**
210      * Create the checkout directory.
211      *
212      * @return the File representing the checkout directory.
213      */
214     private File createCheckoutDirectory() {
215         File checkoutDirectory;
216 
217         DecimalFormat fmt = new DecimalFormat("#####");
218 
219         Random rand = new Random(System.currentTimeMillis() + Runtime.getRuntime().freeMemory());
220 
221         do {
222             checkoutDirectory = new File(System.getProperty("java.io.tmpdir"),
223                                          "wagon-scm" + fmt.format(Math.abs(rand.nextInt())) + ".checkout");
224         } while (checkoutDirectory.exists());
225 
226         return checkoutDirectory;
227     }
228 
229     /**
230      * Remove (delete) the checkout directory.
231      *
232      * @throws ConnectionException if unable to clean up the checkout directory.
233      */
234     private void removeCheckoutDirectory() throws ConnectionException {
235         if (checkoutDirectory == null) {
236             return; // Silently return.
237         }
238 
239         try {
240             FileUtils.deleteDirectory(checkoutDirectory);
241         } catch (IOException e) {
242             throw new ConnectionException("Unable to cleanup checkout directory", e);
243         }
244     }
245 
246     /**
247      * Get the SCM repository from the URL.
248      *
249      * @param  url the URL.
250      *
251      * @return the SCM repository.
252      *
253      * @throws ScmRepositoryException     if an SCM error occurs.
254      * @throws NoSuchScmProviderException if there is no matching provider for
255      *                                    the URL.
256      */
257     private ScmRepository getScmRepository(String url) throws ScmRepositoryException, NoSuchScmProviderException {
258         String username = null;
259 
260         String password = null;
261 
262         String privateKey = null;
263 
264         String passphrase = null;
265 
266         if (authenticationInfo != null) {
267             username = authenticationInfo.getUserName();
268 
269             password = authenticationInfo.getPassword();
270 
271             privateKey = authenticationInfo.getPrivateKey();
272 
273             passphrase = authenticationInfo.getPassphrase();
274         }
275 
276         ScmRepository scmRepository = getScmManager().makeScmRepository(url);
277 
278         ScmProviderRepository providerRepository = scmRepository.getProviderRepository();
279 
280         if (StringUtils.isNotEmpty(username)) {
281             providerRepository.setUser(username);
282         }
283 
284         if (StringUtils.isNotEmpty(password)) {
285             providerRepository.setPassword(password);
286         }
287 
288         if (providerRepository instanceof ScmProviderRepositoryWithHost) {
289             ScmProviderRepositoryWithHost providerRepo = (ScmProviderRepositoryWithHost) providerRepository;
290 
291             if (StringUtils.isNotEmpty(privateKey)) {
292                 providerRepo.setPrivateKey(privateKey);
293             }
294 
295             if (StringUtils.isNotEmpty(passphrase)) {
296                 providerRepo.setPassphrase(passphrase);
297             }
298         }
299 
300         return scmRepository;
301     }
302 
303     /**
304      * Configure and perform the check-in process.
305      *
306      * @param  scmProvider   the SCM provider.
307      * @param  scmRepository the SCM repository.
308      * @param  msg           the commit message.
309      *
310      * @throws ScmException
311      */
312     private void checkIn(ScmProvider scmProvider, ScmRepository scmRepository, String msg) throws ScmException {
313         CommandParameters parameters = new CommandParameters();
314 
315         parameters.setScmVersion(CommandParameter.SCM_VERSION, new ScmBranch(siteBranch));
316 
317         parameters.setString(CommandParameter.MESSAGE, msg);
318 
319         ScmResult result = (CheckInScmResult) executeCommand((GitExeScmProvider) scmProvider, new GitSiteCheckInCommand(),
320                                                              scmRepository.getProviderRepository(),
321                                                              new ScmFileSet(checkoutDirectory), parameters);
322 
323         checkScmResult(result);
324     }
325 
326     /**
327      * Configure and perform the check-out process.
328      *
329      * <p>Returns the relative path to targetName in the checkout dir. If the
330      * targetName already exists in the scm, this will be the empty string.</p>
331      *
332      * @param  scmProvider   the SCM provider.
333      * @param  scmRepository the SCM repository.
334      * @param  targetName    the check-out directory.
335      * @param  resource      the resource.
336      *
337      * @return the relative path to targetName in the check-out directory.
338      *
339      * @throws TransferFailedException
340      */
341     private String checkOut(ScmProvider scmProvider, ScmRepository scmRepository, String targetName, Resource resource)
342         throws TransferFailedException {
343         checkoutDirectory = createCheckoutDirectory();
344 
345         Stack<String> stack = new Stack<String>();
346 
347         String target = targetName;
348 
349         // Totally ignore scmRepository parent stuff since that is not supported by all SCMs.
350         // Instead, assume that that url exists. If not, then that's an error.
351         // Check whether targetName, which is a relative path into the scm, exists.
352         // If it doesn't, check the parent, etc.
353 
354         try {
355             while (target.length() > 0
356                     && !scmProvider.list(scmRepository, new ScmFileSet(new File("."), new File(target)), false, (ScmVersion) null)
357                     .isSuccess()) {
358                 stack.push(getFilename(target));
359                 target = getDirname(target);
360             }
361         } catch (ScmException e) {
362             fireTransferError(resource, e, TransferEvent.REQUEST_PUT);
363 
364             throw new TransferFailedException("Error listing repository: " + e.getMessage(), e);
365         }
366 
367         /* A URL for a module will look like: 
368          *   scm:git:ssh://github.com/auser/project.git/module
369          * so we strip the module to get just:
370          *   scm:git:ssh://github.com/auser/project.git
371          * to ensure a successful checkout, then adjust the relative path.
372          */
373         String url = getRepository().getUrl();
374         String relPath = "";
375         if (!url.endsWith(".git")) {
376             final int iGitSuffix = url.lastIndexOf(".git");
377             if (iGitSuffix > 0) {
378                 relPath = url.substring(iGitSuffix + 5) + '/';
379                 url = url.substring(0, iGitSuffix + 4);
380             }
381         }
382 
383         // ok, we've established that target exists, or is empty.
384         // Check the resource out; if it doesn't exist, that means we're in the svn repo url root,
385         // and the configuration is incorrect. We will not try repo.getParent since most scm's don't
386         // implement that.
387 
388         try {
389             scmRepository = getScmRepository(url);
390 
391             CommandParameters parameters = new CommandParameters();
392 
393             parameters.setScmVersion(CommandParameter.SCM_VERSION, new ScmBranch(siteBranch));
394 
395             parameters.setString(CommandParameter.RECURSIVE, "false");
396 
397             CheckOutScmResult ret = (CheckOutScmResult) executeCommand((GitExeScmProvider) scmProvider, new GitSiteCheckOutCommand(),
398                                                                        scmRepository.getProviderRepository(),
399                                                                        new ScmFileSet(new File(checkoutDirectory, "")), parameters);
400 
401             checkScmResult(ret);
402         } catch (ScmException e) {
403             fireTransferError(resource, e, TransferEvent.REQUEST_PUT);
404 
405             throw new TransferFailedException("Error checking out: " + e.getMessage(), e);
406         }
407 
408         // now create the subdirs in target, if it's a parent of targetName
409 
410         while (!stack.isEmpty()) {
411             String p = (String) stack.pop();
412 
413             relPath += p + '/';
414 
415             File newDir = new File(checkoutDirectory, relPath);
416 
417             if (!newDir.mkdirs()) {
418                 throw new TransferFailedException("Failed to create directory " + newDir.getAbsolutePath() + "; parent should exist: "
419                                                   + checkoutDirectory);
420             }
421 
422             try {
423                 addFiles(scmProvider, scmRepository, checkoutDirectory, relPath);
424             } catch (ScmException e) {
425                 fireTransferError(resource, e, TransferEvent.REQUEST_PUT);
426 
427                 throw new TransferFailedException("Failed to add directory " + newDir + " to working copy", e);
428             }
429         }
430 
431         return relPath;
432     }
433 
434     /**
435      * Add a file or directory to a SCM repository. If it's a directory all its
436      * contents are added recursively.
437      *
438      * <p>TODO this is less than optimal, SCM API should provide a way to add a
439      * directory recursively</p>
440      *
441      * @param  scmProvider   SCM provider
442      * @param  scmRepository SCM repository
443      * @param  basedir       local directory corresponding to scmRepository
444      * @param  scmFilePath   path of the file or directory to add, relative to
445      *                       basedir
446      *
447      * @return the number of files added.
448      *
449      * @throws ScmException
450      */
451     private int addFiles(ScmProvider scmProvider, ScmRepository scmRepository, File basedir, String scmFilePath) throws ScmException {
452         int addedFiles = 0;
453 
454         File scmFile = new File(basedir, scmFilePath);
455 
456         if (scmFilePath.length() != 0) {
457             AddScmResult result = scmProvider.add(scmRepository, new ScmFileSet(basedir, new File(scmFilePath)));
458 
459             /*
460              * TODO dirty fix to work around files with property svn:eol-style=native if a file has that property, first
461              * time file is added it fails, second time it succeeds the solution is check if the scm provider is svn and
462              * unset that property when the SCM API allows it
463              */
464             if (!result.isSuccess()) {
465                 result = scmProvider.add(scmRepository, new ScmFileSet(basedir, new File(scmFilePath)));
466             }
467 
468             addedFiles = result.getAddedFiles().size();
469         }
470 
471         String reservedScmFile = scmProvider.getScmSpecificFilename();
472 
473         if (scmFile.isDirectory()) {
474             File[] files = scmFile.listFiles();
475 
476             for (int i = 0; i < files.length; i++) {
477                 if (reservedScmFile != null && !reservedScmFile.equals(files[i].getName())) {
478                     addedFiles += addFiles(scmProvider, scmRepository, basedir,
479                                            (scmFilePath.length() == 0 ? "" : scmFilePath + "/") + files[i].getName());
480                 }
481             }
482         }
483 
484         return addedFiles;
485     }
486 
487     /**
488      * Return whether or not this wagon supports directory copy.
489      *
490      * @return {@code true}
491      *
492      * @see    org.apache.maven.wagon.AbstractWagon#supportsDirectoryCopy()
493      */
494     public boolean supportsDirectoryCopy() {
495         return true;
496     }
497 
498     /**
499      * @see org.apache.maven.wagon.AbstractWagon#connect(org.apache.maven.wagon.repository.Repository,
500      *      org.apache.maven.wagon.authentication.AuthenticationInfo,
501      *      org.apache.maven.wagon.proxy.ProxyInfoProvider)
502      */
503     public void connect(Repository repository, AuthenticationInfo authenticationInfo, ProxyInfoProvider proxyInfoProvider)
504         throws ConnectionException, AuthenticationException {
505         String url = repository.getUrl();
506 
507         if (url.startsWith("gitsite:")) {
508             url = url.substring(8);
509             int index = url.indexOf(':');
510 
511             if (index > -1) {
512                 siteBranch = url.substring(index + 1);
513                 url        = url.substring(0, index);
514             } else {
515                 siteBranch = "gh-pages";
516             }
517 
518             url = "scm:git:ssh://" + url;
519             repository.setUrl(url);
520         }
521 
522         super.connect(repository, authenticationInfo, proxyInfoProvider);
523     }
524 
525     /**
526      * @see org.apache.maven.wagon.Wagon#put(java.io.File, java.lang.String)
527      */
528     public void put(File source, String destination) throws TransferFailedException {
529         throw new TransferFailedException("Not currently supported: put");
530     }
531 
532     /**
533      * @see org.apache.maven.wagon.AbstractWagon#putDirectory(java.io.File, java.lang.String)
534      */
535     public void putDirectory(File sourceDirectory, String destinationDirectory) throws TransferFailedException,
536         ResourceDoesNotExistException, AuthorizationException {
537         if (!sourceDirectory.isDirectory()) {
538             throw new IllegalArgumentException("Source is not a directory: " + sourceDirectory);
539         }
540 
541         Resource target = new Resource(destinationDirectory);
542 
543         firePutInitiated(target, sourceDirectory);
544 
545         try {
546             ScmRepository scmRepository = getScmRepository(getRepository().getUrl());
547 
548             target.setContentLength(sourceDirectory.length());
549             target.setLastModified(sourceDirectory.lastModified());
550 
551             firePutStarted(target, sourceDirectory);
552 
553             String msg = "Wagon: Deploying " + sourceDirectory.getName() + " to repository";
554 
555             ScmProvider scmProvider = getScmProvider(scmRepository.getProvider());
556 
557             String checkoutTargetName = sourceDirectory.isDirectory() ? destinationDirectory : getDirname(destinationDirectory);
558             String relPath            = checkOut(scmProvider, scmRepository, checkoutTargetName, target);
559 
560             File newCheckoutDirectory = new File(checkoutDirectory, relPath);
561 
562             File scmFile = new File(newCheckoutDirectory, sourceDirectory.isDirectory() ? "" : getFilename(destinationDirectory));
563 
564             boolean fileAlreadyInScm = scmFile.exists();
565 
566             if (!scmFile.equals(sourceDirectory)) {
567                 if (sourceDirectory.isDirectory()) {
568                     FileUtils.copyDirectoryStructure(sourceDirectory, scmFile);
569                 } else {
570                     FileUtils.copyFile(sourceDirectory, scmFile);
571                 }
572             }
573 
574             if (!fileAlreadyInScm || scmFile.isDirectory()) {
575                 int addedFiles = addFiles(scmProvider, scmRepository, newCheckoutDirectory,
576                                           sourceDirectory.isDirectory() ? "" : scmFile.getName());
577 
578                 if (!fileAlreadyInScm && addedFiles == 0) {
579                     throw new ScmException("Unable to add file to SCM: " + scmFile + "; see error messages above for more information");
580                 }
581             }
582 
583             checkIn(scmProvider, scmRepository, msg);
584         } catch (ScmException e) {
585             e.printStackTrace();
586             fireTransferError(target, e, TransferEvent.REQUEST_GET);
587 
588             System.exit(1);
589             throw new TransferFailedException("Error interacting with SCM: " + e.getMessage(), e);
590         } catch (IOException e) {
591             fireTransferError(target, e, TransferEvent.REQUEST_GET);
592 
593             throw new TransferFailedException("Error interacting with SCM: " + e.getMessage(), e);
594         }
595 
596         if (sourceDirectory.isFile()) {
597             postProcessListeners(target, sourceDirectory, TransferEvent.REQUEST_PUT);
598         }
599 
600         firePutCompleted(target, sourceDirectory);
601     }
602 
603     /**
604      * Check that the ScmResult was a successful operation
605      *
606      * @param  result the SCM result.
607      *
608      * @throws ScmException
609      */
610     private void checkScmResult(ScmResult result) throws ScmException {
611         if (!result.isSuccess()) {
612             throw new ScmException("Unable to commit file. " + result.getProviderMessage() + " "
613                                    + (result.getCommandOutput() == null ? "" : result.getCommandOutput()));
614         }
615     }
616 
617     /**
618      * @see org.apache.maven.wagon.AbstractWagon#closeConnection()
619      */
620     public void closeConnection() throws ConnectionException {
621         removeCheckoutDirectory();
622     }
623 
624     /**
625      * @see org.apache.maven.wagon.Wagon#getIfNewer(java.lang.String,java.io.File,
626      *      long)
627      */
628     public boolean getIfNewer(String resourceName, File destination, long timestamp) throws TransferFailedException,
629         ResourceDoesNotExistException, AuthorizationException {
630         throw new UnsupportedOperationException("Not currently supported: getIfNewer");
631     }
632 
633     /**
634      * @see org.apache.maven.wagon.Wagon#get(java.lang.String, java.io.File)
635      */
636     public void get(String resourceName, File destination) throws TransferFailedException, ResourceDoesNotExistException,
637         AuthorizationException {
638         throw new UnsupportedOperationException("Not currently supported: get");
639     }
640 
641     /**
642      * Get the file list for the resource.
643      *
644      * @param  resourcePath the resource path.
645      *
646      * @return a List&lt;String&gt; with filenames/directories at the
647      *         resourcepath.
648      *
649      * @throws TransferFailedException
650      * @throws ResourceDoesNotExistException
651      * @throws AuthorizationException
652      *
653      * @see    org.apache.maven.wagon.AbstractWagon#getFileList(java.lang.String)
654      */
655     public List<String> getFileList(String resourcePath) throws TransferFailedException, ResourceDoesNotExistException,
656         AuthorizationException {
657         try {
658             ScmRepository repository = getScmRepository(getRepository().getUrl());
659             ScmProvider   provider   = getScmProvider(repository.getProvider());
660             ListScmResult result     = provider.list(repository,
661                                                      new ScmFileSet(new File("."), new File(resourcePath)), false, (ScmVersion) null);
662 
663             if (!result.isSuccess()) {
664                 throw new ResourceDoesNotExistException(result.getProviderMessage());
665             }
666 
667             List<String> files = new ArrayList<String>();
668 
669             for (ScmFile f : getListScmResultFiles(result)) {
670                 files.add(f.getPath());
671                 System.out.println("LIST FILE: " + f + " (path=" + f.getPath() + ")");
672             }
673 
674             return files;
675         } catch (ScmException e) {
676             throw new TransferFailedException("Error getting filelist from SCM", e);
677         }
678     }
679 
680     /**
681      * Wrapper around listScmResult.getFiles() to avoid having a larger method
682      * needing the suppressWarnings attribute.
683      *
684      * @param  result the ListScmResult.
685      *
686      * @return the files.
687      */
688     @SuppressWarnings("unchecked")
689     private List<ScmFile> getListScmResultFiles(ListScmResult result) {
690         return (List<ScmFile>) result.getFiles();
691     }
692 
693     /**
694      * @see org.apache.maven.wagon.AbstractWagon#resourceExists(java.lang.String)
695      */
696     public boolean resourceExists(String resourceName) throws TransferFailedException, AuthorizationException {
697         try {
698             getFileList(resourceName);
699             return true;
700         } catch (ResourceDoesNotExistException e) {
701             return false;
702         }
703     }
704 
705     /**
706      * Get the filename format for a file.
707      *
708      * @param  filename the file name.
709      *
710      * @return the file name.
711      */
712     private String getFilename(String filename) {
713         String fname = StringUtils.replace(filename, "/", File.separator);
714 
715         return FileUtils.filename(fname);
716     }
717 
718     /**
719      * Get the directory format for a file.
720      *
721      * @param  filename the file name.
722      *
723      * @return the directory name.
724      */
725     private String getDirname(String filename) {
726         String fname = StringUtils.replace(filename, "/", File.separator);
727 
728         return FileUtils.dirname(fname);
729     }
730 
731     /**
732      * Wrapper around gitCommand.execute to handle setting the logger.
733      *
734      * @param  scmProvider the SCM provider.
735      * @param  command     the command.
736      * @param  repository  the SCM repository.
737      * @param  fileSet     the file set.
738      * @param  parameters  any parameters to the command.
739      *
740      * @return the SCM result.
741      *
742      * @throws ScmException
743      */
744     protected ScmResult executeCommand(GitExeScmProvider scmProvider, GitCommand command, ScmProviderRepository repository,
745             ScmFileSet fileSet, CommandParameters parameters) throws ScmException {
746         command.setLogger(scmProvider.getLogger());
747 
748         return command.execute(repository, fileSet, parameters);
749     }
750 }