Introduction to Git

Table of contents



Foreword

1. Configuring git

.... 1.1 Configuration Files

.... 1.2 Default settings

.... 1.3 Aliases

2. The basics of git

.... 2.1 Creating a repository

.... 2.2 File Status

.... 2.3 Working with the index

.... 2.4 Working with commits

.... 2.5 View History

.... 2.6 Working with a remote repository

3. Branching in git

.... 3.1 Basic operations

.... 3.2 Merging branches

.... 3.3 Rerere

4. Pointers in git

.... 4.1 Moving pointers

5. Recommended reading



Foreword



Git is the most popular distributed version control system. [1] [2]



The main purpose of Git is to save snapshots of the successively improving conditions of your project (Pro git, 2019).



This article is for those who have at least basic knowledge and skill in working with git and who wish to expand their knowledge.



Only technical aspects of git are considered here, for a more detailed immersion in git's philosophy and its internal implementation, I advise you to read several useful books (see Recommended reading ).



1. Configuring git



Before you start working with git, you need to configure it for yourself!



1.1 Configuration Files





There is a special team



git config [<>]
      
      





which will allow you to change the standard behavior of git, if necessary, but you can edit the configuration files manually (I think so faster).



Depending on which parameter you pass to the git config (--system, --global, --local) command, the settings will be written to one of these files. Each of these “levels” (system, global, local) redefines the values ​​of the previous level!



To see in which file what settings are installed, use git config --list --show-origin.



Ignoring files

In git, you decide which files will go into which commit, but perhaps you would like certain files to never be in the index and in the commit, and not even appear in the untracked list. To do this, you can create a special file (.gitignore) in your repository and write the template of ignored files there. If you do not want to create such a file in each repository, you can define it globally using core.excludesfile (see Useful Settings ). You can also download the finished .gitignore file for the programming language you are working on.

To customize .gitignore, use bash regular expressions .



1.2 Default Settings



There are a bunch of git settings for both the server and the client, only basic client settings will be considered here.



Use



 git config name value
      
      





where name is the name of the parameter, and value is its value in order to set the settings.

Example:



 git config --global core.editor nano
      
      





will install the default editor nano.



You can look at the value of an existing parameter with git config --get [name] where name is the parameter whose value you want to get.



Useful settings:





1.3 Aliases



If you don't want to print every command for Git in its entirety, you can easily configure aliases. To create an alias use:



 git config alias.SHORT_NAME COMMAND
      
      





where SHORT_NAME is the name to abbreviate, and COMMAND the command (s) to be abbreviated. Example:



 git config --global alias.last 'log -1 HEAD'
      
      





after executing this command, you can view information about the last commit on the current branch by running git last.



I advise you to use the following abbreviations (you can also define any of your own):





To view configuration settings, use: git config --list.



2. The basics of git



Only mandatory and useful (in my opinion) parameters are listed here, for listing all is inappropriate. To do this, use git command -help or --help, where command is the name of the command the help you want to get about.




2.1 Creating a repository





2.2 File Status



To view the status of files in your repository, use:



 git status [<>]
      
      





This command can show you: which branch you are currently on and the status of all files. There are no required options, only -s can be distinguished from useful ones, which will show a brief idea of ​​the states of files.



File life cycle image

As you can see in the picture, the files can be untracked and tracked. The monitored files can be in 3 states: Not modified (Unmodified), modified (Modified), prepared (Staged).

If you add (using git add) an “Unmonitored” file, then it goes into the “Prepared” state.

If you change the file to the “Not changed” state, then it goes into the “Changed” state. If you save the modified file (that is, in the “Modified” state), it goes into the “Prepared” state. If you commit a file (that is, in the "Prepared" state), it goes into the "Not changed" state.

If the versions of the file in HEAD and the working directory are different, then the file will be in the “Modified” states, otherwise (if the version in the HEAD and the working directory is the same “), the file will be in the“ Not changed ”states.

If the version of the file in HEAD differs from the working directory, but does not differ from the version in the index, the file will be in the “Prepared” state.



This cycle can be represented as follows:

Unmodified -> Modified -> Staged -> Unmodified

That is, you modify the file, save it in the index and make a commit, and then all over again.



2.3 Working with the index



I hope you understand what the life cycle of the git repository looks like. Now let's see how you can manage the index and files in your git repository.



An index is an intermediate place between your last commit and the next. You can add or remove files from the index. When you commit, it gets data from the index, and not from the workspace.



To view the index, use git status.



To add files to the index use

 git add [<>]
      
      





Useful git add command options:







To remove files from the index, you can use 2 git reset and git restore commands.

git-restore - restores working tree files.

git-reset - resets the current HEAD to the specified state.

In fact, you can achieve the same thing with both commands.



To remove some files from the index, use:



 git restore --staged <file>
      
      





this way you will restore your index (or rather, delete specific files from the index), as if git add had not been executed for them since the last commit. Using this command, you can restore the working directory so that it looks as if no changes were made after the commit. But this command has a bit strange behavior - if you added a new version of your file to the index, you cannot change your working directory until the index is different from HEAD. Therefore, you first need to restore your index and only then the working directory. Unfortunately, it is not possible to do this with a single command, since when passing both arguments (git restore -SW) nothing happens. And just like that, transferring -W will also not happen if the file in the index and HEAD are different. Probably, they did it for protection so that you would not accidentally change your working directory. But in this case, why is the -W argument passed by default? In general, I don’t understand why this was done and why this command was added at all. For me, reset copes with this task much better, and it also has richer functionality since it can move the index and working directory not only to the last commit, but also to any other.



But the developers themselves recommend using git restore -S to reset the index. Instead of git reset HEAD.



Using git status you can see which files have changed, but if you also want to know what exactly has changed in the files, use the command:



 git diff [<options>]
      
      





thus, by running the command without arguments, you can compare your index with the working directory. If you have already added files to the index, then use git diff --cached to see the differences between the last commit (or the one you specify) and the working directory. You can also see the differences between two commits or branches by passing them as an argument. Example: git diff 00656c 3d5119 will show the differences between commit 00656c and 3d5119.



2.4 Working with commits



Now that your index is in the right state, it's time to commit your changes. Remember that all files for which you did not run git add after editing are not included in this commit. In fact, there will be files in it, but only their old version (if any).



To commit your changes, use:



 git commit [<>]
      
      





Useful options for the git commit command:





You can define a default commit message using commit.template. This directive in the configuration file is responsible for the file whose contents will be used for the default commit. Example: git config --global commit.template ~ / .gitmessage.txt.



You can also change, delete, merge any commit.

As you may have noticed, you can quickly overwrite the last commit with git commit --amend.

To change the commit in your story, use



 git rebase -i <commit>
      
      





where commit is the top commit in your chain from which you would like to change anything.



After executing git rebase -i in the interactive menu, select what you want to do.





To change the message of a specific commit.

You must change pick to edit above the commit you want to change.

Example: you want to change the message of the 750f5ae commit.



pick 2748cb4 first commit

edit 750f5ae second commit

pick 716eb99 third commit



After saving the script, you will return to the command line and git will tell you what to do next:



Stopped at 750f5ae ... second commit

You can amend the commit now, with



git commit --amend



Once you are satisfied with your changes, run



git rebase --continue



As indicated above, you must run git commit --amend in order to change the commit message. Then execute git rebase --continue. If you have selected several commits for changing the name, then these operations will need to be done on each commit.



To delete a commit

You must delete the line with the commit.

Example: you want to delete the commit 750f5ae

You need to change the script from this:

pick 2748cb4 third commit

pick 750f5ae second commit

pick 716eb99 first commit

on this:

pick 2748cb4 first commit

pick 716eb99 third commit



To merge commits

You must change pick to squash over the commits you want to merge.

Example: you want to combine the commits 750f5ae and 716eb99.

You need to change the script from this:

pick 2748cb4 third commit

pick 750f5ae second commit

pick 716eb99 first commit

On such

pick 2748cb4 third commit

squash 750f5ae second commit

squash 716eb99 first commit



Notice that in the interactive script, commits are shown in reverse order than in git log. Using squash, you combine the commit 750f5ae with 716eb99 and 750f5ae with 2748cb4. As a result, getting one commit containing the changes of all three.



2.5 View History



Using command



 git log [<>] [<->]
      
      





You can view the commit history of your repository. There are also a bunch of options for sorting and searching for a specific commit.



Useful git log command options:





You can also sort commits by time, quantity, etc.





Here are some examples:

