Top 25 Code Repository interview questions
Tags: java, git, vcs, interview
Published: 2023-10-16 10:23:23

## 1. **What is the main purpose of a version control system (VCS)?**

A VCS tracks and manages changes in code, allowing for collaborative work, version history maintenance, and easy codebase restoration.

VCSs, like Git, SVN, and Mercurial, offer a range of benefits:

- **Collaboration**: Multiple developers can work on the same project without stepping on each other's toes. In a Java project, Alice might be working on the `UserService` class, while Bob develops the `OrderService` class — both can work simultaneously without conflicts.
- **History**: Every change is tracked, so you can easily see who made a specific change, when, and why.
- **Backup & Restoration**: If something breaks, it's straightforward to revert to a previous working state.

## 2. **Differentiate between `git clone` and `git fork`.**

`git clone` is a command to copy an existing repository to your local machine. In contrast, `git fork` creates a copy of a repository under your own account on platforms like GitHub, allowing for independent development without affecting the original project.

## 3. **What does the `git commit` command do?**

`git commit` saves the staged changes in the repository with a descriptive message, creating a snapshot of the project's current state.

After making changes in your Java code, you'd first `git add` those changes to stage them. Following this, `git commit -m "Descriptive message"` is used to save these staged changes. Each commit has a unique ID, allowing you to reference or revert to specific project states.

## 4. **Describe the difference between `merge` and `rebase`.**

Both `merge` and `rebase` are Git operations to integrate changes from one branch into another, but they do it in different ways. `Merge` combines branch histories, while `rebase` applies changes from one branch onto another, linearizing the history.

Imagine you have two branches, `master` and `feature`. Both have evolved separately with different commits.

- Using `git merge feature` on `master` creates a new commit in the `master` branch, preserving the history of both branches, but this can sometimes result in a non-linear, harder-to-follow history.
- Using `git rebase master` on `feature` takes all changes in `feature`, and "reapplies" them on top of `master`. This results in a linear commit history, but it also rewrites the commit history of `feature`.

## 5. **Why is it advisable to keep a consistent commit message convention in a team?**

Consistent commit messages improve clarity, making it easier for the team to understand code history, track changes, and collaborate.

When working on a Java project with multiple backend engineers, having standardized commit messages ensures that:

- Changes can be quickly reviewed.
- Issues or features related to a commit can be easily identified.
- Automated tools (like changelog generators) can use the messages.

For instance, using a format like: `[JIRA-1234] Add authentication logic` can immediately link a commit to a specific task in a JIRA board.

## 6. **What are the benefits and drawbacks of a monorepo versus multiple repositories?**

Monorepos centralize code in a single repository, simplifying dependency management and code sharing. However, they can be harder to manage as the project grows. Multiple repositories offer modularity and isolated development but can complicate dependency tracking and coordination.

In a large enterprise with a Java backend system:

- **Monorepo Benefits**: Shared tooling, easy refactoring across modules, and centralized versioning.
- **Monorepo Drawbacks**: Larger repository size, potential for merge conflicts, and slower tooling on massive codebases.

- **Multi-repo Benefits**: Independent versioning, focused access rights per repository, and clearer ownership.
- **Multi-repo Drawbacks**: Dependency management can be harder, less code sharing, and coordinating changes across repositories can be challenging.

## 7. **How would you handle a conflict during a `git merge` operation?**

Handle conflicts by manually editing the conflicting files to resolve discrepancies, then stage and commit the resolved files.

When merging a `feature` branch into `master`, Git might indicate a conflict in `UserService.java`. Open the file and look for conflict markers (`<<<<<<<`, `=======`, `>>>>>>>`). The code between `<<<<<<<` and `=======` is your `master` branch's version, while the code between `=======` and `>>>>>>>` is from your `feature` branch. Edit the file to retain the desired code, remove the conflict markers, save the file, then stage and commit it to complete the merge.

## 8. **What is a `detached HEAD` state in Git, and how can you resolve it?**

A `detached HEAD` state in Git means you're no longer on a branch, but instead are directly referencing a specific commit. To resolve it, you usually create a new branch or switch to an existing branch.

When you checkout a specific commit in Git using its hash (e.g., `git checkout 4f8dce2`), you enter a `detached HEAD` state. In this state, you're not on a branch, so if you make commits, they won't belong to any branch and can be difficult to reference later.

To resolve:

1. If you've made changes and want to keep them, you can create a new branch: `git branch new-branch-name` and then `git checkout new-branch-name`.
2. If you haven't made changes or don't wish to keep them, you can switch back to an existing branch: `git checkout master` or another branch name.

## 9. **Explain the difference between a `fast-forward merge` and a `3-way merge` in Git.**

A `fast-forward merge` simply moves the branch pointer forward if the current branch is a direct ancestor of the branch being merged. A `3-way merge` creates a new commit, combining changes from two different branch points.

Suppose you have a `master` branch and a `feature` branch that you branched off from `master`.

- If no new commits are made to `master` while you work on `feature`, merging `feature` into `master` results in a `fast-forward merge`. The `master` pointer just moves forward to point to the last commit of `feature`.

- If both `master` and `feature` have new commits, Git performs a `3-way merge`, taking the two branch tips and their common ancestor to resolve changes and create a new merge commit.

## 10. **Describe the purpose and use of `git cherry-pick`.**

`git cherry-pick` allows you to apply changes from specific commits onto your current branch, effectively "picking" individual commits.

Suppose you're working on a `master` branch and realize that a commit (e.g., `abc123`) from a `feature` branch introduces a crucial fix you need. Instead of merging the entire `feature` branch, you can use `git cherry-pick abc123` to bring just that commit onto `master`. This is particularly useful for applying isolated fixes without bringing in other changes.

## 11. **How can you squash multiple commits into one?**

You can squash multiple commits into one using the `git rebase -i` command, followed by the `squash` or `fixup` options.

Suppose you have a series of commits you want to squash into a single commit for a cleaner history. Using `git rebase -i HEAD~n` (where `n` is the number of commits you want to review), an interactive editor opens up. In this editor, you can replace `pick` with `squash` or `s` (to combine commit messages) or `fixup` or `f` (to discard the commit message) for the commits you want to squash. Save and exit, and the commits will be combined.

## 12. **Explain the `HEAD`, `index`, and `working tree` in Git.**

In Git, `HEAD` is a reference to the current commit, the `index` (or staging area) holds the changes queued for the next commit, and the `working tree` represents the current state of files on your filesystem.

In a typical Git workflow:

1. You make changes in your `working tree` - this is where you're actively editing files.
2. You stage changes you want to commit using `git add`, which adds these changes to the `index`.
3. When you `git commit`, the changes from the `index` are saved and `HEAD` now points to this new commit.

## 13. **What is a `git stash`? When would you use it?**

`git stash` temporarily saves changes that are not yet committed, allowing you to switch branches without committing. It's useful when you're in the middle of work but need to move to another task temporarily.

Imagine you're working on a `feature` branch and have made some changes. Suddenly, you need to fix a critical bug in the `master` branch. Instead of committing your incomplete work or discarding it, you can use `git stash` to set it aside. After fixing the bug on `master`, you can return to the `feature` branch and use `git stash pop` to retrieve and reapply your stashed changes.

## 14. **How do you revert a commit that has already been pushed and made public?**

Use `git revert` to create a new commit that undoes the changes from the previous commit, then push it. This ensures that you don't rewrite public history.

Suppose you pushed a commit that introduced a bug. Instead of using `git reset` (which would rewrite commit history), you can use `git revert <commit-hash>`. This command creates a new commit that undoes the changes made in the specified commit. After creating the revert commit, you can push it to the repository, effectively undoing the buggy commit in a public-friendly manner.

## 15. **Describe the Git workflow you've used in previous projects or the one you find most effective.**

A commonly effective workflow is the "Feature Branch Workflow", where each new feature or bugfix is developed in its own branch, and then merged into the main branch once completed.

In a Java backend project, for each new feature or bugfix:

1. A new branch is created off the main branch, typically named based on the feature or issue (e.g., `feature/user-authentication` or `fix/database-connection-bug` or `your-name/feature-user-authentication`).
2. Changes are committed to this branch.
3. Once development is complete, the branch undergoes code review and testing.
4. If changes are approved, the feature branch is merged into the main branch, and the feature branch is deleted.

This workflow keeps the main branch stable, encourages collaboration via pull requests, and organizes changes by feature or issue.

## 16. **How do you handle `git submodules`?**

Git submodules allow you to include or embed one Git repository inside another. They are useful for managing projects with external dependencies.

For a Java project that relies on a specific version of an external library hosted on Git:

1. Add the submodule: `git submodule add <repository-url> <path/to/submodule>`.
2. The submodule will be checked out at its latest commit, but you can navigate to the submodule directory and checkout a specific commit or branch if needed.
3. When cloning a repository containing submodules, use `git clone --recursive` to ensure submodules are also fetched.
4. To update the submodule to the latest commit, navigate to the submodule directory and pull the latest changes.

## 17. **What are the advantages of using a rebase workflow?**

Rebase workflow linearizes commit history, avoids merge commits, and makes it easier to understand the chronological order of changes.

In a team working on a Java backend:

1. Developers frequently sync their feature branches with the main branch using `git rebase`. This applies their feature changes on top of the latest changes in the main branch.
2. This avoids merge commits, as changes appear to have been made sequentially, even if they were done in parallel.
3. This workflow can make bisecting (finding a commit that introduced an issue) easier and the commit history cleaner.

However, it requires care, as rebasing rewrites commit history, which can be problematic if not coordinated properly in a team setting.

## 18. **What are hooks in Git and how can they be useful?**

Hooks in Git are scripts that run automatically before or after events in Git, such as commits or pushes. They're useful for enforcing project rules, running tests, or automating tasks.

For instance, in a Java project:

1. You can use a `pre-commit` hook to automatically run unit tests before allowing a commit, ensuring no commit breaks the existing functionality.
2. A `pre-push` hook can check that commit messages adhere to a specific format or ensure code is formatted properly before pushing to a remote.

To use a hook, you'd place a script in the `.git/hooks` directory of your repository and make it executable. For example, for a `pre-commit` hook, you'd add a script named `pre-commit` in the `hooks` directory.

```bash
#!/bin/sh

# Navigate to the project root.
cd $(git rev-parse --show-toplevel)

# Format the code using Maven
mvn formatter:format

# Stage the formatted code
git add -u

# Run tests using Maven
mvn test

# Check if tests passed
if [ $? -ne 0 ]; then
    echo "\nTests failed! Fix them before committing.\n"
    exit 1
fi

exit 0
```

## 19. **How do you find a bug introduced by a certain commit using Git?**

You can use `git bisect` to perform a binary search through your commit history to pinpoint the commit that introduced a bug.

Imagine after several commits to your Java backend, a bug surfaces:

1. You know a commit where things were working (`good` state) and a commit where the bug is present (`bad` state).
2. Start the bisect process with `git bisect start`.
3. Mark the known states with `git bisect bad` (for the buggy commit) and `git bisect good` (for the last known good commit).
4. Git will then checkout a commit halfway between the `good` and `bad` commits. You test the code.
5. If the mid-point commit is good, use `git bisect good`; if it's bad, use `git bisect bad`.
6. Git narrows down the range and continues the process until it identifies the commit that introduced the bug.

## 20. **What is the difference between `HEAD^` and `HEAD~` in Git?**

Both `HEAD^` and `HEAD~` reference previous commits, but `HEAD^` refers to the parent of the current commit (useful for merge commits with multiple parents), while `HEAD~` refers to the first parent of the current commit, or its ancestor based on a specified number.

In a Git commit tree:

- `HEAD^` or `HEAD^1` would refer to the immediate parent of the current commit. If the commit was a merge commit from a branch, `HEAD^2` would refer to the second parent from the merge.
- `HEAD~` is equivalent to `HEAD~1` and refers to the first parent of the current commit. `HEAD~2` would go back two commits in the mainline history.

For instance, in a scenario with a merge commit, `HEAD^2` might reference the tip of the branch that was merged in, while `HEAD~2` would reference two commits back on the mainline.

## 21. **Why would you use `git fetch` instead of `git pull`?**

`git fetch` retrieves updates from a remote repository without automatically merging changes into your current branch, whereas `git pull` fetches and then merges. You'd use `git fetch` to review changes before merging.

When collaborating on a Java project:

1. Using `git fetch` lets you see updates from other team members without immediately integrating them into your local branch. This allows you to review changes and potentially rebase or merge at a time of your choosing.
2. `git pull`, on the other hand, is a combination of `git fetch` followed by `git merge`. This might lead to unexpected merge conflicts if you're not prepared to integrate changes immediately.

Thus, `git fetch` provides more flexibility and control over when and how to integrate updates.

## 22. **How would you resolve a merge conflict in Git?**

To resolve a merge conflict, you manually edit the conflicted files to decide which changes to keep, mark the file as resolved with `git add`, and then complete the merge with `git commit`.

While working on a Java backend, you attempt to merge a branch and encounter a merge conflict:

1. Git will indicate which files have conflicts.
2. Open each conflicted file and look for the conflict markers (`<<<<<<<`, `=======`, and `>>>>>>>`). The code between `<<<<<<<` and `=======` is your local change, and the code between `=======` and `>>>>>>>` is the incoming change.
3. Decide which version of the code to keep, or possibly integrate both. Edit the file to reflect the final desired state and remove the conflict markers.
4. After resolving all conflicts in a file, use `git add <filename>` to mark it as resolved.
5. Once all conflicts are resolved, finalize the merge with `git commit`.

## 23. **Explain the difference between a `fast-forward` merge and a `3-way` merge in Git.**

A `fast-forward` merge simply moves the current branch pointer to the tip of the branch being merged, given that there were no new commits on the current branch. A `3-way` merge creates a new commit, representing the merging of two branches that have diverged.

Imagine you're working on a Java project:

1. If you create a feature branch off `main` and make some commits, but `main` remains unchanged, merging the feature branch back into `main` with a `fast-forward` merge will simply move the `main` branch pointer to the last commit of the feature branch.
2. However, if there were new commits on `main` while you were working on the feature branch, Git will perform a `3-way` merge using the common base commit of the two branches and the latest commits of each branch. This results in a new merge commit on `main`.

## 24. **How would you squash multiple commits into one?**

You can squash multiple commits into one using `git rebase -i` (interactive rebase), followed by force-pushing if the commits were previously pushed to a remote repository.

While working on a Java feature:

1. Run `git rebase -i HEAD~n`, where `n` is the number of commits you want to interact with.
2. An editor will open, showing a list of commits. Each line starts with the word `pick`.
3. To squash commits, replace the word `pick` with `squash` or `s` for the commits you want to squash into the previous commit.
4. Save and exit the editor. If you're squashing, another editor will open for combined commit messages. Edit the message, save, and close.
5. If these commits were previously pushed, you'll need to force push using `git push origin <branch-name> --force`.

## 25. **What are some strategies to keep a Git repository clean?**

Strategies include: following a consistent commit message convention, using branches for features/bugfixes, regularly pruning old branches, squashing related commits, and avoiding committing generated files or binaries.

For maintaining a clean Java project repository:

1. **Commit Conventions**: Follow a consistent commit message pattern. This could be based on conventions like Conventional Commits.
2. **Branching**: Use branches for features, bugfixes, or experiments. Once merged, delete these branches.
3. **Squash Commits**: Squash related changes into a single commit before merging to maintain a linear history.
4. **Avoid Generated Files**: Use a `.gitignore` file to prevent committing generated files, logs, or binaries.
5. **Prune Old Branches**: Regularly use `git branch -d` to delete old branches or `git remote prune origin` to clean up stale references to remote branches.

## Conclusion

For most of these GIT commands you can use UI tools/plugin for VCS into your IDE.

Version control, particularly with tools like Git, is an indispensable skill for every backend engineer, especially in the collaborative environments of today's software development world. These questions, covering the breadth and depth of Git usage and best practices, are indicative of what you might encounter in job interviews.

🗨️💡 **Join the Conversation! Share Your Thoughts Below.**

🗣️ Your opinion matters! We're eager to hear your take on this topic. What are your thoughts, experiences, or questions related to what you've just read? Don't hold back—let's dive into a lively discussion!

**Enjoyed this Article?**

💖 React: Click the heart icon to show your appreciation and help others discover this content too!

🔔 Follow: Stay updated with the latest insights, tips, and trends by subscribing to our profile. Don't miss out on future articles!

🚀 Share: Spread the knowledge! Share this article with your network and help us reach a wider audience.

Your engagement is what keeps our community thriving. Thank you for being a part of it!