TEAMCITY-GIT-CR-7 TW-56415 Improve tests, don't retry after GitExecTimeout
[teamcity/git-plugin.git] / git-tests / src / jetbrains / buildServer / buildTriggers / vcs / git / tests / AgentVcsSupportTest.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.tests;
18
19 import com.intellij.openapi.util.SystemInfo;
20 import com.intellij.openapi.util.io.StreamUtil;
21 import jetbrains.buildServer.TempFiles;
22 import jetbrains.buildServer.TestInternalProperties;
23 import jetbrains.buildServer.agent.AgentRunningBuild;
24 import jetbrains.buildServer.agent.AgentRuntimeProperties;
25 import jetbrains.buildServer.buildTriggers.vcs.git.*;
26 import jetbrains.buildServer.buildTriggers.vcs.git.Constants;
27 import jetbrains.buildServer.buildTriggers.vcs.git.agent.*;
28 import jetbrains.buildServer.buildTriggers.vcs.git.agent.PluginConfigImpl;
29 import jetbrains.buildServer.buildTriggers.vcs.git.agent.command.FetchCommand;
30 import jetbrains.buildServer.buildTriggers.vcs.git.agent.command.LsRemoteCommand;
31 import jetbrains.buildServer.buildTriggers.vcs.git.agent.command.UpdateRefCommand;
32 import jetbrains.buildServer.buildTriggers.vcs.git.agent.command.impl.*;
33 import jetbrains.buildServer.buildTriggers.vcs.git.agent.errors.GitExecTimeout;
34 import jetbrains.buildServer.ssh.VcsRootSshKeyManager;
35 import jetbrains.buildServer.util.FileUtil;
36 import jetbrains.buildServer.util.TestFor;
37 import jetbrains.buildServer.vcs.CheckoutRules;
38 import jetbrains.buildServer.vcs.VcsException;
39 import jetbrains.buildServer.vcs.impl.VcsRootImpl;
40 import org.eclipse.jgit.api.Git;
41 import org.eclipse.jgit.lib.*;
42 import org.eclipse.jgit.revwalk.RevCommit;
43 import org.eclipse.jgit.transport.URIish;
44 import org.jetbrains.annotations.NotNull;
45 import org.jetbrains.annotations.Nullable;
46 import org.testng.SkipException;
47 import org.testng.annotations.AfterMethod;
48 import org.testng.annotations.BeforeMethod;
49 import org.testng.annotations.DataProvider;
50 import org.testng.annotations.Test;
51
52 import java.io.File;
53 import java.io.FileFilter;
54 import java.io.FileReader;
55 import java.io.IOException;
56 import java.lang.reflect.Method;
57 import java.util.HashMap;
58 import java.util.Map;
59 import java.util.Set;
60 import java.util.concurrent.atomic.AtomicInteger;
61 import java.util.regex.Matcher;
62
63 import static com.intellij.openapi.util.io.FileUtil.copyDir;
64 import static com.intellij.openapi.util.io.FileUtil.delete;
65 import static jetbrains.buildServer.buildTriggers.vcs.git.tests.GitTestUtil.dataFile;
66 import static jetbrains.buildServer.buildTriggers.vcs.git.tests.GitVersionProvider.getGitPath;
67 import static jetbrains.buildServer.buildTriggers.vcs.git.tests.VcsRootBuilder.vcsRoot;
68 import static jetbrains.buildServer.buildTriggers.vcs.git.tests.builders.AgentRunningBuildBuilder.runningBuild;
69 import static jetbrains.buildServer.util.FileUtil.writeFileAndReportErrors;
70 import static jetbrains.buildServer.util.Util.map;
71 import static org.assertj.core.api.BDDAssertions.then;
72 import static org.testng.AssertJUnit.*;
73
74 /**
75  * @author dmitry.neverov
76  */
77 @Test
78 public class AgentVcsSupportTest {
79
80   private TempFiles myTempFiles;
81   private File myMainRepo;
82   private File myCheckoutDir;
83   private VcsRootImpl myRoot;
84   private int myVcsRootId = 0;
85   private GitAgentVcsSupport myVcsSupport;
86   private AgentRunningBuild myBuild;
87   private AgentSupportBuilder myBuilder;
88
89   @BeforeMethod
90   public void setUp() throws Exception {
91     TestInternalProperties.init();
92     myTempFiles = new TempFiles();
93
94     File repositoriesDir = myTempFiles.createTempDir();
95
96     File masterRep = dataFile("repo.git");
97     myMainRepo = new File(repositoriesDir, "repo.git");
98     copyRepository(masterRep, myMainRepo);
99
100     File submoduleRep = dataFile("submodule.git");
101     copyRepository(submoduleRep, new File(repositoriesDir, "submodule.git"));
102
103     File submoduleRep2 = dataFile("sub-submodule.git");
104     copyRepository(submoduleRep2, new File(repositoriesDir, "sub-submodule.git"));
105
106     myCheckoutDir = myTempFiles.createTempDir();
107     myBuilder = new AgentSupportBuilder(myTempFiles);
108     myVcsSupport = myBuilder.build();
109     myBuild = createRunningBuild(true);
110     myRoot = vcsRoot().withAgentGitPath(getGitPath()).withFetchUrl(GitUtils.toURL(myMainRepo)).build();
111   }
112
113
114   @AfterMethod
115   protected void tearDown() throws Exception {
116     myTempFiles.cleanup();
117   }
118
119
120   @TestFor(issues = "TW-33401")
121   @Test(dataProvider = "mirrors")
122   public void should_not_remove_remote_tracking_branches(Boolean useMirrors) throws Exception {
123     VcsRootSshKeyManagerProvider provider = new VcsRootSshKeyManagerProvider() {
124       @Nullable
125       public VcsRootSshKeyManager getSshKeyManager() {
126         return null;
127       }
128     };
129     LoggingGitMetaFactory loggingFactory = new LoggingGitMetaFactory();
130
131     GitAgentVcsSupport git = myBuilder.setSshKeyProvider(provider).setGitMetaFactory(loggingFactory).build();
132
133     AgentRunningBuild build = createRunningBuild(map(PluginConfigImpl.USE_MIRRORS, useMirrors.toString()));
134
135     git.updateSources(myRoot, CheckoutRules.DEFAULT, "465ad9f630e451b9f2b782ffb09804c6a98c4bb9", myCheckoutDir, build, false);
136     loggingFactory.clear();
137
138     //we already have everything we need for this update, no fetch should be executed
139     git.updateSources(myRoot, CheckoutRules.DEFAULT, "465ad9f630e451b9f2b782ffb09804c6a98c4bb9", myCheckoutDir, build, false);
140
141     assertFalse("Refs removed: " + loggingFactory.getInvokedMethods(UpdateRefCommand.class),
142                 loggingFactory.getInvokedMethods(UpdateRefCommand.class).contains("delete"));
143     assertTrue("Redundant fetch", loggingFactory.getInvokedMethods(FetchCommand.class).isEmpty());
144   }
145
146
147   @TestFor(issues = "TW-42249")
148   public void should_not_invoke_fetch_in_working_dir_after_clean_checkout() throws Exception {
149     VcsRootSshKeyManagerProvider provider = new VcsRootSshKeyManagerProvider() {
150       @Nullable
151       public VcsRootSshKeyManager getSshKeyManager() {
152         return null;
153       }
154     };
155     LoggingGitMetaFactory loggingFactory = new LoggingGitMetaFactory();
156     GitAgentVcsSupport git = myBuilder.setSshKeyProvider(provider).setGitMetaFactory(loggingFactory).build();
157
158     AgentRunningBuild build = createRunningBuild(map(PluginConfigImpl.VCS_ROOT_MIRRORS_STRATEGY,
159                                                      PluginConfigImpl.VCS_ROOT_MIRRORS_STRATEGY_ALTERNATES));
160
161     myRoot = vcsRoot().withAgentGitPath(getGitPath()).withFetchUrl(GitUtils.toURL(myMainRepo)).withUseMirrors(true).build();
162     git.updateSources(myRoot, CheckoutRules.DEFAULT, "465ad9f630e451b9f2b782ffb09804c6a98c4bb9", myCheckoutDir, build, false);
163
164     assertEquals("Redundant fetch", 1, loggingFactory.getNumberOfCalls(FetchCommand.class));
165   }
166
167
168   @TestFor(issues = {"TW-42551", "TW-46857"})
169   public void should_set_remote_tracking_branch() throws Exception {
170     AgentRunningBuild build = createRunningBuild(map(PluginConfigImpl.VCS_ROOT_MIRRORS_STRATEGY,
171                                                      PluginConfigImpl.VCS_ROOT_MIRRORS_STRATEGY_ALTERNATES));
172
173     myRoot = vcsRoot().withAgentGitPath(getGitPath()).withFetchUrl(GitUtils.toURL(myMainRepo)).withUseMirrors(true).build();
174     myVcsSupport.updateSources(myRoot, CheckoutRules.DEFAULT, "465ad9f630e451b9f2b782ffb09804c6a98c4bb9", myCheckoutDir, build, false);
175
176     Repository r = new RepositoryBuilder().setWorkTree(myCheckoutDir).build();
177     then(new BranchConfig(r.getConfig(), "master").getRemoteTrackingBranch()).isEqualTo("refs/remotes/origin/master");
178
179     //TW-46857
180     myRoot = vcsRoot().withAgentGitPath(getGitPath()).withBranch("personal-branch2").withFetchUrl(GitUtils.toURL(myMainRepo)).withUseMirrors(true).build();
181     myVcsSupport.updateSources(myRoot, CheckoutRules.DEFAULT, "3df61e6f11a5a9b919cb3f786a83fdd09f058617", myCheckoutDir, build, false);
182     then(new BranchConfig(r.getConfig(), "personal-branch2").getRemoteTrackingBranch()).isEqualTo("refs/remotes/origin/personal-branch2");
183   }
184
185
186   @TestFor(issues = "TW-46854")
187   @Test(dataProvider = "mirrors")
188   public void should_update_remote_tracking_branch_in_case_of_fast_forward_update(boolean useMirrors) throws Exception {
189     File remoteRepo = myTempFiles.createTempDir();
190
191     copyRepository(dataFile("repo_for_fetch.2"), remoteRepo);
192     VcsRootImpl root = vcsRoot().withAgentGitPath(getGitPath()).withFetchUrl(remoteRepo).withUseMirrors(useMirrors).build();
193     String buildBranchParam = GitUtils.getGitRootBranchParamName(root);
194
195     //run build in master branch
196     AgentRunningBuild build = createRunningBuild(map(buildBranchParam, "refs/heads/master"));
197     myVcsSupport.updateSources(root, CheckoutRules.DEFAULT, "d47dda159b27b9a8c4cee4ce98e4435eb5b17168", myCheckoutDir, build, false);
198
199     //fast-forward update master to point to the same commit as master
200     Repository remote = new RepositoryBuilder().setGitDir(remoteRepo).build();
201     RefUpdate refUpdate = remote.updateRef("refs/heads/personal");
202     refUpdate.setNewObjectId(ObjectId.fromString("add81050184d3c818560bdd8839f50024c188586"));
203     refUpdate.update();
204
205     //run build in personal branch
206     build = createRunningBuild(map(buildBranchParam, "refs/heads/personal"));
207     myVcsSupport.updateSources(root, CheckoutRules.DEFAULT, "add81050184d3c818560bdd8839f50024c188586", myCheckoutDir, build, false);
208
209     //fast-forward update personal branch to point to the same commit as master
210     refUpdate = remote.updateRef("refs/heads/personal");
211     refUpdate.setNewObjectId(ObjectId.fromString("d47dda159b27b9a8c4cee4ce98e4435eb5b17168"));
212     refUpdate.update();
213
214     //run build on updated personal branch
215     build = createRunningBuild(map(buildBranchParam, "refs/heads/personal"));
216     myVcsSupport.updateSources(root, CheckoutRules.DEFAULT, "d47dda159b27b9a8c4cee4ce98e4435eb5b17168", myCheckoutDir, build, false);
217
218     //both branch and its remote-tracking branch should be updated
219     Repository r = new RepositoryBuilder().setWorkTree(myCheckoutDir).build();
220     then(r.getAllRefs().get("refs/heads/personal").getObjectId().name()).isEqualTo("d47dda159b27b9a8c4cee4ce98e4435eb5b17168");
221     then(r.getAllRefs().get("refs/remotes/origin/personal").getObjectId().name()).isEqualTo("d47dda159b27b9a8c4cee4ce98e4435eb5b17168");
222   }
223
224
225   /**
226    * Test work normally if .git/index.lock file exists
227    */
228   public void testRecoverIndexLock() throws Exception {
229     myVcsSupport.updateSources(myRoot, new CheckoutRules(""), GitVcsSupportTest.VERSION_TEST_HEAD,
230                                myCheckoutDir, myBuild, false);
231
232     //emulate incorrect git termination (in this it could leave index.lock file)
233     FileUtil.copy(new File(myCheckoutDir, ".git" + File.separator + "index"),
234                   new File(myCheckoutDir, ".git" + File.separator + "index.lock"));
235
236     myVcsSupport.updateSources(myRoot, new CheckoutRules(""), GitVcsSupportTest.CUD1_VERSION, myCheckoutDir, myBuild, false);
237   }
238
239
240   /**
241    * Test work normally if .git/refs/heads/<branch>.lock file exists
242    */
243   public void testRecoverRefLock() throws Exception {
244     myVcsSupport.updateSources(myRoot, new CheckoutRules(""), GitVcsSupportTest.VERSION_TEST_HEAD, myCheckoutDir, myBuild, false);
245
246     String firstCommitInPatchTests = GitUtils.makeVersion("a894d7d58ffde625019a9ecf8267f5f1d1e5c341", 1245766034000L);
247     myRoot.addProperty(Constants.BRANCH_NAME, "patch-tests");
248     myVcsSupport.updateSources(myRoot, new CheckoutRules(""), firstCommitInPatchTests, myCheckoutDir, myBuild, false);
249
250     myRoot.addProperty(Constants.BRANCH_NAME, "master");
251     myVcsSupport.updateSources(myRoot, new CheckoutRules(""), GitVcsSupportTest.VERSION_TEST_HEAD, myCheckoutDir, myBuild, false);
252
253     //emulate incorrect git termination (in this it could leave refs/heads/<branch-name>.lock file)
254     FileUtil.createIfDoesntExist(new File(myCheckoutDir, ".git" + File.separator + GitUtils.expandRef("master") + ".lock"));
255     //should recover from locked ref if previous checkout was on the same branch:
256     myRoot.addProperty(Constants.BRANCH_NAME, "master");
257     myVcsSupport.updateSources(myRoot, new CheckoutRules(""), firstCommitInPatchTests, myCheckoutDir, myBuild, false);
258
259     //emulate incorrect git termination (in this it could leave refs/heads/<branch-name>.lock file)
260     FileUtil.createIfDoesntExist(new File(myCheckoutDir, ".git" + File.separator + GitUtils.expandRef("patch-tests") + ".lock"));
261     //should recover from locked ref if previous checkout was on a different branch:
262     myRoot.addProperty(Constants.BRANCH_NAME, "patch-tests");
263     myVcsSupport.updateSources(myRoot, new CheckoutRules(""), firstCommitInPatchTests, myCheckoutDir, myBuild, false);
264   }
265
266
267   @TestFor(issues = "TW-31381")
268   public void recover_from_ref_lock_during_fetch() throws Exception {
269     File repo = dataFile("repo_for_fetch.2.personal");
270     File remoteRepo = myTempFiles.createTempDir();
271     copyRepository(repo, remoteRepo);
272
273     final String fetchUrl = GitUtils.toURL(remoteRepo);
274     VcsRootImpl root = vcsRoot().withBranch("refs/heads/master").withAgentGitPath(getGitPath()).withFetchUrl(fetchUrl).build();
275     myVcsSupport.updateSources(root, CheckoutRules.DEFAULT, "add81050184d3c818560bdd8839f50024c188586", myCheckoutDir, myBuild, false);
276
277     //update remote branch master
278     delete(remoteRepo);
279     File updatedRepo = dataFile("repo_for_fetch.2");
280     copyRepository(updatedRepo, remoteRepo);
281
282     File mirror = myBuilder.getMirrorManager().getMirrorDir(fetchUrl);
283     FileUtil.createIfDoesntExist(new File(mirror, "refs/heads/master.lock"));
284     FileUtil.createIfDoesntExist(new File(myCheckoutDir, ".git/refs/heads/master.lock"));
285     FileUtil.createIfDoesntExist(new File(myCheckoutDir, ".git/refs/remotes/origin/master.lock"));
286
287
288     myVcsSupport.updateSources(root, CheckoutRules.DEFAULT, "d47dda159b27b9a8c4cee4ce98e4435eb5b17168", myCheckoutDir, myBuild, false);
289   }
290
291
292   @TestFor(issues = "TW-31039")
293   @Test(dataProvider = "mirrors")
294   public void build_on_pull_request(Boolean useMirrors) throws Exception {
295     //Remote repo contains a pull request branch refs/changes/2/1 which is not under refs/heads/*,
296     //this branch points to a commit which is not reachable from the default branch in vcs root
297     //and from any other branches under refs/heads/.
298
299     //Ensure that once we pass a pull request branch name, checkout it successful
300     VcsRootImpl root = createRoot(myMainRepo, "master");
301     String pullRequestCommit = "ea5e05051fbfaa7d8da97586807b009cbfebae9d";
302     AgentRunningBuild build = createRunningBuild(map(PluginConfigImpl.USE_MIRRORS, String.valueOf(useMirrors),
303                                                      GitUtils.getGitRootBranchParamName(root), "refs/changes/2/1"));
304     myVcsSupport.updateSources(root, CheckoutRules.DEFAULT, pullRequestCommit, myCheckoutDir, build, false);
305   }
306
307
308   /**
309    * Test checkout submodules on agent. Machine that runs this test should have git installed.
310    */
311   public void testSubmodulesCheckout() throws Exception {
312     myRoot.addProperty(Constants.BRANCH_NAME, "patch-tests");
313     myRoot.addProperty(Constants.SUBMODULES_CHECKOUT, SubmodulesCheckoutPolicy.CHECKOUT.name());
314
315     myVcsSupport.updateSources(myRoot, new CheckoutRules(""), GitVcsSupportTest.SUBMODULE_ADDED_VERSION,
316                                myCheckoutDir, myBuild, false);
317
318     assertTrue(new File(myCheckoutDir, "submodule" + File.separator + "file.txt").exists());
319   }
320
321
322   /**
323    * Test non-recursive submodules checkout: submodules of submodules are not retrieved
324    */
325   public void testSubSubmodulesCheckoutNonRecursive() throws Exception {
326     testSubSubmoduleCheckout(false);
327   }
328
329
330   /**
331    * Test recursive submodules checkout: submodules of submodules are retrieved
332    */
333   public void testSubSubmodulesCheckoutRecursive() throws Exception {
334     testSubSubmoduleCheckout(true);
335   }
336
337
338   @TestFor(issues = "TW-27043")
339   public void clean_files_in_submodules() throws Exception {
340     //vcs root with submodules which cleans all untracked files on every build:
341     myRoot.addProperty(Constants.BRANCH_NAME, "sub-submodule");
342     myRoot.addProperty(Constants.SUBMODULES_CHECKOUT, SubmodulesCheckoutPolicy.CHECKOUT.name());
343     myRoot.addProperty(Constants.AGENT_CLEAN_FILES_POLICY, AgentCleanFilesPolicy.ALL_UNTRACKED.name());
344     myRoot.addProperty(Constants.AGENT_CLEAN_POLICY, AgentCleanPolicy.ALWAYS.name());
345
346     myVcsSupport.updateSources(myRoot, CheckoutRules.DEFAULT, "ce6044093939bb47283439d97a1c80f759669ff5", myCheckoutDir, myBuild, false);
347
348     //create untracked files inside submodules
349     File submoduleDir = new File(myCheckoutDir, "first-level-submodule");
350     File subSubmoduleDir = new File(submoduleDir, "sub-sub");
351     File untrackedFileSubmodule = new File(submoduleDir, "untracked");
352     File untrackedFileSubSubmodule = new File(subSubmoduleDir, "untracked");
353     assertTrue(untrackedFileSubmodule.createNewFile());
354     assertTrue(untrackedFileSubSubmodule.createNewFile());
355
356     myVcsSupport.updateSources(myRoot, CheckoutRules.DEFAULT, "ce6044093939bb47283439d97a1c80f759669ff5", myCheckoutDir, myBuild, false);
357
358     assertFalse(untrackedFileSubmodule.exists());
359     assertFalse(untrackedFileSubSubmodule.exists());
360   }
361
362
363   @DataProvider(name = "ignoredLongFileNames")
364   public Object[][] ignoredLongFileNames() {
365     return new Object[][] {{true}, {false}};
366   }
367
368   @TestFor(issues = "TW-35545")
369   @Test(dataProvider = "ignoredLongFileNames")
370   public void clean_files_with_long_names(Boolean filesWithLongNamesIgnored) throws Exception {
371     myRoot.addProperty(Constants.AGENT_CLEAN_FILES_POLICY, AgentCleanFilesPolicy.ALL_UNTRACKED.name());
372     myRoot.addProperty(Constants.AGENT_CLEAN_POLICY, AgentCleanPolicy.ALWAYS.name());
373
374     myVcsSupport.updateSources(myRoot, CheckoutRules.DEFAULT, "2276eaf76a658f96b5cf3eb25f3e1fda90f6b653", myCheckoutDir, myBuild, false);
375
376     File dirWithLongName = new File(myCheckoutDir, "dirWithLongName");
377     for (int i = 0; i < 20; i++) {
378       dirWithLongName = new File(dirWithLongName, "dirWithLongName");
379     }
380     dirWithLongName.mkdirs();
381
382     File fileWithLongName = new File(dirWithLongName, "test");
383     writeFileAndReportErrors(fileWithLongName, "test");
384
385     if (filesWithLongNamesIgnored) {
386       File exclude = new File(myCheckoutDir, ".git/info/exclude".replaceAll("/", Matcher.quoteReplacement(File.separator)));
387       writeFileAndReportErrors(exclude, "dirWithLongName\n");
388     }
389
390     assertTrue(fileWithLongName.exists());
391
392     myVcsSupport.updateSources(myRoot, CheckoutRules.DEFAULT, "2276eaf76a658f96b5cf3eb25f3e1fda90f6b653", myCheckoutDir, myBuild, false);
393
394     assertFalse(fileWithLongName.exists());
395   }
396
397
398   public void should_create_bare_repository_in_caches_dir() throws Exception {
399     File mirrorsDir = myBuilder.getAgentConfiguration().getCacheDirectory("git");
400     assertTrue(mirrorsDir.listFiles(new FileFilter() {
401       public boolean accept(File f) {
402         return f.isDirectory();
403       }
404     }).length == 0);
405
406     myVcsSupport.updateSources(myRoot, new CheckoutRules(""), GitVcsSupportTest.VERSION_TEST_HEAD, myCheckoutDir, myBuild, false);
407
408     GitVcsRoot root = new GitVcsRoot(myBuilder.getMirrorManager(), myRoot);
409     File bareRepositoryDir = root.getRepositoryDir();
410     assertTrue(bareRepositoryDir.exists());
411     //check some dirs that should be present in the bare repository:
412     File objectsDir = new File(bareRepositoryDir, "objects");
413     assertTrue(new File(bareRepositoryDir, "info").exists());
414     assertTrue(objectsDir.exists());
415     assertTrue(new File(bareRepositoryDir, "refs").exists());
416
417     String config = FileUtil.loadTextAndClose(new FileReader(new File(bareRepositoryDir, "config")));
418     assertTrue(config.contains("[remote \"origin\"]"));
419     String remoteUrl = "url = " + root.getRepositoryFetchURL();
420     assertTrue(config.contains(remoteUrl));
421
422     File packDir = new File(objectsDir, "pack");
423     boolean looseObjectsExists = objectsDir.listFiles().length > 2;//2 - because there are 2 dirs there: info and pack
424     boolean packFilesExists = packDir.listFiles().length >=2; //at least one pack file with its index exists
425     boolean fetchWasDone = looseObjectsExists || packFilesExists;
426     assertTrue(fetchWasDone);
427   }
428
429
430   public void old_cloned_repository_should_use_local_mirror() throws Exception {
431     AgentRunningBuild buildBeforeUsingMirrors = createRunningBuild(false);
432     myVcsSupport.updateSources(myRoot, CheckoutRules.DEFAULT, GitVcsSupportTest.VERSION_TEST_HEAD, myCheckoutDir, buildBeforeUsingMirrors, false);
433     AgentRunningBuild buildWithMirrorsEnabled = createRunningBuild(true);
434     myVcsSupport.updateSources(myRoot, CheckoutRules.DEFAULT, GitVcsSupportTest.VERSION_TEST_HEAD, myCheckoutDir, buildWithMirrorsEnabled, false);
435     GitVcsRoot root = new GitVcsRoot(myBuilder.getMirrorManager(), myRoot);
436     String localMirrorUrl = new URIish(root.getRepositoryDir().toURI().toASCIIString()).toString();
437     Repository r = new RepositoryBuilder().setWorkTree(myCheckoutDir).build();
438     assertEquals(root.getRepositoryFetchURL().toString(), r.getConfig().getString("url", localMirrorUrl, "insteadOf"));
439   }
440
441
442   public void do_not_use_mirror_if_agent_property_set_to_false() throws Exception {
443     AgentRunningBuild build2 = createRunningBuild(false);
444     myVcsSupport.updateSources(myRoot, new CheckoutRules(""), GitVcsSupportTest.VERSION_TEST_HEAD, myCheckoutDir, build2, false);
445     File gitConfigFile = new File(myCheckoutDir, ".git" + File.separator + "config");
446     String config = FileUtil.loadTextAndClose(new FileReader(gitConfigFile));
447     assertFalse(config, config.contains("insteadOf"));
448   }
449
450
451   public void stop_use_mirror_if_agent_property_changed_to_false() throws Exception {
452     myVcsSupport.updateSources(myRoot, new CheckoutRules(""), GitVcsSupportTest.VERSION_TEST_HEAD, myCheckoutDir, myBuild, false);
453
454     AgentRunningBuild build2 = createRunningBuild(false);
455     myVcsSupport.updateSources(myRoot, new CheckoutRules(""), GitVcsSupportTest.VERSION_TEST_HEAD, myCheckoutDir, build2, false);
456
457     File gitConfigFile = new File(myCheckoutDir, ".git" + File.separator + "config");
458     String config = FileUtil.loadTextAndClose(new FileReader(gitConfigFile));
459     assertFalse(config, config.contains("insteadOf"));
460   }
461
462
463   public void stop_use_any_mirror_if_agent_property_changed_to_false() throws Exception {
464     AgentRunningBuild build2 = createRunningBuild(false);
465     GitVcsRoot root = new GitVcsRoot(myBuilder.getMirrorManager(), myRoot);
466     myVcsSupport.updateSources(myRoot, new CheckoutRules(""), GitVcsSupportTest.VERSION_TEST_HEAD, myCheckoutDir, build2, false);
467
468     //add some mirror
469     Repository r = new RepositoryBuilder().setWorkTree(myCheckoutDir).build();
470     StoredConfig config = r.getConfig();
471     config.setString("url", "/some/path", "insteadOf", root.getRepositoryFetchURL().toString());
472     config.save();
473
474     myVcsSupport.updateSources(myRoot, new CheckoutRules(""), GitVcsSupportTest.VERSION_TEST_HEAD, myCheckoutDir, build2, false);
475     config = new RepositoryBuilder().setWorkTree(myCheckoutDir).build().getConfig();
476     assertTrue(config.getSubsections("url").isEmpty());
477   }
478
479
480   public void stop_using_alternates_when_mirrors_are_disabled_in_vcs_root_option() throws Exception {
481     myRoot = vcsRoot().withAgentGitPath(getGitPath()).withFetchUrl(GitUtils.toURL(myMainRepo)).withUseMirrors(true).build();
482     myVcsSupport.updateSources(myRoot, CheckoutRules.DEFAULT, "2276eaf76a658f96b5cf3eb25f3e1fda90f6b653", myCheckoutDir, myBuild, false);
483
484     myRoot = vcsRoot().withAgentGitPath(getGitPath()).withFetchUrl(GitUtils.toURL(myMainRepo)).withUseMirrors(false).build();
485     AgentRunningBuild build2 = createRunningBuild(false);
486     myVcsSupport.updateSources(myRoot, CheckoutRules.DEFAULT, "2276eaf76a658f96b5cf3eb25f3e1fda90f6b653", myCheckoutDir, build2, false);
487
488     assertFalse("Build uses alternates when they disabled in VCS root settings",
489                 new File(myCheckoutDir, ".git/objects/info/alternates").exists());
490   }
491
492
493   public void stop_using_alternates_when_mirror_strategy_changed() throws Exception {
494     myRoot = vcsRoot().withAgentGitPath(getGitPath()).withFetchUrl(GitUtils.toURL(myMainRepo)).withUseMirrors(true).build();
495     myVcsSupport.updateSources(myRoot, CheckoutRules.DEFAULT, "2276eaf76a658f96b5cf3eb25f3e1fda90f6b653", myCheckoutDir, myBuild, false);
496
497     AgentRunningBuild build2 = createRunningBuild(
498       map(PluginConfigImpl.VCS_ROOT_MIRRORS_STRATEGY, PluginConfigImpl.VCS_ROOT_MIRRORS_STRATEGY_MIRRORS_ONLY));
499     myVcsSupport.updateSources(myRoot, CheckoutRules.DEFAULT, "2276eaf76a658f96b5cf3eb25f3e1fda90f6b653", myCheckoutDir, build2, false);
500
501     assertFalse("Build uses alternates when they disabled in VCS root settings",
502                 new File(myCheckoutDir, ".git/objects/info/alternates").exists());
503   }
504
505
506   public void stop_using_mirrors_when_mirrors_are_disabled_in_vcs_root_option() throws Exception {
507     myRoot = vcsRoot().withAgentGitPath(getGitPath()).withFetchUrl(GitUtils.toURL(myMainRepo)).withUseMirrors(true).build();
508     AgentRunningBuild build1 = createRunningBuild(map(PluginConfigImpl.VCS_ROOT_MIRRORS_STRATEGY, PluginConfigImpl.VCS_ROOT_MIRRORS_STRATEGY_MIRRORS_ONLY));
509     myVcsSupport.updateSources(myRoot, CheckoutRules.DEFAULT, "2276eaf76a658f96b5cf3eb25f3e1fda90f6b653", myCheckoutDir, build1, false);
510
511     myRoot = vcsRoot().withAgentGitPath(getGitPath()).withFetchUrl(GitUtils.toURL(myMainRepo)).withUseMirrors(false).build();
512     AgentRunningBuild build2 = createRunningBuild(false);
513     myVcsSupport.updateSources(myRoot, CheckoutRules.DEFAULT, "2276eaf76a658f96b5cf3eb25f3e1fda90f6b653", myCheckoutDir, build2, false);
514
515     StoredConfig config = new RepositoryBuilder().setWorkTree(myCheckoutDir).build().getConfig();
516     assertTrue(config.getSubsections("url").isEmpty());
517   }
518
519
520   @TestFor(issues = "TW-25839")
521   public void update_should_not_fail_if_local_mirror_is_corrupted() throws Exception {
522     AgentRunningBuild buildWithMirrorsEnabled = createRunningBuild(true);
523     myVcsSupport.updateSources(myRoot, CheckoutRules.DEFAULT, GitVcsSupportTest.VERSION_TEST_HEAD, myCheckoutDir, buildWithMirrorsEnabled, false);
524
525     //corrupt local mirror
526     GitVcsRoot root = new GitVcsRoot(myBuilder.getMirrorManager(), myRoot);
527     File mirror = myBuilder.getMirrorManager().getMirrorDir(root.getRepositoryFetchURL().toString());
528     File[] children = mirror.listFiles();
529     if (children != null) {
530       for (File child : children) {
531         delete(child);
532       }
533     }
534
535     myVcsSupport.updateSources(myRoot, CheckoutRules.DEFAULT, GitVcsSupportTest.VERSION_TEST_HEAD, myCheckoutDir, buildWithMirrorsEnabled,
536                                false);
537   }
538
539
540   @TestFor(issues = "TW-29291")
541   public void shallow_clone_should_check_if_auxiliary_branch_already_exists() throws Exception {
542     AgentRunningBuild build = createRunningBuild(new HashMap<String, String>() {{
543       put(PluginConfigImpl.USE_MIRRORS, "true");
544       put(PluginConfigImpl.USE_SHALLOW_CLONE, "true");
545     }});
546     myVcsSupport.updateSources(myRoot, CheckoutRules.DEFAULT, "2276eaf76a658f96b5cf3eb25f3e1fda90f6b653", myCheckoutDir, build, true);
547
548     //manually create a branch tmp_branch_for_build with, it seems like it wasn't removed due to errors in previous checkouts
549     GitVcsRoot root = new GitVcsRoot(myBuilder.getMirrorManager(), myRoot);
550     File mirror = myBuilder.getMirrorManager().getMirrorDir(root.getRepositoryFetchURL().toString());
551     File emptyBranchFile = new File(mirror, "refs" + File.separator + "heads" + File.separator + "tmp_branch_for_build");
552     FileUtil.writeToFile(emptyBranchFile, "2276eaf76a658f96b5cf3eb25f3e1fda90f6b653\n".getBytes());
553
554     myVcsSupport.updateSources(myRoot, CheckoutRules.DEFAULT, "2276eaf76a658f96b5cf3eb25f3e1fda90f6b653", myCheckoutDir, build, true);
555   }
556
557
558   public void when_fetch_for_mirror_failed_remove_it_and_try_again() throws Exception {
559     File repo = dataFile("repo_for_fetch.1");
560     File remoteRepo = myTempFiles.createTempDir();
561     copyRepository(repo, remoteRepo);
562
563     VcsRootImpl root = vcsRoot().withAgentGitPath(getGitPath()).withFetchUrl(GitUtils.toURL(remoteRepo)).build();
564
565     AgentRunningBuild buildWithMirrors = createRunningBuild(true);
566     myVcsSupport.updateSources(root, CheckoutRules.DEFAULT, "add81050184d3c818560bdd8839f50024c188586", myCheckoutDir, buildWithMirrors, false);
567
568     //create branch tmp in the mirror
569     File mirror = myBuilder.getMirrorManager().getMirrorDir(GitUtils.toURL(remoteRepo));
570     Repository r = new RepositoryBuilder().setBare().setGitDir(mirror).build();
571     RefUpdate update = r.updateRef("refs/heads/tmp");
572     update.setNewObjectId(ObjectId.fromString("add81050184d3c818560bdd8839f50024c188586"));
573     update.update();
574
575     //update remote repo
576     delete(remoteRepo);
577     File updatedRepo = dataFile("repo_for_fetch.2.personal");
578     copyRepository(updatedRepo, remoteRepo);
579
580     //create branch tmp/1 in remote repo, so fetch will fail
581     r = new RepositoryBuilder().setBare().setGitDir(remoteRepo).build();
582     update = r.updateRef("refs/heads/tmp/1");
583     update.setNewObjectId(ObjectId.fromString("d47dda159b27b9a8c4cee4ce98e4435eb5b17168"));
584     update.update();
585
586     //update succeeds
587     myVcsSupport.updateSources(root, CheckoutRules.DEFAULT, "d47dda159b27b9a8c4cee4ce98e4435eb5b17168", myCheckoutDir, buildWithMirrors, false);
588   }
589
590
591   public void should_handle_ref_pointing_to_invalid_object() throws Exception {
592     File repo = dataFile("repo_for_fetch.1");
593     File remoteRepo = myTempFiles.createTempDir();
594     copyRepository(repo, remoteRepo);
595     VcsRootImpl root = vcsRoot().withAgentGitPath(getGitPath()).withFetchUrl(GitUtils.toURL(remoteRepo)).withUseMirrors(true).build();
596
597     //first build
598     AgentRunningBuild build = createRunningBuild(map(PluginConfigImpl.VCS_ROOT_MIRRORS_STRATEGY, PluginConfigImpl.VCS_ROOT_MIRRORS_STRATEGY_ALTERNATES));
599     myVcsSupport.updateSources(root, CheckoutRules.DEFAULT, "add81050184d3c818560bdd8839f50024c188586", myCheckoutDir, build, false);
600
601     //create ref pointing to invalid object
602     File gitDir = new File(myCheckoutDir, ".git");
603     String invalidObject = "bba7fbcc200b4968e6abd2f7d475dc15306cafc1";
604     FileUtil.writeFile(new File(gitDir, "refs/heads/brokenRef"), invalidObject);
605
606     //update remote repo
607     delete(remoteRepo);
608     File updatedRepo = dataFile("repo_for_fetch.3");
609     copyRepository(updatedRepo, remoteRepo);
610
611     //second build
612     build = createRunningBuild(map(PluginConfigImpl.VCS_ROOT_MIRRORS_STRATEGY, PluginConfigImpl.VCS_ROOT_MIRRORS_STRATEGY_ALTERNATES));
613     myVcsSupport.updateSources(root, CheckoutRules.DEFAULT, "bba7fbcc200b4968e6abd2f7d475dc15306cafc6", myCheckoutDir, build, false);
614     then(new File(gitDir, "refs/heads/brokenRef")).doesNotExist();
615   }
616
617
618   @TestFor(issues = "TW-43884")
619   public void should_remap_mirror_if_its_fetch_and_remove_failed() throws Exception {
620     MockFS fs = new MockFS();
621     LoggingGitMetaFactory loggingFactory = new LoggingGitMetaFactory();
622     myVcsSupport = myBuilder.setGitMetaFactory(loggingFactory).setFS(fs).build();
623
624     File repo = dataFile("repo_for_fetch.1");
625     File remoteRepo = myTempFiles.createTempDir();
626     copyRepository(repo, remoteRepo);
627
628     //run build to prepare mirror
629     VcsRootImpl root = vcsRoot().withAgentGitPath(getGitPath()).withFetchUrl(GitUtils.toURL(remoteRepo)).build();
630     myVcsSupport.updateSources(root, CheckoutRules.DEFAULT, "add81050184d3c818560bdd8839f50024c188586", myCheckoutDir, createRunningBuild(true), false);
631
632     //update remote repo: add personal branch
633     delete(remoteRepo);
634     File updatedRepo = dataFile("repo_for_fetch.2.personal");
635     copyRepository(updatedRepo, remoteRepo);
636
637
638     //make first fetch in local mirror to fail:
639     AtomicInteger invocationCount = new AtomicInteger(0);
640     loggingFactory.addCallback(FetchCommand.class.getName() + ".call", new GitCommandProxyCallback() {
641       @Override
642       public void call(final Method method, final Object[] args) throws VcsException {
643         if (invocationCount.getAndIncrement() == 0)
644           throw new VcsException("TEST ERROR");
645       }
646     });
647     File mirror = myBuilder.getMirrorManager().getMirrorDir(GitUtils.toURL(remoteRepo));
648
649     //try to fetch unknown branch, fetch fails and delete of the mirror also fails
650     //build should succeed anyway
651     fs.makeDeleteFail(mirror);
652     VcsRootImpl root2 = vcsRoot().withAgentGitPath(getGitPath()).withBranch("refs/heads/personal").withFetchUrl(GitUtils.toURL(remoteRepo)).build();
653     AgentRunningBuild build = runningBuild()
654       .useLocalMirrors(true)
655       .sharedConfigParams("teamcity.git.fetchMirrorRetryTimeouts", "")
656       .build();
657     myVcsSupport.updateSources(root2, CheckoutRules.DEFAULT, "d47dda159b27b9a8c4cee4ce98e4435eb5b17168", myCheckoutDir, build, false);
658     File mirrorAfterBuild = myBuilder.getMirrorManager().getMirrorDir(GitUtils.toURL(remoteRepo));
659     then(mirrorAfterBuild).isNotEqualTo(mirror);//repository was remapped to another dir
660   }
661
662   @TestFor(issues = "TW-56415")
663   public void should_retry_fetch_mirror() throws Exception {
664     MockFS fs = new MockFS();
665     LoggingGitMetaFactory loggingFactory = new LoggingGitMetaFactory();
666     myVcsSupport = myBuilder.setGitMetaFactory(loggingFactory).setFS(fs).build();
667
668     File repo = dataFile("repo_for_fetch.1");
669     File remoteRepo = myTempFiles.createTempDir();
670     copyRepository(repo, remoteRepo);
671
672     //run build to prepare mirror
673     VcsRootImpl root = vcsRoot().withAgentGitPath(getGitPath()).withFetchUrl(GitUtils.toURL(remoteRepo)).build();
674     myVcsSupport.updateSources(root, CheckoutRules.DEFAULT, "add81050184d3c818560bdd8839f50024c188586", myCheckoutDir, createRunningBuild(true), false);
675
676     //update remote repo: add personal branch
677     delete(remoteRepo);
678     File updatedRepo = dataFile("repo_for_fetch.2.personal");
679     copyRepository(updatedRepo, remoteRepo);
680
681
682     //make first two fetches in local mirror to fail:
683     AtomicInteger invocationCount = new AtomicInteger(0);
684     loggingFactory.addCallback(FetchCommand.class.getName() + ".call", new GitCommandProxyCallback() {
685       @Override
686       public void call(final Method method, final Object[] args) throws VcsException {
687         if (invocationCount.getAndIncrement() <= 1)
688           throw new VcsException("TEST ERROR");
689       }
690     });
691     File mirror = myBuilder.getMirrorManager().getMirrorDir(GitUtils.toURL(remoteRepo));
692
693     //try to fetch unknown branch, first fetch fails, second succeeds. If it's not ensure that delete of the mirror also fails
694     //build should succeed anyway
695     fs.makeDeleteFail(mirror);
696     VcsRootImpl root2 = vcsRoot().withAgentGitPath(getGitPath()).withBranch("refs/heads/personal").withFetchUrl(GitUtils.toURL(remoteRepo)).build();
697     AgentRunningBuild build = runningBuild()
698       .useLocalMirrors(true)
699       .sharedConfigParams("teamcity.git.fetchMirrorRetryTimeouts", "0,0")
700       .build();
701     myVcsSupport.updateSources(root2, CheckoutRules.DEFAULT, "d47dda159b27b9a8c4cee4ce98e4435eb5b17168", myCheckoutDir, build, false);
702     File mirrorAfterBuild = myBuilder.getMirrorManager().getMirrorDir(GitUtils.toURL(remoteRepo));
703     then(mirrorAfterBuild).isEqualTo(mirror);//repository was not remapped to another dir
704   }
705
706   @TestFor(issues = "TW-56415")
707   public void should_not_retry_fetch_mirror_for_exec_timeout() throws Exception {
708     MockFS fs = new MockFS();
709     LoggingGitMetaFactory loggingFactory = new LoggingGitMetaFactory();
710     myVcsSupport = myBuilder.setGitMetaFactory(loggingFactory).setFS(fs).build();
711
712     File repo = dataFile("repo_for_fetch.1");
713     File remoteRepo = myTempFiles.createTempDir();
714     copyRepository(repo, remoteRepo);
715
716     //run build to prepare mirror
717     VcsRootImpl root = vcsRoot().withAgentGitPath(getGitPath()).withFetchUrl(GitUtils.toURL(remoteRepo)).build();
718     myVcsSupport
719       .updateSources(root, CheckoutRules.DEFAULT, "add81050184d3c818560bdd8839f50024c188586", myCheckoutDir, createRunningBuild(true),
720                      false);
721
722     //update remote repo: add personal branch
723     delete(remoteRepo);
724     File updatedRepo = dataFile("repo_for_fetch.2.personal");
725     copyRepository(updatedRepo, remoteRepo);
726
727     //make first two fetches in local mirror to fail:
728     loggingFactory.addCallback(FetchCommand.class.getName() + ".call", new GitCommandProxyCallback() {
729       volatile boolean thrown = false;
730
731       @Override
732       public void call(final Method method, final Object[] args) throws VcsException {
733         if (!thrown) {
734           thrown = true;
735           throw new GitExecTimeout();
736         }
737         fail("Should not try to fetch again");
738       }
739     });
740     File mirror = myBuilder.getMirrorManager().getMirrorDir(GitUtils.toURL(remoteRepo));
741
742     //try to fetch unknown branch, first fetch fails with exec timeout, repo would be remapped
743     VcsRootImpl root2 =
744       vcsRoot().withAgentGitPath(getGitPath()).withBranch("refs/heads/personal").withFetchUrl(GitUtils.toURL(remoteRepo)).build();
745     AgentRunningBuild build = runningBuild()
746       .useLocalMirrors(true)
747       .sharedConfigParams("teamcity.git.fetchMirrorRetryTimeouts", "0,0")
748       .build();
749
750     try {
751       myVcsSupport.updateSources(root2, CheckoutRules.DEFAULT, "d47dda159b27b9a8c4cee4ce98e4435eb5b17168", myCheckoutDir, build, false);
752       fail("GitExecTimeout exception expected");
753     } catch (GitExecTimeout ignored) {
754     }
755
756     //try again, should succeed without remapping, means previous code has not changed mirror directory
757     loggingFactory.addCallback(FetchCommand.class.getName() + ".call", (method, args) -> {
758     });
759     myVcsSupport.updateSources(root2, CheckoutRules.DEFAULT, "d47dda159b27b9a8c4cee4ce98e4435eb5b17168", myCheckoutDir, build, false);
760
761     File mirrorAfterBuild = myBuilder.getMirrorManager().getMirrorDir(GitUtils.toURL(remoteRepo));
762     then(mirrorAfterBuild).isEqualTo(mirror);//repository was not remapped to another dir
763   }
764
765
766   //we run ls-remote during outdated refs cleanup which is needed to
767   //successfully checkout when ref a/b is renamed to A/b on win or mac (TW-28735).
768   //If we continue silently this can cause performance problems (TW-44944)
769   @TestFor(issues = "TW-44944")
770   public void should_fail_when_ls_remote_fails() throws Exception {
771     LoggingGitMetaFactory loggingFactory = new LoggingGitMetaFactory();
772     myVcsSupport = myBuilder.setGitMetaFactory(loggingFactory).build();
773
774     File repo = dataFile("repo_for_fetch.1");
775     File remoteRepo = myTempFiles.createTempDir();
776     copyRepository(repo, remoteRepo);
777
778     //run build to prepare working dir
779     VcsRootImpl root = vcsRoot().withAgentGitPath(getGitPath()).withFetchUrl(GitUtils.toURL(remoteRepo)).build();
780     myVcsSupport.updateSources(root, CheckoutRules.DEFAULT, "add81050184d3c818560bdd8839f50024c188586", myCheckoutDir, createRunningBuild(false), false);
781
782     loggingFactory.addCallback(LsRemoteCommand.class.getName() + ".call", new GitCommandProxyCallback() {
783       @Override
784       public void call(final Method method, final Object[] args) throws VcsException {
785         throw new VcsException("TEST ERROR");
786       }
787     });
788
789     //ls-remote will fail during this build
790     try {
791       myVcsSupport.updateSources(root, CheckoutRules.DEFAULT, "d47dda159b27b9a8c4cee4ce98e4435eb5b17168", myCheckoutDir, createRunningBuild(false), false);
792       fail("should fail");
793     } catch (VcsException e) {
794       assertTrue(true);
795     }
796   }
797
798
799   public void do_not_delete_mirror_if_remote_ref_not_found() throws Exception {
800     MockFS fs = new MockFS();
801     myVcsSupport = myBuilder.setFS(fs).build();
802
803     File mirror = myBuilder.getMirrorManager().getMirrorDir(GitUtils.toURL(myMainRepo));
804     fs.makeDeleteFail(mirror);//if plugin will remove mirror it will fail and try to remap
805     myRoot = vcsRoot().withBranch("refs/heads/unknown").withAgentGitPath(getGitPath()).withFetchUrl(GitUtils.toURL(myMainRepo)).build();
806     try {
807       String unknownRevision = "abababababababababababababababababababab";
808       myVcsSupport.updateSources(myRoot, CheckoutRules.DEFAULT, unknownRevision, myCheckoutDir, createRunningBuild(true), false);
809       fail("update on unknown branch should fail");
810     } catch (VcsException e) {
811       File mirrorAfterFailure = myBuilder.getMirrorManager().getMirrorDir(GitUtils.toURL(myMainRepo));
812       then(mirrorAfterFailure).isEqualTo(mirror);//failure should not cause delete or remap
813     }
814   }
815
816
817   public void do_not_delete_mirror_on_timeout() throws Exception {
818     MockFS fs = new MockFS();
819     myVcsSupport = myBuilder.setFS(fs).build();
820
821     String unreachableRepository = "git://some.org/unreachable.git";
822     File mirror = myBuilder.getMirrorManager().getMirrorDir(unreachableRepository);
823     fs.makeDeleteFail(mirror);//if plugin will remove mirror it will fail and try to remap
824     myRoot = vcsRoot().withAgentGitPath(getGitPath()).withFetchUrl(unreachableRepository).build();
825     try {
826       String revision = "abababababababababababababababababababab";
827       AgentRunningBuild build = runningBuild().useLocalMirrors(true).sharedConfigParams("teamcity.git.idle.timeout.seconds", "1").build();
828       myVcsSupport.updateSources(myRoot, CheckoutRules.DEFAULT, revision, myCheckoutDir, build, false);
829       fail("update on unreachable repository should fail");
830     } catch (VcsException e) {
831       if (e instanceof GitExecTimeout) {
832         File mirrorAfterFailure = myBuilder.getMirrorManager().getMirrorDir(unreachableRepository);
833         then(mirrorAfterFailure)
834           .overridingErrorMessage("Mirror changed after error " + e.toString())
835           .isEqualTo(mirror);//failure should not cause delete or remap
836       } else {
837         //on some platforms fetch from unknown host doesn't result in timeout error
838         throw new SkipException("Not a timeout error: " + e.toString());
839       }
840     }
841   }
842
843
844   @TestFor(issues = "TW-43884")
845   public void mirror_delete_can_be_disabled() throws Exception {
846     MockFS fs = new MockFS();
847     LoggingGitMetaFactory loggingFactory = new LoggingGitMetaFactory();
848     myVcsSupport = myBuilder.setGitMetaFactory(loggingFactory).setFS(fs).build();
849
850     File repo = dataFile("repo_for_fetch.1");
851     File remoteRepo = myTempFiles.createTempDir();
852     copyRepository(repo, remoteRepo);
853
854     //run build to prepare mirror
855     VcsRootImpl root = vcsRoot().withAgentGitPath(getGitPath()).withFetchUrl(GitUtils.toURL(remoteRepo)).build();
856     myVcsSupport.updateSources(root, CheckoutRules.DEFAULT, "add81050184d3c818560bdd8839f50024c188586", myCheckoutDir, createRunningBuild(true), false);
857
858     //update remote repo: add personal branch
859     delete(remoteRepo);
860     File updatedRepo = dataFile("repo_for_fetch.2.personal");
861     copyRepository(updatedRepo, remoteRepo);
862
863
864     //create refs/heads/personal/1 so that incremental fetch will fail
865     AtomicInteger invocationCount = new AtomicInteger(0);
866     loggingFactory.addCallback(FetchCommand.class.getName() + ".call", new GitCommandProxyCallback() {
867       @Override
868       public void call(final Method method, final Object[] args) throws VcsException {
869         if (invocationCount.getAndIncrement() <= 1)
870           throw new VcsException("TEST ERROR");
871       }
872     });
873     File mirror = myBuilder.getMirrorManager().getMirrorDir(GitUtils.toURL(remoteRepo));
874
875     VcsRootImpl root2 = vcsRoot().withAgentGitPath(getGitPath()).withBranch("refs/heads/personal").withFetchUrl(GitUtils.toURL(remoteRepo)).build();
876     fs.makeDeleteFail(mirror);
877     try {
878       AgentRunningBuild build = runningBuild()
879         .useLocalMirrors(true)
880         .sharedConfigParams(AgentRuntimeProperties.FAIL_ON_CLEAN_CHECKOUT, "true")
881         .sharedConfigParams("teamcity.git.fetchMirrorRetryTimeouts", "0")
882         .build();
883       myVcsSupport.updateSources(root2, CheckoutRules.DEFAULT, "d47dda159b27b9a8c4cee4ce98e4435eb5b17168", myCheckoutDir, build, false);
884       fail("Should fail");
885     } catch (VcsException e) {
886       File mirrorAfterBuild = myBuilder.getMirrorManager().getMirrorDir(GitUtils.toURL(remoteRepo));
887       then(mirrorAfterBuild).isEqualTo(mirror);//should fail on first fetch attempt and not remap or delete the mirror
888     }
889   }
890
891
892   public void checkout_tag() throws Exception {
893     myRoot.addProperty(Constants.BRANCH_NAME, "refs/tags/v1.0");
894     myVcsSupport.updateSources(myRoot, new CheckoutRules(""), GitVcsSupportTest.VERSION_TEST_HEAD, myCheckoutDir, myBuild, false);
895
896     Repository r = new RepositoryBuilder().setWorkTree(myCheckoutDir).build();
897     Ref tagRef = r.getRef("refs/tags/v1.0");
898     assertNotNull(tagRef);
899   }
900
901
902   @TestFor(issues = "TW-38247")
903   public void checkout_revision_reachable_from_tag() throws Exception {
904     myRoot.addProperty(Constants.BRANCH_NAME, "refs/tags/v1.0");
905     String revisionInBuild = "2c7e90053e0f7a5dd25ea2a16ef8909ba71826f6";
906     myVcsSupport.updateSources(myRoot, CheckoutRules.DEFAULT, revisionInBuild, myCheckoutDir, myBuild, false);
907
908     Repository r = new RepositoryBuilder().setWorkTree(myCheckoutDir).build();
909     String workingDirRevision = r.getAllRefs().get("HEAD").getObjectId().name();
910     assertEquals("Wrong revision on agent", revisionInBuild, workingDirRevision);
911   }
912
913
914   public void do_not_create_branch_when_checkout_tag() throws Exception {
915     myRoot.addProperty(Constants.BRANCH_NAME, "refs/tags/v1.0");
916     myVcsSupport.updateSources(myRoot, new CheckoutRules(""), GitVcsSupportTest.VERSION_TEST_HEAD, myCheckoutDir, myBuild, false);
917
918     Repository r = new RepositoryBuilder().setWorkTree(myCheckoutDir).build();
919     Map<String, Ref> refs = r.getRefDatabase().getRefs("refs/");
920     assertTrue(refs.containsKey("tags/v1.0"));
921     assertTrue(refs.containsKey("tags/v0.7"));//it is reachable from refs/tags/v1.0
922     assertTrue(refs.containsKey("tags/v0.5"));//also reachable
923     assertEquals(3, refs.size());
924   }
925
926
927   @TestFor(issues = "TW-23707")
928   public void do_not_create_remote_branch_unexisting_in_remote_repository() throws Exception {
929     myRoot.addProperty(Constants.BRANCH_NAME, "refs/changes/1/1");
930     myVcsSupport.updateSources(myRoot, CheckoutRules.DEFAULT, "5711cbfe566b6c92e331f95d4b236483f4532eed", myCheckoutDir, myBuild, false);
931     Repository r = new RepositoryBuilder().setWorkTree(myCheckoutDir).build();
932     assertEquals("5711cbfe566b6c92e331f95d4b236483f4532eed", r.getBranch());//checkout a detached commit
933     Map<String, Ref> refs = r.getAllRefs();
934     assertFalse(refs.containsKey("refs/heads/refs/changes/1/1"));
935     assertFalse(refs.containsKey("refs/remotes/origin/changes/1/1"));
936   }
937
938
939   public void checkout_tag_after_branch() throws Exception {
940     myRoot.addProperty(Constants.BRANCH_NAME, "sub-submodule");
941     myVcsSupport.updateSources(myRoot, new CheckoutRules(""), GitVcsSupportTest.VERSION_TEST_HEAD, myCheckoutDir, myBuild, false);
942
943     myRoot.addProperty(Constants.BRANCH_NAME, "refs/tags/v1.0");
944     myVcsSupport.updateSources(myRoot, new CheckoutRules(""),
945                                GitUtils.makeVersion("465ad9f630e451b9f2b782ffb09804c6a98c4bb9", 1289483394000L), myCheckoutDir, myBuild,
946                                false);
947     Repository r = new RepositoryBuilder().setWorkTree(myCheckoutDir).build();
948     Ref headRef = r.getRef("HEAD");
949     assertEquals("465ad9f630e451b9f2b782ffb09804c6a98c4bb9", headRef.getObjectId().name());
950   }
951
952
953   public void should_checkout_tags_reachable_from_branch() throws Exception {
954     myVcsSupport.updateSources(myRoot, new CheckoutRules(""), GitVcsSupportTest.VERSION_TEST_HEAD, myCheckoutDir, myBuild, false);
955     assertTagExists("refs/tags/v0.5");
956     assertTagExists("refs/tags/v1.0");
957   }
958
959
960   @Test(dataProvider = "mirrors")
961   public void deleted_tag_in_remote_repository_should_be_deleted_in_local_repository(Boolean useMirrors) throws Exception {
962     AgentRunningBuild build = createRunningBuild(useMirrors);
963     myVcsSupport.updateSources(myRoot, CheckoutRules.DEFAULT, GitVcsSupportTest.VERSION_TEST_HEAD, myCheckoutDir, build, false);
964     removeTag(myMainRepo, "refs/tags/v0.5");
965
966     myVcsSupport.updateSources(myRoot, CheckoutRules.DEFAULT, GitVcsSupportTest.VERSION_TEST_HEAD, myCheckoutDir, build, false);
967     assertNoTagExist("refs/tags/v0.5");
968   }
969
970
971   @Test(dataProvider = "mirrors")
972   public void updated_tag_in_remote_repository_should_be_updated_in_local_repository(Boolean useMirrors) throws Exception {
973     AgentRunningBuild build = createRunningBuild(useMirrors);
974     myVcsSupport.updateSources(myRoot, CheckoutRules.DEFAULT, GitVcsSupportTest.VERSION_TEST_HEAD, myCheckoutDir, build, false);
975
976     final String newCommit = "2c7e90053e0f7a5dd25ea2a16ef8909ba71826f6";
977     updateRef(myMainRepo, "refs/tags/v1.0", newCommit);
978
979     myVcsSupport.updateSources(myRoot, CheckoutRules.DEFAULT, GitVcsSupportTest.VERSION_TEST_HEAD, myCheckoutDir, build, false);
980     assertTagExists("refs/tags/v1.0");
981     Repository r = new RepositoryBuilder().setWorkTree(myCheckoutDir).build();
982     Ref tag = r.getRef("refs/tags/v1.0");
983     assertEquals("Local tag is not updated", newCommit, tag.getObjectId().name());
984   }
985
986
987   @TestFor(issues = "TW-47805")
988   public void no_redundant_fetches_for_pull_requests() throws Exception {
989     LoggingGitMetaFactory loggingFactory = new LoggingGitMetaFactory();
990     myVcsSupport = myBuilder.setGitMetaFactory(loggingFactory).setFS(new MockFS()).build();
991
992     //create pull-request in remote repo
993     myRoot.addProperty(Constants.BRANCH_NAME, "refs/changes/1/1");
994     updateRef(myMainRepo, "refs/pull/1/head", "5711cbfe566b6c92e331f95d4b236483f4532eed");
995
996     //run build on pull-request
997     myVcsSupport.updateSources(myRoot, CheckoutRules.DEFAULT, "5711cbfe566b6c92e331f95d4b236483f4532eed", myCheckoutDir, myBuild, false);
998
999     //run build again
1000     int fetchesBefore = loggingFactory.getNumberOfCalls(FetchCommand.class);
1001     myVcsSupport.updateSources(myRoot, CheckoutRules.DEFAULT, "5711cbfe566b6c92e331f95d4b236483f4532eed", myCheckoutDir, myBuild, false);
1002
1003     //there should be no fetches in the second build
1004     int redundantFetches = loggingFactory.getNumberOfCalls(FetchCommand.class) - fetchesBefore;
1005     then(redundantFetches).isEqualTo(0);
1006   }
1007
1008
1009   @Test(dataProvider = "mirrors")
1010   public void fetch_all_heads(boolean useMirrors) throws Exception {
1011     AgentRunningBuild build = createRunningBuild(map(PluginConfigImpl.USE_MIRRORS, String.valueOf(useMirrors),
1012                                                      PluginConfigImpl.FETCH_ALL_HEADS, "true"));
1013
1014     myVcsSupport.updateSources(myRoot, CheckoutRules.DEFAULT, "465ad9f630e451b9f2b782ffb09804c6a98c4bb9", myCheckoutDir, build, false);
1015
1016     Repository remoteRepo = new RepositoryBuilder().setBare().setGitDir(myMainRepo).build();
1017     Set<String> remoteHeads = remoteRepo.getRefDatabase().getRefs("refs/heads/").keySet();
1018
1019     Repository r = new RepositoryBuilder().setWorkTree(myCheckoutDir).build();
1020     then(r.getRefDatabase().getRefs("refs/remotes/origin/").keySet()).containsAll(remoteHeads);
1021   }
1022
1023
1024   @TestFor(issues = "TW-50714")
1025   @Test(dataProvider = "mirrors")
1026   public void fetch_all_heads__non_head_ref(boolean useMirrors) throws Exception {
1027     AgentRunningBuild build = createRunningBuild(map(PluginConfigImpl.FETCH_ALL_HEADS, "true"));
1028
1029     myRoot = vcsRoot().withAgentGitPath(getGitPath()).withFetchUrl(GitUtils.toURL(myMainRepo)).withUseMirrors(useMirrors).withBranch("refs/pull/1").build();
1030
1031     myVcsSupport.updateSources(myRoot, CheckoutRules.DEFAULT, "b896070465af79121c9a4eb5300ecff29453c164", myCheckoutDir, build, false);
1032   }
1033
1034
1035   @Test(dataProvider = "mirrors")
1036   public void fetch_all_heads_before_build_branch(boolean useMirrors) throws Exception {
1037     AgentRunningBuild build = createRunningBuild(map(PluginConfigImpl.USE_MIRRORS, String.valueOf(useMirrors),
1038                                                      PluginConfigImpl.FETCH_ALL_HEADS, "beforeBuildBranch"));
1039
1040     myVcsSupport.updateSources(myRoot, CheckoutRules.DEFAULT, "465ad9f630e451b9f2b782ffb09804c6a98c4bb9", myCheckoutDir, build, false);
1041
1042     Repository remoteRepo = new RepositoryBuilder().setBare().setGitDir(myMainRepo).build();
1043     Set<String> remoteHeads = remoteRepo.getRefDatabase().getRefs("refs/heads/").keySet();
1044
1045     //local repo should contain all heads since build's commit wasn't on the agent
1046     Repository r = new RepositoryBuilder().setWorkTree(myCheckoutDir).build();
1047     then(r.getRefDatabase().getRefs("refs/remotes/origin/").keySet()).containsAll(remoteHeads);
1048   }
1049
1050
1051   @Test(dataProvider = "mirrors")
1052   public void fetch_all_heads_before_build_branch_commit_found(boolean useMirrors) throws Exception {
1053     //run build to make sure commit is on the agent
1054     AgentRunningBuild build = createRunningBuild(map(PluginConfigImpl.USE_MIRRORS, String.valueOf(useMirrors)));
1055     myVcsSupport.updateSources(myRoot, CheckoutRules.DEFAULT, "465ad9f630e451b9f2b782ffb09804c6a98c4bb9", myCheckoutDir, build, false);
1056
1057     //run build with fetch_all_head before build's branch
1058     build = createRunningBuild(map(PluginConfigImpl.USE_MIRRORS, String.valueOf(useMirrors),
1059                                    PluginConfigImpl.FETCH_ALL_HEADS, "beforeBuildBranch"));
1060     myVcsSupport.updateSources(myRoot, CheckoutRules.DEFAULT, "465ad9f630e451b9f2b782ffb09804c6a98c4bb9", myCheckoutDir, build, false);
1061
1062     //local repo shouldn't contain all heads since build commit was already on the agent and no fetch is required
1063     Repository r = new RepositoryBuilder().setWorkTree(myCheckoutDir).build();
1064     then(r.getRefDatabase().getRefs("refs/remotes/origin/").keySet()).containsOnly("master");
1065   }
1066
1067
1068   private void removeTag(@NotNull File dotGitDir, @NotNull String tagName) {
1069     delete(tagFile(dotGitDir, tagName));
1070   }
1071
1072   private void updateRef(@NotNull File dotGitDir, @NotNull String refName, @NotNull String commit) throws IOException {
1073     File tagFile = tagFile(dotGitDir, refName);
1074     FileUtil.writeToFile(tagFile, commit.getBytes());
1075   }
1076
1077   private File tagFile(@NotNull File dotGitDir, @NotNull String tagName) {
1078     return new File(dotGitDir, tagName.replaceAll("/", Matcher.quoteReplacement(File.separator)));
1079   }
1080
1081   private void assertNoTagExist(String tag) throws IOException {
1082     Repository r = new RepositoryBuilder().setWorkTree(myCheckoutDir).build();
1083     assertNull("tag \'" + tag + "\' exists", r.getRef(tag));
1084   }
1085
1086   private void assertTagExists(String tag) throws IOException {
1087     Repository r = new RepositoryBuilder().setWorkTree(myCheckoutDir).build();
1088     assertNotNull("tag \'" + tag + "\' doesn't exist", r.getRef(tag));
1089   }
1090
1091   @DataProvider(name = "mirrors")
1092   public Object[][] mirrors() {
1093     return new Object[][] {{true}, {false}};
1094   }
1095
1096   @Test(dataProvider = "mirrors")
1097   public void should_do_fetch_if_ref_is_outdated(Boolean useMirrors) throws Exception {
1098     AgentRunningBuild build = createRunningBuild(useMirrors);
1099     final File remote = myTempFiles.createTempDir();
1100     copyRepository(dataFile("repo_for_fetch.2.personal"), remote);
1101     VcsRootImpl masterRoot = createRoot(remote, "master");
1102     VcsRootImpl personalRoot = createRoot(remote, "personal");
1103
1104     myVcsSupport.updateSources(personalRoot, new CheckoutRules(""), "d47dda159b27b9a8c4cee4ce98e4435eb5b17168@1303829462000", myCheckoutDir, build, false);
1105     myVcsSupport.updateSources(masterRoot,   new CheckoutRules(""), "add81050184d3c818560bdd8839f50024c188586@1303829295000", myCheckoutDir, build, false);
1106
1107     FileUtil.delete(remote);
1108     copyRepository(dataFile("repo_for_fetch.2"), remote);
1109
1110     myVcsSupport.updateSources(masterRoot, new CheckoutRules(""), "d47dda159b27b9a8c4cee4ce98e4435eb5b17168@1303829462000", myCheckoutDir,
1111                                build, false);
1112   }
1113
1114
1115   @Test(dataProvider = "mirrors")
1116   public void test_update_on_revision_from_feature_branch(Boolean useMirrors) throws Exception {
1117     AgentRunningBuild build = createRunningBuild(useMirrors);
1118     final File remote = myTempFiles.createTempDir();
1119     copyRepository(dataFile("repo_for_fetch.2.personal"), remote);
1120     VcsRootImpl root = createRoot(remote, "master");
1121     String commitFromFeatureBranch = "d47dda159b27b9a8c4cee4ce98e4435eb5b17168";
1122     myVcsSupport.updateSources(root, CheckoutRules.DEFAULT, commitFromFeatureBranch, myCheckoutDir, build, false);
1123   }
1124
1125
1126   @Test(dataProvider = "mirrors")
1127   public void should_use_branch_specified_in_build_parameter(Boolean useMirrors) throws Exception {
1128     final File remote = myTempFiles.createTempDir();
1129     copyRepository(dataFile("repo_for_fetch.2.personal"), remote);
1130     VcsRootImpl root = createRoot(remote, "master");
1131
1132     AgentRunningBuild build = createRunningBuild(map(PluginConfigImpl.USE_MIRRORS, String.valueOf(useMirrors)));
1133     String commitFromFeatureBranch = "d47dda159b27b9a8c4cee4ce98e4435eb5b17168";
1134     myVcsSupport.updateSources(root, CheckoutRules.DEFAULT, commitFromFeatureBranch, myCheckoutDir, build, false);
1135     Repository r = new RepositoryBuilder().setWorkTree(myCheckoutDir).build();
1136     assertEquals("master", r.getBranch());
1137
1138     build = createRunningBuild(map(PluginConfigImpl.USE_MIRRORS, String.valueOf(useMirrors),
1139                                    GitUtils.getGitRootBranchParamName(root), "refs/heads/personal"));
1140     myVcsSupport.updateSources(root, CheckoutRules.DEFAULT, commitFromFeatureBranch, myCheckoutDir, build, false);
1141     r = new RepositoryBuilder().setWorkTree(myCheckoutDir).build();
1142     assertEquals("personal", r.getBranch());
1143   }
1144
1145
1146   @Test
1147   public void test_shallow_clone() throws Exception {
1148     AgentRunningBuild build = createRunningBuild(new HashMap<String, String>() {{
1149       put(PluginConfigImpl.USE_MIRRORS, "true");
1150       put(PluginConfigImpl.USE_SHALLOW_CLONE, "true");
1151     }});
1152     myVcsSupport.updateSources(myRoot, CheckoutRules.DEFAULT, GitVcsSupportTest.VERSION_TEST_HEAD, myCheckoutDir, build, false);
1153   }
1154
1155
1156   @TestFor(issues = "TW-27677")
1157   public void shallow_clone_in_non_master_branch() throws Exception {
1158     AgentRunningBuild build = createRunningBuild(new HashMap<String, String>() {{
1159       put(PluginConfigImpl.USE_MIRRORS, "true");
1160       put(PluginConfigImpl.USE_SHALLOW_CLONE, "true");
1161       put(GitUtils.getGitRootBranchParamName(myRoot), "refs/heads/version-test");//build on non-master branch
1162     }});
1163     myVcsSupport.updateSources(myRoot, CheckoutRules.DEFAULT, "2276eaf76a658f96b5cf3eb25f3e1fda90f6b653", myCheckoutDir, build, false);
1164   }
1165
1166
1167   @TestFor(issues = "TW-37122")
1168   public void shallow_clone_on_tag() throws Exception {
1169     myRoot.addProperty(Constants.BRANCH_NAME, "refs/tags/v1.0");
1170     AgentRunningBuild build = createRunningBuild(new HashMap<String, String>() {{
1171       put(PluginConfigImpl.USE_MIRRORS, "true");
1172       put(PluginConfigImpl.USE_SHALLOW_CLONE, "true");
1173     }});
1174     myVcsSupport.updateSources(myRoot, CheckoutRules.DEFAULT, "465ad9f630e451b9f2b782ffb09804c6a98c4bb9", myCheckoutDir, build, false);
1175   }
1176
1177
1178   @TestFor(issues = "TW-20165")
1179   public void push_with_local_mirrors_should_go_to_original_repository() throws Exception {
1180     AgentRunningBuild build = createRunningBuild(true);
1181     myVcsSupport.updateSources(myRoot, CheckoutRules.DEFAULT, GitUtils.makeVersion("465ad9f630e451b9f2b782ffb09804c6a98c4bb9", 1289483394000L), myCheckoutDir, build, false);
1182
1183     final File fileToChange = new File(myCheckoutDir, "file");
1184     FileUtil.writeToFile(fileToChange, "text".getBytes());
1185
1186     Repository r = new RepositoryBuilder().setWorkTree(myCheckoutDir).build();
1187     Git git = new Git(r);
1188     git.add().addFilepattern("file").call();
1189     RevCommit commitDuringTheBuild = git.commit().setMessage("Commit during the build").call();
1190     new PushCommand().run(getGitPath(), myCheckoutDir.getAbsolutePath());//push using native git, seems like jgit doesn't respect url.insteadOf settings
1191
1192     Repository remote = new RepositoryBuilder().setGitDir(myMainRepo).build();
1193     assertTrue("Push didn't go to the remote repository", remote.hasObject(commitDuringTheBuild));
1194   }
1195
1196
1197   @TestFor(issues = "TW-28735")
1198   public void fetch_branch_with_same_name_but_different_register() throws Exception {
1199     AgentRunningBuild buildWithMirrorsEnabled = createRunningBuild(true);
1200     myRoot = vcsRoot().withBranch("refs/heads/master").withAgentGitPath(getGitPath()).withFetchUrl(GitUtils.toURL(myMainRepo)).build();
1201     myVcsSupport.updateSources(myRoot, CheckoutRules.DEFAULT, "465ad9f630e451b9f2b782ffb09804c6a98c4bb9", myCheckoutDir, buildWithMirrorsEnabled, false);
1202
1203     //rename master->Master
1204     Repository r = new RepositoryBuilder().setGitDir(myMainRepo).build();
1205     r.renameRef("refs/heads/master", "refs/heads/Master").rename();
1206
1207     myRoot = vcsRoot().withBranch("refs/heads/Master").withAgentGitPath(getGitPath()).withFetchUrl(GitUtils.toURL(myMainRepo)).build();
1208     myVcsSupport.updateSources(myRoot, CheckoutRules.DEFAULT, "465ad9f630e451b9f2b782ffb09804c6a98c4bb9", myCheckoutDir, buildWithMirrorsEnabled, false);
1209   }
1210
1211
1212   @TestFor(issues = "TW-46266")
1213   public void should_not_use_custom_clone_on_server() throws Exception {
1214     File serverCustomCloneDir = myTempFiles.createTempDir();
1215     VcsRootImpl root = vcsRoot()
1216       .withAgentGitPath(getGitPath())
1217       .withFetchUrl(GitUtils.toURL(myMainRepo))
1218       .withRepositoryPathOnServer(serverCustomCloneDir.getCanonicalPath())
1219       .build();
1220
1221     myVcsSupport.updateSources(root, CheckoutRules.DEFAULT, "465ad9f630e451b9f2b782ffb09804c6a98c4bb9", myCheckoutDir, createRunningBuild(true), false);
1222
1223     then(serverCustomCloneDir.listFiles()).isEmpty();
1224   }
1225
1226
1227   @TestFor(issues = "TW-44844")
1228   @Test(dataProviderClass = BaseRemoteRepositoryTest.class, dataProvider = "true,false")
1229   public void handle_files_marked_as_unchanged(boolean switchBranch) throws Exception {
1230     //checkout
1231     myVcsSupport.updateSources(myRoot, CheckoutRules.DEFAULT, "97442a720324a0bd092fb9235f72246dc8b345bc", myCheckoutDir, myBuild, false);
1232
1233     //modify file
1234     File f = new File(myCheckoutDir, "dir/a.txt");
1235     writeFileAndReportErrors(f, "update by build script");
1236
1237     //git update-index --no-assume-unchanged <file>
1238     Process updateIndex = new ProcessBuilder().directory(myCheckoutDir).command(getGitPath(), "update-index", "--assume-unchanged", "dir/a.txt").start();
1239     updateIndex.waitFor();
1240     if (updateIndex.exitValue() != 0) {
1241       fail("git update-index failed, exit code " + updateIndex.exitValue() +
1242            "\nstdout: " + StreamUtil.readText(updateIndex.getInputStream()) +
1243            "\nstderr: " + StreamUtil.readText(updateIndex.getErrorStream()));
1244     }
1245
1246     //update to commit which changes the file
1247     if (switchBranch)
1248       myBuild = createRunningBuild(map(GitUtils.getGitRootBranchParamName(myRoot), "refs/heads/personal-branch1"));
1249     myVcsSupport.updateSources(myRoot, CheckoutRules.DEFAULT, "ad4528ed5c84092fdbe9e0502163cf8d6e6141e7", myCheckoutDir, myBuild, false);
1250     then(FileUtil.readFile(f)).doesNotContain("update by build script");
1251   }
1252
1253
1254   @TestFor(issues = "TW-40313")
1255   public void should_remove_orphaned_indexes() throws Exception {
1256     //checkout
1257     VcsRootImpl root = vcsRoot()
1258       .withAgentGitPath(getGitPath())
1259       .withFetchUrl(GitUtils.toURL(myMainRepo))
1260       .build();
1261
1262     myVcsSupport.updateSources(root, CheckoutRules.DEFAULT, "465ad9f630e451b9f2b782ffb09804c6a98c4bb9", myCheckoutDir, createRunningBuild(true), false);
1263
1264     //create orphaned idx files
1265     File mirror = myBuilder.getMirrorManager().getMirrorDir(GitUtils.toURL(myMainRepo));
1266     File idxInMirror = new File(new File(new File(mirror, "objects"), "pack"), "whatever.idx");
1267     FileUtil.writeFileAndReportErrors(idxInMirror, "whatever");
1268     File idxInCheckoutDir = new File(new File(new File(mirror, "objects"), "pack"), "whatever.idx");
1269     FileUtil.writeFileAndReportErrors(idxInCheckoutDir, "whatever");
1270
1271     //checkout again
1272     myVcsSupport.updateSources(root, CheckoutRules.DEFAULT, "465ad9f630e451b9f2b782ffb09804c6a98c4bb9", myCheckoutDir, createRunningBuild(true), false);
1273
1274     //orphaned idx files are removed
1275     then(idxInCheckoutDir).doesNotExist();
1276     then(idxInMirror).doesNotExist();
1277   }
1278
1279
1280   private VcsRootImpl createRoot(final File remote, final String branch) throws IOException {
1281     myVcsRootId++;
1282     return new VcsRootImpl(myVcsRootId, new HashMap<String, String>() {{
1283       put(VcsRootImpl.VCS_NAME_PROP, Constants.VCS_NAME);
1284       put(VcsRootImpl.VCS_ROOT_NAME_PROP, "test" + myVcsRootId);
1285       put(Constants.FETCH_URL, GitUtils.toURL(remote));
1286       put(Constants.AGENT_GIT_PATH, getGitPath());
1287       put(Constants.BRANCH_NAME, branch);
1288     }});
1289   }
1290
1291
1292   private void testSubSubmoduleCheckout(boolean recursiveSubmoduleCheckout) throws Exception {
1293     myRoot.addProperty(Constants.BRANCH_NAME, "sub-submodule");
1294     if (recursiveSubmoduleCheckout) {
1295       myRoot.addProperty(Constants.SUBMODULES_CHECKOUT, SubmodulesCheckoutPolicy.CHECKOUT.name());
1296     } else {
1297       myRoot.addProperty(Constants.SUBMODULES_CHECKOUT, SubmodulesCheckoutPolicy.NON_RECURSIVE_CHECKOUT.name());
1298     }
1299
1300     myVcsSupport.updateSources(myRoot, new CheckoutRules(""), GitVcsSupportTest.AFTER_FIRST_LEVEL_SUBMODULE_ADDED_VERSION,
1301                                myCheckoutDir, myBuild, false);
1302
1303     assertTrue(new File(myCheckoutDir, "first-level-submodule" + File.separator + "submoduleFile.txt").exists());
1304     if (recursiveSubmoduleCheckout) {
1305       assertTrue(new File (myCheckoutDir, "first-level-submodule" + File.separator + "sub-sub" + File.separator + "file.txt").exists());
1306       assertTrue(new File (myCheckoutDir, "first-level-submodule" + File.separator + "sub-sub" + File.separator + "new file.txt").exists());
1307     } else {
1308       assertFalse(new File (myCheckoutDir, "first-level-submodule" + File.separator + "sub-sub" + File.separator + "file.txt").exists());
1309       assertFalse(new File (myCheckoutDir, "first-level-submodule" + File.separator + "sub-sub" + File.separator + "new file.txt").exists());
1310     }
1311   }
1312
1313
1314   private AgentRunningBuild createRunningBuild(boolean useLocalMirrors) {
1315     return runningBuild().useLocalMirrors(useLocalMirrors).build();
1316   }
1317
1318
1319   private AgentRunningBuild createRunningBuild(final Map<String, String> sharedConfigParameters) {
1320     return runningBuild().sharedConfigParams(sharedConfigParameters).build();
1321   }
1322
1323
1324   private void copyRepository(File src, File dst) throws IOException {
1325     copyDir(src, dst);
1326     new File(dst, "refs" + File.separator + "heads").mkdirs();
1327   }
1328
1329
1330   private class PushCommand {
1331     void run(String gitPath, String workDirectory) throws Exception {
1332       File tmpDir = new File(FileUtil.getTempDirectory());
1333       GitCommandLine cmd = new GitCommandLine(null, SystemInfo.isUnix ? new UnixScriptGen(tmpDir, new EscapeEchoArgumentUnix())
1334                                                                       : new WinScriptGen(tmpDir, new EscapeEchoArgumentWin()),
1335                                               tmpDir,
1336                                               true,
1337                                               GitProgressLogger.NO_OP,
1338                                               GitVersion.MIN,
1339                                               new HashMap<String, String>(),
1340                                               new NoBuildContext());
1341       cmd.setExePath(gitPath);
1342       cmd.setWorkingDirectory(new File(workDirectory));
1343       cmd.addParameters("push", "origin", "master");
1344       CommandUtil.runCommand(cmd);
1345     }
1346   }
1347 }