Garbage Collction in C#.

Garbage Collection

Garbage Collection (GC) in C# is an automatic memory management feature provided by the Common Language Runtime (CLR). Its primary goal is to free memory occupied by objects that are no longer in use, preventing memory leaks and improving application stability. 

Unlike languages like C or C++, developers do not manually free memory in C#. The CLR’s garbage collector handles it automatically.

When Garbage Collection Is Needed?

When a C# application runs, objects are dynamically created on the managed heap. As the application continues executing, many of these objects become unused because they are no longer referenced. If this unused memory is not reclaimed, the application can start consuming excessive memory, experience performance degradation, and may eventually crash due to insufficient available memory. 

Garbage Collection addresses this problem by automatically identifying objects that are no longer in use, releasing the memory occupied by those objects, and compacting the heap to improve memory efficiency and performance.

These are the tasks that Garbage Collection performs behind the scenes.

  • Identifies which objects are still in use (reachable from GC roots)
  • Detects objects that are no longer referenced
  • Reclaims memory occupied by unused objects
  • Compacts the managed heap to reduce fragmentation
  • Updates object references after compaction
  • Promotes surviving objects to older generations
  • Improves memory availability and application performance

Generation in Garbage Collection.

The C# Garbage Collector uses a generational model based on a simple but powerful assumption:
Most objects are short-lived. To optimize performance, objects are grouped into generations based on their longevity. Each generation is collected at different frequencies.

1. Generation 0 (Gen 0)

Generation 0 contains newly created objects. Almost every object starts its life here. It is collected very frequently because most objects become unused quickly. A Gen 0 collection is fast and inexpensive.
If an object survives a Gen 0 collection, it is promoted to Generation 1.

Example:
void CreateObjects()
{
    int x = 10;
    var temp = new object();   // Gen 0 object
}
Once CreateObjects() finishes, temp is no longer referenced and will likely be collected in the next Gen 0 GC.

Typical Gen 0 objects:
  • Temporary objects
  • Method-local objects
  • Short-lived calculations

2. Generation 1 (Gen 1)

Generation 1 acts as a buffer between Gen 0 and Gen 2. It contains objects that survived at least one garbage collection. Objects in Gen 1 are collected less frequently than Gen 0. If they survive a Gen 1 collection, they are promoted to Generation 2.

Example:
class CacheItem
{
    public string Data { get; set; }
}

CacheItem item = new CacheItem(); // Starts in Gen 0
If the item is still referenced after a Gen 0 GC, it moves to Gen 1.

Typical Gen 1 objects:
  • Objects with medium lifetime
  • Temporary caches
  • Objects shared across a few methods

3. Generation 2 (Gen 2)

Generation 2 contains long-lived objects that remain in memory for a long time. Gen 2 collections are infrequent and expensive, but they clean a large portion of memory. Objects in Gen 2 usually stay there until the application ends or the reference is explicitly removed.

Example:
class AppConfig
{
    public static AppConfig Instance = new AppConfig();
}
Because Instance is static, it remains alive throughout the application’s lifetime and eventually ends up in Gen 2.

Typical Gen 2 objects:
  • Static objects
  • Singletons
  • Long-lived caches
  • Application-wide configuration data

Large Object Heap (LOH)

Objects larger than 85,000 bytes are allocated on the Large Object Heap, which is logically part of Gen 2.
  • LOH objects are collected only during Gen 2 GC
  • Not compacted by default (to avoid performance cost)
Example:
byte[] largeArray = new byte[100_000]; // LOH

In short:
  • Gen 0 collections are fast and frequent
  • Gen 1 filters objects before reaching Gen 2
  • Gen 2 collections are rare but thorough
  • Improves overall performance and responsiveness
Generation Lifetime Collection Frequency
Gen 0 Short-lived Very frequent
Gen 1 Medium-lived Less frequent
Gen 2 Long-lived Rare
LOH Very large objects During Gen 2

How Garbage Collection is Triggered Automatically?

When a C# application is running, objects are constantly being allocated on the managed heap. The CLR keeps track of these allocations and monitors the available free memory. Garbage Collection is automatically triggered when the CLR detects that continuing allocations without cleanup would negatively impact performance or stability.

The most common automatic triggers are:
  • The managed heap does not have enough free space for new allocations
  • Allocation pressure becomes high (too many objects being created quickly)
  • A generation threshold is exceeded (Gen 0, Gen 1, or Gen 2)
  • The system is under memory pressure (low available RAM)
  • The application enters certain runtime phases (for example, during idle time)
  • At this point, the CLR decides on its own that a garbage collection cycle is required and pauses the application briefly to perform it.

How To Manually Call Garbage Collection in C#?

First of all, it is never recommended to call GC manually by a developer, and it is completely handled by CLR. However, there are rare, controlled scenarios where forcing garbage collection can make sense.

Example: Imagine a console application that processes a huge amount of data, creates large objects, and then finishes its work.
using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        Console.WriteLine("Application started");

        CreateLargeObjects();

        Console.WriteLine("Large objects created and released");

        // Force garbage collection
        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();

        Console.WriteLine("Garbage collection completed");

        Console.ReadLine();
    }

    static void CreateLargeObjects()
    {
        List<byte[]> largeData = new List<byte[]>();

        for (int i = 0; i < 10; i++)
        {
            // Each array is ~10 MB
            largeData.Add(new byte[10_000_000]);
        }

        // Remove references
        largeData = null;

        Console.WriteLine("References removed");
    }
}

What Happens in This Example
  • The application allocates large objects (LOH).
  • References are explicitly removed.
  • GC.Collect() forces garbage collection.
  • GC.WaitForPendingFinalizers() ensures finalizers run.
  • Memory is reclaimed immediately.
This is a controlled environment, where the developer knows exactly when memory is no longer needed.

What is the use of Dispose() method in C#?

The Dispose() method is used to explicitly and immediately release unmanaged resources such as file handles, database connections, network sockets, or native memory. It provides deterministic cleanup, meaning the developer controls when the resources are freed instead of waiting for the Garbage Collector.

Garbage Collection only frees managed memory. It does not know how or when to release unmanaged resources. If these resources are not released promptly, the application can run out of OS-level resources even if enough memory is available.

Dispose() solves this by allowing the developer to clean up resources as soon as they are no longer needed.

Example:
class FileLogger : IDisposable
{
    private StreamWriter _writer = new StreamWriter("log.txt");

    public void Write(string message)
    {
        _writer.WriteLine(message);
    }

    public void Dispose()
    {
        _writer.Dispose(); // releases file handle immediately
        Console.WriteLine("Resources released using Dispose()");
    }
}
Usage:
using (var logger = new FileLogger())
{
    logger.Write("Hello");
} // Dispose() is called automatically here

Key Points
  • Dispose() releases unmanaged resources immediately.
  • It is called explicitly by the developer or via the using keyword.
  • Provides predictable and safe cleanup.
  • Preferred over Finalize() for resource management.
Dispose() is used to deterministically release unmanaged resources as soon as they are no longer needed, improving performance and reliability.

What is the use of Finalizer in C#?

The Finalize method (finalizer) is used to clean up unmanaged resources if they were not released explicitly using Dispose(). It acts as a last-resort safety mechanism and is automatically called by the Garbage Collector before an object’s memory is reclaimed.

Example:
class NativeResource
{
    ~NativeResource()
    {
        // Release unmanaged resource
        Console.WriteLine("Finalize called");
    }
}
If an object of NativeResource is not disposed properly, the GC will eventually call Finalize() to clean up the unmanaged resource.

Best Practices for Garbage Collection

  • Avoid unnecessary object creation
  • Reuse objects when possible
  • Dispose of unmanaged resources properly
  • Avoid manual GC.Collect()
  • Use using for streams, files, and DB connections
  • Be careful with static references

How C# Code Execution Takes Place?

When you press Build or Run in Visual Studio or execute "dotnet run", a lot happens behind the scenes. While we write simple C# programs, the .NET runtime performs multiple sophisticated operations to turn that high-level code into instructions your CPU can understand.

In this article, we will discuss how C# code is compiled, loaded, converted, and executed inside the .NET runtime, with all intermediate steps clearly explained.

Several steps take place behind the scenes, and we will understand each of them one by one in sequence.

step-by-step-diagram-of-C-sharp-code-execution

Step 1: Write a C# Code.

Everything begins with a .cs file that contains C# source code. These files contain human-readable instructions written in the C# programming language. But your computer cannot execute C# directly, so it needs to be converted several times before execution.

using System;

namespace MyApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World");
        }
    }
}

Step 2: The Role of the C# Compiler (Roslyn)

When you write C# code in Program.cs, that code is not directly understood by your CPU. There’s a critical component in between: the C# compiler, also known as Roslyn.

Understanding how the C# compiler (Roslyn) works is key to understanding:

  • How your C# source turns into something runnable.
  • Why do you see certain errors at compile time?
  • How tools like IntelliSense, analyzers, and refactorings work so “smartly” in IDEs.

Let’s walk through this step by step.

What is Roslyn?

Roslyn is the official C# and Visual Basic .NET compiler platform from Microsoft. It converts your C# code into IL (Intermediate Language), also known as CIL (Common Intermediate Language).

Intermediate Language (IL), also known as Common Intermediate Language (CIL) or formerly Microsoft Intermediate Language (MSIL), is a low-level, object-oriented, stack-based bytecode instruction set.

Along with IL, Roslyn also produces metadata that contains type definitions (classes, structs, enums), method signatures, properties, fields, and references to other assemblies.

IL + Metadata is the final output of the Roslyn Compiler.

After compilation of your source code, your project produces assembly:

  • .exe file → console app / Windows app
  • .dll file → class library

Both contain IL Code + Metadata + Manifest (Assembly name, version, culture, references, entry point). These files are platform-independent and can run on Windows, Linux, Mac, or any OS that supports .NET.

Step 3: Role of CLR (Common Language Runtime)

The Common Language Runtime (CLR) is the heart of the .NET runtime environment.
Just like the Java Virtual Machine (JVM) for Java, the CLR is the execution engine that runs applications written in C#, F#, VB.NET, and other .NET languages.

What does CLR do in C# Code Execution

What does CLR do?

The CLR performs many tasks to ensure that your program runs smoothly. Let's understand each role one by one:

Role 1. Loads and Verifies Assembly.

When you run a .NET application, the CLR reads the manifest present inside the (.exe or .dll) and loads the assembly into your memory. It also verifies the IL code and prepares it for execution. This is the first job of the CLR - loading your compiled IL into the runtime environment.

Role 2: JIT Compilation.

IL cannot run directly on your processor, so the CLR uses the JIT (Just-In-Time) compiler to convert IL into native machine code just before execution. JIT compiles methods only when needed to improve startup time, memory usage, and overall performance.

JIT is one of the biggest strengths of the CLR, giving you portability + performance.

Role 3: Memory Allocation.

The CLR allocates memory on the managed heap and keeps track of the objects. You don't need to manually allocate memory because the CLR handles everything.

Role 4: Garbage Collection.

CLR includes Garbage Collection (GC), which finds unused objects and frees memory automatically to prevent memory leaks. This gives C# extremely efficient memory handling without manual malloc/free.

Garbage Collection works in three generations:

Gen 0: The youngest generation, where all new objects are initially allocated. This is where the majority of short-lived objects reside. Garbage collection runs most frequently in this generation to quickly reclaim memory from objects that are no longer in use. 

Gen 1: Objects that have survived a collection in Generation 0 are moved to this generation. These objects are considered to be slightly longer-lived than those in Gen 0. Collection here happens less frequently than in Gen 0.

Gen 2: Objects that survive collection in Generation 1 are moved to this, the oldest generation. This generation holds the long-lived or "permanent" objects that have survived multiple collection cycles. Garbage collection runs least frequently in this generation, as it's assumed these objects will be in use for a long time.

Role 5: Exception Handling

CLR provides a structured and unified exception system. If something goes wrong:

  • CLR throws an exception object
  • Stops the current execution path
  • Jumps to the nearest matching catch
  • Ensures cleanup using finally

Role 6: Thread Management