git log --since = 3.weeks - Show commits in the last 2 weeks

git log --since = "2019-01-14" - Show the commits made on 2019-01-14

git log --since = "2 years 1 day ago" - Show the commits made 2 years and one day ago.



You can also customize your commits output format with



 git log --format:["format"]
      
      





Formatting options for git log --format.





Example:



 git log --pretty=format:"%h - %ar : %s"
      
      





will show a list of commits consisting of a time hash and a commit message.



2.6 Working with a remote repository



Since git is a distributed hard currency, you can work not only with local but also with external repositories.



Remote repositories are versions of your project stored on an external server.



To work with external repositories, use:



 git remote [<options>]
      
      





If you cloned the repositories via the http URL, then you already have a link to the external one. Otherwise, you can add it with



 git remote add [<options>] <name> <adres>
      
      





You can immediately extract external branches using -f, --fetch (you get the names and state of the branches of the external repository). You can only configure repositories to send or receive data using --mirror [= (push | fetch)]. To get tags, specify --tags.



To view connected external repositories, use git remote with no arguments or git remote -v to view the addresses to send and receive data from the repository.



To track branches, use git branch -u <rep / br> where rep is the name of the repository, br is the name of the external branch, and branch is the name of the local branch. Or git branch --set-upstream local_br origin / br in order to indicate which local branch will monitor the external branch.



When your branch is tracking the outside, you can find out which branch (local or external) is behind or ahead and by how many commits. For example, if after a commit you did not perform git push, then your branch will be ahead of the outer one by 1 commit. You can find out about this by running git branch -vv, but first do git fetch [remote-name] (--all to get updates from all repositories) to get the latest data from an external repository. To cancel branch tracking, use git branch --unset-upstream [<local_branch>].



Use git pull [rep] [branch] to download data from an external repository. If your branches track external, then you can not specify them when performing git pull. By default, you will receive data from all monitored branches.



To upload branches to a new branch, use git checkout -b <new_branch_name> <rep / branch>.



To send data to the server, use



 git push [<rep>] [<br>]
      
      





where rep is the name of the external repository, and br is the local branch that you want to send. You can also use this entry git push origin master: dev. Thus, you upload your local master branch to origin (but there it will be called dev). You will not be able to send data to an external repository if you do not have rights to do so. Also, you will not be able to send data to an external branch if it is ahead of yours (in general, you can send using -f, --forse in this case, you will rewrite the history on the external repository). You can omit the name of the branch if your branch is tracking the outside.



To delete external branches use



 git push origin --delete branch_name
      
      





For detailed information about the external repository (addresses for sending and receiving, as indicated by HEAD, external branches, local branches configured for git pull and local links configured for git push)



 git remote show <remote_name>
      
      





To rename the name of the external repository, use



 git remote rename <last_name> <new_name>
      
      





To remove links to an external repository, use



 git remote rm <name>
      
      





3. Branching in git



Branching is a powerful tool and one of the main features of git since it allows you to quickly create and switch between different branches of your repository. The main concept of branching is that you can take off from the main line of development and continue working independently of it, without interfering with the main line. A branch always points to the last commit in it, and HEAD points to the current branch (see Pointers in git ).



3.1 Basic operations



To create a branch, use



 git branch <branch_name> [<start_commit>]
      
      





Here branch_name is the name for the new branch, and start_commit is the commit to which the branch will point (that is, the last commit in it). By default, the branch will be on the last commit of the parent branch.



Git branch options:





See git branch -h for more information | --help.



To switch to a branch, use git checkout. You can also create a branch by running git checkout -b <branch>.



3.2 Merging branches



To merge 2 branches of a git repository, use git merge.



Useful options for git merge:





The merger process.

If you did not execute new commits on the parent branch, then the merge boils down to fast-forward fast, as if you were not creating a new branch, and all changes occurred right here (on the parent branch).



If you made commits on both branches, but did not create a conflict, then the merge will take place in the “recursive strategy”, that is, you just need to create a merge commit to apply the changes (use the --squash option to avoid creating an extra commit) .

If you made commits on both branches that made different changes to the same part of the same file, then you will have to resolve the conflict and commit the merge commit.



When resolving the conflict, you need to choose which part of the changes from the two branches you want to leave. When you open a conflicting file, it will contain the following:

<<<<<<< HEAD

