常用命令:
https://blog.csdn.net/Augurlee/article/details/103434894
场景列表:
这里不介绍基本操作,如克隆远程仓库继续开发等。在掌握基本git操作的前提下直接进行场景实践。关于git 文件状态的介绍可以参考 https://www.jianshu.com/p/c2ec5f06cf1a
怎么为本地代码新建远程仓库
- 在Github等网站新建仓库,获得git链接(
选择ssh
)
- 进入本地代码的存放目录,进行初始化
git init
- 添加远程仓库链接
$ git remote add origin
git@github.com:QCHighing/GitTest.git - 新建配置文件
.gitignore
,用来控制禁止上传的文件类型和文件夹
注意: 文件夹后面必须得有/
斜杠,可以使用通配符*
/images/ /videos/ /x64/ /x86/ *.tmp *.tmp_proj *.vcxproj *.vcxproj.filters *.vcxproj.user
- 添加代码到暂存区 Staging
git add .
- 提交本地更改
git commit -m "first commit"
- 拉取远程仓库
git pull --rebase origin master
,会自动合并。如果提示合并失败:
先执行CONFLICT (add/add): Merge conflict in README.md Auto-merging README.md CONFLICT (add/add): Merge conflict in .gitignore Auto-merging .gitignore error: Failed to merge in the changes.
git rebase --abort
放弃这一操作,恢复源文件; 再 往下看 如何解决冲突。 - 推送到远程
git push -u origin master
,第一次推送加u,后续推送可以不选择远端主机和分支,执行git push
即可
发生冲突怎么解决
当本地和远端发生冲突时,pull和push产生失败,需要先解决冲突,再add/commit/push
-
典型冲突:
当pull origin的时候,自动合并 auto merge失败,就需要手动解决:
如果pull的时候进行了--rebase
就先放弃 (git rebase --abort
) , 再手动维护。 -
查看差异:
git log --graph --oneline git log --graph --oneline origin/master git status
可以看出,这两个分支没有交集,是无关的unrelated
,如果单单执行git pull origin master
就会出现提示 “fatal: refusing to merge unrelated histories”。这是因为本地分支是在现有代码基础上创建的,远程分支也是在github上独立创建的,如果想要关联二者,只是添加远程链接git remote add origin xxxxx
是不够的,需要执行--rebase
,然后git会尝试自动合并。上一场景中,如果远程仓库不勾选README和gitignore等文件,那就不会失败。 -
可视化解决:
冲突是不可避免的,必须要有合适的可视化工具去解决冲突。我一直用的比较工具就是BCompare,最强大,在Git上做如下配置即可:win/Linux下编辑配置文件:
vi ~/.gitconfig
[diff] tool = bc4 [difftool] prompt = false [difftool "bc4"] cmd = "\"c:/program files (x86)/beyond compare 4/bcomp.exe\" \"$LOCAL\" \"$REMOTE\"" [merge] tool = bc [mergetool] prompt = false [mergetool "bc4"] cmd = "\"c:/program files (x86)/beyond compare 4/bcomp.exe\" \"$LOCAL\" \"$REMOTE\" \"$BASE\" \"$MERGED\""
注意: 为了避免git自动生成不必要的备份文件,配置完 mergetool 需要先执行:
git config --global mergetool.keepBackup false
取回最新的远程分支:
git fetch origin master
继续pull,发现还是失败,让手动进行合并,那就手动合并吧~~
执行 mergetool :git mergetool
弹出BC4的合并界面,左边是远程,右边是本地,中间是选择合并的结果。要在底部框进行选择合并和编辑,最后点保存按钮。
按照提示执行后续命令git add . git rebase --continue
最后拉取成功,就可以push了。
看一下最终的树结构:git log --graph --oneline
因为执行的是
--rebase1
,所以没有分支合并的痕迹
多人协作时的冲突怎么解决
其实和上一场景一样,差别就在你是选择了pull
,还是选择了push --rebase
。多人协作的开发流程一般是:参考:如何使用Git Rebase
- checkout feature 分支到本地
- 开发,并把开发的内容提交到本地 feature
- 使用 pull 进行同步
- push 到远程仓库
那各自开发完成分别进行合并,手动解决了冲突之后,就变成了这样
会多出C8, C9, C10三个合并结点。但是如果使用git pull --rebase
,就会变成这样,开发线更加清晰。但是丢失了一部分记录,仁者见仁吧。
有冲突,就用git mergetool
手动合并,然后按照要求进行add/pull/push
--continue
想要忽略冲突, 强行push/pull
某些场景下,不需要保留本地的更改,或者需要重置远端,就可以强行pull来覆盖本地代码,或者强行push覆盖远端代码。
- 强行PULL
git fetch origin master # 取回
git reset --hard origin/master # 重置 HEAD 指针
- 强行PUSH
git push -f origin master
同一台电脑上,怎么存在多个用户
为了方便后续实践,先考虑这种情况,便于模拟多人协作的情景。
大多数时候,我们的机器上会有很多的git host,比如公司gitlab、github、oschina等,那我们就需要在本地配置多个ssh key,使得不同的host能使用不同的ssh key
第一步:配置SSH秘钥
比如同时需要管理Github和Gitlab,或者拥有一个公司的github账户,还有一个私人的github账户,这就需要配置多个用户。本地和每个远程端进行ssh连接都需要专一的秘钥配对。因此需要用两个账户生成两对秘钥。
-
生成账户A的公钥和私钥,保存路径改为
C:/Users/Administrator/.sshid_rsa_1
,将生成的id_rsa_1.pub
填入Githubssh-keygen -t rsa -C "user_A_@example.com"
-
生成账户A的公钥和私钥,保存路径改为
C:/Users/Administrator/.sshid_rsa_2
,将生成的id_rsa_2.pub
填入Githubssh-keygen -t rsa -C "user_B_@example.com"
-
配置ssh config(没有的话自己新建一个
~/.ssh/config
):不为主机域名HostName
设置别名Host
就是配置为默认用户;User是访问名可以随意填;端口默认22不起作用时换成443# 配置user1 -> host1 Host github.com HostName github.com IdentityFile C:/Users/Administrator/.ssh/id_rsa_1 PreferredAuthentications publickey User git # Port 443 # 配置user2 -> host2 Host two.github.com HostName github.com IdentityFile C:/Users/Administrator/.ssh/id_rsa_2 PreferredAuthentications publickey User git # Port 443
-
测试github网站
ssh -T git@github.com
和ssh -T git@two.github.com
将公钥填入哪个账户,就能以哪个账户登录了。SSH就是为了免密码登录。
第二步:配置本地仓库
-
移除全局的账户配置
git config --global --unset user.name git config --global --unset user.email git config --global --unset user.password
-
在不同的路径克隆项目
-
在不同的仓库配置用户信息
git config user.name xxx git config user.email xxx git config user.password xxx
-
查看配置信息
git config --list
-
移除远程
git remote rm origin
-
添加远程(重点)
原来的远程链接是:remote.origin.url
=git@github.com:QCH/GitTest.git
如果要用默认账户,那这个远程就不用改,因为SSH配置里没有对默认账户设置别名,如果设置了就需要,比如two
用户应该是:git remote add origin git@two.github.com:QCH/GitTest.git
其中区别自行领悟。
怎么放弃当前工作区或暂存区的修改,恢复到上一次提交?
比如没有pull远程的更新就开始修改,如果及时发现想要重置怎么办?当然还是用 reset
命令。但是要分情况:
-
清空工作区 如果只是修改了代码文件,未新增文件,只需要重新
checkout
,但这只适用于工作区发生更改,如果已经执行了git add
,这样是没有作用的。git checkout .
-
清空暂存区 执行了
git add
后想要重置
方法一:reset --hard
命令,慎重!!!会删除新添加的文件git reset --hard
方法二: git rm --cached xxx
只是删除记录,不删除新增文件
-
恢复上次提交 执行
git commit
后想要重置,还是用reset --hard
命令。HEAD~1
中的1
是默认的,可以不加,也可以换成其他的次数。git reset --hard HEAD~1
-
清空工作区 如果不仅修改了文件,还新增了文件,那么只是重新
checkout
是不够,无法删除新增文件,需要git clean
命令git checkout . git clean -f
当然也可以多执行一步
git add
,然后reset --hard
版本回滚
一般使用log
命令可查看提交记录
git log --oneline --graph
如果执行过reset --hard
,是看不见被强制重置的操作的,需要用到reflog
来查看reset记录
git reflog
然后使用reset进行版本移动:
git reset --hard 索引值 # 前进或后退
或
git reset --hard HEAD~3 # 后退3步
本地多次commit,怎么合并成一次
直接上操作记录
可以看到,现在本地进行了三次提交,已经领先了origin/master 3次提交。现在想要将这三次不重要的commit合并成一次,那只需要在不改动代码的前提下,修改HEAD指针就可以了。用reset
的默认模式–mixed
回退到origin状态,此时代码修改也得以保留。
重新提交,push到远程即可。
后续:
这一场景执行到这里就可以了,但是有人会问 reset --mixed
和 git --soft
的区别。它们都是改变HEAD指针,保留了修改,但是保存的阶段不同:前者执行后所有修改处于工作区
,所以重新commit时需要git add
;而后者执行后所有修改处于暂存区
,后续只需要直接commit即可。
这一差别可以进行以下验证:(谨慎操作,只做测试)
- 修改文件后,进行
git add/commit
- 执行HEAD重置
git reset --soft HEAD^1
,再执行git checkout .
,发现修改还存在; - 执行HEAD重置
git reset HEAD^1
,再执行git checkout .
,发现修改被重置了
本地commit后,发现误添加了文件,怎么取消
需要明确的是,commit之后 HEAD
指针就已经更新了,只是清理缓存区(git rm -r --cache .
)是不行的,可以执行 “退回” 操作。和上一场景一样,选用git reset --mixed
模式,这里不再演示了。我遇到这一情况是因为忘记修改gitignore
文件,把一个第三方库commit了…
临时测试代码怎么办?
- 保存commit当前分支
- 新建分支
git checkout -b test
- 进行代码修改
- … …
- 测试完成
- 保存commit当前的测试分支
- 切回原分支
git checkout -b master
- 删除测试分支
git branch -D test
注意: 如果不commit而是直接切换分支,则当前工作区做过的所有更改都将跟随到新分支,见 https://segmentfault.com/q/1010000010715691,所以必须提交后再checkout,可以执行分支对比 git difftool -b test
git clone 断点续传
- 运行以下命令进行clone
git clone --recursive https:xxxxxx
- 如果clone中止,则进入项目根目录,继续下载
cd eigen-git-mirror
git submodule update --init --recursive
(To be continue)