Kiln » Dependencies » Dulwich Read More
Clone URL:  
Pushed to one repository · View In Graph Contained in master-1, master-0, and 0.9.4

Merge branch 'porcelain' of git://git.samba.org/jelmer/dulwich

Changeset 91b8507f80dd

Parents dd6998855ad9

Parents c71b6367faf9

by Jelmer Vernooij

Changes to 5 files · Browse files at 91b8507f80dd Showing diff from parent dd6998855ad9 c71b6367faf9 Diff from another changeset...

Change 1 of 4 Show Entire File bin/​dulwich Stacked
 
30
31
32
 
33
34
35
36
37
38
39
40
41
42
43
44
 
45
46
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
48
49
 
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
 
100
101
102
 
159
160
161
162
163
164
165
166
167
168
 
169
170
171
172
 
173
174
175
176
177
178
179
 
 
180
181
182
183
184
185
186
187
188
189
190
191
 
 
 
 
 
192
193
194
195
196
197
198
199
200
201
202
 
203
204
205
206
207
 
 
 
 
 
 
208
209
210
 
219
220
221
 
 
 
222
223
224
 
30
31
32
33
34
35
36
37
38
39
 
40
41
42
43
44
45
46
 
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
 
91
92
93
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
95
96
97
 
154
155
156
 
 
 
 
 
 
 
157
158
159
160
 
161
162
163
164
165
166
 
 
167
168
169
 
 
 
 
 
 
 
 
 
 
 
170
171
172
173
174
175
176
177
178
179
 
 
 
 
 
 
180
181
182
183
 
 
184
185
186
187
188
189
190
191
192
 
201
202
203
204
205
206
207
208
209
@@ -30,20 +30,34 @@
 import sys  from getopt import getopt   +from dulwich import porcelain  from dulwich.client import get_transport_and_path  from dulwich.errors import ApplyDeltaError  from dulwich.index import Index  from dulwich.pack import Pack, sha_to_hex  from dulwich.patch import write_tree_diff  from dulwich.repo import Repo -from dulwich.server import update_server_info      def cmd_archive(args):   opts, args = getopt(args, "", [])   client, path = get_transport_and_path(args.pop(0)) + location = args.pop(0)   committish = args.pop(0) - client.archive(path, committish, sys.stdout.write, sys.stderr.write) + porcelain.archive(location, committish, outstream=sys.stdout, + errstream=sys.stderr) + + +def cmd_add(args): + opts, args = getopt(args, "", []) + + porcelain.add(".", paths=args) + + +def cmd_rm(args): + opts, args = getopt(args, "", []) + + porcelain.rm(".", paths=args)      def cmd_fetch_pack(args): @@ -77,26 +91,7 @@
  path = args.pop(0)   else:   path = "." - r = Repo(path) - todo = [r.head()] - done = set() - while todo: - sha = todo.pop() - assert isinstance(sha, str) - if sha in done: - continue - done.add(sha) - commit = r[sha] - print "-" * 50 - print "commit: %s" % sha - if len(commit.parents) > 1: - print "merge: %s" % "...".join(commit.parents[1:]) - print "author: %s" % commit.author - print "committer: %s" % commit.committer - print "" - print commit.message - print "" - todo.extend([p for p in commit.parents if p not in done]) + porcelain.log(repo=path, outstream=sys.stdout)      def cmd_diff(args): @@ -159,52 +154,39 @@
  else:   path = args[0]   - if not os.path.exists(path): - os.mkdir(path) - - if "--bare" in opts: - Repo.init_bare(path) - else: - Repo.init(path) + porcelain.init(path, bare=("--bare" in opts))      def cmd_clone(args): - opts, args = getopt(args, "", []) + opts, args = getopt(args, "", ["bare"])   opts = dict(opts)     if args == []:   print "usage: dulwich clone host:path [PATH]"   sys.exit(1) - client, host_path = get_transport_and_path(args.pop(0)) - + + source = args.pop(0)   if len(args) > 0: - path = args.pop(0) - else: - path = host_path.split("/")[-1] - - if not os.path.exists(path): - os.mkdir(path) - r = Repo.init(path) - remote_refs = client.fetch(host_path, r, - determine_wants=r.object_store.determine_wants_all, - progress=sys.stdout.write) - r["HEAD"] = remote_refs["HEAD"] + target = args.pop(0) + else: + target = None + + porcelain.clone(source, target, bare=("--bare" in opts))      def cmd_commit(args):   opts, args = getopt(args, "", ["message"])   opts = dict(opts) - r = Repo(".") - committer = "%s <%s>" % (os.getenv("GIT_COMMITTER_NAME"), - os.getenv("GIT_COMMITTER_EMAIL")) - author = "%s <%s>" % (os.getenv("GIT_AUTHOR_NAME"), - os.getenv("GIT_AUTHOR_EMAIL")) - r.do_commit(committer=committer, author=author, message=opts["--message"]) + porcelain.commit(".", message=opts["--message"])      def cmd_update_server_info(args): - r = Repo(".") - update_server_info(r) + porcelain.update_server_info(".") + + +def cmd_show(args): + opts, args = getopt(args, "", []) + porcelain.show(".")      commands = { @@ -219,6 +201,9 @@
  "archive": cmd_archive,   "update-server-info": cmd_update_server_info,   "diff": cmd_diff, + "add": cmd_add, + "rm": cmd_rm, + "show": cmd_show,   }    if len(sys.argv) < 2:
