# Git

# 团队中使用git的操作说明

# 说明使用git的流程, 以及相关问题

# 给出使用流程图

# 示意图

# git操作示意图

git_opr.png

# git命令数据流转图

git_data_transport_cmd.png

# 日常操作【配置、忽略】

# DNS问题导致无法访问GitHub(1)

因为dns问题无法访问GitHub时,使用git会出现提示: Could not resolve hostname github.com: Temporary failure in name resolution 查看dns配置,配置新的nameserver即可。

# 检查是否能ping通github.com
$ ping github.com
# ping不通,查看dns配置
$ sudo vim /etc/resolv.conf
# 增加nameserver配置=>resolv.conf
nameserver 8.8.8.8
nameserver 8.8.4.4
# 重启网络配置(以下为raspbian系统重启命令,各os不一样)
$ sudo service networking restart
# final:重启后用ping github.com检查

# DNS问题导致无法访问GitHub(2)

执行git push或其他git命令时经常出现以下错误,也是dns问题导致。

kex_exchange_identification: Connection closed by remote host Connection closed by UNKNOWN port 65535 fatal: Could not read from remote repository.

# windows上一般刷新dns缓存即可解决
# cmd或powershell中输入以下命令即可
$ ipconfig /flushdns
# linux中直接重启网络配置即可(以下为raspbian系统重启命令,各os不一样)
$ sudo service networking restart

# 远程仓库配置ssh key

  1. 生成
ssh-keygen -t rsa -C "平台注册邮箱号"
  1. 三次回车即可便生成了

  2. 查看公钥

cat ~/.ssh/id_rsa.pub
  1. 将公钥添加到对应平台上

  2. 测试是否成功

ssh -T git@github.com

# 修改已经提交的author信息

使用rebase命令

//此处n代表重塑(类似重新提交)前几个版本
git rebase -i HEAD~n
//但是上一步时, 如果工作区存在未提交的内容则可以使用git stash暂存工作区内容
git stash
//取出stash内容
git stash pop
//rebase后会出现一个文件, 用于修改git接下来进行的操作, 这里可以修改成pick,edit等方式, 修改完后保存确定.
pick表示会直接commit
edit表示要修改这次提交
//完成修改author的任务.
git commit --amend --author "xw <aa.hbl@gmail.com>"
//完成修改操作文件后, 使用continue让git继续操作
git rebase --continue

# 修改name/email等config信息

//查看git全局配置信息
git config --list
//设置指定git配置信息
git config --global user.name ""
git config --global user.email ""

# 生成新的ssh key

//会覆盖之前的key, 也可以使用指定生成key名, 来覆盖.
$ cd ~/.ssh/
$ ssh-keygen -t rsa -C "xxxx@email.com"

# 配置github多账号使用

//生成key, 这里-f是指定生成key的路径, 如果不指定也想给生成文件取名(不使用默认名称), 可以在此命令后, 第一个提示进行输入.
$ ssh-keygen -t rsa -C "xxxx@email.com" -f .ssh/id_rsa_xxx
//将key添加到ssh agent上, 因为不是使用默认的方式, 所以需要使用让agent来确定使用哪一个key
$ ssh-add .ssh/id_rsa_xxx
//如果上一步添加失败, 报错Could not open a connection to your authentication agent, 则执行如下命令
$ ssh-agent bash
//创建config文件, 如果存在则跳过此步骤
$ touch config
//编辑config文件, 输入内容如下, 指定哪个host使用哪个公钥
		# 默认的 github 用户
		Host github.com
		port 22​
		 HostName github.com
		 User git
		 IdentityFile ~/.ssh/id_rsa
		 
		# 使用 id_rsa_2 验证的 github 别名
		Host blog.github
		port 22​​
		 HostName github.com
		 User git
		 IdentityFile ~/.ssh/id_rsa_blog_github
//测试是否成功
$ ssh -T blog.github

# 使用ssh连接替换https连接

  1. 先查看远程仓库url
git remote -v
  1. 删除远程连接
git remote rm origin
git remote remove origin
  1. 添加远程ssh连接
git remote add origin SSH地址
  1. 要完成最后一步必须得本地生成公钥添加到对应平台上去.

  2. 与本地分支进行关联并提交内容到远程仓库

git push --set-upstream origin 本地分支名

# git pull时出现unable to get local issuer certificate报错

使用命令关闭ssl验证
git config --global http.sslVerify false

# gitk/git gui中文乱码问题

git config --global gui.encoding utf-8

# 忽略文件

  1. 将path写入到gitignore中

  2. 本地建一个私有的gitignore

//使用如下命令配置 git config --global core.excludesfile {path}

  1. 直接将本地要忽略的文件添加到.git/info/exclude中

  2. 针对已跟踪的文件, 使用--assume-unchanged来忽略

//忽略 git update-index --assume-unchanged {path} //恢复 git update-index --no-assume-unchanged {path}

  1. 如果某些文件需要被提交到仓库中以供使用, 但每个人都可能会有不同的修改, 个人修改不能提交到仓库中.

将需要提交的文件加上后缀.bak/.tamplate等, 然后个人pull下来后修改去掉这个后缀使用, 并且.gitignore中添加使用时的文件path

下文参考来源:https://blog.csdn.net/DaSunWarman/article/details/79384307

git update-index –assume-unchanged –path 可以忽略文件 git update-index –no-assume-unchanged –path 可以取消忽略文件 但是忽略的文件多了,想找出所有被忽略的文件,暂时找到下面的办法,

git ls-files -v | grep '^h\ ' 提取文件路径,方法如下

git ls-files -v | grep '^h\ ' | awk '{print $2}' 所有被忽略的文件,取消忽略的方法,如下

git ls-files -v | grep '^h' | awk '{print $2}' |xargs git update-index --no-assume-unchanged

# 从远程clone时指定文件夹名(不用项目名)

git clone <Repo> <指定文件夹名>

# 日常操作【查看】

# 查看某个文件的详细修改记录

$ git log -p <files>

# git status说明

  • 处于暂存区的文件状态::staged(已暂存);处于工作区的文件状态::untrack(未跟踪)、modified(已修改)
  • 工作区中的空目录不会被git追踪

# 优化git log输出样式

由于git log输出的内容比较乱, 特别是当提交信息比较长的时候, 整个显示效果就大打折扣了, 因此需要使用--pretty=format优化输出格式

# 直接使用
$ git log --pretty=format:'%Cblue%h%Creset %<(40,trunc)%s [%C(green)%<(21,trunc)%ai%x08%x08%Creset %Cred%an%Creset%C(yellow)%d%Creset]'

# 将其设置成alias
$ git config alias.l "log --pretty=format:'%Cblue%h%Creset %<(40,trunc)%s [%C(green)%<(21,trunc)%ai%x08%x08%Creset %Cred%an%Creset%C(yellow)%d%Creset]'"

# 日常操作【分支、拉取、更新】

# 远程和本地都新建了一个仓库, 但两个仓库都进行了初始化操作, 此时无法同步两个仓库

此时相当于两个独立的仓库, 本地仓库进行过提交, 远程仓库也初始化了(gitee中勾选了初始化)

这种情况下, 本地执行git push没有用, 会提示你要使用git push --set-upstream origin master, 但是此时会 告诉你远程库的内容与你的不一样, 其实是判断commit数, 这时会提示你要git pull试试, 结果还是各种错误提示.

此时其实需要将两个仓库关联起来, 利用pull合并成一个仓库

//忽略历史进行拉取关联
git pull origin master --allow-unrelated-histories

之后就可以正常操作了, 提交到远程仓库中

git push --set-upstream origin master

# 更新远程仓库的单个文件/指定文件

先更新本地的远程仓库origin/master, 然后从本地origin拉取指定文件回来(git checkout origin/master -- filename)

# 查看所有的分支, 包括远程分支

  • a是查看所有分支, 包含远程分支
  • v给予简单说明, 能看到版本号和commit msg
git branch -av

# 拉取指定的远程分支

 git fetch origin master

# 合并指定的远程分支

 git merge origin/master

# 切换其他远程分支,拉取远程没有的分支

//此处dev是指本地创建的分支, origin/dev是指远程分支.
$ git checkout -b dev origin/dev

# 新建分支的几种方式

  • 此方式不会切换到分支上, 是从当前分支复制出一个分支 git branch 分支名
  • 创建并切换到某分支上 git checkout -b 分支名
  • 不存在则创建并切换到某分支上 git checkout -B 分支名

# 修改分支名

  1. 本地修改分支名
git branch -m oldName newName
  1. 如果修改远程分支, 则删除远程分支, 再修改本地分支重推上远程

# 删除分支

  • 所有删除分支的操作都必须切换到其他分支上进行 git branch -d 分支名
  • 强制删除 git branch -D 分支名

# git批量删除本地分支

要将对应要排除的分支都加在条件中, 或者不用grep -v反选.

git branch -a | grep -E -v 'master|V2|remotes' | xargs git branch -d

# 日常操作【提交、推送】

# push -u 的用法

//将当前分支推到origin上
$ git push origin
//如果当前分支与多个主机有关联, 则需要-u来指定, 指定之后就可以只使用: git push了.
$ git push -u origin master

# git add -u/git add -A

//提交所有的变化, 不包括删除文件
git add .
//提交所有被删除和修改的文件
git add -u == git add --update
//提交所有的文件, 包括删除, 是上面两者的集合
git add -A == git add --all

git version 2.x: image.png

# 有本地分支, 没有远程分支, 将本地推到远程上

//此时已经切换到local_branch上
git push origin local_branch:remote_branch

# 推送项目到新建的gitlab上

//初始化本地项目为git项目
$ git init
//将本地项目进行本地提交
//添加远程分支关联
$ git remote add origin ssh:xxxxx.git
//如果添加有误, 移除远程分支关联
$ git remote remove origin
//push代码到remote上
$ git push -u origin --all
//push所有标签到remote上
$ git push -u origin --tags

# 项目提交远程仓库, 但历史记录包含敏感信息或需要清空历史记录

  1. 如果直接提交远程仓库, 则会将历史记录一并提交

  2. 新建一个独立的分支并检出

//这个分支是干净的, 并且没有提交过任何东西的
git checkout --orphan newbranch
  1. 将工作区的代码提交到新的分支中
git add .    |  git add -A
git commit -m 'new commit'
  1. 删除原来的分支
//推荐使用-D强制删除, 使用-d的话, 如果原来的分支有未合并或者其他情况就可能无法删除
git branch -D dev
  1. 重命名当前分支
git branch -m newName
  1. 提交到远程仓库中就好了

# 提交合并到历史(非上一次)commit中

  1. git rebase -i xxx_id

  2. 找到需要更改的commit, 将行首的pick改成edit

  3. 更改文件(或者从stash中pop)

  4. 使用git add 改动的文件添加改动文件到暂存

  5. 使用git commit –amend追加改动到第一步中指定的commit上

  6. 使用git rebase –continue移动HEAD到最新的commit处

参考: https://blog.csdn.net/Nathan1987_/article/details/81675750

# 提交合并到上一次commit中

  1. 做好更改的内容
  2. git add .
  3. git commit --amend

# git将多个commit合为一个

  1. 使用rebase, commitId为想变基的前一个commitId

git rebase -i [commitId]

  1. 使用squash

  2. 如果提交到remote后, 不建议使用此命令

# git作为镜像仓库/一个项目推送到多个远程仓库

当有需要将一个项目推送到多个远程仓库, 则可以使用镜像仓库来解决. https://blog.csdn.net/hwangfantasy/article/details/77006016

# 日常操作【暂存】

# 查看stash中的内容

git stash show

# stash的相关操作

//存放内容到stash中
$ git stash
//取出stash中的内容
$ git stash pop
//暂存未跟踪或忽略的文件, 使用-u或者--include-untracked可以stash untracked文件
//使用-a或者--all命令可以stash当前目录下的所有修改
//将stash应用到当前工作目录, 但并不清除stash中内容
$ git stash apply
//清除所有stash
$ git stash clear
//清除指定的stash, xxx表示指定的stash, 可以通过list查看
$ git stash drop xxx
//查看所有的stash
$ git stash list
//保存时添加备注
$ git stash save "完成user接口"

# stash使用技巧

add 那些你不想备份的文件(例如: git add file1.js, file2.js) 调用 git stash –keep-index。只会备份那些没有被add的文件。 调用 git reset 取消已经add的文件的备份,继续自己的工作。

//暂存 未add文件/untrack文件/, -u 表示暂存untrack.
$ git stash save '备注' --keep-index -u

# git stash部分文件

只能add部分文件后, 再使用git stash -k(keep index) 来暂存工作区中的内容

或者使用git stash -p,这里会出现交互式操作, 针对每个改动按照如下操作指定

y - stage this hunk
   n - do not stage this hunk
   q - quit; do not stage this hunk nor any of the remaining ones
   a - stage this hunk and all later hunks in the file
   d - do not stage this hunk nor any of the later hunks in the file
   g - select a hunk to go to
   / - search for a hunk matching the given regex
   j - leave this hunk undecided, see next undecided hunk
   J - leave this hunk undecided, see next hunk
   k - leave this hunk undecided, see previous undecided hunk
   K - leave this hunk undecided, see previous hunk
   s - split the current hunk into smaller hunks
   e - manually edit the current hunk
   ? - print help

# 日常操作【撤销、回退】

# 撤销工作区所有的修改

//不会影响untracked file
$ git checkout .

# 恢复工作区删除的文件

工作区就是所有当前操作/浏览的文件的地方, 未add的文件只会存在于工作区.

//指定文件
$ git checkout -- <file>
//所有文件
$ git ls-files -d | xargs -i git checkout {}
//或者
$ git ls-files -d | xargs git checkout --

# 工作区做了修改, 但没有提交到暂存区中, 此时要撤销工作区的修改

//可以先使用git status查看状态, 此时会看到你修改的文件, 同时也会告诉你可以进行的操作, 这里就有checkout
//使用checkout清空工作区的改动
git checkout -- fileName

# 恢复暂存区删除的文件

暂存区里变动的文件, 就是只经过add, 没有commit的文件.

//HEAD表示当前指针指向的本地仓库记录
$ git reset HEAD <file>

# 恢复工作区所有被修改的文件

//checkout只适用于未提交到暂存区的文件
$ git ls-files -m | xargs git checkout --

# 删除git关联(本地代码)

//删除.git文件后, 目录下使用git命令还是有效, 证明能识别此处还是仓库目录, 只能下一步了.
//一步到位
$ find . -name ".git" | xargs rm -Rf

# 清除untracked files

git clean -f

// 连untracked目录也删掉
git clean -df

# 撤回add到暂存区中的文件

-r代表文件夹

git rm --cached -r filename

# git中的概念

表示活跃分支的游标, 你现在在哪儿, 它就指向哪儿.

# FETCH_HEAD

表示一个版本链接, 记录在本地文件中, 指向目前从远程仓库中取下来分支的末端(最新)版本.

# origin/HEAD -> origin/master

HEAD代表一个头指针, 指向的分支代表此分支为默认分支. 也可以删除它: git remote set-head origin -d

# 超前ahead, 落后behind

commit了代码, 但是没有push, 就是ahead. remote上有更新, 但是没有pull, 就是behind.

# staged, unstaged

staged表示暂存的文件 unstaged表示未暂存的文件

# 配置git bash的ssh代理,解决连接问题(https方式也同样提供解决方案)

# 配置ssh方式代理

  1. ~/.ssh目录下建一个config文件,在其中编辑如下内容:
Host github.com
    User git
    IdentityFile "C:\Users\jesse\.ssh\id_rsa"
    ProxyCommand connect.exe -H 127.0.0.1:10809 %h %p
  1. 下载connect.exe到config目录下。下载地址 (opens new window)
  2. 再使用ssh -T github.com命令测试,如果本地做了10809端口代理,就会走代理,此时速度就起飞了。

# 设置https方式代理

在git bash中输入设置即可

git config --global http.proxy "http://127.0.0.1:10809"
git config --global https.proxy "https://127.0.0.1:10809"

# 合理使用cherry-pick和rebase组合拳,谨慎使用revert

如果碰到紧急使用revert的情形,将容易导致此commit再也合并不上来了,因此最好谨慎使用revert命令, 建议使用reset+cherry-pick+rebase。

  1. 如果使用过revert命令,就先用reset回退到merge之前。
  2. cherry-pick需要的那几个commit
  3. rebase最先的commit(前开后闭区间)
  4. 比较diff比较两个分支的代码差异:git diff origin/master (比较当前分支与origin/master区别)

# cherry-pick的使用

指定一个commit:git cherry-pick {commitId} 指定几个commit区间: git cherry-pick {id1...id5} (前开后闭区间,不包括id1) git cherry-pick {id1^..id5} (闭区间,包括id1)

# rebase的使用:

指定一个commit:git rebase -i {commitId} (不包括此commitId,重塑此commitId之后的提交) -i表示开启交互模式,commit是倒序排列,最新的显示在最下面,使用squash来将commit合并到前一个commit上。

# 配置命令简写

  1. 找到git安装目录

  2. 进入mingw64/etc

  3. vim gitconfig

pl = pull
ps = push
l = log --stat
c = commit -am
ca = commit --amend
  1. wq

# 为什么merge时都建议加-no-ff

关键要理解ff(fast-forward)的意思

ff表示快进式合并, git在合并分支时如果发现可以顺着其中一个分支走下去的话, 就会简单的将HEAD指针右移, 完成快进式合并(ff), 这样的合并, 不会产生一次commit.

-no-ff表示强行关闭快进式合并

# fast-forward

          A---B---C feature
         /
D---E---F master
===============↓↓↓================
          A---B---C feature
         /         master
D---E---F 

# -no-ff

          A---B---C feature
         /
D---E---F master
===============↓↓↓================
          A---B---C feature
         /         \
D---E---F-----------G master

# revert之后你应该这样做

# 一般什么时候你使用revert?

一般我用到revert都是master分支代码需要回滚某个功能分支的commit时使用.

大家有其他用到的场景分享一下~

# revert之后如何处理

一般在紧急revert之后, 修复完此commit代码问题, 后续还要重新应用此次提交

这时有两个方法

# 方法一: revert上次的revert

因为revert实际上是向前做了一次commit, 因此可以revert那次revert commit

# 方法二: chrrey-pick之前revert的那次提交重新提交

# # 如何查询所有的config设置

$ git config [--global] --list

# config的使用

# 如何筛选查询config设置

$ git config --get-regexp alias

# 如何设置config

$ git config [--global] alias.l 'log --oneline --graph'

# 删除config的设置

$ git config [--global] --unset alias.l

# 直接编辑config配置

# 下面命令直接打开config配置文件进行编辑, 修改后直接保存即可
$ git config -e
修改于: 8/11/2022, 3:17:56 PM