There is a wonderful book Pro Git, which describes in detail all the commands and features of the gita. But after reading it, many still have a misunderstanding of how to use all this in practice. In particular, programmers of different levels often have questions about how to work with branches in Git, when to start them and how to merge with each other. Sometimes I came across very "original" and unreasonably complicated schemes of working with git. While in the community of programmers has already formed a scheme of work with git and branches in it. In this article I want to give a brief overview of the main points when working with Git, and describe the “classical” workflow with branches. Much of what is described in this article will be true for other version control systems.
This article may be useful for programmers who are just starting to learn Git, or some other version control system. For experienced programmers, this article will seem very simple and banal.
First, let's understand what a branch and commit are.
Commit
We can say that a commit is the main object in any version control system. It contains a description of the changes that the user makes to the application code. In Git, a commit consists of several so-called objects. For ease of understanding, we can assume that commits are a simply linked list consisting of objects that contain modified files, and a link to the previous commit.
')

A commit has other properties. For example, commit date, author, comment to commit, etc.
As a comment, they usually indicate the changes that this commit makes to the code, or the name of the task that it solves.
Git is a distributed version control system. This means that each project participant has its own copy of the repository, which is located in the “.git” folder, which is located in the project root. It is in this folder that all commits and other Git objects are stored. When you work with Git, it in turn works with this folder.
Creating a new repository is very simple, it is done by the team.
git init
So you get a new empty repository. If you want to join the development of an existing project, then you will need to copy this repository to your local folder from a remote repository. This is done like this:
git clone <url >
After that, a .git directory will appear in the current folder, which will contain a copy of the remote repository.
There are several main areas where code is located.
- The working directory is the files in the project root, the code you are working with.
- The local repository is the “.git” directory. It stores commits and other objects.
- Remote repository is the same repository that is considered to be shared, to which you can transfer your commits from the local repository, so that other programmers could see them. There may be several remote repositories, but usually it is one.
- There is another area with the understanding that there are usually problems. This is the so-called staging area . The fact is that before you include any changes in a commit, you must first note what exactly you want to include in this commit. You can change several files in your working directory, but not all of these changes should be included in one particular commit.
In general, working with git looks like this: you change files in your working directory, then add these changes to the staging area using the command
git add </>
You can use masks with an asterisk.
Then you commit to your local repository.
git commit –m “ ”
When kommitov to collect enough that it was possible to share them, you execute a command
git push
Then your commits go to the remote repository.
If you need to receive changes from a remote repository, then you need to run the command
git pull
After that, the changes that were sent by other programmers will appear in your local repository.
The code in the project workspace is formed by applying those changes that are contained in commits. Each commit has its own name, which is the result of the hash function sha-1 from the contents of the commit itself.
You can view commits using the command
git log
The default response format of this command is not very convenient. Here such a command will display the answer in a more readable form.
git log --pretty=format:"%H [%cd]: %an - %s" --graph --date=format:%c
To finish viewing you need to press the q key
You can see what is in the working directory and staging area with the command
git status
The working directory can be switched to the previous state by executing the command
git checkout <hash >
Just before doing this, run git status and make sure you don’t have any local or unchanged changes. Otherwise, Git will not understand how to switch. git status will tell you what can be done with local changes so that you can switch. This rule should be followed in all other switchings of the working area.
Branch
A branch in Git is a moving pointer to one of the commits. Usually a branch points to the last commit in a chain of commits. The branch originates from a single commit. Visually, this can be represented like this.

You can make a new branch and switch to it by executing the commands
git pull
git checkout –b < >
Just make a branch, without switching to it, you can command
git branch < >
switch to branch
git checkout < >
It is important to understand that the branch originates not from the branch, but from the last commit that is in the branch in which you were located.
A branch usually ends with a special
merge commit , which says that the branch needs to be merged with some other branch. A merge commit contains two links to two commits that are combined into one branch.

There is another situation when merging branches in which merge can occur without merge commit. The fact is that if in one of the branches there are no changes, then the need for a merge commit with two ancestors disappears. In this case, when merging branches, Git will simply make a note that the commits of the branch with which this branch was merged will go on. Such a merge scheme is called fast-forward merge, visually this can be represented as follows.

