Skip to content

Project 5 ​

UYNoWork

Overview ​

In this lab, you are tasked with taking an existing data structure that is not thread safe and wrapping it to use in a multi-threaded environment. In industry you may be tasked with modifying legacy code to work in a multi-threaded environment so this is a critical skill to master while in school πŸ˜ƒ.

You are given an existing implementation of merge sort. This implementation is derived directly from the book "Introduction to Algorithms" by Cormen, Leiserson, Rivest, and Stein. Your job is to wrap this algorithm so you can use it in a multi-threaded environment.

Learning Outcomes ​

  • 1.4 Apply computer science theory and software development fundamentals to produce computing-based solutions. (ABET Outcome 6)
  • 1.5 Use simple shell scripts and system tools to analyze process behavior
  • 3.1 Analyze a complex computing problem and apply principles of computing and other relevant disciplines to identify solutions. (ABET Outcome 1)
  • 3 Construct applications that utilize processes, threads, and synchronization primitives to solve problems requiring concurrent or parallel computation
  • 3.2 Explore the effects of multiple threads operating on the same buffer

Grading Rubric ​

Make sure and review the class grading rubric so you know how your project will be graded.

Task 1 - Setup ​

Follow the steps below to get your repository all setup and ready to use. The steps below show you how to use and setup GitHub codespaces. You are not required to use codespaces, all the steps below can be completed in the CS Lab or on your personal machine if you prefer.

Fork the starter repository ​

  1. Fork the starter repository into your personal GitHub account: https://github.com/shanep/makefile-project-starter

fork repo

  1. In the new fork name the repository cs452-p5

fork name

Start a new Codespace ​

We will use GitHub Codespaces to do most of our coding. Codespaces is just VSCode in the cloud. This makes it really easy to setup a developer environment and code from any computer that has a browser and internet connection!

Start Codespace

  1. If you are asked to install recommended extensions click "install". You may not be asked to install extensions if you are already syncing your account.

Codespace extensions

You now should have a new repository (forked from the starter template) that is ready to use.

Task 2 - Prepare your repository ​

The starter repository is a bare bones template that you will need to update with the starter code below.

src/lab.h ​

c
#ifndef LAB_H
#define LAB_H
#include <pthread.h>

#ifdef __cplusplus
extern "C"
{
#endif

  // The threshold that we will use to switch to insertion sort, make sure that
  // you use test arrays bigger than 5 so you are testing the merge sort
#define INSERTION_SORT_THRESHOLD 2
#define MAX_THREADS 32
  /**
   * @brief Sorts an array of ints into ascending order using the constant
   * INSERTION_SORT_THRESHOLD internally
   *
   * @param A A pointer to the start of the array
   * @param p The starting index
   * @param r The ending index
   */
  void mergesort_s(int *A, int p, int r);

  /**
   * @brief Merge two sorted sequences A[p..q] and A[q+1..r] and place merged
   *              output back in array A. Uses extra space proportional to
   *              A[p..r].
   *
   * @param A The array to merge into
   * @param p The starting index of the first half
   * @param q The middle
   * @param r The ending index of the second half
   */
  void merge_s(int A[], int p, int q, int r);

  /**
   * @brief Sorts an array of ints into ascending order using multiple
   * threads
   *
   * @param A A pointer to the start of the array
   * @param n The size of the array
   * @param num_threads The number of threads to use.
   */
  void mergesort_mt(int *A, int n, int num_thread);

  /**
   * @brief retuns the current time as milliseconds
   * @return the number of milliseconds
   */
  double getMilliSeconds();

  /**
   * @brief Represents a chunk of the array to be sorted by a thread
   *
   */
  struct parallel_args
  {
    int *A;
    int start;
    int end;
    pthread_t tid;
  };

  /**
   * @brief The function that is called by each thread to sort their chunk
   *
   * @param args see struct parallel_args
   * @return void* always NULL
   */
  void *parallel_mergesort(void *args);

  /**
   * @brief Entry point for the main function
   *
   * @param argc The argument count
   * @param argv The argument array
   * @return The exit code
   */
  int myMain(int argc, char **argv);

#ifdef __cplusplus
} // extern "C"
#endif

#endif

app/main.c ​

c
#include <stdio.h>
#include <stdlib.h>
#include "../src/lab.h"



int main(int argc, char **argv)
{

  if (argc < 3)
    {
      printf("usage: %s <array_size> <num_threads>", argv[0]);
      return 1;
    }
  int size = atoi(argv[1]);
  int t = atoi(argv[2]);

  int *A_ = malloc(sizeof(int) *size);
  srandom(1);
  for (int i = 0; i < size; i++)
    A_[i] = random() % 100000;

  double end = 0;
  double start = getMilliSeconds();
  mergesort_mt(A_, size, t);
  end = getMilliSeconds();
  printf("%f %d\n",end-start, t);

  return 0;
}

app/main.c ​

c
#include <stdlib.h>
#include <sys/time.h> /* for gettimeofday system call */
#include "lab.h"

/**
 * @brief Standard insertion sort that is faster than merge sort for small array's
 *
 * @param A The array to sort
 * @param p The starting index
 * @param r The ending index
 */
static void insertion_sort(int A[], int p, int r)
{
  int j;

  for (j = p + 1; j <= r; j++)
    {
      int key = A[j];
      int i = j - 1;
      while ((i > p - 1) && (A[i] > key))
        {
	  A[i + 1] = A[i];
	  i--;
        }
      A[i + 1] = key;
    }
}


void mergesort_s(int A[], int p, int r)
{
  if (r - p + 1 <=  INSERTION_SORT_THRESHOLD)
    {
      insertion_sort(A, p, r);
    }
  else
    {
      int q = (p + r) / 2;
      mergesort_s(A, p, q);
      mergesort_s(A, q + 1, r);
      merge_s(A, p, q, r);
    }

}

void merge_s(int A[], int p, int q, int r)
{
  int *B = (int *)malloc(sizeof(int) * (r - p + 1));

  int i = p;
  int j = q + 1;
  int k = 0;
  int l;

  /* as long as both lists have unexamined elements */
  /*  this loop keeps executing. */
  while ((i <= q) && (j <= r))
    {
      if (A[i] < A[j])
        {
	  B[k] = A[i];
	  i++;
        }
      else
        {
	  B[k] = A[j];
	  j++;
        }
      k++;
    }

  /* now only at most one list has unprocessed elements. */
  if (i <= q)
    {
      /* copy remaining elements from the first list */
      for (l = i; l <= q; l++)
        {
	  B[k] = A[l];
	  k++;
        }
    }
  else
    {
      /* copy remaining elements from the second list */
      for (l = j; l <= r; l++)
        {
	  B[k] = A[l];
	  k++;
        }
    }

  /* copy merged output from array B back to array A */
  k = 0;
  for (l = p; l <= r; l++)
    {
      A[l] = B[k];
      k++;
    }

  free(B);
}

double getMilliSeconds()
{
  struct timeval now;
  gettimeofday(&now, (struct timezone *)0);
  return (double)now.tv_sec * 1000.0 + now.tv_usec / 1000.0;
}

tests/test-lab.c ​

c
#include "harness/unity.h"
#include "../src/lab.h"
#include <stdlib.h>


static int defaultRange = 10;
static int defaultSize = 103;
static int defaultSeed = 1;
static int *expected = NULL;
static int *actual = NULL;

void setUp(void) {
  actual = malloc(sizeof(int) *defaultSize);
  expected = malloc(sizeof(int) *defaultSize);
  srandom(defaultSeed);
  for (int i = 0; i < defaultSize; i++){
    int value = random() % defaultRange;
    actual[i]= value;
    expected[i] =value;
  }
}

void tearDown(void) {
  free(actual);
  free(expected);
}

int compare_ints(const void* a, const void* b)
{
    int arg1 = *(const int*)a;
    int arg2 = *(const int*)b;

    if (arg1 < arg2) return -1;
    if (arg1 > arg2) return 1;
    return 0;
 }

void test_mergesort_mt_small_one_thread(void)
{
  //Run our sort and the C library qsort to compare to
  mergesort_mt(actual, defaultSize, 1);
  qsort(expected, defaultSize, sizeof(int), compare_ints);
  TEST_ASSERT_EQUAL_INT32_ARRAY(expected, actual, defaultSize);
}

void test_mergesort_mt_small_two_threads(void)
{
  mergesort_mt(actual, defaultSize, 2);
  qsort(expected, defaultSize, sizeof(int), compare_ints);
  TEST_ASSERT_EQUAL_INT32_ARRAY(expected, actual, defaultSize);
}

void test_mergesort_mt_small_three_threads(void)
{
  mergesort_mt(actual, defaultSize, 3);
  qsort(expected, defaultSize, sizeof(int), compare_ints);
  TEST_ASSERT_EQUAL_INT32_ARRAY(expected, actual, defaultSize);
}


