| @@ -151,7 +151,7 @@ class GitIndexer(threading.Thread): | |||||
| :type repo_url: :class:`GitRepository` | :type repo_url: :class:`GitRepository` | ||||
| """ | """ | ||||
| file_meta = self._get_file_metadata(repo) | |||||
| file_meta = self._get_file_metadata(repo.repo) | |||||
| if file_meta is None: | if file_meta is None: | ||||
| return | return | ||||
| @@ -196,63 +196,6 @@ class GitIndexer(threading.Thread): | |||||
| parts = [repo.url, "src", commit_hash, filename] | parts = [repo.url, "src", commit_hash, filename] | ||||
| return "/".join(s.strip("/") for s in parts) | return "/".join(s.strip("/") for s in parts) | ||||
| def _walk_history(self, files, head): | |||||
| """Walk a repository's history for metadata.""" | |||||
| def update_entry(commit, entry, new_file): | |||||
| if commit.author.name not in entry["authors"]: | |||||
| entry["authors"].append(commit.author.name) | |||||
| commit_ts = datetime.utcfromtimestamp(commit.committed_date) | |||||
| if commit_ts > entry["time_last_modified"]: | |||||
| entry["time_last_modified"] = commit_ts | |||||
| if new_file: | |||||
| entry["time_created"] = commit_ts | |||||
| def get_diffs(commit, parent): | |||||
| cache_key = parent.binsha + commit.binsha | |||||
| if cache_key in diff_cache: | |||||
| return diff_cache[cache_key] | |||||
| diffs = parent.diff(commit, create_patch=True) | |||||
| for diff in diffs: | |||||
| del diff.diff | |||||
| diff_cache[cache_key] = diffs | |||||
| return diffs | |||||
| def handle_commit(commit, paths): | |||||
| if not commit.parents: | |||||
| for item in commit.tree.traverse(): | |||||
| if item.type == "blob" and item.path in paths: | |||||
| update_entry(commit, files[paths[item.path]], True) | |||||
| return | |||||
| for parent in commit.parents: | |||||
| for diff in get_diffs(commit, parent): | |||||
| if not diff.b_blob: # Happens when file modes are changed | |||||
| continue | |||||
| pth = diff.rename_to if diff.renamed else diff.b_blob.path | |||||
| if pth not in paths: | |||||
| continue | |||||
| update_entry(commit, files[paths[pth]], diff.new_file) | |||||
| if diff.renamed: | |||||
| paths[diff.rename_from] = paths[pth] | |||||
| del paths[pth] | |||||
| pending = [(head, {path: path for path in files})] | |||||
| diff_cache = {} | |||||
| processed = {} | |||||
| while pending: | |||||
| commit, paths = pending.pop() | |||||
| handle_commit(commit, paths) | |||||
| hash_key = hash(frozenset(paths.items())) | |||||
| for parent in commit.parents: | |||||
| new_paths = paths.copy() if len(commit.parents) > 1 else paths | |||||
| if parent.binsha in processed: | |||||
| if hash_key not in processed[parent.binsha]: | |||||
| pending.append((parent, new_paths)) | |||||
| processed[parent.binsha].append(hash_key) | |||||
| else: | |||||
| pending.append((parent, new_paths)) | |||||
| processed[parent.binsha] = [hash_key] | |||||
| def _get_file_metadata(self, repo): | def _get_file_metadata(self, repo): | ||||
| """ | """ | ||||
| Return a dictionary containing every valuable tracked file's metadata. | Return a dictionary containing every valuable tracked file's metadata. | ||||
| @@ -271,22 +214,27 @@ class GitIndexer(threading.Thread): | |||||
| :rtype: dictionary of dictionaries | :rtype: dictionary of dictionaries | ||||
| """ | """ | ||||
| try: | try: | ||||
| tree = repo.repo.head.commit.tree | |||||
| tree = repo.head.commit.tree | |||||
| except ValueError: # No commits | except ValueError: # No commits | ||||
| return {} | return {} | ||||
| files = {} | files = {} | ||||
| for item in tree.traverse(): | for item in tree.traverse(): | ||||
| if item.type == "blob" and self._is_ascii(item.data_stream): | if item.type == "blob" and self._is_ascii(item.data_stream): | ||||
| log = repo.git.log("--follow", '--format=%an %ct', item.path) | |||||
| lines = log.splitlines() | |||||
| authors = {line.rsplit(" ", 1)[0] for line in lines} | |||||
| last_mod = int(lines[0].rsplit(" ", 1)[1]) | |||||
| created = int(lines[-1].rsplit(" ", 1)[1]) | |||||
| files[item.path] = { | files[item.path] = { | ||||
| "blob": item, | "blob": item, | ||||
| "authors" : [], | |||||
| "time_last_modified": datetime.utcfromtimestamp(0), | |||||
| "time_created": datetime.utcfromtimestamp(0) | |||||
| "authors" : authors, | |||||
| "time_last_modified": datetime.fromtimestamp(last_mod), | |||||
| "time_created": datetime.fromtimestamp(created) | |||||
| } | } | ||||
| self._logger.debug("Building file metadata") | self._logger.debug("Building file metadata") | ||||
| self._walk_history(files, repo.repo.head.commit) | |||||
| return files | return files | ||||
| def _is_ascii(self, source): | def _is_ascii(self, source): | ||||