本文介绍Git的基础知识, 具体包括Git的初始化配置, 常用的基础指令, Git的基本原理等内容.

基本使用

初始化

1
2
3
git config --global user.name "Your Name"
git config --global user.email "email@example.com"
git config --global core.editor vim

前两条指令用于指定用户名和邮箱, 这些信息会显示在之后的提交信息之中, –global表明上述配置为全局有效. 完成上述配置以后才能使用Git.

第三条指令将git默认的编辑器设置为vim.

设置简称指令

如果经常使用命令行进行Git操作, 那么执行如下的指令来设置一些简称可能有助于提高指令的输入速度.

1
2
3
4
5
git config --global alias.st status
git config --global alias.co checkout
git config --global alias.ci commit
git config --global alias.br branch
git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"

更多简化方案可以参考如下的配置文件, 输入git config --global -e后在打开的文件中进行编辑即可生效.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[alias]
co = checkout
ci = commit
st = status
pl = pull
ps = push
dt = difftool
l = log --stat
cp = cherry-pick
ca = commit -a
cm = commit -m
b = branch
a = add .
s = stash

基础指令

指令 作用
git init 将当前文件夹初始化为一个git库
git add . 将当前目录和子目录下的所有改变提交
git add –all 将所有改变放入暂存区(包括父目录中的更改)
git add filename 将指定的文件提交
git commit -m “XXX” 使用-m参数指定本次提交的信息, 该信息保留在提交日志中, 从而用于识别提交的内容
git status 查看当前的状态, Git同时给出下一步可以执行的操作

基本概念与原理

Git的几个区域

Git中存在三个区域, 每个区域的名称和作用如下

名称 解释
工作区 与当前文件系统同步变化的区域工作区
暂存区 执行add之后后的文件存在的区域
版本库 执行commit操作以后的文件进入版本库

其中暂存区(staging area)也被称为缓冲区(buffer)或者索引(index).

Git基本原理

Git的三个区域都直接保存文件的快照, 而不是文件的修改. Git通过对比两个快照之间的差异来识别做出了什么更改. 当暂存区和工作区存在差异时提示有文件被修改(或者有新创建的文件), 当版本库和暂存区存在差异时提示有Changes to be committed

工作区的快照始终和当前的文件系统保持同步. 执行add操作后, 相应的文件就会同步到暂存区, 使得工作区和暂存区保持一致. 执行commit操作时, 暂存区的快照同步到版本库之中, 使得版本库和暂存区保持一致.

Git存储原理

每次执行commit操作后, 版本库都会有一个新的快照, Git会对此快照计算哈希值, 并且使用一个节点表示, 各次提交产生的节点通过链表的形式连接(类似比特币), 从而形成提交历史.

同时Git中还存在一个HEAD指针, 此指针指向当前版本库的状态, 通过修改此指针的指向就可以将版本库恢复到某个历史状态, 例如某个版本库中可能有如下的状态:

1
2
3
4
* f6264d3 <- master <- HEAD
* fae731a
* 104e8db
* 61cfc62

常用撤销操作

所有版本控制系统的本质都是创建一些安全的文件快照, 从而保证你的代码不会受到不可挽回的修改破坏. 因此Git中所有的撤销操作本质上都是基于对三个工作区的修改, 明确这一点有助于对Git撤销的原理的理解.

基本操作

操作类型 操作指令 含义
版本库 git reset --soft <loc> 版本库快照回退到指定的版本, 其他区域快照不变
版本库->暂存区 git reset --mixed <loc> 版本库和暂存区都回到指定版本, 其他区域不变
版本库->暂存区->工作区 git reset --hard <loc> 版本库, 暂存区, 工作区都回到版本
版本库->工作区 git checkout <loc> <file> 将指定文件的指定版本回退到工作区

其中<loc>表示某一个提交版本, 既可以使用HEAD表达, 也可以使用哈希值表达. <file>表示一个文件的文件名

原理分析

假设当前有如下的一组提交历史

1
D-C-B-A

即按照时间顺序依次提交了D,C,B,A四个版本, 当前处于A状态. 注意, 这意味着版本库, 暂存区以及工作区都处于A状态. 如果此时使用

1
git reset --mixed HEAD~

则版本库和暂存区回退到B状态, 但是工作区还维持A状态. 此时Git对比暂存区和工作区的区别, 就会将B到A之间的差异视为Changes not staged for commit, 这就如同将之前的提交撤回到了工作区, 从而可以重新组织需要提交的内容.

一些常见的撤回操作对应的指令如下所示:

撤销类型 指令 说明
撤回提交 git reset HEAD~ commit的内容撤回到工作区
撤回暂存 git reset HEAD 暂存区更改丢失,工作区代码不变
清除未跟踪文件 git clean -df 删除新创建的文件和文件夹
撤销工作区更改 git checkout -- filename 工作区更改完全丢失

HEAD是头指针,表示当前版本. HEAD表示上一个版本, 如果是之前的第100个版本, 则可写为HEAD100. 使用git status显示当前状态时, Git会提示上面的大部分指令, 因此这些指令并不需要记忆, 了解实现原理即可.

清理工作区

工作区与其他区域相比有一些不同, 在工作区存在新建文件和修改文件的区别. 虽然两种都会使工作区与暂存区产生差异, 但是新建的文件默认都是不跟踪的, 如果不执行add操作, 则Git默认不管理这些文件.

因此撤销工作区的更改, 有两个指令, 分别用于清除新添加的文件和恢复文件的修改. 对于git clean指令, 具有如下的参数

参数 含义
-d 清理文件夹
-f 强制清理, 通常情况下不指定此参数会拒绝执行
-n 模拟执行一次, 显示会删除的文件
-x 删除被.gitignore忽略的文件

对于git checkout, 既可以使用 git checkout -- filename也可以使用git checkout <loc> <file>, 两种格式是等价的.

远程撤销更改

1
git revert HEAD

使用后分支并不会被回退, 而是创建了一个新的提交, 该提交通过反向修改恢复了代码. 由于这本身也是一个提交操作, 因此即使以及有其他人在此分支上切出, 只要没有同时修改回退的内容, 就不会产生任何的影响. reset适用于私有分支,而revert可以适用于公共分支.

分支文件替换

git checkout指令可以将一个文件切换到指定的版本, 因此可以通过此指令将一个文件用另外一个分支的文件替换, 例如

1
git checkout test -- pom.xml

可以将当前分支的pom.xml文件替换为test分支上的相应文件.

参考资料

分支管理

查看现有分支

1
git branch

创建和切换分支

1
2
3
4
5
git branch        <分支名>                        //创建新分支
git checkout <分支名> //切换分支
git checkout digs <分支名> //从指定的ID创建分支
git checkout -b <分支名> //创建并切换分支
git checkout -b <本地分支名> <origin/远程分支名> //创建并拉取一个分支

分支合并和撤销

1
2
3
git merge branchName          //将指定分支的内容合并到当前分支
git merge --no-gg branchName //将指定分支的内容合并到当前分支(非快进合并)
git reset --merge //撤销本次合并

例如, 想要将dev分支的内容合并到master分支, 则应该按照如下步骤操作

1
2
git checkout master
git merge dev

快进合并

  • 假设当前有两个分支master和dev, 如果仅仅在dev分支上进行更改, 而在master上没有操作, 此时在master分支上进行合并, 则默认进行快速合并, 此时相当于将master的指针移动到和dev指针相关的位置
  • 如果使用非快进合并, 则相当于在master分支上进行一次commit, 此commit后的内容将和dev分支相同, 从而可以保留提交的相关信息, 以便于之后的查找

快进合并示意图

修改和删除分支

1
2
3
git branch -m branchName newBranchName    // 重命名分支
git branch -d branchName // 删除本地分支
git push origin --delete branchname // 删除远程分支

冲突合并

如果两个分支的修改存在冲突, 合并时会提示相关的冲突文件, 并且在相关的文件中留有标记, 因此可以手动到相关的文件中修改冲突的内容, 之后再次进行提交

注意:手动解决冲突不是必要的, 可以在冲突的文件中选择保留某个分支的修改, 也可以选择两个分支的更改都抛弃, 或者不进行任何更改

保存现场和恢复现场

1
2
3
4
5
git stash list    //列出所有的保存内容
git stash //保存当前工作区和暂存区的内容
git stash apply //应用栈顶的内容
git stash drop //删除栈顶的内容
git stash pop //应用并删除栈顶的内容
  • 通常情况下, 如果工作区或暂存区有未提交的内容时, 是不允许切换分支的
  • 使用上述命令可以先保存相关的操作, 切换分支处理优先级高的问题, 最后切换回来并恢复环境

参考资料

分支高级操作

变基操作

  • 例如有两个分支master和dev,master分支在A提交后分出dev分支, 之后进行了B提交, 在dev分支上进行了C和D提交,
  • 使用变基操作后, 相当于将dev分支从B上分出
  • 相当于对于C提交和D提交都进行合并
    1
    git rebase master

变基操作示意图

变基操作的冲突处理

  • 如果在变基过程中产生了冲突, 变基操作会终止, 并提示冲突原因
  • 此时需要手动处理相关冲突, 并add到缓冲区
  • 之后指定 git rebase –continue

移植分支

1
2
git chechout dev
git rebase master --onto release

移植分支示意图

从另一个分支提取指定的提交

使用如下的指令, 可以将一个指定的提交应用到当前分支.

1
git cherry-pick <commit-id>

删除冗余

1
git gc
  • 删除不可访问的提交
  • 改善文件保存结构
  • 使用gc命令可以节约硬盘空间

远程仓库

初始化SSH密钥

检查当前用户目录下是否有.ssh文件夹, 其中是否包含id_rsa和id_rsa.pub这两个文件, 如果有则说明已经创建过SSH密钥. 如果没有则执行如下的指令来创建密钥

1
ssh-keygen -t rsa -C "youremail@example.com"

指令执行过程中会询问几个问题, 全部使用默认值即可. 执行完毕后就可以在当前用户目录的.ssh文件夹下看到id_rsa和id_rsa.pub这两个文件.

添加SSH公钥

  1. 登陆GitHub, 进入个人主页, 选择设置选项后, 依次选择Account settings->SSH Keys
  2. 点击Add SSH Key, 添加任意Title, 在Key文本框里粘贴将上一节生成的id_rsa.pub文件的内容

注意: Title用来标识不同的Key的, 例如在不同的设备上有不同的Key, 则Title可以设置为设备的名称

完成上述操作后, 即可免密码的从GitHub上传和下载代码.

关联远程仓库

  • 在GitHub上创建相关的仓库, 如果为远程仓库为空, 本地仓库非空, 则可使用
    1
    git remote add origin git@github.com:XXX/YYY.git      //关联本地仓库和远程仓库
  • 若本地尚未建立仓库, 远程仓库非空, 则可使用
    1
    git clone git@github.com:XXX/YYY.git                  //将远程仓库内容拷贝到本地并关联

因为只在第一次使用时需要关联远程仓库, 所以不建议GitHub和本地同时存在内容时进行关联. 在这种条件下的关联操作非常繁琐, 强烈不建议尝试.

远程分支操作

一个本地的git仓库可以关联多个远程仓库, 并且每个仓库可以分别具有不同的用户权限.

操作 指令
查看已有仓库 git remote -V
添加 git remote add <name> <address>
删除 git remote remove <name>
重命名 git remote rename <old> <new>

Git的监视指令

查看记录

1
2
3
git log
git log --pretty=oneline
git reflog

直接使用log指令会展示详细的提交记录, 如果使用online参数, 则只显示提交ID和提交信息.

reflog相对于对git所有的操作进行了版本控制, 其中展示了所有git指令的哈希值, 从而可以方便的回滚git操作

查看更新

1
2
3
git diff
git diff filename
git diff --staged
  • 使用前两种指令将显示工作区中的内容和暂存区中内容的区别(尚未add的版本和最近一次add或commit的版本的区别)
  • 使用第三中指令将显示暂存区中内容和版本库中内容的区别

标签管理

添加标签

1
2
3
4
git tag                                //显示所有标签
git tag tagName //给最近一次提交添加标签
git tag tagName digs //给指定ID的提交添加标签
git tag -a tagName -m tagMessage digs //给指定ID的提交添加标签和详细信息

操作标签

1
2
3
git tag -d tagName            //删除指定的标签
git push origin tagName //推送指定的标签
git push --tags //推送所有的标签

私有Git服务器

本节介绍如何将Git仓库与本地其他位置的仓库关联. 当其他仓库位于可移动设备上时, 可以使用Git进行同步.

创建公共仓库

使用以下指令在需要的位置创建一个没有工作区的仓库

1
git --bare init

管理公共仓库

Git支持将远程仓库指定为本地的仓库, 因此使用和远程仓库相同的指令进行关联即可. 例如以下代码联了位于G:/files文件夹下的公共仓库

1
git clone /g/files

一旦和本地的仓库管理以后, 之后的操作与一般的远程仓库没有区别

Windows下可能的问题

Windows上不能直接在分区跟目录创建公共仓库. 如果执行这一操作, Windows会提示Permission denied.

产生这一错误的原因是Git创建仓库时, Git需要先创建文件夹, 而Windows会认为Git想要创建一个驱动器, 从而拒绝Git的操作. Git的操作被拒绝后, 又会认为是权限不够, 因此返回Permission denied的提示.

解决这一问题的方法就是先在根目录创建一个文件夹, 在此文件夹内再创建Git仓库. 详细的讨论可以参考这个回答

第三方私有仓库

虽然可以仅仅使用Git就可以搭建私有服务器, 但该方案过于简陋, 缺乏很多需要的功能. 此时可以考虑在服务器上部署第三方工具(例如Gogs), 从而实现类似Github的体验. 此外, Github目前也已经免费开发私有项目, 也可以考虑将代码以私有项目的形式放在Github上.

Win10创建可执行文件

使用如下的指令, 将foo.sh添加可执行权限.

1
git update-index --chmod=+x foo.sh

参考资料和扩展阅读

最后更新: 2024年04月18日 13:26

版权声明:本文为原创文章,转载请注明出处

原始链接: https://lizec.top/2017/08/07/Git%E7%AC%94%E8%AE%B0%E4%B9%8B%E5%9F%BA%E7%A1%80%E6%93%8D%E4%BD%9C/