If you’re a junior developer, you’ve probably run into Git frustrations. Maybe you’ve committed to the wrong branch, lost work to a merge conflict, or written a commit message that says nothing useful. These are common Git mistakes that even experienced engineers make from time to time. The good news? They’re all avoidable once you understand the right workflow. This article walks through the top 10 mistakes junior developers make in Git and how to fix them.

1. Writing Vague Commit Messages
You’ve seen it before: a commit message that just says “fix” or “update.” These messages make it nearly impossible to understand what happened when you look back at the history. A good commit message tells you why a change was made.
Fix: Use a consistent format. Start with a short summary line (under 50 characters), then a blank line, then a more detailed explanation if needed. Prefix your messages with the type of change, like “feat:” for new features, “fix:” for bug fixes, or “refactor:” for code restructuring. For example: “fix: resolve null pointer exception in user login”. This practice helps everyone on the team – and future you – understand the project’s evolution.
When writing the detailed body, explain the motivation behind the change, not just the change itself. For instance, instead of “Changed the login method”, write “The previous login method threw a null pointer when the user object was missing a username field. Added a null check and a fallback default.” This context is invaluable during debugging or auditing. Also, consider using a commit message template that includes fields for the type, scope, and description. Many teams adopt the Conventional Commits specification for consistency across repositories.
2. Committing Directly to the Main Branch
Many juniors work directly on main or master. This is dangerous because every commit becomes part of the production-ready code instantly. If you make a mistake, it’s harder to undo.
Fix: Create feature branches for everything. When you start a new task, branch off main: git checkout -b feature/your-feature-name. Work there, commit often, and only merge back to main after review and testing. Branching keeps your work isolated and safe.
But branching alone isn’t enough. You also need a strategy. Name your branches descriptively, like feature/add-user-authentication or bugfix/fix-login-crash. This makes it clear what each branch contains. If your team uses issue tracking, include the ticket number, e.g., feature/PROJ-123-add-user-auth. Also, regularly delete stale branches after they’re merged to keep the repository clean. Many teams automate deletion with Git hooks or CI/CD pipelines.
3. Not Pulling Before You Push
You’ve made changes, committed them, and then git push fails because the remote has new commits. This leads to awkward merges or force pushes.
Fix: Make it a habit to pull the latest changes from the remote branch before you start working, and again right before you push. Use git pull --rebase to keep your history linear. Rebasing applies your changes on top of the latest remote commits, avoiding unnecessary merge commits. If there are conflicts, resolve them locally before pushing.
One common mistake is pulling from the wrong remote branch. Always verify your current branch and the upstream branch with git status and git branch -vv. If you work on multiple features simultaneously, you might forget to pull changes from the base branch into your feature branch. Set an upstream tracking branch with git branch -u origin/main so that git pull automatically fetches from the correct remote. Another tip: configure Git to rebase by default when you pull: git config --global pull.rebase true. This saves you from typing --rebase every time.
4. Panicking at Merge Conflicts
Merge conflicts are scary when you first see them. Junior developers often try to force a push without understanding the conflict markers.
Fix: Embrace conflicts as a normal part of collaboration. Open the conflicted files and look for the <<<<<<<, =======, and >>>>>>> markers. Those show the two versions of the code. Decide which to keep, or combine them, then remove the markers. Stage the resolved files and commit. Visual tools like VS Code’s merge editor can make this process much easier.
To prevent conflicts in the first place, communicate with your team about which files you’re working on. If you know someone else is modifying the same file, coordinate early. Also, pull and rebase frequently to keep your branch up to date. When resolving conflicts, don’t rush. Understand both sides of the change. Sometimes the conflict isn’t about which code to keep, but about how to integrate both changes meaningfully. After resolving, test the code thoroughly before pushing.
5. Forgetting to Stage Changes Properly
Sometimes you add files with git add . without checking what’s included. This can commit temporary files, debug logs, or unfinished work.
Fix: Use git add -p (or git add --patch) to review each change interactively before staging. This lets you stage only the parts you want, leaving out unwanted changes. Also, use a .gitignore file to prevent common temporary files from being tracked in the first place.
Another helpful command is git diff --cached to review what’s staged before you commit. If you accidentally stage a file you didn’t intend, you can unstage it with git reset HEAD <file>. For large refactors, consider committing in small, logical chunks rather than one massive commit. This makes it easier to revert specific changes later. Use git status constantly to stay aware of what’s staged, modified, and untracked.

