mirror of
https://git.proxmox.com/git/libgit2
synced 2025-06-02 21:55:59 +00:00
Add basic branch management API: git_branch_create(), git_branch_delete(), git_branch_list()
This commit is contained in:
parent
79fd42301e
commit
731df57080
@ -4,12 +4,97 @@
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
*/
|
||||
#ifndef INCLUDE_branch_h__
|
||||
#define INCLUDE_branch_h__
|
||||
#ifndef INCLUDE_git_branch_h__
|
||||
#define INCLUDE_git_branch_h__
|
||||
|
||||
struct git_branch {
|
||||
char *remote; /* TODO: Make this a git_remote */
|
||||
char *merge;
|
||||
};
|
||||
#include "common.h"
|
||||
#include "types.h"
|
||||
|
||||
/**
|
||||
* @file git2/branch.h
|
||||
* @brief Git branch parsing routines
|
||||
* @defgroup git_branch Git branch management
|
||||
* @ingroup Git
|
||||
* @{
|
||||
*/
|
||||
GIT_BEGIN_DECL
|
||||
|
||||
/**
|
||||
* Create a new branch pointing at a target commit
|
||||
*
|
||||
* A new direct reference will be created pointing to
|
||||
* this target commit. If `force` is true and a reference
|
||||
* already exists with the given name, it'll be replaced.
|
||||
*
|
||||
* @param oid_out Pointer where to store the OID of the target commit.
|
||||
*
|
||||
* @param repo Repository where to store the branch.
|
||||
*
|
||||
* @param branch_name Name for the branch; this name is
|
||||
* validated for consistency. It should also not conflict with
|
||||
* an already existing branch name.
|
||||
*
|
||||
* @param target Object to which this branch should point. This object
|
||||
* must belong to the given `repo` and can either be a git_commit or a
|
||||
* git_tag. When a git_tag is being passed, it should be dereferencable
|
||||
* to a git_commit which oid will be used as the target of the branch.
|
||||
*
|
||||
* @param force Overwrite existing branch.
|
||||
*
|
||||
* @return GIT_SUCCESS or an error code.
|
||||
* A proper reference is written in the refs/heads namespace
|
||||
* pointing to the provided target commit.
|
||||
*/
|
||||
GIT_EXTERN(int) git_branch_create(
|
||||
git_oid *oid_out,
|
||||
git_repository *repo,
|
||||
const char *branch_name,
|
||||
const git_object *target,
|
||||
int force);
|
||||
|
||||
/**
|
||||
* Delete an existing branch reference.
|
||||
*
|
||||
* @param repo Repository where lives the branch.
|
||||
*
|
||||
* @param branch_name Name of the branch to be deleted;
|
||||
* this name is validated for consistency.
|
||||
*
|
||||
* @param branch_type Type of the considered branch. This should
|
||||
* be valued with either GIT_BRANCH_LOCAL or GIT_BRANCH_REMOTE.
|
||||
*
|
||||
* @return GIT_SUCCESS on success, GIT_ENOTFOUND if the branch
|
||||
* doesn't exist or an error code.
|
||||
*/
|
||||
GIT_EXTERN(int) git_branch_delete(
|
||||
git_repository *repo,
|
||||
const char *branch_name,
|
||||
enum git_branch_type branch_type);
|
||||
|
||||
/**
|
||||
* Fill a list with all the branches in the Repository
|
||||
*
|
||||
* The string array will be filled with the names of the
|
||||
* matching branches; these values are owned by the user and
|
||||
* should be free'd manually when no longer needed, using
|
||||
* `git_strarray_free`.
|
||||
*
|
||||
* @param branch_names Pointer to a git_strarray structure
|
||||
* where the branch names will be stored.
|
||||
*
|
||||
* @param repo Repository where to find the branches.
|
||||
*
|
||||
* @param list_flags Filtering flags for the branch
|
||||
* listing. Valid values are GIT_BRANCH_LOCAL, GIT_BRANCH_REMOTE
|
||||
* or a combination of the two.
|
||||
*
|
||||
* @return GIT_SUCCESS or an error code.
|
||||
*/
|
||||
GIT_EXTERN(int) git_branch_list(
|
||||
git_strarray *branch_names,
|
||||
git_repository *repo,
|
||||
unsigned int list_flags);
|
||||
|
||||
/** @} */
|
||||
GIT_END_DECL
|
||||
#endif
|
||||
|
@ -160,6 +160,11 @@ typedef enum {
|
||||
GIT_REF_LISTALL = GIT_REF_OID|GIT_REF_SYMBOLIC|GIT_REF_PACKED,
|
||||
} git_rtype;
|
||||
|
||||
/** Basic type of any Git branch. */
|
||||
typedef enum {
|
||||
GIT_BRANCH_LOCAL = 1,
|
||||
GIT_BRANCH_REMOTE = 2,
|
||||
} git_branch_type;
|
||||
|
||||
typedef struct git_refspec git_refspec;
|
||||
typedef struct git_remote git_remote;
|
||||
|
180
src/branch.c
Normal file
180
src/branch.c
Normal file
@ -0,0 +1,180 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "commit.h"
|
||||
#include "branch.h"
|
||||
#include "tag.h"
|
||||
|
||||
static int retrieve_branch_reference(
|
||||
git_reference **branch_reference_out,
|
||||
git_repository *repo,
|
||||
const char *branch_name,
|
||||
int is_remote)
|
||||
{
|
||||
git_reference *branch;
|
||||
int error = -1;
|
||||
char *prefix;
|
||||
git_buf ref_name = GIT_BUF_INIT;
|
||||
|
||||
*branch_reference_out = NULL;
|
||||
|
||||
prefix = is_remote ? GIT_REFS_REMOTES_DIR : GIT_REFS_HEADS_DIR;
|
||||
|
||||
if (git_buf_joinpath(&ref_name, prefix, branch_name) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if ((error = git_reference_lookup(&branch, repo, ref_name.ptr)) < 0) {
|
||||
giterr_set(GITERR_REFERENCE,
|
||||
"Cannot locate %s branch '%s'.", is_remote ? "remote-tracking" : "local", branch_name);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
*branch_reference_out = branch;
|
||||
|
||||
cleanup:
|
||||
git_buf_free(&ref_name);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int create_error_invalid(const char *msg)
|
||||
{
|
||||
giterr_set(GITERR_INVALID, "Cannot create branch - %s", msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int git_branch_create(
|
||||
git_oid *oid_out,
|
||||
git_repository *repo,
|
||||
const char *branch_name,
|
||||
const git_object *target,
|
||||
int force)
|
||||
{
|
||||
git_otype target_type = GIT_OBJ_BAD;
|
||||
git_object *commit = NULL;
|
||||
git_reference *branch = NULL;
|
||||
git_buf canonical_branch_name = GIT_BUF_INIT;
|
||||
int error = -1;
|
||||
|
||||
assert(repo && branch_name && target && oid_out);
|
||||
|
||||
if (git_object_owner(target) != repo)
|
||||
return create_error_invalid("The given target does not belong to this repository");
|
||||
|
||||
target_type = git_object_type(target);
|
||||
|
||||
switch (target_type)
|
||||
{
|
||||
case GIT_OBJ_TAG:
|
||||
if (git_tag_peel(&commit, (git_tag *)target) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (git_object_type(commit) != GIT_OBJ_COMMIT) {
|
||||
create_error_invalid("The given target does not resolve to a commit");
|
||||
goto cleanup;
|
||||
}
|
||||
break;
|
||||
|
||||
case GIT_OBJ_COMMIT:
|
||||
commit = (git_object *)target;
|
||||
break;
|
||||
|
||||
default:
|
||||
return create_error_invalid("Only git_tag and git_commit objects are valid targets.");
|
||||
}
|
||||
|
||||
if (git_buf_joinpath(&canonical_branch_name, GIT_REFS_HEADS_DIR, branch_name) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (git_reference_create_oid(&branch, repo, git_buf_cstr(&canonical_branch_name), git_object_id(commit), force) < 0)
|
||||
goto cleanup;
|
||||
|
||||
git_oid_cpy(oid_out, git_reference_oid(branch));
|
||||
error = 0;
|
||||
|
||||
cleanup:
|
||||
if (target_type == GIT_OBJ_TAG)
|
||||
git_object_free(commit);
|
||||
|
||||
git_reference_free(branch);
|
||||
git_buf_free(&canonical_branch_name);
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_branch_delete(git_repository *repo, const char *branch_name, enum git_branch_type branch_type)
|
||||
{
|
||||
git_reference *branch = NULL;
|
||||
git_reference *head = NULL;
|
||||
int error;
|
||||
|
||||
assert((branch_type == GIT_BRANCH_LOCAL) || (branch_type == GIT_BRANCH_REMOTE));
|
||||
|
||||
if ((error = retrieve_branch_reference(&branch, repo, branch_name, branch_type == GIT_BRANCH_REMOTE)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (git_reference_lookup(&head, repo, GIT_HEAD_FILE) < 0) {
|
||||
giterr_set(GITERR_REFERENCE, "Cannot locate HEAD.");
|
||||
error = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if ((git_reference_type(head) == GIT_REF_SYMBOLIC)
|
||||
&& (strcmp(git_reference_target(head), git_reference_name(branch)) == 0)) {
|
||||
giterr_set(GITERR_REFERENCE,
|
||||
"Cannot delete branch '%s' as it is the current HEAD of the repository.", branch_name);
|
||||
error = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
return git_reference_delete(branch);
|
||||
|
||||
cleanup:
|
||||
git_reference_free(head);
|
||||
git_reference_free(branch);
|
||||
return error;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
git_vector *branchlist;
|
||||
unsigned int branch_type;
|
||||
} branch_filter_data;
|
||||
|
||||
static int branch_list_cb(const char *branch_name, void *payload)
|
||||
{
|
||||
branch_filter_data *filter = (branch_filter_data *)payload;
|
||||
|
||||
if ((filter->branch_type & GIT_BRANCH_LOCAL && git__prefixcmp(branch_name, GIT_REFS_HEADS_DIR) == 0)
|
||||
|| (filter->branch_type & GIT_BRANCH_REMOTE && git__prefixcmp(branch_name, GIT_REFS_REMOTES_DIR) == 0))
|
||||
return git_vector_insert(filter->branchlist, git__strdup(branch_name));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_branch_list(git_strarray *branch_names, git_repository *repo, unsigned int list_flags)
|
||||
{
|
||||
int error;
|
||||
branch_filter_data filter;
|
||||
git_vector branchlist;
|
||||
|
||||
assert(branch_names && repo);
|
||||
|
||||
if (git_vector_init(&branchlist, 8, NULL) < 0)
|
||||
return -1;
|
||||
|
||||
filter.branchlist = &branchlist;
|
||||
filter.branch_type = list_flags;
|
||||
|
||||
error = git_reference_foreach(repo, GIT_REF_OID|GIT_REF_PACKED, &branch_list_cb, (void *)&filter);
|
||||
if (error < 0) {
|
||||
git_vector_free(&branchlist);
|
||||
return -1;
|
||||
}
|
||||
|
||||
branch_names->strings = (char **)branchlist.contents;
|
||||
branch_names->count = branchlist.length;
|
||||
return 0;
|
||||
}
|
17
src/branch.h
Normal file
17
src/branch.h
Normal file
@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
*/
|
||||
#ifndef INCLUDE_branch_h__
|
||||
#define INCLUDE_branch_h__
|
||||
|
||||
#include "git2/branch.h"
|
||||
|
||||
struct git_branch {
|
||||
char *remote; /* TODO: Make this a git_remote */
|
||||
char *merge;
|
||||
};
|
||||
|
||||
#endif
|
114
tests-clar/refs/branches/create.c
Normal file
114
tests-clar/refs/branches/create.c
Normal file
@ -0,0 +1,114 @@
|
||||
#include "clar_libgit2.h"
|
||||
#include "refs.h"
|
||||
#include "branch.h"
|
||||
|
||||
static git_repository *repo;
|
||||
static git_reference *fake_remote;
|
||||
static git_oid branch_target_oid;
|
||||
static git_object *target;
|
||||
|
||||
void test_refs_branches_create__initialize(void)
|
||||
{
|
||||
cl_fixture_sandbox("testrepo.git");
|
||||
cl_git_pass(git_repository_open(&repo, "testrepo.git"));
|
||||
}
|
||||
|
||||
void test_refs_branches_create__cleanup(void)
|
||||
{
|
||||
git_object_free(target);
|
||||
git_repository_free(repo);
|
||||
|
||||
cl_fixture_cleanup("testrepo.git");
|
||||
}
|
||||
|
||||
static void retrieve_target_from_oid(git_object **object_out, git_repository *repo, const char *sha)
|
||||
{
|
||||
git_oid oid;
|
||||
|
||||
cl_git_pass(git_oid_fromstr(&oid, sha));
|
||||
cl_git_pass(git_object_lookup(object_out, repo, &oid, GIT_OBJ_ANY));
|
||||
}
|
||||
|
||||
static void retrieve_known_commit(git_object **object, git_repository *repo)
|
||||
{
|
||||
retrieve_target_from_oid(object, repo, "e90810b8df3e80c413d903f631643c716887138d");
|
||||
}
|
||||
|
||||
#define NEW_BRANCH_NAME "new-branch-on-the-block"
|
||||
|
||||
void test_refs_branches_create__can_create_a_local_branch(void)
|
||||
{
|
||||
retrieve_known_commit(&target, repo);
|
||||
|
||||
cl_git_pass(git_branch_create(&branch_target_oid, repo, NEW_BRANCH_NAME, target, 0));
|
||||
cl_git_pass(git_oid_cmp(&branch_target_oid, git_object_id(target)));
|
||||
}
|
||||
|
||||
void test_refs_branches_create__creating_a_local_branch_triggers_the_creation_of_a_new_direct_reference(void)
|
||||
{
|
||||
git_reference *branch;
|
||||
|
||||
retrieve_known_commit(&target, repo);
|
||||
|
||||
cl_git_fail(git_reference_lookup(&branch, repo, GIT_REFS_HEADS_DIR NEW_BRANCH_NAME));
|
||||
|
||||
cl_git_pass(git_branch_create(&branch_target_oid, repo, NEW_BRANCH_NAME, target, 0));
|
||||
|
||||
cl_git_pass(git_reference_lookup(&branch, repo, GIT_REFS_HEADS_DIR NEW_BRANCH_NAME));
|
||||
cl_assert(git_reference_type(branch) == GIT_REF_OID);
|
||||
|
||||
git_reference_free(branch);
|
||||
}
|
||||
|
||||
void test_refs_branches_create__can_not_create_a_branch_if_its_name_collide_with_an_existing_one(void)
|
||||
{
|
||||
retrieve_known_commit(&target, repo);
|
||||
|
||||
cl_git_fail(git_branch_create(&branch_target_oid, repo, "br2", target, 0));
|
||||
}
|
||||
|
||||
void test_refs_branches_create__can_force_create_over_an_existing_branch(void)
|
||||
{
|
||||
retrieve_known_commit(&target, repo);
|
||||
|
||||
cl_git_pass(git_branch_create(&branch_target_oid, repo, "br2", target, 1));
|
||||
cl_git_pass(git_oid_cmp(&branch_target_oid, git_object_id(target)));
|
||||
}
|
||||
|
||||
void test_refs_branches_create__can_not_create_a_branch_pointing_at_an_object_unknown_from_the_repository(void)
|
||||
{
|
||||
git_repository *repo2;
|
||||
|
||||
/* Open another instance of the same repository */
|
||||
cl_git_pass(git_repository_open(&repo2, cl_fixture("testrepo.git")));
|
||||
|
||||
/* Retrieve a commit object from this different repository */
|
||||
retrieve_known_commit(&target, repo2);
|
||||
|
||||
cl_git_fail(git_branch_create(&branch_target_oid, repo, NEW_BRANCH_NAME, target, 0));
|
||||
|
||||
git_repository_free(repo2);
|
||||
}
|
||||
|
||||
void test_refs_branches_create__creating_a_branch_targeting_a_tag_dereferences_it_to_its_commit(void)
|
||||
{
|
||||
/* b25fa35 is a tag, pointing to another tag which points to a commit */
|
||||
retrieve_target_from_oid(&target, repo, "b25fa35b38051e4ae45d4222e795f9df2e43f1d1");
|
||||
|
||||
cl_git_pass(git_branch_create(&branch_target_oid, repo, NEW_BRANCH_NAME, target, 0));
|
||||
cl_git_pass(git_oid_streq(&branch_target_oid, "e90810b8df3e80c413d903f631643c716887138d"));
|
||||
}
|
||||
|
||||
void test_refs_branches_create__can_not_create_a_branch_pointing_to_a_non_commit_object(void)
|
||||
{
|
||||
/* 53fc32d is the tree of commit e90810b */
|
||||
retrieve_target_from_oid(&target, repo, "53fc32d17276939fc79ed05badaef2db09990016");
|
||||
|
||||
cl_git_fail(git_branch_create(&branch_target_oid, repo, NEW_BRANCH_NAME, target, 0));
|
||||
git_object_free(target);
|
||||
|
||||
/* 521d87c is an annotated tag pointing to a blob */
|
||||
retrieve_target_from_oid(&target, repo, "521d87c1ec3aef9824daf6d96cc0ae3710766d91");
|
||||
|
||||
cl_git_fail(git_branch_create(&branch_target_oid, repo, NEW_BRANCH_NAME, target, 0));
|
||||
}
|
76
tests-clar/refs/branches/delete.c
Normal file
76
tests-clar/refs/branches/delete.c
Normal file
@ -0,0 +1,76 @@
|
||||
#include "clar_libgit2.h"
|
||||
#include "refs.h"
|
||||
#include "branch.h"
|
||||
|
||||
static git_repository *repo;
|
||||
static git_reference *fake_remote;
|
||||
|
||||
void test_refs_branches_delete__initialize(void)
|
||||
{
|
||||
git_oid id;
|
||||
|
||||
cl_fixture_sandbox("testrepo.git");
|
||||
cl_git_pass(git_repository_open(&repo, "testrepo.git"));
|
||||
|
||||
cl_git_pass(git_oid_fromstr(&id, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"));
|
||||
cl_git_pass(git_reference_create_oid(&fake_remote, repo, "refs/remotes/nulltoken/master", &id, 0));
|
||||
}
|
||||
|
||||
void test_refs_branches_delete__cleanup(void)
|
||||
{
|
||||
git_reference_free(fake_remote);
|
||||
git_repository_free(repo);
|
||||
|
||||
cl_fixture_cleanup("testrepo.git");
|
||||
}
|
||||
|
||||
void test_refs_branches_delete__can_not_delete_a_non_existing_branch(void)
|
||||
{
|
||||
cl_git_fail(git_branch_delete(repo, "i-am-not-a-local-branch", GIT_BRANCH_LOCAL));
|
||||
cl_git_fail(git_branch_delete(repo, "neither/a-remote-one", GIT_BRANCH_REMOTE));
|
||||
}
|
||||
|
||||
void test_refs_branches_delete__can_not_delete_a_branch_pointed_at_by_HEAD(void)
|
||||
{
|
||||
git_reference *head;
|
||||
|
||||
/* Ensure HEAD targets the local master branch */
|
||||
cl_git_pass(git_reference_lookup(&head, repo, GIT_HEAD_FILE));
|
||||
cl_assert(strcmp("refs/heads/master", git_reference_target(head)) == 0);
|
||||
git_reference_free(head);
|
||||
|
||||
cl_git_fail(git_branch_delete(repo, "master", GIT_BRANCH_LOCAL));
|
||||
}
|
||||
|
||||
void test_refs_branches_delete__can_not_delete_a_branch_if_HEAD_is_missing(void)
|
||||
{
|
||||
git_reference *head;
|
||||
|
||||
cl_git_pass(git_reference_lookup(&head, repo, GIT_HEAD_FILE));
|
||||
git_reference_delete(head);
|
||||
|
||||
cl_git_fail(git_branch_delete(repo, "br2", GIT_BRANCH_LOCAL));
|
||||
}
|
||||
|
||||
void test_refs_branches_delete__can_delete_a_branch_pointed_at_by_detached_HEAD(void)
|
||||
{
|
||||
git_reference *master, *head;
|
||||
|
||||
/* Detach HEAD and make it target the commit that "master" points to */
|
||||
cl_git_pass(git_reference_lookup(&master, repo, "refs/heads/master"));
|
||||
cl_git_pass(git_reference_create_oid(&head, repo, "HEAD", git_reference_oid(master), 1));
|
||||
git_reference_free(head);
|
||||
git_reference_free(master);
|
||||
|
||||
cl_git_pass(git_branch_delete(repo, "master", GIT_BRANCH_LOCAL));
|
||||
}
|
||||
|
||||
void test_refs_branches_delete__can_delete_a_local_branch(void)
|
||||
{
|
||||
cl_git_pass(git_branch_delete(repo, "br2", GIT_BRANCH_LOCAL));
|
||||
}
|
||||
|
||||
void test_refs_branches_delete__can_delete_a_remote_branch(void)
|
||||
{
|
||||
cl_git_pass(git_branch_delete(repo, "nulltoken/master", GIT_BRANCH_REMOTE));
|
||||
}
|
49
tests-clar/refs/branches/listall.c
Normal file
49
tests-clar/refs/branches/listall.c
Normal file
@ -0,0 +1,49 @@
|
||||
#include "clar_libgit2.h"
|
||||
#include "refs.h"
|
||||
#include "branch.h"
|
||||
|
||||
static git_repository *repo;
|
||||
static git_strarray branch_list;
|
||||
static git_reference *fake_remote;
|
||||
|
||||
void test_refs_branches_listall__initialize(void)
|
||||
{
|
||||
git_oid id;
|
||||
|
||||
cl_fixture_sandbox("testrepo.git");
|
||||
cl_git_pass(git_repository_open(&repo, "testrepo.git"));
|
||||
|
||||
cl_git_pass(git_oid_fromstr(&id, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"));
|
||||
cl_git_pass(git_reference_create_oid(&fake_remote, repo, "refs/remotes/nulltoken/master", &id, 0));
|
||||
}
|
||||
|
||||
void test_refs_branches_listall__cleanup(void)
|
||||
{
|
||||
git_strarray_free(&branch_list);
|
||||
git_reference_free(fake_remote);
|
||||
git_repository_free(repo);
|
||||
|
||||
cl_fixture_cleanup("testrepo.git");
|
||||
}
|
||||
|
||||
static void assert_retrieval(unsigned int flags, unsigned int expected_count)
|
||||
{
|
||||
cl_git_pass(git_branch_list(&branch_list, repo, flags));
|
||||
|
||||
cl_assert(branch_list.count == expected_count);
|
||||
}
|
||||
|
||||
void test_refs_branches_listall__retrieve_all_branches(void)
|
||||
{
|
||||
assert_retrieval(GIT_BRANCH_LOCAL | GIT_BRANCH_REMOTE, 6 + 1);
|
||||
}
|
||||
|
||||
void test_refs_branches_listall__retrieve_remote_branches(void)
|
||||
{
|
||||
assert_retrieval(GIT_BRANCH_REMOTE, 1);
|
||||
}
|
||||
|
||||
void test_refs_branches_listall__retrieve_local_branches(void)
|
||||
{
|
||||
assert_retrieval(GIT_BRANCH_LOCAL, 6);
|
||||
}
|
Loading…
Reference in New Issue
Block a user