Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for Graphs #88

Merged
merged 3 commits into from
Apr 25, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions Raw.xs
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,72 @@ STATIC SV *git_oid_to_sv(const git_oid *oid) {
return newSVpv(out, 0);
}

STATIC git_oid *git_sv_to_commitish(Repository repo, SV *sv, git_oid *oid) {
git_oid *result = NULL;
git_reference *ref = NULL;
git_object *obj = NULL;

if (sv_isobject(sv)) {
if (sv_derived_from(sv, "Git::Raw::Reference")) {
int rc = git_reference_peel(&obj,
GIT_SV_TO_PTR(Reference, sv),
GIT_OBJ_COMMIT
);
git_check_error(rc);

git_oid_cpy(oid, git_object_id(obj));
} else if (sv_derived_from(sv, "Git::Raw::Commit")) {
git_oid_cpy(oid, git_commit_id(GIT_SV_TO_PTR(Commit, sv)));
} else
goto on_error;
} else {
STRLEN len;
const char *commitish_name = NULL;

/* substr() may return a SVt_PVLV, need to perform some
* force majeur */
if (SvPOK(sv)) {
commitish_name = SvPVbyte(sv, len);
} else if (SvTYPE(sv) == SVt_PVLV) {
commitish_name = SvPVbyte_force(sv, len);
}

if (commitish_name) {
/* first try and see if its a commit id, otherwise see if its
* a reference */
if (git_oid_fromstrn(oid, commitish_name, len) >= 0) {
if (len < GIT_OID_MINPREFIXLEN)
goto on_error;

if (len != GIT_OID_HEXSZ) {
if (git_object_lookup_prefix(&obj, repo, oid,
len, GIT_OBJ_COMMIT) < 0)
goto on_error;

git_oid_cpy(oid, git_object_id(obj));
}
} else {
if (git_reference_lookup(&ref, repo, commitish_name) < 0 &&
git_reference_dwim(&ref, repo, commitish_name) < 0)
goto on_error;

if (git_reference_peel(&obj, ref, GIT_OBJ_COMMIT) < 0)
goto on_error;

git_oid_cpy(oid, git_object_id(obj));
}
} else
goto on_error;
}

result = oid;

on_error:
git_object_free(obj);
git_reference_free(ref);
return result;
}

