Ejemplo n.º 1
0
def chain(request, permission, repo_name, oid):
    """Display chain of Git repository commits.

    Args:
        permission: permission rights of accessing user.
        repo_name: name of managed repository.
        oid: offset object ID to walk from when listing commits.
    """
    if permission == permission.NO_ACCESS:
        # TODO return Http404 properly
        return HttpResponseNotFound("no matching repository")

    db_repo_obj = get_object_or_404(Repository, name=repo_name)
    repo = mpygit.Repository(db_repo_obj.path)

    context = {
        "repo_name": repo_name,
        "oid": oid,
        "can_manage": permission == Permission.CAN_MANAGE,
    }

    try:
        obj = repo[oid]
        if obj is None:
            return HttpResponse("Invalid branch or commit ID")
    except KeyError:
        pass
    else:
        context["commits"] = gitutil.walk(repo, obj.oid, 100)

    return render(request, "chain.html", context=context)
Ejemplo n.º 2
0
    def _get_chain(self, repo_name):
        db_repo = Repository.objects.get(name=repo_name)
        repo = mpygit.Repository(db_repo.path)

        response = self.client.get(f"/{repo_name}/chain/", follow=True)
        self.assertEquals(response.status_code, 200)
        return response.content.decode()
Ejemplo n.º 3
0
    def test_has_diff(self):
        linear = mpygit.Repository(Repository.objects.get(name="linear").path)
        commit = next(gitutil.walk(linear, linear.HEAD, 1))

        ENDPOINT = f"/linear/info/{commit.oid}"

        response = self.client.get(ENDPOINT)
        self.assertEqual(response.status_code, 200)
        content = response.content.decode()

        # this took far longer than i'm willing to admit
        DIFF_REGEX = (r"""<table class="highlighttable">"""
                      r"""\s*<tr>"""
                      r"""\s*<td class="linenos">"""
                      r"""[\s\S]*"""
                      r"""</td>"""
                      r"""<td class="code">"""
                      r"""\s*<div class="highlight">"""
                      r"""\s*<pre>[\s\S]*"""
                      r"""\s*<span class="gd">\-\-\- a/file</span>"""
                      r"""\s*<span class="gi">\+\+\+ b/file</span>"""
                      r"""\s*<span class="gu">@@ -1,4 \+1,4 @@</span>"""
                      r"""\s*<span class="gd">-#4</span>"""
                      r"""\s*<span class="gi">\+#5</span>"""
                      """\n multi"""
                      """\n line"""
                      """\n file"""
                      """\n"""
                      r"""</pre>"""
                      r"""\s*</div>"""
                      r"""\s*</td>"""
                      r"""\s*</tr>"""
                      r"""\s*</table>""")
        self.assertTrue(re.search(DIFF_REGEX, content))
Ejemplo n.º 4
0
    def test_displays_textual_contents(self):
        ENDPOINT = "/files/view/master/multi_line_textual_file"
        content = self._get_content(ENDPOINT)

        files = mpygit.Repository(Repository.objects.get(name="files").path)
        commit = [c for c in gitutil.walk(files, files.HEAD, 2)][-1]
        fs_blob = resolve_path(files, commit.tree, "multi_line_textual_file")

        # validate contents
        HIGHLIGHT_REGEX = (r"""<div class="blob_box blob_code">"""
                           r"""\s*<table class="highlighttable">"""
                           r"""\s*<tr>"""
                           r"""\s*<td class="linenos">"""
                           r"""\s*<div class="linenodiv">"""
                           r"""\s*<pre>"""
                           r"""\s*<span class="normal">1</span>"""
                           r"""\s*<span class="normal">2</span>"""
                           r"""\s*<span class="normal">3</span>"""
                           r"""\s*</pre>"""
                           r"""\s*</div>"""
                           r"""\s*</td>"""
                           r"""\s*<td class="code">"""
                           r"""\s*<div class="highlight">"""
                           r"""\s*<pre>"""
                           r"""\s*<span></span>multi\n"""
                           r"""line\n"""
                           r"""file\n"""
                           r"""\s*</pre>"""
                           r"""\s*</div>"""
                           r"""\s*</td>"""
                           r"""\s*</tr>"""
                           r"""\s*</table>"""
                           r"""\s*</div>""")
        self.assertTrue(re.search(HIGHLIGHT_REGEX, content))
Ejemplo n.º 5
0
    def test_empty_chain(self):
        db_repo = Repository.objects.get(name="simple")
        repo = mpygit.Repository(db_repo.path)

        response = self.client.get("/simple/chain/", follow=True)
        self.assertEquals(response.status_code, 200)
        content = response.content.decode()
        self.assertFalse(re.search(self.REGEX_CHAIN_ENTS, content))
