不是什么教学贴…扯到哪是哪吧!

除了偶尔参与一些多人项目,实际上我还是在单独使用 Git,一个好处就是可以随意折腾不用担心影响他人,不过即便是单人使用的经验,在多人项目中大部分依然是适用的。

我们公司依然在使用 SVN,虽然我在 SVN 遇到问题时曾经推荐过 Git,不过团队里的人都不买账,没人愿意因为一点小问题就换掉整个版本控制系统,毕竟 SVN 在绝大部分时候都是工作的很好的,何况还有像 TortoiseSVN 这样优秀的 Windows 客户端。

人总是喜欢留在舒适区的,我也一样 :)

来继续吹 Git!

基本的入库操作

几乎每天都会做的:

> git add README.md
> git commit -m "add README.md"

为什么 Git 要搞出这种两段式提交的工作方式,这大概和 Git 的设计理念有关。

使用暂存区的好处很多,其中一个很大的好处就是可以在不打断当前工作的情况下提交部分修改。

举个例子来说,当你正在开发项目的重要功能的时候,需要修改一个小 BUG,而这相关文件已经做了大量的修改。这时就可以通过使用 git add -p 打开补丁式暂存将所有修改的内容拆分成更小的部分(chunk),然后选择需要的部分提交,其它部分则不受任何影响。

当然也可以先储藏(stash)当前的修改,然后再修改 BUG,但是弹出储藏的时候,可能会面对冲突的情况。

开新分支就更麻烦了… 不提。

关于 commit,少用 -a 参数,它会把当前工作区的所有被修改的文件一并提交,这样不管是 review 代码、撤销修改或是要重写历史记录,都会变得麻烦,commit 应当尽量细小且频繁。

轻便的分支

在 SVN 中操作分支是非常痛苦的一件事,以我们公司的项目为例,源码、文档、素材等乱七八糟的东西加一起 3 个多 G,创建或是拉取一个分支都要数分钟之久,而相同的操作在 Git 中只要几秒而已。

# 切换到已有的分支 <branch>
git checkout <branch>

# 以当前分支为基准,开启一个新的分支 <new_branch>
git checkout -b <new_branch>

# 删除某个分支,假设这个分支的所有提交已经合并到其它分支
git branch -d <branch>

# 如果要强制删除分支
git branch -D <branch>

# 删除远程分支
git push :<branch>

# 合并分支,注意顺序,trunk -> master
git checkout master
git merge trunk

储藏

# 储藏当前工作区的修改
git stash

# 弹出当前工作区的修改,应用到当前工作区
# 相当于 apply + drop
git stash pop

# 保留某个储藏,并手动应用到当前工作区
git stash apply stash@{1}

# 丢弃储藏
git stash drop stash@{1}

使用比较频繁的命令,但是我很多时候容易忘掉…

撤销操作

撤销操作有很多种场景,常见的如下:

# 编辑上次提交的注释信息,比如注释中有错别字或者表述不清楚
git commit --amend

# 回退上一次提交,并保留修改的内容,比如遗漏了某个文件需要补齐或者修改尚未完成
git reset HEAD~
git reset --mixed HEAD~
git reset --soft HEAD~

# 丢弃最近一次的提交,包括修改的内容
git reset --hard HEAD~

# 丢弃最近3次的提交
git reset --hard HEAD~3

# 彻底丢弃当前工作区修改的内容
# 请慎重操作,最好先 diff <file> 看一下,以免误操作
# 此操作仅作用于工作区文件,对于已经 add 到暂存区的文件是不起作用的
git checkout <file>

reset 的五种工作模式,默认–mixed:

模式 描述
–soft 回退操作,修改内容保留在暂存区
–mixed 回退操作,修改内容保留在工作区
–hard 回退操作,任意修改内容均不保留
–merge 回退操作,修改内容不保留,回退合并的修改,保留本地未提交的修改内容
–keep 回退操作,如果工作区文件有改动则停止操作

merge/keep 看看文档知道怎么回事就好,大部分时候用不到的…

拉取与推送

重写历史记录

有的时候我们可能对历史记录不太满意,希望能够重新整理一番,下面的命令提供了这种可能。

# 提出最近10条提交记录以供整理
git rebase -i HEAD~10

命令会打开一个编辑器供我们整理提交记录,可以丢弃、合并,排序或者修改注释等等。需要注意的是——这个操作最好不要包括那些已经推送到远程仓库的 commit,只针对尚未推送的 commit 作整理即可,否则会触发合并操作,并产生冲突,你的提交记录会变得一团糟。

最后的最后

git push --force

在任何情况下,如果没有绝对的理由,千万不要这么干… 虽然我干过好多次了 ;)