Everything you need to know about GIT
Tags: beginners, tutorial, learning, git
Published: 2024-02-05 14:30:00

I'm sure you can imagine the importance of versioning code, so that we can **revert changes** and **recover lost data among other possibilities**.
I bet you know someone _(not me hehe)_ who does version control with their files by creating copies of them with increasingly creative names...

![GIF that simulates the duplication of a file with different names. Example: article, article-finalversion, etc.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sffhjyl41pfnjklivnsv.gif)

This was probably how anyone would do version control with their code as well before 1972, with the release of **SCCS _(Source Code Control System)_**, one of the first **centralized** version control software ever released.

But we're not here to talk about SCCS, what really interests us now is **GIT**, the **distributed** open-source version control software that celebrates its 20th anniversary next year _(07/04/2005)_.

## Table of contents
* [1. What's GIT?](#chapter-1)
* [2. How does GIT work?](#chapter-2)
* [3. Installing GIT](#chapter-3)
* [4. Configuring GIT](#chapter-4)
* [5. Starting a local repository](#chapter-5)
* [6. Working with GIT](#chapter-6)
* [7. Knowing branches](#chapter-7)
* [8. Syncing with the remote repository](#chapter-8)
* [9. Conclusion](#chapter-9)
* [10. References](#chapter-10)


## 1. What's GIT? <a name="chapter-1"></a>
GIT is an open-source **distributed** version control system launched in 2005 and developed by Linus Torvald _(yh, the Linux kernel creator)_.

With GIT, we can locally **control the versions of a project** _(in the working folder)_ and synchronize all changes to a remote repository _(on GitHub, for example)_.


## 2. How does GIT work? <a name="chapter-2"></a>
Imagine a **physical** file cabinet where there's a folder with all the project files. Whenever someone needs to manipulate a file, they have to pick it up, **removing it from the folder and returning it to the folder after finishing**. So, it is **impossible** for two people to work on the same file, completely avoiding any possible conflicts.

**BUT THAT'S NOT HOW GIT WORKS!** _(thank God)_

This is how a **CENTRALIZED** version control system works, in which the user needs to **"check-out"** and **"check-in"** files, i.e. whenever someone needs to work on a specific file, they need to check-out that file, **removing it from the repository**, and then check-in the file once the work is done, **returning it to the repository**.

![GIF that simulates the operation of a centralized version control system.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/26a9j0m4pcu5prb0kauo.gif)

In a **DISTRIBUTED** system like **GIT**, it's possible for several people to access files from the same remote repository. Whenever someone needs to manipulate a file, they can simply **clone** it _(or clone the entire repository)_ locally to their machine, and then send the modifications back to the remote repository. This makes it possible for **several people to work on the same project**, even manipulating the **same files**.

![GIF that simulates the operation of a distributed version control system.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nb0x0u9kc6m7z2hz7zfj.gif)

This is what allows the distribution of large **open-source** projects, with people from different parts of the world working on the same project, managing modifications and possible conflicts _(yes, merge conflicts can happen here)_.


## 3. Installing GIT <a name="chapter-3"></a>
**GIT** is available for the main operating systems _(Windows, Linux, MacOs...)_ with a very simple installation process, which can be done by **command line** or through the **official installer** at [git-scm.com](https://git-scm.com/).

### 3.1 On Windows
To install GIT on Windows, simply go to the official website and [download the installer](https://git-scm.com/download/win). Then just **follow the instructions** and everything should be fine then we'll be able to use the GIT commands in our terminal.

### 3.2 On Linux
For Linux, we can install **GIT** using the **command** below:

```bash
sudo apt install git-all
```

By doing this, **GIT** must be ready to run in our terminal.

### 3.3 On MacOS
For Mac, the easiest way to install **GIT** is to install [Homebrew](https://brew.sh/) and then run the **command** below in the terminal:

```bash
brew install git
```

Then **GIT** must be ready to run in our terminal.


## 4. Configuring GIT <a name="chapter-4"></a>
After installing, it's important to **configure GIT** with the **commands** below:

```bash
git config --global user.name "[username]"
# e.g. John Doe
```

```bash
git config --global user. email "[email@email.com]"
# e.g. johndoe@email.com
```

>Also, it's possible to configure specific users for certain local repositories by removing the `--global` tag.


## 5. Starting a local repository <a name="chapter-5"></a>
With **GIT** configured, we can **start our local repository**. To do this, we can **start a new repository from scratch** or **clone an existing remote repository**.

### 5.1 Starting from scratch (git init)
To start a new repository, simply navigate to the desired repository **root folder** and run the command below:

```bash
git init
```

![Screenshot of a Linux terminal running the "git init" command and displaying the result.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iw35xe9jwrwzo937to5b.png)

By doing this, a `.git` directory will be created inside the project folder, which will be **responsible for the version control** in the working folder of this local repository.

### 5.2 Cloning an existing repository (git clone)

Cloning an existing remote repository is as easy as starting a new one from scratch. To do this, simply use the `git clone` command, with the **remote repository URL** to be cloned inside the folder where we want to download the repository:

```bash
git clone [repository-url]
```

![Screenshot of a Linux terminal running the "git clone" command and displaying the result.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nlt8rtua4ri7kpnna5el.png)

Then the entire repository must be **cloned** to our local machine and **automatically linked to the related remote repository**.

>With a cloned repository, we will no longer need to use the `git remote` command in the future.


## 6. Working with GIT <a name="chapter-6"></a>
Within our **local repository**, we can create the files needed for our project, but they **won't be automatically synced by GIT**, we'll need to report it when there are any changes to be versioned.

Thus, we can **manipulate** the files as we wish and **after finishing the desired changes**, **send the updated files to GIT**.

To do this, it is important to understand that there is a **3 stage infinite flow** _(yes, infinite)_ in version control: 

```
              MODIFY -> STAGE -> COMMIT
```

* **MODIFY:** The first stage of version control, this is where we find the **files that have changed** compared to the last available version.

* **STAGE:** The second stage of version control, this is where we place **modified files that we want to add** to our next commit.

* **COMMIT:** Final stage of version control, when we **confirm the changes**, sending the modified files that were in stage to the local repository.

After committing the modified files, we have a **new version** available in the local repository, which can again receive updates, going one more time to _"modified"_, then placed in _"stage"_ and, again, being _"committed"_, confirming a newer version and so on _(and therefore, "infinite" lol)_.

>It's important to notice that a commit doesn't overwrite the old version of the modified files, but includes the new version with a pointer to the last version, thus keeping track of the versions of each file tracked by GIT.

### 6.1 Adding and commiting (git add and git commit)
Although it might sounds complex, performing the version control flow is **very simple**. Since the desired modifications are completed, we **add the modified files that we want to commit on stage** with the command below:

```bash
git add [filename]
```

>`git add -A` -> adds all modified files to stage at once.
>
>`git add *.[extensão-do-arquivo]` -> adds all modified files with the specified file extension to stage at once (e.g. `git add *.html`)

We can check our current local repository **status** at any time using the `git status` command:

![Screenshot of a Linux terminal running the "git status" command and displaying the result.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e28lg7wym8s0woytgzqz.png)

Note that when we run `git status` inside the repository **after creating a new file**, the new file is shown as _"Untracked"_. This means that this file is **fresh new** and still needs to be added to any commit in order to be **tracked** by **GIT**.

>It's possible to let GIT ignore specific files or folders within the repository. To do this, we can simply add a file to the root folder called `.gitignore` and write the name of the files or folders that should be ignored inside it.
>
>CAUTION: Ignored files and folders will no longer appear to the GIT track, not even as "Untracked". To reset the tracking, simply delete the desired names from the `.gitignore` file.

To include a file, we can run the `git add` command with the name of the file that we want to add _("index.html" in this case)_:

![Screenshot of a Linux terminal running the command "git add" and then "git status", showing the results.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pz1hnk9gpnxtv2s8xort.png)

This way, by re-running `git status` we can see that the new file has been added to _"stage"_ and is **finally ready to be sent in our next commit**, which can be done using the command below:

```bash
git commit -m "[descriptive-message]"
```

>Commits have unique IDs (hash codes) and are **IMMUTABLE**, i.e. they cannot be modified once they have been confirmed.
>
>`git commit -a` -> performs a direct commit, adding all the modified files to stage and committing them.

**After successfully committing a file**, when running `git status` we notice that **there are no more modified files to be uploaded**, since all the modifications were effectively saved in our local repository with our last **commit**.

![Screenshot of a Linux terminal running the command "git commit" and then "git status", showing the results.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ryqqs4mi46hxtuex199i.png)

Also, it's possible to **verify the changes made** by reviewing the repository's commit log, using the `git log` command, which shows some metadata of all the commits made, such as the hash code, branch, author, date, etc.

![Screenshot of a Linux terminal running the command "git log" and displaying the results.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ljvj9se6srxslsu8vsj7.png)

This whole process can be repeated to add new files that are needed for our project, modify them and send them to the local repository by making new commits.

![GIF that simulates multiple commits in a GIT branch.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2uez3henix8kchperuw8.gif)

>`git log -N` -> displays a log with the last N commits.
>
>`git log [branch-A] [branch-B]` -> displays a log of commits that are in "branch-B" but not in "branch-A".
>
>`git log --follow [filename]` -> displays a log of commits that changed the specified file, even if it has changed its name.
>
>`git diff` -> lists the changes made compared to the latest available version in the repository.
> 
>`git diff [nome-do-arquivo]` -> lists the changes made to the specified file in relation to its last available version in the repository.

### 6.2 Undo changes before and after commiting
**Before a commit is made**, any changes made to the local repository can be undone or changed, but **once the commit is made, it cannot be changed**. This is because commits are **immutable objects**, meaning that it is impossible to edit or change the data in a commit.

However, it is **possible to make new commits** that undo changes, or correct incorrect information in previous commits. In either way, we can use one of the commands below:

```bash
git checkout -- [filename]
# Discards changes made to the local file before the commit (irreversible action)
```

```bash
git reset --hard HEAD
# Discards changes made to a file that is in stage (before the commit)
```

```bash
git reset --hard HEAD~1
# Discards the last commit made in the local repository (only the last commit)
```

```bash
git commit --amend
# Creates a new commit, replacing the last commit made in the local repository
```

```bash
git revert [commit-hash]
# Creates a new commit, reverting the changes of the specified commit
```


## 7. Knowing branches <a name="chapter-7"></a>
A **branch** is nothing more than a **ramification** of the repository, and so far all actions have been performed on the branch `master/main'`.

>By default, the first branch created in the repository is the `master/main`, which is the main branch of the repository.

### 7.1 Why use branches?
It may not seem like much at first, but **branches give enormous power to the project development**.

Imagine we're developing a web platform, and we want to **test a new feature**, but our repository is already **hosted or shared** with other people, and any problematic change could cause a bad experience for them. What can we do?

If you've been thinking about **copy and paste** the project folder, creating a new **"test version"**, you're right! Well, almost...

With GIT, we can do something similar with branches. Since they are **branches**, we can simply **create a new branch called "test"**, and thus have a version of our project in a completely **isolated branch**, ready to be flipped **without risking the main branch**.

![GIF that simulates the creation of a new branch with new commits.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/szc081nndoi18vklty5r.gif)

### 7.2 Creating branches (git branch)
Creating a **branch** means creating a parallel copy of the repository that can be worked on independently, without affecting the `master/main` branch. To do this, we can simply run the command below:

```bash
git branch [branch-name]
```

>Running the `git branch` command without a specific branch name must display the list of available branches in the repository, with a "\*" marking the branch that's currently in use.

Before running the `git branch test` command, the `git branch` command only returned the `master` branch.

![Screenshot of a Linux terminal running the "git branch" command, creating a new branch, and displaying the results.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vqpdrtvhze9qhr31ewbh.png)

After creating a new branch, we can run the command below to switch between the available branches:

```bash
git checkout [branch-name]
```

After running the `git checkout test` command we can see that the **active branch is switched**. From that moment on, **all committed information will be sent to the `test` branch** of the repository, without affecting the branch `master/main`.

![Screenshot of a Linux terminal running the "git checkout" command, switching the active branch, and displaying the results.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/egau7y81leig8za36myi.png)

>It's possible to create as many branches as we need, and we can interact with the existing branches using the commands below:
>
>`git checkout -b [branch-name]` -> creates a new branch with the given name and directly switches to it.
>
>`git branch -d [branch-name]` -> deletes the specified branch.
>
>`git branch -m [new-name]` -> changes the name of the current branch to the given name.

### 7.3 Merging branches (git merge)
**When finished working** on a **different branch**, and we're sure that the changes we've made haven't caused any problems in the project, we can **merge** the current branch in the `master/main` branch, **applying all the changes from the current branch to the main branch of the repository**.

To **merge** branches, we need to **switch to the branch that will receive the changes** and run the following command:

```bash
git merge [branch-name]
# Merge the given branch into the active branch
```

Here, since we are on the branch `test`, **we should switch to the branch `master`** using the `git checkout` command, and then run the `git merge` command with the name of the branch we want to merge _("test", in this case)_.

![Screenshot of a Linux terminal running the command "git checkout", switching to the master branch and then "git merge", showing the results.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a6tqap1uu5gjipkety6w.png)

By doing this, all the work done on the branch `test` _(in this case, the creation of the `style.css` file)_ will be merged in the branch `master`.

![GIF that simulates the merge process between two branches.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3hs17m005ogm0qlbedjy.gif)

### 7.4 Merge conflicts
Merging different branches with `git merge` can lead to some **conflicts** in cases where **one or more files have been changed on the same lines** and the merge cannot be done **automatically**.

![Screenshot of a Linux terminal running the "git merge" command, which returns a conflict warning.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9mwedudsff1yf8kpzguc.png)

When this happens, we can run the `git status` command to check **which files** are in conflict.

![Screenshot of a Linux terminal running the "git status" command after the merge conflict alert.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hcfpmta2yiy1xpyc729u.png)

We'll need to **solve the conflicts** before proceeding with the merge, either by defining which changes should take place, or by reviewing the changes so that they are mutually compatible. To do this, **GIT will insert markers into the conflicting files** to help with the resolution.

![Screenshot of the conflicting file opened in a text editor, showing the markers created by GIT to help resolve the conflicts.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a0e727le3ogmhtuq0g7w.png)

After solving the conflicts, we just need to put the modified files back on stage, commit the new no-conflict versions, and run the `git merge` command again, which must successfully merge without any problems.


## 8. Syncing with the remote repository <a name="chapter-8"></a>
We already know that it's possible to **connect our local repository to a remote repository** and synchronize all our work remotely, keeping it **up to date**.

To do this we'll need to run the `git push` command, which **sends all commits from the local repository to the remote repository**, but first we need to **configure a remote repository.

### 8.1 Configuring the remote repository
Starting a *remote repository* is quite simple. Here we'll use **GitHub** to do it.

First, we need to **start a new empty repository** in our GitHub account _(just by choosing a name and clicking "Create repository")_:

![Screenshot of the repository creation page on GitHub.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ug1aw5eq15du36vd1hri.png)

Next, we need to **configure the relationship between the remote repository and the local repository** by running the following command inside our local repository:

```bash
git remote add origin [remote-repository-url]
```

![Screenshot of a Linux terminal running the "git remote" command and displaying the result.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rhrlfg0f0pgljl5b18vl.png)

>`git remote -v` -> shows the URL of the remote repository that's actually connected to the local repository.

With the remote repository properly **connected**, we need to **change the name of our local branch `master/main`** to "main" with the command `git branch -m main` _(ignore this step if your local branch is already called `main`)_:

![Screenshot of a Linux terminal running the "git branch" command, renaming the "master" branch to "main", and displaying the result.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f0al7joro01pch0ac124.png)

>It's important to keep the main branch of the local repository with the same name as the main branch of the remote repository to which we are pushing.

Finally, after completing the above steps, we can **sync** our local repository with the remote repository for the first time using the following command:

```bash
git push -u origin main
```

![Screenshot of a Linux terminal running the "git push" command, which requires GitHub authentication.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fvkdyis8y989xron52nc.png)

When we run the `git push -u origin main` command, we may need to enter our **GitHub credentials** _(user and access token)_.

>If you don't know what a **GitHub access token** is, or you don't have one access token set up, [click here](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-personal-access-token-classic).
>
>We can also work around this by configuring **authentication using the GitHub CLI**. Find out how [by clicking here](https://docs.github.com/en/get-started/getting-started-with-git/caching-your-github-credentials-in-git).

After **authenticating**, `git push` should run successfully, synchronizing all commits in the local repository with the remote repository.

![Screenshot of a Linux terminal showing the continuation of the "git push" command after GitHub authentication.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vp7q4htszkzrfc6vepcl.png)

![Screenshot of the remote repository on GitHub after receiving the "git push" with the new files.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2rqfhi6s6jh369tseh5y.png)

### 8.2 Git push after the first time (git push)
After going through all the above steps, **new syncs** can be done using the `git push` command alone, without any additional parameters, like shown below.

![Screenshot of a Linux terminal running the "git status", "git commit" and "git push" commands, performing a new commit and pushing the updates to the remote repository.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aoatcspiwosywih07nsm.png)

![Screenshot of the remote repository on GitHub after receiving the new updates.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jskxpghboi62mqw2qcb2.png)

>In this case, the authentication needed to run the command `git push` was bypassed using the **GitHub CLI**. You can find out how by [clicking here](https://docs.github.com/en/get-started/getting-started-with-git/caching-your-github-credentials-in-git).

### 8.3 Updating the local repository (git pull)
With a **distributed** remote repository, it's possible for changes to be made **remotely** _(directly in the remote repository)_, causing our local repository to become **outdated**.

Thinking about that, it's very important to **update the local repository** and sync any changes that we got in the remote repository, **ensuring that the local project is always with the latest version available in the remote repository**. To do this we can run the following command:

```bash
git pull
```

Imagine that a **new file** `README.md` has been created **directly in our remote repository** and because of that our local repository is now outdated.

![Screenshot of the remote repository with a new README.md file added remotely.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hccw00vyr7j6lcfm02d2.png)

Within the local repository we can **synchronize** the changes from the remote repository using `git pull` as mentioned above.

![Screenshot of a Linux terminal running the "git pull" command, updating the local repository with the new changes from the remote repository.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/it9ylx1imd94yey5d0wd.png)

>The first 7 lines returned when we run the `git pull` command are the return of the `git fetch` command. In other words, if we run the `git pull` command without first running the `git fetch` command, GIT will run both together to retrieve the updates from the remote repository and synchronize them to the local repository.
>
>`git fetch` -> fetch updates from the remote repository, but does not sync the local repository (requires `git pull`).

## 9. Conclusion <a name="chapter-9"></a>
All this leads us to the certainty that GIT is a version control system that is necessary in the daily life of a programmer, and knowing its main commands and uses can be the turning point in our technical seniority. Finally, with the local and remote repositories synced and updated and with everything we've learned so far, we're ready to move forward with the practicality of this awesome version control system.

## 10. References <a name="chapter-10"></a>
* [GIT's official documentation.](https://git-scm.com/docs/git#_git_commands)
* [[PT-BR] GIT: Mini course to get you started (learn in 45 minutes) - Codigo Fonte TV](https://youtu.be/ts-H3W1uLMM?si=-hKGkUmwgT2lZJwy)