View on GitHub

COMP 491/492

Dickinson College Computer Science Senior Seminar

Git and GitHub Workflow activity

Introduction

Many H/FOSS projects use GitHub as the repository for their source code. This activity is designed to familiarize you with the common workflow elements for interacting with H/FOSS projects hosted on GitHub. The activity is divided into four parts:

Prerequisites:

General Information:

In the following text, commands to be typed at the terminal are shown in mono-spaced type like this and any parts of those commands that you will need to customize for yourself are shown in angle brackets <like this>. The angle brackets are not part of the command and should be omitted when you fill in the appropriate values.

Each time you reach a stopCheckpointstop, please stop and wait. These are points at which we will discuss subtleties of the operations and ensure that the class is progressing together. If you reach a checkpoint before other groups, check to see if you might be able to provide assistance.

The slides used during the activity are available for later reference. The titles of the slides illustrating each task correspond to underlined sub-section names below.

A GitHub cheatsheet with a short summary of each of the sets of commands that we use for particular tasks is also available.

Part 1 - Before the First Class: Getting Setup

In order to allow us to focus on the more interesting and complicated parts of Git/GitHub in class you are asked to do some parts of this activity now, before the first class period. You’ll also be asked after this class to do some additional work in preparation for the second class period.

Configuring Git

There are a few settings in the Git configuration that need to be set before we begin. Note that these commands will only need to be issued once on each machine where you use Git. Issue the following commands in a terminal (customizing as appropriate) to setup the Git configuration:

git config --global user.name <GitHubUserName>
git config --global user.email <you@email.org>
git config --global push.default simple
git config --global merge.tool meld
git config --global mergetool.keepBackup false
git config --global credential.helper store

Use the following command to ensure that you have set all of the configurations:

git config --global --list

Note: Setting the property credential.helper to store will cause your GitHub Personal Access Token (PAT - see below) to be stored in the ~/git-credentials file indefinitely. This prevents you having to reenter it. However, the PAT is stored in plain text. If you prefer a more secure approach you should read more about the Git credential.helper and how to interface it with an encrypted keychain.

Forking the Upstream

Any H/FOSS project on which you will work will maintain a repository on a Git compatible site that contains all of the code for the project. We will use GitHub, but a similar process exists for all of the others as well. The project’s repository will be read-only except for the core project team that manages the project. So to work on the code for the project you will create a copy of the repository in your own GitHub account. The process of making this copy is called forking and the repository belonging to the H/FOSS project itself is called the upstream repository.

To fork the Upstream Repository:

  1. Log into your GitHub account
  2. Go to the repository for the project in GitHub
  3. Click on the “Fork” button in the upper right hand corner.
  4. If a pop-up dialog appears, select your GitHub account from those shown.
  5. Look at your repositories in GitHub and verify that you have a fork of the github-issues-sec repository for your section (1 or 2).

Cloning your Origin

The fork of the upstream repository in your GitHub account is called your origin repository. Because this repository is in your GitHub account you will be able to both read and write to this repository. It will provide cloud storage for all of the work you do on the project and will also be the means for contributing code back to the main project through pull requests. To work on the code however, you will clone your origin repository onto your local machine making a copy of it into a local repository.

To clone your origin repository:

  1. From the github-issues-sec1 or sec2 repository click the green “Code” button.
  2. Make sure that the URL shown has your GitHub user name in it.
  3. Click the little clipboard icon to copy your Origin Repository URL.
    • There are several different options here, but we want the URL corresponding to the “HTTPS” method, which is usually the default.
  4. Open a terminal window on your machine.
  5. Navigate to the directory where you want to store your cloned repository.
  6. Use the command: git clone <Origin Repository URL>
  7. Verify that you now have a local repository:
    • You should have a directory named github-issues-sec1/sec1 or sec2 in the current directory.
    • Change into the github-issues-sec1/sec or sec2 directory.
    • Verify that it contains three files (Calculator.java, Instructor.md, README.md).

Setting the Upstream Remote

You now have a local repository which is a clone all of the code in your origin repository, which was forked from the upstream repository. Your origin repository and the upstream repository are both referred to as remote repositories in Git terminology, reflecting that they are not local to your machine. The Git tool on your machine will keep track of the remote repositories (i.e. your origin and the upstream) corresponding to your local repository. This will be useful when working on H/FOSS projects to push changes from your local repository to your origin and to pull changes made by others in the upstream into your local repository. However, at this time your Git tool only knows about your origin remote because that is the repository you cloned. You will need to tell Git where to find the upstream repository.

To set the Upstream Repository:

  1. Change into the github-issues-sec1 or sec2 directory.
  2. Use the command: git remote -v
    • This will list all of the remote repositories that Git knows about. At this time it should list only your origin repository (2 lines, one for fetch and one for push). Verify that the URL for the origin contains your GitHub username in the URL. So something similar to (with your username and the correct section number):
      origin https://github.com/braughtstu/github-issues-sec1.git (fetch)
      origin https://github.com/braughtstu/github-issues-sec1.git (push)
      

      If it does not and instead contains the course GitHub URL from which you forked the repository you will need to delete the github-issues-sec1 or sec2 directory and repeat the “cloning your origin” step above before continuing.

  3. Go to the upstream repository on GitHub.
  4. Use the “Code” button to copy the upstream repository URL (again choosing the HTTPS option).
  5. Use the command: git remote add upstream <Upstream Repository URL>
  6. Use the command: git remote -v
    • You should now see four lines (2 for origin and 2 for upstream). The lines for upstream should contain the course GitHub URL and those for origin should contain your GitHub username. So something like (with your username and the correct section number):
      origin https://github.com/braughtstu/github-issues-sec1.git (fetch)
      origin https://github.com/braughtstu/github-issues-sec1.git (push)
      upstream https://github.com/Dickinson-COMP-491-492/github-issues-sec1.git (fetch)
      upstream https://github.com/Dickinson-COMP-491-492/github-issues-sec1.git (push)
      

Getting a Personal Access Token (PAT)

GitHub will allow you to log in through the browser using your password. However, when issuing commands thorugh the git command line interface (CLI) GitHub requires that you use a Personal Access Token (PAT).

If you do not already have a GitHub PAT, please use the following instructions to obtain one.

Follow the step-by-step instructions provided there, taking note of the following points which help with our specific scenario:

Storing the Personal Access Token (PAT)

Your PAT now serves the purpose of a password for accessing your GitHub repository through the git CLI. Whenever you are asked for a “password” to access a repository, you should instead use this PAT. Your GitHub account password is still used for accessing GitHub from a web browser.

To store your PAT:

  1. Change into the git-hub-issues-sec1 or sec2 directory
  2. Use the command git push origin main
  3. Enter your GitHub username and PAT when prompted
  4. If you are successful you will see the message Everything up-to-date. If you do not get that message try the steps for “Getting a Personal Access Token” again.

Once successful, your GitHub username and PAT will be stored by Git so that you do not have to enter them for every operation. To confirm this try the command in step 2 above again. You should receive the Everything up-to-date message without entering your username or PAT.

Snapshot

Make a snapshot of your virtual machine.

stopCheckpointstop

Part 2 - Hands-On in the First Class: Branching Workflow

In a common GitHub workflow you will, as you have done, fork a repository (the upstream repo) into your own GitHub account (your origin repo) and then clone that to your local machine (your local repo). At that point your local repo, your origin repo and the upstream repo will all be identical. In addition, each of these repositories will have a main branch, which contains the current reference version of the code according to the upstream repository. The main branch may in fact be called something else, such as “master” or “trunk”.

In practice you will never edit code on the main branch. Instead, each time you want to work on the code (add a feature, fix a bug, etc.) you will create a new feature branch and edit the code there. The feature branch is effectively a full copy of the main branch, but Git is smart enough to just store the changes you make without actually making a full copy. In other words, it really just keeps track of the changes that would have to be made to get from the main branch to the version in your feature branch. By editing code on your feature branch, you will be able to leave the main branch in a state that is consistent with the upstream repository. This will be very helpful in integrating changes made by others into your local and origin repositories. It also has the advantage of making it easy to discard changes if you decide they are not working out.

As you work on a feature or bug fix, you’ll commit changes to the feature branch in your local repo. When you’ve finished work on the feature or bug fix, you’ll push your feature branch to your origin and then issue a pull request for that feature branch to the upstream. The manager of the upstream can then merge your changes into the project (often after requesting some additional modifications).

The following steps walk you through the basic process of creating and working on a feature branch, committing changes to your local repository, pushing a feature branch to your origin, creating a pull request, and finally bringing your local and origin main branches back into sync with the upstream.

Claim An Issue

Go to the Issues tab on the upstream repository in GitHub, and browse the “Round1” bugs. Comment on one of the “Round1” tickets that you want to fix. Refresh the page and check if you are the first commenter on the ticket. If you are, then the bug is yours. If you are not the first commenter on the issue, try another until you are!

stopCheckpointstop

Branch and Fix

As described above you should never edit code on the main branch. So any time you plan to make changes to the code you will need to create a new feature branch.

To create a feature branch:

  1. Open a terminal on your machine.
  2. Change into the directory containing your local repository.
  3. git branch
    • This will list all of the branches in your local repository. Currently there is only a main branch. Also notice that a * appears beside main. This indicates that the files on which you are working are those as they exist in the main branch.
  4. git branch round1fix
    • This creates a new branch named round1fix in your local repository.
  5. git branch
    • Notice the difference.
  6. git switch round1fix
    • This will switch you to the round1fix branch and allow you to edit the files on that branch, ensuring that the files in the main branch are unchanged and remain consistent with the upstream.
  7. git branch
    • Notice the difference.

Now edit the Calculator.java file to make the fix described in the issue that you claimed. Be sure not to edit any other code in the class.

gedit Calculator.java

If time permits you should also be sure that your modified code compiles:

javac Calculator.java

stopCheckpointstop

Status, Stage and Commit

When you edit a file, you are changing its contents—but not the copy that is in your local repository. To make your changes part of the local repository you will commit the changes to the repository. There are several steps to this process. To get started: (i) determine which files have been changed; (ii) add some or all of these changed files to the staging area; and (iii) confirm that they have been added. The commands are:

git status
git add <FileToAdd>
git status

Once the files are in the staging area they can be committed to the repository. Each commit should reflect a complete unit of work and the commit should have a clear message indicating what that work was.

Commit your changes to your local repository and verify they have been committed:

git commit -m "<a descriptive message here>"
git status

You can also see a list of the commits that have been made to the repository:

git log

Within the log program, if there is more than one screen of output:

To get a little more feel for the way your local repository behaves, do the following:

  1. cat Calculator.java
    • Is your change there?
  2. git switch main
  3. git branch
  4. cat Calculator.java
    • Is your change there? Why not?
  5. git switch round1fix
  6. git branch
  7. cat Calculator.java
    • Is your change there?

stopCheckpointstop

Push Branch to Origin

The changes you have made are now in your local repository with a nice descriptive commit message. To contribute those changes to the original project you’ll need to first push your feature branch from your local repository to your origin.

Push your branch to origin:

git push origin round1fix

Note: If you are asked for your GitHub username and password, use your PAT and not your password.

Check that your feature branch and changes have been pushed to your origin by looking at the github-issues-sec1/sec2 repository in your GitHub account. You should verify several things:

stopCheckpointstop

Making a Pull Request

A pull request is the mechanism by which code changes that you have made can be contributed back to a project. More specifically, it is a request from you to a project, made through GitHub. The request asks the project managers to consider incorporating the changes you’ve made in your fork of their project (i.e. your origin) into their repository (i.e. the main branch of the upstream).

Make a pull request for your round1fix branch:

  1. Open your github-issues-sec1/sec2 repository on GitHub.
  2. Create a new pull request. There are multiple ways to do this within the GitHub interface:
    • The green “Compare & pull request” button may be visible if your change was made recently. Click it if you see it.
    • Otherwise, navigate to the branch whose changes you would like to submit. In our case, this is round1fix.
      • Look for a drop-down menu labeled “contribute”. This is probably just below and to the left of the green “Code” button.
      • From the drop-down menu, click on the green “Open pull request” button.
  3. Check that the “base repository” and “base” branch are set to the main branch of the upstream.
    • This is always the branch where you want your changes to go. In this case, we want the main branch of the Dickinson-COMP-491-492/github-issues-sec1 or sec2 repository.
  4. Set the “head repository” and “compare” branch to the round1fix branch in your own fork of github-issues-sec1 or sec2.
    • This is always the branch containing the changes that you want to integrate into the upstream.
  5. Scroll down and verify that the pull request contains the changes that you have made:
    • Your commit message should be listed.
    • The changes you made to the Calculator.java file should be shown.
  6. Edit the title and the “Leave a comment” box to give information about the changes you are proposing.
    • Use this to explain to the project managers what you have done and why. In particular, because this work resolves an issue from the issue tracker, the issue should be referenced in the comment. Include the text “Fixes #bugid” (e.g. Fixes #123) to indicate which bug your changes have fixed. We’ll see later that this will allow the issue to be automatically closed if/when the project managers merge your pull request into the upstream main.
  7. Click the green “Create pull request” button.

Once you create the pull request it will be reviewed by the project managers. If everything looks good they will merge the changes into the main branch of the project. The more likely scenario is that they will comment on your pull request with some additional things to consider or other changes to make. When you do that you can then update your pull request by pushing the branch to your origin again. They will then review the pull request again. This cycle may repeat several times before your proposed changes are merged into the project.

stopCheckpointstop

Synchronize Local and Origin with Upstream

Once your pull request, and everyone else’s too, has been merged into the upstream main, the main branch of your local and origin repositories will be out of sync with (i.e. different than) the upstream main. This will occur frequently in any project where there are multiple developers contributing. When this happens you will need to synchronize your repositories with the upstream repository. This is done by pulling the changes from the upstream main and merging them into to the main branch of your local repo and then finally pushing them to your origin. Because you have been careful to never edit the code in the main branch of your local repo this is an easy process.

Verify that your local main is out of sync with the upstream:

  1. git switch main
  2. git branch
    • Ensure that you are on the main branch.
  3. cat Calculator.java
  4. Compare what you see in the terminal to the version of Calculator.java that is in the upstream repository on GitHub.
    • Your local version should contain no changes, not even yours. The upstream should contain everyone’s changes, including yours.

Synchronize your main branches with the upstream main:

  1. git switch main
  2. git branch
    • Ensure that you are on the main branch.
  3. git pull upstream main
  4. cat Calculator.java
  5. Check that what you see now in the terminal is identical to the version of Calculator.java that is in the upstream repository on GitHub.
  6. Verify that the changes do not yet exist in the version of Calculator.java in your origin on GitHub.
  7. git push origin main
  8. Check that the version of Calculator.java in your origin on GitHub now contains the changes too, and thus your local and origin main branches are now in sync with the upstream.

stopCheckpointstop

Delete a Feature Branch

Once the changes made in a feature branch have been merged into the upstream there is no need to keep the feature branch around. Thus, the feature branch can be deleted from your local and origin repositories.

Delete the feature branch from your local and origin:

  1. git branch -d round1fix
  2. git branch
    • Verify that the round1fix branch is no longer in your local repo.
  3. Notice that the round1fix branch still exists in your origin on your GitHub.
  4. git push -d origin round1fix
    • Reload your GitHub origin repository page in the browser and verify that the round1fix branch has been deleted.

stopCheckpointstop

Part 3 - Before the Second Class: Making Another PR

Thus far, the changes you have made and integrated into the project have been carefully selected so that no change you made conflicted with a change made by someone else. This meant that your feature branch was able to be merged into the upstream main automatically. In practice, however, it is quite likely that when multiple people are changing code concurrently they will make changes that are in conflict with each other. Complete the steps below before the next class. These steps will have you make a change that will generate conflicts and we will then walk through the process of resolving those conflicts in the next class.

Fix A Round2 Issue

The steps below are the same steps you used in fixing your “Round1” bug and making a pull request. So, you can refer to the steps above or use the Git/GitHub Cheatsheet to refresh your memory on the commands required.

  1. Go to the Issues tab on the upstream repository on GitHub, and browse the “Round2” bugs. Comment on one of the “Round2” tickets that you want to fix. Refresh the page and check if you are the first commenter on the ticket. If you are, then the bug is yours. If you are not the first commenter on the issue try another until you are!
  2. git branch round2fix
  3. Edit the code in your round2fix branch to address the issue that you have claimed.
  4. git status
  5. git add Calculator.java
  6. git commit -m "<Descriptive commit message>"
  7. git push origin round2fix
  8. Make a pull request to the upstream for your feature branch. (Note: GitHub may tell you that it “Can’t automatically merge” your pull request. That’s expected, go ahead and create it anyway.)

