Skip to main content

GIT

 

Git:



  • Git is a tool that helps us to track or roll back in code or versions of our code development. For example, if we are working on a project with different parts. If the implementation of today has issues, it can be rolled back to the previous version with a simple rollback.
  • Git can also be used to work on projects collaboratively.

GitHub:


Configuration of  Git in PC:


Two types of Configurations:

  • Local: when planning to use multiple git accounts we use local.
  • Global: when we are about to use a single account, then we can simply configure Global.

Operations:


we can clone our  git data into the local system using the clone commands below 
first, we need to copy the HTTP link of the repository that we want to copy to the local system



This is the command 


Now we can make changes to these imported folders and we can check the status using the status command.




In the above picture, initially, the content is not modified so it says "nothing to commit". But when we check the status for the second time it indicates that the content is altered, due to changes we made to the file.

Types of Git status:



The picture depicts all four stages in a clear sense.
 



Add is a middle stage between the commit and initial state. we can add files using the following commands 
to add single changes at a time we use 


to add all the changes at once we can do 



Commit:


Commit indicates that the changes we have made are successfully saved in the Git but it will not reflect in the Git Hub as long as we push it.


This indicates that, due to a commit in the previous step Git in our local system is a head-by-1 commit. To resolve we need to push the committed changes to the GitHub repository.

Git Log and Checkout: Navigating Commit History

When working with Git, understanding how to navigate through your commit history is essential for reviewing changes, debugging, or even reverting to previous states of your code. Two fundamental commands that facilitate this are git log and git checkout.

1. Viewing Commit History with git log

The git log command is used to display a list of all commits made to the repository along with details like the commit hash, author, date, and commit message. This command is helpful to track the progression of changes over time.

bash
git log

Using git log will show a history like this:

plaintext
commit 1a2b3c4d5e6f7g8h9i0jklmnopqrst Author: Your Name <your.email@example.com> Date: Wed Nov 3 10:32:22 2024 +0000 Commit message describing what was done in this commit

Each entry contains a unique hash (e.g., 1a2b3c4d5e6f7g8h9i0jklmnopqrst), which is the identifier for that commit. You can use these hashes to reference specific points in the project's history.

2. Moving to a Specific Commit with git checkout

Once you've found the commit you want to explore, you can use git checkout followed by the commit hash to navigate to that state of the code. This doesn’t delete any files but lets you see your code as it was at that point.

bash
git checkout <commit-hash>

For example:

bash
git checkout 1a2b3c4d5e6f7g8h9i0jklmnopqrst

By running this command, Git will switch the files in your working directory to match the selected commit. This can be very useful if you need to review or test a previous version without altering your current branch.Note: When you checkout a specific commit, Git puts you in a "detached HEAD" state. This means you are no longer on a branch but at a specific commit. To return to the latest commit on your branch, use: git checkout main 

In summary, git log helps you view the entire commit history, while git checkout allows you to navigate to a specific commit based on its unique hash. This combination of commands gives you full control to move through your project’s timeline, experiment with past changes, or review the evolution of your codebase.

Making Changes While on a Previous Commit

When you’re working in Git, sometimes you might want to explore changes from a previous commit. If you make edits while you’re in a detached HEAD state (which occurs when you checkout a specific commit rather than a branch), there are two paths you can take with those changes:

  1. If You Commit the Changes: Committing changes while in the detached HEAD state will create a new branch starting from that point. This means you’ve essentially branched off from your main history, and Git will treat it as an alternative history. In this scenario, the HEAD pointer will move to the new commit, but your main branch will remain as it was, unaffected by these new changes. This can be useful if you want to explore a different direction in your project without altering the main branch.

  2. If You Don’t Want to Keep the Changes: If you decide the changes aren’t needed and you just want to return to the main branch’s latest state, you can use git checkout -f main. The -f (force) option here tells Git to discard any uncommitted changes made during your time on the previous commit, ensuring a clean transition back to the main branch’s latest commit. This way, any edits made while exploring the older commit are deleted, and you’re brought back to the most recent state of your main branch.

Using git checkout -f main to Discard Changes and Return to the Latest Commit:

The git checkout -f main command is your go-to option if you’ve made changes in the detached HEAD state and want to abandon them altogether. It discards any modifications and takes you back to the latest commit on the main branch, ensuring that no temporary changes from the previous commit are carried over.

This ability to check out older commits, make experimental changes, and then either commit to a new branch or discard changes, highlights Git’s versatility, giving you full control over your project’s history without impacting your main development flow.

When you create a new commit after checking out a previous commit (detached HEAD state) and then switch back to main, the new commit might not be linked to any branch. Here’s how you can get back to it:


Steps to Recover Your Commit

  1. Find the Commit Hash

    • Git retains the new commit, even if it’s not attached to a branch. Use the following command to find it:
      bash
      git reflog
    • This command lists all actions, including your newly created commit.
    • Look for an entry like:
      sql
      <commit-hash> HEAD@{n}: commit: <your commit message>
  2. Switch Back to the Commit

    • Once you have the commit hash, use:
      bash
      git checkout <commit-hash>
  3. Create a Branch for the Commit

    • To ensure you don’t lose this commit again, create a branch:
      bash
      git branch my-recovered-branch
    • This attaches the commit to a branch for easier access in the future.
  4. Checkout the New Branch

    • Move to the new branch:
      bash
      git checkout my-recovered-branch

Example Workflow

  1. Suppose you make a new commit in a detached HEAD state:

    bash
    git commit -m "My new commit in detached HEAD"
  2. You switch back to main:

    bash
    git checkout main
  3. Later, you want to recover your previous commit:

    • Run git reflog:

      sql
      abc1234 (HEAD -> main) HEAD@{0}: checkout: moving from <hash> to main def5678 HEAD@{1}: commit: My new commit in detached HEAD
    • Identify the commit hash (def5678).

    • Checkout the commit:

      bash
      git checkout def5678
    • Create a branch:

      bash
      git branch my-recovered-branch git checkout my-recovered-branch

INIT:


Push:

 

here origin indicates the repo in the git hub that we are trying to push.

Git Restore:

How Does git restore Work?

Let’s go step by step with examples to make it clear.

  1. Restoring a File to Its Last Committed State: Imagine you have a file called example.txt that you modified, but you now want to discard those changes.

    bash
    git restore example.txt

    This command reverts example.txt to its state in the last commit.

  2. Unstaging a File: Suppose you’ve added changes to the staging area using git add, but now you want to unstage them.

    bash
    git restore --staged example.txt

    This command removes example.txt from the staging area without affecting your modifications in the working directory.

  3. Restoring All Files: If you’ve made multiple changes across your working directory and want to discard all of them, you can run:

    bash
    git restore .

    This reverts all modified files in the current directory to their last committed state.

  4. Restoring a Specific Version: If you want to restore a file from a specific commit, use:

    bash
    git restore --source=<commit-hash> example.txt

    Replace <commit-hash> with the ID of the commit you want to restore from.

The Difference Between git restore and git reset

Let’s clarify this because it’s a common point of confusion:

  • git restore: Focuses on undoing changes to files in the working directory or staging area.
  • git reset: Can undo commits, unstage files, and reset the working directory—more powerful but also riskier.

Git Reset:

What is git reset?

git reset is used to undo changes by resetting the current branch to a specified state. It affects the staging area, your working directory, and the commit history depending on the options you use.

Think of it as a "rewind" button that can:

  1. Move your branch pointer to an earlier commit.
  2. Modify the staging area (unstage files).
  3. Change files in your working directory.

Types of git reset