int main(void) {
  UNITY_BEGIN();
  RUN_TEST(test_mergesort_mt_small_one_thread);
  RUN_TEST(test_mergesort_mt_small_two_threads);
  RUN_TEST(test_mergesort_mt_small_three_threads);
  return UNITY_END();
}

Once you have updated all the starter code lets make your first commit so everything is saved. Open up a terminal and lets make a commit!

bash
git add --all
git commit -m "Added in starter code"

Task 3 - Make Thread safe (ABET Outcome 6) ​

Read through the files src/lab.h and src/lab.cpp in the project to make sure you understand the algorithm. You are not allowed to modify the single threaded implementation in you must implement the function mergesort_mt. Your implementation needs to split up the array into equal chunks and then hand each chunk to a new thread. You will then use pthread_join to wait for all the chucks to be sorted at which point you will need to merge the results together. You will not need to use locks for this approach because you are taking a large array and splitting it up between threads so each thread only operates on its section of the larger array. C pointers come in really handy for this approach because you can keep the original array in place and just declare new pointers into separate chunks of the existing array.

Task 4 - Driver app ​

Once you have completed writing the multi-threaded version of the code you will need to test your code with a simple command line driver that will take two command line arguments and output the number of threads used and the total time to sort the array as shown in the example below. You should see a speed up when you increase the number of threads used to sort the array. However, if you add too many threads you will see a slow down because of the overhead of context switching between threads. You will need to experiment with the number of threads to see what the optimal number is for your machine and document that in your Retrospective.md file.

c
usage: ./myprogram <array_size> <num_threads>
$ ./myprogram 1000 2
0.0820312 2
$ ./myprogram 1000 3
0.104004 3

Task 5 - Add bash files ​

Below is an example bash script and a gnuplot macro file that you will need to use to generate a nice plot showing the speedup when using multiple threads. You will need to install gnuplot on your machine to complete this task. The lab machines in the CS department already have gnuplot installed. You can install gnuplot on codespaces using sudo apt-get install -y gnuplot.

You will need to modify the script to use in your project! The script will NOT work as is. Part of this project is to learn how to modify scripts to automate tasks.

bash
#!/usr/bin/env bash
#NOTE!!! THIS WILL NOT WORK IF YOU JUST COPY AND PASTE IT INTO YOUR PROJECT
#YOU WILL NEED TO MODIFY IT TO WORK WITH YOUR PROJECT
function usage() {
    echo "$0 usage:" && grep " .)\ #" $0
    exit 0
}
[ $# -eq 0 ] && usage
while getopts "hs:f:" arg; do
    case $arg in
    s) # The size of the array to sort.
        size=${OPTARG}
        ;;
    f) # The plot file name
        name=${OPTARG}
        ;;
    h | *) # Display help.
        usage
        exit 0
        ;;
    esac
done
if [ "$name" == "" ] || [ "$size" == "" ]
then
        usage
        exit 0
fi
if [ -e ./build/myprogram ]; then
    if [ -e "data.dat" ]; then
        rm -f data.dat
    fi
    echo "Running myprogram to generate data"
    echo "#Time Threads" >> data.dat
    for n in {1..32}; do
        echo -ne "running $n thread \r"
        ./build/myprogram "$size" "$n" >> data.dat
    done
    gnuplot -e "filename='$name.png'" graph.plt
    echo "Created plot $name.png from data.dat file"
else
    echo "myprogram is not present in the build directory. Did you compile your code?"
fi
text
# Gnuplot script file for plotting data in file "data.dat"
set   autoscale                        # scale axes automatically
unset log                              # remove any log-scaling
unset label                            # remove any previous labels
set title "Time to sort vs number of threads"
set xlabel "Time to sort (milliseconds)"
set ylabel "Number of Threads"
set style data linespoints
set term png
set output filename
plot "data.dat" t "time to sort"

Assuming that you named your script createplot.sh and the gnuplot file graph.plt you can now generate your plot as follows:

bash
$ ./createplot.sh -s 10000000 -f student_plot
Running myprogram to generate data
Created plot student_plot.png from data.dat file

After the script finishes you should see a new file named student_plot.png. We can see in the example below the optimum number of threads is 5 on my M1 Mac. Your results will vary depending on your hardware and current system load.

example plot

Task 6 - Complete the Analysis (ABET Outcome 1) ​

Once you have completed all the tasks craete a file named Analysis.md and add a new section named ## Analysis to the bottom of the document. In this section you will need to include your graph as an image so it will display when viewed on github.com. You will then need to talk about your results. Your discussion should be at least 500 words.

  • Were you able to generate something close to what the example showed? Why or why not.
  • Did you see a slow down at some point why or why not?
  • Did your program run faster and faster when you added more threads? Why or why not?
  • What was the optimum number of threads for your machine?
  • What was the slowest number of threads for your machine?

If your graph does not look like the example graph you will need to explain why, maybe go back and look at your original implementation, did you make a mistake somewhere? If you found a bug in your original implementation please note that and explain what you fixed πŸ˜ƒ.

Additional Resources ​

When you are working on multi-threaded programs it is pretty easy to get into a deadlocked scenario. Debugging multi-threaded programs can be difficult because running your program under the debugger can change the timing of your code and thus make the issue go away. So sometimes the only way to see where your program is stuck is to attach a debugger post mortem and dump out the threads.

So assuming you have a program that you ran and it is currently stuck somewhere here are the steps to at least see where it is stuck which can help you fix the issue!

Get the PID of the process ​

Before you attach a debugger you want to confirm that your program is completely stopped and you want to get the pid of the program so you can attach to it. Open up a second terminal and run the command ps ux and look for your program. If you can’t find your program you can try ps aux which will output all users. If you still cant find it make sure that your program hasn’t exited. NOTE: You can not attach to another users program unless you have access to the root account, you can only attach to your programs.

In the example below you can see the program ./mylab -c 3 -p 8 -i 100 -s 5 with PID 254167 is what we want to debug.

c
$ ps ux
USER        PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
shanepa+  46764  0.0  0.0 1178788    0 ?        S     2020   0:00 /bin/bash
shanepa+ 129368  0.0  0.0 182776  2632 ?        S    10:02   0:01 sshd: shanepanter@pts/32
shanepa+ 129377  0.0  0.0 127848  3864 pts/32   Ss   10:02   0:02 -bash
shanepa+ 154093  0.0  0.0 1178792    0 ?        S     2020   0:00 /bin/bash
shanepa+ 240623  0.0  0.0 1178788    0 ?        S     2020   0:00 /bin/bash
shanepa+ 254167  0.1  0.0 590304   828 pts/32   Sl+  11:36   0:00 ./mylab -c 3 -p 8 -i 100 -s 5
shanepa+ 254357  0.0  0.0 182776  2472 ?        S    11:36   0:00 sshd: shanepanter@pts/6
shanepa+ 254358  0.5  0.0 127716  3536 pts/6    Ss   11:36   0:00 -bash
shanepa+ 254514  0.0  0.0 166156  2392 pts/6    R+   11:36   0:00 ps ux

Attach to the process ​

In the same terminal that you ran the ps ux command start gdb and attach to the process with the PID that you found in step 1. Once you have attached to the process issue the command thread apply all bt where bt stands for backtrace applied to all the current threads.

The example below shows that we have 4 threads. Thread 1 is the main thread and we can see that it is waiting on a pthread_join which is expected so we don’t need to worry about Thread 1. Thread 2, 3, and 4 are all blocked on a condition variable and we can see in the stack trace they are all stuck in the dequeue function.

bash
$ sudo gdb
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-120.el7
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
(gdb) attach 254167
Attaching to process 254167
Reading symbols from /home/ShanePanter/repos/shanep.github.io/os/labs/lab2/_solution/mylab...done.
Reading symbols from /lib64/libpthread.so.0...(no debugging symbols found)...done.
[New LWP 254178]
[New LWP 254177]
[New LWP 254176]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
Loaded symbols for /lib64/libpthread.so.0
Reading symbols from /lib64/libc.so.6...(no debugging symbols found)...done.
Loaded symbols for /lib64/libc.so.6
Reading symbols from /lib64/ld-linux-x86-64.so.2...(no debugging symbols found)...done.
Loaded symbols for /lib64/ld-linux-x86-64.so.2
Reading symbols from /lib64/libgcc_s.so.1...(no debugging symbols found)...done.
Loaded symbols for /lib64/libgcc_s.so.1
0x00007f067f11e017 in pthread_join () from /lib64/libpthread.so.0
Missing separate debuginfos, use: debuginfo-install glibc-2.17-317.el7.x86_64 libgcc-4.8.5-44.el7.x86_64
(gdb) thread apply all bt
Thread 4 (Thread 0x7f06767fc700 (LWP 254176)):
#0  0x00007f067f120a35 in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
#1  0x00000000004014a0 in dequeue (q=0x2066010) at queue.c:97
#2  0x0000000000400e55 in consumer (args=0x0) at lab.c:76
#3  0x00007f067f11cea5 in start_thread () from /lib64/libpthread.so.0
#4  0x00007f067ee4596d in clone () from /lib64/libc.so.6
Thread 3 (Thread 0x7f0675ffb700 (LWP 254177)):
#0  0x00007f067f120a35 in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
#1  0x00000000004014a0 in dequeue (q=0x2066010) at queue.c:97
#2  0x0000000000400e55 in consumer (args=0x0) at lab.c:76
#3  0x00007f067f11cea5 in start_thread () from /lib64/libpthread.so.0
#4  0x00007f067ee4596d in clone () from /lib64/libc.so.6
Thread 2 (Thread 0x7f06757fa700 (LWP 254178)):
#0  0x00007f067f120a35 in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
#1  0x00000000004014a0 in dequeue (q=0x2066010) at queue.c:97
#2  0x0000000000400e55 in consumer (args=0x0) at lab.c:76
#3  0x00007f067f11cea5 in start_thread () from /lib64/libpthread.so.0
#4  0x00007f067ee4596d in clone () from /lib64/libc.so.6
Thread 1 (Thread 0x7f067f534740 (LWP 254167)):
#0  0x00007f067f11e017 in pthread_join () from /lib64/libpthread.so.0
#1  0x0000000000401151 in main (argc=9, argv=0x7fff5b567418) at lab.c:176
(gdb)

Debug it ​

Now that we at least know where our process is stuck we can exam the code and come up with all the ways that this scenario could occur. Be aware that the issue may not be in the dequeue function! More likely than not the bug is somewhere else entirely. All this does is give you some insight into where your program is getting stuck NOT where the problem is!

Final Task - Submit your code ​

Now that you have completed all the tasks the only thing left to do is to submit your code as a patch so you can receive a grade for all your hard work.

Turn on Two Factor ​

Turn on Two factor authentication for your Boise State provided email account

DANGER

Do NOT skip this step. Boise State University uses Gmail as their email provider and Gmail requires you to use two factor authentication in order to generate an app password.

You need to use your Boise State University issued email account to send the email and you must have two factor authentication turned on.

Generate an app password ​

INFO

If you are working in GitHub codespaces you will need to setup SMTP for EACH project. This is because each codespace is tied to a specific repository and the settings are not shared. However, if you are working on your own personal machine or in the CS Lab then you will only have to setup SMTP once!

In order to use git send-email you will need to generate an app password. Navigate to https://security.google.com/settings/security/apppasswords and generate a new app password. Make sure and copy the password before you close the window because you will not be able to see it again.

generate app password

I can't generate a password

If you get the error shown below it typically means that you have not enabled two factor authentication. Follow these steps to resolve the issue:

  1. Go back and ensure you have two factor authentication enabled.
  2. Log out of your Gmail account
  3. Log back into your Gmail account and make sure you did have to use two factor authentication
  4. If the steps above fail then open an incognito tab and go back to the first step
  5. If you still have issues reboot your machine and go back to the first step

app password error

Setup SMTP ​

  1. Open up a terminal in codespaces

Open Terminal

  1. In the terminal type git config --global --edit and modify the file with the info listed below. You will need to change the info listed below to match your own name, email and App Password that you generated in the previous step.
text
[User]
	name =  YOUR NAME
	email = YOURNAME@u.boisestate.edu
[sendemail]
	smtpserver = smtp.gmail.com
	smtpuser = YOURNAME@u.boisestate.edu
	smtpPass = xxxx xxxx xxxx xxxx
	smtpencryption = ssl
	smtpserverport = 465

Edit config

Congrats you should be all setup to send code patch's over email. Now lets create a patch!

Create a patch file ​

We are now going to do what is called a squash merge and then create a patch file with all our changes in one commit.

  1. First lets fetch the upstream branch. This is the branch that you originally forked from at the start of the project
bash
git fetch upstream
  1. Checkout a new branch named submit from the upstream/master branch.
bash
git checkout upstream/master -b submit
  1. Now we will do a squash merge all the commits we did onto our new submit branch.
bash
git merge --squash master
  1. Now commit those new changes to your submit branch
bash
git commit -m "Submit project"
  1. Push your submit branch to GitHub
bash
git push -u origin
  1. Open up a web browser and navigate to your repository on GitHub and confirm that your submit branch is correctly pushed. Open up the files and make sure that everything looks good before going to the next step.

submit-branch

Install Libraries ​

If you are working on codespaces you will need to install the required dependencies before you attempt to email out your patch file.

bash
make install-deps

Email Patch File ​

First make sure you are still on the submit branch that you created. If you type git branch you should see a star next to the submit branch that indicates you are currently on the submit branch.

bash
$ git branch
  master
* submit

WARNING

You MUST test your patch by emailing it to yourself first! You will go through all the steps below with your own email. After you have sent the email to yourself and tested it you can then email your submission to the class mailing list!

Finally we can create our patch to email out!

bash
git send-email --to youremail@u.boisestate.edu HEAD^

You should see results similar to what is show below.

bash
$ git send-email --to youremail@u.boisestate.edu HEAD^
/tmp/T/NWEw4f1sIj/0001-Submit-project-2.patch

From: youremail@u.boisestate.edu
To:  youremail@u.boisestate.edu
Subject: [PATCH] Submit project
Date: Thu,  7 Dec 2023 20:31:55 -0700
Message-Id: <20231208033155.83099-1-shanepanter@boisestate.edu>
X-Mailer: git-send-email 2.39.3 (Apple Git-145)
MIME-Version: 1.0
Content-Transfer-Encoding: 8bit

The Cc list above has been expanded by additional
addresses found in the patch commit message. By default
send-email prompts before sending whenever this occurs.
This behavior is controlled by the sendemail.confirm
configuration setting.

For additional information, run 'git send-email --help'.
To retain the current behavior, but squelch this message,
run 'git config --global sendemail.confirm auto'.

Send this email? ([y]es|[n]o|[e]dit|[q]uit|[a]ll): y
OK. Log says:
Server: smtp.gmail.com
MAIL FROM:  youremail@u.boisestate.edu
RCPT TO:  youremail@u.boisestate.edu
RCPT TO:  youremail@u.boisestate.edu
From: youremail@u.boisestate.edu
To:  youremail@u.boisestate.edu
Subject: [PATCH] Submit project 2
Date: Thu,  7 Dec 2023 20:31:55 -0700
Message-Id: <20231208033155.83099-1-shanepanter@boisestate.edu>
X-Mailer: git-send-email 2.39.3 (Apple Git-145)
MIME-Version: 1.0
Content-Transfer-Encoding: 8bit

Result: 250

You should now be able to check your email and see your patch. Be aware that sometimes email delivery is slightly delayed so you may have to wait a few minutes for it to show up. Make sure and check your spam folder if you don't see any mail.

Test your patch ​

You can now get your patch from Gmail and test it to make sure that everything works and your patch was correct.

  1. Checkout a new branch named test-patch from the upstream/master branch
bash
git checkout upstream/master -b test-patch
  1. Push your new test-patch branch to your repo instead of the upstream
bash
 git push -u origin
  1. Get the patch file from Gmail

download gmail

  1. Copy the email to your clip board

copy to clipboard

  1. Create a new file and paste the contents that you just copied and save it in the root folder in a file named my-patch.txt.

new file

  1. Make sure you saved the file correctly. It should show up in the file explorer as shown below.

created file

  1. Now apply that patch to your new test-patch branch
bash
git am my-patch.txt
  1. Commit your patch
bash
git commit -m "Testing my email patch"
  1. Push your test patch branch to your Github Account
bash
git push
  1. After you have successfully applied the patch you can delete the file my-patch.txt. Don't commit the file my-patch.txt it is just a temporary file that you don't want to save.

  2. Finally open up the browser again and make sure you have 3 branches master, submit, and test-patch. The submit and test-patch branches should be identical. Each should have exactly 1 commit from you with all your changes.

final state

Submit your Patch for grading ​

Assuming you have successfully completed all the steps above with your own email and everything looked good you can now submit your patch for grading.

DANGER

Do not use the class mailing list to test your patch. You should only send an email to email is in the syllabus after you have tested the process with your own email. Spamming the mailing list with excessive patches will result in a lower grade.

When you submit to the mailing list you will automatically be cc'd on the email so you will have a copy in your own email as proof that you completed the assignment.

You are allowed to submit up to 3 times without penalty.

Open a terminal and submit your patch.

git checkout submit
git send-email --to email is in the syllabus HEAD^

Assuming all went well you are now complete! You have created a patch file from a squash merge, emailed it and tested the resulting patch. You are well on your way to becoming an advanced git user!

Submitting ​

You do not need to submit anything to canvas for this assignment. Your email is your submission, your grade will be updated after the due date (and late window) have passed.

Released under the MIT License.