Using SPI and 2 shift registers WITHOUT libraries
My strategy: Write the sketch using the standard Arduino library. The goal is have the LED lit up, as you need. 

Then, change all standard function calls in your sketch (like digitalWrite) with your own (called it digitalWrite2).

Now implement digitalWrite2 using register manipulation.

Pros:


Divide and conquer. You know have two independent smaller problems (LED and registers).
When is time to implement digitalWrite2 you can learn from your classmate how to do that. I bet that they will start with the hard part (register manipulation). 
It makes for a easier debuging. You can compare your own implementation against the standard implementation just by deleting/adding a single "2" in the call.


Of course, taking a look at the Arduino source code will made things trivial :-)
Is that cheating? No, we learn from others, we learn reading other people's code.

Edit: Step by Step Example

Let use Blink to show how to do it:

Original

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(1000);                       // wait for a second
  digitalWrite(LED_BUILTIN, LOW);    // turn the LED off by making the voltage LOW
  delay(1000);                       // wait for a second
}


For the new version I will replace pinMode and digitalWrite for the my own  implementation them. How?

Go to the directory where you put your Arduino binaries, which include the source code. There is a subdirectory with the sources (mine is ~/bin/arduino-1.8.3/hardware/arduino/avr/cores/arduino).

There is a file (wiring_digital.c) where I found the source code for pinMode and digitalWrite. Without any shame, I copy-paste and rename. 

Note: the original includes a call to turnOffPWM, which I simply comment out, because we will not use it here and I want to cut it short.

That's all.

New version 

void setup() {
  // initialize digital pin LED_BUILTIN as an output.
  pinMode2(LED_BUILTIN, OUTPUT);
}

void loop() {
  digitalWrite2(LED_BUILTIN, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(1000);                       // wait for a second
  digitalWrite2(LED_BUILTIN, LOW);    // turn the LED off by making the voltage LOW
  delay(1000);                       // wait for a second
}

void pinMode2(uint8_t pin, uint8_t mode)
{
  uint8_t bit = digitalPinToBitMask(pin);
  uint8_t port = digitalPinToPort(pin);
  volatile uint8_t *reg, *out;

  if (port == NOT_A_PIN) return;

  // JWS: can I let the optimizer do this?
  reg = portModeRegister(port);
  out = portOutputRegister(port);

  if (mode == INPUT) { 
    uint8_t oldSREG = SREG;
                cli();
    *reg &amp;= ~bit;
    *out &amp;= ~bit;
    SREG = oldSREG;
  } else if (mode == INPUT_PULLUP) {
    uint8_t oldSREG = SREG;
                cli();
    *reg &amp;= ~bit;
    *out |= bit;
    SREG = oldSREG;
  } else {
    uint8_t oldSREG = SREG;
                cli();
    *reg |= bit;
    SREG = oldSREG;
  }
}

void digitalWrite2(uint8_t pin, uint8_t val)
{
  uint8_t timer = digitalPinToTimer(pin);
  uint8_t bit = digitalPinToBitMask(pin);
  uint8_t port = digitalPinToPort(pin);
  volatile uint8_t *out;

  if (port == NOT_A_PIN) return;

  // If the pin that support PWM output, we need to turn it off
  // before doing a digital write.
  // if (timer != NOT_ON_TIMER) turnOffPWM(timer); By Look Alterno

  out = portOutputRegister(port);

  uint8_t oldSREG = SREG;
  cli();

  if (val == LOW) {
    *out &amp;= ~bit;
  } else {
    *out |= bit;
  }

  SREG = oldSREG;
}