Change 1 of 1 Show Entire File dulwich/​porcelain.py Stacked
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
@@ -1,0 +1,202 @@
+# porcelain.py -- Porcelain-like layer on top of Dulwich +# Copyright (C) 2013 Jelmer Vernooij <jelmer@samba.org> +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# or (at your option) a later version of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. + +import os +import sys + +from dulwich.client import get_transport_and_path +from dulwich.patch import write_tree_diff +from dulwich.repo import (BaseRepo, Repo) +from dulwich.server import update_server_info as server_update_server_info + +"""Simple wrapper that provides porcelain-like functions on top of Dulwich. + +Currently implemented: + * archive + * add + * clone + * commit + * init + * remove + * update-server-info + +These functions are meant to behave similarly to the git subcommands. +Differences in behaviour are considered bugs. +""" + +__docformat__ = 'restructuredText' + + +def open_repo(path_or_repo): + """Open an argument that can be a repository or a path for a repository.""" + if isinstance(path_or_repo, BaseRepo): + return path_or_repo + return Repo(path_or_repo) + + +def archive(location, committish=None, outstream=sys.stdout, + errstream=sys.stderr): + """Create an archive. + + :param location: Location of repository for which to generate an archive. + :param committish: Commit SHA1 or ref to use + :param outstream: Output stream (defaults to stdout) + :param errstream: Error stream (defaults to stderr) + """ + + client, path = get_transport_and_path(location) + if committish is None: + committish = "HEAD" + client.archive(path, committish, outstream.write, errstream.write) + + +def update_server_info(repo="."): + """Update server info files for a repository. + + :param repo: path to the repository + """ + r = open_repo(repo) + server_update_server_info(r) + + +def commit(repo=".", message=None, author=None, committer=None): + """Create a new commit. + + :param repo: Path to repository + :param message: Optional commit message + :param author: Optional author name and email + :param committer: Optional committer name and email + :return: SHA1 of the new commit + """ + # FIXME: Support --all argument + # FIXME: Support --signoff argument + r = open_repo(repo) + return r.do_commit(message=message, author=author, + committer=committer) + + +def init(path=".", bare=False): + """Create a new git repository. + + :param path: Path to repository. + :param bare: Whether to create a bare repository. + :return: A Repo instance + """ + if not os.path.exists(path): + os.mkdir(path) + + if bare: + return Repo.init_bare(path) + else: + return Repo.init(path) + + +def clone(source, target=None, bare=False, outstream=sys.stdout): + """Clone a local or remote git repository. + + :param source: Path or URL for source repository + :param target: Path to target repository (optional) + :param bare: Whether or not to create a bare repository + :param outstream: Optional stream to write progress to + :return: The new repository + """ + client, host_path = get_transport_and_path(source) + + if target is None: + target = host_path.split("/")[-1] + + if not os.path.exists(target): + os.mkdir(target) + if bare: + r = Repo.init_bare(target) + else: + r = Repo.init(target) + remote_refs = client.fetch(host_path, r, + determine_wants=r.object_store.determine_wants_all, + progress=outstream.write) + r["HEAD"] = remote_refs["HEAD"] + return r + + +def add(repo=".", paths=None): + """Add files to the staging area. + + :param repo: Repository for the files + :param paths: Paths to add + """ + # FIXME: Support patterns, directories, no argument. + r = open_repo(repo) + r.stage(paths) + + +def rm(repo=".", paths=None): + """Remove files from the staging area. + + :param repo: Repository for the files + :param paths: Paths to remove + """ + r = open_repo(repo) + index = r.open_index() + for p in paths: + del index[p] + index.write() + + +def print_commit(commit, outstream): + """Write a human-readable commit log entry. + + :param commit: A `Commit` object + :param outstream: A stream file to write to + """ + outstream.write("-" * 50 + "\n") + outstream.write("commit: %s\n" % commit.id) + if len(commit.parents) > 1: + outstream.write("merge: %s\n" % "...".join(commit.parents[1:])) + outstream.write("author: %s\n" % commit.author) + outstream.write("committer: %s\n" % commit.committer) + outstream.write("\n") + outstream.write(commit.message + "\n") + outstream.write("\n") + + +def log(repo=".", outstream=sys.stdout): + """Write commit logs. + + :param repo: Path to repository + :param outstream: Stream to write log output to + """ + r = open_repo(repo) + walker = r.get_walker() + for entry in walker: + print_commit(entry.commit, outstream) + + +def show(repo=".", committish=None, outstream=sys.stdout): + """Print the changes in a commit. + + :param repo: Path to repository + :param committish: Commit to write + :param outstream: Stream to write to + """ + if committish is None: + committish = "HEAD" + r = open_repo(repo) + commit = r[committish] + parent_commit = r[commit.parents[0]] + print_commit(commit, outstream) + write_tree_diff(outstream, r.object_store, parent_commit.tree, commit.tree)
Change 1 of 3 Show Entire File dulwich/​repo.py Stacked
 
864
865
866
 
867
868
869
 
1212
1213
1214
 
 
1215
1216
1217
 
1218
1219
1220
 
1222
1223
1224
 
 
1225
1226
1227
 
1228
1229
1230
 
864
865
866
867
868
869
870
 
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
 
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
@@ -864,6 +864,7 @@
  :param determine_wants: Optional function to determine what refs to   fetch.   :param progress: Optional progress function + :return: The local refs   """   if determine_wants is None:   determine_wants = lambda heads: heads.values() @@ -1212,9 +1213,12 @@
  # FIXME: Read merge heads from .git/MERGE_HEADS   merge_heads = []   if committer is None: + # FIXME: Support GIT_COMMITTER_NAME/GIT_COMMITTER_EMAIL environment + # variables   committer = self._get_user_identity()   c.committer = committer   if commit_timestamp is None: + # FIXME: Support GIT_COMMITTER_DATE environment variable   commit_timestamp = time.time()   c.commit_time = int(commit_timestamp)   if commit_timezone is None: @@ -1222,9 +1226,12 @@
  commit_timezone = 0   c.commit_timezone = commit_timezone   if author is None: + # FIXME: Support GIT_AUTHOR_NAME/GIT_AUTHOR_EMAIL environment + # variables   author = committer   c.author = author   if author_timestamp is None: + # FIXME: Support GIT_AUTHOR_DATE environment variable   author_timestamp = commit_timestamp   c.author_time = int(author_timestamp)   if author_timezone is None:
 
125
126
127
 
128
129
130
 
125
126
127
128
129
130
131
@@ -125,6 +125,7 @@
  'missing_obj_finder',   'pack',   'patch', + 'porcelain',   'protocol',   'repository',   'server',
Change 1 of 1 Show Entire File dulwich/​tests/​test_porcelain.py Stacked
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
@@ -1,0 +1,162 @@
+# test_porcelain.py -- porcelain tests +# Copyright (C) 2013 Jelmer Vernooij <jelmer@samba.org> +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; version 2 +# of the License or (at your option) a later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. + +"""Tests for dulwich.porcelain.""" + +from cStringIO import StringIO +import os +import shutil +import tarfile +import tempfile + +from dulwich import porcelain +from dulwich.repo import Repo +from dulwich.tests import ( + TestCase, + ) +from dulwich.tests.utils import ( + build_commit_graph, + ) + + +class PorcelainTestCase(TestCase): + + def setUp(self): + super(TestCase, self).setUp() + repo_dir = tempfile.mkdtemp() + self.addCleanup(shutil.rmtree, repo_dir) + self.repo = Repo.init(repo_dir) + + +class ArchiveTests(PorcelainTestCase): + """Tests for the archive command.""" + + def test_simple(self): + c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1], [3, 1, 2]]) + self.repo.refs["refs/heads/master"] = c3.id + out = StringIO() + err = StringIO() + porcelain.archive(self.repo.path, "refs/heads/master", outstream=out, + errstream=err) + self.assertEquals("", err.getvalue()) + tf = tarfile.TarFile(fileobj=out) + self.addCleanup(tf.close) + self.assertEquals([], tf.getnames()) + + +class UpdateServerInfoTests(PorcelainTestCase): + + def test_simple(self): + c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1], + [3, 1, 2]]) + self.repo.refs["refs/heads/foo"] = c3.id + porcelain.update_server_info(self.repo.path) + self.assertTrue(os.path.exists(os.path.join(self.repo.controldir(), + 'info', 'refs'))) + + +class CommitTests(PorcelainTestCase): + + def test_simple(self): + c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1], + [3, 1, 2]]) + self.repo.refs["refs/heads/foo"] = c3.id + sha = porcelain.commit(self.repo.path, message="Some message") + self.assertTrue(type(sha) is str) + self.assertEquals(len(sha), 40) + + def test_custom_author(self): + c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1], + [3, 1, 2]]) + self.repo.refs["refs/heads/foo"] = c3.id + sha = porcelain.commit(self.repo.path, message="Some message", + author="Joe <joe@example.com>", committer="Bob <bob@example.com>") + self.assertTrue(type(sha) is str) + self.assertEquals(len(sha), 40) + + +class CloneTests(PorcelainTestCase): + + def test_simple_local(self): + c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1], + [3, 1, 2]]) + self.repo.refs["refs/heads/master"] = c3.id + target_path = tempfile.mkdtemp() + outstream = StringIO() + self.addCleanup(shutil.rmtree, target_path) + r = porcelain.clone(self.repo.path, target_path, outstream=outstream) + self.assertEquals(r.path, target_path) + self.assertEquals(Repo(target_path).head(), c3.id) + + +class InitTests(TestCase): + + def test_non_bare(self): + repo_dir = tempfile.mkdtemp() + self.addCleanup(shutil.rmtree, repo_dir) + porcelain.init(repo_dir) + + def test_bare(self): + repo_dir = tempfile.mkdtemp() + self.addCleanup(shutil.rmtree, repo_dir) + porcelain.init(repo_dir, bare=True) + + +class AddTests(PorcelainTestCase): + + def test_add_file(self): + f = open(os.path.join(self.repo.path, 'foo'), 'w') + try: + f.write("BAR") + finally: + f.close() + porcelain.add(self.repo.path, paths=["foo"]) + + +class RemoveTests(PorcelainTestCase): + + def test_remove_file(self): + f = open(os.path.join(self.repo.path, 'foo'), 'w') + try: + f.write("BAR") + finally: + f.close() + porcelain.add(self.repo.path, paths=["foo"]) + porcelain.rm(self.repo.path, paths=["foo"]) + + +class LogTests(PorcelainTestCase): + + def test_simple(self): + c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1], + [3, 1, 2]]) + self.repo.refs["HEAD"] = c3.id + outstream = StringIO() + porcelain.log(self.repo.path, outstream=outstream) + self.assertTrue(outstream.getvalue().startswith("-" * 50)) + + +class ShowTests(PorcelainTestCase): + + def test_simple(self): + c1, c2, c3 = build_commit_graph(self.repo.object_store, [[1], [2, 1], + [3, 1, 2]]) + self.repo.refs["HEAD"] = c3.id + outstream = StringIO() + porcelain.show(self.repo.path, committish=c3.id, outstream=outstream) + self.assertTrue(outstream.getvalue().startswith("-" * 50))