2

TLDR; The goal is to maintain two codes (if possible on one repo, if not on two), one that contains exercises and one that contains also the correction, by avoiding duplicating code as much as possible. The normal users should not be able to read the corrected code. Any trick is welcome.

Long version:

I'm trying to do the following things: the idea is that we are a few "teachers", and we are working on some programming exercises for our students. Usually we have 3 kinds of "resources" :

  1. resources that the students have access to but should not in theory modify (like libraries, assets, tests that will be run by CI...)
  2. resources that the students should modify, like a skeleton of an application
  3. resources that should not be readable to students, usually the correction of the exercise

What we do so far is that we have two repos, one private for the teachers (with correction), and one public for the students, then the students forks the public channel into a private one, add the teachers as maintainers, and work from here.

However, it's a bit annoying to have code duplicated between two repositories. Is there any smarter option that git (or more specifically gitlab as it's the tool that we use, we are using CI for testing automatically the code of the students) could provide to let us protect some files from being read/clone? Or is there any other way to maintain easily two repositories that differ just by one file?

tobiasBora
  • 1,542
  • 14
  • 23

2 Answers2

1

You could have two remote repositories, say solution (that contains the private code), and student (that contains the public code that students need to work on).

Put all your code, except the solution, in a branch b1 in solution. Create a new branch b2 off b1 and push the private code there. Then, sync only branch b1 with the other remote, student.

The students can then fork student and work from there. If you need to compare code, you can use git diff with your private branch to see all that has been changed. You could also merge changes to a specific file in the students' code to your fresh b1 branch, in order to quickly discard any other changes.

GoodDeeds
  • 7,956
  • 5
  • 34
  • 61
  • Thanks for your comment. I'll use this method, but I needed to add some scripts to facilitate the synchronization between the two branches. I'll put that in another answer, and accept yours meanwhile. – tobiasBora Jan 23 '20 at 19:33
  • 1
    Slightly related: you could have a look at the features offered by GitHub classroom to see if they are relevant to your use case. E.g. you can create repository templates and share with your class. – GoodDeeds Jan 23 '20 at 19:35
  • Hum, it's good to know thanks! It's just too bad it's a paid service, I'd prefer solutions that I can selfhost and this seems to be quite linked with github. But it looks very interesting! – tobiasBora Jan 23 '20 at 19:40
  • Hum, it seems that Github classroom is open source. But it's not clear to me yet how it fits with github/gitlab (is it only for github?), and if you need to pay even if you self host the service. – tobiasBora Jan 23 '20 at 19:43
0

I used the method proposed by GoodDeeds (Thanks!), but in order to merge the two branches, I wrote some scripts (easy to share with the other teachers), that may be useful to others:

A script that merge the branches, by making sure files in FILES_TO_REMOVE are not present on the student version, and FILES_NOT_MERGED are not synchronized anymore.

#!/usr/bin/env bash
# This script will move the teacher changes on the student branch... except
# for the solutions and the scripts files.
set -e

# https://stackoverflow.com/questions/4691956/how-to-make-bash-expand-wildcards-in-variables
function expand { for arg in "$@"; do [[ -f $arg ]] && echo $arg; done }

#### /!\ Lines to edit if you add some more files to correct! ####
FILES_TO_REMOVE=$(expand gestion/*)
FILES_NOT_MERGED=$(expand mono.py cesar.py)

BRANCH=$(git rev-parse --abbrev-ref HEAD)
if [[ "$BRANCH" != "master" ]]; then
  echo '/!\ Please go first on master branch.';
  exit 1;
fi

# Go to the root folder
cd $(git rev-parse --show-toplevel)
# Go on the student branch
git checkout students
# merge but don't commit yet
echo "### If you see some errors on the next line about"
echo "### conflicts with a file that should be removed,"
echo "### it's not important."
git merge --no-ff --no-commit master || true
# Remove files if needed
for FILE in $FILES_TO_REMOVE
do
    echo "- Removing file $FILE..."
    git rm "$FILE" || true
done

# reset the files we don't want to commit
for FILE in $FILES_NOT_MERGED
do
    echo "- Reseting $FILE..."
    # Undo changes made on the files
    git reset HEAD "$FILE" || true
    echo "  Checkout..."
    git checkout -- "$FILE" || true
done
echo "### Git status:"
git status
read -p "### Is the last git status fine for you? [Y/n] " -n 1 -r
echo    # (optional) move to a new line
echo "The reply is '$REPLY'"
if [[ $REPLY =~ ^[^Yy]+$ ]]
then
    echo "You are not happy with the last status?"
    echo "Then manually fix the conflits/issues,"
    echo "eventually commit with:"
    echo " $ git commit -m \"Merged master\""
    echo "and when you are done come back on"
    echo "the master branch with:"
    echo "$ git checkout master"
    exit 1
fi
git commit -m "Merged master"
echo "### Status of student branch:"
git status
echo "### Checkout back on master:"
git checkout master

echo "########################################"
echo "### Congrats, the branch is synced ! ###"
echo "########################################"

I also wrote a script to automatically push the students branch on the master branch of the second remote:

#!/usr/bin/env bash
# This script will push the students branch on the student online repo.
set -e
NAME_REMOVE="students_repo"
DEFAULT_REMOTE="https://gitlab.com/3i024_2020/tme_01_mono_students"

if ! git config "remote.${NAME_REMOTE}.url" > /dev/null; then
    echo "### I will add a new remote named 'students_repo'."
    echo "What is the adress of this remote? (default is '${DEFAULT_REMOTE}'):"
    read -p "" remote
    remote=${remote:-$DEFAULT_REMOTE}
    git remote add students_repo "$remote"
    echo "New remote created!"
fi
echo "### Please, make sure you merged your branch with the student branch with:"
echo "$ gestion/merge_student.sh "
echo ""
echo "### I will now push the students branch on the master branch"
echo "### of the 'students_repo' remote."
git push students_repo students:master
echo "### Done!"
tobiasBora
  • 1,542
  • 14
  • 23