The following guide is deprecated and no longer valid for the new oliver.
Git Usage Guide
Git @ i10
Install Git
Git comes with Xcode 4 or later, or you can get the Git OS X Installer package. To resolve conflicts, you will need a diff tool like the one Xcode installs.
Upload repository
- Ignore Xcode project files (standard gitignore)
- Create bare clone of your local git project and prepare for server:
cd GIT_PROJECT git clone --bare . ../PROJECT.git git update-server-info
- Copy PROJECT.git folder to oliver. Just mount afp://oliver.informatik.rwth-aachen.de/Public Review the Read me first document to find out where to place the git folder.
- Check out the repository from the server as described below (you can delete the local copy)
Check out repository via SSH
- (optional) Set up SSH public key authentication
- Check out repository:
git clone ssh://oliver.informatik.rwth-aachen.de/Public/Research%20Projects/<project name>/Software/Sources/<git>.git
Hint: You might need to add your username for oliver if it differs from your local username. Add it right before
oliver.informatik[...]
with an @ before. Example:
git clone ssh://This email address is being protected from spambots. You need JavaScript enabled to view it. /Public/Research%20Projects/<project name>/Software/Sources/<git>.git
When using Tower:
- Download Tower. We have a 30-user site license; ask Jan-Peter for details
- Select "Clone Remote Repository".
- In the Finder, drag the .git file from its folder on oliver to the RemoteURL textfield in Tower.
- Replace all whitespaces with %20.
- Replace everything before /Public/.... with ssh://oliver.informatik.rwth-aachen.de.
- Use your i10 login to download the repository.
- Double-click the resulting repository, switch to the "Browse" tab, then right-click the files there to edit them using your favorite editor.
- After changing a file, go to the Status tab, select the file to stage it, then select "Commit" to locally(!) commit the change.
- Now "Pull" to get any changes by others. If nothing happens, or merge happens automatically, you're fine, otherwise resolve merge conflicts manually.
- "Push" your merged changes into the main remote repository.
NOTE: If you want to store a repository under your user account, use ssh://This email address is being protected from spambots. You need JavaScript enabled to view it. /Volumes/Raid1/Users/USERNAME/.../REPOSITORY.git
Software Support
Versions is a SubVersion tool, so it won't work with Git. Alternatives are:
- Tower: the recommended GUI for Mac OS X, we have licenses
- GitKraken: free, available for all platforms, Aaron's recommendation
- GitX: another GUI for Mac OS X
- Sourcetree: ...
- TortoiseGit: Windows Explorer integration
- EGit: Eclipse plugin
Basic functions
Create local repository:
git init
Stage changes in files:
git add FILE1 FILE2...
Remove files:
git rm FILE1 FILE2...
See status (changed files):
git status
Commit staged changes (to the local repository):
git commit
Remote Repositories
Git stores its repository locally in the .git/ folder inside the versioned project - there is no central repository like in SubVersion. Instead, Git allows you to synchronize your repository with others directly, or through a central remote repository.
Create a remote repository:
Create a bare clone of your repository (copies only the repository):
git clone --bare PROJECT_PATH project.git
Upload project.git to
- for code: afp://oliver.informatik.rwth-aachen.de/Public/Research Projects/<your project name>/Software/Sources/<ProjectName>.git
- for paper submissions: afp://oliver.informatik.rwth-aachen.de/Public/Research Projects/<your project name>/Publications/<Conference><Year><Format>.git
Check out remote repository
git clone ssh://oliver.informatik.rwth-aachen.de/Public/Research%20Projects/<your project name>/...
After checking out a remote repository, Git remembers the URL of the repository, which is then used to synchronize between yours and the remote.
Note that this is only true for the master branch by default!
Synchronize with remote repository
Pull changes from remote repository:
git pull
Push changes to the remote repository:
git push
Note that this is only true for the master branch if your other branches are not set up to track a remote branch (see below)!
Always pull before push!
Branches and Merging
Branches are used for parallel development, where you do not want your changes to affect other people's code. The master branch is the trunk.
Managing Branches
List (local|remote|all) branches (active is marked with *):
git branch [|-r|-a]
Create branch named foo (does not change the active branch):
git branch foo
Switch to branch foo:
git checkout foo
Set up a remote branch bar on the server that inherits from the master branch
git push origin master:bar
Set up a local branch foo that is tracking an existing branch bar on the server (i.e. pushing and pulling in foo will automatically target bar on the server):
git branch --track foo origin/bar
If you want an existing local branch foo (that does not exist on the server yet) to be published to the server under the name bar and make your local branch track that new server-side branch:
git push origin foo:bar git checkout master git branch --track -f foo origin/bar
The -f switch forces the local branch to be re-created with the new tracking info. Only do this if you do not have any pending changes on foo that have not been committed!
When you are done merging (see below) and are really done with your local branch foo, you may want to de-clutter your branch list and delete it:
git branch -d foo
If you are really done with a remote branch bar and do not even want it on the server anymore:
git push origin :bar
Merging
When you are done with the branch you need to merge it with the master:
- Commit local changes
- Switch to the target branch (master), then merge from the source branch:
git checkout master git merge foo
Merges are automatically committed, unless there are conflicts.
Branching summary (when foo does not exist on server)
git push origin foo:bar -> push local foo as remote bar
git checkout master -> switch to master
git branch --track -f foo origin/bar -> create track to make push/pull easier
Rules of thumb
pull before push EVERY TIME!7
Reusable Resources
Default .gitignore
*.DS_Store ./build *.xcodeproj/*.pbxuser *.xcodeproj/*.mode1v3 *.xcodeproj/*.perspectivev3 xcuserdata
The first line is for the default Finder cache file.
The second line excludes Xcode's build folder.
The following three lines are for Xcode 3.
The last line is for Xcode 4.
up-to-date Alternative:
Use gitignore.io to create a .gitignore based on a big community on the web.
Used by a lot of famous companies ;-)
Convert SVN to GIT
If you installed git via ports you need to explicitly add svn support
port install git-core +svn +bash_completion
The following script requires git-svn. It takes two parameters via command line: URL for SVN (use svn+ssh protocol; https won't work.) and the name of your git repository (the new git repository will be created in your home directory)
#!/bin/bash SVN_PATH=${1} GIT_NAME=${2} mkdir ~/tmp cd ~/tmp git svn init $SVN_PATH --no-metadata git svn fetch cd ~ git clone --bare tmp $GIT_NAME.git rm -rf ~/tmp
Example: suppose you have the script in svnToGit.sh and the URL is svn+ssh//oliver.informatik.rwth-aachen.de/svn/bla. The following code will convert your repository to ~/newGitRepository.git
./svnToGit.sh svn+ssh//oliver.informatik.rwth-aachen.de/svn/bla newGitRepository
Troubeshooting
Entry 'foo' not uptodate. Cannot merge
$ /tmp/repo2 git pull remote: Counting objects: 5, done. remote: Compressing objects: 100% (2/2), done. remote: Total 3 (delta 0), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. From /tmp/repo1 5851692..0a5b573 master -> origin/master Updating 5851692..0a5b573 error: Entry 'foo' not uptodate. Cannot merge.
This happens when you have local changes to files that have also been changed in the repository you are pulling from. You can remove the conflicting file if you do not want to keep your changes, then pull. If you want to keep the changes, you have to commit them first, then pull (potentially resulting in a conflict - see below).
Non-fast forwards and merge conflicts
Let's create a repository in directory repo1
$ /tmp mkdir repo1 $ /tmp cd repo1 $ /tmp/repo1 git init Initialized empty Git repository in /tmp/repo1/.git/
Clone it to directory repo2
$ /tmp/repo1 cd .. $ /tmp git clone repo1 repo2 Initialized empty Git repository in /tmp/repo2/.git/ warning: You appear to have cloned an empty repository.
Now we create a file "foo" in repo1 with the following contents:
A flea and a fly in a flue Were caught, so what could they do? Said the fly, "Let us flee." "Let us fly," said the flea. So they flew through a flaw in the flue.
Then we add this file and commit.
$ /tmp cd repo1 $ /tmp/repo1 git add foo $ /tmp/repo1 git commit -am "Created file foo" [master (root-commit) be64d03] Created file foo 1 files changed, 5 insertions(+), 0 deletions(-) create mode 100755 foo
Then we create a file "bar" in repo2 and commit
$ /tmp/repo1 cd ../repo2 $ /tmp/repo2 echo whatever > bar $ /tmp/repo2 git add bar $ /tmp/repo2 git commit -am "Created file bar" [master (root-commit) 993b876] Created file bar 1 files changed, 1 insertions(+), 0 deletions(-) create mode 100644 bar
We now have two distinct modifications of the same data. Let's try to push the changes from repo2 to repo1.
$ /tmp/repo2 git push To /tmp/repo1 ! [rejected] master -> master (non-fast-forward) error: failed to push some refs to '/tmp/repo1' To prevent you from losing history, non-fast-forward updates were rejected Merge the remote changes before pushing again. See the 'non-fast-forward' section of 'git push --help' for details.
This is a non-fast forward, because our changes are not based on the current state of the target repository. We'll try to resolve this by pulling from repo1, thus incorporating repo1's changes into repo2, giving us a chance to handle any conflicts that might occur in the process.
$ /tmp/repo2 git pull warning: no common commits remote: Counting objects: 3, done. remote: Compressing objects: 100% (2/2), done. remote: Total 3 (delta 0), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. From /tmp/repo1 * [new branch] master -> origin/master Merge made by recursive. foo | 5 +++++ 1 files changed, 5 insertions(+), 0 deletions(-) create mode 100755 foo
Seems to have worked! Let's verify that.
$ /tmp/repo2 git status # On branch master # Your branch is ahead of 'origin/master' by 2 commits. # nothing to commit (working directory clean)
Alright, now we should be able to push to repo1.
$ /tmp/repo2 git push Counting objects: 6, done. Compressing objects: 100% (3/3), done. Writing objects: 100% (5/5), 516 bytes, done. Total 5 (delta 0), reused 0 (delta 0) Unpacking objects: 100% (5/5), done. warning: updating the current branch warning: Updating the currently checked out branch may cause confusion, warning: as the index and work tree do not reflect changes that are in HEAD. warning: As a result, you may see the changes you just pushed into it warning: reverted when you run 'git diff' over there, and you may want warning: to run 'git reset --hard' before starting to work to recover. warning: warning: You can set 'receive.denyCurrentBranch' configuration variable to warning: 'refuse' in the remote repository to forbid pushing into its warning: current branch. warning: To allow pushing into the current branch, you can set it to 'ignore'; warning: but this is not recommended unless you arranged to update its work warning: tree to match what you pushed in some other way. warning: warning: To squelch this message, you can set it to 'warn'. warning: warning: Note that the default will change in a future version of git warning: to refuse updating the current branch unless you have the warning: configuration variable set to either 'ignore' or 'warn'. To /tmp/repo1 be64d03..b5ee60e master -> master
What the... okay... basically it says that while the repository in repo1 itself is up to date, the working copy in repo1 is not. The working copy will appear as if it is based on the new state of the repository, though. Let's verify:
$ /tmp/repo2 cd ../repo1 $ /tmp/repo1 git status # On branch master # Changes to be committed: # (use "git reset HEAD <file>..." to unstage) # # deleted: bar #
So we see, it seems as if we deleted the file "bar" (originating from repo2) in repo1 while in reality the files are just out of date. Let's bring that working copy up to speed:
$ /tmp/repo1 git reset --hard HEAD is now at b5ee60e Merge branch 'master' of /tmp/repo1 $ /tmp/repo1 git status # On branch master nothing to commit (working directory clean) $ /tmp/repo1 ls bar foo
Now we're talking. No changes according to git and file "bar" is present.
Now, let's modify the repositories asynchronously again, but this time we'll introduce a conflict by modifying the same line twice in both repositories and commit (we change line 1 in repo1, line 5 in repo2 and line 3 in both).
$ /tmp/repo1 cat foo A flea and a fly in a flue # repo1 Were caught, so what could they do? Said the fly, "Let us flee." # repo1 "Let us fly," said the flea. So they flew through a flaw in the flue. $ /tmp/repo1 git commit -am "foo changed in repo1" [master 4f2f9d5] foo changed in repo1 1 files changed, 2 insertions(+), 2 deletions(-) $ /tmp/repo1 cd ../repo2 $ /tmp/repo2 cat foo A flea and a fly in a flue Were caught, so what could they do? Said the fly, "Let us flee." # repo2 "Let us fly," said the flea. So they flew through a flaw in the flue. # repo2 $ /tmp/repo2 git commit -am "foo changed in repo2" [master 4d5a6dd] foo changed in repo2 1 files changed, 2 insertions(+), 2 deletions(-)
We don't even try to push this time but pull right away.
$ /tmp/repo2 git pull remote: Counting objects: 5, done. remote: Compressing objects: 100% (3/3), done. remote: Total 3 (delta 1), reused 0 (delta 0) Unpacking objects: 100% (3/3), done. From /tmp/repo1 b5ee60e..4f2f9d5 master -> origin/master Auto-merging foo CONFLICT (content): Merge conflict in foo Automatic merge failed; fix conflicts and then commit the result.
Houston, we have a conflict. Git has joined the two versions and marked the conflicting differences for us:
$ /tmp/repo2 cat foo A flea and a fly in a flue # repo1 Were caught, so what could they do? <<<<<<< HEAD Said the fly, "Let us flee." # repo2 ======= Said the fly, "Let us flee." # repo1 >>>>>>> 4f2f9d50a5544d58354398fd9ad6abb5211faf27 "Let us fly," said the flea. So they flew through a flaw in the flue. # repo2
We should now edit the file appropriately ("fix conflicts", as instructed). Since we hate limericks, this is easy:
$ /tmp/repo2 echo "Not funny" > foo
We continue to follow git's instructions and "commit the result":
$ /tmp/repo2 git commit -am "Resolved a conflict, once and for all" [master 158c9f3] Resolved a conflict, once and for all $ /tmp/repo2 git status # On branch master # Your branch is ahead of 'origin/master' by 2 commits. # nothing to commit (working directory clean)
Done. Now push the changes to repo1 and we see something familar:
$ /tmp/repo2 git push Counting objects: 10, done. Compressing objects: 100% (5/5), done. Writing objects: 100% (6/6), 640 bytes, done. Total 6 (delta 1), reused 0 (delta 0) Unpacking objects: 100% (6/6), done. warning: updating the current branch warning: Updating the currently checked out branch may cause confusion, warning: as the index and work tree do not reflect changes that are in HEAD. warning: As a result, you may see the changes you just pushed into it warning: reverted when you run 'git diff' over there, and you may want warning: to run 'git reset --hard' before starting to work to recover. warning: warning: You can set 'receive.denyCurrentBranch' configuration variable to warning: 'refuse' in the remote repository to forbid pushing into its warning: current branch. warning: To allow pushing into the current branch, you can set it to 'ignore'; warning: but this is not recommended unless you arranged to update its work warning: tree to match what you pushed in some other way. warning: warning: To squelch this message, you can set it to 'warn'. warning: warning: Note that the default will change in a future version of git warning: to refuse updating the current branch unless you have the warning: configuration variable set to either 'ignore' or 'warn'. To /tmp/repo1 4f2f9d5..158c9f3 master -> master
The resolution is of course the same:
$ /tmp/repo2 cd ../repo1 $ /tmp/repo1 git reset --hard HEAD is now at 158c9f3 Resolved a conflict, once and for all $ /tmp/repo1 cat foo Not funny
That's it for now!
Guides
- Git Manual
- Concise Git Guide
- Gite Remote Repositories
- A hands on demo of GIT and Tower Thorsten and Jan-Peter recording during a Club i10
- Leonhard's favorite Reference and Guide
- Git for the Masters