Implementing Pull with Pygit2

2019-07-07 02:58发布

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:

Thoughts?

1条回答
Rolldiameter
2楼-- · 2019-07-07 03:38

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

查看更多
登录 后发表回答