一 . 安装
在Windows上使用Git,可以从Git官网直接下载安装程序,
安装完成后,在开始菜单里找到“Git”->“Git Bash”,蹦出一个类似命令行窗口的东西,就说明Git安装成功!
安装完成后,还需要最后一步设置,在命令行输入:
$ git config --global user.name "Your Name"
$ git config --global user.email "email@example.com"
小记:
Git 有三种状态,你的文件可能处于其中之一:已提交(committed)、已修改(modified)和已暂存(staged)。
a. 已提交表示数据已经安全的保存在本地数据库中。
b. 已修改表示修改了文件,但还没保存到数据库中。
c. 已暂存表示对一个已修改文件的当前版本做了标记,使之包含在下次提交的快照中。
Git 项目的三个工作区域的概念:Git 仓库、工作目录以及暂存区域。
a. Git 仓库目录是 Git 用来保存项目的元数据和对象数据库的地方。 这是 Git 中最重要的部分, 从其它计算机克隆仓库时,拷贝的就是这里的数据。
b. 工作目录是对项目的某个版本独立提取出来的内容。 这些从 Git 仓库的压缩数据库中提取出来的文件,放在磁盘上供你使用或修改。 也就是你在电脑里能看到的目录,比如我的learngit
文件夹就是一个工作区
工作区有一个隐藏目录.git,这个不算工作区,而是Git的版本库。Git的版本库里存了很多东西, 其中最重要的就是称为stage(或者叫index)的暂存区,还有Git为我们自动创建的第一个分支master,以及指向master的一个指针叫HEAD。
c. 暂存区域是一个文件,保存了下次将提交的文件列表信息,一般在 Git 仓库目录中。 有时候也被称作‘索引’,不过一般说法还是叫暂存区域。
我们把文件往Git版本库里添加的时候,是分两步执行的:
第一步是用git add把文件添加进去,实际上就是把文件修改添加到暂存区;
第二步是用git commit提交更改,实际上就是把暂存区的所有内容提交到当前分支。
因为我们创建Git版本库时,Git自动为我们创建了唯一一个master分支,所以,现在,git commit就是往master分支上提交更改。你可以简单理解为,需要提交的文件修改通通放到暂存区,然后,一次性提交暂存区的所有修改。
二. 获取一个Git仓库,使用git init命令。分为2种情况
a. 在现有目录中初始化仓库
如果你打算使用 Git 来对现有的项目进行管理,你只需要进入该项目目录并输入:git init 。如果你是在一个已经存在文件的文件夹(而不是空文件夹)中初始化 Git 仓库来进行版本控制的话,你应该开始跟踪这些文件并提交。 你可通过 git add 命令来实现对指定文件的跟踪,然后执行 git commit 提交。
b.克隆现有的仓库,克隆仓库的命令格式是 git clone [url]
git clone https://github.com/libgit2/libgit2
这会在当前目录下创建一个名为 “libgit2” 的目录,并在这个目录下初始化一个 .git 文件夹,从远程仓库拉取下所有数据放入 .git 文件夹,然后从中读取最新版本的文件的拷贝。 如果你进入到这个新建的libgit2 文件夹,你会发现所有的项目文件已经在里面了,准备就绪等待后续的开发和使用。如果你想在克隆远程仓库的时候,自定义本地仓库的名字,你可以使用如下命令:
git clone https://github.com/libgit2/libgit2 mylibgit
三. git常用命令
1. 添加文件
添加文件到Git仓库,分两步:
- 使用命令git add <file>,注意,可反复多次使用,添加多个文件;
- 使用命令git commit -m <message>,完成。
2. 修改文件
- git status告诉你有文件被修改过
- git diff可以查看修改内容
- 保存修改, 和添加文件一样
git add <update_fiel>
git commit -m "commit update_file"
或者直接用 -a 命令
git commit -a -m "Changed some files"
git commit 命令的-a选项可将所有被修改或者已删除的且已经被git管理的文档,不需要git add 而直接提交到仓库中。
千万注意,-a不会造成新文件被提交,只能修改。
- 若要查看已暂存的将要添加到下次提交里的内容,可以用 git diff –cached 命令,Git 1.6.1 及更高版本还允许使用 git diff –staged,效果是相同的,但更好记些。
3. 版本回退
HEAD指向的版本就是当前版本,因此,Git允许我们在版本的历史之间穿梭,使用命令:
git reset --hard commit_id(前5,6位即可)
穿梭前,用git log可以查看提交历史,以便确定要回退到哪个版本。
要重返未来,用git reflog查看命令历史,以便确定要回到未来的哪个版本。
4. 撤销修改
分2种情况:
- 一种是 文件 自修改后还没有被放到暂存区,现在,撤销修改就回到和版本库一模一样的状态;
git checkout -- readme.txt (其实是从版本库重新检出一份文件)
- 一种是readme.txt修改后已经添加到暂存区后,执行以下两条命令
git reset HEAD readme.txt
git checkout -- readme.txt
5. 删除文件
有以下几个步骤:
1.手动删除工作区文件 readme.txt
2.删除暂存区文件 git rm/add readme.txt
3.提交 git commit -m "remove readme.txt"
另一种情况是删错了,因为版本库里还有呢,所以可以很轻松地把误删的文件恢复到最新版本:
git checkout -- readme.txt
6. 文件忽略
命令:git add .gitignore git commit -m "commit ignore file"
一般我们总会有些文件无需纳入 Git 的管理,也不希望它们总出现在未跟踪文件列表。 通常都是些自动生成的文件,比如日志文件,或者编译过程中创建的临时文件等。
在这种情况下,我们可以创建一个名为.gitignore 的文件,列出要忽略的文件模式。 来看一个实际的例子:
$ cat .gitignore
*.[oa]
*~
第一行告诉 Git 忽略所有以 .o 或 .a 结尾的文件。一般这类对象文件和存档文件都是编译过程中出现的。 第二行告诉 Git 忽略所有以波浪符(~)结尾的文件,许多文本编辑软件(比如 Emacs)都用这样的文件名保存副本。 此外,你可能还需要忽略 log,tmp 或者 pid 目录,以及自动生成的文档等等。 要养成一开始就设置好 .gitignore 文件的习惯,以免将来误提交这类无用的文件。
文件 .gitignore 的格式规范如下:
- 所有空行或者以 # 开头的行都会被 Git 忽略。
- 可以使用标准的 glob 模式匹配。
- 匹配模式可以以(/)开头防止递归。
- 匹配模式可以以(/)结尾指定目录。
- 要忽略指定模式以外的文件或目录,可以在模式前加上惊叹号(!)取反。
所谓的 glob 模式是指 shell 所使用的简化了的正则表达式。 星号(*)匹配零个或多个任意字符;[abc]匹配任何一个列在方括号中的字符(这个例子要么匹配一个 a,要么匹配一个 b,要么匹配一个 c);问号(?)只匹配一个任意字符;如果在方括号中使用短划线分隔两个字符,表示所有在这两个字符范围内的都可以匹配(比如 [0-9] 表示匹配所有 0 到 9 的数字)。 使用两个星号(*) 表示匹配任意中间目录,比如`a/**/z` 可以匹配 a/z, a/b/z 或 `a/b/c/z`等。
我们再看一个 .gitignore 文件的例子:
# no .a files
*.a
# but do track lib.a, even though you're ignoring .a files above
!lib.a
# only ignore the TODO file in the current directory, not subdir/TODO
/TODO
# ignore all files in the build/ directory
build/
# ignore doc/notes.txt, but not doc/server/arch.txt
doc/*.txt
# ignore all .pdf files in the doc/ directory
doc/**/*.pdf
7. 移动文件
git mv README.md README
git commit -m "README.md -> README"
其实,运行 git mv 就相当于运行了下面三条命令:
mv README.md README
git rm README.md
git add README
8. 远程仓库
- 查看远程仓库命令: git remote -v
- 生成密钥:ssh-keygen -t rsa -C "example01@163.com" 为什么GitHub需要SSHKey呢?因为GitHub需要识别出你推送的提交确实是你推送的,而不是别人冒充的,而Git支持SSH协议,所以,GitHub只要知道了你的公钥, 就可以确认只有你自己才能推送。当然,GitHub允许你添加多个Key。假定你有若干电脑,你一会儿在公司提交, 一会儿在家里提交,只要把每台电脑的Key都添加到GitHub,就可以在每台电脑上往GitHub推送了。
- 为本地仓库配置远程仓库:git remote add <shortname> <url> 添加一个新的远程 Git 仓库,同时指定一个你可以轻松引用的简写:
git remote add learngit git@gitee.com:superjay/learngit.git
本地项目名最好与远程项目名一致
- 从远程库拉取最新代码:
git pull learngit master
- 把本地仓库代码推到远程仓库:
git push -u learngit master 如果没有配置公钥,过程中会让你输入帐号,密码(自己的远程仓库账户密码)
我们第一次推送master分支时,加上了-u参数,Git不但会把本地的master分支内容推送的远程新的master分支,还会把本地的master分支和远程的master分支关联起来,在以后的推送或者拉取时就可以简化命令。
以后:git push learngit master
- 团队合作中:项目创建者把项目成员账户添加到项目中来,项目成员执行克隆命令,获取代码
git clone https://gitee.com/superjay/learngit.git learngit
之后项目成员即可进行项目的推拉操作
- 查看远程仓库
git remote show learngit
- 删除远程仓库
git remote remove <远程仓库名>
- 修改远程仓库名
git remote rename <旧名> <新名>
9. 分支管理
有了分支,就不用怕了。你创建了一个属于你自己的分支,别人看不到,还继续在原来的分支上正常工作,而你在自己的分支上干活,想提交就提交,直到开发完毕后,再一次性合并到原来的分支上,这样,既安全,又不影响别人工作。
a. 创建分支,首先,我们创建dev分支,然后切换到dev分支:
git checkout -b dev
git checkout命令加上-b参数表示创建并切换,相当于以下两条命令:
git branch dev (创建分支)
git checkout dev (切换分支)
查看当前分支:git branch
b. 在dev分支上工作,并提交修改过的或新文件
c. 现在,dev分支的工作完成,我们就可以切换回master分支:
git checkout master
d. 现在,我们把dev分支的工作成果合并到master分支上
git merge dev
通常,合并分支时,如果可能,Git会用Fast forward模式,但这种模式下,删除分支后,会丢掉分支历史信息, 如果要强制禁用Fast forward模式,Git就会在merge时生成一个新的commit,这样,从分支历史上就可以看出分支信息。 请注意--no-ff参数,表示禁用Fast forward,加上-m参数,把commit描述写进去。
git merge --no-ff -m "merge with no-ff" dev
e. 合并完成后,就可以放心地删除dev分支了:
git branch -d dev
如果要丢弃一个没有被合并过的分支,可以通过git branch -d <name>强行删除。
10. 分支合并冲突
有时候合并操作不会如此顺利。 如果你在两个不同的分支中,对同一个文件的同一个部分进行了不同的修改,Git 就没法干净的合并。
为了解决冲突,你必须选择使用由 ======= 分割的两部分中的一个,或者你也可以自行合并这些内容,解决了所有文件里的冲突之后,对每个文件使用 git add 命令来将其标记为冲突已解决。 一旦暂存这些原本有冲突的文件,Git 就会将它们标记为冲突已解决。
如 :
git add readme.txt
git commit -m "conflict fixed"
11. bug分支
假如你正在dev分支上进行工作,当你接到一个修复一个代号101的bug的任务时,很自然地,你想创建一个分支issue-101来修复它, 但是,等等,当前正在dev上进行的工作还没有提交:并不是你不想提交,而是工作只进行到一半,还没法提交,怎么办?
幸好,Git还提供了一个stash功能,可以把当前工作现场“储藏”起来,等以后恢复现场后继续工作:
git stash
现在,用git status查看工作区,就是干净的(除非有没有被Git管理的文件),因此可以放心地创建分支来修复bug。
首先确定要在哪个分支上修复bug,假定需要在master分支上修复,就从master创建临时分支:
git checkout master
git checkout -b issue-101 ...
太棒了,原计划两个小时的bug修复只花了5分钟!现在,是时候接着回到dev分支干活了!
切到dev分支:git checkout dev
查看工作现场:git stash list
回到工作现场:git stash pop
12. 抓取远程分支
当你的小伙伴从远程库clone时,默认情况下,你的小伙伴只能看到本地的master分支。
现在,你的小伙伴要在dev分支上开发,就必须创建远程origin的dev分支到本地,于是他用这个命令创建本地dev分支:
git checkout -b dev origin/dev
你的小伙伴已经向origin/dev分支推送了他的提交,而碰巧你也对同样的文件作了修改,并试图推送:
推送失败,因为你的小伙伴的最新提交和你试图推送的提交有冲突,解决办法也很简单,Git已经提示我们,先用git pull把最新的提交从origin/dev抓下来,然后,在本地合并,解决冲突,再推送:git pull也失败了,原因是没有指定本地dev分支与远程origin/dev分支的链接,根据提示,设置dev和origin/dev的链接:
git branch --set-upstream-to=origin/dev dev
13. git rebase
rebase操作可以把本地未push的分叉提交历史整理成直线;
rebase的目的是使得我们在查看历史提交的变化时更容易,因为分叉的提交需要三方对比。
14. 标签管理
发布一个版本时,我们通常先在版本库中打一个标签(tag),这样,就唯一确定了打标签时刻的版本。将来无论什么时候,取某个标签的版本,就是把那个打标签的时刻的历史版本取出来。所以,标签也是版本库的一个快照。
Git的标签虽然是版本库的快照,但其实它就是指向某个commit的指针(跟分支很像对不对?但是分支可以移动,标签不能移动),所以,创建和删除标签都是瞬间完成的。
Git有commit,为什么还要引入tag?
“请把上周一的那个版本打包发布,commit号是6a5819e...”
“一串乱七八糟的数字不好找!”
如果换一个办法:
“请把上周一的那个版本打包发布,版本号是v1.2”
“好的,按照tag v1.2查找commit就行!”
所以,tag就是一个让人容易记住的有意义的名字,它跟某个commit绑在一起。
a.创建标签
在Git中打标签非常简单,首先,切换到需要打标签的分支上:
然后,敲命令git tag <name>就可以打一个新标签:
git tag v1.0
默认标签是打在最新提交的commit上的。也可以对指定的commit id 打标签:
git tag v0.9 f52c633
还可以创建带有说明的标签,用-a指定标签名,-m指定说明文字:
git tag -a v0.1 -m "version 0.1 released" 1094adb
b.查看标签
git tag 查看全部标签
git show <tagname> 查看标签信息:
c.删除标签
git tag -d v0.1
因为创建的标签都只存储在本地,不会自动推送到远程。所以,打错的标签可以在本地安全删除。
d.推送标签到远程
git push origin v1.0
或者,一次性推送全部尚未推送到远程的本地标签:
git push origin --tags
如果标签已经推送到远程,要删除远程标签就麻烦一点,先从本地删除:
git tag -d v0.9
然后,从远程删除。删除命令也是push,但是格式如下:
git push origin :refs/tags/v0.9
15. git fetch和git pull
git fetch是将远程主机的最新内容拉到本地,用户在检查了以后决定是否合并到工作本机分支中。而git pull 则是将远程主机的最新内容拉下来后直接合并,即:git pull = git fetch + git merge,这样可能会产生冲突,需要手动解决。
a. git fetch 用法
git fetch <远程主机名> //这个命令将某个远程主机的更新全部取回本地
git fetch <远程主机名> <分支名> //注意之间有空格,只想取回特定分支的更新,可以指定分支名,取回更新后,会返回一个FETCH_HEAD ,指的是某个branch在服务器上的最新状态,我们可以在本地通过它查看刚取回的更新信息:
git log -p FETCH_HEAD
b.git pull 用法
前面提到,git pull 的过程可以理解为:
git fetch origin master //从远程主机的master分支拉取最新内容
git merge FETCH_HEAD //将拉取下来的最新内容合并到当前所在的分支中
例子:
git fetch origin master:tmp
git diff tmp
git merge tmp
git branch -d tmp
即将远程主机的某个分支的更新取回,并与本地指定的分支合并,完整格式可表示为:
git pull <远程主机名> <远程分支名>:<本地分支名>
如果远程分支是与当前分支合并,则冒号后面的部分可以省略:
git pull <远程主机名> <分支名>