您现在的位置是:首页 >技术杂谈 >【Git基础】常用git命令(四)网站首页技术杂谈
【Git基础】常用git命令(四)
文章目录
1. 处理突发事件
1.1 暂存修改
遇到这样一种情况,你正在专注于开发某个feature或者是在重构(FT-12345分支),但是突然线上冒出了一个BUG,你需要去解BUG,但是目前的修改怎么办才好呢?如果提交则会产生一个没有意义的commit。那么此时就可以使用git stash,下面讲解一下使用步骤:
-
首先,确保你处于需要暂存修改的分支(例如:FT-12345)。您可以使用以下命令查看当前分支:
git branch
如果需要切换分支,请使用:
git checkout FT-12345
-
使用
git stash
命令将您的修改保存到一个新的存储区。您可以添加一个可选的描述消息,以便于在以后找到和应用这个存储:git stash save "Work in progress on FT-12345"
这将把您的修改保存到一个新的存储区,并将工作区恢复到上次提交的状态。
-
现在,您可以自由地切换到需要修复 bug 的分支,例如:
git checkout master
或者:
git checkout hotfix-branch
-
在切换到正确的分支后,进行所需的 bug 修复并提交更改。
-
一旦 bug 修复完成,您可以切换回到原来的分支(例如:FT-12345):
git checkout FT-12345
-
接下来,使用
git stash list
命令查看所有的存储区。您会看到类似这样的输出:stash@{0}: On FT-12345: Work in progress on FT-12345
-
要将暂存的修改应用回当前分支,请使用
git stash apply
命令加上存储区的引用:git stash apply stash@{0}
这将把您之前暂存的修改应用回工作区。
-
如果您想从存储列表中删除已应用的存储,可以使用
git stash drop
命令:git stash drop stash@{0}
这样,您就可以在处理紧急 bug 修复时,安全地保存当前分支的修改,然后在处理完 bug 之后,再恢复这些修改。
但如果你仅仅只需要暂存最近的一次修改,然后再切换回来,仅仅只需要使用git stash
和git stash pop
即可:
- 在切换到其它分支之前使用
git stash
暂存修改 - 在修改完成并切换回之前工作的分支后使用
git stash pop
恢复修改
1.2 git stash的一些命令
-
git stash save [<message>]
:将当前工作区的修改保存到一个新的存储区,并恢复到上次提交的状态。您可以提供一个可选的描述消息,以便于区分不同的存储。git stash save "Work in progress"
-
git stash list
:列出所有的存储区。输出类似于:stash@{0}: On branch-name: Work in progress stash@{1}: On another-branch: Another work in progress
-
git stash apply [<stash>]
:将指定的存储区应用到当前工作区。如果不指定存储区,默认使用最近的存储区(stash@{0})。git stash apply stash@{0}
-
git stash branch <branch-name> [<stash>]
:创建一个新的分支,并将指定的存储区应用到新分支。如果不指定存储区,默认使用最近的存储区。git stash branch new-feature-branch stash@{1}
-
git stash pop [<stash>]
:类似于git stash apply
,但在应用存储区后,会将其从存储列表中删除。如果不指定存储区,默认使用最近的存储区。git stash pop stash@{0}
-
git stash drop [<stash>]
:从存储列表中删除指定的存储区。如果不指定存储区,默认删除最近的存储区。git stash drop stash@{1}
-
git stash clear
:删除所有的存储区。git stash clear
-
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时,一般遵循以下规则:
- 忽略操作系统自动生成的文件,例如缩略图等;
- 忽略编译生成的中间文件、可执行文件等。换句话说,如果一个文件是通过另一个文件自动生成的,那么自动生成的文件就没必要放进版本库。例如Java编译产生的.class文件,或C++的.obj文件;
- 忽略包含敏感信息的配置文件,例如存放口令的配置文件或日志文件等。
通过遵循这些规则,我们可以确保Git仓库中仅包含源代码和必要的配置文件,保持仓库的整洁和安全。
2.2 .gitignore的规则
-
空行:空行会被忽略,可用于分隔不同的规则组。
-
注释:以井号(#)开头的行将被视为注释,Git将忽略此行。
例子:
# 这是一个注释
-
通配符:可以使用星号(*)、问号(?)和方括号([])等通配符进行模式匹配。
- 星号(*):匹配任意长度的任意字符。
例子:
*.log # 忽略所有.log文件
- 问号(?):匹配任意单个字符。
例子:
?.txt # 忽略所有单个字符加.txt后缀的文件,如a.txt,但不包括ab.txt
- 方括号([]):匹配方括号内的任意单个字符。
例子:
[abc].txt # 忽略a.txt、b.txt、c.txt
- 星号(*):匹配任意长度的任意字符。
-
斜杠(/):用于指定目录。
- 在规则开头使用斜杠,表示规则仅适用于当前目录。
例子:
/debug.log # 仅忽略当前目录下的debug.log文件
- 在规则结尾使用斜杠,表示仅匹配目录,而不匹配文件或符号链接。
例子:
tmp/ # 忽略所有名为tmp的目录,但不忽略tmp文件或tmp符号链接
- 在规则开头使用斜杠,表示规则仅适用于当前目录。
-
叹号(!):用于否定规则,表示不忽略匹配该规则的文件或目录。
例子:
*.log # 忽略所有.log文件 !important.log # 但不忽略important.log文件
-
双星号(**):用于匹配任意数量的目录层级。
例子:
**/debug.log # 忽略所有目录下的debug.log文件,包括子目录
-
组合规则:可以将多个规则组合在一起使用,以满足复杂的需求。
例子:
# 忽略所有.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子项目。这些子项目可以单独进行提交、推送、拉取等操作,而父项目中的提交不会影响到子项目。
使用子模块具有以下优势:
-
将子模块单独作为一个Git项目,它们可以独立开发,子模块中的错误不会影响到父项目。这有助于保持项目之间的隔离性,降低项目间的耦合性。
-
团队可以在不同的Git项目上分模块工作,减少代码提交的依赖,从而降低更新操作带来的冲突和复杂性。
通过使用Git子模块,我们可以更好地管理项目之间的依赖关系,实现模块化开发,提高团队协作的效率。
3.2 submodule的使用方式
-
添加子模块
要将一个仓库作为子模块添加到当前项目中,请使用以下命令:
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
子模块。 -
初始化和更新子模块
添加子模块后,需要对其进行初始化和更新。使用以下命令完成这些操作:
git submodule init git submodule update
这将检出子模块的代码。你还可以使用
--recursive
选项来同时初始化和更新所有子模块及其子模块:git submodule update --init --recursive
-
拉取主项目时更新子模块
当从远程仓库拉取主项目时,为了确保子模块也得到更新,可以使用以下命令:
git pull --recurse-submodules
-
在子模块中进行更改
要在子模块中进行更改,首先进入子模块目录,然后像处理普通Git仓库一样进行更改、提交和推送:
cd libs/my_library # 对子模块进行更改 git add . git commit -m "Update submodule" git push
-
更新主项目中子模块的引用
在子模块中进行更改并推送后,需要更新主项目中子模块的引用。回到主项目目录,使用以下命令:
cd ../.. git add libs/my_library git commit -m "Update submodule reference" git push
这将更新主项目中子模块的引用,使其指向子模块的最新提交。
-
删除子模块
要从项目中删除子模块,请按照以下步骤操作:
- 删除
.gitmodules
文件中相关的子模块条目。 - 删除
.git/config
文件中相关的子模块条目。 - 在文件系统中删除子模块的目录。
然后提交这些更改:
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的内容
当子模块的远程仓库有更新时,需要在主项目中更新子模块的内容。下面给出了更新子模块内容的步骤:
-
进入子模块目录:首先,导航到子模块的目录:
cd <path_to_submodule>
-
拉取子模块的最新更改:使用
git fetch
命令从远程仓库拉取子模块的最新更改:git fetch
也可以选择拉取特定分支的更新,例如:
git fetch origin master
-
检出子模块的更新:使用
git checkout
命令将子模块更新到最新的提交。如果想更新到远程分支的最新版本,可以这样操作:git checkout origin/master
如果想更新到特定的提交,可以这样操作:
git checkout <commit_sha>
-
返回主项目目录:更新子模块后,返回到主项目目录:
cd ..
-
更新主项目中的子模块引用:使用
git add
和git commit
命令将子模块的更新添加到主项目:git add <path_to_submodule> git commit -m "Update submodule to the latest version"
-
推送主项目的更改:将主项目的更改推送到远程仓库:
git push
现在,子模块已经更新到最新版本,且主项目中的子模块引用也已更新。请注意,这些步骤需要在主项目和子模块之间进行切换,因为它们是独立的Git仓库。
3.5 如何同步submodule的更新
当子模块的远程仓库有更新时,你需要在主项目中同步子模块的内容。以下是同步子模块更新的步骤:
-
拉取主项目的最新更改:首先,确保已经拉取了主项目的最新更改:
git pull
-
初始化子模块(如果尚未初始化):如果子模块尚未初始化,使用以下命令进行初始化:
git submodule init
-
更新子模块内容:使用以下命令更新子模块内容:
git submodule update
这将会将子模块更新到主项目中的引用的特定提交。
如果子模块中还有嵌套的子模块,可以使用
--recursive
选项同时初始化和更新所有子模块及其子模块:git submodule update --init --recursive
-
拉取子模块的最新更改(可选):如果你想将子模块更新到远程仓库的最新更改,而不仅仅是主项目引用的特定提交,可以在子模块目录中执行
git pull
:cd <path_to_submodule> git pull cd ..
请注意,如果执行此步骤,需要确保主项目中的子模块引用也更新到最新更改,以避免引用不一致。
通过以上步骤,你可以在主项目中同步子模块的更新。这将确保子模块内容与主项目引用的提交保持一致。
3.6 在子模块中工作
当在子模块中进行开发和修改时,可以将子模块视为一个独立的Git仓库。以下是在子模块中进行工作的基本步骤:
-
导航到子模块目录:首先,进入子模块的目录:
cd <path_to_submodule>
-
拉取子模块的最新更改:确保你在子模块的最新版本上进行工作,从远程仓库拉取最新更改:
git fetch git checkout <branch_name> git pull
-
创建新分支(可选):如果你需要在新分支上进行工作,可以创建一个新分支:
git checkout -b <new_branch_name>
-
进行更改:在子模块中进行所需的更改和修改。
-
提交更改:将更改添加到暂存区并进行提交:
git add . git commit -m "Your commit message"
-
推送更改:将更改推送到远程仓库:
git push
-
返回主项目目录:完成子模块的更改后,返回主项目目录:
cd ..
-
更新主项目中的子模块引用:将子模块的更改添加到主项目,并提交更新的引用:
git add <path_to_submodule> git commit -m "Update submodule reference"
-
推送主项目的更改:将主项目的更改推送到远程仓库:
git push
通过这些步骤,在子模块中进行工作和开发就像在任何其他独立的Git仓库中一样。只需确保在完成子模块的更改后,正确地更新并提交主项目中的子模块引用。
3.7 删除子模块
如果要从Git仓库中删除一个子模块,需要完成以下步骤:
-
删除子模块目录:删除子模块目录及其内容:
git rm --cached <path_to_submodule>
这将从暂存区中删除子模块,但不会删除子模块的物理文件。
-
删除子模块的物理文件:删除子模块的物理文件和目录:
rm -rf <path_to_submodule>
-
修改
.gitmodules
文件:编辑.gitmodules
文件,删除与子模块相关的条目。这可能类似于以下内容:[submodule "path_to_submodule"] path = path_to_submodule url = https://github.com/user/repo.git
将整个条目删除,然后保存并关闭文件。
-
提交更改:将这些更改添加到暂存区并提交:
git add .gitmodules git commit -m "Remove submodule"
-
删除子模块配置:从
.git/config
文件中删除与子模块相关的配置。编辑.git/config
文件,找到与子模块相关的部分,类似于以下内容:[submodule "path_to_submodule"] url = https://github.com/user/repo.git
将整个部分删除,然后保存并关闭文件。
-
清除子模块的缓存:运行以下命令以从Git缓存中删除子模块相关信息:
git rm --cached <path_to_submodule>
-
推送更改:将更改推送到远程仓库:
git push
现在,子模块已从Git仓库中完全删除。请注意,这些步骤涉及多个文件的修改,因此需要仔细操作以避免引起问题。
4. 如何备份git仓库
为了容灾,很多公司都会对git仓库进行备份,以下是一些基本步骤:
- 设置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
- 书写以下脚本:我们使用ssh协议进行
git clone
,--mirror
是拷贝镜像的意思(不能省掉,因为git仓库有很多的分支和tag信息)
giturl="root@47.106.79.26:/srv/"
# reslist=("nginx-docs.git")
reslist=$(ssh root@47.106.79.26 "cd /srv ; ls")
for res in ${reslist[@]};
do
cd ${res}
#echo ssh://${giturl}${res}
git clone --mirror ssh://${giturl}${res}
done
- 通过crontab添加定时任务:
crontab –e # 在定时任务中添加: 0 0 * * * sh /srv/backup_remote_git.sh,然后保存
systemctl restart cron # 重启cron服务,如果在centos是systemctl restart crond