In all these cases, after a branch merges with another branch, all commits made in it fall into the branch with which it was merged. It is also important to understand that merge is not a bidirectional operation. If you merge a task branch into the master branch, then the code that was in the task branch will appear in the master branch, and no new code from the master branch will appear in the task branch. If you want this to happen, you need to merge the master branch into the task branch.
To transfer one branch to another you first need to switch to the branch you want to merge into.
git checkout < >
Then get the latest changes made in this thread by running
git pull
Then execute the command
git merge < >
This is how work with branches looks in general.
Please note that before starting a new branch you need to perform git pull. This is done for several reasons.
- Another programmer could change the code, including making such changes that will affect the solution of the problem for which you are starting a new branch. These changes may be useful to you when solving your problem.
- Because of these changes, you can get a conflict with merzh.
- More chance for merge commit. This is not as bad as the previous two points. But if you can avoid unnecessary commits, then why not do this?
Popular branching schemes in Git
Now you can describe the popular schemes of working with branches in the gita.
Branches are needed so that programmers can work together on a project and not interfere with each other. When creating a project, Git creates a basic branch. It is called the master branch. It is considered a central branch, i.e. it contains the main application code.
The classic scheme of working with branches
Usually, before starting to solve a problem, the programmer starts a new branch from the last working commit of the master branch and solves the problem in this new branch. During the solution, he makes a series of commits, then tests the code directly in the branch of the problem. And after the problem is solved, they do the merge back to the master branch. Such a scheme of work is often used with unit tests and automated deploem. If the unit tests cover the whole code, then you can configure the deployment so that first all the tests in the task branch will be run. And after that, if they are successful, merge and deployment will occur. With this scheme, you can achieve full automation during testing and deployment.
Nominal branch
Inexperienced programmers give birth to their own branch and always work in it. They solve one task at a time, and when they finish solving one of the tasks, they make a new Pull request via the Web interface (more on this later). The disadvantage of this approach is that only one task can be solved this way and one cannot quickly switch to solving another task. Another drawback is that the branches will diverge more and more over time and the code in the programmer’s branch will sooner or later become obsolete with respect to the master of the branch and will have to be updated. To do this, you can either merge the master branch into the programmer's branch, or start a new branch for this programmer from the last working state into the master branch. True, by the time this happens, the programmer can already master the git sufficiently to switch to the “classical” scheme of work. Thus, this scheme is the place to be for inexperienced Git users.
Diagram with dev branch
The other scheme is very similar to the classical one, only there is a development branch in addition to the master branch, which is deployed to the test server. This branch is usually called dev. The scheme of work with this. A programmer, before performing a new task, gets a branch for it from the last working state to the master branch. When he finishes work on the task, he merges the branch of the task into the dev branch himself. After that, jointly, the task is tested on a test server along with the rest of the tasks. If there are errors, then the task is refined in the same branch and re-merged with the dev branch. When the testing of the task ends, the TASK TAG is merged with the master branch. It is important to note that in this scheme of working with the master branch, you need to merge the branch of the task, and not the dev branch. After all, the dev branch will contain changes made not only in this task, but also in others and not all of these changes may turn out to be working. The master branch and the dev branch will diverge over time, so with such a scheme of work, a new dev branch from the last working state of the master branch is periodically started. The disadvantage of this approach is redundancy, compared with the classical scheme. This branching scheme is often used if there are no automated tests in the project and all testing is done manually on the development server.
It should also be noted that these work schemes can be combined with each other, if there is any need for this.
Pull requests
There is confusion with this concept. The fact is that in Git there are two completely different things that can be called a pull request. One of them is the git pull console command. The other is the button in the repository web interface. On github.com, it looks like this

About this button and will be discussed further.
If the programmer is experienced and responsible enough, he usually merges his code into the master branch himself. Otherwise, the programmer makes a so-called pull request. Pull request is essentially a request for permission to make a merge. Pull request can be done from the Git web interface, or using the request-pull git command. After the pull request is created, other participants can see this, view the code that the programmer is proposing to contribute to the project, and either approve this code or not. Merge through pull requests has its pros and cons. The downside is that for a close team of experienced programmers such an approach would be superfluous. This will only slow down the work and bring it into the shades of bureaucracy.
On the other hand, if there are not experienced programmers in the project who can break the code, then Pull requests can help avoid errors, and quickly train these programmers by observing what changes they propose to make to the code.
Similarly, pull queries are suitable for a wide community of open source programmers. In this case, one cannot say in advance something about the competence of such developers and what they want to change in the code.
Conflicts
Conflicts arise when branches are merged if in these branches the same line of code was changed differently. Then it turns out that Git cannot decide which of the changes to apply, and he suggests manually solving this situation. This slows down the work with the code in the project. This can be avoided by various methods. For example, you can distribute tasks so that related tasks are not performed simultaneously by different programmers.
Another way to avoid this is to agree on a particular style of code. Then programmers will not change the formatting of the code and the likelihood that they will change the same line will be lower.
Another good advice that will help you avoid conflicts when working in a team is to make minimal changes to the code when solving problems. The fewer lines you have changed, the less likely you are to change the same line as another programmer in another task.
After the master branch reaches a state that can be considered stable, it is tagged with a version of this state. This is what is called the program version.
This is done like this
git tag -a v1.0
To transfer branches to a remote repository, you need to run the command
git push –tags
Tags are also convenient because you can easily switch to the state of the code tagged. This is done using the same command.
git checkout < >
Various deployment and automated assembly systems use tags to identify the state that needs to be fixed or assembled. This is done because if we collect or deploy the code of the latest version, then there is a risk that some other programmer will make some changes to the master branch at that moment, and we will collect not what we wanted. In addition, it will be easier to switch between working and verified project states.
If you adhere to these rules and the “classical” scheme of working with branches, then it will be easier for you to integrate your Git with other systems. For example, with a continuous integration system or with a package repository, such as packagist.org. Usually, third-party solutions and any extensions are designed specifically for such a scheme of working with git, and if you immediately start doing everything correctly, then this can be a big plus for you in the future.
This is an overview of the main points when working with Git. If you want to learn more about Git, then I would advise you to read the Pro Git book.
Here it is .
In this article, a simplified scheme for presenting commits has been given. But before writing it, I decided to figure out how exactly the commits are stored on the disk. If you are also interested in this question, then you can read about it
here .