1 /* 2 * Copyright Andrej Mitrovic 2013. 3 * Distributed under the Boost Software License, Version 1.0. 4 * (See accompanying file LICENSE_1_0.txt or copy at 5 * http://www.boost.org/LICENSE_1_0.txt) 6 */ 7 module git.common; 8 9 // todo: port more of these from git.c.common 10 11 import std.array; 12 import std.conv; 13 import std.exception; 14 import std.stdio; 15 import std.string; 16 import std.traits; 17 18 import deimos.git2.common; 19 import deimos.git2.types; 20 21 import git.config; 22 import git.exception; 23 import git.types; 24 import git.util; 25 26 package 27 { 28 version(Windows) 29 enum GitPathSep = ";"; 30 else 31 enum GitPathSep = ":"; 32 } 33 34 /** 35 * The maximum length of a valid git path. 36 */ 37 enum MaxGitPathLen = GIT_PATH_MAX; 38 39 /** 40 * The string representation of the null object ID. 41 */ 42 enum GitOid_HexZero = GIT_OID_HEX_ZERO; 43 44 /** 45 The capabilities of libgit2. 46 */ 47 struct GitFeatures 48 { 49 /** 50 Libgit2 was compiled with thread support. Note that thread support is 51 still to be seen as a 'work in progress' - basic object lookups are 52 believed to be threadsafe, but other operations may not be. 53 */ 54 bool usesThreads; 55 56 /** 57 Libgit2 supports the https:// protocol. This requires the openssl 58 library to be found when compiling libgit2. 59 */ 60 bool usesSSL; 61 } 62 63 /** 64 Get the capabilities of the runtime $(D libgit2) library. 65 */ 66 GitFeatures getLibGitFeatures() 67 { 68 typeof(return) result; 69 70 int flags = git_libgit2_capabilities(); 71 72 if (flags | GIT_CAP_THREADS) 73 result.usesThreads = true; 74 75 if (flags | GIT_CAP_HTTPS) 76 result.usesSSL = true; 77 78 return result; 79 } 80 81 /// 82 unittest 83 { 84 auto features = getLibGitFeatures(); 85 if (features.usesSSL) { } 86 } 87 88 /** Memory caching mode for libgit2. */ 89 enum CacheMode 90 { 91 /// 92 disabled, 93 94 /// 95 enabled 96 } 97 98 /** 99 Static functions with which to query or set global libgit2 options. 100 */ 101 struct globalOptions 102 { 103 static: 104 /// Get the maximum mmap window size. 105 @property size_t mwindowSize() 106 { 107 typeof(return) result; 108 git_libgit2_opts(GIT_OPT_GET_MWINDOW_SIZE, &result); 109 return result; 110 } 111 112 /// Set the maximum mmap window size. 113 @property void mwindowSize(size_t size) 114 { 115 git_libgit2_opts(GIT_OPT_SET_MWINDOW_SIZE, size); 116 } 117 118 /// 119 unittest 120 { 121 auto oldSize = globalOptions.mwindowSize; 122 scope(exit) globalOptions.mwindowSize = oldSize; 123 124 globalOptions.mwindowSize = 1; 125 assert(globalOptions.mwindowSize == 1); 126 } 127 128 /// Get the maximum memory in bytes that will be mapped in total by the library. 129 @property size_t mwindowMappedLimit() 130 { 131 typeof(return) result; 132 git_libgit2_opts(GIT_OPT_GET_MWINDOW_MAPPED_LIMIT, &result); 133 return result; 134 } 135 136 /// Set the maximum amount of memory in bytes that can be mapped at any time by the library. 137 @property void mwindowMappedLimit(size_t limit) 138 { 139 git_libgit2_opts(GIT_OPT_SET_MWINDOW_MAPPED_LIMIT, limit); 140 } 141 142 /// 143 unittest 144 { 145 auto oldLimit = globalOptions.mwindowMappedLimit; 146 scope(exit) globalOptions.mwindowMappedLimit = oldLimit; 147 148 globalOptions.mwindowMappedLimit = 1; 149 assert(globalOptions.mwindowMappedLimit == 1); 150 } 151 152 /** 153 Get the search paths for a given level of config data. 154 155 $(B Note:) $(D configLevel) must be one of $(D GitConfigLevel.system), 156 $(D GitConfigLevel.xdg), or $(D GitConfigLevel.global). 157 */ 158 string[] getSearchPaths(GitConfigLevel configLevel) 159 { 160 int level = cast(int)configLevel; 161 char[MaxGitPathLen] buffer; 162 163 require(git_libgit2_opts(GIT_OPT_GET_SEARCH_PATH, level, &buffer, buffer.length) == 0); 164 return to!string(buffer.ptr).split(GitPathSep); 165 } 166 167 /** 168 Set the search paths for a given level of config data. 169 170 $(B Note:) Use the magic path $(B "$PATH") to include the old value 171 of the path. This is useful for prepending or appending paths. 172 173 $(B Note:) Passing a null or empty array of paths will reset the 174 paths to their defaults (based on environment variables). 175 176 $(B Note:) $(D configLevel) must be one of $(D GitConfigLevel.system), 177 $(D GitConfigLevel.xdg), or $(D GitConfigLevel.global). 178 */ 179 void setSearchPaths(GitConfigLevel configLevel, string[] paths) 180 { 181 int level = cast(int)configLevel; 182 const(char)* cPaths = paths.join(GitPathSep).gitStr(); 183 require(git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, level, cPaths) == 0); 184 } 185 186 /// 187 unittest 188 { 189 foreach (config; EnumMembers!GitConfigLevel) 190 { 191 if (config != GitConfigLevel.system 192 && config != GitConfigLevel.xdg 193 && config != GitConfigLevel.global) 194 { 195 assertThrown!GitException(getSearchPaths(config)); 196 assertThrown!GitException(setSearchPaths(config, [])); 197 continue; 198 } 199 else 200 { 201 auto oldPaths = getSearchPaths(config); 202 scope(exit) setSearchPaths(config, oldPaths); 203 204 auto newPaths = ["/foo", "$PATH", "/foo/bar"]; 205 setSearchPaths(config, newPaths); 206 207 auto chained = newPaths[0] ~ oldPaths ~ newPaths[2]; 208 assert(getSearchPaths(config) == chained); 209 210 setSearchPaths(config, []); 211 assert(getSearchPaths(config) == oldPaths); 212 } 213 } 214 } 215 216 /** 217 Set the maximum data size for the given type of object to be 218 considered eligible for caching in memory. Setting to value to 219 zero means that that type of object will not be cached. 220 221 Defaults to 0 for $(D GitType.blob) (i.e. won't cache blobs) and 4k 222 for $(D GitType.commit), $(D GitType.tree), and $(D GitType.tag). 223 */ 224 void setCacheObjectLimit(GitType type, size_t size) 225 { 226 require(git_libgit2_opts(GIT_OPT_SET_CACHE_OBJECT_LIMIT, cast(git_otype)type, size) == 0); 227 } 228 229 /// 230 unittest 231 { 232 setCacheObjectLimit(GitType.commit, 4096); 233 } 234 235 /** 236 Set the maximum total data size that will be cached in memory 237 across all repositories before libgit2 starts evicting objects 238 from the cache. This is a soft limit, in that the library might 239 briefly exceed it, but will start aggressively evicting objects 240 from cache when that happens. 241 242 The default cache size is 256Mb. 243 */ 244 void setCacheMaxSize(ptrdiff_t maxStorageBytes) 245 { 246 require(git_libgit2_opts(GIT_OPT_SET_CACHE_MAX_SIZE, maxStorageBytes) == 0); 247 } 248 249 /** Return the default cache size - 256Mb. */ 250 @property ptrdiff_t defaultCacheMaxSize() 251 { 252 return 256 * 1024 * 1024; 253 } 254 255 /// 256 unittest 257 { 258 setCacheMaxSize(defaultCacheMaxSize); 259 } 260 261 /** 262 Enable or disable caching completely. 263 264 Since caches are repository-specific, disabling the cache 265 cannot immediately clear all the cached objects, but each cache 266 will be cleared on the next attempt to update anything in it. 267 */ 268 void setCacheMode(CacheMode mode) 269 { 270 require(git_libgit2_opts(GIT_OPT_ENABLE_CACHING, cast(int)mode) == 0); 271 } 272 273 /// 274 unittest 275 { 276 setCacheMode(CacheMode.disabled); 277 setCacheMode(CacheMode.enabled); 278 } 279 280 /** The cache status of libgit2. */ 281 struct CacheMemory 282 { 283 /// current bytes in the cache. 284 ptrdiff_t currentSize; 285 286 /// the maximum bytes allowed in the cache. 287 ptrdiff_t maxSize; 288 } 289 290 /** Get the current status of the cache. */ 291 CacheMemory getCacheMemory() 292 { 293 ptrdiff_t current; 294 ptrdiff_t allowed; 295 require(git_libgit2_opts(GIT_OPT_GET_CACHED_MEMORY, ¤t, &allowed) == 0); 296 297 return CacheMemory(current, allowed); 298 } 299 300 /// 301 unittest 302 { 303 auto cache = getCacheMemory(); 304 } 305 306 /// alternate spelling 307 alias getCachedMemory = getCacheMemory; 308 }