Commit ceab4e26 by Ben Straub

Port blame from git.git

parent 54993167
...@@ -36,6 +36,8 @@ typedef enum { ...@@ -36,6 +36,8 @@ typedef enum {
/** Track lines that have been copied from another file that exists in *any* /** Track lines that have been copied from another file that exists in *any*
* commit (like `git blame -CCC`) */ * commit (like `git blame -CCC`) */
GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES = (1<<1 | 1<<2 | 1<<3), GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES = (1<<1 | 1<<2 | 1<<3),
/** Track through file renames */
GIT_BLAME_TRACK_FILE_RENAMES = (1<<4),
} git_blame_flag_t; } git_blame_flag_t;
/** /**
...@@ -51,11 +53,10 @@ typedef enum { ...@@ -51,11 +53,10 @@ typedef enum {
* associate those lines with the parent commit. The default value is 20. * associate those lines with the parent commit. The default value is 20.
* This value only takes effect if any of the `GIT_BLAME_TRACK_COPIES_*` * This value only takes effect if any of the `GIT_BLAME_TRACK_COPIES_*`
* flags are specified. * flags are specified.
* - `newest_commit` is a rev-parse spec that resolves to the most recent * - `newest_commit` is the id of the newest commit to consider. The default
* commit to consider. The default is HEAD. * is HEAD.
* - `newest_commit` is the newest commit to consider. The default is HEAD. * - `oldest_commit` is the id of the oldest commit to consider. The default
* - `oldest_commit` is the oldest commit to consider. The default is the * is the first commit encountered with a NULL parent.
* first commit encountered with a NULL parent.
* - `min_line` is the first line in the file to blame. The default is 1 (line * - `min_line` is the first line in the file to blame. The default is 1 (line
* numbers start with 1). * numbers start with 1).
* - `max_line` is the last line in the file to blame. The default is the last * - `max_line` is the last line in the file to blame. The default is the last
...@@ -67,8 +68,8 @@ typedef struct git_blame_options { ...@@ -67,8 +68,8 @@ typedef struct git_blame_options {
uint32_t flags; uint32_t flags;
uint16_t min_match_characters; uint16_t min_match_characters;
git_commit *newest_commit; git_oid newest_commit;
git_commit *oldest_commit; git_oid oldest_commit;
uint32_t min_line; uint32_t min_line;
uint32_t max_line; uint32_t max_line;
} git_blame_options; } git_blame_options;
...@@ -105,39 +106,40 @@ typedef struct git_blame_hunk { ...@@ -105,39 +106,40 @@ typedef struct git_blame_hunk {
} git_blame_hunk; } git_blame_hunk;
typedef struct git_blame_results git_blame_results; /* Opaque structure to hold blame results */
typedef struct git_blame git_blame;
/** /**
* Gets the number of hunks that exist in the results structure. * Gets the number of hunks that exist in the blame structure.
*/ */
GIT_EXTERN(uint32_t) git_blame_results_hunk_count(git_blame_results *results); GIT_EXTERN(uint32_t) git_blame_get_hunk_count(git_blame *blame);
/** /**
* Gets the blame hunk at the given index. * Gets the blame hunk at the given index.
* *
* @param results the results structure to query * @param blame the blame structure to query
* @param index index of the hunk to retrieve * @param index index of the hunk to retrieve
* @return the hunk at the given index, or NULL on error * @return the hunk at the given index, or NULL on error
*/ */
GIT_EXTERN(const git_blame_hunk*) git_blame_results_hunk_byindex( GIT_EXTERN(const git_blame_hunk*) git_blame_get_hunk_byindex(
git_blame_results *results, git_blame *blame,
uint32_t index); uint32_t index);
/** /**
* Gets the hunk that relates to the given line number in the newest commit. * Gets the hunk that relates to the given line number in the newest commit.
* *
* @param results the results structure to query * @param blame the blame structure to query
* @param lineno the (1-based) line number to find a hunk for * @param lineno the (1-based) line number to find a hunk for
* @return the hunk that contains the given line, or NULL on error * @return the hunk that contains the given line, or NULL on error
*/ */
GIT_EXTERN(const git_blame_hunk*) git_blame_results_hunk_byline( GIT_EXTERN(const git_blame_hunk*) git_blame_get_hunk_byline(
git_blame_results *results, git_blame *blame,
uint32_t lineno); uint32_t lineno);
/** /**
* Get the blame for a single file. * Get the blame for a single file.
* *
* @param out pointer that will receive the results object * @param out pointer that will receive the blame object
* @param repo repository whose history is to be walked * @param repo repository whose history is to be walked
* @param path path to file to consider * @param path path to file to consider
* @param options options for the blame operation. If NULL, this is treated as * @param options options for the blame operation. If NULL, this is treated as
...@@ -146,32 +148,41 @@ GIT_EXTERN(const git_blame_hunk*) git_blame_results_hunk_byline( ...@@ -146,32 +148,41 @@ GIT_EXTERN(const git_blame_hunk*) git_blame_results_hunk_byline(
* about the error.) * about the error.)
*/ */
GIT_EXTERN(int) git_blame_file( GIT_EXTERN(int) git_blame_file(
git_blame_results **out, git_blame **out,
git_repository *repo, git_repository *repo,
const char *path, const char *path,
git_blame_options *options); git_blame_options *options);
/** /**
* Get blame data for a file that has been modified. * Get blame data for a file that has been modified in memory. The `reference`
* parameter is a pre-calculated blame for the in-odb history of the file. This
* means that once a file blame is completed (which can be expensive), updating
* the buffer blame is very fast.
* *
* @param out pointer that will receive the results object * Lines that differ between the buffer and the committed version are marked as
* @param reference output from git_blame_file for the file in question * having a zero OID for their final_commit_id.
*
* @param out pointer that will receive the resulting blame data
* @param reference cached blame from the history of the file (usually the output
* from git_blame_file)
* @param buffer the (possibly) modified contents of the file * @param buffer the (possibly) modified contents of the file
* @param buffer_len number of valid bytes in the buffer
* @return 0 on success, or an error code. (use giterr_last for information * @return 0 on success, or an error code. (use giterr_last for information
* about the error) * about the error)
*/ */
GIT_EXTERN(int) git_blame_buffer( GIT_EXTERN(int) git_blame_buffer(
git_blame_results **out, git_blame **out,
git_blame_results *reference, git_blame *reference,
const char *buffer); const char *buffer,
size_t buffer_len);
/** /**
* Free memory allocated by git_blame. * Free memory allocated by git_blame_file or git_blame_buffer.
* *
* @param results results structure to free * @param blame the blame structure to free
*/ */
GIT_EXTERN(void) git_blame_free(git_blame_results *results); GIT_EXTERN(void) git_blame_free(git_blame *blame);
/** @} */ /** @} */
GIT_END_DECL GIT_END_DECL
......
...@@ -36,7 +36,7 @@ GIT_BEGIN_DECL ...@@ -36,7 +36,7 @@ GIT_BEGIN_DECL
* @param repo the repository to look up the object * @param repo the repository to look up the object
* @param id the unique identifier for the object * @param id the unique identifier for the object
* @param type the type of the object * @param type the type of the object
* @return 0 or an error code * @return a reference to the object
*/ */
GIT_EXTERN(int) git_object_lookup( GIT_EXTERN(int) git_object_lookup(
git_object **object, git_object **object,
...@@ -78,6 +78,23 @@ GIT_EXTERN(int) git_object_lookup_prefix( ...@@ -78,6 +78,23 @@ GIT_EXTERN(int) git_object_lookup_prefix(
size_t len, size_t len,
git_otype type); git_otype type);
/**
* Lookup an object that represents a tree entry.
*
* @param out buffer that receives a pointer to the object (which must be freed
* by the caller)
* @param treeish root object that can be peeled to a tree
* @param path relative path from the root object to the desired object
* @param type type of object desired
* @return 0 on success, or an error code
*/
GIT_EXTERN(int) git_object_lookup_bypath(
git_object **out,
const git_object *treeish,
const char *path,
git_otype type);
/** /**
* Get the id (SHA1) of a repository object * Get the id (SHA1) of a repository object
* *
......
This diff is collapsed. Click to expand it.
#ifndef INCLUDE_blame_h__
#define INCLUDE_blame_h__
#include "git2/blame.h"
#include "common.h"
#include "vector.h"
#include "diff.h"
#include "array.h"
#include "git2/oid.h"
struct git_blame {
const char *path;
git_repository *repository;
git_blame_options options;
git_vector hunks;
git_vector unclaimed_hunks;
git_vector paths;
git_blob *final_blob;
size_t num_lines;
git_oid current_commit;
git_oid parent_commit;
size_t current_diff_line;
size_t current_blame_line;
git_blame_hunk *current_hunk;
};
git_blame *git_blame__alloc(
git_repository *repo,
git_blame_options opts,
const char *path);
git_blame_hunk *git_blame__alloc_hunk();
#endif
This diff is collapsed. Click to expand it.
#ifndef INCLUDE_blame_git__
#define INCLUDE_blame_git__
#include "git2.h"
#include "xdiff/xinclude.h"
#include "blame.h"
/*
* One blob in a commit that is being suspected
*/
struct origin {
int refcnt;
struct origin *previous;
git_commit *commit;
git_blob *blob;
char path[];
};
/*
* Each group of lines is described by a blame_entry; it can be split
* as we pass blame to the parents. They form a linked list in the
* scoreboard structure, sorted by the target line number.
*/
struct blame_entry {
struct blame_entry *prev;
struct blame_entry *next;
/* the first line of this group in the final image;
* internally all line numbers are 0 based.
*/
int lno;
/* how many lines this group has */
int num_lines;
/* the commit that introduced this group into the final image */
struct origin *suspect;
/* true if the suspect is truly guilty; false while we have not
* checked if the group came from one of its parents.
*/
char guilty;
/* true if the entry has been scanned for copies in the current parent
*/
char scanned;
/* the line number of the first line of this group in the
* suspect's file; internally all line numbers are 0 based.
*/
int s_lno;
/* how significant this entry is -- cached to avoid
* scanning the lines over and over.
*/
unsigned score;
};
/*
* The current state of the blame assignment.
*/
struct scoreboard {
/* the final commit (i.e. where we started digging from) */
git_commit *final;
const char *path;
/*
* The contents in the final image.
* Used by many functions to obtain contents of the nth line,
* indexed with scoreboard.lineno[blame_entry.lno].
*/
const char *final_buf;
git_off_t final_buf_size;
/* linked list of blames */
struct blame_entry *ent;
/* look-up a line in the final buffer */
int num_lines;
git_blame *blame;
};
int get_origin(struct origin **out, struct scoreboard *sb, git_commit *commit, const char *path);
int make_origin(struct origin **out, git_commit *commit, const char *path);
struct origin *origin_incref(struct origin *o);
void origin_decref(struct origin *o);
void assign_blame(struct scoreboard *sb, uint32_t flags);
void coalesce(struct scoreboard *sb);
#endif
...@@ -364,3 +364,39 @@ int git_object_dup(git_object **dest, git_object *source) ...@@ -364,3 +364,39 @@ int git_object_dup(git_object **dest, git_object *source)
*dest = source; *dest = source;
return 0; return 0;
} }
int git_object_lookup_bypath(
git_object **out,
const git_object *treeish,
const char *path,
git_otype type)
{
int error = -1;
git_tree *tree = NULL;
git_tree_entry *entry = NULL;
git_object *tmpobj = NULL;
assert(out && treeish && path);
if (((error = git_object_peel((git_object**)&tree, treeish, GIT_OBJ_TREE)) < 0) ||
((error = git_tree_entry_bypath(&entry, tree, path)) < 0) ||
((error = git_tree_entry_to_object(&tmpobj, git_object_owner(treeish), entry)) < 0))
{
goto cleanup;
}
if (type == GIT_OBJ_ANY || git_object_type(tmpobj) == type) {
*out = tmpobj;
} else {
giterr_set(GITERR_OBJECT,
"object at path '%s' is not of the asked-for type %d",
path, type);
error = GIT_EINVALIDSPEC;
git_object_free(tmpobj);
}
cleanup:
git_tree_entry_free(entry);
git_tree_free(tree);
return error;
}
#include "blame_helpers.h"
void hunk_message(size_t idx, const git_blame_hunk *hunk, const char *fmt, ...)
{
va_list arglist;
printf("Hunk %zd (line %d +%d): ", idx,
hunk->final_start_line_number, hunk->lines_in_hunk-1);
va_start(arglist, fmt);
vprintf(fmt, arglist);
va_end(arglist);
printf("\n");
}
void check_blame_hunk_index(git_repository *repo, git_blame *blame, int idx,
int start_line, int len, const char *commit_id, const char *orig_path)
{
char expected[41] = {0}, actual[41] = {0};
const git_blame_hunk *hunk = git_blame_get_hunk_byindex(blame, idx);
cl_assert(hunk);
if (!strncmp(commit_id, "0000", 4)) {
strcpy(expected, "0000000000000000000000000000000000000000");
} else {
git_object *obj;
cl_git_pass(git_revparse_single(&obj, repo, commit_id));
git_oid_fmt(expected, git_object_id(obj));
git_object_free(obj);
}
if (hunk->final_start_line_number != start_line) {
hunk_message(idx, hunk, "mismatched start line number: expected %d, got %d",
start_line, hunk->final_start_line_number);
}
cl_assert_equal_i(hunk->final_start_line_number, start_line);
if (hunk->lines_in_hunk != len) {
hunk_message(idx, hunk, "mismatched line count: expected %d, got %d",
len, hunk->lines_in_hunk);
}
cl_assert_equal_i(hunk->lines_in_hunk, len);
git_oid_fmt(actual, &hunk->final_commit_id);
if (strcmp(expected, actual)) {
hunk_message(idx, hunk, "has mismatched original id (got %s, expected %s)\n",
actual, expected);
}
cl_assert_equal_s(actual, expected);
if (strcmp(hunk->orig_path, orig_path)) {
hunk_message(idx, hunk, "has mismatched original path (got '%s', expected '%s')\n",
hunk->orig_path, orig_path);
}
cl_assert_equal_s(hunk->orig_path, orig_path);
}
#include "clar_libgit2.h"
#include "blame.h"
void hunk_message(size_t idx, const git_blame_hunk *hunk, const char *fmt, ...);
void check_blame_hunk_index(
git_repository *repo,
git_blame *blame,
int idx,
int start_line,
int len,
const char *commit_id,
const char *orig_path);
#include "blame_helpers.h"
git_repository *g_repo;
git_blame *g_fileblame, *g_bufferblame;
void test_blame_buffer__initialize(void)
{
cl_git_pass(git_repository_open(&g_repo, cl_fixture("blametest.git")));
cl_git_pass(git_blame_file(&g_fileblame, g_repo, "b.txt", NULL));
g_bufferblame = NULL;
}
void test_blame_buffer__cleanup(void)
{
git_blame_free(g_fileblame);
git_blame_free(g_bufferblame);
git_repository_free(g_repo);
}
void test_blame_buffer__added_line(void)
{
const char *buffer = "\
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
\n\
abcdefg\n\
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
\n\
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\n";
cl_git_pass(git_blame_buffer(&g_bufferblame, g_fileblame, buffer, strlen(buffer)));
cl_assert_equal_i(5, git_blame_get_hunk_count(g_bufferblame));
check_blame_hunk_index(g_repo, g_bufferblame, 2, 6, 1, "000000", "b.txt");
}
void test_blame_buffer__deleted_line(void)
{
const char *buffer = "\
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
\n\
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
\n\
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\n";
cl_git_pass(git_blame_buffer(&g_bufferblame, g_fileblame, buffer, strlen(buffer)));
check_blame_hunk_index(g_repo, g_bufferblame, 2, 6, 3, "63d671eb", "b.txt");
check_blame_hunk_index(g_repo, g_bufferblame, 3, 9, 1, "63d671eb", "b.txt");
check_blame_hunk_index(g_repo, g_bufferblame, 4, 10, 5, "aa06ecca", "b.txt");
}
void test_blame_buffer__add_splits_hunk(void)
{
const char *buffer = "\
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
\n\
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
abc\n\
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
\n\
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\n";
cl_git_pass(git_blame_buffer(&g_bufferblame, g_fileblame, buffer, strlen(buffer)));
check_blame_hunk_index(g_repo, g_bufferblame, 2, 6, 2, "63d671eb", "b.txt");
check_blame_hunk_index(g_repo, g_bufferblame, 3, 8, 1, "00000000", "b.txt");
check_blame_hunk_index(g_repo, g_bufferblame, 4, 9, 3, "63d671eb", "b.txt");
}
void test_blame_buffer__delete_crosses_hunk_boundary(void)
{
const char *buffer = "\
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
\n\
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\n";
cl_git_pass(git_blame_buffer(&g_bufferblame, g_fileblame, buffer, strlen(buffer)));
check_blame_hunk_index(g_repo, g_bufferblame, 2, 6, 1, "63d671eb", "b.txt");
check_blame_hunk_index(g_repo, g_bufferblame, 3, 7, 2, "aa06ecca", "b.txt");
}
void test_blame_buffer__replace_line(void)
{
const char *buffer = "\
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
\n\
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
abc\n\
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
\n\
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\n";
cl_git_pass(git_blame_buffer(&g_bufferblame, g_fileblame, buffer, strlen(buffer)));
check_blame_hunk_index(g_repo, g_bufferblame, 2, 6, 1, "63d671eb", "b.txt");
check_blame_hunk_index(g_repo, g_bufferblame, 3, 7, 1, "00000000", "b.txt");
check_blame_hunk_index(g_repo, g_bufferblame, 4, 8, 3, "63d671eb", "b.txt");
}
#include "clar_libgit2.h"
#include "blame.h"
git_blame *g_blame;
void test_blame_getters__initialize(void)
{
size_t i;
git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
git_blame_hunk hunks[] = {
{ 3, {{0}}, 1, {{0}}, "a", 0},
{ 3, {{0}}, 4, {{0}}, "b", 0},
{ 3, {{0}}, 7, {{0}}, "c", 0},
{ 3, {{0}}, 10, {{0}}, "d", 0},
{ 3, {{0}}, 13, {{0}}, "e", 0},
};
g_blame = git_blame__alloc(NULL, opts, "");
for (i=0; i<5; i++) {
git_blame_hunk *h = git_blame__alloc_hunk();
h->final_start_line_number = hunks[i].final_start_line_number;
h->orig_path = git__strdup(hunks[i].orig_path);
h->lines_in_hunk = hunks[i].lines_in_hunk;
git_vector_insert(&g_blame->hunks, h);
}
}
void test_blame_getters__cleanup(void)
{
git_blame_free(g_blame);
}
void test_blame_getters__byindex(void)
{
const git_blame_hunk *h = git_blame_get_hunk_byindex(g_blame, 2);
cl_assert(h);
cl_assert_equal_s(h->orig_path, "c");
h = git_blame_get_hunk_byindex(g_blame, 95);
cl_assert_equal_p(h, NULL);
}
void test_blame_getters__byline(void)
{
const git_blame_hunk *h = git_blame_get_hunk_byline(g_blame, 5);
cl_assert(h);
cl_assert_equal_s(h->orig_path, "b");
h = git_blame_get_hunk_byline(g_blame, 95);
cl_assert_equal_p(h, NULL);
}
#include "clar_libgit2.h"
#include "blame.h"
/**
* The test repo has a history that looks like this:
*
* * (A) bc7c5ac
* |\
* | * (B) aa06ecc
* * | (C) 63d671e
* |/
* * (D) da23739
* * (E) b99f7ac
*
*/
static git_repository *g_repo = NULL;
void test_blame_harder__initialize(void)
{
cl_git_pass(git_repository_open(&g_repo, cl_fixture("blametest.git")));
}
void test_blame_harder__cleanup(void)
{
git_repository_free(g_repo);
g_repo = NULL;
}
void test_blame_harder__m(void)
{
/* TODO */
git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
opts.flags = GIT_BLAME_TRACK_COPIES_SAME_FILE;
}
void test_blame_harder__c(void)
{
git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
/* Attribute the first hunk in b.txt to (E), since it was cut/pasted from
* a.txt in (D).
*/
opts.flags = GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES;
}
void test_blame_harder__cc(void)
{
git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
/* Attribute the second hunk in b.txt to (E), since it was copy/pasted from
* a.txt in (C).
*/
opts.flags = GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES;
}
void test_blame_harder__ccc(void)
{
git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
/* Attribute the third hunk in b.txt to (E). This hunk was deleted from
* a.txt in (D), but reintroduced in (B).
*/
opts.flags = GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES;
}
#include "clar_libgit2.h"
#include "repository.h"
static git_repository *g_repo;
static git_tree *g_root_tree;
static git_commit *g_head_commit;
static git_object *g_expectedobject,
*g_actualobject;
void test_object_lookupbypath__initialize(void)
{
git_reference *head;
git_tree_entry *tree_entry;
cl_git_pass(git_repository_open(&g_repo, cl_fixture("attr/.gitted")));
cl_git_pass(git_repository_head(&head, g_repo));
cl_git_pass(git_reference_peel((git_object**)&g_head_commit, head, GIT_OBJ_COMMIT));
cl_git_pass(git_commit_tree(&g_root_tree, g_head_commit));
cl_git_pass(git_tree_entry_bypath(&tree_entry, g_root_tree, "subdir/subdir_test2.txt"));
cl_git_pass(git_object_lookup(&g_expectedobject, g_repo, git_tree_entry_id(tree_entry),
GIT_OBJ_ANY));
git_tree_entry_free(tree_entry);
git_reference_free(head);
g_actualobject = NULL;
}
void test_object_lookupbypath__cleanup(void)
{
git_object_free(g_actualobject);
git_object_free(g_expectedobject);
git_tree_free(g_root_tree);
git_commit_free(g_head_commit);
g_expectedobject = NULL;
git_repository_free(g_repo);
g_repo = NULL;
}
void test_object_lookupbypath__errors(void)
{
cl_assert_equal_i(GIT_EINVALIDSPEC,
git_object_lookup_bypath(&g_actualobject, (git_object*)g_root_tree,
"subdir/subdir_test2.txt", GIT_OBJ_TREE)); // It's not a tree
cl_assert_equal_i(GIT_ENOTFOUND,
git_object_lookup_bypath(&g_actualobject, (git_object*)g_root_tree,
"file/doesnt/exist", GIT_OBJ_ANY));
}
void test_object_lookupbypath__from_root_tree(void)
{
cl_git_pass(git_object_lookup_bypath(&g_actualobject, (git_object*)g_root_tree,
"subdir/subdir_test2.txt", GIT_OBJ_BLOB));
cl_assert_equal_i(0, git_oid_cmp(git_object_id(g_expectedobject),
git_object_id(g_actualobject)));
}
void test_object_lookupbypath__from_head_commit(void)
{
cl_git_pass(git_object_lookup_bypath(&g_actualobject, (git_object*)g_head_commit,
"subdir/subdir_test2.txt", GIT_OBJ_BLOB));
cl_assert_equal_i(0, git_oid_cmp(git_object_id(g_expectedobject),
git_object_id(g_actualobject)));
}
void test_object_lookupbypath__from_subdir_tree(void)
{
git_tree_entry *entry = NULL;
git_tree *tree = NULL;
cl_git_pass(git_tree_entry_bypath(&entry, g_root_tree, "subdir"));
cl_git_pass(git_tree_lookup(&tree, g_repo, git_tree_entry_id(entry)));
cl_git_pass(git_object_lookup_bypath(&g_actualobject, (git_object*)tree,
"subdir_test2.txt", GIT_OBJ_BLOB));
cl_assert_equal_i(0, git_oid_cmp(git_object_id(g_expectedobject),
git_object_id(g_actualobject)));
git_tree_entry_free(entry);
git_tree_free(tree);
}
[core]
repositoryformatversion = 0
filemode = true
bare = true
ignorecase = true
Unnamed repository; edit this file 'description' to name the repository.
bc7c5ac2bafe828a68e9d1d460343718d6fbe136
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment