GitPython: Pull/Checkout from remote, discard local changes - python

For deploying files to some target (Windows) computers, I wanted to create a Python module I can feed with the necessary parameters.
The module should then check if the specified repo exists in the outputpath.
a) If it doesn't exist: clone the latest commit from remote
b) If it exists: discard all local changes, pull the latest commit from the remote
A way (that at least this worked for me) would be to delete the local target folder, recreate it and clone everything again.
My code, that only works for an empty dir:
stderr: 'fatal: remote origin already exists.'
import git, os, shutil
#outputfolder there?
if not os.path.exists(MY_outputfolder):
os.makedirs(MY_outputfolder)
repowrk = git.Repo.init(MY_outputfolder)
wrkr = repowrk.create_remote('origin',MY_REMOTE_URL)
wrkr.fetch()
wrkr.pull(wrkr.refs[0].remote_head)
print("---- DONE ----")

If the repo exists and you want to discard all local changes, and pull latest commit from remote, you can use the following commands:
# discard any current changes
repo.git.reset('--hard')
# if you need to reset to a specific branch:
repo.git.reset('--hard','origin/master')
# pull in the changes from from the remote
repo.remotes.origin.pull()
With these commands you don't have to delete the repo and clone again.
You can check the doc here for more information.

This is the code, that solved my problem.
a.) Output directory contains a .git folder: Assume, this is a local repo. Revert all local changes, purge unversioned files
b.) Output directory does not contain a .git folder (or filetree does not exist): Assume the target directory is dirty or not a local repository. Delete target tree and clone the remote directory to the specified target.
outdir_checker = outdir+'\.git'
if os.path.exists(outdir_checker):
repo_worker = git.Repo.init(outdir)
repo_worker.git.fetch(remote_url)
repo_worker.git.reset('--hard')
repo_worker.git.clean('-fdx')
print('git dir not created; already existed')
if not os.path.exists(outdir_checker):
shutil.rmtree(outdir, ignore_errors=True)
os.makedirs(outdir)
git.Repo.clone_from(remote_url, outdir)
print('git dir created')

For those who would prefer to use gitpython proper, rather than the codified version of the command line interface:
# Create a new branch
new_branch = repo.create_head("new_branch")
# Point your head to the new branch. Note that no working tree changes have taken place yet
repo.head.reference=new_branch
# Perform a reset. Note that index and working_tree must be set to True
# to ensure that the staging area and working tree are overwritten
repo.head.reset(index=True, working_tree=True)

Related

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

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

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')

How do I get the path from $AZ_BATCH_NODE_SHARED_DIR?

I am using Azure Batch with Python and I would like to create a directory within the shared space from a batch task.
According to the docs:
Shared: This directory provides read/write access to all tasks that run on a node. Any task that runs on the node can create, read, update, and delete files in this directory. Tasks can access this directory by referencing the AZ_BATCH_NODE_SHARED_DIR environment variable.
Imagine that folder is called test_dir:
if not os.path.exists('test_dir'):
os.makedirs('test_dir')
Now, what if I want to write a file to that directory? I cannot use:
with open('$AZ_BATCH_NODE_SHARED_DIR/test_dir/test.txt', 'a') as output:
output.write('hello\n')
How do I get the full path from $AZ_BATCH_NODE_SHARED_DIR?
Use os.environ, which exposes the current environment as a mapping:
shared = os.environ['AZ_BATCH_NODE_SHARED_DIR']
with open(os.path.join(shared, 'test_dir', 'test.txt'), 'a') as output:

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