Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Push new local branch to remote using Gitpython

Tags:

I looked at a few references but I am still having problems:

I want to clone a remote repo, create a new branch, and push the new branch back to remote using GitPython.

This seems to work:

import git
import subprocess

nm_brnch = 'new_branch'

# Clone    
repo_url = r'my_remote.git'
repo = git.Repo.clone_from(repo_url, dnm_wrk, branch=r'some_branch')

# Create new branch
git = repo.git
git.checkout('HEAD', b=nm_brnch)

# Push new branch to remote
subprocess.call(f'git push -u origin {nm_brnch}')

But it's ugly, since it uses subprocess, instead of using GitPython.

I tried using GitPython, but without success:

repo.head.set_reference(nm_brnch)
repo.git.push("origin", nm_brnch)

I have consulted the following references:

  • Pushing local branch to remote branch

  • Use GitPython to Checkout a new branch and push to remote

  • Related GitHub issue/question

  • Tutorial from official docs

like image 819
deckard Avatar asked Nov 23 '18 13:11

deckard


People also ask

How do I push a new branch to a remote?

Push a new Git branch to a remote repoClone the remote Git repo locally. Create a new branch with the branch, switch or checkout commands. Perform a git push with the –set-upstream option to set the remote repo for the new branch. Continue to perform Git commits locally on the new branch.

How do I push changes from local repo to remote?

To push the commit from the local repo to your remote repositories, run git push -u remote-name branch-name where remote-name is the nickname the local repo uses for the remote repositories and branch-name is the name of the branch to push to the repository. You only have to use the -u option the first time you push.

How do I push locally to remote GitHub?

At the top of your repository on GitHub.com's Quick Setup page, click to copy the remote repository URL. In the Command prompt, add the URL for the remote repository where your local repository will be pushed. Push the changes in your local repository to GitHub.com.


1 Answers

I'm using gitpython==2.1.11 with Python 3.7. Below is my push function in which I first try a high-level push, and then a low-level push as necessary. Note how I check the return value of either command. I also log the push actions, and this explains what's happening at every step.

class GitCommandError(Exception):
    pass

class Git:
    def _commit_and_push_repo(self) -> None:
        repo = self._repo
        remote = repo.remote()
        remote_name = remote.name
        branch_name = repo.active_branch.name

        # Note: repo.index.entries was observed to also include unpushed files in addition to uncommitted files.
        log.debug('Committing repository index in active branch "%s".', branch_name)
        self._repo.index.commit('')
        log.info('Committed repository index in active branch "%s".', branch_name)

        def _is_pushed(push_info: git.remote.PushInfo) -> bool:
            valid_flags = {push_info.FAST_FORWARD, push_info.NEW_HEAD}  # UP_TO_DATE flag is intentionally skipped.
            return push_info.flags in valid_flags  # This check can require the use of & instead.

        push_desc = f'active branch "{branch_name}" to repository remote "{remote_name}"'
        log.debug('Pushing %s.', push_desc)
        try:
            push_info = remote.push()[0]
        except git.exc.GitCommandError:  # Could be due to no upstream branch.
            log.warning('Failed to push %s. This could be due to no matching upstream branch.', push_desc)
            log.info('Reattempting to push %s using a lower-level command which also sets upstream branch.', push_desc)
            push_output = repo.git.push('--set-upstream', remote_name, branch_name)
            log.info('Push output was: %s', push_output)
            expected_msg = f"Branch '{branch_name}' set up to track remote branch '{branch_name}' from '{remote_name}'."
            if push_output != expected_msg:
                raise RepoPushError(f'Failed to push {push_desc}.')
        else:
            is_pushed = _is_pushed(push_info)
            logger = log.debug if is_pushed else log.warning
            logger('Push flags were %s and message was "%s".', push_info.flags, push_info.summary.strip())
            if not is_pushed:
                log.warning('Failed first attempt at pushing %s. A pull will be performed.', push_desc)
                self._pull_repo()
                log.info('Reattempting to push %s.', push_desc)
                push_info = remote.push()[0]
                is_pushed = _is_pushed(push_info)
                logger = log.debug if is_pushed else log.error
                logger('Push flags were %s and message was "%s".', push_info.flags, push_info.summary.strip())
                if not is_pushed:
                    raise RepoPushError(f'Failed to push {push_desc} despite a pull.')
        log.info('Pushed %s.', push_desc)
like image 155
Asclepius Avatar answered Oct 17 '22 21:10

Asclepius