git学习笔记
GIT学习笔记
教程
配置
忽略
一个repo
vim repo的根目录 或 其下任意一个文件夹中,创建.gitignore
,则适用于.gitignore
所在的文件夹
# 注释
# 以下文件会被忽略
# 相对本.gitignore文件所在文件夹的 路径
/path/relative/to/here
# 相对本.gitignore文件所在文件夹下 任意这样结尾的路径
tail/of/path
# 文件或文件夹
xx/x/xxx
/xx/x/xxx
# 文件夹
xx/x/xxx/
/xx/x/xxx/
# 通配符
xx/xx*xx/xx
xx/xx*
*xx/xx
# 不可写成 `ignore-this-dir`, 这样整个ignore-this-dir文件夹被忽略了, 下面的例外无法生效
ignore-this-dir/*
# 例外,即不忽略
!ignore-this-dir/exception
全局(整个电脑)
vim ~/.gitignore_global
- 代码行尾勿加注释,会无法执行那一行
# attention: 代码行尾勿加注释,会无法执行那一行
# Windows
Thumbs.db
# Thumbnail cache files
desktop.ini
# Folder view configuration files
# OS X
.DS_Store
# Folder view configuration files
.Spotlight-V100
# Files that might appear on external disks
.Trashes
# Files that might appear on external disks
._*
# Thumbnail cache files
# Compiled Python files
*.pyc
# Compiled C++ files
*.out
# Application specific files
venv
node_modules
.sass-cache
# Temp File
*.swp
*.swa
*.swo
# github merge file
*.orig
# vscode
.vscode
vim ~/.gitconfig, 添加如下
[core]
excludesfile = .gitignore_global
# 其他core配置...
适用于电脑上所有repo
作用
Mac系统
- ._*: Mac系统打开windows或linux系统的文件,会把的文件xxx.xx的附属信息,写入"._xxx.xx"二进制文件
- .DS_Store: 记录该文件夹下各文件在访达下的布局
- 在Mac用Macfusion访问linux服务器上的git项目,需ignore上述文件
文件的状态
其中 ignore是忽略的文件, HEAD是当前所在节点的那次提交版本, index是暂存( git add
即 git stage
)所生成的文件索引, wkdir是工作目录
ignore/HEAD/index/wkdir[file] = None if 其中无此file else 其中此file的内容
文件的状态分如下几种, git的判断机制是
- 不存在的文件: ignore[file] == HEAD[file] == index[file] == wkdir[file] == None
- 存在的文件: ! ( ignore[file] == HEAD[file] == index[file] == wkdir[file] == None)
- 忽略的文件: None != ignore[file]
- 未被忽略的文件: None == ignore[file] and ! (HEAD[file] == index[file] == wkdir[file] == None)
其中未被忽略的文件进一步判断:
-
提交文件
commited
: None != HEAD[file] == index[file] == wkdir[file] -
未提交/不干净的文件
uncommited
: index[file] != wkdir[file] or HEAD[file] != index[file]-
未跟踪:
untracked
: None == index[file] != wkdir[file] -
未暂存: None != index[file] != wkdir[file]
unstaged modified
: None != index[file] != wkdir[file] != Noneunstaged deleted
: None != index[file] != wkdir[file] == None -
暂存: HEAD[file] != index[file]
staged new file
: None == HEAD[file] != index[file]staged modified
: None != HEAD[file] != index[file] != Nonestaged deleted
: None != HEAD[file] != index[file] == None and HEAD[file] != index[f] for any f in indexstaged renamed: file1-> file2
: None != HEAD[file1] != index[file1] == None and HEAD[file1] == index[file2]
-
checkout的逻辑
改动可否带走
执行上述两命令成功的充要条件为: 一切当前不干净的文件(未跟踪/跟踪未暂存/暂存未提交), 在git checkout
去往节点和HEAD
节点中完全一样(是否被跟踪一样, 文件内容一样)
执行后, 原来不干净的文件, 其内容/是否被跟踪/是否被暂存 均完全未变
从历史节点开分支
去历史节点试着修改
checkout到历史的节点,可以试着做改动,然后提交(git commit
),会变成一个临时分支,而不改动历史。
这个临时分支,只有一个节点hash码、没有分支名。例如下图,checkout到历史节点cf8e102
,修改完一些文件再提交,产生临时分支节点883ed24
![屏幕快照 2019-08-19 20.12.00](assets/屏幕快照 2019-08-19 20.12.00.png)
将修改存成分支
处在临时分支节点,可以执行git branch 分支 && git checkout 分支
(等价于 git checkout -b 分支
),从而使得临时分支变成一个有名字的分支。
若checkout到别的节点,再git log
就不显示这个临时节点了,但是仍然可以git checkout [临时节点的hash码]
到这个临时节点。
stash
我们有时会遇到这样的情况,正在dev分支开发新功能,做到一半时有人过来反馈一个bug,让马上解决,但是新功能做到了一半你又不想提交,这时就可以使用git stash命令先把当前进度保存起来,然后切换到另一个分支去修改bug,修改完提交后,再切回dev分支,使用git stash pop来恢复之前的进度继续开发新功能。下面来看一下git stash命令的常见用法.
参考
https://www.yiibai.com/git/git_stash.html
https://blog.csdn.net/qq_32452623/article/details/76100140
https://blog.csdn.net/daguanjia11/article/details/73810577
git stash save与git stash push区别:
save是阉割版的push, 当前推荐使用git stash push命令, save不能指定只存哪个目录, save 写mesage的格式和push不同
储藏
git stash [push [--patch] [-k|--[no-]keep-index] [-q|--quiet]
[-u|--include-untracked] [-a|--all] [-m <message>]
[-- <pathspec>...]]
- 无
-u
无-a
: 不存未跟踪的文件; 只存跟踪且修改了的文件(不论是否add)
- 加
-u
无-a
: 还存未跟踪的文件, 但不存忽略的文件 - 有
-a
: 还存未跟踪的文件, 也不存忽略的文件, 故-a
包含了-u
- 被存的文件都回到HEAD时的样子(包括文件的有无,文件的内容); 未跟踪的文件不变
查看储藏
git stash list
- 会显示整个分支图中(不仅仅是HEAD的)的所有储藏
其中 stash@{0}
等, 是stash编号; 在glg中显示的节点hash ( 位于 三角 的最顶上的那个点) 也可以做stash编号
-
glg
只显示stash@{0}
, 此即当前的stash, 整个分支图中仅此一个. 此stash即git stash
各个命令里可缺省的stash编号
git stash show [<stash编号>]
-
会显示跟踪的文件的增删改
取出储藏
当当前目录有储藏的文件改动了, 则无法取出储藏
git stash pop [stash编号] [--index]
等价于 git stash apply [stash编号] [--index]; git stash drop [stash编号]
- 不加—index: 原来跟踪且修改的文件还原, 现在都没git add
- 加 index: 原来跟踪且修改的文件还原, 分别还原到add和没add的状态
glg
和git stash list
中看不到这个储藏了
git stash apply [stash编号] [--index]
- 不加—index: 原来跟踪且修改的文件还原, 现在都没git add
- 加 index: 原来跟踪且修改的文件还原, 分别还原到add和没add的状态
- glg 和
git stash list
中还能到这个储藏了
git stash drop [stash编号]
- 当前文件的状态不变
- glg 和
git stash list
中不能到这个储藏了
git stash clear
- 等价于
git stash drop [stash编号]
对于所有[stash编号]
git stash branch <branchname> [<stash编号>] [--index]
等价于 git stash pop [<stash编号>] [--index]; gb <branchname>
取消apply储藏
在某些情况下,可能想应用储藏的修改,在进行了一些其他的修改后,又要取消之前所应用储藏的修改。Git没有提供类似于 stash unapply
的命令,但是可以通过取消该储藏的补丁达到同样的效果:
法一
git stash show -p [stash编号] | git apply -R
可能会想要新建一个別名,在你的 Git 里增加一个 stash-unapply
命令,这样更有效率。例如:
git config --global alias.stash-unapply '!git stash show -p | git apply -R'
git stash apply # 不能有 --index
# (... work work work # 不能改储藏的文件)
git stash-unapply # 不会还原 git stash push -u 所储存的未跟踪文件
# 回到干净的目录
法二
如果在应用储藏前, 目录是干净的, 则可直接
cd "`git rev-parse --show-toplevel`" # 来到当前repo的根目录
git reset HEAD # 取消暂存(git add)的所有文件
git checkout . # 取消被跟踪文件的所有修改
git clean -df # 删除未跟踪也未被忽略的文件夹(-d)和文件(-f)
rebase
带上sub-branch一起rebase
https://stackoverflow.com/questions/32652293/automatically-rebase-git-sub-branches
http://voidcanvas.com/how-to-rebase-a-branch-when-the-parent-is-rebased-with-another/
https://stackoverflow.com/questions/43711961/rebase-a-branch-that-has-child-branches
rebase 一棵树 https://stackoverflow.com/questions/14504029/git-rebase-subtree/45861361#45861361
git checkout dev
git rebase --preserve-merges master
What exactly does git’s “rebase --preserve-merges” do (and why?)
Rebase a tree: https://stackoverflow.com/questions/17315285/rebasing-a-tree-a-commit-branch-and-all-its-children
https://stackoverflow.com/questions/17106840/git-rebase-with-branches-whole-tree
对比rebase/merge的化解冲突
merge
初始状态: 当前分支是A
o-A(ours,HEAD)
o-o-B(theirs)
执行 git merge B
, 得到最终状态:
o-o(ours:原A)-A(HEAD)
/
o-o-B(theirs)
若中间发生冲突, 文件中显示
<<<<<<< HEAD
A的内容
=======
B的内容
>>>>>>> B分支名
解决冲突的方法
-
手动修改冲突的文件
-
只保留A, 或只保留B
git checkout --ours 冲突的文件(夹)名 # => 保留A的内容
git checkout --theirs 冲突的文件(夹)名 # => 保留B的内容
若要对repo内所有文件采样上述策略, 则 冲突的文件(夹)名
写作 "$(git rev-parse --show-toplevel 2> /dev/null)”
整个repo都保留B的内容, 也可执行, 这个命令不存在, 只能git merge —skip
解决完冲突, 执行 git add -A "$(git rev-parse --show-toplevel 2> /dev/null)"
, 然后 git commit
, 则来到最终状态. 注: git merge --continue
命令在老版本的git没有, 如git2.7.4没有, git2.20.1有.
若放弃merge, 则执行 git merge --abort
, 则回到初始状态.
rebase
初始状态: 当前分支是A
A1-A2-A(HEAD)
/
o-o-B
执行 git rebase A
则git内部会先 git checkout B
, 得到中间状态:
A1-A2-A(theirs)
/
o-o-B(ours,HEAD)
再将A分支rebase, 然后 git checkout A
, 从而变成最终状态:
A1-A2-A(HEAD)
/
o-o-B
若发生冲突, 则当前处在中间状态. 冲突的文件所显示如下, HEAD所指的分支正好和merge冲突时相反
<<<<<<< HEAD
B的内容
=======
A的内容
>>>>>>> 当前所rebase的A的某次提交的message (即A1或A2的message)
解决冲突的方法:
-
手动修改冲突的文件
-
只保留A, 或只保留B: 解决冲突的命令中, ours和theirs 也与merge冲突时相反
git checkout --ours 冲突的文件(夹)名 # => 保留B的内容
git checkout --theirs 冲突的文件(夹)名 # => 保留A的内容
若要对repo内所有文件采样上述策略, 则则 冲突的文件(夹)名
写作 "$(git rev-parse --show-toplevel 2> /dev/null)”
-
整个repo都保留A的内容, 也可执行
git rebase —skip
. 区别如下git rebase —skip
(推荐 ): 若某文件, B 分支删除 而A分支当前冲突的commit有, 则此命令会删除此文件
git checkout --ours "$(git rev-parse --show-toplevel 2> /dev/null)"
: 若某文件, B 分支删除 而A分支当前冲突的commit有, 则此命令不会删除此文件
解决完冲突, 执行 git add -A "$(git rev-parse --show-toplevel 2> /dev/null)"
, 然后git rebase --continue
, 则来到最终状态
若放弃merge, 则执行 git rebase --abort
, 则回到初始状态.
批量化解冲突:
一个批量处理脚本。这个脚本根据设定的规则自动执行指令,让我在三分钟之内完成了一个14步(以 git rebase --continue
计)
rebase的缺点
-
git rebase会产生连环冲突,git merge不会
这是因为
A1-A2-A3(A) / B1-B2-B3(B)
在A,
git rebase B
, 则会依次将 A1, A2, A3 rebase 到 B3. 若B3中某文件与A1-A3均冲突, 就会连环冲突.例如B1中有文件a, B3中无文件a, 而A1-A3均修改了文件a, 则要保留B3的文件, 则会连环冲突, 中间有三次要执行若
git rebase —skip
`来化解冲突, 保留B3的文件. 最终rebase结束将无a文件. -
有时git rebase会产生冲突,git merge不会
如
A1-A2(A) / B1-B2-B3(B)
B1有文件a, B2,B3未动a, 动了别的文件; A1将a删除, A2将a恢复了a.
则在A,
git merge B
, 不会出冲突. 而git rebase B
则在rebase A1时会冲突. -
push后merge可以原样保留正确运行的历史commit,而rebase不会
例如
A1-A2(A) / B1-B2-B3(B)
在A,
git rebase B
, 则得到的A1节点, 是由 B3git merge A1
所得. 故A1节点内容可能发生变化, 从而未被能正确运行.而在A,
git merge B
, 则A1节点未改动, 故A1还能正确运行.
git rebase 的例子
- 当前
git rebase dev
# 或
git rebase --onto dev a2405ce f1
得到
- 当前
git rebase dev
# 或
git rebase --onto dev a2405ce f1
得到
cherry-pick
教程: git cherry-pick的使用 cherry-pick 挑好看的小樱桃
不存在git cherry-pick —squash
, 得用 git cherry-pick -n
, 详见