Exemple #1
0
def commit(author: str, message: str) -> str:
    """https://benhoyt.com/writings/pygit/"""
    index = read_index()
    if len(index) == 0:
        raise ValueError("nothing to commit")

    repo = repo_find()
    assert repo is not None
    try:
        with open(str(repo_file(repo, "refs", "heads", "master")), "r") as f:
            parent: Optional[str] = f.read().strip()
    except FileNotFoundError:
        _LOG.debug("No prior commits")
        parent = None

    # git write-tree
    sha_of_tree = tree_write(repo, index)

    # git commit-tree
    lines = [f"tree {sha_of_tree}"]
    if parent:
        lines.append(f"parent {parent}")
    author_time = author_timestamp()
    lines = lines + [
        f"author {author} {author_time}", f"committer {author} {author_time}",
        '', message, ''
    ]
    data = '\n'.join(lines).encode()
    sha1 = generic_object_hash(io.BytesIO(data), b"commit", repo)

    # git update-ref
    master_path = repo_file(repo, "refs", "heads", "master", write=True)
    write_file(str(master_path), (sha1 + "\n").encode())
    return sha1
Exemple #2
0
def object_write(obj: GitObject, actually_write: bool = True) -> str:
    # Serialize object data
    data = obj.serialize()

    # Add header
    result = obj.fmt + b' ' + str(len(data)).encode() + b'\x00' + data

    # Compute hash
    sha = hashlib.sha1(result).hexdigest()

    if actually_write:
        if obj.repo is None:
            raise ValueError("repo is None on actually_write in object_write")
        # Compute path
        path = repo_file(obj.repo,
                         "objects",
                         sha[0:2],
                         sha[2:],
                         mkdir=True,
                         write=True)

        with open(str(path), "wb") as f:
            # Compress and write
            f.write(zlib.compress(result))

    return sha
Exemple #3
0
def generic_object_read(repo: GitRepository, sha: Sha) -> GitObject:
    """Read object object_id from Git repository repo. Return a GitObject whose exact
       type depends on the object"""
    path = repo_file(repo, "objects", sha[0:2], sha[2:])
    assert path is not None, f"Path {path} for object {sha} could not be found"

    raw = zlib_read(path)

    # Read object type
    x = raw.find(b' ')
    fmt = raw[0:x]

    # Read and validate object size
    y = raw.find(b'\x00', x)
    size = int(raw[x:y].decode("ascii"))
    if size != len(raw) - y - 1:
        raise ValueError(f"Malformed object {sha}: bad length")

    # Pick constructor
    if fmt == b'commit':
        return GitCommit(repo, raw[y + 1:])
    elif fmt == b'tree':
        return GitTree(repo, raw[y + 1:])
    elif fmt == b'tag':
        return GitTag(repo, raw[y + 1:])
    elif fmt == b'blob':
        return GitBlob(repo, raw[y + 1:])
    else:
        raise ValueError(
            f"Unknown type {fmt.decode('ascii')} for object {sha}")
Exemple #4
0
def ref_resolve(repo: GitRepository, ref: str) -> str:
    with open(str(repo_file(repo, ref)), 'r') as fp:
        data = fp.read()[:-1]  # .trim()

    if data.startswith("ref: "):
        return ref_resolve(repo, data[5:])

    return data
Exemple #5
0
def object_get_type(repo: GitRepository, sha: Sha) -> bytes:
    """Read object object_id from Git repository repo. Return a GitObject whose exact
       type depends on the object"""
    path = repo_file(repo, "objects", sha[0:2], sha[2:])
    assert path is not None, f"Path {path} for object {sha} could not be found"

    raw = zlib_read(path)

    # Read object type
    x = raw.find(b' ')
    return raw[0:x]
Exemple #6
0
def object_resolve(repo: GitRepository, name: str) -> List[str]:
    """Resolve name to an object hash in repo.

       This function is aware of:
        - the HEAD literal
        - short and long hashes
        - tags
        - branches
        - remote branches"""

    candidates = []
    hashRE = re.compile(r"^[0-9A-Fa-f]{40}$")
    smallHashRE = re.compile(r"^[0-9A-Fa-f]{4,40}$")

    # Empty string? Abort.
    if not name.strip():
        return []

    if name == "HEAD":
        return [ref_resolve(repo, "HEAD")]

    if hashRE.match(name):
        # This is a complete hash
        return [name.lower()]
    elif smallHashRE.match(name):
        # This is a small hash. 4 seems to be the minimal length for git to
        # consider something a short hash. This limit is documented in man
        # git-rev-parse
        name = name.lower()
        prefix = name[:2]
        path = repo_dir(repo, "objects", prefix, mkdir=False)
        if path:
            rem = name[2:]
            for f in path.iterdir():
                if str(f).startswith(rem):
                    candidates.append(prefix + str(f))

    # search for branches and tags (with or without "refs" and "heads" or "tags" prefixes)
    for ref_path in [
            f'refs/heads/{name}', f'refs/tags/{name}', f'refs/{name}', name
    ]:
        ref = repo_file(repo, ref_path)
        assert ref is not None
        if ref.exists():
            candidates.append(ref_resolve(repo, ref_path))

    return candidates
Exemple #7
0
def write_index(entries: List[GitIndexEntry]) -> None:
    """Write list of index entries objects to git index file.

       https://github.com/benhoyt/pygit/blob/master/pygit.py"""
    packed_entries = []
    for entry in entries:
        entry_head = struct.pack(ENTRY_FORMAT, entry.ctime[0], entry.ctime[1],
                                 entry.mtime[0], entry.mtime[1], entry.dev,
                                 entry.ino, entry.mode, entry.uid, entry.gid,
                                 entry.size, entry.obj, entry.flags)
        path = entry.name.encode()
        length = ((62 + len(path) + 8) // 8) * 8
        packed_entry = entry_head + path + b'\x00' * (length - 62 - len(path))
        packed_entries.append(packed_entry)
    header = struct.pack(HEADER_FORMAT, b'DIRC', 2, len(entries))
    all_data = header + b''.join(packed_entries)
    digest = hashlib.sha1(all_data).digest()
    repo = repo_find()
    assert repo is not None
    with open(str(repo_file(repo, 'index', write=True)), "wb") as f:
        f.write(all_data + digest)
Exemple #8
0
def ref_create(repo: GitRepository, ref_name: str, sha: str) -> None:
    with open(str(repo_file(repo, "refs/" + ref_name, write=True)), "w") as fp:
        fp.write(sha + '\n')