progit笔记:Git工具
选择修订版本
单个修订版本
# 查看提交信息
$ git log
# git show sha1 显示这次提交的信息
$ git show 1c002d
# 输出结果会显示简短且唯一的值 abbrev(缩写)
$ git log --abbrev-commit --pretty=oneline
分支引用
# 查看一个分支的最后一次提交的对象
$ git show topic1
显示提交信息:
git show <sha1>
git show <branch>
git show <tag>
# rev-parse 查看分支的最后一次提交的对象
$ git rev-parse topic1
ca82a6dff817ec66f44342007202690a93763949
引用日志
# 查看引用日志
$ git reflog
# @{n} 查看仓库中 HEAD 在5次前的所指向的提交
$ git show HEAD@{5}
# 查看你的master分支在昨天的时候指向了哪个提交
$ git show master@{yesterday}
# 查看类似于 git log 输出格式的引用日志信息
$ git log -g master
注意:引用日志只存在于本地仓库,它只是一个记录你在自己的仓库里做过什么的日志。
祖先引用
# 查看当前分支信息
$ git log --pretty=format:'%h %s' --graph
# 查看上一个提交,即父提交
$ git show HEAD^
# d921970^2 代表 “d921970 的第二父提交,这个语法只适用于合并的提交,第二父提交是你所合并的分支(例如 topic):
# 父提交
$ git show d921970^
# 第二父提交
$ git show d921970^2
# ~ 同样是指向第一父提交,HEAD~ 和 HEAD^
# HEAD~2 代表“第一父提交的第一父提交”,也就是“祖父提交”, HEAD~~ 等价于 HEAD~2
$ git show HEAD~3
提交区间
- 双点
# 查看experiment分支中还有哪些提交尚未被合并入master分支(master分支没有的)
$ git log master..experiment
# 查看在master分支中而不在experiment分支中的提交(experiment分支没有的)
$ $ git log experiment..master
# 会输出在你当前分支中而不在远程origin中的提交(远程origin/master没有的)
$ git log origin/master..HEAD
# git log origin/master.. 等价于上面命令
- 多点
# ^ 字符或者 --not 来指明你不希望提交被包含其中的分支
# 下面三个命令等价
$ git log refA..refB
$ git log ^refA refB
$ git log refB --not refA
# 超过两个的引用
$ git log refA refB ^refC
$ git log refA refB --not refC
- 三点
# 可以选择出被两个引用之一包含但又不被两者同时包含的提交( !(A&B) )
$ git log master...experiment
F
E
D
C
# --left-right,它会显示每个提交到底处于哪一侧的分支
< F
< E
> D
> C
贮藏与清理
贮藏工作
想要切换分支,但是还不想要提交之前的工作;所以贮藏修改
# 将新的贮藏推送到栈上,运行 git stash或 git stash push:
$ git stash
# 查看贮藏的东西
$ git stash list
# 贮藏的工作重新应用
$ git stash apply
# 可以通过名字指定
$ git stash apply stash@{2}
注意:文件的改动被重新应用了,但是之前暂存的文件却没有重新暂存,回到原来的位置
# --index 选项,来尝试重新应用暂存的修改
$ git stash apply --index
# git stash drop 加上将要移除的贮藏的名字来移除它
$ git stash drop stash@{0}
# 来应用贮藏然后立即从栈上扔掉它
$ git stash pop
贮藏的创意性使用
# --keep-index 选项不仅要贮藏所有已暂存的内容,同时还要将它们保留在索引中(通过git status -s 仍然可以查到)
$ git status -s
M index.html
M lib/simplegit.rb
$ git stash --keep-index
Saved working directory and index state WIP on master: 1b65b17 added the
index file
HEAD is now at 1b65b17 added the index file
$ git status -s
M index.html
# -u 选项会贮藏任何未跟踪文件
$ git stash -u
# 额外包含忽略的文件,使用 --all 或 -a选项
# --patch 标记,Git不会贮藏所有修改过的任何东西, 但是会交互式地提示哪些改动想要贮藏、哪些改动需要保存在工作目录中
$ git stash --patch
从贮藏创建一个分支
# git stash branch <new branchname> 以你指定的分支名创建一个新分支,检出贮藏工作时所在的提交,重新在那应用工作,然后在应用成功后丢弃贮藏:
$ git stash branch testchanges
清理工作目录
# git clean 被设计为从工作目录中移除未被追踪的文件
# -f(force) 来移除工作目录中所有未追踪的文件以及空的子目录
$ git clean -f -d
# -n 选项说明将要移除什么(实际并不移除)
$ git clean -d -n
# -x 选项移除包括.gitignore或其他忽略文件中的模式匹配的文件,例如 .o 文件
$ git clean -d -n -x
Would remove test.o
搜索
Git Grep
# -n 或 --line-number 选项来输出 Git 找到的匹配行的行号:
$ git grep -n test
# -c 或 --count 选项选项输出概述的信息:
$ git grep -c test
# -p 或 --show-function 选项来显示每一个匹配的字符串所在的方法或函数:
$ git grep -p test *.txt
# --and 标志来查看复杂的字符串组合
$ git grep --break --heading -n -e 'test' --and -e 'stash'
Git日志搜索
# -S 选项来找到是什么时候引入的
$ git log -S 'test' --oneline
行日志搜索
# -L 选项展示代码中一行或者一个函数的历史
$ git log -L :test:new.txt
重写历史
修改最后一次提交
# 新的改进后的提交来 替换 掉旧有的最后一次提交
$ git commit --amend
注意:修正会改变提交的 SHA-1 校验和
# 忘记一个暂存的文件,避免不必要的编辑器环节
$ git commit --amend --no-edit
修改多个提交信息
# 尝试修改最后三次提交
$ git rebase -i HEAD~3
# 编辑器会显示一个提交列表,将要修改提交信息的pick 属性改为 edit,保存退出按提示信息操作
#pick f7f3f6d changed my name a bit
#pick 310154e updated README formatting and added blame
#pick a5f4a0d added cat-file
# stop在设置了 edit 的提交信息上,修改该提交信息,继续处理下一个edit
$ git commit --amend
# 编辑器修改...
$ git rebase --continue
重新排序提交
# 修改脚本中提交信息的顺序,保存退出即可
$ git rebase -i HEAD~3
压缩提交
# 指定 “squash(压缩)”
#pick f7f3f6d changed my name a bit
#squash 310154e updated README formatting and added blame
# 保存并退出编辑器时,会弹出编辑器显示合并这两次的提交信息,当保存后,就合并了这两个提交
拆分提交
git rebase -i HEAD~2
# 将要拆分的提交的指令修改为 “edit”:
#pick 6fb1cb6 test rebase amend
#edit 32d7774 add a.txt & b.txt
# stop 在add a.txt & b.txt,重置这个提交
$ git reset HEAD^
$ git add a.txt
$ git commit -m 'add a.txt'
$ git add b.txt
$ git commit -m 'add b.txt'
# git status 可以看出 interactive rebase in progress; onto <sha1>
$ git rebase --continue
重置解密(reset和checkout)
三棵树
树 | 用途 |
---|---|
HEAD | 上一次提交的快照,下一次提交的父结点 |
Index | 预期的下一次提交的快照 |
Working Directory | 沙盒 |
HEAD: 是当前分支引用的指针,它总是指向该分支上的最后一次提交。这表示 HEAD 将是下一次提交的父结点。
索引:是你的 预期的下一次提交。我们也会将这个概念引用为 Git 的“暂存区”。
工作目录:通常也叫工作区。
工作流程
git init
,这会创建一个 Git 仓库,其中的 HEAD 引用指向未创建的 master 分支。
git add
来获取工作目录中的内容,并将其复制到索引中。
git commit
,它会取得索引中的内容并将它保存为一个永久的快照, 然后创建一个指向该快照的提交对象,最后更新 master 来指向本次提交。
此时运行 git status
,三棵树完全相同。
切换分支或者克隆:它会修改 HEAD 指向新的分支引用,将索引填充为该次提交的快照, 然后将索引的内容复制到工作目录中。
重置的作用
reset
命令会以特定的顺序重写这三棵树,在你指定以下选项时停止:
- 移动 HEAD 分支的指向 (若指定了
--soft
,则到此停止) - 使索引看起来像 HEAD (若未指定
--hard
,则到此停止) - 使工作目录看起来像索引
注意:--hard
标记是 reset 命令唯一的危险用法,会覆盖掉工作目录文件。
通过路径来重置
若指定了一个路径,reset
将会跳过第 1 步,并且将它的作用范围限定为指定的文件或文件集合(HEAD无法同时指向两个提交中各自的一部分)。不过索引和工作目录 可以部分更新,所以重置会继续进 行第 2、3 步。
git reset file.txt
(git reset --mixed HEAD file.txt
的简写),它会:
- 移动 HEAD 分支的指向 (已跳过)
- 让索引看起来像 HEAD (到此处停止)
它有取消暂存文件的作用,和git add
正好相反
# 通过具体指定一个提交来拉取该文件的对应版本
$ git reset eb43bf file.txt
与git add
一样,就是 reset
命令也可以接受一个 --patch
选项来一块一块地取消暂存的内容。
压缩
# 来将 HEAD 分支移动到一个旧一点的提交上(即你想要保留的最 近的提交)
$ git reset --soft HEAD~2
$ git commit
检出(checkout)
不带路径
git checkout [branch]
与运行 git reset --hard [branch]
非常相似,它会更新所有三棵树使其看起来像 [branch]
,不过有两点区别:
- 不同于
reset --hard
,checkout
对工作目录是安全的,它会通过检查来确保不会将已更改的文件弄丢。它会在工作目录中先试着简单合并一下,这样所有 还未修改过的 文件都会被更新。而reset --hard
则会不做检查就全面地替换所有东西。 reset
会移动 HEAD 分支的指向,而checkout
只会移动HEAD 自身来指向另一个分支。
带路径
运行 checkout
,这会像 reset
一样不会移动 HEAD。 它就像 gitreset [branch] file
那样用该次提交中的那个文件来更新索引,但是它也会覆盖工作目录中对应的文件。它就像是 git reset --hard [branch] file
(如果 reset
允许你这样运行的话), 这样对工作目录并不安全,它也不会移动 HEAD。
同 git reset
和 git add
一样,checkout
也接受一个 --patch
选项,允许你根据选择一块一块地恢复文件内容。
总结
“HEAD” 一列中的 “REF” 表示该命令移动了 HEAD 指向的分支引用,而 “HEAD” 则表示只移动了 HEAD 自身。
<> | HEAD | INDEX | WorkDIR | WD Safe |
---|---|---|---|---|
Commit Level | ||||
reset --soft [commit] | REF | NO | NO | YES |
reset [commit] | REF | YES | NO | YES |
reset --hard [commit] | REF | YES | YES | NO |
checkout <commit> | HEAD | YES | YES | YES |
File Level | ||||
reset [commit] <paths> | NO | YES | NO | YES |
checkout [commit] <paths> | NO | YES | YES | NO |
高级合并
# 中断一次合并
$ git status -sb
## master
UU hello.rb
$ git merge --abort
$ git status -sb
## master
注意:git merge --abort
选项会尝试恢复到你运行合并前的状态,但当运行命令前,在工作目录中有未储藏、未提交的修改时它不能完美处理。
忽略空白
#-Xignore-all-space 或 -Xignore-space-change选项。 第一个选项在比较行时完全忽略空白修改,第二个选项将一个空白符与多个连续的空白字符视作等价的。
$ git merge -Xignore-space-change whitespace
手动文件再合并
在合并冲突状态下
# 将冲突文件的这些版本释放出一份拷贝
$ git show :1:hello.rb > hello.common.rb
$ git show :2:hello.rb > hello.ours.rb
$ git show :3:hello.rb > hello.theirs.rb
# 最后使用git clean 清理这些文件
#$ git clean -f
# ls-files -u 底层命令来得到这些文件的 Git blob 对象的实际 SHA-1值
$ git ls-files -u
100755 ac51efdc3df4f4fd328d1a02ad05331d8e2c9111 1 hello.rb
100755 36c06c8752c78d2aff89571132f3bf7841a7b5c3 2 hello.rb
100755 e85207e04dfdd5eb0a1e9febbc67fd837c44a1cd 3 hello.rb
# 手工修复它们来修复空白问题,然后使用 git merge-file 命令来重新合并那个文件
$ git merge-file -p hello.ours.rb hello.common.rb hello.theirs.rb > hello.rb
$ git diff -b
# --ours 合并前比较结果与在你的分支上的内容
$ git diff --ours
# 查看合并的结果与他们那边有什么不同
$ git diff --theirs
# 查看文件在两边是如何改动的
$ git diff --base -b
检出冲突
# --conflict 选项的 git checkout 重新检出文件并替换合并冲突标记
# 可以传递给 --conflict 参数 diff3 或 merge(默认选项, 不仅仅只给你 “ours” 和 “theirs” 版本,同时也会有 “base” 版本在中间来给你更多的上下文。
$ git checkout --conflict=diff3 hello.rb
合并日志
在合并冲突状态下
# 得到此次合并中包含的每一个分支的所有独立提交的列表
$ git log --oneline --left-right HEAD...MERGE_HEAD
# --merge 选项显示任何一边接触了合并冲突文件的提交
$ git log --oneline --left-right --merge
组合式差异格式
在合并冲突下
# 组合式差异格式,
# 第一列显示‘ours’分支与工作目录的文件区 别(添加或删除)
# 第二列显示 “theirs” 分支与工作目录的拷贝区别
$ git diff
diff --cc hello.rb
index 0399cd5,59727f0..0000000
--- a/hello.rb
+++ b/hello.rb
@@@ -1,7 -1,7 +1,11 @@@
#! /usr/bin/env ruby
def hello
++<<<<<<< HEAD
+ puts 'hola world'
++=======
+ puts 'hello mundo'
++>>>>>>> mundo
end
hello()
解决冲突后
# $ git diff显示
- puts 'hola world'
- puts 'hello mundo'
++ puts 'hola mundo'
# -CC 日志显示合并的补丁
$ git log -p
$ git log --cc -p -1
撤销合并
修复引用
在错误的 git merge
后运行 git reset --hard HEAD~
reset --hard
通常会经历三步:
- 移动 HEAD 指向的分支。
- 使索引看起来像 HEAD。
- 使工作目录看起来像索引。
注意:它会重写历史,在一个共享的仓库中这会造成问题的(参考变基的风险)
还原提交
# 生成一个新提交的选项,提交将会撤消一个已存在提交的所有修改,
$ git revert -m 1 HEAD
新的提交 ^M 与 C6 有完全一样的内容
当尝试再次合并topic到master
$ git merge topic
Already up-to-date.
topic
中并没有东西不能从 master 中追踪到达。更糟的是,如果你在 topic 中增加工作然后再次合并,Git 只会引入被还原的合并之后的修改(没有合并C3
和C4
)。
解决方法是撤销还原原始的合并,创建一个新的合并并提交
$ git revert ^M
[master 09f0126] Revert "Revert "Merge branch 'topic'""
$ git merge topic
在本例中,M
与 \^
M抵消了。 ^^M
事实上合并入了 C3
与 C4
的修改,C8
合并了 C7
的修改,所以现在 topic 已经完全被合并了。
其他类型的合并
# -Xours 或 -Xtheirs 参数,选择特定的一边并忽略另外一边
$ git merge -Xours mundo
# 假的合并,不关注你正合并入的分支, 它只会简单地把当前分支的代码当作合并结果记录下来
$ git merge -s ours mundo
Rerere
rerere 重用记录的解决方案(reuse recorded resolution),它允许你让 Git 记住解决一个块冲突的方法, 这样在下一次看到相同冲突时,Git 可以为你自动地解决它。
使用情形:
- 保持一个长期分支会干净地合并,不想要一串中间的合并提交弄乱你的提交历史(可以尝试合并,退出合并,方便最终合并)。
- 维持一个变基的分支时,就不用每次解决同样的变基冲突。
# 启用rerere
$ git config --global rerere.enabled true
使用Git调试
文件标注
# 显示任何文件中每行最后一次修改的提交记录 -L range
$ git blame -L 69,82 Makefile
# -C 会分析你正在标注的文件, 并且尝试找出文件中从别的地方复制过来的代码片段的原始出处
$ git blame -C -L 141,153 GITPackUpload.m
二分查找
二分法查找有问题的提交
$ git bisect start
$ git bisect bad
$ git bisect good v1.0
Bisecting: 6 revisions left to test after this
[ecb6e1bc347ccecc5f9350d878ce677feb13d3b2] error handling on repo
# git bisect good继续寻找
$ git bisect good
Bisecting: 3 revisions left to test after this
[b047b02ea83310a70fd603dc8cd7a6cd13d15c04] secure this thing
# 发现这个提交下是有问题的,git bisect bad 告诉Git:
$ git bisect bad
Bisecting: 1 revisions left to test after this
[f71ce38690acf49c1f3c9bea38e09d82a5ce6014] drop exceptions table
# 重置HEAD指针到最开始的位置
$ git bisect reset
子模块
子模块允许你将一个 Git 仓库作为另一个 Git 仓库的子目录。 它能让你将另一个仓库克隆到自己的项目中,同时还保持提交的独立。
# git submodule add 命令添加新的子模块
$ git submodule add https://github.com/abelgaxiola/DBconnector
# 查看差异输出
$ git diff --cached --submodule
克隆含有子模块的项目
当你在克隆这样的项目时,默认会包含该子模块目录,但其中还没有任何文件。
需要运行两个命令:git submodule init
用来初始化本地配置文件,而 git submodule update
则从该项目中抓取所有数据并检出父项目中列出的合适的提交。
$ git submodule init
$ git submodule update
# --recurse-submodules 选项,它就会自动初始化并更新仓库中的每一个子模块, 包括可能存在的嵌套子模块
$ git clone --recurse-submodules https://github.com/chaconinc/MainProject
如果克隆忘记加 --recurse-submodules
,可以运行那么可以运行 git submodule update--init
将 git submodule init
和 git submodule update
合并成一步。如果还要初始化、抓取并检出任何嵌套的子模块, 请使用简明的 git submodule update --init --recursive
。
在包含子模块的项目上工作
从子模块的远端拉取上游修改
# 在子目录运行,合并上游分支来更新本地代码
$ git fetch
$ git merge origin/master
# 父目录运行,看到子目录更新的同时获得了一个包含新添加提交的列表
$ git diff --submodule
不想在子目录中手动抓取与合并,可以
# 默认你想要更新并检出子模块仓库的 master 分支
$ git submodule update --remote DbConnector
想要 DbConnector 子模块跟踪仓库的 “stable” 分支
# 如果不用 -f .gitmodules 选项,那么它只会为你做修改
$ git config -f .gitmodules submodule.DbConnector.branch stable
$ git submodule update --remote
# git status Git 会显示子模块中有“新提交”
$ git status
# 通过设置,使 git status 显示你的子模块的更改摘要
$ git config status.submodulesummary 1
从项目远端拉取上游更改
通过 git pull
获取项目中新的提交,会显示 Submodules changed but not updated 不会更新子模块。
# 在git pull后,输入以下命令完成更新
$ git submodule update --init --recursive
$ git status
自动化上述过程
$ git pull --recurse-submodules
想让 Git总是以 --recurse-submodules
拉取,可以将配置选项 submodule.recurse
设置为 true
。
在拉取的提交中,可能 .gitmodules
文件中记录的子模块的 URL 发生了改变,
那么执行 git pull --recurse-submodules
或 git submodule update
就会失败,执行下面命令:
# 将新的 URL 复制到本地配置中
$ git submodule sync --recursive
# 从新 URL 更新子模块
$ git submodule update --init --recursive
在子模块上工作
运行 git submodule update
从子模块仓库中抓取修改时, Git 将会获得这些改动并更新子目录中的文件,但是会将子仓库留在一个称作“游离的 HEAD”的状态。这意味着没有本地工作分支(例如“master” )跟踪改动。 如果没有工作分支跟踪更改,也就意味着即便你将更改提交到了子模块,这些更改也很可能会在下次运行 git submodule update 时丢失。
为了让子模块设置得更容易进入并修改
- 进入每个子模块并检出其相应的工作分支
- 若你做了更改,运行 git submodule update --remote 来从上游拉取新工作。 你可以选择将它们合并到你的本地工作中,也可以尝试将你的工作变基到新的更改上。
$ cd DbConnector/
# 1. 检出分支
$ git checkout stable
# 2. update 并 merge
$ cd ..
$ git submodule update --remote --merge
如果你忘记 --rebase
或 --merge
,Git 会将子模块更新为服务器上的状态。并且会将项目重置为一个游离的HEAD 状态。
$ git submodule update --remote
Submodule path 'DbConnector': checked out
'5d60ef9bbebf5a0c1c1050f242ceeb54ad58da94'
这时需要回到目录再次检出你的分支(即还包含着你的工作的分支)然后手动地合
并或变基 origin/stable
(或任何一个你想要的远程分支)就行了。
-
如果你没有提交子模块的改动,那么运行一个子模块更新也不会出现问题,此时 Git 会只抓取更改而并不会覆盖子模块目录中未保存的工作,会提示 Your local changes to the following files would be overwritten by checkout。
-
如果做了一些和上游改动冲突的改动,Git会提示冲突合并,可以进入子模块目录像平时那样修复冲突。
发布子模块改动
如果我们在主项目中提交并推送但并不推送子模块上的改动,其他尝试检出我们修改的人会遇到麻烦, 因为他 们无法得到依赖的子模块改动。那些改动只存在于我们本地的拷贝中。
# 使用“check” 的 --recurse-submodules 参数,会提示如何做
$ git push --recurse-submodules=check
The following submodule paths contain changes that can
not be found on any remote:
DbConnector
Please try
git push --recurse-submodules=on-demand
or cd to the path and use
git push
to push them to a remote.
可以通过设置 git config push.recurseSubmodules check
让它成为默认行为。
# Git 进入到 DbConnector 模块中然后在推送主项目前推送了它
$ git push --recurse-submodules=on-demand
可以通过设置 git config push.recurseSubmodules on-demand
让 它成为默认行为。
合并子模块改动
# 运行 git diff,得到试图合并的两个分支中记录的提交的 SHA-1 值。
$ git diff
diff --cc DbConnector
index eb41d76,c771610..0000000
--- a/DbConnector
+++ b/DbConnector
eb41d76
是我们的子模块中大家共有的提交,而 c771610
是上游拥有的提交。
# 创建一个分支然后尝试合并
$ cd DbConnector
$ git rev-parse HEAD
eb41d764bccf88be77aced643c13a7fa86714135
$ git branch try-merge c771610
$ git merge try-merge
# 接下来解决真正的合并冲突
子模块的技巧
子模块遍历
# 保存所有子模块的工作进度
$ git submodule foreach 'git stash'
# 创建一个新分支,并将所有子模块都切换过去
$ git submodule foreach 'git checkout -b featureA
# 生成一个主项目与所有子项目的改动的统一差异
$ git diff; git submodule foreach 'git diff'
有用的别名
$ git config alias.sdiff '!'"git diff && git submodule foreach 'git diff'"
$ git config alias.spush 'push --recurse-submodules=on-demand'
$ git config alias.supdate 'submodule update --remote --merge'
子模块的问题
切换分支
$ git --version
$ git checkout -b add-crypto
$ git submodule add https://github.com/cryptomator/cryptolib
$ git commit -am 'adding crypto library'
$ git checkout master
warning: unable to rmdir CryptoLibrary: Directory not empty
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Untracked files:
(use "git add <file>..." to include in what will be committed)
CryptoLibrary/
nothing added to commit but untracked files present (use "git add" to
track)
移除它然后切换回有那个子模块的 分支,需要运行 submodule update --init 来重新建立和填充
$ git clean -fdx
$ git checkout add-crypto
$ git submodule update --init
注意:git clean -fdx
如果删不掉,可以在加上第二个 -f 选项,用力删除。
--recurse-submodules
选项简化了这些步骤,切换到的分支让子模块处于的正确状态。
$ git --version
$ git checkout -b add-crypto
$ git submodule add https://github.com/cryptomator/cryptolib
$ git commit -am 'adding crypto library'
$ git checkout --recurse-submodules master
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working tree clean
可以通过 git config submodule.recurse true
设置 submodule.recurse
选项, 告诉Git(>=2.14)总是使用 --recurse-submodules
。
从子目录切换到子模块
$ rm -Rf CryptoLibrary/
$ git submodule add https://github.com/chaconinc/CryptoLibrary
'CryptoLibrary' already exists in the index
必须要先取消暂存 CryptoLibrary 目录。 然后才可以添加子模块
$ git rm -r CryptoLibrary
$ git submodule add https://github.com/chaconinc/CryptoLibrary
在假设你在一个分支下做了这样的工作。 如果尝试切换回的分支中那些文件还在子目录而非子模块中时
$ git checkout master
error: The following untracked working tree files would be overwritten by
checkout:
CryptoLibrary/Makefile
CryptoLibrary/includes/crypto.h
...
Please move or remove them before you can switch branches.
Aborting
可以通过 checout -f
来强制切换,如果其中还有未保存的修改,这个命令会把它们覆盖掉
$ git checkout -f master
warning: unable to rmdir CryptoLibrary: Directory not empty
Switched to branch 'master'
当你切换回来之后,因为某些原因你得到了一个空的 CryptoLibrary
目录,并且 git submodule update
也无法修复它。 你需要进入到子模块目录中运行 git checkout .
来找回所有的文件。 你也可以通过 submodule foreach
脚本来为多个子模块运行它。
打包
# 包含了重建master分支所需的数据
$ git bundle create repo.bundle HEAD master
注意: 如果希望这个仓库可以在别处被克隆,应该像例子中那样增加一个 HEAD 引用
使用bundle
$ git clone repo.bundle repo
如果你在打包时没有包含 HEAD 引用,你还需要在命令后指定一个 -b master
或者其他被引入的分支, 否则 Git 不知道应该检出哪一个分支。
根据提交区间打包
$ git log --oneline master ^origin/master
71b84da last commit - second repo
c99cf5b fourth commit - second repo
7011d3d third commit - second repo
# 9a466c5 是 7011d3d的父提交
$ git bundle create commits.bundle master ^9a466c5
# bundle verify 检查这个文件是否是一个合法的 Git 包,是否拥有共同的祖先来导入
$ git bundle verify ../commits.bundle
# 列出顶端
$ git bundle list-heads ../commits.bundle
导入提交
# 使用 fetch 或 pull导入提交,从包中取出 master 分支到我们仓库中的 other-master分支:
$ git fetch ../commits.bundle master:other-master
```