gclient: Set with_branch_heads if asked to sync to refs/branch-heads/*

The bug is caused because we already have refs/branch-heads/XXXX locally,
so we don't attempt to fetch it again, even if it has been updated.

This solution is a bit of a hack to make sure that we fetch
refs/branch-heads/*, which is the most common case for this to fail.

Bug: 1070545
Change-Id: Iec7cfff18219794230188e2b9e901c587acd274b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/tools/depot_tools/+/2149849
Commit-Queue: Edward Lesmes <ehmaldonado@chromium.org>
Reviewed-by: Josip Sokcevic <sokcevic@google.com>
diff --git a/tests/gclient_scm_test.py b/tests/gclient_scm_test.py
index 81cc233..fae7a08 100755
--- a/tests/gclient_scm_test.py
+++ b/tests/gclient_scm_test.py
@@ -981,6 +981,79 @@
     scm.update(None, (), [])
 
 
+class BranchHeadsFakeRepo(fake_repos.FakeReposBase):
+  def populateGit(self):
+    # Creates a tree that looks like this:
+    #
+    #    5 refs/branch-heads/5
+    #    |
+    #    4
+    #    |
+    # 1--2--3 refs/heads/master
+    self._commit_git('repo_1', {'commit 1': 'touched'})
+    self._commit_git('repo_1', {'commit 2': 'touched'})
+    self._commit_git('repo_1', {'commit 3': 'touched'})
+    self._create_ref('repo_1', 'refs/heads/master', 3)
+
+    self._commit_git('repo_1', {'commit 4': 'touched'}, base=2)
+    self._commit_git('repo_1', {'commit 5': 'touched'}, base=2)
+    self._create_ref('repo_1', 'refs/branch-heads/5', 5)
+
+
+class BranchHeadsTest(fake_repos.FakeReposTestBase):
+  FAKE_REPOS_CLASS = BranchHeadsFakeRepo
+
+  def setUp(self):
+    super(BranchHeadsTest, self).setUp()
+    self.enabled = self.FAKE_REPOS.set_up_git()
+    self.options = BaseGitWrapperTestCase.OptionsObject()
+    self.url = self.git_base + 'repo_1'
+    self.mirror = None
+    mock.patch('sys.stdout').start()
+    self.addCleanup(mock.patch.stopall)
+
+  def setUpMirror(self):
+    self.mirror = tempfile.mkdtemp('mirror')
+    git_cache.Mirror.SetCachePath(self.mirror)
+    self.addCleanup(gclient_utils.rmtree, self.mirror)
+    self.addCleanup(git_cache.Mirror.SetCachePath, None)
+
+  def testCheckoutBranchHeads(self):
+    scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
+    file_list = []
+
+    self.options.revision = 'refs/branch-heads/5'
+    scm.update(self.options, None, file_list)
+    self.assertEqual(self.githash('repo_1', 5), self.gitrevparse(self.root_dir))
+
+  def testCheckoutUpdatedBranchHeads(self):
+    # Travel back in time, and set refs/branch-heads/5 to its parent.
+    subprocess2.check_call(
+        ['git', 'update-ref', 'refs/branch-heads/5', self.githash('repo_1', 4)],
+        cwd=self.url)
+
+    # Sync to refs/branch-heads/5
+    scm = gclient_scm.GitWrapper(self.url, self.root_dir, '.')
+    self.options.revision = 'refs/branch-heads/5'
+    scm.update(self.options, None, [])
+
+    # Set refs/branch-heads/5 back to its original value.
+    subprocess2.check_call(
+        ['git', 'update-ref', 'refs/branch-heads/5', self.githash('repo_1', 5)],
+        cwd=self.url)
+
+    # Attempt to sync to refs/branch-heads/5 again.
+    self.testCheckoutBranchHeads()
+
+  def testCheckoutBranchHeadsMirror(self):
+    self.setUpMirror()
+    self.testCheckoutBranchHeads()
+
+  def testCheckoutUpdatedBranchHeadsMirror(self):
+    self.setUpMirror()
+    self.testCheckoutUpdatedBranchHeads()
+
+
 class GerritChangesFakeRepo(fake_repos.FakeReposBase):
   def populateGit(self):
     # Creates a tree that looks like this: