Commit to Success: Mastering Git From Init to Push
Remote Repositories and Collaboration
Push, Fetch, Pull and the Rejected Push

In plain words: a remote is a copy of your project that lives on another computer, such as GitHub. In this article you will connect your project to one, send your commits up, bring other people's commits down, and know what to do when Git says your push was rejected.
Where the commits go#
Every commit so far lives on one computer. If the disk dies, so does the history, and nobody else can see it. A remote fixes both. Git is distributed, so each person keeps a full copy of the repository, including its whole history, and the remote is the copy everyone agrees to send changes to. We can work offline, commit locally, and synchronize when we are ready.
Here we connect a local repository (the project folder on your computer, with its history) to a remote and learn the commands that move commits between them.
Remote Repository Concepts#
What is a Remote?#
A remote repository in Git is simply a version of your project hosted on the internet or network somewhere. It could be on GitHub, GitLab, Bitbucket, or even a custom server within your organization. Remotes allow you to push your changes to a shared location and pull others' changes into your local repository.
You can have more than one remote on a repository, each pointing at a different shared copy of the project.
What a real remote URL looks like#
A remote is just a nickname for a URL. To get a real one, create an empty repository on a host: on GitHub, choose New repository, give it a name, and leave the README and license options off so there is nothing in it yet. The page then shows its address, which looks like one of these:
-
HTTPS:
https://github.com/your-username/your-repo.git -
SSH:
git@github.com:your-username/your-repo.git
Copy that address. It is what goes after git remote add origin below. The practice terminal later in this article uses a made-up address, so nothing is sent anywhere; the commands are the same ones you will type for real.
Origin and Upstream Explained#
When you clone a repository, Git automatically creates a remote called "origin" that points to the repository you cloned from. This naming convention helps you remember where your code originally came from.
# View your remote connections
git remote -v
In many workflows, particularly when working with forked repositories, you'll encounter another common remote name: "upstream." This typically refers to the original repository that you forked from, while "origin" points to your personal fork. You need that distinction when you keep your fork up to date with the original project.
# Add an upstream remote
git remote add upstream https://github.com/original-owner/repository.git
Authentication Methods#
To interact with remote repositories, you'll need to authenticate yourself. Git supports several authentication methods:
-
HTTPS Authentication: Using your username and a personal access token, a long random string you create in your account settings. GitHub no longer accepts your account password for Git over HTTPS.
-
SSH Authentication: Using SSH keys for passwordless, secure communication.
-
OAuth and Personal Access Tokens: Many platforms offer token-based authentication for enhanced security.
SSH is the usual choice for daily use because you do not type a secret for every operation.
Optional: setting up SSH keys#
You can skip this for now, because HTTPS with a token works without it. SSH gives you a key pair so the host recognizes your computer without asking for a password each time:
-
Generate an SSH key pair:
ssh-keygen -t ed25519 -C "your.email@example.com" -
Start the SSH agent, a small background program that holds your key. Note the
eval: plainssh-agent -sonly prints the commands that set it up, and the agent is not usable in your shell until they run.eval "$(ssh-agent -s)" -
Add your private key to the agent:
ssh-add ~/.ssh/id_ed25519 -
Copy your public key (the
.pubfile is the one that is safe to share) and add it to your GitHub/GitLab account settings:cat ~/.ssh/id_ed25519.pub
Essential Remote Operations#
Adding and Removing Remotes#
You can connect your local repository to multiple remote repositories using the git remote command:
# Add a new remote
git remote add <name> <url>
# Example: Add a remote named "upstream"
git remote add upstream https://github.com/original-owner/project.git
If you no longer need a connection to a particular remote, you can remove it:
# Remove a remote
git remote remove <name>
# Example: Remove the "upstream" remote
git remote remove upstream
Fetching Updates#
The git fetch command downloads objects and references from a remote repository without integrating those changes into your working files. This is a safe way to see what others have been working on without affecting your local work.
# Fetch updates from all remotes
git fetch
# Fetch from a specific remote
git fetch origin
# Fetch a specific branch from a remote
git fetch origin feature-branch
Fetching is like checking what's new in the shared repository without actually applying any changes. It updates your local database with information about what has changed remotely.
Pulling Changes#
While fetch only downloads remote content, pull goes a step further by downloading and then integrating those changes into your local branch. Essentially, git pull is a combination of git fetch followed by git merge or git rebase (depending on your configuration).
# Pull changes from the remote's tracking branch
git pull
# Pull from a specific remote branch
git pull origin main
# Pull using rebase instead of merge
git pull --rebase origin main
Pushing Changes#
When you're ready to share your commits with others, you use the git push command to upload your local branch commits to the remote repository.
# Push your current branch to its tracking branch
git push
# Push a specific branch to a specific remote
git push origin feature-branch
# Push a local branch to a different remote branch
git push origin local-branch:remote-branch
If you're pushing a new branch for the first time, you'll need to set up tracking:
# Push and set up tracking
git push -u origin feature-branch
Understanding Tracking Branches#
Tracking branches are local branches that have a direct relationship to a remote branch. When you clone a repository, Git automatically creates a local main branch that tracks the remote origin/main branch.
This tracking relationship makes commands like git pull and git push work without additional arguments—Git knows which remote branch corresponds to your local branch. That linked remote branch is called the upstream branch, which is different from the "upstream" remote name used for forks above.
# Check which remote branches your local branches are tracking
git branch -vv
# Set up a local branch to track a remote branch
git branch --set-upstream-to=origin/feature-branch feature-branch
Connect this practice repository to a remote, publish your work, and then bring a teammate's change back down. The terminal accepts any URL, HTTPS or SSH, because nothing really connects.
Type these commands in order, in a terminal inside a practice folder:
- Add a remote named origin pointing at a repository URL (for example https://github.com/you/demo.git). Try
git remote add origin https://github.com/you/demo.git. origin is the conventional nickname for the main remote. - List your remotes with their URLs. Try
git remote -v. Two lines per remote: one URL for downloading, one for uploading. - Push main to origin. Remember it as the upstream if you can. Try
git push -u origin main.-ulinks local main to origin/main, so a plain git push works next time. - A teammate pushed. Download their work without changing your files. Try
git fetch origin. Your branch has not changed. Git only updated its picture of the remote. - Now bring those commits into your branch. Try
git pull.git pullisgit fetchfollowed by a merge (or a rebase with--rebase).
Fetch and pull look similar but are not. Fetch only updates what Git knows about the remote; pull also merges that into your branch.
What origin/main is#
After you push or fetch, git branch -vv shows the link between your branch and its remote counterpart. This is real output after a first push:
$ git branch -vv
* main 1ae84fe [origin/main] First
origin/main is a remote-tracking branch: your own copy's bookmark of where main was on origin the last time you talked to it. You cannot commit to it directly, and it only moves when you fetch, pull, or push.
Challenge: make your own remote
You do not need GitHub to try this. A folder on your own disk can play the remote. In an empty scratch folder:
git init
echo "hi" > README.md
git add README.md
git commit -m "First"
git init --bare ../demo-remote.git
# TODO 1: add a remote named origin that points at ../demo-remote.git
# TODO 2: push main and link it to origin/main in one command
Expected result: git branch -vv shows [origin/main] after your branch, and git remote -v lists origin twice, once for fetch and once for push.
When your push is rejected#
Two people start from the same commit. Bob pushes first. Then Alice commits and pushes. Git refuses (the remote path is left out of this real output):
! [rejected] main -> main (fetch first)
error: failed to push some refs to '<your remote URL>'
hint: Updates were rejected because the remote contains work that you do not
hint: have locally. This is usually caused by another repository pushing to
hint: the same ref. If you want to integrate the remote changes, use
hint: 'git pull' before pushing again.
Nothing is wrong with your commit. Git is protecting Bob's: the remote has a commit you have never seen, and pushing would erase it. The fix is to bring his work in first, then push. On Git 2.45 a plain git pull here stops with fatal: Need to specify how to reconcile divergent branches until you say how, for example git pull --no-rebase to merge (or git config pull.rebase false once). If you both edited the same line, the pull ends like this, and you resolve it exactly as in a local merge conflict:
Auto-merging n.txt
CONFLICT (content): Merge conflict in n.txt
Automatic merge failed; fix conflicts and then commit the result.
Edit the file, git add it, git commit, then git push again. Branching and Merging Strategies walks through that conflict step by step. Pushing is now accepted, because your branch contains everything the remote has.
Collaboration Workflows#
Fork and Pull Request Model#
The fork and pull request model is particularly popular in open-source projects. Here's how it typically works:
-
You fork (create a copy of) the original repository to your account.
-
Clone your fork to your local machine.
-
Create a branch for your changes.
-
Make and commit your changes.
-
Push your branch to your fork.
-
Create a pull request from your fork to the original repository.
This workflow provides a structured approach to contributions while giving project maintainers control over what gets merged.
Shared Repository Model#
In smaller teams or private projects, you might use the shared repository model, where all contributors have push access to the same repository:
-
Clone the repository.
-
Create a branch for your feature or fix.
-
Make and commit your changes.
-
Push your branch to the shared repository.
-
Create a pull request or merge request for review.
-
After approval, merge your changes into the main branch.
This approach simplifies the process by eliminating the need for forking but requires more careful coordination to avoid conflicts.
Code review#
A pull request is where a teammate reads the diff and comments before it merges. Git Best Practices and Workflows covers what a good review looks like.
Working with GitHub/GitLab/Bitbucket#
What the platforms add#
Git itself only moves commits around. Platforms such as GitHub, GitLab, and Bitbucket add the collaboration layer on top: automated checks that run on every push, and project-tracking tools such as boards and labels. They differ in the details, so pick by your team's existing tools rather than by feature lists.
Issues and Pull/Merge Requests#
Two features matter for everything in this article:
-
Issues: Track bugs, enhancements, tasks, and questions related to your project.
-
Pull/Merge Requests: Propose changes, facilitate code reviews, and integrate new features or fixes.
Most platforms allow you to link issues to pull requests, creating a traceable history of how and why changes were made.
Handling Remote Conflicts#
Strategies for Reducing Merge Conflicts#
Merge conflicts are inevitable in collaborative environments, but several strategies can help minimize their frequency and complexity:
-
Pull Frequently: Regularly integrate changes from the remote repository to avoid large divergences.
-
Use Feature Branches: Keep changes isolated to minimize overlap with other developers' work.
-
Coordinate on Shared Files: Communicate with team members when working on the same files.
-
Keep Commits Focused: Make smaller, targeted commits rather than massive changes.
-
Establish Code Ownership: Assign primary responsibility for different parts of the codebase.
Resolving Conflicts from Remote Changes#
A conflict in a pull is the same conflict as in a local merge: git status lists the files, you edit out the markers, git add the files, and git commit. If you would rather not fight it now, git merge --abort backs out. The steps, with a practice run, are in Branching and Merging Strategies.
What we learned#
- A remote is a nickname for a URL, and
originis the usual one. fetchupdates your picture of the remote.pullis a fetch plus a merge.push -usends a branch and links it to its remote branch.- A rejected push means the remote has commits you do not. Pull first, then push, and do not force it.
You can now share work. Part 7, Advanced Git Features, is about tidying it before you share: stashing unfinished work, fixing commits, and copying one commit to another branch.
Comments require JavaScript.