Git Fundamentals
1. What is Git, and how does it differ from older Version Control Systems like SVN?
Git is a distributed version control system (DVCS), meaning every developer has a complete copy of the repository, including its full history, locally on their machine. This contrasts with centralized VCS like SVN (Subversion), where a single server hosts the entire repository, and developers only check out working copies. The key difference lies in offline work capability, speed, and resilience. Git operations like committing, branching, and merging are significantly faster because they primarily interact with the local repository. If the central server goes down, SVN users cannot commit or access history, while Git users remain fully operational.
2. Explain the three states of files in Git.
Git tracks files in three primary states:
- Working Directory: This is where you make changes to your files. These are the actual files on your filesystem.
- Staging Area (Index): This is an intermediate area where you prepare changes before committing them. You add files here using
git add. It allows you to craft precise commits by selecting specific changes. - Git Repository: This is where Git permanently stores your project's history as commits. Once changes are committed from the staging area, they become part of the repository's history.
git add . # Move changes from Working Dir to Staging Area
git commit -m "Commit message" # Move changes from Staging Area to Repository
3. How do you initialize a new Git repository and add files to it?
To initialize a new Git repository in your current directory, you use the git init command. This creates a hidden .git subdirectory that contains all the necessary Git metadata. After initialization, you can add existing files to the staging area using git add <file_name> or git add . to add all modified/new files. Finally, you commit these staged changes to the repository with git commit -m "Initial commit".
mkdir my_project
cd my_project
git init
echo "Hello World" > README.md
git add README.md
git commit -m "Add initial README"
4. What does git status do, and why is it important?
git status is a crucial command that displays the current state of your working directory and staging area. It tells you which files are untracked, modified, or staged for the next commit. It also indicates your current branch and whether your local branch is ahead or behind its remote counterpart. Regularly checking git status helps you understand what changes are pending, prevents accidental commits of unintended files, and guides your workflow, ensuring you're committing precisely what you intend.
git status
#
#
5. Explain the purpose of git commit and its best practices.
git commit captures the staged changes (from the staging area) and permanently stores them as a snapshot in the repository's history. Each commit has a unique SHA-1 hash, a commit message, an author, and a timestamp. Best practices for commit messages include:
- Concise summary line (50-72 chars): Explains what the commit does.
- Blank line: Separates summary from body.
- Detailed body (optional): Explains why the changes were made, context, and potential impact.
- Use imperative mood: "Fix bug" instead of "Fixed bug."
- Atomic commits: Each commit should represent a single, logical change.
6. What is HEAD in Git?
HEAD is a symbolic reference in Git that points to the tip of the current branch you are working on. It essentially indicates your current position in the repository's history. When you switch branches or make new commits, HEAD automatically updates to point to the latest commit of that branch. It's often visualized as a pointer to the "current commit." In most cases, HEAD points to a branch name (e.g., ref: refs/heads/main), which then points to a specific commit. When you git checkout <commit_hash>, HEAD detaches, pointing directly to a commit rather than a branch.
7. Describe the .git directory and its essential contents.
The .git directory is the core of any Git repository. It contains all the information Git needs to manage your project's history, revisions, and branches. It's typically hidden and should not be manually modified.
Essential contents include:
HEAD: A file indicating the current branch or commit.config: Repository-specific configuration settings.hooks: Client-side or server-side scripts that Git executes before or after certain events.objects: The database for all your content (commits, trees, blobs), stored as compressed objects.refs: Contains pointers to commits for branches (heads) and tags (tags).
Branching and Merging Strategies
8. What is a Git branch, and why is it useful?
A Git branch is essentially a lightweight, movable pointer to a commit. By default, every repository starts with a main (or master) branch. Branches allow developers to diverge from the main line of development to work on new features, bug fixes, or experiments without affecting the stable codebase. This isolation is highly useful for concurrent development, enabling multiple people to work on different tasks simultaneously without stepping on each other's toes, and later integrating their work safely.
git branch feature/new-login
git checkout feature/new-login # Switch to the new branch
9. How do you create, switch, and delete branches?
- Create:
git branch <branch_name>creates a new pointer to the current commit, leaving you on the old branch. - Switch:
git checkout <branch_name>moves yourHEADpointer and updates your working directory to reflect the snapshot of the new branch.git switch <branch_name>is a newer, safer alternative for switching. - Create and Switch (shortcut):
git checkout -b <new_branch_name>orgit switch -c <new_branch_name>. - Delete:
git branch -d <branch_name>deletes a merged branch.git branch -D <branch_name>force deletes an unmerged branch (use with caution).
git branch develop
git switch develop
git switch main
git branch -d develop
10. Explain git merge and the concept of a fast-forward merge.
git merge is used to integrate changes from one branch into another. When merging, Git finds a common ancestor commit and creates a new merge commit that combines the histories of both branches. This merge commit has two parent commits.
A fast-forward merge occurs when the branch you're merging into has not diverged from the branch you're merging from. In this scenario, Git simply moves the pointer of the target branch forward to the tip of the source branch, effectively "fast-forwarding" without creating a new merge commit. This maintains a linear history.
git merge feature-x # This would be a fast-forward merge
11. What is git rebase and when would you use it instead of git merge?
git rebase is used to integrate changes from one branch onto another by rewriting commit history. Instead of creating a new merge commit, rebase takes the commits from your feature branch, "undoes" them, applies the latest commits from the target branch (e.g., main), and then reapplies your feature branch commits on top. This results in a linear history, making the commit log cleaner and easier to read.
You might use rebase when:
- You want to maintain a clean, linear project history.
- You're working on a private feature branch and want to incorporate upstream changes without cluttering history with merge commits.
- You want to squash multiple small commits into a single, meaningful one before merging.
You should avoid rebasing commits that have already been pushed to a shared remote repository, as it rewrites history and can cause problems for collaborators.
12. Compare git merge and git rebase using a table.
| Feature | git merge | git rebase |
|---|---|---|
| History | Preserves exact history, adds a new merge commit. | Rewrites commit history, creates a linear history. |
| Commit IDs | Original commit IDs preserved. | New commit IDs generated for rebased commits. |
| Conflicts | Resolved once at the merge commit. | Resolved sequentially for each replayed commit. |
| Use Case | Integrating public branches, maintaining complete history, simple integration. | Cleaning up local branch history, integrating upstream changes linearly, preparing for PRs. |
| Safety | Generally safer, especially for shared branches. | Can be dangerous if used on shared branches already pushed. |
13. How do you resolve a merge conflict?
Merge conflicts occur when Git cannot automatically reconcile diverging changes between two branches. To resolve:
- Git will pause the merge and inform you of the conflicting files.
- Use
git statusto see which files are unmerged. - Open the conflicting files; Git inserts conflict markers (
<<<<<<<,=======,>>>>>>>) showing the differing versions. - Manually edit the file to choose the desired code, removing the markers.
- After resolving,
git add <conflicting_file>to stage the resolved version. - Finally,
git committo complete the merge (Git pre-populates the merge commit message). You can also use a mergetool like VS Code, KDiff3, or Meld for visual conflict resolution.
<<<<<<< HEAD
This is from the current branch (HEAD, usually main).
=======
This is from the feature branch I'm merging.
>>>>>>> feature-branch
14. Explain the Git Flow and GitHub Flow branching models.
Git Flow: A highly structured branching model typically used for projects requiring strict release management. It uses two long-lived branches: master (for releases) and develop (for ongoing development). Short-lived branches (feature, release, hotfix) branch off and merge into these.
- Pros: Clear roles, strong separation of concerns, good for scheduled releases.
- Cons: Can be complex for small teams, many branches.
GitHub Flow: A simpler, lightweight, and continuous delivery-oriented model. It uses only one long-lived branch: main (always deployable). All development happens in short-lived feature branches that branch off main, are merged back into main via pull requests after review, and then immediately deployed.
- Pros: Simpler, faster iterations, continuous deployment.
- Cons: Less structured, relies heavily on good test coverage and frequent deployments.
Advanced Git Operations
15. What is git stash and when would you use it?
git stash temporarily saves your uncommitted changes (both staged and unstaged) in your working directory, allowing you to switch contexts or address an urgent bug without committing incomplete work. It effectively "stashes" your modifications onto a stack, reverting your working directory to a clean state.
You'd use git stash when:
- You need to quickly switch branches to work on something else.
- You pulled new changes and have local modifications that would conflict.
- You want to discard your current work temporarily but might need it later.
git stash save "Work in progress on feature X"
git stash list
git stash pop # Apply the last stashed changes and remove from stash list
16. Describe git cherry-pick and its common use cases.
git cherry-pick allows you to apply a specific commit from one branch onto another branch. Instead of merging an entire branch, it selectively brings over a single commit. Git attempts to reapply the changes introduced by that commit onto your current HEAD.
Common use cases:
- Hotfixes: Applying a critical bug fix from a development branch directly to a release branch without merging the entire feature branch.
- Backporting: Bringing specific features or fixes from a newer branch to an older, stable branch.
- Refactoring: Moving a single, self-contained commit to a different part of the history. Be aware that cherry-picking creates a new commit with a different SHA-1, effectively duplicating the changes.
git cherry-pick <commit_hash>
17. Explain the differences between git reset --soft, --mixed, and --hard.
git reset is a powerful command used to undo changes by moving the HEAD pointer to a specified commit. The --soft, --mixed, and --hard options control how the working directory and staging area are affected.
--soft: MovesHEADto the target commit. Keeps changes in the staging area and working directory. Useful for modifying the last commit message or combining multiple commits.--mixed(default): MovesHEADto the target commit and unstages changes (moves them to the working directory). Clears the staging area. Useful for uncommitting recent changes while keeping your work.--hard: MovesHEADto the target commit and discards all changes in both the staging area and working directory. This is destructive and should be used with extreme caution, as it permanently removes uncommitted work.
git reset --soft HEAD~1 # Undo last commit, keep changes staged
git reset HEAD~1 # Undo last commit, unstage changes (default --mixed)
git reset --hard HEAD~1 # Undo last commit, discard all changes
18. What is git reflog and how can it help recover lost commits?
git reflog (reference logs) is a powerful command that records every single action that modifies HEAD or other references in your local repository. This includes commits, merges, rebases, checkouts, resets, and more. It acts as a safety net, showing a chronological history of where your HEAD has been. Each entry has a unique index and a commit hash.
If you accidentally reset --hard or delete a branch, git reflog can show you the SHA-1 of the "lost" commit. You can then use git cherry-pick <SHA-1> or git reset --hard <SHA-1> to recover your changes, essentially time-traveling back to that point in history. It's an invaluable tool for disaster recovery in Git.
git reflog
19. How do you rewrite commit history, and when should you avoid it?
You can rewrite commit history using commands like git rebase -i (interactive rebase), git commit --amend, and git reset.
git commit --amend: Changes the most recent commit, allowing you to modify its message or add/remove files.git rebase -i <commit_hash>: Allows you to interactively reorder, squash, edit, or drop commits within a specified range, effectively cleaning up your history.
You should strongly avoid rewriting history that has already been pushed to a shared remote repository. Doing so changes the commit IDs, creating a diverging history for anyone who has pulled your original commits. This can lead to complex synchronization problems, requiring collaborators to force push or rebase their own work, which is disruptive and error-prone. Only rewrite history on branches that exist solely on your local machine.
20. When would you use git revert instead of git reset?
Both git revert and git reset undo changes, but they do so differently and are used in different scenarios:
git revert: Creates a new commit that undoes the changes introduced by a specific prior commit. It does not alter existing history.- Use case: To undo changes that have already been pushed to a shared remote repository. Since it creates a new commit, it maintains a clean, linear history without rewriting public history, making it safe for collaboration.
git reset: Moves theHEADpointer (and optionally the staging area and working directory) to a previous commit, effectively rewriting history.- Use case: To undo changes on your local, unpushed branches. It's destructive with
--hardand should not be used on shared branches.
- Use case: To undo changes on your local, unpushed branches. It's destructive with
Choose git revert for public history, git reset for private, local history.
git revert HEAD # Creates a new commit that undoes the last commit
Remote Repositories & Collaboration (GitHub Specifics)
21. Explain the concepts of git fetch, git pull, and git push.
These commands manage synchronization between your local repository and a remote repository (like GitHub).
git fetch: Downloads new data (commits, branches, tags) from the remote repository to your local repository. It does not modify your local working directory or current branch. It only updates your remote-tracking branches (e.g.,origin/main), allowing you to see what's changed upstream.git pull: A convenience command that is essentiallygit fetchfollowed bygit merge. It fetches changes from the remote and then automatically merges them into your current local branch. This updates your working directory.git push: Uploads your local commits to the remote repository. It sends changes from your local branch to its corresponding remote-tracking branch. You typicallygit push origin <branch_name>.
git fetch origin
git pull origin main
git push origin feature/new-design
22. What is a remote repository, and how do you manage them?
A remote repository is a version of your project hosted on the internet or network, often on platforms like GitHub, GitLab, or Bitbucket. It serves as a central point of collaboration and a backup for your project.
You manage remotes using git remote:
git remote add <name> <url>: Adds a new remote (e.g.,origin).git remote -v: Lists existing remotes and their URLs.git remote show <name>: Displays detailed information about a remote.git remote remove <name>: Removes a remote. The default remote name is typicallyorigin.
git remote add origin https://github.com/user/repo.git
git remote -v
23. What is a Pull Request (PR) on GitHub, and how does it facilitate collaboration?
A Pull Request (PR) (or Merge Request on GitLab) is a mechanism for reviewing and proposing changes before they are integrated into a target branch (e.g., main). When a developer finishes work on a feature branch, they open a PR to notify collaborators.
PRs facilitate collaboration by:
- Code Review: Others can review the proposed changes, provide feedback, and suggest improvements.
- Discussion: A dedicated space for discussion, comments, and questions related to the changes.
- CI/CD Integration: Automatically trigger checks (tests, linters) via GitHub Actions.
- Tracking: Provides a clear record of changes, reviews, and decisions. Once approved, the changes can be merged into the target branch, often by the PR creator or a maintainer.
24. Explain the concept of forking a repository on GitHub.
Forking a repository on GitHub means creating a personal copy of another user's repository under your own GitHub account. This copy is completely independent of the original ("upstream") repository. You typically fork a repository when:
- You want to contribute to an open-source project but don't have write access to the original.
- You want to experiment with a project without affecting the original. Once forked, you clone your personal fork to your local machine, make changes, commit them, and push them to your fork. To propose changes back to the original project, you create a Pull Request from your fork's branch to the upstream repository's branch.
25. How do you keep your forked repository synchronized with the original upstream repository?
To keep your forked repository synchronized with the original ("upstream") repository:
- Add the upstream remote: First, you need to add the original repository as a new remote to your local clone of your fork.
bash
git remote add upstream https://github.com/original-owner/original-repo.git git remote -v - Fetch changes from upstream: Fetch all branches from the upstream remote.
bash
git fetch upstream - Merge/Rebase into your local branch: Switch to your local
main(ormaster) branch and merge or rebase the upstream'smainbranch into it.bashgit switch main git merge upstream/main # Or git rebase upstream/main - Push to your fork: Push the updated
mainbranch to your GitHub fork.bashgit push origin main
Troubleshooting & System Design Scenarios
26. You committed sensitive information (like API keys) by mistake. How do you remove it from history?
This is a critical scenario requiring history rewriting.
- Stop using the affected credentials immediately. Revoke or regenerate them.
- Identify the commit(s): Use
git log --onelineto find the SHA-1 of the commit(s) containing the sensitive data. - Use
git rebase -iorgit filter-repo:git rebase -i <commit_before_sensitive_data_commit>: Interactively rebase, mark the problematic commit withedit, remove the file,git add -u,git commit --amend, thengit rebase --continue. This is suitable for a few recent commits.git filter-repo(recommended for older/many commits): A powerful tool designed specifically for cleaning history. Install it, then usegit filter-repo --path sensitive_file.txt --invert-paths. This rewrites the entire history to remove the file.
- Force push: After rewriting history, you must
git push --force-with-leaseto update the remote. Inform collaborators as they will need to rebase their work.
27. Your feature branch diverged significantly from main. What steps would you take to integrate it smoothly?
When a feature branch diverges, frequent synchronization is key.
- Fetch latest
main:git fetch origin main. - Choose strategy:
mergeorrebase:- If your feature branch is private/not yet pushed:
git rebase origin/main. This will apply your feature branch commits on top of the latestmain, creating a clean, linear history. Resolve conflicts as they arise during the rebase. - If your feature branch is shared/public:
git merge origin/main. This will create a merge commit, preserving the history of both branches. Resolve conflicts once.
- If your feature branch is private/not yet pushed:
- Test thoroughly: After integration, run all tests to ensure no regressions were introduced.
- Push to remote: Push your updated feature branch. If you rebased and it was already pushed, you'll need
git push --force-with-lease.
28. How would you structure a large repository (monorepo vs. polyrepo) and manage dependencies in Git?
- Monorepo: A single repository containing multiple projects or modules, often with shared dependencies.
- Pros: Easier code sharing/refactoring across projects, atomic commits across services, simplified dependency management (single version).
- Cons: Can be slow for Git operations on huge repos, potential for long CI/CD pipelines, harder to manage permissions.
- Git Management: Use tools like Git Submodules or Git Subtree for internal dependencies, or language-specific package managers (npm workspaces, Cargo workspaces, Maven multi-modules) for dependency resolution within the monorepo.
- Polyrepo: Each project resides in its own repository.
- Pros: Clear ownership, smaller repository sizes, independent deployments, easier permission management.
- Cons: Complex dependency management across repos, difficulty in atomic changes across services, harder to discover related code.
- Git Management: Rely on package managers (pip, npm, nuget) and versioning strategies (Semantic Versioning) to manage dependencies between distinct repositories.
The choice depends on team size, project interdependencies, and desired deployment strategies.
29. Describe how Git integrates with CI/CD pipelines (e.g., GitHub Actions).
Git is fundamental to CI/CD. When changes are pushed to a Git repository, hooks (like webhooks) notify the CI/CD system (e.g., GitHub Actions, Jenkins, GitLab CI).
- Trigger: A
git push,pull request, ortagcreation event triggers a workflow defined in the repository (e.g.,.github/workflows/*.ymlfor GitHub Actions). - Checkout: The CI/CD runner clones the specific commit/branch from the Git repository.
- Build & Test: The pipeline executes build steps, runs unit, integration, and end-to-end tests on the code fetched from Git.
- Deploy: If tests pass, the pipeline proceeds to deploy the application. This often involves fetching specific commit hashes or tags for reliable deployment.
- Status Reporting: The CI/CD system reports the status (success/failure) back to GitHub, often visible on pull requests or commit pages, directly leveraging Git's commit history for traceability. Git tags are often used to mark releases for deployment.
30. What are Git hooks, and can you give an example of their use?
Git hooks are scripts that Git executes automatically before or after certain events like committing, pushing, or receiving commits. They allow you to automate tasks and enforce policies within your repository. Hooks are located in the .git/hooks directory of your repository.
There are client-side hooks (affecting your local repo) and server-side hooks (affecting the remote repo).
Example Use Case (Client-side - pre-commit hook):
A pre-commit hook can run linters or formatters on your code before a commit is created. If the linter finds errors, the commit can be aborted, ensuring only clean code enters the repository.
#!/bin/sh
if git rev-parse --verify HEAD >/dev/null 2>&1
then
echo "Checking for whitespace errors..."
exec git diff --check --cached
fi
Quick revision table
| Topic | One-line answer | Common follow-up |
|---|---|---|
| Git vs SVN | Git is DVCS (local repo, faster, resilient); SVN is CVCS (central server, slower, single point of failure). | What are the benefits of a DVCS model in a large team? |
| File States | Working Dir (changes) -> Staging Area (prepared) -> Repository (committed). | How do you unstage a file? (git reset HEAD <file>) |
HEAD | Pointer to the tip of the current branch (or a specific commit if detached). | What happens if HEAD is detached? |
| Branching | Lightweight movable pointer to a commit, isolates development efforts. | When should you delete a branch? |
git merge | Integrates branch changes, creates new merge commit (or fast-forwards). | Explain a 3-way merge. |
git rebase | Integrates branch changes by rewriting history linearly, avoids merge commits. | Why is rebasing on a shared branch risky? |
| Merge Conflicts | Occur when Git can't auto-reconcile diverging changes; resolved manually. | What tools help with conflict resolution? |
git stash | Temporarily saves uncommitted changes, cleans working directory. | How do you apply a specific stash from the list? (git stash apply stash@{N}) |
git cherry-pick | Applies a single commit from one branch to another, creating a new commit. | What are the downsides of cherry-picking? (Duplicated history, new SHA) |
git reset | Moves HEAD (and optionally index/working dir) to a previous state, rewrites history. | When would you use --soft over --mixed? |
git reflog | Records all changes to HEAD and other references, invaluable for recovery. | How exactly would you recover a lost commit using reflog? |
git revert | Creates a new commit that undoes the changes of a previous commit, preserves history. | Why choose revert over reset for public changes? |
git fetch | Downloads remote changes to local repo, updates remote-tracking branches, doesn't merge. | What's the difference between git fetch and git pull? |
git pull | Fetches remote changes and automatically merges them into current branch. | What if git pull causes conflicts? |
git push | Uploads local commits to the remote repository. | What is git push --force and when is it acceptable? |
| Pull Request (PR) | Mechanism for code review and collaboration before merging changes into a target branch. | What makes a good PR description? |
| Forking | Creating a personal copy of a remote repository under your own account for contribution. | How do you contribute back to the original repo after forking? |
| Sensitive Data | Use git filter-repo or rebase -i to rewrite history, then force push. Revoke credentials. | What's the risk of not removing sensitive data from history? |
| CI/CD Integration | Git events (push, PR) trigger pipelines that clone, build, test, and deploy code. | How do Git tags play a role in CI/CD? |
| Git Hooks | Scripts executed automatically by Git before or after specific events, for automation. | Can you write a pre-commit hook to check for large files? |
How to prepare
- Practice with the CLI: Hands-on experience is paramount. Set up a local repository, experiment with all core commands (
add,commit,branch,merge,rebase,reset,stash,cherry-pick). Create conflicts and resolve them manually. - Understand the Mental Model: Beyond memorizing commands, grasp how Git stores data, how
HEADworks, and the implications of history rewriting. Visualize the commit graph. - Study Branching Strategies: Be able to articulate Git Flow, GitHub Flow, and their pros/cons. Understand when to use each and why.
- Learn GitHub/GitLab Workflows: Practice opening Pull Requests, reviewing code, understanding forks, and keeping them synchronized. These are crucial for collaboration.
- Prepare Scenario-Based Answers: Think about common issues (undoing mistakes, fixing sensitive data, large repositories) and how you'd approach them using specific Git commands. Explain the why behind your choices.
