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