Repository: ishepard/pydriller

Issue:
Add an option to retrieve commits modifying a deleted file
Hi :) I'm extracting the commit history of a certain configuration file on thousands of public repos hosted on Github. Some projects have this file deleted at some point, making it impossible to do so. 

It would be nice to add an argument to the `Repository` class, which inserts `--` to the `git log` call:
```python
for commit in Repository(repo
     filepath=file_to_look_for,
     full_log=True).traverse_commits():
```

This feature is actually quite easy to implement [reference here](https://feeding.cloud.geek.nz/posts/querying-deleted-content-in-git/), [my fork](https://github.com/12f23eddde/pydriller/commit/9a44e7f5526c4dec4f43ed8d3cabf34cfa1254b4) made just a little change to get it working:

```python
def get_commits_modified_file(self, filepath: str, full_log=False) -> List[str]:
      if full_log:
          commits = self.repo.git.log("--follow", "--format=%H", "--", path).split('\n')
      else:
          commits = self.repo.git.log("--follow", "--format=%H", path).split('\n')
```

I would like to open a pull request if this change sounds reasonable.





Discussion:

Patch:
diff --git a/pydriller/git.py b/pydriller/git.py
index 3beaf5a..4359222 100644
--- a/pydriller/git.py
+++ b/pydriller/git.py
@@ -297,19 +297,23 @@ class Git:
                line.startswith('"""') or \
                line.startswith("*")
 
-    def get_commits_modified_file(self, filepath: str) -> List[str]:
+    def get_commits_modified_file(self, filepath: str, include_deleted_files=False) -> List[str]:
         """
         Given a filepath, returns all the commits that modified this file
         (following renames).
 
         :param str filepath: path to the file
+        :param bool include_deleted_files: if True, include commits that modifies a deleted file
         :return: the list of commits' hash
         """
         path = str(Path(filepath))
 
         commits = []
         try:
-            commits = self.repo.git.log("--follow", "--format=%H", path).split('\n')
+            if include_deleted_files:
+                commits = self.repo.git.log("--follow", "--format=%H", "--", path).split('\n')
+            else:
+                commits = self.repo.git.log("--follow", "--format=%H", path).split('\n')
         except GitCommandError:
             logger.debug(f"Could not find information of file {path}")
 
diff --git a/pydriller/repository.py b/pydriller/repository.py
index 9433985..d6079d4 100644
--- a/pydriller/repository.py
+++ b/pydriller/repository.py
@@ -56,6 +56,7 @@ class Repository:
                  only_commits: List[str] = None,
                  only_releases: bool = False,
                  filepath: str = None,
+                 include_deleted_files: bool = False,
                  histogram_diff: bool = False,
                  skip_whitespaces: bool = False,
                  clone_repo_to: str = None,
@@ -97,6 +98,7 @@ class Repository:
         :param bool skip_whitespaces: add the "-w" option when asking for the diff
         :param bool clone_repo_to: if the repo under analysis is remote, clone the repo to the specified directory
         :param str filepath: only commits that modified this file will be analyzed
+        :param bool include_deleted_files: include commits modifying a deleted file (useful when analyzing a deleted `filepath`)
         :param str order: order of commits. It can be one of: 'date-order',
             'author-date-order', 'topo-order', or 'reverse'. Default is reverse.
         """
@@ -130,6 +132,7 @@ class Repository:
             "only_releases": only_releases,
             "skip_whitespaces": skip_whitespaces,
             "filepath": filepath,
+            "include_deleted_files": include_deleted_files,
             "filepath_commits": None,
             "tagged_commits": None,
             "histogram": histogram_diff,
@@ -215,7 +218,11 @@ class Repository:
                 # git rev-list since it doesn't have the option --follow, necessary to follow
                 # the renames. Hence, we manually call git log instead
                 if self._conf.get('filepath') is not None:
-                    self._conf.set_value('filepath_commits', git.get_commits_modified_file(self._conf.get('filepath')))
+                    self._conf.set_value(
+                        'filepath_commits',
+                        git.get_commits_modified_file(self._conf.get('filepath'),
+                                                      self._conf.get('include_deleted_files'))
+                    )
 
                 # Gets only the commits that are tagged
                 if self._conf.get('only_releases'):


Test patch:
diff --git a/tests/test_repository_mining.py b/tests/test_repository_mining.py
index 585148f..0e9b5aa 100644
--- a/tests/test_repository_mining.py
+++ b/tests/test_repository_mining.py
@@ -281,3 +281,12 @@ def test_deletion_remotes():
 
     for path in paths:
         assert os.path.exists(path) is False
+
+
+def test_deleted_files():
+    deleted_commits = list(
+        Repository('https://github.com/ishepard/pydriller',
+                   filepath='.bettercodehub.yml',
+                   include_deleted_files=True).traverse_commits()
+    )
+    assert len(deleted_commits) > 0