I would suggest using git submodule
to manage the VS aspect:
git submodule
Don't include the visual studio project in the source code repo. Make it a separate git repo that has the source code repo as a module. This will locate it in a subdirectory, which, last time I used Visual Studio (admittedly quite a long time ago), it was okay with.
You should make a branch in the VS repo for each branch in the code repo, and then use the git submodule
commands to set the submodule commit for each branch to the correct branch in the code repo.
This way you can still merge changes from earlier lessons into newer ones, and can still add files into VS that only exist in particular branches.
- This is all a bit experimental to me too, so my apologies if I sound like I know what I'm talking about. I've worked a bit with submodules but not done anything quite this complex, specifically not somethine involving branches in an outer repo that map to branches in an inner repo.
The downside to this is that VS users will I think have to run git submodule update
in addition to git checkout lesson2
. Otherwise it should be pretty straightforward and will let you keep the binary VS stuff out of the code repo, which will make it easier to push through changes to code, especially ones that don't add files (since this would entail modifying the VS project).
It's still going to be a hassle if you have to change the VS repo, since it's not (AFAIK) possible to merge in changes to a .dsp or whatever the extension is these days. So if the students have made their own changes to the project file they'll have to replace it and make those same changes again. But this will keep the confusion somewhat more organized, assuming that your students are comfortable with this little extra updraft on the learning curve. For people not using VS it makes things much more straightforward and keeps the clutter out of their way.
Propagating changes
When you make changes you will have to check out each branch and merge the changes from the previous lesson. You could script this fairly easily. Assuming there were no conflicts, the whole procedure would only take a moment. EG
lessons=lesson1 lesson2 lesson3
unset prev;
function mergenext {
while [[ $lessons ]]; do
if [[ $prev ]]; then
{ git checkout "$lessons" && git merge "$prev"; } || return $?
fi
prev="$lessons"
lessons=( "${lessons[@]:1}" );
done;
}
mergenext
# resolve conflicts
mergenext
# ...
Or at least something like that. I didn't really review that code very thoroughly so probably use with caution. But the basic idea is that you can just call mergenext
from a shell, and if there are any conflicts you can resolve them, commit, and then call mergenext
again, from the same shell, and so on. If there aren't any conflicts it should just run and probably take 20 seconds to do it all.
Paired branches
An alternative to using submodule would be to create a second VS branch for each lesson branch. This is definitely easier to figure out, both for you and your students. You would end up merging in changes from the modified lesson's pure-source branch, to that lesson's VS branch, and then upstream from both of those along the pure-source and VS branch lines. So it ends up being a little more work but it's easier on the brain. The advantages over just including VS with every branch here is mainly that it's arguably easier to grok when you look at the revision history.
Rebase vs Merge
I would avoid using rebase
, partly because it's not really necessary, and partly because it's going to be problematic if your students have already pulled from your repo before somebody spots a bug. I think your branch layout is sound as it is, because it lets you keep a current version of each lesson and merge changes up the line. If you use a single branch then you're basically forced to rebase.
When you rebase, you're rewriting history, which changes the SHA1 hashes that identify the commits, which means that there's no longer a common lineage with cloned repos, which means that anyone who has already cloned the repo and started making commits (ie working on the assignment) is going to have to basically build a patch of their work, re-clone the repo, and apply it again. There are I think more elegant ways to resolve this than that approach, but I haven't read that part of the manpage yet.
Also the rebase approach prevents your clever error-fixing students from just sending you a pull request for their branch. In that situation you would be able to merge in their changes and then notify the class that they should all pull from each branch in your repo to get the revised code.
Pulling updates
I'm not aware of any way to pull all of the branches of a repo at one time. This doesn't really make sense in git, since whenever you pull in changes you're merging, and generally you want to be able to intervene in case of a conflict. A script similar to the one above would be a good way to do this though. Normally in git-land you would be working on your local branch(es) and only be pulling from upstream when something new and relevant has been added.
Of course there is a myriad of possible topologies, but in general you would be very aware that you need to pull in changes. Also in your case, if the changes made to your repo conflict with something that a student has done while completing the assignment, she is going to have to manually resolve that conflict when pulling from your repo.
Tags
I'm not aware of any way to create a "tagset" with references to a set of commits; this is a very atypical use case (ie not something that people generally do when working on software projects) so I would be surprised if there is support for it.
History
Keeping the VS stuff out of the code repo, you will end up with something like
A--B--C-----------------J
\ \
D--E--F-----------K
\ \
G--H--I-----L
which is fairly straightforward when you realize that J was a patch that fixed a bug in the original lesson. It seems a bit confusing, but this is quite similar to how a typical repository would work, in the case where a bugfix had been put into the mainline code (A-B-C) and then pulled into the topic branch (D-E-F) and from there pulled into someone's personal branch (G-H-I).
I usually use git show-branch
for reviewing history, but git rev-list --graph --oneline --branches
, although harder to remember, may be easier to understand. They both show you the history of commits along with the first line of the commit log for each commit. git rev-list...
makes it more of a graph, similar to what gitk lesson1 lesson2 ...
will show you.