一. 版本控制术语
- 版本控制系统(VCS):管理不同版本源代码的系统
- 提交(commit):将源代码的变动提交到VCS中进行“存档”
- 仓库(repository):由多次提交所组成的代码库
- 工作目录(working directory):存在于本地文件系统的源代码目录
- 签出(checkout):从仓库中“读档”某次提交
- 暂存区:用来存放下一次将被提交到仓库的变更
- SHA:每次提交的唯一ID
- 分支:从开发主线分离出来的开发支线
二. 创建Git仓库
1. 创建新的仓库
git init
会创建.git文件夹,包含以下内容:
- config文件:与项目有关的配置文件
- description文件:GitWeb程序相关,可忽略
- hooks文件夹:存放客户端或服务端脚本,作为钩子程序在Git的不同生命周期事件中被调用
- info文件夹:包含一个全局型排除文件,用来追踪不希望被记录在.gitignore文件中的忽略模式
- objects文件夹:存放所有的提交
- refs文件夹:存放指向数据(分支和标签)的提交对象的指针(分支和标签)
- HEAD文件:指示目前被检出的分支
- index文件:保存暂存区信息
进阶阅读:
2. 克隆已存在的仓库
git clone <path-to-repo-to-clone>
命令从指定的URL克隆仓库,可以在URL后追加名字重命名仓库。
进阶阅读:
3. 查看仓库状态
git status
查看仓库的当前状态,特别是有没有还未提交的修改。
进阶阅读:
三. 浏览仓库提交记录
git log
命令浏览仓库的提交历史记录。默认会显示SHA,作者,日期和提交信息。
git log --oneline
显示短SHA和提交信息。
git log --stat
显示每次提交发生改变的文件,以及被添加/删除的代码行数。
git log -p/--patch
显示文件中发生的详细修改信息。
git log -p --stat
将上述两者结合显示统计信息和详细修改信息。
git log -w
忽略空白行。
git log --decorate
显示隐藏的细节信息,比如打好的标签和分支信息。
git log --oneline --decorate --graph --all
命令显示所有分支的图形化信息。
进阶阅读:
git log/show 短SHA
查看某次提交。git show
命令默认显示提交,作者,日期,提交信息和补丁信息。同样适用以下参数:
-
--stat
- 显示有多少文件被修改,以及添加/删除的代码行数 -
-p
or--patch
- 默认显示补丁信息,但如果使用了--stat
则补丁信息不会显示,需要再加上这个参数 -
-w
- 忽略空白行
四. 提交代码
1. 代码提交到暂存区
git add <file>将文件提交到暂存区,
git add .`将所有变更都提交到暂存区。
git rm --cached <file>
将提交到暂存区的文件删除。
2. 将暂存区代码提交到本地仓库
git config --global core.editor <your-editor>
配置编写commit message的文本编辑器。
git commit
提交代码,会弹出配置好的文本编辑器,用于输入本次提交所需提供的commit message。在文本编辑器中写好之后保存退出,代码就提交到本地仓库中。
或者使用git commit -m 'xxx'
来直接附加简单的提交信息。
进阶阅读:
3. 查看没有提交的改变
git diff
命令可以用来查看还没有提交到本地仓库的改变,它显示:
- 被修改的文件
- 发生添加或删除的代码行位置
- 发生了什么样的改变
进阶阅读:
4. 忽略不想提交的文件
对于不想提交的文件,可以使用.gitignore
文件进行忽略。.gitignore
文件可以使用通配符:
- blank lines can be used for spacing
-
#
- 注释 -
*
- 匹配0个或多个字符 -
?
- 匹配1个字符 -
[abc]
- 匹配 a, b, 或 c - ** - 匹配嵌套目录 - 比如a/**/z 匹配
- a/z
- a/b/z
- a/b/c/z
进阶阅读:
五. 标签,分支和代码合并
1. 给代码打标签
git tag -a v1.0
给最近一次提交的代码打上“v1.0”标签。同样会弹出文本编辑器用于输入必要信息。
git tag
命令列出所有的标签。
git tag -d v1.0
删除标签。
git tag -a v1.0 a87984
给某次提交打标签
进阶阅读:
2. 分支管理
git branch
命令查看所有的分支,活跃分支以*标记。
git branch sidebar
创建一个叫sidebar的分支。
git branch alt-sidebar-loc 42a69f
创建一个叫alt-sidebar-loc的分支,该分支基于SHA为42a69f的提交创建。
git checkout -b footer master
从master分支创建footer分支。
git branch -d sidebar
删除名为sidebar的分支。
git checkout sidebar
切换到sidebar分支。
进阶阅读:
3. 代码合并
git merge <name-of-branch-to-merge-in>
将其他分支代码合并到当前分支。
分支合并的工作原理:
- 检查要被合并的分支
- 回溯分支历史找到两个分支共同的祖先
- 合并两个分支上发生改变的代码
- 创建一次提交来记录合并操作
如果b分支从a分支创建出来有了几次提交,而a分支在这个过程中没有任何提交,那么此时将b分支合并到a分支的操作被称为“快进合并”(fast-forward merge)。相当于分支指针往前移动。反之,如果a分支在这个过程中有了自己的提交,那么两个分支的合并操作就是常规合并。
进阶阅读:
4. 解决冲突
如果在多个分支中修改了相同的代码行,就会导致合并冲突,进而造成合并失败。Git会尽量合并能合并的代码,无法合并的地方会用>>>
和<<<
标记,需要手动修复。
合并冲突标记解释:
-
<<<<<<< HEAD
从这行往下直到下一个标记之前所有的内容显示当前分支冲突区域是什么样的。 -
||||||| merged common ancestors
从这行往下直到下一个标记之前所有的内容显示修改之前原始的代码行。 -
=======
表示原始代码行的终点,从这行往下直到下一个标记之前的内容显示被合并的分支冲突域是什么样的。 -
>>>>>>> <分支名>
被合并分支冲突域的结束标记。
手动修复代码后保存,然后进行常规的代码提交工作即可。
进阶阅读:
- Basic Merge Conflicts from the Git book
- How Conflicts Are Presented from the Git docs
五. 撤销操作
1. 修改上一次提交
如果当前工作目录是干净的,git commit --amend
命令会弹出文本编辑器,我们可以修改上一次的提交信息,然后重新保存覆盖。如果我们发现有文件漏改了,可以修改文件,保存文件,将文件提交到暂存区,然后使用该命令将漏改的文件更新到上一次的提交记录中。
2. 回滚提交
git revert <SHA-of-commit-to-revert>
回滚某次提交的代码,撤销那次提交所做的所有修改,并创建一个新的提交来记录改变。
进阶阅读:
3. 重置提交
不同于回滚,重置提交会将提交从历史记录中彻底抹掉。被彻底抹掉的信息会被Git保留30天,可以使用git reflog
访问到这些信息。
进阶阅读:
在对提交进行重置时,常常需要指定从某次提交开始之前的某个相对位置:
-
^
– 表示父提交 -
~
– 表示第一个父提交
比如当前(HEAD)提交的父提交可以表示为:
- HEAD^
- HEAD~
- HEAD~1
当前提交的祖父提交为:
- HEAD^^
- HEAD~2
曾祖父提交:
- HEAD^^^
- HEAD~3
^
和~
的区别在于,对于因为代码合并而产生的提交会有两个父提交,^
表示第1个父提交,即合并代码时所在的分支;^2
表示第2个父提交,即被合并的代码分支。举例:
* 9ec05ca (HEAD -> master) Revert "Set page heading to "Quests & Crusades""
* db7e87a Set page heading to "Quests & Crusades"
* 796ddb0 Merge branch 'heading-update'
|\
| * 4c9749e (heading-update) Set page heading to "Crusade"
* | 0c5975a Set page heading to "Quest"
|/
* 1a56a81 Merge branch 'sidebar'
|\
| * f69811c (sidebar) Update sidebar with favorite movie
| * e6c65a6 Add new sidebar content
* | e014d91 (footer) Add links to social media
* | 209752a Improve site heading for SEO
* | 3772ab1 Set background color for page
|/
* 5bfe5e7 Add starting HTML structure
* 6fa5f34 Add .gitignore file
* a879849 Add header to blog
* 94de470 Initial commit
-
HEAD^
即SHA为db7e87a
的提交 -
HEAD~1
也是SHA为db7e87a
的提交 -
HEAD^^
即SHA为796ddb0
的提交 -
HEAD~2
也是SHA为796ddb0
的提交 -
HEAD^^^
即SHA为0c5975a
的提交 -
HEAD~3
也是SHA为0c5975a
的提交 -
HEAD^^^2
即SHA为4c9749e
的提交 (祖父提交 (HEAD^^
) 的第2个父提交 (^2
))
git reset <reference-to-commit>
命令用于重置(消除)提交。作用:
- 移动HEAD和当前分支指针到某次提交
- --hard清除提交
- --soft将提交的改变移回暂存区
- --mixed将提交的改变移回工作目录
* 9ec05ca (HEAD -> master) Revert "Set page heading to "Quests & Crusades""
* db7e87a Set page heading to "Quests & Crusades"
* 796ddb0 Merge branch 'heading-update'
git reset --mixed HEAD^
命令会将9ec05ca
中的提交移回工作目录;git reset --soft HEAD^
命令会将9ec05ca
中的提交移回暂存区;git reset --hard HEAD^
会将9ec05ca
中的提交彻底清除。
进阶阅读:
六. 与远程仓库协作
Git是一个分布式的版本控制系统,在多人协作开发的模式下,每一个开发者都有项目代码的一份拷贝。远程仓库可以通过URL或者文件系统路径的方式访问,其中URL是最常见的方式。可以为每个本地仓库配置与多个远程仓库的连接。
1. 添加远程仓库
通过git remote
命令完成与远程仓库的各种交互。该命令用于显示该仓库与远程仓库的联系。命令显示每个与之有关的远程仓库的短名,比如仓库是从远程clone下来的,那么默认会显示origin
,origin
默认代表主远程仓库,短名可以被重命名。
git remote -v
显示远程仓库的短名和与之相对应的完整路径:
origin https://github.com/leesper/become-cloud-developer.git (fetch)
origin https://github.com/leesper/become-cloud-developer.git (push)
在Github上创建好远程仓库之后,可以使用如下命令将其与本地仓库关联起来:
git remote add origin https://github.com/xxx/yyy.git
进阶阅读
2. 推送提交到远程仓库
git push <remote-shortname> <branch>
命令将分支代码推送到远程仓库,例如git push origin master
,此时如果使用git log
命令查看提交记录,就会发现多了一个叫origin/master
的跟踪分支,即远程仓库origin
具有一个分支master
,可以使用这个信息来追踪远程仓库的情况。
3. 从远程仓库更新
git pull origin master
从远程仓库origin
中拉取master
分支的代码。
4. Pull和Fetch
如果远程仓库有本地仓库没有的提交,同时本地仓库也有远程仓库没有的提交,这时就需要使用git fetch
命令来从远程拉取代码。git fetch
从远程拉取代码,但不会自动合并到本地。
git fetch origin master
将从远程仓库拉取代码到本地仓库,本地的追踪分支移动并指向最近的一次提交记录,但本地分支不会发生改变。相当于fetch
只做了pull
的一半工作。将代码拉取到本地后,可以使用git merge origin/master
来手动合并代码。然后推送代码到远程仓库。
5. 为别人的项目贡献代码
当我们需要给别人的项目贡献代码时,我们首先需要在Github上点击fork
按钮将别人的代码分叉到自己的账号下,形成一个独立的拷贝,然后在这个拷贝上进行相关的代码维护,并最终将自己的贡献合并到别人的仓库中(通过Pull Request)。
当开发者作为小组成员参与到一个项目中时,最重要的事情是首先了解项目当前的进度,特别是各开发分支的工作进度。git log
作为强大的命令可以满足我们的要求,下面总结几个相关的命令。
git shortlog
命令按照提交者分组显示相关提交信息,按照提交者名字字母排序显示。
git shortlog -s -n
显示每个提交者的提交次数,降幂排序。
git log --author=Surma
查看所有Surma的提交。
git show 5966b66
查看SHA为5966b66的提交。
git log --grep=bug
筛选带bug关键字的提交记录。
可以通过查看CONTRIBUTING.md
文件来确定要开展的工作。某些项目会通过该文件告诉开发者维护或者贡献项目所必须遵循的相关信息。如果提交的改动比较大,那么可能需要在Github Issues中先征得项目维护者的同意,也可以通过该列表了解其他开发者是否遇到过类似问题。与相关开发人员进行充分的沟通之后再开展工作,避免自己的贡献变成无用功。
开发工作完成后,可以在自己fork的副本中创建PR,向源项目申请提交贡献,此时源项目的作者会审阅你的代码,并评估是否合并到源仓库中,或者向你提出修改意见。要想与源项目保持同步,首先可以STAR,STAR越多的项目流行度越高;其次可以WATCH该项目,这样当项目有任何变更我们都能在Github主页上及时看到。另外我们还可以为副本仓库添加对上游源项目的跟踪来保持与源项目的同步。
git remote add upstream https://github.com/xxx/yyy.git
命令将源项目指定为短名upstream
。
git fetch upstream master
可以从源项目的远程仓库中拉取代码到本地。也可以通过git pull upstream master
拉取并合并代码到本地。
git merge upstream/master
然后git push origin master
可以将源项目代码合并到本地,然后推送到自己的代码副本。
6. 使用rebase命令合并提交
git rebase -i HEAD~3
命令以最近一次提交的曾祖父提交为base,将HEAD~2
,HEAD~1
,和 HEAD
合并为一次提交。合并后将代码同步到远程仓库时,需要git push -f
命令强制同步。rebase命令可以干很多事情:
-
p
或pick
– 保持原有提交不变 -
r
或reword
– 提交内容不变,但修改commit message -
e
或edit
– 提交内容不变但保持提交前的状态,然后我们可以: - 添加新内容或文件
- 删除内容或文件
- 修改本来是要提交的内容
-
s
或squash
– 将这次代码提交的改变合并到前一次提交中(列表中的上一个) -
f
或fixup
– 将这次代码提交的改变合并到前一次提交中,删除commit message -
x
或exec
– 运行shell命令 -
d
或drop
– 删除提交
注意:如果代码已经推送到远程仓库,最好不要执行rebase命令,因为这会清除某些提交记录,导致其他开发者的本地仓库与远程仓库不同步从而产生严重问题。
进阶阅读:
- Git Branching - Rebasing
- git-rebase
- https://www.atlassian.com/git/tutorials/rewriting-history#git-rebase
七. 读别人的代码,然后自己写代码
这里列举的一些资源对新人快速上手并贡献代码有帮助:
- http://up-for-grabs.net/:列举的项目包含有专门针对新手代码贡献者设计的任务,可以从这些任务入手开始提交贡献
- http://www.firsttimersonly.com/:包含很多指导新手开发者为开源项目做贡献的指南
-
first-timers-only label on GitHub:Github上所有包含
first-timers-only
标签的issue