PS:小白的入门笔记,希望能有用,主要参考廖君Git教程
操作环境说明:
- github仓库
https://github.com/zhanghlHUST/figureForMarkdown
,作为名词说明的示例- 本地工作区
E:\github\figureForMarkdown
- 操作系统 win10 64位
- Github Desktop 3.3.4.0
- Windows PowerShell(x86) 运行 Git Shell 作为命令行工具
参考:
廖君Git教程
git pro
图解Git
知乎-分支操作的一些讨论
简单粗暴的注意事项:
- 使用
git checkout
删除工作区修改、撤销删除、切换分支时,一定要注意备份当前工作区的内容,git会采用仓库内容直接更新工作区,造成工作区修改的丢失
为什么学 git ?
- 版本控制,git、CVS 及 SVN 都能做到
- 分布式的合作开发的控制
- github 的开放的资源
git 的版本控制
工作时从 版本库中提取文件到 工作区进行文件的创建、编辑、修改、删除等操作,工作 完成后提交至版本库,由版本库跟踪文件的修改历史,所有的版本控制系统 只能跟踪文本文件的改动,版本控制系统可以告诉你每次的改动,如 txt 文件、网页、代码等。而图片、视频这些二进制文件,虽然也能由版本控制系统管理,但 没法跟踪二进制文件的变化,只能把二进制文件每次改动串起来,也就是只知道图片从100KB改成了120KB,但到底改了啥,版本控制系统不知道,也没法知道。
知识结构
后续学习任务
- 结合《Falsk Web 开发》对 git 在实际项目的控制过程进行实战,写成总结
- github 的正确使用姿势,写成总结
- git 的底层机制的学习,特别是
diff
,checkout
,reset
,branch
,commit
这几个概念的深入理解,写成总结
工作区及版本库的概念
- 工作区: 就是你在电脑里能看到的目录,即
E:\github\figureForMarkdown
- 版本库: 工作区有一个隐藏目录.git,即本地的Git的版本库,当前仓库下,如果没有任何的提交,那么版本库就是对应上次提交后的内容。主要包含暂存区
index
,git自动创建的第一个分支master
以及指向master
的指针HEAD
- 暂存区: git的版本库里面最重要的就是称为
stage
(或index
)的暂存区,实际上就是一个包含文件索引的目录树,像是一个虚拟的工作区。在这个虚拟工作区的目录树中,记录了文件名、文件的状态信息(时间戳、文件长度等),文件的内容并不存储其中,而是保存在Git对象库(.git/objects)中,文件索引建立了文件和对象库中对象实体之间的对应。如果当前仓库,有文件更新,并且使用git add命令,那么这些更新就会出现在暂存区中。
操作
创建版本库(by shell)
- 首先,选择一个合适的地方,创建一个空目录,
mkdir learngit
,cd learngit
,pwd OUTPUT: E:\github\learngit
,windows系统中各级目录名中最好不要使用中文- 通过
git init
将该目录编程git可以管理的仓库,OUTPUT: Initialized empty Git repository in E:/github/learngit/.git/
,至此仓库生成目录下包含.git
目录用来跟踪管理版本库。ls -ah
查看隐藏的.git
文件
创建版本库(by desktop)
+
->Create
-> 选择 Name
及local path
-> Create repository
添加文件到版本库 (by shell)
git add命令实际上就是把要提交的所有修改放到暂存区(Stage),然后,执行git commit就可以一次性把暂存区的所有修改提交到分支。一旦提交后,如果你又没有对工作区做任何修改,那么工作区就是“干净”的,git status
查看
- 在工作区
E:\github\learngit
中创建readme.txt
文件,编辑输入:
Git is a version control system.
Git is free software.
- 使用
git add readme.txt
添加文件至仓库(暂存区index
以及objects
)。没有输出表示操作成功,可反复多次使用,可以同时添加多个文件;- 使用
git commit -m 'wrote a readme file'
将文件提交至当前分支,并添加描述信息,会显示分支当前的状态与暂存区的比较结果,-m
后为本次提交说明
添加文件到版本库 (by Desktop)
Desktop没有先add到暂存区,然后commit到分支的概念,文件编辑后,直接查看
changes
查看文件的变化,勾选需要提交的变化,输入comment summary and descriptipn
提交
修改文件 (by Shell)
git status
命令可以让我们时刻掌握仓库当前的状态。- 工作区文件与仓库完全一致时,
git status
结果为:
On branch master
nothing to commit, working tree clean
即 没有待提交内容,工作目录是干净的
- 工作区文件改变,如上修改
readme.txt
, 改为Git is a distributed version control system.
,git status
结果为:
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working dir
modified: reademe.txt
no changes added to commit (use "git add" and/or "git commit -a")
即 文件被修改,但是还未准备提交修改
- 工作区文件修改,并
git add
提交至缓存区,git status
结果为:
修改文件 (by Desktop)
与添加文件操作完全一致,客户端不严格区分暂存区与工作区
历史版本的操作
像这样,你不断对文件进行修改,然后不断提交修改到版本库里,就好比玩RPG游戏时,每通过一关就会自动把游戏状态存盘,如果某一关没过去,你还可以选择读取前一关的状态。有些时候,在打Boss之前,你会手动存盘,以便万一打Boss失败了,可以从最近的地方重新开始。Git也是一样,每当你觉得文件修改到一定程度的时候,就可以“保存一个快照”,这个快照在Git中被称为commit。一旦你把文件改乱了,或者误删了文件,还可以从最近的一个commit恢复,然后继续工作,而不是把几个月的工作成果全部丢失。
查看历史纪录(by Shell)
git log
命令显示从最近到最远的提交日志,可以看到两次操作,分别是创建文件,及修改文件,git log --pretty=online
整理输出信息
将仓库撤回到指定版本
首先,Git必须知道当前版本是哪个版本,在Git中,用HEAD表示当前版本,也就是最新的提交3628164...882e1e0(注意我的提交ID和你的肯定不一样),上一个版本就是HEAD,上上一个版本就是HEAD,当然往上100个版本写100个比较容易数不过来,所以写成HEAD~100。也可以直接使用
git log
显示的commit
版本号,且版本号没必要写全,前几位就可以了,Git会自动去找。
git reflog
查看历史commit的版本号- 参照廖君Git教程 设置三次提交
git reset --hard HEAD^
将文件撤回到add distributed
版本,并且git log
查看,add GPL
已经消失,- git的版本撤回是将HEAD指针的指向,并更新工作区的文件。
commit ea34578d5496d7dd233c827ed32a8cd576c5ee85
Author: Michael Liao <askxuefeng@gmail.com>
Date: Tue Aug 20 14:53:12 2013 +0800
add distributed
commit cb926e7ea50ad11b8f9e909c05226233bf755030
Author: Michael Liao <askxuefeng@gmail.com>
Date: Mon Aug 19 17:51:55 2013 +0800
wrote a readme file
On branch master
Changes to be committed:
(use "git reset HEAD <file>..."
modified: reademe.txt
即 文件被修改,待提交
查看文件的对比区分
git diff
查看缓存区与工作区的文件的改变细节。
撤销修改
撤销工作区的修改(by Shell)
- 当工作区的文件修改且该修改并未提交到暂存区,
git checkout -- file
可以丢弃工作区的修改- 当工作区的文件修改已经提交到暂存区,又做了修改,
git checkout -- file
撤销修改就回到添加到暂存区后的状态。- 就是让这个文件回到最近一次
git commit
或git add
时的状态。git checkout
其实是用版本库里的版本替换工作区的版本,无论工作区是修改还是删除,都可以“一键还原”
撤销暂存区的修改(by Shell)
git reset HEAD file
可以把暂存区的修改撤销掉(unstage),重新放回工作区
撤销工作区及暂存区的修改(by Desktop)
- Destop 并未严格区分工作区与暂存区,
changes
-> 选择指定的修改 ->discard changes
撤销提交到本地版本库的修改(by Shell)
- 上文中的将仓库撤回指定版本的操作,
git reset --hard 版本号
撤销提交到本地版本库的修改(by Desktop)
History
-> 选择指定的提交 ->revert
删除文件(by Shell)
git rm
相当于手动删除文件并提交至暂存区,然后git commit -m "commit"
至本地版本库git checkout -- file
恢复误删的文件
删除文件(by Desktop)
- 手动删除文件 ->
changes
-> 提交修改到版本库
本地仓库与远程仓库
- 远程仓库就是在公网服务器上的仓库(repository),即
https://github.com/zhanghlHUST/figureForMarkdown
,Git是分布式版本控制系统, 同一个Git仓库,可以分布到不同的机器上,找一台电脑充当服务器的角色,每天24小时开机,其他每个人都从这个“服务器”仓库克隆一份到自己的电脑上,并且各自把各自的提交推送到服务器仓库里,也从服务器仓库中拉取别人的提交。Github用来提供Git仓库的托管服务,注册一个Github账号就免费获得Git远程仓库。
操作
设置 SSH-Key
github官网 头像下拉 -> Settings
-> SSH and GPG keys
-> SSH keys
创建远程仓库
在本地创建了一个Git仓库后,可以在GitHub创建一个Git仓库,并且让这两个仓库进行远程同步,这样,GitHub上的仓库既可以作为备份,又可以让其他人通过该仓库来协作,github上的仓库是公开的,可以选择缴费或者自己搭建远程仓库服务器的方式创建私有的仓库。
github官网 -> +
-> new repository
-> 输入Repository name
-> 不要选 Initialize this repository with a README
为本地仓库添加远程仓库(by Shell)
在本地仓库运行 -> git remote add origin https://github.com/zhanghlHUST/learngit.git
(添加后远程库的名字是origin)
为本地仓库添加远程仓库(by Desktop)
右上角Publish
-> 输入 Name
和 Description
-> 下拉选择 github 账号 -> Publish repository
,之后按钮 Publish
会转变成 Sync
推送本地仓库内容到远程仓库(by Shell)
把本地库的内容推送到远程,用
git push
命令,实际上是把当前分支master推送到远程。
由于远程库是空的,我们 第一次推送master分支时,加上了-u参数,Git不但会把本地的master分支内容推送的远程新的master分支,还会把本地的master分支和远程的master分支关联起来,此后,每次本地提交后,只要有必要,就可以使用命令git push origin master推送最新修改
在本地仓库运行 -> git push -u origin master
在本地仓库运行 -> git push origin dev
将 dev
分支推送至远程仓库 origin
什么分支需要推送?
-
master
分支是主分支,因此要时刻与远程同步,保存程序的发行版本。 -
dev
分支是开发分支,保存程序的主要开发版本,团队所有成员都需要在上面工作,所以也需要与远程同步; -
bug
分支只用于在本地修复bug
,就没必要推到远程了,除非老板要看看你每周到底修复了几个bug
; -
feature
分支是否推到远程,取决于你是否和你的小伙伴合作在上面开发。
推送本地仓库内容到远程仓库(by Desktop)
右上角Sync
,完全同步本地库与远程库,让两者保持一致,功能单一
创建远程 orgin
的 dev
分支到本地
git checkout -b dev origin/dev
在dev
分支上协作开发,就必须创建远程origin
的dev
分支到本地
从远程库克隆创建本地仓库(by Shell)
在本地仓库运行 -> git clone git@github.com:zhanghlHUST/learngit.git
, 当从远程仓库克隆时,实际上Git自动把本地的master
分支和远程的master
分支对应起来了,并且,远程仓库的默认名称是origin
从远程库克隆创建本地仓库(by Desktop)
+
-> Clone
-> 选择 repository -> Clone repositoty
-> 选择本地仓库目录
查看远程库的信息 (by Shell)
git remote
, git remote -v
查看远程库的详细信息
合作开发的控制
分支的概念
- 合作开发的控制: 可以同时拥有多个开放的分支,每个分支用于完成特定的任务,随着开发的推进,可以随时把某个特性分支的成果并到其他分支中。
- 维护不同程度的稳定性:通常在
master
分支中保留完全稳定的代码(通常是已经发布或者即将发布的代码),与此同时还有一个名为master
或者next
的分支 专门用于后续的开发或者测试,当next
的新特性一旦进入某种稳定状态,就可以将新特性合并到master
,同样设置 针对具体问题的特性分支(短期分支)topic
,在确保指定特性分支能够通过所有测试之后将其合并到主干分支,等待后续的发布。
- 如上图由下往上,起先我们在
master
工作到C1
,然后开始一个新分支iss91
尝试修复 91 号缺陷,提交到C6
的时候,又冒出一个解决该问题的新办法,于是从之前C4
的地方又分出一个分支iss91v2
,干到C8
的时候,又回到主干master
中提交了C9
和C10
,再回到iss91v2
继续工作,提交C11
,接着,又冒出个不太确定的想法,从master
的最新提交C10
处开了个新的分支dumbidea
做些试验。现在,假定两件事情:我们最终决定使用第二个解决方案,即iss91v2
中的办法;另外,我们把dumbidea
分支拿给同事们看了以后,发现它竟然是个天才之作。所以接下来,我们准备抛弃原来的iss91
分支(实际上会丢弃C5
和C6
),直接在主干中并入另外两个分支
- 特性分支 在任何规模的项目中都可以使用特性分支。一个特性分支是指一个短期的,用来实现单一特性或与其相关工作的分支,该技术允许你迅速且完全的进行语境切换,工作将分散在不同的流水线上,每个分支都有特定的目标特性。
操作
创建分支(by Shell)
git branch dev
创建分支dev
,无输出
创建分支(by Desktop)
点击branch前的分支按钮 -> 输入 Name
-> 选择 From branch
-> Create new branch
切换分支(by Shell)
从当前的提交之后创建分支
git checkout dev
切换到分支dev
,输出 Switched to branch 'dev'
git checkout -b dev
创建并切换到 dev
分支,输出 Switched to a new branch 'dev'
切换分支(by Desktop)
点击当前分支名称旁的下拉按钮,选择分支
不同分支的内容的可见性(by Shell)
不同的分支管理不同的commit
,commit
之间通过地址单向访问,继而限制了不同分支的可见性,git checkout branchName
切换到不同分支之后,工作区的文件内容会自动更新。注意备份工作区的修改
查看分支(by Shell)
git branch
查看分支列表
合并分支(by Shell)
将dev
并入master
- 切换到
master
分支,git checkout master
- 合并
dev
分支到当前分支,git merge dev
合并分支(by Desktop)
切换到 master
-> compare
-> 选取待合并的目标分支 -> update from target branch
合并方式
-
Fast-forward
,“快进模式”,也就是直接把master指向dev的当前提交,所以合并速度非常快
合并冲突
设想情景,在
commit n
之后创建分支dev
,提交修改commit n+1
,切换到master
分支,提交修改commit n+2
,然后试图将dev
分支合并到master
时出现冲突,输出Auto-merging README.md \n CONFLICT (content): Merge conflict in README.md \n Automatic merge failed; fix conflicts and then commit the result.
输出:
Git is a distributed version control system.
Git is free software distributed under the GPL.
Git has a mutable index called stage.
Git tracks changes of files.
<<<<<<< HEAD
Creating a new branch is quick & simple.
=======
Creating a new branch is quick AND simple.
>>>>>>> feature1
其中,Git用<<<<<<<
,=======
,>>>>>>>
标记出不同分支的内容。
- 直接对冲突的文件进行修改,删除不同分支冲突的内容。
- 在
master
分支提交修改。 -
git log --graph --pretty=oneline --abbrev-commit
查看分支的合并情况
删除分支 (by Shell)
git branch -d dev
删除分支 dev
删除分支 (by desktop)
切换到目标分支 -> 右上角齿轮状设置按钮 -> Delete target_branch ...
查看分支合并情况 (by Shell)
git log --graph --pretty=oneline --abbrev-commit
查看分支的合并情况及相应的提交
git 分支管理策略
团队合作分支策略
- 首先,
master
分支应该非常稳定,仅用来发布新版本,平时不能在上面干活;- 干活都在
dev
分支上,即dev
时不太稳定的,更超前的;- 不同的开发人员在不同的分支上进行开发,然后将性能稳定的版本合并至
dev
版本上。
bug
分支及当前分支工作的stash
储藏功能
- 情景 软件开发中遇到
bug
时,每个bug都可以通过一个新的临时分支来修复,修复后,合并分支,然后将临时分支删除。比如当你接到一个修复一个代号101
的bug
的任务时,很自然地,你想创建一个分支issue-101
来修复它,但是,假如当前正在dev上进行的工作还没有提交,并且当前的工作仍在进行中,不能提交,然而创建分支修改bug又势在必行。此时创建分支修改bug
后提交修改时,会将dev
的内容同时提交。此时需要stash
保存工作现场。
操作
创建commit的合并方式 (by Shell)
合并分支时,Git会尽可能采用
Fast forward
模式,在这种模式下,删除分支后,会丢掉分支信息。如果强行禁用Fast forward
模式,Git就会在merge
时生成一个新的commit
, 这样分支历史就可以看出分支信息。
工作现场stash
的使用 (by Shell)
当工作区的目标不适合提交(正在开发或者是脏的提交)时,使用
stash
保存快照,并进行恢复,不过stash
只保存改变的需要被跟踪的文件,其他文件不会被跟踪。恢复stash
时,若跟踪的文件被修改,会遇到冲突。
-
git stash
保存工作现场,此时git status
查看工作区没有修改信息 -
git stash list
查看工作现场的列表 -
git stash apply stash@{id}
恢复stash@{id}
的内容,git stash drop
删除工作现场 -
git stash pop stash@{id}
恢复stash@{id}
的内容同时删除它
git merge --no-ff -m "merge message" dev
合并分支并提交merge
信息
强行丢弃分支
git branch -D <name>
可以强行删除未合并到主分支的特性分支
多人协作的工作方式
当有冲突的时候,先用
git pull
把最新的提交从origin/dev
抓下来,然后在本地合并,解决冲突,再推送
- 首先,可以试图用
git push origin branch-name
推送自己的修改; - 如果推送失败,则因为远程分支比你的本地更新,需要先用
git pull
抓取远程仓库的最新更新; - 如果
git pull
提示“no tracking information”
,则说明本地分支和远程分支的链接关系没有创建,指定本地dev
与origin/dev
分支的链接,git branch --set-upstream dev origin/dev
- 然后本地的仓库中出现冲突,需要手动解决,解决方式与分制管理中的解决冲突的方式一致。解决冲突提交到本地仓库,
- 推送到远程仓库
git push origin dev
标签管理
tag就是一个让人容易记住的有意义的名字,它跟某个commit绑在一起,一般给程序的不同版本设置标签,便于快速查找
操作
创建标签 (by Shell)
- 切换到需要打标签的分支上
git checkout <branch name>
- 创建标签
git tag <name>
对最新的提交打标签 - 创建标签
git tag <tag name> <commit id>
给指定提交打标签 - 创建有说明信息的标签
git tag -a <tagname> -m "tag message" <commit id>
查看所签 (by Shell)
- 列出所有标签
git tag
, 标签的顺序是字母顺序,而不是时间顺序 - 查看标签的详细信息
git show <tagname>
删除标签 (by Shell)
- 删除本地仓库标签
git tag -d <tag name>
- 删除远程仓库的标签, 先删除本地标签
git tag -d <tag name>
,然后删除远程的标签git push origin :refs/tags/v0.9
推送标签到远程仓库 (by Shell)
- 推送单个标签
git push <branch name> <tag name>
- 一次性推送多个标签
git push origin --tags