mirror of
				https://github.com/scm-manager/scm-manager.git
				synced 2025-10-31 18:46:07 +01:00 
			
		
		
		
	Introduce merge detection for receive hooks
Here we add a merge detection provider for pre and post receive hooks and implement this new provider for git.
This commit is contained in:
		| @@ -21,7 +21,7 @@ | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
|  * SOFTWARE. | ||||
|  */ | ||||
|      | ||||
|  | ||||
| package sonia.scm.repository; | ||||
|  | ||||
| //~--- non-JDK imports -------------------------------------------------------- | ||||
| @@ -337,6 +337,16 @@ public final class GitUtil | ||||
|     return Strings.nullToEmpty(refName).startsWith(PREFIX_HEADS); | ||||
|   } | ||||
|  | ||||
|   public static Ref getBranchIdOrCurrentHead(org.eclipse.jgit.lib.Repository gitRepository, String requestedBranch) throws IOException { | ||||
|     if ( Strings.isNullOrEmpty(requestedBranch) ) { | ||||
|       logger.trace("no default branch configured, use repository head as default"); | ||||
|       Optional<Ref> repositoryHeadRef = GitUtil.getRepositoryHeadRef(gitRepository); | ||||
|       return repositoryHeadRef.orElse(null); | ||||
|     } else { | ||||
|       return GitUtil.getBranchId(gitRepository, requestedBranch); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Method description | ||||
|    * | ||||
|   | ||||
| @@ -0,0 +1,52 @@ | ||||
| /* | ||||
|  * MIT License | ||||
|  * | ||||
|  * Copyright (c) 2020-present Cloudogu GmbH and Contributors | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all | ||||
|  * copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
|  * SOFTWARE. | ||||
|  */ | ||||
|  | ||||
| package sonia.scm.repository.api; | ||||
|  | ||||
| import org.eclipse.jgit.lib.Repository; | ||||
| import sonia.scm.repository.ChangesetPagingResult; | ||||
| import sonia.scm.repository.spi.GitLogComputer; | ||||
| import sonia.scm.repository.spi.HookMergeDetectionProvider; | ||||
| import sonia.scm.repository.spi.LogCommandRequest; | ||||
|  | ||||
| public class GitPostReceiveHookMergeDetectionProvider implements HookMergeDetectionProvider { | ||||
|   private final Repository repository; | ||||
|   private final String repositoryId; | ||||
|  | ||||
|   public GitPostReceiveHookMergeDetectionProvider(Repository repository, String repositoryId) { | ||||
|     this.repository = repository; | ||||
|     this.repositoryId = repositoryId; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public boolean branchesMerged(String target, String branch) { | ||||
|     LogCommandRequest request = new LogCommandRequest(); | ||||
|     request.setBranch(branch); | ||||
|     request.setAncestorChangeset(target); | ||||
|     request.setPagingLimit(1); | ||||
|  | ||||
|     ChangesetPagingResult changesets = new GitLogComputer(repositoryId, repository).compute(request); | ||||
|     return changesets.getTotal() == 0; | ||||
|   } | ||||
| } | ||||
| @@ -0,0 +1,63 @@ | ||||
| /* | ||||
|  * MIT License | ||||
|  * | ||||
|  * Copyright (c) 2020-present Cloudogu GmbH and Contributors | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all | ||||
|  * copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
|  * SOFTWARE. | ||||
|  */ | ||||
|  | ||||
| package sonia.scm.repository.api; | ||||
|  | ||||
| import org.eclipse.jgit.lib.AnyObjectId; | ||||
| import org.eclipse.jgit.lib.Repository; | ||||
| import org.eclipse.jgit.transport.ReceiveCommand; | ||||
| import sonia.scm.repository.ChangesetPagingResult; | ||||
| import sonia.scm.repository.GitUtil; | ||||
| import sonia.scm.repository.spi.GitLogComputer; | ||||
| import sonia.scm.repository.spi.HookMergeDetectionProvider; | ||||
| import sonia.scm.repository.spi.LogCommandRequest; | ||||
|  | ||||
| import java.util.List; | ||||
|  | ||||
| public class GitPreReceiveHookMergeDetectionProvider implements HookMergeDetectionProvider { | ||||
|   private final List<ReceiveCommand> receiveCommands; | ||||
|   private final Repository repository; | ||||
|   private final String repositoryId; | ||||
|  | ||||
|   public GitPreReceiveHookMergeDetectionProvider(List<ReceiveCommand> receiveCommands, Repository repository, String repositoryId) { | ||||
|     this.receiveCommands = receiveCommands; | ||||
|     this.repository = repository; | ||||
|     this.repositoryId = repositoryId; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public boolean branchesMerged(String target, String branch) { | ||||
|  | ||||
|     String sourceToUse = receiveCommands.stream().filter(receiveCommand -> GitUtil.getBranch(receiveCommand.getRef()).equals(branch)).findFirst().map(ReceiveCommand::getNewId).map(AnyObjectId::getName).orElse(branch); | ||||
|     String targetToUse = receiveCommands.stream().filter(receiveCommand -> GitUtil.getBranch(receiveCommand.getRef()).equals(target)).findFirst().map(ReceiveCommand::getNewId).map(AnyObjectId::getName).orElse(target); | ||||
|  | ||||
|     LogCommandRequest request = new LogCommandRequest(); | ||||
|     request.setBranch(sourceToUse); | ||||
|     request.setAncestorChangeset(targetToUse); | ||||
|     request.setPagingLimit(1); | ||||
|  | ||||
|     ChangesetPagingResult changesets = new GitLogComputer(repositoryId, repository).compute(request); | ||||
|     return changesets.getTotal() == 0; | ||||
|   } | ||||
| } | ||||
| @@ -59,6 +59,7 @@ import static java.util.Optional.empty; | ||||
| import static java.util.Optional.of; | ||||
| import static sonia.scm.ContextEntry.ContextBuilder.entity; | ||||
| import static sonia.scm.NotFoundException.notFound; | ||||
| import static sonia.scm.repository.GitUtil.getBranchIdOrCurrentHead; | ||||
|  | ||||
| //~--- JDK imports ------------------------------------------------------------ | ||||
|  | ||||
| @@ -123,15 +124,9 @@ class AbstractGitCommand | ||||
|   Ref getBranchOrDefault(Repository gitRepository, String requestedBranch) throws IOException { | ||||
|     if ( Strings.isNullOrEmpty(requestedBranch) ) { | ||||
|       String defaultBranchName = context.getConfig().getDefaultBranch(); | ||||
|       if (!Strings.isNullOrEmpty(defaultBranchName)) { | ||||
|         return GitUtil.getBranchId(gitRepository, defaultBranchName); | ||||
|       } else { | ||||
|         logger.trace("no default branch configured, use repository head as default"); | ||||
|         Optional<Ref> repositoryHeadRef = GitUtil.getRepositoryHeadRef(gitRepository); | ||||
|         return repositoryHeadRef.orElse(null); | ||||
|       } | ||||
|       return getBranchIdOrCurrentHead(gitRepository, defaultBranchName); | ||||
|     } else { | ||||
|       return GitUtil.getBranchId(gitRepository, requestedBranch); | ||||
|       return getBranchIdOrCurrentHead(gitRepository, requestedBranch); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -21,27 +21,28 @@ | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
|  * SOFTWARE. | ||||
|  */ | ||||
|      | ||||
|  | ||||
| package sonia.scm.repository.spi; | ||||
|  | ||||
| //~--- non-JDK imports -------------------------------------------------------- | ||||
|  | ||||
| import org.eclipse.jgit.lib.Repository; | ||||
| import org.eclipse.jgit.transport.ReceiveCommand; | ||||
| import org.eclipse.jgit.transport.ReceivePack; | ||||
|  | ||||
| import sonia.scm.repository.RepositoryHookType; | ||||
| import sonia.scm.repository.api.GitHookBranchProvider; | ||||
| import sonia.scm.repository.api.GitHookMessageProvider; | ||||
| import sonia.scm.repository.api.GitHookTagProvider; | ||||
| import sonia.scm.repository.api.GitPostReceiveHookMergeDetectionProvider; | ||||
| import sonia.scm.repository.api.GitPreReceiveHookMergeDetectionProvider; | ||||
| import sonia.scm.repository.api.HookBranchProvider; | ||||
| import sonia.scm.repository.api.HookFeature; | ||||
| import sonia.scm.repository.api.HookMessageProvider; | ||||
|  | ||||
| //~--- JDK imports ------------------------------------------------------------ | ||||
| import sonia.scm.repository.api.HookTagProvider; | ||||
|  | ||||
| import java.util.EnumSet; | ||||
| import java.util.List; | ||||
| import java.util.Set; | ||||
| import sonia.scm.repository.api.GitHookTagProvider; | ||||
| import sonia.scm.repository.api.HookTagProvider; | ||||
|  | ||||
| /** | ||||
|  * | ||||
| @@ -50,24 +51,37 @@ import sonia.scm.repository.api.HookTagProvider; | ||||
| public class GitHookContextProvider extends HookContextProvider | ||||
| { | ||||
|  | ||||
|   /** Field description */ | ||||
|   private static final Set<HookFeature> SUPPORTED_FEATURES = | ||||
|     EnumSet.of(HookFeature.MESSAGE_PROVIDER, HookFeature.CHANGESET_PROVIDER, | ||||
|       HookFeature.BRANCH_PROVIDER, HookFeature.TAG_PROVIDER); | ||||
|   /** | ||||
|    * Field description | ||||
|    */ | ||||
|   private static final Set<HookFeature> SUPPORTED_FEATURES = EnumSet.of( | ||||
|     HookFeature.MESSAGE_PROVIDER, | ||||
|     HookFeature.CHANGESET_PROVIDER, | ||||
|     HookFeature.BRANCH_PROVIDER, | ||||
|     HookFeature.TAG_PROVIDER, | ||||
|     HookFeature.MERGE_DETECTION_PROVIDER | ||||
|   ); | ||||
|  | ||||
|   //~--- constructors --------------------------------------------------------- | ||||
|  | ||||
|   /** | ||||
|    * Constructs a new instance | ||||
|    * | ||||
|    * @param receivePack git receive pack | ||||
|    *  @param receivePack git receive pack | ||||
|    * @param receiveCommands received commands | ||||
|    * @param type | ||||
|    */ | ||||
|   public GitHookContextProvider(ReceivePack receivePack, | ||||
|     List<ReceiveCommand> receiveCommands) | ||||
|   { | ||||
|   public GitHookContextProvider( | ||||
|     ReceivePack receivePack, | ||||
|     List<ReceiveCommand> receiveCommands, | ||||
|     RepositoryHookType type, | ||||
|     Repository repository, | ||||
|     String repositoryId | ||||
|   ) { | ||||
|     this.receivePack = receivePack; | ||||
|     this.receiveCommands = receiveCommands; | ||||
|     this.type = type; | ||||
|     this.repository = repository; | ||||
|     this.repositoryId = repositoryId; | ||||
|     this.changesetProvider = new GitHookChangesetProvider(receivePack, | ||||
|       receiveCommands); | ||||
|   } | ||||
| @@ -99,20 +113,25 @@ public class GitHookContextProvider extends HookContextProvider | ||||
|     return changesetProvider; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public HookMergeDetectionProvider getMergeDetectionProvider() { | ||||
|     if (type == RepositoryHookType.POST_RECEIVE) { | ||||
|       return new GitPostReceiveHookMergeDetectionProvider(repository, repositoryId); | ||||
|     } else { | ||||
|       return new GitPreReceiveHookMergeDetectionProvider(receiveCommands, repository, repositoryId); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public Set<HookFeature> getSupportedFeatures() | ||||
|   { | ||||
|     return SUPPORTED_FEATURES; | ||||
|   } | ||||
|  | ||||
|   //~--- fields --------------------------------------------------------------- | ||||
|  | ||||
|   /** Field description */ | ||||
|   private final GitHookChangesetProvider changesetProvider; | ||||
|  | ||||
|   /** Field description */ | ||||
|   private final List<ReceiveCommand> receiveCommands; | ||||
|  | ||||
|   /** Field description */ | ||||
|   private final ReceivePack receivePack; | ||||
|   private final RepositoryHookType type; | ||||
|   private final Repository repository; | ||||
|   private final String repositoryId; | ||||
| } | ||||
|   | ||||
| @@ -27,19 +27,12 @@ package sonia.scm.repository.spi; | ||||
| //~--- non-JDK imports -------------------------------------------------------- | ||||
|  | ||||
| import com.google.common.base.Strings; | ||||
| import com.google.common.collect.Lists; | ||||
| import org.eclipse.jgit.errors.MissingObjectException; | ||||
| import org.eclipse.jgit.lib.ObjectId; | ||||
| import org.eclipse.jgit.lib.Ref; | ||||
| import org.eclipse.jgit.lib.Repository; | ||||
| import org.eclipse.jgit.revwalk.RevCommit; | ||||
| import org.eclipse.jgit.revwalk.RevWalk; | ||||
| import org.eclipse.jgit.treewalk.filter.AndTreeFilter; | ||||
| import org.eclipse.jgit.treewalk.filter.PathFilter; | ||||
| import org.eclipse.jgit.treewalk.filter.TreeFilter; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| import sonia.scm.NotFoundException; | ||||
| import sonia.scm.repository.Changeset; | ||||
| import sonia.scm.repository.ChangesetPagingResult; | ||||
| import sonia.scm.repository.GitChangesetConverter; | ||||
| @@ -48,9 +41,6 @@ import sonia.scm.repository.InternalRepositoryException; | ||||
| import sonia.scm.util.IOUtil; | ||||
|  | ||||
| import java.io.IOException; | ||||
| import java.util.Collections; | ||||
| import java.util.Iterator; | ||||
| import java.util.List; | ||||
|  | ||||
| import static sonia.scm.ContextEntry.ContextBuilder.entity; | ||||
| import static sonia.scm.NotFoundException.notFound; | ||||
| @@ -183,123 +173,14 @@ public class GitLogCommand extends AbstractGitCommand implements LogCommand | ||||
|   @Override | ||||
|   @SuppressWarnings("unchecked") | ||||
|   public ChangesetPagingResult getChangesets(LogCommandRequest request) { | ||||
|     if (logger.isDebugEnabled()) { | ||||
|       logger.debug("fetch changesets for request: {}", request); | ||||
|     } | ||||
|  | ||||
|     ChangesetPagingResult changesets = null; | ||||
|     GitChangesetConverter converter = null; | ||||
|     RevWalk revWalk = null; | ||||
|  | ||||
|     try (org.eclipse.jgit.lib.Repository repository = open()) { | ||||
|       if (!repository.getAllRefs().isEmpty()) { | ||||
|         int counter = 0; | ||||
|         int start = request.getPagingStart(); | ||||
|  | ||||
|         if (start < 0) { | ||||
|           if (logger.isErrorEnabled()) { | ||||
|             logger.error("start parameter is negative, reset to 0"); | ||||
|           } | ||||
|  | ||||
|           start = 0; | ||||
|         } | ||||
|  | ||||
|         List<Changeset> changesetList = Lists.newArrayList(); | ||||
|         int limit = request.getPagingLimit(); | ||||
|         ObjectId startId = null; | ||||
|  | ||||
|         if (!Strings.isNullOrEmpty(request.getStartChangeset())) { | ||||
|           startId = repository.resolve(request.getStartChangeset()); | ||||
|         } | ||||
|  | ||||
|         ObjectId endId = null; | ||||
|  | ||||
|         if (!Strings.isNullOrEmpty(request.getEndChangeset())) { | ||||
|           endId = repository.resolve(request.getEndChangeset()); | ||||
|         } | ||||
|  | ||||
|         Ref branch = getBranchOrDefault(repository,request.getBranch()); | ||||
|  | ||||
|         ObjectId ancestorId = null; | ||||
|  | ||||
|         if (!Strings.isNullOrEmpty(request.getAncestorChangeset())) { | ||||
|           ancestorId = repository.resolve(request.getAncestorChangeset()); | ||||
|           if (ancestorId == null) { | ||||
|             throw notFound(entity(REVISION, request.getAncestorChangeset()).in(this.repository)); | ||||
|           } | ||||
|         } | ||||
|  | ||||
|         revWalk = new RevWalk(repository); | ||||
|  | ||||
|         converter = new GitChangesetConverter(repository, revWalk); | ||||
|  | ||||
|         if (!Strings.isNullOrEmpty(request.getPath())) { | ||||
|           revWalk.setTreeFilter( | ||||
|             AndTreeFilter.create( | ||||
|               PathFilter.create(request.getPath()), TreeFilter.ANY_DIFF)); | ||||
|         } | ||||
|  | ||||
|         if (branch != null) { | ||||
|           if (startId != null) { | ||||
|             revWalk.markStart(revWalk.lookupCommit(startId)); | ||||
|           } else { | ||||
|             revWalk.markStart(revWalk.lookupCommit(branch.getObjectId())); | ||||
|           } | ||||
|  | ||||
|           if (ancestorId != null) { | ||||
|             revWalk.markUninteresting(revWalk.lookupCommit(ancestorId)); | ||||
|           } | ||||
|  | ||||
|           Iterator<RevCommit> iterator = revWalk.iterator(); | ||||
|  | ||||
|           while (iterator.hasNext()) { | ||||
|             RevCommit commit = iterator.next(); | ||||
|  | ||||
|             if ((counter >= start) | ||||
|               && ((limit < 0) || (counter < start + limit))) { | ||||
|               changesetList.add(converter.createChangeset(commit)); | ||||
|             } | ||||
|  | ||||
|             counter++; | ||||
|  | ||||
|             if (commit.getId().equals(endId)) { | ||||
|               break; | ||||
|             } | ||||
|           } | ||||
|         } else if (ancestorId != null) { | ||||
|           throw notFound(entity(REVISION, request.getBranch()).in(this.repository)); | ||||
|         } | ||||
|  | ||||
|         if (branch != null) { | ||||
|           changesets = new ChangesetPagingResult(counter, changesetList, GitUtil.getBranch(branch.getName())); | ||||
|         } else { | ||||
|           changesets = new ChangesetPagingResult(counter, changesetList); | ||||
|         } | ||||
|       } else if (logger.isWarnEnabled()) { | ||||
|         logger.warn("the repository {} seems to be empty", | ||||
|           this.repository.getName()); | ||||
|  | ||||
|         changesets = new ChangesetPagingResult(0, Collections.EMPTY_LIST); | ||||
|     try (org.eclipse.jgit.lib.Repository gitRepository = open()) { | ||||
|       if (Strings.isNullOrEmpty(request.getBranch())) { | ||||
|         request.setBranch(context.getConfig().getDefaultBranch()); | ||||
|       } | ||||
|       return new GitLogComputer(this.repository.getId(), gitRepository).compute(request); | ||||
|     } catch (IOException e) { | ||||
|       throw new InternalRepositoryException(repository, "could not create change log", e); | ||||
|     } | ||||
|     catch (MissingObjectException e) | ||||
|     { | ||||
|       throw notFound(entity(REVISION, e.getObjectId().getName()).in(repository)); | ||||
|     } | ||||
|     catch (NotFoundException e) | ||||
|     { | ||||
|       throw e; | ||||
|     } | ||||
|     catch (Exception ex) | ||||
|     { | ||||
|       throw new InternalRepositoryException(repository, "could not create change log", ex); | ||||
|     } | ||||
|     finally | ||||
|     { | ||||
|       IOUtil.close(converter); | ||||
|       GitUtil.release(revWalk); | ||||
|     } | ||||
|  | ||||
|     return changesets; | ||||
|   } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,187 @@ | ||||
| /* | ||||
|  * MIT License | ||||
|  * | ||||
|  * Copyright (c) 2020-present Cloudogu GmbH and Contributors | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in all | ||||
|  * copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
|  * SOFTWARE. | ||||
|  */ | ||||
|  | ||||
| package sonia.scm.repository.spi; | ||||
|  | ||||
| import com.google.common.base.Strings; | ||||
| import com.google.common.collect.Lists; | ||||
| import org.eclipse.jgit.errors.InvalidObjectIdException; | ||||
| import org.eclipse.jgit.errors.MissingObjectException; | ||||
| import org.eclipse.jgit.lib.ObjectId; | ||||
| import org.eclipse.jgit.lib.Ref; | ||||
| import org.eclipse.jgit.lib.Repository; | ||||
| import org.eclipse.jgit.revwalk.RevCommit; | ||||
| import org.eclipse.jgit.revwalk.RevWalk; | ||||
| import org.eclipse.jgit.treewalk.filter.AndTreeFilter; | ||||
| import org.eclipse.jgit.treewalk.filter.PathFilter; | ||||
| import org.eclipse.jgit.treewalk.filter.TreeFilter; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| import sonia.scm.ContextEntry; | ||||
| import sonia.scm.NotFoundException; | ||||
| import sonia.scm.repository.Changeset; | ||||
| import sonia.scm.repository.ChangesetPagingResult; | ||||
| import sonia.scm.repository.GitChangesetConverter; | ||||
| import sonia.scm.repository.GitUtil; | ||||
| import sonia.scm.repository.InternalRepositoryException; | ||||
| import sonia.scm.util.IOUtil; | ||||
|  | ||||
| import java.util.Collections; | ||||
| import java.util.Iterator; | ||||
| import java.util.List; | ||||
|  | ||||
| import static sonia.scm.ContextEntry.ContextBuilder.entity; | ||||
| import static sonia.scm.NotFoundException.notFound; | ||||
|  | ||||
| public class GitLogComputer { | ||||
|  | ||||
|   private static final Logger LOG = LoggerFactory.getLogger(GitLogComputer.class); | ||||
|  | ||||
|   private final String repositoryId; | ||||
|   private final Repository gitRepository; | ||||
|  | ||||
|   public GitLogComputer(String repositoryId, Repository repository) { | ||||
|     this.repositoryId = repositoryId; | ||||
|     this.gitRepository = repository; | ||||
|   } | ||||
|  | ||||
|   public ChangesetPagingResult compute(LogCommandRequest request) { | ||||
|     LOG.debug("fetch changesets for request: {}", request); | ||||
|  | ||||
|     GitChangesetConverter converter = null; | ||||
|     RevWalk revWalk = null; | ||||
|  | ||||
|     try { | ||||
|       if (!gitRepository.getAllRefs().isEmpty()) { | ||||
|         int counter = 0; | ||||
|         int start = request.getPagingStart(); | ||||
|  | ||||
|         if (start < 0) { | ||||
|           LOG.error("start parameter is negative, reset to 0"); | ||||
|  | ||||
|           start = 0; | ||||
|         } | ||||
|  | ||||
|         List<Changeset> changesetList = Lists.newArrayList(); | ||||
|         int limit = request.getPagingLimit(); | ||||
|         ObjectId startId = null; | ||||
|  | ||||
|         if (!Strings.isNullOrEmpty(request.getStartChangeset())) { | ||||
|           startId = gitRepository.resolve(request.getStartChangeset()); | ||||
|         } | ||||
|  | ||||
|         ObjectId endId = null; | ||||
|  | ||||
|         if (!Strings.isNullOrEmpty(request.getEndChangeset())) { | ||||
|           endId = gitRepository.resolve(request.getEndChangeset()); | ||||
|         } | ||||
|  | ||||
|         Ref branch = GitUtil.getBranchIdOrCurrentHead(gitRepository, request.getBranch()); | ||||
|         ObjectId branchId; | ||||
|         if (branch == null) { | ||||
|           if (request.getBranch() != null) { | ||||
|             try { | ||||
|               branchId = ObjectId.fromString(request.getBranch()); | ||||
|             } catch (InvalidObjectIdException e) { | ||||
|               throw notFound(entity(GitLogCommand.REVISION, request.getBranch()).in(sonia.scm.repository.Repository.class, repositoryId)); | ||||
|             } | ||||
|           } else { | ||||
|             branchId = null; | ||||
|           } | ||||
|         } else { | ||||
|           branchId = branch.getObjectId(); | ||||
|         } | ||||
|  | ||||
|         ObjectId ancestorId = null; | ||||
|  | ||||
|         if (!Strings.isNullOrEmpty(request.getAncestorChangeset())) { | ||||
|           ancestorId = gitRepository.resolve(request.getAncestorChangeset()); | ||||
|           if (ancestorId == null) { | ||||
|             throw notFound(entity(GitLogCommand.REVISION, request.getAncestorChangeset()).in(sonia.scm.repository.Repository.class, repositoryId)); | ||||
|           } | ||||
|         } | ||||
|  | ||||
|         revWalk = new RevWalk(gitRepository); | ||||
|  | ||||
|         converter = new GitChangesetConverter(gitRepository, revWalk); | ||||
|  | ||||
|         if (!Strings.isNullOrEmpty(request.getPath())) { | ||||
|           revWalk.setTreeFilter( | ||||
|             AndTreeFilter.create( | ||||
|               PathFilter.create(request.getPath()), TreeFilter.ANY_DIFF)); | ||||
|         } | ||||
|  | ||||
|         if (branchId != null) { | ||||
|           if (startId != null) { | ||||
|             revWalk.markStart(revWalk.lookupCommit(startId)); | ||||
|           } else { | ||||
|             revWalk.markStart(revWalk.lookupCommit(branchId)); | ||||
|           } | ||||
|  | ||||
|           if (ancestorId != null) { | ||||
|             revWalk.markUninteresting(revWalk.lookupCommit(ancestorId)); | ||||
|           } | ||||
|  | ||||
|           Iterator<RevCommit> iterator = revWalk.iterator(); | ||||
|  | ||||
|           while (iterator.hasNext()) { | ||||
|             RevCommit commit = iterator.next(); | ||||
|  | ||||
|             if ((counter >= start) | ||||
|               && ((limit < 0) || (counter < start + limit))) { | ||||
|               changesetList.add(converter.createChangeset(commit)); | ||||
|             } | ||||
|  | ||||
|             counter++; | ||||
|  | ||||
|             if (commit.getId().equals(endId)) { | ||||
|               break; | ||||
|             } | ||||
|           } | ||||
|         } else if (ancestorId != null) { | ||||
|           throw notFound(entity(GitLogCommand.REVISION, request.getBranch()).in(sonia.scm.repository.Repository.class, repositoryId)); | ||||
|         } | ||||
|  | ||||
|         if (branch != null) { | ||||
|           return new ChangesetPagingResult(counter, changesetList, GitUtil.getBranch(branch.getName())); | ||||
|         } else { | ||||
|           return new ChangesetPagingResult(counter, changesetList); | ||||
|         } | ||||
|       } else { | ||||
|         LOG.debug("the repository with id {} seems to be empty", this.repositoryId); | ||||
|  | ||||
|         return new ChangesetPagingResult(0, Collections.EMPTY_LIST); | ||||
|       } | ||||
|     } catch (MissingObjectException e) { | ||||
|       throw notFound(entity(GitLogCommand.REVISION, e.getObjectId().getName()).in(sonia.scm.repository.Repository.class, repositoryId)); | ||||
|     } catch (NotFoundException e) { | ||||
|       throw e; | ||||
|     } catch (Exception ex) { | ||||
|       throw new InternalRepositoryException(entity(sonia.scm.repository.Repository.class, repositoryId).build(), "could not create change log", ex); | ||||
|     } finally { | ||||
|       IOUtil.close(converter); | ||||
|       GitUtil.release(revWalk); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @@ -21,7 +21,7 @@ | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
|  * SOFTWARE. | ||||
|  */ | ||||
|      | ||||
|  | ||||
| package sonia.scm.web; | ||||
|  | ||||
| //~--- non-JDK imports -------------------------------------------------------- | ||||
| @@ -122,8 +122,7 @@ public class GitReceiveHook implements PreReceiveHook, PostReceiveHook | ||||
|  | ||||
|       logger.trace("resolved repository to {}", repositoryId); | ||||
|  | ||||
|       GitHookContextProvider context = new GitHookContextProvider(rpack, | ||||
|                                          receiveCommands); | ||||
|       GitHookContextProvider context = new GitHookContextProvider(rpack, receiveCommands, type, repository, repositoryId); | ||||
|  | ||||
|       hookEventFacade.handle(repositoryId).fireHookEvent(type, context); | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user