0、前言
- Workspace:工作区
- Index / Stage:暂存区
- Repository:仓库区(或本地仓库)
- Remote:远程仓库
工作区和暂存区:
我们写代码的地方就是工作区,代码写完后,我们可以把他提交到暂存区,提交到暂存区后,我们可以对自己的代码进行更改,修改文件内容,删除或者增加文件,只需一个git checkout xx即可让暂存区内容覆盖当前工作区的内容,或者说还原!
暂存区(暂时存用)和本地仓库:
我们可以把暂存区的内容提交到我们的本地仓库,此时会在仓库中生成一个快照,我们可以为这个快照打一个TAG信息,比如这个快照叫"完成了UI部分"这样,提交后暂存区中的内容就会被清空,此时我们可以调用reset指令,制定某个快照,然后还原到暂存区!
工作区和本地仓库:
我们可以直接走checkout某个版本的指令,直接让工作空间还原成某个本地仓库中的某个快照。
本地仓库和远程仓库:
我们可以将本地的某个快照push推送到远程仓库,push时可能还需处理一些冲突;也可以从拉取远程仓库到本地,也会有冲突出现。
工作区和远程仓库:
这两者的协作一般是pull,即同步远程仓库的代码到工作空间而已~
.git隐藏文件夹各种文件的含义
.gitignore的文件 commit的时候会忽略,push pull的时候都会忽略
git中文件的状态 Tracked(已跟踪) Untracked(未跟踪) Staged(暂存) Unmodified(未修改) Modified(修改)
Tracked,Untracked区分依据:该文件是否已经加入版本控制?当我们在项目中新增一个文件,这个文件此时就处于Untracked状态!
git add指令将该文件加入暂存区,那么此时该文件此时处于Tracked状态,又或者说这个文件已经被我们的版本控制系统所跟踪了,而且他处于Staged(暂存)状态!
把暂存区的文件commit后,此时该文件处于Unmodified(未修改)状态;此时假如我们编辑下该文件,文件又会变成Modified(修改)状态,接着git add又处于Staged(暂存)状态,然后commit...
1、在master分支操作的五部曲(本地master到远端origin的master分支) --》这里的origin指的是远端的主机名字,可以自定义,默认为origin。
① git status --- 查看工作区对比上一个commit之后的工作区修改状态
② git add -A --- 把工作区的内容提交到暂存区 也就是图中的Index中(-A代表的是全部,如果不想提交全部,那么换成指定的文件名)
③ git commit -m "备注的内容" --- 把Index暂存区的内容提交到本地仓库中。
④ git pull --- 拉去远端默认分支上的代码,并主动与本地仓库的内容进行合并,此处要注意conflict出现。
⑤ git push --- 把本地仓库的代码推送到远端默认仓库
备注:
A git status分别可以在②和③之前用,随时查看当前状态,②之前显示的是红色内容,③之前显示的绿色 Untracked是红色,git add后变成绿色色,git commit后变成普通颜色,
B ②③步两步可用git commit -a -m "备注的内容" (或者git commit -am "备注的内容" ) 一步给代替,效果如图2。
C 为什么会有②③这两个看似重复的步骤呢?直接改成一步不好吗?
暂存区 Index的作用:在commit前面的暂存区是可以随意的将各种文件的修改放进去的,这样我们就可以用多个指令精确的挑选出我们需要提交的所有修改,然后再一次性的(原子性的)提交到版本库。
原子性提交,每一个提交都是由多个文件的修改组成,而且这个提交是原子性的,要么这些修改全部成功,要么全部失败。
原子性提交带来的好处是显而易见的,这使得我们把项目整体还原到某个阶段或者时间点变得极为简便
D 出现了conflict怎么办? (出现冲突会有两种情况------ 一是:pull以后,远端和本地 二是:分支merge或者pop以后,本地的不同分支)
合并冲突,最后重新走一遍上述步骤,add commit pull最后push(在合并冲突的过程会发现现在所处的位置 分支名/mergin)
<<<<<<< HEAD
自己本地的内容 =======
后加进来的内容 >>>>>>
看看留哪个,把不用的和<
<<<<<<
=======
>>>>>>>一起
删去 ,注意把<<<<<<<
=======
>>>>>>> 多出来的空行也删掉,否则下次还会冲突, git是按行来记录内容的,他不会智能的跳过空行
E 如果想pull或者push任意分支到任意分支怎么办?origin代表远端仓库 (这里需要搞清楚本地的所站在的分支和远端的分支两个概念)
① git pull origin "分支名a" --- 代表把 远端的分支名a 拉取到到当前所站在的分支上 括号里的就代表当前所站在的分支
同理 git push origin "分支b" --- 代表把当前所站在的分支内容推送到 远端分支b
② git pull origin "远端分支名c" : "本地分支名d" --- 代表推送或者拉去指定分支到指定分支
git push origin "本地分支名d" : "远端分支名c"
注意:分支推送顺序的写法是<来源地>:<目的地> 所以: git pull是<远程分支>:<本地分支>,git push是<本地分支>:<远程分支>
③ 当推送push到远端的时候,远端并没有你所写的分支会怎么办??远端会自动创建你写的分支名,并进行push操作。
F git add -A进行了操作了哪些文件呢?
被修改过或已删除文件和所有untracted的文件(新建的文件),与之相对应的是 git add -u,它修改过或已删除文件的信息添加到索引库。它不会处理untracted的文件(未跟踪的新文件)
G git pull相当于git fetch
+git merge,意思是:
取回远程主机某个分支的更新,再与本地的指定分支合并(一下子进行了两步操作)
2、git remote命令(让你和远端仓库打交道)
Git要求每个远程主机都必须指定一个主机名。git remote
命令就用于管理主机名。不带选项的时候,git remote
命令列出所有远程主机
命令列表:
。$ git remote 命令列出所有远程主机。 --- origin
。$ git remote -v 参看远程主机的网址 --- https://github.com/yang123guo/git_test.git
。$ git remote add <主机名> <网址> -- 用于添加远程主机。
。$ git remote rm <主机名> -- 用于删除远程主机。
。$ git remote rename <原主机名> <新主机名> -- 用于远程主机的改名。
3、本地项目和远端仓库建立连接(本地仓库推送到远端仓库)
情况1:本地没有项目,从远端地址用git clone,这是会自动建立连接
在git clone
的时候,所有本地分支默认与远程主机的同名分支,建立追踪关系,也就是说,本地的master
分支自动"追踪"origin/master
分支。
情况2:本地已经有项目
a、首先先init,得到隐藏的git文件夹(两种方法,一种是在目录下右键选择 Git init Here,另外一种是在node环境下输入 git init)
b、用git add -A 和git commit -m '' 先保存到本地git中
c、
第一句的意思是:添加一个远端的仓库,并申明地址url,其中git remote是操作远端的意思 add是添加的意思,origin代表远端的主机名(可以随便起,但第一个习惯用origin)最后的url是远端仓库地址
第二句的意思是:把本地仓库push到远程仓库(git push),并把origin设为默认主机和master为主分支 -u是在本地分支与远程分支之间,建立一种追踪关系(tracking)
-u 等于 --set-upstream(建立追踪关系)
注意:在远端的github上的仓库里面不能内容(即使一个README.md文件也能让你push失败)
4、追踪关系是个什么东东??
在Git中‘追踪分支’是用与联系本地分支和远程分支的.
如果你在’追踪分支'(Tracking Branches)上执行推送(push)或拉取(pull)时, 它会自动推送(push)或拉取(pull)到关联的远程分支上.
git clone命令会自动在本地建立一个'master'分支,它是'origin/master'的‘追踪分支’. 而'origin/master'就是被克隆(clone)仓库的'master'分支.
你之前push过(git push origin dev:dev),但是不代表已经建立了追踪关系,要正真的关系需要git push --set-upstream origin dev建立关系,
建立关系以后就可以用最简单的 (git push 主机名 分支名),当你只有唯一的主机和分支名(唯一追踪),用(git push)。
5、git push与git pull的补充
如果省略本地分支名,则表示删除指定的远程分支,因为这等同于推送一个空的本地分支到远程分支。
$ git push origin :master 等同于 $ git push origin --delete master 表示删除origin主机的master分支。
$ git push -u origin master 上面命令将本地的master分支推送到origin主机,同时指定origin为默认主机,后面就可以不加任何参数使用git push了。(以上提到过)
6、git config 配置 命令
获得和设置配置变量,这些变量可以控制Git的外观和操作的各个方面,他们会在三个地方被保存
1./etc/gitconfig 文件: 包含了适用于系统所有用户和所有库的值。如果你传递参数选项’--system’ 给 git config,它将明确的读和写这个文件。
2.~/.gitconfig 文件 : 具体到你的用户。你可以通过传递--global 选项使Git 读或写这个特定的文件。
3.位于git目录的config文件 (也就是 .git/config) : 无论你当前在用的库是什么,特定指向该单一的库。每个级别重写前一个级别的值。因此,在.git/config中的值覆盖了在/etc/gitconfig中的同一个值。
操作指令:
git config --list ---- 列出所有git里面的配置信息 其中git config 具体属性(如:git config user.name) --- 会给出相应的信息
全局配置用户名和邮箱 --- 这个最常用,配置好以后就不用每次提交的时候输入了,这个地方配置我们的身份信息,作为自己参与团队协作的一个身份标记,比如谁对某个文件进行了修改。(用git config --list查看)
$ git config --global user.name 用户名
$ git config --global user.email 邮箱
私人配置用 git config --local (user.name 用户名 或者 user.email 邮箱)
其他配置
$ git config --global core.editor xx编辑器 配置你的缺省文本编辑器
$ git config --global merge.tool xx diff工具 配置diff工具
别名修改(嫌长难记),(git config --global alias.别名 '指定名称')
比如把git status中的status变成st $ git config --global alias.st status
7、git log日志 按q键退出
- 按数量
- -n:显示前n条log git log -2 显示最近两条
- 按日期
- --after=
- 比如git log --after="2014-7-1”,显示2014年7月1号之后的commit(包含7月1号)
- 后边的日期还可以用相对时间表示,比如"1 week ago"和”yesterday",比如git log --after="yesterday"
- 这里的格式可以是什么?
- --before=
- 同上
- 另外这两条命令可以同时使用表示时间段,比如git log --after="2014-7-1" --before="2014-7-4"
- 另外--since --until和 --after --before是一个意思,都可以用
- 按作者
- --author=
- 比如git log --author=“John",显示John贡献的commit
- 注意:作者名不需要精确匹配,只需要包含就行了
- 而且:可以使用正则表达式,比如git log --author="John|Mary”,搜索Marry和John贡献的commit
- 而且:这个--author不仅包含名还包含email, 所以你可以用这个搜索email
- 按commit描述
- --grep=
- 比如:git log --grep="JRA-224"
- 而且:可以传入-i用来忽略大小写
- 注意:如果想同时使用--grep和--author,必须在附加一个--all-match参数
- 按文件
- - -(空格)或[没有]
- 有时你可能只对某个文件的修改感兴趣, 你只想查看跟某个文件相关的历史信息, 你只需要插入你感兴趣文件的路径[对,是路径,所以经常是不太好用]就可以了
- 比如:git log -- foo.py bar.py ,只返回和foo.py或bar.py相关的commit
- 这里的--是告诉Git后面的参数是文件路径而不是branch的名字. 如果后面的文件路径不会和某个branch产生混淆, 你可以省略- -,比如git log foo.py
- 另外,后边的路径还支持正则,比如:git log *install.md 是,指定项目路径下的所有以install.md结尾的文件的提交历史
- 另外,文件名应该放到参数的最后位置,通常在前面加上--并用空格隔开表示是文件
- 另外,git log file/ 查看file文件夹下所有文件的提交记录
- 按分支
- - -
- --branchName branchName为任意一个分支名字,查看某个分支上的提交记录
- 需要放到参数中的最后位置处
- 如果分支名与文件名相同,系统会提示错 误,可通过--选项来指定给定的参数是分支名还是文件名
- 比如:在当前分支中有一个名为v1的文件,同时还存在一个名为v1的分支
- git log v1 -- 此时的v1代表的是分支名字(--后边是空的)
- git log -- v1 此时的v1代表的是名为v1的文件
- git log v1 -- v1 代表v1分支下的v1文件
- 按内容
- -S"<string>"、-G"<string>"
- 有时你想搜索和新增或删除某行代码相关的commit. 可以使用这条命令
- 假设你想知道Hello, World!这句话是什么时候加入到项目里去的,可以用:git log -S"Hello,World!"
- 另外:如果你想使用正则表达式去匹配而不是字符串, 那么你可以使用-G代替-S.
- 这是一个非常有用的debug工具, 使用他你可以定位所有跟某行代码相关的commit. 甚至可以查看某行是什么时候被copy的, 什么时候移到另外一个文件中去的
- 注:-S后没有"=",与查询内容之间也没有空格符
- 按范围
- git log <since>..<until>
- 这个命令可以查看某个范围的commit
- 这个命令非常有用当你使用branch做为range参数的时候. 能很方便的显示2个branch之间的不同
- 比如:git log master..feature,master..feature这个range包含了在feature有而在master没有的所有commit,同样,如果是feature..master包含所有master有但是feature没有的commit
- 另外,如果是三个点,表示或的意思:git log master...test 查询master或test分支中的提交记录
- 过滤掉merge commit
- --no-merges
- 默认情况下git log会输出merge commit. 你可以通过--no-merges标记来过滤掉merge commit,git log --no-merges
- 另外,如果你只对merge commit感兴趣可以使用—merges,git log --merges
- 按标签tag
- git log v1.0
- 直接这样是查询标签之前的commit
- 加两个点git log v1.0.. 查询从v1.0以后的提交历史记录(不包含v1.0)
- 按commit
- git log commit :查询commit之前的记录,包含commit
- git log commit1 commit2:查询commit1与commit2之间的记录,包括commit1和commit2
- git log commit1..commit2:同上,但是不包括commit1
- 其中,commit可以是提交哈希值的简写模式,也可以使用HEAD代替
- HEAD代表最后一次提交,HEAD^为最后一个提交的父提交,等同于HEAD~1
- HEAD~2代表倒数第二次提交
8、版本切换和回退、删除
删除:工作区的文件 rm(linux命令)或者 右键
工作区的回退:1、只修改了工作区的内容,还没有add,忘记修改内容并且ctrl+s保存了很多次代码,或者是误删除了东西 采取的回退方式是:暂存区内容覆盖当前工作区的内容 git checkout -- xxxx
2、修改并且已经add,但还没有commit ,这时候记录在index暂存区有了记录,此时checkout文件是没用了,得本地仓库回退覆盖(git reset)让当前文件回到上一次提交时的状态
此时先 git reset HEAD XX文件 (理解为:暂存区的修改撤销掉) 此时再调用:
git checkout -- XX文件 (空暂存区覆盖工作区) 工作区的文件即可恢复原样!
版本回退: 版本:用commit提交到本地仓库所生成的一个快照,用HEAD
表示当前版本,用git log打印出来的版本ID是十六进制的哈希算法值,如:ea34578d5496d7dd233c827ed32a8cd576c5ee85
上一个版本就是HEAD^
,上上一个版本就是HEAD^^
,当然往上100个版本写100个^
比较容易数不过来,所以写成HEAD~100
。
所以表示版本信息有三种方法 1、 HEAD^n 2、HAED@{n}(1,2按版本保存顺序找,弊端:只能回退不能前进,回退以后比他还晚的就消失了) 3、版本ID 十六进制的哈希算法值 , 如:ea34578d5496d7dd233c827ed32a8cd576c5ee85
回退版本的指令是: git reset --hard 版本 (如:回到上一个版本 git reset --hard HEAD^
) 回到任意一个版本 (git reset --hard 3628164/版本ID输入几位就可以了)
这里的版本回退主要是针对本地仓库来说的,本地的工作区和暂存区是同时会跟着的。
重返未来(找回回退):git reflog 找回并列出所有版本序列(包括已经删除的,不过有几个月的时间限制,太久了也不行) git reset --hard 版本 回到未来版本,此时你可以看见列表区域的HAED是重新计算过的,
如图
其中的 暂存---->工作区 的checkout意思是:checkout可以让暂存东西覆盖工作区,而不是add命令以后 修改内容进入了暂存区再使用checkout,这时候checkout已经无效了。(他的原理是 从 .. 到 .. 的指针指向导致的覆盖)
同理的还有git reset命令,也是从前到后的覆盖,按图中的意思是:版本回退的机理是仓库覆盖了index暂存。 ---- 对的,但是注意的是git reset HEAD XX文件最后是文件名而不是版本号
问题:那么版本回退reset的时候只是本地覆盖暂存吗?本地仓库变了没?可不可以直接push?? 结论:可以直接push,push以后是变更后了本地仓库内容,此时本地工作区也跟着变了。说明reset可以在本地仓库多个历史版本中切换。
结论:git reset 有两种回退方法:图中给出了本地仓库到暂存区的回退 git reset HEAD XX文件 (HEAD 未加版本号代表当前最新版本)
本地仓库之间也可以相互回退跨越 git reset --hard HEAD 版本 (此时相关的工作区也是跟着变化了) 对比 git reset HEAD 版本(工作区不跟着变化)
git checkout -- 文件名
命令中的--
很重要,没有--
,就变成了“切换到另一个分支”的命令
9、其他
Git命令的自动补全 ------ 输入git命令后 按Tab就可以了