GIT : Behind the scene

👋 Hey there! I’m Abhishek Dubey, a passionate technologist and travel enthusiast driven by the belief that technology has the power to bridge gaps and build connections that transform lives. With over 10 years in coding, my journey spans from hands-on roles in the stone industry to spearheading projects that merge innovation with real-world applications. Currently, I’m the mastermind behind Sampresha, a platform designed for avid travelers who feel underserved by mainstream travel platforms. Sampresha aims to create inclusive experiences by tapping into my love for exploration and deep industry insight. I'm also building BuyMyShare, a centeralized real estate tailored for middle class . When I’m not writing code or brainstorming the next big feature, you’ll find me sharing insights on development, building community-driven solutions, and exploring new trends in full-stack and Reading something interesting , Drawing or Pottery. I love making tech accessible, whether that’s through speaking sessions, or guiding new devs. Let’s connect, share, and build together on this incredible journey of tech and innovation! 🚀
Introduction to Git and Version Control Git is a powerful version control system used to manage and track changes in code. Whether you're a beginner or an experienced developer, understanding the nuances of Git commands can greatly enhance your coding workflow. This guide aligns with the instructional content of various resources and practical videos, showcasing the most essential Git commands and workflows.
Initial Setup Setting up Git for the first time? Begin with these commands to configure your identity:
git config --global user.email "me@example.com"
git config --global user.name "My Name"
Creating and Initializing Repositories To create a repository and initialize Git:
mkdir project-directory
cd project-directory
git init
This command sets up an empty Git repository, ready for commits.
Basic Workflow with Git The typical Git workflow involves several key steps:
Staging Changes: Use
git add <filename>to add your changes to the staging area.Committing Changes: Use
git commit -m "Your commit message"to commit staged changes.Viewing Status: Run
git statusto check the state of your working directory and staged changes.
Understanding the Git Directory The .git folder within your project directory is essential. It houses all information about your repository, including configurations, the staging area, and change history.
Diffing Files and Applying Patches Finding differences between files is crucial for identifying changes. The diff command comes to the rescue:
diff -u file1.py file2.py
For a more comprehensive view, save the output to a file:
diff -u file1.py file2.py > differences.diff
Then, apply changes using patch:
patch file1.py < differences.diff
Practical Application of Patching Imagine you’ve edited cpu_usage.py and need to track changes for debugging:
#!/usr/bin/env python3
import psutil
def check_cpu_usage(percent):
usage = psutil.cpu_percent(1)
print("DEBUG: usage: {}".format(usage))
return usage < percent
if not check_cpu_usage(75):
print("ERROR! CPU is overloaded")
else:
print("Everything ok")
The changes can be verified and applied using diff and patch commands as previously shown.
Effective Commit Messages Crafting a clear and descriptive commit message helps maintainable code history:
Summary: A concise 50-character headline.
Description: Detailed explanation within 72 characters per line.
Reference: Include links to related issues or bugs.
Undoing Changes Mistakes happen. Fortunately, Git offers tools like git reset, git revert, and git checkout for undoing changes. git revert is a safe way to reverse commits without rewriting history, unlike git reset.
Advanced Tips for Version Control Mastery
Branches: Use
git branch <branch-name>to create a new branch andgit checkout <branch-name>to switch to it.Merging: Integrate changes from different branches with
git merge.Resolving Merge Conflicts: Carefully edit conflicting sections in files and use
git addto mark conflicts as resolved.
Understanding diff in Depth: A Developer's Guide
The diff command is a powerful utility for comparing the content of files line by line. It allows you to identify what changes have been made between two versions of a file, which is critical for code review, debugging, and version control. Here's an in-depth look at how to leverage diff for a productive coding workflow.
Files for Comparison
# file1.py
def greet_user(name):
return f"Hello, {name}!"
def add_numbers(a, b):
return a + b
# file2.py
def greet_user(name):
return f"Hi, {name}! Welcome."
def add_numbers(a, b):
return a + b
def multiply_numbers(a, b):
return a * b
Basic Usage of diff
The simplest use of diff is to compare two files try creating two text file using:
echo "Apple \n Banana \n orange" >> file1.txt
echo "Grapes \n Pineapple" >> file2.txt
cat file1.txt >> file2.txt
diff file1.txt file2.txt
diff -u file1.py file2.py
By default, diff outputs the changes needed to turn file1.py into file2.py. Lines that have been added are prefixed with >, while those removed are prefixed with <.
Example:
--- file1.py 2024-10-31 10:00:00.000000000
+++ file2.py 2024-10-31 10:02:00.000000000
@@ -1,5 +1,7 @@
# file1.py
def greet_user(name):
- return f"Hello, {name}!"
+ return f"Hi, {name}! Welcome."
def add_numbers(a, b):
return a + b
+
+def multiply_numbers(a, b):
+ return a * b
Files for Comparison
# file1.py
def greet_user(name):
return f"Hello, {name}!"
def add_numbers(a, b):
return a + b
# file2.py
def greet_user(name):
return f"Hi, {name}! Welcome."
def example_function():
return "Hello World"
return "Hello, Git World"
Unified Diff Format
For better readability, especially when working with code, you can use the -u (unified) option:
diff -u file1.py file2.py
This format displays the context around the changes, making it easier to understand what has been modified.
Example Output:
code--- file1.py 2024-10-31 09:30:00.000000000
+++ file2.py 2024-10-31 09:32:00.000000000
@@ -1,4 +1,4 @@
def example_function():
- return "Hello World"
+ return "Hello, Git World"
Explanation:
The
---and+++lines show the file names and timestamps.The
@@ -1,4 +1,4 @@line indicates the chunk location.Lines prefixed with
-show what was removed.Lines prefixed with
+show what was added.
Creating a Patch File
You can save diff output to a file for later use as a patch:
diff -u file1.py file2.py > changes.patch
Apply the patch using the patch command:
patch file1.py < changes.patch
This updates file1.py with the changes from changes.patch, effectively transforming it into file2.py.
Recursive Diff for Directories
To compare directories recursively and identify differences in all files within them:
diff -ur dir1/ dir2/
The -r option makes diff recursively compare all files, and -u ensures the unified format for better readability.
Ignoring Specific Differences
To ignore specific types of differences, use options such as:
Ignore case:
diff -i file1.txt file2.txtIgnore white space:
diff -w file1.txt file2.txt
Advanced Use: diff with Git
While diff is a standalone command, it is also integrated into Git for comparing changes in your repository:
Compare working directory with the last commit:
git diffCompare staged changes:
git diff --cachedCompare two commits:
git diff commit1 commit2
Git Unified Diff Output: The format aligns closely with diff -u, allowing developers to track code modifications seamlessly during code reviews.
Understanding patch in Depth: A Developer's Guide
The patch command works alongside diff to apply changes from one file to another, streamlining the process of updating or reverting code. Here’s a detailed guide on how to create, apply, and manage patches effectively.
What is a Patch?
A patch file is a text file generated by the diff command that records the differences between two versions of a file or directory. It can be used to update the original file to match the changes found in another file.
Creating a Patch File
To create a patch file using diff, use the following syntax:
diff -u file1.py file2.py > changes.patch
-u: Generates a unified diff, which is easier to read and apply.> changes.patch: Redirects the output to a file namedchanges.patch.
Example Patch File (changes.patch):
textCopy code--- file1.py 2024-10-31 10:00:00.000000000
+++ file2.py 2024-10-31 10:02:00.000000000
@@ -1,5 +1,7 @@
# file1.py
def greet_user(name):
- return f"Hello, {name}!"
+ return f"Hi, {name}! Welcome."
def add_numbers(a, b):
return a + b
+
+def multiply_numbers(a, b):
+ return a * b
Applying a Patch
Once you have a patch file, use the patch command to apply it to the original file:
patch file1.py < changes.patch
What Happens When You Apply a Patch:
The original
file1.pywill be updated to reflect the changes found inchanges.patch.patchwill check the patch file and apply the modifications line by line.
Output of Applying a Patch:
patching file file1.py
Verifying Patch Application
To confirm that the patch has been applied correctly, you can use diff again:
diff file1.py file2.py
If the patch was applied successfully, there should be no differences between file1.py and file2.py.
Reversing a Patch
If you need to undo the changes made by a patch, you can reverse it:
patch -R file1.py < changes.patch
-R: Tellspatchto reverse the changes in the patch file.
Output of Reversing a Patch:
patching file file1.py
Handling Patch Conflicts
Sometimes, the patch command encounters issues if the original file has changed significantly since the patch was created. In these cases, you might see:
#1 FAILED at line 3.
1 out of 1 hunk FAILED -- saving rejects to file1.py.rej
Hunk: A portion of the patch file that corresponds to changes in a specific part of the file..rejfile: Contains the rejected changes. You’ll need to manually review and apply these changes.
Applying Patches to Multiple Files
If you have a patch for a directory (created with diff -ur), apply it recursively:
patch -p1 < changes.patch
-p1: Strips the first part of the file path (useful for patches with full file paths).< changes.patch: Applies the changes from the patch file.
Tips for Working with Patches
Backup your files: Always create a backup before applying patches.
Dry-run option: Use
--dry-runto simulate the patching process without making any changes:patch --dry-run file1.py < changes.patch
Check file versions: Ensure the patch file matches the version of the file it’s being applied to.
How diff and patch Work in the Backend
The diff and patch tools play crucial roles in managing code versions, enabling developers to identify changes and apply updates seamlessly. To understand how they work in the backend and create diff patches from scratch, we need to explore the internal mechanisms and manual creation process.
1. The Backend Mechanics of diff
The diff command works by comparing two files line by line. Here's a detailed breakdown of how it operates:
File Comparison:
diffreads two files and compares them line by line. It identifies differences and generates a "difference set" which includes line numbers and content modifications.Algorithm: The core of
diffuses algorithms such as the Longest Common Subsequence (LCS) algorithm to determine the minimal set of edits required to transform one file into another.Unified Format: When using the
-uoption,diffoutputs the differences in a unified format. This format shows context lines and highlights added or removed lines using+and-signs.
Example of How diff Works Internally: Consider the comparison between file1.py and file2.py. The diff command scans both files and identifies matching lines and changes. For each change:
Matching blocks are noted and ignored.
Changed blocks are stored with metadata (line numbers, original and new content).
2. The Backend Mechanics of patch
The patch command takes the difference set produced by diff and applies it to a file. Here’s how it functions internally:
Reading Patch File:
patchreads the patch file, which contains the difference set with metadata (file paths, line numbers, context).Applying Changes: For each change,
patchnavigates to the specified line in the target file and makes the edit (adding or removing lines as indicated).Verification: If the file matches the expected format,
patchapplies the changes seamlessly. If there are discrepancies (e.g., line numbers are off due to intermediate changes),patchmay create a.rejfile for manual review.
3. Creating a diff Patch from Scratch
To create a diff patch manually, follow these steps:
Examine the Original and Modified Files: Prepare two versions of a file: the original (
file1.py) and the modified version (file2.py).Example Original File (
file1.py):def greet_user(name): return f"Hello, {name}!"Example Modified File (
file2.py):def greet_user(name): return f"Hi, {name}! Welcome."Generate a Diff Manually: Create a patch in the unified format by manually comparing the files and noting differences:
Manual Diff Format:
--- file1.py 2024-10-31 10:00:00.000000000 +++ file2.py 2024-10-31 10:02:00.000000000 @@ -1,3 +1,3 @@ def greet_user(name): - return f"Hello, {name}!" + return f"Hi, {name}! Welcome."---: Indicates the original file.+++: Indicates the modified file.@@ -1,3 +1,3 @@: The chunk header showing the line numbers of the changes.-: Line removed from the original file.+: Line added in the modified file.
Save as a Patch File: Save the above diff content as
changes.patch.
4. Applying the Patch
To apply your manually created patch:
patch file1.py < changes.patch
Creating a Patch File Without diff
Manually Compare Files: Compare
file1.pyandfile2.pyside by side, noting the line differences and context.Format the Patch: Use the unified diff format to describe the changes as demonstrated earlier.
Save and Apply: Save the formatted patch and apply it using the
patchcommand.
How Diff and Patch Ensure Accuracy
- Checksums and Metadata: Internally,
patchuses checksums and context lines to ensure that the changes apply correctly, even if line numbers have shifted due to other edits.
Failure Handling: If a change can’t be applied cleanly, patch produces a .rej file containing the rejected hunk for manual review.
Basic code of how diff and patch work
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_LINE_LENGTH 1024
//basic look of diff working
void generateDiff(const char *file1, const char *file2, const char *diffFile) {
FILE *fp1 = fopen(file1, "r");
FILE *fp2 = fopen(file2, "r");
FILE *fpDiff = fopen(diffFile, "w");
if (!fp1 || !fp2 || !fpDiff) {
fprintf(stderr, "Error opening files.\n");
exit(EXIT_FAILURE);
}
char line1[MAX_LINE_LENGTH];
char line2[MAX_LINE_LENGTH];
int lineNum = 1;
while (fgets(line1, MAX_LINE_LENGTH, fp1) && fgets(line2, MAX_LINE_LENGTH, fp2)) {
if (strcmp(line1, line2) != 0) {
fprintf(fpDiff, "--- Line %d ---\n", lineNum);
fprintf(fpDiff, "- %s", line1);
fprintf(fpDiff, "+ %s", line2);
}
lineNum++;
}
// Handle remaining lines in either file
while (fgets(line1, MAX_LINE_LENGTH, fp1)) {
fprintf(fpDiff, "--- Line %d ---\n", lineNum);
fprintf(fpDiff, "- %s", line1);
lineNum++;
}
while (fgets(line2, MAX_LINE_LENGTH, fp2)) {
fprintf(fpDiff, "--- Line %d ---\n", lineNum);
fprintf(fpDiff, "+ %s", line2);
lineNum++;
}
fclose(fp1);
fclose(fp2);
fclose(fpDiff);
printf("Diff file generated successfully as %s.\n", diffFile);
}
int main() {
const char *file1 = "file1.txt";
const char *file2 = "file2.txt";
const char *diffFile = "changes.diff";
generateDiff(file1, file2, diffFile);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_LINE_LENGTH 1024
//basic of patch working
void applyPatch(const char *originalFile, const char *diffFile, const char *patchedFile) {
FILE *fpOrig = fopen(originalFile, "r");
FILE *fpDiff = fopen(diffFile, "r");
FILE *fpPatched = fopen(patchedFile, "w");
if (!fpOrig || !fpDiff || !fpPatched) {
fprintf(stderr, "Error opening files.\n");
exit(EXIT_FAILURE);
}
char line[MAX_LINE_LENGTH];
char diffLine[MAX_LINE_LENGTH];
int lineNum = 1;
int currentDiffLine = -1;
while (fgets(line, MAX_LINE_LENGTH, fpOrig)) {
// Check if we have a diff entry for this line number
if (fgets(diffLine, MAX_LINE_LENGTH, fpDiff)) {
if (strncmp(diffLine, "--- Line", 8) == 0) {
sscanf(diffLine, "--- Line %d ---", ¤tDiffLine);
}
if (currentDiffLine == lineNum) {
if (fgets(diffLine, MAX_LINE_LENGTH, fpDiff) && diffLine[0] == '-') {
// Line from original matches and should be replaced
fgets(diffLine, MAX_LINE_LENGTH, fpDiff); // Read the '+' line to write to patched file
fputs(diffLine + 2, fpPatched); // Skip '+ ' and write to patched file
}
currentDiffLine = -1; // Reset after handling
} else {
fputs(line, fpPatched); // Write original line if no diff entry
}
} else {
fputs(line, fpPatched); // Write remaining lines if no more diff entries
}
lineNum++;
}
fclose(fpOrig);
fclose(fpDiff);
fclose(fpPatched);
printf("Patched file generated successfully as %s.\n", patchedFile);
}
int main() {
const char *originalFile = "file1.txt";
const char *diffFile = "changes.diff";
const char *patchedFile = "file1_patched.txt";
applyPatch(originalFile, diffFile, patchedFile);
return 0;
}