Thread management is one of the most critical responsibilities of the Common Language Runtime (CLR). The CLR provides a managed, safe, and optimised threading model, which frees developers from dealing with complex low-level details like context switching, CPU scheduling, and memory synchronisation.

Role 7: Security Checks

The CLR enforces multiple layers of security to ensure that .NET applications run safely and do not harm the system. It guarantees type safety, preventing invalid type conversions and illegal memory access. Through Code Access Security (CAS), the CLR restricts what code can do based on its origin and granted permissions, such as blocking untrusted code from accessing files, the registry, or network resources.

So these are some basic operations that take place when building and running your C# Code.

Short Quick Answer: C# code is first compiled by the Roslyn compiler into IL (Intermediate Language) and stored in an assembly (.dll or .exe). When the program runs, the CLR loads this IL and the JIT compiler converts it into native machine code for the current processor. The runtime then executes this native code and handles memory using the Garbage Collector.

Difference Between Double, Float, and Decimal Data Types in C#.

When working with numbers that contain decimal points in C#, selecting the correct data type is crucial. The three most commonly used floating-point types are:
  • float
  • double
  • decimal
Although they all store fractional numbers, they differ significantly in precision, range, performance, and intended use cases.

float-vs-double-vs-decimal

1. Float Data Type.

In C#, float is a single-precision floating-point data type used to store numbers that contain decimal (fractional) values. It follows the IEEE 754 standard and is used primarily where performance and memory efficiency are more important than very high precision.

float number = 10.5f;
Note: The f suffix is mandatory; without it, the value is treated as double.

Key Characteristics of float.

Property Value
Size 4 bytes (32 bits)
Precision ~6–7 significant digits
Range ±1.5 × 10-45 to ±3.4 × 1038
Base Binary (base-2)
Speed Very fast

Example:
float a = 1.1f;
float b = 2.2f;

Console.WriteLine(a + b);
// Output: 3.3000002
This slight inaccuracy happens because float store values in binary format, which cannot represent some decimal numbers exactly.

Use float when:
  • A small precision loss is acceptable
  • High performance is required
  • Memory usage must be low
Key Point: Avoid float when you are working with money and financial data. In that case, use decimal.

2. Double Data Type.

In C#, double is a double-precision floating-point data type used to store decimal (fractional) numbers with high precision and a very large range. It follows the IEEE 754 standard and is the default type for floating-point numbers in C#.

double number = 10.5;

Key Characteristics of double:

Property Value
Size 8 bytes (64 bits)
Precision ~15–16 significant digits
Range ±5.0 × 10-324 to ±1.7 × 10308
Base Binary (base-2)
Speed Fast (hardware-accelerated)

Example:
double x = 0.1;
double y = 0.2;

Console.WriteLine(x + y);
// Output: 0.30000000000000004
This happens because double stores numbers in binary format, which cannot exactly represent many decimal fractions.

Use double when:
  • You need high precision
  • Performance is important
  • Exact decimal accuracy is not critical
In Short: Double is a 64-bit floating-point data type that provides high precision and fast performance, commonly used for mathematical and scientific calculations.

Why is double preferred over float?

double provides better precision with minimal performance cost.
float f = 1.0f / 3.0f;
double d = 1.0 / 3.0;

Console.WriteLine(f); // 0.33333334
Console.WriteLine(d); // 0.3333333333333333

3. Decimal Data Type.

In C#, decimal is a high-precision, base-10 numeric data type designed specifically for financial and monetary calculations where accuracy is more important than performance.
Unlike float and double, decimal can exactly represent decimal numbers such as 0.1 and 0.2.
decimal amount = 10.5m;
Note: ⚠️ The m suffix is mandatory; without it, the value is treated as double.

Key Characteristics of decimal:
Property Value
Size 16 bytes (128 bits)
Precision 28–29 significant digits
Range ±1.0 × 10-28 to ±7.9 × 1028
Base Decimal (base-10)
Speed Slower than float and double

Example:
double d = 0.1 + 0.2;
decimal m = 0.1m + 0.2m;

Console.WriteLine(d); // 0.30000000000000004
Console.WriteLine(m); // 0.3
This clearly shows why decimal is preferred for financial data.

