Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/repository.rst
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,4 @@ Below there are some general attributes and methods:
.. automethod:: pygit2.Repository.write
.. automethod:: pygit2.Repository.reset
.. automethod:: pygit2.Repository.state_cleanup
.. automethod:: pygit2.Repository.write_archive
73 changes: 72 additions & 1 deletion pygit2/repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
from _pygit2 import Repository as _Repository
from _pygit2 import Oid, GIT_OID_HEXSZ, GIT_OID_MINPREFIXLEN
from _pygit2 import GIT_CHECKOUT_SAFE_CREATE, GIT_DIFF_NORMAL
from _pygit2 import GIT_FILEMODE_LINK
from _pygit2 import Reference, Tree, Commit, Blob

from .config import Config
Expand All @@ -43,7 +44,7 @@
from .index import Index
from .remote import Remote
from .blame import Blame
from .utils import to_bytes, to_str
from .utils import to_bytes, to_str, is_string


class Repository(_Repository):
Expand Down Expand Up @@ -488,3 +489,73 @@ def index(self):
check_error(err, True)

return Index.from_c(self, cindex)

#
# Utility for writing a tree into an archive
#
def write_archive(self, treeish, archive, timestamp=None):
"""Write treeish into an archive

If no timestamp is provided and 'treeish' is a commit, its committer
timestamp will be used. Otherwise the current time will be used.

Arguments:

treeish
The treeish to write.
archive
An archive from the 'tarfile' module
timestamp
Timestamp to use for the files in the archive.

Example::

>>> import tarfile, pygit2
>>>> with tarfile.open('foo.tar', 'w') as archive:
>>>> repo = pygit2.Repsitory('.')
>>>> repo.write_archive(archive, repo.head.target)
"""

import tarfile, sys
from time import time
if sys.version_info[0] < 3:
from cStringIO import StringIO
else:
from io import BytesIO as StringIO

# Try to get a tree form whatever we got
if isinstance(treeish, Tree):
tree = treeish

if isinstance(treeish, Oid) or is_string(treeish):
treeish = self[treeish]

# if we don't have a timestamp, try to get it from a commit
if not timestamp:
try:
commit = treeish.peel(Commit)
timestamp = commit.committer.time
except:
pass

# as a last resort, use the current timestamp
if not timestamp:
timestamp = int(time())

tree = treeish.peel(Tree)

index = Index()
index.read_tree(tree)

for entry in index:
content = self[entry.id].read_raw()
info = tarfile.TarInfo(entry.path)
info.size = len(content)
info.mtime = timestamp
info.uname = info.gname = 'root' # just because git does this
if entry.mode == GIT_FILEMODE_LINK:
info.type = archive.SYMTYPE
info.linkname = content
info.mode = 0o777 # symlinks get placeholder

archive.addfile(info, StringIO(content))
74 changes: 74 additions & 0 deletions test/test_archive.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# -*- coding: UTF-8 -*-
#
# Copyright 2010-2014 The pygit2 contributors
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2,
# as published by the Free Software Foundation.
#
# In addition to the permissions in the GNU General Public License,
# the authors give you unlimited permission to link the compiled
# version of this file into combinations with other programs,
# and to distribute those combinations without any restriction
# coming from the use of this file. (The General Public License
# restrictions do apply in other respects; for example, they cover
# modification of the file, and distribution when not linked into
# a combined executable.)
#
# This file is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; see the file COPYING. If not, write to
# the Free Software Foundation, 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.

"""Tests for Blame objects."""

from __future__ import absolute_import
from __future__ import unicode_literals
import unittest
import pygit2
from pygit2 import Index, Oid, Tree, Object
import tarfile
import os
from . import utils
from time import time

TREE_HASH = 'fd937514cb799514d4b81bb24c5fcfeb6472b245'
COMMIT_HASH = '2be5719152d4f82c7302b1c0932d8e5f0a4a0e98'

class ArchiveTest(utils.RepoTestCase):

def check_writing(self, treeish, timestamp=None):
archive = tarfile.open('foo.tar', mode='w')
self.repo.write_archive(treeish, archive)

index = Index()
if isinstance(treeish, Object):
index.read_tree(treeish.peel(Tree))
else:
index.read_tree(self.repo[treeish].peel(Tree))

self.assertEqual(len(index), len(archive.getmembers()))

if timestamp:
fileinfo = archive.getmembers()[0]
self.assertEqual(timestamp, fileinfo.mtime)

archive.close()
self.assertTrue(os.path.isfile('foo.tar'))
os.remove('foo.tar')

def test_write_tree(self):
self.check_writing(TREE_HASH)
self.check_writing(Oid(hex=TREE_HASH))
self.check_writing(self.repo[TREE_HASH])

def test_write_commit(self):
commit_timestamp = self.repo[COMMIT_HASH].committer.time
self.check_writing(COMMIT_HASH, commit_timestamp)
self.check_writing(Oid(hex=COMMIT_HASH), commit_timestamp)
self.check_writing(self.repo[COMMIT_HASH], commit_timestamp)