使用Gitpython将新的本地分支推送到远程

5

我查看了一些参考资料,但仍然遇到问题:

我想使用GitPython克隆远程存储库,创建一个新分支,并将新分支推送回远程。

这似乎可以工作:

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

但这段代码很丑,因为它使用了subprocess而不是GitPython。

我尝试使用GitPython,但没有成功:

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

我参考了以下资料:

注意:请勿删除 HTML 标签。
4个回答

3

我正在使用Python 3.7和gitpython==2.1.11。下面是我的推送函数,在其中我首先尝试高级推送,然后根据需要进行低级推送。请注意,我检查每个命令的返回值。我还记录了推送操作,并解释了每个步骤发生的情况。

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)

2
这段代码的关键行 repo.git.push('--set-upstream', remote_name, branch_name) 实际上并没有使用 gitpython API,而是直接调用了 git。难道没有办法使用API来获得更好格式化的返回值吗? - Seanny123
有没有不设置上游的理由?如果设置两次不会改变影响,那么最好这样做并消除try-except。 - Addison Klinke
@Seanny123,GitPython API 在这一点上似乎有些令人困惑。据我所知,诀窍是在 remote.push() 中使用 refspec 参数:https://dev59.com/PLzpa4cB1Zd3GeqPH0lZ#66820830 。这个答案有更多的细节,并链接到一个 GitHub 问题,其中与包作者进行了讨论:https://dev59.com/Din_s4cB2Jgan1znhSmI#61034176 - Tim

1
你需要定义一个远程仓库,然后将代码推送到该仓库。例如:
origin = repo.remote(name='origin')
origin.push()

请参阅处理远程仓库文档,了解更多有关推送/拉取的示例。


0

假设GitPython中失败的一步是推送(就像我遇到的情况一样),只需使用GitPython,我可以这样解决这个问题:

import git
repo = git.Repo('<your repo path>')
repo.git.checkout('HEAD', b=<your branch name>)
# -u fixed it for me
repo.git.push('origin', '-u', branch_name)

0

在 @Fraser 的回答基础上,这里是我成功创建新分支所使用的完整代码:

from pathlib import Path

# initialize repo and remote origin
repo_path = Path("~/git/sandboxes/git-sandbox").expanduser()
repo = git.Repo(repo_path)
origin = repo.remote(name="origin")

# create new head and get it tracked in the origin
repo.head.reference = repo.create_head(branch_name)
repo.head.reference.set_tracking_branch(origin.refs.master).checkout()

# create a file for the purposes of this example
touch[f"{repo_path}/tmp1.txt"] & plumbum.FG

# stage the changed file and commit it
repo.index.add("tmp1.txt")
repo.index.commit("mod tmp1.txt")

# push the staged commits
push_res = origin.push(branch_name)[0]
print(push_res.summary)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接