Git学习笔记
Git Notes
[toc]
零、我在学习Git过程中用到的资料
尚硅谷Git入门到精通全套教程(涵盖GitHub\Gitee码云\GitLab)
https://learngitbranching.js.org/?locale=zh_CN
其他资料(待更新)
一、Git 概述
Git 是分布式的版本控制系统。
版本控制:记录文件内容变化,最重要的是记录文件修改历史记录。
版本控制系统有两类:
- 集中式版本控制系统:CVS、SVN 等,所有的文件修改版本保存在一个单一的集中服务器。用户从服务器上提取最新版文件进行修改,再提交到服务器。
- 分布式版本控制系统:Git 等,提取的不是最新版本的文件快照,而是镜像整个代码仓库为一个本地库,每一次提取实际上就是对代码库进行备份。修改完后将代码推送至代码托管中心(远程库),远程库的代码是最新的。
- 主要区别在于:服务器断网也可以开发,且每个客户端都保存着完整代码。
Git运行机制:
图源:https://walkssi.com/git/ - 工作区:代码存放的位置。在工作区可以修改代码,且没有历史记录;
- 暂存区:将工作区的代码添加 (
git add
) 到暂存区,也可以修改代码并且没有历史记录; - 本地库:将暂存区的代码提交 (
git commit
) 到本地库,就会生成历史版本。在本地库的版本不能修改。若发现代码不好,只能在工作区修改后再次提交,提交后本地库同时存在这两个版本。例如先提交了 v1 版本,发现不好,则只能在 v1 版本基础上修改然后提交为 v2 版本; - 远程库(代码托管中心):将本地库的代码推送 (
git push
) 至远程库。
注意⚠️:所有的版本控制系统都只能跟踪纯文本文件的改动,比如 TXT 文件、网页、源代码等。无法跟踪图片、视频,以及 Word 格式等二进制文件内部的变化。
二、常用的 Git 命令
1 | git config --global user.name <your name> # 设置全局用户签名 |
设置用户签名
用于区分不同的本地作者身份,在每一个提交的版本之中可以看到是谁提交的。
1 | git config --global user.name <your name> # 设置全局用户签名 |
在 C:\Users\SDYZZY\.gitconfig
(Windows) 或 /Users/sdyzzy/.gitconfig
(MacOS) 文件中可以查看是否修改成功。
初始化本地库
让 Git 获得项目目录的管理权。在项目目录内执行 git init
来初始化本地库,完成后项目目录内会增加一个 .git
隐藏目录。
1 | (base) sdyzzy@SDYZZY-MacBook-Pro git_study % git init |
查看本地库状态
在项目目录内执行 git status
来查看当前项目的本地库状态,包括目前所处的分支 (branch),有无需要添加暂存区、提交本地库的文件或目录。
1 | (base) sdyzzy@SDYZZY-MacBook-Pro git_study % git status |
main
指当前分支 (Windows 里面为master
)。no commits yet
指目前没有提交过任何文件。nothing to commit (create/copy files and use "git add" to track)
指当前没有文件需要提交。
在当前目录新建一个文档 hello.txt
,再查看本地库状态。输出中的 Untracked files:
表示此时在工作区存在一个叫 hello.txt
的未追踪的文件,可以使用 git add
追踪(即添加到暂存区)。
1 | (base) sdyzzy@SDYZZY-MacBook-Pro git_study % touch hello.txt |
注意⚠️:新建的空目录无法被检测到,因为
git
是对“文件的内容”来计算的。解决方法是在空目录中随意创建一个文件,使得git
可以检测到该目录的存在。
忽略指定文件或目录
在项目目录内创建一个名为 .gitignore
的文件,在其中添加需要忽略的文件名或目录名(一行只能输入一个名称),可以使用通配符忽略一类文件或目录。
- 以
#
开始:注释行。 - 以
/
开始:防止递归(只忽略当前目录下的该文件,而不忽略子目录下的同名文件)。 - 以
/
结尾:指定目录(忽略项目中所有该名称的目录)。 - **问号
?
只匹配一个任意字符。星号*
匹配零个或多个任意字符。方括号[abc]
匹配任何一个在方括号中的字符 (这个例子要么匹配一个 a,要么匹配一个 b,要么匹配一个 c);如果在方括号中使用短划线分隔两个字符, 表示所有在这两个字符范围内的都可以匹配(比如 [0-9] 表示匹配所有 0 到 9 的数字)。两个星号 ****
表示匹配任意中间目录,比如a/**/z
可以匹配a/z
、a/b/z
或a/b/c/z
等。
1 | # .gitignore |
例如扩展名为 .pyc
的文件根据 .py
文件自动生成,存储在目录 \__pycache__
中,无须让 git
跟踪它们。
1 | (base) sdyzzy@SDYZZY-MacBook-Pro git_study % echo "__pycache__/" >> .gitignore # 忽略__pycache__/ |
添加到暂存区
git add <filename1> <filename2> ...
指定添加哪些文件到暂存区。git add --all
将项目目录中所有被修改过的文件添加到暂存区。
1 | (base) sdyzzy@SDYZZY-MacBook-Pro git_study % git add hello.txt |
此时再查看本地库状态,Changes to be committed:
表示已经追踪到了文件,并且等待提交本地库。在暂存区的文件还未形成历史版本,可以用 git rm --cached <filename>
把暂存区内的文件删除(但是工作区里仍然存在,需要手动删除)。
注意⚠️:在 Windows 系统中可能会由于转换行末换行符而产生警告,一般不用管。
提交到本地库
git commit -m "commit message" <filename1> <filename2> ...
将暂存区内的指定文件提交到本地库。使用-m
或--message
参数添加对本次修改的描述,要求简单具体。如果不指定<filename>
,则会将暂存区中的所有文件提交到本地库。下面的代码表示在本地库主分支 (
main
) 提交了一个信息为 “create hello.txt” 的hello.txt
文件,(精简的)commit number 为 9c84112,1 个文件被改变,0 行内容被插入,0 行内容删除。1
2
3
4
5
6
7(base) sdyzzy@SDYZZY-MacBook-Pro git_study % git commit -m "create hello.txt" hello.txt
[main (root-commit) 9c84112] create hello.txt
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 hello.txt
(base) sdyzzy@SDYZZY-MacBook-Pro git_study % git status
On branch main
nothing to commit, working tree clean提交多行信息:两种方法。
如果给出多个
-m
选项,则它们的值被串联为单独的段落(段落之间会空一行)。1
2
3
4
5
6
7
8
9
10>> git commit -m "refactor" -m "delete file1"
>> git status
Author: SDYZZY <sdyzzy@mail.ustc.edu.cn>
Date: Mon Nov 13 23:06:44 2023 +0800
refactor
delete f1.md
end.先输入一个引号,然后键入内容,按回车键换行,继续输入。信息输入结束后,输入另一半引号,按回车键提交。
1
2
3
4
5
6
7
8
9
10
11
12>> git commit -m "refactor
>> delete f5
>>
>> end."
>> git status
Author: SDYZZY <sdyzzy@mail.ustc.edu.cn>
Date: Mon Nov 13 23:05:21 2023 +0800
refactor
delete f5
end.
commit 规范:
1
2
3
4
5
6
7<type>(<scope>): <subject>
<body>
# 示例
feat(新增视图层):添加登录页面
- 支持自动登录
登录页面,新增链接参数解析,如果链接中包含from=auto,则表示进行自动登录type
: commit 的类型,包括但不限于feat
: 新功能;fix
: 修复BUG;docs
: 文档更新;style
: 代码风格相关无影响运行结果的;- perf: 优化/性能提升;
refactor
: 重构;- revert: 撤销修改;
- test: 增加测试内容;
- undef: 不确定的分类。
scope
: commit 影响的范围,比如某某组件、某某页面。subject
: commit 的简短描述,不超过50个字符。Body
: 本次 commit 的详细描述,可以分成多行。
从工作区、暂存区、本地库删除:
git rm <filename>
:使用该命令删除工作区的文件,然后将“删除”这一修改提交本地库(添加暂存区已经由git
自动完成,因此这一语句相当于rm <filename>
+git add <filename>
),前提是暂存区里面没有该文件(如果有可以用下面的命令)。git rm --cached <filename>
:如果不想继续跟踪某个文件,可以使用该语句将该文件从git
中移除,然后将“删除”这一修改提交本地库(也可以使用该命令删除暂存区的文件)。git restore <filename>
:文件在暂存区且在工作区做了修改,执行该命令可以将工作区的文件恢复到最后一次add
的状态(也就是和暂存区一致)。git restore --staged <filename>
:文件在暂存区且在工作区做了修改,执行该命令可以将暂存区文件删除,且工作区文件不变(如果工作区未做修改,那么将暂存区的文件删除就相当于使文件不被追踪)。
查看 commit 记录
git log
按时间从新到旧顺序列出项目所有的提交历史。git log --oneline
:每次提交的记录只显示 1 行。git log <filename>
:查看特定文件的 commit 记录。git log -p <filename>
:查看特定文件的 commit 记录以及每次提交所引入的差异。git log --graph
:可视化 merge 过程。
git reflog
显示最近1个月 HEAD 和分支引用的指向。每当 HEAD 所指向的位置发生了变化,git 就会将这个信息存储到引用日志里。
1 | (base) sdyzzy@SDYZZY-MacBook-Pro git_study % vim hello.txt # 添加一行文本“Hello world” |
其中的 (HEAD -> main)
表示 HEAD 指针指向 main 分支的 commit number 为 4601fc5 的版本。
版本穿梭
git reset --hard <commit_number>
用于回退到过去的某个版本。
1 | (base) sdyzzy@SDYZZY-MacBook-Pro git_study % git log --oneline |
HEAD 指针已经指向了 commit number 为 9c84112 的历史版本,并且输出文件发现确实回到了该版本。实际上是 HEAD 指针指向了 main ,然后 main 指针改变指向了 9c84112 版本(在 \.git\refs\heads\main
文件内可以查看 main 指向的版本),因此版本穿梭的关键是改变指针指向。
修改 commit 记录
- 修改最后一次 commit 的记录:
git commit --amend -m "your new log"
,只需在原来的 commit 命令加上一个--amend
参数,并提供新的记录即可。 - 修改更早的记录:
git rebase -i <commit_number>
,进入 rebase 命令的交互模式,commit_number 表示从最后一次 commit 到 commit_number(不包括)指定的那一次 commit。此命令会进入一个 vim 编辑器,里面显示了这些 commit 的信息。参数pick
表示不对 commit 做改动;reword
表示修改 commit 信息,将需要修改的记录对应的参数改为reword
后,保存退出。然后会立即弹出另一个 vim 编辑器,在该编辑器之中修改 commit 信息,然后保存退出。命令执行结束后,所有被修改的 commit 以及它之后的 commit 的 SHA-1 值都会改变(因为历史信息改变了)。 - 用
git reset
版本穿梭,然后重新提交。 - 合并多个 commit 为一个commit:
git rebase -i <commit_number>
,rebase 命令的交互模式,commit_number 表示从最后一次 commit 到 commit_number(不包括)指定的那一次 commit。此命令会进入一个 vim 编辑器,里面显示了这些 commit 的信息。将pick
修改为squash
,然后保存退出,在弹出的 vim 编辑器里面修改 commit 信息。 git rebase -i
实现 commit 合并、分解、删除、修改记录、调整顺序、在指定的 commit 之间添加新的 commit等。
1 | (base)sdyzzy@SDYZZY-MacBook-Pro test % git reflog |
追加文件到最后一次 commit
有时会发现某些文件被漏掉了没有提交,但是又不愿意为此新建一次提交,可以使用该方法将漏掉的文件添加到最近一次的提交中。先添加到暂存区,然后按照上面的方法提交本地库并修改提交记录 git commit --amend -m "your log"
或者只提交本地库不修改记录 git commit --amend --no-edit
。
取消版本控制
rm -rf .git
。版本信息都存储在 .git
目录之中,删除该目录就可以取消版本控制。
版本迭代(修改文件)示例
修改 hello.txt
,然后再查看状态,红色表明修改了且还未追踪。
然后进行添加、提交,此时指针已经指向新的版本。
总结
使用 git reflog
或者 git log
查看各个版本情况,修改文件后可以用 git add 文件名
添加到暂存区,然后用 git commit -m "版本信息" 文件名
提交到本地库,若一段时间后不满意当前的版本,可以用 git reset --hard 版本号
回退到过去的某个版本。
三、Git分支
1 | git branch -l # --list,查看本地分支 |
多个分支
用户分支,测试分支,开发分支……不同分支互不干扰,可以同时推进,完成开发后就可以进行分支合并(在用户看来就是更新),如果失败也可以直接删除分支。
分支底层就是指针的引用,当创建一个新的 commit 的时候,当前分支(也就是 HEAD 指向的那个分支)会指向这个新的 commit,而其他分支的指向不会发生任何变化。
切换分支的时候,git 会用新的分支的 commit 内容来更新暂存区和工作目录(最好 commit 了再切换分支)。
创建分支
git branch <new branch name>
:创建分支。git branch <new branch name> <commit_number>
:在指定的 commit 处创建分支。
例如创建热修复分支 hot-fix:
1 | (base) sdyzzy@SDYZZY-MacBook-Pro learn_git % git branch -v |
查看已有分支
git branch -l
:查看本地分支。git branch -v
:查看本地分支以及最后一次 commit 的 hash 值。git branch -a
:查看本地、远程分支。
1 | (base) sdyzzy@SDYZZY-MacBook-Pro learn_git % git branch -v |
切换分支
git checkout <branch name>
。切换分支后,目录内的文件也会跟着切换(例如某个文件在 A 分支有而 B 分支没有,则从 A 分支切换到 B 分支后目录内该文件会消失,再切换回 A 分支又会出现)。
例如下面可以看到星号已经转移到了 hot-fix 分支前:
1 | (base) sdyzzy@SDYZZY-MacBook-Pro learn_git % git checkout hot-fix |
分支合并
git merge –no-ff <branch name>
:将 branch name 分支合并入当前分支。相当于用新的分支覆盖了当前分支(分支仅仅是指针,所以合并分支实际上是指的合并 commit,因此分支名也可以用 commit 号代替)。
Git 合并两个分支时,如果顺着一个分支走下去可以到达另一个分支的话,那么 Git 在合并两者时,只会简单地把指针右移,叫做“快进”(fast-forward)。这种情况历史里面看不出来曾经做过合并,而且如果删除分支则会丢失 merge 分支的信息。
加上
--no-ff
参数可以禁止 fast-forward 模式,这样合并后的历史记录能看出来曾经做过合并。正常合并:由 main 分支的文件创建了 hot-fix 分支,且 main 分支没有修改,但是 hot-fix 分支修改了,因此可以直接合并。
冲突合并:两个分支上的同一文件同一位置有两套不同的修改,不能直接合并,需要手动合并:打开文件,手动删除多余的,然后添加、提交(此时提交不能带文件名)。
例如现在位于 main 分支,新建一个分支切换过去,创建一个
merge.md
文件并输入 “This is BIG”,添加提交。然后回到 main 分支,也创建一个merge.md
文件并输入 “This is small”,添加提交。接着尝试 git merge,会报错。此时可以 vim 进入该文件看冲突在哪里,并手动修改。然后直接添加提交即可完成 merge。此时切回 hot-fix 分支,发现文件没有被修改,只有 main 分支(合并分支)被修改了。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
61
62
63
64
65
66(base) sdyzzy@SDYZZY-MacBook-Pro test % git branch future
(base) sdyzzy@SDYZZY-MacBook-Pro test % git checkout future
(base) sdyzzy@SDYZZY-MacBook-Pro test % echo "This is BIG" >> merge.md
(base) sdyzzy@SDYZZY-MacBook-Pro test % git add .
(base) sdyzzy@SDYZZY-MacBook-Pro test % git commit -m "BIG"
(base) sdyzzy@SDYZZY-MacBook-Pro test % cat merge.md
This is BIG
(base) sdyzzy@SDYZZY-MacBook-Pro test % git checkout main
(base) sdyzzy@SDYZZY-MacBook-Pro test % echo "This is small" >> merge.md
(base) sdyzzy@SDYZZY-MacBook-Pro test % git add .
(base) sdyzzy@SDYZZY-MacBook-Pro test % git commit -m "small"
(base) sdyzzy@SDYZZY-MacBook-Pro test % cat merge.md
This is small
(base) sdyzzy@SDYZZY-MacBook-Pro test % git merge future
Auto-merging merge.md
CONFLICT (add/add): Merge conflict in merge.md
Automatic merge failed; fix conflicts and then commit the result.
(base) sdyzzy@SDYZZY-MacBook-Pro test % git status
On branch main
You have unmerged paths.
(fix conflicts and run "git commit")
(use "git merge --abort" to abort the merge)
Unmerged paths:
(use "git add <file>..." to mark resolution)
both added: merge.md
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)
(commit or discard the untracked or modified content in submodules)
modified: git_study (untracked content)
no changes added to commit (use "git add" and/or "git commit -a")
(base) sdyzzy@SDYZZY-MacBook-Pro test % vim merge.md
<<<<<<< HEAD
This is small
=======
This is BIG
>>>>>>> future
# <<< 和 === 之间的是当前分支内容,=== 和 >>> 之间是被合并进来的分支的内容
# 手动将所有内容删除,然后键入 " This is medium",wq 保存
(base) sdyzzy@SDYZZY-MacBook-Pro test % git commit -am "medium"
[main 3149370] medium
(base) sdyzzy@SDYZZY-MacBook-Pro test % git log --graph
* commit 314937084e5fd0805e834071275149bdd6f1b59f (HEAD -> main)
|\ Merge: 782b132 b0a3a75
| | Author: SDYZZY <sdyzzy@mail.ustc.edu.cn>
| | Date: Mon Nov 20 20:05:28 2023 +0800
| |
| | medium
| |
| * commit b0a3a754dc12d5a23f94d11564934fe09377de89 (future)
| | Author: SDYZZY <sdyzzy@mail.ustc.edu.cn>
| | Date: Mon Nov 20 19:52:33 2023 +0800
| |
| | BIG
| |
* | commit 782b13206ac8a35774f17443c20d9d6009497cb1
|/ Author: SDYZZY <sdyzzy@mail.ustc.edu.cn>
| Date: Mon Nov 20 19:53:38 2023 +0800
|
| small
(base) sdyzzy@SDYZZY-MacBook-Pro test % git branch -d future
Deleted branch future (was b0a3a75).
总结
分支 main、hot-fix 是指向具体版本记录的指针,head 则指向当前分支。创建分支就是多创建一个指针。
四、Git 标签
1 | git tag <your tag> <commit_number> # 创建轻量标签 |
标签与分支类似,都是指向某个 commit 的指针。区别在于,分支会随着 commit 的前进而前进,但是标签一直留在那个 commit 上。
标签分类:轻量标签(lightweight tag)和有附注的标签(annotated tag)。
轻量标签一般用于暂时标记。更推荐使用有附注的标签,一般在开发到阶段性的时候贴上标签,比如版本号 1.0.0。
轻量标签:
git tag <your tag> commit_number
。查看轻量标签只显示指向的那个 commit 的信息。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17>> git log --oneline
70916df (HEAD -> main) refactor
03d9f85 refactor delete f5
>> git tag 测试标签 03d9f85
>> git log --oneline
70916df (HEAD -> main) refactor
03d9f85 (tag: 测试标签) refactor delete f5
>> git show 测试标签
commit 03d9f850b6f87779940534bf71b7e841d3809e54 (tag: 测试标签)
Author: SDYZZY <sdyzzy@mail.ustc.edu.cn>
Date: Mon Nov 13 23:05:21 2023 +0800
refactor
delete f5
end
...有附注的标签:
git tag <your tag> commit_number -a -m "your annotation"
。参数-a
让 git 创建有附注的标签,-m
输入附注。查看有附注的标签除了显示 commit 信息以外,还会显示谁在什么时候贴了这张标签以及附注信息。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21>> git tag 测试有附注的标签 70916df -a -m "这里是一些附注"
>> git log --oneline
70916df (HEAD -> main, tag: 测试有附注的标签) refactor
03d9f85 (tag: 测试标签) refactor delete f5
>> git show 测试有附注的标签
tag 测试有附注的标签
Tagger: SDYZZY <sdyzzy@mail.ustc.edu.cn>
Date: Tue Nov 14 19:44:06 2023 +0800
这里是一些附注
commit 70916dfdd42836afbc36cb2e4a973b9545752577 (HEAD -> main, tag: 测试有附注的标签)
Author: SDYZZY <sdyzzy@mail.ustc.edu.cn>
Date: Mon Nov 13 23:06:44 2023 +0800
refactor
delete f1.md
end.
...
查看标签:
git show <your tag>
。删除标签:
git tag -d <your tag>
。例子:
1
2
3git tag v1.4-lw # 创建轻量标签
git tag -a v2.0 -m '描述信息' # 创建附注标签
git tag -a v1.2 9fceb02 # 后期对指定提交打标签
五、Git 与远程库
1 | git remote -v # 查看当前所有远程地址别名 |
创建远程库
本地库和远程库之间通过 SSH 通信,因此创建远程库之前需要先设置 RSA 密钥对,执行指令
1 | ssh-keygen -t rsa -C "email@example.com" |
该命令会在用户主目录下生成 .ssh
目录,包含 id_rsa
(私钥)和 id_rsa.pub
(公钥)等文件。然后将公钥添加到远程服务器(例如 Github)。
- 点击 New Repository。
- 起远程库名,一般与本地库相同。
- 选择公共库与私有库(公共库读取权限公开)。
- 点击 code,可以看到远程库的
Https
和ssh
的地址链接。
查看远程库
git remote -v
查看已经连接好的远程库的别名和地址。git remote show <remote>
查看有关远程库的更多信息,包括远程库别名、地址、当前分支、远程分支状态(是否被追踪)。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18(base) sdyzzy@SDYZZY-MacBook-Pro GWData-Bootcamp % git remote -v
origin git@github.com:SDYZZY/GWData-Bootcamp.git (fetch)
origin git@github.com:SDYZZY/GWData-Bootcamp.git (push)
teacher git@github.com:iphysresearch/GWData-Bootcamp.git (fetch)
teacher git@github.com:iphysresearch/GWData-Bootcamp.git (push)
(base) sdyzzy@SDYZZY-MacBook-Pro GWData-Bootcamp % git remote show origin
* remote origin
Fetch URL: git@github.com:SDYZZY/GWData-Bootcamp.git
Push URL: git@github.com:SDYZZY/GWData-Bootcamp.git
HEAD branch: main
Remote branches:
homework tracked
main tracked
Local branch configured for 'git pull':
homework merges with remote homework
Local refs configured for 'git push':
homework pushes to homework (up to date)
main pushes to main (up to date)
添加远程库
克隆远程库以添加:
git clone <remote url>
将远程库克隆至本地(一般使用 SSH 地址。如果使用 https 克隆,则不需要 init,也不需要登陆账号)。- clone 做了这三件事:初始化本地库、初始化本地库、拉取代码、创建别名。clone 会在当前目录下创建一个和远程库同名的目录并切换进去,然后在该目录下初始化本地库,并为这个远程库指定别名(默认为 origin),最后将远程库的数据拉取并存放在该目录下。因此
git clone
相当于mkdir
+git init
+git remote add
+git fetch
+git checkout
。 - origin 是 git 给克隆的远程库的默认别名。
- clone 做了这三件事:初始化本地库、初始化本地库、拉取代码、创建别名。clone 会在当前目录下创建一个和远程库同名的目录并切换进去,然后在该目录下初始化本地库,并为这个远程库指定别名(默认为 origin),最后将远程库的数据拉取并存放在该目录下。因此
获取远程库地址以添加:
git remote add 别名 远程地址
。链接太长,对链接起别名,以后 push 和 pull 就可以直接用别名代替链接,一般就用远程库名作为别名。1
2
3
4
5(base) sdyzzy@SDYZZY-MacBook-Pro learn_git % git remote -v
(base) sdyzzy@SDYZZY-MacBook-Pro learn_git % git remote add testgit https://github.com/SDYZZY/testgit.git
(base) sdyzzy@SDYZZY-MacBook-Pro learn_git % git remote -v
testgit https://github.com/SDYZZY/testgit.git (fetch)
testgit https://github.com/SDYZZY/testgit.git (push)
从远程库拉取数据至本地库
git fetch <remote>
拉取指定远程库中本地所没有的数据。它只会下载数据到本地而不会自动合并,所以需要在合适的时候手动合并(git merge
)。git pull 远程库别名或地址 远程分支名
。本地库与远程库不同步的时候,将远程库的内容拉取到本地库。git pull
的拉取过程实际上是git fetch
命令完成的。拉取后远程库的内容会变成本地库的一个远端分支,然后再自动使用git merge <拉取的>
将远端分支与 main 分支合并。即git pull = git fetch + git merge
。显式地使用 fetch 与 merge 命令会更好一些。git clone
和git pull\fetch
的区别:clone 通常在第一次看到某个项目时将其下载到本地;pull\fetch 则是用于之后的更新。
推送本地库数据至远程库
git push 远程库别名或地址 本地分支
将指定的本地分支推送到远程库。- 只有本地分支和远程分支同步时才可以直接 push。如果别人先推送过一次,然后自己再推送,则自己的推送会被拒绝。需要先 pull 远程库,然后才可以推送。
git push <remote> <local branch>:<remote branch>
推送本地 branch 分支,将其作为远程库的 remote branch 分支,可以用于推送本地分支到一个命名不相同的远程分支。
远程库别名的重命名与移除
git remote rename <remote>
修改远程库的别名,同时也会自动修改远程追踪的分支名。git remote rm <remote>
移除远程库,所有和这个远程库相关的远程追踪分支以及配置信息也会一起被删除。
远程跟踪分支
git branch -vv
查看本地分支正在追踪哪个远程分支,以及本地分支相对于远程分支是领先(ahead,有本地的 commit 还未 push)、落后(behind)还是都有(远程分支有 commit 还没有 pull 到本地且本地也有 commit 还没有 push)。
git branch -u <remote>/<remote branch>
修改当前分支所追踪的上游分支为远程库的 remote branch 分支。
1 | (base) sdyzzy@SDYZZY-MacBook-Pro GWData-Bootcamp % git branch -vv |
Github 与合作
参考 gitpro p164
邀请合作者:点击 settings–>collaborators->add people
PullRequest 过程
fork 别人的仓库到自己账户下。
git clone
于自己的本地。本地修改
git push
于自己账户下的仓库。在自己的仓库,点击 Contribute,Open pull request, 输入 PR 相关信息,发送。
有时在自己fork之后,原项目作了更新,此时想让自己本地的和fork的项目跟上原项目,可以将原项目设置为自己的上游项目,fetch 后手动合并。具体步骤如下:
git remote add <原项目别名> <原项目地址>
,将原项目设置为远程节点。这一步做完后使用git remote -v
可以发现现在有两个远程节点,一个是 fork 之后在自己账号下那个仓库的,另一个就是刚设置的原项目仓库。git fetch <原项目别名> <原项目分支>
,将最新的原项目抓取下来。git merge FETCH_HEAD
或者git merge <原项目别名>/<原项目分支>
,将抓取的内容和本地合并。此时本地项目已经和原项目进度相同。git push <远程库别名>/<远程分支>
,将本地项目推送至远程库,此时 fork 的项目也和原项目进度相同。
==待看:第 4 次课 2h25m,如何接受 PR==
六、Git 原理
使用 git add
将文件添加到暂存区后,git 会生成一个 Blob 对象,这个 Blob 对象存储了文件的内容(不是文件本身,而是压缩后的内容)。然后 git 根据这个 Blob 的内容计算出一个 hash,并以 hash 的前两个字节为目录名,后 38 个字节为文件名,将这个 Blob 对象存储在 .git/objects
目录下(只要内容一样就是同一个 Blob 对象,比如两个不同名的空文件)。
文件的内容以 Blob 对象的形式存放;
文件及目录的名称以 Tree 对象的形式存放,Tree 对象的内容是某些 Blob 对象或者其它 Tree 对象的 hash 及名称(如果 Tree 对象里面还有其它 Tree 对象,那么该 Tree 对象就是个目录)
commit 对象指向某个 Tree 对象,并且还对指向前一次的 commit 对象(除了第一次 commit 以外)
Tag对象指向某个 commit 对象。
实际上除了 Blob 对象是存储的文件内容,其他对象都可以看作是存储的指针(hash),通过指针来判断归属关系。
Git flow
附录1:其他命令
查看文件和最后一次 commit 的区别
1 | git diff <filename> |
-
表示第 1 个文件。+
表示第 2 个文件@@ -1,2 +1,3@@
表示比较的区块:- 第 1 个文件的第 1 行起的连续 2 行
- 第 2 个文件的第 1 行起的连续 3 行
附录2:使用过程中遇到的一些问题
提示
fatal: detected dubious ownership in repository
:原因:根源在于文件夹的所有人和当前用户不一致。
解决方法:在文件夹的属性-安全-高级里面,更改所有者,并应用到所有的子目录和文件。
Git 报错:
1
Updates were rejected because the remote contains work that you do not have locally.
问题描述:在存在本地库且创建新的远程库后用
README
或者LICENSE
初始化过的情况下,如果直接 push 就会出现上述报错。问题在于初始化过程使得远程库具有了一些本地库所没有的文件(比如README
),这时直接 push 就会被 git 给 reject。解决方法1:在链接远程库之后、push 之前,先
git pull
远程库到本地库(or optionally,git pull origin master --allow-unrelated-histories
if you have initialized repo in github and also committed locally),然后再进行 push。解决方法2:直接
git push -f <远程分支> <本地分支>
,强行 push。解决方法3:创建远程库后,不要初始化。