Introduction to Git and Github

Jason M. Kelly and Ian Milligan

 

Introduction

You may encounter Git throughout the digital humanities ecosystem. Many projects host their code base or data on the GitHub platform, which uses the open-source "Git" process to help manage files, code, and beyond. Additionally, collaborative projects often use "GitHub" as a way to keep track of various code versions, and allow contirbutors to make contributions -- and project owners to review contributions.

Git, however, has a somewhat steep learning curve. It does not always seem intuitive, and references to "forking repositories," "pulling," "committing," and "pushing" all make specific reference to various commands. In this tutorial, we walk users through the process of installing, using repositories, and contributing to them.

We believe that this is an important skill for a digital humanist to use, both to engage in collaborative open-source software projects, but also to use many of the tools and projects (including the Programming Historian) which use this approach.

What is Git?

The easiest way to think about Git is as a process for saving versions of your files. If you make a change and want to go back, Git will catalog earlier iterations of your work. In other words, at its core, Git is a version manager. Unlike some other programs that manage versions, Git is not automatic. You decide what versions of files you want to save.

The full documentation for Git is available here: https://git-scm.com. While Git can be used independently of GitHub, GitHub is the largest open-source code platform in existence. Consequently, we will show how Git and GitHub can be used together.

You can use Git in two ways: locally and remotely. Using Git locally means that you are versioning only on your own computer. Effectively, you are keeping track of your versions for your personal use. Using Git remotely means that you are keeping your versions on a server. If you make your project public to other users, you can use Git to share or collaboratively develop it. If you want to share your project, users can clone it and use it in their own environments. If you want to work collaboratively, users can branch a project, develop revisions, and then merge the revisions back into your version.

While Git was originally developed for coding, there are numerous applications for individuals working in the digital humanities: from sharing and building datasets to cowriting websites, grant proposals, and books.

At this point, you may be asking yourself why you would want to use Git if many of these features are already available in other programs. After all, WordPress can keep versions of webpages and track who updated them. Microsoft Word can be set to autosave. And, Google Docs can keep track of who made updates. All of these things are true, but there are some good reasons for using Git instead:

  1. Level of Control: None of the programs above offer the level of control provided by Git. For basic projects, such as a single paper, this is less important. But, for larger projects, with multiple types of documents and collaborators (imagine the paper, the datasets, some notes, and bibliographic references), the ability to keep track of every fact is invaluable. Moreover, Git is a single program for handling every type of document that you use. So, even if one of your programs doesn't include version control, Git provides it.

  2. Granularity: Git is ideal for keeping track of every substantive change to your files. On top of this, it allows users to add detailed annotations that explain changes and updates. The added features provided by GitHub allow users to create issues where all discussions and comments can be tracked about specific concerns.

  3. Testing: Git provides an environment in which projects stages can have branches. Imagine branches as alternative timelines where you can try out different scenarios. In the case of coding, this branch might be used to develop a feature or clean up code. In the case of a shared humanities project, this might be for developing a section of a larger piece. Then, when these sections are complete, they can be merged back into the original project.

  4. Freeware: For many users concerned with the ethics of software development, freeware, as defined by the GNU Project, is important. Git is open source, and as a consequence, users are not tied to the exigencies of the market.

The Command Line

You will work with Git using a command line, such as bash or zsh. Having some command line knowledge is valuable, but not necessary for this tutorial. To learn more about the bash command line, read the Programming Historian tutorial Introduction to the Bash Command Line.

Installation

Windows

Windows users will need to install Git. You can do this from the main page of the Git website: https://git-scm.com.

Mac and Linux

Mac and Linux systems come pre-installed with Git, so you should not need to download them.

Setup

To setup Git, you will need to enter the following commands into you bash command line. This is a one-time task.

  1. Setup your user name for git.

git config --global user.name "[firstname lastname]"
  1. Setup your email for git.

git config --global user.email "[email]"
  1. (Optional) Add coloring for Git to the command line.

git config --global color.ui auto

Create a Repository

To create Git repository, you need to navigate to the folder for which you want to create a repository. For this tutorial, we recommend creating a new directory in your documents folder.

You can navigate there by typing


cd Documents

Once you are there, create a new Directory named dh-git with the following command:


mkdir dh-git

To go inside your new directory, type


cd dh-git

To initiate your new repository, type


git init

This simple command creates a database for tracking changes to your directory. It will live inside your dh-git folder. However, you can't see it with a simple ls command. This is because it is hidden. However, if you type the following, you will be able to see your .git repository.


ls -a

Working Within a Repository

Let's create a few files inside the repository. You can do this using the touch command. Let's say that you want to create a new markdown file and a new html file within folder. All you would need to do is type


touch git-summary.md index.html

You could have named these files anything. Use the ls command, and you will see that these file are now in your directory.

You can see that these are empty files by using the open command. For example, you can open git-summary.md using your text editor by typing


open git-summary.md

You've now created a few files inside of your folder. Now, what you want to do is commit them to your Git database. This is a two-stage process.

First, you want to add them to your staging area by using the git add command. You have two options with git add. You can stage individual files, or you can stage your entire folder.

To stage an individual file, you would write


git add git-summary.md

If you wanted to stage the entire folder, you would write


git add .

More often than not, you will be staging the entire folder, but you have the option.

After you have staged the files with git add, you need to commit them to memory. You'll do this by typing


git commit -m "added two files to the folder"

The git commit adds the changes to the database. the -m command adds a message to the commit. In this case, We made a note for ourselves that we had "added two files to the folder". We could have written anything in the quotation marks, but it's most useful when you can provide some details of your updates for later reference.

When you hit return after this line, you will get a report about the changes you made.

git diff

When you are working, you may stage a file and then continue working on it before you commit. If you want to see the differences between the staged file and the unstaged file, you can use git diff. It will display text that you have deleted as well as text that you have added.

You can also use git diff to see the difference between your staged item(s) and your last commit. You do this my typing git diff --staged.

After Your First Commit

Now that you have made an initial commit, you can make changes to the folder. You can add files, add text within files, delete files and folders--whatever you want. Let's try it.

Delete the index.html file using the Remove command:


rm index.html

Then, add text to your markdown file by opening, editing, and saving it. Remember, you can open the file from the command line:


open git-summary.md

Let's create a new file as well:


touch anotherfile.html

Now that you have made these changes, make another commit. Remember, it's a two-step process. First you stage with git add and then you save with git commit:


git add .

git commit -m "some more changes"

As you develop more items for your folder, you would continue this process over and over. Each time you commit, you will gave a snapshot of your updates.

Looking At Your Changes

The whole reason that you are versioning with Git is so that you can go back to a prior version in case something gets deleted or something goes wrong.

The way you go back to look at your commits is through the git log command. Type the following:


git log

You will see that not only are your commits and your notes listed in order, but they are time stamped with your name and email.

If you need to go back to look at a former iteration of a file, all you need are the commit codes listed in your log. These are the long string of numbers and letters listed next to the word "commit."

Returning to a Previous Iteration

If you want to return to a previous version of a your file, Git allows you to go back and use it. It does this by creating a branch--i.e. a copy--of your original set of saved versions. It looks something like this:


    gitGraph

       commit id: "commit 1"

       commit id: "commit 2"

       branch "new branch"

       commit id: "version 2b"

       checkout main

       commit id: "commit 3"

       commit id: "commit 4"

In the diagram, you are creating versions of your folder, just as you did above. These are the versions on your "main" branch (sometimes this is called the "root branch," and in Git and GitHub, it was formerly referred to as the "master" branch - you may encounter older repositories that use this language).

Let's say that you have run four commits. You will see on the diagram that there are four nodes indicating each commit on the main branch. After running git log you then realize that you want to return to your second commit. This is easy to do using the checkout command.

In Git, you go to the main branch and choose the version that you want to have a look at. Remember the commit codes for each version shown by the command git log? All you need to do is copy the code and checkout that version. Let's do this:


git checkout 3d87616f4b35abbff4fb2693b3f90c17b2643692

Now, type


ls

You will receive the list of files from your previous iteration. In your case, these were git-summary.md and index.html.

Let's look at the diagram again.


    gitGraph

       commit id: "commit 1"

       commit id: "commit 2"

       branch "new branch"

       commit id: "version 2b"

       checkout main

       commit id: "commit 3"

       commit id: "commit 4"

By using the checkout function, you have created a copy of your original version. You call this new version a branch, because you are branching off the original version. Any commits you make to this new version will apply to the new branch and not change the original branch. It will look something like this:


    gitGraph

       commit id: "commit 1"

       commit id: "commit 2"

       branch "new branch"

       commit id: "version 2b"

       commit id: "version 2c"

       commit id: "version 2d"

       checkout main

       commit id: "commit 3"

       commit id: "commit 4"

You can always see which branch you are on by using the branch command:


git branch

This command will output something like (HEAD detached at 3d87616). This tells you where your new branch has diverged from the original branch.

You can make as many changes as you want in the the "detached HEAD" state. This includes making commits. It's important to know, however, that the database will not save these changes unless you formally save your new branch. You do this with the switch command.

To save any changes you make (including the list of commits) to the new branch, type


git switch -c new-branch-name

You can rename [new-branch-name] with any filename you want.

Branches

If you want to see which branches are available, you can use the branch command:


git branch

This will list all of you branches. If you do this now, you will see two branches: main and new_branch. The one you are currently on with have an asterisk next to it.

If you want to return to the main branch--or switch between branches in Git--you use the checkout1 command:


git checkout branch-name

To return to your main branch, you would type the following:


git checkout main

You can verify that you are on the main branch using the git branch or git log commands.

Adding and Deleting Branchs

You already know how to add a branch from a specific commit:


git checkout 3d87616f4b35abbff4fb2693b3f90c17b2643692

You can also add a new branch from wherever you are working:


git checkout -b new-branch-name

Try it using the following command:


git checkout -b third_branch

Then, run the git branch command. You will see that the asterisk is next to third_branch, which is now a third branch (and technically the second branch off of the main branch).

Let's add a new file to this branch and work through the staging and commit process:


touch third_branch_file.md

This creates a new markdown file named third_branch_file.md. You can always check this with the ls command.

Now, let's practice staging this new change:


git add .

Now let's compile it into your repository:


git commit -m "added the third_branch_file markdown document"

You can see your change using the git log command.

So far, you have been adding branches, but what if you want to delete a branch? That's just as easy. You use the following command:


git branch -d branch-name

Let's try it with third_branch:


git branch -D third_branch

You will see that you get an error. That's because you cannot delete a branch that you have checked out. You need to switch to another branch:


git checkout main

Now try running


git branch -D third_branch

You will see that you have deleted third_branch.

Merging Branches

You may be wondering why you would create a branch in the first place. After all, versioning along one branch allows you to go back and find an earlier version. This is a reasonable question.

For basic, single-author development, you may not need to create branches. But, if your project is complex--and, more importantly, if you begin collaborating with others--you will want to use branches. Let's take the simple example of a web development project. Let's say that you want to experiment on three different layouts for your web pages. Instead of creating three versions of every component of your page in the same folder structure, you can keep the three versions on three branches. You can experiment easily on each of the branches and then merge your preferred version back into the original version.

For individuals who are hosting live projects, branches allow them to create a development environment until they are ready to update the live version.

For multi-author development, the value of branches is obvious. Different individuals can work on aspects of the larger project without interfering with the others. When their part of the project is complete, whoever is the primary administrator can merge the changes back into the original version.

Let's check in on which branches you have created:


git branch

You will see that you have two branches: the main branch and the new_branch. If you are not already in new_branch., let's go there


git checkout new_branch

Create a new file:


touch newfile.md

You can open it with the open command:


open newfile.md

Your text editor should open the file. Now, add some text. You wrote, "This is my new file." Save it, close the file, and return to your Terminal.

You've updated this file on new_branch. Let's imagine that we're happy with it and want to merge it to the main branch. As always, you will need to stage and commit changes to your repository:


git add .

git commit -m "added text to newfile.md"

To merge this (and any other changes) to your main branch, you need to move back to the main branch:


git checkout main

This is because you want to be in the branch that pulls in the updates.

To merge branches, you want to use the merge command:


git merge new_branch

You can see that the newfile.md has been added to your main branch with ls.

Other Commands and Conclusions

These commands cover the main commands that you will use in git.

There are more options. For example, earlier we noted on Mac systems you may want to not have DS_Store files added to repositories: by default, you would have this file within each Git folder.

You can create a file named gitignore to "ignore" certain kinds of file. To do this, create a new file .gitignore in the main directory of your repository. The period before the file notes that it is a hidden file (remember, to see hidden files, you type ls -a). Open it in your terminal, and then add this one line to the file:


.DS_Store

When .gitignore is added to your repository using git add .gitignore, in the future, these DS_Store files will not be added.

