Initial refactoring of the version control api ##rvc

This commit is contained in:
pancake 2022-11-14 14:10:57 +01:00 committed by pancake
parent b680675d16
commit b2ff7de0f0
10 changed files with 1678 additions and 1616 deletions

View File

@ -42,7 +42,7 @@ R_API void r_project_close(RProject *p) {
R_FREE (p->name);
R_FREE (p->path);
if (p->rvc) {
r_vc_close (p->rvc, true);
rvc_close (p->rvc, true);
p->rvc = NULL;
}
}

View File

@ -320,7 +320,7 @@ static bool r_core_project_load(RCore *core, const char *prj_name, const char *r
char *prj_path = r_file_dirname(rcpath);
if (prj_path) {
//check if the project uses git
Rvc *vc = rvc_git_open (prj_path);
Rvc *vc = rvc_open (prj_path, RVC_TYPE_GIT);
core->prj->rvc = vc;
free (prj_path);
} else {
@ -687,7 +687,7 @@ R_API bool r_core_project_save(RCore *core, const char *prj_name) {
if (core->prj->rvc || r_config_get_b (core->config, "prj.vc")) {
// assume that if the repo is not loaded, the repo doesn't exist
if (!core->prj->rvc) {
core->prj->rvc = rvc_init (prj_dir, RVC_TYPE_GIT);
core->prj->rvc = rvc_open (prj_dir, RVC_TYPE_GIT);
if (!core->prj->rvc) {
R_LOG_WARN ("Cannot initialize git repositorty");
free (prj_dir);
@ -700,13 +700,13 @@ R_API bool r_core_project_save(RCore *core, const char *prj_name) {
if (r_list_append (paths, prj_dir)) {
const char *author = r_config_get (core->config, "cfg.user");
const char *message = r_config_get (core->config, "prj.vc.message");
if (!rvc_git_commit (core->prj->rvc, message, author, paths)) {
if (!rvc_commit (core->prj->rvc, message, author, paths)) {
r_list_free (paths);
free (prj_dir);
free (script_path);
return false;
}
r_vc_save (core->prj->rvc);
rvc_save (core->prj->rvc);
} else {
r_list_free (paths);
free (prj_dir);

View File

@ -1616,7 +1616,7 @@ R_API void r_anal_class_list_vtables(RAnal *anal, const char *class_name);
R_API void r_anal_class_list_vtable_offset_functions(RAnal *anal, const char *class_name, ut64 offset);
R_API RGraph/*<RGraphNodeInfo>*/ *r_anal_class_get_inheritance_graph(RAnal *anal);
R_IPI RAnalEsilCFG *r_anal_esil_cfg_new(void);
R_API RAnalEsilCFG *r_anal_esil_cfg_new(void);
R_API RAnalEsilCFG *r_anal_esil_cfg_expr(RAnalEsilCFG *cfg, RAnal *anal, const ut64 off, char *expr);
R_API RAnalEsilCFG *r_anal_esil_cfg_op(RAnalEsilCFG *cfg, RAnal *anal, RAnalOp *op);
R_API void r_anal_esil_cfg_merge_blocks(RAnalEsilCFG *cfg);

View File

@ -1,4 +1,4 @@
/* radare - LGPL - Copyright 2021 - RHL120, pancake */
/* radare - LGPL - Copyright 2021-2022 - RHL120, pancake */
#ifndef R_RVC_H
#define R_RVC_H 1
@ -8,7 +8,7 @@
extern "C" {
#endif
#include <r_core.h>
#include <r_util.h>
#include <sdb.h>
typedef struct r_vc_blob_t {
@ -29,61 +29,60 @@ typedef struct r_vc_t {
const struct rvc_plugin_t *p;
} Rvc;
typedef bool (*RvcPluginBranch)(struct r_vc_t *rvc, const char *bname);
typedef bool (*RvcPluginCommit)(struct r_vc_t *rvc, const char *message, const char *author, const RList *files);
typedef bool (*RvcPluginCheckout)(struct r_vc_t *rvc, const char *bname);
typedef RList *(*RvcPluginBranches)(struct r_vc_t *rvc);
typedef void (*RvcPluginClose)(struct r_vc_t *vc, bool save);
typedef char *(*RvcPluginCurrentBranch)(struct r_vc_t *rvc);
typedef bool (*RvcPluginPrintCommits) (struct r_vc_t *rvc);
typedef RList *(*RvcPluginUncommited) (struct r_vc_t *rvc);
typedef bool (*RvcPluginReset)(struct r_vc_t *rvc);
typedef bool (*RvcPluginClone)(const struct r_vc_t *rvc, const char *dst);
typedef bool (*RvcPluginSave)(struct r_vc_t *vc);
typedef Rvc *(*RvcPluginOpen)(const char *path);
typedef struct rvc_plugin_t {
const char *name;
const char *author;
const char *desc;
const char *license;
const char *const name;
const char *const author;
const char *const desc;
const char *const license;
RvcType type;
bool (*commit)(struct r_vc_t *rvc, const char *message, const char *author, const RList *files);
bool (*branch)(struct r_vc_t *rvc, const char *bname);
bool (*checkout)(struct r_vc_t *rvc, const char *bname);
RList *(*get_branches) (struct r_vc_t *rvc);
RList *(*get_uncommitted) (struct r_vc_t *rvc);
bool (*print_commits) (struct r_vc_t *rvc);
char *(*current_branch)(struct r_vc_t *rvc);
bool (*reset)(struct r_vc_t *rvc);
bool (*clone)(const struct r_vc_t *rvc, const char *dst);
void (*close)(struct r_vc_t *vc, bool save);
bool (*save)(struct r_vc_t *vc);
RvcPluginCommit commit;
RvcPluginCheckout checkout;
RvcPluginBranch branch;
RvcPluginBranches branches;
RvcPluginCurrentBranch curbranch;
RvcPluginUncommited uncommited;
RvcPluginPrintCommits log;
RvcPluginReset reset;
RvcPluginClone clone;
RvcPluginClose close;
RvcPluginSave save;
RvcPluginOpen open;
} RvcPlugin;
R_API Rvc *rvc_init(const char *path, RvcType type);
R_API Rvc *rvc_open(const char *rp, RvcType type);
R_API void rvc_close(Rvc *vc, bool save);
R_API bool rvc_save(Rvc *vc);
R_API void rvc_free(Rvc *vc);
R_API Rvc *r_vc_git_init(const char *path);
R_API bool r_vc_git_branch(Rvc *vc, const char *name);
R_API bool r_vc_git_checkout(Rvc *vc, const char *name);
R_API bool r_vc_git_add(Rvc *vc, const RList *files);
R_API bool r_vc_git_commit(Rvc *rvc, const char *message, const char *author, const RList *files);
R_API Rvc *r_vc_git_open(const char *path);
R_API RList *rvc_branches(Rvc *vc);
R_API bool r_vc_commit(Rvc *rvc, const char *message, const char *author, const RList *files);
R_API bool r_vc_branch(Rvc *rvc, const char *bname);
R_API bool rvc_commit(Rvc *rvc, const char *message, const char *author, const RList *files);
R_API bool rvc_branch(Rvc *rvc, const char *bname);
R_API Rvc *r_vc_new(const char *path);
R_API bool r_vc_checkout(Rvc *rvc, const char *bname);
R_API RList *r_vc_get_branches(Rvc *rvc);
R_API RList *r_vc_get_uncommitted(Rvc *rvc);
R_API bool r_vc_log(Rvc *rvc);
R_API char *r_vc_current_branch(Rvc *rvc);
R_API bool r_vc_reset(Rvc *rvc);
R_API bool r_vc_clone(const Rvc *rvc, const char *dst);
R_API Rvc *r_vc_open(const char *rp, RvcType type);
R_API void r_vc_close(Rvc *vc, bool save);
R_API bool r_vc_save(Rvc *vc);
R_API void r_vc_free(Rvc *vc);
R_API RList *r_vc_git_get_branches(Rvc *rvc);
R_API RList *r_vc_git_get_uncommitted(Rvc *rvc);
R_API bool r_vc_git_log(Rvc *rvc);
R_API char *r_vc_git_current_branch(Rvc *rvc);
R_API bool r_vc_git_reset(Rvc *rvc);
R_API bool r_vc_git_clone(const Rvc *rvc, const char *dst);
R_API void r_vc_git_close(Rvc *vc, bool save);
R_API bool rvc_git_checkout(Rvc *rvc, const char *bname);
R_API Rvc *rvc_git_init(const char *path);
R_API Rvc *rvc_git_open(const char *path);
R_API bool r_vc_use(Rvc *vc, RvcType);
R_API bool rvc_use(Rvc *vc, RvcType);
R_API bool rvc_checkout(Rvc *vc, const char *bname);
R_API bool rvc_git_commit(Rvc *rvc, const char *message, const char *author, const RList *files);
R_API void rvc_git_close(struct r_vc_t *vc, bool save);
R_API RList *rvc_git_get_branches(Rvc *rvc);

View File

@ -22,7 +22,7 @@ static void help(void) {
" commit [message] [files...] commit the files with the message\n"
" checkout [branch] set the current branch to the given branch\n"
" status print a status message\n"
" reset remove all uncommitted changes\n"
" reset remove all uncommited changes\n"
" log print all commits\n"
" RAVC2_USER=[n] override cfg.user value to author commit\n"
);
@ -95,16 +95,16 @@ R_API int r_main_ravc2(int argc, const char **argv) {
if (opt.argc <= 2) {
R_LOG_ERROR ("Usage: ravc2 <git | rvc>");
} else if (!strcmp (opt.argv[opt.ind + 1], "git")) {
rvc = r_vc_git_init (rp);
rvc = rvc_open (rp, RVC_TYPE_GIT);
} else if (!strcmp (opt.argv[opt.ind + 1], "rvc")) {
rvc = r_vc_new (rp);
rvc = rvc_open (rp, RVC_TYPE_RVC);
} else {
R_LOG_ERROR ("unknown option %s", opt.argv[opt.ind + 1]);
}
free (rp);
return rvc? !r_vc_save (rvc) : 1;
return rvc? !rvc_save (rvc) : 1;
}
Rvc *rvc = rvc_git_open (rp);
Rvc *rvc = rvc_open (rp, RVC_TYPE_ANY);
if (!rvc) {
R_LOG_ERROR ("Invalid action or repository in %s", rp);
R_FREE (rp);
@ -115,13 +115,13 @@ R_API int r_main_ravc2(int argc, const char **argv) {
// commands that need Rvc *
if (!strcmp (action, "branch")) {
if (opt.argc <= 2) {
RList *branches = rvc_git_get_branches (rvc);
RList *branches = rvc_branches (rvc);
RListIter *iter;
char *branch;
r_list_foreach (branches, iter, branch) {
printf ("%s\n", branch);
}
r_list_free(branches);
r_list_free (branches);
} else {
// TODO: use api not plugin fields: rvc_branch (rvc, opt.argv[opt.ind + 1]);
save = rvc->p->branch (rvc, opt.argv[opt.ind + 1]);
@ -155,32 +155,33 @@ R_API int r_main_ravc2(int argc, const char **argv) {
free (message);
}
} else if (!strcmp (action, "checkout") && opt.argc > 2) {
save = rvc_git_checkout (rvc, opt.argv[opt.ind + 1]);
save = rvc_checkout (rvc, opt.argv[opt.ind + 1]);
} else if (!strcmp (action, "status")) {
char *current_branch = rvc->p->current_branch (rvc);
char *current_branch = rvc->p->curbranch (rvc);
if (current_branch) {
printf ("Branch: %s\n", current_branch);
RList *uncommitted = rvc->p->get_uncommitted (rvc);
if (r_list_empty (uncommitted)) {
RList *uncommited = rvc->p->uncommited (rvc);
if (r_list_empty (uncommited)) {
printf ("All files are committed\n");
} else {
printf ("The following files were NOT committed:\n");
RListIter *iter;
const char *file;
r_list_foreach (uncommitted, iter, file) {
r_list_foreach (uncommited, iter, file) {
printf ("%s\n", file);
}
}
r_list_free (uncommitted);
r_list_free (uncommited);
}
} else if (!strcmp (action, "reset")) {
save = rvc->p->reset (rvc);
} else if (!strcmp (action, "log")) {
save = rvc->p->print_commits (rvc);
save = rvc->p->log (rvc);
} else {
R_LOG_ERROR ("Incorrect command");
}
ret:
rvc_git_close (rvc, save);
rvc_close (rvc, save);
// rvc_git_close (rvc, save);
return !save;
}

View File

@ -5,7 +5,7 @@ CFLAGS+=-DR2_PLUGIN_INCORE -I$(TOP)/shlr
PCLIBS=@LIBZIP@ @DL_LIBS@
OBJS=mem.o unum.o str.o hex.o file.o range.o charset.o xdg.o rxml.o
OBJS+=prof.o cache.o sys.o buf.o sys_w32.o ubase64.o base85.o base91.o
OBJS+=list.o chmod.o graph.o event.o alloc.o donut.o print_code.o rvc.o
OBJS+=list.o chmod.o graph.o event.o alloc.o donut.o print_code.o
OBJS+=regex/regcomp.o regex/regerror.o regex/regexec.o uleb128.o rstr.o
OBJS+=sandbox.o calc.o thread.o thread_sem.o thread_lock.o thread_cond.o thread_chan.o
OBJS+=strpool.o bitmap.o time.o format.o pie.o print.o utype.o w32.o w32dw.o
@ -15,6 +15,7 @@ OBJS+=udiff.o bdiff.o stack.o queue.o tree.o idpool.o assert.o bplist.o
OBJS+=punycode.o pkcs7.o x509.o asn1.o asn1_str.o json_parser.o json_indent.o skiplist.o
OBJS+=pj.o rbtree.o intervaltree.o qrcode.o vector.o skyline.o str_constpool.o str_trim.o
OBJS+=ascii_table.o protobuf.o graph_drawable.o axml.o sstext.o new_rbtree.o token.o
OBJS+=rvc.o rvc_git.o rvc_rvc.o
ifeq (${HAVE_GPERF},1)
OBJS+=d/ascii.o

View File

@ -8,6 +8,8 @@ r_util_sources = [
'alloc.c',
'rxml.c',
'rvc.c',
'rvc_git.c',
'rvc_rvc.c',
'charset.c',
'donut.c',
'token.c',

File diff suppressed because it is too large Load Diff

305
libr/util/rvc_git.c Normal file
View File

@ -0,0 +1,305 @@
/* radare - LGPL - Copyright 2021-2022 - RHL120, pancake */
// GIT commands as APIs
#define R_LOG_ORIGIN "vc.git"
#include <rvc.h>
static Rvc *open_git(const char *path) {
char *git_path = r_file_new (path, ".git", NULL);
if (!git_path || !r_file_is_directory (git_path)) {
char *escpath = r_str_escape (path);
int ret = r_sys_cmdf ("git init \"%s\"", escpath);
free (escpath);
if (ret != 0) {
R_LOG_WARN ("git init failed");
}
if (!r_file_is_directory (git_path)) {
free (git_path);
return NULL;
}
}
free (git_path);
Rvc *vc = R_NEW (Rvc);
if (!vc) {
return NULL;
}
vc->path = r_str_new (path);
if (!vc->path) {
free (vc);
return NULL;
}
vc->db = NULL;
if (!rvc_use (vc, RVC_TYPE_GIT)) {
rvc_free (vc);
return NULL;
}
return vc;
}
static bool _git_branch(Rvc *vc, const char *name) {
char *escpath = r_str_escape (vc->path);
if (!escpath) {
return false;
}
char *escname = r_str_escape (name);
if (!escname) {
free (escpath);
return false;
}
int ret = r_sys_cmdf ("git -C \"%s\" branch \"%s\"", escpath, escname);
free (escpath);
free (escname);
return !ret;
}
static bool checkout_git(Rvc *vc, const char *name) {
char *escpath = r_str_escape (vc->path);
char *escname = r_str_escape (name);
int ret = r_sys_cmdf ("git -C \"%s\" checkout \"%s\"", escpath, escname);
free (escname);
free (escpath);
return !ret;
}
static bool add_git(Rvc *vc, const RList *files) {
RListIter *iter;
const char *fname;
char *cwd = r_sys_getdir ();
if (!cwd) {
return false;
}
if (!r_sys_chdir (vc->path)) {
free (cwd);
return false;
}
bool ret = true;
r_list_foreach (files, iter, fname) {
char *escfname = r_str_escape (fname);
if (!escfname) {
ret = false;
break;
}
ret &= !r_sys_cmdf ("git add \"%s\"", escfname);
free (escfname);
}
if (!r_sys_chdir (cwd)) {
free (cwd);
return false;
}
free (cwd);
return ret;
}
static bool commit_git(Rvc *vc, const char *_message, const char *author, const RList *files) {
char *message = _message? strdup (_message): NULL;
if (!add_git (vc, files)) {
return false;
}
char *escauth;
if (!author) {
char *user = r_sys_whoami ();
escauth = r_str_escape (user);
free (user);
} else {
escauth = r_str_escape (author);
}
if (!escauth) {
return false;
}
if (R_STR_ISEMPTY (message)) {
R_FREE (message);
message = strdup ("default message");
}
if (R_STR_ISEMPTY (message)) {
R_FREE (message);
char *epath = r_str_escape (vc->path);
if (epath) {
// XXX ensure CWD in the same line?
int res = r_sys_cmdf ("git -C \"%s\" commit --author \"%s <%s@localhost>\"", epath, escauth, escauth);
free (escauth);
free (epath);
return res == 0;
}
return false;
}
char *epath = r_str_escape (vc->path);
if (epath) {
char *emsg = r_str_escape (message);
if (emsg) {
int res = r_sys_cmdf ("git -C \"%s\" commit -m \"%s\" --author \"%s <%s@localhost>\"",
epath, emsg, escauth, escauth);
free (escauth);
free (message);
free (epath);
free (emsg);
return res == 0;
}
}
free (message);
return false;
}
#if 0
R_API RList *rvc_git_get_branches(Rvc *rvc) {
r_return_val_if_fail (rvc, NULL);
return rvc->p->get_branches (rvc);
}
static bool XXX_git_commit(Rvc *rvc, const char *message, const char *author, const RList *files) {
r_return_val_if_fail (rvc && message && author && files, false);
if (rvc->p->type == RVC_TYPE_RVC) {
#if 0
author = author? author : r_config_get (core->config, "cfg.user");
#endif
r_vc_commit (rvc, message, author, files);
return rvc_save (rvc);
}
return r_vc_git_commit (rvc, message, author, files);
}
#endif
#if 0
R_API bool r_vc_git_repo_exists(const RCore *core, const char *path) {
char *frp = !strcmp (r_config_get (core->config, "prj.vc.type"), "rvc")?
r_file_new (path, ".rvc", NULL):
r_file_new (path, ".git", NULL);
if (frp) {
bool ret = r_file_is_directory (frp);
free (frp);
return ret;
}
return false;
}
#endif
R_API RList *branches_git(Rvc *rvc) {
RList *ret = NULL;
char *esc_path = r_str_escape (rvc->path);
if (esc_path) {
char *output = r_sys_cmd_strf ("git -C %s branch --color=never", esc_path);
r_str_trim (output);
free (esc_path);
if (!R_STR_ISEMPTY (output)) {
ret = r_str_split_duplist (output, "\n", true);
RListIter *iter;
char *name;
r_list_foreach (ret, iter, name) {
if (*(char *)iter->data == '*') {
iter->data = r_str_new (name + 2);
free (name);
}
}
}
}
return ret;
}
static RList *uncommited_git(Rvc *rvc) {
RList *ret = NULL;
char *esc_path = r_str_escape (rvc->path);
if (esc_path) {
char *output = r_sys_cmd_strf ("git -C %s status --short",
esc_path);
free (esc_path);
if (!R_STR_ISEMPTY (output)) {
r_str_trim(output);
ret = r_str_split_duplist (output, "\n", true);
free (output);
RListIter *iter;
char *i;
r_list_foreach (ret, iter, i) {
//after we add one to the output, there maybe
//a space so trim that
char *ni = r_str_trim_dup (i + 2);
if (!ni) {
r_list_free (ret);
ret = NULL;
break;
}
free (i);
iter->data = ni;
}
} else {
ret = r_list_new ();
}
}
return ret;
}
static bool log_git(Rvc *rvc) {
bool ret = true;
char *esc_path = r_str_escape (rvc->path);
if (esc_path) {
ret = !r_sys_cmdf ("git -C %s log", esc_path);
free (esc_path);
}
return ret;
}
R_API char *curbranch_git(Rvc *rvc) {
char *ret = NULL;
char *esc_path = r_str_escape (rvc->path);
if (esc_path) {
char *branch = r_sys_cmd_strf ("git -C %s rev-parse --abbrev-ref HEAD", esc_path);
if (!R_STR_ISEMPTY (branch)) {
ret = r_str_ndup (branch, strlen (branch) - 1);
}
free (branch);
free (esc_path);
}
return ret;
}
static bool reset_git(Rvc *rvc) {
char *esc_path = r_str_escape (rvc->path);
if (esc_path) {
bool ret = r_sys_cmdf ("git -C %s checkout .", esc_path);
free (esc_path);
return !ret;
}
return false;
}
static bool clone_git(const Rvc *rvc, const char *dst) {
char *esc_src = r_str_escape (rvc->path);
char *esc_dst = r_str_escape (dst);
bool ret = false;
if (esc_src && esc_dst) {
ret = !r_sys_cmdf ("git clone %s %s", esc_src, esc_dst);
}
free (esc_src);
free (esc_dst);
return ret;
}
static void close_git(Rvc *vc, bool save) {
if (vc) {
free (vc->path);
free (vc);
}
}
R_API bool save_git(Rvc *vc) {
//do nothing, since git commands are automatically executed
return true;
}
const RvcPlugin r_vc_plugin_git = {
.name = "git",
.type = RVC_TYPE_GIT,
.commit = commit_git,
.branch = _git_branch,
.checkout = checkout_git,
.branches = branches_git,
.uncommited = uncommited_git,
.log = log_git,
.curbranch = curbranch_git,
.reset = reset_git,
.clone = clone_git,
.close = close_git,
.save = save_git,
// .init = init_git,
.open = open_git,
};

1235
libr/util/rvc_rvc.c Normal file

File diff suppressed because it is too large Load Diff