How to make life easier when using Git (as well as a selection of materials for deep immersion)


Tree of Dragons II by surrealistguitarist

For those who use Git every day but feel insecure, the teamMail.ru Cloud Solutions has translated an article by front-end developer Shane Hudson . Here you will find some tricks and tips that can make it a little easier to work with Git, as well as a selection of articles and manuals of a more advanced level.

Git appeared almost 15 years ago. During this time, he went from an underdog to an invincible champion. Today, new projects often start with a team git init. Undoubtedly, this is an important tool that many of us use daily, but often it resembles magic - bright, but dangerous.

Many articles have been published on Habr, how to get started with Git, how Git works under the hood , and descriptions of the best branching strategies. Here, the author focused on how to simplify work with Git.

We put things in order


Git's point is to save your work, switch context - and do something else. This can be a backup of the code or the ability to asynchronously develop several different functions. It would be terrible to throw away the second version only because an error was found in the first. It is no less embarrassing to save files with names like v1_final_bug_fixed. As you know, this leads to a complete mess.

We all know that life becomes much easier when our updates are neatly laid out on Git branches that you can share with colleagues. But often situations arise when you change the context, then go back - and you cannot find the right branch. Was there a commit at all? Maybe he's hidden? Maybe the commit didn’t pass, now everyone has gone to the wrong branch, and everything is bad, and I am doing terribly bad work! Yes, everyone was there and felt such doubts. There are ways to deal with this situation.

Sort branches by date


Sorting by date shows all of your local branches, starting from the last. Pretty commonplace, but it helped me many times:

# To sort branches by commit date
git branch --sort=-committerdate

Previous thread


What if you didn’t commit, switch the branch, and then want to return to the previous one? You can probably find it in the list of branches if you have some idea of ​​its name. But what if it's not a branch, but a detached HEADspecific commit?

It turns out there is a simple way out:

# Checkout previous branch
git checkout -

The operator -is a shorthand for syntax @{-1}that allows you to switch to any number of checkouts back. So if, for example, you created a branch feature/thing-a, then feature/thing-b, and then bugfix/thing-c, the parameter @{-2}will return you to feature/thing-a:

# Checkout branch N number of checkouts ago
git checkout @{-N}

Show information about all branches


The flag vshows a list of all branches with the last commit identifier and message. The double vvwill also show the remote upstream branches, followed by the local branches:

# List branches along with commit ID, commit message and remote
git branch -vv

Find file


We all fell into this situation: somehow it turned out that one file was left in the wrong branch. What to do? Redo all work or copy code from one branch to another? No, fortunately, there is a way to find a specific file.

The method is a bit strange, given that git checkout -takes you to the previous branch. In general, if you specify --after the branch name in checkout, this will allow you to specify the specific file that you are looking for. You won’t guess such a function without a hint, but it is very convenient if you know:

git checkout feature/my-other-branch -- thefile.txt

Clear status


Tomasz Lacoma tweeted about reducing the issuance git statuswith the help of flags -sband added: "For many years I have been using Git, but no one has told me about this." It's not just about finding lost files. There are times when simplifying the issue makes it easier to view changes.

Most Git commands have these flags, so you should learn how to use them to customize your workflow:

# Usually we would use git status to check what files have changed
git status

# Outputs:
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)

modified: README.md

Untracked files:
(use "git add <file>..." to include in what will be committed)

another-file
my-new-file

# Using the flags -sb we can shorten the output
git status -sb

# Outputs:
## master
M README.md
?? another-file
?? my-new-file

Whole story


There are times when something went completely wrong, for example, you accidentally discarded staged (preparatory) changes before committing them. If it git logdoes not allow you to return to the previous state and none of the above tips helps, that is git reflog.

All your actions in Git that change the contents by reference HEAD@{}(for example push/pull/branch/checkout/commit) fall into the reflog (reference log). In fact, this is the story of all your actions, no matter which branch you are in. This is the difference with git log, which shows the changes for a particular branch.


You can do git showwith the commit ID - and see the specific change. If this is what you were looking for, it will git checkouttransfer you to the desired branch or even allow you to select a specific file, as shown above:

# See the reference log of your activity
git reflog --all

# Look at the HEAD at given point from reflog
git show HEAD@{2}

# Checkout the HEAD, to get back to that point
git checkout HEAD@{2}

Prep files that missed the commit


In extreme cases, if it git reflogdoes not help to get your files back (for example, you ran a hard reset with intermediate files), there is one more trick.

Each change is stored inside objects .git/objectsthat are filled with files in the active project, so it’s almost impossible to figure it out. However, there is a Git command called git fsck, which is used to check the integrity (presence of damaged files) in the repository. We can use it with a flag --lost-foundto search for all files not related to a commit. Such files are called "dangling blob".

This command also allows you to find "hanging trees" and "hanging commits." If you want, you can use the flag --dangling, but the advantage--lost-foundin that it extracts all the relevant files into a folder .git/lost-found. Most likely, in an active project you will have many such “hanging” files. Git has a garbage disposal command that runs regularly and removes them.

Thus, it --lost-foundwill show all the files and the time / date of creation, which greatly facilitates the search. Note that each separate file will still be separate, that is, you cannot use checkout. Also, all files will have incomprehensible names (hash), so you have to copy the necessary files to another location:

# This will find any change that was staged but is not attached to the git tree
git fsck --lost-found

# See the dates of the files
ls -lah .git/lost-found/other/

# Copy the relevant files to where you want them, for example:
cp .git/lost-found/other/73f60804ac20d5e417783a324517eba600976d30 index.html

Git in team work


Using Git alone is one thing, but when you work in a team of people, usually with completely different experiences, skills and tools, Git can be a blessing or a curse. This is a powerful tool for sharing the same code base, conducting a code review and monitoring the progress of the entire team. At the same time, all employees are required to have a common understanding of how to use it in team work. Regardless of what it is about: the convention of naming the branches, formatting the accompanying message in the commit, or choosing which files to include in the commit, it is important to ensure good communication and agree on how to use this tool.

It is always important to ensure the simplicity of onboarding for beginners and to think about what will happen if they start making commits without knowing the principles and conventions adopted by the company. This is not the end of the world, but it can cause some confusion and take time to return to a coordinated approach.

This section contains a few recommendations on how to integrate the accepted agreements directly into the repository itself, automate and issue the maximum number of tasks in declarations. In the ideal case, any new employee almost immediately starts working in the same style as the rest of the team.

The same line endings


By default, Windows uses DOS line endings \r\n(CRLF), while Mac and Linux use UNIX line endings \n(LF), while older versions of Mac use \r(CR). Thus, as the team grows, the problem of incompatible line endings becomes more likely. This is inconvenient, they (usually) do not break the code, but because of them, commits and pool requests show various irrelevant changes. Often people simply ignore them, because it’s quite troublesome to walk around and change all the wrong line endings.

There is a solution - you can ask all team members to configure their local configurations for automatic line completion:

# This will let you configure line-endings on an individual basis
git config core.eol lf
git config core.autocrlf input

Of course, you need to sign up for this convention and a beginner, which is easy to forget. How to do this for the whole team? According to the algorithm of work, Git checks for the presence of a configuration file in the .git / config repository, then checks the system-wide configuration of user in ~/.gitconfig, and then checks the global configuration in /etc/gitconfig.

All this is good, but it turns out that none of these configuration files can be installed through the repository itself. You can add repository-specific configurations, but they will not extend to other team members.

However, there is a file that is actually committed to the repository. It is called .gitattributes . By default, you don’t have it, so create a new file and save it as*.gitattributes*. It sets attributes for each file. For example, you can force git diff to use exif headers from image files instead of trying to calculate the difference in binary files. In this case, we can use a wildcard so that the setting works for all files, acting, in fact, as a common configuration file for the entire command:

# Adding this to your .gitattributes file will make it so all files
# are checked in using UNIX line endings while letting anyone on the team
# edit files using their local operating system’s default line endings.
* text=auto

Auto hide


It is customary to add compiled files (such as node_modules/) to .gitignore so that they are stored locally and not uploaded to the repository. However, sometimes you still want to upload the file, but do not want to meet it later each time in the pool request.

In this situation (at least on GitHub), you can add marked paths to .gitattributes linguist-generatedand make sure that .gitattributes is in the root folder of the repository. This will hide the files in the pool request. They will be “minimized”: you can still see the fact of the change, but without the full code.

Everything that reduces stress and cognitive load in the code review process improves its quality and reduces time.

For example, you want to add resource files (asset) to the repository, but you are not going to modify and track them later, so you can add the following line to the file with attributes:

*.asset linguist-generated

Use git blame more often


Harry Roberts 's article “Little Things I Like To Do With Git” recommends git blame(assign git praisetranslation ) to assign an alias (translating from translation “English”) to feel this team as a positive action. Of course, renaming does not change the behavior of the team. But whenever the discussion comes up with the use of a function git blame, everyone gets tense, and of course I do too. It is only natural to perceive the word blame (guilt) as something negative ... but this is completely wrong!

A powerful function git blame (or git praise, if you want) shows who was the last to work with this code. We are not going to blame or praise him, but just want to clarify the situation. It becomes clearer which questions to ask and to whom, which saves time.

It should be presented git blamenot only as something good, but also as a means of communication that helps the entire team to reduce chaos and not waste time figuring out who knows what. Some IDEs, such as Visual Studio, activate this feature as annotations. For each function, you instantly see who last changed it (and, therefore, with whom to talk about it).

Analog git blame for missing files


Recently, I saw a developer on our team trying to figure out who deleted a file, when, and why. It seems like it can help here git blame, but it works with the lines in the file and is useless if the file is missing.

However, there is another solution. Old faithful git log. If you look at the log without arguments, you will see a long list of all changes in the current branch. You can add a commit identifier to see the log of this particular commit, but if you specify --(which we used earlier to target a specific file), you can get a log for a file - even one that no longer exists:

# By using -- for a specific file,
# git log can find logs for files that were deleted in past commits
git log -- missing_file.txt

Commit Message Template


Commit messages often need to be improved. Sooner or later, the developers in the team come to this conclusion. There are many ways to improve. For example, you can refer to bug identifiers from an internal project management tool, or perhaps encourage writing at least some text instead of a blank message.

This command needs to be run manually every time someone clones the repository, since configuration files are not committed to the repository. However, it is convenient because you can create a common file with any name that acts as a commit message template:

# This sets the commit template to the file given,
# this needs to be run for each contributor to the repository.
git config commit.template ./template-file

Git for automation


Git is a powerful automation tool. This is not immediately obvious, but think for yourself: he sees all your activity in the repository - plus the activity of other participants - and he has a lot of information that can be very useful.

Git hooks


Quite often, you see that team members perform repetitive tasks while working. This can be a test of passing tests and linter before sending a branch to the server (hook before sending) or a forced strategy for naming branches (hook before committing). On this subject, Konstantinos Lamonis wrote an article in the Smashing Magazine entitled “How to Simplify Workflow Using Git Hooks” .

Manual automation


One of the key automation features in Git is git bisect. Many have heard of it, but few use it. The bottom line is processing the Git tree (commit history) and finding where the error is entered.

The easiest way to do this is by hand. You start git bisect start, set the identifiers of good and bad commits (where there is no bug and where there is a bug), then execute git bisect goodor git bisect badfor each commit.

This is a more powerful feature than it seems at first glance, because it does not run the Git log linearly, which could be done manually as an iterative process. Instead, it uses a binary search that effectively passes through commits with the least number of steps:

# Begin the bisect
git bisect start

# Tell git which commit does not have the bug
git bisect good c5ba734

# Tell git which commit does have the bug
git bisect bad 6c093f4

# Here, do your test for the bug.
# This could be running a script, doing a journey on a website, unit test etc.

# If the current commit has bug:
git bisect bad

# If the current commit does not have the bug
git bisect good

