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?
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With