Skip to main content

Black Friday 2025! Only until December 1st: coupon FRIDAY25 for 40% off Yearly/Lifetime membership!

Read more here
Premium Members Only
Join to unlock this tutorial and all of our courses.
Tutorial Premium Tutorial

Using Git in Laravel Team: Branches, Pull Requests, Conflicts

April 06, 2023
13 min read

Git is an essential tool for every developer. In this tutorial, I will explain everything you need to know about branches and conflicts while working in a team, with Laravel examples.

In fact, this article is not about Laravel, it's just that example code will be with Laravel framework, but you can apply Git knowledge from here to other coding languages/frameworks, too.

Notice: in this tutorial, we will use Terminal and not use visual tools like Sourcetree/GitHub Desktop or PhpStorm/VSCode editors. Those tools can help but I want you to learn principles and the syntax if you do need to work with the Terminal.


Start with Branches: Main/Master and Develop

Branches are probably the foundation when working with the team. But even when working solo, you may use branches to work on separate features simultaneously, so you may have branches called "feature-payment", "feature-user-update", etc.

Also, you may use them for testing features from branches without deploying them to live. So, your testing server would get the code from "develop" branch, for example, and you can play around there, until you're sure it works, and then merge the code into the "main" branch for deployment.

Every team choose their own branch names and branch logic, but there's a most typical behavior, here's how it goes.

When the repository is created, it is created with the branch called main.

Notice: GitHub changed its default from master to main for new repositories in 2020.

git main branch

Next, let's create a new fresh Laravel project and push its code to GitHub.

laravel new laravel
cd laravel

Now we can push to that main branch:

git init
git add .
git commit -m "Fresh Laravel"
git branch -M main
git remote add origin [email protected]:krekas/Git-Example.git
git push -u origin main

What every command here does?

  • Creates an empty Git repository.
  • Adds all files to the repository.
  • Makes a commit with the message "Fresh Laravel".
  • Sets branch to main.
  • Adds an origin where to push.
  • Pushes code to the main branch.

If you refresh the GitHub repository page, you will see the code was pushed to the main branch.

fresh code in the main branch

Next step: usually the main branch is only for the "finalized" features to deploy to live. And the work is being done in other branches. So, from that main branch, someone creates a develop branch where the work is being done. Here's how you can do it on GitHub directly in the browser.

create develop branch

At this point, the develop code is identical to the main branch.

Then, whenever a developer starts working on the project, they clone the repository, check out the develop branch and starts the work.

The main branch is only for deployment to the live server which happens from time to time, but more rarely than everyday work.

You can even protect the branches on GitHub, to restrict pushing to the main branch, to avoid...

Premium Members Only

This advanced tutorial is available exclusively to Laravel Daily Premium members.

Premium membership includes:

Access to all premium tutorials
Video and Text Courses
Private Discord Channel

Comments & Discussion

P
pappz βœ“ Link copied!

Hey there,

I follow your work from a long time, but honestly I don't accept every point of your article.

What you showed here, is the path from developing a new feature, to merge it into develop. What I don't see is, how you merge features safely to main? Do you merge develop to main from time to time? How can you release features to main separately? With this technique you can't, or there is only a few feature development parallel.

I would change one step (above in the article). Imagine a team with a real client with multiple feature request. The client wants to test feature by feature, so he/she can mark the features, which can go live. And it is really important.

Whenever you create a new feature branch, you create it from the main branch, and not from the develop. The develop branch can have unaccepted features. If you create a new feature branch from here, there is no chance, your newly created feature could be released sooner, if the client approves it. Let's say, there is an ongoing blog feature request, which some part is already in the development, but the client changes his minds from time to time. Also there is a new popup request, if the visitor wants to left site, a popup should appear. If this popup feature comes from the 'unfinished' develop branch, you can't release the popup stuff, until everything is accepted.

As always you say: it depends, of course you are right :)

What I wanted to point out: This approach (features came always from develop branch) can cause headaches and lost of hours, if there are multiple feature developments in parallel & someone create a new feature from develop branch.

It will only show something unexpected, when you want to merge the accepted feature to main and you see more changes than it was committed.

But this is my opinion & experience which I wanted to share :)

MN
Mehmet Nurullah Sağlam βœ“ Link copied!

Hey, I got your point but another view is, you can use your main/master branch for versioning and develop branch to put a last step before release.

For example, your app/package is on version 0.1.0 and want to add x, y and z features for the version 0.2.0. I think it's not a proper way to merge these features to main/master branch separately. You can combine them on develop branch and when it's merged to main/master, it will be the next version.

But, as you said, the client may wants test feature by feature or some other case. And again as we all say, it depends :)

PK
Povilas Korop βœ“ Link copied!

Thanks for the very valuable and long comment Pappz. I agree, this article mostly describes the "almost ideal" world where nothing needs to be hot-fixed or deployed urgently.

For urgent deployments like these, every team have different approaches. Personally in our experience, we used to break all the rules and committed straight to "main" for very urgent things, then merging it into develop, after it all calms down. But do you really think that I need to recommend this approach? I kinda deliberately left it our of the article, pointing out only the "best" scenarios because it's best to keep it that way.

I guess there should be a separate article of "Ways to quickly deploy/launch things", will think about it.

P
pappz βœ“ Link copied!

Thanks for the reply! I know situations, teams and clients are all different. Also I think, maybe there is a little misunderstanding so I try to show it with a timeline.

I think this can be a good approach, and this is what I meant, that every new feature branch should be made from main, in my point of view:

Day 1,

  • main and develop branches are equal
  • client request feature A
  • developer007 checkout from main, create a new feature/A branch

Day2,

  • developer007 merge feature/A branch to develop
  • after it client request feature B
  • developerKing checkout from main, create a new feature/B branch

