4

I am using GitPython to clone a repository from a Gitlab server.

git.Repo.clone_from(gitlab_ssh_URL, local_path)

Later I have another script that tries to update this repo.

try:
    my_repo = git.Repo(local_path)
    my_repo .remotes.origin.pull()

except (git.exc.InvalidGitRepositoryError, git.exc.NoSuchPathError):
    print("Invalid repository: {}".format(local_path)

This working great except if in-between I checkout a tag like this:

tag_id = choose_tag()    # Return the position of an existing tag in my_repo.tags
my_repo .head.reference = my_repo.tags[tag_id]
my_repo .head.reset(index=True, working_tree=True)

In this case I get a GitCommandError when pulling:

git.exc.GitCommandError: 'git pull -v origin' returned with exit code 1

I read the documentation twice already and I don't see where is the problème. Especially since if I try to Pull this repo with a dedicate tool like SourceTree it is working without error or warning. I don't understand how the fact that I checked out a tagged version even with a detached HEAD prevent me from pulling.

  1. What should I do to pull in this case?
  2. What is happening here and what do I miss?

Edit : as advise I tried to look in the exception.stdout and exception.sterr and there is nothing useful here (respectively b'' and None). That's why I have a hard time understanding what's wrong.

2 Answers 2

6

I think it's good idea is to learn more about what is happening first (question 2: what is going on?), and that should guide you to the answer to question 1 (how to fix this?).

To know more about what went wrong you can print out stdout and stderr from the exception. Git normally prints error details to the console, so something should be in stdout or stderr.

try:
    git.Repo.clone_from(gitlab_ssh_URL, local_path)
except git.GitCommandError as exception:
    print(exception)
    if exception.stdout:
        print('!! stdout was:')
        print(exception.stdout)
    if exception.stderr:
        print('!! stderr was:')
        print(exception.stderr)

As a side note, I myself had some issues few times when I did many operations on the git.Repo object before using it to interact with the back-end (i.e. the git itself). In my opinion there sometimes seem to be some data caching issues on the GitPython side and lack of synchronisation between data in the repository (.git directory) and the data structures in the git.Repo object.

EDIT:

Ok, the problem seems to be with pulling on detached head - which is probably not what you want to do anyway.

Still, you can work around your problem. Since from detached head you do git checkout master only in order to do git pull and then you go back to the detached head, you could skip pulling and instead use git fetch <remote> <source>:<destination> like this: git fetch origin master:master. This will fetch remote and merge your local master branch with tracking branch without checking it out, so you can remain in detached head state all along without any problems. See this SO answer for more details on the unconventional use of fetch command: https://stackoverflow.com/a/23941734/4973698

With GitPython, the code could look something like this:

my_repo = git.Repo(local_path)
tag_id = choose_tag() # Return the position of an existing tag in my_repo.tags
my_repo.head.reference = my_repo.tags[tag_id]
my_repo.head.reset(index=True, working_tree=True)
fetch_info = my_repo.remotes.origin.fetch('master:master')
for info in fetch_info:
    print('{} {} {}'.format(info.ref, info.old_commit, info.flags))

and it would print something like this:

master 11249124f123a394132523513 64 

... so flags equal 64. What does it mean? When you do print(git.FetchInfo.FAST_FORWARD) the result is 64 so that means that the fetch was of Fast-Forward type, and that therefore your local branch was successfully merged with remote tracking branch, i.e. you have executed git pull origin master without checking out the master.

Important note: this kind of fetch works only if your branch can be merged with remote branch using fast-forward merge.

1
  • Sadly there is nothing either in stderr or stdout ( respectively b'' and None). The only thing I get is the exception comment: 'git pull -v origin' returned with exit code 1. That's why i'm a little lost here. And i don't do much just create a new git.Repo(path) and try to pull.
    – Rbtnk
    Commented Apr 29, 2016 at 12:35
0

The answer is that trying to pull on a detach head is not a good idea even if some smart client handle this case nicely. So my solution in this case is to checkout the last version on my branch (master), pull then checkout again the desired tagged version. However we can regret the poor error message given by GitPython.

1
  • I updated my answer with another workaround that might be useful. I admit that before I completely missed the pulling on detached head. That, I agree, is not a very good idea. Anyways, don't forget to accept the answer (even if it's your own answer) if it provides the best solution to your problem :)
    – mbdevpl
    Commented May 22, 2016 at 7:18

Not the answer you're looking for? Browse other questions tagged or ask your own question.