GIT初步
git现在是最流行的版本控制软件,大多数linux发行版已经默认安装,github等托管网站也越来越流行。每个程序员都需要学习git。
git最权威的教程是git pro。这里简单记下笔记,目的只是入门,介绍一些最简单和常用的操作命令。
1. git的基本工作模式
在git之前,最流行的版本控制软件是svn。svn非常简单易用,基本命令就三条:checkout
、update
和commit
。svn只有两个区域,一个是服务器版本库,一个是本地工作区。checkout
用来在最开始下载服务器的代码库,update
用来获取服务器端的更新(来源于协助者),commit
用来提交自己本地工作区的更新。
git则要复杂一些,这个复杂性大大增强了git的功能,这个暂且不谈。简而言之,对git用户而言存在四个区域,服务器版本库、本地版本库、暂存区和本地工作区。这比svn
要多两个。这也让git的灵活性大大增加。最大的好处是,svn每次提交都必须联网,但git可以提交到本地版本库,等待联网时再同步到服务器版本库。
2. 获取代码库
使用git的第一步是下载服务器版本库到本地,类似于svn的checkout
。要下载,需要先获得服务器的地址,现在很多项目都采取网络托管,流行的有github,gitcafe和oschina。如果想自己成立一个新项目,可以去这几个地方,其中oschina可以免费建私有项目,github和gitcafe建私有项目是收费的,但公开项目仍然免费。
假设我们有一个服务器版本库的地址http://xxx.com/yyy/zzz
,下面命令便能建立一个本地版本库开始工作:
git clone http://xxx.com/yyy/zzz
它会在当前目录下新建zzz
目录,目录下是工作文件。除了正常代码文件外,还有一个.git
的文件夹,里面便保存了版本信息。我们一般都不用理会它。
我们也可以指定下载的文件夹
git clone http://xxx.com/yyy/zzz abc
此处abc
可以是相对位置,也可以是绝对位置。如果abc
不存在,git将新建该文件夹。abc
的末尾可以带斜杠也可以不带。
如果想直接下载到当前文件夹,可以用.
表示:
git clone http://xxx.com/yyy/zzz .
在clone
的过程中,目标文件夹一定要是空的。如果已经有一些本地文件需要放入版本库,可以先clone
到另外一个文件夹,再复制本地文件到版本库,再把版本库挪到当前文件夹。
比如当前目录已经有一些文件,我们可以这么做:
git clone http://xxx.com/yyy/zzz ../tmp # 假设../tmp文件夹不存在或是空的
cp ./. ../tmp/ -ra
cp ../tmp/. ./ -ra
rm ../tmp -rf
它的一个基本思路是git
的工作目录其实是可以移来移去。
3. 提交自己的代码
现在程序员需在下载到本地的代码上开始自己的编程工作,然后修改了一些代码,新增了几个文件等等。接下来的操作和svn就有很大区别了。
在本地,git有三个区域,一个是工作区域(就是你不断修改代码的地方),一个是暂存区域,一个是本地版本库区域。对于一个已经纳入版本库的文件,根据处于位置的不同,就有三种状态,一种是未暂存,一种是已暂存未提交,第三种是已提交。
如果新增一个文件,其第一步是将其纳入版本库。把文件放入暂存区,就是纳入到版本库。
当文件提交到本地版本库后,最终还需要同步到网络共享服务器上。所以一个文件其实有四个状态(svn则只有两个状态)。一个修改的文件,需要经过三步,才能被其它共享者看到。这三步分别是:add
、commit
和push
。
3.1. status查看状态
使用git status
命令可以看到当前文档的状态。包括
- 目前本地版本库还有几个提交未同步到网络版本库,即
ahead of 'origin/master' by 1 comment
即目前还有一个本地提交未同步到网络共享版本库。 - 未提交的修改:
Changes to be committed
。 - 未暂存的修改:
Changes not stagged for commit
。 - 未纳入新版本的修改:
Untracked files
。
git status
显示的信息示例如下:
# On branch master
# Your branch is ahead of 'origin/master' by 1 commit.
# (use "git push" to publish your local commits)
#
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: xxx/abc
#
# Changes not staged for commit:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified: yyy/edf
#
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
# zzz/ijk
有一些中间文件,比如C++编译产生的.o
文件,不用纳入到版本控制中,但它总是会显示在此处的Untraced files
列表。通过编辑.gitignore,可以在此列表自动排除掉指定文件:
# ./.gitignore
*.o
.gitignore
文件也支持通配符,可以排除特定文件夹和特定文件类型。
.gitignore
文件是递归调用的。当查看一个文件是否该忽略时,直接查找当前目录的.gitignore
,若没有则往上查询,直到根目录为止。因此可以在不同文件夹下放置不同的.gitignore
文件。
3.2. add添加文件到暂存区
无论是新建的文件,还是修改过的文件,都可以通过add
命令将其添加到暂存区:
git add path/to/file
add
已支持通配符:
git add path/to/*
注意上面这个会把所有path/to/目录下所有文件都加入暂存区。如果该目录下有.gitignore
要求忽略的文件,该提交会出错。此时有两种方法,一种方法是增加-f
参数:
git add path/to/* -f
另一种方法是不要用通配符,只用目录,此时.gitignore
要求忽略的文件会被自动忽略:
git add path/to/
参数A
可以把删除行为也提交到暂存区:
rm test.c
git add -A . # test.c的删除也被更新到暂存区
add
会自动忽略.gitignore
里指定忽略的文件。要想添加已要求指定忽略的文件,需添加-f
参数:
git add test.o # not works because *.o is ignored in .gitignore
git add -f test.o # this works
另外一个需注意的地方是,git
只针对文件,不针对目录。所以无法添加空目录到版本库。如果实在要的话怎么办呢?可以在该目录下添加一个空的.gitignore
文件,再把这个文件添加到版本库。这样这个空目录就能添加到版本库了。当需删除该目录时,在版本库里删除该.gitignore
文件即可。
如果有多个空目录,可以用下面bash
命令同时添加.gitignore
文件:
$ find . \( -type d -empty \) -and \( -not -regex ./\.git.* \) -exec touch {}/.gitignore \;
3.3. commit提交到本地存档
commit
将暂存档里的文件保存到本地版本库。在commit
前,可以运行多次add
命令。参数-m
用来指定提交的参考信息。
git commit -m 'bla bla...'
如果提交时无需做特别的选择,add
和commit
可合二为一,在commit
时指定参数-a
,可以自动将所有的修改添加到暂存档,同时提交:
git commit -a -m 'bla bla...'
3.3.1. commit --amend修改最后一次提交
有时候提交之后发现有几个文件没有贴上,commit --amend
给了一次后悔的机会,它将把当前暂存区的文件和上次提交合并再次提交:
git commit -m 'init commit'
git add forgotten_files
git commit --amend
提交之后没有改动直接运行commit --amend
则有机会重新编辑提交说明。
3.4. push同步到网络版本库
程序员可以无限制地通过commit
向本地版本库提交更新,但此时其它人还看不到这些更新。这时候可以通过push
将本地版本库更新到网络共享版本库。
git push
3.5. pull更新本地版本库
当其它程序员通过push
更新了网络共享版本,这时候我们应该把他们的更新下载下来,并在其基础上继续接下来的工作。pull
命令便是完成此事:
git pull
根据情况不同,会出现几种不同的情况。
- 如果本地还没有任何提交,并且网络上的更新的文件在本地没有被更新,这时候网络版本直接下载到本地版本。
- 如果本地还有未提交的修改,并且同一文件被其它人修改并更新到了网络共享版本库,这时候会出错。这时候需先把当前工作区或暂存区的修改提交,然后再次
pull
。 - 如果本地已经有提交,同时和网络上的更新修改了同样的文件。如果这两方面的修改没有冲突(比如修改不同文件,或者修改同一文件的不同位置),git将自动进行合并,并且生成一个新的提交(到本地版本库)。
- 如果这两方面的修改有冲突(一般只修改了同一文件的相同位置),git将合并文件。
合并的文件一般长下面这样:
<<<<<<< HEAD
ver 1
=======
ver 2
>>>>>>> HASHIDFORLASTCOMMIT
此时用户需自行删除一些行,然后重新add
和commit
。
4. 回退到历史版本
版本控制的一个最大的功能(尤其是对于单个开发者的项目)便是保存历史修改记录,以便随时可以撤销修改回退到历史版本。由于git的文件有多个状态,所以回退也有几种情况:
4.1. 回退到上次暂存状态
这个是指在把文件放到暂存区之后,又修改了文件,这时候想把文件回退到暂存区的状态,此时可以用checkout
的命令:
git checkout -- path/to/file.post
4.2. 回退到上次提交的状态
用reset
可以将文件恢复到上次提交的状态:
git reset HEAD
4.3. 取消本地所有修改和提交
git reset
可以取消本地所有修改和提交:
git reset --hard origin/master