We present this example in part because one of the authors really dislikes these DS_Store files, but that it illustrates in some ways the limitless potential of Git. If you can imagine a problem, try searching for it on the Web: chances are, somebody has an idea to handle it.

GitHub

Git and GitHub are not the same thing. Git is an open source program that allows you to manage versioning of your projects. GitHub, a subsidiary of Microsoft, is an online space that uses Git for storing your Git projects. The benefits of using GitHub are twofold. First it allows you to store your projects in the cloud. Second, GitHub is built to facilitate sharing and collaborating on Git projects.

Setting Up GitHub

Setting Up a GitHub Account

In order to use GitHub, you need an account. To set up an account, go to https://github.com. You will need to follow the steps for creating a new account.

GitHub Security

Once you have a GitHub account, you will need to set up your computer's security environment so that you can connect remote repositories (i.e. repositories on your computer with repositories on GitHub).

There are a number of ways to do this, but GitHub recommends using Git Credential Manager. To install Git Credential Manager, you will need to download the version for your system at https://github.com/git-ecosystem/git-credential-manager/releases.2

This should be all you need to do, because the installation will set up your computer system defaults.

Connecting a Repository to GitHub

There are two primary ways to connect repositories to GitHub. This tutorial will walk you through the easiest way to do it (even though this is not the technique suggested in the GitHub documentation). The reason that we are not recommending the technique suggested in the documentation is because there are extra steps that could cause confusion for beginners.

Step 1: Set Up a Repository in GitHub

Setting up a repository in GitHub can be done through your browser. Navigate to https://github.com and click on the green box in the top left side that says New. This will open up the Create New Repository page.

Choose a name for your repository. Let's call it first_github.

Click on the box next to Add a README file.

You can decide whether to keep your site public or private. It doesn't matter for this lesson.

Then, click on the button at the bottom, Create repository.

As you will see, you have created a new repository. Your Initial commit was creating the README.md file.

GitHub repository page for first_github. The main branch contains a README.md file from the initial commit, and the README displays the heading first_github.

Step 2: Setting Up a GitHub Repository Folder

You can put a GitHub Repository anywhere on your computer that you like. For this tutorial, you are going to create a GitHub Repository folder for all of your repositories in your Documents folder.

Navigate to Documents:


cd ./Documents/

Inside Documents, create a new GitHub repository directory:


mkdir git_repositories

Navigate into git_repositories:


cd git_repositories

Step 3: Clone the GitHub Repository

Now that you have created your first_github repository on the GitHub server, you will want to clone it on your local git_repositories directory.

Creating a clone in GitHub means that you have not only downloaded all the files but that you have downloaded the Git repository as well. This allows us to keep track of versions and sync between your local files and those on the server.

To clone the repository, you use the git clone command. To this, you add the URL to your repository. You do this by pressing the green <> Code button on your GitHub repository page.

GitHub Code menu showing options for cloning a repository. The HTTPS tab displays the repository URL with a copy button, along with options to open the repository in GitHub Desktop or download it as a ZIP file.

To clone this repository, you will add this URL to the git clone command in your command line. It would look something like this:


git clone https://github.com/6500jmk4/first_github.git

If you are collaborating with a group of people you will sometimes need to clone only a specific branch within the repository. To do this, you would write the following:


git clone --single-branch --branch <the name of the branch you would like to clone in Github> <the repository url from Github>

If you type ls inside your directory, you will see that the README.md document is now inside your new first_github folder:


cd first_github

ls

Step 4: Add Your Files to the Local Repository

Now that you have a local and remote repository that are linked via your Git database, you can begin making changes and syncing them.

Add a new file to your first_github folder:


touch first.md

This adds a new file to your local repository. If you want to sync this change to your remote repository, you have to stage and commit your changes first--just as you always do when you update your Git database.

Step 5: Stage and Commit Your Changes to the Local Repository


git add .

git commit -m "added the first.md file"

Now that you have committed the changes to your Git database, you want to push them to your online repository.

Step 6: Push Your Files to the Remote Repository

To push your updates to your remote GitHub directory, simply type:


git push origin

Origin is how Github refers to its repositories and lets you know what is remote and what is local. If you refresh your browser and look at your GitHub repository, you will see that your new first.md file is now available in your remote repository.

Working Collaboratively in GitHub

The benefits of working in GitHub include its collaborative features. Going over all of GitHub's features would require its own tutorial. In this section, we will focus on the basics necessary for collaborating on a GitHub project.

