diff --git a/api/gitlab_classes.py b/api/gitlab_classes.py
index 78906d7b347b5adc0e8428b35b791ab9b556ba02..4c2fd4ef2e4362509437fee8bceeed60f71e23fa 100644
--- a/api/gitlab_classes.py
+++ b/api/gitlab_classes.py
@@ -168,6 +168,61 @@ class GitlabIssue:
     # subscribed
 
 
+class GitlabCommit:
+    def __init__(self, commit):
+        super().__init__()
+        self.gitlab_commit = commit
+
+    def get_author(self):
+        return {'name': self.gitlab_commit.author_name, 'email': self.gitlab_commit.author_email}
+
+    def get_timestamp(self):
+        return self.gitlab_commit.created_at
+
+    def get_message(self):
+        return self.gitlab_commit.message
+
+    def is_merge(self):
+        return len(self.gitlab_commit.parent_ids) > 1
+
+    def get_diffs(self):
+        diffs = []
+        gitlab_diffs = self.gitlab_commit.diff()
+        for diff in gitlab_diffs:
+            diffs.append({'file': diff['new_path'], 'text': diff['diff'],
+                          '+': diff['diff'].count('\n+'), '-': diff['diff'].count('\n-')})
+        return diffs
+
+    def get_diff_size(self):
+        insertions = 0
+        deletions = 0
+        if not self.is_merge():
+            for diff in self.get_diffs():
+                insertions += diff['+']
+                deletions += diff['-']
+        return max(insertions, deletions)
+
+    # git_commit fields:
+    # comments
+    # discussions
+    # manager
+    # statuses
+    # attributes:
+    # id
+    # short_id
+    # created_at
+    # parent_ids
+    # title
+    # message
+    # author_name
+    # author_email
+    # authored_date
+    # committer_name
+    # committer_email
+    # committed_date
+    # project_id
+
+
 class GitlabProject:
     def __init__(self, project):
         """
@@ -320,6 +375,32 @@ class GitlabProject:
         gitlab_issue = self.git_project.issues.create({'title': title, 'description': description})
         return GitlabIssue(gitlab_issue)
 
+    def get_commits(self, branch_name = '', after_date ='1970-01-01', before_date ='9999-12-31'):
+        """
+        :param branch_name: the branch to retrieve commits from; if an empty string (default) then retrieves commits
+        from all branches
+        :param after_date: the earliest date of any retrieved commit; if '1970-01-01' (Unix epoch) then treated as
+        having no earliest-bound
+        :param before_date: the latest date of any retrieved commit; if '9999-12-31' (Y10K problem) then treated a
+        having no latest-bound
+        :return: List of Commit objects representing the project's commits that meet the specified constraints
+        """
+        filters = {}
+        if branch_name != '':
+            filters['ref_name'] = branch_name
+        if after_date != '1970-01-01':
+            filters['since'] = after_date
+        if before_date != '9999-12-31':
+            filters['until'] = before_date
+        if len(filters) == 0:
+            gitlab_commits = self.git_project.commits.list(all=True)
+        else:
+            gitlab_commits = self.git_project.commits.list(all=True, query_parameters=filters)
+        commits = []
+        for commit in gitlab_commits:
+            commits.append(GitlabCommit(commit))
+        return commits
+
     def __repr__(self):
         return self.get_name_with_namespace()
 
@@ -378,6 +459,18 @@ if __name__ == '__main__':
     print('All projects in sandbox:')
     for test_project in test_projects:
         print(test_project)
+    print('Selecting last project. Here are the commits:')
+    test_project = test_projects[-1]
+    commits = test_project.get_commits()
+    diff = commits[-5].get_diffs()
+    print(diff)
+    print(commits[0].get_author())
+    for commit in commits:
+        print(commit.get_message())
+        print(f'is a merge? {commit.is_merge()}')
+        print(f'size: {commit.get_diff_size()}')
+        print()
+    """
     print('Selecting second project. Here are the members:')
     test_project = test_projects[1]
     members = test_project.get_users()
@@ -401,3 +494,4 @@ if __name__ == '__main__':
     print(f'after culling, there are {new_number_of_projects} projects that were created in/after August 2019')
     print(f'including {test_projects[0]} created by {test_projects[0].get_creator()} at '
           f'{test_projects[0].get_created_at()}.')
+    """
diff --git a/grade_team_contribution.py b/grade_team_contribution.py
index 403d7efb3be58f195479b24c980c7684528bde65..ab61cb45bed607479c4b055be2ddd31147a7b208 100644
--- a/grade_team_contribution.py
+++ b/grade_team_contribution.py
@@ -1,6 +1,7 @@
 import textwrap
 
 from api.canvas_classes import *
+from api.gitlab_classes import *
 from course import Course
 
 
@@ -34,28 +35,40 @@ def display_peer_reviews(assignment, students):
             print(f'\t\t{line}'.expandtabs(4))
 
 
-def display_git_contributions():
+def get_project_prefix(canvas_groups):
+    name_segments = canvas_groups[0].get_name().split()
+    prefix = input(f'What is the prefix of the gitlab project names? [{name_segments[0]}] ')
+    if prefix == '':
+        prefix = name_segments[0]
+    return prefix
+
+
+def display_git_contributions(project):
     print('Review git contributions offline')
+# TODO: recognize that this only works for projects in namespace; will need to ask whether project should be retrieved.
 
 
 def grade(assignment1, assignment2, students):
-    pass
+    print('Enter grades through Canvas gradebook')
 
 
 if __name__ == '__main__':
     course = CanvasCourse(Course.canvas_course_id)
-    print('First, select the Canvas assignment to review and grade.\n')
+    projects = GitlabProject.get_projects_by_group(Course.gitlab_namespace)
+    print('First, select the "peer review" assignment to review and grade.\n')
     assignment_groups = course.get_assignment_groups()
     assignment_group = select_from_list(assignment_groups, 'assignment group')
     print()
     assignments = assignment_group.get_assignments()
     peer_review_assignment = select_from_list(assignments, 'assignment')
     print(f'\nSelected {peer_review_assignment}.')
+    # TODO: select second assignment (git history)
     print('Now select the student groupset with the teams.\n')
     student_groupsets = course.get_user_groupsets()
     student_groupset = select_from_list(student_groupsets, 'groupset')
     print(f'\nSelected {student_groupset}.\n')
     student_groups = student_groupset.get_groups()
+    project_prefix = get_project_prefix(student_groups)
     print('Are you grading all groups, or are you revisiting a specific group?')
     options = ['All groups', 'Specific group']
     option = select_from_list(options, 'option')
@@ -65,7 +78,8 @@ if __name__ == '__main__':
     for student_group in student_groups:
         input(f'\n\nPress any key to grade {student_group}')
         display_peer_reviews(peer_review_assignment, student_group.get_students())
-        display_git_contributions()
+        project_name = f'{project_prefix}{student_group.get_name().split()[1]}'
+        display_git_contributions(list(filter(lambda p: p.get_name() == project_name, projects))[0])
         # TODO: Ask if you want to grade (keep track of groups that you don't grade)
         if True:
             grade(peer_review_assignment, None, student_group.get_students())