How to do a quick "pull-merge" in gitpython? - python

EDIT: this was edited because I made a mistake in the example.
Assume I have a very simple git structure:
A single remote branch, called "brem"
A local, called "bloc"
self._repo = git.Repo(args.full_path_to_repo)
Having made a change to a bloc, I can push it to the remote using
self._repo.git.add(update=True)
self._repo.index.commit("commit_message")
origin = self._repo.remote(name=self.origin_name)
origin.push()
I would like to now pull and merge this change into the another local repo.
I do
def _pull(self):
origin = self._repo.remote(name=self.origin_name)
try:
origin.pull()
except git.exc.GitCommandError as ex:
print "There was nothing new to pull"
return
which pulls the remote correctly.
Then I try
origin = self._repo.remote(name=self.origin_name)
self._repo.git.merge(origin)
which gives an exception:
stderr: 'merge: bitbucket_repo - not something we can merge'
What is the correct way of merging pulled changes from a remote branch brem to a local branch bloc, assuming no conflicts?
EDIT2:
I managed to get it working with simply
self._repo.git.execute('git pull')
But this defeats the purpose of using a library.
I would still like to see the correct way of doing this

You'll need to specify a git reference from the remote to merge into the current HEAD. If it's a branch and you know the branch name you would do:
self._repo.git.merge(origin.heads["branch_name"])
See docs on accessing references in gitpython here: https://gitpython.readthedocs.io/en/stable/tutorial.html#advanced-repo-usage

Related

How to use GitPython to perform "git push" when using SSH Keys?

I am trying to write a Python Controller, which would help me automate Git -usage. I've gotten all other commands to work - but I am having difficulties with git push equivalent, when using GitPython Library.
This is where I am right now. This should be working without the SSH Key identification, but I have to squeeze that in.
""" Execute Git Push with GitPython Library.
Hardcoded values: 'branch' environment.
TODO: This is not working. """
def push(self, repo_path, branch, commit_message, user):
repo = Repo(repo_path)
repo.git.add('--all')
repo.git.commit('-m', commit_message)
origin = repo.remote(name=branch)
origin.push()
This is what I have on my Initialization. (Cleared some values due to privacy.)
load_dotenv()
self.BRANCH = "TBD" # Hardcoded Value
self.REPO_PATH = os.getenv('REPO_PATH')
self.REPO = Repo(self.REPO_PATH)
self.COMMIT_MESSAGE = '"Commit from Controller."'
# TODO: These should be changed, when deployed.
self.GIT_SSH_KEY = os.path.expanduser('/home/user/.ssh/id_rsa')
self.GIT_SSH_CMD = "ssh -i %s" % self.GIT_SSH_KEY
self.GIT_USER = "user" # This needs to be changed.
From my understanding from this (GitPython and SSH Keys?) the tactic here is to use GIT_SSH environment variable to provide executable, which will call the ssh - but since I am a beginner, I am having trouble understanding what exactly that environment variable should contain, and how to wrap that with the push function.
Thank you in advance!
First, setting values on self isn't going to accomplish anything by itself, unless there are parts of your code you're not showing us. If you need to set the GIT_SSH environment variable, then you would need to set os.environ['GIT_SSH'].
In general, you shouldn't need to set GIT_SSH unless you require a non-default ssh commandline. That is, if I have:
$ git remote -v
origin ssh://git#github.com/larsks/gnu-hello (fetch)
origin ssh://git#github.com/larsks/gnu-hello (push)
Then I can write:
>>> import git
>>> repo = git.Repo('.')
>>> origin = repo.remote('origin')
>>> res = origin.push()
>>> res[0].summary
'[up to date]\n'
I didn't have to set anything special here; the defaults were entirely appropriate. Under the hood, GitPython just calls the git command line, so anything that works with the cli should work fine without special configuration.

How to check out a branch with GitPython

I have cloned a repository with GitPython, now I would like to checkout a branch and update the local repository's working tree with the contents of that branch. Ideally, I'd also be able to check if the branch exists before doing this. This is what I have so far:
import git
repo_clone_url = "git#github.com:mygithubuser/myrepo.git"
local_repo = "mytestproject"
test_branch = "test-branch"
repo = git.Repo.clone_from(repo_clone_url, local_repo)
# Check out branch test_branch somehow
# write to file in working directory
repo.index.add(["test.txt"])
commit = repo.index.commit("Commit test")
I am not sure what to put in the place of the comments above. The documentation seems to give an example of how to detach the HEAD, but not how to checkout an named branch.
If the branch exists:
repo.git.checkout('branchename')
If not:
repo.git.checkout('-b', 'branchename')
Basically, with GitPython, if you know how to do it within command line, but not within the API, just use repo.git.action("your command without leading 'git' and 'action'"), example: git log --reverse => repo.git.log('--reverse')

Replicating "git checkout <commit> with PyGit2