Issues

One of the most valuable features of GitHub is the ability to use the Issues feature. Issues allows users to communicate effectively with each other as they build out their projects. It's similar to a bulletin board system or threaded text chat. An individual can post a comment, question, problem, etc., and their collaborators can then engage in a discussion with them. Each Issue begins a new thread. Teams can use Issues to organize members by assigning one or more individuals to each issue.

To use Issues, team members log in to their repositories. Across the top of the screen, there are a number of tabs to choose from. The first is "<> Code," and the next is "Issues." Clicking on Issues will open the interface.

Adding a New Issue

To add a new issue, click on the green button on the top right: "New Issue." This will open another interface where you can title your issue and describe it. On the right side, you can assign collaborators to issues. You can also use labels to organize issues into categories. Once you have added a new issue, click on the "Submit new issue" button on the bottom right. This will add it to the list on the Issues landing page.

GitHub new issue page for the first_github repository. The title field contains 'I am adding a new issue...' and the description field is empty, with options for assignees, labels, projects, and milestones.

You can use the Issues menu to sort through authors, assignments, and categories.

Closing an Issue

Once you and your collaborators have addressed an issue, you will want to close it. To accomplish this, all you need do is click on the issue, scroll to the bottom of the page, and click the "Close issue" button. You will notice that the button has a dropdown next to it that allows you to indicate whether the issue was resolved or abandoned.

Branching in GitHub

Just as you would in your local version of Git, you can create branches through the GitHub web interface. There are multiple ways to do this, but the easiest way is to to go to your repository's main page and click on the dropdown menu at the top left of your file list. If you are in the first_github repository, it will be labeled main if you have only one branch.

When you click on the dropdown, you will see any branches that you have already created as well as the option to switch to them or create new ones. Let's create a new one titled "branch2". You will notice that you now have an option that should say something similar to "Create branch branch2 from main". Click on it to create your new branch.

You will notice that the dropdown menu now tells you that you are now on branch2 and that there are now two branches. Any updates that you make now will be in this new branch.

Go back into your command line and navigate to the first_github folder. You will see that the changes you made in the GitHub repository have not been synced to your local version. To sync the files, you will need to fetch the files and merge them with your local repository. You will learn more about this after you review some tools that you can use to keep your local and remote repositories in sync.

Keeping Your Local and Remote Repositories in Sync

Since this tutorial is focusing on command line git, it is important to remember a simple principle: you cannot directly edit the remote repository from your command line. In Git, you are always working with local files. When you want to update the remote repository, you push changes from your local repository. When you want to update your local files, you pull changes from the remote repository.

If you make changes to the files using the GitHub web interface, these are not automatically synced to your local computer repository. Likewise, when you make changes to your local repository, these are not automatically synced to the remote repository. Consequently, it's a good idea to make sure that your local and remote repositories are in sync on a regular basis.

Typically, you would check for any syncing issues at the beginning of a working session. There are a few commands you should know and use regularly.

git status

When you use git status, you will be able to check on the state of your local repository. It will let you know 1) what items have not been staged for a commit and 2) what items have been staged for a commit. In other words, it lets you know the state of your current work.

git log

As you have already seen, git log lets you know the history of commits for your repository. When you use git log, you can scroll with the spacebar or the up and down arrows. You can exit by typing q.

git branch

You use git branch to see your branches. You have already used git branch which lets you see the branches in your local repository.

This command can also let you view branches in your remote repository:

git branch -r

Let's try it out. As you will remember, you've already created a new branch for your repository on GitHub. You named it branch2. Make sure you are in your local repository folder and then type git branch -r. You will get the following printout:


  origin/HEAD -> origin/main

  origin/branch2fr

  origin/main

Take a moment to understand what this means. The notation origin is simply the default name of the GitHub location and allows us to distnguish between your local and remote repository.

HEAD is a variable that points to where you are on a specific branch. While it may initially look like there are three branches listed, the origin/HEAD -> origin/main simply means that your HEAD references the main branch.

So, what you have are two branches: the main branch and the second branch that you created: branch2.

Remote Tracking Branches

In the previous section, you saw that you can view remote branches using git branch -r. If you want to edit a remote branch, you will want to create a "tracking" copy of the branch.

To do this, you use the following command:


git checkout --track -b branch2 origin/branch2fr

