Git
Article by
on April 6, 2012, last modified on May 6, 2014Configuration
Color
Adding color is a must:
$ git config --global color.ui true
Prevent Pushing to Master
Option 1: update hook
In the event that you want everyone to be prevented from pushing to master, say if you are using the Integration-Manager workflow and want to implement the policy in Git, you can add a file "update" to the .git/hooks folder that contains the following code:
#!/bin/bash if [ $USER != "gitadmin" ] then if [ "$1" == "refs/heads/master" ] then echo "Manual pushing to this repo is restricted" exit 1 fi fi
Replace "gitadmin" with whatever user you want to allow push access to master. Also, make sure this file is executable. This is a modification of one that I found on ServerFault: http://serverfault.com/a/298206
Option 2: set alternate push url in config
The following will prevent pushing to a remote entirely:
$ git remote set-url --push origin no-push
Now, whenever you attempt to push it will give a fatal error:
$ git push fatal: 'no-push' does not appear to be a git repository fatal: The remote end hung up unexpectedly
Checkout
One of Git's most glorious features is branching. git-checkout is how you switch between branches. The default branch is master, so you will see master as the checked out branch by default:
$ git branch * master
To checkout a remote branch, you can do so in a variety of ways. As Nick Quaranto shows on his blog, the quickest and easiest way to check out a remote branch is:
$ git checkout -t origin/develop
This checks out the branch and tracks it. If you don't track it, then when you use git-pull you will have to specify what branch you are pulling, which I find annoying.
Checkout a Deleted File
1. Find the hash of the delete commit that deleted the file:
$ git log -n 1 -- <file_path>
2. Checkout the deleted file using the commit BEFORE the delete commit:
$ git checkout <delete_commit_hash>^ -- <file_path>
Source: http://stackoverflow.com/a/1113140/990642
Make a Branch Track a Remote Branch
By default, Git does not assume much about tracking remote branches. The chief example is when you want to check out a remote branch which I showed above. The second example is when you have a branch that you want to be a remote tracking branch. Here are your options:
Option 1: Re-Create the Branch
$ git branch -D my-branch $ git checkout -t origin/my-branch
Option 2: Set Upstream
$ git branch --set-upstream my-branch origin/my-branch
Reference: http://stackoverflow.com/a/2286030/990642
Option 3: Use the --set-upstream Flag on Git Push
$ git push --set-upstream origin my-branch
or, more simply,
$ git push -u origin my-branch
I believe that the third option is the easiest, and there was at least one instance where Option 2 didn't work and I received the following error:
$ git branch --set-upstream my-branch origin/my-branch warning: refname 'origin/my-branch' is ambiguous. fatal: Ambiguous object name: 'origin/my-branch'.
However, using Option 3 worked.
Prune Your Remote-Tracking Branches
Sometimes remote branches are deleted, but your local repo doesn't recognize that they've been deleted. So, assuming that any branch that was deleted remotely you no longer need yourself, you can "prune" them out:
$ git remote prune origin Pruning origin URL: site.com:/home/bob/MyRepo * [pruned] origin/branch1 * [pruned] origin/branch2 * [pruned] origin/branch-my-cousin-made
To be clear, this command does NOT delete any branch on the remote, only the branches locally that AREN'T on the remote.
http://stackoverflow.com/a/1072178/990642
Checkout a GitHub Pull Request
https://gist.github.com/piscisaureus/3342247
Committing
How to Write a Git Commit
Thank you Tim Pope for writing a pattern that makes sense:
Capitalized, short (50 chars or less) summary 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. Write your commit message in the imperative: "Fix bug" and not "Fixed bug" or "Fixes bug." This convention matches up with commit messages generated by commands like git merge and git revert. 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 a hanging indent
Reference: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
Change the Author and Commit Dates
$ env GIT_COMMITTER_DATE="Thu Apr 17 10:06:40 2013 -0400" git commit --amend --date="Thu Apr 17 10:06:40 2013 -0400"
Reference: http://stackoverflow.com/questions/367262/git-commit-setting-timestamps-into-the-future
Merging
Merging is the process of taking a branch and merging other branches, commits, or files into it. The two main ways of accomplishing this are git-merge and git-rebase, however, we will go over several unique cases that use other methods like git-cherry-pick.
Merge
Merging will create a merge commit. If there are conflicts Git will block out the conflicting sections of code to let you decide which parts you want to keep.
Important! By default, if a branch can be fast forwarded to complete the merge, Git will NOT create a merge commit. This is BAD news! This means that your history will look seamless and you won't be able to tell what branch a commit came from. See the section "Preserving History".
Rebase
Rebase will do the exact same thing as merge except there is no merge commit. While this can be handy, since merges are transparent it makes the history obscure, which can be difficult when trying to debug an issue.
Cherry Picking on Top of Unmerged Files
Imagine you pull and there is a conflict with only one file and that file is VERY conflicted, so much so that you don't want to resolve it. You simply want to scrap whatever is there and use a file of your choice, say from HEAD, a previous revision, or even a remote branch. What do you do?
Option 1: Checkout (reference1, reference2)
$ git checkout application/lib/Utilities.php error: path 'application/lib/Utilities.php' is unmerged
Fail!
Option 2: Hard Reset
$ git reset --hard -- application/lib/Utilities.php fatal: Cannot do hard reset with paths.
Fail again!
Option 3: Commit and Amend
- Do the pull or stash apply that creates the merge conflicts.
- Add everything (including the unmerged paths) and commit:
$ git add . $ git commit
* Make sure you remember which files were unmerged! We will overwrite them next
* Also, you can make the commit message whatever you want since we will be amending it later. If you want to make sure you keep track that this commit is a bad commit you can mark it somehow, like saying "BAD COMMIT - UNMERGED FILES!". Or, if you will immediately be ammending, you can simply put the commit message you will eventually want it to be.
- Replace the unmerged files with all the files you really wanted them to be. For example, if you really wanted the file that was in your HEAD (now HEAD~), do:
$ git checkout HEAD~ application/lib/Utilities.php
- Finally, amend your commit:
$ git commit --amend
* Here is where you will get to modify your commit message.
A few quick no brainers:
- Do NOT push unmerged files. Make sure you keep track of all the unmerged paths you added in Step 2 and overwrite them in Step 3.
- You can tell if a file is unmerged, even if git doesn't, by opening the file and looking for things like: "<<<<<<< Updated upstream".
Undo a Merge
$ git revert -m 1 HEAD
http://stackoverflow.com/a/6217372/990642
Abort a Rebase
$ git rebase --abort
Also see: http://stackoverflow.com/questions/5104649/how-to-abort-an-interactive-rebase-if-abort-doesnt-work
Preserving History
It is extremely important to preserve history when you need to debug something. The most common issue that arises is when you don't force Git to create merge commits. Here is an example: if you merge a branch that can be fast forwarded, your history by default will look like:
* 194cb58 2012-08-15 | Commit 3 (HEAD, master, feature1) [Joseph D. Purcell] * 779ed05 2012-08-03 | Commit 2 [Joseph D. Purcell] * d9a63b1 2012-08-21 | Commit 1 (origin/master, origin/HEAD) [Joseph D. Purcell]
However, if you use --no-ff=false, then you can see the commits that were part of the branch:
* c7576d9 2012-08-21 | Merge branch 'feature1' (HEAD, master) [Joseph D. Purcell] |\ | * 194cb58 2012-08-15 | Commit 3 (feature1) [Joseph D. Purcell] | * 779ed05 2012-08-03 | Commit 2 [Joseph D. Purcell] |/ * d9a63b1 2012-08-21 | Commit 1 (origin/master, origin/HEAD) [Joseph D. Purcell]
To ensure a merge commit is always made, you can add the following to your .git/config:
[merge] ff = false
Or, simply run:
$ git config merge.ff false
Source: http://stackoverflow.com/a/6810687/990642
Or, to make this config true for all your repositories add it to your ~/.gitconfig:
$ git config --global merge.ff false
Now, this will present an issue: whenever you run git pull it will attempt to create a merge commit. More than likely you don't care that you merged what was in the remote into your local copy of the branch. So, instead you can run:
$ git rebase origin/master
Or,
$ git pull --rebase origin master
Or, you can simply add the following to your .git/config:
[pull] rebase = true
Or, to have git add this to the config for you run:
$ git config pull.rebase true
Or, to make this config true for all your repositories add it to your ~/.gitconfig:
$ git config --global pull.rebase true
Or, to make this config true for only one branch in your repo:
$ git config branch.master.rebase true
If you added it to your config then every time you run a git pull it will simply rebase instead of attempting to merge, and thus create a merge commit.
Preserving Merge Commits When Rebasing
One issue to be aware of is that when you rebase, by default merge commits are squashed (see StackOverflow thread or the "Rebasing Merge Commits in Git" article by the folks at envato). As a result, when you rebase you will want to use the "--preserve-merges" flag like so:
$ git rebase --preserve-merges develop
Preserving Dates When Rebasing
There is no way to "preserve" the commit date of a commit. The commit date of a commit will always be the date and time it was committed. So, when it comes to "preserving" dates you can only do so on the "Author Date". (For a longer explanation, see this excellent comment on a thread on StackOverflow: http://stackoverflow.com/a/2976598.)
In short, Git will preserve the commit date by default. However, if you want to set the commit date and author date to be the same, you can use the --ignore-date flag as follows:
$ git rebase --ignore-date
So, for example, the following commit would be the result of a normal rebase:
$ git log -1 --pretty=fuller commit c78d2a46ff0187199ca940a8d97e3d681e0c6b95 Author: Joseph D. Purcell <email@example.com> AuthorDate: Thu Jan 3 18:13:32 2013 -0500 Commit: Joseph D. Purcell <email@example.com> CommitDate: Fri Jan 4 16:52:05 2013 -0500 This is my example commit message.
That same commit would look like the following if the --ignore-date flag was used:
$ git log -1 --pretty=fuller commit 17808d60428203752551896243c1978aa5e06c73 Author: Joseph D. Purcell <email@example.com> AuthorDate: Fri Jan 4 16:52:05 2013 -0500 Commit: Joseph D. Purcell <email@example.com> CommitDate: Fri Jan 4 16:52:05 2013 -0500 This is my example commit message.
Alex Peattie has an excellent post, "Working with dates in Git", that explains more about Git and dates.
Resolving Conflicts
Resolving Conflicts During a Merge
http://schacon.github.io/git/user-manual.html#resolving-a-merge
Resolving Conflicts During a Rebase
There must be a way to rebase and squash any conflicts produced by "ours" or "theirs". To do it on a conflict-by-conflict basis you can do:
$ git checkout --theirs file.txt
At this point if you will open the file you will see there are no conflicts, but Git still thinks there are. Do a "git add file.txt" and then "rebase --continue".
Thank you Nathan Fox for you answer I found on StackOverflow.
Dealing with Merging Hotfixes
http://stackoverflow.com/questions/10761348/git-merging-hotfix-to-multiple-branches
References
http://stevenharman.net/git-pull-with-automatic-rebase
http://schacon.github.com/git/user-manual.html#resolving-a-merge
http://babygnu.blogspot.com/2009/01/resolve-conflicts-in-git.html
http://blog.wuwon.id.au/2010/09/painless-merge-conflict-resolution-in.html
http://book.git-scm.com/3_basic_branching_and_merging.html
http://book.git-scm.com/5_advanced_branching_and_merging.html
http://www.jarrodspillers.com/2009/08/19/git-merge-vs-git-rebase-avoiding-rebase-hell/
Pushing
Pushing to a Checked Out Branch on a Non-Bare Repository
Git isn't designed to push to a branch that is checked out on a non-bare repository. However, with two tricks you can do so.
- Set the Git config of the repository to accept pushing to the branch that is checked out:
$ git config receive.denyCurrentBranch ignore
- Add a post-receive hook to force update the working directory:
$ vim .git/hooks/post-receive
And make the contents the following:
#!/bin/bash cd .. env -i git reset --hard
Finally, you will need to make it executable:
$ chmod +x .git/hooks/post-receive
Thanks to Ulrich Petri via a post by Felix Geisendörfer on "debuggable" for the post-receive hook tip, which I originally heard from Gerard Sychay, but forgot what it was.
To see an example of how to push to bare repositories that are also websites, check out Abhijit Menon-Sen's article.
Tagging
Tracking Tags
Tags are not shared by default. Read this great StackOverflow thread for more details.
Option 1: tagopt config
Setting the "tagopt" flag via the following command will ensure you are tracking tags:
$ git config remote.origin.tagopt --tags
The problem with the "tagopt" flag is that it only works for pulling tags.
Option 2: pull config
I found this idea from Jakub Narębski on a StackOverflow comment. Run the following two commands to make sure Git will both push and pull your tags:
$ git config --add remote.origin.push +refs/heads/*:refs/heads/* $ git config --add remote.origin.push +refs/tags/*:refs/tags/*
Mark Longair talks about the importance of namespacing these tags in another comment on StackOverflow, which is worth reading to know what is involved.
Staging
Stage All Files
$ git add .
Unstage All Files
$ git reset HEAD
Note: if you have unmerged files they will now show under "Changes not staged for commit" instead of "Unmerged paths". If you are unaware of this, you could accidentally commit a file with all the merge conflict information within the file, i.e. all the "<<<<<<< Updated upstream" stuff.
Stage a File
$ git add path/to/file.php
Unstage a File (reference)
$ git reset path/to/file.php
Undo a Commit
This one is tricky. There are a couple ways to go about it and it depends on what the end state you are looking for is. If you are wanting to undo a commit in a cherry-pick fashion where you leave all commits made after it, see Option 3. Options 1 and 2 undo all commits up to the commit you want to undo.
Option 1: Undo a Commit by Rolling Back the History
This option will roll the history back to a previous state, which makes it more or less impossible to use in distributed repos. If you have multiple people using the repo you are working from you don't want to do this UNLESS you are undoing a commit you haven't pushed yet. If you are undoing a commit you haven't pushed, this is the option you want.
$ git reset --hard HEAD~
To go back more than one commit do:
$ git reset --hard HEAD~10
Replace 10 with the number of commits you want to go back, a 1 meaning "go back to the commit before the last commit you made." In other words, if you roll back 10 commits then commits 1-9 will also be undone. For cherry-picking see Option 3.
NOTE: this action can be undone by viewing the reflog (git reflog
) and resetting the HEAD to the hash before the reset.
Option 2: Undo a Commit by Re-playing Changes
This is the safest bet. But, if you are undoing a commit you haven't pushed you probably want to roll back the history and completely remove the record of this action. This will undo the last commit by making an additional commit called a "revert commit" that re-plays the opposite of the changes you made, i.e. add a file if you deleted a file.
$ git revet HEAD
To undo a commit before that do:
$ git reset --hard HEAD~10
Replace 10 with the number of commits you want to undo. In other words, if you roll back 10 commits then commits 1-9 will also be undone. For cherry-picking see Option 3. Also, as a side note, I have never done a git revet on a revert commit, so I'm not sure what would happen.
NOTE: this action can be undone as well by viewing the reflog.
Option 3: Undo a Specific Commit
This is the "nicest" option in that no history is re-written. This is your only option if you are working on a branch that others will be using. The syntax is simple:
$ git revert b12c54423299ce185e79a1a07f0c5e9fe0786940
Reference: http://stackoverflow.com/a/6441962/990642
Option 4: Undo Part of a Commit
You can revert part of a commit by using the -n flag for no commit:
$ git revert -n b12c54423299ce185e79a1a07f0c5e9fe0786940
Then, reset all the files (conceptually, this means to un-stage them):
$ git reset HEAD .
Now, for each file you don't want to undo, run git checkout on the file:
$ git checkout file1.txt
Finally, you should now have only the files you want to undo all or part of what was changed. Simply open up the file and change it to your desired state, then run commit:
$ git commit
Reference: http://stackoverflow.com/a/4796144/990642
Option 5: Rebase
Rebasing is the most flexible option you have, and the most powerful. However, it involves rewriting the history which cannot happen if you are sharing the branch with others. The command is quite complex, so I will attempt to make it as simple as possible by breaking it into steps of a process that you can follow each time:
- Decide how many commits ago you will need to rewrite. This number will be N.
- Run rebase back to that commit:
$ git rebase -i HEAD~N - You will be prompted with a dialogue (similar to the commit dialogue) that asks you what actions you want to take on each commit. So, you should see a list similar to the following:
pick 61171cd A commit message 6. pick 495341a A commit message 5. pick c906e78 A commit message 4. pick 4f2cd68 A commit message 3. pick b639073 A commit message 2. pick 175e7e4 A commit message 1.
Take your time to consider each commit and what you want to do with it, starting with the oldest commit (i.e. 175e7e4) and working your way to the newest (i.e. 61171cd). (The reason for this is because even though it will process the commits top to bottom, it is easier to think about what will happen by thinking of the forward progress of changes, that is, the progress from the oldest commit to the newest.)
For each line you can do any one of the following:
- pick - will keep the commit as is
- reword - will use the commit, but will give you the commit dialogue to change the message
- edit - will use the commit, but will pause to allow you to change the working directory and will continue after you run git commit
- squash - will use the commit, but will combine it into the previous commit (previous being not in terms of chronology, but in terms of the order in the list of commits in the current dialogue you are editing)
- fixup - will use the commit and will combine it into the previous commit, but will discard this commits message and use the previous one
- exec - when this commit is reached the rebase will pause to run the command that comes after "exec" in a shell
Additionally, you can re-order the commits or delete a commit, which will completely remove that commit from the history.
- Finally, save the file and close and git will run through each line from the rebase dialogue and execute the actions you specified, and will pause where an action by you is needed.
$ git rebase --abort
Reference: http://stackoverflow.com/a/495352/990642
Clear the Working Directory
If you want to clear all changes you have made in your working directory (everything you see in git status), do this:
$ git reset --hard
NOTE: this action CANNOT be undone!!! (Yes, unfortunately I know this from experience.)
Avoiding Merge Commits
Read Linus Torvald's comments on rebasing and merging: http://www.mail-archive.com/dri-devel@lists.sourceforge.net/msg39091.html
On Merge
Avoiding merge commits on a simple merge is straightforward by passing the 'no-commit' flag:
$ git merge --no-commit feature-branch
However, this will only come into play if there are conflicts, and if there are conflicts you probably want to keep track of when they happen. So, you probably will never want to use this.
On Pull
On the other hand, every time you run a git pull it will create a merge commit, even if there are no conflicts. There are a few cons to avoiding merge commits in this case and there are a number of ways to do this, as Levent Ali points out. However, the easiest way is to pull rebase:
$ git pull --rebase
A great post by Clinton Nixon shows how you can edit your git config to make this automatic. Simply do:
$ git config branch.master.rebase true
Now, anytime you run git pull it will rebase instead of merge. Simply replace 'master' with the branch you want to automatically rebase on pull. Or you can set the config for all branches in the repository:
$ git config branch.rebase true
Or, you can set it for all repositories on your system by adding it to the ~/.gitconfig which applies only to your user:
$ git config --global branch.rebase true
However, if you don't want to rebase, you can override with the no rebase flag:
$ git pull --no-rebase origin master
Supposedly, you can also configure git to automatically rebase for all remote tracking branches:
$ git config --global branch.autosetuprebase always
Pros
- No "merge bubbles" (a photo describing this on Skitch is a must see: click here)
- Cleaner commit history (type 'git hist' and you will see these "merge bubbles")
Cons
- More difficult to undo a git pull
- Less clean reflog
Pros and Cons Explained
As mentioned, the issue of merge commits are mainly a concern with pull requests, so we will focus on that. Normally, when you pull it will create a merge commit like this one:
commit 1fe5673d3f1e6e275fd19343a345b57a07e8de65 Merge: b01af47 b9eaac2 Author: Joseph D. Purcell Date: Mon Apr 23 08:30:15 2012 -0700 Merge branch 'master' of github.com:josephdpurcell/dotfiles into master
And, your reflog will look like:
$ git reflog 1fe5673 HEAD@{0}: pull origin master: Merge made by recursive. b01af47 HEAD@{1}: <whatever your previous action was>
Then, to undo the pull you can simply do:
$ git reset --hard HEAD~
On the other hand, if you do a rebase pull you have no commit and your reflog will have a list of all the commits that you made that have not yet been pushed to the remote branch:
$ git reflog 41740ce HEAD@{0}: pull origin master: A commit I just made locally. c286d20 HEAD@{1}: pull origin master: A commit before that one that I made. b9eaac2 HEAD@{2}: checkout: moving from master to b9eaac2b4660c58ba51885897438916d1d02a679^0 b01af47 HEAD@{3}: <whatever your previous action was>
Now, if you want to undo your pull you will have to (correct me if I'm wrong) go to find whatever your previous action was in the reflog and then do a hard reset to it. You can find it by looking for the last 'checkout' and choosing the one before it. In our example above, it is 'b01af47':
$ git reset --hard b01af47
Perhaps there is an easier way, but not to my knowledge. So, as you can see, undoing pulls on rebases is a little more tricky because you have to know this process.
References:
http://mislav.uniqpath.com/2010/07/git-tips/
https://skitch.com/stevenharman/fdhm5/y-u-no-rebase
Stashing
Recover a Deleted Stash
Refer to http://stackoverflow.com/a/91795/990642.
Debugging
Git Bisect
Git bisect is a great tool to help you find the commit that introduced a bug into your system. It's the kind of tool that you will never use, but once you encounter a situation in which it is needed, you feel as though you could never live without it. First, know that I am assuming the following four things:
- You are in the root directory of the project.
- HEAD points to the most recent commit, i.e. you have not done something like git reset --hard HEAD~10
- The latest commit is bad.
- 2fcc6e20a is the last known good commit.
Now, here is how it works:
$ git bisect start $ git bisect bad $ git bisect good 2fcc6e20a
Next, repeat git bisect good
or git bisect bad
until you get a message something like:
$ git bisect good 9bc751aa7f55f7b88310b4d9fc7b6a2e1c220458 is the first bad commit commit 9bc751aa7f55f7b88310b4d9fc7b6a2e1c220458 Author: Mr. Person Man Date: Wed Jul 10 13:24:03 2012 -0400 Break the code because I think that's funny. :040000 040000 352bc21b262f079e8829b48034d30ba0c68653ff 3bbc6a60f8fd8554a533e914c5c835b22788b8eb M core
And then reset bisect to reset HEAD back to the latest commit:
$ git bisect reset
Now, you have the fun task of finding a solution to fix the change that was done. For more information on git-bisect, read Larry Garfield's article "Using git bisect for fun and profit."
Show Commits Made Directly on a Branch
See http://stackoverflow.com/a/7415282/990642. Simply do:
$ git log --first-parent --no-merges develop ^master
Git Rebase Conflicts Prevent Progress
When you are trying to rebase and you type "git add ." and you get the following:
$ git status # Not currently on any branch. nothing to commit (working directory clean)
A StackOverflow thread suggests simply skipping it:
$ git rebase --skip
And it should continue fine. The reason this happens, from what I understand, is that the exact same changes have already been applied on the branch you are rebasing onto.
"You need to run this command from the toplevel of the working tree."
This just means you need to be in the root of the repository you are in. This always shows up when I try using git-bisect.
Git Blame Shows "00000000 (Not Committed Yet 2014-04-01 20:10:44 -0400 99)"
This could be for a couple of reasons:
- make sure you are not trying to run git blame on a file that is symlinked from where you are (i.e. git blame htdocs/index.php where htdocs is a symlink to src)
- some have reported this is due to issues with line endings (StackOverflow)
- try specifying HEAD or the branch: git blame HEAD htdocs/index.php
Collaboration on Projects
Git Branching Models
Read this excellent thread on StackOverflow: http://stackoverflow.com/questions/2621610/what-git-branching-models-actually-work
http://www.kernel.org/pub/software/scm/git/docs/gitworkflows.html
http://reinh.com/blog/2009/03/02/a-git-workflow-for-agile-teams.html
http://nvie.com/posts/a-successful-git-branching-model/
Patches
A quick way to send changes to someone else is to make a patch. Ariejan de Vroom wrote a good article on "How to create and apply a patch with Git", but here is the essence:
Create a patch:
$ git format-patch HEAD~1 --stdout > patch.txt
Apply a patch:
$ git apply patch.txt
Git Submodules
I haven't delved into submodules, but here are some relevant articles:
http://chrisjean.com/2009/04/20/git-submodules-adding-using-removing-and-updating/
http://codingkilledthecat.wordpress.com/2012/04/28/why-your-company-shouldnt-use-git-submodules/
http://git-scm.com/book/en/Git-Tools-Submodules