git学习笔记(希望能够对大家有用)
[toc]
Git学习笔记
集中式管理VS分布式管理
集中式管理(以SVN为例)
优点:
- 代码存放在单一的服务器上,有利于项目的管理
- 版本快照存储的是每个版本之间的差异,存储空间较小
缺点:
- 当服务器出现问题时,轻则代码版本控制功能无法使用,重则整个项目的历史记录丢失
- 由于快照只储存差异,所以回滚需要逐个快照进行比对,速度慢
SVN的机制是: 整个项目的历史快照都保存在服务器中,而客户端只保存这项目当前最新快照,所以当服务器出现故障时,轻则导致由于服务器故障而无法使用版本控制功能,重则整个项目历史记录丢失,就算恢复也只能恢复到客户端所拥有的最新快照
分布式管理(以Git为例)
优点:
- 每个客户端都储存着整个项目的所有历史快照,只要客户端还有一个能用就能保证项目的数据(历史记录)不丢失
- Git的每次快照保存,都是整个项目完整的快照保存,回滚速度极快
缺点:
- 由于每个快照都是项目完整的历史记录,所以储存空间会稍微大一点点(但是由于极致的压缩算法空间占用还是很小)
.git文件夹结构说明
hooks 目录包含客户端或服务端的钩子脚本
info 包含一个全局性排除文件
logs 保存日志信息
objects 目录存储所有数据内容
refs 该目录存储着的文件对应不同的分支,文件内容是分支对应的提交对象的hash
config 局部(local)的配置选项文件
description 用于显示当前仓库的描述信息
HEAD 指向当前所处分支的最新版本
index 文件保存暂存区的信息
git初始化
让一个普通的文件目录成为git工作区
git init
Git的初次配置
对配置应用的位置说明
-
git config --system
是整个系统中所有用户都使用的配置, 对应的配置文件是git安装目录下/etc/gitconfig
-
git config --global
该git配置只能被当前系统用户使用, 对应的配置文件是用户目录/.gitconfig
-
git config
该git配置只能适用于当前git管理(git init
)的这个项目, 对应的配置文件是项目目录/.git/config
==首次安装git环境必须配置, 之后git bash更新将会自动沿用==
个人信息配置
git config --配置位置 user.name "yourName" # 在当前系统用户中配置git的用户名
git config --配置位置 user.email "yourEmail" # 在当前系统用户中配置git的邮箱
个人信息删除
git config --配置位置 --unset user.name
git config --配置位置 --unset user.email
查询不同位置的配置信息
git config --配置位置 --list
Git的三片区域和三种对象(Git底层命令)
区域:
- 工作区 Workplace
- 暂存区 StagePlace
- 版本库 Repository
对象:
- Git对象
- 是Key-Value键值对
- Key是通过文件内容计算出的hash
- 键值对在git内部是一个blob类型的对象
- 树对象
- 提交对象
git对象
git对象用于以键值对的方式存储数据,是一个键值对数据库,==操作的是.git中的object目录==。通过对git对象的了解与使用进而引出树对象
hash值是根据内容计算的
echo 'hello' | git hash-object --stdin
ce013625030ba8dba906f756967f9e9ca394464a
# 再次执行
echo 'hello' | git hash-object --stdin
ce013625030ba8dba906f756967f9e9ca394464a
向数据库中写入内容并得到根据内容计算出的hash值
echo 'hello world' | git hash-object -w --stdin # 写入echo的内容
# | 是多个命令串行操作 相当于先执行echo 再执行 git hash-object
# -w参数 用于将echo的内容写入git对象中
# --stdin参数 用于从标准输入流中读取数据(就是获得echo的内容)
# 如果没有-w就会得到内容计算出的hash,但是不会将内容写入数据库中
echo 'hello world' | git hash-object --stdin
# --stdin不指定则必须替换成文件的路径
echo 'fitz' > test.txt # 创建一个test.txt文件 内容是fitz
git hash-object -w test.txt # 将test.txt中的内容写入到数据库中
去git对象对应的数据库中查看存储的数据
git cat-file -p hash
查看数据库存储数据的类型
git cat-file -t hash # blob类型
==git对象的问题==
- 只能跟踪文件内容的变化,而无法追踪多个文件(版本)的变化,所以不能成为整个项目的一次快照
- 记住繁杂的hash值不现实
- git中没有保存文件名(对应文件内容的不是文件名而是hash值)
- 操作git对象只是单纯的设计数据的存取而不涉及暂存区(Stage/Index Place)
git对象只能代表单个文件的一次次版本快照(每次更改内容都会追加hash记录而不是替换),它不是整个项目的版本(它只能追踪单个文件)
树对象
树对象能够解决git对象只能用hash值而无法用文件名保存的问题, 更允许我们将多个文件组织在一起作为整个项目的一个版本快照,==树对象的操作涉及到暂存区==
树对象可以想象成文件夹,git对象可以想象成文件。==自然地, 树对象可以包含另一个树对象(子树对象)和git对象==
将文件保存到暂存区,为构建树对象(形成版本快照)做准备
此时.git目录中出现INDEX文件,代表暂存区的状态
git update-index --add --cacheinfo 100644 hash fileName.ext
# --add参数 表示当前文件是要新添加进暂存区中的(如果暂存区已有相同名字的文件对象吗,则不需要该参数)
# 100644 表示这是一个普通文件 对应的其他文件模式还有: 100755可执行文件 120000表示符号链接
# --cacheinfo参数 表示要添加的文件位于git数据库中(是一个git对象)而不是当前目录下的一个文件
根据git对象形成树对象后放到暂存区中
echo "" | 文件名 # 创建新文件
git update-index --add 文件名
# 以上两步等价于
echo "" | 文件名 # 创建新文件
git hash-object -w 文件名 # 先生成git对象
git update-index --add --cacheinfo 生成git对象的对应的hash
将暂存区中所有的内容形成一个树对象(构建树对象)作为版本快照
git write-tree # 运行成功后将会得到版本快照对应的hash
# 根据版本hash查看版本快照的类型
git cat-file -t ff75343fd58cff0477965bf65e2bab1e90fe1ab5 # tree类型
查询暂存区状态
注意: 暂存区的文件始终是增量的(只会增加不会减少), ==除非到后面高层命令中切换分支,暂存区的文件才会依据分支增或减==
git ls-files -s
# 注意: 执行git write-tree后不会清空暂存区
查询待删除状态的文件
git ls-files --deleted
将一个树对象添加进暂存区中,然后构建生成一个新的树对象中(形成父子树对象)
git read-tree --prefix=bak 子树对象的hash # 将一个树对象放入暂存区
git write-tree # 完成树对象嵌套
问题:
- 同时对于版本快照的信息不明确,无法从中得到保存快照的时间、原因、谁保存的
树对象可以追踪多个文件的内容的变化(git对象),同时还能将git对象保存将暂存区中,然后再某个时机需要的时候形成整个项目的版本快照,每一个树对象都是一次完整的版本快照
提交对象
创建提交对象
# 当首次创建提交对象时
echo 关于本次提交需要的说明 | git commit-tree 树对象hash
# 当再次创建提交对象时,还需要额外指定父提交对象, 这样在查看提交对象的信息时就能知道其父提交对象是谁
echo 关于本次提交需要的说明 | git commit-tree 树对象hash -p 父提交对象的hash
提交对象是对树对象进行包裹后再添加进一些关于树对象的信息(作者、提交时间等),这一步只是对树对象的进一步美化而已,由于项目的每个版本都需要版本快照及其详细的说明,所以每一个提交对象都是一个完整的项目版本(仅比树对象多了说明信息)
常用Linux命令
clear # 清空控制台
tab # 自动补全
ctrl+u # 撤销当前输入
echo 'msg' # 输出信息
echo 'msg' > test.txt # 创建一个文件并将echo的内容添加进去
mkdir # 创建文件夹
cd # 切换路径
pwd # 显示当前所在路径
ls # 列出当前路径下所有文件
ll # 列出当前路径下所有文件及其详细信息
find 路径 # 列出当前路径及其子孙路径下所有文件与目录
find 路径 -type f # 只列出当前路径及其子孙路径下所有文件
rm # 删除文件
mv # 重命名文件
cat # 查看文件内容
vim # 使用vim编辑器编辑文件
##################### 关于vim的简单编辑 ##################
i 进入编辑模式
按esc后输入:开始输入各种vim的指令(用于保存或退出vim编辑器)
q! 表示不保存强制退出
wq 表示保存后退出
set nu 添加行号
Git日常使用(Git高层命令)
添加文件至暂存区(staged)
git add * / 文件 / 目录 # 将工作区的修改提交到暂存区(具体流程: 工作区修改后转变成git对象->先到版本库->再版本库到暂存区)
# 以上命令等价于执行
git hash-object -w 文件/目录 # 将文件放进git版本库(.git/objects)中成为git对象
git update-index [--add] --cacheinfo 文件/目录 # 将git对象从版本库提取然后放进暂存区中
将暂存区的文件提交到版本库,形成一个版本快照
git commit -m '注释内容'
# 以上命令等价于执行
git write-tree # 生成树对象得到hash值
echo '注释内容' | git commit-tree 树对象的hash值
git commit -a -m # 当版本库中已经有相同名字的文件时, 使用该命令可以跳过暂存区(自动git add)直接书写注释后commit到版本库 等价于 git add + git commit
git commit # 为当前版本书写详细的log
################### 重要 ####################
git commit --amend # 可以将当前暂存区内文件提交,但是不会生成新的版本(提交对象) 用于修改最新一次提交
# 示例:
# 当出现文件漏提交 或者 一个文件修改完添加到暂存区然后又修改这个文件,暂存区产生两个不同状态(modified与unStaged)的同名对象,并且还不小心commit了的时候
vim test.txt # 修改test
git add test.txt # 提交test至暂存区
vim test.txt # 修改test
git status # 此时暂存区的test有 Modified和 Changes not staged(unStaged)两个状态
git commit -m '' # 不小心还提交到了版本库
#最佳实践:
git add ./
git commit --amend
# 这样暂存区会被重新commit,替换最后一次提交对象
################### 重要 ####################
查看git目录当前所处状态
git status # 查看当前git管理目录的状态
git管理目录下文件的状态分类
- 未跟踪(Untracked)
- 已跟踪(tracked)
- 已暂存(staged)
- 已修改(modified)
- 已提交(commited)
- 已删除(deleted)
比较文件的变化
# 工作区与暂存区比较
git diff
# 暂存区与版本库比较
git diff --cached
git diff --staged
# 工作区与版本库比较
git diff HEAD/hash值
文件重命名
# 方式1
mv oldFileName newFileName # Linux下的文件重命名
git add newFileName # 将文件的变化(命名变化)提交到暂存区
git commit -m '' # 将重命名操作提交到版本库
# 重命名文件相当于执行了一下这些操作
# 1. rm删除掉旧的文件
# 2. 创建新的文件
# 3. 将旧文件中的内容添加进新文件
# 方式2(简化操作)
git mv oldFileName newFileName # git下的文件重命名
# git mv 相当于 => linux的mv + git add
git commit -m '' # 将重命名后的文件从暂存区提交到版本库
文件的删除
rm 文件 # 使用linux命令删除文件
git add ./ # 将文件的修改(删除)添加进暂存区
git commit -m ''
# 以上操作使用git命令简化成(简化操作)
git rm 文件
git commit -m ''
# **即: git rm <=> rm fileName + git add fileName
查看版本快照的提交记录
git log # 打印详细的版本提交记录
git log --pretty=oneline # 打印hash详细、其他信息简略的版本提交记录
git log --oneline # 打印7位数hash、其他信息简略的版本提交记录
git reflog # 当bash窗口关闭后又需要调出以前的版本提交记录时使用
Git分支
假如突然需要为项目添加一个新的功能,最佳实践应该是: 将项目完整的复制一份然后在复制的项目中编写新功能。而不是直接在原项目中编写新功能代码,因为这样做不安全,这就是分支的思想
==git分支本质上是复制一份提交对象然后给这个提交对象命名, 切换分支本质上是切换HEAD指针去指向某个分支==。HEAD指针总是指向某个分支的最新的提交对象。默认的HEAD这个指针对象指向着master分支的最新提交对象
查看分支列表(包含所有分支)
git branch # 查看分支状态
创建分支
git branch 分支名 #创建分支
git branch 分支名 提交对象的hash # 创建一个分支并使该分支指向特定的提交对象
切换分支
git checkout 分支名 # 切换分支
git checkout -b 分支名 # 创建并切换分支(简化操作)
创建&切换分支
git checkout -b 分支名 # 创建并切换分支(简化操作)
删除分支
# 删除分支需要先切换到其他分支
git branch -d 分支名 # 用于删除已经合并过的分支
git branch -D 分支名 # 强制删除分支,一般是由于分支没合并但是需要删除
查看每个分支的最后一次提交
git branch -v
关于切换分支的最佳实践: 每次在切换分支前,都将当前分支commit
一次,确保status
是clean
的
最佳实践的原因:
首先我们要知道切换分支意味着3个操作:
-
修改HEAD的指向即:将HEAD指针切换到对应分支
-
修改暂存区
-
修改工作目录
假如切换分支前,当前分支的status
不是干净的但是仍然切换分支的话,会发生两种情况 (坑爹所在)
- 当前的文件从未添加到过版本库中(commit),是完全新增的, 则无论是
Untracked
还是Staged
都会使得未存入过版本库的这些文件会被带入到要切换到的分支中(污染要切换的分支)
- 当前文件已经是添加到过(commit)版本库的, 状态只能是
Modified状态
, 则git会阻止我们进行分支切换
合并分支
git merge 分支
查看所有已经合并过的分支
git branch --merged
分支合并产生冲突
分支合并的几种模式及有可能发生的冲突:
-
Fast-Forward模式: 因为分支之间只是单纯的内容增减(版本线是直的), git会直接将分支(旧的)合并到merge指定的分支(新的)中
-
Auto-merging模式: 当旧分支上对a文件做修改,同时新分支上对a文件也做了修改,此时新旧分支合并就会产生冲突但是git会使用Auto-merging自动将冲突内容显示在a文件中,我们只需要去a文件中将冲突内容进行修改、提交就能解决冲突
配置别名
当遇到难记、冗长的命令时,可以通过为命令配置别名的方式来简化操作
创建别名
git config --位置 alias.别名 '命令'
删除别名
- 命令方式
git config --位置 --unset alias.别名
- 去位置对应的config配置文件中将对应的alias删掉
示例:
git status # 原命令
git config --global alias.st 'status' # 在全局配置中将 git status 别名简化成 git st
git config --global alias.st status # 内置的变量名称可以不加引号
git commit -m '' # 原命令
git config --local alias.gcm 'commit -m'
# 运行gcm '注释' 等价于执行 git commit -m '注释'
==为大家推荐几个好用的命令==
git config --global alias.lol "log --oneline --decorate --graph --all"
git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"
Git暂时隐藏工作区
假如在一个分支上工作到一半突然需要紧急切换到另一个分支,但是又不想因为需要切换到目标分支而将当前未完成工作的分支创建一次提交对象(作为一个版本)时,就可以是用git stash
命令
git stash
命令会将暂存区中未完成的修改保存到一个栈上
git stash # 将当前分支上的暂存区内容先隐藏起来
git stash list # 查看所有的stash(查看stash栈中的情况)
当做完工作回到隐藏工作区的分支上后,需要继续刚刚隐藏的工作时可以使用以下命令
git stash apply # 应用栈顶的隐藏工作区,而且不会删除git stash记录
git stash drop stash记录(stash@{num}) # 删除特定的git stash 记录
git stash pop # 应用栈顶的隐藏工作区,会删除git stash记录
# pop等于 apply+drop
初探Git时光机
工作区: 撤回工作目录中的文件修改,分两种情况
- 假如只修改工作区还没add,则撤销回最近的提交对象
- 假如 首次修改工作区 -> add进暂存区 -> 再次修改工作区,则撤销回到暂存区的状态(工作区首次修改)
git checkout -- 文件
暂存区: 撤回暂存区的特定文件至工作区
git reset HEAD 文件
# 上面命令等价于
git reset --mixed HEAD 文件 # --mixed是默认参数,是可选的
==路径reset原理:==
reset --mixed
命令: HEAD和分支会移动、版本库与暂存区回退到指定版本、工作区不变- 但是由于指定了特定的文件回退(路径reset), git不可能由于一个文件要回退而将整个版本回退
- 所以 路径reset下HEAD和分支不会移动, 版本库不会回退, 暂存区回退到指定版本add之前、工作区不变
文件从暂存区撤回工作区利用路径reset原理:
git reset --mixed HEAD 文件名
: HEAD和分支不会移动, 版本库不会回退, 工作目录不变- 暂存区虽然会改变, 但是会改变成当前版本, 所以也等于没变但是会回到当前版本add之前
- 此时三个区域的情况: 工作区 2 Modified/ => 暂存区 1 Unstaged=> 版本库 1
版本库: 重写版本库最新的提交对象(当最新的提交对象出现错误时)
git commit --amend # 重新commit暂存区(暂存区为空也行), 版本库不会新增提交对象而是修改最新那次提交对象
# 以上命令等价于
git reset --soft HEAD~
git commit -m ''
重改当前的提交对象, 使用git commit --amend
可以将当前暂存区内文件提交但是不会生成新的版本(提交对象),而是替换最新的版本(提交对象) ==用于commit的注释写错时和工作区、暂存区的文件漏提交==
深入Git时光机--reset命令
git reset --soft
: 版本库退回指定版本(提交对象), ==HEAD与版本库的差异add进暂存区, 不会修改工作区==
==操作HEAD、分支==
# HEAD~代表当前分支最新提交的上一次提交, 每一个~都代表前一次
# 多个前一次可使用 HEAD~数字
git reset --soft HEAD~ / hash # 版本回退到指定版本, 暂存区、工作区不改变
git reset --mixed
: 版本库和暂存区退回到指定版本(提交对象), 暂存区处于 Unstaged 状态(git add之前), == 不会修改工作区==
==操作HEAD、分支、暂存区==
git reset [--mixed] HEAD~ / hash # 版本回退到指定版本, 暂存区回退到add前, 工作区不改变
git reset --hard
: 版本库退回指定版本(提交对象),会修改工作区和暂存区
==操作HEAD、分支、暂存区、工作区==
git reset --hard HEAD~ / hash # 版本回退到指定版本, 工作区文件回退到指定版本
总结 --hard 与 checkout 的异同: 相同:
- 都会修改暂存区、工作区、HEAD
不同:
- checkout只移动HEAD, --hard会让HEAD和分支一起移动
- checkout会对工作目录做检查确保文件不会被弄丢(==看GIT分支最后有说明==) , --hard会直接全部替换工作目录有数据丢失风险
路径checkout: git checkout 路径
- 重置暂存区、工作区
- 重置工作区
git checkout hash 文件名
# 重置暂存区 与 工作目录
git checkout -- 文件名 <=> git checkout HEAD 文件名 # 由于HEAD是默认参数可以不写,但是不写就是切换分支, 所以使用 -- 作为区分(可以认为 -- 就是 HEAD)
# 只重置工作区
# 如果暂存区是clean的, 则将工作目录回退到最近的提交对象
# 如果暂存区是Staged to commit(文件被add过)然后再修改, 则将工作目录回退到暂存区的版本
Git时光机--使用reset回到未来
当使用git reset --hard HEAD~~
回到过去版本后,git log --pretty=oneline
将看不见回退前提交
这时可以使用git refklog
看到此仓库所有完整的操作
于是找到"未来的提交"(看不见回退的前的提交)的hash, 然后通过git reset --hard 未来的提交的hash
来回到未来的版本
然而由于reset --hard
对数据具有删除而不可恢复的危险, 所以最佳实践应该是:
- 在"未来版本"上创建一个分支
- 在该分支上进行相关操作、编写代码
- 完成后切换到master分支与"未来版本"的分支进行合并
为提交对象打Tag
Git可以给历史的提交对象打上tag表示重要
git tag 标签名 [HEAD] / hash # 为特定提交对象创建标签
git tag -d 标签名 # 删除标签
git tag # 列出所有标签
git show 标签名 # 展示标签对应的提交对象的详细信息
检出标签: 可以为以前打过tag的提交对象进行修改
git checkout 标签名 #检出标签
检出标签会出现头部分离模式,即HEAD不指向任何分支
我们此时需要在检出标签中创建一个分支,然后在进行增删改查等操作
git checkout -b 分支名
Git远程仓库协作开发
由于本地Git仓库和GitHub仓库之间的传输是通过SSH加密的,所以Github与本地之间需要通过加密通讯,这就需要我们去生成秘钥
先在用户主目录下,看看有没有.ssh目录,如果有, 再看看这个目录下有没有id_rsa
和id_rsa.pub
这两个文件
==如果没有.ssh或者目录文件缺失,就需要我们手动生成秘钥==
ssh-keygen -t rsa -C "邮箱地址"
然后将id_rsa.pub
复制到Github --> Settings --> SSH and GPG keys --> New SSH key
远程跟踪分支
远程跟踪分支是记录远程分支状态的分支, 以remote/branch的方式命名, 它们是只读且不能移动的本地分支,只有当使用git fetch
去拉取远程分支的最新状态时它们才会自动移动
本地的origin/HEAD
远程跟踪分支代表远程库中最新版本
虽然没有多大用处, 但是git支持修改默认的远程分支的指向
git remote set-head <远程库名字> -a|--auto # 根据远程库当前指向自动确定
git remote set-head <远程库名字> -d|--delete # 删除远程库的默认分支
git remote set-head <远程库名字> <本地分支名> # 将分支设为远程库的默认分支
要一定说它有什么用, 我试了好久才试出一种(真的试了好久~~)
根据git-remote documentation中对set-head
的解释:
set-head
Sets or deletes the default branch (i.e. the target of the symbolic-ref refs/remotes//HEAD) for the named remote. Having a default branch for a remote is not required, but allows the name of the remote to be specified in lieu of a specific branch. For example, if the default branch for origin is set to master, then origin may be specified wherever you would normally specify origin/master.
意思很简单明确: 假如使用set-head
命令指定了远程默认分支是master, 则以后origin就等于origin/master
==来看看我理解过后的应用吧==
- 将本地已有仓库的两个分支上传进未初始化的远程库,之后远程库就会初始化并产生对应的远程分支
- 初始实验我的想法
- 这次实验验证了我的想法
- 最后一次实验,得出结论
当第一次向远程仓库推送一个远程仓库没有的新分支时,本地就会生成远程跟踪分支,远程仓库就会生成远程分支
git push origin master # 本地生成远程追踪分支 origin/master 远程仓库生成远程分支
git push origin dev # 本地生成远程追踪分支 origin/dev 远程仓库生成远程分支
当远程分支被成员删除时, 本地的远程跟踪分支同样是不会自动更新的, 通过以下这些命令我们可以查询/清除本地的无用远程跟踪分支 (远程分支已被删除但是本地远程跟踪分支还在)
git remote prune -n <远程仓库名> # 查看本地的无用远程跟踪分支
git remote prune --dry-run <远程仓库名> # 查看本地的无用远程跟踪分支
git remote prune <远程仓库名> # 清除本地的无用远程跟踪分支
示例: 小组成员1删除了远程仓库的issue分支, 隔了段时间不明情况的小组成员2想看看远程分支issue有没有好玩的东西,结果一pull发现报错:远程分支不存在。 一问其他人才发现该远程分支早就被删了, 于是在本地执行该命令删掉本地的远程跟踪分支
git remote prune origin # 删掉本地的远程跟踪分支 origin/issue
远程分支
远程分支就是在远程仓库上的普通分支
跟踪分支
跟踪分支是从远程跟踪分支上生成的本地分支 或 将已经存在的本地分支与远程跟踪分支相关联形成跟踪分支, 跟踪分支可以看作是本地分支与远程跟踪分支紧密结合(可以看做一体)
==总结来说可以认为==
本地分支 + 远程跟踪分支 = 跟踪分支
这样以后可以直接在本地分支中使用git push
、git pull
命令对远程分支进行操作, 而不再以远程跟踪分支作为媒介
当我们clone一个仓库时, 本地分支情况就会是master与origin/master, 此时这个==master默认就是一个跟踪分支==, 这也是为什么clone下来的仓库在下一次进行拉取、推送操作时可以直接git push
、git pull
(自动识别对应的远程分支), 而不用git push <remote> <branch>
将已有的本地分支与远程跟踪分支"合并"变成跟踪分支 此操作需要本地分支 与 远程跟踪分支都已经存在, 否则报错
git branch --set-upstream-to=<upstream> <branch> # 前提是远程分支已经存在与本地同名的分支, 否则会报错 所以一般执行该命令前还需要执行`git push remote branch`
# 以上命令可简写为
git branch -u <upstream> <branch> # 将本地分支与远程分支关联后本地分支变成跟踪分支 本地的branch不存在需要创建,否则报错
# git branch -u origin/master master <=> git branch -u origin/master
示例: 将已有本地分支dev和本地远程跟踪分支origin/dev 的建立成跟踪分支 => 使得dev成为跟踪分支
git branch --set-upstream-to=origin/dev dev
==在第一次推送到远程仓库时==, 将已有的本地分支与远程跟踪分支(push时会在本地自动创建) "合并" 变成跟踪分支 此操作不需要远程跟踪分支存在
git push --set-upstream <远程仓库别名> <分支名> # 推送本地分支并将该本地分支变为跟踪分支,以后拉取/推送可以简化 由于git push会自动创建远程分支,所以无需担心同名远程分支是否存在
# 以上命令可简写为
git push -u <远程仓库别名> <分支名>
示例: 将已有本地分支problem推送到远程仓库, 要求: 在本地创建远程跟踪分支origin/problem、在远程库创建远程分支problem
git push --set-upstream origin problem
git push -u origin problem
#################### 总结 ###################
git push -u origin dev
# 等价于以下这些两条命令
git push origin dev # 创建远程分支 & 将本地分支推送到远程仓库
git branch -u origin/dev dev # 将本地的dev分支与本地的dev远程跟踪分支关联起来
# 对应的非简写写法
git push --set-upstream origin dev
# 等价于以下这些两条命令
git push origin dev
git branch --set-upstream-to=origin/dev dev
#################### 总结 ###################
生成一个新的本地分支与远程跟踪分支合并作为跟踪分支
使用场景: 远程仓库有master和dev分支, ==小组成员clone远程仓库时只会clone下来master分支==但是成员们都要在dev分支下开发, 所以干脆在本地创建dev分支的同时与远程跟踪分支一起变为跟踪分支 ==注意: 如果远程分支原本就有多个分支则第一次克隆就会在本地产生对应多个远程跟踪分支==
git checkout -b 本地分支 远程跟踪分支 # 根据远程跟踪分支创建并切换到本地分支(跟踪分支)
# 以上命令可简写为
git checkout --track 远程跟踪分支 # 这样就会创建并切换到一个同名的本地分支(跟踪分支)
示例: 远程分支有issue, 今天新入职的小伙伴需要解决这个问题(需要拉取这个远程分支)
git checkout -b issue origin/issue
git checkout --track origin/issue
取消跟踪分支
git branch --unset-upstream <branch> # 取消跟踪分支
示例: 取消本地的bug跟踪分支
git branch --unset-upstream bug
远程协作常用命令部分
查看所有分支是否是跟踪分支
git branch -vv
将本地仓库与远程仓库关联
git remote add <远程仓库别名(一般取origin)> <远程仓库URL>
将本地仓库与远程仓库解除关联 ==此操作不会影响远程仓库==
git remote rm 远程仓库别名
远程仓库重命名
git remote rename <旧别名> <新别名>
克隆远程仓库 克隆下来的仓库默认别名是origin 远程库有多个远程分支也只会将master分支克隆下来,其他需要手动添加 clone下来的master分支默认就是跟踪分支
git clone 远程仓库URL
将本地分支推送到远程仓库
git push <远程仓库别名> <分支名> # 远程分支将与本地分支同名
git push <远程主机名> <本地分支名>:<远程分支名> # 本地分支与远程分支分别命名
# 例如: git push <远程仓库别名> <分支名>其实是个简化操作
# 真正展开是 git push <远程仓库别名> <本地分支名>:<远程分支名>
# 所以假如 git push origin hello:world 则意思是将本地分支hello推送到远程分支world, 同时建立远程跟踪分支 origin/world
git push --set-upstream <远程仓库别名> <分支名>
git push -u <远程仓库别名> <分支名> # 推送本地分支并将该本地分支变为跟踪分支 以后拉取/推送可以简化
将远程仓库的远程分支 更新同步 本地的远程跟踪分支。远程仓库的分支更新项目协作者的电脑端是无法自动获知的 (本地不会自动生成远程跟踪分支),需要手动fetch获取(在本地的表现就是生成远程跟踪分支)
git fetch <remote> # 全量获取整个远程仓库(所有分支)
git fetch <remote> <branch> # 获取整个远程仓库(特定分支)
在分支作为跟踪分支后, 获取远程仓库的更新到本地分支
git pull <远程仓库别名> <分支名>
将已有本地分支变成跟踪分支, 要求已有远程分支且与本地分支同名
git branch --set-upstream-to=<远程跟踪分支> <本地分支>
git branch -u <远程跟踪分支> <本地分支>
将已有本地分支变成跟踪分支, 还没有远程分支, 但之后会在本地创建同名远程跟踪分支, 远程库创建同名远程分支
git push --set-upstream <远程仓库别名> <本地分支>
git push -u <远程仓库别名> <本地分支>
查看远程仓库的别名(信息少)
git remote show # 查看远程仓库别名
git remote # 查看远程仓库别名
查看远程仓库使用的别名与URL(信息中, 常用)
git remote -v
查看远程仓库的相关信息(信息多)
git remote show [远程仓库别名]
删除远程分支
git push <remote> --delete <branch>
# 删除远程仓库的dev分支 git push origin --delete dev
解决冲突
产生冲突的情况有两种, ==分别发生在本地与远程库的push和pull操作==:
情况1: 成员1、成员2在本地各自修改同一文件A, 其中一成员向远程库推送, 当另一成员也向远程库推送时, 冲突情况1发生
成员1修改本地的conflict1.txt文件, 并向远程推送
成员2也对本地的conflict1.txt文件进行修改, 然后一推送就会发生冲突了
冲突情况1解决方法: 先将远程库的更新进行拉取,解决完对应文件产生的冲突后,就能正常推送了
情况2: 成员1在本地修改文件B并已经向远程库提交了, 成员2也在文件B上做任务但是这个任务需要依赖成员1对文件B做出的修改, 这就需要成员2在编辑文件B的过程中更新本地。 于是冲突发生
成员1在本地修改文件B并已经向远程库提交
成员2也在文件B上做任务但是这个任务需要依赖成员1对文件B做出的修改
冲突情况2解决方法: 将本地的修改暂时stash起来,然后拉取远程库更新后进行冲突手动解决,之后就能正常推送了
Pull Request
当我们在遨游github/gitee开源世界时,如果发现一些好玩的项目。我们可能就会想弄下来自定义一下
那怎么弄下一个库来就很简单、自然了
git clone <远程仓库地址>
但是这样子有个显著的问题: ==当我们对弄下来的库做了一番自定义修改后想要push进远程库时,会提示我们没有权限==。这是因为我们clone的库是别人团队的,我们在没有收到邀请成为团队成员的情况下,当然不能使用push等增删改仓库代码的操作, 要是每个人都能这样不就天下大乱了吗~~
于是为了能够对仓库有完整的权限操作,最简单的办法就是:
- clone别人的项目仓库
- 把clone下来的本地仓库(目录)的
.git
文件夹删掉 - 然后
git init
初始化仓库 - 一顿
git add
等操作后 - 在自己的github/gitee账号下创建远程仓库后, 将该已经"私有化"的项目push上去,这样以后我们就能随意修改啦
可是这样未免太麻烦了, 于是github/gitee为我们提供了能够迅速克隆复制出一份别人项目副本到自己账号下的功能, 叫Fork, 中文名也叫: 派生
github Fork
gitee Fork
测试使用Fork
能够自定义修改开源项目只是派生的其中一个功能,开源运动今天之所以能够运行的如火如荼与派生的另一功能Pull Request有很大的关系。它能够让一众高手对一个项目进行添砖加瓦,==所有人都能将对项目的进行修改后向项目所有者提Pull Request==,从而让项目能够更好地发展
当派生出的项目中的代码被修改后就会显示可以向源项目提出Pull Request
项目作者可以看到别人提的Pull Requests
在PR页面可以采纳别人的代码或者跟别人在线互怼?
到这就完成了一次Pull Request了
开源作者甚至能够向曾经提出Pull Request的人请求支援
PR在Merge前可以被随时关闭和打开
项目作者能不能关闭(操作)PR取决于你提PR时允不允许项目拥有者操作
要是项目作者关闭了PR是不是就意味着被拒了,嘤嘤嘤~~
写在最后
断断续续学了一星期,写的也是罗里吧嗦的。
但是力求详尽,能在以后给自己作为参考
也希望这篇git学习笔记能够帮到各位
不对的地方欢迎指出, 有疑问可以一起交流交流♂嘿嘿
转载自:https://juejin.cn/post/6948437028237377566