//目录

【持续更新】Git 使用

几个有用的资料:

git在线练习网站:https://learngitbranching.js.org/?locale=zh_CN

参考书:https://www.runoob.com/git/git-branch.html

 

几个常见问题:

1. 从父节点中merge(合并)子节点,会有fast farword,即使子节点改了父节点里面的文件,也不会产生冲突,直接(快速)合并,也就是改了HEAD的值。

2. merge 和 rebase的区别:rebase main 会依次以main节点作为参考,依次将自身的commit放在rebase后面,分支不会成环(拉直时间线——很多资料里面这么写)。

 

基础篇

1.1 git commit 提交。

(注意的是:提交记录是对上一版本进行对比,修改内容作为一个提交,大部分节点都有负节点)

git commit 
git commit

1.2 git branch 分支。

分支其实就是指向某一个提交,意思就是说我想基于这个commit(提交)及其所有父节点进行新的工作。(及时创建再多的分支也不会有内存和存储的开销)

# 方法一
# 创建新的分支可以用
git branch bugFix
# 然后切换至新的分支
git checkout bugFix


# 方法二
git checkout -b bugFix

1.3 git merge 合并分支。

就是将分支合并起来,对应的场景就是,将新开发的一个功能的分支,合并到主线上来。会产生一个新的特殊节点,他有两个父节点。

值得注意的是,git merge 分支1的时候,如果分支1继承至当前节点,不会产生新的节点,而只是移动一下当前分支至分支1。就是前面写的常见问题1.

git checkout -b bugFix
git commit 
git checkout main
git commit
git merge bugFix

 1.4 git rebase 合并分支。

例如:git rebase 分支1,就是将当前分支以分支1为基,将当前分支的提交取出来,放到分支1的后面(当然,公共提交就不用提出来了)。

值得注意的是,如果分支1是继承至当前节点,同git merge一样是移动一下当前分支至分支1。

git checkout -b bugFix
git commit
git checkout main
git commit
git checkout bugFix
git rebase main

高级篇

2.1 分离HEAD

HEAD是什么:当前活跃分支的游标。也就是你正在其基础上进行修改的某个commit提交。HEAD默认为当前分支最近的一次提交(最新的提交显示为分支名?)。但是是可以修改HEAD的。

本题目分离的HEAD,就是让HEAD不指向分支名,而是指向某个具体的commit。可以通过git checkout C1来进行(C1其实就是每次commit的hash值)。

git checkout C4

2.2 相对引用^

可以通过直接的hash值来移动HEAD,但是要通过git log去查看hash。还可以通过相对引用的方式设置HEAD。

方法一: HEAD^

方法二: HEAD~n

# 方法一,通过hash
git checkout C3

# 方法二,通过相对引用
git checkout bugFix
git checkout HEAD^1

# 方法三
git checkout C4^

2.3 相对引用~

使用相对引用最多的地方在于移动分支,就是强制将分支指向某个提交,例如:git branch -f main HEAD~3 

git branch -f main C6
git branch -f bugFix C0
git checkout HEAD~1

2.4 撤销变更(撤销提交)

撤销变更有两种类型,一个是撤销暂存区的提交,一个是撤销commit。这里是对commit进行撤销。使用git reset指令。例如git reset HEAD~1。

本地仓库就不知道最新的一个提交了,但是这个提交还在,处在未加入暂存区的状态。

除了reset之外,还有revert。reset只能撤回本地的commit,而远程的无法撤销,需要使用git revert HEAD~1,然后git push上去。

git reset HEAD~1
git checkout pushed
git revert HEAD

 注意的是不同与reset,revert会产生一个新的提交,和当前的上一次提交相同。然后没有没有~1,而是直接是HEAD。

移动提交记录

3.1 git cherry-pick 

自由修改提交树(主要是处理复杂工作流,什么把某个提交放在XXX的前面,某个提交放到后面)

命令形式: git cherry-pick <提交号>...

# 将C3, C4, C7提交复制到当前HEAD的下面。
git cherry-pick C3 C4 C7

 3.2 交互式rebase

使用-i参数。如果知道所需要的提交commit的hash值,可以优先选择git cherry-pick commit_id。但是如果不知道,可以用rebase -i 交互式合并分支。

主要有三个功能。

(1)调整commit的顺序

(2)删除不想要的commit (切换pick的状态)

  (3) 合并提交。(打包)

例如:git rebase -i HEAD~4,以当前HEAD的父节点*4为基,后面的commit进行修改。

git rebase -i overHere

杂项 

4.1 只取一个提交

场景:

来看一个在开发中经常会遇到的情况:我正在解决某个特别棘手的 Bug,为了便于调试而在代码中添加了一些调试命令并向控制台打印了一些信息。

这些调试和打印语句都在它们各自的提交记录里。最后我终于找到了造成这个 Bug 的根本原因,解决掉以后觉得沾沾自喜!

最后就差把 bugFix 分支里的工作合并回 main 分支了。你可以选择通过 fast-forward 快速合并到 main 分支上,但这样的话 main 分支就会包含我这些调试语句了。你肯定不想这样,应该还有更好的方式……

方案:

只选择那个解决bug的提交即可。

(1) git rebase -i

(2) git cherry-pick

git checkout main
git cherry-pick C4

4.2 提交技巧1

场景:

接下来这种情况也是很常见的:你之前在 newImage 分支上进行了一次提交,然后又基于它创建了 caption 分支,然后又提交了一次。

此时你想对某个以前的提交记录进行一些小小的调整。比如设计师想修改一下 newImage 中图片的分辨率,尽管那个提交记录并不是最新的了。

方案:

我们可以通过下面的方法来克服困难:

  • 先用 git rebase -i 将提交重新排序,然后把我们想要修改的提交记录挪到最前
  • 然后用 git commit --amend 来进行一些小修改
  • 接着再用 git rebase -i 来将他们调回原来的顺序
  • 最后我们把 main 移到修改的最前端(用你自己喜欢的方法),就大功告成啦!
# 使用rebase -i 调整顺序,将newImage放下来
git rebase -i main
git commit --amend
# 再使用rebase -i 将C3放下来
git rebase -i main
# 移动main
git checkout main
git rebase caption // 或者merge都行。都是fast forward

 

4.3 提交技巧2

改用cherry-pick来实现。

git checkout main
git cherry-pick C2
git commit --amend
git cherry-pick C3

 

4.4 git tag

相信通过前面课程的学习你已经发现了:分支很容易被人为移动,并且当有新的提交时,它也会移动。分支很容易被改变,大部分分支还只是临时的,并且还一直在变。

分支很容易改变,可以通过commit改变分支的默认HEAD,可以通过checkout 改变HEAD的指向,还可以git branch -f main C1强制将分支移动到某个commit。

有没有什么可以永远指向某个提交记录的标识呢,比如软件发布新的大版本,或者是修正一些重要的 Bug 或是增加了某些新特性,有没有比分支更好的可以永远指向这些提交的方法呢?

 

Git 的 tag 就是干这个用的啊,它们可以(在某种程度上 —— 因为标签可以被删除后重新在另外一个位置创建同名的标签)永久地将某个特定的提交命名为里程碑,然后就可以像分支一样引用了。

更难得的是,它们并不会随着新的提交而移动。你也不能切换到某个标签上面进行修改提交,它就像是提交树上的一个锚点,标识了某个特定的位置。

例子:git tag v1 C1

git tag v0 C1
git tag v1 C2
git checkout C2

 

4.5 git describe

描述当前位置与最近的tag之间的信息,比如多少个commit等。

例子:git describe <ref>

输出:它输出的结果是这样的: <tag>_<numCommits>_g<hash>

 

高级话题

 

远程

6.1 git clone

git clone 

 

6.2 远程分支

远程分支反应了远程仓库(在上一次与它通信时)的状态。远程分支自动分离HEAD,不能直接操作o/<分支名>。而是在别的地方完成后,(更新了远程分支之后)再用远程分享你的工作成果。

如果你主动切到o/<main>进行提交,o/main也不会更新。只有远程仓库中相应的分支更新了以后才会更新。

git commit
# 注意,远程分支并不会更新,此做法是不对的,只是为了演示
git checkout o/main
git commit

 

6.3 git fetch

git fetch 完成了仅有的但是很重要的两步:

  • 从远程仓库下载本地仓库中缺失的提交记录
  • 更新远程分支指针(如 o/main)

即git fetch将本地仓库的远程分支更新至远程仓库的最新状态。

git fetch

 

6.4 git pull 

git pull 等于 git fetch, 再加上git merge

# fetch之后可以执行
git cherry-pick o/main
git merge o/main
git rebase o/main

# 也可以直接执行
git pull

 

6.5 模拟团队合作

git clone
git fakeTeamwork main 2
git commit
git pull

 

6.6 git push

将本地提交提交至远程服务器,然后更新本地的远程仓库提交记录。

git commit
git commit
git push

 

6.7 偏离的提交记录

场景:

假设你周一克隆了一个仓库,然后开始研发某个新功能。到周五时,你新功能开发测试完毕,可以发布了。但是 —— 天啊!你的同事这周写了一堆代码,还改了许多你的功能中使用的 API,这些变动会导致你新开发的功能变得不可用。但是他们已经将那些提交推送到远程仓库了,因此你的工作就变成了基于项目旧版的代码,与远程仓库最新的代码不匹配了。

这种情况下, git push 就不知道该如何操作了。如果你执行 git push,Git 应该让远程仓库回到星期一那天的状态吗?还是直接在新代码的基础上添加你的代码,亦或由于你的提交已经过时而直接忽略你的提交?

因为这情况(历史偏离)有许多的不确定性,Git 是不会允许你 push 变更的。实际上它会强制你先合并远程最新的代码,然后才能分享你的工作。

解决方案:

先合并远程最新的commit,(如果有冲突解决冲突),然后push上去。

推荐采用rebase的方案进行合并。

git pull --rebase # 如果不采用--rebase参数,则是merge方案合并。

git push

当然可以将pull分开写,先fetch,然后直接merge,或者rebase。最后push 

git fetch
git rebase o/main
git push

git fetch
git merge o/main
git push

// 同 git fetch; git rebase o/main; git push
git pull --rebase
git push

// 同 git fetch; git merge o/main; git push
git pull
git push

 

git clone
git fakeTeamwork main 1
git commit 
git pull --rebase
git push

6.8 锁定Main

Main分支被锁定了,只能通过切一个自己的分支,提交commit后,申请PR。

git checkout -b feature
git push
git checkout main
git reset HEAD~1
git checkout feature

 

远程高级 

7.1 推送主分支

将三个特性分支集成至主分支,然后做一次push。

注意的是,slid1, slid2, slid3都没有设置上游分支。是不能在side1/2/3上进行pull的,除非名字相同。

git checkout side2
git rebase side1

git checkout side3
git rebase side2

git checkout main
git merge side3

git pull --rebase
git push

我写的比较复杂版本:

思路先pull最新的,然后一次把特性分支进行rebase合并。

git checkout main
git pull
git checkout side1
git rebase main
git checkout side2
git rebase side1
git checkout side3
git rebase side2
git checkout main
git rebase side3
git push

 

7.2 合并远程分支

改用merge的方式来推送主分支,rebase可以让提交树更加简洁,但是merge保留了完整历史。

值得注意的是,如果想通过此关,需要注意目标的需要,从谁merge谁,先merge谁

git checkout main
git pull
git merge side1
git merge side2
git merge side3
git push

 

7.3 远程跟踪

main 和 o/main如何关联起来的,其实就是通过远程跟踪属性设置的。git clone 的时候,自动给你设置好了远程上游。

有两种方式设置次属性。

(1) git checkout -b totallyNotMain o/main, 切出分支的时候指定。

(2) git branch -u o/main foo,设置foo的远程跟踪为o/main,如果当前就在foo,那么可以省略foo分支名

git checkout -b side
git branh -u o/main
git commit
git pull --rebase
git push
git checkout -b side o/main
git commit
git pull --rebase
git push

 

7.4 git push 的参数

当HEAD不在分支名上,git push 不带参数是无法进行push当前分支的,除非带上 orgin 分支名。

# 值得注意的是place参数,如果只有一个,说明本地和远程同名
# 另外,如果 head 指针不在分支名上,而是某一个commit上,直接用git push 是找不到对应的远程分支很本地分支的,得用 git push <origin> <place>的形式。
git push origin main git push origin foo

 

7.5 git push 的参数2

如果本地和远程分支不同名字,则命令格式为:git push origin <本地分支>:<远程分支>。如果远程分支不存在,则会在远程新建一个分支。

git push origin main^:foo
git push origin foo:main

 

7.6 git fetch 的参数

# git fetch origin <远程分支>:<本地分支>
git fetch foo:main
git fetch origin main^1:foo
git checkout foo
git merge main

 

7.7 没有source 的source

git push/fetch 的省略参数

git push origin : foo # 删除掉远程foo分支
git fetch origin : bar # 在本地创建一个bar的新分支

 

7.8 git pull 的参数

git pull origin bar:foo
git pull origin main:side

 

posted @ 2022-01-03 21:11  小草的大树梦  阅读(148)  评论(0编辑  收藏  举报