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 enum GitBranchType {
85 	local = GIT_BRANCH_LOCAL,
86 	remote = GIT_BRANCH_REMOTE
87 }
88 
89 alias BranchIterationDelegate = ContinueWalk delegate(GitBranch branch, GitBranchType type);
90 
91 struct GitBranch {
92 	protected this(GitReference ref_)
93 	{
94 		_ref = ref_;
95 	}
96 
97 	@property inout(GitReference) reference() inout { return _ref; }
98 
99 	@property string name()
100 	{
101 		const(char)* ret;
102 		require(git_branch_name(&ret, this.cHandle) == 0);
103 		return ret.to!string();
104 	}
105 
106 	@property GitBranch upstream()
107 	{
108 		git_reference* ret;
109 		require(git_branch_upstream(&ret, this.cHandle) == 0);
110 		return GitBranch(GitReference(_ref.owner, ret));
111 	}
112 
113 	@property string upstreamName()
114 	{
115 		const(char)* branch_name;
116 		require(git_branch_name(&branch_name, this.cHandle) == 0);
117 		auto len = git_branch_upstream_name(null, 0, _ref.owner.cHandle, branch_name);
118 		require(len > 0);
119 		auto dst = new char[len];
120 		require(git_branch_upstream_name(dst.ptr, dst.length, _ref.owner.cHandle, branch_name) == len);
121 		return cast(immutable)dst[0 .. $-1]; // skip trailing 0
122 	}
123 
124 	@property void upstreamName(string name)
125 	{
126 		require(git_branch_set_upstream(_ref.cHandle, name.toStringz()) == 0);
127 	}
128 
129 	@property bool isHead() { return requireBool(git_branch_is_head(_ref.cHandle)); }
130 
131 	@property string remoteName()
132 	{
133 		const(char)* branch_name;
134 		require(git_branch_name(&branch_name, this.cHandle) == 0);
135 		auto len = git_branch_remote_name(null, 0, _ref.owner.cHandle, branch_name);
136 		require(len > 0);
137 		auto dst = new char[len];
138 		require(git_branch_remote_name(dst.ptr, dst.length, _ref.owner.cHandle, branch_name) == len);
139 		return cast(immutable)dst[0 .. $-1]; // skip trailing 0
140 	}
141 
142 	GitBranch move(string new_name, bool force)
143 	{
144 		git_reference* dst;
145 		require(git_branch_move(&dst, _ref.cHandle, new_name.toStringz(), force) == 0);
146 		return GitBranch(GitReference(_ref.owner, dst));
147 	}
148 
149 	alias reference this;
150 
151 private:
152 	GitReference _ref;
153 }