Imagine you are working on a critical report. You save the file as report_v1.doc. You make some changes and save it again as report_v2.doc. As you get feedback, you soon find your folder cluttered with report_final.doc, report_final_REVISED.doc, and report_REALLY_final_I_swear.doc. This system is chaotic, makes it impossible to see what changed between versions, and is a nightmare if you need to collaborate with someone. This is the problem that version control systems were designed to solve.
A version control system, or VCS, is a piece of software that tracks and manages changes to a set of files over time. It allows you to record snapshots of your project at specific moments. If you make a mistake, you can “rewind” to a previous snapshot. If you are working with a team, a VCS allows multiple people to work on the same files without overwriting each other’s changes. It provides a clear, documented history of who changed what, when they changed it, and why. This moves you from a state of chaos to one of order, clarity, and accountability.
Introducing Git: The Modern Standard
Git is a specific, open-source, and free tool for version control. It was created in 2005 by Linus Torvalds, the same developer who created the Linux operating system. He needed a tool to manage the development of Linux, which involved thousands of contributors from all over the world. The existing version control systems were not fast, powerful, or flexible enough for his needs, so he built Git. Today, Git has become the global standard for version control. It is used by over 100 million developers, data scientists, and professionals worldwide, managing projects from the smallest personal website to the largest corporate software systems.
What sets Git apart is that it is a distributed version control system. In older, centralized systems, there was one main server that held all the project’s history. If that server went down, nobody could work. In a distributed system like Git, every single developer has a full, complete copy of the entire project’s history on their local machine. This means you can work completely offline, committing changes, viewing history, and creating branches. You only need to connect to a central server when you are ready to share your changes with the team or receive updates from them. This design makes Git incredibly fast, resilient, and flexible.
Why Git is Dominant
Git’s popularity is not an accident. It holds over 70% of the market share for version control tools, and its adoption continues to grow. One major reason is its speed. Because most operations, like committing or branching, happen on your local computer, they are nearly instantaneous. You are not waiting to communicate with a slow central server. This local-first approach also means you can be on a plane, in a coffee shop with bad Wi-Fi, or anywhere else and still have full access to your project’s entire history and toolset.
Its open-source nature is another key factor. Being free and open-source removes any cost barrier to entry. Anyone, from a student learning to code to a massive corporation, can download and use it for free. This has fostered a massive global community that contributes to its development, writes documentation, and provides support. Furthermore, Git’s design provides a safe environment for experimentation. Less experienced developers can try out new ideas on their own “branch” without any risk of breaking the main project. This safety net encourages innovation and learning.
Core Concepts: The Git Philosophy
To truly understand Git, you must understand its core philosophy, which is different from many older systems. The most important distinction is how Git thinks about its data. Most other systems store information as a list of file-based changes. They store a “delta,” or the difference between one version and the next. If you have a file and change one line, the system records that one-line change.
Git, however, stores its data as a series of snapshots. When you save the state of your project, which Git calls a commit, it doesn’t just look at the differences. Instead, it takes a picture of what all your files look like at that exact moment and stores a reference to that snapshot. If files have not changed from one version to the next, Git doesn’t store the file again; it just stores a link to the previous, identical file it has already stored. This snapshot-based approach is what makes Git so powerful. It allows for fast operations, robust history tracking, and the ability to instantly jump back to any snapshot in your project’s history.
The Three States of Git
Every file in your project, as far as Git is concerned, exists in one of three main states. Understanding these three states is the most fundamental part of learning the Git workflow. The first state is the working directory. This is your project folder on your computer. It contains all the files you are actively editing. Any changes you make, such as adding a new line of code or deleting a file, happen here, in your working directory. This area is “untracked” by Git until you explicitly tell it to pay attention.
The second state is the staging area. This is a unique and powerful concept in Git. The staging area is like a “drafting table” or an “on-deck” circle. When you have made a change in your working directory that you are happy with, you don’t commit it directly. First, you add it to the staging area. This tells Git, “This specific change is ready to be part of the next snapshot.” This is incredibly useful because it allows you to craft your snapshot precisely. You might make changes to five different files but only want to include changes from three of them in your next commit. The staging area lets you do this.
The third and final state is the Git directory, or the repository (often shortened to “repo”). This is the hidden .git folder that Git creates inside your project directory. This directory is the “brain” of Git. It contains all the snapshots, all the history, and all the metadata for your project. When you finally commit, you are taking all the changes that you prepared in the staging area and saving them as a permanent snapshot in the Git directory. So, the standard workflow becomes: 1) Make changes in your working directory, 2) Add those specific changes to the staging area, and 3) Commit the staged changes to create a new snapshot in the repository.
Git in the Real World: Who Uses It?
While Git was born from the world of software engineering, its use has expanded far beyond traditional coding. Any project that involves changes to digital files over time can benefit from version control. For example, writers and technical authors use Git to manage manuscripts. They can track revisions, collaborate with editors, and revert to older drafts. Because Git is text-friendly, it’s perfect for managing plain text files like Markdown or LaTeX.
Design teams are also adopting Git. While it’s not optimized for large binary files like images, it can be used to track vector graphics (like SVG, which is text-based code) and manage design system documentation. The concept of “branching” allows a designer to experiment with a new layout without affecting the “main” version of the design. This ability to experiment freely and then merge the best ideas back is invaluable in any creative field.
Version Control in Data Science
Data science and machine learning are two fields where Git has become indispensable. A data science project is a highly iterative process. It involves writing scripts for data cleaning, creating and tuning machine learning models, and generating visualizations. Git allows a data scientist to track all of these moving parts. They can manage different versions of their data processing scripts or their Jupyter notebooks.
Perhaps more importantly, Git is crucial for reproducibility. A common problem in research and data science is not being able to reproduce a result later. With Git, you can tie a specific result, model, or paper directly to the exact version of the code, data, and parameters that produced it. You can “tag” a commit as “v1.0-model-for-publication,” and you will always be able to go back to that snapshot. This level of versioning is essential for good science and for collaborating on complex machine learning pipelines.
Version Control for Web and Mobile Development
For web and mobile developers, Git is not just useful; it’s a non-negotiable requirement. Modern development involves managing website code, assets like images and stylesheets, and server configurations. Git is the backbone that allows development teams, from solo freelancers to large enterprises, to manage this complexity. It is a key part of modern DevOps practices, which aim to automate and streamline the process of building, testing, and deploying software.
In mobile application development, Git is used to manage the codebase for different platforms, like iOS and Android. New coding assistants and artificial intelligence tools often integrate directly with the version control system, providing code suggestions and helping to manage the development process. Git’s branching capabilities allow teams to work on new features, fix bugs, and handle “hotfixes” for urgent issues, all at the same time, without interfering with each other’s work.
The Job Market Imperative
Because Git is so fundamental to modern development, it has become a must-have skill in the job market. Companies now expect this skill for almost any role in software engineering, data, and IT. A good command of Git is more important than ever. If you look at job listings for roles like Application Developer, Front End Developer, or Data Scientist, you will almost always see “experience with Git” listed as a core requirement.
Learning Git is not just about learning a tool; it’s about learning the process of professional collaboration. When a company hires you, they are trusting you to work on their valuable codebase. Knowing Git shows that you understand how to collaborate safely, how to document your changes, and how to participate in a team workflow. Even mastering the basic commands is often enough to get started. As your role evolves, honing your Git skills will be necessary to advance your career and take on more senior, high-paying positions.
Setting Your Learning Goals
Before you type your first command, it’s important to understand why you are learning Git. It’s not just about memorizing commands but about adopting a new approach to managing your projects. Take a moment to consider your needs and goals. Ask yourself a few questions: How much do I already know about version control? Am I a complete beginner, or have I used other systems in the past?
Are you learning Git to contribute to open-source projects? Is your goal to collaborate more effectively with a team at work? Or are you a solo practitioner, like a researcher or student, who simply wants to streamline your personal workflow and keep track of your own projects? Answering these questions will help you structure your learning path. A data scientist who wants to version notebooks has different needs than a web developer working on a large team with automated deployment pipelines. Knowing your goal will help you focus on the most relevant skills first.
Installing Git on Your Machine
Before you can use Git, you need to install it on your computer. The good news is that it’s a straightforward process on all major operating systems. For macOS users, Git may already be installed. You can check by opening your Terminal application and typing git –version. If it’s not, the system will often prompt you to install the Apple command line developer tools, which include Git. For Windows users, the most popular option is to download “Git for Windows,” which provides the Git command-line tools as well as a helpful “Git Bash” terminal that emulates a Linux environment.
For Linux users, Git is typically available through your distribution’s package manager. For example, on Debian-based systems like Ubuntu, you would simply run sudo apt-get install git from your terminal. Regardless of your operating system, you can always find the latest official installers and detailed instructions on the main Git project website. The goal is to get to a point where you can open a terminal or command prompt, type git –version, and see a response like git version 2.40.0.
Initial Configuration: Telling Git Who You Are
Once Git is installed, there is a crucial one-time setup step you must perform. You need to tell Git your name and email address. Git uses this information to “stamp” every single commit you make. This is essential for collaboration, as it allows your teammates to see who made which change. You can set this by opening your terminal and typing two commands.
The first command is git config –global user.name “Your Name”, replacing “Your Mame” with your actual name. The second command is git config –global user.email “your.email@example.com”. The –global flag tells Git to use this information for every repository on your computer. If you need to use a different name or email for a specific project, you can run the same command without the –global flag from within that project’s directory, and it will override the global setting for that repository only. You can check your settings at any time by typing git config –list.
Command Line vs. GUI Clients
You have two primary ways to interact with Git: the command-line interface (CLI) or a graphical user interface (GUI). The CLI is what you just used to configure Git. You type commands like git commit directly into your terminal. A GUI client provides a visual, point-and-click interface with buttons, menus, and graphs to perform Git operations. Many of these clients are available, some as standalone applications and some integrated directly into code editors.
While GUI clients can be very helpful, especially for visualizing complex branch histories, it is highly recommended that all beginners learn Git using the command line first. The reason is that the GUI applications are just running the real Git commands “under the hood.” If you only learn the GUI, you will never understand the underlying concepts or the “three-state” model of Git. This will leave you completely lost when something goes wrong. Learning the CLI forces you to understand what you are actually doing. Once you are comfortable with the commands, you can use a GUI to make yourself more efficient.
Creating Your First Local Repository
You are now ready to create your first repository. A repository, or “repo,” is a project that Git is tracking. To get started, you can either create a new repository from scratch or “clone” an existing one from a server. Let’s start by creating a new one. First, create a new folder for your project using your normal file explorer or the command line (mkdir my-project). Then, navigate into that new folder (cd my-project).
Once you are inside your project folder, there is only one command you need to run: git init. This command stands for “initialize,” and it tells Git that you want to start tracking this folder as a new repository. It will create a hidden sub-folder named .git. This .git directory is the heart of your repository. It contains all the objects, references, and configuration files that Git needs to track your project’s history. You should never manually edit or delete anything inside this folder. After running git init, your project is officially a Git repository, and you can start working.
The Basic Git Workflow: Add, Commit, Repeat
The fundamental workflow of Git revolves around a simple, three-step cycle. First, you modify files in your working directory. This is just your normal work: writing code, creating documents, or fixing typos. As you work, Git knows these files have been changed, but the changes are not yet part of the repository’s history.
Second, you stage the specific changes you want to include in your next snapshot. This is done with the git add command. For example, if you edited a file named README.md, you would type git add README.md. This moves the file from your working directory into the staging area, which we discussed in Part 1. You can add multiple files, or even parts of files, to the staging area. This step is what allows you to build up your snapshot (commit) piece by piece.
Third, you commit the staged changes. This is done with the git commit command. This command takes all the files from your staging area, wraps them up into a snapshot, and saves that snapshot permanently to your project’s history. When you run the command, it’s best practice to include a message that describes what you changed and why. The most common way to do this is with the -m flag, like this: git commit -m “Add initial project description to README”. This “modify, add, commit” cycle is the loop you will run over and over as you work.
Understanding the Staging Area
The staging area is often the most confusing concept for beginners, but it is also one of Git’s most powerful features. Why have this extra step? Why not just commit changes directly from the working directory? The staging area’s purpose is to allow you to craft your commits with precision. A “commit” should be a logical, self-contained unit of change. This is known as an “atomic commit.”
For example, imagine you are working and you fix a bug. At the same time, you also start working on a new feature, but it’s not finished. You have changes in two files: bug-fix.js and new-feature.js. It would be a bad commit to lump these together. The bug fix is complete, but the feature is not. Using the staging area, you can run git add bug-fix.js and then run git commit -m “Fix critical bug in login process”. Your unfinished new-feature.js file remains in your working directory, un-staged and un-committed, safe to be finished later and added to its own commit. The staging area gives you total control over what goes into your project’s history.
Crafting the Perfect Commit
A commit is more than just a snapshot; it’s a message to your future self and your teammates. A project’s history is only as useful as its commit messages. A history full of messages like “fix” or “more work” is completely useless. Therefore, learning to write good commit messages is a critical skill. When you run git commit -m “My message”, the message is saved as part of the commit.
If your message is short, the -m flag is fine. But for a proper commit, you should omit the -m flag and just type git commit. This will open a text editor (like Vim, Nano, or whatever you configured) where you can write a more detailed message. The standard convention is to write a short subject line (50 characters or less) that summarizes the change. Then, leave a blank line, and then write a more detailed body that explains why you made the change and what its effects are. This detailed explanation is invaluable when someone (including you, six months from now) is trying to understand why a piece of code was written.
Writing Meaningful Commit Messages
Let’s break down a good commit message. The subject line should be written in the imperative mood, as if you are giving a command. For example, use “Fix login bug” instead of “Fixed login bug” or “Fixes bug.” This matches the style of commit messages that Git generates automatically for operations like merging. It keeps the history consistent and easy to read.
The body of the message should provide context. What was the problem? How did this commit solve it? Did it have any side effects? A good rule of thumb is to explain the “why,” not the “how.” The “how” is visible by looking at the code changes themselves. The “why” is the critical context that is often lost. For example, a bad message is “Updated variable.” A good message would have a subject like “Refactor user validation logic” and a body like “The previous validation logic failed on edge cases with international phone numbers. This commit updates the regex to be more inclusive and adds a new function to sanitize input before validation.”
Checking Your Work: Git Status
As you work, you will constantly need to know the state of your project. The single most important command for this is git status. You should run this command all the time. It is your main dashboard for Git. It will tell you exactly what is going in your working directory and staging area.
When you run git status, it will tell you which branch you are on. It will list any files in your working directory that have been modified but not yet staged (“Changes not staged for commit”). It will list any files that are in the staging area and are ready to be committed (“Changes to be committed”). And it will list any new files in your directory that Git doesn’t even know about yet (“Untracked files”). This command gives you a perfect, up-to-the-minute summary and guides you on what your next steps should be, whether it’s to git add some files or to git commit your staged changes.
Understanding Your Project’s History: Git Log
After you have made a few commits, you will want to look back at the history you’ve created. The command for this is git log. In its default form, this command will show you a list of all the commits you have made, starting with the most recent one. For each commit, it will show the long, unique commit “hash” (a unique ID), the author (your configured name and email), the date, and the full commit message.
This default log can be very verbose. Thankfully, git log is highly customizable. One of the most useful variations is git log –oneline. This will show a much more compact view, with each commit on a single line, displaying only the first seven characters of the commit hash and the commit’s subject line. This is a fantastic way to get a quick overview of your project’s recent history. Another popular option is git log –graph, which will draw a small ASCII graph in your terminal showing how different branches have been created and merged. This is incredibly helpful for visualizing the project’s structure.
What Are Remote Repositories?
So far, everything we have done—initializing a repository, adding files, and committing changes—has happened exclusively on your local computer. This is great for personal projects, but the true power of Git is unlocked when you collaborate with others. To do this, you need a central place where the team can share their work. In Git, this is known as a “remote” repository. A remote is simply a version of your project that is hosted on a server, either on the internet or on your company’s internal network.
Your local repository (on your laptop) and the remote repository (on the server) are two separate copies of the project. You work locally, making commits as usual. When you are ready to share your changes with your team, you “push” your local commits to the remote repository. When you want to get the latest changes that your teammates have made, you “pull” their commits from the remote repository down to your local copy. The remote repository acts as the central source of truth for the team. The most common name for your main remote, by convention, is “origin.”
Popular Git Hosting Platforms
When you need a place to host your remote repository, you have many options. You will find several popular and massive platforms designed for this exact purpose. Some of these platforms are famous for hosting the world’s largest collection of open-source projects. They provide excellent tools for community collaboration, issue tracking, and managing contributions. These are often the default choice for individuals and open-source teams due to their robust free offerings and beginner-friendly interfaces.
Other platforms are built with a stronger focus on the entire software development lifecycle, often called “DevOps.” They provide tightly integrated, built-in tools for continuous integration and continuous deployment (CI/CD), which automate the testing and release of software. You may also find platforms that are favored by large enterprises, especially those that already use other tools from the same company for project management and team documentation. Finally, for organizations with high-security needs, it’s also common to host their own private Git servers internally, giving them maximum control over their code.
Connecting to a Remote: Git Remote
Let’s assume you have gone to a hosting platform and created a new, empty remote repository. The platform will provide you with a unique URL for your repository. This URL is the “address” that Git will use to find it on the internet. Now, you need to connect your local repository (the one on your computer) to this new remote one.
You do this from your project’s folder in your terminal using the git remote command. The command to add a new connection is git remote add <name> <url>. By convention, the name for your main remote is “origin.” So, you would run: git remote add origin https://your-hosting-platform.com/your-username/your-project.git. This command doesn’t move any files; it just creates a bookmark. It tells your local repository, “There is a remote named ‘origin’ at this specific URL.” You can check that it worked by running git remote -v, which will list all the remotes you have configured.
Pushing Your Changes: Git Push
Now that your local repository knows where the remote “origin” is, you can share your work. Let’s say you have made several local commits on your main project branch (by default, this branch is often named “main”). To send these commits to the remote repository, you use the git push command.
The full command is git push <remote-name> <branch-name>. The first time you push, you will likely run git push origin main. This command tells Git: “Take my local ‘main’ branch and push all the commits that are on it up to the ‘origin’ remote’s ‘main’ branch.” You may be prompted for your username and password for the hosting platform. After the push is complete, your remote repository is now an exact copy of your local one, and your teammates can see your changes. From now on, after you make a few local commits, you can just run git push to send the new commits up.
Cloning an Existing Repository: Git Clone
In many cases, you won’t be starting a project from scratch. Instead, you’ll be joining an existing project. This means the remote repository already exists on a server, full of code and history from your team. To get a copy of this project on your computer, you don’t use git init. Instead, you use the git clone command.
You will get the project’s URL from your team or the hosting platform. Then, in your terminal (but not inside another Git repository folder), you run git clone <url>. For example: git clone https://your-hosting-platform.com/team-project/awesome-app.git. Git will do several things at once: it will create a new folder on your computer (e.g., “awesome-app”), download all the files, download the entire project history, and automatically set up the “origin” remote to point back to the URL you cloned from. You are now ready to start working.
Getting Changes from Others: Git Pull
You’ve cloned a project and started working. But in the meantime, your teammate has also been working and has “pushed” their own changes to the remote repository. Your local copy is now out of date. To get the latest changes, you use the git pull command.
From within your project folder, you simply run git pull origin main (or often just git pull if your local branch is set up to track the remote one). This command tells Git: “Go to the ‘origin’ remote, look at the ‘main’ branch, and see if there are any new commits. If there are, download them and immediately merge them into my local ‘main’ branch.” After the pull is complete, your working directory will be updated with all of your teammate’s changes, and your local history will be in sync with the remote one. It’s a best practice to git pull every morning before you start working to ensure you have the latest code.
Fetch vs. Pull: Understanding the Difference
This is a subtle but very important concept for intermediates. The git pull command is actually a combination of two separate commands: git fetch and git merge. Understanding this separation will save you from a lot of confusion.
The git fetch command is the “safe” part. When you run git fetch, Git connects to the remote repository and downloads all the new commits, but it does not integrate them into your local working branches. It just updates your “remote-tracking branches” (like origin/main), which are read-only pointers to the state of the remote. You can see what others have done, but your own main branch is untouched. This gives you a chance to inspect the changes before you merge them.
git pull, on the other hand, does git fetch and then immediately runs git merge to combine the downloaded changes with your local branch. This is convenient, but it can sometimes lead to unexpected results if your teammate made a change that conflicts with yours. Many experienced developers prefer to run git fetch first, then use a command like git log origin/main to see what’s new, and then manually run git merge to integrate the changes. This gives them more control over the process.
A Simple Collaborative Workflow
Now we can define a simple workflow for a team.
- Clone: The first time, each team member runs git clone to get a local copy.
- Pull: Before starting new work, run git pull to get any updates from teammates.
- Work (Modify, Add, Commit): Make your changes locally, using the git add and git commit -m “…” cycle. You can make many local commits.
- Pull (Again!): Before you share your work, run git pull one more time. This is critical. Someone else might have pushed changes while you were working. You must integrate their changes into your local repository before you can push yours.
- Push: Once your local repository is up-to-date and contains your new commits, run git push to share your work with the rest of the team.
This “pull, work, pull, push” cycle is the foundation of Git-based collaboration.
Working with Public and Private Repositories
Repositories on hosting platforms are generally one of two types: public or private. Public repositories are visible to everyone on the internet. Anyone can see the code, download it, and even “fork” it (make their own copy). This is the foundation of open-source software. Private repositories are only visible to you and the people you explicitly grant access to. This is what companies use to store their proprietary code.
When you interact with these repositories, you need to authenticate yourself. There are two common ways. The first is HTTPS, which is what we used in our examples. When you push or pull, you will be prompted for your username and password for the hosting platform. This is simple but can be repetitive. The second, more secure and convenient method, is SSH. You generate a “key pair” on your computer: a private key that stays on your machine and a public key that you upload to your hosting platform. When you connect, Git and the server use these keys to “prove” you are who you say you are, without you ever having to type a password.
Contributing to Open Source Projects (The Concept)
If you want to contribute to a public open-source project, you typically can’t push your changes directly to their repository. You don’t have permission. Instead, you use a process called “forking.”
First, you “fork” the project on the hosting platform. This creates a personal copy of the entire repository under your own account. Second, you git clone your fork to your local machine. Third, you make your changes locally, creating a new branch and committing your improvements. Fourth, you git push these changes up to your fork. Finally, from the hosting platform’s interface, you open a “pull request.” This is a formal request to the original project’s maintainers, asking them to review your changes and “pull” them into their main project. This “fork and pull request” model is the lifeblood of open-source collaboration, allowing thousands of strangers to work together on a single project.
What is a Branch?
Branching is arguably the single most important and powerful feature of Git. It’s the feature that sets it apart from older version control systems and allows for the flexible, parallel workflows that modern teams rely on. A branch is simply an independent line of development. Imagine your project’s history as the main trunk of a tree. A branch is a new limb that sprouts from that trunk. You can work on this new branch, adding new commits, without affecting the main trunk at all.
What makes this so powerful in Git is that branches are incredibly “cheap” and “lightweight.” In many old systems, creating a branch meant copying your entire project into a new folder, which was slow and used a lot of disk space. In Git, a branch is just a simple, movable pointer that points to a specific commit. Creating a new branch is as simple and fast as creating a tiny new file. This encourages developers to use branches for everything: every new feature, every bug fix, and every simple experiment.
The Default Branch: Main (or Master)
When you initialize a new repository with git init, Git creates one branch for you automatically. For many years, this default branch was named “master.” In recent years, in an effort to use more inclusive language, the industry has shifted, and this branch is now typically named “main.” You will see both in the wild. This “main” branch is considered the definitive version of your project. It should always be stable, tested, and deployable.
Think of the main branch as the “source of truth.” It represents the official history of your project. The golden rule is that you should almost never commit directly to the main branch. All new work should be done on a separate “feature branch.” Only when that work is complete, tested, and reviewed should it be “merged” back into the main branch. This keeps your main line of development clean and ensures you always have a working version of your project to fall back on.
Creating Your First Branch: Git Branch
Let’s see this in practice. Imagine you are about to start work on a new feature, like adding a login page to your website. You are currently on the main branch. The first thing you do is create a new branch for this feature. The command to create a branch is git branch <branch-name>. So, you would type: git branch add-login-page.
This command creates the new branch, but it does not switch you to it. You are still on the main branch. You can see all the branches in your repository by typing git branch with no arguments. It will list your branches, and an asterisk (*) will appear next to the one you are currently on. You would see:
* main
add-login-page
This shows that the add-login-page branch exists, but you are still on main.
Switching Branches: Git Checkout and Git Switch
To actually start working on the new branch, you need to “check it out.” The classic command for this is git checkout <branch-name>. So, you would type: git checkout add-login-page. Your terminal will now say “Switched to branch ‘add-login-page’.” If you run git branch again, the asterisk will have moved.
In recent versions of Git, new, safer commands were introduced to avoid the confusion of the git checkout command (which is used for many different things). The new command is git switch <branch-name>. This command only switches branches, making its purpose clearer. Even better, you can create and switch to a new branch in a single command using the -c flag: git switch -c add-login-page. This is the modern, preferred command and is equivalent to running git branch … and then git switch ….
The Branching Workflow: A Practical Example
Let’s walk through the full workflow.
- You are on the main branch and want to build a new feature.
- You run git switch -c new-feature. This creates a new branch called new-feature and immediately switches you to it.
- Now, you start working. You edit file1.js and create a new file file2.css.
- You stage your changes: git add file1.js file2.css.
- You commit your changes: git commit -m “Add new feature logic and styling”. This new commit now exists only on the new-feature branch. The main branch is completely unaffected. It is still back where it was before you started. You can continue this cycle, adding more commits to your new-feature branch. Your main branch remains stable and clean, which is exactly what you want.
Merging Branches: Git Merge
Your new feature is now complete, tested, and ready to be added to the main project. The process of taking the history from your feature branch and integrating it back into your main branch is called “merging.”
The process is a two-step. First, you must switch back to the branch you want to merge into. In this case, that is the main branch. So, you run: git switch main. Second, you run the git merge <branch-name> command, telling Git which branch you want to pull in. So, you run: git merge new-feature.
Git will now take all the commits you made on the new-feature branch and add them to the main branch. If no other changes have been made to main while you were away, this will be a “fast-forward” merge. Git simply moves the main branch pointer forward to point to the same commit as your new-feature branch. It’s clean and simple.
Understanding the Three-Way Merge
But what happens if changes were made on the main branch while you were busy working on new-feature? For example, perhaps a teammate fixed an urgent bug and merged that fix into main. Now, the two branches have “diverged.” This is a very common scenario.
When you run git merge new-feature, Git cannot do a simple fast-forward. Instead, it performs a “three-way merge.” It looks at three snapshots: 1) The last common ancestor commit (where the branches split), 2) The current state of your main branch, and 3) The current state of your new-feature branch. Git then combines the changes from both branches and creates a new, special commit called a “merge commit.” This new commit has two parent commits (one from main, one from new-feature) and represents the moment the two histories were rejoined.
Handling Merge Conflicts
A three-way merge works automatically… unless you and your teammate edited the exact same line in the exact same file. When this happens, Git doesn’t know which change is “correct.” It can’t read your mind. This situation is called a merge conflict, and Git will stop the merge process and ask you to resolve it manually.
When you have a conflict, Git will tell you. If you open the conflicting file, you will see special “conflict markers” that Git has inserted, which look like this:
<<<<<<< HEAD
This is the line from the main branch.
=======
This is the line from the new-feature branch.
>>>>>>> new-feature
HEAD is a pointer to your current branch (main). Your job is to edit this file. You must delete the conflict markers and decide what the “correct” version of the line should be. Maybe you want the line from main, maybe the one from new-feature, or maybe you need to rewrite the line to combine both ideas. Once you have fixed the file and it looks correct, you save it. Then, you run git add <conflicted-file-name> to tell Git the conflict is resolved. Finally, you run git commit to finalize the merge.
A Better Way to Collaborate: Pull Requests
The workflow of merging branches locally is perfectly fine, but in a team environment, it has a major flaw: there is no opportunity for review. You just merge your code and push it. This can lead to bugs or low-quality code being added to the main branch.
A much better and more common workflow, enabled by Git hosting platforms, is the “pull request” (or “merge request”) workflow. In this model, you do not merge locally. Instead, you push your feature branch to the remote server: git push origin new-feature. Then, on the hosting platform’s web interface, you open a “pull request.” This is a request that says, “I have completed ‘new-feature,’ and I would like to merge it into ‘main.’ Please review my work.”
This creates a discussion page where your teammates can see your code, leave comments, and suggest changes. An automated testing system (CI/CD) might run. Only after your code has been reviewed and approved by your peers will a team lead “accept” the pull request, and the platform will perform the merge on the server. This is the core of modern, collaborative, quality-controlled development.
Managing Branches
After your feature branch has been successfully merged into main (either locally or through a pull request), it is no longer needed. It’s good practice to delete it to keep your repository clean. You can delete the local copy of the branch with the command: git branch -d new-feature. The -d (lowercase) is a safety-measure; it will only delete the branch if its changes have already been merged.
If you also pushed the branch to the remote, you will want to delete it from there as well. You can do this with the command: git push origin –delete new-feature. Cleaning up old, merged branches is like tidying your workspace. It makes it easier for everyone to see what work is currently in progress and prevents the list of branches from becoming cluttered.
Undoing Changes: A Git Safety Net
One of the best things about Git is that it’s very difficult to truly lose your work. Because Git is built on snapshots, it provides a powerful safety net that lets you undo almost any mistake. However, “undo” in Git is not a single simple command. There are different commands for different scenarios, depending on where the change is: in your working directory, in the staging area, or already committed to your history.
Mastering these “undo” commands is what separates a beginner from an intermediate Git user. It gives you the confidence to experiment and try new things, knowing that you can always “rewind” back to a known-good state. The most important rule to learn is the difference between undoing private, local changes (which is easy and safe) and trying to change public, shared history (which is dangerous and should be avoided).
Unstaging Files: Git Restore
Let’s start with the simplest mistake. You used git add <file> to move a file into the staging area, but you changed your mind. Maybe you added it by accident, or you realized it wasn’t ready to be part of the commit. You need to “unstage” it, moving it out of the staging area but keeping the changes in your working directory.
The modern command for this is git restore. To unstage a file, you run: git restore –staged <file-name>. This command will pull the file out of the staging area, and git status will now show it as “Changes not staged for commit.” This is a very safe operation. You are not losing any code; you are just changing its state from “staged” to “unstaged.” In older tutorials, you may see this done with git reset HEAD <file-name>, which also works but is less intuitive.
Reverting Changes in the Working Directory
Now for a more destructive scenario. You’ve made a bunch of edits to a file in your working directory, but they are all wrong. The changes are un-staged and un-committed. You haven’t run git add yet. You just want to throw away all the changes you made to that file since your last commit and revert it back to the version Git has in its history.
The command for this is also git restore, but without the –staged flag: git restore <file-name>. This will immediately overwrite the file in your working directory with the version from your last commit. Be careful! This operation is destructive. Any changes you made to that file will be permanently lost. Git is “restoring” the file from its database. This is your “Ctrl-Z” for changes you haven’t staged or committed yet.
Amending the Last Commit: Git Commit Amend
This is a very common situation. You just made a commit with git commit -m “My message”. And then, two seconds later, you realize you made a typo in the commit message. Or, even worse, you forgot to git add one of the files that was supposed to be part of that commit. You don’t want to make a new, separate commit just to fix it.
The solution is git commit –amend. If you just want to fix the message, run git commit –amend, and it will open your text editor to let you fix the message. If you forgot a file, simply git add <forgotten-file> and then run git commit –amend. Git will take your staged changes, combine them with the previous commit, and create a new, replacement commit. This is a fantastic tool, but it comes with a major warning: you should never amend a commit that you have already pushed to a remote server and shared with your team.
Reverting a Commit: Git Revert
The “amend” warning brings us to a critical rule: changing public history is bad. If you have pushed a commit and your teammates have pulled it, you must not change that commit. But what if that commit introduced a bug? You can’t amend it, but you need to undo it.
The safe way to undo a public commit is with git revert. git revert <commit-hash> will create a new commit that is the exact inverse of the bad commit. If the bad commit added a line of code, the “revert commit” will remove that same line. The bad commit remains in the project’s history, but its effect is cancelled out by the new revert commit. This preserves the project’s history, showing a clear record of “we added this, then we realized it was a mistake, so we undid it.” This is the correct and safe way to fix mistakes in a collaborative, public history.
Resetting History: Git Reset
This is the “dangerous” tool. git reset is a powerful command that can permanently rewrite your local history. It’s often used to undo local commits before you share them. git reset has three main modes, but the one you might see used is git reset –hard <commit-hash>. This command will do two destructive things: it will move your current branch pointer back to the specified commit, and it will discard all changes in your working directory and staging area that came after it.
This is a way to “rewind” your local branch, permanently throwing away the commits you made. This is useful if you made a series of bad commits locally and just want to start over from a known-good point. However, like git commit –amend, you must NEVER use git reset on a branch that you have shared with others. It changes the history, and if your teammate tries to pull, it will cause a massive conflict and corrupt their repository. Use revert for public changes, and reset only for private, local cleanup.
Rewriting History: Interactive Rebase
The most powerful history-editing tool in Git is “interactive rebase,” or git rebase -i. This tool is like git reset but on steroids. It’s typically used on a feature branch before you merge it or open a pull request. Imagine your feature branch has 10 small, messy commits like “work in progress,” “fix typo,” “oops,” and “final changes.” You don’t want that messy history in your main branch.
You can run git rebase -i main from your feature branch. This will open a text editor with a list of all the commits you made. For each commit, you can choose an action. You can “reword” a commit to fix its message. You can “drop” a commit to delete it entirely. And most usefully, you can “squash” a commit, which means “melt this commit into the one before it.” This allows you to take your 10 messy commits and “squash” them down into a single, clean, beautiful commit that says “Implement new login feature.” This is a highly desired skill for keeping a clean project history.
Rebase vs. Merge
This brings us to one of the great debates in the Git world. When you integrate a feature branch into main, you can use git merge (as we saw in Part 4) or you can git rebase your branch onto main first. Merging creates a “merge commit” and preserves the history exactly as it happened, showing the two parallel lines of development. Rebasing, on the other hand, rewrites your feature branch’s history. It takes all your commits and “re-plays” them, one by one, on top of the latest commit from main.
The result is a perfectly linear, clean history, as if you had done all your work in a straight line with no parallel development. Many teams prefer this clean, linear history. The “golden rule of rebase” applies: it is safe and wonderful to rebase your own private feature branch to clean it up. You must NEVER rebase a public, shared branch like main. This is the same as the “don’t change public history” rule. Rebasing changes history, so only do it on your own, un-shared branches.
Finding What You Need: Git Grep and Git Bisect
As your project grows, it can be hard to find things. git grep is a useful tool that lets you search for a string or regular expression in all the files tracked by Git. It’s like a normal search, but it’s “repository-aware” and very fast.
An even more magical tool is git bisect. Imagine you discover a bug, but you know it worked correctly two months ago. This means one of the hundreds of commits made in the last two months introduced the bug, but you don’t know which one. git bisect is an automated tool that helps you find it. You tell it, “This commit is bad” (git bisect bad) and “This old commit is good” (git bisect good …). Git then uses a binary search algorithm. It checks out a commit in the middle and asks you, “Is this good or bad?” Based on your answer, it cuts the remaining commits in half and repeats. In a few quick steps, it can pinpoint the exact commit that introduced the bug.
Tracking Important Points: Git Tag
As you work, you will reach important milestones, suchci as a public release. A commit is just a long hash, but you might want a human-readable “tag” for that specific snapshot. This is what git tag is for. You can create a “lightweight” tag, which is just a pointer, but it’s better to create an “annotated” tag.
The command git tag -a v1.0.0 -m “Version 1.0.0 release” will create an annotated tag named v1.0.0 on your current commit, with an attached message. This creates a permanent, named reference to that exact point in your project’s history. By default, tags are not sent to the remote repository when you git push. You must push them explicitly by running git push origin v1.0.0 or git push –tags to push all your tags at once. This lets your team and users easily check out a specific release version of your code.
A Structured Learning Plan
Mastering Git is a journey, and it’s best to follow a structured plan. Trying to learn everything at once is overwhelming. Instead, break it down into manageable weekly goals. For your first week, focus only on the “What” and “Why.” Understand the concept of version control, what “distributed” means, and the three-state model (working, staging, repo). Install Git and run the git config commands. Don’t even touch a repository yet; just absorb the concepts.
In your second week, focus on the local workflow. Create a new repository with git init. Practice the “modify, add, commit” cycle over and over. Get completely comfortable with git status and git log. In the third week, introduce remotes. Create a repository on a hosting platform, use git remote add, and practice git push and git clone. In the fourth week, dedicate the entire week to git branch. Create them, switch between them, and merge them. The following weeks can be dedicated to tackling one new topic at a time: merge conflicts, pull requests, and then the more advanced tools like rebase and revert.
Best Ways to Learn Git
People learn in different ways, so you shouldn’t rely on a single method. Interactive online courses are a fantastic way to start. Many platforms offer browser-based tutorials where you type commands directly and get immediate feedback. This “learn by doing” approach is perfect for building muscle memory. In-depth video tutorials are also valuable, as you can watch an expert walk through a real-world workflow, explaining their thought process as they encounter and solve problems.
Don’t overlook books, either. While Git is a digital tool, many of the best explanations of its core concepts are found in well-written books. These can provide a level of depth that a short video cannot. Finally, as you get more comfortable, make “cheat sheets” your best friend. These are one-page summaries of the most common commands. Having one on your desk can be a lifesaver when you can’t remember the exact syntax for a command you only use once a month.
The Power of Official Documentation
When you have a specific question about a command, the best place to get an answer is often the official source. The Git project itself maintains incredibly detailed documentation. In your terminal, you can type git help <command-name> (e.g., git help branch) to open the “man page” (manual page) for that command.
This documentation is dense, technical, and can be intimidating for a beginner. But it is 100% accurate and complete. It describes every single option and flag for a command. As you progress, make it a habit to try and read the official documentation. You will start to understand how the tool is built, and you will discover powerful options and features that are not covered in most beginner tutorials.
Learning by Doing: The Only True Path
You can read every book and watch every video on Git, but you will not learn it until you use it. You cannot learn to swim by reading about water. You must get in the pool. The same is true for Git. The most important tip for learning is to use it for everything.
Starting a new personal project? Use git init. Taking notes for a class? Put them in a folder and use git init. Writing a research paper? Use git init. Even if you are working alone, the practice of using the “add, commit” cycle will build the habit. It will give you a safety net to revert changes. And it will give you a safe, private place to practice the more “dangerous” commands like reset and rebase without any fear of breaking a team’s project. The more you use it, the more it will become second nature.
Creating Your Project Portfolio
For anyone looking for a job in tech or data, a well-maintained portfolio on a Git hosting platform is essential. But a good portfolio is not just a collection of folders with your final code. A great portfolio shows your process. When a recruiter looks at your project, they will also look at your commit history.
A project with a single commit that says “Final project” is a red flag. A project with a clean history of 20 well-written, atomic commits that shows the progression of your work is a massive green flag. It shows that you know how to use Git professionally. It shows you are organized, you can communicate your work, and you understand the development process. Your commit history is part of your portfolio. Use good branch management, write clear commit messages, and “squash” your messy work-in-progress commits before you finalize the project.
Contributing to Open-Source Projects
Once you have some confidence, the ultimate way to practice and demonstrate your skills is to contribute to an open-source project. This is the “final exam” of your Git learning. It combines all the skills you’ve learned: cloning, branching, committing, pushing, and (most importantly) the “pull request” workflow.
Start small. You don’t have to write a complex new feature. Find a project you use, look at their “Issues” page, and find a simple bug or a request for documentation. A typo fix is a perfectly valid and welcome contribution. You will learn how to “fork” a project, create a branch, make your change, and submit a pull request. You will also learn the “soft skill” of interacting with the project maintainers, responding to feedback, and collaborating in public. This is an invaluable experience that recruiters love to see.
Git Best Practices for Professionals
As you transition from a learner to a professional, you should adopt a set of best practices.
- Commit Often, Commit Small: Your commits should be “atomic.” Each commit should represent one single, logical change. This makes your history easy to read and makes it easier to use tools like revert or bisect.
- Write Good Messages: Follow the 50-character subject line and imperative mood convention. Explain the “why” in the body.
- Branch for Everything: Never commit directly to main. Every new feature, bug fix, or experiment, no matter how small, gets its own branch.
- Do Not Push Broken Code: Your main branch should always be stable. Test your code on your feature branch before you open a pull request.
- Review Code Before Merging: The pull request is the most important quality gate. Read the code, ask questions, and be a good partner to your teammates.
Joining a Community
You don’t have to learn in isolation. There are massive online communities of developers who are eager to help. On popular Q&A websites, you can find answers to almost any Git-related question you can imagine. On large forum-style sites, there are entire sections dedicated to version control where you can ask for advice, share your projects, and learn from experts.
These communities are a great way to get “unstuck” when you hit a problem you can’t solve. When you ask a question, be sure to provide context. Explain what you tried, what you expected to happen, and what actually happened (including any error messages). This will help others help you. As you get more experienced, you can give back by answering questions from other beginners, which is a great way to solidify your own knowledge.
Beyond the Basics: Git Hooks and Automation
Once you have truly mastered Git, you can start to explore its advanced automation features. “Git hooks” are one such feature. These are custom scripts that you can place in your .git/hooks directory that Git will automatically run at specific events. For example, you can have a “pre-commit” hook. This script will run before a commit is finalized. You could use this to automatically check your code for style errors or run a quick test. If the script fails, Git will abort the commit, forcing you to fix the problem.
This is the gateway into the world of DevOps and CI/CD (Continuous Integration / Continuous Deployment). These hooks can be used to trigger automated build servers, run comprehensive test suites, and even deploy your website automatically every time a new commit is merged into the main branch. This shows how Git is not just a personal tool but the foundational engine for modern, automated software development.
Final Thoughts
Learning Git is an investment that will pay dividends for your entire career. It is a foundational, “force-multiplier” skill. It makes you a better collaborator, a more organized-practitioner, and a more attractive candidate for any job that involves digital work. It has become a necessity to survive in the competitive job market, especially on the tech side. Recruiters and hiring managers prefer candidates who understand this workflow because it supports more efficient and safer team collaboration.
Your journey will start with basic commands and online tutorials. But it should quickly move to real-world, hands-on projects. Building a solid portfolio that shows your Git process is as important as the code itself. Mastering Git is a lifelong journey; new tools and workflows will emerge. But the core concepts of snapshots, branches, and a clean history will remain with you, making you a more effective and professional developer, data scientist, or creator.