Ejemplo n.º 6
0
def info(request, permission, repo_name, oid):
    """Display commit information.

    Commit information includes:
    - author name and email
    - commit timestamp
    - modified blobs
    - deltas (including highlighted diffs)

    Args:
        permission: permission rights of accessing user.
        repo_name: name of managed repository.
        oid: commit object ID to inspect.
    """
    class FileChange:
        def __init__(self, path, patch, status):
            self.path = path
            self.patch = utils.highlight_code("name.diff", patch)
            self.status = status
            self.deleted = status == "D"

            # The line stats are not very elegant but difflib is kind of limited
            insert = len(re.findall(r"^\+", patch, re.MULTILINE)) - 1
            delete = len(re.findall(r"^-", patch, re.MULTILINE)) - 1
            self.insertion = f"++{insert}"
            self.deletion = f"--{delete}"

    if permission == permission.NO_ACCESS:
        # TODO return Http404 properly
        return HttpResponseNotFound("no  matching repository")

    db_repo_obj = get_object_or_404(Repository, name=repo_name)
    repo = mpygit.Repository(db_repo_obj.path)

    commit = repo[oid]
    if commit is None or not isinstance(commit, mpygit.Commit):
        return HttpResponse("Invalid branch or commit ID")

    changes = []
    parent = repo[commit.parents[0]] if len(commit.parents) > 0 else None
    diffs = gitutil.diff_commits(repo, parent, commit)
    for path, patch, status in diffs:
        changes.append(FileChange(path, patch, status))

    context = {
        "repo_name": repo_name,
        "oid": oid,
        "commit": commit,
        "changes": changes,
        "can_manage": permission == Permission.CAN_MANAGE,
    }

    return render(request, "commit.html", context=context)
Ejemplo n.º 7
0
    def test_correct_committer_info(self):
        linear = mpygit.Repository(Repository.objects.get(name="linear").path)
        commit = next(gitutil.walk(linear, linear.HEAD, 1))

        ENDPOINT = f"/linear/info/{commit.oid}"

        response = self.client.get(ENDPOINT)
        self.assertEqual(response.status_code, 200)

        content = response.content.decode()

        DIFF_REGEX = (
            r"""<tr>\s*"""
            r"""<td>M</td>\s*"""
            r"""<td>\+\+1</td>\s*"""
            r"""<td>--1</td>\s*"""
            f"""<td><a href="/linear/view/{commit.oid}/file">file</a></td>"""
            r"""\s*</tr>""")
        self.assertTrue(re.search(DIFF_REGEX, content))

        COMMITTER_REGEX = (r"""<table class="commit_info">"""
                           r"""\s*<tr>"""
                           r"""\s*<td>From:</td>"""
                           r"""\s*<td>(?P<commiter_info>.*)</td>"""
                           r"""\s*</tr>"""
                           r"""\s*<tr>"""
                           r"""\s*<td>Date:</td>"""
                           r"""\s*<td>"""
                           r"""\s*<time [\s\S]*>(?P<date>.*)</time>"""
                           r"""\s*</td>"""
                           r"""\s*</tr>"""
                           r"""\s*<tr>"""
                           r"""\s*<td class="commit_msg"[\s\S]*>"""
                           r"""\s*<pre>(?P<msg>.*)</pre>"""
                           r"""\s*</td>"""
                           r"""\s*</tr>"""
                           r"""\s*</table>""")

        match = re.search(COMMITTER_REGEX, content)
        self.assertTrue(match)

        committer_info, timestamp, msg = match.groups()
        name, email = committer_info.rsplit(" ", 1)
        email = email[4:-4]

        self.assertEqual(commit.committer.name, name)
        self.assertEqual(commit.committer.email, email)
        self.assertEqual(commit.message, msg)
        fs_timestamp = dt.utcfromtimestamp(
            commit.committer.timestamp).strftime("%Y-%m-%d %H:%M")
        self.assertEqual(fs_timestamp, timestamp)
Ejemplo n.º 8
0
    def test_displays_commits(self):
        content = self._get_chain("linear")
        repo = mpygit.Repository(Repository.objects.get(name="linear").path)
        repo_walker = gitutil.walk(repo, repo.HEAD, 5)
        for fs_ent, match in zip(repo_walker,
                                 re.finditer(self.REGEX_CHAIN_ENTS, content)):
            oid, short_oid, msg, committer, timestamp = match.groups()
            self.assertEqual(fs_ent.oid, oid)
            self.assertEqual(fs_ent.short_oid, short_oid)
            self.assertEqual(fs_ent.message, msg)
            self.assertEqual(fs_ent.committer.name, committer)

            fs_timestamp = dt.utcfromtimestamp(
                fs_ent.committer.timestamp).strftime("%Y-%m-%d")
            self.assertEqual(fs_timestamp, timestamp)
