本文讲述了如何导入新项目到Git,如何修改项目以及如何将修改通知给其他开发者。
如果主要是想了解如何使用Git来取得一个项目,例如测试项目的最新版本,可以参考Git用户手册的前两章。
这里首先介绍下如何查阅Git文档,下面以命令git log --graph为例:
在开始使用Git之前,最好设置使用者的名字和电子邮件以方便Git记录日志。设置方法如下:
- $ git config --global user.name "Your Name Comes Here"
-
$ git config --global user.email you@yourdomain.example.com
假如项目文件为project.tar.gz,可以通过如下方法将其导入到Git。
- $ tar xzf project.tar.gz
-
$ cd project
-
$ git init
- Initialized empty Git repository in .git/
至此,工作目录已经初始化完成,并且在当前位置创建了一个新目录.git。
接着,利用git add命令将当前目录下所有文件的快照通知Git:
当前目录所有文件的快照被存储在索引文件中。可以利用git commit命令把索引文件的内容永远存储到Git中:
这条命令会提示用户输入注释。至此,项目的第一个版本已经存储到Git中了。
- $ git add file1 file2 file3
现在可以提交了。在提交以前,可以通过如下命令检查即将提交的内容与原始内容的差异:
(如果不添加--cached选项, git diff将会显示所有没有添加到索引文件的修改)
也可以通过命令git status来获取当前Git状态:
- $ git status
-
# On branch master
-
# Changes to be committed:
-
# (use "git reset HEAD <file>..." to unstage)
-
#
-
# modified: file1
-
# modified: file2
-
# modified: file3
-
#
如果还需要修改其他内容,那赶紧改吧。然后,就可以把所有的修改加到索引文件,并用下面的命令提交:
执行上面的命令,系统会提示输入注释,然后记录一个新版本到版本库。
如果不想在提交之前执行git add命令,可以使用:
这条命令可以自动检查所有修改(不包括新加文件),然后添加到索引文件中并提交。
注意:在提交修改时,最好在注释的开始写上一行简短的修改摘要(少于50个字符),接着一个空行,最后是关于修改的详细描述。Git会把修改注释转换成电子邮件,例如,把注释的第一行作为邮件的标题,其余部分作为邮件的内容。
很多版本控制系统都提供add命令,用于通知版本控制系统跟踪新添加文件的修改。Git的add命令用法更简单,功能更强:git add 不仅用于跟踪新添加文件,还可以用于跟踪最近修改内容的文件。在这两种情况下,Git都会记录修改,并修改索引文件,再下次提交的时候将修改写入版本库。
查看项目的修改历史日志
更为常用的用法是利用下面的命令查看每一次修改的总结
- $ git log --stat --summary
一个简单的Git版本库可以支持多个分支的开发。使用如下命令创建名为“experimental”的分支:
- $ git branch experimental
"experimental"是刚刚创建的分支,"master"是系统自动创建的默认分支。星号代表当前用户正在工作的分支。输入如下命令:
- $ git checkout experimental
切换为“experimental“分支。然后编辑文件,提交,并返回“master”分支:
- (edit file)
-
$ git commit -a
-
$ git checkout master
检查刚刚的修改在当前分支上并不存在,这是因为刚刚在“experimental”分支上修改,修改后切换回“master”分支。
针对同一文件,在"master"分支做不同的修改:
- (edit file)
-
$ git commit -a
此时,两个分支出现不一致,不同的分支上有着不同的修改。为了把“experimental”分支上的修改合并到“master”分支,需要执行
如果修改没有冲突,那么合并就做完了。否则,用于表明冲突的标记会被添加到冲突的文件中。
git diff可以用来查看冲突。一旦你修改了文件,解决了冲突
这时,可以执行下面的命令删除“experimental”分支
- $ git branch -d experimental
这个命令可以确保“experimental”分支的全部修改已经合并到当前的分支,否则删除将失败。
如果不想在“crazy-idea”分支上继续开发,可以执行如下命令删除分支
- $ git branch -D crazy-idea
Git的分支操作如此的简单高效,所以,这是我们做试验的好方法。
假设Alice已经开始了一个新项目,项目的Git版本库位于/home/alice/project。Bob想要参与到项目中来,并且,Bob在同一台机器上也有home目录。
- bob$ git clone /home/alice/project myrepo
这条命令创建了一个新目录"myrepo",它是Alice的Git版本库的克隆。这个克隆的版本库和原始的项目处于同等地位,一切在克隆版本库上的操作,都会记录在属于克隆版本库自己的项目日志文件中。
- (edit files)
-
bob$ git commit -a
-
(repeat as necessary)
当修改完成后,Bob通知Alice把他的修改从位于同台机器/home/bob/myrepo目录下的Git版本库中合并到本地。Alice执行了下面的命令
- alice$ cd /home/alice/project
-
alice$ git pull /home/bob/myrepo master
这条指令将会把Bob在"master"分支做的修改合并到Alice当前的分支上。如果同时Alice也在修改,那么她需要手动的解决冲突。
"pull"命令实际上执行两个操作:从待合并的分支获取变更,然后把它们合并到当前分支。
注意:一般情况下,Alice希望在执行“pull”命令之前把她的本地修改提交到Git版本库。如果Bob的修改与Alice的修改存在冲突,Alice将要利用她的版本树和索引文件来解决冲突。这时候现有的本地修改将会干扰冲突的解决(Git将会执行获取,但是不会执行合并……Alice将不得不删掉本地的修改,然后重新执行"pull"命令)。
Alice可以在没有合并之前利用“fetch”命令来查看一下Bob的修改;为了决定到底是否需要利用“pull”命令获取Bob的修改,Alice可以使用特殊的符号"FETCH_HEAD"来查看Bob的修改:
- alice$ git fetch /home/bob/myrepo master
-
alice$ git log -p HEAD..FETCH_HEAD
即使Alice存在没有提交的本地修改,这条指令也不会影响本地的修改。范围"HEAD..FETCH_HEAD"的含义是:显示从FETCH_HEAD开始可以取得的修改,但是不包含从HEAD开始可以取得的修改。Alice已经知道使她处于当前状态的(HEAD)的所有修改,通过这个命令Alice可以查看Bob所处状态((FETCH_HEAD)的修改。
如果Alice想要以一种图形化的方式查看Bob的修改,她可以使用如下命令:
这种两个点表示范围的用法和我们刚才使用git log时的用法是一致的。
Alice或许想要查看他们两个人的修改。她可以使用三个点的形式:
三个点表示范围的含义是:显示从它们任意一个开始可以取得的修改,但是不包含从它们两个全都包含的修改。
注意:以三个点表示范围的用法既可以和“gitk”一起使用,也可以和"git log"一起使用。
在检查了Bob的修改以后,如果没有什么紧急的修改,Alice或许会继续本地的修改,而不会合并Bob的修改到本地。如果Bob的修改是Alice当前需要的,Alice可能会选择先备份她本地的修改,然后,取得Bob的修改到本地,最后恢复她的备份。
当你工作在一个小的工作组时,通常会不断操作同一版本库。可以通过定义远程版本库缩写使操作更简单:
- alice$ git remote add bob /home/bob/myrepo
定义了版本库缩写,Alice可以利用git fetch命令单独执行"pull"操作的第一部分,而不去执行合并操作:
和git fetch普通用法不一样的是,当Alice利用缩写来取得Bob的修改时,取得的修改是存储在远程跟踪分支(remote-tracking branch),上面的例子中存储在bob/master分支。因此,运行如下命令:
- alice$ git log -p master..bob/master
显示的是从Bob克隆Alice分支后所有修改的列表。
在检查过所有的修改后,Alice可能会合并所有的修改到本地的"master"分支:
- alice$ git merge bob/master
也可以通过从远程跟踪分支(remote-tracking
branch)上获取来完成合并,像下面这样:
- alice$ git pull . remotes/bob/master
注意:无论在命令行上添加任何参数,git pull总是合并修改到当前的分支。
注意:Bob不需要指定Alice版本库的路径。当Bob克隆Alice版本库的时候,Git将Alice版本库的位置保存在版本库的配置中,这个配置在执行git pull命令的时候使用:
- bob$ git config --get remote.origin.url /home/alice/project
Git还保存了Alice版本库master分支的原始拷贝,名字为"origin/master":
- bob$ git branch -r origin/master
如果以后Bob希望在另一台机器上工作,他可以利用ssh协议来执行克隆,获取:
- bob$ git clone alice.org:/home/alice/project myrepo
Git历史日志被表示为一系列相关的提交。在前面的例子中,已经看到git log可以列出这些提交。值得注意的是,每条日志的第一行是提交的名字:
- $ git log
-
commit c82a22c39cbc32576f64f5c6b3f24b99ea8149c7
- Author: Junio C Hamano <junkio@cox.net>
-
Date: Tue May 16 17:18:22 2006 -0700
- merge-base: Clarify the comments on post processing.
- $ git show c82a22c39cbc32576f64f5c6b3f24b99ea8149c7
Git还支持其他的方法查看提交的详细内容。可以使用提交名字的一部分来标识提交:
- $ git show c82a22c39c # the first few characters of the name are
-
# usually enough
-
$ git show HEAD # the tip of the current branch
- $ git show experimental # the tip of the "experimental" branch
每个提交一般都有一个父提交,父提交指向项目的前一个状态:
- $ git show HEAD^ # to see the parent of HEAD
-
$ git show HEAD^^ # to see the grandparent of HEAD
-
$ git show HEAD~4 # to see the great-great grandparent of HEAD
- $ git show HEAD^1 # show the first parent of HEAD (same as HEAD^)
- $ git show HEAD^2 # show the second parent of HEAD
- $ git tag v2.5 1b2e1d63ff
可以通过自定义名字“v2.5"查看提交1b2e1d63ff。如果你希望和其他人共享这个自定义名字(例如:指定发行版本号), 你应该创建一个"tag"对象并签名,详情参见git-tag(1)。
任何涉及提交内容的命令都可以使用自定义名字。例如:
- $ git diff v2.5 HEAD # compare the current HEAD to v2.5
-
$ git branch stable v2.5 # start a new branch named "stable" based
- # at v2.5
-
$ git reset --hard HEAD^ # reset your current branch and working
- # directory to its state at HEAD^
请注意最后一个命令:除了丢失当前工作目录的所有修改, 当前分支之后的的提交也会被删除。如果那些提交仅仅存在于当前分支,那么它们会彻底丢失。再者,不要在公共可见的分支上使用git reset命令,因为它将强制其他开发者做没有必要的合并,从而导致其他开发者丢失修改。如果你需要回滚你的提交,可以使用git revert。
命令git grep可以用来在项目的任意版本中搜索字符串,因此
上述命令会在v2.5这个提交中搜索"hello"。
如果不指定提交名称,git grep将会搜索当前目录的所有文件。
上述命令是一种比较快捷的方法用来搜索所有Git跟踪的文件。
很多Git命令可以选取一组提交,而且有多种实现方法。下面以git log为例:
- $ git log v2.5..v2.6 # commits between v2.5 and v2.6
- $ git log v2.5.. # commits since v2.5
- $ git log --since="2 weeks ago" # commits from the last 2 weeks
-
$ git log v2.5.. Makefile # commits since v2.5 which modify
- # Makefile
在使用git log的时候,可以指定一个范围,而且范围的起始点不必是终止点的祖先。例如,如果分支“stable"和分支"master"在一段时间以前就已经不一致了,那么
将会列出在“master”分支上的提交,而不会列出在“stable”分支上的提交,然而,
将会列出在“stable”分支上的提交,而不会列出在“master”分支上的提交。
命令git log有一个缺点:它必须把提交以列表的形式展现。当历史记录中包含一些分叉的开发,然后又被合并到一起,那么git log所展现的这些提交的顺序则失去了意义。
有很多参与者的项目(例如Linux kernel或者Git本身)都会频繁的做合并,gitk在这方面做的比较好,提供给用户以图形的界面。例如,
- $ gitk --since="2 weeks ago" drivers/
上述命令允许你浏览最近两个星期在“drivers”目录下的提交。(注意:可以通过按住Ctrl+或者Ctrl-来调整gitk的字体。)
最后,大多数以文件名作为参数的命令,允许你在文件名前指定版本号:
- $ git diff v2.5:Makefile HEAD:Makefile.in
本指南对于在项目中使用分布式版本管理应该是足够了。但是,如果想更全面的理解Git,你应该了解下面两个观念:
如果你不想立刻学习第二部分,还有一些其他方面的或许你会感兴趣: