330de7fb7c8467a1b96b6a38f1abd29ddb817df0
[teamcity/git-plugin.git] / git-server / src / jetbrains / buildServer / buildTriggers / vcs / git / CommitLoaderImpl.java
1 /*
2  * Copyright 2000-2018 JetBrains s.r.o.
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
17 package jetbrains.buildServer.buildTriggers.vcs.git;
18
19 import com.intellij.openapi.diagnostic.Logger;
20 import jetbrains.buildServer.vcs.VcsException;
21 import org.eclipse.jgit.lib.ObjectId;
22 import org.eclipse.jgit.lib.Ref;
23 import org.eclipse.jgit.lib.Repository;
24 import org.eclipse.jgit.revwalk.RevCommit;
25 import org.eclipse.jgit.revwalk.RevWalk;
26 import org.eclipse.jgit.transport.RefSpec;
27 import org.eclipse.jgit.transport.URIish;
28 import org.jetbrains.annotations.NotNull;
29 import org.jetbrains.annotations.Nullable;
30
31 import java.io.File;
32 import java.io.IOException;
33 import java.util.Collection;
34 import java.util.HashMap;
35 import java.util.Map;
36
37 import static java.util.Arrays.asList;
38
39 public class CommitLoaderImpl implements CommitLoader {
40
41   private static final Logger LOG = Logger.getInstance(CommitLoaderImpl.class.getName());
42   private static final Logger PERFORMANCE_LOG = Logger.getInstance(CommitLoaderImpl.class.getName() + ".Performance");
43
44   private final RepositoryManager myRepositoryManager;
45   private final FetchCommand myFetchCommand;
46   private final GitMapFullPath myMapFullPath;
47
48   public CommitLoaderImpl(@NotNull RepositoryManager repositoryManager,
49                           @NotNull FetchCommand fetchCommand,
50                           @NotNull GitMapFullPath mapFullPath) {
51     myRepositoryManager = repositoryManager;
52     myFetchCommand = fetchCommand;
53     myMapFullPath = mapFullPath;
54     myMapFullPath.setCommitLoader(this);
55   }
56
57   @NotNull
58   public RevCommit loadCommit(@NotNull OperationContext context,
59                               @NotNull GitVcsRoot root,
60                               @NotNull String revision) throws VcsException, IOException {
61     String commitSHA = GitUtils.versionRevision(revision);
62     Repository db = context.getRepository(root);
63     ObjectId commitId = ObjectId.fromString(commitSHA);
64     try {
65       return getCommit(db, commitId);
66     } catch (IOException ex) {
67       //ignore error, will try to fetch
68     }
69
70     LOG.debug("Cannot find commit " + commitSHA + " in repository " + root.debugInfo() + ", fetch branch " + root.getRef());
71     fetchBranchData(root, db);
72
73     try {
74       return getCommit(db, commitId);
75     } catch (IOException e) {
76       LOG.debug("Cannot find commit " + commitSHA + " in the branch " + root.getRef() +
77                 " of repository " + root.debugInfo() + ", fetch all branches");
78       RefSpec spec = new RefSpec().setSourceDestination("refs/*", "refs/*").setForceUpdate(true);
79       fetch(db, root.getRepositoryFetchURL(), asList(spec), new FetchSettings(root.getAuthSettings()));
80       try {
81         return getCommit(db, commitId);
82       } catch (IOException e1) {
83         throw new VcsException("Cannot find commit " + commitSHA + " in repository " + root.debugInfo());
84       }
85     }
86   }
87
88   public void fetch(@NotNull Repository db,
89                     @NotNull URIish fetchURI,
90                     @NotNull Collection<RefSpec> refspecs,
91                     @NotNull FetchSettings settings) throws IOException, VcsException {
92     File repositoryDir = db.getDirectory();
93     assert repositoryDir != null : "Non-local repository";
94     final long start = System.currentTimeMillis();
95     synchronized (myRepositoryManager.getWriteLock(repositoryDir)) {
96       final long finish = System.currentTimeMillis();
97       final long waitTime = finish - start;
98       if (waitTime > 20000) {
99         // if wait time was significant, report it in progress
100         settings.getProgress().reportProgress("Waited for exclusive lock in cloned directory, wait time: " + waitTime + "ms");
101       }
102       Map<String, Ref> oldRefs = new HashMap<>(db.getAllRefs());
103       PERFORMANCE_LOG.debug("[waitForWriteLock] repository: " + repositoryDir.getAbsolutePath() + ", took " + waitTime + "ms");
104       myFetchCommand.fetch(db, fetchURI, refspecs, settings);
105       Map<String, Ref> newRefs = new HashMap<>(db.getAllRefs());
106       myMapFullPath.invalidateRevisionsCache(db, oldRefs, newRefs);
107     }
108   }
109
110   @NotNull
111   public RevCommit getCommit(@NotNull Repository repository, @NotNull ObjectId commitId) throws IOException {
112     final long start = System.currentTimeMillis();
113     RevWalk walk = new RevWalk(repository);
114     try {
115       return walk.parseCommit(commitId);
116     } finally {
117       walk.release();
118       final long finish = System.currentTimeMillis();
119       if (PERFORMANCE_LOG.isDebugEnabled()) {
120         PERFORMANCE_LOG.debug("[RevWalk.parseCommit] repository=" + repository.getDirectory().getAbsolutePath() + ", commit=" + commitId.name() + ", took: " + (finish - start) + "ms");
121       }
122     }
123   }
124
125   @Nullable
126   public RevCommit findCommit(@NotNull Repository r, @NotNull String sha) {
127     try {
128       return getCommit(r, ObjectId.fromString(sha));
129     } catch (Exception e) {
130       return null;
131     }
132   }
133
134   private void fetchBranchData(@NotNull GitVcsRoot root, @NotNull Repository repository)
135     throws VcsException, IOException {
136     final String refName = GitUtils.expandRef(root.getRef());
137     RefSpec spec = new RefSpec().setSource(refName).setDestination(refName).setForceUpdate(true);
138     fetch(repository, root.getRepositoryFetchURL(), asList(spec), new FetchSettings(root.getAuthSettings()));
139   }
140 }