git 多仓库

git fetch old-origin master:baseline-main

git push origin baseline-main:baseline-main

git checkout -b baseline-main old-origin/master 以远程分支创建并切换

git push origin baseline-main

git branch baseline-bridge old-origin/relase 以远程分支创建分支

git push origin baseline-main:baseline-main 推上到远程

git branch –set-upstream-to=origin/dev 关联远程分支

git remote rename origin old-origin

Git remote add origin

npm install –save node-sass –registry=https://registry.npm.taobao.org –disturl=https://npm.taobao.org/dist –sass-binary-site=https://npm.taobao.org/mirrors/node-sass/

# 跨仓库合并代码

1. 添加基线仓库

git remote add baseline-origin url

2. 切基线分支到本地

git checkout -b baseline-main baseline-origin/baseline-main

或 git fetch baseline-origin baseline-main

3. 本地开发分支合并基线代码

如果git merge合并的时候出现refusing to merge unrelated histories的错误,原因是两个仓库不同而导致的,需要在后面加上–allow-unrelated-histories进行允许合并,即可解决问题

git merge baseline-main –allow-unrelated-histories

或 git merge baseline-origin/baseline-main –allow-unrelated-histories

# 添加交付仓库 合并代码

1. 添加交付仓库

git remote add jf-origin [交付仓库地址url]

2. 切交付分支到本地

git checkout -b baseline-bridge jf-origin/baseline-bridge // baseline-bridge 是交付中转分支, 交付仓库已存在

git branch –set-upstream-to=jf-origin/baseline-bridge //关联交付远程baseline-bridge分支

3. 在 baseline-bridge 分支上 合并基线分支

git merge [基线分支]

git push // 推送到交付仓库

git submodule

添加子模块

git submodule add https://github.com/chaconinc/DbConnector  [可填具体目录]

此时 会多出 .gitmodules 文件 ,记录子模块

DbConnector是工作目录中的一个子目录,但 Git 还是会将它视作一个子模块。当你不在那个目录中时,Git 并不会跟踪它的内容, 而是将它看作子模块仓库中的某个具体的提交

git diff –cached –submodule 查看子模块变动

克隆含有子模块的项目

当你在克隆这样的项目时,默认会包含该子模块目录,但其中还没有任何文件

你必须运行两个命令:git submodule init 用来初始化本地配置文件,而 git submodule update 则从该项目中抓取所有数据并检出父项目中列出的合适的提交。

如果给 git clone 命令传递 --recurse-submodules 选项,它就会自动初始化并更新仓库中的每一个子模块

你已经克隆了项目但忘记了 --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 查看新添加提交的列表

git config --global diff.submodule log    //把--submdule 设置为默认行为,下次可以直接用git diff

git submodule update –remote // 自动从远程抓取合并,但是默认合并的是master 分支

如果你想跟踪其他分支比如dev分支:

git config -f .gitmodules submodule.DbConnector.branch dev // -f .gitmodules 选项 写入 .gitmodules, 其他人也能更新到这个配置

更新子模块后,运行 git status,Git 会显示子模块中有“新提交”, 然后再提交主git,把这次更新体检

git config status.submodulesummary 1 // 设置这个后,git status 会显示子模块修改摘要

git pull 命令会递归地抓取子模块的更改,如上面第一个命令的输出所示。 然而,它不会 更新 子模块

 git pull 命令添加 --recurse-submodules  自动化拉起合并所有子模块

# 将新的 URL 复制到本地配置中
$ git submodule sync --recursive
# 从新 URL 更新子模块
$ git submodule update --init --recursive

在子模块上工作

说明: 子模块是没有本地分支的,修改时都是在游离的HEAD 上更改,下次运行 git submodule update 时就会丢失

为了方便的在本地修改子模块,需要做以下几步:

先更新

$ cd DbConnector/   //进入子模块
$ git checkout stable // 切出版本
Switched to branch 'stable'

$ cd ..  // 返回根目录
$ git submodule update --remote --merge  //子模块合并远程

修改子模块

$ cd DbConnector/
$ vim src/db.c
$ git commit -am 'unicode support'
[stable f906e16] unicode support
 1 file changed, 1 insertion(+)

此时我们发下远程子模块也有一个更改,就要去合并远程分支

$ cd ..
$ git submodule update --remote --rebase
First, rewinding head to replay your work on top of it...
Applying: unicode support
Submodule path 'DbConnector': rebased into '5d60ef9bbebf5a0c1c1050f242ceeb54ad58da94'

注意:1如果你忘记 --rebase 或 --merge,Git 会将子模块更新为服务器上的状态。并且会将项目重置为一个游离的 HEAD 状态。 如果真的发生了也不要紧,你只需回到目录中再次检出你的分支(即还包含着你的工作的分支)然后手动地合并或变基 origin/stable(或任何一个你想要的远程分支)就行了

2. 如果你没有提交子模块的改动,那么运行一个子模块更新也不会出现问题,此时 Git 会只抓取更改而并不会覆盖子模块目录中未保存的工作

3. 如果你做了一些与上游改动冲突的改动, 你可以进入子模块目录中然后就像平时那样修复冲突

发布子模块改动

说明: 主项目中提交并推送但并不推送子模块上的改动,其他尝试检出我们修改的人会遇到麻烦, 因为他们无法得到依赖的子模块改动。那些改动只存在于我们本地中。为了确保这不会发生,你可以让 Git 在推送到主项目前检查所有子模块是否已推送。 git push 命令接受可以设置为 “check” 或 “on-demand” 的 --recurse-submodules 参数。 如果任何提交的子模块改动没有推送那么 “check” 选项会直接使 push 操作失败。

$ git push --recurse-submodules=check

git config push.recurseSubmodules check  //让它成为默认行为

on-demand,它会使Git 会进入到 子模块中,然后在推送主项目前,推送了子模块。 如果那个子模块因为某些原因推送失败,主项目也会推送失败

合并子模块改动

子模块的问题

git checkout --recurse-submodules master //主项目切换分支时,使用这个可以使子模块目录处于正确的状态。 幸运的是,你可以通过 git config submodule.recurse true 设置 submodule.recurse 选项,是所有命令都带上这个默认配置,除了git clone

从子目录切换到子模块

 假设项目内有一些文件在子目录中,你想要将其转换为一个子模块。 如果删除子目录然后运行 submodule add,Git 会朝你大喊:

$ 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 patch

普通开发者从软件仓库clone下代码,然后写入代码,做一个Patch, 最后用E-mail发给Linux Kernel的维护者就好了。Git最初作为Linux的版本控制工具,提供了透明、完整、稳定的Patch功能。

我们可以首先用git diff制作一个patch。本文示例的工作目录里最初有一个文件a,内容是“This is the file a.”,放置在master分支中。为了修改代码,我们一般的做法是建立一个新分支:

git branch Fix
git checkout Fix
//接下来我们在a文件里面追加一行,然后执行git diff。
git diff
diff --git a/a b/a
index 4add65f..0d295ac 100644
--- a/a
+++ b/a
@@ -1 +1,2 @@
This is the file a.
+Fix!!!
这是一个非常典型的Patch式diff。这样我们可以直接把这个输出变为一个Patch:
git commit -a -m "Fix"
git diff master > patch
git checkout master

我们现在有一个patch文件,并且签出了master,接下来我们可以使用git apply来应用这个patch。当然了,实际应用中,我们不会这样在一个分支建patch,到另一个分支去应用,因为只有merge一下就好了。我们现 在权当没有这个Fix分支。一般情况下,为了保护master,我们会建立一个专门处理新交来的patch的分支:

git branch PATCH 
git checkout PATCH
git apply patch
git commit -a -m "Patch Apply"

现在我们在PATCH分支中应用了这个补丁,我们可以把PATCH分支和Fix比对一下,结果肯定是什么也没有,说明PATCH分支和Fix分支完全一样。patch应用成功。即使有多个文件git diff 也能生成一个patch。

2.git format-patch生成的git专用补丁

git format-patch -M master

git format-patch的-M选项表示这个patch要和那个分支比对。现在它生成了一个patch文件,我们看看那是什么:

 cat 0001-Fix1.patch
git checkout -b PATCH
git am 0001-Fix1.patch
git commit -a -m "PATCH apply"
  • 兼容性:很明显,git diff生成的Patch兼容性强。如果你在修改的代码的官方版本库不是Git管理的版本库,那么你必须使用git diff生成的patch才能让你的代码被项目的维护人接受。
  • 除错功能:对于git diff生成的patch,你可以用git apply –check 查看补丁是否能够干净顺利地应用到当前分支中;如果git format-patch 生成的补丁不能打到当前分支,git am会给出提示,并协助你完成打补丁工作,你也可以使用git am -3进行三方合并,详细的做法可以参考git手册或者《Progit》。从这一点上看,两者除错功能都很强。
  • 版本库信息:由于git format-patch生成的补丁中含有这个补丁开发者的名字,因此在应用补丁时,这个名字会被记录进版本库,显然,这样做是恰当的。因此,目前使用Git的开源社区往往建议大家使用format-patch生成补丁。

git hooks

git hooks 的 过程 link

husky 是一个 Git Hook 工具

  1. npm 安装 husky,lint-staged,@commitlint/cli,@commitlint/config-conventional 依赖

lint-staged: 用于实现每次提交只检查本次提交所修改的文件。

注意:一定要使用 npm 安装 eslint 和 husky,因为在 windows 操作系统下, 用 yarn 安装依赖,不会触发 husky pre-commit 钩子命令。

package.json 添加配置

"husky": {
    "hooks": {
      "pre-commit": "lint-staged",
   // "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
      "pre-push": "yarn test"
    }
  },
  "lint-staged": {
    "*.{js,jsx,ts,tsx}": "eslint --fix",
    "*.{css,scss,sass}": "stylelint --fix",
    "*.{json,md,yml}": "prettier --write"
  },

//.eslintrc .prettierrc  .stylelintrc 添加在根目录下配置,具体见官网

创建 commitlint.config.js 可以配置 commit msg 格式

module.exports = {
  extends: ['@commitlint/config-conventional'],
  rules: {
    'type-enum': [
        2,
        'always',
        [
        'feat', // 新功能(feature)
        'fix', // 修补bug
        'docs', // 文档(documentation)
        'style', // 格式(不影响代码运行的变动)
        'refactor',//重构(即不是新增功能,也不是修改bug的代码变动)
        'test', // 增加测试
        'revert', // 回滚
        'config', // 构建过程或辅助工具的变动
        'chore', // 其他改动
        ],
    ],
    'type-empty': [2, 'never'], // 提交不符合规范时,也可以提交,但是会有警告
    'subject-empty': [2, 'never'], // 提交不符合规范时,也可以提交,但是会有警告
    'subject-full-stop': [0, 'never'],
    'subject-case': [0, 'never'],
  }

pull request

  1. fork the repo
  2. clone your fork
  3. Create the branch : git checkout -b pr/dd
  4. Run npm install
  5. Run npm run build , if everyting work, then, you’re ready to make changes.
  6. Run npm run test. Make you changes and try to make the tests pass. if you can’t or need help then commit what you have with –no-vertify and make a PR.
  7. if you get things working , add your changed files with git add and run npm run commit to git an interactive prompt for creating a commit message that follows our standards. you’ll notice that there are git hooks in place which will run testing, linting, etc. (unless you commit with –no-verify)
  8. Push you changes to your fork with git push
  9. Create a pull request
  10. Iterate on the solution
  11. Get merge!

合并 多个提交

Rebase branch

git fetch upstream
git rebase upstream/master

然后 git push 到自己的远程分支 

git 一些命令

git 第一次提交报错
error: failed to push some refs to ‘git@gitlab.hegdev.com:root/heg-site-pc.git

修改当前的 project
git config user.name “myname”

修改全局
git config –global user.name “Jesse”
git config –global user.email “dfdf@sdf.com”

本地分支推送到远程

git push origin feature-branch:feature-branch

设置分支 upstream

git branch –set-upstream-to=upstream/master master

设置 push upstream

git push –set-upstream origin some-branch or git push -u origin some-branch

git fetch origin master
git log -p master origin/master
git merge origin/master

拉取单个分支并用别名

git fetch origin master:tmp
git diff tmp
git merge tmp

git rebase

Rebase 的一种使用场景:分支合并

1.我们先从 master 分支切出一个 dev 分支,进行开发:
git:(master) git checkout -b feature1
2.这时候,你的同事完成了一次 hotfix ,并合并入了 master 分支,此时 master 已经领先于你的 feature1 分支了:
3.恰巧,我们想要同步 master 分支的改动,首先想到了 merge ,执行:
git:(feature1) git merge master

4. git:(feature1) git log

就会在记录里发现一些 merge 的信息,但是我们觉得这样污染了 commit 记录,想要保持一份干净的 commit ,怎么办呢?这时候, git rebase 就派上用场了。

5.使用 rebase 后来看看结果:
git:(feature1) git rebase master
首先, git 会把 feature1 分支里面的每个 commit 取消掉;

其次,把上面的操作临时保存成 patch 文件,存在 .git/rebase 目录下;

然后,把 feature1 分支更新到最新的 master 分支;

最后,把上面保存的 patch 文件应用到 feature1 分支上;

从 commit 记录我们可以看出来, feature1 分支是基于 hotfix 合并后的 master ,自然而然的成为了最领先的分支,而且没有 merge 的 commit 记录,是不是感觉很舒服了。

6.在 rebase 的过程中,也许会出现冲突 conflict 。在这种情况, git 会停止 rebase 并会让你去解决冲突。在解决完冲突后,用 git add 命令去更新这些内容。
注意,你无需执行 git-commit,只要执行 continue
git rebase –continue

7.在任何时候,我们都可以用 –abort 参数来终止 rebase 的行动,并且分支会回到 rebase 开始前的状态。
git rebase —abort

合并多次提交纪录

1. 设想一下,你要做 code review ,结果一个很小的功能,提交了 60 多次,会不会有一些崩溃?
2. 会造成分支污染
你的项目充满了无用的 commit 纪录,如果有一天线上出现了紧急问题,你需要回滚代码,却发现海量的 commit 需要一条条来看。

3. 基于上面所说问题,我们不难想到:每一次功能开发, 对多个 commit 进行合并处理。

  • 1.我们来合并最近的 4 次提交纪录,执行:
    • git rebase -i HEAD~4
    • git rebase -i ad90262ee7c94a6ade58ac7fd96ab // 合并此hash 之前的 记录
  • 2.这时候,会自动进入 vi 编辑模式:
    • 按照如上命令来修改你的提交纪录:
    • 一般第一用pick, 其余用squash
  • 3.如果保存的时候,你碰到了这个错误:
    • error: cannot ‘squash’ without a previous commit
    • 注意不要合并已push的东西,也就是已经提交远程分支的纪录。
  • 4.如果你异常退出了 vi 窗口,不要紧张:
    • git rebase –edit-todo
    • 这时候会一直处在这个编辑的模式里,我们可以回去继续编辑,修改完保存一下:
    • git rebase –continue
  • 5.查看结果 git log
    • 三次提交合并成了一次,减少了无用的提交信息。
  • 6 pull request 中出现多次提交,可以先合并然后强推 git push -f

git rebase 与merge 区别

rebase 会把你当前分支的 commit 放到公共分支的最后面,所以叫变基。就好像你从公共分支又重新拉出来这个分支一样。

merge 会把公共分支和你当前的commit 合并在一起,形成一个新的 commit 提交

注意:

  • 不要在公共分支使用rebase
    • 主分支上用rebase 会使主分支的提交被篡改,因为rebase 其他分支提交插进来 ,主分支之前的提交就放到了后面
  • 本地和远端对应同一条分支,优先使用rebase,而不是merge

git reset

- A - B - C (HEAD, master)

git reset B
- A - B (HEAD, master)

如果git checkout B
– A – B (HEAD) – C (master)

记住git reset不会产生commits,它仅仅更新一个branch(branch本身就是一个指向一个commit的指针)指向另外一个commit(Head和branch Tip同时移动保持一致).其他的仅剩对于index和work tree(working directory)有什么影响。git checkout xxxCommit则只影响HEAD,如果xxxCommit和一个branch tip是一致的话,则HEAD和branch相匹配,如果xxxCommit并不和任何branch tip相一致,则git进入detached HEAD 状态

git reset –hard // HEAD Index working copy 都恢复
git reset –soft // HEAD 指针变化 ,Index 和 working copy 不变
git reset –mixed // HEAD Index 指针变化, working copy 不变

git reset 别人的上的分支不会自动回退,需要手动处理

git revert 命令意思是撤销某次提交。

它会产生一个新的提交,虽然代码回退了,但是版本依然是向前的,所以,当你用 revert 回退之后,所有人 pull 之后,他们的代码也自动的回退了。

git revert HEAD                     //撤销最近一次提交
git revert HEAD~1                //撤销上上次的提交,注意:数字从0开始
git revert 0ffaacc                  //撤销0ffaacc这次提交

git revert 命令的好处就是不会丢掉别人的提交,即使你撤销后覆盖了别人的提交,他更新代码后,可以在本地用 reset 向前回滚,找到自己的代码,然后拉一下分支,再回来合并上去就可以找回被你覆盖的提交了。

如果错的太远,revert 又要解决大面积冲突,可以从错误提交的前一次拉取一份代码放到其他目录,然后将 master 的代码全部删除,把那份新的代码放上去,然后提交。

git reset 高级用法

1. git reset file.txt 就相当于 git reset –mixed HEAD file.txt

所以它本质上只是将 file.txt 从 HEAD 复制到索引中。 也就是 取消暂存文件 的效果

2. git reset id — file or folder, 和第一种一样, 恢复指定的 commit 到 暂存区, 而HEAD不移动, 这种方法可以用于 对比 两个阶段间 的所有修改

就是把工作目录中的文件恢复到 v1 版本,运行 git add 添加它, 然后再将它恢复到 v3 版本(只是不用真的过一遍这些步骤)

还有一点同 git add 一样,就是 reset 命令也可以接受一个 --patch 选项来一块一块地取消暂存的内容。 这样你就可以根据选择来取消暂存或恢复内容了

3. 历史多个提交压缩单个提交

假设你有一个项目,第一次提交中有一个文件,第二次提交增加了一个新的文件并修改了第一个文件,第三次提交再次修改了第一个文件。 由于第二次提交是一个未完成的工作,因此你想要压缩它

reset squash r1

git reset –soft HEAD~2 后就变成下面的状态

reset squash r2

然后再次 提交 git commit, 中间提交就会消失

reset 会移动 HEAD 分支的指向,而 checkout 则移动 HEAD 自身