显示某次提交的具体信息,其中show
后为某次提交的部分校验和。
git show 9fceb0
查看某个分支的最后一次提交对象。
git show master
查看某个分支指向的提交对象的校验和。
git rev-parse master
可使用@{n}
来引用git reflog
中输出的记录,其中HEAD@{n}
若为提交记录,则输出提交记录信息,若非提交记录,则输出所在的分支的最近一次提交记录。注意reflog
引用日志只存在本地仓库,记录仓库内的操作信息,新克隆的仓库引用日志为空。
git show HEAD@{
2}
查看某个提交的父提交信息。
git show HEAD^
某次提交为合并提交,则其存在多个父提交,^n
表示当前提交的第n
父提交。若某合并提交有两个父提交,其中第一父提交为合并时所在分支的提交,第二父提交为所合并分支的提交。
git show HEAD^2
根据指定的次数获取对应的第一父提交,如下为第一父提交的第一父提交,与HEAD^^
等价。
git show HEAD~2
C —— D <-- dev
/
A —— B —— E —— F <-- master
筛选出在一个分支中而不在另一个分支中的提交。如下命令选出在dev
分支而不在master
分支中的提交,即C
和D
提交。
git log master..dev
也可查看即将推送到远端的某些提交,如下查看当前分支推送到远端master
分支的提交有哪些。
git log origin/master..HEAD
也可加上^
字符或者--not
来指明不希望包含某个分支的提交,如下三个命令等价。
git log A..B
git log ^A B
git log B --not A
查看A
或B
包含的但是C
不包含的提交。
git log A B ^C
git log A B --not C
筛选被两个中的一个包含但又不包括两者同时包含的提交。如下查看master
或dev
中包含但是不包括两者共有提交,即E
、F
、C
和D
。
git log master...dev
常用参数--left-right
显示每个提交是处于哪一侧的分支。
git log --left-right master...dev
< F
< E
> D
> C
运行如下命令进入Git
交互式终端模式,其中-i
为--interactive
简写。
staged unstaged path
1: unchanged +1/-1 TODO
2: +1/-1 nothing index.html
3: unchanged +1/-1 readme.md
*** Commands ***
1: status 2: update 3: revert 4: add untracked
5: patch 6: diff 7: quit 8: help
What now>
其中staged
为已暂存列表,unstaged
为未暂存列表,Commands
为操作命令,What now
后键入数字序号或命令首字母操作。
status
:同git status
一致,信息更简略update
:暂存文件,键入2
或u
后输入文件对应的数字暂存文件(多个文件用,
隔开),每个文件前面的*
意味着选中的文件将会被暂存,>>
提示符后不输入任何东西并回车表示执行此次命令revert
:取消暂存add untracked
:跟踪文件patch
:部分暂存文件,类似git add -p
diff
:暂存区和最近一次提交的差异,类似git diff --cached
quit
:退出交互式终端help
:命令帮助 执行如下命令,部分暂存更改,其中-p
为--patch
简写。
git add -p
其中每一个修改的文件称为一个区块,也可分隔成多个较小的区块,区块命令如下。
y
:暂存此区块n
:不暂存此区块q
:退出,不暂存包括此区块在内的剩余的区块a
:暂存此区块与此文件后面所有的区块d
:不暂存此区块与此文件后面所有的区块g
:选择并跳转至一个区块/
:搜索正则表达示匹配的区块j
:跳转至下一个未处理的区块J
:跳转至下一个区块k
:跳转至上一个未处理的区块K
:跳转至上一个区块s
:将当前的区块分割成多个较小的区块e
:手动编辑当前的区块?
:输出帮助储藏即将还不想提交的但是已修改的内容保存至堆栈中,后续可在某个分支上恢复出堆栈中的内容。不仅可以恢复到原分支,也可以恢复到其他任意指定的分支上。作用范围包括工作区和暂存区中的修改,即未提交的修改都会保存至堆栈中。
将未提交(工作区和暂存区)的修改保存至堆栈中,不包括未跟踪的文件。
git stash
将未提交的修改和未跟踪的文件都保存至堆栈中,其中-u
为--include-untracked
简写,也可执行git stash --all
。
git stash -u
将工作区的修改保存至堆栈,不包括未跟踪的文件,其中-k
为--keep-index
简写。
git stash -k
保存至堆栈中并备注,其中message
为备注信息。
git stash save 'message'
保存部分修改至堆栈中,其中-p
为--patch
简写。
git stash -p
查看堆栈中的内容。
git stash list
运行如下命令,查看保存至堆栈中的某次修改的改动(每个修改的文件增改行统计和共计)。git stash show
查看栈顶即最近一次保存至堆栈的修改的改动。
git stash show stash@{
3}
查看某次修改的改动的详细差异。
git stash show stash@{
3} -p
运行如下命令,应用某次改动到当前分支。git stash apply
应用栈顶的改动。
git stash apply stash@{
3}
已重新应用了文件的改动,但是之前暂存的修改未被重新暂存。--index
选项重新应用暂存的修改。
git stash apply --index
移除堆栈上某次改动,git stash drop
移除栈顶的改动。
git stash drop stash@{
3}
应用堆栈上某次改动并移除。git stash drop
应用并移除栈顶的改动。注意若执行git stash pop
出现冲突,实际已经应用了改动,但是改动依然在堆栈列表内,手动移除即可。
git stash pop stash@{
3}
清空堆栈列表。
git stash clear
运行如下命令,将堆栈中某次改动生成一个新分支,检出储藏时所在的提交,然后应用改动,应用成功后移除改动。git stash branch
将栈顶改动生成一个新分支。
git stash branch dev stash@{
3}
git clean
用来从工作目录删除未被跟踪的文件。
主要选项如下,可指定多项并简写。
-f
: --force
简写,删除时必须指定-n
:--dry-run
简写,用于显示将被删除的文件或文件夹-d
:删除文件夹时必须指定-x
:包括.gitignore
忽略的文件或文件夹-X
:仅为.gitignore
忽略的文件或文件夹-i
:交互式清理查看将被删除的文件列表 。
git clean -n
查看将被删除的文件和文件夹。
git clean -d -n
删除未被跟踪的文件。
git clean -f
删除未被跟踪的文件和文件夹。
git clean -f -d
删除未被跟踪的文件和被.gitignore
忽略的文件。
git clean -f -x
仅删除被.gitignore
忽略的文件。
git clean -f -X
删除未被跟踪的文件和文件夹、.gitignore
忽略的文件和文件夹,也可简写为git clean -fdx
。
git clean -f -d -x
运行git clean -i -d
进入交互模式。
Would remove the following item:
dist/ readme.md index.html
*** Commands ***
1: clean 2: filter by pattern 3: select by numbers
4: ask each 5: quit 6: help
What now>
Would remove the following item
后为即将清理的文件和文件夹列表。
clean
:清理列表内文件和文件夹filter by pattern
:排除清理列表部分文件或文件夹,全部排除清理列表为空自动退出交互模式select by numbers
:选择清理列表部分文件或文件夹,均未选择清理列表为空自动退出交互模式ask each
:询问方式删除列表文件或文件夹quit
:退出交互式模式help
:命令帮助 从工作目录中查找一个字符串或者正则表达式。
查找工作目录包含字符串A
的行。
git grep A
查找工作目录包含字符串A
的行,并展示行号。
git grep -n A
查找包含字符串A
的文件。
git grep --name-only A
统计文件中出现字符串A
的行数,-c
为--count
简写。
git grep -c A
某一行满足多个条件,如下命令满足某一行包括A
或B
,其中--or
可省略。
git grep -e A --or -e B
某一行包括A
并且包括B
。
git grep -e A --and -e B
某一行包括A
和B
或者A
和C
。
git grep -e A --and \( -e B -e C \)
用于变基时对提交历史记录进行复杂的修改,可用于调整提交顺序、改变提交中的提交信息或修改文件、压缩提交或拆分提交、也可用于移除提交等。
D d34... <-- master HEAD
|
C c23...
|
B b12...
|
A <-- HEAD~3
运行如下命令显示交互式变基界面,其中-i
选项后为提交记录区间,HEAD~3
到HEAD
范围的提交记录,左开又闭,即为B
、C
和D
。区间终点可省略,默认为HEAD
指向的提交记录。注意Git
从上到下依次应用每一个提交的修改,越早的提交在越上面。
git rebase -i HEAD~3 HEAD
pick b12... B
pick c23... C
pick d34... D
# Rebase ... onto ... (3 commands)
部分选项参数如下,注意删除某一行提交即移除某个提交,全部删除变基将会终止。
pick
:保留某个提交reword
:修改某个提交的提交信息edit
:修改某个提交squash
:将某个提交与上一个提交合并,可修改提交信息fixup
:将某个提交与上一个提交合并drop
:移除某个提交 将pick
修改为drop
,保存并退出。如下将移除C
的提交信息。
pick b12... B
drop c23... C
pick d34... D
调整编辑器内提交记录顺序,保存并退出。如下将提交顺序由B
、C
、D
调整为D
、B
、C
。
pick d34... D
pick b12... B
pick c23... C
将pick
修改为reword
,保存并退出。如下将修改C
、D
的提交信息。
pick b12... B
reword c23... C
reword d34... D
保存并退出后进入C
的提交信息编辑界面,然后再进入D
的提交信息编辑界面。运行git log --oneline
查看提交历史。
d89... D'
c36... C'
b12... B
将多个提交压缩为一个提交,如下将C
、D
压缩到B
,并修改提交信息。
pick b12... B
squash c23... C
squash d34... D
保存并退回将修改提交信息。
# This is a combination of 3 commits.
# This is the 1st commit message:
B
# This is the commit message #2:
C
# This is the commit message #3:
D
也可执行如下命令,跳过修改提交信息。
pick b12... B
fixup c23... C
fixup d34... D
拆分一个提交为多个提交,如下将拆分提交C
。
pick b12... B
edit c23... C
pick d34... D
保存并退出后,HEAD
指向提交C
,运行git reset HEAD^
实际是撤销C
的提交但是保留了修改且均为未暂存。然后再多次提交,最后执行git rebase --continue
继续变基。
git reset HEAD^
git add readme.md
git commit -m 'C1'
git add index.html
git commit -m 'C2'
git rebase --continue
运行git log --oneline
查看提交历史。
d96... D
c35... C2
c46... C1
b12... B
使用脚本的方式改写大量提交,可全局修改邮箱地址或从每一个提交中移除一个文件。
filter-branch
选项参数如下。
--tree-filter
:在检出项目的每一个提交后运行指定的命令然后重新提交结果--prune-empty
:若修改后的提交为空则扔掉不要-f
:--force
简写,即忽略备份强制覆盖。第二次进行擦除时会出错,即Git
上次已做了备份,若再次运行的话会先处理掉上次的备份--all
:擦除所有分支--index-filter
:与--tree-filter
类似,--tree-filter
将每个提交检出到临时目录,运行filter
命令,并根据临时目录中的内容构建新的提交。而--index-filter
则将每个提交复制到索引中,运行filter
命令,并根据索引中的内容构建新的提交。即从索引构建提交比从目录构建提交要快 擦除dev
分支整个提交历史中的dist/index.html
文件。误操作可运行git reflog
查看历史提交校验和,再版本回退恢复。
git filter-branch -f --prune-empty --index-filter 'git rm -f --cached --ignore-unmatch dist/index.html' dev
批量修改当前分支提交历史中的作者和邮箱地址,如下将提交记录中的邮箱[email protected]
修改为[email protected]
,作者修改为B
。
git filter-branch --commit-filter '
if [ "$GIT_AUTHOR_EMAIL" = "[email protected]" ];
then
GIT_AUTHOR_NAME="B";
GIT_AUTHOR_EMAIL="[email protected]";
git commit-tree "$@";
else
git commit-tree "$@";
fi'
彻底清理提交历史中某个文件,对仓库重新分析打包。
git rm -rf --ignore-unmatch .git/refs/original/
git reflog expire --expire=now --all
git fsck --full --unreachable
git repack -A -d
git gc --aggressive --prune=now
HEAD
是当前分支引用的指针,总是指向该分支上的最后一次提交。
Index
是预期的下一次提交,可引用为暂存区域。
Working Directory
即工作目录。
git init
创建一个Git
仓库,其中的HEAD
引用指向未创建的分支(master
还不存在)。分支即指向提交的指针,初始化的仓库没有提交记录,默认也就不存在分支。
? <-- master <-- HEAD
工作目录新建文件readme.md
,暂为v1
版本。
———— HEAD ———————— Index ———————— Working Directory
? ? readme.md (v1)
git add
获取工作目录中的内容,将其复制到Index
中。
———— HEAD ———————— Index ———————— Working Directory
? readme.md (v1) readme.md (v1)
git commit
将Index
中的内容保存为快照,然后创建指向快照的提交对象,更新master
指向此次提交对象。
v1 <-- master <-- HEAD
———— HEAD ———————— Index ———————— Working Directory
readme.md (v1) readme.md (v1) readme.md (v1)
修改工作目录中文件,定为v2
版本,运行git status
,将会看到Changes not staged for commit
。
v1 <-- master <-- HEAD
———— HEAD ———————— Index ———————— Working Directory
readme.md (v1) readme.md (v1) readme.md (v2)
暂存v2
,运行git status
,将会看到Changes to be committed
。
v1 <-- master <-- HEAD
———— HEAD ———————— Index ———————— Working Directory
readme.md (v1) readme.md (v2) readme.md (v2)
提交此次修改,master
指向v2
版本。
v1 —— v2 <-- master <-- HEAD
———— HEAD ———————— Index ———————— Working Directory
readme.md (v2) readme.md (v2) readme.md (v2)
重置即git reset
版本回退,修改readme.md
并提交,提交历史如下。
v1 —— v2 —— v3 <-- master <-- HEAD
———— HEAD ———————— Index ———————— Working Directory
readme.md (v3) readme.md (v3) readme.md (v3)
第一步移动HEAD
,即移动master
指向v2
,HEAD
再指向master
。此过程可运行git reset --soft HEAD^
实现,其实质是撤销了v3
的提交,再次运行git commit
可完成git commit --amend
所做的事。
v3
/
v1 —— v2 <-- master <-- HEAD
———— HEAD ———————— Index ———————— Working Directory
readme.md (v2) readme.md (v3) readme.md (v3)
第二步更新Index
,即更新暂存区域。此过程可运行git reset --mixed HEAD^
实现,其中--mixed
可省略,实质是撤销v3
的提交,同时取消暂存所有修改。
v3
/
v1 —— v2 <-- master <-- HEAD
———— HEAD ———————— Index ———————— Working Directory
readme.md (v2) readme.md (v2) readme.md (v3)
第三步更新工作目录,即让工作目录与Index
一致。此过程可运行git reset --hard HEAD^
实现,强制将Index
中的v2
覆盖工作目录。
v3
/
v1 —— v2 <-- master <-- HEAD
———— HEAD ———————— Index ———————— Working Directory
readme.md (v2) readme.md (v2) readme.md (v2)
修改readme.md
并暂存,提交历史如下。
v1 <-- master <-- HEAD
———— HEAD ———————— Index ———————— Working Directory
readme.md (v1) readme.md (v2) readme.md (v2)
运行git reset readme.md
(为git reset --mixed HEAD readme.md
的简写形式),实质只是将readme.md
从HEAD
复制到Index
。
v1 <-- master <-- HEAD
———— HEAD ———————— Index ———————— Working Directory
readme.md (v1) readme.md (v1) readme.md (v2)
也可以不从HEAD
复制到Index
,而是复制具体某次提交的文件对应版本,运行git reset 5f5292 readme.md
,其中5f5292
为某次提交的校验和。
v1 (5f5292) —— v2 —— v3 <-- master <-- HEAD
———— HEAD ———————— Index ———————— Working Directory
readme.md (v3) readme.md (v1) readme.md (v3)
若一个项目最近有三次提交,第一次提交A
新增readme.md
,第二次提交B
修改readme.md
并新增index.txt
,第三次提交C
再次修改readme.md
。由于B
、C
两次提交都是修改同一功能,因此需要压缩。
C readme.md (v3) index.txt (v1) <-- master <-- HEAD
/
B readme.md (v2) index.txt (v1)
/
A readme.md (v1)
———— HEAD ———————— Index ———————— Working Directory
readme.md (v3) readme.md (v3) readme.md (v3)
index.txt (v1) index.txt (v1) index.txt (v1)
运行git reset --soft HEAD^^
将HEAD
移动到提交A
上。
C readme.md (v3) index.txt (v1)
/
B readme.md (v2) index.txt (v1)
/
A readme.md (v1) <-- master <-- HEAD
———— HEAD ———————— Index ———————— Working Directory
readme.md (v1) readme.md (v3) readme.md (v3)
index.txt (v1) index.txt (v1)
运行git commit
将B
、C
的修改压缩为一次新的提交D
。
C readme.md (v3) index.txt (v1)
|
B readme.md (v2) index.txt (v1)
| D readme.md (v3) index.txt (v1) <-- master <-- HEAD
| /
A readme.md (v1)
———— HEAD ———————— Index ———————— Working Directory
readme.md (v3) readme.md (v3) readme.md (v3)
index.txt (v1) index.txt (v1) index.txt (v1)
分支的提交历史如下,当前HEAD
指向master
分支。
B readme.md (v2) <-- master <-- HEAD
/
A readme.md (v1) <-- dev
———— HEAD ———————— Index ———————— Working Directory
readme.md (v2) readme.md (v2) readme.md (v2)
git checkout dev
移动HEAD
指向dev
分支,不同于git reset --hard HEAD
,仅仅移动HEAD
自身,且checkout
会检查是否有未提交的修改,防止修改丢失。
B readme.md (v2) <-- master
/
A readme.md (v1) <-- dev <-- HEAD
———— HEAD ———————— Index ———————— Working Directory
readme.md (v1) readme.md (v1) readme.md (v1)
--continue
:某些情况下合并产生冲突,Git
会暂停下来等待解决冲突。一种方式是git add
将冲突文件标记为已解决,再次提交即可。另一种方式是标记后执行git merge --continue
继续合并,若没有冲突产生,Git 会自动创建一个合并提交--abort
:尝试恢复到合并前的状态。当工作目录中有未提交的修改,git merge --abort
某些情况下无法重现合并前的状态。因此建议合并前保持干净的工作目录,可将部分修改通过git stash
储藏,解决冲突后再释放出来-Xignore-all-space
:合并过来的分支和当前分支某一文件除了空格以外没有任何区别的时候,忽略合并过来的分支的那个文件。即若A
合并B
的修改,A
修改为hello wor ld
,B 修改为hello wo rld
,两次修改等效,且忽略合并过来的修改B
-Xignore-space-change
:忽略空格量的变化。若某行在非末尾的位置有空格而另外一个没有,按照冲突处理。即若A
合并B
的修改,A
修改为hello*world
(暂用*
代替空格),B
修改为hello**world
,则两次修改等效,且忽略合并过来的修改B
。A
修改为helloworld
,B
修改为hello world
,则两次修改冲突 查看未合并的文件,运行如下命令。其中包括两者共同祖先的版本1
、当前版本2
(HEAD
)、合并过来的版本3
(MERGE_HEAD
)。
git ls-files -u
100644 ac5336... 1 readme.md
100644 36c569... 2 readme.md
100644 e85456... 3 readme.md
查看未合并版本的具体内容,运行如下命令,其中36c569
为当前版本2
的部分校验和,也可运行一个特别的语法git cat-file -p :2:readme.md
。
git cat-file -p 36c569
冲突文件修改后(不暂存),可运行如下命令查看修改差异。其中--base
为查看修改后的版本与两者共同祖先的版本的差异,--theirs
为查看修改后的版本与合并过来的版本的差异,--ours
为查看修改后的版本和当前版本的差异。
git diff [--base|--ours|--theirs]
master
分支修改了readme.md
文件,dev
分支也修改了readme.md
文件,当前HEAD
指向master
分支,若合并dev
分支的readme.md
修改,将会产生大致如下的冲突。
<<<<<<< HEAD
puts 'hi world'
=======
puts 'hello git'
>>>>>>> dev
此时并不知道保留哪一处修改,缺少更多的参照信息,运行如下命令可查看ours
、base
、theirs
三个版本的差异。可通过配置git config --global merge.conflictstyle diff3
来作为以后合并冲突的默认格式。
git checkout --conflict=diff3 readme.md
cat readme.md
<<<<<<< ours
hi world
||||||| base
hello world
=======
hello git
>>>>>>> theirs
运行如下命令,快速保留某一方的修改。其中--ours
表示保留当前的修改,丢弃掉引入的修改。--theirs
表示保留引入的修改,丢弃掉当前的修改。
git checkout [--ours|--theirs] readme.md
master
分支和dev
分支提交历史如下,当前HEAD
指向master
。其中B
、D
提交修改了readme.md
文件,提交C
新增了index.txt
,提交E
新增了file.txt
。
C index.txt (v1) <-- master <-- HEAD
|
B readme.md (v3)
| E file.txt (v1) <-- dev
| /
| D readme.md (v2)
| /
A readme.md (v1)
git merge dev
合并产生冲突后,可运行如下命令,查看此次合并中包含的每一个分支的所有独立提交。
git log --oneline --left-right HEAD...MERGE_HEAD
< f127062 B
< af9d363 C
> e3eb226 D
> c3ffff1 E
添加--merge
选项,只显示任何一边接触了合并冲突文件的提交。也可再添加-p
选项查看所有冲突文件的区别。
git log --oneline --left-right --merge
< f127062 B
> e3eb226 D
运行如下命令。若有某些可以合并的修改,Git
会直接合并,某些有冲突的修改,Git
根据选项参数选择特定的修改。-Xours
选项即产生冲突优先使用当前HEAD
修改,-Xtheirs
选项即优先使用dev
分支修改,其余可合并的修改直接合并。
git merge [-Xours|-Xtheirs] dev
重用合并记录(reuse recorded resolution
)即让Git
记住解决一个块冲突的方法,下一次看到相同的冲突时自动解决它。
配置如下选项启用rerere
功能,也可不配置,在.git
目录下新建rr-cache
文件夹即可。
git config --local rerere.enabled true
各分支的readme.md
修改内容如下。
C readme.md (hello git) <-- master <-- HEAD
/
A readme.md (hello world) —— B readme.md (hi world) <-- dev
master
分支下合并dev
分支readme.md
的修改。其中Recorded preimage
表示Git
已经开始跟踪此次合并了。
git merge dev
Auto-merging readme.md
CONFLICT (content): Merge conflict in readme.md
Recorded preimage for 'readme.md'
Automatic merge failed; ...
查看readme.md
文件。
cat readme.md
<<<<<<< HEAD
hello git
=======
hi world
>>>>>>> dev
查看.git/rr-cache/0ff6a9/preimage
下记录的合并冲突前的版本,其中0ff6a9
为此次冲突的校验和。
<<<<<<<
hello git
=======
hi world
>>>>>>>
处理readme.md
,将其标记为已解决并提交。其中Recorded resolution
表示记录了此次冲突的解决方法。
git add readme.md
git commit -m 'D'
Recorded resolution for 'readme.md'.
[master ...] D
冲突解决后提交历史如下。
B readme.md (hi world) <-- dev
/ \
A readme.md (hello world) D readme.md (hi git) <-- master <-- HEAD
\ /
C readme.md (hello git)
查看.git/rr-cache/0ff6a9/postimage
下记录的合并冲突后的版本。本质上当Git
看到一个readme.md
文件的一个块冲突中有hi world
在一边、hello git
在另一边,它会将其解决为hi git
。
hi git
撤销合并提交D
,然后再次合并dev
的修改。其中Resolved ... using previous resolution
表示使用了之前的合并记录。
git reset --hard HEAD^
git merge dev
Auto-merging readme.md
CONFLICT (content): Merge conflict in readme.md
Resolved 'readme.md' using previous resolution.
Automatic merge failed; ...
查看使用了合并记录后的readme.md
。
cat readme.md
hi git
执行git merge --abort
撤销本次合并,回到合并前的状态。再来看看将dev
的修改变基到master
的情况。
git switch dev
git rebase master
...
Resolved 'readme.md' using previous resolution.
....
执行git add
将文件标记为已解决,执行git rebase --continue
继续变基,此次变基记录记为B'
,master
成为dev
分支的直接上游。
B' <-- dev <-- HEAD
|
C <-- master
| B
| /
A
Git
自动解决冲突,但是可能你已经忘记冲突时readme.md
的状态了,运行如下命令,恢复至冲突时的readme.me
状态。
git checkout --conflict=merge readme.md
cat readme.md
<<<<<<< ours
hello git
=======
hi world
>>>>>>> theirs
分支提交历史如下。
E —— F <-- dev
/
A —— B —— C <-- master
某种情况下要合并master
分支的修改,来测试dev
分支的修改是否影响了master
分支的部分功能。
E —— F —— G <-- dev
/ /
A —— B ———————— C <-- master
可能多次从master
分支合并至dev
分支进行测试,最终master
分支合并了dev
分支的修改。查看master
分支提交历史,可能看见很多的合并记录,历史树看起来并不直观。
E —— F —— G —— H —— K —— L <-- dev
/ / / \
A —— B ———————— C ———————— J —— M —— N <-- master
其实dev
分支每次合并master
分支完成测试后,可以丢弃掉那次合并记录,因为rerere
已经记录了冲突的解决方法,不必担心以后再次合并,最终dev
分支完成开发合并至master
,提交历史树如下。
E —— F —— H —— L <-- dev
/ \
A —— B —— C —— J —— M —— N <-- master
提交包括常规提交、合并提交,常规提交只有一个父提交对象,而合并提交有两个或者多个的父提交对象。git reset --hard
可取消某次提交的修改,但是对于已经推送至远程仓库的提交,会造成很大的问题。另一种解决方式就是还原提交,即生成一个新的提交,此提交将会撤销指定的某次提交的修改。
若分支提交历史如下,其中HEAD
指向 master。
A —— B (24e888) —— C <-- master <-- HEAD
执行如下命令取消B
的修改,也可执行git reset HEAD^
。
git reset 24e888
运行后Git
会打开Vim
编辑器,可修改此次新提交的提交信息。
Revert "B"
This reverts commit 24e888....
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# On branch master
...
最终提交历史如下。
A —— B —— C —— ^B <-- master <-- HEAD
各分支的提交历史大致如下,HEAD
指向master
分支,其中D
为合并提交,其余均为常规提交。
A —— B —— C —— D (110c0d6) —— G <-- master <-- HEAD
\ /
E —— F <-- dev
某些情况下发现dev
分支合并进来的修改存在重大缺陷,需要丢弃掉dev
分支的修改,即E
、F
两次提交的修改。若运行git resert 110c0d6
撤销提交D
,但是Git
不知道撤销哪一个分支的修改,此时需告诉Git
保留哪一个分支的修改,而另一个分支的修改将被撤销。运行如下命令创建还原提交^D
,其中-m
表示此次取消的是一次合并提交,1
表示保留提交D
的第一父提交C
的修改并撤销E
、F
的修改。
git revert -m 1 110c0d6
A —— B —— C —— D (110c0d6) —— G —— ^D <-- master <-- HEAD
\ /
E —— F <-- dev
当dev
分支的重大缺陷修复后,可再次合并进master
。可能直觉上觉得E
、F
、H
的修改均合并进了master
分支,但是注意由于提交^D
撤销了E
、F
的修改,所以master
并不包含E
、F
的修改,即只有H
的修改合并进了master
。
A —— B —— C —— D —— G —— ^D —— I <-- master <-- HEAD
\ / /
E —— F —————————————— H <-- dev
解决上述情况的办法也很容易,用撤销解决撤销,即先撤销^D
的修改。其中d6d7365
为提交^D
的部分校验和,也可执行git revert HEAD
。
git revert d6d7365
A —— B —— C —— D —— G —— ^D (d6d7365) <-- master <-- HEAD
\ /
E —— F ——— H <-- dev
再执行git merge
合并dev
的修改。
A —— B —— C —— D —— G —— ^D —— ^^D —— I <-- master <-- HEAD
\ / /
E —— F ————————————————————— H <-- dev
查看某一文件每一行的最后一次修改的提交。输出的每一行中第一个字段是最后一次修改此行的提交的部分校验和,^
开头表示的是此文件第一次加入项目时的提交。括号中的字段分别为作者、提交时间(含时区)、行号。最后为某一行的内容。
查看文件第二行到第五行。其中-L
表示范围,2,5
表示第二行到第五行,都是闭区间,不指定-L
参数和范围则查看文件所有行。
git blame -L 2,5 readme.md
^4832fe2 (DonG 2021-01-12 10:31:28 +0800 2) hello
9f6560e4 (DonG 2021-01-13 10:32:29 +0800 3) world
cd564aa5 (DonG 2021-01-14 10:33:30 +0800 4) and
7f3a6645 (DonG 2021-01-15 10:34:31 +0800 5) git
范围也可指定行的个数,+
表示往下,-
表示往上。如下表示从第二行往下三行,则输出行号为2
、3
、4
的行的提交信息。
git blame -L 2,+3 readme.md
bisect
命令会对提交历史进行二分查找来帮助尽快找到是哪一个提交引入了问题。
提交历史如下,提交C101
收到了bug
反馈,但是在提交C1
并未存在此bug
,可以确定的是在提交C1
与C101
之间的提交引入了bug
。
C1 (d564aa) —— C2 ··· C50 ··· C100 —— C101 <-- master <-- HEAD
运行如下命令,选择C1
到C101
的提交历史进行二分查找排查,代码库会切换到范围正当中的那一次提交C51
,其中d564aa
为提交C1
的部分校验和。
git bisect start HEAD d564aa
提交C51
下复现bug
,并不存在,说明在C52
到C101
之间,good
表示本次提交C51
没有问题。
git bisect good
Git
自动切换到C52
与C101
的中点提交C76
,bad
表示本次提交C76
有问题。
git bisect bad
不断重复此过程,最终查找到出问题的那次提交,并打印出那次提交的详细信息。
857293... is the first bad commit
commit 857293...
Author: ...
Date: ...
执行如下命令,退出查错,回到最近一次的代码提交。
git bisect reset
Git
可将分支内容打包成一个二进制文件,用于邮件或其他方式传输。
打包master
分支所有提交历史,其中repo.bundle
为打包生成的二进制文件名。
git bundle create repo.bundle HEAD master
克隆打包生成的二进制文件,其中repos
为自定义的仓库名,也可不指定,如下则默认为repo
。
git clone repo.bundle repos
打包区间范围的提交记录,其中HEAD^^..HEAD
左开右闭区间,即D
、E
两次提交记录。
git bundle create repo.bundle HEAD^^..HEAD master
A —— B —— C —— D —— E <--master <-- HEAD
检查文件是否是合法的Git
包,是否拥有共同的祖先从而导入。其中The bundle requires this ref
表示此包父提交对象校验和为99884a
。
git bundle verify repo.bundle
...
The bundle requires this ref:
99884a...
查看包可导入的分支。
git bundle list-heads repo.bundle
导入包中master
分支的提交到本地dev
分支。
git fetch repo.bundle master:dev
Git
使用HTTP
协议访问远程仓库进行操作时,每一个连接都是需要用户名和密码的。
倘若每次推送或者拉取都输入用户名和密码,显得非常繁琐,Git
提供了一个凭据系统来解决此种问题,部分选项如下。
cache
:将凭据存放在内存中一段时间,密码不会被存储在磁盘中,并且15
分钟后从内存中清除。注意此选项不适用于windows
系统,因为此选项是通过unix
套接字进行通信store
:凭据明文存放在磁盘中永不过期。默认路径为C:/Users/{username}/.git-credentials
manager
:凭据管理至windows
系统中的凭据管理器,可在控制面板中的用户账户的凭据管理器中查看运行如下命令配置上述选项。
git config --global credential.helper [cache|store|manager]
一般安装Git
会默认使用manager
方式,其中Enable Git Credential Manager
即开启manager
方式