Version Control Basics
1. What is version control and why is it important?
Version control is a system that records changes to files over time so that you can recall specific versions later, compare states, and understand how a project evolved. It is important because it enables collaboration among multiple developers, provides a complete history of changes, and allows teams to revert to earlier working states when something breaks. Without it, coordinating work and recovering from mistakes would be slow and error-prone.
2. What is Git and how does it differ from other version control systems?
Git is a free, open-source distributed version control system created by Linus Torvalds in 2005 to manage the Linux kernel. Unlike centralized systems such as Subversion (SVN) or CVS, Git gives every developer a complete copy of the repository, including its full history, on their local machine. This distributed model allows you to commit, branch, and inspect history while offline, and it makes operations like branching and merging extremely fast.
3. What is the difference between centralized and distributed version control?
In a centralized version control system, there is a single central server that holds the canonical repository, and developers check out files from it; if that server goes down, no one can commit or access history. In a distributed system like Git, every clone is a full backup of the repository with complete history, so work can continue locally even without network access. Distributed systems also enable more flexible, branch-heavy workflows because most operations happen locally and only synchronize with remotes when needed.
4. What is a repository in Git?
A repository, or "repo," is the data structure Git uses to store a project's complete set of files along with the entire history of changes made to them. It contains a hidden .git directory at its root that holds all the commits, branches, tags, and configuration. A repository can be local (on your machine) or remote (hosted on a service like GitHub, GitLab, or Bitbucket).
5. How do you create a new Git repository?
You can initialize a brand-new repository in an existing directory by running git init, which creates the .git subdirectory that tracks the project. Alternatively, you can copy an existing remote repository to your machine with git clone <url>, which downloads the project and its full history and automatically sets up a remote named origin. Both approaches give you a fully functional local repository ready for commits.
6. What does the command git clone do?
The git clone command creates a local copy of an existing remote repository, including all of its files, branches, and complete commit history. It automatically configures a remote reference called origin that points back to the source URL, so you can later fetch and push changes. This is typically the first command a developer runs when joining a project that already exists.
7. What are the three main states a file can be in within Git?
Git tracks files through three main states: modified, staged, and committed. A file is "modified" when you have changed it but not yet marked it for the next commit, "staged" when you have marked a modified file to go into your next commit, and "committed" when the data is safely stored in your local repository database. These states correspond to the working directory, the staging area (index), and the .git directory respectively.
8. What is the difference between the working directory, staging area, and repository?
The working directory is the set of files you currently see and edit on disk, representing a single checked-out version of the project. The staging area, also called the index, is an intermediate space where you assemble the exact set of changes that will go into your next commit using git add. The repository is the .git directory where committed snapshots are permanently stored, forming the project history.
9. What is a commit in Git?
A commit is a snapshot of your repository at a specific point in time, capturing the state of all tracked files when the commit was created. Each commit has a unique identifier called a SHA-1 hash, an author, a timestamp, a commit message, and a pointer to its parent commit(s), which together form the project history. Commits are immutable, meaning their content and identity never change once created.
10. What is the purpose of the .gitignore file?
The .gitignore file tells Git which files or directories it should intentionally not track, so they are never staged or committed. It is commonly used to exclude build artifacts, dependency folders like node_modules, log files, secrets, and editor-specific configuration. Each line uses a glob pattern, and ignoring these files keeps the repository clean and prevents accidental commits of unwanted or sensitive data.
Commits & Staging
11. How do you stage changes for a commit?
You stage changes using the git add command, which moves modifications from the working directory into the staging area in preparation for the next commit. You can stage a specific file with git add file.txt, stage everything with git add ., or interactively choose portions of files with git add -p. Staging gives you fine-grained control over exactly what goes into each commit.
12. What does git commit do and how do you write a good commit message?
The git commit command records the staged changes as a new snapshot in the repository's history. A good commit message has a concise summary line of about 50 characters written in the imperative mood (for example, "Fix login redirect bug"), optionally followed by a blank line and a more detailed body explaining what changed and why. Clear messages make the history easier to read, search, and understand later.
13. What is the difference between git add and git commit?
git add moves changes into the staging area, telling Git which modifications you want included in your next snapshot, while git commit actually records those staged changes permanently into the repository history. In other words, add prepares a changeset and commit finalizes it. This two-step process lets you carefully craft each commit before it becomes part of the permanent record.
14. How can you view the commit history?
You view the commit history with the git log command, which lists commits from newest to oldest along with their hashes, authors, dates, and messages. Useful options include git log --oneline for a compact single-line view, git log --graph to visualize branch structure, and git log -p to show the actual diffs introduced by each commit. You can also filter by author, date, or file path to narrow the results.
15. What does git status show you?
The git status command shows the current state of your working directory and staging area, including which files are staged, which are modified but unstaged, and which are untracked. It also tells you the branch you are on and whether it is ahead of or behind its remote counterpart. It is one of the most frequently used commands because it gives a clear snapshot of what will happen on your next commit.
16. What is the difference between git diff and git diff --staged?
git diff with no arguments shows the changes in your working directory that have not yet been staged, comparing the working files against the staging area. git diff --staged (also written git diff --cached) shows the changes that have been staged and will be included in your next commit, comparing the staging area against the last commit. Together they let you review unstaged and staged changes separately.
17. How do you undo changes in the working directory before staging?
To discard uncommitted changes in a tracked file and restore it to the last committed state, you can use git restore <file> (or the older git checkout -- <file>). To discard all such changes at once, you can use git restore ., though this is destructive because the discarded work cannot be recovered. For untracked files, you would use git clean instead.
18. What is the difference between git reset --soft, --mixed, and --hard?
git reset --soft moves the branch pointer to a target commit but leaves both the staging area and working directory untouched, so your changes remain staged. git reset --mixed (the default) moves the pointer and resets the staging area but keeps your working directory changes, leaving them unstaged. git reset --hard moves the pointer and discards all changes in both the staging area and working directory, which is destructive and should be used with caution.
19. What does git commit --amend do?
The git commit --amend command replaces the most recent commit with a new one, allowing you to modify its message or add forgotten staged changes. It is useful for fixing small mistakes immediately after committing, such as a typo in the message or a missing file. Because it rewrites history by creating a new commit hash, you should avoid amending commits that have already been pushed and shared with others.
20. How do you remove a file from Git tracking without deleting it from disk?
You remove a file from Git's tracking while keeping it on disk by running git rm --cached <file>, which deletes it from the index but leaves the working-directory copy intact. This is commonly done when a file was committed by mistake and should now be ignored, often combined with adding it to .gitignore. After running the command, you commit the change so the file is no longer part of future snapshots.
Branching & Merging
21. What is a branch in Git?
A branch in Git is a lightweight, movable pointer to a specific commit, representing an independent line of development. Branches let you work on features, fixes, or experiments in isolation without affecting the main codebase until you are ready to integrate. Because a branch is essentially just a reference to a commit, creating and switching between branches in Git is extremely fast and inexpensive.
22. How do you create and switch to a new branch?
You can create a new branch with git branch <name> and then switch to it with git checkout <name>, or do both in one step with git checkout -b <name>. Modern Git also offers git switch -c <name> as a clearer, dedicated command for creating and switching. The new branch starts pointing at the same commit you were on when you created it.
23. What is the difference between git merge and git rebase?
Both git merge and git rebase integrate changes from one branch into another, but they do so differently. Merge creates a new "merge commit" that ties two histories together, preserving the exact branching structure, while rebase replays your commits on top of another branch's tip, producing a linear history as if you had started your work from that point. Merge is non-destructive and safer for shared branches, whereas rebase yields a cleaner history but rewrites commit hashes.
24. What is a fast-forward merge?
A fast-forward merge happens when the target branch has not diverged from the branch being merged, meaning there are no new commits on the target that the source branch does not already contain. In this case, Git simply moves the branch pointer forward to the latest commit without creating a separate merge commit. You can force a merge commit even in this situation using git merge --no-ff to preserve a record that a branch was merged.
25. What is a merge conflict and when does it occur?
A merge conflict occurs when Git cannot automatically reconcile differences between two branches because the same lines in the same file were changed differently, or one branch edited a file the other deleted. Git pauses the merge and marks the conflicting regions in the affected files with conflict markers (<<<<<<<, =======, >>>>>>>). You must then manually decide how to combine the changes before completing the merge.
26. How do you resolve a merge conflict?
To resolve a merge conflict, you open each conflicted file, locate the conflict markers, and edit the content to reflect the desired final result, removing the markers in the process. After resolving every file, you stage them with git add and then complete the merge by running git commit (or git merge --continue). Visual merge tools or git mergetool can help when conflicts are complex.
27. How do you delete a branch in Git?
You delete a local branch with git branch -d <name>, which safely refuses to delete a branch that has unmerged changes. To force deletion regardless of merge status, you use git branch -D <name>. To delete a remote branch, you run git push origin --delete <name>, which removes it from the shared repository.
28. What does git checkout do compared to git switch?
git checkout is a versatile but historically overloaded command that can switch branches, restore files, and detach the HEAD, which sometimes caused confusion. To address this, newer versions of Git introduced git switch to handle branch changing and git restore to handle file restoration, splitting the responsibilities into clearer commands. Using git switch for branch operations is now considered safer and more intuitive.
29. What is HEAD in Git?
HEAD is a special pointer that refers to the current commit you are working on, which is usually the tip of the currently checked-out branch. When you make a new commit, HEAD (and the branch it points to) moves forward to the new commit. If you check out a specific commit directly rather than a branch, you enter a "detached HEAD" state where HEAD points straight at that commit instead of a branch.
30. What is a detached HEAD state and how do you get out of it?
A detached HEAD state occurs when HEAD points directly to a specific commit rather than to a branch, which typically happens when you check out a commit hash or a tag. In this state, any new commits you make are not associated with a branch and can be lost once you switch away. To preserve your work, you create a new branch with git switch -c <name> before switching, or simply check out an existing branch to leave the state.
Rebasing & Rewriting History
31. When should you use git rebase and when should you avoid it?
You should use git rebase to keep a clean, linear history on your private, local feature branches before merging them, for example by rebasing onto the latest main to incorporate upstream changes. You should avoid rebasing commits that have already been pushed and shared, because rewriting public history forces collaborators to reconcile diverging copies. A common rule is "never rebase commits that exist outside your repository and that others may have based work on."
32. What is interactive rebase and what can you do with it?
Interactive rebase, started with git rebase -i <base>, opens an editor listing the commits to be replayed and lets you modify them as a batch. You can reorder commits, edit their messages (reword), combine them (squash or fixup), drop unwanted commits, or pause to edit content. It is a powerful tool for cleaning up a messy local history into a coherent set of commits before sharing them.
33. What is the difference between squashing and fixup during a rebase?
During an interactive rebase, "squash" combines a commit with the previous one and lets you edit and merge their commit messages together, while "fixup" also combines the commits but discards the squashed commit's message entirely, keeping only the earlier one. Both reduce multiple commits into one, but fixup is convenient when the absorbed commit was just a minor correction whose message adds no value. The result is a cleaner, more meaningful history.
34. What does git cherry-pick do?
The git cherry-pick <commit> command applies the changes introduced by a specific existing commit onto your current branch as a new commit. It is useful when you want to bring a particular fix or feature from one branch into another without merging the entire branch. Because it creates a new commit with a different hash, the cherry-picked change is effectively a copy of the original.
35. What is git revert and how does it differ from git reset?
git revert <commit> creates a brand-new commit that undoes the changes introduced by a specified earlier commit, leaving the original commit in the history. In contrast, git reset moves the branch pointer backward and can remove commits from the branch's history, which rewrites it. Revert is the safe choice for undoing changes on shared branches because it does not alter existing history, whereas reset is better suited to local, unpublished work.
36. What is the difference between git fetch and git pull?
git fetch downloads new commits, branches, and tags from a remote into your local repository's remote-tracking branches, but it does not modify your working branch. git pull combines a fetch with an immediate merge (or rebase) of the fetched changes into your current branch, updating your working files. Fetching first lets you inspect incoming changes before integrating them, giving you more control than a direct pull.
37. What does git reflog do and why is it useful?
The git reflog command shows a log of where HEAD and branch references have pointed over time, including changes that may no longer appear in the normal commit history. It is extremely useful for recovering from mistakes, such as restoring commits lost after a bad reset, rebase, or branch deletion. Because Git keeps reflog entries for a configurable period, you can usually find and recover "lost" commits by referencing their recorded positions.
38. How can you recover a commit that was accidentally lost?
If you lose a commit through a reset, rebase, or branch deletion, you can usually recover it using git reflog to find the commit's hash from when HEAD pointed to it. Once you have the hash, you can restore it by creating a branch with git branch <name> <hash> or by resetting to it. Because Git rarely deletes objects immediately, lost commits typically remain recoverable until garbage collection eventually prunes them.
39. What is the purpose of git stash?
The git stash command temporarily shelves your uncommitted changes (both staged and unstaged) and reverts your working directory to a clean state, so you can switch context without committing half-finished work. You later restore the stashed changes with git stash pop (which removes it from the stash) or git stash apply (which keeps it). It is particularly handy when you need to quickly switch branches to address an urgent task.
40. What is the difference between git reset and git checkout for a specific commit?
When pointed at a specific commit, git reset <commit> moves the current branch pointer to that commit, effectively changing what the branch references and, depending on the mode, the staging area and working directory. In contrast, checking out a commit with git checkout <commit> leaves the branch pointers alone and instead moves HEAD directly to that commit, entering a detached HEAD state for inspection. Reset alters branch history, while checkout is generally used to view or extract a past state.
Remotes & Collaboration
41. What is a remote in Git?
A remote is a reference to a version of your repository that is hosted elsewhere, typically on a server such as GitHub, GitLab, or Bitbucket. Remotes let multiple developers share work by pushing and pulling commits to a common location, and the default remote created by git clone is named origin. You can list configured remotes with git remote -v and add new ones with git remote add <name> <url>.
42. What does git push do?
The git push command uploads your local commits to a remote repository, updating the corresponding remote branch with your changes so others can access them. You typically specify the remote and branch, as in git push origin main, and the first push of a new branch often uses git push -u origin <branch> to set up tracking. Push only succeeds if your local branch is up to date with the remote, otherwise Git asks you to integrate the remote changes first.
43. What is the difference between origin and upstream?
"Origin" is the conventional default name Git assigns to the remote you cloned from, while "upstream" usually refers to the original repository that you forked from in a fork-based workflow. In open-source contributions, you push to your own fork (origin) and fetch updates from the project's main repository (upstream) to keep your fork synchronized. These are naming conventions rather than fixed rules, so you can name remotes whatever you like.
44. What is a pull request (or merge request)?
A pull request (called a merge request on GitLab) is a collaboration feature on hosting platforms that proposes merging changes from one branch into another, typically a feature branch into main. It provides a place for teammates to review the code, leave comments, run automated checks, and discuss changes before they are integrated. Pull requests are central to modern team workflows because they enforce review and quality gates before code reaches the main branch.
45. What is a remote-tracking branch?
A remote-tracking branch is a local reference, such as origin/main, that records the state of a branch on a remote as of your last fetch or pull. These references are updated automatically when you communicate with the remote and serve as a read-only bookmark of the remote's position. They let Git tell you whether your local branch is ahead, behind, or diverged from the remote, which is essential for knowing when to push or pull.
46. How do you keep your local branch up to date with a shared remote branch?
To keep your local branch current, you regularly fetch the latest changes with git fetch and then integrate them, either by merging with git merge origin/<branch> or by rebasing with git rebase origin/<branch>. The git pull command bundles fetch and merge into one step, and git pull --rebase does fetch plus rebase. Frequent updates reduce the size and difficulty of merge conflicts by keeping your work close to the shared history.
Workflows & Best Practices
47. What is the difference between Git Flow and trunk-based development?
Git Flow is a branching model that uses long-lived branches such as develop and main alongside dedicated feature, release, and hotfix branches, which suits projects with scheduled releases. Trunk-based development, by contrast, has developers integrate small changes frequently into a single main branch (the trunk), often behind feature flags, favoring continuous integration. Trunk-based development reduces long-lived branches and merge complexity, while Git Flow provides more structure for versioned release cycles.
48. What are some best practices for writing commits?
Good practice is to make small, focused commits that each represent a single logical change, which makes history easier to review, revert, and understand. Commit messages should have a clear imperative summary line and, when needed, a body explaining the reasoning behind the change rather than just restating what changed. You should also avoid committing generated files, secrets, or unrelated changes together, and commit often enough to capture meaningful progress.
49. What is a Git tag and when would you use one?
A Git tag is a named reference that points to a specific commit, most often used to mark release points such as v1.0.0. Tags come in two forms: lightweight tags, which are simple pointers, and annotated tags, which store extra metadata like the tagger's name, date, and a message and are recommended for releases. Unlike branches, tags do not move, so they provide a stable, permanent marker for important milestones in your history.
50. How can you improve collaboration and avoid conflicts in a team using Git?
Teams reduce conflicts by pulling frequently to stay synchronized, keeping branches short-lived, and integrating changes in small, frequent increments rather than large infrequent merges. Establishing clear conventions, such as a consistent branching strategy, descriptive commit messages, and a pull-request review process with automated CI checks, helps maintain code quality and shared understanding. Communicating about who is working on which areas of the code further minimizes overlapping changes and surprise conflicts.