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 +/