There are three main modes of git reset, and each has a different impact on your repository:

  1. --soft: Only changes the commit history (doesn't touch the staging area or working directory).
  2. --mixed: Resets the commit history and unstages files, leaving changes in the working directory.
  3. --hard: Resets the commit history, unstages files, and discards changes in the working directory (permanently).

How to Use git reset

Let’s go step by step with examples for each mode.

1. git reset --soft: Roll Back Commits Without Losing Changes

Use this mode if you want to undo a commit but keep your changes staged.

Scenario: You’ve committed changes but realized you forgot to include an important update.

bash
git reset --soft HEAD~1

What it does:

  • Moves the branch pointer (HEAD) back by one commit.
  • Keeps the changes from the undone commit in the staging area.

You can now add the missing changes and re-commit:

bash
git add . git commit -m "Updated commit with missing changes"
2. git reset --mixed: Unstage Changes Without Losing Them

This is the default mode of git reset (if no flag is specified). It undoes a commit and removes changes from the staging area but keeps them in the working directory.

Scenario: You’ve staged and committed files, but you realize you need to fix some of them before committing.

bash
git reset HEAD~1

What it does:

  • Moves the branch pointer back by one commit.
  • Unstages all the changes from that commit.
  • Keeps the changes in your working directory.

You can now edit the files and stage them again as needed.

3. git reset --hard: The Nuclear Option

Use this mode if you want to completely undo changes and discard them from your working directory.

Scenario: You’ve committed changes and realized they’re completely wrong. You want to start fresh.

bash
git reset --hard HEAD~1

What it does:

  • Moves the branch pointer back by one commit.
  • Unstages all changes.
  • Discards all changes from the working directory.

Branching:

Understanding Branches in Git

Branches in Git are essential for managing code changes in a collaborative environment, enabling teams to work on different features, bug fixes, or tests independently without impacting the main codebase. For instance, let’s say you create a develop branch from a specific commit. This branch can then be assigned to the development team, where they can freely make changes and add new features without affecting the main branch.

Use Cases for Branches

  • Development: You can create a develop branch for new features and assign it to the development team. They can work on this branch, adding features and updates without interfering with the stable codebase.
  • Testing: Once development is complete, you might want to send the code for testing. You can create a testing branch, allowing the QA team to test the changes independently.
  • Bug Fixes: If there's an issue in production, you can create a separate bugfix branch from a stable point in the code. This allows the bug-fix team to address the problem without interrupting ongoing development.

Creating a Branch in Git

To create a new branch, you can use the command:

bash
git branch <branch-name>

Replace <branch-name> with the desired name of your branch. For example, git branch develop will create a branch named develop.

By using branches, you can organize your work effectively and manage multiple tasks in parallel, whether it’s development, testing, or bug fixes.

Switching Branches in Git

Once you have multiple branches, you can switch between them using:

bash
git checkout <branch-name>

For example, if you want to switch to a branch named develop, you would use:

bash
git checkout develop

This command will update your working directory to reflect the state of the specified branch, allowing you to work on different tasks without affecting other branches.

In recent versions of Git, there's also an alternative command:

bash
git switch <branch-name>

This is a more specific command for switching branches and works similarly to git checkout.

Using these commands, you can easily move between branches to focus on different tasks or features in your project.


When you use:

bash
git checkout -b <branch-name>

it does two things at once:

  1. Creates a new branch with the name you specify.
  2. Switches to that branch immediately after creating it.

For example:

bash
git checkout -b feature-xyz

This will create a new branch called feature-xyz and automatically switch you to that branch.

This command is especially useful when you want to quickly branch off to work on a new feature or bug fix without needing to type two separate commands to create and switch.In Git, when you create a new branch, it captures all the code from the point at which it was created. Here’s a breakdown of how branching works and how you can manage different branches based on your specific needs:

Key Points on Creating Branches

  1. Branch Creation Basics:

    • When you create a branch, it will contain all the code and commits from the branch you are currently on.
    • For example, if you are on the main branch, any new branch you create will reflect the exact code and commit history of main up to that point.
  2. Creating a Branch from a Specific Point:

    • Suppose you’re currently on main and have finished a stable code segment. If you now create a development branch, it will carry over everything from main up to the last commit.
    • If you want to create a branch from a different branch (not the one you’re currently on), you first need to switch to that source branch.
    • Example:
      bash
      git checkout main git checkout -b team-a
  3. Creating a Branch Directly from Another Branch:

    • Alternatively, you can create a branch from a specific source branch without needing to switch first. This is done by specifying both the new branch name and the source branch in one command:
      bash
      git branch <new-branch-name> <source-branch-name>
    • For instance, if you’re working on team-a but want to create a team-b branch based on main, you can do this:
      bash
      git branch team-b main
    • This command creates team-b with the same state as main, regardless of which branch you’re currently on.

Example Scenario

  1. Suppose you start on main and create a branch for Team A:

    bash
    git checkout -b team-a

    Now, Team A can work independently on their branch.

  2. If you want to create a team-b branch with the exact state of main (not including any work done in team-a), you would switch back to main and then create the branch:

    bash
    git checkout main git checkout -b team-b

Or, you could do it directly without switching:

bash
git branch team-b main

This approach gives each team a clean start based on the stable code in main, allowing independent work without accidental overlaps from other branches.

Pushing a New Branch to GitHub

When you create a new branch locally, it only exists on your machine until you push it to the remote repository. To do this, you use one of the following commands:

  • Using --set-upstream:

    bash
    git push --set-upstream origin branchname
  • Using -u (shorthand for --set-upstream):

    bash
    git push -u origin branchname

Both of these commands achieve the same outcome: they push your new branch to the remote repository and set it up to track that remote branch.

How it Works

When you push a branch for the first time with --set-upstream or -u, you’re doing two things:

  1. Pushing the Branch: You’re sending the branch to GitHub (or any remote repository), which means it now exists on both your local machine and GitHub.
  2. Setting Upstream Tracking: This links your local branch with the remote branch. By establishing this link, future pushes and pulls will automatically synchronize changes between your local and remote branches without specifying the branch name.

Benefits of Setting Upstream Tracking

After using --set-upstream or -u, you don’t need to specify the remote or branch name in subsequent pushes. For example:

  • Instead of typing:
    bash
    git push origin branchname
  • You can simply type:
    bash
    git push

This setup makes pushing faster and less repetitive, as Git understands that you want to push changes from your local branch to its corresponding branch on the remote repository.

Merging Branches in Git: 

When working with branches in Git, merging is an essential step to integrate changes from one branch into another. Here’s how merging can be achieved, both on GitHub and your local Git repository:

Merging Branches on GitHub

  1. Navigate to the Repository:

    • Go to the GitHub repository where your branches exist.
  2. Create a Pull Request:

    • On the repository page, click on the Pull Request tab.
    • Click New Pull Request.
  3. Select Branches to Merge:

    • You’ll see two dropdown menus: one for the base branch (e.g., main) and one for the compare branch (e.g., feature-branch).
    • Choose the base branch as the branch you want to merge into (e.g., main) and the compare branch as the branch with the changes you want to merge (e.g., feature-branch).
  4. Review Changes:

    • GitHub will show the differences between the two branches. Review these changes for conflicts or issues.
  5. Complete the Merge:

    • If everything looks good, click Merge Pull Request and confirm the merge.
    • The changes from the feature branch will now be incorporated into the main branch.

Merging Branches Locally

  1. Switch to the Target Branch:

    • Start by switching to the branch you want to merge changes into:
      bash
      git checkout main
  2. Merge the Source Branch:

    • Run the merge command to integrate changes from the source branch:
      bash
      git merge feature-branch
  3. Handle Merge Conflicts (if any):

    • If Git detects conflicting changes, it will pause the merge and mark the conflicting files.
    • Open the conflicting files, resolve the conflicts, and then mark them as resolved:
      bash
      git add <file-name>
  4. Complete the Merge:

    • Once conflicts are resolved, finalize the merge:
      bash
      git commit
  5. Push the Changes:

    • After merging, push the changes to the remote repository to keep everything in sync:
      bash
      git push origin main

Why Merging is Important

Merging ensures that all changes from various branches are integrated into the main branch, creating a synchronized and updated codebase. This is especially critical in team environments to prevent conflicts and maintain consistency.

Whether you use GitHub’s pull requests for a visual merge process or perform the merge locally using Git commands, keeping your branches synchronized ensures smooth collaboration and a clean codebase.

Deleting a Branch: Best Practices and Workflow

Managing branches effectively is crucial for keeping your Git repository organized and ensuring a smooth development workflow. Whether you delete a branch locally or on GitHub, synchronizing changes between your local and remote repositories is essential. Here's a comprehensive guide to deleting branches and keeping everything up-to-date.

Why Delete a Branch?

  • Reduces Clutter: By removing unused branches, you maintain a cleaner repository structure.
  • Improves Collaboration: Fewer branches make it easier for team members to identify active work.
  • Promotes Discipline: Ensures work is completed and merged rather than leaving unfinished branches behind.

Scenario 1: Deleting a Branch on GitHub and Updating Local Repository

After deleting a branch on GitHub, it is important to synchronize your local repository to reflect these changes.

Steps to Update Local Repository After Deleting on GitHub:

  1. Fetch Remote Changes: Use the following command to update your local repository and remove references to deleted remote branches:

    bash
    git fetch -p

    The -p (prune) option removes local references to remote branches that no longer exist.

  2. Verify the Branch is Removed Locally: List remote branches to confirm the deletion:

    bash
    git branch -r
  3. Optional: Remove Locally Cached Branch: If the branch still exists locally (even though it was deleted remotely), delete it:

    bash
    git branch -d branch-name

Scenario 2: Deleting a Branch Locally and Updating GitHub

If you delete a branch locally, you need to push this change to GitHub to reflect the deletion in the remote repository.

Steps to Update GitHub After Deleting Locally:

  1. Delete the Branch Locally: Use the following command to delete the branch in your local repository:

    bash
    git branch -d branch-name
  2. Push the Deletion to GitHub: Inform GitHub of the deletion by using:

    bash
    git push origin --delete branch-name
  3. Verify on GitHub: Navigate to the Branches section of your repository on GitHub and confirm the branch no longer exists.

NOTE:

What if We Want to Undo a Push but Still Want to Have Content Locally Staged or Unstaged?

Sometimes, you may need to undo a push to the remote repository while retaining your changes locally, either staged or unstaged. Here’s how you can do it:

  1. To Keep Changes Staged: Use git reset --soft to undo the last commit but keep your changes staged for further editing:

    bash
    git reset --soft HEAD~1
    • The branch pointer will move to the previous commit.
    • Changes from the undone commit remain staged in the index.
  2. To Keep Changes Unstaged: Use git reset --mixed to undo the last commit and move your changes back to the working directory:

    bash
    git reset --mixed HEAD~1
    • The branch pointer moves to the previous commit.
    • Changes from the undone commit remain in the working directory but are unstaged.
  3. To Remove Changes Completely: If you want to go to the previous commit and discard all changes:

    bash
    git reset --hard HEAD~1
    • The branch pointer moves to the previous commit.
    • All changes from the undone commit are permanently removed.

Key Notes

  • After resetting, if the commit was already pushed to the remote, you’ll need to force push to update the remote:

    bash
    git push origin <branch-name> --force

    Be cautious with --force, as it rewrites the history on the remote repository.

  • Always ensure you back up your branch if you’re unsure:

    bash
    git branch backup-branch

These commands help manage your workflow when you need to undo a push without losing your local progress. Use the appropriate reset option based on whether you want your changes staged, unstaged, or discarded.

What if We Went to the Previous State and Wanted to Ignore the Main State, Making the Previous State the New Main?

If you want to ignore the current main branch, make the previous state your new main, and push it to Git, follow these steps:

Steps to Replace main with the Previous State

1. Ensure You Are on the Previous State

Check out the previous commit or state you want to make the new main:

bash
git checkout <commit-hash>

Confirm you’re on the desired state by checking the commit log:

bash
git log --oneline

2. Delete the Existing main Branch

Since you want to replace the current main with the previous state, delete the existing main branch:

bash
git branch -D main
  • -D: Force deletes the main branch even if it hasn’t been merged.

3. Rename the Current State to main

Rename the current branch (the one pointing to the previous state) to main:

bash
git branch -m main

This makes the branch you're currently on the new main.

4. Push the New main to Git

Force push the updated main branch to the remote repository:

bash
git push origin main --force
  • --force ensures the remote main branch is overwritten with the new state.

Key Notes

  • Old main Is Gone: After deleting and overwriting main, the old state is no longer accessible unless backed up.
  • Use Backup if Needed: If unsure, create a backup of the current main branch before replacing it:
    bash
    git branch backup-main

Command Summary

bash
# Step 1: Go to the previous state git checkout <commit-hash> # Step 2: Delete the current main branch git branch -D main # Step 3: Rename the current state to main git branch -m main # Step 4: Force push the new main to the remote repository git push origin main --force

Outcome

The previous state is now your new main branch, and it has been pushed to the Git remote, replacing the old main. Use this workflow cautiously, especially when working with collaborators, as it rewrites history on the remote branch.

Comments