Featured image of post Git Code Management and Version Control

Git Code Management and Version Control

Git代码托管与版本管理

安装

官网下载安装即可,安装配置基本默认。

https://git-scm.com

常用命令

命令 描述
git config --global user.name 用户名 设置用户签名
git config --global user.email 邮箱 设置用户签名
git init 初始化本地库
git status 查看本地库状态
git add 文件名 添加到暂存区
git commit -m "日志信息" 文件名 提交到本地库
git reflog / git log 查看历史记录
git reset --hard 版本号 版本穿梭

基本操作

设置用户签名

1
2
git config --global user.name user_name
git config --global user.email user_email  

git config --global 命令的作用范围是针对 该设备上所有 Git 仓库 的全局配置。

所以只要配置一次就好了。

要查看当前的 Git 全局配置,可以使用以下命令:

1
git config --global --list

查看特定配置项

如果你只想查看某个特定的全局配置项,可以使用以下命令:

1
git config --global [配置项名称]

例如:

1
git config --global user.name

这将只显示全局配置中的用户名。

初始化本地库

1
git init

在对应项目文件夹目录下创建Git仓库,总的来说会执行2个动作:

  • 创建名为.git的子目录,含有初始化Git仓库中所有的必须文件,这些文件是Git仓库的骨干,但是项目里的文件还没有被跟踪;
  • 一个没有任何commit(提交记录)的初始分支,这一分支默认名称为master

这里有一个很逗的点,在初始化完有概率遇到一条warning:大意是说默认分支叫master,你可以 git branch -m old-name new-name改为新main。

为什么会有这条莫名其妙的警告呢,因为master/slave主从分支在政治上是不正确的,所以现在很多git托管网站都是用main做默认分支名。

查看本地状态

1
git status

git status 命令用于显示当前 Git 仓库的状态。它会告诉你哪些文件被修改了、哪些文件被添加到暂存区(staging area)、哪些文件未被跟踪等信息。

以下是 git status 可能显示的一些关键信息:

  1. 当前分支
    • 显示你当前所在的分支名称。
  2. 本地更改
    • 未跟踪的文件:显示仓库中尚未添加到暂存区的新文件。
    • 修改过的文件:显示自上次提交以来已被修改的文件。
  3. 暂存区更改
    • 已暂存的更改:显示已被添加到暂存区的文件,这些更改将在下一次提交时被包含。
    • 已暂存但修改过的文件:显示已暂存但自暂存后又被修改的文件。
  4. 冲突
    • 如果你在合并或拉取时遇到冲突,git status 会显示这些冲突文件,提示你需要手动解决这些冲突。
  5. 分支状态
    • 如果你的分支与远程分支有差异,git status 会显示这些差异,例如本地分支领先、落后或与远程分支同步。
  6. 未暂存的更改
    • 显示自上次提交以来未被添加到暂存区的文件更改。
  7. 提示信息
    • 根据当前状态,git status 可能会提供一些有用的提示信息,例如如何添加文件到暂存区或如何提交更改。

例如:

干净的工作区(无任何修改)

1
2
3
4
On branch main
Your branch is up to date with 'origin/main'.

nothing to commit, working tree clean

同时存在已暂存、未暂存和未跟踪的文件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
On branch feature/login
Your branch is up to date with 'origin/feature/login'.

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        new file:   config.json

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   app.js

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        logs/debug.log

添加到暂存区

1
git add 文件名

git add 是一个 Git 命令,用于将更改添加到暂存区(staging area),这是提交(commit)更改到本地仓库之前的一个必备步骤。它允许你精确控制哪些更改应该包含在下一次提交中。

暂存区是一个文件,保存了下次将提交到本地仓库的更改列表。

  1. 添加单个文件

    1
    
    git add <文件名>
    

    这会将指定文件的更改添加到暂存区。

  2. 添加多个文件

    1
    
    git add <文件1> <文件2> ...
    

    你可以一次性添加多个文件到暂存区。

  3. 添加所有更改

    1
    
    git add .
    

    或者

    1
    
    git add -A
    

    这会将所有新的、修改的和删除的文件(不包括未跟踪的文件)添加到暂存区。

提交到本地库

git commit 用于将暂存区(staging area)的更改提交(commit)到本地仓库。提交是版本控制中保存项目历史记录的基本单元。

1
git commit -m "提交信息"

这将提交暂存区的更改,并使用提供的提交信息。-m 选项允许你在命令行中直接添加提交信息,而不需要打开文本编辑器。

查看历史记录

git refloggit log 都是 Git 中用于查看项目历史记录的命令,但它们的用途和显示的信息有所不同。

git reflog

git reflog 命令显示了所有引用(包括分支和标签)的更新历史。它记录了HEAD和分支引用的每一次移动,无论这些移动是否由提交引起。

git reflog 可以显示由于各种操作(如提交、回退、创建分支、切换分支等)引起的引用变化。

1
git reflog

这将显示一个按时间排序的列表,列出了HEAD和分支引用的每一次更新。

git log

git log 命令显示了提交历史,包括每次提交的作者、日期、提交信息等详细信息。它主要用于查看项目的提交历史。

1
git log

默认会进入分页器(less),按 q 退出;Space 下翻页,b 上翻页。 若不希望分页,使用:git --no-pager log

这将显示项目的提交历史,包括每次提交的哈希值、作者、日期和提交信息。并且git log支持多种格式:可以通过选项自定义输出格式,如简洁格式、一行列格式等,并且支持过滤:可以通过选项过滤特定的提交,如按作者、日期、路径等过滤。

常用选项

  • --oneline:以一行列格式显示提交信息,只显示哈希值和提交信息。

  • 查看简洁的提交历史

    1
    
    git log --oneline
    
  • --graph:显示分支合并图。

  • 查看带有分支合并图的提交历史

    1
    
    git log --graph --oneline
    
  • --since--until:按时间过滤提交。

  • --author:按作者过滤提交。

  • --grep:搜索提交信息中的关键词。

  • 搜索特定作者的提交

    1
    
    git log --author="用户名"
    
  • 查看特定时间段内的提交

    1
    
    git log --since="2024-01-01" --until="2024-12-31"
    

版本穿梭

1
git reset --hard 版本号

上述命令用于将当前分支和工作目录重置到指定的版本号,即某个特定的提交(commit)。这个命令会改变当前分支的 HEAD 指针,并且会重置工作目录和暂存区,使其与指定的提交完全一致。注意,这意味着所有在该提交之后所做的更改都将丢失,包括未提交的更改和暂存的更改,这是不可逆的。

你需要找到你想要回退到的版本号,可以通过 git log 命令查看提交历史,找到对应的提交哈希值。

忽略文件

有些文件是不需要(不应该)加入版本控制的,例如编译的临时文件、日志文件。通过创建 .gitignore 文件来配置要忽略的文件模式。