You already know that git checkout -b creates a new branch, which you are calling branch2.

Have a look what has happened by typing git branch in the command line. Not only has a new branch been created, but you are now working on branch2.

The –track command turns your local branch into a remote-tracking branch. What this means is that Git 1) will keep track of the updates between the local and remote branch and 2) will know what to sync with a get pull command.

You can see which of your local branches are remote tracking branches as well as which remote branches they are connected to with the command


git branch -vv

When working on a large project, you probably will only create remote tracking branches for the branches that you are developing.

If you create a new branch in your local repository, it won't necessarily become a tracking branch if you upload it to your remote repository. To see this, let's create a new local branch. First, you need to switch to your main branch:


git checkout main

Now, create a new branch based on your main branch:


git checkout -b branch3

This creates a new branch (branch3) off of your main branch. You can see that this branch is not connected to any remote repositories by typing


git branch -vv

If you want to upload this to your remote repository and have it tracked, you will use the following command:


git push --set-upstream-to origin branch3

If you type git branch -vv in the command line, you will see that you are now tracking the remote repository origin/branch3.

Fetching and Merging

git fetch

When you want to review changes to your remote repository before adding them to your local machine, you use git fetch. This command fetches the updates from the remote repository, but it does not merge them with your local repository. This allows us to review the changes before a merge.

Let's see how this works. Go to the GitHub repository in your browser. Navigate to branch3 and add a new file with the name file4.md. You can add any text you wish in the file.

Go to your command line and navigate to your repository. To fetch the update from the remote repository, you will type:


git fetch origin

You could also type


git fetch --all

Both commands fetch all of the updates from the remote repository for you to review.

Similarly, you can do a fetch for specific branches:


git fetch origin/branch3

git diff redux

You have already used the git diff command. As you will remember, it allowed us to see the difference between the file you were working on and the file that was staged before a commit. It does the same thing for us in the contect of fetch.

You can utilize it in a number of ways. You can look at the differences between your local branch3 and your remote branch3 with the following command:


git diff branch3 origin/branch3

You will see not only file4.md but also the text of the file.

You can use git diff at any stage of your workflow. For example, let's say that you are editing a document on your local main branch. Every so often, you are using the git add command to stage your files before a commit. If you want to know all of the staged changes you have made, you type:


git diff --cached

You will be able to double-check all of the changes you made before you run git commit.

git merge

When and if you are happy with remote changes, you can merge them into your local repository. To do this, you would use the following command parameters:


git merge REMOTE-BRANCH-NAME LOCAL-BRANCH-NAME

Try merging the remote branch3 with your local branch3:


git merge origin/branch3 branch3

The output will state that there was "1 insertion." If you use the ls command, you will see that file4.md has been added to branch3 in your local repository.

git pull

Instead of fetch and merge, some people use the git pull command, which combines fetch and merge into one step. While you might want to use git pull if you are connecting your local repository to a GitHub repository for the first time (as you did earlier in this tutorial), you should be a bit more cautious with this command when you are collaborating with other people.

Using git pull skips the review step, which means that you won't be able to comapre and contrast differences in your repository. Only use git pull if you are absolutely positive that there are no merging conflicts.

Pushing Files to a Remote Repository

As with git pull, there is a git push command, which sends local updates to your remote repository.

It's important to keep in mind that Git will not allow you to overwrite a branch in a remote repository if there are unsynced updates. This is another reason to keep your local repository synced with your remote repository.


git push 

Conflicts

At some point in your work, you will run into conflicts between branches or between your local repository and your remote repository. While you will want to avoid conflicts as much as possible by using good communication and establishing a workflow process with your collaborators, conflicts are generally not big problems--assuming, of course, that you are making discrete commits and and syncing regularly.

If you do run into a conflict, Git allows you to compare versions, choose the version you would like to keep, and ignore the changes you don't want to make. To see how conflicts work, let's force a couple of conflicts to see how to resolve them.

In your local repository, make sure that you are in the main branch:


git branch #this will tell us what branch you are on

git checkout main #this will switch us to the main branch

Create a new file on your main branch titled file5.md, and let's add text to the file: "This is file5, and I think the text might conflict with my remote repository." To do this, you use the touch command again:


touch file5.md

You can edit this note to edit it by using the open command:


open file5.md

Don't forget to commit the file to your local repository:


git add .