Comparison Table:

Feature float double decimal
Size 4 bytes 8 bytes 16 bytes
Precision 6–7 digits 15–16 digits 28–29 digits
Base Binary Binary Decimal
Performance Fastest Fast Slowest
Accuracy Lowest Medium Highest
Best For Graphics, games Scientific math Finance, money

Final Summary

  • float → lightweight, fast, low precision
  • double → balanced, default, high performance
  • decimal → accurate, safe for money, slower
Choosing the right numeric type prevents bugs, improves performance, and ensures correctness.

Git Version Control Interview Questions and Answer

1. What is Git?

Git is a distributed version control system that tracks changes in source code and allows multiple developers to work on the same project without overwriting each other’s work.

2. How is Git different from GitHub?

Git is the tool used for version control on your local machine, while GitHub is a cloud platform that hosts Git repositories and provides collaboration features like pull requests, issues, CI/CD, and code reviews.

Example (if required):

  • Git = MS Word (the tool)
  • GitHub = Google Drive (where the file is stored and shared)

3. What is Version Control? Why do we need it?

Version Control is a system that tracks changes made to files over time, allowing you to restore previous versions, compare changes, and collaborate safely. We need version control to avoid losing work, track history, work collaboratively, and manage multiple versions of code without conflicts. It ensures safe, organized, and traceable development across teams.

4. What is a repository?

A repository is a storage location where your project’s files and their complete version history are kept. It can be local on your machine or remote on platforms like GitHub.

5. Difference between local repo and remote repo.

A local repository exists on your machine where you create, edit, and commit code.
A remote repository is hosted on a server like GitHub, GitLab, or Bitbucket and is used for sharing code and collaborating with others.

In simple terms:

  • Local repo = your personal copy
  • Remote repo = shared team copy

6. What is git init?

git init is a fundamental Git command that initializes a new Git repository. This action transforms a standard directory into a Git-managed project, enabling version control.

When git init is executed within a directory, it performs the following:
  • Creates a .git subdirectory: This hidden folder is the core of the Git repository. It contains all the necessary metadata for Git to track changes, including object databases, references (branches and tags), configuration files, and other internal structures.
  • Prepares for version control: The presence of the .git directory allows Git commands like git add, git commit, git branch, and others to function within that directory and its subdirectories.
  • Initializes a default branch: Typically, a default branch (often named main or master) is created that points to the repository's initial state.
Example: If you create a new project from scratch, the first step is often
git init

7. What does git clone do?

git clone is used to create a full local copy of a remote Git repository, including all files, commit history, branches, and tags. It essentially downloads the project from platforms like GitHub or GitLab to your machine so you can start working on it.

When you run:

git clone <repository-url>

Git creates a new folder, pulls all the project data, and automatically sets the remote name as origin, allowing you to sync changes later using git pull and git push.

In short, git clone is used when you want to start working on an existing project hosted remotely.

8. What is the difference between git clone and git pull?

git clone is used once to create a complete local copy of a remote repository.
It downloads everything, files, commit history, branches, and sets up the connection to the remote.
Use case: When you're starting with a project for the first time.

git pull is used after you have cloned the repository, to get the latest changes from the remote repository into your existing local repo.
It performs:
  • git fetch (downloads changes)
  • git merge (merges them into your current branch)
Use case: To keep your local copy updated with the team’s latest changes.

9. What is a commit?

A commit in Git is a snapshot of your project’s files at a specific point in time.
Whenever you make changes and run git commit, Git records:

  • The exact state of your files
  • Who made the change
  • When it was made
  • A message describing why the change was made

Commits form the history of your project, allowing you to go back to earlier versions, compare changes, and understand how the code evolved.

Each commit has a unique SHA-1 hash, which acts like an ID for that snapshot.

10. What is the staging area in Git?

The staging area (also called the index) is a place where Git collects and organizes your changes before creating a commit.

When you modify files, they don’t go directly into a commit.
You first add them to the staging area using:

git add <file>
This step allows you to:

  • Choose which changes you want to include in the next commit
  • Group related changes together
  • Avoid committing accidental or incomplete changes

