1. 基础概念
仓库
每个项目整个文件夹称为一个仓库repository,仓库中包含零到多个文件,仓库的改动由链状的提交commit所表示,这些提交又分布在不同的分支branch上。每个仓库都有一个起始默认的仓库master/main,然后新建和删除不同的分支,分支之间可以相互合并,以达到多人共同开发同一个项目。
HEAD表示当前指向的提交。
远程仓库
origin表示远程仓库,每个仓库包含本地仓库和对应的远程仓库,例如对应为dev和origin/dev,可以从远程仓库分支拉取更新到本地,以及将本地提交的修改推送到远程分支。
1.1 文件状态
对于Git项目,仓库文件夹中的文件有如下几种状态:
untracked/未追踪
未追踪的文件就是临时存在当前仓库文件夹中,但是并未包含在仓库中的文件,一般是新增的准备加入仓库中的文件,或是产生的日志文件等临时文件。
未追踪的文件如果不想加到仓库中,而且也不想通过 git status 看到,可以将文件规则加到.gitignore文件中。
如果是需要加到仓库中的文件,通过 git add 可以将文件变为暂存状态,然后就可以提交到仓库中。
unmodified/未修改
存在仓库中的所有文件,如果本地文件未被修改,则是未修改的状态。
通过 git status 无法看到未修改的文件,但是可以看到哪些文件是属于已修改和暂存状态的。
modified/已修改
存在仓库中的文件,修改后,就会变成已修改状态,已修改的文件所在称为工作区。
通过 git add 可以将已修改的文件转成暂存状态,而 git checkout – file 则将文件修改撤销,变为未修改状态。
staged/暂存
仓库文件转位暂存状态后,就是可以准备提交的状态了,暂存的文件所处暂存区。
通过 git commit 可以将所有暂存的文件提交,文件再次变为未修改状态,而 git reset HEAD file 则会将暂存的文件退回到已修改状态。
从上图中可以看到仓库文件的状态流转。不属于仓库的文件是 untracked 状态,属于仓库的文件是 unmodified 状态,作出更改后变为modified状态,将这些文件添加到暂存区就变成了 staged 状态,最后通过提交更新,将暂存区的文件提交到仓库,又变成了 unmodified 状态。
2. 配置
2.1 GitHub配置密钥
首先在本机创建SSH key,生成的key将会存在于~/.ssh/id_rsa.pub:
$ ssh-keygen -t rsa -C "email@example.com"
在GitHub打开setting,选择添加SSH Key,然后粘贴id_rsa.pub的内容。
2.2 配置文件
.gitconfig
~/.gitconfig文件存在用户根目录,是git的配置文件。
通过 git config 命令修改该配置文件,也可以直接打开文件作修改。
$ git config --list # 查看所以配置
$ git config <key> # 查看某个配置
$ git config --global key value # 设置配置
以下是一部分常用的配置:
$ git config --global user.name "name" # 设置用户名
$ git config --global user.email "email@example.com" # 设置邮箱
$ git config --global alias.st status # 设置命令别名
.gitignore
该文件放在每个仓库的根目录,用来列出忽略的文件的模式,对于忽略的文件模式,新增的文件和产生的修改将不会被显示。
当然也可以存在之前已提交的文件,现在被.gitignore的规则所忽略。
.git-credentials
这个文件会记录用户名和密码,这样操作git的更新和提交的时候,就不需要每次输入用户名和密码了。
首先在~/.git-credentials输入内容:
https://<username>:<password>@github.com
接着通过命令让密码保存生效:
$ git config --global credential.helper store
3. 命令
Git的命令有很多,熟练使用常用的即可,并且通过桌面软件如GitHub Desktop,也可以方便地完成很多操作。
查看参考文档:
$ git help # 查看常用命令
$ git <cmd> -h # 查看某个命令的用法
3.1 仓库
init
在当前文件夹创建仓库,并生成.git/管理版本库。
$ git init
clone
从远端克隆仓库到本地。
$ git clone <url> # 克隆仓库
$ git clone -b <branch> <url> # 克隆某个分支
remote
$ git remote # 远程库名称,一般为origin
$ git remote -v # 查看远程库地址
$ git remote add <name> <url> # 添加新的远程仓库
3.2 拉取上传
pull
拉取远程仓库代码更新,合并到本地。
$ git pull
$ git pull --rebase # 变基后再合并
fetch
拉取远程仓库代码更新到对应的远程分支,但不合并到本地分支。
$ git fetch
$ git fetch <remote> # 从远程库抓取
push
将本地提交推送至远程库。
$ git push # 本地提交推送至当前分支对应的远程库
$ git push <remote> <branch> # 将指定分支推送更新指定远程库
$ git push <remote> HEAD # 将当前分支推送更新指定远程库
3.3 分支
branch
该命令用于显示分支、新建或删除分支。
$ git branch # 显示本地分支
$ git branch -a # 显示本地和远程分支
$ git branch <branch> # 从当前分支创建分支
$ git branch <branch> <commit> # 从某个提交创建分支
$ git branch -d <branch> # 删除分支
$ git branch -D <branch> # 强制删除分支,即使有未合并的提交
$ git branch -u <remote>/<branch> # 设置当前分支对应的远程分支
merge
将指定分支合并到本分支。
$ git merge <branch>
rebase
将当前分支对于基于的分支变基。
假设当前分支 dev 基于 release 分支开发,并且又提交了两次 commit C1 和 C2,而 release 分支在我们新建 dev 分支后又有了新的提交,变基就是从基于的 release 分支最新的一次提交,参照 commit C1 和 C2,生成新的 commit C1’ 和 C2’,然后将 dev 分支的指针指向 C2‘。进行变基操作需要当前的工作区没有未提交的修改,如果有可以先提交或者暂时 stash。
通过 merge 合并分支会生成一个两条分支的合并,而通过 rebase 将分支变基再合并则往往会变成一条直线的提交,提交记录更加优雅整洁。当然如果变基过程有文件冲突的话,需要解决冲突,变基还是会产生两条分支合并的提交记录。
$ git rebase <branch1> # 当前分支基于分支1变基
$ git rebase <branch1> <branch2> # 分支2基于分支1变基
$ git rebase --onto <branch1> <branch2> <branch3> # 当分支3基于分支2而分支2基于分支1,使分支3基于分支1变基
有时候当前分支有多个本地提交,准备推送至远端,但是想要将他们合并成一个提交,再推送至远端。先通过 rebase:
$ git rebase -i HEAD~<n> # 编辑最新的n个提交
然后会打开一个临时文本进行编辑,将第二个到最后的 pick 改为 squash,然后保存退出。就可以将多个本地提交合并了。
cherry-pick
把某个指定提交合并到当前分支。
$ git cherry-pick <commit> # 把提交合并到当前分支
$ git cherry-pick <commit1>..<commit2> # 把从提交1下一个提交到提交2合并到当前分支
$ git cherry-pick <commit1>^..<commit2> # 把从提交1到提交2合并到当前分支
# 选项
# -n 不自动提交,选择多个提交时会将修改合并
cherry
显示本地尚未推送的提交。
$ git cherry # 显示本地尚未推送的提交
$ git cherry <commit1> <commit2> # 从提交1到提交2增加的提交
# 选项
# -v 显示注释
3.4 状态
status
显示仓库状态:所处分支、和远程库的分支差距、有哪些已修改和暂存的文件。
$ git status # 显示仓库状态
$ git status -s # 简洁状态
log
查看提交记录。
$ git log # 查看提交记录
$ git log <file> # 和指定文件或目录相关的提交记录
$ git log <branch1> ^<branch2> # 显示包含分支1不包含分支2提交
$ git log --oneline --decorate --graph --all # 查看分叉历史
# 选项
# -<n> 显示最新n个提交
# --oneline 单行显示
# --no-abbrev 显示完整commit id
# --stat 显示差异统计
# -p 显示差异
reflog
查看版本记录。
$ git reflog # 查看版本记录
$ git reflog <branch> # 查看分支的版本记录
# 选项
# --no-abbrev 显示完整commit id
diff
查看已修改文件的修改。
$ git diff # 查看所有文件的修改
$ git diff <file> # 查看文件的修改
$ git diff --staged <file> # 查看暂存文件的修改
$ git diff <commit1> <commit2> # 比较两个提交
# 选项
# --word-diff 旧的和新的比较显示在一行
difftool
使用外部工具查看修改。
$ git difftool # 查看所有文件的修改
$ git difftool <file> # 查看文件的修改
3.5 编辑与提交
add
将已修改文件移动到暂存区。
$ git add . # 添加所有文件到暂存区
$ git add <file> # 添加文件到暂存区
rm
删除文件。
$ git rm <file> # 删除文件和在仓库中的索引,放到暂存区等待提交
# 选项
# --cached 只删除仓库中的索引,不删除文件
# -r 删除文件夹
mv
重命名文件。
$ git mv <file1> <file2> # 重命名文件
commit
将暂存区的文件提交,提交务必带有提交信息。
$ git commit -m "..." # 提交暂存区的修改
# 选项
# -a 提交所有已追踪的修改,而不只是暂存区
# --amend 本次提交并入上一次提交
checkout
切换分支,或是将工作区的修改撤销。
$ git checkout <branch> # 切换分支
$ git checkout -b <branch> # 创建并切换分支
$ git checkout -- <file> # 撤销工作区文件修改
$ git checkout -- . # 撤销工作区所有文件修改
reset
将版本回退至某一提交,或是将暂存区的文件放回工作区。
$ git reset <commit> # 版本回退到某一提交
$ git reset HEAD <file> # 将暂存区文件放回工作区
$ git reset HEAD # 将暂存区所有文件放回工作区
clean
删除未追踪文件。
$ git clean -f <file> # 删除未追踪文件
$ git clean -fd <file> # 删除未追踪文件和目录
$ git clean -xfd <file> # 删除未追踪文件和目录和.gitignore的文件
stash
工作现场的保存和恢复。
如果当前有一些未提交的修改是有用的,但是还暂时不想提交一个分支,又要切换其他分支的时候,可以选择把工作现场保存,这样可以先做其他改动,或者切换其他分支,后面可以在任何一个分支将工作现场恢复,继续做修改。
$ git stash # 保存当前工作现场
$ git stash save "..." # 保存当前工作现场,注释说明
$ git stash list # 查看工作现场
$ git stash apply # 恢复所有工作现场
$ git stash apply stash@{0} # 恢复某个工作现场
$ git stash pop # 恢复所有工作现场,并删除保存
$ git stash pop stash@{0} # 恢复某个工作现场,并删除保存
$ git stash drop # 删除所有工作现场
$ git stash drop stash@{0} # 删除某个工作现场
$ git stash clear # 清空工作现场
blame
显示文件的每一行所对应的最新提交。
$ git blame <file> # 显示文件每一行的最新提交
3.6 标签
tag
标签。
$ git tag # 查看所有标签
$ git tag <tag> # 创建标签
$ git tag -d <tag> # 删除标签
show
显示各种类型的对象。
$ git show <tag> # 查看某个标签的信息
3.7 管理
gc
删除 Git 仓库中一些不需要的仓库管理文件。
$ git gc
4. 操作技巧
4.1 将分支回退到之前的提交
有时候做了错误的提交,然后想将当前分支回退到之前的某次提交。
这时可以对于想要回退的分支 test,基于之前的某次提交新建一个临时分支 test_tmp,将原本的分支 test 删除掉,再基于临时分支 test_tmp 新建回分支 test,最后将临时分支 test_tmp 删除。这时候分支就相当于回退到之前的某次提交了。