Day3,

  • developerKing merge feature/B branch to develop
  • at this time, main is not touched, but develop has feature/A and feature/B
  • client reply to feature A, it needs some modification, so developer007 will continue working on feature/A branch
  • client reply to feature B, it is ok, release it
  • now developerWhoInChargeToRelease can simply merge feature/B branch back to main
  • now main branch contains only the accepted feature

All-in-all:

What you mentioned above, always create new branch from develop is what I wanted to point out.

On Day2, if developerKing would checkout from develop branch, then his feature/B branch would contain feature/A, which was not accepted on Day3. This means, they couldn't release feature/B on Day3. Only after feature/A acceptance.

I hope it is clearer now.

Moreover, belive me, I made also some ASAP hot fixes, directly to main, so I understand, situations create the rules!

But I have to mention, I like to keep things clear. I always have fix/pappz or hotfixes branch, which is always "my-urgent-asap-fixes" branch. Whenever some hotfixes have to be done, I merge main to my hotfix branch, then after merge it to main. This make things more clear for me. (At least I try to belive in :) ).

PK
Povilas Korop βœ“ Link copied!

Wow what an amazing long comment, thanks for taking the time! And yes, I do agree with the approach you described, as you explained it in more detail.

DL
David Lun βœ“ Link copied!

main branch at origin should reflect production-ready state.

develop branch reflects a state with the latest delivered development changes for the next release, it may be called as "integration branch". In general not completed or not accepted feature shouldn't be merged into develop in the first place, but that's not always the case. Good strategy could be to use --no-ff flag when merging feature into develop.

git checkout develop
git merge --no-ff feature/A

This way it always creates new commit object and preserves historical changes (even if fast-forward is possible) of a feature branch, and allows to easily discard feature from the next release.

Then another dev created feature/B from develop with feature/A merged. Got work done, work was approved.

feature/A gets discarded from develop by this point.

Now feature/B has conflicts and still feature/A present. It is good to resolve that upfront (for example we do not know yet if client approved feature/B or not). So what dev can do is just to run rebase.

git checkout feature/B
git rebase develop

rebase will reapply feature's B commits on top of current develop branch and will allow to resolve conflicts in the feature branch instead of when merging into develop.

This way you can merge feature/B anytime into develop without any consequences, and ever having to deal with main to apply hotfixes.

git checkout develop
git merge --no-ff feature/B

When the code in the develop reaches a stable point, changes are merged back into main and this is a new production release by definition.

K
kaleemullah βœ“ Link copied!

Resolving Conflict Section *I think Re consider third paragraph of this section *

we merge the develop branch into the feature/user-country branch by doing git merge develop. And of course, we get the expected conflict message:

I think, It Should be we merge the feature/user-country branch into the develop branch by doing git merge develop. And of course, we get the expected conflict message:

@Povilas

PK
Povilas Korop βœ“ Link copied!

I think that one is actually correct. To resolve the conflict, we do need to merge the developer into our feature branch, then resolve the conflict in our feature branch, and push the feature branch.

And only then merge into develop.

K
kaleemullah βœ“ Link copied!

BTW, This is The great article. Love it.

A
amaurycid βœ“ Link copied!

Very well layed out Povilas! I think this is the basic way to follow, then depending on different scenarios, teams might proceed as required.

K
kornelkornecki βœ“ Link copied!

I have a following problem: I have 4 laravel apps wich are very similar but not identical and mostly the difference is the design (they are made for different clients). Now I have to add some fixes and some new functionality to all of them. All the fixes and the new functionality are the same for all the apps. Is there a way to make some kind of patch to apply so I will not have to copy-paste all of this fixes and new stuff to every one of the apps separately?

M
Modestas βœ“ Link copied!

Well, this is tricky.

Is there any way this could be done? Yeah! Private packages would help here. They are quite good at keeping things up to date from a single point.

Is there any other way? Sadly - no. Separate projects/repositories means separate bugfixes. And that has to be done manually.

K
kornelkornecki βœ“ Link copied!

I just found the better (I think) way:

  • change one app as you need (if the app has any irrelevant to the topic changes commit them before starting)
  • make all necessary changes

When finished the changes there are 2 ways

  1. from command line do: git diff > mypatch.patch and then commit
  2. if you use PHP Storm commit the work and right click the commit you've just created and select Create Patch... and name it as you wish eg. mypatch.patch

Now copy the mypatch.patch to other apps you want to patch and from command line do: git apply mypatch.patch

tada! you have it patched.

M
Modestas βœ“ Link copied!

Yes, this is possible too, but to be fair - is really hacky :) I'd still recommend extracting common parts as a package, rather than individual stuff. That way you will have re-usable code pieces

K
kornelkornecki βœ“ Link copied!

You can not extract fixes of some errors and few new methods or changes in existing controllers or views as a package. To me that sounds crazy. And even if it was possibe then it would be really really hacky.

M
Modestas βœ“ Link copied!

Oh no, not fixes! I'm talking about the case, where you just mentioned that patch was pretty much identical between all applications. This indicates, that the code behind is also identical (to some extent!). That means - it should be possible to completely move that code to a private package and maintain it via that package.

In other words:

  1. You create a common codebase as a package
  2. Use that in any number of projects
  3. Once they need identical updates - you just update the package and pull newer version in the projects

At least that's how I feel it should be done, as otherwise you will constantly have to update the same thing in 4 or more places :)

K
kornelkornecki βœ“ Link copied!

But as I said I needed fixes + some extensions to many existing apps :) Otherwise I agree

We'd Love Your Feedback

Tell us what you like or what we can improve

Feel free to share anything you like or dislike about this page or the platform in general.