The staging area acts as a buffer between your working directory and the final commit.
It gives developers control and ensures each commit is clean, meaningful, and intentional.

11. Explain the Git file lifecycle (untracked → tracked → staged → committed).

1. Untracked
A file is untracked when it exists in your project folder, but Git is not monitoring it yet. These are usually new files you just created.
Example: You create login.js → Git doesn’t know about it yet.

2. Tracked
A file becomes tracked as soon as you add it to Git using:
git add <file>

Tracked means Git is aware of the file and will monitor its future changes.
Tracked files can be unmodified, modified, or staged.

3. Staged (Index)
A staged file is one whose latest changes are prepared for the next commit.
When you run git add <file>, the file is moved from modifiedstaged.
This allows you to control exactly which changes will go into the next commit.

4. Committed
When you run:
git commit -m "message"
all staged changes are saved into Git’s repository history.
A commit represents a permanent snapshot that you can revert to or compare later.

Simple Summary
  • Untracked: Git doesn’t know about the file.
  • Tracked: Git knows the file exists.
  • Staged: File is ready to be committed.
  • Committed: File changes are permanently stored in Git history.
This lifecycle ensures clean, controlled, and well-organized version management.

12. What do git add . and git add -A do?

Both commands are used to move changes from the working directory to → staging area, but they behave slightly differently.

git add .
This command stages:
  • New files
  • Modified files
within the current directory and its subdirectories.
But it does NOT stage deleted files.
Example: If you delete a file, git add . will ignore that deletion.

git add -A
This command stages all changes in the entire repository, including:
  • New files
  • Modified files
  • Deleted files
It ensures your staging area represents the exact state of your working directory.

Simple Difference
  • git add . → add new + modified files (no deletions)
  • git add -A → add new + modified + deleted files (complete sync)
Most developers use git add -A when preparing a clean commit.

13. What does git status show?

git status gives a clear summary of the current state of your working directory and staging area.
It helps you understand what Git sees before you commit.

1. Untracked files: Files Git is not tracking yet (new files).
2. Modified files: Files that have been changed but are NOT staged.
3. Staged files: Files that are added to the staging area and will be included in the next commit.
4. Branch information: It shows which branch you're currently on and whether your branch is ahead/behind the remote.
5. Instructions/help: It often suggests next steps, like:
  • “Use git add to stage changes.”
  • “Use git restore to discard.”

14. What does git log do?

git log displays the history of commits in your repository.
It shows each commit in reverse chronological order (newest first), along with important details like:
  • Commit hash (SHA) → unique ID of the commit
  • Author → who made the commit
  • Date → when the commit was made
  • Commit message → why the change was made
Example:
commit 8f3a2c...
Author: John Doe
Date:   Tue Dec 2

    Added login validation

15. What is .gitignore? Why is it used?

.gitignore is a special configuration file in a Git project that tells Git which files or folders it should completely ignore.
Files listed inside .gitignore will not be tracked, not appear in git status, and won’t be added to commits, even if they exist in your project directory.

It is used to keep your repository clean and free from files that should not be version-controlled, such as:

  • Temporary or cache files (e.g., dist/, temp/)
  • Build outputs and compiled code (bin/, obj/)
  • Dependency folders (e.g., node_modules/)
  • Sensitive files (API keys, credentials)
  • IDE/system-specific files (.vscode/, .DS_Store)

Using .gitignore prevents unnecessary or private files from being committed, reducing repo size and avoiding security risks.

16. How to remove a file from Git but keep it locally?

To remove a file from Git’s tracking without deleting it from your local system, you use:

git rm --cached <file>
This command removes the file from the staging/index, meaning Git will no longer track its changes, but the file itself remains in your working directory.

After running this, you commit the change:

git commit -m "Stop tracking file"
If it's a file you never want Git to track again, add it to .gitignore.

17. What is a branch in Git?

A branch in Git is a separate line of development that allows you to work on new features, bug fixes, or experiments without affecting the main codebase.

When you create a branch, Git simply creates a new pointer to a commit. It doesn’t copy the entire project, so branching is very fast and lightweight.

Developers commonly use branches to isolate work:
  • main or master → stable production-ready code
  • feature/login → new login feature
  • bugfix/payment → fix for payment issue
