Git 合集

git-config

https://git-scm.com/docs/git-config#_configuration_file

git config --global --edit 能在命令行打开全局的配置文件,即 %userprofile%\.gitconfig~/.gitconfig 文件。

如果想要更换编辑器比如用 VS Code 编辑,可以用 EDITOR=code git config --global --edit

配置文件的格式:

  1. 空白字符都被忽略(无论是 \t 还是空格都可以)
  2. 注释的开头可以是 #;

git pull origin A:B

需要本地有 B 才能成功拉取,不然会拉取到当前分支。拉取的时候会自动 merge。

创建 diff 和合并

https://stackoverflow.com/a/21976357 讲的是如何 diff 文件和生成 patch。git format-patch -<n> <rev> 用来生成 diff,n 表示要包含几条,rev 表示某个 commit。

How can I generate a Git patch for a specific commit? - Stack Overflow 讲的是按 commit 生成 patch,并且提交。如果不想提交也可以生成 patch 之后按照上一个链接的方法 git apply 0001.patch(名字是举例)。

HEAD~1HEAD^1

~n 表示在当前分支上回退 n 次。^n 表示在当前岔路向第 n 条支路回退一次。

What’s the difference between HEAD^ and HEAD~ in Git? - Stack Overflow

将现在的项目推到另外一个仓库

# 假设前一个 remote 名为 origin
git fetch --all
git remote add newremote <new-remote-url>
git push newremote --tags refs/remotes/origin/*:refs/heads/*

打印 ref 的名称

打印的结果可以用在 git branch -rD $(...) 等命令里,应该是不能在 $() 的周围加引号。

git for-each-ref --format="%(refname:short)" refs/remotes/origin/

如果是文件夹,会列举文件里的所有文件;如果是文件,则只会列举文件本身;也可以用 * 来通配。

想要删除远程分支需要给 branch -D 加上 -r 参数。

git 给 ssh 指定密钥

  1. 临时:
set "GIT_SSH_COMMAND=ssh -i ../.ssh/id_rsa_xxx"

需要更改环境变量设置正确的密钥,注意即便是在 Windows 上也需要 / 而不是反斜杠。

  1. 对特定主机永久指定:将网站写在 ssh 的配置里面。比如:
Host github
  HostName github.com
  IdentityFile ~/.ssh/id_rsa

git clone

git clone --bare 不克隆文件,只克隆 .git 文件夹,而且直接将这个文件夹里的内容放到下载目录,而不是包一层 .git

git checkout

https://refine.dev/blog/git-switch-and-git-checkout/#difference-between-git-checkout-and-git-reset

(箭头代表操作过程的数据流向。)

git checkout <other-branch/commit> -- file_or_folder_path

如果不加文件只是 checkout,则会更改当前的 HEAD。

The git switch command replaced git checkout in 2020, although git checkout is still a supported command. The git checkout command performs two functionalities; “switch branch” and “restore working tree files”. To separate these two functionalities, Git introduced the git switch command, which replaces the “switch branch” feature of “git checkout”.

另外,如果当前有个文件和某个分支重名,则会认为给定的名字是文件

所以现在有了新的两个命令:

  • git restore 只恢复文件,不处理分支。
  • git switch 只切换分支,不恢复文件。

🆚 git switch

  • git switch -c NewBranch 相当于 git checkout -b NewBranch
  • git switch - 可以切换到上一个切换来的分支,相当于 git checkout -。(这个是可以无限来回切的,就像 Windows 的 Alt + Tab 一样。cd - 也有类似的效果。)

🆚 git restore

git restore 在恢复文件方面功能要比 checkout 更全一点,还能单独指定 --staged--worktree 两个位置的文件是不是要恢复(可以同时恢复)。用 --source 指定要从哪一个分支恢复(默认是 HEAD)。

git checkout 不给 branch 也是指定 HEAD,但是这个时候对于切换分支的功能没有任何效果。

另见 git restore

🆚 git reset

见下文的 git reset

git reset

全局的 HEAD 是当前 branch 的指针(除非当前是 detached 状态),是和特定的 commit 联系的。每个 branch 都有一个 HEAD 值,而 git reset 只是改变了当前的 HEAD 值。

git checkout (这里讨论的是 checkout 文件而非分支的情况)不会修改 HEAD/index,只会从仓库中找到对应的记录,复原 index 和 working tree(当然是有复原 index 区的,不然 git status 会显示有大量文件更改。Checkout 到某个分支看起来比较像 git reset --hard,但是当前有未保存的更改则会失败(除非启用 -m/--merge)。

git reset 则会改动 HEAD,分有几个等级:

  1. --soft:只修改 HEAD。
  2. --mixed:修改 HEAD 和 index。
  3. --hard:修改 HEAD/index/working tree。
  • git reset is specifically about updating the index, moving the HEAD.
  • git checkout is about updating the working tree (to the index or the specified tree). It will update the HEAD only if you checkout a branch (if not, you end up with a detached HEAD). (actually, with Git 2.23 Q3 2019, this will be git restore, not necessarily git checkout)

可以用 - 指代上一个分支

Git - Switch to previous branch | SoberKoder

以下都是可行的例子:

  • git switch -
  • git checkout -
  • git rebase -
  • git merge -

git show

显示某个 commit 的某个文件在标准输出流中。一面的代码等同于 git checkout feature/B -- path/utils.js

git show feature/B:path/utils.js > path/utils.js

git rebase

git rebase 单参数和双参数

Assume the following history exists and the current branch is “topic”:

      A---B---C topic
      /
D---E---F---G master

From this point, the result of either of the following commands:

git rebase master
git rebase master topic

would be:

              A'--B'--C' topic
              /
D---E---F---G master

虽然文档中说 git rebase master topic 相当于 git rebase master && git checkout topic,但是我测试下来看到 git checkout topic && git rebase master 效果是一样的。

git rebase 三参数(带有 --onto 选项)

First let’s assume your topic is based on branch next. For example, a feature developed in topic depends on some functionality which is found in next.

o---o---o---o---o  master
      \
      o---o---o---o---o  next
                        \
                        o---o---o  topic

We want to make topic forked from branch master; for example, because the functionality on which topic depends was merged into the more stable master branch. We want our tree to look like this:

o---o---o---o---o  master
    |             \
    |              o'--o'--o'  topic
     \
      o---o---o---o---o  next

We can get this using the following command:

git rebase --onto master next topic

master 是想要达到的新分支。next 和 topic 的公共祖先(不包含其本身)到 topic 是想要转移的分支。如果没有 --onto 选项,就会用第一个参数来推断 <new-branch>

🆚 git cherry-pick/rebase/merge

  • git merge 在不能 fast-forward 的时候是创建一个新结点,这个结点有两个分支作为 parents,然后前进到新的结点。
  • git cherry-pick 是重播选定范围的变化(可以选定一系列 commits,也可以选一个),但是创建完全不同的新结点到当前分支上
  • git rebase 是重播分叉以来的变化到给定分支上,也是创建了新结点,但是改变了图结构,会将分叉结构改成线性的!现在从给定分支应该能够直接 ff 到当前分支。这个时候如果能记住之前 commit 的 hash,是可以恢复位置的(前提是还没有运行 gc)。

用 git rebase 修改某个结点的 parent

需要 --onto 选项,省略了当前的 HEAD。

git rebase --onto <new_parent> <old_parent>

举例:

git checkout feature_branch_2
git rebase -–onto main <SHA_OF_COMMIT_13>

git tag

git rebase 的过程中可以临时用 git tag 做好标记,比记忆 commit 的 hash 更容易。

git submodule

git submodule update --recursive --init 慢:

git submodule update --init --recursive --progress

加上 --progress 选项看看进度,确认一下是不是代理没有生效。

git grep

默认是找工作区的文件。以下命令查找历史上曾经存在过的所有文件,并且会打印相关的 commit。

git grep "some pattern" $(git rev-list --all)