Git Interview Questions and Answers
1. Discuss different branching strategies (e.g., GitFlow, Trunk-Based Development) and their pros and cons.
Git branching strategies are vital for organizing and managing code in a collaborative environment. The choice of a branching strategy depends on the team size, project complexity, and release frequency. Let's explore two of the most popular strategies: GitFlow and Trunk-Based Development.
GitFlow (Enhanced Explanation)
GitFlow is a branching model that utilizes a set of long-lived branches and several supporting branches for features, releases, and hotfixes. It is a very structured and explicit model, making it a good choice for projects with scheduled releases and a need to support multiple versions in production.
Core Branches:
main(ormaster): This branch represents the production-ready code. It is tagged for every release.develop: This is the main development branch where all feature branches are merged. It contains the most up-to-date development code.
Supporting Branches:
feature/*: Created fromdevelopfor new features. Merged back intodevelop.release/*: Created fromdevelopwhen ready for a release. Used for final testing and bug fixes. Merged into bothmainanddevelop.hotfix/*: Created frommainto address urgent production issues. Merged into bothmainanddevelop.
GitFlow Diagram:
main ------------------o-------------------------o-----------> (v1.0)
^ /|\ /|\
| / | \ / | \
(hotfix) / | \ (release) / | \ (v1.1)
| / | \ / | \
develop ---o---o---o----o----o---o---o---o-----o----o----o--->
|\ /|\ /| |\ /|\ /| |\ /|
|/ \|/ \| |/ \|/ \| |/ \|
(feature) (feature) (feature) (feature)
Typical Command Workflow:
- Start a new feature:
bash git checkout develop git pull git checkout -b feature/my-new-feature - Finish a feature:
bash git checkout develop git merge --no-ff feature/my-new-feature git branch -d feature/my-new-feature - Start a release:
bash git checkout -b release/1.1.0 develop -
Finish a release: ```bash git checkout main git merge --no-ff release/1.1.0 git tag -a 1.1.0
git checkout develop git merge --no-ff release/1.1.0 git branch -d release/1.1.0 ```
Pros of GitFlow:
- Structured and Predictable: Provides a clear, explicit structure for managing large and complex projects.
- Parallel Development: Allows multiple developers to work on features in parallel without affecting the main codebase.
- Supports Multiple Versions: Well-suited for projects that need to support multiple versions in production.
Cons of GitFlow:
- Complexity: The number of branches can be overwhelming and can lead to a complex workflow.
- Slower Release Cycles: The process of creating and merging release branches can slow down the release frequency.
- Not Ideal for CI/CD: The complexity of GitFlow can make it difficult to implement a fully automated CI/CD pipeline where every commit to
developshould ideally be releasable.
Trunk-Based Development
Trunk-Based Development (TBD) is a simpler branching model where all developers commit to a single branch, the trunk (often main or master). If developers use branches, they are very short-lived and are merged into the trunk as quickly as possible. This strategy is favored by teams practicing Continuous Integration and Continuous Delivery (CI/CD).
Core Concepts:
- Single
trunkbranch: All development happens on a singletrunkbranch. - Short-lived feature branches: If branches are used, they are created from the
trunk, and the work is merged back into thetrunkwithin a few hours or a day. - Feature Flags: Incomplete features are often hidden behind feature flags to avoid impacting the production code.
- Continuous Integration: The
trunkis always kept in a releasable state, and a robust suite of automated tests is essential.
Pros of Trunk-Based Development:
- Simplicity: Easy to understand and implement.
- CI/CD Friendly: Perfectly suited for teams practicing CI/CD, as the
trunkis always releasable. - Fewer Merge Conflicts: Short-lived branches and frequent commits reduce the likelihood of merge conflicts.
Cons of Trunk-Based Development:
- Requires Discipline: Developers must be disciplined about writing small, well-tested commits.
- Heavy Reliance on Automated Tests: A comprehensive and reliable suite of automated tests is crucial to maintain the stability of the
trunk. - Feature Flags Overhead: Managing feature flags can add complexity to the codebase.
Core Git Commands & Concepts
2. What is the difference between git merge and git rebase?
Answer:
Both git merge and git rebase are used to integrate changes from one branch into another, but they do so in fundamentally different ways, resulting in a different commit history.
git merge
- What it does:
git mergecreates a new "merge commit" in the target branch that ties together the histories of both branches. It preserves the original commit history of both branches as-is. - History: This results in a graph-like history, showing exactly when and where branches were merged.
-
Use Case: It's a non-destructive operation and is preferred when you want to maintain the exact historical record of your branches. It's the default for merging feature branches into a shared branch like
developormain. -
Diagram: ``` Before Merge: A---B---C (main) \ D---E---F (feature)
After
git checkout main; git merge feature: A---B---C-------G (main) \ / D---E---F (feature)`` (CommitG` is a new merge commit.)
git rebase
- What it does:
git rebasere-writes the commit history. It takes all the commits from one branch and "replays" them, one by one, on top of another branch. This creates a clean, linear history. - History: The project history looks as if all the commits were made in a straight line, with no merge commits.
-
Use Case: It's often used by individual developers to clean up their local feature branch history before merging it into a shared branch. You should never rebase a public, shared branch (like
mainordevelop) because it rewrites history that other developers may have already pulled. -
Diagram: ``` Before Rebase: A---B---C (main) \ D---E---F (feature)
After
git checkout feature; git rebase main: A---B---C (main) \ D'--E'--F' (feature)`` (CommitsD,E,Fhave been re-written asD',E',F'and now appear on top ofC`.)
Summary:
| Feature | git merge |
git rebase |
|---|---|---|
| History | Preserves history, creates a merge commit. | Re-writes history, creates a linear history. |
| Collaboration | Safe for public/shared branches. | Dangerous for public/shared branches. |
| Readability | Can become cluttered with merge commits. | Clean, linear history is easy to follow. |
| Use Case | Merging a feature branch into develop. |
Cleaning up a local feature branch before merging. |
3. What is git cherry-pick and when would you use it?
Answer:
git cherry-pick is a command that allows you to pick a specific commit from one branch and apply it onto another branch. Unlike a merge or rebase, which apply many commits, cherry-picking allows you to select and apply just one.
Use Case: Backporting a Hotfix
This is the most common use case. Imagine you have a bug in your main branch that you need to fix immediately. You also need this fix in your develop branch for future releases.
- You create a
hotfixbranch frommain. - You make a commit (
commit H) on thehotfixbranch to fix the bug. - You merge the
hotfixbranch back intomain.
Now, how do you get that specific fix into develop without merging all of main? You cherry-pick it.
Example:
-
Diagram: ``` Before cherry-pick: A-----------B (main) \ H (hotfix commit)
D---E---F (develop) ```
-
Commands: ```bash # Get the commit hash of the hotfix git log main -1 # (Let's say the hash is H12345)
Switch to the develop branch
git checkout develop
Cherry-pick the hotfix commit onto develop
git cherry-pick H12345 ```
-
Diagram After cherry-pick: ``` A-----------B---H (main)
D---E---F-------H' (develop)
`` (CommitH'is a copy of commitH, now applied to thedevelop` branch.)
Other Use Cases: * Applying a specific feature commit from a failed feature branch. * Copying a commit that was accidentally made on the wrong branch.
4. What is a .gitignore file and how does it work?
Answer:
A .gitignore file is a text file that tells Git which files or directories to intentionally ignore and not track. These are typically files that are generated during the build process, log files, or files containing sensitive information that should not be committed to the repository.
How it works:
- The file contains a list of patterns.
- Git checks these patterns against the names of files and directories in your project.
- If a file or directory matches a pattern, Git will not track it. This means it won't show up in
git statusand won't be added to commits withgit add ..
Example .gitignore for a Node.js project:
# Ignore dependencies
/node_modules
# Ignore build output
/dist
/build
# Ignore log files
*.log
npm-debug.log*
# Ignore environment variables file
.env
.env.local
# Ignore OS-generated files
.DS_Store
Thumbs.db
Key Patterns:
#: Lines starting with#are comments./node_modules: A leading slash/matches files only in the root directory.*.log: The asterisk*is a wildcard that matches zero or more characters. This ignores any file ending with.log.!important.log: A leading exclamation mark!negates a pattern. This would force Git to trackimportant.logeven if*.logis ignored./build/: A trailing slash/specifies a directory. Git will ignore the entire directory.
It's a best practice to create a .gitignore file at the beginning of a project to prevent unnecessary files from ever being committed to the repository.
Troubleshooting
5. How do you resolve a merge conflict in Git?
Answer:
- Identify the conflict: When you try to merge a branch that has conflicting changes, Git will stop the merge process and will tell you which files have conflicts.
- Open the conflicting files: Open the conflicting files in a text editor. You will see markers in the files that show you where the conflicts are:
<<<<<<< HEAD // Code from your current branch (e.g., main) ======= // Code from the branch you are merging (e.g., feature) >>>>>>> feature - Resolve the conflicts: Edit the files to resolve the conflicts. You must manually delete the conflict markers (
<<<<<<<,=======,>>>>>>>) and decide which code to keep. - Add the resolved files: Once you have resolved the conflicts, you need to add the resolved files to the staging area using the
git addcommand.bash git add <conflicting-file-name> - Commit the merge: Once you have added all of the resolved files, you can commit the merge using
git commit. Git will provide a default commit message that you can edit.bash git commit
6. What is a detached HEAD, and how do you fix it?
Answer:
A detached HEAD is a state where you are not on any branch. This can happen if you check out a specific commit hash, a tag, or a remote branch directly. In this state, any new commits you make will not belong to any branch, and you risk losing them once you switch away.
To fix a detached HEAD, you have two main options:
- Create a new branch from the current commit: This is the best option if you have made new commits that you want to keep.
bash git checkout -b new-branch-name - Switch to an existing branch: If you did not make any new commits and just want to return to a known branch, you can simply check it out.
bash git checkout main
7. How do you recover a lost commit in Git?
Answer:
The git reflog command is your safety net. It shows a log of all the recent movements of HEAD, including commits, resets, and checkouts.
- Find the lost commit: Run
git reflogto see a list of all recent actions. Find the hash of the commit you want to recover.bash git reflog # Output might look like: # 8a1b2c3 HEAD@{0}: reset: moving to HEAD~1 # d4e5f6a HEAD@{1}: commit: my lost commit message # ... - Restore the commit: Once you have the commit hash (e.g.,
d4e5f6a), you can restore it in a few ways:- Create a new branch: (Safest)
bash git checkout -b recovered-branch d4e5f6a - Reset your current branch to it: (Can be destructive)
bash git reset --hard d4e5f6a - Cherry-pick it:
bash git cherry-pick d4e5f6a
- Create a new branch: (Safest)
8. How do you undo a commit in Git?
Answer:
There are two main ways to undo a commit, depending on whether the commit has been pushed to a shared repository.
-
git revert(Safe for pushed commits):- This command creates a new commit that undoes the changes from a previous commit. It does not rewrite the commit history, making it safe for commits that have already been shared with others.
bash git revert <commit-hash>
- This command creates a new commit that undoes the changes from a previous commit. It does not rewrite the commit history, making it safe for commits that have already been shared with others.
-
git reset(For local commits only):- This command moves the current branch pointer to a different commit, effectively removing the commits that came after it. This rewrites history and should not be used on commits that have been pushed to a shared repository.
--soft: Moves the pointer but keeps the changes from the undone commits in your staging area.bash git reset --soft <commit-hash-before-the-one-to-undo>--hard: (Destructive) Moves the pointer and discards all changes from the undone commits.bash git reset --hard <commit-hash-before-the-one-to-undo>