I am trying to replicate the behaviour of the command "git checkout (commit)" where (commit) is the reference to as specific commit and not a branch name.
When using this command, the 'HEAD' of the repository point to the commit (detached head) and the working directory is in the same state that it was in this commit.
For the moment, I managed to make the HEAD of the repository point to a commit with PyGit2 :
def go(self, repo_name, version):
repo = pygit2.Repository(bdd[repo_name])
#commit = repo.revparse_single(version)
#repo.reset(version, pygit2.GIT_RESET_HARD)
repo.set_head(pygit2.Oid(hex=version))
print repo.head_is_detached
My problem is that I can't find how to rollback the working directory like the Git CLI does.
I tried using:
repo.checkout_head() : It doesn't do anything on the working directory.
repo.checkout() : Crash with a GitError: X conflicts prevent checkout
Is there a way to replicate this behaviour without using Repository.reset(ref, pygit2.GIT_RESET_HARD) ?
Low-level checkout is git read-tree -um HEAD $target && git update-ref HEAD $target; pygit2 apparently understands only the one-tree read and none of the options on that, so however it's doing checkout and merge and any number of other operations it doesn't offer more than a crude mock-up of actual git. It looks like you could kludge it with add a ref to your commit, check that out, then reset HEAD and delete the ref.
repo.checkout_tree can directly use a commit. After it's done, you still need the set_head. Also, pass the following strategy to checkout_tree if you want 'git reset' equivalent: pygit2.GIT_CHECKOUT_FORCE | pygit2.GIT_CHECKOUT_RECREATE_MISSING

Cloning only the main branch using PyGit2

I want to clone some remote repositories, but only retrieving the main branch.
My code currently gets all of the branches.
def init_remote(repo, name, url):
# Create the remote with a mirroring url
remote = repo.remotes.create(name, url, "+refs/*:refs/*")
# And set the configuration option to true for the push command
mirror_var = "remote.{}.mirror".format(name)
repo.config[mirror_var] = True
# Return the remote, which pygit2 will use to perform the clone
return remote
pygit2.clone_repository(url, "../../clones/"+location, remote=init_remote)
Your code doesn't just get all the branches, it mirrors the remote, getting its remote-tracking branches as well, which can lead to some confusing layout.
You're already setting your own refspec, so what you need to do is set the refspec to download the default branch. If you know it you can change the code to get just the one branch
remote = repo.remotes.create(name, url, "+refs/heads/master:refs/heads/master")

how to pull, push with remote branch

I'm trying to automate a change process which currently creates source code that gets manually pushed to Git. I'm trying to wrap that code using GitPython:
from git import *
# create the local repo
repo = Repo.init("/tmp/test/repo", bare=True)
assert repo.bare == True
# check status of active branch
repo.is_dirty()
# clone the remote repo
remote_repo = repo.clone("http://user:pass#git/repo.git")
# compile source code into repository
# ...
# track untracked files
repo.untracked_files
# commit changes locally
repo.commit("commit changes")
# push changes to master
remote_repo.push()
When I try running this, I get
Traceback (most recent call last):
File "git_test2.py", line 33, in
repo.commit("commit changes")
BadObject: 636f6d6d6974206368616e676573
The script is able to pull the remote repository, but fails on commit. Is there a better approach to this?
Some of the functions you are using may not work the way you expect them to. Generally, Repo methods are not the equivalent of the git sub-command with the same name.
Repo.commit does not create a commit but retrieve an existing commit. Since there is not commit named “commit changes” in the repository, an exception is raised.
Repo.clone creates a clone of this repository in a directory called http: inside the repository's directory structure which is most probably not what you want.
If you are trying to clone a remote repository, this can be achieved in a single line:
repo = Repo.clone_from("http://user:pass#git/repo.git", "/tmp/test/repo")
See the API Reference for more information on how to use GitPython.
You can't commit against a bare repository. You can only push/pull to them. By parallel think about how you would do this locally. Try cloning a bare repo and doing the actions, they won't work.
I'm not intimately familiar with the pythonic git bindings, but would imagine that you would need to clone a working repository, optionally checkout a given branch instead of master, do your work, call git add against just that stuff, and then commit.
Also, repo.untracked_files is a no op that simply lists them, it doesn't add them.
Honestly it looks like you blindly copy pasted from https://pythonhosted.org/GitPython/0.3.1/tutorial.html without actually reading what it had to say.
you'll need to manipulate the Index Object for example
index = repo.index
for ( path, stage ), entry in index.entries.iteritems: pass
index.add(['SOMEFILE'])
new_commit = index.commit("YOUR COMMIT MESSAGE")
#do somethign with new commit
Another example I found
import git
repo = git.Repo( '/home/me/repodir' )
print repo.git.status()
# checkout and track a remote branch
print repo.git.checkout( 'origin/somebranch', b='somebranch' )
# add a file
print repo.git.add( 'somefile' )
# commit
print repo.git.commit( m='my commit message' )
# now we are one commit ahead
print repo.git.status()
# now push

Categories

Resources