git commit -m "created file5.md"

Now, create a file in your GitHub repository with the same filename, but let's give it different text. To do this, open the GitHub repository in your browser, make sure you are in the main branch, and create a file named file5.md. Include the following text: "This text in my remote repository is definitely different from the my local repository."

See what happens when you attempt to fetch this file:


git diff main origin/main

You will get a report similar to this:


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

index 272b8fc..045768b 100644

--- a/file5.md

+++ b/file5.md

@@ -1 +1 @@

-This text in my remote repository is definitely different from the my local repository.

+This is file5, and I think the text might conflict with my remote repository.

Without going through every line, here is what you are seeing:

  1. The diff command has indicated that a/ is the remote version of file5.md. The b/ file is your local version of file5.md.

  2. The remote file is indicated with a minus sign, while the local is indicated by a plus sign.

  3. The report indicates where the text conflicts by reprinting the remote text next to the minus sign and the local text next to the plus sign.

Now that you have recognized the discrepancy between the two files, you can fix them before you merge them.

Let's say, however, that you forget to run the diff command and just attempt to merge the files:


git merge origin/main main

The result is that you get an error message:


Auto-merging file5.md

CONFLICT (add/add): Merge conflict in file5.md

Automatic merge failed; fix conflicts and then commit the result.

Fortunately, Git has built in tools to resolve this conflict. To understand what is happening, let's look at a visualization.


    gitGraph

       commit id: "commit 1"

       branch "origin/main"

       commit id: "origin/commit 2"

       checkout main

       commit id: "commit 2"

Effectively, what you have done is create a local and a remote branch with the same name. When you added text to them, you sent them down different development paths so that commit 2 on your local branch looks different than commit 2 on your remote (origin/main) branch.

You want to merge commit 2 and origin/commit 2, but they have different text on the same lines. Git can't merge them, because it doesn't know which version you prefer. You will have to tell it.

You already know that you have issues with file5.md. You can use git status to get more information. If you enter git status in the command line, you will get a report that looks something like this:


On branch main

Your branch and 'origin/main' have diverged,

and have 1 and 1 different commits each, respectively.

  (use "git pull" to merge the remote branch into yours)

You have unmerged paths.

  (fix conflicts and run "git commit")

  (use "git merge --abort" to abort the merge)

Unmerged paths:

  (use "git add <file>..." to mark resolution)

        both modified:   file5.md

You have a few options at this point. As the report states, you can abort the merge and come back to the issue later by typing


git merge --abort

While it is not recommended, you could also do a git push or git pull and then deal with the conflict in your local or remote editor.

The preferred response is to address the problem using the following procedure. Because you have attempted a merge (and Git has recognized a conflict) it has updated your file with the conflict information. To see the conflict, just open the file:


open file5.md

You will see that the file has been changed and that the text looks like this:


<<<<<<< HEAD

This is file5, and I think the text might conflict with my remote repository.

=======

This text in my remote repository is definitely different from the my local repository.

>>>>>>> origin/main

While at first this may look confusing, it's relatively easy to interpret. The top section, labelled HEAD indicates your local file and the beginning of the text conflict. Underneath the ====== is the text from your remote file on the origin/main branch.

Since you are already in a text editor, all you need to do is edit the text. Delete the additions added by Git (e.g. <<<<<< HEAD) and change the text to "I have synced my local and remote repository text" and click save.

You have effectively resolved the conflicts by telling Git what you want the final text to look like. Now, all you need to do is make an add, commit, and push:


git add file5.md

git commit -m "merged my local and remote file"

git push

If you go to your remote file in GitHub, you will see that the file text has been updated.

As you can see, Git prevents us from accidentally overwriting text, and it gives us a relatively easy method for resolving conflicts. You can use the same process that you used for remote merges on your local repositories.

If you want to compare local branches before a merge, you would follow the same process as above:


git diff main branch2

Forking in GitHub

Assuming that a repository is set to public, users who do not have write privileges to a repository can still copy your repository to their own GitHub repository. This process is known as a fork. It allows you to clone somebody else's repository and develop it in a new direction.

Forking is useful because you can allow other people to experiment with your repository without giving them user access to your project. Their repository is a fork of your repository, and they can transform or update their copy as they wish. If they develop an improved version of the repository, they may open a Pull Request from their fork. If you decide that you would like to merge their changes to your repository, you can pull their files into your repository.

