Merge branch 'reflog'

This commit is contained in:
Carlos Martín Nieto
2014-02-13 23:32:42 +01:00
16 changed files with 292 additions and 40 deletions

View File

@@ -12,6 +12,7 @@
#include "index.h"
#include "signature.h"
#include "revparse.h"
#include "reflog.h"
#include "geef.h"
#include <stdio.h>
#include <string.h>
@@ -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},

View File

@@ -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

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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);

90
c_src/reflog.c Normal file
View File

@@ -0,0 +1,90 @@
#include "geef.h"
#include "repository.h"
#include "reference.h"
#include "oid.h"
#include "signature.h"
#include <string.h>
#include <git2.h>
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;
}

10
c_src/reflog.h Normal file
View File

@@ -0,0 +1,10 @@
#ifndef GEEF_REFLOG_H
#define GEEF_REFLOG_H
#include "erl_nif.h"
#include <git2.h>
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

View File

@@ -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

View File

@@ -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

View File

@@ -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).

View File

@@ -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{}.

View File

@@ -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).

28
src/geef_reflog.erl Normal file
View File

@@ -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).

View File

@@ -1,11 +1,6 @@
%%%-------------------------------------------------------------------
%%% @author Carlos Martín Nieto <cmn@dwim.me>
%%% @copyright (C) 2013, Carlos Martín Nieto
%%% @doc
%%%
%%% @end
%%% Created : 6 Apr 2013 by Carlos Martín Nieto <cmn@dwim.me>
%%%-------------------------------------------------------------------
%% -*- erlang-indent-level: 4; indent-tabs-mode: nil -*-
%%% @copyright (C) 2013-2014, Carlos Martín Nieto <cmn@dwim.me>
-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}) ->

View File

@@ -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}.

View File

@@ -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) ->