首先附一张Git的cheat sheet 作为开始,方便查阅:https://education.github.com/git-cheat-sheet-education.pdf
Git简介
Git大家肯定都不陌生了,象征性地再介绍一下:Git是一个版本控制系统,换句话说,它可以在整个开发过程中对我们的代码库进行命名快照(即保存),并且在出现问题时,我们可以轻松地回滚到任何此类保存的状态。如果我们写了错误地代码,在没有Git的情况下,我们需要找出我们改变了什么并手动恢复,这既耗费时间又容易出错。有了Git,如果我们在代码运行正常的时候进行了保存,我们可以用一条命令就恢复到那个状态。我们也可以为彼此逐步记录我们的工作。Git还有一些与协作相关的功能,如分支管理,可以很好的用于新feature的开发以及bug fix等。
Git的workflow与常用命令:
首先我们要来一张图,彻底弄清楚我们系统上装的Git和GitHub是什么关系,我们通过各个命令分别是代表着把代码从哪里送到哪里。接下来介绍各个概念和命令的时候请对着这个图来参照,就很清楚我们每一步都在做什么了。
概念:
Git Repository:Git仓库(Git Repository)是一个包含文件及其历史版本的数据库。在上面图中,我们在Local(本地计算机)上有一个仓库localrepo,它将存储在 .git 目录中(这个目录你在本地git init后就会自动生成,如果你看不见可能是被隐藏了,因为其实你也并不需要直接操作它)。在Remote远程或在线存储的仓库remote repo(例如在 GitHub 上)被称为远程仓库。我们的git push和git pull就发生在本地仓库和远程仓库之间。
Working directory:工作目录就是你当前通过git check到的分支下的最新代码,也就是上图的最左边,你的IDE中显示的文件内容。
Staging area: 暂存区是在创建一个Git提交commit之前,我们把希望包含在提交中的文件放在暂存区。相当于做了一个“缓冲”,不是任何修改都commit。
常用命令:
git clone <远程仓库链接> :把一个代码从远程仓库复制到本地,此时我们本地工作目录就有了全部代码,并且和这个远程仓库建立了连接。
git add <文件名1>:把变动的文件加到暂存区(对应着上面图中来理解)。
git commit -m “<提交信息>”:把暂存区里的东西提交到本地仓库。提交会自带一个提交id以及提交信息,提交信息由你来自定义,用来描述这次的修改和变化。
进行git commit时,要尽量对单一的修改分别commit,要经常及时的commit并附上简介清晰明了的提交信息,避免把大量的修改都并到一个commit里,那样会难以追踪和理解。然后就是一些自动生成的文件不要提交,比如npm install生成的node_modules文件夹,mvn site生成的target文件夹等,可以写一个.gitignore文件把不需要被git追踪的文件放进去。
git status: 显示哪些文件已被更改以及哪些文件在暂存区。
git log:显示所有的提交信息等。
git branch “<分支名称>” 创建一个名为<分支名称>的新分支。
git checkout <分支名称>切换到<分支名称>的分支。通过git checkout -b <分支名称> 命令,如果相关分支不存在,会直接创建并且切换过去。
git push :将本地修改提交到远程仓库。
git pull: 获取远程仓库的更新并尝试合并到本地工作目录。
团队协作中的workflow
fork 与 pull request
Fork可以理解成复制一个项目到你自己的账号下,让你可以自由修改这个项目,而不会影响原项目。Fork出来的新项目与原项目之间保持一种链接关系。这种方式非常适合你希望在一个现有的项目基础上进行新的开发,或者你想为一个你没有写权限的项目做贡献。
Pull Request(拉取请求): 当你在Fork的项目中做了修改,觉得这些修改对原项目有帮助,你可以发起一个Pull Request。这是向原项目的维护者提出,希望他们将你的修改合并到原项目中。原项目维护者在接到Pull Request后,可以查看修改内容,进行评论和讨论,然后决定是否接受这些修改。在团队协作中,使用分支和Pull Request可以使团队成员各自在不同的任务上工作,而不会相互干扰。当各自的任务完成后,可以通过Pull Request将各个分支的更改合并,这样就可以集中管理和审查所有的更改。
解决冲突 merge conflicts
当你准备接收一个pull request,但Github通知你遇到了合并冲突conflict,这通常意味着你的分支包含了一个与你要合并进去的分支中的同一处改变。如果冲突足够简单,你通常可以通过点击pull request页面上的"Resolve Conflicts"在网页编辑器中解决它。
一旦你打开了文件,你应该可以看到一些像">>>>>branch1"和"<<<<<branch2"这样的冲突标记在你的冲突文件中。你可以通过删除冲突标记和你想要舍弃的改变来手动修复冲突,一旦你解决了所有的合并冲突,你可以点击"Mark as resolved"继续进行pull request。
通过实践学习:
这是一个可视化练习操作git的在线网页:Learn Git Branching
这个网页大概长这样,会从git的入门开始指导你Git操作,你可以一边操作一边看到当前的分支变化,涉及最基础的commit,branch等,也有一系列高阶操作。
接下来我们输入一些常用命令,来看看它们实际对于分支是如何显示的。
git commit
注意这里的箭头并没有指反,c2作为最新的提交变化,它“指向”旧的状态。Git就是通过这种方式somehow完成回溯的。
git branch newImage, git checkout newImage
由于当前还在master分支,所以此时git commit会把master继续推进。要想推进newImage,应该是先checkout到newImage然后再commit。
Merge和Rebase
git merge
和 git rebase
都是 Git 中用来合并分支的两种方式,但它们的方式和结果有所不同。
git merge
git merge
命令会创建一个新的提交,这个提交会包含两个父提交:一个是你当前所在分支的最新提交,另一个是你想要合并的分支的最新提交。这个新的提交称为“合并提交”。合并的优点是它会保留完整的提交历史和分支的上下文。不过,这种方式也可能导致提交历史变得复杂,尤其是在活跃的代码库中。
git rebase
git rebase
命令会把一个分支的提交移到另一个分支上。首先,Git 找到这两个分支最近的公共祖先,然后获取当前分支自那时以来的所有提交。这些提交被称为“补丁”。然后,Git 会检出你想要把这些补丁应用到的分支,并逐个应用这些补丁。使用 rebase 的优点是它可以创建一个更线性的项目历史,这样就更容易理解。不过,这种方式也会丢失一些上下文信息(因为改变了提交的原有顺序),并且在公共分支上进行 rebase 可能会导致问题,因为它会改变提交历史。
Reset和Revert
git reset
和git revert
都是Git中用来撤销更改的命令,但它们的工作方式和目的有所不同。
git reset
git reset
命令用于撤销对本地仓库的更改,相当于删除你刚刚的commit。git reset
是一个危险的命令,因为它会改变你的Git历史。如果你已经把更改推送到远程仓库,那么使用git reset
可能会引发问题。如果和远程仓库有连接,就不要用这个命令了。
git revert
git revert
命令用于撤销已经提交到Git历史的更改。这个命令会创建一个新的commit,这个commit会把指定的历史commit中的更改反转过来。git revert
的优点是它不会改变现有的Git历史,因此它是一个更安全的选项,特别是当你需要撤销已经推送到远程仓库的更改时。
git cherry-pick
cherry-pick允许你选取任何存在的commit,并将其应用到当前的工作分支上。比如你正在你的feature分支上工作,但是有人对main分支做了个bugFix,你就可以通过cherry-pick把bugFix应用到你自己的分支上。在上图中,master就把另一个分支中的两个commit的变化应用到了自己身上。
需要特别强调的是:图里的一个个commit节点记录的是变化,而不是当前最新代码状态,因此我们可以随意地使用cherry-pick选出那些我们要应用的变化。同样的道理,之前的git revert其实也就是提交一个和上一次commit完全相反的“变化”作为一个新的commit。
小结:
这篇文章介绍了Git这个强大的版本控制系统,介绍了一些基本概念命令、解决冲突方法以及通过实践和插图的方式理解了一些操作步骤的具体含义。弄清楚这些基本知识对于日常学习工作来说是基本够用了。其他Git更深层原理和高级操作有机会再探索吧~