跳到主要内容

Git

一、 前言

学习资料:git 官网

1.为什么要学习 Git

Git 是一个分布式版本控制系统。

二、Git 优点

  • 完全分布式
  • 能高效管理大型项目
  • 非线性分支管理系统(Git 分支)
  • 直接记录快照,而非差异比较
  • 近乎所有的操作都是本地执行
  • Git 保证完整性(Git 的数据在存储前都要计算校验和)
  • Git 一般只添加数据

2、初识 Git

1、已提交、已修改、已暂存

  • 三种状态
    • 已提交 commited : 数据已安全地保存在数据库中。
    • 已修改 modified : 修改了文件,但还没保存到数据库中。
    • 已暂存 staged : 对一个已修改文件的当前版本做了标记,使之包含在下次提交的快照中。

2、工作区、暂存区、git 目录

工作区是对项目的某个版本独立提取出来的内容。 这些从 Git 仓库的压缩数据库中提取出来的文件,放在磁盘上供你使用或修改。

暂存区是一个文件,保存了下次将要提交的文件列表信息,一般在 Git 仓库目录中。 按照 Git 的术语叫做“索引”,不过一般说法还是叫“暂存区”。

Git 仓库目录是 Git 用来保存项目的元数据和对象数据库的地方。 这是 Git 中最重要的部分,从其它计算机克隆仓库时,复制的就是这里的数据。

流程

  1. 在工作区修改文件
  2. 将要提交的文件暂存(添加到暂存区)
  3. 将暂存区的文件提交(提交到 Git 目录/仓库)

三、Git 环境配置

1、安装

具体过程见:

2、初次运行 Git 前的配置

1、git config

文件位置(三个地方,优先级由低往高依次是)

  • /etc/gitconfig 修改这个文件时需要使用 git config --system (需要 root 权限)
  • ~/.gitconfig 或者 ~/.config/git/config : 只对当前用户有效,修改时需要使用 git config --global (对系统上当前用户所有的仓库生效)
  • 当前 git 目录的 config 文件 (.git/config) : 使用 git config --local 修改
# 查看所有的配置,以及所在的文件
git config --list --show-origin
# 设置用户信息
git config --global user.name "wangzhy"
git config --global user.email "iwangzhy@gmail.com"
# 指定文本编辑器
git config --global core.editor vim
# 查看所有的配置
git config --list
# 查看指定配置
git config user.name
# 查看帮助信息
git help config
git config -h

四、Git 基础

1、获取 Git 仓库

  • 自己初始化一个 Git 仓库
    • git init
  • 克隆一个 Git 仓库
    • git clone 会把服务器上的每一个文件的每一个版本都拉取下来
git clone https://github.com/iwangzhy/picgo

2、记录每次更新到仓库

工作目录下的文件只有两种状态

  • 跟踪 tracked
  • 未跟踪 untracked

1、检查当前文件状态

git status

2.跟踪新文件

git add filename 文件状态会变为 Staged (使用 git status 会显示在 Changes to be committed 下面)

3.暂存已修改的文件

如果修改了已经被跟踪的文件,使用 git status 命令,会显示在 Changes not staged fot commit 下面

4、git add

  • 跟踪新文件
  • 把已跟踪的文件添加到暂存区
  • 合并时把有冲突的文件标记为已解决状态

5、git status -s

简化输出

  • ?? :新添加的未跟踪的文件
  • A :新添加到暂存区中的文件
  • M :修改过的文件

左边暂存区,右边工作区

 M 工作区文件已修改,但未暂存
MM 工作区文件已修改,且暂存后又做了修改
A 新添加暂存区的文件
M 文件被修改,且暂存
?? 未被跟踪的文件

6、.gitignore文件

.gitignore 文件规范

  • 所有空行或者以#开头的行都会被 Git 忽略。
  • 可以使用标准的 glob 模式匹配,它会递归地应用在整个工作区中。
  • 匹配模式可以以(/)开头防止递归。
  • 匹配模式可以以(/)结尾指定目录。
  • 要忽略指定模式以外的文件或目录,可以在模式前加上叹号(!)取反。
 * 匹配任意字符
[abc] abc 中的任意一个字符
? 匹配任意一个字符
[0-9]
** 匹配任意中间目录 例如 a/**/z 可以匹配 a/z、 a/b/z、 a/b/c/z

各种语言的 .gitignore 模板

Java.gitignore

# Compiled class file
*.class

# Log file
*.log

# BlueJ files
*.ctxt

# Mobile Tools for Java (J2ME)
.mtj.tmp/

# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar

# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
replay_pid*

7、查看已暂存和未暂存的修改

git diff 比较工作目录中当前文件和暂存区域快照之间的差异,也就是修改之后还没暂存起来的变化内容 git diff --staged 查看已暂存的将要添加到下次提交里的内容

  • 当前做的哪些更新尚未暂存? 通过 git diff
  • 有哪些更新已暂存并准备好下次提交? 通过 git diff --staged git diff --cachedgit diff --staged 等效

8.提交更新

git commit 会弹出编辑器(core.editor),要求输入提交信息。 git commit -m "commit message" 指定提交信息

提交时记录的是放在暂存区域的快照。 任何还未暂存文件的仍然保持已修改状态,可以在下次提交时纳入版本管理。 每一次运行提交操作,都是对你项目作一次快照,以后可以回到这个状态,或者进行比较。

9.跳过使用暂存区

git commit -a 会自动把所有已经跟踪过的文件暂存起来并提交。

11.移动文件

git mv file_from file_to 等价于

mv file_from file_to
git rm file_from
git add file_to

10.移除文件

git rm filename 从工作目录中删除指定文件(会删除文件) git rm -f filename 删除指定文件(已经修改或已经添加到暂存区的文件) git rm --cached filename 让指定文件不在让 git 跟踪,还保留在磁盘中。

3、查看提交历史

git log 
git log -p -2
git log --stat
# --pretty 的值有 oneline、short、full、fuller
git log --pretty=oneline

git log --pretty=format:"%h - %an , %ar : %s"

git log --pretty=oneline --graph

git log --since=2.weeks

git log --pretty=format常用的选项

4.撤销操作

1.修改 commit message

# 注意,这个命令会将暂存区的文件提交
git commit --amend

2.取消暂存的文件

# 取消指定文件的暂存
git reset HEAD <file>...

git reset HEAD hello.md readme.md

3.撤销对文件的修改

# 下面两条命令等价,都是撤销之前的修改
git checkout -- readme.md
git restore readme.md

5.远程仓库

1.查看远程仓库

# clone 一个远程仓库
git clone https://github.com/iwangzhy/picgo
# 列出你指定的每一个远程服务器的简写
git remote
git remote -v

2.添加远程仓库

# 添加远程仓库
git remote add myrepo https://github.com/iwangzhy/picgo
# 添加之后,就可以用 myrepo 代替 iwangzhy/picgo 了
git fetch myrepo

3.拉取代码

git fetch <remote>

git pull

4.推送至远程仓库

git push <remote> <branch>
git push origin master

5.查看某个远程仓库

git remote show origin

6.重命名和删除

git remote rename myrepo picgo
git remote remove picgo

6.打标签

git tag
git tag -l 'v1.1.1*'

1.创建标签

  • 轻量标签 lightweight
git tag v1.1-lt
  • 附注标签 annotated (需要加上 -a )
git tag -a v1.1 -m 'my version 1.1' 
  • 后期打标签
git log --pretty=oneline
git tag -a v1.2 qgdf2s
git tag
  • 共享标签
git push origin <tagname>

git push origin v1.3

# 一次推送很多标签
git push origin --tags
  • 删除标签
git tag -d v1.2
  • 检出标签
git checkout v1.3

# 创建一个新的分支,在 v1.3 处
git checkout -b version3 v1.3

7.Git 别名

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 alias.unstage 'reset HEAD --'
# 查看最后一次提交
git config --global alias.last 'log -1 HEAD'
# 给非 git 命令设置别名需要加上 !
git config --global alias.visual '!gitk'

五、Git 分支

1.分支简介

git 保存的是一系列不同时刻的快照

1、commit object 提交对象

git 在提交的时候,会创建一个提交对象,包含下面几个属性

  • 一个执行暂存内容快照的指针
  • 作者的名称、邮箱,以及提交信息等
  • 父对象的指针
    • 0 个:第一次提交
    • 1 个: 某一个提交(没有其他分支)
    • n 个:在当前提交处有 n 个分支合并

2、git add

  1. 为每一个暂存的文件计算校验和(SHA-1 哈希算法)
  2. 把当前版本的文件快照保存到git仓库中(用 blob 对象保存)
  3. 将校验和加入到暂存区等待提交

3.git commit

  1. 计算每一个子目录的校验和
  2. 将这些校验和保存为树对象
  3. 创建一个提交对象

所以 git 分支的本质上就是一个指向提交对象的可变指针。

4、git branch

#创建一个分支
git branch testing # 只是创建一个新的分支,不会切换到这个分支上

5、git checkout

# 切换分支
git checkout testing

HEAD 指针:指向当前所在的本地分支的指针。即HEAD 指向谁(分支),谁就是当前分支。 git checkout testing,干了两件事

  1. 切换 HEAD 指针指向 testing
  2. 将 工作区 恢复到 testing 的快照内容

6、git log 查看分支

git log --oneline --decorate
git log --oneline --decorate --graph --all

2、一个工作中很常见的例子

现在有一个 master 分支,为现场环境所使用的分支。 你现在在 dev 分支上,执行开发任务 Task2 ,此前,已完成了开发任务 Task1,并且已经提交到了 master 分支上。

现在,有人报告,之前完成的 Task1 有 BUG ,需要你立即解决。

你现在所需要做的是

  1. 提交或者暂存 dev 分支上的修改(stage 或 commit)
# 假设我们是提交当前 dev 分支上的修改
git commit -a -m "stage task2"
  1. 切换到 master 分支
git checkout mastet
  1. 创建一个新分支
git branch issue1
git checkout issue1
  1. 在新的分支上解决这个 BUG,并提交
  2. 切换回 master 分支
git checkout master
  1. 合并 issue1 分支
git merge issue1
# 现在 issue1 分支没有用处了,应该删除
git branch -d issue1
  1. 切换回 dev 分支
git checkout dev
  1. 完成 task2 ,并提交(假设修改了 issue1 所改动的文件,即现在产生了冲突)
  2. 切换 master分支
  3. 合并 dev 分支
git merge dev

控制台会提示合并失败,有冲突。

  1. 使用 git status 可以查看到冲突的文件

  2. 通过 cat work.txt 可以查看到差异

  3. 需要手动修改 work.txt 文件,解决差异

  4. 修改 work.txt 之后,还需要将 work 添加到暂存区(git add work.txt

  5. 将这些修改提交

git commit -m 'complete task2'
  1. 到此,bug 解决了,task2 开发完成了。

3.分支管理

git branch
git branch -v
# --merge --no-merge 来过滤合并和未合并的分支
git branch --merge
# 删除分支
git branch -d testing
# 有的时候,删除分支会失败(有修改未合并),就需要使用 -D 来删除
git branch -D testing

4.分支工作流

  • master 分支:只保留完全稳定的代码(已发布或待发布的代码)
  • develop 分支:后续开发或稳定性测试(会在稳定之后合并到 master 分子)
  • issue 分支:解决bug (短期分支)
  • proposed/pu proposed updates 分支:存放不成熟的内容的代码
  • 主题分支:在解决 issue 的时候,可能会有不同的方案,这个时候就可以创建主题分支 issuev2,解决之后合并到 master 分支上。

5.远程分支

# 获取远程仓库的所有分支的列表
git ls-remote <remote>
# 展示某个远程仓库的分支列表
git remote show <remote>
# 跟踪远程仓库
git fetch <remote>
# 推送本地分支到远程仓库
git push <remote> <branch>
# 推送本地分支到远程仓库的同时指定远程仓库的名称
git push <remote> <local_branch>:<remote_branch>

如何避免每次都要输入密码?

git config --global credential.helper cache
# git fetch 并不会在本地创建一个新的分支,只是会创建一个不可修改的 origin/serverfix 指针
git fetch origin
# 合并到当前所在的分支
git merge origin/serverfix
# 建立本地分支 serverfix

git checkout -b serverfix origin.serverfix

跟踪分支

# 拉去跟踪分支
git pull
#
git checkout --track origin/serverfix
# 根据远程仓库的 serverfix 分支,创建 serverfix 本地分支,并切换到 serverfix 分支
git checkout serverfix
# 根据 serverfix 创建一个 sf 分支,并切换到 sf 分支
git checkout -b sf origin/serverfix

git branch -u origin/serverfix
# 查看所有正在跟踪的分支(与远程仓库的分支的差异比较)
git branch -vv

# 先拉取/跟踪所有远程仓库的分支,再查看差异
git fetch --all;git branch -vv;

# 删除远程分支 serverfix
git push origin --delete serverfix

6.变基 rebase

具体的流程、说明可看变基 过程

  • 首先找到这两个分支(即当前分支experiment、变基操作的目标基底分支master) 的最近共同祖先C2
  • 然后对比当前分支相对于该祖先的历次提交,提取相应的修改并存为临时文件
  • 然后将当前分支指向目标基底C3, 最后以此将之前另存为临时文件的修改依序应用。
# 执行 rebase 
git checkout experiment
git rebase merge

# 合并到 master 分支中
git checkout master
git merge experiment

# 取出 `client` 分支,找出它从 `server` 分支分歧之后的补丁, 然后把这些补丁在 `master` 分支上重放一遍,让 `client` 看起来像直接基于 `master` 修改一样
git rebase --onto master server client

git rebase <basebranch> <topicbranch>
git rebase master server

git pull --rebse
# 等效于
git fetch
git rebase teamone/master

如果提交存在于你的仓库之外,而别人可能基于这些提交开发,那么不要执行变基操作(只在未推送到远程仓库的提交中执行变基操作)

六、服务器上的 Git

一个远程仓库通常只是一个裸仓库bare repositpry --没有当前工作目录的仓库。

1.协议

1.本地协议 local protocol

远程版本库就是同一主机上的另一个目录

git clone /srv/git/project.git
git clone file:///srv/git/project.git

2.HTTP 协议

  • 智能 HTTP 协议
  • 哑 HTTP 协议
    • 需要把 bare repository 放到 HTTP 目录中
    • 设置 post-update
cd /var/www/htdocs
git clone --bard /path/to/git_project gitproject.git
cd gitproject.git
mv hooks/post-update.sample hooks/post-update
chmod a+x hooks/post-update

3.SSH 协议

ssh 协议也是一个验证授权的网络协议。

git clone ssh://[user@]server/project.git
git clone [user@]server:project.git

4.Git 协议

包含在 git 里的一个特殊的守护进程。 9418 端口。

优点

  • 速度快 缺点
  • 没有权限控制

2.在服务器上搭建 Git

  1. 把现有仓库导出为裸仓库
# 需要在 my_project 的父文件夹中执行这个命令
git clone --bare my_project my_project.git
# 等同于
cp -Rf my_project/.git my_project.git
  1. 把裸仓库放到服务器上
scp -r my_project.git user@git.example.com:/srv/git

# 修改仓库目录的组权限为可写
git init --shared
  1. ssh 连接
# 创建 git 用户
# 将 ssh 秘钥写入 ~/.ssh/authorized_keys

3、搭建 Git 服务器

  1. 生成 ssh 公钥
cd ~/.ssh
ssh-keygen -o
  1. 配置服务器
sudo adduser git
su git
mkdit .ssh && chmod 700 .ssh
touch .ssh/authorized_key && chmod 600 .ssh/authorized_keys
  1. 将公钥写入 authorized_keys 中
cat /tmp/id_rsa.john.pub >> ~/.ssh/authroized_krys
  1. 创建一个裸仓库
cd /srv/git
mkdir project.git
cd priject.git
git init --bare
  1. 推送代码至远程库
git clone git@xxx.com:/srv/git/project.git
cd project
# xxxxx

git commit -am 'fix for the readme file'
git push origin master

七、分布式 Git

1、分布式工作流

1、集中式工作流

公司目前就是使用这种,在提交代码之前,需要先合并别人提交的代码。不然就会提示 non-fast-forward.

2.集成管理者工作流

具体流程

  1. 项目维护者推送到主仓库。
  2. 贡献者克隆此仓库,做出修改。
  3. 贡献者将数据推送到自己的公开仓库。
  4. 贡献者给维护者发送邮件,请求拉取自己的更新。
  5. 维护者在自己本地的仓库中,将贡献者的仓库加为远程仓库并合并修改。
  6. 维护者将合并后的修改推送到主仓库。

3、主管与副主管工作流

  1. 普通开发者在自己的主题分支上工作,并根据master分支进行变基。 这里是主管推送的参考仓库的master分支。
  2. 副主管将普通开发者的主题分支合并到自己的master分支中。
  3. 主管将所有副主管的master分支并入自己的master分支中。
  4. 最后,主管将集成后的master分支推送到参考仓库中,以便所有其他开发者以此为基础进行变基。

2.向一个项目贡献

1、提交准则

  • 不应该提交任何空白错误 git diff --check 命令可以检查出来空白错误。
  • 尽量让每一次提交在逻辑上独立。即一次提交只解决一个问题。
    • git commit --amend
    • git commit --squash
  • 提交信息要规范,简练 git log --no-merges
首字母大写的摘要(不多于 50 个字符)

如果必要的话,加入更详细的解释文字。在大概 72 个字符的时候换行。
在某些情形下,第一行被当作一封电子邮件的标题,剩下的文本作为正文。
分隔摘要与正文的空行是必须的(除非你完全省略正文),
如果你将两者混在一起,那么类似变基等工具无法正常工作。

使用指令式的语气来编写提交信息:使用“Fix bug”而非“Fixed bug”或“Fixes bug”。
此约定与 git merge 和 git revert 命令生成提交说明相同。

空行接着更进一步的段落。

- 标号也是可以的。

- 项目符号可以使用典型的连字符或星号,后跟一个空格,行之间用空行隔开,
但是可以依据不同的惯例有所不同。

- 使用悬挂式缩进

2、私人小团队的代码提交

可以看这个例子。 ![[#2. 一个工作中很常见的例子]]

3、私有管理团队

分配任务:

  • A、W 开发功能A
  • B、W 开发功能B 说明:A、B、W 只有 master 的读权限。

工作开始

W 拉取 master 分支,创建 featureA 分支,工作,提交代码到 featureA 中,并推送至远程仓库

git fetch origin --all
git checkout -b featureA
...work...
git commit -am 'add limit to log function'
git push -u origin featureA

W 在将代码推送至远程仓库后,邮件通知 A 之后,再基于 master 分支,创建 featureB 分支。

git fetch master
git checkout -b featureB origin/master
...work...
git commit -am 'add ls-files'

在 W 准备推送 featureB 分支到远程仓库的时候,B 告诉 W 他已经将 featureBee 推送到了远程仓库。这时候 W 需要拉取 远程仓库代码。并合并自己的提交,并推送至远程仓库的 featureBee 分支上。

git fetch origin
git checkout featureB
git merge origin/featureBee
# -u 是 --set-upstream 的缩写。该标记会为之后轻松地推送与拉取配置分支
git push -u origin featureB:featureBee

在 W 推送代码至远程仓库之后,A 告诉 W 他推送了一些改动到 featureA 分支上。 W 代码审查通过之后,合并到本地。

git fetch origin
# 查看新的提交
git log featureA..origin/featureA
# 如果 W 觉得可以,就合并远程仓库 featureA 分支的代码到本地
git checkout featureA
git merge origin/featureA

W 在本地 featureA 分支上修改了一些代码,并推送至远程仓库。

...work...
git commit -am 'small tweak'
# 这里直接 git push 就可以了,因为之前执行过 git push -u
git push

在 A、B、W 将功能 A 、功能B 开发完之后,通过集成管理者,来合并 featureA 和 featureBee 分支到 master分支上。

4、派生的公开项目

git remote add myfork <url>
# 推送修改到 fork 的仓库
git push -u myfork featureA

pull request 拉取请求

# pull request 
# 将下面的命令的输出手动发送给远程仓库的集成管理者。
git request-pull origin/master myfork

本地开发另外一个功能,可以在 origin/master 基础上创建一个 featureB 分支

git fetch origin
git checkout -b featureB origin/master
...work...
git commit -am 'new featureB'
git push myfork featureB
git request-pull origin/master myfork

集成管理者拉取其他分支的代码后,开始拉取你提交的 featureA 分支的代码。这个需要你解决冲突。(因为远程仓库已经合并了别人的代码,你这个 featureA 分支的起点并不是最新代码)把 featureA 变基到 master 的顶部。

git checkout featureA
git rebase origin/master
# 因为你将分支变基了,所以必须为推送命令指定 `-f` 选项,这样才能将服务器上有一个不是它的后代的提交的 `featureA` 分支替换掉
git push -f myfork featureA

改变 featureB 的实现

git checkout -b featureBv2 origin/master
# `--squash` 选项接受被合并的分支上的所有工作,并将其压缩至一个变更集, 使仓库变成一个真正的合并发生的状态,而不会真的生成一个合并提交
git merge --squash featureB
git commit
git push myfork featureBv2

5、通过邮件的公开项目

3.维护项目

1.在主题分支中工作

主题分支中主要是一些实验性的代码。

# 分支名是 wangzhy/ruby_client
git branch wangzhy/ruby_client master
git checkout -b wangzhy/ruby_client master

2.应用来自邮件的补丁

  • git apply
    • git apply /tmp/patch-ruby-client.patch
    • git apply --check 0001-seeing-if-this-helps-the-gem.patch
  • git am
    • git am 0001-limit-log-function.patch

3、检出远程分支

git remote add jessica <url>
git fetch jessica
git checkout -b rubyclienct jessica/ruby-client

八、GitHub(暂时跳过,等有需要的时候再来复习)

#TODO 跳过,等到后面需要的时候,再来学习。

九、Git工具 (这一章多复习复习)

git工具:

1、选择修订版本

1.单个修订版本

  • 指定 40 位的 SHA-1
  • 指定 SHA-1 的前几位(至少4位,且没有歧义)
git log --pretty=oneline

# 下面三条命令等效
git show c002dd4b536e7479fe34593e72e6c6c1819e53b
git show c002dd4b536e747
git show c002

--abbrev-commit : 输出结果会显示简短且唯一的值

git log --abbrev-commit --pretty=oneline

2.分支引用

git show branch_name

查看某个分支指向哪个特定的 SHA-1

git rev-parse master

3.引用日志

引用日志只存在于本地仓库,它只是一个记录你在自己的仓库里做过什么的日志。其他人拷贝的仓库里的引用日志不会和你的相同,而你新克隆一个仓库的时候,引用日志是空的,因为你在仓库里还没有操作

git reflog
git reflog HEAD@{5}
git show master@{yesterday}
git log -g master
git show HEAD@{2.months.ago}

4.祖先引用

git log --pretty=format:'%h %s' --graph

^ 用于合并提交,后面添加一个数字来指明想要哪一个父提交。 合并提交的第一父提交是你合并时所在分支(通常为master),而第二父提交是你所合并的分支

git show HEAD^
git show HEAD^2

~ 指向第一父提交 HEAD^ 与 HEAD~ 等价 HEAD~2 表示第一父提交的父提交,即表示祖先提交

HEAD~3^2 表示HEAD的前第三次提交的第二父提交。

5.提交区间

用提交区间来解决“这个分支还有哪些提交尚未合并到主分支?”的问题

  • 双点:可以让 git 选出在一个分支中而不在另一个分支中的提交。
# 查看在 experiment 分支,而不在 master 分支中的提交
git log master..experiment

# 查看即将推送到远端的内容
git log origin/master..HEAD
等效于
git log origin/master..
  • 多点:查看哪些提交是被包含在某些分支中的一个,但是不在你当前的分支上
# 在 refB 中不在 refA 中
# 下面三个命令等价
git log refA..refB
# 被 refB 包含,不被 refA 包含
git log ^refA refB
git log refB --not refA

你想查看所有被refArefB包含的但是不被refC包含的提交

git log refA refB ^refC
git log refA refB --not refC
  • 三点:可以选择出被两个引用之一包含但又不被两者同时包含的提交(非交集的提交)
git log master...experiment

--left-right 显示每个提交到底处于哪一侧的分支

git log --left-right master...experiment

2.交互式暂存

git add -i

git add -p
git add --patch

git reset --patch # 部分重置文件
git checkout --patch # 部分检出文件
git stash save --patch 部分暂存文件

3.贮藏与清理

git stash 使用场景:想切换分支,但是又不想为了做了一般的工作创建一次提交。

贮藏(stash)会处理工作目录的脏的状态——即跟踪文件的修改与暂存的改动——然后将未完成的修改保存到一个栈上, 而你可以在任何时候重新应用这些改动(甚至在不同的分支上)。

1.贮(zhù)藏工作

git status
git stash
git status

git stash list

git stash apply# 默认最近一次
git stash apply stash@{0}

git stash apply --index

删除

git stash list
git stash drop stash@{0}

--keep-index:告诉 Git 不仅要贮藏所有已暂存的内容,同时还要将它们保留在索引中。

--include-untracked-u :Git 也会贮藏任何未跟踪文件。 --all-a :额外包含忽略的文件

2.从贮藏创建一个分支

git stash branch testchanges

3、清理工作目录

git clean 它被设计为从工作目录中移除未被追踪的文件。 谨慎使用改命令。

git stash -all:移除每一样东西并存放在栈中

git clean -f-d:来移除工作目录中所有未追踪的文件以及空的子目录

4.签署工作

gpg --ist-keys
gpg --gen-key
git config --global user.signingkey 0A46826A
git tag -s v1.5 -m 'my signed 1.5 tag'

5.搜索

git grep

6.重写历史

1.修改最后一次提交

git commit --amend

  • 修改最后一次提交的描述信息(保证 stage 区为空)
  • 修改最后一次提交的提交内容(将修改添加到 stage 区)

2.修改多个提交信息

troubleshooting

1. git pull 报错:refusing to merge unrelated histories

git pull origin master --allow-unrelated-histories

2. git push 强制覆盖远程仓库

git push origin --all --force

3. 推送新分支到远程仓库

git push --set-upstream origin xxx

一、Git 常用命令

1、撤销 git add .

git reset HEAD

2、修改 git 的提交信息

git commit --amend # 会提交暂存区的文件

3、在 Git 中屏蔽某个不想提交的文件?

在.gitingore 文件中添加此文件的路径即可

4、撤销添加到 git status 中的文件

git restore --staged .

5、设置全局 gitignore 文件

# MacMini
git config --global core.excludesfile ~/.gitignore_global
# Windows
git config --global core.excludesfile E:\\OneDrive\\.ssh\\.gitignore_global
# ubuntu
git config --global core.excludesfile ~/.gitignore_global

6、查看某个指定的配置

git config core.excludesfile

7、取消某个配置的设置

git config --global --unset user.name

8、git clone 时需要输入密码

ssh-add -K ~/.ssh/wangzhy_rsa
ssh-add -l

9、让指定文件不在让 git 跟踪,还保留在磁盘中

git rm --cached filename

10、IDEA 中出现未修改文件,但是提示修改了,内容完全一致。Contents are identical.

解决方法:

git config --global core.fileMode false

问题原因:git 不仅能够管理文件内容,还能管理文件权限,这种事属于修改了文件权限(从其他地方复制过来的)导致的。

11、linux 和 windows 的 CRLF 和 LF 问题

执行下面的命令。忽略 CRLFLF

git config --global core.autocrlf true

12、Git 不跟踪本地指定文件

git update-index --skip-worktree logback.xml
git ls-files -v

以 S 开头的表示该文件被标记为 skip-workspace.

取消 skip-workspace 标记

git update-index --no-skip-worktree logback.xml

https://stackoverflow.com/questions/13630849/git-difference-between-assume-unchanged-and-skip-worktree/23806990#23806990

二、Git 使用技巧

1、同步 fork 的项目更新

  1. 在 GitHub 上 fork 一个项目

  2. 下载这个 fork 的项目到本地

  3. 本地配置当前 fork 的原仓库的地址

git remote add upstream <原仓库 github 地址>
  1. 查看远程仓库
git remote -v
  1. 获取远程仓库的更新
git fetch upstream
  1. 合并到本地分支
git merge upstream/master
  1. 通过 git log 查看更新

  2. 推送至 fork 的项目

git push origin master
  1. git 修改远程仓库的地址
# remote_repo_name 通过 git remote -v 查看
git remote set-url remote_repo_name new_remote_repo_url

2. cherry-pick 使用

需要将分支切换到想要被合并的分支上。

三、git 设置和取消代理

https://gist.github.com/laispace/666dd7b27e9116faece6

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

git config --global https.proxy https://127.0.0.1:7890

git config --global --unset http.proxy

git config --global --unset https.proxy

四、通过 SSH 下载 Gitee 和 GitHub 仓库

1、Gitee

配置 ~/.ssh/config

Host gitee.com
HostName gitee.com
User git
Port 22
PubkeyAcceptedAlgorithms +ssh-rsa
HostkeyAlgorithms +ssh-rsa
IdentityFile ~/.ssh/gitee
IdentitiesOnly yes

Gitee 中配置 SSH 公钥 https://gitee.com/profile/sshkeys

2、GitHub

配置 ~/.ssh/config

Host github.com
Hostname ssh.github.com
Port 443
User git
PubkeyAcceptedAlgorithms +ssh-rsa
HostkeyAlgorithms +ssh-rsa
IdentityFile ~/.ssh/github
IdentitiesOnly yes

GitHub 中配置 SSH 公钥 https://github.com/settings/keys

五、Git 提交注释约定

1、格式

<type>(<scope>): <subject>
<BLANK LINE>
<body>
<BLANK LINE>
<footer>

2、type

  • feat:新功能(feature)
  • fix:修补 BUG
  • docs:文档 (documentation)
  • style:格式(不影响代码运行的改动)
  • refactor:重构(既不是新增功能,也不是修改 bug 的代码改动)
  • test:增加测试
  • chore:构建过程或辅助工具的变动

3、案例

  • 修改文件名称: refactor: rename file1 to file2
  • 修改构建相关的文件名称: chore: rename file1 to file2

六、Git 私有仓库

1、git 服务器初始化一个仓库

Setting up a Git HTTP server with Nginx

(注意:要带上 --bare ) git init --bare learning

cd ${GIT_REPORISTORY_PATH}
git init --bare xxxx.git

cd xxxx.git
git update-server-info

讲一个已有仓库(例如:origin.git)转为 bare 仓库(bare.git)

git clone --bare origin.git bare.git

将仓库变更为一个 bare 仓库

git clone --bare /path/to/original-repo /path/to/new-bare-repo.git

通过 https push/pull

如果需要通过 http 推送 git 提交记录,需要修改 xxx.git/config 文件

[core]
repositoryformatversion = 0
filemode = true
bare = true
sharedrepository = 1
[receive]
denyNonFastforwards = true
[http]
receivepack = true

修改文件夹的权限

chown -R nginx:nginx ${GIT_REPORISTORY_PATH}

git 修改 remote url

git remote set-url origin https://xxxx.git

提示 403

有 2 个解决方案:

  1. 如果宿主机没有 nginx 用户,可以执行下面的命令
chown -R 101:101 /wangzhy/gitrepo
  1. 修改 docker-compose.yml, 指定 nginx 启动进程的用户
  nginx:
restart: always
container_name: nginx
user: root
image: nginx