mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-29 22:25:04 +00:00
Merge pull request #1920 from libgit2/cmn/ref-with-log
Reference operations with log
This commit is contained in:
commit
4e1f517c61
@ -47,7 +47,7 @@ GIT_EXTERN(int) git_reflog_read(git_reflog **out, git_repository *repo, const c
|
||||
GIT_EXTERN(int) git_reflog_write(git_reflog *reflog);
|
||||
|
||||
/**
|
||||
* Add a new entry to the reflog.
|
||||
* Add a new entry to the in-memory reflog.
|
||||
*
|
||||
* `msg` is optional and can be NULL.
|
||||
*
|
||||
@ -59,23 +59,6 @@ GIT_EXTERN(int) git_reflog_write(git_reflog *reflog);
|
||||
*/
|
||||
GIT_EXTERN(int) git_reflog_append(git_reflog *reflog, const git_oid *id, const git_signature *committer, const char *msg);
|
||||
|
||||
/**
|
||||
* Add a new entry to the named reflog.
|
||||
*
|
||||
* This utility function loads the named reflog, appends to it and
|
||||
* writes it back out to the backend.
|
||||
*
|
||||
* `msg` is optional and can be NULL.
|
||||
*
|
||||
* @param repo the repository to act on
|
||||
* @param name the reflog's name
|
||||
* @param id the OID the reference is now pointing to
|
||||
* @param committer the signature of the committer
|
||||
* @param msg the reflog message
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_reflog_append_to(git_repository *repo, const char *name, const git_oid *id, const git_signature *committer, const char *msg);
|
||||
|
||||
/**
|
||||
* Rename a reflog
|
||||
*
|
||||
|
@ -98,6 +98,48 @@ GIT_EXTERN(int) git_reference_dwim(git_reference **out, git_repository *repo, co
|
||||
*/
|
||||
GIT_EXTERN(int) git_reference_symbolic_create(git_reference **out, git_repository *repo, const char *name, const char *target, int force);
|
||||
|
||||
/**
|
||||
* Create a new symbolic reference and update the reflog with a given
|
||||
* message.
|
||||
*
|
||||
* A symbolic reference is a reference name that refers to another
|
||||
* reference name. If the other name moves, the symbolic name will move,
|
||||
* too. As a simple example, the "HEAD" reference might refer to
|
||||
* "refs/heads/master" while on the "master" branch of a repository.
|
||||
*
|
||||
* The symbolic reference will be created in the repository and written to
|
||||
* the disk. The generated reference object must be freed by the user.
|
||||
*
|
||||
* Valid reference names must follow one of two patterns:
|
||||
*
|
||||
* 1. Top-level names must contain only capital letters and underscores,
|
||||
* and must begin and end with a letter. (e.g. "HEAD", "ORIG_HEAD").
|
||||
* 2. Names prefixed with "refs/" can be almost anything. You must avoid
|
||||
* the characters '~', '^', ':', '\\', '?', '[', and '*', and the
|
||||
* sequences ".." and "@{" which have special meaning to revparse.
|
||||
*
|
||||
* This function will return an error if a reference already exists with the
|
||||
* given name unless `force` is true, in which case it will be overwritten.
|
||||
*
|
||||
* @param out Pointer to the newly created reference
|
||||
* @param repo Repository where that reference will live
|
||||
* @param name The name of the reference
|
||||
* @param target The target of the reference
|
||||
* @param force Overwrite existing references
|
||||
* @param signature The identity that will used to populate the reflog entry
|
||||
* @param log_message The one line long message that has to be appended
|
||||
* to the reflog
|
||||
* @return 0 on success, EEXISTS, EINVALIDSPEC or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_reference_symbolic_create_with_log(
|
||||
git_reference **out,
|
||||
git_repository *repo,
|
||||
const char *name,
|
||||
const char *target,
|
||||
int force,
|
||||
const git_signature *signature,
|
||||
const char *log_message);
|
||||
|
||||
/**
|
||||
* Create a new direct reference.
|
||||
*
|
||||
@ -130,6 +172,49 @@ GIT_EXTERN(int) git_reference_symbolic_create(git_reference **out, git_repositor
|
||||
*/
|
||||
GIT_EXTERN(int) git_reference_create(git_reference **out, git_repository *repo, const char *name, const git_oid *id, int force);
|
||||
|
||||
/**
|
||||
* Create a new direct reference and update the reflog with a given
|
||||
* message.
|
||||
*
|
||||
* A direct reference (also called an object id reference) refers directly
|
||||
* to a specific object id (a.k.a. OID or SHA) in the repository. The id
|
||||
* permanently refers to the object (although the reference itself can be
|
||||
* moved). For example, in libgit2 the direct ref "refs/tags/v0.17.0"
|
||||
* refers to OID 5b9fac39d8a76b9139667c26a63e6b3f204b3977.
|
||||
*
|
||||
* The direct reference will be created in the repository and written to
|
||||
* the disk. The generated reference object must be freed by the user.
|
||||
*
|
||||
* Valid reference names must follow one of two patterns:
|
||||
*
|
||||
* 1. Top-level names must contain only capital letters and underscores,
|
||||
* and must begin and end with a letter. (e.g. "HEAD", "ORIG_HEAD").
|
||||
* 2. Names prefixed with "refs/" can be almost anything. You must avoid
|
||||
* the characters '~', '^', ':', '\\', '?', '[', and '*', and the
|
||||
* sequences ".." and "@{" which have special meaning to revparse.
|
||||
*
|
||||
* This function will return an error if a reference already exists with the
|
||||
* given name unless `force` is true, in which case it will be overwritten.
|
||||
*
|
||||
* @param out Pointer to the newly created reference
|
||||
* @param repo Repository where that reference will live
|
||||
* @param name The name of the reference
|
||||
* @param id The object id pointed to by the reference.
|
||||
* @param force Overwrite existing references
|
||||
* @param signature The identity that will used to populate the reflog entry
|
||||
* @param log_message The one line long message that has to be appended
|
||||
* to the reflog
|
||||
* @return 0 on success, EEXISTS, EINVALIDSPEC or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_reference_create_with_log(
|
||||
git_reference **out,
|
||||
git_repository *repo,
|
||||
const char *name,
|
||||
const git_oid *id,
|
||||
int force,
|
||||
const git_signature *signature,
|
||||
const char *log_message);
|
||||
|
||||
/**
|
||||
* Get the OID pointed to by a direct reference.
|
||||
*
|
||||
@ -232,6 +317,31 @@ GIT_EXTERN(int) git_reference_symbolic_set_target(
|
||||
git_reference *ref,
|
||||
const char *target);
|
||||
|
||||
/**
|
||||
* Create a new reference with the same name as the given reference but a
|
||||
* different symbolic target and update the reflog with a given message.
|
||||
*
|
||||
* The reference must be a symbolic reference, otherwise this will fail.
|
||||
*
|
||||
* The new reference will be written to disk, overwriting the given reference.
|
||||
*
|
||||
* The target name will be checked for validity.
|
||||
* See `git_reference_create_symbolic()` for rules about valid names.
|
||||
*
|
||||
* @param out Pointer to the newly created reference
|
||||
* @param ref The reference
|
||||
* @param target The new target for the reference
|
||||
* @param signature The identity that will used to populate the reflog entry
|
||||
* @param log_message The one line long message that has to be appended
|
||||
* @return 0 on success, EINVALIDSPEC or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_reference_symbolic_set_target_with_log(
|
||||
git_reference **out,
|
||||
git_reference *ref,
|
||||
const char *target,
|
||||
const git_signature *signature,
|
||||
const char *log_message);
|
||||
|
||||
/**
|
||||
* Create a new reference with the same name as the given reference but a
|
||||
* different OID target. The reference must be a direct reference, otherwise
|
||||
@ -249,6 +359,29 @@ GIT_EXTERN(int) git_reference_set_target(
|
||||
git_reference *ref,
|
||||
const git_oid *id);
|
||||
|
||||
/**
|
||||
* Create a new reference with the same name as the given reference but a
|
||||
* different OID target and update the reflog with a given message.
|
||||
*
|
||||
* The reference must be a direct reference, otherwise this will fail.
|
||||
*
|
||||
* The new reference will be written to disk, overwriting the given reference.
|
||||
*
|
||||
* @param out Pointer to the newly created reference
|
||||
* @param ref The reference
|
||||
* @param id The new target OID for the reference
|
||||
* @param signature The identity that will used to populate the reflog entry
|
||||
* @param log_message The one line long message that has to be appended
|
||||
* to the reflog
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_reference_set_target_with_log(
|
||||
git_reference **out,
|
||||
git_reference *ref,
|
||||
const git_oid *id,
|
||||
const git_signature *signature,
|
||||
const char *log_message);
|
||||
|
||||
/**
|
||||
* Rename an existing reference.
|
||||
*
|
||||
@ -428,12 +561,24 @@ GIT_EXTERN(int) git_reference_foreach_glob(
|
||||
/**
|
||||
* Check if a reflog exists for the specified reference.
|
||||
*
|
||||
* @param ref A git reference
|
||||
*
|
||||
* @param repo the repository
|
||||
* @param refname the reference's name
|
||||
* @return 0 when no reflog can be found, 1 when it exists;
|
||||
* otherwise an error code.
|
||||
*/
|
||||
GIT_EXTERN(int) git_reference_has_log(git_reference *ref);
|
||||
GIT_EXTERN(int) git_reference_has_log(git_repository *repo, const char *refname);
|
||||
|
||||
/**
|
||||
* Ensure there is a reflog for a particular reference.
|
||||
*
|
||||
* Make sure that successive updates to the reference will append to
|
||||
* its log.
|
||||
*
|
||||
* @param repo the repository
|
||||
* @param refname the reference's name
|
||||
* @return 0 or an error code.
|
||||
*/
|
||||
GIT_EXTERN(int) git_reference_ensure_log(git_repository *repo, const char *refname);
|
||||
|
||||
/**
|
||||
* Check if a reference is a local branch.
|
||||
|
@ -93,11 +93,13 @@ struct git_refdb_backend {
|
||||
* must provide this function.
|
||||
*/
|
||||
int (*write)(git_refdb_backend *backend,
|
||||
const git_reference *ref, int force);
|
||||
const git_reference *ref, int force,
|
||||
const git_signature *who, const char *message);
|
||||
|
||||
int (*rename)(
|
||||
git_reference **out, git_refdb_backend *backend,
|
||||
const char *old_name, const char *new_name, int force);
|
||||
const char *old_name, const char *new_name, int force,
|
||||
const git_signature *who, const char *message);
|
||||
|
||||
/**
|
||||
* Deletes the given reference from the refdb. A refdb implementation
|
||||
@ -114,6 +116,17 @@ struct git_refdb_backend {
|
||||
*/
|
||||
int (*compress)(git_refdb_backend *backend);
|
||||
|
||||
/**
|
||||
* Query whether a particular reference has a log (may be empty)
|
||||
*/
|
||||
int (*has_log)(git_refdb_backend *backend, const char *refname);
|
||||
|
||||
/**
|
||||
* Make sure a particular reference will have a reflog which
|
||||
* will be appended to on writes.
|
||||
*/
|
||||
int (*ensure_log)(git_refdb_backend *backend, const char *refname);
|
||||
|
||||
/**
|
||||
* Frees any resources held by the refdb. A refdb implementation may
|
||||
* provide this function; if it is not provided, nothing will be done.
|
||||
|
24
src/refdb.c
24
src/refdb.c
@ -167,14 +167,14 @@ void git_refdb_iterator_free(git_reference_iterator *iter)
|
||||
iter->free(iter);
|
||||
}
|
||||
|
||||
int git_refdb_write(git_refdb *db, git_reference *ref, int force)
|
||||
int git_refdb_write(git_refdb *db, git_reference *ref, int force, const git_signature *who, const char *message)
|
||||
{
|
||||
assert(db && db->backend);
|
||||
|
||||
GIT_REFCOUNT_INC(db);
|
||||
ref->db = db;
|
||||
|
||||
return db->backend->write(db->backend, ref, force);
|
||||
return db->backend->write(db->backend, ref, force, who, message);
|
||||
}
|
||||
|
||||
int git_refdb_rename(
|
||||
@ -182,12 +182,14 @@ int git_refdb_rename(
|
||||
git_refdb *db,
|
||||
const char *old_name,
|
||||
const char *new_name,
|
||||
int force)
|
||||
int force,
|
||||
const git_signature *who,
|
||||
const char *message)
|
||||
{
|
||||
int error;
|
||||
|
||||
assert(db && db->backend);
|
||||
error = db->backend->rename(out, db->backend, old_name, new_name, force);
|
||||
error = db->backend->rename(out, db->backend, old_name, new_name, force, who, message);
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
@ -219,3 +221,17 @@ int git_refdb_reflog_read(git_reflog **out, git_refdb *db, const char *name)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_refdb_has_log(git_refdb *db, const char *refname)
|
||||
{
|
||||
assert(db && refname);
|
||||
|
||||
return db->backend->has_log(db->backend, refname);
|
||||
}
|
||||
|
||||
int git_refdb_ensure_log(git_refdb *db, const char *refname)
|
||||
{
|
||||
assert(db && refname);
|
||||
|
||||
return db->backend->ensure_log(db->backend, refname);
|
||||
}
|
||||
|
10
src/refdb.h
10
src/refdb.h
@ -33,18 +33,24 @@ int git_refdb_rename(
|
||||
git_refdb *db,
|
||||
const char *old_name,
|
||||
const char *new_name,
|
||||
int force);
|
||||
int force,
|
||||
const git_signature *who,
|
||||
const char *message);
|
||||
|
||||
int git_refdb_iterator(git_reference_iterator **out, git_refdb *db, const char *glob);
|
||||
int git_refdb_iterator_next(git_reference **out, git_reference_iterator *iter);
|
||||
int git_refdb_iterator_next_name(const char **out, git_reference_iterator *iter);
|
||||
void git_refdb_iterator_free(git_reference_iterator *iter);
|
||||
|
||||
int git_refdb_write(git_refdb *refdb, git_reference *ref, int force);
|
||||
int git_refdb_write(git_refdb *refdb, git_reference *ref, int force, const git_signature *who, const char *message);
|
||||
int git_refdb_delete(git_refdb *refdb, const char *ref_name);
|
||||
|
||||
int git_refdb_reflog_read(git_reflog **out, git_refdb *db, const char *name);
|
||||
int git_refdb_reflog_write(git_reflog *reflog);
|
||||
|
||||
int git_refdb_has_log(git_refdb *db, const char *refname);
|
||||
int git_refdb_ensure_log(git_refdb *refdb, const char *refname);
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
217
src/refdb_fs.c
217
src/refdb_fs.c
@ -688,9 +688,8 @@ static int reference_path_available(
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int loose_write(refdb_fs_backend *backend, const git_reference *ref)
|
||||
static int loose_lock(git_filebuf *file, refdb_fs_backend *backend, const git_reference *ref)
|
||||
{
|
||||
git_filebuf file = GIT_FILEBUF_INIT;
|
||||
git_buf ref_path = GIT_BUF_INIT;
|
||||
|
||||
/* Remove a possibly existing empty directory hierarchy
|
||||
@ -702,25 +701,29 @@ static int loose_write(refdb_fs_backend *backend, const git_reference *ref)
|
||||
if (git_buf_joinpath(&ref_path, backend->path, ref->name) < 0)
|
||||
return -1;
|
||||
|
||||
if (git_filebuf_open(&file, ref_path.ptr, GIT_FILEBUF_FORCE, GIT_REFS_FILE_MODE) < 0) {
|
||||
if (git_filebuf_open(file, ref_path.ptr, GIT_FILEBUF_FORCE, GIT_REFS_FILE_MODE) < 0) {
|
||||
git_buf_free(&ref_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
git_buf_free(&ref_path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int loose_commit(git_filebuf *file, const git_reference *ref)
|
||||
{
|
||||
if (ref->type == GIT_REF_OID) {
|
||||
char oid[GIT_OID_HEXSZ + 1];
|
||||
git_oid_nfmt(oid, sizeof(oid), &ref->target.oid);
|
||||
|
||||
git_filebuf_printf(&file, "%s\n", oid);
|
||||
git_filebuf_printf(file, "%s\n", oid);
|
||||
} else if (ref->type == GIT_REF_SYMBOLIC) {
|
||||
git_filebuf_printf(&file, GIT_SYMREF "%s\n", ref->target.symbolic);
|
||||
git_filebuf_printf(file, GIT_SYMREF "%s\n", ref->target.symbolic);
|
||||
} else {
|
||||
assert(0); /* don't let this happen */
|
||||
}
|
||||
|
||||
return git_filebuf_commit(&file);
|
||||
return git_filebuf_commit(file);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -907,12 +910,33 @@ fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int reflog_append(refdb_fs_backend *backend, const git_reference *ref, const git_signature *author, const char *message);
|
||||
static int has_reflog(git_repository *repo, const char *name);
|
||||
|
||||
/* We only write if it's under heads/, remotes/ or notes/ or if it already has a log */
|
||||
static bool should_write_reflog(git_repository *repo, const char *name)
|
||||
{
|
||||
if (has_reflog(repo, name))
|
||||
return 1;
|
||||
|
||||
if (!git__prefixcmp(name, GIT_REFS_HEADS_DIR) ||
|
||||
!git__strcmp(name, GIT_HEAD_FILE) ||
|
||||
!git__prefixcmp(name, GIT_REFS_REMOTES_DIR) ||
|
||||
!git__prefixcmp(name, GIT_REFS_NOTES_DIR))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int refdb_fs_backend__write(
|
||||
git_refdb_backend *_backend,
|
||||
const git_reference *ref,
|
||||
int force)
|
||||
int force,
|
||||
const git_signature *who,
|
||||
const char *message)
|
||||
{
|
||||
refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
|
||||
git_filebuf file = GIT_FILEBUF_INIT;
|
||||
int error;
|
||||
|
||||
assert(backend);
|
||||
@ -921,7 +945,17 @@ static int refdb_fs_backend__write(
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
return loose_write(backend, ref);
|
||||
/* We need to perform the reflog append under the ref's lock */
|
||||
if ((error = loose_lock(&file, backend, ref)) < 0)
|
||||
return error;
|
||||
|
||||
if (should_write_reflog(backend->repo, ref->name) &&
|
||||
(error = reflog_append(backend, ref, who, message)) < 0) {
|
||||
git_filebuf_cleanup(&file);
|
||||
return error;
|
||||
}
|
||||
|
||||
return loose_commit(&file, ref);
|
||||
}
|
||||
|
||||
static int refdb_fs_backend__delete(
|
||||
@ -969,15 +1003,20 @@ static int refdb_fs_backend__delete(
|
||||
return packed_write(backend);
|
||||
}
|
||||
|
||||
static int refdb_reflog_fs__rename(git_refdb_backend *_backend, const char *old_name, const char *new_name);
|
||||
|
||||
static int refdb_fs_backend__rename(
|
||||
git_reference **out,
|
||||
git_refdb_backend *_backend,
|
||||
const char *old_name,
|
||||
const char *new_name,
|
||||
int force)
|
||||
int force,
|
||||
const git_signature *who,
|
||||
const char *message)
|
||||
{
|
||||
refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
|
||||
git_reference *old, *new;
|
||||
git_filebuf file = GIT_FILEBUF_INIT;
|
||||
int error;
|
||||
|
||||
assert(backend);
|
||||
@ -998,7 +1037,28 @@ static int refdb_fs_backend__rename(
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((error = loose_write(backend, new)) < 0 || out == NULL) {
|
||||
if ((error = loose_lock(&file, backend, new)) < 0) {
|
||||
git_reference_free(new);
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Try to rename the refog; it's ok if the old doesn't exist */
|
||||
error = refdb_reflog_fs__rename(_backend, old_name, new_name);
|
||||
if (((error == 0) || (error == GIT_ENOTFOUND)) &&
|
||||
((error = reflog_append(backend, new, who, message)) < 0)) {
|
||||
git_reference_free(new);
|
||||
git_filebuf_cleanup(&file);
|
||||
return error;
|
||||
}
|
||||
|
||||
if (error < 0) {
|
||||
git_reference_free(new);
|
||||
git_filebuf_cleanup(&file);
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
if ((error = loose_commit(&file, new)) < 0 || out == NULL) {
|
||||
git_reference_free(new);
|
||||
return error;
|
||||
}
|
||||
@ -1173,7 +1233,7 @@ static int create_new_reflog_file(const char *filepath)
|
||||
return error;
|
||||
|
||||
if ((fd = p_open(filepath,
|
||||
O_WRONLY | O_CREAT | O_TRUNC,
|
||||
O_WRONLY | O_CREAT,
|
||||
GIT_REFLOG_FILE_MODE)) < 0)
|
||||
return -1;
|
||||
|
||||
@ -1185,6 +1245,50 @@ GIT_INLINE(int) retrieve_reflog_path(git_buf *path, git_repository *repo, const
|
||||
return git_buf_join_n(path, '/', 3, repo->path_repository, GIT_REFLOG_DIR, name);
|
||||
}
|
||||
|
||||
static int refdb_reflog_fs__ensure_log(git_refdb_backend *_backend, const char *name)
|
||||
{
|
||||
refdb_fs_backend *backend;
|
||||
git_repository *repo;
|
||||
git_buf path = GIT_BUF_INIT;
|
||||
int error;
|
||||
|
||||
assert(_backend && name);
|
||||
|
||||
backend = (refdb_fs_backend *) _backend;
|
||||
repo = backend->repo;
|
||||
|
||||
if ((error = retrieve_reflog_path(&path, repo, name)) < 0)
|
||||
return error;
|
||||
|
||||
return create_new_reflog_file(git_buf_cstr(&path));
|
||||
}
|
||||
|
||||
static int has_reflog(git_repository *repo, const char *name)
|
||||
{
|
||||
int ret = 0;
|
||||
git_buf path = GIT_BUF_INIT;
|
||||
|
||||
if (retrieve_reflog_path(&path, repo, name) < 0)
|
||||
goto cleanup;
|
||||
|
||||
ret = git_path_isfile(git_buf_cstr(&path));
|
||||
|
||||
cleanup:
|
||||
git_buf_free(&path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int refdb_reflog_fs__has_log(git_refdb_backend *_backend, const char *name)
|
||||
{
|
||||
refdb_fs_backend *backend;
|
||||
|
||||
assert(_backend && name);
|
||||
|
||||
backend = (refdb_fs_backend *) _backend;
|
||||
|
||||
return has_reflog(backend->repo, name);
|
||||
}
|
||||
|
||||
static int refdb_reflog_fs__read(git_reflog **out, git_refdb_backend *_backend, const char *name)
|
||||
{
|
||||
int error = -1;
|
||||
@ -1264,34 +1368,48 @@ static int serialize_reflog_entry(
|
||||
return git_buf_oom(buf);
|
||||
}
|
||||
|
||||
static int lock_reflog(git_filebuf *file, refdb_fs_backend *backend, const char *refname)
|
||||
{
|
||||
git_repository *repo;
|
||||
git_buf log_path = GIT_BUF_INIT;
|
||||
int error;
|
||||
|
||||
repo = backend->repo;
|
||||
|
||||
if (retrieve_reflog_path(&log_path, repo, refname) < 0)
|
||||
return -1;
|
||||
|
||||
if (!git_path_isfile(git_buf_cstr(&log_path))) {
|
||||
giterr_set(GITERR_INVALID,
|
||||
"Log file for reference '%s' doesn't exist.", refname);
|
||||
error = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
error = git_filebuf_open(file, git_buf_cstr(&log_path), 0, GIT_REFLOG_FILE_MODE);
|
||||
|
||||
cleanup:
|
||||
git_buf_free(&log_path);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int refdb_reflog_fs__write(git_refdb_backend *_backend, git_reflog *reflog)
|
||||
{
|
||||
int error = -1;
|
||||
unsigned int i;
|
||||
git_reflog_entry *entry;
|
||||
git_repository *repo;
|
||||
refdb_fs_backend *backend;
|
||||
git_buf log_path = GIT_BUF_INIT;
|
||||
git_buf log = GIT_BUF_INIT;
|
||||
git_filebuf fbuf = GIT_FILEBUF_INIT;
|
||||
|
||||
assert(_backend && reflog);
|
||||
|
||||
backend = (refdb_fs_backend *) _backend;
|
||||
repo = backend->repo;
|
||||
|
||||
if (retrieve_reflog_path(&log_path, repo, reflog->ref_name) < 0)
|
||||
if ((error = lock_reflog(&fbuf, backend, reflog->ref_name)) < 0)
|
||||
return -1;
|
||||
|
||||
if (!git_path_isfile(git_buf_cstr(&log_path))) {
|
||||
giterr_set(GITERR_INVALID,
|
||||
"Log file for reference '%s' doesn't exist.", reflog->ref_name);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if ((error = git_filebuf_open(&fbuf, git_buf_cstr(&log_path), 0, GIT_REFLOG_FILE_MODE)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
git_vector_foreach(&reflog->entries, i, entry) {
|
||||
if (serialize_reflog_entry(&log, &(entry->oid_old), &(entry->oid_cur), entry->committer, entry->msg) < 0)
|
||||
goto cleanup;
|
||||
@ -1308,7 +1426,49 @@ cleanup:
|
||||
|
||||
success:
|
||||
git_buf_free(&log);
|
||||
git_buf_free(&log_path);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Append to the reflog, must be called under reference lock */
|
||||
static int reflog_append(refdb_fs_backend *backend, const git_reference *ref, const git_signature *who, const char *message)
|
||||
{
|
||||
int error;
|
||||
git_oid old_id, new_id;
|
||||
git_buf buf = GIT_BUF_INIT, path = GIT_BUF_INIT;
|
||||
git_repository *repo = backend->repo;
|
||||
|
||||
/* Creation of symbolic references doesn't get a reflog entry */
|
||||
if (ref->type == GIT_REF_SYMBOLIC)
|
||||
return 0;
|
||||
|
||||
error = git_reference_name_to_id(&old_id, repo, ref->name);
|
||||
if (error == GIT_ENOTFOUND) {
|
||||
memset(&old_id, 0, sizeof(git_oid));
|
||||
error = 0;
|
||||
}
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
git_oid_cpy(&new_id, git_reference_target(ref));
|
||||
|
||||
if ((error = serialize_reflog_entry(&buf, &old_id, &new_id, who, message)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if ((error = retrieve_reflog_path(&path, repo, ref->name)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (((error = git_futils_mkpath2file(git_buf_cstr(&path), 0777)) < 0) &&
|
||||
(error != GIT_EEXISTS)) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
error = git_futils_writebuffer(&buf, git_buf_cstr(&path), O_WRONLY|O_CREAT|O_APPEND, GIT_REFLOG_FILE_MODE);
|
||||
|
||||
cleanup:
|
||||
git_buf_free(&buf);
|
||||
git_buf_free(&path);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -1340,6 +1500,11 @@ static int refdb_reflog_fs__rename(git_refdb_backend *_backend, const char *old_
|
||||
if (git_buf_joinpath(&new_path, git_buf_cstr(&temp_path), git_buf_cstr(&normalized)) < 0)
|
||||
return -1;
|
||||
|
||||
if (!git_path_exists(git_buf_cstr(&old_path))) {
|
||||
error = GIT_ENOTFOUND;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/*
|
||||
* Move the reflog to a temporary place. This two-phase renaming is required
|
||||
* in order to cope with funny renaming use cases when one tries to move a reference
|
||||
@ -1454,6 +1619,8 @@ int git_refdb_backend_fs(
|
||||
backend->parent.del = &refdb_fs_backend__delete;
|
||||
backend->parent.rename = &refdb_fs_backend__rename;
|
||||
backend->parent.compress = &refdb_fs_backend__compress;
|
||||
backend->parent.has_log = &refdb_reflog_fs__has_log;
|
||||
backend->parent.ensure_log = &refdb_reflog_fs__ensure_log;
|
||||
backend->parent.free = &refdb_fs_backend__free;
|
||||
backend->parent.reflog_read = &refdb_reflog_fs__read;
|
||||
backend->parent.reflog_write = &refdb_reflog_fs__write;
|
||||
|
19
src/reflog.c
19
src/reflog.c
@ -230,22 +230,3 @@ int git_reflog_drop(git_reflog *reflog, size_t idx, int rewrite_previous_entry)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_reflog_append_to(git_repository *repo, const char *name, const git_oid *id,
|
||||
const git_signature *committer, const char *msg)
|
||||
{
|
||||
int error;
|
||||
git_reflog *reflog;
|
||||
|
||||
if ((error = git_reflog_read(&reflog, repo, name)) < 0)
|
||||
return error;
|
||||
|
||||
if ((error = git_reflog_append(reflog, id, committer, msg)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
error = git_reflog_write(reflog);
|
||||
|
||||
cleanup:
|
||||
git_reflog_free(reflog);
|
||||
return error;
|
||||
}
|
||||
|
217
src/refs.c
217
src/refs.c
@ -21,6 +21,7 @@
|
||||
#include <git2/refs.h>
|
||||
#include <git2/refdb.h>
|
||||
#include <git2/sys/refs.h>
|
||||
#include <git2/signature.h>
|
||||
|
||||
GIT__USE_STRMAP;
|
||||
|
||||
@ -328,13 +329,18 @@ static int reference__create(
|
||||
const char *name,
|
||||
const git_oid *oid,
|
||||
const char *symbolic,
|
||||
int force)
|
||||
int force,
|
||||
const git_signature *signature,
|
||||
const char *log_message)
|
||||
{
|
||||
char normalized[GIT_REFNAME_MAX];
|
||||
git_refdb *refdb;
|
||||
git_reference *ref = NULL;
|
||||
int error = 0;
|
||||
|
||||
assert(repo && name);
|
||||
assert(symbolic || signature);
|
||||
|
||||
if (ref_out)
|
||||
*ref_out = NULL;
|
||||
|
||||
@ -347,15 +353,34 @@ static int reference__create(
|
||||
return error;
|
||||
|
||||
if (oid != NULL) {
|
||||
git_odb *odb;
|
||||
|
||||
assert(symbolic == NULL);
|
||||
ref = git_reference__alloc(normalized, oid, NULL);
|
||||
|
||||
/* Sanity check the reference being created - target must exist. */
|
||||
if ((error = git_repository_odb__weakptr(&odb, repo)) < 0)
|
||||
return error;
|
||||
|
||||
if (!git_odb_exists(odb, oid)) {
|
||||
giterr_set(GITERR_REFERENCE,
|
||||
"Target OID for the reference doesn't exist on the repository");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ref = git_reference__alloc(name, oid, NULL);
|
||||
} else {
|
||||
ref = git_reference__alloc_symbolic(normalized, symbolic);
|
||||
char normalized_target[GIT_REFNAME_MAX];
|
||||
|
||||
if ((error = git_reference__normalize_name_lax(
|
||||
normalized_target, sizeof(normalized_target), symbolic)) < 0)
|
||||
return error;
|
||||
|
||||
ref = git_reference__alloc_symbolic(name, normalized_target);
|
||||
}
|
||||
|
||||
GITERR_CHECK_ALLOC(ref);
|
||||
|
||||
if ((error = git_refdb_write(refdb, ref, force)) < 0) {
|
||||
if ((error = git_refdb_write(refdb, ref, force, signature, log_message)) < 0) {
|
||||
git_reference_free(ref);
|
||||
return error;
|
||||
}
|
||||
@ -375,22 +400,37 @@ int git_reference_create(
|
||||
const git_oid *oid,
|
||||
int force)
|
||||
{
|
||||
git_odb *odb;
|
||||
int error = 0;
|
||||
git_signature *who;
|
||||
int error;
|
||||
|
||||
assert(repo && name && oid);
|
||||
assert(oid);
|
||||
|
||||
/* Sanity check the reference being created - target must exist. */
|
||||
if ((error = git_repository_odb__weakptr(&odb, repo)) < 0)
|
||||
/* Should we return an error if there is no default? */
|
||||
if (((error = git_signature_default(&who, repo)) < 0) &&
|
||||
((error = git_signature_now(&who, "unknown", "unknown")) < 0)) {
|
||||
return error;
|
||||
|
||||
if (!git_odb_exists(odb, oid)) {
|
||||
giterr_set(GITERR_REFERENCE,
|
||||
"Target OID for the reference doesn't exist on the repository");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return reference__create(ref_out, repo, name, oid, NULL, force);
|
||||
error = reference__create(ref_out, repo, name, oid, NULL, force, who, NULL);
|
||||
|
||||
git_signature_free(who);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_reference_create_with_log(
|
||||
git_reference **ref_out,
|
||||
git_repository *repo,
|
||||
const char *name,
|
||||
const git_oid *oid,
|
||||
int force,
|
||||
const git_signature *signature,
|
||||
const char *log_message)
|
||||
{
|
||||
assert(oid && signature && log_message);
|
||||
|
||||
return reference__create(
|
||||
ref_out, repo, name, oid, NULL, force, signature, log_message);
|
||||
}
|
||||
|
||||
int git_reference_symbolic_create(
|
||||
@ -400,16 +440,17 @@ int git_reference_symbolic_create(
|
||||
const char *target,
|
||||
int force)
|
||||
{
|
||||
char normalized[GIT_REFNAME_MAX];
|
||||
int error = 0;
|
||||
assert(target);
|
||||
return reference__create(ref_out, repo, name, NULL, target, force, NULL, NULL);
|
||||
}
|
||||
|
||||
assert(repo && name && target);
|
||||
static int ensure_is_an_updatable_direct_reference(git_reference *ref)
|
||||
{
|
||||
if (ref->type == GIT_REF_OID)
|
||||
return 0;
|
||||
|
||||
if ((error = git_reference__normalize_name_lax(
|
||||
normalized, sizeof(normalized), target)) < 0)
|
||||
return error;
|
||||
|
||||
return reference__create(ref_out, repo, name, NULL, normalized, force);
|
||||
giterr_set(GITERR_REFERENCE, "Cannot set OID on symbolic reference");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int git_reference_set_target(
|
||||
@ -417,43 +458,68 @@ int git_reference_set_target(
|
||||
git_reference *ref,
|
||||
const git_oid *id)
|
||||
{
|
||||
int error;
|
||||
|
||||
assert(out && ref && id);
|
||||
|
||||
if (ref->type != GIT_REF_OID) {
|
||||
giterr_set(GITERR_REFERENCE, "Cannot set OID on symbolic reference");
|
||||
return -1;
|
||||
}
|
||||
if ((error = ensure_is_an_updatable_direct_reference(ref)) < 0)
|
||||
return error;
|
||||
|
||||
return git_reference_create(out, ref->db->repo, ref->name, id, 1);
|
||||
}
|
||||
|
||||
int git_reference_set_target_with_log(
|
||||
git_reference **out,
|
||||
git_reference *ref,
|
||||
const git_oid *id,
|
||||
const git_signature *signature,
|
||||
const char *log_message)
|
||||
{
|
||||
int error;
|
||||
|
||||
assert(out && ref && id);
|
||||
assert(signature && log_message);
|
||||
|
||||
if ((error = ensure_is_an_updatable_direct_reference(ref)) < 0)
|
||||
return error;
|
||||
|
||||
return git_reference_create_with_log(
|
||||
out, ref->db->repo, ref->name, id, 1, signature, log_message);
|
||||
}
|
||||
|
||||
static int ensure_is_an_updatable_symbolic_reference(git_reference *ref)
|
||||
{
|
||||
if (ref->type == GIT_REF_SYMBOLIC)
|
||||
return 0;
|
||||
|
||||
giterr_set(GITERR_REFERENCE, "Cannot set symbolic target on a direct reference");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int git_reference_symbolic_set_target(
|
||||
git_reference **out,
|
||||
git_reference *ref,
|
||||
const char *target)
|
||||
{
|
||||
int error;
|
||||
|
||||
assert(out && ref && target);
|
||||
|
||||
if (ref->type != GIT_REF_SYMBOLIC) {
|
||||
giterr_set(GITERR_REFERENCE,
|
||||
"Cannot set symbolic target on a direct reference");
|
||||
return -1;
|
||||
}
|
||||
if ((error = ensure_is_an_updatable_symbolic_reference(ref)) < 0)
|
||||
return error;
|
||||
|
||||
return git_reference_symbolic_create(out, ref->db->repo, ref->name, target, 1);
|
||||
}
|
||||
|
||||
int git_reference_rename(
|
||||
git_reference **out,
|
||||
git_reference *ref,
|
||||
const char *new_name,
|
||||
int force)
|
||||
static int reference__rename(git_reference **out, git_reference *ref, const char *new_name, int force,
|
||||
const git_signature *signature, const char *message)
|
||||
{
|
||||
unsigned int normalization_flags;
|
||||
char normalized[GIT_REFNAME_MAX];
|
||||
bool should_head_be_updated = false;
|
||||
int error = 0;
|
||||
int reference_has_log;
|
||||
|
||||
assert(ref && new_name && signature);
|
||||
|
||||
normalization_flags = ref->type == GIT_REF_SYMBOLIC ?
|
||||
GIT_REF_FORMAT_ALLOW_ONELEVEL : GIT_REF_FORMAT_NORMAL;
|
||||
@ -462,13 +528,14 @@ int git_reference_rename(
|
||||
normalized, sizeof(normalized), new_name, normalization_flags)) < 0)
|
||||
return error;
|
||||
|
||||
|
||||
/* Check if we have to update HEAD. */
|
||||
if ((error = git_branch_is_head(ref)) < 0)
|
||||
return error;
|
||||
|
||||
should_head_be_updated = (error > 0);
|
||||
|
||||
if ((error = git_refdb_rename(out, ref->db, ref->name, new_name, force)) < 0)
|
||||
if ((error = git_refdb_rename(out, ref->db, ref->name, new_name, force, signature, message)) < 0)
|
||||
return error;
|
||||
|
||||
/* Update HEAD it was pointing to the reference being renamed */
|
||||
@ -478,17 +545,43 @@ int git_reference_rename(
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Rename the reflog file, if it exists. */
|
||||
reference_has_log = git_reference_has_log(ref);
|
||||
if (reference_has_log < 0)
|
||||
return reference_has_log;
|
||||
|
||||
if (reference_has_log && (error = git_reflog_rename(git_reference_owner(ref), git_reference_name(ref), new_name)) < 0)
|
||||
return error;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int git_reference_rename(
|
||||
git_reference **out,
|
||||
git_reference *ref,
|
||||
const char *new_name,
|
||||
int force)
|
||||
{
|
||||
git_signature *who;
|
||||
int error;
|
||||
|
||||
/* Should we return an error if there is no default? */
|
||||
if (((error = git_signature_default(&who, ref->db->repo)) < 0) &&
|
||||
((error = git_signature_now(&who, "unknown", "unknown")) < 0)) {
|
||||
return error;
|
||||
}
|
||||
|
||||
error = reference__rename(out, ref, new_name, force, who, NULL);
|
||||
|
||||
git_signature_free(who);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_reference_rename_with_log(
|
||||
git_reference **out,
|
||||
git_reference *ref,
|
||||
const char *new_name,
|
||||
int force,
|
||||
const git_signature *who,
|
||||
const char * message)
|
||||
{
|
||||
return reference__rename(out, ref, new_name, force, who, message);
|
||||
}
|
||||
|
||||
int git_reference_resolve(git_reference **ref_out, const git_reference *ref)
|
||||
{
|
||||
switch (git_reference_type(ref)) {
|
||||
@ -956,22 +1049,30 @@ int git_reference__update_terminal(
|
||||
return reference__update_terminal(repo, ref_name, oid, 0);
|
||||
}
|
||||
|
||||
int git_reference_has_log(
|
||||
git_reference *ref)
|
||||
int git_reference_has_log(git_repository *repo, const char *refname)
|
||||
{
|
||||
git_buf path = GIT_BUF_INIT;
|
||||
int result;
|
||||
int error;
|
||||
git_refdb *refdb;
|
||||
|
||||
assert(ref);
|
||||
assert(repo && refname);
|
||||
|
||||
if (git_buf_join_n(&path, '/', 3, ref->db->repo->path_repository,
|
||||
GIT_REFLOG_DIR, ref->name) < 0)
|
||||
return -1;
|
||||
if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
|
||||
return error;
|
||||
|
||||
result = git_path_isfile(git_buf_cstr(&path));
|
||||
git_buf_free(&path);
|
||||
return git_refdb_has_log(refdb, refname);
|
||||
}
|
||||
|
||||
return result;
|
||||
int git_reference_ensure_log(git_repository *repo, const char *refname)
|
||||
{
|
||||
int error;
|
||||
git_refdb *refdb;
|
||||
|
||||
assert(repo && refname);
|
||||
|
||||
if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
|
||||
return error;
|
||||
|
||||
return git_refdb_ensure_log(refdb, refname);
|
||||
}
|
||||
|
||||
int git_reference__is_branch(const char *ref_name)
|
||||
|
@ -19,6 +19,7 @@
|
||||
#define GIT_REFS_HEADS_DIR GIT_REFS_DIR "heads/"
|
||||
#define GIT_REFS_TAGS_DIR GIT_REFS_DIR "tags/"
|
||||
#define GIT_REFS_REMOTES_DIR GIT_REFS_DIR "remotes/"
|
||||
#define GIT_REFS_NOTES_DIR GIT_REFS_DIR "notes/"
|
||||
#define GIT_REFS_DIR_MODE 0777
|
||||
#define GIT_REFS_FILE_MODE 0666
|
||||
|
||||
|
24
src/stash.c
24
src/stash.c
@ -412,25 +412,15 @@ static int update_reflog(
|
||||
const char *message)
|
||||
{
|
||||
git_reference *stash;
|
||||
git_reflog *reflog = NULL;
|
||||
int error;
|
||||
|
||||
if ((error = git_reference_create(&stash, repo, GIT_REFS_STASH_FILE, w_commit_oid, 1)) < 0)
|
||||
goto cleanup;
|
||||
if ((error = git_reference_ensure_log(repo, GIT_REFS_STASH_FILE)) < 0)
|
||||
return error;
|
||||
|
||||
error = git_reference_create_with_log(&stash, repo, GIT_REFS_STASH_FILE, w_commit_oid, 1, stasher, message);
|
||||
|
||||
git_reference_free(stash);
|
||||
|
||||
if ((error = git_reflog_read(&reflog, repo, GIT_REFS_STASH_FILE) < 0))
|
||||
goto cleanup;
|
||||
|
||||
if ((error = git_reflog_append(reflog, w_commit_oid, stasher, message)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if ((error = git_reflog_write(reflog)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
cleanup:
|
||||
git_reflog_free(reflog);
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -638,7 +628,11 @@ int git_stash_drop(
|
||||
entry = git_reflog_entry_byindex(reflog, 0);
|
||||
|
||||
git_reference_free(stash);
|
||||
error = git_reference_create(&stash, repo, GIT_REFS_STASH_FILE, &entry->oid_cur, 1);
|
||||
if ((error = git_reference_create(&stash, repo, GIT_REFS_STASH_FILE, &entry->oid_cur, 1) < 0))
|
||||
goto cleanup;
|
||||
|
||||
/* We need to undo the writing that we just did */
|
||||
error = git_reflog_write(reflog);
|
||||
}
|
||||
|
||||
cleanup:
|
||||
|
51
tests/refs/createwithlog.c
Normal file
51
tests/refs/createwithlog.c
Normal file
@ -0,0 +1,51 @@
|
||||
#include "clar_libgit2.h"
|
||||
|
||||
#include "repository.h"
|
||||
#include "git2/reflog.h"
|
||||
#include "reflog.h"
|
||||
#include "ref_helpers.h"
|
||||
|
||||
static const char *current_master_tip = "a65fedf39aefe402d3bb6e24df4d4f5fe4547750";
|
||||
|
||||
static git_repository *g_repo;
|
||||
|
||||
void test_refs_createwithlog__initialize(void)
|
||||
{
|
||||
g_repo = cl_git_sandbox_init("testrepo.git");
|
||||
}
|
||||
|
||||
void test_refs_createwithlog__cleanup(void)
|
||||
{
|
||||
cl_git_sandbox_cleanup();
|
||||
}
|
||||
|
||||
void test_refs_createwithlog__creating_a_direct_reference_adds_a_reflog_entry(void)
|
||||
{
|
||||
git_reference *reference;
|
||||
git_oid id;
|
||||
git_signature *signature;
|
||||
git_reflog *reflog;
|
||||
const git_reflog_entry *entry;
|
||||
|
||||
const char *name = "refs/heads/new-head";
|
||||
const char *message = "You've been logged, mate!";
|
||||
|
||||
git_oid_fromstr(&id, current_master_tip);
|
||||
|
||||
cl_git_pass(git_signature_now(&signature, "foo", "foo@bar"));
|
||||
|
||||
cl_git_pass(
|
||||
git_reference_create_with_log(&reference, g_repo, name, &id, 0, signature, message));
|
||||
|
||||
cl_git_pass(git_reflog_read(&reflog, g_repo, name));
|
||||
cl_assert_equal_sz(1, git_reflog_entrycount(reflog));
|
||||
|
||||
entry = git_reflog_entry_byindex(reflog, 0);
|
||||
cl_assert(git_oid_streq(&entry->oid_old, GIT_OID_HEX_ZERO) == 0);
|
||||
cl_assert(git_oid_cmp(&id, &entry->oid_cur) == 0);
|
||||
cl_assert_equal_s(message, entry->msg);
|
||||
|
||||
git_reflog_free(reflog);
|
||||
git_reference_free(reference);
|
||||
git_signature_free(signature);
|
||||
}
|
@ -49,17 +49,20 @@ static void assert_appends(const git_signature *committer, const git_oid *oid)
|
||||
|
||||
/* Read and parse the reflog for this branch */
|
||||
cl_git_pass(git_reflog_read(&reflog, repo2, new_ref));
|
||||
cl_assert_equal_i(2, (int)git_reflog_entrycount(reflog));
|
||||
cl_assert_equal_i(3, (int)git_reflog_entrycount(reflog));
|
||||
|
||||
/* The first one was the creation of the branch */
|
||||
entry = git_reflog_entry_byindex(reflog, 2);
|
||||
cl_assert(git_oid_streq(&entry->oid_old, GIT_OID_HEX_ZERO) == 0);
|
||||
|
||||
entry = git_reflog_entry_byindex(reflog, 1);
|
||||
assert_signature(committer, entry->committer);
|
||||
cl_assert(git_oid_streq(&entry->oid_old, GIT_OID_HEX_ZERO) == 0);
|
||||
cl_assert(git_oid_cmp(oid, &entry->oid_old) == 0);
|
||||
cl_assert(git_oid_cmp(oid, &entry->oid_cur) == 0);
|
||||
cl_assert(entry->msg == NULL);
|
||||
|
||||
entry = git_reflog_entry_byindex(reflog, 0);
|
||||
assert_signature(committer, entry->committer);
|
||||
cl_assert(git_oid_cmp(oid, &entry->oid_old) == 0);
|
||||
cl_assert(git_oid_cmp(oid, &entry->oid_cur) == 0);
|
||||
cl_assert_equal_s(commit_msg, entry->msg);
|
||||
|
||||
@ -97,29 +100,6 @@ void test_refs_reflog_reflog__append_then_read(void)
|
||||
git_signature_free(committer);
|
||||
}
|
||||
|
||||
void test_refs_reflog_reflog__append_to_then_read(void)
|
||||
{
|
||||
/* write a reflog for a given reference and ensure it can be read back */
|
||||
git_reference *ref;
|
||||
git_oid oid;
|
||||
git_signature *committer;
|
||||
|
||||
/* Create a new branch pointing at the HEAD */
|
||||
git_oid_fromstr(&oid, current_master_tip);
|
||||
cl_git_pass(git_reference_create(&ref, g_repo, new_ref, &oid, 0));
|
||||
git_reference_free(ref);
|
||||
|
||||
cl_git_pass(git_signature_now(&committer, "foo", "foo@bar"));
|
||||
|
||||
cl_git_fail(git_reflog_append_to(g_repo, new_ref, &oid, committer, "no inner\nnewline"));
|
||||
cl_git_pass(git_reflog_append_to(g_repo, new_ref, &oid, committer, NULL));
|
||||
cl_git_pass(git_reflog_append_to(g_repo, new_ref, &oid, committer, commit_msg "\n"));
|
||||
|
||||
assert_appends(committer, &oid);
|
||||
|
||||
git_signature_free(committer);
|
||||
}
|
||||
|
||||
void test_refs_reflog_reflog__renaming_the_reference_moves_the_reflog(void)
|
||||
{
|
||||
git_reference *master, *new_master;
|
||||
@ -147,13 +127,7 @@ void test_refs_reflog_reflog__renaming_the_reference_moves_the_reflog(void)
|
||||
|
||||
static void assert_has_reflog(bool expected_result, const char *name)
|
||||
{
|
||||
git_reference *ref;
|
||||
|
||||
cl_git_pass(git_reference_lookup(&ref, g_repo, name));
|
||||
|
||||
cl_assert_equal_i(expected_result, git_reference_has_log(ref));
|
||||
|
||||
git_reference_free(ref);
|
||||
cl_assert_equal_i(expected_result, git_reference_has_log(g_repo, name));
|
||||
}
|
||||
|
||||
void test_refs_reflog_reflog__reference_has_reflog(void)
|
||||
@ -207,3 +181,36 @@ void test_refs_reflog_reflog__renaming_with_an_invalid_name_returns_EINVALIDSPEC
|
||||
cl_assert_equal_i(GIT_EINVALIDSPEC,
|
||||
git_reflog_rename(g_repo, "refs/heads/master", "refs/heads/Inv@{id"));
|
||||
}
|
||||
|
||||
void test_refs_reflog_reflog__write_only_std_locations(void)
|
||||
{
|
||||
git_reference *ref;
|
||||
git_oid id;
|
||||
|
||||
git_oid_fromstr(&id, current_master_tip);
|
||||
|
||||
cl_git_pass(git_reference_create(&ref, g_repo, "refs/heads/foo", &id, 1));
|
||||
git_reference_free(ref);
|
||||
cl_git_pass(git_reference_create(&ref, g_repo, "refs/tags/foo", &id, 1));
|
||||
git_reference_free(ref);
|
||||
cl_git_pass(git_reference_create(&ref, g_repo, "refs/notes/foo", &id, 1));
|
||||
git_reference_free(ref);
|
||||
|
||||
assert_has_reflog(true, "refs/heads/foo");
|
||||
assert_has_reflog(false, "refs/tags/foo");
|
||||
assert_has_reflog(true, "refs/notes/foo");
|
||||
|
||||
}
|
||||
|
||||
void test_refs_reflog_reflog__write_when_explicitly_active(void)
|
||||
{
|
||||
git_reference *ref;
|
||||
git_oid id;
|
||||
|
||||
git_oid_fromstr(&id, current_master_tip);
|
||||
git_reference_ensure_log(g_repo, "refs/tags/foo");
|
||||
|
||||
cl_git_pass(git_reference_create(&ref, g_repo, "refs/tags/foo", &id, 1));
|
||||
git_reference_free(ref);
|
||||
assert_has_reflog(true, "refs/tags/foo");
|
||||
}
|
||||
|
55
tests/refs/settargetwithlog.c
Normal file
55
tests/refs/settargetwithlog.c
Normal file
@ -0,0 +1,55 @@
|
||||
#include "clar_libgit2.h"
|
||||
|
||||
#include "repository.h"
|
||||
#include "git2/reflog.h"
|
||||
#include "reflog.h"
|
||||
#include "ref_helpers.h"
|
||||
|
||||
static const char *br2_tip = "a4a7dce85cf63874e984719f4fdd239f5145052f";
|
||||
static const char *master_tip = "a65fedf39aefe402d3bb6e24df4d4f5fe4547750";
|
||||
static const char *br2_name = "refs/heads/br2";
|
||||
|
||||
static git_repository *g_repo;
|
||||
|
||||
void test_refs_settargetwithlog__initialize(void)
|
||||
{
|
||||
g_repo = cl_git_sandbox_init("testrepo.git");
|
||||
}
|
||||
|
||||
void test_refs_settargetwithlog__cleanup(void)
|
||||
{
|
||||
cl_git_sandbox_cleanup();
|
||||
}
|
||||
|
||||
void test_refs_settargetwithlog__updating_a_direct_reference_adds_a_reflog_entry(void)
|
||||
{
|
||||
git_reference *reference, *reference_out;
|
||||
git_oid current_id, target_id;
|
||||
git_signature *signature;
|
||||
git_reflog *reflog;
|
||||
const git_reflog_entry *entry;
|
||||
|
||||
const char *message = "You've been logged, mate!";
|
||||
|
||||
git_oid_fromstr(¤t_id, br2_tip);
|
||||
git_oid_fromstr(&target_id, master_tip);
|
||||
|
||||
cl_git_pass(git_reference_lookup(&reference, g_repo, br2_name));
|
||||
|
||||
cl_git_pass(git_signature_now(&signature, "foo", "foo@bar"));
|
||||
|
||||
cl_git_pass(git_reference_set_target_with_log(
|
||||
&reference_out, reference, &target_id, signature, message));
|
||||
|
||||
cl_git_pass(git_reflog_read(&reflog, g_repo, br2_name));
|
||||
|
||||
entry = git_reflog_entry_byindex(reflog, 0);
|
||||
cl_assert(git_oid_cmp(¤t_id, &entry->oid_old) == 0);
|
||||
cl_assert(git_oid_cmp(&target_id, &entry->oid_cur) == 0);
|
||||
cl_assert_equal_s(message, entry->msg);
|
||||
|
||||
git_reflog_free(reflog);
|
||||
git_reference_free(reference_out);
|
||||
git_reference_free(reference);
|
||||
git_signature_free(signature);
|
||||
}
|
Loading…
Reference in New Issue
Block a user