libgit2 - need help to understand how git_diff_foreach works - libgit2

I'm using libgit2 0.22.2, odb/refdb backed by mysql. I was testing git_diff_foreach function, but not sure how it works, please advise!
Below is my testing data and code, I put a blob 'README.md' into odb and commit, the content was 'commit' and later I put another blob with same name into odb and commit, this time the content was 'commit test'
I was trying to diff the 2 tree to print diff deltas in 3 callbacks (file, hunk, line). But only the file callback printed something
update:
i debugged into the foreach function, there's error in diff.h git_diff_file__resolve_zero_size function. after return statement error pops up:
Run-Time Check Failure #2 - Stack around the variable 'len' was corrupted.
code:
get diff:
int error;
git_object *commit;
git_commit *parent = NULL;
error = git_revparse_single(&commit, repo, "HEAD^{commit}");
error = git_commit_parent(&parent, (git_commit *)commit, 0);
git_tree *commit_tree = NULL, *parent_tree = NULL;
error = git_commit_tree(&commit_tree, (git_commit *)commit);
error = git_commit_tree(&parent_tree, parent);
git_diff *diff = NULL;
error = git_diff_tree_to_tree(&diff, repo, commit_tree, parent_tree, NULL);
git_mysql_tree_diff(mysql, repo, diff);
git_object_free(commit);
git_commit_free(parent);
git_tree_free(commit_tree);
git_tree_free(parent_tree);
git_diff_free(diff);
iterate diff:
int each_file_cb(const git_diff_delta *delta,float progress,void *payload)
{
printf("new file:%s \n", delta->new_file.path);
printf("old file:%s \n", delta->old_file.path);
return 0;
}
int each_hunk_cb(
const git_diff_delta *delta,
const git_diff_hunk *hunk,
void *payload)
{
printf("hunk header:%s \n", hunk->header);
return 0;
}
int each_line_cb(
const git_diff_delta *delta,
const git_diff_hunk *hunk,
const git_diff_line *line,
void *payload)
{
printf("line content:%s \n", line->content);
return 0;
}
int git_mysql_tree_diff(git_diff *diff){
int error;
error = git_diff_foreach(diff,
each_file_cb,
each_hunk_cb,
each_line_cb,
NULL);
return error;
}
Data:
# oid, type, size, data
'92168310a2a8e8d6577028d97931fd104965f6c5', '2', '37', 100644 README.md �����؊�J��B���f�N
'9f4ee02f459f6a28fa7a129fde6653d971429336', '1', '248', tree 92168310a2a8e8d6577028d97931fd104965f6c5 parent ad6aa3d0fcecefa5e9ea250a069da4d3be1f8e3d author Jerry Jin <jerry.yang.jin#gmail.com> 1430376253 +0800 committer Jerry Jin <jerry.yang.jin#gmail.com> 1430376253 +0800 encoding UTF-8 update readme
'ad6aa3d0fcecefa5e9ea250a069da4d3be1f8e3d', '1', '201', tree af0f65dba4ddecf5f4faf889cbd2852c341958c7 author Jerry Jin <jerry.yang.jin#gmail.com> 1430376223 +0800 committer Jerry Jin <jerry.yang.jin#gmail.com> 1430376223 +0800 encoding UTF-8 Initial Commit
'af0f65dba4ddecf5f4faf889cbd2852c341958c7', '2', '37', 100644 README.md ��vX"��}���lW�`#�#
'f8a612e3f687d88a984acaf41142ba85a3668d4e', '3', '11', commit test
'fcad765822a69c7de41bb1db186c57b160409923', '3', '6', commit
Output:
file progress:0.000000

after fixing memory issue, diff each printed below
new file:README.md
old file:README.md
hunk header:## -1 +1 ##
line content:commit testd
line content:
\ No newline at end of file
line content:commit;
line content:
\ No newline at end of file

Related

libgit2: Resolve merge conflict automatically

