1. git是什么
一种分布式版本控制工具。
2. 为什么选择使用git
2.1 git的特点
git和其他版本控制工具的主要差别:git 只关心文件数据的整体是否发生变化,而大多数其他版本控制工具则只关心文件内容的具体差异。
git并不保存version变化前后的差异数据。git作为一个小型的文件系统,每次提交更新(commit)时,它会纵览一遍所有的文件指纹信息并对文件做一次快照,然后保存一个指向这次快照的索引。为了提高性能,若文件没有变化,git只对上次保存的快照索引做一次链接。
2.2 git的优点
(1)几乎所有操作都可以本地执行;
(2)可以时刻保证数据的完整性:SHA-1算法计算数据的校验和;
(3)多数的操作仅是添加数据。
3. git的基础
3.1 git的三种状态
对于任何一个文件,在 git 内都只有三 种状态:已提交(committed),已修改(modified)和已暂存(staged)。已提交表示该文件已经被安全地保存在本地数据库中了;已修改表示修改了某个文件,但还没有提交保存;已暂存表示把已修改的文件放在下次提交时要保存的清单中。
所谓的暂存区域只不过是个简单的文件,一般都放在 git 目录中。有时候人们会把这个文件叫做索引文件,不过标准说法还是叫暂存区域。
基本的 Git 工作流程如下所示:
(1) 在工作目录中修改某些文件。
(2)对这些修改了的文件作快照,并保存到暂存区域。
(3)提交更新,将保存在暂存区域的文件快照转储到 git 目录中。
git 提供了一个叫做 git config 的工具,专门用来配置或读取相应的工作环境变量。这些变量可以存放在以下三个不同的地方:系统级别的/etc/gitconfig文件 ;用户级别的 ~/.gitconfig ;项目级别的 在工作目录中的.git/config 。使用git config --list 命令可以查看配置信息。
其中有些在目录中但是不需要被git管理的文件可以在.gitignore文件中进行设置,具体设置方法请参考。
3.2 git的基本命令
git config --global user.name "**" # "**"换成自己名字的拼音,注意中间不要有空格
git config --global user.email *@****.com
|
1. git init # cd到工作目录中
2. git clone ssh://git@git.**.com/web/www.git # 其中www是要克隆的版本库
|
git pull
|
git status
|
git add **.java # 提交**.java文件到暂存区
git commit -m [-a] “更新描述” # 使用-a可以跳过使用暂存区域,即跳过git add
|
git pull # 更新代码库到最新版本
git push # 开始推送
git push origin master # 对新建远程仓库的第一次推送,需要指定主分支名master
|
git add **.java # 提交**.java文件到暂存区
git reset HEAD **.java # 删除暂存区中的**.java文件
|
git diff # 查看工作区与暂存区的文件差异
git diff --cached # 查看暂存区与最后一次本地提交的文件差异
git diff HEAD # 工作区与最后一次本地提交之间的差异
git diff origin # 工作区与本地仓库原始版本比较
|
git log # 后面加上参数可以查看加上限制条件的记录
|
git remote # 列出每个远程库的简短名字
git remote add [remote-name] [url] # 添加远程库
git fetch [remote-name] # 把远程仓库remote-name更新至本地仓库
git push [remote-name] [branch-name] # 将本地仓库中的数据推送到远程仓库
git remote show [remote-name] # 查看远程仓库remote-name的信息
|
git mv file_from file_to 等价于
git rm file_from
git add file_to
|
git help command # command是对应的git命令
|
3.3 git的分支与合并
(1)分支的概念
当使用 git commit 新建一个提交对象前,git 会先计算每一个子目录的校验和,然后在 git 仓库中将这些目录保存为树(tree)对象。之后 git 创建的提交对象,除了包含相关提交信息以外,还包含着指向这个树对象的指 针,如此它就可以在将来需要的时候,重现此次快照的内容了。如下左图所示,git仓库中有五个对象:三个表示文件快照内容的 blob 对象;一个记录着目录树内容及其中各个文件对应 blob 对象索引的 tree 对象;以及一个包含指向 tree 对象的索引和其他提交信息元数据的 commit 对象。作些修改后再次提交,提交对象会包含一个指向上次提交对象的指针,如下右图所示。
git 中的分支,其实本质上仅仅是个指向 commit 对象的可变指针。git 会使用 master 作为分支的默认名字。在若干次提交后,你其实已经有了一个指向最后一次 提交对象的 master 分支,它在每次提交的时候都会自动向前移动。
git branch [branch-name] # 建立新的分支
git checkout [branch-name] # 切换分支
git branch -b [branch-name] # 新建并切换到新的分支
git checkout -b [branch-name] master # 从master分支创建新分支,并切换到新分支
|
(2)分支的合并
git merge master # 把master分支合并到当前分支
git merge [branch-name] --squash # 合并[branch-name]分支到当前分支,但将分支上的提交压缩,然后手工提交变成一次提交。
git merge origin/master # 合并远程的master分支到当前分支
|
在合并时,如果你修改了两个待合并分支里同一个文件的同一部 分,git 就无法干净地把两者合到一起,出现冲突(conflit)。任何包含未解决冲突的文件都会以未合并(unmerged)状态列出。git 会在有冲突的文件里加入标准的冲突解决标记,可以通过它们来手工定位并解决这些冲突。
git branch # 查看所有的分支,带*号的是当前所在分支
git branch -v # 查看各个分支最后一次commit信息
git branch --merged # 查看与当前分支合并过的分支,只要合并过的分支即使删掉也不用担心
git branch --no-merged # 查看与当前分支没有合并过的分支
git branch -d [branch-name] # 删除分支
git branch -D [branch-name] # 强制删除分支
|
许多使用 git的开发者都喜欢以如下方式来开展工作,比如仅在 master 分支中保留完全稳定的代码,即已经发布或即将发布的代码。与此同时,他们还有一个名为 develop 或 next 的平行分支,专门用于后续的开发,或仅用于稳定性测试 —— 当然并不是说一定要绝对稳定,不过一旦进入某种稳定状态,便可以把它合并到master 里。这样,在确保这些已完成的特性分支(短期分支)能够通过所有测试,并且不会引入更多错误之后,就可以并到主干分支中,等待下一次的发布。稳定分支的指针总是在提交历 史中落后一大截,而前沿分支总是比较靠前。
(3)远程分支
如果我们克隆一个项目(git clone ssh://git@git.sankuai.com/web/www.git),git服务器 会自动将此远程仓库命名为 origin,并下载其中所有的数据,建立一个指向它的 master 分支的指针,在本地命名为 origin/master,但我们无法在本地更改其数据。接着,git 建立一个属于自己的本地 master 分支,始于 origin上 master分支相同的位置,我们可以就此开始工作。
可以运行 git fetch origin 来进行同步,该命令首先找到 origin 是哪个服务器,从上面获取本地尚未拥有的数据,更新本地的数据库,然后把 origin/master 的指针移到它最新的位置 。要想和其他人分享某个分支,你需要把它推送到一个你拥有写权限的远程仓库 ,可以运行 git push (远程仓库名) (分支名) 。
git push origin [branch-name] # 将本地分支[branch-name]保存到远程
git push origin :[branch-name] # 将远程分支[branch-name]删除
git branch -a #查看目录下的所有分支
|
3.4 rebase与merge
把一个分支整合到另一个分支的办法有两种:merge(合并) 和 rebase(衍合)。
(1)rebase与merge的区别
最容易的整合分支的方法是 merge 命令,如下图所示,它会把两个分支最新的快照(C4和 C6)以及二者最新的共同祖先(C2)进行三方合并。同时,也可以把在 C36里产生的变化补丁重新在 C4 的基础上打一 遍。在 Git 里,这种操作叫做衍合。有了 rebase 命令,就可以把在一个分支 里提交的改变在另一个分支里重放一遍。它的工作原理是回到两个分支(你所在的分支和你想要衍合进去的分支)的共同祖先,提取你所在分支每次提交时产生的差异(diff),把这些差异分别保存到临时文件里,然后从当前分支转换到你需要衍合入的分支,依序施用每一个差异补丁文件。
当合并时发生conflict的时候,merge只需要解决一次冲突即可,而rebase则可能需要多次解决。在解决完冲突后,用"git-add"命令去更新这些内容的索引(index), 使用rebase你无需执行 git-commit,只要执行:
(2)rebase使用时可能存在的问题
(3)使用时rebase与merge的选择
如果你修改比较多,预期会有较多的 conflict,建议用 merge。如果修改范围较小,不太预期有 conflict,則建议可以加上 rebase 参数。
“同分支总是 git pull --rebase origin xxx, 合并分支总是 git merge xxx 禁止 rebase”
4. git 工具
4.1 版本回滚
HEAD指向的版本就是当前版本,因此,Git允许我们在版本的历史之间穿梭,使用命令git reset --hard commit_id。
穿梭前,用git log可以查看提交历史,以便确定要回退到哪个版本。
要重返未来,用git reflog查看命令历史,以便确定要回到未来的哪个版本。
4.2 撤销修改
场景1:当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令git checkout -- file。
场景2:当你不但改乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步,第一步用命令git reset HEAD file,就回到了场景1,第二步按场景1操作。
场景3:已经提交了不合适的修改到版本库时,想要撤销本次提交,使用版本回滚,不过前提是没有推送到远程库。
4.3 git reset –soft与git reset –hard区别
git reset –soft之后,git diff返回空,而git diff –cached和git diff HEAD会返回有效信息。这说明使用–soft选项后,只回退了commit的信息,而不会回复到index file一级。你如果想撤销commit,并且只回退commit的信息,那么就用–soft吧!git reset –hard会完全撤销一个commit,彻底的回复到上一次commit的状态。连working tree的源代码也会完全倒退到上次commit之时的状态。所以使用–hard后,git diff,git diff –cached和git diff HEAD都会返回空。
有了这个–hard好工具,你可以这样做:在当前的current working tree中修改了代码,你可以选择git add或者不add,然后使用git reset –hard HEAD命令就可以恢复到修改之前的最初状态了。你修改的代码和git add的信息都会被丢弃。这个用法记住它,早晚你会用到它。但往往你会武断的认为git reset只能恢复到之前的commit状态,但你往往想不到git reset还可以恢复到当前的HEAD所指定的commit状态。
5. gitWorkflow
工作流程如下图所示:
假如已经有一份Git代码仓库,代码仓库里面默认会有一个master分支。
git checkout master # 切换到master分支
git checkout -b develop # 从master创建develop分支,并切换到该分支
|
git commit -m -a "fix bug 1" # 修复了bug1
git commit -m -a "add file 2" # 新增文件2
git checkout -b mirror # 从develop创建mirror分支,并切换到该分支
|
git checkout master
git pull # 更新master到最新
git checkout mirror
git merge master # 合并master到mirror
|
git checkout master
git merge mirror --squash # 压缩合并mirror分支上的代码更新
git commit -a -m 'xxxx' #注意需要进行一次提交
git push # 推送到远程
|
主要参考: