mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-29 06:20:56 +00:00
commit
6c08e69fd9
@ -44,5 +44,6 @@
|
||||
#include "git2/indexer.h"
|
||||
#include "git2/submodule.h"
|
||||
#include "git2/notes.h"
|
||||
#include "git2/reset.h"
|
||||
|
||||
#endif
|
||||
|
44
include/git2/reset.h
Normal file
44
include/git2/reset.h
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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_git_reset_h__
|
||||
#define INCLUDE_git_reset_h__
|
||||
|
||||
/**
|
||||
* @file git2/reset.h
|
||||
* @brief Git reset management routines
|
||||
* @ingroup Git
|
||||
* @{
|
||||
*/
|
||||
GIT_BEGIN_DECL
|
||||
|
||||
/**
|
||||
* Sets the current head to the specified commit oid and optionally
|
||||
* resets the index and working tree to match.
|
||||
*
|
||||
* When specifying a Soft kind of reset, the head will be moved to the commit.
|
||||
*
|
||||
* Specifying a Mixed kind of reset will trigger a Soft reset and the index will
|
||||
* be replaced with the content of the commit tree.
|
||||
*
|
||||
* TODO: Implement remaining kinds of resets.
|
||||
*
|
||||
* @param repo Repository where to perform the reset operation.
|
||||
*
|
||||
* @param target Object to which the Head should be moved to. 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 reset_type Kind of reset operation to perform.
|
||||
*
|
||||
* @return GIT_SUCCESS or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_reset(git_repository *repo, const git_object *target, git_reset_type reset_type);
|
||||
|
||||
/** @} */
|
||||
GIT_END_DECL
|
||||
#endif
|
@ -166,6 +166,12 @@ typedef enum {
|
||||
GIT_BRANCH_REMOTE = 2,
|
||||
} git_branch_t;
|
||||
|
||||
/** Kinds of reset operation. */
|
||||
typedef enum {
|
||||
GIT_RESET_SOFT = 1,
|
||||
GIT_RESET_MIXED = 2,
|
||||
} git_reset_type;
|
||||
|
||||
typedef struct git_refspec git_refspec;
|
||||
typedef struct git_remote git_remote;
|
||||
|
||||
|
62
src/commit.c
62
src/commit.c
@ -81,66 +81,6 @@ int git_commit_create_v(
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Update the reference named `ref_name` so it points to `oid` */
|
||||
static int update_reference(git_repository *repo, git_oid *oid, const char *ref_name)
|
||||
{
|
||||
git_reference *ref;
|
||||
int res;
|
||||
|
||||
res = git_reference_lookup(&ref, repo, ref_name);
|
||||
|
||||
/* If we haven't found the reference at all, we assume we need to create
|
||||
* a new reference and that's it */
|
||||
if (res == GIT_ENOTFOUND) {
|
||||
giterr_clear();
|
||||
return git_reference_create_oid(NULL, repo, ref_name, oid, 1);
|
||||
}
|
||||
|
||||
if (res < 0)
|
||||
return -1;
|
||||
|
||||
/* If we have found a reference, but it's symbolic, we need to update
|
||||
* the direct reference it points to */
|
||||
if (git_reference_type(ref) == GIT_REF_SYMBOLIC) {
|
||||
git_reference *aux;
|
||||
const char *sym_target;
|
||||
|
||||
/* The target pointed at by this reference */
|
||||
sym_target = git_reference_target(ref);
|
||||
|
||||
/* resolve the reference to the target it points to */
|
||||
res = git_reference_resolve(&aux, ref);
|
||||
|
||||
/*
|
||||
* if the symbolic reference pointed to an inexisting ref,
|
||||
* this is means we're creating a new branch, for example.
|
||||
* We need to create a new direct reference with that name
|
||||
*/
|
||||
if (res == GIT_ENOTFOUND) {
|
||||
giterr_clear();
|
||||
res = git_reference_create_oid(NULL, repo, sym_target, oid, 1);
|
||||
git_reference_free(ref);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* free the original symbolic reference now; not before because
|
||||
* we're using the `sym_target` pointer */
|
||||
git_reference_free(ref);
|
||||
|
||||
if (res < 0)
|
||||
return -1;
|
||||
|
||||
/* store the newly found direct reference in its place */
|
||||
ref = aux;
|
||||
}
|
||||
|
||||
/* ref is made to point to `oid`: ref is either the original reference,
|
||||
* or the target of the symbolic reference we've looked up */
|
||||
res = git_reference_set_oid(ref, oid);
|
||||
git_reference_free(ref);
|
||||
return res;
|
||||
}
|
||||
|
||||
int git_commit_create(
|
||||
git_oid *oid,
|
||||
git_repository *repo,
|
||||
@ -192,7 +132,7 @@ int git_commit_create(
|
||||
git_buf_free(&commit);
|
||||
|
||||
if (update_ref != NULL)
|
||||
return update_reference(repo, oid, update_ref);
|
||||
return git_reference__update(repo, oid, update_ref);
|
||||
|
||||
return 0;
|
||||
|
||||
|
59
src/refs.c
59
src/refs.c
@ -1705,3 +1705,62 @@ int git_reference_cmp(git_reference *ref1, git_reference *ref2)
|
||||
return git_oid_cmp(&ref1->target.oid, &ref2->target.oid);
|
||||
}
|
||||
|
||||
/* Update the reference named `ref_name` so it points to `oid` */
|
||||
int git_reference__update(git_repository *repo, const git_oid *oid, const char *ref_name)
|
||||
{
|
||||
git_reference *ref;
|
||||
int res;
|
||||
|
||||
res = git_reference_lookup(&ref, repo, ref_name);
|
||||
|
||||
/* If we haven't found the reference at all, we assume we need to create
|
||||
* a new reference and that's it */
|
||||
if (res == GIT_ENOTFOUND) {
|
||||
giterr_clear();
|
||||
return git_reference_create_oid(NULL, repo, ref_name, oid, 1);
|
||||
}
|
||||
|
||||
if (res < 0)
|
||||
return -1;
|
||||
|
||||
/* If we have found a reference, but it's symbolic, we need to update
|
||||
* the direct reference it points to */
|
||||
if (git_reference_type(ref) == GIT_REF_SYMBOLIC) {
|
||||
git_reference *aux;
|
||||
const char *sym_target;
|
||||
|
||||
/* The target pointed at by this reference */
|
||||
sym_target = git_reference_target(ref);
|
||||
|
||||
/* resolve the reference to the target it points to */
|
||||
res = git_reference_resolve(&aux, ref);
|
||||
|
||||
/*
|
||||
* if the symbolic reference pointed to an inexisting ref,
|
||||
* this is means we're creating a new branch, for example.
|
||||
* We need to create a new direct reference with that name
|
||||
*/
|
||||
if (res == GIT_ENOTFOUND) {
|
||||
giterr_clear();
|
||||
res = git_reference_create_oid(NULL, repo, sym_target, oid, 1);
|
||||
git_reference_free(ref);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* free the original symbolic reference now; not before because
|
||||
* we're using the `sym_target` pointer */
|
||||
git_reference_free(ref);
|
||||
|
||||
if (res < 0)
|
||||
return -1;
|
||||
|
||||
/* store the newly found direct reference in its place */
|
||||
ref = aux;
|
||||
}
|
||||
|
||||
/* ref is made to point to `oid`: ref is either the original reference,
|
||||
* or the target of the symbolic reference we've looked up */
|
||||
res = git_reference_set_oid(ref, oid);
|
||||
git_reference_free(ref);
|
||||
return res;
|
||||
}
|
||||
|
@ -54,6 +54,7 @@ void git_repository__refcache_free(git_refcache *refs);
|
||||
|
||||
int git_reference__normalize_name(char *buffer_out, size_t out_size, const char *name);
|
||||
int git_reference__normalize_name_oid(char *buffer_out, size_t out_size, const char *name);
|
||||
int git_reference__update(git_repository *repo, const git_oid *oid, const char *ref_name);
|
||||
|
||||
/**
|
||||
* Lookup a reference by name and try to resolve to an OID.
|
||||
|
103
src/reset.c
Normal file
103
src/reset.c
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* 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 "tag.h"
|
||||
#include "git2/reset.h"
|
||||
|
||||
#define ERROR_MSG "Cannot perform reset"
|
||||
|
||||
static int reset_error_invalid(const char *msg)
|
||||
{
|
||||
giterr_set(GITERR_INVALID, "%s - %s", ERROR_MSG, msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int git_reset(
|
||||
git_repository *repo,
|
||||
const git_object *target,
|
||||
git_reset_type reset_type)
|
||||
{
|
||||
git_otype target_type = GIT_OBJ_BAD;
|
||||
git_object *commit = NULL;
|
||||
git_index *index = NULL;
|
||||
git_tree *tree = NULL;
|
||||
int error = -1;
|
||||
|
||||
assert(repo && target);
|
||||
assert(reset_type == GIT_RESET_SOFT || reset_type == GIT_RESET_MIXED);
|
||||
|
||||
if (git_object_owner(target) != repo)
|
||||
return reset_error_invalid("The given target does not belong to this repository.");
|
||||
|
||||
if (reset_type == GIT_RESET_MIXED && git_repository_is_bare(repo))
|
||||
return reset_error_invalid("Mixed reset is not allowed in a bare 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) {
|
||||
reset_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 reset_error_invalid("Only git_tag and git_commit objects are valid targets.");
|
||||
}
|
||||
|
||||
//TODO: Check for unmerged entries
|
||||
|
||||
if (git_reference__update(repo, git_object_id(commit), GIT_HEAD_FILE) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (reset_type == GIT_RESET_SOFT) {
|
||||
error = 0;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (git_commit_tree(&tree, (git_commit *)commit) < 0) {
|
||||
giterr_set(GITERR_OBJECT, "%s - Failed to retrieve the commit tree.", ERROR_MSG);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (git_repository_index(&index, repo) < 0) {
|
||||
giterr_set(GITERR_OBJECT, "%s - Failed to retrieve the index.", ERROR_MSG);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (git_index_read_tree(index, tree) < 0) {
|
||||
giterr_set(GITERR_INDEX, "%s - Failed to update the index.", ERROR_MSG);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (git_index_write(index) < 0) {
|
||||
giterr_set(GITERR_INDEX, "%s - Failed to write the index.", ERROR_MSG);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
error = 0;
|
||||
|
||||
cleanup:
|
||||
if (target_type == GIT_OBJ_TAG)
|
||||
git_object_free(commit);
|
||||
|
||||
git_index_free(index);
|
||||
git_tree_free(tree);
|
||||
|
||||
return error;
|
||||
}
|
47
tests-clar/reset/mixed.c
Normal file
47
tests-clar/reset/mixed.c
Normal file
@ -0,0 +1,47 @@
|
||||
#include "clar_libgit2.h"
|
||||
#include "posix.h"
|
||||
#include "reset_helpers.h"
|
||||
#include "path.h"
|
||||
|
||||
static git_repository *repo;
|
||||
static git_object *target;
|
||||
|
||||
void test_reset_mixed__initialize(void)
|
||||
{
|
||||
repo = cl_git_sandbox_init("attr");
|
||||
target = NULL;
|
||||
}
|
||||
|
||||
void test_reset_mixed__cleanup(void)
|
||||
{
|
||||
git_object_free(target);
|
||||
cl_git_sandbox_cleanup();
|
||||
}
|
||||
|
||||
void test_reset_mixed__cannot_reset_in_a_bare_repository(void)
|
||||
{
|
||||
git_repository *bare;
|
||||
|
||||
cl_git_pass(git_repository_open(&bare, cl_fixture("testrepo.git")));
|
||||
cl_assert(git_repository_is_bare(bare) == true);
|
||||
|
||||
retrieve_target_from_oid(&target, bare, KNOWN_COMMIT_IN_BARE_REPO);
|
||||
|
||||
cl_git_fail(git_reset(bare, target, GIT_RESET_MIXED));
|
||||
|
||||
git_repository_free(bare);
|
||||
}
|
||||
|
||||
void test_reset_mixed__resetting_refreshes_the_index_to_the_commit_tree(void)
|
||||
{
|
||||
unsigned int status;
|
||||
|
||||
cl_git_pass(git_status_file(&status, repo, "macro_bad"));
|
||||
cl_assert(status == GIT_STATUS_CURRENT);
|
||||
retrieve_target_from_oid(&target, repo, "605812ab7fe421fdd325a935d35cb06a9234a7d7");
|
||||
|
||||
cl_git_pass(git_reset(repo, target, GIT_RESET_MIXED));
|
||||
|
||||
cl_git_pass(git_status_file(&status, repo, "macro_bad"));
|
||||
cl_assert(status == GIT_STATUS_WT_NEW);
|
||||
}
|
10
tests-clar/reset/reset_helpers.c
Normal file
10
tests-clar/reset/reset_helpers.c
Normal file
@ -0,0 +1,10 @@
|
||||
#include "clar_libgit2.h"
|
||||
#include "reset_helpers.h"
|
||||
|
||||
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));
|
||||
}
|
6
tests-clar/reset/reset_helpers.h
Normal file
6
tests-clar/reset/reset_helpers.h
Normal file
@ -0,0 +1,6 @@
|
||||
#include "common.h"
|
||||
|
||||
#define KNOWN_COMMIT_IN_BARE_REPO "e90810b8df3e80c413d903f631643c716887138d"
|
||||
#define KNOWN_COMMIT_IN_ATTR_REPO "217878ab49e1314388ea2e32dc6fdb58a1b969e0"
|
||||
|
||||
extern void retrieve_target_from_oid(git_object **object_out, git_repository *repo, const char *sha);
|
102
tests-clar/reset/soft.c
Normal file
102
tests-clar/reset/soft.c
Normal file
@ -0,0 +1,102 @@
|
||||
#include "clar_libgit2.h"
|
||||
#include "reset_helpers.h"
|
||||
|
||||
static git_repository *repo;
|
||||
static git_object *target;
|
||||
|
||||
void test_reset_soft__initialize(void)
|
||||
{
|
||||
repo = cl_git_sandbox_init("testrepo.git");
|
||||
}
|
||||
|
||||
void test_reset_soft__cleanup(void)
|
||||
{
|
||||
git_object_free(target);
|
||||
cl_git_sandbox_cleanup();
|
||||
}
|
||||
|
||||
static void assert_reset_soft(bool should_be_detached)
|
||||
{
|
||||
git_oid oid;
|
||||
|
||||
cl_git_pass(git_reference_name_to_oid(&oid, repo, "HEAD"));
|
||||
cl_git_fail(git_oid_streq(&oid, KNOWN_COMMIT_IN_BARE_REPO));
|
||||
|
||||
retrieve_target_from_oid(&target, repo, KNOWN_COMMIT_IN_BARE_REPO);
|
||||
|
||||
cl_assert(git_repository_head_detached(repo) == should_be_detached);
|
||||
|
||||
cl_git_pass(git_reset(repo, target, GIT_RESET_SOFT));
|
||||
|
||||
cl_assert(git_repository_head_detached(repo) == should_be_detached);
|
||||
|
||||
cl_git_pass(git_reference_name_to_oid(&oid, repo, "HEAD"));
|
||||
cl_git_pass(git_oid_streq(&oid, KNOWN_COMMIT_IN_BARE_REPO));
|
||||
}
|
||||
|
||||
void test_reset_soft__can_reset_the_non_detached_Head_to_the_specified_commit(void)
|
||||
{
|
||||
assert_reset_soft(false);
|
||||
}
|
||||
|
||||
static void detach_head(void)
|
||||
{
|
||||
git_reference *head;
|
||||
git_oid oid;
|
||||
|
||||
cl_git_pass(git_reference_name_to_oid(&oid, repo, "HEAD"));
|
||||
|
||||
cl_git_pass(git_reference_create_oid(&head, repo, "HEAD", &oid, true));
|
||||
git_reference_free(head);
|
||||
}
|
||||
|
||||
void test_reset_soft__can_reset_the_detached_Head_to_the_specified_commit(void)
|
||||
{
|
||||
detach_head();
|
||||
|
||||
assert_reset_soft(true);
|
||||
}
|
||||
|
||||
void test_reset_soft__resetting_to_the_commit_pointed_at_by_the_Head_does_not_change_the_target_of_the_Head(void)
|
||||
{
|
||||
git_oid oid;
|
||||
char raw_head_oid[GIT_OID_HEXSZ + 1];
|
||||
|
||||
cl_git_pass(git_reference_name_to_oid(&oid, repo, "HEAD"));
|
||||
git_oid_fmt(raw_head_oid, &oid);
|
||||
raw_head_oid[GIT_OID_HEXSZ] = '\0';
|
||||
|
||||
retrieve_target_from_oid(&target, repo, raw_head_oid);
|
||||
|
||||
cl_git_pass(git_reset(repo, target, GIT_RESET_SOFT));
|
||||
|
||||
cl_git_pass(git_reference_name_to_oid(&oid, repo, "HEAD"));
|
||||
cl_git_pass(git_oid_streq(&oid, raw_head_oid));
|
||||
}
|
||||
|
||||
void test_reset_soft__resetting_to_a_tag_sets_the_Head_to_the_peeled_commit(void)
|
||||
{
|
||||
git_oid oid;
|
||||
|
||||
/* b25fa35 is a tag, pointing to another tag which points to commit e90810b */
|
||||
retrieve_target_from_oid(&target, repo, "b25fa35b38051e4ae45d4222e795f9df2e43f1d1");
|
||||
|
||||
cl_git_pass(git_reset(repo, target, GIT_RESET_SOFT));
|
||||
|
||||
cl_assert(git_repository_head_detached(repo) == false);
|
||||
cl_git_pass(git_reference_name_to_oid(&oid, repo, "HEAD"));
|
||||
cl_git_pass(git_oid_streq(&oid, KNOWN_COMMIT_IN_BARE_REPO));
|
||||
}
|
||||
|
||||
void test_reset_soft__cannot_reset_to_a_tag_not_pointing_at_a_commit(void)
|
||||
{
|
||||
/* 53fc32d is the tree of commit e90810b */
|
||||
retrieve_target_from_oid(&target, repo, "53fc32d17276939fc79ed05badaef2db09990016");
|
||||
|
||||
cl_git_fail(git_reset(repo, target, GIT_RESET_SOFT));
|
||||
git_object_free(target);
|
||||
|
||||
/* 521d87c is an annotated tag pointing to a blob */
|
||||
retrieve_target_from_oid(&target, repo, "521d87c1ec3aef9824daf6d96cc0ae3710766d91");
|
||||
cl_git_fail(git_reset(repo, target, GIT_RESET_SOFT));
|
||||
}
|
Loading…
Reference in New Issue
Block a user