GitHub

Registering

Go to https://github.com and register for an account. Make sure to choose a username that's professional and recognizable, since this will be your username even if you switch around jobs.

For purposes of this book, you can go with the free plan. With it, you can create unlimited number of public and private repositories. The steps and processes will be the same for public or private repositories.

If you decide to configure your account with alternative security mechanisms, such as 2 Factor Authentication (2FA), you may be on your own when it comes to authenticating with the git command. This book only deals with the basic username/password authentication. For more information on authenticating with GitHub, please see GitHub's Documentation.

Some of the images in this chapter show pages on GitHub. The GitHub interface may have changed since we created these images, so the content may look a bit different. Some of these images may show the repository branch name as master instead of main; this is not crucial for our purposes.

Creating a Repo

Now that you have set up your GitHub account, it is time to learn how to use it. Whenever you are logged in to the GitHub website, you will see a plus icon in the upper right corner. When you click on that, you will see a menu that includes a New Repository link. Click on that link.

Creating a new GitHub repo

Choosing a repository name comes next. There are two general rules to follow:

  1. The repository name has to be unique to your account.

    That means that someone else can have a repository on GitHub named my-awesome-repo, and you can have one as well. You cannot however have two repos by the same name under your account.

  2. Choose a descriptive name based on your project.

    As far as naming conventions, some people separate words with hyphens and some use underscores. This is personal preference, and you may separate your words with any character you want except a space or tab.

In this guide, we are going to create a remote repository on GitHub.com where we can push the local repository that we created in the last chapter.

As you will see in this example, your local and remote repos do not have to have the same name. We will use my-first-repository as the name of our remote repository on GitHub.

Describing the new GitHub repo

You will notice that GitHub has added a green checkmark in the text field where we typed our repo name. That tells us that our repo name has no problems and can be used. If you have spaces in your repo name, GitHub will automatically replace them with hyphens.

The next thing we need to look at is the Initialize this repository with a README checkbox. This is a very important step in creating your remote repo.

  • If you check the box, it will automatically create and commit a README file in your remote repo. Before you can push any commits to the newly created remote repo, you will need to pull the code from your remote repo into your local repo. You will learn more about this later in the chapter.

  • If, however, you do not check the box, you will be given instructions for creating and linking a local repo with the newly created remote repo.

You will usually create your local repo first, so you will rarely check this box. Whether you initialize the GitHub repository with a README file or not, you need to understand conceptually the consequences.

For our example, do not initialize a README file, then click the Create repository button to create a remote repo on GitHub.com.

Connecting to GitHub Repo

Now you should see instructions on how to connect your local repo to the remote one you just created on GitHub. Looking at just the first option, the set of git commands should look very familiar. We've already done the first four or five commands, though with some differences. We only need to perform the last two commands to link our local repository with the remote.

Setting up a new local repo

In the command line, execute the following command. You will need to insert your GitHub username:

# Substitute your GitHub username and your repo name where indicated
$ git remote add origin https://github.com/GITHUBUSERNAME/REPONAME.git

For instance:

$ git remote add origin https://github.com/xyzzyfixlet/this-is-my-repo.git

What did we just do?

Before we move on, let's take a look at the command we just executed.

$ git remote add origin url_to_remote_git_repo.git

The primary git command we issued is git remote. Use this command to work with remote repositories. If you just issue git remote by itself, git will list all the remote repositories that your local repository knows about. If you pass additional sub-commands to the git remote command, you can further add, remove, and modify the remote repos that your local repo is linked to. Knowing that, you can guess that we're using the add sub-command to add a remote repository to the local repository.

That explains the git remote add parts of the command, but what about origin and url_to_remote_git_repo? Both of those things refer to the remote repository. The latter, the url_to_remote_git_repo, is pretty self explanatory: it's the URL to the remote git repository, which should end with a .git extension.

The origin is what we call an alias. The alias is the name that you will use locally to interact with the remote repo. You can have as many remote repos linked to your local repo as you want, but they must all have unique aliases. The instructions generated by GitHub always use origin as the alias. Any interaction that you have between your local repo and a remote repo will need to include this alias. You can name it whatever you like, but it must follow git naming conventions (no spaces, for example). Most developers use origin to mean the central repository where we're keeping the official copy of our codebase. More often than not, that implies a remote repo hosted on github.com.

