简介
Git是一款免费、开源的分布式版本控制系统,方便相互协作开发。
安装
我主要的环境是windows,所以在介绍在Windows上安装Git,我们只需要百度Git for Windows,然后点击下载,安装后,桌面会有Git BASH和Git GUI。
Git BASH
Git for Windows提供了用于从命令行运行Git的BASH仿真。* NIX用户应该感觉到在家里,
因为BASH仿真的行为就像LINUX和UNIX环境中的“git”命令。
Git GUI
由于Windows用户通常期望图形用户界面,Git for Windows还提供了Git GUI,
它是Git BASH的强大替代品,
提供了几乎所有Git命令行功能的图形化版本,以及全面的视觉差异工具
配置Git用户信息
$ git config --global user.name "Your Name"
$ git config --global user.email "email@example.com"
因为Git是分布式版本控制系统,所以,每个机器都必须自报家门:你的名字和Email地址。要检查已有的配置信息,可以使用 git config --list 命令
这两条配置很重要,每次 Git 提交时都会引用这两条信息,说明是谁提交了更新,所以会随更新内容一起被永久纳入历史记录,如果用了 --global 选项,那么更改的配置文件就是位于你用户主目录下的那个,以后你所有的项目都会默认使用这里配置的用户信息。如果要在某个特定的项目中使用其他名字或者电邮,只要去掉--global 选项重新配置即可,新的设定保存在当前项目的.git/config 文件里。
创建版本库
版本库又名仓库,英文名repository,你可以简单理解成一个目录,这个目录里面的所有文件都可以被Git管理起来,每个文件的修改、删除,Git都能跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻可以“还原”
在电脑的任何地方,创建一个空的文件夹,然后cd到文件夹,pwd显示当前目录
*(备注:使用Windows系统,为了避免遇到各种莫名其妙的问题,请确保目录名(包括父目录)不包含中文。)*,
通过git init命令把这个目录变成Git可以管理的仓库
$ git init
Initialized empty Git repository in 你的pwd
瞬间Git就把仓库建好了,而且告诉你是一个空的仓库(empty Git repository),可以发现当前目录下多了一个.git的目录,这个目录是Git来跟踪管理版本库的,没事千万不要手动修改这个目录里面的文件,不然改乱了,就把Git仓库给破坏了。
如果你没有看到.git目录,那是因为这个目录默认是隐藏的,用ls -ah命令就可以看见
仓库的提交
根据上面的图片,下面给出了每个部分的简要说明:
Directory:使用Git管理的一个目录,也就是一个仓库,包含我们的工作空间和Git的管理空间。
WorkSpace:需要通过Git进行版本控制的目录和文件,这些目录和文件组成了工作空间。
.git:存放Git管理信息的目录,初始化仓库的时候自动创建。
Index/Stage:暂存区,或者叫待提交更新区,在提交进入repo之前,我们可以把所有的更新放在暂存区。
Local Repo:本地仓库,一个存放在本地的版本库;HEAD会只是当前的开发分支(branch)。
Stash:是一个工作状态保存栈,用于保存/恢复WorkSpace中的临时状态。
First:我们需要先打要提交的文件,放入工作空间--把文件放入建立仓库的文件夹
Second: 用命令git add告诉Git,把文件添加到仓库
Last:用命令git commit告诉Git,把文件提交到仓库
备注:解释一下git commit命令,-m后面输入的是本次提交的说明,可以输入任意内容,
当然最好是有意义的,这样你就能从历史记录里方便地找到改动记录。
当然文件的编码最好选择utf-8
查看仓库的更新
- 通过git status可以查看WorkSpace的状态
- 通过git diff用于显示WorkSpace中的文件和暂存区文件的差异
- 通过git log命令显示从最近到最远的提交日志(备注:如果嫌输出信息太多,看得眼花缭乱的,可以试试加上--pretty=oneline,这样就只有commit id(版本号)和改动内容
- 通过git reflog命令会显示,记录这个仓库中所有的分支的所有更新记录,包括已经撤销的更新
对仓库进行撤回
根据前面对基本概念的了解,更新可能存在三个地方,WorkSpace中,Stage中和repo中。下面就分别介绍一下怎么撤销这些更新
使用"git checkout --<file>..."来撤销WorkSpace中的更新
(备注:使用这种方法撤销更新的时候一定要慎重,因为通过这种方式撤销后,
更新将没有办法再找回)
通过"git reset HEAD <file>..."把暂存区的更新移出到WorkSpace中
使用命令git reset --hard commit_id或者git reset --hard HEAD^,用来指向repo中想要的版本。
(备注:使用HEAD指针或者使用commit id
HEAD指针指向当前分支中的版本。上一个版本就是HEAD^,
上上一个版本就是HEAD^^如果想回退到更早的提交,可以使用"HEAD~n"。
(也就是,HEAD^=HEAD~1,HEAD^^=HEAD~2))
commit id(版本号)是一个SHA1计算出来的一个非常大的数字,用十六进制表示,
版本号没必要写全,前几位就可以了,Git会自动去找。
当然也不能只写前一两位,因为Git可能会找到多个版本号,就无法确定是哪一个了
--hard和--soft
前面在使用reset来撤销更新的时候,我们都是使用的"--head"选项,其实与之对应的还有一个"--soft"选项,区别如下:
--head:撤销并删除相应的更新
--soft:撤销相应的更新,把这些更新的内容放到Stage中
仓库删除文件
- 对WorkSpace中的文件进行删除了,Git知道你删除了文件,因此,工作区和版本库就不一致了,git status命令会立刻告诉你哪些文件被删除了
- 现在你有两个选择:
一是确实要从版本库中删除该文件,那就用命令git rm删掉,并且git commit -m
另一种情况是删错了,因为版本库里还有呢,所以可以很轻松地把误删的文件恢复到最新版本 git checkout -- <file>
远程仓库
- GitHub账号。由于你的本地Git仓库和GitHub仓库之间的传输是通过SSH加密,在Github中绑定id_rsa.pub公钥。
创建SSH Key。在用户主目录下,看看有没有.ssh目录,
如果有,再看看这个目录下有没有id_rsa和id_rsa.pub这两个文件,
如果已经有了,可直接到 Github里面设置。如果没有,打开GitBash,创建SSH Key
$ ssh-keygen -t rsa -C "youremail@example.com"
- 把自己本地的仓库和Github的开源仓库进行关联
git remote add origin git@github.com:user_name/Github仓库的名字.git - 把本地库的内容推送到远程,用git push命令
$ git push -u origin master (备注:由于远程库是空的,我们第一次推送master分支时,加上了-u参数,Git不但会把本地的master分支内容推送的远程新的master分支,还会把本地的master分支和远程的master分支关联起来,在以后的推送或者拉取时就可以简化命令) - 每次本地提交后,只要有必要,就可以使用命令git push origin master推送最新修改
从远程仓库克隆
1.勾选Initialize this repository with a README,这样GitHub会自动为我们创建一个README.md文件(.md文件是markdown格式的写法)
用命令git clone克隆一个本地库(path取决于pwd)
Cloning into 'mygit'...
remote: Counting objects: 3, done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Receiving objects: 100% (3/3), done.
$ cd gitskills
$ ls
README.md
注意把Git库的地址换成你自己的,然后进入mygit目录看看,已经有README.md文件了
Github的分支管理
Git鼓励大量使用分支:
查看分支:git branch
创建分支:git branch <name>
切换分支:git checkout <name>
创建+切换分支:git checkout -b <name>
合并某分支到当前分支:git merge <name>
删除分支:git branch -d <name>
强制删除:git branch -D <name>
有时候分支合并会有冲突git status也可以查看冲突的文件,
我们也可以自己cat查看文件内容
Git用<<<<<<<,=======,>>>>>>>标记出不同分支的内容
我们找出不同点,进行修改,然后add和commit
用git log --graph命令可以看到分支合并图
通常,合并分支时,如果可能,Git会用Fast forward模式,
但这种模式下,删除分支后,会丢掉分支信息
强制禁用Fast forward模式,Git就会在merge时生成一个新的commit,
这样,从分支历史上就可以看出分支信息
git merge --no-ff -m "merge with no-ff" dev
备注:因为本次合并要创建一个新的commit,所以加上-m参数,把commit描述写进去。
bug分支
$ git checkout -b issue-101
$ git merge --no-ff -m "merged bug fix 101" issue-101
(备注:修复一个代号101的bug的任务时,很自然地,你想创建一个分支issue-101来修复它,最后进行合并)
Git提供了一个stash功能,可以把当前工作现场“储藏”起来
用git stash list命令查看stash内容
一是用git stash apply恢复,但是恢复后,stash内容并不删除,
你需要用git stash drop来删除;
另一种方式是用git stash pop,恢复的同时把stash内容也删了
多人协作
查看远程库的信息用git remote或者用git remote -v显示更详细的信息
推送分支git push origin master/git push origin dev
远程库clone时,默认情况下,你的小伙伴只能看到本地的master分支
要在dev分支上开发,就必须创建远程origin的dev分支到本地
git checkout -b dev origin/dev
从本地推送分支,如果推送失败,先用git pull抓取远程的新提交;
在本地创建和远程分支对应的分支,使用git checkout -b branch-name origin/branch-name,
本地和远程分支的名称最好一致;
建立本地分支和远程分支的关联,
使用git branch --set-upstream branch-name origin/branch-name;
从远程抓取分支,使用git pull,如果有冲突,要先处理冲突
更新远程代码到本地仓库
理解 fetch 的关键, 是理解 FETCH_HEAD,FETCH_HEAD指的是: 某个branch在服务器上的最新状态’。这个列表保存在 .Git/FETCH_HEAD 文件中, 其中每一行对应于远程服务器的一个分支。
当前分支指向的FETCH_HEAD, 就是这个文件第一行对应的那个分支.
一般来说, 存在两种情况:
- 如果没有显式的指定远程分支, 则远程分支的master将作为默认的FETCH_HEAD.
- 如果指定了远程分支, 就将这个远程分支作为FETCH_HEAD.
git fetch origin branch1
这个操作是git pull origin branch1
的第一步, 而对应的pull
操作,并不会在本地创建新的branch
。设定当前分支的FETCH_HEAD
为远程服务器的branch1
分支。
这个命令可以用来测试远程主机的远程分支branch1是否存在, 如果存在, 返回0, 如果不存在, 返回128, 抛出一个异常.
git fetch origin branch1:branch2
首先执行上面的fetch
操作,使用远程branch1
分支在本地创建branch2
(但不会切换到该分支),如果本地不存在branch2
分支, 则会自动创建一个新的branch2
分支,
如果本地存在branch2
分支, 并且是`fast forward', 则自动合并两个分支, 否则, 会阻止以上操作.
fetch更新本地仓库两种方式:
//方法一
$ git fetch origin master //从远程的origin仓库的master分支下载代码到本地的origin master
$ git log -p master.. origin/master//比较本地的仓库和远程参考的区别
$ git merge origin/master//把远程下载下来的代码合并到本地仓库,远程的和本地的合并
//方法二
$ git fetch origin master:temp //从远程的origin仓库的master分支下载到本地并新建一个分支temp
$ git diff temp//比较master分支和temp分支的不同
$ git merge temp//合并temp分支到master分支
$ git branch -d temp//删除temp
1、git reset
没有push,这种情况发生在你的本地代码仓库,可能你add ,commit 以后发现代码有点问题.
首先,Git必须知道当前版本是哪个版本,在Git中,用HEAD
表示当前版本,也就是最新的提交commit_id(79f673d631b08907496ce792f429e1f00da25b73),上一个版本就是HEAD^
,上上一个版本就是HEAD^^
,当然往上100个版本写100个^
比较容易数不过来,所以写成HEAD~100
。
HEAD
指向的版本就是当前版本,因此,Git允许我们在版本的历史之间穿梭,使用命令git reset --hard 79f673d631b08907496ce792f429e1f00da25b73
。穿梭前,用
git log
可以查看提交历史,以便确定要回退到哪个版本。要重返未来,用
git reflog
查看命令历史,以便确定要回到未来的哪个版本。
2、git revert
已经push,对于已经把代码push到线上仓库,你回退本地代码其实也想同时回退线上代码,回滚到某个指定的版本,线上,线下代码保持一致.你要用到下面的命令
git revert用一个新提交来消除一个历史提交所做的任何修改.
revert 之后你的本地代码会回滚到指定的历史版本,这时你再 git push 既可以把线上的代码更新.(这里不会像reset造成冲突的问题)
revert 使用,需要先找到你想回滚版本唯一的commit标识代码,可以用 git log 或者在adgit搭建的web环境历史提交记录里查看.
<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">git revert c011eb3c20ba6fb38cc94fe5a8dda366a3990c61</pre>
3、两者区别
git revert是用一次新的commit来回滚之前的commit,git reset是直接删除指定的commit看似达到的效果是一样的,其实完全不同.
第一:上面我们说的如果你已经push到线上代码库, reset 删除指定commit以后,你git push可能导致一大堆冲突(或git push -f强制推送).但是revert 并不会.
第二:如果在日后现有分支和历史分支需要合并的时候,reset 恢复部分的代码依然会出现在历史分支里.但是revert 方向提交的commit 并不会出现在历史分支里.
第三:reset 是在正常的commit历史中,删除了指定的commit,这时 HEAD 是向后移动了,而 revert 是在正常的commit历史中再commit一次,只不过是反向提交,他的 HEAD 是一直向前的.
Git的标签
- 命令git tag <name>用于新建一个标签,默认为HEAD,也可以指定一个commit id;命令git tag <name> commit_id
- 创建带有说明的标签,用-a指定标签名,-m指定说明文字
git tag -a <name> -m "blablabla..." commit_id - 用git show <tagname>查看标签信息
- 推送某个标签到远程,使用命令 git push origin <tagname>
- 次性推送全部尚未推送到远程的本地标签 git push origin --tags
- 命令git tag -d <tagname>可以删除一个本地标签;
- 命令git push origin :refs/tags/<tagname>可以删除一个远程标签
总结
整体来说,git的功能都是很强大的,我自己写这样一篇git的操作,也是相当于,自己给自己做笔记,方便,自己以后找资料用,同时也可以给予初学者一定的帮助。