Git gives you a history of your code and a way to work on changes without overwriting the stable version. GitHub adds a shared place to review those changes, track issues, run checks, and publish releases. A small project does not need a complicated branching model: a clean default branch, short-lived feature branches, and pull requests are enough for many solo developers and small teams.
This guide walks through that workflow, from starting a change to shipping a version. If Git itself is new to you, start with this introduction to version control systems.
A simple workflow to start with
Keep your default branch (main in these examples) in a state you can build or deploy. Create a branch for each focused change, commit related work there, and open a pull request (PR) when it is ready for review. Run the project’s checks before merging. For a solo project, a PR still gives you a useful place to read the final diff and see check results; a small team can use the same steps for discussion and review.
Git branches are lightweight, so you can use short-lived branches without making a separate copy of the whole project. The Git branching workflow guide describes this approach.
1. Start a focused change on a new branch
Before switching branches, check whether you have uncommitted work:
git status --short
If the output lists changes you still need, commit them or save them before switching. Then update your local default branch and create a branch for the task:
git switch main
git pull --ff-only origin main
git switch -c feat/add-search
Replace main with your repository’s default branch if it has a different name. The --ff-only option updates your local branch only when Git can do so as a fast-forward; if the branches have diverged, Git stops instead of creating a merge commit for you. Read the git switch documentation and git pull options if you need a different branch or update strategy.
Use a short name that explains the work, such as fix/mobile-menu or docs/install-guide. Keep one branch focused on one task. A focused branch is easier to review and less likely to include unrelated changes.
2. Review and commit only the work you intend to share
Make the change, run the relevant tests or checks, then inspect the diff before committing:
git status --short
git diff
git add -p
git diff --cached
git diff --cached --check
git commit -m "Add search to product list"
git push -u origin feat/add-search
git add -p lets you choose individual hunks when a file contains both related and unrelated edits. If you prefer to stage whole files, use git add path/to/file instead. Review git diff --cached to confirm the staged changes are the ones you mean to commit. The -u option on the first push sets the remote branch as the upstream for later pushes; see the git push documentation.
Small commits with clear messages are easier to understand later. If you notice unrelated formatting or local configuration changes, leave them out of this branch rather than bundling them into a feature commit.
3. Open a pull request and use it as the review checklist
After pushing the branch, open a PR against the default branch. Describe the problem, the change, and how you checked it. Keep the PR small enough that someone can understand the important decisions without reconstructing the whole project.
If the work belongs to a GitHub issue, add a closing keyword such as Closes #42 to the PR description. GitHub links the issue and PR, and closes the issue when the PR is merged into the repository’s default branch. See GitHub’s guide to linking a pull request to an issue.
Before merging, read the PR diff as a whole. Check that the change solves the issue, tests cover important behavior, and no credentials or unrelated files were added. Ask reviewers for specific feedback where useful. Reviewers can comment, approve, or request changes; GitHub documents these options in its pull request review guide.
When you make requested changes, commit them on the same branch and push again. The PR updates with the new commits, so discussion and checks stay together. Agree on a merge method that fits your project; for a small team, consistency is more important than adopting a complex branching policy.
4. Bring the latest default branch into your work safely
If the default branch changes while your PR is open, update your feature branch before merging when your project needs it. First make sure you have no uncommitted work, then fetch the remote and merge its default branch:
git status --short
git fetch origin
git switch feat/add-search
git merge origin/main
If Git reports conflicts, open each affected file and choose or combine the intended changes. Then stage the resolved files, finish the merge, run your checks again, and push:
git add path/to/resolved-file
git merge --continue
git push
Do not resolve a conflict by blindly keeping “ours” or “theirs”; inspect the surrounding code and preserve the behavior you need from both branches. If you are unsure how to resolve it, stop the merge and ask a teammate rather than pushing a guess. GitHub’s instructions cover resolving a merge conflict from the command line.
5. Automate checks that catch mistakes early
If you repeat the same test or build command for every change, configure a GitHub Actions workflow to run it when a PR is opened or updated. Start with the test command the project already uses; add more jobs only when they answer a real need. A small, reliable check is more useful than a long workflow that nobody maintains.
GitHub’s Actions quickstart walks through adding a workflow. If the repository has settings for protected branches, you can require important checks to pass before a PR is merged. That setting is separate from creating the workflow; see GitHub’s guide to protected branches.
For an example of GitHub Actions running checks around pull requests, see this related guide to using GitHub Actions with Terraform.
6. Create a release when you ship a version
For a library, app, or other versioned project, create a release after the changes are merged and you know which commit you are shipping. An annotated Git tag gives the version a name and message; pushing it makes the tag available on the remote. Git’s tagging guide explains annotated tags:
git switch main
git pull --ff-only origin main
git tag -a v1.2.0 -m "Release v1.2.0"
git push origin v1.2.0
Replace v1.2.0 with the version and commit you intend to release. On GitHub, you can create a release from that tag and add notes or downloadable files. A release is optional for projects that deploy continuously or do not publish versioned software. Read GitHub’s overview of releases before choosing a release process.
Adjust the workflow to your team
For a solo project, keep the process light: use a feature branch, check the diff, run tests, and merge when you are satisfied. For a small team, use the PR description to explain the user-visible change, request a review for decisions that affect others, and require only the checks that protect the project. Keep branches short-lived so they do not drift far from the default branch.
You can add release tags, issue templates, or more automation later. Start with a workflow you can follow consistently, and add rules when they solve a problem you have actually encountered.
Common Git workflow problems
- You cannot switch branches: Run
git status --shortand handle local changes first. Git avoids switching when it would overwrite work. - The fast-forward update fails: Your local and remote branches have diverged, or your local branch is not set up as expected. Fetch the remote and check the branch history before deciding whether to merge or rebase.
- The first push cannot find an upstream: Push the branch once with
git push -u origin branch-name, then usegit pushfor later updates. - A conflict is hard to resolve: List the conflicted files, inspect both versions and the surrounding code, and run the relevant tests after resolving. GitHub’s command-line conflict guide above shows the merge steps.
- The PR contains unrelated changes: Review the staged diff before each commit. If unrelated work was committed, coordinate with your team before rewriting a branch that others may already use.
The goal is not to use every Git feature. Make changes in small, reviewable steps; keep a known-good default branch; and let your project’s checks catch problems before they ship.