-
Notifications
You must be signed in to change notification settings - Fork 39
Git Environment
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.
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
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
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.
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
To establish a secure connection between your computer and GitHub see detailed instructions at https://github.com/settings/keys.
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
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
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
.
- 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 orfeature/control_10kHz
might define a new control loop that runs at 10kHz instead of 1kHz.)
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.
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 justImproved 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 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 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.
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
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).
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).
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