Hi gang!
Editorial
As I've mentioned a few (too many?) times in the past already I'm a pretty vivid Minecraft player and last weekend that got me into contact with some GitHub repositories. One project provided an easy way to compile it but unfortunately the process didn't work smoothly on FreeBSD. However, it was also easily fixed so I figured I'd try to do something back for both said project and FreeBSD in general. That led up to me doing a crash course in Git (and GitHub) to make this work (I had this on my todo for quite a while already).
The whole process was quite impressive and although you won't see me mention anything about one product being better than the other I do have to admit that I see many good uses for Git. Even for system administrators such as myself! I'm well aware that there's already some solid documentation available (the gittutorial(7) for example) and it's all provided out of the box. But I was still looking for something a bit more focused on FreeBSD and perhaps also something a little more descriptive. I've become quite enthusiastic about Git (I just finished porting my local Subversion repositories to Git this evening) so I figured I'd share some of the fun. However... As always I'll do my best not to let bias slip into this piece, but some slight favoritism might be unavoidable
What is Git? (general introduction)
Git is a VCS program, in other words a Version Control System. Version control means that you can work on a project (consisting of a file or a whole collection of them) and while you progress in updating said project you make sure to save and describe your changes along the way. This doesn't merely allow you to keep track of what you did in the past, it also allows you to undo any changes.
For example... Let's say we're working on a shell script and we decided to use a VCS system. There are four points in time in which we saved our work:
You could consider reverting all your changes up to point 2 but then you'd lose an awful lot of work. But a good VCS system can help deal with such disasters. Because they keep track of the changes between every step they can also save and re-apply those changes elsewhere. For example: if you save the differences between steps 2 and 3 you'd effectively end up with all your documentation. Between steps 3 and 4 you'd get the new feature.
So all you'd basically need to do is go back to undo the bug, then re-apply the rest of the changes. Not the easiest of tasks (no matter the kind of VCS you're using) but still doable. But better yet: when done right you could even have prepared yourself for such a disaster by creating (and maintaining) multiple versions of your work, often referred to as branches.
So instead of merely trying to fix a bug "just like that" you could start by creating a new branch and applying your fixes in there. Then you'd switch back to the main branch and add your documentation and the new feature for the project. Then when all things work out in the end you could merge both branches together to form the end result we have above. Or... in our example you come to the conclusion that it wasn't a bug at all, so instead of merging you simply get rid of the "test branch" and continue with the main. Or if you did perform the merge already you could undo it after which only the bug fix gets removed while all your other work would stay intact.
The one thing which makes Git so interesting for all this is because it is extremely easy to set up such a working repository of your own.
But let's start with the beginning...
Installing Git and setting up our first repository
Git is available through the Ports collection as devel/git and has quite a few configurable options. I strongly suggest to enable HTMLDOCS, especially if you run a webserver. Not only is the documentation (such as the user guide) quite good, this will also allow you to provide it on your local network. If you're currently using Subversion then you may also benefit from enabling SVN support.
Then some options which I'd personally turn off: Gitweb and CVS. There's nothing wrong with these options, but in my example we're going to use Git on the commandline, so there's no need for a web interface (see gitweb(1)). And even though I really enjoyed working with CVS back in the days it's all Subversion now. And I'd rather not add features which I know I'll never use. But as always: it's all up to personal preference.
Awesome documentation
One thing which Git definitely does better than Subversion is making its documentation a lot more accessible. It's my main gripe with Subversion: after not having used it for a while I start with svn(1) only to be told:
But.. but... I like having manual pages available! This allows me to set up the command line on my first virtual console and keep the manual page open in the second. Not to mention that
Fortunately for us Git does this "a little bit" different.
First is the main manual page, git(1), which will refer you to all the other available information. From the tutorial to the previously mentioned user guide.
The second advantage is that Git provides manual pages for all its commands. For example, if you want to check the status of your current repository you'd use:
Our first repository
So here comes the big change between Subversion and Git: unlike Subversion where everyone accesses the same repository Git actually works "decentralized". Or in plain English: Git sets up local repositories for you to use. If you need to commit a change to a remote repository (in Git terms this is called doing a 'push') then you would basically push the changes from your own local repository. So first you'd commit any changes to your local repository, then you'd push all of that onto the remote repository.
Another big difference, one I actually quite like, is that you don't need any fancy commands to set this up. Just find a directory which you'd like to put under version control. It doesn't even matter if the directory isn't empty. Then run:
The init command creates an empty repository which is then immediately available to use. This is what sets this apart from Subversion where you'd have to create the repository, and then check out ('initialize') your local environment. With Git you simply start working right away. If you run
So here we are, our own version controlled Minecraft server 
The fun part is that we can easily tell Git to track the config files (such as server.properties, the *.json files and finally runserver which is a custom script I made), and then also totally ignore the jar files. We'll just leave the other files for what they are. At least for now.
First the easy part: adding new files to our repository. Couldn't be easier:
As you can see it has added all the files with a A in front yet doesn't know what to do with the others. But we're not done yet. Right now all we did is tell Git that we want it to track these files. But we haven't actually added them yet, for that to happen we'll need to commit our changes. So:
If we run
This feature is obviously ideal for developers who work with a project where every file is a part of the whole thing. But for us it's only a bit of a hindrance, but one which is very easily remedied. We could use:
Another option is to tell Git to permanently ignore these files. We have several options for this, and the easiest is .gitignore (see also gitignore(5)). If you check its manualpage we'll learn 3 things: there are patterns being used (somewhat comparable to regular expressions), we can also use $PROJECT_DIR/.git/info/exclude to ignore entries throughout the entire project and finally there's also a configuration option available called core.excludesFile.
Now, for starters I only want to ignore the server jarfiles, so I'm going to add this to .gitignore:
So now we get to see something different:
Both jarfiles are gone, but now we have the .gitignore file itself to deal with
When working with a repository which is actually a "usable project" then I often simply add the ignore file to the repository too so that this behavior will also be used on other clients. If you don't want this then you could consider editing the previously mentioned exclude file.
Git configuration
We could also re-configure Git by changing it's current configuration. This is explained in more detail in git-config(1).
Git knows 3 different configuration levels and each supersedes the other:
For example, if we don't want Git to show any untracked files then we could consider to set the right option for it. Easily done. If you check the git-config(1) manualpage you'll soon discover status.showUntrackedFiles. If we want to set this option to no we'd use:
And if you look at the actual configuration file (.git/config) then you'll quickly see why it's also easy to edit this manually:
So far, so good. We have a local repository and if other people on the same server have access to our home directory then they can easily make a copy for themselves. For example, consider this command:
If you use Git then you don't check out a repository as with Subversion, instead you actually create a new one for your own. You're literally forking the whole project:
(for the record: this was obviously not done as root, just using the C shell)
So not only did I copy all the files, I also copied the entire backlog. This can both be a pro as well as a con, for obvious reasons.
Making changes
So how do we deal with changes? It's one thing to share something with others so that they can grab it for themselves. But lets say for the sake of argument that I don't have write access to /home/peter/snapshots, but I do have an important change which I think the repository owner should consider.
Well, easy!
First I make the change. I decided to edit ops.json and remove an entry. If we look at
But what about my first example? Where the repository is actually part of an environment which is actively in use (including several untracked files)? Also easy! Just tell Git that it should only add updated files:
Now we'll commit our changes and then we'll see something like this:
So what is happening here? As I showed you above I cloned this repository from an original: /home/peter/snapshot (called origin above). I then made several changes but those were only commited to my own, local, repository. This is also clearly visible when checking the log using
So my repository is now newer than the original. If I had access then I could push my changes onto the remote. But I don't. So we need to do something else: instead of pushing our changes we're going to do this the other way around: we'll save our changes and then request the remote operator to pull these instead.
How? By using the
).
I don't know about you but I really like it when an environment uses to-the-point yet still related terms like this. Pulling and pushing, easy! It always reminds me a bit of Java: we have Java as an environment, it uses jar files which basically store its programs and an often used programming methodology (coding standard) is to use so called 'beans' (JavaBeans). So the beans go in the jar which then make up for the Java program (to put this in an extremely simplified example).
Editorial
As I've mentioned a few (too many?) times in the past already I'm a pretty vivid Minecraft player and last weekend that got me into contact with some GitHub repositories. One project provided an easy way to compile it but unfortunately the process didn't work smoothly on FreeBSD. However, it was also easily fixed so I figured I'd try to do something back for both said project and FreeBSD in general. That led up to me doing a crash course in Git (and GitHub) to make this work (I had this on my todo for quite a while already).
The whole process was quite impressive and although you won't see me mention anything about one product being better than the other I do have to admit that I see many good uses for Git. Even for system administrators such as myself! I'm well aware that there's already some solid documentation available (the gittutorial(7) for example) and it's all provided out of the box. But I was still looking for something a bit more focused on FreeBSD and perhaps also something a little more descriptive. I've become quite enthusiastic about Git (I just finished porting my local Subversion repositories to Git this evening) so I figured I'd share some of the fun. However... As always I'll do my best not to let bias slip into this piece, but some slight favoritism might be unavoidable

