Git是用C语言开发的分布版本号控制系统。版本号控制系统可以保留一个文件集合的历史记录,并能回滚文件集合到还有一个状态(历史记录状态)。还有一个状态可以是不一样的文件,也可以是不一样的文件内容。举个样例,你可以将文件集合转换到两天以前的状态,或者你可以在生产代码和实验性质的代码之间进行切换。文件集合每每被称做是“源码”。在一个分布版本号控制系统中,每个人都有一份完整的源码(包含源码所有的历史记录信息),而且可以对这个本地的数据进行操做。分布版本号控制系统不需要一个集中式的代码仓库。html
当你对本地的源码进行了改动,你可以标注他们跟下一个版本号相关(将他们加到index中),而后提交到仓库中来(commit)。Git保存了所有的版本号信息,因此你可以转换你的源码到不论什么的历史版本号。你可以对本地的仓库进行代码的提交,而后与其它的仓库进行同步。你可以使用Git来进行仓库的克隆(clone)操做,完整的复制一个已有的仓库。仓库的所有者可以经过push操做(推送变动到别处的仓库)或者Pull操做(从别处的仓库拉取变动)来同步变动。git
Git支持分支功能(branch)。假设你想开发一个新的产品功能,你可以创建一个分支,对这个分支的进行改动,而不至于会影响到主支上的代码。github
Git提供了命令行工具;这个教程会使用命令行。你也可以找到图形工具,譬如与Eclipse配套的EGit工具,但是这些都不会在这个教程中进行描写叙述。算法
Git 术语
ubuntu
术语 | 定义 |
---|---|
仓库 | 一个仓库包含了所有的版本号信息、所有的分支和标记信息. |
Repository | 在Git中仓库的每份拷贝都是完整的。仓库让你可以从中 |
取得你的工做副本。 | |
一个分支意味着一个独立的、拥有本身历史信息的代码线 | |
分支 | (code line)。你可以从已有的代码中生成一个新的分支 |
Branches | ,这个分支与剩余的分支全然独立。默认的分支每每是叫 |
master。用户可以选择一个分支,选择一个分支叫作 | |
checkout. | |
标记 | 一个标记指的是某个分支某个特定时间点的状态。经过标 |
Tags | 记,可以很是方便的切换到标记时的状态,好比2009年1月25 |
号在testing分支上的代码状态 | |
提交 | 提交代码后,仓库会建立一个新的版本号。这个版本号可以在 |
Commit | 兴许被又一次得到。每次提交都包含做者和提交者,做者和 |
提交者可以是不一样的人 | |
URL | URl用来标识一个仓库的位置 |
用来表示代码的一个版本号状态。Git经过用SHA1 hash算法 | |
修订 | 表示的id来标识不一样的版本号。每一个 SHA1 id都是160位长 |
Revision | ,16进制标识的字符串.最新的版本号可以经过HEAD来获取. |
以前的版本号可以经过"HEAD~1"来获取,以此类推。 |
在Ubuntu上,你可以经过apt来安装git命令行工具并发
sudo apt-get install git-core
对于其它的Linux版本号,请查看相关的软件包安装工具用法。msysgit项目提供了Windows版本号的Git,地址是http://code.google.com/p/msysgit/app
你可以在.gitconfig文件里防止git的全局配置。文件位于用户的home文件夹。上述已经提到每次提交都会保存做者和提交者的信息,这些信息都可以保存在全局配置中。兴许将会介绍配置用户信息、高亮显示和忽略特定的文件。less
经过例如如下命令来配置username和Email
git config --global user.name "Example Surname" git config --global user.email "your.email@gmail.com" # Set default so that all changes are always pushed to the repository git config --global push.default "matching"
获取Git配置信息,运行下面命令:
git config --list
兴许将经过一个典型的Git工做流来学习。在这个过程当中,你会建立一些文件、建立一个本地的Git仓库、提交你的文件到这个仓库中。这以后,你会克隆一个仓库、在仓库之间经过pull和push操做来交换代码的改动。凝视(以#开头)解释了命令的详细含义,让咱们打开命令行開始操做吧。
如下建立一些文件,它们会被放到版本号控制之中
#Switch to home cd ~/ # Create a directory mkdir ~/repo01 # Switch into it cd repo01 # Create a new directory mkdir datafiles # Create a few files touch test01 touch test02 touch test03 touch datafiles/data.txt # Put a little text into the first file ls >test01
每个Git仓库都是放置在.git文件夹下.这个文件夹包括了仓库的所有历史记录,.git/config文件包括了仓库的本地配置。
下面将会建立一个Git仓库,加入�文件倒仓库的索引中,提交更改。
# Initialize the local Git repository git init # Add all (files and directories) to the Git repository git add . # Make a commit of your file to the local repository git commit -m "Initial commit" # Show the log file git log
经过git diff命令,用户可以查看更改。经过改变一个文件的内容,看看gitdiff命令输出什么,而后提交这个更改到仓库中
# Make some changes to the file echo "This is a change" > test01 echo "and this is another change" > test02 # Check the changes via the diff command git diff # Commit the changes, -a will commit changes for modified files # but will not add automatically new files git commit -a -m "These are new changes"
如下会向你展现仓库现有的状态以及过往的提交历史
# Make some changes in the file echo "This is a new change" > test01 echo "and this is another new change" > test02 # See the current status of your repository # (which files are changed / new / deleted) git status # Show the differences between the uncommitted files # and the last commit in the current branch git diff # Add the changes to the index and commit git add . && git commit -m "More chaanges - typo in the commit message" # Show the history of commits in the current branch git log # This starts a nice graphical view of the changes gitk --all
经过git amend命令,咱们可以改动最后提交的的信息。上述的提交信息中存在错误,如下会改动这个错误。
git commit --amend -m "More changes - now correct"
假设你删除了一个在版本号控制之下的文件,那么使用git add .不会在索引中删除这个文件。需要经过带-a选项的git commit命令和-A选项的git add命令来完毕
# Create a file and put it under version control touch nonsense.txt git add . && git commit -m "a new file has been created" # Remove the file rm nonsense.txt # Try standard way of committing -> will not work git add . && git commit -m "a new file has been created" # Now commit with the -a flag git commit -a -m "File nonsense.txt is now removed" # Alternatively you could add deleted files to the staging index via git add -A . git commit -m "File nonsense.txt is now removed"
咱们将建立一个远端的Git仓库。这个仓库可以存储在本地或者是网络上。
远端Git仓库和标准的Git仓库有例如如下区别:一个标准的Git仓库包含了源码和历史信息记录。咱们可以直接在这个基础上改动代码,因为它已经包含了一个工做副本。但是远端仓库没有包含工做副本,仅仅包含了历史信息。可以使用–bare选项来建立一个这种仓库。
为了方便起见,演示样例中的仓库建立在本地文件系统上
# Switch to the first repository cd ~/repo01 # git clone --bare . ../remote-repository.git # Check the content, it is identical to the .git directory in repo01 ls ~/remote-repository.git
作一些更改,而后将这些更改从你的第一个仓库推送到一个远端仓库
cd ~/repo01
echo "Hello, hello. Turn your radio on" > test01echo "Bye, bye. Turn your radio off" > test02
git commit -a -m "Some changes"
git push ../remote-repository.git
除了经过完整的URL来訪问Git仓库外,还可以经过git remote add命令为仓库加入�一个短名称。当你克隆了一个仓库之后,origin表示所克隆的原始仓库。即便咱们从零開始,这个名称也存在。
# Add ../remote-repository.git with the name origin git remote add origin ../remote-repository.git # Again some changes echo "I added a remote repo" > test02 # Commit git commit -a -m "This is a test for the new remote origin" # If you do not label a repository it will push to origin git push origin
经过下面命令在新的文件夹下建立一个新的仓库
# Switch to home cd ~ # Make new directory mkdir repo02 # Switch to new directory cd ~/repo02 # Clone git clone ../remote-repository.git .
经过拉取,可以从其它的仓库中获取最新的更改。在第二个仓库中,作一些更改,而后将更改推送到远端的仓库中。而后第一个仓库拉取这些更改
# Switch to home cd ~ # Switch to second directory cd ~/repo02 # Make changes echo "A change" > test01 # Commit git commit -a -m "A change" # Push changes to remote repository # Origin is automatically maintained as we cloned from this repository git push origin # Switch to the first repository and pull in the changes cd ~/repo01 git pull ../remote-repository.git/ # Check the changes less test01
假设在你的工做副本中,你建立了不想被提交的文件,你可以丢弃它。
# Create a new file with content touch test04 echo "this is trash" > test04 # Make a dry-run to see what would happen # -n is the same as --dry-run git clean -n # Now delete git clean -f 你可以提取老版本号的代码,经过提交的ID。git log命令可以查看提交ID # Switch to home cd ~/repo01 # Get the log git log # Copy one of the older commits and checkout the older revision via 译者注:checkout 后加commit id就是把commit的内容拷贝到index和工做副本中 git checkout commit_name
假设你还未把更改添�到索引中,你也可以直接还原所有的更改
#Some nonsense change echo "nonsense change" > test01 # Not added to the staging index. Therefore we can # just checkout the old version #译者注:checkout后假设没有commit id号,就是从index中拷贝数据到工做副本,不涉及commit部分的改变 git checkout test01 # Check the result cat test01 # Another nonsense change echo "another nonsense change" > test01 # We add the file to the staging index git add test01 # Restore the file in the staging index #译者注:复制HEAD所指commit的test01文件到index中 git reset HEAD test01 # Get the old version from the staging index #译者注:复制index中test01到工做副本中 git checkout test01 #译者注,以上两条命令可以合并为git checkout HEAD test01 也可以经过revert命令进行还原操做 # Revert a commit git revert commit_name
即便你删除了一个未加入�到索引和提交的文件,你也可以还原出这个文件
# Delete a file rm test01 # Revert the deletion git checkout test01
假设你已经加入�一个文件到索引中,但是未提交。可以经过git resetfile 命令将这个文件从索引中删除
// Create a file touch incorrect.txt // Accidently add it to the index git add . // Remove it from the index git reset incorrect.txt // Delete the file rm incorrect.txt
假设你删除了目录且还没有提交,可以经过下面命令来恢复这个目录 。译者注:即便已经提交,也可以还原
git checkout HEAD -- your_dir_to_restore
译者注:checkout和reset这两个命令的含义是不一样的,可以參阅这篇文章http://marklodato.github.com/visual-git-guide/index-en.html
经过分支,可以创造独立的代码副本。默认的分支叫master。Git消耗很是少的资源就能建立分支。Git鼓舞开发者多使用分支
如下的命令列出了所有的本地分支,当前所在的分支前带有*号
git branch
假设你还想看到远端仓库的分支,可以使用如下的命令
git branch -a
可以经过如下的命令来建立一个新的分支
# Syntax: git branch <name> <hash> # <hash> in the above is optional # if not specified the last commit will be used # If specified the corresponding commit will be used git branch testing # Switch to your new branch git checkout testing # Some changes echo "Cool new feature in this branch" > test01 git commit -a -m "new feature" # Switch to the master branch git checkout master # Check that the content of test01 is the old one cat test01
经过Merge咱们可以合并两个不一样分支的结果。Merge经过所谓的三路合并来完毕。分别来自两个分支的最新commit和两个分支的最新公共commit.可以经过例如如下的命令进行合并
# Syntax: git merge <branch-name> git merge testing
一旦合并发生了冲突,Git会标志出来,开发者需要手工的去解决这些冲突。解决冲突之后,就可以将文件加入�到索引中,而后提交更改
删除分支的命令例如如下:
#Delete branch testing git branch -d testing # Check if branch has been deleted git branch
默认的,Git仅仅会推送匹配的分支的远端仓库。这意味在使用git push命令默认推送你的分支以前,需要手工的推送一次这个分支。
# Push testing branch to remote repository git push origin testing # Switch to the testing branch git checkout testing # Some changes echo "News for you" > test01 git commit -a -m "new feature in branch" # Push all including branch git push
经过这样的方式,你可以肯定哪些分支对于其它仓库是可见的,而哪些仅仅是本地的分支
假设两个不一样的开发者对同一个文件进行了改动,那么合并冲突就会发生。而Git没有智能到本身主动解决合并两个改动。
在这一节中,咱们会首先制造一个合并冲突,而后解决它,并应用到Git仓库中。
如下会产生一个合并冲突
# Switch to the first directory cd ~/repo01 # Make changes touch mergeconflict.txt echo "Change in the first repository" > mergeconflict.txt # Stage and commit git add . && git commit -a -m "Will create merge conflict 1" # Switch to the second directory cd ~/repo02 # Make changes touch mergeconflict.txt echo "Change in the second repository" > mergeconflict.txt # Stage and commit git add . && git commit -a -m "Will create merge conflict 2" # Push to the master repository git push # Now try to push from the first directory # Switch to the first directory cd ~/repo01 # Try to push --> you will get an error message git push # Get the changes git pull origin master
Git将冲突放在收到影响的文件里,文件内容例如如下:
<<<<<<< HEAD Change in the first repository ======= Change in the second repository >>>>>>> b29196692f5ebfd10d8a9ca1911c8b08127c85f8
上面部分是你的本地仓库,如下部分是远端仓库。现在编辑这个文件,而后commit更改。另外的,你可以使用git mergetool命令
# Either edit the file manually or use git mergetool # You will be prompted to select which merge tool you want to use # For example on Ubuntu you can use the tool "meld" # After merging the changes manually, commit them git commit -m "merged changes"
经过rebase命令可以合并多个commit为一个。这样用户push更改到远端仓库的时候就可以先改动commit历史
接下来咱们将建立多个commit,而后再将它们rebase成一个commit
# Create a new file touch rebase.txt # Add it to git git add . && git commit -m "rebase.txt added to index" # Do some silly changes and commit echo "content" >> rebase.txt git add . && git commit -m "added content" echo " more content" >> rebase.txt git add . && git commit -m "added more content" echo " more content" >> rebase.txt git add . && git commit -m "added more content" echo " more content" >> rebase.txt git add . && git commit -m "added more content" echo " more content" >> rebase.txt git add . && git commit -m "added more content" echo " more content" >> rebase.txt git add . && git commit -m "added more content" # Check the git log message git log
咱们合并最后的七个commit。你可以经过例如如下的命令交互的完毕
git rebase -i HEAD~7
这个命令会打开编辑器让你改动commit的信息或者 squash/ fixup最后一个信息.Squash会合并commit信息而fixup会忽略commit信息(待理解)
你也可以对两个分支进行rebase操做。例如如下所述,merge命令合并两个分支的更改。rebase命令为一个分支的更改生成一个补丁,而后应用这个补丁到还有一分支中
使用merge和rebase,最后的源码是同样的,但是使用rebase产生的commit历史更加的少,而且历史记录看上去更加的线性
# Create new branch git branch testing # Checkout the branch git checkout testing # Make some changes echo "This will be rebased to master" > test01 # Commit into testing branch git commit -a -m "New feature in branch" # Rebase the master git rebase master
在push更改到其它的Git仓库以前,咱们需要细致检查本地分支的commit历史
在Git中,你可以使用本地的commit。开发者可以利用这个功能方便的回滚本地的开发历史。但是在push以前,需要观察你的本地分支历史,是否当中有些commit历史对其它用户来讲是无关的
假设所有的commit历史都跟同一个功能有关,很是多状况下,你需要rebase这些commit历史为一个commit历史。
交互性的rebase主要就是作重写commit历史的任务。这样作是安全的,因为commit尚未被push到其余的仓库。这意味着commit历史仅仅有在被push以前被改动。
假设你改动而后push了一个已经在目标仓库中存在的commit历史,这看起来就像是你实现了一些别人已经实现的功能
一个补丁指的是一个包括对源码进行改动的文本文件。你可以将这个文件发送给某人,而后他就可以应用这个补丁到他的本地仓库。
如下会建立一个分支,对这个分支所一些改动,而后建立一个补丁,并应用这个补丁到master分支
# Create a new branch git branch mybranch # Use this new branch git checkout mybranch # Make some changes touch test05 # Change some content in an existing file echo "New content for test01" >test01 # Commit this to the branch git add . git commit -a -m "First commit in the branch" # Create a patch --> git format-patch master git format-patch origin/master # This created patch 0001-First-commit-in-the-branch.patch # Switch to the master git checkout master # Apply the patch git apply 0001-First-commit-in-the-branch.patch # Do your normal commit in the master git add . git commit -a -m "Applied patch" # Delete the patch rm 0001-First-commit-in-the-branch.patch
Git赞成你设定你本身的Git命令。你可以给你本身常用的命令起一个缩写命令,或者合并几条命令道一个命令上来。
如下的样例中,定义了git add-commit 命令,这个命令合并了git add . -A 和git commit -m 命令。定义这个命令后,就可以使用git add-commit -m"message" 了.
git config --global alias.add-commit '!git add . -A && git commit'
但是很不幸,截止写这篇文章以前,定义同名命令在msysGit中尚未支持。同名命令不能以!開始。
有时候,你不但愿某些文件或者目录被包括在Git仓库中。但是假设你把它们加到.gitignore文件里之后,Git会中止跟踪这个文件。但是它不会将这个文件从仓库中删除。这致使了文件或者目录的最后一个版本号仍是存在于仓库中。为了取消跟踪这些文件或者目录,你可以使用例如如下的命令
# Remove directory .metadata from git repo git rm -r --cached .metadata # Remove file test.txt from repo git rm --cached test.txt
这样作不会将这些文件从commit历史中去掉。假设你想将这些文件从commit历史中去掉,可以參考git filter-branch命令
如下列出了在平常工做中很实用的Git命令
实用的Git命令
命令 | 描写叙述 |
---|---|
git blame filename | 谁建立了或者是改动了这个文件 |
git checkout -b mybranch | 以上上个commit信息为起点,建立一条 |
master~1 | 新的分支 |
如上所述,咱们的操做不需要Git服务。我可以仅仅使用文件系统或者是Git仓库的提供者,像Github或Bitbucket。但是,有时候,拥有一个本身的服务是比較方便的,在ubuntu下安装一个服务相对来讲是比較easy的
肯定你已经安装了ssh
apt-get install ssh
假设你尚未安装Git服务,安装它
sudo apt-get install git-core
加入�一个名为git的用户
sudo adduser git
而后使用git用户进行登录,建立一个空的仓库
# Login to server # to test use localhost ssh git@IP_ADDRESS_OF_SERVER # Create repository git init --bare example.git
现在你就可以向远端的仓库提交变动了
mkdir gitexample cd gitexample git init touch README git add README git commit -m 'first commit' git remote add origin git@IP_ADDRESS_OF_SERVER:example.git git push origin master
Git支持远端的操做。Git支持多种的传输类型,Git自带的协议就叫作git。如下的的命令经过git协议从克隆一个仓库
git clone git@github.com:vogella/gitbook.git
相同的,你可以经过http协议来克隆仓库
# The following will clone via HTTP git clone http://vogella@github.com/vogella/gitbook.git
假设你克隆了一个远端仓库,那么原先的仓库就叫作origin
你可以push改动到origin中,经过 git push origin 命令. 固然,push到一个远端的仓库需要对仓库的写权限
你可以经过git remote add name gitrepo 命令加入�多个仓库。好比,你可以经过http协议再次加入�以前clone过来的仓库:
// Add the https protocol git remote add githttp https://vogella@github.com/vogella/gitbook.git
假设你的防火墙屏蔽了出http之外的所有协议,那么使用http协议来获取仓库是很好的方法。.
Git相同支持经过代理server使用http协议。如下的Git命令会展现这一点。你可以为所有的程序设置代理server或者仅仅是为Git服务提供。
如下的样例用到了环境变量
# Linux export http_proxy=http://proxy:8080 # On Windows # Set http_proxy=http://proxy:8080 git clone http://dev.eclipse.org/git/org.eclipse.jface/org.eclipse.jface.snippets.git # Push back to the origin using http git push origin
如下的样例仅仅是用到了Git的配置
// Set proxy for git globally git config --global http.proxy http://proxy:8080 // To check the proxy settings git config --get http.proxy // Just in case you need to you can also revoke the proxy settings git config --global --unset http.proxy
除了若是本身的服务,你也可以使用Git服务提供商提供的服务。最流行的Git服务提供站点是GitHub和Bitbucket。它们都提供了有限制的免费服务
可以经过 https://github.com/ 訪问GitHub. GitHub上所有的公开仓库都是免费的。假设你想在上面使用私有的仓库,那么就需要付费给GitHub
GitHub需要你建立ssh的公钥私钥。生成一份Ubuntu的公钥私钥可以訪问 sshkey creation in Ubuntu ,Windows环境可以訪问msysgit ssh key generation.
在GitHub上建立一个帐户和一个仓库之后。你会收到怎样将你的项目上传到GitHUb的指南,当中的命令大体例如如下:
Global setup: Set up git git config --global user.name "Your Name" git config --global user.email your.email@gmail.com Next steps: mkdir gitbook cd gitbook git init touch README git add README git commit -m 'first commit' git remote add origin git@github.com:vogella/gitbook.git git push -u origin master Existing Git Repo? cd existing_git_repo git remote add origin git@github.com:vogella/gitbook.git git push -u origin master
可以经过 https://bitbucket.org/ 訪问Bitbucket. Bitbucket 提供了无限制了公共仓库和仅仅能有五我的訪问的私有仓库。假设你需要超过五我的訪问私有仓库,就需要付费给Bitbucket
这个教程主要说明Git命令行的使用。完毕了这个教程之后,你可能想要找到一个Git的图形工具
Git提供了两个图形工具。 gitk能够展现仓库的历史信息、git gui 让你能够经过编辑器来完毕Git操做
Eclipse EGit 项目提供了Git与Eclipse的集成,在最新的Eclipse版本号中可以找到
在提出问题以前,请先查看 vogella FAQ. 假设你有不论什么的问题或者是从文章中找到错误,那么可以使用www.vogella.com Google Group. 我本身写了一个简短的列表 how to create good questions 可能会对你实用.
Git homepage
EGit - Teamprovider for Eclipse
Video with Linus Torwalds on Git
Progit book - Free Git book
Video casts about Git
http://code.google.com/p/msysgit/ Git on Windows
http://github.com/guides/git-cheat-sheet Git Cheat Sheets
Date: 2012-08-26 日
HTML generated by org-mode 6.33x in emacs 23