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 命令,用于将一个分支的提交历史重新应用到另一个分支之上。

基本工作流程

假设我们有两个分支,main 和 feature,并且 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

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

  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 pull、git push 和 git 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