ファイルの1つのバージョンをプライベートに保つためのgithub戦略


11

私は学生のためにコーディングの問題を書く講師です。私がやりたいのは、生徒が完了すべき機能のプレースホルダーを含む定型コードを生徒に与えることです。これを複製するために、学生にプライベートgithubリポジトリへのアクセスを許可します。

ただし、サンプルソリューションを備えたバージョンのコードベースも必要です。明らかに、生徒にソリューションへのアクセス権を与えたくありません(課題が終了するまで)。

私はブランチについて考えましたが、私の知る限り、1つのブランチをプライベートに保つことはできません。

プロジェクトを別のプライベートリポジトリにフォークすることもできますが、プロジェクトをsnycに(ソリューションを含むファイルは別として)保持する方法はわかりません。

この状況のワークフローはありますか?


1
そうは思わない。しかし、あなたが冷たくすること:実装されるすべてのメソッドのためのdelcareインターフェース。学生公開リポジトリで、空のメソッド本体を使用してこれらのインターフェイスを実装するクラスを作成します。別のプライベートリポジトリでソリューションを維持します。これは、同期の問題を完全に解決するわけではありませんが、タスクの範囲に限定します。
-marstato

github APIを使用してブランチへのアクセスを制御することを検討しましたか?

回答:


8

非常に実行可能なこと:

  • 学生と教師の2つのリポジトリを作成します。
  • それらをマシンにクローンします(Githubクライアントで実行できます)
  • あなたは先生だけで働き、生徒に触れないでください。

したがって、ディレクトリ構造は2つのクローンgitリポジトリです:

  • / student(.gitフォルダーを使用)
  • / teacher(.gitフォルダーを使用)

あなたの言語のコメントにある「プライベート」コードの周りにマーカーを置きます。以下の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クライアントを使用する場合(ワンクリックで簡単に確認できる)にワンクリックで実行できます。または、コマンドラインで手動で実行することもできます。

学生レポは出力リポジトリのみであるため、常に最新の状態に保たれ、コミットを確認することで何が変更されたかが明確になり(変更のみが表示されるため)、処理が簡単です。

さらに1つのステップは、スクリプトを自動実行するgit commit-hookを作成することです。

編集:投稿を編集したことを確認します。

明らかに、生徒にソリューションへのアクセス権を与えたくありません(課題が終了するまで)。

はっきりしていると思いますが、完全なものです。完成したエクササイズのタグを削除するだけで、エクササイズの通常の更新と同じ方法で回答が公開されます。


git voodooでこれができるといいのですが、あなたのソリューションは非常に実用的です。
ケン

@Kenもそれについて考えていましたが、それは間違った仕事のための少し間違ったツールです。Gitはマージ、更新などを行いますが、一般的にはコードを選択することは考えていません。複数のマシンでコードベースの一貫性を維持するのに適しています。だから、私は別の解決策を考えました。このアプローチについて私が気に入っているのは、リスクと労力が最小限に抑えられるため、簡単に対応できることです。そして、最後に、とにかく学生レポジトリにコミットメッセージを書いて、学生に良い例を示す必要があります;)
リュックフランケン

gitが変更を追跡できるようにするには、教師のリポジトリに学生ブランチを作成し、マージするときにスクリプトを実行します(または、マーカー間の何かを手動で削除してマージします)。次に、学生ブランチをローカルで同期し、教師のオリジンの代わりに学生リポジトリにプッシュします。このようにして、gitは変更を追跡し、あるレポから次のレポに履歴を適切に転送するためにより良い形になります。両方の長所。私はあなたにこの心を試していませんが、なぜそれがうまくいかないのかわかりません。
ニュートピア

1
開始終了タグを削除するという考えを除いて、これが気に入っています。「ソリューション」という言葉を追加して、それらを破壊する方が良いでしょう。
candied_orange

@CandiedOrangeも素晴らしいものです、それに同意します。また、ソリューションはいくつかの異なるフォーマットを許可し、忘れられたタグとソリューションを公開するという実際の決定を明確に区別します。@ newtopian:私はそれについて考えていましたが、十分な利点が見られませんでした。また、学生の出力をまったく異なる種類のコードとして見ることにしました。それは本当のソースではないので、私はそうしないことにしました。教師レポでブランチを使用して行うことは、たとえば次の学期のタスクに取り組みます。準備ができたら、それらをマスターにマージしてからスクリプトを実行します。
リュックフランケン

6

あなたは出来る

  • ボイラープレートコードをコミットした場合、パブリックGitHubの再投稿を作成します
  • このリポジトリをプライベートGitHubの再投稿としてフォークします
  • 分岐したリポジトリの割り当てを解決する
  • 割り当てが完了したら、各ソリューションをパブリックリポジトリにマージします

これが、このワークフローの実装方法です。

  • assignmentsGitHubでホストされている公開再投稿を作成します。割り当ての定型コードを追加します。たとえば、割り当てごとに、割り当ての定型コードを含む新しいサブディレクトリを導入します。
  • GitHubに新しいプライベートリポジトリassignments-solvedを作成します。クローン assignmentsマシン上のレポ、そしてそれをプッシュするassignments-solved (基本的に民間のコピーとして、独自のリポジトリをfork)レポ: 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リポジトリに各割り当てを実装します。各コミットには、1つの割り当てからの変更のみが含まれていることを確認してください。
  • 元の割り当てが変更されないようsolvedに、assignmentsリポジトリにブランチを作成することもできます。 cd assignments # change to the assignments repo on your machine git branch -b solutions git push -u origin
  • ソリューションをに公開する場合は、ソリューションを含むリモートおよびコミットをassignmentsフェッチ します。 ソリューションのコミットが含まれている 場所。solvedcherry-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リポジトリにプルリクエストを作成することにより、ワークフローを実装することもできます。しかし、assignments-solvedレポは本当の分岐ではないので、これがGitHubで機能するかどうかはわかりません。


同様の方法を使用して、提出された回答からプログラミングテストを分離することに成功しました。私の場合、提出されたソリューションはプライベートクローンの個々のブランチに追加され、パブリックリポジトリにマージされることはありません。時間の経過とともに進化するため、各候補者が解決したテストのバージョンを確認できるという利点もあります。
axl

0

.gitignoreリポジトリ内のファイルの暗号化と暗号化を目的としたユーティリティを提案できます。ワークフローはやや使いにくいですが、ファイルの暗号化された対応物を他の非秘密ファイルと一緒に作業コピーで使用できるようにします。これにより、通常どおり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(「ロード」)各ファイルに対してユーティリティを実行する必要があります。この場合、ユーティリティは、作業コピーのgit*.sshareによって追跡されないテキストファイルにファイルを復号化します。

秘密ファイルごとに異なるパスワードを使用できます。

このユーティリティを使用すると、gitは変更を効率的に追跡できます(ファイルの差分.sshare1行だけです)。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.