A remote is just a name for a remote server you can clone, push, and pull from.
We identify these by a URL. With GitHub, this is a URL we copied when we went to clone the repo initially.
It’s possible to use this URL to identify the server in our everyday Git usage, but it’s unwieldy to type. So we give the remote server URLs nicknames that we just tend to call “remotes”.
A remote we’ve already seen a bunch of is origin
. This is the nickname for the remote repo you cloned from, and it gets set automatically by Git when you clone.
Before we begin, note that Git uses slash notation to refer to a specific branch on a specific remote: remotename/branchname
.
For example, this refers to the main
branch on the remote named origin
:
origin/main
And this refers to the branch named feature3490
on a remote named nitfol
:
nitfol/feature3490
We’ll talk more about this in the Remote Tracking Branches chapter.
You can run git remote
with the -v
option in any repo directory to see what remotes you have for that repo:
$ git remote -v
origin https://github.com/example-repo.git (fetch) origin https://github.com/example-repo.git (push)
We see that we’re using the same URL for the remote named origin
for both pull (part of which is fetch
) and push. Having the same URL for both is super common.
And that URL is the exact same one we copied from GitHub when cloning the repo in the first place.
Remember that a remote name is just an alias for some URL that you cloned the repo from.
Let’s say that you are all set up with your SSH keys to use GitHub for both push and pull, but you accidentally cloned the repo using the HTTPS URL. In that case, you’ll see the following remote:
$ git remote -v
origin https://github.com/example-repo.git (fetch) origin https://github.com/example-repo.git (push)
And then you try to push, and GitHub tells you that you can’t push to an HTTPS remote… dang it!
You meant to copy the SSH URL when you cloned, which for me looks like:
git@github.com:beejjorgensen/git-example-repo.git
Luckily it’s not the end of the world. We can just change what the alias points to.
(The example below is split into two lines so that it’s not too wide for the book, but it can be on a single line. The backslash lets Bash know that the line continues.)
$ git remote set-url origin \ git@github.com:beejjorgensen/git-example-repo.git
And now when we look at our remotes, we see:
$ git remote -v
origin git@github.com:beejjorgensen/git-example-repo.git (fetch) origin git@github.com:beejjorgensen/git-example-repo.git (push)
And now we can push! (Assuming we have our SSH keys set up.)
There’s nothing stopping you from adding another remote.
A common example is if you forked a GitHub Project (more on that later). A fork is a GitHub construct that enables you to easily clone someone else’s public repo into your own account, and gives you a handy way to share changes you make with the original repo.
Let’s say I forked the Linux source repo. When I clone my fork, I’ll see these remotes:
origin git@github.com:beejjorgensen/linux.git (fetch) origin git@github.com:beejjorgensen/linux.git (push)
I don’t have access to the real Linux source code, but I can fork it and get my own copy of the repo.
Now, if Linus Torvalds makes changes to his repo, I won’t automatically see them. So I’d like some way to get his changes and merge them in with my repo.
I need some way to refer to his repo, so I’m going to add a remote called reallinux
that points to it:
$ git remote add reallinux https://github.com/torvalds/linux.git
Now my remotes look like this:
origin git@github.com:beejjorgensen/linux.git (fetch)
origin git@github.com:beejjorgensen/linux.git (push)
reallinux https://github.com/torvalds/linux.git (fetch) reallinux https://github.com/torvalds/linux.git (push)
Normally when setting up a remote that refers to the source of a forked repo on GitHub, people tend to call that remote
upstream
, whereas I’ve clearly called itreallinux
.I did this because when we subsequently talk about remote tracking branches, we’re going to use “upstream” to mean something else, and I don’t want the two to be confusing.
Just remember IRL when you set up a remote to point to the forked-from repo, it’s relatively customary to call that remote
upstream
.
Now I can run this to get all the changes from Linus’s repo:
$ git fetch reallinux
And I can merge it into my branch (the Linux repo uses master
for the instead of main
for the main branch):
$ git switch master # My local master $ git merge reallinux/master # Note the slash notation!
That will merge the master
branch from the reallinux
into my local master
, once we’ve dealt with any conflicts.
If I make another commit, this will move my local HEAD
and master
to that new commit, and will leave origin/master
(my fork on GitHub) and reallinux/master
(Linus’s repo) farther behind.
Let’s say for fun I made two commits that I didn’t have on my origin
remote at GitHub. In that case, a chopped up and fabricated-for-demonstration-purposes log might look like this:
commit 2d7d5d (HEAD -> master)
commit cde831
commit 311eb3 (origin/master) commit d5d2cc (reallinux/master)
At this point I’d do a git push
to send my local master
changes to GitHub and catch up my origin/master
there. So the top commit would show:
commit 2d7d5d (HEAD -> master, origin/master)
commit cde831
commit 311eb3 commit d5d2cc (reallinux/master)
And reallinux/master
would still be behind somewhere. (And there it would remain until Linus deigned to merge my changes.)
It’s interesting that my local master
can be out of sync from the master
on origin
, right?
We’ll look at this in the Remote Tracking Branches chapter.