What is Git? (general introduction)
Git is a VCS program, in other words a Version Control System. Version control means that you can work on a project (consisting of a file or a whole collection of them) and while you progress in updating said project you make sure to save and describe your changes along the way. This doesn't merely allow you to keep track of what you did in the past, it also allows you to undo any changes.
For example... Let's say we're working on a shell script and we decided to use a VCS system. There are four points in time in which we saved our work:
- Added our script to the repository.
- Found a bug and fixed it.
- Added some much required documentation.
- Added a new feature: the script can now actually be stopped!
You could consider reverting all your changes up to point 2 but then you'd lose an awful lot of work. But a good VCS system can help deal with such disasters. Because they keep track of the changes between every step they can also save and re-apply those changes elsewhere. For example: if you save the differences between steps 2 and 3 you'd effectively end up with all your documentation. Between steps 3 and 4 you'd get the new feature.
So all you'd basically need to do is go back to undo the bug, then re-apply the rest of the changes. Not the easiest of tasks (no matter the kind of VCS you're using) but still doable. But better yet: when done right you could even have prepared yourself for such a disaster by creating (and maintaining) multiple versions of your work, often referred to as branches.
So instead of merely trying to fix a bug "just like that" you could start by creating a new branch and applying your fixes in there. Then you'd switch back to the main branch and add your documentation and the new feature for the project. Then when all things work out in the end you could merge both branches together to form the end result we have above. Or... in our example you come to the conclusion that it wasn't a bug at all, so instead of merging you simply get rid of the "test branch" and continue with the main. Or if you did perform the merge already you could undo it after which only the bug fix gets removed while all your other work would stay intact.
The one thing which makes Git so interesting for all this is because it is extremely easy to set up such a working repository of your own.
But let's start with the beginning...
Installing Git and setting up our first repository
Git is available through the Ports collection as devel/git and has quite a few configurable options. I strongly suggest to enable HTMLDOCS, especially if you run a webserver. Not only is the documentation (such as the user guide) quite good, this will also allow you to provide it on your local network. If you're currently using Subversion then you may also benefit from enabling SVN support.
Then some options which I'd personally turn off: Gitweb and CVS. There's nothing wrong with these options, but in my example we're going to use Git on the commandline, so there's no need for a web interface (see gitweb(1)). And even though I really enjoyed working with CVS back in the days it's all Subversion now. And I'd rather not add features which I know I'll never use. But as always: it's all up to personal preference.
Awesome documentation
One thing which Git definitely does better than Subversion is making its documentation a lot more accessible. It's my main gripe with Subversion: after not having used it for a while I start with svn(1) only to be told:
Run `svn help' to access the built-in tool documentation.
But.. but... I like having manual pages available! This allows me to set up the command line on my first virtual console and keep the manual page open in the second. Not to mention that
man svn
is a lot easier to type than: svn help checkout | less
.Fortunately for us Git does this "a little bit" different.
First is the main manual page, git(1), which will refer you to all the other available information. From the tutorial to the previously mentioned user guide.
The second advantage is that Git provides manual pages for all its commands. For example, if you want to check the status of your current repository you'd use:
git status
. Therefor its manual page is git-status(1), easy huh? This is fully comparable to pkg(8); if you want to learn more about the available options when using pkg info
then you'd check pkg-info(8), couldn't be easier! And easy is good because this also helps making this a lot more accessible.Our first repository
So here comes the big change between Subversion and Git: unlike Subversion where everyone accesses the same repository Git actually works "decentralized". Or in plain English: Git sets up local repositories for you to use. If you need to commit a change to a remote repository (in Git terms this is called doing a 'push') then you would basically push the changes from your own local repository. So first you'd commit any changes to your local repository, then you'd push all of that onto the remote repository.
Another big difference, one I actually quite like, is that you don't need any fancy commands to set this up. Just find a directory which you'd like to put under version control. It doesn't even matter if the directory isn't empty. Then run:
git init
, and you're done. Want to know more about what this command does? Remember: git-init(1) will tell you all you need to know. But so will my tutorial 
The init command creates an empty repository which is then immediately available to use. This is what sets this apart from Subversion where you'd have to create the repository, and then check out ('initialize') your local environment. With Git you simply start working right away. If you run
git status
you'll notice a few things.. It will mention that you're on branch master, it tells you that you haven't got any commits yet and finally if there are already some files in the directory then those will be listed in red as 'Untracked files':
Code:
unicron:/home/peter/snapshot $ git status
On branch master
No commits yet
Untracked files:
(use "git add <file>..." to include in what will be committed)
banned-ips.json
banned-players.json
crash-reports/
eula.txt
logs/
minecraft_server.18w07c.jar
minecraft_server.18w08b.jar
ops.json
runserver
server.properties
usercache.json
whitelist.json
world/
nothing added to commit but untracked files present (use "git add" to track)

The fun part is that we can easily tell Git to track the config files (such as server.properties, the *.json files and finally runserver which is a custom script I made), and then also totally ignore the jar files. We'll just leave the other files for what they are. At least for now.
First the easy part: adding new files to our repository. Couldn't be easier:
git add server.properties *.json runserver
. If we run git status
again we'll see something completely different (I used the --short option to keep things easier to look at):
Code:
unicron:/home/peter/snapshot $ git status -s
A banned-ips.json
A banned-players.json
A ops.json
A runserver
A server.properties
A usercache.json
A whitelist.json
?? crash-reports/
?? eula.txt
?? logs/
?? minecraft_server.18w07c.jar
?? minecraft_server.18w08b.jar
?? world/
git commit
. You'll be taken into an editor (vi by default) and asked to add a commit message. The file you're editing will show you an overview of all the currently available files; both tracked and untracked. Type a description, save your edit and then we're done. Now we added our first files to our repository.If we run
git status
again you'll notice that we're still on branch master, and the only thing Git talks about are all the other untracked files. There are several ways how we can handle those. First we can simply ignore this and also tell Git not to show this to us. The option for untracked files is -u and it can use three values: no (don't show untracked files), normal (default, show untracked files in the current directory) and all (show all untracked files throughout your entire project, which can be extremely slow).This feature is obviously ideal for developers who work with a project where every file is a part of the whole thing. But for us it's only a bit of a hindrance, but one which is very easily remedied. We could use:
git status -uno
after which no untracked files will be shown.Another option is to tell Git to permanently ignore these files. We have several options for this, and the easiest is .gitignore (see also gitignore(5)). If you check its manualpage we'll learn 3 things: there are patterns being used (somewhat comparable to regular expressions), we can also use $PROJECT_DIR/.git/info/exclude to ignore entries throughout the entire project and finally there's also a configuration option available called core.excludesFile.
Now, for starters I only want to ignore the server jarfiles, so I'm going to add this to .gitignore:
Code:
/minecraft_server.18w???.jar
Code:
unicron:/home/peter/snapshot $ git status -s
?? .gitignore
?? crash-reports/
?? eula.txt
?? logs/
?? world/

Git configuration
We could also re-configure Git by changing it's current configuration. This is explained in more detail in git-config(1).
Git knows 3 different configuration levels and each supersedes the other:
- Local; this is basically the configuration as defined by the repository itself. The used filename is: $PROJECT_DIR/.git/config.
- Global; this configuration file determines how Git should behave for the current user. Keep in mind though that a local configuration will supersede a global one. The used filename is: $HOME/.gitconfig.
- System; this configuration file determines the behavior for the entire system. So all users (and processes) which run Git. As before it will be overruled by another level mentioned above. The used filename on FreeBSD is: /usr/local/etc/gitconfig.
git config -e
to edit the configuration file. It will use the local configuration by default, but you can also specify --global or --system. Now, I personally prefer this option because I'm used to messing around in config files, but if you don't like the idea you could also simply set (or remove) the individual options yourself.For example, if we don't want Git to show any untracked files then we could consider to set the right option for it. Easily done. If you check the git-config(1) manualpage you'll soon discover status.showUntrackedFiles. If we want to set this option to no we'd use:
git config --add status.showUntrackedFiles no
, and done. The change will be applied immediately:
Code:
$ git status
On branch master
nothing to commit (use -u to show untracked files)
Code:
$ less .git/config
[core]
repositoryformatversion = 0
filemode = true
bare = false
[status]
showUntrackedFiles = no
git clone /home/peter/snapshot mcsnap
.If you use Git then you don't check out a repository as with Subversion, instead you actually create a new one for your own. You're literally forking the whole project:
Code:
% git clone /home/peter/snapshot mcsnap
Cloning into 'mcsnap'...
done.
% cd mcsnap/
% git log
commit b88a24c97937d84d5eb09df6b672627a7900a005 (HEAD -> master, origin/master, origin/HEAD)
Author: ShelLuser <pl@intranet.lan>
Date: Wed Feb 28 21:03:25 2018 +0100
Setting it up for the server jarfiles to be ignored.
commit 4d0d108dfdb1639cc4d6f343b5f9888c9120fef6
Author: ShelLuser <pl@intranet.lan>
Date: Wed Feb 28 20:43:26 2018 +0100
First init of the snapshot server.
% ls -F
banned-ips.json runserver* whitelist.json
banned-players.json server.properties
ops.json usercache.json
So not only did I copy all the files, I also copied the entire backlog. This can both be a pro as well as a con, for obvious reasons.
Making changes
So how do we deal with changes? It's one thing to share something with others so that they can grab it for themselves. But lets say for the sake of argument that I don't have write access to /home/peter/snapshots, but I do have an important change which I think the repository owner should consider.
Well, easy!
First I make the change. I decided to edit ops.json and remove an entry. If we look at
git status
it will now tell us that we have a modified file. Now, there are 2 good ways to add the changes to your repository. If, in the situation above, the whole directory is a part of the repository then you can simply add all files: git add .
. This will check the current directory (and subdirs.) and then adds all changed files.But what about my first example? Where the repository is actually part of an environment which is actively in use (including several untracked files)? Also easy! Just tell Git that it should only add updated files:
git add -u
.Now we'll commit our changes and then we'll see something like this:
Code:
% git status
On branch master
Your branch is ahead of 'origin/master' by 1 commit.
(use "git push" to publish your local commits)
git log
:
Code:
% git log
commit 2eb95bf7ddcc87d6f4c32fc7f15c58626ede149f (HEAD -> master)
Author: TheOtherShell <peter@localhost>
Date: Wed Feb 28 21:57:56 2018 +0100
Removed AyanamiKun as operator.
commit b88a24c97937d84d5eb09df6b672627a7900a005 (origin/master, origin/HEAD)
Author: ShelLuser <pl@intranet.lan>
Date: Wed Feb 28 21:03:25 2018 +0100
Setting it up for the server jarfiles to be ignored.
commit 4d0d108dfdb1639cc4d6f343b5f9888c9120fef6
Author: ShelLuser <pl@intranet.lan>
Date: Wed Feb 28 20:43:26 2018 +0100
First init of the snapshot server.
How? By using the
git request-pull
command! Which I'll explain in part II (message was too long 
I don't know about you but I really like it when an environment uses to-the-point yet still related terms like this. Pulling and pushing, easy! It always reminds me a bit of Java: we have Java as an environment, it uses jar files which basically store its programs and an often used programming methodology (coding standard) is to use so called 'beans' (JavaBeans). So the beans go in the jar which then make up for the Java program (to put this in an extremely simplified example).