Skip to content

Git Environment

quinnabrvau edited this page Apr 30, 2018 · 2 revisions

Install git

Linux-like systems:

Install git via your native package management system:

$ yum install git

or

$ sudo apt-get install git

Windows and Mac OS X:

The easiest way to get git is to download GitHub's software, which will install git, and also provide a nice GUI (this tutorial will be based on the command line interface). Note, you may need to go into the GitHub preferences and choose the "Install Command Line Tools" option to get git installed into the terminal.

If you do decide to use the GitHub GUI, you should make sure that any "sync does rebase" option is disabled in the settings.

Configure git settings

Git tracks who makes each commit by checking the user’s name and email. In addition, we use this info to associate your commits with your GitHub account.

To set these, enter the code below, replacing the name and email with your own (--global is optional).

$ git config --global user.name "Firstname Lastname"
$ git config --global user.email "[email protected]"

The name should be your actual name, not your GitHub username (and keep the quotation marks!).

Sometimes git will open an editor for you to input or modify information. To set your editor preference:

$ git config --global core.editor "XXXX"

where XXXX is your favorite text editor.

These global options (i.e. applying to all repositories) are placed in ~/.gitconfig. To edit the file from command line:

$ git config --global --edit

You can edit this file to add setup colors and some handy shortcuts:

[user]
    name = Firstname Lastname
    email = [email protected]

[color]
    diff  = auto
    status= auto
    branch= auto
    interactive = true

[alias]
    ci = commit
    di = diff --color-words
    st = status
    co = checkout
    log1 = log --pretty=oneline --abbrev-commit
    logs = log --stat

Mergetools

Git is setup to use a merge tool. I recommend you setup a diff and merge tool such as kdiff3 or diffmerge. On a mac a config for kdiff3 looks like:

[difftool "kdiff3"]
    path = /Applications/kdiff3.app/Contents/MacOS/kdiff3
    trustExitCode = false
[difftool]
    prompt = false
[diff]
    tool = kdiff3
[mergetool "kdiff3"]
    path = /Applications/kdiff3.app/Contents/MacOS/kdiff3
    trustExitCode = false
[mergetool]
    keepBackup = false
[merge]
    tool = kdiff3

Tune bash prompt

It can be convenient in future to tune the bash prompt to display the current git branch.

The easiest way to do it, is to add the snippet below to your .bashrc or .bash_profile:

PS1="[\u@\h \W\$(git branch 2> /dev/null | grep -e '\* ' | sed 's/^..\(.*\)/{\1}/')]\$ "

But better is to use git-completion from the git source. This also has the advantage of adding tab completion to just about every git command. It also includes many other useful features, for example, promptings. To use git-completion, first download the git source code (about 27 MiB), then copy the file to your profile directory::

$ git clone git://git.kernel.org/pub/scm/git/git.git
$ cp git/contrib/completion/git-completion.bash ~/.git-completion.sh

Read instructions in ~/.git-completion.sh

Note that if you install git from the package manager in many Linux distros, this file is already installed for you. You can check if it is installed by seeing if tab completion works on git commands (try, e.g., git commi<TAB>, or git log --st<TAB>). You can also check if the PS1 commands work by doing something like::

$ PS1='\W $(__git_ps1 "%s")\$ '

And your command prompt should change to something like::

Raven2 master$

Note, it is important to define your PS1 using single quotes ('), not double quotes ("), or else bash will not update the branch name.

Create GitHub account

As you are going to use GitHub you should have a GitHub account. If you have not one yet then sign up at: https://github.com/signup/free

Set up SSH keys

To establish a secure connection between your computer and GitHub see detailed instructions at https://github.com/settings/keys.

Fork Raven-II project

Create your own fork of the Raven-II project (if you have not yet). Go to the Raven-II GitHub repository: https://github.com/uw-biorobotics/raven2

and click the “Fork” button.

Now you have your own repository for the Raven-II project. If your username in GitHub is mynick then the address of the forked project will look something like: https://github.com/mynick/raven2

Clone Raven-II

On your machine browse to where you would like to store Raven-II, and clone (download) the latest code from Raven-II's original repository:

$ git clone git://github.com/uw-biorobotics/raven2.git
$ cd raven2

Then assign your read-and-write repo to a remote called "github":

$ git remote add github [email protected]:mynick/raven2.git

Create a new branch

Typically, you will create a new branch to begin work on a new issue. Also pull request related with them. See the next section for naming branches.

To create and checkout (that is, make it the working branch) a new branch, say issue/11:

$ git branch issue/11
$ git checkout issue/11

or in one command using :1

$ git checkout -b issue/11

To view all branches, with your current branch highlighted, type:

$ git branch

And remember, never type the following commands in master: git merge, git commit, git rebase.

Branch names

  • Briefly describe the topic of the patch or pull request.
  • Include the issue number if you know it. (e.x. issue/11 if the branch fixes issue 11 or feature/control_10kHz might define a new control loop that runs at 10kHz instead of 1kHz.)

Commits

You can check what files are changed:

$ git status

Add new files to the index if necessary:

$ git add new_file.c new_file.h

Check total changes:

$ git diff

You are ready to commit changes locally. A commit also contains a commit message which describes it. See the next section for guidelines on writing good commit messages. Type:

$ git commit

An editor window will appear automatically in this case. In Linux, this is vim by default. You can change what editor pops up by changing the $EDITOR shell variable.

Also with the help of option -a you can tell the command commit to automatically stage files that have been modified and deleted, but new files you have not told git about will not be affected, e.x.,:

$ git commit -a

If you want to stage only part of your changes, you can use the interactive commit feature. Just type:

$ git commit --interactive

and choose the changes you want in the resulting interface.

Writing commit messages

The commit message has two parts: a title (first line) and the body. The two are separated by a blank line.

Title (summary)

Commit message titles summarise what the commit does. Tools like git shortlog or even GitHub only show the first line of the commit by default, so it is important to convey the most important aspects of the commit in the first line.

  • Keep to 71 characters or less.

    This allows the one-line form of the log to display the summary without wrapping.

  • Do not end with a period (full stop).

  • Provide context for the commit if possible,

    e.g. integrals: Improved speed of heurisch() instead of just Improved speed of heurisch()

A commit won't always be seen in the context of your branch, so it is often helpful to give each commit some context. This is not required, though, as it is not hard to look at the commit metadata to see what files were modified or at the commit history to see the nearby related commits.

Try to avoid short commit messages, like "Fix", and commit messages that give no context, like "Found the bug". When in doubt, a longer commit message is probably better than a short one.

Body

Commit messages are intended for human readers, both for people who will be reviewing your code right now, and for people who might come across your commit in the future while researching some change in the code. Thus, include information that helps others understand your commit here, if necessary.

  • Make sure to leave a blank line after the summary

  • Keep all lines to 78 characters or less (so they can be easily be read in terminals which don't automatically wrap lines.)

  • Give an overview of what the commit does if it is difficult to figure out just from looking at the diff.

  • Include other relevant information, e.g.

    • Known issues
    • A concrete example (for commits that add new features/improve performance etc.)
  • Use bullet lists when suitable

  • Feel free to use Unicode characters, such as output from the Raven-II Unicode pretty printer.

  • Use plain English

Example of a good commit message

Here is an example commit message (from the commit from the sympy project):

integrals: Improved speed of heurisch() and revised tests

Improved speed of anti-derivative candidate expansion and solution
phases using explicit domains and solve_lin_sys(). The upside of
this change is that large integrals (those that generate lots of
monomials) are now computed *much* faster. The downside is that
integrals involving Derivative() don't work anymore. I'm not sure
if they really used to work properly or it was just a coincidence
and/or bad implementation. This needs further investigation.

Example:

In [1]: from sympy.integrals.heurisch import heurisch

In [2]: f = (1 + x + x*exp(x))*(x + log(x) + exp(x) - 1)/(x + log(x) + exp(x))**2/x

In [3]: %time ratsimp(heurisch(f, x))
CPU times: user 7.27 s, sys: 0.04 s, total: 7.31 s
Wall time: 7.32 s
Out[3]:
   ⎛ 2        x                 2⋅x      x             2   ⎞
log⎝x  + 2⋅x⋅ℯ  + 2⋅x⋅log(x) + ℯ    + 2⋅ℯ ⋅log(x) + log (x)⎠          1
──────────────────────────────────────────────────────────── + ───────────────
                             2                                      x
                                                               x + ℯ  + log(x)

Previously it took 450 seconds and 4 GB of RAM to compute.

Merging

Merging creates a special commit, called a "merge commit", that joins your branch and master together:

A---B---C------D       origin/master
         \      \
          \      M     merge
           \    /
            a--b       issue/11

Note that the commits A, B, C, and D from master and the commits a and b from issue/11 remain unchanged. Only the new commit, M, is added to issue/11, which merges in the new commit branch from master.

Rebasing

Rebasing essentially takes the commits from issue/11 and reapplies them on the latest master, so that it is as if you had made them from the latest version of that branch instead. Since these commits have a different history, they are different (they will have different SHA1 hashes, and will often have different content):

A---B---C---D---a'---b' origin/master

Rebasing is required if you want to edit your commit history (e.g., squash commits, edit commit messages, remove unnecessary commits). In general, if you want to rebase you should only rebase before you submit a pull request.

How to merge

First merge your local repository with the remote:

$ git checkout master
$ git pull

This results in:

A---B---C---D       master
         \
          a---b     issue/11

Then merge your issue/11 branch from issue/11::

$ git checkout issue/11
$ git merge master

If the last command tells you that conflicts must be solved for a few indicated files.

If that's the case then the marks >>> and <<< will appear at those files. Fix the code with >>> and <<< around it to what it should be. You must manually remove useless pieces, and leave only new changes from your branch.

The best way to accomplish this is to use:

$ git mergetool

Then be sure that all tests pass!

Then commit:

$ git commit

So the result will be like that (automatic merging c)::

A---B---C-------D     master
         \       \
          a---b---M   issue/11

How to rebase

If you have already made a pull request, please merge instead of rebasing.

The final aim, that we want to obtain is::

A---B---C---D           master
             \
              a---b     issue/11

The way to do it is first of all to merge local repository with the remote uw-biorobotics/raven2:

$ git checkout master
$ git pull

So we obtain::

A---B---C---D       master
         \
          a---b     issue/11

Then::

$ git checkout issue/11
$ git rebase master

Note that this last one will require you to fix some merge conflicts if there are changes to the same file in master and issue/11. Open the file that it tells you is wrong, fix the code with >>> and <<< around it to what it should be.

The best way to accomplish this is to use:

$ git mergetool

Then be sure that all tests pass!

Then do:

$ git add Raven-II/matrices/your_conflict_file
$ git rebase --continue

(git rebase will also guide you in this).

Merging vs Rebasing

It is important to note that since rebase rewrites history, it is possible to lose data, and it makes it harder for people reviewing your code, because they can no longer just look at the "new commits"; they have to look at everything again, because all the commits are effectively new.

There are several advantages to merging instead of rebasing. Rebasing reapplies each commit iteratively over master, and if the state of the files changed by that commit is different from when it was originally made, the commit will change. This means what you can end up getting commits that are broken, or commits that do not do what they say they do (because the changes have been "rebased out"). This can lead to confusion if someone in the future tries to test something by checking out commits from the history. Finally, merge conflict resolutions can be more difficult with rebasing, because you have to resolve the conflicts for each individual commit. With merging, you only have to resolve the conflicts between the branches, not the commits. It is quite common for a merge to not have any conflicts but for a rebase to have several, because the conflicts are "already resolved" by later commits.

Merging keeps everything intact. The commits you make are exactly the same, down to the SHA1 hash, which means that if you checkout a commit from a merged branch, it is exactly the same as checking it out from a non-merged branch. What it does instead is create a single commit, the merge commit, that makes it so that the history is both master and your branch. This commit contains all merge conflict resolution information, which is another advantage over rebasing (all merge conflict resolutions when rebasing are "sifted" into the commits that caused them, making them invisible).

Changing of commit messages

The only time when it is recommended to rebase instead of merge is when you need to edit your commit messages, or remove unnecessary commits.

Note, it is much better to get your commit messages right the first time. See the section on writing good commit messages above.

Consider these commit messages::

$ git log --oneline
7bbbc06 bugs fixing
4d6137b some additional corrections.
925d88fx sequences base implementation.

Then run rebase command in interactive mode::

$ git rebase --interactive 925d88fx

Or you can use other ways to point to commits, e.g. git rebase --interactive HEAD^^ or git rebase --interactive HEAD~2.

A new editor window will appear (note that order is reversed with respect to the git log command)::

pick 4d6137b some additional corrections.
pick 7bbbc06 bugs fixing

# Rebase 925d88f..7bbbc06 onto 925d88f
#
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit's log message

To edit a commit message, change pick to reword (or on old versions of git, to edit) for those that you want to edit and save that file.

To squash two commits together, change pick to squash. To remove a commit, just delete the line with the commit.

To edit a commit, change pick to edit.

After that, git will drop you back into your editor for every commit you want to reword, and into the shell for every commit you wanted to edit:

$ (Change the commit in any way you like.)
$ git commit --amend -m "your new message"
$ git rebase --continue

For commits that you want to edit, it will stop. You can then do:

$ git reset --mixed HEAD^

This will "uncommit" all the changes from the commit. You can then recommit them however you want. When you are done, remember to do:

$ git rebase --continue

Most of this sequence will be explained to you by the output of the various commands of git. Continue until it says:

Successfully rebased and updated refs/heads/master.

If at any point you want to abort the rebase, do:

$ git rebase --abort

Warning: this will run git reset --hard, deleting any uncommitted changes you have. If you want to save your uncommitted changes, run git stash first, and then run git stash pop when you are done.

For more information about GitHub forking and tuning see: 1 pull, 2 fork or 3 linux setup or 4 git-scm