Skip to main content

User account menu

  • Log in

Main navigation

  • All Articles
  • Home

Breadcrumb

  1. Home

Git Tutorial for Beginners: Learn Git in 1 Hour

By admin on Fri, 29 Aug 2025 - 08:47
Article Type
tutorial
Video url
https://www.youtube.com/watch?v=8JJ101D3knE

Tutorial: Git Tutorial for Beginners: Learn Git in 1 Hour

Prerequisites

  • None explicitly stated, but implies basic computer literacy and comfort with command line interfaces.

What You'll Learn

  • A good grasp of Git basics for beginners.
  • Understanding fundamental Git concepts and essential commands used daily.
  • Readiness to learn more about intermediate to advanced Git concepts.
  • Ability to track project history and collaborate effectively with others.
  • Confidence in using the command line for Git operations.

In this tutorial, you will gain a strong foundation in Git, the most widely used version control system. This article will guide you through understanding fundamental Git concepts and essential commands used daily. By following the steps outlined, you will learn how to track project history, collaborate effectively with others, and confidently use the command line for Git operations.

Table of Contents

  1. Introduction to Git
  2. Installing Git
  3. Basic Git Workflow
  4. Viewing History
  5. Advanced Topics

Step 1: Introduction to Git

Git is the most popular distributed version control system.

If you're an absolute beginner or have been using Git for a while but never truly understood its mechanics, this tutorial is designed for you. You will grasp all fundamental concepts and essential commands used daily. By the end, you'll have a solid understanding of the basics and be prepared for intermediate to advanced topics.

Understanding Git and Its Popularity
Git stands as the world's most popular version control system (VCS). A VCS tracks changes made to code over time within a special database called a repository. This allows you to:

  • Review your project's history, identifying who made what changes, when, and why.
  • Easily revert your project to an earlier state if mistakes occur.

Without a VCS, managing code changes would involve constantly creating copies of the entire project in various folders, a method that is slow and doesn't scale, especially when multiple individuals are working on the same project. This would lead to continuously emailing the latest code and manually merging changes. In essence, a VCS enables project history tracking and collaborative work.

Centralized vs. Distributed Version Control Systems
Version control systems are categorized into two main types:

  1. Centralized Systems: In a centralized system, all team members connect to a central server to retrieve the latest code and share their changes. Examples include Subversion and Microsoft Team Foundation Server. A key drawback of this architecture is the single point of failure; if the central server goes offline, collaboration ceases, and project snapshots cannot be saved until the server is back online.
  2. Distributed Systems: Distributed systems overcome the limitations of centralized systems. Every team member possesses a full copy of the project history on their local machine. This allows you to save project snapshots locally, and if the central server is unavailable, team members can synchronize their work directly with each other. Git and Mercurial are examples of distributed version control systems.

Git is the most popular distributed VCS globally due to several factors:

  • Free and Open-Source: It is available at no cost and its source code is publicly accessible.
  • Speed and Scalability: Operations like branching and merging, which can be slow and challenging in other VCS like Subversion or TFS, are exceptionally fast and efficient in Git.

Git's widespread adoption is evident, with over 90% of software projects worldwide utilizing it. Consequently, almost every software developer job description lists Git as a required skill. Therefore, having an in-depth understanding of Git—how it works and how to use it for tracking project history and collaborating effectively—is a must-have for any aspiring software developer.

Ways to Use Git

You can interact with Git in several ways:

  1. Command Line:

    • Open a terminal or command prompt window to execute Git commands.
    • This is often the fastest and easiest way to perform tasks, which is why many developers prefer it.
  2. Code Editors and IDEs (Integrated Development Environments):

    • Most modern code editors and IDEs come with built-in support for basic Git features. For instance, Visual Studio Code (VS Code) includes a Source Control panel providing essential Git functionalities.
    • Extensions are also available to enhance Git features within these environments. GitLens is a popular VS Code extension that adds many Git features.
  3. Graphical User Interfaces (GUIs):

    • There are dedicated GUI tools specifically designed for Git. The Git website (git-scm.com) lists a comprehensive collection of these tools for various platforms (Windows, Mac, Linux, Android, iOS).
    • Among these, GitKraken and SourceTree are two of the most popular:
      • GitKraken: This is a preferred GUI tool, known for its beautiful design and cross-platform compatibility. It integrates with other GitKraken products like GitKraken Boards for issue tracking and GitKraken Timelines for project management. It is free for open-source projects but requires an annual fee for commercial use.
      • SourceTree: This tool is completely free but is only available for Windows and Mac. Linux users would need to opt for GitKraken or another GUI tool.

In this course, the primary focus will be on using Git via the command line for two main reasons:

  • GUI Limitations: Most GUI tools have limitations, supporting only the most frequently used Git commands. There will be instances where you'll need to resort to the command line to complete a task, even if you primarily use a GUI.
  • Command Line Availability: The command line is always accessible. In situations where a GUI tool might not be available (e.g., connecting to a remote server without installation permissions), knowing the command line is crucial to avoid being stuck.

In practice, many developers, including myself, use both the command line and a GUI tool. This course will demonstrate both approaches. There are scenarios where a GUI tool is more intuitive and efficient, while other times, the command line is quicker and simpler. The goal is to use the right tool for the job. Avoid the pitfall of developers who exclusively use the command line and dismiss GUI users; choose the method that best suits the task.

While the command line will be central to this course, examples using VS Code and GitKraken will be provided when a GUI tool is beneficial, as they are widely popular. If you are new to the command line, don't worry; this guide will walk you through everything step-by-step. It's often much easier than it seems.

Installing Git

Before proceeding, let's verify if Git is installed on your machine and determine its version.

  1. Open your terminal or command prompt:

    • Mac: Press Command + Space and type terminal.
    • Windows: Click the search icon in the bottom navigation bar and type cmd. Your terminal window's appearance may vary, but its function for executing Git commands remains the same.
  2. Check Git version:

    • In the terminal, type git --version.
    • For example, a machine might show Git version 2.19.2, but the latest version at the time of recording this video is 2.27.0. It is highly recommended to download and install the latest version.
  3. Download and Install Git:

    • Go to git-scm.com/download.
    • You will find instructions for installing Git on various operating systems. The process is straightforward.
    • Windows Users: After installation, you will get an application called Git Bash (short for Bourne Again Shell). This is a command prompt window that emulates Unix/Linux environments. Throughout this course, it is recommended to use Git Bash instead of the built-in Windows Command Prompt for a more consistent experience with the examples provided.

Customizing Your Git Environment

The first time you use Git, you must specify a few configuration settings: your name, email, default editor, and how Git should handle line endings. These settings can be configured at three levels:

  1. System Level: Settings here apply to all users on the current computer.
  2. Global Level: Settings here apply to all repositories for the current user. This is the most common level for personal configurations.
  3. Local Level: Settings here apply only to the current repository or the repository in the current folder. This allows for specific configurations per project.

Configure Global Settings:

  1. Set your name:

    git config --global user.name "Your Name"
    
    • Use double quotes around your name if it contains spaces.
  2. Set your email:

    git config --global user.email your.email@example.com
    
    • Double quotes are not needed for email addresses without spaces.
  3. Set your default editor:

    • On Mac, Git defaults to vim, which can be challenging for beginners. This course uses Visual Studio Code (VS Code).
    • If you don't have VS Code, download the latest version from code.visualstudio.com. Ensure VS Code is added to your system's PATH so you can open it directly from the terminal by typing code. (Troubleshoot PATH issues based on your operating system if code command doesn't work.)
    • To set VS Code as your default editor:
      git config --global core.editor "code --wait"
      
      • "code --wait": This command launches VS Code and the --wait flag tells the terminal to wait until the VS Code instance is closed before regaining control.
  4. Edit Global Configuration File (Optional - for verification):

    • All these settings are stored in a text file. You can open and edit this file using your default editor:
      git config --global -e
      
    • This will open your ~/.gitconfig file (or C:\Users\YourUser\.gitconfig on Windows). You will see sections for [user] and [core] containing your configured name, email, and editor.
    • The terminal will wait for you to close VS Code.
  5. Configure Line Endings (core.autocrlf):

    • This is a crucial setting that many users overlook.
    • Background: On Windows, end-of-lines (EOL) are marked with two special characters: carriage return (\r) and line feed (\n) (CRLF). On Mac and Linux, EOLs are indicated by only a line feed (\n) (LF). Inconsistent EOL handling can lead to unexpected issues.
    • Scenario: Consider John (Windows) and Julie (Mac) collaborating on the same repository.
      • John (Windows): When John commits code, Git removes the carriage return (\r) character from the EOLs. When he checks out code, Git adds the carriage return character back. To achieve this, set core.autocrlf to true.
      • Julie (Mac/Linux): When Julie checks out code, she doesn't want the carriage return. Git should not add it. However, if a carriage return is accidentally introduced (e.g., by her editor), Git should remove it when storing code in the repository. To achieve this, set core.autocrlf to input.
    • Command to set core.autocrlf:
      • On Windows:
        git config --global core.autocrlf true
        
      • On Mac or Linux:
        git config --global core.autocrlf input
        

Getting Help with Git Commands

You can get help for Git commands in several ways:

  1. Online Documentation:

    • Simply Google "git [command name]" (e.g., "git config") to find comprehensive online documentation with various options and their usage.
  2. Terminal Help (Man Pages):

    • For detailed help directly in your terminal, type git [command name] --help (e.g., git config --help).
    • This displays the full manual page for the command.
    • Press Space to navigate to the next page and q to quit.
  3. Terminal Help (Short Summary):

    • For a quick refresher or a short summary of a command's options, type git [command name] -h (e.g., git config -h).

Project Snapshots and Basic Git Workflow

The first crucial skill in using Git effectively is taking snapshots of your project. This section will cover fundamental Git concepts that are often misunderstood. Even if you think you know the basics, pay close attention, as many people use Git commands without fully understanding their underlying mechanisms, leading to frequent confusion.

Creating a Project Directory and Initializing a Repository:

  1. Create a directory for your project:

    • You can name this directory anything and place it anywhere on your machine. For this example, if you are in a projects directory, create a new directory named moon:
      mkdir moon
      cd moon
      
    • Imagine this moon directory as your project, potentially containing tens or hundreds of files.
  2. Initialize a new empty Git repository:

    • Inside your project directory, run:
      git init
      
    • You will see a message like Initialized empty Git repository in /path/to/your/project/moon/.git/. This indicates that Git has created a hidden subdirectory named .git inside your moon directory.
    • Note: The .git subdirectory is hidden by default because you are not supposed to touch its contents directly.
    • To see hidden files and directories:
      • Run ls -a (short for "all"). This will show the .git subdirectory. On Mac, you can view it in Finder, and on Windows, in File Explorer.
    • Inside .git: This directory (or Git repository) is where Git stores information about your project's history. It contains subdirectories like branches, hooks, info, objects, and references. As a Git user, you don't need to understand this internal structure; it's an implementation detail and should not be modified. Corrupting or removing this directory will result in the loss of your project history.
  3. Demonstrating Consequences of Deleting .git:

    • Notice the green git marker in your terminal prompt (if you have Zsh on Mac or Posh-Git on Windows for a colorful terminal). This indicates you are within a Git repository.
    • If you remove the .git subdirectory:
      rm -rf .git
      
    • The git marker will disappear, indicating that the directory is no longer a Git repository, and your project history for that directory is lost.
    • To re-initialize:
      git init
      
    • The git marker will reappear.

Basic Git Workflow

Now that you have a Git repository, let's explore the basic workflow you'll follow daily:

  1. Modify Files: As you work, you will modify one or more files in your project directory.
  2. Commit Changes: When your project reaches a significant state that you want to record, you will "commit" those changes into your repository. A commit is equivalent to taking a snapshot of your project at that specific moment.
  3. The Staging Area (or Index): Git introduces a special intermediate step called the staging area (or index), which is unique compared to most other version control systems. It represents what you are proposing for your next commit or snapshot.
    • Once you finish making changes, you add the modified files to the staging area.
    • You then review these staged changes.
    • If everything looks correct, you perform a commit. The proposed snapshot is then permanently stored in your repository.
    • The staging area is crucial as it allows you to carefully review your work before permanently recording a snapshot. If some changes should not be part of the next snapshot, you can "unstage" them and commit them later as part of a different snapshot.

Example Walkthrough of the Basic Git Workflow:

  1. Initial State: Your project directory is empty.
  2. Add Files to Working Directory: Create file1.txt and file2.txt.
    • Initially, these files are untracked by Git.
  3. Add Files to Staging Area: Use the git add command to move these files to the staging area. Now, these files are "staged," representing the state proposed for the next commit.
  4. Commit Changes: Use the git commit command to permanently store this snapshot in the repository. Provide a meaningful commit message explaining what this snapshot represents. This is vital for maintaining a useful project history. Each commit will clearly explain the project's state at that time.
    • At this point, you have one commit in your repository.
    • Common Misconception: Many beginners believe the staging area becomes empty after a commit. This is incorrect. The staging area now contains the same snapshot that was just stored in the repository. It acts much like a staging environment in software releases, reflecting either the current production version or the next version destined for production.
  5. Modify Existing File: Suppose you fix a bug and modify file1.txt.
    • The staging area still holds the old version of file1.txt because the new changes haven't been staged yet.
  6. Stage Modified File: Use git add again to stage the changes in file1.txt. Now, the staging area contains the updated content of file1.txt from your working directory.
  7. Commit Changes: Commit these new changes with a message describing the bug fix.
    • Now you have two commits in your repository.
  8. Delete File: You decide file2.txt is no longer needed and delete it from your working directory.
    • However, file2.txt still exists in the staging area (as per the last commit).
  9. Stage Deletion: Use git add to stage this deletion. Git intelligently recognizes that file2.txt was deleted and will remove it from the staging area.
  10. Commit Deletion: Commit this change to permanently record the deletion.
    • You now have three commits in your repository.

Understanding Commits:
Each commit in Git has:

  • A unique identifier (a 40-character hexadecimal string) generated by Git, akin to a revision number.
  • Information about:
    • Who made the changes (author).
    • When the changes were made (date and time).
    • A complete snapshot of your project's state at the time of creation.

Unlike many other VCS that only store deltas (what changed), Git stores the full content of your project with each snapshot. This allows for quick restoration to any earlier state without computing changes. Although storing full content might seem inefficient, Git is highly efficient in data storage, compressing file contents and avoiding duplicate content. As a Git user, you don't need to know the specifics of how Git stores data, as it's an implementation detail that might change. What's essential is knowing that each commit provides a complete project snapshot for easy restoration to previous states.

This basic workflow will be demonstrated in action over the next few lessons.

Adding Files to Your Project

Let's begin by adding a couple of files to our project directory. We will use the echo command, which is a standard Unix/Linux command for writing content to a file, not a Git command. This course primarily uses text files to ensure the workflow is applicable to any programming language.

  1. Create file1.txt:

    echo hello > file1.txt
    
    • The > symbol writes "hello" to file1.txt.
  2. Create file2.txt:

    echo world > file2.txt
    
    • Now you have file1.txt and file2.txt in your project directory.

Checking Status (git status)

When you have new files in a Git-initialized directory, Git doesn't automatically track them. You need to explicitly instruct Git to track them.

  1. Check the status of your working directory and staging area:
    git status
    
    • Initially, you will see Untracked files: listed in red, showing file1.txt and file2.txt. This indicates they are not yet in the staging area.

Adding Files to the Staging Area (git add)

To move file1.txt and file2.txt to the staging area, use the git add command.

  1. Ways to add files to the staging area:

    • Single file: git add file1.txt
    • Multiple files: git add file1.txt file2.txt (separate by space)
    • Patterns: git add *.txt (adds all files with .txt extension)
    • Recursively add entire directory: git add . (adds all files and directories recursively from the current directory)
      • Caution: Be careful with git add . as it adds everything. You generally don't want to add large binary files or log files, as they increase repository size and are not meant for sharing across the team. (Ignoring files will be covered later.)
  2. Add file1.txt and file2.txt:

    git add .
    
    • After executing this, your terminal prompt's indicator might change (e.g., to yellow), signifying that there are changes in the staging area ready to be committed.
  3. Check status again:

    git status
    
    • You will now see new file: listed in green for both file1.txt and file2.txt, indicating they are in the staging area.

Modifying and Re-staging Files

What happens if you modify a file after adding it to the staging area? Git captures a snapshot when git add is run. If you change the file again, a new snapshot needs to be taken.

  1. Modify file1.txt by appending text:

    echo world >> file1.txt
    
    • Using >> appends "world" to file1.txt.
  2. Check status:

    git status
    
    • You will see both file1.txt (green new file: due to the earlier git add) and file1.txt (red modified:) listed. This means the staging area holds the first version of file1.txt, while your working directory has the second version with unstaged changes.
  3. Re-stage the modified file1.txt:

    git add file1.txt
    

    or

    git add .
    
  4. Check status again:

    git status
    
    • Both files should now be listed in green under Changes to be committed:, and there should be no unstaged changes.

Committing Changes (git commit)

Now, with a snapshot in the staging area, you are ready to permanently store it in your Git repository.

  1. Commit with a short message:

    git commit -m "Initial commit"
    
    • -m (for message) allows you to provide a short, single-line description in double quotes.
  2. Commit with a multi-line message (for detailed explanations):

    • If a short message is insufficient for context (e.g., explaining bug fixes or constraints), omit the -m flag:
      git commit
      
    • This will open your default editor (VS Code, as configured earlier).
    • In the editor, the first line is for a short description (ideally less than 80 characters).
    • Add a line break, then type a longer description for more details.
    • Lines starting with # are comments and will be ignored.
    • Example message:
      Initial commit
      
      This is our first commit.
      
    • Save the file and close the editor.
    • Output: Git will report statistics about the commit, such as the number of files changed and insertions/deletions. For instance, 2 files changed, 3 insertions... indicates file1.txt (now "hello\nworld") and file2.txt ("world") contributed to 3 total lines inserted across the two files.
    • Your working directory will now be "clean" (e.g., terminal indicator turns green), meaning its content matches the staging area, which in turn matches the latest commit.

Best Practices for Committing Code

Adhering to these practices will result in a clean, useful project history:

  1. Commit Size:

    • Not too small: Avoid committing every single file update (e.g., "update file1," "update file2"). Such commits are useless.
    • Not too big: Don't wait to implement an entire feature end-to-end before committing. The purpose of committing is to create checkpoints. If you make a mistake, you can easily revert.
    • Commit Often: Strive to commit frequently. In a real-world scenario, you might commit 5 to 10 times a day or more, depending on your work. This is a guideline, not a strict rule. Commit when your code reaches a stable, recordable state.
  2. Logical Change Sets:

    • Each commit should represent a single, logically separate change set.
    • Do not mix concerns: For example, if you fix a bug and then notice a typo, do not commit both in one go. Create two separate commits: one for the typo and one for the bug fix. (If you accidentally stage both, you can unstage them, which will be shown later).
  3. Meaningful Commit Messages:

    • Develop the habit of writing meaningful commit messages. These messages appear in your project's history and are crucial for you and your team members.
    • If your commit represents a single unit of work (as advised above), it will be easier to craft a good message.
    • Wording: Most developers prefer using the present tense for commit messages (e.g., "Fix the bug" instead of "Fixed the bug"). While this is a convention, consistency within your team is more important than strict adherence.

Skipping the Staging Area

While the staging area is generally recommended for reviewing changes, you can skip it if you are 100% confident that your changes don't need review.

  1. Modify file1.txt and commit in one step:
    • Append "test" to file1.txt:
      echo test >> file1.txt
      
    • Your working directory will show changes (e.g., yellow indicator).
    • Commit all modified files directly:
      git commit -a -m "Fix the bug that prevented the users from signing up"
      
      • -a (or --all) automatically stages all modified files before committing. Note that -a does not stage newly created files; they must still be added with git add.
    • Output: Git confirms the commit, showing 1 file changed, 1 insertion....
    • Caution: Use this only when you are certain. In 99% of cases, you should stage your code before committing.

Removing Files (git rm)

To remove a file from your project, you must remove it from both your working directory and the staging area.

  1. Initial Method (Standard rm command):

    • If you delete file2.txt using the standard Unix rm command:
      rm file2.txt
      
    • Check status: git status will show deleted: file2.txt in red under Changes not staged for commit:.
    • Verify staging area: Running git ls-files (lists files in the staging area) will show file2.txt is still there.
    • Stage the deletion: You would then need to stage this change:
      git add file2.txt
      
    • git ls-files would now confirm file2.txt is gone from the staging area.
    • Commit the deletion:
      git commit -m "Remove unused code"
      
  2. Recommended Method (Git's git rm command):

    • Git provides a command that performs both the deletion from the working directory and the staging of that deletion in one step.
    • Remove file2.txt directly using git rm:
      git rm file2.txt
      
    • This command removes the file from both the working directory and the staging area instantly.
    • You can also specify multiple files or patterns (e.g., git rm *.txt).

Renaming and Moving Files (git mv)

Renaming or moving files in Git is conceptually a two-step operation: a deletion of the old file and an addition of a new file. Git provides a specialized command to simplify this.

  1. Initial Method (Standard mv command):

    • Suppose file1.txt is the only file. Rename it to main.js using the standard Unix mv command:
      mv file1.txt main.js
      
    • Check status: git status will show deleted: file1.txt and untracked files: main.js (both in red), because standard mv doesn't inform Git about the logical rename.
    • Stage changes: You would then need to stage both the deletion and the addition:
      git add file1.txt  # Stages the deletion of file1.txt
      git add main.js    # Stages the addition of main.js
      
    • Check status again: git status would now recognize the rename: renamed: file1.txt -> main.js (in green, indicating staged).
  2. Recommended Method (Git's git mv command):

    • Git provides a command that handles the rename/move operation for both the working directory and the staging area automatically.
    • Rename main.js to file1.js directly using git mv:
      git mv main.js file1.js
      
    • Check status: git status will instantly show renamed: main.js -> file1.js (in green, meaning staged).
    • Commit the rename:
      git commit -m "Refactor code"
      
      • Statistics: A rename will result in 1 file changed, 0 insertions, 0 deletions if only the name changed, demonstrating Git's efficiency in tracking.

Ignoring Files and Directories (.gitignore)

In almost every project, you will need to tell Git to ignore certain files or directories, such as log files, temporary files, or compiled binaries, which are generated artifacts and don't need to be version-controlled or shared. Adding them would unnecessarily bloat your repository.

  1. Create a log directory and file:

    mkdir logs
    echo hello > logs/dev.log
    
  2. Check status:

    git status
    
    • Git will report Untracked files: logs/ with dev.log inside. We don't want to add this to the staging area.
  3. Create a .gitignore file:

    • To prevent Git from tracking the logs directory, create a special file named .gitignore in the root of your project. This file has no name, only an extension.
    echo logs/ > .gitignore
    
    • Open .gitignore (optional): You can open this file in your editor (e.g., code .gitignore).
    • Inside .gitignore, logs/ means ignore the logs directory. You can add multiple entries, use patterns (e.g., *.log to ignore all .log files), and comments (lines starting with #).
  4. Check status again:

    git status
    
    • Git will no longer list logs/ as untracked. Instead, it will show .gitignore itself as a new untracked file, as it is part of your project's configuration.
  5. Stage and commit .gitignore:

    git add .gitignore
    git commit -m "Add .gitignore"
    

Important Note on .gitignore:
.gitignore only works for files or directories that Git is not already tracking. If you accidentally commit a file or directory, and later add it to .gitignore, Git will not ignore it. You must explicitly remove it from Git's tracking first.

Removing Accidentally Committed Files from Tracking:

Let's illustrate how to fix the issue where a file/directory was accidentally committed and is now being tracked despite being in .gitignore.

  1. Create a bin directory and a binary file:

    mkdir bin
    echo hello > bin/app.bin
    
  2. Accidentally commit bin:

    git add .
    git commit -m "Add bin"
    
    • Now, every time app.bin changes (e.g., after a recompile), Git will report it as modified, which is undesirable.
  3. Add bin/ to .gitignore:

    echo bin/ >> .gitignore
    
    • Check status git status: It will say .gitignore is modified.
    • Commit .gitignore update:
      git add .
      git commit -m "Include bin/ in .gitignore"
      
  4. Confirm the issue:

    • Modify app.bin:
      echo hello world > bin/app.bin
      
    • Check status git status: Git will still report bin/app.bin as modified. This is because Git is already tracking it.
  5. Remove from Staging Area (i.e., Git's tracking) using git rm --cached:

    • We need to remove bin/app.bin from the staging area without deleting it from the working directory. The git rm command handles this with the --cached option.
    • View files in staging area: git ls-files will show bin/app.bin.
    • Remove from staging area recursively:
      git rm --cached -r bin/
      
      • --cached: Removes only from the index (staging area).
      • -r: Required for recursive removal of directories.
    • Verify removal: git ls-files will no longer list bin/app.bin.
    • Check status: git status will now show deleted: bin/app.bin (in green, indicating a staged deletion). This means the commit will record that bin/app.bin is no longer tracked.
  6. Commit the untracking:

    git commit -m "Remove the bin directory that was accidentally committed"
    
    • From this point forward, Git will no longer track changes in the bin directory or app.bin.
    • Test: If you modify bin/app.bin again (e.g., echo test > bin/app.bin), git status will show a clean working directory because Git is now ignoring it.

.gitignore Templates:
You can find various .gitignore templates for different programming languages (e.g., Java, Python, Node.js) on GitHub directly (e.g., github.com/github/gitignore). These templates suggest common files to ignore (e.g., .class files in Java, node_modules in Node.js) that are typically generated during compilation or dependency installation. Lines starting with # are comments.

Displaying Status with git status --short (git status -s)

The default output of git status is comprehensive but often verbose. A shorter, more concise output is available using the --short or -s flag.

  1. Modify an existing file and create a new one:

    • Append sky to file1.js:
      echo sky >> file1.js
      
    • Create file2.js:
      echo sky > file2.js
      
  2. Check full status (for comparison):

    git status
    
    • This will show modified: file1.js (red) and Untracked files: for file2.js.
  3. Check short status:

    git status --short
    
    • Alternatively, git status -s.
    • Output interpretation:
      • The output has two columns: the left column represents the staging area, and the right column represents the working directory.
      • M file1.js: The M in the right column indicates file1.js is modified in the working directory. There's no entry in the left column because these changes are not yet staged.
      • ?? file2.js: The ?? indicates file2.js is a new untracked file, not in either the staging area or tracked by Git.
  4. Add file1.js to staging and re-check short status:

    git add file1.js
    git status -s
    
    • Output: M file1.js (green M in left column) and ?? file2.js.
      • The M in the left column signifies that file1.js has changes staged. The right column is now empty for file1.js because all its modifications are in the staging area.
  5. Re-modify file1.js and re-check short status (demonstrating re-staging):

    • Append ocean to file1.js:
      echo ocean >> file1.js
      git status -s
      
    • Output: MM file1.js.
      • The green M in the left column indicates existing staged changes for file1.js.
      • The red M in the right column indicates further unstaged modifications in the working directory that need to be re-staged.
  6. Re-stage file1.js again:

    git add file1.js
    git status -s
    
    • Output: M file1.js. All changes for file1.js are now in the staging area.
  7. Add file2.js to staging and re-check short status:

    git add file2.js
    git status -s
    
    • Output:
      • M file1.js
      • A file2.js (green A in the left column): indicates file2.js was newly added to the staging area.

This detailed breakdown helps understand how git status -s conveniently summarizes the state of your files.

Reviewing Changes Before Committing (git diff)

Before committing, it's best practice to review what's in your staging area to avoid committing bad or broken code. While git status shows which files are affected, git diff shows the exact line-by-line changes.

  1. View Staged Changes (what will go into the next commit):

    git diff --staged
    
    • Output Analysis (git diff):
      • Header: diff --git a/file1.js b/file1.js indicates a comparison between file1.js from the old version (a/) and the new version (b/).
      • Old vs. New: The "old copy" refers to what's in the last commit. The "new copy" refers to what's currently in the staging area.
      • Index: index ... is metadata and not typically important for users.
      • Legend:
        • Lines prefixed with - (red) denote changes in the old copy (removed lines).
        • Lines prefixed with + (green) denote changes in the new copy (added lines).
      • Chunk Headers (@@): @@ -1,3 +1,5 @@ signifies a "chunk" of changes.
        • -1,3: From the old copy, starting at line 1, 3 lines were extracted.
        • +1,5: From the new copy, starting at line 1, 5 lines were extracted.
      • Content: The actual lines of code, with + for new lines (e.g., "sky", "ocean").
      • New File Diff: For file2.js, you'd see diff --git a/dev/null b/file2.js. a/dev/null indicates no old copy existed (it's a new file), and 0,0 +1,1 in the chunk header means 0 lines from line 0 in the old copy, vs 1 line from line 1 in the new copy.
  2. View Unstaged Changes (what's in your working directory but not yet staged):

    git diff
    
    • This command compares your working directory with your staging area.
    • Example: If you modify file1.js (e.g., change "hello world" to "hello world!") and don't stage it, git diff will show:
      • diff --git a/file1.js b/file1.js
      • --- a/file1.js (old copy, which is the staged version)
      • +++ b/file1.js (new copy, which is the working directory version)
      • Headers and content showing hello (removed from staging) and hello world (added in working directory, implying a character change).

To Recap git diff:

  • git diff: Shows unstaged changes (Working Directory vs. Staging Area).
  • git diff --staged: Shows staged changes (Staging Area vs. Last Commit).

Visual Diff Tools (git difftool)

While the terminal git diff is useful, visual diff tools offer a much better experience for comparing files.

  1. Configure VS Code as your default diff tool:

    • You need to set two global configuration settings.
    • Name your diff tool:
      git config --global diff.tool vscode
      
      • Here, "vscode" is just a chosen name for the tool.
    • Define how to launch VS Code for diffing:
      git config --global difftool.vscode.cmd "code --wait --diff \"$LOCAL\" \"$REMOTE\""
      
      • code --wait --diff: Launches VS Code for diffing and waits until the VS Code instance is closed.
      • "$LOCAL" and "$REMOTE": These are placeholders that Git replaces with the paths to the old and new copies of the files being compared. Ensure these are accurately typed, including the double quotes.
    • Verify configuration (optional):
      git config --global -e
      
      • This opens your ~/.gitconfig file. Look for [diff] and [difftool "vscode"] sections to confirm the settings.
  2. Using git difftool:

    • View unstaged changes:

      git difftool
      
      • If you have only one modified file (e.g., file1.js), Git will ask if you want to launch VS Code for that file. Type y or yes.
      • VS Code will open side-by-side, showing the old copy (from staging area) on one side and the new copy (from working directory) on the other. This visual comparison makes it easy to see line deletions (red) and additions (green).
      • Close VS Code to return control to the terminal.
    • View staged changes:

      git difftool --staged
      
      • This will launch your visual diff tool to compare the files in the staging area against the last commit.
      • Git will prompt you for each affected file. You can choose to view each diff or skip some.

Note: While git difftool is covered for completeness, many modern editors and IDEs (like VS Code) have built-in diff viewers that automatically show staged and unstaged changes within the environment, making git difftool less frequently used.

Viewing Commit History (git log)

To see the history of your commits, use the git log command.

  1. View full history:

    git log
    
    • Output:
      • Commits are listed from newest to oldest.
      • Each commit shows:
        • Unique Identifier (SHA-1 hash): A 40-character hexadecimal string generated by Git (e.g., commit d601b90...). This acts like a revision number but is not sequential.
        • HEAD -> master: master represents the main branch (or main line of work). HEAD is a pointer to the current branch you are working on. This will be explained further in the course's branching section.
        • Author: Name and email of the committer.
        • Date: Date and time the commit was created.
        • Commit Message: The one-line or multi-line description you provided.
      • If there are many commits spanning multiple pages, press Space to go to the next page, and q to quit.
  2. View concise history (--oneline):

    git log --oneline
    
    • This shows a short summary: the first 7 characters of the commit ID and the one-line description. It omits author and date/time.
  3. Reverse sort order (--reverse):

    git log --oneline --reverse
    
    • This shows commits from oldest to newest.

The git log command is powerful and will be explored further in the "Browsing History" section. For now, focus on these basic views.

Inspecting Commits (git show)

To view the exact changes within a specific commit, use the git show command.

  1. Inspect a commit by its ID:

    • You can use the full 40-character commit ID or a shorter, unambiguous prefix (e.g., first 7 characters).
    git show d601b90
    
    • Output: Displays the commit message, author, date, and a diff showing what changed in that commit. For example, if a .gitignore file was modified, it might show lines removed (-) and lines added (+).
  2. Inspect a commit using HEAD reference:

    • HEAD always points to the latest commit on your current branch.
    • git show HEAD: Shows the latest commit.
    • Relative references (~): To refer to previous commits:
      • git show HEAD~1 (or git show HEAD~): Shows the commit one step before HEAD.
      • git show HEAD~2: Shows the commit two steps before HEAD.
    • This is useful for quickly navigating history without knowing commit IDs.
  3. View the final version of a file in a specific commit:

    • To see the exact content of a file as it existed in a particular commit, not just the diff:
    git show HEAD~1:.gitignore
    
    • HEAD~1: Refers to the commit one step before the current HEAD.
    • :.gitignore: Specifies the file path within that commit. This will display the content of .gitignore at that specific historical point.

Understanding Git's Internal Structure (Blobs and Trees - git ls-tree)

Git stores a complete snapshot of your working directory in each commit, not just deltas. While git show displays differences, you can also view all files and directories within a commit. This brings us to the concept of "trees" in Git.

  • Tree: In Git, a "tree" is a data structure representing hierarchical information, similar to how a directory structure works (directories contain files and subdirectories).
  • Blob: A "blob" (Binary Large Object) is how Git stores the content of files.
  1. List files and directories in a commit (git ls-tree):

    git ls-tree HEAD
    
    • This command lists the contents of the commit pointed to by HEAD.
    • Output:
      • For each item, you'll see a type (e.g., blob for files, tree for directories), its unique content-based identifier, and its filename/path.
      • Example: 100644 blob b2f0e0e0... file1.js, 040000 tree d1c7e7e7... bin.
  2. Inspect individual objects (Blobs or Trees) with git show:

    • You can use git show with the unique identifier of a blob or tree to inspect its contents.
    • View a blob (file content):
      git show b2f0e0e0
      
      • (Replace b2f0e0e0 with an actual blob ID from your git ls-tree output). This will display the content of file1.js as it was stored in that blob.
    • View a tree (directory content):
      git show d1c7e7e7
      
      • (Replace d1c7e7e7 with an actual tree ID). This will show a list of files and subdirectories within that tree object.

Thus, git show allows you to view various objects in Git's database: commits, blobs (files), and trees (directories).

Undoing Changes with git restore (and git clean)

It's common to make changes that you later decide to discard or revert. Git provides robust tools for this. git restore is a newer, less confusing command for undoing changes compared to the older git reset. (Ensure you are using Git version 2.28 or later for restore).

Undoing git add (Unstaging Changes):

Suppose you staged changes in file1.js but decide they shouldn't be part of the next commit. You want to "undo" the git add operation.

  1. Unstage file1.js (from staging area to working directory):

    git restore --staged file1.js
    
    • --staged: Tells Git to restore the file in the staging area.
    • When you restore a file in the staging area, Git takes the copy from the next environment, which is the last commit in the repository, and places it into the staging area.
    • Check status (git status -s): The staged changes for file1.js will disappear, and the changes will reappear as unstaged modifications in your working directory.
  2. Understanding git restore with newly added files:

    • Consider file2.js that was newly added (git add .) and is now staged (A file2.js in git status -s).
    • This file doesn't exist in the last commit.
    • If you run git restore --staged file2.js, Git will remove file2.js from the staging area. Since there's no previous version in the repository, it effectively makes file2.js an untracked file again.
    • Check status (git status -s): ?? file2.js

Discarding Local Changes (Undoing modifications in Working Directory):

Sometimes you make changes in your working directory that you want to completely throw away and revert to the last staged or committed version.

  1. Example: Modify file1.js (e.g., using echo). git status -s will show M file1.js.

  2. Discard local changes in file1.js:

    git restore file1.js
    
    • When you execute this, Git will take the version of file1.js from the staging area (or the last commit if not staged) and copy it into your working directory, overwriting your local modifications.
    • You can also use git restore . to discard all local changes in the current directory.
  3. Handling Untracked Files after discarding local changes (git clean):

    • After git restore ., if you still have untracked files (e.g., ?? file2.js), git restore won't remove them. This is because Git isn't tracking them and doesn't have a previous version to revert to.
    • To remove untracked files: Use the git clean command.
    • Caution: git clean is a dangerous operation because discarded untracked files cannot be recovered.
    • View git clean help: git clean -h
    • Force remove untracked files and directories:
      git clean -fd
      
      • -f (force): Required to actually remove files.
      • -d: Also removes untracked directories.
    • Check status: git status -s will now show a clean working directory (no untracked files).

Restoring a Deleted File to a Previous Version

Git stores every version of a tracked file, so you can always restore a file to a previous version, even after deleting it.

  1. Delete a tracked file:

    • Use git rm file1.js to remove it from both the working directory and staging area, and stage the deletion.
    • Check status: git status -s will show D file1.js.
    • Commit the deletion:
      git commit -m "Delete file1.js"
      
  2. Realize the mistake and restore file1.js:

    • You want to restore file1.js from the commit before the deletion.
    • Check history: git log --oneline to find the commit ID before the delete commit.
    • Restore the file:
      git restore --source HEAD~1 file1.js
      
      • --source HEAD~1: Tells Git to get the version of file1.js from the commit one step before the current HEAD (i.e., the commit before the deletion).
    • Result: file1.js will reappear in your working directory as an untracked file (?? file1.js in git status -s). This is because Git restored the file's content but does not automatically re-track it; you would need to git add and git commit it again if you want it tracked.

This concludes the introductory section to Git, equipping you with fundamental concepts and commands to get started.

Step 2: Installing Git

First, you need to determine if Git is already installed on your machine and, if so, which version.

  1. Check for Existing Git Installation

    • Open your terminal or command prompt window.
      • On Mac, press Command + Space and type terminal.
      • On Windows, click the search icon on the bottom navigation bar and type cmd.
    • Once the terminal or console window is open, type the following command and press Enter:
      git --version
      
    • This command displays the installed Git version. For example, you might see git version 2.19.2. It is highly recommended to download and install the latest stable version of Git, which at the time of this recording is 2.27.0.
  2. Download and Install the Latest Git Version

    • Go to the official Git website: git-scm.com/download.
    • Follow the instructions provided on the website for installing Git on your specific operating system. The installation process is generally straightforward.
    • For Windows Users: After installing Git, you will find an application called Git Bash (short for Born Again SHell). This is a command prompt window that emulates Unix or Linux environments. It is recommended to use Git Bash throughout this course as it provides a consistent environment with the demonstrations.
  3. Configure User Identity
    The first time you use Git, you must specify a few configuration settings, including your name, email, default text editor, and how Git should handle line endings. These settings can be configured at three levels:

    • System Level: Settings here apply to all users on the current computer.
    • Global Level: Settings here apply to all repositories for the current user.
    • Local Level: Settings here apply only to the current repository or the repository in the current folder. This allows for different settings for different projects.

    To configure your user identity:

    • In your terminal window (or Git Bash for Windows), enter the following commands to set your global username and email. The --global flag indicates that these settings should apply to all your repositories.
    • Set User Name:
      git config --global user.name "Your Name"
      
      • Note: Use double quotes around your name if it contains spaces.
    • Set User Email:
      git config --global user.email your.email@example.com
      
      • Note: Double quotes are not necessary here unless your email address contains spaces.
  4. Configure Default Text Editor
    Git requires a default text editor for certain operations, such as writing commit messages that span multiple lines.

    • On Mac, if you don't set a default editor, Git will use Vim by default, which can be challenging for beginners.
    • For this course, Visual Studio Code (VS Code) will be used as the default editor. If you want to follow along, download the latest version of VS Code from code.visualstudio.com. Ensure VS Code is added to your system's PATH environment variable so you can launch it from any directory by typing code. If code does not work in your terminal, you might need to troubleshoot how to add VS Code to your PATH for your specific operating system.
    • To set VS Code as your default Git editor:
      git config --global core.editor "code --wait"
      
      • Explanation:
        • core.editor: This is the setting for the default editor.
        • "code --wait": This tells Git to use the code command (VS Code) and the --wait flag ensures the terminal waits until you close the VS Code instance that Git opened before returning control to the terminal.
  5. Verify or Edit Global Configuration Settings
    All global configuration settings are stored in a text file. You can open and edit this file using your default editor.

    • To open the global configuration file:
      git config --global -e
      
    • This command will open the .gitconfig file (identified by its full path at the top) in VS Code.
    • Inside this file, you will see different sections. For example, [user] will contain name and email settings, and [core] will contain editor settings.
    • After reviewing or making changes, save the file and close VS Code. The terminal (or Git Bash) will then resume accepting commands.
  6. Configure Line Ending Preferences (core.autocrlf)
    This is a crucial setting to prevent issues when collaborating across different operating systems.

    • Understanding Line Endings:
      • On Windows, end-of-lines are marked with two special characters: "carriage return" (\r) and "line feed" (\n), often represented as CRLF.
      • On Mac and Linux systems, end-of-lines are indicated with a single "line feed" (\n), represented as LF.
    • Mismatched line endings can cause unexpected diff results and unnecessary changes when files are exchanged between different operating systems.
    • Scenario Example:
      • Imagine John uses Windows and Julie uses Mac, working on the same repository.
      • When John commits code from his Windows machine, Git should remove the \r character from line endings before storing the code in the repository. When he checks out code, Git should add the \r character back. To achieve this, John should set core.autocrlf to true.
      • When Julie checks out code on her Mac, she does not want \r characters. Git should not add them. However, if a \r character is accidentally added (e.g., by her editor), Git should remove it when storing the code in the repository. To achieve this, Julie should set core.autocrlf to input.
    • Set core.autocrlf:
      • In your terminal, use the following command:
        git config --global core.autocrlf [value]
        
      • Replace [value] based on your operating system:
        • If you are on Windows: Set core.autocrlf to true.
        • If you are on Mac or Linux: Set core.autocrlf to input.
  7. Accessing Git Help
    You can easily get help and documentation for Git commands:

    • Full Documentation: To view the full documentation for a command (e.g., config), use:
      git config --help
      

      This command opens the man page (manual page) for git config directly in your terminal, showing all options and their usage. Press Space to go to the next page and q to quit the viewer.

    • Quick Summary: For a short summary of a command and its options, use the -h flag:
      git config -h
      

      This provides a concise overview of the command's functionality.

Step 3: Basic Git Workflow

Basic Git Workflow

The first step in using Git effectively is understanding how to take snapshots of your project. This section will cover fundamental Git concepts that are often misunderstood.

How to Create a Git Repository

  1. Create a Project Directory:
    First, you need to create a directory for your project. You can name this directory anything and place it anywhere on your machine.
    For example, if you are in your projects directory, you can create a new directory named moon and then navigate into it:

    mkdir moon
    cd moon
    

    This moon directory will serve as your project directory, which can contain tens or hundreds of files.

  2. Initialize a New Git Repository:
    To add this directory to a Git repository for the first time, you must initialize a new empty repository within it.
    Type the following command:

    git init
    

    You will see a message similar to: Initialized empty Git repository in /path/to/your/project/moon/.git/.

  3. Understand the .git Subdirectory:
    Inside your project directory (moon in this example), Git creates a hidden subdirectory called .git. This directory is hidden by default because you are not supposed to directly interact with its contents.
    If you list the files and directories (ls), you won't see it. To see all files, including hidden ones, use:

    ls -a
    

    You will now see the .git subdirectory. This is your Git repository; it's where Git stores all information about your project's history. It contains directories like branches, hooks, info, objects, and references. As a Git user, you don't need to understand this internal structure; it's Git's implementation detail. Modifying or deleting this directory will result in the loss of your project history.

    Caution: If you remove the .git subdirectory, you will lose your repository. For example:

    rm -rf .git
    

    You will notice that the green (git) marker (if you have one in your terminal) disappears, indicating that you are no longer in a Git repository. To re-initialize it, run git init again.

Understanding the Staging Area (Index) and its Role in Committing

Now that you have a Git repository, let's discuss the basic Git workflow you'll follow daily.

  1. Workflow Overview:

    • Your project directory contains your files, and within it is the hidden .git repository.
    • As you work, you modify one or more files.
    • When your project reaches a desirable state, you commit those changes to your repository. A commit is like taking a snapshot of your project.
    • Git has a special intermediate step called the "staging area" or "index," which doesn't exist in most other version control systems.
    • The staging area is where you propose changes for your next commit or snapshot.
    • After making changes, you add the modified files to the staging area.
    • You then review these staged changes.
    • If everything is satisfactory, you make a commit, and this proposed snapshot is permanently stored in your repository.
    • The staging area allows you to review your work before permanently recording it. If some changes shouldn't be part of the next snapshot, you can "unstage" them and commit them later as part of a different snapshot.
  2. Illustrative Example of the Staging Area:

    • Initial State: Your project directory is currently empty.

    • Step 1: Create Files:
      You add a couple of files, for instance, file1.txt and file2.txt.

      echo "hello" > file1.txt
      echo "hi" > file2.txt
      
    • Step 2: Add Files to Staging Area:
      These files are new and untracked by Git. To prepare them for the first commit, you use the git add command.

      git add .
      

      (The . adds all changes in the current directory and its subdirectories recursively.)
      Now, these files are in the staging area. This represents the state you are proposing for your next commit.

    • Step 3: Commit Changes:
      You review the staged files. If everything looks good, you use the git commit command to permanently store this snapshot in the repository. As part of this, you provide a meaningful message describing the snapshot.

      git commit -m "Initial commit"
      

      You now have one commit in your repository.

    • Common Misconception:
      A common misconception is that the staging area becomes empty after a commit. This is incorrect. What you have in the staging area after a commit is the same snapshot that you just stored in the repository. The staging area can be thought of as a reflection of what's currently in your repository, or the next version destined for it.

    • Step 4: Modify a File:
      Suppose you fix a bug and make changes to file1.txt.

      echo "world" >> file1.txt
      

      At this point, the staging area still contains the old version of file1.txt because you haven't staged these new changes.

    • Step 5: Stage Modified File:
      You use git add again to stage the latest changes to file1.txt.

      git add file1.txt
      

      Now, the staging area contains the same content as your working directory for file1.txt.

    • Step 6: Commit New Changes:
      You then make another commit to record this updated state, including a descriptive message about the bug fix.

      git commit -m "Fix: Add 'world' to file1"
      

      You now have two commits in your repository, each clearly explaining the state of the project at that point in time.

    • Step 7: Delete a File:
      Imagine file2.txt is no longer needed. You delete it from your working directory:

      rm file2.txt
      

      However, file2.txt is still in the staging area.

    • Step 8: Stage Delete Operation:
      You must use the git add command to stage this deletion. Even though you are running git add, Git understands that the file has been deleted and will remove it from the staging area for the next snapshot.

      git add file2.txt
      
    • Step 9: Commit Deletion:
      You commit this change to permanently record the state without file2.txt.

      git commit -m "Remove unused file2"
      

      You now have three commits.

    • Commit Content:
      Each commit has a unique identifier (a 40-character hexadecimal string) generated by Git, acting like a revision number. Each commit also includes information about what was changed, by whom, when, and a complete snapshot of your project at the time it was created. Unlike many other version control systems, Git stores the full content of the project in each snapshot, not just the deltas (changes). This allows for quick restoration to an earlier state without computing changes. Git is very efficient, compressing file contents and avoiding duplicate storage.

Adding and Committing Changes

Let's put the basic workflow into practice with a live example.

  1. Add Files to Your Project:
    Begin by creating two files within your project directory:

    echo "hello" > file1.txt
    echo "hi" > file2.txt
    

    After creating these files, you'll notice a question mark ? next to them if your terminal is configured to show Git status indicators. This means the files are new and not yet tracked by Git. Git does not automatically track new files.

  2. Check Working Directory Status (git status):
    Run git status to see the status of your working directory and the staging area:

    git status
    

    The output will indicate "No commits yet" and list file1.txt and file2.txt as "Untracked files" in red. Red indicates that they are not yet in the staging area.

  3. Add Files to the Staging Area (git add):
    To prepare these files for committing, you need to add them to the staging area using git add.
    You have several options:

    • git add file1.txt: Adds a single file.
    • git add file1.txt file2.txt: Adds multiple specific files.
    • git add *.txt: Adds all files with a .txt extension.
    • git add .: Adds all new and modified files in the current directory and its subdirectories recursively. Be cautious with git add . as you might not want to add all files (e.g., large binary files or log files). You'll learn how to ignore files later.

    For this demo, add both files using git add .:

    git add .
    

    If your terminal has status indicators, the question marks will change to yellow, indicating that files are staged and ready to be committed.

  4. Verify Staging Area Status:
    Run git status again:

    git status
    

    The output will now show file1.txt and file2.txt under "Changes to be committed" in green, indicating they are in the staging area.

  5. Modify a Staged File:
    Now, let's modify file1.txt after it has been staged.
    Append "world" to file1.txt:

    echo "world" >> file1.txt
    

    (Note: >> appends to the file, while > overwrites it.)

  6. Observe Status After Modification:
    Run git status again:

    git status
    

    You'll see file1.txt listed twice:

    • Under "Changes to be committed" (green), it shows the original staged version of file1.txt.
    • Under "Changes not staged for commit" (red), it shows the modified version of file1.txt that is currently in your working directory.
      This demonstrates that Git took a snapshot of file1.txt when you first ran git add .. The subsequent change to file1.txt created a new, unstaged difference.
  7. Re-stage Modified File:
    To include the latest changes to file1.txt in your next commit, you must stage it again:

    git add .
    
  8. Verify All Changes Staged:
    Run git status one more time:

    git status
    

    Now, both file1.txt and file2.txt should be listed under "Changes to be committed" in green, and there should be no unstaged changes.

  9. Commit Staged Changes (git commit):
    With your snapshot prepared in the staging area, commit it permanently to the repository.
    Provide a concise, descriptive message using the -m flag:

    git commit -m "Initial commit"
    

    The output will show statistics, such as "2 files changed," "3 insertions." The indicator in your terminal will turn green, signifying a clean working directory (no pending changes). What you have in your working directory is now identical to what's in your staging area, which is identical to your last commit.

  10. Committing with a Multi-line Message:
    Sometimes, a short one-liner description isn't enough, and you need to provide more context (e.g., explaining constraints for a bug fix). In such cases, omit the -m flag:

    git commit
    

    This will open your default editor (e.g., VS Code, configured earlier in the course) to a file named COMMIT_EDITMSG (or similar) in your .git subdirectory.

    • Structure of a Multi-line Commit Message:

      • The first line should be a short summary, ideally less than 80 characters.
      • Add a line break (an empty line).
      • After the empty line, you can type a longer, more detailed description.
      • Lines starting with a pound sign (#) are comments and will be ignored by Git.
    • Example:

      Initial commit
      
      This is our first commit.
      It sets up the basic project structure
      and includes the initial files.
      
    • Save the changes in your editor and then close the window.

    • Back in the terminal, Git will proceed with the commit, displaying statistics.

  11. Skipping the Staging Area (Advanced - Use with Caution):
    While the staging area is crucial for review, you can skip it in specific scenarios. This should only be done if you are 100% certain that your changes don't need review.

    • Modify a file:
      echo "test" >> file1.txt
      

      Your working directory is now "dirty" (yellow indicator).

    • Commit directly:
      You can combine the add and commit steps for modified (not new) files using the -a (or --all) option with git commit:
      git commit -a -m "Fix the bug that prevented users from signing up"
      

      This command stages all modified (tracked) files and then commits them in one go. The output will confirm the commit, e.g., "1 file changed, 1 insertion."

    • Warning: Most of the time, you should always stage your code before committing it to the repository. Use this shortcut only when you are certain.
  12. Removing Files (git rm):
    If you determine a file is no longer needed (e.g., file2.txt), you can remove it.

    • Standard Unix rm (Not Recommended for tracked files):
      If you use rm file2.txt, it only deletes the file from your working directory. git status will show a deleted change that is "not staged for commit." You would then need to git add file2.txt to stage the deletion, and then git commit. The file file2.txt would still appear in git ls-files (which shows files in the staging area) until committed.
    • Git rm (Recommended for tracked files):
      Git provides a command that removes the file from both your working directory and the staging area simultaneously:
      git rm file2.txt
      

      After this, git status will immediately show file2.txt as deleted and "Changes to be committed" (green).

    • Commit the deletion:
      git commit -m "Remove unused code"
      
  13. Renaming or Moving Files (git mv):
    Renaming or moving files in Git is a two-step process if you use standard operating system commands: modify the working directory, then stage both the deletion of the old file and the addition of the new file. Git provides a single command for this:

    • Standard Unix mv (Not Recommended for tracked files):
      If you rename file1.txt to main.js using mv file1.txt main.js, git status will show two unstaged changes: deleted: file1.txt and untracked: main.js. You would then need to manually git add file1.txt (to stage the deletion) and git add main.js (to stage the new file). After staging, Git will recognize it as a "rename."
    • Git mv (Recommended for tracked files):
      Git's mv command handles both the file system operation and staging:
      git mv main.js file1.js
      

      After this, git status will directly show renamed: main.js -> file1.js as "Changes to be committed" (green).

    • Commit the rename:
      git commit -m "Refactor code"
      

      Note that during a rename, the commit statistics will show 0 insertions and 0 deletions because no file content was added or removed, only the name changed.

  14. Ignoring Files (.gitignore):
    In most projects, you need to tell Git to ignore certain files or directories that should not be tracked (e.g., log files, compiled binaries, temporary files). Including these unnecessary files increases repository size without providing value.

    • Create Unwanted Files/Directories (Example):
      Create a directory logs and a file dev.log inside it:
      mkdir logs
      echo "hello" > logs/dev.log
      
    • Check Status:
      git status will show "Untracked files: logs/". You don't want to add this.
    • Create .gitignore:
      To ignore files, you create a special file named .gitignore in the root of your project. This file has no name, only an extension.
      echo "logs/" > .gitignore
      

      You can also open this file in your editor: code .gitignore (if using VS Code).

    • Content of .gitignore:
      Add entries for files or directories to ignore.
      • logs/: Ignores the logs directory.
      • main.log: Ignores a specific file main.log.
      • *.log: Ignores all files ending with .log.
        Save the .gitignore file.
    • Verify Ignoring:
      Run git status again. Git will no longer report the logs directory as untracked. Instead, it will show a new untracked file: .gitignore.
    • Add and Commit .gitignore:
      You should commit your .gitignore file so that all collaborators automatically ignore the same files.
      git add .gitignore
      git commit -m "Add .gitignore"
      
    • Important Note on Ignoring: .gitignore only works for files that Git is not already tracking. If you accidentally commit a file (e.g., a compiled binary) and then later add it to .gitignore, Git will not ignore it. It will continue to track changes to that file.
      • Example (Accidentally tracking a binary):
        mkdir bin
        echo "hello" > bin/app.bin
        git add . # Accidentally adds bin/app.bin
        git commit -m "Add bin"
        

        Now, if you later add bin/ to .gitignore and modify bin/app.bin, git status will still show bin/app.bin as modified.

      • Solution (Removing tracked files from index):
        To tell Git to stop tracking a file that is already committed while keeping it in your working directory, you need to remove it from the staging area (index).
        • First, confirm the file is in the staging area: git ls-files
        • Use git rm --cached to remove it from the index only:
          git rm --cached bin/app.bin
          

          (If it's a directory, you need the -r flag: git rm --cached -r bin/)

        • Verify it's removed from staging: git ls-files
        • Now git status will show the file as deleted and ready for commit.
        • Commit this change:
          git commit -m "Remove bin directory that was accidentally committed"
          

        From this point, Git will no longer track changes in the bin directory, provided bin/ is in your .gitignore.

    • Git Ignore Templates:
      You can find .gitignore templates for various programming languages on GitHub: github.com/github/gitignore. These templates help you ignore common files (e.g., .class files in Java, compiled assets).

Best Practices for Crafting Meaningful and Appropriately Sized Commits

Following best practices for commits is crucial for maintaining a clean and useful project history.

  1. Commit Size:

    • Not too small: Avoid committing every single time you update a file. This leads to useless commit messages like "Update file1," "Update file2," etc.
    • Not too big: Don't wait to implement an entire feature end-to-end before committing. The purpose of commits is to record checkpoints as you progress. If you make a mistake, you can easily revert to an earlier state.
    • Commit frequently: In a real-world scenario, you might commit 5 to 10 times a day or even more. As you code and reach a stable, recordable state, make a commit.
  2. Logically Separate Changes:

    • Each commit should represent a logically separate set of changes. Do not mix unrelated changes in a single commit.
    • Example: If you are fixing a bug and accidentally discover a typo in the code, do not commit both changes in one commit. Make two separate commits: one for the typo and another for the bug fix. This keeps your history clear and easily traceable. (If you accidentally stage both changes, you can unstage them, which will be covered later.)
  3. Meaningful Commit Messages:

    • Make it a habit to create meaningful commit messages. These messages will appear in your project's history, and cryptic messages are unhelpful to both you and your team members.
    • If you adhere to the practice of each commit representing a single unit of work, crafting a descriptive message becomes much easier. If a commit tries to do too many things, it will be difficult to write a clear message.
  4. Wording Convention (Present Tense):

    • Most developers prefer to use the present tense in commit messages.
    • Instead of: "Fixed the bug"
    • Use: "Fix the bug"
    • While this is a common convention, if you prefer a different style, that's fine. The key is consistency within your team and project.

By following these best practices, you contribute to a clean, comprehensible, and effective Git history for your projects.

Viewing Status and Differences

  1. Short Status (git status -s or git status --short):
    The standard git status command provides comprehensive but verbose output. For a quicker summary, use the short status flag:

    git status -s
    
    • Output Structure: This command shows two columns:
      • Left Column: Represents the staging area.
      • Right Column: Represents the working directory.
    • Example Usage:
      • Modify file1.js (example assumes it exists and is tracked):
        echo "sky" >> file1.js
        
      • Create a new file file2.js:
        echo "sky" > file2.js
        
      • Run git status -s:
        • M file1.js: The M in the right column (working directory) indicates file1.js is modified but not yet staged.
        • ?? file2.js: ?? in both columns indicates file2.js is a new, untracked file.
      • Add file1.js to staging:
        git add file1.js
        
      • Run git status -s again:
        • M file1.js: A green M in the left column (staging area) means file1.js is modified and staged. The right column is empty because there are no further unstaged changes for this file.
          Remember, when you stage a file, Git takes a snapshot. If you modify it again after staging, you need to restage those new changes.
        • Modify file1.js one more time:
          echo "ocean" >> file1.js
          
        • Run git status -s:
          • MM file1.js: The first M (green) indicates staged changes, while the second M (red) indicates additional unstaged changes in the working directory.
        • Re-stage file1.js:
          git add file1.js
          
        • Run git status -s:
          • M file1.js: All changes for file1.js are now staged.
      • Add file2.js to staging:
        git add file2.js
        
      • Run git status -s again:
        • M file1.js
        • A file2.js: A green A in the left column indicates file2.js is a new file that has been added to the staging area.
  2. Viewing Staged Changes (git diff --staged):
    Before committing, it's a best practice to review what's in your staging area to avoid committing bad or broken code. While git status shows which files are affected, git diff --staged shows the exact lines of code that are staged to go into the next commit.

    git diff --staged
    
    • Understanding the Output:
      • The output shows diff --git a/file1.js b/file1.js, indicating a comparison between two copies of the same file.
      • a/file1.js represents the old copy (what was in the last commit).
      • b/file1.js represents the new copy (what's currently in the staging area).
      • Lines prefixed with - (red) indicate changes in the old copy (removed lines).
      • Lines prefixed with + (green) indicate changes in the new copy (added lines).
      • Headers like @@ -1,3 +1,5 @@ indicate the chunk of the file being shown.
        • -1,3: In the old copy, starting from line 1, 3 lines have been extracted.
        • +1,5: In the new copy, starting from line 1, 5 lines have been extracted.
      • For brand new files (file2.js in our example), the diff will show 0,0 for the old copy's header (e.g., @@ -0,0 +1,1 @@) because there was no old version of the file.
  3. Viewing Unstaged Changes (git diff):
    To see changes in your working directory that are not yet staged, use git diff without any arguments:

    git diff
    
    • This command compares what you have in your working directory with what you have in the staging area.
    • Example after git add . and further modification:
      • If all changes are staged, git diff will show no output.
      • If you then modify file1.js again (e.g., change "hello" to "hello world"):
        • # Open file1.js in your editor and change 'hello' to 'hello world'
          # Save and close
          git diff
          
        • The output will show:
          • -hello (red, line removed from staging area's version)
          • +hello world (green, line added in working directory's version)
        • This diff compares the staged version of file1.js (a/file1.js) with the unstaged version in your working directory (b/file1.js).
    • Recap:
      • git diff: Shows unstaged changes (working directory vs. staging area).
      • git diff --staged: Shows staged changes (staging area vs. last commit).

Using a Visual Diff Tool

While the terminal-based git diff is useful, visual diff tools provide a much better experience for comparing files.

  1. Configure Git for a Visual Diff Tool (VS Code Example):
    You need to tell Git which visual diff tool to use. For VS Code:

    • Set the tool name:
      git config --global diff.tool vscode
      

      This sets vscode as the name for your default diff tool.

    • Define the launch command:
      git config --global difftool.vscode.cmd "code --wait --diff \"\$LOCAL\" \"\$REMOTE\""
      
      • code: The command to launch VS Code (ensure code is in your system's PATH).
      • --wait: Tells the terminal to wait until you close the VS Code instance.
      • --diff: Tells VS Code to open in diff mode.
      • "$LOCAL" and "$REMOTE": These are placeholders for the old and new copies of the file being compared. Ensure these are correctly typed, including the double quotes and backslashes for proper escaping in some shells. If you edit your global config with git config --global -e, you might need to manually re-add these placeholders if they disappear.
  2. Verify Configuration:
    To confirm your settings, open your global Git configuration file:

    git config --global -e
    

    This opens the .gitconfig file in your default editor. Look for sections like [diff] and [difftool "vscode"].

  3. Launch the Visual Diff Tool (git difftool):
    Instead of git diff, use git difftool:

    git difftool
    
    • Without arguments, git difftool will show your unstaged changes (working directory vs. staging area).
    • To see staged changes (staging area vs. last commit), use:
      git difftool --staged
      

    Git will prompt you to launch the diff tool for each affected file.

    • When VS Code opens, it will display two panes: the old copy and the new copy, with changes highlighted, making it much easier to visualize differences.
    • After reviewing, close the VS Code instance to return to the terminal.
      While visual diff tools are powerful, many modern IDEs (like VS Code) have built-in Git integrations that allow you to view staged and unstaged changes directly within the editor environment, often making external diff tools less frequently used in daily workflows.

Browsing History

Git provides powerful tools to view your project's history.

  1. Viewing Commit History (git log):
    Use the git log command to see a list of all commits in your repository, sorted from the latest to the earliest:

    git log
    
    • Output Details:
      • Commit ID: Each commit has a unique 40-character hexadecimal string, a unique identifier.
      • HEAD -> master: HEAD is a pointer to your current branch. master is typically the main branch or main line of work. Git allows multiple branches for parallel development. Further details on branching will be covered later.
      • Author: The name and email of the commit author.
      • Date: The date and time the commit was created.
      • Commit Message: The one-line description and any detailed message provided during the commit.
    • Navigation: If the history spans multiple pages, press space to go to the next page and q to quit.
  2. Short Summary (git log --oneline):
    For a concise summary of commits, showing only the shortened commit ID and the one-line description:

    git log --oneline
    
  3. Reverse Order (git log --reverse):
    To view commits from the earliest to the latest:

    git log --oneline --reverse
    
  4. Inspecting a Specific Commit (git show):
    To see what exactly was changed in a particular commit, use git show followed by the commit identifier.

    • Referencing a Commit:
      • Full or Partial ID: You can use the full 40-character commit ID or a shortened version as long as it's unique (e.g., d601b90 from a git log --oneline output).
        git show d601b90
        
      • HEAD Pointer with Tilde (~): HEAD refers to the latest commit on your current branch. You can reference previous commits relative to HEAD using ~ followed by a number.
        • HEAD: The latest commit.
        • HEAD~1: The commit before the latest one.
        • HEAD~2: Two commits before the latest one, and so on.
        git show HEAD~1
        
    • Output of git show:
      • The author, date, and commit message.
      • A diff showing what has changed in that commit. Similar to git diff, it uses + for additions and - for deletions.
      • Example: git show HEAD~1 might show that git-ignore was modified, with certain lines removed and others added.
  5. Viewing a File's Version in a Specific Commit:
    While git show displays the changes, you can also retrieve the exact content of a file as it existed in a specific commit.

    git show :
    
    • Example: To see the version of .gitignore as it was in HEAD~1:
      git show HEAD~1:.gitignore
      

      This command will output the entire content of the .gitignore file from that specific commit.

  6. Listing Files and Directories in a Commit (git ls-tree):
    Git stores a complete snapshot of your project in each commit, not just changes. You can view all files and directories for a given commit using git ls-tree.

    • Understanding "Tree": In Git, "tree" refers to a data structure representing hierarchical information, similar to a file system directory where nodes can have children (files and subdirectories).
    • Command:
      git ls-tree HEAD~1
      
    • Output Details:
      • It lists files (represented as "blobs") and directories (represented as "trees") with their unique identifiers (generated based on their content).
      • Example: You might see 100644 blob .gitignore or 040000 tree bin.
      • Files are blob objects, and directories are tree objects. All are stored in Git's internal database.
  7. Viewing Git Objects (git show ):
    You can directly inspect the content of any Git object (blob, tree, commit) if you have its unique identifier.

    • Example: To view the content of the .gitignore blob identified by `` from the git ls-tree output:
      git show 
      

      This will display the plain text content of that file.

    • Example: To view the content of the bin tree:
      git show 
      

      This will show the contents of the bin directory, including files like app.bin it contains.

Undoing Changes

Git allows you to undo various types of changes, from unstaging files to recovering deleted ones.

  1. Undoing git add (Unstaging Files):
    If you've git added changes to the staging area but decide they shouldn't be part of the next commit (e.g., they belong to a different task), you can undo the add operation.

    • Using git restore --staged:
      Git introduced the restore command for this purpose (ensure you are using Git version 2.28 or later).
      git restore --staged 
      
      • Example: If file1.js was added to the staging area:
        git restore --staged file1.js
        
      • After running this, git status -s will show file1.js with a red M in the right column, meaning it's back in the working directory as an unstaged change. The staged version is now reverted to what was in the last commit.
    • Understanding git restore --staged: This command takes the copy of the file from the "next" environment, which for the staging area is the last commit (repository), and puts it back into the staging area. Essentially, it replaces the staged version with the version from the last commit.
    • Unstaging a New File A (added): If you git add a new file (A in git status -s), and then run git restore --staged , Git will effectively remove it from the staging area, returning it to an "untracked" (??) state, because it doesn't exist in the last commit for Git to restore from.
      git restore --staged file2.js # If file2.js was a new, staged file.
      git status -s # file2.js will now show ??
      
  2. Discarding Local Changes (git restore):
    Sometimes you make changes in your working directory that you want to completely discard and revert to the last committed or staged version.

    • Command:
      git restore 
      
      • This command takes the version of the file from the staging area (or the last commit if the file is not staged) and copies it into your working directory, overwriting your local changes.
      • You can also discard changes for all files in the current directory: git restore .
    • Example: If you modified file1.js locally:
      git restore file1.js
      

      The uncommitted changes in file1.js will be gone.

    • Note on New Untracked Files: git restore will not remove new, untracked files (?? in git status -s). This is because Git is not tracking them, so it doesn't have a previous version to restore from.
  3. Removing Untracked Files (git clean):
    To remove new, untracked files (that git restore cannot touch), use git clean.

    • Warning: This is a dangerous operation. Once untracked files are removed with git clean, there is no way to recover them.
    • Fatal Error by Default: If you run git clean by itself, it will give a fatal error and ask for --force.
    • Options:
      • -f or --force: Forces the removal of untracked files.
      • -d: Also removes untracked directories.
      • git clean -fd: Commonly used to remove untracked files and directories.
    • Example: To remove file2.js after it's in ?? state:
      git clean -fd
      

      file2.js will then be gone.

  4. Restoring a Deleted File:
    If you accidentally delete a file that was previously tracked and committed, you can restore it to a prior version.

    • Scenario: Assume file1.js exists and is already committed.
    • Delete the file:
      Using git rm file1.js will delete it from both working directory and staging area, and git status -s will show D file1.js.
    • Commit the deletion:
      git commit -m "Delete file1.js"
      
    • Restore the file:
      To restore file1.js to the commit before the deletion (e.g., HEAD~1):
      git restore --source HEAD~1 file1.js
      
      • --source : This option tells Git to restore the file from a specific commit, overriding the default behavior of restoring from the next environment (staging area/last commit).
    • After restoration, git status -s will show ?? file1.js, indicating it's back in your working directory as a new, untracked file. You will then need to git add and git commit it if you want to re-add it to your repository history, or simply keep it as an untracked file if that's your intention.

You have now learned the basic Git workflow, including how to create a repository, understand the staging area, perform adds and commits, and apply best practices for clean and meaningful commit history. You can also view status, differences, browse history, and undo various changes.

Step 4: Viewing History

Viewing History

We have made a few commits so far, but where are these commits? We can view them using the git log command.

  1. Using git log to Browse Commit History
    Type git log.
    You will see a list of all your commits, sorted from the latest to the earliest. Each commit entry typically includes:

    • A unique 40-character hexadecimal string, which is the commit's unique identifier (like a revision number, but not sequential).
    • HEAD -> master: HEAD is a reference to the current branch, and master is the main branch or line of work. Git uses HEAD to know which branch you are currently on.
    • Author: The name and email of the person who made the commit.
    • Date and Time: When the commit was created.
    • Commit Message: A one-line description of the commit.

    If you have many commits that spread across multiple pages, you can press space to go to the next page and q to quit the log view.

  2. git log Options

    • --oneline: This option provides a short summary of each commit.
      Type git log --oneline.
      The output will show the unique identifier, shortened to seven characters, and only the one-line description. It omits the author, date, and time.

    • --reverse: This option reverses the sort order, showing the first commit on top.
      Type git log --oneline --reverse.
      This will show the commits from earliest to latest.

  3. Inspecting Individual Commits with git show
    While git log is great for viewing a list of commits, git show allows you to see the exact changes made in a specific commit.

    • Referencing a commit by its unique identifier:
      To inspect a commit, you need its unique identifier. For example, if you want to look at a commit with an ID starting with d601b90, you can type:
      git show d601b90
      You don't always need to type all 40 characters; fewer characters are usually sufficient as long as there's no ambiguity with other commit IDs.

    • Referencing a commit using HEAD:
      HEAD points to the latest commit on the current branch.
      To view the last commit, you can type git show HEAD.
      To view a previous commit, you can use the tilde (~) operator followed by a number to specify how many steps back you want to go. For example, to go one step back from HEAD:
      git show HEAD~1
      This command will display:

      • The author and date of that specific commit.
      • The commit message.
      • A diff (difference) of what files were changed and what specific lines were altered (added, removed, or modified). For example, it might show a git ignore file where a line was removed and two new lines were added.
    • Viewing the final version of a file in a commit:
      If you want to see the exact version of a file as it was stored in a particular commit, rather than just the differences, use the following syntax:
      git show :
      For example, to view the git ignore file from the commit referenced by HEAD~1:
      git show HEAD~1:.git-ignore
      If the file is in a subdirectory, you would specify the full path, such as bin/app.bin.

      Git stores a complete snapshot of your project with each commit, not just the changes. The git show command, by default, highlights the differences.

  4. Understanding Git's Internal Object Model (Blobs, Trees, Commits)
    To see all the files and directories in a specific commit, you use the git ls-tree command. This command is named ls-tree because a tree is a data structure used to represent hierarchical information, similar to how directories contain files and subdirectories.

    • git ls-tree :
      Type git ls-tree HEAD~1.
      This will list all the files and directories stored in that commit. You will see:

      • File names (e.g., .git-ignore, bin/app.bin).
      • A unique identifier (a hash) generated based on the content of the file or directory. This ID refers to an object in Git's internal database.
      • The type of the object:
        • blob: Represents a file.
        • tree: Represents a directory.
    • Inspecting Git Objects with git show:
      You can use git show to view the content of any Git object, including blobs and trees, by specifying their unique identifier.

      • To view a blob (file content):
        git show (e.g., git show )
        This will display the exact content of that file as stored in Git's database.

      • To view a tree (directory content):
        git show (e.g., git show where bin is a directory)
        This will list the contents of that directory, including any files (blob) or subdirectories (tree) it contains.

      Git's objects include commits, blobs (files), trees (directories), and tags (which will be discussed later).

  5. Configuring and Using Visual Diff Tools
    Comparing files using the terminal can be challenging. Visual diff tools provide a much better experience. This section will guide you on setting up VS Code as your default Git diff tool. If you prefer another tool like KDiff3 or P4Merge, you'll need to find their specific configuration instructions.

    • Configure VS Code as the Default Diff Tool:
      In your terminal, you need to set two Git configuration settings globally.

      1. Specify the name of your diff tool:
        git config --global diff.tool vscode
        This command gives the name vscode to your chosen diff tool.
      2. Configure how Git should launch VS Code for diffing:
        git config --global difftool.vscode.cmd "code --wait --diff \$LOCAL \$REMOTE"
        • code: The command to launch VS Code. (Ensure code is added to your system's PATH, so it can be run from any directory. If not, you may need to troubleshoot this for your OS.)
        • --wait: This flag tells the terminal to wait until you close the VS Code instance where the diff is shown.
        • --diff: This flag tells VS Code that you intend to use it for comparing files.
        • $LOCAL and $REMOTE: These are placeholders that Git replaces with the paths to the old and new copies of the file being compared. Make sure to type these exactly, including the dollar signs and capitalization.
    • Verify Configuration:
      To ensure the settings are correctly applied, you can open your global Git configuration file:
      git config --global -e
      This will open the .gitconfig file in your default editor (VS Code, if you configured it previously). You should see a [diff] section with tool = vscode and a [difftool "vscode"] section with cmd = code --wait --diff $LOCAL $REMOTE. Close the editor to return to the terminal.

    • Using git difftool:
      Instead of git diff, you now use git difftool to launch your configured visual diff tool.

      1. View Unstaged Changes (Working Directory vs. Staging Area):
        Type git difftool
        If you have unstaged changes (modifications in your working directory that are not yet in the staging area), Git will prompt you to launch VS Code to view them.
        (Example: If you modified file1.js, it will show 1 of 1 files being diffed.)
        When VS Code opens, it will display two panes:

        • The old copy (what's currently in the staging area).
        • The new copy (what's in your working directory).
          You can easily see the changes, with replaced lines clearly highlighted. Close the VS Code window when you're done.
      2. View Staged Changes (Staging Area vs. Last Commit):
        Type git difftool --staged
        This will show you the changes that are currently in your staging area and are ready to be committed. Git will launch VS Code for each changed file in the staging area.
        In this view:

        • The old copy is what you have in the last commit.
        • The new copy is what you have in the staging area.
          These are the changes that will become part of your next commit.

      While visual diff tools are powerful, modern IDEs and code editors (like VS Code) often have built-in source control panels that allow you to view staged and unstaged changes directly within your development environment, reducing the need for separate diff tools in many workflows.

  6. Analyzing Changes in Working Directory vs. Staging Area
    To recap how to check changes within various Git environments:

    • git diff (without arguments): Shows unstaged changes. This compares what is in your working directory with what is currently in your staging area.
      (Example: If you have a file file1.js with "hello" in staging and change it to "hello world" in the working directory, git diff will show "hello" as removed and "hello world" as added.)

    • git diff --staged: Shows staged changes. This compares what is in your staging area with what was in the last commit. These are the changes that will be included in your next commit.
      (Example: If "hello world" is in staging, git diff --staged will show "hello world" as an addition relative to the last commit.)

    Note that the git status --short (or git status -s) command is still useful for a quick overview of which files are modified, added, or deleted in both the staging area and working directory, offering a concise representation of your project's current state.

Step 5: Advanced Topics

Advanced Topics

Occasionally, you may make changes that you no longer need. This section will cover methods to undo modifications, discard unnecessary changes, manage untracked files, and restore files to previous historical versions.

Undoing git add using git restore --staged

You should always review what you have in the staging area before making a commit to avoid committing bad or broken code. You can use the git diff --staged command to review staged changes.

For example, if you have staged changes in a file, such as file1.js, and then discover that these changes should not be part of the next commit, you can undo the git add operation.

In the past, the git reset command was used for this purpose, but it was often confusing. A newer, simpler command, git restore, is now available (make sure you are using Git version 2.28 or later).

To undo the staging of a specific file:

  1. Type git restore --staged file1.js.
    • You can list multiple files or use patterns (e.g., *.js).
    • To restore everything in the staging area, use a period (.) instead of a file name.
  2. Run git status -s to confirm.
    • You will see that file1.js no longer has a green M in the left column (staging area). The changes are now in the working directory (red M in the right column if the file was modified, or no M if it was an addition that became untracked again).

The git restore command takes a copy of the specified file from "the next environment" and places it into the target environment.

  • When restoring a file in the staging area, Git takes the last committed version of that file from the repository and places it into the staging area.

Consider file2.js, which was a new, untracked file that was then added to the staging area (indicated by a green A in git status -s).

  • Since file2.js did not exist in the last commit (repository), taking a copy from "the next environment" (the last commit) means there's no previous version to restore from the repository.
  • Therefore, when you run git restore --staged file2.js, Git understands that this file was newly added and not yet committed. It will remove file2.js from the staging area and return it to its previous state: an untracked file.
  • To verify this, run git status -s again. You will see file2.js listed with two question marks (??), indicating it is still an untracked file, meaning the git add for it has been undone.

Discarding Local Changes in the Working Directory

Sometimes, you make changes to files in your working directory that you decide to discard, either because you don't like them or they are no longer needed.

To undo local changes in file1.js:

  1. Type git restore file1.js.
    • You can use a period (.) to undo all local changes in the working directory.
  2. Execute the command.
    • Git will take the version of file1.js from the staging area and copy it into your working directory, overwriting your local modifications.
  3. Run git status -s to confirm that the changes in file1.js have been discarded.

If you have new, untracked files in your working directory (e.g., file2.js from the previous example, which is now untracked), git restore will not affect them. This is because Git is not tracking these files yet, so it has no "previous version" in the staging area or the repository to restore from.

To remove all new, untracked files:

  1. Type git clean.
    • By default, Git will give a "fatal error: clean.requireForce defaults to true and neither -i nor -f given." This is a safeguard because git clean is a destructive operation; there's no way to recover files once removed.
  2. Type git clean --help for options.
    • The --force or -f option forces Git to remove untracked files and directories.
    • The -d option allows you to remove untracked directories as well.
  3. To remove untracked files and directories, run git clean -fd.
  4. Run git status -s to verify that file2.js (or any other untracked files) is gone.

Managing Untracked Files with git clean

As demonstrated, git clean is used to remove untracked files from your working directory. It's distinct from git restore as git restore only deals with files that Git knows about (tracked files or files in the staging area).

Restoring Files to Specific Historical Versions

Git stores every version of a tracked file in its database. This means you can always restore a file or directory to a previous version, even if it has been deleted.

Let's illustrate by deleting a file:

  1. Delete file1.js.
    • Instead of using the standard rm command, which only removes the file from the working directory and would require you to then stage the deletion, use git rm file1.js.
    • git rm removes the file from both the working directory and the staging area.
  2. Run git status -s.
    • You'll see D file1.js (green D), indicating a deleted file in the staging area.
  3. Commit this change: git commit -m "Delete file1.js".

Now, imagine you realize you shouldn't have deleted file1.js and want to restore it.

  1. Look at your commit history using git log --oneline.
    • Identify the commit immediately before the deletion of file1.js. This is the version you want to restore.
  2. To restore file1.js to the version from the commit before the last commit:
    • Type git restore --source HEAD~1 file1.js.
      • HEAD~1 refers to the commit that is one step back from the current HEAD. You can also use the commit's unique identifier (e.g., d601b90 as shown in previous examples), but HEAD~1 is often more convenient for recent history.
      • The --source option tells git restore to get the file from the specified commit, overriding the default behavior of restoring from the next environment (staging area or last commit).
  3. Run git status -s.
    • You will see ?? file1.js, indicating that file1.js has been restored to your working directory but is now an untracked file.
    • You would then need to git add file1.js to re-track it and stage it for a new commit if you want to bring it back into your repository's tracked history.

Key Concepts

  • Git
  • Version Control System (VCS)
  • Repository
  • Centralized vs. Distributed VCS
  • Staging Area (Index)
  • Commit
  • Untracked Files
  • Global, System, and Local Configuration
  • HEAD (pointer)
  • Master Branch
  • Git Object Model (Blobs, Trees, Commits)
  • Line Endings (CRLF, LF)

Conclusion

This tutorial covered the core principles of Git, from its basic workflow and essential commands to viewing project history. You now have a solid understanding of fundamental Git concepts, including repositories, commits, and the staging area. With this knowledge, you are ready to explore more intermediate to advanced Git concepts and enhance your version control skills.

Legal

  • Contact
Clear keys input element