I'm writing a program that is using libgit2 with a repo for management of config files across multiple instances. If there is a conflicting merge I would like to automatically resolve this (by picking the latest commits), without user interaction.
After performing a git_merge I get the git_repository_index and check if git_index_has_conflicts.
When I iterate over the conflicts I am able to get the git_index_entry for the contributing parts, but here is where my basic understanding of git falls short. My approach is to resolve what git_commit that relates to the git_index_entry and check the commit time to determine which entry to pick for merged solution. At the moment I'm a bit stuck on this so any pointers on how to go about doing or if there is a better approach to this problem is much appreciated.
I need to use the libgit2 API to solve this.
Here is some simplified sample code to show my approach:
void merge(const git_a_repository* a_repo, const git_signature* a_signature)
{
//Get the FETCH_HEAD annotated commit
git_annotated_commit* annotated = nullptr;
git_object* obj = nullptr;
git_revparse_single(&obj, a_repo, "FETCH_HEAD");
git_annotated_commit_lookup(annotated, a_repo, git_object_id(obj));
git_object_free(obj);
//Omitting git_merge_analysis for simplicity
//and assuming merge condition
//Merge the FETCH_HEAD with HEAD
git_merge_options merge_opts = GIT_MERGE_OPTIONS_INIT;
git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE
| GIT_CHECKOUT_ALLOW_CONFLICTS;
git_merge(a_repo,(const git_annotated_commit**)(&annotated),
1, &merge_opts, &checkout_opts);
git_index* index = nullptr;
git_a_repository_index(&index, a_repo);
//Handle conflicts
if (git_index_has_conflicts(index))
{
const git_index_entry* ancestor = nullptr;
const git_index_entry* our = nullptr;
const git_index_entry* their = nullptr;
git_index_conflict_iterator* conflicts = nullptr;
int err = 0;
std::vector entriesToKeep;
git_index_conflict_iterator_new(&conflicts, index);
while ((err = git_index_conflict_next(&ancestor, &our, &their, conflicts))
== GIT_OK)
{
git_commit* ourcommit = nullptr;
git_commit* theircommit = nullptr;
//TODO: Need to find the commit belonging to git_index_entry
// to look up the comit time and use this to select
// which git_index_entry should be staged
//entriesToKeep.push_back(selectedEntry);
}
for (auto r : entriesToKeep)
{
git_index_add(index, r);
}
git_index_conflict_iterator_free(conflicts);
git_index_conflict_cleanup(index);
}
//Commit merge
git_tree* tree = nullptr;
git_oid tree_oid, commit_oid;
git_index_write_tree(&tree_oid, index);
git_tree_lookup(&tree, a_repo, &tree_oid);
git_reference* head_ref = nullptr;
git_a_repository_head(&head_ref, a_repo);
git_commit** parents = (git_commit**)(calloc(2, sizeof(git_commit*)));
git_reference_peel((git_object**)&parents[0], head_ref, GIT_OBJECT_COMMIT);
git_commit_lookup(&parents[1], a_repo, git_annotated_commit_id(annotated));
git_commit_create(&commit_oid, a_repo, git_reference_name(head_ref),
a_signature, a_signature, NULL, "Merge", tree, 2,
(const git_commit**)parents);
free(parents);
git_tree_free(tree);
git_annotated_commit_free(annotated);
git_a_repository_state_cleanup(a_repo);
}

libgit2: How to mark resolution

I can't figure out how to mark a resolution after resolving merge conflicts with libgit2. calling git status on the repo returns Unmerged paths: and says both added: <'myfilename'>
Edit: Her is example code of how I got it to work for future reference.
git_merge_options merge_opts = GIT_MERGE_OPTIONS_INIT;
git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
git_merge(repo, annotated_commit, 1, &merge_opts, &checkout_opts);
git_index* index = nullptr;
git_repository_index(&index, repo);
if (git_index_has_conflicts(index))
{
git_index_conflict_iterator* conflicts = nullptr;
git_index_entry entries[3];
git_index_conflict_iterator_new(&conflicts, index);
while (git_index_conflict_next(&entries[0], &entries[1], &entries[2], conflicts)
!= GIT_ITEROVER)
{
//Write desired content to file in working copy
handle_conflict(entries[2]->path);
git_index_conflict_remove(index, entries[2]->path);
git_index_add_bypath(index, entries[2]->path);
}
git_index_conflict_iterator_free(conflicts);
git_index_conflict_cleanup(index);
//At this point index does not have more conflicts so write it
git_index_write(index);
}
git_commit* parents[2];
git_reference* head_ref = nullptr;
git_repository_head(&head_ref, repo);
git_reference_peel((git_object**)&parents[0], head_ref, GIT_OBJECT_COMMIT);
git_commit_lookup(&parents[1], repo, git_annotated_commit_id(annotated));
git_tree* tree = nullptr;
git_oid tree_oid, commit_oid;
git_index_write_tree(&tree_oid, index);
git_tree_lookup(&tree, repo, &tree_oid);
git_commit_create(&commit_oid, repo,
git_reference_name(head_ref), signature, signature, NULL, "Merge",
tree, 2, (const git_commit**)parents);
git_tree_free(tree);

