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