5

I am working on a site with other three developers.

We are all working on different features for the same site.

We are having conflicts with project config files, since we are merging our changes into the main branch at different "random" times, and this makes our project config folder have added and deleted files each time another member does a push or fetch to/from main.

How do you recommend organizing the team workflow so that we don't run into this problem while having multiple people which are working on multiple branches?

I have been trying to understand where the conflict comes from, so:

  • Person A creates a new field or changes a setting, it gets reflected into the database and into the project yaml files.
  • Person A commits and pushes changes into main.
  • Person B fetches the changes while still working on two different branches locally.
  • Craft asks for a project config update but then a bunch of project config files get deleted, and others added.
  • And what's even worse, if person A did a pull request to main but wants to work on a different branch before that pull request gets merged, changing to another branch will also result in "pending changes"

This is very confusing, and a minor error can lead to total disaster, having to redo all the lost fields and settings.

1 Answer 1

11

Your trouble comes from working in the main branch directly, don't do that. Instead, follow a feature branch workflow where every feature is developed in a dedicated branch. This way, every developer can work in their own branch without other people interfering. You can even configure branch protection rules in GitHub (if you're using GitHub) to prevent everyone from pushing to the main branch directly. Instead, merging finished features is done through pull requests. You can allow developers to merge pull requests as soon as they're opened up, though I highly recommend requiring at least one approving review from another developer. This has the added benefit of having at least two pairs of eyes on every feature that gets introduced to the project, which allows you to catch bugs early and increase everyone's knowledge of the entire codebase.

If you're working in your own branch, other developers merging something into the main branch on the remote won't interfere with your work, and you can update your local main branch whenever you want without conflicts. You still might have to fix conflicts between your changes and the changes introduced, but you can do so at your leisure and only need to do so when your feature is done.

With this workflow, the regular process looks a little like this:

  1. Developers A(lice) and B(ob) both start work on a feature. Both create a local branch (feature-a and feature-b, respectively) based on the main branch at this point in time.
  2. While Bob is still working, Alice is done. She pushes her branch to GitHub and opens a pull request. Charlie reviews the request and approves it, Alice merges it into main (and deletes branch-a on GitHub). Now Alice can check out the main branch locally, pull the merged changes from GitHub and delete her feature-a branch. Now she can create a new branch for the next feature she is going to work on based on the current main.
  3. Bob is finished with his feature by now. Once all his changes are committed in feature-b, Bob checks out the main branch and pulls the latest changes. Then he checks out feature-b again. Now he can either rebase his branch on main (git rebase main) or merge main into his branch (git merge main). I prefer rebasing, because it results in a linear history. Both of those operations will report any conflicts and place conflict markers if necessary. Often times, there won't be any conflicts, in this case Bob can continue to open a pull request.
  4. Let's assume there are conflicts. Now is the time to resolve them, the same way you would resolve other conflicts in git. Once all conflicts are resolved, Bob has a branch that's either rebased on main or has the latest changes from main merged into it, so it includes both the first feature Alice worked on, and the feature Bob worked on. Now Bob can open a pull request that won't result in any conflicts.

The key to not get other people's config changes as uncommitted changes in your working directory is to apply the config every time you change branches. That means:

composer install
php craft project-config/apply
php craft migrate/all

You can just put those three commands in a script and call it whenever you change branches. You can even use git hooks to automatically execute those commands whenever you change branches.

By the way, you can usually skip step 3 with no problems. If you open a pull request without having pulled the latest changes from main, GitHub will tell you if you can merge the PR without causing any conflicts. If GitHub reports no conflicts, you can just merge the PR which performs a three-way merge directly on the remote. If you do get conflicts, you can proceed to do step 3 (pull changes from main and merge/rebase). Note that in this case, after you rebase, you have to push using --force to force the remote to accept the rebased commits and overwrite the old commits.

And what's even worse, if person A did a pull request to main but wants to work on a different branch before that pull request gets merged, changing to another branch will also result in "pending changes"

You just need to use php craft project-config/apply after switching branches, to make sure your development environment matches the state of the branch you're working on now.

Working on dependent features

A special case is that you one developer wants to work on multiple features that depend on each other in succession, without waiting for the previous PRs to be reviewed and merged. In this case, you can create a new branch based on the previous branch and work on that. As soon as the first branch (PR) is merged, you rebase the second one onto main.

Let's say Alice wants to work on feature-a, then feature-b which depends on feature-a. First, she creates feature-a:

  • git checkout -b feature-a
  • git add .
  • git commit -m "Feature A"
  • git push
  • Now Alice opens a PR for feature-a on GitHub.

Now Alice wants to start work on feature-b right away. Without first switching to main, the creates a new branch:

  • git checkout -b feature-b
  • git add .
  • git commit -m "Feature B"
  • git push
  • Now Alice opens another PR for feature-b.

One caveat of this is that GitHub will show all changes from both feature-a and feature-b in the interface, because relative to main, feature-b includes all commits from both branches. You can work around this by setting the PR base to feature-a:

Screenshot of the GitHub UI showing how to change the base of a pull request.

This way, GitHub will only display the changes relative to feature-a. As soon as feature-a is merged and the branch is deleted, GitHub will automatically change the base to main. Keep in mind that if you have any branch protection rules, they will not apply to the PR in this case.

4
  • 1
    Vielen Dank Moritz! This is clearly an amazinlgy clear answer! Really helps. How would you go about this case: Bob works on feature-a and edits a field which causes an edit to a specific project file (let's call it specific-file.yaml). Then Bob does a pull request on feature-a. Then Bob wants to work on feature-b before feature-a gets approved. When Bob checks out feature-b, it will appear that specific-file.yaml has been edited (since it now contains different content). Then Alice approves the pending Pull Request but Bob has now a wrong content for specific-file.yaml which will cause issues. Mar 1, 2022 at 11:17
  • 1
    @ÁlvaroFranz Always use project-config/apply after switching branches. After opening the PR, if Bob wants to work on a new feature, he first checks out main and uses project-config/apply. Now the changes in the feature-a branch are gone from his working dir and his local installation and he can work on feature-b without problems. If feature-a is merged while Bob works on feature-b, he can do the rebase/merge (step 3 & 4) once he' finished, which usually won't result in any conflicts.
    – MoritzLost
    Mar 1, 2022 at 11:30
  • 1
    @ÁlvaroFranz A special case is if feature-b depends on feature-a. In this case, Bob can base the feature-b branch on feature-a, which means he can continue to work on a feature that require the changes from feature-a even while feature-a is in review. Once feature-a is merged, it's again time to rebase feature-b on main. It's also important that every developer has a good understanding of Git, in particular the branching model, to be able to use this workflow effectively.
    – MoritzLost
    Mar 1, 2022 at 11:32
  • 1
    Thanks again! ✌️ Mar 1, 2022 at 11:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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