Pushing Changes
Pushing is the process of uploading your local repository commits to a remote repository. In this lesson, we'll explore how to push changes effectively, handle common scenarios, and understand the various push options.
Basic Push Command
The basic syntax for pushing changes is:
# Push to the default remote branch
git push
# Push to a specific remote and branch
git push <remote> <branch>
# Example: Push to origin remote, main branch
git push origin main
Key Concept: Pushing uploads your local commits to the remote repository, making them available to other collaborators.
Understanding Push Behavior
When you push, Git uploads all commits from your local branch that don't exist on the remote branch:
# Check what will be pushed (before pushing)
git log origin/main..HEAD
# Or see a summary
git status
# Example output:
Your branch is ahead of 'origin/main' by 3 commits.
(use "git push" to publish your local commits)
Pushing a New Branch
When pushing a branch for the first time, you need to set up the tracking relationship:
# Create and checkout a new branch
git checkout -b feature-login
# Make some commits
git add .
git commit -m "Add login feature"
# Push and set upstream tracking
git push -u origin feature-login
# OR
git push --set-upstream origin feature-login
# Now you can use just "git push" in the future
git push
Tip: The -u flag (short for --set-upstream) sets up the tracking relationship, so future pushes and pulls know which remote branch to use.
Checking Push Status
Before pushing, it's good practice to check your status:
# Check current branch status
git status
# See commits that will be pushed
git log origin/main..HEAD --oneline
# See detailed changes
git diff origin/main..HEAD
# Verify remote branch exists
git branch -r
Push Rejected Scenarios
Sometimes your push will be rejected. Here are common scenarios:
Scenario 1: Remote has changes you don't have
Error: Updates were rejected because the remote contains work
that you do not have locally.
Solution:
git fetch origin
git merge origin/main
# Or use pull (fetch + merge)
git pull origin main
# Then try pushing again
git push origin main
Important: Never force push to shared branches unless you absolutely know what you're doing and have coordinated with your team.
Force Pushing
Force pushing overwrites the remote branch with your local version. Use with extreme caution:
# Force push (dangerous!)
git push --force origin feature-branch
# OR
git push -f origin feature-branch
# Safer alternative: force with lease
git push --force-with-lease origin feature-branch
Differences between force push options:
--force:
- Overwrites remote regardless of changes
- Can lose other people's work
- Very dangerous on shared branches
--force-with-lease:
- Only overwrites if remote hasn't changed since you last fetched
- Safer alternative to --force
- Protects against accidentally overwriting others' work
- Recommended when force push is necessary
When to Force Push:
✓ After rebasing your own feature branch
✓ After amending commits on your feature branch
✓ Cleaning up your own work-in-progress branch
✗ Never on main/master branch
✗ Never on branches others are working on
✗ Never on public release branches
Pushing Multiple Branches
You can push multiple branches at once:
# Push all branches with matching names on remote
git push --all origin
# Push all tags
git push --tags origin
# Push specific branch
git push origin feature-login
# Push multiple specific branches
git push origin main develop
Deleting Remote Branches
You can delete branches on the remote repository:
# Delete a remote branch
git push origin --delete feature-old
# OR
git push origin :feature-old
# Verify the branch was deleted
git branch -r
# Clean up local tracking references
git fetch --prune
Tip: Deleting a remote branch doesn't delete the local branch. You need to delete it separately with git branch -d.
Pushing Tags
Tags are not automatically pushed with commits. You must push them explicitly:
# Create a tag
git tag v1.0.0
# Push a specific tag
git push origin v1.0.0
# Push all tags
git push origin --tags
# Push commits and tags together
git push --follow-tags origin main
Setting Push Defaults
You can configure default push behavior:
# Set push to only push current branch
git config --global push.default current
# Set push to only push if remote branch has same name
git config --global push.default simple
# Set push to push all matching branches
git config --global push.default matching
# View current push configuration
git config --get push.default
Recommended: Use "simple" or "current" as your push default. "matching" can accidentally push branches you didn't intend to push.
Dry Run Push
Test what would be pushed without actually pushing:
# Perform a dry run
git push --dry-run origin main
# Example output:
To https://github.com/user/repo.git
abc1234..def5678 main -> main
# Nothing was actually pushed
Verbose Push Output
Get detailed information about what's being pushed:
# Verbose push output
git push --verbose origin main
# OR
git push -v origin main
# See exactly what objects are being transferred
Push with Lease Options
Advanced force push protection:
# Force push only if remote matches expected value
git push --force-with-lease=main:abc1234 origin main
# Force push with lease on current branch
git push --force-with-lease origin feature-branch
# This fails if someone else pushed to the branch
# since your last fetch
Pushing to Multiple Remotes
Push to multiple remote repositories:
# Push to origin
git push origin main
# Push to backup remote
git push backup main
# Set up a remote to push to multiple URLs
git remote set-url --add --push origin https://github.com/user/repo.git
git remote set-url --add --push origin https://gitlab.com/user/repo.git
# Now git push origin main pushes to both
git push origin main
Tracking Branch Information
View and configure upstream tracking:
# View tracking information
git branch -vv
# Output shows upstream relationship:
* main abc1234 [origin/main] Latest commit
feature def5678 [origin/feature: ahead 2] Work in progress
# Set upstream for current branch
git branch --set-upstream-to=origin/main
# Remove upstream tracking
git branch --unset-upstream
Push Hooks
Git can run scripts before and after pushing:
# Pre-push hook runs before push
# Located at: .git/hooks/pre-push
# Example pre-push hook (prevents pushing to main):
#!/bin/sh
if [ "$(git rev-parse --abbrev-ref HEAD)" = "main" ]; then
echo "Direct push to main is not allowed"
exit 1
fi
# Make it executable
chmod +x .git/hooks/pre-push
Security Note: Always review your commits before pushing, especially to public repositories. Never push sensitive information like passwords, API keys, or private data.
Common Push Workflows
Workflow 1: Basic Feature Development
git checkout -b feature-new
# Make changes and commit
git add .
git commit -m "Implement new feature"
git push -u origin feature-new
Workflow 2: Update and Push
git fetch origin
git merge origin/main
git push origin feature-branch
Workflow 3: Rebase and Force Push
git fetch origin
git rebase origin/main
git push --force-with-lease origin feature-branch
Workflow 4: Push After Amend
git commit --amend
git push --force-with-lease origin feature-branch
Troubleshooting Push Issues
Issue: "failed to push some refs"
Cause: Remote has commits you don't have
Solution:
git pull origin main
git push origin main
Issue: "insufficient permission"
Cause: No write access to repository
Solution: Check repository permissions or use correct credentials
Issue: "would clobber existing tag"
Cause: Tag already exists on remote
Solution:
git push origin :refs/tags/v1.0.0 # Delete remote tag
git push origin v1.0.0 # Push new tag
Issue: "push declined due to email privacy"
Cause: GitHub email privacy settings
Solution:
git config user.email "your-github-username@users.noreply.github.com"
git commit --amend --reset-author
git push origin main
Practice Exercise:
Complete Push Workflow:
# 1. Create a test repository
mkdir push-practice
cd push-practice
git init
# 2. Create initial commit
echo "# Push Practice" > README.md
git add README.md
git commit -m "Initial commit"
# 3. Add remote (use your own repository)
git remote add origin https://github.com/user/repo.git
# 4. Push with upstream tracking
git push -u origin main
# 5. Create and push a feature branch
git checkout -b feature-test
echo "Feature content" > feature.txt
git add feature.txt
git commit -m "Add feature"
git push -u origin feature-test
# 6. Check status and tracking
git branch -vv
git status
# 7. Make another commit and push (no -u needed now)
echo "More content" >> feature.txt
git commit -am "Update feature"
git push
# 8. Practice dry run
git push --dry-run origin feature-test
Push Best Practices
✓ Always pull before pushing to avoid conflicts
✓ Use --force-with-lease instead of --force
✓ Push small, focused changes frequently
✓ Write clear commit messages before pushing
✓ Test your code before pushing to shared branches
✓ Use feature branches for experimental work
✓ Set up upstream tracking with -u on first push
✗ Never force push to main/master
✗ Never push sensitive data (passwords, keys)
✗ Avoid pushing large binary files
✗ Don't push broken or untested code to shared branches
Summary
In this lesson, you learned:
- git push uploads local commits to remote repositories
- Use -u flag when pushing a new branch to set up tracking
- Force push with --force-with-lease for safety
- Never force push to shared branches like main
- Push rejected means remote has changes you need to pull first
- You can delete remote branches with git push --delete
- Tags must be pushed explicitly with --tags or individually
- Configure push defaults for consistent behavior
Next Up: In the next lesson, we'll learn about fetching and pulling changes from remote repositories!