libgit2 how to get merge conflict preview in buffer?

I'm want use libgit2 get a preview of a conflict commit in a bare repo, which means there is no workdir.
have using those functions, git_merge_commits git_index_has_conflicts and git_index_conflict_get, to get relative infomation.
Main code like this:
git_index *index;
int error = git_merge_commits(&index, repo, git_commit1, git_commit2, NULL);
int conflict_count = git_index_has_conflicts(index);
git_oid index_tree_oid, index_commit_oid;
git_tree *index_tree;
if(conflict_count == 0) {
// do commit the index
} else {
// solve conflict
const git_index_entry *ancestor = NULL,
*ours = NULL,
*theirs = NULL;
error = git_index_conflict_get(
&ancestor,
&ours,
&theirs,
index,
"file2");
error_check(error);
...
How to get preview of the merge in buffer
contains <<<< HEAD like this?
aaaaaaaa
bbbbbbbb22222222
<<<<<<< HEAD
bbbbbbbb11111111
bbbbbbbb11111111
=======
bbbbbbbb22222222
>>>>>>> b3
aaaaaaaa
aaaaaaaa
aaaaaaaa
Currently, a merge only has one file can conflict.
Thanks

Read 'hidden' input for CLI Dart app

What's the best way to receive 'hidden' input from a command-line Dart application? For example, in Bash, this is accomplished with:
read -s SOME_VAR
Set io.stdin.echoMode to false:
import 'dart:io' as io;
void main() {
io.stdin.echoMode = false;
String input = io.stdin.readLineSync();
// or
var input;
while(input != 32) {
input = io.stdin.readByteSync();
if(input != 10) print(input);
}
// restore echoMode
io.stdin.echoMode = true;
}
This is a slightly extended version, key differences are that it uses a finally block to ensure the mode is reset if an exception is thrown whilst the code is executing.
The code also uses a waitFor call (only available in dart cli apps) to turn this code into a synchronous call. Given this is a cli command there is no need for the complications that futures bring to the table.
The code also does the classic output of '*' as you type.
If you are doing much cli work the below code is from the dart package I'm working on called dcli. Have a look at the 'ask' method.
https://pub.dev/packages/dcli
String readHidden() {
var line = <int>[];
try {
stdin.echoMode = false;
stdin.lineMode = false;
int char;
do {
char = stdin.readByteSync();
if (char != 10) {
stdout.write('*');
// we must wait for flush as only one flush can be outstanding at a time.
waitFor<void>(stdout.flush());
line.add(char);
}
} while (char != 10);
} finally {
stdin.echoMode = true;
stdin.lineMode = true;
}
// output a newline as we have suppressed it.
print('');
return Encoding.getByName('utf-8').decode(line);
}

failed to create commit: current tip is not the first parent error during commit in libgit2

I am using libgit2 v0.23.0 library for git pull & commit operation. I am calling git_merge(repo,their_heads,1,&merge_opt,&checkout_opts); method & it works fine & merge the changes from remote repository to local repository. But after that when I am calling git_commit_create() method, it throw error as failed to create commit: current tip is not the first parent with error code -15.
I investigate & found that FETCH_HEAD and MERGE_HEAD file contains updated oid, but ORIG_HEAD still containing previous/outdated oid. I am not sure this is the cause of error which I am getting during git_commit_create().
int fetch()
{
qDebug()<<"Fetch";
git_remote *remote = NULL;
const git_transfer_progress *stats;
struct dl_data data;
git_fetch_options fetch_opts = GIT_FETCH_OPTIONS_INIT;
git_repository *repo = NULL;
QString repoPath = "repopath/.git";
int error = git_repository_open(&repo, repoPath.toStdString().c_str());
if (git_remote_lookup(&remote, repo, "origin") < 0) {
if (git_remote_create_anonymous(&remote, repo,"repoURL") < 0)
return -1;
}
fetch_opts.callbacks.update_tips = &update_cb;
fetch_opts.callbacks.sideband_progress = &progress_cb;
fetch_opts.callbacks.credentials = cred_acquire_cb;
data.remote = remote;
data.fetch_opts = &fetch_opts;
data.ret = 0;
data.finished = 0;
stats = git_remote_stats(remote);
download(&data);
if (stats->local_objects > 0) {
printf("\rReceived %d/%d objects in % bytes (used %d local objects)\n",
stats->indexed_objects, stats->total_objects, stats->received_bytes, stats->local_objects);
} else{
printf("\rReceived %d/%d objects in %bytes\n",
stats->indexed_objects, stats->total_objects, stats->received_bytes);
}
git_remote_disconnect(remote);
if (git_remote_update_tips(remote, &fetch_opts.callbacks, 1, fetch_opts.download_tags, NULL) < 0)
return -1;
const git_remote_head **head = NULL;
size_t size = 0;
(git_remote_ls(&head, &size, remote));
git_oid oid = head[0]->oid;
char * commit_id1 = new char[41]; //Commit ID
qDebug()<<"oid:"<<git_oid_tostr(commit_id1, 41, &oid);
git_annotated_commit *anno_out ;
git_annotated_commit_lookup(&anno_out,repo,&oid);
git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
const git_annotated_commit **their_heads = const_cast<const git_annotated_commit**>(&anno_out);
git_merge_options merge_opt = GIT_MERGE_OPTIONS_INIT;
merge_opt.file_favor = GIT_MERGE_FILE_FAVOR_UNION;
error = git_merge(repo,their_heads,1,&merge_opt,&checkout_opts);
if(error!=0){
//Error handling
}
else{
qDebug()<<"Merge successfully";
}
git_repository_state_cleanup(repo);
/* Create signature */
git_signature *me = NULL;
(git_signature_now(&me, "username", "username#gmail.com"));
//Tree Lookup
git_tree *tree;
git_object *tree_obj;
(git_revparse_single(&tree_obj, repo, "HEAD^{tree}"));
// Get parent commit
git_oid parentCommitId;
git_commit *parent;
git_oid remoteParentCommitId;
git_commit *remoteParent;
int nparents;
int err;
(git_reference_name_to_id( &parentCommitId, repo, "ORIG_HEAD" ));
(git_commit_lookup( &parent, repo, &parentCommitId ));
(git_reference_name_to_id( &remoteParentCommitId, repo, "MERGE_HEAD" ));
(git_commit_lookup( &remoteParent, repo, &remoteParentCommitId ));
const git_commit *parents [1] = {remoteParent };
git_oid new_commit_id;
err = (git_commit_create(
&new_commit_id,
repo,
"HEAD", /* name of ref to update */
me, /* author */
me, /* committer */
"UTF-8", /* message encoding */
"pull fetch", /* message */
(git_tree *) tree_obj, /* root tree */
1, /* parent count */
parents)); /* parents */
if(err !=0){
//I am getting error here
}
git_remote_free(remote);
return 0;
}
Please suggest me what I have to do in order to resolve this issue ?
Generally, you're seeing this error because you are building a new commit on a branch whose parent is not the current tip of the branch. Indeed, you're building a new commit whose parent is the remote commit not the local one.
There are a few problems:
Some error checking on all the functions is recommended. I see some functions that are likely failing but there is no check for that. For example:
Don't call git_repository_state_cleanup in the middle of your operation. That will abort the merge and cleanup the state files that you're trying to read later. Like MERGE_HEAD.
You're doing a merge. You should have two parent commits (the two commits you're merging) to the new commit. You should pass { parent, remoteParent } as the parents.