花半天时间上手了 Git,跟正则表达式一样,大部分功能用不到,但用得到的地方非常有用。我用它搭配 Github、软链接、硬链接管理 Windows 里各种软件的配置文件、游戏存档文件。博客文章是用 markdown 本地写好再发出来的,准备把文章、配图用 git 同步到一个 github仓库做备份,也在摸索 Github Pages :)
安装
1. Linux安装
首先,输入git
看看系统有没有安装Git:
$ git
在 Ubuntu 或 Debian下,输入:
apt-get install git
2. windows 安装
打开 Git Bash,输入:
$ git config --global user.name "Your Name"
$ git config --global user.email "[email protected]"
创建版本库(repository)
首先,选择一个合适的地方,创建一个空目录:
mkdir learngit
cd learngit
pwd
#=> C:\Hi\gitone
pwd
命令显示当前目录,在我的PC上,这个仓库位于C:\Hi\gitone
第二步,通过git init
命令把这个目录变成Git可以管理的仓库.
git init
这时 gitone 就变成了Git仓库,并且多了一个跟踪管理版本库的.git
目录。
把文件添加到版本库
需要注意:版本控制系统只能跟踪本文文件的改动,比如第二个版本在第一个版本的第5行加了一个单词,不能跟踪非文本文件(图片、视频等)的变动内容
在 gitone 文件夹下新建readme.txt
,输入:
Git is a version control system.
Git is free software.
保存后,在Git Bash里用git add
命令,将文件添加到暂存区:
git add readme.txt
用git commit
将文件提交到版本库:
$ git commit -m "注释"
查看工作区状态:
git status
会做两次对比,工作区vs暂存区 及 暂存区vs版本库,并将两次对比的结果输出。
比较文件修改内容
git diff <file>
git diff
比较工作区和暂存区的文件,如果暂存区为空,则结果等同于 git diff HEAD
,即比较工作区和版本库的差异。如果要比较暂存区和版本库的差异用 git diff --cached
版本管理
log 可以告诉我们版本历史,会显示commit_id、创建人、邮件、版本时间、注释,当前版本后显示有(HEAD -> master)
git log <file>
按行显示版本历史,只显示 commit_id、注释
git log --pretty=oneline <file>
HEAD
表示当前版本,HEAD^
表示上一个版本,HEAD^^
表示上上版本,HEAD~100
表示往上100个版本。HEAD
是方便用于指代版本号的相对名称,也可以用 commit_id 来指代版本,commit_id 只需要用前几位字符,不需要写全。
将工作区当前版本的文件回退到上一个版本:
git reset --hard HEAD^
再用 git log 查看版本,会发现之前的版本不见了,如果要回到之前的版本,在命令行窗口还没有关掉时,可以用之前版本的 commit_id 恢复
git reset --hard commit_id
如果已经关闭命令行,可以用 reflog 查询曾经 commit 的 commit_id,reset 到该版本
git reflog
工作区和暂存区
git 的本地基本流程:
工作区 → 暂存区 → 版本库
add commit
reset
checkout/restore
reset HEAD/restore --staged
暂存区文件是 stage 或 index,Git 会自动创建第一个分支 master,git add
把工作区文件添加到暂存区,git commit
将暂存区的内容提交到当前分支。
撤销修改
将工作区文件回到最近一次 git add 或 git commit 时的状态:
git checkout -- <file>
如果文件已经 commit,我们可以将工作区文件 git reset –hard 到某个版本。
如果文件已经 add 但未 commit,只是想把暂存区修改回退到工作区:
git reset HEAD <file>
回退后工作区恢复到上次 add 的版本,暂存区被清空
git checkout 可以用 git restore 代替
git reset HEAD 可以用 git restore –staged 代替
删除文件
git rm <file>
git commit -m "注释"
添加远程库
在Github添加 repository 后,在本地新建存储库linux
并运行命令:
git remote add origin [email protected]:iliyz/linux.git
远程仓库的默认名称是origin
,也可以改成别的
关联远程库后,使用命令第一次推送 master 分支的所有内容,-u
会把本地当前 master分支的所有内容推送到远程库的master分支,还会把本地的master分支和远程的master分支关联起来
git push -u origin master
此后,每次本地提交后,只要有必要,就可以使用命令推送最新修改:
git push origin master
查看远程库信息
git remote -v
解除本地和远程的绑定关系
git remote rm origin
解除绑定后对本地文件的操作不影响远程库
从远程库克隆
git clone [email protected]:iliyz/linux.git
Git 支持多种协议,默认的git://
使用ssh,也可以使用 https 等其它协议.
git clone 会在本地新建目录。
创建 SSH Key
在用户主目录下,看看有没有.ssh
目录,有的话看目录下有没有id_rsa
和id_rsa.pub
这两个文件. 如果没有,打开 Shell(windows下打开 Git Bash),创建 SSH Key:
ssh-keygen -t rsa -C "[email protected]"
一路回车默认值即可,如果对保密要求较高,可以设置密码.
id_rsa
是私钥,不能泄露出去,id_rsa.pub
是公钥,可以放心告诉任何人.
分支管理
创建 dev
分支
git branch dev
查看分支
git branch
切换分支(也可以用 git checkout)
git switch <name>
切换分支需要当前分支下工作区、暂存区与分支存储库的提交相同,即 git diff HEAD 无差异,否则可能报错无法跳转,分支跳转后,工作区目录会回复到当前分支最近一次提交下的目录情况。
需要注意,切换分支时未add或未commit的内容会一并带过去,因为未add的内容不明确属于任何一个分支。
创建并切换到新创建的分支(也可以用 git checkout -b)
git switch -c <name>
合并某分支到当前分支
git merge <name>
删除分支
git branch -d <name>
解决冲突
当两个分支合并有冲突时,Git 会在冲突文件中用 <<<<<<<
,=======
,>>>>>>>
标记出不同分支的内容,必须手动解决冲突后再提交
可以用 git status 查看冲突的文件,用 git log
加上参数可以查看分支的合并情况
git log --graph --pretty=oneline --abbrev-commit
分支策略
Git默认使用Fast forward
模式合并分支,这种模式下,删除分支后,会丢掉分支信息。
A—B—C dev
/ \
D—————E main 合并后 D—————E main
可以强制禁用 Fast forward
模式,Git就会在merge时生成一个新的commit,并合并分支历史信息。--no-ff
模式仅当合并的历史是当前历史的后代时有效。
git merge --no-ff -m "merge with no-ff" dev
A—B—C dev
/ \
D—————E main 合并后 D–A–B–C–E main
常用的策略是,main
仅用于发布新版本,项目开发在 dev
分支,每个人都有自己的分支并不时向 dev
合并。
BUG分支
另一种常见情况是正在 dev
分支,突然被提交了一个需要马上修复的 bug,而当前 dev
分支的工作只进行到一部分,还不能提交。
这时可以用到 stash
功能,储藏当前工作现场,等以后恢复现场后继续工作:
git stash
再用 git status
查看工作区,是干净的
然后开始修复bug,假设在 main
分支做修复
git switch main
git switch -c issue-101
# 修复并提交
git switch main
git merge --no-ff -m "merged bug fix 101" issue-101
然后回到 dev
分支继续工作
git switch dev
git stash list # 查看储藏的工作现场
恢复工作现场,但不删除储藏的工作现场
git stash apply #如果有多个stash:git stash apply stash@{0}
删除保留的储藏的工作现场
git stash drop
恢复工作现场,并删除储藏的工作现场
git stash pop
这个bug已经在 main 分支做了修复,但其实在 dev 上也存在,假设要在 dev 分支修复相同的 bug,只需要把 issue-101 这个提交所做的修改复制到 dev 分支
git cherry-pick commit_id
Feature分支
如果要丢弃一个没有被合并过的分支,可以强行删除
git branch -D <name>
多人协作
查看远程库信息,显示了可以抓取和推送的 origin
地址。如果没有推送权限就看不到 push 地址
git remote -v
推送时,要指定本地分支。这样,Git就会把该分支推送到远程库对应的远程分支上,比如:
git push origin main
当协作者从远程库 clone 时,默认情况下只能看到本地的 main
分支
假设协作者创建了远程库的 dev 分支来进行开发
git checkout -b dev origin/dev
协作者就可以在 dev 上继续修改,时不时将 dev分支 push 到远程
假设协作者和你试图推送的提交有冲突,需要先从 origin/dev pull 最新的提交,然后,在本地合并,解决冲突,再推送:
git pull
提示no tracking information
是因为没有指定本地 dev
分支与远程 origin/dev
分支的链接,设置链接:
git branch --set-upstream-to=origin/dev dev
再次 pull,成功后手动解决冲突,再 push
git push origin dev
Rebase
git rebase
rebase操作可以把本地未push的分叉提交历史整理成直线,在查看历史提交的变化时更容易。
rebase会改变提交历史记录,这会影响到别人使用这一远程仓库,只建议对尚未推送或分享给别人的本地修改执行变基操作清理历史。
标签管理
标签比40位的 commit_id 更易读,比如 v1.0、v2.0,它相当于 commit_id 的别名
默认为当前分支最新提交的 commit 打标签,比如 v1.0
git tag v1.0
为指定的提交打标签
git tag v0.9 <commit_id>
为为指定的提交打标签并创建说明
git tag -a v0.8 -m "说明" <commit_id>
查看所有标签
git tag
查看标签信息
git show <tagname>
删除标签
git tag -d v1.0
创建的标签都只存储在本地,不会自动推送到远程,如果要推送到远程
git push origin <tagname>
或将全部尚未推送到远程的本地标签推送到远程:
git push origin --tags
删除远程标签,先删除本地标签,再 push 删除远程标签
git tag -d v1.0
git push origin :refs/tags/v1.0
自定义 Git
代码高亮
git config --global color.ui true
忽略特殊文件
git 会忽略工作区根目录下 .gitignore
文件中列出的文件
Github已经准备了各种不同环境下的配置文件,见github/gitignore
主要为系统自动生成的文件,如缩略图、desktop.ini等;编译生成的中间文件、可执行文件等;带有敏感信息的文件,如存放口令的配置文件
有时候想添加一个文件到 git失败,原因是 The following paths are ignored by one of your .gitignore files:
,要强制添加可以用-f
:
git add -f <file>
.ignore 的写法
# 排除所有 . 开头的隐藏文件,所有 .class文件,等等
.*
*.class
dist
build
db.ini
Desktop.ini
Thumbs.db
#不排除 .gitignore 和 App.class
!.gitignore
!App.class
搭建Git服务器
自建非常简单,大规模管理公钥用Gitosis,需要控制权限用Gitolite。