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