惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
Tailwind CSS Blog
大猫的无限游戏
大猫的无限游戏
L
LINUX DO - 热门话题
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
雷峰网
雷峰网
aimingoo的专栏
aimingoo的专栏
博客园_首页
MongoDB | Blog
MongoDB | Blog
V
V2EX
GbyAI
GbyAI
量子位
Microsoft Azure Blog
Microsoft Azure Blog
有赞技术团队
有赞技术团队
G
Google Developers Blog
云风的 BLOG
云风的 BLOG
B
Blog
Microsoft Security Blog
Microsoft Security Blog
S
SegmentFault 最新的问题
O
OpenAI News
N
News and Events Feed by Topic
博客园 - Franky
爱范儿
爱范儿
Forbes - Security
Forbes - Security
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
V2EX - 技术
V2EX - 技术
Application and Cybersecurity Blog
Application and Cybersecurity Blog
N
News and Events Feed by Topic
N
News | PayPal Newsroom
Schneier on Security
Schneier on Security
Cloudbric
Cloudbric
Security Archives - TechRepublic
Security Archives - TechRepublic
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Recent Commits to openclaw:main
Recent Commits to openclaw:main
人人都是产品经理
人人都是产品经理
P
Privacy International News Feed
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
B
Blog RSS Feed
阮一峰的网络日志
阮一峰的网络日志
D
DataBreaches.Net
Last Week in AI
Last Week in AI
罗磊的独立博客
Spread Privacy
Spread Privacy
Recent Announcements
Recent Announcements
The Cloudflare Blog
Google DeepMind News
Google DeepMind News
AWS News Blog
AWS News Blog
The Register - Security
The Register - Security
Y
Y Combinator Blog
J
Java Code Geeks
I
Intezer

阁子

小工具(三) 相机小述 四元数与旋转矩阵 小工具(二) 私有办公服务搭建 小工具(一) 图床搭建 Telegram接管聊天消息 Hashcat密码破解 Docker博客环境封装及自动化部署 小聊乐理 LXD搭设服务器 Time Machine 不满就折腾小记 GStreamer笔记五: Media Formats and Pad Capabilities GSreamer笔记四: GUI Toolkit Integration GStreamer笔记三: Time Management GStreamer笔记二: Dynamic Pipeline GStreamer笔记一: GStreamer Concepts
Git基本用法
本文作者: dfine · 2019-03-21 · via 阁子

目前版本控制系统比较流行的就是SVN和Git了,相比较而言,Git有分布式的优势,对网络依赖性更低,但SVN简单,这一条就有很强的生存能力了。用Git已经好几年了,不过很长一段时间只是在用clone pull add commit push这些类Ctrl+C/V的命令(Office中),连操作Head指针实现Ctrl+Z/Y都没怎么用,想起去年收到了Leancloud的10X程序员笔记本,里面附页还写着几行Git命令,突然觉得有些陌生了。

也只是突然想到,回忆一下,当是补上多年前未肯作的笔记了。

基本文件操作

检查文件状态

Git检查文件状态可以使用git status,可以看到已经提交的修改和未提交的修改:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

On branch master

Your branch is up to date with 'origin/master'.

Changes to be committed:

(use "git reset HEAD <file>..." to unstage)

modified: source/_drafts/git.md

modified: source/talks/index.md

Changes not staged for commit:

(use "git add <file>..." to update what will be committed)

(use "git checkout -- <file>..." to discard changes in working directory)

modified: source/_drafts/git.md

使用git diff可以查看尚未暂存的文件的修改:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

@@ -1,4 +1,26 @@

...

+Git检查文件状态可以使用`git status`,可以看到已经提交的修改和未提交的修改:

+On branch master

+Your branch is up to date with 'origin/master'.

+

+Changes to be committed:

+ (use "git reset HEAD <file>..." to unstage)

+

+ modified: source/_drafts/git.md

+ modified: source/talks/index.md

+

+Changes not staged for commit:

+ (use "git add <file>..." to update what will be committed)

+ (use "git checkout -- <file>..." to discard changes in working directory)

+

+ modified: source/_drafts/git.md

+

+使用`git diff`可以勘察尚未暂存的文件的修改:

\ No newline at end of file

另外加上--cached或者--staged(新版支持)参数,可以直接查看已暂存的和上次提交时的差异。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

git diff --staged

diff --git a/source/_drafts/git.md b/source/_drafts/git.md

index b220f55b..ce996f92 100644

--- a/source/_drafts/git.md

+++ b/source/_drafts/git.md

@@ -1 +1,4 @@

-title: Git用法

+title: Git基本用法

+

+目前版本控制系统比较流行的就是SVN和Git了,相比较而言,Git有分布式的优势,对网络依赖性更低,但SVN简单,这一条就有很强的生存能力了。用Git已经好几年了,不过很长一段时间只是在用`clone pull add commit push`这些类`Ctrl+C/V`的命令(Office中),连操作Head

指针实现`Ctrl+Z/Y`都没怎么用,想起去年收到了Leancloud的`10X`程序员笔记本,里面附页还写着几行Git命令

+#

\ No newline at end of file

diff --git a/source/talks/index.md b/source/talks/index.md

index d13db982..63515f47 100644

基本文件操作

除去系统自带的mv或者rm命令,Git也有自己的git mvgit rm命令,在Git仓库中,后者不仅仅是对文件做了前者的操作,也在工作目录中做了前者的操作。
git rm在删除文件后,也从跟踪文件清单中删除了该文件(使用--cached只是从暂存区中删除,使用-f同时也删除文件),以后不会再跟踪该文件,而rm命令的操作记录依然会被记录在跟踪文件清单中。
一个简单的例子,先创建一个文件:

1

touch test

此时未放入暂存区,直接删除就可以,Git也不会记录,但是如果Git已经跟踪了该文件,则直接删除状态为:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

git add test

rm test

git status

On branch master

Your branch is up to date with 'origin/master'.

Changes to be committed:

(use "git reset HEAD <file>..." to unstage)

modified: source/_drafts/git.md

new file: test

Changes not staged for commit:

(use "git add/rm <file>..." to update what will be committed)

(use "git checkout -- <file>..." to discard changes in working directory)

modified: source/_drafts/git.md

deleted: test

如果使用git rm test,可以看到:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

git rm test

git status

On branch master

Your branch is up to date with 'origin/master'.

Changes to be committed:

(use "git reset HEAD <file>..." to unstage)

modified: source/_drafts/git.md

Changes not staged for commit:

(use "git add <file>..." to update what will be committed)

(use "git checkout -- <file>..." to discard changes in working directory)

modified: source/_drafts/git.md

可以看到,test文件的记录已经被删除了。
同样,git mv也是一样的类型,git mv file1 file2相当于:

1

2

3

mv file1 file2

git rm file1

git add file2

查看提交历史

查看每次的提交历史可以直接使用git log,可以看到每次的提交记录。另外,加上-p参数可以展开每次提交的内容差异,加上-{d}可以指定显示最近次数的差异,如-2显示最近两次提交的差异。加上--since或者--until可以限制时间查询,如可以用git long --since=2.weeks显示最近两周的修改。加上--word-diff可以进行单词层面的对比,加上--graphASCII图形表示的分支合并历史。如果只想看每次提交的简略信息,可以加上-stat参数。另外,可以使用--pretty指定展示提交历史的格式,如用oneline将每个提交放在一行显示(--pretty常用参数有oneline,short,full,fuller和format(后跟指定格式))。

撤销操作

仅修改提交信息

如果提交信息写错了,或者有些文件漏掉了未添加到暂存区,可以使用amend指令重新提交:

1

2

3

git commit -m "add test"

git add test

git commit --amend

这样就完成了提交信息的修改。

取消暂存区文件

如果想取消暂存区的某个文件的暂存,有两种方法。一是上面的git rm --cached直接将文件从暂存区中删除,实际文件不受影响。另外一个是HEAD指针的操作。
HEAD可以理解为指向当前分支的指针,指向该分支最近一次的调用,操作HEAD指针即可实现版本回退等操作。
这里直接使用reset命令,将某个文件重置到最近一次提交时的状态:

1

git reset HEAD test

因为上次test未暂存,所以相当于从暂存区中取消该文件。

撤销对文件的修改

