关联本地仓库和远程仓库并进行push

1
2
git remote add <reposiotry-name> <url>
# <reposiotry-name>是自定义的一个远程仓库nickname
1
2
git push <remote-repository> <local-branch>:<remote:branch>
# <remote-repository>用名字或者url都行

克隆刚刚push的仓库修改后再push

1
git clone <remote-repository-url> <target-directory>
1
修改其中的sample.txt文件
1
2
git push
# 因为是clone的,在.git中保留了远程仓库的相关信息

git diff,比较工作区和暂存区的文件之间的差异。

以暂存区的文件作为参照物来描述工作区的文件的差异。

同理git diff --staged,比较暂存区和存储区的差异,以存储区作为参照物

Git中,代码的管理围绕提交记录进行

提交记录就是一个提交对象,提交对象包含了很多信息,比如提交时的注释指向上一个提交对象的指针指向树对象的指针

项目结构树对象,其上的根节点和分支节点其实就是目录或者说文件夹。而其叶子节点指向实际的文件对象,文件对象实际上就是blob对象

blob对象,就是实际的文件对象

项目结构树对象和若干个blob对象就组成了一次提交时产生的暂存内容的快照,提交对象虽然说实际上指向的是树对象,但也可以说指向的是这次提交产生的快照

分支,就是一个指针,这个指针指向提交对象,比如说master,本质就是一个指针,通常情况下指向最新的一次提交对象。因为分支实际是指针,所以在Git中,称分支为ref,引用

HEAD,也是一个指针,这个指针可以指向分支,也可以指向提交对象

特别需要说明的是,比如说HEAD^,通常说是上一个节点,但是HEAD哪来的上一个节点?假设这时的情况是HEAD->master->commit1commit1是有上一个节点的,所以这个过程是从HEAD递归寻找到其最终指向的一个提交对象,然后然后得出该提交对象的上一个提交对象,即commit0。这有点像运算符重载。同理master^也是一样。ref^ref~n运算的结果都是提交对象

git checkout <ref>/<commit>命令,用来修改HEAD的指向,后面跟一个分支名或者一个提交对象。

1
2
git checkout main # 让HEAD指向分支main
git checkout <commit> # 让HEAD指向某次提交

git branch -f <ref1> <commit>/<ref2>/HEAD命令,改变分支ref1的指向,后面跟一个提交对象或者另一个分支。注意分支最终指向的一定是提交对象,所以这里第二个参数为分支或HEAD也有点递归的意思。

1
2
3
git branch -f main <commit> # 让分支main指向某次提交
git branch -f main master # 让分支main指向分支master指向的提交
git branch -f main HEAD # 让分支main指向HEAD最终指向的提交

git reset <commit>/<ref>命令,这个命令是修改当前HEAD所指向的分支的指向,但是HEAD的指向仍然是刚才的分支不会变。比如此时HEAD->main->c1,用了这个命令后HEAD->main->c0,也有点递归的意思

1
git reset master # 假如此前HEAD->main->c1,master->c0; 此后HEAD->main->c0

git cherry-pick <commit>/<ref>命令,复制其它分支上的提交对象,commit 到当前HEAD所指向的分支,比如此时HEAD->main->c1,master->c2,使用命令后HEAD->main->c2->c1'

1
2
3
4
# HEAD->main-><commit>
git cherry-pick <commit>
git cherry-pick master
git cherry-pick master^

git rebase <ref>命令,修改当前HEAD所指向的分支的基。Git会找到当前分支与参数中ref分支的第一个公共祖先节点,然后把当前分支指向的节点之前的提交对象做一个拷贝,把拷贝的对象都移到ref指向的提交对象的后面,最后修改当前分支的指向到最后一个拷贝对象上,注意HEAD的指向的还是当前分支不变,由此实现变基。加了-i或者--iteractive还可以打开交互式GUI界面,改变当前分支指向的节点到公共祖先节点之间的节点的顺序,或者删除节点

1
2
# HEAD->main->c4->c3->c1,master->c2->c1
git rebase master # 此后HEAD->main->c4'->c3'->c2->c1, master->c2->c1, c4->c3->c1

git rebase <ref1> <ref2>

1
git rebase <ref1> <ref2> # 把<ref2>分支变基到<ref1>分支上

git commit --amend命令,只会再次提交一次当前HEAD指向的提交对象,然后修改分支指向,不会影响后面子提交的父指向

1
2
# c3->c2->c1. HEAD->main->c2->c1
git commit --amend # c3->c2->c1, HEAD->main->c2'->c1

git describe <commit>/<ref>/HEAD,描述离指定节点最近的一次有tag的节点

git checkout main; git meerge master,合并的新节点属于main分支,也就是HEAD->main-><new-commit>

git fetch 完成了仅有的但是很重要的两步:

  • 从远程仓库下载本地仓库中缺失的提交记录
  • 更新远程分支指针(如 origin/main)

git fetch 不会做的事

git fetch 并不会改变你本地仓库的状态。它不会更新你的 main 分支,也不会修改你磁盘上的文件。

先抓取更新再合并到本地分支

这个流程指的是先git fetch

再执行以下命令:

  • git cherry-pick orign/main
  • git rebase orign/main
  • git merge orign/main
  • 等等

但是,git pull合并了这两步操作

git pull 就是 git fetchgit merge 的缩写!

git pull --rebase 就是 git fetchgit rebase 的简写!

git push 负责将你的变更上传到指定的远程仓库,并在远程仓库上合并你的新提交记录。

git push默认会把当前HEAD指向的分支上的所有节点提交到远程仓库,在远程仓库修改或创建同名分支,在本地仓库修改或创建origin/<ref>远程分支以同步远程仓库

  • pull 操作时, 提交记录会被先下载到 o/main 上,之后再合并到本地的 main 分支。隐含的合并目标由这个关联确定的。
  • push 操作时, 我们把工作从 main 推到远程仓库中的 main 分支(同时会更新远程分支 o/main) 。这个推送的目的地也是由这种关联确定的!

直接了当地讲,maino/main 的关联关系就是由分支的“remote tracking”属性决定的。main 被设定为跟踪 o/main —— 这意味着为 main 分支指定了推送的目的地以及拉取后合并的目标。

当你克隆时, Git 会为远程仓库中的每个分支在本地仓库中创建一个远程分支(比如 o/main)。然后再创建一个跟踪远程仓库中活动分支的本地分支,默认情况下这个本地分支会被命名为 main

这也解释了为什么会在克隆的时候会看到下面的输出:

1
local branch "main" set to track remote branch "o/main"

可以让任意分支跟踪 o/main, 然后该分支会像 main 分支一样得到隐含的 push 目的地以及 merge 的目标。 这意味着你可以在分支 totallyNotMain 上执行 git push,将工作推送到远程仓库的 main 分支上。

有两种方法设置这个属性,第一种就是通过远程分支切换到一个新的分支,执行:

1
git checkout -b totallyNotMain o/main

就可以创建一个名为 totallyNotMain 的分支,它跟踪远程分支 o/main

另一种设置远程追踪分支的方法就是使用:git branch -u 命令,执行:

1
git branch -u o/main foo

这样 foo 就会跟踪 o/main 了。如果当前就在 foo 分支上, 还可以省略 foo:

1
git branch -u o/main