Once the work is complete, the branch can be merged back into the main branch through a merge or pull request.

18. How to create a branch?

Use git branch <name> to create a branch, and git switch -c <name> to create and start working on it immediately.

19. Difference between git merge and git rebase.

git merge and git rebase both integrate changes from one branch into another, but they do it in very different ways, affecting your project history.
  • git merge: When you merge a branch, Git combines the commit histories of two branches and creates a merge commit.
  • git rebase: Rebase takes your commits and reapplies them on top of another branch, as if you created them from there originally.

In short:
  • Merge preserves history; Rebase rewrites it.
  • Merge is safer for collaboration; Rebase is best for clean, linear commits.

20. What is a merge conflict?

A merge conflict happens when Git is unable to automatically combine changes from two branches because both branches modified the same part of the same file, or one branch deleted something that the other branch edited.

Git doesn’t know which version is correct, so it stops the merge and asks the developer to resolve it manually.

During a merge conflict, Git marks the conflicting sections in the file using:

<<<<<< HEAD
Your changes
======
Other branch changes
>>>>>> branch-name

21. What is a detached HEAD state?

A detached HEAD state happens when your HEAD pointer (which normally points to the latest commit of a branch) points directly to a specific commit, tag, or old revision instead of a branch.

In this state, you are not on any branch.
You are simply viewing or modifying code at a past commit.

Example of entering detached HEAD:

git checkout a7c9d12
Here, HEAD points to commit a7c9d12, not to a branch like main or dev.

Any new commits you make in a detached HEAD are not tied to a branch, so they can be lost if you switch branches unless you create a new branch for them.

22. What is git checkout used for?

git checkout is used to switch branches or restore files, making it one of the most essential commands in Git.
In modern Git, git checkout has been split into two clearer commands:
  • git switch → for switching branches
  • git restore → for restoring files
But git checkout still works, and developers commonly use it.

23. What is a feature branch workflow?

A Feature Branch Workflow is a Git development strategy where each new feature, enhancement, or bug fix is developed in its own separate branch instead of directly in the main or develop branch.

The goal is to isolate work so developers can build, test, and review changes without affecting the stable codebase.

24. Difference between git pull and git fetch?

git pull and git fetch both interact with the remote repository, but they behave very differently.
git fetch - Downloads changes but DOES NOT merge

1. git fetch only retrieves new commits from the remote repository and updates your remote-tracking branches (like origin/main). It does not modify your working directory or your current branch.
Example:
git fetch
After fetching, you can compare or review the changes before merging manually:
git merge origin/main

When to use it:
  • When you want to see what others have changed without affecting your code
  • When you want full control before merging
2. git pull - Fetch + Merge (auto-merge)

git pull does both steps automatically:
  • Fetch latest changes
  • Merge them into your current branch
Example:
git pull
Your working directory is immediately updated with the remote changes.

When to use it:
  • When you want the latest code quickly
  • When you trust the remote changes and don’t need review
In short:
  • git fetch = safely download changes without touching your work.
  • git pull = download + merge changes into your current branch.
Most developers use git fetch when they want control, and git pull when they want quick updates.

25. How do you undo the last commit?

Undoing the last commit depends on whether the commit has been pushed and whether you want to keep the changes or discard them.

Here are the common and safe ways:
1. Undo last commit but KEEP the changes (soft reset)
Use this when you made a wrong commit message or forgot to include a file.
git reset --soft HEAD~1
This removes the commit but keeps all your changes staged.

2. Undo last commit but KEEP changes unstaged (mixed reset - default)
This removes the commit and moves your changes back to the working directory.
git reset HEAD~1
You can edit them again and recommit.

3. Undo last commit and REMOVE the changes (hard reset - destructive)
Use this only if you don’t need the changes.
git reset --hard HEAD~1
This deletes the commit and all file changes.

4. If the commit is already pushed - use revert (safe way)
If you’ve pushed the commit to a shared branch (like main), never reset.
Instead, create a new commit that reverses the old one:
git revert HEAD
This is safe for team collaboration.

