Using Git to contribute to VASSAL development

This guide is intended to document the first steps in using Git for Vassal development. If you do not intend to contribute and just want a copy of the code to play around with, follow this guide instead: Git clone guide.

Prerequisites

  • On Linux, install Git using your distributions package management tool, e.g. apt install git
  • On Windows, Git Bash is very good: https://gitforwindows.org/ (and once you have installed it you can also use git from a regular command line)

Definitions

Setting up Git

If you are relatively new to git, we highly recommend “The Git Book” which you can read for free here – Git - Book – It is a quick and easy read and will help you understand what is going on.

Fork the central Vassal repository

To create a fork, log into your GitHub account, navigate to GitHub - vassalengine/vassal: VASSAL, the open-source boardgame engine, look for a button on the top right labeled “Fork”, click on it.

You can also follow one of these guides which explain the forking process in detail:

The result will be a vassal repository in your GitHub account:

Clone the repository to a local computer

The official guide for this step is here: Cloning a repository - GitHub Docs

The contributor creates a clone of his fork on his local computer by running git clone:

(on posix systems) cd /home/contributor/developmentProjects (on Windows) cd C:\Users\contributor\developmentProjects git clone https://github.com/contributor/vassal.git

This creates a clone of the contributor’s repo in /home/contributor/developmentProjects/vassal or C:\Users\contributor\developmentProjects\vassal. This clone is yet another fully featured repository, like another SVN server, a third one after central and the contributor’s GitHub repo, but it points to the contributor’s repo on GitHub and knows that it is a kind of “child” of the contributor’s GitHub repo.

Link Git on the local computer to GitHub account

Link local repo to upstream

First some more definitions. The links between repositories have names, we want the local repository to link to both the contributor’s GitHub repo and to the central Vassal repo.

The remote is already known to the local repo since we cloned it from there, we can see it like this:

(on posix systems) cd /home/contributor/developmentProjects/vassal (on Windows) cd C:\Users\contributor\developmentProjects\vassal git remote -v

Tell the local repo where the central Vassal repo is located. Follow this guide: Configuring a remote repository for a fork - GitHub Docs Or change directory to your local repository and then add the upstream. So on POSIX systems do this:

cd /home/contributor/developmentProjects/vassal

and on Windows:

cd C:\Users\contributor\developmentProjects\vassal.

Then on either platform:

git remote add upstream https://github.com/vassalengine/vassal.git

Check if it worked:

git remote -v

This should show 2 lines with origin and another 2 lines with upstream now, e.g.:

origin https://github.com/contributor/vassal.git (fetch) origin https://github.com/contributor/vassal.git (push) upstream https://github.com/vassalengine/vassal.git (fetch) upstream https://github.com/vassalengine/vassal.git (push)

Branches in Git

Git branches are per repository. The central repo has a branch called master. This is the trunk of the SVN server, in SVN terms. This is the branch where Vassal development happens, the branch where all our commits have to end up. The contributor’s repo on GitHub also has a branch called master. And the contributor’s repo on the local computer also has a branch called master. Three repositories, one master branch in each:

Workflow for keeping the master branches in sync

First let’s make sure we are in sync, we do this regularly, usually after we see that there are new commits in central master and our other 2 master branches fell behind. Since we (the contributor) do not have write access to the central repo, we can only read from the master branch. We never do any commits into this branch, instead we regularly sync the master branches by following the official guide here: Syncing a fork - GitHub Docs

Or by following these steps:

Update information about central/master

Make changes to central/master known to the local repo:

git fetch upstream

Switch to master branch

Make sure we have the master branch selected (make sure local changes are all committed before this, more about this later):

git checkout master

Bring local master up to date

Sync central/master with our local master:

git merge upstream/master

Bring github.com/contributor/vassal:master up to date

At this point our local master and central/master are in sync, now let’s update our GitHub master as well by pushing our local master to origin:

git push origin refs/heads/master:master

Now our own two master branches are in sync with central/master.

Workflow for changing code and getting the changes into the central master branch

Next, the workflow for actually changing the code. This is best done right after syncing the master branches, to make sure we build upon the latest commits in master.

Create new branch

Create a new branch in the local repo and switch to it. Let’s call it bugfix-12345:

git checkout -b bugfix-12345

Change code and commit locally

Change code. Commit it to the local branch. Change some more, commit again. As often as we want. At this point we’re only working in our local branch, the outside world does not know anything about our local branch and the commits in it.

(change ClassA.java) git add ClassA.java git commit -m ‘changes to ClassA’ (change ClassB.java and ClassC.java) git add ClassB.java ClassC.java git commit -m ‘changed ClassB and ClassC’ (change ClassA.java ClassB.java and ClassC.java again) git add ClassA.java ClassB.java ClassC.java git commit -m ‘a third round of changes’

Each commit consists of 2 steps, the files that go into the commit have to be added first, then comes the actual commit. Files that were not added do NOT end up in the commit. To see which files are added, which are not, and which files are not tracked by git at all:

git status

If all files with changes should end up in the commit, there is a shorthand that adds all files and commits them:

git add * git commit -m ‘changes to several files, and maybe even added some new ones’

One can also use -a switch as shorthand, and this will pick up changes to any tracked files (files already in the repository), but will NOT notice brand new files previously untracked by the project:

git commit -a -m ‘changes to several existing files’

Optional: Rewrite history

Now is a good time for a “git rebase” if we want to change history i.e. reword commit messages, smash several commits into one, change the order of the commits. Complex commit reordering, squashing and fixup is best done with the help of a tool or a graphical git client.

Push commits to GitHub

Now we think we are ready with the changes we wanted to do and we want to get them into central/master by doing a pull request (PR). First we push our local branch, which so far is not known outside of our local computer, to our GitHub repo:

git push -u origin bugfix-12345

Open PR

Now we log into our GitHub account on the GitHub website, open up our repository, and see how GitHub noticed that we just pushed a new branch and since it knows that our GitHub repo is a fork of another repo, it is smart enough to offer us to create a PR from this branch. We do this, we push the green button that creates a new PR, verify that it shows something like this:

contributor wants to merge N commits into vassalengine:master from contributor:bugfix-12345

We write a few words into the description (or we don’t) and confirm.

Modify PR

As usual, we have forgotten some detail, or someone else has a good idea how to make our code change even better, we log into our GitHub account a while later, check our PR and see that some changes were requested. We change to our local branch again, in case we switched away from it in the meanwhile:

git checkout bugfix-12345

We apply the requested changes, commit them and push them to GitHub:

(apply changes) git commit -a -m ‘applied requested changes’ git push -u origin bugfix-12345

We look at our PR on GitHub again and see that GitHub is smart enough to add our new commits to the existing PR.

This can go back and forth several times, more changes can be requested, we repeat step 5, and so on. At some point the PR will be accepted and closed. GitHub will then offer us to delete the branch, this is the branch on GitHub not the local one.

Delete branch on GitHub

Accept GitHub’s proposal and delete the branch “bugfix-12345” on GitHub. There will be a button “Delete branch” at the bottom of the PR on GitHub.

Delete branch locally

Delete the branch locally:

git branch -d bugfix-12345

Using graphical git clients

When using graphical git clients, each of the above commands maps to an action in the graphical client, e.g.:

  • git fetch upstream → in the IntelliJ git client that’s a button with two arrows circling each other with a popup info saying “fetch all remotes”
  • git checkout -b bugfix-12345 → right-click on the master branch, click on “create branch…”, enter “bugfix-12345”
  • git checkout bugfix-12345 → right-click the branch bugfix-12345, click on “checkout branch”
  • git push -u origin bugfix-12345 → right-click the branch bugfix-12345, click on “push” or “push…”

For committing changes, the graphical git clients usually offer a convenient view where the files to be committed can be seen and each file can be marked to include it or exclude it from the commit.

The major Java IDEs all come with built-in graphical git clients.

A good generic graphical git client for Windows is Sourcetree, downloadable for free from here: https://www.sourcetreeapp.com/.

Additional tips

Practice

You can practice interactions between git repos locally by creating 2 or more git repos on the local filesystem, then create branches, commit, push/pull branches etc:

cd /home/user/tmp
mkdir gitrepo-main
cd gitrepo-main
git init .
echo "first file" > first.txt
git add first.txt
git commit -m "first commit"
cd .. 
git clone gitrepo-main/ ./gitrepo-clone1
cd gitrepo-clone1
git checkout -b bugfix-1
echo "\na second line of text" >> first.txt
git commit -a -m "added second line to first.txt"
git push -u origin bugfix-1 (.... etc)

View repository state on the command line

For a neat ascii-art view of the branches and commits, add these aliases as shorthands for complex commands:

git config --global alias.lg1 log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)%h%C(reset) - %C(bold green)(%ar)%C(reset) %C(white)%s%C(reset) %C(dim white)- %an%C(reset)%C(bold yellow)%d%C(reset)' --all
git config --global alias.lg2 log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)%h%C(reset) - %C(bold cyan)%aD%C(reset) %C(bold green)(%ar)%C(reset)%C(bold yellow)%d%C(reset)%n'' %C(white)%s%C(reset) %C(dim white)- %an%C(reset)' --all
git config --global alias.tree log --graph --decorate --pretty=oneline --abbrev-commit git config --global alias.lg !git lg1

Then use them in any git repo:

git lg git lg1 git lg2 git tree

Rewrite history

git rebase is a very powerful feature of git, once comfortable with the basics explained above, learning to use rebase is a good next step.

Save changes temporary

git stash is a good way to temporary keep changes without having to commit them, e.g. when switching branches between changes.

Use Git locally to track changes

When working on custom code for Vassal modules, other kinds of software projects, or any kind of simple plaintext documents that are not uploaded to a remote source code management system, why not create a local git repo underneath the files and use git to track all changes, be able to go back to previous versions, find out what exactly changed and when, maybe even branch and try out different things while keeping the other variations?

cd anyDirectoryWithFilesToTrack
git init .

(change files, commit, branch, look at commit history etc.)

Use Git to track changes to a Vassal Module

Since Vassal modules compress multiple image and data files into a “zipped” .vmod file, committing .vmod files directly into a git repository can be cumbersome. But you can also “unzip” the .vmod file into a directory and turn THAT directory into a git repository.

cd DirectoryYouExtractedModuleInto
git init .

(change files, commit, branch, look at commit history etc.)

You can then “build” a copy of the vmod file from the component parts using a utility like 7Zip:

del ModuleName.vmod # or rm ModuleName.vmod on Mac or Linux 7z a ModuleName.vmod *.xml -mx1 -tzip 7z a ModuleName.vmod *.html -mx1 -tzip 7z a ModuleName.vmod *.vsav -mx1 -tzip 7z a ModuleName.vmod moduledata -mx1 -tzip 7z a ModuleName.vmod help\ -r -mx1 -tzip 7z a ModuleName.vmod images\ -r -mx1 -tzip # any other lines needed to add custom code or other files into the module # a = add to archive # -mx1 = fastest compression # -tzip = zip archive format

When you use the VASSAL Editor on your module, you will then need to re-extract the new buildFile.xml and moduledata into your repo directory so that you can update them in git. Likewise any other files you have added to the module in the Editor (e.g. image files) should be likewise extracted and put in the correct place in the repo’s directory structure – then “git add” and “git commit” (and “git push” if you are working with your own github.com remote repository) to commit your changes.

1 Like