【Git基础】常用git命令(四)

1. 处理突发事件

1.1 暂存修改

遇到这样一种情况,你正在专注于开发某个feature或者是在重构(FT-12345分支),但是突然线上冒出了一个BUG,你需要去解BUG,但是目前的修改怎么办才好呢?如果提交则会产生一个没有意义的commit。那么此时就可以使用git stash,下面讲解一下使用步骤:

  1. 首先,确保你处于需要暂存修改的分支(例如:FT-12345)。您可以使用以下命令查看当前分支:

    git branch
    

    如果需要切换分支,请使用:

    git checkout FT-12345
    
  2. 使用 git stash 命令将您的修改保存到一个新的存储区。您可以添加一个可选的描述消息,以便于在以后找到和应用这个存储:

    git stash save "Work in progress on FT-12345"
    

    这将把您的修改保存到一个新的存储区,并将工作区恢复到上次提交的状态。

  3. 现在,您可以自由地切换到需要修复 bug 的分支,例如:

    git checkout master
    

    或者:

    git checkout hotfix-branch
    
  4. 在切换到正确的分支后,进行所需的 bug 修复并提交更改。

  5. 一旦 bug 修复完成,您可以切换回到原来的分支(例如:FT-12345):

    git checkout FT-12345
    
  6. 接下来,使用 git stash list 命令查看所有的存储区。您会看到类似这样的输出:

    stash@{
          
          0}: On FT-12345: Work in progress on FT-12345
    
  7. 要将暂存的修改应用回当前分支,请使用 git stash apply 命令加上存储区的引用:

    git stash apply stash@{
          
          0}
    

    这将把您之前暂存的修改应用回工作区。

  8. 如果您想从存储列表中删除已应用的存储,可以使用 git stash drop 命令:

    git stash drop stash@{
          
          0}
    

这样,您就可以在处理紧急 bug 修复时,安全地保存当前分支的修改,然后在处理完 bug 之后,再恢复这些修改。

但如果你仅仅只需要暂存最近的一次修改,然后再切换回来,仅仅只需要使用git stashgit stash pop即可:

  • 在切换到其它分支之前使用git stash暂存修改
  • 在修改完成并切换回之前工作的分支后使用git stash pop恢复修改

1.2 git stash的一些命令

  1. git stash save [<message>]:将当前工作区的修改保存到一个新的存储区,并恢复到上次提交的状态。您可以提供一个可选的描述消息,以便于区分不同的存储。

    git stash save "Work in progress"
    
  2. git stash list:列出所有的存储区。输出类似于:

    stash@{
          
          0}: On branch-name: Work in progress
    stash@{
          
          1}: On another-branch: Another work in progress
    
  3. git stash apply [<stash>]:将指定的存储区应用到当前工作区。如果不指定存储区,默认使用最近的存储区(stash@{0})。

    git stash apply stash@{
          
          0}
    
  4. git stash branch <branch-name> [<stash>]:创建一个新的分支,并将指定的存储区应用到新分支。如果不指定存储区,默认使用最近的存储区。

    git stash branch new-feature-branch stash@{
          
          1}
    
  5. git stash pop [<stash>]:类似于 git stash apply,但在应用存储区后,会将其从存储列表中删除。如果不指定存储区,默认使用最近的存储区。

    git stash pop stash@{
          
          0}
    
  6. git stash drop [<stash>]:从存储列表中删除指定的存储区。如果不指定存储区,默认删除最近的存储区。

    git stash drop stash@{
          
          1}
    
  7. git stash clear:删除所有的存储区。

    git stash clear
    
  8. git stash push [-m|--message <message>]:这是 git stash save 的替代命令。Git 2.13.2 及更高版本推荐使用 git stash push。它将当前工作区的修改保存到一个新的存储区,并恢复到上次提交的状态。您可以提供一个可选的描述消息,以便于区分不同的存储。

    git stash push -m "Work in progress"
    

2. 指定不需要git管理的文件

2.1 指定不需要git管理的文件

在使用Git进行版本控制时,Java、C/C++等编程语言的程序员可能会遇到一个问题:在执行git add .等命令时,编译生成的中间文件(如.class、.obj、.o等)会被提交到仓库中。为了避免这种情况,Git提供了一个名为.gitignore的文件来定义规则,指定哪些文件不应被提交到Git仓库中。

.gitignore文件可以提交到远程仓库,这样所有的开发人员都可以共享这些规则。在.gitignore文件中,每一行都定义了一个忽略规则,例如:

*.[oa]
*~

第一行表示忽略所有以.o或.a结尾的文件,第二行表示忽略所有以“~”结尾的文件。

