зеркало из https://github.com/mozilla/playdoh-lib.git
Added jingo-minify and GitPython
This commit is contained in:
Родитель
a88a45646d
Коммит
50f7ed7ae3
|
@ -13,3 +13,6 @@
|
|||
[submodule "src/tower"]
|
||||
path = src/tower
|
||||
url = git://github.com/clouserw/tower.git
|
||||
[submodule "src/jingo-minify"]
|
||||
path = src/jingo-minify
|
||||
url = git://github.com/jsocol/jingo-minify.git
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
GitPython was originally written by Michael Trier.
|
||||
|
||||
Contributors are:
|
||||
|
||||
-Michael Trier <mtrier _at_ gmail.com>
|
||||
-Alan Briolat
|
||||
-Florian Apolloner <florian _at_ apolloner.eu>
|
||||
-David Aguilar <davvid _at_ gmail.com>
|
||||
-Jelmer Vernooij <jelmer _at_ samba.org>
|
||||
-Steve Frécinaux <code _at_ istique.net>
|
||||
-Kai Lautaportti <kai _at_ lautaportti.fi>
|
||||
-Paul Sowden <paul _at_ idontsmoke.co.uk>
|
||||
-Sebastian Thiel http://www.linkedin.com/in/sebastianthiel
|
||||
|
||||
Portions derived from other open source works and are clearly marked.
|
|
@ -0,0 +1,188 @@
|
|||
=======
|
||||
CHANGES
|
||||
=======
|
||||
|
||||
0.1.7
|
||||
=======
|
||||
This is a bugfix release and the last of its kind in 0.1.X, as 0.2.X will receive
|
||||
a major redesign that will change the API.
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
* Paths in Tree objects can now handle whitespace
|
||||
* Blob.blame was returning incorrect results which has been fixed.
|
||||
|
||||
General
|
||||
-------
|
||||
* The test suite now supports Mock 0.5 and above. The transition between Mock 0.4
|
||||
0.5 changed the API which required adjustments.
|
||||
* Many small enhancements done to the method documentation
|
||||
|
||||
|
||||
0.1.6
|
||||
=====
|
||||
|
||||
General
|
||||
-------
|
||||
* Added in Sphinx documentation.
|
||||
|
||||
* Removed ambiguity between paths and treeishs. When calling commands that
|
||||
accept treeish and path arguments and there is a path with the same name as
|
||||
a treeish git cowardly refuses to pick one and asks for the command to use
|
||||
the unambiguous syntax where '--' seperates the treeish from the paths.
|
||||
|
||||
* ``Repo.commits``, ``Repo.commits_between``, ``Reop.commits_since``,
|
||||
``Repo.commit_count``, ``Repo.commit``, ``Commit.count`` and
|
||||
``Commit.find_all`` all now optionally take a path argument which
|
||||
constrains the lookup by path. This changes the order of the positional
|
||||
arguments in ``Repo.commits`` and ``Repo.commits_since``.
|
||||
|
||||
Commit
|
||||
------
|
||||
* ``Commit.message`` now contains the full commit message (rather than just
|
||||
the first line) and a new property ``Commit.summary`` contains the first
|
||||
line of the commit message.
|
||||
|
||||
* Fixed a failure when trying to lookup the stats of a parentless commit from
|
||||
a bare repo.
|
||||
|
||||
Diff
|
||||
----
|
||||
* The diff parser is now far faster and also addresses a bug where
|
||||
sometimes b_mode was not set.
|
||||
|
||||
* Added support for parsing rename info to the diff parser. Addition of new
|
||||
properties ``Diff.renamed``, ``Diff.rename_from``, and ``Diff.rename_to``.
|
||||
|
||||
Head
|
||||
----
|
||||
* Corrected problem where branches was only returning the last path component
|
||||
instead of the entire path component following refs/heads/.
|
||||
|
||||
Repo
|
||||
----
|
||||
* Modified the gzip archive creation to use the python gzip module.
|
||||
|
||||
* Corrected ``commits_between`` always returning None instead of the reversed
|
||||
list.
|
||||
|
||||
|
||||
0.1.5
|
||||
=====
|
||||
|
||||
General
|
||||
-------
|
||||
* upgraded to Mock 0.4 dependency.
|
||||
|
||||
* Replace GitPython with git in repr() outputs.
|
||||
|
||||
* Fixed packaging issue caused by ez_setup.py.
|
||||
|
||||
Blob
|
||||
----
|
||||
* No longer strip newlines from Blob data.
|
||||
|
||||
Commit
|
||||
------
|
||||
* Corrected problem with git-rev-list --bisect-all. See
|
||||
http://groups.google.com/group/git-python/browse_thread/thread/aed1d5c4b31d5027
|
||||
|
||||
Repo
|
||||
----
|
||||
* Corrected problems with creating bare repositories.
|
||||
|
||||
* Repo.tree no longer accepts a path argument. Use:
|
||||
|
||||
>>> dict(k, o for k, o in tree.items() if k in paths)
|
||||
|
||||
* Made daemon export a property of Repo. Now you can do this:
|
||||
|
||||
>>> exported = repo.daemon_export
|
||||
>>> repo.daemon_export = True
|
||||
|
||||
* Allows modifying the project description. Do this:
|
||||
|
||||
>>> repo.description = "Foo Bar"
|
||||
>>> repo.description
|
||||
'Foo Bar'
|
||||
|
||||
* Added a read-only property Repo.is_dirty which reflects the status of the
|
||||
working directory.
|
||||
|
||||
* Added a read-only Repo.active_branch property which returns the name of the
|
||||
currently active branch.
|
||||
|
||||
|
||||
Tree
|
||||
----
|
||||
* Switched to using a dictionary for Tree contents since you will usually want
|
||||
to access them by name and order is unimportant.
|
||||
|
||||
* Implemented a dictionary protocol for Tree objects. The following:
|
||||
|
||||
child = tree.contents['grit']
|
||||
|
||||
becomes:
|
||||
|
||||
child = tree['grit']
|
||||
|
||||
* Made Tree.content_from_string a static method.
|
||||
|
||||
0.1.4.1
|
||||
=======
|
||||
|
||||
* removed ``method_missing`` stuff and replaced with a ``__getattr__``
|
||||
override in ``Git``.
|
||||
|
||||
0.1.4
|
||||
=====
|
||||
|
||||
* renamed ``git_python`` to ``git``. Be sure to delete all pyc files before
|
||||
testing.
|
||||
|
||||
Commit
|
||||
------
|
||||
* Fixed problem with commit stats not working under all conditions.
|
||||
|
||||
Git
|
||||
---
|
||||
* Renamed module to cmd.
|
||||
|
||||
* Removed shell escaping completely.
|
||||
|
||||
* Added support for ``stderr``, ``stdin``, and ``with_status``.
|
||||
|
||||
* ``git_dir`` is now optional in the constructor for ``git.Git``. Git now
|
||||
falls back to ``os.getcwd()`` when git_dir is not specified.
|
||||
|
||||
* add a ``with_exceptions`` keyword argument to git commands.
|
||||
``GitCommandError`` is raised when the exit status is non-zero.
|
||||
|
||||
* add support for a ``GIT_PYTHON_TRACE`` environment variable.
|
||||
``GIT_PYTHON_TRACE`` allows us to debug GitPython's usage of git through
|
||||
the use of an environment variable.
|
||||
|
||||
Tree
|
||||
----
|
||||
* Fixed up problem where ``name`` doesn't exist on root of tree.
|
||||
|
||||
Repo
|
||||
----
|
||||
* Corrected problem with creating bare repo. Added ``Repo.create`` alias.
|
||||
|
||||
0.1.2
|
||||
=====
|
||||
|
||||
Tree
|
||||
----
|
||||
* Corrected problem with ``Tree.__div__`` not working with zero length files.
|
||||
Removed ``__len__`` override and replaced with size instead. Also made size
|
||||
cach properly. This is a breaking change.
|
||||
|
||||
0.1.1
|
||||
=====
|
||||
Fixed up some urls because I'm a moron
|
||||
|
||||
0.1.0
|
||||
=====
|
||||
initial release
|
|
@ -0,0 +1,30 @@
|
|||
Copyright (C) 2008-2010 Michael Trier and contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the GitPython project nor the names of
|
||||
its contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
include VERSION
|
||||
include LICENSE
|
||||
include CHANGES
|
||||
include AUTHORS
|
||||
include README
|
|
@ -0,0 +1,26 @@
|
|||
Metadata-Version: 1.0
|
||||
Name: GitPython
|
||||
Version: 0.1.7
|
||||
Summary: Python Git Library
|
||||
Home-page: http://gitorious.org/projects/git-python/
|
||||
Author: Michael Trier
|
||||
Author-email: mtrier@gmail.com
|
||||
License: BSD License
|
||||
Description: GitPython is a python library used to interact with Git repositories.
|
||||
|
||||
GitPython provides object model access to your git repository. Once you have
|
||||
created a repository object, you can traverse it to find parent commit(s),
|
||||
trees, blobs, etc.
|
||||
|
||||
GitPython is a port of the grit library in Ruby created by
|
||||
Tom Preston-Werner and Chris Wanstrath.
|
||||
|
||||
Platform: UNKNOWN
|
||||
Classifier: Development Status :: 3 - Alpha
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: License :: OSI Approved :: BSD License
|
||||
Classifier: Operating System :: OS Independent
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Programming Language :: Python :: 2.5
|
||||
Classifier: Programming Language :: Python :: 2.6
|
||||
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
@ -0,0 +1,42 @@
|
|||
==========
|
||||
GitPython
|
||||
==========
|
||||
|
||||
GitPython is a python library used to interact with Git repositories.
|
||||
|
||||
GitPython is a port of the grit_ library in Ruby created by
|
||||
Tom Preston-Werner and Chris Wanstrath.
|
||||
|
||||
.. _grit: http://grit.rubyforge.org
|
||||
|
||||
REQUIREMENTS
|
||||
============
|
||||
|
||||
* Git_ tested with 1.5.3.7
|
||||
* `Python Nose`_ - used for running the tests
|
||||
* `Mock by Michael Foord`_ used for tests. Requires 0.4
|
||||
|
||||
.. _Git: http://git.or.cz/
|
||||
.. _Python Nose: http://code.google.com/p/python-nose/
|
||||
.. _Mock by Michael Foord: http://www.voidspace.org.uk/python/mock.html
|
||||
|
||||
INSTALL
|
||||
=======
|
||||
|
||||
python setup.py install
|
||||
|
||||
SOURCE
|
||||
======
|
||||
|
||||
GitPython's git repo is available on Gitorious, which can be browsed at:
|
||||
|
||||
http://gitorious.org/projects/git-python/
|
||||
|
||||
and cloned from:
|
||||
|
||||
git://gitorious.org/git-python/mainline.git
|
||||
|
||||
LICENSE
|
||||
=======
|
||||
|
||||
New BSD License. See the LICENSE file.
|
|
@ -0,0 +1 @@
|
|||
0.1.7
|
|
@ -0,0 +1,26 @@
|
|||
Metadata-Version: 1.0
|
||||
Name: GitPython
|
||||
Version: 0.1.7
|
||||
Summary: Python Git Library
|
||||
Home-page: http://gitorious.org/projects/git-python/
|
||||
Author: Michael Trier
|
||||
Author-email: mtrier@gmail.com
|
||||
License: BSD License
|
||||
Description: GitPython is a python library used to interact with Git repositories.
|
||||
|
||||
GitPython provides object model access to your git repository. Once you have
|
||||
created a repository object, you can traverse it to find parent commit(s),
|
||||
trees, blobs, etc.
|
||||
|
||||
GitPython is a port of the grit library in Ruby created by
|
||||
Tom Preston-Werner and Chris Wanstrath.
|
||||
|
||||
Platform: UNKNOWN
|
||||
Classifier: Development Status :: 3 - Alpha
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: License :: OSI Approved :: BSD License
|
||||
Classifier: Operating System :: OS Independent
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Programming Language :: Python :: 2.5
|
||||
Classifier: Programming Language :: Python :: 2.6
|
||||
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
@ -0,0 +1,25 @@
|
|||
AUTHORS
|
||||
CHANGES
|
||||
LICENSE
|
||||
MANIFEST.in
|
||||
README
|
||||
VERSION
|
||||
setup.py
|
||||
lib/GitPython.egg-info/PKG-INFO
|
||||
lib/GitPython.egg-info/SOURCES.txt
|
||||
lib/GitPython.egg-info/dependency_links.txt
|
||||
lib/GitPython.egg-info/top_level.txt
|
||||
lib/git/__init__.py
|
||||
lib/git/actor.py
|
||||
lib/git/blob.py
|
||||
lib/git/cmd.py
|
||||
lib/git/commit.py
|
||||
lib/git/diff.py
|
||||
lib/git/errors.py
|
||||
lib/git/head.py
|
||||
lib/git/lazy.py
|
||||
lib/git/repo.py
|
||||
lib/git/stats.py
|
||||
lib/git/tag.py
|
||||
lib/git/tree.py
|
||||
lib/git/utils.py
|
|
@ -0,0 +1 @@
|
|||
|
|
@ -0,0 +1 @@
|
|||
git
|
|
@ -0,0 +1,27 @@
|
|||
# __init__.py
|
||||
# Copyright (C) 2008-2010 Michael Trier (mtrier@gmail.com) and contributors
|
||||
#
|
||||
# This module is part of GitPython and is released under
|
||||
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
|
||||
|
||||
import os
|
||||
import inspect
|
||||
|
||||
__version__ = '0.1.7'
|
||||
|
||||
from git.actor import Actor
|
||||
from git.blob import Blob
|
||||
from git.commit import Commit
|
||||
from git.diff import Diff
|
||||
from git.errors import InvalidGitRepositoryError, NoSuchPathError, GitCommandError
|
||||
from git.cmd import Git
|
||||
from git.head import Head
|
||||
from git.repo import Repo
|
||||
from git.stats import Stats
|
||||
from git.tag import Tag
|
||||
from git.tree import Tree
|
||||
from git.utils import dashify
|
||||
from git.utils import touch
|
||||
|
||||
__all__ = [ name for name, obj in locals().items()
|
||||
if not (name.startswith('_') or inspect.ismodule(obj)) ]
|
|
@ -0,0 +1,42 @@
|
|||
# actor.py
|
||||
# Copyright (C) 2008-2010 Michael Trier (mtrier@gmail.com) and contributors
|
||||
#
|
||||
# This module is part of GitPython and is released under
|
||||
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
|
||||
|
||||
import re
|
||||
|
||||
class Actor(object):
|
||||
"""Actors hold information about a person acting on the repository. They
|
||||
can be committers and authors or anything with a name and an email as
|
||||
mentioned in the git log entries."""
|
||||
def __init__(self, name, email):
|
||||
self.name = name
|
||||
self.email = email
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def __repr__(self):
|
||||
return '<git.Actor "%s <%s>">' % (self.name, self.email)
|
||||
|
||||
@classmethod
|
||||
def from_string(cls, string):
|
||||
"""
|
||||
Create an Actor from a string.
|
||||
|
||||
``str``
|
||||
is the string, which is expected to be in regular git format
|
||||
|
||||
Format
|
||||
John Doe <jdoe@example.com>
|
||||
|
||||
Returns
|
||||
Actor
|
||||
"""
|
||||
if re.search(r'<.+>', string):
|
||||
m = re.search(r'(.*) <(.+?)>', string)
|
||||
name, email = m.groups()
|
||||
return Actor(name, email)
|
||||
else:
|
||||
return Actor(string, None)
|
|
@ -0,0 +1,161 @@
|
|||
# blob.py
|
||||
# Copyright (C) 2008-2010 Michael Trier (mtrier@gmail.com) and contributors
|
||||
#
|
||||
# This module is part of GitPython and is released under
|
||||
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
|
||||
|
||||
import mimetypes
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
from actor import Actor
|
||||
from commit import Commit
|
||||
|
||||
class Blob(object):
|
||||
"""A Blob encapsulates a git blob object"""
|
||||
DEFAULT_MIME_TYPE = "text/plain"
|
||||
|
||||
def __init__(self, repo, id, mode=None, name=None):
|
||||
"""
|
||||
Create an unbaked Blob containing just the specified attributes
|
||||
|
||||
``repo``
|
||||
is the Repo
|
||||
|
||||
``id``
|
||||
is the git object id
|
||||
|
||||
``mode``
|
||||
is the file mode
|
||||
|
||||
``name``
|
||||
is the file name
|
||||
|
||||
Returns
|
||||
git.Blob
|
||||
"""
|
||||
self.repo = repo
|
||||
self.id = id
|
||||
self.mode = mode
|
||||
self.name = name
|
||||
|
||||
self._size = None
|
||||
self.data_stored = None
|
||||
|
||||
@property
|
||||
def size(self):
|
||||
"""
|
||||
The size of this blob in bytes
|
||||
|
||||
Returns
|
||||
int
|
||||
|
||||
NOTE
|
||||
The size will be cached after the first access
|
||||
"""
|
||||
if self._size is None:
|
||||
self._size = int(self.repo.git.cat_file(self.id, s=True).rstrip())
|
||||
return self._size
|
||||
|
||||
@property
|
||||
def data(self):
|
||||
"""
|
||||
The binary contents of this blob.
|
||||
|
||||
Returns
|
||||
str
|
||||
|
||||
NOTE
|
||||
The data will be cached after the first access.
|
||||
"""
|
||||
self.data_stored = self.data_stored or self.repo.git.cat_file(self.id, p=True, with_raw_output=True)
|
||||
return self.data_stored
|
||||
|
||||
@property
|
||||
def mime_type(self):
|
||||
"""
|
||||
The mime type of this file (based on the filename)
|
||||
|
||||
Returns
|
||||
str
|
||||
|
||||
NOTE
|
||||
Defaults to 'text/plain' in case the actual file type is unknown.
|
||||
"""
|
||||
guesses = None
|
||||
if self.name:
|
||||
guesses = mimetypes.guess_type(self.name)
|
||||
return guesses and guesses[0] or self.DEFAULT_MIME_TYPE
|
||||
|
||||
@property
|
||||
def basename(self):
|
||||
"""
|
||||
Returns
|
||||
The basename of the Blobs file name
|
||||
"""
|
||||
return os.path.basename(self.name)
|
||||
|
||||
@classmethod
|
||||
def blame(cls, repo, commit, file):
|
||||
"""
|
||||
The blame information for the given file at the given commit
|
||||
|
||||
Returns
|
||||
list: [git.Commit, list: [<line>]]
|
||||
A list of tuples associating a Commit object with a list of lines that
|
||||
changed within the given commit. The Commit objects will be given in order
|
||||
of appearance.
|
||||
"""
|
||||
data = repo.git.blame(commit, '--', file, p=True)
|
||||
commits = {}
|
||||
blames = []
|
||||
info = None
|
||||
|
||||
for line in data.splitlines():
|
||||
parts = re.split(r'\s+', line, 1)
|
||||
if re.search(r'^[0-9A-Fa-f]{40}$', parts[0]):
|
||||
if re.search(r'^([0-9A-Fa-f]{40}) (\d+) (\d+) (\d+)$', line):
|
||||
m = re.search(r'^([0-9A-Fa-f]{40}) (\d+) (\d+) (\d+)$', line)
|
||||
id, origin_line, final_line, group_lines = m.groups()
|
||||
info = {'id': id}
|
||||
blames.append([None, []])
|
||||
elif re.search(r'^([0-9A-Fa-f]{40}) (\d+) (\d+)$', line):
|
||||
m = re.search(r'^([0-9A-Fa-f]{40}) (\d+) (\d+)$', line)
|
||||
id, origin_line, final_line = m.groups()
|
||||
info = {'id': id}
|
||||
elif re.search(r'^(author|committer)', parts[0]):
|
||||
if re.search(r'^(.+)-mail$', parts[0]):
|
||||
m = re.search(r'^(.+)-mail$', parts[0])
|
||||
info["%s_email" % m.groups()[0]] = parts[-1]
|
||||
elif re.search(r'^(.+)-time$', parts[0]):
|
||||
m = re.search(r'^(.+)-time$', parts[0])
|
||||
info["%s_date" % m.groups()[0]] = time.gmtime(int(parts[-1]))
|
||||
elif re.search(r'^(author|committer)$', parts[0]):
|
||||
m = re.search(r'^(author|committer)$', parts[0])
|
||||
info[m.groups()[0]] = parts[-1]
|
||||
elif re.search(r'^filename', parts[0]):
|
||||
info['filename'] = parts[-1]
|
||||
elif re.search(r'^summary', parts[0]):
|
||||
info['summary'] = parts[-1]
|
||||
elif parts[0] == '':
|
||||
if info:
|
||||
c = commits.has_key(info['id']) and commits[info['id']]
|
||||
if not c:
|
||||
c = Commit(repo, id=info['id'],
|
||||
author=Actor.from_string(info['author'] + ' ' + info['author_email']),
|
||||
authored_date=info['author_date'],
|
||||
committer=Actor.from_string(info['committer'] + ' ' + info['committer_email']),
|
||||
committed_date=info['committer_date'],
|
||||
message=info['summary'])
|
||||
commits[info['id']] = c
|
||||
|
||||
m = re.search(r'^\t(.*)$', line)
|
||||
text, = m.groups()
|
||||
blames[-1][0] = c
|
||||
blames[-1][1].append( text )
|
||||
info = None
|
||||
|
||||
return blames
|
||||
|
||||
def __repr__(self):
|
||||
return '<git.Blob "%s">' % self.id
|
|
@ -0,0 +1,221 @@
|
|||
# cmd.py
|
||||
# Copyright (C) 2008-2010 Michael Trier (mtrier@gmail.com) and contributors
|
||||
#
|
||||
# This module is part of GitPython and is released under
|
||||
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
|
||||
|
||||
import os, sys
|
||||
import subprocess
|
||||
import re
|
||||
from utils import *
|
||||
from errors import GitCommandError
|
||||
|
||||
# Enables debugging of GitPython's git commands
|
||||
GIT_PYTHON_TRACE = os.environ.get("GIT_PYTHON_TRACE", False)
|
||||
|
||||
execute_kwargs = ('istream', 'with_keep_cwd', 'with_extended_output',
|
||||
'with_exceptions', 'with_raw_output')
|
||||
|
||||
extra = {}
|
||||
if sys.platform == 'win32':
|
||||
extra = {'shell': True}
|
||||
|
||||
class Git(object):
|
||||
"""
|
||||
The Git class manages communication with the Git binary.
|
||||
|
||||
It provides a convenient interface to calling the Git binary, such as in::
|
||||
|
||||
g = Git( git_dir )
|
||||
g.init() # calls 'git init' program
|
||||
rval = g.ls_files() # calls 'git ls-files' program
|
||||
|
||||
``Debugging``
|
||||
Set the GIT_PYTHON_TRACE environment variable print each invocation
|
||||
of the command to stdout.
|
||||
Set its value to 'full' to see details about the returned values.
|
||||
"""
|
||||
def __init__(self, git_dir=None):
|
||||
"""
|
||||
Initialize this instance with:
|
||||
|
||||
``git_dir``
|
||||
Git directory we should work in. If None, we always work in the current
|
||||
directory as returned by os.getcwd()
|
||||
"""
|
||||
super(Git, self).__init__()
|
||||
self.git_dir = git_dir
|
||||
|
||||
def __getattr__(self, name):
|
||||
"""
|
||||
A convenience method as it allows to call the command as if it was
|
||||
an object.
|
||||
Returns
|
||||
Callable object that will execute call _call_process with your arguments.
|
||||
"""
|
||||
if name[:1] == '_':
|
||||
raise AttributeError(name)
|
||||
return lambda *args, **kwargs: self._call_process(name, *args, **kwargs)
|
||||
|
||||
@property
|
||||
def get_dir(self):
|
||||
"""
|
||||
Returns
|
||||
Git directory we are working on
|
||||
"""
|
||||
return self.git_dir
|
||||
|
||||
def execute(self, command,
|
||||
istream=None,
|
||||
with_keep_cwd=False,
|
||||
with_extended_output=False,
|
||||
with_exceptions=True,
|
||||
with_raw_output=False,
|
||||
):
|
||||
"""
|
||||
Handles executing the command on the shell and consumes and returns
|
||||
the returned information (stdout)
|
||||
|
||||
``command``
|
||||
The command argument list to execute.
|
||||
It should be a string, or a sequence of program arguments. The
|
||||
program to execute is the first item in the args sequence or string.
|
||||
|
||||
``istream``
|
||||
Standard input filehandle passed to subprocess.Popen.
|
||||
|
||||
``with_keep_cwd``
|
||||
Whether to use the current working directory from os.getcwd().
|
||||
GitPython uses get_work_tree() as its working directory by
|
||||
default and get_git_dir() for bare repositories.
|
||||
|
||||
``with_extended_output``
|
||||
Whether to return a (status, stdout, stderr) tuple.
|
||||
|
||||
``with_exceptions``
|
||||
Whether to raise an exception when git returns a non-zero status.
|
||||
|
||||
``with_raw_output``
|
||||
Whether to avoid stripping off trailing whitespace.
|
||||
|
||||
Returns::
|
||||
|
||||
str(output) # extended_output = False (Default)
|
||||
tuple(int(status), str(stdout), str(stderr)) # extended_output = True
|
||||
|
||||
Raise
|
||||
GitCommandError
|
||||
|
||||
NOTE
|
||||
If you add additional keyword arguments to the signature of this method,
|
||||
you must update the execute_kwargs tuple housed in this module.
|
||||
"""
|
||||
if GIT_PYTHON_TRACE and not GIT_PYTHON_TRACE == 'full':
|
||||
print ' '.join(command)
|
||||
|
||||
# Allow the user to have the command executed in their working dir.
|
||||
if with_keep_cwd or self.git_dir is None:
|
||||
cwd = os.getcwd()
|
||||
else:
|
||||
cwd=self.git_dir
|
||||
|
||||
# Start the process
|
||||
proc = subprocess.Popen(command,
|
||||
cwd=cwd,
|
||||
stdin=istream,
|
||||
stderr=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
**extra
|
||||
)
|
||||
|
||||
# Wait for the process to return
|
||||
try:
|
||||
stdout_value = proc.stdout.read()
|
||||
stderr_value = proc.stderr.read()
|
||||
status = proc.wait()
|
||||
finally:
|
||||
proc.stdout.close()
|
||||
proc.stderr.close()
|
||||
|
||||
# Strip off trailing whitespace by default
|
||||
if not with_raw_output:
|
||||
stdout_value = stdout_value.rstrip()
|
||||
stderr_value = stderr_value.rstrip()
|
||||
|
||||
if with_exceptions and status != 0:
|
||||
raise GitCommandError(command, status, stderr_value)
|
||||
|
||||
if GIT_PYTHON_TRACE == 'full':
|
||||
if stderr_value:
|
||||
print "%s -> %d: '%s' !! '%s'" % (command, status, stdout_value, stderr_value)
|
||||
elif stdout_value:
|
||||
print "%s -> %d: '%s'" % (command, status, stdout_value)
|
||||
else:
|
||||
print "%s -> %d" % (command, status)
|
||||
|
||||
# Allow access to the command's status code
|
||||
if with_extended_output:
|
||||
return (status, stdout_value, stderr_value)
|
||||
else:
|
||||
return stdout_value
|
||||
|
||||
def transform_kwargs(self, **kwargs):
|
||||
"""
|
||||
Transforms Python style kwargs into git command line options.
|
||||
"""
|
||||
args = []
|
||||
for k, v in kwargs.items():
|
||||
if len(k) == 1:
|
||||
if v is True:
|
||||
args.append("-%s" % k)
|
||||
elif type(v) is not bool:
|
||||
args.append("-%s%s" % (k, v))
|
||||
else:
|
||||
if v is True:
|
||||
args.append("--%s" % dashify(k))
|
||||
elif type(v) is not bool:
|
||||
args.append("--%s=%s" % (dashify(k), v))
|
||||
return args
|
||||
|
||||
def _call_process(self, method, *args, **kwargs):
|
||||
"""
|
||||
Run the given git command with the specified arguments and return
|
||||
the result as a String
|
||||
|
||||
``method``
|
||||
is the command. Contained "_" characters will be converted to dashes,
|
||||
such as in 'ls_files' to call 'ls-files'.
|
||||
|
||||
``args``
|
||||
is the list of arguments
|
||||
|
||||
``kwargs``
|
||||
is a dict of keyword arguments.
|
||||
This function accepts the same optional keyword arguments
|
||||
as execute().
|
||||
|
||||
Examples::
|
||||
git.rev_list('master', max_count=10, header=True)
|
||||
|
||||
Returns
|
||||
Same as execute()
|
||||
"""
|
||||
|
||||
# Handle optional arguments prior to calling transform_kwargs
|
||||
# otherwise these'll end up in args, which is bad.
|
||||
_kwargs = {}
|
||||
for kwarg in execute_kwargs:
|
||||
try:
|
||||
_kwargs[kwarg] = kwargs.pop(kwarg)
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
# Prepare the argument list
|
||||
opt_args = self.transform_kwargs(**kwargs)
|
||||
ext_args = map(str, args)
|
||||
args = opt_args + ext_args
|
||||
|
||||
call = ["git", dashify(method)]
|
||||
call.extend(args)
|
||||
|
||||
return self.execute(call, **_kwargs)
|
|
@ -0,0 +1,289 @@
|
|||
# commit.py
|
||||
# Copyright (C) 2008-2010 Michael Trier (mtrier@gmail.com) and contributors
|
||||
#
|
||||
# This module is part of GitPython and is released under
|
||||
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
|
||||
|
||||
import re
|
||||
import time
|
||||
|
||||
from actor import Actor
|
||||
from lazy import LazyMixin
|
||||
from tree import Tree
|
||||
import diff
|
||||
import stats
|
||||
|
||||
class Commit(LazyMixin):
|
||||
"""
|
||||
Wraps a git Commit object.
|
||||
|
||||
This class will act lazily on some of its attributes and will query the
|
||||
value on demand only if it involves calling the git binary.
|
||||
"""
|
||||
def __init__(self, repo, id, tree=None, author=None, authored_date=None,
|
||||
committer=None, committed_date=None, message=None, parents=None):
|
||||
"""
|
||||
Instantiate a new Commit. All keyword arguments taking None as default will
|
||||
be implicitly set if id names a valid sha.
|
||||
|
||||
The parameter documentation indicates the type of the argument after a colon ':'.
|
||||
|
||||
``id``
|
||||
is the sha id of the commit
|
||||
|
||||
``parents`` : list( Commit, ... )
|
||||
is a list of commit ids
|
||||
|
||||
``tree`` : Tree
|
||||
is the corresponding tree id
|
||||
|
||||
``author`` : Actor
|
||||
is the author string ( will be implicitly converted into an Actor object )
|
||||
|
||||
``authored_date`` : (tm_year, tm_mon, tm_mday, tm_hour, tm_min, tm_sec, tm_wday, tm_yday, tm_isdst )
|
||||
is the authored DateTime
|
||||
|
||||
``committer`` : Actor
|
||||
is the committer string
|
||||
|
||||
``committed_date`` : (tm_year, tm_mon, tm_mday, tm_hour, tm_min, tm_sec, tm_wday, tm_yday, tm_isdst)
|
||||
is the committed DateTime
|
||||
|
||||
``message`` : string
|
||||
is the commit message
|
||||
|
||||
Returns
|
||||
git.Commit
|
||||
"""
|
||||
LazyMixin.__init__(self)
|
||||
|
||||
self.repo = repo
|
||||
self.id = id
|
||||
self.parents = None
|
||||
self.tree = None
|
||||
self.author = author
|
||||
self.authored_date = authored_date
|
||||
self.committer = committer
|
||||
self.committed_date = committed_date
|
||||
self.message = message
|
||||
|
||||
if self.id:
|
||||
if parents is not None:
|
||||
self.parents = [Commit(repo, p) for p in parents]
|
||||
if tree is not None:
|
||||
self.tree = Tree(repo, id=tree)
|
||||
|
||||
def __bake__(self):
|
||||
"""
|
||||
Called by LazyMixin superclass when the first uninitialized member needs
|
||||
to be set as it is queried.
|
||||
"""
|
||||
temp = Commit.find_all(self.repo, self.id, max_count=1)[0]
|
||||
self.parents = temp.parents
|
||||
self.tree = temp.tree
|
||||
self.author = temp.author
|
||||
self.authored_date = temp.authored_date
|
||||
self.committer = temp.committer
|
||||
self.committed_date = temp.committed_date
|
||||
self.message = temp.message
|
||||
|
||||
@property
|
||||
def id_abbrev(self):
|
||||
"""
|
||||
Returns
|
||||
First 7 bytes of the commit's sha id as an abbreviation of the full string.
|
||||
"""
|
||||
return self.id[0:7]
|
||||
|
||||
@property
|
||||
def summary(self):
|
||||
"""
|
||||
Returns
|
||||
First line of the commit message.
|
||||
"""
|
||||
return self.message.split('\n', 1)[0]
|
||||
|
||||
@classmethod
|
||||
def count(cls, repo, ref, path=''):
|
||||
"""
|
||||
Count the number of commits reachable from this ref
|
||||
|
||||
``repo``
|
||||
is the Repo
|
||||
|
||||
``ref``
|
||||
is the ref from which to begin (SHA1 or name)
|
||||
|
||||
``path``
|
||||
is an optional path
|
||||
|
||||
Returns
|
||||
int
|
||||
"""
|
||||
return len(repo.git.rev_list(ref, '--', path).strip().splitlines())
|
||||
|
||||
@classmethod
|
||||
def find_all(cls, repo, ref, path='', **kwargs):
|
||||
"""
|
||||
Find all commits matching the given criteria.
|
||||
``repo``
|
||||
is the Repo
|
||||
|
||||
``ref``
|
||||
is the ref from which to begin (SHA1 or name)
|
||||
|
||||
``path``
|
||||
is an optinal path, if set only Commits that include the path
|
||||
will be considered
|
||||
|
||||
``kwargs``
|
||||
optional keyword arguments to git where
|
||||
``max_count`` is the maximum number of commits to fetch
|
||||
``skip`` is the number of commits to skip
|
||||
|
||||
Returns
|
||||
git.Commit[]
|
||||
"""
|
||||
options = {'pretty': 'raw'}
|
||||
options.update(kwargs)
|
||||
|
||||
output = repo.git.rev_list(ref, '--', path, **options)
|
||||
return cls.list_from_string(repo, output)
|
||||
|
||||
@classmethod
|
||||
def list_from_string(cls, repo, text):
|
||||
"""
|
||||
Parse out commit information into a list of Commit objects
|
||||
|
||||
``repo``
|
||||
is the Repo
|
||||
|
||||
``text``
|
||||
is the text output from the git-rev-list command (raw format)
|
||||
|
||||
Returns
|
||||
git.Commit[]
|
||||
"""
|
||||
lines = [l for l in text.splitlines() if l.strip()]
|
||||
|
||||
commits = []
|
||||
|
||||
while lines:
|
||||
id = lines.pop(0).split()[1]
|
||||
tree = lines.pop(0).split()[1]
|
||||
|
||||
parents = []
|
||||
while lines and lines[0].startswith('parent'):
|
||||
parents.append(lines.pop(0).split()[-1])
|
||||
author, authored_date = cls.actor(lines.pop(0))
|
||||
committer, committed_date = cls.actor(lines.pop(0))
|
||||
|
||||
messages = []
|
||||
while lines and lines[0].startswith(' '):
|
||||
messages.append(lines.pop(0).strip())
|
||||
|
||||
message = '\n'.join(messages)
|
||||
|
||||
commits.append(Commit(repo, id=id, parents=parents, tree=tree, author=author, authored_date=authored_date,
|
||||
committer=committer, committed_date=committed_date, message=message))
|
||||
|
||||
return commits
|
||||
|
||||
@classmethod
|
||||
def diff(cls, repo, a, b=None, paths=None):
|
||||
"""
|
||||
Creates diffs between a tree and the index or between two trees:
|
||||
|
||||
``repo``
|
||||
is the Repo
|
||||
|
||||
``a``
|
||||
is a named commit
|
||||
|
||||
``b``
|
||||
is an optional named commit. Passing a list assumes you
|
||||
wish to omit the second named commit and limit the diff to the
|
||||
given paths.
|
||||
|
||||
``paths``
|
||||
is a list of paths to limit the diff to.
|
||||
|
||||
Returns
|
||||
git.Diff[]::
|
||||
|
||||
between tree and the index if only a is given
|
||||
between two trees if a and b are given and are commits
|
||||
"""
|
||||
paths = paths or []
|
||||
|
||||
if isinstance(b, list):
|
||||
paths = b
|
||||
b = None
|
||||
|
||||
if paths:
|
||||
paths.insert(0, "--")
|
||||
|
||||
if b:
|
||||
paths.insert(0, b)
|
||||
paths.insert(0, a)
|
||||
text = repo.git.diff('-M', full_index=True, *paths)
|
||||
return diff.Diff.list_from_string(repo, text)
|
||||
|
||||
@property
|
||||
def diffs(self):
|
||||
"""
|
||||
Returns
|
||||
git.Diff[]
|
||||
Diffs between this commit and its first parent or all changes if this
|
||||
commit is the first commit and has no parent.
|
||||
"""
|
||||
if not self.parents:
|
||||
d = self.repo.git.show(self.id, '-M', full_index=True, pretty='raw')
|
||||
if re.search(r'diff --git a', d):
|
||||
if not re.search(r'^diff --git a', d):
|
||||
p = re.compile(r'.+?(diff --git a)', re.MULTILINE | re.DOTALL)
|
||||
d = p.sub(r'diff --git a', d, 1)
|
||||
else:
|
||||
d = ''
|
||||
return diff.Diff.list_from_string(self.repo, d)
|
||||
else:
|
||||
return self.diff(self.repo, self.parents[0].id, self.id)
|
||||
|
||||
@property
|
||||
def stats(self):
|
||||
"""
|
||||
Create a git stat from changes between this commit and its first parent
|
||||
or from all changes done if this is the very first commit.
|
||||
|
||||
Return
|
||||
git.Stats
|
||||
"""
|
||||
if not self.parents:
|
||||
text = self.repo.git.diff_tree(self.id, '--', numstat=True, root=True)
|
||||
text2 = ""
|
||||
for line in text.splitlines()[1:]:
|
||||
(insertions, deletions, filename) = line.split("\t")
|
||||
text2 += "%s\t%s\t%s\n" % (insertions, deletions, filename)
|
||||
text = text2
|
||||
else:
|
||||
text = self.repo.git.diff(self.parents[0].id, self.id, '--', numstat=True)
|
||||
return stats.Stats.list_from_string(self.repo, text)
|
||||
|
||||
def __str__(self):
|
||||
""" Convert commit to string which is SHA1 """
|
||||
return self.id
|
||||
|
||||
def __repr__(self):
|
||||
return '<git.Commit "%s">' % self.id
|
||||
|
||||
@classmethod
|
||||
def actor(cls, line):
|
||||
"""
|
||||
Parse out the actor (author or committer) info
|
||||
|
||||
Returns
|
||||
[Actor, gmtime(acted at time)]
|
||||
"""
|
||||
m = re.search(r'^.+? (.*) (\d+) .*$', line)
|
||||
actor, epoch = m.groups()
|
||||
return [Actor.from_string(actor), time.gmtime(int(epoch))]
|
|
@ -0,0 +1,71 @@
|
|||
# diff.py
|
||||
# Copyright (C) 2008-2010 Michael Trier (mtrier@gmail.com) and contributors
|
||||
#
|
||||
# This module is part of GitPython and is released under
|
||||
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
|
||||
|
||||
import re
|
||||
import commit
|
||||
|
||||
class Diff(object):
|
||||
"""
|
||||
A Diff contains diff information between two commits.
|
||||
"""
|
||||
|
||||
def __init__(self, repo, a_path, b_path, a_commit, b_commit, a_mode,
|
||||
b_mode, new_file, deleted_file, rename_from,
|
||||
rename_to, diff):
|
||||
self.repo = repo
|
||||
self.a_path = a_path
|
||||
self.b_path = b_path
|
||||
|
||||
if not a_commit or re.search(r'^0{40}$', a_commit):
|
||||
self.a_commit = None
|
||||
else:
|
||||
self.a_commit = commit.Commit(repo, id=a_commit)
|
||||
if not b_commit or re.search(r'^0{40}$', b_commit):
|
||||
self.b_commit = None
|
||||
else:
|
||||
self.b_commit = commit.Commit(repo, id=b_commit)
|
||||
|
||||
self.a_mode = a_mode
|
||||
self.b_mode = b_mode
|
||||
self.new_file = new_file
|
||||
self.deleted_file = deleted_file
|
||||
self.rename_from = rename_from
|
||||
self.rename_to = rename_to
|
||||
self.renamed = rename_from != rename_to
|
||||
self.diff = diff
|
||||
|
||||
@classmethod
|
||||
def list_from_string(cls, repo, text):
|
||||
diffs = []
|
||||
|
||||
diff_header = re.compile(r"""
|
||||
#^diff[ ]--git
|
||||
[ ]a/(?P<a_path>\S+)[ ]b/(?P<b_path>\S+)\n
|
||||
(?:^similarity[ ]index[ ](?P<similarity_index>\d+)%\n
|
||||
^rename[ ]from[ ](?P<rename_from>\S+)\n
|
||||
^rename[ ]to[ ](?P<rename_to>\S+)(?:\n|$))?
|
||||
(?:^old[ ]mode[ ](?P<old_mode>\d+)\n
|
||||
^new[ ]mode[ ](?P<new_mode>\d+)(?:\n|$))?
|
||||
(?:^new[ ]file[ ]mode[ ](?P<new_file_mode>.+)(?:\n|$))?
|
||||
(?:^deleted[ ]file[ ]mode[ ](?P<deleted_file_mode>.+)(?:\n|$))?
|
||||
(?:^index[ ](?P<a_commit>[0-9A-Fa-f]+)
|
||||
\.\.(?P<b_commit>[0-9A-Fa-f]+)[ ]?(?P<b_mode>.+)?(?:\n|$))?
|
||||
""", re.VERBOSE | re.MULTILINE).match
|
||||
|
||||
for diff in ('\n' + text).split('\ndiff --git')[1:]:
|
||||
header = diff_header(diff)
|
||||
|
||||
a_path, b_path, similarity_index, rename_from, rename_to, \
|
||||
old_mode, new_mode, new_file_mode, deleted_file_mode, \
|
||||
a_commit, b_commit, b_mode = header.groups()
|
||||
new_file, deleted_file = bool(new_file_mode), bool(deleted_file_mode)
|
||||
|
||||
diffs.append(Diff(repo, a_path, b_path, a_commit, b_commit,
|
||||
old_mode or deleted_file_mode, new_mode or new_file_mode or b_mode,
|
||||
new_file, deleted_file, rename_from, rename_to, diff[header.end():]))
|
||||
|
||||
return diffs
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
# errors.py
|
||||
# Copyright (C) 2008-2010 Michael Trier (mtrier@gmail.com) and contributors
|
||||
#
|
||||
# This module is part of GitPython and is released under
|
||||
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
|
||||
"""
|
||||
Module containing all exceptions thrown througout the git package,
|
||||
"""
|
||||
|
||||
class InvalidGitRepositoryError(Exception):
|
||||
"""
|
||||
Thrown if the given repository appears to have an invalid format.
|
||||
"""
|
||||
|
||||
class NoSuchPathError(Exception):
|
||||
"""
|
||||
Thrown if a path could not be access by the system.
|
||||
"""
|
||||
|
||||
class GitCommandError(Exception):
|
||||
"""
|
||||
Thrown if execution of the git command fails with non-zero status code.
|
||||
"""
|
||||
def __init__(self, command, status, stderr=None):
|
||||
self.stderr = stderr
|
||||
self.status = status
|
||||
self.command = command
|
||||
|
||||
def __str__(self):
|
||||
return repr("%s returned exit status %d" %
|
||||
(str(self.command), self.status))
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
# head.py
|
||||
# Copyright (C) 2008-2010 Michael Trier (mtrier@gmail.com) and contributors
|
||||
#
|
||||
# This module is part of GitPython and is released under
|
||||
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
|
||||
|
||||
import commit
|
||||
|
||||
class Head(object):
|
||||
"""
|
||||
A Head is a named reference to a Commit. Every Head instance contains a name
|
||||
and a Commit object.
|
||||
|
||||
Examples::
|
||||
|
||||
>>> repo = Repo("/path/to/repo")
|
||||
>>> head = repo.heads[0]
|
||||
|
||||
>>> head.name
|
||||
'master'
|
||||
|
||||
>>> head.commit
|
||||
<git.Commit "1c09f116cbc2cb4100fb6935bb162daa4723f455">
|
||||
|
||||
>>> head.commit.id
|
||||
'1c09f116cbc2cb4100fb6935bb162daa4723f455'
|
||||
"""
|
||||
|
||||
def __init__(self, name, commit):
|
||||
"""
|
||||
Initialize a newly instanced Head
|
||||
|
||||
`name`
|
||||
is the name of the head
|
||||
|
||||
`commit`
|
||||
is the Commit object that the head points to
|
||||
"""
|
||||
self.name = name
|
||||
self.commit = commit
|
||||
|
||||
@classmethod
|
||||
def find_all(cls, repo, **kwargs):
|
||||
"""
|
||||
Find all Heads in the repository
|
||||
|
||||
`repo`
|
||||
is the Repo
|
||||
|
||||
`kwargs`
|
||||
Additional options given as keyword arguments, will be passed
|
||||
to git-for-each-ref
|
||||
|
||||
Returns
|
||||
git.Head[]
|
||||
|
||||
List is sorted by committerdate
|
||||
"""
|
||||
|
||||
options = {'sort': "committerdate",
|
||||
'format': "%(refname)%00%(objectname)"}
|
||||
options.update(kwargs)
|
||||
|
||||
output = repo.git.for_each_ref("refs/heads", **options)
|
||||
return cls.list_from_string(repo, output)
|
||||
|
||||
@classmethod
|
||||
def list_from_string(cls, repo, text):
|
||||
"""
|
||||
Parse out head information into a list of head objects
|
||||
|
||||
``repo``
|
||||
is the Repo
|
||||
``text``
|
||||
is the text output from the git-for-each-ref command
|
||||
|
||||
Returns
|
||||
git.Head[]
|
||||
"""
|
||||
heads = []
|
||||
|
||||
for line in text.splitlines():
|
||||
heads.append(cls.from_string(repo, line))
|
||||
|
||||
return heads
|
||||
|
||||
@classmethod
|
||||
def from_string(cls, repo, line):
|
||||
"""
|
||||
Create a new Head instance from the given string.
|
||||
|
||||
``repo``
|
||||
is the Repo
|
||||
|
||||
``line``
|
||||
is the formatted head information
|
||||
|
||||
Format::
|
||||
|
||||
name: [a-zA-Z_/]+
|
||||
<null byte>
|
||||
id: [0-9A-Fa-f]{40}
|
||||
|
||||
Returns
|
||||
git.Head
|
||||
"""
|
||||
full_name, ids = line.split("\x00")
|
||||
|
||||
if full_name.startswith('refs/heads/'):
|
||||
name = full_name[len('refs/heads/'):]
|
||||
else:
|
||||
name = full_name
|
||||
|
||||
c = commit.Commit(repo, id=ids)
|
||||
return Head(name, c)
|
||||
|
||||
def __repr__(self):
|
||||
return '<git.Head "%s">' % self.name
|
|
@ -0,0 +1,32 @@
|
|||
# lazy.py
|
||||
# Copyright (C) 2008-2010 Michael Trier (mtrier@gmail.com) and contributors
|
||||
#
|
||||
# This module is part of GitPython and is released under
|
||||
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
|
||||
|
||||
class LazyMixin(object):
|
||||
lazy_properties = []
|
||||
|
||||
def __init__(self):
|
||||
self.__baked__ = False
|
||||
|
||||
def __getattribute__(self, attr):
|
||||
val = object.__getattribute__(self, attr)
|
||||
if val is not None:
|
||||
return val
|
||||
else:
|
||||
self.__prebake__()
|
||||
return object.__getattribute__(self, attr)
|
||||
|
||||
def __bake__(self):
|
||||
""" This method should be overridden in the derived class. """
|
||||
raise NotImplementedError(" '__bake__' method has not been implemented.")
|
||||
|
||||
def __prebake__(self):
|
||||
if self.__baked__:
|
||||
return
|
||||
self.__bake__()
|
||||
self.__baked__ = True
|
||||
|
||||
def __bake_it__(self):
|
||||
self.__baked__ = True
|
|
@ -0,0 +1,517 @@
|
|||
# repo.py
|
||||
# Copyright (C) 2008-2010 Michael Trier (mtrier@gmail.com) and contributors
|
||||
#
|
||||
# This module is part of GitPython and is released under
|
||||
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
|
||||
|
||||
import os
|
||||
import re
|
||||
import gzip
|
||||
import StringIO
|
||||
from errors import InvalidGitRepositoryError, NoSuchPathError
|
||||
from utils import touch, is_git_dir
|
||||
from cmd import Git
|
||||
from head import Head
|
||||
from blob import Blob
|
||||
from tag import Tag
|
||||
from commit import Commit
|
||||
from tree import Tree
|
||||
|
||||
class Repo(object):
|
||||
"""
|
||||
Represents a git repository and allows you to query references,
|
||||
gather commit information, generate diffs, create and clone repositories query
|
||||
the log.
|
||||
"""
|
||||
DAEMON_EXPORT_FILE = 'git-daemon-export-ok'
|
||||
|
||||
def __init__(self, path=None):
|
||||
"""
|
||||
Create a new Repo instance
|
||||
|
||||
``path``
|
||||
is the path to either the root git directory or the bare git repo
|
||||
|
||||
Examples::
|
||||
|
||||
repo = Repo("/Users/mtrier/Development/git-python")
|
||||
repo = Repo("/Users/mtrier/Development/git-python.git")
|
||||
|
||||
Raises
|
||||
InvalidGitRepositoryError or NoSuchPathError
|
||||
|
||||
Returns
|
||||
``git.Repo``
|
||||
"""
|
||||
|
||||
epath = os.path.abspath(os.path.expanduser(path or os.getcwd()))
|
||||
|
||||
if not os.path.exists(epath):
|
||||
raise NoSuchPathError(epath)
|
||||
|
||||
self.path = None
|
||||
curpath = epath
|
||||
while curpath:
|
||||
if is_git_dir(curpath):
|
||||
self.bare = True
|
||||
self.path = curpath
|
||||
self.wd = curpath
|
||||
break
|
||||
gitpath = os.path.join(curpath, '.git')
|
||||
if is_git_dir(gitpath):
|
||||
self.bare = False
|
||||
self.path = gitpath
|
||||
self.wd = curpath
|
||||
break
|
||||
curpath, dummy = os.path.split(curpath)
|
||||
if not dummy:
|
||||
break
|
||||
|
||||
if self.path is None:
|
||||
raise InvalidGitRepositoryError(epath)
|
||||
|
||||
self.git = Git(self.wd)
|
||||
|
||||
# Description property
|
||||
def _get_description(self):
|
||||
filename = os.path.join(self.path, 'description')
|
||||
return file(filename).read().rstrip()
|
||||
|
||||
def _set_description(self, descr):
|
||||
filename = os.path.join(self.path, 'description')
|
||||
file(filename, 'w').write(descr+'\n')
|
||||
|
||||
description = property(_get_description, _set_description,
|
||||
doc="the project's description")
|
||||
del _get_description
|
||||
del _set_description
|
||||
|
||||
@property
|
||||
def heads(self):
|
||||
"""
|
||||
A list of ``Head`` objects representing the branch heads in
|
||||
this repo
|
||||
|
||||
Returns
|
||||
``git.Head[]``
|
||||
"""
|
||||
return Head.find_all(self)
|
||||
|
||||
# alias heads
|
||||
branches = heads
|
||||
|
||||
@property
|
||||
def tags(self):
|
||||
"""
|
||||
A list of ``Tag`` objects that are available in this repo
|
||||
|
||||
Returns
|
||||
``git.Tag[]``
|
||||
"""
|
||||
return Tag.find_all(self)
|
||||
|
||||
def commits(self, start='master', path='', max_count=10, skip=0):
|
||||
"""
|
||||
A list of Commit objects representing the history of a given ref/commit
|
||||
|
||||
``start``
|
||||
is the branch/commit name (default 'master')
|
||||
|
||||
``path``
|
||||
is an optional path to limit the returned commits to
|
||||
Commits that do not contain that path will not be returned.
|
||||
|
||||
``max_count``
|
||||
is the maximum number of commits to return (default 10)
|
||||
|
||||
``skip``
|
||||
is the number of commits to skip (default 0) which will effectively
|
||||
move your commit-window by the given number.
|
||||
|
||||
Returns
|
||||
``git.Commit[]``
|
||||
"""
|
||||
options = {'max_count': max_count,
|
||||
'skip': skip}
|
||||
|
||||
return Commit.find_all(self, start, path, **options)
|
||||
|
||||
def commits_between(self, frm, to):
|
||||
"""
|
||||
The Commits objects that are reachable via ``to`` but not via ``frm``
|
||||
Commits are returned in chronological order.
|
||||
|
||||
``from``
|
||||
is the branch/commit name of the younger item
|
||||
|
||||
``to``
|
||||
is the branch/commit name of the older item
|
||||
|
||||
Returns
|
||||
``git.Commit[]``
|
||||
"""
|
||||
return reversed(Commit.find_all(self, "%s..%s" % (frm, to)))
|
||||
|
||||
def commits_since(self, start='master', path='', since='1970-01-01'):
|
||||
"""
|
||||
The Commits objects that are newer than the specified date.
|
||||
Commits are returned in chronological order.
|
||||
|
||||
``start``
|
||||
is the branch/commit name (default 'master')
|
||||
|
||||
``path``
|
||||
is an optional path to limit the returned commits to.
|
||||
|
||||
|
||||
``since``
|
||||
is a string representing a date/time
|
||||
|
||||
Returns
|
||||
``git.Commit[]``
|
||||
"""
|
||||
options = {'since': since}
|
||||
|
||||
return Commit.find_all(self, start, path, **options)
|
||||
|
||||
def commit_count(self, start='master', path=''):
|
||||
"""
|
||||
The number of commits reachable by the given branch/commit
|
||||
|
||||
``start``
|
||||
is the branch/commit name (default 'master')
|
||||
|
||||
``path``
|
||||
is an optional path
|
||||
Commits that do not contain the path will not contribute to the count.
|
||||
|
||||
Returns
|
||||
``int``
|
||||
"""
|
||||
return Commit.count(self, start, path)
|
||||
|
||||
def commit(self, id, path = ''):
|
||||
"""
|
||||
The Commit object for the specified id
|
||||
|
||||
``id``
|
||||
is the SHA1 identifier of the commit
|
||||
|
||||
``path``
|
||||
is an optional path, if set the returned commit must contain the path.
|
||||
|
||||
Returns
|
||||
``git.Commit``
|
||||
"""
|
||||
options = {'max_count': 1}
|
||||
|
||||
commits = Commit.find_all(self, id, path, **options)
|
||||
|
||||
if not commits:
|
||||
raise ValueError, "Invalid identifier %s, or given path '%s' too restrictive" % ( id, path )
|
||||
return commits[0]
|
||||
|
||||
def commit_deltas_from(self, other_repo, ref='master', other_ref='master'):
|
||||
"""
|
||||
Returns a list of commits that is in ``other_repo`` but not in self
|
||||
|
||||
Returns
|
||||
git.Commit[]
|
||||
"""
|
||||
repo_refs = self.git.rev_list(ref, '--').strip().splitlines()
|
||||
other_repo_refs = other_repo.git.rev_list(other_ref, '--').strip().splitlines()
|
||||
|
||||
diff_refs = list(set(other_repo_refs) - set(repo_refs))
|
||||
return map(lambda ref: Commit.find_all(other_repo, ref, max_count=1)[0], diff_refs)
|
||||
|
||||
def tree(self, treeish='master'):
|
||||
"""
|
||||
The Tree object for the given treeish reference
|
||||
|
||||
``treeish``
|
||||
is the reference (default 'master')
|
||||
|
||||
Examples::
|
||||
|
||||
repo.tree('master')
|
||||
|
||||
|
||||
Returns
|
||||
``git.Tree``
|
||||
"""
|
||||
return Tree(self, id=treeish)
|
||||
|
||||
def blob(self, id):
|
||||
"""
|
||||
The Blob object for the given id
|
||||
|
||||
``id``
|
||||
is the SHA1 id of the blob
|
||||
|
||||
Returns
|
||||
``git.Blob``
|
||||
"""
|
||||
return Blob(self, id=id)
|
||||
|
||||
def log(self, commit='master', path=None, **kwargs):
|
||||
"""
|
||||
The Commit for a treeish, and all commits leading to it.
|
||||
|
||||
``kwargs``
|
||||
keyword arguments specifying flags to be used in git-log command,
|
||||
i.e.: max_count=1 to limit the amount of commits returned
|
||||
|
||||
Returns
|
||||
``git.Commit[]``
|
||||
"""
|
||||
options = {'pretty': 'raw'}
|
||||
options.update(kwargs)
|
||||
arg = [commit, '--']
|
||||
if path:
|
||||
arg.append(path)
|
||||
commits = self.git.log(*arg, **options)
|
||||
return Commit.list_from_string(self, commits)
|
||||
|
||||
def diff(self, a, b, *paths):
|
||||
"""
|
||||
The diff from commit ``a`` to commit ``b``, optionally restricted to the given file(s)
|
||||
|
||||
``a``
|
||||
is the base commit
|
||||
``b``
|
||||
is the other commit
|
||||
|
||||
``paths``
|
||||
is an optional list of file paths on which to restrict the diff
|
||||
|
||||
Returns
|
||||
``str``
|
||||
"""
|
||||
return self.git.diff(a, b, '--', *paths)
|
||||
|
||||
def commit_diff(self, commit):
|
||||
"""
|
||||
The commit diff for the given commit
|
||||
``commit`` is the commit name/id
|
||||
|
||||
Returns
|
||||
``git.Diff[]``
|
||||
"""
|
||||
return Commit.diff(self, commit)
|
||||
|
||||
@classmethod
|
||||
def init_bare(self, path, mkdir=True, **kwargs):
|
||||
"""
|
||||
Initialize a bare git repository at the given path
|
||||
|
||||
``path``
|
||||
is the full path to the repo (traditionally ends with /<name>.git)
|
||||
|
||||
``mkdir``
|
||||
if specified will create the repository directory if it doesn't
|
||||
already exists. Creates the directory with a mode=0755.
|
||||
|
||||
``kwargs``
|
||||
keyword arguments serving as additional options to the git init command
|
||||
|
||||
Examples::
|
||||
|
||||
git.Repo.init_bare('/var/git/myrepo.git')
|
||||
|
||||
Returns
|
||||
``git.Repo`` (the newly created repo)
|
||||
"""
|
||||
|
||||
if mkdir and not os.path.exists(path):
|
||||
os.makedirs(path, 0755)
|
||||
|
||||
git = Git(path)
|
||||
output = git.init('--bare', **kwargs)
|
||||
return Repo(path)
|
||||
create = init_bare
|
||||
|
||||
def fork_bare(self, path, **kwargs):
|
||||
"""
|
||||
Fork a bare git repository from this repo
|
||||
|
||||
``path``
|
||||
is the full path of the new repo (traditionally ends with /<name>.git)
|
||||
|
||||
``kwargs``
|
||||
keyword arguments to be given to the git clone command
|
||||
|
||||
Returns
|
||||
``git.Repo`` (the newly forked repo)
|
||||
"""
|
||||
options = {'bare': True}
|
||||
options.update(kwargs)
|
||||
self.git.clone(self.path, path, **options)
|
||||
return Repo(path)
|
||||
|
||||
def archive_tar(self, treeish='master', prefix=None):
|
||||
"""
|
||||
Archive the given treeish
|
||||
|
||||
``treeish``
|
||||
is the treeish name/id (default 'master')
|
||||
|
||||
``prefix``
|
||||
is the optional prefix to prepend to each filename in the archive
|
||||
|
||||
Examples::
|
||||
|
||||
>>> repo.archive_tar
|
||||
<String containing tar archive>
|
||||
|
||||
>>> repo.archive_tar('a87ff14')
|
||||
<String containing tar archive for commit a87ff14>
|
||||
|
||||
>>> repo.archive_tar('master', 'myproject/')
|
||||
<String containing tar bytes archive, whose files are prefixed with 'myproject/'>
|
||||
|
||||
Returns
|
||||
str (containing bytes of tar archive)
|
||||
"""
|
||||
options = {}
|
||||
if prefix:
|
||||
options['prefix'] = prefix
|
||||
return self.git.archive(treeish, **options)
|
||||
|
||||
def archive_tar_gz(self, treeish='master', prefix=None):
|
||||
"""
|
||||
Archive and gzip the given treeish
|
||||
|
||||
``treeish``
|
||||
is the treeish name/id (default 'master')
|
||||
|
||||
``prefix``
|
||||
is the optional prefix to prepend to each filename in the archive
|
||||
|
||||
Examples::
|
||||
|
||||
>>> repo.archive_tar_gz
|
||||
<String containing tar.gz archive>
|
||||
|
||||
>>> repo.archive_tar_gz('a87ff14')
|
||||
<String containing tar.gz archive for commit a87ff14>
|
||||
|
||||
>>> repo.archive_tar_gz('master', 'myproject/')
|
||||
<String containing tar.gz archive and prefixed with 'myproject/'>
|
||||
|
||||
Returns
|
||||
str (containing the bytes of tar.gz archive)
|
||||
"""
|
||||
kwargs = {}
|
||||
if prefix:
|
||||
kwargs['prefix'] = prefix
|
||||
resultstr = self.git.archive(treeish, **kwargs)
|
||||
sio = StringIO.StringIO()
|
||||
gf = gzip.GzipFile(fileobj=sio, mode ='wb')
|
||||
gf.write(resultstr)
|
||||
gf.close()
|
||||
return sio.getvalue()
|
||||
|
||||
def _get_daemon_export(self):
|
||||
filename = os.path.join(self.path, self.DAEMON_EXPORT_FILE)
|
||||
return os.path.exists(filename)
|
||||
|
||||
def _set_daemon_export(self, value):
|
||||
filename = os.path.join(self.path, self.DAEMON_EXPORT_FILE)
|
||||
fileexists = os.path.exists(filename)
|
||||
if value and not fileexists:
|
||||
touch(filename)
|
||||
elif not value and fileexists:
|
||||
os.unlink(filename)
|
||||
|
||||
daemon_export = property(_get_daemon_export, _set_daemon_export,
|
||||
doc="If True, git-daemon may export this repository")
|
||||
del _get_daemon_export
|
||||
del _set_daemon_export
|
||||
|
||||
def _get_alternates(self):
|
||||
"""
|
||||
The list of alternates for this repo from which objects can be retrieved
|
||||
|
||||
Returns
|
||||
list of strings being pathnames of alternates
|
||||
"""
|
||||
alternates_path = os.path.join(self.path, 'objects', 'info', 'alternates')
|
||||
|
||||
if os.path.exists(alternates_path):
|
||||
try:
|
||||
f = open(alternates_path)
|
||||
alts = f.read()
|
||||
finally:
|
||||
f.close()
|
||||
return alts.strip().splitlines()
|
||||
else:
|
||||
return []
|
||||
|
||||
def _set_alternates(self, alts):
|
||||
"""
|
||||
Sets the alternates
|
||||
|
||||
``alts``
|
||||
is the array of string paths representing the alternates at which
|
||||
git should look for objects, i.e. /home/user/repo/.git/objects
|
||||
|
||||
Raises
|
||||
NoSuchPathError
|
||||
|
||||
Note
|
||||
The method does not check for the existance of the paths in alts
|
||||
as the caller is responsible.
|
||||
|
||||
Returns
|
||||
None
|
||||
"""
|
||||
alternates_path = os.path.join(self.path, 'objects', 'info', 'alternates')
|
||||
if not alts:
|
||||
if os.path.isfile(alternates_path):
|
||||
os.remove(alternates_path)
|
||||
else:
|
||||
try:
|
||||
f = open(alternates_path, 'w')
|
||||
f.write("\n".join(alts))
|
||||
finally:
|
||||
f.close()
|
||||
# END file handling
|
||||
# END alts handling
|
||||
|
||||
alternates = property(_get_alternates, _set_alternates, doc="Retrieve a list of alternates paths or set a list paths to be used as alternates")
|
||||
|
||||
@property
|
||||
def is_dirty(self):
|
||||
"""
|
||||
Return the status of the index.
|
||||
|
||||
Returns
|
||||
``True``, if the index has any uncommitted changes,
|
||||
otherwise ``False``
|
||||
|
||||
NOTE
|
||||
Working tree changes that have not been staged will not be detected !
|
||||
"""
|
||||
if self.bare:
|
||||
# Bare repositories with no associated working directory are
|
||||
# always considered to be clean.
|
||||
return False
|
||||
|
||||
return len(self.git.diff('HEAD', '--').strip()) > 0
|
||||
|
||||
@property
|
||||
def active_branch(self):
|
||||
"""
|
||||
The name of the currently active branch.
|
||||
|
||||
Returns
|
||||
str (the branch name)
|
||||
"""
|
||||
branch = self.git.symbolic_ref('HEAD').strip()
|
||||
if branch.startswith('refs/heads/'):
|
||||
branch = branch[len('refs/heads/'):]
|
||||
|
||||
return branch
|
||||
|
||||
def __repr__(self):
|
||||
return '<git.Repo "%s">' % self.path
|
|
@ -0,0 +1,59 @@
|
|||
# stats.py
|
||||
# Copyright (C) 2008-2010 Michael Trier (mtrier@gmail.com) and contributors
|
||||
#
|
||||
# This module is part of GitPython and is released under
|
||||
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
|
||||
|
||||
class Stats(object):
|
||||
"""
|
||||
Represents stat information as presented by git at the end of a merge. It is
|
||||
created from the output of a diff operation.
|
||||
|
||||
``Example``::
|
||||
|
||||
c = Commit( sha1 )
|
||||
s = c.stats
|
||||
s.total # full-stat-dict
|
||||
s.files # dict( filepath : stat-dict )
|
||||
|
||||
``stat-dict``
|
||||
|
||||
A dictionary with the following keys and values::
|
||||
|
||||
deletions = number of deleted lines as int
|
||||
insertions = number of inserted lines as int
|
||||
lines = total number of lines changed as int, or deletions + insertions
|
||||
|
||||
``full-stat-dict``
|
||||
|
||||
In addition to the items in the stat-dict, it features additional information::
|
||||
|
||||
files = number of changed files as int
|
||||
|
||||
"""
|
||||
def __init__(self, repo, total, files):
|
||||
self.repo = repo
|
||||
self.total = total
|
||||
self.files = files
|
||||
|
||||
@classmethod
|
||||
def list_from_string(cls, repo, text):
|
||||
"""
|
||||
Create a Stat object from output retrieved by git-diff.
|
||||
|
||||
Returns
|
||||
git.Stat
|
||||
"""
|
||||
hsh = {'total': {'insertions': 0, 'deletions': 0, 'lines': 0, 'files': 0}, 'files': {}}
|
||||
for line in text.splitlines():
|
||||
(raw_insertions, raw_deletions, filename) = line.split("\t")
|
||||
insertions = raw_insertions != '-' and int(raw_insertions) or 0
|
||||
deletions = raw_deletions != '-' and int(raw_deletions) or 0
|
||||
hsh['total']['insertions'] += insertions
|
||||
hsh['total']['deletions'] += deletions
|
||||
hsh['total']['lines'] += insertions + deletions
|
||||
hsh['total']['files'] += 1
|
||||
hsh['files'][filename.strip()] = {'insertions': insertions,
|
||||
'deletions': deletions,
|
||||
'lines': insertions + deletions}
|
||||
return Stats(repo, hsh['total'], hsh['files'])
|
|
@ -0,0 +1,92 @@
|
|||
# tag.py
|
||||
# Copyright (C) 2008-2010 Michael Trier (mtrier@gmail.com) and contributors
|
||||
#
|
||||
# This module is part of GitPython and is released under
|
||||
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
|
||||
|
||||
from commit import Commit
|
||||
|
||||
class Tag(object):
|
||||
def __init__(self, name, commit):
|
||||
"""
|
||||
Initialize a newly instantiated Tag
|
||||
|
||||
``name``
|
||||
is the name of the head
|
||||
|
||||
``commit``
|
||||
is the Commit that the head points to
|
||||
"""
|
||||
self.name = name
|
||||
self.commit = commit
|
||||
|
||||
@classmethod
|
||||
def find_all(cls, repo, **kwargs):
|
||||
"""
|
||||
Find all Tags in the repository
|
||||
|
||||
``repo``
|
||||
is the Repo
|
||||
|
||||
``kwargs``
|
||||
Additional options given as keyword arguments, will be passed
|
||||
to git-for-each-ref
|
||||
|
||||
Returns
|
||||
``git.Tag[]``
|
||||
|
||||
List is sorted by committerdate
|
||||
"""
|
||||
options = {'sort': "committerdate",
|
||||
'format': "%(refname)%00%(objectname)"}
|
||||
options.update(**kwargs)
|
||||
|
||||
output = repo.git.for_each_ref("refs/tags", **options)
|
||||
return cls.list_from_string(repo, output)
|
||||
|
||||
@classmethod
|
||||
def list_from_string(cls, repo, text):
|
||||
"""
|
||||
Parse out tag information into an array of Tag objects
|
||||
|
||||
``repo``
|
||||
is the Repo
|
||||
|
||||
``text``
|
||||
is the text output from the git-for-each command
|
||||
|
||||
Returns
|
||||
git.Tag[]
|
||||
"""
|
||||
tags = []
|
||||
for line in text.splitlines():
|
||||
tags.append(cls.from_string(repo, line))
|
||||
return tags
|
||||
|
||||
@classmethod
|
||||
def from_string(cls, repo, line):
|
||||
"""
|
||||
Create a new Tag instance from the given string.
|
||||
|
||||
``repo``
|
||||
is the Repo
|
||||
|
||||
``line``
|
||||
is the formatted tag information
|
||||
|
||||
Format::
|
||||
|
||||
name: [a-zA-Z_/]+
|
||||
<null byte>
|
||||
id: [0-9A-Fa-f]{40}
|
||||
|
||||
Returns
|
||||
git.Tag
|
||||
"""
|
||||
full_name, ids = line.split("\x00")
|
||||
name = full_name.split("/")[-1]
|
||||
commit = Commit(repo, id=ids)
|
||||
return Tag(name, commit)
|
||||
|
||||
def __repr__(self):
|
||||
return '<git.Tag "%s">' % self.name
|
|
@ -0,0 +1,108 @@
|
|||
# tree.py
|
||||
# Copyright (C) 2008-2010 Michael Trier (mtrier@gmail.com) and contributors
|
||||
#
|
||||
# This module is part of GitPython and is released under
|
||||
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
|
||||
|
||||
import os
|
||||
from lazy import LazyMixin
|
||||
import blob
|
||||
|
||||
class Tree(LazyMixin):
|
||||
def __init__(self, repo, id, mode=None, name=None):
|
||||
LazyMixin.__init__(self)
|
||||
self.repo = repo
|
||||
self.id = id
|
||||
self.mode = mode
|
||||
self.name = name
|
||||
self._contents = None
|
||||
|
||||
def __bake__(self):
|
||||
# Ensure the treeish references directly a tree
|
||||
treeish = self.id
|
||||
if not treeish.endswith(':'):
|
||||
treeish = treeish + ':'
|
||||
|
||||
# Read the tree contents.
|
||||
self._contents = {}
|
||||
for line in self.repo.git.ls_tree(self.id).splitlines():
|
||||
obj = self.content_from_string(self.repo, line)
|
||||
if obj is not None:
|
||||
self._contents[obj.name] = obj
|
||||
|
||||
@staticmethod
|
||||
def content_from_string(repo, text):
|
||||
"""
|
||||
Parse a content item and create the appropriate object
|
||||
|
||||
``repo``
|
||||
is the Repo
|
||||
|
||||
``text``
|
||||
is the single line containing the items data in `git ls-tree` format
|
||||
|
||||
Returns
|
||||
``git.Blob`` or ``git.Tree``
|
||||
"""
|
||||
try:
|
||||
mode, typ, id, name = text.expandtabs(1).split(" ", 3)
|
||||
except:
|
||||
return None
|
||||
|
||||
if typ == "tree":
|
||||
return Tree(repo, id=id, mode=mode, name=name)
|
||||
elif typ == "blob":
|
||||
return blob.Blob(repo, id=id, mode=mode, name=name)
|
||||
elif typ == "commit":
|
||||
return None
|
||||
else:
|
||||
raise(TypeError, "Invalid type: %s" % typ)
|
||||
|
||||
def __div__(self, file):
|
||||
"""
|
||||
Find the named object in this tree's contents
|
||||
|
||||
Examples::
|
||||
|
||||
>>> Repo('/path/to/python-git').tree()/'lib'
|
||||
<git.Tree "6cc23ee138be09ff8c28b07162720018b244e95e">
|
||||
>>> Repo('/path/to/python-git').tree()/'README'
|
||||
<git.Blob "8b1e02c0fb554eed2ce2ef737a68bb369d7527df">
|
||||
|
||||
Returns
|
||||
``git.Blob`` or ``git.Tree`` or ``None`` if not found
|
||||
"""
|
||||
return self.get(file)
|
||||
|
||||
@property
|
||||
def basename(self):
|
||||
os.path.basename(self.name)
|
||||
|
||||
def __repr__(self):
|
||||
return '<git.Tree "%s">' % self.id
|
||||
|
||||
# Implement the basics of the dict protocol:
|
||||
# directories/trees can be seen as object dicts.
|
||||
def __getitem__(self, key):
|
||||
return self._contents[key]
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self._contents)
|
||||
|
||||
def __len__(self):
|
||||
return len(self._contents)
|
||||
|
||||
def __contains__(self, key):
|
||||
return key in self._contents
|
||||
|
||||
def get(self, key):
|
||||
return self._contents.get(key)
|
||||
|
||||
def items(self):
|
||||
return self._contents.items()
|
||||
|
||||
def keys(self):
|
||||
return self._contents.keys()
|
||||
|
||||
def values(self):
|
||||
return self._contents.values()
|
|
@ -0,0 +1,27 @@
|
|||
# utils.py
|
||||
# Copyright (C) 2008-2010 Michael Trier (mtrier@gmail.com) and contributors
|
||||
#
|
||||
# This module is part of GitPython and is released under
|
||||
# the BSD License: http://www.opensource.org/licenses/bsd-license.php
|
||||
|
||||
import os
|
||||
|
||||
def dashify(string):
|
||||
return string.replace('_', '-')
|
||||
|
||||
def touch(filename):
|
||||
fp = open(filename, 'a')
|
||||
fp.close()
|
||||
|
||||
def is_git_dir(d):
|
||||
""" This is taken from the git setup.c:is_git_directory
|
||||
function."""
|
||||
|
||||
if os.path.isdir(d) and \
|
||||
os.path.isdir(os.path.join(d, 'objects')) and \
|
||||
os.path.isdir(os.path.join(d, 'refs')):
|
||||
headref = os.path.join(d, 'HEAD')
|
||||
return os.path.isfile(headref) or \
|
||||
(os.path.islink(headref) and
|
||||
os.readlink(headref).startswith('refs'))
|
||||
return False
|
|
@ -0,0 +1,26 @@
|
|||
Metadata-Version: 1.0
|
||||
Name: GitPython
|
||||
Version: 0.1.7
|
||||
Summary: Python Git Library
|
||||
Home-page: http://gitorious.org/projects/git-python/
|
||||
Author: Michael Trier
|
||||
Author-email: mtrier@gmail.com
|
||||
License: BSD License
|
||||
Description: GitPython is a python library used to interact with Git repositories.
|
||||
|
||||
GitPython provides object model access to your git repository. Once you have
|
||||
created a repository object, you can traverse it to find parent commit(s),
|
||||
trees, blobs, etc.
|
||||
|
||||
GitPython is a port of the grit library in Ruby created by
|
||||
Tom Preston-Werner and Chris Wanstrath.
|
||||
|
||||
Platform: UNKNOWN
|
||||
Classifier: Development Status :: 3 - Alpha
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: License :: OSI Approved :: BSD License
|
||||
Classifier: Operating System :: OS Independent
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Programming Language :: Python :: 2.5
|
||||
Classifier: Programming Language :: Python :: 2.6
|
||||
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
@ -0,0 +1,25 @@
|
|||
AUTHORS
|
||||
CHANGES
|
||||
LICENSE
|
||||
MANIFEST.in
|
||||
README
|
||||
VERSION
|
||||
setup.cfg
|
||||
lib/git/__init__.py
|
||||
lib/git/actor.py
|
||||
lib/git/blob.py
|
||||
lib/git/cmd.py
|
||||
lib/git/commit.py
|
||||
lib/git/diff.py
|
||||
lib/git/errors.py
|
||||
lib/git/head.py
|
||||
lib/git/lazy.py
|
||||
lib/git/repo.py
|
||||
lib/git/stats.py
|
||||
lib/git/tag.py
|
||||
lib/git/tree.py
|
||||
lib/git/utils.py
|
||||
pip-egg-info/GitPython.egg-info/PKG-INFO
|
||||
pip-egg-info/GitPython.egg-info/SOURCES.txt
|
||||
pip-egg-info/GitPython.egg-info/dependency_links.txt
|
||||
pip-egg-info/GitPython.egg-info/top_level.txt
|
|
@ -0,0 +1 @@
|
|||
|
|
@ -0,0 +1 @@
|
|||
git
|
|
@ -0,0 +1,5 @@
|
|||
[egg_info]
|
||||
tag_build =
|
||||
tag_date = 0
|
||||
tag_svn_revision = 0
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
try:
|
||||
from setuptools import setup, find_packages
|
||||
except ImportError:
|
||||
from ez_setup import use_setuptools
|
||||
use_setuptools()
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
from distutils.command.build_py import build_py as _build_py
|
||||
from setuptools.command.sdist import sdist as _sdist
|
||||
import os
|
||||
from os import path
|
||||
|
||||
v = open(path.join(path.dirname(__file__), 'VERSION'))
|
||||
VERSION = v.readline().strip()
|
||||
v.close()
|
||||
|
||||
class build_py(_build_py):
|
||||
def run(self):
|
||||
init = path.join(self.build_lib, 'git', '__init__.py')
|
||||
if path.exists(init):
|
||||
os.unlink(init)
|
||||
_build_py.run(self)
|
||||
_stamp_version(init)
|
||||
self.byte_compile([init])
|
||||
|
||||
class sdist(_sdist):
|
||||
def make_release_tree (self, base_dir, files):
|
||||
_sdist.make_release_tree(self, base_dir, files)
|
||||
orig = path.join('lib', 'git', '__init__.py')
|
||||
assert path.exists(orig)
|
||||
dest = path.join(base_dir, orig)
|
||||
if hasattr(os, 'link') and path.exists(dest):
|
||||
os.unlink(dest)
|
||||
self.copy_file(orig, dest)
|
||||
_stamp_version(dest)
|
||||
|
||||
def _stamp_version(filename):
|
||||
found, out = False, []
|
||||
f = open(filename, 'r')
|
||||
for line in f:
|
||||
if '__version__ =' in line:
|
||||
line = line.replace("'git'", "'%s'" % VERSION)
|
||||
found = True
|
||||
out.append(line)
|
||||
f.close()
|
||||
|
||||
if found:
|
||||
f = open(filename, 'w')
|
||||
f.writelines(out)
|
||||
f.close()
|
||||
|
||||
|
||||
setup(name = "GitPython",
|
||||
cmdclass={'build_py': build_py, 'sdist': sdist},
|
||||
version = VERSION,
|
||||
description = "Python Git Library",
|
||||
author = "Michael Trier",
|
||||
author_email = "mtrier@gmail.com",
|
||||
url = "http://gitorious.org/projects/git-python/",
|
||||
packages = find_packages('lib'),
|
||||
package_dir = {'':'lib'},
|
||||
license = "BSD License",
|
||||
long_description = """\
|
||||
GitPython is a python library used to interact with Git repositories.
|
||||
|
||||
GitPython provides object model access to your git repository. Once you have
|
||||
created a repository object, you can traverse it to find parent commit(s),
|
||||
trees, blobs, etc.
|
||||
|
||||
GitPython is a port of the grit library in Ruby created by
|
||||
Tom Preston-Werner and Chris Wanstrath.
|
||||
""",
|
||||
classifiers = [
|
||||
"Development Status :: 3 - Alpha",
|
||||
"Intended Audience :: Developers",
|
||||
"License :: OSI Approved :: BSD License",
|
||||
"Operating System :: OS Independent",
|
||||
"Programming Language :: Python",
|
||||
"Programming Language :: Python :: 2.5",
|
||||
"Programming Language :: Python :: 2.6",
|
||||
"Topic :: Software Development :: Libraries :: Python Modules",
|
||||
]
|
||||
)
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 95f24e9ecdbeb5d1d88029896b3d53ae8828e533
|
|
@ -1,6 +1,8 @@
|
|||
packages/GitPython/lib
|
||||
src/commonware
|
||||
src/django
|
||||
src/django-sha2
|
||||
src/jingo
|
||||
src/jingo-minify
|
||||
src/tower
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче