1 /*
2 * git2r, R bindings to the libgit2 library.
3 * Copyright (C) 2013-2019 The git2r contributors
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License, version 2,
7 * as published by the Free Software Foundation.
8 *
9 * git2r is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19 #include <git2.h>
20
21 #include "git2r_arg.h"
22 #include "git2r_deprecated.h"
23 #include "git2r_error.h"
24 #include "git2r_oid.h"
25 #include "git2r_repository.h"
26 #include "git2r_S3.h"
27
28 /**
29 * Count the number of unique commits between two commit objects
30 *
31 * @param local The commit for local
32 * @param upstream The commit for upstream
33 * @return Integer vector of length two with the values ahead and
34 * behind.
35 */
git2r_graph_ahead_behind(SEXP local,SEXP upstream)36 SEXP git2r_graph_ahead_behind(SEXP local, SEXP upstream)
37 {
38 size_t ahead, behind;
39 int error, nprotect = 0;
40 SEXP result = R_NilValue;
41 SEXP local_repo, local_sha;
42 SEXP upstream_repo, upstream_sha;
43 git_oid local_oid, upstream_oid;
44 git_repository *repository = NULL;
45
46 if (git2r_arg_check_commit(local))
47 git2r_error(__func__, NULL, "'local'", git2r_err_commit_arg);
48 if (git2r_arg_check_commit(upstream))
49 git2r_error(__func__, NULL, "'upstream'", git2r_err_commit_arg);
50
51 local_repo = git2r_get_list_element(local, "repo");
52 upstream_repo = git2r_get_list_element(upstream, "repo");
53 if (git2r_arg_check_same_repo(local_repo, upstream_repo))
54 git2r_error(__func__, NULL, "'local' and 'upstream' not from same repository", NULL);
55
56 repository = git2r_repository_open(local_repo);
57 if (!repository)
58 git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL);
59
60 local_sha = git2r_get_list_element(local, "sha");
61 git2r_oid_from_sha_sexp(local_sha, &local_oid);
62
63 upstream_sha = git2r_get_list_element(upstream, "sha");
64 git2r_oid_from_sha_sexp(upstream_sha, &upstream_oid);
65
66 error = git_graph_ahead_behind(&ahead, &behind, repository, &local_oid,
67 &upstream_oid);
68 if (error)
69 goto cleanup;
70
71 PROTECT(result = Rf_allocVector(INTSXP, 2));
72 nprotect++;
73 INTEGER(result)[0] = ahead;
74 INTEGER(result)[1] = behind;
75
76 cleanup:
77 git_repository_free(repository);
78
79 if (nprotect)
80 UNPROTECT(nprotect);
81
82 if (error)
83 git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL);
84
85 return result;
86 }
87
88 /**
89 * Determine if a commit is the descendant of another commit.
90 *
91 * @param commit A commit.
92 * @param ancestor A potential ancestor commit.
93 * @return TRUE or FALSE
94 */
git2r_graph_descendant_of(SEXP commit,SEXP ancestor)95 SEXP git2r_graph_descendant_of(SEXP commit, SEXP ancestor)
96 {
97 int error, descendant_of = 0;
98 SEXP commit_repo, commit_sha;
99 SEXP ancestor_repo, ancestor_sha;
100 git_oid commit_oid, ancestor_oid;
101 git_repository *repository = NULL;
102
103 if (git2r_arg_check_commit(commit))
104 git2r_error(__func__, NULL, "'commit'", git2r_err_commit_arg);
105 if (git2r_arg_check_commit(ancestor))
106 git2r_error(__func__, NULL, "'ancestor'", git2r_err_commit_arg);
107
108 commit_repo = git2r_get_list_element(commit, "repo");
109 ancestor_repo = git2r_get_list_element(ancestor, "repo");
110 if (git2r_arg_check_same_repo(commit_repo, ancestor_repo))
111 git2r_error(__func__, NULL, "'commit' and 'ancestor' not from same repository", NULL);
112
113 repository = git2r_repository_open(commit_repo);
114 if (!repository)
115 git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL);
116
117 commit_sha = git2r_get_list_element(commit, "sha");
118 git2r_oid_from_sha_sexp(commit_sha, &commit_oid);
119
120 ancestor_sha = git2r_get_list_element(ancestor, "sha");
121 git2r_oid_from_sha_sexp(ancestor_sha, &ancestor_oid);
122
123 error = git_graph_descendant_of(repository, &commit_oid, &ancestor_oid);
124 if (0 > error || 1 < error)
125 goto cleanup;
126 descendant_of = error;
127 error = 0;
128
129 cleanup:
130 git_repository_free(repository);
131
132 if (error)
133 git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL);
134
135 return Rf_ScalarLogical(descendant_of);
136 }
137