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.branch;
8 
9 import git.commit;
10 import git.oid;
11 import git.reference;
12 import git.repository;
13 import git.types;
14 import git.util;
15 import git.version_;
16 
17 import deimos.git2.branch;
18 import deimos.git2.errors;
19 import deimos.git2.types;
20 
21 import std.conv : to;
22 import std.string : toStringz;
23 
24 
25 GitBranch createBranch(GitRepo repo, string name, GitCommit target, bool force)
26 {
27 	git_reference* ret;
28 	require(git_branch_create(&ret, repo.cHandle, name.toStringz, target.cHandle, force) == 0);
29 	return GitBranch(GitReference(repo, ret));
30 }
31 
32 GitBranch lookupBranch(GitRepo repo, string name, GitBranchType type)
33 {
34 	git_reference* ret;
35 	require(git_branch_lookup(&ret, repo.cHandle, name.toStringz(), cast(git_branch_t)type) == 0);
36 	return GitBranch(GitReference(repo, ret));
37 }
38 
39 void deleteBranch(GitBranch branch)
40 {
41 	require(git_branch_delete(branch.cHandle) == 0);
42 }
43 
44 void iterateBranches(GitRepo repo, GitBranchType types, scope BranchIterationDelegate del)
45 {
46 	static if (targetLibGitVersion == VersionInfo(0, 19, 0)) {
47 		static struct CTX { BranchIterationDelegate del; GitRepo repo; Exception e; }
48 		static extern(C) nothrow int callback(const(char)* name, git_branch_t type, void* payload) {
49 			auto ctx = cast(CTX*)payload;
50 			try {
51 				auto gtp = cast(GitBranchType)type;
52 				if (ctx.del(lookupBranch(ctx.repo, name.to!string, gtp), gtp) == ContinueWalk.no)
53 					return 1;
54 			} catch (Exception e) {
55 				ctx.e = e;
56 				return -1;
57 			}
58 			return 0;
59 		}
60 
61 		auto ctx = CTX(del, repo);
62 		auto ret = git_branch_foreach(repo.cHandle, cast(git_branch_t)types, &callback, &ctx);
63 		if (ret == GIT_EUSER) {
64 			if (ctx.e) throw ctx.e;
65 			else return;
66 		}
67 		require(ret == 0);
68 	} else {
69 		git_branch_iterator* it;
70 		require(git_branch_iterator_new(&it, repo.cHandle, cast(git_branch_t)types) == 0);
71 		scope (exit) git_branch_iterator_free(it);
72 		while (true) {
73 			git_reference* br;
74 			git_branch_t brtp;
75 			auto ret = git_branch_next(&br, &brtp, it);
76 			if (ret == GIT_ITEROVER) break;
77 			require(ret == 0);
78 			if (del(GitBranch(GitReference(repo, br)), cast(GitBranchType)brtp) == ContinueWalk.no)
79 				break;
80 		}
81 	}
82 }
83 
84 
85 alias BranchIterationDelegate = ContinueWalk delegate(GitBranch branch, GitBranchType type);
86 
87 
88 struct GitBranch {
89 	protected this(GitReference ref_)
90 	{
91 		_ref = ref_;
92 	}
93 
94 	@property inout(GitReference) reference() inout { return _ref; }
95 
96 	@property string name()
97 	{
98 		const(char)* ret;
99 		require(git_branch_name(&ret, this.cHandle) == 0);
100 		return ret.to!string();
101 	}
102 
103 	@property GitBranch upstream()
104 	{
105 		git_reference* ret;
106 		require(git_branch_upstream(&ret, this.cHandle) == 0);
107 		return GitBranch(GitReference(_ref.owner, ret));
108 	}
109 
110 	@property string upstreamName()
111 	{
112 		const(char)* branch_name;
113 		require(git_branch_name(&branch_name, this.cHandle) == 0);
114 		auto len = git_branch_upstream_name(null, 0, _ref.owner.cHandle, branch_name);
115 		require(len > 0);
116 		auto dst = new char[len];
117 		require(git_branch_upstream_name(dst.ptr, dst.length, _ref.owner.cHandle, branch_name) == len);
118 		return cast(immutable)dst[0 .. $-1]; // skip trailing 0
119 	}
120 
121 	@property void upstreamName(string name)
122 	{
123 		require(git_branch_set_upstream(_ref.cHandle, name.toStringz()) == 0);
124 	}
125 
126 	@property bool isHead() { return requireBool(git_branch_is_head(_ref.cHandle)); }
127 
128 	@property string remoteName()
129 	{
130 		const(char)* branch_name;
131 		require(git_branch_name(&branch_name, this.cHandle) == 0);
132 		auto len = git_branch_remote_name(null, 0, _ref.owner.cHandle, branch_name);
133 		require(len > 0);
134 		auto dst = new char[len];
135 		require(git_branch_remote_name(dst.ptr, dst.length, _ref.owner.cHandle, branch_name) == len);
136 		return cast(immutable)dst[0 .. $-1]; // skip trailing 0
137 	}
138 
139 	GitBranch move(string new_name, bool force)
140 	{
141 		git_reference* dst;
142 		require(git_branch_move(&dst, _ref.cHandle, new_name.toStringz(), force) == 0);
143 		return GitBranch(GitReference(_ref.owner, dst));
144 	}
145 
146 	alias reference this;
147 
148 private:
149 	GitReference _ref;
150 }