chiến lược github để giữ một phiên bản của tệp riêng tư


11

Tôi là một giảng viên viết các vấn đề mã hóa cho sinh viên. Những gì tôi muốn làm là cung cấp cho sinh viên mã soạn sẵn với phần giữ chỗ cho các chức năng mà sinh viên sẽ hoàn thành. Tôi sẽ cung cấp cho sinh viên quyền truy cập vào một repo github tư nhân để sao chép này.

Tuy nhiên, tôi cũng muốn có một phiên bản của cơ sở mã, hoàn thành với các giải pháp mẫu. Rõ ràng tôi không muốn các sinh viên có quyền truy cập vào giải pháp (cho đến khi bài tập kết thúc).

Tôi đã nghĩ về các chi nhánh, nhưng AFAIK, tôi không thể giữ một chi nhánh riêng tư.

Có lẽ tôi có thể chia dự án thành một repo riêng khác, nhưng không chắc chắn làm thế nào tôi có thể giữ các dự án trong snyc (ngoài tệp có chứa giải pháp).

Có một quy trình làm việc cho tình huống này?


1
Tôi không nghĩ vậy. Nhưng những gì bạn lạnh làm: giao diện delcare cho alle các phương pháp sẽ được thực hiện. Trong repo sinh viên-công cộng của bạn, tạo các lớp thực hiện các giao diện đó với các thân phương thức trống. Duy trì các giải pháp trong một repo riêng. Điều này không hoàn toàn giải quyết vấn đề đồng bộ hóa của bạn nhưng nó giảm nó xuống phạm vi của các tác vụ.
marstato

Bạn đã xem xét sử dụng API github để kiểm soát quyền truy cập vào các chi nhánh chưa?

Câu trả lời:


8

Điều có thể khá khả thi:

  • Tạo 2 kho lưu trữ: học sinh và giáo viên.
  • Sao chép chúng vào máy của bạn (có thể được thực hiện với ứng dụng khách Github)
  • Bạn chỉ làm việc trong giáo viên , không bao giờ chạm vào học sinh.

Vì vậy, cấu trúc thư mục của bạn là 2 nhân bản git repo:

  • / student (có thư mục .git)
  • / giáo viên (với một thư mục .git)

Bạn đặt các điểm đánh dấu xung quanh mã "riêng tư" trong các bình luận cho ngôn ngữ của bạn, ví dụ javascript bên dưới. Các điểm đánh dấu cho biết nơi mã riêng bắt đầu và kết thúc.

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

Sau đó, tạo một tập lệnh đơn giản trên máy cục bộ của bạn:

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);
});

Nó sẽ: lấy tất cả các tệp của bạn và sao chép nội dung vào / student (ghi đè) mà không có các phần được đánh dấu riêng tư của mã. Nếu bạn muốn bạn có thể chèn các dòng trống ở đó nhưng điều đó có thể đưa ra gợi ý về loại giải pháp mà bạn mong đợi.

Đó là mã ví dụ chưa được kiểm tra, vì vậy có khả năng bạn phải thực hiện một số gỡ lỗi.

Bây giờ chỉ có điều bạn phải làm là cam kết và đẩy vào kho lưu trữ của sinh viên khi bạn hài lòng về đầu ra. Điều đó có thể được thực hiện trong một cú nhấp chuột khi sử dụng ứng dụng khách GitHub (vì vậy bạn có thể thực hiện đánh giá trực quan nhanh) hoặc chỉ thực hiện thủ công trên dòng lệnh.

Repo của sinh viên chỉ là một kho lưu trữ đầu ra nên nó sẽ luôn cập nhật, rõ ràng cho sinh viên biết những gì đã thay đổi bằng cách xem các cam kết (vì chúng chỉ hiển thị các thay đổi) và thật đơn giản để xử lý.

Một bước nữa sẽ là tạo một git commit-hook tự động chạy tập lệnh của bạn.

Chỉnh sửa: Xem bạn đã thực hiện chỉnh sửa bài đăng của mình:

Rõ ràng tôi không muốn các sinh viên có quyền truy cập vào giải pháp (cho đến khi bài tập kết thúc).

Tôi nghi ngờ điều đó rõ ràng nhưng phải hoàn thành: Chỉ cần xóa các thẻ xung quanh bài tập đã hoàn thành sẽ xuất bản câu trả lời giống như cách bạn làm để cập nhật bình thường cho bài tập.


đã hy vọng tôi có thể làm điều này với một số git voodoo, tuy nhiên giải pháp của bạn là rất thực tế.
Ken

@Ken cũng đã nghĩ về điều đó nhưng đó là một công cụ sai cho công việc sai. Git không hợp nhất, cập nhật, v.v. nhưng nói chung không phải là ý tưởng để chọn mã. Nó tốt trong việc giữ cho cơ sở mã của bạn nhất quán trên nhiều máy. Vì vậy, đó là lý do tại sao tôi lên một giải pháp khác. Điều tôi cũng thích về cách tiếp cận này là nó giảm thiểu rủi ro và lao động để dễ dàng theo kịp nó. Và cuối cùng, bạn nên viết thông điệp cam kết của mình cho repo sinh viên bằng tay để đưa ra một ví dụ tốt cho các sinh viên của bạn;)
Luc Franken

Để giúp git theo dõi các thay đổi, bạn có thể tạo một nhánh sinh viên trong repo giáo viên của mình, chạy tập lệnh khi hợp nhất (hoặc hợp nhất bằng tay loại bỏ bất cứ thứ gì giữa các điểm đánh dấu). Sau đó đồng bộ hóa chi nhánh sinh viên tại địa phương và đẩy nó đến repo sinh viên thay vì nguồn gốc giáo viên. Bằng cách này, git sẽ ở trạng thái tốt hơn để theo dõi các thay đổi và có lịch sử được chuyển tiếp chính xác từ một repo sang tiếp theo. Tốt nhất của cả hai thế giới. Tôi đã không thử tâm trí này bạn nhưng tôi không hiểu tại sao nó không hoạt động.
Newtopian

1
Tôi thích điều này ngoại trừ ý tưởng loại bỏ các thẻ bắt đầu. Tốt hơn là mang chúng bằng cách thêm từ "giải pháp".
candied_orange

@CandiedOrange đó cũng là một cái hay, đồng ý về điều đó. Giải pháp cũng sẽ cho phép một số định dạng khác nhau và nó phân biệt rõ ràng giữa các thẻ bị quên và quyết định thực sự rằng giải pháp sẽ được công bố. @ newtopian: Tôi đã suy nghĩ về điều đó nhưng tôi không thấy đủ lợi thế. Ngoài ra tôi quyết định xem đầu ra của sinh viên là một loại mã hoàn toàn khác. Đó không phải là nguồn thực sự nên tôi quyết định không. Những gì tôi sẽ làm với các chi nhánh trong repo giáo viên là ví dụ: Làm việc trên các nhiệm vụ cho học kỳ tiếp theo. Khi bạn đã sẵn sàng, bạn hợp nhất chúng thành chủ và sau đó chạy tập lệnh.
Luc Franken

6

Bạn có thể

  • Tạo một kho lưu trữ GitHub công khai khi bạn cam kết mã soạn sẵn
  • Ngã ba kho lưu trữ này như là một kho lưu trữ GitHub riêng
  • Giải các bài tập trong kho lưu trữ rẽ nhánh
  • Hợp nhất từng giải pháp vào kho lưu trữ công cộng khi hoàn thành nhiệm vụ

