快速参考

1. git常用指令

了解工作区、暂存区、版本库 git tag 打标签

# 在本地用户(root和user是两个用户)设置自己gitlab账户的用户名和密码,${GIT_USER_NAME}替换为自己的gitlab账户名,${GIT_USER_PASSWD}替换为自己的gitlab账户密码(用于CPMAddPackage克隆指定的仓库)
git config --global credential.helper store
echo "http://${GIT_USER_NAME}:${GIT_USER_PASSWD}@git.aubo-robotics.cn:8001" > ~/.git-credentials 

# 创建仓库 用于把当前所在目录(如果你使用 Windows 系统,为了避免遇到各种莫名其妙的问题,请确保各级目录名不包含中文)变成 git 可以管理的仓库
git init 

# 添加文件 git add
# 添加一个或多个文件到暂存区
git add [file1] [file2] ...

# 添加指定目录到暂存区,包括该目录中的子目录
git add [dir]

# 同时添加文件和目录:
git add [dir] [file]

# 添加当前目录下的所有文件到暂存区:
git add .


# 删除文件 git rm 
# 用于删除工作区文件,并且将这次删除放入暂存区,相当于 rm 指令与 git add 两条指令的叠加。
# 文件已提交至仓库,且工作区和暂存区均无更改
# 工作区和暂存区均删除文件
git rm [file1] [file2] ...

# 工作区和暂存区均删除整个目录
git rm -r [dir]

# 工作区和暂存区均删除所有文件和目录 
git rm -r *

# 文件已提交至仓库,工作区或暂存区有更改
#  - 工作区保留、暂存区删除文件,同时被删除项变为未跟踪状态
git rm --cached [file1] [file2] ...

#  - 工作区保留、暂存区删除整个目录,同时被删除项变为未跟踪状态
git rm --cached -r [dir]

#  - 工作区保留、暂存区均删除所有文件和目录,同时被删除项变为未跟踪状态
git rm -r *

#  - 工作区和暂存区均删除文件
git rm -f [file1] [file2] ...

#  - 工作区和暂存区均删除整个目录
git rm -fr [dir]

#  - 工作区和暂存区均删除所有文件和目录
git rm -fr *

# 文件已添加至暂存区
#  - 工作区保留、暂存区删除文件,同时文件变为未跟踪状态
git rm --cached [file1] [file2] ...

#  - 工作区保留、暂存区删除整个目录,同时目录中的所有文件变为未跟踪状态
git rm --cached -r [dir]

#  - 工作区保留、暂存区均删除所有文件和目录,同时被删除项变为未跟踪状态
git rm -r *

#  - 工作区和暂存区均删除文件
git rm -f [file1] [file2] ...

#  - 工作区和暂存区均删除整个目录
git rm -fr [dir]

#  - 工作区和暂存区均删除所有文件和目录
git rm -fr *

# 注意:未跟踪的文件不能使用 git rm 来删除

# 提交,将暂存区内容提交至仓库中
# 将暂存区所有内容提交至仓库,message 是备注信息
git commit -m [message]

# 将暂存区指定文件提交至仓库,message 是备注信息
git commit [file1] [file2] ... -m [message]

# 文件已经提交至仓库,再次修改文件后不需要执行 git add 命令,直接提交至仓库
git commit -am [message]

# 编辑多行提交信息
git commit -a

# 修改提交 
# 用于修改最后一次的提交信息或提交内容,commit id 会改变,用法详见下文"修改最近一次提交内容"的的方法一。
git commit --amend


# 查看状态 
# 用于查看在你上次提交之后是否有对文件进行再次修改,当前仓库状态信息包含:
#     当前所在分支
#     有未跟踪的文件时会给出提示
#     有已提交至仓库中的文件再次被修改时会给出提示
git status


# 推送分支
# 一般格式
git push <远程主机名> <本地分支名>:<远程分支名>

# 若本地分支推送到与之存在追踪关系的远程分支(通常两者同名),可使用简化格式,如果该远程分支不存在,则会被新建
git push <远程主机名> <本地分支名>

# 不管是否存在对应的远程分支,将本地的所有分支都推送到远程主机,这时需要 --all 选项
git push --all <远程主机名>

# 若确定本地仓库分支与远程仓库分支存在冲突,且远程仓库可覆盖,可执行
git push -f <远程主机名> <本地分支名>:<远程分支名>

# 拉取分支
# 用于从远程仓库分支获取代码并合并本地仓库分支,其实就是 git fetch 和 git merge FETCH_HEAD 指令的叠加

# 一般格式
git pull <远程主机名> <远程分支名>:<本地分支名>

# 远程分支是与当前分支合并
git pull <远程主机名> <远程分支名>


# 变基
git rebase
# 合并或修改当前分支最近的4次提交,-i表示交互式
git rebase -i HEAD~4

# 合并或修改当前分支自1234567(commit id 前7位)以来的提交,-i表示交互式
git rebase -i 1234567

# 将master分支上所有新增提交合并到当前分支,合并后,当前分支的新增提交均在 master 分支的提交之后
git rebase -i master

# rebase本地存储的origin远程对应的master分支,为了保证本地与远程一致,可以先使用git fetch origin命令
git rebase -i origin/master   

# 将指定远程仓库的全部更新取回本地
git fetch <远程主机名>

# 将指定远程仓库的指定分支的更新取回本地
git fetch <远程主机名> <分支名>


# 查看历史提交
# git log 用于查看历史提交,不包含已经删除的提交记录和 git reset 的操作记录
# 输出 commit id, author, date, commit message
git log

# 仅输出 commit id 前7个字符串和 commit message
git log --oneline

# 在git log 的基础上输出文件增删改的统计数据
git log --stat

# 输出每个commit具体修改的内容,输出的形式以diff的形式给出
git log -p

# 自定义输出的信息(包含字体和颜色),感兴趣可参考 https://www.cnblogs.com/bellkosmos/p/5923439.html,此处给出 一个例子
git log --pretty="%h %cd %an ==>%s" --date=format:'%Y-%m-%d %H:%M'
# 输出结果:
#  9e36820 2021-01-17 11:11 lianglongxiao ==>测试持续集成
#  4ff2c7b 2020-10-19 18:51 lianglongxiao ==>初步完成运动参数设置过程

# 查看dev中log有的commit,而master中log没有的commit
git log dev ^master

# 用于查看历史提交,包含已经删除的提交记录和git reset的操作记录,在版本恢复中很有作用!
git reflog


# git reset 用于回退版本,可以退回某次提交的版本,
# 将全部内容从当前版本回退3个版本,保留工作区和暂存区的内容,简单来说就是变为未提交状态
git reset --soft HEAD~3 
git reset --soft HEAD^^^

# 将全部内容从当前版本回退2个版本,保留工作区内容,简单来说就是变为未跟踪状态
git reset --mixed HEAD~2 
git reset --mixed HEAD^^ 
git reset HEAD~2

# 将全部内容从当前版本回退到指定版本(commit id为1234567),工作区内容也一起回退
git reset --hard 1234567

# 将部分目录或文件从当前版本回退到指定版本(commit id 为1234567),工作区内容也一起回退
git reset --hard 1234567 [dir] [file]


# git submodule 用于将一个git仓库作为另一个git仓库的子目录。它能让你将另一个仓库克隆到自己的项目中,同时还保持提交的独立
# 将已存在的 Git 仓库(url 为远程仓库地址)添加为正在工作的仓库的子模块,若不指定path,子模块会将子项目放到一个与仓库同名的目录中path
git submodule add [url] [path]

# 克隆含子模块的仓库时,克隆仓库操作完成后,用于初始化并更新子模块
git submodule update --init --recursive

# 克隆含子模块的仓库时,同步初始化并更新子模块,url为主仓库远程地址
git clone --recurse -submodules [url]

# 仓库创建完成后,日常拉取所有子模块
git submodule foreach git pull

# 子模块的远程仓库地址更新后,首先需要手动更改 .gitmodules 文件中的子模块url地址,然后将新的url复制到本地配置中
git submodule sync


# git cherry-pick  用于部分代码变动(某几个提交)从一个分支转移到另一个分支
# 将其他分支的指定提交应用于当前分支
git cherry-pick [commit id]

# 将其他分支的最新提交应用于当前分支
git cherry-pick [branch name]

# 将其他分支的多个指定提交应用于当前分支
git cherry-pick [commit id 1] [commit id 2]

# 将其他分支的A到B的所有提交(不包含A)应用于当前分支
git cherry-pick A..B

# 将其他分支的A到B的所有提交(包含A)应用于当前分支
git cherry-pick A^..B

# 注意:
#     -e,--edit:打开外部编辑器,编辑提交信息
#     -n,--no-commit:只更新工作区和暂存区,不产生新的提交
#     -x:在提交信息的末尾追加一行(cherry picked from commit ...),方便以后查到这个提交是如何产生的
#     -s,--signoff:在提交信息的末尾追加一行操作者的签名,表示是谁进行了这个操作
#     -m parent-number,--mainline parent-number:如果原始提交是一个合并节点,来自于两个分支的合并,那么 Cherry pick 默认将失败,
#         因为它不知道应该采用哪个分支的代码变动。-m 配置项告诉 git,应该采用哪个分支的变动。它的参数parent-number是一个从1开始的整数,
#         代表原始提交的父分支编号,可以从git log中查看编号
#     如果操作过程中发生代码冲突,Cherry pick 会停下来,让用户决定如何继续操作
#         --continue:用户解决代码冲突后,第一步将修改的文件重新加入暂存区(git add .),第二步使用 git cherry-pick --continue 命令,让 Cherry pick 过程继续执行
#         --abort:发生代码冲突后,退出 Cherry pick,回到操作前的样子
#         --quit:发生代码冲突后,退出 Cherry pick,但是不回到操作前的样子


# git diff 用于比较已跟踪的文件的不同
# 显示暂存区和工作区的差异,若不指定file或dir,默认比较全部文件
git diff [file] [dir]

# 显示暂存区和版本库的差异,若不指定file或dir,默认比较全部文件
git diff --cached [file] [dir]

# 显示工作区和版本库的差异,若不指定file或dir,默认比较全部文件
git diff HEAD [file] [dir]

# 比较两个分支上最后commit的内容的差别
git diff branch1 branch2

# 比较两个分支上最后commit的指定文件的差别
git diff branch1 branch2 [file]

# 比较两个分支上最后commit的有差异的文件(不详细,没有对比内容)
git diff branch1 branch2 --stat


# git stash 用于备份当前工作区已跟踪的文件
# 当前工作区内容不具备提交条件,但此时需要切换分支、拉取代码进行其它工作,此时需要git stash保存现场,待其它工作完成后,使用git stash pop恢复现场
# 保存当前工作区的内容,保存到git栈中,工作区内容与当前版本库保持一致,save及其后内容用于标记本次stash,可省略:
git stash save "test stash"

# 显示git栈中的所有工作区内容的备份
git stash list

# 清空git栈
git stash clear

# 显示做了哪些改动,默认show第一个存储,如果要显示其他存贮,后面加stash@{$num},比如第二个
git stash show stash@{1}:

# 从git栈中获取到倒数第3次stash进去的内容,用以恢复工作区的内容,会删除栈中对应的stash,其中stash@{2}可以省略,表示倒数最后一次的stash
git stash pop stash@{2}

# 从git栈中获取到最后一次stash进去的内容,用以恢复工作区的内容,同时保留stash
git stash apply stash@{0}
git stash apply


# git checkout 用于切换分支或恢复工作区文件(文件必须为已跟踪状态)
# 在不改变工作区与暂存区内容的情况下,从当前分支切换到 dev 分支
git checkout dev

# 在不改变工作区与暂存区内容的情况下,创建新分支 dev,并从当前分支切换到 dev 分支
git checkout -b dev

# 把readme.txt文件在工作区的修改全部撤销
# 这里有两种情况:
#  一种是readme.txt自修改后还没有被放到暂存区,现在撤销修改就回到和版本库一模一样的状态
#  一种是readme.txt已经添加到暂存区后,又作了修改,现在撤销修改就回到添加到暂存区后的状态
git checkout -- readme.txt


# 以递归方式把所有后缀名为.txt 的文件在工作区的修改全部撤销
git checkout -- "*.txt"

# 把工作区全部文件的修改全部撤销
git checkout .

# 查看所有远程仓库
get remote -v

基本开发流程

# 从远程clone仓库到本地
git clone ssh://...

# 切换到开发分支
git checkout -b <devel>

# 添加修改
git add .
# 编辑commit记录
git commit -a

# 更新远程分支(主要是更新origin/master主分支)
git fetch origin
git rebase origin/master

# 如果遇到CONFLICT冲突,在代码中解决冲突,然后
git add .
# 继续执行rebase
git rebase --continue
# 如果rebase操作失误,可以执行abort操作,取消本次rebase
git rebase --abort
# 如果在操作过程中不小心关闭窗口,可以重新打开终端,切换到仓库目录
git rebase --edit-todo

# rebase完成之后,需要push到远程的devel分支,但是由于rebase改变了devel分支的历史记录,在确保无误的问题下,需要强制push
git push -f origin <devel>
# 上述强制push有丢失更改的风险,保险起见,可以在远程新建一个分支
git push origin <devel>:<devel_fater_rebase>

自己在多台电脑上开发同一个分支

# 添加修改
git add .
# 编辑commit记录
git commit -a

# 更新远程分支到本地分支
git fetch origin
git rebase origin/<devel>
# 或者
git pull --rebase origin <devel>
# 以上操作是为了避免git pull添加不必要的commit记录,并且搞乱自己分支

# rebase完成之后,需要push到远程的devel分支,但是由于rebase改变了devel分支的历史记录,在确保无误的问题下,需要强制push
git push -f origin <devel>
# 上述强制push有丢失更改的风险,保险起见,可以在远程新建一个分支
git push origin <devel>:<devel_fater_rebase>

远程分支替换本地分支

# 拉取远程分支
git fetch origin master
# 然后使用远程分支重置本地分支
git reset --hard origin/master


# 删除您的本地分行
git branch -d master
# 获取最新的远程分支
git fetch origin master
# 重建基于远程的本地分支
git checkout -b master origin/master

修改最近一次提交内容

# 方法一: 可以修改commit message,也可以修改提交内容
#修改需要修改的地方(只是修改commit message就不用做)
git add .  #这一步如果只是修改commit message不用输入
git commit --amend

# 方法二:与上面方法基本一致,也可以修改提交内容和commit message
git reset HEAD^
#修改需要修改的地方(只是修改commit message就不用做)
git add . #这一步如果只是修改commit message不用输入
git commit -m "new commit message" # 或者git commit -c ORIG_HEAD

# 方法三:提交到了错误的分支上的处理
git reset HEAD~ --soft
git stash
# 切换到正确的分支
git checkout name-of-the-correct-branch
git stash pop
git add .    # 或添加特定文件
git commit -m "你的提交说明"

# 方法四:rebase
# 查看修改
git rebase -i master~1 #最后一次
git rebase -i master~5 #最后五次
git rebase -i HEAD~3   #当前版本的倒数第三次状态
git rebase -i 32e0a87f #指定的SHA位置
# 需要注意最后的^号,意思是commit id的前一次提交
git rebase -i "5b3ba7a"^

撤销

未进行git push前的所有操作,都是在“本地仓库”中执行的。我们暂且将“本地仓库”的代码还原操作叫做“撤销”!

# 情况一:文件被修改了,但未执行git add操作(工作区内撤销)
git checkout fileName
git checkout .

# 情况二:同时对多个文件执行了git add操作,但本次只想提交其中一部分文件
git reset HEAD <filename>

# 情况三:文件执行了git add操作,但想撤销对其的修改(index内回滚)
git reset HEAD fileName # 取消暂存
git checkout fileName # 撤销修改

#情况四:修改的文件已被git commit,但想再次修改不再产生新的commit
git add sample.txt # 修改最后一次提交 
git commit --amend -m "说明"

# 情况五:已在本地进行了多次git commit操作,现在想撤销到其中某次Commit
git reset [--hard|soft|mixed|merge|keep] [commit|HEAD]

具体参数和使用说明,请查看:Git Pro深入浅出(二)中的重置揭秘部分

删除子模块

rm -rf 子模块目录 删除子模块目录及源码
vi .gitmodules 删除项目目录下.gitmodules文件中子模块相关条目
vi .git/config 删除配置项中子模块相关条目
rm .git/module/* 删除模块下的子模块目录,每个子模块对应一个目录,注意只删除对应的子模块目录即可

从远端更新子模块

git submodule update --remote  # 主工程中执行

以下为指令输出结果:

remote: Enumerating objects: 7, done.
remote: Counting objects: 100% (7/7), done.
remote: Compressing objects: 100% (6/6), done.
remote: Total 21 (delta 2), reused 1 (delta 1), pack-reused 14
展开对象中: 100% (21/21), 完成.
来自 http://git.aubo-robotics.cn:8001/arcs/arcs_env
6a04d6b..41a84a9 master -> origin/master

[新分支] dev/smartpointer -> origin/dev/smartpointer 6a04d6b..30b6505 dev_lianglongxiao -> origin/dev_lianglongxiao
子模组路径 'arcs_env':检出 '41a84a9cf5654a0cae2a6638790e65c1905e3940'

cd arcs_env/  # 进入子模块arcs_env
git status   # 查看子模块状态

以下为指令输出结果:

头指针分离于 41a84a9
无文件要提交,干净的工作区

git log  # 查看子模块提交,41a84a9指的是远端master的最新提交

以下为指令输出结果:

41a84a9 - (HEAD -> master, origin/master, origin/HEAD) fix: 增加qt静默安装方式说明 (27 分钟前)
63e9eb1 - fix: gitlab-runner以root用户启动方式 (27 分钟前)
30b6505 - (origin/dev_lianglongxiao) feat: make_helper.sh中增加函数,用于同时打包同一个工程下的多个库或可执行程序,使用示例见nanolog模块 (47 分钟前)

git checkout master # 子模块切回master分支

以下为指令输出结果:

之前的 HEAD 位置是 41a84a9... fix: 增加qt静默安装方式说明
切换到分支 'master'
您的分支落后 'origin/master' 共 5 个提交,并且可以快进。
 (使用 "git pull" 来更新您的本地分支)

git rebase origin/master  # 当前分支合并master分支最新提交

以下为指令输出结果:

首先,回退分支以便在上面重放您的工作...
快进 master 至 origin/master。

cd ..  # 回到主工程目录
git config status.submodulesummary 1  # 显示子模块的更改摘要,以前设置过可以跳过该指令
git status   # 查看主工程状态,提示子模块有新提交

以下为指令输出结果:

尚未暂存以备提交的变更:
 (使用 "git add <文件>..." 更新要提交的内容)
 (使用 "git checkout -- <文件>..." 丢弃工作区的改动)

   修改: arcs_env (新提交)

子模组已修改但尚未更新:

arcs_env 6a04d6b...41a84a9 (5):

fix: 增加qt静默安装方式说明

git add .  # 添加修改至暂存区
git commit -m "refactor: 更新子模块arcs_env"

本地更新子模块并推送到远端

在主工程的子模块目录中进行的修改,并且主工程要依赖子模块的更新

cd arcs_env/  # 进入子模块arcs_env
git status   # 查看子模块状态

以下为指令输出结果:

位于分支 dev_lianglongxiao
您的分支领先 'origin/dev_lianglongxiao' 共 2 个提交。
 (使用 "git push" 来发布您的本地提交)
尚未暂存以备提交的变更:
 (使用 "git add <文件>..." 更新要提交的内容)
 (使用 "git checkout -- <文件>..." 丢弃工作区的改动)

   修改: 02_cheatlist.md
   修改: scripts/make_helper.sh

在子模块目录里提交、推送更新:

git add .
git commit -m "feat: 02_cheatlist.md中添加子模块更新相关的git操作说明"
git fetch
git rebase origin/master
git push -f origin dev_lianglongxiao

回到主工程目录,查看状态

cd ..
git status

以下为指令输出结果:

位于分支 dev_lianglongxiao
您的分支和 'origin/dev_lianglongxiao' 出现了偏离,
并且分别有 2 和 2 处不同的提交。
(使用 "git pull" 来合并远程分支)

尚未暂存以备提交的变更:
 (使用 "git add <文件>..." 更新要提交的内容)
 (使用 "git checkout -- <文件>..." 丢弃工作区的改动)

   修改: arcs_env (新提交)

子模组已修改但尚未更新:

arcs_env 41a84a9...5109863 (1):

feat: 02_cheatlist.md中添加子模块更新相关的git操作说明

在主工程目录里提交、推送更新:

git add .
git commit -m "feat: 更新arcs_env子模块"
git fetch
git rebase origin/master
git push -f origin dev_lianglongxiao

回滚到某次提交,在此基础上建立新的分支

git checkout [commit id]

将其它分支某次提交合入到本地当前分支,直接使用原始提交信息

在本地仓库中,有两个分支:branch1和branch2,我们先来查看各个分支的提交:

# 切换到branch2分支
root@ubuntu:~/auboData/gittest# git checkout branch1
# 查看提交
root@ubuntu:~/auboData/gittest# git log

 552e796 - feat: brancn2 edit c (16 分钟前)  30f19ec - (origin/master, origin/HEAD, master) feat: add c (3 周前)
 c3ce9ac - feat: add b (3 周前)  a62306e - feat: add a (3 周前)
* c28c3a8 - Initial commit (3 周前) <梁龙晓>

# 切换到branch1分支
root@ubuntu:~/auboData/gittest# git checkout branch1
# 查看最近三次提交
root@ubuntu:~/auboData/gittest# git log

 7d2a0e5 - feat: brancn1 edit c (17 分钟前)  30f19ec - (origin/master, origin/HEAD, master) feat: add c (3 周前)  c3ce9ac - feat: add b (3 周前)  a62306e - feat: add a (3 周前) * c28c3a8 - Initial commit (3 周前) <梁龙晓>

将branch2分支上的最近一次提交内容合入到branch1分支上

root@ubuntu:~/auboData/gittest# git cherry-pick 552e796

error: 不能应用 552e796... feat: brancn2 edit c
提示:冲突解决完毕后,用 'git add <路径>' 或 'git rm <路径>'
提示:对修正后的文件做标记,然后用 'git commit' 提交

可以看出,cherry-pick时,没有成功自动提交,这说明存在冲突,因此首先需要解决冲突,解决冲突后执行git add .,然后执行git commit 或git cherry-pick --continue:

root@ubuntu:~/auboData/gittest# git add .
root@ubuntu:~/auboData/gittest# git commit

branch1 edit c

 Conflicts:

       c

 

 似乎您正在做一个拣选提交。如果不对,请删除文件

       .git/CHERRY_PICK_HEAD

 然后重试。

 请为您的变更输入提交说明。以 '#' 开始的行将被忽略,而一个空的提交

 说明将会终止提交。

 

 日期: Wed Apr 7 10:12:25 2021 +0800

 

 位于分支 branch1

 您在执行拣选提交 552e796 的操作。

 

 要提交的变更:

       修改: c

修改提交信息后退出编辑器、保存,branch2分支上的最新一次提交成功合入到了branch1分支上。

root@ubuntu:~/auboData/gittest# git log

 c434251 - (HEAD -> branch2) feat: branch1 and brancn2 edit c (15 分钟前)  552e796 - feat: brancn2 edit c (28 分钟前)
 30f19ec - (origin/master, origin/HEAD, master) feat: add c (3 周前)  c3ce9ac - feat: add b (3 周前)
 a62306e - feat: add a (3 周前)  c28c3a8 - Initial commit (3 周前) <梁龙晓>

将其它分支某次提交合入到本地当前分支,重新编辑提交信息

root@ubuntu:~/auboData/gittest# git cherry-pick -e b244e9

修改提交信息后退出编辑器、保存,branch2分支上的最新一次提交成功合入到了branch1分支上: [branch2 940de17] feat: cherry-pick branch1 add d
 Date: Wed Apr 7 10:42:49 2021 +0800
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 d

root@ubuntu:~/auboData/gittest# git log

 940de17 - (HEAD -> branch2) feat: cherry-pick branch1 add d (35 秒钟前)  552e796 - feat: brancn2 edit c (48 分钟前)
 30f19ec - (origin/master, origin/HEAD, master) feat: add c (3 周前)  c3ce9ac - feat: add b (3 周前)
 a62306e - feat: add a (3 周前)  c28c3a8 - Initial commit (3 周前) <梁龙晓>

将其它分支所有特有的提交合入到本地当前分支

git cherry-pick ^HEAD [branchname]或git cherry-pick .. [branchname],这些提交是branchname的祖先但不是HEAD的祖先,比如,现在我的仓库中有两个分支,其提交历史如下图:

               C<---D<---E  branch1
              /
master   A<---B  
              \
               F<---G<---H  branch2
                         |
                         HEAD

如果我使用git cherry-pick ..branch1或者git cherry-pick ^HEAD branch1,那么会将属于branch2的祖先但不属于branch3的祖先的所有提交引入到当前分支branch3上,并生成新的提交,执行命令如下:

root@ubuntu:~/auboData/gittest# git cherry-pick ..branch1

[branch2 1ea344e] feat: branch1 add d
 Date: Wed Apr 7 10:42:49 2021 +0800
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 d
[branch2 0b3be14] feat: add f
 Date: Wed Apr 7 11:06:04 2021 +0800
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 f
[branch2 45ed6tg] feat: branch1 add f
 Date: Wed Apr 7 11:08:41 2021 +0800
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 d

执行后的提交历史如下:


               C<---D<---E  branch1
              /
master   A<---B  
              \
               F<---G<---H<---C'<---D'<---E'  branch2
                                          |
                                         HEAD

git cherry-pick常见问题

1、The previous cherry-pick is now empty, possibly due to conflict resolution.

原因:
在cherry-pick时出现冲突,解决冲突后本地分支中内容和cherry-pick之前相比没有改变,因此当在以后的步骤中继续git cherry-pick或执行其他命令时,由于此时还处于上次cherry-pick,都会提示该信息,表示可能是由于解决冲突造成上一次cherry-pick内容是空的。

解决方案:
1.执行git cherry-pick --abort取消上次操作。
2.执行git commit --allow-empty,表示允许空提交。

2、fatal: You are in the middle of a cherry-pick – cannot amend.

原因:
在cherry-pick时出现冲突,没有解决冲突就执行git commit --amend命令,从而会提示该信息。

解决方案:
首先在git commit --amend之前解决冲突,并完成这次cherry-pick。

比较文件的不同

比较「暂存区」与「工作区」之间的差异

首先查看仓库状态

root@ubuntu:~/auboData/gittest# git st

位于分支 branch2
尚未暂存以备提交的变更:
 (使用 "git add <文件>..." 更新要提交的内容)
 (使用 "git checkout -- <文件>..." 丢弃工作区的改动)

    修改: f

修改尚未加入提交(使用 "git add" 和/或 "git commit -a")

从仓库状态可知,此时暂存区为空,此时执行 git diff 指令会将工作区所有已跟踪文件的修改显示出来,现在比较文件不同:

root@ubuntu:~/auboData/gittest# git diff

diff --git a/f b/f
index e69de29..821adb1 100644
--- a/f
+++ b/f
@@ -0,0 +1,2 @@
+branch2
+branch3

此时我们将工作区改动添加至暂存区,再次比较不同:

root@ubuntu:~/auboData/gittest# git add .
root@ubuntu:~/auboData/gittest# git diff

可以看出,没有任何不同,因为工作区已经与暂存区同步了,没有任何不同

比较「暂存区」与「某次提交」之间的差异

上一步操作中,我们已经执行git add .将工作区改动同步到暂存区,此时我们比较暂存区与最新一次提交的不同:

root@ubuntu:~/auboData/gittest# git diff --cached 0b3be1 # 与 git diff --cached HEAD 等价

diff --git a/f b/f
index e69de29..821adb1 100644
--- a/f
+++ b/f
@@ -0,0 +1,2 @@
+branch2
+branch3

比较「暂存区」「工作区」与「某次提交」之间的差异

接上一步操作,我们再对文件f添加一行内容,但是不同步至暂存区,比较暂存区、工作区与最新一次提交的不同

root@ubuntu:~/auboData/gittest# git diff  0b3be1 # 与 git diff  HEAD 等价

diff --git a/f b/f
index e69de29..76e2936 100644
--- a/f
+++ b/f
@@ -0,0 +1,3 @@
+branch2
+branch3
+branch4

作为对比,我们再次比较暂存区与最新一次提交的不同:

root@ubuntu:~/auboData/gittest# git diff --cached 0b3be1 # 与 git diff --cached HEAD 等价

diff --git a/f b/f
index e69de29..821adb1 100644
--- a/f
+++ b/f
@@ -0,0 +1,2 @@
+branch2
+branch3

比较两个分支上最新提交的内容的差别

我们首先将branch2的修改提交,再比较branch2分支最新提交相对与branch1分支最新提交的不同:

root@ubuntu:~/auboData/gittest# git diff branch1 branch2

diff --git a/f b/f
index e69de29..76e2936 100644
--- a/f
+++ b/f
@@ -0,0 +1,3 @@
+branch2
+branch3
+branch4

交换指令中branch1、branch2位置,则是比较branch1分支最新提交相对与branch2分支最新提交的不同:

root@ubuntu:~/auboData/gittest# git diff branch2 branch1

diff --git a/f b/f
index 76e2936..e69de29 100644
--- a/f
+++ b/f
@@ -1,3 +0,0 @@
-branch2
-branch3
-branch4

生成补丁

git diff [commitId1] [commitId2] > [name].patch是将commitId2的提交相对于commitId1的提交的修改生成名称为[name].patch的补丁文件,特别的,我们将branch1分支最新提交相对与branch2分支最新提交的修改生成名称为1.patch的补丁文件

root@ubuntu:~/auboData/gittest# git diff branch2 branch1 > 1.patch

git format-patch 与git diff的区别在于前者打出来的patch中不带有提交信息

使用补丁

接上文,我们在branch2分支上使用1.patch打补丁

root@ubuntu:~/auboData/gittest# git apply 1.patch

查看仓库状态:

root@ubuntu:~/auboData/gittest# git st

位于分支 branch2
尚未暂存以备提交的变更:
 (使用 "git add <文件>..." 更新要提交的内容)
 (使用 "git checkout -- <文件>..." 丢弃工作区的改动)

   修改: f

可以看到patch已经打上去了,但是需要自己提交并写提交信息

生成补丁及打补丁的过程

1、执行命令 git am xxxx.patch 尝试直接打入补丁。因为我们使用的 patch 已经过时了,所以这一步肯定会报错并中断(注意,虽然命令停止执行了,但我们依然处于git am命令的运行环境中,可以通过git status命令查看到当前的状态)。
2、执行命令 git apply --reject xxxx.patch 自动合入 patch 中不冲突的代码改动,同时保留冲突的部分。这些存在冲突的改动内容会被单独存储到目标源文件的相应目录下,以后缀为 .rej 的文件进行保存。比如对 ./test/someDeviceDriver.c 文件中的某些行合入代码改动失败,则会将这些发生冲突的行数及内容都保存在 ./test/someDeviceDriver.c.rej 文件中。我们可以在执行 git am 命令的目录下执行 find -name .rej 命令以查看所有存在冲突的源文件位置。
3、依据 步骤2 中生成的
.rej 文件内容逐个手动解决冲突,然后删除这些 *.rej 文件。完成这一步骤的操作后,我们就可以继续执行 git am 的过程了。
4、执行命令 git status 查看当前改动过的以及新增的文件,确保没有多添加或少添加文件。
5、执行命令 git add . 将所有改动都添加到暂存区(注意,关键字add后有一个小数点 . 作为参数,表示当前路径)。
6、执行命令 git am --resolved 继续 步骤1 中被中断的 patch 合入操作。合入完成后,会有提示信息输出。
7、执行命令 git log 确认合入状态。

git-rebase操作

有几个命令需要注意一下:

pick:正常选中
reword:选中,并且修改提交信息
edit:选中,rebase时会暂停,允许你修改这个commit(参考这里
squash:选中,会将当前commit与上一个commit合并
fixup:与squash相同,但不会保存当前commit的提交信息
exec:执行其他shell命令

分支合并

1.我们先从 master 分支切出一个开发分支,进行开发:

(master) git checkout -b feature1

git1

2.这时候,你的同事完成了一次 hotfix,并合并入了 master 分支,此时 master 已经领先于你的 feature1 分支了: git2

3.恰巧,我们想要同步 master 分支的改动,首先想到了 merge,执行:

(feature1) git merge master

git3

图中绿色的点就是我们合并之后的结果,执行:

(feature1) git log

就会在记录里发现一些 merge 的信息,但是我们觉得这样污染了 commit 记录,想要保持一份干净的 commit,怎么办呢?这时候,git rebase 就派上用场了。

4.让我们来试试 git rebase ,先回退到同事 hotfix 后合并 master 的步骤:
git4

5.使用 rebase 后来看看结果:

(feature1) git rebase master

这里补充一点:rebase 做了什么操作呢?

首先,git 会把 feature1 分支里面的每个 commit 取消掉; 其次,把上面的操作临时保存成 patch 文件,存在 .git/rebase 目录下; 然后,把 feature1 分支更新到最新的 master 分支; 最后,把上面保存的 patch 文件应用到 feature1 分支上;

git5

commit 记录我们可以看出来,feature1 分支是基于 hotfix 合并后的 master ,自然而然的成为了最领先的分支,而且没有 mergecommit 记录,是不是感觉很舒服了。

6.在 rebase 的过程中,也许会出现冲突 conflict。在这种情况,git 会停止 rebase 并会让你去解决冲突。在解决完冲突后,用 git add 命令去更新这些内容。

注意,你无需执行 git-commit,只要执行 continue

git rebase --continue

这样 git 会继续应用余下的 patch 补丁文件。

7.在任何时候,我们都可以用 --abort 参数来终止 rebase 的行动,并且分支会回到 rebase 开始前的状态。

git rebase —abort

更多 rebase 的使用场景

git-rebase 存在的价值是:对一个分支做「变基」操作。

  1. 当我们在一个过时的分支上面开发的时候,执行 rebase 以此同步 master 分支最新变动;
  2. 假如我们要启动一个放置了很久的并行工作,现在有时间来继续这件事情,很显然这个分支已经落后了。这时候需要在最新的基准上面开始工作,所以 rebase 是最合适的选择。

为什么会是危险操作?

根据上文来看,git-rebase 很完美,解决了我们的两个问题:

  1. 合并 commit 记录,保持分支整洁;
  2. 相比 merge 来说会减少分支合并的记录;

如果你提交了代码到远程,提交前是这样的:

git2

提交后远程分支变成了这样:

git5

而此时你的同事也在 feature1 上开发,他的分支依然还是:

git6

那么当他 pull 远程 master 的时候,就会有丢失提交纪录。这就是为什么我们经常听到有人说 git rebase 是一个危险命令,因为它改变了历史,我们应该谨慎使用。

除非你可以肯定该 feature1 分支只有你自己使用,否则请谨慎操作。

结论:只要你的分支上需要 rebase 的所有 commits 历史还没有被 push 过,就可以安全地使用 git-rebase来操作。

参考

rebase
git-rebase 使用总结
git 中的 rebase操作
git-rebase vs git-merge 详解

2. gitlab

# 索引到issue
close #1

2. ssh指令

# 远程登录
ssh

# scp远程拷贝
scp

3. dh_make

# 创建新的debian文件夹
dh_make -s -e louwei@aubo-robotics.cn -n -p libcstone_0.0.1 

# 构建
fakeroot debian/rules binary-arch  
# 清除
fakeroot debian/rules clean
# 安装
sudo dpkg -i build/*.deb

# 新增发布版本
dch -v x.y.z
# 然后编辑changelog
dch -r

4. gitbook

# 生成文档结构
gitbook init
# 预览
# 首先切换到repo的根目录(README.md所在的目录),然后在浏览器中打开`http://localhost:4000`即可看到相关文档说明
gitbook serve

# 生成html
gitbook build

1. 排版
2. gitbook基本操作
3. 移除GitBook目录下方的“本书使用GitBook发布”字样
4. 生成pdf

5. clang-format

clang-format -i 

# 另外clang-format还提供一个clang-format-diff.py脚本,用来格式化patch,code review提交代码前,可以先跑一下这个脚本,看有没有问题。

# 格式化最新的commit,并直接在原文件上修改
git diff -U0 HEAD^ | clang-format-diff.py -i -p1






results matching ""

    No results matching ""