我是一位为学生编写编码问题的讲师。我要做的是给学生提供样板代码,并带有占位符以表示学生要完成的功能。我将为学生提供访问私人github存储库的权限,以克隆它。
但是,我还想要一个版本的代码库,以及示例解决方案。显然,我不希望学生可以使用该解决方案(直到作业结束)。
我已经考虑过分支机构,但是AFAIK,我不能将一个分支机构设为私有。
也许我可以将项目分叉到另一个私有仓库中,但是不确定如何将项目保留在snyc中(除了包含解决方案的文件之外)。
有针对这种情况的工作流程吗?
我是一位为学生编写编码问题的讲师。我要做的是给学生提供样板代码,并带有占位符以表示学生要完成的功能。我将为学生提供访问私人github存储库的权限,以克隆它。
但是,我还想要一个版本的代码库,以及示例解决方案。显然,我不希望学生可以使用该解决方案(直到作业结束)。
我已经考虑过分支机构,但是AFAIK,我不能将一个分支机构设为私有。
也许我可以将项目分叉到另一个私有仓库中,但是不确定如何将项目保留在snyc中(除了包含解决方案的文件之外)。
有针对这种情况的工作流程吗?
Answers:
什么可能是完全可行的:
因此,您的目录结构是2个克隆的git repo:
您可以在语言的注释中的“私有”代码周围加上标记,例如下面的javascript。标记指示私有代码的开始和结束位置。
function sum(a, b) {
// -----------------------START
return a + b; // so this is what you expect from the student
// -----------------------END
}
console.log(sum(1,1)); // I expect 2 as a result of your homework
然后在本地计算机上创建一个简单的脚本:
files.forEach((fileContent, fileName) => {
let newFileContent = '';
let public = true;
fileContent.forEach((line) => {
switch(line) {
case '// -----------------------START':
public = false;
break;
case '// -----------------------END':
public = true;
break;
default:
if(public) {
newFileContent = newFileContent + line + "\n";
}
}
});
writeFile('../student/' + fileName, newFileContent);
});
它将:获取所有文件,然后将内容复制到/ student(覆盖),而无需使用代码的私有标记部分。如果需要,可以在其中插入空行,但这可能会提示您期望的解决方案。
这是未经测试的示例代码,因此您可能必须进行一些调试。
现在,您唯一要做的就是在对输出感到满意时提交并推入学生存储库。这可以在使用GitHub客户端时一键完成(因此您可以进行快速的视觉检查),也可以仅在命令行上手动完成。
学生回购协议仅是一个输出存储库,因此它将始终保持最新状态。通过查看提交,学生可以很清楚地了解发生了什么更改(因为他们只显示更改),并且操作简单。
下一步将是创建一个git commit-hook,它可以自动运行脚本。
编辑:看到您对帖子进行了编辑:
显然,我不希望学生可以使用该解决方案(直到作业结束)。
我怀疑这很清楚,但是很完整:只需删除完成的练习周围的标签,就可以像对练习进行常规更新一样来发布答案。
你可以
这就是我实现此工作流程的方式:
assignments
托管在GitHub上的公共目录。添加分配的样板代码。例如,对于每个作业,您都会引入一个新的子目录,其中包含该作业的样板代码。assignments-solved
。assignments
在计算机上克隆
assignments-solved
存储库,然后将其推送到存储库(本质上是将您自己的存储库作为私有副本存储):
git clone https://github.com/[user]/assignments assignments-solved
cd assignments-solved
git remote set-url origin https://github.com/[user]/assignments-solved
git push origin master
git push --all
assignments-solved
仓库作为远程仓库添加到assignments
仓库中:
cd assignments # change to the assignments repo on your machine
git remote add solutions https://github.com/[user]/assignments-solved
assignments-solved
存储库中实现每个分配。确保每个提交仅包含一次分配的更改。solved
分支assignments
,以便不更改原始分配:
cd assignments # change to the assignments repo on your machine
git branch -b solutions
git push -u origin
assignments
,请获取
solved
遥控器和cherry-pick
包含解决方案的提交。
cd assignments # change to the assignments repo on your machine
git checkout solved
git fetch solutions
git cherry-pick [commithash]
其中[commithash]
包含解决方案的提交。您还可以通过在存储库的单独分支中实现每个分配assignments-solved
,然后在存储库中创建拉取请求来实现工作流assignments
。但是我不确定这是否可以在GitHub上使用,因为该assignments-solved
仓库不是真正的分支。
我只是可以向您提出一个实用程序,该实用程序旨在对.gitignore
存储库中的文件进行-ing和加密。工作流程有点难以使用,但是它使您的文件的加密副本与其他非机密文件一起在工作副本中可用,从而可以照常通过git对其进行跟踪。
#!/bin/bash
set -o errexit
set -o pipefail
set -o nounset
version=1
OPTIND=1
verbose=0
mode="add"
recurse=()
files=()
while getopts ":vaslr:" opt
do
case "$opt" in
\?) echo "error: invalid option: -$OPTARG" >&2 ; exit 1
;;
:) echo "error: option -$OPTARG requires an argument" >&2 ; exit 1
;;
v) let "verbose++" ; echo "verbosity increased"
;;
a) mode="add"
;;
s) mode="save"
;;
l) mode="load"
;;
r) recurse+=("$OPTARG")
;;
esac
done
shift $((OPTIND-1))
if [[ "${#recurse[@]}" != 0 ]]
then
for pattern in "${recurse[@]}"
do
while IFS= read -d $'\0' -r file
do
files+=("$file")
done < <(find . -name "$pattern" -type f -print0)
done
else
files=("$@")
fi
[[ "${#files[@]}" != 0 ]] || { echo "list of files to process is empty" >&2 ; exit 1 ; }
if [[ $mode == "add" ]]
then
for file in "${files[@]}"
do
[[ -e $file ]] && cp "$file" "${file}.bak" || touch "$file"
sshare_file="${file}.sshare"
[[ -e $sshare_file ]] || { echo "$version" > "$sshare_file" ; git add --intent-to-add "$sshare_file" ; echo "$file" >> .gitignore ; echo "${file}.bak" >> .gitignore ; git add .gitignore ; }
done
exit 0
fi
tmp_dir=`mktemp --tmpdir -d sshare.XXXX`
read -r -s -p "enter password to $mode tracked files:" sshare_password && echo ;
for file in "${files[@]}"
do
[[ ! -e $file ]] && touch "$file" || cp "$file" "${file}.bak"
sshare_file="${file}.sshare"
[[ -r $sshare_file ]] || { echo "warning: can't read file '$sshare_file' (file '$file' skipped)" >&2 ; continue ; }
file_version=$(head -1 "$sshare_file")
[[ "$file_version" == $version ]] || { echo "warning: version '$file_version' of '$sshare_file' file differs from version '$version' of script (file '$file' skipped)" >&2 ; continue ; }
tmp_file="$tmp_dir/$file"
mkdir -p "$(dirname "$tmp_file")"
> "$tmp_file"
line_number=0
while IFS= read -r line
do
let "line_number++" || :
[[ -n $line ]] || { echo "warning: empty line encountered at #$line_number in file '$sshare_file' (ignored)" >&2 ; continue ; }
echo "$line" | openssl enc -d -A -base64 -aes256 -k "$sshare_password" | gunzip --to-stdout --force | patch "$tmp_file" --normal --quiet
done < <(tail --lines=+2 "$sshare_file")
if [[ $mode == "load" ]]
then
cp -f "$tmp_file" . || { echo "warning: can't write to file '$file' (file '$file' skipped)" >&2 ; continue ; }
elif [[ $mode == "save" ]]
then
chunk=$(diff "$tmp_file" "$file" || :)
[[ -n $chunk ]] || { echo "nothing to comit since last edit for file '$file'" ; continue ; }
[[ -w $sshare_file ]] || { echo "warning: can't update sshare database '$sshare_file' (file '$file' skipped)" ; continue ; }
echo "$chunk" | gzip --stdout | openssl enc -e -A -base64 -aes256 -k "$sshare_password" >> "$sshare_file"
echo >> "$sshare_file"
echo "changes encrypted for file '$file'"
fi
done
要创建文件名a.txt
类型为的秘密文件sshare -a a.txt
。实用程序创建文件,a.txt
并将文件添加到中.gitignore
。然后,a.txt.sshare
通过.sshare
在文件名中添加扩展名来创建加密的“数据库”副本。
然后,您可以填写a.txt
一些文本。要在git commit
键入之前保存其状态sshare -s a.txt
,然后实用程序会提示您输入密码来加密文件的新状态a.txt
。然后,使用此密码的实用程序将文件的先前状态和当前状态之间的加密差异添加到文件a.txt
末尾a.txt.sshare
。
在获取/拉出带有加密文件的存储库之后,您应该sshare
使用-l
(“加载”)密钥为每个文件运行实用程序。在这种情况下,实用程序会将*.sshare
文件解密为工作副本中git无法跟踪的文本文件。
您可以为每个秘密文件使用不同的密码。
此程序可允许GIT中轨道有效地改变(DIFF的.sshare
文件是简单的一行)。