---
title: Git commands I wish I knew earlier as a developer - Level Up Coding
source: https://levelup.gitconnected.com/git-commands-i-wish-i-knew-earlier-as-a-developer-a5f9f47d5644
---

![Stephen David-Williams](1_lnDZ3VmlTfvWPYJLRsPz0w_12c5db92f7f94401b08461ba0.png)

![Level Up Coding](1_5D9oYBd58pyjMkV_5-zXXQ_886f10edd62e46c3856865173.jpeg)

![](0_4BB20zYwx79LGXfl_14ee7c5f85e14f24856e8c80c27a050.jpg)
Photo by [Roman Synkevych](https://unsplash.com/@synkevych?utm_source=medium&utm_medium=referral) on [Unsplash](https://unsplash.com/?utm_source=medium&utm_medium=referral)

## What is Git?🤔💭

Git is an open-source tool that allows you to version control your code repositories and collaborate with other developers. This means you can

- save and track different versions of your repositories
- coordinate changes across different teams without impacting the work of other collaborators
- share local copies of the same codebases as other developers when working offline
- isolate new fixes and features in development without impacting production

## List of git commands📝

Git commands can be used for

1.  Starting your project🚀
2.  Managing branches🌿
3.  Tracking and committing changes👀💾
4.  Synchronizing your project💫
5.  Reviewing and debugging changes🤓

Let’s walk through some of these using the terminal. I will be using [Git Bash](https://git-scm.com/) for the code walkthroughs which you can [install from here.](https://git-scm.com/downloads)

**Just a heads up:** Not all the code examples used in this article follow a chronological order, so at times you may need to navigate between the different sub-topics for context!

## Table of contents📃

· [What is Git?](#2096)
· [List of git commands](#a703)

· [1\. Starting your project🚀](#191e)
· [git init](#b5d9)
· [git config](#b1d4)
· [git clone](#ecd1)

· [2\. Managing branches🌿](#db44)

· [git branch](#d8fd)

· [git checkout](#e215)

· [Switch from one branch to another existing branch](#68bd)
· [Create and switch to a new branch](#2b84)

· [git merge](#faa5)
· [Merge a branch to your current branch](#b726)
· [Reverse a merge operation by rewriting the change history](#8416)
· [Reverse a merge operation by creating a new commit](#8f7a)

· [git rebase](#4115)
· [Perform a basic rebase](#feb8)
· [Continue rebasing after resolving conflicts](#54b3)

· [3\. Tracking and committing changes👀💾](#3d2d)
· [git add](#8732)
· [Stage a single file](#4d57)
· [Stage multiple files](#b868)
· [Stage all files with the same format](#0dbd)
· [Stage all new and modified file, but NOT deleted files](#e769)
· [Stage one change at a time](#381e)
· [Stage all new, modified AND deleted files in the local repository](#93d9)

· [git status](#96af)
· [What does git status do?](#0b44)
· [Different types of files in git status](#d455)
· [→ Untracked (U)](#d439)
· [→ Added (A)](#a939)
· [→ Modified (M)](#d511)
· [→ Deleted (D)](#0b56)

· [git commit](#8652)
· [Save changes with a simple message](#f37f)
· [Save all the changes](#62e7)
· [Commit specific files only](#727d)
· [Save the commit with a title and a message](#2740)
· [Use editor to add a commit message](#2e2c)
· [Amend the previous commit](#2df4)
· [Stage and commit a change in one line](#6e5f)

· [git stash](#4ff1)
· [Save changes in a temporary location](#1148)
· [Save a stash with a specific name](#263f)
· [List the stashes](#a3ad)
· [Apply a stash](#31a3)
· [Apply a specific stash](#7b87)
· [Delete a stash](#96c6)
· [Apply and then delete a stash](#1dcd)

· [git rm](#c595)
· [Remove a single file](#842b)
· [Remove multiple files](#9ebc)
· [Remove a directory](#135e)
· [Remove staged files with the same format from the index](#9e1c)
· [Remove a file from the staging area only](#4eda)
· [Remove a file with force](#93bf)

· [git restore](#1d9e)
· [Restore a single file](#1b12)
· [Restore multiple files](#2074)
· [Restore files from the index](#0c6c)
· [Restore all files from the working directory](#7689)

· [4\. Synchronizing your project💫](#6c00)
· [git push](#b22d)
· [Send commits to the default remote branch](#3cea)
· [Send commits to a specific branch](#bfb9)
· [Send commits to a branch with force (unsafe)❌](#41b8)
· [Safely send commits to a branch with force✅](#020a)
· [Push all local branches to remote server](#7673)

· [git pull](#a6d9)
· [Pull the commits from the default remote branch to the local branch](#cdbc)
· [Pull from a specific remote branch to the current local branch](#e2af)
· [Pull changes from all remote branches](#e306)
· [Pull from remote branch WITHOUT merging with local branch](#b8d8)
· [Rebase while pulling](#f83b)

· [git remote](#78f6)

· [git cherry-pick](#7eb3)
· [Cherry pick a single commit](#7fa8)
· [Cherry pick multiple commits](#930b)
· [Cherry pick a merge commit from the source branch](#9026)

· [5\. Reviewing and debugging changes🤓](#59cf)
· [git log](#11f8)
· [Show each logged commit on a single line](#f392)
· [Filter the log commits by time period](#82fc)
· [Filter the log commits by date range](#ca9c)
· [Filter the log commits by one author](#d640)
· [Filter the log commits by multiple authors](#04dd)
· [Show the differences between a commit and its parent](#3aa4)

· [git reflog](#2b23)
· [View the change history of your repo](#39c2)
· [View the change history of a specific branch](#b312)
· [Limit the changes shown by a specific number](#0313)
· [View the change log for all references](#c712)
· [View each change & commit entry on a single line](#10b8)
· [View each change entry in a short format](#e88c)
· [View each change entry in a medium format](#0afb)
· [View each change entry in full detail](#5c88)

· [git diff](#9aff)
· [Show the changes between the modified files](#f994)
· [Show the names of modified files](#cffc)
· [Show the name and statuses of modified files](#49a9)
· [Show useful stats about each modified file](#e11c)
· [Compare different versions of some work](#6bcc)

· [git reset](#4d42)
· [Reset the commit history only (soft reset)🟢](#2941)
· [Reset the commit history and staging area only (mixed reset)🟡](#fad1)
· [Reset the commit history, working tree and staging area (hard reset)🔴](#fec9)

· [git revert](#26f2)
· [Revert the latest commit](#c2da)
· [Revert a specific commit](#b4c0)
· [Revert a merge commit](#7898)
· [Revert the last n commits](#7de3)

· [git grep](#32d5)
· [Search for a text string in your repo](#2884)
· [Search for a text string within a subdirectory](#339a)
· [Search for a text string within a specific branch](#a480)
· [Search for a text string within the staging area](#5c8c)
· [Search for a text string within an untracked file](#5483)
· [Search for a term with its line number](#f0ab)
· [Search for the number of matches for a term](#637c)

## 1\. Starting your project🚀

Before you can track changes, it’s crucial to set up (or initialize) your Git environment correctly

## git init

Starts a new project by creating a new Git repository

To use this,

- navigate to your project’s directory like so:

<a id="0fb0"></a>cd {your-directory-path}

- create a new folder and navigate into it:

<a id="94d6"></a>mkdir {new-folder-name}
cd {new-folder-name}

- Initialize a new repository by typing:

<a id="beb7"></a>git init

## git config

Sets up information related to a user e.g. username, email, file format etc

Use this code in the terminal and replace the placeholders with the relevant details:

<a id="e0b2"></a>git config user.name "{your-username}"
git config user.email "{your-email@example.com}"

## git clone

Copies a remote repository from a remote server (usually a GitHub repository) into the local repository.

This allows you to clone a repository into your local environment so that you can freely add, edit or remove features without affecting the live repository.

Let’s clone a repository from GitHub. Before we do that, let’s see what the **my\_test\_project** directory looks like:

At the moment only the **.git** folder resides, which means the directory is empty. We will clone a repository into this location.

I’ve created a dummy repository in GitHub for this example called **my\_test\_repository**:

To clone this into our local machine,

- copy the URL of the repo (which can be found by clicking the green `<> Code` button )
- Add the URL to the `git clone` command, like so:

<a id="82c2"></a>git clone {repository-url}

Here are the results:

![](1_5x7iz6ELFo27Y-e2u5yhLg_2a5c4044de4546499f90b02f5.png)

![](1_aoSuDtJOps5rxLPH24eDeg_68047d7150a64c8293155bcfa.png)

The **my\_test\_repository** repository has been successfully copied into the local repository!

## 2\. Managing branches🌿

A branch is a personal workspace for a specific project. Within a team, each programmer would have their branch (or feature branch), where they build their features, edits, and other changes to contribute their creative efforts to the tool they’re building. Here’s how to handle multiple branches in Git:

## git branch

Used for displaying, creating and deleting branches.

When collaborating on the same project, this command allows each developer to separate their work from others to avoid accidentally changing, mixing and overwriting the work of others.

To check the branches in your repository, simply navigate into the repo and type `git branch`, like so:

<a id="86e1"></a>cd my\_test\_repository
git branch

![](1_ZtPkh3Ua5zLHYvn88xRj-w_607fa7555ce54f4295eee1864.png)

To create a new branch, type `git branch` followed by the new branch’s name:

<a id="4eb4"></a>git branch {new-repo-name}

In our case, we created a new branch called **dev_branch** in the **my\_test\_repository** repo (from the previous example) because it only had one branch named **main**:

![](1_OLl6P-BIp_k0PzSww4sVRQ_04def4028f8941b082a1d8f39.png)

## git checkout

Switches branches and/or restores working files

## Switch from one branch to another existing branch

<a id="2d49"></a>git checkout {branch-name}

![](1_rMQg7cAGMCx5ypy-3X6mVw_a1ba3c99962f4f5a818064108.png)

In **Git Bash**, the branch names will also change once you switch to different branches, like the example above

## Create and switch to a new branch

You can also create a new feature branch and switch to it immediately using:

<a id="e1c6"></a>git checkout -b {new-branch-name}

![](1_hVe8iyabw5SVIA9EqznWuA_ab9fd6d303ce482c849b88e17.png)

Here we create a new branch called **staging_branch** and switch to it. Each of these branches is still within the local repository so we would need to send any changes made locally to the remote repository to ensure they’re reflected in GitHub too.

## git merge

For merging one or more branches in your current branch

In Git, merging branches is combining the changes made across different branches into one branch. This approach stores the commit history of both branches.

## Merge a branch to your current branch

<a id="c3a3"></a>git merge {branch_name}

![](1__QksHC49to3p2vju_1JU7w_0b96e022a5274880ac7d68761.png)

## Reverse a merge operation by rewriting the change history

<a id="c671"></a>git reset --hard {merge\_commit\_hash}

This will undo the merge by rolling back the previous commit before the merge operation, therefore deleting commits in the process. It is usually recommended to use the `git revert` approach over this one to avoid data loss or impacting the work of other collaborators.

Let’s reverse a merge operation by performing a hard reset. Pay attention to the commit hash, which serves as the latest commit displayed in both screenshots:

![](1_lB1zeNEUS0hq3fYKhtoUMg_03bf63adcf0545ee82cf727a5.png)

![](1_JisO3YH7wfjEVvkmcoHuxg_67583a36fa614d87ac93e11b2.png)

Once we apply the `git reset —hard` command, the systems look like this now:

![](1_GwYNQAt1t-7TRAgi4nDkOw_12368f558237448aaa19fb165.png)

![](1_XVPKN2yL2L3UaxT52xZacg_ae6f3a8d01e64660a8b069f41.png)

The command managed to reverse the merge operation while deleting the commits responsible for it in the first place.

## Reverse a merge operation by creating a new commit

<a id="16de"></a>git revert -m {parent\_number} {merge\_commit_hash}

This will undo the merge by creating a new commit that rolls back to the branches to the previous state before the merge operation, without modifying the commit history in the process.

The `parent_number` is the number of the branch you want to merge into (i.e. the main branch), which is usually a **1,** and 2 for the merge operation’s target branch.

Let’s undo the merge operation safely, observe the commit hash in both screenshots which serves as the change responsible for merging the branches together:

![](1_jjhYBWHKuNbJ7MQIr2sGtg_d886f10bfab24ee58c19bcd44.png)

![](1_GkwA7vSkROa3Sshr2ZSC9w_e2102a1b058340b281d5caabd.png)

Using the `git revert` command by typing `git revert -m 1 HEAD@{0}` into the terminal opens up an interactive session via Vim like so:

![](../_resources/1_SWTmeSiOCSTSEc30Feyd4g_8ac6b8c8317b43e58de684f2d.png)

The custom message provided by the system should suffice so we can advance by typing `Esc` \+ `:wq` and then hit `Enter`, which creates the new commit that reverses these changes.

Here are the results:

![](../_resources/1_z-myHXmzS30rAMz0jX3NBg_ed84d30d57644194a13cdf309.png)

![](../_resources/1_LWKr3u4Bxnxr6bGKSC-KhA_9757793c99a94335a067dd3d3.png)

Instead of deleting the previous merge commit, Git has created a new one to roll back to the previous state before the commit, which safely preserves the commit history and avoids impacting the work of other collaborators on the same repository as us.

## git rebase

Moves or combines a sequence of commits to a new base commit

In Git, rebasing means integrating the changes made on one branch into another, similar to merging. But instead of maintaining the commit history of the two branches, rebasing rewrites the commit history to produce a cleaner, linear progression of changes.

So rebasing is appending all the commits of one branch to the commits of another one. This makes the commit history of the principal branch a single, clean line of commits but may be difficult to debug if conflicts occur.

⚠️**Warning:** Use with caution as other developers may be working on the branches you’re rebasing with. Resolving conflicts caused by rebasing can be a painful experience (trust me…I know what I’m talking about)😖

## Perform a basic rebase

<a id="4db9"></a>git rebase {branch_name}

## Continue rebasing after resolving conflicts

<a id="1be2"></a>git rebase --continue

Let’s try to rebase the **dev_branch** branch onto the **main** one.

First, we have a few conflicts that require resolving:

![](../_resources/1_VUrluUdUhWaJqxoPVxIpBA_fd7c967abd5946c1a3f6e2282.png)

![](../_resources/1_k9c0ryFGxsS_7fq1ROScew_f7b6b1888ea64aeb99d3b63a6.png)

In our case, we **accept both changes** and continue with the rebasing operation by entering `git rebase —continue` in the terminal, which results in the Vim editor opening up, like so:

![](../_resources/1_MegmVScyF-O3sKwoGXPDDg_a6a6152d45ab4b5399e73f1cc.png)

Doing this successfully rebases the **dev_branch** branch — compare the commit hashes in the screenshot to observe how the rebase has integrated the branches to create a clean, linear commit history:

![](../_resources/1_d0YyKp49QjHkKR12vSZl_w_61dd8ed6499f49ba97ebcd0b5.png)

![](../_resources/1_TmdivAaksKM0SKFGG2PSvQ_5db8af569f1141e9aaec4dfca.png)

![](../_resources/1_-jqyARGaXxlIHfVSPu3izA_f732540eeb6147dfa391305d2.png)

## 3\. Tracking and committing changes👀💾

This is where you can monitor and save changes made in your Git repositories

## git add

For adding changes made to files and folders in a repository to the staging area

If I opened up **my\_test\_project** folder in VS Code, here’s what it looks like after cloning to the local machine:

![](../_resources/1_o73sVG5K1Ub3oH92qkIfcA_d7176d0745af47b5b0c03c87e.png)

The [README.md](http://readme.md/) file is the only file in the repository at the moment. Let’s change that.

We will add a new file called **dummy\_file\_1.md** with some placeholder text:

![](../_resources/1_MrIP6bxHm9IZLcDML2BGMA_03b4c2eef65245678b86efa24.png)

Once we enter the **Source Control** tab on VS Code we notice two things:

- the file name has turned into green text
- there is a letter **U** in green text labelled next to the file name under **Changes**

This indicates that the file has not been added to the staging area, and therefore is **untracked**.

## Stage a single file

<a id="7852"></a>git add {file-name}

To track the changes made to the **dummy\_file\_1.md** file, we type this command in the terminal:

<a id="5d30"></a>git add dummy\_file\_1.md

This changes the U to an A, which stands for **added** because the **dummy\_file\_1.md** has officially been *added* to the local repository:

![](../_resources/1_u8C3-GgfhCmSBi6txxV0hA_6823d1ccbffa4d0487c98eebc.png)

## Stage multiple files

<a id="48d7"></a>git add {file-name-1} {file-name-2}

Two more files have been added to the **my\_test\_repository** repo:

- dummy\_file\_2.md
- dummy\_file\_3.md

Here’s what they look like in VS Code:

![](../_resources/1_a2bVTwulbxgzKgh5YVyNxQ_f7a98a3ac7b140c491095a21a.png)

Both files are now labelled as untracked, while the dummy\_file\_1.md is sitting safely in the staging area. This is what the same information looks like in the terminal when we use `git status`:

![](../_resources/1_MgVTqbQOeKP3at2v5q9AuA_3add1b23a1634c939f6b90b5d.png)

To add the files to to the staging area, we type:

<a id="f3bc"></a>git add dummy\_file\_2.md dummy\_file\_3.md

…which should make the staging area look like this:

![](../_resources/1_qh7FPxOmgR5p3TG79Ptorw_40d364b8ae9e418688ec3d305.png)

## Stage all files with the same format

You can stage multiple files with the same file format within the local repo using:

<a id="6b7a"></a>git add *.{file_extension}

In our **my\_test\_repository** repository, we create 3 JSON files and 2 text files:

- animal_facts.json
- shopping_list.json
- to\_do\_list.json
- poem.txt
- recipe.txt

![](../_resources/1_2v0VG75UdyaADv8dBEmB6Q_b41e3b9b327443c082506cc6c.png)

Let’s only add the JSON files to the staging area:

<a id="01bc"></a>git add *.json

Here are the results:

![](../_resources/1_gkmT7pvAXldvIF5fvvdBkw_dd9704b4a1a542edabcebc07c.png)

The files underlined in green are the JSON files that were successfully added to the staging area, while the files underlined in red show the text files that were not included in the process.

## Stage all new and modified file, but NOT deleted files

Here’s how to add every newly created and modified file from the local repository (excluding all deleted files):

<a id="5e79"></a>git add .

**Note:** This will not stage files found in the .gitignore file, plus it won’t stage deleted files

Let’s do 3 things:

- create a new file (called **planets.csv**)
- modify an existing file (**dummy\_file\_2.md**)
- delete one staged file (**shopping_list.json**) *and* one untracked file (**poem.txt**)

Results:

![](../_resources/1_jW1HbMrNfML-ukzxYVHcjA_e20952dc7c8d4efea58b58740.png)

We notice a few things:

- the new **planets.csv** file created is marked as **untracked (U)**
- the existing file **dummy\_file\_2.md** is marked as modified (M)
- the previously staged file **shopping_list.json** is marked as deleted (D)
- there is no record of the **poem.txt** file on this list even though it was deleted the same moment as the **shopping_list.json** file

By running `git add .` in the terminal, the new results are:

![](1_IGgksTXaXVOLkkm70aRYgg_9ab3d4cd3bb5424c8946af315.png)

Now we see that the:

- new **planets.csv** file created is now marked as **added (A)**
- modified **dummy\_file\_2.md** file is now marked as **added (A)**
- **shopping_list.json** file is no longer found on this list because it was deleted in the previous step and therefore not included in the staging process
- **poem.txt** file is still nowhere on the list, as it is also a deleted file (on top of it previously marked as untracked)

## Stage one change at a time

In Git, patching allows you to review each change made one at a time. This approach allows you to accept, reject or modify each change before staging them.

<a id="5e15"></a>git add -p

or

<a id="db86"></a>git add 

Let’s do the following:

- create a new file called **finances.json**
- modify the **planets.csv** file by adding a column that includes interesting fun facts for each planet on the list
- delete the **to\_do\_list.json** file

Once done, the **Source Control** tab looks like this:

![](1_59kele1WaJSbNb1uE1kOtA_1b24262fa19f433fa04b3d725.png)

…and the terminal version of this looks like this:

![](1_8u0rfqcjoDG7zLypGxiiXA_a864d85bf1a34ce5a8601a21a.png)

We can see that the:

- new **finances.json** file is marked as untracked (U)
- existing **planets.csv** file is marked as modified (M)
- previously existing **to\_do\_list.json** file is now marked as deleted (D)

Let’s run the `git add -p` command in the terminal:

![](1_Qz7-5ifQ1nz3cxouTlJ4Dg_fe30299647784c28bf85f1b2a.png)

Before we advance, let’s break down the output quickly:

- `diff --git a/planets.csv b/planets.csv` \- this means there are changes between two files; a) the original file, and b) the modified one
- `index 846b016..5739dbf 100644` \- this uniquely identifies this specific change (basically an ID)
- `-- a/planets.csv` \- this is the original file of the **planets.csv** file
- `+++ b/planets.csv` \- this is the modified version of the **planets.csv** file
- `@@ -1,9 +1,9 @@` \- this is the “hunk” of changes. A hunk is a portion of code that has been added or modified. So the hunk in this example would be the 9 lines in the CSV file (1 header, 8 rows). In the `-1,9` section, the 1 means the hunk starts at line 1 in the original version, and the 9 means the file contains 9 lines. The same principle applies to the `+1,9` for the modified version.

We can decide to advance with this patch by typing `y` into the terminal, or we can reject the changes by typing `n`. There are other options included but that will be for a future article, so stay tuned for that!

By typing `y`, we advance to the next change that needs reviewing:

![](1_v2JSyCT0uCPwMwIvipznLA_3daae67c272c4a3fb37c44308.png)

We stage this deletion too, and then we can check what the staging area looks like:

![](1_7BksYmfZiQzJof5Z9w7_IQ_9fd0d32820b74e40b0bff88ea.png)

Here we see that the:

- **planets.csv** file is now marked as added (A)
- **to\_do\_list.json** file is now marked as added (A)
- **finances.json** file is still marked as untracked (U) because this approach does **NOT** stage new or untracked files

## Stage all new, modified AND deleted files in the local repository

This will add every file in the local repo to the staging area, including the ones dropped from staging, the ones with changes and the ones recently created:

<a id="57ed"></a>git add -A

Let’s shake up the repository a little by:

- creating a new file (called home_appliances.csv)
- modifying an existing file (animal_facts.json)
- deleting a tracked file (dummy\_file\_3.md)

Let’s look at the current state of our repo:

![](1_CofEU2rp2eln8Csz8pV8iw_d8b52cb22c9d46d59969c759b.png)

…and here’s the terminal version too:

![](1_p_HrfuDXBk1gB1T65k_Yjg_bbe41a22777d4851bf1e1317a.png)

We’ve

- modified the **animal_facts.json** file
- deleted the **dummy\_file\_3.md** file
- created a **home_appliances.csv** file

Now let’s run the `git add -A` command and see the results:

![](1_VaYIW8NlRfDywY5SqImpXA_87686a010b0b413fba311b414.png)

This command has added the:

- new **home_appliances.csv** file we just created
- modified **animal_facts.json** file

But if you notice, the **dummy\_file\_3.md** file was not included in the staging process. This is because we deleted it from our local machine first and then we ran the `git add -A` staging command, meaning the process wasn’t able to find the file on the machine to stage in the first place.

If we wanted it part of the staging, we would

1.  stage the file, then
2.  delete it from the local machine

But if we want it permanently deleted from Git, we would use `git rm {file-name}` to accomplish this.

## git status

Displays the status of changes as untracked, modified or staged

## What does `git status` do?

With `git status` you can

- view the current feature branch you’re on
- see the list any changes made to the files within the repo and whether they’re staged for the next commit
- points out the files that are not tracked by Git in the working directory (usually new files created that are not added to the repository)

## Different types of files in `git status`

Here are the different types of files you can find when you run `git status`:

- **Untracked (U)**
- **Added (A)**
- **Modified (M)**
- **Deleted (D)**

## → Untracked (U)

Any file that is not traced by Git is marked as an **untracked file (U)**. Here’s how they appear in VS Code and the terminal:

- **VS Code:**

![](../_resources/1_3h46spa_9buvOqetZdgAZg_fd1e07927953416597dec6f89.png)

- **CLI (Git Bash)**

![](../_resources/1_jPHk_DVQgkM8mlgjOpdQYg_543e178c0faa447b8a7078ae5.png)

## → Added (A)

A file that has been staged (i.e. added to the staging area) is marked as **added (A)** in VS Code and **new file** in the CLI:

- **VS Code:**

![](../_resources/1_6VmYMIgFJNBgux8QxTpX1A_92762afb522842c4b255d3afb.png)

- CLI

![](../_resources/1_rZiakhEpUzTtmONOeKBHFw_a65a9c1190e14c3eafd8f986a.png)

## → Modified (M)

A file is marked as **modified (M)** if any changes have been made inside it. Here’s what this would look like:

- **VS Code:**

![](../_resources/1_YkStN9ZE9jwIrlmF2u_mnQ_d76fe4db50d340e7961b569a3.png)

- **CLI**

![](../_resources/1_GAcpzamLa07iKIjGV_uyrA_41976a7cc7c8465a849b89ae4.png)

## → Deleted (D)

Any file that has been removed from the working directory or repository is considered a **deleted file (D)**:

- **VS Code:**

![](../_resources/1_kWg16jxhSYu8ep92f4Zv7A_9a2cb97133574ee59a56cdf86.png)

- **CLI**

![](../_resources/1_KfBhrdgrmPEV1lHFhVPzpw_42fcd99a5ad04545813cb64cc.png)

## git commit

Records the changes made to the repository

Here are some of the ways to use `git commit`:

## Save changes with a simple message

<a id="9d57"></a>git commit -m "Add a message about the commit here"

This will add and commit files that are already tracked

![](../_resources/1_VOCaxCXAIF3UViD5Lvo5pw_ebb6dc726eb24212bc1953f27.png)

## Save all the changes

<a id="b595"></a>git commit -a -m "Add a message about the commit here"

![](../_resources/1_uWAMu2qKPKGZviaU399IdA_5706b3a31f844381ba3579f43.png)

## Commit specific files only

<a id="aee6"></a>git commit {file-name-1} {file-name-2} -m "Add commit message here"

Let’s commit only two files from the staging area:

![](../_resources/1_gGjG-X4lQqfY4k-n7SbxxQ_46421a3c23ae4978bd0c51cc4.png)

![](../_resources/1_HAwGNFHEwCDQo_7p0TZVlQ_2e7e83b0e10a4bb18839e5d3f.png)

Here’s what the staging area now looks like:

![](../_resources/1_6G-DtRXOkolwkdVvqCnHkQ_29e64278074743d7ba1ec3756.png)

The **man_utd.json** and **planets.csv** files are no longer found in the staging area, and are now in fact committed, as you can see in the commit log:

![](../_resources/1_sZeyz73C_g7ivqe4-g134w_ec802429220c44d0a7fa09bf1.png)

## Save the commit with a title and a message

<a id="e13e"></a>git commit -m "Add title or short headline here" -m "Add a detailed explanation here"

Let’s put this to the test:

<a id="24bb"></a>git commit -m "Test Title for Commit" -m "This is a short explanation proving you can write short messages into your commits to provide more meaning and context for other developers to understand your commits"

![](../_resources/1_ekaUm9MommviAvF-IZhzQQ_f1d50df0a1d945afbea803426.png)

This is what it looks like in the commit log:

![](../_resources/1_bwHUYt4qHE1N1YcXb0I6uQ_4d4e5611668b4cc88164078a4.png)

## Use editor to add a commit message

<a id="f4b8"></a>git commit

This will open your default editor. Because I’m using **Git Bash**, my default editor is **Vim**, as presented below, but you can change the default editor to your preference:

![](../_resources/1_i9VWI3OjKW6KP5Yuhk-4QQ_865640ffac8a456d9a4fa09f8.png)

To save this commit,

- Enter `Esc` button on the keyboard
- Type `:wq` (like below)
- Hit `Enter`

![](../_resources/1_PO8TQVNC20RUoc6QXz2pCg_c527ada1e00c406ab0f5b0118.png)

This should then display this:

![](../_resources/1_Hm5vlhXAOnkurRe28ppHmQ_926f7fa59e404b48917dba149.png)

## Amend the previous commit

<a id="3fd8"></a>git commit 

This is what the previous example’s commit looks like when we view the commit log (via `git log`):

![](../_resources/1_APvVDWb1EVlHCpmJf3nbKg_f8ff9cc62c1949b289a4224a0.png)

The commit title is currently “Add Commit Message via Editor”. Let’s change it:

<a id="22e1"></a>git commit 

![](../_resources/1_GfaSMJaU6-pxddkuhSQG1g_7b1cab8a616d4ce4a9ac6b719.png)

Here’s what it looks like now:

![](../_resources/1_6WVPhr0aJZX_2z-QIEug9g_25f2aa4961224e8dbf3a54d7c.png)

## Stage and commit a change in one line

<a id="9cdd"></a>git commit -am "Add message here"

This works for files that are already being tracked

## git stash

Save a change in a temporary location

In Git, stashing allows you to save changes in a temporary location and then resume when you’re ready to continue from where you left off with them.

This is used mainly when you have changes that are staged, modified or even untracked that you’re not ready to commit yet even though you need to make other changes elsewhere e.g. in another branch. Stashing will make your working directory clean enough for you to work on different changes while you have others stored elsewhere.

## Save changes in a temporary location

<a id="cb43"></a>git stash

Here are the changes in my staging area I want to stash away temporarily:

![](../_resources/1_6YTIGwuGUHo78mi2U90DMg_439483bdbe934301a1f8ec111.png)

All I need to do is apply `git stash`, and here are the results:

![](../_resources/1_9BSS84i56JrI7EW2ngeRvw_044bac5182544654a85491756.png)

The staging area is officially empty, providing us with a clean, working directory to make other changes.

We can even create new files and stash these away too:

![](../_resources/1_Ps1ICOCU4hLUCpzRxZWMkg_ec0a359a1d6a450eb89a9bbe9.png)

## Save a stash with a specific name

<a id="08bc"></a>git stash save {custom\_name\_for_stash}

You can provide a custom name to a stash instead of using the default ones provided using the command above

## List the stashes

<a id="7c59"></a>git stash list

Let’s list all the stashes we’ve made:

![](../_resources/1_HDhOA95huZsEJ1tfCVV54w_81da1ce801874f7396e17ee31.png)

These are the two stashes made from the previous example

## Apply a stash

To apply the most recent stash to the current branch, use:

<a id="4cc8"></a>git stash apply

## Apply a specific stash

To apply a specific stash to the current branch, use:

<a id="d1a8"></a>git stash apply {stash-id}

If we switch to another branch, like **staging_branch**, we can apply one of the stashes from the **List the stashes** example:

![](../_resources/1_uaMniaeIscE28DxLO4lEhg_f6210fad4ff547238f551e968.png)

## Delete a stash

<a id="c3e7"></a>git stash drop

or

<a id="658d"></a>git stash drop {stash-id}

![](../_resources/1_WSOOTL7bYvvOG5PjLeYAeA_932f09de3791449da4cff8bc4.png)

## Apply and then delete a stash

This is referred to as popping, where you can apply a stash to the current branch and then drop it immediately:

<a id="ade2"></a>git stash pop

Here’s an example of how this works:

![](../_resources/1_WPKGPNL5PkPZgJ1U91XdQw_d180cd9f620644bdba1b3476c.png)

## git rm

Removes the specified file from the working repository and the staging area (a.k.a. the index).

Here’s how it works:

## Remove a single file

<a id="a895"></a>git rm {file-name-1}

## Remove multiple files

<a id="ac6d"></a>git rm {file-name-1} {file-name-2}

## Remove a directory

<a id="9bac"></a>git rm -r {directory-path}/

## Remove staged files with the same format from the index

<a id="32e5"></a>git rm --cached *.log

Let’s drop all of the CSV files from the staging area:

![](../_resources/1_50inx7P4aU1ZEMqZitFGdQ_4aed9a3180094d18920938690.png)

## Remove a file from the staging area only

<a id="3d84"></a>git rm --cached {file_name}

![](../_resources/1_D2AFji3eFUVZ8WTtzIfcCw_991c494af6a3496d8c15c6ac7.png)

## Remove a file with force

<a id="e860"></a>git rm -f {file_name}

![](../_resources/1_89viHkZLtuvdhSwdokxudw_cc9364b1caf449ec8a5387e43.png)

## git restore

Restores an existing file in the working directory to its previous state.

It reverts all unstaged and uncommitted files in the working directory to the version they had in the last commit. This is useful for reversing accidental modifications made to files, similar to how Ctrl + Z is used.

It’s also important to note that this does **not** affect the staging area or the commit history, so it is generally recommended to discard staged changes using `git reset HEAD` if you need to record the changes made in the commit history too.

## Restore a single file

<a id="6e90"></a>git restore {file_name}

Let’s create a new file called **top\_tech\_companies**:

![](../_resources/1_7nEuQUMKMzetxnb1D0mdew_df4978d24af447b98d3d77a96.png)

Once we add this to the index and add data to the file, the terminal should look like this:

![](../_resources/1_00O9N48x0ghWbpwz94eLIg_dc4bd0d6569148238842511e5.png)

Unfortunately, a developer has accidentally overwritten the data in the file with some random characters, and it wasn’t staged or committed at the time…

![](../_resources/1_mrnzWXnmlX1nPwWxSB-ZHw_641e23ce48b44fb8950f8146d.png)

Thankfully, we can restore the file to its previous state, when it did have all the data:

![](../_resources/1_bakpIn5V7kXs4OAujZFZxA_16ec13ae798a46d198fc7e504.png)

Here is the results:

![](../_resources/1_fM0bZ4H67-cqtrhM29s4cg_a8e4e94829494137930f6ff61.png)

It’s almost like the mistake never happened in the first place!

## Restore multiple files

<a id="77a4"></a>git restore {file\_name\_1} {file\_name\_2}

There appears to be a joker within the team that felt the need to change some of the information within some of the files in our repository. They appear to have modified the following files:

- **man_utd.json**
- [**README.md**](http://readme.md/)

Here’s what each says respectively:

![](../_resources/1_XnW7g0d9DSwly7lmwRi6FA_63a75bad22364f2684e329690.png)

![](../_resources/1_pN2S7-eYMws8Lj_veGzQVw_039d42bdae5541ae932588bc4.png)

Let’s correct these corrupted **unstaged** files by restoring them to their previous version since the last commit:

![](../_resources/1_j0DzoNJze7L64XfAG8nohQ_b9e070b76b7141cea2259387b.png)

And the results:

![](../_resources/1_7VHY01iTQHos1iG0_-WEpw_ed36cf0d7e214b91ac61cd43b.png)

![](../_resources/1_49MBc_FMgBM2d7W1rjyFoQ_8b336f33f355479e94cca5c0f.png)

Excellent! We wouldn’t want to spread that kind of misinformation across our repos now, would we?

## Restore files from the index

<a id="17da"></a>git restore --staged {file_name}

We created a new file called **dummy\_file\_3.md**, but discovered there was an error made in the file:

![](../_resources/0_WRyfAN5N9-j0JNpx_a6439a32970247819923bd15041d068.png)

The total number of continents provided was incorrect…we can’t commit this until we correct it, so let’s offload it from the index:

![](../_resources/0_0HXohRd1JxcZCyrK_5806b0d8a30344cfa75577d11db8c4d.png)

It’s successfully returned the file to its original state since the last commit

## Restore all files from the working directory

<a id="ff10"></a>git restore .

## 4\. Synchronizing your project💫

Keeping your local repository in sync with your remote repository is vital for version controlling your changes and enhancing good collaboration with other teams and developers

## git push

For uploading changes to the remote repository

## Send commits to the default remote branch

<a id="1cfc"></a>git push

![](../_resources/0_kq3gUHGGTWjOp1lo_0bfc868294714e31914dd5fb389f2eb.png)

## Send commits to a specific branch

<a id="cf10"></a>git push {remote} {branch}

![](../_resources/0_-hdiDYtLICbVcpnr_0f6774aa3e4e47789013ed6a5824db3.png)

## Send commits to a branch with force (unsafe)❌

<a id="ab60"></a>git push --f {remote} {branch}

or

<a id="2c65"></a>git push --force {remote} {branch}

This sends the local changes made to the remote server specified, even if there are commits in the remote server that are not included in the local repository.

**⚠️Warning:** This operation overwrites the entire commit history on the remote branch, which means the commits made on the repository since you last pulled will be permanently deleted.

Some of the dangers with this approach include:

- Deleting the commits of team members working on the same remote repository, causing data loss or conflicts
- Breaking the build process on the remote repo, which makes it difficult for other team members to push their respective changes
- Overwriting commits that have already been deployed to production may cause unexpected issues for your users

Remember to use this command cautiously, otherwise strongly consider the alternatives mentioned in this article.

If you do need to force push, consider the next example instead.

## Safely send commits to a branch with force✅

<a id="3105"></a>git push --force-with-lease {remote} {branch}

If you do need to force push, you can use this option instead. This approach is less dangerous than using `git push -f` because it prevents you from overwriting the commit history on the remote server if there is a commit on the remote branch that isn’t included in the local changes.

## Push all local branches to remote server

<a id="8cec"></a>git push --all

![](../_resources/0_oFhrZa6pO82Gez0I_e4fdf06b8960408aadb5c75dab9c9b9.png)

## git pull

For retrieving all the changes from a remote repository AND merging them to the local repository

Here are some of the different ways to use the `git pull` command:

## Pull the commits from the default remote branch to the local branch

<a id="ffc7"></a>git pull

## Pull from a specific remote branch to the current local branch

<a id="e457"></a>git pull {remote} {branch}

## Pull changes from all remote branches

<a id="5ac3"></a>git pull --all

## Pull from remote branch WITHOUT merging with local branch

<a id="45bb"></a>git fetch {remote}

## Rebase while pulling

<a id="3c9c"></a>git pull --rebase

## git remote

For listing all the remotes for collaborative purposes

![](0_HFlxjjwC7RoMukPQ_712ee0b0aa6442a280ab492600f2090.png)

## git cherry-pick

For picking an individual commit from one branch and pasting it into another

As the name implies, `git cherry-pick` lets you select specific commits from one branch and apply them to another, without merging the changes from the source branch.

## Cherry pick a single commit

<a id="8ed8"></a>
git checkout {target_branch}

<a id="9350"></a>\# Apply the commit to the target/child branch
git cherry-pick {commit_hash}

![](../_resources/0_KNJucgICLSOnaWqB_b5e482eb69514604b208b15dbbd6543.png)

## Cherry pick multiple commits

<a id="07aa"></a>
git checkout {target_branch}

<a id="62aa"></a>\# Apply the commits to the target/child branch
git cherry-pick {commit\_hash\_1} {commit\_hash\_2}

## Cherry pick a merge commit from the source branch

<a id="8926"></a>
git checkout {target_branch}

<a id="23a1"></a>\# Apply the commits to the target/child branch
git cherry-pick -m {parent\_no} {commit\_hash}

## 5\. Reviewing and debugging changes🤓

Sometimes we need to troubleshoot and compare different changes and working branches, here are some of the popular commands that handle that:

## git log

Displays the log of commits

## Show each logged commit on a single line

<a id="6f7b"></a>git log 

![](../_resources/0_Nk-IuRTvC9L0441__c72823ba649946acb40f4899b4e53d1.png)

## Filter the log commits by time period

<a id="c13d"></a>git log --since="time period"

![](../_resources/0_6i9soz1MCTiVV7rE_286711511f2a414cb1110a277dcbe9d.png)

## Filter the log commits by date range

<a id="044e"></a>git log --since="earlier date" --until="later date"

![](../_resources/0_8SiQSCzZ-xcwHrDe_bdb2142808b74ad2b4518323d124af7.png)

## Filter the log commits by one author

<a id="2ef6"></a>git log 

## Filter the log commits by multiple authors

<a id="a4d9"></a>git log --author="{name\_of\_user\_1}|{name\_of\_user\_2}"

## Show the differences between a commit and its parent

<a id="0ba4"></a>git log -p

or

<a id="4e70"></a>git log 

## git reflog

Lists the changes made in Git

The `git reflog` command shows the list of changes made in the Git repository. This command is useful for recovering lost commits caused by merges, rebases and resets, among other actions.

The main difference between the `git reflog` and `git log` command is that `git reflog` shows all the commits and changes of any branch within your repository, including the ones created, modified and deleted. `git log` only shows the complete commit history of the current (or specified) branch, and therefore doesn’t include commits from other branches or deleted ones.

## View the change history of your repo

<a id="d384"></a>git reflog

![](../_resources/0_y53AG0EEU6VStaq7_540318e9b67c44eea0bc5da87e023e3.png)

## View the change history of a specific branch

<a id="08dd"></a>git reflog {branch_name}

![](../_resources/0_EkSAPdncHTw3lP90_20f5db32ad7a43d49afcc742e1f77a2.png)

## Limit the changes shown by a specific number

<a id="1e7d"></a>git reflog -n {number\_of\_entries}

Specify the number of entries to display when viewing the repo’s change history

![](../_resources/0_GOHPp8v15W1_4qJo_1bd42d0f588c49dc9494631130254b4.png)

## View the change log for all references

<a id="6c8a"></a>git reflog show 

This will show the change history for every branch and tag within the repo instead of just the HEAD pointer

![](../_resources/0_boH_wCSPAGiiAdLI_f78d3a2ff3164c89ab1db55de24323f.png)

## View each change & commit entry on a single line

<a id="6cda"></a>git reflog 

This displays the changes and commits in the changes history in one-line format, which includes the commit hash, commit message and branch name.

![](../_resources/0_Bx20UB2tevFV1ER5_b0a73588142b4059a58f7f04fd86d5e.png)

## View each change entry in a short format

<a id="802f"></a>git reflog --pretty=short

Each change is displayed in a short, condensed format which includes the commit hash, commit message, author etc.

![](../_resources/0_dh6b1zNjHc-DwMvY_9816f617f99f4b18a8a27ab69251b6c.png)

## View each change entry in a medium format

<a id="8225"></a>git reflog 

This displays the changes in a format similar to `git log`, as per below:

![](../_resources/0_aMQqgsu6tVvJjDx9_b218ea46e34e4f749cd787660705e18.png)

The `git reflog` (left image) and the `git log` commands share similar details, but the main difference is that the `git reflog` version contains the HEAD pointer’s reflog details which the `git log` one lacks.

## View each change entry in full detail

<a id="bee3"></a>git reflog 

This will display each change with more robust information than the previous `git reflog --pretty` options mentioned, like below:

![](../_resources/0_Yd0KMAuDfplVtJV-_85f2dda927474353816a8c77137075e.png)

## git diff

Displays the differences between commits

## Show the changes between the modified files

<a id="cfa0"></a>git diff

![](../_resources/0_zI-iy43IkpK9_Wcg_d1ff932e454f4ff5947b55d7b5f19df.png)

## Show the names of modified files

<a id="0c50"></a>git diff --name-only

![](../_resources/0_6aPzGgmpaXVY4mCB_181efd8bcacd475f845fb87118aec2c.png)

## Show the name and statuses of modified files

<a id="1334"></a>git diff --name-status

![](../_resources/0_hy0kDs4-bssSM6y5_9521598d2cc249049358d06daf991e6.png)

## Show useful stats about each modified file

This displays some key statistics about each file that has changed in your repo, like the number of lines added or deleted, the percentage of changes made etc

<a id="1839"></a>git diff --stat

![](../_resources/0_Lj0FnQmkv10dFSt__2dcfe43456264488bbbc1551071d40c.png)

## Compare different versions of some work

<a id="fee1"></a>git diff -p {commit\_hash\_1} {commit\_hash\_2}

or

<a id="a63d"></a>git diff -patch {commit\_hash\_1} {commit\_hash\_2}

![](../_resources/0_2twigL0txOwvcsD8_88d928498ed644be85173fb4902d510.png)

## git reset

Rolls back to a previous commit

In Git, the `git reset` command is used to undo changes made in the working directory and/or staging area. This is achieved by the HEAD pointer moving to a previous commit, which means any changes since that commit will be permanently deleted.

## Reset the commit history only (soft reset)🟢

<a id="aa5f"></a>git reset --soft {commit_hash}

This reverts to the commit specified by the user while leaving the changes untouched in the working directory and staging area. In other words, this approach only changes the **commit history** and not the staging area or working directory.

## Reset the commit history and staging area only (mixed reset)🟡

<a id="a140"></a>git reset {commit_hash}

or

<a id="db4a"></a>git reset --mixed {commit_hash}

This undoes any changes to the **commit history** and the **staging area** by resetting the HEAD pointer to the previous (or specified) commit, and therefore will permanently wipe out all **staged changes** that have **not** been committed.

## Reset the commit history, working tree and staging area (hard reset)🔴

<a id="ab09"></a>git reset --hard {commit_hash}

This approach resets any changes made to the commit history, staging areas and the working directory. This is done by resetting the HEAD pointer to the specified commit.

⚠️**Warning:** This could impact the work of other colleagues if you’re working on the same repository, so only use this if you know exactly what you’re doing, otherwise consider safer alternatives.

## git revert

Creates a new commit that undoes a previous change

The `git revert` is a command that reverses the changes made in a previous commit within the Git repository by creating a new commit.

## Revert the latest commit

<a id="7a6d"></a>git revert HEAD

## Revert a specific commit

<a id="a3ef"></a>git revert {commit_hash}

## Revert a merge commit

<a id="022c"></a>git revert -m {parent\_number} {commit\_hash}

## Revert the last n commits

<a id="2be7"></a>git revert -i HEAD~n

Change `n` to the number of your choice

## git grep

Searches for keywords in the repository

The `git grep` is used to search for a string of text, characters and patterns within any file of your Git repository (i.e. tracked **and** untracked files).

## Search for a text string in your repo

<a id="4be6"></a>git grep "{string\_of\_text}"

Here we try to find the planet named “Uranus” within the planets.csv file, however it is not currently tracked, so running `git grep “Uranus”` would return nothing:

![](../_resources/0_QK5-vh0heR60aqdQ_87adc7ee297441659569834e5e0faac.png)

Once the files are added to the staging area, we can expect a different result:

![](../_resources/0_GWOCira08IxP2dpI_83fd3ad2f1574921b55fd1589c63327.png)

## Search for a text string within a subdirectory

<a id="1116"></a>git grep "{string\_of\_text}" {sub\_directory\_path}/

## Search for a text string within a specific branch

<a id="54ae"></a>git grep "{string\_of\_text}" {branch_name}

## Search for a text string within the staging area

<a id="9bef"></a>git grep --cached "{string\_of\_text}"

![](0_7YHaOlfyYW6sA0Ju_cf2e8c5ae62149ae92f7ba309329eee.png)

## Search for a text string within an untracked file

<a id="4059"></a>git grep --untracked "{string\_of\_text}"

![](0_G1_u1drAfvN_C_BQ_384a98efbbc34844aa1b2e4f2216124.png)

## Search for a term with its line number

<a id="b2f8"></a>git grep -n "{string\_of\_text}"

![](0_eRE2mV75Y-PpacH0_545057ca483d4e8db7a32c57d02dca5.png)

## Search for the number of matches for a term

<a id="ff07"></a>git grep -c "{string\_of\_text}"

## Conclusion 🏁

For deeper dives and insights into these data engineering topics with clear and simple code examples on how they are applied in the real world, subscribe to my newsletter below where you can also forward any questions you have privately and I will be happy to answer them for free:

Feel free to reach out via my handles and I’ll be happy to point you in the right direction where I can: [**LinkedIn**](https://www.linkedin.com/in/stephen-david-williams-860428123/)| [**Email**](mailto:stephenodavidwilliams@gmail.com) | [**Twitter**](https://twitter.com/sdw_online) **|** [**Tiktok**](https://www.tiktok.com/@sdw.online)