Git Tutorial

  1. 1. 服务器上搭建Git准备工作
    1. 1.1. 服务器密钥
    2. 1.2. 本地电脑密钥的生成
    3. 1.3. 服务器登录密钥(初次登陆服务器设置)
    4. 1.4. git配置密钥
    5. 1.5. 登录AWS
    6. 1.6. 设置权限
  2. 2. 服务器上搭建Git流程和使用
    1. 2.1. 配置个人信息【1】
    2. 2.2. 登录服务器,在git用户下创建裸仓库(项目后面要加.git)【2】
    3. 2.3. 在本地电脑上创建仓库【3】
    4. 2.4. 将工作目录的所有文件增加到暂存区域后提交到git仓库【4】
    5. 2.5. commit后再对刚才提交文件修改【5】
    6. 2.6. 链接远程仓库:(local -> remote) 【6】
    7. 2.7. 同步到远程仓库【7】
    8. 2.8. 克隆本地仓库:(不用再部署)
    9. 2.9. 克隆远程仓库
    10. 2.10. Git中.gitignore文件不起作用的解决(git清除本地缓存命令)
  3. 3. git的基本概念 & 指令
    1. 3.1. 工作区域,暂存区域,Git仓库
      1. 3.1.1. Git的工作流程
      2. 3.1.2. Git管理的文件有3种状态
    2. 3.2. git add -A 和 git add . 的区别
    3. 3.3. git reset命令
      1. 3.3.1. git reset 版本快照的ID号
      2. 3.3.2. git reflog
    4. 3.4. 查看历史提交
    5. 3.5. 比较暂存区域与工作目录
    6. 3.6. 比较两个历史快照
    7. 3.7. 比较当前工作目录和Git仓库中的快照
    8. 3.8. 比较暂存区域和Git仓库中的快照
    9. 3.9. 修改最后一次提交
    10. 3.10. 删除文件
    11. 3.11. 重命名文件
    12. 3.12. 查看远程链接仓库
    13. 3.13. 删除远程仓库链接
    14. 3.14. 更改远程仓库链接
    15. 3.15. 同时push到多个远程仓库
  4. 4. Git分支
    1. 4.1. 创建分支
    2. 4.2. 切换分支
    3. 4.3. 合并分支
    4. 4.4. 删除分支
    5. 4.5. 匿名分支
    6. 4.6. checkout命令
      1. 4.6.1. checkout命令和reset命令
  5. 5. Git使用中遇到的错误
    1. 5.1. git pull时遇到的错误
      1. 5.1.1. error: Your local changes to the following files would be overwritten by merge
    2. 5.2. git push时遇到的错误
      1. 5.2.1. fatal: You are not currently on a branch
    3. 5.3. Commit your changes or stash them before you can merge
    4. 5.4. git push: fatal: the remote end hung up unexpectedly
    5. 5.5. git push: fatal: the remote end hung up unexpectedly
  6. 6. 工作使用git
  7. 7. git总结
    1. 7.1. 新建代码库
    2. 7.2. 配置
    3. 7.3. 增加/删除文件
    4. 7.4. 代码提交
    5. 7.5. 分支
    6. 7.6. 标签
    7. 7.7. 查看信息
    8. 7.8. 远程同步
    9. 7.9. 撤销
      1. 7.9.1. git commit后,想撤销怎么办
    10. 7.10. 其他
  8. 8. github相关
    1. 8.1. GPG keys
    2. 8.2. GPG keys 报错

自从租用了亚马逊AWS的服务器后,想通过Git来管理代码。以下记录了服务器上Git的搭建过程和Git的学习记录。安装过程省略,具体Git教程可以参考这个链接

服务器上搭建Git准备工作

服务器密钥

没有ssh先创建

1
sudo apt-get install openssh-server

进入服务器用户(ubuntu)

1
2
cd .ssh
nano authorized_keys

复制本地生成的公钥(id_isa.pub)到这个文件夹

本地电脑密钥的生成

1
ssh-keygen -m PEM -t rsa -b 2048 -C <email>

执行后本地电脑的.ssh下会产生 id_isa(私钥)id_isa.pub(公钥) 文件


现在可以用4096加密:

1
ssh-keygen -t rsa -b 4096 -C "user-xps"

然后按回车键即可,不用输入密码

服务器登录密钥(初次登陆服务器设置)

服务器用户下将本地生成的 公钥(id_isa.pub) 复制到服务器.ssh下的authorized_keys 里面

git配置密钥

git用户下将本地生成的公钥(id_isa.pub)复制到git的.ssh下的authorized_keys里面

登录AWS

1
ssh -i key.pem 用户名@ec2-3-19-55-120.us-east-2.compute.amazonaws.com

设置权限

1
2
chmod 600 authorized_keys
chmod 700 .ssh

服务器上搭建Git流程和使用

配置个人信息【1】

1
2
git config --global user.name <username>
git config --global user.email <email>

登录服务器,在git用户下创建裸仓库(项目后面要加.git)【2】

1
git init --bare /home/git/repositories/C/data_structure/.git

在本地电脑上创建仓库【3】

1
2
git init (当前项目路径下)
或者 git init C:\Users\<username>\Documents\programming\C\data_structure\.git

将工作目录的所有文件增加到暂存区域后提交到git仓库【4】

1
2
3
4
git add .
(反悔时:git reset HEAD) # 所有内容
(反悔时:git reset HEAD file) # 指定内容
git commit -m "update"

commit后再对刚才提交文件修改【5】

1
git checkout -- file          # 会覆盖掉刚才的修改

链接远程仓库:(local -> remote) 【6】

1
git remote add origin ssh://git@ssh.tyokyo320.com:/home/git/repositories/C/data_structure/.git

同步到远程仓库【7】

1
git push -u origin master

克隆本地仓库:(不用再部署)

1
2
3
4
5
# 这样会让用户和群组变成root 需要变更权限
sudo git clone /home/git/repositories/TUS-Server/.git

# chown -R或-recursive:递归处理,将指定目录下的所有文件及子目录一并处理
sudo chown -R ec2-user:ec2-user /home/ec2-user/

克隆远程仓库

注意所在路径!(第一次clone 之后git pull)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
git clone ssh://git@ssh.tyokyo320.com:/home/git/repositories/C/data_structure/.git
git clone ssh://nuc-git:/home/git/repositories/Php/TUS-2/.git

git@ip-172-31-38-169:~$ tree
.
├── Blog
│   └── hexo
├── C
│   ├── data_structure
│   ├── experiment-1
│   ├── experiment-2
│   ├── leetcode
│   ├── TUS-1-c
│   ├── TUS-2-c
│   └── web
├── C++
│   └── TUS-2
├── Django
│   ├── blog
│   └── src
├── Git
│   └── mac
├── Html
│   ├── myprofile
│   └── tus-plan
├── Latex
│   ├── homework2-1
│   ├── homework2-2
│   └── plan
├── Php
│   └── TUS-2
└── Python
└── leetcode

Git中.gitignore文件不起作用的解决(git清除本地缓存命令)

1
2
3
git rm -r --cached .vscode
git add.
git commit -m "update .gitignore"

git的基本概念 & 指令

工作区域,暂存区域,Git仓库

Working Directory (add)——> Stage(Index) (commit)——> Repository(HEAD)

Working Directory <——(checkout) Stage(Index) <——(reset) Repository(HEAD)

Git的工作流程

  • 在工作目录中添加,修改文件
  • 将需要进行版本管理的文件放入暂存区域
  • 将暂存区域的文件提交到Git仓库

Git管理的文件有3种状态

  • 已修改(modified)
  • 已暂存(staged)
  • 已提交(committed)

git add -A 和 git add . 的区别

  • git add -A 提交所有变化
  • git add -u 提交被修改(modified)和被删除(deleted)文件,不包括新文件(new)
  • git add . 提交新文件(new)和被修改(modified)文件,不包括被删除(deleted)文件

git reset命令

  • git reset --mixed HEAD~
    • 移动HEAD的指向, 将其指向上一个快照(一个波浪线就是上一个快照)
    • 将HEAD移动后指向的快照回滚到暂存区域
  • git reset --soft HEAD~
    • 移动HEAD的指向,将其指向上一个快照
  • git reset --hard HEAD~
    • 移动HEAD的指向,将其指向上一个快照
    • 将HEAD移动后指向的快照回滚到暂存区域
    • 将暂存区域的文件还原到工作目录

总结:

  • 移动HEAD的指向(–soft)
  • 将快照回滚到暂存区域([–mixed], 默认)
  • 将暂存区域还原到工作目录(–hard)
  • soft: 工作区域和暂存区都是当前版本的内容
  • mix: 暂存区的内容是前一个版本的,工作区却是现在的版本,要想回到现在,必须add后再commit

git reset 版本快照的ID号

1
2
# 回滚个别文件(注意这里HEAD不会改变)
git reset 版本快照 文件名/路径

git reflog

查看历史所有的commit id

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
git reflog

ba5c846 (HEAD -> master, origin/master) HEAD@{0}: commit: change name
744934e HEAD@{1}: commit: update
feac247 HEAD@{2}: commit: computation
c33ae9e HEAD@{3}: commit: python
de57ed6 HEAD@{4}: commit: optimality_theory
8436ba2 HEAD@{5}: commit: statistic
ec2eb81 HEAD@{6}: commit: linear programming
7d4ef67 HEAD@{7}: commit: fix pace-theme
1a93a6a HEAD@{8}: commit: python
1ba260a HEAD@{9}: commit: picture
b5cec63 HEAD@{10}: commit: visitor count
4bcdd27 HEAD@{11}: commit: music
b295aaf HEAD@{12}: commit: docker
5b2da36 HEAD@{13}: commit: tpo
d511fd9 HEAD@{14}: commit: live2d
(END)

查看历史提交

1
git log

比较暂存区域与工作目录

1
git diff

比较两个历史快照

1
git diff 快照ID1 快照ID2

比较当前工作目录和Git仓库中的快照

1
git diff 快照ID

比较暂存区域和Git仓库中的快照

1
git diff --cached [快照ID]

修改最后一次提交

  • 在实际开发中,可能会遇到以下两种情景:
    • 情景1: 版本刚一提交(commit)到仓库,突然想起漏掉两个文件还没有添加(add)。
    • 场景2: 版本刚一提交(commit)到仓库,突然想起版本说明写的不够全面,无法彰显你本次修改的重大意义。
  • 执行带 --amend选项的commit提交命令,Git就会“更正”最近的一次提交。
1
git commit --amend

删除文件

  • git rm 文件名
    • 该命令删除的只是工作目录和暂存区域的文件,也就是取消跟踪,在下次提交时不纳入版本管理。
    • 当工作目录和暂存区域的同一个文件存在不同内容时,执行git rm -f 文件名命令就可以把两个都删除。
    • 如果只删除暂存区域的文件(保留工作目录的),那么你可以执行git rm -cached 文件名命令实现目的。

重命名文件

  • git mv 旧文件名 新文件名
    • ren/mv 旧文件名 新文件名
    • git rm 旧文件名
    • git add 新文件名

查看远程链接仓库

1
git remote -v

删除远程仓库链接

1
git remote remove origin

更改远程仓库链接

1
git remote set-url origin ssh://git@ssh.tyokyo320.com:/home/git/repositories/C/data_structure/.git

同时push到多个远程仓库

当有不止一个远程仓库时候,一次git push又想同时push到多个远程仓库的时候:

首先需要添加第二个远程地址时使用以下命令:

1
git remote set-url --add origin https://github.com/tyokyo320/bilibili-downloader.git

查看远程分支:

1
2
3
4
5
git remote -v

origin https://git.tyokyo320.com/<username>/bilibili-downloader.git (fetch)
origin https://git.tyokyo320.com/<username>/bilibili-downloader.git (push)
origin https://github.com/tyokyo320/bilibili-downloader.git (push)

添加完成后git push -u origin master可以同时push到多个远程仓库

Git分支

创建分支

1
2
3
4
5
6
7
8
git branch 分支名

# 让log显示指向这个提交的所有引用(分支,标签))
git log --decorate

# 创建并切换分支
git checkout -b 分支名

切换分支

1
2
3
4
5
6
7
8
# 切换分支后HEAD会指向这个分支
git checkout 分支名

# oneline相当于log的精简版本形式,一行显示一个快照
git log --decorate --oneline

# graph以图形化的形式显示,all显示所有分支
git log --decorate --oneline --graph --all

合并分支

1
2
3
4
# 将指定的分支合并到主分支
git merge 分支名

# 有冲突的时候可能是相同文件名但内容不一样,这时需要修改

删除分支

1
git branch -d 分支名

Git的分支原理是通过指针实现的,所以删除分支后快照还是会留下的,创建和删除都是可以瞬间完成的,这也是Git的魅力。

匿名分支

准备工作: 新建工作目录, 用git init初始化项目. 分别创建1.txt, 2.txt, 3.txt, 依次对三个文件执行git addgit commit -m命令.

1
2
3
4
5
git log --decorate --oneline --graph --all

* 0bc27d5 (HEAD -> master) 3.txt
* 8df4cf6 2.txt
* e9ed88e 1.txt

执行git checkout HEAD~返回上一个快照

1
2
3
4
5
6
7
8
9
10
11
12
13
14
git checkout HEAD~

Note: checking out 'HEAD~'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:

git checkout -b <new-branch-name>

HEAD is now at 8df4cf6... 2.txt

这一段话的意思是, 使用了checkout命令, 但是没有指定分支名, 所以git会创建一个叫做匿名分支(没有名字的分支)的东西, 当你切换到别的分支的时候, 在这个匿名分支中, 所做的所有操作, 提交都会被丢弃掉(3.txt不见了), 所以可以使用匿名分支来做一些实验.

执行git log --decorate --oneline --graph --all

1
2
3
4
5
git log --decorate --oneline --graph --all

* 0bc27d5 (master) 3.txt
* 8df4cf6 (HEAD) 2.txt
* e9ed88e 1.txt

再创建4.txt, 执行git addgit commit -m命令.然后执行git log --decorate --oneline --graph --all命令

1
2
3
4
5
6
7
git log --decorate --oneline --graph --all

* 2fa31e3 (HEAD) 4.txt
| * 0bc27d5 (master) 3.txt
|/
* 8df4cf6 2.txt
* e9ed88e 1.txt

将 HEAD 指向切换到 master: git checkout master.

1
2
3
4
5
6
7
8
9
10
11
12
13
git checkout master

Warning: you are leaving 1 commit behind, not connected to
any of your branches:

2fa31e3 4.txt

If you want to keep it by creating a new branch, this may be a good time
to do so with:

git branch <new-branch-name> 2fa31e3

Switched to branch 'master'

说有一个没有连接到任何分支的提交(1920177 4.txt), 如果要保留为一个新的分支, 那么现在是最好的时机(因为现在有快照ID). 使用git branch <分支名> 1920177”就可以创建分支.匿名分支用于实验一些有风险的提交, 从匿名分支切回master分支后, 匿名分支就找不到了.

checkout命令

  • 从历史快照(或暂缓区域)中拷贝文件到工作目录

给定某个文件名时,git会从指定的提交中拷贝文件到暂存区域和工作目录。比如执行git checkout HEAD~ README.md命令,会将上一个快照中的 README.md 文件复制到工作区域和暂存区域中。

如果命令中没有指定具体的快照id,则将从暂存区域恢复指定文件到工作目录git checkout README.md

注意:之前命令中文件名前有两个横杠(–),这里又没有了。git提醒你写成git checkout --README.md,是为了预防你恰好有一个分支叫做 README.md,那么它就搞不懂你要恢复文件还是切换分支了,所以约定两个横杠(–)后面跟的是文件名。反过来说,如果你确保没有一个 README.md 的分支,你直接写git checkout README.md也是没有问题的

  • 切换分支

首先知道git的分支其实就是添加一个指向快照的指针,其次还知道切换分支除了修改 HEAD 指针的指向,还会改变暂存区域和工作目录的内容。

checkout命令和reset命令

  • 恢复文件
    checkout 命令和 reset 命令都可以用于恢复指定快照的指定文件,并且他们都不会改变 HEAD 指针的指向

它们的区别是:reset 命令只将指定文件恢复到暂存区域(–mixed),而checkout 命令是同时覆盖暂存区域和工作目录

注意:也许你试图使用”git reset --hard HEAD~ README.md“ 命令让reset同时覆盖工作目录,但git会告诉你这是徒劳(此时reset不允许使用 --soft 或 --hard 选项)。

这样看来在恢复文件方面,reset 命令要比 checkout 命令更安全一些。

  • 恢复快照(commit级别)

reset 命令是用来”回到过去“的,根据选项的不同,reset 命令将移动 HEAD 指针(–soft)->覆盖暂存区域(–mixed,默认)->覆盖工作目录(–hard)。

  • 两者的区别

【1】对于 reset --hard 命令来说,checkout 命令更安全。因为checkout 命令在切换分支前会先检查一下当前的工作状态,如果不是”clean“的话,git不允许你这样做;而
reset --hard命令则是直接覆盖所有数据

【2】如何更新 HEAD 指向,reset 命令会移动 HEAD 所在分支的指向,而 checkout 命令只会移动 HEAD 自身来指向另一个分支。也就是,checkout只是HEAD指针跑到分支那儿去了,但是reset命令将HEAD指向的分支以及HEAD本身都切到了分支里,换句话说,原来的快照已经消失了

Git使用中遇到的错误

git pull时遇到的错误

error: Your local changes to the following files would be overwritten by merge

方法1:如果你想保留刚才本地修改的代码,并把git服务器上的代码pull到本地(本地刚才修改的代码将会被暂时封存起来)

1
2
3
git stash
git pull origin master
git stash pop

如此一来,服务器上的代码更新到了本地,而且你本地修改的代码也没有被覆盖,之后使用add,commit,push 命令即可更新本地代码到服务器了。

方法2: 如果你想完全地覆盖本地的代码,只保留服务器端代码,则直接回退到上一个版本,再进行pull:

1
2
git reset --hard
git pull origin master

git push时遇到的错误

fatal: You are not currently on a branch

首先描述一下发生了什么:今天在学校本想开始写课题时,打开仓库发现前一次在家里用的PC写的代码没有同步到本地笔记本电脑上。想着无所谓先在本地笔记本电脑上写,写完上传回家同步一下就行了。晚上回到家用家里的PCgit push的时候,发现在学校写的东西并没有同步过来。
看了git graph后发现,本地笔记本电脑上git push以后,本地的master(HEADS)跟远程的origin master是分离的。

考虑了一下打算不采用风险较大的git push origin HEAD:master --force,而是采用了风险较小的解决方案:从分离的HEAD创建一个临时分支,然后将该分支合并到master.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
git branch temp-branch

git checkout master
Previous HEAD position was 86b71ac 1017
Switched to branch 'master'
Your branch is up to date with 'origin/master'.

git merge temp-branch
Updating cfbb0d3..86b71ac
Fast-forward
20191017/20191017_kadai1.cpp | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
create mode 100644 20191017/20191017_kadai1.cpp

git push origin master
Enumerating objects: 9, done.
Counting objects: 100% (9/9), done.
Delta compression using up to 4 threads
Compressing objects: 100% (6/6), done.
Writing objects: 100% (8/8), 764 bytes | 382.00 KiB/s, done.
Total 8 (delta 3), reused 0 (delta 0)
To nuc-git:/home/git/repositories/C++/TUS-2/.git
cfbb0d3..86b71ac master -> master
1
2
3
4
5
6
7
8
9
10
11
12
git log --decorate --oneline --graph --all
* c78bceb (HEAD -> master, origin/master) check 2
* 01859f2 check
* 86b71ac 1017
* 4034177 create a file
* cfbb0d3 Merge commit 'c1333735464e0fbf0adaf802f6eea04289027953'
|\
| * c133373 20191010
|/
* 01ce379 dp
* 89e9bc8 update
* 132927f update

检查一下发现恢复正常了,家里的PCgit pull之后也同步了代码。最后我把创建出来的temp-branch分支删除了。这是我今天采用的解决办法。

在网上查询了还发现了其他解决方法:

  • git status查看所有变化的文件, 把有改动的先删除 ps: 这个是从别的网站粘贴的, 个人觉得单纯删除不保险, 注意, 注意, 注意, 这里最好是将自己的项目 copy 一份到本地其他地方作为备份
  • git checkout master回到主分支 ps:这里说白了就是回退到 master 主分支上这次修改的内容会消失
  • git pull保证一下本地是最新代码

然后将备份的内容paste就好了.

其实最开始在学校发现代码没同步的时候应该git pull一下的。然后继续写,写完上传也没啥问题。结果自己当时想用之前学过的命令乱搞导致了一些麻烦。解决了这个问题之后,也是很奇怪当时为啥没有git pull于是查了一下bash history发现确实输入过这个命令,因为终端关闭了自己也忘记当时是怎么回事了。还是感觉git真的太强大了而自己真的还差的很远很远。

Commit your changes or stash them before you can merge

You can’t merge with local modifications. Git protects you from losing potentially important changes.
You have three options:

  • Commit the change using
1
git commit -m "My message"
  • Stash it

Stashing acts as a stack, where you can push changes, and you pop them in reverse order.
To stash, type

1
git stash

Do the merge, and then pull the stash:

1
git stash pop
  • Discard the local changes

using

1
git reset --hard

or

1
git checkout -t -f remote/branch
  • Or: Discard local changes for a specific file
1
git checkout filename

git push: fatal: the remote end hung up unexpectedly

1
2
3
4
5
6
7
8
9
10
11
12
git push -u gitea

Enumerating objects: 12505, done.
Counting objects: 100% (12505/12505), done.
Delta compression using up to 4 threads
Compressing objects: 100% (4158/4158), done.
Writing objects: 100% (12505/12505), 45.40 MiB | 30.85 MiB/s, done.
Total 12505 (delta 8073), reused 12435 (delta 8045)
error: RPC failed; HTTP 413 curl 22 The requested URL returned error: 413
fatal: the remote end hung up unexpectedly
fatal: the remote end hung up unexpectedly
Everything up-to-date

git上传时,由于文件太大git push报错,解决方法如下

新建文件~/docker/nginx-proxy/conf/client_max_body_size.conf

1
client_max_body_size 10G;

然后重启容器即可解决

git push: fatal: the remote end hung up unexpectedly

与上面报错类似,再上个问题解决的情况下,再次出现了类似问题,这个问题只在mac上出现,主机Windows从未出现

1
2
3
4
5
6
7
8
9
10
11
12
git push

Enumerating objects: 15, done.
Counting objects: 100% (15/15), done.
Delta compression using up to 4 threads
Compressing objects: 100% (12/12), done.
Writing objects: 100% (12/12), 134.35 KiB | 7.90 MiB/s, done.
Total 12 (delta 4), reused 0 (delta 0)
error: RPC failed; curl 92 HTTP/2 stream 0 was not closed cleanly: PROTOCOL_ERROR (err 1)
fatal: the remote end hung up unexpectedly
fatal: the remote end hung up unexpectedly
Everything up-to-date

暂时解决方案如下:

1
2
3
sudo nano /private/etc/host

192.168.10.106 git.tyokyo320.com

然后执行code .git/config:把url中的https改为http:

1
2
3
4
5
6
7
8
9
10
11
12
git push

Enumerating objects: 15, done.
Counting objects: 100% (15/15), done.
Delta compression using up to 4 threads
Compressing objects: 100% (12/12), done.
Writing objects: 100% (12/12), 134.35 KiB | 13.43 MiB/s, done.
Total 12 (delta 4), reused 0 (delta 0)
remote: . Processing 1 references
remote: Processed 1 references in total
To http://git.tyokyo320.com/<username>/homework3-2.git
c841097..fe03078 master -> master

这样可以暂时解决问题

工作使用git

项目经理:

  1. 项目经理搭建项目的框架
  2. 搭建完项目框架之后,项目经理吧项目框架代码放到服务器

普通员工:

  1. 在自己的电脑上,生成ssh公钥,然后把公钥给项目经理,项目经理把它添加到服务器上面
  2. 项目经理会给每个组员的项目代码的地址,组员把代码下载到自己的电脑上
  3. 创建本地的分支dev,在dev分支中进行每天的开发
  4. 每一个员工开发完自己的代码之后,都需要将代码发布远程的dev分支上

两个重要的分支:

  • Master:用户保存发布的项目代码,eg. V1.0, V2.0
  • Dev:保存开发过程中的代码

git总结

  • Workspace:工作区
  • Index / Stage:暂存区
  • Repository:仓库区(或本地仓库)
  • Remote:远程仓库

新建代码库

1
2
3
4
5
6
7
8
# 在当前目录新建一个Git代码库
git init

# 新建一个目录,将其初始化为Git代码库
git init [project-name]

# 下载一个项目和它的整个代码历史
git clone [url]

