Repository
使用GIT管理一个项目的时候,就需要一个仓库(repository)用于存放GIT对项目管理所必须要保存的各种文件,使用 git init 命令创建一个新的仓库。GIT会在当前目录下创建一个.git文件夹,用作仓库。然后,当你使用GIT记录每一次代码改动的时候,GIT就会把需要的信息都放到这个文件夹下面。
Commit
每当你完成一个改动(change)的时候,就可以commit一下,记录当前项目的情况(snapshot)。每一个commit包含如下信息:
- commit-id(sha-id):这是一个通过sha-1算法算出来的一个id,它不只作为每个commit的唯一识别码,也可以用来验证保存的代码是否损坏。所以在GIT在管理代码的时候,如果发生代码损坏,可以很有效的检测到。
- author:作者
- date: 日期时间
- log messge:一段作者对代码改动的文字描述。
- change-id:如果是和repo、gerrit一起使用的话,repo会加上这个信息,以便在整个代码系统中识别同一个改动的多个版本。
用代码管理软件管理代码的时候,有个理念就是:每次改动提交以后,整个项目都应该是可以工作的。所以,每次commit最好都能是这样的的改动,虽然这不是必须的,但是这样对项目整体质量的提高很有好处。
在GIT使用中,一个change开发的常见工作流程大概是这样的:
- 新建一个工作分支(topic branch)
- 作一些代码改动
- 查看当前状态(很常用!):git status 查看当前branch以及那些文件有改动;git diff 查看目前为止的详细的改动情况。
- git add xxx 将这些改动暂时归档。在很多时候,一个change包含的东西并不少,我们在做到一半的时候通常也需要暂时保存一些东西。使用git add会将指定的文件的现有改动暂时保存下来(这个暂时归档的地方,GIT称它为staging area,可以认为是一个临时的snapshot。顺便说一下HEAD,在GIT中,HEAD总是指向项目当前状态最近的一次正式的snapshot。)。git add . 会将所有改动暂时归档。
- git reset xxx 将某个文件从staging area中移出来。git reset 不带参数的时候,就是将staging area中所有的文件移出。
- 查看staging area中的详细改动:git diff --cached
- 继续写代码,git add xxx 将新的改动归档。
- 当你发现你的改动非常不合理,想要取消这些改动的时候:git checkout xxx 将某个文件恢复到修改之前(最后一次snapshot)。git checkout . 会恢复所有的文件到修改之前的状态,很方便的功能,但使用的时候一定注意,不要让自己的心血付诸流水。
- 重复上面的步骤,直到你认为你的改动已经可以作为一个有效的change,那你就可以执行 git commit 来将当前的改动真正的保存下来。记得在commit message里面写上适当的描述信息,这样在查看代码历史的时候,就能方便的知道每个change都干了些什么。
- 有时候,你commit以后,你还发现还有需要修改的地方(特别是让别人review的话,commit之后需要多次改动也是很常见的事情),那么你就重复上面的步骤,不过在最后commit的时候加上 --amend 参数,这样,它就会将目前的修改内容添加到最近的一次commit。如果忘记/误用了 --amend 参数的时候怎么办呢?只要在编辑commit message的时候把内容全部清空,保存退出,GIT就会放弃这次commit。
- 根据情况,将这个改动合并(merge)回原来的主分支
Tag
在git中,每一个commit都是一个项目当时的snapshot,tag的作用就是给commit打上标签。比如你当项目进行到某一点,你觉得可以发布(release)一下,那你就可以在这个位置做一个标记,打一个tag,一个发布点就被明确的标示出来了,这样可以方便的对项目的情况进行管理。我通常把它理解为某个commit的别名。GIT对项目的版本管理通常就是在这里体现的。由于tag通常是用于项目发布管理的,所以通常是项目管理人员来使用它。
查看tag情况:git tag
新建一个tag:git tag <tagname> <commitid>
删除一个tag:git tag -d <tagname>
查看帮助:git tag -h
Branch
GIT最方便的莫过于本地分支(branch)的使用了。就开发来讲,你需要尝试你的各种想法,就发布项目来说,不同的
branch可以让你有效的管理各个版本软件的发布以及维护。这里只说说开发。
查看
branch:
git branch命令就可以查看本地当前的所有
branch。常用的参数有:
-r 查看远程(remote)的branch
-a 查看(本地+远程)所有branch
-v 查看
branch的时候显示每个
branch的最后一个commit信息
跳转到一个
branch:
你任何时候只能在某一个branch上面,或者某一个特定的snapshot。
git checkout branchname 跳转到某一个branch
git checkout sha1/tag 跳转到某个指定的snapshot,这是你不在任何一个branch上面。
新建一个
branch:
git branch newbranch 从项目当前所在的commit创建一个branch
git checkout sha1/tag -b newbranch 从某一个指定的snapshot创建一个新的branch,并跳转到新建的branch
git checkout remote_branch -b newbranch 以某个远程branch最新的一个snapshot为基础创建一个新的branch,注意这不会影响远程
branch,因为他的实质和上面其实是一样的。
那么我们该如何使用
branch呢?首先,你要知道GIT是个分布式的代码管理工具,在你没有和远程代码仓库交流的时候,你的任何改动都是本地的,不用担心影响到远程代码仓库。而你只要不刻意去破坏你本地的仓库,那么,就大胆的使用
branch吧,做各种尝试。任何时候你有一个新的想法,在本地建一个
branch尽情的去尝试,很快你就会喜欢上这东西的。
如果是个多人协作项目的话,通常你的工作并不只是改动一个地方,那么当你完成了某一个change正在等待review的时候,你就可以新起一个branch,接着就开始你的另一个change了。
Merge
有了分支你就可以尽情的做各种尝试,随心所欲的写代码。但我们通常是有一个主干分支,然后我们把每个成熟的改动的合并(merge)到我们的主干,这样,项目才真正有效的在前进。这个工作流程就完美的解决了多人协作开发项目的问题:因为每个人都是在自己的本地工作区进行修改,互不影响,在完成后将自己的成果放进主仓库(某个remote repository)就可以了,最坏的情况就是可能会有些代码冲突,但代码冲突通常并不是什么很难解决的问题:毕竟在分配任务的时候就会有大致进行安排,所以一般是不会有什么无法解决的冲突。
说到merge的话,很多时候有人就会和rebase搞混。虽然两个命令的功能都是把两个branch合并起来,但是还是有很大差别的。
rebase主要用于如下情形:在多人协作的项目中,当你完成了一个改动以后,你发现别人已经在远端主代码仓库添加了很多改动了,这时候很可能会有代码冲突,或者已经有代码冲突,那这时候,你就需要把你当前的工作branch更新到最新的状态,然后解决代码冲突,然后再提交你的change。这时候,你就:
- 从远端获取最新的代码
- git rebase xxx 更新你当前的branch到最新状态,然后把你的代码改动放在最上面
- 向远端提交代码
而merge的话,就只是简单的将两个branch合并,如果出现GIT无法处理的冲突的时候,那么就先采取上面的步骤,然后在merge。在Android的工作环境中,我们通常是不需要使用merge这个操作的,这个操作是在gerrit里面完成review以后,点merge按钮来完成的。
查看历史
最常用的查看项目历史(实际上是某个分支的历史)的命令就是git log,常见用法如下:
- git log 查看项目从当前位置(最近一个snapshot)之前的历史记录。可以这么来理解:当你在某个branch上面的时候,这个snapshot就是当前分支的最新的一个comit,当你不在一个branch上面的时候,就是当前情况下最新的那个commit。
- git log shaid/tag 同上,不过指定了snapshot的位置
- git log file_name 同上上,不过查看的是特定文件相关的改动记录
- git log branch 查看特定branch的历史记录
- git log branch file_name 同上,不过查看的是特定文件相关的改动记录
- git log --no-merges 由于GIT在进行merge的时候,会自动生成一个merge的commit,但其实这个commit本身是没有内容的,--no-merges这个参数的作用就是在查看log的时候忽略这种commit。
这个命令可以满足大部分需要查询历史记录的情况,但有时候,你需要一个更强大的工具,那就是GITK。在当前branch下面,使用gitk命令,就可以使用gitk查看当前项目状态之前的历史记录。它是一个有界面的工具,基本上是一目了然的,所以我也不过多的介绍,花几分钟去试试它吧。
Remote
GIT既然是分布式代码管理工具,那么就必然涉及到远程的交互了。GIT和远端的交互可以通过常见的HTTP、HTTPS,在比较正式的项目中,通常是基于SSH的GIT协议。
以github.com为例,上面支持3种方式:
- HTTP:只是从服务器上下载代码
- HTTPS:需要登录,可以下载和上传代码,但上传代码的时候需要每次登录
- SSH:配置好以后,GIT会自动采取SSH验证关于SSH的配置,这是github上面关于如何配置SSH的帮助:https://help.github.com/categories/56/articles
在Android系统的管理中,由于和服务器的交互都是封装在repo这个工具里面,所以,通常是不需要用到remote操作的。所以只简单说一下:
git remote 查看当前已添加的远程服务器
git remote -v 同上,不过显示更详细的信息
git remote -h 查看帮助
其他
.gitignore
在开发一个项目的时候,有时候总会有些项目无关的文件,比如bin文件夹下面的内容,一般是不需要用GIT管理起来的。这时候,你就把这些文件的信息写到.gitignore里面,然后,把.gitignore的改动使用GIT管理起来,这样,GIT在查询状态的时候,就会忽略在.gitignore里面提到的文件。
color & alias
GIT可以给各个命令的输出结果加上适当的颜色以提高视觉识别度,只需要简单的配置即可。如果你嫌有些GIT命令太长,每次打完太麻烦,没关系,有办法,添加别名。
因此我通常会在我的/etc/gitconfig文件中加入如下的内容:
[color]
ui = auto
[alias]
st = status
cm = commit
cma = commit --amend
br = branch -v
cp = cherry-pick
co = checkout
df = diff
dfc = diff --cached
如何使用alias:以第一个为例,当我想用git status的时候,我打git st就可以了。
小结
GIT的功能很丰富,上面说的这些不过是一些常用的功能而已,要想把每个细节都说清楚的话,还远远不够。想要用好GIT,还需要在实际运用中进行实践和进一步学习。
【Git & Repo & Gerrit 】