Git
From KevinWiki
(→Usage) |
|||
(45 intermediate revisions not shown) | |||
Line 5: | Line 5: | ||
In the client, | In the client, | ||
$ apt-get install git-core | $ apt-get install git-core | ||
+ | |||
+ | Or to install the latest version of Git on Ubuntu | ||
+ | add-apt-repository ppa:git-core/ppa | ||
+ | apt-get update | ||
+ | apt-get install git-core | ||
+ | |||
+ | == Configuration == | ||
+ | === User Info === | ||
$ git config --global user.name "GivenName Surname" | $ git config --global user.name "GivenName Surname" | ||
$ git config --global user.email user@email.address | $ git config --global user.email user@email.address | ||
+ | |||
+ | === Default Text Editor === | ||
+ | $ git config --global core.editor "sublime-text -w" | ||
+ | |||
+ | === External Diff Tool === | ||
+ | ==== Meld As External Diff Tool ==== | ||
+ | * To use Meld as a diff tool, install Meld | ||
+ | $ sudo apt-get install meld | ||
+ | * Set it as the default diff tool | ||
+ | $ git config --global diff.external meld | ||
+ | |||
+ | * It doesn't work and gives the following error messages. | ||
+ | <pre> | ||
+ | $ git diff | ||
+ | Usage: | ||
+ | meld Start with an empty window | ||
+ | meld <file|dir> Start a version control comparison | ||
+ | meld <file> <file> [<file>] Start a 2- or 3-way file comparison | ||
+ | meld <dir> <dir> [<dir>] Start a 2- or 3-way directory comparison | ||
+ | meld <file> <dir> Start a comparison between file and dir/file | ||
+ | |||
+ | meld: error: too many arguments (wanted 0-4, got 7) | ||
+ | </pre> | ||
+ | |||
+ | * Create ~/diff.py | ||
+ | <pre> | ||
+ | #!/usr/bin/python | ||
+ | |||
+ | import sys | ||
+ | import os | ||
+ | |||
+ | os.system('meld "%s" "%s"' % (sys.argv[2], sys.argv[5])) | ||
+ | </pre> | ||
+ | * Make it executable | ||
+ | $ chmod a+x ~/diff.py | ||
+ | * Set the script as the diff tool | ||
+ | $ git config --global diff.external ~/diff.py | ||
+ | * Now it works | ||
+ | $ git diff | ||
+ | $ git diff HEAD | ||
+ | |||
+ | |||
+ | ==== Meld as Diff Tool ==== | ||
+ | * This way is much easier. | ||
+ | $ git config --global diff.tool meld | ||
+ | |||
+ | $ git difftool | ||
+ | |||
+ | ==== Meld as Diff Tool ==== | ||
+ | $ git config --global diff.tool vimdiff | ||
+ | |||
+ | |||
+ | === Unset External Diff Tool === | ||
+ | $ git config --global --unset diff.external | ||
+ | |||
+ | |||
+ | === Commit Template === | ||
+ | Create a template file<br/> | ||
+ | e.g.) $HOME/git/.your-config/git-commit-comment-template.txt | ||
+ | CLOSED - #TICKET_NUMBER: Summary | ||
+ | TICKET_URI | ||
+ | Details | ||
+ | |||
+ | $ git config --global commit.template $HOME/git/.your-config/git-commit-comment-template.txt | ||
+ | |||
+ | |||
+ | === Etc. === | ||
+ | $ git config --global color.ui true | ||
== Server-side Installation == | == Server-side Installation == | ||
Line 49: | Line 125: | ||
$ su - git | $ su - git | ||
$ cd ~ | $ cd ~ | ||
- | $ echo "PATH=$HOME/bin:$PATH" > ~/.bashrc | + | $ echo "PATH=$HOME/bin:$PATH" >> ~/.bashrc |
- | + | * don't need these 2 lines | |
$ mkdir .ssh | $ mkdir .ssh | ||
$ cat /home/user/git.pub >> /home/git/.ssh/authorized_keys | $ cat /home/user/git.pub >> /home/git/.ssh/authorized_keys | ||
Line 59: | Line 135: | ||
== Gitolite Installation == | == Gitolite Installation == | ||
$ sudo -i | $ sudo -i | ||
- | $ apt-get install gitolite | + | $ apt-get install gitolite |
+ | [ENTER] -> :q to exit | ||
+ | $ exit | ||
== Setup git User == | == Setup git User == | ||
Line 74: | Line 152: | ||
== Test == | == Test == | ||
- | + | * Testing (from user local machine) | |
$ ssh -p PORT_NUMBER git@host | $ ssh -p PORT_NUMBER git@host | ||
Having many keys? | Having many keys? | ||
$ ssh -p PORT_NUMBER -i ~/.ssh/user@host1.pub git@host | $ ssh -p PORT_NUMBER -i ~/.ssh/user@host1.pub git@host | ||
- | $ mkdir some- | + | For debugging |
+ | $ ssh -p PORT_NUMBER -vT git@host | ||
+ | |||
+ | $ mkdir some-dir | ||
+ | $ cd some-dir | ||
$ git clone git@host:gitolite-admin | $ git clone git@host:gitolite-admin | ||
Or if you use a specific port, | Or if you use a specific port, | ||
- | $ git clone ssh://git@host:PORT_NUMBER/gitolite-admin | + | $ git clone ssh://git@host:PORT_NUMBER/gitolite-admin |
- | + | ||
== Add Git User (through Gitolite) == | == Add Git User (through Gitolite) == | ||
Line 89: | Line 170: | ||
$ cd gitolite-admin/keydir | $ cd gitolite-admin/keydir | ||
$ mkdir user_name | $ mkdir user_name | ||
- | $ cp ~/.ssh/user@host1.pub . | + | $ cp -p ~/.ssh/user@host1.pub . |
- | $ cp ~/.ssh/user@host2.pub . | + | $ cp -p ~/.ssh/user@host2.pub . |
$ git add user@host1.pub | $ git add user@host1.pub | ||
$ git commit -a -m "users added: user@host1 and user@host2" | $ git commit -a -m "users added: user@host1 and user@host2" | ||
$ git push | $ git push | ||
+ | |||
+ | If directly copied to <code>/home/git/.gitolite/keydir</code>, run | ||
+ | $ gl-setup | ||
+ | to apply the changes | ||
+ | |||
+ | = Usage = | ||
+ | == Initializing a Repository in an Existing Directory == | ||
+ | * To make a directory a git repository, go to the project directory and run the following command | ||
+ | <pre> | ||
+ | $ git init | ||
+ | </pre> | ||
+ | Then it will create a new directory named <code>.git</code> inside the project directory. The <code>.git</code> folder contains all necessary files for the git repository. | ||
+ | |||
+ | == Host Info Config using SSH_Config == | ||
+ | Create <code>~/.ssh/config</code> file and edit. | ||
+ | |||
+ | Host host.name | ||
+ | Hostname host.name | ||
+ | Port 1111 | ||
+ | |||
+ | Host another.host.name | ||
+ | Hostname another.host.name | ||
+ | Port 2222 | ||
+ | |||
+ | Host host1 | ||
+ | Hostname host.name | ||
+ | Port 1111 | ||
+ | User git | ||
+ | IdentityFile ~/.ssh/git_rsa | ||
+ | |||
+ | Host user-host2 | ||
+ | Hostname host2.name | ||
+ | Port 1234 | ||
+ | User user | ||
+ | IdentityFile ~/.ssh/user@localhost1 | ||
+ | |||
+ | Host user-host3 | ||
+ | Hostname host3.com | ||
+ | Port 1122 | ||
+ | User user | ||
+ | IdentityFile ~/.ssh/user@localhost1 | ||
+ | |||
+ | '''IdentityFile is a private key''' | ||
+ | |||
+ | To use this info, | ||
+ | $ git clone git@host1:repo-name | ||
+ | # it will use 'git' as a user and access the server 'host.name' using the port nubmer 1111. | ||
+ | |||
+ | $ git clone git@host1:repo-name | ||
+ | # it will use 'user' as a user and access the server 'host2.name' using the port nubmer 1234. | ||
+ | |||
+ | == Push to Specific Host == | ||
+ | |||
+ | $ git push git@host1:repo-name master | ||
+ | |||
+ | == Set up Project Specific User Info == | ||
+ | * Move to the project repository | ||
+ | $ git config user.email user@email.address | ||
+ | |||
+ | To see and edit all the config details | ||
+ | $ git config -e | ||
+ | |||
+ | == .gitignore == | ||
+ | To exclude files when committing, create .gitignore file and put in the project root (git project repo root). | ||
+ | For maven project, the content should be | ||
+ | target/ | ||
+ | |||
+ | To set up the global ignore file | ||
+ | $ git config --global core.excludesfile $HOME/git/.your-config/.global-gitignore | ||
+ | |||
+ | Other gitignore files, | ||
+ | https://github.com/github/gitignore | ||
+ | |||
+ | |||
+ | === .gitignore for Eclipse and Maven (for Kevin) === | ||
+ | <pre> | ||
+ | target/ | ||
+ | |||
+ | *.pydevproject | ||
+ | .project | ||
+ | .classpath | ||
+ | .settings/ | ||
+ | .metadata | ||
+ | bin/ | ||
+ | tmp/ | ||
+ | *.tmp | ||
+ | *.bak | ||
+ | *.swp | ||
+ | *~.nib | ||
+ | local.properties | ||
+ | .loadpath | ||
+ | |||
+ | # External tool builders | ||
+ | .externalToolBuilders/ | ||
+ | |||
+ | # Locally stored "Eclipse launch configurations" | ||
+ | *.launch | ||
+ | |||
+ | # CDT-specific | ||
+ | .cproject | ||
+ | |||
+ | # PDT-specific | ||
+ | .buildpath | ||
+ | </pre> | ||
+ | |||
+ | |||
+ | == Commit == | ||
+ | * Add a file | ||
+ | $ git add fileName | ||
+ | |||
+ | * Commit changes | ||
+ | $ git commit -m "comment" | ||
+ | |||
+ | * Add all the files and folders | ||
+ | $ git add -A | ||
+ | # It is equivalent to git add .; git add -u | ||
+ | # git add . -> add all changed and new ones but not removed ones | ||
+ | # git add -u -> changed and removed but not new ones. | ||
+ | |||
+ | === Amend Last Comment === | ||
+ | * Amend the last comment | ||
+ | $ git commit --amend -m "New comment" | ||
+ | |||
+ | == Push == | ||
+ | === Add Remote === | ||
+ | * To view all remote | ||
+ | <pre> | ||
+ | $ git remote -v | ||
+ | </pre> | ||
+ | |||
+ | * Add a remote | ||
+ | <pre> | ||
+ | $ git remote add origin git@git-repo:project | ||
+ | </pre> | ||
+ | Then | ||
+ | <pre> | ||
+ | $ git push -u origin master | ||
+ | </pre> | ||
+ | Or | ||
+ | <pre> | ||
+ | $ git config branch.master.remote origin | ||
+ | $ git config branch.master.merge refs/heads/master | ||
+ | </pre> | ||
+ | === Push to Remote === | ||
+ | * Push to the remote git repository | ||
+ | $ git push | ||
+ | |||
+ | * If it gives error messages like: | ||
+ | No refs in common and none specified; doing nothing. | ||
+ | Perhaps you should specify a branch such as 'master'. | ||
+ | (It doesn't happen if you have already done <code>git push -u origin master </code>). | ||
+ | * Do | ||
+ | $ git push origin master | ||
+ | |||
+ | == Revert == | ||
+ | === Reset === | ||
+ | * Revert to last commit. | ||
+ | $ git reset --hard HEAD | ||
+ | Or to specific commit/tag/branch | ||
+ | $ git reset --hard <tag/branch/commit id> | ||
+ | |||
+ | * Check the id using git log | ||
+ | $ git log | ||
+ | |||
+ | * To push it | ||
+ | $ git push <reponame> -f | ||
+ | |||
+ | === Check out === | ||
+ | * Or change the name of master branch then check out from the commit to be used and make it master | ||
+ | |||
+ | # Rename the current master branch | ||
+ | #: <pre>$ git branch -m another_name_for_master </pre> | ||
+ | # Check out from another commit: | ||
+ | #: <pre> $ git checkout a1b2c3a3blah </pre> | ||
+ | # Make it the new master branch: | ||
+ | #: <pre> $ git checkout -b master </pre> | ||
+ | * If the old master is no longer needed and has to be removed, remove it by | ||
+ | $ git branch -D another_name_for_master | ||
+ | |||
+ | |||
+ | === Restore Removed File === | ||
+ | '''This has to be tested first then should be rewritten'''.<br /> | ||
+ | Find the last commit that affected the given path. As the file isn't in the HEAD commit, this commit must have deleted it. | ||
+ | $ git rev-list -n 1 HEAD -- <file_path> | ||
+ | |||
+ | Then checkout the version at the commit before. | ||
+ | $ git checkout <deleting_commit>^ -- <file_path> | ||
+ | |||
+ | Or in one command, if $file is the file. | ||
+ | $ git checkout $(git rev-list -n 1 HEAD -- "$file")^ -- "$file" | ||
+ | * Reference | ||
+ | [http://stackoverflow.com/questions/953481/restore-a-deleted-file-in-a-git-repo?answertab=votes#tab-top http://stackoverflow.com/questions/953481/restore-a-deleted-file-in-a-git-repo?answertab=votes#tab-top] | ||
+ | |||
+ | |||
+ | == Branch == | ||
+ | === Create Branch === | ||
+ | * Create branch | ||
+ | <pre> | ||
+ | $ git branch branch_name | ||
+ | </pre> | ||
+ | * Change to the branch just created | ||
+ | <pre> | ||
+ | $ git checkout branch_name | ||
+ | </pre> | ||
+ | OR | ||
+ | * Create and change | ||
+ | <pre> | ||
+ | git checkout -b branch_name | ||
+ | </pre> | ||
+ | |||
+ | * Back to master | ||
+ | <pre> | ||
+ | $ git checkout master | ||
+ | </pre> | ||
+ | |||
+ | * To push, | ||
+ | <pre> | ||
+ | $ git push origin branch_name | ||
+ | </pre> | ||
+ | |||
+ | === Change Branch === | ||
+ | * Change branch | ||
+ | <pre> | ||
+ | $ git checkout branch_name | ||
+ | </pre> | ||
+ | |||
+ | * It does not work when the local git repo has multiple remote repositories. If it's the case, | ||
+ | <pre> | ||
+ | $ git checkout -b branch remote/branch | ||
+ | </pre> | ||
+ | |||
+ | e.g.) | ||
+ | <pre> | ||
+ | $ git checkout -b my-branch origin/my-branch | ||
+ | </pre> | ||
+ | |||
+ | |||
+ | === Pull Without Checkout === | ||
+ | * To pull a specific branch without checking out | ||
+ | <pre> | ||
+ | $ git fetch <remote> <remoteBranch>:<localBranch> | ||
+ | </pre> | ||
+ | e.g.) | ||
+ | <pre> | ||
+ | git fetch origin master:master | ||
+ | </pre> | ||
+ | <pre> | ||
+ | git fetch origin adding-new-feature:adding-new-feature | ||
+ | </pre> | ||
+ | |||
+ | == Tag == | ||
+ | === List Tags === | ||
+ | <pre> | ||
+ | $ git tag | ||
+ | </pre> | ||
+ | |||
+ | === View Tag === | ||
+ | * To view tag data with commit details. | ||
+ | <pre> | ||
+ | $ git show tag_name | ||
+ | </pre> | ||
+ | |||
+ | === Create Tag === | ||
+ | * Create Tag | ||
+ | <pre> | ||
+ | $ git tag -a tag_name -m 'tag message: new version blah blah' | ||
+ | </pre> | ||
+ | * Tag later | ||
+ | <pre> | ||
+ | $ git tag -a tag_name -m 'tag message' commit_checksum | ||
+ | </pre> | ||
+ | e.g.) | ||
+ | <pre> | ||
+ | $ git tag -a v1.5 -m 'new version with blah blah' b015e9d | ||
+ | </pre> | ||
+ | * To see the list of checksum use the following command. | ||
+ | <pre> | ||
+ | $ git log --pretty=oneline | ||
+ | </pre> | ||
+ | |||
+ | === Remove Tag === | ||
+ | <pre> | ||
+ | $ git tag -d tag_name | ||
+ | </pre> | ||
+ | |||
+ | |||
+ | == Merge Multiple Git Repositories into One == | ||
+ | If the directory structures look like this | ||
+ | <pre> | ||
+ | parent-dir (not git repo) | ||
+ | │ | ||
+ | ├─git-repo1 ## each is connected to its own remote git repository | ||
+ | │ ├─src | ||
+ | │ └.git | ||
+ | │ | ||
+ | ├─git-repo2 ## each is connected to its own remote git repository | ||
+ | │ ├─src | ||
+ | │ └.git | ||
+ | │ | ||
+ | └─ git-repo3 ## each is connected to its own remote git repository | ||
+ | ├─src | ||
+ | └.git | ||
+ | </pre> | ||
+ | |||
+ | To make it like | ||
+ | <pre> | ||
+ | parent-dir (now it is git repo) | ||
+ | ├─src ## contains all the files and sub-dirs from those three repositories (git-repo1, git-repo2 and git-repo3). | ||
+ | └.git | ||
+ | |||
+ | </pre> | ||
+ | and it still keeps all the histories from all those repositories, do following instructions. | ||
+ | |||
+ | * Initialize the new repository directory. | ||
+ | <pre> | ||
+ | $ cd parent-dir | ||
+ | $ git init | ||
+ | </pre> | ||
+ | |||
+ | * Pull the old ones and remove these one by one. | ||
+ | <pre> | ||
+ | $ git pull git-repo1 | ||
+ | |||
+ | ## If there is any conflict, solve it first then | ||
+ | ## commit the changes | ||
+ | $ git add file | ||
+ | $ git commit -a | ||
+ | ## Now pull the one had conflict issue again. | ||
+ | $ git pull git-repo1 | ||
+ | |||
+ | $ rm -rf git-repo1 | ||
+ | |||
+ | $ git pull git-repo2 | ||
+ | $ rm -rf git-repo2 | ||
+ | |||
+ | $ git pull git-repo3 | ||
+ | $ rm -rf git-repo3 | ||
+ | </pre> | ||
+ | |||
+ | * Done! Add the remote repo info if necessary. | ||
+ | |||
+ | |||
+ | == Multiple Remote Repositories == | ||
+ | |||
+ | === Push === | ||
+ | ==== Add more remote repository (origin) for <code>push</code> ==== | ||
+ | <pre> | ||
+ | git remote set-url origin --add git@github.com:YOUR_USERNAME/your_project.git | ||
+ | </pre> | ||
+ | |||
+ | === Pull === | ||
+ | ==== Add remote repository info for <code>pull</code> ==== | ||
+ | <pre> | ||
+ | git remote add github git@github.com:YOUR_USERNAME/YOUR_PROJECT.git | ||
+ | git remote add gitlab git@gitlab.com:YOUR_USERNAME/YOUR_PROJECT.git | ||
+ | git remote add bitbucket git@bitbucket.org:YOUR_USERNAME/your_project.git | ||
+ | </pre> | ||
+ | |||
+ | ==== Pull from all remote repos ==== | ||
+ | * For bash | ||
+ | <pre> | ||
+ | gitpullall() | ||
+ | { | ||
+ | repos_to_ignore="origin" | ||
+ | should_ignore=-1 | ||
+ | |||
+ | echo "running: git pull" | ||
+ | git pull | ||
+ | echo "" | ||
+ | |||
+ | for remote in `git remote` | ||
+ | do | ||
+ | echo "==================================" | ||
+ | echo "Remote: $remote" | ||
+ | echo "----------------------------------" | ||
+ | for ignore in $repos_to_ignore | ||
+ | do | ||
+ | [ "$remote" == "$ignore" ] && { should_ignore=1; break; } || : | ||
+ | done | ||
+ | |||
+ | if [ "$should_ignore" == "-1" ] | ||
+ | then | ||
+ | THIS_BRANCH_NAME=`git rev-parse --abbrev-ref HEAD` | ||
+ | git branch -r | grep -s "^[[:space:]]\+$remote/$THIS_BRANCH_NAME$" 2>&1 > /dev/null || git branch -r | grep -s "^[[:space:]]\+$remote/$THIS_BRANCH_NAME""[[:space:]]\+" 2>&1 > /dev/null || { | ||
+ | echo "No info of the remote repo named '$remote' is found." | ||
+ | echo "So $remote will be fetched." | ||
+ | echo "running: git fetch $remote" | ||
+ | git fetch "$remote" | ||
+ | echo "" | ||
+ | } | ||
+ | if git branch -r | grep -sw "$remote/$THIS_BRANCH_NAME" 2>&1 > /dev/null ; then | ||
+ | echo "running: git pull $remote $THIS_BRANCH_NAME" | ||
+ | git pull "$remote" "$THIS_BRANCH_NAME" | ||
+ | else | ||
+ | echo "$THIS_BRANCH_NAME branch does not exist on the remote called '$remote'." | ||
+ | echo "So $remote will not be pulled from $remote." | ||
+ | fi | ||
+ | else | ||
+ | echo "$remote is on the ignored list so ignore it." | ||
+ | echo "Ignored: $remote" | ||
+ | fi | ||
+ | should_ignore=-1 | ||
+ | echo "==================================" | ||
+ | echo "" | ||
+ | done | ||
+ | } | ||
+ | </pre> | ||
+ | * For ZSH | ||
+ | <pre> | ||
+ | gitpullall() | ||
+ | { | ||
+ | repos_to_ignore="origin" | ||
+ | should_ignore=-1 | ||
+ | |||
+ | echo "running: git pull" | ||
+ | git pull | ||
+ | echo "" | ||
+ | |||
+ | for remote in `git remote` | ||
+ | do | ||
+ | echo "==================================" | ||
+ | echo "Remote: $remote" | ||
+ | echo "----------------------------------" | ||
+ | for ignore in $repos_to_ignore | ||
+ | do | ||
+ | [ "$remote" = "$ignore" ] && { should_ignore=1; break; } || : | ||
+ | done | ||
+ | |||
+ | if [ "$should_ignore" = "-1" ] | ||
+ | then | ||
+ | THIS_BRANCH_NAME=`git rev-parse --abbrev-ref HEAD` | ||
+ | { git branch -r | grep -s "^[[:space:]]\+$remote/$THIS_BRANCH_NAME$" 2>&1 > /dev/null } || { git branch -r | grep -s "^[[:space:]]\+$remote/$THIS_BRANCH_NAME""[[:space:]]\+" 2>&1 > /dev/null } || { | ||
+ | echo "No info of the remote repo named '$remote' is found." | ||
+ | echo "So $remote will be fetched." | ||
+ | echo "running: git fetch $remote" | ||
+ | git fetch "$remote" | ||
+ | echo "" | ||
+ | } | ||
+ | if git branch -r | grep -sw "$remote/$THIS_BRANCH_NAME" 2>&1 > /dev/null ; then | ||
+ | echo "running: git pull $remote $THIS_BRANCH_NAME" | ||
+ | git pull "$remote" "$THIS_BRANCH_NAME" | ||
+ | else | ||
+ | echo "$THIS_BRANCH_NAME branch does not exist on the remote called '$remote'." | ||
+ | echo "So $remote will not be pulled from $remote." | ||
+ | fi | ||
+ | else | ||
+ | echo "$remote is on the ignored list so ignore it." | ||
+ | echo "Ignored: $remote" | ||
+ | fi | ||
+ | should_ignore=-1 | ||
+ | echo "==================================" | ||
+ | echo "" | ||
+ | done | ||
+ | } | ||
+ | </pre> | ||
+ | |||
+ | === Checkout === | ||
+ | ==== Checkout to a branch for git repo having multiple remote repos. ==== | ||
+ | <pre> | ||
+ | git checkout -b existing-branch origin/existing-branch | ||
+ | </pre> | ||
+ | |||
+ | = Migration from SVN to Git = | ||
+ | == Install git-svn == | ||
+ | $ apt-get install git-svn | ||
+ | |||
+ | == Prepare for Migration == | ||
+ | In some dir, create users.txt having all the user info | ||
+ | e.g.) | ||
+ | USERNAME = FirstName LastName <user@host> | ||
+ | |||
+ | == Migrate == | ||
+ | === Without Tags === | ||
+ | $ git svn clone --stdlayout --no-metadata -A users.txt --username=USERNAME https://svn.repo/some-parent-folder/project-folder project-tmp | ||
+ | (https://svn.repo/some-parent-folder/project-folder <- Not trunk) | ||
+ | |||
+ | $ cd project-tmp | ||
+ | |||
+ | $ git svn fetch | ||
+ | |||
+ | * To view the other SVN branches | ||
+ | $ git branch -r | ||
+ | |||
+ | $ cd .. | ||
+ | |||
+ | $ git clone project-tmp project | ||
+ | $ rm -Rf project-tmp | ||
+ | |||
+ | $ cd project | ||
+ | $ git remote rm origin | ||
+ | |||
+ | * Before this, add git repo for the project first. | ||
+ | $ git remote add origin git@git-repo:project | ||
+ | |||
+ | * To view all remote | ||
+ | $ git remote -v | ||
+ | |||
+ | $ git config branch.master.remote origin | ||
+ | $ git config branch.master.merge refs/heads/master | ||
+ | |||
+ | |||
+ | * add user info to project/.git/config | ||
+ | [user] | ||
+ | email = user@host | ||
+ | |||
+ | $ git push -u origin master | ||
+ | |||
+ | * Check status | ||
+ | $ git status | ||
+ | # On branch master | ||
+ | nothing to commit (working directory clean) | ||
+ | |||
+ | |||
+ | === With Tags === | ||
+ | If there are nested tag folders like <code>tags/released/1.x/1.1.0, tags/released/1.x/1.2.0, tags/released/1.x/1.3.0, tags/released/2.x/2.0.1 and tags/released/2.x/2.2.0</code>, add <code>--tags</code> (for branches, it's <code>--branches</code>) | ||
+ | $ git svn clone --stdlayout --tags=tags/*/*/* --no-metadata -A users.txt --username=USERNAME https://svn.repo/some-parent-folder/project-folder project | ||
+ | $ cd project | ||
+ | $ git svn fetch | ||
+ | |||
+ | <pre> | ||
+ | #### not necessary as it will be handled differently (by create tag and remove the svn tag) | ||
+ | $ cp -Rf .git/refs/remotes/tags/* .git/refs/tags/ | ||
+ | $ rm -Rf .git/refs/remotes/tags | ||
+ | |||
+ | $ cp -Rf .git/refs/remotes/* .git/refs/heads/ | ||
+ | $ rm -Rf .git/refs/remotes | ||
+ | ################################################################################### | ||
+ | </pre> | ||
+ | |||
+ | $ git branch -r | ||
+ | Then for each tag listed do: | ||
+ | |||
+ | $ git tag tagname tags/tagname | ||
+ | $ git branch -r -d tags/tagname | ||
+ | |||
+ | $ git tag tag-name-for-git tags/tag-name-from-svn | ||
+ | $ git branch -r -d tags/tag-name-from-svn | ||
+ | |||
+ | e.g.) | ||
+ | $ git tag 0.1.15 tags/released/0.x/some-app-0.1.15 | ||
+ | $ git branch -r -d tags/released/0.x/some-app-0.1.15 | ||
+ | |||
+ | * list tags | ||
+ | $ git tag -l | ||
+ | |||
+ | * remove old svn tags | ||
+ | $ rm -Rf .git/refs/remotes/tags | ||
+ | |||
+ | $ git remote add origin git@git-repo:project | ||
+ | |||
+ | * add user info to project/.git/config | ||
+ | $ git config -e | ||
+ | |||
+ | [user] | ||
+ | email = user@host | ||
+ | |||
+ | and remove [svn-remote "svn"] and [svn] | ||
+ | $ git push origin --all | ||
+ | |||
+ | It doesn't seem like the tags are pushed? | ||
+ | $ git push origin --tags |
Latest revision as of 10:43, 31 March 2015
Git
Client-side Installation
In the client,
$ apt-get install git-core
Or to install the latest version of Git on Ubuntu
add-apt-repository ppa:git-core/ppa apt-get update apt-get install git-core
Configuration
User Info
$ git config --global user.name "GivenName Surname" $ git config --global user.email user@email.address
Default Text Editor
$ git config --global core.editor "sublime-text -w"
External Diff Tool
Meld As External Diff Tool
- To use Meld as a diff tool, install Meld
$ sudo apt-get install meld
- Set it as the default diff tool
$ git config --global diff.external meld
- It doesn't work and gives the following error messages.
$ git diff Usage: meld Start with an empty window meld <file|dir> Start a version control comparison meld <file> <file> [<file>] Start a 2- or 3-way file comparison meld <dir> <dir> [<dir>] Start a 2- or 3-way directory comparison meld <file> <dir> Start a comparison between file and dir/file meld: error: too many arguments (wanted 0-4, got 7)
- Create ~/diff.py
#!/usr/bin/python import sys import os os.system('meld "%s" "%s"' % (sys.argv[2], sys.argv[5]))
- Make it executable
$ chmod a+x ~/diff.py
- Set the script as the diff tool
$ git config --global diff.external ~/diff.py
- Now it works
$ git diff $ git diff HEAD
Meld as Diff Tool
- This way is much easier.
$ git config --global diff.tool meld
$ git difftool
Meld as Diff Tool
$ git config --global diff.tool vimdiff
Unset External Diff Tool
$ git config --global --unset diff.external
Commit Template
Create a template file
e.g.) $HOME/git/.your-config/git-commit-comment-template.txt
CLOSED - #TICKET_NUMBER: Summary TICKET_URI Details
$ git config --global commit.template $HOME/git/.your-config/git-commit-comment-template.txt
Etc.
$ git config --global color.ui true
Server-side Installation
In the server,
$ apt-get install git
Gitolite
Add User to Manage Gitolite
- Add user to manage gitolite
$ adduser \ --system \ --shell /bin/bash \ --gecos 'git version control' \ --group \ --disabled-password \ --home /home/git \ git Adding system user `git' (UID 108) ... Adding new group `git' (GID 110) ... Adding new user `git' (UID 108) with group `git' ... Creating home directory `/home/git' ...
Create SSH key
$ ssh-keygen -t rsa
/home/username/.ssh/some_git_rsa [ENTER] [ENTER]
Transfer SSH Keys to Server
$ ssh-copy-id '-p PORT_NUMBER -i /home/user/.ssh/some_git_rsa.pub git_user@host'
If it doesn't work because the user is the only user who can access the server with his/her ssh key so accessing with git_user doesn't work.
$ scp -P PORT ~/.ssh/some_git_rsa.pub user@host:
Log in with the user account
$ ssh -p PORT user@host $ mv some_git_rsa.pub git.pub
Setup
$ su - git $ cd ~ $ echo "PATH=$HOME/bin:$PATH" >> ~/.bashrc
- don't need these 2 lines
$ mkdir .ssh $ cat /home/user/git.pub >> /home/git/.ssh/authorized_keys
$ exit
Gitolite Installation
$ sudo -i $ apt-get install gitolite [ENTER] -> :q to exit $ exit
Setup git User
$ su - git $ gl-setup /home/user/git.pub
$ vim /etc/ssh/sshd_config
Add git to the allowed user
AllowUsers user git
Restart ssh
$ service ssh restart
Test
- Testing (from user local machine)
$ ssh -p PORT_NUMBER git@host
Having many keys?
$ ssh -p PORT_NUMBER -i ~/.ssh/user@host1.pub git@host
For debugging
$ ssh -p PORT_NUMBER -vT git@host
$ mkdir some-dir $ cd some-dir $ git clone git@host:gitolite-admin
Or if you use a specific port,
$ git clone ssh://git@host:PORT_NUMBER/gitolite-admin
Add Git User (through Gitolite)
Adding User
$ cd gitolite-admin/keydir $ mkdir user_name $ cp -p ~/.ssh/user@host1.pub . $ cp -p ~/.ssh/user@host2.pub . $ git add user@host1.pub $ git commit -a -m "users added: user@host1 and user@host2" $ git push
If directly copied to /home/git/.gitolite/keydir
, run
$ gl-setup
to apply the changes
Usage
Initializing a Repository in an Existing Directory
- To make a directory a git repository, go to the project directory and run the following command
$ git init
Then it will create a new directory named .git
inside the project directory. The .git
folder contains all necessary files for the git repository.
Host Info Config using SSH_Config
Create ~/.ssh/config
file and edit.
Host host.name Hostname host.name Port 1111 Host another.host.name Hostname another.host.name Port 2222 Host host1 Hostname host.name Port 1111 User git IdentityFile ~/.ssh/git_rsa Host user-host2 Hostname host2.name Port 1234 User user IdentityFile ~/.ssh/user@localhost1 Host user-host3 Hostname host3.com Port 1122 User user IdentityFile ~/.ssh/user@localhost1
IdentityFile is a private key
To use this info,
$ git clone git@host1:repo-name # it will use 'git' as a user and access the server 'host.name' using the port nubmer 1111.
$ git clone git@host1:repo-name # it will use 'user' as a user and access the server 'host2.name' using the port nubmer 1234.
Push to Specific Host
$ git push git@host1:repo-name master
Set up Project Specific User Info
- Move to the project repository
$ git config user.email user@email.address
To see and edit all the config details
$ git config -e
.gitignore
To exclude files when committing, create .gitignore file and put in the project root (git project repo root). For maven project, the content should be
target/
To set up the global ignore file
$ git config --global core.excludesfile $HOME/git/.your-config/.global-gitignore
Other gitignore files,
https://github.com/github/gitignore
.gitignore for Eclipse and Maven (for Kevin)
target/ *.pydevproject .project .classpath .settings/ .metadata bin/ tmp/ *.tmp *.bak *.swp *~.nib local.properties .loadpath # External tool builders .externalToolBuilders/ # Locally stored "Eclipse launch configurations" *.launch # CDT-specific .cproject # PDT-specific .buildpath
Commit
- Add a file
$ git add fileName
- Commit changes
$ git commit -m "comment"
- Add all the files and folders
$ git add -A # It is equivalent to git add .; git add -u # git add . -> add all changed and new ones but not removed ones # git add -u -> changed and removed but not new ones.
Amend Last Comment
- Amend the last comment
$ git commit --amend -m "New comment"
Push
Add Remote
- To view all remote
$ git remote -v
- Add a remote
$ git remote add origin git@git-repo:project
Then
$ git push -u origin master
Or
$ git config branch.master.remote origin $ git config branch.master.merge refs/heads/master
Push to Remote
- Push to the remote git repository
$ git push
- If it gives error messages like:
No refs in common and none specified; doing nothing. Perhaps you should specify a branch such as 'master'.
(It doesn't happen if you have already done git push -u origin master
).
- Do
$ git push origin master
Revert
Reset
- Revert to last commit.
$ git reset --hard HEAD
Or to specific commit/tag/branch
$ git reset --hard <tag/branch/commit id>
- Check the id using git log
$ git log
- To push it
$ git push <reponame> -f
Check out
- Or change the name of master branch then check out from the commit to be used and make it master
- Rename the current master branch
-
$ git branch -m another_name_for_master
-
- Check out from another commit:
-
$ git checkout a1b2c3a3blah
-
- Make it the new master branch:
-
$ git checkout -b master
-
- If the old master is no longer needed and has to be removed, remove it by
$ git branch -D another_name_for_master
Restore Removed File
This has to be tested first then should be rewritten.
Find the last commit that affected the given path. As the file isn't in the HEAD commit, this commit must have deleted it.
$ git rev-list -n 1 HEAD -- <file_path>
Then checkout the version at the commit before.
$ git checkout <deleting_commit>^ -- <file_path>
Or in one command, if $file is the file.
$ git checkout $(git rev-list -n 1 HEAD -- "$file")^ -- "$file"
- Reference
Branch
Create Branch
- Create branch
$ git branch branch_name
- Change to the branch just created
$ git checkout branch_name
OR
- Create and change
git checkout -b branch_name
- Back to master
$ git checkout master
- To push,
$ git push origin branch_name
Change Branch
- Change branch
$ git checkout branch_name
- It does not work when the local git repo has multiple remote repositories. If it's the case,
$ git checkout -b branch remote/branch
e.g.)
$ git checkout -b my-branch origin/my-branch
Pull Without Checkout
- To pull a specific branch without checking out
$ git fetch <remote> <remoteBranch>:<localBranch>
e.g.)
git fetch origin master:master
git fetch origin adding-new-feature:adding-new-feature
Tag
List Tags
$ git tag
View Tag
- To view tag data with commit details.
$ git show tag_name
Create Tag
- Create Tag
$ git tag -a tag_name -m 'tag message: new version blah blah'
- Tag later
$ git tag -a tag_name -m 'tag message' commit_checksum
e.g.)
$ git tag -a v1.5 -m 'new version with blah blah' b015e9d
- To see the list of checksum use the following command.
$ git log --pretty=oneline
Remove Tag
$ git tag -d tag_name
Merge Multiple Git Repositories into One
If the directory structures look like this
parent-dir (not git repo) │ ├─git-repo1 ## each is connected to its own remote git repository │ ├─src │ └.git │ ├─git-repo2 ## each is connected to its own remote git repository │ ├─src │ └.git │ └─ git-repo3 ## each is connected to its own remote git repository ├─src └.git
To make it like
parent-dir (now it is git repo) ├─src ## contains all the files and sub-dirs from those three repositories (git-repo1, git-repo2 and git-repo3). └.git
and it still keeps all the histories from all those repositories, do following instructions.
- Initialize the new repository directory.
$ cd parent-dir $ git init
- Pull the old ones and remove these one by one.
$ git pull git-repo1 ## If there is any conflict, solve it first then ## commit the changes $ git add file $ git commit -a ## Now pull the one had conflict issue again. $ git pull git-repo1 $ rm -rf git-repo1 $ git pull git-repo2 $ rm -rf git-repo2 $ git pull git-repo3 $ rm -rf git-repo3
- Done! Add the remote repo info if necessary.
Multiple Remote Repositories
Push
Add more remote repository (origin) for push
git remote set-url origin --add git@github.com:YOUR_USERNAME/your_project.git
Pull
Add remote repository info for pull
git remote add github git@github.com:YOUR_USERNAME/YOUR_PROJECT.git git remote add gitlab git@gitlab.com:YOUR_USERNAME/YOUR_PROJECT.git git remote add bitbucket git@bitbucket.org:YOUR_USERNAME/your_project.git
Pull from all remote repos
- For bash
gitpullall() { repos_to_ignore="origin" should_ignore=-1 echo "running: git pull" git pull echo "" for remote in `git remote` do echo "==================================" echo "Remote: $remote" echo "----------------------------------" for ignore in $repos_to_ignore do [ "$remote" == "$ignore" ] && { should_ignore=1; break; } || : done if [ "$should_ignore" == "-1" ] then THIS_BRANCH_NAME=`git rev-parse --abbrev-ref HEAD` git branch -r | grep -s "^[[:space:]]\+$remote/$THIS_BRANCH_NAME$" 2>&1 > /dev/null || git branch -r | grep -s "^[[:space:]]\+$remote/$THIS_BRANCH_NAME""[[:space:]]\+" 2>&1 > /dev/null || { echo "No info of the remote repo named '$remote' is found." echo "So $remote will be fetched." echo "running: git fetch $remote" git fetch "$remote" echo "" } if git branch -r | grep -sw "$remote/$THIS_BRANCH_NAME" 2>&1 > /dev/null ; then echo "running: git pull $remote $THIS_BRANCH_NAME" git pull "$remote" "$THIS_BRANCH_NAME" else echo "$THIS_BRANCH_NAME branch does not exist on the remote called '$remote'." echo "So $remote will not be pulled from $remote." fi else echo "$remote is on the ignored list so ignore it." echo "Ignored: $remote" fi should_ignore=-1 echo "==================================" echo "" done }
- For ZSH
gitpullall() { repos_to_ignore="origin" should_ignore=-1 echo "running: git pull" git pull echo "" for remote in `git remote` do echo "==================================" echo "Remote: $remote" echo "----------------------------------" for ignore in $repos_to_ignore do [ "$remote" = "$ignore" ] && { should_ignore=1; break; } || : done if [ "$should_ignore" = "-1" ] then THIS_BRANCH_NAME=`git rev-parse --abbrev-ref HEAD` { git branch -r | grep -s "^[[:space:]]\+$remote/$THIS_BRANCH_NAME$" 2>&1 > /dev/null } || { git branch -r | grep -s "^[[:space:]]\+$remote/$THIS_BRANCH_NAME""[[:space:]]\+" 2>&1 > /dev/null } || { echo "No info of the remote repo named '$remote' is found." echo "So $remote will be fetched." echo "running: git fetch $remote" git fetch "$remote" echo "" } if git branch -r | grep -sw "$remote/$THIS_BRANCH_NAME" 2>&1 > /dev/null ; then echo "running: git pull $remote $THIS_BRANCH_NAME" git pull "$remote" "$THIS_BRANCH_NAME" else echo "$THIS_BRANCH_NAME branch does not exist on the remote called '$remote'." echo "So $remote will not be pulled from $remote." fi else echo "$remote is on the ignored list so ignore it." echo "Ignored: $remote" fi should_ignore=-1 echo "==================================" echo "" done }
Checkout
Checkout to a branch for git repo having multiple remote repos.
git checkout -b existing-branch origin/existing-branch
Migration from SVN to Git
Install git-svn
$ apt-get install git-svn
Prepare for Migration
In some dir, create users.txt having all the user info e.g.)
USERNAME = FirstName LastName <user@host>
Migrate
Without Tags
$ git svn clone --stdlayout --no-metadata -A users.txt --username=USERNAME https://svn.repo/some-parent-folder/project-folder project-tmp
(https://svn.repo/some-parent-folder/project-folder <- Not trunk)
$ cd project-tmp
$ git svn fetch
- To view the other SVN branches
$ git branch -r
$ cd ..
$ git clone project-tmp project $ rm -Rf project-tmp
$ cd project $ git remote rm origin
- Before this, add git repo for the project first.
$ git remote add origin git@git-repo:project
- To view all remote
$ git remote -v
$ git config branch.master.remote origin $ git config branch.master.merge refs/heads/master
- add user info to project/.git/config
[user] email = user@host
$ git push -u origin master
- Check status
$ git status # On branch master nothing to commit (working directory clean)
With Tags
If there are nested tag folders like tags/released/1.x/1.1.0, tags/released/1.x/1.2.0, tags/released/1.x/1.3.0, tags/released/2.x/2.0.1 and tags/released/2.x/2.2.0
, add --tags
(for branches, it's --branches
)
$ git svn clone --stdlayout --tags=tags/*/*/* --no-metadata -A users.txt --username=USERNAME https://svn.repo/some-parent-folder/project-folder project $ cd project $ git svn fetch
#### not necessary as it will be handled differently (by create tag and remove the svn tag) $ cp -Rf .git/refs/remotes/tags/* .git/refs/tags/ $ rm -Rf .git/refs/remotes/tags $ cp -Rf .git/refs/remotes/* .git/refs/heads/ $ rm -Rf .git/refs/remotes ###################################################################################
$ git branch -r
Then for each tag listed do:
$ git tag tagname tags/tagname $ git branch -r -d tags/tagname
$ git tag tag-name-for-git tags/tag-name-from-svn $ git branch -r -d tags/tag-name-from-svn
e.g.)
$ git tag 0.1.15 tags/released/0.x/some-app-0.1.15 $ git branch -r -d tags/released/0.x/some-app-0.1.15
- list tags
$ git tag -l
- remove old svn tags
$ rm -Rf .git/refs/remotes/tags
$ git remote add origin git@git-repo:project
* add user info to project/.git/config $ git config -e
[user] email = user@host
and remove [svn-remote "svn"] and [svn]
$ git push origin --all
It doesn't seem like the tags are pushed?
$ git push origin --tags