Git是什么?
- 版本控制工具
- 命令行程序
Git能干什么?
- 管理代码,控制版本
- 同步文件
本文不具体介绍怎么使用git,只做基本的概念解说,更多信息请参考git help
Git是怎么工作的
代码仓库(repository)
进入软件源代码的根目录(项目文件夹),执行git init
命令,会生成一个.git目录,.git目录里保存着git工作时所需的所有信息,它就是代码仓库。
文件跟踪(track)
通过git add <文件名>
来跟踪某个文件。一个文件只有被git跟踪才会受到版本控制,可以通过git status
命令来查看当前目录下的文件跟踪情况。
git目录里的文件有这么几个状态
- 未跟踪(untracked) 可以说是从没被
git add
过的文件 - 未修改(unmodified) 指自从上次被提交后还没有过改动的文件
- 已修改(modified) 指自从上次被提交后有过改动的文件
- 已暂存(staged) 已修改状态的文件再次经过
git add
后会被暂存
提交(commit)
软件项目每完成一个阶段(添加了一个功能或者修复了一个bug等),都要提交一次,而且要有提交说明。每一次提交都会将相比上次提交有过改动的文件加入到repository里,可以通过git log
命令来查看提交历史。
分支(branch)
git分支的出现是出于以下的需求:我的程序开发到一个稳定的阶段,我想添加一个新的功能,但是在开发的过程中可能会破坏程序原来的稳定性。怎样才能保证用户能正常使用程序原有的稳定的功能,又不耽误基于现有代码上的新功能的开发呢?如果没有git,我们会考虑将现有的代码拷贝一份保存起来,这样也不是很麻烦,可是我的代码每稳定一次就要完全保存一份,岂不是太麻烦了,而且有很多代码相比上次稳定的没有过改动。
对于保存改动过的代码,我们自然想到了commit。我们可以把稳定的代码commit了,要用稳定版代码的时候再通过git reset
系列命令reset回来。但是这样问题来了,我把开发版的代码reset到稳定版了,我再怎么回到开发版呢?
于是乎,分支来了。我们可以在主分支上提交过稳定代码过后,通过git checkout -b dev
命令新建并切换到dev分支,这样日后我们在dev分支里不管代码有过什么改动,都可以通过git checkout master
命令切换到主分支,切换回来后代码会完好如初。用完master的代码,我们随时都可以再通过git checkout dev
命令切换到dev分支,代码又变成了你正在开发的版本。等新功能开发完了,我们commit一下,再切换到master分支,用git merge dev
命令将dev分支的代码合并过来,至此,我们的新功能才添加到主分支里。至于dev分支,如果不需要了,可以使用git branch -D dev
命令删掉。
远程分支(remote branch)
删除远程仓库中的分支
git push :dev ----- 删除远程仓库中的dev分支
Git ignore
在项目根目录下建立一个.gitignore文件,里面可以存放需要忽略的文件信息。文件名直接写,目录要在目录名后面加斜线/,可以使用通配符,不同的条目之前要单独成行。像下面这样的.gitignore文件:
bin/
test-*.c
bugs.md
git在跟踪文件会忽略bin目录,所有test-*.c形式的文件以及bugs.md文件,git status
命令也不会提示这些文件未跟踪。
Git深入
Git内部对象
Git将所有的跟踪信息都放在了.git目录里,我们的提交、分支等都是特定的对象,都被git以文件的形式保存在了.git目录里。.git目录像是我们在开发过程中的内存,里面的文件组织结构便是git的数据结构。一般不要直接修改.git目录里的文件,毕竟手动修改'内存'是很危险的,除非你对git内部的工作原理非常熟悉。
- blob ----- 对应文件
- tree ----- 对应目录
- commit ----- 提交
branch是在内部也是以commit的形式记录,或者说branch是特殊的commit。
杂记
不常用但很有用的命令
没见过的命令要记得git help <>
,或者度娘
git commit --amend
修定上次提交git ls-tree
列出树对象git clone --depth=1 <url>
只克隆最后一次提交git rebase
删除以前没用的提交git clone --recursive
递归克隆submodule
Git reset 命令的三种方式
- git reset –mixed:此为默认方式,不带任何参数的git reset,即时这种方式,它回退到某个版本,只保留源码,回退commit和index信息
- git reset –soft:回退到某个版本,只回退了commit的信息,不会恢复到index file一级。如果还要提交,直接commit即可
- git reset –hard:彻底回退到某个版本,本地的源码也会变为上一个版本的内容
git revert
Commit关系图
图文来源于StackOverflow: http://stackoverflow.com/questions/2221658/whats-the-difference-between-head-and-head-in-git
G H I J
\ / \ /
D E F
\ | / \
\ | / |
\|/ |
B C
\ /
\ /
A
A = = A^0
B = A^ = A^1 = A~1
C = A^2 = A^2
D = A^^ = A^1^1 = A~2
E = B^2 = A^^2
F = B^3 = A^^3
G = A^^^ = A^1^1^1 = A~3
H = D^2 = B^^2 = A^^^2 = A~2^2
I = F^ = B^3^ = A^^3^
J = F^2 = B^3^2 = A^^3^2
最近的一次提交可以用HEAD表示,本例中A就是HEAD。G-D-B-A是主干(master)分支,C是A的次要祖先,B是A的直接祖先,当前分支的上级commit用,其它分支用^。多个、、~1、1的效果是一样的,因为git在识别时会从左向右计算。
参考资料
- Progit : http://git.oschina.net/progit/
- Git for Windows : https://git-for-windows.github.io/