Đây là cách tôi sẽ thực hiện quy trình công việc này:

  • Tạo một kho assignmentslưu trữ công khai được lưu trữ trên GitHub. Thêm mã soạn sẵn cho các bài tập. Ví dụ, đối với mỗi bài tập, bạn giới thiệu một thư mục con mới chứa mã soạn sẵn của bài tập.
  • Tạo một kho lưu trữ riêng mới assignments-solvedtrên GitHub. Sao chép assignmentsrepo trên máy của bạn và đẩy nó vào assignments-solvedrepo (về cơ bản là phân nhánh kho lưu trữ của riêng bạn dưới dạng bản sao riêng tư): 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
  • Thêm assignments-solvedrepo dưới dạng từ xa vào assignmentsrepo: cd assignments # change to the assignments repo on your machine git remote add solutions https://github.com/[user]/assignments-solved
  • Thực hiện mỗi nhiệm vụ trong assignments-solvedkho lưu trữ. Đảm bảo rằng mỗi cam kết chỉ chứa các thay đổi từ một nhiệm vụ.
  • Bạn có thể muốn tạo một solvednhánh trong assignmentsrepo, để các bài tập ban đầu không bị thay đổi: cd assignments # change to the assignments repo on your machine git branch -b solutions git push -u origin
  • Khi bạn muốn xuất bản một giải pháp vào assignments, hãy tìm nạp solvedtừ xa và cherry-pickcác xác nhận có chứa các giải pháp. cd assignments # change to the assignments repo on your machine git checkout solved git fetch solutions git cherry-pick [commithash] Nơi [commithash]chứa cam kết của giải pháp của bạn.

Bạn cũng có thể thực hiện quy trình công việc bằng cách triển khai từng nhiệm vụ trong một nhánh riêng của assignments-solvedrepo và sau đó tạo yêu cầu kéo trong assignmentsrepo. Nhưng tôi không chắc chắn nếu điều này sẽ hoạt động trong GitHub, vì assignments-solvedrepo không phải là một ngã ba thực sự .


Tôi đã sử dụng thành công một phương pháp tương tự để tách bài kiểm tra lập trình khỏi các câu trả lời đã gửi. Trong trường hợp của tôi, các giải pháp đã gửi được thêm vào các nhánh riêng lẻ của một bản sao riêng tư và không bao giờ được hợp nhất trở lại repo công khai. Nó có thêm lợi ích là cho tôi xem phiên bản thử nghiệm nào mà mỗi ứng viên đã giải, vì nó phát triển theo thời gian.
axl

0

Tôi chỉ có thể đề xuất cho bạn một tiện ích nhằm mục đích .gitignore-ing và mã hóa các tệp trong kho lưu trữ của bạn. Quy trình làm việc hơi khó sử dụng, nhưng nó làm cho các đối tác được mã hóa của các tệp của bạn có sẵn tại bản sao làm việc cùng với các tệp không bí mật khác, cho phép theo dõi chúng bằng git như bình thường.

#!/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

Để tạo tập tin bí mật với a.txtloại tên tệp sshare -a a.txt. Tiện ích tạo tập tin a.txtvà tập tin được thêm vào .gitignore. Sau đó, nó tạo đối tác "cơ sở dữ liệu" được mã hóa a.txt.ssharebằng cách thêm .ssharephần mở rộng vào tên tệp.

Sau đó, bạn có thể điền vào a.txtmột số văn bản. Để lưu trạng thái của nó ngay trước khi git commitsshare -s a.txt, sau đó tiện ích nhắc bạn nhập mật khẩu để mã hóa trạng thái mới của tệp a.txt. Sau đó, sử dụng mật khẩu này sẽ thêm mã hóa khác nhau giữa trạng thái tệp trước đó và hiện tại a.txtvào cuối a.txt.ssharetệp.

Sau khi tìm nạp / kéo kho lưu trữ với các tệp được mã hóa, bạn nên chạy ssharetiện ích cho mỗi tệp bằng -lphím ("tải"). Trong trường hợp này, tiện ích giải mã *.ssharecác tệp thành các tệp văn bản chưa được mã hóa bởi git trong bản sao làm việc.

Bạn có thể sử dụng các mật khẩu khác nhau cho mỗi tệp bí mật.

Tiện ích cho phép git để theo dõi thay đổi một cách hiệu quả ( diff của .ssharefile chỉ đơn giản là một dòng là).

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.