This git fundamentals post will help you to understand how to collaborate using git with your colleagues. Considering you already know what is git and how to create a repository, now we can dig further using git effectively with other collaborators. Note that this won’t include open source project approach. Even the principle is the same, some of the projects have different requirements for pull requests. You can further investigate how to create pull requests for this. First of all, I would like to stress what a development process would be like without source control. A friendly advice: if you think it is hard to work with git, think twice. It might take a while to understand and get used to work on git, but it definetely worths your time.
Without source control, basically the major problems are those:
- In order to work together, you should set up a NAS, cloud or use something like USB to share your work between your colleagues.
- It would be impossible to work on the same code at the same time. If not careful, you can override your friends work with your save. If you are careful, you should check the conflicts by hand.
- You can’t check the progress of your colleagues and create test builds using only the features you want to test out
The list above is the common problems, and imagine you can hit much more road blocks in the development process.
Before diving further, we should talk about some theory first. In order to work effectively you and your collaborators should know the theory behind Git. To keep it simple, we will focus on 2 developers: Back-End and Front-End. Consider your repository is a station at the beginning of a road and each developer is at the same station. Since they are working on different stuff, they should take different paths from that station. When Front-End finishes his work, he puts his work into the road, as a new station. Same applies for Back-End developer as well. This time, when Back-End developer puts her work into the road, it is automatically put after Front-End station. Now that the road contains 2 developers work, when they start a new path they will both have that stations combined together. To illustrate this in a graph
* Road 3 (All Back-End Merged) / | Back-End Work 3 * * Road 2 (All Front-End Merged) | | \ Back-End Work 2 * | * Front-End Work 2 | | | Back-End Work 1 * | * Front-End Work 1 \ | / * Road 1 (Base)
As you can see above, Road now includes all work done! Whenever someone creates a new path from Road 3, they will all have the work done so far, allowing everyone to keep up. All those Road, and Paths are actually called branches. Branching is one of the most important things you should learn while collaborating. There are different branching approaches around, you should find or create one that best suits your workflow. You can check GitFlow, an extension for making branching easily. But we will focus on our simple branching model, close to GitFlow rules.
Here, we have our basic branching rules every colleague should use:
- Master Branch: Master branch will be your release branch. Everything merged to here should have passed QA tests.
- Develop Branch: Everything worked on should be merged into develop. When time comes, QA should test from latest commit’s build. When everything is in order, develop merges into master for release.
- Feature Branches: Feature branches are the branches you will spend time mostly. Since branches can be nested, if you create a branch feature/MyBranch it will be created under feature, just like a folder. Those features can be merged into develop.
When you create a repository, there will be a master branch by default. When you clone the repo for the first time (considering you didn’t directly checked-out to a different branch) you will also be checked-out to master branch. You can use that branch to push your initial commit including your .gitignore and readme files. Then, time to create your first branch:
git branch develop git checkout develop
This will create develop branch and check out to it. This branch includes everything commited to master branch at the time you checked-out. You can also create and check-out to a branch with a single command: 'git branch -b develop’. Now everything seems to be set up. but you need to push your branch to server to let your colleagues, access it.
git commit -m "Initial Develop Branch" git push
You will realise there is an error telling you there is no branch on server called develop, and it shows a code instead to set upstream. You can copy-paste that code to push it to remote. Once you have pushed develop into remote, you can only use push command instead. Now it is time for your colleagues to checkout to remote develop to create their branches.
git checkout --track origin/develop
This code pulls the develop branch and tracks it. Basicly tracking is saying “this branch on my local is that branch on remote” in a short way.
Working on feature branch
This section is a general approach for all your collaborators. It is time to create your own branch to move on
git branch -b feature/MyFeatureBranch
Now you have your own feature branch to work on. When you have created files, you are ready to commit. Don’t forget, commiting is also useful when you wan’t to revert your changes back, until you push to remote. Here is the code to do so:
git add * # Adds all files. You can add files one by one by giving file path git commit -m "Lovely Message" # Write something simple yet self-explanatory
You can continue commiting your changes whenever you feel right. You can also push your code to remote. First, check if you have anything left to be commited and be sure you are on the correct branch.
With that command, you can see untracked files as well. If you need to commit files, you can do so. Otherwise no necessary files have changed, you can set them to their first state.
# Reset all tracked files to their latest state git checkout . # Remove all untracked files git clean -n # Shows all untracked files git clean -f # Remove all untracked files if not necessary
You can also stash files to get back to them, but to keep it simple I am not going to explain it. You can further investigate how to use stash commands from Git SCM. Now that your branch is clean, you can either push it to remote or merge directly into develop. If you are working on a branch that should take time, you can push it to remote a prototype version, so that other colleagues can merge it to their branches to keep adding their code. If you want to keep going on that branch, you can use
If you haven’t pushed remote before, you will see that upstream error meaning there is no such branch on remote. Simply use the given code and push it to remote. Now that you can continue commiting changes into your local as long as it takes. If you want to finish this feature for good, then without pushing you can directly merge it into develop branch. To do so, you should checkout to develop branch.
git checkout develop # Checks out the develop branch git fetch # Looks for any changes for develop branch on remote git status # Check if you are behind the remote develop git pull # If you are behind, pull the latest changes
Now that you have the latest develop branch, you can merge your branch into develop and push it. Be sure that you are still on develop branch!
git merge feature/MyFeatureBranch git push
If there is no conflict between your changes and develop’s changes, it should directly merge. Otherwise, console will give you conflict error where you should edit conflicts. There are lots of different GUI merge tools to help you out there! I am using VSCode's internal merge layout bu you can use other apps such as Meld on Windows, Kaleidoscope on MacOS. You can also use GUI Git Clients such as Fork, SourceTree and GitKraken, where they have conflict resolving GUI as well. Decide an app, and learn their approach how to resolve conflicts. Basically, they have your code and the code on branch side-by-side. You can see who deleted what and added where, so you can add yours into final one. This final one will be the resolved file.
You are asking where is the collaboration part, right? Well, above is the general workflow working on branches actually. But consider this, if everyone minds the rules above, they should do the very same thing on different branches, just like you did above! So, to sum it up:
- Create a branching rule. One of the most common approach is master, develop and features. Be sure to know what merges into where.
- Don’t commit unnecessary changes. Commit often, when necessary.
- Check your status before commiting and pushing.
- Use clear commit messages, your colleagues needs to understand what you did from those messages.
- Don’t leave your HEAD with uncommited changes, if you try to checkout you may conflict with existing ones. You can stash them, or revert them if unnecessary.
- Whenever you checkout to a branch used by multiple collaborators, always fetch and see if you are behind the remote branch.
- Solve conflicts and test them. If you are using Pull-Requests for merging into master, ask PR sender to fix conflicts.