Git从原理到实践

背景

如果有一个软件,不但能自动帮我记录每次文件的改动,还可以让同事协作编辑,这样就不用自己管理一堆类似的文件了,也不需要把文件传来传去。如果想查看某次改动,只需要在软件里瞄一眼就可以,岂不是很方便?

这个软件用起来就应该像这个样子,能记录每次文件的改动:

版本 用户 说明 日期
1 张三 删除了软件服务条款5 7/12 10:38
2 张三 增加了License人数限制 7/12 18:09
3 李四 财务部门调整了合同金额 7/13 9:51
4 张三 延长了免费升级周期 7/14 15:17

那么它就是Git

到了 2002 年,Linux 系统已经发展了十年了,代码库之大让 Linus 很难继续通过手工方式管理了,社区的弟兄们也对这种方式表达了强烈不满,于是 Linus 选择了一个商业的版本控制系统 BitKeeper,BitKeeper 的东家 BitMover 公司出于人道主义精神,授权 Linux 社区免费使用这个版本控制系统。

安定团结的大好局面在 2005 年就被打破了,原因是 Linux 社区牛人聚集,不免沾染了一些梁山好汉的江湖习气。开发 Samba 的 Andrew 试图破解 BitKeeper 的协议(这么干的其实也不只他一个),被 BitMover 公司发现了(监控工作做得不错!),于是 BitMover 公司怒了,要收回 Linux 社区的免费使用权。

Linus 可以向 BitMover 公司道个歉,保证以后严格管教弟兄们,嗯,这是不可能的。实际情况是这样的:

Linus 花了两周时间自己用 C 写了一个分布式版本控制系统,这就是 Git!一个月之内,Linux 系统的源码已经由 Git 管理了!牛是怎么定义的呢?大家可以体会一下。

常用命令

git init 初始化仓库

git add <file> 添加文件到缓冲区,git add .添加所有文件到缓冲区(Stage)

git commit 提交代码到本地版本库,此时会生成版本的提交hash

image-20240602080854175

git status 查看状态,可以查看当前所处的分支,当前的修改内容和提交到版本库里的内容

image-20240602081304965

git diff 可以让你看到你更改的内容

image-20240602091847345

git log 查看commit的版本,参数--pretty=oneline会只显示版本和commit

image-20240602091859951

git rest --hard hash or head^会进行版本号的回退可以选用版本的hash值或者HEAD^代表回退上一版本,

git reflog 记录你的版本操作,

git checkout -- file 可以丢弃工作区的修改。把 readme.txt 文件在工作区的修改全部撤销,这里有两种情况:

  • 一种是 readme.txt 自修改后还没有被放到暂存区,现在,撤销修改就回到和版本库一模一样的状态;
  • 一种是 readme.txt 已经添加到暂存区后,又作了修改,现在,撤销修改就回到添加到暂存区后的状态。

git checkout file不加横杠就是创建分支

git reset HEAD file 撤销存储在缓冲区里的内容,HEAD 代表最新版本,也可以使用hash。 unstage是撤销缓存区中的内容

image-20240602093448608

将你本地删除的文件在git上也删除,然后再提交,现在,文件就从版本库中被删除了。

1
2
git rm test.txt
git commit -m "remove test.txt"

git checkout -- test.txt 是用版本库里的版本替换工作区的版本,无论工作区是修改还是删除,都可以“一键还原”。

远程库需要把一个已有的本地仓库与之关联,然后,把本地仓库的内容推送到 GitHub 仓库。

1
git remote add origin git@github.com:michaelliao/learngit.git

git push -u origin main把本地库的所有内容推送到远程库上,-u参数,Git 不但会把本地的 master 分支内容推送的远程新的 master 分支,还会把本地的 master 分支和远程的 master 分支关联起来

创建分支就是创建了一个指针,他可以独立于master进行提交和更改

img

1
2
3
4
git checkout -b dev #创建并切换分支
#等于下边两条指令
git branch dev #创建分支
git checkout dev #切换分支

git merge dev 可以将你对分支的提交的修改提交到master上,这里的dev是把dev合并到当前分支,当你同时修改一个文件时才会冲突,修改两个文件不会冲突。然后再提交到缓冲区,在提交版本

image-20240602100519014

git branch -d dev 删除分支

git branch 查看分支

当出现如下修改时不能自动的合并分支

img

image-20240602102003297

需要我们去手动解决冲突后再提交。git status 也可以告诉我们冲突的文件:

可以看到合并的过程

1
git log --graph --pretty=oneline --abbrev-commit

image-20240602142812506

git merge --no-ff -m "merge with no-ff" dev会以提交的方式合并分支

git stash 可以保存缓冲区的内容而不用提交

git stash list 可以查看工作现场的列表

git stash apply 恢复保存起来的现场

git stash drop 删除保存起来的现场

git stash apply stash@{0} 恢复指定现场

git branch -D feature-vulcan 强制删除分支

远端合并解决方案

分支策略

在实际开发中,我们应该按照几个基本原则进行分支管理:

首先,master 分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;

那在哪干活呢?干活都在 dev 分支上,也就是说,dev 分支是不稳定的,到某个时候,比如 1.0 版本发布时,再把 dev 分支合并到 master 上,在 master 分支发布 1.0 版本;

你和你的小伙伴们每个人都在 dev 分支上干活,每个人都有自己的分支,时不时地往 dev 分支上合并就可以了。

所以,团队合作的分支看起来就像这样:

image-20240602144803817

如果版本库领先自己一个版本应该如何操作和更改

应该git rebase

如何pull和push

git和pull 和是在和远程库操作,只是在拉取相应的分支并合并,与其他分支之间没有关系

git rebase 原理

chatgpt

git rebase 是一个强大的 Git 命令,用于将一个分支的提交历史重新应用到另一个分支之上。

基本工作流程

假设我们有两个分支,mainfeature,并且 feature 分支是从 main 分支分出来的,现在我们想要把 feature 分支的更改重新基于 main 最新的提交之上。我们可以使用 git rebase main 来实现这个目标。

内部指令解析

假设初始状态:

  • main 分支:A - B - C
  • feature 分支:A - B - C - D - E

执行 git rebase main

  1. 确定共同祖先提交:

    1
    git merge-base main feature

    假设共同祖先提交是 A

  2. 创建补丁并保存到临时文件:

    1
    git format-patch A..feature -o /tmp/patches

    这会在临时目录中创建从 Afeature 的补丁文件。

  3. 切换到main分支并重置feature分支:

    1
    2
    git checkout feature
    git reset --hard main

    现在 feature 分支的 HEAD 和 main 分支的 HEAD 是一致的。

  4. 应用补丁:

    1
    git am /tmp/patches/*.patch

    这一步会逐个应用补丁,并创建新的提交。应用过程中,如果有冲突,Git 会暂停并允许用户手动解决冲突。

其他常见操作

处理冲突

如果在应用补丁的过程中遇到冲突,Git 会暂停并提示用户解决冲突。用户解决冲突后,需要执行以下命令继续:

1
2
git add <resolved-files>
git rebase --continue

如果用户希望放弃变基,可以执行:

1
git rebase --abort

git rebase 和git pull 的替换

可以使用 git pullgit pushgit merge 来实现类似 git rebase 的效果。虽然这并不是完全相同的操作,但通过一些步骤,你可以手动完成类似的变基过程。

以下是一个示例,展示如何使用这些命令来实现 git rebase 的效果:

场景设置

假设有两个分支:

  • main 分支
  • feature 分支

我们希望将 feature 分支的更改重新基于 main 分支的最新提交之上。

步骤

1. 更新 main 分支

首先,确保 main 分支是最新的:

1
2
git checkout main
git pull origin main

这将把 main 分支拉到最新状态。

2. 更新 feature 分支

接下来,切换到 feature 分支并拉取最新的更改:

1
2
git checkout feature
git pull origin feature

这将确保 feature 分支也是最新的。

3. 创建临时分支

创建一个临时分支来保存 feature 分支的更改:

1
git checkout -b feature-temp

4. 将 feature 分支重置到 main 分支

切换回 feature 分支并将其重置到 main 分支的最新提交:

1
2
git checkout feature
git reset --hard main

现在 feature 分支的 HEAD 与 main 分支的 HEAD 相同。

5. 合并临时分支的更改

将临时分支的更改合并到 feature 分支:

1
git merge feature-temp

在这个过程中,如果有冲突,解决冲突并继续合并。

6. 推送更改

如果合并成功,将 feature 分支的更改推送到远程仓库:

1
git push origin feature --force

总结

通过这样述步骤,可以实现类似 git rebase 的效果总结步骤如下:

  1. 更新 main 分支和 feature 分支。
  2. 创建一个临时分支保存 feature 分支的更改。
  3. feature 分支重置到 main 分支。
  4. 合并临时分支的更改到 feature 分支。
  5. 推送更改到远程仓库。

hexo常用命令

https://www.jianshu.com/p/c14e70f5885d

参考

https://doc.yonyoucloud.com/doc/wiki/project/git-tutorial/index.html