# This will repeat until it finds the first commit with the bug
# To exit the bisect, either:

# Go back to original branch:
git bisect reset

# Or stick with current HEAD
git bisect reset HEAD

# Or you can exit the bisect at a specific commit
git bisect reset <commit ID>

Moving On: Scientific Automation


In his scientific debugging report, Stuart Halloway explained how to use the command git bisectto automate debugging.
He focuses on Clojure, but we don’t need to know this language to benefit from his talk.

Git bisect is partly scientific automation. You write a small program that will test something, and Git jumps back and forth, cutting the world in half with each jump, until it finds the border at which your test changes state.
Stuart Halloway

At first it git bisectmay seem like an interesting and pretty cool feature, but in the end it is not very useful. Stewart’s performance to a large extent shows that it is actually counterproductive to debug as we are used to. If you instead focus on empirical facts, whether the test passes or not, then you can run it on all commits, starting with the working version, and reduce the feeling of “wandering in the dark” that we are used to.

So how do we automategit bisect? You can pass it a script for each corresponding commit. Earlier, I said that you can manually run the script at each step of bisect, but if you pass a command to run, it will automatically run the script at each step. It can be a script specifically for debugging this specific problem or a test (modular, functional, integration, any type of test). Thus, you can write a test to verify that the regression does not repeat, and run this test on previous commits:

# Begin the bisect
git bisect start

# Tell git which commit does not have the bug
git bisect good c5ba734

# Tell git which commit does have the bug
git bisect bad 6c093f4

# Tell git to run a specific script on each commit
# For example you could run a specific script:
git bisect run ./test-bug

# Or use a test runner
git bisect run jest

On every past commit


One of the strengths git bisectis the effective use of binary search to bypass all events in history in a non-linear way. But sometimes a linear bypass is needed. You can write a script that reads the Git log and cycles through the code on every commit. But there is a friend, who will do it for you: git rebase.

Kamran Ahmed in a tweet written as rebaseis, what commit does not pass the test:

Find a commit that fails the test:

$ git rebase -i --exec "yarn test" d294ae9

The command runs yarn test on all commits between d294ae9 and HEAD and stops at the commit where the test crashes.

We have already considered git bisectfor this task, which may be more efficient, but in this case we are not limited to one use case.

There is a place for creativity. Perhaps you want to generate a report on how the code has changed over time (or show the history of tests), and just parsing the Git log is not enough. Maybe this is not the most useful trick in this article, but it is interesting and shows a task, the reality of which we could not believe before:

# This will run for every commit between current and the given commit ID
git rebase -i --exec ./my-script

A selection of articles and manuals to help you get deeper into Git


In such an article, it is impossible to delve into the topic, otherwise the whole book will turn out. I chose some small tricks that even experienced users might not know about. But Git has much more features: from basic functions to complex scripts, precise configurations and console integration. Therefore, here are some resources that may be of interest to you:

  1. Explorer git . An interactive site that helps you easily understand how to achieve what you want.
  2. Dang it git! . Each of us at some point can get lost in Git and does not know how to solve any problem. This site provides solutions to many of the most common problems.
  3. Pro git . This free book is an invaluable resource for understanding Git.
  4. Git Docs. — . , Git Docs, Git (, man git-commit) Git .
  5. Thoughtbot. Git Thoughtbot .
  6. Git. Git.
  7. Git. , … . Git, .
  8. Git: from beginner to advanced level . Mike Ritmüller has written this useful article, which is ideal for novice Git users.
  9. Little things I love to do with Git . It was this article by Harry Roberts that made me realize how many possibilities still lurk in Git, apart from moving code across branches.
  10. Atlassian Advanced Git Guides . These tutorials detail many of the topics mentioned in this article.
  11. Git cheat sheet on Github . It's always convenient to have a good cheat sheet for tools like Git.
  12. Git reduction . This article details various Git command flags and recommends many aliases.

But what else can you read :

  1. Git. №1: , .git
  2. Git. №2: rebase.
  3. .

All Articles