diff --git a/c_src/geef.c b/c_src/geef.c index bee4dad..4e111fa 100644 --- a/c_src/geef.c +++ b/c_src/geef.c @@ -12,6 +12,7 @@ #include "index.h" #include "signature.h" #include "revparse.h" +#include "reflog.h" #include "geef.h" #include #include @@ -72,6 +73,7 @@ static int load(ErlNifEnv *env, void **priv, ERL_NIF_TERM load_info) atoms.blob = enif_make_atom(env, "blob"); atoms.tag = enif_make_atom(env, "tag"); atoms.undefined = enif_make_atom(env, "undefined"); + atoms.reflog_entry = enif_make_atom(env, "geef_reflog_entry"); /* Revwalk */ atoms.toposort = enif_make_atom(env, "sort_topo"); atoms.timesort = enif_make_atom(env, "sort_time"); @@ -137,6 +139,22 @@ int geef_terminate_binary(ErlNifBinary *bin) return 1; } +int geef_string_to_bin(ErlNifBinary *bin, const char *str) +{ + size_t len; + + if (str == NULL) + len = 0; + else + len = strlen(str); + + if (!enif_alloc_binary(len, bin)) + return -1; + + memcpy(bin->data, str, len); + return 0; +} + static ErlNifFunc geef_funcs[] = { {"repository_init", 2, geef_repository_init}, @@ -157,6 +175,9 @@ static ErlNifFunc geef_funcs[] = {"reference_resolve", 2, geef_reference_resolve}, {"reference_create", 5, geef_reference_create}, {"reference_dwim", 2, geef_reference_dwim}, + {"reference_has_log", 2, geef_reference_has_log}, + {"reflog_read", 2, geef_reflog_read}, + {"reflog_delete", 2, geef_reflog_delete}, {"oid_fmt", 1, geef_oid_fmt}, {"oid_parse", 1, geef_oid_parse}, {"object_lookup", 2, geef_object_lookup}, diff --git a/c_src/geef.h b/c_src/geef.h index e3dec2a..e06e2d4 100644 --- a/c_src/geef.h +++ b/c_src/geef.h @@ -23,6 +23,7 @@ typedef struct { ERL_NIF_TERM timesort; ERL_NIF_TERM reversesort; ERL_NIF_TERM iterover; + ERL_NIF_TERM reflog_entry; ERL_NIF_TERM enomem; ERL_NIF_TERM eunknown; @@ -32,5 +33,7 @@ extern geef_atoms atoms; /** NUL-terminate a binary */ int geef_terminate_binary(ErlNifBinary *bin); +/** Copy a string into a binary */ +int geef_string_to_bin(ErlNifBinary *bin, const char *str); #endif diff --git a/c_src/oid.c b/c_src/oid.c index a0cf889..00ff35a 100644 --- a/c_src/oid.c +++ b/c_src/oid.c @@ -6,8 +6,10 @@ int geef_oid_bin(ErlNifBinary *bin, const git_oid *id) { - if (!enif_alloc_binary(GIT_OID_RAWSZ, bin)) + if (!enif_alloc_binary(GIT_OID_RAWSZ, bin)) { + printf("cannot alloc an id!\n"); return -1; + } memcpy(bin->data, id, GIT_OID_RAWSZ); return 0; diff --git a/c_src/reference.c b/c_src/reference.c index e6c73a0..1994d6f 100644 --- a/c_src/reference.c +++ b/c_src/reference.c @@ -418,3 +418,34 @@ geef_reference_create(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) return atoms.ok; } + +ERL_NIF_TERM +geef_reference_has_log(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) +{ + geef_repository *repo; + ErlNifBinary name; + int error; + const char *pname; + ERL_NIF_TERM ret; + + if (!enif_get_resource(env, argv[0], geef_repository_type, (void **) &repo)) + return enif_make_badarg(env); + + if (!enif_inspect_iolist_as_binary(env, argv[1], &name)) + return enif_make_badarg(env); + + if (!geef_terminate_binary(&name)) + return geef_oom(env); + + pname = (char *) name.data; + error = git_reference_has_log(repo->repo, pname); + + enif_release_binary(&name); + + if (error < 0) + return geef_error(env); + + ret = error ? atoms.true : atoms.false; + + return enif_make_tuple2(env, atoms.ok, ret); +} diff --git a/c_src/reference.h b/c_src/reference.h index 89bd0bb..492e00d 100644 --- a/c_src/reference.h +++ b/c_src/reference.h @@ -20,6 +20,7 @@ ERL_NIF_TERM geef_reference_create(ErlNifEnv *env, int argc, const ERL_NIF_TERM ERL_NIF_TERM geef_reference_dwim(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); ERL_NIF_TERM geef_reference_iterator(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); ERL_NIF_TERM geef_reference_next(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM geef_reference_has_log(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); void geef_ref_iter_free(ErlNifEnv *env, void *cd); diff --git a/c_src/reflog.c b/c_src/reflog.c new file mode 100644 index 0000000..0efbdf6 --- /dev/null +++ b/c_src/reflog.c @@ -0,0 +1,90 @@ +#include "geef.h" +#include "repository.h" +#include "reference.h" +#include "oid.h" +#include "signature.h" +#include +#include + +ERL_NIF_TERM +geef_reflog_read(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) +{ + git_reflog *reflog; + geef_repository *repo; + ErlNifBinary bin; + int error; + size_t count, i; + ERL_NIF_TERM list; + + if (!enif_get_resource(env, argv[0], geef_repository_type, (void **) &repo)) + return enif_make_badarg(env); + + if (!enif_inspect_iolist_as_binary(env, argv[1], &bin)) + return enif_make_badarg(env); + + if (!geef_terminate_binary(&bin)) + return geef_oom(env); + + if ((error = git_reflog_read(&reflog, repo->repo, (char *)bin.data)) < 0) + return geef_error(env); + + count = git_reflog_entrycount(reflog); + list = enif_make_list(env, 0); + + for (i = count; i > 0; i--) { + ErlNifBinary id_old, id_new, message; + ERL_NIF_TERM tentry, name, email, time, offset; + const git_reflog_entry *entry; + + entry = git_reflog_entry_byindex(reflog, i-1); + + if (geef_oid_bin(&id_old, git_reflog_entry_id_old(entry))) + goto on_oom; + + if (geef_oid_bin(&id_new, git_reflog_entry_id_new(entry))) + goto on_oom; + + if (geef_signature_to_erl(&name, &email, &time, &offset, + env, git_reflog_entry_committer(entry))) + goto on_oom; + + if (geef_string_to_bin(&message, git_reflog_entry_message(entry))) + goto on_oom; + + tentry = enif_make_tuple7(env, name, email, time, offset, + enif_make_binary(env, &id_old), + enif_make_binary(env, &id_new), + enif_make_binary(env, &message)); + list = enif_make_list_cell(env, tentry, list); + } + + git_reflog_free(reflog); + return enif_make_tuple2(env, atoms.ok, list); + +on_oom: + git_reflog_free(reflog); + return geef_oom(env); +} + +ERL_NIF_TERM +geef_reflog_delete(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) +{ + geef_repository *repo; + ErlNifBinary bin; + int error; + + if (!enif_get_resource(env, argv[0], geef_repository_type, (void **) &repo)) + return enif_make_badarg(env); + + if (!enif_inspect_iolist_as_binary(env, argv[1], &bin)) + return enif_make_badarg(env); + + if (!geef_terminate_binary(&bin)) + return geef_oom(env); + + error = git_reflog_delete(repo->repo, (char *) bin.data); + + enif_release_binary(&bin); + + return error ? geef_error(env) : atoms.ok; +} diff --git a/c_src/reflog.h b/c_src/reflog.h new file mode 100644 index 0000000..d1717c1 --- /dev/null +++ b/c_src/reflog.h @@ -0,0 +1,10 @@ +#ifndef GEEF_REFLOG_H +#define GEEF_REFLOG_H + +#include "erl_nif.h" +#include + +ERL_NIF_TERM geef_reflog_read(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM geef_reflog_delete(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); + +#endif diff --git a/c_src/signature.c b/c_src/signature.c index 5719f39..771b7b1 100644 --- a/c_src/signature.c +++ b/c_src/signature.c @@ -90,49 +90,53 @@ on_oom: } -static int geef_string_to_bin(ErlNifBinary *bin, const char *str) +int geef_signature_to_erl(ERL_NIF_TERM *out_name, ERL_NIF_TERM *out_email, ERL_NIF_TERM *out_time, ERL_NIF_TERM *out_offset, ErlNifEnv *env, const git_signature *sig) { - size_t len; - - len = strlen(str); - if (!enif_alloc_binary(len, bin)) - return -1; - - memcpy(bin->data, str, len); - return 0; -} - -ERL_NIF_TERM -geef_signature_default(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) -{ - git_signature *sig; - geef_repository *repo; ErlNifBinary name, email; - if (!enif_get_resource(env, argv[0], geef_repository_type, (void **) &repo)) - return enif_make_badarg(env); - memset(&name, 0, sizeof(ErlNifBinary)); memset(&email, 0, sizeof(ErlNifBinary)); - if (git_signature_default(&sig, repo->repo) < 0) - return geef_error(env); - if (geef_string_to_bin(&name, sig->name) < 0) goto oom; if (geef_string_to_bin(&email, sig->email) < 0) goto oom; - return enif_make_tuple5(env, atoms.ok, - enif_make_binary(env, &name), enif_make_binary(env, &email), - enif_make_ulong(env, sig->when.time), enif_make_uint(env, sig->when.offset)); + *out_name = enif_make_binary(env, &name); + *out_email = enif_make_binary(env, &email); + *out_time = enif_make_ulong(env, sig->when.time); + *out_offset = enif_make_uint(env, sig->when.offset); + + return 0; + oom: - git_signature_free(sig); enif_release_binary(&name); enif_release_binary(&email); + return -1; +} - return geef_oom(env); +ERL_NIF_TERM +geef_signature_default(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) +{ + git_signature *sig; + geef_repository *repo; + ERL_NIF_TERM name, email, time, offset; + int error; + + if (!enif_get_resource(env, argv[0], geef_repository_type, (void **) &repo)) + return enif_make_badarg(env); + + if (git_signature_default(&sig, repo->repo) < 0) + return geef_error(env); + + error = geef_signature_to_erl(&name, &email, &time, &offset, env, sig); + git_signature_free(sig); + + if (error < 0) + return geef_oom(env); + + return enif_make_tuple5(env, atoms.ok, name, email, time, offset); } ERL_NIF_TERM diff --git a/c_src/signature.h b/c_src/signature.h index b3e1e97..a6cc28f 100644 --- a/c_src/signature.h +++ b/c_src/signature.h @@ -7,4 +7,6 @@ ERL_NIF_TERM geef_signature_new(ErlNifEnv *env, int argc, const ERL_NIF_TERM arg ERL_NIF_TERM geef_signature_default(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); int geef_signature_from_erl(git_signature **out, ErlNifEnv *env, ERL_NIF_TERM *err, ERL_NIF_TERM term); +int geef_signature_to_erl(ERL_NIF_TERM *out_name, ERL_NIF_TERM *out_email, ERL_NIF_TERM *out_time, ERL_NIF_TERM *out_offset, ErlNifEnv *env, const git_signature *sig); + #endif diff --git a/src/geef_nif.erl b/src/geef_nif.erl index dd7241f..1134adf 100644 --- a/src/geef_nif.erl +++ b/src/geef_nif.erl @@ -69,6 +69,18 @@ reference_resolve(_RepoHandle, _Name) -> reference_dwim(_Handle, _Name) -> ?NIF_FN. +-spec reference_has_log(term(), iolist()) -> {ok, boolean()} | {error, term()}. +reference_has_log(_Handle, _Name) -> + ?NIF_FN. + +-spec reflog_read(term(), iolist()) -> {ok, binary(), binary(), non_neg_integer(), non_neg_integer()} | {error, term()}. +reflog_read(_Handle, _Name) -> + ?NIF_FN. + +-spec reflog_delete(term(), iolist()) -> ok | {error, term()}. +reflog_delete(_Handle, _Name) -> + ?NIF_FN. + odb_object_exists(_Val, _Val) -> nif_error(?LINE). diff --git a/src/geef_records.hrl b/src/geef_records.hrl index 6f5931f..6fa0edb 100644 --- a/src/geef_records.hrl +++ b/src/geef_records.hrl @@ -14,4 +14,8 @@ -record(geef_signature, {name :: iolist(), email :: iolist(), time :: geef_sig:time()}). -record(geef_iterator, {type :: atom(), repo :: pid(), regexp :: iolist(), handle}). +-record(geef_reflog_entry, {committer :: geef_sig:signature(), + id_old :: geef_oid:oid(), id_new :: geef_oid:oid(), + message :: iolist()}). + -type geef_request() :: #geef_request{}. diff --git a/src/geef_ref.erl b/src/geef_ref.erl index 2e28467..d4ad65d 100644 --- a/src/geef_ref.erl +++ b/src/geef_ref.erl @@ -1,6 +1,7 @@ -module(geef_ref). -export([lookup/2, iterator/1, iterator/2, next/1, resolve/1, create/4, create_symbolic/4, dwim/2, shorthand/1]). +-export([has_log/1, has_log/2]). -include("geef_records.hrl"). @@ -103,3 +104,12 @@ shorthand(<<"refs/", Rest/binary>>) -> Rest; shorthand(#geef_reference{name=Name}) -> shorthand(Name). + +%% @doc Return whether the reference has a reflog +-spec has_log(pid(), iolist()) -> {ok, boolean()} | {error, term()}. +has_log(Repo, Name) -> + geef_repo:reference_has_log(Repo, Name). + +-spec has_log(ref()) -> boolean() | {ok, boolean()} | {error | term()}. +has_log(#geef_reference{repo=Repo, name=Name}) -> + has_log(Repo, Name). diff --git a/src/geef_reflog.erl b/src/geef_reflog.erl new file mode 100644 index 0000000..706f37d --- /dev/null +++ b/src/geef_reflog.erl @@ -0,0 +1,28 @@ +%% -*- erlang-indent-level: 4; indent-tabs-mode: nil -*- + +-module(geef_reflog). + +-include("geef_records.hrl"). + +-type entry() :: #geef_reflog_entry{}. +-export_type([entry/0]). + +%% API +-export([read/2, delete/2]). + +to_entry({Name, Email, Timestamp, Offset, IdOld, IdNew, Message}) -> + Sig = geef_sig:convert(Name, Email, Timestamp, Offset), + #geef_reflog_entry{committer=Sig, id_old=IdOld, id_new=IdNew, message=Message}. + +%% @doc Read in a reflog +-spec read(pid(), iolist()) -> {ok, [entry()]} | {error, term()}. +read(Repo, Name) -> + case geef_repo:reflog_read(Repo, Name) of + {ok, List} -> + {ok, lists:map(fun to_entry/1, List)}; + Other -> + Other + end. + +delete(Repo, Name) -> + geef_repo:reflog_delete(Repo, Name). diff --git a/src/geef_repo.erl b/src/geef_repo.erl index 1dcdd80..6ebf507 100644 --- a/src/geef_repo.erl +++ b/src/geef_repo.erl @@ -1,11 +1,6 @@ -%%%------------------------------------------------------------------- -%%% @author Carlos Martín Nieto -%%% @copyright (C) 2013, Carlos Martín Nieto -%%% @doc -%%% -%%% @end -%%% Created : 6 Apr 2013 by Carlos Martín Nieto -%%%------------------------------------------------------------------- +%% -*- erlang-indent-level: 4; indent-tabs-mode: nil -*- +%%% @copyright (C) 2013-2014, Carlos Martín Nieto + -module(geef_repo). -behaviour(gen_server). @@ -18,6 +13,8 @@ -export([open/1, init/2, path/1, workdir/1, odb/1, is_bare/1, references/1, discover/1, lookup_object/2, revwalk/1, stop/1, reference_dwim/2, handle/1, iterator/2]). +-export([reference_has_log/2]). +-export([reflog_read/2, reflog_delete/2]). -include("geef_records.hrl"). -record(state, {handle}). @@ -95,11 +92,23 @@ reference_dwim(Pid, Name) -> revwalk(Pid) -> gen_server:call(Pid, revwalk). +%% @private +reference_has_log(Pid, Name) -> + gen_server:call(Pid, {has_log, Name}). + +%% @private +reflog_read(Pid, Name) -> + gen_server:call(Pid, {reflog_read, Name}). + +%% @private +reflog_delete(Pid, Name) -> + gen_server:call(Pid, {reflog_delete, Name}). + stop(Pid) -> gen_server:call(Pid, stop). %% @private -%% @doc Get the underlying repo resource6 +%% @doc Get the underlying repo resource handle(Pid) -> gen_server:call(Pid, handle). @@ -136,6 +145,19 @@ handle_call({iterator, Regexp}, _From, State = #state{handle=Handle}) -> handle_call({dwim_reference, Name}, _From, State = #state{handle=Handle}) -> Reply = geef_nif:reference_dwim(Handle, Name), {reply, Reply, State}; + +handle_call({has_log, Name}, _From, State = #state{handle=Handle}) -> + Reply = geef_nif:reference_has_log(Handle, Name), + {reply, Reply, State}; + +handle_call({reflog_read, Name}, _From, State = #state{handle=Handle}) -> + Reply = geef_nif:reflog_read(Handle, Name), + {reply, Reply, State}; + +handle_call({reflog_delete, Name}, _From, State = #state{handle=Handle}) -> + Reply = geef_nif:reflog_delete(Handle, Name), + {reply, Reply, State}; + handle_call(stop, _From, State) -> {stop, normal, ok, State}; handle_call(revwalk, _From, State = #state{handle=Handle}) -> diff --git a/src/geef_sig.erl b/src/geef_sig.erl index 22a49f0..d8bab2f 100644 --- a/src/geef_sig.erl +++ b/src/geef_sig.erl @@ -7,6 +7,7 @@ -export_type([time/0, signature/0]). -export([default/1, now/2]). +-export([convert/4]). %% @doc Create a signature for the repository's configured username and %% email, with a timestamp of now. @@ -15,9 +16,7 @@ default(Repo) -> RepoHandle = geef_repo:handle(Repo), case geef_nif:signature_default(RepoHandle) of {ok, Name, Email, Timestamp, Offset} -> - Time = {{Timestamp div 1000000, Timestamp rem 1000000, 0}, Offset}, - Sig = #geef_signature{name=Name, email=Email, time=Time}, - {ok, Sig}; + {ok, convert(Name, Email, Timestamp, Offset)}; Err = {error, _} -> Err end. @@ -32,3 +31,10 @@ now(Name, Email) -> UTC = calendar:datetime_to_gregorian_seconds(calendar:now_to_universal_time(Now)), Offset = (Local - UTC) div 60, #geef_signature{name=Name, email=Email, time={Now, Offset}}. + +%% @private +%% @doc convert the return from the NIF to a signature +-spec convert(binary(), binary(), non_neg_integer(), non_neg_integer()) -> signature(). +convert(Name, Email, Timestamp, Offset) -> + Time = {{Timestamp div 1000000, Timestamp rem 1000000, 0}, Offset}, + #geef_signature{name=Name, email=Email, time=Time}. diff --git a/test/repo_test.erl b/test/repo_test.erl index 54c8d4e..c9b0f28 100644 --- a/test/repo_test.erl +++ b/test/repo_test.erl @@ -59,10 +59,16 @@ ref_test(Repo) -> {ok, Ref1} = geef_ref:lookup(Repo, "refs/heads/other"), {ok, Ref2} = geef_ref:resolve(Ref0), {ok, Dwimed} = geef_ref:dwim(Repo, "branch"), + {ok, Reflog0} = geef_reflog:read(Repo, "refs/heads/branch"), + ok = geef_reflog:delete(Repo, "refs/heads/branch"), + {ok, Reflog1} = geef_reflog:read(Repo, "refs/heads/branch"), [?_assertEqual(Ref0#geef_reference.target, Id), ?_assertEqual(Ref1#geef_reference.target, <<"refs/heads/branch">>), ?_assertEqual(<<"branch">>, geef_ref:shorthand(Ref0)), ?_assertEqual(Ref2#geef_reference.target, Id), + ?_assertEqual({ok, true}, geef_ref:has_log(Ref0)), + ?_assertEqual(1, length(Reflog0)), + ?_assertEqual(0, length(Reflog1)), ?_assertEqual(Ref0, Dwimed)]. ref_iter_test(Repo) ->