How do you merge the master branch into a feature branch with GitPython?
Asked Answered
P

2

8

I'm trying to automate some of my standard workflow and one thing I find myself doing often is to merge changes to a remote master branch into my own local branch and push the result.

So the steps are as follows:

  1. Switch to master
  2. Pull changes from remote
  3. Switch to original feature branch
  4. Merge from master into feature branch
  5. Push feature branch to remote

I've been trying to write a short python script to do this for me with a single call but I'm stuck on step 4. I can't make sense of the docs to work out how to do this either.

Using git.exe itself I would simply do this: git.exe merge master

Is this possible using the GitPython module and if so, how should one do so?

Payroll answered 22/4, 2016 at 16:50 Comment(0)
L
14

Absent a very compelling reason, I would suggest just using the git binary to perform your tasks. However, if you want to do this using GitPython, take a look at the Advanced Repo usage section of the documentation, which includes an example merge operation.

For example, let's say I have a repository with two branches named main and feature. I'm currently on the feature branch, and I want to merge in changes from main.

I start by initializing a Repo object:

>>> import git
>>> repo = git.Repo('.')

Now I need a reference to my current branch; I can do this:

>>> current = repo.active_branch
>>> current
<git.Head "refs/heads/feature">

Or I can get the branch by name:

>>> current = repo.branches['feature']
>>> current
<git.Head "refs/heads/feature">

I also need a reference to the main branch:

>>> main = repo.branches['main']
>>> main
<git.Head "refs/heads/main">

Now I need to find the merge base of these two branches (that is, the point at which they diverge:

>>> base = repo.merge_base(current, main)
>>> base
[<git.Commit "9007141b5daa35c39afda2d6baf670438d7424a7">]

Now we stage a merge operation:

>>> repo.index.merge_tree(main, base=base)
<git.index.base.IndexFile object at 0x7fa8bb6a9f70>

And commit it, providing links to the two parent commits:

>>> repo.index.commit('Merge main into feature',
... parent_commits=(current.commit, main.commit))
<git.Commit "fb7051d7be36b7998a8f6c480120f84f3f9d9f73">
>>> 

At this point, we have successfully merged the two branches but we have not modified the working directory. If we return to the shell prompt, git status file show that file1 has been modified (because it no longer matches what is in the repository):

$ git status
On branch feature
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

  modified:   file1

We need to perform a checkout of the new commit:

>>> current.checkout(force=True)
<git.Head "refs/heads/feature">

And now:

$ git status
On branch feature
nothing to commit, working directory clean

The above process is fragile; if there are merge conflicts, it's just going to blow up, which is why you are probably better off sticking to the CLI.

Luu answered 22/4, 2016 at 20:35 Comment(4)
Thanks; that's really useful. Even if you use the CLI though, things can go wrong in the merge as you suggest so either way, wouldn't you have to check for and handle merge conflicts? Or is what you've suggested above even more dangerous? :-)Payroll
I don't think it's dangerous, necessarily, but the git cli is heavily tested and is designed to be an end-user facing tool, and so may respond to failures in a more useful fashion.Luu
Okay, got it. I think I can use what you've posted above then resort to resolving the merge with the CLI and finally, resume my script to do the last few commands.Payroll
Now if I can just work out how to simulate the user resolving the issue: stackoverflow.com/questions/36843061 ;-)Payroll
G
2

Merging source_branch to dest_branch:

def merge(source_branch, dest_branch):
    repo = git.Repo(repository_path)
    repo.git.checkout(dest_branch)
    repo.git.merge(source_branch)
    origin = repo.remote(name='origin')
    origin.push()
Gunpaper answered 24/3, 2022 at 11:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.