Git 基础学习
- 版本控制
- 常见的版本控制工具
- 版本控制分类
- 本地式版本控制
- 集中式版本控制
- 分布式版本控制
- Git与SVN的主要区别
- Git
- git工作空间的三个区域
- 版本控制(git管理文件夹)的基本步骤
- git日常命令
- 撤销修改
- Git 分支
- 分支的常用指令
- 分支冲突
- 分支管理策略
- Bug 分支
- Future 分支
- 多人协作
- tags 标签管理
- 新建标签
- 操作标签
- Github
- Github 准备:SSH配置
- Github 创建仓库
- 开发基本流程
- 主仓库作为服务器
- 仓库迁移
- 忽略提交
- 补充
- 本地仓库关联多个远程仓库
- rebase应用场景
- 多个提交记录合并成一个提交记录
- dev 记录合并到 master 记录
- 合并分叉
- 合并成一条直线
- 快速解决冲突
- 多人协同开发
- 创建初始项目和版本
- 其他
- 免密码登录
- 自定义 Git
- 问题
- 注意
- 分支名称的命名规则
- 面试题
参考廖雪峰-git教程
Git GitHub GitLab使用教程
版本控制
版本控制(Revision control)是一种在开发的过程中用于管理我们对文件、目录或工程等内容的修改历史,方便查看更改历史记录,备份以便恢复以前的版本的软件工程技术,简单说就是用于管理多人协同开发项目的技术。
如果你用Microsoft Word写过长篇大论,那你一定有这样的经历:想删除一个段落,又怕将来想恢复找不回来怎么办?有办法,先把当前文件“另存为……”一个新的Word文件,再接着改,改到一定程度,再“另存为……”一个新文件,这样一直改下去,最后你的Word文档变成了这样:
多人开发就必须要使用版本控制!
常见的版本控制工具
- Git
- SVN(Subversion)
- CVS(Concurrent Versions System)
- VSS(Micorosoft Visual SourceSafe)
- TFS(Team Foundation Server)
- Visual Studio Online
版本控制分类
本地式版本控制
相当于每个版本一个快照,多人开发每个人电脑上一个版本,不适合多人开发,适合个人使用
集中式版本控制
版本库是集中存放在中央服务器的,而干活的时候,用的都是自己的电脑,所以要先从中央服务器取得最新的版本,然后开始干活,干完活了,再把自己的活推送给中央服务器。中央服务器就好比是一个图书馆,你要改一本书,必须先从图书馆借出来,然后回到家自己改,改完了,再放回图书馆。
- 所有的版本数据都保存在服务器上,协同开发者从服务器上同步更新或上传自己的修改
- 不同用户和云盘(服务器)进行交互,服务器中存储不同版本,用户的本地只有自己以前所同步的版本
- 如果不连网的话,用户就看不到历史版本,也无法切换版本验证问题,或在不同分支工作
- 所有数据都保存在单一的服务器上,有很大的风险这个服务器会损坏,这样就会丢失所有的数据,当然可以定期备份。代表产品:SVN、CVS、VSS
分布式版本控制
在实际使用分布式版本控制系统的时候,其实很少在两人之间的电脑上推送版本库的修改,因为可能你们俩不在一个局域网内,两台电脑互相访问不了,也可能今天你的同事病了,他的电脑压根没有开机。因此,分布式版本控制系统通常也有一台充当“中央服务器”的电脑,但这个服务器的作用仅仅是用来方便“交换”大家的修改,没有它大家也一样干活,只是交换修改不方便而已。
- 集中式版本控制只能从服务器拿到一个版本,分布式版本控制每个用户可以拿到服务器的所有版本,由于每个用户那里保存的都是所有的版本数据,只要有一个用户的设备没有问题就可以恢复所有的数据,但这增加了本地存储空间的占用
- 可以离线在本地提交,只需在连网时push到相应的服务器或其他用户那里
- 用户提交版本先提交到本地,再提交到服务器中心
- 不会因为服务器损坏或者网络问题,造成不能工作的情况
Git与SVN的主要区别
- SVN是集中式版本控制系统,版本库是集中放在中央服务器的,而工作的时候,用的都是自己的电脑,所以首先要从中央服务器得到最新的版本,然后工作,完成工作后,需要把自己的代码推送到中央服务器。集中式版本控制系统是必须联网才能工作,对网络带宽要求较高。
- Git是分布式版本控制系统,没有中央服务器,每个人的电脑就是一个完整的版本库,工作的时候不需要联网了,因为版本都在自己电脑上。协同的方法是这样的:比如说自己在电脑上改了文件A,其他人也在电脑上改了文件A,这时,你们两之间只需把各自的修改推送给对方,就可以互相看到对方的修改了。Git可以直接看到更新了哪些代码和文件!
Git
git工作空间的三个区域
-
工作目录(Working Directory):正在操作的文件夹,工作区的文件分为,已管理文件和新文件/正在修改的文件,我们看到的目录文件夹就是一个工作区
-
暂存区(Stage/Index):git add . 之后的文件,git管理的文件暂时放到这里,可以提交可以回滚,事实上它只是一个文件,保存即将提交到文件列表信息
-
版本库(Repository或Git Directory):git commit 之后保存的版本,就是安全存放数据的位置,这里面有你提交到所有版本的数据
工作区的 .git 文件夹就是版本库,创建Git版本库时,Git自动为我们创建了唯一一个master分支 -
如果在加上远程的git仓库(Remote Directory):远程仓库,托管代码的服务器,可以简单的认为是你项目组中的一台电脑用于远程数据交
项目修改流向:
-
使用两次命令git add,把readme.txt和LICENSE都添加后
-
执行git commit就可以一次性把暂存区的所有修改提交到分支
版本控制(git管理文件夹)的基本步骤
- 创建项目文件夹,并进入,在工作目录中添加、修改文件
1
2mkdir 项目名
- git初始化,让git帮助管理当前文件夹
1
2
3
4
5
6
7
8git init 进入要管理的文件夹执行 git init demo 创建demo文件夹,并管理这个文件夹中的内容 取消项目版本控制:进入项目目录 ——> find . -name ".git" | xargs rm -Rf mac中所有 . 开头的文件都是隐藏文件,可以通过 ls -la 查看隐藏文件
- 管理目录下文件状态
1
2
3
4
5
6git status 新增的文件和修改后的文件都是红色 以下表示没有需要提交的修改 On branch master nothing to commit, working tree clean
- 查看修改内容,查看文件不同 (difference),只要明确修改文件是在工作区修改的,add之后,暂存区就有了修改后的文件,commit之后,分支(版本库)中就有了修改后的文件
1
2
3
4
5
6
7
8
9
10$ git diff 查看工作区(work dict)和暂存区(stage)的区别 $ git diff <file> 查看文件做了什么修改 $ git diff --cached 查看暂存区(stage)和分支(master)的区别 $ git diff HEAD -- <file> 查看工作区和版本库里面最新版本的区别 如果文件已经add了,暂存区和工作区内容就一样了,git diff是什么都不显示的 已经commit了,工作区,暂存区,版本库(分支)中就都一样了,以上命令都没显示
- 管理指定文件
1
2
3
4
5
6
7
8
9
10
11
12git add 文件名 实际是将文件从工作区add到暂存区 git add . 管理所有文件 添加许多同种类型的文件,可以使用通配符 * (记得加引号) 如: git add "*.txt" 命令就是添加所有 .txt 文件 - git rm -r --cached App.class git rm -r --cached . 将所有文件从暂存区踢出
1
2
3fatal: pathspec 'readme.txt' did not match any files 添加某个文件时,该文件必须在当前目录下存在,用ls或者dir命令查看当前目录的文件,看看文件是否存在,或者是否写错了文件名
- 个人信息配置:用户名,邮箱(仅需第一次使用git时配置)
1
2
3
4
5
6
7
8
9查看当前git的用户名和邮箱 git config --global user.name git config --global user.email 设置git用户名和邮箱 git config --global user.name [username] git config --global user.email [email] # Git 是分布式版本控制系统,所以,每个机器都必须自报家门:你的名字和Email地址 #git config 命令的 --global 参数,用了这个参数,表示你这台机器上所有的 Git 仓库都会使用这个配置,当然也可以对某个仓库指定不同的用户名和Email地址(不加 --global)。
1
2
3
4
5
6
7
8
9
10
11项目配置文件:项目/.git/config git config --local user.name 'name' git config --local user.email 'email' 全局配置文件:~/.gitconfig git config --global user.name 'name' git config --global user.email 'email' 系统配置文件:/etc/.gitconfig 需要root权限 git config --system user.name 'name' git config --system user.email 'email'
- 生成版本
你不断对文件进行修改,然后不断提交修改到版本库里,就好比玩RPG游戏时,每通过一关就会自动把游戏状态存盘,如果某一关没过去,你还可以选择读取前一关的状态。有些时候,在打Boss之前,你会手动存盘,以便万一打Boss失败了,可以从最近的地方重新开始。Git也是一样,每当你觉得文件修改到一定程度的时候,就可以“保存一个快照”,这个快照在Git中被称为commit。一旦你把文件改乱了,或者误删了文件,还可以从最近的一个commit恢复,然后继续工作,而不是把几个月的工作成果全部丢失。
1
2
3
4
5git commit -m '描述信息' 1 file changed:1个文件被改动(我们新添加的readme.txt文件) 2 insertions:插入了两行内容(readme.txt有两行内容)
- 查看版本记录
git log命令显示从最近到最远的提交日志,我们可以看到3次提交,最近的一次是append GPL,上一次是add distributed,最早的一次是wrote a readme file复制代码1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22git lg 会展示简短的版本号,提交时间,提交人 bogon:项目 didi$ git lg * 版本号 - (HEAD -> lhf-dev, origin/lhf-dev, origin/dev, dev) 提交名 (13 days ago) <提交人> git log git log --oneline #美化输出信息,每个记录显示为一行,显示 commit_id 前几位数 git log --oneline --graph 当前分支的版本线图 git log --oneline --graph --all 所有分支的版本线图 git log --pretty=oneline #美化输出信息,每个记录显示为一行,显示完整的 commit_id git log --graph 图像化显示提交记录 git log --graph --pretty=format:"%h %s" 显示格式化,h 表示哈希值,s 表示提交记录 如果嫌输出信息太多,看得眼花缭乱的,可以试试加上--pretty=oneline参数 $ git log --pretty=oneline 版本号 (HEAD -> master) append GPL 版本号 add distributed 版本号 wrote a readme file
git日常命令
接上面 【版本控制(git管理文件夹)的基本步骤】
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26拓展新功能 git add git commit -m '新功能' git commit -am '新功能' 相当于执行了 add 和 commit,但是只能针对【被 add 过的,修改了的文件】 修改后,add之前,恢复到没有修改之前的状态 git checkout -- index.html 恢复index.html到上一个版本 git checkout b52ed7d -- index.html 恢复index.html到id为b52ed7d的提交版本,但其他文件不能恢复 git checkout b52ed7d -- . 恢复所有文件到b52ed7d版本 修改后,add之后,回滚到之前的版本 git log git reset --hard 要回滚到的版本号 版本号只需要写不重复的前几位即可,git会自动去匹配 git reset --hard HEAD^ 回滚到上一个版本 HEAD~3 往上第三个版本 git reset --hard ORIG_HEAD 回滚到最开始的版本 你从21世纪坐时光穿梭机来到了19世纪,想再回去已经回不去了,肿么办? 只要上面的命令行窗口还没有被关掉,你就可以顺着往上找未来版本的版本号,就可以指定回到未来的某个版本 回滚到之后的版本 git reflog git reset --hard 要回滚到的版本号
HEAD指向的版本是当前版本,git允许我们在版本的历史 git log 和未来 git reflog 穿梭
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26其他指令 git rm <file> 相当于执行 rm <file> 单独的 rm 只是删除了工作区的文件,git status 会显示出 暂存区、版本库的这个文件并没有被删除 git add <file> 删除暂存区的这个文件 git commit -m 'remove test.txt' 删除版本库中这个文件 git checkout -- test.txt 删错了,从版本库恢复到工作区 open ~/.ssh Mac 打开存放 ssh 文件夹 git clone -b dev 项目远程http或者ssh链接 clone项目的某个分支到本地,这里我们clone dev分支 git remote 检查一下仓库名称,查看当前配置有哪些远程仓库,在克隆完某个项目后,命令行进到该项目文件夹 git remote,至少可以看到一个名为 origin 的远程库,Git 默认使用这个名字来标识你所克隆的原始仓库 git remote remove origin 删除本地与远程仓库origin的关联 git push origin lhf_dev 本地分支推上去,远程会自动生成同名新分支 lhf_dev git branch --set-upstream-to=origin/lhf_dev 将当前分支与远程某分支关联 git branch -vv 查看关联情况 git pull origin dev 相当于以下两句 git fetch origin dev 代码拉到本地版本库 git merge origin/dev 把本地版本库代码合并到工作区
撤销修改
让这个文件回到最近一次 git commit 或 git add 时的状态
- 当你改乱了工作区某个文件的内容,该文件没有放到暂存区,想直接丢弃工作区的修改时
1
2git checkout -- filename 撤销修改就回到和版本库一模一样的状态
- 当你不但改乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步
1
2
3git reset HEAD <file> 回到场景一,将文件移出了暂存区 git checkout -- filename
- 已经提交了不合适的修改到版本库时,想要撤销本次提交,参考版本回退一节,不过前提是没有推送到远程库。
Git 分支
文章详情:廖雪峰-分支管理
- 每次提交,Git都把它们串成一条时间线,这条时间线就是一个分支。截止到目前,只有一条时间线,在Git里,这个分支叫主分支,即master分支。
- HEAD严格来说不是指向提交,而是指向master,master才是指向提交的,所以,HEAD指向的就是当前分支。不过,在某个时间点,两个平行宇宙合并了,我们就需要处理一些问题了。
- 下图中,每个 C 是一个版本,C2是在C1基础上修改的版本,C4、C5是在C3基础上修改的,C6是C4、C5合并后的版本。
- master:主分支,dev:应用分支,bug:bug分支,v3.0:不同版本的分支
分支的常用指令
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53# 列出所有本地分支 git branch # 列出所有远程分支 git branch -r git branch -a 其中红色是远程分支,黑色是本地分支,绿色是本地当前分支 注意:不要 git checkout origin/new1 切换到远程分支,会造成重大错误,远程分支只用来追踪 只需要 git branch 远程分支名字,git 就会创建对应远程分支的本地分支 git branch --merged 查看已经合并的分支 git branch --no-merged 查看没有合并的分支 # 新建一个分支,但依然停留在当前分支 git branch [branch-name] 相当于基于所在分支创建了新的分支 # 切换到分支 git checkout [branch] git master 仅主分支可以git master 实际上,切换分支这个动作,用switch更科学 git switch master 切换到master Git还会自动提示我们当前master分支比远程的master分支要超前1个提交。 Your branch is ahead of 'origin/master' by 1 commit. (use "git push" to publish your local commits) # 新建一个分支,并切换到该分支 git checkout -b [branch] git switch -c dev # 合并指定分支到当前分支 $ git merge [branch] $ git merge [branch] --no-ff 不使用快转机制 # 删除分支 $ git branch -d [branch-name] 注意:不能删除现在所在的分支lhf-dev,必须切换到其他分支dev,才能删除lhf-dev分支 # 强制删除分支,要删除没有合并的分支 not fully merged $ git branch -D [branch-name] 针对 The branch 'v3' is not fully merged ,if you are sure you want to delete it ,run 'git branch -D v3' # 删除远程分支 $ git push origin --delete [branch-name] $ git branch -dr [remote/branch] # 推送代码和拉取代码 git push 将本地当前分支 推送到 与本地当前分支同名的远程分支上 git pull 将与本地当前分支同名的远程分支 拉取到 本地当前分支上 git push --all 推送本地所有分支到远程同名分支 git pull --all 拉取与本地分支同名的所有远程分支 git push origin dev 将本地 所在分支 推送到远程 dev 分支 git pull origin dev 将远程 dev 分支拉取到本地 所在分支 git push origin <本地分支名>:<远程分支名> 本地指定分支推送到远程指定分支 git pull origin <远程分支名>:<本地分支名> 远程指定分支拉取到本地指定分支
快转机制:
下图中,676d89e和6d065f6版本实际上是相同的,只是将分支做了合并,使用快转机制合并分支时,会出现第一个红色框中的现象;merge 不加参数时,不使用快转机制,就会默认把这两个相同的版本合并,(HEAD ——> master, develop )表示 master 和 develop 分支目前的版本都是 676d89e
分支冲突
- dev分支的新功能代码在合并到master分支时,以master代码为基础,增加dev的新代码
- 当dev分支中,master的基础代码部分,和master分支中代码不一致时,会产生分支冲突
- 手动创建冲突:不同分支修改相同部分(不管修改还是新增),add,commit,切换到要被合并的分支,merge 合并的分支
1
2
3CONFLICT (content): Merge conflict in test.html Automatic merge failed; fix conflicts and then commit the result.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31dev分支 <ul> <li>亚洲</li> <li>欧美</li> <li>日韩</li> <li>直播</li> 冲突代码 <li>商城100%</li> </ul> master分支 <ul> <li>亚洲</li> <li>欧美</li> <li>日韩</li> <li>直播(修复完毕)</li> 冲突代码 </ul> git master git merge dev 合并分支后 <ul> <li>亚洲</li> <li>欧美</li> <li>日韩</li> <<<<<<< HEAD <li>直播(修复完毕)</li> ======= <li>直播</li> <li>商城100%</li> >>>>>>> dev </ul>
分支冲突解决方法:
1
2
3
41. 手动修改,删除需要删除的部分 2. git merge --abort 取消合并
分支管理策略
合并分支时如果可能,Git会用Fast forward模式,但这种模式下,删除分支后,会丢掉分支信息。如果要强制禁用Fast forward模式,Git就会在merge时生成一个新的commit,这样,从分支历史上就可以看出分支信息。
1
2
3
4
5
6
7
8
9
10
11
12git merge --no-ff -m "merge with no-ff" dev --no-ff参数,表示禁用Fast forward $ git log --graph --pretty=oneline --abbrev-commit * e1e9c68 (HEAD -> master) merge with no-ff | | * f52c633 (dev) add merge |/ * cf810e4 conflict fixed ...
不使用快速合并 fast forward,–no-ff 使用普通模式合并
实际工作中,你和你的小伙伴们每个人都在dev分支上干活,每个人都有自己的分支,时不时地往dev分支上合并就可以了
Bug 分支
每个bug都可以通过一个新的临时分支来修复,修复后,合并分支,然后将临时分支删除
当你接到一个修复一个代号101的bug的任务时,很自然地,你想创建一个分支issue-101来修复它,但是,等等,当前正在dev上进行的工作还没有提交:
1
2
3
4
5
6
7
8
9
10
11
12$ git status On branch dev Changes to be committed: (use "git reset HEAD <file>..." to unstage) new file: hello.py Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: readme.txt
并不是你不想提交,而是工作只进行到一半,还没法提交,预计完成还需1天时间。但是,必须在两个小时内修复该bug,怎么办?
1
2
3
4
5$ git stash 可以把当前工作现场“储藏”起来,等以后恢复现场后继续工作 Saved working directory and index state WIP on dev: f52c633 add merge 用git status查看工作区,就是干净的(除非有没有被Git管理的文件),因此可以放心地创建分支来修复bug。
首先确定要在哪个分支上修复bug,假定需要在master分支上修复,就从master创建临时分支
1
2
3
4
5
6
7$ git checkout master Switched to branch 'master' Your branch is ahead of 'origin/master' by 6 commits. (use "git push" to publish your local commits) $ git checkout -b issue-101 Switched to a new branch 'issue-101'
修复完成后,提交
1
2
3
4
5$ git add readme.txt $ git commit -m "fix bug 101" [issue-101 4c805e2] fix bug 101 1 file changed, 1 insertion(+), 1 deletion(-)
切换到master分支,合并,删除bug分支
1
2
3
4
5
6
7
8
9$ git switch master Switched to branch 'master' Your branch is ahead of 'origin/master' by 6 commits. (use "git push" to publish your local commits) $ git merge --no-ff -m "merged bug fix 101" issue-101 Merge made by the 'recursive' strategy. readme.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
回到dev
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19$ git status On branch dev nothing to commit, working tree clean 工作区是干净的,工作现场还在,Git把stash内容存在某个地方了,但是需要恢复一下,使用 git stash pop 恢复工作区,同时删除 stash $ git stash pop On branch dev Changes to be committed: (use "git reset HEAD <file>..." to unstage) new file: hello.py Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: readme.txt Dropped refs/stash@{0} (5d677e2ee266f39ea296182fb2354265b91b3b2a) $ git stash list 查看工作区的 stash list $ git stash apply stash@{0} 恢复到指定 stash
当master分支上修复了bug后,我们要想一想,dev分支是早期从master分支分出来的,所以,这个bug其实在当前dev分支上也存在
1
2
3
4
5
6
7
8
9同样的bug,要在dev上修复 我们只需要把4c805e2 fix bug 101这个提交所做的修改“复制”到dev分支 注意:我们只想复制4c805e2 fix bug 101这个提交所做的修改,并不是把整个master分支merge过来 $ git cherry-pick 4c805e2 [master 1d4b803] fix bug 101 1 file changed, 1 insertion(+), 1 deletion(-) Git自动给dev分支做了一次提交,注意这次提交的commit是1d4b803 它并不同于master的4c805e2,因为这两个commit只是改动相同,但确实是两个不同的commit
Future 分支
开发一个新feature,最好新建一个分支;
如果要丢弃一个没有被合并过的分支,可以通过git branch -D 强行删除。
1
2
3
4
5
6
7$ git branch -d feature-vulcan error: The branch 'feature-vulcan' is not fully merged. If you are sure you want to delete it, run 'git branch -D feature-vulcan'. 使用强行删除 $ git branch -D feature-vulcan Deleted branch feature-vulcan (was 287773e).
多人协作
- master分支是主分支,因此要时刻与远程同步
- dev分支是开发分支,团队所有成员都需要在上面工作,所以也需要与远程同步
- bug分支只用于在本地修复bug,就没必要推到远程了,除非老板要看看你每周到底修复了几个bug
- feature分支是否推到远程,取决于你是否和你的小伙伴合作在上面开发
1
2
3
4
5git remote 查看远程仓库的信息 git remote -v 查看远程仓库的详细信息 git push origin master 推送分支,将分支上所有本地提交推送到远程仓库
1
2
3
4从远程库clone时,默认情况下,你的小伙伴只能看到本地的master分支 要在dev分支上开发,就必须创建远程origin的dev分支到本地 git checkout -b dev origin/dev
工作模式
- 首先,可以试图用git push origin 推送自己的修改;
1
2
3
4
5
6
7
8
9
10$ git push origin dev To github.com:michaelliao/learngit.git ! [rejected] dev -> dev (non-fast-forward) error: failed to push some refs to 'git@github.com:michaelliao/learngit.git' hint: Updates were rejected because the tip of your current branch is behind hint: its remote counterpart. Integrate the remote changes (e.g. hint: 'git pull ...') before pushing again. hint: See the 'Note about fast-forwards' in 'git push --help' for details.
- 如果推送失败,则因为远程分支比你的本地更新,需要先用git pull试图合并;
1
2
3
4
5
6
7
8$ git pull There is no tracking information for the current branch. Please specify which branch you want to merge with. See git-pull(1) for details. git pull <remote> <branch> If you wish to set tracking information for this branch you can do so with: git branch --set-upstream-to=origin/<branch> dev
- 如果合并有冲突,则解决冲突,并在本地提交;
1
2
3CONFLICT (add/add): Merge conflict in env.txt Automatic merge failed; fix conflicts and then commit the result.
- 没有冲突或者解决掉冲突后,再用git push origin 推送就能成功!
1
2
3
4
5
6
7
8
9
10$ git push origin dev Counting objects: 6, done. Delta compression using up to 4 threads. Compressing objects: 100% (4/4), done. Writing objects: 100% (6/6), 621 bytes | 621.00 KiB/s, done. Total 6 (delta 0), reused 0 (delta 0) To github.com:michaelliao/learngit.git 7a5e5dd..57c53ab dev -> dev
- 如果git pull提示no tracking information,则说明本地分支和远程分支的链接关系没有创建
1
2
3$ git branch --set-upstream-to=origin/dev dev Branch 'dev' set up to track remote branch 'dev' from 'origin'.
tags 标签管理
发布一个版本时,我们通常先在版本库中打一个标签(tag),这样,就唯一确定了打标签时刻的版本。将来无论什么时候,取某个标签的版本,就是把那个打标签的时刻的历史版本取出来。所以,标签也是版本库的一个快照。其实它就是指向某个commit的指针(跟分支很像对不对?但是分支可以移动,标签不能移动),相当于对commit之后的hash值做的简写
“请把上周一的那个版本打包发布,commit号是6a5819e…”
“请把上周一的那个版本打包发布,版本号是v1.2,按照tag v1.2查找commit”
新建标签
1
2
3
4
5
6
7
8
9
101. checkout 到需要打标签的分支上 2. 打标签 git tag v1.0 相当于为HEAD打标签 git tag -a v1 -m "第一版" -a:接版本名称 -m:描述 git tag v0.9 要打的标签的版本id 周一提交的内容忘记打标签了,周五 git log 找到周一提交的commit id 然后
1
2
3
4
5git tag 查看所有标签 git show v0.9 查看标签信息
操作标签
1
2
3
4
5
6
7
8
9
10git tag -d v0.1 删除标签,创建的标签都只存储在本地,不会自动推送到远程。所以,打错的标签可以在本地安全删除 git push origin v1.0 推送标签到远程 git push origin --tags 一次性推送全部尚未推送到远程的本地标签 如果本地标签已经推送到远程,先删本地,再删远程 git tag -d v0.9 git push origin :refs/tags/v0.9 将本地标签提交到github git push origin --tags
Github
Github 准备:SSH配置
- 创建SSH Key。在用户主目录下,看看有没有.ssh目录,如果有,再看看这个目录下有没有id_rsa和id_rsa.pub这两个文件,如果已经有了,可直接跳到下一步。如果没有,打开Shell(Windows下打开Git Bash),创建SSH Key:
1
2
3
4ssh-keygen -t rsa -C "youremail@example.com" 一路回车,使用默认值即可,由于这个Key也不是用于军事目的,所以也无需设置密码 如果一切顺利的话,用户主目录里找到.ssh目录,里面有id_rsa和id_rsa.pub两个文件,这两个就是SSH Key的秘钥对,id_rsa是私钥,不能泄露出去,id_rsa.pub是公钥,可以放心地告诉任何人
- 登陆GitHub,点击头像,Settings,SSH and GPG keys,New SSH key
为什么GitHub需要SSH Key呢?
因为GitHub需要识别出你推送的提交确实是你推送的,而不是别人冒充的,而Git支持SSH协议,所以,GitHub只要知道了你的公钥,就可以确认只有你自己才能推送。
当然,GitHub允许你添加多个Key。假定你有若干电脑,你一会儿在公司提交,一会儿在家里提交,只要把每台电脑的Key都添加到GitHub,就可以在每台电脑上往GitHub推送了。
Github 创建仓库
- 根据GitHub的提示,在本地的learngit仓库下运行命令
1
2
3git remote add origin https://github.com/dddmmiao/winwork.git 关联一个远程库时必须给远程库指定一个名字,origin是默认习惯命名
- 添加后,远程库的名字就是origin,这是Git默认的叫法,也可以改成别的,但是origin这个名字一看就知道是远程库。下一步,就可以把本地库的所有内容 git push 到远程库上
1
2
3
4
5git push -u origin master 实际上是把当前分支master推送到远程 我们第一次推送master分支时,加上了-u参数,Git不但会把本地的master分支内容推送的远程新的master分支,还会把本地的master分支和远程的master分支关联起来,在以后的推送或者拉取时就可以简化命令 之后只要本地作了提交,就可以通过以下命令push git push origin master
开发基本流程
第一个开发者上传代码到仓库
1
2
3
4
5
61. 给远程仓库起别名: git remote add 别名origin 远程仓库地址 2. 向远程仓库推送代码: git push -u origin 分支master/dev -u表示指定 origin 默认提交到 master 分支 向远程仓库推送代码: git push origin dev 手动将 origin 提交到 dev 分支
员工A第一次获取仓库代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
151. 克隆远程仓库代码: git clone 远程仓库地址 (内部已经实现了git remote add origin 远程仓库地址) git clone 和 直接下载项目zip的区别: 直接下载项目zip只会把项目下载下来,分支,提交记录,.git文件都不会被下载,项目需要重新git init,重新初始化再push到远程仓库就会导致项目所有内容提交人变成你 git clone 得到的项目是包含.git文件的,分支等提交记录都会存在 2. git status 查看文件,有必要的话,设置 .gitignore 忽略上传内容 3. 查看分支,切换到dev分支(要基于谁创建分支就先切换到哪个分支) 4. 基于dev分支新建并切换到自己的分支: git checkout -b 分支 (clone 时会把所有的分支clone,git branch只能看到master分支,git branch -a 查看本地和远程所有分支) 5. 开发功能 6. git status 查看修改的文件,add,commit 提交本地版本 7. git log 查看提交记录 8. 测试通过后,切换到基于开发的分支,比如dev,merge 自己的分支 9. 推送本地dev分支到远程dev分支,推送自己本地分支到自己远程分支
员工A下班了,员工B开发了功能并push,员工A第二天继续开发
1
2
3
4
5
6
7
8
9
10
111. 切换到 dev 分支,git status 查看一下提交记录,将远程最新的dev分支拉取到本地dev分支 2. 切换到自己分支 3. git pull origin dev 将员工B已经合并到的远程dev分支拉到自己的分支 4. 如果自己的本地分支超前自己的远程分支,先在自己分支 git push 或者 git push origin lhf-dev ,更新自己的远程分支 5. 开发功能 6. git status 查看修改的文件,add,commit 提交本地版本 7. git log 查看提交记录 8. 测试通过后,切换到基于开发的分支,比如dev,merge 自己的分支 9. 推送本地dev分支到远程dev分支,推送自己本地分支到自己远程分支
开发完毕,上线
1
2
3
4
5
6
7
8
91. 将dev分支合并到master,推送到仓库,进行上线 git checkout master git merge dev git push origin master 2. 把dev分支也推送到仓库 git checkout dev git merge master git push origin dev
主仓库作为服务器
- github 上创建一个远程仓库,作为服务器的仓库名字要求:owner名字.github.io
- 直接clone 或者创建本地仓库,关联远程仓库
1
2
3git remote add origin 远程仓库链接 git push -u origin master
- 复制远程仓库名字 owner名字.github.io 到浏览器,可以直接访问项目
仓库迁移
新建一个远程仓库,将旧仓库内容迁移到新仓库
忽略提交
- clone 拿到一个项目,或者本地项目要push到远程仓库了,先 git status 检查项目中有没有不该提交的内容,比如下图中 .DS_Store 和 .vscode/ 不需要提交到远程
- 编辑 .gitignore 文件,忽略它们的提交
- 再次 git status 检查设置忽略的内容,是否不再出现在待提交区
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28有些时候,你必须把某些文件放到Git工作目录中,但又不能提交它们 在Git工作区的根目录下创建一个特殊的.gitignore文件,然后把要忽略的文件名填进去,Git就会自动忽略这些文件 Github 提供了一些配置文件 https://github.com/github/gitignore 有些时候,你想添加一个文件到Git,但发现添加不了,原因是这个文件被.gitignore忽略了 git add App.class The following paths are ignored by one of your .gitignore files: App.class Use -f if you really want to add them. git add -f App.class 强制添加文件到Git git check-ignore -v App.class 检查忽略规则 .gitignore:3:*.class App.class 表示.gitignore的第3行规则忽略了该文件 此时我们这样写.gitignore .* # 排除所有.开头的隐藏文件 *.class # 排除所有.class文件 /node_moudles #忽略node_moudles文件夹 App.class #忽略App.class文件 # 不排除.gitignore和App.class: !.gitignore !App.class 如果已commit了App.class文件,再在.gitignore文件中设置忽略是不会生效的,首先要执行以下命令将App.class从缓存中踢出来 git rm -r --cached App.class git rm -r --cached . 将所有文件从暂存区踢出
补充
本地仓库关联多个远程仓库
本地仓库要关联到远程仓库才可以实现,本地仓库的 pull 和 push
1
2
3
4
5
6
7git remote add 别名origin 远程仓库地址 此后就可以使用pull和push了 如果报错:fatal: remote origin already exists,说明本地仓库已经关联了一个远程仓库 git remote -v 先查看关联 git remote rm origin 删除已有的远程仓库联接 重新关联
git本身是分布式版本控制系统,可以同步到另外一个远程库,当然也可以同步到另外两个远程库。使用多个远程库时,我们要注意,git给远程库起的默认名称是origin,如果有多个远程库,我们需要用不同的名称来标识不同的远程库
1
2
3
4
5
6
7
8
9
10
11
12git remote add github github远程仓库地址 git remote add gitee gitee远程仓库地址 查看远程库信息,可以看到两个远程库 git remote -v gitee git@gitee.com:liaoxuefeng/learngit.git (fetch) gitee git@gitee.com:liaoxuefeng/learngit.git (push) github git@github.com:michaelliao/learngit.git (fetch) github git@github.com:michaelliao/learngit.git (push) git push github master 推送到github远程仓库 git push gitee master 推送到gitee远程仓库
rebase应用场景
rebase:使git记录变得简洁,可以把本地未push的分叉提交历史整理成直线,目的是使得我们在查看历史提交的变化时更容易,因为分叉的提交需要三方对比
多个提交记录合并成一个提交记录
注意:合并的记录尽量都是没有 push 过的记录,不要和已 push 到仓库的记录合并,否则容易产生记录冲突
提交记录如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22didideMacBook-Pro-150:rebase didi$ git log commit a47723a609a1b1a53690935e3827ae87ba77e37f (HEAD -> master) Author: [lvhaifeng] <[lvhaifeng_i@didiglobal.com]> Date: Sun Jul 11 16:50:33 2021 +0800 v4 commit 1f8839b441c0430171f9d8b92e54deabdc6caf3d Author: [lvhaifeng] <[lvhaifeng_i@didiglobal.com]> Date: Sun Jul 11 16:50:07 2021 +0800 v3 commit c52cad893700e7ab059ba2bc8d4eb7ef38a32494 Author: [lvhaifeng] <[lvhaifeng_i@didiglobal.com]> Date: Sun Jul 11 16:49:46 2021 +0800 v2 commit ebd96a682bb849350134cffb0cca4589c5670c65 Author: [lvhaifeng] <[lvhaifeng_i@didiglobal.com]> Date: Sun Jul 11 16:48:38 2021 +0800 v1
第一步
1
2
3
4
5git rebase -i v2的版本号 将v4,v3,v2 合并 git rebase -i HEAD~3 从当前记录开始,将最近的三条记录合并
显示信息
1
2
3
4
5didideMacBook-Pro-150:rebase didi$ git rebase -i HEAD~3 pick c52cad8 v2 pick 1f8839b v3 pick a47723a v4
第二步
将 pick 改成 s ,s 表示 squash ,将当前版本合并到上一个版本
1
2
3
4
5didideMacBook-Pro-150:rebase didi$ git rebase -i HEAD~3 pick c52cad8 v2 s 1f8839b v3 s a47723a v4
:wq 退出
1
2
3
4
5
6
7
8
9
10# This is a combination of 3 commits. # This is the 1st commit message: v2 # This is the commit message #2: v3 # This is the commit message #3: v4 # Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit.
第三步
自定义合并的提交信息
1
2
3
4
5
6# This is a combination of 3 commits. # This is the 1st commit message: v2 & v3 & v4 # Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit.
:wq 退出
1
2
3
4
5
6
7
8
9
10
11
12didideMacBook-Pro-150:rebase didi$ git log commit d09c4c545bc4aa529b82f86f394ffa44c295e8ff (HEAD -> master) Author: [lvhaifeng] <[lvhaifeng_i@didiglobal.com]> Date: Sun Jul 11 16:49:46 2021 +0800 v2 & v3 & v4 commit ebd96a682bb849350134cffb0cca4589c5670c65 Author: [lvhaifeng] <[lvhaifeng_i@didiglobal.com]> Date: Sun Jul 11 16:48:38 2021 +0800 v1
dev 记录合并到 master 记录
操作步骤见:7小时学会Git 基础全套完整教程(从入门到精通)
合并分叉
公司提交到本地,从家开发别的功能提交到仓库,回到公司从云端拉代码,如果 dev 相对 master 的基代码不同,会产生分叉
解决办法如下
1
2
3
4
5回到公司从仓库拉代码时 git pull origin dev 改为下面两行代码 git fetch origin dev 代码拉到本地版本库 git rebase origin/dev
合并成一条直线
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16$ git log --graph --pretty=oneline --abbrev-commit * 582d922 (HEAD -> master) add author * 8875536 add comment * d1be385 (origin/master) init hello * e5e69f1 Merge branch 'dev' | | * 57c53ab (origin/dev, dev) fix env conflict | | | | * 7a5e5dd add env | * | 7bd91f1 add new env ... 注意到Git用(HEAD -> master)和(origin/master)标识出当前分支的HEAD和远程origin的位置分别是582d922 add author和d1be385 init hello 本地分支比远程分支快两个提交
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51$ git push origin master To github.com:michaelliao/learngit.git ! [rejected] master -> master (fetch first) error: failed to push some refs to 'git@github.com:michaelliao/learngit.git' hint: Updates were rejected because the remote contains work that you do hint: not have locally. This is usually caused by another repository pushing 尝试 git push origin dev 本地分支失败说明 本地版本不是最新的了,有人先于我们push了dev 不是最新版本,先 git pull 获取最新版本 $ git pull remote: Counting objects: 3, done. remote: Compressing objects: 100% (1/1), done. remote: Total 3 (delta 1), reused 3 (delta 1), pack-reused 0 Unpacking objects: 100% (3/3), done. From github.com:michaelliao/learngit d1be385..f005ed4 master -> origin/master * [new tag] v1.0 -> v1.0 Auto-merging hello.py Merge made by the 'recursive' strategy. hello.py | 1 + 1 file changed, 1 insertion(+) 下面可以发现提交历史分叉了,push到远程后不美观 $ git log --graph --pretty=oneline --abbrev-commit * e0ea545 (HEAD -> master) Merge branch 'master' of github.com:michaelliao/learngit | | * f005ed4 (origin/master) set exit=1 * | 582d922 add author * | 8875536 add comment |/ * d1be385 init hello ... $ git rebase 将本地提交历史合并成一条直线 $ git log --graph --pretty=oneline --abbrev-commit * 7e61ed4 (HEAD -> master) add author * 3611cfe add comment * f005ed4 (origin/master) set exit=1 * d1be385 init hello ... $ git push origin master 再推送到远程分支后,远程分支也就是一条直线了 $ git log --graph --pretty=oneline --abbrev-commit * 7e61ed4 (HEAD -> master, origin/master) add author * 3611cfe add comment * f005ed4 set exit=1 * d1be385 init hello ...
快速解决冲突
使用工具 beyond compare
1
2
3
4
5
6
7
8
9
10
111. 安装beyond compare 2. 在 git 中配置 3. 执行以下指令 git config --local merge.tool bc3 --local 表示只对当前本地仓库项目有效 git config --local mergetool.path '/usr/local/bin/bcomp' git config --local mergetool.keepBackup false 4. 输入命令打开beyond compare git mergetool 5. 会自动打开beyond compare ,在工具中进行冲突处理并保存
多人协同开发
git flow工作流
创建初始项目和版本
- github创建组织 new organization
- 添加成员
- 创建仓库 create repository
其他
免密码登录
解决每次 pull push 都要输入密码的问题
- git 有自动管理凭证,实际上只需要输入一次,git就会把用户密码保存下来,mac系统中是在【钥匙串访问】应用中可以查到
- url 中实现
1
2
3
4使用以下方式关联远程仓库 git remote add origin https://用户名:密码@github.com/..... git push origin master
- SSH实现
1
2
3
4
5
6
7
81. 生成公钥和私钥(默认放在 ~/.ssh 目录下,id_rsa.pub 公钥,id_rsa 私钥) ssh-keygen 2. 拷贝公钥的内容,设置到GitHub中 3. 在git本地配置ssh地址 git remote add origin git@github.com...... 4. 设置之后再push就可以 git push origin master
自定义 Git
1
2
3git config --global color.ui true 设置Git显示颜色
问题
- 在lhf-dev分支下修改了index.html,此时checkout到其他分支,比如master分支,此时 git status 查看,会发现master分支中index.html也会发生修改,lhf-dev 分支修改完,切换到master分支时,会出现以下代码。想要避免这种情况就,修改完后,先add,commit 再切换到其他分支
1
2M index.html
- push或者pull时保存:Could not read from remote repository,使用 git remote -u 设置关联远程仓库
1
2
3
4
5
6
7和远程仓库的管理有问题,先查看远程仓库关联情况 git remote -v origin /Users/wupeiqi/Desktop/git_project/realDemo.git (fetch) origin /Users/wupeiqi/Desktop/git_project/realDemo.git (push) 表示现在关联的还是本地仓库 git remote set-url origin 远程仓库地址
- 不小心删除了master分支,怎么办?
1
2
3
4
5
6
7
8
9
10强制删除master分支时,会给一个master的版本号 $ git branch -D master Deleted branch master (was 8d78fc9). $ git branch * (HEAD detached at 8d78fc9) dev lhf-dev 根据提供的master版本号,直接创建一个名称为master,带版本号的分支,就能恢复master分支 $ git branch master 8d78fc9
- commit 后忘记 push 了
在公司开发commit到公司电脑本地忘记push到仓库了,于是在家开发了一些其他的功能之后 push ,到公司后 git pull origin dev 会出现分支冲突;手动解决分支冲突,继续开发push
1
2
3CONFLICT (content): Merge conflict in test.html Automatic merge failed; fix conflicts and then commit the result.
- git clone 和 git clone --no-checkout
项目内容很多,如果不想默认checkout到master分支,就可以 --no-checkout 再切换到想要的分支,相比 git clone 再切换分支,速度会快
1
2
3
4
5
6git clone 项目链接 clone到本地后会自动 checkout 到master分支,本地项目中的内容显示的都是master分支中的内容 git clone --no-checkout 项目链接 不会自动切换到master分支,本地项目中没有内容,只要checkout到指定的分支,就可以拿到指定分支的内容 实际上是切换到了一个删除的分支,git status 查看可以看到内容前面的 deleted:
-
Please supply the message using either -m or -F option
复制代码1
2
3
4
5
6在mac中,如果使用 git commit –amend,会出现如下错误: error: There was a problem with the editor ‘vi’. Please supply the message using either -m or -F option. 原因是vi有问题,需要为 git 换一个默认的编辑器,比如 vim,如下进行配置即正常了。 git config --global core.editor "vim"
注意
git commit 最新的代码后,git pull 不会把commit的代码覆盖,只会把git commit 之前的代码更新
分支名称的命名规则
面试题
- 紧急修复线上bug?
创建一个新的分支,对bug进行修复,随后合并到主分支
- 你们公司是怎么做多人协同开发?
每人一个自己的分支,每个人在自己的分支上写代码
最后
以上就是落后白羊最近收集整理的关于Git 基础学习版本控制GitGit 分支tags 标签管理Github本地仓库关联多个远程仓库rebase应用场景快速解决冲突多人协同开发其他面试题的全部内容,更多相关Git内容请搜索靠谱客的其他文章。
发表评论 取消回复