DEV Community

Full Stack from Full-Stack
Full Stack from Full-Stack

Posted on

Top 25 Code Repository interview questions

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.

#!/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
Enter fullscreen mode Exit fullscreen mode

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!

Top comments (0)