Imported Upstream version 0.1.5
SVN-Git Migration
8 years ago
1 | 1 | Alan Briolat |
2 | 2 | Florian Apolloner <florian _at_ apolloner.eu> |
3 | 3 | David Aguilar <davvid _at_ gmail.com> |
4 | Jelmer Vernooij <jelmer _at_ samba.org> | |
5 | Steve Frécinaux <code _at_ istique.net> | |
6 | Kai Lautaportti <kai _at_ lautaportti.fi> |
4 | 4 | 0.1.5 |
5 | 5 | ===== |
6 | 6 | |
7 | * removed ``method_missing`` stuff and replaced with a ``__getattr__`` | |
7 | General | |
8 | ------- | |
9 | * upgraded to Mock 0.4 dependency. | |
10 | ||
11 | * Replace GitPython with git in repr() outputs. | |
12 | ||
13 | * Fixed packaging issue caused by ez_setup.py. | |
14 | ||
15 | Blob | |
16 | ---- | |
17 | * No longer strip newlines from Blob data. | |
18 | ||
19 | Commit | |
20 | ------ | |
21 | * Corrected problem with git-rev-list --bisect-all. See | |
22 | http://groups.google.com/group/git-python/browse_thread/thread/aed1d5c4b31d5027 | |
23 | ||
24 | Repo | |
25 | ---- | |
26 | * Corrected problems with creating bare repositories. | |
27 | ||
28 | * Repo.tree no longer accepts a path argument. Use: | |
29 | ||
30 | >>> dict(k, o for k, o in tree.items() if k in paths) | |
31 | ||
32 | * Made daemon export a property of Repo. Now you can do this: | |
33 | ||
34 | >>> exported = repo.daemon_export | |
35 | >>> repo.daemon_export = True | |
36 | ||
37 | * Allows modifying the project description. Do this: | |
38 | ||
39 | >>> repo.description = "Foo Bar" | |
40 | >>> repo.description | |
41 | 'Foo Bar' | |
42 | ||
43 | * Added a read-only property Repo.is_dirty which reflects the status of the | |
44 | working directory. | |
45 | ||
46 | * Added a read-only Repo.active_branch property which returns the name of the | |
47 | currently active branch. | |
48 | ||
49 | ||
50 | Tree | |
51 | ---- | |
52 | * Switched to using a dictionary for Tree contents since you will usually want | |
53 | to access them by name and order is unimportant. | |
54 | ||
55 | * Implemented a dictionary protocol for Tree objects. The following: | |
56 | ||
57 | child = tree.contents['grit'] | |
58 | ||
59 | becomes: | |
60 | ||
61 | child = tree['grit'] | |
62 | ||
63 | * Made Tree.content_from_string a static method. | |
64 | ||
65 | 0.1.4.1 | |
66 | ======= | |
67 | ||
68 | * removed ``method_missing`` stuff and replaced with a ``__getattr__`` | |
8 | 69 | override in ``Git``. |
9 | 70 | |
10 | 71 | 0.1.4 |
0 | 0 | Metadata-Version: 1.0 |
1 | 1 | Name: GitPython |
2 | Version: 0.1.4.1 | |
2 | Version: 0.1.5 | |
3 | 3 | Summary: Python Git Library |
4 | 4 | Home-page: http://gitorious.org/projects/git-python/ |
5 | 5 | Author: Michael Trier |
12 | 12 | ============ |
13 | 13 | |
14 | 14 | * Git_ tested with 1.5.3.7 |
15 | * `Python Nose`_ - used for running the tests | |
16 | * `Mock by Michael Foord`_ used for tests | |
15 | * `Python Nose`_ - used for running the tests | |
16 | * `Mock by Michael Foord`_ used for tests. Requires 0.4 | |
17 | 17 | |
18 | 18 | .. _Git: http://git.or.cz/ |
19 | 19 | .. _Python Nose: http://code.google.com/p/python-nose/ |
38 | 38 | LICENSE |
39 | 39 | ======= |
40 | 40 | |
41 | New BSD License. See the LICENSE file.⏎ | |
41 | New BSD License. See the LICENSE file. |
26 | 26 | objects. |
27 | 27 | |
28 | 28 | >>> repo.commits() |
29 | [<GitPython.Commit "207c0c4418115df0d30820ab1a9acd2ea4bf4431">, | |
30 | <GitPython.Commit "a91c45eee0b41bf3cdaad3418ca3850664c4a4b4">, | |
31 | <GitPython.Commit "e17c7e11aed9e94d2159e549a99b966912ce1091">, | |
32 | <GitPython.Commit "bd795df2d0e07d10e0298670005c0e9d9a5ed867">] | |
29 | [<git.Commit "207c0c4418115df0d30820ab1a9acd2ea4bf4431">, | |
30 | <git.Commit "a91c45eee0b41bf3cdaad3418ca3850664c4a4b4">, | |
31 | <git.Commit "e17c7e11aed9e94d2159e549a99b966912ce1091">, | |
32 | <git.Commit "bd795df2d0e07d10e0298670005c0e9d9a5ed867">] | |
33 | 33 | |
34 | 34 | Called without arguments, ``Repo.commits`` returns a list of up to ten commits |
35 | 35 | reachable by the master branch (starting at the latest commit). You can ask |
60 | 60 | '207c0c4418115df0d30820ab1a9acd2ea4bf4431' |
61 | 61 | |
62 | 62 | >>> head.parents |
63 | [<GitPython.Commit "a91c45eee0b41bf3cdaad3418ca3850664c4a4b4">] | |
63 | [<git.Commit "a91c45eee0b41bf3cdaad3418ca3850664c4a4b4">] | |
64 | 64 | |
65 | 65 | >>> head.tree |
66 | <GitPython.Tree "563413aedbeda425d8d9dcbb744247d0c3e8a0ac"> | |
66 | <git.Tree "563413aedbeda425d8d9dcbb744247d0c3e8a0ac"> | |
67 | 67 | |
68 | 68 | >>> head.author |
69 | <GitPython.Actor "Michael Trier <mtrier@gmail.com>"> | |
69 | <git.Actor "Michael Trier <mtrier@gmail.com>"> | |
70 | 70 | |
71 | 71 | >>> head.authored_date |
72 | 72 | (2008, 5, 7, 5, 0, 56, 2, 128, 0) |
73 | 73 | |
74 | 74 | >>> head.committer |
75 | <GitPython.Actor "Michael Trier <mtrier@gmail.com>"> | |
75 | <git.Actor "Michael Trier <mtrier@gmail.com>"> | |
76 | 76 | |
77 | 77 | >>> head.committed_date |
78 | 78 | (2008, 5, 7, 5, 0, 56, 2, 128, 0) |
106 | 106 | the root tree of the latest commit on the master branch. |
107 | 107 | |
108 | 108 | >>> tree = repo.commits()[0].tree |
109 | <GitPython.Tree "a006b5b1a8115185a228b7514cdcd46fed90dc92"> | |
109 | <git.Tree "a006b5b1a8115185a228b7514cdcd46fed90dc92"> | |
110 | 110 | |
111 | 111 | >>> tree.id |
112 | 112 | 'a006b5b1a8115185a228b7514cdcd46fed90dc92' |
114 | 114 | Once you have a tree, you can get the contents. |
115 | 115 | |
116 | 116 | >>> contents = tree.contents |
117 | [<GitPython.Blob "6a91a439ea968bf2f5ce8bb1cd8ddf5bf2cad6c7">, | |
118 | <GitPython.Blob "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391">, | |
119 | <GitPython.Tree "eaa0090ec96b054e425603480519e7cf587adfc3">, | |
120 | <GitPython.Blob "980e72ae16b5378009ba5dfd6772b59fe7ccd2df">] | |
117 | [<git.Blob "6a91a439ea968bf2f5ce8bb1cd8ddf5bf2cad6c7">, | |
118 | <git.Blob "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391">, | |
119 | <git.Tree "eaa0090ec96b054e425603480519e7cf587adfc3">, | |
120 | <git.Blob "980e72ae16b5378009ba5dfd6772b59fe7ccd2df">] | |
121 | 121 | |
122 | 122 | This tree contains three ``Blob`` objects and one ``Tree`` object. The trees |
123 | 123 | are subdirectories and the blobs are files. Trees below the root have |
124 | 124 | additional attributes. |
125 | 125 | |
126 | >>> contents = tree.contents[-2] | |
127 | <GitPython.Tree "e5445b9db4a9f08d5b4de4e29e61dffda2f386ba"> | |
126 | >>> contents = tree["lib"] | |
127 | <git.Tree "c1c7214dde86f76bc3e18806ac1f47c38b2b7a3"> | |
128 | 128 | |
129 | 129 | >>> contents.name |
130 | 130 | 'test' |
133 | 133 | '040000' |
134 | 134 | |
135 | 135 | There is a convenience method that allows you to get a named sub-object |
136 | from a tree. | |
136 | from a tree with a syntax similar to how paths are written in an unix | |
137 | system. | |
137 | 138 | |
138 | 139 | >>> tree/"lib" |
139 | <GitPython.Tree "c1c7214dde86f76bc3e18806ac1f47c38b2b7a30"> | |
140 | <git.Tree "c1c7214dde86f76bc3e18806ac1f47c38b2b7a30"> | |
140 | 141 | |
141 | 142 | You can also get a tree directly from the repository if you know its name. |
142 | 143 | |
143 | 144 | >>> repo.tree() |
144 | <GitPython.Tree "master"> | |
145 | <git.Tree "master"> | |
145 | 146 | |
146 | 147 | >>> repo.tree("c1c7214dde86f76bc3e18806ac1f47c38b2b7a30") |
147 | <GitPython.Tree "c1c7214dde86f76bc3e18806ac1f47c38b2b7a30"> | |
148 | <git.Tree "c1c7214dde86f76bc3e18806ac1f47c38b2b7a30"> | |
148 | 149 | |
149 | 150 | The Blob object |
150 | 151 | *************** |
152 | 153 | A blob represents a file. Trees often contain blobs. |
153 | 154 | |
154 | 155 | >>> blob = tree.contents[-1] |
155 | <GitPython.Blob "b19574431a073333ea09346eafd64e7b1908ef49"> | |
156 | <git.Blob "b19574431a073333ea09346eafd64e7b1908ef49"> | |
156 | 157 | |
157 | 158 | A blob has certain attributes. |
158 | 159 | |
176 | 177 | You can also get a blob directly from the repo if you know its name. |
177 | 178 | |
178 | 179 | >>> repo.blob("b19574431a073333ea09346eafd64e7b1908ef49") |
179 | <GitPython.Blob "b19574431a073333ea09346eafd64e7b1908ef49"> | |
180 | <git.Blob "b19574431a073333ea09346eafd64e7b1908ef49"> | |
180 | 181 | |
181 | 182 | What Else? |
182 | 183 | ********** |
0 | 0 | Metadata-Version: 1.0 |
1 | 1 | Name: GitPython |
2 | Version: 0.1.4.1 | |
2 | Version: 0.1.5 | |
3 | 3 | Summary: Python Git Library |
4 | 4 | Home-page: http://gitorious.org/projects/git-python/ |
5 | 5 | Author: Michael Trier |
22 | 22 | lib/git/stats.py |
23 | 23 | lib/git/tag.py |
24 | 24 | lib/git/tree.py |
25 | lib/git/utils.py | |
25 | lib/git/utils.py⏎ |
6 | 6 | import os |
7 | 7 | import inspect |
8 | 8 | |
9 | __version__ = '0.1.4.1' | |
9 | __version__ = '0.1.5' | |
10 | 10 | |
11 | 11 | from git.actor import Actor |
12 | 12 | from git.blob import Blob |
14 | 14 | return self.name |
15 | 15 | |
16 | 16 | def __repr__(self): |
17 | return '<GitPython.Actor "%s <%s>">' % (self.name, self.email) | |
17 | return '<git.Actor "%s <%s>">' % (self.name, self.email) | |
18 | 18 | |
19 | 19 | @classmethod |
20 | 20 | def from_string(cls, string): |
13 | 13 | class Blob(object): |
14 | 14 | DEFAULT_MIME_TYPE = "text/plain" |
15 | 15 | |
16 | def __init__(self, repo, **kwargs): | |
16 | def __init__(self, repo, id, mode=None, name=None): | |
17 | 17 | """ |
18 | 18 | Create an unbaked Blob containing just the specified attributes |
19 | 19 | |
20 | 20 | ``repo`` |
21 | 21 | is the Repo |
22 | 22 | |
23 | ``atts`` | |
24 | is a dict of instance variable data | |
23 | ``id`` | |
24 | is the git object id | |
25 | ||
26 | ``mode`` | |
27 | is the file mode | |
28 | ||
29 | ``name`` | |
30 | is the file name | |
25 | 31 | |
26 | 32 | Returns |
27 | GitPython.Blob | |
33 | git.Blob | |
28 | 34 | """ |
29 | self.id = None | |
30 | self.mode = None | |
31 | self.name = None | |
35 | self.repo = repo | |
36 | self.id = id | |
37 | self.mode = mode | |
38 | self.name = name | |
39 | ||
32 | 40 | self._size = None |
33 | 41 | self.data_stored = None |
34 | ||
35 | self.repo = repo | |
36 | for k, v in kwargs.items(): | |
37 | setattr(self, k, v) | |
38 | 42 | |
39 | 43 | @property |
40 | 44 | def size(self): |
45 | 49 | int |
46 | 50 | """ |
47 | 51 | if self._size is None: |
48 | self._size = int(self.repo.git.cat_file(self.id, **{'s': True}).rstrip()) | |
52 | self._size = int(self.repo.git.cat_file(self.id, s=True).rstrip()) | |
49 | 53 | return self._size |
50 | 54 | |
51 | 55 | @property |
56 | 60 | Returns |
57 | 61 | str |
58 | 62 | """ |
59 | self.data_stored = self.data_stored or self.repo.git.cat_file(self.id, **{'p': True}) | |
63 | self.data_stored = self.data_stored or self.repo.git.cat_file(self.id, p=True, with_raw_output=True) | |
60 | 64 | return self.data_stored |
61 | 65 | |
62 | 66 | @property |
82 | 86 | The blame information for the given file at the given commit |
83 | 87 | |
84 | 88 | Returns |
85 | list: [GitPython.Commit, list: [<line>]] | |
89 | list: [git.Commit, list: [<line>]] | |
86 | 90 | """ |
87 | data = repo.git.blame(commit, '--', file, **{'p': True}) | |
91 | data = repo.git.blame(commit, '--', file, p=True) | |
88 | 92 | commits = {} |
89 | 93 | blames = [] |
90 | 94 | info = None |
119 | 123 | if info: |
120 | 124 | c = commits.has_key(info['id']) and commits[info['id']] |
121 | 125 | if not c: |
122 | c = Commit(repo, **{'id': info['id'], | |
123 | 'author': Actor.from_string(info['author'] + ' ' + info['author_email']), | |
124 | 'authored_date': info['author_date'], | |
125 | 'committer': Actor.from_string(info['committer'] + ' ' + info['committer_email']), | |
126 | 'committed_date': info['committer_date'], | |
127 | 'message': info['summary']}) | |
126 | c = Commit(repo, id=info['id'], | |
127 | author=Actor.from_string(info['author'] + ' ' + info['author_email']), | |
128 | authored_date=info['author_date'], | |
129 | committer=Actor.from_string(info['committer'] + ' ' + info['committer_email']), | |
130 | committed_date=info['committer_date'], | |
131 | message=info['summary']) | |
128 | 132 | commits[info['id']] = c |
129 | 133 | |
130 | 134 | m = re.search(r'^\t(.*)$', line) |
136 | 140 | return blames |
137 | 141 | |
138 | 142 | def __repr__(self): |
139 | return '<GitPython.Blob "%s">' % self.id | |
143 | return '<git.Blob "%s">' % self.id |
0 | # cmd.py | |
0 | # cmd.py | |
1 | 1 | # Copyright (C) 2008 Michael Trier (mtrier@gmail.com) and contributors |
2 | 2 | # |
3 | 3 | # This module is part of GitPython and is released under |
4 | 4 | # the BSD License: http://www.opensource.org/licenses/bsd-license.php |
5 | 5 | |
6 | import os | |
6 | import os, sys | |
7 | 7 | import subprocess |
8 | 8 | import re |
9 | 9 | from utils import * |
14 | 14 | |
15 | 15 | execute_kwargs = ('istream', 'with_keep_cwd', 'with_extended_output', |
16 | 16 | 'with_exceptions', 'with_raw_output') |
17 | ||
18 | extra = {} | |
19 | if sys.platform == 'win32': | |
20 | extra = {'shell': True} | |
17 | 21 | |
18 | 22 | class Git(object): |
19 | 23 | """ |
82 | 86 | cwd=cwd, |
83 | 87 | stdin=istream, |
84 | 88 | stderr=subprocess.PIPE, |
85 | stdout=subprocess.PIPE | |
89 | stdout=subprocess.PIPE, | |
90 | **extra | |
86 | 91 | ) |
87 | 92 | |
88 | 93 | # Wait for the process to return |
8 | 8 | |
9 | 9 | from actor import Actor |
10 | 10 | from lazy import LazyMixin |
11 | import tree | |
11 | from tree import Tree | |
12 | 12 | import diff |
13 | 13 | import stats |
14 | 14 | |
15 | 15 | class Commit(LazyMixin): |
16 | def __init__(self, repo, **kwargs): | |
16 | def __init__(self, repo, id, tree=None, author=None, authored_date=None, | |
17 | committer=None, committed_date=None, message=None, parents=None): | |
17 | 18 | """ |
18 | 19 | Instantiate a new Commit |
19 | 20 | |
41 | 42 | ``message`` |
42 | 43 | is the first line of the commit message |
43 | 44 | |
44 | Returns | |
45 | GitPython.Commit | |
45 | ``parents`` | |
46 | is the list of the parents of the commit | |
47 | ||
48 | Returns | |
49 | git.Commit | |
46 | 50 | """ |
47 | 51 | LazyMixin.__init__(self) |
48 | 52 | |
49 | 53 | self.repo = repo |
50 | self.id = None | |
54 | self.id = id | |
55 | self.parents = None | |
51 | 56 | self.tree = None |
52 | self.author = None | |
53 | self.authored_date = None | |
54 | self.committer = None | |
55 | self.committed_date = None | |
56 | self.message = None | |
57 | self.parents = None | |
58 | ||
59 | for k, v in kwargs.items(): | |
60 | setattr(self, k, v) | |
57 | self.author = author | |
58 | self.authored_date = authored_date | |
59 | self.committer = committer | |
60 | self.committed_date = committed_date | |
61 | self.message = message | |
61 | 62 | |
62 | 63 | if self.id: |
63 | if 'parents' in kwargs: | |
64 | self.parents = map(lambda p: Commit(repo, **{'id': p}), kwargs['parents']) | |
65 | if 'tree' in kwargs: | |
66 | self.tree = tree.Tree(repo, **{'id': kwargs['tree']}) | |
64 | if parents is not None: | |
65 | self.parents = [Commit(repo, p) for p in parents] | |
66 | if tree is not None: | |
67 | self.tree = Tree(repo, id=tree) | |
67 | 68 | |
68 | 69 | def __bake__(self): |
69 | temp = Commit.find_all(self.repo, self.id, **{'max_count': 1})[0] | |
70 | temp = Commit.find_all(self.repo, self.id, max_count=1)[0] | |
70 | 71 | self.parents = temp.parents |
71 | 72 | self.tree = temp.tree |
72 | 73 | self.author = temp.author |
111 | 112 | ``skip`` is the number of commits to skip |
112 | 113 | |
113 | 114 | Returns |
114 | GitPython.Commit[] | |
115 | git.Commit[] | |
115 | 116 | """ |
116 | 117 | options = {'pretty': 'raw'} |
117 | 118 | options.update(kwargs) |
131 | 132 | is the text output from the git command (raw format) |
132 | 133 | |
133 | 134 | Returns |
134 | GitPython.Commit[] | |
135 | git.Commit[] | |
135 | 136 | """ |
136 | 137 | lines = [l for l in text.splitlines() if l.strip()] |
137 | 138 | |
138 | 139 | commits = [] |
139 | 140 | |
140 | 141 | while lines: |
141 | id = lines.pop(0).split()[-1] | |
142 | tree = lines.pop(0).split()[-1] | |
142 | id = lines.pop(0).split()[1] | |
143 | tree = lines.pop(0).split()[1] | |
143 | 144 | |
144 | 145 | parents = [] |
145 | while lines and re.search(r'^parent', lines[0]): | |
146 | while lines and lines[0].startswith('parent'): | |
146 | 147 | parents.append(lines.pop(0).split()[-1]) |
147 | 148 | author, authored_date = cls.actor(lines.pop(0)) |
148 | 149 | committer, committed_date = cls.actor(lines.pop(0)) |
149 | 150 | |
150 | 151 | messages = [] |
151 | while lines and re.search(r'^ {4}', lines[0]): | |
152 | while lines and lines[0].startswith(' '): | |
152 | 153 | messages.append(lines.pop(0).strip()) |
153 | 154 | |
154 | 155 | message = messages and messages[0] or '' |
178 | 179 | is a list of paths to limit the diff. |
179 | 180 | |
180 | 181 | Returns |
181 | GitPython.Diff[] | |
182 | git.Diff[] | |
182 | 183 | """ |
183 | 184 | paths = paths or [] |
184 | 185 | |
192 | 193 | if b: |
193 | 194 | paths.insert(0, b) |
194 | 195 | paths.insert(0, a) |
195 | text = repo.git.diff(*paths, **{'full_index': True}) | |
196 | text = repo.git.diff(full_index=True, *paths) | |
196 | 197 | return diff.Diff.list_from_string(repo, text) |
197 | 198 | |
198 | 199 | @property |
199 | 200 | def diffs(self): |
200 | 201 | if not self.parents: |
201 | d = self.repo.git.show(self.id, **{'full_index': True, 'pretty': 'raw'}) | |
202 | d = self.repo.git.show(self.id, full_index=True, pretty='raw') | |
202 | 203 | if re.search(r'diff --git a', d): |
203 | 204 | if not re.search(r'^diff --git a', d): |
204 | 205 | p = re.compile(r'.+?(diff --git a)', re.MULTILINE | re.DOTALL) |
212 | 213 | @property |
213 | 214 | def stats(self): |
214 | 215 | if not self.parents: |
215 | text = self.repo.git.diff(self.id, **{'numstat': True}) | |
216 | text = self.repo.git.diff(self.id, numstat=True) | |
216 | 217 | text2 = "" |
217 | 218 | for line in text.splitlines(): |
218 | 219 | (insertions, deletions, filename) = line.split("\t") |
219 | 220 | text2 += "%s\t%s\t%s\n" % (deletions, insertions, filename) |
220 | 221 | text = text2 |
221 | 222 | else: |
222 | text = self.repo.git.diff(self.parents[0].id, self.id, **{'numstat': True}) | |
223 | text = self.repo.git.diff(self.parents[0].id, self.id, numstat=True) | |
223 | 224 | return stats.Stats.list_from_string(self.repo, text) |
224 | 225 | |
225 | 226 | def __str__(self): |
227 | 228 | return self.id |
228 | 229 | |
229 | 230 | def __repr__(self): |
230 | return '<GitPython.Commit "%s">' % self.id | |
231 | return '<git.Commit "%s">' % self.id | |
231 | 232 | |
232 | 233 | @classmethod |
233 | 234 | def actor(cls, line): |
19 | 19 | if not a_commit or re.search(r'^0{40}$', a_commit): |
20 | 20 | self.a_commit = None |
21 | 21 | else: |
22 | self.a_commit = commit.Commit(repo, **{'id': a_commit}) | |
22 | self.a_commit = commit.Commit(repo, id=a_commit) | |
23 | 23 | if not b_commit or re.search(r'^0{40}$', b_commit): |
24 | 24 | self.b_commit = None |
25 | 25 | else: |
26 | self.b_commit = commit.Commit(repo, **{'id': b_commit}) | |
26 | self.b_commit = commit.Commit(repo, id=b_commit) | |
27 | 27 | |
28 | 28 | self.a_mode = a_mode |
29 | 29 | self.b_mode = b_mode |
19 | 19 | 'master' |
20 | 20 | |
21 | 21 | >>> head.commit |
22 | <GitPython.Commit "1c09f116cbc2cb4100fb6935bb162daa4723f455"> | |
22 | <git.Commit "1c09f116cbc2cb4100fb6935bb162daa4723f455"> | |
23 | 23 | |
24 | 24 | >>> head.commit.id |
25 | 25 | '1c09f116cbc2cb4100fb6935bb162daa4723f455' |
36 | 36 | is the Commit that the head points to |
37 | 37 | |
38 | 38 | Returns |
39 | GitPython.Head | |
39 | git.Head | |
40 | 40 | """ |
41 | 41 | self.name = name |
42 | 42 | self.commit = commit |
53 | 53 | is a dict of options |
54 | 54 | |
55 | 55 | Returns |
56 | GitPython.Head[] | |
56 | git.Head[] | |
57 | 57 | """ |
58 | 58 | |
59 | 59 | options = {'sort': "committerdate", |
74 | 74 | is the text output from the git command |
75 | 75 | |
76 | 76 | Returns |
77 | GitPython.Head[] | |
77 | git.Head[] | |
78 | 78 | """ |
79 | 79 | heads = [] |
80 | 80 | |
100 | 100 | id: [0-9A-Fa-f]{40} |
101 | 101 | |
102 | 102 | Returns |
103 | GitPython.Head | |
103 | git.Head | |
104 | 104 | """ |
105 | print line | |
106 | 105 | full_name, ids = line.split("\x00") |
107 | 106 | name = full_name.split("/")[-1] |
108 | c = commit.Commit(repo, **{'id': ids}) | |
107 | c = commit.Commit(repo, id=ids) | |
109 | 108 | return Head(name, c) |
110 | 109 | |
111 | 110 | def __repr__(self): |
112 | return '<GitPython.Head "%s">' % self.name | |
111 | return '<git.Head "%s">' % self.name |
30 | 30 | repo = Repo("/Users/mtrier/Development/git-python.git") |
31 | 31 | |
32 | 32 | Returns |
33 | ``GitPython.Repo`` | |
33 | ``git.Repo`` | |
34 | 34 | """ |
35 | 35 | |
36 | 36 | epath = os.path.abspath(os.path.expanduser(path or os.getcwd())) |
43 | 43 | while curpath: |
44 | 44 | if is_git_dir(curpath): |
45 | 45 | self.bare = True |
46 | self.path, self.wd = curpath | |
46 | self.path = curpath | |
47 | self.wd = curpath | |
47 | 48 | break |
48 | 49 | gitpath = os.path.join(curpath, '.git') |
49 | 50 | if is_git_dir(gitpath): |
60 | 61 | |
61 | 62 | self.git = Git(self.wd) |
62 | 63 | |
63 | @property | |
64 | def description(self): | |
65 | """ | |
66 | The project's description. Taken verbatim from GIT_REPO/description | |
67 | ||
68 | Returns | |
69 | str | |
70 | """ | |
71 | try: | |
72 | f = open(os.path.join(self.path, 'description')) | |
73 | result = f.read() | |
74 | return result.rstrip() | |
75 | finally: | |
76 | f.close() | |
64 | # Description property | |
65 | def _get_description(self): | |
66 | filename = os.path.join(self.path, 'description') | |
67 | return file(filename).read().rstrip() | |
68 | ||
69 | def _set_description(self, descr): | |
70 | filename = os.path.join(self.path, 'description') | |
71 | file(filename, 'w').write(descr+'\n') | |
72 | ||
73 | description = property(_get_description, _set_description, | |
74 | doc="the project's description") | |
75 | del _get_description | |
76 | del _set_description | |
77 | 77 | |
78 | 78 | @property |
79 | 79 | def heads(self): |
82 | 82 | this repo |
83 | 83 | |
84 | 84 | Returns |
85 | ``GitPython.Head[]`` | |
85 | ``git.Head[]`` | |
86 | 86 | """ |
87 | 87 | return Head.find_all(self) |
88 | 88 | |
95 | 95 | A list of ``Tag`` objects that are available in this repo |
96 | 96 | |
97 | 97 | Returns |
98 | ``GitPython.Tag[]`` | |
98 | ``git.Tag[]`` | |
99 | 99 | """ |
100 | 100 | return Tag.find_all(self) |
101 | 101 | |
113 | 113 | is the number of commits to skip (default 0) |
114 | 114 | |
115 | 115 | Returns |
116 | ``GitPython.Commit[]`` | |
116 | ``git.Commit[]`` | |
117 | 117 | """ |
118 | 118 | options = {'max_count': max_count, |
119 | 119 | 'skip': skip} |
132 | 132 | is the branch/commit name of the older item |
133 | 133 | |
134 | 134 | Returns |
135 | ``GitPython.Commit[]`` | |
135 | ``git.Commit[]`` | |
136 | 136 | """ |
137 | 137 | return Commit.find_all(self, "%s..%s" % (frm, to)).reverse() |
138 | 138 | |
148 | 148 | is a string represeting a date/time |
149 | 149 | |
150 | 150 | Returns |
151 | ``GitPython.Commit[]`` | |
151 | ``git.Commit[]`` | |
152 | 152 | """ |
153 | 153 | options = {'since': since} |
154 | 154 | |
174 | 174 | is the SHA1 identifier of the commit |
175 | 175 | |
176 | 176 | Returns |
177 | GitPython.Commit | |
177 | git.Commit | |
178 | 178 | """ |
179 | 179 | options = {'max_count': 1} |
180 | 180 | |
189 | 189 | Returns a list of commits that is in ``other_repo`` but not in self |
190 | 190 | |
191 | 191 | Returns |
192 | ``GitPython.Commit[]`` | |
192 | ``git.Commit[]`` | |
193 | 193 | """ |
194 | 194 | repo_refs = self.git.rev_list(ref).strip().splitlines() |
195 | 195 | other_repo_refs = other_repo.git.rev_list(other_ref).strip().splitlines() |
196 | 196 | |
197 | 197 | diff_refs = list(set(other_repo_refs) - set(repo_refs)) |
198 | return map(lambda ref: Commit.find_all(other_repo, ref, **{'max_count': 1}[0]), diff_refs) | |
199 | ||
200 | def tree(self, treeish = 'master', paths = []): | |
198 | return map(lambda ref: Commit.find_all(other_repo, ref, max_count=1)[0], diff_refs) | |
199 | ||
200 | def tree(self, treeish = 'master'): | |
201 | 201 | """ |
202 | 202 | The Tree object for the given treeish reference |
203 | 203 | |
204 | 204 | ``treeish`` |
205 | 205 | is the reference (default 'master') |
206 | ``paths`` | |
207 | is an optional Array of directory paths to restrict the tree (default []) | |
208 | 206 | |
209 | 207 | Examples:: |
210 | 208 | |
211 | repo.tree('master', ['lib/']) | |
212 | ||
213 | ||
214 | Returns | |
215 | ``GitPython.Tree`` | |
216 | """ | |
217 | return Tree.construct(self, treeish, paths) | |
209 | repo.tree('master') | |
210 | ||
211 | ||
212 | Returns | |
213 | ``git.Tree`` | |
214 | """ | |
215 | return Tree(self, id=treeish) | |
218 | 216 | |
219 | 217 | def blob(self, id): |
220 | 218 | """ |
224 | 222 | is the SHA1 id of the blob |
225 | 223 | |
226 | 224 | Returns |
227 | ``GitPython.Blob`` | |
228 | """ | |
229 | return Blob(self, **{'id': id}) | |
225 | ``git.Blob`` | |
226 | """ | |
227 | return Blob(self, id=id) | |
230 | 228 | |
231 | 229 | def log(self, commit = 'master', path = None, **kwargs): |
232 | 230 | """ |
233 | 231 | The commit log for a treeish |
234 | 232 | |
235 | 233 | Returns |
236 | ``GitPython.Commit[]`` | |
234 | ``git.Commit[]`` | |
237 | 235 | """ |
238 | 236 | options = {'pretty': 'raw'} |
239 | 237 | options.update(kwargs) |
264 | 262 | ``commit`` is the commit name/id |
265 | 263 | |
266 | 264 | Returns |
267 | ``GitPython.Diff[]`` | |
265 | ``git.Diff[]`` | |
268 | 266 | """ |
269 | 267 | return Commit.diff(self, commit) |
270 | 268 | |
285 | 283 | |
286 | 284 | Examples:: |
287 | 285 | |
288 | GitPython.Repo.init_bare('/var/git/myrepo.git') | |
289 | ||
290 | Returns | |
291 | ``GitPython.Repo`` (the newly created repo) | |
286 | git.Repo.init_bare('/var/git/myrepo.git') | |
287 | ||
288 | Returns | |
289 | ``git.Repo`` (the newly created repo) | |
292 | 290 | """ |
293 | 291 | |
294 | 292 | if mkdir and not os.path.exists(path): |
295 | 293 | os.makedirs(path, 0755) |
296 | 294 | |
297 | 295 | git = Git(path) |
298 | output = git.init(**kwargs) | |
296 | output = git.init('--bare', **kwargs) | |
299 | 297 | return Repo(path) |
300 | 298 | create = init_bare |
301 | 299 | |
310 | 308 | is any additional options to the git clone command |
311 | 309 | |
312 | 310 | Returns |
313 | ``GitPython.Repo`` (the newly forked repo) | |
311 | ``git.Repo`` (the newly forked repo) | |
314 | 312 | """ |
315 | 313 | options = {'bare': True} |
316 | 314 | options.update(kwargs) |
375 | 373 | kwargs['prefix'] = prefix |
376 | 374 | self.git.archive(treeish, "| gzip", **kwargs) |
377 | 375 | |
378 | def enable_daemon_serve(self): | |
379 | """ | |
380 | Enable git-daemon serving of this repository by writing the | |
381 | git-daemon-export-ok file to its git directory | |
382 | ||
383 | Returns | |
384 | None | |
385 | """ | |
386 | touch(os.path.join(self.path, DAEMON_EXPORT_FILE)) | |
387 | ||
388 | def disable_daemon_serve(self): | |
389 | """ | |
390 | Disable git-daemon serving of this repository by ensuring there is no | |
391 | git-daemon-export-ok file in its git directory | |
392 | ||
393 | Returns | |
394 | None | |
395 | """ | |
396 | return os.remove(os.path.join(self.path, DAEMON_EXPORT_FILE)) | |
376 | def _get_daemon_export(self): | |
377 | filename = os.path.join(self.path, self.DAEMON_EXPORT_FILE) | |
378 | return os.path.exists(filename) | |
379 | ||
380 | def _set_daemon_export(self, value): | |
381 | filename = os.path.join(self.path, self.DAEMON_EXPORT_FILE) | |
382 | fileexists = os.path.exists(filename) | |
383 | if value and not fileexists: | |
384 | touch(filename) | |
385 | elif not value and fileexists: | |
386 | os.unlink(filename) | |
387 | ||
388 | daemon_export = property(_get_daemon_export, _set_daemon_export, | |
389 | doc="git-daemon export of this repository") | |
390 | del _get_daemon_export | |
391 | del _set_daemon_export | |
397 | 392 | |
398 | 393 | def _get_alternates(self): |
399 | 394 | """ |
402 | 397 | Returns |
403 | 398 | list[str] (pathnames of alternates) |
404 | 399 | """ |
405 | alternates_path = os.path.join(self.path, *['objects', 'info', 'alternates']) | |
400 | alternates_path = os.path.join(self.path, 'objects', 'info', 'alternates') | |
406 | 401 | |
407 | 402 | if os.path.exists(alternates_path): |
408 | 403 | try: |
429 | 424 | raise NoSuchPathError("Could not set alternates. Alternate path %s must exist" % alt) |
430 | 425 | |
431 | 426 | if not alts: |
432 | os.remove(os.path.join(self.path, *['objects', 'info', 'alternates'])) | |
427 | os.remove(os.path.join(self.path, 'objects', 'info', 'alternates')) | |
433 | 428 | else: |
434 | 429 | try: |
435 | f = open(os.path.join(self.path, *['objects', 'info', 'alternates']), 'w') | |
430 | f = open(os.path.join(self.path, 'objects', 'info', 'alternates'), 'w') | |
436 | 431 | f.write("\n".join(alts)) |
437 | 432 | finally: |
438 | 433 | f.close() |
439 | 434 | |
440 | 435 | alternates = property(_get_alternates, _set_alternates) |
441 | 436 | |
437 | @property | |
438 | def is_dirty(self): | |
439 | """ | |
440 | Return the status of the working directory. | |
441 | ||
442 | Returns | |
443 | ``True``, if the working directory has any uncommitted changes, | |
444 | otherwise ``False`` | |
445 | ||
446 | """ | |
447 | if self.bare: | |
448 | # Bare repositories with no associated working directory are | |
449 | # always consired to be clean. | |
450 | return False | |
451 | ||
452 | return len(self.git.diff('HEAD').strip()) > 0 | |
453 | ||
454 | @property | |
455 | def active_branch(self): | |
456 | """ | |
457 | The name of the currently active branch. | |
458 | ||
459 | Returns | |
460 | str (the branch name) | |
461 | """ | |
462 | branch = self.git.symbolic_ref('HEAD').strip() | |
463 | if branch.startswith('refs/heads/'): | |
464 | branch = branch[len('refs/heads/'):] | |
465 | ||
466 | return branch | |
467 | ||
442 | 468 | def __repr__(self): |
443 | return '<GitPython.Repo "%s">' % self.path | |
469 | return '<git.Repo "%s">' % self.path |
17 | 17 | is the Commit that the head points to |
18 | 18 | |
19 | 19 | Returns |
20 | ``GitPython.Tag`` | |
20 | ``git.Tag`` | |
21 | 21 | """ |
22 | 22 | self.name = name |
23 | 23 | self.commit = commit |
34 | 34 | is a dict of options |
35 | 35 | |
36 | 36 | Returns |
37 | ``GitPython.Tag[]`` | |
37 | ``git.Tag[]`` | |
38 | 38 | """ |
39 | 39 | options = {'sort': "committerdate", |
40 | 40 | 'format': "%(refname)%00%(objectname)"} |
55 | 55 | is the text output from the git command |
56 | 56 | |
57 | 57 | Returns |
58 | ``GitPython.Tag[]`` | |
58 | ``git.Tag[]`` | |
59 | 59 | """ |
60 | 60 | tags = [] |
61 | 61 | for line in text.splitlines(): |
79 | 79 | id: [0-9A-Fa-f]{40} |
80 | 80 | |
81 | 81 | Returns |
82 | ``GitPython.Tag`` | |
82 | ``git.Tag`` | |
83 | 83 | """ |
84 | 84 | full_name, ids = line.split("\x00") |
85 | 85 | name = full_name.split("/")[-1] |
86 | commit = Commit(repo, **{'id': ids}) | |
86 | commit = Commit(repo, id=ids) | |
87 | 87 | return Tag(name, commit) |
88 | 88 | |
89 | 89 | def __repr__(self): |
90 | return '<GitPython.Tag "%s">' % self.name | |
90 | return '<git.Tag "%s">' % self.name |
8 | 8 | import blob |
9 | 9 | |
10 | 10 | class Tree(LazyMixin): |
11 | def __init__(self, repo, **kwargs): | |
11 | def __init__(self, repo, id, mode=None, name=None): | |
12 | 12 | LazyMixin.__init__(self) |
13 | 13 | self.repo = repo |
14 | self.id = None | |
15 | self.mode = None | |
16 | self.name = None | |
17 | self.contents = None | |
18 | ||
19 | for k, v in kwargs.items(): | |
20 | setattr(self, k, v) | |
14 | self.id = id | |
15 | self.mode = mode | |
16 | self.name = name | |
17 | self._contents = None | |
21 | 18 | |
22 | 19 | def __bake__(self): |
23 | temp = Tree.construct(self.repo, self.id) | |
24 | self.contents = temp.contents | |
20 | # Ensure the treeish references directly a tree | |
21 | treeish = self.id | |
22 | if not treeish.endswith(':'): | |
23 | treeish = treeish + ':' | |
25 | 24 | |
26 | @classmethod | |
27 | def construct(cls, repo, treeish, paths = []): | |
28 | output = repo.git.ls_tree(treeish, *paths) | |
29 | return Tree(repo, **{'id': treeish}).construct_initialize(repo, treeish, output) | |
25 | # Read the tree contents. | |
26 | self._contents = {} | |
27 | for line in self.repo.git.ls_tree(self.id).splitlines(): | |
28 | obj = self.content_from_string(self.repo, line) | |
29 | if obj is not None: | |
30 | self._contents[obj.name] = obj | |
30 | 31 | |
31 | def construct_initialize(self, repo, id, text): | |
32 | self.repo = repo | |
33 | self.id = id | |
34 | self.contents = [] | |
35 | self.__baked__ = False | |
36 | ||
37 | for line in text.splitlines(): | |
38 | self.contents.append(self.content_from_string(self.repo, line)) | |
39 | ||
40 | self.contents = [c for c in self.contents if c is not None] | |
41 | ||
42 | self.__bake_it__() | |
43 | return self | |
44 | ||
45 | def content_from_string(self, repo, text): | |
32 | @staticmethod | |
33 | def content_from_string(repo, text): | |
46 | 34 | """ |
47 | 35 | Parse a content item and create the appropriate object |
48 | 36 | |
53 | 41 | is the single line containing the items data in `git ls-tree` format |
54 | 42 | |
55 | 43 | Returns |
56 | ``GitPython.Blob`` or ``GitPython.Tree`` | |
44 | ``git.Blob`` or ``git.Tree`` | |
57 | 45 | """ |
58 | 46 | try: |
59 | 47 | mode, typ, id, name = text.expandtabs(1).split(" ", 4) |
61 | 49 | return None |
62 | 50 | |
63 | 51 | if typ == "tree": |
64 | return Tree(repo, **{'id': id, 'mode': mode, 'name': name}) | |
52 | return Tree(repo, id=id, mode=mode, name=name) | |
65 | 53 | elif typ == "blob": |
66 | return blob.Blob(repo, **{'id': id, 'mode': mode, 'name': name}) | |
54 | return blob.Blob(repo, id=id, mode=mode, name=name) | |
67 | 55 | elif typ == "commit": |
68 | 56 | return None |
69 | 57 | else: |
76 | 64 | Examples:: |
77 | 65 | |
78 | 66 | >>> Repo('/path/to/python-git').tree/'lib' |
79 | <GitPython.Tree "6cc23ee138be09ff8c28b07162720018b244e95e"> | |
67 | <git.Tree "6cc23ee138be09ff8c28b07162720018b244e95e"> | |
80 | 68 | >>> Repo('/path/to/python-git').tree/'README.txt' |
81 | <GitPython.Blob "8b1e02c0fb554eed2ce2ef737a68bb369d7527df"> | |
69 | <git.Blob "8b1e02c0fb554eed2ce2ef737a68bb369d7527df"> | |
82 | 70 | |
83 | 71 | Returns |
84 | ``GitPython.Blob`` or ``GitPython.Tree`` or ``None`` if not found | |
72 | ``git.Blob`` or ``git.Tree`` or ``None`` if not found | |
85 | 73 | """ |
86 | contents = [c for c in self.contents if c.name == file] | |
87 | return contents and contents[0] or None | |
74 | return self.get(file) | |
88 | 75 | |
89 | 76 | @property |
90 | 77 | def basename(self): |
91 | 78 | os.path.basename(self.name) |
92 | 79 | |
93 | 80 | def __repr__(self): |
94 | return '<GitPython.Tree "%s">' % self.id | |
81 | return '<git.Tree "%s">' % self.id | |
82 | ||
83 | # Implement the basics of the dict protocol: | |
84 | # directories/trees can be seen as object dicts. | |
85 | def __getitem__(self, key): | |
86 | return self._contents[key] | |
87 | ||
88 | def __iter__(self): | |
89 | return iter(self._contents) | |
90 | ||
91 | def __len__(self): | |
92 | return len(self._contents) | |
93 | ||
94 | def __contains__(self, key): | |
95 | return key in self._contents | |
96 | ||
97 | def get(self, key): | |
98 | return self._contents.get(key) | |
99 | ||
100 | def items(self): | |
101 | return self._contents.items() | |
102 | ||
103 | def keys(self): | |
104 | return self._contents.keys() | |
105 | ||
106 | def values(self): | |
107 | return self._contents.values() |
0 | from ez_setup import use_setuptools | |
1 | use_setuptools() | |
2 | from setuptools import setup, find_packages | |
0 | try: | |
1 | from setuptools import setup, find_packages | |
2 | except ImportError: | |
3 | from ez_setup import use_setuptools | |
4 | use_setuptools() | |
5 | from setuptools import setup, find_packages | |
6 | ||
3 | 7 | from distutils.command.build_py import build_py as _build_py |
4 | 8 | from setuptools.command.sdist import sdist as _sdist |
5 | 9 | import os |