文章目录
- 安装git
-
- 配置git
-
- [什么是git 仓库](#什么是git 仓库)
- 创建版本库
- 时光穿梭机
-
- 版本回退
- [工作区 和 暂存区](#工作区 和 暂存区)
-
- [工作区= 你看得见、摸得着的文件,编辑的永远是工作区](#工作区= 你看得见、摸得着的文件,编辑的永远是工作区)
- [暂存区stage(或者叫index) = 下次提交要带走哪些改动的"候选区 / 提交清单"](#暂存区stage(或者叫index) = 下次提交要带走哪些改动的“候选区 / 提交清单”)
- 管理修改
- 撤销修改
-
- [git checkout/git restore <file>(撤销工作区修改)](#git checkout/git restore <file>(撤销工作区修改))
- [git reset HEAD <file>/git restore --staged <file>可以把暂存区的修改撤销掉(unstage),重新放回工作区](#git reset HEAD <file>/git restore --staged <file>可以把暂存区的修改撤销掉(unstage),重新放回工作区)
- 删除文件
- 远程仓库
-
- 添加远程仓库(先有本地后有远程)
-
- [ git remote add origin](# git remote add origin)
- [git push -u origin master 把本地 master 推到远程 origin,并且记住"master 对应 origin/master"。远程没有 master 分支:在远程 创建 master,远程已经有 master 分支:把你本地 master 上比远程新的提交推到远程 master](#git push -u origin master 把本地 master 推到远程 origin,并且记住“master 对应 origin/master”。远程没有 master 分支:在远程 创建 master,远程已经有 master 分支:把你本地 master 上比远程新的提交推到远程 master)
-
- ["为什么远程叫 origin?"](#“为什么远程叫 origin?”)
- [".git/config 里的 [remote "xxx"],是在给这个本地仓库配置:它知道有哪些远程仓库,每个远程叫什么名字、地址是什么。"](#“.git/config 里的 [remote "xxx"],是在给这个本地仓库配置:它知道有哪些远程仓库,每个远程叫什么名字、地址是什么。”)
- 单个远程仓库?多个远程仓库?
-
- ["只有一个的时候,git pull / push 是从哪个远程仓库操作?"](#“只有一个的时候,git pull / push 是从哪个远程仓库操作?”)
- "有多个远程仓库?"
- [git branch -vv 看"本地分支跟哪个远程分支绑定"](#git branch -vv 看“本地分支跟哪个远程分支绑定”)
- [git remote -v 查看当前这个本地仓库配置了哪些远程仓库,以及它们各自的地址](#git remote -v 查看当前这个本地仓库配置了哪些远程仓库,以及它们各自的地址)
- [git push <远程名> <本地分支名>:<远程分支>](#git push <远程名> <本地分支名>:<远程分支>)
- [git pull <远程名> <远程分支名>](#git pull <远程名> <远程分支名>)
- 删除远程仓库
- 从远程库克隆(假设我们从零开发,那么最好的方式是先创建远程库,然后,从远程库克隆。)
-
- [git clone 从远程仓库,把代码 + 历史 整套复制一份到本地,顺便帮你配好远程。](#git clone 从远程仓库,把代码 + 历史 整套复制一份到本地,顺便帮你配好远程。)
-
- [git@xx:michaelliao/gitskills.git 是什么?](#git@xx:michaelliao/gitskills.git 是什么?)
- [clone 完成后,本地仓库里都配置了什么?](#clone 完成后,本地仓库里都配置了什么?)
- 带"自定义目录名"的克隆
- 分支管理
-
- 创建与合并分支
-
- [git merge xx 用于合并指定分支到当前分支](#git merge xx 用于合并指定分支到当前分支)
- 解决冲突
-
- [git status](#git status)
-
- [use "git add <file>..." to mark resolution 用 git add <文件名> 来"标记:这个冲突我已经解决好了"](#use "git add <file>..." to mark resolution 用 git add <文件名> 来“标记:这个冲突我已经解决好了”)
- [fix conflicts and run "git commit" 先把冲突文件改好(fix conflicts),再执行 git commit 完成这次合并](#fix conflicts and run "git commit" 先把冲突文件改好(fix conflicts),再执行 git commit 完成这次合并)
- [git merge --abort 放弃这次合并 回到你执行 git merge 之前的状态](#git merge --abort 放弃这次合并 回到你执行 git merge 之前的状态)
- pycharm
- vscode
- [git log --graph --pretty=oneline --abbrev-commit](#git log --graph --pretty=oneline --abbrev-commit)
- Bug分支
- 多人协作
-
- [git push origin master](#git push origin master)
- [git push reject](#git push reject)
-
- [failed to push some refs](#failed to push some refs)
- [hint: Updates were rejected because the tip of your current branch is behind,hint: its remote counterpart.](#hint: Updates were rejected because the tip of your current branch is behind,hint: its remote counterpart.)
- [Integrate the remote changes (e.g. 'git pull ...') before pushing again](#Integrate the remote changes (e.g. 'git pull ...') before pushing again)
- 解决方案
- [git pull <remote> <branch>](#git pull <remote> <branch>)
- Rebase
-
- [--graph 里的每一根竖线 | 不是某一个分支专属的线。](#--graph 里的每一根竖线 | 不是某一个分支专属的线。)
- [Updates were rejected because the remote contains work that you do not have locally](#Updates were rejected because the remote contains work that you do not have locally)
- [First, rewinding head to replay your work on top of it](#First, rewinding head to replay your work on top of it)
- [git rebase](#git rebase)
-
- [rebase 过程中遇到冲突](#rebase 过程中遇到冲突)
- [git rebase -i 整理本地提交(把很多小提交合成一个)](#git rebase -i 整理本地提交(把很多小提交合成一个))
- 使用Github
-
- [pull resquest](#pull resquest)
- [git diff](#git diff)
-
- [git diff <commit1> <commit2>](#git diff <commit1> <commit2>)
-
- [diff 输出其实等价于一个"补丁(patch),即从第一个参数变到第二个参数该怎么改。](#diff 输出其实等价于一个“补丁(patch),即从第一个参数变到第二个参数该怎么改。)
安装git
https://liaoxuefeng.com/books/git/time-travel/delete/index.html
配置git
shell
git config --global user.name "Your Name"
git config --global user.email "email@example.com"
Git 每次你 git commit 的时候,都会在提交里写上:
提交人名字(user.name)
提交人邮箱(user.email)
这两个信息就是用这两行命令配置的。
以后看 git log 就会看到类似:
Author: Your Name email@example.com
shell
# 命令行窗口运行,查看所有配置
git config --list
如果只想对"某一个仓库"生效?
那就:
命令行:cd 到那个仓库根目录(有 .git 的地方)
不加 --global,只对当前仓库配置:
git config user.name "Project-Specific Name"
git config user.email "project-email@example.com"
这样这个项目用一个名字,其他项目仍然用全局配置
什么是git 仓库
"Git 仓库" = 有 .git 的那棵目录树;
.git 所在的那一层目录,就是这个仓库的根目录。

创建版本库
git add
vscode
git status

On branch master -- 所在的分支是 master
Changes not staged for commit:--对提交来说修改还没有暂存



git commit -a :"把所有已跟踪文件的修改/删除都一起提交掉"




git add
更改:更改下的内容还没有放到暂存区,还在工作区
暂存的更改:修改放到了暂存区

git add xxx 等价于 vscode 在更改目录下点击 + 号

git status
shell
$ git status
On branch master
Changes to be committed:-- 将要被提交的修改
(use "git restore --staged <file>..." to unstage)
modified: readme.txt


pycharm

变更列表视图

git add xxx 等价于 pycharm点击添加到VCS
添加到 VCS" = 把你的代码交给 Git 之类的版本管理工具来管。
VCS = Version Control System,版本控制系统。
最常见的就是:
Git(你现在在用的)
还有别人家的:SVN、Mercurial 等


如果创建文件的时候选择了添加到git版本控制
git add xxx 等价于右击文件-》git->添加
【changes里的可能是已暂存的,也可能是没暂存的】

暂存区域视图
未暂存:工作区的修改还没放到暂存区

git add xxx 等价于 pycharm 点击 +

时光穿梭机
版本回退
修改文件
vscode

pycharm
变更列表
changes里的内容可能已暂存,也可能没有
直接显示在changes处

运行git add readme.txt 还是在change处

等价于右击文件-》git->添加

暂存区域
显示在未暂存

git log
vscode

pycharm

git reset 版本回退
git reset
--hard会回退到上个版本的已提交状态,而--soft会回退到上个版本的未提交状态(已暂存状态),--mixed会回退到上个版本已添加但未提交的状态(未暂存状态)。
hard
回退到上个版本的已提交状态

soft
--soft会回退到上个版本的未提交状态(已暂存状态)
vscode

pycharm
1、变更列表

2、暂存区域

mixed
mixed会回退到上个版本已添加但未提交的状态(未暂存状态)。
vscode
Unstaged changes:
工作区里有改动,但还没有用 git add 加到暂存区(还没进入 commit 的候选列表)

pycharm
1、变更列表

2、暂存区域

工作区 和 暂存区
工作区= 你看得见、摸得着的文件,编辑的永远是工作区



暂存区stage(或者叫index) = 下次提交要带走哪些改动的"候选区 / 提交清单"
只有暂存区里的内容,才会被 git commit 写进历史。
git commit 把暂存区的所有内容提交到当前分支

管理修改
pycharm
变更列表
变更列表展示时,虽然低2次的修改没有git add,但是点击提交的时候,会自动git add 2次修改都会带上

暂存区域
暂存区域展示时,点提交只会提交在暂存的内容,第二次的修改不会提交

总结
1、变更列表视图时,暂存和没暂存的都在changes里,此时点击提交,pycharm会把没暂存的先暂存在提交
2、暂存区域视图,点提交只会提交已经暂存 的
git diff HEAD -- readme.txt
看看现在工作区里的 readme.txt,和最近一次提交(HEAD 里的那个版本)相比,到底改了哪几行


- = 老版本里有、现在没了(被删/被换掉)
- = 新版本里有、老版本里没有(新增/替换后的内容)


撤销修改
git checkout/git restore (撤销工作区修改)
vscode
暂存区没内容,撤回到和上次提交一样的状态

等价于点放弃更改按钮

暂存区有内容
撤回到加到暂存区后的状态
等价于点放弃更改按钮

pycharm
暂存区没内容,撤回到和上次提交一样的状态
变更列表
变更列表,不好观察的一点就是,不管有没有放到暂存区,都在changers展示

暂存区域

等价于点 回滚按钮

暂存区有内容
变更列表

不等价于点 回滚。点回滚后上次暂存的也没了

暂存区域

等价于点 回滚按钮

git reset HEAD /git restore --staged 可以把暂存区的修改撤销掉(unstage),重新放回工作区
vscode

等价于 vscode 点击 -

pycharm
变更列表

没找到等价按钮
暂存区域

等价于 点击取消暂存

删除文件
vscode

删除文件,恢复,和,撤销工作区修改时一样的(删除也是一个修改操作)

pycharm
变更列表

暂存区域

远程仓库
添加远程仓库(先有本地后有远程)
$ git remote add origin



git push -u origin master 把本地 master 推到远程 origin,并且记住"master 对应 origin/master"。远程没有 master 分支:在远程 创建 master,远程已经有 master 分支:把你本地 master 上比远程新的提交推到远程 master

git push -u origin master = 第一次把本地 master 推到远程 origin,并且记住"master 对应 origin/master",以后可以直接 git push / git pull。


省略了远程分支名,所以 Git 默认:
"本地分支名 == 远程分支名",也就是 master -> master。

"为什么远程叫 origin?"


".git/config 里的 [remote "xxx"],是在给这个本地仓库配置:它知道有哪些远程仓库,每个远程叫什么名字、地址是什么。"
.git/config 里的 [remote "xxx"]:
👉 告诉本地仓库:"我有一个叫 xxx 的远程,它的地址是 url = ..."
.git/config 里的 [branch "master"] remote = xxx:
👉 告诉本地分支:"我默认和远程 xxx 上的某个分支同步。"




单个远程仓库?多个远程仓库?


"只有一个的时候,git pull / push 是从哪个远程仓库操作?"


"有多个远程仓库?"




git branch -vv 看"本地分支跟哪个远程分支绑定"

git remote -v 查看当前这个本地仓库配置了哪些远程仓库,以及它们各自的地址


git push <远程名> <本地分支名>:<远程分支>


git pull <远程名> <远程分支名>


删除远程仓库
git remote rm origin 这条命令的意思是:
把当前本地仓库里,名字叫 origin 的"远程配置"删掉。




从远程库克隆(假设我们从零开发,那么最好的方式是先创建远程库,然后,从远程库克隆。)
git clone 从远程仓库,把代码 + 历史 整套复制一份到本地,顺便帮你配好远程。
git clone git@github.com:michaelliao/gitskills.git 会:
在本地创建一个 git 仓库,并自动添加远程:
remote "origin"
url = git@github.com:michaelliao/gitskills.git
fetch = +refs/heads/:refs/remotes/origin/
这条 fetch 规则表示:远程所有分支在本地都有对应的"远程跟踪分支" origin/分支名。
同时只创建一个本地分支(比如 master 或 main),并配置:
branch "master"
remote = origin
merge = refs/heads/master
这表示:本地 master 默认跟 origin/master 同步。
其他远程分支(比如 origin/dev)需要你手动 git switch -c dev origin/dev 才会变成本地可开发的 dev 分支。


git 会给这个远程仓库自动起名叫 origin,并写进 .git/config






git@xx:michaelliao/gitskills.git 是什么?

ssh密钥
每换一台电脑都要配置下ssh公钥,证明该电脑有操作权限




clone 完成后,本地仓库里都配置了什么?




带"自定义目录名"的克隆





分支管理
创建与合并分支
在哪个分支下运行创建分支的命令,就是基于哪个分支拉一个分支出来

git merge xx 用于合并指定分支到当前分支







分支分叉图



两个分支各自发展一段时间之后,被 merge 到一起,产生了一个"合并提交 M"







然后在 master 上做:git merge dev。得出这个结论是因为,合并后的新节点在master上吗?
因为 合并后的新节点 M 被 master 指向,而 dev 还在 F,
所以我们能判断这次操作是:
git switch master
git merge dev




解决冲突
git status
shell
$ git status
On branch master
Your branch is ahead of 'origin/master' by 2 commits.
(use "git push" to publish your local commits)
You have unmerged paths.
(fix conflicts and run "git commit")
(use "git merge --abort" to abort the merge)
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: readme.txt
no changes added to commit (use "git add" and/or "git commit -a")



use "git add ..." to mark resolution 用 git add <文件名> 来"标记:这个冲突我已经解决好了"




fix conflicts and run "git commit" 先把冲突文件改好(fix conflicts),再执行 git commit 完成这次合并


git merge --abort 放弃这次合并 回到你执行 git merge 之前的状态


pycharm


vscode


git log --graph --pretty=oneline --abbrev-commit

不加--pretty=oneline会显示作者,日期等

Bug分支
pycharm


vscode

git cherry-pick


冲突






git cherry-pick 需要的是 "你当前仓库里能找到的那个提交对象" 的 id
本地分支上的提交
远程分支上的提交。

git fetch/ git pull



多人协作
git push origin master

git push reject
push 失败是因为:远程 dev 比你本地 dev 更新,你必须先把远程的提交拉下来并合并/变基到本地,再 push。
failed to push some refs

hint: Updates were rejected because the tip of your current branch is behind,hint: its remote counterpart.
是一句话,终端换行窗口导致的

Integrate the remote changes (e.g. 'git pull ...') before pushing again

解决方案



git pull
从远程仓库 origin 拉取 dev 分支的更新,并把它合并(或 rebase)到你当前本地分支(通常也是 dev)。


Rebase
--graph 里的每一根竖线 | 不是某一个分支专属的线。
往下追,第一个遇见的*是最近公共祖先

Updates were rejected because the remote contains work that you do not have locally


ref



First, rewinding head to replay your work on top of it

git rebase
git rebase 你可以把它理解成:把"你这条分支上自己做的提交",搬家到另一个更新的基础上,然后按顺序重新贴一遍。
目的是让历史更直、更干净(少很多"合并提交")。
git rebase = 把当前分支 rebase 到它配置的 upstream(上游跟踪分支) 上。
git rebase xxx = 把"当前分支"上的提交,挪到 xxx 的最新提交后面去


rebase 过程中遇到冲突

冲突文件



在 rebase 冲突里,HEAD 指的是"你正在 rebase 到的那个新底座(upstream/onto)在当前时刻的内容"





git rebase -i 整理本地提交(把很多小提交合成一个)


常用命令
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit(融入之前的提交)
# f, fixup [-C | -c] <commit> = like "squash" but keep only the previous
# commit's log message(仅保留上一次提交的日志), unless -C is used, in which case
# keep only this commit's message; -c is same as -C but
# opens the editor(除非使用-C,在这种情况下,只会保留该<commit>的提交信息,-c,-C是一样的,只不过-c会打开编辑器)

多个提交合并成1个





rebase -i 为啥看起来像"只能保留最老的"?




使用Github
pull resquest
https://blog.csdn.net/qq_33429968/article/details/62219783
git diff
git diff

git diff A B 到底在说什么?






diff 输出其实等价于一个"补丁(patch),即从第一个参数变到第二个参数该怎么改。

"补丁"长什么样(它其实就是一串编辑指令)
统一 diff(unified diff)里,每一块会有类似:
@@ -oldStart,oldLen +newStart,newLen @@
意思是:
旧文件从第 oldStart 行开始取 oldLen 行
新文件从第 newStart 行开始取 newLen 行
中间用 -/+/空格 表示删/增/不变
所以它本质上就是:"在旧文件的这些位置,删这些行,加这些行"。
即,"下面这段 diff 描述的是:旧版本(A)该文件从第 oldStart 行开始的 oldLen 行([oldStart,oldStart+oldLen -1]),和 新版本(B)该文件从第 newStart 行开始的 newLen 行 ([newStart,newStart+newLen-1])之间的差别(一个差异块)。"

"下面这段 diff 描述的是:旧文件第 1~5 行 和 新文件第 1~6 行之间的差别。