stopCheckpointstop

Part 4 - Hands-On in the Second Class: Resolving Conflicts

Before this class you made changes that fixed a “Round2” issue and generated a pull request to the upstream to merge your results. In practice, while you are working on your fix other contributors will make pull requests and their work will be merged into the upstream. To simulate this, the instructor has made changes to the upstream. These changes have been designed to conflict (i.e. have changes in the same line of code, or changes that cannot be merged automatically). You may have noticed when you created your pull request that GitHub indicated (in red) that it “Can’t automatically merge” your pull request. This is GitHub’s way of letting you and the upstream project managers know that there is a conflict between the code in the upstream and the edits in your pull request.

When a branch cannot be merged automatically, the person issuing the pull request will typically be asked to merge the changes from the current upstream main into their feature branch. During this process you will have to manually resolve the conflicts that exist, by deciding what the conflicting code should look like in each case. Once the conflicts are resolved, the pull request is updated by pushing your edited feature branch to your origin. The upstream manager will then be able to merge your updated pull request into the upstream.

Synchronize Local and Origin with Upstream main:

  1. git switch main
  2. git pull upstream main
  3. git push

stopCheckpointstop

Merge main Branch Changes into your Feature Branch:

  1. git switch round2fix
  2. git merge main
    • Notice that the merge will fail due to conflicts. Git needs your help to manually resolve the conflicts. You can see the conflicts if you look inside the Calculator.java file.

Examine the Conflicts

  1. cat Calculator.java
  2. Scroll to where you made your edits. You should see something similar to:
    <<<<<<< HEAD
     public double sphereVolume(double r) {
    =======
     public static double sphVol(double r) {
    >>>>>>> main
    

This is Git’s way of indicating what has happened. The main branch had been edited to make the sphVol method static. But the local branch (HEAD) had been edited to rename the sphVol method to sphereVolume, as requested in the issue ticket. Because these changes are in the same line Git is unable to automatically merge them. You could resolve the conflict by manually editing the file and removing the information about the conflict leaving only the desired code. But it is often easier, more convenient and less error prone to use a graphical merge tool as you will below.

stopCheckpointstop

Resolve the Conflicts:

  1. Launch the graphical merge tool (Meld):
    • git mergetool
      • The left pane (LOCAL) shows the code in your feature branch. This includes the edits that you have included in your pull request.
      • The right pane (REMOTE) shows the code from the current main branch in your local repository. This is also the code as it exists in the upstream since you just synced.
      • The middle pane shows the file as it existed at the time your feature branch was created. Thus, it does not contain any of the changes to the main branch or any of the changes in your feature branch. This is also where you will indicate what the file should look like with all of the conflicts resolved.
      • Code highlighted in blue are changes in the main branch that do not conflict with changes in your feature branch. These need to be moved into your feature branch to bring it into sync with the main branch.
      • Code highlighted in red are changes in the main branch that conflict with changes in your feature branch. You will need to resolve these conflicts.
  2. Use the arrow buttons to move the blue highlighted lines from the main branch into the center pane. This is you accepting the non-conflicting upstream changes into your feature branch.
  3. Edit the code in the center panel to resolve the remaining conflict. (i.e. the method should have your new name and also be static).
  4. Save the changes in the merge tool. Note that, when you save, it is the code in the center pane that is saved.
  5. Exit the mergetool.
  6. Use cat Calculator.java and verify that the merged file now appears as desired.

stopCheckpointstop

Commit and Push (Update Pull Request):

  1. git status
  2. git commit -m "merged upstream changes"
  3. git push origin round2fix
    • Now check your pull request on GitHub. It will have been updated to have your merged changes and will also indicate that it can now be automatically merged.

stopCheckpointstop

Synchronize Local and Origin main branches with Upstream main:

  1. git switch main
  2. git pull upstream main
  3. git push origin main

Delete the feature branch:

  1. git branch -d round2fix
  2. git push -d origin round2fix

stopCheckpointstop


Acknowledgements: This assignment builds from and adapts ideas and content from the following activities created by others: