1 /* 2 * Copyright Sönke Ludwig 2014. 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.tree; 8 9 import git.object_; 10 import git.oid; 11 import git.repository; 12 import git.types; 13 import git.util; 14 import git.version_; 15 16 import deimos.git2.tree; 17 import deimos.git2.types; 18 19 import std.conv : to; 20 import std..string : toStringz; 21 import std.exception : enforce; 22 23 24 /// 25 GitTree lookupTree(GitRepo repo, GitOid id) 26 { 27 git_tree* tree; 28 require(git_tree_lookup(&tree, repo.cHandle, &id._get_oid()) == 0); 29 return GitTree(repo, tree); 30 } 31 32 /// 33 GitTree lookupTree(GitRepo repo, GitOid id, size_t id_length) 34 { 35 git_tree* tree; 36 require(git_tree_lookup_prefix(&tree, repo.cHandle, &id._get_oid(), id_length) == 0); 37 return GitTree(repo, tree); 38 } 39 40 GitTreeBuilder createTreeBuilder() 41 { 42 return GitTreeBuilder(false); 43 } 44 45 GitTreeBuilder createTreeBuilder(GitTree source) 46 { 47 return GitTreeBuilder(source); 48 } 49 50 51 /// 52 struct GitTree { 53 package this(GitRepo repo, git_tree* tree) 54 { 55 _object = GitObject(repo, cast(git_object*)tree); 56 } 57 58 this(GitObject object) 59 { 60 enforce(object.type == GitType.tree, "GIT object is not a tree."); 61 _object = object; 62 } 63 64 @property GitOid id() { return GitOid(*git_tree_id(this.cHandle)); } 65 @property GitRepo owner() { return _object.owner; } 66 67 @property size_t entryCount() { return git_tree_entrycount(this.cHandle); } 68 69 GitTreeEntry getEntryByName(string filename) 70 { 71 auto ret = git_tree_entry_byname(this.cHandle, filename.toStringz()); 72 enforce(ret !is null, "Couldn't find tree entry "~filename); // FIXME: use return value 73 return GitTreeEntry(this, ret); 74 } 75 76 GitTreeEntry getEntryByIndex(size_t index) 77 { 78 auto ret = git_tree_entry_byindex(this.cHandle, index); 79 enforce(ret !is null, "Couldn't find tree entry at "~index.to!string); // FIXME: use return value 80 return GitTreeEntry(this, ret); 81 } 82 83 GitTreeEntry getEntryByOid(GitOid oid) 84 { 85 auto ret = git_tree_entry_byoid(this.cHandle, &oid._get_oid()); 86 enforce(ret !is null, "Couldn't find tree entry "~oid.toHex()); // FIXME: use return value 87 return GitTreeEntry(this, ret); 88 } 89 90 GitTreeEntry getEntryByPath(string path) 91 { 92 git_tree_entry* ret; 93 require(git_tree_entry_bypath(&ret, this.cHandle, path.toStringz()) == 0); 94 return GitTreeEntry(this.owner, ret); 95 } 96 97 void walk(GitTreewalkMode mode, scope GitTreewalkDelegate del) 98 { 99 struct CTX { GitTreewalkDelegate del; GitTree tree; } 100 101 static extern(C) nothrow int callback(const(char)* root, const(git_tree_entry)* entry, void *payload) 102 { 103 auto ctx = cast(CTX*)payload; 104 try { 105 final switch (ctx.del(root.to!string(), GitTreeEntry(ctx.tree, entry))) { 106 case ContinueWalkSkip.yes: return 0; 107 case ContinueWalkSkip.no: return -1; 108 case ContinueWalkSkip.skip: return 1; 109 } 110 } catch (Exception e) return -1; 111 } 112 113 auto ctx = CTX(del, this); 114 require(git_tree_walk(this.cHandle, cast(git_treewalk_mode)mode, &callback, cast(void*)&ctx) == 0); 115 } 116 117 package @property inout(git_tree)* cHandle() inout { return cast(inout(git_tree)*)_object.cHandle; } 118 119 private GitObject _object; 120 } 121 122 123 /// 124 enum GitTreewalkMode { 125 pre = GIT_TREEWALK_PRE, 126 post = GIT_TREEWALK_POST 127 } 128 129 /// 130 alias GitTreewalkDelegate = ContinueWalkSkip delegate(string root, GitTreeEntry entry); 131 132 133 /// 134 struct GitTreeBuilder { 135 private this(bool dummy) 136 { 137 git_treebuilder* builder; 138 require(git_treebuilder_create(&builder, null) == 0); 139 _data = Data(builder); 140 } 141 142 private this(GitTree src) 143 { 144 git_treebuilder* builder; 145 require(git_treebuilder_create(&builder, src.cHandle) == 0); 146 _data = Data(builder); 147 } 148 149 @property size_t entryCount() { return git_treebuilder_entrycount(this.cHandle); } 150 151 void clear() { git_treebuilder_clear(this.cHandle); } 152 153 GitTreeEntry get(string filename) 154 { 155 return GitTreeEntry(this, git_treebuilder_get(this.cHandle, filename.toStringz())); 156 } 157 158 GitTreeEntry insert(string filename, GitOid id, GitFileModeType filemode) 159 { 160 const(git_tree_entry)* ret; 161 require(git_treebuilder_insert(&ret, this.cHandle, filename.toStringz(), &id._get_oid(), cast(git_filemode_t) filemode) == 0); 162 return GitTreeEntry(this, ret); 163 } 164 165 void remove(string filename) 166 { 167 require(git_treebuilder_remove(this.cHandle, filename.toStringz()) == 0); 168 } 169 170 // return false from the callback to remove the item 171 void filter(scope bool delegate(GitTreeEntry entry) del) 172 { 173 struct CTX { bool delegate(GitTreeEntry) del; GitTreeBuilder builder; bool exception; } 174 175 static extern(C) nothrow int callback(const(git_tree_entry)* entry, void *payload) 176 { 177 auto ctx = cast(CTX*)payload; 178 if (ctx.exception) return 0; 179 try { 180 return ctx.del(GitTreeEntry(ctx.builder, entry)) ? 0 : 1; 181 } catch (Exception e) { 182 ctx.exception = true; 183 return 0; 184 } 185 } 186 187 auto ctx = CTX(del, this, false); 188 git_treebuilder_filter(this.cHandle, &callback, cast(void*)&ctx); 189 } 190 191 GitOid write(GitRepo repo) 192 { 193 GitOid ret; 194 require(git_treebuilder_write(&ret._get_oid(), repo.cHandle, this.cHandle) == 0); 195 return ret; 196 } 197 198 199 mixin RefCountedGitObject!(git_treebuilder, git_treebuilder_free); 200 } 201 202 203 /// 204 struct GitTreeEntry { 205 package this(GitTree owner, const(git_tree_entry)* entry) 206 { 207 _tree = owner; 208 _repo = _tree.owner; 209 _entry = entry; 210 } 211 212 package this(GitTreeBuilder owner, const(git_tree_entry)* entry) 213 { 214 _builder = owner; 215 _tree = GitTree.init; 216 _repo = GitRepo.init; 217 _entry = entry; 218 } 219 220 package this(GitRepo owner, git_tree_entry* entry) 221 { 222 _tree = GitTree.init; 223 _repo = owner; 224 _data = Data(entry); 225 } 226 227 @property string name() { return git_tree_entry_name(cHandle()).to!string(); } 228 @property GitOid id() { return GitOid(*git_tree_entry_id(cHandle())); } 229 @property GitType type() { return cast(GitType)git_tree_entry_type(cHandle()); } 230 @property GitFileModeType fileMode() { return cast(GitFileModeType)git_tree_entry_filemode(cHandle()); } 231 232 static if (targetLibGitVersion >= VersionInfo(0, 20, 0)) 233 @property GitFileModeType fileModeRaw() { return cast(GitFileModeType)git_tree_entry_filemode_raw(cHandle()); } 234 235 @property GitTreeEntry dup() { return GitTreeEntry(_repo, git_tree_entry_dup(cHandle())); } 236 237 GitObject toObject() 238 { 239 git_object* ret; 240 require(git_tree_entry_to_object(&ret, _repo.cHandle, cHandle()) == 0); 241 return GitObject(_repo, ret); 242 } 243 244 int opCmp(GitTreeEntry other) { return git_tree_entry_cmp(cHandle(), other.cHandle()); } 245 bool opEquals(GitTreeEntry other) { return opCmp(other) == 0; } 246 247 248 package const(git_tree_entry)* cHandle() { return _entry ? _entry : _data._payload; } 249 250 mixin RefCountedGitObject!(git_tree_entry, git_tree_entry_free, false); 251 252 private: 253 // foreign ownership 254 GitTreeBuilder _builder; 255 GitTree _tree; 256 GitRepo _repo; 257 const(git_tree_entry)* _entry; 258 }