Forking in GitHub allows multiple users to work on developing the same project even if only one of them manages the primary account. Additionally, this feature allows users to create new versions of a project that can evolve in a different direction from the original.

If you would like to fork another person's repository, you simply navigate to their GitHub page and click the fork button. You will add it to your account and then you can edit it in GitHub or create a local repository following the directions outlined above.

Pull Requests

Pull requests are different from the pull command. Pull requests are unique to the GitHub interface, while pull is a command for Git. There are commonalities, but whereas you don't need to use the pull command in Git (remember, pull collapses the fetch and merge commands), you will need to work with Pull Requests in GitHub.

As you are working with GitHub, you are going to have a local repository on your machine and a remote repository on the GitHub server. Other users may be developing a branch, and still others may have a forked version of a repository in their own GitHub repositories as well as a cloned copy on their local machines. Keeping track of everything can get a bit complicated, so it is essential that you follow best practices by 1) maintaining a workflow that supports collaboration, 2) using branches to develop your work, and 3) keeping each commit limited to a single feature, making them regularly, and providing good descriptions.

Making a Pull Request

As you can imagine, multiple people working on different branches and forks means that you will eventually want to merge their updates into your main work.

Below, you can see what appears after there has been activity on a branch. In this case, we used GitHub to write this tutorial, and one of the co-authors was potenitally ready for a pull request.

GitHub repository page for the Ian-pass branch of git_tutorial. A banner notes that the branch had recent pushes and provides a Compare and pull request button. The branch is one commit ahead of main.

To open a pull request, you can either click on the Compare & Pull Request button or you can click on the "Pull Requests" tab and select New Pull Request. Doing so opens up a window like the following:

GitHub pull request creation page comparing the Ian-pass branch with main. GitHub indicates that the branches can be automatically merged. The pull request is titled 'Adding Ian's Suggestions to the GitHub Tutorial' and includes a description of the proposed changes.

A good pull request explains what is happening: a list of major changes, for example, to assist in reviewing. If there are no conflicts, the pull request can be merged automatically. However, if merging the pull request would lead to conflicts, these will have to be resolved (see below).

Reviewing Pull Request

Once a pull request has been opened, it is ready to be reviewed.

GitHub pull request Files changed tab showing edits to Git Guide.md. A review comment identifies the typo 'contirbutors' and suggests changing it to 'contributors.'

Pull Request Conflicts

As you have seen earlier in this tutorial, it is inevitable that conflicts will emerge when merging repositories.

As you saw with Git, the program will let you know when there is a merge conflict, and it will identify where the conflict is in your files. GitHub provides a similar experience that you may enjoy using.

You will know that there is a conflict, when you go to open up a pull request and you see a red X with the warning that you "Can't automatically merge."

GitHub pull request creation page comparing the ian-finalish-pass branch with main. A red warning states that the branches cannot be automatically merged, although the pull request can still be created.

Don't worry!

Create the pull request as you would normally. But, where you would normally see the "merge" button, you will now see that you will need to resolve the conflicts.

GitHub pull request warning stating that the branch has conflicts that must be resolved. git_guide.md is listed as the conflicting file, with a Resolve conflicts button and the Merge pull request button disabled.

When you select this button, you will see a familar text editor that provides identical functionality to the merge resolution issues that you handled using git. Remember that the top section, labelled HEAD indicates the local file and the bottom part, underneath ======, refers to the remote file. The image below shows what the affected area looks like in the repository:

GitHub conflict editor showing merge conflict markers in a Markdown file. The markers separate differing text from the ian-finalish-pass and main branches.

You just need to edit the text to what you want the final text to be.

Conclusions

As you have seen throughout this lesson, Git and GitHub offer considerable advantages for any digital humanist who is undertaking a complicated, collaborative project.

This tutorial, as comprehensive as it is, has only provided a relatively basic introduction to Git. There are many more commands, as well as functionalities within GitHub, to explore. More information can be found in the Git and GitHub documentation pages. With the basic foundation in this lesson, however, we believe that you will be well equipped to undertaking more advanced projects.

We hope that you enjoy your time with Git!


Footnotes


  1. Note that git checkout is being replaced by git switch, but both can be used interchangeably for this lesson.↩︎

  2. Note: If you wish to uninstall GCM, run the following script: $ /usr/local/share/gcm-core/uninstall.sh↩︎

Jason KellyComment