/** * Count the number of unique commits between two commit objects * * @param local The commit for local * @param upstream The commit for upstream * @return Integer vector of length two with the values ahead and * behind. */ SEXP git2r_graph_ahead_behind(SEXP local, SEXP upstream) { size_t ahead, behind; int err; SEXP result = R_NilValue; SEXP slot; git_oid local_oid; git_oid upstream_oid; git_repository *repository = NULL; if (git2r_arg_check_commit(local)) git2r_error(__func__, NULL, "'local'", git2r_err_commit_arg); if (git2r_arg_check_commit(upstream)) git2r_error(__func__, NULL, "'upstream'", git2r_err_commit_arg); slot = GET_SLOT(local, Rf_install("repo")); repository = git2r_repository_open(slot); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); slot = GET_SLOT(local, Rf_install("sha")); git2r_oid_from_sha_sexp(slot, &local_oid); slot = GET_SLOT(upstream, Rf_install("sha")); git2r_oid_from_sha_sexp(slot, &upstream_oid); err = git_graph_ahead_behind(&ahead, &behind, repository, &local_oid, &upstream_oid); if (err) goto cleanup; PROTECT(result = allocVector(INTSXP, 2)); INTEGER(result)[0] = ahead; INTEGER(result)[1] = behind; cleanup: if (repository) git_repository_free(repository); if (R_NilValue != result) UNPROTECT(1); if (err) git2r_error(__func__, giterr_last(), NULL, NULL); return result; }
/** * Determine if a commit is the descendant of another commit. * * @param commit A commit. * @param ancestor A potential ancestor commit. * @return TRUE or FALSE */ SEXP git2r_graph_descendant_of(SEXP commit, SEXP ancestor) { int err; SEXP slot; SEXP result = R_NilValue; git_oid commit_oid; git_oid ancestor_oid; git_repository *repository = NULL; if (git2r_arg_check_commit(commit)) git2r_error(__func__, NULL, "'commit'", git2r_err_commit_arg); if (git2r_arg_check_commit(ancestor)) git2r_error(__func__, NULL, "'ancestor'", git2r_err_commit_arg); slot = GET_SLOT(commit, Rf_install("repo")); repository = git2r_repository_open(slot); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); slot = GET_SLOT(commit, Rf_install("sha")); git2r_oid_from_sha_sexp(slot, &commit_oid); slot = GET_SLOT(ancestor, Rf_install("sha")); git2r_oid_from_sha_sexp(slot, &ancestor_oid); err = git_graph_descendant_of(repository, &commit_oid, &ancestor_oid); if (0 > err || 1 < err) goto cleanup; PROTECT(result = allocVector(LGLSXP, 1)); LOGICAL(result)[0] = err; err = GIT_OK; cleanup: if (repository) git_repository_free(repository); if (R_NilValue != result) UNPROTECT(1); if (err) git2r_error(__func__, giterr_last(), NULL, NULL); return result; }
/** * Parents of a commit * * @param commit S4 class git_commit * @return list of S4 class git_commit objects */ SEXP git2r_commit_parent_list(SEXP commit) { int err; size_t i, n; SEXP repo; SEXP list = R_NilValue; git_commit *commit_obj = NULL; git_repository *repository = NULL; if (git2r_arg_check_commit(commit)) git2r_error(git2r_err_commit_arg, __func__, "commit"); repo = GET_SLOT(commit, Rf_install("repo")); repository = git2r_repository_open(repo); if (!repository) git2r_error(git2r_err_invalid_repository, __func__, NULL); err = git2r_commit_lookup(&commit_obj, repository, commit); if (GIT_OK != err) goto cleanup; n = git_commit_parentcount(commit_obj); PROTECT(list = allocVector(VECSXP, n)); for (i = 0; i < n; i++) { git_commit *parent = NULL; SEXP item; err = git_commit_parent(&parent, commit_obj, i); if (GIT_OK != err) goto cleanup; SET_VECTOR_ELT(list, i, item = NEW_OBJECT(MAKE_CLASS("git_commit"))); git2r_commit_init(parent, repo, item); git_commit_free(parent); } cleanup: if (commit_obj) git_commit_free(commit_obj); if (repository) git_repository_free(repository); if (R_NilValue != list) UNPROTECT(1); if (GIT_OK != err) git2r_error(git2r_err_from_libgit2, __func__, giterr_last()->message); return list; }
/** * Get the tree pointed to by a commit * * @param commit S4 class git_commit or git_stash * @return S4 class git_tree */ SEXP git2r_commit_tree(SEXP commit) { int err; SEXP result = R_NilValue; SEXP repo; git_commit *commit_obj = NULL; git_repository *repository = NULL; git_tree *tree = NULL; if (git2r_arg_check_commit(commit)) git2r_error(git2r_err_commit_arg, __func__, "commit"); repo = GET_SLOT(commit, Rf_install("repo")); repository = git2r_repository_open(repo); if (!repository) git2r_error(git2r_err_invalid_repository, __func__, NULL); err = git2r_commit_lookup(&commit_obj, repository, commit); if (GIT_OK != err) goto cleanup; err = git_commit_tree(&tree, commit_obj); if (GIT_OK != err) goto cleanup; PROTECT(result = NEW_OBJECT(MAKE_CLASS("git_tree"))); git2r_tree_init((git_tree*)tree, repo, result); cleanup: if (commit_obj) git_commit_free(commit_obj); if (tree) git_tree_free(tree); if (repository) git_repository_free(repository); if (R_NilValue != result) UNPROTECT(1); if (GIT_OK != err) git2r_error(git2r_err_from_libgit2, __func__, giterr_last()->message); return result; }
/** * Make the repository HEAD directly point to the commit. * * @param commit S4 class git_commit * @return R_NilValue */ SEXP git2r_repository_set_head_detached(SEXP commit) { int err; SEXP sha; git_oid oid; git_commit *treeish = NULL; git_repository *repository = NULL; if (git2r_arg_check_commit(commit)) git2r_error(git2r_err_commit_arg, __func__, "commit"); repository = git2r_repository_open(GET_SLOT(commit, Rf_install("repo"))); if (!repository) git2r_error(git2r_err_invalid_repository, __func__, NULL); sha = GET_SLOT(commit, Rf_install("sha")); err = git_oid_fromstr(&oid, CHAR(STRING_ELT(sha, 0))); if (GIT_OK != err) goto cleanup; err = git_commit_lookup(&treeish, repository, &oid); if (GIT_OK != err) goto cleanup; err = git_repository_set_head_detached( repository, git_commit_id(treeish)); cleanup: if (treeish) git_commit_free(treeish); if (repository) git_repository_free(repository); if (GIT_OK != err) git2r_error(git2r_err_from_libgit2, __func__, giterr_last()->message); return R_NilValue; }
/** * Reset current HEAD to the specified state * * @param commit The commit to which the HEAD should be moved to. * @param reset_type Kind of reset operation to perform. 'soft' means * the Head will be moved to the commit. 'mixed' reset will trigger a * 'soft' reset, plus the index will be replaced with the content of * the commit tree. 'hard' reset will trigger a 'mixed' reset and the * working directory will be replaced with the content of the index. * @return R_NilValue */ SEXP git2r_reset(SEXP commit, SEXP reset_type) { int error; SEXP repo; git_commit *target = NULL; git_repository *repository = NULL; if (git2r_arg_check_commit(commit)) git2r_error(__func__, NULL, "'commit'", git2r_err_commit_arg); if (git2r_arg_check_integer(reset_type)) git2r_error(__func__, NULL, "'reset_type'", git2r_err_integer_arg); repo = git2r_get_list_element(commit, "repo"); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); error = git2r_commit_lookup(&target, repository, commit); if (error) goto cleanup; error = git_reset( repository, (git_object*)target, INTEGER(reset_type)[0], NULL); cleanup: git_commit_free(target); git_repository_free(repository); if (error) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return R_NilValue; }
/** * Find a merge base between two commits * * @param one One of the commits * @param two The other commit * @return The commit of a merge base between 'one' and 'two' * or NULL if not found */ SEXP git2r_merge_base(SEXP one, SEXP two) { int err; SEXP result = R_NilValue; SEXP repo; SEXP sha; git_oid oid; git_oid oid_one; git_oid oid_two; git_commit *commit = NULL; git_repository *repository = NULL; if (git2r_arg_check_commit(one)) git2r_error(__func__, NULL, "'one'", git2r_err_commit_arg); if (git2r_arg_check_commit(two)) git2r_error(__func__, NULL, "'two'", git2r_err_commit_arg); repo = GET_SLOT(one, Rf_install("repo")); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); sha = GET_SLOT(one, Rf_install("sha")); err = git_oid_fromstr(&oid_one, CHAR(STRING_ELT(sha, 0))); if (err) goto cleanup; sha = GET_SLOT(two, Rf_install("sha")); err = git_oid_fromstr(&oid_two, CHAR(STRING_ELT(sha, 0))); if (err) goto cleanup; err = git_merge_base(&oid, repository, &oid_one, &oid_two); if (err) { if (GIT_ENOTFOUND == err) err = GIT_OK; goto cleanup; } err = git_commit_lookup(&commit, repository, &oid); if (err) goto cleanup; PROTECT(result = NEW_OBJECT(MAKE_CLASS("git_commit"))); git2r_commit_init(commit, repo, result); cleanup: if (commit) git_commit_free(commit); if (repository) git_repository_free(repository); if (R_NilValue != result) UNPROTECT(1); if (err) git2r_error(__func__, giterr_last(), NULL, NULL); return result; }