There will be a version of the last commit of the current branch

======

There will be a version of the last commit of the merged branch

>>>>>>> Here the name branches with which we merge



Having resolved the conflict, you must complete the merge by committing.



During a conflict, you can see what differences are in which files.

git diff --ours - Difference before merging and after

git diff --theirs - Difference of merged branch before merging and after

git diff --base - Difference with both branches before merging and after



If you do not want to allow merging, then use different merge strategies, either choosing “our” version (that is, the one located on the current branch) or choosing “their” version located on the merged branch without fixing the conflict. Run git merge --Xours or git merge --Xtheirs respectively.



3.3 Rerere



Rerere - “reuse recorded resolution” - “reuse of saved conflict resolutions”. The rerere mechanism is able to remember how you resolved a certain part of the conflict in the past and automatically correct the conflict the next time it occurs.



To enable rerere do



 git config --global rerere.enabled true
      
      





You can also enable rerere by creating the .git / rr-cache directory in the desired repository.



Use git rerere status to see which files rerere has saved snapshots for before merging.



Use git rerere diff to view the current state of conflict.



If during the merge it says: Resolved 'nameFile' using previous resolution. So rerere has already resolved the conflict using the cache.



To cancel automatic conflict resolution, use git checkout --conflict = merge so you cancel automatic conflict resolution and return the file (s) to the conflict state for manual resolution.



4. Pointers in git



git has pointers like HEAD branch. In fact, everything is very simple HEAD points to the current branch, and the branch points to the last commit in it. But for understanding it is better to imagine that HEAD indicates the last commit.



4.1 Moving Pointers



The Pro git book provides a very good example of how you can manage your repository, so I will stick with it too. Imagine that Git manages the contents of three different trees. Here, “tree” refers to a “set of files”.

In its usual operations, Git manages three trees:





Actually git provides tools for manipulating all three trees. Next, the git reset command will be discussed, which allows you to work with three trees of your repository.



Using the various options of this command you can:





By reset means moving to the specified commit. The default is --mixed.



Example 1. You made 3 extra commits each of which brings small changes and you want to make one of them, so you can use git reset --soft to move the HEAD pointer while leaving the index and working directory untouched and commit. As a result, your story will look like all the changes happened in one commit.



Example 2. You added extra files to the index and want to remove them from there. You can use git reset HEAD <files ...> for this. Or do you want the commit files to look like a couple of commits back. As I said earlier, you can reset the index to any commit, unlike git restore, which resets only until the last commit. Only with the mixed option can you apply an action to the specified file!



Example 3. You started working on a new feature on your project, but suddenly the employer says that it is no longer needed and in a fit of anger you perform git reset --hard returning your index, files and HEAD by the time you have not started working on features. And the next day you are told that the feature should still be washed down. But what to do? How to move forward, because you rolled back all 3 trees and now you can’t find them in the history using git log. And there is a way out - this is the git reflog link log. With this command, you can see where HEAD was pointing and it will move not only down the commit history, but also up. This log is local to each user.



In general, I think you can come up with a lot more examples than me. In conclusion, I’ll say that with git reset you can do magic ...



5.



  1. Pro git — Scott Chacon
  2. Git — . , ,
  3. Git Essentials — F. Santacroce
  4. Git: Version Control for Everyone (2013) — R. Somasundaram
  5. Version Control with Git: Powerful tools and techniques for collaborative software development (2009) — J. Loeliger, M. McCullough
  6. Practical Git and GitHub (2016) — D. Cruz
  7. Git in Practice (2016) — M. McQuaid
  8. Git Best Practices Guide (2014) — E. Pidoux
  9. Learn Enough Git to Be Dangerous (2016) — M. Hartl
  10. Learn Version Control with Git: A step-by-step course for the complete beginner (2014) — T. Günther
  11. Git: Learn Version Control with Git: A step-by-step Ultimate beginners Guide (2017) — D. Hutten
  12. Pragmatic Guide to Git (2010) — S. Travis
  13. Git (2016) — .
  14. A Hacker's Guide to Git (2014) — J. Wynn
  15. Practical Git and GitHub (2016) — D. Cruz
  16. Deploying to OpenShift(2018) — G. Dumpleton
  17. Git for Teams (2015) — Emma Jane Hogbin Westby



All Articles