Ejemplo n.º 9
0
    def test_inspect_tree_at_commit(self):
        linear = mpygit.Repository(Repository.objects.get(name="linear").path)
        commit = next(gitutil.walk(linear, linear.HEAD, 1))

        ENDPOINT = f"/linear/info/{commit.oid}"

        response = self.client.get(ENDPOINT)
        self.assertEqual(response.status_code, 200)
        content = response.content.decode()

        ANCHOR_REGEX = (r"""<a class="button commit_inspect" [\s\S]*"""
                        f"""href="/linear/view/{commit.oid}/">"""
                        r"""Inspect Tree"""
                        r"""\s*</a>""")
        self.assertTrue(re.search(ANCHOR_REGEX, content))
Ejemplo n.º 10
0
def view(request, permission, repo_name, oid, path):
    if permission == permission.NO_ACCESS:
        raise Http404("no matching repository")

    db_repo_obj = get_object_or_404(Repository, name=repo_name)
    repo = mpygit.Repository(db_repo_obj.path)

    # First we normalize the path so libgit2 doesn't choke
    path = utils.normalize_path(path)

    commit = repo[oid]
    if commit is None or not isinstance(commit, mpygit.Commit):
        return HttpResponse("Invalid commit ID")

    # Resolve path inside commit
    obj = utils.resolve_path(repo, commit.tree, path)
    if obj == None:
        return HttpResponse("Invalid path")

    context = {
        "repo_name": repo_name,
        "oid": oid,
        "path": path,
        "branches": gen_branches(repo_name, repo, oid),
        "crumbs": gen_crumbs(repo_name, oid, path),
        "can_manage": permission == Permission.CAN_MANAGE,
    }

    if isinstance(obj, mpygit.Tree):
        template = "tree.html"
        context["entries"] = utils.tree_entries(repo, commit, path, obj)
    elif isinstance(obj, mpygit.Blob):
        template, code = read_blob(obj)
        if template == "blob.html":
            context["code"] = utils.highlight_code(path, code)
        else:
            context["code"] = code
        commit = gitutil.get_latest_change(repo, commit.oid,
                                           utils.split_path(path))
        context["change"] = commit
    else:
        return HttpResponse("Unsupported object type")

    return render(request, template, context=context)
Ejemplo n.º 11
0
def chain(request, permission, repo_name, oid):
    if permission == permission.NO_ACCESS:
        raise Http404("no matching repository")

    db_repo_obj = get_object_or_404(Repository, name=repo_name)
    # Open a repo object to the requested repo
    repo = mpygit.Repository(db_repo_obj.path)

    obj = repo[oid]
    if obj is None:
        return HttpResponse("Invalid branch or commit ID")

    context = {
        "repo_name": repo_name,
        "oid": oid,
        "commits": gitutil.walk(repo, obj.oid, 100),
        "can_manage": permission == Permission.CAN_MANAGE,
    }
    return render(request, "chain.html", context=context)
Ejemplo n.º 12
0
    def test_displays_binary_contents(self):
        ENDPOINT = "/files/view/master/small_binary_file"
        content = self._get_content(ENDPOINT)

        files = mpygit.Repository(Repository.objects.get(name="files").path)
        commit = [c for c in gitutil.walk(files, files.HEAD, 2)][-2]
        fs_blob = resolve_path(files, commit.tree, "multi_line_textual_file")

        HEXDUMP_REGEX = (r"""<div class="blob_box">"""
                         r"""\s*<table id="hex-dump">"""
                         r"""\s*<tr>"""
                         r"""\s*<td>00000000</td>"""
                         r"""\s*<td>89 50 4e 47 0d 0a 1a 0a</td>"""
                         r"""\s*<td>00 00 00 0d 49 48 44 52</td>"""
                         r"""\s*<td>.PNG........IHDR</td>"""
                         r"""\s*</tr>"""
                         r"""\s*</table>"""
                         r"""\s*</div>""")
        self.assertTrue(re.search(HEXDUMP_REGEX, content))
