/** * Fetch new data and update tips * * @param repo S4 class git_repository * @param name The name of the remote to fetch from * @param credentials The credentials for remote repository access. * @param msg The one line long message to be appended to the reflog * @return R_NilValue */ SEXP git2r_remote_fetch( SEXP repo, SEXP name, SEXP credentials, SEXP msg) { int err; SEXP result = R_NilValue; const git_transfer_progress *stats; git_remote *remote = NULL; git_repository *repository = NULL; git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT; if (git2r_arg_check_string(name)) git2r_error(git2r_err_string_arg, __func__, "name"); if (git2r_arg_check_credentials(credentials)) git2r_error(git2r_err_credentials_arg, __func__, "credentials"); if (git2r_arg_check_string(msg)) git2r_error(git2r_err_string_arg, __func__, "msg"); repository = git2r_repository_open(repo); if (!repository) git2r_error(git2r_err_invalid_repository, __func__, NULL); err = git_remote_lookup(&remote, repository, CHAR(STRING_ELT(name, 0))); if (GIT_OK != err) goto cleanup; callbacks.credentials = &git2r_cred_acquire_cb; callbacks.payload = credentials; err = git_remote_set_callbacks(remote, &callbacks); if (GIT_OK != err) goto cleanup; err = git_remote_fetch(remote, NULL, CHAR(STRING_ELT(msg, 0))); if (GIT_OK != err) goto cleanup; stats = git_remote_stats(remote); PROTECT(result = NEW_OBJECT(MAKE_CLASS("git_transfer_progress"))); git2r_transfer_progress_init(stats, result); cleanup: if (remote) { if (git_remote_connected(remote)) git_remote_disconnect(remote); git_remote_free(remote); } 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; }
/** * Give the remote a new name * * @param repo S4 class git_repository * @param oldname The old name of the remote * @param newname The new name of the remote * @return R_NilValue */ SEXP git2r_remote_rename(SEXP repo, SEXP oldname, SEXP newname) { int err; git_strarray problems = {0}; git_repository *repository = NULL; if (git2r_arg_check_string(oldname)) git2r_error(__func__, NULL, "'oldname'", git2r_err_string_arg); if (git2r_arg_check_string(newname)) git2r_error(__func__, NULL, "'newname'", git2r_err_string_arg); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); err = git_remote_rename( &problems, repository, CHAR(STRING_ELT(oldname, 0)), CHAR(STRING_ELT(newname, 0))); if (err) goto cleanup; git_strarray_free(&problems); cleanup: if (repository) git_repository_free(repository); if (err) git2r_error(__func__, giterr_last(), NULL, NULL); return R_NilValue; }
/** * Give the remote a new name * * @param repo S4 class git_repository * @param oldname The old name of the remote * @param newname The new name of the remote * @return R_NilValue */ SEXP git2r_remote_rename(SEXP repo, SEXP oldname, SEXP newname) { int err; git_strarray problems = {0}; git_repository *repository = NULL; if (git2r_arg_check_string(oldname)) git2r_error(git2r_err_string_arg, __func__, "oldname"); if (git2r_arg_check_string(newname)) git2r_error(git2r_err_string_arg, __func__, "newname"); repository = git2r_repository_open(repo); if (!repository) git2r_error(git2r_err_invalid_repository, __func__, NULL); err = git_remote_rename( &problems, repository, CHAR(STRING_ELT(oldname, 0)), CHAR(STRING_ELT(newname, 0))); if (GIT_OK != err) goto cleanup; git_strarray_free(&problems); cleanup: if (repository) git_repository_free(repository); if (GIT_OK != err) git2r_error(git2r_err_from_libgit2, __func__, giterr_last()->message); return R_NilValue; }
/** * Add a remote with the default fetch refspec to the repository's * configuration. * * @param repo S4 class git_repository * @param name The name of the remote * @param url The url of the remote * @return R_NilValue */ SEXP git2r_remote_add(SEXP repo, SEXP name, SEXP url) { int err; git_repository *repository = NULL; git_remote *remote = NULL; if (git2r_arg_check_string(name)) git2r_error(__func__, NULL, "'name'", git2r_err_string_arg); if (git2r_arg_check_string(url)) git2r_error(__func__, NULL, "'url'", git2r_err_string_arg); if (!git_remote_is_valid_name(CHAR(STRING_ELT(name, 0)))) git2r_error(__func__, NULL, git2r_err_invalid_remote, NULL); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); err = git_remote_create( &remote, repository, CHAR(STRING_ELT(name, 0)), CHAR(STRING_ELT(url, 0))); if (remote) git_remote_free(remote); if (repository) git_repository_free(repository); if (err) git2r_error(__func__, giterr_last(), NULL, NULL); return R_NilValue; }
/** * Set the SSL certificate-authority locations * * Either parameter may be 'NULL', but not both. * @param filename Location of a file containing several certificates * concatenated together. Default NULL. * @param path Location of a directory holding several certificates, * one per file. Default NULL. * @return NULL */ SEXP git2r_ssl_cert_locations(SEXP filename, SEXP path) { const char *f = NULL; const char *p = NULL; if (!Rf_isNull(filename)) { if (git2r_arg_check_string(filename)) git2r_error(__func__, NULL, "'filename'", git2r_err_string_arg); f = CHAR(STRING_ELT(filename, 0)); } if (!Rf_isNull(path)) { if (git2r_arg_check_string(path)) git2r_error(__func__, NULL, "'path'", git2r_err_string_arg); p = CHAR(STRING_ELT(path, 0)); } if (f == NULL && p == NULL) git2r_error(__func__, NULL, git2r_err_ssl_cert_locations, NULL); if (git_libgit2_opts(GIT_OPT_SET_SSL_CERT_LOCATIONS, f, p)) git2r_error(__func__, GIT2R_ERROR_LAST(), NULL, NULL); return R_NilValue; }
/** * Add a remote with the default fetch refspec to the repository's * configuration. * * @param repo S4 class git_repository * @param name The name of the remote * @param url The url of the remote * @return R_NilValue */ SEXP git2r_remote_add(SEXP repo, SEXP name, SEXP url) { int err; git_repository *repository = NULL; git_remote *remote = NULL; if (git2r_arg_check_string(name)) git2r_error(git2r_err_string_arg, __func__, "name"); if (git2r_arg_check_string(url)) git2r_error(git2r_err_string_arg, __func__, "url"); if (!git_remote_is_valid_name(CHAR(STRING_ELT(name, 0)))) git2r_error("Error in '%s': Invalid remote name", __func__, NULL); repository = git2r_repository_open(repo); if (!repository) git2r_error(git2r_err_invalid_repository, __func__, NULL); err = git_remote_create(&remote, repository, CHAR(STRING_ELT(name, 0)), CHAR(STRING_ELT(url, 0))); if (remote) git_remote_free(remote); if (repository) git_repository_free(repository); if (GIT_OK != err) git2r_error(git2r_err_from_libgit2, __func__, giterr_last()->message); return R_NilValue; }
/** * Check credentials argument * * @param arg the arg to check * @return 0 if OK, else 1 */ int git2r_arg_check_credentials(SEXP arg) { SEXP class_name; /* It's ok if the credentials is R_NilValue */ if (R_NilValue == arg) return 0; if (S4SXP != TYPEOF(arg)) return 1; class_name = getAttrib(arg, R_ClassSymbol); if (0 == strcmp(CHAR(STRING_ELT(class_name, 0)), "cred_plaintext")) { /* Check username and password */ if (git2r_arg_check_string(GET_SLOT(arg, Rf_install("username"))) || git2r_arg_check_string(GET_SLOT(arg, Rf_install("password")))) return 1; } else if (0 == strcmp(CHAR(STRING_ELT(class_name, 0)), "cred_ssh_key")) { SEXP passphrase; /* Check public and private key */ if (git2r_arg_check_string(GET_SLOT(arg, Rf_install("publickey"))) || git2r_arg_check_string(GET_SLOT(arg, Rf_install("privatekey")))) return 1; /* Check that passphrase is a character vector */ passphrase = GET_SLOT(arg, Rf_install("passphrase")); if (git2r_arg_check_string_vec(passphrase)) return 1; /* Check that length of passphrase < 2, i.e. it's either * character(0) or some "passphrase" */ switch (length(passphrase)) { case 0: break; case 1: if (NA_STRING == STRING_ELT(passphrase, 0)) return 1; break; default: return 1; } } else { return 1; } return 0; }
/** * Check branch argument * * @param arg the arg to check * @return 0 if OK, else 1 */ int git2r_arg_check_branch(SEXP arg) { SEXP class_name; SEXP slot; if (R_NilValue == arg || S4SXP != TYPEOF(arg)) return 1; class_name = getAttrib(arg, R_ClassSymbol); if (0 != strcmp(CHAR(STRING_ELT(class_name, 0)), "git_branch")) return 1; if (git2r_arg_check_string(GET_SLOT(arg, Rf_install("name")))) return 1; slot = GET_SLOT(arg, Rf_install("type")); if (git2r_arg_check_integer(slot)) return 1; switch (INTEGER(slot)[0]) { case GIT_BRANCH_LOCAL: case GIT_BRANCH_REMOTE: break; default: return 1; } return 0; }
/** * Check note argument * * @param arg the arg to check * @return 0 if OK, else 1 */ int git2r_arg_check_note(SEXP arg) { SEXP class_name; if (R_NilValue == arg || S4SXP != TYPEOF(arg)) return 1; class_name = getAttrib(arg, R_ClassSymbol); if (0 != strcmp(CHAR(STRING_ELT(class_name, 0)), "git_note")) return 1; if (git2r_arg_check_string(GET_SLOT(arg, Rf_install("hex")))) return 1; if (git2r_arg_check_string(GET_SLOT(arg, Rf_install("refname")))) return 1; return 0; }
/** * List all the notes within a specified namespace. * * @param repo S4 class git_repository * @param ref Optional reference to read from. * @return VECXSP with S4 objects of class git_note */ SEXP git2r_notes(SEXP repo, SEXP ref) { int err; SEXP result = R_NilValue; const char *notes_ref = NULL; git2r_note_foreach_cb_data cb_data = {0, R_NilValue, R_NilValue, NULL, NULL}; git_repository *repository = NULL; if (R_NilValue != ref) { if (git2r_arg_check_string(ref)) git2r_error(git2r_err_string_arg, __func__, "ref"); notes_ref = CHAR(STRING_ELT(ref, 0)); } repository = git2r_repository_open(repo); if (!repository) git2r_error(git2r_err_invalid_repository, __func__, NULL); if (NULL == notes_ref) { err = git_note_default_ref(¬es_ref, repository); if (GIT_OK != err) goto cleanup; } /* Count number of notes before creating the list */ err = git_note_foreach(repository, notes_ref, &git2r_note_foreach_cb, &cb_data); if (GIT_OK != err) { if (GIT_ENOTFOUND == err) { err = GIT_OK; PROTECT(result = allocVector(VECSXP, 0)); } goto cleanup; } PROTECT(result = allocVector(VECSXP, cb_data.n)); cb_data.n = 0; cb_data.list = result; cb_data.repo = repo; cb_data.repository = repository; cb_data.notes_ref = notes_ref; err = git_note_foreach(repository, notes_ref, &git2r_note_foreach_cb, &cb_data); cleanup: 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; }
/** * Check hex argument * * @param arg the arg to check * @return 0 if OK, else 1 */ int git2r_arg_check_hex(SEXP arg) { size_t len; if (git2r_arg_check_string(arg)) return 1; len = LENGTH(STRING_ELT(arg, 0)); if (len < GIT_OID_MINPREFIXLEN || len > GIT_OID_HEXSZ) return 1; return 0; }
/** * Check signature argument * * @param arg the arg to check * @return 0 if OK, else 1 */ int git2r_arg_check_signature(SEXP arg) { SEXP class_name; SEXP when; if (R_NilValue == arg || S4SXP != TYPEOF(arg)) return 1; class_name = getAttrib(arg, R_ClassSymbol); if (0 != strcmp(CHAR(STRING_ELT(class_name, 0)), "git_signature")) return 1; if (git2r_arg_check_string(GET_SLOT(arg, Rf_install("name"))) || git2r_arg_check_string(GET_SLOT(arg, Rf_install("email")))) return 1; when = GET_SLOT(arg, Rf_install("when")); if (check_real_arg(GET_SLOT(when, Rf_install("time"))) || check_real_arg(GET_SLOT(when, Rf_install("offset")))) return 1; return 0; }
/** * List the reflog within a specified reference. * * @param repo S4 class git_repository * @param ref Reference to read from. * @return VECXSP with S4 objects of class git_reflog */ SEXP git2r_reflog_list(SEXP repo, SEXP ref) { int err; size_t i, n; SEXP result = R_NilValue; git_reflog *reflog = NULL; git_repository *repository = NULL; if (git2r_arg_check_string(ref)) git2r_error(__func__, NULL, "'ref'", git2r_err_string_arg); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); err = git_reflog_read(&reflog, repository, CHAR(STRING_ELT(ref, 0))); if (err) goto cleanup; n = git_reflog_entrycount(reflog); PROTECT(result = allocVector(VECSXP, n)); for (i = 0; i < n; i++) { const git_reflog_entry *entry = git_reflog_entry_byindex(reflog, i); if (entry) { SEXP item; SET_VECTOR_ELT(result, i, item = NEW_OBJECT(MAKE_CLASS("git_reflog_entry"))); git2r_reflog_entry_init(entry, i, repo, ref, item); } } cleanup: if (reflog) git_reflog_free(reflog); if (repository) git_repository_free(repository); if (R_NilValue != result) UNPROTECT(1); if (err) git2r_error(__func__, giterr_last(), NULL, NULL); return result; }
/** * Init a repository. * * @param path A path to where to init a git repository * @param bare If TRUE, a Git repository without a working directory * is created at the pointed path. If FALSE, provided path will be * considered as the working directory into which the .git directory * will be created. * @return R_NilValue */ SEXP git2r_repository_init(SEXP path, SEXP bare) { int err; git_repository *repository = NULL; if (git2r_arg_check_string(path)) git2r_error(git2r_err_string_arg, __func__, "path"); if (git2r_arg_check_logical(bare)) git2r_error(git2r_err_logical_arg, __func__, "bare"); err = git_repository_init(&repository, CHAR(STRING_ELT(path, 0)), LOGICAL(bare)[0]); if (GIT_OK != err) git2r_error("Error in '%s': Unable to init repository", __func__, NULL); if (repository) git_repository_free(repository); return R_NilValue; }
/** * Make the repository HEAD point to the specified reference. * * @param repo S4 class git_repository * @param ref_name Canonical name of the reference the HEAD should point at * @return R_NilValue */ SEXP git2r_repository_set_head(SEXP repo, SEXP ref_name) { int err; git_repository *repository = NULL; if (git2r_arg_check_string(ref_name)) git2r_error(git2r_err_string_arg, __func__, "ref_name"); repository = git2r_repository_open(repo); if (!repository) git2r_error(git2r_err_invalid_repository, __func__, NULL); err = git_repository_set_head(repository, CHAR(STRING_ELT(ref_name, 0))); if (repository) git_repository_free(repository); if (GIT_OK != err) git2r_error(git2r_err_from_libgit2, __func__, giterr_last()->message); return R_NilValue; }
/** * Check if valid repository. * * @param path The path to the potential repository * @return TRUE if the repository can be opened else FALSE */ SEXP git2r_repository_can_open(SEXP path) { SEXP result; int can_open; git_repository *repository = NULL; if (git2r_arg_check_string(path)) git2r_error(git2r_err_string_arg, __func__, "path"); can_open = git_repository_open(&repository, CHAR(STRING_ELT(path, 0))); if (repository) git_repository_free(repository); PROTECT(result = allocVector(LGLSXP, 1)); if (0 != can_open) LOGICAL(result)[0] = 0; else LOGICAL(result)[0] = 1; UNPROTECT(1); return result; }
/** * Get repo slot from S4 class git_repository * * @param repo S4 class git_repository * @return a git_repository pointer on success else NULL */ git_repository* git2r_repository_open(SEXP repo) { SEXP class_name; SEXP path; git_repository *repository; if (R_NilValue == repo || S4SXP != TYPEOF(repo)) return NULL; class_name = getAttrib(repo, R_ClassSymbol); if (0 != strcmp(CHAR(STRING_ELT(class_name, 0)), "git_repository")) return NULL; path = GET_SLOT(repo, Rf_install("path")); if (git2r_arg_check_string(path)) return NULL; if (git_repository_open(&repository, CHAR(STRING_ELT(path, 0))) < 0) return NULL; return repository; }
/** * Remove an existing remote * * All remote-tracking branches and configuration settings for the * remote will be removed. * @param repo S4 class git_repository * @param name The name of the remote to remove * @return R_NilValue */ SEXP git2r_remote_remove(SEXP repo, SEXP name) { int err; git_repository *repository = NULL; if (git2r_arg_check_string(name)) git2r_error(__func__, NULL, "'name'", git2r_err_string_arg); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); err = git_remote_delete(repository, CHAR(STRING_ELT(name, 0))); if (repository) git_repository_free(repository); if (err) git2r_error(__func__, giterr_last(), NULL, NULL); return R_NilValue; }
/** * Find repository base path for given path * * @param path A character vector specifying the path to a file or folder * @return R_NilValue if repository cannot be found or * a character vector of length one with path to repository's git dir * e.g. /path/to/my/repo/.git */ SEXP git2r_repository_discover(SEXP path) { int err; SEXP result = R_NilValue; git_buf buf = GIT_BUF_INIT; if (git2r_arg_check_string(path)) git2r_error(git2r_err_string_arg, __func__, "path"); /* note that across_fs (arg #3) is set to 0 so this will stop when * a filesystem device change is detected while exploring parent * directories */ err = git_repository_discover(&buf, CHAR(STRING_ELT(path, 0)), 0, /* const char *ceiling_dirs */ NULL); if (GIT_OK != err) { /* NB just return R_NilValue if we can't discover the repo */ if (GIT_ENOTFOUND == err) err = GIT_OK; goto cleanup; } PROTECT(result = allocVector(STRSXP, 1)); SET_STRING_ELT(result, 0, mkChar(buf.ptr)); cleanup: git_buf_free(&buf); if (R_NilValue != result) UNPROTECT(1); if (GIT_OK != err) git2r_error(git2r_err_from_libgit2, __func__, giterr_last()->message); return result; }
/** * Find object specified by revision * * @param repo S4 class git_repository * @param revision The revision string, see * http://git-scm.com/docs/git-rev-parse.html#_specifying_revisions * @return S4 object of class git_commit, git_tag or git_tree. */ SEXP git2r_revparse_single(SEXP repo, SEXP revision) { int err; SEXP result = R_NilValue; git_repository *repository = NULL; git_object *treeish = NULL; if (git2r_arg_check_string(revision)) git2r_error(git2r_err_string_arg, __func__, "revision"); repository = git2r_repository_open(repo); if (!repository) git2r_error(git2r_err_invalid_repository, __func__, NULL); err = git_revparse_single( &treeish, repository, CHAR(STRING_ELT(revision, 0))); if (GIT_OK != err) goto cleanup; switch (git_object_type(treeish)) { case GIT_OBJ_COMMIT: PROTECT(result = NEW_OBJECT(MAKE_CLASS("git_commit"))); git2r_commit_init((git_commit*)treeish, repo, result); break; case GIT_OBJ_TAG: PROTECT(result = NEW_OBJECT(MAKE_CLASS("git_tag"))); git2r_tag_init((git_tag*)treeish, repo, result); break; case GIT_OBJ_TREE: PROTECT(result = NEW_OBJECT(MAKE_CLASS("git_tree"))); git2r_tree_init((git_tree*)treeish, repo, result); break; default: giterr_set_str(GITERR_NONE, git2r_err_revparse_single); err = GIT_ERROR; break; } cleanup: if (treeish) git_object_free(treeish); if (repository) git_repository_free(repository); if (R_NilValue != result) UNPROTECT(1); if (GIT_OK != err) { if (GIT_ENOTFOUND == err) { git2r_error( git2r_err_from_libgit2, __func__, "Requested object could not be found"); } else { git2r_error( git2r_err_from_libgit2, __func__, giterr_last()->message); } } return result; }
/** * Add a note for an object * * @param repo S4 class git_repository * @param sha The sha string of object * @param commit S4 class git_commit * @param message Content of the note to add * @param ref Canonical name of the reference to use * @param author Signature of the notes note author * @param committer Signature of the notes note committer * @param force Overwrite existing note * @return S4 class git_note */ SEXP git2r_note_create( SEXP repo, SEXP sha, SEXP message, SEXP ref, SEXP author, SEXP committer, SEXP force) { int err; SEXP result = R_NilValue; int overwrite = 0; git_oid note_oid; git_oid object_oid; git_signature *sig_author = NULL; git_signature *sig_committer = NULL; git_repository *repository = NULL; if (git2r_arg_check_sha(sha)) git2r_error(git2r_err_sha_arg, __func__, "sha"); if (git2r_arg_check_string(message)) git2r_error(git2r_err_string_arg, __func__, "message"); if (git2r_arg_check_string(ref)) git2r_error(git2r_err_string_arg, __func__, "ref"); if (git2r_arg_check_signature(author)) git2r_error(git2r_err_signature_arg, __func__, "author"); if (git2r_arg_check_signature(committer)) git2r_error(git2r_err_signature_arg, __func__, "committer"); if (git2r_arg_check_logical(force)) git2r_error(git2r_err_logical_arg, __func__, "force"); repository = git2r_repository_open(repo); if (!repository) git2r_error(git2r_err_invalid_repository, __func__, NULL); err = git2r_signature_from_arg(&sig_author, author); if (GIT_OK != err) goto cleanup; err = git2r_signature_from_arg(&sig_committer, committer); if (GIT_OK != err) goto cleanup; err = git_oid_fromstr(&object_oid, CHAR(STRING_ELT(sha, 0))); if (GIT_OK != err) goto cleanup; if (LOGICAL(force)[0]) overwrite = 1; err = git_note_create( ¬e_oid, repository, CHAR(STRING_ELT(ref, 0)), sig_author, sig_committer, &object_oid, CHAR(STRING_ELT(message, 0)), overwrite); if (GIT_OK != err) goto cleanup; PROTECT(result = NEW_OBJECT(MAKE_CLASS("git_note"))); err = git2r_note_init(¬e_oid, &object_oid, repository, CHAR(STRING_ELT(ref, 0)), repo, result); cleanup: if (sig_author) git_signature_free(sig_author); if (sig_committer) git_signature_free(sig_committer); 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; }
/** * Commit * * @param repo S4 class git_repository * @param message The message for the commit * @param author S4 class git_signature * @param committer S4 class git_signature * @return S4 class git_commit */ SEXP git2r_commit( SEXP repo, SEXP message, SEXP author, SEXP committer) { int err; SEXP result = R_NilValue; git_signature *c_author = NULL; git_signature *c_committer = NULL; git_index *index = NULL; git_oid oid; git_repository *repository = NULL; git_commit *commit = NULL; if (git2r_arg_check_string(message)) git2r_error(git2r_err_string_arg, __func__, "message"); if (git2r_arg_check_signature(author)) git2r_error(git2r_err_signature_arg, __func__, "author"); if (git2r_arg_check_signature(committer)) git2r_error(git2r_err_signature_arg, __func__, "committer"); repository = git2r_repository_open(repo); if (!repository) git2r_error(git2r_err_invalid_repository, __func__, NULL); err = git2r_signature_from_arg(&c_author, author); if (GIT_OK != err) goto cleanup; err = git2r_signature_from_arg(&c_committer, committer); if (GIT_OK != err) goto cleanup; err = git2r_any_changes_in_index(repository); if (GIT_OK != err) goto cleanup; err = git_repository_index(&index, repository); if (GIT_OK != err) goto cleanup; err = git2r_commit_create( &oid, repository, index, CHAR(STRING_ELT(message, 0)), c_author, c_committer); if (GIT_OK != err) goto cleanup; err = git_commit_lookup(&commit, repository, &oid); if (GIT_OK != err) goto cleanup; PROTECT(result = NEW_OBJECT(MAKE_CLASS("git_commit"))); git2r_commit_init(commit, repo, result); cleanup: if (c_author) git_signature_free(c_author); if (c_committer) git_signature_free(c_committer); if (index) git_index_free(index); if (repository) git_repository_free(repository); if (commit) git_commit_free(commit); if (R_NilValue != result) UNPROTECT(1); if (GIT_OK != err) git2r_error(git2r_err_from_libgit2, __func__, giterr_last()->message); return result; }
/** * Fetch new data and update tips * * @param repo S4 class git_repository * @param name The name of the remote to fetch from * @param credentials The credentials for remote repository access. * @param msg The one line long message to be appended to the reflog * @param verbose Print information each time a reference is updated locally. * @param refspecs The refspecs to use for this fetch. Pass R_NilValue * to use the base refspecs. * @return R_NilValue */ SEXP git2r_remote_fetch( SEXP repo, SEXP name, SEXP credentials, SEXP msg, SEXP verbose, SEXP refspecs) { int err; SEXP result = R_NilValue; const git_transfer_progress *stats; git_remote *remote = NULL; git_repository *repository = NULL; git_fetch_options fetch_opts = GIT_FETCH_OPTIONS_INIT; git2r_transfer_data payload = GIT2R_TRANSFER_DATA_INIT; git_strarray refs = {0}; if (git2r_arg_check_string(name)) git2r_error(__func__, NULL, "'name'", git2r_err_string_arg); if (git2r_arg_check_credentials(credentials)) git2r_error(__func__, NULL, "'credentials'", git2r_err_credentials_arg); if (git2r_arg_check_string(msg)) git2r_error(__func__, NULL, "'msg'", git2r_err_string_arg); if (git2r_arg_check_logical(verbose)) git2r_error(__func__, NULL, "'verbose'", git2r_err_logical_arg); if (refspecs != R_NilValue && git2r_arg_check_string_vec(refspecs)) git2r_error(__func__, NULL, "'refspecs'", git2r_err_string_vec_arg); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); err = git_remote_lookup(&remote, repository, CHAR(STRING_ELT(name, 0))); if (err) goto cleanup; if (refspecs != R_NilValue) { size_t i, len; /* Count number of non NA values */ len = length(refspecs); for (i = 0; i < len; i++) if (NA_STRING != STRING_ELT(refspecs, i)) refs.count++; if (refs.count) { /* Allocate the strings in refs */ refs.strings = malloc(refs.count * sizeof(char*)); if (!refs.strings) { giterr_set_str(GITERR_NONE, git2r_err_alloc_memory_buffer); err = GIT_ERROR; goto cleanup; } /* Populate the strings in refs */ for (i = 0; i < refs.count; i++) if (NA_STRING != STRING_ELT(refspecs, i)) refs.strings[i] = (char *)CHAR(STRING_ELT(refspecs, i)); } } if (LOGICAL(verbose)[0]) payload.verbose = 1; payload.credentials = credentials; fetch_opts.callbacks.payload = &payload; fetch_opts.callbacks.credentials = &git2r_cred_acquire_cb; fetch_opts.callbacks.update_tips = &git2r_update_tips_cb; err = git_remote_fetch(remote, &refs, &fetch_opts, CHAR(STRING_ELT(msg, 0))); if (err) goto cleanup; stats = git_remote_stats(remote); PROTECT(result = NEW_OBJECT(MAKE_CLASS("git_transfer_progress"))); git2r_transfer_progress_init(stats, result); cleanup: if (refs.strings) free(refs.strings); if (remote) { if (git_remote_connected(remote)) git_remote_disconnect(remote); git_remote_free(remote); } if (repository) git_repository_free(repository); if (R_NilValue != result) UNPROTECT(1); if (err) git2r_error( __func__, giterr_last(), git2r_err_unable_to_authenticate, NULL); return result; }
/** * Fetch new data and update tips * * @param repo S4 class git_repository * @param name The name of the remote to fetch from * @param credentials The credentials for remote repository access. * @param msg The one line long message to be appended to the reflog * @return R_NilValue */ SEXP git2r_remote_fetch( SEXP repo, SEXP name, SEXP credentials, SEXP msg) { int err; SEXP result = R_NilValue; const git_transfer_progress *stats; git_remote *remote = NULL; git_repository *repository = NULL; git_fetch_options fetch_opts = GIT_FETCH_OPTIONS_INIT; git2r_transfer_data payload = GIT2R_TRANSFER_DATA_INIT; if (git2r_arg_check_string(name)) git2r_error(__func__, NULL, "'name'", git2r_err_string_arg); if (git2r_arg_check_credentials(credentials)) git2r_error(__func__, NULL, "'credentials'", git2r_err_credentials_arg); if (git2r_arg_check_string(msg)) git2r_error(__func__, NULL, "'msg'", git2r_err_string_arg); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); err = git_remote_lookup(&remote, repository, CHAR(STRING_ELT(name, 0))); if (err) goto cleanup; payload.credentials = credentials; fetch_opts.callbacks.payload = &payload; fetch_opts.callbacks.credentials = &git2r_cred_acquire_cb; fetch_opts.callbacks.update_tips = &git2r_update_tips_cb; err = git_remote_fetch(remote, NULL, &fetch_opts, CHAR(STRING_ELT(msg, 0))); if (err) goto cleanup; stats = git_remote_stats(remote); PROTECT(result = NEW_OBJECT(MAKE_CLASS("git_transfer_progress"))); git2r_transfer_progress_init(stats, result); cleanup: if (remote) { if (git_remote_connected(remote)) git_remote_disconnect(remote); git_remote_free(remote); } if (repository) git_repository_free(repository); if (R_NilValue != result) UNPROTECT(1); if (err) git2r_error( __func__, giterr_last(), git2r_err_unable_to_authenticate, NULL); return result; }
/** * Get the remote's url * * Based on https://github.com/libgit2/libgit2/blob/babdc376c7/examples/network/ls-remote.c * @param repo S4 class git_repository * @param name Character vector with URL of remote. * @return Character vector for each reference with the associated commit IDs. */ SEXP git2r_remote_ls(SEXP name, SEXP repo, SEXP credentials) { const char *name_ = NULL; SEXP result = R_NilValue; SEXP names = R_NilValue; git_remote *remote = NULL; int err; const git_remote_head **refs; size_t refs_len, i; git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT; git2r_transfer_data payload = GIT2R_TRANSFER_DATA_INIT; git_repository *repository = NULL; if (git2r_arg_check_string(name)) git2r_error(__func__, NULL, "'name'", git2r_err_string_arg); if (git2r_arg_check_credentials(credentials)) git2r_error(__func__, NULL, "'credentials'", git2r_err_credentials_arg); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); name_ = CHAR(STRING_ELT(name, 0)); err = git_remote_lookup(&remote, repository, name_); if (err) { err = git_remote_create_anonymous(&remote, repository, name_); if (err) goto cleanup; } payload.credentials = credentials; callbacks.payload = &payload; callbacks.credentials = &git2r_cred_acquire_cb; err = git_remote_connect(remote, GIT_DIRECTION_FETCH, &callbacks, NULL, NULL); if (err) goto cleanup; err = git_remote_ls(&refs, &refs_len, remote); if (err) goto cleanup; PROTECT(result = allocVector(STRSXP, refs_len)); setAttrib(result, R_NamesSymbol, names = allocVector(STRSXP, refs_len)); for (i = 0; i < refs_len; i++) { char oid[GIT_OID_HEXSZ + 1] = {0}; git_oid_fmt(oid, &refs[i]->oid); SET_STRING_ELT(result, i, mkChar(oid)); SET_STRING_ELT(names, i, mkChar(refs[i]->name)); } cleanup: if (repository) git_repository_free(repository); if (result != R_NilValue) UNPROTECT(1); if (err) git2r_error(__func__, giterr_last(), NULL, NULL); return(result); }
/** * Create tag targeting HEAD commit in repository. * * @param repo S4 class git_repository * @param name Name for the tag. * @param message The tag message. * @param tagger The tagger (author) of the tag * @return S4 object of class git_tag */ SEXP git2r_tag_create(SEXP repo, SEXP name, SEXP message, SEXP tagger) { SEXP result = R_NilValue; int err; git_oid oid; git_repository *repository = NULL; git_signature *sig_tagger = NULL; git_tag *tag = NULL; git_object *target = NULL; if (git2r_arg_check_string(name)) git2r_error(__func__, NULL, "'name'", git2r_err_string_arg); if (git2r_arg_check_string(message)) git2r_error(__func__, NULL, "'message'", git2r_err_string_arg); if (git2r_arg_check_signature(tagger)) git2r_error(__func__, NULL, "'tagger'", git2r_err_signature_arg); repository = git2r_repository_open(repo); if (!repository) git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL); err = git2r_signature_from_arg(&sig_tagger, tagger); if (err) goto cleanup; err = git_revparse_single(&target, repository, "HEAD^{commit}"); if (err) goto cleanup; err = git_tag_create( &oid, repository, CHAR(STRING_ELT(name, 0)), target, sig_tagger, CHAR(STRING_ELT(message, 0)), 0); if (err) goto cleanup; err = git_tag_lookup(&tag, repository, &oid); if (err) goto cleanup; PROTECT(result = NEW_OBJECT(MAKE_CLASS("git_tag"))); git2r_tag_init(tag, repo, result); cleanup: if (tag) git_tag_free(tag); if (sig_tagger) git_signature_free(sig_tagger); if (target) git_object_free(target); if (repository) git_repository_free(repository); if (R_NilValue != result) UNPROTECT(1); if (err) git2r_error(__func__, giterr_last(), NULL, NULL); return result; }