Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing Pull with Pygit2

I am trying to implement some 'porcelain' commands using pygit2. It seems that I have run into a bit of road block while implementing pull. Specifically the easiest pull case, a fast forward.

Setup:

I have two git repos. One 'remote' and one 'local'. I make one commit on the remote repo and then create the local repo using pygit2's clone_repository(). I make a subsequent commit on the remote and then attempt to run the pull() function outlined below.

My implementation:

def pull(repo, remote_name='origin'):
    for remote in repo.remotes:
        if remote.name == remote_name:
            remote.fetch()
            remote_master_id = repo.lookup_reference('refs/remotes/origin/master').target
            merge_result, _ = repo.merge_analysis(remote_master_id)
            # Up to date, do nothing
            if merge_result & pygit2.GIT_MERGE_ANALYSIS_UP_TO_DATE:
                return
            # We can just fastforward
            elif merge_result & pygit2.GIT_MERGE_ANALYSIS_FASTFORWARD:
                print repo.head.target
                print repo.status()
                master_ref = repo.lookup_reference('refs/heads/master')
                master_ref.set_target(remote_master_id)
                repo.head.set_target(master_ref)
                repo.checkout_head()
                print repo.status()
            elif merge_result & pygit2.GIT_MERGE_ANALYSIS_NORMAL:
                repo.merge(remote_master_id)
                print repo.index.conflicts

                assert repo.index.conflicts is None, 'Conflicts, ahhhh!'
                user = repo.default_signature
                tree = repo.index.write_tree()
                commit = repo.create_commit('HEAD',
                                            user,
                                            user,
                                            'Merge!',
                                            tree,
                                            [repo.head.target, remote_master_id])
                repo.state_cleanup()
            else:
                raise AssertionError('Unknown merge analysis result')

Full Source

After the fast forward bit of my code executes, my index is no longer clean and I have no idea why. Looking at the git-log, it looks successful. My head and the master branch are now pointing at the most recent commit on the remote repo. However, why was remote_repo_test.txt modified in the process of running set_target().

The Outputs:

The pull print statements:

abfe58ce5098e106a14263df725247bc1f4b22d2
{}
{'remote_repo_test.txt': 2} 

Git log:

* commit b1842f03efe959e93ebad197f36d50ee658e71a4
| Author: Michael Boselowitz <xxx>
| Date:   Fri Jan 2 17:21:45 2015 -0500
| 
|     Version 2 of test.txt on remote_repo
| 
| diff --git a/remote_repo_test.txt b/remote_repo_test.txt
| index a1665f0..13f7f3f 100644
| --- a/remote_repo_test.txt
| +++ b/remote_repo_test.txt
| @@ -1,2 +1,4 @@
|  Version 1.
|  
| +Version 2.
| +
|  
* commit abfe58ce5098e106a14263df725247bc1f4b22d2
  Author: Michael Boselowitz <xxx>
  Date:   Fri Jan 2 17:21:45 2015 -0500

      Version 1 of test.txt on remote_repo

  diff --git a/remote_repo_test.txt b/remote_repo_test.txt
  new file mode 100644
  index 0000000..a1665f0
  --- /dev/null
  +++ b/remote_repo_test.txt
  @@ -0,0 +1,2 @@
  +Version 1.
  +

Git status:

On branch master
Your branch is up-to-date with 'origin/master'.

Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    modified:   remote_repo_test.txt

remote_repo_test.txt content:

Version 1.

Related Questions without Answers:

  • pulling and integrating remote changes with pygit2
  • Implementing 'git pull' with libgit2?
  • Performing a “fast-forward” merge with Rugged

Thoughts?

like image 345
Michael Boselowitz Avatar asked Jan 02 '15 22:01

Michael Boselowitz


1 Answers

First Solution (Not Recommended):

You should not do this. It is possible to lose work this way. However, it works as a hacky solution.

master_ref = repo.lookup_reference('refs/heads/master')
master_ref.set_target(remote_master_id)
# Terrible hack to fix set_target() screwing with the index
repo.reset(master_ref.target, pygit2.GIT_RESET_HARD)

Full source

Second Solution:

This one seems to be promising. After much trial and error, I may have found the solution. If you checkout the tree object you want to target before you update the reference it works. The index is clean like what you would expect when running a git pull or git merge

repo.checkout_tree(repo.get(remote_master_id))
master_ref = repo.lookup_reference('refs/heads/master')
master_ref.set_target(remote_master_id)
repo.head.set_target(remote_master_id)

Full source

like image 189
Michael Boselowitz Avatar answered Oct 02 '22 02:10

Michael Boselowitz