diff --git a/c_src/geef.c b/c_src/geef.c index dd14ece..ca8f0d7 100644 --- a/c_src/geef.c +++ b/c_src/geef.c @@ -7,6 +7,7 @@ #include "tree.h" #include "blob.h" #include "library.h" +#include "revwalk.h" #include "geef.h" #include #include @@ -16,6 +17,7 @@ ErlNifResourceType *geef_repository_type; ErlNifResourceType *geef_odb_type; ErlNifResourceType *geef_ref_type; ErlNifResourceType *geef_object_type; +ErlNifResourceType *geef_revwalk_type; geef_atoms atoms; @@ -43,6 +45,10 @@ static int load(ErlNifEnv *env, void **priv, ERL_NIF_TERM load_info) geef_object_type = enif_open_resource_type(env, NULL, "object_type", geef_object_free, ERL_NIF_RT_CREATE, NULL); + + geef_revwalk_type = enif_open_resource_type(env, NULL, + "revwalk_type", geef_revwalk_free, ERL_NIF_RT_CREATE, NULL); + if (geef_repository_type == NULL) return -1; @@ -57,6 +63,10 @@ static int load(ErlNifEnv *env, void **priv, ERL_NIF_TERM load_info) atoms.tree = enif_make_atom(env, "tree"); atoms.blob = enif_make_atom(env, "blob"); atoms.tag = enif_make_atom(env, "tag"); + /* Revwalk */ + atoms.toposort = enif_make_atom(env, "sort_topo"); + atoms.timesort = enif_make_atom(env, "sort_time"); + atoms.reversesort = enif_make_atom(env, "sort_reverse"); return 0; } @@ -122,6 +132,11 @@ static ErlNifFunc geef_funcs[] = {"blob_size", 1, geef_blob_size}, {"blob_content", 1, geef_blob_content}, {"library_version", 0, geef_library_version}, + {"revwalk_new", 1, geef_revwalk_new}, + {"revwalk_push", 3, geef_revwalk_push}, + {"revwalk_next", 1, geef_revwalk_next}, + {"revwalk_sorting", 2, geef_revwalk_sorting}, + {"revwalk_reset", 1, geef_revwalk_reset}, }; ERL_NIF_INIT(geef, geef_funcs, load, NULL, upgrade, unload) diff --git a/c_src/geef.h b/c_src/geef.h index 8265f2f..263cc86 100644 --- a/c_src/geef.h +++ b/c_src/geef.h @@ -17,6 +17,9 @@ typedef struct { ERL_NIF_TERM tree; ERL_NIF_TERM blob; ERL_NIF_TERM tag; + ERL_NIF_TERM toposort; + ERL_NIF_TERM timesort; + ERL_NIF_TERM reversesort; } geef_atoms; extern geef_atoms atoms; diff --git a/c_src/object.c b/c_src/object.c index 3956c17..1e40633 100644 --- a/c_src/object.c +++ b/c_src/object.c @@ -63,6 +63,9 @@ geef_object_lookup(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) git_oid_fromraw(&id, bin.data); obj = enif_alloc_resource(geef_object_type, sizeof(geef_object)); + if (!obj) + return atoms.error; + if (git_object_lookup(&obj->obj, repo->repo, &id, GIT_OBJ_ANY) < 0) { enif_release_resource(obj); return geef_error(env); diff --git a/c_src/revwalk.c b/c_src/revwalk.c new file mode 100644 index 0000000..9c6b093 --- /dev/null +++ b/c_src/revwalk.c @@ -0,0 +1,118 @@ +#include "geef.h" +#include "oid.h" +#include "revwalk.h" +#include +#include + +void geef_revwalk_free(ErlNifEnv *env, void *cd) +{ + geef_revwalk *walk = (geef_revwalk *) cd; + enif_release_resource(walk->repo); + git_revwalk_free(walk->walk); +} + +ERL_NIF_TERM +geef_revwalk_new(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) +{ + geef_repository *repo; + geef_revwalk *walk; + ERL_NIF_TERM walk_term; + + if (!enif_get_resource(env, argv[0], geef_repository_type, (void **) &repo)) + return enif_make_badarg(env); + + walk = enif_alloc_resource(geef_revwalk_type, sizeof(geef_revwalk)); + if (!walk) + return atoms.error; + + if (git_revwalk_new(&walk->walk, repo->repo) < 0) { + enif_release_resource(walk); + return geef_error(env); + } + + walk_term = enif_make_resource(env, walk); + enif_release_resource(walk); + walk->repo = repo; + enif_keep_resource(repo); + + return enif_make_tuple2(env, atoms.ok, walk_term); +} + +ERL_NIF_TERM +geef_revwalk_push(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) +{ + ErlNifBinary bin; + geef_revwalk *walk; + int hide; + + if (!enif_get_resource(env, argv[0], geef_revwalk_type, (void **) &walk)) + return enif_make_badarg(env); + + if (!enif_inspect_binary(env, argv[1], &bin)) + return enif_make_badarg(env); + + hide = !enif_compare(argv[2], atoms.true); + + if (hide) + return git_revwalk_hide(walk->walk, (git_oid *)bin.data) ? geef_error(env) : atoms.ok; + + return git_revwalk_push(walk->walk, (git_oid *)bin.data) ? geef_error(env) : atoms.ok; +} + +ERL_NIF_TERM +geef_revwalk_next(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) +{ + ErlNifBinary bin; + geef_revwalk *walk; + + if (!enif_get_resource(env, argv[0], geef_revwalk_type, (void **) &walk)) + return enif_make_badarg(env); + + if (!enif_alloc_binary(GIT_OID_RAWSZ, &bin)) + return atoms.error; + + if (git_revwalk_next((git_oid *)bin.data, walk->walk) < 0) + return geef_error(env); + + return enif_make_tuple2(env, atoms.ok, enif_make_binary(env, &bin)); +} + +ERL_NIF_TERM +geef_revwalk_sorting(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) +{ + geef_revwalk *walk; + ERL_NIF_TERM term, head = argv[1]; + unsigned int flags = 0; + + if (!enif_get_resource(env, argv[0], geef_revwalk_type, (void **) &walk)) + return enif_make_badarg(env); + + while (enif_get_list_cell(env, head, &term, &head)) { + if (!enif_compare(atoms.toposort, term)) + flags |= GIT_SORT_TOPOLOGICAL; + else if (!enif_compare(atoms.timesort, term)) + flags |= GIT_SORT_TIME; + else if (!enif_compare(atoms.reversesort, term)) + flags |= GIT_SORT_REVERSE; + else + return enif_make_badarg(env); + } + + git_revwalk_sorting(walk->walk, flags); + + return atoms.ok; +} + + +ERL_NIF_TERM +geef_revwalk_reset(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) +{ + geef_revwalk *walk; + + if (!enif_get_resource(env, argv[0], geef_revwalk_type, (void **) &walk)) + return enif_make_badarg(env); + + git_revwalk_reset(walk->walk); + + return atoms.ok; +} diff --git a/c_src/revwalk.h b/c_src/revwalk.h new file mode 100644 index 0000000..c10b898 --- /dev/null +++ b/c_src/revwalk.h @@ -0,0 +1,23 @@ +#ifndef GEEF_REVWALK_H +#define GEEF_REVWALK_H + +#include "erl_nif.h" +#include +#include "repository.h" + +extern ErlNifResourceType *geef_revwalk_type; + +typedef struct { + git_revwalk *walk; + geef_repository *repo; +} geef_revwalk; + +void geef_revwalk_free(ErlNifEnv *env, void *cd); + +ERL_NIF_TERM geef_revwalk_new(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM geef_revwalk_next(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM geef_revwalk_push(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM geef_revwalk_sorting(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM geef_revwalk_reset(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); + +#endif diff --git a/src/geef.erl b/src/geef.erl index 947d77c..d983670 100644 --- a/src/geef.erl +++ b/src/geef.erl @@ -19,8 +19,13 @@ % git_libgit2 -export([library_version/0]). +% revision walker +-export([revwalk_new/1, revwalk_push/3, revwalk_next/1, revwalk_sorting/2, revwalk_reset/1]). + -on_load(load_enif/0). +-define(NIF_FN, nif_error(?LINE)). + -spec repository_open(iolist()) -> {ok, term()} | {error, term()}. repository_open(_Val) -> nif_error(?LINE). @@ -112,6 +117,21 @@ blob_content(_ObjHandle) -> library_version() -> nif_error(?LINE). +revwalk_new(_Repo) -> + ?NIF_FN. + +revwalk_push(_Walk, _Id, _Hide) -> + ?NIF_FN. + +revwalk_next(_Walk) -> + ?NIF_FN. + +revwalk_sorting(_Walk, _Sort) -> + ?NIF_FN. + +revwalk_reset(_Walk) -> + ?NIF_FN. + nif_error(Line) -> erlang:nif_error({nif_not_loaded,module,?MODULE,line,Line}). diff --git a/src/geef_records.hrl b/src/geef_records.hrl index a8eb869..e304726 100644 --- a/src/geef_records.hrl +++ b/src/geef_records.hrl @@ -1,11 +1,13 @@ -record(ref, {handle, type :: atom()}). -record(repo, {handle}). -record(odb, {handle}). +-record(revwalk, {handle}). -record(oid, {oid}). -record(object, {type :: atom(), handle}). -type ref() :: #ref{}. -type repo() :: #repo{}. +-type revwalk() :: #revwalk{}. -type odb() :: #odb{}. -type oid() :: #oid{}. -type object() :: #object{}. diff --git a/src/geef_revwalk.erl b/src/geef_revwalk.erl new file mode 100644 index 0000000..8707bc7 --- /dev/null +++ b/src/geef_revwalk.erl @@ -0,0 +1,45 @@ +-module(geef_revwalk). + +-export([new/1, push/2, hide/2, next/1, sorting/2]). + +-include("geef_records.hrl"). + + +%% @doc Create a revision walker +-spec new(repo()) -> {ok, revwalk()} | {error, term}. +new(#repo{handle=Handle}) -> + case geef:revwalk_new(Handle) of + {ok, WalkHandle} -> + {ok, #revwalk{handle=WalkHandle}}; + Other -> + Other + end. + + +%% @doc Push a commit. This commit and its parents will be included in +%% the walk as long as they haven't been hidden. At least one commit +%% must be pushed before starting a walk. +-spec push(revwalk(), oid()) -> ok | {error, binary()}. +push(#revwalk{handle=Handle}, #oid{oid=Oid}) -> + geef:revwalk_push(Handle, Oid, false). + +%% @doc Hide a commit. Hide a commit and its parents. Any Parent of +%% this commit won't be included in the walk. +-spec hide(revwalk(), oid()) -> ok | {error, binary()}. +hide(#revwalk{handle=Handle}, #oid{oid=Oid}) -> + geef:revwalk_push(Handle, Oid, true). + +%% @doc Next commit in the walk +next(#revwalk{handle=Handle}) -> + case geef:revwalk_next(Handle) of + {ok, Oid} -> + {ok, #oid{oid=Oid}}; + Other -> + Other + end. + +sorting(#revwalk{handle=Handle}, Opts) when is_list(Opts) -> + geef:revwalk_sorting(Handle, Opts); +sorting(Walk = #revwalk{}, Opt) when is_atom(Opt) -> + sorting(Walk, [Opt]). +