使用.gitignore时,一般遵循以下规则:

  1. 忽略操作系统自动生成的文件,例如缩略图等;
  2. 忽略编译生成的中间文件、可执行文件等。换句话说,如果一个文件是通过另一个文件自动生成的,那么自动生成的文件就没必要放进版本库。例如Java编译产生的.class文件,或C++的.obj文件;
  3. 忽略包含敏感信息的配置文件,例如存放口令的配置文件或日志文件等。

通过遵循这些规则,我们可以确保Git仓库中仅包含源代码和必要的配置文件,保持仓库的整洁和安全。

2.2 .gitignore的规则

  1. 空行:空行会被忽略,可用于分隔不同的规则组。

  2. 注释:以井号(#)开头的行将被视为注释,Git将忽略此行。

    例子:

    # 这是一个注释
    
  3. 通配符:可以使用星号(*)、问号(?)和方括号([])等通配符进行模式匹配。

    • 星号(*):匹配任意长度的任意字符。
      例子:
    *.log  # 忽略所有.log文件
    
    • 问号(?):匹配任意单个字符。
      例子:
    ?.txt  # 忽略所有单个字符加.txt后缀的文件,如a.txt,但不包括ab.txt
    
    • 方括号([]):匹配方括号内的任意单个字符。
      例子:
    [abc].txt  # 忽略a.txt、b.txt、c.txt
    
  4. 斜杠(/):用于指定目录。

    • 在规则开头使用斜杠,表示规则仅适用于当前目录。
      例子:
    /debug.log  # 仅忽略当前目录下的debug.log文件
    
    • 在规则结尾使用斜杠,表示仅匹配目录,而不匹配文件或符号链接。
      例子:
    tmp/  # 忽略所有名为tmp的目录,但不忽略tmp文件或tmp符号链接
    
  5. 叹号(!):用于否定规则,表示不忽略匹配该规则的文件或目录。

    例子:

    *.log      # 忽略所有.log文件
    !important.log  # 但不忽略important.log文件
    
  6. 双星号(**):用于匹配任意数量的目录层级。

    例子:

    **/debug.log  # 忽略所有目录下的debug.log文件,包括子目录
    
  7. 组合规则:可以将多个规则组合在一起使用,以满足复杂的需求。

    例子:

    # 忽略所有.txt文件,但不忽略doc/目录下的.txt文件
    *.txt
    !/doc/*.txt
    /doc/**/*.txt
    

了解这些规则后,你可以创建一个适合自己项目需求的.gitignore文件,有针对性地忽略不需要纳入版本控制的文件和目录。

在这里再推荐一个自动生成.gitignore文件的网址:https://www.toptal.com/developers/gitignore

3. 如何解决项目之间的依赖

3.1 如何使用git处理项目之间的依赖

在产品开发过程中,我们经常会遇到将产品架构划分为多个模块的情况,以便不同团队开发不同模块。例如,源码的src目录可能包含以下模块:

src
 |------- buffer
 |------- f-threadpool
 |------- iniconfig
 |------- intf
 |------- store
 |------- router

甚至可能依赖于第三方项目。为了管理这些依赖,我们可以使用Git的子模块(submodule)功能。子模块允许父项目包含许多独立的Git子项目。这些子项目可以单独进行提交、推送、拉取等操作,而父项目中的提交不会影响到子项目。

使用子模块具有以下优势:

  1. 将子模块单独作为一个Git项目,它们可以独立开发,子模块中的错误不会影响到父项目。这有助于保持项目之间的隔离性,降低项目间的耦合性。

  2. 团队可以在不同的Git项目上分模块工作,减少代码提交的依赖,从而降低更新操作带来的冲突和复杂性。

通过使用Git子模块,我们可以更好地管理项目之间的依赖关系,实现模块化开发,提高团队协作的效率。

3.2 submodule的使用方式

  1. 添加子模块

    要将一个仓库作为子模块添加到当前项目中,请使用以下命令:

    git submodule add <repository_url> <path_to_submodule>
    

    其中<repository_url>是子模块仓库的URL,<path_to_submodule>是子模块在主项目中的路径。

    例如,我们有一个主项目my_project,现在我们想添加一个名为my_library的子模块:

    git submodule add https://github.com/username/my_library.git libs/my_library
    

    这将在libs/my_library目录下添加my_library子模块。

  2. 初始化和更新子模块

    添加子模块后,需要对其进行初始化和更新。使用以下命令完成这些操作:

    git submodule init
    git submodule update
    

    这将检出子模块的代码。你还可以使用--recursive选项来同时初始化和更新所有子模块及其子模块:

    git submodule update --init --recursive
    
  3. 拉取主项目时更新子模块

    当从远程仓库拉取主项目时,为了确保子模块也得到更新,可以使用以下命令:

    git pull --recurse-submodules
    
  4. 在子模块中进行更改

    要在子模块中进行更改,首先进入子模块目录,然后像处理普通Git仓库一样进行更改、提交和推送:

    cd libs/my_library
    # 对子模块进行更改
    git add .
    git commit -m "Update submodule"
    git push
    
  5. 更新主项目中子模块的引用

    在子模块中进行更改并推送后,需要更新主项目中子模块的引用。回到主项目目录,使用以下命令:

    cd ../..
    git add libs/my_library
    git commit -m "Update submodule reference"
    git push
    

    这将更新主项目中子模块的引用,使其指向子模块的最新提交。

  6. 删除子模块

    要从项目中删除子模块,请按照以下步骤操作:

    1. 删除.gitmodules文件中相关的子模块条目。
    2. 删除.git/config文件中相关的子模块条目。
    3. 在文件系统中删除子模块的目录。

    然后提交这些更改:

    git add .gitmodules
    git rm --cached <path_to_submodule>
    git commit -m "Remove submodule"
    git push
    

    现在已经成功删除子模块。

3.3 如何clone submodule

当克隆包含子模块的项目时,默认情况下,Git会克隆主项目,但不会克隆子模块的内容。为了同时克隆主项目和子模块,可以使用以下方法:

方法1:在克隆时添加--recurse-submodules选项

使用--recurse-submodules选项克隆主项目及其所有子模块:

git clone --recurse-submodules <repository_url>

这将克隆主项目及其所有子模块,并自动初始化和更新子模块。

方法2:在克隆后手动初始化和更新子模块

如果已经克隆了主项目,但没有克隆子模块,可以使用以下命令手动初始化和更新子模块:

git submodule init
git submodule update

如果子模块中还有嵌套的子模块,可以使用--recursive选项同时初始化和更新所有子模块及其子模块:

git submodule update --init --recursive

3.4 submodule的陷阱和挑战

  • 子模块的复杂性:子模块的使用和管理比普通的Git仓库更复杂,需要额外的命令和操作。特别是对于不熟悉子模块的团队成员,可能会遇到困难和挑战。

  • 子模块的版本引用:子模块在主项目中只是一个指向特定提交的引用,而不是实时跟踪子模块的最新更改。这可能导致在更新子模块时出现问题,需要手动更新主项目中的子模块引用。

  • 忘记初始化和更新子模块:当克隆主项目时,可能会忘记使用--recurse-submodules选项或手动初始化和更新子模块。这可能导致缺少子模块的代码,从而引发构建或运行时错误。

  • 子模块和主项目的分离:子模块是一个独立的Git仓库,需要单独进行提交、拉取和推送操作。这可能导致团队成员在管理子模块和主项目时出现混淆和误操作。

  • 权限和访问控制:子模块可能位于不同的Git仓库,具有不同的权限和访问控制。在配置子模块时,需要确保所有团队成员都有适当的访问权限,以避免权限问题导致的错误。

  • 删除和移动子模块:删除或移动子模块涉及多个步骤,容易出错。如果操作不当,可能导致Git仓库中的引用错误或不一致。

3.4 如何更新submodule的内容

当子模块的远程仓库有更新时,需要在主项目中更新子模块的内容。下面给出了更新子模块内容的步骤:

  1. 进入子模块目录:首先,导航到子模块的目录:

    cd <path_to_submodule>
    
  2. 拉取子模块的最新更改:使用git fetch命令从远程仓库拉取子模块的最新更改:

    git fetch
    

    也可以选择拉取特定分支的更新,例如:

    git fetch origin master
    
  3. 检出子模块的更新:使用git checkout命令将子模块更新到最新的提交。如果想更新到远程分支的最新版本,可以这样操作:

    git checkout origin/master
    

    如果想更新到特定的提交,可以这样操作:

    git checkout <commit_sha>
    
  4. 返回主项目目录:更新子模块后,返回到主项目目录:

    cd ..
    
  5. 更新主项目中的子模块引用:使用git addgit commit命令将子模块的更新添加到主项目:

    git add <path_to_submodule>
    git commit -m "Update submodule to the latest version"
    
  6. 推送主项目的更改:将主项目的更改推送到远程仓库:

    git push
    

现在,子模块已经更新到最新版本,且主项目中的子模块引用也已更新。请注意,这些步骤需要在主项目和子模块之间进行切换,因为它们是独立的Git仓库。

3.5 如何同步submodule的更新

当子模块的远程仓库有更新时,你需要在主项目中同步子模块的内容。以下是同步子模块更新的步骤:

  1. 拉取主项目的最新更改:首先,确保已经拉取了主项目的最新更改:

    git pull
    
  2. 初始化子模块(如果尚未初始化):如果子模块尚未初始化,使用以下命令进行初始化:

    git submodule init
    
  3. 更新子模块内容:使用以下命令更新子模块内容:

    git submodule update
    

    这将会将子模块更新到主项目中的引用的特定提交。

    如果子模块中还有嵌套的子模块,可以使用--recursive选项同时初始化和更新所有子模块及其子模块:

    git submodule update --init --recursive
    
  4. 拉取子模块的最新更改(可选):如果你想将子模块更新到远程仓库的最新更改,而不仅仅是主项目引用的特定提交,可以在子模块目录中执行git pull

    cd <path_to_submodule>
    git pull
    cd ..
    

    请注意,如果执行此步骤,需要确保主项目中的子模块引用也更新到最新更改,以避免引用不一致。

通过以上步骤,你可以在主项目中同步子模块的更新。这将确保子模块内容与主项目引用的提交保持一致。

3.6 在子模块中工作

当在子模块中进行开发和修改时,可以将子模块视为一个独立的Git仓库。以下是在子模块中进行工作的基本步骤:

  1. 导航到子模块目录:首先,进入子模块的目录:

    cd <path_to_submodule>
    
  2. 拉取子模块的最新更改:确保你在子模块的最新版本上进行工作,从远程仓库拉取最新更改:

    git fetch
    git checkout <branch_name>
    git pull
    
  3. 创建新分支(可选):如果你需要在新分支上进行工作,可以创建一个新分支:

    git checkout -b <new_branch_name>
    
  4. 进行更改:在子模块中进行所需的更改和修改。

  5. 提交更改:将更改添加到暂存区并进行提交:

    git add .
    git commit -m "Your commit message"
    
  6. 推送更改:将更改推送到远程仓库:

    git push
    
  7. 返回主项目目录:完成子模块的更改后,返回主项目目录:

    cd ..
    
  8. 更新主项目中的子模块引用:将子模块的更改添加到主项目,并提交更新的引用:

    git add <path_to_submodule>
    git commit -m "Update submodule reference"
    
  9. 推送主项目的更改:将主项目的更改推送到远程仓库:

    git push
    

通过这些步骤,在子模块中进行工作和开发就像在任何其他独立的Git仓库中一样。只需确保在完成子模块的更改后,正确地更新并提交主项目中的子模块引用。

3.7 删除子模块

如果要从Git仓库中删除一个子模块,需要完成以下步骤:

  1. 删除子模块目录:删除子模块目录及其内容:

    git rm --cached <path_to_submodule>
    

    这将从暂存区中删除子模块,但不会删除子模块的物理文件。

  2. 删除子模块的物理文件:删除子模块的物理文件和目录:

    rm -rf <path_to_submodule>
    
  3. 修改.gitmodules文件:编辑.gitmodules文件,删除与子模块相关的条目。这可能类似于以下内容:

    [submodule "path_to_submodule"]
        path = path_to_submodule
        url = https://github.com/user/repo.git
    

    将整个条目删除,然后保存并关闭文件。

  4. 提交更改:将这些更改添加到暂存区并提交:

    git add .gitmodules
    git commit -m "Remove submodule"
    
  5. 删除子模块配置:从.git/config文件中删除与子模块相关的配置。编辑.git/config文件,找到与子模块相关的部分,类似于以下内容:

    [submodule "path_to_submodule"]
        url = https://github.com/user/repo.git
    

    将整个部分删除,然后保存并关闭文件。

  6. 清除子模块的缓存:运行以下命令以从Git缓存中删除子模块相关信息:

    git rm --cached <path_to_submodule>
    
  7. 推送更改:将更改推送到远程仓库:

    git push
    

现在,子模块已从Git仓库中完全删除。请注意,这些步骤涉及多个文件的修改,因此需要仔细操作以避免引起问题。

4. 如何备份git仓库

为了容灾,很多公司都会对git仓库进行备份,以下是一些基本步骤:
在这里插入图片描述

  1. 设置ssh的免密登录方式:
    ssh-kengen –t rsa  # 以rsa算法生成密钥对
    vim ~/.ssh/id_rsa.pub # 把id_rsa.pub的内容拷贝后放在git仓库的/root/.ssh/authorized_keys里
    chmod 400 /root/.ssh/authorized_keys   # 在git服务器上设置文件的权限为400
    
  2. 书写以下脚本:我们使用ssh协议进行git clone--mirror是拷贝镜像的意思(不能省掉,因为git仓库有很多的分支和tag信息)
giturl="[email protected]:/srv/"
# reslist=("nginx-docs.git")
reslist=$(ssh [email protected] "cd /srv ; ls")
for res in ${reslist[@]};
do
	cd ${res}
	#echo ssh://${giturl}${res}
	git clone --mirror ssh://${giturl}${res}
done
  1. 通过crontab添加定时任务:
crontab –e   # 在定时任务中添加: 0 0 * * * sh /srv/backup_remote_git.sh,然后保存
systemctl restart cron  # 重启cron服务,如果在centos是systemctl restart crond

猜你喜欢

转载自blog.csdn.net/weixin_52665939/article/details/130515971
今日推荐