Git 学习笔记
起步
安装
# 在 ubuntu 上安装
sudo apt install git-all
初次运行 Git 前的配置
安装完 git 之后,要做的第一件事就是设置你的用户名和邮件地址,因为每一个 git 提交都会使用这些信息,它们会写入到你的每一次提交中,不可更改:
git config --global user.name "lockegogo"
git config --global user.email [email protected]
如果使用了
--global
选项,那么该命令只需要运行一次,因为之后无论你在该系统上做任何事情, Git 都会使用那些信息。
Git 基础
获取 git 仓库
在已存在目录中初始化仓库
# 进入该项目录
cd /home/user/my_project
git init
上述命令将创建一个名为 .git
的子目录,这个子目录含有你初始化的 git 仓库中所有的必须文件,但是目前我们仅仅是做了一个初始化的操作,项目里的文件还没有被跟踪。
如果在一个已存在文件的文件夹(而非空文件夹)中进行版本控制,你应该开始追踪这些文件并进行初始提交。可以通过 git add
命令来指定所需的文件来进行追踪,然后执行 git commit
:
git add *.c
git add LICENSE
git commit -m 'initial project version
克隆现有的仓库
# 远程拉取
git clone <url>
# 自定义本地仓库的名字
git clone <url> <新目录名>
# 举个栗子
git clone https://github.com/libgit2/libgit2 mylibgit
记录每次更新到仓库
# 检查当前文件状态
git status
# 跟踪新文件,将文件放入暂存区
git add README
# 注意:如果运行了 git add 之后又对文件做了修订,需要重新运行 git add 把最新版本暂存起来
# 忽略文件
$ cat .gitignore
*.[oa]
*~
# 第一行告诉 Git 忽略所有以 .o 或 .a 结尾的文件。一般这类对象文件和存档文件都是编译过程中出现的。 第二行告诉 Git 忽略所有名字以波浪符(~)结尾的文件
我们再看一个 .gitignore 文件的例子:
# 忽略所有的 .a 文件
*.a
# 但跟踪所有的 lib.a,即便你在前面忽略了 .a 文件
!lib.a
# 只忽略当前目录下的 TODO 文件,而不忽略 subdir/TODO
/TODO
# 忽略任何目录下名为 build 的文件夹
build/
# 忽略 doc/notes.txt,但不忽略 doc/server/arch.txt
doc/*.txt
# 忽略 doc/ 目录及其所有子目录下的 .pdf 文件
doc/**/*.pdf
如果要查看尚未暂存的文件更新了哪些部分,可使用 git diff
,但注意其本身只显示尚未暂存的改动,而不是自上次提交以来所作的所有改动。
# 比较工作目录中当前文件和暂存区域快照之间的差异
git diff
# 比对已暂存文件与最后一次提交的文件差异
git diff --staged
git diff --cached
接下来提交更新:
git commit
# 接下来输入提交说明
# -m: 或者将提交信息与命令放在同一行
git commit -m "新增 README"
# -a: 跳过暂存,直接将所有已经跟踪过的文件提交
git commit -a -m "新增 README"
一些小 tips:
# 对文件重命名
git mv file_from file_to
查看提交历史
git log
# 查看每次提交的简略统计信息
git log --stat
Git 分支
# 创建分支
git branch testing
# 分支切换
git checkout testing
# 也可以使用一行命令同时执行创建和切换
git checkout -b testing
# 查看左右分支
git branch
--merged:过滤已经合并到当前分支的分支
--no-merged: 过滤尚未合并到当前分支的分支
接下来,我们模拟一个真实的场景:你将经历如下步骤:
- 开发某个网站。
- 为实现某个新的用户需求,创建一个分支。
- 在这个分支上开展工作。
正在此时,你突然接到一个电话说有个很严重的问题需要紧急修补。 你将按照如下方式来处理:
- 切换到你的线上分支(production branch)。
- 为这个紧急任务新建一个分支,并在其中修复它。
- 在测试通过之后,切换回线上分支,然后合并这个修补分支,最后将改动推送到线上分支。
- 切换回你最初工作的分支上,继续工作。
git checkout -b iss53
vim index.html
git commit -a -m 'added a new footer [issue 53]'
git checkout master
git checkout -b hotfix
vim index.html
git commit -a -m 'fixed the broken email address'
# 运行测试,确保修改是正确的,然后将 hotfix 分支合并回你的 master 分支来部署到线上
git checkout master
git merge hotfix
# 解决紧急问题之后,应该先删除 hotfix 分支,然后再回到之前的工作
git branch -d hotfix
git checkout iss53
vim index.html
git commit -a -m 'finished the new footer [issue 53]'
# 最终进行合并
git checkout master
git merge iss53
git branch -d iss53
# 注意如果一个分支还没有合并,使用 -d 是删除不掉的,如果真的不想要了,可以使用 -D 强制删除
如果在两个不同的分支中,对用一个文件的同一个部分进行了不同的修改,在合并时就会遇到冲突,Git 没办法干净的合并它们。
git status
# 使用图形化工具来解决冲突
git mergetool
服务器上的 Git
在服务器上搭建 Git
如何给团队的每个人提供服务器上的 git 的访问权?
我们可以给每个人创建账号并且设置临时密码,但这种方案十分麻烦。
更好的做法是:在主机上建立一个 git 账户,让每个需要写权限的人发送一个 SSH 公钥,然后将其加入 git 账户的 ~/.ssh/authorized_keys
文件。
生成 SSH 公钥
ssh-keygen -o
首先 ssh-keygen 会确认密钥的存储位置(默认是 .ssh/id_rsa
),然后它会要求你输入两次密钥口令。 如果你不想在使用密钥时输入口令,将其留空即可。 然而,如果你使用了密码,那么请确保添加了 -o
选项,它会以比默认格式更能抗暴力破解的格式保存私钥。
现在,进行了上述操作的用户需要将各自的公钥发送给任意一个 Git 服务器管理员 (假设服务器正在使用基于公钥的 SSH 验证设置)。 他们所要做的就是复制各自的 .pub 文件内容,并将其通过邮件发送。 公钥看起来是这样的:
$ cat ~/.ssh/id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAklOUpkDHrfHY17SbrmTIpNLTGK9Tjom/BWDSUGPl+nafzlHDTYW7hdI4yZ5ew18JH4JW9jbhUFrviQzM7xlELEVf4h9lFX5QVkbPppSwg0cda3Pbv7kOdJ/MTyBlWXFCR+HAo3FXRitBqxiX1nKhXpHAZsMciLq8V6RjsNAQwdsdMFvSlVK/7XAt3FaoJoAsncM1Q9x5+3V0Ww68/eIFmb1zuUFljQJKprrX88XypNDvjYNby6vw/Pb0rwert/EnmZ+AW4OZPnTPI89ZPmVMLuayrD2cE86Z/il8b+gw3r3+1nKatmIkjn2so1d01QraTlMqVSsbxNrRFi9wrf+M7Q== [email protected]
配置服务器
搭建好之后,开发者可以克隆仓库并进行改动:
$ git clone git@gitserver:/srv/git/project.git
$ cd project
$ vim README
$ git commit -am 'fix for the README file'
# 将本地的 master 分支推送到 origin 主机的 master 分支
$ git push origin master
git push 命令用于从将本地的分支版本上传到远程并合并。
git push <远程主机名> <本地分支名>:<远程分支名>
# 如果本地分支名与远程分支名相同,则可以省略冒号:
git push <远程主机名> <本地分支名>
GitHub
对 Github 项目做出贡献
如何对 Github 项目做出贡献?下面举个栗子:
# 克隆项目
git clone https://github.com/tonychacon/blink
# 创建出名称有意义的分支
cd blink
git checkout -b slow-blink
# 修改代码
sed -i '' 's/1000/3000/' blink.ino (macOS)
# 检查改动
git diff --word-diff
# 将改动提交到分支中
git commit -a -m 'three seconds is better'
# 将新分支推送到 GitHub 的副本中
git push origin slow-blink
现在到 GitHub 上查看之前的项目副本,可以看到 GitHub 提示我们有新的分支, 并且显示了一个大大的绿色按钮让我们可以检查我们的改动,并给源项目创建拉取请求。
让你的 Github 仓库保持更新
当你派生了一个 GitHub 仓库之后,你的仓库(即你的“派生”)会独立于原仓库而独立。 特别地,当原仓库有新的提交时,GitHub 会通知你,但你的 GitHub 仓库不会被 GitHub 自动更新,这件事必须由你自己来做:
# 切换到 master
git checkout master
# 抓取更改
git pull https://github.com/progit/progit2.git
# 将 master 分支推送到 origin
git push origin master
这虽然可行,但每次都要输入从哪个 URL 抓取有点麻烦。你可以稍微设置一下来自动完成它:
# 添加源仓库并取一个名字,这里叫它 progit
git remote add progit https://github.com/progit/progit2.git
# 将 master 分支设置为从 progit 远端抓取
git branch --set-upstream-to=progit/master master
# 将默认推送仓库设置为 origin
git config --local remote.pushDefault origin
搞定之后,工作流程为更加简单:
git checkout master
git pull
git push
参考资料
- Scott Chacon, Ben Straub:《Pro Git》