1 /*
2 * Copyright David Nadlinger 2014.
3 * Copyright Sönke Ludwig 2014.
4 * Distributed under the Boost Software License, Version 1.0.
5 * (See accompanying file LICENSE_1_0.txt or copy at
6 * http://www.boost.org/LICENSE_1_0.txt)
7 */
8 module git.remote;
9
10 import git.oid;
11 import git.repository;
12 import git.net;
13 import git.types;
14 import git.util;
15 import git.version_;
16
17 import deimos.git2.net;
18 import deimos.git2.oid;
19 import deimos.git2.remote;
20 import deimos.git2.strarray;
21 import deimos.git2.types;
22
23 import std.conv : to;
24 import std..string : toStringz;
25
26
27 string[] listRemotes(GitRepo repo)
28 {
29 git_strarray dst;
30 require(git_remote_list(&dst, repo.cHandle) == 0);
31 scope (exit) git_strarray_free(&dst);
32 auto ret = new string[dst.count];
33 foreach (i; 0 .. dst.count)
34 ret[i] = dst.strings[i].to!string();
35 return ret;
36 }
37
38
39 ///
40 enum GitDirection
41 {
42 ///
43 fetch = GIT_DIRECTION_FETCH,
44
45 ///
46 push = GIT_DIRECTION_PUSH
47 }
48
49 ///
50 enum GitRemoteAutotagOption
51 {
52 ///
53 automatic = 0,
54
55 ///
56 none = 1,
57
58 ///
59 all = 2
60 }
61
62 ///
63 enum GitRemoteCompletionType {
64 download = GIT_REMOTE_COMPLETION_DOWNLOAD,
65 indexing = GIT_REMOTE_COMPLETION_INDEXING,
66 error = GIT_REMOTE_COMPLETION_ERROR,
67 }
68
69 ///
70 struct GitRemoteCallbacks {
71 void delegate(string str) progress;
72 void delegate(GitRemoteCompletionType type) completion;
73 //void delegate(GitCred *cred, string url, string username_from_url, uint allowed_types) credentials;
74 TransferCallbackDelegate transferProgress;
75 }
76
77 alias GitUpdateTipsDelegate = void delegate(string refname, in ref GitOid a, in ref GitOid b);
78
79
80 ///
81 struct GitRemote
82 {
83 // Internal, see free-standing constructor functions below.
84 private this(GitRepo repo, git_remote* remote)
85 {
86 _repo = repo;
87 _data = Data(remote);
88 }
89
90 ///
91 void save()
92 {
93 require(git_remote_save(_data._payload) == 0);
94 }
95
96 ///
97 @property string name() const
98 {
99 return to!string(git_remote_name(_data._payload));
100 }
101
102 ///
103 @property string url() const
104 {
105 return to!string(git_remote_url(_data._payload));
106 }
107
108 ///
109 @property void name(in char[] url)
110 {
111 require(git_remote_set_url(_data._payload, url.gitStr) == 0);
112 }
113
114 ///
115 @property string pushURL() const
116 {
117 return to!string(git_remote_pushurl(_data._payload));
118 }
119
120 ///
121 @property void pushURL(in char[] url)
122 {
123 require(git_remote_set_pushurl(_data._payload, url.gitStr) == 0);
124 }
125
126 @property GitTransferProgress stats()
127 {
128 return GitTransferProgress(git_remote_stats(_data._payload));
129 }
130
131 @property GitRemoteAutotag autoTag() { return cast(GitRemoteAutotag)git_remote_autotag(this.cHandle); }
132 @property void autoTag(GitRemoteAutotag value) { git_remote_set_autotag(this.cHandle, cast(git_remote_autotag_option_t)value); }
133
134 void connect(GitDirection direction)
135 {
136 require(git_remote_connect(_data._payload, cast(git_direction)direction) == 0);
137 }
138
139 ///
140 @property bool connected()
141 {
142 return git_remote_connected(_data._payload) != 0;
143 }
144
145 ///
146 void stop()
147 {
148 git_remote_stop(_data._payload);
149 }
150
151 ///
152 void disconnect()
153 {
154 git_remote_disconnect(_data._payload);
155 }
156
157 ///
158 void download(TransferCallbackDelegate progressCallback)
159 {
160 GitRemoteCallbacks cb;
161 cb.transferProgress = progressCallback;
162 download(&cb);
163 }
164 ///
165 void download(GitRemoteCallbacks* callbacks = null)
166 {
167 assert(connected, "Must connect(GitDirection.push) before invoking download().");
168
169 GitRemoteCallbackCTX ctx;
170 ctx.cb = callbacks;
171
172 git_remote_callbacks gitcallbacks;
173 gitcallbacks.progress = &progress_cb;
174 gitcallbacks.completion = &completion_cb;
175 static if (targetLibGitVersion >= VersionInfo(0, 20, 0)) {
176 //gitcallbacks.credentials = &cred_acquire_cb;
177 gitcallbacks.transfer_progress = &transfer_progress_cb;
178 }
179 gitcallbacks.payload = cast(void*)&ctx;
180 require(git_remote_set_callbacks(_data._payload, &gitcallbacks) == 0);
181
182 static if (targetLibGitVersion == VersionInfo(0, 19, 0)) {
183 require(git_remote_download(_data._payload, &transfer_progress_cb, cast(void*)&ctx) == 0, ctx.ex);
184 } else {
185 require(git_remote_download(_data._payload) == 0, ctx.ex);
186 }
187 if (ctx.ex) throw ctx.ex;
188 }
189
190 void addFetch(string refspec) { require(git_remote_add_fetch(this.cHandle, refspec.toStringz) == 0); }
191
192 void updateTips(scope void delegate(string refname, in ref GitOid a, in ref GitOid b) updateTips)
193 {
194 static struct CTX { GitUpdateTipsDelegate updateTips; Exception e; }
195
196 static extern(C) nothrow int update_cb(const(char)* refname, const(git_oid)* a, const(git_oid)* b, void* payload)
197 {
198 auto ctx = cast(CTX*)payload;
199 if (ctx.updateTips) {
200 try {
201 auto ac = GitOid(*a);
202 auto bc = GitOid(*b);
203 ctx.updateTips(refname.to!string, ac, bc);
204 } catch (Exception e) {
205 ctx.e = e;
206 return -1;
207 }
208 }
209 return 0;
210 }
211
212 CTX ctx;
213 ctx.updateTips = updateTips;
214
215 git_remote_callbacks gitcallbacks;
216 gitcallbacks.update_tips = &update_cb;
217 gitcallbacks.payload = &ctx;
218 require(git_remote_set_callbacks(_data._payload, &gitcallbacks) == 0);
219 auto ret = git_remote_update_tips(_data._payload);
220 if (ctx.e) throw ctx.e;
221 require(ret == 0);
222 }
223
224 immutable(GitRemoteHead)[] ls()
225 {
226 static if (targetLibGitVersion == VersionInfo(0, 19, 0)) {
227 static struct CTX { immutable(GitRemoteHead)[] heads; }
228
229 static extern(C) int callback(git_remote_head* rhead, void* payload) {
230 auto ctx = cast(CTX*)payload;
231 ctx.heads ~= GitRemoteHead(rhead);
232 return 0;
233 }
234
235 CTX ctx;
236 require(git_remote_ls(this.cHandle, &callback, &ctx) == 0);
237 return ctx.heads;
238 } else {
239 const(git_remote_head)** heads;
240 size_t head_count;
241 require(git_remote_ls(&heads, &head_count, _data._payload) == 0);
242 auto ret = new GitRemoteHead[head_count];
243 foreach (i, ref rh; ret) ret[i] = GitRemoteHead(heads[i]);
244 return cast(immutable)ret;
245 }
246 }
247
248 mixin RefCountedGitObject!(git_remote, git_remote_free);
249 // Reference to the parent repository to keep it alive.
250 private GitRepo _repo;
251 }
252
253 ///
254 GitRemote createRemote(GitRepo repo, in char[] name, in char[] url)
255 {
256 git_remote* result;
257 require(git_remote_create(&result, repo.cHandle, name.gitStr, url.gitStr) == 0);
258 return GitRemote(repo, result);
259 }
260
261 ///
262 GitRemote createRemoteInMemory(GitRepo repo, in char[] fetch, in char[] url)
263 {
264 git_remote* result;
265 require(git_remote_create_inmemory(&result, repo.cHandle, fetch.gitStr, url.gitStr) == 0);
266 return GitRemote(repo, result);
267 }
268
269 ///
270 GitRemote loadRemote(GitRepo repo, in char[] name)
271 {
272 git_remote* result;
273 require(git_remote_load(&result, repo.cHandle, name.gitStr) == 0);
274 return GitRemote(repo, result);
275 }
276
277
278 private extern(C) nothrow {
279 static if (targetLibGitVersion == VersionInfo(0, 19, 0)) {
280 void progress_cb(const(char)* str, int len, void* payload)
281 {
282 auto ctx = cast(GitRemoteCallbackCTX*)payload;
283 if (ctx.cb && ctx.cb.progress) {
284 try ctx.cb.progress(str[0 .. len].idup);
285 catch (Exception e) {
286 ctx.ex = e;
287 }
288 }
289 }
290 } else {
291 int progress_cb(const(char)* str, int len, void* payload)
292 {
293 auto ctx = cast(GitRemoteCallbackCTX*)payload;
294 if (ctx.cb && ctx.cb.progress) {
295 try ctx.cb.progress(str[0 .. len].idup);
296 catch (Exception e) {
297 ctx.ex = e;
298 return -1;
299 }
300 }
301 return 0;
302 }
303 }
304
305 /*int cred_acquire_cb(git_cred** dst, const(char)* url, const(char)* username_from_url, uint allowed_types, void* payload)
306 {
307 auto ctx = cast(GitRemoteCallbackCTX)payload;
308 try ctx.cb.credentials(...);
309 catch (Exception e) {
310 ctx.ex = e;
311 return -1;
312 }
313 return 0;
314 }*/
315
316 int completion_cb(git_remote_completion_type type, void* payload)
317 {
318 auto ctx = cast(GitRemoteCallbackCTX*)payload;
319 if (ctx.cb && ctx.cb.completion) {
320 try ctx.cb.completion(cast(GitRemoteCompletionType)type);
321 catch (Exception e) {
322 ctx.ex = e;
323 return -1;
324 }
325 }
326 return 0;
327 }
328
329 int transfer_progress_cb(const(git_transfer_progress)* stats, void* payload)
330 {
331 auto ctx = cast(GitRemoteCallbackCTX*)payload;
332 if (ctx.cb && ctx.cb.transferProgress) {
333 try {
334 auto tp = GitTransferProgress(stats);
335 ctx.cb.transferProgress(tp);
336 } catch (Exception e) {
337 ctx.ex = e;
338 return -1;
339 }
340 }
341 return 0;
342 }
343 }
344
345 enum GitRemoteAutotag {
346 auto_ = GIT_REMOTE_DOWNLOAD_TAGS_AUTO,
347 none = GIT_REMOTE_DOWNLOAD_TAGS_NONE,
348 all = GIT_REMOTE_DOWNLOAD_TAGS_ALL
349 }
350
351 private struct GitRemoteCallbackCTX {
352 GitRemoteCallbacks* cb;
353 Exception ex;
354 }
355
356 /+ TODO: Port these.
357
358 alias git_remote_rename_problem_cb = int function(const(char)* problematic_refspec, void *payload);
359
360 int git_remote_get_fetch_refspecs(git_strarray *array, git_remote *remote);
361
362 int git_remote_add_push(git_remote *remote, const(char)* refspec);
363
364 int git_remote_get_push_refspecs(git_strarray *array, git_remote *remote);
365
366 void git_remote_clear_refspecs(git_remote *remote);
367
368 size_t git_remote_refspec_count(git_remote *remote);
369
370 const(git_refspec)* git_remote_get_refspec(git_remote *remote, size_t n);
371
372 int git_remote_remove_refspec(git_remote *remote, size_t n);
373
374 int git_remote_valid_url(const(char)* url);
375
376 int git_remote_supported_url(const(char)* url);
377
378 void git_remote_check_cert(git_remote *remote, int check);
379
380 void git_remote_set_cred_acquire_cb(
381 git_remote *remote,
382 git_cred_acquire_cb cred_acquire_cb,
383 void *payload);
384
385 int git_remote_set_transport(
386 git_remote *remote,
387 git_transport *transport);
388
389 enum git_remote_completion_type {
390 GIT_REMOTE_COMPLETION_DOWNLOAD,
391 GIT_REMOTE_COMPLETION_INDEXING,
392 GIT_REMOTE_COMPLETION_ERROR,
393 } ;
394
395 mixin _ExportEnumMembers!git_remote_completion_type;
396
397 struct git_remote_callbacks {
398 uint version_ = GIT_REMOTE_CALLBACKS_VERSION;
399 void function(const(char)* str, int len, void *data) progress;
400 int function(git_remote_completion_type type, void *data) completion;
401 int function(const(char)* refname, const(git_oid)* a, const(git_oid)* b, void *data) update_tips;
402 void *payload;
403 }
404
405 enum GIT_REMOTE_CALLBACKS_VERSION = 1;
406 enum git_remote_callbacks GIT_REMOTE_CALLBACKS_INIT = { GIT_REMOTE_CALLBACKS_VERSION };
407
408 int git_remote_set_callbacks(git_remote *remote, git_remote_callbacks *callbacks);
409
410 const(git_transfer_progress)* git_remote_stats(git_remote *remote);
411
412
413
414 int git_remote_rename(
415 git_remote *remote,
416 const(char)* new_name,
417 git_remote_rename_problem_cb callback,
418 void *payload);
419
420 int git_remote_update_fetchhead(git_remote *remote);
421
422 void git_remote_set_update_fetchhead(git_remote *remote, int value);
423
424 int git_remote_is_valid_name(const(char)* remote_name);
425
426 +/