使用git checkout -- file可以撤销上次提交以来,对某个文件的所有修改,本质上是拷贝了上次提交时的该文件来覆盖它,因此对该文件做的任何修改都会消失。该命令需要谨慎使用,最好的方式是通过分支的保存进度来恢复。
Git中所有已经提交的东西基本上都是可以恢复的,但未暂存的就不属于Git恢复的范畴了。

远程仓库

Git主要是在本地修改好了再推送到远程仓库,实际上对远程仓库的操作比较少,就一些基本的推拉行为。

  1. 查看远程仓库。
    直接使用git remote即可查看当前的远程仓库,加上-v选项可以以详细模式查看。
  2. 添加远程仓库。
    直接使用git remote add <shortname> <url>,将仓库名和地址添加即可。
  3. 从远程仓库抓取数据。
    有两种需求,一种是只从远程仓库拉取数据,但并不合并到当前分支,可以使用git fetch <remotename>命令。
    另外,使用git clone获取的远程仓库会自动归于origin名下。
    另一种,需求是自动抓取并合并到当前分支,可以使用git pull命令。
  4. 推送数据到远程仓库。
    基本操作,git push <remotename> <branch>
  5. 查看远程仓库信息。

    1

    git remote show <remote-name>

  6. 远程仓库的删除和重命名。

  • 删除远程仓库: git remote rm <remotename>
  • 重命名远程仓库: git remote rename <orignname> <newname>

标签

Git可以给历史中的某个提交打上标签,以示其重要性,如v1.0等。

列出标签

列出已有标签,可以直接使用git tag命令,加上-l参数可以过滤选项。如

1

git tag -l 'v1.0.1*'

创建标签

标签分为轻量标签和附注标签,轻量标签如其名轻量,只是一个特定提交的引用,本质上是将提交校验和存储到一个文件中,没有保存其他任何信息,因此创建也比较简单。附注标签则是Git数据库中的一个完整对象,是可以被校验的。附注标签通常包含打标签者的姓名、邮件地址、日期、标签信息等,并可以使用GPG(GNU Privacy Guard)签名及验证。

  • 创建附注标签: 最简单的方式是使用tag-a选项:

1

git tag -a v1.1 -m "new test version"

查看标签:

1

2

3

git tag

v1.0

v1.1

其中,-m是存储在标签中的信息,是必填内容。使用git show也可以看到标签信息与对应的提交信息。

  • 创建轻量标签: 轻量标签的创建不需要任何选项,直接提供标签名字即可。

1

git tag v1.11

查看标签:

1

2

3

4

git tag

v1.0

v1.1

v1.11

此时用git show只能看到标签的提交信息,没有额外信息。

后期上标签

也可以对过去的提交上标签,使用git log --pretty=oneline时可以看到每次提交的校验和,如某次校验和是e0c29751bf13be3df3b5030cc589685752bd9fb6,则可以通过该校验和给该次提交打上标签:

1

git tag -a v0.8 e0c2975

实际只需要部分校验和即可。

分享标签

通常情况,git push并不会将标签推送到服务器上,需要通过显示命令才能分享标签到远程仓库。

1

git push origin <tagname>

如果要一次性推送所有本地新增标签到服务器上,则可以使用--tags参数:

1

git push origin --tags

删除标签

删除本地仓库的标签,可以使用:

1

git tag -d <tagname>

如果要同时删除远程标签,则需要使用git push <remotename> :refs/tags/<tagname>来更新远程仓库标签。

标签检出

可以使用git checkout命令查看某个标签指向的文件版本。但会使仓库处于头指针分离(“detacthed HEAD”)的状态:在”头指针分离“状态下,如果做了某些更改然后提交他们,标签不会发生变化,但新的提交不属于任何分支,也无法访问,除非确切的提交哈希。所以如果要进行更改,通常需要创建一个新分支:

1

2

git checkout -b newversion v1.12

Switched to a new branch 'newversion'

如果继续对newversion分支做改动,该分支的提交指针会继续向前移动,就不是原来的v1.12标签了。

分支

Git好用很大原因是其极具优势的分支模型,使得分支处理方式更为轻量。
在使用git commit新建一个提交对象前,Git会先计算每一个子目录的校验和,然后在Git仓库将这些目录保存为一个Tree对象,然后就可以创造一个提交对象,并包含了指向这个Tree对象的指针。Git使用blob类型的对象存储此次保存的快照。
关于Git的树结构,可以用Git官方仓库中的一张图说明:

这是首次提交后的结构图,此时Git仓库中有五个对象(五个校验和),最右侧的是三个存储文件快照的blob对象,中间是记录目录结构和blob对象索引的树对象,最左侧是包含指向书对象的指针和所有提交信息的提交对象。
此时因为是第一次提交,相当于祖先提交,提交对象中没有父对象,但之后的所有提交对象中,都会多一个父对象指针,指向上次提交。

Git分支在本质上是一个指向最新提交对象的指针,每次提交操作之后,指针都会更新到最新提交。

分支就是某个提交对象往回看的历史。

使用git branch可以列出所有的分支,加上--merged--no-merged可以显示已合并或未合并的分支。

分支创建

Git使用master作为默认的分支名,如果要创建分支,可以使用branch选项。

1

git branch <branchname>

但此时只是新建了一个分支,并未将当前工作分支切换过去。Git确定当前工作的分支是使用HEAD指针,HEAD指针指向哪个分支,当前就在哪个分支工作。

也可以使用git log -decorate命令查看各个分支当前所指的对象。

分支切换

切换分支即修改HEAD指针指向,可以使用chenkout命令实现。

1

git checkout <branchname>

在每次提交后,HEAD指针会随着当前分支一起向前移动以保证以后分支能正确切换回来。
或者直接使用命令:

1

git checkout -b <branchname>

可以在新建分支的同时切换到该分支,-b可以理解为branch,相当于:

1

2

git branch <branchname>

git checkout <branchname>

分支合并

在某个分支上进行操作,使得该分支指针向前移动后,如果要将该分支合并到其他分支,则可以切换到其他分支进行merge操作:

1

git merge <branchname>

当两个分支没有需要解决的分歧时,可以直接合并。

删除分支

当分支不再使用时,可以删除:

1

git branch -d <branchname>

对于未合并的分支,直接删除会失败,可以使用-D强制删除。

冲突合并

如果合并的两个分支,并不是直接祖先关系,两个分支在其共同祖先分支上都做了修改,如果修改没有冲突,如修改的都是不同的文件,则Git会自动新建一个提交,将共同祖先分支以及两个要合并的分支共同合并建立一个新的提交。此时Git会自行决定选取哪个提交作为最优的共同祖先。
但是如果两个不同分支都对同一个文件做了修改,在合并时就会引起冲突,因为Git不知道到底该对这个文件做如何操作。此时Git会先暂停下来,等待用户解决冲突。这种情况在平时也经常会遇到,如在本地对某个远程仓库做了修改,但是远程仓库在此之前已经在另一台电脑上做了push操作,这时使用pull操作就会自动抓取并合并到当前分支,如果存在冲突,pull时就会提示哪个文件修改冲突,并等待用户解决。此时,可以使用git status查看状态。
解决冲突后可以重新使用git add将其标记为冲突已解决。

远程分支

远程引用是指向远程仓库的指针,包括分支、标签等,可以通过git ls-remote <remotename>查看远程引用的完整列表,或者通过git remote show <remote>查看远程分支的更多信息。
远程跟踪则是指向远程分支状态的引用,只有当与远程仓库通信时,它们会自动移动。用户无法手动修改其状态。
可以使用git fetch命令将远程仓库中的内容拉取到本地,同事远程跟踪会更新到新的远程分支状态。当本地与远程的工作出现分叉之后,合并到本地分支时,依然会考虑是否有冲突的问题,解决方式和其他冲突分支合并一样。

推送本地分支

使用git push将本地分支推送到远端:

1

git push origin test

等价于

1

git push origin test:test

Git会自动将test名字展开为refs/heads/test:refs/heads/test

跟踪分支

使用checkout可以实现对分支的跟踪:

1

git checkout --track origin/test

通常可以新建一个本地分支来跟踪拉取的远程分支:

1

git checkout -b sf origin/test

也可以使用-u--set-upstream-to选项来直接设置已有的本地分支来跟踪拉取的远程分支:

1

git branch -u origin/test

另外,可以使用git branch -vv命令查看设置的所有跟踪分支。

合并分支

可以使用git fetch拉取分支后再使用git merge合并到本地分支,也可以直接使用git pull拉取并合并到本地分支。但是有时候git pull会显得有些佛性,难以理解,最简单的方式是fetchmerge的组合。

删除分支

删除远程分支可以使用:

1

git push origin --delete test

或者直接将空分支推送到远端覆盖远端分支即可:

1

git push origin :<remotebranch>

变基

这个是个有趣的用法,自从有了变基,Github就变成了Gayhub (逃 stuck_out_tongue_winking_eye )。
啊呸!当然不是这个原因。
变基是一种整合分支的方法,通常整合分支有两种方法:合并和变基。
合并(merge)之前已经经常用到了,主要就是将一个分支合并到另一个上。而变基(rebase)则是将一个分支里提交的修改在另一个分支上重放一边,也就是走别人的路,让别人说去吧。
一个基本的例子如下:

1

2

git checkout branch1

git rebase branch2

此时,Git会先找到这两个分支的分叉点(即最近共同祖先),然后从分叉点开始,将branch1所经历的操作,给branch2也体验一下。然后回到branch2,进行一次快进合并:

1

2

git checkout branch2

git merge branch1

其实就这个例子来看,变基和合并没有任何区别,但这样可以保证在向远程分支推送时保持提交历史的简洁。
另外,变基可以放到其他分支进行,并不一定非得依据分化之前的分支。可以从一个特性分支里再分出一个特性分支,然后跳过前面的特性分支,将后者与主分支进行变基,可以使用--onto选项。

1

git rebash --onto master branch1 branch2

即取出branch2分支,找到branch1branch2的分离点,然后在master分支上重放其共同祖先之后的修改。
然后就可以将变基后的分支快进合并到master分支上:

1

2

git checkout master

git merge branch2

剩下的也可以将branch1合并到master中:

1

git rebase master branch1

然后快进合并master分支:

1

2

git checkout master

git merge branch1

之后就可以删除无用的分支了。

变基风险

因为人人都可以编辑,所以一旦分支中的对象提交发布到公共仓库,就千万不要对该分支进行变基,不然其他人不得不重新将手里的工作和你的提交进行整合,接下来你也要重新拉取他们的提交进行整合,引入太多不必要的麻烦。
总之用官方一句加粗的话说:

不要对在你的仓库外有副本的分支执行变基。

其他操作

别名

和Linux的alias命令一样的意思,也是方便在git中快速操作。

1

2

3

4

$ 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 co即可实现git checkout命令。

储藏

当不想提交现在的工作状态,又想切换到别的分支进行工作,可以先将当前状态出藏起来。储藏(Stash)可以获取工作目录的中间状态——也就是修改过的被追踪的文件和暂存的变更——并将它保存到一个未完结变更的堆栈中,随时可以重新应用。
使用git stash list可以查看当前储藏的列表。
如果之后要恢复储藏的状态,可以使用:

1

git stash apply

Git则会默认恢复最近一次的储藏,如果想应用更早的储藏,则可以通过名字指定,如:

1

git stash apply stash@{2}

此时对文件的变更被重新应用,但是被暂存的文件没有重新被暂存。可以通过运行git stash apply命令时带上一个--index的选项来告诉命令重新应用被暂存的变更。
apply选项只尝试应用储藏的工作,但储藏的栈上仍然有该储藏。可以通过运行git stash drop,加上希望移除的储藏的名字来移除该储藏,或者直接通过git stash pop来重新应用储藏并在此之后快速删除栈上的储藏。

取消储藏

如果要取消之前所应用的储藏的修改,可以通过取消该储藏的补丁达到该效果:

1

git stash show -p stash@{0} | git apply -R

如果没有指定储藏名称,则会自动选择最近的储藏:

1

git stash show -p | git apply -R

从储藏中创建分支

在储藏一个工作状态后,继续在该分支上工作,最后还原储藏的时候可能会引起合并冲突,此时可以新建一个储藏分支简化工作。

1

git stash branch <branchname>

此时Git会创建一个新的分支,检出储藏工作时的所处的提交,重新应用,如果成功,则丢弃储藏。