Git

Table of Contents

1. Git 基础

1.1. 环境配置

  • git config --global core.editor "vim" : 设置默认编辑器为 vim
  • git config --list : 查看现有的配置
  • git config color.ui always 设置 color,Emacs eshell 中 git diff 高亮
  • git config core.quotepath false : 解决中文文件名显示为数字问题
  • git config user.email your_email : 设置你的邮箱
  • git config user.name your_name : 设置你的用户名, 提交会显示

几个 alias 方便操作:

git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci commit
git config --global alias.st status

git config 加上 --global 表示全局配置,如果不加的话则会被认为是当前仓库的。

1.2. 基本操作

  • git add <filename> : 添加一个文件到 git 仓库中
  • git checkout -- <file> : 还原未暂存(staged)的文件
  • git commit --amend 修改最后一条提交
  • git commit -m "commit message": 提交到本地。 Git 提交信息规范指南
  • git init: 初始化一个 git 仓库
  • git log: 查看提交日志
    • --stat :显示每次更新的文件修改统计信息
    • --shortstat :只显示 –stat 中最后的行数修改添加移除统计
    • --name-status: 查看改动文件
    • --pretty= :设置日志输出格式
      • oneline 只展示一行
      • short medium full fuller email 展示不同程度的信息(不太实用)
      • raw 原始日志数据
      • format 格式化,比如 git log --pretty=format:"%h %ad | %s %d [%an]" --date=short
    • -p: 显示更多提交记录详情(类似 git diff ),~git log -p -2~ 显示最近两次
    • --author :仅显示指定作者相关的提交
    • --since, --after, --until, --before :仅显示指定时间之后、之前的提交
    • --date= 指定日期格式, relative 相对时间,更多参数命令行补全查看
  • git pull --rebase: 更新仓库 git pull = git fetch + git rebase ,You should use git pull --rebase when your changes do not deserve a separate branch 1
  • git pull: 更新仓库 git pull = git fetch + git merge
  • git push [remote-name] [branch-name] : 把本地的提交记录推送到远端分支
  • git remote set-url origin new_repo_addr 更换远端 repo 地址2
  • git reset HEAD <file>... : 取消暂存,那么还原一个暂存文件,应该是先 resetcheckout
  • git shortlog -sn 提交统计3
  • git stash : 隐藏本地提交记录, 恢复的时候 git stash pop 。这样可以在本地和远程有冲突的情况下,更新其他文件
  • git stash --list: 查看储藏文件状态

git clone 提示4

Cloning into 'project'… Unable to negotiate with 192.168.4.100 port 22: no matching host key type found. Their offer: ssh-rsa,ssh-dss fatal: Could not read from remote repository.

Please make sure you have the correct access rights and the repository exists.

解决办法:在 ~/.ssh/config 中添加:

Host git.xxx.com
    User git
    PubkeyAcceptedAlgorithms +ssh-rsa
    HostkeyAlgorithms +ssh-rsa

1.3. 分支

  • git branch: 查看所有本地分支
    • -v 显示更多信息
    • -a 查看本地和远端分支
    • <branch-name> : 基于当前 commit 新建一个分支,但是不切换到新分支
  • git checkout <branch-name> :切换分支
    • -b <branch-name> :新建并切换分支
    • -d <branch -name> :删除本地分支
    • -b <local-branch-name> origin/<origin-branch-name> : 基于某个远程分支新建一个分支开发
    • --track origin/<origin-branch-name> : 跟踪远程分支(创建跟踪远程分支,Git 在 git push 的时候不需要指定 originbranch-name ,其实当我们 clone 一个 repo 到本地的时候, master 分支就是 origin/master 的跟踪分支,所以提交的时候直接 git push )。
  • git push origin <branch-name> : 推送本地分支
  • git push origin :<origin-branch-name> : 删除远程分支

如何放弃所有本地提交/改动,强制更新?

git fetch --all
git reset --hard origin/master
git pull

远端分支被别人删除,自己本地 git br -a 还是能看到怎么办?

git remote prune origin 5

1.4. 标签

1.4.1. 查看标签

  • git tag 列出所有标签,标签多时可模糊匹配, git tag -l 'v0.1.*'
  • git show v0.1 可查看标签明细,对于轻量标签只是指向某次提交记录的引用,而附注标签则会显示附注等相关信息
  • 查看标签对应的 hash 值(提交记录):
    • git show-ref --tags
    • git log --oneline --decorate --tags --no-walk
    • git log --tags --no-walk --date=iso-local --pretty='%C(auto)%h %cd%d %s 在上面的基础上显示创建时间

1.4.2. 创建标签

标签分两种:轻量标签和附注标签,轻量标签是不会改变的标签,只是一个特定提交的引用。而附注标签是一个提交记录,包含附注信息和提交人的信息(名字、电子邮件、日志等)。

  • git tag v1.0 :创建轻量标签
  • git tag -a v1.0 -m 'tag remark info' :创建附注标签,和提交更改类似,指定 tag 命令的 -a 选项

标签可以相对于最新的提交创建,也可以相对于某次提交记录,只需要在创建标签时添加提交记录的校验值( git log --pretty=oneline 快速查看提交记录)。

1.4.3. 推送标签

git tag 设置的标签只是在本地有效,如果想要和别人共享的话(版本发布),需要推送到远端分支。

git push origin v1.0 会把 v1.0 推送到远端服务器。带有 --tagsgit push 命令会将所有本地的 tag 推送到远端。

1.4.4. 创建分支

可以基于某个 tag 创建分支, git checkout -b branch_name v0.1

其实这个很好理解,不管是轻量标签还是附注标签,本质上对应的都是某一次的提交记录。既然可以通过提交记录创建分支,自然就可以通过某个 tag 创建分支。

1.4.5. 删除标签

  • git tag -d v0.1 :删除本地标签
  • git push origin :v0.1 :删除远端标签

可以看到分支和标签非常类似,一些操作命令也很像,那么分支和标签的区别是什么?How is a tag different from a branch in Git? Which should I use, here?:一句话,tag 一般代表发布节点,不会被修改;branch 一般针对不同功能的开发分支,这更像是一种约定。

1.5. 代码回退

  1. 使用 git log 查看提交的 hash 值
  2. git reset --hard hash

可在本地回退到某一次提交,撤掉回退 git pull 即可。

如果想回退到远端分支的某一次提交,需要强制推送到远端分支: git push origin master -f

回退未纳入版本管理的文件和目录: git clean -fd, f 表示文件, d 表示目录6

1.6. 子模块

当一个项目引用另外一个 Git 项目时,你又不想只直接把另外一个项目的代码作为本项目的一部分(这样的话,就没法实时更新依赖项目了),Git 为此提供了 submodule 工具。

将 xxx 项目添加到 yyy 目录下: git submodule add git://xxx.git yyy/xxx

添加完之后,在本项目目录下会新增一个 .gitmodules 文件,用来记录本地子项目目录和远端 url 的对应关系。之后你需要将子模块映射关系提交到本项目远端。

克隆一个带子模块的项目时,得到的是一个空项目,你需要运行两个命令:

  • git submodule init :初始化本地配置文件
  • git submodule update :拉取子模块
  • git submodule update --remote --merge :更新 submodule

1.7. 合并改动

  • 切换到接受合并的分支上: git checkout master
  • 执行合并命令: git merge dev1

冲突处理

撤销合并: git merge --abort

合并单次提交

git cherry-pick commit-id

1.8. 代理

http.https 协议:

git config http.proxy http://127.0.0.1:7890
git config https.proxy http://127.0.0.1:7890

取消代理:

git config --unset http.proxy
git config --unset https.proxy

对于 ssh 协议,在 ~/.ssh/config 中添加:

Host github.com
ProxyCommand nc -X 5 -x 127.0.0.1:1080 %h %p  # socket5
ProxyCommand socat - PROXY:127.0.0.1:%h:%p,proxyport=8080  # http

1.9. 编码自动转换

# commit 时转换为 LF, checkout 转换为 CRLF
git config --global core.autocrlf true
# commit 时转换为 LF, checkout 不转换
git config --global core.autocrlf input
# commit/checkout 都不做处理
git config --global core.autocrlf false

一般选择 input 最合理。

2. 知识点

基本命令让你快速的上手使用 Git,知识点能让你更好的理解 Git。

2.1. 文件的几种状态

  • untracked:未被跟踪的,没有纳入 Git 版本控制,使用 git add <filename> 纳入版本控制
  • unmodified:未修改的,已经纳入版本控制,但是没有修改过的文件
  • modified:对纳入版本控制的文件做了修改,git 将标记为 modified
  • staged:暂存的文件,简单理解: 暂存文件就是 add 之后,commit 之前的文件状态

理解这几种文件状态对于理解 Git 是非常关键的(至少可以看懂一些错误提示了)。

2.2. 快照和差异

详细可看:Pro Git: Git基础 中有讲到 直接记录快照,而非差异比较 ,这里只讲我个人的理解。

Git 关心的是文件数据整体的变化,其他版本管理系统(以 svn 为例)关心的某个具体文件的*差异*。这个差异是好理解的,也就是两个版本具体文件的不同点,比如某一行的某个字符发生了改变。

Git 不保存文件提交前后的差异,不变的文件不会发生任何改变,对于变化的文件,前后两次提交则保存两个文件。举个例子:

SVN:

  1. 新建 3 个文件 a, b, c,做第一次提交 -> version1 : file_a file_b file_c
  2. 修改文件 b, 做第二次提交(真正提交的是 修改后的文件 b 和修改前的 file_b 的 diff) -> version2: diff_b_2_1
  3. 当我要 checkout version2 的时候,实际上得到的是 file_a file_b+diff_b_2_1 file_c

Git:

  1. 新建 3 个文件 a, b, c,做第一次提交 -> version1 : file_a file_b file_c
  2. 修改文件 b (得到~file_b1~), 做第二次提交 -> version2: file_a file_b1 file_c
  3. 当我要用 version2 的时候,实际上得到的是 file_a file_b1 file_c

上面的 file_a file_b1 file_c 就是 version2 的 快照

3. 学习资源

Footnotes:

First created: 2014-04-05 00:00
Last updated: 2022-12-11 Sun 12:49
Power by Emacs 29.0.91 (Org mode 9.6.6)