6

I've looked at the answer in this post (libgit2 (fetch & merge & commit)) but I am struggling to get a Git Pull to work. I get no error messages. The Fetch appears to work but the Merge doesn't occur. Doing a Git Status shows that my branch is behind by 1 commit...

On branch Branch_1_1.
Your branch is behind 'origin/Branch_1_1' by 1 commit, and can be fast-forwarded.
  (use "git pull" to update your local branch)
nothing to commit, working directory clean

My code is below...

static int fetchhead_ref_cb(const char *name, const char *url,
   const git_oid *oid, unsigned int is_merge, void *payload)

{
   if ( is_merge )
   {
      strcpy_s( branchToMerge, 100, name );
      memcpy( &branchOidToMerge, oid, sizeof( git_oid ) );
   }
   return 0;
}

void GitPull()
{
   git_libgit2_init();

   git_repository *repo = NULL;
   git_remote *remote;

   int error = git_repository_open( &repo, "C:\\work\\GitTestRepos\\Repo1" );
   CHECK_FOR_ERROR

   error = git_remote_lookup( &remote, repo, "origin" );
   CHECK_FOR_ERROR

   git_fetch_options options = GIT_FETCH_OPTIONS_INIT;
   error = git_remote_fetch( remote, NULL, &options, NULL );
   CHECK_FOR_ERROR

   git_repository_fetchhead_foreach( repo, fetchhead_ref_cb, NULL );

   git_merge_options merge_options = GIT_MERGE_OPTIONS_INIT;
   git_checkout_options checkout_options = GIT_CHECKOUT_OPTIONS_INIT;
   git_annotated_commit *heads[ 1 ];
   git_reference *ref;

   error = git_annotated_commit_lookup( &heads[ 0 ], repo, &branchOidToMerge );
   CHECK_FOR_ERROR
   error = git_merge( repo, (const git_annotated_commit **)heads, 1, &merge_options, &checkout_options );
   CHECK_FOR_ERROR

   git_annotated_commit_free( heads[ 0 ] );
   git_repository_state_cleanup( repo );
   git_libgit2_shutdown();
}

What am I doing that's causing the merge not to work ?

Community
  • 1
  • 1
Anthony
  • 101
  • 1
  • 6

1 Answers1

4

First, git_checkout_options.checkout_strategy has a rather surprising default. It defaults to dry run. So you probably want to change it to something like GIT_CHECKOUT_SAFE.

The second thing to understand is that git_merge doesn't actually commit the merge. It only sets up your working directory and index for a merge. You still have to check for conflicts and call git_commit_create to finish the merge.

However, it looks like you don't really need to merge in this specific case. Your branch can be fast-forwarded. Call git_merge_analysis to determine which kind of merge to do. To fast-forward call git_reference_set_target on the current branch with the new fetch head.

Jason Haslam
  • 2,617
  • 13
  • 19
  • Thanks Jason. I've tried your suggestion to do a commit. I specify both the local and remote branches as parents but it leaves the local repo one commit ahead of the remote which doesn't happen with a "git pull". – Anthony Sep 26 '16 at 22:07
  • The local branch *should* be one commit ahead after committing a merge. However, I failed to recognize that you don't really need to merge because you your local history hasn't diverged from the remote history. You can fast-forward. I'll update my answer accordingly. – Jason Haslam Sep 27 '16 at 18:04
  • If you just do `git_reference_set_target()` with the new fetch head, it won't update the working tree in the same way that `git pull` does though. – Timmmm Jul 17 '18 at 13:18
  • Yes, you also have to checkout with something like `git_checkout_head` after setting the reference target. – Jason Haslam Jul 18 '18 at 16:58
  • I experienced this strange behavior of `git_checkout_options.checkout_strategy` and opened this bug report: https://github.com/libgit2/libgit2/issues/5803 It seems that the default is suitable for some non-dry-run operations, such as `git_apply`. – Aaron Franke Apr 17 '21 at 08:01