TortoiseGit的使用

目录

1. Concept

2. 用Git Submodule方式将SDK链接到FW_TLC仓库中

2.1. 日常开发使用说明

2.2. 将SDK提升为项目(RD无需关注)

3. gitlab远程子仓库配置为SSH

3.1. Flow

3.2. Q&A

4. Revert

4.1. Gitlab Revert

4.2. TortoiseGit Revert

5. Reset

5.1. Revert VS Reset

6. rebase

7. merge

7.1. Flow

7.2. Edit conflict

8. Abort Merge

9. Stash changes

10. Clean up

11. Ignore

12. Amend

13. Cherry Pick

14. Q&A

1. Concept

HEAD:HEAD是一个指向你正在工作中的本地分支的指针。简单来讲,就是你现在在哪儿,HEAD就指向哪儿。例如当前我们处于master分支,所以HEAD这个指针指向了master分支。

working tree:实际操作的目录被称为工作树,也就是工作区域。

Index:索引是工作树和仓库之间的一个暂存区域。在这个区域放置了你想要提交给仓库的文件,如工作树的代码通过右键TortoiseGit → add添加到索引中,通过commit 则将索引区域的文件提交到本地仓库中。

2. 用Git Submodule方式将SDK链接到FW_TLC仓库中

为了降低代码维护成本,在YS8803项目开发中,计划将Flycode / LLF等Project共用同一份SDK代码,于是将原本存放在各个project下的SDK文件夹提取出来,单独建立一个SDK仓库,让Flycode / LLF Project都调用该SDK仓库下的SDK代码。在此我们将FW_TLC称为主项目,将SDK称为子项目。

2.1. 日常开发使用说明

代码clone:因为主项目FW_TLC和子项目SDK的包含关系在submodule中体现了,所以只需要clone FW_TLC仓库,clone Main Branch后FW_TLC下的SDK文件夹是空的,需在FW_TLC文件夹内右键TortoiseGit -> Submodule Update。switch branch时需要在switch完成界面点击Update Submodules。

2.2. 将SDK提升为项目(RD无需关注)

以下内容是将旧的代码仓库结构调整为SDK独立仓库,并通过submodule方式将子项目链接到主项目的操作步骤,开发人员在日常开发中无需关注。

(1)将Git 工程中的文件夹(模块)提升为项目

完成上述步骤后,本地FW_TLC文件夹下就只剩下SDK文件夹,然后再在该目录下将文件推送到服务器端的SDK仓库,就可以将原本在FW_TLC文件夹下的SDK文件夹推送到外层的SDK仓库,并且能保留之前的所有log。

(2)将SDK仓库链接到FW_TLC仓库

FW_TLC文件夹内右键TortoiseGit -> Submodule Add,添加子仓库path。

3. gitlab远程子仓库配置为SSH

在gitlab main path下原本是链接了一个http的子仓库SDK,但是由于要使用持续集成工具Jenkins检查merge的代码是否编译通过、是否单元测试通过等,因此需要将子仓库SDK的链接引用改为SSH的。改链接引用很容易,将git main path下的.gitmodules文件里的url改为SSH的就好。

但是遇到了一个问题:重新在本地Git Clone main path,右键点击Submodules update,提示要输入gitlab的密码,输入密码后还是弹出输入密码的框。此时需要配置下子仓库,

3.1. Flow

  1. win+r输入cmd打开cmd运行窗口输入,git config --global user.name "你的gitlab用户名",回车,输入git config --global user.email "你的gitlab邮箱",回车;

gitlab用户名和gitlab邮箱可以在这里看到

  1. 输入git config --global --list,回车,这一步知识看下前面的步骤有没有成功;
  2. 输入ssh-keygen,连续3次回车,成功之后id_rsa,id_rsa.pub两个文件默认在C:\Users\username/.ssh目录下,cmd运行窗口也显示了文件路径。

ps:如果输入命令ssh-keygen回车显示“ssh-keygen 不是内部或外部命令,也不是可运行的程序xxx”,那么在本地main code文件夹下右键选择Git Bash Here,输入ssh-keygen就可以了,盲猜可能是之前环境变量没配好。

  1. 打开id_rsa.pub,复制ssh key

法一:直接用UltraEdit打开id_rsa.pub,复制所有内容;

法二:在本地main code文件夹下右键选择Git Bash Here,输入cat ~/.ssh/id_rsa.pub,回车,