In short:
  • Use reset to undo local commits (soft/mixed/hard).
  • Use revert to undo a commit that is already pushed and shared.

26. Difference between git revert and git reset.

The main difference is that git revert is a safe, forward-moving operation that creates a new commit to undo changes, preserving project history, while git reset rewrites history by moving the branch pointer to a previous commit, potentially losing work.
Example:
git revert <commit-id>
git reset --hard HEAD~1

In short:
  • Use git revert to safely undo a pushed commit without breaking history.
  • Use git reset to rewrite or clean up local commit history before pushing.

27. What is a soft, mixed, and hard reset?

git reset has three main modes that determine how the staging area and working directory are affected when you move the HEAD pointer to a previous commit.

28. What is an interactive rebase?

Interactive rebase is a powerful Git tool for modifying and cleaning up a series of commits in your project's history. It allows you to control exactly how commits are integrated or changed, rather than just blindly moving them. 

This process involves using the git rebase -i <base-commit> command, which opens a text editor showing a list of commits in the specified range. You can then use various commands to manipulate individual commits. 

Interactive rebase is typically used on local, unshared branches to create a clean, linear, and meaningful history before sharing it with a team.

29. How to squash commits?

Squashing commits means combining multiple commits into a single commit so that your Git history looks clean and meaningful (for example, before merging a feature branch).

The most common way to squash commits is to use an interactive rebase.

Steps to squash the last N commits

Suppose you want to squash the last 3 commits into one:

git rebase -i HEAD~3
This opens an editor showing something like:
pick a1b2c3 First commit
pick d4e5f6 Second commit
pick 789abc Third commit
You then change it to:
pick a1b2c3 First commit
squash d4e5f6 Second commit
squash 789abc Third commit
  • pick → keep this commit as is
  • squash (or s) → combine this commit into the one above
After saving and closing, Git will ask you to edit the final commit message (you can keep one or write a new combined message).

Important Note: If the branch is already pushed and used by others, squashing rewrites history, so you should coordinate with your team (or avoid squashing shared history).

30. What does git stash do?

git stash temporarily saves your uncommitted changes (both staged and unstaged) and cleans your working directory, allowing you to switch branches or pull updates without losing your work.

It’s like putting your work on a temporary shelf.

You use it when you need to:

  • Switch branches quickly
  • Pull the latest test code
  • Work on an urgent bug fix
  • Pause your current work without committing half-done changes

Example scenario:

You're working on feature/login, but suddenly need to fix a production bug on main.

Your changes aren’t complete, so you can't commit them.

  • git stash → saves changes and cleans the working directory
  • git stash list → shows stashed items
  • git stash apply → restores the most recent stash
  • git stash drop → removes the stash
  • git stash pop → restores + deletes the stash in one step

31. Fork vs Clone vs Branch.

Forking creates an independent copy of a repository on a hosting service (like GitHub) for separate development, Cloning makes a local copy on your machine for offline work, and Branching creates isolated development lines within a single repository (local or remote) for features, all enabling parallel work, but Forks are for public contribution, Clones for local sync, and Branches for features inside a project.

32. What happens during git pull --rebase?

The git pull --rebase command is a combination of git fetch and git rebase. It is used to update a local branch with changes from a remote branch while maintaining a linear commit history.
Here is a breakdown of what happens:
  • git fetch: Git first fetches the latest changes from the remote repository and updates the remote-tracking branch (e.g., origin/main). These changes are downloaded but not yet integrated into your local working branch.
  • git rebase: After fetching, Git then takes your local commits (those that are not yet pushed to the remote) and temporarily "puts them aside." It then "rewinds" your local branch to the point where it diverged from the remote-tracking branch. Finally, it reapplies your local commits one by one on top of the newly fetched remote changes.

33. You accidentally deleted a branch—how to restore it?

To restore a deleted Git branch, use git reflog to find the commit hash (SHA) of the branch's tip, then git checkout -b <branch-name> <sha> to recreate it locally; for remote/hosted repos (GitHub, Azure DevOps), use the platform's UI to find branch deletions in activity logs or closed pull requests and click "Restore".

DON'T MISS

Tech News
© all rights reserved
made with by AlgoLesson