Repository: readthedocs/readthedocs.org

Issue:
Delete untracked tags on fetch step
Currently, if the user deletes a tag, it needs to wipe the environment for this change be reflected in their version list.

There are some solutions to delete untracked tags (require more than 2 commands). But I found that the newest version of git has the `--prune-tags` option, which is used as `git fetch --prune --prune-tags` (`git >2.17`). We need to update git on the servers (we use 2.7.4) and change the fetch command. Or we can find a way to wipe the environment if we detect something like this case.

Raised in https://github.com/rtfd/readthedocs.org/pull/3913#issuecomment-396673349


Discussion:
I think we can implement the command you mentioned here very soon since we are migrating our servers to Ubuntu 18.04 which includes 2.17.1 by default: https://packages.ubuntu.com/bionic/git

The PR needed for this should be very simple and shouldn't have any secondary effect :)

I'm :+1: on adding the `--prune-tags` to our current command.
Blocking until .com update to 18.04

Resolving patch:
diff --git a/readthedocs/vcs_support/backends/git.py b/readthedocs/vcs_support/backends/git.py
--- a/readthedocs/vcs_support/backends/git.py
+++ b/readthedocs/vcs_support/backends/git.py
@@ -122,7 +122,9 @@ def validate_submodules(self, config):
         return True, submodules.keys()
 
     def fetch(self):
-        code, _, _ = self.run('git', 'fetch', '--tags', '--prune')
+        code, _, _ = self.run(
+            'git', 'fetch', '--tags', '--prune', '--prune-tags',
+        )
         if code != 0:
             raise RepositoryError
 


Test patch:
diff --git a/readthedocs/rtd_tests/tests/test_backend.py b/readthedocs/rtd_tests/tests/test_backend.py
--- a/readthedocs/rtd_tests/tests/test_backend.py
+++ b/readthedocs/rtd_tests/tests/test_backend.py
@@ -1,21 +1,33 @@
 # -*- coding: utf-8 -*-
 
 from __future__ import (
-    absolute_import, division, print_function, unicode_literals)
+    absolute_import,
+    division,
+    print_function,
+    unicode_literals,
+)
 
+import os
 from os.path import exists
+from tempfile import mkdtemp
 
 import django_dynamic_fixture as fixture
 import pytest
 from django.contrib.auth.models import User
-from mock import Mock
+from mock import Mock, patch
 
 from readthedocs.config import ALL
 from readthedocs.projects.exceptions import RepositoryError
 from readthedocs.projects.models import Feature, Project
 from readthedocs.rtd_tests.base import RTDTestCase
 from readthedocs.rtd_tests.utils import (
-    create_git_tag, make_test_git, make_test_hg)
+    create_git_branch,
+    create_git_tag,
+    delete_git_branch,
+    delete_git_tag,
+    make_test_git,
+    make_test_hg,
+)
 
 
 class TestGitBackend(RTDTestCase):
@@ -118,6 +130,51 @@ def test_check_invalid_submodule_urls(self):
             repo.checkout('invalidsubmodule')
             self.assertEqual(e.msg, RepositoryError.INVALID_SUBMODULES)
 
+    @patch('readthedocs.projects.models.Project.checkout_path')
+    def test_fetch_clean_tags_and_branches(self, checkout_path):
+        upstream_repo = self.project.repo
+        create_git_tag(upstream_repo, 'v01')
+        create_git_tag(upstream_repo, 'v02')
+        create_git_branch(upstream_repo, 'newbranch')
+
+        local_repo = os.path.join(mkdtemp(), 'local')
+        os.mkdir(local_repo)
+        checkout_path.return_value = local_repo
+
+        repo = self.project.vcs_repo()
+        repo.clone()
+
+        delete_git_tag(upstream_repo, 'v02')
+        delete_git_branch(upstream_repo, 'newbranch')
+
+        # We still have all branches and tags in the local repo
+        self.assertEqual(
+            set(['v01', 'v02']),
+            set(vcs.verbose_name for vcs in repo.tags)
+        )
+        self.assertEqual(
+            set([
+                'relativesubmodule', 'invalidsubmodule',
+                'master', 'submodule', 'newbranch',
+            ]),
+            set(vcs.verbose_name for vcs in repo.branches)
+        )
+
+        repo.checkout()
+
+        # We don't have the eliminated branches and tags in the local repo
+        self.assertEqual(
+            set(['v01']),
+            set(vcs.verbose_name for vcs in repo.tags)
+        )
+        self.assertEqual(
+            set([
+                'relativesubmodule', 'invalidsubmodule',
+                'master', 'submodule'
+            ]),
+            set(vcs.verbose_name for vcs in repo.branches)
+        )
+
 
 class TestHgBackend(RTDTestCase):
     def setUp(self):