From creating a feature branch to pushing code and merging it safely, learn a repeatable Git workflow that keeps your changes organized, your history understandable, and your team in sync.
# A Practical Git Workflow for Everyday Development
You've learned how Git tracks changes, how branches work, how to merge independent histories, and how to recover from common mistakes.
Now let's put everything together.
Imagine you're working on a web application and need to add a search feature. You want to make the change without disrupting the main branch, keep your commits understandable, and share your work with your team.
What's the best way to approach it?
You don't need a complicated process. You need a repeatable one.
In this article, we'll walk through a practical feature-branch workflow, from checking your repository to integrating your finished feature.
## 1. Start by checking your repository
Before changing anything, find out where you stand.
Run:
```bash
git status
git branch --show-current
git log --oneline -5
```
These commands answer three useful questions:
- Are there uncommitted changes?
- Which branch am I working on?
- What does the recent history look like?
This quick check can prevent several common mistakes, such as starting a feature on the wrong branch or accidentally mixing unrelated changes into your work.
If you already have uncommitted changes, decide whether to commit them, preserve them separately, or otherwise handle them before switching branches.
Don't blindly discard changes just to get a clean working tree.
## 2. Update your view of the remote repository
If you're collaborating with a team, the remote repository may have changed since your last session.
Fetch the latest information:
```bash
git fetch origin
```
This downloads the necessary remote objects and updates your local remote-tracking references.
Remember, fetching doesn't automatically merge those changes into your current branch.
Now inspect the history:
```bash
git log --oneline --graph --decorate --all -10
```
You can see where your local branches and remote-tracking branches point.
If your team uses `main` as its primary branch, switch to it:
```bash
git switch main
```
If your local `main` is behind `origin/main`, update it using your team's preferred approach. For a simple workflow in which you have no local commits on `main` that need preserving, you might use:
```bash
git pull --ff-only
```
The `--ff-only` option allows Git to update the branch when it can fast-forward, but refuses to create a merge commit if the histories have diverged.
If the command fails because your local branch has diverged, inspect the history rather than forcing the update.
## 3. Create a feature branch
Now you're ready to work on the search feature.
Create a branch from the updated `main`:
```bash
git switch -c feature/search
```
Your branch is now a separate line of development.
Any new commits you make on `feature/search` will advance that branch rather than `main`.
You can verify the current branch with:
```bash
git branch --show-current
```
Why bother with a branch?
Because a feature branch gives you room to work, experiment, and make commits without immediately integrating unfinished work into the main line of development.
It's a simple habit that makes collaboration much easier.
## 4. Make changes in small, meaningful steps
Suppose the search feature involves three tasks:
1. Add the search input.
2. Implement the search logic.
3. Display the results.
You could do all three and create one enormous commit.
But smaller, meaningful commits often make the history easier to understand and review.
For example:
```bash
git add src/components/SearchInput.js
git commit -m "Add search input component"
```
Then implement the search logic:
```bash
git add src/lib/search.js
git commit -m "Implement search logic"
```
Finally, add the results display:
```bash
git add src/components/SearchResults.js
git commit -m "Display search results"
```
These file paths are illustrative; use the paths in your own project.
You don't need a separate commit for every tiny edit. The goal is to keep each commit focused on a coherent change.
A useful commit message explains what the commit does:
```text
Add search input component
Implement search logic
Display search results
```
Compare that with:
```text
Changes
Updates
Fix stuff
Final version
```
A good commit message saves time when you—or another developer—need to investigate the history later.
## 5. Review your changes before committing
You don't have to wait until the end of a feature to review your work.
Check the current state:
```bash
git status
```
Inspect unstaged changes:
```bash
git diff
```
Stage the changes you intend to include:
```bash
git add src/lib/search.js
```
Then inspect the staged changes:
```bash
git diff --staged
```
This is your opportunity to catch accidental changes, debugging statements, unrelated formatting, or sensitive information that shouldn't be committed.
If you need to stage only part of a file, use:
```bash
git add -p
```
Git will let you select individual change hunks interactively.
Before committing, run the checks appropriate to your project:
```bash
npm test
npm run build
```
These are examples; use the actual test and build commands defined by your project's scripts.
A successful commit doesn't guarantee your application works. Tests and builds provide additional confidence that your changes behave as intended.
## 6. Keep your feature branch synchronized
While you're working, someone else might merge changes into `main`.
Your feature branch won't automatically include them.
Fetch the latest remote information:
```bash
git fetch origin
```
If your team prefers a linear history, you can rebase your feature branch onto the latest remote `main`:
```bash
git switch feature/search
git rebase origin/main
```
Rebase replays your local commits on top of the updated base.
If your team prefers merge-based integration, you can instead merge the updated branch into your feature branch:
```bash
git merge origin/main
```
Choose the approach that matches your team's conventions.
If a rebase or merge encounters conflicts, resolve them carefully and run your tests again. For a rebase, continue with `git rebase --continue` after resolving and staging the conflicts, or abort the operation with `git rebase --abort` if you need to return to the pre-rebase state.
Avoid rewriting commits that other developers already depend on unless the team has explicitly agreed to it.
## 7. Push your feature branch
Your commits currently exist in your local repository.
To share them with your team, push the branch:
```bash
git push -u origin feature/search
```
The `-u` option sets the upstream tracking relationship for the branch, so later pushes can usually use the shorter command:
```bash
git push
```
Pushing publishes your commits to the remote repository, assuming the server accepts the update.
It doesn't automatically merge your feature into `main`.
In many team workflows, the next step is to open a pull request or merge request.
## 8. Review the feature before merging
A pull request is an opportunity to review the proposed changes before integrating them.
Reviewers can ask questions, suggest improvements, and identify problems that automated tests might not catch.
Before requesting review, check your branch history:
```bash
git log --oneline --decorate origin/main..HEAD
```
This shows commits reachable from your current `HEAD` that aren't reachable from `origin/main`.
Inspect the feature's overall changes relative to the merge base:
```bash
git diff origin/main...HEAD
```
The three-dot form compares the merge base of `origin/main` and `HEAD` with the current `HEAD`.
Review the result and make sure the changes are focused on the intended feature.
A useful pre-review checklist:
- The feature behaves as intended.
- Relevant tests pass.
- No debugging statements or accidental files remain.
- Commit messages explain the changes.
- The branch doesn't contain unrelated work.
- Documentation is updated where necessary.
Once the review is complete and any requested changes have been addressed, the feature is ready for integration.
## 9. Merge the feature
There are different ways to integrate a reviewed feature.
Your team may merge the pull request through its hosting platform, use a merge commit, use squash merging, or rebase the feature branch.
If you're merging locally and the team permits a regular merge, the basic workflow is:
```bash
git fetch origin
git switch main
git pull --ff-only
git merge --no-ff feature/search
```
The `--no-ff` option requests a merge commit even when a fast-forward would otherwise be possible. This preserves an explicit integration point in the history.
Use it only if it fits your team's conventions. Other teams prefer fast-forward merges or squash merges through their review platform.
After integration, verify the result:
```bash
git status
git log --oneline --graph --decorate -10
```
Run the relevant tests again if appropriate.
One important detail: the local commands above assume your local `main` can be safely updated from `origin/main` and that the feature branch is ready to merge. If your team requires pull requests, branch protection, or CI approval, follow that process instead of bypassing it.
## 10. Clean up when you're finished
After the feature has been successfully integrated, you may no longer need the local feature branch.
You can remove it with:
```bash
git branch -d feature/search
```
If the feature branch was pushed to the remote and your team wants it removed, that is a separate operation:
```bash
git push origin --delete feature/search
```
Only remove a shared branch when it's appropriate to do so. Your team may keep branches for release management or audit purposes.
Finally, switch to your main branch and prepare for the next task:
```bash
git switch main
git pull --ff-only
git status
```
You're ready to start again.
## 11. The complete workflow at a glance
Here's a compact reference for the process.
```bash
# Inspect your current state
git status
# Get the latest remote information
git fetch origin
# Update your local main branch
git switch main
git pull --ff-only
# Create a feature branch
git switch -c feature/search
# Make changes, stage, and commit
git add src/lib/search.js
git commit -m "Implement search logic"
# Push the feature branch
git push -u origin feature/search
# After review and integration, clean up
git switch main
git pull --ff-only
git branch -d feature/search
```
The example assumes the changes have been committed and the feature has already been integrated before cleanup. In a real project, include your tests, review, and merge process between pushing and deleting the branch.
Don't treat this sequence as a script you must run blindly. Each command has a purpose, and checking the repository state between steps is part of the workflow.
## 12. Common workflow mistakes
**Starting work on the wrong branch**
Check `git branch --show-current` before creating or committing changes.
**Using `git add .` without reviewing the result**
Inspect `git status` and `git diff --staged` before committing.
**Pushing without fetching for days**
Fetch regularly when collaborating, especially before integrating changes. A rejected push is a signal to inspect the remote history, not to force an update.
**Creating one enormous commit**
Prefer coherent commits that make code review and debugging easier.
**Treating a successful merge as proof that everything works**
Run the appropriate tests and builds. Git can integrate code that still contains bugs.
**Using destructive commands to make the repository look clean**
Don't use `git reset --hard` or force-push simply to remove inconvenient status messages. Understand what would be discarded or rewritten first.
## The takeaway
A practical Git workflow doesn't need to be complicated.
Remember the rhythm:
1. **Inspect** your repository.
2. **Fetch** the latest remote information.
3. **Branch** for the feature.
4. **Edit, stage, and commit** focused changes.
5. **Test and review** the work.
6. **Push** the feature branch.
7. **Integrate** it according to your team's workflow.
8. **Clean up** when the work is complete.
Git becomes much easier to use when these steps become habits rather than a collection of commands you have to look up every time.
The goal isn't to memorize every option Git offers. It's to understand what you're doing, know where your changes are, and keep your project's history useful.
## What's next?
You've now completed the introductory journey from Git's mental model to an everyday development workflow.
But Git has more to offer. You can continue with topics such as interactive rebasing, stashing, cherry-picking, tags and releases, bisecting regressions, and advanced collaboration workflows.
**The series doesn't end with knowing the commands. It starts with understanding why they work.**