Ejemplo n.º 13
0
    def test_displays_commit_info(self):
        ENDPOINT = "/files/view/master/multi_line_textual_file"
        content = self._get_content(ENDPOINT)

        files = mpygit.Repository(Repository.objects.get(name="files").path)
        commit = [c for c in gitutil.walk(files, files.HEAD, 2)][-1]

        # validate author info
        # if the message and OIDs match then the author info will match similarly
        # otherwise the commit info page would fail (same backend logic)
        COMMIT_ANCHOR_REGEX = (
            r"""<table class="blob_box">"""
            r"""\s*<tr>"""
            r"""\s*<td>"""
            f"""{commit.message}"""
            f"""\\s*\\[<a href="/files/info/{commit.oid}">{commit.short_oid}</a>\\]"""
            r"""\s*</td>"""
            r"""\s*</tr>"""
            r"""[\s\S]*</table>""")
        self.assertTrue(re.search(COMMIT_ANCHOR_REGEX, content))
Ejemplo n.º 14
0
def info(request, permission, repo_name, oid):
    class FileChange:
        def __init__(self, path, patch, status):
            self.path = path
            self.patch = utils.highlight_code("name.diff", patch)
            self.status = status
            self.deleted = status == "D"

            # The line stats are not very elegant but difflib is kind of limited
            insert = len(re.findall(r"^\+", patch, re.MULTILINE)) - 1
            delete = len(re.findall(r"^-", patch, re.MULTILINE)) - 1
            self.insertion = f"++{insert}"
            self.deletion = f"--{delete}"

    if permission == permission.NO_ACCESS:
        raise Http404("no matching repository")

    db_repo_obj = get_object_or_404(Repository, name=repo_name)
    repo = mpygit.Repository(db_repo_obj.path)

    commit = repo[oid]
    if commit is None or not isinstance(commit, mpygit.Commit):
        return HttpResponse("Invalid branch or commit ID")

    changes = []
    parent = repo[commit.parents[0]] if len(commit.parents) > 0 else None
    diffs = gitutil.diff_commits(repo, parent, commit)
    for path, patch, status in diffs:
        changes.append(FileChange(path, patch, status))

    context = {
        "repo_name": repo_name,
        "oid": oid,
        "commit": commit,
        "changes": changes,
        "can_manage": permission == Permission.CAN_MANAGE,
    }

    return render(request, "commit.html", context=context)
Ejemplo n.º 15
0
    def test_can_inspect(self):
        users = [
            ("admin", 200),
            ("manager", 200),
            ("viewer", 200),
            ("regular", 404),
        ]

        linear = mpygit.Repository(Repository.objects.get(name="linear").path)
        ENDPOINT = f"/linear/info/{linear.HEAD}/file"

        for username, status in users:
            self.client.login(username=username, password="")
            response = self.client.get(ENDPOINT)
            self.assertEqual(
                response.status_code,
                status,
                f'user "{username}" has unexpected commit visibility',
            )

        self.client.logout()
        response = self.client.get(ENDPOINT)
        self.assertEqual(response.status_code, 404)
Ejemplo n.º 16
0
def view(request, permission, repo_name, oid, path):
    """Display blob or tree.

    If the path in the URL references a blob then the blob view template
    is rendered (which is specialised to whether the blob is a binary blob
    or not). Otherwise, a tree view is presented which displays the contents
    of a (sub)tree.

    Args:
        permission: permission rights of accessing user.
        repo_name: name of repository to inspect.
        path: path to blob or tree.
    """
    if permission == permission.NO_ACCESS:
        # TODO use Http404
        return HttpResponseNotFound("no matching repository")

    db_repo_obj = get_object_or_404(Repository, name=repo_name)
    repo = mpygit.Repository(db_repo_obj.path)

    # First we normalize the path so libgit2 doesn"t choke
    path = utils.normalize_path(path)

    try:
        commit = repo[oid]
    except KeyError:
        # TODO use Http404
        return HttpResponseNotFound("invalid head")

    if commit is None or not isinstance(commit, mpygit.Commit):
        return HttpResponse("Invalid commit ID")

    # Resolve path inside commit
    obj = utils.resolve_path(repo, commit.tree, path)
    if obj == None:
        return HttpResponse("Invalid path")

    context = {
        "repo_name": repo_name,
        "oid": oid,
        "path": path,
        "branches": gen_branches(repo_name, repo, oid),
        "crumbs": gen_crumbs(repo_name, oid, path),
        "can_manage": permission == Permission.CAN_MANAGE,
    }

    # specialise view to display object type correctly
    if isinstance(obj, mpygit.Tree):
        template = "tree.html"
        context["entries"] = utils.tree_entries(repo, commit, path, obj)
    elif isinstance(obj, mpygit.Blob):
        template, code = read_blob(obj)
        if template == "blob.html":
            # highlight code in textual blobs
            context["code"] = utils.highlight_code(path, code)
        else:
            context["code"] = code
        commit = gitutil.get_latest_change(repo, commit.oid,
                                           utils.split_path(path))
        context["change"] = commit
    else:
        return HttpResponse("Unsupported object type")

    return render(request, template, context=context)