6. Using Force Push Without Understanding It
git push --force can overwrite remote history, causing issues for anyone else working on the same branch. Juniors sometimes use it to fix a mistake without realizing the consequences.
Fix: Prefer git push --force-with-lease. This safer version will refuse to push if the remote branch has commits you don’t have locally. If you must rewrite history (e.g., to squash commits before a merge), do it on a feature branch that only you are working on, and communicate with your team.
Before force-pushing, always check if anyone else might be using the branch. A good practice is to rebase interactively (git rebase -i) to clean up your commits before pushing to a shared branch. If you do need to force-push, notify your team immediately. Some teams protect certain branches (like main) from force pushes using branch protection rules in GitHub or GitLab. This adds another safety layer.
7. Neglecting to Use .gitignore Properly
Config files, environment files, build artifacts, and third-party libraries often get committed by mistake. This clutters the repository and can even expose sensitive information like API keys.
Fix: Create a comprehensive .gitignore at the start of your project. For common languages, GitHub has templates you can start from. Regularly check what’s being tracked with git status and update the ignore file as needed.
One common pitfall is ignoring files after they’ve already been committed. If you commit a file that should be ignored, you must remove it from tracking with git rm --cached <file>, then add it to .gitignore. But be careful: this won’t remove the file from existing commit history. For sensitive data like passwords, you should rotate credentials immediately. To clean up a past commit that exposed secrets, use git filter-branch or BFG Repo-Cleaner, but this rewrites history and requires team coordination.
8. Creating Long-Lived Branches Without Merging
A branch that lives for weeks or months becomes a divergence nightmare. When it’s finally merged, conflicts are massive and code reviews are painful.
Fix: Keep branches short-lived. Ideally, merge your feature branch within a day or two. If it’s taking longer, consider rebasing your branch onto main regularly to keep it up to date. This way, conflicts are small and manageable.
But sometimes a feature genuinely takes longer. In that case, break it into smaller sub-features that can be merged incrementally. Use feature flags to hide incomplete functionality behind a toggle. This lets you merge code frequently without exposing it to users. Also, schedule regular branch updates: every morning, rebase your branch onto main. This way, you handle conflicts as they arise, not in a giant batch at the end.
9. Ignoring the Stash
You’re in the middle of work when you need to switch branches urgently. Some juniors commit half-done work to avoid losing it, creating messy commits.
Fix: Use git stash to temporarily save your changes without committing. Run git stash to stash your work, switch branches, do what you need, then come back and run git stash pop to restore your changes. You can even stash multiple times and list them with git stash list.
You can also stash specific files with git stash push -- <file> or keep stashed changes with git stash --keep-index if you want to keep staged changes separate. To apply a stash without dropping it, use git stash apply. If you have multiple stashes, apply a specific one with git stash apply stash@{2}. Just be careful: stashes are not permanent, so don’t rely on them for long-term storage. They’re best for quick context switching.
10. Not Understanding the Difference Between Git Merge and Rebase
Junior developers often stick to merge because it’s familiar, but they might not know when rebase is better. Overusing merge can create a cluttered, non-linear history.
| Command | When to Use | Result |
|---|---|---|
git merge |
Merging a feature branch into main or when sharing history matters. | Creates a merge commit that preserves branch structure. |
git rebase |
Updating a feature branch with changes from main, or cleaning up local history. | Reapplies commits on top of another branch, creating a linear history. |
Fix: Use rebase to keep your feature branch up to date with the latest changes from main without extra merge commits. Use merge when you want to record that a feature was integrated as a distinct chunk of work. A good rule is: rebase on your private branches, merge when you bring changes into a shared branch.
One practical scenario: suppose you have a feature branch off main, and main has advanced. Instead of merging main into your feature branch, which creates a merge commit, rebase your feature branch onto main. This replays your commits on top of the latest main, giving a clean linear history. However, never rebase commits that have been pushed to a shared branch unless you coordinate with everyone who might have pulled them. That’s a sure way to cause confusion and lost work.
11. Overlooking Branch Protection Rules
Many junior developers work on repositories without branch protection rules, or they ignore them. This can lead to direct pushes to main and broken builds.
Fix: If you’re a repository admin, set up branch protection rules on your main branch. Require pull request reviews, status checks (like CI passes), and prevent direct pushes. As a contributor, respect these rules. Always create a pull request for changes to protected branches. If you’re working on a team without protection, advocate for adding them. They’re free and save a lot of headaches.
Additionally, use status checks to run automated tests before merging. Configure your CI pipeline to run on every push to a pull request. This catches issues early. If a status check fails, don’t merge until it’s fixed. This discipline ensures the main branch stays deployable at all times.
12. Not Using Aliases and Git Configurations
Typing long Git commands repeatedly is error-prone and slows you down. Junior developers often don’t customize their Git environment.
Fix: Set up Git aliases for common commands. For example, add to your .gitconfig:
[alias]
co = checkout
br = branch
ci = commit
st = status
lg = log --oneline --graph --all --decorate
Also, configure a good diff tool like git difftool to use a visual diff viewer. Set your editor for commit messages: git config --global core.editor "code --wait". These small tweaks make your daily Git workflow faster and less error-prone. Share your aliases with your team so everyone benefits.
Mastering Git is a journey. Start by fixing these common mistakes, and you’ll become a more reliable and efficient developer. For a deeper dive into automated workflows, check out the guide on how to set up a continuous deployment pipeline with GitHub Actions. And if you’re thinking about software design, the article on event-driven architecture offers a great perspective on building scalable systems.
Frequently asked questions
What is the most common Git mistake junior developers make?
Writing vague commit messages like 'fix' or 'update' is very common. This makes it hard to understand the purpose of a commit later. Using descriptive messages that explain why the change was made helps maintain a clean and understandable project history.
How can I avoid merge conflicts in Git?
Merge conflicts are easier to avoid by pulling frequently from the target branch using git pull –rebase. This keeps your feature branch up to date. Also, break large changes into smaller, more frequent commits and merges to minimize the scope of conflicts.
Should I use git merge or git rebase?
Use git rebase to incorporate changes from the target branch into your feature branch, keeping history linear. Use git merge when you want to integrate a feature branch into the main branch and preserve the fact that it was a separate line of development.
How do I undo a commit I just made?
If your commit hasn't been pushed yet, you can use git reset HEAD~1 to unstage the last commit while keeping your changes. To completely discard the changes, use git reset –hard HEAD~1. For commits that have been pushed, consider git revert to create a new commit that undoes the previous one.
What is git stash and when should I use it?
Git stash temporarily saves unstaged or staged changes so you can switch branches or work on something else. Use it when you need to stop mid-task to fix a bug on another branch. Later, apply the stashed changes back with git stash pop.

Leave a Reply