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 git_remote_callbacks gitcallbacks; 170 gitcallbacks.progress = &progress_cb; 171 gitcallbacks.completion = &completion_cb; 172 static if (targetLibGitVersion >= VersionInfo(0, 20, 0)) { 173 //gitcallbacks.credentials = &cred_acquire_cb; 174 gitcallbacks.transfer_progress = &transfer_progress_cb; 175 } 176 gitcallbacks.payload = cast(void*)callbacks; 177 require(git_remote_set_callbacks(_data._payload, &gitcallbacks) == 0); 178 179 static if (targetLibGitVersion == VersionInfo(0, 19, 0)) { 180 require(git_remote_download(_data._payload, &transfer_progress_cb, cast(void*)callbacks) == 0); 181 } else { 182 require(git_remote_download(_data._payload) == 0); 183 } 184 } 185 186 void addFetch(string refspec) { require(git_remote_add_fetch(this.cHandle, refspec.toStringz) == 0); } 187 188 void updateTips(scope void delegate(string refname, in ref GitOid a, in ref GitOid b) updateTips) 189 { 190 static struct CTX { GitUpdateTipsDelegate updateTips; Exception e; } 191 192 static extern(C) nothrow int update_cb(const(char)* refname, const(git_oid)* a, const(git_oid)* b, void* payload) 193 { 194 auto ctx = cast(CTX*)payload; 195 if (ctx.updateTips) { 196 try { 197 auto ac = GitOid(*a); 198 auto bc = GitOid(*b); 199 ctx.updateTips(refname.to!string, ac, bc); 200 } catch (Exception e) { 201 ctx.e = e; 202 return -1; 203 } 204 } 205 return 0; 206 } 207 208 CTX ctx; 209 ctx.updateTips = updateTips; 210 211 git_remote_callbacks gitcallbacks; 212 gitcallbacks.update_tips = &update_cb; 213 gitcallbacks.payload = &ctx; 214 require(git_remote_set_callbacks(_data._payload, &gitcallbacks) == 0); 215 auto ret = git_remote_update_tips(_data._payload); 216 if (ctx.e) throw ctx.e; 217 require(ret == 0); 218 } 219 220 immutable(GitRemoteHead)[] ls() 221 { 222 static if (targetLibGitVersion == VersionInfo(0, 19, 0)) { 223 static struct CTX { immutable(GitRemoteHead)[] heads; } 224 225 static extern(C) int callback(git_remote_head* rhead, void* payload) { 226 auto ctx = cast(CTX*)payload; 227 ctx.heads ~= GitRemoteHead(rhead); 228 return 0; 229 } 230 231 CTX ctx; 232 require(git_remote_ls(this.cHandle, &callback, &ctx) == 0); 233 return ctx.heads; 234 } else { 235 const(git_remote_head)** heads; 236 size_t head_count; 237 require(git_remote_ls(&heads, &head_count, _data._payload) == 0); 238 auto ret = new GitRemoteHead[head_count]; 239 foreach (i, ref rh; ret) ret[i] = GitRemoteHead(heads[i]); 240 return cast(immutable)ret; 241 } 242 } 243 244 mixin RefCountedGitObject!(git_remote, git_remote_free); 245 // Reference to the parent repository to keep it alive. 246 private GitRepo _repo; 247 } 248 249 /// 250 GitRemote createRemote(GitRepo repo, in char[] name, in char[] url) 251 { 252 git_remote* result; 253 require(git_remote_create(&result, repo.cHandle, name.gitStr, url.gitStr) == 0); 254 return GitRemote(repo, result); 255 } 256 257 /// 258 GitRemote createRemoteInMemory(GitRepo repo, in char[] fetch, in char[] url) 259 { 260 git_remote* result; 261 require(git_remote_create_inmemory(&result, repo.cHandle, fetch.gitStr, url.gitStr) == 0); 262 return GitRemote(repo, result); 263 } 264 265 /// 266 GitRemote loadRemote(GitRepo repo, in char[] name) 267 { 268 git_remote* result; 269 require(git_remote_load(&result, repo.cHandle, name.gitStr) == 0); 270 return GitRemote(repo, result); 271 } 272 273 274 private extern(C) nothrow { 275 static if (targetLibGitVersion == VersionInfo(0, 19, 0)) { 276 void progress_cb(const(char)* str, int len, void* payload) 277 { 278 auto cbs = cast(GitRemoteCallbacks*)payload; 279 if (cbs && cbs.progress) { 280 try cbs.progress(str[0 .. len].idup); 281 catch (Exception e) {} // FIXME: store exception and skip calling the callback during the next iterations 282 } 283 } 284 } else { 285 int progress_cb(const(char)* str, int len, void* payload) 286 { 287 auto cbs = cast(GitRemoteCallbacks*)payload; 288 if (cbs && cbs.progress) { 289 try cbs.progress(str[0 .. len].idup); 290 catch (Exception e) return -1; // FIXME: store and rethrow exception 291 } 292 return 0; 293 } 294 } 295 296 /*int cred_acquire_cb(git_cred** dst, const(char)* url, const(char)* username_from_url, uint allowed_types, void* payload) 297 { 298 auto cbs = cast(GitRemoteCallbacks)payload; 299 try cbs.credentials(...); 300 catch (Exception e) return -1; // FIXME: store and rethrow exception 301 return 0; 302 }*/ 303 304 int completion_cb(git_remote_completion_type type, void* payload) 305 { 306 auto cbs = cast(GitRemoteCallbacks*)payload; 307 if (cbs && cbs.completion) { 308 try cbs.completion(cast(GitRemoteCompletionType)type); 309 catch (Exception e) return -1; // FIXME: store and rethrow exception 310 } 311 return 0; 312 } 313 314 int transfer_progress_cb(const(git_transfer_progress)* stats, void* payload) 315 { 316 auto cbs = cast(GitRemoteCallbacks*)payload; 317 if (cbs && cbs.transferProgress) { 318 try { 319 auto tp = GitTransferProgress(stats); 320 cbs.transferProgress(tp); 321 } catch (Exception e) return -1; // FIXME: store and rethrow exception 322 } 323 return 0; 324 } 325 } 326 327 enum GitRemoteAutotag { 328 auto_ = GIT_REMOTE_DOWNLOAD_TAGS_AUTO, 329 none = GIT_REMOTE_DOWNLOAD_TAGS_NONE, 330 all = GIT_REMOTE_DOWNLOAD_TAGS_ALL 331 } 332 333 334 /+ TODO: Port these. 335 336 alias git_remote_rename_problem_cb = int function(const(char)* problematic_refspec, void *payload); 337 338 int git_remote_get_fetch_refspecs(git_strarray *array, git_remote *remote); 339 340 int git_remote_add_push(git_remote *remote, const(char)* refspec); 341 342 int git_remote_get_push_refspecs(git_strarray *array, git_remote *remote); 343 344 void git_remote_clear_refspecs(git_remote *remote); 345 346 size_t git_remote_refspec_count(git_remote *remote); 347 348 const(git_refspec)* git_remote_get_refspec(git_remote *remote, size_t n); 349 350 int git_remote_remove_refspec(git_remote *remote, size_t n); 351 352 int git_remote_valid_url(const(char)* url); 353 354 int git_remote_supported_url(const(char)* url); 355 356 void git_remote_check_cert(git_remote *remote, int check); 357 358 void git_remote_set_cred_acquire_cb( 359 git_remote *remote, 360 git_cred_acquire_cb cred_acquire_cb, 361 void *payload); 362 363 int git_remote_set_transport( 364 git_remote *remote, 365 git_transport *transport); 366 367 enum git_remote_completion_type { 368 GIT_REMOTE_COMPLETION_DOWNLOAD, 369 GIT_REMOTE_COMPLETION_INDEXING, 370 GIT_REMOTE_COMPLETION_ERROR, 371 } ; 372 373 mixin _ExportEnumMembers!git_remote_completion_type; 374 375 struct git_remote_callbacks { 376 uint version_ = GIT_REMOTE_CALLBACKS_VERSION; 377 void function(const(char)* str, int len, void *data) progress; 378 int function(git_remote_completion_type type, void *data) completion; 379 int function(const(char)* refname, const(git_oid)* a, const(git_oid)* b, void *data) update_tips; 380 void *payload; 381 } 382 383 enum GIT_REMOTE_CALLBACKS_VERSION = 1; 384 enum git_remote_callbacks GIT_REMOTE_CALLBACKS_INIT = { GIT_REMOTE_CALLBACKS_VERSION }; 385 386 int git_remote_set_callbacks(git_remote *remote, git_remote_callbacks *callbacks); 387 388 const(git_transfer_progress)* git_remote_stats(git_remote *remote); 389 390 391 392 int git_remote_rename( 393 git_remote *remote, 394 const(char)* new_name, 395 git_remote_rename_problem_cb callback, 396 void *payload); 397 398 int git_remote_update_fetchhead(git_remote *remote); 399 400 void git_remote_set_update_fetchhead(git_remote *remote, int value); 401 402 int git_remote_is_valid_name(const(char)* remote_name); 403 404 +/