This page will give you a quickstart guide to git and the MINIX 3 git workflow.

A general Git quickstart site: gitready.com.

Git Basics

Git is a distributed version control system. As such, you don't just checkout a specific revision of your project as in subversion. Instead, you make a clone of the entire repository, including all history. You can 'commit' changes locally to your repository, but will need to 'push' them if you want your changes into a remote repository or, in our case, the main minix git repository. The workflow is that you edit files as normal, use 'git add' to add files, and 'git commit' to commit to your locally cloned repository. To push your changes to the master repository, do a 'git push.'

Installing Git

Git is available in pkgsrc. You can install git by running the following command as root:

# pkgin up
# pkgin in git-base

If you would like to clone the MINIX 3 repository on a non-MINIX platform, please the Git Book Chapter 2: Installing Git.

Configuring Git

Now, you will need to set your git config so that your full name and email address (that git will use in commit messages) are correct.

$ git config --global user.name "John Doe"
$ git config --global user.email "johndoe@minix3.org"

<!> Note: MINIX 3 developers should ideally use their minix3.org email addresses.

We want to rebase branches on pull, so do:

$ git config --global branch.autosetuprebase always

If you cloned your repository before setting this option, you must re-clone it. Or you can set this manually in the per-repo config, but that syntax isn't described here.

Also, useful:

$ git config --global color.ui auto

This will create a .gitconfig in your home directory. If you would like to send patches via email, the following snippet will also be useful. You can also use 'git config' to set each variable.

[sendemail]
        smtpuser = john.doe@gmail.com
        smtpserver = smtp.gmail.com
        smtpserverport = 587
        smtpencryption = tls

Please consult the Git documentation for more information.

Also, git relies on the local date and time being correct to make sensible commit messages; so please check your time and timezone are sensible.

Cloning the repository

For Developers

Assuming you have push access to the repository, you would clone the main git repository like so:

$ git clone gitosis@git.minix3.org:minix

To clone one of the developer repos, you would do the following. This step is optional. You only need to do it if you you want to use one of the developer repos. Replace minix-sandbox with a repository that actually exists.

$ cd minix
# Add personal repo as a git remote
$ git remote add minix-sandbox gitosis@git.minix3.org:minix-sandbox
# Fetch the remote minix-sandbox repo's contents
git fetch minix-sandbox
# Create a tracking branch called "sandbox" that tracks the "sandbox" branch in the minix-sandbox repository
# and checkout the local "sandbox" branch
git checkout --track minix-sandbox/sandbox

So, you will create a branch in your local repository called "sandbox." This branch will track the "sandbox" branch in the remote repository minix-sandbox. See minix-sandbox on gitweb.

For community members

Community members can clone like so:

$ git clone git://git.minix3.org/minix

Then, you can push changes to gitorious, github, etc. Please see the github and gitorious docs for more info.

To clone the developer repos, you would do the following. This step is optional. You only need to do it if you you want to use one of the developer repos.

$ cd minix
# Add developer repo as a git remote
$ git remote add minix-sandbox git://git.minix3.org/minix-sandbox
# Fetch the remote minix-sandbox repo's contents
git fetch minix-sandbox
# Create a tracking branch called "sandbox" that tracks the "sandbox" branch in the minix-sandbox repository
# and checkout the local "sandbox" branch
git checkout --track minix-sandbox/sandbox

Basic Git Workflows

If you plan on just using the main minix3 repository, use workflow 1. If you want to use your own long-lived feature branch (e.g., the 'sandbox' branch in the 'minix-sandbox' repository, the 'smp' branch in the 'minix-smp' repository), you should use workflow 2. The workflows are different, so please read carefully.

Workflow 1: Using the main MINIX repo

If you plan on using this workflow, you might consider using TopGit instead. It does all the git magic for you. This workflow requires you to have commit access to the MINIX repository, so community members should use the workflow 2 instead.

Creating a Topic Branch

First, you will create a topic (short-lived) branch for your work. The branch will be named bug_fix. This branch is a local branch. It does not exist on the remote repository unless you explicitly tell git to create a branch in the remote repository.

$ cd minix
# checkout the master branch first, so bug_fix branch will be based on master
$ git checkout master
$ git checkout -b bug_fix

<!> Note: you should create a new branch for every bug/feature/experiment/idea you have. Branches are cheap. We want to keep the public branches in your repositories (e.g., the "master" branch in the main repo, the "sandbox" branch in minix-sandbox repo, the "smp" branch in minix-smp repo) pristine, so please use a local (private) topic branch.

Committing to Your Local Repo

Now, you can edit files using your text editor of choice. When you want to add a new file, you will type:

# Edit some files
$ vim blah.c
# Create a new file
$ touch file1
# Add the new file to your local git repository
$ git add file1

Before you commit, you should review your changes by doing:

$ git diff

Then, you can commit to your local repository using:

$ git commit -a -m"commit message"

For git beginners, you should use the '-a' flag for 'commit'. It allows you to ignore git's staging area. Once you become more familiar with git, you'll come to appreciate the staging area.

<!> Note: The first line of your commit message should describe your change in a small sentence (50 chars or less). Add more details after one newline. Wrap the commit at 72 characters.

You can examine your commit by doing:

$ git show

Updating your Branch

To update your branch with the changes from the origin's master branch:

# update master with contents from remote repo
$ git checkout master
$ git pull --rebase

# rebase your changes on top of the new commits from master
$ git checkout bug_fix
$ git rebase master

Pushing Your Changes

When you're ready to push your changes to the master branch:

# checkout the master branch and merge in your changes
$ git checkout master
# merge bug_fix branch into master, collapsing all commits
$ git merge --squash bug_fix
# Now commit the changes
$ git commit -a -m"Commit message"
# See what the commit looks like
$ git show
# Before you push, see which commits you're about to push
$ git log origin/master..master
# dry-run of git push to make sure everything's ok
$ git push --dry-run
# push to the master branch in the remote repo
$ git push

Note: this will collapse all the changes in your 'bug_fix' branch into one commit to master.

If the push fails, it's most likely because master is out of date. You can update master by doing:

$ git pull --rebase
$ git push

You can delete your bug_fix branch now if you like.

TopGit makes this workflow a bit simpler.

Workflow 2: Using Your Developer Repo

Creating a Topic Branch

First, you will create a topic (short-lived) branch for your work. The branch will be named bug_fix. This branch is a local branch. It does not exist on the remote repository unless you explicitly tell git to create a branch in the remote repository.

$ cd minix
# checkout the sandbox branch first, so bug_fix branch will be based on sandbox
$ git checkout sandbox
$ git checkout -b bug_fix

<!> Note: you should create a new branch for every bug/feature/experiment/idea you have. Branches are cheap. We want to keep the public branches in your repositories (e.g., the "master" branch in the main repo, the "sandbox" branch in minix-sandbox repo, the "smp" branch in minix-smp repo) pristine, so please use a local (private) topic branch.

Committing to Your Local Repo

Now, you can edit files using your text editor of choice. When you want to add a new file, you will type:

# Edit some files
$ vim blah.c
# Create a new file
$ touch file1
# Add the new file to your local git repository
$ git add file1

Before you commit, you should review your changes by doing:

$ git diff

Then, you can commit to your local repository using:

$ git commit -a -m"commit message"

For git beginners, you should use the '-a' flag for 'commit'. It allows you to ignore git's staging area. Once you become more familiar with git, you'll come to appreciate the staging area.

<!> Note: The first line of your commit message should describe your change in a small sentence (50 chars or less). Add more details after one newline. Wrap the commit at 72 characters.

You can examine your commit by doing:

$ git show

Updating Your Branch

To update your local sandbox branch with changes from the main repo:

# update local master branch with contents from remote repo
$ git checkout master
$ git pull --rebase

# Merge the master branch into your local sandbox branch
$ git checkout sandbox
$ git merge master

<!> Note: This must be a merge. Otherwise, the SHA1 hashes will change, and it will cause problems if you push this branch.

To update your branch with changes from the developer repo:

# update sandbox with contents from remote repo
$ git checkout sandbox
$ git pull --rebase

# rebase your changes on top of the new commits from sandbox
$ git checkout bug_fix
$ git rebase sandbox

<!> Note: You can rebase here, since you will not be pushing the bug_fix topic branch.

Pushing Changes to Your Developer Repo

Once you're done editing your branch, you can merge it into the 'sandbox' branch and push to the remote repo's 'sandbox' branch:

# checkout the sandbox branch and merge in your changes
$ git checkout sandbox
# merge bug_fix branch into sandbox, collapsing all commits
$ git merge --squash bug_fix
# Now commit the changes
$ git commit -a -m"Commit message"
# See what the commit looks like
$ git show
# Before you push, see which commits you're about to push
$ git log minix-sandbox/sandbox..sandbox
# dry-run of git push to make sure everything's ok
$ git push --dry-run
# push to the sandbox branch in the remote repo
$ git push

Note: this will collapse all the changes in your 'sandbox' branch into one commit to master.

If the push fails, it's most likely because your local 'sandbox' branch is out of date. You can update master by doing:

$ git pull --rebase
$ git push

Pushing Changes to the Main Repo

Now, if you want to push changes to master, you can cherry-pick changes from your 'sandbox' branch:

$ git checkout master
# cherry pick the commit with ID 140abcde
$ git cherry-pick 140abcde
# dry-run of git push to make sure everything's ok
$ git push --dry-run
$ push to the master branch in the origin repo
$ git push

Or if you want all the changes on the branch

$ git checkout master
# merge local 'sandbox' branch into local 'master' branch, collapsing all commits
$ git merge --squash sandbox
# Now commit the changes
$ git commit -a -m"Commit message"
# See what the commit looks like
$ git show
# Before you push, see which commits you're about to push
$ git log origin/master..master
# dry-run of git push to make sure everything's ok
$ git push --dry-run
$ push to the master branch in the origin repo
$ git push

Note: this will collapse all the changes in your 'sandbox' branch into one commit to master.

Merging

Fast-forwards. What's that? Well, it's a simple concept but you can't really tell from the name. It's just a word for a set of commits that can be applied to someone else's current state by simply replaying them, i.e. without merging. The changes are a pure extension. If you pull someone else's changes in, and you have no locally committed changes, then that pull will simply be a fast-forward. More significantly, if you want to push your own changes, but you're pushing to a place that has diverged, i.e. received commits since you started making your local commits, git will complain if the change does not result in a fast-forward, i.e. the change has to be merged with the other change. Do this by pulling over the new changes, then pushing again, effectively rebasing your working copy. Put simply, pull has the power to merge (it's fetch + merge), push does not.

Committing

To commit, use git commit -a, which is the equivalent of svn commit, i.e adding and committing changes in all tracked files:

Once you become more comfortable with git, you can skip the '-a' and make full use of the staging area. The staging area is where you stage your current commit before it is actually committed to the repository. If you change or add files in/to a working directory, 'git commit' still won't do anything. You have to explicitly say 'git add' to added files and changed files. Then they get added to the staging area, and you can commit them. This also means there are is no single 'svn diff' equivalent, something I use quite a lot to see 'what am i about to commit.' There are two git diffs: 'git diff' shows you the differences between your working copy and the staging area, i.e. the difference between your working copy and the staged copy. 'git diff --staged' shows you what *staged* changes there are between the Staging Area and the latest commit, i.e. what you are about to commit. Most of the time the staging area and the current commit will have the same copies. You will probably use 'git diff' to find out what changes you have made to the working copy, then 'git add' to put those changes into the Index, then 'git commit' to put those changes into a new commit.

If you want to get something unstaged, do 'git reset.' If you want to get the original file back before your changes, do 'git checkout <file>.'

If you want to modify a commit, you can use:

# Redo a commit (if you forgot a file, for instance)
$ git commit -a -m"Awesome commit" --amend
# Undo a commit (leaving changes in working tree)
$ git reset HEAD^

Inspecting Your Git Repository

If you want to inspect the repository, you can use 'git log':

# Standard log
$ git log
# Also shows a diffstat
$ git log --stat
# Also shows the patch
$ git log -p
# Shorter history (shows first line of commits)
$ git log --summary
# Another format
$ git whatchanged

You can use 'git diff' to see what local changes you've made:

# Diff your local tree against the last commit
$ git diff
# Diff your staged changes against the last commit
$ git diff --staged

To find out what branches are in your repository:

# List only local branches
$ git branch
# Also list remote branches
$ git branch -a

You can search the repository using 'git grep':

# grep entire repo
$ git grep kernel_call
# grep only subdir
$ git grep kernel_call ./kernel

Git Policies and Recommendations

  • Don't work on the master branch or any public branches directly. Create a local branch instead, so you can keep the public branches clean.
  • Don't rebase commits that you've pushed to a public repository. That is, on long-lived feature branches (e.g., sandbox), you shouldn't rebase against other branches.
  • Don't make unnecessary merge commits. Always do a git pull --rebase

  • Don't use 'git push --force'
  • Don't commit extraneous whitespace. Before you do a commit, you should do a 'git diff' and make sure you don't see any red.
  • Use the commit message format from Pro Git, especially the part about the first line being only 50 characters or less:

Short (50 chars or less) summary of changes

More detailed explanatory text, if necessary.  Wrap it to about 72
characters or so.  In some contexts, the first line is treated as the
subject of an email and the rest of the text as the body.  The blank
line separating the summary from the body is critical (unless you omit
the body entirely); tools like rebase can get confused if you run the
two together.

Further paragraphs come after blank lines.

 - Bullet points are okay, too

 - Typically a hyphen or asterisk is used for the bullet, preceded by a
   single space, with blank lines in between, but conventions vary here
  • Use your minix3.org email address when committing.

Git web interface and Mirrors

There is git web interface at http://git.minix3.org.

There are also mirrors on repo.or.cz:

TODO: Add gitorious mirror

Granting Commit Access

We are using gitosis to do our account management for developers and those who need commit access to our repostiories. Gitosis is a very thin layer between git on unix and providing read/write access to many other users. Not even the Gitosis admin(s) need unix access in order to add/remove users and groups, add git repositories, and manage access. Users are identified by ssh keys. Gitosis is controlled by cloning and committing to its config file and keyfile directory.

If you have access (i.e. a Gitosis admin has added your keyfile, and has given your key Gitosis admin powers), clone the Gitosis config and update it like so. The example is adding someone's keyfile. The git repository syntax implies ssh. the unix user is always Gitosis, and users are identified using which ssh keys were used to connect.

$ git clone gitosis@git.minix3.org:gitosis-admin
$ cd gitosis-admin
$ cp ~/user1.pub keydir
$ vi gitosis.conf
(refer to this user as 'user1' in adding him to groups or repository lines)
$ git add keydir/user1.pub gitosis.conf
$ git commit
$ git push

For more information: http://wiki.archlinux.org/index.php/Gitosis

Making Your Own Private Repository

To keep your personal working branches easily stored in more than once place, it is suggested to do this in your own repository (git book chapter here). You can easily rebase your work on the master one, and merge your changes with master.

To do this in Gitosis, add a repository in gitosis.conf and let the minix group write to it, in this example minix-beng. Change into this:

[group minix]
members = @gitosis-admin
writable = pkgsrc minix minix-beng

And add this:

[repo minix-beng]
daemon = yes
description = Ben's repo
owner = Ben

Now, to fill your repository with the master repository, do this:

$ git clone gitosis@git.minix3.org:minix
$ cd minix

To tell git about your remote repository and push the contents of the minix3-beng branch there:

$ git remote add minix-beng gitosis@git.minix3.org:minix-beng
$ git push minix-beng master

Now, you have your own repository.

Advanced Git

Mailing Patches

Use 'git send-email' or TopGit'

Rebasing

For local branches that you will not be pushing, you can rebase your changes against master. This will preserve a linear history.

$ git checkout beng
$ git rebase master

Doing countless little experimental commits, e.g. to save your work remotely before doing a risky experiment, which would look messy in svn, is no problem; you can always rewrite history and clean up the commit history squeaky clean (i.e. reorder commits, merge commits together, take a commit right out) before merging your work with master. The swiss army chainsaw of commit editing is git rebase. See especially git rebase -i. But see also the warning about this changing commit id's - you can only do this if you're the only user of your branch. problems with rewriting history.

TopGit

TopGit is recommended. See the TopGit Readme and this TopGit workflow.

Basic workflow:

  • tg create wip/<patch_name>

  • Edit .topmsg (Will become commit message and your patch email) and .topdeps (list all patches/topic branches this patch depends upon)
  • git commit -a -m"inital patch version"
  • Keep modifying your patch and making commits
  • To update your branch from master, do a tg update (TopGit will recursively update all branches this branch depends upon)

  • To mail out your patch: tg mail
  • To push to the main repo: tg export submit/<patch_name> (This will collapse the branches like --squash)

  • Then, git checkout master
  • git merge submit/<patch_name>

  • git push

Further Reading

If you want to learn more about git, check out the Pro Git book and the Gitcast Videos. If you want to really get how git works under the hood, you should read Git From the Bottom Up.

Here's the full list of git resources:

MinixWiki: DevelopersGuide/UsingGit (last edited 2014-10-23 07:59:58 by MaxFrisch)