Git是一个开源的分布式版本控制系统,而GitHub是开源代码托管平台,它提供基于Git的代码托管服务
你也可以自己搭建和管理Git服务器来进行代码库的管理,或者选择别的代码托管平台,如:GitLab
ssh-keygen -t rsa -b 4096 -C "[email protected]"
使用 SSH 的方式进行克隆,将使得我们本地与 GitHub 之间建立了信任连接,也就意味着之后所有需要进行用户认证的地方都不再需要显式地用户名密码认证。
git clone [-b 分支名] [--single-branch] 仓库地址 [local_path]
例子:git clone -b dev --single-branch xxx demo
将远程仓库xxx的dev分支克隆到本地,目录名字为demo
--single-branch
表示只克隆特定分支到本地,如果不加则将远端代码库全部克隆到本地,一般不加
在克隆了代码库之后,我们一般仍需要对 Git 做一些基本的配置才能使用 Git 进行日常工作。
Git 配置的作用域主要有三种:System、Global 和 Local,分别对应的配置文件地址为:
另外我们也可以使用 git config --system -l
,git config --global -l
,git config --local -l
命令分别列出三个作用域下的配置。
在使用 git config
命令进行配置的时候,也可以使用 git config --system
,git config --global
,git config --local
三种不同的选项来修改不同作用域的配置。
配置 user 信息在 Git 中是十分重要的一个步骤, username 和 email 不能为空,它们将会被记录在每一条该 user 的 commit 信息中
我们可以配置user.name
和 user.email
的值来配置 user 信息
git config --global user.name "caozhi"
git config --global user.email "[email protected]"
配置成功后可以使用 git config --global -l
命令查看配置
Git 也可以对不需要被代码库所管理的文件或文件类型进行配置,使得提交代码时,这些文件不会被提交到代码库中。Git 是通过忽略清单.gitignore 文件进行配置的。
.gitignore 文件每行表示一个匹配模式(# 开头的行或者空行除外,# 用于注释)。它使用 glob 模式来进行匹配,glob 模式是一种简化的正则表达式,常用于来进行路径的模式匹配。我们可以在代码库的根目录或者任意子目录添加.gitignore 文件,特定目录下的.gitignore 文件使得忽略规则只在该目录及其子目录下有效。
每个目录下都可以放单独的 .gitignore 文件以控制子目录的忽略规则。
GitHub 有一个十分详细的针对数十种项目及语言的 .gitignore 文件列表模板,可以在 https://github.com/github/gitignore 找到它。
一个文件在 Git 中被管理时有三种状态以及对应所处的三种工作区域,理解这一特性将有助于我们更好的理解 Git 的常用命令的原理。在随后的 Git 操作介绍中,也会经常提到文件的各种状态变化和所处的工作区域。
Git 中有三个工作区域与上述三种状态相对应
总结下来,一次完整的提交包含以下操作:
当然如果需要将本地代码库的修改同步到远程代码库中(例如 GitHub),还需要将本地修改 push 到远程。
列出本地所有的分支 git branch
或者git branch -a
origin 实际上是 git 默认生成的一个仓库名称,在每次 clone 的时候 git 会生成一个 origin 仓库,该仓库是一个本地仓库,它指向其对应的远程仓库。
查看本地所有的仓库所指向的远程仓库 git remote -v
添加一个本地仓库 x 指向另一个远端仓库Xgit remote add x X
HEAD 针是指向当前工作分支中的最新的分支或者 commit。Git 通过 HEAD 知道当前工作分支指向的哪条 commit 上。
当我们完成了本地的代码提交,需要将本地的 commit 提交到远端,我们会使用 git push 命令。Push 操作实际上是先提交代码到本地的 remote/** 分支中,再将 remote/** 分支中的代码上传至对应的远端仓库。
当远端仓库的提交历史要超前于本地的 remote/** 提交历史,说明本地的 remote 分支并不是远端最新的分支,因此这种情况下 push 代码,Git 会提交失败并提示 fetch first 要求我们先进行同步
fetch 和 pull 操作都可以用来同步远端代码到本地。在多数开发者的实践中,可能更习惯使用 git pull 去同步远端代码到本地, 但是 git fetch 也可以用于同步远端代码到本地,那二者的区别是什么呢?
让 Git 自动为我们去生成这样的 merge commit 可能会打乱我们的提交历史,因此比较好的实践方式是先 git fetch 同步代码到本地 remote 分支再自己执行 git merge 来合并代码到本地工作分支,通过这种方式来代替 git pull 命令去同步代码。
git branch [-av]
git branch branchname
。创建本地分支时时会基于当前的分支去创建,因此需要注意当前工作分支是什么分支。git push origin branchname:remote_branchname
。技术上本地分支 branchname 和远端分支 remote_branchname 必是相同的名字,但实践中为了方便记忆,最好使用相同的名字。git checkout branchname
git branch -d branchname
git push :remote_branchname
下面列出了一次完成的提交流程:
Log 命令用于查看代码库的提交历史。结合 log 命令提供的各种选项,可以帮助我们查看提交历史中有用的提交信息。
git status 是另一个常用的命令,用于查看当前分支的修改状态。当前分支没有任何修改时,执行 git status 命令会显示 working tree clean
有三种状态:已经提交到了暂存区;还在工作区未被提交到暂存区;新文件,这些文件没有被代码库所跟踪。
Diff 操作用于查看比较两个 commit 或者两个不同代码区域的文件异同。
比较操作是开发过程中最常用的操作之一,场景包括通过比较来查看本地修改了哪些代码,比较特定分支之间的代码,或者 Tag 与 Tag 之间、Tag 与分支之间的比较。
Git 中比较操作可以通过 diff 操作和 log 完成,diff 主要用于比较文件内容的差异,而 log 操作主要比较 commit 的差异。
Diff 命令的基本格式是 git diff 。其作用是相比 src,列出目标对象 dst 的差异。
Git 中 Tag 和分支本质上都是指向对应 commit 的指针。因此 Tag、分支、commit 三者之间可以很平滑的进行比较操作。
使用 git diff 也可以查看单个文件的差异
回滚(Rollback)操作指的是将已经提交到代码库的 commit 生成一个与对应 commit 完全相反的 commit,相当于是对目标 commit 进行一次代码修改的逆向操作。
在实际项目中,经常用于进行版本的回滚或对某些错误提交进行回滚。Git 中是使用 revert 命令进行回滚操作,它会生成一条新的反向 commit,同时保留目标 commit。
撤销操作指的是丢弃我们的代码修改。实际开发中撤销通常包含多种情况:
撤销未保存到暂存区的代码
git checkout – filepath 命令来进行撤销某个文件的修改
git reset – hard HEAD 命令来丢弃本地所有修改 即回到HEAD的状态
撤销已保存至暂存区但是还未提交到代码库的代码
git reset HEAD 将暂存的修改撤销到工作区
撤销已提交到本地代码库但还未 push 到远端进行同步的代码
git reset [–hard] commit_id 命令来操作
如果我们只是想保留修改,我们可以使用 git reset commit_id 命令来使得 HEAD 指针指向对应的 commit,这样在其之后 commit 的代码修改会被撤销到工作区
当我们不需要保留修改,而想要完全丢弃掉 commit 时,我们可以使用 git reset --hard commit_id 命令,这样对应 commit 之后的 commit 将会完全被丢弃
撤销已提交到远端的代码
而对于已经提交到远端的 commit,此时我们没有办法再使用 reset 命令撤销掉原先的 commit,即使在本地用 reset 进行了撤销,再进行同步拉取代码时,仍然会将远端的 commit 拉回本地。因此这种情况我们只有通过 revert 进行回滚。
由此我们也可以看到,revert 和 reset 命令都可以用于撤销 commit,它们最大的不同在于 revert 会生成一条与之前完全相反的 commit 同时保留原先的 commit,而 reset 则是抛弃掉原先的 commit。
Reset 命令本质上是重置工作区的 HEAD 指针使其指向对应位置,当重置 HEAD 指针之后,会将 HEAD 指针之后的 commit 丢弃掉从而也达到了撤销修改的目的。reset 命令有三个参数:
Fast-forward 是指快进合并,它是直接将 master 分支指针直接指向了 dev 分支的 commit,而并没有在 master 分支上产生新的 merge commit。
执行 git merge 命令时通过加上–no-ff 选项来禁止 Fast-forward。可以看到非快进合并模式下,git 会产生一条新的 merge commit。使用 Fast-forward 模式的好处是可以快速的进行合并且不会产生 merge commit,但其缺点在于它不会保留合并分支的信息,因此当合并分支被删除时,也就不知道对应的提交是来自于哪个分支。
有时候我们实际项目中在自己的开发分支上可能会提交很多跟业务意义关系不大的 commit,例如格式修改、删除空格、撤销前次提交等等,执行 git merge 操作时默认情况下会将合并分支上这些原始 commit 直接合并过来,在目标分支上保留了详细的提交历史,往往这些无意义的提交历史会导致主分支的历史显得杂乱。这种情况下我们可以使用 squash 选项将待合并的所以 commit 重新替换成一条新的 commit。
git merge --squash 合并分支名
除了使用 git merge 命令来合并分支之外,我们还可以通过 cherry-pick 命令来检出特定的一个或多个 commit 进行合并
git cherry-pick commitId
当在代码中解决了冲突之后,我们需要将修改后的代码重新使用 git add/rm/mv 提交到暂存区,并重新 commit 到代码库中。