Using Git is probably a hard request for every dev team. It might be confusing to use at the beginning, but once you are getting more fluent, it makes the work typically teamwork much more efficient. To fully understand how Git works in and out isn’t an easy job, but actually there are only several most important and most useful git commands that you need in the daily routine.
It’s just a tool, so grab the most useful parts and practise them.
Workflow and general rules
A small scale group tends to use the “centralized workflow” with only one master branch. This sounds ok for one-person project, but working on one branch won’t be a good idea among several developers. Instead, adapting the “feature branch workflow” with pull requests will be a good starting point for a team. In this workflow, developers work on their own isolated branches, which are branched off from the same master. When the job is done, instead of immediately merging into master, each developer should open a pull request (aka. PR) in order to merge into the master. My team also has the rule to review the PR by at least one another developer before merging. During the PR, you can set the reviewing task to your colleague, leave comments or open a discussion around the feature. This process aims to avoid any mistake and to shoot it as early as possible.
Below is based on my own experience as a front-end developer. It explains the usual flow with just plain words.
Kickoff
If you are assigned an ongoing project, the git repo should be already there in Github. To kick off, locate your local folder such as cd desktop/dev
, then git clone the project into this dev folder. This copies all the current files for you to work locally. As a front-end, I also do npm install
after cloning the project to install the node module. This sets you on the same page as other developers and you should be ready to contribute your brain and sweat.
Work from a new branch
Imaging you have a task to code the signup page, first thing you should do is to create a new branch by git checkout -b your-branch-name
. (e.g. git checkout -b feature/signup
). This not only creates the new branch but also automatically switch you from the branch master to your own branch feature/signup, whatever you change will be encapsulated here without interfering with the master.
Commit your changes
Sometimes for the bigger task, you may try to divide into smaller chunks and do several commits. Each time you can do as:
git add (e.g. git add signup.component.html)
git commit -m (e.g. git commit -m 'add the form')
git add
technically means to stage the file, think it as “save” a snapshot of the current state.
git commit
commits your work to the git history with a message.
With the modern code editor such as vs code, there is source control option on the left side bar, where you can simply stage any file you want and leave a message from just a click. This saves time compared with manually typing git add
and git commit
in the terminal.
Push your work to the remote repo
When the work is completed, it’s time to push your local branch to the remote repo by git push. Of course you should still be in this branch feature/signup when pushing it. However for the first push, simply do git push will give you an error:
fatal: The current branch feature/signup has no upstream branch. To push the current branch and set the remote as upstream, use git push — set-upstream origin feature/signup
To solve this error, just do as it says, copy this git push --set-upstream origin feature/signup
, paste it and enter.
Under the hood, git push
chooses 2 more parameter: the remote repo to push and the branch to push, as this format git push <remote> <branch>
. By default, Git chooses origin for the remote and your current branch as the branch to push. If your current branch is feature/signup, git push effectively is running git push origin feature/signup. However for the first time, this branch feature/signup only exists locally but not available in the remote. The aim of set-upstream here is to tell Git where do you want to push the code: the master branch of the origin repo.
Finally you will have one local feature/signup branch and one remote feature/signup branch, technically the local one is called the "remote tracking branch", and the remote one is the "upstream branch". With this tracking relationship, your local and remote branch are now associated, which makes push and pull much easier. For any future push, the shortcut git push will just be enough, because Git is fully aware of the address since the upstream branch was set.
Merge and teamwork
Working in a dev team means everyone takes responsibility of one part and eventually commits to one repo. It’s a constant movement around push, pull and merge. While working on your task signup page, other people were coding other features and probably already pushed or merged something into master. To check what has been done and keep yourself updated with the team pace, switch yourself to master by git checkout master
, and pull all commits by git pull
. Now your master should be updated as the latest state, imagine you find out that a profile page has been finished and you would like to include that to your feature branch as reference while keep working on your signup page, you can just merge it to your feature branch. First, do git checkout feature/signup
to switch yourself to this branch. Second, do git merge master
to merge what is in your master into your current feature branch.
Stash and solve conflicts
Sometimes merging brings you conflicts. One of the common reasons is from git pull
. When you pull the origin master to your master, if another person also modified the signup page, it definitely won’t be the same as your local file. Having this conflict is because two versions of the same file do not match. As soon as you try to do git pull, this error will occur:
error: Your local changes to the following files would be overwritten by merge: signup.component.html Please commit your changes or stash them before you merge. Aborting
Git reminds you the file will be overwritten by merging, one suggestion from Git is stash, so the solution is:
First, git stash
. It instantly hides away your uncommitted changes (both staged and un-staged) to save away for later use.
Second, git pull
. Once you hide away your codes, you can pull down the most recent version from origin master without the previous error.
Third, git stash apply
. Now you want to have your own work back, while Git detects a merge conflict among your local repo, it places some additional lines in the conflicting file. It marks the incoming change from the stashed file, and marks what the existing file looks like. Since you want to replace the current contents with the stashed changes, you can choose to accept the stashed changes following Git mark, or simply delete what you don't need and delete the Git marking lines.
Once the codes are stashed, you are free to make any git commands such as creating or switching branch etc. Whenever you are ready, just apply the stashed codes as you wish.
What if merging in the wrong direction?
The normal way to use git merge is to merge your updated master into your feature branch in order to have more contents. That’s to say you should firstly sit in this feature branch by checkout to it, and then do git merge master. I remember I had once a beginner mistake when I was confused where to do this git merge command. I was in master branch, I only remember git merge, and I wrongly typed git merge my-feature. As you can imagine, I merged in the wrong direction, my changes were committed to local master and my feature branch didn’t have my changes that I would like to push. All I was worried at that moment was I messed up and how could I submit my task? According to our workflow, we should always push the feature branch and open the pull request. I needed my feature branch back with my changes, hoping time could travel back. My senior colleague saved my day with this solution:
git checkout my-feature
git reset --hard master
git push
git checkout master
git reset --hard origin/master
So the way here is firstly checkout to your feature branch, then do git reset --hard master
to set the feature branch as master, (this was what I needed to get my changes back because they were wrongly sitting in the master), then do git push
to push your work to the remote repo, then checkout to your master, then do git reset --hard origin/master
to reset your master exactly as the origin master in remote repo.
Simply speaking, git reset --hard master
resets your current branch same as master, but git reset --hard origin/master
resets your current branch exactly as the master in remote repo. These two masters are not same sometimes, one may be faster than another, both can be helpful when things are messed up.
As a safe rule, you should never merge your feature branch into master directly, this is risky of breaking the code in master. Always first merge master into the feature branch, check everything works fine, then merge the approved feature branch into the master, normally this sort of “heavy” merging job is done by the team leader.
A second look
All above talks about the critical git usage and occasional mess-up in a daily dev routine. As a takeaway, here are the most used commands.
git branch
is effectively a pointer to a commit. It allows you to create, list, rename, and delete branches. However it doesn’t let you switch between branches.
git branch signup
This creates a branch called signup, but it does NOT switch to this new branch, to create the new branch and immediately switch to it, do:
git checkout -b signup
If you suddenly have a better branch name, simply rename your current branch by:
git branch -m newSignup
If you want to rename another branch, you also need to put down the old branch name as:
git branch -m old_name new_name
After the feature branch is merged, you can delete your local branch safely, notice that -d option will delete the branch only if it has already been pushed and merged with the remote branch.
git branch -d newSignup
Using -D will force the branch to delete, even if it hasn’t been pushed or merged yet.
git branch -D newSignup
git checkout
lets you switch between different branches. Once checking out to a branch, the files in the working directory will instantly be updated to match the version stored in this branch.
To switch to a branch, you can either use the branch name or the id, which can be found in git log.
git checkout master
git checkout id_of_commit
To quickly go back to the previous branch, there’s the shortcut as:
git checkout -
Conclusion
I guess every developper must have struggled with Git one or even more times. It happens that no solution can be found or works for your Git problem, and sometimes I just want to close everything so desperately, and run git clone
to restart, on yeh this does work.
Voilà, hope that my thought helps.