配置

Git的设置文件为.gitconfig,它可以在用户主目录下(全局配置),也可以在项目目录下(项目配置)

1
2
3
4
5
6
7
8
9
# 显示当前的Git配置
git config --list

# 编辑Git配置文件
git config -e [--global]

# 设置提交代码时的用户信息
git config [--global] user.name "[name]"
git config [--global] user.email "[email address]"

增加/删除文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 添加指定文件到暂存区
git add [file1] [file2] ...

# 添加指定目录到暂存区,包括子目录
git add [dir]

# 添加当前目录的所有文件到暂存区
git add .

# 添加每个变化前,都会要求确认
# 对于同一个文件的多处变化,可以实现分次提交
git add -p

# 删除工作区文件,并且将这次删除放入暂存区
git rm [file1] [file2] ...

# 停止追踪指定文件,但该文件会保留在工作区
git rm --cached [file]

# 改名文件,并且将这个改名放入暂存区
git mv [file-original] [file-renamed]

代码提交

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 提交暂存区到仓库区
git commit -m [message]

# 提交暂存区的指定文件到仓库区
git commit [file1] [file2] ... -m [message]

# 提交工作区自上次commit之后的变化,直接到仓库区
git commit -a

# 提交时显示所有diff信息
git commit -v

# 使用一次新的commit,替代上一次提交
# 如果代码没有任何新变化,则用来改写上一次commit的提交信息
git commit --amend -m [message]

# 重做上一次commit,并包括指定文件的新变化
git commit --amend [file1] [file2] ...

分支

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# 列出所有本地分支
git branch

# 列出所有远程分支
git branch -r

# 列出所有本地分支和远程分支
git branch -a

# 新建一个分支,但依然停留在当前分支
git branch [branch-name]

# 新建一个分支,并切换到该分支
git checkout -b [branch]

# 新建一个分支,指向指定commit
git branch [branch] [commit]

# 新建一个分支,与指定的远程分支建立追踪关系
git branch --track [branch] [remote-branch]

# 切换到指定分支,并更新工作区
git checkout [branch-name]

# 切换到上一个分支
git checkout -

# 建立追踪关系,在现有分支与指定的远程分支之间
git branch --set-upstream [branch] [remote-branch]

# 合并指定分支到当前分支
git merge [branch]

# 选择一个commit,合并进当前分支
git cherry-pick [commit]

# 删除分支
git branch -d [branch-name]

# 删除远程分支
git push origin --delete [branch-name]
git branch -dr [remote/branch]

标签

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 列出所有tag
git tag

# 新建一个tag在当前commit
git tag [tag]

# 新建一个tag在指定commit
git tag [tag] [commit]

# 删除本地tag
git tag -d [tag]

# 删除远程tag
git push origin :refs/tags/[tagName]

# 查看tag信息
git show [tag]

# 提交指定tag
git push [remote] [tag]

# 提交所有tag
git push [remote] --tags

# 新建一个分支,指向某个tag
git checkout -b [branch] [tag]

查看信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# 显示有变更的文件
git status

# 显示当前分支的版本历史
git log

# 显示commit历史,以及每次commit发生变更的文件
git log --stat

# 搜索提交历史,根据关键词
git log -S [keyword]

# 显示某个commit之后的所有变动,每个commit占据一行
git log [tag] HEAD --pretty=format:%s

# 显示某个commit之后的所有变动,其"提交说明"必须符合搜索条件
git log [tag] HEAD --grep feature

# 显示某个文件的版本历史,包括文件改名
git log --follow [file]
git whatchanged [file]

# 显示指定文件相关的每一次diff
git log -p [file]

# 显示过去5次提交
git log -5 --pretty --oneline

# 显示所有提交过的用户,按提交次数排序
git shortlog -sn

# 显示指定文件是什么人在什么时间修改过
git blame [file]

# 显示暂存区和工作区的差异
git diff

# 显示暂存区和上一个commit的差异
git diff --cached [file]

# 显示工作区与当前分支最新commit之间的差异
git diff HEAD

# 显示两次提交之间的差异
git diff [first-branch]...[second-branch]

# 显示今天你写了多少行代码
git diff --shortstat "@{0 day ago}"

# 显示某次提交的元数据和内容变化
git show [commit]

# 显示某次提交发生变化的文件
git show --name-only [commit]

# 显示某次提交时,某个文件的内容
git show [commit]:[filename]

# 显示当前分支的最近几次提交
git reflog

远程同步

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 下载远程仓库的所有变动
git fetch [remote]

# 显示所有远程仓库
git remote -v

# 显示某个远程仓库的信息
git remote show [remote]

# 增加一个新的远程仓库,并命名
git remote add [shortname] [url]

# 取回远程仓库的变化,并与本地分支合并
git pull [remote] [branch]

# 上传本地指定分支到远程仓库
git push [remote] [branch]

# 强行推送当前分支到远程仓库,即使有冲突
git push [remote] --force

# 推送所有分支到远程仓库
git push [remote] --all

撤销

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 恢复暂存区的指定文件到工作区
git checkout [file]

# 恢复某个commit的指定文件到暂存区和工作区
git checkout [commit] [file]

# 恢复暂存区的所有文件到工作区
git checkout .

# 重置暂存区的指定文件,与上一次commit保持一致,但工作区不变
git reset [file]

# 重置暂存区与工作区,与上一次commit保持一致
git reset --hard

# 重置当前分支的指针为指定commit,同时重置暂存区,但工作区不变
git reset [commit]

# 重置当前分支的HEAD为指定commit,同时重置暂存区和工作区,与指定commit一致
git reset --hard [commit]

# 重置当前HEAD为指定commit,但保持暂存区和工作区不变
git reset --keep [commit]

# 新建一个commit,用来撤销指定commit
# 后者的所有变化都将被前者抵消,并且应用到当前分支
git revert [commit]

# 暂时将未提交的变化移除,稍后再移入
git stash
git stash pop

git commit后,想撤销怎么办

git reset --soft HEAD^:不删除工作空间改动代码,撤销commit,不撤销git add .

git reset --mixed HEAD^:不删除工作空间改动代码,撤销commit,并且撤销git add . 操作,和 git reset HEAD^ 效果是一样的

git reset --hard HEAD^删除工作空间改动代码,撤销commit,撤销git add .

其他

1
2
# 生成一个可供发布的压缩包
git archive

github相关

GPG keys

GPG的功能十分丰富,这里主要是用它来对Git中的commit进行签名验证,主要步骤如下:

  1. 生成自己的GPG密钥
  2. 关联GPG公钥与Github,Gitea账户
  3. 设置利用GPG私钥对commit进行签名
  4. 可选步骤:信任Github的GPG密钥

参考网站:

其他设备上的关联:

1
gpg --export-secret-key -a > secretkey.asc

然后在另一台机器import:

1
gpg --import secretkey.asc
1
shred secretkey.asc && rm secretkey.asc

import之后,还需要让Git知道签名所用的GPG密钥ID:

1
git config --global user.signingkey {key_id}

可以设置Git为每次commit自动要求签名:

1
git config --global commit.gpgsign true

此时如果 uid 后面显示 [ unknown] 的话,可以 gpg --edit-key tyokyo320@gmail.com

1
2
3
4
5
6
7
8
9
10
11
12
13
14
gpg> trust

Please decide how far you trust this user to correctly verify other users' keys
(by looking at passports, checking fingerprints from different sources, etc.)

1 = I don't know or won't say
2 = I do NOT trust
3 = I trust marginally
4 = I trust fully
5 = I trust ultimately
m = back to the main menu

Your decision? 5
Do you really want to set this key to ultimate trust? (y/N) y
1
gpg> save

保存即可

GPG keys 报错

若报错为error: gpg failed to sign the data,但是查看后发现我的GPG key并没有过期,解决方案如下:

1
2
3
4
5
6
7
8
echo 'test'|gpg --clearsign

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

test
gpg: signing failed: Inappropriate ioctl for device
gpg: [stdin]: clear-sign failed: Inappropriate ioctl for device

~/.zshrc 中添加 export GPG_TTY=$TTY,执行 gpgconf --kill gpg-agent,然后再执行 echo 'test'|gpg --clearsign,会有输入密码界面

1
2
3
4
5
6
7
8
echo 'test'|gpg --clearsign

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

test
-----BEGIN PGP SIGNATURE-----
-----END PGP SIGNATURE-----