STATIC SV *get_callback_option(HV *callbacks, const char *name) {
SV **opt;

Expand Down Expand Up @@ -1307,6 +1373,7 @@ INCLUDE: xs/Diff/File.xs
INCLUDE: xs/Diff/Hunk.xs
INCLUDE: xs/Filter.xs
INCLUDE: xs/Filter/Source.xs
INCLUDE: xs/Graph.xs
INCLUDE: xs/Index.xs
INCLUDE: xs/Index/Entry.xs
INCLUDE: xs/Patch.xs
Expand Down
7 changes: 7 additions & 0 deletions lib/Git/Raw/Commit.pm
Original file line number Diff line number Diff line change
Expand Up @@ -104,10 +104,17 @@ Merge C<$commit> into this commit. See C<Git::Raw::Repository-E<gt>merge()>
for valid C<%merge_opts> values. Returns a C<Git::Raw::Index> object
containing the merge result.

=head2 ancestor( $gen )

Retrieve the C<Git::Raw::Commit> object that is the C<$gen>'th generation
ancestor of this commit, following only the first parents.

=head1 AUTHOR

Alessandro Ghedini <alexbio@cpan.org>

Jacques Germishuys <jacquesg@striata.com>

=head1 LICENSE AND COPYRIGHT

Copyright 2012 Alessandro Ghedini.
Expand Down
60 changes: 60 additions & 0 deletions lib/Git/Raw/Graph.pm
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package Git::Raw::Graph;

use strict;
use warnings;

use Git::Raw;

=head1 NAME

Git::Raw::Graph - Git graph class

=head1 SYNOPSIS

use Git::Raw;

# open the Git repository at $path
my $repo = Git::Raw::Repository -> open($path);

my $commit1 = Git::Raw::Commit -> lookup($repo,
'4a202b346bb0fb0db7eff3cffeb3c70babbd2045');

my $commit2 = Git::Raw::Commit -> lookup($repo,
'5b5b025afb0b4c913b4c338a42934a3863bf3644');

if (Git::Raw::Graph -> is_descendant_of($repo, $commit1, $commit2)) {
print $commit1 -> id, ' is a descendant of ', $commit2 -> id, "\n";
} else {
print $commit1 -> id, ' is a not descendant of ', $commit2 -> id, "\n";
}

=head1 DESCRIPTION

B<WARNING>: The API of this module is unstable and may change without warning
(any change will be appropriately documented in the changelog).


=head2 is_descendant_of( $repo, $commitish, $ancestor )

Determine if C<$commitish> is the descendant of C<$ancestor>. C<$commitish>
and C<$ancestor> should be peelable to a C<Git::Raw::Commit> object, that is,
it should be a C<Git::Raw::Commit> or C<Git::Raw::Reference> object, or
alternatively a commit id or commit id prefix.

=head1 AUTHOR

Jacques Germishuys <jacquesg@striata.com>

=head1 LICENSE AND COPYRIGHT

Copyright 2014 Jacques Germishuys.

This program is free software; you can redistribute it and/or modify it
under the terms of either: the GNU General Public License as published
by the Free Software Foundation; or the Artistic License.

See http://dev.perl.org/licenses/ for more information.

=cut

1; # End of Git::Raw::Graph
9 changes: 6 additions & 3 deletions lib/Git/Raw/Repository.pm
Original file line number Diff line number Diff line change
Expand Up @@ -344,9 +344,10 @@ Example:

=head2 merge_base( @objects )

Find the merge base between C<@objects>. Each element in C<@objects> should
either be a C<Git::Raw::Reference> or a C<Git::Raw::Commit>. A minimum
of 2 objects should be provided.
Find the merge base between C<@objects>. Each element in C<@objects> should be
peelable to a C<Git::Raw::Commit> object, that is, it should be a
C<Git::Raw::Commit> or C<Git::Raw::Reference> object, or alternatively a commit
id or commit id prefix.

=head2 merge_analysis( $reference )

Expand Down Expand Up @@ -750,6 +751,8 @@ a commit.

Alessandro Ghedini <alexbio@cpan.org>

Jacques Germishuys <jacquesg@striata.com>

=head1 LICENSE AND COPYRIGHT

Copyright 2012 Alessandro Ghedini.
Expand Down
22 changes: 22 additions & 0 deletions t/02-commit.t
Original file line number Diff line number Diff line change
Expand Up @@ -125,10 +125,19 @@ my $commit2 = $repo -> commit(
"second commit\n", $me, $me, [$repo -> head -> target], $tree
);

is $commit2 -> ancestor(0) -> id, $commit2 -> id;
is $commit2 -> ancestor(1) -> id, $commit -> id;

my $head = $repo -> head -> target;

isa_ok $head, 'Git::Raw::Commit';

is (Git::Raw::Graph -> is_descendant_of($repo, $commit2, $commit), 1);
is (Git::Raw::Graph -> is_descendant_of($repo, $commit2 -> id, $commit -> id), 1);

is (Git::Raw::Graph -> is_descendant_of($repo, $commit, $commit2), 0);
is (Git::Raw::Graph -> is_descendant_of($repo, $commit -> id, $commit2 -> id), 0);

is $head -> message, "second commit\n";
is $head -> summary, "second commit";

Expand Down Expand Up @@ -163,6 +172,19 @@ my $commit3 = $repo -> commit(
"third commit\n", $me, $me, [$repo -> head -> target], $tree
);

is $commit3 -> ancestor(0) -> id, $commit3 -> id;
is $commit3 -> ancestor(1) -> id, $commit2 -> id;
is $commit3 -> ancestor(2) -> id, $commit -> id;
ok (!eval { $commit3 -> ancestor(3) });

is (Git::Raw::Graph -> is_descendant_of($repo, $commit3, $commit), 1);
is (Git::Raw::Graph -> is_descendant_of($repo, $commit3 -> id, $commit -> id), 1);
is (Git::Raw::Graph -> is_descendant_of($repo, substr($commit3 -> id, 0, 7), $commit -> id), 1);

is (Git::Raw::Graph -> is_descendant_of($repo, $commit, $commit3), 0);
is (Git::Raw::Graph -> is_descendant_of($repo, $commit -> id, $commit3 -> id), 0);
is (Git::Raw::Graph -> is_descendant_of($repo, substr($commit -> id, 0, 7), $commit3 -> id), 0);

$head = $repo -> head -> target;

isa_ok $head, 'Git::Raw::Commit';
Expand Down
29 changes: 23 additions & 6 deletions t/15-merge.t
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,10 @@ my $me = Git::Raw::Signature -> default($repo);
my $commit = $repo -> commit("initial commit\n", $me, $me, [],
$repo -> lookup($index -> write_tree));

my $branch1 = $repo -> branch('branch1', $repo -> head -> target);
my $branch2 = $repo -> branch('branch2', $repo -> head -> target);
my $branch3 = $repo -> branch('branch3', $repo -> head -> target);
my $initial_head = $repo -> head;
my $branch1 = $repo -> branch('branch1', $initial_head -> target);
my $branch2 = $repo -> branch('branch2', $initial_head -> target);
my $branch3 = $repo -> branch('branch3', $initial_head -> target);

$repo -> head($branch1);
write_file($file1, 'this is file1 on branch1');
Expand All @@ -50,8 +51,11 @@ $repo -> checkout($repo -> head($master), {
}
});

ok (!eval {$repo -> merge_base("refs/heads/master", $commit1 -> id)});
is $master -> target -> id, $repo -> merge_base("refs/heads/master", $commit1 -> id);
is $master -> target -> id, $repo -> merge_base("refs/heads/master", $commit1);
is $master -> target -> id, $repo -> merge_base("refs/heads/master", substr($commit1 -> id, 0, 7));
is $master -> target -> id, $repo -> merge_base($master, $commit1);
is $master -> target -> id, $repo -> merge_base($master, 'refs/heads/branch1');

my $r = $repo -> merge_analysis($branch1);
is_deeply $r, ['normal', 'fast_forward'];
Expand Down Expand Up @@ -95,6 +99,9 @@ is $commit -> id, $repo -> merge_base($commit1, $commit2);
$r = $repo ->merge_analysis($branch2);
is_deeply $r, ['normal'];

Git::Raw::Graph -> is_descendant_of($repo, $branch1, $branch2), 0;
Git::Raw::Graph -> is_descendant_of($repo, $branch2, $branch1), 0;

$repo -> merge($branch2);
is $index -> has_conflicts, 1;

Expand All @@ -114,17 +121,24 @@ ok $conflict -> {'ancestor'} -> id ne $conflict -> {'ours'} -> id;
ok $conflict -> {'ancestor'} -> id ne $conflict -> {'theirs'} -> id;
ok $conflict -> {'ours'} -> id ne $conflict -> {'theirs'} -> id;


write_file($file1, 'this is file1 on branch1 and branch2');
$index -> add('test1');
$index -> write;

my $merge_msg = $repo -> message();
is $merge_msg, "Merge branch 'branch2'\n\nConflicts:\n\ttest1\n";

$commit = $repo -> commit("Merge commit!", $me, $me, [$master -> target, $commit2],
my $target = $master -> target;
$commit = $repo -> commit("Merge commit!", $me, $me, [$target, $commit2],
$repo -> lookup($index -> write_tree));

Git::Raw::Graph -> is_descendant_of($repo, $commit, $target), 1;
Git::Raw::Graph -> is_descendant_of($repo, $commit, $commit2), 1;
Git::Raw::Graph -> is_descendant_of($repo, $commit, 'refs/heads/branch2'), 1;
Git::Raw::Graph -> is_descendant_of($repo, $commit, 'branch2'), 1;
Git::Raw::Graph -> is_descendant_of($repo, $target, $commit), 0;
Git::Raw::Graph -> is_descendant_of($repo, $commit2, $commit), 0;

is $repo -> state, "merge";
$repo -> state_cleanup;
is $repo -> state, "none";
Expand All @@ -145,6 +159,9 @@ $index -> write;
$commit = $repo -> commit("commit on branch3\n", $me, $me, [$branch3 -> target],
$repo -> lookup($index -> write_tree));

is (Git::Raw::Graph -> is_descendant_of($repo, $commit, $initial_head), 1);
is (Git::Raw::Graph -> is_descendant_of($repo, $commit -> id, $initial_head), 1);

$repo -> checkout($repo -> head($master), {
checkout_strategy => {
'force' => 1
Expand Down
27 changes: 27 additions & 0 deletions xs/Commit.xs
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,33 @@ merge(self, commit, ...)

OUTPUT: RETVAL

SV *
ancestor(self, gen)
SV *self
unsigned int gen

PREINIT:
int rc;

SV *repo;
Commit anc;

CODE:
repo = GIT_SV_TO_MAGIC(self);

rc = git_commit_nth_gen_ancestor(
&anc,
GIT_SV_TO_PTR(Commit, self),
gen
);
git_check_error(rc);

GIT_NEW_OBJ_WITH_MAGIC(
RETVAL, "Git::Raw::Commit", anc, repo
);

OUTPUT: RETVAL

void
DESTROY(self)
SV *self
Expand Down
26 changes: 26 additions & 0 deletions xs/Graph.xs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
MODULE = Git::Raw PACKAGE = Git::Raw::Graph

SV *
is_descendant_of(class, repo, commitish, ancestor)
SV *class
Repository repo
SV *commitish
SV *ancestor

PREINIT:
int result = 0;

git_oid commitish_id, ancestor_id;

CODE:
if (git_sv_to_commitish(repo, commitish, &commitish_id) == NULL)
Perl_croak(aTHX_ "Could not resolve 'commitish' to a commit id");

if (git_sv_to_commitish(repo, ancestor, &ancestor_id) == NULL)
Perl_croak(aTHX_ "Could not resolve 'ancestor' to a commit id");

result = git_graph_descendant_of(repo, &commitish_id, &ancestor_id);

RETVAL = newSViv(result);

OUTPUT: RETVAL
33 changes: 2 additions & 31 deletions xs/Repository.xs
Original file line number Diff line number Diff line change
Expand Up @@ -638,38 +638,9 @@ merge_base(self, ...)
count = items - 1;
Renew(oids, count, git_oid);
for (i = 0; i < count; ++i) {
SV *item = ST(i + 1);

if (sv_isobject(item)) {
if (sv_derived_from(item, "Git::Raw::Reference")) {
git_object *obj = NULL;

rc = git_reference_peel(&obj,
GIT_SV_TO_PTR(Reference, item),
GIT_OBJ_COMMIT);

/* Need to ensure we don't leak, git_check_error()
* may croak */
if (rc != GIT_OK)
Safefree(oids);
git_check_error(rc);

git_oid_cpy(oids + i, git_object_id(obj));
git_object_free(obj);

} else if (sv_derived_from(item, "Git::Raw::Commit")) {
const git_oid *commit_id =
git_commit_id(GIT_SV_TO_PTR(Commit, item));
git_oid_cpy(oids + i, commit_id);
} else {
Safefree(oids);
Perl_croak(aTHX_ "Expected a 'Git::Raw::Commit' "
"or 'Git::Raw::Reference'");
}
} else {
if (git_sv_to_commitish(self, ST(i + 1), oids + i) == NULL) {
Safefree(oids);
Perl_croak(aTHX_ "Expected a 'Git::Raw::Commit' "
"or 'Git::Raw::Reference'");
Perl_croak(aTHX_ "Could not resolve 'object' to a commit id");
}
}

Expand Down