Repository: readthedocs/readthedocs.org

Issue:
Simplify vcs_support backend git by using GitPython
We already started using GitPython for some submodules pieces. We should continue porting some of our hand rolled logic to use GitPython instead.

Pieces of `readthedocs.vcs_support.backend.git` we should definitely port:

* `parse_branches()` -- this supersedes a branch I started at #2997 to replace parsing this with a csvreader
* `parse_tags()` -- same silly code using a csvreader
* `find_ref()`
* `ref_exists()`
* `repo_exists()`

Maybe:

* `tags()`
* `branches()`

These would be good first targets to port over, as we are executing these commands to get data out of the repository. It's not important that these messages are surfaced to users in build command output.

I'm going to block on getting a feature out that executes these commands in docker first though, as we need to clone and submodule checkout inside the docker container to isolate these calls. I can't quite consider how relying on gitpython for some of these calls, but not all, works with regard to docker vcs checkouts.


Discussion:
Just want to note here that I'm starting to feel uncomfortable with `gitpython` because it has several issues around unicode that cause our builds to fail and to debug them, write tests, etc.

I mentioned some of these problem at: https://github.com/rtfd/readthedocs.org/pull/4318#discussion_r214176024

I'd like to start thinking for better alternatives. Although, I don't have one in mind yet.
We are only missing `git status` and `git submodule status` here
But not sure if we want to replace those, the code looks more complex with git python p:

```diff
-        code, _, _ = self.run('git', 'status', record=False)
-        return code == 0
+        try:
+            git.Repo(self.working_dir)
+        except InvalidGitRepositoryError:
+            return False
+        return True
```

```diff
-        code, out, _ = self.run('git', 'submodule', 'status', record=False)
-        return code == 0 and bool(out)
+        repo = git.Repo(self.working_dir)
+        return bool(repo.submodules)
```
If you think that we are ok with git status and git submodule status, we can close this issue.
I like the consistency: all `git` command or all `gitpython`. A mixture of both it's hard to maintain and to keep in mind. On the other hand here, there are `git` commands that we can't replace: those with `record=True`

The code maybe looks longer, but it's more Pythonic, I'd say and we are relying on an external library instead of a local and custom command.

I'm :+1: on migrating them also.

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
@@ -15,8 +15,7 @@
 import git
 from builtins import str
 from django.core.exceptions import ValidationError
-from django.conf import settings
-from git.exc import BadName
+from git.exc import BadName, InvalidGitRepositoryError
 
 from readthedocs.config import ALL
 from readthedocs.projects.exceptions import RepositoryError
@@ -67,8 +66,11 @@ def update(self):
         return self.clone()
 
     def repo_exists(self):
-        code, _, _ = self.run('git', 'status', record=False)
-        return code == 0
+        try:
+            git.Repo(self.working_dir)
+        except InvalidGitRepositoryError:
+            return False
+        return True
 
     def are_submodules_available(self, config):
         """Test whether git submodule checkout step should be performed."""
@@ -83,8 +85,8 @@ def are_submodules_available(self, config):
             return False
 
         # Keep compatibility with previous projects
-        code, out, _ = self.run('git', 'submodule', 'status', record=False)
-        return code == 0 and bool(out)
+        repo = git.Repo(self.working_dir)
+        return bool(repo.submodules)
 
     def validate_submodules(self, config):
         """


Test patch: