‹ 返回笔记 Esc·

Git 问题与修复

git worktree Git worktree 是 Git 提供的一种在同一个仓库中同时维护多个工作目录(Working Tree)的机制。它的核心目标是:让你在不同分支上并行工作,而不需要频繁切…

git worktree

Git worktree 是 Git 提供的一种**在同一个仓库中同时维护多个工作目录(Working Tree)**的机制。它的核心目标是:让你在不同分支上并行工作,而不需要频繁切换分支或重复克隆仓库

它解决了什么问题?

  1. 频繁切换分支的痛点

传统方式:

1git checkout main
2# 改代码
3git checkout feature-x
4# 改代码

问题:

  • 容易丢失未提交的修改
  • 上下文频繁切换,效率低
  1. 多任务并行开发困难

例如:

  • 一个分支修 Bug
  • 另一个分支开发新功能
  • 还要切回主分支测试

worktree 可以让每个任务拥有独立目录

一个分支只能绑定一个 worktree,同一分支,在同一时间,只能被一个 worktree 使用


git clone 免密登录 Gitee

使用 Git 的凭据帮助器(credential helper)来实现。 git config --global credential.helper store 执行一次需要用户名和密码认证的 Git 操作,比如git pullgit push。之后 Git 会提示您输入用户名和密码,并且会把这些凭据保存到本地。


Git 同时推送 Gitee 和 GitLab

方法一:为 origin 添加多个 push-url

1# 添加 Gitee 和 GitLab 地址
2git remote add origin https://gitee.com/zailiang/ai-vue.git
3
4# 为 origin 添加 Gitee push 地址
5git remote set-url --add --push origin https://github.com/zailiangs/ai-vue.git
6git remote set-url --add --push origin https://gitee.com/zailiang/ai-vue.git
7
8# 验证配置
9git remote -v
10git remote show origin
11# 输出应包含:
12# origin …(fetch) gitee
13# origin …(push) gitee
14# origin …(push) gitlab
15
16# 提交一次测试
17git add .
18git commit -m "测试 Gitee + GitLab 同步"
19git push origin main

方法二:新增一个聚合 remote(例如命名为 all)

1# 添加一个新 remote all 来聚合 Gitee 和 GitLab
2git remote add all http://192.168.2.41:8880/gw/nantong-competition.git
3
4# 为 all 添加第二个 push-url(GitLab)
5git remote set-url --add --push all https://gitee.com/zailiang/nantong-competition.git
6
7# 推送时使用 all
8git push all main

首次 set-url 是覆盖 push 源

1  ai-vue git:(main)  git add .
2  ai-vue git:(main)  git commit -m "first commit"
3  ai-vue git:(main) git remote -v
4  ai-vue git:(main) git remote add origin https://gitee.com/zailiang/ai-vue.git
5origin  https://gitee.com/zailiang/ai-vue.git (fetch)
6origin  https://gitee.com/zailiang/ai-vue.git (push)
7
8  ai-vue git:(main) git remote set-url --add --push origin https://github.com/zailiangs/ai-vue.git
9  ai-vue git:(main) git remote -v
10origin  https://gitee.com/zailiang/ai-vue.git (fetch)
11origin  https://github.com/zailiangs/ai-vue.git (push)
12
13  ai-vue git:(main) git remote set-url --add --push origin https://gitee.com/zailiang/ai-vue.git
14  ai-vue git:(main) git remote -v
15origin  https://gitee.com/zailiang/ai-vue.git (fetch)
16origin  https://github.com/zailiangs/ai-vue.git (push)
17origin  https://gitee.com/zailiang/ai-vue.git (push)
  • 由于你用了 –add 标志,它不会替换已存在的设置,而是把新的 push URL 追加 到已有列表中。

为什么第一次 –add –push 看起来像是“覆盖”了 push URL?

这是因为此前并 显式设置过 push URL,而是只依赖 fetch URL 作为默认 push 地址(两者相同)。因此第一次添加行为如下:

  • 当前未存在显式 push URL,于是 Git 将 默认的 push URL(原先等同于 fetch URL)替换为你新指定的 URL。
  • 结果列表中只剩下你新指定的 push URL,看起来像“覆盖”了之前的默认。

而在第二次执行相同命令时:

  • 已存在一个显式 push URL,上次操作已经把它设为新的 GitHub 地址。因此这次再加 –add,就真正追加了另一个 push URL(即回到 Gitee),形成了两个

提交记录带有详细描述

1git commit -m "title" -m "detail1" -m "detail2"

1git commit
2# 进入编辑器 第一行是标题,提供了对变更的快速概述。后面的行是详细描述,提供了对变更的深入说明。
3修复登录页面的错误
4
51.修复了在用户输入无效密码时,页面没有反馈的问题。
62.更新了相关的验证逻辑,并添加了错误提示。
73.此更改提高了用户体验,使用户在登录时能及时获得反馈。
8# 保存

可用 git log查看提交记录


按功能或修改范围提交代码

分组更改:将相关的更改放在一起,每次只提交一组相关的更改。

逐个添加文件:使用 git add <file> 添加需要提交的特定文件,而不是使用 git add .

1# 修改了登录页面和用户注册页面
2git add src/views/Login.vue
3git commit -m "修复登录页面的样式" -m "在用户输入无效密码时,页面现在会显示错误提示。"
4
5git add src/views/Register.vue
6git commit -m "添加用户注册功能"
1# 或者每次只改动一处使用
2git commit -a -m "修改内容"

本地分支开发来合并到主分支

创建分支:每次开发新功能或修复bug时,先创建一个新的分支。这样可以将相关的提交集中在一起。

在分支上工作:在分支上进行修改后,再合并到主分支。

1# 创建新分支
2git checkout -b feature/login-page
3# 进行修改并提交
4git add src/views/Login.vue
5git commit -m "修复登录页面的样式"
6# 切换回主分支
7git checkout main
8# 合并新分支
9git merge feature/login-page
10# 推送
11git push origin main

新仓库推送代码命令

-u--set-upstream 的简写。这个选项的主要作用是将本地分支与远程分支建立追踪关系

1cd existing_folder
2git init --initial-branch=main
3git remote add origin http://192.168.2.41:8880/gw/intelligence-ai.git
4git add .
5git commit -m "Initial commit"
6git push -u origin main / git push --set-upstream origin main

本地修改提交到指定标签

1git add .
2git commit -m "Your commit message"
3git tag -a <tag_name> -m "Your message here"
4git push origin <tag_name>

再次向已存在的标签提交代码

Git 标签是指向特定提交的引用,一旦创建后,它们通常不会更改。不过,您可以通过以下步骤向已有标签(如 v1.0.1)提交代码的方式,实际上是先提交代码,然后移动标签以指向最新的提交。

1git checkout v1.0.1  # 检出标签所在的提交
2git checkout -b update-v1.0.1  # 创建一个新分支
3# 进行修改并提交
4git add .
5git commit -m "Update for v1.0.1"
6# 移动标签
7git tag -d v1.0.1  # 删除旧标签
8git tag -a v1.0.1 -m "Updated v1.0.1"  # 在最新提交上重新创建标签
9# 推送更改和标签到远程仓库
10git push origin update-v1.0.1  # 推送新分支
11git push origin --tags  # 推送标签
12# 如果推送标签已经在远程存在,则加 -f 强制推送或删除远程标签后重新推送
13git push origin --tags -f
14git push origin -d v1.0.1 & git push origin v1.0.1

本地修改导致拉代码冲突

使用代码暂存 git stash


远程主分支重命名

  1. 在本地仓库中重命名 master 分支为 main: git branch -m master main
  2. 将重命名后的分支推送到远程仓库: git push -u origin main
  3. 删除远程仓库中的旧 master 分支: git push origin --delete master
  4. 更新远程仓库的默认分支: 仓库 - 设置 - 分支 - 默认分支 - 编辑 - 选择 main 作为默认分支

初始化默认分支名称

Git 2.28(2020年7月发布)及之后的版本:Git 允许用户配置默认的分支名称,官方推荐的默认分支名称是 main

Git 2.28 之前的版本:默认的分支名称是 master

查看默认分支名称(从 Git 2.28 开始提供的配置项): 你可以使用以下命令查看当前 Git 的全局配置,看看默认分支名称设置是什么:

1git config --global init.defaultBranch

更改默认分支名称为 main: 如果你想将默认的分支名称改为 main,可以运行以下命令:

1git config --global init.defaultBranch main

手动指定分支名称: 如果你希望在初始化时指定分支名称,可以在执行 git init 时通过参数指定分支名称:

1git init --initial-branch=main

安装 Git LFS

git lfs (Git Large File Storage) 是一个 Git 扩展,用于管理和版本控制大文件。

  1. 使用包管理器安装 Git LFS
1brew install git-lfs # MacOS
2apt install git-lfs # Ubuntu
  1. 安装后初始化 Git LFS
1git lfs install
  1. 验证安装
1git lfs --version

存储凭证的不同形式

  1. git-credential-store将凭证以纯文本形式保存在本地文件中。这个文件默认位于用户主目录下的 .git-credentials文件中。
  2. git-credential-cache将凭证存储在内存中,并在指定的时间内有效。默认缓存时间为 15 分钟,可以通过配置进行调整。由于凭证存储在内存中,重启计算机后会丢失。
1git config --global credential.helper cache
2git config --global credential.helper 'cache --timeout=3600'
  1. git-credential-osxkeychain专为 macOS 设计的凭证存储工具。它将 Git 凭据存储在 macOS 的钥匙串中。
1brew install git-credential-osxkeychain
2git config --global credential.helper osxkeychain
  1. git-credential-manager一个跨平台的凭证管理工具,适用于 Windows、macOS 和 Linux。
1brew tap git/git-credential-manager
2brew install --cask git-credential-manager-core
3git config --global credential.helper manager-core

拉取时合并分歧

1hint: You have divergent branches and need to specify how to reconcile them.
2hint: You can do so by running one of the following commands sometime before
3hint: your next pull:
4hint:
5hint:   git config pull.rebase false  # merge
6hint:   git config pull.rebase true   # rebase
7hint:   git config pull.ff only       # fast-forward only
8hint:
9hint: You can replace "git config" with "git config --global" to set a default
10hint: preference for all repositories. You can also pass --rebase, --no-rebase,
11hint: or --ff-only on the command line to override the configured default per
12hint: invocation.

在使用git pull时候,Git 发现了本地分支和远程分支之间存在分歧,所以需要你指定如何将这些分支合并在一起,Git 给你提供了 3 种选项,根据自己的需求选择其中一种方式,指定一个默认行为:

1git config pull.rebase false # 合并(merge)
2git config pull.rebase true # 重基(rebase)
3git config pull.ff only # 仅快进(fast-forward only)

还可以在每次git pull时临时指定使用哪种方式,通过添加参数来覆盖默认配置:

1git pull --rebase | --no-rebase | --ff-only  # 临时使用 rebase merge fast-forward only

后面git pull命令会触发需要你输入合并的提交消息,其中第 1 行是标题,后面行都是详细描述内容

1Merge branch 'main' of https://gitee.com/zailiang/vue-project
2# Please enter a commit message to explain why this merge is necessary,
3# especially if it merges an updated upstream into a topic branch.
4# Lines starting with '#' will be ignored, and an empty message aborts
5# the commit.

本地分支落后远程分支

1 ! [rejected]        main -> main (non-fast-forward)
2error: failed to push some refs to 'https://gitee.com/zailiang/vue-project.git'
3hint: Updates were rejected because the tip of your current branch is behind
4hint: its remote counterpart. Integrate the remote changes (e.g.
5hint: 'git pull ...') before pushing again.
6hint: See the 'Note about fast-forwards' in 'git push --help' for details.
1git pull
2git fetch & git merge

推送无上游分支

1fatal: The current branch main has no upstream branch.
2To push the current branch and set the remote as upstream, use
3
4    git push --set-upstream origin main
5
6To have this happen automatically for branches without a tracking
7upstream, see 'push.autoSetupRemote' in 'git help config'.

当前的 Git 分支 main 没有与远程仓库(origin)中的对应分支关联。简单来说,Git 不知道要将本地的 main 分支推送到远程仓库的哪个分支。

因此,在你尝试使用git push命令时,Git 提示你需要先设置一个 “上游分支”(upstream branch),即本地分支和远程分支之间的关联。

1git push --set-upstream origin main
2git branch --set-upstream-to=origin/main

Pull 与 Fetch 的区别

  • git pull 实际上是 git fetchgit merge 的组合命令。它首先从远程仓库下载更新,然后将这些更新自动合并到当前分支。这意味着在执行 git pull 后,你的当前分支会立即包含远程分支的更改。

  • git fetch 用于从远程仓库下载所有新的提交和数据,但不会自动合并这些更新到当前分支。它仅仅是将更新的数据下载到本地,使你可以查看和审查这些更改。

命令 作用 影响
git fetch 下载远程更新,但不自动合并到当前分支 本地分支不变,可以审查更新
git pull 下载远程更新并自动合并到当前分支 当前分支被更新

Merge 与 Rebase(变基) 的区别

  • merge 是一种将两个分支的历史结合在一起的方式,通常用于将一个功能分支的更改合并到主分支(如 maindevelop)中。merge 会保留两个分支的提交历史,并创建一个新的合并提交(merge commit)。
1A---B---C feature
2 \      /
3  D----E master
  • rebase 是将一个分支的更改应用到另一个分支的顶部,通常用于在合并之前将功能分支的提交“平铺”到主分支上。rebase 会重写提交历史,使得历史记录更线性。
1A---B---C feature
2         \
3          D----E master
特性 merge rebase
历史 保留所有提交历史,可能产生合并提交 重新排列提交,使历史线性,消除合并提交
适用场景 适合于需要保留分支历史的场景 适合于需要保持历史整洁、简洁的场景
处理冲突 在合并时解决冲突一次 在每个提交时可能需要解决冲突多次
  • **使用 **merge
    • 当你希望保留完整的历史记录,尤其是在团队合作中,可以选择合并方式。
    • 适合于大规模的项目,尤其是多个开发者同时在多个分支上工作的情况下。
  • **使用 **rebase
    • 当你希望保持提交历史的整洁,尤其是在个人项目或较小团队中,可以使用变基方式。
    • 在准备将功能分支合并到主分支之前,可以先进行变基,以确保没有多余的合并提交。

Switch 与 Checkout 的区别

在 Git 中,switchcheckout 都可以用于切换分支,但它们的用途和设计上有一些不同。switch 是 Git 2.23(2019年)引入的一个新命令,用来简化分支切换操作,并避免 checkout 的多功能性带来的困惑。

操作 git checkout git switch
切换分支 git checkout <branch_name> git switch <branch_name>
创建并切换到新分支 git checkout -b <new_branch_name> git switch -c <new_branch_name>
恢复文件到某个提交的状态 git checkout <commit_hash> -- <file> 不支持恢复文件
功能复杂性 多功能(切换分支、恢复文件等) 专注于分支切换,功能更简单

尽管 git checkout 仍然可以用于分支切换和其他操作,但其多功能性有时会带来混淆。

git switchgit restore(另一个新命令,用于恢复文件)一起引入,旨在分离 git checkout 的功能,提供更清晰的用途:

  • **git switch**:用于切换分支。
  • **git restore**:用于恢复工作目录中的文件或目录到某个提交的状态。

切换标签

如果您只是想查看标签的内容,git checkout v1.0.1 是一个简单直接的方法。

如果您希望在标签的基础上进行开发,使用 git switch -b <new_branch_name> v1.0.1 是更好的选择。这样可以避免因处于“分离头指针”状态而可能导致的提交丢失问题。


Reset 与 Revert 的区别

**git reset**

  • 直接移动分支指针到指定提交,并可选择清除暂存区和工作目录的更改。
  • 例如:git reset --hard HEAD~1 会删除最后一次提交和所有相关的更改。

**git revert**

  • 生成一个新的提交,撤销指定提交的所有更改。
  • 例如:git revert <commit_hash> 会创建一个新的提交,使之前的提交影响反向生效。

HEAD 引用移动

1git reset --hard HEAD~1

**HEAD**:表示当前分支的最新提交(也就是你当前所处的提交)。

**~1**:表示向后移动一个提交。因此,HEAD~1 指的是当前提交的上一个提交。

1* be4cdbf (HEAD -> main) 提交信息 A
2* 7fef373 提交信息 B
3* 1234567 提交信息 C

HEAD 指向 be4cdbf(最新的提交 A)

HEAD~1 指向 7fef373(提交 B)

HEAD~2 指向 1234567(提交 C)