复制以ssh-rsa开头的这段ssh key;

  1. 将ssh key填写到gitlab SSH Keys上。

  1. 生成.ppk;打开PuTTYgen -> 点击load,选择.ssh/id_rsa -> 点击Save private key,保存ppk文件到.ssh目录下;

  1. 本地新建文件夹load ssh path,输入gitlab的个人密码,成功clone sdk到本地;

  1. clone main path到本地

(1)右键单击TortoiseGit → Git Clone,clone main path(http);

(2)将生成的.ppk加到Git putty key;

(3)在本地main仓库下右键pull;

(4)右键submodules update将sdk子仓库(ssh)拉取到本地。

3.2. Q&A

  1. clone代码报错:error: cannot spawn C:\ProgramData\Microsoft\Windows\Start Menu\Programs\TortoiseGit: Permission denied

原因:之前安装过TortoiseGit,但是残留文件没有清除

解决方法:打开C:\ProgramData\Microsoft\Windows\Start Menu\Programs\TortoiseGit,找到其快捷键,打开文件所在位置,复制其路径,粘贴到下图所示的位置应用即可。

  1. 配置了SSH仍要输入密钥

这种情况下,再应用一次ppk即可。

4. Revert

在TortoiseGit中,"Revert"文件 相当于git checkout HEAD--文件名(或git checkout-REVISION--文件名),用于将文件重置为其最后(或特定)提交状态。而git的revert仅由日志提交对话框中的revert更改引用。

4.1. Gitlab Revert

4.2. TortoiseGit Revert

如果要撤消自上次提交以来对文件所做的所有更改,则需要选择该文件,右键单击弹出上下文菜单,然后选择TortoiseGit → Revert 将弹出一个对话框,显示您已更改并可以还原的文件。选择要还原的对象,然后单击“OK”。

Revert将仅撤消本地更改。它不会撤消已push的任何更改。如果要撤消在特定修订中提交的所有更改,请阅读Reset这节内容。

撤消删除或重命名

需要在父文件夹(或commit或repository status dialog)上使用“Revert”,因为删除的项目不存在,无法右键单击。

撤消添加项目

5. Reset

如果你merge其它分支的代码到本地分支,但是你想回退到之前的节点,那么可以使用Reset。右键TortoiseGit → Show log → 选中要回退的节点 → 右键点击Reset "xxx" → 选择相应的Reset Type即可。

  • Soft:保持工作树和索引不变。完全不接触索引文件和工作树(但将头重置为所选提交,就像所有模式一样)。这将使所有更改的文件保持“要提交的更改”不变。
  • Mixed:同Abort Merge,保持工作树不变,重置索引,但不重置工作树(即保留更改的文件,但不标记为提交),并报告未更新的内容。
  • Hard:同Abort Merge,重置工作树和索引(放弃所有本地更改),对工作树中跟踪文件的任何更改都将被丢弃。与TortoiseGit的revert或clean功能不同,Hard不使用Windows回收站,直接永久删除这些文件,未提交的更改可能会丢失!

上述Reset操作只能将本地仓库回退到之前的节点,并不能将远端节点回退。如果要将远端节点回退,在上述操作的基础上还需要进行一个步骤。右键点击Git Bash Here,输入git push --force -u origin 分支名,回车即可看到远端仓库已经回退到之前节点了;

注:developer只能回退远端分支,不能回退远端main;

5.1. Revert VS Reset

TortoiseGit revert aimed to revert effects of previous commit. For example,

A <- B <- C

                ^ HEAD

If I found B I committed before is wrong, and I want to "undo" its change, revert B will cause:

A <- B <- C <- B'

                        ^ HEAD

其中B' 与B中所做的改变相反。

Reset is more straight-forward, it is simply setting the HEAD to a certain commit,

A <- B <- C

                ^ HEAD

git-reset-ting to B will give you

A <- B <- C

                ^ HEAD

6. rebase

        Rebase相当复杂,它会改变/重写存储库的历史。在使用它之前,请确保您了解它的原理(有关Git和重新定基的一般提示,请参阅“阅读指南”部分,尤其是Git rebase(1)手册页)。

Squash:该提交与位于列表下方(ID较低)的前一个提交合并。

Force rebase -> 选中log右键Squash -> start rebase -> 修改commit log -> commit -> done

 

7. merge

        由于用Git Submodule方式将SDK作为子仓库链接到FW_TLC仓库中,并且Ci Jenkins只能使用SSH Clone的方式下载代码,.gitmodules里的path由http改为了ssh,所以代码merge到主干的方式发生了改变,步骤稍微繁琐一些,但总体思想不变,即将其看作两个独立的仓库进行管理。

7.1. Flow

        以开发一个新需求为例,从FW_TLC Main的最新节点上创建一个FW_TLC branch进行模块开发。若此模块涉及SDK的修改,则创建FW_TLC branch对应的SDK branch;当功能开发完成并通过研发内测后,将FW_TLC branch merge到Main,此时需按如下步骤进行。如果此FW_TLC branch不涉及SDK的修改,则从步骤7开始。

SDK源分支:SDK branch

SDK目标分支 :SDK Main

FW源分支:FW_TLC branch

FW目标分支 :FW_TLC Main

  1. 确认SDK源分支上的变更是否都已经commit了。(不是push)
  2. pull 从远程仓库拉取最新版本到本地。
  3. merge SDK目标分支SDK源分支上。
  4. 如果有冲突的话,解决冲突,commit & push 到远程仓库。

如果没有冲突,直接push 到远程仓库。

  1. 在gitlab上创建SDK的merge request,等待直至merge通过。
  2. SDK源分支 switch到 SDK目标分支 ,在FW源分支下commit & push SDK的引用修改。
  3. merge FW目标分支FW源分支上,进行步骤4。
  4. 在gitlab上创建FW的merge request,等待merge通过。

7.2. Edit conflict

在merge的时候,可能有冲突导致merge不成功,此时需要使用TortoiseGit来解决冲突。在本地FW_TLC branch右键merge from FW_TLC main(此时需要保证本地仓库是最新的),有冲突则点击点击resolve,双击打开冲突文件,此时会出现三窗格视图,即合并视图。

  • merge操作涉及4个文件:mine file、theirs file、MergedfileBasefile
  • Base mine theirs 的最后一个共同祖先,表示文件的最旧版本,从那里您和他们开始进行更改。
  • theirs 代表了其他人对该文件所做的全部更改。左窗格显示了 theirs 相对于基本文件的更改。
  • mine 代表了包含您所做的所有更改的基本文件,右窗格显示了 mine 相对于基本文件所做的更改。
  • 底部窗格 Merged是您试图解决冲突的输出文件。

Merged file存在以下几种情况:

  1. 所有的修改 Mine 是最新的
  2. 所有的修改 Theirs 是最新的
  3. 一部分修改 Mine是最新的,一部分修改 Theirs是最新的

情况1选中文件右键,点击 Resolve conflict using "HEAD"即可
情况2选中文件右键,点击 Resolve conflict using "MERGE_HEAD"即可
情况3可以直接编辑底部窗格。或者使用上方工具栏的Use 'theirs' text block 、Use 'mine' text block、Use 'mine' text block then 'theirs'、Use 'theirs' text block then 'mine'。

8. Abort Merge

Merge:重置索引并且尝试重构merge之前的状态;

Mixed:保持工作树不变,重置索引,但不重置工作树(即保留更改的文件,但不标记为提交),并报告未更新的内容。

Hard:重置工作树和索引(放弃所有本地更改),对工作树中跟踪文件的任何更改都将被丢弃。与TortoiseGit的revert或clean功能不同,Hard不使用Windows回收站,直接永久删除这些文件,未提交的更改可能会丢失!

9. Stash changes

通常,当你在做项目的一部分时,事情处于一种混乱的状态,你想切换一下分支来做其他事情。问题是,你不想只做一半的工作,然后再回到这一点。那么可以通过Stash changes来解决。

Stash changes就是将当前未commit的修改存放在堆栈,需要使用时pop出来即可。

Stash change操作会获取工作目录的脏状态,即修改后的跟踪文件和暂存的更改,并将其保存在一堆未完成的更改中,你可以随时重新应用这些更改,即使在不同的分支上。

当你想记录工作目录和索引的当前状态,但又想返回一个干净的工作目录时,右键选择命令TortoiseGit → Stash changes将弹出一个对话框,你可以在其中选择输入此状态的消息:

  • include untracked:将未跟踪文件也隐藏起来。
  • --all:要隐藏所有文件,包括被忽略的文件和未跟踪的文件。

如果你需要应用Stash起来的更改,右键选择命令TortoiseGit → stash list → 选中要应用的修改 → 右键选择Stash Apply

  • Stash List:提供了整个Stash堆栈的概览。你还可以删除并查看隐藏在那里的更改。
  • Stash Apply:会将选中的那条Stash的更改应用到当前分支,应用后选中的Stash仍然在stash list中。
  • Stash Pop:会将最新的那条Stash的更改应用到当前分支,并且会将此条stash从stash list中删除。

10. Clean up

要从工作树中删除未跟踪或忽略的文件,请使用TortoiseGit → Clean up。然后出现一个对话框,允许你从当前目录或整个工作树(取决于安装的git版本)开始,通过递归删除不受版本控制或被忽略的文件来清理工作树。

Remove all untracked files:这将删除所有未跟踪文件,包括Git忽略的文件。这是最干净的选择。

Remove non-ignore untracked files:这将删除未跟踪文件,但不包括Git忽略的文件。

Remove ignored files:只清除被忽略的文件这只删除Git忽略的文件

Remove untracked directories :删除未跟踪目录。

Do not use recycle bin:不要使用回收站,即直接永久删除这些文件。

Dry run:这只是给出了要删除的文件列表,但不执行任何删除。

Submodules :递归地清理子模块。

11. Ignore

编译后会产生一些不受版本控制的文件和文件夹。如用于存储可执行文件bin/、obj/、db、*table等等。每当提交更改时,TortoiseGit都会显示这些派生文件,这将填充提交对话框中的文件列表。当然,你可以关闭此显示,但可能会忘记添加新的源文件。

避免这些问题的最佳方法是将派生文件添加到项目的忽略列表中。这样,它们将永远不会出现在提交对话框中,但真正需要commit的源文件仍将被标记。

右键单击一个或多个未版本化的文件,并选择TortoiseGit→ add to ignore list,将出现一个子菜单,允许你选择按名称或扩展名忽略。如下图二所示,忽略对话框允许你选择忽略类型和忽略文件。

Ignore Type

  • 仅忽略包含文件夹中的项目:仅忽略该文件夹中的选定pattern。
  • 递归忽略项:忽略该文件夹和子文件夹中具有选定模式的项目。

Ignore File

  • 存储库根目录中的.gitignore:在存储库根目录的.gitignore中写入忽略条目。这允许您将忽略列表与远程存储库同步。
  • .gitignore位于项目的包含目录中:在包含项的目录中的.gitignore中写入忽略项。这允许您将忽略列表与远程存储库同步。
  • .git/info/exclude:在存储库元数据中的.git/info/exclude中写入忽略条目。这允许您在本地存储忽略列表,但不能与远程存储库同步。

12. Amend

  1. 提交代码到远端仓库后,如果发现提交有遗漏或者提交有误,可以使用如下方法修改最后一条提交信息。

修改后,右键commit -> 选中Amend Last Commit -> 修改代码 or log -> 点击commit -> push的时候选中unknown changes。

13. Cherry Pick

Cherry-pick,即挑选一个需要的commit 进行操作。它可以将在源分支A上的commit修改移植到目标分支B

(1)pull or fetch,确保本地仓库含有源分支A和目标分支B的最新内容。

(2)本地仓库切到目标分支B -> 右键TortoiseGit show log -> 点击右上角的目标分支,选中源分支A -> 选中所需的commit -> 右键,点击Cherry pick this comment -> 如果需要多个commit,点击add添加 -> 点击continue,即可在目标分支的log里看到刚刚cherry pick的commit。

14. Q&A

情形1:代码clone到本地,simstudio编译fail,但报错很奇怪,看起来代码没有问题,

情形2:编译pass,但是运行的时候断点没触发,表面看起来运行的不是这份代码生成的dll,但是remove、 rebuild工程都不行

以上两种情况很可能是文件的行尾造成的,不同操作系统使用不同的换行符表示行尾,例如Windows使用回车换行符(CRLF,\r\n),而Linux、Mac OS使用换行符(LF,\n)。代码行尾符的变化可能由于编辑器或者git的行尾符默认设置导致的。

配置git的行尾符默认设置,如果AutoCrLf为false(表示Git不会自动转换换行符),点击Edit global.gitconfig改为true,ture代表Git会自动将换行符转换为操作系统默认的换行符,如下图所示:

如果还存在行尾符不是PC行尾符的情况,可以使用git bash的unix2dos命令。

查看行尾符:使用beyonce compare打开文件,点击规则,选中比较行结尾即可。