文件 .gitignore 的格式规范如下:

  • 所有空行或者以 # 开头的行都会被 Git 忽略
  • 可以使用标准的 glob 模式匹配,它会递归地应用在整个工作区中
  • 匹配模式可以以 / 开头防止递归
  • 匹配模式可以以 / 结尾指定目录
  • 要忽略指定模式以外的文件或目录,可以在模式前加上叹号 ! 取反
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# .gitignore
# 忽略任何目录下的 build 目录
build/
# 忽略当前目录下的 hello.txt 文件,而不忽略子目录下的
/hello.txt
# 忽略所有以 .log 结尾的文件
*.log
# 忽略 doc 目录下的所有 .txt 结尾的文件
doc/*.txt
# 跟踪 doc/config.txt,即使前面忽略了 doc/*.txt
!doc/*.txt

分支操作

分支的好处

  • 同时并进行多个功能开发,提高了开发效率
  • 各个分支再开发过程中,如果某个分支开发失败,不会对其他分支有任何影响,失败的分支删除重新开始即可

分支常用命令

命令 描述
git branch 分支名 创建分支
git branch -v 查看分支
git checkout 分支名 切换分支
git merge 需要合并的分支名 把指定的分支合并到当前分支上

查看分支

1
2
3
4
5
6
# 列出本地详细分支
# * 代表当前所在分支
git branch -v
* master
# 列出远程分支
git branch -r

git branch -v 命令用于列出 Git 仓库中的所有分支,并显示每个分支的最新提交信息。这个命令是 git branch 命令的一个变体,其中的 -v 选项代表“verbose”,即详细模式。

创建分支

1
git branch 分支名

git branch 分支名 是一个用于创建新分支的 Git 命令。当你想要从当前开发线(通常是主分支)创建一个新的开发线时,这个命令非常有用。

这将在当前 HEAD 指向的提交处创建一个新分支,但不会自动切换到该分支。

切换分支

1
git checkout 分支名

这将切换到指定的分支,并更新工作目录以反映该分支的状态。

也可以使用较新的命令:git switch 分支名,专用于分支切换。

重命名分支

如果你当前就在要重命名的分支上,可以使用:

1
git branch -m <新分支名>

例如,将当前分支 old-name 重命名为 new-name(假设你已在 old-name 分支):

1
git branch -m new-name

如果你不在要重命名的分支上,可以指定原分支名和新分支名:

1
git branch -m <原分支名> <新分支名>

例如:

1
git branch -m old-name new-name

注意事项:

  • -m--move 的缩写,表示移动(重命名)分支。

  • 如果你已经将旧分支推送到远程仓库,重命名本地分支后,还需要更新远程分支:

    1
    2
    3
    4
    5
    
    # 删除远程旧分支
    git push origin --delete old-name
    
    # 推送新分支并设置上游
    git push origin -u new-name
    
  • 重命名分支不会影响提交历史,只是更改了分支的引用名称。

删除分支

  • 删除本地分支(已合并建议用 -d,未合并需强制 -D):
1
2
git branch -d 分支名
git branch -D 分支名
  • 删除远程分支:
1
git push origin --delete 分支名
  • 清理本地过期的远程跟踪引用:
1
2
3
git fetch -p
# 或
git remote prune origin

合并分支

1
git merge 需要合并的分支名  //把指定的分支合并到当前分支上

git merge 用于将一个分支的更改合并到当前分支。这通常用于合并功能分支到主分支(如 mainmaster),或者合并修复分支到开发分支。

合并过程中的冲突

在合并过程中,如果存在冲突(即两个分支对同一文件的同一部分进行了不同的更改),Git 会停止合并并让你手动解决这些冲突。你需要:

  1. 手动解决冲突:打开冲突的文件,手动编辑以解决冲突。
  2. 标记冲突已解决:使用 git add 命令将解决冲突后的文件标记为已解决。
  3. 完成合并:使用 git commit 命令完成合并。

进阶操作

标签(Tags)

  • 创建轻量标签与附注标签:
1
2
git tag v1.0.0
git tag -a v1.0.0 -m "release 1.0.0"
  • 查看与推送标签:
1
2
3
git tag
git push origin v1.0.0
git push --tags
  • 删除标签:
1
2
git tag -d v1.0.0
git push origin --delete tag v1.0.0

暂存工作(stash)

1
2
3
4
git stash push -m "临时保存:修复登录"
git stash list
git stash apply  stash@{0}
git stash pop    stash@{0}
  • 只暂存部分文件:git stash push -m "msg" 路径/文件
  • 从暂存创建分支:git stash branch feature/fix-login stash@{0}

拣选提交(cherry-pick)

1
2
git checkout 目标分支
git cherry-pick <commit>
  • 多个提交:git cherry-pick A..Bgit cherry-pick A^..B
  • 保留来源信息:git cherry-pick -x <commit>
  • 仅引入变更不提交:git cherry-pick --no-commit <commit>

变基(rebase)与合并(merge)

  • 变基让历史“线性化”,便于阅读;合并保留真实分叉与合并点。
  • 避免对“已公开的分支”做 rebase,以免破坏他人历史。
1
2
3
4
5
6
# 将当前分支变基到最新的 main
git fetch origin
git rebase origin/main

# 交互式编辑历史(压缩、重写提交信息)
git rebase -i HEAD~5

撤销提交(revert)

  • “安全回滚”某次提交,保留历史:
1
git revert <commit>
  • 撤销一段提交(可能产生冲突需手动解决):
1
git revert A..B

restore 与 checkout 的差异

  • git restore:用于恢复文件内容(工作区/暂存区),不切换分支。
  • git checkout:既能切分支又能恢复文件,功能较多、语义不够直观。
  • 推荐:分支相关用 git switch,文件恢复用 git restore
1
2
3
4
5
6
7
8
# 恢复工作区文件到最近一次提交状态
git restore 路径/文件

# 取消已暂存(从暂存区移回工作区)
git restore --staged 路径/文件

# 切换分支(现代命令)
git switch 分支名

远程仓库的操作

添加远程库地址

1
git remote add origin 远程库地址

git remote add origin 远程库地址 用于将远程仓库添加到你的本地 Git 仓库中。当你创建一个新的本地仓库并希望将其与远程仓库(如 Gitee、GitHub、GitLab 等)关联时,这个命令非常有用。

命令解释

  • git remote:这是用于管理远程仓库引用的命令。
  • add:这个子命令用于添加一个新的远程仓库引用。
  • origin:这是远程仓库的默认短名称。Git 使用 origin 作为远程仓库的默认名称,但你也可以使用其他名称。
  • 远程库地址:这是远程仓库的 URL 地址。

验证远程仓库

添加远程仓库后,你可以使用以下命令来查看所有远程仓库的 URL:

1
git remote -v

这将列出所有远程仓库的名称和对应的 URL。

获取远程仓库信息

git fetch <代码库名>(通常是origin) 是从远程仓库拉取最新的数据(比如提交、分支、标签等),但不会自动合并到你的本地代码中。

执行 git fetch origin 后,Git 会做以下事情:

  1. 从远程仓库(如 origin)拉取所有新的提交、分支、标签等信息
  2. 把这些信息保存在本地,但不会影响你当前的工作目录
  3. 更新本地对远程分支的引用(例如 origin/main

这样你就知道:“哦,别人已经在 main 分支上提交了新代码”,但你自己的代码还是原来的,没变。

场景 1:你想先看看别人改了什么,再决定是否合并

1
git fetch origin

现在你可以查看远程有哪些变化:

1
git log origin/main..main

这会显示:别人提交了哪些你还没有的改动

然后你可以选择:

  • 如果没问题 → git merge origin/main
  • 如果有问题 → 先修复冲突或回滚

⚠️ 而 git pull 是直接拉 + 合并,没有缓冲,容易出错!


场景 2:团队协作中避免意外覆盖

如果你刚写了一半的功能,同事突然推送了新代码,你不想立刻合并,可以:

1
git fetch origin

→ 看看有没有冲突,再决定要不要合并。

修改远程仓库 URL

如果你需要修改远程仓库的 URL(例如,从 HTTPS 更改为 SSH),你可以使用以下命令:

1
git remote set-url origin 新的远程库地址

新的远程库地址 替换为你新的远程仓库 URL。

删除远程仓库

如果你不再需要某个远程仓库,你可以使用以下命令删除它:

1
git remote remove origin

这将删除名为 origin 的远程仓库引用。

拉取远程库文件

1
git pull origin master

这个命令会从远程仓库的 master 分支拉取最新的更改,并尝试将这些更改合并到你当前所在的本地分支。(有时是用main分支)

简言之就是从远程库拉取文件到工作区。

上传远程库文件

1
git add . //将所有改变添加到暂存区

上传前要执行git add 将更改添加到暂存区(staging area)。

1
git commit -m "message"

再执行git commit 将暂存区(staging area)的更改提交(commit)到本地仓库。

1
git push origin (master/分支的名字)

最后执行git push将本地库文件上传到远程库。

1
git push -u origin 分支名

这条命令做了两件事:

  1. 推送分支:将您的本地分支推送到远程仓库 origin
  2. 设置上游分支-u 参数将远程分支设置为本地分支的上游分支,这样您以后可以直接使用 git pushgit pull 命令而不需要指定远程仓库和分支名。

代码冲突

协作场景

假设有这么一个场景,你和同事协作,你的同事push了新的代码,而你也在开发中,进行了pull:

  • 远程仓库(比如 origin/main):有你本地没有的新提交(比如同事推送了代码)。

  • 你的本地分支(比如 main):有你远程没有的新提交(比如你写了一些新功能)。

此时你执行了:

1
git pull

实际上你执行了

1
git pull = git fetch + git merge
  1. fetch:把远程的新提交拉到本地(更新 origin/main)。
  2. merge:尝试把 origin/main 合并到你当前的本地分支(比如 main)。

那么可能有两种情况:

  • 情况一:没有冲突 → 自动创建合并提交

    Git 能自动把两边的修改“合”在一起。你会看到类似这样的输出:

    1
    2
    3
    4
    
    Auto-merging README.md
    Merge made by the 'ort' strategy.
     README.md | 2 ++
     1 file changed, 2 insertions(+)
    

​ Git 会自动创建一个 合并提交(merge commit),包含两个“父母”:本地提交和远程的新提交。 ​ 结果:代码合并成功,历史变成“分叉再合并”的样子。

  • 情况二:有冲突 → 合并失败,需要手动解决

    如果你和远程修改了同一个文件的同一部分,Git 不知道该保留谁的,就会报冲突。

    • 输出类似:

      1
      2
      3
      
      Auto-merging src/app.js
      CONFLICT (content): Merge conflict in src/app.js
      Automatic merge failed; fix conflicts and then commit the result.
      
    • 此时:

      • 你的工作区处于 “合并中”状态MERGING

      • 文件里会出现冲突标记:

        1
        2
        3
        4
        5
        
        <<<<<<< HEAD
        console.log("my local change");
        =======
        console.log("remote change from colleague");
        >>>>>>> origin/main
        

    此时需要手动操作:

    1. 编辑文件,决定保留哪部分(或融合两者)
    2. 删除冲突标记<<<<<<<, =======, >>>>>>>
    3. git add 解决完冲突的文件
    4. git commit 完成合并(Git 会自动生成合并提交信息)

    完成后,本地就同时包含了你和远程的修改。

所以每天开工前最好先pull,减少冲突概率。

代码冲突

Git工作流

GitFlow工作流

Gitflow工作流(Gitflow Workflow)是2010年由Vincent Driessen在他的一篇博客里提出来的。它定义了一整套完善的基于Git分支模型的框架,结合了版本发布的研发流程,适合管理具有固定发布周期的大型项目。

和特性分支工作流相比,Gitflow工作流并没有引入任何新的概念。不同的地方在于,它强化了对Git分支模型的使用,结合产品或项目发布周期的特定需求,定义了各种不同类型的分支,每一种分支都有它自己特定的职责,并且分支之间什么时候、以什么样的方式交互,也都有相应的规则。下面我们就具体来看一下。

Master分支

Master分支作为唯一一个正式对外发布的分支,是所有分支里最稳定的。这是因为,只有经过了严格审核和测试,并且在当前发布计划里的特性,才会被合并到master分支。当某个版本发布的时候,我们通常还会为master分支加上带有相应版本号的tag。

Develop分支

Develop分支是根据master分支创建出来的,它作为一种集成分支(Integration Branch),是专门用来集成开发完成的各种特性的。Develop分支通常具有更加详细和完整的提交历史,包括一些很细节的提交记录。而master分支则因为是面向版本发布的,所以它的提交历史会略去这些细节,显得比较精简。

Feature分支

Feature分支是根据develop分支创建出来的,Gitflow工作流里的每个新特性都有自己的feature分支,这一点和特性分支工作流是一样的。这些分支除了在开发人员的本地存在以外,也可以被推送到共享的远程Git库,作为工作备份,以及与其他人协同工作的基础。当特性开发结束以后,这些分支上的工作会被合并到develop分支。但feature分支从来不会直接和master分支打交道。

Release分支

当积累了足够多的已完成特性,或者预定的系统发布周期临近的时候,我们就会从develop分支创建出一个release分支,专门用来做和当前版本发布有关的工作。Release分支一旦开出来以后,就不允许再有新的特性被加入到这个分支了,只有bug修复或者文档编辑之类的工作才允许进入该分支。

Release分支上的内容最终会被合并到master分支,等版本发布的时候,我们通常还会为master分支加上带有相应版本号的tag。同时,release分支也会被合并到develop分支。在release分支活跃其间,develop分支也一直处于Open状态。Release分支上的内容代表当前版本在发布之前的准备工作,develop分支上的内容则代表下一个版本的开发工作,两者是可以并行展开的。

Hotfix分支

Hotfix(热补丁)分支不从是develop分支创建出来的,而是直接根据master分支创建得到的,其目的是为了给运行在生产环境中的系统快速提供补丁,同时确保不会给正在其他上分支进行的工作造成影响。当hotfix分支上的工作完成以后,可以合并到master分支和develop分支,以及当前的release分支。如果有版本的更新,也可以为master分支打上相应的tag。

大致关系如图:

Gitflow

来源:A successful Git branching model » nvie.com

具体工作

Vincent Driessen不仅定义了Gitflow的工作流程,还提供了一个相应的命令行工具git-flow

具体工作流详见:Git工作流面面观——Gitflow工作流 - 晴耕小筑

GitHubFlow

GitHubFlow看名字也知道和GitHub有关,它来源于GitHub团队的工作实践。当代码托管在GitHub上时,则需要使用GitHubFlow。相比GitFlow而言,GitHubFlow没有那么多分支。

GitHubFlow通常只有一个Master分支是固定的,而且GitHubFlow中的Master分支通常是受保护的,只有特定权限的人才可以向Master分支合入代码。

在GitHubFlow中,新功能开发或修复Bug需要从Master分支拉取一个新分支,在这个新分支上进行代码提交;功能开发完成,开发者创建Pull Request(简称PR),通知源仓库开发者进行代码修改review,确认无误后,将由源仓库开发人员将代码合入Master分支。

GitHubflow

很多人可能会问,提交代码通常是commit或者push,拉取代码才是pull,为什么GitHubFlow中提交代码提出的是“Pull Request”。因为在GitHubFlow中,PR是通知其他人员到你的代码库去拉取代码至本地,然后由他们进行最终的提交,所以用“pull”而非“push”。

GitHubFlow优点是相对于GitFlow来说比较简单,其缺点是因为只有一条Master分支,万一代码合入后,由于某些因素Master分支不能立刻发布,就会导致最终发布的版本和计划不同。

GitLabFlow

GitLabFlow出现的最晚,GitLabFlow是开源工具GitLab推荐的做法。

GitLabFlow支持GitFlow的分支策略,也支持GitHubFlow的“Pull Request”(在GitLabFlow中被称为“Merge Request”)。

相比于GitHubFlow,GitLabFlow增加了对预生产环境和生产环境的管理,即Master分支对应为开发环境的分支,预生产和生产环境由其他分支(如Pre-Production、Production)进行管理。在这种情况下,Master分支是Pre-Production分支的上游,Pre-Production是Production分支的上游;GitLabFlow规定代码必须从上游向下游发展,即新功能或修复Bug时,特性分支的代码测试无误后,必须先合入Master分支,然后才能由Master分支向Pre-Production环境合入,最后由Pre-Production合入到Production。

img

GitLabFlow中的Merge Request是将一个分支合入到另一个分支的请求,通过Merge Request可以对比合入分支和被合入分支的差异,也可以做代码的Review。

Gitflow

Licensed under CC BY-NC-SA 4.0
© 2023-2025 Ch0ser. All Rights Reserved.
使用 Hugo 构建
主题 StackJimmy 设计