Git精通之路
git [-v | --version] [-h | --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>]
git help git
使用查看上面命令说明
git --version
查看当前git版本,也可以用缩写形式-v,但是有些指令不支持缩写
git -v
git help -a
git help --all
查看当前帮助文档
git help <subcommand>
查看具体subcommand帮助 ,例如:
git help add
git status -- A.java
使用裸双破折号来分离命令行的控制部分和操作数部分
你可能需要使用双破折号分离并显式标识文件名,否则可能会误认为它们是命令的另一部分。例如,如果你碰巧有一个文件和一个标签都叫 main.c,然后你会看到不同的行为。
# Checkout the tag named "main.c"$ git checkout main.c
# Checkout the file named "main.c"$ git checkout-- main.c
保存用户名和邮箱到git,提交的版本库必须要有用户名和邮箱
git config user.name "Thor"
git config user.email "thor@126.com"
git config 文件
系统级别配置:/etc/gitconfig文件包含了适用于系统所有用户和所有库的值。这个文件通常由系统管理员编辑,用于设置系统级的Git配置。
用户级别配置:\~/.gitconfig文件具体到每个用户,位于用户的主目录下。通过传递--global选项,Git会读写这个特定的文件,用于设置用户级别的配置。这个文件适用于当前用户的所有Git仓库。
仓库级别配置:每个Git仓库都有一个.git/config文件,位于仓库的.git目录中。这个文件仅针对当前仓库有效,用于设置仓库级别的配置。
配置优先级
仓库级别>用户级别>系统级别
在Windows系统上,Git还会找寻用户主目录下的.gitconfig文件,并且还会尝试找寻/mingw64/etc/gitconfig文件,具体位置取决于Git的安装目录。例如,如果Git安装在C:\Program Files\Git,则相应的文件位置是C:\Program Files\Git\mingw64\etc。
通过命令行,可以使用以下命令查看不同级别的配置文件内容:
查看系统级别配置:git config --list --system
查看用户级别配置:git config --list --global
查看仓库级别配置:在仓库目录下执行cat .git/config或者直接打开仓库目录查看.git/config文件。
git config --list 或者 git config -l
查看所有配置选项
git config --global user.name ="username"
git config --global user.emal ="email"
去掉--global则可以设置一个特定版本库的username和email
git --system 修改系统级别配置
git config --unset user.name
移除一个配置参数
git config --global alias.show-b 'show-branch --more=10'
设置命令别名,这里添加了一个新的命令别名show-b 代替后面的长命令,这样以后都可以使用这个别名就可以代替这个长命令
git config 可以查看更多命令
Git基本命令
git init
将目录转换为版本库
Git不关心你是从一个完全空白的目录还是由一个装满文件的目录开始的。在这两种情况下,将目录转换到Git版本库的过程是一样的。
为了显示目录是一个Git版本库,git init命令创建了一个隐藏目录,在项目的顶层目录,名为.git。而CVS和SVN则将修订版本信息放在项目的每一个目录下的CVS和.svn子目录里,Git把所有修订信息都放在这唯一的顶层.git目录里。~/public_html目录下的一切都保持不变。Git将这个目录当做项目的工作目录,或者修改文件的目录。相反,隐藏在.git内的版本库由Git维护。
git init --bare
创建一个bare版本库,也就是裸版本库,是不包含工作区的版本库,通常用于作为远程仓库(remote repository),是不能直接修改文件,只能通过git push修改,后面可以同步不同的版本库到这个裸库,上面直接用git init出来的库别的开发版本库是不能往上面同步的,会导致版本问题。
查看历史提交记录
git log
e.g.
commit 8ca2b78a7eb0e13abf67af711da733e9c75c40d6
Author: yuxuehandong
Date: Fri Mar 31 17:34:56 2023 +0800
无
commit 23f432b179ddbbb9a648c50a5860d78ba1619af4
Author: yuxuehandong
Date: Tue Mar 28 11:31:35 2023 +0800
添加装饰模式demo
git log -1 -p + 提交码/提交码前4到6个字节
-1指定一个提交记录,-p查看变更
$ git log -1 -p 64b708
commit 64b7089bdaffe3eed60d259388382fba8800fa24
Author: yuxuehandong
Date: Mon Mar 24 10:03:56 2025 +0800
modify data
diff --git a/data b/data
index 116c7ee..59fba14 100644
--- a/data
+++ b/data
@@ -1 +1,2 @@
new data
+hello
-stat选项列举了提交中所更改的文件以及每个更改的文件中有多少行做了改动。
$ git log -1 --stat 64b708
commit 64b7089bdaffe3eed60d259388382fba8800fa24
Author: yuxuehandong
Date: Mon Mar 24 10:03:56 2025 +0800
modify data
data | 1 +
1 file changed, 1 insertion(+)
git show +提交码
显示更详细的信息,如果直接执行git show 后面不跟提交码则会显示最近的一次提交的详细信息
也可以使用 git show HEAD~2来查看父父提交,也就是上上一次提交
git show 0414099285562a6dd84e1cc554945c1cb75eed32
commit 0414099285562a6dd84e1cc554945c1cb75eed32 (HEAD -> master)
Author: yuxuehandong
Date: Wed Sep 4 09:20:10 2024 +0800
covert to heml
diff --git a/index.html b/index.html
index 2086113..fdd7352 100644
--- a/index.html
+++ b/index.html
@@ -1 +1,5 @@
+ <html>
+ <body>
My website is alive!
+ </body>
+ </html>
git diff
diff -r 项目1 项目2
查看两个版本库详细的差别,这个是linux的命令
在开头,原始文件被“---”符号标记起来,新文件被用“+++”标记。@@之间表示两个不同文件版本的上下文行号。以减号(-)开始的行表示从原始文件删除该行以得到新文件。相反,以加号(+)开始的行表示从原始文件中添加该行以产生新文件。而以空格开始的行是两个版本都有的行,是由-u选项作为上下文提供的。
$ cat initial $ cat rewrite
Now is the time Today is the time
For all good men For all good men
To come to the aid And women
Of their country. To come to the aid
Of their country.
$ diff -u initial rewrite
--- initial 1867-01-02 11:22:33.000000000 -0500
+++ rewrite 2000-01-02 11:23:45.000000000 -0500
@@ -1,4 +1,5 @@
-Now is the time
+Today is the time
For all good men
+And women
To come to the aid
Of their country.
git diff 工作目录与索引进行比较,也就是本地缓存区
git diff commit1_id 工作目录与提交进行比较
如果不写commit1_i默认是HEAD,这个形式命令会显示工作目录和给定提交间的差异。常见的一种用法是用HEAD或者一个特定的分支名作为commit。git diff显示仍留在工作目录中且未暂存的变更,还没有使用git add命令,此时使用git diff --cached则什么都不显示
$git diff
warning: in the working copy of 'b', LF will be replaced by CRLF the next time Git touches it
diff --git a/b b/b
index ce01362..b1bdbca 100644
--- a/b
+++ b/b
@@ -1 +1 @@
-hello
+hello b
git diff --cached commit1_id
这条命令会显示索引中的变更中和给定提交中的变更之间的差异。如果省略commit这一项,则默认为HEAD。使用HEAD,该命令会显示下次提交会如何修改当前分支。显示已经暂存并且因此要有助于下次提交的变更。 已经执行了git add命令,此时使用git diff 则什么都不显示
$git diff --cached
diff --git a/a b/a
index ce01362..74614c9 100644
--- a/a
+++ b/a
@@ -1 +1 @@
-hello
+hello a
执行git commit后 git diff 和git diff --cached 则什么都不显示
比较两次提交的差异
git diff commit1_id commit2_id
git diff --stat 这个选项会显示针对两个树状态之间差异的统计数据。报告用简洁的语法显示有多少行发生了改变,有多少行添加了,有多少行删除了。
$ git diff --stat
warning: in the working copy of 'a.txt', LF will be replaced by CRLF the next time Git touches it
a.txt | 1 +
1 file changed, 1 insertion(+)
git diff --stat HEAD~5 HEAD
显示连个提交之间文件的变更
$ git diff --stat HEAD~5 HEAD
a.txt | 1 +
b.txt | 1 +
bug005 | 1 +
c.txt | 1 +
d.txt | 1 +
5 files changed, 5 insertions(+)
只查看某个文件夹下文件的变化,或者某个文件的变化
$git diff HEAD~5 HEAD --stat dir1
$ git diff --stat HEAD~5 HEAD a.txt
a.txt | 1 +
1 file changed, 1 insertion(+)
$ git diff --stat HEAD~5 HEAD dir1/a.txt
a.txt | 1 +
1 file changed, 1 insertion(+)
git diff -S"hello" master~n -S"string"选项用来在master分支最近nn个提交中搜索包含string的变更。
$ git diff -S"helloa" master~3
diff --git a/a.txt b/a.txt
index f3e8683..a77a6ce 100644
--- a/a.txt
+++ b/a.txt
@@ -1 +1,3 @@
hhhh
+HelloWorld
+helloa
重命名文件
更改文件名字,修改后同样记得git commit 提交变更
git mv oldName newName
查看文件的提交记录,只能查询修改名称后的记录
$ git log newName
commit 0abff5b44b5462d560e3d8cd12dac26a4f39d473 (HEAD -> master)
Author: yuxuehandong
Date: Mon Mar 24 10:05:29 2025 +0800
rename data
可以查看修改名称之前的提交记录和修改名称之后的提交记录
$ git log --follow newName
commit 0abff5b44b5462d560e3d8cd12dac26a4f39d473 (HEAD -> master)
Author: yuxuehandong
Date: Mon Mar 24 10:05:29 2025 +0800
rename data
commit 64b7089bdaffe3eed60d259388382fba8800fa24
Author: yuxuehandong
Date: Mon Mar 24 10:03:56 2025 +0800
modify data
commit 10d4e7cc58fef7292363c6b4c3089f02313ea872
Author: yuxuehandong
Date: Thu Mar 20 11:02:05 2025 +0800
init
使用gitk查看提交历史的图表
创建项目副本
git clone 原项目目录 新项目目录
拉取远程仓库
方式一
git clone 远程仓库url
方式二 使用ssh方式
git clone 远程仓库ssh地址
使用此方式首先要配置ssh密钥,把本地的rsa public key配置到github账号中。Settings → SSH and GPG keys
ls -lsa 文件夹1 文件夹2
比较两个文件夹目录结构
git status
查看当前仓库同步状态
git add file1 file2
添加file1 和file2 只是暂存到git版本库,后面还需要commit提交变更到仓库才能更新版本库,文件只有在版本库中不存在才需要git add,如果存在后面可以直接git commit
git rm --cached file1
将刚添加的file1移除
git add .
把当前目录及子目录中的文件都添加到版本库里(参数“.”、点或者UNIX说法中的“dot”,是当前目录的简写)
提交变更
提交更新到当前分支
使用git commit 提交变更不会提交未暂存的,也就是没有使用git add添加的文件
git commit -m "这里填写提交注释"
git commit的-a或者-all选项Git会递归遍历整个版本库,暂存所有已知的和修改的文件,然后提交它们。虽然Git会递归遍历整个版本库,查找修改的和删除的文件,但是全新的文件目录subdir/及其中所有文件还是不会成为提交的一部分
删除文件
git rm fileName
删除一个文件,删除后记得使用git commit 提交变更
git rm--cached会删除索引中的文件并把它保留工作目录中,也就是git add的反向操作。而git rm则会将文件从索引和工作目录中都删除。删除后可以使用git ls-files --stage来查看索引状态
执行了git add, 没有执行git commit, git rm 必须使用 git rm --cached 指明删除索引,不删除文件。或者git rm -f 强制删除索引和文件
执行了git add,执行了git commit , git rm 文件名 会直接删除文件和索引, git rm --cached 会删除文件索引,但会保留文件
找回删除的文件
//HEAD 表示最新一次的提交。
//如果文件已经提交,则可以找回文件并自动添加索引。如果文件只是添加了索引未提交则找不回来
git checkout HEAD -- file_path
//恢复已经提交的被删除的文件
//--diff-filter=D 用于筛选删除的文件。
git log 查看之前的提交码
//找到删除前的提交哈希值(例如 abc123):
git checkout abc123^ -- file_path
git push origin master
推送更新到远程origin的master分支
通过SHA1值查看对应得内容,6a6a6y为SHA1值的前三个字节
git cat-file -p 6a6a6y
使用git ls-files命令查看隐藏在对象模型下的东西,并且可以找到那些暂存文件的SHA1值
git ls-files --stage
git hash-object file来直接计算和输出这个新版本的SHA1散列值。
$ git hash-object data
116c7ee1423b9a469b3b0e122952cdedc3ed28fc
在任何情况下,最重要的事是要记住工作目录下的文件版本和索引中暂存的文件版本可能是不同步的。当提交的时候,Git会使用索引中的文件版本。
撤销提交
撤销提交但保留更改(修改最近一次提交)
如果你只是想修改最近一次提交的内容(例如修改提交信息或添加漏掉的文件),可以使用 --amend 选项。
# 1. 修改文件(如果需要)
git add <file> # 添加漏掉的文件或修改后的文件
# 2. 修改提交
git commit --amend
撤销提交但保留更改(回退到提交前的状态)
如果你想撤销提交,但保留工作目录中的更改(即回到 git add 后的状态),可以使用 git reset,可以不改变修改,只改变提交的消息
git reset --soft HEAD~1
HEAD~1 表示回退到上一次提交。
--soft 选项会保留工作目录和暂存区的更改。
如果你想撤销提交,但保留工作目录中的更改(即回到 git add 之前的状态),可以使用 git reset --mixed。 也是git reset的默认模式
git reset --mixed HEAD~1
HEAD~1 表示回退到上一次提交。
--mixed 选项会保留工作目录的更改,回到git add之前的状态
撤销提交并丢弃更改(完全回退)
如果你想彻底撤销提交并丢弃所有更改(回到提交前的状态),可以使用 git reset --hard。
git reset --hard HEAD~1
--hard 选项会丢弃工作目录和暂存区的所有更改。
注意:此操作不可逆,谨慎使用!
撤销提交并生成一个新的反向提交
如果你已经将提交推送到远程仓库,并且不想修改历史记录,可以使用 git revert。这会创建一个新的提交,撤销最近一次提交的更改。适用于已经推送到远程仓库的情况,因为它不会修改历史记录。
git revert HEAD
撤销多次提交
如果你想撤销多次提交,可以使用以下命令:将 n 替换为你想撤销的提交次数。
# 撤销最近 n 次提交,但保留更改
git reset --soft HEAD~n
# 撤销最近 n 次提交,并丢弃更改
git reset --hard HEAD~n
cherry-pick 增加某个分支的某次提交到当前分支
当前rel分支最新log
admin@DESKTOP-ERUVGSL MINGW64 /e/liuhanze/project/testMerge (rel)
$ git log
commit f5b0bc47dec524561a3884380963206c6e1f339f (HEAD -> rel)
Author: yuxuehandong
Date: Wed Apr 23 10:35:38 2025 +0800
modify hello
commit 08d258d372a3fc79c52d4c5b62ee1ca5e611a889 (master)
Author: yuxuehandong
Date: Mon Apr 14 10:06:01 2025 +0800
initialize hello
dev分支最新的log
admin@DESKTOP-ERUVGSL MINGW64 /e/liuhanze/project/testMerge (dev)
$ git log
commit ee06185286bca75265caef51a39f3d261182c89d (HEAD -> dev)
Author: yuxuehandong
Date: Wed Apr 23 10:39:04 2025 +0800
modify a
commit e4eb087f5415b95498e889fc9ca0d1851fcb8ef7
Author: yuxuehandong
Date: Wed Apr 23 10:38:31 2025 +0800
add a and modify hello
commit 2b90e6c0e01950ca41c063e6887ecaf719211551
Author: yuxuehandong
Date: Wed Apr 23 10:35:38 2025 +0800
modify hello
在rel分支 合并dev分支的dev~1次提交
admin@DESKTOP-ERUVGSL MINGW64 /e/liuhanze/project/testMerge (rel)
$ git cherry-pick dev~1
[rel 0fe649f] add a and modify hello
Date: Wed Apr 23 10:38:31 2025 +0800
2 files changed, 2 insertions(+)
create mode 100644 a
rel分支已经合并了dev~1次提交,dev的对应文件已经添加或者修改
admin@DESKTOP-ERUVGSL MINGW64 /e/liuhanze/project/testMerge (rel)
$ git log
commit 0fe649fc2aca0fdfab888e75f5fe21122fdc62a2 (HEAD -> rel)
Author: yuxuehandong
Date: Wed Apr 23 10:38:31 2025 +0800
add a and modify hello
commit f5b0bc47dec524561a3884380963206c6e1f339f
Author: yuxuehandong
Date: Wed Apr 23 10:35:38 2025 +0800
modify hello
commit 08d258d372a3fc79c52d4c5b62ee1ca5e611a889 (master)
Author: yuxuehandong
Date: Mon Apr 14 10:06:01 2025 +0800
initialize hello
变基提交
git rebase 需要变到的分支
例如:
一个分支dev基于master分支建立,此后master分支又更新了两次提交,如果这个dev分支想基于最新的master分支开发,就可以使用变基
$git checkout dev
$ git rebase master 或者 git rebase master dev
git rebase ---onto master dev bug/001
变基到一个完全不同的分支, bug/001原来的base分支dev,最新的完全不同分支master,当前分支bug/001
Git将所有文件分成3类:已追踪的、被忽略的以及未追踪的。
已追踪的(Tracked)
已追踪的文件是指已经在版本库中的文件,或者是已暂存到索引中的文件。如果想将新文件somefile添加为已追踪的文件,执行git add somefile。
被忽略的(Ignored)
被忽略的文件必须在版本库中被明确声明为不可见或被忽略,即使它可能会在你的工作目录中出现。一个软件项目通常都会有很多被忽略的文件。普通被忽略的文件包括临时文件、个人笔记、编译器输出文件以及构建过程中自动生成的大多数文件等。Git维护一个默认忽略文件列表,也可以配置版本库来识别其他文件。被忽略的文件将会在本章后面详细讨论
未追踪的(Untracked)
未追踪的文件是指那些不在前两类中的文件。Git把工作目录下的所有文件当成一个集合,减去已追踪的文件和被忽略的文件,剩下的部分作为未追踪的文件。
git status
On branch master
No commits yet
Untracked files:
(use "git add <file>..." to include in what will be committed)
data
garbage
nothing added to commit but untracked files present (use "git add" to track)
执行 echo garbage > .gitignore 将garbage添加到被忽略文件列表中
$ git status
On branch master
No commits yet
Untracked files:
(use "git add <file>..." to include in what will be committed)
.gitignore
data
nothing added to commit but untracked files present (use "git add" to track)
这样garbage已经被忽略了,但是git status现在显示一个新的未追踪的文件.gitignore。虽然.gitignore文件对Git有特殊的意义,但是它和版本库中任何其他普通文件都是同样管理的。除非把.gitignore添加到索引中,否则Git仍会把它当成未追踪的文件。
.gitignore规则
# 忽略整个文件夹
.idea/
.gradle/
build/
# 忽略文件
local.properties
*.iml # 忽略所有 .iml 文件
# 忽略某种文件类型
*.log
*.tmp
# 例外(即使前面忽略了,也强制包含)
!important-config.xml
注意:.gitignore 只能忽略 还没被 Git 跟踪的文件。
如果文件已经被提交过了,即使加到 .gitignore 也不会自动消失,需要手动取消跟踪,
git rm --cached -r .idea
git rm --cached -r .gradle
然后
git commit -m "Remove ignored files"
Android 项目常用 .gitignore 模板
# Gradle
.gradle/
build/
# Android Studio
.idea/
*.iml
*.ipr
*.iws
# Local configuration file
local.properties
# Logs
*.log
# OS-specific files
.DS_Store
Thumbs.db
引用
引用(ref)是一个SHA1散列值,指向Git对象库中的对象。虽然一个引用可以指向任何Git对象,但是它通常指向提交对象。符号引用(symbolic reference),或称为symref,间接指向Git对象。它仍然只是一个引用。
每一个符号引用都有一个以ref/开始的明确全称,并且都分层存储在版本库的.git/refs/目录中。目录中基本上有三种不同的命名空间代表不同的引用:refs/heads/ref代表本地分支,refs/remotes/ref代表远程跟踪分支,refs/tags/ref代表标签
例如,一个叫做dev的本地特性分支就是refs/heads/dev的缩写。因为远程追踪分支在refs/remotes/命名空间中,所以origin/master实际上是refs/remotes/origin/master。最后,标签v2.6.23就是refs/tags/v2.6.23的缩写。
Git自动维护几个用于特定目的的特殊符号引用。这些引用可以在使用提交的任何地方使用。
HEAD HEAD 始终指向当前分支的最近提交。当切换分支时,HEAD会更新为指向新分支的最近提交。
ORIG_HEAD 某些操作,例如合并(merge)和复位(reset),会把调整为新值之前的先前版本的HEAD记录到ORIG_HEAD中。可以使用ORIG_HEAD来恢复或回滚到之前的状态或者做一个比较。
FETCH_HEAD 当使用远程库时,git fetch命令将所有抓取分支的头记录到.git/FETCH_HEAD中。FETCH_HEAD是最近抓取(fetch)的分支HEAD的简写,并且仅在刚刚抓取操作之后才有效。使用这个符号引用,哪怕是一个对没有指定分支名的匿名抓取操作,都可以也在git fetch时找到提交的HEAD。
MERGE_HEAD 当一个合并操作正在进行时,其他分支的头暂时记录在MERGE_HEAD中。换言之,MERGE_HEAD是正在合并进HEAD的提交。
所有这些符号引用都可以用底层命令git symbolic-ref进行管理。
master^始终指的是在master分支中的倒数第二个提交。还有一些其他的方法,例如master^^、master~2,甚至像master~10^2~2^2这样复杂的名字。
除了第一个根提交之外,每一个提交都来自至少一个比它更早的提交,这其中的直接祖先称作该提交的父提交。若某一提交存在多个父提交,那么它必定是由合并操作产生的。其结果是,每个分支都有一个父提交贡献给合并提交。
在同一代提交中,插入符号^是用来选择不同的父提交的。给定一个提交C,C^1是其第一个父提交,C^2是其第二个父提交,C^3是其第三个父提交,等等。
波浪线~用于返回父提交之前并选择上一代提交。同样,给定一个提交C,C~1是其第一个父提交,C~2是其第一个祖父提交,C~3是第一个曾祖父提交。当在同一代中存在多个父提交时,紧跟其后的是第一个父提交的第一个父提交。你可能会注意到,C^1和C~1都指的是C的第一个父提交,两个名字都是对的。
Git也支持其他形式的简写和组合。如C^和C~两种简写形式分别等同于C^1和C~1。另外,C^^与C^1^1相同,因为这代表提交C的第一父提交的第一父提交,它也可以写做C~2。
分支
可以创建多个分支例如:发布一个特殊的版本,改一个特别的bug,进行某项测试等,可以创建一个带层次的分支,例如:bug/pr-1023和bug/pr-17,使用层次分支命名的其中一个原因是Git就像UNIX shell一样支持通配符。例如,给定命名方案bug/pr-1023和bug/pr-17,通过智能而熟悉的简写,可以一次选择所有bug分支。
git show-branch 'bug/*'
分支命名规则
1、可以使用斜杠(/)创建一个分层的命名方案。但是,该分支名不能以斜线结尾。
2、分支名不能以减号(−)开头。
3、以斜杠分割的组件不能以点(.)开头。如feature/.new这样的分支名是无效的。
4、分支名的任何地方都不能包含两个连续的点(..)。
5、此外,分支名不能包含以下内容:
1)空白或空格
2)具有特殊意义的字符包括 ~ ,^,:,?,*,[
在任何给定的时间里,版本库中可能有许多不同的分支,但最多只有一个当前的或活动的分支。活动分支决定在工作目录中检出哪些文件。
创建一个新的分支 git branch +分支名
git branch bug/001
列出所有的分支git branch , git show-branch 列出更详细的输出,可以使用git show-branch --more=10查看更多提交
显示当前开发分支简洁的单行摘要。参数“--more=10”表示额外10个版本,但是因为只有两个版本,所以显示了两行。master是分支的名字
git show-branch 后面不加参数,只会显示最近的一次提交单行摘要
$ git branch
bug/-001
* master //当前分支前面有星号
$ git show-branch
!(red) [bug/002] add d.txt
!(green) [bug/-001] add d.txt
*(yellow) [master] add d.txt (*号表示是当前活动分支)
---
+(red)+(green)*(yellow) [bug/002] add d.txt
//+号表示当前提交存在某个分支中,*号表示当前提交存在于活动分支中, -号表示一个合并提交
同时显示多个分支的提交记录 git show-branch bug/-001 bug/002
$ git show-branch bug/-001 bug/002
! [bug/-001] add d.txt
! [bug/002] add d.txt
--
++ [bug/-001] add d.txt
如果分支很多,还可以使用通配符*号
$ git show-branch bug/*
! [bug/002] add d.txt
! [bug/-001] add d.txt
--
++ [bug/002] add d.txt
在指定分支上创建分支,在指定分支bug/-001上创建分支 bug/002
git branch bug/002 bug/-001
$ git branch
bug/-001
bug/002
* master
切换到分支 git checkout
工作目录中的未被追踪的文件和目录始终会置之不管;Git不会删除或修改它们。
$ git checkout bug/002
Switched to branch 'bug/002'
选择一个新的当前分支可能会对工作树文件和目录结构产生巨大影响。当然,改变的程度取决于当前分支和你想检出的目标分支之间的差异。改变分支的影响有:
1)在要被检出的分支中但不在当前分支中的文件和目录,会从对象库中检出并放置到工作树中;
2)在当前分支中但不在要被检出的分支中的文件和目录,会从工作树中删除;
3)这两个分支都有的文件会被修改为要被检出的分支的内容。
admin@DESKTOP-ERUVGSL MINGW64 /e/liuhanze/project/TestGit (bug/002)
$ ls
a.txt b.txt bug c.txt d.txt
admin@DESKTOP-ERUVGSL MINGW64 /e/liuhanze/project/TestGit (bug/002)
$ git checkout master
Switched to branch 'master'
admin@DESKTOP-ERUVGSL MINGW64 /e/liuhanze/project/TestGit (master)
$ ls
a.txt b.txt c.txt d.txt
创建一个新的分支并切换到新分支 git checkout -b +分支名
$ git checkout -b bug/005
Switched to a new branch 'bug/005'
从指定分支的提交开始创建新的分支,下面例子从master分支的倒数第二次提交开始创建分支
$ git checkout -b bug/001 master^
Switched to a new branch 'bug/001'
显示当前分支的更改
$ git diff a.txt
warning: in the working copy of 'a.txt', LF will be replaced by CRLF the next time Git touches it
diff --git a/a.txt b/a.txt
index e08bc98..906f5b1 100644
--- a/a.txt
+++ b/a.txt
@@ -1,2 +1,3 @@
hhhh
HelloWorld
+hell a
显示bug/002分支a.txt文件的内容
$ git show bug/002:a.txt
hhhh
HelloWorld
分支操作常用命令
git branch 显示本地分支
git branch -r 显示远程分支
git branch -a 显示全部分支
git branch -vv 显示本地分支 + 跟踪信息
git fetch --all 别人新建远程分支后,你可能看不到。 需要先执行git fetch origin 来获取远程分支信息,之后就可以看到远程分支了。
git switch -c dev origin/dev 创建本地分支dev并且跟踪远程分支origin/dev
在当前分支修改文件后,如果未提交或者未贮藏则修改后的文件会出现在其他分支,哪个分支提交了文件,此次修改就属于哪个分支。如果在当前分支进行修改后不想提交可以选择贮藏
git stash贮藏修改后未提交的文件 git stash apply恢复最近的一次贮藏
admin@DESKTOP-ERUVGSL MINGW64 /e/liuhanze/project/TestGit (bug/003)
$ ls
a.txt b.txt b3 bug c.txt d.txt
admin@DESKTOP-ERUVGSL MINGW64 /e/liuhanze/project/TestGit (bug/003)
$ git stash
Saved working directory and index state WIP on bug/003: 02fc694 add bug
admin@DESKTOP-ERUVGSL MINGW64 /e/liuhanze/project/TestGit (bug/003)
$ ls
a.txt b.txt bug c.txt d.txt
admin@DESKTOP-ERUVGSL MINGW64 /e/liuhanze/project/TestGit (bug/003)
$ git stash apply
On branch bug/003
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: b3
admin@DESKTOP-ERUVGSL MINGW64 /e/liuhanze/project/TestGit (bug/003)
$ ls
a.txt b.txt b3 bug c.txt d.txt
显示贮藏列表 git stash list
$ git stash list
stash@{0}: WIP on bug/003: 02fc694 add bug
stash@{1}: WIP on bug/003: 02fc694 add bug
stash@{2}: WIP on bug/003: 02fc694 add bug
恢复指定贮藏,指定要恢复的储藏(使用 stash@{n}):
$ git stash apply stash@{1}
On branch bug/003
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: b3
使用-m选项,Git通过在你的本地修改和对目标分支之间进行一次合并操作,尝试将你的本地修改加入到新工作目录中。
经过测试,目前版本git(git version 2.37.3.windows.1)默认添加了-m选项
admin@DESKTOP-ERUVGSL MINGW64 /e/liuhanze/project/TestGit (bug/003)
$ git checkout master
Switched to branch 'master'
A b3
admin@DESKTOP-ERUVGSL MINGW64 /e/liuhanze/project/TestGit (master)
$ git checkout bug/003
Switched to branch 'bug/003'
A b3
admin@DESKTOP-ERUVGSL MINGW64 /e/liuhanze/project/TestGit (bug/003)
$ git checkout -m master
Switched to branch 'master'
A b3
使用git switch和git restore代替git checkout
切换到已有分支
git switch branch-name
创建并切换到新分支
git switch -c branch-name
丢弃未提交的更改并强制切换
git switch -f branch-name
切换分支时保留未提交的更改,尝试合并未提交的更改到新的切换分支,测试发现默认增加了此选项
git switch -m branch-name
git restore 专门用于恢复工作区的文件
还原工作区文件(撤销修改),这会把文件恢复成最近一次提交(HEAD)的状态,丢弃本地改动(不可逆)。
git restore main.kt
还原所有文件
git restore .
从暂存区还原文件(即撤销 git add)
git restore --staged main.kt
从某个提交还原文件内容
git restore --source commit_id main.kt
删除分支 git branch -d +分支名
$ git branch -d bug/005
Deleted branch bug/005 (was 22abf51).
merge
合并分支 git merge
$ git merge bug/006
Updating 22abf51..38080e1
Fast-forward
bug005 | 1 +
1 file changed, 1 insertion(+)
create mode 100644 bug005
git log --graph --pretty=oneline --abbrev-commit
使用工具图来查看git分支合并的过程
$ git log --graph --pretty=oneline --abbrev-commit
* 8b544b8 (HEAD -> bug/004) Merge branch 'master' into bug/004
|\
| * a53def1 (master) add e
* | 38080e1 add bug005
|/
* 22abf51 (origin/master, bug/-001) add d.txt
* 6c5703a add c.txt
* e70d1dc modify a.txt
* 8041d36 add b.txt
* 370c1ba init
当遇到合并冲突时,多个分支同时修饰一个文件就会遇到合并冲突。
可以使用git status查看冲突的文件
$ git status
On branch master
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 modified: hello
no changes added to commit (use "git add" and/or "git commit -a")
可以使用 git ls-files -u 来显示工作树中仍然未合并的一组文件。
$ git ls-files -u
100644 ce013625030ba8dba906f756967f9e9ca394464a 1 hello
100644 0d3d9d5b69b7d39547337c5bdcd7950305eca44f 2 hello
100644 eb8f6c2dc16010d4eda656ad39ba56c8bc182a35 3 hello
获取上面的索引后可以使用git show+索引查看具体的内容
$ git show ce0136
hello
$ git show 0d3d9d
hello
helloworl
yeah
$ git show eb8f6c
hello
YeaH!
可以使用git diff命令来显示没合并的内容,在传统的diff命令输出风格里,改变的内容显示在<<<<<<和======之间,替代的内容在======和>>>>>>之间。
$ git diff
diff --cc hello
index 0d3d9d5,eb8f6c2..0000000
--- a/hello
+++ b/hello
@@@ -1,3 -1,2 +1,7 @@@
hello
++<<<<<<< HEAD
+helloworl
+yeah
++=======
+ YeaH!
++>>>>>>> bug/001
三方合并标记线(<<<<<<<<、========和>>>>>>>>)是自动生成的,但是它们只是提供给你看的,而不(必须)是给程序看的。一旦解决了冲突,就应该在文本编辑器里删除它们。
自己手动解决冲突后,可以简单地选择其中一个,移除冲突标记,然后执行git add和git commit命令。
这只是两个diff文件的简单组合:一个对应第一个称为HEAD的父版本,另一个对应第二个称为bug/001的父版本(如果第二个父版本是代表某个其他版本库中未命名提交的绝对SHA1名,请不要感到惊讶!)。为了让事情容易点,Git也给第二个父版本起了一个特殊的名字——MERGE_HEAD。
可以使用此冲突的版本和HEAD和MERGE_HEAD进行比较,也就是和原来的HEAD和原来bug/001的版本进行比较,看看冲突文件多了什么内容
和HEAD版本进行比较
$ git diff HEAD
diff --git a/hello b/hello
index 0d3d9d5..5b5ba08 100644
--- a/hello
+++ b/hello
@@ -1,3 +1,7 @@
hello //原HEAD版本内容
+<<<<<<< HEAD
helloworl //原HEAD版本内容
yeah //原HEAD版本内容
+=======
+YeaH!
+>>>>>>> bug/001
和bug/001的版本进行比较
$ git diff MERGE_HEAD
diff --git a/hello b/hello
index eb8f6c2..5b5ba08 100644
--- a/hello
+++ b/hello
@@ -1,2 +1,7 @@
hello //原bug/001的版本内容
+<<<<<<< HEAD
+helloworl
+yeah
+=======
YeaH! //原bug/001的版本内容
+>>>>>>> bug/001
git log --merge --left-right -p,使用一些特殊的git log选项来帮助你找出变更的确切来源和原因,找出合并冲突的每个提交的明细
--merge: 只显示跟产生冲突的文件相关的提交。
--left-right:如果提交来自合并的“左”边则显示<(“我们的”版本,就是你开始的版本),如果提交来自合并的“右”边则显示>(“他们的”版本,就是你要合并到的版本)。
-p: 显示提交消息和每个提交相关联的补丁。
$ git log --merge --left-right -p
commit < e54e7ac7c7a5d666fdb4e905aed6346d3c5d75aa (HEAD -> master)
Author: yuxuehandong
Date: Mon Apr 14 10:09:22 2025 +0800
modify hello from master
diff --git a/hello b/hello
index ce01362..0d3d9d5 100644
--- a/hello
+++ b/hello
@@ -1 +1,3 @@
hello
+helloworl
+yeah
commit > 4619b0dbc1523aa1f5a5f5bc3f94b7a561fca524 (bug/001)
Author: yuxuehandong
Date: Mon Apr 14 10:07:00 2025 +0800
modify a from bug/001 branch
diff --git a/hello b/hello
index ce01362..eb8f6c2 100644
--- a/hello
+++ b/hello
@@ -1 +1,2 @@
hello
+YeaH!
如果版本库更加复杂而且有好几个文件发生冲突,也可以在命令行参数里指定你感兴趣的确切文件名
$ git log --merge --left-right -p hello
中止合并
在合并提交执行最后的git commit命令前中止合并
git reset--hard HEAD
git reset--hard ORIG_HEAD
如果要中止或在它已经结束(也就是,引进一个新的合并提交)后放弃
$ git reset --hard ORIG_HEAD
HEAD is now at e54e7ac modify hello from master
git reflog
git reflog 可以让你找到曾经丢失的提交、误删的分支、错误的 reset/merge/rebase 操作之前的状态。
什么是 "reflog"?
Git 中的分支、HEAD、stash 等是“引用”(ref)。
每当这些引用指向的提交发生改变时,Git 会在本地记录一条 reflog 日志。
即使某个提交被“丢弃”了,只要 reflog 还在,它就还能找回来。
eflog 是本地记录,远程仓库是看不到的。
Git 会定期清理超过 90 天的 reflog 日志,除非你做特殊配置。
一旦 Git 仓库被强行清理、GC、或者 .git 丢失,reflog 也无法恢复。
恢复提交或者分支
$git reflog
38080e1 (HEAD -> bug/004) HEAD@{0}: merge bug/006: Fast-forward
22abf51 (origin/master, master, bug/-001) HEAD@{1}: checkout: moving from bug/006 to bug/004
38080e1 (HEAD -> bug/004) HEAD@{2}: commit: add bug005
22abf51 (origin/master, master, bug/-001) HEAD@{3}: checkout: moving from bug/005 to bug/006
22abf51 (origin/master, master, bug/-001) HEAD@{4}: checkout: moving from bug/004 to bug/005
# 找到 reset 之前的 commit,比如 HEAD@{3}
$git checkout HEAD@{3}
//找到误删除的分支或者提交,查看文件,找回文件