Git 简介
Git是免费、开源的分布式版本控制系统,可以有效、高速地处理从很小到非常大的项目版本管理。
SVN与Git的最主要的区别?
SVN是集中式版本控制系统,版本库是集中放在中央服务器的,而干活的时候,用的都是自己的电脑,所以首先要从中央服务器哪里得到最新的版本,然后干活,干完后,需要把自己做完的活推送到中央服务器。集中式版本控制系统是必须联网才能工作,如果在局域网还可以,带宽够大,速度够快,如果在互联网下,如果网速慢的话,就纳闷了。
Git是分布式版本控制系统,那么它就没有中央服务器的,每个人的电脑就是一个完整的版本库,这样,工作的时候就不需要联网了,因为版本都是在自己的电脑上。既然每个人的电脑都有一个完整的版本库,那多个人如何协作呢?比如说自己在电脑上改了文件A,其他人也在电脑上改了文件A,这时,你们两之间只需把各自的修改推送给对方,就可以互相看到对方的修改了。
安装 Git
在Linux上安装Git
首先,你可以试着输入git
,看看系统有没有安装Git:
$ git
The program 'git' is currently not installed. You can install it by typing:
sudo apt-get install git
像上面的命令,有很多Linux会友好地告诉你Git没有安装,还会告诉你如何安装Git。
如果你碰巧用Debian或Ubuntu Linux,通过一条sudo apt-get install git
就可以直接完成Git的安装,非常简单。
老一点的Debian或Ubuntu Linux,要把命令改为sudo apt-get install git-core
,因为以前有个软件也叫GIT(GNU Interactive Tools),结果Git就只能叫git-core
了。由于Git名气实在太大,后来就把GNU Interactive Tools改成gnuit
,git-core
正式改为git
。
如果是其他Linux版本,可以直接通过源码安装。先从Git官网下载源码,然后解压,依次输入:./config
,make
,sudo make install
这几个命令安装就好了。
在Mac OS X上安装Git
如果你正在使用Mac做开发,有两种安装Git的方法。
一是安装homebrew,然后通过homebrew安装Git,具体方法请参考homebrew的文档:http://brew.sh/。
第二种方法更简单,也是推荐的方法,就是直接从AppStore安装Xcode,Xcode集成了Git,不过默认没有安装,你需要运行Xcode,选择菜单“Xcode”->“Preferences”,在弹出窗口中找到“Downloads”,选择“Command Line Tools”,点“Install”就可以完成安装了。
Xcode是Apple官方IDE,功能非常强大,是开发Mac和iOS App的必选装备,而且是免费的!
在Windows上安装Git
在Windows上使用Git,可以从Git官网直接下载安装程序,然后按默认选项安装即可。
安装完成后,在开始菜单里找到“Git”->“Git Bash”,蹦出一个类似命令行窗口的东西,就说明Git安装成功!
安装完成后,还需要最后一步设置,在命令行输入:
$ git config --global user.name "Your Name"
$ git config --global user.email "email@example.com"
因为Git是分布式版本控制系统,所以,每个机器都必须自报家门:你的名字和Email地址。你也许会担心,如果有人故意冒充别人怎么办?这个不必担心,首先我们相信大家都是善良无知的群众,其次,真的有冒充的也是有办法可查的。
注意git config
命令的--global
参数,用了这个参数,表示你这台机器上所有的Git仓库都会使用这个配置,当然也可以对某个仓库指定不同的用户名和Email地址。
Git的相关理论基础
- Git的四大工作区域
- Git的工作流程
- Git文件的四种状态
- 一张图解释Git的工作原理
Git的四大工作区域
先复习Git的几个工作区域哈:
- Workspace:你电脑本地看到的文件和目录,在Git的版本控制下,构成了工作区。
- Index/Stage:暂存区,一般存放在 .git目录下,即.git/index,它又叫待提交更新区,用于临时存放你未提交的改动。比如,你执行git add,这些改动就添加到这个区域啦。
- Repository:本地仓库,你执行git clone 地址,就是把远程仓库克隆到本地仓库。它是一个存放在本地的版本库,其中HEAD指向最新放入仓库的版本。当你执行git commit,文件改动就到本地仓库来了~
- Remote:远程仓库,就是类似github,码云等网站所提供的仓库,可以理解为远程数据交换的仓库~
Git的工作流程
上一小节介绍完Git的四大工作区域,这一小节呢,介绍Git的工作流程咯,把git的操作命令和几个工作区域结合起来,个人觉得更容易理解一些吧,哈哈,看图:
git 的正向工作流程一般就这样:
- 从远程仓库拉取文件代码回来;
- 在工作目录,增删改查文件;
- 把改动的文件放入暂存区;
- 将暂存区的文件提交本地仓库;
- 将本地仓库的文件推送到远程仓库;
Git文件的四种状态
根据一个文件是否已加入版本控制,可以把文件状态分为:Tracked(已跟踪)和Untracked(未跟踪),而tracked(已跟踪)又包括三种工作状态:Unmodified,Modified,Staged
- Untracked: 文件还没有加入到git库,还没参与版本控制,即未跟踪状态。这时候的文件,通过git add 状态,可以变为Staged状态
- Unmodified:文件已经加入git库, 但是呢,还没修改, 就是说版本库中的文件快照内容与文件夹中还完全一致。 Unmodified的文件如果被修改, 就会变为Modified. 如果使用git remove移出版本库, 则成为Untracked文件。
- Modified:文件被修改了,就进入modified状态啦,文件这个状态通过stage命令可以进入staged状态
- staged:暂存状态. 执行git commit则将修改同步到库中, 这时库中的文件和本地文件又变为一致, 文件为Unmodified状态.
一张图解释Git的工作原理
日常开发中,Git的基本常用命令
- git clone
- git checkout -b dev
- git add
- git commit
- git log
- git diff
- git status
- git pull/git fetch
- git push
这个图只是模拟一下git基本命令使用的大概流程哈~
git remote -v
命令查看当前远端仓库的地址
git clone
当我们要进行开发,第一步就是克隆远程版本库到本地呢
git clone url 克隆远程版本库 [指定目录]
git checkout -b dev
克隆完之后呢,开发新需求的话,我们需要新建一个开发分支,比如新建开发分支dev
创建分支:
git checkout -b dev 创建开发分支dev,并切换到该分支下
git add
添加内容到索引(暂存区)
git add . 添加当前目录的所有文件到暂存区
git add [dir] 添加指定目录到暂存区,包括子目录
git add [file1] 添加指定文件到暂存区
git add . # 添加所有 新增、修改 的文件到暂存区
git add -A # all 添加所有 新增、修改、删除 的文件到暂存区
参数 | 新增 | 修改 | 删除 |
---|---|---|---|
. | ✅ | ✅ | |
-A / -all / -a | ✅ | ✅ | ✅ |
-u / --update | ✅ | ✅ |
**
有了开发分支dev之后,我们就可以开始开发啦,假设我们开发完HelloWorld.java,可以把它加到暂存区,命令如下
git add Hello.java 把HelloWorld.java文件添加到暂存区去
git commit
git commit -m [message] 提交暂存区到仓库区,message为说明信息
git commit [file1] -m [message] 提交暂存区的指定文件到本地仓库
git commit --amend -m [message] 使用一次新的commit,替代上一次提交
把HelloWorld.java文件加到暂存区后,我们接着可以提交到本地仓库啦~
git commit -m 'helloworld开发'
git status
git status,表示查看工作区状态,使用命令格式:
git status 查看当前工作区暂存区变动
git status -s 查看当前工作区暂存区变动,概要信息
git status --show-stash 查询工作区中是否有stash(暂存的文件)
当你忘记是否已把代码文件添加到暂存区或者是否提交到本地仓库,都可以用git status看看哦~
git log
git log,这个命令用得应该比较多,表示查看提交历史/提交日志~
git log 查看提交历史
git log --oneline 以精简模式显示查看提交历史
git log -p <file> 查看指定文件的提交历史
git blame <file> 一列表方式查看指定文件的提交历史
git log --graph --pretty=oneline --abbrev-commit --graph 图形化 --pretty指定展示样式 =oneline 将每个提交 放在一行显示 abbrev-commit 仅显示 SHA-1 的前几个字符,而非所有的 40 个字符
git log --pretty-format
--pretty=format
定制显示的格式,-2
表示仅显示最近的两次更新
$ git log --pretty=format:"%h - %an, %ar : %s" -2
20d1045 - dongchuanxi, 35 minutes ago : 清分基类更新
b4e8dbf - miaocansky, 37 minutes ago : 异常
常用的格式占位符写法及其代表的意义
选项 说明
%H 提交对象(commit)的完整哈希字串
%h 提交对象的简短哈希字串
%T 树对象(tree)的完整哈希字串
%t 树对象的简短哈希字串
%P 父对象(parent)的完整哈希字串
%p 父对象的简短哈希字串
%an 作者(author)的名字
%ae 作者的电子邮件地址
%ad 作者修订日期(可以用 -date= 选项定制格式)
%ar 作者修订日期,按多久以前的方式显示
%cn 提交者(committer)的名字
%ce 提交者的电子邮件地址
%cd 提交日期
%cr 提交日期,按多久以前的方式显示
%s 提交说明
用 oneline 或 format 时结合 --graph
选项,可以看到开头多出一些 ASCII 字符串表示的简单图形,形象地展示了每个提交所在的分支及其分化衍合情况。
$ git log --pretty=format:"%h %an %s" --graph
* ad32f1c dongchuanxi Merge branch 'dev'
|
| * 20d1045 dongchuanxi 清分基类更新
| * b4e8dbf miaocansky 异常
| * d996254 miaocansky skt调整精度问题
| * 0b3b71d miaocansky sk脚本市经调
git diff
git diff 显示暂存区和工作区的差异
git diff filepath filepath路径文件中,工作区与暂存区的比较差异
git diff HEAD filepath 工作区与HEAD ( 当前工作分支)的比较差异
git diff branchName filepath 当前分支的文件与branchName分支的文件的比较差异
git diff commitId filepath 与某一次提交的比较差异
如果你想对比一下你改了哪些内容,可以用git diff对比一下文件修改差异哦
git pull/git fetch
git pull 拉取远程仓库所有分支更新并合并到本地分支。
git pull origin master 将远程master分支合并到当前本地master分支
git pull origin master:master 将远程master分支合并到当前本地master分支,冒号后面表示本地分支
git fetch --all 拉取所有远端的最新代码
git fetch origin master 拉取远程最新master分支代码
git log -p master.. origin/master 比较本地的仓库和远程仓库的区别
git merge origin/master 把远程下载下来的代码合并到本地仓库,远程的和本地的合并
git fetch origin master:dev 从远程的 origin 仓库的master 分支下载到本地并新建一个分支 dev
git diff dev 比较 master 分支和 dev 分支的不同
git merge dev 合并 dev 分支到 master 分支
git branch -d dev 删除本地分支 dev
我们一般都会用git pull拉取最新代码看看的,解决一下冲突,再推送代码到远程仓库的。
有些伙伴可能对使用git pull还是git fetch有点疑惑,其实 git pull = git fetch+ git merge。pull的话,拉取远程分支并与本地分支合并,fetch只是拉远程分支,怎么合并,可以自己再做选择。
git push
git push 可以推送本地分支、标签到远程仓库,也可以删除远程分支哦。
git push origin master 将本地分支的更新全部推送到远程仓库master分支。
git push origin -d <branchname> 删除远程branchname分支
git push --tags 推送所有标签
如果我们在dev开发完,或者就想把文件推送到远程仓库,给别的伙伴看看,就可以使用git push origin dev~
Git进阶之分支处理
Git一般都是存在多个分支的,开发分支,回归测试分支以及主干分支等,所以Git分支处理的命令也需要很熟悉的呀~
- git branch
- git checkout
- git merge
git branch
git branch用处多多呢,比如新建分支、查看分支、删除分支等等
新建分支:
git checkout -b dev2 新建一个分支,并且切换到新的分支dev2
git branch dev2 新建一个分支,但是仍停留在原来分支
查看分支:
git branch 查看本地所有的分支
git branch -r 查看所有远程的分支
git branch -a 查看所有远程分支和本地分支
删除分支:
git branch -D <branchname> 删除本地branchname分支
git push origin --delete <branchname> 删除远程 branchname 分支
git checkout
切换分支:
git checkout master 切换到master分支
git merge
我们在开发分支dev开发、测试完成在发布之前,我们一般需要把开发分支dev代码合并到master,所以git merge也是程序员必备的一个命令。
git merge master 在当前分支上合并master分支过来
git merge --no-ff origin/dev 在当前分支上合并远程分支dev
git merge --abort 终止本次merge,并回到merge前的状态
比如,你开发完需求后,发版全需要把代码合到主干master分支,如下:
Git进阶之修复bug到多个分支
git branch master #切换到 master 分支
git checkout -b bug0806001 #创建并切换到 bug 分支
vim a.txt #修复bug
git add -u
git commit -m "fix bug" #提交 bug 得到一个 hash 值 3d003e86
git checkout master #切换回 master
git merge bug0608001 #将 bug 分支内容合并到 master 分支
git push origin master #提交到远程 master 分支
开发中经常会遇到一个主干分支A,分出了B,C,D,E等多个分支,然后每个分支经过多个版本迭代之后,已经各自有了不少差异An,Bn,Cn,Dn,En。
这时如果有一个bug是多个分支都存在的,如何将共同存在的bug修正后,更新到所有分支呢,这个时候可以用到git的cherry-pick命令。
这个命令 可以将指定的提交(commit)应用于其他分支。
将 bug 修复应用于 dev 分支
git checkout dev #切换到 dev 分支
git cherry-pick 3d003e86 #将 3d003e86 提交应用于 当前分支
git push origin dev #提交到远程 dev 分支
最后删除 bug 分支
git branch -D bug0608001 #删除本地 bug 分支
如果要指定多个提交 应用于其他分支
Cherry pick 支持一次转移多个提交。
$ git cherry-pick <HashA> <HashB>
上面的命令将 A 和 B 两个提交应用到当前分支。这会在当前分支生成两个对应的新提交。
如果想要转移一系列的连续提交,可以使用下面的简便语法。
$ git cherry-pick A..B
上面的命令可以转移从 A 到 B 的所有提交。它们必须按照正确的顺序放置:提交 A 必须早于提交 B,否则命令将失败,但不会报错。
注意,使用上面的命令,提交 A 将不会包含在 Cherry pick 中。如果要包含提交 A,可以使用下面的语法。
$ git cherry-pick A^..B
Git进阶之处理冲突
Git版本控制,还是多个人一起搞的,多个分支并存的,这就难免会有冲突出现~
Git合并分支,冲突出现
同一个文件,在合并分支的时候,如果同一行被多个分支或者不同人都修改了,合并的时候就会出现冲突。
举个粟子吧,我们现在在dev分支,修改HelloWorld.java文件,假设修改了第三行,并且commit提交到本地仓库,修改内容如下:
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello,捡田螺的小男孩!");
}
}
我们切回到master分支,也修改HelloWorld.java同一位置内容,如下:
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello,jay!!");
}
}
再然后呢,我们提交一下master分支的这个改动,并把dev分支合并过下,就出现冲突啦,如图所示:
Git解决冲突
Git 解决冲突步骤如下:
- 查看冲突文件内容
- 确定冲突内容保留哪些部分,修改文件
- 重新提交,done
1.查看冲突文件内容
git merge提示冲突后,我们切换到对应文件,看看冲突内容哈,,如下:
2.确定冲突内容保留哪些部分,修改文件
- Git用<<<<<<<,=======,>>>>>>>标记出不同分支的内容,
- <<<<<<>>>>>> dev是指dev分支上修改的内容
所以呢,我们确定到底保留哪个分支内容,还是两个分支内容都保留呢,然后再去修改文件冲突内容~
3.修改完冲突文件内容,重新提交,冲突done
Git进阶之撤销与回退
Git的撤销与回退,在日常工作中使用的比较频繁。比如我们想将某个修改后的文件撤销到上一个版本,或者想撤销某次多余的提交,都要用到git的撤销和回退操作。
代码在Git的每个工作区域都是用哪些命令撤销或者回退的呢,如下图所示:
有关于Git的撤销与回退,一般就以下几个核心命令
- git checkout
- git reset
- git revert
git checkout
如果文件还在工作区,还没添加到暂存区,可以使用git checkout撤销
git checkout [file] 丢弃某个文件file
git checkout . 丢弃所有文件
以下demo,使用git checkout -- test.txt 撤销了暂存区test.txt的修改
git reset
git reset的理解
git reset的作用是修改HEAD的位置,即将HEAD指向的位置改变为之前存在的某个版本.
为了更好地理解git reset,我们来回顾一下,Git的版本管理及HEAD的理解
Git的所有提交,会连成一条时间轴线,这就是分支。如果当前分支是master,HEAD指针一般指向当前分支,如下:
假设执行git reset,回退到版本二之后,版本三不见了哦,如下:
git reset的使用
Git Reset的几种使用模式
git reset HEAD --file
回退暂存区里的某个文件,回退到当前版本工作区状态
git reset –-soft 目标版本号 可以把版本库上的提交回退到暂存区,修改记录保留
git reset –-mixed 目标版本号 可以把版本库上的提交回退到工作区,修改记录保留
git reset –-hard 可以把版本库上的提交彻底回退,修改的记录全部revert。
先看一个粟子demo吧,代码git add到暂存区,并未commit提交,就以下酱紫回退,如下:
git reset HEAD file 取消暂存
git checkout file 撤销修改
再看另外一个粟子吧,代码已经git commit了,但是还没有push:
git log 获取到想要回退的commit_id
git reset --hard commit_id 想回到过去,回到过去的commit_id
如果代码已经push到远程仓库了呢,也可以使用reset回滚哦(这里大家可以自己操作实践一下哦)~
git log
git reset --hard commit_id
git push origin HEAD --force
git revert
与git reset不同的是,revert复制了那个想要回退到的历史版本,将它加在当前分支的最前端。
revert之前:
revert 之后:
当然,如果代码已经推送到远程的话,还可以考虑revert回滚呢
git log 得到你需要回退一次提交的commit id
git revert -n <commit_id> 撤销指定的版本,撤销也会作为一次提交进行保存
Git进阶之标签tag
打tag就是对发布的版本标注一个版本号,如果版本发布有问题,就把该版本拉取出来,修复bug,再合回去。
git tag 列出所有tag
git tag [tag] 新建一个tag在当前commit
git tag [tag] [commit] 新建一个tag在指定commit
git tag -d [tag] 删除本地tag
git push origin [tag] 推送tag到远程
git show [tag] 查看tag
git checkout -b [branch] [tag] 新建一个分支,指向某个tag
Git进阶之移除版本控制
git rm
: 同时从工作区和索引中删除文件。即本地的文件也被删除了。
git rm --cached
: 从索引中删除文件。但是本地文件还存在, 只是不希望这个文件被版本控制。
删除文件并移除版本控制
git rm text1.txt
git commit -m "删除text1文件"
git push origin master
删除文件夹并移除版本控制
git rm -r mydir
git commit -m "删除文件夹"
git push origin master
仅文件移除版本控制
git rm --cached text1.txt
git commit -m "将text1文件移除版本控制"
git push origin master
仅文件夹移除版本控制
git rm -r mydir --cached
git commit -m "将mydir文件夹移除版本控制"
git push origin master
Git忽略未被跟踪的文件
git的文件操作本质上来讲是基于文件索引来做追踪的。
至于忽略未跟踪(untrack)文件文件,git提供了三种方式
方式一:编辑 .gitignore 注意:该文件会提交到版本库中
方式二: git config --global core.excludesfile '~/.gitignore' 设置本地全局忽略文件到当前用户根目录
方式三: 编辑 .git/info/exclude 这里设置的 则是你自己本地需要排除的文件。 他不会影响到其他人。也不会提交到版本库中去。
Git 忽略已被跟踪的文件
忽略已跟踪(tracked)文件,使得当本地某系文件(尤其是配置文件发生改变时,不需要体现在git变化列表中)有以下两种方式:
方式一:git update-index --assume-unchanged /path/file 与git update-index --no-assume-unchanged /path/file
方式二:git update-index --skip-worktree /path/file 与git update-index --no-skip-worktree /path/file
此时再对/path/file进行更改,你会发现不会体现在git status列表中
解释:
--assume-unchanged
假定开发人员不会更改文件。此标记旨在为无变化文件夹(如 SDK)改善性能。
--skip-worktree
用于命 GIT 不再染指特定文件——即便开发人员可能更改它——的情形。例如,如果主源码库上游承载某些即将投入生产的配置文件而你不希望意外的提交影响到那些文件,--skip-worktree 正是你的菜。
git update-index --assume-unchanged –path
可以忽略文件
git update-index --no-assume-unchanged –path
可以取消忽略文件
但是忽略的文件多了,想找出所有被忽略的文件,暂时找到下面的办法,
#找出所有被忽略的文件
git ls-files -v | grep '^h '
#提取文件路径
git ls-files -v | grep '^h ' | awk '{print $2}'
#所有被忽略的文件,取消忽略
git ls-files -v | grep '^h' | awk '{print $2}' |xargs git update-index --no-assume-unchanged
Git其他一些经典命令
git rebase
rebase又称为衍合,是合并的另外一种选择。
假设有两个分支master和test
D---E test
/
A---B---C---F--- master
执行 git merge test得到的结果
D--------E
/
A---B---C---F----G--- test, master
执行git rebase test,得到的结果
A---B---D---E---C‘---F‘--- test, master
rebase好处是:获得更优雅的提交树,可以线性的看到每一次提交,并且没有增加提交节点。所以很多时候,看到有些伙伴都是这个命令拉代码:git pull --rebase
git stash
stash命令可用于临时保存和恢复修改
git stash 把当前的工作隐藏起来 等以后恢复现场后继续工作
git stash list 显示保存的工作进度列表
git stash pop stash@{num} 恢复工作进度到工作区
git stash show :显示做了哪些改动
git stash drop stash@{num} :删除一条保存的工作进度
git stash clear 删除所有缓存的stash。
git reflog
显示当前分支的最近几次提交
git blame filepath
git 记录了某个文件的更改历史和更改人,可以查看背锅人,哈哈
git remote
git remote 查看关联的远程仓库的名称
git remote add url 添加一个远程仓库
git remote show [remote] 显示某个远程仓库的信息