Git 的工作区域和流程
首先了解 Git 的工作区域是如何构成的。如下图所示,此图包含了 Git 的4个工作区和一些常见的操作。
Workspace:工作区,我们平时进行开发改动的地方,开发的过程也就是对工作区的操作
Index:暂存区,当执行 git add 的命令后,工作区的文件就会被移入暂存区,暂存区标记了当前工作区中那些内容是被 Git 管理的,当完成某个需求或者功能后需要提交代码,第一步就是通过 git add 先提交到暂存区。
Repository:本地仓库,位于自己的电脑上,通过 git commit 提交暂存区的内容,会进入本地仓库。
Remote:远程仓库,用来托管代码的服务器,远程仓库的内容能够被分布在多个地点的处于协作关系的本地仓库修改,本地仓库修改完代码后通过 git push 命令同步代码到远程仓库。
一般来说,Git 的工作流程分为以下几步
1.在工作区开发,添加,修改文件。
2.将修改后的文件放入暂存区。
3.将暂存区域的文件提交到本地仓库。
4.将本地仓库的修改推送到远程仓库。
Git 常见的基本操作
git add
添加文件到暂存区
# 添加某个文件到暂存区,后面可以跟多个文件,以空格区分
git add xxx
# 添加当前更改的所有文件到暂存区。
git add .
git commit
# 提交暂存的更改,会新开编辑器进行编辑
git commit
# 提交暂存的更改,并记录下备注
git commit -m "you message"
# 等同于 git add . && git commit -m
git commit -am
git pull
# 从远程仓库拉取代码并合并到本地,可简写为 git pull 等同于 git fetch && git merge
git pull <远程主机名> <远程分支名>:<本地分支名>
# 使用rebase的模式进行合并
git pull --rebase <远程主机名> <远程分支名>:<本地分支名>
git fetch
与 git pull 不同的是 git fetch 操作仅仅只会拉取远程的更改,不会自动进行 merge 操作。对你当前的代码没有影响
# 获取远程仓库特定分支的更新
git fetch <远程主机名> <分支名>
# 获取远程仓库所有分支的更新
git fetch --all
git branch
# 新建本地分支,但不切换
git branch <branch-name>
# 查看本地分支
git branch
# 查看远程分支
git branch -r
# 查看本地和远程分支
git branch -a
# 删除本地分支
git branch -D <branch-nane>
# 重新命名分支
git branch -m <old-branch-name> <new-branch-name>
Git 的一些额外实用命令
git rebase 让你的提交记录更加清晰可读
git rebase 的使用
rebase 翻译为变基,他的作用和 merge 很相似,用于把一个分支的修改合并到当前分支上。
举个例子。
假设我们现在有2条分支,一个为 master,一个为 feature/1,他们都基于初始的一个提交 add console 进行检出分支,之后,master 分支增加了 3.js 和 4.js 的文件,分别进行了2次提交,feature/1 也增加了 1.js 和 2.js 的文件,分别对应以下2条提交记录。
此时,对应分支的提交记录如下。
master 分支如下:
add 1
add 2
feature/1 分支如下:
add 3
add 4
此时,切换到 feature/1 分支下,执行 git rebase master,成功之后,通过 git log 查看记录。
可以看到先是逐个应用了 mater 分支的更改,然后以 master 分支最后的提交作为基点,再逐个应用 feature/1 的每个更改。
feature/1:
add 1
add 2
add 3
add 4
所以,我们的提交记录就会非常清晰。上面演示的是比较顺利的情况,但是大部分情况下,rebase 的过程中会产生冲突的,此时,就需要手动解决冲突,然后使用依次 git add 、git rebase --continue 的方式来处理冲突,完成 rebase 的过程,如果不想要某次 rebase 的结果,那么需要使用 git rebase --skip 来跳过这次 rebase 操作。
git merge 和 git rebase 的区别
不同于 git rebase 的是,git merge 可能会产生一条额外的合并记录,类似 Merge branch 'xxx' into 'xxx' 的一条提交信息。
另外,在解决冲突的时候,用 merge 只需要解决一次冲突即可,简单粗暴,而用 rebase 的时候 ,需要依次解决每次的冲突,才可以提交。
使用 git cherry-pick 获取指定的 commit
git cherry-pick 意为择优挑选,表示”挑拣”提交,和 merge 合并一个分支的所有提交不同的是,它会获取某一个分支的单笔提交,并作为一个新的提交引入到你当前分支上。当我们需要在本地合入其他分支的提交时,如果我们不想对整个分支进行合并,而是只想将某一次提交合入到本地当前分支上,那么就要使用 git cherry-pick 了。
例如有以下两条分支,feature/cherry-pick1 和 feature/cherry-pick2 它们都是基于 master 创建的分支
feature/cherry-pick1 log:
add origin1
add p1
feature/cherry-pick2 log:
add origin2
add p2
master 只需要 feature/cherry-pick1 和 feature/cherry-pick2 有关 origin 的修改,并不关心其他内容的修改。此时就可以用 cherry-pick 指令了。
语法: git cherry-pick [commit-hash]
commit-hash 表示的是某次 commit 的 hash 值。现在,依次执行以下两条指令 git cherry-pick e0bb7f3、git cherry-pick c9a3101,过程中,如果出现冲突,解决冲突后 进行 git add ,接着执行 git cherry-pick --continue。
最后,master 上的提交如下:
add origin2
add origin1
add origin
此时,master 分支上应用了需要的提交,就达到了我们想要的效果。如果需要多个 cherry-pick 需要同步到目标分支,可以简写为 git cherry-pick <first-commit-id>...<last-commit-id>,这是一个左开右闭的区间,也就时说 first-commit-id 提交带来的代码的改动不会被合并过去,如果需要合并过去,可以使用 git cherry-pick <first-commit-id>^...<last-commit-id>,它表示包含 first-commit-id 到 last-commit-id 在内的提交都会被合并过去。
git revert 回滚提交
场景如下,项目有2个需求版本并行开发,突然传来噩耗,第二个版本要携带第一个版本某个提交上线,但是第一个版本里还携带了其他的需求开发,想要通过 reset 的方式粗暴摘除之前的关于 bug 修复的 commit 肯定是不行的,同时,这种做法比较危险,此时,我们既不想破坏之前的提交记录,又想撤回我们遗留 bug 的 commit 记录应该怎么做呢?git revert 就派上了用场。
git revert撤销某次操作,此操作不会修改原本的提交记录,而是会新增一条提交记录来抵消某次操作。
语法: git revert <commit-id> 针对普通 commit
git revert <commit-id> -m 针对 merge 的 commit
下面就用一个案例来理解一下这个命令,如下图所示,假设被红框框起来的地方是会引起 bug 的一次提交,在他的提交之后,又进行了2次提交,其中包含了其它同事的提交。
此时想把引起提交的 bug 的干掉,执行 git revert [commit-hash],执行操作后,再打开查看日志,能够看到是新增了一条 commit 记录,这个 commit 的产生的 msg 是自动生成的,Revert 开头,后面跟撤回的 commit-msg 信息,之前的 commit 记录并没有消失,此时也达到了代码回退的效果
此外 git revert 也可以回滚多次的提交
语法:git revert [commit-id1] [commit-id2] ... 注意这是一个前开后闭区间,即不包括 commit1 ,但包括 commit2 。
回滚我们的提交有二种方式,一种是上文提到的git revert命令外,还可以使用 git reset 命令,那么它们两者有什么区别呢?
git revert 会新建一条 commit 信息,来撤回之前的修改。
git reset 会直接将提交记录退回到指定的 commit 上。
对于个人的 feature 分支而言,可以使用 git reset 来回退历史记录,之后使用 git push --force 进行推送到远程,但是如果是在多人协作的集成分支上,不推荐直接使用 git reset 命令,而是使用更加安全的 git revert 命令进行撤回提交。这样,提交的历史记录不会被抹去,可以安全的进行撤回。
git stash 暂存文件
会有这么一个场景,现在你正在用你的 feature 分支上开发新功能。这时,生产环境上出现了一个 bug 需要紧急修复,但是你这部分代码还没开发完,不想提交,怎么办?这个时候可以用 git stash 命令先把工作区已经修改的文件暂存起来,然后切换到 hotfix 分支上进行 bug 的修复,修复完成后,切换回 feature 分支,从堆栈中恢复刚刚保存的内容。
基本命令如下
git stash //把本地的改动暂存起来
git stash save "message" 执行存储时,添加备注,方便查找。
git stash pop // 应用最近一次暂存的修改,并删除暂存的记录
git stash apply // 应用某个存储,但不会把存储从存储列表中删除,默认使用第一个存储,即 stash@{0},如果要使用其他个,git stash apply stash@{$num} 。
git stash list // 查看 stash 有哪些存储
git stash clear // 删除所有缓存的 stash
此时,我正在开发一个新功能,修改了 某个文件里的内容
还没开发完成,这个时候,我想切换到 另一个分支上修复 bug,得暂停下开发切换到 对应的fix 分支,但是现在工作区还有内容,此时如果切换分支 Git 会报出下面的错误
error: Your local changes to the following files would be overwritten by checkout:
xxx.js
Please commit your changes or stash them before you switch branches.
意思就是说工作区有文件修改,不能提交,需要先进行 commit 或者 stash 操作,执行 git stash,结果如下
Saved working directory and index state WIP on stash: 22e561c feat: add xxx
此时,我们的工作区已经干净了,可以切换到 fix 分支进行 bug 修复的工作,假设我们现在 bug 修复完成了,继续切回 feature 分支进行原本功能的开发,此时只需要执行 git stash pop或者git stash apply,之前我们暂存的修改就会恢复到工作区,如下图所示。
当我们想要暂存文件,切换分支做某些事的时候,可以用 git stash 这种机制帮助开发。
推荐在使用 stash 的相关命令时,每一次暂存的时候,不要直接使用 git stash 命令进行暂存下来,而是使用 git stash save "message..." 这种方式,给本次的提交做一个信息的记录。这样,想应用更改的时候,先通过 git stash list 查看一下所有的暂存列表。之后,推荐使用 git stash apply stash@${num} 的方式进行应用对应的 stash,这样不会清空已有的 stash 的列表项,并且能应用到当前的工作区,不需要这个暂存的话,再手动清除就可以了。
git alias 配置别名
通常我们在工作中,新创建一个分支进行开发经常用到 git branch、git checkout、 git pull 等命令,在我们一顿猛如虎的操作后,开发完成,到了提交代码的阶段,又会使用诸如 git add 、git commit、git push 等命令,虽然简单,但是输入起来也是不够简洁,作为一个程序员,正所谓懒是人类进步的源泉,所以我们可以通过配置别名的方式,简化这些命令。
它的基本用法是 git config --global alias.<简化的字符> 原始命令
如下面的例子:
$ git config --global alias.br branch
$ git config --global alias.co checkout
$ git config --global alias.ci commit
...
这里将 co 表示 checkout,ci 表示 commit,br 表示 branch,以后提交就可以简写成
--global 是全局参数,配置一次后,这些命令可以在这台电脑下的所有仓库都适用。这些命令其实是更新你全局的 .gitconfig 文件,该文件用来保存全局的 git 配置,执行vim ~/.gitconfig,命令,命令行会显示我们添加的所有 alias。
配置 .gitconfig 文件里的 [alias] 所属的区域,然后就可以愉快的使用了~
[alias]
st = status -sb
co = checkout
br = branch
ci = commit
...
reset --soft
回退你已提交的 commit,并将 commit 的修改内容放回到暂存区。
git reset --hard 会被提及的比较多,能让 commit 记录强制回溯到某一个节点。
git reset --soft 的作用正如其名,--soft (柔软的) 除了回溯节点外,还会保留节点的修改内容。
命令使用
学会 reset --soft 之后,你只需要:
# 恢复最近一次 commit
git reset --soft HEAD^
reset --soft 相当于后悔药,给你重新改过的机会。对于上面的场景,就可以再次修改重新提交,保持干净的 commit 记录。
以上说的是还未 push 的commit。对于已经 push 的 commit,也可以使用该命令,不过再次 push 时,由于远程分支和本地分支有差异,需要强制推送 git push -f 来覆盖被 reset 的 commit。
还有一点需要注意,在 reset --soft 指定 commit 号时,会将该 commit 到最近一次 commit 的所有修改内容全部恢复,而不是只针对该 commit。
总结
学会了这些 Git 的使用技巧后,在日常工作中多多练习,可以带来很大的便利!