Git

280 阅读10分钟

git简介

Git为分散性版本管理系统,为版本管理而设计的。

版本系统分为:

  • 分散性 git。说个仓库,不用远程连接仓库就可进行开发。所有仓库之间都可push或pull。
  • 集中性 svn,将仓库几种放在一个服务器中。便于管理,但一旦开发者所处环境不能连接服务器,就获取不到代码。

仓库

  • workspace 工作区
    git add .到 stage
  • Index/Stage 暂存区
    git commit 到Repository
  • Repository 仓库区(本地仓库)
    git push 到remote
  • Remote 远程仓库

git使用

git初始设置

  • 设置姓名和邮箱地址

    git config --global user.name ""
    git config --global user.email ""
    

    输入 ~/.gitconfig可查看[user]配置
    git config --list 查看配置项

  • 提高命令输出的可读性 git config --global color.ui auto

  • 设置 SSH Key

    ssh-keygen -t -C "邮箱"  
    

    一直enter,就会设置成功

    id_rsa文件是私有密钥

    id_rsa.pub是公开密钥

    ssh-keygen -C "邮箱" -f 自定义秘钥名称(github_hub)

  • Giuhub添加公开密钥

    Account Settings 选择SSH Keys。-> Add SSH Key

    控制台查看公钥内容 cat ~/.ssh/id_rsa.pub

完成设置后,就用私有密钥与GitHub进行认证通信了

ssh -T git@github.com 查看是否通讯成功

  • 在Github中new repository(新建 仓库)

    • description 描述可选

    • pubilc/private

    • add .gitignore

      把不需要在git仓库中进行版本管理的文件记录在.gitignore中。

    • add a license

      许可协议文件。

git基本操作

  • git init 初始化仓库
    生成的.git目标存储这当前目录内容所需的仓库数据
  • git status 查看仓库状态
  • git add filename 向暂存区中添加文件
  • git commit 保存仓库的历史记录
    • git commit -m "first commit" -m后接提交信息
  • git log 查看提交日志
    • git log --pretty=short 只显示提交信息的第一行
    • git log 目录名/文件名 只显示该目录、文件的日志
    • git log -p filename 查看文件的改动
    • git log --graph 以图表形式查看分支
    • git reflog 查看当前仓库的操作日志
      git log 只会查看以当前状态为中的的历史日志(即用git reset恢复以前的看不到)
  • git diff 查看更改前后的差别
    可查看工作树、暂存区、最新提交之间的差别

分支的操作

  • git branch 显示分支一览表
    -a 查看所有远程分支和本地分支
    -r 查看所有远程分支
    远程分支和本地分支有什么区别

    • git branch -d 删除本地branchname
    • git branch -m brandcholdname branchewname 重命名分支
    • git branch 创建branchname分支
  • git checkout -b 创建、切换分支

    • git checkout -b feature-A
      等价于
      git branch feature-A
      git checkout feature-A
    • git checkout master 切换到master分支
    • git checkout - 切换会上一个分支
  • 开发分支(feature)
    在开发过程中,常会创建多个特性分支(实现不同的功能),保留一个随时可发布软件的稳定分支(release)。 当特性分支完成后,再与master分支合并。

  • 发布分支(release)
    稳定分支,可随时供人查看。
    Tag创建版本信息,同时管理多个版本。拥有多个版本分布时,发布分支也有多个。

  • 主干分支(master)
    当release分支上线后,当前分支打tag、封板,合并到master。

  • git merge 合并分支
    把feature-A分支合并到master
    git checkout master
    git merge --no--ff feature-A\

更改提交的操作

git的一大特性:可灵活操作历史版本。在不影响其他仓库的前提下对历史版本进行操作。

git跟踪的是管理的修改而非文件。

  • git reset 回溯历史版本

    git reset --hard 版本号id(git log查看) 可恢复到该时间点的状态

    HEAD指向的版本就是当前版本

    git reset --hard HEAD^ 回溯到上一个版本

    ^^^每个都是返回上一个

  • 撤销修改

    • 若改了工作区的内容,就丢弃工作区的修改 git checkout --文件名

    • 不但该了工作区,还添加到了暂存区,向丢弃修改。分两步

      第一步git reset HEAD 文件名 回到场景一

      第二步git checkout --文件名

    • 若提交到了版本库修改,就用版本回退。前提没有推送到远程库

  • 删除文件

    从版本库中删除文件

    git rm test.txt

    git commit -m "remove test.txt"

    git checkout是用版本库里的版本替换工作区的版本,无论工作区是修改还是删除,都可以一键还原。

    git checkout test.txt

    如果一个文件已经别提交到版本库,就永远不用担心误删,但要小心,只能恢复文件到最新版本,会丢失最近一次提交后你修改的内容,

  • 消除冲突

    比如在合并fix-B时发送了冲突(Conflict)。打开README.md文件可看到。

    <<<<<<< HEAD
     - feature-A
    ======
     - fix-B
    >>>>>>> fix-B
    

    fix-B与feature-A冲突了。修改冲突部分或删除之一

    • 提交解决后的结果

      git add filename
      git commit -m "fix confilct"
      
    • git commit --amend 撤销上一次提交,并将暂存区文件重新提交

  • git rebase -i 压缩历史

推送远程操作

  • git remote add 添加远程仓库

  • git push 推送至远程仓库

    git push -u origin master

    git pish -u origin feature-D 将本地feature-D分支推送到GitHun的feature-D分支

从远程仓库获取

不在同一个目录进入,表示另外一名新开发者共同开发。

  • git clone 地址

    会默认处于master分支下,同时系统会自动将origin设置成该远程仓库的标识符。

    • 获取远程的feature-D到本地仓库

      git checkout -b feature-D origin/feature-D

      -b 后是本地仓库新建分支的名称。

      origin/feature-D 以名origin的(github)仓库的feature-D分支为来源,在本地仓库中创建feature-D分支

    • 向本地feature-D分支提交修改

      git commit -am "add feature-D"

      git push 推送feature-D分支

  • git pull 获取最新的远程仓库分支

    git pull origin feature-D 将本地的feature-D分支更新到最新状态。

    • git pull origin -d 删除远程branchname分支

合并分支

假设初始分支状态:\

  • main 分支: A->B->C(最新提交C)

  • feature分支:基于B开发,D->E(专属提交D、E)

  • git merge main feature(合并后)

image.png

  • git rebase main feature (变基后)

image.png

  • git merge(适合团队协作) 需要一次性解决所有冲突,哈希值不变,git rebase(适合个人分支) 逐个解决提交的冲突,哈希值会变

git rebase

  • git rebase -i commitId 将当前commit都合并到commitid一条上

    • pick 保留
    • squach 用新的commit,且合并到上一条
    • fixup 用上一条commit,且合并到上一条
    • drop
  • git rebase -i HEAD~3 合并前三次提交

  • git rebase --continue rebase过程中,解决冲突后,执行

  • git rebase --abort 中止:会回到rebase开始前的状态

  • git push --force-with-lease 若远端有更改推送是会被提示的

  • git rebase --onto 目标commit 起始commit 结束commit/分支 想要把指定commit移植到哪个commit之后

  • rebase 后只能用git push -f 强推,但不安全, 对于远端有更改是无感的

  • git rebase main 在当前个人分支rebase mian,拉取最新的main代码,保持自己分支的整洁。

remote 远程

  • git remote add origin(别名) git地址
  • git remote set-url 别名 新的远程地址
  • git remote -v :查看别名和地址
  • git remote :查看别名
  • git remote rename 旧别名 新别名 :重命名
  • git remote rm 别名:删除别名

push

  • git push
  • git push <远程别名> <分支名> :推送到指定远程的同名分支
  • git push <远程别名> <本地分支>:<远程分支> : 当本地和远程分支名不同时
  • git push --set-upstream origin main 关联本地分支到远程分支(首次推送):缩写-u
  • git push --force-with-lease 安全强制推送,仅远程未被修改时覆盖
  • git push --force origin feature (简写 -f) :强推,推之前一定要pull
  • git push --dry-run origin main (简写 -n): 模拟推送(仅检查,不实际推送)
  • git push --delete origin feature (简写 -d) 删除远程分支/标签
  • git push --tags origin 推送本地所有标签到远程
  • git push --verbose origin main (简写-v) 显示详细推送日志

标签

  • git tag 列出所有tag
  • git tag [tag] 在当前commit新建一个tag
  • git tag [tag] [commit] 新建一个tag在指定commit
  • git tag -d [tag] 删除本地tag
  • git push origin :refs/tags/[tagName] 删除tag
  • git show [tag] 查看tag信息
  • git push [remote] [tag] 提交指定tag
  • git push [remote] --tags 提交所有tag
  • git checkout -b [branch] [tag] 新建一个分支,指向某个tag

pull 和 fetch

pull = fetch + pull

fetch 后,用rebase线性化历史

git fetch origin main
git rebase origin/main // 替代merge,线性合并远程代码

最好使用fetch, pull用于只有自己开发的这条分分支\

git pull 和 git pull --rebase 区别
  • 假设main分支有A、B两个提交
  • 本地基于当前切换出dev分支,新增C、D两个本地提交(还没推到远程)
  • 此时远程main分支增加了E提交,远程是A、B、E,本地是A、C、D
场景一:使用git pull(默认merge合并)

image.png
冲突 \

  • 解决冲突后 git add .
  • git commit -m "merge: 解决冲突,合并远程代码"
  • 特点:只需要解决一次冲突,合并冲突多的场景
场景二:git pull --rebase (变基合并)

相当于 先 暂存本地提交CD,把本地分支挪到远程最新提交E的后面,在把暂存的C、D重新应用到E之后,全程无多余提交

image.png
冲突 \

  • git add . // 解决冲突后,添加修改的文件

  • git rebase --continue // 继续处理下一个提交的冲突(若有)

  • 若想放弃变基,执行 git rebase --abort

  • 特点:需逐个提交解决冲突(比如本地有3个本地提交,可能需要解决3次冲突),但解决后记录更整洁

操作提交记录形态核心行为优点缺点
git pull(merge)分叉状保留双方提交历史,生成合并提交操作简单,不修改原有提交提交记录混乱,无多意义合并提交
git pull --rebase线性把本地提交[重放]到远程最新提交之后,无合并提交记录整洁,易追溯若有冲突,需逐个解决,比merge稍繁琐,但最终记录最优
  • rebase 重新定基,把最新的提交重新定在远程最新提交的基础上,所以记录是直的

实践问题

修改githuub的commit(版本回退)

  • git pull
  • git log ->找到版本号id
  • git reset --hard 版本号id
  • git push -f origin master

直接覆盖

如果直接git push 之前没有git pull 会直接覆盖掉项目,原先的都不存在了。 这种情况git push -u 会报错。 虽然可用git push -f 强制覆盖,但要谨慎使用. git push以前,要先git pull才不会报错

  • 或者可用 git push --force-with-lease 如果远程有更改会提示需要fetch的

commit相关

  • git cherry-pick 合并某条commit到本分支
  • 多条commit合并成一个
    git add .
    git commit --amend
    :wq 保存
    或者Esc编辑commit信息
    
  • git push --force-with-lease

push规范

用于说明commit的类别

  • br: 针对bug号,用于向测试反馈bug列表的bug修改情况
  • feat: 新功能(feature)
  • fix: 修复bug
  • docs: 文档(documentation)
  • style: 格式(不影响代码运行的变动)
  • refactor: 重构
  • test: 增加测试
  • chore: 构建过程或辅助工具的变动

  • 版本库
    git add->stage->git commit ->提交到当前分支
  • 暂存区(stage),HEAD:指向master的指针
  • git status 查看暂存区
  • git checkout -- filename
    丢弃对fliename的修改
    git checkout其实是用版本库里的版本替换工作区的版本
    从来没有被添加到版本库就删除的文件,是无法恢复的。
  • rm filename 把工作区的file删除
  • git rm filename 把版本库的file删除
    用git ckeckout -- filename把误删的恢复到最新版本
  • git push --set-upstream origin 分支名
    将本地分支和远程分支绑定。

参考《GitHub入门与实践》 阮一峰