Version Control (Git)

这篇文章是我在上一份工作中完成的,现在分享出来希望能帮到大家 ;)

官方文档 方便查阅~

安装Git

Ubuntu

我的版本是ubuntu2204 LTS,git在ubuntu中已经预装,在terminal中输入gitgit version以检查git版本。

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
fr3y@fr3y-pc:~$ git
usage: git [--version] [--help] [-C <path>] [-c <name>=<value>]
[--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
[-p | --paginate | -P | --no-pager] [--no-replace-objects] [--bare]
[--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
[--super-prefix=<path>] [--config-env=<name>=<envvar>]
<command> [<args>]

These are common Git commands used in various situations:

start a working area (see also: git help tutorial)
clone Clone a repository into a new directory
init Create an empty Git repository or reinitialize an existing one

work on the current change (see also: git help everyday)
add Add file contents to the index
mv Move or rename a file, a directory, or a symlink
restore Restore working tree files
rm Remove files from the working tree and from the index

examine the history and state (see also: git help revisions)
bisect Use binary search to find the commit that introduced a bug
diff Show changes between commits, commit and working tree, etc
grep Print lines matching a pattern
log Show commit logs
show Show various types of objects
status Show the working tree status

grow, mark and tweak your common history
branch List, create, or delete branches
commit Record changes to the repository
merge Join two or more development histories together
rebase Reapply commits on top of another base tip
reset Reset current HEAD to the specified state
switch Switch branches
tag Create, list, delete or verify a tag object signed with GPG

collaborate (see also: git help workflows)
fetch Download objects and refs from another repository
pull Fetch from and integrate with another repository or a local branch
push Update remote refs along with associated objects

'git help -a' and 'git help -g' list available subcommands and some
concept guides. See 'git help <command>' or 'git help <concept>'
to read about a specific subcommand or concept.
See 'git help git' for an overview of the system.
fr3y@fr3y-pc:~$ git --version
14:24:49.617849 git.c:455 trace: built-in: git version
git version 2.34.1

Mac OS

直接从AppStore安装Xcode,Xcode集成了Git,不过默认没有安装,你需要运行Xcode,选择菜单“Xcode”->“Preferences”,在弹出窗口中找到“Downloads”,选择“Command Line Tools”,点“Install”就可以完成安装了。

Windows

从官网下载。

创建版本库

terminal中输入:

1
2
$ git config --global user.name "Your Name"
$ git config --global user.email "email@example.com"

注意git config命令的--global参数,用了这个参数,表示你这台机器上所有的Git仓库都会使用这个配置,当然也可以对某个仓库指定不同的用户名和Email地址。

创建新文件夹,打开,然后执行 git init以创建新的 git 仓库。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
fr3y@fr3y-pc:~$ mkdir learngit
fr3y@fr3y-pc:~$ cd learngit
fr3y@fr3y-pc:~/learngit$ pwd
/home/fr3y/learngit
fr3y@fr3y-pc:~/learngit$ git init
15:50:51.222462 git.c:455 trace: built-in: git init
hint: Using 'master' as the name for the initial branch. This default branch name
hint: is subject to change. To configure the initial branch name to use in all
hint: of your new repositories, which will suppress this warning, call:
hint:
hint: git config --global init.defaultBranch <name>
hint:
hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and
hint: 'development'. The just-created branch can be renamed via this command:
hint:
hint: git branch -m <name>
Initialized empty Git repository in /home/fr3y/learngit/.git/

把文件添加到版本库

你的本地仓库由 git 维护的三棵“树”组成。第一个是你的 工作目录,它持有实际文件;第二个是 暂存区(Index),它像个缓存区域,临时保存你的改动;最后是 HEAD,它指向你最后一次提交的结果。

你可以提出更改(把它们添加到暂存区),使用如下命令:
git add <filename>
git add *
这是 git 基本工作流程的第一步;使用如下命令以实际提交改动:
git commit -m "代码提交信息"
-m后面输入的是本次提交的说明,可以输入任意内容。现在,你的改动已经提交到了 HEAD,但是还没到你的远端仓库。

下面创建一个readme.txt,在里面写入Hello Git!之后提交。

1
2
3
4
5
6
7
8
9
10
11
12
fr3y@fr3y-pc:~/learngit$ touch readme.txt
fr3y@fr3y-pc:~/learngit$ cat readme.txt
Hello Git!
fr3y@fr3y-pc:~/learngit$ git add readme.txt
16:23:13.117650 git.c:455 trace: built-in: git add readme.txt
fr3y@fr3y-pc:~/learngit$ git commit -m "wrote a readme file"
16:25:33.582306 git.c:455 trace: built-in: git commit -m 'wrote a readme file'
16:25:33.588250 run-command.c:668 trace: run_command: git maintenance run --auto --no-quiet
16:25:33.607080 git.c:455 trace: built-in: git maintenance run --auto --no-quiet
[master (root-commit) b3a217b] wrote a readme file
1 file changed, 1 insertion(+)
create mode 100644 readme.txt

接下来我们在readme.txt中添加一行Git is a distributed version control system.之后运行git status

1
2
3
4
5
6
7
8
9
fr3y@fr3y-pc:~/learngit$ git status
16:43:03.856838 git.c:455 trace: built-in: git status
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: readme.txt

no changes added to commit (use "git add" and/or "git commit -a")

git status命令可以让我们时刻掌握仓库当前的状态,上面的命令输出告诉我们,readme.txt被修改过了,但还没有准备提交的修改。

使用git diff查看修改了什么内容。

1
2
3
4
5
6
7
8
9
10
fr3y@fr3y-pc:~/learngit$ git diff readme.txt 
16:46:33.087526 git.c:455 trace: built-in: git diff readme.txt
16:46:33.087753 run-command.c:668 trace: run_command: unset GIT_PAGER_IN_USE; LESS=FRX LV=-c pager
diff --git a/readme.txt b/readme.txt
index 106287c..768b2ea 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1 +1,2 @@
Hello Git!
+Git is a distributed version control system.

提交修改。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
fr3y@fr3y-pc:~/learngit$ git add readme.txt
16:48:27.422250 git.c:455 trace: built-in: git add readme.txt
fr3y@fr3y-pc:~/learngit$ git status
16:48:47.928238 git.c:455 trace: built-in: git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: readme.txt

fr3y@fr3y-pc:~/learngit$ git commit -m "add a sentence"
16:49:48.431119 git.c:455 trace: built-in: git commit -m 'add a sentence'
16:49:48.434682 run-command.c:668 trace: run_command: git maintenance run --auto --no-quiet
16:49:48.436286 git.c:455 trace: built-in: git maintenance run --auto --no-quiet
[master 354bc8d] add a sentence
1 file changed, 1 insertion(+)
fr3y@fr3y-pc:~/learngit$ git status
16:50:12.694291 git.c:455 trace: built-in: git status
On branch master
nothing to commit, working tree clean

版本回退

git log查看历史记录,可加--pretty=oneline简洁输出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
fr3y@fr3y-pc:~/learngit$ git log
21:14:00.857909 git.c:455 trace: built-in: git log
21:14:00.860545 run-command.c:668 trace: run_command: unset GIT_PAGER_IN_USE; LESS=FRX LV=-c pager
commit b2e5ef1688ec5ab1db4fef39c3ab497efccf2691 (HEAD -> master)
Author: fr3y <fr3y@qq.com>
Date: Fri Jul 1 21:03:51 2022 +0800

append GPL

commit 354bc8d6c2d30c2627b7c7b2195f4907adf5e8a6
Author: fr3y <fr3y@qq.com>
Date: Thu Jun 30 16:49:48 2022 +0800

add a sentence

commit b3a217bf0f776da017119085fcde8b8133edb93a
Author: fr3y <fr3y@qq.com>
Date: Thu Jun 30 16:25:33 2022 +0800

wrote a readme file

使用git reset回退到上一个版本add a sentenceHEAD表示上一版本,^表示前一版本。

1
2
3
4
5
6
7
fr3y@fr3y-pc:~/learngit$ git reset --hard HEAD^
21:28:29.207423 git.c:455 trace: built-in: git reset --hard HEAD^
HEAD is now at 354bc8d add a sentence
fr3y@fr3y-pc:~/learngit$ cat readme.txt
Hello Git!
Git is a distributed version control system.

回退到任一版本,输入git reset --hard+commit id 前几位。

1
2
3
4
5
6
fr3y@fr3y-pc:~/learngit$ git reset --hard b2e5e
21:32:41.759184 git.c:455 trace: built-in: git reset --hard b2e5e
HEAD is now at b2e5ef1 append GPL
fr3y@fr3y-pc:~/learngit$ cat readme.txt
Hello Git!
Git is a distributed version control system under the GPL.

使用git reflog用来记录你的每一次命令。

1
2
3
4
5
6
7
8
fr3y@fr3y-pc:~/learngit$ git reflog
21:50:58.489027 git.c:455 trace: built-in: git reflog
21:50:58.489821 run-command.c:668 trace: run_command: unset GIT_PAGER_IN_USE; LESS=FRX LV=-c pager
b2e5ef1 (HEAD -> master) HEAD@{0}: reset: moving to b2e5e
354bc8d HEAD@{1}: reset: moving to HEAD^
b2e5ef1 (HEAD -> master) HEAD@{2}: commit: append GPL
354bc8d HEAD@{3}: commit: add a sentence
b3a217b HEAD@{4}: commit (initial): wrote a readme file

撤销和删除

把工作区的修改全部撤销。

1
git checkout -- readme.txt

git reset HEAD <file>可以把暂存区的修改撤销掉(unstage),重新放回工作区。

从版本库中删除文件:用git rm删掉,并且git commit

1
2
$ git rm test.txt
$ git commit -m "remove test.txt"

远程仓库

创建SSH Key

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

生成.ssh目录,里面有id_rsa(私钥)和id_rsa.pub(公钥)两个文件。登陆GitHub,打开“Account settings”,“SSH Keys”页面,点“Add SSH Key”,填上任意Title,在Key文本框里粘贴id_rsa.pub文件的内容,点“Add Key”,你就应该看到已经添加的Key。

添加远程库

登陆GitHub,在右上角找到“Create a new repo”按钮,创建一个新的仓库,在本地的learngit仓库下运行命令:

1
$ git remote add origin git@github.f23y/learngit.git

下一步,就可以把本地库的所有内容推送到远程库上:git push -u origin master

之后推送后就可以去掉-u参数。

删除远程库

1
2
$ git remote -v
$ git remote rm origin

克隆远程库

1
git clone username@host:/path/to/repository

分支

分支是用来绝缘开发功能的。在你创建仓库的时候,master 是主分支。在其他分支上进行开发,完成后再将它们合并到主分支上。

Create

创建一个叫dev的分支。

1
2
3
fr3y@fr3y-pc:~/learngit$ git checkout -b dev
00:05:42.329505 git.c:455 trace: built-in: git checkout -b dev
Switched to a new branch 'dev'

修改readme.txt,加上一句Branches are amazing.并提交。

1
2
3
4
5
6
7
8
9
10
11
12
fr3y@fr3y-pc:~/learngit$ cat readme.txt
Hello Git!
Git is a distributed version control system under the GPL.
Branches are amazing.
fr3y@fr3y-pc:~/learngit$ git add readme.txt
00:13:37.503954 git.c:455 trace: built-in: git add readme.txt
fr3y@fr3y-pc:~/learngit$ git commit -m "branch test"
00:13:53.264494 git.c:455 trace: built-in: git commit -m 'branch test'
00:13:53.266471 run-command.c:668 trace: run_command: git maintenance run --auto --no-quiet
00:13:53.268711 git.c:455 trace: built-in: git maintenance run --auto --no-quiet
[dev 2b34020] branch test
1 file changed, 1 insertion(+)

Delete

切换回主分支,合并分支,删除分支。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
fr3y@fr3y-pc:~/learngit$ git checkout master
00:14:06.064454 git.c:455 trace: built-in: git checkout master
Switched to branch 'master'
fr3y@fr3y-pc:~/learngit$ cat readme.txt
Hello Git!
Git is a distributed version control system under the GPL.
fr3y@fr3y-pc:~/learngit$ git merge dev
00:14:37.890681 git.c:455 trace: built-in: git merge dev
Updating b2e5ef1..2b34020
Fast-forward
00:14:37.901014 run-command.c:668 trace: run_command: git maintenance run --auto --no-quiet
00:14:37.902864 git.c:455 trace: built-in: git maintenance run --auto --no-quiet
readme.txt | 1 +
1 file changed, 1 insertion(+)
fr3y@fr3y-pc:~/learngit$ git branch -d dev
00:15:24.651829 git.c:455 trace: built-in: git branch -d dev
Deleted branch dev (was 2b34020).
fr3y@fr3y-pc:~/learngit$ git branch
00:15:55.718369 git.c:455 trace: built-in: git branch
00:15:55.718654 run-command.c:668 trace: run_command: unset GIT_PAGER_IN_USE; LESS=FRX LV=-c pager
* master

switch

创建并切换到新的dev分支,可以使用:

1
$ git switch -c dev

直接切换到已有的master分支,可以使用:

1
$ git switch master

总结

Git和其他版本控制系统如SVN的一个不同之处就是有暂存区的概念。

Git跟踪并管理的是修改,而非文件。

Git Cheat Sheet

配置

列出当前配置:
1
$ git config --list
列出repository配置:
1
$ git config --local --list
列出全局配置:
1
$ git config --global --list
列出系统配置:
1
$ git config --system --list
设置用户名:
1
$ git config --global user.name “[firstname lastname]”
设置用户邮箱:
1
$ git config --global user.email “[valid-email]”
复制一个已创建的仓库:
1
2
3
4
5
# 通过 SSH
$ git clone ssh://user@domain.com/repo.git

#通过 HTTP
$ git clone http://domain.com/user/repo.git
创建一个新的本地仓库:
1
$ git init

本地修改

显示工作路径下已修改的文件:
1
$ git status
显示与上次提交版本文件的不同:
1
$ git diff
把当前所有修改添加到下次提交中:
1
$ git add .
把对某个文件的修改添加到下次提交中:
1
$ git add -p <file>
提交本地的所有修改:
1
$ git commit -a
提交之前已标记的变化:
1
$ git commit
附加消息提交:
1
$ git commit -m 'message here'
提交,并将提交时间设置为之前的某个日期:
1
git commit --date="`date --date='n day ago'`" -am "Commit Message"
修改上次提交
1
$ git commit --amend
修改上次提交的committer date:
1
GIT_COMMITTER_DATE="date" git commit --amend
修改上次提交的author date:
1
git commit --amend --date="date"
把当前分支中未提交的修改移动到其他分支:
1
2
3
git stash
git checkout branch2
git stash pop
将 stashed changes 应用到当前分支:
1
git stash apply
删除最新一次的 stashed changes:
1
git stash drop

把bug提交的修改复制到当前分支

1
git cherry-pick <commit>

搜索

从当前目录的所有文件中查找文本内容:
1
$ git grep "Hello"
在某一版本中搜索文本:
1
$ git grep "Hello" v2.5

提交历史

从最新提交开始,显示所有的提交记录(显示hash, 作者信息,提交的标题和时间):
1
$ git log
显示所有提交(仅显示提交的hash和message):
1
$ git log --oneline
显示某个用户的所有提交:
1
$ git log --author="username"
显示某个文件的所有修改:
1
$ git log -p <file>
仅显示远端<remote/master>分支与远端<origin/master>分支提交记录的差集:
1
$ git log --oneline <origin/master>..<remote/master> --left-right
谁,在什么时间,修改了文件的什么内容:
1
$ git blame <file>
显示reflog:
1
$ git reflog show 
删除reflog:
1
$ git reflog delete

分支与标签

列出所有的分支:
1
$ git branch
列出所有的远端分支:
1
$ git branch -r
切换分支:
1
$ git checkout <branch>
创建并切换到新分支:
1
$ git checkout -b <branch>
基于当前分支创建新分支:
1
$ git branch <new-branch>
基于远程分支创建新的可追溯的分支:
1
$ git branch --track <new-branch> <remote-branch>
删除本地分支:
1
$ git branch -d <branch>
强制删除一个本地分支:
1
$ git branch -D <branch>
给当前版本打标签:
1
$ git tag <tag-name>
给当前版本打标签并附加消息:
1
$ git tag -a <tag-name>

更新与发布

列出当前配置的远程端:
1
$ git remote -v
显示远程端的信息:
1
$ git remote show <remote>
添加新的远程端:
1
$ git remote add <remote> <url>
下载远程端版本,但不合并到HEAD中:
1
$ git fetch <remote>
下载远程端版本,并自动与HEAD版本合并:
1
$ git remote pull <remote> <url>
将远程端版本合并到本地版本中:
1
$ git pull origin master
以rebase方式将远端分支与本地合并:
1
git pull --rebase <remote> <branch>
将本地版本发布到远程端:
1
$ git push <remote> <branch>
删除远程端分支:
1
2
3
$ git push <remote> :<branch> (since Git v1.5.0)
or
git push <remote> --delete <branch> (since Git v1.7.0)
发布标签:
1
$ git push --tags

合并与重置(Rebase)

将分支合并到当前HEAD中:
1
$ git merge <branch>
将当前HEAD版本重置到分支中:
1
$ git rebase <branch>
退出重置:
1
$ git rebase --abort
解决冲突后继续重置:
1
$ git rebase --continue
使用配置好的merge tool 解决冲突:
1
$ git mergetool
在编辑器中手动解决冲突后,标记文件为已解决冲突
1
$ git add <resolved-file>
1
$ git rm <resolved-file>
合并提交:
1
$ git rebase -i <commit-just-before-first>

把上面的内容替换为下面的内容:

原内容:

1
2
3
pick <commit_id>
pick <commit_id2>
pick <commit_id3>

替换为:

1
2
3
pick <commit_id>
squash <commit_id2>
squash <commit_id3>

撤销

放弃工作目录下的所有修改:
1
$ git reset --hard HEAD
移除缓存区的所有文件(i.e. 撤销上次git add):
1
$ git reset HEAD
放弃某个文件的所有本地修改:
1
$ git checkout HEAD <file>
重置一个提交(通过创建一个截然不同的新提交)
1
$ git revert <commit>
将HEAD重置到指定的版本,并抛弃该版本之后的所有修改:
1
$ git reset --hard <commit>
用远端分支强制覆盖本地分支:
1
git reset --hard <remote/branch> e.g., upstream/master, origin/my-feature
将HEAD重置到上一次提交的版本,并将之后的修改标记为未添加到缓存区的修改:
1
$ git reset <commit>
将HEAD重置到上一次提交的版本,并保留未提交的本地修改:
1
$ git reset --keep <commit>
删除添加.gitignore文件前错误提交的文件:
1
2
3
$ git rm -r --cached .
$ git add .
$ git commit -m "remove xyz file"