The git remote add command

If you open ~/git_basics/.git/config in a text editor, you will see how git stores the information we just added. Recall that the .git/config file is where all the configuration settings for your local repository are held, and overrides any global settings. Note that your .git/config file may not necessarily look like the one below, which already contains an example remote repository called "origin".

[core]
        repositoryformatversion = 0
        filemode = true
        bare = false
        logallrefupdates = true
        ignorecase = true
        precomposeunicode = true
[remote "origin"]
        url = https://github.com/joesmith2444/my-first-repository.git
        fetch = +refs/heads/*:refs/remotes/origin/*
[branch "main"]
        remote = origin
        merge = refs/heads/main

The entries under [remote "origin"] are settings specific to that remote. The settings under [branch "main"] set up the tracking. For example, remote = origin tells your local repo to use the origin remote as the default remote for the local main branch to push and pull against.

GitHub Authentications

As you interact with GitHub via the CLI, you'll notice that some form of authentication is required to modify and access resources on GitHub. As of August 13th 2021, GitHub is switching from password-based authentication to require the use of token-based authentication. Token-based authentication can be achieved in a few ways, the simplest being the use of a Personal Access Token (PAT). A PAT is a type of hashed password, meaning that the plaintext form of the password is not saved on the server. When the client (you) provides the token to the server, the token is hashed using the same algorithm that hashed the stored version of the password and these values are compared to determine if they match. If you're enrolled in the Launch School curriculum, you'll learn more about hashed passwords in the Networked Applications course.

The chief benefits of PATs are twofold:

  • PATs have narrow scope; if one is compromised, it limits the damage that can be done.
  • PATs can be revoked individually; if one is compromised, you just have to create a new one and delete the old without having to track down every place where you might have been using a specific password.

The following steps will walk you through creating a PAT. Note that these steps and menu options are subject to change and may require some troubleshooting on your part. Please submit a feedback item if you find that any of the information below is no longer accurate.

  1. Verify your email address if you haven't done so already.
  2. Sign in to GitHub.com and go to 'Settings'.

    Select Settings

  3. Select 'Developer Settings' then 'Personal access tokens' in the left sidebar.

    Developer Settings

    Personal Access Tokens

  4. Click 'Generate new token' and give it a name (e.g., personal use)

    Generate New Token

  5. Select the scopes you'd like to grant the token. Scopes are a way of setting the permissions for users that have access to the token.

    Since this token should be used for your purposes only, we recommend not limiting any scopes and selecting all checkboxes as shown below. If you were working with other individuals or organizations and wanted to grant them different levels of access to your repositories, you could generate new, separate tokens and set the appropriate scopes.

    Scopes

  6. Click 'Generate Token'.

    Generate Token

  7. Copy the token and save it for future use. You should treat this token like you would another password. Consider using a password management app, such as LastPass, or any other secure means of saving the token. It is also recommended that you cache the token in Git using the cache credential helper. This allows you to enter the token as your password when authenticating for the first time and will retain your credentials so you won't be asked for it again until the specified timeout is reached. Follow the 'Linux' instructions in the link above if working in the Cloud9 environment.

    Note: Caching your GitHub credentials in Git does not permanently save your token and thus should not be used as the only means of securing your token.

  8. You should now test that the token works as intended by using it in the 'password' field when you push to GitHub in the next section.

    If you decide to configure your account with alternative security mechanisms, such as Two-Factor Authentication (2FA), you can now use this PAT as a starting point for authentication via the command line.

git push

You can cause a branch on your local repo (remember, every repo starts with a main branch by default) to track a branch on your remote repo. When you use a command that interacts with a remote repo, and do not specify which branch to interact with, the tracked branch will be used.

Now that we have laid all of the groundwork necessary to push our code to GitHub, let's do that!

In the command line, type the following:

$ git push -u origin main

The -u flag tells Git to set the default upstream repo -- that is, the remote repo that you intend to use by default for push, pull, and similar operations. In this case, we'll be using the main branch in the origin repository, which is a shorthand name for your default remote repo. (The name origin is conventional in most cases, though other names sometimes get used.) Once you've run this command, you can use git push and git pull without specifically giving the repo or branch name.

If you have never pushed anything to GitHub, you should now see a login prompt:

$ git push -u origin main
Username for 'https://github.com':

After you successfully login, you should see a message saying that your objects have been written. You will also see a message saying that you have set up your local main branch to track the remote main branch, thanks to the -u flag.

$ git push -u origin main
Username for 'https://github.com': joesmith2444
Password for 'https://joesmith2444@example.com':
Counting objects: 5, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (5/5), 368 bytes | 0 bytes/s, done.
Total 5 (delta 0), reused 0 (delta 0)
To https://github.com/joesmith2444/my-first-repository.git
 * [new branch]      main -> main
Branch main set up to track remote branch main from origin.

Once again, here's a breakdown of the command.

The `git push -u` command

git pull

Sometimes the code in your remote repository will contain commits you do not have in your local repository. In those situations, you will need to pull the commits from the remote repository into your local repository. There are four basic scenarios when you will run into this:

  1. You work on a team, and multiple people pushed code to the remote repository.
  2. You pushed to the remote repository from a different machine.
  3. Your project is public on GitHub and somebody contributed to it.
  4. You made a change to a file directly on GitHub.com.

github.com lets you modify files directly on the website, but behind the scenes, it's packaging that edit into a commit on the remote repository directly. This is not a good habit to get into and is only recommended for very special situations.

The first two scenarios are by far the most likely scenarios, while scenarios #3 and #4 are pretty rare.

Although it is not recommended to make changes using the GitHub editor, we will use this method to simulate and work through this workflow.

The first thing we need to do is view the remote repo. To do that, visit https://github.com. On the bottom right section of the screen, you will see a list of the repos that you currently have stored on GitHub.com.

Note: The GitHub interface may change over time, so these images may be out of date. If you see something different on your screen, don't panic. You should still be able to find the information that you need, though you may need to look around a bit.

GitHub top page

Click on the my-first-repository repo, and you will see the files that we pushed earlier in this chapter.

Your new GitHub repo

Notice that the contents of the README.md file are shown below the file list. This allows you to easily create documentation for your project, which GitHub will automatically display.

Now we need to make a change to a file. Click on README.md and then click on the pencil icon which is shown above the file's contents.

The README for your repo

Modify the contents of the file using the editor and then scroll down. You will see two input fields. These fields allow you to create commit messages for the remote repo just like the git commit -m command did for our local repo. Type in a commit message and then click the Commit changes button.

While GitHub gives us a nice UI for editing files, committing a change in their UI has the same effect on the remote repository that modifying a file locally and then using git add and git commit does on our local repo. Once again, we're editing files directly on GitHub for purposes of this book only, as this is something you rarely want to do in real projects.

Next, we switch back to our terminal and work with our local repository. We need to check our remote repo for changes. The first thing we need to do is fetch the repo, which will download the commit data for each branch that the remote repo contains. Because we are currently working with our local main branch, the git fetch command will fetch the commits from the remote repo containing the tracked remote branch.

In the command line, type:

$ git fetch

You should see some messages related to git downloading the remote changes. If you do not see any messages, your remote repo has no changes.

$ git fetch
remote: Counting objects: 3, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 1), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From https://github.com/joesmith2444/my-first-repository
   5afaf12..9389be5  main     -> origin/main

The next thing we need to do is look at what has changed. We do that using the diff command. On the command line, type:

$ git diff main origin/main

That produces the following output:

$ git diff main origin/main
diff --git a/README.md b/README.md
index 0647d92..56289ec 100644
--- a/README.md
+++ b/README.md
@@ -1,0 +2,4 @@
+
+This is my first GitHub Repository!
+
+Here is some more text.

The output from the git diff command shows some changes in our README.md file. Let's look at the output in some more detail.

First, we ran this command:

$ git diff main origin/main

This command tells git to compare the main branch on the local repo to the main branch on the remote repo (origin/main). Put another way, we're comparing the most recent commit in the local repository with the most recent commit in the remote repository. This sequence treats the local repo as the original content, and the remote repo as the updated content.

Since we're comparing a modified remote repo to our unmodified local repo, the sequence of repos in the command is backwards from usual. Normally, you want to compare a modified local repo to the original code in a remote repo. When you do that, you should specify the repo names in the opposite order:

$ git diff origin/main main

If you get the repos backwards, the output from git diff can be confusing.

The git diff command compares all files in the first repo (the local here) with the corresponding files in the second repo (the remote here). The individual differences begin with a line that looks like this:

diff --git a/README.md b/README.md

This shows that we're doing a git diff on the file named README.md. The file name that begins with a/ comes from the first repo in the git diff command (the local), while b/ refers to the second repo (the remote).

Next up comes some mysterious output that you can ignore for now:

index 0647d92..56289ec 100644

We now see the following:

--- a/README.md
+++ b/README.md

This indicates that output will use a - at the beginning of a line that is only present in the a file (the local repo), and + shows lines that are only present in the remote. You can think of + as added lines, and - as removed lines. If a line is different in both the local and remote repo, you will see both the + and - lines in the output. If some lines haven't changed, they will begin with a space character if they are displayed at all.

The next line shows where a specific difference occurred:

@@ -1,0 +2,4 @@

These are a little hard to read, but they represent line numbers in the - and + files. In this case, we're looking at one line starting with line 1 from the local repo version of README.md and 4 lines starting at line 2 from the remote repo version.

Finally, we see the differences:

+
+This is my first GitHub Repository!
+
+Here is some more text.

Here we see the differences as four additional lines from the remote repo, each preceded by a + indicating a new line.

Reading diffs can be a little difficult at first. With practice, though, you can learn to read them. The most important thing to remember is that + represents added lines from the repo specified second on the command line, while - represents deleted lines from the repo specified first.

Once you're sure that you want to pull the changes into your local repo, type:

$ git pull --ff-only

Since the local main branch has been configured to track the origin/main branch, you do not have to specify what branch to pull from.

Note that you can also type git pull --ff-only origin main if you want to specify exactly from which remote repository (using the alias) and which remote branch you want to pull. The commits will be pulled and merged into the current branch you're on in your local repository.

$ git pull --ff-only
Updating 5afaf12..9389be5
Fast-forward
 README.md | 4 ++++
 1 file changed, 4 insertions(+)

Now if we open our local README.md file in a text editor, we will see the changes that we made on our remote repo using GitHub's editor.

# README #

This is my first GitHub Repository!

Here is some more text.

Your local repo is now identical to the remote repo, and you can start working on files locally again. You can make changes to the README file, commit the changes and push them to GitHub again.

Some things to note

  1. The --ff-only part of this command is an option that is passed to the command. This tells git exactly how we want to try and combine the remote changes with our local branch. In this case it tells git to use a fast-forward merge to combine the changes (the ff part). You don't need to understand exactly what this means or how this works, but in simple terms it moves the history of the local branch forwards to match the history fetched from the remote branch. The -only part of ff-only means that if git can't combine the changes with a fast-forward merge it should abort the merge.

  2. There are other options that you can pass to git pull in order to combine the changes in different ways, but they are beyond the scope of this book. Until you start working collaboratively and/ or with multiple different branches, git pull --ff-only should be sufficient for the majority of situations where you need to pull changes from a remote repository.

  3. If you use git pull without passing an option such as --ff-only, git will decide itself how best to combine the changes. Generally though, it is good practice to be specific about what you want git to do. Since version 2.26.0 of git, if you issue a git pull without an option you will see a message like this:

    warning: Pulling without specifying how to reconcile divergent branches is
    discouraged. You can squelch this message by running one of the following
    commands sometime before your next pull:
    
    git config pull.rebase false  # merge (the default strategy)
    git config pull.rebase true   # rebase
    git config pull.ff only       # fast-forward only
    
    You can replace "git config" with "git config --global" to set a default
    preference for all repositories. You can also pass --rebase, --no-rebase,
    or --ff-only on the command line to override the configured default per
    invocation.
    
  4. You can issue a git pull --ff-only without first using git fetch and checking the changes using git diff. You should only do this if you are sure that you want to combine the remote changes with your local branch without needing to check them first.

  5. If you have already issued a git fetch, you can use git merge --ff-only rather than git pull --ff-only. Essentially, git pull --ff-only combines git fetch and git merge --ff-only into one command.

git clone

What is git clone

Thus far, we have been talking about connecting an existing remote repository with an existing local repository. But what if we don't have an existing local repository, and just want to pull down all the contents of a remote repository? This is where git clone comes in. It makes an exact clone of an existing remote repository, and copies it onto your local machine. The cloned local repository will include all the files, commit history, branches, and everything else associated to that git repository.

The git clone command has several options, but most of the time you will only need its basic usage. Here is what it looks like:

$ git clone <remote repository url> <local directory name>

Here's what it looks like in action:

$ git clone https://github.com/bob/example.git my_amazing_project
Cloning into 'my_amazing_project'...
remote: Counting objects: 218, done.
remote: Total 218 (delta 0), reused 0 (delta 0)
Receiving objects: 100% (218/218), 180.21 KiB | 0 bytes/s, done.
Resolving deltas: 100% (54/54), done.
Checking connectivity... done.
$ ls
my_amazing_project/

In the one command above, several things happened:

  1. A new local git repository was created in a directory called my_amazing_project.
  2. All contents in the remote repository github.com/bob/example.git were downloaded into the newly created local repository.
  3. A remote was added to the local repository's configuration, pointing to the remote repository URL, aliased as origin. This is the same thing as issuing git remote add origin https://github.com/bob/example.git from within the local repository directory.

This means that you can now git push or git pull from the newly created local repository to/from the remote repository.

When to use 'git clone'

git clone is used mainly when you need to work with a remote repository, and do not yet have a local repository created. For example, if you just joined a new project, the first thing you'd likely do is git clone the project in order to start working on it locally.

Another common use case is if you want to pull down an open source project. For example, if you wanted to check out the source code for the popular open source web development framework, Ruby on Rails, you can issue this command:

$ git clone https://github.com/rails/rails.git

Notice that we didn't specify a second parameter, the directory name. If you don't specify a directory name, like we did here, then the default behavior is that a new directory will be created with the same name as the cloned repo. In this case, the directory will be called rails.

After running this command, you should have a directory called rails, which is a local git repository containing all the code and contents of the official Rails repository. Further, this local repo will have a remote called origin that points to the official Rails repository. Unfortunately, this doesn't imply you can now push commits to the official Rails codebase. Though you can set it as a remote in your local repo, you still need permission to push to the actual remote repository, which you don't have. You can, however, poke around and make changes to your local repo.

A note on git init and git remote

The three commands, git init, git clone and git remote add origin <remote repo> may seem very similar at first, so let's take a look at when you should use them.

Command When To Use
git init Create a new local repository.
git remote add origin REMOTEURL Add an existing remote repo as a remote of existing local repo.
git clone REMOTEURL Pull down contents of existing remote repo into a new local repo, and add a remote to the local repo pointing to remote repo.

Summary

Let's rehash what just happened in this chapter:

  • First, we created a repository on github.com, and chose not to initialize it with any files, leaving it a blank slate.
  • Next, we made our local repo aware of our newly created remote repo with git remote add origin REMOTEURL.
  • Then, we pushed some commits from our local repo to our remote GitHub repo using git push.
  • We then made a modification on github.com directly, simulating a coworker pushing commits to our remote github.com repo.
  • Finally, we pulled those commits down to our local repository using git pull, syncing up our local repo with our remote repo.
  • We also learned what git clone is, and how you would use it to work on an existing git repository.

As you can see from the above steps, working with git requires very detailed orchestration to make sure all repositories -- your local, your teammates', your other machines, and finally the central repo -- are all synchronized.

You may already see that there are many potential problems lurking just around the corner, but don't worry about that yet. You'll learn how to deal with conflicts and work with advanced git workflows over time. For now, just focus on working with git and GitHub on a one-person project.